Comment{ jlb.ulb jimblue66@gmail.com } class JLB_SwitchFormula(common.ulb:Formula) { ; modified from MMF_SwitchFormula in mmf.ulb ; meant to be used with JLB_SwitchCombo for building switchable combinations of SFormulas. ; works with both convergent and divergent formulas. public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_SwitchFormula(Generic pparent) Formula.Formula(pparent) if @v_simpleswitch < 200 if (fType = @p_mandy) fValue = fConstant = @p_start else fValue = fConstant = @p_seed endif else fType = true fValue = fConstant = @p_start endif endfunc ; @param pz the initial location ; @return initial z for iteration complex func Init(complex pz) Formula.Init(pz) if @v_simpleswitch < 200 if fType fConstant = pz return fValue else return pz endif endif return pz endfunc protected: complex fConstant complex fValue bool fType default: ; no title, so just a base class rating = notRecommended int param v_simpleswitch caption = "Version (Simple Switch Formula)" default = 200 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_simpleswitch < 200 endparam ; all these ignored for version 2 bool param p_mandy caption = "Mandelbrot ?" default = true hint = "Disable for Julia mode." visible = false endparam complex param p_start caption = "Mandelbrot mode Start Value" default = (0,0) visible = @p_mandy endparam complex param p_seed caption = "Julia mode Constant" default = (0.3,0) visible = !@p_mandy && @v_simpleswitch < 200 endparam complex param p_power ; hides the one from base class caption = "Exponent" default = (2,0) hint = "Defines the primary exponent for the fractal." visible = false endparam } ; version 1, always starts in M mode ; version 2 each formula choose M or J mode class JLB_SwitchCombo(JLB_SwitchFormula) { public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_SwitchCombo(Generic pparent) JLB_SwitchFormula.JLB_SwitchFormula(pparent) if @v_switchcombo < 200 fType = @p_mandy if fType fValue = fConstant = @p_start else fValue = fConstant = @p_seed endif else fType = true fValue = fConstant = #pixel ;@p_seed endif m_SFormula1 = new @f_SFormula1(this) m_SFormula2 = new @f_SFormula2(this) ; S = new State(#pixel, fValue, @p_bType, @p_divtype, @p_diverge, @p_angle, @p_converge) S = new State(#pixel, fValue, @p_bType, @p_divtype, @p_diverge, @p_converge) endfunc ; @param pz the initial location ; @return initial z for iteration complex func Init(complex pz) JLB_SwitchFormula.Init(pz) m_count = 0 if @f_SFormula1 == JLB_NothingSFormula ; skip initial formula m_count = @n1 endif complex rvalue if fType ; Mandelbrot type fConstant = pz rvalue = fValue else ; Julia type rvalue = pz endif S.pzold = S.pz = pz S.pc = fConstant if @n1 > 0 && @p_ibail ; if formula 1 has its own bail criteria S.bType = @p_ibType S.divType = @p_idivtype S.dValue = @p_idiverge S.cValue = @p_iconverge ; S.dCos = cos(@p_iangle * #pi/180.0) ; S.dSin = sin(@p_iangle * #pi/180.0) else ; formulas 1 and 2 have the same criteria S.bType = @p_bType S.divType = @p_divtype S.dValue = @p_diverge S.cValue = @p_converge endif S.iters = 0 S.tooMany = false S.bailed = false m_SFormula1.Init(pz, S.pc) m_SFormula2.Init(pz, S.pc) return rvalue ; value ignored endfunc ; Called by Switch Combo Formula in jlb.ufm ; @param f = flag for Mandelbrot or Julia mode, true for Mandelbrots ; @param v the value to be used as the start value or constant func SetParams(bool f, complex v) fType = f fValue = fConstant = v endfunc ; @param pz ; Iterate--all the work is done in the plug-in. complex func Iterate(complex pz) ; argument is ignored ; Initial formula if m_count < @n1 setz() m_SFormula1.Iterate(S) m_count = m_count + 1 if S.bailed ; set up for formula 2, which may have different bailing criteria m_count = @n1 S.bType = @p_bType S.divType = @p_divtype S.dValue = @p_diverge S.cValue = @p_converge ;S.tooMany = false ; S.dCos = cos(@p_angle * #pi/180.0) ; S.dSin = sin(@p_angle * #pi/180.0) S.bailed = S.IsBailedOut(S.pz) ; check for new bailout criterion endif else ; Main formula setz() m_SFormula2.Iterate(S) m_count = m_count + 1 endif return S.pz endfunc func setz () if S.iters > 0 && @zdz > 0 zz = S.pz if @zdz == "z-z_prev" zz = zz - S.pzold elseif @zdz == "z/z_prev" zz = zz / S.pzold elseif @zdz == "z-pixel" zz = zz - #pixel elseif @zdz == "z/pixel" zz = zz / #pixel endif S.pz = zz endif endfunc ; @param pz ; @return true if bailed out. bool func IsBailedOut(complex pz) return S.bailed endfunc protected: complex fConstant complex fValue bool fType State S SFormula m_SFormula1 SFormula m_SFormula2 int m_count default: title = "SwitchCombo" rating = recommended int param v_switchcombo caption = "Version" default = 220801 ; changes today don't break old formulas hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_switchcombo < 200 endparam param zdz caption = "Use for zz" enum = "z" "z-z_prev" "z/z_prev" "z-pixel" "z/pixel" default = 0 visible = true ;@v_switchcombo > 200 endparam ; complex param p_power ; Hide p_power from Formula ; visible = false ; endparam ; this group only for version 100 bool param p_mandy caption = "Mandelbrot?" default = true hint = "Disable for Julia mode." visible = false ;@p_manual endparam complex param p_start caption = "Mandelbrot Start Value" default = (0,0) visible = false ; @p_mandy && @p_manual ; always starts with pz = #pixel endparam complex param p_seed caption = "Julia Constant" default = (0.3,0) visible = false ; !@p_mandy && @p_manual endparam SFormula param f_SFormula1 caption = "Initial Formula" default = JLB_NothingSFormula endparam int param n1 caption = "Repeats of Initial Formula" default = 1 min = 0 visible = @f_SFormula1 != JLB_NothingSFormula endparam ; Heading ; Endheading bool param p_ibail caption = "Separate bailout critera?" default = false visible = (@f_SFormula1 != JLB_NothingSFormula) && @n1 > 0 endparam param p_ibType caption = "How to stop" enum = "Z diverges" "Z converges" "Either one" default = 0 visible = (@f_SFormula1 != JLB_NothingSFormula) && @p_ibail && @n1 > 0 endparam float param p_idiverge caption = "Initial divergence test" exponential = true default = 1e4 visible = (@f_SFormula1 != JLB_NothingSFormula) && @p_ibail && (@p_ibtype == 0 || @p_ibType == 2) && @n1 > 0 hint = "Defines how soon an orbit bails out as z gets large." endparam param p_idivtype caption = "Initial divergence type" default = 0 enum = "mod" "real" "imag" "or" "and" "manh" \ "real+imag" "real-imag" "real*imag" "max(real,imag)" "min(real,imag)" visible = (@f_SFormula1 != JLB_NothingSFormula) && @p_ibail && (@p_ibtype == 0 || @p_ibType == 2) && @n1 > 0 endparam ; float param p_iangle ; caption = "angle" ; default = -45 ; hint = "Distance is from a line at this angle. Angle 0 gives same result as imag, \ ; 90 same as real, 45 same as manr." ; visible = (@f_SFormula1 != JLB_NothingSFormula) && @p_ibail && (@p_ibtype == 0 || @p_ibType == 2) && @p_idivtype == 7 && @n1 > 0 ; endparam float param p_iangle ; wasn't used before. keep this to avoid error messages default = 0 visible = false endparam float param p_iconverge caption = "Initial convergence test" exponential = true default = 1e-6 visible = (@f_SFormula1 != JLB_NothingSFormula) && @p_ibail && (@p_ibtype == 1 || @p_ibType == 2) && @n1 > 0 hint = "Defines how soon an orbit bails out as z converges to a value. \ With multiple formulas, false convergence is possible." endparam ; Heading ; caption = "End of Initial Formula, start of Main Formula" ; Endheading SFormula param f_SFormula2 caption = "Main Formula" default = JLB_MandelbrotSFormula hint = "Repeat until bailout, convergence, or iteration limit." endparam Heading caption = "Bailout" Endheading param p_bType caption = "How to stop" enum = "Z diverges" "Z converges" "Either one" default = 0 endparam float param p_diverge caption = "Divergence test" exponential = true default = 1e4 visible = @p_btype == 0 || @p_bType == 2 hint = "Defines how soon an orbit bails out as z gets large." endparam param p_divtype caption = "Divergence type" default = 0 enum = "mod" "real" "imag" "or" "and" "manh" \ "real+imag" "real-imag" "real*imag" "max(real,imag)" "min(real,imag)" visible = @p_btype == 0 || @p_bType == 2 endparam float param p_angle ; wasn't used before. keep this to avoid error messages default = 0 visible = false endparam float param p_converge caption = "Convergence test" exponential = true default = 1e-6 visible = @p_btype == 1 || @p_bType == 2 hint = "Defines how soon an orbit bails out as z converges to a value. \ With multiple formulas, false convergence is possible." endparam } ; the State of an iteration for a pixel class State { public: ; constructor initializes the state ; func State(complex z, complex c, int b, int dt, float dv, float angle, float cv) func State(complex z, complex c, int b, int dt, float dv, float cv) pzold = pz = z pc = c bType = b ; 0, 1, or 2 for divergence, convergence, or both divType = dt ; 0 to 8 ; dCos = cos(angle * #pi/180.0) ; dSin = sin(angle * #pi/180.0) dvalue = dv cvalue = cv iters = 0 tooMany = false ; bailed = false endfunc ; after an iteration has calculated a new z, and possibly a new c, update bool func Update(const complex z, const complex c) pzold = pz pz = z pc = c iters = iters + 1 bailed = IsBailedOut(pz) return bailed endfunc bool func IsBailedOut(const complex pz) tooMany = iters >= #maxiter if btype == 3 bailed = toomany return bailed endif bool diverged = false if bType == 0 || bType == 2 float tmp = |pz| if (isNan(tmp) || isInf(tmp)) return true endif float x = real(pz) float y = imag(pz) if divType == 0 ; "mod" diverged = (tmp > dValue) elseif divType == 1 ;"real" diverged = (sqr(x) > dValue) elseif divType == 2 ; "imag" diverged = (sqr(y) > dValue) elseif divType == 3 ; "or" diverged = (sqr(x) > dValue || sqr(y) > dValue) elseif divType == 4 ; "and" diverged = (sqr(x) > dValue && sqr(y) > dValue) elseif divType == 5 ;"manh" diverged = (sqr(abs(x) + abs(y)) > dValue) elseif divType == 6 ; "real+imag" diverged = (sqr(x + y) > dValue) elseif divType == 7 ; "real-imag" diverged = (sqr(x - y) > dValue) elseif divType == 8 ; "real*imag" diverged = (abs(x * y) > dValue) elseif divType == 9 ; "max(real,imag)" if abs(x) >= abs(y) diverged = (sqr(x) > dValue) else diverged = (sqr(y) > dValue) endif elseif divType == 10 ; "min(real,imag)" if abs(x) <= abs(y) diverged = (sqr(x) > dValue) else diverged = (sqr(y) > dValue) endif endif endif bool converged = false if bType == 1 || bType == 2 converged = (|pz - pzold| < cValue) endif bailed = converged || diverged return bailed endfunc complex pz complex pc ; version 1, pixel for M-type, seed for J-type ; version 2, not used (each SFormula has its own) complex pzold ; previous z for testing convergence or for use in formulas int bType ; test divergence, convergence, or both int divType ; type of divergence test ; float dCos ; cosine of angle for divergence test ; float dSin ; sine of angle for divergence test float dValue ; divergence criterion float cValue ; convergence criterion int iters ; number of iterations, often not the same as #numiter bool tooMany ; true if too many iterations bool bailed ; true if diverged or converged } class SFormula(common.ulb:Generic) { ; SFormula base class for use with JLB_SwitchCombo. ; similar to Formula, but uses State public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func SFormula(Generic pparent) Generic.Generic(pparent) endfunc ; @param pz ; @param pc func Init(complex pz, complex pc) endfunc ; @param State ; @return true if done bool func Iterate(State S) return false endfunc default: ; no title, just a base class int param v_SFormula caption = "Version (SFormula)" default = 200 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_SFormula < 200 endparam } class JLB_NothingSFormula(SFormula) { ; A do-nothing SFormula, a place-holder when needed. public: ; @param pz ; @param pc bool func Iterate(State S) return S.bailed endfunc default: title = "Do nothing" rating = recommended int param v_NothingzSFormula caption = "Version (JLB_NothingSFormula)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_NothingzSFormula < 100 endparam } class JLB_MandelbrotSFormula(SFormula) { ; The Mandelbrot formula as an SFormula. ; 220213 added the final tweak. Doesn't break old parameter sets. ; 230318 added second power. Doesn't break old parameter sets. public: import "common.ulb" func JLB_MandelbrotSFormula(Generic pparent) SFormula(pparent) if @addT > 0 && !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if @v_MandelbrotSFormula > 100 && !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz complex znew if (@p_std) if @v_MandelbrotSFormula < 201 ;|| @v_MandelbrotSFormula > 201 znew = sqr(pz) + ppc else znew = pz^@p + ppc endif else znew = @a*pz^@p_power + @b*ppc*pz^@p_power2 + @p_a_old*S.pzold if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex ppc UtilityTransform finalTransform default: title = "Mandelbrot" rating = Recommended int param v_MandelbrotSFormula caption = "Version (Mandelbrot_SFormula)" default = 220318 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_MandelbrotSFormula < 220318 endparam bool param p_std caption = "Standard" default = true endparam bool param p_Mtype caption = "M-type?" default = true visible = @v_MandelbrotSFormula > 100 endparam complex param p_seed caption = "Seed for J-type" default = (0.5,0.5) visible = @v_MandelbrotSFormula > 100 && !@p_Mtype endparam heading caption = "z_new = z^2 + c" visible = @p_std && @v_MandelbrotSFormula <= 201 endheading heading caption = "z_new = z^p + c" visible = @p_std && @v_MandelbrotSFormula > 201 endheading heading caption = "z_new = a1*z^p + b*c*z^q + a2*z_prev" visible = !@p_std endheading complex param p caption = "p" default = 2 visible = @p_std && @v_MandelbrotSFormula >= 201 ; && @v_MandelbrotSFormula < 220318 endparam complex param a caption = "Mandelbrot a1" default = 1 visible = !@p_std endparam complex param p_power caption = "Mandelbrot p" default = (2,0) visible = !@p_std endparam complex param b caption = "Mandelbrot b" default = 1 visible = !@p_std endparam complex param p_power2 caption = "Mandelbrot q" default = 0 visible = !@p_std endparam complex param p_a_old caption = "Mandelbrot a2" default = 0.0 visible = !@p_std endparam heading caption = "Tweak after each iteration" visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 && !@p_std selectable = false endparam } class JLB_MandelbarSFormula(SFormula) { ; The Mandelbar formula as a SFormula. public: import "common.ulb" func JLB_MandelbarSFormula(Generic pparent) SFormula(pparent) if @addT > 0 && !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if @v_MandelbarSFormula > 100 && !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = conj(S.pz) complex znew if (@p_std) znew = sqr(pz) + ppc else znew = @a*pz^@p_power + @b*ppc + @p_a_old*S.pzold if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex ppc UtilityTransform finalTransform default: title = "Mandelbar" rating = Recommended int param v_MandelbarSFormula caption = "Version (Mandelbar_SFormula)" default = 200 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_MandelbarSFormula < 200 endparam bool param p_std caption = "Standard" hint = "Standard: z_new = sqr(conj(z)) + c" default = true endparam bool param p_Mtype caption = "M-type?" default = true visible = @v_MandelbarSFormula > 100 endparam complex param p_seed caption = "Seed for J-type" default = (0.3, 0) visible = @v_MandelbarSFormula > 100 && !@p_Mtype endparam heading caption = "z_new = conj(z)^2 + c" visible = @p_std endheading heading caption = "z_new = a1*conj(z)^p + b*c + a2*z_prev" visible = !@p_std endheading complex param a caption = "Mandelbar a1" default = 1 visible = !@p_std endparam complex param p_power caption = "Mandelbar p" default = (2,0) visible = !@p_std endparam complex param b caption = "Mandelbar b" default = 1 visible = !@p_std endparam complex param p_a_old caption = "Mandelbar a2" default = 0.0 visible = !@p_std endparam heading caption = "Tweak after each iteration" visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 selectable = false endparam } class JLB_MandelVar2SFormula(SFormula) { ; The MandelVar2 formula as a SFormula. public: import "common.ulb" func JLB_MandelVar2SFormula(Generic pparent) SFormula(pparent) if @addT > 0 finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if @v_Mandelvar2SFormula > 100 && !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz float x = real(pz) float y = imag(pz) float x2 = sqr(x) float xy = x*y float y2 = sqr(y) complex znew = (@a_x2*x2 + @a_xy*xy + @a_y2*y2) + flip(@b_x2*x2 + @b_xy*xy + @b_y2*y2) + ppc if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif S.Update(znew, S.pc) return S.bailed endfunc protected: UtilityTransform finalTransform complex ppc default: title = "MandelVar2" ;rating = notRecommended int param v_MandelVar2SFormula caption = "Version (MandelVar2_SFormula)" default = 200 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_MandelVar2SFormula < 200 endparam heading caption = "z_new = (a_x2*x^2 + a_xy*x*y + a_y2*y^2) + i*(b_x2*x^2 + b_xy*x*y + b_y2*y^2)" endheading bool param p_Mtype caption = "M-type?" default = true visible = @v_Mandelvar2SFormula > 100 endparam complex param p_seed caption = "Seed for J-type" default = (0.3, 0) visible = @v_Mandelvar2SFormula > 100 && !@p_Mtype endparam complex param a_x2 caption = "a_x2" default = 1 ;hint = "z_new = (a_x2*x^2 + a_xy*x*y + a_y2*y^2) + i*(b_x2*x^2 + b_xy*x*y + b_y2*y^2)" endparam complex param a_xy caption = "a_xy" default = 0 ;hint = "z_new = (a_x2*x^2 + a_xy*x*y + a_y2*y^2) + i*(b_x2*x^2 + b_xy*x*y + b_y2*y^2)" endparam complex param a_y2 caption = "a_y2" default = -1 ;hint = "z_new = (a_x2*x^2 + a_xy*x*y + a_y2*y^2) + i*(b_x2*x^2 + b_xy*x*y + b_y2*y^2)" endparam complex param b_x2 caption = "b_x2" default = 0 ;hint = "z_new = (a_x2*x^2 + a_xy*x*y + a_y2*y^2) + i*(b_x2*x^2 + b_xy*x*y + b_y2*y^2)" endparam complex param b_xy caption = "b_xy" default = 2 ;hint = "z_new = (a_x2*x^2 + a_xy*x*y + a_y2*y^2) + i*(b_x2*x^2 + b_xy*x*y + b_y2*y^2)" endparam complex param b_y2 caption = "b_y2" default = 0 ;hint = "z_new = (a_x2*x^2 + a_xy*x*y + a_y2*y^2) + i*(b_x2*x^2 + b_xy*x*y + b_y2*y^2)" endparam heading caption = "Tweak after each iteration" endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 selectable = false endparam } class JLB_PeacockSFormula(SFormula) { ; The Peacock formula as a SFormula. ; July 2021 added pzold option; changed parameter names for consistency with other Sformulas. public: import "common.ulb" func JLB_PeacockSFormula(Generic pparent) SFormula(pparent) if @addT > 0 && !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if @v_PeacockSFormula > 100 && !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz complex znew if (@p_std) znew = pz + @a * (pz^@p_power - 1.0)/(pz^@p_power + 1.0) + ppc else if @v_PeacockSFormula < 210707 znew = pz + @a * (@f(pz)^@p_power - @b)/(@f(pz)^@p_power + @b) + @a2*ppc else znew = pz + @aa1 * (@f(pz)^@p_power - @aa2)/(@f(pz)^@p_power + @aa2) \ + @bb*ppc + @aa3*S.pzold endif if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex ppc UtilityTransform finalTransform default: title = "Peacock" rating = Recommended int param v_PeacockSFormula caption = "Version (Peacock_SFormula)" default = 210707 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_PeacockSFormula < 210707 endparam bool param p_std caption = "Standard" default = true endparam bool param p_Mtype caption = "M-type?" default = true visible = @v_PeacockSFormula > 100 endparam complex param p_seed caption = "Seed for J-type" default = (0.3, 0) visible = @v_PeacockSFormula > 100 && !@p_Mtype endparam heading caption = "z_new = z + a1*(z^p-1)/(z^p+1) + c" visible = @p_std endheading heading caption = "z_new = z + a1*(f(z)^p-b)/(f(z)^p+b) + a2*c" visible = !@p_std && @v_PeacockSFormula < 210707 endheading heading caption = "z_new = z + a1*(f(z)^p-a2)/(f(z)^p+a2) + b*c + a3*z_prev" visible = !@p_std && @v_PeacockSFormula >= 210707 endheading complex param p_power caption = "Peacock: p" default = 2 endparam func f caption = "Peacock: f" default = sqr() visible = !@p_std endfunc complex param a caption = "Peacock: a1" default = 1 visible = !@p_std && @v_PeacockSFormula < 210707 endparam complex param a2 caption = "Peacock: a2" default = 1 visible = !@p_std && @v_PeacockSFormula < 210707 endparam complex param b caption = "Peacock: b" default = 1 visible = !@p_std && @v_PeacockSFormula < 210707 endparam ; new versions complex param aa1 caption = "Peacock: a1" default = 1 visible = !@p_std && @v_PeacockSFormula >= 210707 endparam complex param aa2 caption = "Peacock: a2" default = 1 visible = !@p_std && @v_PeacockSFormula >= 210707 endparam complex param bb caption = "Peacock: b" default = 1 visible = !@p_std && @v_PeacockSFormula >= 210707 endparam complex param aa3 caption = "Peacock: a3" default = 1 visible = !@p_std && @v_PeacockSFormula >= 210707 endparam heading caption = "Tweak after each iteration" visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 && !@p_std selectable = false endparam } class JLB_PhoenixSFormula(SFormula) { ; The Phoenix formula as a SFormula. public: import "common.ulb" func JLB_PhoenixSFormula(Generic pparent) SFormula(pparent) if @addT > 0 && !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz complex znew if (@p_std) znew = @a1 * pz^@p1 + @a2 * ppc * pz^@p2 + @a_old * S.pzold else znew = @a1 * @f1(pz)^@p1 + @a2 * ppc * @f2(pz)^@p2 + @a_old * S.pzold if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex ppc UtilityTransform finalTransform default: title = "Phoenix" rating = Recommended int param v_PhoenixSFormula caption = "Version (Phoenix_SFormula)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_PhoenixSFormula < 100 endparam bool param p_std caption = "Standard" default = true endparam bool param p_Mtype caption = "M-type?" default = true endparam complex param p_seed caption = "Seed for J-type" default = (0.56667,0) ; to match Phoenix in Standard.ufm visible = !@p_Mtype endparam heading caption = "z_new = a1*z^p1 + a2*c*z^p2 + a3*z_old" visible = @p_std endheading heading caption = "z_new = a1*f1(z)^p1 + a2*c*f2(z)^p2 + a3*z_old" visible = !@p_std endheading func f1 caption = "f1" default = sin() visible = !@p_std endfunc complex param p1 caption = "p1" default = (2,0) endparam complex param a1 caption = "a1" default = 1 endparam func f2 caption = "f2" default = ident() visible = !@p_std endfunc complex param p2 caption = "p2" default = (0,0) endparam complex param a2 caption = "a2" default = 1 endparam complex param a_old caption = "a3" default = (0.5, 0) endparam heading caption = "Tweak after each iteration" endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 selectable = false endparam } class JLB_DualSFormula(SFormula) { ; DualSFormula combines two formulas ; first n1 calls to formula 1 ; then n2 calls to formula 2 public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_DualSFormula(Generic pparent) SFormula.SFormula(pparent) m_SFormula1 = new @f_SFormula1(this) m_SFormula2 = new @f_SFormula2(this) endfunc func Init(complex pz, complex pc) m_SFormula1.Init(pz, pc) m_SFormula2.Init(pz, pc) count = 0 n1 = @p_n1 ; both have min - 0 n2 = @p_n2 if n1 + n2 == 0, n1 = 1, endif endfunc bool func Iterate(State S) if S.bailed return true endif if @v_DualSFormula < 200 ; old version updates #numiter after all done int i i = 0 while (i < @p_n1) if m_SFormula1.Iterate(S) return true endif i = i + 1 endwhile i = 0 while (i < @p_n2) if m_SFormula2.Iterate(S) return true endif i = i + 1 endwhile else ; new version updates #numiter each time if count < n1 if m_SFormula1.Iterate(S) return true endif else if m_SFormula2.Iterate(S) return true endif endif count = count + 1 if count >= n1 + n2 ; only gets to equality count = 0 n1 = n1 + @delta_n1 n2 = n2 + @delta_n2 if n1 < 0, n1 = 0, endif if n2 < 0, n2 = 0, endif if n1 + n2 == 0, n1 = 1, endif endif endif return false endfunc protected: SFormula m_SFormula1 SFormula m_SFormula2 int count int n1 int n2 default: rating = Recommended title = "Two Formulas" int param v_DualSFormula caption = "Version (DualSFormula)" default = 200 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_DualSFormula < 200 endparam heading text = "Do n1 iterations of Formula 1, then n2 iterations of Formula2, then optionally \ change n1 and n2." endheading SFormula param f_SFormula1 caption = "Formula 1" default = JLB_Gnarl3SFormula endparam int param p_n1 caption = "Initial n1" min = 0 default = 1 endparam heading endheading SFormula param f_SFormula2 caption = "Formula 2" default = JLB_MandelbrotSFormula endparam int param p_n2 caption = "Initial n2" min = 0 default = 1 endparam heading endheading int param delta_n1 caption = "Change in n1" default = 0 endparam int param delta_n2 caption = "Change in n2" default = 0 endparam } class JLB_ComboSFormula(SFormula) { ; This does two or three SFormulas and combines them ; with one of the operators +, -, *, /, ^, (). The latter means use as argument to the previous function. ; This allows formulas such as z = (f1 + f2)/f3 + c ; or z = f1(f2(f3())) + c public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_ComboSFormula(Generic pparent) SFormula.SFormula(pparent) m_SFormula1 = new @f_SFormula1(this) m_SFormula2 = new @f_SFormula2(this) if @order3 > 0 m_SFormula3 = new @f_SFormula3(this) endif m_Combine = new Combine() endfunc func Init(complex pz, complex pc) m_SFormula1.Init(pz, pc) m_SFormula2.Init(pz, pc) if @order3 > 0 m_SFormula3.Init(pz, pc) endif endfunc ; @param pz ; @param pc ; @return new pz bool func Iterate(State S) complex pz = S.pz complex pc = S.pc ; used only to restore in case one of the subsidiary ; User Transform formulas changes it complex t1 complex t2 complex t3 if (S.bailed) return true endif if @order3 == 0 ;"f1 op f2" m_SFormula2.Iterate(S) if @op1a == "( )" ; f1(f2(pz)) m_SFormula1.Iterate(S) t1 = S.pz else ; f1(pz) op1 f2(pz) t2 = S.pz S.pz = pz, S.pc = pc m_SFormula1.Iterate(S) t1 = S.pz t1 = m_Combine.go(t1, @op1a, t2) endif elseif @order3 == "f1 op1 (f2 op2 f3)" if @op2a == "( )" ; f1(pz) op1 f2(f3(pz)) m_SFormula3.Iterate(S) t3 = S.pz m_SFormula2.Iterate(S) t2 = S.pz if @op1 == "( )" ; f1(f2(f3(pz))) S.pz = t2 m_SFormula1.Iterate(S) t1 = S.pz else ; f1(pz) op1 f2(f3(pz)) S.pz = pz, S.pc = pc m_SFormula1.Iterate(S) t1 = S.pz t1 = m_Combine.go(t1, @op1, t2) endif else m_SFormula2.Iterate(S) t2 = S.pz S.pz = pz, S.pc = pc m_SFormula3.Iterate(S) t3 = S.pz t2 = m_Combine.go(t2, @op2a, t3) if @op1 == "( )" ; f1(f2(pz) op2a f3(pz)) S.pz = pz, S.pc = pc m_SFormula1.Iterate(S) t1 = S.pz else S.pz = pz, S.pc = pc m_SFormula1.Iterate(S) t1 = S.pz t1 = m_Combine.go(t1, @op1, t2) endif endif else ;@order3 == "(f1 op1 f2) op2 f3" ; @op3a == "()" not allowed with this ordering m_SFormula2.Iterate(S) t2 = S.pz if @op1 == "( )" ; (f1(f2(pz))) op2b f3(pz) m_SFormula1.Iterate(S) t1 = S.pz else m_SFormula1.Iterate(S) t1 = S.pz t1 = m_Combine.go(t1, @op1, t2) endif S.pz = pz, S.pc = pc m_SFormula3.Iterate(S) t3 = S.pz t1 = m_Combine.go(t1, @op2b, t3) endif complex znew = @mult * t1 if @v_ComboSFormula >= 201 && @addc ;znew = znew + @cfac*S.pc complex cc = #pixel if !@p_Mtype cc = @p_seed endif znew = znew + @cfac*cc endif S.Update(znew, S.pc) return S.bailed endfunc protected: SFormula m_SFormula1 SFormula m_SFormula2 SFormula m_SFormula3 Combine m_Combine default: rating = Recommended title = "Combo" int param v_ComboSFormula caption = "Version (ComboSFormula)" default = 201 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_ComboSFormula < 201 endparam param order3 caption = "How to combine" default = 0 enum = "f1 op f2" "f1 op1 (f2 op2 f3)" "(f1 op1 f2) op2 f3" ; hint = "Order of combining can make a big difference!" endparam heading caption = "f1" endheading SFormula param f_SFormula1 caption = "Formula 1" default = JLB_FuncSFormula endparam heading endheading param op1 caption = "Operator 1" enum = "+" "-" "*" "/" "^" "( )" default = 0 hint = "How to combine Formula values." visible = @order3 > 0 endparam param op1a caption = "Operator for Combo" enum = "+" "-" "*" "/" "^" "( )" default = 5 hint = "How to combine Formula values." visible = @order3 == 0 endparam ; heading ; endheading heading caption = "f2" endheading SFormula param f_SFormula2 default = JLB_LambdaSFormula caption = "Formula 2" endparam param op2a caption = "Operator 2 for Combo" enum = "+" "-" "*" "/" "^" "( )" default = 2 hint = "How to combine Formula values." visible = (@order3 == 1) endparam param op2b caption = "Operator 2 for Combo" enum = "+" "-" "*" "/" "^" ; () doesn't make sense default = 2 hint = "How to combine Formula values." visible = (@order3 == 2) endparam heading caption = "f3" visible = @order3 > 0 endheading SFormula param f_SFormula3 default = JLB_FuncSFormula caption = "Formula 3" visible = @order3 > 0 endparam heading caption = "Final" endheading complex param mult caption = "Combo multiplier" default = (1,0) visible = @v_ComboSFormula > 100 endparam bool param addc caption = "Add c (pixel or seed)?" default = true visible = @v_ComboSFormula >= 201 endparam bool param p_Mtype caption = "M-type? (add pixel)" default = true visible = @v_ComboSFormula >= 201 && @addc hint = "This is used only for the final 'Add c'" endparam complex param p_seed caption = "Seed" default = (0.3, 0) visible = @v_ComboSFormula >= 201 && @addc && !@p_Mtype hint = "This is used only for the final 'Add c'" endparam complex param cfac caption= "Multiplier of c" default = 1 visible = false ;@addc && @v_ComboSFormula >= 201 endparam heading caption = "End of Combo" endheading } class JLB_FuncSFormula(SFormula) { ; Early version, z = a1*f(a2*z) + b*c. ; Later version, z = a1*f(a2*z)^p + b*c. ; July 2021 added a3*z_prev, as in Mandelbrot public: import "common.ulb" func JLB_FuncSFormula(Generic pparent) SFormula(pparent) if @addT > 0 && !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if @v_FuncSFormula > 100 && !@p_Mtype ppc = @p_seed endif endfunc bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz complex znew if @p_std znew = @f(pz) + ppc else znew = @a1*@f(@a2*pz)^@p + @b*ppc + @a3*S.pzold if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex ppc UtilityTransform finalTransform default: rating = notRecommended title = "Function" int param v_FuncSFormula caption = "Version (Func_SFormula)" default = 220626 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_FuncSFormula < 220626 endparam bool param p_std caption = "Standard?" default = true ;visible = @v_FuncSFormula >= 220626 endparam bool param p_Mtype caption = "M-type?" default = true visible = @v_FuncSFormula > 100 endparam complex param p_seed caption = "Seed for J-type" default = (0.3, 0) visible = @v_funcSFormula > 100 && !@p_Mtype endparam heading caption = "z_new = f(z) + c" visible = @p_std && @v_FuncSFormula >= 220626 endheading heading caption = "z_new = a1*f(a2*z)^p + b*c + a3*z_prev" visible = @v_FuncSFormula >= 201 && !@p_std endheading heading caption = "z_new = a1*f(a2*z) + b*c" visible = @v_FuncSFormula < 201 endheading func f caption = "Function: f" default = cos() endfunc complex param a1 caption = "Function: a1" default = 1 visible = !@p_std endparam complex param a2 caption = "Function: a2" default = 1 visible = !@p_std endparam complex param p caption = "Function: p" default = 1 visible = @v_FuncSFormula >= 201 && !@p_std endparam complex param b caption = "Function b" default = 1 visible = !@p_std endparam complex param a3 caption = "Function: a3" default = 0 visible = @v_FuncSFormula >= 201 && !@p_std endparam heading caption = "Tweak after each iteration" visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 && !@p_std selectable = false endparam } class JLB_PowerSFormula(SFormula) { ; 05 June 2021 added UserTransform public: import "common.ulb" func JLB_PowerSFormula(Generic pparent) SFormula.SFormula(pparent) m_Combine = new Combine() m_Transform = new @f_T(this) m_SFormula = new @f_SFormula(this) if @addT > 0 ;&& !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) m_SFormula.Init(pz, pc) ppc = #pixel if @v_PowerSFormula > 100 && !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @return the new pz bool func Iterate(State S) bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex zz if @v_PowerSFormula < 210627 zz = S.pz - @a2 else zz = S.pz - @a endif if @p_base == 1 zz = @f(zz) elseif @p_base == 2 zz = m_Transform.Iterate(zz) endif complex t1 if @v_PowerSFormula < 210627 t1 = @a0 + @a1 * zz^@p_power else t1 = zz^@p_power endif complex t2 = @b * ppc complex znew = m_Combine.go(t1, @op, t2) ;if !@p_std if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif ;endif S.Update(znew, S.pc) return S.bailed endfunc protected: Combine m_Combine UserTransform m_Transform SFormula m_SFormula complex ppc UtilityTransform finalTransform default: rating = Recommended title = "Power" int param v_PowerSFormula caption = "Version (Power_SFormula)" default = 210627 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_PowerSFormula < 210627 endparam ; bool param p_std ; caption = "Standard?" ; default = true ; endparam bool param p_Mtype caption = "M-type?" default = true visible = @v_PowerSFormula > 100 endparam complex param p_seed caption = "Seed for J-type" default = (0.3, 0) visible = @v_PowerSFormula > 100 && !@p_Mtype endparam heading caption = "z_new = (a0 + a1*(z-a2)^p) op b*c" visible = @p_base == 0 && @v_PowerSFormula < 210627 endheading heading caption = "z_new = (a0 + a1*f(z-a2)^p) op b*c" visible = @p_base > 0 && @v_PowerSFormula < 210627 endheading heading caption = "z_new = (z-a)^p op b*c" visible = @p_base == 0 && @v_PowerSFormula >= 210627 endheading heading caption = "z_new = f(z-a)^p op b*c" visible = @p_base > 0 && @v_PowerSFormula >= 210627 endheading param p_base caption = "Power of" enum = "z" "Built-in function of z" "Transform of z" "SFormula" default = 0 endparam func f caption = "f Built-in function" default = sin() ;hint = "Function" visible = @p_base == 1 endfunc UserTransform param f_T caption = "f Transform" default = JLB_MoebiusUserTransform visible = @p_base == 2 endparam SFormula param f_SFormula caption = "f SFormula" default = JLB_FuncSFormula visible = @p_base == 3 endparam complex param a0 caption = "Power: a0" default = 0 visible = @v_PowerSFormula < 210627 endparam complex param a1 caption = "Power: a1" default = 1 visible = @v_PowerSFormula < 210627 endparam complex param a2 caption = "Power: a2" default = 0 visible = @v_PowerSFormula < 210627 endparam complex param a caption = "Power: a" default = 0 visible = @v_PowerSFormula >= 210627 endparam complex param p_power caption = "Power: p" default = 2 endparam param op caption = "Power: op" enum = "+" "-" "*" "/" "^" default = 0 endparam complex param b caption = "Power: b" default = 1 endparam heading caption = "Tweak after each iteration" ;visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 ;visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 ; && !@p_std selectable = false endparam } class JLB_PolynomialSFormula(SFormula) { public: import "common.ulb" func JLB_PolynomialSFormula(Generic pparent) SFormula(pparent) if @p_base == 2 m_Transform = new @zT(this) endif if @addT > 0 ;&& !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if @v_PolynomialSFormula > 100 && !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @return the new pz bool func Iterate(State S) bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz if @p_base == 1 pz = @f(pz) elseif@p_base == 2 pz = m_Transform.Iterate(pz) endif complex zz = pz complex z_calc = @a0 if @p_order >= 1 z_calc = z_calc + @a1 * zz if @p_order >= 2 zz = zz * pz z_calc = z_calc + @a2 * zz if @p_order >= 3 zz = zz * pz z_calc = z_calc + @a3 * zz if @p_order >= 4 zz = zz * pz z_calc = z_calc + @a4 * zz if @p_order >= 5 zz = zz * pz z_calc = z_calc + @a5 * zz if @p_order >= 6 zz = zz * pz z_calc = z_calc + @a6 * zz endif endif endif endif endif endif ; return @p_a_calc*z_calc + @p_a_old*pz + ppc complex znew = z_calc + @b*ppc + @d * S.pzold ;if !@p_std if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif ;endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex ppc UserTransform m_Transform UtilityTransform finalTransform default: rating = Recommended title = "Polynomial" int param v_PolynomialSFormula caption = "Version (Polynomial_SFormula)" default = 200 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_PolynomialSFormula < 200 endparam ; bool param p_std ; caption = "Standard?" ; default = true ; endparam bool param p_Mtype caption = "M-type?" default = true visible = @v_PolynomialSFormula > 100 endparam complex param p_seed caption = "Seed for J-type" default = (0.3, 0) visible = @v_PolynomialSFormula > 100 && !@p_Mtype endparam heading caption = "z_calc = a0 + a1*z + a2*z^2 + ... + b*c + d*z_prev" visible = @p_base == 0 endheading heading caption = "z_calc = a0 + a1*f(z) + a2*f(z)^2 + ... + b*c + d*z_prev" visible = @p_base == 1 endheading heading caption = "z_calc = a0 + a1*T(z) + a2*T(z)^2 + ... + b*c + d*z_prev" visible = @p_base == 2 endheading param p_base caption = "Polynomial in" enum = "z" "F(z)" "T(z)" default = 0 endparam func f caption = "F (built-in function)" default = sin() hint = "Function" visible = @p_base == 1 endfunc UserTransform param zT caption = "T (Transform)" default = JLB_MoebiusUserTransform visible = @p_base == 2 endparam int param p_order caption = "Max power" default = 2 min = 0 max = 6 endparam complex param a0 caption = "a0" default = 0 endparam complex param a1 caption = "a1" default = 1 visible = @p_order >= 1 endparam complex param a2 caption = "a2" default = 1 visible = @p_order >= 2 endparam complex param a3 caption = "a3" default = 1 visible = @p_order >= 3 endparam complex param a4 caption = "a4" default = 1 visible = @p_order >= 4 endparam complex param a5 caption = "a5" default = 1 visible = @p_order >= 5 endparam complex param a6 caption = "a6" default = 1 visible = @p_order >= 6 endparam complex param b caption = "b" default = 1 endparam complex param d caption = "d" default = 0 endparam heading caption = "Tweak after each iteration" ;visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 ;visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 ;&& !@p_std selectable = false endparam } class JLB_LambdaSFormula(SFormula) { ;The Lambda formula as a SFormula, generalized. public: import "common.ulb" func JLB_LambdaSFormula(Generic pparent) SFormula(pparent) if @addT > 0 && !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if @v_LambdaSFormula > 100 && !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz if !@p_std && (@p_base == "func(z)") pz = @f(pz) endif complex znew if @v_LambdaSFormula < 200 if (pz == (0,0)) || (pz == (1,0)) ; avoid unproductive starts pz = #pixel endif endif if (@p_std) znew = ppc * pz * (1 - pz) else complex z_calc = ppc * (pz^@p_power1) * ((@b-pz)^@p_power) znew = @p_a_calc * z_calc + @p_a_old * pz if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex ppc UtilityTransform finalTransform default: rating = Recommended title = "Lambda" int param v_LambdaSFormula caption = "Version (Lambda_SFormula)" default = 201 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_LambdaSFormula < 201 endparam bool param p_std caption = "Standard?" default = true endparam bool param p_Mtype caption = "M-type?" default = true visible = @v_LambdaSFormula > 100 endparam complex param p_seed caption = "Seed for J-type" default = (1.25, 0) visible = @v_LambdaSFormula > 100 && !@p_Mtype endparam heading caption = "z_new = c*z*(1-z)" visible = @p_std endheading heading caption = "z_new = a1*c* z^p1 * (1-z)^(p2-1) + a2*z" visible = !@p_std && (@v_LambdaSFormula == 200) endheading heading caption = "z_new = a1* (c * z^p1 * (b-z)^p2) + a2*z" visible = !@p_std && (@p_base == 0) && (@v_LambdaSFormula > 200) endheading heading caption = "z_new = a1* (c * f(z)^p1 * (b-f(z))^p2) + a2*z" visible = !@p_std && (@p_base == 1) && (@v_LambdaSFormula > 200) endheading param p_base caption = "Power of" enum = "z" "func(z)" default = 0 visible = !@p_std endparam func f caption = "Lambda f" default = sin() visible = !@p_std && @p_base == 1 endfunc complex param p_a_calc caption = "Lambda a1" default = 1.0 visible = !@p_std endparam complex param p_power1 caption = "Lambda p1" default = 1 visible = !@p_std endparam complex param b caption = "Lambda b" default = 1 visible = !@p_std endparam complex param p_power caption = "Lambda p2" default = 1 visible = !@p_std endparam complex param p_a_old caption = "Lambda a2" default = 0.0 visible = !@p_std endparam heading caption = "Tweak after each iteration" visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 && !@p_std selectable = false endparam } ; Calculate function and its first two derivatives. ; Derivatives courtesy of https://www.derivative-calculator.net/ class Deriv { public: ; constructor initializes the state func Deriv(int k) kind = k ; don't break old version endfunc func Set_k(int k) kind = k endfunc func pow_calc(complex z, complex n) complex c = 0 complex s = 0 complex q = 0 complex t = 0 if kind == 0 ; z f = z^n fp = n*z^(n-1) fpp = n*(n-1)*z^(n-2) elseif kind == 1 ; sin(z) s = sin(z) c = cos(z) f = s^n fp = n*c*s^(n-1) fpp = n*(n-1)*c*c*s^(n-2) - n*f elseif kind == 2 ; sinh(z) s = sinh(z) c = cosh(z) f = s^n fp = n*c*s^(n-1) fpp = n*(n-1)*c*c*s^(n-2) + n*f elseif kind == 3 ; asin(z) s = asin(z) q = sqrt(1-z*z) f = s^n fp = n*s^(n-1)/q fpp = n*(n-1)*s^(n-2)/q^2 + n*z*s^(n-1)/q^3 elseif kind == 4 ; asinh(z) s = asinh(z) q = sqrt(1+z*z) f = s^n fp = n*s^(n-1)/q fpp = n*(n-1)*s^(n-2)/q^2 - n*z*s^(n-1)/q^3 elseif kind == 5 ; cos(z) s = sin(z) c = cos(z) f = c^n fp = -n*s*c^(n-1) fpp = n*(n-1)*s*s*c^(n-2) - n*f elseif kind == 6 ; cosh(z) s = sinh(z) c = cosh(z) f = c^n fp = n*s*c^(n-1) fpp = n*(n-1)*s*s*c^(n-2) + n*f elseif kind == 7 ; acos(z) c = acos(z) q = sqrt(1-z*z) f = c^n fp = -n*c^(n-1)/q fpp = n*(n-1)*c^(n-2)/q^2 - n*z*c^(n-1)/q^3 elseif kind == 8 ; acosh(z) c = acosh(z) q = sqrt(z*z-1) f = c^n fp = n*c^(n-1)/q fpp = n*(n-1)*c^(n-2)/q^2 - n*z*c^(n-1)/q^3 elseif kind == 9 ; tan(z) t = tan(z) s = 1/cos(z) ;sec(z) f = t^n fp = n*s*s*t^(n-1) fpp = n*(n-1)*s*s*s*s*t^(n-2) + 2*n*s*s*f elseif kind == 10 ; tanh(z) t = tanh(z) s = 1/cosh(z) ;sech(z) f = t^n fp = n*s*s*t^(n-1) fpp = n*(n-1)*s*s*s*s*t^(n-2) - 2*n*s*s*f elseif kind == 11 ; atan(z) t = atan(z) q = 1 + z*z f = t^n fp = n*t^(n-1)/q fpp = n*(n-1)*t^(n-2)/q^2 - 2*n*z*t^(n-1)/q^2 elseif kind == 12 ; atanh(z) t = atanh(z) q = 1 - z*z f = t^n fp = n*t^(n-1)/q fpp = n*(n-1)*t^(n-2)/q^2 + 2*n*z*t^(n-1)/q^2 elseif kind == 13 ;cotan(z) c = cotan(z) s = 1/sin(z) ;csq(z) f = c^n fp = -n*s*s*c^(n-1) fpp = n*(n-1)*s*s*s*s*c^(n-2) + 2*n*s*s*f elseif kind == 14 ;cotanh(z) c = cotanh(z) s = 1/sinh(z) ;csch (z) f = c^n fp = -n*s*s*c^(n-1) fpp = n*(n-1)*s*s*s*s*c^(n-2) + 2*n*s*s*f elseif kind == 15 ; log(z) q = log(z) f = q^n fp = n*q^(n-1)/z fpp = ( n*(n-1)*q^(n-2) - n*q^(n-1) ) / z^2 elseif kind == 16 ; exp(z) f = exp(n*z) fp = n*f fpp = n*n*f endif endfunc int kind complex f complex fp complex fpp } class JLB_PolyNewtonSFormula(SFormula) { ; Iteration of polynomial(z) - c = 0 by relaxed Newton's method or other ; iterative method. public: import "common.ulb" func JLB_PolyNewtonSFormula(Generic pparent) SFormula(pparent) if @addT > 0 ;&& !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) if (@p_poly_type == "Polynomial") m_a[0] = @p_a0, m_a[1] = @p_a1, m_a[2] = @p_a2, m_a[3] = @p_a3 m_a[4] = @p_a4, m_a[5] = @p_a5, m_a[6] = @p_a6, m_a[7] = @p_a7 m_a[8] = @p_a8, m_a[9] = @p_a9, m_a[10] = @p_a10 m_a[@p_degree+1] = 0 int i = 0 while (i < @p_degree) m_ap[i] = (i+1)*m_a[i+1] ; f' - coefficient of z^i m_app[i] = (i+2)*(i+1)*m_a[i+2] ; f" - coefficient of z^i i = i + 1 endwhile endif D = new Deriv(@p_kind) if @p_poly_type == 0 n = @p_degree else n = @p_power endif if @p_iter_type == "Type 3" n = 2 endif ppc = #pixel if @v_PolyNewtonSFormula > 100 && !@p_Mtype ppc = - @p_seed ; to be like that in Standard.ufm endif endfunc ; @param pz ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz complex znew complex f complex fp complex fpp = 0 ; avoid compiler warning if @v_PolyNewtonSFormula < 200 if pz == (0,0) ; avoid unproductive starts pz = #pixel endif endif int i if (@p_poly_type == "Polynomial") ; Solving F(z) = 0 D.Set_k(@p_kind) D.pow_calc(pz, 1) complex fz = D.f complex fzp = D.fp complex fzpp = D.fpp i = @p_degree f = m_a[i] while (i > 0) i = i - 1 f = m_a[i] + fz * f endwhile f = f + @b*ppc ; df/dz i = @p_degree - 1 complex sum1 = m_ap[i] while (i > 0) i = i - 1 sum1 = m_ap[i] + fz * sum1 endwhile fp = fzp * sum1 if (@p_iter_type > 0) ; d2f/dz2 i = @p_degree - 2 complex sum2 = m_app[i] while (i > 0) i = i - 1 sum2 = m_app[i] + fz * sum2 endwhile fpp = fzpp * sum1 + fzp * fzp * sum2 endif else;if (@p_poly_type == "Power") D.Set_k(@p_kind) D.pow_calc(pz, @p_power) f = D.f fp = D.fp fpp = D.fpp D.Set_k(@p_kind2) D.pow_calc(pz, @p_power2) f = f + @a*D.f + @b*ppc fp = fp + @a*D.fp fpp = fpp + @a*D.fpp endif if (@p_iter_type == "Newton") znew = pz - @step * f / fp elseif (@p_iter_type == "Type 1") znew = pz - @step * (f/fp)*(1 + @p_alpha*f*fpp/(2*fp*fp)) elseif (@p_iter_type == "Type 2") znew = pz - @step * 2*(f/fp) / ( 2 - @p_alpha*f*fpp/(fp*fp)) else;if (@p_iter_type == "Type 3") ; see https://mathworld.wolfram.com/LaguerresMethod.html ; G = fp/f, H = G^2-fpp/f ; znew = z - n/max{G+-sqrt[(n-1)(nH-G^2)]} ; Halley sets n=2 ; Laguerre sets n=polynomial degree, alpha = 1 ; complex root = sqrt(1-@p_alpha*2*f*fpp/(fp*fp)) complex root = sqrt((n-1)*(n-1-@p_alpha*n*f*fpp/(fp*fp))) complex d1 = 1 + root complex d2 = 1 - root if (|d1| < |d2|), d1 = d2, endif znew = pz - @step * 2*(f/fp) / d1 endif ;if !@p_std if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif ;endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex m_a[12] ; coefficients of polynomial complex m_ap[12] ; coefficients of first derivative of polynomial complex m_app[12] ; coefficients of second derivative of polynomial Deriv D complex n ; real for polynomial type; complex for powers of functions complex ppc UtilityTransform finalTransform default: title = "Poly Newton" rating = Recommended int param v_PolyNewtonSFormula caption = "Version (PolyNewton_SFormula)" default = 200 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_PolyNewtonSFormula < 200 endparam ; bool param p_std ; caption = "Standard?" ; default = true ; endparam bool param p_Mtype caption = "M-type (c = pixel)?" default = true visible = @v_PolyNewtonSFormula > 100 endparam complex param p_seed caption = "J-type, c =" default = (1, 0) visible = @v_PolyNewtonSFormula > 100 && !@p_Mtype endparam heading caption = "Solve a0 + a1*f(z) + a2*f(z)^2 + ... - b*c = 0" visible = @p_poly_type == 0 endheading heading caption = "Solve f(z)^p + a*g(z)^q - b*c = 0" visible = @p_poly_type == 1 endheading param p_poly_type caption = "Function type" enum = "Polynomial" "Powers" default = 0 endparam ; first function param p_kind caption = "f" enum = "z" "sin(z)" "sinh(z)" "asin(z)" "asinh(z)" \ "cos(z)" "cosh(z)" "acos(z)" "acosh(z)" \ "tan(z)" "tanh(z)" "atan(z)" "atanh(z)" \ "log(z)" "exp(z)" default = 1 visible = @v_PolyNewtonSFormula > 100 ; && @p_poly_type == 1 endparam int param p_degree caption = "Max power (<=10)" default = 3 min = 2 max = 10 visible = (@p_poly_type == 0) endparam heading caption = "Coeffients" visible = (@p_poly_type == 0) endheading complex param p_a0 caption = "a0 = constant coef" default = (1,0) visible = (@p_poly_type == 0) endparam complex param p_a1 caption = "a1 = degree 1 coef" default = (1,0) visible = (@p_poly_type == 0) endparam complex param p_a2 caption = "a2 = degree 2 coef" default = (1,0) visible = (@p_poly_type == 0) endparam complex param p_a3 caption = "a3 = degree 3 coef" default = (1,0) visible = (@p_degree >= 3) && (@p_poly_type == 0) endparam complex param p_a4 caption = "a4 = degree 4 coef" default = (1,0) visible = (@p_degree >= 4) && (@p_poly_type == 0) endparam complex param p_a5 caption = "a5 = degree 5 coef" default = (1,0) visible = (@p_degree >= 5) && (@p_poly_type == 0) endparam complex param p_a6 caption = "a6 = degree 6 coef" default = (1,0) visible = (@p_degree >= 6) && (@p_poly_type == 0) endparam complex param p_a7 caption = "a7 = degree 7 coef" default = (1,0) visible = (@p_degree >= 7) && (@p_poly_type == 0) endparam complex param p_a8 caption = "a8 = degree 8 coef" default = (1,0) visible = (@p_degree >= 8) && (@p_poly_type == 0) endparam complex param p_a9 caption = "a9 = degree 9 coef" default = (1,0) visible = (@p_degree >= 9) && (@p_poly_type == 0) endparam complex param p_a10 caption = "a10 = degree 10 coeff" default = (1,0) visible = (@p_degree >= 10) && (@p_poly_type == 0) endparam complex param p_power caption = "p" default = (3,0) visible = (@p_poly_type == 1) endparam ; second function complex param a caption = "a" default = 1 visible = (@p_poly_type == 1) endparam param p_kind2 caption = "g" enum = "z" "sin(z)" "sinh(z)" "asin(z)" "asinh(z)" \ "cos(z)" "cosh(z)" "acos(z)" "acosh(z)" \ "tan(z)" "tanh(z)" "atan(z)" "atanh(z)" \ "log(z)" "exp(z)" default = 0 visible = @v_PolyNewtonSFormula > 100 && @p_poly_type == 1 endparam complex param p_power2 caption = "q" default = (2,0) visible = (@p_poly_type == 1) endparam complex param b caption = "b" default = 1 endparam heading endheading param p_iter_type caption = "Iteration type" enum = "Newton" "Type 1" "Type 2" "Type 3" ;"Type 4" default = 0 hint = "Newton is the usual, using only the first derivative. \ The other types also use the second derivative. \ Type 1 is Householder iteration; type 2 is Halley's iteration; \ type 3 is Halley's irrational iteration; \ type 4 is Laguerre's method for polynomials (all if alpha = 1)." endparam complex param p_alpha caption = "alpha" default = (1,0) ; hint = "Nonstandard values may not allow convergence, but can give \ ; interesting shapes." visible = (@p_iter_type > 0) endparam param step caption = "Step size" default = (1,0) ; hint = "Nonstandard values may not allow \ ; convergence, but can give interesting shapes." endparam heading caption = "Tweak after each iteration" ;visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 ;visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 ;&& !@p_std selectable = false endparam } class JLB_UXSFormula(SFormula) { ; an SFormula that calls a UserTransform public: import "common.ulb" func JLB_UXSFormula(Generic pparent) SFormula.SFormula(pparent) m_UX = new @f_UX(this) if @addT > 0 finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex znew = m_UX.Iterate(S.pz) if @v_UXSFormula >= 221227 znew = znew + ppc endif if @addT == "c" ; "c" added 230429 ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif S.Update(znew, S.pc) return S.bailed endfunc protected: UserTransform m_UX UtilityTransform finalTransform complex ppc default: ;rating = Recommended title = "UserTransform" int param v_UXSFormula caption = "Version (UX_SFormula)" default = 221227 ; hint = "This version parameter is used to detect when a change has \ ; been made to the formula that is incompatible with the \ ; previous version. When that happens, this field will reflect \ ; the old version number to alert you to the fact that an \ ; alternate rendering is being used." visible = @v_UXSFormula < 221227 endparam heading text = "(Current is 221227.)" visible = @v_UXSFormula < 221227 endheading bool param p_Mtype caption = "M-type?" default = true visible = @v_UXSFormula >= 221227 endparam complex param p_seed caption = "Seed for J-type" default = (0.3, 0) visible = @v_UXSFormula >= 221227 && !@p_Mtype endparam heading caption = "znew = T(z) + c" endheading UserTransform param f_UX caption = "Transform T" default = JLB_BasicUserTransform endparam heading caption = "Tweak after each iteration" endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 selectable = false endparam } class JLB_PFSFormula(SFormula) { ; an SFormula that calls a DivergentFormula public: import "common.ulb" import "Standard.ulb" func JLB_PFSFormula(Generic pparent) SFormula.SFormula(pparent) m_PF = new @f_PF(this) endfunc func Init(complex pz, complex pc) m_PF.Init(#pixel) endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif S.pz = m_PF.Iterate(S.pz) S.Update(S.pz, S.pc) return S.bailed endfunc protected: Formula m_PF default: rating = NotRecommended title = "Plug-In Formula" int param v_PFSFormula caption = "Version (PFSFormula)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_PFSFormula < 100 endparam Formula param f_PF caption = "Plug-In Formula" default = Standard_Mandelbrot endparam } class JLB_GnarlSFormula(SFormula) { ; Based on the Gnarl formula from mt.ufm, as a SFormula. ; Calculate dx and dy based on the real or imaginary part of z, then combine. ; Add a multiple of z, #pixell, or a seed. public: import "common.ulb" func JLB_GnarlSFormula(Generic pparent) SFormula.SFormula(pparent) m_rot = cos(@theta*#pi/180) + flip(sin(@theta*#pi/180)) if @type == 1 m_SFormula1 = new @f_SFormula1(this) m_SFormula2 = new @f_SFormula2(this) m_SFormula3 = new @f_SFormula3(this) endif if @addT > 0 finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if (@ctype == "seed") ppc = @p_seed endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if s.bailed return true endif if s.tooMany return false endif complex pz = S.pz complex znew float xc float yc if (@ctype == "z") xc = real(pz) yc = imag(pz) elseif (@ctype == "seed") xc = real(ppc) yc = imag(ppc) else;if (@ctype == "pixel") xc = real(#pixel) yc = imag(#pixel) endif complex zrot = pz * m_rot float x = real(zrot) float y = imag(zrot) complex dx complex dy if @type == "function" float px, float py, float qx, float qy if @args == "x,y" px = x, qx = xc, py = y, qy = yc else ; "y,x" the original version px = y, qx = yc, py = x, qy = xc endif if (@mode == 0) ; f(f(f())) dx = @a1*@f1(@b1*px + @a2*@f2(@b2*px + @a3*@f3(@b3*px))) + @a4*qx dy = @a1*@f1(@b1*py + @a2*@f2(@b2*py + @a3*@f3(@b3*py))) + @a4*qy else ; f()+f()+f() dx = @a1*@f1(@b1*px) + @a2*@f2(@b2*px) + @a3*@f3(@b3*px) + @a4*qx dy = @a1*@f1(@b1*py) + @a2*@f2(@b2*py) + @a3*@f3(@b3*py) + @a4*qy endif if @v_GnarlSFormula <= 100 dx = real(dx) dy = real(dy) endif else ; @type = "formula" complex zz float q if @mode == 0 ; f(f(f())) if @v_GnarlSFormula <= 100 zz = flip(zrot), q = yc else if @args == "x,y" zz = x, q = xc else ; "y,x" the original version zz = y, q = yc endif endif S.pz = zz m_SFormula3.Iterate(S) S.pz = zz + @a3 * S.pz m_SFormula2.Iterate(S) S.pz = zz + @a2 * S.pz m_SFormula1.Iterate(S) dx = @a1*S.pz + @a4*q if @v_GnarlSFormula <= 100 dx = real(dx) ; real(a1*f1(zz+a2*f2(zz+a3*f3(zz)))+a4*yc) zz = zrot, q = xc else if @args == "x,y" zz = y, q = yc else ; "y,x" the original version zz = x, q = xc endif endif S.pz = zz m_SFormula3.Iterate(S) S.pz = zz + @a3 * S.pz m_SFormula2.Iterate(S) S.pz = zz + @a2 * S.pz m_SFormula1.Iterate(S) dy = @a1*S.pz + @a4*q if @v_GnarlSFormula <= 100 dy = real(dy) ; real(a1*f1(zz+a2*f2(zz+a3*f3(zz)))+a4*xc) endif else ; f()+f()+f() complex t1 complex t2 complex t3 if @v_GnarlSFormula <= 100 zz = flip(zrot), q = yc else if @args == "x,y" zz = x, q = xc else ; "y,x" the original version zz = y, q = yc endif endif S.pz = zz m_SFormula3.Iterate(S) t3 = S.pz S.pz = zz m_SFormula2.Iterate(S) t2 = S.pz S.pz = zz m_SFormula1.Iterate(S) t1 = S.pz dx = @a1*t1 + @a2*t2 + @a3*t3 + @a4*q if @v_GnarlSFormula <= 100 dx = real(dx) q = xc else if @args == "x,y" zz = y, q = yc else ; "y,x" the original version zz = x, q = xc endif endif S.pz = zz m_SFormula3.Iterate(S) t3 = S.pz S.pz = zz m_SFormula2.Iterate(S) t2 = S.pz S.pz = zz m_SFormula1.Iterate(S) t1 = S.pz dy =@a1*t1 + @a2*t2 + @a3*t3 + @a4*q if @v_GnarlSFormula <= 100 dy = real(dy) endif endif endif if (@xop=="-") dx = -dx endif if (@yop=="-") dy = -dy endif znew = pz + @step * (dx + flip(dy)) ;if !@p_std if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif ;endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex m_rot SFormula m_SFormula1 SFormula m_SFormula2 SFormula m_SFormula3 complex ppc UtilityTransform finalTransform default: rating = notRecommended title = "Gnarl" int param v_GnarlSFormula caption = "Version (Gnarl_SFormula)" default = 200 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_GnarlSFormula < 200 endparam ; bool param p_Mtype ; caption = "M-type?" ; default = true ; visible = @v_GnarlSFormula > 100 ; endparam heading caption = "Gnarl3 is Recommended instead of Gnarl" endheading param type caption = "type" enum = "function" "formula" default = 0 hint = "function = built-in functions, like sin; formula = any SFormula plug-in" endparam param mode caption = "mode" enum = "f(f(f()))" "f()+f()+f()" default = 1 endparam float param theta caption = "Theta" default = 0 hint = "First rotate z by this much (360 = full circle)" endparam param args caption = "args" enum = "x,y" "y,x" default = 1 endparam heading endheading complex param a1 caption = "Gnarl a1" default = 1 hint = "multiplies f1" endparam complex param b1 caption = "Gnarl b1" default = 1 hint = "multiplies f1's arg" visible = @type == 0 endparam func f1 caption = "Gnarl f1" default = cos() hint = "Gnarl left function" visible = @type == 0 endfunc SFormula param f_SFormula1 caption = "Gnarl formula 1" default = JLB_FuncSFormula visible = @type == 1 endparam heading endheading complex param a2 caption = "Gnarl a2" default = 2.7 hint = "multiplies f2" endparam complex param b2 caption = "Gnarl b2" default = 2.7 hint = "multiplies f2's arg" ; visible=( (@a1*@a2) != 0) visible = @type == 0 endparam func f2 caption = "Gnarl f2" default = sin() hint = "second function" visible = @type == 0 endfunc SFormula param f_SFormula2 caption = "Gnarl formula 2" default = JLB_FuncSFormula visible = @type == 1 endparam heading endheading complex param a3 caption = "Gnarl a3" default = 0 hint = "multiplies f3" endparam complex param b3 caption = "Gnarl b3" default = 1 hint = "multiplies f3's arg" visible = @type == 0 endparam func f3 caption = "Gnarl f3" default = sqr() hint = "inmost or right function" visible = @type == 0 ; visible=( (@a1*@a2*@a3) != 0) endfunc SFormula param f_SFormula3 caption = "Gnarl formula 3" default = JLB_FuncSFormula visible = @type == 1 endparam heading endheading complex param a4 caption = "Gnarl a4" default = 1.0 hint = "multiplies final addition" endparam param ctype enum = "z" "seed" "pixel" caption = "final addition" ;hint = "makes a difference sometimes" default = 0 endparam complex param p_seed caption = "Seed" default = (0.3, 0) visible = @v_GnarlSFormula > 100 && @ctype==1 endparam heading endheading param step caption = "Step_size" default = 0.1 hint = "How much of this to use. z_new = z + Step_size * z_calc" endparam param xop enum = "+" "-" default = 1 hint = "multiply real part of z_calc" endparam param yop enum = "+" "-" default = 0 hint = "multiply imaginary part of z_calc" endparam heading caption = "Tweak after each iteration" endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 selectable = false endparam } class JLB_Gnarl2SFormula(SFormula) { ; Based on the Gnarl formula from mt.ufm, as a SFormula. ; Calculate dx and dy based on the real or imaginary part of z, then combine. ; Add a multiple of z, #pixell, or a seed. public: import "common.ulb" func JLB_Gnarl2SFormula(Generic pparent) SFormula.SFormula(pparent) m_rot = cos(@theta*#pi/180) + flip(sin(@theta*#pi/180)) if @type == 1 m_Sf1 = new @f_SFormula1(this) m_Sf2 = new @f_SFormula2(this) m_Sf3 = new @f_SFormula3(this) endif m_Combine = new Combine() endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if s.bailed return true endif if s.tooMany return false endif complex pz = S.pz ; complex pc = @p_seed complex znew float xc float yc if @v_Gnarl2SFormula < 220 if (@ctype == "z") xc = real(pz) yc = imag(pz) elseif (@ctype == "seed") xc = real(@p_seed) yc = imag(@p_seed) else;if (@ctype == "pixel") xc = real(#pixel) yc = imag(#pixel) endif else if (@ctype1 == "z") xc = real(pz) yc = imag(pz) elseif (@ctype1 == "seed (J-type)") xc = real(@p_seed) yc = imag(@p_seed) else;if (@ctype1 == "pixel(M-type)") xc = real(#pixel) yc = imag(#pixel) endif endif complex zrot = pz * m_rot float x = real(zrot) float y = imag(zrot) complex dx = 0 ; satisfy compiler complex dy = 0 ; ditto complex tx1, complex tx2, complex tx3 complex ty1, complex ty2; complex ty3 complex px, complex py, complex qx, complex qy if @args == "x,y" px = x, qx = xc, py = y, qy = yc else ; "y,x" the original version, usually better px = y, qx = yc, py = x, qy = xc endif if @mode == 0 ;"f1 op1 f2" ;tx2 = @a2*@f2(@b2*px) ;ty2 = @a2*@f2(@b2*py) tx2 = iter(2, px, qx, S) ty2 = iter(2, py, qy, S) if @op1a == "( )" ; f1(f2(.)) ; dx = @a1*@f1(@b1*tx2) ; dy = @a1*@f1(@b1*ty2) dx = iter(1, @a0*px + tx2, qx, S) dy = iter(1, @a0*py + ty2, qy, S) else ; f1(.) op1 f2(.) ; tx1 = @a1*@f1(@b1*px) ; ty1 = @a1*@f1(@b1*py) tx1 = iter(1, px, qx, S) ty1 = iter(1, py, qy, S) dx = m_Combine.go(tx1, @op1a, tx2) dy = m_Combine.go(ty1, @op1a, ty2) endif elseif @mode == 1 ;"f1 op1 (f2 op2 f3)" if @op2a == "( )" ; f1 op f2(f3(.)) ; tx3 = @a3*@f3(@b3*px) ; ty3 = @a3*@f3(@b3*py) tx3 = iter(3, px, qx, S) ty3 = iter(3, py, qy, S) ; tx2 = @a2*@f2(@b2*tx3) ; ty2 = @a2*@f2(@b2*ty3) tx2 = iter(2, @a0*px + tx3, qx, S) ty2 = iter(2, @a0*py + ty3, qy, S) if @op1 == "( )" ; f1(f2(f3(.))) ; dx = @a1*@f1(@b1*tx2) ; dy = @a1*@f1(@b1*ty2) dx = iter(1, @a0*px + tx2, qx, S) dy = iter(1, @a0*py + ty2, qy, S) else ; f1(.) op1 f2(f3(.)) ; tx1 = @a1*@f1(@b1*px) ; ty1 = @a1*@f1(@b1*py) tx1 = iter(1, px, qx, S) ty1 = iter(1, py, qy, S) dx = m_Combine.go(tx1, @op1, tx2) dy = m_Combine.go(ty1, @op1, ty2) endif else ; tx3 = @a3*@f3(@b3*px) ; ty3 = @a3*@f3(@b3*py) tx3 = iter(3, px, qx, S) ty3 = iter(3, py, qy, S) ; tx2 = @a2*@f2(@b2*px) ; ty2 = @a2*@f2(@b2*py) tx2 = iter(2, px, qx, S) ty2 = iter(2, py, qy, S) tx2 = m_Combine.go(tx2, @op2a, tx3) ty2 = m_Combine.go(ty2, @op2a, ty3) if @op1 == "( )" ; f1(f2(.) op2a f3(.)) ; dx = @a1*@f1(@b1*tx2) ; dy = @a1*@f1(@b1*ty2) dx = iter(1, @a0*px + tx2, qx, S) dy = iter(1, @a0*py + ty2, qy, S) else ; tx1 = @a1*@f1(@b1*px) ; ty1 = @a1*@f1(@b1*py) tx1 = iter(1, px, qx, S) ty1 = iter(1, py, qy, S) dx = m_Combine.go(tx1, @op1, tx2) dy = m_Combine.go(ty1, @op1, ty2) endif endif else ;@omode == "(f1 op1 f2) op2 f3" ; @op3a == "()" not allowed with this ordering ; tx2 = @a2*@f2(@b2*px) ; ty2 = @a2*@f2(@b2*py) tx2 = iter(2, px, qx, S) ty2 = iter(2, py, qy, S) if @op1 == "( )" ; (f1(f2(.))) op2b f3(.) ; tx1 = @a1*@f1(@b1*tx2) ; ty1 = @a1*@f1(@b1*ty2) tx1 = iter(1, @a0*px + tx2, qx, S) ty1 = iter(1, @a0*py + ty2, qy, S) else ; tx1 = @a1*@f1(@b1*px) ; ty1 = @a1*@f1(@b1*py) tx1 = iter(1, px, qx, S) ty1 = iter(1, py, qy, S) tx1 = m_Combine.go(tx1, @op1, tx2) ty1 = m_Combine.go(ty1, @op1, ty2) endif ; tx3 = @a3*@f3(@b3*px) ; ty3 = @a3*@f3(@b3*py) tx3 = iter(3, px, qx, S) ty3 = iter(3, py, qy, S) dx = m_Combine.go(tx1, @op2b, tx3) dy = m_Combine.go(ty1, @op2b, ty3) endif dx = dx + @a4*qx dy = dy + @a4*qy if @v_Gnarl2SFormula < 230 if (@xop=="-") dx = -dx endif if (@yop=="-") dy = -dy endif else if @xyop == "-dx +dy" || @xyop == "-dx -dy" dx = -dx endif if @xyop == "+dx -dy" || @xyop == "-dx -dy" dy = -dy endif endif znew = pz + @step * (dx + flip(dy)) S.Update(znew, S.pc) return S.bailed endfunc complex func iter(int n, complex v, complex c, State S) if @type == 0 if n == 1 return @a1*@f1(@b1*v) elseif n == 2 return @a2*@f2(@b2*v) else return @a3*@f3(@b3*v) endif else complex pz = S.pz ; save these complex pc = S.pz S.pc = c complex t if n == 1 S.pz = @b1*v m_Sf1.Iterate(S) t = @a1*S.pz elseif n == 2 S.pz = @b2*v m_Sf2.Iterate(S) t = @a2*S.pz else S.pz = @b3*v m_Sf3.Iterate(S) t = @a3*S.pz endif S.pz = pz, S.pc = pc return t endif endfunc ;protected: complex m_rot SFormula m_Sf1 SFormula m_Sf2 SFormula m_Sf3 Combine m_Combine default: rating = NotRecommended title = "Gnarl2" int param v_Gnarl2SFormula caption = "Version (Gnarl2_SFormula)" default = 230 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_Gnarl2SFormula < 230 endparam heading caption = "Gnarl2 doesn't work quite as promised; use Gnarl3 instead." endheading param ctype enum = "z" "seed" "pixel" caption = "final addition" ;hint = "makes a difference sometimes" default = 0 visible = @v_Gnarl2SFormula < 220 endparam complex param p_seed caption = "Seed" default = (0.3, 0) visible = (@ctype==1 || @ctype1 == 1) endparam param type caption = "Type" enum = "built-in function" "Sformula" default = 0 ;hint = "function = built-in functions, like sin; formula = any SFormula plug-in" endparam param mode caption = "How to combine" default = 0 enum = "f1 op f2" "f1 op1 (f2 op2 f3)" "(f1 op1 f2) op2 f3" endparam complex param a0 caption = "a0" default = 0 visible = @type == 1 && ( \ (@mode == 0 && @op1a == 5) || \ (@mode == 1 && (@op1 == 5 || @op2a == 5)) || \ (@mode == 2 && @op1 == 5) ) endparam param op1 caption = "Operator 1" enum = "+" "-" "*" "/" "^" "( )" default = 0 hint = "How to combine Formula values." visible = @mode > 0 endparam param op1a caption = "Operator" enum = "+" "-" "*" "/" "^" "( )" default = 5 hint = "How to combine Formula values." visible = @mode == 0 endparam param op2a caption = "Operator 2" enum = "+" "-" "*" "/" "^" "( )" default = 2 hint = "How to combine Formula values." visible = (@mode == 1) endparam param op2b caption = "Operator 2" enum = "+" "-" "*" "/" "^" ; () doesn't make sense default = 2 hint = "How to combine Formula values." visible = (@mode == 2) endparam float param theta caption = "Theta" default = 0 hint = "First rotate z by this much (360 = full circle)" endparam param args caption = "args" enum = "x,y" "y,x" default = 1 endparam heading caption = "a1*f1(b1*v)" endheading complex param a1 caption = "Gnarl2 a1" default = 1 hint = "multiplies f1" endparam complex param b1 caption = "Gnarl2 b1" default = 1 hint = "multiplies f1's arg" endparam func f1 caption = "Gnarl2 f1" default = cos() hint = "outer or left function" visible = @type == 0 endfunc SFormula param f_SFormula1 caption = "Gnarl2 formula 1" default = JLB_FuncSFormula visible = @type >= 1 endparam heading caption = "a2*f2(b2*v)" endheading complex param a2 caption = "Gnarl2 a2" default = 2.7 hint = "multiplies f2" endparam complex param b2 caption = "Gnarl2 b2" default = 2.7 hint = "multiplies f2's arg" ; visible=( (@a1*@a2) != 0) endparam func f2 caption = "Gnarl2 f2" default = sin() hint = "second function" visible = @type == 0 endfunc SFormula param f_SFormula2 caption = "Gnarl2 formula 2" default = JLB_FuncSFormula visible = @type >= 1 endparam heading caption = "a3*f3(b3*v)" visible = @mode > 0 endheading complex param a3 caption = "Gnarl2 a3" default = 0 hint = "multiplies f3" visible = @mode > 0 endparam complex param b3 caption = "Gnarl2 b3" default = 1 hint = "multiplies f3's arg" visible = @mode > 0 endparam func f3 caption = "Gnarl2 f3" default = sqr() hint = "inmost or right function" visible = @type == 0 && @mode > 0 endfunc SFormula param f_SFormula3 caption = "Gnarl2 formula3" default = JLB_FuncSFormula visible = @type >= 1 && @mode > 0 endparam heading caption = "finally..." endheading param xyop enum = "+dx +dy" "+dx -dy" "-dx +dy" "-dx -dy" default = 1 hint = "final multiplcations of real and imaginary parts of z_calc" visible = @v_Gnarl2SFormula >= 230 endparam param ctype1 enum = "pixel (M-type)" "seed (J-type)" "z" caption = "final addition of c" default = 0 visible = @v_Gnarl2SFormula >= 230 endparam ; heading ; caption = "multiplies c" ; endheading complex param a4 caption = "c multiplier" default = 1.0 hint = "multiplies final addition" endparam param step caption = "Gnarl2 Step_size" default = 0.1 hint = "How much of this to use. z_new = z + Step_size * z_calc" endparam param xop enum = "+" "-" default = 1 hint = "multiply real part of z_calc" visible = @v_Gnarl2SFormula < 230 endparam param yop enum = "+" "-" default = 0 hint = "multiply imaginary part of z_calc" visible = @v_Gnarl2SFormula < 230 endparam } class JLB_Gnarl3SFormula(SFormula) { ; Based on the Gnarl formula from mt.ufm, as a SFormula. ; Calculate dx and dy based on the real or imaginary part of z, then combine. ; Add a multiple of z, #pixel, or a seed. ; Corrected version of Gnarl2, June 2021. public: import "common.ulb" func JLB_Gnarl3SFormula(Generic pparent) SFormula.SFormula(pparent) m_rot = cos(@theta*#pi/180) + flip(sin(@theta*#pi/180)) if @type == 1 m_Sf1 = new @f_SFormula1(this) m_Sf2 = new @f_SFormula2(this) m_Sf3 = new @f_SFormula3(this) endif m_Combine = new Combine() if @addT > 0 ;&& !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if (@ctype1 == "seed (J-type)") ppc = @p_seed endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if s.bailed return true endif if s.tooMany return false endif complex pz = S.pz ; complex pc = @p_seed complex znew float xc float yc if (@ctype1 == "z") xc = real(pz) yc = imag(pz) elseif (@ctype1 == "seed (J-type)") xc = real(ppc) yc = imag(ppc) else;if (@ctype1 == "pixel(M-type)") xc = real(#pixel) yc = imag(#pixel) endif complex zrot = pz * m_rot float x = real(zrot) float y = imag(zrot) complex dx = 0 ; satisfy compiler complex dy = 0 ; ditto complex tx1, complex tx2, complex tx3 complex ty1, complex ty2; complex ty3 complex px, complex py, complex qx, complex qy if @args == "x,y" px = x, qx = xc, py = y, qy = yc else ; "y,x" the original version, usually better px = y, qx = yc, py = x, qy = xc endif if @mode == 0 ;"f1 op1 f2" ;tx2 = @a2*@f2(@b2*px) ;ty2 = @a2*@f2(@b2*py) tx2 = iter(2, @b2*px, qx, S) ty2 = iter(2, @b2*py, qy, S) if @op1a == "( )" ; f1(f2(.)) ; dx = @a1*@f1(@b1*tx2) ; dy = @a1*@f1(@b1*ty2) dx = iter(1, @b1*px + tx2, qx, S) ; wrong! dy = iter(1, @b1*py + ty2, qy, S) else ; f1(.) op1 f2(.) ; tx1 = @a1*@f1(@b1*px) ; ty1 = @a1*@f1(@b1*py) tx1 = iter(1, @b1*px, qx, S) ty1 = iter(1, @b1*py, qy, S) dx = m_Combine.go(tx1, @op1a, tx2) dy = m_Combine.go(ty1, @op1a, ty2) endif elseif @mode == 1 ;"f1 op1 (f2 op2 f3)" if @op2a == "( )" ; f1 op f2(f3(.)) ; tx3 = @a3*@f3(@b3*px) ; ty3 = @a3*@f3(@b3*py) tx3 = iter(3, @b3*px, qx, S) ty3 = iter(3, @b3*py, qy, S) ; tx2 = @a2*@f2(@b2*tx3) ; ty2 = @a2*@f2(@b2*ty3) tx2 = iter(2, @b2*px + tx3, qx, S) ty2 = iter(2, @b2*py + ty3, qy, S) if @op1 == "( )" ; f1(f2(f3(.))) ; dx = @a1*@f1(@b1*tx2) ; dy = @a1*@f1(@b1*ty2) dx = iter(1, @b1*px + tx2, qx, S) dy = iter(1, @b1*py + ty2, qy, S) else ; f1(.) op1 f2(f3(.)) ; tx1 = @a1*@f1(@b1*px) ; ty1 = @a1*@f1(@b1*py) tx1 = iter(1, @b1*px, qx, S) ty1 = iter(1, @b1*py, qy, S) dx = m_Combine.go(tx1, @op1, tx2) dy = m_Combine.go(ty1, @op1, ty2) endif else ; tx3 = @a3*@f3(@b3*px) ; ty3 = @a3*@f3(@b3*py) tx3 = iter(3, @b3*px, qx, S) ty3 = iter(3, @b3*py, qy, S) ; tx2 = @a2*@f2(@b2*px) ; ty2 = @a2*@f2(@b2*py) tx2 = iter(2, @b2*px, qx, S) ty2 = iter(2, @b2*py, qy, S) tx2 = m_Combine.go(tx2, @op2a, tx3) ty2 = m_Combine.go(ty2, @op2a, ty3) if @op1 == "( )" ; f1(f2(.) op2a f3(.)) ; dx = @a1*@f1(@b1*tx2) ; dy = @a1*@f1(@b1*ty2) dx = iter(1, @b1*px + tx2, qx, S) dy = iter(1, @b1*py + ty2, qy, S) else ; tx1 = @a1*@f1(@b1*px) ; ty1 = @a1*@f1(@b1*py) tx1 = iter(1, @b1*px, qx, S) ty1 = iter(1, @b1*py, qy, S) dx = m_Combine.go(tx1, @op1, tx2) dy = m_Combine.go(ty1, @op1, ty2) endif endif else ;@omode == "(f1 op1 f2) op2 f3" ; @op3a == "()" not allowed with this ordering ; tx2 = @a2*@f2(@b2*px) ; ty2 = @a2*@f2(@b2*py) tx2 = iter(2, @b2*px, qx, S) ty2 = iter(2, @b2*py, qy, S) if @op1 == "( )" ; (f1(f2(.))) op2b f3(.) ; tx1 = @a1*@f1(@b1*tx2) ; ty1 = @a1*@f1(@b1*ty2) tx1 = iter(1, @b1*px + tx2, qx, S) ty1 = iter(1, @b1*py + ty2, qy, S) else ; tx1 = @a1*@f1(@b1*px) ; ty1 = @a1*@f1(@b1*py) tx1 = iter(1, @b1*px, qx, S) ty1 = iter(1, @b1*py, qy, S) tx1 = m_Combine.go(tx1, @op1, tx2) ty1 = m_Combine.go(ty1, @op1, ty2) endif ; tx3 = @a3*@f3(@b3*px) ; ty3 = @a3*@f3(@b3*py) tx3 = iter(3, @b3*px, qx, S) ty3 = iter(3, @b3*py, qy, S) dx = m_Combine.go(tx1, @op2b, tx3) dy = m_Combine.go(ty1, @op2b, ty3) endif dx = dx + @a4*qx dy = dy + @a4*qy if @xyop == "-dx +dy" || @xyop == "-dx -dy" dx = -dx endif if @xyop == "+dx -dy" || @xyop == "-dx -dy" dy = -dy endif znew = pz + @step * (dx + flip(dy)) ;if !@p_std if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif ;endif S.Update(znew, S.pc) return S.bailed endfunc complex func iter(int n, complex v, complex c, State S) if @type == 0 if n == 1 return @a1*@f1(v) elseif n == 2 return @a2*@f2(v) else return @a3*@f3(v) endif else complex pz = S.pz ; save these complex pc = S.pz S.pc = c complex t if n == 1 S.pz = v m_Sf1.Iterate(S) t = @a1*S.pz elseif n == 2 S.pz = v m_Sf2.Iterate(S) t = @a2*S.pz else S.pz = v m_Sf3.Iterate(S) t = @a3*S.pz endif S.pz = pz, S.pc = pc return t endif endfunc ;protected: complex m_rot SFormula m_Sf1 SFormula m_Sf2 SFormula m_Sf3 Combine m_Combine complex ppc UtilityTransform finalTransform default: ;rating = Recommended title = "Gnarl3" int param v_Gnarl3SFormula caption = "Version (Gnarl3_SFormula)" default = 1 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_Gnarl3SFormula < 1 endparam ; bool param p_std ; caption = "Standard" ; default = true ; endparam param type caption = "Type" enum = "built-in function" "Sformula" default = 0 ;hint = "function = built-in functions, like sin; formula = any SFormula plug-in" endparam param mode caption = "How to combine" default = 1 enum = "f1 op f2" "f1 op1 (f2 op2 f3)" "(f1 op1 f2) op2 f3" endparam param op1 caption = "Operator 1" enum = "+" "-" "*" "/" "^" "( )" default = 0 hint = "How to combine Formula values." visible = @mode > 0 endparam param op1a caption = "Operator" enum = "+" "-" "*" "/" "^" "( )" default = 0 hint = "How to combine Formula values." visible = @mode == 0 endparam param op2a caption = "Operator 2" enum = "+" "-" "*" "/" "^" "( )" default = 2 hint = "How to combine Formula values." visible = (@mode == 1) endparam param op2b caption = "Operator 2" enum = "+" "-" "*" "/" "^" ; () doesn't make sense default = 2 hint = "How to combine Formula values." visible = (@mode == 2) endparam float param theta caption = "Theta" default = 0 hint = "First rotate z by this much (360 = full circle)" endparam param args caption = "args" enum = "x,y" "y,x" default = 1 endparam heading caption = "a1*f1(b1*v)" endheading complex param a1 caption = "Gnarl3 a1" default = 1 hint = "multiplies f1" endparam complex param b1 caption = "Gnarl3 b1" default = 2 hint = "multiplies f1's arg" endparam func f1 caption = "Gnarl3 f1" default = sin() hint = "outer or left function" visible = @type == 0 endfunc SFormula param f_SFormula1 caption = "Gnarl3 formula 1" default = JLB_FuncSFormula visible = @type >= 1 endparam heading caption = "a2*f2(b2*v)" endheading complex param a2 caption = "Gnarl3 a2" default = 2.7 hint = "multiplies f2" endparam complex param b2 caption = "Gnarl3 b2" default = 2.7 hint = "multiplies f2's arg" endparam func f2 caption = "Gnarl3 f2" default = sin() hint = "second function" visible = @type == 0 endfunc SFormula param f_SFormula2 caption = "Gnarl3 formula 2" default = JLB_FuncSFormula visible = @type >= 1 endparam heading caption = "a3*f3(b3*v)" visible = @mode > 0 endheading complex param a3 caption = "Gnarl3 a3" default = 0 hint = "multiplies f3" visible = @mode > 0 endparam complex param b3 caption = "Gnarl3 b3" default = 1 hint = "multiplies f3's arg" visible = @mode > 0 endparam func f3 caption = "Gnarl3 f3" default = sqr() hint = "inmost or right function" visible = @type == 0 && @mode > 0 endfunc SFormula param f_SFormula3 caption = "Gnarl3 formula3" default = JLB_FuncSFormula visible = @type >= 1 && @mode > 0 endparam heading caption = "finally..." endheading param xyop enum = "+dx +dy" "+dx -dy" "-dx +dy" "-dx -dy" default = 1 hint = "final multiplcations of real and imaginary parts of z_calc" endparam param ctype1 enum = "pixel (M-type)" "seed (J-type)" "z" caption = "final addition of c" default = 0 endparam complex param p_seed caption = "Seed" default = (0.3, 0) visible = @ctype1 == 1 endparam complex param a4 caption = "c multiplier" default = 1.0 hint = "multiplies final addition" endparam param step caption = "Gnarl3 Step_size" default = 0.1 hint = "How much of this to use. z_new = z + Step_size * z_calc" endparam heading caption = "Tweak after each iteration" ;visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 ;visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 ;&& !@p_std selectable = false endparam } ; combine two complex values in various ways class Combine { public: func Combine() endfunc static complex func go(const complex t1, int op, const complex t2) if (op == 0 ) return t1 + t2 elseif (op == 1) return t1 - t2 elseif (op == 2) return t1 * t2 elseif (op == 3) return t1 / t2 else;if (op == 4) return t1 ^ t2 endif endfunc } class JLB_GnarlySFormula(SFormula) { ; Similar in concept to Dynamical Systems in mt.ufm public: import "common.ulb" func JLB_GnarlySFormula(Generic pparent) SFormula.SFormula(pparent) m_rot = cos(@theta*#pi/180) + flip(sin(@theta*#pi/180)) m_SFormula1 = new @f_SFormula1(this) m_SFormula2 = new @f_SFormula2(this) if @addT > 0 ;&& !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if @v_GnarlySFormula > 100 && !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz complex znew complex zrot = pz * m_rot float dx float dy S.pz = imag(zrot) + flip(real(zrot)) m_SFormula1.Iterate(S) dx = real(S.pz) S.pz = zrot m_SFormula2.Iterate(S) dy = real(S.pz) if (@xop=="-") dx = -dx endif if (@yop=="-") dy = -dy endif znew = pz + @step * (dx + flip(dy)) + ppc ;if !@p_std if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif ;endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex m_rot SFormula m_SFormula1 SFormula m_SFormula2 complex ppc UtilityTransform finalTransform default: ;rating = Recommended title = "Gnarly" int param v_GnarlySFormula caption = "Version (Gnarly_SFormula)" default = 200 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_GnarlySFormula < 200 endparam ; bool param p_std ; caption = "Standard" ; default = true ; endparam bool param p_Mtype caption = "M-type?" default = true visible = @v_GnarlySFormula > 100 endparam complex param p_seed caption = "Seed for J-type" default = (0.3, 0) visible = @v_GnarlySFormula > 100 && !@p_Mtype endparam SFormula param f_SFormula1 caption = "dx formula" default = JLB_FuncSFormula endparam SFormula param f_SFormula2 caption = "dy formula" default = JLB_FuncSFormula endparam float param theta caption = "Theta" default = 0 hint = "First rotate z by this much (360 = full circle)" endparam param ctype enum = "z" "c" "pixel" caption = "c multiplier" default = 0 endparam param step caption = "Step_size" default = 0.1 hint = "How much of this to use. z_new = z + Step_size * z_calc" endparam param xop enum = "+" "-" default = 1 hint = "multiply real part of z_calc" endparam param yop enum = "+" "-" default = 0 hint = "multiply imaginary part of z_calc" endparam heading caption = "Tweak after each iteration" ;visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 ;visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 ;&& !@p_std selectable = false endparam } ; August 2020 ; Fixed grad function ; Changed to JLB_Random ; Noise based on #screenpixel, not on pz! class JLB_Perlin3DNoiseSFormula(SFormula) { ; Code adapted from https://flafla2.github.io/2014/08/09/perlinnoise.html ; (Not Ken Perlin's original version.) ; pc not used public: import "common.ulb" func JLB_Perlin3DNoiseSFormula(Generic pparent) SFormula.SFormula(pparent) ;m_FractalCoordinate = new FractalCoordinate(this) r = new JLB_Random(0) r.Init(@seed) int i = 0 ; generate random permutation of integers 0 - 255 while i < 256 px[i] = py[i] = i i = i + 1 endwhile int tmp int j r.SetNMax(256) j = 0 while j < 256 i = r.RandomIntInRange(0) tmp = px[i] px[i] = px[j] px[j] = tmp j = j + 1 endwhile j = 0 while j < 256 i = r.RandomIntInRange(0) tmp = py[i] py[i] = py[j] py[j] = tmp j = j + 1 endwhile i = 0 while i < 256 px[i+512] = px[i+256] = px[i] py[i+512] = py[i+256] = py[i] i = i + 1 endwhile px[768] = px[0] ; in case of overflow in grad, bbb line py[768] = py[0] endfunc func Init(complex pz, complex pc) ;m_FractalCoordinate.Init(pz) endfunc ; @param pz ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex tz = #screenpixel ; NOT m_FractalCoordinate.Iterate(#screenpixel) float tx = real(tz) float ty = imag(tz) m_doingX = true complex dx = OctavePerlin(tx, ty, @zz-#pi/2) ; @zz always 0 m_doingX = false complex dy = OctavePerlin(tx, ty, @zz+#pi/2) ; scale so range is about (-1,+1) if strength ~ 1 complex znew = @strength * (2 / #magn) * (dx + flip(dy)) if !@do_dz znew = S.pz + znew endif S.update(znew, S.pc) return S.bailed endfunc float func OctavePerlin(float x, float y, float z) float total = 0 float freq = @frequency / 100 ; empirical scaling float amp = 1 float maxValue = 0 int i = 0 while i < @octaves total = total + amp * perlin(x*freq, y*freq, z*freq) maxValue = maxValue + amp amp = amp * @persistence freq = 2 * freq i = i + 1 endwhile return 2*total/maxValue - 1.0 endfunc float func perlin(float x, float y, float z) x = x % 256 y = y % 256 z = z % 256 int xi = floor(x) ; Calculate the "unit cube" that the point asked will be located in. int yi = floor(y) ; The left bound is ( xi, yi, zi) and the right bound is that plus 1. int zi = floor(z) float xf = x - floor(x) float yf = y - floor(y) ; Next we calculate the location (from 0.0 to 1.0) in that cube. float zf = z - floor(z) float u = fade(xf); ; We also fade the location to smooth the result. float v = fade(yf); float w = fade(zf); int aaa, int aba, int aab, int abb int baa, int bba, int bab, int bbb if m_doingX aaa = px[px[px[ xi ]+ yi ]+ zi ] aba = px[px[px[ xi ]+ 1+yi ]+ zi ] aab = px[px[px[ xi ]+ yi ]+ 1+zi ] abb = px[px[px[ xi ]+ 1+yi ]+ 1+zi ] baa = px[px[px[ 1+xi ]+ yi ]+ zi ] bba = px[px[px[ 1+xi ]+ 1+yi ]+ zi ] bab = px[px[px[ 1+xi ]+ yi ]+ 1+zi ] ; if inc just adds 1, max index = 3*256 bbb = px[px[px[ 1+xi ]+ 1+yi ]+ 1+zi ] ; so p needs to be 3*256+1 = 769 else aaa = py[py[py[ xi ]+ yi ]+ zi ] aba = py[py[py[ xi ]+ 1+yi ]+ zi ] aab = py[py[py[ xi ]+ yi ]+ 1+zi ] abb = py[py[py[ xi ]+ 1+yi ]+ 1+zi ] baa = py[py[py[ 1+xi ]+ yi ]+ zi ] bba = py[py[py[ 1+xi ]+ 1+yi ]+ zi ] bab = py[py[py[ 1+xi ]+ yi ]+ 1+zi ] ; if inc just adds 1, max index = 3*256 bbb = py[py[py[ 1+xi ]+ 1+yi ]+ 1+zi ] endif ; The gradient function calculates the dot product between a pseudorandom gradient vector ; and the vector from the input coordinate to the 8 surrounding points in its unit cube. ; This is all then lerped together as a sort of weighted average based on the faded (u,v,w). float x1 = lerp(grad (aaa, xf , yf , zf), grad (baa, xf-1, yf , zf), u) float x2 = lerp(grad (aba, xf , yf-1, zf), grad (bba, xf-1, yf-1, zf), u) float y1 = lerp(x1, x2, v) x1 = lerp(grad (aab, xf , yf , zf-1), grad (bab, xf-1, yf , zf-1), u) x2 = lerp(grad (abb, xf , yf-1, zf-1), grad (bbb, xf-1, yf-1, zf-1), u) float y2 = lerp (x1, x2, v) return (lerp (y1, y2, w) + 1) / 2 ; value between 0 and 1 endfunc ; hash is in [0,255] float func grad(int hash, float x, float y, float z) int h = hash % 16; ; Use the last 4 bits of hash if h == 0, return x + y elseif h == 1, return -x + y elseif h == 2, return x - y elseif h == 3, return -x - y elseif h == 4, return x + z elseif h == 5, return -x + z elseif h == 6, return x - z elseif h == 7, return -x - z elseif h == 8, return y + z elseif h == 9, return -y + z elseif h ==10, return y - z elseif h ==11, return -y - z elseif h ==12, return y + x elseif h ==13, return -y + z elseif h ==14, return y - x else , return -y - z endif endfunc ; increment num by 1 with roll-over ; int func inc(int num) ; return (num + 1 ) % 256 ; endfunc ; Fade function as defined by Ken Perlin. Replaces linear interpolation between 0 and 1, ; providing continuous first and second derivatives at 0 and 1. ; This ends up smoothing the final output. float func fade(float t) return t * t * t * (t * (t * 6 - 15) + 10) ; 6t^5 - 15t^4 + 10t^3 endfunc ; linearly interpret between a and b, assuming that t is from 0 to 1, inclusive float func lerp(float a, float b, float t) return a + t * (b - a) endfunc protected: int px[769] ; 3*256 + 1 int py[769] bool m_doingX JLB_Random r default: title = "Perlin 3D Noise" rating = Recommended int param v_PerlinNoiseSFormula caption = "Version (PerlinNoise_SFormula)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_PerlinNoiseSFormula < 100 endparam heading text = "The noise is based on the screen position. Probably this is best used \ for one iteration as the Initial Fractal, or as a texture layer." endheading int param seed caption ="Seed" hint = "Seed for random numbers" default = 12345679 endparam int param octaves caption = "Octaves" default = 3 min = 1 hint = "The lowest octave has the largest amplitude and the lowest frequency. \ Subsequent octaves have lower amplitude and higher frequency. \ Noise is the sum of all octaves." endparam float param frequency caption = "Frequency" hint = "Frequency of lowest octave noise. Lower values give smoother noise, \ higher values less smooth." default = 1 endparam float param strength caption = "Noise Strength" default = 0.05 endparam float param persistence caption = "Persistence" hint = "Smaller values give smoother noise." max = 1.0 min = 0.0 default = 0.5 endparam float param zz caption = "3D parameter" ;default = 0 visible = false endparam bool param do_dz caption = "Return dz instead of z?" default = false visible = false ; just for debugging endparam } class JLB_BarnsleySFormula(SFormula) { ; A version of a Barnsley formula as a SFormula. public: import "common.ulb" func JLB_BarnsleySFormula(Generic pparent) SFormula(pparent) if @addT > 0 finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if @v_BarnsleySFormula > 100 && !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz float temp = 0 ; avoid compiler warning if @mode == "real" temp = real(pz) elseif @mode == "imaginary" temp = imag(pz) elseif @mode == "angle" temp = atan2(pz) elseif @mode == "cross+" temp = real(pz)*imag(ppc) + imag(pz)*real(ppc) elseif @mode == "cross-" temp = real(pz)*imag(ppc) - imag(pz)*real(ppc) elseif @mode == "dot+" temp = real(pz)*real(ppc) + imag(pz)*imag(ppc) elseif @mode == "dot-" temp = real(pz)*real(ppc) - imag(pz)*imag(ppc) endif if temp >= @crit if @type_hi == "times" pz = (pz^@pow_hi + @off_hi) * ppc elseif @type_hi == "plus" pz = (pz^@pow_hi + @off_hi) + ppc elseif @type_hi == "minus" pz = (pz^@pow_hi + @off_hi) - ppc elseif @type_hi == "divided" pz = (pz^@pow_hi + @off_hi) / ppc elseif @type_hi == "exponential" pz = (pz^@pow_hi + @off_hi) ^ ppc endif else if @type_lo == "times" pz = (pz^@pow_lo + @off_lo) * ppc elseif @type_lo == "plus" pz = (pz^@pow_lo + @off_lo) + ppc elseif @type_lo == "minus" pz = (pz^@pow_lo + @off_lo) - ppc elseif @type_lo == "divided" pz = (pz^@pow_lo + @off_lo) / ppc elseif @type_lo == "exponential" pz = (pz^@pow_lo + @off_lo) ^ ppc endif endif complex znew = pz if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex ppc UtilityTransform finalTransform default: title = "Barnsley" rating = notRecommended int param v_BarnsleySFormula caption = "Version (Barnsley_SFormula)" default = 200 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_BarnsleySFormula < 200 endparam bool param p_Mtype caption = "M-type?" default = true visible = @v_BarnsleySFormula > 100 endparam complex param p_seed caption = "Seed for J-type" default = (0.3, 0) visible = @v_BarnsleySFormula > 100 && !@p_Mtype endparam ; parameters to calculate the number to compare to the critical value heading text = "Use combinations of real and imaginary parts of z and c\ to calculate value to compare with critical value." endheading param mode caption = "mode" default = 0 enum = "real" "imaginary" "angle" "cross+" "cross-" "dot+" "dot-" endparam float param crit caption = "critical value" default = 0.0 endparam heading caption = "If value >= critical value" endheading heading caption = " z_new = (z^p1 + a1) op1 c" endheading complex param pow_hi caption = "p1" ;hint = "formula contains z^pow_high + off_high" default = 2 endparam complex param off_hi caption = "a1" ;hint = "formula contains z^pow_high + off_high" default = (0,0) endparam param type_hi caption = "op1" default = 0 enum = "times" "plus" "minus" "divided" "exponential" endparam heading caption = "If value < critical value" endheading heading caption = " z_new = (z^p2 + a2) op2 c" endheading complex param pow_lo caption = "p2" ;hint = "formula contains z^pow_low + off_low" default = 2 endparam complex param off_lo caption = "a2" ;hint = "formula contains z^pow_low + off_low" default = (0,0) endparam param type_lo caption = "op2" default = 0 enum = "times" "plus" "minus" "divided" "exponential" endparam heading caption = "Tweak after each iteration" endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 selectable = false endparam } ; class FractalCoordinate(UtilityTransform) { ; Use the one in common.ulb ; } class Contraction(common.ulb:Generic) { ; Contraction base class. ; Exact copy from mmf.ulb in case of future changes public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func Contraction(Generic pparent) Generic.Generic(pparent) endfunc ; @param pz ; @param pc func Init(complex pz, complex pc) endfunc ; @param pz ; @param pc ; @return the contracted value complex func Iterate(complex pz, complex pc) return pz endfunc default: int param v_contraction caption = "Version (Contraction)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_contraction < 100 endparam } class JLB_PhoenixContraction(Contraction) { ; The Phoenix formula as a Contraction. public: ; @param pz ; @param pc func Init(complex pz, complex pc) z_older = 0 ; pz ? endfunc ; @param pz ; @param pc ; @return the new pz complex func Iterate(complex pz, complex pc) complex z_new if @p_std z_new = sqr(pz) + pc + 0.5 * z_older z_older = pz return z_new else z_new = pz^@p_power + pc*(pz) + @p_a*z_older z_older = pz return z_new endif endfunc protected: complex z_older default: title = "Phoenix" ;rating = Recommended int param v_Phoenixcontraction caption = "Version (Phoenix_Contraction)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_Phoenixcontraction < 100 endparam bool param p_std caption = "Standard" hint = "z_new = sqr(z_old) + c + a * z_older" default = true endparam complex param p_power caption = "p1" default = 2 hint = "z_calc = z_old^p1 + c * z_old^p2 + a * z_older" visible = !@p_std endparam complex param p_p2 caption = "p2" default = 0 hint = "z_calc = z_old^p1 + c * z_old^p2 + a * z_older" visible = !@p_std endparam complex param p_a caption = "a" default = 0.5 hint = "z_calc = z_old^p1 + c * z_old^p2 + a * z_older" visible = !@p_std endparam } class JLB_NovaContraction(Contraction) { ; The Nova formula as a Contraction. public: ; @param pz ; @param pc ; @return the new pz complex func Iterate(complex pz, complex pc) if (@p_std) return pz - @relax*(pz*pz*pz-1) / (3*pz*pz) + pc else return pz - @relax * (pz^@p_power-1) / (@p_power * pz^(@p_power-1)) + pc endif endfunc private: default: title = "Nova" ;rating = notRecommended int param v_Novacontraction caption = "Version (Nova_Contraction)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_Novacontraction < 100 endparam bool param p_std caption = "Standard" default = true endparam param relax caption = "Relaxation" default = (1,0) hint = "This can be used to slow down the convergence of \ the formula." endparam complex param p_power caption = "Power" default = (3,0) visible = !@p_std endparam } class JLB_TalisContraction(Contraction) { ; The Talis formulas, part of the "Switch Talis and friends" formula in tma.ulb, ; as a Contraction. public: ; @param pz ; @param pc ; @return the new pz complex func Iterate(complex pz, complex pc) complex tz = pz complex tc = pc if (!@p_std) tz = tz - @p_zoffset tc = tc - @p_coffset endif if @var == "Talis" tz =@fn1((tz-@p1)*@p1a)*@fn2(tz)/@fn3(@p2+(@fn4(tz)*@p2a))+\ @fn5(tc-@p3)*@p3a elseif @var == "Talis Var 1" tz =@fn1(((tz-@p1)*@p1a)*@fn2(tz)-@fn6(tz)) /@fn3(@p2+(@fn4(tz)*@p2a))+\ @fn5(tc-@p3)*@p3a elseif @var == "Talis Var 2" tz =@fn1(((tz-@p1)*@p1a)*@fn2(tz)*@fn6(tz)) /@fn3(@p2+(@fn4(tz)*@p2a))+\ @fn5(tc-@p3)*@p3a elseif @var == "Talis Var 3" tz =@fn1(((tz-@p1)*@p1a)*@fn2(tz)*@fn6(tz)*tz) /@fn3(@p2+(@fn4(tz)*@p2a)\ *@fn7(tz)-@fn8(tz))+@fn5(tc-@p3)*@p3a elseif @var == "Talis Var 4" tz =@fn1(((tz-@p1)*@p1a)*@fn2(tz)*@fn6(tz)) /@fn3(@p2+(@fn4(tz)*@p2a)\ *@fn7(tz)-@fn8(tz))+@fn5(tc-@p3)*@p3a elseif @var == "Talis Var 5" tz =@fn1(((tz-@p1)*@p1a)*@fn2(tz)*@fn6(tz)) /@fn3(@p2+(@fn4(tz)*@p2a)\ *@fn7(tz)-@fn8(tz)-tz)+@fn5(tc-@p3)*@p3a elseif @var == "Talis Var 6" tz =@fn1(((tz-@p1)*@p1a)*@fn2(tz)*@fn6(tz)) /@fn3(@p2+(@fn4(tz)*@p2a)\ *@fn7(tz)-@fn8(tz)+tz)+@fn5(tc-@p3)*@p3a endif return tz endfunc private: default: title = "Talis" ;rating = notRecommended int param v_Taliscontraction caption = "Version (Talis_Contraction)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_Taliscontraction < 100 endparam param var caption = "Formula Type" enum = "Talis" "Talis Var 1" "Talis Var 2" "Talis Var 3" \ "Talis Var 4" "Talis Var 5" "Talis Var 6" default = 0 visible = !@p_std endparam complex param p1 caption = "1st Z Offset" default = (0,0) visible = !@p_std endparam complex param p1a caption = "1st Z Strength" default = (1,0) visible = !@p_std endparam complex param p2 caption = "2nd Z Offset" default = (1,0) visible = !@p_std endparam complex param p2a caption = "2nd Z Strength" default = (1,0) visible = !@p_std endparam complex param p3 caption = "C Offset" default = (0,0) visible = !@p_std endparam complex param p3a caption = "C Strength" default = (1,0) visible = !@p_std endparam func fn1 caption = "1st Z Function" default = ident () visible = !@p_std endfunc func fn2 caption = "2nd Z Function" default = ident () visible = !@p_std endfunc func fn3 caption = "3rd Z Function" default = ident () visible = !@p_std endfunc func fn4 caption = "Another Function" default = ident () visible = !@p_std endfunc func fn5 caption = "C Function" default = ident () visible = !@p_std endfunc func fn6 caption = "Extra Z Function" default = ident () visible = @var > 0 && !@p_std endfunc func fn7 caption = "Extra Z2 Function" default = ident () visible = @var > 2 && !@p_std endfunc func fn8 caption = "Extra Z3 Function" default = ident () visible = @var > 2 && !@p_std endfunc complex param p_zoffset caption = "pz offset" default = (0,0) visible = !@p_std endparam complex param p_coffset caption = "pc offset" default = (0,0) visible = !@p_std endparam bool param p_std caption = "Standard" default = true endparam } class JLB_SwitchSuper(mmf.ulb:MMF_SwitchDivergentFormula) { ; A wrapper that enables many formulas to be duplicated ; and new ones made. Plug this into David Makin's ; Generic Switch Formula to do Mandelbrot-style and Julia-style ; formulas. ; public: import "common.ulb" import "mmf.ulb" func JLB_SwitchSuper(Generic pparent) MMF_SwitchDivergentFormula.MMF_SwitchDivergentFormula(pparent) m_BailoutFunc = new @f_bailout(this) m_DualTransform = new @f_DualTransform(this) m_DualTransformInit = new @f_DualTransformInit(this) endfunc ; @param pz ; @Initialize complex func Init(complex pz) m_BailoutFunc.Init(@p_bailout) ; from parent class pz = MMF_SwitchDivergentFormula.Init(pz) m_DualTransformInit.Init(pz) m_DualTransformInit.Iterate(pz, fConstant) ;do the work return pz endfunc ; @param pz ; Iterate--all the work is done in the plug-in. complex func Iterate(complex pz) m_DualTransform.Iterate(pz, fConstant) return pz endfunc ; @param pz ; @return true if bailed out. bool func IsBailedOut(complex pz) return m_BailoutFunc.Iterate(pz) endfunc protected: DualTransform m_DualTransform DualTransform m_DualTransformInit Reduction m_BailoutFunc default: rating = notRecommended title = "Switch Super" ;rating = recommended param p_power ; Overrides p_power from Formula caption = "Power" default = (2,0) visible = false enabled = false endparam int param v_superswitch caption = "Version (JLB_superswitch)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_superswitch < 100 endparam Heading caption = "Start of Switch Super parameters" Endheading Heading Endheading DualTransform param f_DualTransformInit caption = "Initialize" default = JLB_NullDualTransform endparam Heading Endheading DualTransform param f_DualTransform caption = "Formula" default = JLB_MandelbrotDualTransform endparam Heading caption = "Bailout" Endheading Reduction param f_bailout caption = "Bailout Type" default = JLB_SimpleBailout endparam } class DualTransform(common.ulb:Generic) { ; DualTransform base class. No title, so only used for a base class. public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func DualTransform(Generic pparent) Generic.Generic(pparent) endfunc complex func Init(complex pz) return pz endfunc ; @param pz ; @param pc ; @return true if okay; possibly change both arguments bool func Iterate(complex &pz, complex &pc) return true endfunc default: rating = notRecommended int param v_DualTransform caption = "Version (DualTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_DualTransform < 100 endparam } class JLB_DoubleDualTransform(DualTransform) { ; A wrapper for two DualTransforms public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_DoubleDualTransform(Generic pparent) DualTransform.DualTransform(pparent) m_DualTransform1 = new @f_DualTransform1(this) m_DualTransform2 = new @f_DualTransform2(this) endfunc ; @param pz ; @param pc ; @return true if okay; possibly change both arguments bool func Iterate(complex &pz, complex &pc) bool result1 = m_DualTransform1.Iterate(pz, pc) bool result2 = m_DualTransform2.Iterate(pz, pc) return (result1 && result2) endfunc protected: DualTransform m_DualTransform1 DualTransform m_DualTransform2 default: rating = notRecommended title = "Double DualTransform" int param v_DoubleDualTransform caption = "Version (DoubleDualTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_DoubleDualTransform < 100 endparam DualTransform param f_DualTransform1 caption = "First DualTransform" default = JLB_ZCZDualTransform ; hint = "The default Null DualTransform does nothing." endparam Heading endHeading DualTransform param f_DualTransform2 caption = "Second DualTransform" default = JLB_ZCZDualTransform ; hint = "The default Null DualTransform does nothing." endparam } class JLB_NullDualTransform(DualTransform) { ; A DualTransform placeholder that does nothing. public: import "common.ulb" ; @param pz ; @param pc ; @return true if okay; possibly change both arguments bool func Iterate(complex &pz, complex &pc) return true endfunc default: rating = notRecommended title = "Null DualTransform" int param v_NullDualTransform caption = "Version (NullDualTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_NullDualTransform < 100 endparam } class JLB_MandelbrotDualTransform(DualTransform) { ; The Mandelbrot formula as a DualTransform public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_MandelbrotDualTransform(Generic pparent) DualTransform.DualTransform(pparent) endfunc ; @param pz ; @param pc ; @return true if okay; possibly change both arguments bool func Iterate(complex &pz, complex &pc) pz = sqr(pz) + pc return true endfunc default: rating = notRecommended title = "Mandelbrot DualTransform" int param v_MandelbrotDualTransform caption = "Version (MandelbrotDualTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_MandelbrotDualTransform < 100 endparam } class JLB_ZZDualTransform(DualTransform) { ; ZZDualTransform does two Z contractions ; p_z1 calls to a function, new z = ZContraction1(z, c) ; p_z2 calls to a function, new z = ZContraction2(z, c) public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_ZZDualTransform(Generic pparent) DualTransform.DualTransform(pparent) m_ZContraction1 = new @f_ZContraction1(this) m_ZContraction2 = new @f_ZContraction2(this) endfunc ; @param pz ; @param pc ; @return true if okay; possibly change both arguments bool func Iterate(complex &pz, complex &pc) int i i = 0 while (i < @p_z1) pz = m_ZContraction1.Iterate(pz, pc) if (isNan(|pz|)) return true endif i = i + 1 endwhile i = 0 while (i < @p_z2) pz = m_ZContraction2.Iterate(pz, pc) if (isNan(|pz|)) return true endif i = i + 1 endwhile return true endfunc protected: Contraction m_ZContraction1 Contraction m_ZContraction2 default: rating = notRecommended title = "ZZ DualTransform" int param v_ZZDualTransform caption = "Version (ZZDualTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_ZZDualTransform < 100 endparam Heading Endheading Heading caption = "New z based on z and c" Endheading int param p_z1 caption = "Times" min = 0 default = 1 endparam Contraction param f_ZContraction1 caption = "z/c -> new z" default = JLB_MandelbrotContraction ; hint = "The default Null Contraction does nothing." endparam Heading caption = "New z based on z and c" Endheading int param p_z2 caption = "Times" min = 0 default = 1 endparam Contraction param f_ZContraction2 caption = "z/c -> new z" default = JLB_MandelbrotContraction ; hint = "The default Null Contraction does nothing." endparam Heading Endheading } class JLB_ZCZDualTransform(DualTransform) { ; ZCZDualTransform does three functions. ; p_z calls to a function, new z = ZContraction(z, c) ; p_c calls to a function, new c = CContraction(z, c) ; p_t calls to a function, new z = UserTransform(z) ; (Was UtilityTransform in version 100) public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_ZCZDualTransform(Generic pparent) DualTransform.DualTransform(pparent) m_ZContraction = new @f_ZContraction(this) m_CContraction = new @f_CContraction(this) ; m_UserTransform = new @f_UserTransform(this) if (@v_ZCZDualTransform > 100) m_UserTransform = new @f_UserTransform(this) else m_UtilityTransform = new @f_UtilityTransform(this) endif endfunc ; @param pz ; @param pc ; @return true if okay; possibly change both arguments bool func Iterate(complex &pz, complex &pc) int i i = 0 while (i < @p_z) pz = m_ZContraction.Iterate(pz, pc) if (isNan(|pz|)) return true endif i = i + 1 endwhile i = 0 while (i < @p_c) pc = m_CContraction.Iterate(pz, pc) i = i + 1 endwhile if (@v_ZCZDualTransform > 100) i = 0 while (i < @p_t) pz = m_UserTransform.Iterate(pz) if (isNan(|pz|)) return true endif i = i + 1 endwhile else i = 0 while (i < @p_t) pz = m_UtilityTransform.Iterate(pz) if (isNan(|pz|)) return true endif i = i + 1 endwhile endif return true endfunc protected: Contraction m_ZContraction Contraction m_CContraction UserTransform m_UserTransform UtilityTransform m_UtilityTransform default: rating = notRecommended title = "ZCZ DualTransform" int param v_ZCZDualTransform caption = "Version (ZCZDualTransform)" default = 101 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_ZCZDualTransform < 101 endparam Heading Endheading Heading caption = "New z based on z and c" Endheading int param p_z caption = "Times" min = 0 default = 1 endparam Contraction param f_ZContraction caption = "z/c -> new z" default = JLB_MandelbrotContraction ; hint = "The default Null Contraction does nothing." endparam Heading caption = "New c based on z and c" Endheading int param p_c caption = "Times" min = 0 default = 0 endparam Contraction param f_CContraction caption = "z/c -> new c" default = JLB_NullCContraction hint = "The default Null Contraction does nothing." endparam Heading caption = "New z based on z" Endheading int param p_t caption = "Times" min = 0 default = 0 endparam UserTransform param f_UserTransform caption = "z -> new z" default = JLB_NullUserTransform hint = "The default Null Transform does nothing." visible = (@v_ZCZDualTransform > 100) endparam UtilityTransform param f_UtilityTransform caption = "z -> new z" default = JLB_NullUtilityTransform hint = "The default Null Transform does nothing." visible = (@v_ZCZDualTransform < 101) endparam Heading Endheading } class JLB_BurningShipContraction(Contraction) { ; The Burning Ship formula as a Contraction. public: ; @param pz ; @param pc ; @return the new pz complex func Iterate(complex pz, complex pc) float zx float zy if (@p_std) zx = abs(real(pz)) zy = abs(imag(pz)) return sqr(zx+flip(zy)) + pc else zx=abs(real(@xfunction(real(pz-@p_zoffset))))^@xpower zy=abs(real(@yfunction(imag(pz-@p_zoffset))))^@ypower return (@xweight*zx+@yweight*flip(zy))^@zpower+(pc - @p_coffset) endif endfunc default: title = "Burning Ship" ;rating = notRecommended int param v_BurningShipcontraction caption = "Version (BurningShip_Contraction)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_BurningShipcontraction < 100 endparam bool param p_std caption = "Standard" default = true endparam heading caption="General" visible = !@p_std endheading complex param p_zoffset caption = "pz offset" default = (0,0) visible = !@p_std endparam complex param p_coffset caption = "pc offset" default = (0,0) visible = !@p_std endparam complex param zpower caption="power" default=2 visible = !@p_std endparam ; ; x parameters ; heading caption="X" visible = !@p_std endheading float param xpower caption="power" default=1 visible = !@p_std endparam func xfunction caption="function" default=ident() visible = !@p_std endfunc complex param xweight caption="weight" default=1 visible = !@p_std endparam ; ; y parameters ; heading caption="Y" visible = !@p_std endheading float param ypower caption="power" default=1 visible = !@p_std endparam func yfunction caption="function" default=ident() visible = !@p_std endfunc complex param yweight caption="weight" default=1 visible = !@p_std endparam } class JLB_ZTweakContraction(Contraction) { ; The Z tweaks, as in Compounding Tweaked Mandelbrot from lkm.ufm, ; as a Contraction. public: ; @param pz ; @param pc ; @return the new pz complex func Iterate(complex pz, complex pc) if (@tweaktype=="fn(c)") return pz + @tweakage*@tweakfunction(pc) elseif (@tweaktype=="fn(z)") return pz + @tweakage*@tweakfunction(pz) elseif (@tweaktype=="fn(c*z)") return pz + @tweakage*@tweakfunction(pc*pz) elseif (@tweaktype=="fn(z/c)") return pz + @tweakage*@tweakfunction(pz/pc) else;if (@tweaktype=="fn(c/z)") return pz + @tweakage*@tweakfunction(pc/pz) endif endfunc default: title = "Tweak Z" rating = notRecommended int param v_ztweakcontraction caption = "Version (ztweak_Contraction)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_ztweakcontraction < 100 endparam param tweaktype caption="tweaking type" default=0 enum= "fn(c)" "fn(z)" "fn(c*z)" "fn(z/c)" "fn(c/z)" hint="Sets how z gets tweaked." endparam param tweakage caption="tweaking amount" default=(0.01,0.0) hint="Usually small." endparam func tweakfunction caption="tweaking function" default=recip() hint="Function of the tweaking variable." endfunc } class JLB_CTweakContraction(Contraction) { ; The C tweaks, as in Compounding Tweaked Mandelbrot from lkm.ufm, ; as a Contraction. public: ; @param pz ; @param pc ; @return the new pz complex func Iterate(complex pz, complex pc) if (@tweaktype=="fn(c)") return pc + @tweakage*@tweakfunction(pc) elseif (@tweaktype=="fn(z)") return pc + @tweakage*@tweakfunction(pz) elseif (@tweaktype=="fn(c*z)") return pc + @tweakage*@tweakfunction(pc*pz) elseif (@tweaktype=="fn(z/c)") return pc + @tweakage*@tweakfunction(pz/pc) else;if (@tweaktype=="fn(c/z)") return pc + @tweakage*@tweakfunction(pc/pz) endif endfunc default: title = "Tweak C" rating = notRecommended int param v_ctweakcontraction caption = "Version (ztweak_Contraction)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_ctweakcontraction < 100 endparam param tweaktype caption="tweaking type" default=0 enum= "fn(c)" "fn(z)" "fn(c*z)" "fn(z/c)" "fn(c/z)" hint="Sets how c gets tweaked." endparam param tweakage caption="tweaking amount" default=(0.01,0.0) hint="Usually small." endparam func tweakfunction caption="tweaking function" default=recip() hint="Function of the tweaking variable." endfunc } class JLB_NullCContraction(Contraction) { ; A do-nothing C Contraction. public: ; @param pz ; @param pc ; @return the same pc complex func Iterate(complex pz, complex pc) return pc endfunc default: title = "returns c" ;"Null Contraction" rating = notRecommended int param v_nullccontraction caption = "Version (JLB_NullCContraction)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_nullccontraction < 100 endparam } class JLB_NewtonUserTransform(common.ulb:UserTransform) { ; The Newton formula as a Transform. public: ; @param pz ; @return the new pz complex func Iterate(complex pz) if (@p_std) return (2*sqr(pz)*pz+1)/(3*sqr(pz)) else ; complex pm1 = @p_power - 1 ; complex pzp = pz ^ pm1 ; return pz * (1-@relax) + \ ; @relax * (pm1 * pz * pzp + @r) / (@p_power * pzp) return pz * (1-@relax) + \ @relax * ((@p_power - 1) * pz^@p_power + @r) / \ (@p_power * pz ^ (@p_power - 1)) endif endfunc default: title = "Newton" rating = notrecommended int param v_NewtonUserTransform caption = "Version (Newton_UserTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_NewtonUserTransform < 100 endparam bool param p_std caption = "Standard" default = true endparam complex param p_power caption = "Exponent" default = (3,0) hint = "Specifies the exponent of the equation that is solved by \ Newton's method. Use real numbers (set the imaginary part \ to zero) to obtain classic Newton fractals." visible = !@p_std endparam param r caption = "Root" default = (1,0) hint = "Specifies the root of the equation that is solved. Use larger \ numbers for slower convergence." visible = !@p_std endparam param relax caption = "Relaxation" default = (1,0) hint = "This can be used to alter the convergence of \ the formula." visible = !@p_std endparam } class JLB_NullUserTransform(common.ulb:UserTransform) { ; A do-nothing Transform public: func JLB_NULLUserTransform(Generic pparent) UserTransform.UserTransform(pparent) endfunc ; @param pz ; @return the same pz complex func Iterate(complex pz) return pz endfunc default: title = "(No change)" ;"Null User Transform" rating = notRecommended int param v_nullUsertransform caption = "Version (NullUserTransform)" default = 100 hint = "This version parameter is used to detect when a change has been made to the formula that is incompatible with the previous version. When that happens, this field will reflect the old version number to alert you to the fact that an alternate rendering is being used." visible = @v_nullUsertransform < 100 endparam } class JLB_SpiroGlyphicUserTransform(common.ulb:UserTransform) { ; Part of the Spiroglyphics formula, as in tma2.ufm, as a Transform. public: func JLB_SpiroGlyphicUserTransform(Generic pparent) UserTransform.UserTransform(pparent) endfunc func Init(complex pz) if @op1 == "++++" m1=+1, m2=+1, m3=+1, m4=+1 elseif @op1 == "+++-" m1=+1, m2=+1, m3=+1, m4=-1 elseif @op1 == "++-+" m1=+1, m2=+1, m3=-1, m4=+1 elseif @op1 == "+-++" m1=+1, m2=-1, m3=+1, m4=+1 elseif @op1 == "-+++" m1=-1, m2=+1, m3=+1, m4=+1 elseif @op1 == "++--" m1=+1, m2=+1, m3=-1, m4=-1 elseif @op1 == "+--+" m1=+1, m2=-1, m3=-1, m4=+1 elseif @op1 == "--++" m1=-1, m2=-1, m3=+1, m4=+1 elseif @op1 == "-++-" m1=-1, m2=+1, m3=+1, m4=-1 elseif @op1 == "+-+-" m1=+1, m2=-1, m3=+1, m4=-1 elseif @op1 == "-+-+" m1=-1, m2=+1, m3=-1, m4=+1 elseif @op1 == "+---" m1=+1, m2=-1, m3=-1, m4=+1 elseif @op1 == "-+--" m1=-1, m2=+1, m3=-1, m4=-1 elseif @op1 == "--+-" m1=-1, m2=-1, m3=+1, m4=-1 elseif @op1 == "---+" m1=-1, m2=-1, m3=-1, m4=+1 else;if @op1 == "----" m1=-1, m2=-1, m3=-1, m4=-1 endif if @op2 == "++++" n1=+1, n2=+1, n3=+1, n4=+1 elseif @op2 == "+++-" n1=+1, n2=+1, n3=+1, n4=-1 elseif @op2 == "++-+" n1=+1, n2=+1, n3=-1, n4=+1 elseif @op2 == "+-++" n1=+1, n2=-1, n3=+1, n4=+1 elseif @op2 == "-+++" n1=-1, n2=+1, n3=+1, n4=+1 elseif @op2 == "++--" n1=+1, n2=+1, n3=-1, n4=-1 elseif @op2 == "+--+" n1=+1, n2=-1, n3=-1, n4=+1 elseif @op2 == "--++" n1=-1, n2=-1, n3=+1, n4=+1 elseif @op2 == "-++-" n1=-1, n2=+1, n3=+1, n4=-1 elseif @op2 == "+-+-" n1=+1, n2=-1, n3=+1, n4=-1 elseif @op2 == "-+-+" n1=-1, n2=+1, n3=-1, n4=+1 elseif @op2 == "+---" n1=+1, n2=-1, n3=-1, n4=+1 elseif @op2 == "-+--" n1=-1, n2=+1, n3=-1, n4=-1 elseif @op2 == "--+-" n1=-1, n2=-1, n3=+1, n4=-1 elseif @op2 == "---+" n1=-1, n2=-1, n3=-1, n4=+1 else;if @op2 == "----" n1=-1, n2=-1, n3=-1, n4=-1 endif endfunc ; @param pz ; @return the new pz complex func Iterate(complex pz) complex x = real(@fnx(pz)) + flip(real(@start/10)) complex y = imag(@fny(pz)) + flip(imag(@start/10)) complex newx = y complex newy = x int i = 0 if @chain == "Separate" while i < @flavor i = i + 1 newx = y + m1*real(@h2a/10)*@fn1(@a1 * newx) + \ m2*imag(@h2a/10)*@fn3(@a2 * newx) newy = x + m3*real(@h2b/10)*@fn2(@a3 * newy) + \ m4 *imag(@h2b/10)*@fn4(@a4 * newy) endwhile x = x + n1*real(@h1a/10)*@fn5(newx) + n2*imag(@h1a/10)*@fn7(newx) y = y + n3*real(@h1b/10)*@fn6(newy) + n4*imag(@h1b/10)*@fn8(newy) elseif @chain == "Ganged" while i < @flavor i = i + 1 newx = y + m1*real(@h2/10)*@fn1(@a1 * newx) + \ m2*imag(@h2/10)*@fn3(@a2 * newx) newy = x + m3*real(@h2/10)*@fn2(@a3 * newy) + \ m4*imag(@h2/10)*@fn4(@a4 * newy) endwhile x = x + n1*real(@h1/10)*@fn5(newx) + n2*imag(@h1/10)*@fn7(newx) y = y + n3*real(@h1/10)*@fn6(newy) + n4*imag(@h1/10)*@fn8(newy) endif if @op == "x+y" pz = @fna(x) + flip(@fnb(y)) elseif @op == "x-y" pz = @fna(x)- flip(@fnb(y)) else;if @op == "y-x" pz = flip(@fnb(y)) - @fna(x) endif return pz endfunc protected: int m1, int m2, int m3, int m4 int n1, int n2, int n3, int n4 default: rating = notRecommended title = "Spiroglyphic" int param v_SpiroglyphicUsertransform caption = "Version (SpiroglyphicUserTransform)" default = 100 hint = "This version parameter is used to detect when a change has been made to the formula that is incompatible with the previous version. When that happens, this field will reflect the old version number to alert you to the fact that an alternate rendering is being used." visible = @v_SpiroglyphicUsertransform < 100 endparam int param chain caption = "Intensity Mode" enum = "Separate" "Ganged" default = 0 hint = "Choose whether X/Y intensity and step sizes vary together \ or separately" endparam complex param h1a caption = "Intensity X" default = (.1,.1) visible = @chain == 0 endparam complex param h1b caption = "Intensity Y" default = (.1,.1) visible = @chain == 0 endparam complex param h2a caption = "Step Size X" default = (.8,.8) visible = @chain == 0 && (@h1a != (0,0) || @h1b != (0,0)) endparam complex param h2b caption = "Step Size Y" default = (.8,.8) visible = @chain == 0 && (@h1a != (0,0) || @h1b != (0,0)) endparam complex param h1 caption = "Intensity X/Y" default = (.1, .1) visible = @chain == 1 endparam complex param h2 caption = "Step Size X/Y" default = (.8,.8) visible = @chain == 1 && @h1 != (0,0) endparam complex param start caption = "Glyph Start" default = (0,0) endparam int param flavor caption = "Flavor" default = 2 min = 0 hint = "This is an integer param which works in steps. Higher \ numbers generally create more intense effects" visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam float param a1 caption = "Alpha 1" default = 2.7 visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam float param a2 caption = "Alpha 2" default = 2.7 visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam float param a3 caption = "Alpha 3" default = 2.7 visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam float param a4 caption = "Alpha 4" default = 2.7 visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam param op caption = "Main Operator" enum = "x+y" "x-y" "y-x" default = 0 endparam param op1 caption = "Operator 1" enum = "++++" "+++-" "++-+" "+-++" "-+++" "++--" "+--+" "--++" \ "-++-" "+-+-" "-+-+" "+---" "-+--" "--+-" "---+" "----" default = 0 visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam param op2 caption = "Operator 2" enum = "++++" "+++-" "++-+" "+-++" "-+++" "++--" "+--+" "--++" \ "-++-" "+-+-" "-+-+" "+---" "-+--" "--+-" "---+" "----" default = 7 visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam func fn1 caption = "Function 1" default = sin() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn2 caption = "Function 2" default = sin() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn3 caption = "Function 3" default = cos() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn4 caption = "Function 4" default = cos() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn5 caption = "Function 5" default = sin() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn6 caption = "Function 6" default = sin() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn7 caption = "Function 7" default = cos() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn8 caption = "Function 8" default = cos() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fna caption = "Initial X Fn" default = ident() endfunc func fnb caption = "Initial Y Fn" default = ident() endfunc func fnx caption = "Global X Fn" default = ident() endfunc func fny caption = "Global Y Fn" default = ident() endfunc } class JLB_MoebiusUserTransform(common.ulb:UserTransform) { ; The Moebius Transform, z = (a1*z + a2) / (a3*z + a4). ; Can also be applied to c, of course. ; June 2021 renamed parameters and added quadratic option public: ; @param pz ; @return the new pz complex func Iterate(complex pz) if @fz == 0 return (@a0 + @a1*pz) / (@b0 + @b1*pz) else return (@a0 + @a1*@f(@a2*pz)) / (@b0 + @b1*@f(@b2*pz)) endif endfunc default: rating = Recommended title = "Moebius" int param v_MoebiusUserTransform caption = "Version (Moebius_UserTransform)" default = 200 hint = "Old version." visible = @v_MoebiusUserTransform < 200 endparam heading caption = "v_new = (a0+a1*v)/(b0+b1*v)" visible = @fz == 0 endheading heading caption = "v_new = (a0+a1*F(a2*v)/(b0+b1*F(b2*v))" visible = @fz == 1 endheading param fz caption = "v or F(a*v)" enum = "v" "F(a*v)" default = 0 endparam func f caption = "F" default = sin() visible = @fz == 1 endfunc complex param a0 caption = "Moebius: a0" default = (0,0) endparam complex param a1 caption = "Moebius: a1" default = (1,0) endparam complex param a2 caption = "Moebius: a2" default = (1,0) visible = @fz == 1 endparam complex param b0 caption = "Moebius: b0" default = (1,0) endparam complex param b1 caption = "Moebius: b1" default = (-1,0) endparam complex param b2 caption = "Moebius: b2" default = (1,0) visible = @fz == 1 endparam } class JLB_ComboDualTransform(DualTransform) { ; ComboDualTransform does two UserTransforms and combines them ; with one of the operators +, -, *, /, ^. public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_ComboDualTransform(Generic pparent) DualTransform.DualTransform(pparent) m_UserTransform1 = new @f_UserTransform1(this) m_UserTransform2 = new @f_UserTransform2(this) endfunc ; @param pz ; @param pc ; @return true if okay; possibly change both arguments bool func Iterate(complex &pz, complex &pc) complex t1 if (@mode1 == "z") t1 = m_UserTransform1.Iterate(pz) else t1 = m_UserTransform1.Iterate(pc) endif complex t2 if (@mode2 == "z") t2 = m_UserTransform2.Iterate(pz) else t2 = m_UserTransform2.Iterate(pc) endif if (@op == "+") t1 = t1 + t2 elseif (@op == "-") t1 = t1 - t2 elseif (@op == "*") t1 = t1 * t2 elseif (@op == "/") t1 = t1 / t2 else;if (@op == "^") t1 = t1 ^ t2 endif if (@mode0 == "z") pz = t1 else pc = t1 endif return true endfunc protected: UserTransform m_UserTransform1 UserTransform m_UserTransform2 default: rating = notRecommended title = "Combo Dual Transform" int param v_ComboDualTransform caption = "Version (ComboDualTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_ComboDualTransform < 100 endparam param mode0 caption = "Change z or c?" default = 0 enum = "z" "c" hint = "Which variable to change" endparam Heading caption = "First transform" Endheading param mode1 caption = "Based on z or c?" default = 0 enum = "z" "c" endparam UserTransform param f_UserTransform1 caption = "Transform 1" default = JLB_BasicUserTransform ; hint = "The default Null BasicTransform does nothing." endparam Heading Endheading param op caption = "op" enum = "+" "-" "*" "/" "^" default = 0 hint = "result = First transform op second transform" endparam Heading caption = "Second transform" Endheading param mode2 caption = "Based on z or c?" default = 1 enum = "z" "c" endparam UserTransform param f_UserTransform2 default = JLB_NullUserTransform caption = "Transform 2" ; hint = "The default Null BasicTransform does nothing." endparam } class JLB_SpiroglyphicContraction(Contraction) { ; Part of the Spiroglyphics formula, as in tma2.ufm, as a Contraction. public: ; @param pz ; @param pc ; @return the contracted value complex func Iterate(complex pz, complex pc) if @select == "z"; select variable type pz = @fnz(pz-@zg)*@ezg elseif @select == "z+c" pz = @fnz(pz-@zg)*@ezg + @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z-c" pz = @fnz(pz-@zg)*@ezg - @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "c-z" pz = @fnc((pc-@cg)*@cmg)^@ecg - @fnz(pz-@zg)*@ezg elseif @select == "z*c" pz = @fnz(pz-@zg)*@ezg * @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z/c" pz = @fnz(pz-@zg)*@ezg / @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z+c|z+c" pz = @fnz(pz-@zg)*@ezg + @fnc((pc-@cg)*@cmg)^@ecg pz = @fnz(pz-@zg)*@ezg + @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z+c|c-z" pz = @fnz(pz-@zg)*@ezg + @fnc((pc-@cg)*@cmg)^@ecg pz = @fnc((pc-@cg)*@cmg)^@ecg - @fnz(pz-@zg)*@ezg elseif @select == "z+c|z*c" pz = @fnz(pz-@zg)*@ezg + @fnc((pc-@cg)*@cmg)^@ecg pz = @fnz(pz-@zg)*@ezg * @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z+c|z/c" pz = @fnz(pz-@zg)*@ezg + @fnc((pc-@cg)*@cmg)^@ecg pz = @fnz(pz-@zg)*@ezg / @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z-c|z-c" pz = @fnz(pz-@zg)*@ezg - @fnc((pc-@cg)*@cmg)^@ecg pz = @fnz(pz-@zg)*@ezg - @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z-c|c-z" pz = @fnz(pz-@zg)*@ezg - @fnc((pc-@cg)*@cmg)^@ecg pz = @fnc((pc-@cg)*@cmg)^@ecg - @fnz(pz-@zg)*@ezg elseif @select == "z-c|z*c" pz = @fnz(pz-@zg)*@ezg - @fnc((pc-@cg)*@cmg)^@ecg pz = @fnz(pz-@zg)*@ezg * @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z-c|z/c" pz = @fnz(pz-@zg)*@ezg - @fnc((pc-@cg)*@cmg)^@ecg pz = @fnz(pz-@zg)*@ezg / @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "c-z|z*c" pz = @fnc((pc-@cg)*@cmg)^@ecg - @fnz(pz-@zg)*@ezg pz = @fnz(pz-@zg)*@ezg * @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "c-z|z/c" pz = @fnc((pc-@cg)*@cmg)^@ecg - @fnz(pz-@zg)*@ezg pz = @fnz(pz-@zg)*@ezg / @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z*c|c-z" pz = @fnz(pz-@zg)*@ezg * @fnc((pc-@cg)*@cmg)^@ecg pz = @fnc((pc-@cg)*@cmg)^@ecg - @fnz(pz-@zg)*@ezg elseif @select == "z*c|z*c" pz = @fnz(pz-@zg)*@ezg * @fnc((pc-@cg)*@cmg)^@ecg pz = @fnz(pz-@zg)*@ezg * @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z/c|z+c" pz = @fnz(pz-@zg)*@ezg / @fnc((pc-@cg)*@cmg)^@ecg pz = @fnz(pz-@zg)*@ezg + @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z/c|z-c" pz = @fnz(pz-@zg)*@ezg / @fnc((pc-@cg)*@cmg)^@ecg pz = @fnz(pz-@zg)*@ezg - @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z/c|c-z" pz = @fnz(pz-@zg)*@ezg / @fnc((pc-@cg)*@cmg)^@ecg pz = @fnc((pc-@cg)*@cmg)^@ecg - @fnz(pz-@zg)*@ezg elseif @select == "z/c|z/c" pz = @fnz(pz-@zg)*@ezg / @fnc((pc-@cg)*@cmg)^@ecg pz = @fnz(pz-@zg)*@ezg / @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z*c+(Rz+Iz)" pz = (@fnz(pz-@zg)*@ezg*@fnc((pc-@cg)*@cmg)^@ecg)+((real(pz+@addz)\ *@muzr)^@exzr+(imag(pz+@addz)*@muzi)^@exzi) elseif @select == "z/c+(Rz+Iz)" pz = (@fnz(pz-@zg)*@ezg/@fnc((pc-@cg)*@cmg)^@ecg)+((real(pz+@addz)\ *@muzr)^@exzr+(imag(pz+@addz)*@muzi)^@exzi) elseif @select == "z/c-(Rz+Iz)" pz = (@fnz(pz-@zg)*@ezg/@fnc((pc-@cg)*@cmg)^@ecg)-((real(pz+@addz)\ *@muzr)^@exzr+(imag(pz+@addz)*@muzi)^@exzi) elseif @select == "z*c-+(Rz+Iz)" pz = (@fnz(pz-@zg)*@ezg*@fnc((pc-@cg)*@cmg)^@ecg)-((real(pz+@addz)\ *@muzr)\ ^@exzr+(imag(pz+@addz)*@muzi)^@exzi) pz = (@fnz(pz-@zg)*@ezg*@fnc((pc-@cg)*@cmg)^@ecg)+((real(pz+@addz)\ *@muzr)^@exzr+(imag(pz+@addz)*@muzi)^@exzi) elseif @select == "z/c-+(Rz+Iz)" pz = (@fnz(pz-@zg)*@ezg/@fnc((pc-@cg)*@cmg)^@ecg)-((real(pz+@addz)\ *@muzr)^@exzr+(imag(pz+@addz)*@muzi)^@exzi) pz = (@fnz(pz-@zg)*@ezg/@fnc((pc-@cg)*@cmg)^@ecg)+((real(pz+@addz)\ *@muzr)^@exzr+(imag(pz+@addz)*@muzi)^@exzi) elseif @select == "z*c+(Rz+Iz)|z+c" pz = (@fnz(pz-@zg)*@ezg*@fnc((pc-@cg)*@cmg)^@ecg)+((real(pz+@addz)\ *@muzr)^@exzr+(imag(pz+@addz)*@muzi)^@exzi) pz = @fnz(pz-@zg)*@ezg + @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z*c+(Rz+Iz)|z-c" pz = (@fnz(pz-@zg)*@ezg*@fnc((pc-@cg)*@cmg)^@ecg)+((real(pz+@addz)\ *@muzr)^@exzr+(imag(pz+@addz)*@muzi)^@exzi) pz = @fnz(pz-@zg)*@ezg - @fnc((pc-@cg)*@cmg)^@ecg elseif @select == "z*c+(Rz+Iz)|c-z" pz = (@fnz(pz-@zg)*@ezg*@fnc((pc-@cg)*@cmg)^@ecg)+((real(pz+@addz)\ *@muzr)^@exzr+(imag(pz+@addz)*@muzi)^@exzi) pz = @fnc((pc-@cg)*@cmg)^@ecg - @fnz(pz-@zg)*@ezg elseif @select == "z*c+(Rz+Iz)|z*c" pz = (@fnz(pz-@zg)*@ezg*@fnc((pc-@cg)*@cmg)^@ecg)+((real(pz+@addz)\ *@muzr)^@exzr+(imag(pz+@addz)*@muzi)^@exzi) pz = @fnz(pz-@zg)*@ezg * @fnc((pc-@cg)*@cmg)^@ecg else;if @select == "z*c+(Rz+Iz)|z/c" pz = (@fnz(pz-@zg)*@ezg*@fnc((pc-@cg)*@cmg)^@ecg)+((real(pz+@addz)\ *@muzr)^@exzr+(imag(pz+@addz)*@muzi)^@exzi) pz = @fnz(pz-@zg)*@ezg / @fnc((pc-@cg)*@cmg)^@ecg endif return pz endfunc default: title = "Spiroglyphic" ;rating = notRecommended int param v_SpiroglyphicContraction caption = "Version (TMA_Contraction)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_SpiroglyphicContraction < 100 endparam param select caption = "Z Mode" enum = "z" "z+c" "z-c" "c-z" "z*c" "z/c" "z+c|z+c" "z+c|c-z" "z+c|z*c"\ "z+c|z/c" "z-c|z-c" "z-c|c-z" "z-c|z*c" "z-c|z/c" "c-z|z*c" "c-z|z/c"\ "z*c|c-z" "z*c|z*c" "z/c|z+c" "z/c|z-c" "z/c|c-z" "z/c|z/c"\ "z*c+(Rz+Iz)" "z/c+(Rz+Iz)" "z/c-(Rz+Iz)" "z*c-+(Rz+Iz)"\ "z/c-+(Rz+Iz)" "z*c+(Rz+Iz)|z+c" "z*c+(Rz+Iz)|z-c" "z*c+(Rz+Iz)|c-z"\ "z*c+(Rz+Iz)|z*c" "z*c+(Rz+Iz)|z/c" default = 5 hint = "Determines the initial definition of Z" endparam complex param zg caption = "Z Param 1" default = (0,0) endparam complex param ezg caption = "Z Param 2" default = (1,0) endparam complex param addz caption = "Z Param 3" default = (1.0,0.0) visible = ( ((@select == 22)||(@select == 23)||(@select == 24)||\ (@select == 25)||(@select == 26)|| (@select == 27)||\ (@select == 28)||(@select == 29)||(@select == 30)|| \ (@select == 31)) endparam complex param muzr caption = "Z Strength re" default = (1.0,0.0) visible = ((@select == 22)||(@select == 23)||(@select == 24)||\ (@select == 25)||(@select == 26)|| (@select == 27)||\ (@select == 28)||(@select == 29)||(@select == 30)|| \ (@select == 31)) endparam complex param muzi caption = "Z Strength im" default = (1.0,0.0) visible = ((@select == 22)||(@select == 23)||(@select == 24)||\ (@select == 25)||(@select == 26)|| (@select == 27)||\ (@select == 28)||(@select == 29)||(@select == 30)|| \ (@select == 31)) endparam complex param exzr caption = "Z Power re" default = (1.0,0.0) visible = ((@select == 22)||(@select == 23)||(@select == 24)||\ (@select == 25)||(@select == 26)|| (@select == 27)||\ (@select == 28)||(@select == 29)||(@select == 30)|| \ (@select == 31)) endparam complex param exzi caption = "Z Power im" default = (1.0,0.0) visible = ((@select == 22)||(@select == 23)||(@select == 24)||\ (@select == 25)||(@select == 26)|| (@select == 27)||\ (@select == 28)||(@select == 29)||(@select == 30)|| \ (@select == 31)) endparam complex param cg caption = "C Param 1" default = (0,0) visible = @select != 0 endparam complex param cmg caption = "C Param 2" default = (1,0) visible = @select != 0 endparam complex param ecg caption = "C Param 3" default = (1,0) visible = @select != 0 endparam func fnz caption = "Global Z Func" default = sin() hint = "Applies a function to the initial Z" endfunc func fnc caption = "Global C Func" default = ident() visible = @select != 0 hint = "Applies a function to the initial C" endfunc } ; Here only for compatibility with version 100 of JLB_ZCZDualTransform class JLB_NullUtilityTransform(common.ulb:UtilityTransform) { ; A do-nothing Transform public: func JLB_NULLUtilityTransform(Generic pparent) UtilityTransform.UtilityTransform(pparent) endfunc ; @param pz ; @return the same pz complex func Iterate(complex pz) return pz endfunc default: rating = notRecommended title = "(No change)" ;"Null Utility Transform" int param v_nullUtilitytransform caption = "Version (NullUtilityTransform)" default = 100 hint = "This version parameter is used to detect when a change has been made to the formula that is incompatible with the previous version. When that happens, this field will reflect the old version number to alert you to the fact that an alternate rendering is being used." visible = @v_nullUtilitytransform < 100 endparam } ; Here only for compatibility with version 100 of JLB_ZCZDualTransform class JLB_NewtonUtilityTransform(common.ulb:UtilityTransform) { ; The Newton formula as a Transform. public: ; @param pz ; @return the new pz complex func Iterate(complex pz) if (@p_std) return (2*sqr(pz)*pz+1)/(3*sqr(pz)) else return pz*(1-@relax) + \ @relax * ((@p_power - 1) * pz^@p_power + @r) / \ (@p_power * pz ^ (@p_power - 1)) endif endfunc default: rating = notRecommended title = "Newton" int param v_NewtonUtilityTransform caption = "Version (Newton_UtilityTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_NewtonUtilityTransform < 100 endparam bool param p_std caption = "Standard" default = true endparam complex param p_power caption = "Exponent" default = (3,0) hint = "Specifies the exponent of the equation that is solved by \ Newton's method. Use real numbers (set the imaginary part \ to zero) to obtain classic Newton fractals." visible = !@p_std endparam param r caption = "Root" default = (1,0) hint = "Specifies the root of the equation that is solved. Use larger \ numbers for slower convergence." visible = !@p_std endparam param relax caption = "Relaxation" default = (1,0) hint = "This can be used to alter the convergence of \ the formula." visible = !@p_std endparam } ; Here only for compatibility with version 100 of JLB_ZCZDualTransform class JLB_SpiroGlyphicUtilityTransform(common.ulb:UtilityTransform) { ; Part of the Spiroglyphics formula, as in tma2.ufm, as a Transform. public: func JLB_SpiroGlyphicUtilityTransform(Generic pparent) UtilityTransform.UtilityTransform(pparent) endfunc func Init(complex pz) if @op1 == "++++" m1=+1, m2=+1, m3=+1, m4=+1 elseif @op1 == "+++-" m1=+1, m2=+1, m3=+1, m4=-1 elseif @op1 == "++-+" m1=+1, m2=+1, m3=-1, m4=+1 elseif @op1 == "+-++" m1=+1, m2=-1, m3=+1, m4=+1 elseif @op1 == "-+++" m1=-1, m2=+1, m3=+1, m4=+1 elseif @op1 == "++--" m1=+1, m2=+1, m3=-1, m4=-1 elseif @op1 == "+--+" m1=+1, m2=-1, m3=-1, m4=+1 elseif @op1 == "--++" m1=-1, m2=-1, m3=+1, m4=+1 elseif @op1 == "-++-" m1=-1, m2=+1, m3=+1, m4=-1 elseif @op1 == "+-+-" m1=+1, m2=-1, m3=+1, m4=-1 elseif @op1 == "-+-+" m1=-1, m2=+1, m3=-1, m4=+1 elseif @op1 == "+---" m1=+1, m2=-1, m3=-1, m4=+1 elseif @op1 == "-+--" m1=-1, m2=+1, m3=-1, m4=-1 elseif @op1 == "--+-" m1=-1, m2=-1, m3=+1, m4=-1 elseif @op1 == "---+" m1=-1, m2=-1, m3=-1, m4=+1 else;if @op1 == "----" m1=-1, m2=-1, m3=-1, m4=-1 endif if @op2 == "++++" n1=+1, n2=+1, n3=+1, n4=+1 elseif @op2 == "+++-" n1=+1, n2=+1, n3=+1, n4=-1 elseif @op2 == "++-+" n1=+1, n2=+1, n3=-1, n4=+1 elseif @op2 == "+-++" n1=+1, n2=-1, n3=+1, n4=+1 elseif @op2 == "-+++" n1=-1, n2=+1, n3=+1, n4=+1 elseif @op2 == "++--" n1=+1, n2=+1, n3=-1, n4=-1 elseif @op2 == "+--+" n1=+1, n2=-1, n3=-1, n4=+1 elseif @op2 == "--++" n1=-1, n2=-1, n3=+1, n4=+1 elseif @op2 == "-++-" n1=-1, n2=+1, n3=+1, n4=-1 elseif @op2 == "+-+-" n1=+1, n2=-1, n3=+1, n4=-1 elseif @op2 == "-+-+" n1=-1, n2=+1, n3=-1, n4=+1 elseif @op2 == "+---" n1=+1, n2=-1, n3=-1, n4=+1 elseif @op2 == "-+--" n1=-1, n2=+1, n3=-1, n4=-1 elseif @op2 == "--+-" n1=-1, n2=-1, n3=+1, n4=-1 elseif @op2 == "---+" n1=-1, n2=-1, n3=-1, n4=+1 else;if @op2 == "----" n1=-1, n2=-1, n3=-1, n4=-1 endif endfunc ; @param pz ; @return the new pz complex func Iterate(complex pz) complex x = real(@fnx(pz)) + flip(real(@start/10)) complex y = imag(@fny(pz)) + flip(imag(@start/10)) complex newx = y complex newy = x int i = 0 if @chain == "Separate" while i < @flavor i = i + 1 newx = y + m1*real(@h2a/10)*@fn1(@a1 * newx) + \ m2*imag(@h2a/10)*@fn3(@a2 * newx) newy = x + m3*real(@h2b/10)*@fn2(@a3 * newy) + \ m4 *imag(@h2b/10)*@fn4(@a4 * newy) endwhile x = x + n1*real(@h1a/10)*@fn5(newx) + n2*imag(@h1a/10)*@fn7(newx) y = y + n3*real(@h1b/10)*@fn6(newy) + n4*imag(@h1b/10)*@fn8(newy) elseif @chain == "Ganged" while i < @flavor i = i + 1 newx = y + m1*real(@h2/10)*@fn1(@a1 * newx) + \ m2*imag(@h2/10)*@fn3(@a2 * newx) newy = x + m3*real(@h2/10)*@fn2(@a3 * newy) + \ m4*imag(@h2/10)*@fn4(@a4 * newy) endwhile x = x + n1*real(@h1/10)*@fn5(newx) + n2*imag(@h1/10)*@fn7(newx) y = y + n3*real(@h1/10)*@fn6(newy) + n4*imag(@h1/10)*@fn8(newy) endif if @op == "x+y" pz = @fna(x) + flip(@fnb(y)) elseif @op == "x-y" pz = @fna(x)- flip(@fnb(y)) else;if @op == "y-x" pz = flip(@fnb(y)) - @fna(x) endif return pz endfunc protected: int m1, int m2, int m3, int m4 int n1, int n2, int n3, int n4 default: rating = notRecommended title = "Spiroglyphic" int param v_SpiroglyphicUtilitytransform caption = "Version (SpiroglyphicUtilityTransform)" default = 100 hint = "This version parameter is used to detect when a change has been made to the formula that is incompatible with the previous version. When that happens, this field will reflect the old version number to alert you to the fact that an alternate rendering is being used." visible = @v_SpiroglyphicUtilitytransform < 100 endparam int param chain caption = "Intensity Mode" enum = "Separate" "Ganged" default = 0 hint = "Choose whether X/Y intensity and step sizes vary together \ or separately" endparam complex param h1a caption = "Intensity X" default = (.1,.1) visible = @chain == 0 endparam complex param h1b caption = "Intensity Y" default = (.1,.1) visible = @chain == 0 endparam complex param h2a caption = "Step Size X" default = (.8,.8) visible = @chain == 0 && (@h1a != (0,0) || @h1b != (0,0)) endparam complex param h2b caption = "Step Size Y" default = (.8,.8) visible = @chain == 0 && (@h1a != (0,0) || @h1b != (0,0)) endparam complex param h1 caption = "Intensity X/Y" default = (.1, .1) visible = @chain == 1 endparam complex param h2 caption = "Step Size X/Y" default = (.8,.8) visible = @chain == 1 && @h1 != (0,0) endparam complex param start caption = "Glyph Start" default = (0,0) endparam int param flavor caption = "Flavor" default = 2 min = 0 hint = "This is an integer param which works in steps. Higher \ numbers generally create more intense effects" visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam float param a1 caption = "Alpha 1" default = 2.7 visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam float param a2 caption = "Alpha 2" default = 2.7 visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam float param a3 caption = "Alpha 3" default = 2.7 visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam float param a4 caption = "Alpha 4" default = 2.7 visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam param op caption = "Main Operator" enum = "x+y" "x-y" "y-x" default = 0 endparam param op1 caption = "Operator 1" enum = "++++" "+++-" "++-+" "+-++" "-+++" "++--" "+--+" "--++" \ "-++-" "+-+-" "-+-+" "+---" "-+--" "--+-" "---+" "----" default = 0 visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam param op2 caption = "Operator 2" enum = "++++" "+++-" "++-+" "+-++" "-+++" "++--" "+--+" "--++" \ "-++-" "+-+-" "-+-+" "+---" "-+--" "--+-" "---+" "----" default = 7 visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endparam func fn1 caption = "Function 1" default = sin() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn2 caption = "Function 2" default = sin() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn3 caption = "Function 3" default = cos() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn4 caption = "Function 4" default = cos() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn5 caption = "Function 5" default = sin() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn6 caption = "Function 6" default = sin() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn7 caption = "Function 7" default = cos() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fn8 caption = "Function 8" default = cos() visible = (@chain == 0 && @h1a != (0,0) || @h1b != (0,0) ) || \ (@chain == 1 && @h1 != (0,0)) endfunc func fna caption = "Initial X Fn" default = ident() endfunc func fnb caption = "Initial Y Fn" default = ident() endfunc func fnx caption = "Global X Fn" default = ident() endfunc func fny caption = "Global Y Fn" default = ident() endfunc } class Reduction(common.ulb:Generic) { ; Reduction base class. No title, so only used for a base class. ; Iterate takes a complex argument, returns a bool. public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func Reduction(Generic pparent) Generic.Generic(pparent) endfunc func Init(complex pz) endfunc ; @param pz ; @param pc bool func Iterate(complex pz) return true endfunc default: int param v_Reduction caption = "Version (Reduction)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_Reduction < 100 endparam } class JLB_SimpleBailout(Reduction) { public: import "common.ulb" func JLB_SimpleBailout(Generic pparent) Reduction.Reduction(pparent) endfunc func Init(complex pz) m_BailoutValue = real(pz) ;@p_default endfunc ; @param pz ; @return true if bailed out bool func Iterate(complex pz) float tmp = |pz| return (tmp > m_BailoutValue || isNan(tmp)) endfunc ; func SetBailoutValue(float value) ; m_BailoutValue = value ; endfunc protected: float m_BailoutValue default: title = "Simple Bailout" int param v_SimpleBailout caption = "Version (SimpleBailout)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_SimpleBailout < 100 endparam ; float param p_default ; default = 1 ; visible = false ; endparam } class JLB_AdvancedBailout(Reduction) { ; The Iterate code is a direct copy from Ron Barnett's code, which appears in ; several places. public: import "common.ulb" func JLB_AdvancedBailout(Generic pparent) Reduction.Reduction(pparent) endfunc func Init(complex pz) m_BailoutValue = real(pz) endfunc ; @param pz ; @return true if bailed out. bool func Iterate(complex pz) float tmp = |pz| if (isNan(tmp) || isInf(tmp)) return true elseif @bailout_type == "mod" return (tmp > m_BailoutValue) elseif @bailout_type == "real" return (sqr(real(pz)) > m_BailoutValue) elseif @bailout_type == "imag" return (sqr(imag(pz)) > m_BailoutValue) elseif @bailout_type == "or" return (sqr(real(pz)) > m_BailoutValue || sqr(imag(pz)) > m_BailoutValue) elseif @bailout_type == "and" return (sqr(real(pz)) > m_BailoutValue && sqr(imag(pz)) > m_BailoutValue) elseif @bailout_type == "manh" return (sqr(abs(real(pz)) + abs(imag(pz))) > m_BailoutValue) else ;if @bailout_type == "manr" return (sqr(real(pz) + imag(pz)) > m_BailoutValue) ; else;if @bailout_type == "function" ; return (sqr(real(@bailout_func(pz))) > m_BailoutValue) endif endfunc protected: float m_BailoutValue default: title = "Advanced Bailout" int param v_AdvancedBailout caption = "Version (AdvancedBailout)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_AdvancedBailout < 100 endparam param bailout_type caption = "Bailout test type" default = 0 enum = "mod" "real" "imag" "or" "and" "manh" "manr" ; "function" hint = "mod gives the usual behavior" endparam } class JLB_DualTransformContraction(Contraction) { ; A Contraction that calls a DualTransforms public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_DualTransformContraction(Generic pparent) Contraction.Contraction(pparent) m_DualTransform = new @f_DualTransform(this) endfunc ; @param pz ; @param pc ; @return pz complex func Iterate(complex pz, complex pc) m_DualTransform.Iterate(pz, pc) return pz ; ignore possible change in pc endfunc protected: DualTransform m_DualTransform default: rating = notRecommended title = "DualTransform Contraction" int param v_DualTransformContraction caption = "Version (DualTransformContraction)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_DualTransformContraction < 100 endparam DualTransform param f_DualTransform caption = "DualTransform" default = JLB_MandelbrotDualTransform endparam } class JLB_ComboMultiTransform(jlb.ulb:DualTransform) { ; ComboMultiTransform does three UsertTransforms and combines them ; with one of the operators +, -, *, /, ^. ; This allows formulas such as z = (f1 + f2)/f3 public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_ComboMultiTransform(Generic pparent) DualTransform.DualTransform(pparent) m_UserTransform1 = new @f_UserTransform1(this) if (@num >= 1) m_UserTransform2 = new @f_UserTransform2(this) if (@num >= 2) m_UserTransform3 = new @f_UserTransform3(this) endif endif m_Combine = new Combine() endfunc ; @param pz ; @param pc ; @return true if okay; possibly change both arguments bool func Iterate(complex &pz, complex &pc) complex t1 complex t2 complex t3 if (@mode1 == "z") t1 = m_UserTransform1.Iterate(pz) else t1 = m_UserTransform1.Iterate(pc) endif if (@num >= 1) ; "2" if (@mode2 == "z") t2 = m_UserTransform2.Iterate(pz) else t2 = m_UserTransform2.Iterate(pc) endif if (@num == "2") t1 = m_Combine.go(t1, @op1, t2) else if (@mode3 == "z") t3 = m_UserTransform3.Iterate(pz) else t3 = m_UserTransform3.Iterate(pc) endif if (@order3 == "t1 op1 (t2 op2 t3)" ) t2 = m_Combine.go(t2, @op2, t3) t1 = m_Combine.go(t1, @op1, t2) else ;if (@order == "(t1 op1 t2) op2 t3" ) t1 = m_Combine.go(t1, @op1, t2) t1 = m_Combine.go(t1, @op2, t3) endif endif endif if (@addc) t1 = t1 + pc endif if (@mode0 == "z") pz = t1 else pc = t1 endif return true endfunc protected: UserTransform m_UserTransform1 UserTransform m_UserTransform2 UserTransform m_UserTransform3 Combine m_Combine default: ;rating = notRecommended title = "Combo Multi Transform" int param v_ComboMultiTransform caption = "Version (ComboMultiTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_ComboMultiTransform < 100 endparam param mode0 caption = "Change z or c?" default = 0 enum = "z" "c" hint = "Which variable to change" endparam param num caption = "Number of transforms" default = 1 enum = "1" "2" "3" endparam param order3 caption = "Ordering" default = 0 enum = "t1 op1 (t2 op2 t3)" "(t1 op1 t2) op2 t3" hint = "Order of combining can make a big difference!" visible = (@num == 2) ; "3" endparam Heading caption = "Transform" visible = (@num == 0) Endheading Heading caption = "Transform #1" visible = (@num > 0) Endheading param mode1 caption = "Transform z or c?" default = 0 enum = "z" "c" endparam UserTransform param f_UserTransform1 caption = "Transform" default = JLB_BasicUserTransform ; hint = "The default Null BasicTransform does nothing." endparam Heading caption = "Operator" visible = (@num == 1) Endheading Heading caption = "Operator #1" visible = (@num > 1) Endheading param op1 caption = "Operator" enum = "+" "-" "*" "/" "^" default = 0 hint = "How to combine transform values." visible = (@num >= 1) endparam Heading caption = "Transform #2" visible = (@num > 0) Endheading param mode2 caption = "Transform z or c?" default = 0 enum = "z" "c" visible = (@num > 0) endparam UserTransform param f_UserTransform2 default = JLB_BasicUserTransform caption = "Transform" visible = (@num > 0) endparam Heading caption = "Operator #2" visible = (@num > 1) Endheading param op2 caption = "Operator" enum = "+" "-" "*" "/" "^" default = 0 hint = "How to combine transform values." visible = (@num > 1) endparam Heading caption = "Transform #3" visible = (@num > 1) Endheading param mode3 caption = "Transform z or c?" default = 1 enum = "z" "c" visible = (@num > 1) endparam UserTransform param f_UserTransform3 default = JLB_NullUserTransform caption = "Transform 3" visible = (@num > 1) endparam Heading caption = "Final addition" Endheading bool param addc default = true caption = "Add c?" endparam } class JLB_PowerUserTransform(common.ulb:UserTransform) { ; A basic Transform, z = a1+a2*f(a3*z+a4) ; Can also be applied to c, of course. public: ; @param pz ; @return the new pz complex func Iterate(complex pz) return @a1 + @a2*(@a3*pz + @a4)^@power endfunc default: rating = Recommended title = "Power" int param v_PowerUserTransform caption = "Version (Power_UserTransform)" default = 200 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_PowerUserTransform < 200 endparam heading caption = "v_new = a1+a2*(a3*v+a4)^Power" endheading complex param power caption = "Power" default = (2,0) ;hint = "New v = a1+a2*(a3*v+a4)^Power" endparam complex param a1 caption = "a1" default = (0,0) ;hint = "New v = a1+a2*(a3*v+a4)^Power" endparam complex param a2 caption = "a2" default = (1,0) ;hint = "New v = a1+a2*(a3*v+a4)^Power" endparam complex param a3 caption = "a3" default = (1,0) ;hint = "New v = a1+a2*(a3*v+a4)^Power" endparam complex param a4 caption = "a4" default = (0,0) ;hint = "New v = a1+a2*(a3*v+a4)^Power" endparam } class JLB_DoubleUserTransform(common.ulb:UserTransform) { ; A wrapper for two UserTransforms on one variable. public: import "common.ulb" import "jlb.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_DoubleUserTransform(Generic pparent) UserTransform.UserTransform(pparent) m_UserTransform1 = new @f_UserTransform1(this) m_UserTransform2 = new @f_UserTransform2(this) endfunc ; @param pz ; @return new value complex func Iterate(complex pz) if (@type == "regular") pz = m_UserTransform2.Iterate(m_UserTransform1.Iterate(pz)) else;if @type == "gnarl") int i = 0 while (i < @num) complex x = real(pz) complex y = imag(pz) float dx = real(m_UserTransform2.Iterate(m_UserTransform1.Iterate(y))) float dy = real(m_UserTransform2.Iterate(m_UserTransform1.Iterate(x))) if (@xop=="-") dx = -dx endif if (@yop=="-") dy = -dy endif pz = pz + @step * (dx + flip(dy)) i = i + 1 endwhile endif return pz endfunc protected: UserTransform m_UserTransform1 UserTransform m_UserTransform2 default: rating = notRecommended title = "Double UserTransform" int param v_DoubleUserTransform caption = "Version (DoubleUserTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_DoubleUserTransform < 100 endparam param type caption = "Combining type?" enum = "regular" "gnarl" default = 0 endparam float param num caption = "Number of Steps" default = 1 min = 1 visible = (@type == 1) endparam float param step caption = "Step size" default = 0.1 visible = (@type == 1) endparam param xop enum = "+" "-" default = 1 visible = (@type == 1) endparam param yop enum = "+" "-" default = 0 visible = (@type == 1) endparam UserTransform param f_UserTransform1 caption = "First Transform" default = JLB_BasicUserTransform endparam UserTransform param f_UserTransform2 caption = "Second Transform" default = JLB_BasicUserTransform endparam } class JLB_BasicUserTransform(common.ulb:UserTransform) { ; A basic Transform, z = a1+a2*f(a3*z+a4) ; Can also be applied to c, of course. ; June 2020 removed show parameter public: import "common.ulb" ; @param pz ; @return the new pz complex func Iterate(complex pz) return @a1 + @a2*@f(@a3*pz + @a4) endfunc default: rating = Recommended title = "Basic" int param v_BasicUserTransform caption = "Version (Basic_UserTransform)" default = 200 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_BasicUserTransform < 100 endparam heading caption = "v_new = a1+a2*f(a3*v+a4)" endheading func f caption = "f" default = sqr() ;hint = "New v = a1+a2*f(a3*v+a4)" endfunc complex param a1 caption = "a1" default = (0,0) ;hint = "New v = a1+a2*f(a3*v+a4)" endparam complex param a2 caption = "a2" default = (1,0) ;hint = "New v = a1+a2*f(a3*v+a4)" endparam complex param a3 caption = "a3" default = (1,0) ;hint = "New v = a1+a2*f(a3*v+a4)" endparam complex param a4 caption = "a4" default = (0,0) ;hint = "New v = a1+a2*f(a3*v+a4)" endparam } class JLB_SwitchSuperCD(mmf.ulb:MMF_SwitchConvergentDivergentFormula) { ; A wrapper that enables many formulas to be duplicated ; and new ones made. Plug this into David Makin's ; Generic Switch Formula to do Mandelbrot-style and Julia-style ; formulas. Both convergent and divergent formulas can be used. ; public: import "common.ulb" import "mmf.ulb" func JLB_SwitchSuperCD(Generic pparent) MMF_SwitchConvergentDivergentFormula.MMF_SwitchConvergentDivergentFormula(pparent) if (@p_InitType == "Contraction") m_InitContraction = new @f_InitContraction(this) elseif (@p_InitType == "DualTransform") m_InitDualTransform = new @f_InitDualTransform(this) elseif (@p_InitType == "Transform") m_InitUserTransform = new @f_InitUserTransform(this) endif if (@p_IterType == "Contraction") m_IterContraction = new @f_IterContraction(this) elseif (@p_IterType == "DualTransform") m_IterDualTransform = new @f_IterDualTransform(this) else;if (@p_IterType == "Transform") m_IterUserTransform = new @f_IterUserTransform(this) endif endfunc ; @param pz ; @Initialize complex func Init(complex pz) m_IsBad = false pz = MMF_SwitchConvergentDivergentFormula.Init(pz) if (@p_InitType == "Contraction") pz = m_InitContraction.Iterate(pz, fConstant) elseif (@p_InitType == "DualTransform") m_InitDualTransform.Iterate(pz, fConstant) elseif (@p_InitType == "Transform") pz = m_InitUserTransform.Iterate(pz) endif return pz endfunc ; @param pz ; Iterate--all the work is done in the plug-in. complex func Iterate(complex pz) pz = MMF_SwitchConvergentDivergentFormula.Iterate(pz) if (m_IsBad) return pz endif if (@p_IterType == "Contraction") pz = m_IterContraction.Iterate(pz, fConstant) elseif (@p_IterType == "DualTransform") m_IterDualTransform.Iterate(pz, fConstant) else;if (@p_IterType == "Transform") pz = m_IterUserTransform.Iterate(pz) + fConstant endif return pz endfunc ; ; @param pz ; ; @return true if bailed out. bool func IsBailedOut(complex pz) ; First check to see if iteration has gone bad float tmp = 0 ; to satisfy compiler if (!m_IsBad) if (@p_CorDBailout == "Convergent") tmp = |pz-m_ZOld| else;if (@p_CorDBailout == "Divergent") tmp = |pz| endif m_IsBad = IsInf(tmp) || IsNan(tmp) endif if (m_IsBad) if (@p_CorDBailout == "Convergent") return false else;if (@p_CorDBailout == "Divergent") return true endif endif ; pz is still okay ; The bailout code is slightly adapted from Ron Barnett's code, where it ; appears in several places. if (@p_CorDBailout == "Convergent") complex dif = pz - m_ZOld if @bailout_type == "mod" return (tmp < @p_lowerbailout) elseif @bailout_type == "real" return (sqr(real(dif)) < @p_lowerbailout) elseif @bailout_type == "imag" return (sqr(imag(dif)) < @p_lowerbailout) elseif @bailout_type == "or" return (sqr(real(dif)) < @p_lowerbailout || sqr(imag(dif)) < @p_lowerbailout) elseif @bailout_type == "and" return (sqr(real(dif)) < @p_lowerbailout && sqr(imag(dif)) < @p_lowerbailout) elseif @bailout_type == "manh" return (sqr(abs(real(dif)) + abs(imag(dif))) < @p_lowerbailout) else;if @bailout_type == "manr" return (sqr(real(dif) + imag(dif)) < @p_lowerbailout) endif else if @bailout_type == "mod" return (tmp > @p_upperbailout) elseif @bailout_type == "real" return (sqr(real(pz)) > @p_upperbailout) elseif @bailout_type == "imag" return (sqr(imag(pz)) > @p_upperbailout) elseif @bailout_type == "or" return (sqr(real(pz)) > @p_upperbailout || sqr(imag(pz)) > @p_upperbailout) elseif @bailout_type == "and" return (sqr(real(pz)) > @p_upperbailout && sqr(imag(pz)) > @p_upperbailout) elseif @bailout_type == "manh" return (sqr(abs(real(pz)) + abs(imag(pz))) > @p_upperbailout) else;if @bailout_type == "manr" return (sqr(real(pz) + imag(pz)) > @p_upperbailout) endif endif endfunc protected: UserTransform m_IterUserTransform UserTransform m_InitUserTransform DualTransform m_IterDualTransform DualTransform m_InitDualTransform Contraction m_IterContraction Contraction m_InitContraction bool m_IsBad default: rating = notRecommended title = "Switch Super CD" ;rating = recommended param p_power ; Overrides p_power from Formula caption = "Power" default = (2,0) visible = false enabled = false endparam int param v_superswitchcd caption = "Version (JLB_superswitchcd)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_superswitchcd < 100 endparam Heading caption = "Start of Switch Super CD parameters" Endheading Heading Endheading param p_InitType caption = "Initialization type" default = 0 enum = "None" "Contraction" "DualTransform" "Transform" hint = "A Contraction produces a new z from the old z and c. \ A DualTransform can produce a new z, a new c, or both \ from the old z and c. A Transform produces a new z from the old z." endparam Contraction param f_InitContraction caption = "Formula" default = JLB_NullZContraction visible = @p_InitType == 1 endparam DualTransform param f_InitDualTransform caption = "Formula" default = JLB_NullDualTransform visible = @p_InitType == 2 endparam UserTransform param f_InitUserTransform caption = "Formula" default = JLB_NullUserTransform visible = @p_InitType == 3 endparam Heading Endheading param p_IterType caption = "Formula type" default = 0 enum = "Contraction" "DualTransform" "Transform" hint = "A Contraction produces a new z from the old z and c. \ A DualTransform can produce a new z, a new c, or both \ from the old z and c. A Transform produces a new z from the old z; \ for iterations, c is added." endparam Contraction param f_IterContraction caption = "Formula" default = JLB_MandelbrotContraction visible = @p_IterType == 0 endparam DualTransform param f_IterDualTransform caption = "Formula" default = JLB_MandelbrotDualTransform visible = @p_IterType == 1 endparam UserTransform param f_IterUserTransform caption = "Formula" default = JLB_BasicUserTransform visible = @p_IterType == 2 endparam Heading caption = "Bailout" Endheading param p_CorDBailout enum = "Convergent" "Divergent" caption = "Fractal type" default = 1 endparam param bailout_type caption = "Bailout test type" default = 0 enum = "mod" "real" "imag" "or" "and" "manh" "manr" hint = "mod gives the usual behavior, same as the Standard bailout" ; visible = !@p_SimpleBailout endparam } class JLB_NewtonPolyUserTransform(common.ulb:UserTransform) { ; Iteration of polynomial(z) = root by Newton's method. ; A Transform version of JLB_PolyNewtonContraction public: func JLB_NewtonPolyUserTransform(Generic pparent) UserTransform.UserTransform(pparent) if (@p_poly_type == "Poly") m_a[0] = @p_a0, m_a[1] = @p_a1, m_a[2] = @p_a2, m_a[3] = @p_a3 m_a[4] = @p_a4, m_a[5] = @p_a5, m_a[6] = @p_a6, m_a[7] = @p_a7 m_a[8] = @p_a8, m_a[9] = @p_a9, m_a[10] = @p_a10 int i = 0 while (i < @p_degree - 1) m_ap[i] = (i+1)*m_a[i+1] m_app[i] = (i+2)*(i+1)*m_a[i+2] i = i + 1 endwhile i = @p_degree - 1 m_ap[i] = (i+1)*m_a[i+1] endif endfunc ; @param pz ; @return the new pz complex func Iterate(complex pz) complex f complex fp complex fpp = 0 ; to satisfy compiler int i if (@p_poly_type == "Poly") ; Solving f(z) = 0 i = @p_degree f = m_a[i] while (i > 0) i = i - 1 f = m_a[i] + pz * f endwhile ; df/dz i = @p_degree - 1 fp = m_ap[i] while (i > 0) i = i - 1 fp = m_ap[i] + pz * fp endwhile if (@p_iter_type > 0) ; d2f/dz2 i = @p_degree - 2 fpp = m_app[i] while (i > 0) i = i - 1 fpp = m_app[i] + pz * fpp endwhile endif else;if (@p_poly_type == "Power") f = pz^@p_power fp = @p_power*pz^(@p_power-1) if (@p_iter_type > 0) fpp = @p_power*(@p_power-1)*pz^(@p_power-2) endif endif if (@p_type == "Constant") f = f - @p_const ; elseif (@p_type == "C") ; f = f - pc else;if (@p_type == "Pixel") f = f - #pixel endif if (@p_iter_type == "Newton") return pz - @relax * f / fp elseif (@p_iter_type == "Type 1") return pz - @relax * (f/fp)*(1 + @p_alpha*f*fpp/(2*fp*fp)) elseif (@p_iter_type == "Type 2") return pz - @relax * 2*(f/fp) / ( 2 - @p_alpha*f*fpp/(fp*fp)) else;if (@p_iter_type == "Type 3") complex root = sqrt(fp*fp-@p_alpha*2*f*fpp) complex d1 = fp + root complex d2 = fp - root if (|d1| >= |d2|) return pz - @relax * 2*f / d1 else return pz - @relax * 2*f / d2 endif endif endfunc protected: complex m_a[11] ; coefficients of polynomial complex m_ap[10] ; coefficients of first derivative of polynomial complex m_app[10] ; coefficients of second derivative of polynomial default: rating = notRecommended title = "Newton Poly" int param v_NewtonPolyUserTransform caption = "Version (NewtonPoly_UserTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_NewtonPolyUserTransform < 100 endparam param p_poly_type caption = "Polynomial type" enum = "Poly" "Power" default = 1 hint = "Poly: polynomial has real powers, degree 10 or less. \ Power: polynomial is a single power, real or complex." endparam int param p_degree caption = "Highest power" default = 3 min = 2 max = 10 visible = (@p_poly_type == 0) endparam complex param p_a0 caption = "Constant coefficient" default = (0,0) visible = (@p_poly_type == 0) endparam complex param p_a1 caption = "Degree 1 coefficient" default = (0,0) visible = (@p_poly_type == 0) endparam complex param p_a2 caption = "Degree 2 coefficient" default = (0,0) visible = (@p_poly_type == 0) endparam complex param p_a3 caption = "Degree 3 coefficient" default = (1,0) visible = (@p_degree >= 3) && (@p_poly_type == 0) endparam complex param p_a4 caption = "Degree 4 coefficient" default = (0,0) visible = (@p_degree >= 4) && (@p_poly_type == 0) endparam complex param p_a5 caption = "Degree 5 coefficient" default = (0,0) visible = (@p_degree >= 5) && (@p_poly_type == 0) endparam complex param p_a6 caption = "Degree 6 coefficient" default = (0,0) visible = (@p_degree >= 6) && (@p_poly_type == 0) endparam complex param p_a7 caption = "Degree 7 coefficient" default = (0,0) visible = (@p_degree >= 7) && (@p_poly_type == 0) endparam complex param p_a8 caption = "Degree 8 coefficient" default = (0,0) visible = (@p_degree >= 8) && (@p_poly_type == 0) endparam complex param p_a9 caption = "Degree 9 coefficient" default = (0,0) visible = (@p_degree >= 9) && (@p_poly_type == 0) endparam complex param p_a10 caption = "Degree 10 coefficient" default = (0,0) visible = (@p_degree >= 10) && (@p_poly_type == 0) endparam complex param p_power caption = "Power" default = (3,0) visible = (@p_poly_type == 1) endparam param p_type caption = "cc" ; enum = "Constant" "C" "Pixel" enum = "Constant" "Pixel" default = 0 hint = "Iteration is one step in solving polynomial(z)-cc = 0. Try Pixel \ with Mandelbrot types, Constant with Julia types." endparam complex param p_const caption = "Constant" default = (1,0) visible = (@p_type == 0) endparam param p_iter_type caption = "Iteration type" enum = "Newton" "Type 1" "Type 2" "Type 3" default = 0 hint = "Newton is the usual, using only the first derivative. \ The other types also use the second derivative. Type 1 is Householder \ iteration if alpha = 1. Type 2 is Halley iteration if alpha = 2 and \ Schroder iteration if alpha = 1. Type 3 is Halley's irrational iteration \ if alpha = 1." endparam complex param p_alpha caption = "alpha" default = (1,0) hint = "Nonstandard values of alpha may not allow convergence, but can give \ interesting shapes." visible = (@p_iter_type > 0) endparam param relax caption = "Relaxation" default = (1,0) hint = "The standard value is 1. Nonstandard values may not allow \ convergence, but can give interesting shapes." endparam } class JLB_ContractionDualTransform(jlb.ulb:DualTransform) { ; Wrapper for a Contraction to plug into a DualTransform slot public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_ContractionDualTransform(Generic pparent) DualTransform.DualTransform(pparent) m_Contraction = new @f_Contraction(this) endfunc ; @param pz ; @param pc ; @return true if okay; possibly change both arguments bool func Iterate(complex &pz, complex &pc) pz = m_Contraction.Iterate(pz, pc) return true endfunc protected: Contraction m_Contraction default: rating = notRecommended title = "ContractionDualTransform" int param v_ContractionDualTransform caption = "Version (ContractionDualTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_ContractionDualTransform < 100 endparam Contraction param f_Contraction caption = "Contraction" default = JLB_MandelbrotContraction ; hint = "The default Null Contraction does nothing." endparam } class JLB_UserTransformDualTransform(jlb.ulb:DualTransform) { ; Wrapper for a UserTransform to plug into a DualTransform slot public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_UserTransformDualTransform(Generic pparent) DualTransform.DualTransform(pparent) m_UserTransform = new @f_UserTransform(this) endfunc ; @param pz ; @param pc ; @return true if okay; possibly change both arguments bool func Iterate(complex &pz, complex &pc) pz = m_UserTransform.Iterate(pz) + @c*pc return true endfunc protected: UserTransform m_UserTransform default: rating = notRecommended title = "UserTransformDualTransform" int param v_UserTransformDualTransform caption = "Version (UserTransformDualTransform)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_UserTransformDualTransform < 100 endparam UserTransform param f_UserTransform caption = "Transform" default = JLB_PowerUserTransform ; hint = "The default Null Contraction does nothing." endparam complex param c caption = "Const" default = 1 hint = "New z = transform(z) + const * c" endparam } class JLB_ShapesDirect2(common.ulb:DirectColoring) { ; version 100 of 03 November 2012 ; This is a direct coloring formula. ; ; Superpose up to 500 annulus shapes with optional texture. The coloring ; can be blended into a background color. Overlapping shapes get averaged. ; ; Best with Linear Transfer Function. With Linear and Color Density = 1, the ; gradient is used exactly once around the annulus. public: import "common.ulb" import "dmj5.ulb" ; texture class ; constructor func JLB_ShapesDirect2(Generic pparent) DirectColoring.DirectColoring(pparent) m_distortion = new @fdistortion(this) do_distort = (@fdistortion != DMJ_TrapShapeFlat) && (@pDistSize != 0.0) m_texture = new @ftexture(this) do_texture = (@ftexture != DMJ_TrapShapeFlat) && (@pTexSize != 0.0) m_gb = new GammaBlend( ) r = new JLB_Random(0) r.Init(@seed) if (@two_D) nshapes = @n2_1*@n2_2 if (nshapes > 500) nshapes = 500 endif else nshapes = @num endif bool keep_aspect = @aspect || (@shape_type == "Circle") || (@shape_type == "Square") float x_size float y_size float x_spread float y_spread if (@shape_type == "Circle") x_size = y_size = @radius x_spread = y_spread = @radius_spread elseif (@shape_type == "Triangle") x_size = @t_w y_size = 1.0 x_spread = @tx_spread0 y_spread = @ty_spread0 else x_size = @x_size0/2 ; @x_size0 is side length, x_size is half-length x_spread = @x_spread0 if (@shape_type == "Square") y_size = @x_size0/2 y_spread = x_spread else y_size = @y_size0/2 y_spread = @y_spread0 endif endif if (keep_aspect) y_spread = x_spread * y_size / x_size endif back_out = rgb(1,1,1) ; for quieting the compiler back_ins = rgb(1,1,1) if (@blend_type == "Blend outside edge") back_out = @bkg_out elseif (@blend_type == "Blend inside edge") back_ins = @bkg_ins elseif (@blend_type == "Blend both edges") back_ins = @bkg_ins back_out = @bkg_out endif ; generate the shapes float ctr_rad = cabs(@center) float ctr_ang = atan2(@center) int i = 0 int i2_1 = 0 int i2_2 = 0 while (i < nshapes) if (@two_D) ctr[i] = @center + i2_1*@off2_1 + i2_2*@off2_2 if (@ctr_chg_type == "Random") ctr[i] = ctr[i] + @center_spread*((r.RandomFloat(0)-0.5) + flip(r.RandomFloat(0)-0.5)) endif i2_1 = i2_1 + 1 if (i2_1 == @n2_1) i2_1 = 0 i2_2 = i2_2 + 1 endif else ; not 2D ; centers and angles w.r.t. the center of each shape if (@ctr_chg_type == "Random") ctr[i] = @center + @center_spread*((r.RandomFloat(0)-0.5) + flip(r.RandomFloat(0)-0.5)) else ;if (@ctr_chg_type == "Incremental") float p = ctr_rad*(@ctr_rad_mult^i) float a = ctr_ang - i*@ctr_ang_add*#pi/180 ctr[i] = p*exp(flip(a)) endif endif ; x and y sizes of each shape if (@rad_chg_type == "Random") x_rad[i] = x_size + x_spread * (r.RandomFloat(0)-0.5) if keep_aspect y_rad[i] = x_rad[i] * y_size/x_size else y_rad[i] = y_size + y_spread * (r.RandomFloat(0)-0.5) endif else ;if (@rad_chg_type == "Incremental") if (@rad_chg_type2 == "Add") x_rad[i] = x_size + (x_spread * i) y_rad[i] = y_size + (y_spread * i) else ;if (@rad_chg_type2 == "Multiply") x_rad[i] = x_size * (x_spread ^ i) y_rad[i] = y_size * (y_spread ^ i) endif if keep_aspect y_rad[i] = x_rad[i] * y_size/x_size endif endif ; thickness of each shape float thk if (@thick_chg_type == "Random") thk = @thick + @thick_spread * (r.RandomFloat(0)-0.5) else ;if (@thick_chg_type == "Incremental") if (@thick_chg_type2 == "Add") thk = @thick + (@thick_spread * i) else ;if (@thick_chg_type2 == "Multiply") thk = @thick * (@thick_spread ^ i) endif endif if (thk < 0.0) thk = 0.0 endif if (@shape_type == "Ellipse") delta1[i] = thk/(0.5*(x_rad[i]+y_rad[i])) delta2[i] = delta1[i] elseif (@shape_type == "Triangle") delta1[i] = thk/2 ; max delta = radius of circumscribed circle float dmax = Geometry.DistanceToCircumCenter(abs(x_rad[i]), \ cabs(@t_voff*y_rad[i]), cabs(@t_voff*y_rad[i] - x_rad[i])) if (delta1[i] > dmax) ; radius of inscribed circle delta1[i] = dmax endif delta2[i] = 1.0 ; vertices ccw if (x_rad[i]*imag(@t_voff)*y_rad[i] < 0) ; vertices cw delta2[i] = -1.0 endif else delta1[i] = thk/x_rad[i] delta2[i] = thk/y_rad[i] endif if (@shape_type != "Triangle") ;Avoid some weird behavior if (delta1[i] > 1.0) delta1[i] = 1.0 endif if (delta2[i] > 1.0) delta2[i] = 1.0 endif endif ; orientation angle for each ahape if (@ang_chg_type == "Random") angle[i] = @ang + @ang_spread * (r.RandomFloat(0)-0.5) else ;if @ang_chg_type == "Incremental" angle[i] = @ang - @ang_spread * i endif angle[i] = angle[i] * #pi/180 ; gradient offset for each shape if (@grad_chg_type == "Random") grad_off[i] = @grad_offset + @grad_offset_spread* (r.RandomFloat(0)-0.5) else ;if (@grad_chg_type == "Icremental") grad_off[i] = @grad_offset + @grad_offset_spread * i endif while (grad_off[i] > 1.0) grad_off[i] = grad_off[i] - 1.0 endwhile while (grad_off[i] < 0.0) grad_off[i] = grad_off[i] + 1.0 endwhile i = i + 1 endwhile endfunc ; initialize the objects func Init(complex pz, complex ppixel) DirectColoring.Init(pz, ppixel) m_distortion.Init(pz) m_texture.Init(pz) m_gb.SetGamma(@gamma) ; default is 2.2 endfunc ; call for each iterated point func Iterate(complex pz) DirectColoring.Iterate(pz) endfunc ; override the parent and call in the final section of the coloring formula.
color func Result(complex pz) float idx int count = 0 int i = 0 color color_list[500] complex z = #pixel if (do_distort) z = z + m_distortion.Iterate(z) * @pDistSize z = z + flip(m_distortion.Iterate(z) * @pDistSize) endif while (i < nshapes) bool hit = false ; get rotated relative coordinates complex rot_ctr = ctr[i] if (@shape_type == "Triangle") ; rotate about triangle "center" rot_ctr = rot_ctr + (x_rad[i] + @t_voff*y_rad[i])/3 endif float x = real(z)-real(rot_ctr) float y = imag(z)-imag(rot_ctr) float c = cos(angle[i]) float s = sin(angle[i]) float xr = x*c-y*s float yr = x*s+y*c complex zr = rot_ctr + xr + flip(yr) ;; 9/28/12 !! ; Scale to size float dx = xr / x_rad[i] float dy = yr / y_rad[i] float d1 float d2 float d3 float dist = 1 ; Make dist = 0 at the center of the annulus, +1 or -1 at the edges. if (@shape_type == "Circle" || @shape_type == "Ellipse") dist= (abs(dx))^@p + (abs(dy))^@p dist = (dist - 1.0)/delta1[i] ;dist = abs(dist)/delta1[i] hit = abs(dist) <= 1.0 elseif (@shape_type == "Square" || @shape_type == "Rectangle") ; Have to look at all sides. Opposites are identical, though. d1 = (abs(dx) - 1.0) / delta1[i] d2 = (abs(dy) - 1.0) / delta2[i] if (abs(d1) <= 1.0 && abs(dy) <= 1.0+delta2[i]) hit = true dist = d1 endif if (abs(d2) <= 1.0 && abs(dx) <= 1.0+delta1[i]) if (hit) ; corners are tricky if (d2 > d1) dist = d2 endif else hit = true dist = d2 endif endif else ;if (@shape_type == "Triangle") complex v0 = ctr[i] ; lower left corner complex v1 = v0 + x_rad[i] complex v2 = v0 + @t_voff*y_rad[i] float del2 = cabs(v2-v0) ; variables in derivation float del3 = cabs(v2-v1) float a = real(@t_voff)*y_rad[i] float b = imag(@t_voff)*y_rad[i] float w = x_rad[i] float del = delta1[i] ; It's a hit if zr is within the outer annulus and outside the inner annulus complex dv0 complex dv1 complex dv2 if (imag(@t_voff) > 0.0) dv0 = del*((delta2[i]*del2+a)/b+(0,1)) dv1 = del*(-(delta2[i]*del3+w-a)/b+(0,1)) dv2 = del*(delta2[i]*((w-a)*del2-a*del3)/(b*w)-flip(delta2[i]*(del2+del3)/w)) else dv0 = del*((delta2[i]*del2-a)/b-(0,1)) dv1 = del*((-delta2[i]*del3+w-a)/b-(0,1)) dv2 = del*(delta2[i]*((w-a)*del2-a*del3)/(b*w)-flip(delta2[i]*(del2+del3)/w)) endif bool outer = Geometry.PointInTriangle(zr, v0-dv0, v1-dv1, v2-dv2) bool inner = Geometry.PointInTriangle(zr, v0+dv0, v1+dv1, v2+dv2) hit = outer && !inner if (hit) xr = real(zr-ctr[i]) yr = imag(zr-ctr[i]) if (imag(@t_voff) > 0.0) d1 = -yr/del d2 = -delta2[i]*(b*xr-a*yr)/del2/del d3 = delta2[i]*(b*xr+(w-a)*yr-b*w)/del3/del else d1 = yr/del d2 = -delta2[i]*(b*xr-a*yr)/del2/del d3 = delta2[i]*(b*xr+(w-a)*yr-b*w)/del3/del endif bool inside = Geometry.PointInTriangle(zr, v0, v1, v2) if (inside) ; dist should be negative dist = -1 if (d1 <= 0 && d1 >= -1) dist = d1 endif if (d2 <= 0 && d2 >= -1 && d2 > dist) dist = d2 endif if (d3 <= 0 && d3 >= -1 && d3 > dist) dist = d3 endif else ; outside, dist should be positive dist = -1 if (d1 >= 0 && d1 <= 1) dist = d1 endif if (d2 >= 0 && d2 <= 1 && d2 > dist) dist = d2 endif if (d3 >= 0 && d3 <= 1 && d3 > dist) dist = d3 endif endif endif endif ; @shape_type if (hit) if (@uniform_grad) idx = grad_off[i] else complex gctr = ctr[i] if (@shape_type == "Triangle") gctr = gctr + (x_rad[i] + @t_voff*y_rad[i])/3 endif float theta = atan2(z-gctr)/(2*#pi) ; gradient scale is 0 to 1. if (theta < 0.0) theta = theta + 1.0 endif idx = grad_off[i] + theta endif float dt = 0.0 if (do_texture) dt = m_Texture.Iterate(z) * @pTexSize endif idx = idx + dt color new_color = gradient(idx - trunc(idx)) float t = dist ; -1<=t<=1 color cb if (t >= 0) if (@blend_type == "Blend outside edge" || @blend_type == "Blend both edges") cb = back_out if (@keep_hue_out) cb = hsla(hue(new_color), sat(cb), lum(cb), alpha(cb)) endif ; if @v_ShapesDirect2 < 220806 new_color = blend(new_color, cb, t^@pb_out) ; else ; new_color = m_gb.Blend2(new_color, 1-t^@pb_out, cb, t^@pb_out) ; endif endif else ;if (t < 0) if (@blend_type == "Blend inside edge" || @blend_type == "Blend both edges") cb = back_ins if (@keep_hue_ins) cb = hsla(hue(new_color), sat(cb), lum(cb), alpha(cb)) endif t = -t ; if @v_ShapesDirect2 < 220806 new_color = blend(new_color, cb, t^@pb_ins) ; else ; new_color = m_gb.Blend2(new_color, 1-t^@pb_ins, cb, t^@pb_ins) ; endif endif endif color_list[count] = new_color count = count + 1 endif i = i + 1 endwhile m_solid = (count == 0) ; parameter in Coloring class color return_color = rgb(0,0,0) if (count > 0) int ntot = count if ((@overlap == "First N" || @overlap == "Last N") && @N_over < count) ntot = @N_over endif int n1, int n2 if (@overlap == "First N" || @overlap == "Average All") n1 = 0 n2 = ntot - 1 else ;if if (@overlap == "Last N") n1 = count - ntot n2 = count - 1 endif if @v_ShapesDirect2 < 220806 while (n1 <= n2) return_color = blend(return_color, color_list[n1], 1.0/ntot) n1 = n1 + 1 endwhile else m_gb.Zero() ; if n1 == n2 ; m_gb.SetGamma(1) ; endif while (n1 <= n2) m_gb.AddOne(color_list[n1], 1.0/ntot) n1 = n1 + 1 endwhile return_color = m_gb.final() endif endif return return_color endfunc protected: TrapShape m_distortion TrapShape m_texture JLB_Random r GammaBlend m_gb ; For each shape complex ctr[500] ; center float x_rad[500] ; sizes float y_rad[500] float delta1[500] ; Scaled thickness float delta2[500] float grad_off[500] ; Offset of the gradient float angle[500] ; Orientation angle int nshapes color back_out color back_ins bool do_distort bool do_texture default: title = "Shapes Direct2" int param v_ShapesDirect2 caption = "Version (ShapesDirect2)" default = 220806 hint = "This version parameter is used to detect when a change has been made to the formula that is incompatible with the previous version. When that happens, this field will reflect the old version number to alert you to the fact that an alternate rendering is being used." visible = @v_ShapesDirect2 < 220806 endparam ; general parameters ; heading text="This coloring ignores the formula, so use a Pixel formula." endheading param shape_type caption = "Shape" enum = "Circle" "Ellipse" "Square" "Rectangle" "Triangle" default = 0 endparam bool param two_D caption = "Two-D array?" default = false endparam float param p caption = "Power" default = 2.0 min = 0.0 hint = "Use 2 for standard circles or ellipses" visible = (@shape_type < 2) endparam int param num caption = "Number of shapes" min = 1 max = 500 default = 10 visible = !@two_D endparam complex param off2_1 caption = "First offset" default = (1,0) visible = @two_D endparam complex param off2_2 caption = "Second offset" default = (0,1) visible = @two_D endparam int param n2_1 caption = "# along offset 1" default = 4 min = 1 visible = @two_D endparam int param n2_2 caption = "# along offset 2" default = 4 min = 1 visible = @two_D endparam heading caption = "Initial Point and its movement" endheading complex param center caption = "Initial point" default = (0,0) hint = "Center of first shape, except ower left corner for triangles" endparam param ctr_chg_type caption = "Point change" enum = "Random" "Incremental" default = 0 hint = "How to move the centers of subsequent shapes" visible = !@two_D endparam float param center_spread caption = "Random movement" min = 0.0 default = 0.1 visible = (@ctr_chg_type == 0) hint = "Maximum movement of X and Y components of the shape center" endparam ; param ctr_incr_type ; caption = "Center change type?" ; ebum = "Add" "Multipl ; default = 0 ; visible = (@ctr_chg_type == 1) ; endparam float param ctr_rad_mult caption = "Ctr. radius multiply" min = 0.0 default = 1.1 visible = (@ctr_chg_type == 1) endparam float param ctr_ang_add caption = "Ctr. angle add (degrees)" default = 15 visible = (@ctr_chg_type == 1) endparam heading caption = "Shape parameters" endheading float param radius caption = "Initial radius" min = 0.0 default = 1.0 hint = "Radius of first circle" visible = (@shape_type==0) endparam float param x_size0 caption = "Initial x_size" min = 0.0 default = 1.0 visible = (@shape_type > 0 && @shape_type < 4) hint = "X semi-axis for ellipses, X side length for squares and rectangles" endparam float param y_size0 caption = "Initial y_size" min = 0.0 default = 0.75 visible = (@shape_type == 1 || @shape_type == 3) hint = "Y semi-axis for ellipses, Y side length for rectangles" endparam float param t_w caption = "Triangle horiz. leg" ; min = 0.0 default = 1.0 visible = (@shape_type == 4) endparam complex param t_voff caption = "Triangle 3rd vertex" hint = "Offset from lower left corner" default = (0.0, 1.0) visible = (@shape_type == 4) endparam param rad_chg_type caption = "Size change" enum = "Random" "Incremental" default = 0 hint = "How to change the size of subsequent shapes" endparam param rad_chg_type2 caption = "Size change type" enum = "Add" "Multipy" default = 0 visible = (@rad_chg_type == 1) endparam float param radius_spread caption = "Variation of radius" min = 0.0 default = 0.1 visible = (@shape_type == 0) hint = "Maximum change of x_size and y_size for subsequent shapes" endparam float param x_spread0 caption = "Change of x_size" min = 0.0 default = 0.5 visible = (@shape_type > 0 && @shape_type < 4) ; hint = "X semi-axis for ellipses, X side length for squares and rectangles" endparam float param y_spread0 caption = "Change of y_size" min = 0.0 default = 0.1 visible = (@shape_type == 1 || @shape_type == 3) && !@aspect ; hint = "Y semi-axis for ellipses, Y side length for rectangles" endparam float param tx_spread0 caption = "Change of bottom" ; min = 0.0 default = 0.5 visible = (@shape_type == 4) endparam float param ty_spread0 caption = "Change of top" ; min = 0.0 default = 0.1 visible = (@shape_type == 4) && !@aspect endparam bool param aspect caption = "Keep aspect ratio?" default = true visible = (@shape_type !=0 && @shape_type !=2) hint = "Keep the ratio of x_size to y_size constant? " endparam heading caption = "Rotation angle and its changing" endheading float param ang caption = "Initial rotation angle" default = 0.0 hint = "Rotation angle if first shape" visible = (@shape_type > 0) || (@p != 2.0) endparam param ang_chg_type caption = "Angle change" enum = "Random" "Incremental" default = 0 hint = "How to change the rotation angle of subsequent shapes" visible = (@shape_type > 0) || (@p != 2.0) endparam float param ang_spread caption = "Variation of angle" ;min = 0.0 default = 45 hint = "Maximum degrees to add for each subsequent shape" ;visible = (@shape_type > 0) endparam heading caption = "Thickness of annulus and its changing" endheading float param thick caption="Initial thickness" default=0.1 hint = "Thickness of initial shape's annulus" min = 0 endparam param thick_chg_type caption = "Thickness change" enum = "Random" "Incremental" hint = "How to change the thickness of subsequent shapes" default = 0 endparam param thick_chg_type2 caption = "Thickness change type" enum = "Add" "Multipy" default = 0 visible = (@thick_chg_type == 1) endparam float param thick_spread caption = "Thickness change" hint = "Amount to add or factor to multiply by" default = 0.1 endparam ; float param tp ; caption = "Thickness power" ; visible = (@shape_type <= 1) ; min = 0 ; default = 1.0 ; endparam heading caption = "Gradient offset and its changing" endheading bool param uniform_grad caption = "Uniform shape coloring?" default = false hint = "Shape coloring uniform around the shape?" endparam float param grad_offset caption="Initial gradient offset" min = 0.0 max = 1.0 default=0 hint = "Offset of the gradient of the first shape. \ With Color Density = 1 and Transfer Function = Linear, the gradient goes \ around the annulus exactly once, for total offset = 1." endparam param grad_chg_type caption = "Offset change" enum = "Random" "Incremental" hint = "How to change the gradient offset" default = 0 endparam float param grad_offset_spread caption = "Variation of offset" hint = "Amount of offset change" default = 0.1 endparam heading Caption = "Edge blending" endheading param blend_type caption = "Blend type" enum = "Flat" "Blend outside edge" "Blend inside edge" "Blend both edges" hint = "Make the edges of the annulas blend into a background color?" default = 3 endparam float param pb_out caption = "Outside blend power" default = 2.0 min = 0.0 visible = (@blend_type == 1 || @blend_type == 3) ; hint = "Large numbers give flatter blending" endparam bool param keep_hue_out caption = "Keep hue?" default = false hint = "Use hue of annulus with saturation, luminosity, and opacity of \ outside blend color" visible = (@blend_type == 1 || @blend_type == 3) endparam color param bkg_out caption = "Outside blend color" default = rgb(1,1,1) ; white visible = (@blend_type == 1 || @blend_type == 3) hint = "Color to blend the outside edge into" endparam float param pb_ins caption = "Inside blend power" default = 2.0 min = 0.0 visible = (@blend_type == 2 || @blend_type == 3) ; hint = "Large numbers give flatter blending" endparam bool param keep_hue_ins caption = "Keep hue?" default = false hint = "Use hue of annulus with saturation, luminosity, and opacity of \ inside blend color" visible = (@blend_type == 2 || @blend_type == 3) endparam color param bkg_ins caption = "Inside blend color" default = rgb(1,1,1) ; white visible = (@blend_type == 2 || @blend_type == 3) hint = "Color to blend the inside edge into" endparam heading caption = "Overlap coloring" endheading param overlap caption = "Overlap coloring" enum = "First N" "Average All" "Last N" default = 1 hint = "How to color pixels if shapes overlap" endparam int param N_over caption = "N" default = 1 min = 1 visible = (@overlap == 0 || @overlap == 2) hint = "How many shapes contribute to coloring" endparam float param gamma caption = "gamma for blending" default = 2.2 endparam heading caption = "Texture" endheading TrapShape param ftexture caption = "Texture" default = DMJ_TrapShapeFlat hint = "TrapShape plugins can be used to add texture" endparam float param pTexSize caption = "Texture Strength" default = 0.1 visible = @ftexture != DMJ_TrapShapeFlat endparam heading caption = "Shape distortion" endheading TrapShape param fdistortion caption = "Distortion" default = DMJ_TrapShapeFlat hint = "TrapShape plugins can be used to distort the shapes." endparam float param pDistSize caption = "Distortion Strength" default = 0.1 visible = @fdistortion != DMJ_TrapShapeFlat endparam heading caption = "Random number seed" visible = (@ctr_chg_type==0 || @rad_chg_type==0 || @ang_chg_type==0) endheading int param seed caption = "Seed" default = 12345 visible = (@ctr_chg_type==0 || @rad_chg_type==0 || @ang_chg_type==0 || \ @grad_chg_type==0) endparam } class Geometry { ; Perhaps the start of a general Geometry class. ; Original version 03 November 2012 ; Latest change 14 March 2023 static bool func PointInTriangle(complex z, complex v0, complex v1, complex v2) ; Is point z strictly inside the triangle specified by three vertices? ; (For allowing points on the triangle itself, change the > to >= and < to <=.) ; Usually with roundoff there is no difference. ; ; Method and notation from mathworld.wolfram.com/TriangleInterior.html complex v = z v1 = v1 - v0 v2 = v2 - v0 float v1v2 = real(v1)*imag(v2)-imag(v1)*real(v2) float vv1 = real(v )*imag(v1)-imag(v )*real(v1) float vv2 = real(v )*imag(v2)-imag(v )*real(v2) float v0v1 = real(v0)*imag(v1)-imag(v0)*real(v1) float v0v2 = real(v0)*imag(v2)-imag(v0)*real(v2) float a = (vv2-v0v2)/v1v2 float b = -(vv1-v0v1)/v1v2 return (a >= 0 && b >= 0 && a+b <= 1) endfunc static float func DistanceToCircumCenter(float s1, float s2, float s3) ; Distance to meeting point of the three angle bisectors, ; given the side lengths. Equal to radius of circumcircle. float k = (s1 + s2 + s3)/2 return sqrt((k-s1)*(k-s2)*(k-s3)/k) endfunc ; next three functions added October 2020 static float func Det(complex v1, complex v2) ; det is actually the cross product of the two points considered as vectors return real(v1)*imag(v2)-imag(v1)*real(v2) endfunc static float func DistancePointToLine(complex p, complex v1, complex v2) ; distance from point p to the infinite line determined by two end points ; formula from mathworld.wolfram.com/Point-LineDistance2-Dimensional.html if v1 == v2 return cabs(p - v1) endif return abs(Det(v2-v1, v1-p))/cabs(v2-v1) endfunc ; this one added 230416 static float func DistancePointToLine2(complex pt, complex pv, float angle) ; distance from point p to the infinite line determined by point pv and angle ; formula from https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line return abs(cos(angle)*imag(pv-pt) - sin(angle)*real(pv-pt)) endfunc static float func TriangleArea(complex v0, complex v1, complex v2) ; formula from mathworld.wolfram.com/TriangleArea.html return 0.5*abs(Det(v1-v0,v2-v0)) endfunc ; longest side of a triangle static int func LongestSide(complex v0, complex v1, complex v2) float d1 = |v0-v1| float d2 = |v1-v2| float d3 = |v2-v0| if d1 >= d2 && d1 >= d3 return 1 elseif d2 >= d3 && d2 >= d1 return 2 else return 3 endif endfunc ; next two added 17 October 2021 ; formula for t from http://paulbourke.net/geometry/pointlineplane/ static float func DistancePointToLIneSegment(complex p, complex v1, complex v2) if v1 == v2 return cabs(p - v1) endif ; the perpendicular from p to the segment is at v1 + t * (v2-v1) float t = ( (real(p)-real(v1))*(real(v2)-real(v1)) + (imag(p)-imag(v1))*(imag(v2)-imag(v1)) ) \ / | v2 - v1 | if t >= 0 && t <= 1 ; the perpendicular hits the segment return cabs(p-(v1+t*(v2-v1)) ) ; or as above in DistancePointToLine ;return abs(Det(v2-v1, v1-p))/cabs(v2-v1) else ; it's one of the endpoints float d1 = cabs(p-v1) float d2 = cabs(p-v2) if d1 <= d2 return d1 else return d2 endif endif endfunc ; find distance from a point to a triangle or quadrilateral static float func DistancePointToFigure(complex pt, int n, complex v1, complex v2, complex v3, complex v4) if n < 3 || n > 4 return -1 endif float d1, float d2, float d3, float d4 d1 = Geometry.DistancePointToLIneSegment(pt, v1, v2) d2 = Geometry.DistancePointToLIneSegment(pt, v2, v3) if n == 3 d3 = Geometry.DistancePointToLIneSegment(pt, v3, v1) d4 = d3 else d3 = Geometry.DistancePointToLIneSegment(pt, v3, v4) d4 = Geometry.DistancePointToLIneSegment(pt, v4, v1) endif if d2 < d1, d1 = d2, endif if d3 < d1, d1 = d3, endif if d4 < d1, d1 = d4, endif return d1 endfunc ; formula for t from http://paulbourke.net/geometry/pointlineplane/ ; 220620 return vector from closest point on a line segment to p static complex func ClosestPointToLineSegment(complex p, complex v1, complex v2) if v1 == v2 return p-v1 endif ; the perpendicular from p to the segment is at v1 + t * (v2-v1) float t = ( (real(p)-real(v1))*(real(v2)-real(v1)) + (imag(p)-imag(v1))*(imag(v2)-imag(v1)) ) \ / | v2 - v1 | if t >= 0 && t <= 1 ; the perpendicular hits the segment return p-(v1+t*(v2-v1)) else ; it's one of the endpoints float d1 = |p-v1| float d2 = |p-v2| if d1 <= d2 return p-v1 else return p-v2 endif endif endfunc ; 220620 return vector from from closest point on a triangle or quadrilateral to pt ; if quadrilateral, the vertices are assumed to be in clockwise or counterclockwise order. static complex func ClosestPointToFigure(complex pt, int n, \ complex v1, complex v2, complex v3, complex v4) complex pv = (0,0) if n < 3 || n > 4 return pv endif complex pv1, complex pv2, complex pv3, complex pv4 pv1 = Geometry.ClosestPointToLIneSegment(pt, v1, v2) pv2 = Geometry.ClosestPointToLIneSegment(pt, v2, v3) if n == 3 pv3 = Geometry.ClosestPointToLIneSegment(pt, v3, v1) pv4 = pv3 else pv3 = Geometry.ClosestPointToLIneSegment(pt, v3, v4) pv4 = Geometry.ClosestPointToLIneSegment(pt, v4, v1) endif float d1 = |pv1|, float d2 = |pv2|, float d3 = |pv3|, float d4 = |pv4| float d = d1, pv = pv1 if d2 < d, d = d2, pv = pv2, endif if d3 < d, d = d3, pv = pv3, endif if d4 < d, pv = pv4, endif return pv endfunc ; 220923 return vector from from closest vertex on a triangle or quadrilateral to pt static complex func ClosestPointToVertex(complex pt, int n, \ complex v1, complex v2, complex v3, complex v4) complex pv = (0,0) if n < 3 || n > 4 return pv endif complex pv1, complex pv2, complex pv3, complex pv4 pv1 = pt - v1, pv2 = pt - v2, pv3 = pt - v3, pv4 = pt - v4 float d1 = |pv1|, float d2 = |pv2|, float d3 = |pv3|, float d4 = |pv4| float d = d1, pv = pv1 if d2 < d, d = d2, pv = pv2, endif if d3 < d, d = d3, pv = pv3, endif if n == 4 && d4 < d, pv = pv4, endif return pv endfunc ; 230314 calculate Barycentric Coordinates of a point in a triangle ; Does not check to see if the point actually is inside the triangle ; Formulas as in https://en.wikipedia.org/wiki/Barycentric_coordinate_system static func BarycentricCoordinates(complex pt, complex v1, complex v2, complex v3, \ float &b1, float &b2, float &b3) float x1 = real(v1), float y1 = imag(v1) float x2 = real(v2), float y2 = imag(v2) float x3 = real(v3), float y3 = imag(v3) float xp = real(pt), float yp = imag(pt) float det = (y2-y3)*(x1-x3) + (x3-x2)*(y1-y3) b1 = ( (y2-y3)*(xp-x3) + (x3-x2)*(yp-y3) )/det b2 = ( (y3-y1)*(xp-x3) + (x1-x3)*(yp-y3) )/det b3 = 1 - b1 - b2 endfunc } class JLB_Random(common.ulb:Generic) { ; This class provides a robust pseudo-random number generator for UF.

; The basic generator produces a random sequence of integers in the range ; from 0 to 2147483647, inclusive. Note that 2147483647 is the largest ; positive integer in UF.
; ; To use, first call Init(seed) with seed an odd positive integer. This ; initializes the sequence. If the call to init is omitted, the default seed ; (1245) is used. ; Then i = RandomInt(0) provides successive numbers in the sequence. ; Also, i = RandomInt(j) with j non-zero will first reset the sequence ; using j as the new seed.

; To produce a random integer in [1, N], inclusive, use RandomIntInRange, ; which uses RandomInt(). First set N with SetRange(). If the call to ; SetRange is omitted, the default N (16) is used.

; ; To produce a random float in [0, 1), use RandomFloat(), which uses ; RandomInt(). It works by dividing the integer returned by RandomInt() ; by 2147483648.0 (which is a float equal to 2^31). The value ; 0 can be obtained, but not the value 1. This is sometimes useful. <> ; ; The random number generator is robust, probably the simplest robust ; generator. It uses the same algorithm as a routine in the NIST math ; library.
; ; Original version: br> ; Blue, James L., Applied and Computational Mathematics Division, NIST
; Kahaner, David K., Applied and Computational Mathematics Division, NIST
; Marsaglia, George, Florida State University
; ; Reference: Marsaglia G., "a Current View of Random Number Generators," ; Proceedings Computer Science and Statistics: 16th ; Symposium on the Interface, Elsevier, Amsterdam, 1985. ; public: import "common.ulb" ; Constructor func JLB_Random(Generic pparent) Generic.Generic(pparent) ; ; Init with default seed. ; Init(@p_seed) ; ; Set default range maximum ; m_Nmax = 16 endfunc ; Call to this to initialize the random number generator. ; @param seed an odd positive integer func Init(int seed) ; Fix up seed if Init is improperly called if (seed < 0) seed = seed + 2147483647 endif if (seed % 2 == 0) seed = 2147483647 - seed endif count = 0 ; Initialize the history array with a simple linear congruential generator. float x = seed int i = 0 ; Note: floats in UF have enough range so that 9069*2147483647 ; is exactly representable. repeat x = (9069.0 * x) % 2147483648.0 m_hist[i] = round(x) ; x is exactly an integer i = i + 1 until (i == 17) m_i = 4 m_j = 16 endfunc ; Init and run off some random ints. Sometimes this helps, historically. func Init2(int seed, int n) Init(seed) while n > 0 RandomInt(0) n = n - 1 endwhile endfunc ; @param pn If non-zero, initialize with seed = pn. If zero, just get next value ; @return the next value in the integer sequence. Values are from 0 to 2147483647, inclusive int func RandomInt(const int pn) if (pn != 0) ;reset seed Init(pn) endif count = count + 1 int k = m_hist[m_i] - m_hist[m_j] if (k < 0) k = k + 2147483647 endif m_hist[m_j] = k m_i = m_i - 1 if (m_i < 0) m_i = 16 endif m_j = m_j - 1 if (m_j < 0) m_j = 16 endif return k endfunc int func HowMany( ) return count endfunc ; Set the maximum integer for the range version. ; @param Nmax maximum integer (set to 16 if Nmax <= 0) ; @return true if Nmax > 0, false if Nmax <= 0 ; not needed if use RandomIntInRange2 instead of RandomIntInRange bool func SetNmax(const int Nmax) if (Nmax <= 0) ; bad, so use default value m_Nmax = 16 return false endif m_Nmax = Nmax return true endfunc ; Produce the next value in the sequence - range version ; ; @param pn If non-zero, initialize with seed = pn. If zero, just get next value ; @return the next value in the sequence. Values are from 1 to Nmax, inclusive. ; The second version is preferred; the first is retained for not breaking old ones. int func RandomIntInRange(const int pn) ; In general the high-order bits have better random properties than ; the low-order bits, so do it this way instead of RandomInt(pn)%m_Nmax. ; float x = RandomInt(pn) / 2147483648.0 return (1 + trunc( x * m_Nmax)) endfunc ; returns values in [1..nmax] int func RandomIntInRange2(const int pn, const int nmax) float x = RandomInt(pn) / 2147483648.0 int n = nmax if n <= 0 n = 1 endif return (1 + trunc( x * n)) endfunc ; Produce the next value in the sequence - float version ; ; @param pn If non-zero, initialize with seed = pn. If zero, just get next value ; @return the next value in the sequence. Value range is [0,1). float func RandomFloat(const int pn) return (RandomInt(pn) / 2147483648.0) endfunc ; this one is random in [-1,1) float func RandomFloat2(const int pn) return 2*RandomFloat(pn)-1 endfunc ; 230401 random unit vector complex func RandomVector(const int pn) float ang = #pi * RandomFloat2(pn) return exp(flip(ang)) endfunc protected: int m_hist[17] ; history array int m_i ; index 1 into array int m_j ; index 2 into array int m_Nmax ; maximum integer for range version int count ; how many randoms so far default: title = "Random" int param p_seed caption = "Seed" default = 12345 hint = "Sets the seed for the random number generator. \ Each different seed produces a different sequence of random values." endparam } class MyMath{ static float func fmax(float a, float b) if a >= b, return a, endif, return b endfunc static float func fmin(float a, float b) if a <= b, return a, endif, return b endfunc static int func imax(int a, int b) if a >= b, return a, endif, return b endfunc static int func imin(int a, int b) if a <= b, return a, endif, return b endfunc static int func quadrant(complex z) ; 1 0 ; 2 3 if real(z) >= 0 && imag(z) >= 0, return 0, endif if real(z) <= 0 && imag(z) >= 0, return 1, endif if real(z) <= 0 && imag(z) <= 0, return 2, endif return 3 endfunc static float func fround(float x, int n) ;n = imax(n, 0) float scale = 10.0 ^ n return round(scale*x)/scale endfunc static complex func cround(complex z, int n) return fround(real(z), n) + flip(fround(imag(z), n)) endfunc } class JLB_QuadMirror(common.ulb:UserTransform) { ; Pick a block from the image, then repeat it mirroring both ; horizontally and vertically. public: import "common.ulb" func JLB_QuadMirror(Generic pparent) UserTransform.UserTransform(pparent) m_PixelCoordinate = new PixelCoordinate(pparent) m_FractalCoordinate = new FractalCoordinate(pparent) endfunc func Init(complex pz) UserTransform.Init(pz) m_PixelCoordinate.Init(pz) m_FractalCoordinate.Init(pz) int ix = @nx int iy = @ny if (@orig) ix = 1 iy = 1 endif ; block size in pixels (floats) m_hx = real(#screenmax) / ix m_hy = imag(#screenmax) / iy complex z if (@ssb) ; which block to copy with mirroring z = m_PixelCoordinate.Iterate(@z_ctr1) ix = 1 + floor(real(z) / m_hx) if (ix > @nx) ix = @nx elseif (ix < 1) ix = 1 endif iy = 1 + floor(imag(z) / m_hy) if (iy > @ny) iy = @ny elseif (iy < 1) iy = 1 endif m_x2 = ix * m_hx ; right side of block m_x1 = m_x2 - m_hx + 1 ; left side m_y2 = iy * m_hy ; top side m_y1 = m_y2 - m_hy + 1 ;bottom side else ; where selected block goes ;z = m_PixelCoordinate.Iterate(@z_ctr2) m_x1 = #width/2 - m_hx/2 m_x2 = m_x1 + m_hx m_y1 = #height/2 - m_hy/2 m_y2 = m_y1 + m_hy endif endfunc complex func Iterate(complex pz) UserTransform.Iterate(pz) complex tz = #screenpixel ; calculate pixel within source block for mirrored pixel to use int n float tx = real(tz) float dx if (tx > m_x2) dx = tx - m_x2 n = floor(dx/m_hx) dx = dx - n*m_hx if (n % 2 == 0) tx = m_x1 + m_hx - dx else tx = m_x1 + dx endif elseif (tx < m_x1) dx = m_x1 - tx n = floor(dx/m_hx) dx = dx - n * m_hx if (n % 2 == 0) tx = m_x1 + dx else tx = m_x1 + m_hx - dx endif endif ; now m_x1 <= tx <= m_x2 float ty = imag(tz) float dy if (ty > m_y2) dy = ty - m_y2 n = floor(dy/m_hy) dy = dy - n*m_hy if (n % 2 == 0) ty = m_y1 + m_hy - dy else ty = m_y1 + dy endif elseif (ty < m_y1) dy = m_y1 - ty n = floor(dy/m_hy) dy = dy - n * m_hy if (n % 2 == 0) ty = m_y1 + dy else ty = m_y1 + m_hy - dy endif endif ; now m_y1 <= ty <= m_y2 tz = tx + flip(ty) complex sz = m_FractalCoordinate.Iterate(tz) if (!@ssb && !@orig) sz = sz + @z_ctr2 - #center endif return sz endfunc protected: PixelCoordinate m_PixelCoordinate FractalCoordinate m_FractalCoordinate float m_hx, float m_hy ; block dimensions in pixels float m_x2, float m_x1 ; block edges in pixels float m_y2, float m_y1 default: title = "QuadMirror" rating = notRecommended heading caption = "Use QuadMirror2 instead of QuadMirror" endheading heading caption = "How many repeated blocks?" endheading int param nx caption = "Horizontal" default = 3 min = 1 endparam int param ny caption = "Vertical" default = 2 min = 1 endparam heading text = "To make the blocks fit evenly in the resulting image, check here." endheading bool param ssb caption = "Select source block?" endparam heading text = "Choose any point in the source block. In the resulting image, \ this block will be in its original position." visible = @ssb endheading complex param z_ctr1 caption = "Point" default = #center visible = @ssb hint = "Check See Original, use the eyedropper to pick it, or use Explore." endparam heading text = "Select the center point of the source block. In the resulting image, \ the block will be moved to the image center." visible = !@ssb endheading complex param z_ctr2 caption = "Center point" default = #center visible = !@ssb hint = "Check See Original, use the eyedropper to pick it, or use Explore." endparam bool param orig caption = "See Original?" default = false hint = "Check the box to see where to set the point" endparam } class JLB_QuadMirror2(common.ulb:UserTransform) { ; Pick a block from the image, then repeat it mirroring both ; horizontally and vertically. public: import "common.ulb" func JLB_QuadMirror2(Generic pparent) UserTransform.UserTransform(pparent) m_PixelCoordinate = new PixelCoordinate(pparent) m_FractalCoordinate = new FractalCoordinate(pparent) endfunc func Init(complex pz) UserTransform.Init(pz) m_PixelCoordinate.Init(pz) m_FractalCoordinate.Init(pz) int ix = 1 if (@bpos > 1) ix = 2 endif int iy = 1 if (@bpos == 1 || @bpos == 3) iy = 2 endif ; block size in pixels (floats) m_bx = real(#screenmax) / @nx m_by = imag(#screenmax) / @ny m_pix_c = m_PixelCoordinate.Iterate(@z_c) if (@show == 1) m_x1 = real(#screenmax)/2 - m_bx/2 m_y1 = imag(#screenmax)/2 - m_by/2 elseif (@show == 2) ; pixel location of original block m_x1 = (ix - 1) * m_bx ; in mirrored image m_y1 = (iy - 1) * m_by endif m_x2 = m_x1 + m_bx m_y2 = m_y1 + m_by mm_pix_c = (m_x1 + m_x2)/2 + flip((m_y1 + m_y2)/2) endfunc complex func Iterate(complex pz) UserTransform.Iterate(pz) complex tz = #screenpixel float tx = real(tz) float ty = imag(tz) if (@show == 0) return pz elseif (@show == 1) if (tx >= m_x1 && tx <= m_x2 && ty >= m_y1 && ty <= m_y2) return m_FractalCoordinate.Iterate(tz + m_pix_c - #screenmax/2) else return @z_c endif else ; calculate pixel within source block for mirrored pixel to use int n float dx if (tx > m_x2) dx = tx - m_x2 n = floor(dx/m_bx) dx = dx - n*m_bx if (n % 2 == 0) tx = m_x1 + m_bx - dx else tx = m_x1 + dx endif elseif (tx < m_x1) dx = m_x1 - tx n = floor(dx/m_bx) dx = dx - n * m_bx if (n % 2 == 0) tx = m_x1 + dx else tx = m_x1 + m_bx - dx endif endif ; now m_x1 <= tx <= m_x2 float dy if (ty > m_y2) dy = ty - m_y2 n = floor(dy/m_by) dy = dy - n*m_by if (n % 2 == 0) ty = m_y1 + m_by - dy else ty = m_y1 + dy endif elseif (ty < m_y1) dy = m_y1 - ty n = floor(dy/m_by) dy = dy - n * m_by if (n % 2 == 0) ty = m_y1 + dy else ty = m_y1 + m_by - dy endif endif ; now m_y1 <= ty <= m_y2 tz = tx + flip(ty) ; pixel offset in original block return m_FractalCoordinate.Iterate(tz + m_pix_c - mm_pix_c) endif endfunc protected: PixelCoordinate m_PixelCoordinate FractalCoordinate m_FractalCoordinate complex m_pix_c ; center of mirrored block in pixels complex mm_pix_c ; center of mirrored block in new location float m_bx, float m_by ; block dimensions in pixels float m_x1, float m_x2 ; block edges in pixels float m_y1, float m_y2 default: title = "QuadMirror2" heading caption = "How many repeated blocks?" endheading int param nx caption = "Horizontal" default = 4 min = 1 endparam int param ny caption = "Vertical" default = 2 min = 1 endparam heading caption = "Where to put the original block" endheading param bpos caption = "Block position" enum = "1, 1" "1, 2" "2, 1" "2, 2" default = 2 endparam heading endheading complex param z_c caption = "Center point" default = #center hint = "Center of block to be mirrored" endparam heading endheading param show caption = "Show" enum = "Unmirrored original" "Selected block" "Mirrored image" default = 2 ; hint = "?" endparam } class JLB_Vignette(common.ulb:DirectColoring) { ; This is a direct coloring formula. ; put a vignette shape on a layer; use it as the top layer to modify the image ; type 1 : center is white, use it to multiply the lower layer ; type 2 : center is transparent ; Use with a pixel formula public: import "common.ulb" ; constructor func JLB_Vignette(Generic pparent) DirectColoring.DirectColoring(pparent) endfunc ; initialize the objects func Init(complex pz, complex ppixel) DirectColoring.Init(pz, ppixel) if @v_Vignette == 102 type = 0 else type = @vtype rr = red(gradient(0)) gg = green(gradient(0)) bb = blue(gradient(0)) endif endfunc ; call for each iterated point func Iterate(complex pz) DirectColoring.Iterate(pz) endfunc ; override the parent and call in the final section of the coloring formula.
color func Result(complex pz) ; screen relative coordinates go from 0 to +1, symmetric in x and y ; zero at screen center, 1 at screen edges float x = abs( (real(#screenpixel) / (0.5*#width )) - 1) float y = abs( (imag(#screenpixel) / (0.5*#height)) - 1) float r = x^@shape + y^@shape float r0 = @radius^@shape if r <= r0 if type == 0 return hsl(0, 1, 1) ; white else return hsla(0,0,0,0) ; transparent endif endif ; t = 0 at inner radius, 1 at outer radius (at 2 in earliest version) float t float r1 = @r_outer if @v_Vignette > 102 r1 = r1^@shape ; it was wrong before endif if @v_Vignette < 102 t = (r - r0) / (2 - r0) else t = (r - r0) / (r1 - r0) endif t = MyMath.fmin(t, 1) ;t= MyMath.fmax(0, t) ; not needed float luminance if @v_Vignette < 102 luminance = 1 - (1-@dark) * t^@power return hsl(0, 0, luminance) ; gray elseif type == 0 luminance = 1 - (1-@dark2) * t^@power return hsl(0, 0, luminance) ; gray else float opacity = t^@power return rgba(rr, gg, bb, opacity) ; partly transparent endif endfunc protected: int type float rr, float gg, float bb default: title = "Vignette" int param v_Vignette caption = "Version (Vignette)" default = 230105 visible = @v_Vignette < 102 ; 230105 change is backwards compatible endparam heading text="This coloring ignores the formula. Use a Pixel formula. \ Distances are screen-relative." endheading heading text = "White center: useful as a multiplying layer for darkening corners." visible = @vtype == 0 endheading heading text = "Transparent center: useful as a normal layer for making a border." visible = @vtype == 1 endheading param vtype caption = "Center color" enum = "White" "Transparent" default = 0 endparam float param radius caption = "Inner radius" min = 0 max = 2 default = 0.5 endparam float param r_outer caption = "Outer radius" min = 0 max = 2 default = 2 visible = @v_Vignette > 101 endparam float param shape caption = "Shape of vignette" min = 0.25 default = 2 hint = "Use 2 for circle/ellipse shape, >2 for squarer, 1 for diamond, 0.5 for astroid" endparam float param power caption = "Smoothness of vignette" min = 0 default = 2 hint = "small values make abrupt inner edge, large values smoother" endparam float param dark caption = "Brightness of vignette at corners" default = 0.25 hint = "0 for solid black, 1 for no effect" min = 0 max = 1 visible = @v_Vignette < 102 endparam float param dark2 caption = "Brightness at outer radius" default = 0.25 hint = "0 for solid black, 1 for no effect" min = 0 max = 1 visible = @v_Vignette == 102 || @vtype == 0 endparam } class JLB_Cells(common.ulb:GradientColoring) { ; ; November 2020 ; Divide into rectangular or triangularcells ; public: ;$define debug func JLB_Cells(Generic pparent) GradientColoring.GradientColoring(pparent) g = new Geometry ; only used for triangles StartRandom(100) MinArea = 0.01 * @pMinArea * #width * #height ; convert from percent MinSide = @pMinSide * #width MinSideSq = sqr(MinSide) xb = @pXBorder * #width yb = @pYBorder * #width lw = @B * #width NumSmall = 0 if @pWhich == 0 FirstTriangles() else NumCells = 1 V1[0] = xb + flip(yb) V2[0] = #width-xb + flip(#height-yb) Check(0) endif int tries = 0 ; just in case of disaster while NumSmall < NumCells && NumCells < @pMaxCells && tries < @pMaxCells if @pWhich == 0 Divide3() else Divide4() endif tries = tries + 1 endwhile Shuffle() endfunc func FirstTriangles() int num = 2 if @pFirstDiv == "3" && @pMaxCells >= 3 num = 3 endif if @pFirstDiv == "4" && @pMaxCells >= 4 num = 4 endif complex vul = xb + flip(yb) complex vur = #width-xb + flip(yb) complex vll = xb + flip(#height-yb) complex vlr = #width-xb + flip(#height-yb) if num == 2 ; two triangles if r.RandomFloat(0) < 0.5 V1[0] = vul, V2[0] = vur, V3[0] = vll V1[1] = vlr, V2[1] = vur, V3[1] = vll else V1[0] = vll, V2[0] = vul, V3[0] = vlr V1[1] = vur, V2[1] = vul, V3[1] = vlr endif elseif num == 3 ; first choose which side to divide float w = #width - 2*xb float h = #height - 2*yb int k if @pChoose == "Random" k = r.RandomIntInRange2(0, 4) elseif @pChoose == "Larger" float x = 2*(w+h)*r.RandomFloat(0) if x <= w k = 1 elseif x <= w + h k = 2 elseif x <= 2*w + h k = 3 else k = 4 endif else ; "Largest" if w == h k = r.RandomIntInRange2(0, 4) else k = 1 if w < h k = 2 endif if r.RandomFloat(0) > 0.5 k = k + 2 endif endif endif float frac = 0.5 if @pSlop > 0 frac = 0.5 + @pSlop*r.RandomFloat2(0) endif complex vvv if k == 1 ; top vvv = vul + frac*(vur-vul) V1[0] = vul, V2[0] = vvv, V3[0] = vll V1[1] = vlr, V2[1] = vvv, V3[1] = vll V1[2] = vur, V2[2] = vvv, V3[2] = vlr elseif k == 2 ; right vvv = vur + frac*(vlr-vur) V1[0] = vul, V2[0] = vvv, V3[0] = vll V1[1] = vul, V2[1] = vvv, V3[1] = vur V1[2] = vll, V2[2] = vvv, V3[2] = vlr elseif k == 3 ; bottom vvv = vll + frac*(vlr-vll) V1[0] = vul, V2[0] = vvv, V3[0] = vll V1[1] = vul, V2[1] = vvv, V3[1] = vur V1[2] = vur, V2[2] = vvv, V3[2] = vlr else ; left vvv = vul + frac*(vll-vul) V1[0] = vul, V2[0] = vvv, V3[0] = vur V1[1] = vlr, V2[1] = vvv, V3[1] = vur V1[2] = vll, V2[2] = vvv, V3[2] = vlr endif else ; four triangles float frac = 0.5 if @pSlop > 0 frac = 0.5 + @pSlop*r.RandomFloat2(0) endif float xc = xb + frac*(#width-2*xb) if @pSlop > 0 frac = 0.5 + @pSlop*r.RandomFloat2(0) endif float yc = yb + frac*(#height-2*yb) complex vc = xc + flip(yc) ; the center vertex V1[0] = vul, V2[0] = vur, V3[0] = vc V1[1] = vll, V2[1] = vul, V3[1] = vc V1[2] = vll, V2[2] = vlr, V3[2] = vc V1[3] = vlr, V2[3] = vur, V3[3] = vc endif NumCells = num int i = 0 while i < NumCells Check(i) i = i + 1 endwhile endfunc func Init(complex pz, complex ppixel) GradientColoring.Init(pz, ppixel) endfunc func Divide3() int n = Choose() float frac if NumCells+3 > @pMaxCells || r.RandomFloat(0) > @pDivide4 ; divide into 2 triangles frac = 0.5 if @pSlop > 0 frac = 0.5 + @pSlop*r.RandomFloat2(0) endif ; divide longest side. later maybe other possibilities int k = g.LongestSide(V1[n], V2[n], V3[n]) complex w1, complex w2, complex w3 if k == 1 ; side v1v2 is longest; divide it w1 = V3[n], w2 = V1[n], w3 = V2[n] elseif k == 2 ; side v2v3 is longest w1 = V1[n], w2 = V3[n], w3 = V2[n] else ; side v3v1 is longest w1 = V2[n], w2 = V1[n], w3 = V3[n] endif complex w4 = w2 + frac*(w3-w2) V1[n] = w1, V2[n] = w2, V3[n] = w4 int m = NumCells V1[m] = w1, V2[m] = w3, V3[m] = w4 ; are the new cells divisible? Check(n) Check(m) NumCells = NumCells + 1 else ; divide into 4 triangles complex p1 = V1[n] complex p2 = V2[n] complex p3 = V3[n] frac = 0.5 + @pSlop*r.RandomFloat2(0) complex p4 = p1 + frac*(p2-p1) frac = 0.5 + @pSlop*r.RandomFloat2(0) complex p5 = p2 + frac*(p3-p2) frac = 0.5 + @pSlop*r.RandomFloat2(0) complex p6 = p3 + frac*(p1-p3) V1[n] = p1, V2[n] = p4, V3[n] = p6, Check(n) m = NumCells V1[m] = p3, V2[m] = p5, V3[m] = p6, Check(m) m = m + 1 V1[m] = p2, V2[m] = p4, V3[m] = p5, Check(m) m = m + 1 V1[m] = p4, V2[m] = p5, V3[m] = p6, Check(m) NumCells = NumCells + 3 endif endfunc func Divide4() int n = Choose() float frac if NumCells+4 > @pMaxCells || r.RandomFloat(0) >= @pDivide5 ; divide into 2 cells frac = 0.5 + @pSlop*r.RandomFloat2(0) bool divideX if Small[n] == 2 divideX = true elseif Small[n] == 1 divideX = false else float xx = real(V2[n]-V1[n]) float yy = imag(V2[n]-V1[n]) if xx != yy && r.RandomFloat(0) < @pPreferLong divideX = (xx >= yy) else divideX = r.RandomFloat(0) <= @pXFrac endif endif if divideX ; n is left part, new is right part V1[NumCells] = real(V1[n]+frac*(V2[n]-V1[n])) + flip(imag(V1[n])) V2[NumCells] = V2[n] ;V1[n] unchanged V2[n] = real(V1[NumCells]) + flip(imag(V2[n])) else ; n is top part, new is bottom part V1[NumCells] = real(V1[n]) + flip(imag(V1[n]+frac*(V2[n]-V1[n]))) V2[NumCells] = V2[n] ;V1[n] unchanged V2[n] = real(V2[n]) + flip(imag(V1[NumCells])) endif Check(n) Check(NumCells) NumCells = NumCells + 1 else ; divide into 5 cells float x1 = real(V1[n]), float y1 = imag(V1[n]) float x2 = real(V2[n]), float y2 = imag(V2[n]) ; if all the fracs are the same, the inner rectangle is centered. frac = (1+@pSlop*r.RandomFloat2(0))/3 float x3 = x1 + frac*(x2-x1) ;frac = (1+@pSlop*r.RandomFloat2(0))/3 float x4 = x2 - frac*(x2-x1) ;frac = (1+@pSlop*r.RandomFloat2(0))/3 float y3 = y1 + frac*(y2-y1) ;frac = (1+@pSlop*r.RandomFloat2(0))/3 float y4 = y2 - frac*(y2-y1) ; clockwise or counter-clockwise int m if r.RandomFloat2(0) > @pCW V1[n] = x1+flip(y1), V2[n] = x4+flip(y3), Check(n), m = NumCells V1[m] = x4+flip(y1), V2[m] = x2+flip(y4), Check(m), m = m + 1 V1[m] = x3+flip(y4), V2[m] = x2+flip(y2), Check(m), m = m + 1 V1[m] = x1+flip(y3), V2[m] = x3+flip(y2), Check(m), m = m + 1 V1[m] = x3+flip(y3), V2[m] = x4+flip(y4), Check(m) else V1[n] = x1+flip(y1), V2[n] = x3+flip(y4), Check(n), m = NumCells V1[m] = x3+flip(y1), V2[m] = x2+flip(y3), Check(m), m = m + 1 V1[m] = x4+flip(y3), V2[m] = x2+flip(y2), Check(m), m = m + 1 V1[m] = x1+flip(y4), V2[m] = x4+flip(y2), Check(m), m = m + 1 V1[m] = x3+flip(y3), V2[m] = x4+flip(y4), Check(m) endif NumCells = NumCells + 4 endif endfunc ; select cell to divide int func Choose() int n int nLarge = MakeLargeList() if LargestArea > @pRatio*SmallestArea int m = r.RandomIntInRange2(0, nLarge) - 1 return Large[m] endif if @pChoose == 0 ; any available cell is okay; uniform probability per cell ;r.SetNMax(NumCells) repeat n = r.RandomIntInRange2(0, NumCells) - 1 until Small[n] < 3 elseif @pChoose == 1 ; prefer to divide larger cells; probability proportional to area repeat repeat float x = r.RandomFloat(0)*#width float y = r.RandomFloat(0)*#height n = CellNum(x, y) until n >= 0 ; rounding can make the point be not in any triangle. until Small[n] < 3 else ; only do one of the largest ones. (Guaranteed to be dividable.) int m = r.RandomIntInRange2(0, nLarge) - 1 n = Large[m] endif return n endfunc int func MakeLargeList() SmallestArea = LargestArea = Area(0) Large[0] = 0 int count = 1 int i = 1 while i < NumCells float tmp = Area(i) if tmp < SmallestArea SmallestArea = tmp endif float ratio = tmp/LargestArea if ratio > 1+@eps count = 1 Large[0] = i LargestArea = tmp elseif ratio > 1-@eps ; essentially the same area Large[count] = i count = count + 1 endif i = i + 1 endwhile return count endfunc float func Area(int n) if @pWhich == 0 return g.TriangleArea(V1[n], V2[n], V3[n]) else return real(V2[n]-V1[n])*imag(V2[n]-V1[n]) endif endfunc ; is cell n too small to divide? func Check(int n) int i = 0 if Area(n) < 2*MinArea i = 4 elseif @pWhich == 0 if |V1[n]-V2[n]| < 2*MinSideSq i = 3 elseif |V2[n]-V3[n]| < 2*MinSideSq i = 3 elseif |V3[n]-V1[n]| < 2*MinSideSq i = 3 endif else if real(V2[n]-V1[n]) < 2*MinSide || @pXFrac == 0.0 i = 1 endif if imag(V2[n]-V1[n]) < 2*MinSide || @pXFrac == 1.0 i = i + 2 endif endif if i >= 3 NumSmall = NumSmall + 1 endif Small[n] = i endfunc func Iterate(complex pz) GradientColoring.Iterate(pz) endfunc float func ResultIndex(complex pz) float x = real(#screenpixel) float y = imag(#screenpixel) int n = CellNum(x, y) if @pLines ; just show cell border lines if n < 0 ; the point is in the outside border, maybe close enough to be in the line if (x<=xb && x>=xb-lw && y>=yb-lw && y<=#height-yb+lw) ; left outer line return 0 elseif (y<=yb && y>=yb-lw && x>=xb-lw && x<=#width-xb+lw) ; top outer line return 0 elseif (y>=#height-yb && y<=#height-yb+lw && x>=xb-lw && x<=#width-xb+lw) ; bottom outer line return 0 elseif (x>=#width-xb && x<=#width-xb+lw && y>=yb-lw && y<=#height-yb+lw) ; right outer line return 0 else return 0.667 ; usually this will be transparent endif else ; the point is inside a cell, is it near an edge? if @pWhich == 0 if g.DistancePointToLine(#screenpixel, V1[n], V2[n]) <= lw + @eps return 0 elseif g.DistancePointToLine(#screenpixel, V2[n], V3[n]) <= lw + @eps return 0 elseif g.DistancePointToLine(#screenpixel, V3[n], V1[n]) <= lw + @eps return 0 endif elseif x<=real(V1[n])+lw || x>=real(V2[n])-lw || y<=imag(V1[n])+lw || y>=imag(V2[n])-lw return 0 endif return 0.333 ; usually this will be transparent endif else ; not doing cell lines if n < 0 m_Solid = true ; m_Solid inherited through GradientColoring class ; from Coloring class return -1 ; m_Solid dominates #index in Coloring else return Perm[n]/NumCells endif endif endfunc int func CellNum(float x, float y) if x#width-xb || y#height-yb ; in the outer border return -1 endif int i = 0 while i < NumCells bool yes if @pWhich == 0 yes = g.PointInTriangle(x+flip(y), V1[i], V2[i], V3[i]) else yes = x >= real(V1[i]) && x <= real(V2[i]) && y >= imag(V1[i]) && y <= imag(V2[i]) endif if yes return i endif i = i + 1 ; endwhile return -1 ; should never get here endfunc func StartRandom(int n) ; this seems to improve the random sequence r = new JLB_Random(0) r.Init(@seed) int i = 0 while i < n r.RandomInt(0) i = i + 1 endwhile endfunc func Shuffle( ) int n = 0 while n < NumCells Perm[n] = n n = n + 1 endwhile int times = 0 while times < @pRandomColor int n = 0 while n < NumCells int m = r.RandomIntInRange2(0, NumCells) - 1 int tmp = Perm[n] Perm[n] = Perm[m] Perm[m] = tmp n = n + 1 endwhile times = times + 1 endwhile endfunc private: JLB_Random r int NumCells int NumSmall ; if triangles, V1, V2, V3 = the 3 vertices ; if rectangles, V1 = upper left corner of cell, V2 = lower right corner of cell, V3 not used complex V1[@pMaxCells] complex V2[@pMaxCells] complex V3[@pMaxCells] int Small[@pMaxCells] ; 0 = okay, 1 = small in X, 2 = in Y, 3 = both, 4 in area int Large[@pMaxCells] ; list of largest cell. Doesn't need to be this big, but ... float SmallestArea float LargestArea int Perm[@pMaxCells] ; permutation used for randomizing colors ; float MinXSize ; used only for rectangles ; float MinYSize float MinArea float MinSide float MinSideSq ; square of minimum side length for triangles only float xb, float yb ; border widths float lw ; line width Geometry g default: title = "Cells" rating = recommended ; heading ; caption = "Use a pixel formula" ; endheading ; heading ; caption = "Distances are from 0 to 1." ; endheading int param v_cells caption = "Version (Cells)" default = 100 hint = "This version parameter is used to detect when a change has been made \ to the formula that is incompatible with the previous version. When that \ happens, this field will reflect the old version number to alert you to the fact \ that an alternate rendering is being used." visible = @v_cells < 100 endparam param pWhich caption = "Cell type" enum = "Triangles" "Rectangles" default = 1 endparam int param pMaxCells caption = "Maximum # of cells" default = 25 min = 1 endparam int param seed caption = "Seed" hint = "Seed for the random number generator" default = 12345 endparam float param pXBorder caption = "Left and right borders" hint = "as fraction of width" default = 0.05 min = 0 max = 0.3 endparam float param pYBorder caption = "Top and bottom borders" hint = "as fraction of width (not height!)" default = 0.05 min = 0 max = 0.3 endparam param pFirstDiv caption = "First divide" hint = "Divide initial rectangle in 2, 3, or 4 parts" default = 1 enum = "2" "3" "4" visible = @pWhich == 0 && @pMaxCells >= 3 endparam param pChoose caption = "How to choose a cell" enum = "Random" "Larger" "Largest" hint = "Larger: larger cells are more likely to be divided. \ Largest: only the largest cells will be divided." default = 1 endparam float param pRatio caption = "Max ratio of areas" default = 4 min = 1 visible = @pChoose != 2 endparam float param pDivide5 caption = "Divide 5 fraction" default = 0.125 min = 0 max = 1 hint = "Fraction of the time to divide cells into 5 cells instead of 2." visible = @pWhich == 1 endparam float param pCW caption = "Preference for clockwise" hint = "Fraction of the time for 5 cell divisions to go clockwise instead of \ counter-clockwise." min = 0 max = 1 default = 0.5 visible = @pWhich == 1 && @pDivide5 > 0 endparam float param pDivide4 caption = "Divide 4 fraction" default = 0.125 min = 0 max = 1 hint = "Fraction of the time to divide triangles into 4 triangles\ instead of 2." visible = @pWhich == 0 endparam float param pPreferLong caption = "Longer side preference" hint = "Fraction of time to divide the longer side" default = 0.75 min = 0 max = 1 visible = @pWhich == 1 endparam float param pXFrac caption = "Horizontal divide fraction" hint = "Fraction of cells divided left to right; the rest divided top to bottom" default = 0.5 min = 0 max = 1 visible = @pWhich == 1 endparam float param pSlop caption = "Divide tolerance" default = 0.05 hint = "How much off the nominal value a cell division can be." min = 0 max = 0.49 endparam float param pMinSide caption = "Minimum side length" hint = "Cells with a side less than twice this cannot be divided" default = 0.05 ;min = 0.001 ;visible = @pWhich == 0 endparam float param pMinArea caption = "Minimum cell area (%)" hint = "Cells less than twice this area cannot be divided." default = 0.1 max = 25 endparam int param pRandomColor caption = "Shuffle colors" hint = "See which you prefer." min = 0 default = 1 visible = !@pLines endparam bool param pLines caption = "Show lines?" default = false hint = "If true, cell lines are colored as #index 0, \ cell interiors as 0.333, and outer border as 0.667." endparam float param B caption = "Half-width of edge lines" hint = "Width as a fraction of horizontal dimension" default = 0.004 min = 0 max = 0.25 visible = @pLines endparam float param eps ; slop for Large calculation default = 1.e-6 visible = false endparam } class JLB_SESFormula(SFormula) { ; z = a*f1(z op1 f2(c)) op2 b*f3(c) ; simplified version of SomethingElseMkII in tma2.ufm, rewritten as an SFormula public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_SESFormula(Generic pparent) SFormula.SFormula(pparent) m_Combine = new Combine() if @addT > 0 ;&& !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if !@p_Mtype ppc = @p_seed endif endfunc bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz complex t1 = @f2(ppc) t1 = m_Combine.go(pz, @op1, t1) t1 = @a*@f1(t1) complex t2 = @b*@f3(ppc) znew = m_Combine.go(t1, @op2, t2) ;if !@p_std if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif ;endif S.Update(znew, S.pc) return S.bailed endfunc protected: Combine m_Combine complex ppc UtilityTransform finalTransform default: ;rating = Recommended title = "Something Else" int param v_SESFormula caption = "Version (SE_SFormula)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_SESFormula < 100 endparam ; bool param p_std ; caption = "Standard?" ; default = true ; endparam heading caption = "z_new = a*f1(z op1 f2(c)) op2 b*f3(c)" endheading bool param p_Mtype caption = "M-type?" default = true endparam complex param p_seed caption = "Seed for J-type" default = (0.3, 0) visible = !@p_Mtype endparam complex param a caption = "a" default = 1 endparam func f1 caption = "f1" default = cos() endfunc param op1 caption = "op1" default = 4 enum = "+" "-" "*" "/" "^" endparam func f2 caption = "f2" default = ident() endfunc heading endheading param op2 caption = "op2" default = 0 enum = "+" "-" "*" "/" "^" endparam complex param b caption = "b" default = 1 endparam func f3 caption = "f3" default = ident() endfunc heading caption = "Tweak after each iteration" ;visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 ;visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 ;&& !@p_std selectable = false endparam } class JLB_SetZSFormula(SFormula) { ; Set pz to a value or #pixel. Useful for setting the start value. public: bool func Iterate(State S) S.pz = 0 ; so pzold = 0 if @type == 0 S.Update(@zz, S.pc) else S.Update(#pixel, S.pc) endif return S.bailed endfunc default: title = "Set z" rating = recommended int param v_SetZSFormula caption = "Version (JLB_SetZSFormula)" default = 100 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_SetZSFormula < 100 endparam param type enum = "value" "pixel" caption = "Set z to" default = 0 endparam complex param zz caption = "z" default = (0,0) visible = @type == 0 endparam } class JLB_Smooth(common.ulb:GradientColoring) { ; ; Object version of Smooth in Standard.ucl, slightly generalized. ; public: float func ResultIndex(complex pz) return @a * (m_Iterations + @b - log(log(cabs(pz)))/log(@p)) endfunc default: title = "Smooth" rating = recommended heading caption = "index = a*(iters+b-log(log(cabs(z)))/log(p))" endheading float param a caption = "a = overall multiplier" default = 0.05 endparam float param b caption = "b = offset" default = 0 endparam float param p caption = "p = power in main formula" default = 2 min = 1 endparam } class JLB_TwoFuncSFormula(SFormula) { ; Combine two functions ; This is a simpler subset of Combo ; public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_TwoFuncSFormula(Generic pparent) SFormula.SFormula(pparent) m_fSFormula = new @f_SFormula(this) m_gSFormula = new @g_SFormula(this) m_Combine = new Combine() if @addT > 0 ;&& !@p_std finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) m_fSFormula.Init(pz, pc) m_gSFormula.Init(pz, pc) endfunc ; @param pz ; @return true if need to quit bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz complex pc = S.pc if !@p_Mtype pc = @p_seed endif complex znew if @v_TwoFuncSFormula < 201 znew = @f(pz)^@p + @a*@g(pz)^@q else complex t2 if @gtype == 0 t2 = @a*(@g(pz))^@q else if !@p_Mtypeg S.pc = @p_seedg endif if m_gSFormula.Iterate(S) return true endif t2 = @a*(S.pz)^@q S.pz = pz S.pc = pc endif if @op == "( )" if @ftype == 0 znew = @a*(@f(t2))^@p else S.pz = t2 if !@p_Mtypef S.pc = @p_seedf endif if m_fSFormula.Iterate(S) return true endif znew = (S.pz)^@p endif else complex t1 if @ftype == 0 t1 = @f(pz)^@p else S.pz = pz ; original z value if !@p_Mtypef S.pc = @p_seedf endif if m_fSFormula.Iterate(S) return true endif t1 = (S.pz)^@p endif znew = m_combine.Go(t1, @op, t2) endif endif if !@p_Mtypec pc = @p_seedc endif znew = m_combine.Go(znew, @op2, @b*pc) ;if !@p_std if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif ;endif S.Update(znew, S.pc) return S.bailed endfunc protected: Combine m_combine SFormula m_fSFormula SFormula m_gSFormula UtilityTransform finalTransform default: title = "Two Functions" ;rating = Recommended int param v_TwoFuncSFormula caption = "Version (TwoFuncSFormula)" default = 201 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_TwoFuncSFormula < 201 endparam ; bool param p_std ; caption = "Standard?" ; default = true ; endparam heading caption = "znew = f(z)^p + a*g(z)^q + b*c" visible = @v_TwoFuncSFormula < 201 endheading heading caption = "znew = (f(z)^p) op1 (a*g(z)^q) op2 (b*c)" visible = @v_TwoFuncSFormula >= 201 endheading heading endheading bool param p_Mtype caption = "M-type" default = true visible = @v_TwoFuncSFormula < 201 endparam complex param p_seed caption = "J-type, seed =" default = (1, 0) visible = @v_TwoFuncSFormula < 201 && !@p_Mtype endparam param ftype caption = "f function type" default = 0 enum = "built-in func" "SFormula" endparam func f caption = "f (func)" default = sin() visible = @ftype == 0 endfunc SFormula param f_SFormula caption = "f (SFormula)" default = JLB_FuncSFormula visible = @ftype == 1 endparam bool param p_Mtypef caption = "f: M-type" default = true visible = @ftype == 1 endparam complex param p_seedf caption = "f: J-type seed =" default = (1, 0) visible = @ftype == 1 && !@p_Mtypef endparam complex param p caption = "p" default = (3,0) endparam heading endheading param op caption = "op1" default = 0 enum = "+" "-" "*" "/" "^" "( )" visible = @v_TwoFuncSFormula >= 201 endparam heading endheading complex param a caption = "a" default = 1 endparam param gtype caption = "g function type" default = 0 enum = "built-in func" "SFormula" endparam func g caption = "g (func)" default = tan() visible = @gtype == 0 endfunc SFormula param g_SFormula caption = "g (SFormula)" default = JLB_FuncSFormula visible = @gtype == 1 endparam bool param p_Mtypeg caption = "g: M-type" default = true visible = @gtype == 1 endparam complex param p_seedg caption = "g: J-type seed =" default = (1, 0) visible = @gtype == 1 && !@p_Mtypeg endparam complex param q caption = "q" default = (2,0) endparam heading endheading param op2 caption = "op2" default = 0 enum = "+" "-" "*" "/" "^" visible = @v_TwoFuncSFormula >= 201 endparam bool param p_Mtypec caption = "c: M-type" default = true visible = @v_TwoFuncSFormula >= 201 endparam complex param p_seedc caption = "c: J-type seed =" default = (1, 0) visible = @v_TwoFuncSFormula >= 201 && !@p_Mtypec endparam complex param b caption = "b" default = 1 endparam heading caption = "Tweak after each iteration" ;visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 ;visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 ;&& !@p_std selectable = false endparam } class JLB_MoveCenter(common.ulb:UserTransform) { ; Move the fractal to a new center public: import "common.ulb" func JLB_MoveCenter(Generic pparent) UserTransform.UserTransform(pparent) m_FractalCoordinate = new FractalCoordinate(pparent) endfunc func Init(complex pz) UserTransform.Init(pz) m_FractalCoordinate.Init(pz) complex new_center = @horiz*#width + flip(@vert*#height) offset = #center - m_FractalCoordinate.Iterate(new_center) if @clip xctr = real(new_center) yctr = imag(new_center) xmin = xctr - 0.5 * @xclip * real(#screenmax) xmax = xctr + 0.5 * @xclip * real(#screenmax) ymin = yctr - 0.5 * @yclip * imag(#screenmax) ymax = yctr + 0.5 * @yclip * imag(#screenmax) if @vMoveCenter < 230912 inclip = false else inclip = @iclip endif if inclip rad = 0.5 * MyMath.fmin(xmax-xmin,ymax-ymin) * @r endif endif endfunc complex func Iterate(complex pz) UserTransform.Iterate(pz) if @clip float x = real(#screenpixel) float y = imag(#screenpixel) if x < xmin || x > xmax || y < ymin || y > ymax m_solid = true elseif inclip float dx = abs(x-xctr)/rad float dy = abs(y-yctr)/rad float d = (dx^@p + dy^@p) ^ (1/@p) if d > 1 m_solid = true endif endif endif return pz + offset endfunc protected: FractalCoordinate m_FractalCoordinate complex offset float xmin, float xmax float ymin, float ymax float xctr, float yctr float rad bool inclip default: title = "MoveCenter" rating = Recommended int param vMoveCenter caption = "Version" default = 230912 visible = @vMoveCenter < 230912 endparam heading text = "Current version us 230912" visible = @vMoveCenter < 230912 endheading heading caption = "Move the fractal to a new center." endheading heading caption = "Optionally clip a rectangle around it." endheading heading caption = "Upper left is (0,0), lower right is (1,1)" endheading float param horiz caption = "Horizontal center" default = 0.25 min = 0 max = 1 endparam float param vert caption = "Vertical center" default = 0.25 min = 0 max = 1 endparam bool param clip caption = "Clip outside rectangle?" default = false endparam float param xclip caption = "Horizontal width" default = 0.25 min = 0 max = 1 visible = @clip endparam float param yclip caption = "Vertical width" default = 0.25 min = 0 max = 1 visible = @clip endparam bool param iclip caption = "Clip inside?" default = false endparam float param r caption = "Inside fraction" default = 0.5 min = 0 visible = @clip && @iclip endparam float param p caption = "Shape power" default = 2 min = 0.1 visible = @clip && @iclip endparam } class CalcValue { public: func CalcValue() endfunc static float func go(const int mode, const complex pz, const complex pc) if mode == 0 ; "cabs" or mod return cabs(pz) elseif mode == 1 ; "real" return real(pz) elseif mode == 2 ; "imag" return imag(pz) elseif mode == 3 ; "real+imag" return real(pz) + imag(pz) elseif mode == 4 ; "real-imag" return real(pz) - imag(pz) elseif mode == 4 ; "real*imag" return real(pz) * imag(pz) elseif mode == 5 ; "z angle" return atan2(pz) elseif mode == 6 ; "z cross+ c" return real(pz)*imag(pc) + imag(pz)*real(pc) elseif mode == 7 ; "z cross- c" return real(pz)*imag(pc) - imag(pz)*real(pc) elseif mode == 8 ;"z dot+ c" return real(pz)*real(pc) + imag(pz)*imag(pc) else ; "z dot- c" return real(pz)*real(pc) - imag(pz)*imag(pc) endif endfunc } class JLB_ThreeLevelSFormula(SFormula) { ; A Barnsley-like formula as an SFormula, with a non-abrupt transition \ ; possible from the upper formula to the middle and lower formulas. public: import "common.ulb" ; @param pparent the parent, generally "this" for the parent, or zero func JLB_ThreeLevelSFormula(Generic pparent) SFormula.SFormula(pparent) m_SFormulaU = new @f_SFormulaU(this) m_SFormulaM = new @f_SFormulaM(this) m_SFormulaL = new @f_SFormulaL(this) if @a*@v1 >= @a*@v2 m_critU = @a*@v1 m_critL = @a*@v2 else m_critU = @a*@v2 m_critL = @a*@v1 endif m_useM = m_critU > m_critL if @v_ThreeLevelSFormula < 211003 m_CalcValue = new CalcValue() ; else ; m_CTF2 = new ComplexToFloat2() endif mTSmooth = new TSmooth() endfunc func Init(complex pz, complex pc) m_SFormulaU.Init(pz, pc) ;if m_useM m_SFormulaM.Init(pz, pc) ;endif m_SFormulaL.Init(pz, pc) endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz complex pzold = S.pzold complex pc = S.pc complex pcc = pc ; this pc value only used for critical value if !@p_Mtype pcc = @p_seed endif float test if @v_ThreeLevelSFormula < 211003 test = m_CalcValue.Go(@mode, pz, pcc) else test = ComplexToFloat2.go(pz, pcc, @mode2, false) endif ; find U, M, and L z-values if m_SFormulaU.Iterate(S) return true endif complex zU = S.pz S.pz = pz, S.pzold = pzold, S.pc = pc ; use original values complex zM = 0 if m_useM if m_SFormulaM.Iterate(S) return true endif zM = S.pz S.iters = S.iters - 1 endif S.pz = pz, S.pzold = pzold, S.pc = pc if m_SFormulaL.Iterate(S) return true endif complex zL = S.pz S.pzold = pzold S.iters = S.iters - 1 ; if trans == 0, use all of U, M, or L complex fracU = 0 complex fracM = 0 complex fracL = 0 if @trans == 0 if test > m_critU fracU = 1 elseif test < m_critL fracL = 1 endif else ; otherwise, do a smooth transition using tanh fracU = mTSmooth.go((test-m_critU)/@trans) fracL = mTSmooth.go((m_critL-test)/@trans) endif fracM = 1 - fracU - fracL pz = fracU*zU + fracM*zM + fracL*zL S.pz = S.pzold S.Update(pz, pc) ; keep original pc, otherwise don't know which to use. return S.bailed endfunc protected: SFormula m_SFormulaU SFormula m_SFormulaM SFormula m_SFormulaL CalcValue m_CalcValue TSmooth mTSmooth ; ComplexToFloat2 m_CTF2 float m_critU float m_critL bool m_useM default: title = "ThreeLevel" rating = Recommended int param v_ThreeLevelSFormula caption = "Version (ThreeLevelSFormula)" default = 211003 hint = "This version parameter is used to detect when a change has \ been made to the formula that is incompatible with the \ previous version. When that happens, this field will reflect \ the old version number to alert you to the fact that an \ alternate rendering is being used." visible = @v_ThreeLevelSFormula < 211003 endparam heading text = "Apply change depending on critical value" hint = "Use real and imaginary parts of z and c to \ calculate value to compare with critical value." endheading bool param p_Mtype caption = "M-type (for critical value)" hint = "For calculating the critical value, 'c' is the pixel" default = true visible = (@v_ThreeLevelSFormula < 211003 && @mode >= 7) \ || (@v_ThreeLevelSFormula >= 211003 && @mode2 >= 17) endparam complex param p_seed caption = "Seed (for critical value)" default = (1, 0) hint = "For calculating the critical value, 'c' is this seed. \ It is not the 'c' in any of the formulas." visible = !@p_Mtype && ( \ (@v_ThreeLevelSFormula < 211003 && @mode >= 7) \ || (@v_ThreeLevelSFormula >= 211003 && @mode2 >= 17) ) endparam param mode caption = "mode" hint = "How to calculate the critical value." default = 0 enum = "mod" "real" "imag" "real+imag" "real-imag" "real*imag" "z angle" \ "z cross+ c" "z cross- c" "z dot+ c" "z dot- c" visible = @v_ThreeLevelSFormula < 211003 endparam param mode2 caption = "mode" hint = "How to calculate the critical value." default = 0 enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag|""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" "z cross+ c" "z cross- c" "z dot+ c" "z dot- c" "cabs(z*c)" visible = @v_ThreeLevelSFormula >= 211003 endparam float param v1 caption = "V1" default = 1 endparam float param v2 caption = "V2" default = -1 endparam float param a caption = "V multiplier" hint = "Upper critical value = larger of a*V1, a*V2; lower critical value =\ the smaller" default = 1 endparam float param trans caption = "Transition parameter" hint = "Smaller values make a steeper transition. For an abrupt transition, use 0." default = 1 min = 0 endparam SFormula param f_SFormulaU caption = "Upper formula" default = JLB_FuncSFormula endparam heading text = "Middle formula not needed" visible = @v1 == @v2 endheading SFormula param f_SFormulaM caption = "Middle formula" default = JLB_MandelbrotSFormula visible = @v1 != @v2 endparam SFormula param f_SFormulaL caption = "Lower formula" default = JLB_PowerSFormula endparam } class Distance(common.ulb:Generic) { ; ; Distance base class. No title, so only used for a base class. ; Given a complex value, return a float. public: import "common.ulb" func Distance(Generic pparent) Generic.Generic(pparent) endfunc func Init(complex zz) m_mode = 0 m_abs_value = false ; m_see_shape = false endfunc func SetParams(int m, bool a) m_mode = m m_abs_value = a endfunc func SetIter(int k) m_iter = k endfunc int func GetIter( ) return m_iter endfunc ; @param pz ; @param pc float func Iterate(complex zz) return cabs(zz) endfunc protected: int m_mode bool m_abs_value int m_iter ; bool m_see_shape default: int param v_Distance caption = "Version (Distance)" default = 211019 visible = false endparam } class JLB_DExpSmooth(Distance) { ; 21 August 2021 added complications as seen in jlb.ucl "Exponential Smoothing Plus" ; and in ejb.ucl "Smooth colouring EB adjustment" public: func JLB_DExpSmooth(Generic pparent) endfunc func Init(complex zz) center = (0,0) if @ctr == "Screen center" center = #center elseif @ctr == "Pixel" center = #pixel elseif @ctr == "Point" center = @pt endif scale = @a endfunc float func Iterate(complex zz) float d = ComplexToFloat.go(zz-center, m_mode, m_abs_value) d = real(@F(d)) ; real matches ejb. cabs maybe better? d = exp(-scale*d) scale = @r * scale return d endfunc private: complex center float scale default: title = "Exponential Smoothing" rating = notRecommended heading text = "(Exponential Smoothing 2 is the preferred plug-in.)" endheading param ctr caption = "Center" enum = "Origin" "Screen center" "Pixel" "Point" default = 0 hint = "Calculate distance from here." endparam complex param pt caption = "Point" default = (0,0) visible = @ctr == 3 endparam func F caption = "Function of distance" default = ident() hint = "Apply this function before exponential." endfunc float param a caption = "Scale" default = 1 ;min = 0 hint = "Large positive values give more emphasis to earlier iterations." endparam float param r caption = "Factor to change scale" default = 1 endparam } class JLB_DExpSmooth2(Distance) { ; 21 August 2021 added complications as seen in jlb.ucl "Exponential Smoothing Plus" ; and in ejb.ucl "Smooth colouring EB adjustment" public: func JLB_DExpSmooth2(Generic pparent) Distance(pparent) if @version >= 220703 m_dist = new @dist(this) endif center = (0,0) if @ctr == "Screen center" center = #center elseif @ctr == "Pixel" center = #pixel elseif @ctr == "Point" center = @pt endif scale = @a endfunc func Init(complex zz) if @version >= 220703 m_dist.SetParams(0, true) endif endfunc float func Iterate(complex zz) float d if @version >= 220703 d = m_dist.Iterate(zz) else int mmode = m_mode if @version < 220619 mmode = @mode endif d = ComplexToFloat2.go(zz-center, (0,0), mmode, true) endif d = real(@F(d)) ; real matches ejb. cabs maybe better? d = exp(-scale*d) scale = @r * scale return d endfunc private: complex center float scale Distance m_dist default: title = "Exponential Smoothing 2" rating = Recommended int param version caption = "Version" default = 220703 visible = @version < 220703 endparam param mode caption = "Use for distance" enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag|""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" visible = @version < 220619 endparam param ctr caption = "Center" enum = "Origin" "Screen center" "Pixel" "Point" default = 0 hint = "Calculate distance from here." endparam complex param pt caption = "Point" default = (0,0) visible = @ctr == 3 endparam func F caption = "Function of distance" default = ident() hint = "Apply this function before exponential." endfunc float param a caption = "Scale" default = 1 ;min = 0 hint = "Large positive values give more emphasis to earlier iterations." endparam float param r caption = "Factor to change scale" default = 1 endparam Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DGaussianInteger(Distance) { ; The essential part of Kerry Mitchell's code in Standard.ucl ; 220921 allowed real and imag parts to use different types. ; backward compatible. public: func JLB_DGaussianInteger(Generic pparent) Distance(pparent) m_dist = new @dist(this) endfunc func Init(complex zz) m_dist.SetParams(0, true) endfunc float func Iterate(complex zz) complex temp if @p_std if @type == "round" temp = round(zz) elseif @type == "trunc" temp = trunc(zz) elseif @type == "floor" temp = floor(zz) elseif @type == "ceil" temp = ceil(zz) else temp = aux(real(zz), 4) + flip(aux(imag(zz), 4)) endif else temp = aux(real(zz), @xtype) + flip(aux(imag(zz), @ytype)) endif return m_dist.Iterate(zz-temp) endfunc float func aux(float v, int t) if t == 0 return round(v) elseif t == 1 return trunc(v) elseif t == 2 return floor(v) elseif t == 3 return ceil(v) else if v >= 0, return ceil(v), else, return floor(v), endif endif endfunc private: Distance m_dist default: title = "Gaussian Integer" rating = Recommended int param version caption = "Version" default = 220703 visible = @version < 220703 endparam bool param p_std caption = "Standard?" default = true endparam param xtype caption="Real Type" default=0 enum="round" "trunc" "floor" "ceil" "trunc*" visible = !@p_std endparam param ytype caption="Imag Type" default=0 enum="round" "trunc" "floor" "ceil" "trunc*" visible = !@p_std endparam param type caption="Integer Type" default=0 enum="round" "trunc" "floor" "ceil" "trunc*" visible = @p_std endparam Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DGrid(Distance) { public: func JLB_DGrid(Generic pparent) Distance(pparent) m_dist = new @dist(this) if @screen_center ctr = #center else ctr = @dcenter endif rot = cos(@angle*#pi/180) + flip(sin(@angle*#pi/180)) ; m_CTF = new ComplexToFloat2() if @type == "Rectangular" xgrid = abs(real(@grid)) ygrid = abs(imag(@grid)) endif endfunc func Init(complex zz) m_dist.SetParams(0, true) endfunc float func Iterate(complex zz) ; Note that floor(v) doesn't work if v > 2147483647, the largest positive integer. zz = ctr + rot * (zz - ctr) if @type == "Rectangular" if real(zz/xgrid) > 2e9 || imag(zz/ygrid) > 2e9 ; not enough precision return -1 endif int i = floor(real(zz)/xgrid) int j = floor(imag(zz)/ygrid) ; zz is in rectangle whose lower left is (i,j) ; dx and dy are non-negative float dx = real(zz) - i*xgrid float dy = imag(zz) - j*ygrid if dx > 0.5 * xgrid dx = xgrid - dx endif if dy > 0.5 * ygrid dy = ygrid - dy endif complex v if @do_scale v = dx/xgrid + flip(dy/ygrid) else v = dx + flip(dy) endif if @version < 220619 if real(v) <= imag(v) return real(v) else return imag(v) endif else ;return ComplexToFloat2.go(v/rot,(0,0), m_mode, true) return m_dist.Iterate(v/rot)^@pow endif else float r = cabs(zz) if r/@rgrid > 2e9 return -1 endif int i = floor(r/@rgrid) ; if i==0 use distance to first circle, float d1 = r - i * @rgrid ; not distance to center if d1 > 0.5 * @rgrid || i == 0 d1 = @rgrid - d1 endif ;choose angular spacing to make the grid more or less square int n = round(2 * #pi * (i + 0.5)) float agrid = 2 * #pi / n float theta = atan2(zz) if theta < 0, theta = theta + 2*#pi, endif int j = floor(theta / agrid) float d2 = theta - j * agrid if d2 > 0.5 * agrid d2 = agrid - d2 endif d2 = r * d2 if @version < 220619 if d1 > d2 d1 = d2 endif else if d1 <= d2 v = d1 * zz / r ;d1 = ComplexToFloat2.go(v,(0,0), m_mode, true) d1 = m_dist.Iterate(v) else v = d2 * zz * (0,1) / r ; right angle to zz ;d1 = ComplexToFloat2.go(v,(0,0), m_mode, true) d1 = m_dist.Iterate(v) endif endif if @do_pscale d1 = d1 / @rgrid endif return d1^@pow endif endfunc private: complex ctr complex rot float xgrid float ygrid Distance m_dist default: title = "Grid" rating = Recommended heading text = "Divide the plane into a grid. Calculate the distance to the \ nearest grid edge." endheading int param version caption = "Version" default = 220619 visible = @version < 220619 ; backwards compatible to 211012 endparam param type enum = "Rectangular" " Polar" caption = "Grid type" default = 0 endparam bool param screen_center caption = "Use screen center?" default = true endparam complex param dcenter caption = "Center" default = (0,0) visible = !@screen_center endparam complex param grid caption = "Grid spacing" default = (1,1) visible = @type == 0 endparam float param rgrid caption = "Radial spacing" default = 1 min = 0 visible = @type == 1 endparam float param angle caption = "Rotation angle" default = 0 visible = @type == 0 endparam bool param do_scale caption = "Scale to grid?" default = false visible = @type == 0 endparam bool param do_pscale caption = "Scale to radial spacing?" default = false visible = @type == 1 endparam float param pow caption = "Power" default = 1 endparam Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DGrid2(Distance) { ; ??? backwards compatible ??? public: func JLB_DGrid2(Generic pparent) Distance(pparent) m_dist = new @dist(this) if @screen_center ctr = #center else ctr = @dcenter endif rot = cos(@angle*#pi/180) + flip(sin(@angle*#pi/180)) xgrid = abs(real(@grid)) ygrid = abs(imag(@grid)) if @type == "Hexagonal" a3 = 1.5 * xgrid v = 0.5 * sqrt(3.0) * ygrid endif endfunc func Init(complex zz) m_dist.SetParams(0, true) if @type == "Rectangular" || @type == "Triangular" Get_Rect(zz, old_even, old_up) endif endfunc float func Iterate(complex zz) if @version < 220910 zz = ctr + rot * (zz - ctr) ; wrong! else zz = rot * (zz - ctr) endif ; Note that floor(v) doesn't work if v > 2147483647, the largest positive integer. complex grid_ctr float d1, float d2 complex tmp if @type == "Rectangular" || @type == "Triangular" complex za = abs(zz) if real(za/xgrid) > 2e9 || imag(za/ygrid) > 2e9 ; not enough precision return -1 endif ; zz is in the rectangle whose lower left is (i,j) ; find coordinates of the rectangle bool new_even, bool new_up Get_Rect(zz, new_even, new_up) if @type == "Rectangular" if @parity == "Odd" && new_even return -1 endif if @parity == "Even" && !new_even return -1 endif if @parity == "Change" && new_even == old_even return -1 endif if @parity == "Same" && new_even != old_even return -1 endif old_even = new_even if @type2 == "Center" grid_ctr = 0.5*(x1+x2) + flip(0.5*(y1+y2)) tmp = zz - grid_ctr elseif @type2 == "Edge" tmp = Geometry.ClosestPointToFigure(zz, 4, v1, v2, v3, v4) else;if @type2 == "Corner" tmp = Geometry.ClosestPointToVertex(zz, 4, v1, v2, v3, v4) endif else if @tri == "Up" && !new_up return -1 endif if @tri == "Down" && new_up return -1 endif if @tri == "Change" && new_up == old_up return -1 endif if @tri == "Same" && new_up != old_up return -1 endif old_up = new_up ; the point is in the triangle (v1, v2, v3) if @type2 == "Center" tmp = zz - (v1 + v2 + v3) / 3 elseif @type2 == "Edge" tmp = Geometry.ClosestPointToFigure(zz, 3, v1, v2, v3, v3) ; last arg not used else;if @type2 == "Corner" tmp = Geometry.ClosestPointToVertex(zz, 3, v1, v2, v3, v3) endif endif if @do_scale tmp = real(tmp)/xgrid + flip(imag(tmp)/ygrid) endif if @version < 220619 return MyMath.fmin(real(tmp), imag(tmp)) else float t = m_dist.Iterate(tmp/rot)^@pow return t endif elseif @type == "Hexagonal" int j = floor(abs(imag(zz))/v ) int i = floor(abs(real(zz))/a3) complex ctr1 complex ctr2 if j % 2 == 1 ; odd band if i % 2 == 1 ctr1 = (i )*a3 + flip((j )*v) ctr2 = (i+1)*a3 + flip((j+1)*v) else ctr1 = (i )*a3 + flip((j+1)*v) ctr2 = (i+1)*a3 + flip((j )*v) endif else ; even band if i % 2 == 1 ctr1 = (i )*a3 + flip((j+1)*v) ctr2 = (i+1)*a3 + flip((j )*v) else ctr1 = (i )*a3 + flip((j )*v) ctr2 = (i+1)*a3 + flip((j+1)*v) endif endif zz = abs(zz) tmp = ctr2-zz if |ctr1-zz| <= |ctr2-zz| tmp = ctr1-zz endif return m_dist.Iterate(tmp/rot)^@pow ; end heaxagonal else;if @type == "Polar" float r = cabs(zz) if r/@rgrid > 2e9 return -1 endif int i = floor(r/@rgrid) float r1 = i*@rgrid, float r2 = r1+@rgrid ; angular spacing chosen to make the grid more or less square, except inmost one, ; with each grid about the same area. int n = 6*i+3 float agrid = 2*#pi / n float offset = (@angle + i * @delta) * #pi/180 float theta = atan2(zz) + offset if theta < 0, theta = theta + 2*#pi, endif ;if theta >= 2*#pi, theta = theta - 2*#pi, endif int j = floor(theta / agrid) ; j >= 0 float theta1 = j*agrid - offset float theta2 = theta1+agrid ; okay if >2*#pi if @type2 == "Center" grid_ctr = (r1+0.5*@rgrid) * exp(flip((theta1+0.5*agrid))) tmp = zz - grid_ctr else v1 = r1 * exp(flip(theta1)) ; v1-v2 is straight v2 = r2 * exp(flip(theta1)) ; v2-v3 is outer arc v3 = r2 * exp(flip(theta2)) ; v3-v4 is straight v4 = r1 * exp(flip(theta2)) ; v4-v1 is inner arc if @type2 == "Corner" tmp = Geometry.ClosestPointToVertex(zz, 4, v1, v2, v3, v4) else;if @type == "Edge" tmp = Geometry.ClosestPointToLIneSegment(zz, v1, v2) d1 = |tmp| vv = zz - r2 * exp(flip((theta))) ; outer arc d2 = |vv| if d2 < d1 d1 = d2, tmp = vv endif complex vv = Geometry.ClosestPointToLIneSegment(zz, v3, v4) d2 = |vv| if d2 < d1 d1 = d2, tmp = vv endif vv = zz - r1 * exp(flip((theta))) ; inner arc d2 = |vv| if d2 < d1 tmp = vv endif endif ; polar endif d1 = m_dist.Iterate(tmp) if @do_pscale d1 = d1 / @rgrid endif return d1^@pow endif ; @type endfunc func Get_Rect(complex zz, bool &even, bool &up) float xx = real(zz) float i = floor(xx/xgrid) x1 = i * xgrid x2 = x1 + xgrid float yy = imag(zz) float j = floor(yy/ygrid) y1 = j * ygrid y2 = y1 + ygrid even = (i + j) % 2 == 0 v1 = x1 + flip(y1) ; lower left corner v2 = x2 + flip(y1) ; lower right corne v3 = x2 + flip(y2) ; upper right corner v4 = x1 + flip(y2) ; upper left corner if @type == "Triangular" ; which of the triangles contains the point? Assign v1, v2, v3 to its vertices up = false if @ttype == "Left" if Geometry.PointInTriangle(zz, v1, v2, v4) v3 = v4 ; 4-3 else ; |\| v1 = v4 ; 1-2 up = true endif else;if @ttype == "Right" if Geometry.PointInTriangle(zz, v1, v4, v3) v2 = v4 ; 4-3 up = true; |/| ; 1-2 ;else it's in the original 1, 2, 3 endif endif endif endfunc private: complex ctr complex rot float xgrid, float ygrid complex v1, complex v2, complex v3, complex v4 bool old_even, bool old_up float x1, float x2 float y1, float y2 Distance m_dist float a3, float v default: title = "Grid2" rating = Recommended heading text = "Divide the plane into a grid. Calculate the distance to the \ nearest grid edge or grid center." endheading int param version caption = "Version" default = 220910 visible = @version < 220910 ; backwards compatible to 211012 endparam heading text = "(Current is 2220910.)" visible = @version < 220910 endheading bool param screen_center caption = "Use screen center?" default = true endparam complex param dcenter caption = "Center" default = (0,0) visible = !@screen_center endparam param type enum = "Rectangular" "Polar" "Hexagonal" "Triangular" caption = "Grid type" default = 0 endparam param parity enum = "Even" "Odd" "Either" "Change" "Same" caption = "Which rectangles?" default = 2 visible = @type == 0 endparam param ttype enum = "Left" "Right" caption = "Split the rectangle" default = 0 visible = @type == "Triangular" endparam param tri enum = "Up" "Down" "Either" "Change" "Same" caption = "Which half" default = 2 visible = @type == "Triangular" endparam param type2 enum = "Edge" "Center" "Corner" caption = "Distance to" default = 0 visible = @type !=2 endparam complex param grid caption = "Grid spacing" default = (1,1) visible = @type == 0 || @type >= 2 endparam float param rgrid caption = "Radial spacing" default = 1 min = 0 visible = @type == 1 endparam float param angle caption = "Rotation angle" default = 0 endparam float param delta caption = "Delta angle" default = 0 visible = @type == 1 endparam bool param do_scale caption = "Scale to grid?" default = false visible = @type == 0 || @type == 3 endparam bool param do_pscale caption = "Scale to radial spacing?" default = false visible = @type == 1 endparam float param pow caption = "Power" default = 1 endparam heading endheading Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DLines(Distance) { ; line is a*x + b*y + c = 0 ; a*real(zz) + b*imag(zz) + c = d public: func JLB_DLines(Generic pparent) float t = @angle1*(#pi/180) a1 = sin(t) b1 = -cos(t) c1 = -real(@point1)*sin(t) + imag(@point1)*cos(t) if @num == " 2" t = @angle2*(#pi/180) a2 = sin(t) b2 = -cos(t) c2 = -real(@point2)*sin(t) + imag(@point2)*cos(t) endif endfunc float func Iterate(complex zz) float d = -1 bool okay float dist1 = a1 * real(zz) + b1 * imag(zz) + c1 if @num == " 1" if @side == " +" okay = dist1 > 0 elseif @side == " -" okay = dist1 < 0 else ; Either side okay = true endif if okay d = (abs(dist1)/@scale)^@pow endif else float dist2 = a2 * real(zz) + b2 * imag(zz) + c2 if @sides == 0 okay = dist1 > 0 && dist2 > 0 elseif @sides == 1 okay = dist1 > 0 && dist2 < 0 elseif @sides == 2 okay = dist1 < 0 && dist2 > 0 elseif @sides == 3 okay = dist1 < 0 && dist2 < 0 else okay = true endif if okay d = choose(dist1, dist2) endif endif if okay d = (d/@scale)^@pow endif return d endfunc float func choose(float v1, float v2) if @which == "Larger" if abs(v1) > abs(v2) return abs(v1) else return abs(v2) endif else if abs(v1) < abs(v2) return abs(v1) else return abs(v2) endif endif endfunc private: float a1,float b1, float c1 float a2,float b2, float c2 default: title = "Lines" rating = notRecommended heading text = "Lines are defined by a point and an angle. Use iterations only when z is on the \ specified positive or negative side of the line(s)." endheading int param version caption = "Current is 220619" default = 220619 visible = @version < 220619 endparam param num caption = "1 or 2 lines?" enum = " 1" " 2" default = 0 endparam param which caption = "Smaller / larger distance?" enum = " Smaller" "Larger" default = 0 visible = @num == " 2" hint = "Distances are calculated to each line. Color using the smaller or larger of the two." endparam complex param point1 caption = "Point on line 1" default = (0,0) endparam float param angle1 caption = "Angle of line 1" default = 45 endparam param side caption = "Pos. or neg. side" enum = " +" " -" " Either" default = 0 visible = @num == " 1" endparam complex param point2 caption = "Point on line 2" default = (0,0) visible = @num == " 2" endparam float param angle2 caption = "Angle of line 2" default = 0 ; min = 0 ; max = 180 visible = @num == " 2" endparam param sides caption = "Pos. or neg. sides" enum = " + +" " + -" " - +" " - -" " Any" default = 0 visible = @num == " 2" endparam float param scale caption = "Scale (> 0)" default = 2.0 min = 1e-30 endparam float param pow caption = "Distance power" default = 1 hint = "Positive values emphasize iterations with z farther from the line(s); \ negative values emphasize iterations with z nearer to the line(s)." endparam } class JLB_DLines2(Distance) { ; line is a*x + b*y + c = 0 ; a*real(zz) + b*imag(zz) + c = d ; 230126 added "Change" option; backwards compatible. public: func JLB_DLines2(Generic pparent) Distance(pparent) m_dist = new @dist(this) float t = @angle1*(#pi/180) a1 = sin(t) b1 = -cos(t) c1 = -real(@point1)*sin(t) + imag(@point1)*cos(t) if @num == " 2" t = @angle2*(#pi/180) a2 = sin(t) b2 = -cos(t) c2 = -real(@point2)*sin(t) + imag(@point2)*cos(t) endif endfunc func Init(complex zz) m_dist.SetParams(0, true) float tmp old_side = Get_Side(zz, a1, b1, c1, tmp) if @num == " 2" old_side = old_side + 10 * Get_Side(zz, a2, b2, c2, tmp) endif endfunc float func Iterate(complex zz) float d = -MyMath.fmax( 1, |zz| ) float d1, float d2 bool okay new_side = Get_Side(zz, a1, b1, c1, d1) if @num == " 1" if @side == " +" okay = d1 >= 0 elseif @side == " -" okay = d1 < 0 elseif @side == "Either" okay = true elseif @side == "Change" okay = old_side != new_side else;if @side == "Same" okay = old_side == new_side endif if okay d1 = abs(d1) zz = (a1+flip(b1)) * d1 endif else new_side = new_side + 10 * Get_Side(zz, a2, b2, c2, d2) if @sides == 0 okay = d1 > 0 && d2 > 0 elseif @sides == 1 okay = d1 > 0 && d2 < 0 elseif @sides == 2 okay = d1 < 0 && d2 > 0 elseif @sides == 3 okay = d1 < 0 && d2 < 0 elseif @sides == "Any" okay = true elseif @sides == "Change" okay = old_side != new_side else;if @sides == "Same" okay = old_side == new_side endif if okay d1 = abs(d1) d2 = abs(d2) if d1 <= d2 zz = (a1+flip(b1)) * d1 else zz = (a2+flip(b2)) * d2 endif endif endif if okay d = m_dist.Iterate(zz)/@scale bool neg = ( d < 0 ) d = abs(d)^@pow if neg, d = -d, endif endif return d endfunc int func Get_Side(complex zz, float a, float b, float c, float &d) float x = real(zz) float y = imag(zz) d = a * x + b * y + c if d >= 0 return 1 else return 0 endif endfunc private: float a1,float b1, float c1 float a2,float b2, float c2 Distance m_dist int old_side default: title = "Lines2" rating = Recommended heading text = "Lines are defined by a point and an angle. Use iterations only when z is on the \ specified positive or negative side of the line(s)." endheading int param version caption = "Current is 220619" default = 220619 visible = @version < 220619 endparam param num caption = "1 or 2 lines?" enum = " 1" " 2" default = 0 endparam complex param point1 caption = "Point on line 1" default = (0,0) endparam float param angle1 caption = "Angle of line 1" default = 45 endparam param side caption = "Pos. or neg. side" enum = " +" " -" "Either" "Change" "Same" default = 0 visible = @num == " 1" endparam complex param point2 caption = "Point on line 2" default = (0,0) visible = @num == " 2" endparam float param angle2 caption = "Angle of line 2" default = 0 ; min = 0 ; max = 180 visible = @num == " 2" endparam param sides caption = "Pos. or neg. sides" enum = " + +" " + -" " - +" " - -" "Any" "Change" "Same" default = 0 visible = @num == " 2" endparam float param scale caption = "Scale (> 0)" default = 2.0 min = 1e-30 endparam float param pow caption = "Distance power" default = 1 hint = "Positive values emphasize iterations with z farther from the line(s); \ negative values emphasize iterations with z nearer to the line(s)." endparam Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DQuad(Distance) { public: func JLB_DQuad(Generic pparent) ; m_CTF = new ComplexToFloat() endfunc func Init(complex zz) if @screen_center ctr = #center else ctr = @dcenter endif rot = cos(@angle*#pi/180) + flip(sin(@angle*#pi/180)) endfunc float func Iterate(complex zz) float d = -1 bool okay = false complex v if @version < 220910 v = ctr + rot * (zz - ctr) ; wrong! else v = rot * (zz - ctr) endif if @quad == " 1" okay = real(v) > 0 && imag(v) > 0 elseif @quad == " 2" okay = real(v) < 0 && imag(v) > 0 elseif @quad == " 3" okay = real(v) < 0 && imag(v) < 0 else;if @quad == " 4" okay = real(v) > 0 && imag(v) < 0 endif if okay d = ComplexToFloat.go(zz, m_mode, true) endif return d endfunc private: complex ctr complex rot ; ComplexToFloat m_ctf default: title = "Quadrant" rating = notRecommended heading text = "(Quadrant2 is the preferred plug-in)." endheading int param version caption = "Version" default = 220910 visible = @version < 220910 ; backwards compatible to 211012 endparam heading text = "(Current is 2220910.)" visible = @version < 220910 endheading bool param screen_center caption = "Use screen center?" default = true endparam complex param dcenter caption = "Center" default = (0,0) visible = !@screen_center endparam float param angle caption = "Rotation angle" default = 0 endparam param quad caption = "Quadrant" enum = " 1" " 2" " 3" " 4" default = 0 hint = "1 is upper right, 2 is upper left, 3 is lower left, 4 is lower right." endparam } class JLB_DQuad2(Distance) { ; 211124 added "Any" option ; 230126 added "Change" option; backwards compatible. public: func JLB_DQuad2(Generic pparent) Distance(pparent) m_dist = new @dist(this) if @screen_center ctr = #center else ctr = @dcenter endif rot = cos(@angle*#pi/180) + flip(sin(@angle*#pi/180)) endfunc func Init(complex zz) m_dist.SetParams(0, m_abs_value) old_quad = get_quad(rot * (zz - ctr)) endfunc float func Iterate(complex zz) if @version < 220910 zz = ctr + rot * (zz - ctr) ; wrong! else zz = rot * (zz - ctr) endif float d = -MyMath.fmax( 1, |zz|) int new_quad = get_quad(zz) bool okay = false if @quad == "Any" okay = true elseif @quad == "Change" okay = old_quad != new_quad elseif @quad == "Same" okay = old_quad == new_quad else okay = @quad == new_quad endif if okay int mmode = m_mode if @version < 220619 mmode = @mode ; set in Distance Coloring d = ComplexToFloat2.go(zz/rot,(0,0), mmode, true) else d = m_dist.Iterate(zz/rot) endif bool neg = ( d < 0 ) d = abs(d)^@pow if neg, d = -d, endif endif return d endfunc int func get_quad(complex zz) float x = real(zz) float y = imag(zz) if x >= 0 && y >= 0 return 0 elseif x <= 0 && y >= 0 return 1 elseif x <= 0 && y <= 0 return 2 else return 3 endif endfunc private: complex ctr complex rot Distance m_dist int old_quad default: title = "Quadrant2" rating = Recommended heading text = "Distance to a quadrant boundary. Optionally use only iterations that are \ in a specified quadrant." endheading int param version caption = "Version" default = 220910 visible = @version < 220910 ; backwards compatible to 211012 endparam heading text = "(Current is 2220910.)" visible = @version < 220910 endheading param mode caption = "Use for distance" enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag|""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" visible = @version < 220619 endparam bool param screen_center caption = "Use screen center?" default = true endparam complex param dcenter caption = "Center" default = (0,0) visible = !@screen_center endparam float param angle caption = "Rotation angle" default = 0 endparam param quad caption = "Quadrant" enum = " 1" " 2" " 3" " 4" "Any" "Change" "Same" default = 0 hint = "1 is upper right, 2 is upper left, 3 is lower left, 4 is lower right." endparam float param pow caption = "Distance power" default = 1 hint = "Positive values emphasize iterations with z farther from the line(s); \ negative values emphasize iterations with z nearer to the line(s)." endparam Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DSpiral(Distance) { ; log spiral: r = a * exp(b * angle) ; Power spiral: r = a * angle^p public: func JLB_DSpiral(Generic pparent) m_dist = new @dist(this) if @screen_center sctr = #center else sctr = @ctr endif aa = abs(@a); scale = 1 if @do_scale scale = aa endif twopi = 2*#pi endfunc func Init(complex zz) m_dist.SetParams(0, true) endfunc float func Iterate(complex zz) zz = zz - sctr float theta = atan2(zz) if @a > 0 if theta < 0, theta = theta + twopi, endif ; theta in [0, twopi) else if theta > 0, theta = theta - twopi, endif ; theta in (-twopi, 0] theta = abs(theta) endif float r = cabs(zz) float tmp if @type == 0 tmp = ((r/aa)^(1/@p) - theta) / twopi else tmp = (log(r/aa)/@b - theta) / twopi endif int k = floor(tmp) if k < 0, k = 0, endif ; inside first arm or lack of precision float r1, float r2 if @type == 0 r1 = aa * (theta + k*twopi )^@p r2 = aa * (theta + k*twopi + twopi)^@p else r1 = aa * exp(@b*(theta + k*twopi )) r2 = aa * exp(@b*(theta + k*twopi + twopi) ) endif float d if k == 0 ; inside first arm d = 1 - r/r2 if d < 0 || d > 1 ; failure because of lack of precision return -1 endif else d = (r - r1) / (r2 - r1) if d < 0 || d > 1 ; failure because of lack of precision return -1 endif if d > 0.5, d = 1 - d, endif ; distance to closer arm endif ; d is between 0 and 1 d = d * scale zz = d * zz / cabs(zz) ;d = ComplexToFloat2.go(zz, (0,0), m_mode, true) d = m_dist.Iterate(zz) return d endfunc private: float scale float twopi complex sctr float aa Distance m_dist default: title = "Spiral" rating = Recommended heading text = "Color by distance to closest arm of the spiral." endheading param type caption = "Spiral type" default = 0 enum = "power" "log" endparam bool param screen_center caption = "Use screen center?" default = true endparam param ctr caption = "Spiral center" default = (0,0) visible = !@screen_center endparam float param a caption = "Spiral multiplier" default = 1 hint = "If negative, spiral goes in the opposite direction." endparam float param p caption = "Spiral power" min = 1e-4 default = 1 hint = "Useful values mostly between 0.5 and 2." visible = @type == 0 endparam float param b caption = "Coefficient" default = 0.1 min = 1e-6 visible = @type == 1 endparam bool param do_scale caption = "Scale by multiplier?" default = true visible = @a != 1 endparam Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DSquare(Distance) { ; 211124 added"Either" option ; 211229 added Rectangle option ; 230128 added "Change" and "Same" options; backwards compatible public: func JLB_DSquare(Generic pparent) Distance(pparent) m_dist = new @dist(this) if @screen_center ctr = #center else ctr = @center endif rot = cos(@angle*#pi/180) + flip(sin(@angle*#pi/180)) if @type == "Square" h = v = 0.5 * @side else h = 0.5 * @horiz v = 0.5 * @vert endif scale = 1 if @do_scale if @version >= 220720 scale = 2*sqrt(h*v) else scale = h if v > h, scale = v, endif endif endif size = sqrt(h*v) v1 = ctr + h + flip(v) ; upper right corner v2 = ctr + h - flip(v) ; lower right corner v3 = ctr - h - flip(v) ; lower left corner v4 = ctr - h + flip(v) ; upper left corner endfunc func Init(complex zz) m_dist.SetParams(0, m_abs_value) old_inside = IsInside(rot * (zz - ctr)) endfunc float func Iterate(complex zz) zz = rot * (zz - ctr) bool new_inside = IsInside(zz) if @version < 220916 zz = ctr + zz ; wrong endif float d = -MyMath.fmax(1, |zz|) if @version < 230811 && @method == "Original" float dist = cabs(zz - ctr) if new_inside if @in_out_old == "Inside"|| @in_out_old == "Either" d = (1-dist/h)^@pow endif return d else; if x > h || y > v if @in_out_old == "Outside" || @in_out_old == "Either" d = (1-h/dist)^@pow endif endif else;if @method == "Corrected" bool okay = false if @in_out == "Either" okay = true elseif @in_out == "Inside" okay = new_inside elseif @in_out == "Outside" okay = !new_inside elseif @in_out == "Change" okay = old_inside != new_inside elseif @in_out == "Same" okay = old_inside == new_inside endif if @version >= 230808 && !okay if @in_out == "In->In" okay = old_inside && new_inside elseif @in_out == "In->Out" okay = old_inside && !new_inside elseif @in_out == "Out->In" okay = !old_inside && new_inside elseif @in_out == "Out->Out" okay = !old_inside && !new_inside endif endif old_inside = new_inside if okay complex pv = Geometry.ClosestPointToFigure(zz, 4, v1, v2, v3, v4) if @version >= 230811 pv = zz * cabs(pv)/size ; to be like circle2 endif d = m_dist.Iterate(pv)/scale bool neg = ( d < 0 ) d = abs(d)^@pow if neg, d = -d, endif endif endif return d endfunc bool func IsInside(complex zz) float x = abs(real(zz)) float y = abs(imag(zz)) return (x <= h && y <= v) endfunc private: complex ctr complex rot float h float v float size float scale complex v1, complex v2, complex v3, complex v4 Distance m_dist bool old_inside default: title = "Square" rating = Recommended heading text = "Distance to a square. Optionally use only those iterations that are \ inside or outside the square." endheading int param version caption = "Version" default = 230811 visible = @version < 230811 endparam heading text = "(Current is 230811.)" visible = @version < 230811 endheading param type caption = "Square / Rectangle" enum = "Square" "Rectangle" default = 0 endparam float param side caption = "Square side length" default = 2.0 min = 0 visible = @type == 0 endparam float param horiz caption = "Rectangle x length" default = 2 min = 0 visible = @type == 1 endparam float param vert caption = "Rectangle y length" default = 2 min = 0 visible = @type == 1 endparam bool param screen_center caption = "Use screen center?" default = true endparam param center caption = "Square center" default = (0,0) visible = !@screen_center endparam float param angle caption = "Rotation angle" default = 0 endparam param in_out_old caption = "Inside/outside the Square" enum = "Inside" "Outside" "Either" default = 2 ; changed 230102, was 0 visible = @method == 0 endparam param in_out caption = "Inside/outside the Square" enum = "Inside" "Outside" "Either" "Change" "Same" \ "In->In" "In->Out" "Out->In" "Out->Out" default = 2 visible = @version >= 230811 || @method == 1 endparam param method caption = "Distance method" enum = "Original" "Corrected" default = 1 visible = @version < 230811 endparam float param pow caption = "Distance power" default = 1 min = 0 ; hint = "Higher values emphasize iterations with z away from the Square edge." endparam bool param do_scale caption = "Scale by size?" default = true endparam Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DSimple(Distance) { ; Take a complex number as input and return a float public: func JLB_Dsimple(Generic pparent) ; m_CTF = new ComplexToFloat() endfunc float func Iterate(complex zz) float ans = ComplexToFloat.go(zz, m_mode, m_abs_value) return ans endfunc private: ; ComplexToFloat2 m_CTF default: title = "Simple" rating = notRecommended heading text = "(Simple2 is the preferred plug-in.)" endheading } class JLB_DSimple2(Distance) { ; Take a complex number as input and return a float public: func JLB_DSimple2(Generic pparent) Distance(pparent) if @version < 220622 ctr = (0,0) else if @screen_center ctr = #center else ctr = @center endif endif if @version >=220703 m_dist = new @dist(this) endif endfunc func Init(complex zz) if @version >=220703 m_dist.SetParams(0, true) endif endfunc float func Iterate(complex zz) if @version >= 220703 return m_dist.Iterate(zz) endif int mmode = m_mode if @version < 220619 mmode = @mode endif float ans = ComplexToFloat2.go(zz-ctr,(0,0), mmode, true) return ans endfunc private: complex ctr Distance m_dist default: title = "Simple2" rating = Recommended heading text = "Color according to a combination of real and imaginary parts of z." endheading int param version caption = "Version" default = 220703 visible = @version < 220703 endparam heading text = "(Current is 220703.)" visible = @version < 220703 endheading bool param screen_center caption = "Use screen center?" default = true visible = @version >= 220622 endparam complex param center caption = "Center" default = (0,0) visible = @version >= 220622 && !@screen_center endparam param mode caption = "Use for distance" enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag|""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" visible = @version < 220619 endparam Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DMultiple(Distance) { public: func JLB_DMultiple(Generic pparent) Distance(pparent) fDist1 = new @dist1(this) if @num >= 2 fDist2 = new @dist2(this) if @num >= 3 fDist3 = new @dist3(this) if @num >= 4 fDist4 = new @dist4(this) if @num >= 5 fDist5 = new @dist5(this) endif endif endif endif endfunc func Init(complex zz) fDist1.Init(zz) fDist1.SetParams(0, true) if @num >= 2 fDist2.Init(zz) fDist2.SetParams(0, true) if @num >= 3 fDist3.Init(zz) fDist3.SetParams(0, true) if @num >= 4 fDist4.Init(zz) fDist4.SetParams(0, true) if @num >= 5 fDist5.Init(zz) fDist5.SetParams(0, true) endif endif endif endif endfunc float func Iterate(complex zz) bool okay_d = false float d = -1 float dd dd = fDist1.Iterate(zz) if dd > 0 d = dd, okay_d = true elseif @how == "All" return -1 endif if @num >= 2 dd = fDist2.Iterate(zz) if dd > 0 if okay_d if dd < d, d = dd, endif else d = dd, okay_d = true endif elseif @how == "All" return -1 endif endif if @num >= 3 dd = fDist3.Iterate(zz) if dd > 0 if okay_d if dd < d, d = dd, endif else d = dd, okay_d = true endif elseif @how == "All" return -1 endif endif if @num >= 4 dd = fDist4.Iterate(zz) if dd > 0 if okay_d if dd < d, d = dd, endif else d = dd, okay_d = true endif elseif @how == "All" return -1 endif endif if @num >= 5 dd = fDist5.Iterate(zz) if dd > 0 if okay_d if dd < d, d = dd, endif else d = dd, okay_d = true endif elseif @how == "All" return -1 endif endif return d endfunc private: Distance fDist1 Distance fDist2 Distance fDist3 Distance fDist4 Distance fDist5 default: title = "Multiple" rating = Recommended heading text = "Designed for methods that do Inside/Outside (Circle, Square, etc.), \ but usable for all." endheading int param version caption = "Version" default = 211021 visible = @version < 211021 endparam int param num caption = "How many (max 5)?" default = 2 min = 1 max = 5 endparam param how caption = "Require any or all?" enum = "Any" "All" hint = "Allow any distance within range, or require all destances to be within range" default = 0 endparam heading ; caption = "Distance method 1" endheading Distance param dist1 caption = "Method 1" default = JLB_DTri endparam heading ; caption = "Distance method 2" visible = @num >= 2 endheading Distance param dist2 caption = "Method 2" default = JLB_DSquare visible = @num >= 2 endparam heading ; caption = "Distance method 3" visible = @num >= 3 endheading Distance param dist3 caption = "Method 3" default = JLB_DSquare visible = @num >= 3 endparam heading ; caption = "Distance method 4" visible = @num >= 4 endheading Distance param dist4 caption = "Method 4" default = JLB_DSquare visible = @num >= 4 endparam heading ; caption = "Distance method 5" visible = @num >= 5 endheading Distance param dist5 caption = "Method 5" default = JLB_DSquare visible = @num >= 5 endparam } class JLB_DOverUnder(Distance) { public: func JLB_DOverUnder(Generic pparent) fDistOver = new @distOver(this) fDistUndr = new @distUndr(this) endfunc func Init(complex zz) fDistOver.Init(zz) fDistUndr.Init(zz) if @version < 220703 fDistOver.SetParams(@scoloring1, true) fDistUndr.SetParams(@scoloring2, true) endif endfunc float func Iterate(complex zz) float v = ComplexToFloat2.go(zz, (0,0), @mode, false) if v >= @crit return fDistOver.Iterate(zz) else return fDistUndr.Iterate(zz) endif endfunc private: Distance fDistOver Distance fDistUndr default: title = "OverUnder" rating = Recommended heading text = "Use a distance method depending on a critical value." hint = "Use real and imaginary parts of z to \ calculate value to compare with critical value." endheading int param version caption = "Version" default = 220703 visible = @version < 220703 endparam param mode caption = "Use for critical value" enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag|""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" endparam float param crit caption = "Critical value" default = 1 endparam heading endheading param scoloring1 caption = "Use for Over distance" enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag| (manh)""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" visible = @version < 220703 && @distOver != JLB_DTrap endparam Distance param distOver caption = "Over method" default = JLB_DSimple2 endparam heading endheading param scoloring2 caption = "Use for Under distance" enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag| (manh)""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" visible = @version < 220703 && @distUndr != JLB_DTrap endparam Distance param distUndr caption = "Under method" default = JLB_DCircle endparam } class JLB_DTri(Distance) { ; distance to nearest triangle vertex ; okay if the triangle is degenerate ; 211124 added "Either" option public: func JLB_DTri(Generic pparent) Distance(pparent) m_dist = new @dist(this) scale = 1 if @do_scale scale = (cabs(@v1-@v2) + cabs(@v2-@v3) + cabs(@v3-@v1) ) / 3 endif rot = cos(@angle*#pi/180) + flip(sin(@angle*#pi/180)) ctr = (@v1 + @v2 + @v3) / 3 endfunc func Init(complex zz) m_dist.SetParams(0, true) zz = ctr + rot * (zz - ctr) old_inside = Geometry.PointInTriangle(zz, @v1, @v2, @v3) endfunc float func Iterate(complex zz) ; if @version < 220910 zz = ctr + rot * (zz - ctr) ; wrong! ? ; else ; zz = rot * zz ; endif bool new_inside = Geometry.PointInTriangle(zz, @v1, @v2, @v3) if @method == "Original" if (new_inside && @in_out_old == "Outside") || (!new_inside && @in_out_old == "Inside") return -1 endif float d1 = cabs(zz - @v1) float d2 = cabs(zz - @v2) float d3 = cabs(zz - @v3) if d2 < d1, d1 = d2, endif if d3 < d1, d1 = d3, endif return d1 else float d = -1 bool okay = true ; "Either" if @in_out == "Inside" okay = new_inside elseif @in_out == "Outside" okay = !new_inside elseif @in_out == "Change" okay = old_inside != new_inside elseif @in_out == "Same" okay = old_inside == new_inside endif old_inside = new_inside if okay complex pv = Geometry.ClosestPointToFigure(zz, 3, @v1, @v2, @v3, @v3) float d = m_dist.Iterate(pv) bool neg = ( d < 0 ) d = abs(d/scale)^@pow if neg, d = -d, endif endif return d endif endfunc private: float scale complex rot complex ctr Distance m_dist bool old_inside default: title = "Triangle" rating = Recommended heading text = "Distance to a triangle. Optionally use only those iterations that are \ inside or outside the square." endheading int param version caption = "Version" default = 220910 visible = @version < 220910 endparam heading text = "(Current is 220910.)" visible = @version < 220910 endheading complex param v1 caption = "Vertex 1" default = (0,0) endparam complex param v2 caption = "Vertex 2" default = (2,0) endparam complex param v3 caption = "Vertex 3" default = (0,2) endparam float param angle caption = "Rotation angle" default = 0 endparam param in_out_old caption = "Inside/outside the Triangle" enum = "Inside" "Outside" "Either" default = 2 ; changed 230102, was 0 visible = @method == 0 endparam param in_out caption = "Inside/outside the Triangle" enum = "Inside" "Outside" "Either" "Change" "Same" default = 2 visible = @method == 1 endparam param method caption = "Distance method" enum = "Original" "Corrected" default = 1 ; visible = @in_out < 2 endparam float param pow caption = "Distance power" default = 1 min = 0 visible = @method == 1 ; hint = "Higher values emphasize iterations with z away from the Square edge." endparam bool param do_scale caption = "Scale by side length?" default = false visible = @method == 1 endparam Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DTriangle(Distance) { public: func JLB_DTriangle(Generic pparent) Distance(pparent) m_dist = new @dist(this) endfunc func Init(complex zz) m_dist.SetParams(0, true) first = true endfunc ; The insides of Triangle Inequality Average float func Iterate(complex zz) if first first = false ; to agree with the original return -1 endif ; float ac = ComplexToFloat2.go(#pixel, (0,0), m_mode, true) ; float bc = ComplexToFloat2.go(zz-#pixel, (0,0), m_mode, true) ; float cc = ComplexToFloat2.go(zz, (0,0), m_mode, true) float ac = m_dist.Iterate(#pixel) float bc = m_dist.Iterate(zz-#pixel) float cc = m_dist.Iterate(zz) float least = abs(bc - ac) float most = bc + ac if most <= least return -1 endif return (cc - least) / (most - least) endfunc private: bool first Distance m_dist default: title = "TIA Distance" rating = Recommended ; heading ; text = "Standard Triangle Inequality Average coloring. Using TIA type as the Final smooth \ ; may be useful." ; endheading Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DCircle(Distance) { ;211124 added "Either" option ;211229 added Ellipse option and "use screen center" option ;220916 added option to change h and v by @ratio each iteration. backwards compatible. public: func JLB_DCircle(Generic pparent) Distance(pparent) m_dist = new @dist(this) if @screen_center ctr = #center else ctr = @center endif scale = 1 if @type == "Circle" h = v = @r else h = @horiz, v = @vert endif if @do_scale if @version >= 220720 scale = sqrt(h*v) else scale = MyMath.fmax(h, v) endif endif rot = cos(@angle*#pi/180) + flip(sin(@angle*#pi/180)) endfunc func Init(complex zz) m_dist.SetParams(0, true) endfunc float func Iterate(complex zz) float x, float y zz = rot * (zz - ctr) x = abs(real(zz)) y = abs(imag(zz)) int iter = GetIter( ) float rr = @ratio^iter float hh = h*rr float vv = v*rr float ss = scale*rr float q= (x/hh)^@p + (y/vv)^@p if @version < 220619 ; this version has errors float d = -1 q= q^(1/@p) if q <= 1 && (@in_out == "Inside" || @in_out == "Either") d = ((1-q)/ss)^@pow endif if q > 1 && (@in_out == "Outside" || @in_out == "Either") d = ((1-1/q)/ss)^@pow endif return d else float d1 = -1 if q <= 1 && @in_out != "Outside" if q == 0 d1 = MyMath.fmin(hh, vv) else zz = (1/q-1) * zz d1 = m_dist.Iterate(zz/rot)/ss endif endif float d2 = -1 if q > 1 && @in_out != "Inside" zz = (q-1) * zz d2 = m_dist.Iterate(zz/rot)/ss endif ; at least one of d1 and d2 is non-negative if d2 < 0, return d1, endif if d1 < 0, return d2, endif return MyMath.fmin(d1, d2) endif endfunc private: complex ctr float h float v float scale complex rot Distance m_dist default: title = "Circle" rating = notRecommended heading text = "Distance to a circle or ellipse. Optionally use only those iterations that are \ inside or outside the shape." endheading int param version caption = "Version" default = 220720 visible = @version < 220720 endparam heading text = "(Curent is 220720.}" visible = @version < 220720 endheading param type caption = "Circle or ellipse" enum = "Circle" "Ellipse" default = 0 endparam float param p caption = "Shape power" default = 2 min = 0 hint = "Power = 2 for circle, < 2 for indented, < 1 for astroid, > 2 for shapes nearing a square." endparam bool param screen_center caption = "Use screen center?" default = true endparam param center caption = "Center" default = (0,0) visible = !@screen_center endparam float param r caption = "Circle radius" default = 2.0 min = 0 visible = @type == 0 endparam float param horiz caption = "Ellipse x value" default = 2.0 min = 0 visible = @type == 1 endparam float param vert caption = "Ellipse y value" default = 2.0 min = 0 visible = @type == 1 endparam float param ratio caption = "Ratio" hint = "Change circle radius / ellipse values by this factor each iteration." default = 1 min = 0 endparam float param angle caption = "Rotation angle" default = 0 visible = @p != 2.0 || (@type == 1 && @horiz != @vert) endparam param in_out caption = "Inside/outside?" enum = "Inside" "Outside" "Either" default = 2 ; changed 230102, was 0 endparam float param pow caption = "Distance power" default = 1 min = 0 visible = @version < 220619 ; hint = "Higher values emphasize iterations with z away from the Square edge." endparam bool param do_scale caption = "Scale by size?" default = false ;visible = @version < 220619 endparam heading endheading Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DCircle2(Distance) { ; 230126 added "Change" option; backwards compatible. public: func JLB_DCircle2(Generic pparent) Distance(pparent) m_dist = new @dist(this) if @screen_center, ctr = #center, else, ctr = @center, endif if @type == "Circle", h = v = @r, else, h = @horiz, v = @vert, endif scale = 1 if @do_scale scale = 2 * MyMath.fmax(h, v) ; 2 to be like Square scaling endif rot = cos(@angle*#pi/180) + flip(sin(@angle*#pi/180)) endfunc func Init(complex zz) m_dist.SetParams(0, m_abs_value) float r old_inside = IsInside(rot * (zz - ctr), r) ; calculates r endfunc ;$define debug float func Iterate(complex zz) complex pix = (1,0) zz = rot * (zz - ctr) float d = -MyMath.fmax(1, |zz|) float r bool new_inside = IsInside(zz, r) ; calculates r complex t bool okay = false if @in_out == "Either" okay = true elseif @in_out == "Inside" okay = new_inside elseif @in_out == "Outside" okay = !new_inside elseif @in_out == "Change" okay = new_inside != old_inside elseif @in_out == "Same" okay = new_inside == old_inside endif if @version >= 230808 && !okay if @in_out == "In->In" okay = old_inside && new_inside elseif @in_out == "In->Out" okay = old_inside && !new_inside elseif @in_out == "Out->In" okay = !old_inside && new_inside elseif @in_out == "Out->Out" okay = !old_inside && !new_inside endif endif if |#pixel-pix|<1e-5 print(#pixel, " zz ", zz, " r ", r, " o ", old_inside, " n ", new_inside," ", okay) endif if okay t = zz * (1-r) / rot if @version >= 230808 && !new_inside t = -t endif d = m_dist.Iterate(t)/scale old_inside = new_inside bool neg = ( d < 0 ) d = abs(d)^@pow if neg, d = -d, endif if |#pixel-pix|<1e-5 print(" t ", t, " d ", d) endif endif return d endfunc bool func IsInside(complex zz, float &r) float x = abs(real(zz)) float y = abs(imag(zz)) r= ( (x/h)^@p + (y/v)^@p ) ^ (1/@p) return r <= 1 endfunc private: complex ctr float h float v float scale complex rot bool old_inside Distance m_dist default: title = "Circle2" rating = Recommended int param version caption = "Version" default = 230808 visible = @version < 230808 endparam heading text = "(Curent is 230808.}" visible = @version < 230808 endheading ; param using ; caption = "Use type" ; enum = "Coloring" "Formula" ; default = 0 ; endparam param type caption = "Circle or ellipse" enum = "Circle" "Ellipse" default = 0 endparam float param p caption = "Shape power" default = 2 min = 0 hint = "Power = 2 for circle, < 2 for indented, < 1 for astroid, > 2 for shapes nearing a square." endparam bool param screen_center caption = "Use screen center?" default = true endparam param center caption = "Center" default = (0,0) visible = !@screen_center endparam float param r caption = "Circle radius" default = 2.0 min = 0 visible = @type == 0 endparam float param horiz caption = "Ellipse x value" default = 2.0 min = 0 visible = @type == 1 endparam float param vert caption = "Ellipse y value" default = 2.0 min = 0 visible = @type == 1 endparam float param angle caption = "Rotation angle" default = 0 visible = @p != 2.0 || (@type == 1 && @horiz != @vert) endparam param in_out caption = "Inside/outside?" enum = "Inside" "Outside" "Either" "Change" "Same" \ "In->In" "In->Out" "Out->In" "Out->Out" hint = "Change: use iterations where new inside is different from \ old inside. Same: use iterations where new inside equals \ old inside" default = 2 ; changed 230102, was 0 endparam float param pow caption = "Distance power" default = 1 min = 0 ; hint = "Higher values emphasize iterations with z away from the circle edge." endparam bool param do_scale caption = "Scale by size?" default = false endparam heading ;text = "Use for distance:" endheading Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_DCircles(Distance) { public: func Init(complex zz) r[1] = @r1 if @polar ctr[1] = @rad1*(cos(@ang1*#pi/180) + flip(sin(@ang1*#pi/180))) else ctr[1] = @dctr1 endif if @num >= 2 r[2] = @r2 if @polar ctr[2] = @rad2*(cos(@ang2*#pi/180) + flip(sin(@ang2*#pi/180))) else ctr[2] = @dctr2 endif if @num >= 3 r[3] = @r3 if @polar ctr[3] = @rad3*(cos(@ang3*#pi/180) + flip(sin(@ang3*#pi/180))) else ctr[3] = @dctr3 endif if @num >= 4 r[4] = @r4 if @polar ctr[4] = @rad4*(cos(@ang4*#pi/180) + flip(sin(@ang4*#pi/180))) else ctr[4] = @dctr4 endif if @num >= 5 r[5] = @r5 if @polar ctr[5] = @rad4*(cos(@ang5*#pi/180) + flip(sin(@ang5*#pi/180))) else ctr[5] = @dctr5 endif endif endif endif endif endfunc float func Iterate(complex zz) float d = -1 float dd int n = 1 while n <= @num float x, float y, float rz x = abs(real(zz-ctr[n])) y = abs(imag(zz-ctr[n])) rz = (x^@shape + y^@shape)^(1/@shape) if @in_out == "Inside" if rz < r[n] if @how == "Original" dd = (1 - rz/r[n])^@pow else dd = ((r[n]-rz)/r[n]) endif if d < 0 ; first one that's inside d = dd else if dd < d, d = dd, endif endif endif else;if @in_out == "Outside" if rz > r[n] if @how == "Original" dd = (1 - r[n]/rz)^@pow else dd = ((rz-r[n])/r[n]) endif if d < 0 d = dd else if dd < d, d = dd, endif endif endif endif n = n + 1 endwhile return d endfunc private: complex ctr[6] float r[6] default: title = "Circles" rating = notRecommended heading text = "Use only the iterations that are inside or outside the circle(s), as specified." endheading int param num caption = "How many (max 5)?" default = 1 min = 1 max = 5 endparam float param shape caption = "Circle shape" default = 2 min = 0 hint = "Use 2 for circle, < 2 for indented, > 2 for shapes nearing a square" endparam param how caption = "How to calculate" enum = "Original" "Corrected" default = 0 endparam bool param polar caption = "Polar coords, for centers?" default = false hint = "Centers may be specified by real and imaginary part or using distance and angle." endparam heading endheading float param r1 caption = "Circle #1 size" default = 2.0 min = 0 endparam param dctr1 caption = "Circle #1 ctr" default = (0,0) visible = !@polar endparam float param rad1 caption = "Circle #1 ctr. distance" default = 2 visible = @polar endparam float param ang1 caption = "Circle #1 ctr. angle" default = 0 visible = @polar endparam float param r2 caption = "Circle #2 size" default = 1.0 min = 0 visible = @num >= 2 endparam param dctr2 caption = "Circle #2 center" default = (2,0) visible = @num >= 2 && !@polar endparam float param rad2 caption = "Circle #2 ctr. distance" default = 2 visible = @num >= 2 && @polar endparam float param ang2 caption = "Circle #2 ctr. angle" default = 60 visible = @num >= 2 && @polar endparam float param r3 caption = "Circle #3 size" default = 1.0 min = 0 visible = @num >= 3 endparam param dctr3 caption = "Circle #3 center" default = (-2,0) visible = @num >= 3 && !@polar endparam float param rad3 caption = "Circle #3 ctr. distance" default = 2 visible = @num >= 3 && @polar endparam float param ang3 caption = "Circle #3 ctr. angle" default = 120 visible = @num >= 3 && @polar endparam float param r4 caption = "Circle #4 size" default = 1.0 min = 0 visible = @num >= 4 endparam param dctr4 caption = "Circle #4 center" default = (0,2) visible = @num >= 4 && !@polar endparam float param rad4 caption = "Circle #4 ctr. distance" default = 2 visible = @num >= 4 && @polar endparam float param ang4 caption = "Circle #4 ctr. angle" default = -120 visible = @num >= 4 && @polar endparam float param r5 caption = "Circle #5 size" default = 1.0 min = 0 visible = @num >= 5 endparam param dctr5 caption = "Circle #5 center" default = (0,-2) visible = @num >= 5 && !@polar endparam float param rad5 caption = "Circle #5 ctr. distance" default = 2 visible = @num >= 5 && @polar endparam float param ang5 caption = "Circle #5 ctr. angle" default = -60 visible = @num >= 5 && @polar endparam heading endheading param in_out caption = "Inside/outside the circles" enum = "Inside" "Outside" default = 0 hint = "Inside means inside any 'circle'; outside means outside any 'circle'." endparam float param pow caption = "Distance power" default = 1 min = 0 hint = "Higher values emphasize iterations with z away from the circle edge." visible = @how == 0 endparam } class JLB_DTrap(Distance) { ; Use a trap shape to calculate a distance public: import "common.ulb" import "Standard.ulb" func JLB_DTrap(Generic pparent) fShape = new @shape(this) endfunc func Init(complex zz) fShape.init(zz) endfunc float func Iterate(complex zz) float ans = fshape.iterate(zz) return abs(ans) endfunc private: TrapShape fShape default: title = "Trap" rating = Recommended heading text = "Use a trap shape for calculating distance." endheading TrapShape param shape caption = "Trap Shape" default = Standard_TrapShapeCross hint = "Selects the shape of the orbit trap." endparam } class ComplexToFloat{ public: func ComplexToFloat() endfunc static float func go(const complex zz, int mode, bool abs_value) float ans float r = real(zz) float i = imag(zz) float rabs = abs(r) float iabs = abs(i) if mode == 0 ; "Mod" ans = sqrt(r^2 + i^2) elseif mode == 1 ; "Real" ans = r elseif mode == 2 ; "Imag" ans = i elseif mode == 3 ; "Real+Imag" ans = r+i elseif mode == 4 ; "Real-Imag" ans = r-i elseif mode == 5 ; "Real*Imag" ans = abs(r*i) elseif mode == 6 ; "Real/Imag" ans = r/i elseif mode == 7 ; "Real^Imag" ans = r^i elseif mode == 8 ; "Imag/Real" ans = i/r elseif mode == 9 ; "Imag^Real" ans = i^r elseif mode == 10 ; "|Real|+|Imag|" ans = rabs+iabs elseif mode == 11 ; "|Real|-|Imag|" ans = rabs-iabs elseif mode == 12 ; "Max(Real,Imag)" ans = MyMath.fmax(i, r) elseif mode == 13 ;"Min(Real,Imag)" ans = MyMath.fmin(i, r) elseif mode == 14 ; "Max(|Real|,|Imag|)" ans = MyMath.fmax(iabs, rabs) elseif mode == 15 ; "Min(|Real|,|Imag|)" ans = MyMath.fmin(iabs, rabs) else;if mode == 16 ; "Angle" ans = atan2(zz)/ (2 * #pi) if ans < 0 ans = ans + 1 endif endif if abs_value ans = abs(ans) endif return ans endfunc } ; an auxiliary function ; 211003 corrected #12 to #15, added cc argument and more cases class ComplexToFloat2{ public: func ComplexToFloat2() endfunc static float func go(const complex zz, const complex cc, int mode, bool abs_value) float ans float r = real(zz) , float i = imag(zz) float rabs = abs(r), float iabs = abs(i) float rc = real(cc), float ic = imag(cc) if mode == 0 ; "Mod" ans = sqrt(r^2 + i^2) elseif mode == 1 ; "Real" ans = r elseif mode == 2 ; "Imag" ans = i elseif mode == 3 ; "Real+Imag" ans = r+i elseif mode == 4 ; "Real-Imag" ans = r-i elseif mode == 5 ; "Real*Imag" ans = r*i ;ans = abs(r*i) (old version used abs for no good reason) elseif mode == 6 ; "Real/Imag" ans = r/i elseif mode == 7 ; "Real^Imag" ans = r^i elseif mode == 8 ; "Imag/Real" ans = i/r elseif mode == 9 ; "Imag^Real" ans = i^r elseif mode == 10 ; "|Real|+|Imag|" ans = rabs+iabs elseif mode == 11 ; "|Real|-|Imag|" ans = rabs-iabs elseif mode == 12 ; "Max(|Real|,|Imag|)" ans = rabs if iabs > rabs, ans = iabs, endif elseif mode == 13 ; "Max(Real,Imag)" ans = r if i > r, ans = i, endif elseif mode == 14 ; "Min(|Real|,|Imag|)" ans = rabs if iabs < rabs, ans = iabs, endif elseif mode == 15 ;"Min(Real,Imag)" ans = rabs if i < r, ans = i, endif elseif mode == 16 ; "Angle" ans = atan2(zz)/ (2 * #pi) if ans < 0 ans = ans + 1 endif elseif mode == 17 ; "z cross+ c" ans = r*ic + i*rc elseif mode == 18 ; "z cross- c" ans = r*ic - i*rc elseif mode == 19 ;"z dot+ c" ans = r*rc + i*ic elseif mode == 20 ; "z dot- c" ans = r*rc - i*ic else;if mode == 21 ; cabs(z*c) ans = cabs(zz*cc) endif if abs_value ans = abs(ans) endif return ans endfunc } class JLB_DistanceColoring(common.ulb:GradientColoring) { ; Accumulate a sum at each step, or at selected steps, of the orbit. ; Use the result to calculate a color index. ; public: import "common.ulb" func JLB_DistanceColoring(Generic pparent) GradientColoring(pparent) if @twodist fDistance1 = new @dist1(this) fDistance2 = new @dist2(this) else fDistance = new @dist(this) endif if @version < 220621 zDistance = new @zDist(this) endif if @fz == 2 zzTransform = new @fUT(this) elseif @fz == 1 && @version >= 220801 zzTransform = new @FF(this) endif rotation = cos(@angle * #pi/180.0) - flip(sin(@angle * #pi/180.0)) endfunc func Init(complex pz, complex ppixel) GradientColoring.Init(pz, ppixel) if @twodist fDistance1.init(pz) fDistance2.init(pz) if @version < 220703 fDistance1.SetParams(@scoloring1, true) fDistance2.SetParams(@scoloring2, true) else fDistance1.SetParams(0, true) fDistance2.SetParams(0, true) endif else fDistance.init(pz) if @version < 220703 fDistance.SetParams(@scoloring, true) else fDistance.SetParams(0, true) endif endif cSum = 0, cSum2 = 0, cSumSq = 0 wtSum = 0, wtSum2 = 0 dmin = 1e30 dmax = -1e30 z_prev = pz iter = 0 ; iter updated each call to Iterate() iter1 = 1 iter2 = 2000000000 ; largest int is 2147483647 iter3 = -1 iter4 = -1 if @some_all == " Some" || @some_all == " Some + More" iter1 = @start iter2 = @start + @howmany - 1 if @some_all == " Some + More" iter3 = @start2 iter4 = @start2 + @howmany2 - 1 endif endif count = 0 ; count update only for succesful distance calculation icount = 0 endfunc func Iterate(complex pz) GradientColoring.Iterate(pz) iter = iter + 1 bool okay = (iter >= iter1 && iter <= iter2) || (iter >= iter3 && iter <= iter4) if !okay return endif complex zz = pz if @zdz == "z-z_prev" zz = zz - z_prev elseif @zdz == "z/z_prev" zz = zz / z_prev elseif @zdz == "z-pixel" zz = zz - #pixel elseif @zdz == "z/pixel" zz = zz / #pixel endif z_prev = pz zz = zz * rotation if @fz == "F(a*zz)" if @version < 220801 zz = @F(@a*zz) else zz = zzTransform.Iterate(@a*zz) endif elseif @fz == "T(a*zz)" zz = zzTransform.Iterate(@a*zz) endif float d if @twodist if icount < @n1 fDistance1.SetIter(iter) ; added 220916 - backwards compatible d = fDistance1.Iterate(zz) else fDistance2.SetIter(iter) ; added 220916 - backwards compatible d = fDistance2.Iterate(zz) endif else fDistance.SetIter(iter) ; added 220916 - backwards compatible d = fDistance.Iterate(zz) endif if @twodist icount = icount + 1 if icount == @n1 + @n2 icount = 0 endif endif if d <= 0 || (@UseMinMax && (d <= @ddmin || d > @ddmax)) return endif complex zd = d if @version < 220621 if @ctype > 5 if @dmult == "d*zz" zd = d * zz elseif @dmult == "d*G(b*zz)" zd = d * @G(@b*zz) endif endif endif count = count + 1 if @ctype == 0;"First Distance" if count == 1 cSum = d endif elseif @ctype == 1;"Last Distance" cSum = d elseif @ctype == 2;"Smallest Distance" if d < dmin cSum = d dmin = d endif elseif @ctype == 3;"Largest Distance" if d > dmax cSum = d dmax = d endif endif if d < dmin, dmin = d, endif if d > dmax, dmax = d, endif if @ctype == 4;"Distance range" cSum = dmax - dmin elseif @ctype == 5;"Distance ratio" if dmin > 0 cSum = dmax / dmin endif elseif @ctype > 5 ; some kind of average float w = 1 if @wtype w = count^@pwt endif Accumulate(zd, w) endif endfunc float func ResultIndex(complex pz) if @solid && count == 0 m_solid = true ; from base class return 0 endif if @ctype <= 5 wtsum = 1 else ; averages of some kind FinalAverage() endif float index = real(cSum) if @version < 220621 if @dmult > 0 if @ctype > 5 && @ctype < 9 index = zDistance.Iterate(csum) endif endif endif if @ctype == "Count" index = count / @Cscale endif if @final > 0 index = FinalSmooth(index) endif if @recip && index != 0 index = 1/index endif return index endfunc func Accumulate(complex zd, float w) cSum2 = cSum if @ctype == "Arithmetic Average" cSum = cSum + w * zd elseif @ctype == "Geometric Average" cSum = cSum + w * log(zd) elseif @ctype == "Harmonic Average" cSum = cSum + w / zd else cSum = cSum + w*zd cSumSq = cSumSq + |w*zd| ; real^2+imag^2 endif wtSum2 = wtSum wtSum = wtSum + w endfunc func FinalAverage() if count > 0 ; there were some if @ctype >= 9 || (@ctype > 5 && !@skip_avg) cSum = cSum / wtSum if wtSum2 > 0 cSum2 = cSum2 / wtSum2 endif cSumSq = cSumSq / wtSum endif if @ctype == "Geometric Average" ;7 cSum = exp(cSum) elseif @ctype == "Harmonic Average" ;8 cSum = 1 / cSum elseif (@ctype == "Variation 1") ;9 "Std. Deviation" ; ignoring the -1 of n-1 cSum = sqrt(cSumSq - |cSum|) elseif (@ctype == "Variation 2") ;10 "Coef. of Variation" cSum = sqrt(cSumSq - |cSum|) / cabs(cSum) elseif (@ctype == "Variation 3") ;11 "Fractal Dimension" if dmax == dmin cSum = 0 else cSum = sqrt(cSumSq - |cSum|) / (dmax-dmin) endif endif endif endfunc ; Some ftype coloring methods get a final smoothing. float func FinalSmooth(float ans) if @final == 1 float il = 1/log(@power) float lp = log(log(@bailout)) ans = 0.05 * (count - 1 + il*lp - il*log(log(abs(ans)))) ; 0.05 and -1 to agree with original elseif @final == 2 float il = 1/log(@power) ; as in Triangle in Standard.ulb float lp = log(log(@bailout)/2.0) float fac = (1 + il * lp - il*log(log(cabs(#z)))) csum = csum * wtSum / (wtSum + 1) ; to agree with original csum2 = csum2 * wtSum2 / (wtSum2 + 1) ans = cabs(cSum2 + (cSum - cSum2) * fac) endif return ans endfunc private: complex rotation Distance fDistance Distance fDistance1 ; for following the orbit Distance fDistance2 Distance zDistance ; for coloring the averages UserTransform zzTransform complex cSum complex cSum2 complex cSumsq float wtSum float wtSum2 float dmin float dmax complex z_prev int count int iter, int iter1, int iter2, int iter3, int iter4 int icount default: title = "Distance Coloring" rating = recommended int param version caption = "Version" default = 220801 visible = @version < 220801 ; backward compatible to 220703 endparam heading text = "(Current is 220801.)" visible = @version < 220801 endheading bool param solid caption = "Use solid background?" default = true endparam ; maybe next as a hint? ; heading ; text = "Pixels for which the formula bails out on the first iteration will have the background color, \ ; rather than the zero location of the gradient." ; visible = @solid ; endheading heading caption = "Accumulation phase" endheading param zdz caption = "Use for zz " enum = "z" "z-z_prev" "z/z_prev" "z-pixel" "z/pixel" default = 0 endparam float param angle caption = "Rotate zz by " default = 0 endparam heading caption = "Use zz, F(a*zz), or T(a*zz)" endheading param fz caption = "Which?" enum = "zz" "F(a*zz)" "T(a*zz)" default = 0 endparam complex param a caption = "a" default = 1 visible = @fz > 0 endparam func F caption = "Function" default = sinh() visible = @fz == 1 && @version < 220801 endfunc UserTransform param FF default = JLB_FTransform selectable = false visible = @fz == 1 && @version >= 220801 endparam UserTransform param fUT caption = "Transform" default = JLB_MoebiusUserTransform visible = @fz == 2 endparam heading visible = @fz > 0 endheading bool param twodist caption = "Two distance methods?" default = false endparam ; heading ; visible = @twodist ; endheading bool param UseMinMax caption = "Min/max distances?" default = false endparam float param ddmin caption = "Min distance" default = 1 min = 0 visible = @UseMinMax endparam float param ddmax caption = "Max distance" default = 100 min = 0 visible = @UseMinMax endparam heading endheading param scoloring caption = "Use for distance" enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag| (manh)""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" visible = @version < 220703 && !@twodist && @dist != JLB_DTrap \ && @dist != JLB_DOverUnder; && @dist != JLB_DCurve endparam Distance param dist caption = "Distance method" default = JLB_DXYZ ;Simple2 visible = !@twodist endparam param scoloring1 caption = "Use for distance 1" enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag| (manh)""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" visible = @version < 220703 && @twodist && @dist != JLB_DTrap \ && @dist != JLB_DOverUnder; && @dist != JLB_DCurve endparam int param n1 caption = "Repetitions for #1" default = 1 min= 1 visible = @twodist endparam Distance param dist1 caption = "Distance method #1" default = JLB_DXYZ ;Simple2 visible = @twodist endparam Distance param zDist default = JLB_DXYZ ;Simple2 visible = false ; can't change it--maybe selectable=false instead? endparam heading visible = @twodist endheading param scoloring2 caption = "Use for distance 2" enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag|""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" visible = @version < 220703 && @twodist && @dist2 != JLB_DTrap \ && @dist2 != JLB_DOverUnder; && @dist2 != JLB_DCurve endparam int param n2 caption = "Repetitions for #2" default = 1 min = 0 visible = @twodist endparam Distance param dist2 caption = "Distance method #2" default = JLB_DCircle visible = @twodist endparam heading endheading param some_all enum = " All" " Some" " Some + More" default = 0 caption = "Use all / some iterations?" ;visible = @type > 0 endparam int param start caption = "Starting with iteration?" default = 4 min = 1 visible = @some_all > 0 endparam int param howmany caption = "How many to use?" default = 100 min = 1 visible = @some_all > 0 endparam int param start2 caption = "More: starting with?" default = 1 min = 1 visible = @some_all > 1 endparam int param howmany2 caption = "More: how many?" default = 1 min = 0 visible = @some_all > 1 endparam bool param wtype caption = "Weighted averages?" default = false visible = @ctype > 5 endparam float param pwt caption = "Weight power p" default = 0.5 visible = @wtype == true && @ctype > 5 hint = "Positive p gives more weight to later iterations; negative p gives more weight \ to earlier iterations; p = 0 weights all iterations the same." endparam ; heading ; visible = @ctype > 5 ; endheading param dmult caption = "Use distance multiplier?" default = 0 enum = "d" "d*zz" "d*G(b*zz)" visible = @version < 220621 && @ctype > 5 endparam func G caption = "G" default = sinh() visible = @version < 220621 && @dmult == 2 && @ctype > 5 endfunc complex param b caption = "b" default = 1 visible = @version < 220621 && @dmult == 2 && @ctype > 5 endparam heading caption = "Final phase" endheading param ctype caption = "How to color" enum = "First Distance" "Last Distance" "Smallest Distance" "Largest Distance" \ "Distance range" "Distance ratio" \ "Arithmetic Average" "Geometric Average" "Harmonic Average" \ "Variation 1" "Variation 2" "Variation 3" "Count" ; "Std. Deviation" "Coef. of Variation" "Fractal Dimension" default = 6 ;visible = @final != 1 endparam int param Cscale caption = "Count scale" default = 100 visible = @ctype == 12 endparam bool param skip_avg ; maybe later if more checking caption = "Use sum instead of average?" default = false visible = @ctype > 5 && @ctype < 9 endparam param zcoloring caption = "Average this" enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag|" "|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" ; "Count" "First Iteration" "Last Iteration" visible = @dmult > 0 && @ctype > 5 && @ctype < 9 endparam param final caption = "Final smooth" enum = "None" "Mandelbrot type" "TIA type" default = 0 endparam float param power caption = "Exponent" default = 2.0 hint = "This should be set to match the exponent of the \ formula you are using." visible = @final > 0 endparam float param bailout caption = "Bailout" default = 1e20 min = 1 hint = "This should be set to match the bail-out value in the Formula tab. " visible = @final > 0 endparam bool param recip caption = "Use reciprocal?" default = false hint = "Color based on, for example, 1/Mod(z) instead of Mod(z)." endparam } class JLB_LinearUserTransform(common.ulb:UserTransform) { public: complex func Iterate(complex pz) return (@a + @b*pz) endfunc default: rating = Recommended title = "Linear" int param v_LinearUserTransform caption = "Version (Linear_UserTransform)" default = 220212 visible = @v_LinearUserTransform < 220212 endparam heading caption = "v_new = a + b*v" endheading complex param a caption = "Linear: a" default = (0,0) endparam complex param b caption = "Linear: b" default = (1,0) endparam } class Tweak(common.ulb:UtilityTransform) { public: import "common.ulb" func Tweak(Generic pparent) UtilityTransform(pparent) if @type == 2 zzTransform = new @uT(pparent) endif endfunc complex func Iterate(complex pz) ; m_iterations = m_iterations + 1 ; inherited from Transform class through UtilityTransform class ; if (@some_all == " Some") && (m_iterations < @start || m_iterations >= @start + @howmany) ; return pz ; endif complex tmp if @type == "Linear" tmp = @e1 + @e2*pz elseif @type == "Built-in Function" tmp = @e1 + @e2*@F(@a*pz) else;if @type == "Transform" tmp = @e1 + @e2*zzTransform.Iterate(@a*pz) endif if @kind == "Add" pz = pz + tmp else pz = tmp endif return pz endfunc private: UserTransform zzTransform default: title = "Tweak" ; param some_all ; enum = " All" " Some" ; default = 0 ; caption = "Tweak all / some iterations?" ; ;visible = @type > 0 ; endparam ; int param start ; caption = "Starting with iteration?" ; default = 4 ; min = 1 ; visible = @some_all > 0 ; endparam ; int param howmany ; caption = "How many to use?" ; default = 100 ; min = 1 ; visible = @some_all > 0 ; endparam param kind caption = "Add or Replace" enum = "Add" "Replace" default = 0 endparam heading caption = "v_new = v + e1 + e2*v" visible = @type == 0 && @kind == 0 endheading heading caption = "v_new = e1 + e2*v" visible = @type == 0 && @kind == 1 endheading heading caption = "v_new = v + e1 + e2*F(a*v)" visible = @type == 1 && @kind == 0 endheading heading caption = "v_new = e1 + e2*F(a*v)" visible = @type == 1 && @kind == 1 endheading heading caption = "v_new = v + e1 + e2*T(a*v)" visible = @type == 2 && @kind == 0 endheading heading caption = "v_new = e1 + e2*T(a*v)" visible = @type == 2 && @kind == 1 endheading param type caption = "Tweak type" enum = "Linear" "Built-in Function" "Transform" default = 0 endparam func F caption = "Tweak Function" default = ident() visible = @type == 1 endfunc UserTransform param uT caption = "Tweak Transform" default = JLB_LinearUserTransform visible = @type == 2 endparam complex param e1 caption = "Tweak: e1" default = (0,0) endparam complex param e2 caption = "Tweak: e2" default = (0.1,0) endparam complex param a caption = "a" default = 1 visible = @type > 0 endparam } class JLB_TwoUserTransforms(common.ulb:UserTransform) { ; A wrapper for two UserTransforms on one variable. public: import "common.ulb" func JLB_TwoUserTransforms(Generic pparent) UserTransform.UserTransform(pparent) mUT1 = new @fUT1(this) mUT2 = new @fUT2(this) m_Combine = new Combine() endfunc complex func Iterate(complex pz) if (@how == "( )") pz = mUT1.Iterate(mUT2.Iterate(pz)) else complex z1 = mUT1.Iterate(pz) complex z2 = mUT2.Iterate(pz) pz = m_Combine.go(z1, @how, z2) endif return pz endfunc protected: UserTransform mUT1 UserTransform mUT2 Combine m_Combine default: rating = Recommended title = "Two Transforms" int param v_TwoUserTransforms caption = "Version (Two Transforms)" default = 220312 visible = @v_TwoUserTransforms < 220312 endparam param how caption = "How to combine" enum = "+" "-" "*" "/" "^" "( )" default = 0 hint = "How to combine Transform values." endparam UserTransform param fUT1 caption = "First Transform" default = JLB_LinearUserTransform endparam UserTransform param fUT2 caption = "Second Transform" default = JLB_BasicUserTransform endparam } class JLB_ComboUserTransform(common.ulb:UserTransform) { ; Combine two functions or transforms or one each public: import "common.ulb" func JLB_ComboUserTransform(Generic pparent) UserTransform.UserTransform(pparent) if (@ww == "T1 F2") || (@ww == "T1 T2") mUT1 = new @fUT1(this) endif if (@ww == "F1 T2") || (@ww == "T1 T2") mUT2 = new @fUT2(this) endif m_Combine = new Combine() endfunc complex func Iterate(complex pz) complex z1 complex z2 if (@ww == "F1 F2") || (@ww == "T1 F2") z2 = @F2(pz) else z2 = mUT2.Iterate(pz) endif if (@how == "( )") if (@ww == "F1 F2") || (@ww == "F1 T2") pz = @F1(z2) else pz = mUT1.Iterate(z2) endif else if (@ww == "F1 F2") || (@ww == "F1 T2") z1 = @F1(pz) else z1 = mUT1.Iterate(pz) endif pz = m_Combine.go(z1, @how, z2) endif return pz endfunc protected: UserTransform mUT1 UserTransform mUT2 Combine m_Combine default: rating = notRecommended title = "Combo" heading text = "Combo Transform is the preferred formula." endheading int param v_ComboUserTransform caption = "Version (Combo)" default = 220316 visible = @v_ComboUserTransform < 220316 endparam heading caption = "Combine two functions or transforms" endheading param ww caption = "Which to use" enum = "F1 F2" "F1 T2" "T1 F2" "T1 T2" default = 0 endparam param how caption = "How to combine" enum = "+" "-" "*" "/" "^" "( )" default = 0 hint = "How to combine Transform values." endparam func F1 caption = "Function F1" default = sin() visible = (@ww == 0) || (@ww == 1) endfunc UserTransform param fUT1 caption = "Transform T1" default = JLB_LinearUserTransform visible = (@ww == 2) || (@ww == 3) endparam func F2 caption = "Function F2" default = tanh() visible = (@ww == 0) || (@ww == 2) endfunc UserTransform param fUT2 caption = "Transform T2" default = JLB_BasicUserTransform visible = (@ww == 1) || (@ww == 3) endparam } class JLB_RotateUserTransform(common.ulb:UserTransform) { public: func JLB_RotateUserTransform(Generic pparent) UserTransform.UserTransform(pparent) rotate = cos(@degrees*#pi/180) + flip(sin(@degrees*#pi/180)) endfunc ; func Init(complex pz) ; rotate = cos(@degrees*#pi/180) + flip(sin(@degrees*#pi/180)) ; endfunc complex func Iterate(complex pz) return (rotate*pz) endfunc private: complex rotate default: rating = Recommended title = "Rotate" int param v_RotateUserTransform caption = "Version (Rotate_UserTransform)" default = 220320 visible = @v_RotateUserTransform < 220320 endparam heading caption = "v_new = rotation*v" endheading ; complex param a ; caption = "Rotate: a" ; default = (0,0) ; endparam float param degrees caption = "Rotate: degrees" default = 45 endparam } class JLB_OverUnderUserTransform(common.ulb:UserTransform) { public: func JLB_OverUnderUserTransform(Generic pparent) UserTransform.UserTransform(pparent) mTO = new @uTO(pparent) mTU = new @uTU(pparent) mCalcValue = new CalcValue() mTSmooth = new TSmooth() endfunc complex func Iterate(complex pz) float test = mCalcValue.Go(@mode, pz, pz) if @trans == 0 if test >= @crit return mTO.Iterate(pz) else return mTU.Iterate(pz) endif else float x = (test-@crit)/@trans float frac = mTSmooth.go(x) return frac*mTO.Iterate(pz) + (1-frac)*mTU.Iterate(pz) ; if x >= 32 ; good to 28 decimals even with 30 added precision ; return mTO.Iterate(pz) ; elseif x <= -32 ; return mTU.Iterate(pz) ; else ; float frac = 0.5*(1+tanh(x)) ; return frac*mTO.Iterate(pz) + (1-frac)*mTU.Iterate(pz) ; endif endif endfunc private: CalcValue mCalcValue TSmooth mTSmooth UserTransform mTO UserTransform mTU default: rating = Recommended title = "OverUnder" int param v_OverUnderUserTransform caption = "Version (OverUnderUserTransform)" default = 220323 visible = @v_OverUnderUserTransform < 220323 endparam heading caption = "Apply different transforms if a test value is over or under the critical value." endheading float param crit caption = "Critical value" default = 0 endparam param mode caption = "How to calculate the test value." default = 0 enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag|""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" endparam UserTransform param uTO caption = "Over Transform" default = JLB_RotateUserTransform endparam UserTransform param uTU caption = "Under Transform" default = JLB_LinearUserTransform endparam float param trans caption = "Transition parameter" hint = "Larger values make a smoother transition. For an abrupt transition, use 0." default = 0.1 min = 0 endparam } ; make a smooth transition avoiding tanh overflow class TSmooth { public: func TSmooth() endfunc ; return 0.5*(1+tanh(x)) while avoiding overflow static float func go(const float x) float xx = 2*x float t ;t = tanh(x) if xx >= 0 t= (1 - exp(-xx))/(1 + exp(-xx)) else t = (exp(xx)-1)/(exp(xx)+1) endif return 0.5 * (1 + t) endfunc } ; tanh(z) avoiding tanh overflow class TanhZ { public: func TanhZ() endfunc static complex func go(complex z) z = z*2 if real(z) >= 0 return (1 - exp(-z))/(1 + exp(-z)) else return (exp(z)-1)/(exp(z)+1) endif endfunc } class JLB_MaskColoring(common.ulb:DirectColoring) { ; Generate a mask with opacity 1 at the center, going to 0 far away. public: func JLB_MaskColoring(Generic pparent) DirectColoring(pparent) c = 1 s = 0 if (@shape_type > 0) ;|| (@p != 2.0) ang = @angle * #pi / 180.0 c = cos(ang) s = sin(ang) endif if @shape_type == 0 rx = ry = @r else rx = @xsemi ry = @ysemi endif endfunc ; func Init(complex pz, complex ppixel) ; DirectColoring.Init(pz, ppixel) ; endfunc ; func Iterate(complex pz) ; endfunc color func Result(complex pz) if rx == 0 || ry == 0 return rgba(0.5,0.5,1.0, 0.0) ; no mask endif float d ;if (@shape_type == "Circle" || @shape_type == "Ellipse") float x = real(pz-@center) float y = imag(pz-@center) float xr = abs(x*c + y*s) / rx float yr = abs(x*s - y*c) / ry d = (xr^2 + yr^2) ; d = (xr^@p + yr^@p) ;endif if @type == 0 d = 1 - d / @scale if d < 0, d = 0, endif else d = exp(-d / @scale) endif if @inverse d = 1.0 - d endif return rgba(0,0,0, d) endfunc private: float ang, float c, float s float rx, float ry default: title = "Mask Coloring" rating = notrecommended heading caption = "'Mask from shape' is preferred." endheading heading text = "Generate a mask with opacity 1 at the center, decaying to 0 far away.\ The formula and the gradient are ignored." visible = !@inverse endheading heading text = "Generate a mask with opacity 0 at the center, growing to 1 far away.\ The formula and the gradient are ignored." visible = @inverse endheading bool param inverse caption = "Inverse?" default = false endparam param shape_type caption = "Shape" enum = "Circle" "Ellipse" default = 0 endparam complex param center caption = "Center" default = (0,0) endparam ; float param p ; caption = "Power" ; default = 2.0 ; min = 0.1 ; hint = "Use 2 for standard circles or ellipses" ; ;visible = (@shape_type < 2) ; endparam float param r caption = "Radius" default = 1 min = 0.0 visible = @shape_type == 0 endparam float param xsemi caption = "X semi-axis" default = 1 min = 0.0 visible = @shape_type == 1 endparam float param ysemi caption = "Y semi-axis" default = 1 min = 0.0 visible = @shape_type == 1 endparam float param angle caption = "Rotation angle" default = 0.0 visible = (@shape_type > 0) ;|| (@p != 2.0) endparam param type caption = "Type" enum = "1" "2" default = 0 endparam float param scale caption = "Scale" default = 1 endparam } class JLB_ModularSFormula(SFormula) { ; The Modular formula as an SFormula. ; 220605 public: import "common.ulb" func JLB_ModularSFormula(Generic pparent) SFormula(pparent) if @v == 2 m_Transform = new @zT(this) endif if @addT > 0 finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if !@p_Mtype ppc = @p_seed endif endfunc bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz if @v == 1 pz = @F(@a*pz) elseif @v == 2 pz = m_Transform.Iterate(@a*pz) endif complex pzn = pz int n = 1 complex tmp = pz * (1 - pzn)^@p while n < @nbig pzn = pz * pzn tmp = tmp * (1 - pzn)^@p n = n + 1 endwhile znew = tmp + ppc ;if !@p_std if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif ;endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex ppc UserTransform m_Transform UtilityTransform finalTransform default: title = "Modular" rating = Recommended int param v_ModularSFormula caption = "Version (Modular_SFormula)" default = 220604 visible = @v_ModularSFormula < 220604 endparam ; bool param p_std ; caption = "Standard?" ; default = true ; endparam ; heading ; caption = "Set v = F(z)" ; endheading heading caption = "z_new = v*((1-v)^p)*((1-v^2)^p)*...*((1-v^n)^p) + c" endheading param v caption = "Use for v" enum = "z" "F(a*z)" "T(a*z)" default = 0 endparam func F caption = "F (Built-in function)" default = sin() ;hint = "Function" visible = @v == 1 endfunc UserTransform param zT caption = "T (Transform)" default = JLB_MoebiusUserTransform visible = @v == 2 endparam complex param a caption = "a" default = 1 visible = @v > 0 endparam float param p caption = "p" default = 2 min = 1 endparam int param nbig caption = "n" min = 1 endparam bool param p_Mtype caption = "M-type?" default = false endparam complex param p_seed caption = "Seed for J-type" default = (0.5,0.5) visible = !@p_Mtype endparam heading caption = "Tweak after each iteration" ;visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 ;visible = !@p_std endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 ;&& !@p_std selectable = false endparam } class JLB_DXYZ(Distance){ ; A distance class similar to ComplexToFloat2 ; Unlike ComplexToFloat2, there's an option to scale some distances ; so that the full possible range is (0,1). public: func JLB_DXYZ(Generic pparent) Distance(pparent) endfunc float func Iterate(complex zz) float ans float x = real(zz), float y = imag(zz) float ax = abs(x), float ay = abs(y) float a = cabs(zz), float b = sqrt(2.0) if @type == "Mod" ans = a elseif @type == "Real" ans = x ; range = (-a,+a) if @s, ans = (ans+a)/(a+a), endif elseif @type == "Imag" ans = y ; range = (-a,+a) if @s, ans = (ans+a)/(a+a), endif elseif @type == "Real+Imag" ans = x+y ; range = (-a,+a*b) if @s, ans = (ans+a)/(a*b+a), endif elseif @type == "Real-Imag" ans = x-y ; range = (-a*b,+a*b) if @s, ans = (ans+a*b)/(2*a*b), endif elseif @type == "Real*Imag" ans = x*y ; range = (-0.5,+0.5)a^2 if @s, ans = (ans+0.5*a^2)/(a^2), endif elseif @type == "Real/Imag" ans = x/y elseif @type == "Imag/Real" ans = y/x elseif @type == "Real^Imag" ans = x^y elseif @type == "Imag^Real" ans = y^x elseif @type == "|Real|+|Imag|" ans = ax+ay ;range = (a,a*b) if @s, ans = (ans-a)/(a*b-a), endif elseif @type == "|Real|-|Imag|" ans = ax-ay ;range = (-a,a) if @s, ans = (ans+a)/(a+a), endif elseif @type == "Max(|Real|,|Imag|)" ans = ax ; range = (a,a/b) if ay > ax, ans = ay, endif if !@s, ans = (ans-a)/(a-a/b), endif elseif @type == "Max(Real,Imag)" ans = x ; range = (0,a) if y > x, ans = y, endif if @s, ans = (ans)/(a), endif elseif @type == "Min(|Real|,|Imag|)" ans = ax ; range = (0,a/b) if ay < ax, ans = ay, endif if @s, ans = (ans)/(a/b), endif elseif @type == "Min(Real,Imag)" ans = x ; range = (-a,a/b) if y < x, ans = y, endif if @s, ans = (ans+a)/(a/b+a), endif else;if @type == "Angle" ans = atan2(zz)/ (2 * #pi) if ans < 0 ans = ans + 1 endif if @version >= 220809 && !@s ans = a*ans endif endif if @invert && ans != 0 ans = 1/ans endif if @tlog && ans != 0 ans = log(ans) endif ; if @oneminus ; experiment 220916 ; ans = 1 - ans ; endif if m_abs_value ans = abs(ans) endif return ans endfunc private: default: title = "XYZ distance" rating = Recommended int param version caption = "Version" default = 220809 visible = @version < 220809 endparam heading text = "(Curent is 220809.}" visible = @version < 220809 ;backward compatible to 220703 endheading param type caption = "Use for distance" enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag|""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" default = 0 endparam bool param s caption = "Z scale?" default = false visible = @type != 0 && (@type < 6 || @type > 9) && \ !(@version < 220809 && @type == 16) hint = "Scale answer to be between 0 and 1." endparam bool param invert caption = "Invert?" default = false visible = @version >= 220809 endparam bool param tlog caption = "Take log?" default = false visible = @version >= 220809 endparam ; bool param oneminus ; caption = "1 - distance?" ; default = false ; visible = @version >= 220809 ; endparam } class JLB_TentSFormula(SFormula) { ; The Tent formula as an SFormula, generalized. ; Original Tent formula in lkm.ufm and reb.ufm public: import "common.ulb" func JLB_TentSFormula(Generic pparent) SFormula(pparent) if @addT > 0 && !@p_std finalTransform = new @finalUT(this) endif m_dist = new @dist(this) if @type == 2 && !@p_std m_T = new @zT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if !@p_Mtype ppc = @p_seed endif if @p_std tscale = 0, p = 1 else tscale = @trans, p = @power endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif complex pz = S.pz if !@p_std if @type == 1 pz = @f(pz) elseif@type == 2 pz = m_T.Iterate(pz) endif endif complex znew float test = m_dist.Iterate(pz) complex z1 = ppc * @scale * pz^p complex z2 = ppc * @scale * (@top - pz)^p if tscale == 0 if test <= @crit znew = z1 else znew = z2 endif else float r = (test-@crit)/tscale float frac = TSmooth.go(r) znew = frac*z1 + (1-frac)*z2 endif if !@p_std if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex ppc UtilityTransform finalTransform Distance m_dist UserTransform m_T float tscale float p default: rating = Recommended title = "Tent" int param v_TentSFormula caption = "Version" default = 220727 visible = @v_TentSFormula < 220727 ; changes 220729 backward compatible to 220727 endparam heading text = "(Current is 220727.)" visible = @v_TentSFormula < 220727 endheading bool param p_std caption = "Standard?" default = true endparam bool param p_Mtype caption = "M-type?" default = true visible = @v_TentSFormula > 100 endparam complex param p_seed caption = "Seed for J-type" default = (0.5, -0.5) visible = @v_TentSFormula > 100 && !@p_Mtype endparam bool param std caption = "Standard?" default = true endparam float param crit caption = "Critical distance" default = 1 endparam heading text = "Use for critical distance:" endheading Distance param dist default = JLB_DXYZ selectable = false endparam heading caption = "" endheading param type caption = "Tent function type" enum = "z" "F(z)" "T(z)" default = 0 visible = !@p_std endparam func f caption = "F (built-in function)" default = sin() hint = "Function" visible = @type == 1 && !@p_std endfunc UserTransform param zT caption = "T (Transform)" default = JLB_MoebiusUserTransform visible = @type == 2 && !@p_std endparam complex param scale caption = "Tent scale" default = 1 endparam float param top Caption = "Tent value" default = 1 endparam float param power Caption = "Tent power" default = 1 visible = !@p_std endparam float param trans caption = "Transition parameter" hint = "Larger values make a smoother transition. For an abrupt transition, use 0." default = 0.1 min = 0 visible = !@p_std endparam heading caption = "Tweak after each iteration" endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 selectable = false endparam heading caption = "" endheading } class JLB_TwoDistances(common.ulb:UserTransform) { ; Make a UserTransform out of two Distance formulas public: ;import "common.ulb" func JLB_TwoDistances(Generic pparent) UserTransform.UserTransform(pparent) mx = new @fx(this) my = new @fy(this) if @version < 221224 xyswitch = @switch else xyswitch = @switch2 == 1 || @switch2 == 3 endif endfunc func Init(complex pz) UserTransform.Init(pz) mx.Init(pz) my.Init(pz) mx.SetParams(0, false) my.SetParams(0, false) endfunc complex func Iterate(complex pz) float x = mx.Iterate(pz) float y = my.Iterate(pz) if xyswitch pz = y + flip(x) else pz = x + flip(y) endif if @version >= 221224 && @switch2 > 1 xyswitch = !xyswitch endif return pz endfunc protected: Distance mx Distance my bool xyswitch default: rating = Recommended title = "Two Distances" int param version caption = "Version" default = 221224 visible = @version < 221224 endparam heading text = "(Current is 221224.)" visible = @version < 221224 endheading ; heading ; caption = "Make a transform from two Distances." ; endheading bool param switch caption = "Switch X and Y distances?" default = false visible = @version < 221224 endparam param switch2 caption = "X/Y switch?" enum = "No" "Yes" "No/Yes/No . . ." "Yes/No/Yes . . ." default = 0 visible = @version >= 221224 endparam Distance param fx caption = "X Distance" default = JLB_DSimple2 endparam Distance param fy caption = "Y Distance" default = JLB_DCircle endparam } class JLB_FTransform(common.ulb:UserTransform) { ; Inspired by Bona Spallona in OM.ulb ; A Transform replacement of UF's built-in functions ; First trig and hyperbolic, completing UF's built-in set, ; then then the rest of UF's standard set, ; then a few extras from OM.ulb ; ; Many interesting complications are possible using Combo Transform. public: complex func Iterate(complex pz) if @F == "sin" pz = sin(pz) elseif @F == "sinh" pz = sinh(pz) elseif @F == "asin" pz = asin(pz) elseif @F == "asinh" pz = asinh(pz) elseif @F == "cos" pz = cos(pz) elseif @F == "cosh" pz = cosh(pz) elseif @F == "acos" pz = acos(pz) elseif @F == "acosh" pz = acosh(pz) elseif @F == "tan" pz = tan(pz) elseif @F == "tanh" pz = tanh(pz) elseif @F == "atan" pz = atan(pz) elseif @F == "atanh" pz = atanh(pz) elseif @F == "cotan" pz = cotan(pz) elseif @F == "cotanh" pz = cotanh(pz) elseif @F == "acotan" pz = atan(1/pz) elseif @F == "acotanh" pz = atanh(1/pz) elseif @F == "sec" pz = 1/cos(pz) elseif @F == "sech" pz = 1/cosh(pz) elseif @F == "asec" pz = acos(1/pz) elseif @F == "asech" pz = acosh(1/pz) elseif @F == "cosec" pz = 1/sin(pz) elseif @F == "cosech" pz = 1/sinh(pz) elseif @F == "acosec" pz = asin(1/pz) elseif @F == "acosech" pz = asinh(1/pz) elseif @F == "sqr" pz = sqr(pz) elseif @F == "sqrt" pz = sqrt(pz) elseif @F == "log" pz = log(pz) elseif @F == "exp" pz = exp(pz) elseif @F == "abs" pz = abs(pz) elseif @F == "cabs" pz = cabs(pz) elseif @F == "conj" pz = conj(pz) elseif @F == "flip" pz = flip(pz) elseif @F == "ident" pz = ident(pz) elseif @F == "zero" pz = 0 elseif @F == "recip" pz = recip(pz) elseif @F == "ceil" pz = ceil(pz) elseif @F == "floor" pz = floor(pz) elseif @F == "trunc" pz = trunc(pz) elseif @F == "trunc*" float x = real(pz) if x >= 0, x = ceil(x), else, x = floor(x), endif float y = imag(pz) if y >= 0, y = ceil(y), else, y = floor(y), endif pz = x + flip(y) elseif @F == "round" pz = round(pz) elseif @F == "power" pz = pz^@p elseif @F == "gd" ;Gudermannian (Wikipedia) pz = asin(tanh(pz)) elseif @F == "agd" ; inverse Gudermannian pz = atanh(sin(pz)) elseif @F == "logit" pz = log(pz/(1-pz)) elseif @F == "expit" pz = 1/(1+exp(-pz)) elseif @F == "gauss" pz = exp(-sqr(pz)) elseif @F == "softplus" pz = log(1+exp(pz)) elseif @F == "asoftplus" pz = log(exp(pz)-1) elseif @F == "softminus" pz = pz-log(1+exp(pz)) elseif @F == "asoftminus" pz = pz-log(1-exp(pz)) elseif @F == "sinc" if pz == 0 pz = 1 else pz = sin(pz)/pz endif endif return pz endfunc default: rating = Recommended title = "Function" int param version caption = "Version" default = 220730 visible = @version < 220730 endparam heading text = "(Current is 220730.)" visible = @version < 220730 endheading param F caption="Function" enum = "sin" "sinh" "asin" "asinh" "cos" "cosh" "acos" "acosh" \ "tan" "tanh" "atan" "atanh" "cotan" "cotanh" "acotan" "acotanh" \ "sec" "sech" "asec" "asech" "cosec" "cosech" "acosec" "acosech" \ "sqr" "sqrt" "log" "exp" "abs" "cabs" "conj"\ "flip" "ident" "zero" "recip" "ceil" "floor" "trunc" "trunc*" "round" \ "power" "gd" "agd" "logit" "expit" "gauss" \ "softplus" "softminus" "asoftplus" "asoftminus" "sinc" default=0 endparam complex param p caption = "Power" default = 2 visible = @F == "power" endparam } class JLB_ComboTransform (common.ulb:UserTransform) { public: func JLB_ComboTransform(Generic pparent) UserTransform(pparent) m_T1 = new @T1(this) if @v >= 3 m_T2 = new @T2(this) endif endfunc complex func Iterate(complex pz) complex z1 if @v == "T(a*z)" return m_T1.Iterate(@a*pz) elseif @v == "T(a*z) op pixel" z1 = Combine.go(@a*pz, @op, #pixel) return m_T1.Iterate(z1) elseif @v == "T(a*z op pixel)" z1 = Combine.go(@a*pz, @op, #pixel) return m_T1.Iterate(z1) else ; "T1(a*z) op T2(b*z)" complex z2 = m_T2.Iterate(@b*pz) if @op1 == "( )" return m_T1.Iterate(@a*z2) else z1 = m_T1.Iterate(@a*pz) return Combine.go(z1, @op1, z2) endif endif endfunc private: UserTransform m_T1 UserTransform m_T2 default: rating = Recommended title = "Combo Transform" int param version caption = "Version" default = 220731 visible = @version < 220731 endparam heading text = "(Current is 220731.)" visible = @version < 220731 endheading param v caption = "Type" enum = "T(a*z)" "T(a*z) op pixel" "T(a*z op pixel)" \ "T1(a*z) op T2(b*z)" ;"T1(a*z) op1 (T2(b*z) op2 pixel))" \ ;"T1(a*z) op1 (T2(b*z op2 pixel))" default = 0 endparam complex param a caption = "a" default = (1,0) endparam complex param b caption = "b" default = (0, 1) visible = @v >= 3 endparam param op caption = "op" enum = "+" "-" "*" "/" "^" default = 0 hint = "How to combine." visible = @v==1 || @v ==2 endparam param op1 caption = "op1" enum = "+" "-" "*" "/" "^" "( )" default = 0 hint = "How to combine." visible = @v>=3 endparam ; param op2 ; caption = "op2" ; enum = "+" "-" "*" "/" "^" ; default = 0 ; hint = "How to combine." ; visible = @v>=4 ; endparam UserTransform param T1 default = JLB_FTransform endparam UserTransform param T2 default = JLB_FTransform visible = @v >= 3 endparam } class JLB_DCurve(Distance) { ; Curve definitions from ; https://mathcurve.com/courbes2d.gb/courbes2d.shtml ; www.2dcurves/com ; mathworld.wolfram.com/ ; with inspiration and some code from Otto Magus, om.ulb ; ; sources vary on what some of the curves are. ; I've changed a^2 many places to just a and b^2 to b, ; added parameters in a sensible way to many curves, ; and omitted any curves with three or more parameters public: func JLB_DCurve(Generic pparent) Distance(pparent) a = @aa ; to save typing b = @bb endfunc float func Iterate(complex zz) float x=real(zz) float y=imag(zz) if @exch float t=x, x=y, y=t endif float c1 = cabs(zz), float c2 = c1*c1, float c3 = c2*c1, float c4 = c2*c2 float x2 = x^2, float y2 = y^2, float d2 = x2-y2 float d = 0 if @V=="Alain" d = d2^2 - a*x2 + b*y2 if @s, d = d/c4, endif elseif @V=="Ampersand" d = (y2-x2)*(x-a)*(2*x-3*a) - b*(c2-2*x)^2 if @s, d = d/c4, endif elseif @V=="Anguinea" d = y*(x2+a^2) - a*b*x if @s, d = d/c3, endif elseif @V=="Arachnea" d = (c2-a*x)^2 - a^2*c2 if @s, d = d/c4, endif elseif @V=="Arcs of Samothrace" d = x2*(a*x2-y2)^2 - y2*c2 if @s, d = d/c4/c2, endif elseif @V=="Astroid" d = x^@p + y^@p - a^@p if @s, d = d/c1^@p, endif elseif @v=="Atripthaloid" d = x^4*c2 - (a*x2-b)^2 if @s, d = d/c4/c2, endif elseif @V=="Beetle'" d = c2*(c2-a*x-a*y) - b*x2*y2 if @s, d = d/c4, endif elseif @V=="Besace" d = (x2-b*y)^2 - a*d2 if @s, d = d/c4, endif elseif @V=="Bicorn" d = y2*(a-x2) - (x2+2*a*y-a^2)^2 if @s, d = d/c4, endif elseif @V=="Bicuspid" d = (x2-a^2)*(x-a)^2 + (y2-a)^2 if @s, d = d/c4, endif elseif @V=="Bifoliate" d = x2*x2 + y2*y2 - a*x*y2 if @s, d = d/c4, endif elseif @V=="Bifolium" d = c2^2 - (a*x+b*y)*x2 if @s, d = d/c4, endif elseif @V=="Bow" d = x2*x2 - a*y*d2 if @s, d = d/c4, endif elseif @V=="Bow Tie" d = x2*x2*c2 - a*d2 if @s, d = d/c2/c4, endif elseif @V=="Bullet-Nose" d = a^2*y2 - b^2*x*2 - x2*y2 if @s, d = d/c4, endif elseif @V=="Butterfly" d = x2^3 + y2^3 - a*x2 if @s, d = d/c2/c4, endif elseif @V=="Cardioid" d = (c2-a*x)^2 - a^2*c2 if @s, d = d/c4, endif elseif @V=="Cassinian Ovals" d = c2^2 - a*d2 + b^4 if @s, d = d/c4, endif elseif @V=="Cayley's Sextic" d = 4*(c2-a*x)^3 - 27*a^2*c2^2 if @s, d = d/c4/c2, endif elseif @V=="Cissoid of Diocles" d = x*c2 - a*y2 if @s, d = d/c3, endif elseif @V=="Conchoid of Nicomedes" d = (x-a)^2*c2 - b^2*x2 if @s, d = d/c4, endif elseif @V=="Cornoid" d = c2^3 + a*(3*x^4 - 6*x2*y2 - 5*y^4) + 8*a^2*y2 - 4*a^3 if @s, d = d/c2/c4, endif elseif @V=="Cross" d = x2*y2 - a*x2 - b*y2 if @s, d = d/c4, endif elseif @V=="Curve of Cramer" d = x*c2 + (a+b)*x2 - (a-b)*y2 elseif @V=="Cubical Parabola" d = a^2*y - x^3 + b*x if @s, d = d/c3, endif elseif @V=="Cubic of de Sluze" d = a*(x-a)*c2 - b*x2 if @s, d = d/c3, endif elseif @V=="Deltoid" d = c2^2 + 8*a*x*(x2-3*y2) + 18*a^2*c2 - 27*a^4 if @s, d = d/c4, endif elseif @V=="Dipole" d = c2^3 - a*x2 if @s, d = d/c2/c3, endif elseif @V=="Double-Heart" d = x2*c2 - (a+b)*x2*y + (a-b)^2*y/4 if @s, d = d/c4, endif elseif @v=="Dumbbell" d = b*y2 - x^4*(a-x2) if @s, d = d/c2/c4, endif elseif @V=="Egg of Granville" d = x2*y2 - (x-a)*(b-x) if @s, d = d/c4, endif elseif @V=="Egg of Kepler" d = c2^2 - a*x^3 if @s, d = d/c4, endif elseif @v=="Eight" d = x^4 - a*d2 if @s, d = d/c4, endif elseif @V=="Ellipse" d = b*x2 + a*y2 - a/b if @s, d = d/c2, endif elseif @V=="Euler's Cubic" d = y*(y2-a) - b*x*(x2-a) if @s, d = d/c3, endif elseif @V=="Folium of Descartes" d = x^3 + y^3 - 3*a*x*y if @s, d = d/c3, endif elseif @V=="Folium of Durer" d = c2*(2*c2-a)^2 - a^2*x2 if @s, d = d/c2/c4, endif elseif @V=="Folium of Kepler" d = c2*(x*(x-a)+y2) - b*x*y2 if @s, d = d/c4, endif elseif @V=="Function" d = y - a*real(@F(b*x)) if @s, d = d/c1, endif elseif @v=="Heat Capacity" d = x2*(x-y) + a if @s, d = d/c3, endif elseif @V=="Hippopede" d = c2^2 - a*x2 - b*y2 if @s, d = d/c4, endif elseif @V=="Humbert's Cubic" d = x^3 - 3*x*y2 - a if @s, d = d/c3, endif elseif @V=="Hyperbola" d = x2 - a*y2 if @s, d = d/c2, endif elseif @V=="Kampyle of Eudoxus" d = x^4 - a*c2 if @s, d = d/c4, endif elseif @V=="Kappa" d = x2*c2 - a*y2 if @s, d = d/c4, endif elseif @V=="Kiepert" d = c2^3 - a*x*(x2-3*y2) if @s, d = d/c2/c4, endif elseif @V=="Kiss" d = a^2*y2 - (a-x2)^3 if @s, d = d/c2/c4, endif elseif @V=="Knot" d = (x2-a)^2 - y2*(3*a+2*y) if @s, d = d/c4, endif elseif @v=="Kulp Quartic" d = x2*y2 - a*(a-y2) if @s, d = d/c4, endif elseif @V=="Lemniscate of Bernoulli" d = c2^2 - a*d2 if @s, d = d/c4, endif elseif @V=="Limacon of Pascal" d = (c2-a*x)^2 - b*c2 if @s, d = d/c4, endif elseif @V=="Maltese Cross" d = x*y*d2 - a*c2 if @s, d = d/c2/c4, endif elseif @V=="Nephroid" d = (c2-a)^3 - b*y2 if @s, d = d/c2/c4, endif elseif @V=="Nephroid of Freeth" d = c2*(x*2+y2-a)^2 - 4*a*(c2-a*x)^2 if @s, d = d/c2/c4, endif elseif @V=="Ophiuride" d = x*c2 - b*x*y - a*y2 if @s, d = d/c3, endif elseif @V=="Parabola" d = y - a*x2 if @s, d = d/c2, endif elseif @V=="Pear" d = x*x2*(a-x) - b*y2 if @s, d = d/c4, endif elseif @V=="Piriform Quartic" d = b^2*y2 + (x-a)*x^3 if @s, d = d/c4, endif elseif @V=="Quadrifolium" d = c2^3 - a*x2*y2 if @s, d = d/c2/c4, endif elseif @v=="Right Strophoid" d = x*c2 - a*d2 if @s, d = d/c3, endif elseif @V=="Semicubical Parabola" d = y2 - a*x*x2 if @s, d = d/c3, endif elseif @V=="Serpentine" d = y*(x+b*y)^2 - a*x if @s, d = d/c3, endif elseif @V=="Sextic" d = c2*(c2-a*y)^2 - x2*d2^2 if @s, d = d/c2/c4, endif elseif @V=="Tighrope" d = y2*(a^2*b-x2) - x2*(a-x)^2 if @s, d = d/c4, endif elseif @V=="Torpedo" d = c2^2 - a*x*d2 if @s, d = d/c4, endif elseif @V=="Trifolium" d = c2^2 - a*x*(x2-b*y2) if @s, d = d/c4, endif elseif @V=="Trisectrix of Catalan" d = b*y2 + x2*(x-a) if @s, d = d/c3, endif elseif @V=="Trisectrix of Ceva" d = c2^3 - a*(b*x2-y2)^2 if @s, d = d/c2/c4, endif elseif @V=="Trisectrix of Delange" d = (c2-a)^2 - x2*c2 if @s, d = d/c4, endif elseif @V=="Trisectrix of Longchamps" d = x*(x2-3*y2) - a*c2 if @s, d = d/c3, endif elseif @V=="Trisectrix of Maclaurin" d = x*c2 - a*(3*x2-y2) if @s, d = d/c3, endif elseif @V=="Trott" d = 144*(x^4+y^4) - 225*c2 + 350*x2*y2 + 81*a if @s, d = d/c4, endif elseif @V=="Visiera" d = x*c2 - a*(x2+2*y2) if @s, d = d/c3, endif elseif @V=="Viviani's" d = y^4 + a*d2 ;mathworld version if @s, d = d/c4, endif elseif @V=="Wavy Cross" ; Swastika d = x2*x2-y2*y2 + a*x*y if @s, d = d/c4, endif elseif @V=="Windmill" d = 4*x2*y2*c2 - a*(x2-b*y2)^2 if @s, d = d/c2/c4, endif elseif @V=="Witch of Agnesi" d = x2*y + a^2*(y-a) if @s, d = d/c3, endif endif if m_abs_value d = abs(d) endif return d endfunc private: float a, float b default: rating = Recommended title = "Curve" int param version caption = "Version" default = 220804 visible = @version < 220804 endparam heading text = "(Current is 220804.)" visible = @version < 220804 endheading param V caption="Curve" enum="Alain" "Ampersand" "Anguinea" "Arachnea" "Arcs of Samothrace" \ "Astroid" "Atripthaloid" "Beetle'" "Besace" "Bicorn" "Bicuspid" \ "Bifoliate" "Bifolium" "Bow" "Bow Tie" "Bullet-Nose" "Butterfly"\ "Cardioid" "Cassinian Ovals" "Cayley's Sextic" "Cissoid of Diocles" \ "Conchoid of Nicomedes" "Cornoid" "Cross" "Cubical Parabola"\ "Curve of Cramer" "Cubic of de Sluze" "Deltoid" "Dipole" \ "Double-Heart" "Dumbbell""Egg of Granville" "Egg of Kepler" "Eight"\ "Ellipse" "Euler's Cubic" "Folium of Descartes" \ "Folium of Durer" "Folium of Kepler""Function" "Heat Capacity"\ "Hippopede" "Humbert's Cubic" "Hyperbola" "Kampyle of Eudoxus""Kappa" "Kiepert" \ "Kiss" "Knot" "Kulp Quartic" "Lemniscate of Bernoulli"\ "Limacon of Pascal" "Maltese Cross""Nephroid" "Nephroid of Freeth"\ "Ophiuride" "Parabola" "Pear""Piriform Quartic" "Quadrifolium" \ "Right Strophoid""Semicubical Parabola" "Serpentine" "Sextic" \ "Tighrope" "Torpedo" "Trifolium" "Trisectrix of Catalan" \ "Trisectrix of Ceva" "Trisectrix of Delange" "Trisectrix of Longchamps" \ "Trisectrix of Maclaurin""Trott" "Visiera" "Viviani's" "Wavy Cross" \ "Windmill" "Witch of Agnesi" default=0 endparam func F caption = "Function" default = sin() visible = @V=="Function" endfunc bool param exch caption = "Switch x and y?" default = false visible = @V!="Astroid" && @V!="Folium of Descartes" \ && @V!="Quadrifolium" && @V!="Trott" endparam bool param s caption = "Z scale?" default = true ; 221220, was false endparam float param aa caption = "a" default=1 endparam float param p caption = "Power" default = 0.5 visible = @V=="Astroid" endparam float param bb caption = "b" default=0.5 visible=@V=="Alain" || @V=="Ampersand" || @V=="Atripthaloid" \ || @V=="Beetle'" || @V=="Besace" || @V=="Bifolium" || @V=="Bullet-Nose" \ || @V=="Cassinian Ovals" || @V=="Cross" ||@V=="Cubical Parabola" \ || @V=="Cubic of de Sluze" || @V=="Conchoid of Nicomedes" \ || @V=="Curve of Cramer" || @V=="Double-Heart" || @V=="Dumbbell" \ || @V=="Ellipse" || @V=="Egg of Granville" || @V=="Euler's Cubic" \ || @V=="Folium of Kepler" ||@V=="Function" || @V=="Hippopede" \ || @V=="Limacon of Pascal" || @V=="Nephroid" || @V=="Ophiuride" \ || @V=="Pear" || @V== "Piriform Quartic" || @V=="Serpentine" \ || @V=="Tighrope" || @V=="Trifolium" || @V=="Trisectrix of Catalan" \ || @V=="Trisectrix of Ceva" || @V=="Windmill" endparam } class Standard_Decomposition(common.ulb:GradientColoring) { public: float func ResultIndex(complex pz) float d = atan2(pz) ; get angle of z IF (d < 0) ; it's negative d = d + #pi * 2 ; make it positive ENDIF return d / (#pi * 2) endfunc default: title = "Decomposition" } class GammaBlend{ public: func GammaBlend( ) gamma = 2.2 ; default value endfunc func SetGamma(float gam) gamma = gam endfunc func Zero() rr = 0, gg = 0, bb = 0, aa = 0, ff = 0 ;gamma = 2 endfunc func AddOne(color c, float f) rr = rr + f * red(c)^gamma gg = gg + f * green(c)^gamma bb = bb + f * blue(c)^gamma aa = aa + f * alpha(c) ff = ff + f endfunc color func Final( ) rr = (rr / ff)^(1/gamma) gg = (gg / ff)^(1/gamma) bb = (bb / ff)^(1/gamma) aa = (aa / ff) return rgba(rr, gg, bb, aa) endfunc color func Blend2(color c1, float f1, color c2, float f2) float rr = ((f1* red(c1)^gamma + f2* red(c2)^gamma)/(f1+f2))^(1/gamma) float gg = ((f1*green(c1)^gamma + f2*green(c2)^gamma)/(f1+f2))^(1/gamma) float bb = ((f1* blue(c1)^gamma + f2* blue(c2)^gamma)/(f1+f2))^(1/gamma) float aa = (f1*alpha(c1) + f2*alpha(c2) ) /(f1+f2) return rgba(rr, gg, bb, aa) endfunc protected: float rr, float gg, float bb, float aa, float ff float gamma } class JLB_FractaliaSFormula(SFormula) { ; based on the Fractalia formulas in mt.ufm public: import "common.ulb" ; for UtilityTransform func JLB_FractaliaSFormula(Generic pparent) SFormula.SFormula(pparent) r1 = new JLB_Random(0) r1.Init(@seed) r2 = new JLB_Random(0) r2.Init(@seed2) a=@aa, ax=@aax, axx=@aaxx, axy=@aaxy, ay=@aay, ayy=@aayy b=@bb, bx=@bbx, bxx=@bbxx, bxy=@bbxy, by=@bby, byy=@bbyy if @randomize > 0 float c = 1, if @randomize == "Replace", c = 0, endif a = c * a + @scale * r1.RandomFloat2(0), b = c * b + @scale * r1.RandomFloat2(0) ax = c * ax + @scale * r1.RandomFloat2(0), bx = c * bx + @scale * r1.RandomFloat2(0) axx = c * axx + @scale * r1.RandomFloat2(0), bxx = c * bxx + @scale * r1.RandomFloat2(0) axy = c * axy + @scale * r1.RandomFloat2(0), bxy = c * bxy + @scale * r1.RandomFloat2(0) ay = c * ay + @scale * r1.RandomFloat2(0), by = c * by + @scale * r1.RandomFloat2(0) ayy = c * ayy + @scale * r1.RandomFloat2(0), byy = c * byy + @scale * r1.RandomFloat2(0) if @randomize2 > 0 c = 1, if @randomize2 == "Replace", c = 0, endif a = c * a + @scale2 * r2.RandomFloat2(0), b = c * b + @scale2 * r2.RandomFloat2(0) ax = c * ax + @scale2 * r2.RandomFloat2(0), bx = c * bx + @scale2 * r2.RandomFloat2(0) axx = c * axx + @scale2 * r2.RandomFloat2(0), bxx = c * bxx + @scale2 * r2.RandomFloat2(0) axy = c * axy + @scale2 * r2.RandomFloat2(0), bxy = c * bxy + @scale2 * r2.RandomFloat2(0) ay = c * ay + @scale2 * r2.RandomFloat2(0), by = c * by + @scale2 * r2.RandomFloat2(0) ayy = c * ayy + @scale2 * r2.RandomFloat2(0), byy = c * byy + @scale2 * r2.RandomFloat2(0) endif endif if @addT > 0 finalTransform = new @finalUT(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @return the new pz bool func Iterate(State S) bool func Iterate(State S) if S.bailed return true endif if S.tooMany return false endif float x = real(S.pz) float y = imag(S.pz) float xx = a + ax*x + axx*x*x + axy*x*y + ay*y + ayy*y*y float yy = b + bx*x + bxx*x*x + bxy*x*y + by*y + byy*y*y complex znew = xx + flip(yy) + ppc if @addT == "c" ppc = finalTransform.Iterate(ppc) elseif @addT == "z" znew = finalTransform.Iterate(znew) endif S.Update(znew, S.pc) return S.bailed endfunc protected: complex ppc float a, float ax, float axx, float axy, float ay, float ayy float b, float bx, float bxx, float bxy, float by, float byy JLB_Random r1 JLB_Random r2 UtilityTransform finalTransform default: rating = Recommended title = "Fractalia" int param version caption = "Version" default = 221010 visible = @version < 221010 endparam bool param p_Mtype caption = "M-type?" default = true endparam complex param p_seed caption = "Seed for J-type" default = (0.3, 0) visible = !@p_Mtype endparam bool param hide caption = "Hide parameters?" default = true endparam heading caption = "Method" visible = !@hide endheading heading text = "xnew = a + ax*x + axx*x^2 + axy*x*y + ay*y + ayy*y^2" visible = !@hide endheading heading text = "ynew = b + bx*x + bxx*x^2 + bxy*x*y + by*y + byy*y^2" visible = !@hide endheading heading text = "znew = xnew + flip(ynew)" visible = !@hide endheading ; heading ; caption = "Parameters" ; endheading float param aa caption = "a" default = 0 visible = !@hide endparam float param aax caption = "ax" default = 1 visible = !@hide endparam float param aaxx caption = "axx" default = 0.5 visible = !@hide endparam float param aaxy caption = "axy" default = 0.4 visible = !@hide endparam float param aay caption = "ay" default = -0.7 visible = !@hide endparam float param aayy caption = "ayy" default = 0.9 visible = !@hide endparam float param bb caption = "b" default = 0 visible = !@hide endparam float param bbx caption = "bx" default = -0.2 visible = !@hide endparam float param bbxx caption = "bxx" default = 0.8 visible = !@hide endparam float param bbxy caption = "bxy" default = 0.9 visible = !@hide endparam float param bby caption = "by" default = -0.7 visible = !@hide endparam float param bbyy caption = "byy" default = -0.2 visible = !@hide endparam heading caption = "Randomization" endheading param randomize caption = "Randomize?" enum = "No" "Replace" "Add to" default = 0 endparam int param seed caption ="Seed" hint = "Seed for random numbers" default = 12345679 visible = @randomize > 0 endparam float param scale caption = "Randomness size" default = 1 min = 0 visible = @randomize > 0 endparam param randomize2 caption = "More?" enum = "No" "Replace" "Add to" default = 0 visible = @randomize > 0 endparam int param seed2 caption ="Second seed" hint = "Seed for random numbers" default = 12345679 visible = @randomize2 > 0 endparam float param scale2 caption = "Randomness size" default = 0.1 min = 0 visible = @randomize2 > 0 endparam heading caption = "Tweak after each iteration" endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 endparam UtilityTransform param finalUT caption = "Tweak transform" default = Tweak visible = @addT > 0 selectable = false endparam } class JLB_BubbleTransform(common.ulb:UserTransform) { ; public: func JLB_BubbleTransform(Generic pparent) UserTransform.UserTransform(pparent) if @use_ctr ctr = #center else ctr = @center endif if @xfer == 0 ctr2 = ctr else ctr2 = @ectr endif r0 = @rad0 r1 = r0 if @mode == "Linear" r1 = r0 * @rfac endif rot = exp(flip(@angle*#pi/180)) b_rot = 1 if @b != 2 b_rot = exp(flip(@b_angle*#pi/180)) endif endfunc complex func Iterate(complex pz) complex tmp = b_rot * (pz-ctr) float x = abs(real(tmp)) float y = abs(imag(tmp)) float r = (x^@b + y^@b)^(1/@b) float term = @s * (r/r0)^@p if r <= r0 complex pznew = ctr2 + term * (pz-ctr) * rot return pznew endif if @mode == "None" return pz endif float keep if @mode == "Linear" if r >= r1 return pz endif keep = 1 - (r-r0)/(r1-r0) elseif @mode == "Exponential" keep = exp(-@a*(r/r0-1)) else;if @mode == "Gaussian" keep = exp(-@a*(r/r0-1)^2) endif float ang = keep * @angle complex pznew = (1-keep) * pz + \ keep * (ctr2 + term * (pz-ctr) * exp(flip(ang*#pi/180))) return pznew endfunc private: complex ctr complex ctr2 float r0 float r1 complex b_rot complex rot default: title = "Bubble" rating = Recommended param use_ctr caption = "Use Screen Center" default = true endparam complex param center caption = "Bubble center" default = (0, 0) visible = !@use_ctr endparam float param rad0 caption = "Bubble size" default = 0.25/#magn ;min = 0 endparam float param b caption = "Bubble shape" default = 2 min = 0.25 ; best value? endparam float param b_angle caption = "Bubble rotation" default = 0 visible = @b != 2 endparam heading endheading param xfer caption = "Transfer from" enum = "Bubble" "Elsewhere" default = 0 endparam complex param ectr caption = "Elsewhere center" default = #center visible = @xfer == 1 endparam float param s caption = "Strength" default = 2 ;min = 0 endparam float param p caption = "Power"; from center" default = 2 ;min = 0 endparam float param angle caption = "Rotation angle" default = 0 endparam param mode caption = "Fade mode" enum = "None" "Linear" "Exponential" "Gaussian" default = 0 endparam float param rfac caption = "Fade distance factor" default = 1.5 min = 1 visible = @mode == 1 endparam float param a caption = "Fade strength" default = 5 min = 0 visible = @mode > 1 endparam } class JLB_DCurve2(Distance) { ; Curve definitions from ; https://mathcurve.com/courbes2d.gb/courbes2d.shtml ; www.2dcurves/com ; mathworld.wolfram.com/ ; with inspiration and some code from Otto Magus, om.ulb ; ; sources vary on what some of the curves are. ; I've changed a^2 many places to just a and b^2 to b, ; added parameters in a sensible way to many curves, ; and omitted any curves with three or more parameters ; 230204 added Heart curve. Backwards compatible. public: func JLB_DCurve2(Generic pparent) Distance(pparent) if @numpar == "1" a = @aa else a = real(@ab) b = imag(@ab) endif endfunc float func Iterate(complex zz) float x=real(zz) float y=imag(zz) if @exch float t=x, x=y, y=t endif float c1 = cabs(zz), float c2 = c1*c1, float c3 = c2*c1, float c4 = c2*c2 float x2 = x^2, float y2 = y^2, float d2 = x2-y2 float d = 0 if @numpar == "1" ;45 if @V1=="Arachnea" d = (c2-a*x)^2 - a^2*c2 if @s, d = d/c4, endif elseif @V1=="Arcs of Samothrace" d = x2*(a*x2-y2)^2 - y2*c2 if @s, d = d/c4/c2, endif elseif @V1=="Astroid" d = x^@p + y^@p - a^@p if @s, d = d/c1^@p, endif elseif @V1=="Bicorn" d = y2*(a-x2) - (x2+2*a*y-a^2)^2 if @s, d = d/c4, endif elseif @V1=="Bicuspid" d = (x2-a^2)*(x-a)^2 + (y2-a)^2 if @s, d = d/c4, endif elseif @V1=="Bifoliate" d = x2*x2 + y2*y2 - a*x*y2 if @s, d = d/c4, endif elseif @V1=="Bow" d = x2*x2 - a*y*d2 if @s, d = d/c4, endif elseif @V1=="Bow Tie" d = x2*x2*c2 - a*d2 if @s, d = d/c2/c4, endif elseif @V1=="Butterfly" d = x2^3 + y2^3 - a*x2 if @s, d = d/c2/c4, endif elseif @V1=="Cardioid" d = (c2-a*x)^2 - a^2*c2 if @s, d = d/c4, endif elseif @V1=="Cayley's Sextic" d = 4*(c2-a*x)^3 - 27*a^2*c2^2 if @s, d = d/c4/c2, endif elseif @V1=="Cissoid of Diocles" d = x*c2 - a*y2 if @s, d = d/c3, endif elseif @V1=="Cornoid" d = c2^3 + a*(3*x^4 - 6*x2*y2 - 5*y^4) + 8*a^2*y2 - 4*a^3 if @s, d = d/c2/c4, endif elseif @V1=="Deltoid" d = c2^2 + 8*a*x*(x2-3*y2) + 18*a^2*c2 - 27*a^4 if @s, d = d/c4, endif elseif @V1=="Dipole" d = c2^3 - a*x2 if @s, d = d/c2/c3, endif elseif @V1=="Egg of Kepler" d = c2^2 - a*x^3 if @s, d = d/c4, endif elseif @V1=="Eight" d = x^4 - a*d2 if @s, d = d/c4, endif elseif @V1=="Folium of Descartes" d = x^3 + y^3 - 3*a*x*y if @s, d = d/c3, endif elseif @V1=="Folium of Durer" d = c2*(2*c2-a)^2 - a^2*x2 if @s, d = d/c2/c4, endif elseif @V1=="Heart" d = (x2+y2-a)^3 - x2*y2*y if @s, d = d/c3/c3, endif elseif @V1=="Heat Capacity" d = x2*(x-y) + a if @s, d = d/c3, endif elseif @V1=="Humbert's Cubic" d = x^3 - 3*x*y2 - a if @s, d = d/c3, endif elseif @V1=="Hyperbola" d = x2 - a*y2 if @s, d = d/c2, endif elseif @V1=="Kampyle of Eudoxus" d = x^4 - a*c2 if @s, d = d/c4, endif elseif @V1=="Kappa" d = x2*c2 - a*y2 if @s, d = d/c4, endif elseif @V1=="Kiepert" d = c2^3 - a*x*(x2-3*y2) if @s, d = d/c2/c4, endif elseif @V1=="Kiss" d = a^2*y2 - (a-x2)^3 if @s, d = d/c2/c4, endif elseif @V1=="Knot" d = (x2-a)^2 - y2*(3*a+2*y) if @s, d = d/c4, endif elseif @V1=="Kulp Quartic" d = x2*y2 - a*(a-y2) if @s, d = d/c4, endif elseif @V1=="Lemniscate of Bernoulli" d = c2^2 - a*d2 if @s, d = d/c4, endif elseif @V1=="Maltese Cross" d = x*y*d2 - a*c2 if @s, d = d/c2/c4, endif elseif @V1=="Nephroid of Freeth" d = c2*(x*2+y2-a)^2 - 4*a*(c2-a*x)^2 if @s, d = d/c2/c4, endif elseif @V1=="Parabola" d = y - a*x2 if @s, d = d/c2, endif elseif @V1=="Quadrifolium" d = c2^3 - a*x2*y2 if @s, d = d/c2/c4, endif elseif @V1=="Right Strophoid" d = x*c2 - a*d2 if @s, d = d/c3, endif elseif @V1=="Semicubical Parabola" d = y2 - a*x*x2 if @s, d = d/c3, endif elseif @V1=="Sextic" d = c2*(c2-a*y)^2 - x2*d2^2 if @s, d = d/c2/c4, endif elseif @V1=="Torpedo" d = c2^2 - a*x*d2 if @s, d = d/c4, endif elseif @V1=="Trisectrix of Delange" d = (c2-a)^2 - x2*c2 if @s, d = d/c4, endif elseif @V1=="Trisectrix of Longchamps" d = x*(x2-3*y2) - a*c2 if @s, d = d/c3, endif elseif @V1=="Trisectrix of Maclaurin" d = x*c2 - a*(3*x2-y2) if @s, d = d/c3, endif elseif @V1=="Trott" d = 144*(x^4+y^4) - 225*c2 + 350*x2*y2 + 81*a if @s, d = d/c4, endif elseif @V1=="Visiera" d = x*c2 - a*(x2+2*y2) if @s, d = d/c3, endif elseif @V1=="Viviani's" d = y^4 + a*d2 ;mathworld version if @s, d = d/c4, endif elseif @V1=="Wavy Cross" ; Swastika d = x2*x2-y2*y2 + a*x*y if @s, d = d/c4, endif elseif @V1=="Witch of Agnesi" d = x2*y + a^2*(y-a) if @s, d = d/c3, endif endif else;if @numpar == "2" ; 32 !! if @V2=="Alain" d = d2^2 - a*x2 + b*y2 if @s, d = d/c4, endif elseif @V2=="Ampersand" d = (y2-x2)*(x-a)*(2*x-3*a) - b*(c2-2*x)^2 if @s, d = d/c4, endif elseif @V2=="Anguinea" d = y*(x2+a^2) - a*b*x if @s, d = d/c3, endif elseif @V2=="Atripthaloid" d = x^4*c2 - (a*x2-b)^2 if @s, d = d/c4/c2, endif elseif @V2=="Beetle'" d = c2*(c2-a*x-a*y) - b*x2*y2 if @s, d = d/c4, endif elseif @V2=="Besace" d = (x2-b*y)^2 - a*d2 if @s, d = d/c4, endif elseif @V2=="Bifolium" d = c2^2 - (a*x+b*y)*x2 if @s, d = d/c4, endif elseif @V2=="Bullet-Nose" d = a^2*y2 - b^2*x*2 - x2*y2 if @s, d = d/c4, endif elseif @V2=="Cassinian Ovals" d = c2^2 - a*d2 + b^4 if @s, d = d/c4, endif elseif @V2=="Conchoid of Nicomedes" d = (x-a)^2*c2 - b^2*x2 if @s, d = d/c4, endif elseif @V2=="Cross" d = x2*y2 - a*x2 - b*y2 if @s, d = d/c4, endif elseif @V2=="Cubical Parabola" d = a^2*y - x^3 + b*x if @s, d = d/c3, endif elseif @V2=="Cubic of de Sluze" d = a*(x-a)*c2 - b*x2 if @s, d = d/c3, endif elseif @V2=="Curve of Cramer" d = x*c2 + (a+b)*x2 - (a-b)*y2 if @s, d = d/c3, endif elseif @V2=="Double-Heart" d = x2*c2 - (a+b)*x2*y + (a-b)^2*y/4 if @s, d = d/c4, endif elseif @V2=="Dumbbell" d = b*y2 - x^4*(a-x2) if @s, d = d/c2/c4, endif elseif @V2=="Egg of Granville" d = x2*y2 - (x-a)*(b-x) if @s, d = d/c4, endif elseif @V2=="Ellipse" d = b*x2 + a*y2 - a/b if @s, d = d/c2, endif elseif @V2=="Euler's Cubic" d = y*(y2-a) - b*x*(x2-a) if @s, d = d/c3, endif elseif @V2=="Folium of Kepler" d = c2*(x*(x-a)+y2) - b*x*y2 if @s, d = d/c4, endif elseif @V2=="Function" d = y - a*real(@F(b*x)) if @s, d = d/c1, endif elseif @V2=="Hippopede" d = c2^2 - a*x2 - b*y2 if @s, d = d/c4, endif elseif @V2=="Limacon of Pascal" d = (c2-a*x)^2 - b*c2 if @s, d = d/c4, endif elseif @V2=="Nephroid" d = (c2-a)^3 - b*y2 if @s, d = d/c2/c4, endif elseif @V2=="Ophiuride" d = x*c2 - b*x*y - a*y2 if @s, d = d/c3, endif elseif @V2=="Pear" d = x*x2*(a-x) - b*y2 if @s, d = d/c4, endif elseif @V2=="Piriform Quartic" d = b^2*y2 + (x-a)*x^3 if @s, d = d/c4, endif elseif @V2=="Serpentine" d = y*(x+b*y)^2 - a*x if @s, d = d/c3, endif elseif @V2=="Tighrope" d = y2*(a^2*b-x2) - x2*(a-x)^2 if @s, d = d/c4, endif elseif @V2=="Trifolium" d = c2^2 - a*x*(x2-b*y2) if @s, d = d/c4, endif elseif @V2=="Trisectrix of Ceva" d = c2^3 - a*(b*x2-y2)^2 if @s, d = d/c2/c4, endif elseif @V2=="Windmill" d = 4*x2*y2*c2 - a*(x2-b*y2)^2 if @s, d = d/c2/c4, endif endif endif if (d >= 0 && @how == "Negative") || (d <= 0 && @how == "Positive") return -1 endif return abs(d) ; if m_abs_value ; d = abs(d) ; endif ; return d endfunc private: float a, float b default: rating = Recommended title = "Curve2" int param version caption = "Version" default = 221220 visible = @version < 221220 endparam heading text = "(Current is 221220.)" visible = @version < 221220 endheading param numpar caption = "Group" enum = "1" "2" default = 0 endparam param V1 ;45 caption="Curve" enum= "Arachnea" "Arcs of Samothrace" \ "Astroid" "Bicorn" "Bicuspid" \ "Bifoliate" "Bow" "Bow Tie" "Butterfly" \ "Cardioid" "Cayley's Sextic" "Cissoid of Diocles" \ "Cornoid" "Deltoid" "Dipole" "Egg of Kepler" "Eight" \ "Folium of Descartes" "Folium of Durer" "Heat Capacity" \ "Humbert's Cubic" "Hyperbola" "Kampyle of Eudoxus" "Kappa" "Kiepert" \ "Kiss" "Knot" "Kulp Quartic" "Lemniscate of Bernoulli" \ "Maltese Cross" "Nephroid of Freeth" "Parabola" "Quadrifolium" \ "Right Strophoid" "Semicubical Parabola" "Sextic" \ "Torpedo" "Trisectrix of Delange" "Trisectrix of Longchamps" \ "Trisectrix of Maclaurin" "Trott" "Visiera" "Viviani's" "Wavy Cross" \ "Witch of Agnesi" "Heart" default=0 visible = @numpar == "1" endparam param V2 ;32 caption="Curve" enum = "Alain" "Ampersand" "Anguinea" "Atripthaloid" \ "Beetle'" "Besace" "Bifolium" "Bullet-Nose" \ "Cassinian Ovals" "Conchoid of Nicomedes" "Cross" "Cubical Parabola" \ "Cubic of de Sluze" "Curve of Cramer" "Double-Heart" "Dumbbell" \ "Ellipse" "Egg of Granville" "Euler's Cubic" \ "Folium of Kepler" "Function" "Hippopede" \ "Limacon of Pascal" "Nephroid" "Ophiuride" \ "Pear" "Piriform Quartic" "Serpentine" \ "Tighrope" "Trifolium" "Trisectrix of Catalan" \ "Trisectrix of Ceva" "Windmill" default=0 visible = @numpar == "2" endparam func F caption = "Function" default = sin() visible = @numpar=="2" && @V2=="Function" endfunc bool param exch caption = "Switch x and y?" default = false visible = @numpar == "2" || \ ( @V1 !="Astroid" && @V1 !="Folium of Descartes" \ && @V1 !="Quadrifolium" && @V1 !="Trott" ) endparam bool param s caption = "Z scale?" default = true ; 221220, was false endparam float param aa caption = "a" default = 1 visible = @numpar == "1" endparam float param p caption = "Power" default = 0.5 visible = @numpar == "1" && @V1=="Astroid" endparam complex param ab caption = "a and b" hint = "a is the real part, b the imaginary part" default = (1,0.5) visible = @numpar == "2" endparam param how caption = "How" enum = "Positive" "Negative" "Both" default = 2 endparam } class JLB_DTransform(Distance) { public: import "common.ulb" func JLB_DTransform(Generic pparent) Distance(pparent) m_dist = new @dist(this) zzTransform = new @T(this) endfunc func Init(complex zz) m_dist.SetParams(0, m_abs_value) endfunc float func Iterate(complex zz) zz = zzTransform.Iterate(@a*zz) float d = m_dist.Iterate(zz) return d endfunc private: Distance m_dist UserTransform zzTransform default: title = "Transform" rating = Recommended int param version caption = "Version" default = 230103 visible = @version < 230103 endparam heading text = "(Curent is 230103.}" visible = @version < 230103 endheading heading text = "Transform T(a*z)" endheading complex param a caption = "a" default = 1 endparam UserTransform param T caption = "Transform T" default = JLB_FTransform endparam heading endheading Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_Combo2Transform (common.ulb:UserTransform) { ; 230822 Added variations. Doesn't break old fractals. public: func JLB_Combo2Transform(Generic pparent) UserTransform(pparent) m_T1 = new @T1(this) m_T2 = new @T2(this) if @mode > 0 m_T3 = new @T3(this) endif m_Combine = new Combine() mult = 0 if @variation == "Variation 1" mult = 1 elseif @variation == "Variation 2" mult = -1 endif endfunc complex func Iterate(complex pz) float px = real(pz) float py = imag(pz) complex dx complex dy complex tx1, complex tx2, complex tx3 complex ty1, complex ty2, complex ty3 if @mode == 0 ;"T1 op1 T2" ;tx2 = @a2*@T2(@b2*px) ;ty2 = @a2*@T2(@b2*py) tx2 = iter(2, @b2*px) ty2 = iter(2, @b2*py) if @op1a == "( )" ; T1(T2(.)) ; dx = @a1*@T1(@b1*tx2) ; dy = @a1*@T1(@b1*ty2) dx = iter(1, @b1*tx2 + mult*px) dy = iter(1, @b1*ty2 + mult*py) else ; T1(.) op1 T2(.) ; tx1 = @a1*@T1(@b1*px) ; ty1 = @a1*@T1(@b1*py) tx1 = iter(1, @b1*px) ty1 = iter(1, @b1*py) dx = m_Combine.go(tx1, @op1a, tx2) dy = m_Combine.go(ty1, @op1a, ty2) endif elseif @mode == 1 ;"T1 op1 (T2 op2 T3)" if @op2a == "( )" ; T1 op T2(T3(.)) ; tx3 = @a3*T3(@b3*px) ; ty3 = @a3*T3(@b3*py) tx3 = iter(3, @b3*px) ty3 = iter(3, @b3*py) ; tx2 = @a2*T2(@b2*tx3) ; ty2 = @a2*T2(@b2*ty3) tx2 = iter(2, @b2*tx3 + mult*px) ty2 = iter(2, @b2*ty3 + mult*py) if @op1 == "( )" ; T1(T2(T3(.))) ; dx = @a1*T1(@b1*tx2) ; dy = @a1*T1(@b1*ty2) dx = iter(1, @b1*tx2 + mult*px) dy = iter(1, @b1*ty2 + mult*py) else ; T1(.) op1 T2(T3(.)) ; tx1 = @a1*T1(@b1*px) ; ty1 = @a1*T1(@b1*py) tx1 = iter(1, @b1*px) ty1 = iter(1, @b1*py) dx = m_Combine.go(tx1, @op1, tx2) dy = m_Combine.go(ty1, @op1, ty2) endif else; ; tx3 = @a3*T3(@b3*px) ; ty3 = @a3*T3(@b3*py) tx3 = iter(3, @b3*px) ty3 = iter(3, @b3*py) ; tx2 = @a2*T2(@b2*px) ; ty2 = @a2*T2(@b2*py) tx2 = iter(2, @b2*px) ty2 = iter(2, @b2*py) tx2 = m_Combine.go(tx2, @op2a, tx3) ty2 = m_Combine.go(ty2, @op2a, ty3) if @op1 == "( )" ; T1(T2(.) op2a T3(.)) ; dx = @a1*T1(@b1*tx2) ; dy = @a1*T1(@b1*ty2) dx = iter(1, @b1*tx2 + mult*px) dy = iter(1, @b1*ty2 + mult*px) else ; tx1 = @a1*T1(@b1*px) ; ty1 = @a1*T1(@b1*py) tx1 = iter(1, @b1*px) ty1 = iter(1, @b1*py) dx = m_Combine.go(tx1, @op1, tx2) dy = m_Combine.go(ty1, @op1, ty2) endif endif else ;@mode == "(T1 op1 T2) op2 T3" ; @op3a == "( )" not allowed with this ordering ; tx2 = @a2*T2(@b2*px) ; ty2 = @a2*T2(@b2*py) tx2 = iter(2, @b2*px) ty2 = iter(2, @b2*py) if @op1 == "( )" ; (T1(T2(.))) op2b T3(.) ; tx1 = @a1*T1(@b1*tx2) ; ty1 = @a1*T1(@b1*ty2) tx1 = iter(1, @b1*tx2) ty1 = iter(1, @b1*ty2) else ; tx1 = @a1*T1(@b1*px) ; ty1 = @a1*T1(@b1*py) tx1 = iter(1, @b1*px) ty1 = iter(1, @b1*py) tx1 = m_Combine.go(tx1, @op1, tx2) ty1 = m_Combine.go(ty1, @op1, ty2) endif ; tx3 = @a3*T3(@b3*px) ; ty3 = @a3*T3(@b3*py) tx3 = iter(3, @b3*px) ty3 = iter(3, @b3*py) dx = m_Combine.go(tx1, @op2b, tx3) dy = m_Combine.go(ty1, @op2b, ty3) endif return dx + flip(dy) endfunc complex func iter(int n, complex v) if n == 1 return @a1*m_T1.Iterate(v) elseif n == 2 return @a2*m_T2.Iterate(v) else return @a3*m_T3.Iterate(v) endif endfunc private: UserTransform m_T1 UserTransform m_T2 UserTransform m_T3 Combine m_Combine float mult default: rating = Recommended title = "Combo2 Transform" int param version caption = "Version" default = 230117 visible = @version < 230117 endparam heading text = "(Current is 230117.)" visible = @version < 230117 endheading param mode caption = "How to combine" default = 0 enum = "T1 op T2" "T1 op1 (T2 op2 T3)" "(T1 op1 T2) op2 T3" endparam param op1 caption = "Operator 1" enum = "+" "-" "*" "/" "^" "( )" default = 0 hint = "How to combine Formula values." visible = @mode > 0 endparam param op1a caption = "Operator" enum = "+" "-" "*" "/" "^" "( )" default = 0 hint = "How to combine Formula values." visible = @mode == 0 endparam param op2a caption = "Operator 2" enum = "+" "-" "*" "/" "^" "( )" default = 2 hint = "How to combine Formula values." visible = (@mode == 1) endparam param op2b caption = "Operator 2" enum = "+" "-" "*" "/" "^" ; () doesn't make sense default = 2 hint = "How to combine Formula values." visible = (@mode == 2) endparam param variation enum = "Original" "Variation 1" "Variation 2" default = 0 visible = ( @mode == 0 && @op1a == "( )" ) || \ ( @mode == 1 && ( @op2a == "( )" || @op1 == "( )" ) ) || \ ( @mode == 2 && @op1 == "( )" ) endparam heading caption = "a1*T1(b1*v)" endheading complex param a1 caption = "Combo2 a1" default = 1 hint = "multiplies T1" endparam complex param b1 caption = "Combo2 b1" default = 2 hint = "multiplies T1's arg" endparam UserTransform param T1 caption = "Combo2 T1" default = JLB_FTransform endparam heading caption = "a2*T2(b2*v)" endheading complex param a2 caption = "Combo2 a2" default = 2.7 hint = "multiplies T2" endparam complex param b2 caption = "Combo2 b2" default = 2.7 hint = "multiplies T2's arg" endparam UserTransform param T2 caption = "Combo2 T2" default = JLB_FTransform endparam heading caption = "a3*T3(b3*v)" visible = @mode > 0 endheading complex param a3 caption = "Combo2 a3" default = 0 hint = "multiplies T3" visible = @mode > 0 endparam complex param b3 caption = "Combo2 b3" default = 1 hint = "multiplies T3's arg" visible = @mode > 0 endparam UserTransform param T3 caption = "Combo2 T3" default = JLB_FTransform visible = @mode > 0 endparam } class JLB_Gnarl5SFormula(SFormula) { ; Based on the Gnarl formula from mt.ufm, as a SFormula. ; Calculate dx and dy based on the real or imaginary part of z, then combine. ; Add a multiple of z, #pixel, or a seed. ; Changed from Gnarl3: omit SFormula; each f now is a UserTransform public: import "common.ulb" func JLB_Gnarl5SFormula(Generic pparent) SFormula.SFormula(pparent) m_rot = cos(@theta*#pi/180) + flip(sin(@theta*#pi/180)) m_T = new @T(this) if @addT > 0 m_f4 = new @f4(this) endif endfunc func Init(complex pz, complex pc) ppc = #pixel if !@p_Mtype ppc = @p_seed endif endfunc ; @param pz ; @param pc ; @return the new pz bool func Iterate(State S) if s.bailed return true endif if s.tooMany return false endif complex pz = S.pz ; complex pc = @p_seed complex znew float xc = real(ppc) float yc = imag(ppc) complex zrot = pz * m_rot float x = real(zrot) float y = imag(zrot) complex px, complex py if @args == "x,y" px = x, py = y else ; "y,x" the original version, usually better px = y, py = x endif complex dz = m_T.Iterate(px+flip(py)) float dx = real(dz) float dy = imag(dz) float qx, float qy if @args == "x,y" qx = xc, qy = yc else ; "y,x" the original version, usually better qx = yc, qy = xc endif dx = dx + qx dy = dy + qy if @xyop == "-dx +dy" || @xyop == "-dx -dy" dx = -dx endif if @xyop == "+dx -dy" || @xyop == "-dx -dy" dy = -dy endif znew = pz + @step * (dx + flip(dy)) if @addT == "c" ppc = m_f4.iterate(ppc) elseif @addT == "z" znew = m_f4.Iterate(znew) endif S.Update(znew, S.pc) return S.bailed endfunc ;protected: complex m_rot UserTransform m_T Combine m_Combine complex ppc UtilityTransform m_f4 default: rating = Recommended title = "Gnarl5" int param version caption = "Version (Gnarl5_SFormula)" default = 230116 visible = @version < 230116 endparam bool param p_Mtype caption = "M-type?" default = true endparam complex param p_seed caption = "Seed for J-type" default = (0.5,0.5) visible = !@p_Mtype endparam float param theta caption = "Theta" default = 0 hint = "First rotate z by this much (360 = full circle)" endparam param args caption = "args" enum = "x,y" "y,x" default = 1 endparam UserTransform param T caption = "Gnarl5 T" default = JLB_Combo2Transform selectable = false ; don't want this changed endparam heading endheading param xyop caption = "Gnarl5 dx,dy" enum = "+dx +dy" "+dx -dy" "-dx +dy" "-dx -dy" default = 1 hint = "final multiplcations of real and imaginary parts of z_calc" endparam param step caption = "Gnarl5 Step_size" default = 0.1 hint = "How much of this to use. z_new = z + Step_size * z_calc" endparam heading caption = "Tweak after each iteration" ;visible = !@p_std endheading param addT caption = "Type" enum = "none" "c" "z" default = 0 ;visible = !@p_std endparam UtilityTransform param f4 caption = "Tweak transform" default = Tweak visible = @addT > 0 ;&& !@p_std selectable = false endparam } class JLB_MaskShape(common.ulb:DirectColoring) { ; some code copied from Bubble transform and Circle2 ; public: func JLB_MaskShape(Generic pparent) DirectColoring(pparent) if @use_ctr, ctr = #center, else, ctr = @center, endif if @type == "Circle", h = v = @rad, else, h = @horiz, v = @vert, endif rot = exp(flip(-@angle*#pi/180)) rr = red(gradient(0)) gg = green(gradient(0)) bb = blue(gradient(0)) endfunc func Init(complex pz, complex ppixel) DirectColoring.Init(pz, ppixel) endfunc func Iterate(complex pz) endfunc color func Result(complex pz) m_solid = false complex tmp = rot * (pz-ctr) float x = abs(real(tmp)) float y = abs(imag(tmp)) float r= ( (x/h)^@p + (y/v)^@p ) ^ (1/@p) float opacity = 0 if r <= 1 opacity = 1 else if @mode == "Linear" if r > @rfade opacity = 0 else opacity = 1 - (r-1)/(@rfade-1) endif elseif @mode == "Exponential" opacity = exp(-@a*(r-1)) elseif @mode == "Gaussian" opacity = exp(-@a*(r-1)^2) endif endif if @invert opacity = 1 - opacity endif return rgba(rr, gg, bb, opacity) endfunc private: complex ctr complex rot float h, float v float rr, float gg, float bb default: title = "Mask from shape" rating = recommended bool param invert caption = "Invert mask shape?" default = false endparam param use_ctr caption = "Use Screen Center" default = true endparam complex param center caption = "Shape center" default = (0, 0) visible = !@use_ctr endparam param type caption = "Shape type" enum = "Circle" "Ellipse" default = 0 endparam float param p caption = "Shape power" default = 2 min = 0 hint = "Power = 2 for circle, < 2 for indented, < 1 for astroid, > 2 for shapes nearing a square." endparam float param rad caption = "Circle radius" default = 2.0 min = 0 visible = @type == 0 endparam float param horiz caption = "Ellipse x value" default = 2.0 min = 0 visible = @type == 1 endparam float param vert caption = "Ellipse y value" default = 2.0 min = 0 visible = @type == 1 endparam float param angle caption = "Rotation angle" default = 0 visible = @p != 2.0 || (@type == 1 && @horiz != @vert) endparam param mode caption = "Fade mode" enum = "None" "Linear" "Exponential" "Gaussian" default = 0 endparam float param rfade caption = "Fade distance factor" default = 1.5 min = 1 visible = @mode == 1 endparam float param a caption = "Fade strength" default = 5 min = 0 visible = @mode > 1 endparam } class JLB_ChaoticAttractors(common.ulb:GradientColoring) { ; A re-writing of Latööcarfian Attractors in mt.ucl ; with drastic changes. ; Standard. Alpha, Beta, and Gamma are from mt.ucl; ; the others I've found online. ; http://paulbourke.net/fractals ; https://anaconda.org/jbednar/clifford_attractor/notebook ; https://softologyblog.wordpress.com/2017/03/04/2d-strange-attractors/ public: import "common.ulb" $define debug ; only for printing values from Randomize( ) func JLB_ChaoticAttractors(Generic pparent) GradientColoring(pparent) m_PixelCoordinate = new PixelCoordinate(pparent) r = new JLB_Random(0) r.Init(@seed) if @version >= 230219 a = real(@ab), b = imag(@ab), c = real(@cd), d = imag(@cd) else a = @aa, b = @bb, c = @cc, d = @dd endif if @more && @exch && @which == "Type 2" float t t = a, a = c, c = t t = b, b = d, d = t endif m1 = new @f1(this) m2 = new @f2(this) assign_Fs( ) assign_ab( ) DoTheWork() ; possibly randomize. fill the pix array endfunc func Init(complex pz, complex ppixel) GradientColoring.Init(pz, ppixel) m_PixelCoordinate.Init(pz) endfunc func Iterate(complex pz) GradientColoring.Iterate(pz) endfunc float func ResultIndex(complex pz) if (pix[#x,#y] == 0) m_solid = @solid return 0 endif ; densest gets index of 1 float idx idx = (log(pix[#x, #y]) / log(max)) ^(1-@contrast/2) return idx endfunc func DoTheWork( ) if @rnd Randomize( ) endif int x = 0, int y = 0 ; initialize pix array while x < #width y = 0 while y < #height pix[x, y] = 0 y = y + 1 endwhile x = x + 1 endwhile max = 0 float xx = 0.1, float yy = 0.1 if @more, xx = real(@start), yy = imag(@start), endif int i = 0 int iters = #width * #height * @density while i < iters float xnew, float ynew OneIteration(xx, yy, xnew, ynew) xx = xnew yy = ynew complex zz = m_PixelCoordinate.Iterate(xx+flip(yy)) x = round(real(zz)) y = round(imag(zz)) ; plot the point only if within the screen if x >= 0 && x < #width && y >= 0 && y < #height pix[x, y] = pix[x, y] + 1 if pix[x, y] > max max = pix[x, y] endif endif i = i + 1 endwhile if @lprnt print("Goodness ", L_eval( )) endif endfunc func OneIteration(float xx, float yy, float &xnew, float &ynew) if @which == "Type 1" xnew = m11.Iterate(a11*yy) + cd1*m12.Iterate(a12*xx) ; like Standard (any, bbaa, cd), ynew = m21.Iterate(a21*xx) + cd2*m22.Iterate(a22*yy) ; Clifford (1122, aabb, cd) else;if @which == "Type 2" xnew = m11.Iterate(a11*yy) - m12.Iterate(a12*xx) ; like DeJong (1212, ab, cd) ynew = m21.Iterate(cd1*xx) - m22.Iterate(cd2*yy) endif endfunc func Randomize( ) ; modified code from mt.ucl ; We randomize the parameters in a loop ; until we find a set that has a reasonably ; high Lyapunov exponent. int times = 0, int tbest = -1 ; int maxtimes = 1000 float L = -1, float Lmax = -1 float asave = 0, float bsave = 0, float csave = 0, float dsave = 0 while L < @Lgoal && times < @maxtimes ; Parameters in the range (-@range to +@range) a = @range * r.RandomFloat2(0) b = @range * r.RandomFloat2(0) c = @range * r.RandomFloat2(0) d = @range * r.RandomFloat2(0) ; keep only 3 decimal places a = round(1000*a)/1000, b = round(1000*b)/1000 c = round(1000*c)/1000, d = round(1000*d)/1000 assign_ab( ) L = L_eval( ) times = times + 1 if L > Lmax Lmax = L asave = a, bsave = b, csave = c, dsave = d tbest = times endif endwhile L = Lmax a = asave, b = bsave, c = csave, d = dsave assign_ab( ) Lmax = round(100*Lmax)/100 if (@prnt || @lprnt) && times >= @maxtimes print("Best result at try #", tbest,"; goodness ", Lmax) endif if @prnt print("a ", a, " b ", b, " c ", c, " d ", d, " goodness ", Lmax, \ " in ", times, " tries") endif endfunc float func L_eval( ) float L = 0, float Lsum = 0 float lx = 0.1, float ly = 0.1 float xe = lx + 1e-6, float ye = ly int i = 0 while i < 1000 ; Pickover suggests 10 million iterations, ; but this is enough to produce decent ; attractors float xx, float yy OneIteration(lx, ly, xx, yy) float xsave = xx, float ysave = yy, lx = xe, ly = ye i = i + 1 OneIteration(lx, ly, xx, yy) float dLx = xx - xsave, float dLy = yy - ysave float dL2 = dLx * dLx + dLy * dLy float df = 1e12 * dL2 float rs = 1/sqrt(df) xe = xsave + rs * (xx - xsave) ye = ysave + rs * (yy - ysave) xx = xsave, yy = ysave Lsum = Lsum + log(df) L = 0.721347 * Lsum / i lx = xx ly = yy endwhile return round(100*L)/100 endfunc func assign_Fs( ) if @more if @p == "1122" m11 = m1, m12 = m1, m21 = m2, m22 = m2 elseif @p == "1212" m11 = m1, m12 = m2, m21 = m1, m22 = m2 elseif @p == "1221" m11 = m1, m12 = m2, m21 = m2, m22 = m1 elseif @p == "2112" m11 = m2, m12 = m1, m21 = m1, m22 = m2 elseif @p == "2121" m11 = m2, m12 = m1, m21 = m2, m22 = m1 else;if @p == "2211" m11 = m2, m12 = m2, m21 = m1, m22 = m1 endif else ; default 1212 m11 = m1, m12 = m2, m21 = m1, m22 = m2 endif endfunc func assign_ab( ) if @which == "Type 1" if @qx1 == "aabb" || !@more a11 = a, a12 = a, a21 = b, a22 = b elseif @qx1 == "abab" a11 = a, a12 = b, a21 = a, a22 = b elseif @qx1 == "abba" a11 = a, a12 = b, a21 = b, a22 = a elseif @qx1 == "baab" a11 = b, a12 = a, a21 = a, a22 = b elseif @qx1 == "baba" a11 = b, a12 = a, a21 = b, a22 = a else;if @qx1 == "bbaa" a11 = b, a12 = b, a21 = a, a22 = b endif endif if @qx2 == "cd" cd1 = c, cd2 = d else;if @qx2 == "dc" cd1 = d, cd2 = c endif if @which == "Type 2" if @qy == "ab" || !@more a11 = a, a12 = b else;if @qy == "ba" a11 = b, a12 = a endif endif endfunc private: PixelCoordinate m_PixelCoordinate JLB_Random r float a, float b, float c, float d float a11, float a12, float a21, float a22 float cd1, float cd2 int pix[#width, #height] int max Transfer m1, Transfer m2 Transfer m11, Transfer m12, Transfer m21, Transfer m22 default: title = "Chaotic Attractors" rating = recommended heading text = "Use with pixel(jp) formula in jlb.ufm or jp.ufm. Use Plug-In Coloring (Gradient) in jlb.ucl." endheading heading text = "Ignores all Mappings." endheading int param version caption = "Version" default = 230219 visible = @version < 230219 endparam heading text = "(Current is 230219.)" visible = @version < 230219 endheading bool param solid caption = "Use Solid Background?" default = true endparam ;render = !@rnd ;false param which caption = "Attractor" enum = "Type 1" "Type 2" default = 0 endparam Transfer param f1 caption = "F1" default = ChaosFunc enabled = false endparam Transfer param f2 caption = "F2" default = Chaosfunc enabled = false endparam bool param more caption = "More options?" default = false endparam param p caption = "F Permutation" enum = "1122" "1212" "1221" "2112" "2121" "2211" default = 0 visible = @more endparam bool param exch caption = "Exchange ab and cd?" default = false visible = @more && @which == "Type 2" endparam param qx1 caption = "ab Permutation" enum = "aabb" "abab" "abba" "baab" "baba" "bbaa" default = 0 visible = @more && (@which == "Type 1") endparam param qy caption = "ab Permutation" enum = "ab" "ba" default = 0 visible = @more && (@which == "Type 2") endparam param qx2 caption = "cd Permutation" enum = "cd" "dc" default = 0 visible = @more endparam bool param lprnt caption = "Print goodness value?" default = false visible = !@more || !@prnt|| !@rnd endparam bool param rnd caption = "Find good abcd?" default = false visible = @more endparam bool param prnt caption = "Print abcd?" default = false visible = @rnd endparam heading endheading int param maxtimes caption = "How many tries" default = 100 min = 1 visible = @rnd endparam float param range caption = "Range for searching" default = 3 min = 1 visible = @rnd endparam float param Lgoal caption = "How good?" default = 0.25 min = 0 visible = @rnd endparam int param seed caption = "Seed" default = 12345678 min = 1 visible = @rnd endparam heading text = "a=real(ab), b=imag(ab), c=real(cd), d=imag(cd)" visible = !@rnd && @version >= 230219 endheading complex param ab default = (-1.4, 1.6) visible = !@rnd && @version >= 230219 endparam complex param cd default = (1.0, 0.7) visible = !@rnd && @version >= 230219 endparam float param aa caption = "a" default = -1.4 visible = !@rnd && @version < 230219 endparam param bb caption = "b" default = 1.6 visible = !@rnd && @version < 230219 endparam float param cc caption = "c" default = 1.0 visible = !@rnd && @version < 230219 endparam float param dd caption = "d" default = 0.7 visible = !@rnd && @version < 230219 endparam heading endheading complex param start caption = "Starting Point" default = (0.1,0.1) visible = @more endparam int param density caption = "Sample Density" default = 10 min = 1 endparam float param contrast caption = "Added Contrast (-1:+1)" default = 0 min = -1 max = +1 endparam } class ChaosFunc(common.ulb:Transfer){ public: import "common.ulb" func ChaosFunc(Generic pparent) Transfer(pparent) if @version < 230512 type = @w else type = @ww endif endfunc float func Iterate(float pr) float ans if type == 0, ans = sin(pr) elseif type == 1, ans = cos(pr) elseif type == 2, ans = tanh(pr) elseif type == 3, ans = Math.RealBesselJ0(pr) elseif type == 4, ans = Math.RealBesselJ1(pr) elseif type == 5 if pr == 0, ans = 1, else, ans = sin(pr)/pr, endif else;if type == 6 if @version < 230512 ans = abs(pr)^@p if pr < 0, ans = -ans, endif else ans = pr / (1 + abs(pr)^@pp) endif endif return ans endfunc private: int type default: title = "Chaos Function" int param version caption = "Version" default = 230512 visible = @version < 230512 endparam param w caption = " F" enum = "sin" "cos" "tanh" "J0" "J1" "sync" "spower" default = 0 visible = @version > 230512 endparam param ww caption = " F" enum = "sin" "cos" "tanh" "J0" "J1" "sync" "rational" default = 0 visible = @version >= 230512 endparam float param p caption = "p" default = 1 visible = @version < 230512 && @w == 6 endparam float param pp caption = "power" default = 1 visible = @version >= 230512 && @ww == 6 endparam } class JLB_Waves(common.ulb:UserTransform) { public: func JLB_Waves(Generic pparent) Generic.Generic(pparent) hfreq = real(@freq) vfreq = imag(@freq) phase = @p*#pi/180 rot = exp(flip(@dir*#pi/180)) ctr = #center if !@center ctr = @ctr endif endfunc complex func Iterate(complex pz) ; if mapping, called with pz = #pixel float factor = 1 if @fade > 0 factor = 0 float dist = cabs(pz - ctr) if @fade == "Power" if dist < @distance factor = (1 - dist/@distance)^@power endif elseif @fade == "Exponential" float t = dist/@distance factor = exp(-t) else;if @fade == "Gaussian" float t = sqr(dist/@distance) factor = exp(-t) endif endif if factor > 0 float x = real(pz-ctr), float y = imag(pz-ctr) float arg = phase if @type == 1 || @type == 3 arg = arg + @hfac * real(@FH(hfreq*x)) else arg = arg + hfreq*x endif if @type == 2 || @type == 3 arg = arg + @vfac * real(@FV(vfreq*y)) else arg = arg + vfreq*y endif float delta = @a * real(@F(arg)) pz = pz + delta * rot * factor endif return pz endfunc private: float hfreq, float vfreq float phase complex rot complex ctr default: title = "Waves" rating = Recommended int param version caption = "Version-Waves" default = 230601 visible = @version < 230601 endparam heading text = "(Curent is 230601.}" visible = @version < 230601 endheading bool param center caption = "Use screen center?" default = true endparam complex param ctr caption = "Center" default = (0,0) visible = !@center endparam float param a caption = "Amplitude" default = 0.5 endparam float param dir caption = "Direction (degrees)" hint = "90 means vertical" default = 90 endparam func F caption = "Function" default = sin() endfunc complex param freq caption = "H && V frequencies" default = (1,1) endparam float param p caption = "Phase (degrees)" default = 0 endparam param type caption = "Modification" enum = "none" "H" "V" "both" default = 0 endparam func FH caption = "H modification" default = ident() visible = @type == 1 || @type == 3 endfunc float param hfac caption = "H factor" default = 1 visible = @type == 1 || @type == 3 endparam func FV caption = "V modification" default = ident() visible = @type == 2 || @type == 3 endfunc float param vfac caption = "V factor" default = 1 visible = @type == 2 || @type == 3 endparam heading visible = @fade > 0 endheading param fade caption = "Decay?" enum = "None" "Power" "Exponential" "Gaussian" default = 0 endparam float param distance caption = "Distance" default = 1 min = 0 visible = @fade > 0 endparam float param power caption = "Decay power" default = 2 min = 0 visible = @fade == 1 endparam } class JLB_Simple(common.ulb:GradientColoring) { func JLB_Simple(Generic pparent) GradientColoring(pparent) if @fz == 2 FT = new @fUT(this) endif endfunc float func ResultIndex(complex pz) complex zz = @a * pz if @fz == 1 zz = @FF(zz) elseif @fz == 2 zz = FT.Iterate(zz) endif float x = real(zz), float y = imag(zz) float ax = abs(x), float ay = abs(y) float a = cabs(zz) float ans = 0 if @type == "Mod" ans = a elseif @type == "Real" ans = x elseif @type == "Imag" ans = y elseif @type == "Real+Imag" ans = x+y elseif @type == "Real-Imag" ans = x-y elseif @type == "Real*Imag" ans = x*y elseif @type == "Real/Imag" ans = x/y elseif @type == "Imag/Real" ans = y/x elseif @type == "Real^Imag" ans = x^y elseif @type == "Imag^Real" ans = y^x elseif @type == "|Real|+|Imag|" ans = ax+ay elseif @type == "|Real|-|Imag|" ans = ax-ay elseif @type == "Max(|Real|,|Imag|)" ans = ax if ay > ax, ans = ay, endif elseif @type == "Max(Real,Imag)" ans = x ; range = (0,a) if y > x, ans = y, endif elseif @type == "Min(|Real|,|Imag|)" ans = ax if ay < ax, ans = ay, endif elseif @type == "Min(Real,Imag)" ans = x if y < x, ans = y, endif else;if @type == "Angle" ans = atan2(zz)/ (2 * #pi) if ans < 0 ans = ans + 1 endif endif ; make index be in [0,1] even if Repeat Gradient isn't checked ans = ans - trunc(ans) if ans < 0 ans = ans + 1 endif return ans endfunc private: UserTransform FT default: title = "Simple Coloring" rating = recommended int param version caption = "Version-Simple Coloring" default = 230601 visible = @version < 230601 endparam heading text = "(Curent is 230601.}" visible = @version < 230601 endheading heading text = "Color using the last z value. Use a pixel formula if you want to \ color the gradient." endheading heading caption = "Use z, F(a*z), or T(a*z)" endheading param fz caption = "Which?" enum = "a*z" "F(a*z)" "T(a*z)" default = 0 endparam complex param a caption = "a" default = 1 endparam func FF caption = "Function" default = sinh() visible = @fz == 1 endfunc UserTransform param fUT caption = "Transform" default = JLB_MoebiusUserTransform visible = @fz == 2 endparam param type caption = "Color by" enum = "Mod" "Real" "Imag" "Real+Imag" "Real-Imag" "Real*Imag" \ "Real/Imag" "Imag/Real" "Real^Imag" "Imag^Real" \ "|Real|+|Imag|""|Real|-|Imag|" \ "Max(|Real|,|Imag|)" "Max(Real,Imag)" "Min(|Real|,|Imag|)" "Min(Real,Imag)"\ "Angle" default = 0 endparam } class JLB_DFuncs(Distance) { public: import "common.ulb" func JLB_DFuncs(Generic pparent) Distance(pparent) m_dist = new @dist(this) m_fx = new @f1(this) m_fy = new @f2(this) endfunc func Init(complex zz) m_dist.SetParams(0, true) endfunc float func Iterate(complex zz) int n = 0 while n < @nmax complex xa = @b*ComplexToFloat.go(zz, @xarg, true) complex ya = @b*ComplexToFloat.go(zz, @yarg, true) float xx = real(@a*m_fx.Iterate(xa)) float yy = real(@a*m_fy.Iterate(ya)) if @kind == "Add" zz = zz + xx + flip(yy) else zz = xx + flip(yy) endif n = n + 1 endwhile return m_dist.Iterate(zz) endfunc private: Distance m_dist UserTransform m_fx UserTransform m_fy default: title = "Funcs" rating = Recommended heading caption = "xnew = a*F(b*arg)" endheading UserTransform param f1 caption = "X function" default = JLB_FTransform ;selectable = false endparam param xarg caption = "X arg" enum = "Mod" "x" "y" "x+y" "x-y" "x*y" "x/y" "y/x" "x^y" "y^x" "|x|+|y|" "|x|-|y|" \ "Max(|x|,|y|)" "Max(x,y)" "Min(|x|,|y|)" "Min(x,y)" "Angle" endparam heading caption = "ynew = a*F(b*arg)" endheading UserTransform param f2 caption = "Y function" default = JLB_FTransform ;selectable = false endparam param yarg caption = "Y arg" enum = "Mod" "x" "y" "x+y" "x-y" "x*y" "x/y" "y/x" "x^y" "y^x" "|x|+|y|" "|x|-|y|" \ "Max(|x|,|y|)" "Max(x,y)" "Min(|x|,|y|)" "Min(x,y)" "Angle" endparam heading endheading complex param a caption = "a" default = 1 endparam complex param b caption = "b" default = 1 endparam int param @nmax caption = "Iterations" default = 1 min = 1 endparam param kind caption = "Add or Replace" enum = "Add" "Replace" default = 1 endparam heading endheading Distance param dist default = JLB_DXYZ selectable = false endparam } class JLB_ComboFunction (common.ulb:UserTransform) { public: func JLB_ComboFunction(Generic pparent) UserTransform(pparent) m_F1 = new @F1(this) m_F2 = new @F2(this) if @mode > 0 m_F3 = new @F3(this) endif m_Combine = new Combine() endfunc complex func Iterate(complex pz) float px = real(pz) float py = imag(pz) complex xx complex yy complex xx1, complex xx2, complex xx3 complex yy1, complex yy2, complex yy3 if @mode == 0 ;"F1 op1 F2" ;xx2 = @a2*@F2(@b2*px) ;yy2 = @a2*@F2(@b2*py) xx2 = iter(2, @b2*px) yy2 = iter(2, @b2*py) if @op1 == "( )" ; F1(F2(.)) ; xx = @a1*@F1(@b1*xx2) ; yy = @a1*@F1(@b1*yy2) xx = iter(1, @b1*xx2) yy = iter(1, @b1*yy2) else ; F1(.) op1 F2(.) ; xx1 = @a1*@F1(@b1*px) ; yy1 = @a1*@F1(@b1*py) xx1 = iter(1, @b1*px) yy1 = iter(1, @b1*py) xx = m_Combine.go(xx1, @op1, xx2) yy = m_Combine.go(yy1, @op1, yy2) endif elseif @mode == 1 ;"F1 op1 (F2 op2 F3)" if @op2a == "( )" ; F1 op F2(F3(.)) ; xx3 = @a3*F3(@b3*px) ; yy3 = @a3*F3(@b3*py) xx3 = iter(3, @b3*px) yy3 = iter(3, @b3*py) ; xx2 = @a2*F2(@b2*xx3) ; yy2 = @a2*F2(@b2*yy3) xx2 = iter(2, @b2*xx3) yy2 = iter(2, @b2*yy3) if @op1 == "( )" ; F1(F2(F3(.))) ; xx = @a1*F1(@b1*xx2) ; yy = @a1*F1(@b1*yy2) xx = iter(1, @b1*xx2) yy = iter(1, @b1*yy2) else ; F1(.) op1 F2(F3(.)) ; xx1 = @a1*F1(@b1*px) ; yy1 = @a1*F1(@b1*py) xx1 = iter(1, @b1*px) yy1 = iter(1, @b1*py) xx = m_Combine.go(xx1, @op1, xx2) yy = m_Combine.go(yy1, @op1, yy2) endif else ; xx3 = @a3*F3(@b3*px) ; yy3 = @a3*F3(@b3*py) xx3 = iter(3, @b3*px) yy3 = iter(3, @b3*py) ; xx2 = @a2*F2(@b2*px) ; yy2 = @a2*F2(@b2*py) xx2 = iter(2, @b2*px) yy2 = iter(2, @b2*py) xx2 = m_Combine.go(xx2, @op2a, xx3) yy2 = m_Combine.go(yy2, @op2a, yy3) if @op1 == "( )" ; F1(F2(.) op2a F3(.)) ; xx = @a1*F1(@b1*xx2) ; yy = @a1*F1(@b1*yy2) xx = iter(1, @b1*xx2) yy = iter(1, @b1*yy2) else ; xx1 = @a1*F1(@b1*px) ; yy1 = @a1*F1(@b1*py) xx1 = iter(1, @b1*px) yy1 = iter(1, @b1*py) xx = m_Combine.go(xx1, @op1, xx2) yy = m_Combine.go(yy1, @op1, yy2) endif endif else ;@omode == "(F1 op1 F2) op2 F3" ; @op3a == "()" not allowed with this ordering ; xx2 = @a2*F2(@b2*px) ; yy2 = @a2*F2(@b2*py) xx2 = iter(2, @b2*px) yy2 = iter(2, @b2*py) if @op1 == "( )" ; (F1(F2(.))) op2b F3(.) ; xx1 = @a1*F1(@b1*xx2) ; yy1 = @a1*F1(@b1*yy2) xx1 = iter(1, @b1*xx2) yy1 = iter(1, @b1*yy2) else ; xx1 = @a1*F1(@b1*px) ; yy1 = @a1*F1(@b1*py) xx1 = iter(1, @b1*px) yy1 = iter(1, @b1*py) xx1 = m_Combine.go(xx1, @op1, xx2) yy1 = m_Combine.go(yy1, @op1, yy2) endif ; xx3 = @a3*F3(@b3*px) ; yy3 = @a3*F3(@b3*py) xx3 = iter(3, @b3*px) yy3 = iter(3, @b3*py) xx = m_Combine.go(xx1, @op2b, xx3) yy = m_Combine.go(yy1, @op2b, yy3) endif return xx + flip(yy) endfunc complex func iter(int n, complex v) if n == 1 return @a1*m_F1.Iterate(v) elseif n == 2 return @a2*m_F2.Iterate(v) else return @a3*m_F3.Iterate(v) endif endfunc private: UserTransform m_F1 UserTransform m_F2 UserTransform m_F3 Combine m_Combine default: rating = Recommended title = "Combo Function" int param version caption = "Version" default = 230817 visible = @version < 230817 endparam heading text = "(Current is 230817.)" visible = @version < 230817 endheading param mode caption = "How to combine" default = 0 enum = "F1 op F2" "F1 op1 (F2 op2 F3)" "(F1 op1 F2) op2 F3" endparam param op1 caption = "Operator 1" enum = "+" "-" "*" "/" "^" "( )" default = 0 hint = "How to combine Formula values." endparam param op2a caption = "Operator 2" enum = "+" "-" "*" "/" "^" "( )" default = 2 hint = "How to combine Formula values." visible = (@mode == 1) endparam param op2b caption = "Operator 2" enum = "+" "-" "*" "/" "^" ; () doesn't make sense default = 2 hint = "How to combine Formula values." visible = (@mode == 2) endparam heading caption = "a1*F1(b1*v)" endheading complex param a1 caption = "XX a1" default = 1 hint = "multiplies F1" endparam complex param b1 caption = "XX b1" default = 2 hint = "multiplies F1's arg" endparam UserTransform param F1 caption = "XX F1" default = JLB_FTransform selectable = false endparam heading caption = "a2*F2(b2*v)" endheading complex param a2 caption = "XX a2" default = 2.7 hint = "multiplies F2" endparam complex param b2 caption = "XX b2" default = 2.7 hint = "multiplies F2's arg" endparam UserTransform param F2 caption = "XX F2" default = JLB_FTransform selectable = false endparam heading caption = "a3*F3(b3*v)" visible = @mode > 0 endheading complex param a3 caption = "XX a3" default = 0 hint = "multiplies F3" visible = @mode > 0 endparam complex param b3 caption = "XX b3" default = 1 hint = "multiplies F3's arg" visible = @mode > 0 endparam UserTransform param F3 caption = "XX F3" default = JLB_FTransform visible = @mode > 0 selectable = false endparam } class JLB_FuncsUserTransform(common.ulb:UserTransform) { public: func JLB_FuncsUserTransform(Generic pparent) UserTransform(pparent) m_fx = new @f1(this) m_fy = new @f2(this) endfunc complex func Iterate(complex pz) int n = 0 while n < @nmax complex xa = @bb*ComplexToFloat.go(pz, @xarg, true) complex ya = @bb*ComplexToFloat.go(pz, @yarg, true) float xx = real(@aa*m_fx.Iterate(xa)) float yy = real(@aa*m_fy.Iterate(ya)) if @kind == "Add" pz = pz + xx + flip(yy) else pz = xx + flip(yy) endif n = n + 1 endwhile return pz endfunc private: UserTransform m_fx UserTransform m_fy default: rating = Recommended title = "Funcs" int param version caption = "Version" default = 230819 visible = @version < 230819 endparam heading text = "(Current is 230819.)" visible = @version < 230819 endheading heading caption = "xnew = aa*F(bb*arg)" endheading UserTransform param f1 caption = "X function" default = JLB_FTransform ;selectable = false endparam param xarg caption = "X arg" enum = "Mod" "x" "y" "x+y" "x-y" "x*y" "x/y" "y/x" "x^y" "y^x" "|x|+|y|" "|x|-|y|" \ "Max(|x|,|y|)" "Max(x,y)" "Min(|x|,|y|)" "Min(x,y)" "Angle" endparam heading caption = "ynew = aa*F(bb*arg)" endheading UserTransform param f2 caption = "Y function" default = JLB_FTransform ;selectable = false endparam param yarg caption = "Y arg" enum = "Mod" "x" "y" "x+y" "x-y" "x*y" "x/y" "y/x" "x^y" "y^x" "|x|+|y|" "|x|-|y|" \ "Max(|x|,|y|)" "Max(x,y)" "Min(|x|,|y|)" "Min(x,y)" "Angle" endparam heading endheading complex param aa caption = "aa" default = 1 endparam complex param bb caption = "bb" default = 1 endparam int param @nmax caption = "Iterations" default = 1 min = 1 endparam param kind caption = "Add or Replace" enum = "Add" "Replace" default = 1 endparam }