Summary

How to cover a given object

Demo-file: Demo3.pov

The following series of images demonstrates, how it is possible to cover a given object with a network of sphere_sweeps. First we built up a random (red) blob (image 2), from random red positions (image 1). This blob is surrounded by positions, which are distributed randomly on a sphere. These positions are moved towards the center of the sphere until they reach the surface of the blob, which results in randomly distributed positions on this surface. Now we only have to use these positions for the construction of sphere_sweeps (blue) The sphere_sweeps start at the green sphere. Needless to say that there are different possibilities for forming these sweeps. Here we show one example of non-ramified and one example of ramified sweeps.

random position for defining the blob a random blob object surrounded by random positions random positions on the object sphere_sweep covering the object sphere_sweep covering the object sphere_sweep covering the object sphere_sweep covering the object sphere_sweep covering the object

First we need a number of random positions to build up the random blob. The following macro for this task is similar to the macro creating a variable xyz-pattern. This time, however, we use polar instead of cartesian coordinates to define our positions.

#macro SphereDistributionArray (Radius, Sum)
//This macro randomly distributes Positions in a polar coordinate system.
//The positions are stored in the array "Positions".
//The Size of this Array in the Variable "Number".


#declare Positions = array [Sum];
#declare Number = Sum;

#local ticker = 0;
#while (ticker < Sum)
#local var1 = rand(chance1);
#local var2 = rand(chance2);
#local var3 = rand(chance3);
#local P1 = <Radius* var1, 0, 0>;
#local P1 = vrotate (P1, <0, 360 * var2, 0>);
#local P1 = vrotate (P1, <0, 0, 360 * var3>);
#declare Positions[ticker] = P1;
#local ticker = ticker + 1;
#end
#end

The Positions defined by this macro are used to build up a blob.

SphereDistributionArray (1.3, 100)
//macro for the generation of randomly distributed positions.
//The positions from the macro above (stored in the array "Positions") are used to build a blob.


#declare Body = blob {
threshold 0.2
#declare ticker = 0;
#while (ticker < Number)
#declare P1 = Positions[ticker];
sphere { <0, 0, 0>, 1, 1
translate P1
}
#declare ticker = ticker + 1;
#end
pigment {
color rgb <1,0,0>
}
}

object {Body}

Next we have to find random positions on the surface of this random body. For doing this, we create positions distributed randomly on a sphere surrounding the random blob and make those positions migrate towards the center of the sphere until they reach the blob.

#macro SurfaceDistributionArray (Object, Radius, Number)
//This macro generates Positions surrounding a given object in the form of a sperical envelope
//and lets those positions migrate versus the center of the object, until they have reached it.


#declare Positions = array [Number];
#local ticker = 0;
#while (ticker < Number)
#local var1 = rand(chance1);
#local var2 = rand(chance2);
#local P1 = <Radius, 0, 0>;
#local P1 = vrotate (P1, <0, 360 * var1, 360 * var2>);

//Here we make these positions move towards the center of the sphere until they reach the random blob.
#while (inside (Object, P1) = 0)
#local P1 = P1 - (1/100 * (P1 - <0, 0, 0>));
#end

#declare Positions[ticker] = P1;
#local ticker = ticker + 1;
#end
#end

The next macro finally looks for a path through the cloud of positions just defined. In our example this path is directed from negative to positive z-values.

//This macro looks for a way through the cloud defined in the array Positions.
//It will try to find a way from negative to positive z-values.
//Therefore the start should be defined on the negative z-side of the object.
//It will always take the closest available position, unless the distance is not below the minimal distance defined in the macro.
//The respective elements are stored in Positions2 and deleted from Positions.
//The size of the subarray is stored in the variable Sum2.


#macro SubArray (ArrayName, Start, SubArraySize)
//The output-array is initialized.
#declare Positions2 = array[SubArraySize];
#declare Sum2 = SubArraySize;

//Here Positions2 gets its first elements.
#local P0 = Start;
#declare Positions2[0] = P0 + <0, 0, P0.z/10>;
#declare Positions2[1] = P0;

//In the course of this loop the various elements of the input array are searched.
#local ticker1 = 1;
#while (ticker1< SubArraySize-1)

//A first element of the main array (Positions) is defined.
#local P1a = ArrayName[0];
#local Distancea = vlength (P1a-P0);

