@database tutorial.guide 1.2 (21.02.00) @author Maxime Gamboni @wordwrap @node main "picFX tutorial" @prev main @next main @{fg shine}Welcome to the picFX tutorial guide.@{fg text} [This has been written in February '00 with version 1.3 of picFX] I have added/moved a few sections. The changes are put into brackets below.. Chapter one: Introduction @{"1.1: Introduction" link 11} @{"1.2: What can't be done with picFX" link 12} @{"1.3: Simple spreads" link 13} (previously section 3.1) Chapter two: Mathematical function summary (new chapter!) @{"2.1: Four operators" link 21} @{"2.2: Trigonometric functions" link 22} @{"2.3: Min, Max, Modulo, Abs" link 23} @{"2.4: Other functions" link 24} Chapter three: Simple graphs for a start @{"3.1: Monochrome graphs" link 31} @{"3.2: Implicit functions" link 32} Chapter four: Inter-referencing @{"4.1: Having fun with colours" link 41} @{"4.2: Convolutions" link 42} @{"4.3: Linear transformations" link 43} Chapter five: Using masks @{"5.1: Selection masks" link 51} @{"5.2: Translation masks" link 52} @{"5.3: Absolute masks" link 53} This guide is not finished (the chapter two, actually, as well as some broken links), this is why I haven't uploaded it to Aminet yet. The pictures are still in uncompressed IFF, this is why they take so much room. I will turn them into jpeg in the final version @endnode @node 11 "1.1: introduction" * This guide is designed to help you using picFX and going further than the boring "ripples", "sphere", "and so on" deformations. * I will assume that you already know the use of picFX. If not, first read some of the "standard" guide. * Now if you hate maths, try to forget it a little and read the explanations anyway (no matter if you do not understand the words like convolutions, matrix, and so on, try at least to follow the rules) And if maths really make spots grow on your face, then do not use picFX :-) * There is no specific order for the chapters, you can jump over a chapter that does not interest you and go later back to it. * This tutorial is still quite small, but do not hesitate to write me both for asking how to do this or that thing on a picture, and to suggest me some effect you have discovered. * So I will update this tutorial every time there is something to add. @endnode @node 12 "1.2: What can't be done with picFX" picFX, like any software, isn't perfect, and there are things that simply can't be done. You will have to use other programs to do these things: 1) Write text, like in paint programs. If you want to put some text in your picture, you can either save the picture and reload it elsewhere, but is might be better (so that you will beneficiate of picFX features while doing it) to write the text with some other software on a blank page, then save it and finally reload it in picFX in a new project. You can then insert the text in your picture. 2) Use freehand drawing. Unless you find an implicit equation defining your drawing, you can't do that. (This is why I said you would need some math skills ;-) (But note that implicit equations are more powerful that they might seem at first use...) 3) Filling an area by its borders. Found in most paint programs, this feature is unavailable in picFX, as it can't be described using equations. The only thing you can do is try to find some equations desribing the surface, or, again, use some other software. 4) Draw line segments, rectangles (full and especially empty), circles, and other things that seem so easy in other software are possible but rather complicated to do in picFX. Use implicit equatons for empty circles: x^2+y^2=c, rectangles: x^big+y^big=c and lines: ax+by=c. See the @{"3.3: Implicit functions" link 33} chapter. Please note that I do not intend to put these features in picFX (it would break the 'philosophy' of my program). But I will maybe make it easier to communicate with other programs, for instance using clipboard. Also handling of complex numbers is not handled by picFX. This might come once @endnode @node 13 "1.3: Simple spreads" Each colour on your computer screen is composed by a certain amount of red, green and blue. These amounts vary from zero (no colour, black) upto 255 (full colour). For example, mixing red and green makes some yellow, and yellow's definition is (255,255,0): full red and green and no blue. You have probably already seen those red,green and blue sliders in paint software. Here is a small list of colours and their definitions: (0,0,0) is black and (255,255,255) is white. (255,0,0) is red, (0,255,0) is green and (0,0,255) is blue. (0,255,255) is cyan, (255,0,255) is magenta and (255,255,0) is yellow. Now open a new project window if none is opened yet (@{fg shine}NEW@{fg text} button) The three fields named r(0,x,y)=, g(0,x,y)= and b(0,x,y)= [zero may be another number, depending on the current project]. contains these definitions. So if you write: r(0,x,y)=@{fg shine}255@{fg text} (copy only what is in white) g(0,x,y)=@{fg shine}255@{fg text} b(0,x,y)=@{fg shine}0@{fg text} And click on "render", the current project window will fill with yellow. You may try to put other numbers. Instead of just numbers, you may put functions of @{b}x@{ub} and @{b}y@{ub}. X is the number of pixels from the @{u}left@{uu} of the window, beginning from zero, and y is the number of pixels from the top. Try now setting: r(0,x,y)=x g(0,x,y)=y b(0,x,y)=255 @{"View picture" link "pictures/1.3.a/main"} You will see a nice spread. In the top left of the window, x=y=0 and the colour components is (0,0,255), blue. In the top right corner, x is 255, and the components will be (255,0,255), which is magenta. In the bottom border, it goes from (0,255,255) (cyan) to (255,255,255) (white). Try clicking in some places of the project window and look what is written in the bottom part of the messages-window. ˇ The computer automatically applies a 256 modulo to avoid overflowing. Try the following to see it: r(0,x,y)=2*x g(0,x,y)=2*y b(0,x,y)=255 @{"View picture" link "pictures/1.3.b/main"} Let's try some more complex operations: The following are the default settings. r(0,x,y)=x g(0,x,y)=y b(0,x,y)=(x+y)/2 @{"View picture" link "pictures/1.3.c/main"} Try clicking in some places of the project window after having rendered, and look in the message window.. In the second chapter we will do more theory than pratic. When I give an expression, you can write it in a field (it does not matter which one). You can empty the other or let them. Then click render. @endnode @node 21 "2.1: Four operators" Starting from now, I will explain all functions available in picFX, as well as some words about their use in picture manipulation Note that there are functions, like log or sinh, where the exact meaning of the function has no importance, just the form of the curve is interesting. There are others, like atan where you may either be interested in the form of the curve (I have used it once for making a spread where the colour changes in quite a narrow place) or in the actual 'use' of that function. And remember that you can use @{"Implicit function" link 33} to know what a function's curve looks like. The four basic operators are a+b; a-b; a*b; a/b: I guess you know what they do :-) Use (x-a) instead of x in a function to move the graph a pixel to the @{u}right@{uu}. (x+a) Does the same to the left.. Use (x/a) instead of x to @{u}enlarge@{uu} the graph a times. (x*a) reduces by a times The same are of course valid for y. Remember that they act in the inverse of what they seem to do. For example, For t fixed, x/2 gets t when x is 2t. For t fixed, x-1 gets t when x is t+1... I will speak about this a little again in the linear transformation part of section @{"4.3: linear and non-linear transformations" link 43} @endnode @node 22 "2.2: Trigonometric functions" Note that trigonometric functions require a floating point mode All trigonometric functions in picFX use radient angles. I invite you to forget about degrees. I have to say you that, except to tell the coordinates on the earth (or in astronomy) as longitude and latitude (decl/r.-a.), I do never use degrees at school, for personal research or whatever else. Never. In radient, the "right" angle (is it so we say? I mean the one between two perpendicular lines, 90°) is pi/2, the flat angle (=180°) is pi, and the full angle (=360°) is 2*pi. When you draw a part (an arc) of a circle (radius 1), the length of the path is equal to the angle between the two bounds, from the center. And, of course, the length of the circumference of a circle of radius one is... 2pi. If you don't know what sin, cos, tan, asin, acos or atan mean... Imagine an object moving in circle, for example the Moon around the Earth, or the Earth around the Sun. Trigonometric functions allow us to know at which coordinate the object is at a given time. If the circle has a radius of one, the horizontal coordinate is cos(a), a being the angle the object has moved since being on the right, equal to the length of the path it made. sin(a) is the vertical coordinate. As you can guess, these functions both vary between -1 and 1. To see how the function looks like, put "y+sin(x/10)*10-128" in a function field and render. @{"View picture" link "pictures/2.2.a/main"} If you draw a vertical line @{u}tangent@{uu} to the circle on the right, and then imagine there is a light in the center of the circle, then tan(a) gives the vertical coordinate of the "shadow" of the turning object, the crossing point of the tangent and a line coming from the center and going throught the moving point. tan(ąpi/2) is undefined (infinite) because at this moment the "shadow" does not reach the tangent, and the line going through the point is vertical and therefore parralel to the tangent.. Note that when the point is on the left half (cos(a)<0), the shadow does not actually reach the tangent. You are forced to use the line going through the point and the center :-) See the appearance of the tangent curve using y+tan(x/10)*10-128, or 25500/((y+tan(x/20)*20-128)^2+100). acos() is the inverse function of cos(). You give the horizontal coordinate and you get the angle. Obviously, acos(x) is not defined if x is bigger than one or smaller than -1... asin() is the inverse of sin(). atan() is the inverse of tan(). This function is defined for any value. You say where is the "shadow" and you get the angle... It always returns a value between -pi/2 and +pi/2, with most of the change around zero.. End of definitions :-) As sin and cos return a value between -1 and 1, it is often (when your expression is the (co)sine of something and nothing else) needed to multiply it (127), and add a constant (128) to make it take the [0;255] range. So use sin(...)*127+128. Use the sine if you want to have height lines of some function, a line each 2pi, starting at pi/2. For example, sqrt((x-128)^2+(y-128)^2) (try it!, maybe putting "255-" at the beginning) is a kind of cone centered on (128;128), and the sine of it makes concentrical circles... @{"View picture" link "pictures/2.2.b/main"} sin(x/2)*127+128 makes vertical lines; you can think of x/2 to be a little slope raising in your direction to the right... The other most handful use of the sine is to make waves. Do two dimensional waves with sin(x)*sin(y). You can also use addition, but note that it will return something within [-2;2] instead of [-1;1]... Do not hesitate to put sines inside sines. Try for example @{fg shine}sin(y/2+10*sin(x/10))*127+128@{fg text} @{"View picture" link "pictures/2.2.c/main"} The cosine function is basically the same as the sine, only with a little (pi/2) shift to the left. Tangent. I use this if I want to have some regular areas separated by thin lines where sudden and chaotic changes occur. Try for example the above one, where you add a tangent of the horizontal coordinate (and divided the sinx by two, too): @{fg shine}sin(tan(x/17)+y/4+5*sin(x/10))*127+128@{fg text} Also try this tangent of the vertical coordinate to which I added the sine of the horizontal:@{fg shine}tan(y/10+sin(x/5))*2+128@{fg text}. A little variation:@{fg shine}tan(y/10+sin(x/10))*40+128@{fg text} @{"View pictures" link "pictures/2.2.d/main"} Because of their annoying limitation to the [-1;1] range, the asin and acos function are not really useful. (Actually asin(2) and other are defined but not real... wait picFX version supporting complex numbers :-) ArcTangent, however can be quite useful if you want a quite sudden colour change, however smooth. Try @{fg shine}atan((y-128)/8)/pi*255+128@{fg text} A general formula would be: atan(f(x,y))/pi*255+128: dark when f(x,y) is negative and bright when f(x,y) is positive. The change occur when f(x,y) is around zero. This actually shows the solutions of an inequation.. I will come again on this when speaking of implicit functions. Remember that it outputs a value between -pi/2 and pi/2... If you feel ready, you can already try to experiment all sorts of functions using trig. functions, you can get really nice pictures just with the three sin, tan, atan functions. As a rule: Do not be afraid to write things that you would think stupid ;-). (Btw, Divisions by zero, arc[co-]sines on undefined values do not make picFX crash :-) @endnode @node 23 "2.3: min, max, modulo, abs, neg" I will introduce five new functions in this section: min(a,b), max(a,b), a%b, abs(a) and neg(a) min(a,b): this outputs the smallest value between @{b}a@{ub} and @{b}b@{ub}. max(a,b): outputs the biggest one. a%b: a modulo b (remainder of the division of a by b). You will usually have b constant. abs(a) is |a|, the absolute value of a, ie the same number when it is positive, and the opposite when it's negative... neg(a) is -a. (But faster than it, as -a is interpreted as 0-a by picFX...) The min/max functions can be used to fix a bound to some function, having one of the two parameters constant. Try the following, applying a lowest bound to a sine curve (so we take the *largest* value between the bound and the sine!!) @{fg shine}(y-128)+max(sin(x/4)*4,-1)@{fg text} @{"View picture" link "pictures/2.3.a/main"} (In the picture, I put as green component the same formula without the max, to better view the bound..) But using two different functions for the argument is very common as well. For instance if you built separately several parts of a picture and want to put them together, you'll usually use the max() function for that... You'll see an example in the implicit function chapter.. @{b}Modulo@{ub} a%b is always a number between zero (included) and b (not included), and will be the remainder, as said above, of the division of a by b. As you may expect, having b smaller or equal to zero is a nonsense :-) (Actually, it returns zero in picFX) To see what this does, Look at this @{"picture" link "pictures/2.3.b/main"} where I drew a sine curve in green and the same sine curve having a modulo in magenta.. picFX automatically applies a modulo 256 to all the functions you give to it (to stay in the 0-255 range), and you can use % to modify this behaviour a bit (eg if you want to have modulo 200 instead of modulo 256, then you put (if necessary) your whole expression between brackets and add %200 after it... You can also use modulo to have sort of "stairs", ie when you substract to some variable its modulo in some base. Eg u-(u%1) outputs the integer part of u... Try the following functions, that use this in both dimensions (ie, with x and y): r(x,y)=@{fg shine}1+x-(x%16)@{fg text} g(x,y)=@{fg shine}1+y-(y%16)@{fg text} b(x,y)=@{fg shine}1+(x+y)/2-((x+y)/2%16)@{fg text} @{"View picture" link "pictures/2.3.c/main"} @{b}Absolute Value@{ub} This function can either be used in its actual meaning, but I usually use it to make some sudden change. For instance, (x*2+abs(x))/2 is equal to x when x is negative, and equal to 2*x otherwise. So the change is really sudden. Look at the following two pictures. I got the first one using the absolute value function and the other one with arctangent ( x*(atan(x)/(pi/2)) being similar two abs(x), with a less rough transition) @{"Absolute" link "pictures/2.3.d/main"} @{"ArcTangent" link "pictures/2.3.e/main"}... Finally note that abs() can easily be simulated with max(x,-x), and that both min and max can be simulated with the absolute functions: max(a,b) = (a+b+abs(b-a))/2. So, in all cases, you can freely choose if you want to use abs() or min/max(). But usually one of them is clearly better and faster... @endnode @node 24 "Other functions (angle(), #?h(), r(), g() and b())" @{u}Summary@{uu} angle(h,v): the argument (angle), in radians, of vector (h,v). sinh(a); cosh(a); tanh(a) are the hyperbolic functions. The inverses (a...) are available but do not work yet. a^b: Raise a to power b. Use sqrt(a) instead of a^0.5 for square root. ln(t), log(t,b) r(c,x',y');g(c,x',y');b(c,x',y'): Inter-referencing. The possibilities of these functions are infinite and exposed in chapter three. c is a project number, it must be a constant. (x';y') is the coordinate that will be read. Operators priority is %; +; -; *; /; ^. When having the same operator several times, the left-most one has more priority. Example: a/b^2/c is read as a/((b^2)/c), which equals to "ac÷b˛". This is @{u}not@{uu} as we usually read them. Take care! @{b}Angle(h,v)@{ub} This function takes an horizontal distance as first argument and a vertical one as second argument. It returns a number between -pi and pi and some undefined value when both arguments are zero. I have found two important uses to this function. The actual reason why I created this function was for spirals. You calculate the sine of the sum of the angle and the distance. You get the angle with angle(dx,dy) and the distance with sqrt(dx^2+dy^2) Try the following equation and try to change the constants in it: sin(angle(x-128,y-128)+sqrt((x-128)^2+(y-128)^2)/3)*127+128 @{"View picture" link "pictures/2.4.a/main"} If you replace the power of two by power of a larger even number (eg ten), you'll notice that your spiral gets square (you also have to adjust the other numerical constants for that). If you multiply the angle() function in the above equation by a whole number, you will get more than one spiral on the same picture (actually as many as the number you multiplied by) If you add a constant somewhere inside the sine function, it will make your spiral turn. If you multiply or divide the distance by some constant, you can set the distance between two rings of the spiral. Note that instead of putting the angle() function on its own, you can put some function of it, you can do polar graphics. For instance take its sine (having multiplied it by a sufficiently large number). You would get a picture @{"like this" link "pictures/2.4.b/main"}... If you want to have only one ring, then use implicit functions. The other main use of the angle function is precisely for implicit functions. If you want to get only the results of the equation on some area of the picture, then you can use an arctangent of the angle function, maybe having first taken its absolute value. More on this in the implicit function chapter. sinh(), cosh(), tanh() are hyperbolic functions. Their actual meaning will probably never be of any use for picFX, so I won't explain it here. (Well, shortly, it's the same as trigonometric function sin cos tan, except that we are not moving on a circle but on the right branch an hyperbola) Their mathematical definition is sinh(z)=˝(e^z-e^(-z)), cosh(z)=˝(e^z+e^(-z)) and tanh=sinh/cosh. What is important to see here is the exponentials e^... They cause the cosh and sinh functions to take really big values once the argument is a bit large. sinh(x) looks a bit like x^3, ie it has the same sign as x and sinh(0) equals 0, and sinh(-x)=-sinh(x) (ie the graph is symmetric through the center). cosh(x) is roughly equal to abs(sinh(x)), except that cosh(0) equals one, ie cosh is always bigger than one. The physicians would tell you that the hyperbolic cosine is the equation of the chain, ie you hold a chain between your two hands, and the curve, looking like a parabolic but *not* a parabolic is a hyperbolic cosine :-) Well, this interesting feature has no use in picFX, unless you want to draw realistic strings or chains in your picture [but remember then to take care of the perspective] ;-) I use sinh and cosh very rarely. It is possible to replace a square somewhere by an hyperbolic cosine, just for fun...(But remember that cosh(x) is @{u}always@{uu} bigger or equal to one, while x^2 goes down to zero!) tanh, however, is always between -1 and 1, and can replace very handfully arctangent, as you do not have to divide by pi every time you use it :-) (You can think of arctangent as being roughly equal to the sign of x (-1 or +1) but with a smooth change near zero...) Mmh. Well. You have a headache? Shall I dear to speak about the ^ (power) operator before we go to easy stuff again? :-) You know, the main reason why I created picFX was just to have fun, and I invite you to have fun as well... If you feel tired just write anything in the fields (I mean, anything but still mathematical :-) and look what comes, then try to transform it a little until it reaches to something nice... ... Welcome again ;-) a^b, as said above, is a to the power of b. Usually you will have b constant, eg for calculating the square of some value (eg for the famous Pythagoras theorem, when calculating the distance between two points). When working in floating point mode, you can have b fractional. Note that a negative number raised to a fractional power is not defined as a real number and therefore returns some random value in picFX Raising a variable to a large even number is gives near of (but superior to) zero when between -1 and +1, gives one on these two bounds and yields very large values out of this [-1,1] range. If you do something like adding one and doing the inverse, you will get about one between -1 and +1, and about zero elsewhere. (Then you can multiply an expression by this to put everything out of this strip to zero...) @{"This picture" link "pictures/2.4.c/main"} shows this function when using 20 as exponent... And raising to an uneven number is the same except that the value is negative when the variable is negative. sqrt(z) is equivalent to z^0.5 but is faster and more accurate (and works in 32bits mode) ln(z), log(z,b) are the inverse functions of the exponential. For instance, log(y,10) is the inverse of x^10, which means that log(y,10)=x is equivalent to x^10=y. For instance, log(1000,10) is three (as 10^3=1000)... (b is the base. log(z,b) being for z the inverse of b^x; ln(z) is equal to log(z,e)). b must be positive and different from one... ˇ ln(z) is not defined (in real numbers) when z is smaller or equal to zero. ˇ ln(z) is equal to zero when z equals one, and runs towards minus infinity when z gets near of zero, and slowly grows when z gets a little larger. ˇ On @{"this picture" link "pictures/2.4.d/main"}, you see the graph of the function, horizontal is the value you give to z and vertical is the value of the logarithm. @endnode @node 31 "3.1: Monochrome graphs" When you have r=g=b (e.g. (153,153,153)) you will have a colour between black and white, i.e. a shade of grey. In this part we will study monochrome graphs [because it is easier to understand what happens]. I will only give you single functions. Write it as red component and set green and blue to copy the red one. First example, a funny fractal-like picture: If you do (x*y)/255, You will have a kind of diagonal spread, black in the top left corner and brighter in the bottom right one. But because modulo 256 is used when it gets too big, you do not see anymore this spread but some other stange structure. Try it with x*y. Try then to put multiply it by some constant, like three (x*y*3) or bigger ones (15,31,...). You will remark that when you are quite near from a power of two, the picture seems regular but is actually quite chaotic. Second example, some circles to go on... If you write x^2+y^2, you will get the square of the distance from the top left corner, which will quickly overcome 255. You will see some other (quite unexpected) circles appear. These other circles are not actually "real", but are caused by the fact that the program does not calculate all possible places but only the one that correspond to whole values of x and y (I.e. it does for instance calculate the pixels (37;0) and (38;0) but nothing between, like (37.5;0).) Replace now x by (128-x) and y by (128-y): (128-x)^2+(128-y)^2. You may wonder why I asked you to do that, since there is no visible change. Actually the only "real" circle is now at the center and the other are "fake". If you divide by something, for example 4:((128-x)^2+(128-y)^2)/4, you will see what I mean. You can see some circles that seem to appear over the "real" ones. These are the "fakes" that seemed so "real" before... Calculate now the square root of the previous expression: sqrt((128-x)^2+(128-y)^2) Now the circles are gone and the colour of pixel is the distance to the center (Thanks to Pythagore ;-) @{"View picture" link "pictures/3.1.a/main"} Let's have some fun with the trig functions. Keep the functions you had before, but put a sine on it: sin(sqrt((128-x)^2+(128-y)^2)). You should see ugly black and white rings. This is because the sine function varies between -1 and 1. So try now to multiply it by 127 and add 128 (the [-1;1] range changes now to [1;255]). Now you can at last see the circles. But how tight they are, they cause a headache, don't they? But it is possible to make the gap quite wider, dividing the contents of the sine function by a constant. Try the following: sin(sqrt((128-x)^2+(128-y)^2)/5)*127+128. (Now the distance to the center varies slower than before and the rings are therefore more spaced. To have some fun, try now adding y to the whole function (replacing 128): sin(sqrt((128-x)^2+(128-y)^2)/5)*127+y. This is like if the whole rings had been a little bent, and overcoming 255 (or getting under zero) in some places. @{"View picture" link "pictures/3.1.b/main"} Third example, looking in the water. We'll start with almost the same function than before, the rings, but a lot tighter: sin(sqrt((128-x)^2+(128-y)^2)*2)*127+128 Save this somewhere (e.g. create a new project) and try the following before we go on: sin(x/5)*sin(y/5)*127+128 Once you have seen it, let's go back to the previous one, adding sin(x/5)*sin(y/5)*5 to the contents of the sine: sin(sqrt((128-x)^2+(128-y)^2)*2+sin(x/5)*sin(y/5)*5)*127+128 @{"View picture" link "pictures/3.1.c/main"} You can now have fun changing some constants. E.g. change the *2 (right after the square root) by *3 and change sin(x/5)*sin(y/5) by sin(x/5)*sin(y/6).. Last example, some abstract art. Let's go back to our concentric rings: cos(sqrt((x-128)^2+(y-128)^2)/5)*127+128 Replace +128 by +x+y. You will see the effect you already know. Now add some little waves: (cos(sqrt((x-128)^2+(y-128)^2)/5)+sin(x/5)*sin(y/5))*127+x+y This makes some funny effect Multiplying these little waves by (y-100)/100 breaks the symetry and makes the picture more interesting: (cos(sqrt((x-128)^2+(y-128)^2)/5)+sin(x/5)*sin(y/5)*(y-100)/100)*127+x+y @{"View picture" link "pictures/3.1.d/main"} @endnode @node 32 "3.2: Implicit functions" An implicit function is an equation containing both x and y variable, and every solution (for x and y) of this equation is a part of the graph. For instance, the equation of a simple circle would be: x^2+y^2=1. All (x;y) points satisfying this equation are on the graph of this function, which is a circle centered at (0;0) and of radius one. Putting the whole equation in the left part of the equation (at the left of the "=") makes a function f(x,y)=0. You can very easily turn an explicit ( y=f(x) ) equation into implicit form: f(x)-y=0. picFX lets you "enlight" the graph of any implicit equation. Look at the steps below: 1) First adapt your equation so that it suits your project (remember that x goes from zero to the width of the project, usually 255; y does the same) Once you have your equation f(x,y)=0, we will work on the left expression and forget about the "=0". 2) Since f(x,y) can be either negative or positive, raise it to square (unnecessary if you're sure that it will never happen). You will then have f(x,y)^2 [You can try rendering your function here. It is usually some chaothic stuff, but you will sometimes get some interesting results] 3) Now add a constant [We'll name it k]. Depending how "steep" your function is near its zeros, that constant may have to be quite big (upto one million sometimes). You will anyway have to change this constant once it is finished, so that the result suits your needs. Now we are sure that the function is always equal to or bigger than this constant. f(x,y)^2+k 4) Let's do now the inverse of this function (It will be very small, except near the places where the original function is near zero, where it will get near 1/k). 1/(f(x,y)^2+k) 5) To use the full [0-255] range, multiply by k*255 k*255/(f(x,y)^2+k) Ok. Now you know how to do it, you may either try it at once (If you can't wait ;-) or do what I suggest step by step. Let's try a some conics for a start. 1) A simple equation would be: (128-x)^2+((128-y)/2)^2=1000 Substracting one thousand makes: (128-x)^2+((128-y)/2)^2-1000=0 2) Take care here: We know that x^2+y^2/4 is always positive. However now we substracted the squared radius (1000), it may go upto -1000, so we will have to raise to square: ((128-x)^2+((128-y)/2)^2-1000)^2 You can try rendering it now. It will look completely random trash. However if you divide it by e.g. 100, you will see the implicit graph in black. 3) Since we do not know yet how many give to k, let's try e.g. ten: ((128-x)^2+((128-y)/2)^2-1000)^2+10 4 and 5) 2550/(((128-x)^2+((128-y)/2)^2-1000)^2+10) Render this and you will see that there are only some lonesome pixels. This means that we will have to make k quite bigger: 2550000/(((128-x)^2+((128-y)/2)^2-1000)^2+10000) Now it is quite nicer. Replacing the "+" by a "-" [between the two squared x and y] makes an hyperbola: Write it in the green field and let the red like before. And if you try the opposite (y^2-x^2 instead of x^2-y^2), you will get the other one: [I removed here the "/2" to have a more regular picture] r=2550000/(((128-x)^2+(128-y)^2-1000)^2+10000) g=2550000/(((128-x)^2-(128-y)^2-1000)^2+10000) b=2550000/(((128-y)^2-(128-x)^2-1000)^2+10000) @{"View Picture" link "pictures/3.2.a/main"} Second example, Descartes' folium: 1) The generic equation is: x^3+y^3-3*a*x*y=0, with a a real number bigger than zero. As usual, to have our center at (128;128), we shall replace x by (x-128) and y by (y-128): (x-128)^3+(y-128)^3-3*a*(x-128)*(y-128) As it require some fine tuning, I give you directely my solution: 51000/((((x-128)^3+(y-128)^3)/1000-(x-128)*(y-128)/5)^2+200) You can have fun putting different k values for each colour component: r=510000/((((x-128)^3+(y-128)^3)/1000-(x-128)*(y-128)/5)^2+2000) g=255000/((((x-128)^3+(y-128)^3)/1000-(x-128)*(y-128)/5)^2+1000) b=25500/((((x-128)^3+(y-128)^3)/1000-(x-128)*(y-128)/5)^2+100) @{"View Picture" link "pictures/folium.jpeg"} Last example, Bernouilli's Lemniscate: The generic equation is (x^2+y^2)^2=a^2*(x^2-y^2) Once adapted to our needs (I have set a=100 and k=1000000 and then divided the f(x,y) expression by 1000 to keep small enough numbers), this equation gets: 255000000/((((128-x)^2+(128-y)^2)^2/1000-10*((128-x)^2-(128-y)^2))^2+1000000) @{"View Picture" link "pictures/3.2.b/main"} @endnode @node 41 "4.1: Having fun with colours" We will now start studying inter-referencing, i.e. accessing another picture to render a project. In this section we will only modify the colours of the original pictures. Open any picture (if possible some colourful one, @{u}no black and white@{uu}) with the change size mode. Then create a new project, with same size than the original picture. Set the following equations, just to copy the original picture.. Here each pixel of project one will read the same pixel of project zero. r(1,x,y)=r(0,x,y) g(1,x,y)=g(0,x,y) b(1,x,y)=b(0,x,y) We will now create a negative version of project one. As each component range from 0 to 255, just do 255-x to have the negative version: r(1,x,y)=255-r(0,x,y) g(1,x,y)=255-g(0,x,y) b(1,x,y)=255-b(0,x,y) Now, black and white. To have the intensity of a colour, make the mean value of all components (r+g+b)/3 and apply it to all components of the resulting picture to get black 'n' white result: r(1,x,y)=(r(0,x,y)+g(0,x,y)+b(0,x,y))/3 g(1,x,y)=(r(0,x,y)+g(0,x,y)+b(0,x,y))/3 b(1,x,y)=(r(0,x,y)+g(0,x,y)+b(0,x,y))/3 Note that each component has the same equation, so I can only render the red one and copy it to other components, applying Copy Red output mode to green and blue. Now, what about dividing the colour by its intensity? This sometimes results in strange effects, but it sometimes may look nice... Create a new project so that picFX will not have to recalculate the intensity (we will just reuse the result of previous rendering). When you divide a colour component by its intensity (and multiply by 85 to have something to see ;-), the whole picture will have the same intensity (except black areas).: r(2,x,y)=85*r(0,x,y)/r(1,x,y) g(2,x,y)=85*g(0,x,y)/r(1,x,y) b(2,x,y)=85*b(0,x,y)/r(1,x,y) * I have always read the red component of project one because it is supposed to be black and white, but it doesn't matter, actually. * If you want to avoid the ugly gray you obtain were it was black (zero divided by zero outputs some random value), replace r(1,x,y) by max(1,r(1,x,y)). (or r(1,x,y)+1). * You may wonder why I chose 85 as factor? this is because the a component may be upto three times bigger than the mean intensity, this is why I multiplyed by 255/3. Raise it a little if you do not have any plain (x,0,0) colour. @{u}Colour Extraction@{uu} Suppose you have a picture where you would like to apply an effect only at some precise places, being e.g. blue. You would have to use what I call a mask, i.e. a picture of intensity 255 or as high as possible where we want the effect to act, and lower elsewhere. We will first try to get one precise colour, i.e. only that exact colour will be in the mask and everything else will be out. This is quite easy. You can for example take a magicWB backdrop (I took MarbleSpecky, for instance). I did not include it in the distribution for copyright reasons. I suggest to first look for colours that are higher than the one we want and then the ones that are lower. We will then add these two masks. If you write min(constant,colourcomponent), all colours @{u}smaller@{uu} than the constant will be "highered" to this value, and when the result is different from constant, the original value was bigger. And max(cte+1,min(cte,rgb)) will be cte+1 if rgb is bigger than cte, and cte if it isn't. Substract then cte: max(cte+1,min(cte,rgb))-cte and you will have either zero or one. Doing the same for an upper bound, just do it the other way round: min(cte-1,max(cte,rgb))-cte. If we add these two values, we will get: if rgb < cte, 0 +(-1)=-1 (which will get 255 when rendered!) if rgb = cte, 0 + 0 = 0 if rgb > cte, 1 + 0 = 1. We can then do this for each colour component, and the mask should be where the resulting colour is black (i.e. 0,0,0): r(1,x,y)=max(r+1,min(r,r(0,x,y)))+min(r-1,max(r,r(0,x,y)))-2*r g(1,x,y)=max(g+1,min(g,g(0,x,y)))+min(g-1,max(g,g(0,x,y)))-2*g b(1,x,y)=max(b+1,min(b,b(0,x,y)))+min(b-1,max(b,b(0,x,y)))-2*b Of course you have to replace r, g and b by the components you want (click in the window of project zero to read the colour under the pixel!) Then create a new project (number two), which will contain the actual mask: r(2,x,y)=min(1,r(1,x,y)+g(1,x,y)+b(1,x,y))) * multiply by 255 if you want to see what your mask looks like, but do not forget to divide again when you want to combine them! * only one component is required, so you can either let the classic "x", "y" in the two others or copy the whole expression in all three fields (but it will be thrice slower!) You will see later how to combine project, but if you're eager to know it, try the following: r(1,x,y)=r(3,x,y)*r(2,x,y)+r(0,x,y)*(1-r(2,x,y)) g(1,x,y)=g(3,x,y)*r(2,x,y)+g(0,x,y)*(1-r(2,x,y)) b(1,x,y)=b(3,x,y)*r(2,x,y)+b(0,x,y)*(1-r(2,x,y)) project zero is still our source picture; project one was used to create the mask. Since it is not used anymore, we can put the resulting picture there (please recycle ;-) project two is the mask (I assumed it was only zero/one here, i.e. that you didn't multiply it by 255. If you did, just divide the whole expression by 255 (or whatever you put)). project three is the picture you want to put where the (r,g,b) colour of project one was. Now some hints if you want to do some more complicated stuff, extracting colour having some precise features (e.g. having red component between 100 and 150 and so on). Note that masks are not required to be full/none, there can be intermediary values in this mask (i.e. this mask works a little like an alpha channel). * You have already seen how to get a single colour, you can use it to take colours between wider bounds: c(1,x,y)=max(l+1,min(l,c(0,x,y)))+min(h-1,max(h,c(0,x,y)))-l-h c is the component you are working on (r,g or b) and l,h are the bounds between which (bounds included) the colour must be. The disadvantage of this way is that the mask is full and then suddenly zero when we get though a bound. It would be better to have a slighter slope. * You can either use the way I showed you with implicit equations, an equation of the form k/((x-c)^2+l) (k is a factor, x is the variable, here something like r(0,x,y); c is the value where we want the mask to be the higher and l lets you adjust the slope. (The maximum value comes when x gets to c and is k/l) * You can also use something like max(0,l-k*(x-c)^2): here l is the maximum value you want it to have (usually 255!), k is a factor, the bigger it is the quicker you will get to zero. X and c are as before. This one has the advantage to have really no mask when you get far enough from c, unlike the previous. You can work with each components separately, like I did before, but you can also combine them (e.g. to get the intensity). If all this is not enough to let you make the mask you want, you can try to use the x and y vars. (e.g. if the mask you want is only at a given distance from a point, make a second mask that contains just this circle and multiply both to get what you want. You can also use another paint package to draw your mask, if really necessary. @endnode @node 42 "43.2: Convolutions" First, some math: I will try to explain clearly what a convolution products between two matrix works is :-) Let's first name the matrixes m1 and m2, and m is the destination. Let's also give coordinates to both matrixes. So m1(2,3) is item of column two and row three. m(x,y) = @{u}the sum of all products of the form@{uu} [m1(x1,y1)*m2(x2,y2), where x1+x2 = x and y1+y2 = y]. Don't panic, you'll understand with examples ;-) There is no function in picFX for convultion products, so you will have to do everything 'by hand' and writing unfinishing expressions ( :-( ). That function might come once, even if it causes problems to code ;-) You usually will have a picture which will be the first matrix (m1) and a smaller one (m2) usually not overcoming size 3×3. The destination matrix m is the resulting picture. If you try to do a convolution between two pictures or larger matrix, you will quickly be overwhelmed by the size of the expressions ;-) We will in this section work on two projects: one source-image and a destination project. I will reference them as projects zero and one. All three components will always have the same formulas (just replacing r by g and then b ;-), so I will only tell you the red one. A general formula would be: r(1,x,y) = r(0,x,y)*m2(0,0)+ r(0,x+1,y)*m2(1,0)+ ... + r(0,x+l,y)*m2(l,0)+ r(0,x,y+1)*m2(0,1)+ ... + r(0,x+i,y+j)*m2(i,j) + ... r(0,x+l,y+n)*m2(l,n). Here, 1 is dest. project and 0 is source. l and n are size of the m2 matrix. Note that you can of course remove some constant to each x and y, to avoid having the picture 'moving' to the lower right. Ok, enough math for now ;-) let us go on with nice pictures :-) Choose a colourful and contrasted picture for the first project (0), so that you can easily see where all parts have gone, and make the second picture the same size as the first one. * Let's begin with the classic Blur effect. |@{b}1@{ub} 1| We will do with m2= 1/3 | | -> Here the sum of all values of the matrix is one ( 1/3(1+1+1+0)=3/3 ), so the luminance of the resulting picture will be similar to the source's. |1 0| -> All values are positive, and it is what we call 'high pass' matrixes, which usually blurs the picture. This yields to: r(1,x,y)=(r(0,x,y)*1+r(0,x+1,y)*1+r(0,x,y+1)*1+r(0,x+1,y+1)*0)/3, and after simplifications: r(1,x,y)=(r(0,x,y)+r(0,x+1,y)+r(0,x,y+1))/3 If you want some better effect, you can also add the lower-right pixel: r(0,x+1,y+1). And if you want to give some more weight to a pixel, multiply it by two or even three. Do not forget to adjust the divider at the end to the sum of all factors in order to keep the lightness. You could for example give some horizontal blur (to give a movement effect): m2 = 1/6( 3 2 1) and the expression gets: r(1,x,y)=(3*r(0,x,y)+2*r(0,x+1,y)+r(0,x,y))/6 * Now to the (also classic) relief effect. Note that it may be better to first turn the picture to black and white before doing it. Just try and see. The idea is to calculate the difference between the current pixel and the ones that are near it, with the following matrix (horizontal relief): m2 = (1 -1). Two things to see here: First the sum is zero, so all values will be around zero. Let's add 128 to the final expression to correct this. Secondly, there's a negative value and m2 is a 'low-pass' matrix, which sometimes raises the contrast of the picture r(1,x,y)=r(0,x,y)-r(0,x+1,y)+128 To have two-dimensional relief, you add horizontal and vertical relief: |@{b}2@{ub} -1| m2 = | |, and... |-1 0| r(1,x,y)=2*r(0,x,y)-r(0,x,y+1)-r(0,x+1,y)+128 I let you play with these values (e.g. try multiplying the expression (before +128) by a constant to increase/decrease the effect. Just take care to set a floating point mode when doing this, and avoid overcoming 256 or going under zero, it wouldn't look nice... (even if you can use the min()/max() functions, but in these case the relief will be somehow "wrong". To see a picture with more contrast, you can somehow add that 'relief' matrix to (1 0 0 0), this will let you see better where colour changes, but plain areas will keep the same colour. It is quite a nice effect. |@{b}4@{ub} -1| m2 = | | -> here the sum is one, so you won't need to add 128. |-1 -1| r(1,x,y)=4*r(0,x,y)-r(0,x+1,y)-r(0,x,y+1)-r(0,x+1,y+1) It is possible, as I said above, to have the origin (the term in bold) elsewhere: | 0 1 0 | | | m2 =1/5 | 1 @{b}1@{ub] 1 | -> it has theorically (with 'real' matrix convolution product) no importance where that bold char is, but here, as m1 and m have the same size, you have to 'clip' a part of the resulting theoric picture. | | | 0 1 0 | r(1,x,y)=(r(0,x,y-1)+r(0,x-1,y)+r(0,x,y)+r(0,x+1,y)+r(0,x,y+1))/5 @endnode @node 43 "4.3: linear and non-linear transformations" * Linear transformations: These are of the form r(1,x,y)=r(0,ax+by+c,a'x+b'y+c'), with a,b,c,a',b',c' constants. Some simple examples: r(0,y,x) is a symetry through the diagonal (y=x) line. r(0,x/2,y/2) doubles the size of the image (you will only see the top left quarter) r(0,x+y/2,y-x/2) rotates (and reduces) the picture a little, 'round the top-left corner. I can't really give you some general formulas for these, I will let you experiment yourself. Just remember that you may replace x by (x-t) or y by (y-u), or both, to move the "center" of the transformation. And: r(1,x,y)=r(0,ax+by+c,ay-bx+d) does always a regular rotation (no other linear distortion, like (x,y+x/2) would do). * Non-linear transformations They are all other transformations of the form r(1,x,y)=r(0,f(x,y),g(x,y)), where you put what you want as f and g. Note that convolution is not a simple non-linear transformation, because it accesses the inter-referencing functions several times. Here it is even worse than the linear transformation. Although I can give you some tricks, there are no general formula to make you get a precise thing. You have to guess :-). Compute the distance to a specific point (X;Y) (remember that you can read the coordinates of a given point clicking on a project window!) with: sqrt((x-X)^2+(y-Y)^2) And the argument (angle) to that point (X;Y). Use the internal function angle: angle(x-X,y-Y). You will get a number between 0 and 2pi. @endnode @node 51 "5.1: Selection masks" We have always applied the effects to the whole bitmap. It will sometimes be useful to use an intermediary project to "filter" this effect: Example: You have two projects 0 and 1, a mask in project 2 and want to draw the result in project 3: 0:AA 1:BB 2:01 will result in 3:AB In this example, you could have obtained project one applying some effect to project zero. This works a bit like an alpha channel, project two holding that alpha value (of course projects one and two may be two completely different loaded pictures). Let's say that a value of zero in project 2 results in outputing the value of project 0 and a value of 255 outputs project 1. To read from zero, you put r(0,x,y)*(255-r(2,x,y))/255, and reading from one is r(1,x,y)*r(2,x,y)/255 Note that if you only use 0 or 255 (i.e. none or full alpha), you may make your mask have only values zero and one, so that you aren't required to divide by 255 everytime. Note well that r(r(2,x,y),x,y) is not allowed, as the project number must be a @{u}constant value@{uu}! (Because of freezing problems) Well, now add the two values, to obtain: (r(0,x,y)*(255-r(2,x,y))+r(1,x,y)*r(2,x,y))/255 In all components, you obtain: r(3,x,y)=(r(0,x,y)*(255-r(2,x,y))+r(1,x,y)*r(2,x,y))/255 g(3,x,y)=(g(0,x,y)*(255-r(2,x,y))+g(1,x,y)*r(2,x,y))/255 b(3,x,y)=(b(0,x,y)*(255-r(2,x,y))+b(1,x,y)*r(2,x,y))/255. You may also read the different components from the mask, if you want, instead of having one mask for the whole picture. To obtain these masks, you can use colour extraction (@{"chapter 4.1" link 41}) Some mathematic formula (e.g. implicit graphes, @{"chapter 3.3" link 33}, but anything else is ok :-)) You can combine these two ways. If for example you did some colour extraction and got some superfluous areas, you can try to find a function that covers everything but these areas and multiply both. If you want to add masks (a bit like a logic OR), use the max() function between them. Another way to do it is to multiply their inverses, something like r(2,x,y)=255-(255-r(0,x,y))*(255-r(1,x,y) And to apply a mask to a transformation, you can 'multiply the changes' by c(k,x,y)/255, replace c and k accordingly. This is mostly useful for instance with blur and other convolution effects, and mayve with colour effects. @endnode @node 52 "5.2: Translation masks" This method consists in having a "translation project", which contains how much a pixel has to move in the source project. (Actually it works the other way round, it indicates how far you have to go to fetch the pixel data). You can then do effects that would either be very difficult or awfully slow to do directely. In all these examples, project zero is the source project, one is the mask and two is the destination. If you want a linear translation, only one component of the mask will be used, and multiplied by different constanty horizontally and vertically: r(2,x,y)=r(0,x+u*r(1,x,y)/255,y+v*r(1,x,y)/255) g(2,x,y)=g(0,x+u*r(1,x,y)/255,y+v*r(1,x,y)/255) b(2,x,y)=b(0,x+u*r(1,x,y)/255,y+v*r(1,x,y)/255) The translation will always be done in the direction of vector (u,v). (Of course, I let you replace u/255 by a single value; you may also add a constant to x and y) Two dimensional translation is easy, just read two different components of the mask (here, r and g): r(2,x,y)=r(0,x+k*r(1,x,y),y+k*g(1,x,y)) g(2,x,y)=g(0,x+k*r(1,x,y),y+k*g(1,x,y)) b(2,x,y)=b(0,x+k*r(1,x,y),y+k*g(1,x,y)) It is possible to load pictures as a mask, pictures you might have done with other paint or picture manipulation programs. @endnode @node 53 "5.3: Absolute masks" As you can do relative masks, you can also do absolute ones, the only problem being that you can't do so with picture bigger than 256x256. The advantage is that you can sometimes see better what you are doing. I have done a mosaic-like picture once using absolute mask. As you would have guessed, you use expressions like r(0,r(1,x,y),g(1,x,y)). I let you explore this if you want :-) @endnode