written by Daniele Varrazzo
(w) 1997-1999 by PiroSoftware c.n.f.
Homepage: Punto triplo
For any suggests, bug reports, new ideas you can write to piro@officine.it
Index
splLinearSpline()
PlotSpline()
PlotXYSpline()
splCubicSpline()
splBezierSpline()
SetShapeSpline()
SetPathSpline()
Striscia()
gCorrArea
gvSky
splCircle()
splArc()
splSumSplines()
splPolygon()
splTranslate()
splRotate()
splScale()
splCombineSplines()
splJoinSplines()
gPathDiv
gShapeDiv
gPathDepth
gPathErr
gShapeDepth
gShapeErr
slpSlope()
PlotSlope()
slpLinearSlope()
gPathLength
AddShapeTransform()
AddShapeSpline()
SetMorphSlope()
SetWrappedTexture()
gsBufferName
Diaframma()
gvShapeCenter
ResetParameters()
PutOnSurface()
PutUnderSurface()
bBaseOnSurface()
Intro
An old fashion surface definition is
"the place of the points of a curve that is moved and deformed at the same time"
I want to give you exactly this: you will take a curve in your hands and will move it in the space. You could take a simple shape, such as a yellow hula-hop and start walking, moving it in the air.
Think about a camera watching the scene, with a long exposure time: in the photo you will see a yellow surface, something like a "pipeline". If you chose to walk in a straight line, you obtained an open cylinder. If you spinned around, now you have a "torus", a donut.
You will hold in your hands any shape you want. And walk on any path.
And more.
1. Basics.
Before doing anything, you must create a new scene file, and add the line
#include "Striscia.inc"
"Striscia.inc
" and all the other files should stay in a library path.
Now you have to learn to describe curves before going on. If you already used lathe
and prism
objects in POV language, then you already know almost everything.
You can describe curves with the same prism base rules: you can describe a curve made up with linear pieces, or a cubic curve described with interpolated points or with the Bezier polygon. You won't use the quadric system: i consider it hard to control and pretty useless.
I wrote some macros that return a curve as it were an object: you can, for example, assign it to a variable.
This is the simplest spline function: it returns a linear spline. You can call it with:
splLinearSpline(array[n] {v1, v2, ...., vn})
The elements from v1 to vn are 3D vectors. This is the main difference from the prism
and lathe
control curves, that must be defined on the z=0 plane.
This example shows a square contained in the horizontal plane (the XY plane) that abruptly "raises" along the z axis:
#declare splOddSquare = splLinearSpline(array[6] { <-1, 0, -1>, <1, 0, -1>, <1, 0, 1>, <0, 0, 1>, <-1, 1, 1>, <-1, 1, -1> })You don't have to use the semicolon ";" at the end of the row. The curve don't need to be closed and i won't close it for you.
Do you want to see the look of your curve? Well, i wrote a macro for this. Add to your scene the row:
PlotSpline(splOddSquare, <0.75, 2, -2.5>)
where the vector following the name of the spline to be plotted is the observer point of view. The result will be:
A not planar curve.
If you define a curve laying in the XY plane, you can plot it using the macroPlotXYSpline(splSpline)
. For example:
#declare splFlat = splLinearSpline(array[5] { <-1, -1, 0>, <-2, 2, 0>, <-1, 4, 0>, <3, 1, 0>, <1, -1, 0> }) PlotXYSpline(splFlat)
A curve in the XY plane.
You can plot more curves on the same image: if you add to the previous scene the rows:
#declare splSecond = splLinearSpline(array[6] { <-1, 1, 0>, <-0.5, 2, 0>, <0.5, 2, 0>, <1, 1, 0>, <-1, -1, 0>, <1, -1, 0> }) PlotXYSpline(splSecond)
Two curves in the same image.
Other spline functions are:
|
1, v2, ..., vn})
it's analogous to the curve generated when you use the #declare splCubicCurve = splCubicSpline(array[6] { < 3, -5, 0>, //control knot < 3, 5, 0>, <-5, 0, 0>, < 3, -5, 0>, < 3, 5, 0>, <-5, 0, 0> //control knot }) PlotXYSpline(splCubicCurve) Cubic curve in the plane. |
|
1, v2, ..., vn})
generates a cubic curve from its Bezier polygon. Each four points it will generate a cubic segment, from the first to the fourth. The second and the third point are control points. Same analogies and differences with a prism defined with |
spl
" prefix for all the functions returning a spline. This will be true for many other functions i will talk of, and i will follow similar rules with other peculiar objects.Now you already know almost everything...
2. Two parameters, to begin.
To specify a surface, you have to define two parameters. They both are spline curves.The first parameter is the shape you want to hold in your hands. The shape can be any spline curve, both planar or not. But there is a privileged coordinates system.
If you define as shape a curve completely lying in the z=0 plane, walking along the path, it will always remain perpendicular to it. Think about a rubber-tube: you can bend it in any way, but if you cut it perpendicularly to the path you will always find a circular section.
You can define your shape anywhere in the space, but the alignment will always be made with the z=0 plane. If you define a not planar curve, the perpendicularity condition could not be respected anywhere along the path.
To create a curve representing a circle with radius 1 in the z=0 plane, you can use the function:
#declare MyShape = splCircle(4)
I will show later its usage, you should only know it generates the curve:
A radius 1 circle.
Now you have to declare that "this is the surface shape": do it with the macro:
SetShapeSpline(MyShape)
The other half of the work is to specify the path. To describe it yu have to define a spline, then call the macro SetPathSpline()
. The path can be any curve. The shape defined with SetShapeSpline()
will follow it faithfully, keeping itself always perpendicular to it. Do you want to walk in a straight line? Do it with the instructions
#declare MyPath = splLinearSpline(array[2] {<0, 2, 0>, <8, 2, 0>}) SetPathSpline(MyPath)
Striscia
()
object { Striscia(UNION) texture { pigment { color rgb <1, 1, 0> } } }...sketch a background...
camera { location <-2, 6, -4> look_at <3, 1, 0> } light_source { <-50, 50, -0> color rgb 1} plane { y, 0 pigment { checker color rgb 1 color blue 1 } }...run the scene and...
Ok, ok, it's just a cylinder, but...
Do you want to take a zigzag walk? Do it with
#declare MyPath = splCubicSpline(array[8] { <-1, 0, 0> <0, 0, 0>, <1, 0, 1>, <2, 0, -1>, <3, 0, 1>, <4, 0, -1>, <5, 0, 0>, <6, 0, 0>}) SetPathSpline(MyPath)
...that's something new.
Striscia (pl: strisce) literally means "strip". The argument in the parenthesis of theStriscia()
macro is the kind of the object you want to create. It can assume one of these values:
- TRIANGLES
: generates only triangles;
- UNION
: generates a triangles union;
- MESH
: generates a triangles mesh.If you define a path with a corner, the surface will have a corner too. I can't work like frame-makers do, cutting wooden board with 45° angles to obtain my corners: this solution doesn't fit with all the shapes. So i use another technique: i skew the sections while they go close to the corner, so they will reach to a common plane from both sides.
All the work is done internally. You only have to specify, if you have to change the default value, how long the correction area must be. The default value is gCorrArea=2
: the deformation will take effect in an area that extends for 2 units after and before the corner (walking on the path). In this area, there isn't perpendicularity between sections and path. The default value fits for roughly unit-size sections: if the section would be bigger, you could see bumps and odd phenomena in this area (surface collapsing onto itself). In this case, just raise the value for the gCorrArea
variable.
You can even deal with 180° angles: the surface won't be distorted (it would requira an infinite distortion) but it will "bounce" on an invisible plane. There will be an hole in the corner, but later i will show you how to close it.
Here are two pictures showing how corners distorsion routine works. It's a detail of a striscia with circular shape with radius=1/5 and square path with side=1. The striscia is halved to show its inside. Squares are side=1/10.
![]() |
gCorrArea=1/3 the correction area is big enough compared to the section. |
![]() |
gCorrArea=1/25 the correction area is too little: the curve "enters" the corner, then folds onto itself to align. |
Warning! The corner may not work properly if the section is not planar: there could be holes on the surface. Does anybody here have any idea?
3. Surface building.
Here are the rules for the surface creation: there is a little man in your PC (a kind of gnome, sure!), and, for each path point:
camera
" looking straight to the "sky
" direction. So you have to specify a new sky by setting the gvSky
varaible with a new vector, such as
#declare gvSky = z;
Make your gnomes happy! Give them a new sky!
4. Widen your basics.
It's important to be free in the control curves description. Using Bezier cubics you have all the power you need, but in simple tasks they can be too much complex. I wrote some easier macro.
You already met one of them: the splCircle(n)
macro.
|
generates a circle on the z=0 plane with radius 1. the "n" parameters indicates how many segments will form the circle: it's impossible to describe a whole circle using only a cubic segment. In order to archieve good results, n must be at least 4. With values greater than 4, the result won't be better, it will be useful for other goals. Later we will see how to morph a curve into another one. But both curves must have th same number of segments: you can morph a square in a circle with 4 segments and an exagon in a circle with 6 segments. |
|
generates an arc on the circle in the z=0 plane and with radius 1. V is an array where each element indicates, in degrees, the breakpoint position in the arc. So, a n elements array will make a n-1 segments arc. The 0° angle is on the x axis and your left hand will show you the angles positive direction in the usual way: point the thumb toward +z and the finger will curl in the positive verse of rotation. Each segment should not extend for more than 90°, or it will dent. Some examples:
is the same circle of splCircle(4) ;
splits the 90° angle from <1,0,0> to <0,1,0> in 3 pieces;
strange: it turns two times. What can be used for? You can use it together with... |
|
generates a spline by adding up two other splines: #declare splFirst = splArc(array[9] { 0, 90, 180, 270, 360, 450, 540, 630, 720 }) #declare splSecond = splLinearSpline(array[9] { <0, 0, 0>, <0, 0, 1/3>, <0, 0, 2/3>, <0, 0, 3/3>, <0, 0, 4/3>, <0, 0, 5/3>, <0, 0, 6/3>, <0, 0, 7/3>, <0, 0, 8/3> }) #declare splSum = splSumSplines(splFirst, splSecond) PlotSpline(splSum, <3, 1, 3>) A spiral |
|
generates a regular polygon with n sides, lying in the z=0 plane and inscribed in the unit circle. The first corner is always in the <1,0,0> point. For example: #declare splOctagon = splPolygon(8) |
|
|
|
|
|
they perform on a spline the same transformations of the Notice that you must use these macros like they were functions: they return a result that must be assigned to some variable, even the starting one, e.g. // Creates a square #declare splSpline = splPolygon(4) // Draws it with a red pen PlotXYSpline(splSpline) // Rotates the square #declare splSpline = splRotateSpline(splSpline, z * 45) // Changes the square into a rectangle #declare splSpline = splScaleSpline(splSpline, <2, 0.5, 1>) // Translates the rectangle #declare splSpline = splTranslateSpline(splSpline, <1, 1, 0>) // Draws the rectangle with the red pen PlotXYSpline(splSpline) The same curve, before and after some changes |
|
Makes a curve from two curves. This doesn't mean you will obtain a continue curve: if the splines don't touch each other they won't even after their combining. |
|
Another way to make a curve from two curves. Now, the second one will be translated so that its starting point will meet the first one's ending point, and then combined. The result will be a continue spline. |
5. Quantities and qualities.
Nice, isn't it? you made a pipe. Ugly, isn't it? its quality is quite low... Of course you can increase the drawing precision.If the triangles approximation is too coarse, you can divide the curves more. You can do it by setting a pair of variables.
They are the main variables: each spline path segment is divided into
gPathDiv
linear segments; in the same way each shape segment is divided into gShapeDiv
linear segments. Each time you double one of these values, the number of generated triangles doubles.
The default values are gPathDiv=4
e gShapeDiv=4
.
gPathDepth
gPathDepth
variable means to divide more even the straight parts, wasting lots of time and space toward no quality increasing.
You can use an adaptive algorithm: you can enable it by setting a value greater than 0 to the gPathDepth
variable. So, if a path section is too much bent, it will be split in two halves, and each half, if too bent, split again... This process can stop for two reasons:
gPathDepth
times;gPathErr
.gPathDiv
to gPathDiv
*(2^gPathDepth
) linear segments.
This is true for sections too, using gShapeDepth
and gShapeErr
. Each section is split up as it needs.
If you reach the maximum splitting depth but the error is still greater than what you asked for, you will be warned after the surface parsing. Now you can choose if to increase the splitting depth or keep the results as they are.
The default values are gPathDepth
= gShapeDepth
= 0 and gPathErr
= gShapeErr
= 0.01.
Error values are not "high" or "low" in an absolute way: it depends from the size of the surface in your image and from the image resolution too. Default values could be good for about unit-size objects, but it depends from many factors... just try.
If you add the rows
#declare gPathDepth = 3; #declare gShapeDepth = 3;to the zigzag scene (before the
Striscia()
instruction) you will obtain:
A better result than the previous one.
gPathDiv
, gPathDepth
and gPathErr
variables even work with PlotSpline
and PlotXYSpline
macros.The error estimation routine along the path uses an approximation: it's optimized to evaluate as it were drawing a square section of side 2, centered on the path. For surfaces with bigger sections there could be bigger errors.
Furthermore, the error along the path is measured only in one of the square vertex: the one with both coordinates positive. This could lead to asymmetrical evaluations.
Eventually, in some (rare, i hope) cases, the evaluation routines (mainly the shapes one) make... very rough errors! If, for example, i have a S shaped curve, perfectly symmetrical, and i cut it in two halves, it looks like the error were 0. In fact the routine measures how much the curve segment middle point is far from the linear segment middle point connecting its ends. Because the error looks really little, the routine thinks it's dealing with a straight line, so the S is approximated only with one segment. To avoid troubles like this (you can notice it because increasing the splitting depth the quality doesn't increase) try to increment (or even decrement) by 1 the starting division: this will break symmetrical schemes.
6. Another kind of control.
We are going to learn how to deform the shape while it moves along the path. But first you have to learn another kind of control. You already met it too in POV language.
slpSlope
()slope_map
command. A slope function, like a spine, can be seen as an object and assigned to a variable. I can generate it using the macro:
slpSlope(array[n] {<X1, Y1, M1>, <X2, Y2, M2>,..., <Xn, Yn, Mn>})
The parameter is a 3D vectors array, but the array components are not read as a space point's x, y, z components, but respectively as:
#declare slpMySlope = slpSlope(array[3] { <0, 0, 0>, <2, 1, 0>, <3, 0, -2> })
Do you want to see your function graph? Just add the instruction
PlotSlope(slpMySlope)
. With the previous declaration, you will get the graph:
A simple slope function.
Even this graph quality is ruled by the usualgPathDiv
, gPathDepth
and gPathErr
variables.A more complex slope example, copied and slightly modified from POV documentation, is:
/*#declare MySlope = slpSlope(array[15] { < 0, 0, 1>, // Do tiny triangle here < 2, 2, 1>, // down < 2, 2,-1>, // to < 4, 0,-1>, // here. < 4, 0, 0>, // Flat area < 5, 0, 0>, // through here. < 5, 2, 0>, // Square wave leading edge < 6, 2, 0>, // trailing edge < 6, 0, 0>, // Flat again < 7, 0, 0>, // through here. < 7, 0, 3>, // Start scallop < 8, 2, 0>, // flat on top < 9, 0,-3>, // finish here. < 9, 0, 0>, // Flat remaining through 1.0 <10, 0, 0> })
A more complex slope function.
If you want to create a linear slope, you can use the
slpLinearSlope(V)
macro, where V is a 2D vectors array. These vectors are to be read as:
:(
There is a bug in the POV parser (still there up to 3.1a version, the one i'm working with), that prevents to read the 2D vectors components. Does you version works properly? If not, use the slpLinearSlopeBug(V)
, where V is a 3D vectors array. The x and y components will work as the u and v components do in slpLinearSlope(V)
, while the z component will be ignored.
Of course i never tested the slpLinearSlope(V)
macro, so i don't bet anything on its work...
7. Variable transformations.
When i draw a shape, i can deform it before applying to the path. But why don't do it dnamically, changing the amount of transformations along the path?
You have to know how much is long your path. If it's bent, it's hard to calculate it. Once you set the path with the SetPathSpline()
macro, you will know it by reading the gPathLength
variable.
Don't change this value, only read it!
Let's create a different kind of torus, with major radius 3 and minor radius 1, but with a square section. You can do it with this code:
#declare splPath = splCircle(4) // Circle in the xy plane #declare splPath = splRotate(splPath, x * 90) // Moves the circle in the xz plane #declare splPath = splScale(splPath, <3, 3, 3>) // Makes the radius 3 SetPathSpline(splPath) // Apply it as path #declare splShape = splPolygon(4) // Square in the xy plane SetShapeSpline(splShape) // Apply it as shape
But now i want the square, moving along the circle, rotates entirely around itself. I do this defining a slope that describes the rotation:
#declare slpRotSlope = slpLinearSlope(array[2] {<0, 0>, <gPathLength, 360>}) AddShapeTransform(PATH_ROTATE, slpRotSlope)
Not just the usual torus.
TheAddShapeTransform()
macro takes two parameters. The first one tells what transformation to perform. It can assume one of these values:
- HORZ_TRANSLATE
: translates the shape along the "horizontal" direction, that is perpendicular to the sky vector gvSky
. If i look in the path direction, a positive translation will move the shape to the right, a negative one to the left;
- VERT_TRANSLATE
: translates the shape along the "vertical" direction, parallel to the sky vector gvSky
. a positive translation moves toward the sky;
- PATH_TRANSLATE
: moves the shape along the path direction. A positive value will move the shape in the path direction. Probably this will lead the shape to loose the perpendicularity with the path, because you won't align the plane where you drew your shape;
- HORZ_SCALE
: scales the shape along the horizontal direction, making it bigger if you specify a value greater than 1 or littler otherwise;
- VERT_SCALE
: scales the shape along the vertical direction;
- PATH_SCALE
: scales the shape along the path direction (useless with a shape in the xy plane);
- UNIF_SCALE
: scales the shape uniformly;
- PATH_ROTATE
: rotates the shape along the path. The verse of positive rotation is the one of your left hand fingers, pointing the thumb in the path direction.Another example: a torus with variable minor radius. The path is the same as before.
#declare splShape = splCircle(4) SetShapeSpline(splShape) #declare slpScaleSlope = slpSlope(array[3] {<0, 1.2, 0>, <gPathLength/2, 0.5, 0>, <gPathLength, 1.2, 0> }) AddShapeTransform(UNIF_SCALE, slpScaleSlope)
Another variation on the same theme.
You can specify any number of transformations you want: they will be applied in the same order you declare them. All the considerations for the objects transformations are still valid: if i translate a shape and then rotate it, the shape will also orbit around the path. If i first rotate, then translate, there won't be any orbit, only a rotation on the place.I can apply the same transformations in the section dimension of the second example to the rotation of the first one:
Melting the previous examples.
Here is another example in composing transformations: it's like a cone with base radius 1 and height 1 but, while rising, it twists along a vertical axis, at distance 1 from the cone axis.// The path is a vertical segment, from <0, 0, 0> to <0, 1, 0> #declare gvSky = z; #declare splPath = splLinearSpline(array[2] {<0, 0, 0>, <0, 1, 0>}) SetPathSpline(splPath) // The shape is a circle with radius 1 #declare splShape = splCircle(4) SetShapeSpline(Shape) // The shape, moving upside, become smaller #declare slpScale = slpLinearSlope(array[2] {<0, 1>, <1, 0.0001>}) AddShapeTransform(UNIF_SCALE, slpScale) // Translate the shape to make it orbit #declare slpTrans = slpLinearSlope(array[2] {<0, 1>, <1, 1>}) AddShapeTransform(HORZ_TRANSLATE, slpTrans) // Orbit the shape along the path #declare slpRotate = slpLinearSlope(array[2] {<0, 0>, <1, 360>}) AddShapeTransform(PATH_ROTATE, slpRotate)
Odd cone
I end the point with a very little value instead of 0, in order to prevent degenerate triangles generation.If you apply any transformation to the section, evaluating the sampling error they will be taken into account, both in path and shape direction. It's like the transform were applied to the "square" used to measure the errors. So, to have a good sampling precision with big sections too, you could draw the shape in about unit size, and then scale it up with a transform.
8. Sections morph.
Another way to deform the shape is to set different sections in different path points. In the other points the shape will be a blend of the previous and the next ones.For example, here is an easy to think but hard to describe primitive: a cylinder morphing into a box.
First i set up the path: a straight one will fit.
#declare Path = splLinearSpline(array[2] {<-1, 1, 0>, <2, 1, 0>}) SetPathSpline(Path)To define the shape, you have to use a more flexible version of the
SetShapeSpline()
:
AddShapeSpline(splShape, X)
In the X point on the path, the shape will be splShape
. X is measured with the usual arc length, so it should range from 0 to gPathLength
. In the "cylinder turning into a box" example...
#declare Shape = splCircle(4) #declare Shape = splRotateSpline(Shape, z * 45) AddShapeSpline(Shape, 0) #declare Shape = splPolygon(4) #declare Shape = splRotateSpline(Shape, z * 45) #declare Shape = splScaleSpline(Shape, <1.4, 1.4, 0>) AddShapeSpline(Shape, 3)
A cylinder turns into a box.
Now the surface is no more drawn from the path start to its end, but only from the first shape to the last one. Of course you need at least two shapes to see anything...
9. Morph control.
In each point, the surface section is a weighted sum only of the previous shape and the next one. The shapes percentage depends on the point distance from the shapes: if i morph a square in a circle, when i'm closer to the square i obtain "more square" sections.A control lets me manually choose the shapes percentages. For example i could choose to keep the shape circular along almost all the path and only at the end change it into the square.
SetMorphSlope
(slpMorph)You can also choose value bigger than 1 or lesser than 0, but the results are hardly predictable...
Here are some examples of morph function graphs with the relative effect on the previous "cylinder to box":
|
|
The usual morph, without modifiers |
|
|
|
Circle for 2/3 of the path, then quickly square |
|
|
|
1/3 circle, 1/3 morph, 1/3 square |
|
|
|
Ping pong effect |
|
|
|
External values to the [0,1] interval: interesting effect but not too much intuitive... |
10. Texture mapping.
Did you like the odd cone texture? I did it with the uv mapping technique, that maps a functions (in our case the texture) defined on a simple set (such as a square) onto a more complex surface.Think about the object:
polygon { <0, 0, 0>, <1, 0, 0>, <1, 1, 0>, <0, 1, 0>, <0, 0, 0> }
representing a square with side 1 in the z=0 plane. Apply to it any texture you want, multi-layered too. The same texture you would see on this square will be wrapped on your surface.
To do this you must use the
SetWrappedTexture(MyTexture)
macro, with any texture as parameter. In the odd cone example i used the code:
SetWrappedTexture(texture { pigment { checker color rgb 1 color blue 1 scale 1/16 } finish { specular 0.8 } })The lower side of the square will be mapped on the shape at the path start, while the upper side will be mapped on the shape at the end. The "left" side (the one with x=0) will be mapped on the starts of each section while the "right" side on their ends.
Less words and more draws... The command for this wrapped texture is:
SetWrappedTexture(texture { pigment { image_map { png "c:\\Bitmaps\\Me.png" interpolate 2 once } } })
![]() That's me in the corner... |
![]() ...that's me in the spotlight! |
Er... forgive me :)
The blue arrow shows the path, the red ones show two shapes.
Be careful: you can't generate a MESH
if you apply a wrapped texture. In fact a wrapping requires a different matrix
transform for the texture in each triangle.
11. I store something.
Generating a surface could be a slow task. If i have to compose my scene, i don't want to waste all the time generating always the same surface, only to move a knick-knack of an inch. Even if i want to create an animation, i don't want to generate the same surface at each frame. I could ask to store the surface data in a file.To store the surface data in a file you only have to specify the name of the file you want to create, using the
gsBufferName
variable. For example
#declare gsBufferName = "OddCone"
stores all the data in the "OddCone.inc
" file. You don't have to specify the buffer file extension. The file will be written in the scene path, but you can also specify a different one.
The second time you will launch the scene, the routine will find the saved file and will read it (it must be in a reachable path, such the scene path or in a library path), without generating any new triangle.
If you want to change something in the surface, you have to edit its parameters and delete the buffer file before render the scene again.
If you save a surface with a wrapped texture, the routine will store only the texture transformation, not the texture data. So you can change the texture without generating the surface again.
Watch out! If you stop the macro while it's generating the surface, it will create a partial buffer file. On next rendering you could get errors (maybe a "missing }"). In this case, simply delete the buffer file and parse again. To avoid this problem i need instructions to delete and erase files, but POV-Ray lacks them... Will anybody...? :)
The routine also reads a compressed mesh file, using the compressed mesh macro file by Chris Colefax. You only have to give the .pcm
compressed file the same name of the .ini
generated file. You must have the pcm.mcr
macro file in a library path: you can find it on the The POV-Ray Include File Page by Chris Colefax. Maybe in the future i will output the data directly in this format, but i still know too little about it...
12. Caps here and there.
Diaframma means "diaphragm". If you want to close your surface with two caps, you can use the rows:
object { Diaframma( 0, POLYGON) texture {MyTexture}} object { Diaframma(gPathLength, POLYGON) texture {MyTexture}}The first parameters is the cap location. You will usually want to close your surface adding a cap on each path end (0 and
gPathLength
), but it's not a duty. You could add internal diaphragms and watch them through holes in the surface, or even not draw the surface at all, leaving only the diaphragms...The second parameter indicates what kind of object you want to generate. It can be:
- POLYGON
: generates a polygon. It works only if the section on that point is a planar curve, otherwise it gives an error;
- TRIANGLES
: generates a set of triangles. It works with not planar curves too, but there could be some troubles with very complex shapes;
- UNION
: generates a triangle union;
- MESH
: generates a triangle mesh.
#declare splPath = splBezierSpline(array[8] { <0, 0, 0>, <-6, 6, 0>, <0, 9, 0>, <0, 5, 0> <0, 5, 0>, <0, 9, 0>, <6, 6, 0>, <0, 0, 0> }) SetPathSpline(splPath) #declare gvSky = z; #declare splShape = splCircle(4) #declare splShape = splScaleSpline(splShape, <1,1,1>*0.5) SetShapeSpline(splShape) union { object { Striscia(UNION) } object { Diaframma(gPathLength/2, POLYGON) } pigment { color red 1 } finish { phong 0.7 } }
Heart and diaphragm...
All the triangles in a diaphragm have a common vertex. This mean that you can properly draw a diaframma only on those curves where you can connect an internal point to each point on the curve with a straight line, without crossing the curve itself with this line.
By default the triangles meet in the section's coordinates system origin. You can express any point where to meet by setting the gvShapeCenter
with a 3D vector value.
This value describes a point in the section coordinates system. The drawing routine will move it in the proper position in the space. For example, with the curve
#declare splSpline = splCircle(4) #declare splSpline = splTranslate(splSpline, <4, 0, 0>)
you could set
#declare gvShapeCenter = <4, 0, 0>;
Using triangles you could also create a conic cap: You just have to put the gvShapeCenter
not on the z=0 plane. I.e...
#declare gvShapeCenter = <0, 0, 0.75>; object {Diaframma(gPathLength/2, UNION) }
Now the diaphragm is conic.
13. Many strisce in your scene.
If you want to add more surfaces in your scene, you have to reset the control parameters using the ResetParameters()
macro. It will reset the path and the shapes, the control slopes and the wrapped texture. It won't change the other variables (sky directions, sampling parameters...).
14. Objects on the surface.
To put any object on the surface you could use the PutOnSurface(X,T)
transform. X and T coordinates indicate the point on the surface: X is the coordinate in the path direction and can range from 0 to gPathLength
. T is the coordinate in the shapes direction and can always range from 0 to 1.The object will be rotated and translated on the surface. The x axis of the object's coordinate system will be parallel to the section while its y axis will be perpendicular to the surface. The z axis will be perpendicular to the other ones.
The PutOnSurface()
macro works properly only when you describe your shape turning counterclockwise, such as the one generated by splCircle()
. If your section turns clockwise, the object would end inside the surface. In this case you could use the PutUnderSurface(X,T)
macro, that works exactly at the same way but puts the object on the other side of the surface.
For example, always using the "cylinder in shape" i can add:
// loop along the path #declare I=0; #while (I<=8) // loop along the sections #declare J=0; #while (J<16) // a cone planted in the ground cone { <0, -0.05, 0>, 0, <0, 0.2, 0>, 0.05 pigment {color blue 1} // move it on the surface PutOnSurface(I/8*gPathLength, J/16) } #declare J=J+1; #end #declare I=I+1; #end
Stings in the surface
For a deeper interaction between the surface and the scene, you can read the transformation matrix values given by
PutOnSurface()
. The bBaseOnSurface(X,T)
returns a four components array filled up with 3D vectors: they represent in order the x, y and z axis and the origin point of the coordinates system that touchs the surface in the (X,T) point.In this example, i simulate a wire-frame surface joining a grid of points on the surface with thin cylinders.
// Cylinders texture #declare txtWireFrame = texture { pigment {color rgbf <0, 1, 0, 0.8>} finish {ambient 0.6 diffuse 0.4} } // Buffer to store the points #declare Points = array[9][16] // points sampling #declare I=0; #while (I<=8) #declare J=0; #while (J<16) #declare bBase = bBaseOnSurface(I/8*gPathLength, J/16) #declare Points[I][J] = bBase[3]; #declare J=J+1; #end #declare I=I+1; #end // drawing loop #declare I=0; #while (I<=8) #declare J=0; #while (J<16) cylinder { Points[I][J], Points[I][mod(J+1,16)], 0.02 no_shadow texture {txtWireFrame} } cylinder { Points[I][J], Points[mod(I+1,8)][J], 0.02 no_shadow texture {txtWireFrame} } #declare J=J+1; #end #declare I=I+1; #end
An old fashion surface displaying.
This macro file, its documentation and samples are copyright 1999 by Daniele Varrazzo.
You can freely use, copy and edit this macro file. If you edit it and obtain something good, let me know!
If you want to include this macro file in any distribution, please before contact me: i will give you the more recent version.
This program is provided as is without any warranty. I'm not liable for any losses, damage or injury caused by its usage, including divorces, teeth-aches, punctures, alien invasions.
POV-Ray and Persistence of Vision are trade marks of the POV-Ray Team.
the Compressed Mesh Macro File is copyright by Chris Colefax.