comment {
dmj3-pub.uxf 3.0
Transformations for Ultra Fractal 3
by Damien M. Jones
August 18, 2006
For more information about this formula collection,
please visit its home page:
http://www.fractalus.com/ultrafractal/dmj3-pub-uf.htm
Most (all?) of the transformations in this file
have little or no redeeming mathematical value,
but are great for artistic control over fractal
images.
}
dmj3-fBm-Distort {
;
; fBm-based distortions.
; This provides one-dimensional distortion based
; on fBm textures. However it has three fBm sources
; that can be combined in various ways.
;
; Note: this code is pulled from the Traps coloring
; formula and contains many references to traps.
; Since these formulas are undergoing continual
; development, I did not take the time to remove all
; the references to traps, since this complicates
; keeping the formulas in sync.
;
transform:
; trap transfer arrays
;
int trapdistancetransfers[3]; holds trap distance transfer functions
trapdistancetransfers[0] = @trap1distancetransfer
trapdistancetransfers[1] = @trap2distancetransfer
trapdistancetransfers[2] = @trap3distancetransfer
float trapdistanceprescales[3]; holds trap distance pre-transfer multipliers
trapdistanceprescales[0] = @trap1distanceprescale
trapdistanceprescales[1] = @trap2distanceprescale
trapdistanceprescales[2] = @trap3distanceprescale
; float trapdistancepostscales[3]; holds trap distance post-transfer multipliers
; trapdistancepostscales[0] = @trap1distancepostscale
; trapdistancepostscales[1] = @trap2distancepostscale
; trapdistancepostscales[2] = @trap3distancepostscale
BOOL trapnonegdistances[3]; flags indicating whether negative distances are allowed
trapnonegdistances[0] = @trap1nonegdistance
trapnonegdistances[1] = @trap2nonegdistance
trapnonegdistances[2] = @trap3nonegdistance
BOOL trapeveryiter[3]; flags indicating whetner transfer applies at every iteration
trapeveryiter[0] = @trap1everyiter
trapeveryiter[1] = @trap2everyiter
trapeveryiter[2] = @trap3everyiter
; trap fBm arrays
;
BOOL trapfBmenables[3]; flags indicating whether fBm is enabled for a trap
trapfBmenables[0] = @trap1fBmenable
trapfBmenables[1] = @trap2fBmenable
trapfBmenables[2] = @trap3fBmenable
; BOOL trapfBmenables1[3]; flags indicating actual fBm enabled setting in user interface
; BOOL trapfBmenables2[3]; flags indicating whether fBm is enabled for a trap, in pseudo-coordinate mode
; BOOL trapfBmuseasdistorts[3]; flags indicating whether fBm is used for distortion
; int trapfBmdistortionstyles[3]; holds fBm distortion styles
; float trapfBmdistortionstrengths[3]; holds fBm distortion strengths
; float trapfBmdistortionangles[3]; holds fBm distortion angles
; complex trapfBmdistortioncenters[3]; holds fBm distortion center offsets
; BOOL trapfBmuseasadders[3]; flags indicating whether fBm is used for coloring
; float trapfBmadderscales[3]; holds fBm adder scales
; int trapfBmadderstages[3]; holds fBm adder usage stage
complex trapfBmtransforms[3]; holds fBm transformation modes
trapfBmtransforms[0] = @trap1fBmtransform
trapfBmtransforms[1] = @trap2fBmtransform
trapfBmtransforms[2] = @trap3fBmtransform
complex trapfBmoffsets[3]; holds fBm offsets
trapfBmoffsets[0] = @trap1fBmoffset
trapfBmoffsets[1] = @trap2fBmoffset
trapfBmoffsets[2] = @trap3fBmoffset
float trapfBmscales[3]; holds fBm scales
trapfBmscales[0] = @trap1fBmscale
trapfBmscales[1] = @trap2fBmscale
trapfBmscales[2] = @trap3fBmscale
float trapfBmbiases[3]; holds fBm biases
trapfBmbiases[0] = @trap1fBmbias
trapfBmbiases[1] = @trap2fBmbias
trapfBmbiases[2] = @trap3fBmbias
float trapfBmaspects[3]; holds fBm aspects
trapfBmaspects[0] = @trap1fBmaspect
trapfBmaspects[1] = @trap2fBmaspect
trapfBmaspects[2] = @trap3fBmaspect
float trapfBmrots[3]; holds fBm rotations
trapfBmrots[0] = @trap1fBmangle / 90.0
trapfBmrots[1] = @trap2fBmangle / 90.0
trapfBmrots[2] = @trap3fBmangle / 90.0
; float trapfBmrotitersteps[3]; holds fBm rotation steps for each fractal iteration
float trapfBmscalesteps[3]; holds fBm scale steps
trapfBmscalesteps[0] = @trap1fBmscalestep
trapfBmscalesteps[1] = @trap2fBmscalestep
trapfBmscalesteps[2] = @trap3fBmscalestep
float trapfBmrotsteps[3]; holds fBm rotation steps for each fBm iteration
trapfBmrotsteps[0] = @trap1fBmanglestep / 90.0
trapfBmrotsteps[1] = @trap2fBmanglestep / 90.0
trapfBmrotsteps[2] = @trap3fBmanglestep / 90.0
float trapfBmoctaves[3]; holds fBm octaves
trapfBmoctaves[0] = @trap1fBmoctaves
trapfBmoctaves[1] = @trap2fBmoctaves
trapfBmoctaves[2] = @trap3fBmoctaves
float trapfBmpowers[3]; holds fBm noise exponents
trapfBmpowers[0] = @trap1fBmpower
trapfBmpowers[1] = @trap2fBmpower
trapfBmpowers[2] = @trap3fBmpower
float trapfBmamplifiers[3]; holds fBm amplifiers
trapfBmamplifiers[0] = @trap1fBmamplify
trapfBmamplifiers[1] = @trap2fBmamplify
trapfBmamplifiers[2] = @trap3fBmamplify
complex fBmrots[3]; holds complex multipliers to perform rotations for fBm
fBmrots[0] = (0,1) ^ trapfBmrots[0]
fBmrots[1] = (0,1) ^ trapfBmrots[1]
fBmrots[2] = (0,1) ^ trapfBmrots[2]
complex fBmoctaverots[3]; holds complex multipliers to perform rotations for fBm
fBmoctaverots[0] = (0,1) ^ trapfBmrotsteps[0]
fBmoctaverots[1] = (0,1) ^ trapfBmrotsteps[1]
fBmoctaverots[2] = (0,1) ^ trapfBmrotsteps[2]
; complex fBmdistortrots[3]; holds complex multipliers to perform rotations for fBm
float fBmsums[3]; holds fBm noise results
complex z2 = #pixel; copy of pixel coordinates
complex fBmpoint = (0,0)
complex v = (0,0)
float pointdistortionamount = 1.0
float pointdistortionangle = @distortionangle / 90.0
float fBmiter
complex feedbackangle = (1,0)
float pointdistortionanglestep = @distortionanglestep / 90.0
IF (@screenrelative)
IF (#width > #height)
float aspect = #width/#height
z2 = (real(#screenpixel)/#width*aspect) + flip(imag(#screenpixel)/#height)
ELSE
float aspect = #height/#width
z2 = (real(#screenpixel)/#width) + flip(imag(#screenpixel)/#height*aspect)
ENDIF
ENDIF
; compute fBm values
;
int h = 0
WHILE (h < @distortionfeedback)
int j = 0; start with first trap
WHILE (j <= 2); still need to evaluate a trap
IF (trapfBmenables[j]); yes, fBm is enabled
IF (trapfBmtransforms[j] == 0); rectangular coordinates
fBmpoint = z2 * trapfBmscales[j] * fBmrots[j] + trapfBmoffsets[j]
ELSEIF (trapfBmtransforms[j] == 1) ; polar coordinates
fBmpoint = (cabs(z2) + flip(atan2(z2))) * trapfBmscales[j] * fBmrots[j] + trapfBmoffsets[j]
ENDIF
IF (trapfBmaspects[j] != 1.0); aspect adjustment required
fBmpoint = real(fBmpoint) + flip(imag(fBmpoint) * trapfBmaspects[j]); apply aspect
ENDIF
float fBmsum = 0.0
float fBmweight = 1.0
float k = trapfBmoctaves[j]
WHILE (k > 0)
; determine integer coordinate for corners of square
; surrounding p
float bx0 = floor(real(fBmpoint)) % 256
float by0 = floor(imag(fBmpoint)) % 256
IF (bx0 < 0)
bx0 = bx0 + 256
ENDIF
IF (by0 < 0)
by0 = by0 + 256
ENDIF
float bx1 = (bx0 + 1) % 256
float by1 = (by0 + 1) % 256
float rx0 = real(fBmpoint) - floor(real(fBmpoint))
float ry0 = imag(fBmpoint) - floor(imag(fBmpoint))
float rx1 = rx0 - 1
float ry1 = ry0 - 1
; create a "random" index for each corner
; (this is where Intel's version differs from Perlin's;
; I used Intel's version because it doesn't require a
; pre-computed random table, which is difficult to manage
; in UF.)
float b00 = (bx0^trapfBmpowers[j] % 65536 + by0)^trapfBmpowers[j] % 65536
float b10 = (bx1^trapfBmpowers[j] % 65536 + by0)^trapfBmpowers[j] % 65536
float b01 = (bx0^trapfBmpowers[j] % 65536 + by1)^trapfBmpowers[j] % 65536
float b11 = (bx1^trapfBmpowers[j] % 65536 + by1)^trapfBmpowers[j] % 65536
; produce a "random" vector for each corner
float g_b00_0 = (b00)^trapfBmpowers[j]*0.25 % 512 - 256
float g_b10_0 = (b10)^trapfBmpowers[j]*0.25 % 512 - 256
float g_b01_0 = (b01)^trapfBmpowers[j]*0.25 % 512 - 256
float g_b11_0 = (b11)^trapfBmpowers[j]*0.25 % 512 - 256
float g_b00_1 = (b00+1)^trapfBmpowers[j]*0.25 % 512 - 256
float g_b10_1 = (b10+1)^trapfBmpowers[j]*0.25 % 512 - 256
float g_b01_1 = (b01+1)^trapfBmpowers[j]*0.25 % 512 - 256
float g_b11_1 = (b11+1)^trapfBmpowers[j]*0.25 % 512 - 256
; normalize each vector
float d = 0.0;
d = 1 / sqrt(sqr(g_b00_0) + sqr(g_b00_1))
g_b00_0 = g_b00_0 * d
g_b00_1 = g_b00_1 * d
d = 1 / sqrt(sqr(g_b10_0) + sqr(g_b10_1))
g_b10_0 = g_b10_0 * d
g_b10_1 = g_b10_1 * d
d = 1 / sqrt(sqr(g_b01_0) + sqr(g_b01_1))
g_b01_0 = g_b01_0 * d
g_b01_1 = g_b01_1 * d
d = 1 / sqrt(sqr(g_b11_0) + sqr(g_b11_1))
g_b11_0 = g_b11_0 * d
g_b11_1 = g_b11_1 * d
; produce colors for each corner
float u1 = rx0 * g_b00_0 + ry0 * g_b00_1
float v1 = rx1 * g_b10_0 + ry0 * g_b10_1
float u2 = rx0 * g_b01_0 + ry1 * g_b01_1
float v2 = rx1 * g_b11_0 + ry1 * g_b11_1
; interpolate between corners using
; bilinear filtering
float sx = sqr(rx0) * (3 - rx0*2)
float sy = sqr(ry0) * (3 - ry0*2)
float a = u1 + sx*(v1-u1)
float b = u2 + sx*(v2-u2)
fBmiter = (a + sy*(b-a))
IF (trapeveryiter[j])
; apply trap transfer function
; a function is not used because these are floats
; and not all functions apply to floats
fBmiter = fBmiter * trapdistanceprescales[j]
IF (trapdistancetransfers[j] == 1); log
fBmiter = log(fBmiter)
ELSEIF (trapdistancetransfers[j] == 2); sqrt
fBmiter = sqrt(abs(fBmiter))
ELSEIF (trapdistancetransfers[j] == 3); cuberoot
fBmiter = (fBmiter)^(1/3)
ELSEIF (trapdistancetransfers[j] == 4); exp
fBmiter = exp(fBmiter)
ELSEIF (trapdistancetransfers[j] == 5); sqr
fBmiter = sqr(fBmiter)
ELSEIF (trapdistancetransfers[j] == 6); cube
fBmiter = (fBmiter)^3
ELSEIF (trapdistancetransfers[j] == 7); sin
fBmiter = sin(fBmiter)
ELSEIF (trapdistancetransfers[j] == 8); cos
fBmiter = cos(fBmiter)
ELSEIF (trapdistancetransfers[j] == 9); tan
fBmiter = tan(fBmiter)
ELSEIF (trapdistancetransfers[j] == 10); sinc (modified form)
IF (fBmiter == 0)
fBmiter = 1
ELSE
fBmiter = sin(#pi*fBmiter)/fBmiter
ENDIF
ENDIF
; fBmiter = fBmiter * trapdistancepostscales[j]
; eliminate negative distances
IF (trapnonegdistances[j]); absolute value only
fBmsum = abs(fBmsum)
ENDIF
ENDIF
IF (k < 1.0)
fBmsum = fBmsum + fBmiter*fBmweight*k
ELSE
fBmsum = fBmsum + fBmiter*fBmweight
ENDIF
fBmweight = fBmweight * trapfBmscalesteps[j]
fBmpoint = fBmpoint * fBmoctaverots[j] / trapfBmscalesteps[j]
k = k - 1
ENDWHILE
IF (!trapeveryiter[j])
; apply trap transfer function
; a function is not used because these are floats
; and not all functions apply to floats
fBmsum = fBmsum * trapdistanceprescales[j]
IF (trapdistancetransfers[j] == 1); log
fBmsum = log(fBmsum)
ELSEIF (trapdistancetransfers[j] == 2); sqrt
fBmsum = sqrt(abs(fBmsum))
ELSEIF (trapdistancetransfers[j] == 3); cuberoot
fBmsum = (fBmsum)^(1/3)
ELSEIF (trapdistancetransfers[j] == 4); exp
fBmsum = exp(fBmsum)
ELSEIF (trapdistancetransfers[j] == 5); sqr
fBmsum = sqr(fBmsum)
ELSEIF (trapdistancetransfers[j] == 6); cube
fBmsum = (fBmsum)^3
ELSEIF (trapdistancetransfers[j] == 7); sin
fBmsum = sin(fBmsum)
ELSEIF (trapdistancetransfers[j] == 8); cos
fBmsum = cos(fBmsum)
ELSEIF (trapdistancetransfers[j] == 9); tan
fBmsum = tan(fBmsum)
ELSEIF (trapdistancetransfers[j] == 10); sinc (modified form)
IF (fBmsum == 0)
fBmsum = 1
ELSE
fBmsum = sin(#pi*fBmsum)/fBmsum
ENDIF
ENDIF
; fBmsum = fBmsum * trapdistancepostscales[j]
; eliminate negative distances
IF (trapnonegdistances[j]); absolute value only
fBmsum = abs(fBmsum)
ENDIF
ENDIF
fBmsum = fBmsum*trapfBmamplifiers[j] + trapfBmbiases[j]; scale and add bias
IF (j == 0)
IF (@trap1fBmuse == 0); distortion strength
pointdistortionamount = fBmsum
ELSEIF (@trap1fBmuse == 1); distortion angle
pointdistortionangle = fBmsum + pointdistortionangle
ELSEIF (@trap1fBmuse == 2); fBm 2 scale
trapfBmscales[1] = fBmsum
ELSEIF (@trap1fBmuse == 3); fBm 2 bias
trapfBmbiases[1] = fBmsum
ELSEIF (@trap1fBmuse == 4); fBm 2 aspect
trapfBmaspects[1] = fBmsum
ELSEIF (@trap1fBmuse == 5); fBm 2 rotation
fBmrots[1] = (0,1) ^ fBmsum
ELSEIF (@trap1fBmuse == 6); fBm 2 scale iter.
trapfBmscalesteps[1] = fBmsum
ELSEIF (@trap1fBmuse == 7); fBm 2 rot. iter.
trapfBmrotsteps[1] = fBmsum
ELSEIF (@trap1fBmuse == 8); fBm 2 octaves
trapfBmoctaves[1] = fBmsum
ELSEIF (@trap1fBmuse == 9); fBm 2 exponent
trapfBmpowers[1] = fBmsum
ELSEIF (@trap1fBmuse == 10); fBm 3 scale
trapfBmscales[2] = fBmsum
ELSEIF (@trap1fBmuse == 11); fBm 3 bias
trapfBmbiases[2] = fBmsum
ELSEIF (@trap1fBmuse == 12); fBm 3 aspect
trapfBmaspects[2] = fBmsum
ELSEIF (@trap1fBmuse == 13); fBm 3 rotation
fBmrots[2] = (0,1) ^ fBmsum
ELSEIF (@trap1fBmuse == 14); fBm 3 scale iter.
trapfBmscalesteps[2] = fBmsum
ELSEIF (@trap1fBmuse == 15); fBm 3 rot. iter.
trapfBmrotsteps[2] = fBmsum
ELSEIF (@trap1fBmuse == 16); fBm 3 octaves
trapfBmoctaves[2] = fBmsum
ELSEIF (@trap1fBmuse == 17); fBm 3 exponent
trapfBmpowers[2] = fBmsum
ENDIF
ELSEIF (j == 1)
IF (@trap2fBmuse == 0); distortion strength
pointdistortionamount = fBmsum
ELSEIF (@trap2fBmuse == 1); distortion angle
pointdistortionangle = fBmsum + pointdistortionangle
ELSEIF (@trap2fBmuse == 2); fBm 3 scale
trapfBmscales[2] = fBmsum
ELSEIF (@trap2fBmuse == 3); fBm 3 bias
trapfBmbiases[2] = fBmsum
ELSEIF (@trap2fBmuse == 4); fBm 3 aspect
trapfBmaspects[2] = fBmsum
ELSEIF (@trap2fBmuse == 5); fBm 3 rotation
fBmrots[2] = (0,1) ^ fBmsum
ELSEIF (@trap2fBmuse == 6); fBm 3 scale iter.
trapfBmscalesteps[2] = fBmsum
ELSEIF (@trap2fBmuse == 7); fBm 3 rot. iter.
trapfBmrotsteps[2] = fBmsum
ELSEIF (@trap2fBmuse == 8); fBm 3 octaves
trapfBmoctaves[2] = fBmsum
ELSEIF (@trap2fBmuse == 9); fBm 3 exponent
trapfBmpowers[2] = fBmsum
ENDIF
ELSEIF (j == 2)
IF (@trap3fBmuse == 0); distortion strength
pointdistortionamount = fBmsum
ELSEIF (@trap3fBmuse == 1); distortion angle
pointdistortionangle = fBmsum + pointdistortionangle
ENDIF
ENDIF
ELSE
float fBmsum = 0.0
ENDIF
fBmsums[j] = fBmsum
j = j + 1
ENDWHILE
; now compute distortion
;
complex c = @distortioncenter
float s = @distortionstrength * pointdistortionamount
complex r = (0,1) ^ (pointdistortionangle + h * pointdistortionanglestep) * feedbackangle
IF (@distortionstyle == 0); radial distortion
v = (#pixel-c)/cabs(#pixel-c) * r; use vector based on angle to distortion center
ELSEIF (@distortionstyle == 1); linear distortion
v = r; just use rotation vector
ENDIF
#pixel = #pixel + v * s * 0.5
h = h + 1
IF (h < @distortionfeedback)
IF (@screenrelative)
z2 = z2 + v * s * 0.5
ELSE
z2 = #pixel
ENDIF
IF (@distortionloopstyle == "90° clockwise")
z2 = (z2 - @distortionloopcenter) * (0,-1) + @distortionloopcenter
feedbackangle = feedbackangle * (0,-1)
ELSEIF (@distortionloopstyle == "90° counter-clockwise")
z2 = (z2 - @distortionloopcenter) * (0,1) + @distortionloopcenter
feedbackangle = feedbackangle * (0,1)
ELSEIF (@distortionloopstyle == "popcorn")
z2 = flip(z2 - @distortionloopcenter) + @distortionloopcenter
feedbackangle = flip(feedbackangle)
ENDIF
ENDIF
ENDWHILE
default:
title = "fBm Distort"
helpfile = "dmj3-pub\dmj3-pub-uf-fbmdistort.htm"
; distortion parameters
;
; param distortionsource
; caption = "Distortion Strength Source"
; default = 1
; enum = "fixed value" "source 1" "source 2" "source 3"
; hint = "Selects the source for distortion strength, the amount the noise distorts the image."
; endparam
param distortionstyle
caption = "Distortion Style"
default = 0
enum = "radial" "linear"
hint = "This selects whether the distortion will be focused \
around a single point, or directed along a line."
endparam
param distortionstrength
caption = "Distortion Strength"
default = 1.0
hint = "This is the amount of pushing or pulling on the image to do. When strength comes from\
an fBm source, it acts as a multiplier, controlling the overall amount of distortion."
endparam
; param distanglesource
; caption = "Distortion Angle Source"
; default = 0
; enum = "fixed value" "source 1" "source 2" "source 3"
; hint = "Selects the source for distortion angle, the angle to rotate the distortion."
; endparam
param distortionangle
caption = "Distortion Angle"
default = 0.0
hint = "This is the angle to rotate the distortion."
endparam
; param distortionanglescale
; caption = "Distortion Angle Scale"
; default = 360.0
; hint = "Scales the range of angles extracted from an fBm source."
; visible = (@distanglesource != 0)
; endparam
complex param distortioncenter
caption = "Distortion Center"
default = #center
hint = "Sets the center of distortion."
endparam
int param distortionfeedback
caption = "Feedback Iterations"
default = 1
min = 1
hint = "Sets the number of times distortion will be calculated. Use values greater than 1 for special feedback effects."
endparam
float param distortionanglestep
caption = "Distortion Angle Step"
default = 0.0
visible = (@distortionfeedback > 1)
hint = "Sets the amount the distortion angle will rotate with each feedback iteration."
endparam
param distortionloopstyle
caption = "Repeat Transform Style"
default = 0
enum = "normal" "90° clockwise" "90° counter-clockwise" "popcorn"
hint = "Sets the transformation to use at each repeat step."
visible = (@distortionfeedback > 1)
endparam
complex param distortionloopcenter
caption = "Repeat Rotation Center"
default = #center
hint = "Sets the center of rotation for repeated transformation."
visible = (@distortionfeedback > 1 && @distortionloopstyle != "normal")
endparam
param screenrelative
caption = "Screen-Relative Coordinates"
default = false
hint = "If enabled, distortion will be based on pixel coordinates \
rather than previously-transformed coordinates. This will \
allow you to do varied distortion even when using a \
transform that maps different parts of the screen to the \
same area of the complex plane (e.g. mosaic, kaleidoscope) \
but the distortion will not be zoomable."
endparam
; fBm Parameters
;
heading
caption = "Source 1 fBm Parameters"
endheading
param trap1fBmenable
caption = "Use fBm Source 1"
default = true
hint = "Check this if you want to use this fBm source."
endparam
; param trap1fBmuseasdistort
; param trap1fBmdistortionstyle
; param trap1fBmdistortionstrength
; param trap1fBmdistortionangle
; param trap1fBmdistortioncenter
; param trap1fBmuseasadder
; param trap1fBmadderscale
; param trap1fBmadderstage
param trap1fBmtransform
caption = "fBm Transform"
default = 0
enum = "rectangular" "polar"
hint = "Selects the transform mode for orbit values used in fBm calculations."
visible = (@trap1fBmenable)
endparam
param trap1fBmoffset
caption = "fBm Offset"
default = (0,0)
hint = "Sets the offset of the fBm pattern."
visible = (@trap1fBmenable)
endparam
param trap1fBmscale
caption = "fBm Scale"
default = 1.0
hint = "Sets the scale of the fBm pattern. Larger values give a finer grain."
visible = (@trap1fBmenable)
endparam
param trap1fBmbias
caption = "fBm Bias"
default = 0.0
hint = "This constant is added to the result of the fBm noise. fBm noise typically produces \
values from -0.5 to 0.5; in some cases negative values will be a problem, so you can use \
this setting (try 1) to bias the results and always get a positive value."
visible = (@trap1fBmenable)
endparam
param trap1fBmaspect
caption = "fBm Aspect"
default = 1.0
hint = "Sets the aspect ratio of the fBm pattern. You can use this to stretch \
or squeeze the fBm pattern."
visible = (@trap1fBmenable)
endparam
param trap1fBmangle
caption = "fBm Rotation"
default = 0.0
hint = "Sets the angle, in degrees, of the fBm pattern."
visible = (@trap1fBmenable)
endparam
; param trap1fBmangleiterstep
; caption = "fBm Rot. Step"
; default = 0.0
; hint = "This is the angle, in degrees, that the fBm pattern will rotate with \
; each fractal iteration."
; visible = (@trap1fBmenable)
; endparam
param trap1fBmscalestep
caption = "fBm Scale Iter."
default = 0.5
hint = "This is the amount the fBm pattern is scaled by with each fBm iteration."
visible = (@trap1fBmenable)
endparam
param trap1fBmanglestep
caption = "fBm Rot. Iter."
default = 37.0
hint = "This is the angle, in degrees, the fBm pattern is scaled by with each \
fBm iteration."
visible = (@trap1fBmenable)
endparam
param trap1fBmoctaves
caption = "fBm Octaves"
default = 3.0
min = 1.0
max = 16.0
hint = "This sets the number of fBm iterations to perform with each fractal \
iteration. Increasing this number can drastically affect rendering \
speed, but gives a more detailed fBm texture."
visible = (@trap1fBmenable)
endparam
param trap1fBmpower
caption = "fBm Exponent"
default = 2.0
hint = "This is an exponent used in the random number generator. Different \
values will give different noise patterns. Try using values near 2.0."
visible = (@trap1fBmenable)
endparam
; Transfer Parameters
;
param trap1distancetransfer
caption = "fBm Transfer"
default = 0
enum = "linear" "log" "sqrt" "cuberoot" "exp" "sqr" "cube" \
"sin" "cos" "tan" "sinc"
hint = "This function is applied to fBm values"
visible = (@trap1fBmenable)
endparam
param trap1distanceprescale
caption = "fBm Pre-scale"
default = 1.0
hint = "This is a multiplier applied to the fBm value, \
before the fBm Transfer function is applied to it."
visible = (@trap1fBmenable)
endparam
; param trap1distancepostscale
; caption = "fBm Post-scale"
; default = 1.0
; hint = "This is a multiplier applied to the fBm value, \
; after the fBm Transfer function is applied to it."
; visible = (@trap1fBmenable)
; endparam
param trap1nonegdistance
caption = "No Negative Distance"
default = false
hint = "If enabled, eliminates 'negative' distances by taking \
the absolute value of the distance."
visible = (@trap1fBmenable)
endparam
param trap1everyiter
caption = "Apply at Each Iteration"
default = false
hint = "If enabled, the transfer function will be applied \
at each step of the fBm calculation, instead of on \
the final fBm value."
visible = (@trap1fBmenable)
endparam
param trap1fBmuse
caption = "Use Source As"
default = 0
enum = "distortion strength" "distortion angle" \
"fBm 2 scale" "fBm 2 bias" "fBm 2 aspect" "fBm 2 rotation" \
"fBm 2 scale iter." "fBm 2 rot. iter." "fBm 2 octaves" "fBm 2 exponent" \
"fBm 3 scale" "fBm 3 bias" "fBm 3 aspect" "fBm 3 rotation" \
"fBm 3 scale iter." "fBm 3 rot. iter." "fBm 3 octaves" "fBm 3 exponent"
hint = "This sets what the fBm value should be used for."
visible = (@trap1fBmenable)
endparam
param trap1fBmamplify
caption = "fBm Amplifier"
default = 1.0
hint = "Amplifies the fBm value, useful when it's used to control \
other fBm sources."
visible = (@trap1fBmenable)
endparam
; fBm Parameters
;
heading
caption = "Source 2 fBm Parameters"
endheading
param trap2fBmenable
caption = "Use fBm Source 2"
default = false
hint = "Check this if you want to use this fBm source."
endparam
; param trap2fBmuseasdistort
; param trap2fBmdistortionstyle
; param trap2fBmdistortionstrength
; param trap2fBmdistortionangle
; param trap2fBmdistortioncenter
; param trap2fBmuseasadder
; param trap2fBmadderscale
; param trap2fBmadderstage
param trap2fBmtransform
caption = "fBm Transform"
default = 0
enum = "rectangular" "polar"
hint = "Selects the transform mode for orbit values used in fBm calculations."
visible = (@trap2fBmenable)
endparam
param trap2fBmoffset
caption = "fBm Offset"
default = (0,0)
hint = "Sets the offset of the fBm pattern."
visible = (@trap2fBmenable)
endparam
param trap2fBmscale
caption = "fBm Scale"
default = 1.0
hint = "Sets the scale of the fBm pattern. Larger values give a finer grain."
visible = (@trap2fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 2)
endparam
param trap2fBmbias
caption = "fBm Bias"
default = 0.0
hint = "This constant is added to the result of the fBm noise. fBm noise typically produces \
values from -0.5 to 0.5; in some cases negative values will be a problem, so you can use \
this setting (try 1) to bias the results and always get a positive value."
visible = (@trap2fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 3)
endparam
param trap2fBmaspect
caption = "fBm Aspect"
default = 1.0
hint = "Sets the aspect ratio of the fBm pattern. You can use this to stretch \
or squeeze the fBm pattern."
visible = (@trap2fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 4)
endparam
param trap2fBmangle
caption = "fBm Rotation"
default = 0.0
hint = "Sets the angle, in degrees, of the fBm pattern."
visible = (@trap2fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 5)
endparam
; param trap2fBmangleiterstep
; caption = "fBm Rot. Step"
; default = 0.0
; hint = "This is the angle, in degrees, that the fBm pattern will rotate with \
; each fractal iteration."
; visible = (@trap2fBmenable)
; endparam
param trap2fBmscalestep
caption = "fBm Scale Iter."
default = 0.5
hint = "This is the amount the fBm pattern is scaled by with each fBm iteration."
visible = (@trap2fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 6)
endparam
param trap2fBmanglestep
caption = "fBm Rot. Iter."
default = 41.0
hint = "This is the angle, in degrees, the fBm pattern is scaled by with each \
fBm iteration."
visible = (@trap2fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 7)
endparam
param trap2fBmoctaves
caption = "fBm Octaves"
default = 3.0
min = 1.0
max = 16.0
hint = "This sets the number of fBm iterations to perform with each fractal \
iteration. Increasing this number can drastically affect rendering \
speed, but gives a more detailed fBm texture."
visible = (@trap2fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 8)
endparam
param trap2fBmpower
caption = "fBm Exponent"
default = 2.01
hint = "This is an exponent used in the random number generator. Different \
values will give different noise patterns. Try using values near 2.0."
visible = (@trap2fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 9)
endparam
; Transfer Parameters
;
param trap2distancetransfer
caption = "fBm Transfer"
default = 0
enum = "linear" "log" "sqrt" "cuberoot" "exp" "sqr" "cube" \
"sin" "cos" "tan" "sinc"
hint = "This function is applied to fBm values"
visible = (@trap2fBmenable)
endparam
param trap2distanceprescale
caption = "fBm Pre-scale"
default = 1.0
hint = "This is a multiplier applied to the fBm value, \
before the fBm Transfer function is applied to it."
visible = (@trap2fBmenable)
endparam
; param trap2distancepostscale
; caption = "fBm Post-scale"
; default = 1.0
; hint = "This is a multiplier applied to the fBm value, \
; after the fBm Transfer function is applied to it."
; visible = (@trap2fBmenable)
; endparam
param trap2nonegdistance
caption = "No Negative Distance"
default = false
hint = "If enabled, eliminates 'negative' distances by taking \
the absolute value of the distance."
visible = (@trap2fBmenable)
endparam
param trap2everyiter
caption = "Apply at Each Iteration"
default = false
hint = "If enabled, the transfer function will be applied \
at each step of the fBm calculation, instead of on \
the final fBm value."
visible = (@trap2fBmenable)
endparam
param trap2fBmuse
caption = "Use Source As"
default = 0
enum = "distortion strength" "distortion angle" \
"fBm 3 scale" "fBm 3 bias" "fBm 3 aspect" "fBm 3 rotation" \
"fBm 3 scale iter." "fBm 3 rot. iter." "fBm 3 octaves" "fBm 3 exponent"
hint = "This sets what the fBm value should be used for."
visible = (@trap2fBmenable)
endparam
param trap2fBmamplify
caption = "fBm Amplifier"
default = 1.0
hint = "Amplifies the fBm value, useful when it's used to control \
other fBm sources."
visible = (@trap2fBmenable)
endparam
; fBm Parameters
;
heading
caption = "Source 3 fBm Parameters"
endheading
param trap3fBmenable
caption = "Use fBm Source 3"
default = false
hint = "Check this if you want to use this fBm source."
endparam
; param trap3fBmuseasdistort
; param trap3fBmdistortionstyle
; param trap3fBmdistortionstrength
; param trap3fBmdistortionangle
; param trap3fBmdistortioncenter
; param trap3fBmuseasadder
; param trap3fBmadderscale
; param trap3fBmadderstage
param trap3fBmtransform
caption = "fBm Transform"
default = 0
enum = "rectangular" "polar"
hint = "Selects the transform mode for orbit values used in fBm calculations."
visible = (@trap3fBmenable)
endparam
param trap3fBmoffset
caption = "fBm Offset"
default = (0,0)
hint = "Sets the offset of the fBm pattern."
visible = (@trap3fBmenable)
endparam
param trap3fBmscale
caption = "fBm Scale"
default = 1.0
hint = "Sets the scale of the fBm pattern. Larger values give a finer grain."
visible = (@trap3fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 10) && (!@trap2fBmenable || @trap2fBmuse != 2)
endparam
param trap3fBmbias
caption = "fBm Bias"
default = 0.0
hint = "This constant is added to the result of the fBm noise. fBm noise typically produces \
values from -0.5 to 0.5; in some cases negative values will be a problem, so you can use \
this setting (try 1) to bias the results and always get a positive value."
visible = (@trap3fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 11) && (!@trap2fBmenable || @trap2fBmuse != 3)
endparam
param trap3fBmaspect
caption = "fBm Aspect"
default = 1.0
hint = "Sets the aspect ratio of the fBm pattern. You can use this to stretch \
or squeeze the fBm pattern."
visible = (@trap3fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 12) && (!@trap2fBmenable || @trap2fBmuse != 4)
endparam
param trap3fBmangle
caption = "fBm Rotation"
default = 0.0
hint = "Sets the angle, in degrees, of the fBm pattern."
visible = (@trap3fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 13) && (!@trap2fBmenable || @trap2fBmuse != 5)
endparam
; param trap3fBmangleiterstep
; caption = "fBm Rot. Step"
; default = 0.0
; hint = "This is the angle, in degrees, that the fBm pattern will rotate with \
; each fractal iteration."
; visible = (@trap3fBmenable)
; endparam
param trap3fBmscalestep
caption = "fBm Scale Iter."
default = 0.5
hint = "This is the amount the fBm pattern is scaled by with each fBm iteration."
visible = (@trap3fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 14) && (!@trap2fBmenable || @trap2fBmuse != 6)
endparam
param trap3fBmanglestep
caption = "fBm Rot. Iter."
default = 43.0
hint = "This is the angle, in degrees, the fBm pattern is scaled by with each \
fBm iteration."
visible = (@trap3fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 15) && (!@trap2fBmenable || @trap2fBmuse != 7)
endparam
param trap3fBmoctaves
caption = "fBm Octaves"
default = 3.0
min = 1.0
max = 16.0
hint = "This sets the number of fBm iterations to perform with each fractal \
iteration. Increasing this number can drastically affect rendering \
speed, but gives a more detailed fBm texture."
visible = (@trap3fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 16) && (!@trap2fBmenable || @trap2fBmuse != 8)
endparam
param trap3fBmpower
caption = "fBm Exponent"
default = 2.03
hint = "This is an exponent used in the random number generator. Different \
values will give different noise patterns. Try using values near 2.0."
visible = (@trap3fBmenable)
enabled = (!@trap1fBmenable || @trap1fBmuse != 17) && (!@trap2fBmenable || @trap2fBmuse != 9)
endparam
; Transfer Parameters
;
param trap3distancetransfer
caption = "fBm Transfer"
default = 0
enum = "linear" "log" "sqrt" "cuberoot" "exp" "sqr" "cube" \
"sin" "cos" "tan" "sinc"
hint = "This function is applied to fBm values"
visible = (@trap3fBmenable)
endparam
param trap3distanceprescale
caption = "fBm Pre-scale"
default = 1.0
hint = "This is a multiplier applied to the fBm value, \
before the fBm Transfer function is applied to it."
visible = (@trap3fBmenable)
endparam
; param trap3distancepostscale
; caption = "fBm Post-scale"
; default = 1.0
; hint = "This is a multiplier applied to the fBm value, \
; after the fBm Transfer function is applied to it."
; visible = (@trap3fBmenable)
; endparam
param trap3nonegdistance
caption = "No Negative Distance"
default = false
hint = "If enabled, eliminates 'negative' distances by taking \
the absolute value of the distance."
visible = (@trap3fBmenable)
endparam
param trap3everyiter
caption = "Apply at Each Iteration"
default = false
hint = "If enabled, the transfer function will be applied \
at each step of the fBm calculation, instead of on \
the final fBm value."
visible = (@trap3fBmenable)
endparam
param trap3fBmuse
caption = "Use Source As"
default = 0
enum = "distortion strength" "distortion angle"
hint = "This sets what the fBm value should be used for."
visible = (@trap3fBmenable)
endparam
param trap3fBmamplify
caption = "fBm Amplifier"
default = 1.0
hint = "Amplifies the fBm value, useful when it's used to control \
other fBm sources."
visible = (@trap3fBmenable)
endparam
}
dmj-FastMosaic {
;
; This transform can be used to break an image into
; many mosaic pieces, and either have those solid-
; colored or contain pieces of the fractal itself.
;
; In general you want to use as few tiles as you can
; get away with. Fewer tiles allows the algorithm to
; run much faster. Although speedups have been added
; to this version, about 500,000 tiles will make this
; formula run very slowly. (2,500 was the "slow" point
; for previous versions.)
;
; You may also want to re-enable guessing if you've
; turned it off, especially if you're using a small
; number of tiles. Mosaic normally produces large
; areas of solid color, which guessing can speed up.
;
; Because of the shortcuts used to speed up rendernig,
; this transform requires some memory; 1,000,000 tiles
; will require about 40M of RAM.
;
; The original code was much simpler; the algorithm
; was straightforward in that each pixel was checked
; against each tile center to find the closest one.
;
global:
;
; The general idea is to generate, one time, the
; entire list of center points for each tile. But,
; as the list is generated, it is stored in sorted
; order (by X coordinate). This will allow fast
; finding of nearest center point.
;
; We store centerpoints as an array of float instead
; of an array of complex so we don't have to keep
; cutting our tree node numbers in half.
;
float tilecenters[@mostiles*2]; tile center points
float tileangles[@mostiles]; tile rotation angles
int tiletree[@mostiles*2]; tile tree structure
int tileorder[@mostiles*2]; tile ordering
complex center2 = @moscenter
IF (@centermove)
center2 = #center
ENDIF
int i = 0
int j = 0
int k = 0
int l = 0
int rightbranch = 0
float random1 = @seed1
float random2 = @seed2
float random3 = @seed3
complex p = 0
float range = 1.0 / 2147483648.0
tiletree[0] = -1; initialize tree (one leaf node)
tiletree[1] = -1
tileorder[0] = -1; initialize order (one double-endpoint)
tileorder[1] = -1
WHILE (i < @mostiles); still another tile to generate
;
; generate random data
;
random1 = (random1 * 1103515245 + 12345) % 2147483648.0
random2 = (random2 * 1103515245 + 12345) % 2147483648.0
p = center2 + ((random1 - 1073741824) + flip(random2 - 1073741824)) * range * @mosscale
tilecenters[j] = real(p)
tilecenters[j+1] = imag(p)
IF (@mosangle != 0.0); want an angle, too
random3 = (random3 * 1103515245 + 12345) % 2147483648.0
tileangles[i] = random3
ENDIF
;
; find appropriate insertion point in the tree
;
IF (i > 0); have at least one item in the tree
k = 0; current node
WHILE (k >= 0); not at a leaf node
l = k; save current node
IF (real(p) < tilecenters[k] || \
(real(p) == tilecenters[k] && \
imag(p) < tilecenters[k+1])) ; less than current node
k = tiletree[k]; follow left branch
rightbranch = 0
ELSE; greater than current node
k = tiletree[k+1]; follow right branch
rightbranch = 1
ENDIF
ENDWHILE
;
; insert node into tree
;
tiletree[j] = -1; this node is a leaf node
tiletree[j+1] = -1
tiletree[l+rightbranch] = j; point this item to it
;
; insert node into sorted list
;
IF (rightbranch == 0); followed the left branch
IF (tileorder[l] >= 0); not the leftmost node so far
tileorder[tileorder[l]+1] = j; follow it and point it to the new node
ENDIF
tileorder[j] = tileorder[l]; point new node to nodes it's between
tileorder[j+1] = l
tileorder[l] = j; point previous tree node's left to this node
ELSE
IF (tileorder[l+1] >= 0); not the rightmost node so far
tileorder[tileorder[l+1]] = j; follow it and point it to the new node
ENDIF
tileorder[j] = l; point new node to nodes it's between
tileorder[j+1] = tileorder[l+1]
tileorder[l+1] = j; point previous tree node's right to this node
ENDIF
ENDIF
i = i + 1
j = j + 2
ENDWHILE
transform:
;
; At this point, we already have a list of tile
; centers, with forward and backward order links,
; and a tree structure for finding the closest X
; value. For each pixel, find the tile center with
; the closest X value, then scan outwards until
; the tile centers under examination are too far
; (in the X direction) to beat what we already have.
;
int i2 = 0
int j2 = 0
int k2 = 0
int l2 = 0
int i3 = 0
int j3 = 0
int k3 = 0
int l3 = 0
int l4 = 0
BOOL scanleft = TRUE
BOOL scanright = TRUE
float d = 0
float d2 = 0
float closest1 = 1e20
float closest2 = 1e20
float closest3 = 1e20
complex q = 0
complex point1 = 0
complex point2 = 0
complex point3 = 0
float r1 = 0
;
; First, find the closest X value.
;
k2 = 0; current node
WHILE (k2 >= 0); not at a leaf node
l2 = k2; save current node
IF (real(#pixel) < tilecenters[k2] || \
(real(#pixel) == tilecenters[k2] && \
imag(#pixel) < tilecenters[k2+1])) ; less than current node
k2 = tiletree[k2]; follow left branch
ELSE; greater than current node
k2 = tiletree[k2+1]; follow right branch
ENDIF
ENDWHILE
q = tilecenters[l2]+flip(tilecenters[l2+1]) ; tile center (as a complex)
closest1 = |q - #pixel|; save distance
point1 = q; save center
IF (@mosangle != 0.0); want a random angle, too
r1 = tileangles[floor(l2 * 0.5)]; save angle
ENDIF
i2 = tileorder[l2]; left node to check
j2 = tileorder[l2+1]; right node to check
WHILE (scanleft || scanright); still scanning in at least one direction
IF (i2 < 0); hit the leftmost node
scanleft = FALSE; don't scan to the left
ENDIF
IF (scanleft); scanning to the left...
q = tilecenters[i2]+flip(tilecenters[i2+1]) ; tile center (as a complex)
d = sqr(real(q) - real(#pixel)); distance on X axis
IF (d > closest1); X distance alone is greater than our closest
scanleft = FALSE; so nothing else can possibly match
ELSE; well maybe it's closer
d = |q - #pixel|; distance to this tile
IF (d < closest1); new closest value!
closest1 = d; save distance
l2 = i2; save index
point1 = q; save center
IF (@mosangle != 0.0); want a random angle, too
r1 = tileangles[floor(l2 * 0.5)]; save angle
ENDIF
ENDIF
i2 = tileorder[i2]; follow link to the left
ENDIF
ENDIF
IF (j2 < 0); hit the rightmost node
scanright = FALSE; don't scan to the right
ENDIF
IF (scanright); scanning to the right...
q = tilecenters[j2]+flip(tilecenters[j2+1]) ; tile center (as a complex)
d = sqr(real(q) - real(#pixel)); distance on X axis
IF (d > closest1); X distance alone is greater than our closest
scanright = FALSE; so nothing else can possibly match
ELSE; well maybe it's closer
d = |q - #pixel|; distance to this tile
IF (d < closest1); new closest value!
closest1 = d; save distance
l2 = j2; save index
point1 = q; save center
IF (@mosangle != 0.0); want a random angle, too
r1 = tileangles[floor(l2 * 0.5)]; save angle
ENDIF
ENDIF
j2 = tileorder[j2+1]; follow link to the right
ENDIF
ENDIF
ENDWHILE
;
; We now have the location of the closest tile
; (point1) and the index of it (l2). If we're
; doing anything with the seams, we'll need the
; second-closest point, too. We scan left and
; right for it. This time, we don't need the
; angle, we just want to know where the tile is.
;
IF ((@usesolid > 0 && @tileseams > 0.0) || @tileuse > 0 || @mosforce2 != 0.0)
scanleft = TRUE
scanright = TRUE
i3 = tileorder[l2]; left node to check
j3 = tileorder[l2+1]; right node to check
WHILE (scanleft || scanright); still scanning in at least one direction
IF (i3 < 0); hit the leftmost node
scanleft = FALSE; don't scan to the left
ENDIF
IF (scanleft); scanning to the left...
q = tilecenters[i3]+flip(tilecenters[i3+1]) ; tile center (as a complex)
d = sqr(real(q) - real(#pixel)); distance on X axis
IF (d > closest2); X distance alone is greater than our closest
scanleft = FALSE; so nothing else can possibly match
ELSE; well maybe it's closer
d = |q - #pixel|; distance to this tile
IF (d < closest2); new closest value!
closest2 = d; save distance
l3 = i3; save index
point2 = q; save center
ENDIF
i3 = tileorder[i3]; follow link to the left
ENDIF
ENDIF
IF (j3 < 0); hit the rightmost node
scanright = FALSE; don't scan to the right
ENDIF
IF (scanright); scanning to the right...
q = tilecenters[j3]+flip(tilecenters[j3+1]) ; tile center (as a complex)
d = sqr(real(q) - real(#pixel)); distance on X axis
IF (d > closest2); X distance alone is greater than our closest
scanright = FALSE; so nothing else can possibly match
ELSE; well maybe it's closer
d = |q - #pixel|; distance to this tile
IF (d < closest2); new closest value!
closest2 = d; save distance
l3 = j3; save index
point2 = q; save center
ENDIF
j3 = tileorder[j3+1]; follow link to the right
ENDIF
ENDIF
ENDWHILE
IF (@usesolid >= 5 || @tileuse == 2 || @mosforce2 != 0.0)
scanleft = TRUE
scanright = TRUE
i3 = tileorder[l2]; left node to check
j3 = tileorder[l2+1]; right node to check
WHILE (scanleft || scanright); still scanning in at least one direction
IF (i3 < 0); hit the leftmost node
scanleft = FALSE; don't scan to the left
ENDIF
IF (scanleft); scanning to the left...
q = tilecenters[i3]+flip(tilecenters[i3+1]) ; tile center (as a complex)
d = sqr(real(q) - real(#pixel)) ; distance on X axis
IF (d > closest3); X distance alone is greater than our closest
scanleft = FALSE; so nothing else can possibly match
ELSE; well maybe it's closer
d = |q - #pixel|; distance to this tile
IF (d < closest3 && i3 != l3) ; new closest value!
closest3 = d; save distance
l4 = i3; save index
point3 = q; save center
ENDIF
i3 = tileorder[i3]; follow link to the left
ENDIF
ENDIF
IF (j3 < 0); hit the rightmost node
scanright = FALSE; don't scan to the right
ENDIF
IF (scanright); scanning to the right...
q = tilecenters[j3]+flip(tilecenters[j3+1]) ; tile center (as a complex)
d = sqr(real(q) - real(#pixel)) ; distance on X axis
IF (d > closest3); X distance alone is greater than our closest
scanright = FALSE; so nothing else can possibly match
ELSE; well maybe it's closer
d = |q - #pixel|; distance to this tile
IF (d < closest3 && j3 != l3) ; new closest value!
closest3 = d; save distance
l4 = j3; save index
point3 = q; save center
ENDIF
j3 = tileorder[j3+1]; follow link to the right
ENDIF
ENDIF
ENDWHILE
ENDIF
ENDIF
;
; With the coordinates of the closest and
; second-closest tile, we can transform our
; point (and apply the solid color).
;
; The algorithm for determining whether the
; point is in the "seam" between two tiles
; is not perfect. To be truly right, it would
; need to test against the seam for every
; neighboring tile; we don't have that data
; handy and it's not easy to get. So, we
; fudge it.
;
; By also considering the third-closest tile,
; the most common errors in seam modes 1 and 2
; are corrected. If the seam width is increased
; further it's possible to re-introduce errors
; from the fourth-closest point.
;
IF (@usesolid >= 1 || @mosforce2 != 0.0)
d = real((#pixel-(point2+point1)*0.5) * conj(point2-point1)/cabs(point2-point1))
d2 = real((#pixel-(point3+point1)*0.5) * conj(point3-point1)/cabs(point3-point1))
ENDIF
IF (@usesolid == 1 && @tileseams > 0.0)
IF (abs(d) < @tileseams)
#solid = true
ENDIF
ELSEIF (@usesolid == 2 && @tileseams > 0.0)
IF (abs(d) > @tileseams)
#solid = true
ENDIF
ELSEIF (@usesolid == 3 && @tileseams > 0.0)
closest1 = sqrt(closest1)
closest2 = sqrt(closest2)
IF (closest2-closest1 < @tileseams)
#solid = true
ENDIF
ELSEIF (@usesolid == 4 && @tileseams > 0.0)
closest1 = sqrt(closest1)
closest2 = sqrt(closest2)
IF (closest2-closest1 > @tileseams)
#solid = true
ENDIF
ELSEIF (@usesolid == 5)
IF (abs(d) < @tileseams || abs(d2) < @tileseams)
#solid = true
ENDIF
ELSEIF (@usesolid == 6)
IF (abs(d) > @tileseams && abs(d2) > @tileseams)
#solid = true
ENDIF
ENDIF
IF (@tileuse == 1)
point1 = point2
ELSEIF (@tileuse == 2)
point1 = point3
ENDIF
IF (@mosangle != 0.0)
r1 = (r1 - 1073741824) * range
ENDIF
IF (@mosforce2 != 0.0 && (abs(d) < @tileseams || abs(d2) < @tileseams))
IF (abs(d) < @tileseams && abs(d) < abs(d2))
q = (point2-point1)/cabs(point2-point1)
ELSE
q = (point3-point1)/cabs(point3-point1)
d = d2
ENDIF
IF (@mosforce2mode == "outside")
#pixel = #pixel - q * @mosforce2 * d
ELSE
#pixel = #pixel + q * @mosforce2 * (abs(d) - @tileseams)
ENDIF
ELSE
#pixel = point1*(1-@mosforce) + (#pixel-point1)*@tilescale * (0,1)^(r1*@mosangle/90.0)
ENDIF
default:
title = "Mosaic (Fast)"
helpfile = "dmj3\dmj3-pub-uf-mosaic.htm"
param mostiles
caption = "Number of Tiles"
default = 500
hint = "Sets the number of mosaic tiles. More tiles take \
longer to render."
endparam
param moscenter
caption = "Tiled Area Center"
default = (0,0)
hint = "Sets the center of the tiled area. The cluster of \
tiles will be centered at this point."
endparam
param centermove
caption = "Use Screen Center"
default = TRUE
hint = "If set, the tiled area center is assumed to be at the center of \
the window, regardless of the Tiled Area Center setting."
endparam
param mosscale
caption = "Tile Density"
default = 5.0
hint = "Specifies the overall scale of the tiles. Smaller numbers \
will pack the tiles together more closely."
endparam
param tilescale
caption = "Tile Magnification"
default = 0.0
hint = "Specifies the scale of the image within each tile. Use 0 \
for solid-color tiles, use 1 for no effect."
endparam
param tileseams
caption = "Seam Width"
default = 0.0
hint = "Sets the width of seams between tiles. If set to 0, then \
no seam will be calculated (faster)."
endparam
param tileuse
caption = "Tile to Use"
default = 0
enum = "closest" "second closest" "third closest"
hint = "Selects which tile to use. Closest is normal mosaic; use \
the other modes to produce a more shattered appearance."
endparam
param usesolid
caption = "Solid Color Use"
default = 0
enum = "none" "seams, no gaps" "tiles, no gaps" "seams, with gaps" "tiles, with gaps" \
"correct seams" "correct tiles"
hint = "Sets what the solid color will be used for."
endparam
param mosforce
caption = "Force Tiles to Origin"
default = 0.0
hint = "Forces the center of each tile towards the origin. If zero, \
has no effect (tiles are centered normally). If one, tiles \
are fully moved towards the origin."
endparam
param mosforce2
caption = "Force Seams to Edge"
default = 0.0
hint = "Forces points within the seam area to be exactly on the edge. \
Use zero for no effect (seam area is unaffected). Use 1.0 for \
full effect. Best used when solid color is not used for seams."
endparam
param mosforce2mode
caption = "Force Seams Target"
default = 1
enum = "inside" "outside"
hint = "Chooses which edge of the seam to force points towards. The inside \
edge will force points onto the inside edge of the mosaic tile, \
resulting in a double band and discontinuity at the outside edge. \
Using the outside edge will result in a single band and discontinuity \
at the inside edge."
endparam
param mosangle
caption = "Rotation Range"
default = 0.0
hint = "Sets the range on rotations for each tile."
endparam
param seed1
caption = "Random Seed 1"
default = 51853571
hint = "This is the 'seed' for the random number generator for \
horizontal positions."
endparam
param seed2
caption = "Random Seed 2"
default = 8072177
hint = "This is the 'seed' for the random number generator for \
vertical positions."
endparam
param seed3
caption = "Random Seed 3"
default = 654187327
hint = "This is the 'seed' for the random number generator for \
rotations."
endparam
}
dmj-Stipple {
;
; This transform can be used to create a "stippled"
; look in a fractal. It is largely based on the
; FastMosaic transform, but is a bit slower because
; it has to check more random points per pixel.
; Instead of simply center points for tiles, we
; create a list of center points and radii.
;
; Later additions provided alternate transforms to
; perform at each stipple point, like twist, pinch,
; bulge, etc.
;
global:
;
; The general idea is to generate, one time, the
; entire list of center points for each tile. But,
; as the list is generated, it is stored in sorted
; order (by X coordinate). This will allow fast
; finding of nearest center point.
;
; We store centerpoints as an array of float instead
; of an array of complex so we don't have to keep
; cutting our tree node numbers in half.
;
float tilecenters[@mostiles*2]; tile center points
float tileangles[@mostiles]; tile rotation angles
int tiletree[@mostiles*2]; tile tree structure
int tileorder[@mostiles*2]; tile ordering
complex center2 = @moscenter
IF (@centermove)
center2 = #center
ENDIF
int i = 0
int j = 0
int k = 0
int l = 0
int rightbranch = 0
float random1 = @seed1
float random2 = @seed2
float random3 = @seed3
complex p = 0
float range = 1.0 / 2147483648.0
tiletree[0] = -1; initialize tree (one leaf node)
tiletree[1] = -1
tileorder[0] = -1; initialize order (one double-endpoint)
tileorder[1] = -1
WHILE (i < @mostiles); still another tile to generate
;
; generate random data
;
random1 = (random1 * 1103515245 + 12345) % 2147483648.0
random2 = (random2 * 1103515245 + 12345) % 2147483648.0
p = center2 + ((random1 - 1073741824) + flip(random2 - 1073741824)) * range * @mosscale
tilecenters[j] = real(p)
tilecenters[j+1] = imag(p)
; IF (@mosangle != 0.0); want an angle, too
random3 = (random3 * 1103515245 + 12345) % 2147483648.0
tileangles[i] = sqr((random3 - 1073741824) * range * @mosangle + @mosanglecenter)
; ENDIF
;
; find appropriate insertion point in the tree
;
IF (i > 0); have at least one item in the tree
k = 0; current node
WHILE (k >= 0); not at a leaf node
l = k; save current node
IF (real(p) < tilecenters[k] || \
(real(p) == tilecenters[k] && \
imag(p) < tilecenters[k+1])) ; less than current node
k = tiletree[k]; follow left branch
rightbranch = 0
ELSE; greater than current node
k = tiletree[k+1]; follow right branch
rightbranch = 1
ENDIF
ENDWHILE
;
; insert node into tree
;
tiletree[j] = -1; this node is a leaf node
tiletree[j+1] = -1
tiletree[l+rightbranch] = j; point this item to it
;
; insert node into sorted list
;
IF (rightbranch == 0); followed the left branch
IF (tileorder[l] >= 0); not the leftmost node so far
tileorder[tileorder[l]+1] = j; follow it and point it to the new node
ENDIF
tileorder[j] = tileorder[l]; point new node to nodes it's between
tileorder[j+1] = l
tileorder[l] = j; point previous tree node's left to this node
ELSE
IF (tileorder[l+1] >= 0); not the rightmost node so far
tileorder[tileorder[l+1]] = j; follow it and point it to the new node
ENDIF
tileorder[j] = l; point new node to nodes it's between
tileorder[j+1] = tileorder[l+1]
tileorder[l+1] = j; point previous tree node's right to this node
ENDIF
ENDIF
i = i + 1
j = j + 2
ENDWHILE
float mosanglemax = sqr(@mosanglecenter + @mosangle)
transform:
;
; At this point, we already have a list of tile
; centers, with forward and backward order links,
; and a tree structure for finding the closest X
; value. For each pixel, find the tile center with
; the closest X value, then scan outwards until
; the tile centers under examination are too far
; (in the X direction) to beat what we already have.
;
int i2 = 0
int j2 = 0
int k2 = 0
int l2 = 0
int i3 = 0
int j3 = 0
int k3 = 0
int l3 = 0
BOOL scanleft = TRUE
BOOL scanright = TRUE
float d = 0
float closest1 = 1e20
float closest2 = 1e20
complex q = 0
complex point1 = 0
complex point2 = 0
float r1 = 0
float size1 = 1e20
;
; First, find the closest X value.
;
k2 = 0; current node
WHILE (k2 >= 0); not at a leaf node
l2 = k2; save current node
IF (real(#pixel) < tilecenters[k2] || \
(real(#pixel) == tilecenters[k2] && \
imag(#pixel) < tilecenters[k2+1])) ; less than current node
k2 = tiletree[k2]; follow left branch
ELSE; greater than current node
k2 = tiletree[k2+1]; follow right branch
ENDIF
ENDWHILE
q = tilecenters[l2]+flip(tilecenters[l2+1]) ; tile center (as a complex)
d = |q - #pixel|; save distance
i2 = tileorder[l2]; left node to check
j2 = tileorder[l2+1]; right node to check
IF (d < tileangles[floor(l2 * 0.5)]); this point is within range
closest1 = d
point1 = q; save center
size1 = tileangles[floor(l2 * 0.5)]; save size
ELSE; this point is not within range
l2 = @mostiles * 2; flag this as not found
ENDIF
WHILE (scanleft || scanright); still scanning in at least one direction
IF (i2 < 0); hit the leftmost node
scanleft = FALSE; don't scan to the left
ENDIF
IF (scanleft); scanning to the left...
q = tilecenters[i2]+flip(tilecenters[i2+1]) ; tile center (as a complex)
d = sqr(real(q) - real(#pixel)); distance on X axis
IF (d > mosanglemax); X distance alone is greater than our closest
scanleft = FALSE; so nothing else can possibly match
ELSE; well maybe it's closer
d = |q - #pixel|; distance to this tile
; IF (d < closest1); new closest value!
IF (d < tileangles[floor(i2 * 0.5)] && i2 < l2); new closest value!
closest1 = d; save distance
l2 = i2; save index
point1 = q; save center
size1 = tileangles[floor(l2 * 0.5)]; save size
; IF (@mosangle != 0.0); want a random angle, too
; r1 = tileangles[floor(l2 * 0.5)]; save angle
; ENDIF
ENDIF
i2 = tileorder[i2]; follow link to the left
ENDIF
ENDIF
IF (j2 < 0); hit the rightmost node
scanright = FALSE; don't scan to the right
ENDIF
IF (scanright); scanning to the right...
q = tilecenters[j2]+flip(tilecenters[j2+1]) ; tile center (as a complex)
d = sqr(real(q) - real(#pixel)); distance on X axis
IF (d > mosanglemax); X distance alone is greater than our closest
scanright = FALSE; so nothing else can possibly match
ELSE; well maybe it's closer
d = |q - #pixel|; distance to this tile
; IF (d < closest1); new closest value!
IF (d < tileangles[floor(j2 * 0.5)] && j2 < l2); new closest value!
closest1 = d; save distance
l2 = j2; save index
point1 = q; save center
size1 = tileangles[floor(l2 * 0.5)]; save size
; IF (@mosangle != 0.0); want a random angle, too
; r1 = tileangles[floor(l2 * 0.5)]; save angle
; ENDIF
ENDIF
j2 = tileorder[j2+1]; follow link to the right
ENDIF
ENDIF
ENDWHILE
;
; We now have the location of the closest tile
; (point1) and the index of it (l2). If we're
; doing anything with the seams, we'll need the
; second-closest point, too. We scan left and
; right for it. This time, we don't need the
; angle, we just want to know where the tile is.
;
; IF (@usesolid > 0 && @tileseams > 0.0)
; scanleft = TRUE
; scanright = TRUE
; i3 = tileorder[l2]; left node to check
; j3 = tileorder[l2+1]; right node to check
;
; WHILE (scanleft || scanright); still scanning in at least one direction
; IF (i3 < 0); hit the leftmost node
; scanleft = FALSE; don't scan to the left
; ENDIF
; IF (scanleft); scanning to the left...
; q = tilecenters[i3]+flip(tilecenters[i3+1]) ; tile center (as a complex)
; d = sqr(real(q) - real(#pixel)); distance on X axis
; IF (d > closest2); X distance alone is greater than our closest
; scanleft = FALSE; so nothing else can possibly match
; ELSE; well maybe it's closer
; d = |q - #pixel|; distance to this tile
; IF (d < closest2); new closest value!
; closest2 = d; save distance
; l3 = i3; save index
; point2 = q; save center
; ENDIF
; i3 = tileorder[i3]; follow link to the left
; ENDIF
; ENDIF
;
; IF (j3 < 0); hit the rightmost node
; scanright = FALSE; don't scan to the right
; ENDIF
; IF (scanright); scanning to the right...
; q = tilecenters[j3]+flip(tilecenters[j3+1]) ; tile center (as a complex)
; d = sqr(real(q) - real(#pixel)); distance on X axis
; IF (d > closest2); X distance alone is greater than our closest
; scanright = FALSE; so nothing else can possibly match
; ELSE; well maybe it's closer
; d = |q - #pixel|; distance to this tile
; IF (d < closest2); new closest value!
; closest2 = d; save distance
; l3 = j3; save index
; point2 = q; save center
; ENDIF
; j3 = tileorder[j3+1]; follow link to the right
; ENDIF
; ENDIF
; ENDWHILE
;
; ENDIF
;
; With the coordinates of the closest and
; second-closest tile, we can transform our
; point (and apply the solid color).
;
; The algorithm for determining whether the
; point is in the "seam" between two tiles
; is not perfect. To be truly right, it would
; need to test against the seam for every
; neighboring tile; we don't have that data
; handy and it's not easy to get. So, we
; fudge it.
;
IF (@usesolid == 1 && @tileseams > 0.0)
IF (sqrt(closest1) > sqrt(size1)-@tileseams)
#solid = true
ENDIF
ELSEIF (@usesolid == 2 && @tileseams > 0.0)
IF (sqrt(closest1) < sqrt(size1)-@tileseams)
#solid = true
ENDIF
ENDIF
; IF (@usesolid == 1 && @tileseams > 0.0)
; d = real((#pixel-(point2+point1)*0.5) * conj(point2-point1)/cabs(point2-point1))
; IF (abs(d) < @tileseams)
; #solid = true
; ENDIF
; ELSEIF (@usesolid == 2 && @tileseams > 0.0)
; d = real((#pixel-(point2+point1)*0.5) * conj(point2-point1)/cabs(point2-point1))
; IF (abs(d) > @tileseams)
; #solid = true
; ENDIF
; ELSEIF (@usesolid == 3 && @tileseams > 0.0)
; closest1 = sqrt(closest1)
; closest2 = sqrt(closest2)
; IF (closest2-closest1 < @tileseams)
; #solid = true
; ENDIF
; ELSEIF (@usesolid == 4 && @tileseams > 0.0)
; closest1 = sqrt(closest1)
; closest2 = sqrt(closest2)
; IF (closest2-closest1 > @tileseams)
; #solid = true
; ENDIF
; ENDIF
; IF (@mosangle != 0.0)
; r1 = (r1 - 1073741824) * range
; ENDIF
; #pixel = point1*(1-@mosforce) + (#pixel-point1)*@tilescale * (0,1)^(r1*@mosangle/90.0)
IF (l2 < @mostiles * 2); found a point inside
#pixel = point1*(1-@mosforce) + (#pixel-point1)*@tilescale
ELSE
#solid = true
ENDIF
default:
title = "Stipple"
helpfile = "dmj3\dmj3-pub-uf-stipple.htm"
; param spottype
; caption = "Spot Effect"
; default = 0
; enum = "mosaic" "twist"
;
param mostiles
caption = "Number of Spots"
default = 500
hint = "Sets the number of stipple spots. More spots take \
longer to render."
endparam
param moscenter
caption = "Spotted Area Center"
default = (0,0)
hint = "Sets the center of the spotted area. The cluster of \
spots will be centered at this point."
endparam
param centermove
caption = "Use Screen Center"
default = TRUE
hint = "If set, the spotted area center is assumed to be at the center of \
the window, regardless of the Spotted Area Center setting."
endparam
param mosscale
caption = "Spot Density"
default = 5.0
hint = "Specifies the overall scale of the spots. Smaller numbers \
will pack the spots together more closely."
endparam
param tilescale
caption = "Spot Magnification"
default = 0.0
hint = "Specifies the scale of the image within each tile. Use 0 \
for solid-color tiles, use 1 for no effect."
endparam
param tileseams
caption = "Seam Width"
default = 0.0
hint = "Sets the width of seams between tiles. If set to 0, then \
no seam will be calculated (faster)."
endparam
param usesolid
caption = "Solid Color Use"
default = 0
enum = "none" "seams" "tiles"
hint = "Sets what the solid color will be used for."
endparam
param mosforce
caption = "Force Spots to Origin"
default = 0.0
hint = "Forces the center of each spot towards the origin. If zero, \
has no effect (spots are centered normally). If one, spots \
are fully moved towards the origin."
endparam
param mosangle
caption = "Size Range"
default = 0.05
hint = "Sets the range on sizes for each spot."
endparam
param mosanglecenter
caption = "Average Size"
default = 0.1
hint = "Sets the average size for each spot."
endparam
param seed1
caption = "Random Seed 1"
default = 51853571
hint = "This is the 'seed' for the random number generator for \
horizontal positions."
endparam
param seed2
caption = "Random Seed 2"
default = 8072177
hint = "This is the 'seed' for the random number generator for \
vertical positions."
endparam
param seed3
caption = "Random Seed 3"
default = 654187327
hint = "This is the 'seed' for the random number generator for \
sizes."
endparam
}
dmj-WedgeClipping {
;
; Clipping shapes in the form of wedges.
;
transform:
float l = @left
float r = @right
float d = cabs(#pixel - @center)
float a = -atan2(#pixel - @center) * 180 / #pi + 90
IF (a < 0)
a = a + 360
ENDIF
IF (l < 0)
l = l + 360
ENDIF
IF (l >= 360)
l = l - 360
ENDIF
IF (r <= 0)
r = r + 360
ENDIF
IF (r > 360)
r = r - 360
ENDIF
BOOL state = false
IF (l < r)
IF (a >= l && a < r && d >= @bottom && d <= @top)
state = true
ENDIF
ELSE
IF ((a >= l || a < r) && d >= @bottom && d <= @top)
state = true
ENDIF
ENDIF
IF (@inside == "inside")
#solid = state
ELSEIF (@inside == "outside")
#solid = !state
ENDIF
default:
title = "Wedge Clipping"
param center
caption = "Center"
default = (0,0)
endparam
param left
caption = "Left Edge"
default = 0.0
endparam
param right
caption = "Right Edge"
default = 60.0
endparam
param top
caption = "Outside Edge"
default = 1.0
endparam
param bottom
caption = "Inside Edge"
default = 0.5
endparam
param border
caption = "Border thickness"
default = 0.0
endparam
param inside
caption = "Clipping Region"
default = 0
enum = "inside" "outside"
endparam
}
dmj-RadialTiles {
;
; Random-ish Low-Res Tiles in a Radial Pattern
;
transform:
float d = cabs(#pixel - @center)
float a = atan2(#pixel - @center)
d = ceil(d / @rres) * @rres
float mostiles = d / @ares / @rres
int i = 0
float d2 = 0
float closest1 = 1e20
float random1 = @seed1 + d*7
float p = 0
float point1 = 0
float range = 1.0 / 2147483648.0
WHILE (i < mostiles); still another tile to check
random1 = (random1 * 1103515245 + 12345) % 2147483648.0
p = (random1 - 1073741824) * range * #pi * 2
d2 = abs(p - a)
IF (d2 > #pi)
d2 = 2*#pi - d2
ENDIF
IF (d2 < closest1)
closest1 = d2
point1 = p
ENDIF
i = i + 1
ENDWHILE
#pixel = (cos(point1) + flip(sin(point1))) * d + @center
default:
title = "Radial Tiles"
param center
caption = "Center"
default = (0,0)
endparam
param rres
caption = "Ring Spacing"
default = 0.2
endparam
param ares
caption = "Angular Spacing"
default = 0.2
endparam
param seed1
caption = "Random Seed 1"
default = 51853571
hint = "This is the base 'seed' for the random number generator for \
radial positions."
endparam
}
dmj3-LinearWave {
;
; Linear distortion waves.
;
transform:
complex r = (0,1) ^ (@waveangle / 90.0)
complex r2 = (0,1) ^ (@distangle / 90.0)
complex z2 = #pixel * r
float d = sin(real(z2)*@distfrequency/#pi+@distphase/#pi)*@diststrength
z2 = z2 + d * r2
#pixel = z2 * conj(r)
default:
title = "Linear Wave"
param waveangle
caption = "Wave Angle"
default = 0.0
hint = "Sets the angle the wave is generated at."
endparam
param distangle
caption = "Distortion Angle"
default = 0.0
hint = "Sets the angle of the distortion, relative to the \
wave angle."
endparam
param diststrength
caption = "Distortion Strength"
default = 1.0
hint = "Sets the amount of distortion the wave will cause."
endparam
param distfrequency
caption = "Distortion Frequency"
default = 10.0
hint = "Sets the frequency of the wave."
endparam
param distphase
caption = "Distortion Phase"
default = 0.0
hint = "Sets the phase of the wave. Use this to shift the wave \
without changing its strength or direction."
endparam
}
dmj3-LogSpiral {
;
; Logarithmic spiral transform
;
; This transform takes a thin strip of the complex
; plane and wraps it into a logarithmic spiral.
;
transform:
float a = atan2(#pixel - @spiralcenter) * 0.5 / #pi
float d = cabs(#pixel - @spiralcenter)
float d2 = log(d)*@spiraltightness + a
float d2f = d2 - floor(d2)
float d2i = d2 - d2f
#pixel = (d2i-a)*@spiralascale + flip(d2f)*@spiraldscale + @spiraloffset
default:
title = "Log Spiral"
helpfile = "dmj3\dmj3-pub-uf-logspiral.htm"
param spiralcenter
caption = "Spiral Center"
default = (0,0)
endparam
param spiralascale
caption = "Rotational scale"
default = 1.0
endparam
param spiraldscale
caption = "Distance scale"
default = 1.0
endparam
param spiraltightness
caption = "Tightness"
default = 1.0
endparam
param spiraloffset
caption = "Strip Offset"
default = (0,0)
endparam
}
dmj-CheckerboardClipping {
;
; Checkerboard Clipping Transformation
; Colors every other square the solid color.
;
transform:
complex r = (0,1) ^ (@angle / 90.0)
complex p = #pixel - @scicenter
IF (@aspect != 1.0)
p = real(p) + flip(imag(p) * @aspect) ; apply aspect
ENDIF
p = floor(p * r * @scale)
IF ((real(p) + imag(p)) % 2 == 0)
#solid = true
ENDIF
default:
title = "Checkerboard Clipping"
helpfile = "dmj-pub\dmj-pub-uf-checker.htm"
param scicenter
caption = "Clipping Center"
default = (0,0)
hint = "Sets the center of clipping."
endparam
param angle
caption = "Rotation angle"
default = 0.0
hint = "Sets how much to rotate the pattern (in degrees)."
endparam
param scale
caption = "Pattern Scale"
default = 1.0
hint = "Sets how large the pattern is."
endparam
param aspect
caption = "Aspect Ratio"
default = 1.0
min = 0.0000000001
hint = "This is how square the pattern is. You can \
distort the pattern by using a value other than 1.0."
endparam
}
dmj3-DeluxeClipping {
;
; New clipping transform created for Janet Parke's Masking
; course for Ultra Fractal.
;
; This transform is intended to be useful both for students
; and for experienced UF artists.
;
global:
int digits[11]
digits[0] = 432534
digits[1] = 139874
digits[2] = 1000086
digits[3] = 430742
digits[4] = 195170
digits[5] = 925327
digits[6] = 433798
digits[7] = 139807
digits[8] = 431766
digits[9] = 399254
digits[10] = 0
transform:
complex p = (0,0)
complex r = (0,1) ^ (@rotation / 90)
complex c = (0,0)
complex q = (0,0)
complex n = (0,0)
complex s = (0,0)
complex t = (0,0)
complex o = ((0,1) ^ (@offsetangle / 90)) * @offsetamount
float d = 0.0
float a = 1.0
bool in = false
int j = 0
int k = 0
int l = 0
complex polypoints[1001]; arbitrary polygon points
IF (@shape == "arbitrary polygon"); doing a polygon; copy endpoints
polypoints[0] = @polypoint1
polypoints[1] = @polypoint2
polypoints[2] = @polypoint3
polypoints[3] = @polypoint4
polypoints[4] = @polypoint5
polypoints[5] = @polypoint6
polypoints[6] = @polypoint7
polypoints[7] = @polypoint8
polypoints[8] = @polypoint9
polypoints[9] = @polypoint10
polypoints[10] = @polypoint11
polypoints[11] = @polypoint12
polypoints[12] = @polypoint13
polypoints[13] = @polypoint14
polypoints[14] = @polypoint15
polypoints[15] = @polypoint16
polypoints[16] = @polypoint17
polypoints[17] = @polypoint18
polypoints[18] = @polypoint19
polypoints[19] = @polypoint20
polypoints[@polyorder] = @polypoint1
ENDIF
int polypointcount = @polyorder
complex curvepoints[1001]; curve control points
BOOL curveauto[1001]; auto-generate tangent point flags
BOOL curveclose[1001]; close curve
IF (@shape == "Bézier curve"); doing a curve; copy control points
curvepoints[0] = @curvepoint1
curvepoints[1] = @curvepoint2
curvepoints[2] = @curvepoint3
curvepoints[3] = @curvepoint4
curvepoints[4] = @curvepoint5
curvepoints[5] = @curvepoint6
curvepoints[6] = @curvepoint7
curvepoints[7] = @curvepoint8
curvepoints[8] = @curvepoint9
curvepoints[9] = @curvepoint10
curvepoints[10] = @curvepoint11
curvepoints[11] = @curvepoint12
curvepoints[12] = @curvepoint13
curvepoints[13] = @curvepoint14
curvepoints[14] = @curvepoint15
curvepoints[15] = @curvepoint16
curvepoints[16] = @curvepoint17
curvepoints[17] = @curvepoint18
curvepoints[18] = @curvepoint19
curvepoints[19] = @curvepoint20
polypoints[0] = @curvetangent1; copy tangent points
polypoints[1] = @curvetangent2
polypoints[2] = @curvetangent3
polypoints[3] = @curvetangent4
polypoints[4] = @curvetangent5
polypoints[5] = @curvetangent6
polypoints[6] = @curvetangent7
polypoints[7] = @curvetangent8
polypoints[8] = @curvetangent9
polypoints[9] = @curvetangent10
polypoints[10] = @curvetangent11
polypoints[11] = @curvetangent12
polypoints[12] = @curvetangent13
polypoints[13] = @curvetangent14
polypoints[14] = @curvetangent15
polypoints[15] = @curvetangent16
polypoints[16] = @curvetangent17
polypoints[17] = @curvetangent18
polypoints[18] = @curvetangent19
polypoints[19] = @curvetangent20
curveauto[0] = @curvetangent1auto; copy auto-generate flags
curveauto[1] = @curvetangent2auto
curveauto[2] = @curvetangent3auto
curveauto[3] = @curvetangent4auto
curveauto[4] = @curvetangent5auto
curveauto[5] = @curvetangent6auto
curveauto[6] = @curvetangent7auto
curveauto[7] = @curvetangent8auto
curveauto[8] = @curvetangent9auto
curveauto[9] = @curvetangent10auto
curveauto[10] = @curvetangent11auto
curveauto[11] = @curvetangent12auto
curveauto[12] = @curvetangent13auto
curveauto[13] = @curvetangent14auto
curveauto[14] = @curvetangent15auto
curveauto[15] = @curvetangent16auto
curveauto[16] = @curvetangent17auto
curveauto[17] = @curvetangent18auto
curveauto[18] = @curvetangent19auto
curveauto[19] = @curvetangent20auto
curveclose[0] = false ;@curveclose1; copy curve-close flags
curveclose[1] = @curveclose2
curveclose[2] = @curveclose3
curveclose[3] = @curveclose4
curveclose[4] = @curveclose5
curveclose[5] = @curveclose6
curveclose[6] = @curveclose7
curveclose[7] = @curveclose8
curveclose[8] = @curveclose9
curveclose[9] = @curveclose10
curveclose[10] = @curveclose11
curveclose[11] = @curveclose12
curveclose[12] = @curveclose13
curveclose[13] = @curveclose14
curveclose[14] = @curveclose15
curveclose[15] = @curveclose16
curveclose[16] = @curveclose17
curveclose[17] = @curveclose18
curveclose[18] = @curveclose19
curveclose[19] = false ;@curveclose20
curvepoints[@curveorder] = curvepoints[0]
polypoints[@curveorder] = polypoints[0]
curveauto[@curveorder] = curveauto[0]
ENDIF
; While debugging, a test pixel was helpful in
; identifying results of inside/outside tests
; for individual line/curve segments. If you
; want to see how the winding number algorithm
; works, enable debugging and set the testx/y
; to the pixel coordinates you'd like to see.
; $define debug
int testx = -50
int testy = -50
complex anchorpoints[102]; anchor point locations
int anchorpointcount = 0
int anchorpointtype[102]; anchor point type
int anchorpointnumber[102]
WHILE (j < 102)
anchorpointtype[j] = 0; by default, all are tangent points
anchorpointnumber[j] = 0; without numbers
j = j + 1
ENDWHILE
; determine pixel location
IF (@screenrelative); relative coordinates
p = real(#screenpixel)/#width + flip(1.0-imag(#screenpixel)/#height)
a = #width/#height; aspect ratio of screen, used to enforce squareness
ELSE; absolute coordinates
p = #pixel
ENDIF
; apply offset
p = p - o
IF (@shape == "circle"); circle clipping area
IF (@circletype == "bounding box"); bounding box method
c = (@circleupperleft + @circlelowerright) * 0.5
IF (abs(real(@circleupperleft)-real(@circlelowerright)) > abs(imag(@circleupperleft)-imag(@circlelowerright)))
d = sqr(abs(imag(@circleupperleft)-imag(@circlelowerright)) * 0.5)
ELSE
d = sqr(abs(real(@circleupperleft)-real(@circlelowerright)) * 0.5)
ENDIF
anchorpoints[0] = @circleupperleft
anchorpointtype[0] = 1
anchorpoints[1] = @circlelowerright
anchorpointtype[1] = 1
anchorpointcount = 2
ELSEIF (@circletype == "center and edge"); center and edge method
c = @circlecenter
s = @circleedge
s = real(s-c)*a + flip(imag(s-c)) + c; correct for aspect distortion, centered on c
d = |s - c|
anchorpoints[0] = @circlecenter
anchorpointtype[0] = 2
anchorpoints[1] = @circleedge
anchorpointcount = 2
ELSEIF (@circletype == "opposite edges"); opposite edges method
c = (@circleedge1 + @circleedge2) * 0.5
s = @circleedge1
s = real(s-c)*a + flip(imag(s-c)) + c; correct for aspect distortion, centered on c
d = |s - c|
anchorpoints[0] = @circleedge1
anchorpoints[1] = @circleedge2
anchorpointcount = 2
ELSEIF (@circletype == "three edge points"); three edge points method
; from mathworld.wolfram.com
float ca = real(@circleedge1)*(imag(@circleedge2)*1-imag(@circleedge3)*1) - \
imag(@circleedge1)*(real(@circleedge2)*1-real(@circleedge3)*1) + \
1*(real(@circleedge2)*imag(@circleedge3)-real(@circleedge3)*imag(@circleedge2))
float cd = -( (|@circleedge1|)*(imag(@circleedge2)*1-imag(@circleedge3)*1) - \
imag(@circleedge1)*((|@circleedge2|)*1-(|@circleedge3|)*1) + \
1*((|@circleedge2|)*imag(@circleedge3)-(|@circleedge3|)*imag(@circleedge2)) )
float ce = (|@circleedge1|)*(real(@circleedge2)*1-real(@circleedge3)*1) - \
real(@circleedge1)*((|@circleedge2|)*1-(|@circleedge3|)*1) + \
1*((|@circleedge2|)*real(@circleedge3)-(|@circleedge3|)*real(@circleedge2))
float cf = -( (|@circleedge1|)*(real(@circleedge2)*imag(@circleedge3)-real(@circleedge3)*imag(@circleedge2)) - \
real(@circleedge1)*((|@circleedge2|)*imag(@circleedge3)-(|@circleedge3|)*imag(@circleedge2)) + \
imag(@circleedge1)*((|@circleedge2|)*real(@circleedge3)-(|@circleedge3|)*real(@circleedge2)) )
c = -cd / (2*ca) + flip( -ce / (2*ca) )
d = (sqr(cd)+sqr(ce)) / (4*sqr(ca)) - cf/ca
anchorpoints[0] = @circleedge1
anchorpoints[1] = @circleedge2
anchorpoints[2] = @circleedge3
anchorpointcount = 3
ENDIF
p = real(p-c)*a + flip(imag(p-c)) + c; correct for aspect distortion, centered on c
IF (|c-p| <= d)
in = true
ENDIF
ELSEIF (@shape == "ellipse"); ellipse clipping area
IF (@ellipsetype == "three corners")
c = (@ellipseupperleft + @ellipselowerright) * 0.5
p = real(p-c)*a + flip(imag(p-c)) + c; correct for aspect distortion, centered on c
q = real(@ellipseupperleft-c)*a + flip(imag(@ellipseupperleft-c)) + c; correct for aspect distortion, centered on c
t = real(@ellipseupperright-c)*a + flip(imag(@ellipseupperright-c)) + c; correct for aspect distortion, centered on c
p = (p - c) * conj(r) + c; apply rotation
d = cabs(q - c)
s = c + (t-c)*d/cabs(t-c); corrected upper-right
n = (s-q) / cabs(s-q); unit vector along the top edge
s = (s-c)*conj(n)
p = (p-c)*conj(n)
s = abs(s)
IF (sqr(real(p)/real(s))+sqr(imag(p)/imag(s)) <= 1)
in = true
ENDIF
anchorpoints[0] = @ellipseupperleft
anchorpointtype[0] = 1
anchorpoints[1] = @ellipselowerright
anchorpointtype[1] = 1
anchorpoints[2] = @ellipseupperright
anchorpointtype[2] = 1
anchorpointcount = 3
ELSEIF (@ellipsetype == "center and two edges")
c = @ellipsecenter
p = real(p-c)*a + flip(imag(p-c)) + c; correct for aspect distortion, centered on c
q = real(@ellipsetop-c)*a + flip(imag(@ellipsetop-c)) + c; correct for aspect distortion, centered on c
t = real(@ellipseright-c)*a + flip(imag(@ellipseright-c)) + c; correct for aspect distortion, centered on c
p = (p - c) * conj(r) + c; apply rotation
n = (q-c) / cabs(q-c); unit vector to top edge
n = conj(flip(n))
s = real((t-c)*conj(n)) + flip(imag((q-c)*conj(n)))
p = (p-c)*conj(n)
s = abs(s)
IF (sqr(real(p)/real(s))+sqr(imag(p)/imag(s)) <= 1)
in = true
ENDIF
anchorpoints[0] = @ellipsetop
anchorpoints[1] = @ellipseright
anchorpoints[2] = @ellipsecenter
anchorpointtype[2] = 2
anchorpointcount = 3
ELSEIF (@ellipsetype == "fixed angle")
c = @ellipsecenter
p = real(p-c)*a + flip(imag(p-c)) + c; correct for aspect distortion, centered on c
q = real(@ellipsetop-c)*a + flip(imag(@ellipsetop-c)) + c; correct for aspect distortion, centered on c
t = real(@ellipseright-c)*a + flip(imag(@ellipseright-c)) + c; correct for aspect distortion, centered on c
p = (p - c) * conj(r) + c; apply rotation
n = (0,1)^(@ellipse/90); unit vector along top edge
s = real((t-c)*conj(n)) + flip(imag((q-c)*conj(n)))
p = (p-c)*conj(n)
s = abs(s)
IF (sqr(real(p)/real(s))+sqr(imag(p)/imag(s)) <= 1)
in = true
ENDIF
anchorpoints[0] = @ellipsetop
anchorpoints[1] = @ellipseright
anchorpoints[2] = @ellipsecenter
anchorpointtype[2] = 2
anchorpointcount = 3
ENDIF
ELSEIF (@shape == "rectangle"); rectangle clipping area
IF (@recttype == "three corners")
c = (@rectupperleft + @rectlowerright) * 0.5
p = real(p-c)*a + flip(imag(p-c)) + c; correct for aspect distortion, centered on c
q = real(@rectupperleft-c)*a + flip(imag(@rectupperleft-c)) + c; correct for aspect distortion, centered on c
t = real(@rectupperright-c)*a + flip(imag(@rectupperright-c)) + c; correct for aspect distortion, centered on c
p = (p - c) * conj(r) + c; apply rotation
d = cabs(q - c)
s = c + (t-c)*d/cabs(t-c); corrected upper-right
n = (s-q) / cabs(s-q); unit vector along the top edge
s = (s-c)*conj(n)
p = (p-c)*conj(n)
s = abs(s)
IF (real(p) >= -real(s) && real(p) <= real(s) && imag(p) >= -imag(s) && imag(p) <= imag(s))
in = true
ENDIF
anchorpoints[0] = @rectupperleft
anchorpoints[1] = @rectupperright
anchorpoints[2] = @rectlowerright
anchorpointcount = 3
ELSEIF (@recttype == "center and two edges")
c = @rectcenter
p = real(p-c)*a + flip(imag(p-c)) + c; correct for aspect distortion, centered on c
q = real(@recttop-c)*a + flip(imag(@recttop-c)) + c; correct for aspect distortion, centered on c
t = real(@rectright-c)*a + flip(imag(@rectright-c)) + c; correct for aspect distortion, centered on c
p = (p - c) * conj(r) + c; apply rotation
n = (q-c) / cabs(q-c); unit vector to top edge
n = conj(flip(n))
s = real((t-c)*conj(n)) + flip(imag((q-c)*conj(n)))
p = (p-c)*conj(n)
s = abs(s)
IF (real(p) >= -real(s) && real(p) <= real(s) && imag(p) >= -imag(s) && imag(p) <= imag(s))
in = true
ENDIF
anchorpoints[0] = @recttop
anchorpoints[1] = @rectcenter
anchorpointtype[1] = 2
anchorpoints[2] = @rectright
anchorpointcount = 3
ELSEIF (@recttype == "fixed angle")
c = @rectcenter
p = real(p-c)*a + flip(imag(p-c)) + c; correct for aspect distortion, centered on c
q = real(@recttop-c)*a + flip(imag(@recttop-c)) + c; correct for aspect distortion, centered on c
t = real(@rectright-c)*a + flip(imag(@rectright-c)) + c; correct for aspect distortion, centered on c
p = (p - c) * conj(r) + c; apply rotation
n = (0,1)^(@rectangle/90); unit vector along top edge
s = real((t-c)*conj(n)) + flip(imag((q-c)*conj(n)))
p = (p-c)*conj(n)
s = abs(s)
IF (real(p) >= -real(s) && real(p) <= real(s) && imag(p) >= -imag(s) && imag(p) <= imag(s))
in = true
ENDIF
anchorpoints[0] = @recttop
anchorpoints[1] = @rectcenter
anchorpointtype[1] = 2
anchorpoints[2] = @rectright
anchorpointcount = 3
ENDIF
ELSEIF (@shape == "regular polygon"); regular polygon clipping area
c = @polygoncenter
p = real(p-c)*a + flip(imag(p-c)) + c; correct for aspect distortion, centered on c
p = (p - c) * conj(r) + c; apply rotation
IF (@polygontype == "fixed angle")
s = @polygonedge
d = (floor((atan2(s-c)-@polygonangle*#pi/180) * @polygonorder * 0.5/#pi + 0.5 + @polygonorder) % @polygonorder) * 4/@polygonorder
anchorpoints[0] = @polygoncenter
anchorpoints[1] = @polygonedge
anchorpointcount = 2
ELSEIF (@polygontype == "center and corner")
s = @polygoncorner
anchorpoints[0] = @polygoncenter
anchorpoints[1] = @polygoncorner
anchorpointcount = 2
ELSEIF (@polygontype == "center and edge")
s = @polygonedge
anchorpoints[0] = @polygoncenter
anchorpoints[1] = @polygonedge
anchorpointcount = 2
ENDIF
anchorpointtype[0] = 2
s = real(s-c)*a + flip(imag(s-c)) + c; correct for aspect distortion, centered on c
in = true; assume we're inside the polygon
j = 0
WHILE (j < @polygonorder); test each line segment in the polygon
IF (@polygontype == "center and corner"); specified corner rather than edge
n = c + (s - c) * ((0,1)^((j+0.5)*4/@polygonorder))
q = c + (s - c) * ((0,1)^(j*4/@polygonorder))
IF (real((q-p) * conj(n-c)) < 0)
in = false
ENDIF
ELSEIF (@polygontype == "center and edge")
q = c + (s - c) * ((0,1)^(j*4/@polygonorder))
IF (real((q-p) * conj(q-c)) < 0)
in = false
ENDIF
ELSEIF (@polygontype == "fixed angle")
n = c + (0,1)^((j*4)/@polygonorder+@polygonangle/90+d)
q = c + (s - c) * ((0,1)^(j*4/@polygonorder))
IF (real((q-p) * conj(n-c)) < 0)
in = false
ENDIF
ENDIF
j = j + 1
ENDWHILE
ELSEIF (@shape == "arbitrary polygon"); arbitrary polygon clipping area
IF (@polystar); doing a star!
l = @polystarorder
IF (@polystarlimit > 0)
l = @polystarlimit
ENDIF
k = 1
WHILE (k < l)
q = (0,1) ^ (k * 4 / @polystarorder); rotation vector
j = 0
WHILE (j < @polyorder)
IF (k*@polyorder + j < 1000)
polypoints[k*@polyorder + j] = (polypoints[j]-@polystarcenter) * q + @polystarcenter
ENDIF
j = j + 1
ENDWHILE
k = k + 1
ENDWHILE
polypointcount = @polyorder * l
IF (polypointcount > 1000)
polypointcount = 1000
ENDIF
IF (@polystarlimit > 0 && @polystarusecenter)
polypoints[polypointcount] = @polystarcenter
polypointcount = polypointcount + 1
ENDIF
polypoints[polypointcount] = polypoints[0]
ENDIF
j = 0; copy control points before rotating them
WHILE (j < @polyorder)
anchorpoints[j] = polypoints[j]
anchorpointnumber[j] = j + 1
j = j + 1
ENDWHILE
c = (0,0); assume center is at origin
j = 0
WHILE (j < polypointcount)
c = c + polypoints[j]; sum corner points
j = j + 1
ENDWHILE
c = c / polypointcount; center is average of all points
j = 0
WHILE (j <= polypointcount)
polypoints[j] = (polypoints[j]-c) * r + c; rotate each point around center
j = j + 1
ENDWHILE
IF (@polywinding == "convex polygon only")
in = true; assume we're inside the polygon
BOOL out = true; assume we're outside the polygon
j = 0
WHILE (j < polypointcount)
n = polypoints[j+1] - polypoints[j]; edge vector
IF (n != 0)
n = -imag(n) + flip(real(n)); normal to edge vector (90-degree left turn)
IF (real((p-polypoints[j]) * conj(n)) < 0)
in = false
ELSE
out = false
ENDIF
ENDIF
j = j + 1
ENDWHILE
in = in || out
ELSE; some variant of winding formula
; The winding number method counts all line segments
; that cross the vertical position of the pixel to
; the RIGHT of it. Segments that cross up increment
; the count, segments that cross down decrement it.
; With a closed shape, this will equal zero if the
; point is not inside (regardless of the clockwise/
; counter-clockwise direction of points). Very
; clever algorithm.
int wn = 0
j = 0
WHILE (j < polypointcount)
IF (imag(polypoints[j]) <= imag(p)); line segment imag <= point imag
IF (imag(polypoints[j+1]) > imag(p)); upward crossing
IF ( (real(polypoints[j+1]) - real(polypoints[j])) * (imag(p) - imag(polypoints[j])) - \
(real(p) - real(polypoints[j])) * (imag(polypoints[j+1]) - imag(polypoints[j])) > 0 )
wn = wn + 1
ENDIF
ENDIF
ELSE
IF (imag(polypoints[j+1]) <= imag(p)); downward crossing
IF ( (real(polypoints[j+1]) - real(polypoints[j])) * (imag(p) - imag(polypoints[j])) - \
(real(p) - real(polypoints[j])) * (imag(polypoints[j+1]) - imag(polypoints[j])) < 0 )
wn = wn - 1
ENDIF
ENDIF
ENDIF
j = j + 1
ENDWHILE
IF (@polywinding == "all inside")
in = (wn != 0)
ELSE
in = (wn % 2 != 0)
ENDIF
ENDIF
IF (@polystar)
anchorpoints[@polyorder] = @polystarcenter
anchorpointcount = @polyorder + 1
anchorpointtype[@polyorder] = 2
ELSE
anchorpointcount = @polyorder
ENDIF
ELSEIF (@shape == "Bézier curve"); arbitrary curve clipping area
; Just so you know, I worked this out myself. No doubt there's
; source code I could have ripped into a formula, but I wanted
; the satisfaction of knowing I could do it. I am quite sure
; this is not the most optimal method, but it does work. Pay
; particular attention to the use of < vs. <= as if you make a
; mistake, you will have horizontal line segment errors in the
; drawn shape.
; This is essentially the same algorithm as arbitrary polygon,
; but extended so that each segment can be a quadratic Bézier
; curve rather than just a straight line. Each curve is
; described by a triangle, and a parabola inscribed within the
; triangle such that two sides of the triangle are tangents
; to the parabola. Points that do not lie within the triangle
; are treated the same as the arbitrary polygon (only the chord
; cutting across the parabola, the third side of the triangle,
; matters) but for points inside the triangle a determination
; must be made as to whether the point is to the left of the
; parabola or not. There are a few edge cases where the
; parabola loops back on itself and the winding number can be
; changed multiple times for each segment.
polypointcount = @curveorder
IF (@curvestar); doing a star!
l = @curvestarorder
IF (@curvestarlimit > 0)
l = @curvestarlimit
ENDIF
k = 1
WHILE (k < l)
q = (0,1) ^ (k * 4 / @curvestarorder); rotation vector
j = 0
WHILE (j < @curveorder)
IF (k*@curveorder + j < 1000)
polypoints[k*@curveorder + j] = (polypoints[j]-@curvestarcenter) * q + @curvestarcenter
curvepoints[k*@curveorder + j] = (curvepoints[j]-@curvestarcenter) * q + @curvestarcenter
curveauto[k*@curveorder + j] = curveauto[j]
curveclose[k*@curveorder + j] = curveclose[j]
ENDIF
j = j + 1
ENDWHILE
k = k + 1
ENDWHILE
polypointcount = @curveorder * l
IF (polypointcount > 1000)
polypointcount = 1000
ENDIF
IF (@curvestarlimit > 0 && @curvestarusecenter)
polypoints[polypointcount] = @curvestarcenter
curvepoints[polypointcount] = (@curvestarcenter + polypoints[0]) * 0.5
curveauto[polypointcount] = false
polypointcount = polypointcount + 1
ENDIF
polypoints[polypointcount] = polypoints[0]
ENDIF
curveclose[polypointcount-1] = true
j = 0
k = 0
l = -1
WHILE (j < polypointcount)
IF (curveauto[j]); compute anchor point if necessary
IF (j == l+1)
l = l + 2
WHILE (l < polypointcount && !curveclose[l])
l = l + 1
ENDWHILE
polypoints[j] = (curvepoints[j] + curvepoints[l]) * 0.5
polypoints[l] = polypoints[j]
ELSE
polypoints[j] = (curvepoints[j] + curvepoints[j-1]) * 0.5
ENDIF
anchorpointtype[k] = 3
ENDIF
IF (j < @curveorder)
anchorpointnumber[k] = j + 1
anchorpoints[k] = polypoints[j]
k = k + 1
anchorpoints[k] = curvepoints[j]
anchorpointtype[k] = 1
anchorpointnumber[k] = j + 1
k = k + 1
ENDIF
j = j + 1
ENDWHILE
anchorpointcount = k
IF (@curvestar)
anchorpoints[anchorpointcount] = @curvestarcenter
anchorpointtype[anchorpointcount] = 2
anchorpointcount = anchorpointcount + 1
ENDIF
int wn = 0
float isleft1
float isleft2
float isleft3
float isleft4
float px
float py
float isy
j = 0
l = 0
; testx = 240
; testy = 206
WHILE (j < polypointcount)
$ifdef debug
IF (#x == testx && #y == testy)
print(j, " ", #x, ",", #y)
ENDIF
$endif
; determine index of endpoint for this segment
IF (curveclose[j]); curve is closed
k = l; endpoint is last saved starting point
l = j + 1; save next starting point
ELSE; curve is not closed here
k = j + 1
ENDIF
; three cases: pixel is below triangle containing curve, above it, or between top and bottom of it
; if either of the first two, don't examine this curve further
IF ((imag(p) >= imag(polypoints[j]) && (imag(p) < imag(polypoints[k]) || imag(p) < imag(curvepoints[j]))) || \
(imag(p) < imag(polypoints[j]) && (imag(p) >= imag(polypoints[k]) || imag(p) >= imag(curvepoints[j]))))
$ifdef debug
IF (#x == testx && #y == testy)
print(j, " considered for curve segment")
ENDIF
$endif
; pixel is between top and bottom of curve
; see if it's within the triangle
isleft1 = (real(polypoints[k]) - real(polypoints[j])) * (imag(p) - imag(polypoints[j])) - \
(real(p) - real(polypoints[j])) * (imag(polypoints[k]) - imag(polypoints[j]))
isleft2 = (real(curvepoints[j]) - real(polypoints[k])) * (imag(p) - imag(polypoints[k])) - \
(real(p) - real(polypoints[k])) * (imag(curvepoints[j]) - imag(polypoints[k]))
isleft3 = (real(polypoints[j]) - real(curvepoints[j])) * (imag(p) - imag(curvepoints[j])) - \
(real(p) - real(curvepoints[j])) * (imag(polypoints[j]) - imag(curvepoints[j]))
IF ((isleft1 <= 0 && isleft2 < 0 && isleft3 < 0) || (isleft1 >= 0 && isleft2 > 0 && isleft3 > 0))
; point is inside the triangle; determine if/where the ray
; intersects the parabola. start by normalizing the parabola
; and the ray
$ifdef debug
IF (#x == testx && #y == testy)
print(j, " considered inside triangle")
ENDIF
$endif
q = 2*conj(polypoints[k]-polypoints[j]) / |polypoints[k]-polypoints[j]|; rotation and scaling vector
c = (polypoints[j] + polypoints[k]) * 0.5
t = (p - c) * q; transform pixel so line segment is rotated to X-axis and centered at origin
s = (curvepoints[j] - c) * q; transform control point the same way
isy = 1/imag(s); precalc
px = real(t) - imag(t)*real(s)*isy; shear
py = imag(t)*isy; squash
d = 0.5-0.5*sqr(px); height of parabola at this point
isleft4 = py - d
IF (imag(s) < 0)
isleft4 = -isleft4
ENDIF
IF (imag(polypoints[k]) > imag(polypoints[j])); upward crossing
IF (imag(p) >= imag(polypoints[j]) && imag(p) < imag(polypoints[k])); within line segment
IF (isleft4 > 0)
wn = wn + 1; confirmed upward crossing
ENDIF
ELSE; outside line segment
IF (isleft1 < 0); loop back occurs to the left
IF (isleft4 > 0)
wn = wn + 1
ENDIF
ELSE; loop back occurs to the right
IF (isleft4 < 0)
wn = wn - 1; loop back; downward crossing
ENDIF
ENDIF
ENDIF
ELSE; downward crossing
IF (imag(p) < imag(polypoints[j]) && imag(p) >= imag(polypoints[k])); within line segment
IF (isleft4 < 0)
wn = wn - 1; confirmed downward crossing
ENDIF
ELSE; outside line segment
IF (isleft1 > 0); loop back occurs to the left
IF (isleft4 < 0)
wn = wn - 1
ENDIF
ELSE; loop back occurs to the right
IF (isleft4 > 0)
wn = wn + 1; loop back; upward crossing
ENDIF
ENDIF
ENDIF
ENDIF; upward/downward
ELSE
$ifdef debug
IF (#x == testx && #y == testy)
print(j, " tested against line segment only")
ENDIF
$endif
; point is outside the triangle; all that matters is its relation to the line segment
IF (imag(polypoints[j]) <= imag(p)); line segment imag < point imag
IF (imag(polypoints[k]) > imag(p)); upward crossing
IF ( isleft1 > 0 )
wn = wn + 1
ENDIF
ENDIF
ELSE
IF (imag(polypoints[k]) <= imag(p)); downward crossing
IF ( isleft1 < 0 )
wn = wn - 1
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
$ifdef debug
IF (#x == testx && #y == testy)
print(j, " test results: ", isleft1 > 0, " ", isleft2 > 0, " ", isleft3 > 0, " ", isleft4 > 0)
print(j, " current winding: ", wn)
ENDIF
$endif
j = j + 1
ENDWHILE
IF (@curvewinding == "all inside")
in = (wn != 0)
ELSE
in = (abs(wn) % 2 != 0)
ENDIF
$ifdef debug
IF (#x == testx && #y == testy)
print("final result: ", in)
in = !in
ENDIF
$endif
ENDIF
IF (@insideoutside == "outside")
in = !in
ENDIF
; handle display
; if you'd like to rip this out and use it in your own
; formulas, please give appropriate credit; this was a
; bit obnoxious to write
; handle types:
; 0: square
; 1: circle
; 2: diamond
; 3: X
IF (@showhandles && (#width > @handlesnopreviewsize || #height > @handlesnopreviewsize || !@handlesnopreview) && ((#width < @handlesnorendersize && #height < @handlesnorendersize) || !@handlesnorender))
; compute pixel coordinates of anchor points
; first determine the reverse transform, to
; go from complex plane back to pixel coordinates
float sa = sin(#angle)
float ca = cos(#angle)
IF (#width*3 <= #height*4)
float pd = 4 / (#width*#magn)
ELSE
float pd = 3 / (#height*#magn)
ENDIF
float dx = 0
float dy = 0
float sx = 0
float sy = 0
float osx = 0
float osy = 0
float fsx = 0
float fsy = 0
float lsx = 0
float lsy = 0
j = 0
l = 0
WHILE (j < anchorpointcount+1)
IF (j == anchorpointcount)
sx = testx
sy = testy
ELSEIF (@screenrelative)
sx = real(anchorpoints[j]) * #width
sy = (1 - imag(anchorpoints[j])) * #height
ELSE
p = (anchorpoints[j] - #center) / pd
sx = ca*real(p) + sa*imag(p)
sy = ca*imag(p) - sa*real(p)
sx = sx*#stretch + sy * tan(#skew)
sx = sx + #width/2
sy = -sy + #height/2
ENDIF
dx = abs(#x - sx)
dy = abs(#y - sy)
; handle
IF ((anchorpointtype[j] == 0 && \
(dx >= @handlesize || dy >= @handlesize) && \
(dx < @handlesize+@handlewidth && dy < @handlesize+@handlewidth)) || \
(anchorpointtype[j] == 1 && \
dx*dx+dy*dy >= @handlesize*@handlesize && dx*dx+dy*dy < sqr(@handlesize+@handlewidth)) || \
(anchorpointtype[j] == 2 && \
dx+dy >= @handlesize && dx+dy < @handlesize+@handlewidth) || \
(anchorpointtype[j] == 3 && \
2*abs(dx-dy) < @handlewidth && (dx < @handlesize+@handlewidth && dy < @handlesize+@handlewidth)))
in = !in
ENDIF
; number
IF (@handleswithnumbers && \
anchorpointnumber[j] > 0 && \
#x >= sx+@handlesize+@handlewidth && #x < sx+@handlesize+@handlewidth*10 && \
#y >= sy+@handlesize+@handlewidth && #y < sy+@handlesize+@handlewidth*6)
dx = floor((#x-sx-@handlesize-@handlewidth)/@handlewidth)
dy = floor((#y-sy-@handlesize-@handlewidth)/@handlewidth)
IF (dx < 4)
IF (anchorpointnumber[j] < 10)
k = anchorpointnumber[j]
ELSE
k = floor(anchorpointnumber[j] / 10)
ENDIF
ELSEIF (dx > 4)
IF (anchorpointnumber[j] < 10)
k = 10
ELSE
k = anchorpointnumber[j] % 10
ENDIF
dx = dx - 5
ELSE
k = 10
ENDIF
dx = (3-dx) + 4*dy
dy = 2^dx
IF (floor(digits[k] / dy) % 2 > 0)
in = !in
ENDIF
ENDIF
; line segment
IF (@shape == "Bézier curve" && @handleswithlines)
IF (j > 0)
IF (anchorpointtype[j-1] != 2 && anchorpointtype[j] != 2)
IF (j == anchorpointcount || (l > 0 && curveclose[l-1] && anchorpointtype[j-1] == 1))
lsx = sx
lsy = sy
sx = fsx
sy = fsy
ENDIF
d = sqrt(sqr(sx-osx)+sqr(sy-osy))
dx = ((sx - osx) * (#y - osy) - (#x - osx) * (sy - osy)) / d
dy = ((sy - osy) * (#y - osy) - (#x - osx) * (osx - sx)) / d
IF (abs(dx) < 1 && dy > @handlesize*1.5 && dy < d-@handlesize*1.5 && dy-floor(dy*0.5/@handlesize)*@handlesize*2 > @handlesize)
in = !in
ENDIF
ENDIF
ENDIF
ENDIF
osx = sx
osy = sy
IF (j == 0)
fsx = sx
fsy = sy
ENDIF
IF (l > 0)
IF (curveclose[l-1] && anchorpointtype[j-1] == 1)
fsx = lsx
fsy = lsy
osx = lsx
osy = lsy
ENDIF
ENDIF
j = j + 1
IF (anchorpointtype[j] == 1)
l = l + 1
ENDIF
ENDWHILE
ENDIF
#solid = in
default:
title = "Deluxe Clipping"
helpfile = "dmj-pub\dmj-pub-uf-deluxe.htm"
heading
caption = "General"
endheading
param clipversion
caption = "Clipping Formula Version"
default = 101
hint = "You should never see this parameter; it's used internally to track \
which version of the formula was used to create your image, so that \
if a bug is found which breaks backwards-compatibility, the formula \
can adapt transparently."
visible = (@clipversion < 101)
enabled = false
endparam
param insideoutside
caption = "Clipping Region"
default = 1
enum = "inside" "outside"
hint = "Determines whether points inside the clipping shape or outside the \
clipping shape will be solid-colored."
endparam
param screenrelative
caption = "Screen-Relative Coordinates"
default = false
hint = "If checked, all coordinates are relative to screen coordinates. \
Real numbers indicate horizontal values, imaginary numbers indicate \
vertical values. 0 indicates the left and bottom edges, 1 indicates \
the right and top edges. Since these coordinates are relative to the \
screen and not the fractal location, they will not change as you zoom. \
Also note that if your image is not square, circles and squares will \
not be true; they will be squashed or stretched by whatever amount \
your image isn't square."
endparam
param offsetamount
caption = "Offset Amount"
default = 0.0
hint = "If you need to shift your entire clipping region without altering its \
shape, you can specify an amount and a direction to offset it. Keep in \
mind that if you have an offset enabled, it will still be in effect \
while you use the eyedropper or explorer tools to select points."
endparam
param offsetangle
caption = "Offset Angle"
default = 0.0
visible = (@offsetamount != 0.0)
hint = "Indicates the angle of the offset. Note that, if your image is rotated, \
and you're not using screen-relative clipping, offset angles will be \
rotated too. Normally an angle of 0 is to the right and 90 is up, if \
your image is not rotated."
endparam
param rotation
caption = "Rotation"
default = 0.0
visible = (@shape != "circle" && @shape != "arbitrary polygon" && @shape != "Bézier curve")
hint = "Rotates the clipping shape. Note that, if you're using screen-relative \
coordinates and your image is not square, or if you've squashed or \
stretched the zoombox, the clipping shape will deform as you rotate it. \
Angles are in degrees, counter-clockwise."
endparam
heading
caption = "Visual Aids"
endheading
param showhandles
caption = "Show Handles"
default = true
hint = "If enabled, shows 'handles' in the image to mark where the control points \
of the shape are located. Very useful for working, but you probably want \
to turn this off before a final render."
endparam
param handlesnopreview
caption = "Not on previews"
default = true
visible = (@showhandles == true)
hint = "If checked, handles will not be shown on previews. Handles don't scale with \
window size, so they appear disproportionately large in the preview window."
endparam
param handlesnopreviewsize
caption = "Preview Threshold"
default = 275
visible = (@showhandles == true && @handlesnopreview == true)
hint = "Sets the threshold for determining a preview. Anything this size or \
smaller is considered a preview and handles will be hidden."
endparam
param handlesnorender
caption = "Not on renders"
default = true
visible = (@showhandles == true)
hint = "If checked, handles will be disabled for any image above a certain size. \
This is useful for automatically disabling handles on disk renders."
endparam
param handlesnorendersize
caption = "Render Threshold"
default = 1600
visible = (@showhandles == true && @handlesnorender == true)
hint = "Sets the threshold for determining a disk render. Anything this size or \
larger is considered a final render and handles will be hidden."
endparam
param handleswithnumbers
caption = "Show Numbers"
default = true
visible = (@showhandles == true && (@shape == "arbitrary polygon" || @shape == "Bézier curve"))
hint = "If checked, handles will be numbered."
endparam
param handleswithlines
caption = "Show Lines"
default = true
visible = (@showhandles == true && @shape == "Bézier curve")
hint = "If checked, dashed lines connecting tangent points to control points will be shown."
endparam
param handlesize
caption = "Handle Size"
default = 8
visible = (@showhandles == true)
hint = "Sets the size of the inside area of the handle. You may need to adjust this \
to make the handles more visible on some fractals."
endparam
param handlewidth
caption = "Handle Thickness"
default = 2
visible = (@showhandles == true)
hint = "Sets the thickness of the handles. You may need to adjust this to make the \
handles more visible on some fractals."
endparam
heading
caption = "Shape"
endheading
param shape
caption = "Clipping Shape"
default = 2
enum = "circle" "ellipse" "rectangle" "regular polygon" "arbitrary polygon" "Bézier curve"
hint = "Determines the shape of the clipping region. For squares, use a \
regular polygon with four sides."
endparam
; circle parameters
param circletype
caption = "Define Circle By"
default = 1
enum = "bounding box" "center and edge" "opposite edges" "three edge points"
visible = (@shape == "circle")
hint = "Choose how you want to define where the circle is. Different methods \
are useful in different circumstances."
endparam
complex param circleupperleft
caption = "Box Upper Left"
default = #center - 1/#magn + flip(1/#magn)
visible = (@shape == "circle") && (@circletype == "bounding box")
hint = "Sets the upper left corner of the bounding box that the circle fits in. \
Note that if your image is rotated, and you're not using screen-relative \
clipping, the bounding box for your circle will be rotated along with the \
image. In that case, you may find 'center and edge' easier to use."
endparam
complex param circlelowerright
caption = "Box Lower Right"
default = #center + 1/#magn - flip(1/#magn)
visible = (@shape == "circle") && (@circletype == "bounding box")
hint = "Sets the upper left corner of the bounding box that the circle fits in. \
Note that if your image is rotated, and you're not using screen-relative \
clipping, the bounding box for your circle will be rotated along with the \
image. In that case, you may find 'center and edge' easier to use."
endparam
complex param circlecenter
caption = "Circle Center"
default = #center
visible = (@shape == "circle") && (@circletype == "center and edge")
hint = "Sets the center of the circle. If you're not sure exactly where the center \
should go, you may find 'opposite edges' easier to use."
endparam
complex param circleedge
caption = "Circle Edge"
default = #center + 1/#magn
visible = (@shape == "circle") && (@circletype == "center and edge")
hint = "Sets the edge of the circle. If you need the edge to line up with something \
else in the image, you may find 'bounding box' easier to use."
endparam
complex param circleedge1
caption = "Circle Edge 1"
default = #center - 1/#magn
visible = (@shape == "circle") && (@circletype == "opposite edges" || @circletype == "three edge points")
hint = "Sets one of the edge points of the circle."
endparam
complex param circleedge2
caption = "Circle Edge 2"
default = #center + 1/#magn
visible = (@shape == "circle") && (@circletype == "opposite edges" || @circletype == "three edge points")
hint = "Sets one of the edge points of the circle."
endparam
complex param circleedge3
caption = "Circle Edge 3"
default = #center + flip(1/#magn)
visible = (@shape == "circle") && (@circletype == "three edge points")
hint = "Sets one of the edge points of the circle."
endparam
; ellipse parameters
param ellipsetype
caption = "Define Ellipse By"
default = 2
enum = "three corners" "center and two edges" "fixed angle"
visible = (@shape == "ellipse")
hint = "Choose how you want to define where the bounding box of the \
ellipse is. Different methods are useful in different circumstances."
endparam
complex param ellipseupperleft
caption = "Ellipse Upper Left"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "ellipse") && (@ellipsetype == "three corners")
hint = "Selects the upper left corner of the bounding box of the ellipse."
endparam
complex param ellipselowerright
caption = "Ellipse Lower Right"
default = #center + (1/#magn + flip(-0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "ellipse") && (@ellipsetype == "three corners")
hint = "Selects the lower right corner of the bounding box of the ellipse."
endparam
complex param ellipseupperright
caption = "Ellipse Upper Right"
default = #center + (1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "ellipse") && (@ellipsetype == "three corners")
hint = "Selects one of the other corners of the bounding box of the ellipse (upper right is \
suggested, but not required). Note that only points that are on a \
circle, centered on the ellipse's center, and passing through \
the upper left and lower right corners of the bounding box, will produce a valid \
bounding box; whatever point you actually choose for this third corner \
will be moved to that circle in order to produce a proper bounding box, and the \
ellipse will be placed inside that box. \
Practically speaking, you should use this corner to set the overall \
angle of the ellipse, after selecting opposite corners."
endparam
float param ellipse
caption = "Ellipse Angle"
default = #angle*180/#pi
visible = (@shape == "ellipse") && (@ellipsetype == "fixed angle")
hint = "Selects the rotation angle of the ellipse. This is a bit different \
from the general 'Rotation' parameter. If 'Rotation' is zero, then \
this parameter is used to set the angle of the ellipse, and the \
point you select for 'Ellipse Top Edge' will lie exactly on the ellipse bounding box's \
top edge, even if it's not the center of that edge. Changing this parameter \
after you've selected ellipse bounding box edge points will change the size of the \
ellipse, as different parts of the box edges have to pass through the points \
to make your bounding box. If you want to rotate the clipping shape without \
changing the size, use the general 'Rotation' parameter. Doing so, \
however, will affect your selection of 'Ellipse Edge' with the eyedropper \
or explorer tools. Angles are in degrees, counter-clockwise."
endparam
complex param ellipsecenter
caption = "Ellipse Center"
default = #center
visible = (@shape == "ellipse") && (@ellipsetype == "center and two edges" || @ellipsetype == "fixed angle")
hint = "Selects the center point of the ellipse."
endparam
complex param ellipsetop
caption = "Ellipse Top Edge"
default = #center + (flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "ellipse") && (@ellipsetype == "center and two edges" || @ellipsetype == "fixed angle")
hint = "Selects the center of the top edge of the bounding box of the ellipse, for 'center and \
two edges', or any point on the top edge of the bounding box, for 'fixed angle'. Note that, \
for 'center and two edges', this point also sets an implicit rotation for the ellipse."
endparam
complex param ellipseright
caption = "Ellipse Right Edge"
default = #center + (-1/#magn) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "ellipse") && (@ellipsetype == "center and two edges" || @ellipsetype == "fixed angle")
hint = "Selects the right edge of the bounding box of the ellipse. It can be anywhere on the \
right edge of the bounding box."
endparam
; rectangle parameters
param recttype
caption = "Define Rect By"
default = 2
enum = "three corners" "center and two edges" "fixed angle"
visible = (@shape == "rectangle")
hint = "Choose how you want to define where the rectangle is. Different methods \
are useful in different circumstances."
endparam
complex param rectupperleft
caption = "Rect Upper Left"
default = #center + (-1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "rectangle") && (@recttype == "three corners")
hint = "Selects the upper left corner of the rectangle."
endparam
complex param rectlowerright
caption = "Rect Lower Right"
default = #center + (1/#magn + flip(-1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "rectangle") && (@recttype == "three corners")
hint = "Selects the lower right corner of the rectangle."
endparam
complex param rectupperright
caption = "Rect Upper Right"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "rectangle") && (@recttype == "three corners")
hint = "Selects one of the other corners of the rectangle (upper right is \
suggested, but not required). Note that only points that are on a \
circle, centered on the rectangle's center, and passing through \
the upper left and lower right corners, will produce a valid \
rectangle; whatever point you actually choose for this third corner \
will be moved to that circle in order to produce a proper rectangle. \
Practically speaking, you should use this corner to set the overall \
angle of the rectangle, after selecting opposite corners."
endparam
float param rectangle
caption = "Rectangle Angle"
default = #angle*180/#pi
visible = (@shape == "rectangle") && (@recttype == "fixed angle")
hint = "Selects the rotation angle of the rectangle. This is a bit different \
from the general 'Rotation' parameter. If 'Rotation' is zero, then \
this parameter is used to set the angle of the rectangle, and the \
point you select for 'Rect Top Edge' will lie exactly on the rectangle's \
top edge, even if it's not the center of that edge. Changing this parameter \
after you've selected rectangle edge points will change the size of the \
rectangle, as different parts of the edges have to pass through the points \
to make your rectangle. If you want to rotate the clipping shape without \
changing the size, use the general 'Rotation' parameter. Doing so, \
however, will affect your selection of 'Rect Edge' with the eyedropper \
or explorer tools. Angles are in degrees, counter-clockwise."
endparam
complex param rectcenter
caption = "Rect Center"
default = #center
visible = (@shape == "rectangle") && (@recttype == "center and two edges" || @recttype == "fixed angle")
hint = "Selects the center point of the rectangle."
endparam
complex param recttop
caption = "Rect Top Edge"
default = #center + (flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "rectangle") && (@recttype == "center and two edges" || @recttype == "fixed angle")
hint = "Selects the center of the top edge of the rectangle, for 'center and \
two edges', or any point on the top edge, for 'fixed angle'. Note that, \
for 'center and two edges', this point also sets an implicit rotation for the rectangle."
endparam
complex param rectright
caption = "Rect Right Edge"
default = #center + (-1/#magn) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "rectangle") && (@recttype == "center and two edges" || @recttype == "fixed angle")
hint = "Selects the right edge of the rectangle. It can be anywhere on the \
right edge of the rectangle."
endparam
; regular polygon parameters
param polygontype
caption = "Define Polygon By"
default = 2
enum = "center and corner" "center and edge" "fixed angle"
visible = (@shape == "regular polygon")
hint = "Choose how you want to define where the polygon is. Different methods \
are useful in different circumstances."
endparam
param polygonorder
caption = "Polygon Sides"
default = 3.0
min = 1
visible = (@shape == "regular polygon")
hint = "Sets the number of sides to the polygon. Note that you can use 2 to \
create a 'line' polygon, if you're using the 'center and edge' method. \
You can use 1 side to clip half the plane (one side of the line is \
clipped, the other is not clipped)."
endparam
float param polygonangle
caption = "Polygon Angle"
default = #angle*180/#pi
visible = (@shape == "regular polygon") && (@polygontype == "fixed angle")
hint = "Selects the rotation angle of the polygon. This is a bit different \
from the general 'Rotation' parameter. If 'Rotation' is zero, then \
this parameter is used to set the angle of the polygon, and the \
point you select for 'Polygon Edge' will lie exactly on the polygon's \
edge, even if it's not the center of that edge. Changing this parameter \
after you've selected a polygon edge point will change the size of the \
polygon, as different parts of the edge have to pass through the point \
to make your polygon. If you want to rotate the clipping shape without \
changing the size, use the general 'Rotation' parameter. Doing so, \
however, will affect your selection of 'Polygon Edge' with the eyedropper \
or explorer tools. Angles are in degrees, counter-clockwise."
endparam
complex param polygoncenter
caption = "Polygon Center"
default = #center
visible = (@shape == "regular polygon")
hint = "Sets the center of the polygon."
endparam
complex param polygoncorner
caption = "Polygon Corner"
default = #center + (1/#magn) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "regular polygon") && (@polygontype == "center and corner")
hint = "Selects one corner of the polygon. Note that if you have 'Lock Rotation' \
checked, you're only selecting a point on the circumscribed circle, and \
you must use the general 'Rotation' parameter to rotate the polygon."
endparam
complex param polygonedge
caption = "Polygon Edge"
default = #center + (1/#magn) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "regular polygon") && (@polygontype == "center and edge" || @polygontype == "fixed angle")
hint = "Selects the center of an edge of the polygon, for 'center and edge'; \
selects an edge point (not necessarily the center of an edge) for \
'fixed angle'."
endparam
; arbitrary polygon parameters
param polywinding
caption = "Overlap Handling"
default = 1
enum = "convex polygon only" "all inside" "invert overlaps"
hint = "Chooses how to handle overlapped regions of a polygon. 'Convex polygon \
only' is the original behavior and requires your polygon be purely \
convex; it is provided for backwards-compatibility with your old \
parameters. 'All inside' will treat all inside areas the same even \
if they are part of your polygon overlapping itself. 'Invert overlaps' \
will treat inside, but overlapped, areas of your polygon as outside."
visible = (@shape == "arbitrary polygon")
enabled = (@clipversion >= 101)
endparam
param polystar
caption = "Polygon is a Star"
default = false
visible = (@shape == "arbitrary polygon")
hint = "If set, the polygon is a star. You need only set the shape of one arm \
of the star, and the center; the rest of the points will be computed \
for you."
endparam
complex param polystarcenter
caption = "Star Center"
default = #center
visible = (@shape == "arbitrary polygon") && (@polystar == true)
hint = "Sets the center of the star shape. The polygon shape you set will be \
rotated around this point. Note that moving the center will change \
the star's shape."
endparam
param polystarorder
caption = "Star Points"
default = 8
visible = (@shape == "arbitrary polygon") && (@polystar == true)
hint = "Sets the number of arms in the star. Note that the number of arms \
multiplied by the number of points in the polygon must be less than \
1000. Also note that the larger this product is, the slower this \
transform will become."
endparam
param polystarlimit
caption = " Limit to"
default = 0
visible = (@shape == "arbitrary polygon") && (@polystar == true)
hint = "If not zero, this will limit the star to just this many repetitions. \
You can use this to create fans with a particular edge."
endparam
param polystarusecenter
caption = "Include Center"
default = true
visible = (@shape == "arbitrary polygon") && (@polystar == true) && (@polystarlimit > 0)
hint = "If checked, the center point will be included in the limited star shape."
endparam
param polyorder
caption = "Polygon Sides"
default = 3
min = 2
max = 20
visible = (@shape == "arbitrary polygon")
hint = "Sets the number of sides to the polygon."
endparam
complex param polypoint1
caption = "Point 1"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon")
hint = "Sets the first point in the polygon."
endparam
complex param polypoint2
caption = "Point 2"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon")
hint = "Sets the second point in the polygon."
endparam
complex param polypoint3
caption = "Point 3"
default = #center + (0.75/#magn + flip(-0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 3)
hint = "Sets the third point in the polygon."
endparam
complex param polypoint4
caption = "Point 4"
default = #center + (0.25/#magn + flip(-0.75/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 4)
hint = "Sets the fourth point in the polygon."
endparam
complex param polypoint5
caption = "Point 5"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 5)
hint = "Sets the fifth point in the polygon."
endparam
complex param polypoint6
caption = "Point 6"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 6)
hint = "Sets the sixth point in the polygon."
endparam
complex param polypoint7
caption = "Point 7"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 7)
hint = "Sets the seventh point in the polygon."
endparam
complex param polypoint8
caption = "Point 8"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 8)
hint = "Sets the eighth point in the polygon."
endparam
complex param polypoint9
caption = "Point 9"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 9)
hint = "Sets the ninth point in the polygon."
endparam
complex param polypoint10
caption = "Point 10"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 10)
hint = "Sets the tenth point in the polygon."
endparam
complex param polypoint11
caption = "Point 11"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 11)
hint = "Sets the eleventh point in the polygon."
endparam
complex param polypoint12
caption = "Point 12"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 12)
hint = "Sets the twelfth point in the polygon."
endparam
complex param polypoint13
caption = "Point 13"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 13)
hint = "Sets the thirteenth point in the polygon."
endparam
complex param polypoint14
caption = "Point 14"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 14)
hint = "Sets the fourteenth point in the polygon."
endparam
complex param polypoint15
caption = "Point 15"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 15)
hint = "Sets the fifteenth point in the polygon."
endparam
complex param polypoint16
caption = "Point 16"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 16)
hint = "Sets the sixteenth point in the polygon."
endparam
complex param polypoint17
caption = "Point 17"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 17)
hint = "Sets the seventeenth point in the polygon."
endparam
complex param polypoint18
caption = "Point 18"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 18)
hint = "Sets the eighteenth point in the polygon."
endparam
complex param polypoint19
caption = "Point 19"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 19)
hint = "Sets the nineteenth point in the polygon."
endparam
complex param polypoint20
caption = "Point 20"
default = #center + (-0.5/#magn + flip(-0.25/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "arbitrary polygon") && (@polyorder >= 20)
hint = "Sets the twentieth point in the polygon."
endparam
; Bézier curve parameters
param curvewinding
caption = "Overlap Handling"
default = 1
enum = "all inside" "invert overlaps"
hint = "Chooses how to handle overlapped regions of a curve. 'All inside' will \
treat all inside areas the same even if they are part of your polygon \
overlapping itself. 'Invert overlaps' will treat inside, but overlapped, \
areas of your polygon as outside."
visible = (@shape == "Bézier curve")
endparam
param curvestar
caption = "Curve is a Star"
default = false
visible = (@shape == "Bézier curve")
hint = "If set, the curve is a star. You need only set the shape of one arm \
of the star, and the center; the rest of the points will be computed \
for you."
endparam
complex param curvestarcenter
caption = "Star Center"
default = #center
visible = (@shape == "Bézier curve") && (@curvestar == true)
hint = "Sets the center of the star shape. The curve shape you set will be \
rotated around this point. Note that moving the center will change \
the star's shape."
endparam
param curvestarorder
caption = "Star Points"
default = 8
visible = (@shape == "Bézier curve") && (@curvestar == true)
hint = "Sets the number of arms in the star. Note that the number of arms \
multiplied by the number of points in the curve must be less than \
1000. Also note that the larger this product is, the slower this \
transform will become."
endparam
param curvestarlimit
caption = " Limit to"
default = 0
visible = (@shape == "Bézier curve") && (@curvestar == true)
hint = "If not zero, this will limit the star to just this many repetitions. \
You can use this to create fans with a particular edge."
endparam
param curvestarusecenter
caption = "Include Center"
default = true
visible = (@shape == "Bézier curve") && (@curvestar == true) && (@curvestarlimit > 0)
hint = "If checked, the center point will be included in the limited star shape."
endparam
param curveorder
caption = "Curve Control Points"
default = 3
min = 1
max = 20
visible = (@shape == "Bézier curve")
hint = "Sets the number of control points in the curve."
endparam
param curvetangent1auto
caption = "Automatically set point 1"
default = true
visible = (@shape == "Bézier curve")
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent1
caption = "Tangent Point 1"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent1auto == false)
hint = "Sets the first tangent point on the curve."
endparam
complex param curvepoint1
caption = "Control Point 1"
default = #center + (-0.5/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve")
hint = "Sets the first control point in the curve."
endparam
; param curveclose1
; caption = "Close curve at this point"
; default = false
; visible = (@shape == "Bézier curve") && (@curveorder >= 2)
; hint = "Sets whether the curve is closed (looped back to its starting \
; point) at this point. You can add another curve within the same \
; transform by adding more points; they will be a separate curve."
; endparam
param curvetangent2auto
caption = "Automatically set point 2"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 2)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent2
caption = "Tangent Point 2"
default = #center + (0/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent2auto == false) && (@curveorder >= 2)
hint = "Sets the second tangent point on the curve."
endparam
complex param curvepoint2
caption = "Control Point 2"
default = #center + (0.75/#magn + flip(0.75/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 2)
hint = "Sets the second control point in the curve."
endparam
param curveclose2
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 3 || (@curveorder >= 2 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent3auto
caption = "Automatically set point 3"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 3)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent3
caption = "Tangent Point 3"
default = #center + (1/#magn + flip(-0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent3auto == false) && (@curveorder >= 3)
hint = "Sets the third tangent point on the curve."
endparam
complex param curvepoint3
caption = "Control Point 3"
default = #center + (0/#magn + flip(-1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 3)
hint = "Sets the third control point in the curve."
endparam
param curveclose3
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 4 || (@curveorder >= 3 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent4auto
caption = "Automatically set point 4"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 4)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent4
caption = "Tangent Point 4"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent4auto == false) && (@curveorder >= 4)
hint = "Sets the fourth tangent point on the curve."
endparam
complex param curvepoint4
caption = "Control Point 4"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 4)
hint = "Sets the fourth control point in the curve."
endparam
param curveclose4
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 5 || (@curveorder >= 4 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent5auto
caption = "Automatically set point 5"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 5)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent5
caption = "Tangent Point 5"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent5auto == false) && (@curveorder >= 5)
hint = "Sets the fifth tangent point on the curve."
endparam
complex param curvepoint5
caption = "Control Point 5"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 5)
hint = "Sets the fifth control point in the curve."
endparam
param curveclose5
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 6 || (@curveorder >= 5 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent6auto
caption = "Automatically set point 6"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 6)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent6
caption = "Tangent Point 6"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent6auto == false) && (@curveorder >= 6)
hint = "Sets the sixth tangent point on the curve."
endparam
complex param curvepoint6
caption = "Control Point 6"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 6)
hint = "Sets the sixth control point in the curve."
endparam
param curveclose6
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 7 || (@curveorder >= 6 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent7auto
caption = "Automatically set point 7"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 7)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent7
caption = "Tangent Point 7"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent7auto == false) && (@curveorder >= 7)
hint = "Sets the seventh tangent point on the curve."
endparam
complex param curvepoint7
caption = "Control Point 7"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 7)
hint = "Sets the seventh control point in the curve."
endparam
param curveclose7
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 8 || (@curveorder >= 7 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent8auto
caption = "Automatically set point 8"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 8)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent8
caption = "Tangent Point 8"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent8auto == false) && (@curveorder >= 8)
hint = "Sets the eighth tangent point on the curve."
endparam
complex param curvepoint8
caption = "Control Point 8"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 8)
hint = "Sets the eighth control point in the curve."
endparam
param curveclose8
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 9 || (@curveorder >= 8 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent9auto
caption = "Automatically set point 9"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 9)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent9
caption = "Tangent Point 9"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent9auto == false) && (@curveorder >= 9)
hint = "Sets the ninth tangent point on the curve."
endparam
complex param curvepoint9
caption = "Control Point 9"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 9)
hint = "Sets the ninth control point in the curve."
endparam
param curveclose9
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 10 || (@curveorder >= 9 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent10auto
caption = "Automatically set point 10"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 10)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent10
caption = "Tangent Point 10"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent10auto == false) && (@curveorder >= 10)
hint = "Sets the tenth tangent point on the curve."
endparam
complex param curvepoint10
caption = "Control Point 10"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 10)
hint = "Sets the tenth control point in the curve."
endparam
param curveclose10
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 11 || (@curveorder >= 10 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent11auto
caption = "Automatically set point 11"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 11)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent11
caption = "Tangent Point 11"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent11auto == false) && (@curveorder >= 11)
hint = "Sets the eleventh tangent point on the curve."
endparam
complex param curvepoint11
caption = "Control Point 11"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 11)
hint = "Sets the eleventh control point in the curve."
endparam
param curveclose11
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 12 || (@curveorder >= 11 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent12auto
caption = "Automatically set point 12"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 12)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent12
caption = "Tangent Point 12"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent12auto == false) && (@curveorder >= 12)
hint = "Sets the twelfth tangent point on the curve."
endparam
complex param curvepoint12
caption = "Control Point 12"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 12)
hint = "Sets the twelfth control point in the curve."
endparam
param curveclose12
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 13 || (@curveorder >= 12 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent13auto
caption = "Automatically set point 13"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 13)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent13
caption = "Tangent Point 13"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent13auto == false) && (@curveorder >= 13)
hint = "Sets the thirteenth tangent point on the curve."
endparam
complex param curvepoint13
caption = "Control Point 13"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 13)
hint = "Sets the thirteenth control point in the curve."
endparam
param curveclose13
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 14 || (@curveorder >= 13 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent14auto
caption = "Automatically set point 14"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 14)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent14
caption = "Tangent Point 14"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent14auto == false) && (@curveorder >= 14)
hint = "Sets the fourteenth tangent point on the curve."
endparam
complex param curvepoint14
caption = "Control Point 14"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 14)
hint = "Sets the fourteenth control point in the curve."
endparam
param curveclose14
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 15 || (@curveorder >= 14 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent15auto
caption = "Automatically set point 15"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 15)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent15
caption = "Tangent Point 15"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent15auto == false) && (@curveorder >= 15)
hint = "Sets the fifteenth tangent point on the curve."
endparam
complex param curvepoint15
caption = "Control Point 15"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 15)
hint = "Sets the fifteenth control point in the curve."
endparam
param curveclose15
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 16 || (@curveorder >= 15 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent16auto
caption = "Automatically set point 16"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 16)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent16
caption = "Tangent Point 16"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent16auto == false) && (@curveorder >= 16)
hint = "Sets the sixteenth tangent point on the curve."
endparam
complex param curvepoint16
caption = "Control Point 16"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 16)
hint = "Sets the sixteenth control point in the curve."
endparam
param curveclose16
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 17 || (@curveorder >= 16 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent17auto
caption = "Automatically set point 17"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 17)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent17
caption = "Tangent Point 17"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent17auto == false) && (@curveorder >= 17)
hint = "Sets the seventeenth tangent point on the curve."
endparam
complex param curvepoint17
caption = "Control Point 17"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 17)
hint = "Sets the seventeenth control point in the curve."
endparam
param curveclose17
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 18 || (@curveorder >= 17 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent18auto
caption = "Automatically set point 18"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 18)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent18
caption = "Tangent Point 18"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent18auto == false) && (@curveorder >= 18)
hint = "Sets the eighteenth tangent point on the curve."
endparam
complex param curvepoint18
caption = "Control Point 18"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 18)
hint = "Sets the eighteenth control point in the curve."
endparam
param curveclose18
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 19 || (@curveorder >= 18 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent19auto
caption = "Automatically set point 19"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 19)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent19
caption = "Tangent Point 19"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent19auto == false) && (@curveorder >= 19)
hint = "Sets the nineteenth tangent point on the curve."
endparam
complex param curvepoint19
caption = "Control Point 19"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 19)
hint = "Sets the nineteenth control point in the curve."
endparam
param curveclose19
caption = "Close curve at this point"
default = false
visible = (@shape == "Bézier curve") && (@curveorder >= 20 || (@curveorder >= 19 && @curvestar))
hint = "Sets whether the curve is closed (looped back to its starting \
point) at this point. You can add another curve within the same \
transform by adding more points; they will be a separate curve."
endparam
param curvetangent20auto
caption = "Automatically set point 20"
default = true
visible = (@shape == "Bézier curve") && (@curveorder >= 20)
hint = "If checked, the tangent point will be automatically computed by \
averaging adjacent control points. This will ensure a smooth \
line between the two. If you need a corner, uncheck this box \
and set the tangent point to be the corner."
endparam
complex param curvetangent20
caption = "Tangent Point 20"
default = #center + (-1/#magn + flip(0.5/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curvetangent20auto == false) && (@curveorder >= 20)
hint = "Sets the twentieth tangent point on the curve."
endparam
complex param curvepoint20
caption = "Control Point 20"
default = #center + (1/#magn + flip(1/#magn)) * ((0,1)^(#angle*2/#pi))
visible = (@shape == "Bézier curve") && (@curveorder >= 20)
hint = "Sets the twentieth control point in the curve."
endparam
; curve always closes here!
; param curveclose20
; caption = "Close curve at this point"
; default = false
; visible = (@shape == "Bézier curve") && (@curveorder >= 21 || (@curveorder >= 20 && @curvestar))
; hint = "Sets whether the curve is closed (looped back to its starting \
; point) at this point. You can add another curve within the same \
; transform by adding more points; they will be a separate curve."
; endparam
}
dmj3-UndoGradient {
;
; This transform will partially "undo" other transforms,
; in a gradient that sweeps across the image. Note that,
; even if multiple UGs are used, each will always undo
; towards the ORIGINAL UNTRANSFORMED IMAGE, not to the
; last UG.
;
transform:
complex p = (0,0)
complex t = (0,1) ^ (@ugtwist / 90.0)
float fudge = 1.0
float fudge2 = 1.0
IF (@ugversion < 102)
fudge = #pi/180
fudge2 = -1.0
ENDIF
; first, determine where the "original" point was, before transformation
float sx = real(#screenpixel) - #width/2
float sy = #height/2 - imag(#screenpixel)
sx = (sx - fudge2 * sy * tan(#skew*fudge)) / #stretch
float sa = sin(#angle*fudge)
float ca = cos(#angle*fudge)
float rx = ca*sx - sa*sy
float ry = ca*sy + sa*sx
IF (#width*3 <= #height*4)
float pd = 4 / (#width*#magn)
ELSE
float pd = 3 / (#height*#magn)
ENDIF
p = #center + rx*pd + flip(ry*pd)
; determine the gradient value at this point
float g = 0.0
IF (@ugtype == "circular")
g = cabs(p-@ugcenter) / @uglength
ELSEIF (@ugtype == "linear")
g = real((p-@ugcenter) * conj((0,1)^(@ugangle/90)))
IF (@ugversion >= 101); should have been done originally
g = g / @uglength
ENDIF
ELSEIF (@ugtype == "radial")
g = (atan2(-(p-@ugcenter) * conj((0,1)^(@ugangle/90))) / #pi + 1) * 0.5
ENDIF
; transform g
IF (@ugstartclip && g < 0)
g = 0
ENDIF
IF (@ugendclip && g > 1)
g = 1
ENDIF
g = @ugstart + (@ugend-@ugstart)*g
IF (@ugclamplow && g < 0)
g = 0
ENDIF
IF (@ugclamphigh && g > 1)
g = 1
ENDIF
IF (@ugtransfer == "sqr")
g = sqr(g)
ELSEIF (@ugtransfer == "sqrt")
g = sqrt(abs(g))
ELSEIF (@ugtransfer == "hyperbolic")
IF (g == 0)
g = 1e20
ELSE
g = 1/g
ENDIF
ENDIF
IF (@ugtwistmode == "untransformed point")
#pixel = p + (#pixel-p) * t * g
ELSE
#pixel = #pixel + (p-#pixel) * t * (1-g)
ENDIF
default:
title = "Undo Gradient"
helpfile = "dmj-pub\dmj-pub-uf-undo.htm"
param ugversion
caption = "Undo Formula Version"
default = 102
hint = "You should never see this parameter; it's used internally to track \
which version of the formula was used to create your image, so that \
if a bug is found which breaks backwards-compatibility, the formula \
can adapt transparently. You are seeing this because the formula \
was added at an older version, and will render differently from a \
newly-applied copy of the formula."
visible = (@ugversion < 102)
enabled = false
endparam
param ugtype
caption = "Gradient Type"
default = 1
enum = "linear" "circular" "radial"
hint = "Selects the overall shape of the gradient."
endparam
complex param ugcenter
caption = "Gradient Point"
default = #center
hint = "Selects the anchor point of the gradient. This is a point \
at which full undo would normally be applied."
endparam
float param ugangle
caption = "Gradient Angle"
default = #angle
hint = "Selects the overall angle for the gradient. Note that this angle is \
relative to the image's own rotation angle; if you want the angle \
relative to the image border, subtract the angle from the Location \
tab from this value and enter it here."
visible = (@ugtype != "circular")
endparam
float param uglength
caption = "Gradient Length"
default = 2/#magn
visible = (@ugtype != "radial")
enabled = (@ugtype == "circular" || @ugversion >= 101)
hint = "Sets the length of the gradient. If this is disabled, it is because \
the formula was applied when it was a buggy version and this parameter \
was incorrectly ignored; delete and re-add the formula to gain access \
to this parameter."
endparam
param ugstart
caption = "Gradient Start"
default = 0.0
hint = "Sets the value of the gradient at the gradient point. \
A value of 0 indicates full undo (an untransformed \
image) and a value of 1 indicates no undo (a fully \
transformed image). You may enter values beyond 0 and 1, \
including negative values, if you like."
endparam
param ugstartclip
caption = "Extend Gradient"
default = false
visible = (@ugtype == "linear")
hint = "If checked, the gradient will extend past the gradient \
point and be interpolated accordingly. This is useful \
if you want to fix the undo amount at the gradient point \
at a value other than 0 or 1, but still allow the gradient \
to continue past that point."
endparam
param ugend
caption = "Gradient End"
default = 1.0
hint = "Sets the value of the gradient at the end of the gradient. \
A value of 0 indicates full undo (an untransformed \
image) and a value of 1 indicates no undo (a fully \
transformed image). You may enter values beyond 0 and 1, \
including negative values, if you like."
endparam
param ugendclip
caption = "Extend Gradient"
default = false
visible = (@ugtype != "radial")
hint = "If checked, the gradient will extend past the end of the gradient \
and be interpolated accordingly. This is useful \
if you want to fix the undo amount at the gradient end \
at a value other than 0 or 1, but still allow the gradient \
to continue past that point."
endparam
param ugclamplow
caption = "Clamp Gradient at 0"
default = true
hint = "If checked, gradient values will be clamped to 0 if they \
fall below 0. This is most useful if you've set up your \
gradient with a range greater than 0 to 1, especially if \
you have Extend Gradient selected."
endparam
param ugclamphigh
caption = "Clamp Gradient at 1"
default = true
hint = "If checked, gradient values will be clamped to 1 if they \
go above 1. This is most useful if you've set up your \
gradient with a range greater than 0 to 1, especially if \
you have Extend Gradient selected."
endparam
param ugtransfer
caption = "Gradient Transfer"
default = 0
enum = "linear" "sqr" "sqrt" "hyperbolic"
hint = "Selects the transfer function. You can use this to skew \
the gradient more towards one end or the other, without \
changing the gradient values at the ends. (Hyperbolic, \
however, WILL change the endpoint values, as one end will \
tend towards infinity.)"
endparam
param ugtwist
caption = "Undo Angle"
default = 0.0
hint = "Rotates the Undo effect. Measured in degrees."
endparam
param ugtwistmode
caption = "Measure Angle From"
default = 1
enum = "transformed point" "untransformed point"
hint = "Sets whether to measure the rotation from the transformed \
point or the untransformed point. If the untransformed point \
is used, this will in effect rotate all the transforms around \
the untransformed point. Otherwise, the transform will be \
twisted in odd ways."
endparam
}