LHS=RHS,
The Left Hand Side (LHS) tells you what is being given a value. The Right Hand Side (RHS) specifies the value to give it. These are separated by an equals sign with no white space on either side. Finally, each statement except the last ends with a comma to separate it from the next one. I'm not sure how robust the parser is, so it's best to keep each statement as one line.
There are two flavors of LHS. Some of them only take a number for a RHS. Others only take expressions enclosed in double quotes. Don't confuse the two.
A comment can be on a line by itself, or can follow a statement. (It obviously can't precede a statement, because then the statement would get treated as part of the comment and be thrown out.)
Aspc=0,
or
Aspc=1,
The first component of any DeltaField is the aspect ratio statement. It controls how the DF sees the screen coordinates when the screen is not perfectly square (i.e. almost all the time). Aspc can be set to zero or one.
If the aspect ratio is 0, then the lower left corner of the screen is at X=-1,Y=-1, and the upper right corner is at X=1,Y=1, no matter what the actual shape of the screen. I often use Aspc=0 for DFs that I want to fit the screen fully. The advantage is that, once you get the DF tuned to fit the screen at one size and shape, it will always still fit the screen at every other size and shape too. The disadvantage is that your DF will change shape if the screen is a different size than what you expected. For example, you might design something circular, and on someone else's machine it will become elliptical.
If the aspect ratio is 1, then the shortest dimension of the screen (usually the vertical or Y dimension) will get the range -1 to +1, and the other dimension will get whatever range it needs for each pixel to be "square". So for example on a 640x480 screen, Y would run from -1 to +1, but X would run from -640/480 = -4/3 to +4/3. The advantage is that shapes will be preserved, and the central square of the screen will look exactly as you intended, but the area outside of that square will be of different sizes on different machines and can sometimes be hard to predict.
As a general rule of thumb, use Aspc=1 if your DF creates a simple or regular pattern whose behavior outside of the central square can be easily predicted and is acceptable to you, or if you want to preserve shape exactly. Use Aspc=0 if you want your DF to always fill up the same fraction of the screen or have the same relationship to the edges of the screen.
Note that Aspc takes only a single digit without quotes! Also remember that it should always be the first statement in the DF.
A variables are only evaluated once, when the DF loads. (They will get re-evaluated again if the DF loads again, but that usually would be many minutes later.) They can use any of the built-in functions except for the ones related to the sound or to the location of the pixel. They can also use other A variables which have previously been set.
One curious thing is that, when you're setting a variable, you use its name in upper case, but when you're using it later, you use its name in lower case. This can be confusing at first. But here's a simple example:
A0="rnd(1)", // a random number between 0 and 1
A1="1-a0",
Because the A variables only get evaluated once, they should only be used for computation that is the same for every pixel. Also, for clarity, you should group all your A variables together and put them before your D variables (which get evaluated later).
D variables are not required. It is OK for your DF not to have any.
I'd like to emphasize the point that the DeltaField specifies where flowing data comes from, not where it goes to. It's easy for a beginner to think the opposite. Each pixel on the screen gets a single entry in the DF table after it's computed, so it can only come from one place. However, multiple pixels can easily grab their data from the same place. In math terms, a DF can implement a one-to-one mapping or a one-to-many mapping, but not a many-to-one or many-to-many mapping. (Our Most Assiduous Reader will note that most fractals are many-to-one mappings, which I've just said G-Force DeltaFields cannot implement, and yet I myself have written DFs for Julia sets and other fractals. Be patient, all will become clear in a bit.)
It doesn't matter which order you specify srcX and srcY, or srcR and srcT, but they should always be after any A or D variables.
Vers=209
Many people just put 100, assuming that their DF will be backwards compatible with earlier versions of G-Force. This is presumptuous unless they have actually tested the DF with those versions. So, I don't recommend this practice, although many of my early DFs do it (I was coding by example).
Aspc=0,
srcX="x",
srcY="y",
Vers=100Even though nothing flows in this DF, it can still look interesting if the WaveShape drawing onto it moves.
An alternate way of coding Nowhere would use:
srcR="r",
srcT="theta",
Recall that srcX specifies the X coordinate of the pixel we get our intensity from. So, if we want intensity to flow to the left, it must be coming from the right, or the direction of more positive X. So we can get a constant rate of flow, the same for all pixels, by specifying:
srcX="x + 0.01",
This will cause each pixel to get its data from a pixel that is slightly to its right, and so patches of intensity will creep slowly leftward. How slowly? Well, it depends on the framerate and the size of the screen. For a square screen (or if Aspc=0), the screen width is 2.0, so it will take about 200 frames for something to go from the right edge to the left edge. If the frame rate is 25 frames per second, that should take about 8 seconds. But you usually don't need to be making calculations like that; it's enough to know that increasing the number will make it flow faster and decreasing it will make it flow slower. Once you have a DF more or less working, you can play with the parameters until it feels right.
If you had trouble with some of these functions in high school (or if you haven't gone to high school yet :-), don't worry. It's actually a lot easier to understand them when you can see what they're doing, and G-Force gives you a simple and beautiful way to do that. You may find that you like trigonometry a lot better when it's making pretty pictures for you than you did when it was just squiggles on a blackboard.
| Add a and b, return the sum | |
| Subtract b from a, return the difference | |
| Multiply a times b, return the product | |
| Divide a by b, return the quotient | |
| a modulo b, divide a by b and return the remainder. (This may only work well if a and b are integers, not sure.) | |
| Raise a to the b power. Equivalent to exp(b*log(a)) except that a=0 is legal (log(0) is undefined). |
Note that there is also the so-called "unary minus" operator, which is a minus sign put in front of a number or variable. Although this uses the same character as the binary subtraction operator, in practice it's hard to confuse them. "-b + a" is the same thing as "a - b".
This is especially important when using operators which have differing priorities. Traditionally, exponentiation "binds tighter" than multiplication and division, which in turn bind tighter than addition and subtraction. So for example "a*b+c" is understood to mean "(a*b) + c", and "a*b^c" to mean "a * (b^c)". If you really want "a*(b+c)", you must use the parentheses. (Well, you could write "a*b+a*c", but this takes two multiplications instead of one and will make your DF load more slowly because the CPU will have to do more work to interpret it.) The rest of the functions either come with their own parentheses, or don't even take an argument, so they're not subject to this sort of confusion.
| Absolute value. x if x>0 and -x if x<0. Equivalent to x*sgn(x). | |
| Sign of x. 1 if x≥0 and -1 if x<0. | |
| 0 if x<0, x if 0<x<1, and 1 if x>1. |
More soon ...