//In case the two positions are by chance identical, another position is taken.
#if (Distancea = 0)
#local P1a = ArrayName[1];
#local ticker = 2;
#else
#local ticker = 1;
#end

//Now the main array is searched for a position that has a more positive z-value
//and that is as close as possible to the current position unless it is not closer than a predefined value.

#while (ticker < Sum)
#local P1 = ArrayName[ticker];
#local Distance = vlength (P1-P0);
#if (Distance = Distancea)
#else
#if (Distance<Distancea & P1.z>P0.z & Distance > 0.1)
#local Distancea = Distance;
#local P1a = P1;
#else
#end
#end
#local ticker = ticker + 1;
#end
#local ticker1 = ticker1 + 1;

//The new point is written into the array Positions2.
#declare Positions2[ticker1] = P1a;
#local P0 = P1a;

//In all cases, except for the very first position, the positions found are now deleted from the original array.
#if (ticker1>2)
DeleteElement (Positions, Sum, P1a) //This macro deletes P1a from the array Positions and adjusts the size "Sum" accordingly.
#else
#end
#end
#end

The way the positions defined for the path are deleted from the original array decides about the branching pattern of the sphere_sweep. A deletion of all points (as defined above) results in sweeps, which will branch immediately. Below we demonstrate a way to generate a more gradual branching. As we will show in the following chapters, there are other methods available to create more realistic branching patterns.

//The following conditions cause to start the deletion of selected points at different levels.
//This is leading to a gradual ramification of the sphere_sweeps.
//First, the branching variable is more or less randomly defined.


#if (rand(chance1) <0.2)
#local branching = 5;
#else
#if (rand(chance1) <0.35)
#local branching = 8;
#else
#if (rand(chance1) <0.5)
#local branching = 9;
#else
#if (rand(chance1) <0.65)
#local branching = 10;
#else
#local branching = 11;
#end
#end
#end
#end

//The branching variable designates the point of the sphere_sweep, where the positions start to be deleted from the main array. This corresponds to the positions, where a new sphere_sweep has to find a new way, that means, where the sphere_sweeps are branching.
#if (ticker1<branching)
#else
DeleteElement (Positions, Sum, P1a) //This macro deletes P1a from the array Positions and adjusts the size "Sum" accordingly.
#end

After defining all these macros, here comes the code to actually use them.

//Definition of 4 pseudo-random streams.
#declare chance1 = seed (8);
#declare chance2 = seed (7);
#declare chance3 = seed (17);
#declare chance4 = seed (5);

//Macro for the generation of randomly distributed positions.
SphereDistributionArray (1.3, 100)

//The positions from the macro above (stored in the array "Positions") are used to build a blob.
#declare Body = blob {
threshold 0.2
#declare ticker = 0;
#while (ticker < Number)
#declare P1 = Positions[ticker];
sphere { <0, 0, 0>, 1, 1
translate P1
}
#declare ticker = ticker + 1;
#end
pigment {
color rgb <1,0,0>
}
}

//The positions defined below will lie directly on the surface of the blob. To make them lie a bit above this surface,
//the size of the blob is slightly reduced, before showing it.

object {Body
scale <0.97, 0.97, 0.97>}

//Macro for defining positions on the surface of the blob.
SurfaceDistributionArray (Body, 2.4, 910)

//The following loop marks all positions defined above by a small yellow sphere.
#declare ticker = 0;
#while (ticker < 910)
#declare P1 = Positions[ticker];
sphere { <0, 0, 0>, 0.025
translate P1
pigment {
color rgb <1,1,0>
}
}
#declare ticker = ticker + 1;
#end

//The following loop creates 20 sphere_sweeps crossing the cloud of positions defined above.
#declare ticker = 0;
#while (ticker <20)
SubArray (Positions, <0, 0, -2.2>, 30)//This macro defines the path across the cloud of points.
DrawSphereSweep (Positions2, 0.007, Sum2) //This macro defines the actual sphere_sweep.
#declare ticker = ticker + 1;
#end

//This last sphere is included to show the point, where all sphere_sweeps started.
sphere { <0, 0, 0>, 0.05
translate <0, 0, -2.2>
pigment {
color rgb <0,1,0>
}}

Summary