Wait! Nevermind about the bugs! I updated everything with an actual user interface! It's not a nice user interface, mind you. Compile it, and run it *windowed*, so that you can refer to the commented out command list in the code.
Uhh, google base motif fractal if you don't know what this is.
Basically, it generates images like this:
the fractal is considered to be the limit of the approximation. This program approximates a fractal. Generally, every single point where two lines meet, is a part of the fractal. almost all the lines are not part of the fractal, they're just there so you can see the in between part.
What's really interesting is that some of these curves have *no* area. They are infinitely long, continuous, non-differentiable, curves. Some of these curves, however, *do* fill a 2d volume. Basically, these curves have weird properties.
Command list:
`Q/A changes totalIterations`
`W/S changes variable4 - determines switching by each bit`
`spacebar renders up to depth totalIterations
`E/D changes variable1 - determines height`
`R/F changes variable2 - determines offset`
`T/G changes variable3 - determines width`
`not all of these values are used all of the time.
`z changes the base to a line`
`x changes the base to a triangle``
`c changes the base to a square`
`d changes the base to a regular hexagon`
`up arrow/down arrow switches between fractals`
`p randomizes all variables1-4`
`o zeros all variables 1-4`
`i exits the loop, in case you try to render too much!`
`y/h increases preview render depth.`
`preview render depth is so that you can see how the fractal will look while you're messing with
`the other variables. Normally this should be 2 or 3.
note - press and hold I if you're stuck! press I, hold I, and then change preview render depth and/or total iterations.
version using cloggy's DLL:
`BaseMotifFractal
`BaseMotifFractal.dba
`======================
`BaseMotifFractal
`BaseMotifFractal.dba
`======================
type vec2
x as float
y as float
invx as integer
invy as integer
endtype
type graphScreen
tl as vec2 `top left corner
br as vec2 `bottom right corner
c as vec2 `location of (0,0) on the screen, or where it should be if its not visible. (in pixels)
PPU as float `Pixels per graph Unit.
endtype
global cmc `Current frame Mouse Click
global lmc `Last frame Mouse Click
global mh `Mouse Held
global ZeroVector as vec2
zeroVector.x=0
zeroVector.y=0
dim graphs(2) as graphScreen
graphs(0).tl=ZeroVector
graphs(0).br.x=screen width()/3-10
graphs(0).br.y=screen height()/2-5
`visible: from 1.1 to -1.1 on the y axis
graphs(0).ppu=screen height()/4.4
`(.5,0) is on the center of the graph area
graphs(0).c.x=(graphs(0).tl.x+graphs(0).br.x-graphs(0).ppu)/2
graphs(0).c.y=(graphs(0).tl.y+graphs(0).br.y)/2
graphs(1).tl.x=0
graphs(1).tl.y=screen height()/2+5
graphs(1).br.x=screen width()/3-10
graphs(1).br.y=screen height()
`visible: from 1.1 to -1.1 on the y axis
graphs(1).ppu=screen height()/4.4
`(.5,0) is on the center of the graph area
graphs(1).c.x=(graphs(1).tl.x+graphs(1).br.x-graphs(1).ppu)/2
graphs(1).c.y=(graphs(1).tl.y+graphs(1).br.y)/2
graphs(2).tl.x=0
graphs(2).tl.y=0
graphs(2).br.x=screen width()
graphs(2).br.y=screen height()
`visible: from 1.1 to -1.1 on the y axis
graphs(2).ppu=400
`(.5,0) is on the center of the graph area
graphs(2).c.x=(graphs(2).tl.x+graphs(2).br.x-graphs(2).ppu)/2
graphs(2).c.y=(graphs(2).tl.y+graphs(2).br.y)/2
global ret as vec2
dim finalCurve(-1) as vec2
dim base(1) as vec2
`--
clearBase()
temp as vec2
lastP as vec2
switch as integer
switch=0
totalIterations as integer
variable1 as float
variable2 as float
variable3 as float
variable4 as integer
whichfractal as integer
whichfractal=1
fractalchanged as integer
fractalchanged=1
previewrenderdepth as integer
variable1=.5
variable2=.5
variable3=.5
variable4=0
totalIterations=5
previewRenderDepth=2
`Q/A changes totalIterations`
`W/S changes variable4 - determines switching by each bit`
`spacebar renders up to depth totalIterations
`E/D changes variable1 - determines height`
`R/F changes variable2 - determines offset`
`T/G changes variable3 - determines width`
`not all of these values are used all of the time.
`z changes the base to a line`
`x changes the base to a triangle``
`c changes the base to a square`
`d changes the base to a regular hexagon`
`up arrow/down arrow switches between fractals`
`p randomizes all variables1-4`
`o zeros all variables 1-4`
`i exits the loop, in case you try to render too much!`
`y/h increases preview render depth.`
`preview render depth is so that you can see how the fractal will look while you're messing with
`the other variables. Normally this should be 2 or 3.
d3d_init
do
if keystate(16)=0 and keystate(17)=0 and keystate(30)=0 and keystate(31)=0 and spacekey()=0
if upkey()=0 and downkey()=0 and keystate(21)=0 and keystate(35)=0
switch=0 `lol, lots of ifs...
endif
endif
if switch=0
if keystate(16) =1
switch=1
fractalchanged=1
inc totalIterations
endif
if keystate(30)=1
switch=1
if totalIterations>1
fractalchanged=1
dec totalIterations
endif
endif
if keystate(17)=1
switch=1
fractalchanged=1
inc variable4
endif
if keystate(31)=1
switch=1
fractalchanged=1
dec variable4
endif
if keystate(21)=1
switch=1
fractalchanged=1
inc previewrenderdepth
endif
if keystate(35)=1
switch=1
fractalchanged=1
if previewrenderdepth>1 then dec previewrenderdepth
endif
if upkey()=1 or downkey()=1
switch=1
if upkey()=1
inc whichFractal
if whichFractal>8 then whichFractal=1
endif
if downkey()=1
dec whichFractal
if whichFractal<1 then whichFractal=8
endif
fractalchanged=1
if whichFractal=1
preset1()
endif
if whichFractal=2
preset2()
endif
if whichFractal=3
preset3()
endif
if whichFractal=4
preset4()
endif
if whichFractal=5
variable1=0
variable2=0
variable3=0
variable4=0
RightAngleSweep(variable4)
endif
if whichFractal=6
variable1=.5
variable2=.5
variable3=0
variable4=0
AngleGeneralized(variable4,variable2,variable1)
endif
if whichFractal=7
variable1=.4
variable2=.3
variable3=.02
variable4=0
kochGeneralized(variable4,variable2,variable3,variable1)
endif
if whichFractal=8
variable1=.2
variable2=.5
variable3=.2
variable4=0
squiggleSweep(variable4,variable2,variable3,variable1)
endif
endif
endif
tempscale#=.1/screen fps()
if tempscale#>.01 then tempscale#=.01
if keystate(18)=1
fractalchanged=1
inc variable1,tempscale#
endif
if keystate(19)=1
fractalchanged=1
inc variable2,tempscale#
endif
if keystate(20)=1
fractalchanged=1
inc variable3,tempscale#
endif
if keystate(32)=1
fractalchanged=1
dec variable1,tempscale#
endif
if keystate(33)=1
fractalchanged=1
dec variable2,tempscale#
endif
if keystate(34)=1
fractalchanged=1
dec variable3,tempscale#
endif
if keystate(25)=1
fractalchanged=1
variable1=rnd(10000)/10000.0
variable2=rnd(10000)/10000.0
variable3=rnd(10000)/10000.0
variable4=rnd(65536)
if whichFractal=7
variable1=variable1/3.0
variable2=variable2*.45
variable3=variable3*(1-2*variable2)+variable2
endif
if whichFractal=7
variable1=variable1/3.0
variable2=variable2*.45
variable3=variable3*(1-2*variable2)
endif
endif
if keystate(24)=1
fractalchanged=1
variable1=0
variable2=0
variable3=0
variable4=0
endif
if keystate(44) or keystate(45) or keystate(46) or keystate(47) then fractalChanged=1
if keystate(44)=1 then clearBase()
if keystate(45)=1 then setBasetriangle()
if keystate(46)=1 then setBasebox()
if keystate(47)=1 then setBaseHex()
d3d_batch_set_line2d 100000
if fractalChanged=1
`now we need to populate motif with the right values.
fractalChanged=0
if whichFractal=1
preset1()
endif
if whichFractal=2
preset2()
endif
if whichFractal=3
preset3()
endif
if whichFractal=4
preset4()
endif
if whichFractal=5
RightAngleSweep(variable4)
endif
if whichFractal=6
AngleGeneralized(variable4,variable2,variable1)
endif
if whichFractal=7
kochGeneralized(variable4,variable2,variable3,variable1)
endif
if whichFractal=8
squiggleSweep(variable4,variable2,variable3,variable1)
endif
cls
text 0,0,"totalIterations: "+str$(totalIterations)
text 0,16,"FractalNum: "+str$(whichFractal)
text 0,32,"height: "+str$(variable1)
text 0,48,"offset: "+str$(variable2)
text 0,64,"width: "+str$(variable3)
text 0,80,"switch flags: "+bin$(variable4)
text 0,96,"preview detail: "+str$(previewrenderdepth)
drawSide(previewrenderdepth)
endif
if spacekey()=1 and switch=0 `render the big'un
switch=1
drawSide(previewrenderdepth)
fillFinalCurve(totalIterations,1.0/(graphs(2).ppu))
drawFract(2)
endif
d3d_batch_draw_line2d 1
loop
end
function drawSide(depth as integer)
fillFinalCurve(depth,1.0/(graphs(0).ppu))
drawFract(0)
fillFinalCurve(0,1.0/(graphs(1).ppu))
drawFract(1)
endfunction
function DrawFract(graph as integer)
temp as vec2
lastP as vec2
lastP=finalCurve(0)
for n=1 to array count(finalCurve())
temp=finalCurve(n)
drawLine(lastP,temp,rgb(200,200,200),graph)
`drawThickDot(temp.x,temp.y,rgb(255,0,0),2)
lastP=temp
next n
endfunction
function preset1() ` squiggle
undim motif()
dim motif(9) as vec2
for n=0 to array count(motif())
motif(n).invx=1
motif(n).invy=1
next n
`element 0 to 9
motif(0).x=0
motif(9).x=1
for n=1 to 4
motif(n*2).x=n/5.0
motif(n*2-1).x=n/5.0
next n
for n=0 to 4
v#=sin(n*90)*.2
motif(n*2).y=v#
motif(n*2+1).y=v#
next n
`--
`makes a "mySequence" that looks like:
` _
`_| |_ _
` |_|
`^^^coded so weirdly because I didn't want to have 20 lines of BLAH =some lame boring value
`remend
endfunction
function preset2() `koch curve
undim motif()
dim motif(4) as vec2
for n=0 to array count(motif())
motif(n).invx=1
motif(n).invy=1
next n
q as integer
q=0
motif(q).x=0
motif(q).y=0 : inc q
motif(q).x=.33333
motif(q).y=0 : inc q
motif(q).x=.5
motif(q).y=0.288675 : inc q
motif(q).x=.66666
motif(q).y=0 : inc q
motif(q).x=1
motif(q).y=0 : inc q
endfunction
function preset3() `right angle bracket
undim motif()
dim motif(3) as vec2
for n=0 to array count(motif())
motif(n).invx=1
motif(n).invy=1
next n
q as integer
q=0
motif(q).x=0
motif(q).y=0 : inc q
motif(q).x=.5
motif(q).y=.5 : inc q
motif(q).x=1
motif(q).y=0 : inc q
endfunction
function RightAngleSweep(a as integer) `the kind of sweep depends on a
undim motif()
dim motif(2) as vec2
q as integer
q=0
motif(q).x=0
motif(q).y=0 : inc q
motif(q).x=.5
motif(q).y=.5 : inc q
motif(q).x=1
motif(q).y=0 : inc q
for n=0 to array count(motif())-1
if getBit(a,n*2)=1
motif(n).invx=-1
else
motif(n).invx=1
endif
if getBit(a,n*2+1)=1
motif(n).invy=-1
else
motif(n).invy=1
endif
next n
endfunction
function AngleGeneralized(a as integer, offset as float,height as float)
undim motif()
dim motif(2) as vec2
q as integer
q=0
motif(q).x=0
motif(q).y=0 : inc q
motif(q).x=offset
motif(q).y=height : inc q
motif(q).x=1
motif(q).y=0 : inc q
for n=0 to array count(motif())-1
if getBit(a,n*2)=1
motif(n).invx=-1
else
motif(n).invx=1
endif
if getBit(a,n*2+1)=1
motif(n).invy=-1
else
motif(n).invy=1
endif
next n
endfunction
function kochGeneralized(a as integer, offset as float, width as float, height as float)
undim motif()
dim motif(4) as vec2
q as integer
q=0
motif(q).x=0
motif(q).y=0 : inc q
motif(q).x=offset-width/2.0
motif(q).y=0 : inc q
motif(q).x=offset
motif(q).y=height : inc q
motif(q).x=offset+width/2.0
motif(q).y=0 : inc q
motif(q).x=1
motif(q).y=0 : inc q
for n=0 to array count(motif())-1
if getBit(a,n*2)=1
motif(n).invx=-1
else
motif(n).invx=1
endif
if getBit(a,n*2+1)=1
motif(n).invy=-1
else
motif(n).invy=1
endif
next n
endfunction
function squiggleSweep(a as integer,offset as float, width as float, height as float)
undim motif()
dim motif(4) as vec2
q as integer
q=0
motif(q).x=0
motif(q).y=0 : inc q
motif(q).x=width
motif(q).y=height : inc q
motif(q).x=offset
motif(q).y=-height*.5 : inc q
motif(q).x=1.0-width
motif(q).y=height : inc q
motif(q).x=1
motif(q).y=0 : inc q
for n=0 to array count(motif())-1
if getBit(a,n*2)=1
motif(n).invx=-1
else
motif(n).invx=1
endif
if getBit(a,n*2+1)=1
motif(n).invy=-1
else
motif(n).invy=1
endif
next n
endfunction
function preset4()
undim motif()
dim motif(7) as vec2
q as integer
q=0
motif(q).x=0
motif(q).y=0
motif(q).invx=1
motif(q).invy=-1 : inc q
motif(q).x=1/6.0
motif(q).y=.288675135
motif(q).invx=-1
motif(q).invy=1 : inc q
motif(q).x=1/3.0
motif(q).y=.288675135*2
motif(q).invx=-1
motif(q).invy=1 : inc q
motif(q).x=2/3.0
motif(q).y=.288675135*2
motif(q).invx=-1
motif(q).invy=1 : inc q
motif(q).x=5/6.0
motif(q).y=.288675135
motif(q).invx=-1
motif(q).invy=-1 : inc q
motif(q).x=1/3.0
motif(q).y=0
motif(q).invx=1
motif(q).invy=-1 : inc q
motif(q).x=2/3.0
motif(q).y=0
motif(q).invx=-1
motif(q).invy=1 : inc q
motif(q).x=1
motif(q).y=0
motif(q).invx=1
motif(q).invy=1 : inc q
endfunction
function getBit(a as integer, n as integer)
retvalue as integer
retvalue=(a>>n) && 1
endfunction retvalue
function drawThickDot(x1 as float, y1 as float, color as dword,graph)
units_to_pix(x1,y1,graph)
x1=ret.x
y1=ret.y
for a=-3 to 3
for b=-3 to 3
dot x1+a,y1+b,color
next b
next a
endfunction
`what we're drawing is a *curve*. This curve is an *estimation* of the fractal. You draw
`lines from one point, to the next, to the next, to the next. As such, the last point
`of one transformed motif should be the first point of the next. That's why the first
`point is omitted in the code. It should be at (0,0) anyways.
function fillFinalCurve(its as integer,cutoff as float)
cutoff=cutoff*cutoff
temp as vec2
dim tempArray(-1) as vec2
empty array finalCurve()
for n=0 to array count(base())
array insert at bottom finalCurve()
finalCurve(n)=base(n)
next n
currentIterate as integer
for currentIterate=1 to its `every iteration, bump the information from tempArr to finalCurve, and repeat.
anyNewGeometry as integer
anyNewGeometry=0
`Okay, so for every line segment in finalCurve(), we replace it with the series of line segments
`in motif(), taking into account inverting each one. We then store these line segments in tempArray(),
`then clear finalcurve(), set finalCurve to tempArray, and unlink/clear tempArray.
a as vec2
b as vec2
for n=0 to array count(finalCurve())-1
a=finalCurve(n)
b=finalCurve(n+1)
invx as integer
invy as integer
invx=a.invx
invy=a.invy `inversions are determined by the first point.
a1#=a.x-b.x
b1#=a.y-b.y
if a1#*a1#+b1#*b1#>cutoff `usually cutoff is used to determine if the point is visible or not.
`basically, if the distance between a and b squared is less than cutoff, then we don't create
`new geometry, and instead just copy what we have over.
anyNewGeometry=1
for q=0 to array count(motif())-1
qq=q
if invx=-1
qq=array count(motif())-q
temp.x=1-motif(qq).x
else
temp.x=motif(qq).x
endif
temp.y=motif(qq).y*invy
array insert at bottom tempArray()
transformPointToSegment(temp,a,b)
if invx=-1 then qq=qq-1
ret.invx=motif(qq).invx*invx
ret.invy=motif(qq).invy*invy
tempArray(array count(tempArray()))=ret
next q
else `if the geometry isn't visible/is less than "cutoff"
array insert at bottom tempArray()
tempArray(array count(tempArray()))=a
endif
if keystate(23)=1 then exitfunction
next n
array insert at bottom tempArray()
tempArray(array count(tempArray()))=base(array count(base()))
`^^the last point of the curve is the last point of the base
empty array finalCurve()
for n=0 to array count(tempArray())
array insert at bottom finalCurve()
finalCurve(n)=tempArray(n)
next n
empty array tempArray()
if anyNewGeometry=0 then goto _skip
next
_skip:
undim temparray()
endfunction
function setBaseHex()
undim base()
dim base(6) as vec2
base(0).x=0
base(0).y=0
base(1).x=.25
base(1).y=0.433012702
base(2).x=.75
base(2).y=0.433012702
base(3).x=1
base(3).y=0
base(4).x=.75
base(4).y=-0.433012702
base(5).x=.25
base(5).y=-0.433012702
base(6).x=0
base(6).y=0
for n=0 to 6
base(n).invx=1
base(n).invy=1
next n
endfunction
function setBaseBox()
empty array base()
array insert at bottom base()
array insert at bottom base()
array insert at bottom base()
array insert at bottom base()
array insert at bottom base()
base(0).x=-.5
base(0).y=-.5
base(4)=base(0)
base(1).x=-.5
base(1).y=.5
base(2).x=.5
base(2).y=.5
base(3).x=.5
base(3).y=-.5
for n=0 to 4
base(n).invx=1
base(n).invy=1
base(n).x=base(n).x/2.0+.5
base(n).y=base(n).y/2.0
next n
endfunction
function setBaseTriangle()
empty array base()
array insert at bottom base()
array insert at bottom base()
array insert at bottom base()
array insert at bottom base()
base(0).x=0
base(0).y=-0.288675134
base(3)=base(0)
base(1).x=.5
base(1).y=.5656893
base(2).x=1
base(2).y=-0.288675134
for n=0 to 3
base(n).invx=1
base(n).invy=1
next n
`approximately an equilateral triangle centered at (.5,0), with side length 1.
endfunction
function drawThickBorder(x1,y1,x2,y2,thickness,color as dword)
ink color,color
a=floor(-thickness/2.0)
b=floor(thickness/2.0)
for n=a to b
line x1,y1+n,x2,y1+n
line x1,y2+n,x2,y2+n
line x1+n,y1,x1+n,y2
line x2+n,y1,x2+n,y2
next n
endfunction
function removeBasePoint(n)
if n>0 and n<array count(base())
array delete element base(),n
endif
endfunction
function addBaseSegment(a,P as vec2) `add a point between index a and a+1.
array insert at element base(),a+1
base(a+1)=P
base(a+1).invx=1
base(a+1).invy=1
endfunction
function clearbase()`reset base to the unit segment
empty array base()
array insert at bottom base()
array insert at bottom base()
base(0).x=0
base(0).y=0
base(1).x=1
base(1).y=0
for n=0 to 1
base(0).invx=1
base(0).invy=1
next n
endfunction
function removeMotifPoint(n)
if n>0 and n<array count(motif())
array delete element motif(),n
endif
endfunction
function addMotifSegment(a,P as vec2) `add a point between index a and a+1.
array insert at element motif(),a+1
motif(a+1)=P
motif(a+1).invx=1
motif(a+1).invy=1
endfunction
function clearmotif()`reset mySequence to the unit segment
empty array motif()
array insert at bottom motif()
array insert at bottom motif()
motif(0).x=0
motif(0).y=0
motif(1).x=1
motif(1).y=0
for n=0 to 1
motif(n).invx=1
motif(n).invy=1
next n
endfunction
function drawLine(A as vec2, B as vec2, color as dword,graph)
units_to_pix(A.x,A.y,graph)
x1=ret.x
y1=ret.y
units_to_pix(B.x,B.y,graph)
x2=ret.x
y2=ret.y
ink color,color
d3d_batch_add_line2d x1,y1,x2,y2
endfunction
function pix_to_units(x,y,graph)
ret.x=(x-graphs(graph).c.x)*1.0/graphs(graph).ppu
ret.y=-(y-graphs(graph).c.y)*1.0/graphs(graph).ppu
endfunction
function units_to_pix(x as float, y as float,graph)
ret.x=x*graphs(graph).ppu+graphs(graph).c.x
ret.y=-y*graphs(graph).ppu+graphs(graph).c.y
endfunction
function transformPointToSegment(pt as vec2, a as vec2, b as vec2)
`where R is a rotation matrix giving a rotation 90 degrees CCW, and A, B, and P are column vectors
`P'=(B-A)*P.x+R*(B-A)*p.y+A
`this transformation scales (P-B) and (P-A) by a factor of ||B-A||, and rotates P by atan2(B-A)
`easier expressed in matrix form by:
` [P.x -P.y]
`P=[P.y P.x]*(B-A) + A
ret.x=pt.x*(b.x-a.x)-pt.y*(b.y-a.y)+a.x
ret.y=pt.y*(b.x-a.x)+pt.x*(b.y-a.y)+a.y
endfunction
`remend
Version without:
`BaseMotifFractal
`BaseMotifFractal.dba
`======================
`BaseMotifFractal
`BaseMotifFractal.dba
`======================
type vec2
x as float
y as float
invx as integer
invy as integer
endtype
type graphScreen
tl as vec2 `top left corner
br as vec2 `bottom right corner
c as vec2 `location of (0,0) on the screen, or where it should be if its not visible. (in pixels)
PPU as float `Pixels per graph Unit.
endtype
global cmc `Current frame Mouse Click
global lmc `Last frame Mouse Click
global mh `Mouse Held
global ZeroVector as vec2
zeroVector.x=0
zeroVector.y=0
dim graphs(2) as graphScreen
graphs(0).tl=ZeroVector
graphs(0).br.x=screen width()/3-10
graphs(0).br.y=screen height()/2-5
`visible: from 1.1 to -1.1 on the y axis
graphs(0).ppu=screen height()/4.4
`(.5,0) is on the center of the graph area
graphs(0).c.x=(graphs(0).tl.x+graphs(0).br.x-graphs(0).ppu)/2
graphs(0).c.y=(graphs(0).tl.y+graphs(0).br.y)/2
graphs(1).tl.x=0
graphs(1).tl.y=screen height()/2+5
graphs(1).br.x=screen width()/3-10
graphs(1).br.y=screen height()
`visible: from 1.1 to -1.1 on the y axis
graphs(1).ppu=screen height()/4.4
`(.5,0) is on the center of the graph area
graphs(1).c.x=(graphs(1).tl.x+graphs(1).br.x-graphs(1).ppu)/2
graphs(1).c.y=(graphs(1).tl.y+graphs(1).br.y)/2
graphs(2).tl.x=0
graphs(2).tl.y=0
graphs(2).br.x=screen width()
graphs(2).br.y=screen height()
`visible: from 1.1 to -1.1 on the y axis
graphs(2).ppu=400
`(.5,0) is on the center of the graph area
graphs(2).c.x=(graphs(2).tl.x+graphs(2).br.x-graphs(2).ppu)/2
graphs(2).c.y=(graphs(2).tl.y+graphs(2).br.y)/2
global ret as vec2
dim finalCurve(-1) as vec2
dim base(1) as vec2
`--
clearBase()
temp as vec2
lastP as vec2
switch as integer
switch=0
totalIterations as integer
variable1 as float
variable2 as float
variable3 as float
variable4 as integer
whichfractal as integer
whichfractal=1
fractalchanged as integer
fractalchanged=1
previewrenderdepth as integer
variable1=.5
variable2=.5
variable3=.5
variable4=0
totalIterations=5
previewRenderDepth=2
`Q/A changes totalIterations`
`W/S changes variable4 - determines switching by each bit`
`spacebar renders up to depth totalIterations
`E/D changes variable1 - determines height`
`R/F changes variable2 - determines offset`
`T/G changes variable3 - determines width`
`not all of these values are used all of the time.
`z changes the base to a line`
`x changes the base to a triangle``
`c changes the base to a square`
`d changes the base to a regular hexagon`
`up arrow/down arrow switches between fractals`
`p randomizes all variables1-4`
`o zeros all variables 1-4`
`i exits the loop, in case you try to render too much!`
`y/h increases preview render depth.`
`preview render depth is so that you can see how the fractal will look while you're messing with
`the other variables. Normally this should be 2 or 3.
do
if keystate(16)=0 and keystate(17)=0 and keystate(30)=0 and keystate(31)=0 and spacekey()=0
if upkey()=0 and downkey()=0 and keystate(21)=0 and keystate(35)=0
switch=0 `lol, lots of ifs...
endif
endif
if switch=0
if keystate(16) =1
switch=1
fractalchanged=1
inc totalIterations
endif
if keystate(30)=1
switch=1
if totalIterations>1
fractalchanged=1
dec totalIterations
endif
endif
if keystate(17)=1
switch=1
fractalchanged=1
inc variable4
endif
if keystate(31)=1
switch=1
fractalchanged=1
dec variable4
endif
if keystate(21)=1
switch=1
fractalchanged=1
inc previewrenderdepth
endif
if keystate(35)=1
switch=1
fractalchanged=1
if previewrenderdepth>1 then dec previewrenderdepth
endif
if upkey()=1 or downkey()=1
switch=1
if upkey()=1
inc whichFractal
if whichFractal>8 then whichFractal=1
endif
if downkey()=1
dec whichFractal
if whichFractal<1 then whichFractal=8
endif
fractalchanged=1
if whichFractal=1
preset1()
endif
if whichFractal=2
preset2()
endif
if whichFractal=3
preset3()
endif
if whichFractal=4
preset4()
endif
if whichFractal=5
variable1=0
variable2=0
variable3=0
variable4=0
RightAngleSweep(variable4)
endif
if whichFractal=6
variable1=.5
variable2=.5
variable3=0
variable4=0
AngleGeneralized(variable4,variable2,variable1)
endif
if whichFractal=7
variable1=.4
variable2=.3
variable3=.02
variable4=0
kochGeneralized(variable4,variable2,variable3,variable1)
endif
if whichFractal=8
variable1=.2
variable2=.5
variable3=.2
variable4=0
squiggleSweep(variable4,variable2,variable3,variable1)
endif
endif
endif
tempscale#=.1/screen fps()
if tempscale#>.01 then tempscale#=.01
if keystate(18)=1
fractalchanged=1
inc variable1,tempscale#
endif
if keystate(19)=1
fractalchanged=1
inc variable2,tempscale#
endif
if keystate(20)=1
fractalchanged=1
inc variable3,tempscale#
endif
if keystate(32)=1
fractalchanged=1
dec variable1,tempscale#
endif
if keystate(33)=1
fractalchanged=1
dec variable2,tempscale#
endif
if keystate(34)=1
fractalchanged=1
dec variable3,tempscale#
endif
if keystate(25)=1
fractalchanged=1
variable1=rnd(10000)/10000.0
variable2=rnd(10000)/10000.0
variable3=rnd(10000)/10000.0
variable4=rnd(65536)
if whichFractal=7
variable1=variable1/3.0
variable2=variable2*.45
variable3=variable3*(1-2*variable2)+variable2
endif
if whichFractal=7
variable1=variable1/3.0
variable2=variable2*.45
variable3=variable3*(1-2*variable2)
endif
endif
if keystate(24)=1
fractalchanged=1
variable1=0
variable2=0
variable3=0
variable4=0
endif
if keystate(44) or keystate(45) or keystate(46) or keystate(47) then fractalChanged=1
if keystate(44)=1 then clearBase()
if keystate(45)=1 then setBasetriangle()
if keystate(46)=1 then setBasebox()
if keystate(47)=1 then setBaseHex()
if fractalChanged=1
`now we need to populate motif with the right values.
fractalChanged=0
if whichFractal=1
preset1()
endif
if whichFractal=2
preset2()
endif
if whichFractal=3
preset3()
endif
if whichFractal=4
preset4()
endif
if whichFractal=5
RightAngleSweep(variable4)
endif
if whichFractal=6
AngleGeneralized(variable4,variable2,variable1)
endif
if whichFractal=7
kochGeneralized(variable4,variable2,variable3,variable1)
endif
if whichFractal=8
squiggleSweep(variable4,variable2,variable3,variable1)
endif
cls
text 0,0,"totalIterations: "+str$(totalIterations)
text 0,16,"FractalNum: "+str$(whichFractal)
text 0,32,"height: "+str$(variable1)
text 0,48,"offset: "+str$(variable2)
text 0,64,"width: "+str$(variable3)
text 0,80,"switch flags: "+bin$(variable4)
text 0,96,"preview detail: "+str$(previewrenderdepth)
drawSide(previewrenderdepth)
endif
if spacekey()=1 and switch=0 `render the big'un
switch=1
drawSide(previewrenderdepth)
fillFinalCurve(totalIterations,1.0/(graphs(2).ppu))
drawFract(2)
endif
loop
end
function drawSide(depth as integer)
fillFinalCurve(depth,1.0/(graphs(0).ppu))
drawFract(0)
fillFinalCurve(0,1.0/(graphs(1).ppu))
drawFract(1)
endfunction
function DrawFract(graph as integer)
temp as vec2
lastP as vec2
lastP=finalCurve(0)
for n=1 to array count(finalCurve())
temp=finalCurve(n)
drawLine(lastP,temp,rgb(200,200,200),graph)
`drawThickDot(temp.x,temp.y,rgb(255,0,0),2)
lastP=temp
next n
endfunction
function preset1() ` squiggle
undim motif()
dim motif(9) as vec2
for n=0 to array count(motif())
motif(n).invx=1
motif(n).invy=1
next n
`element 0 to 9
motif(0).x=0
motif(9).x=1
for n=1 to 4
motif(n*2).x=n/5.0
motif(n*2-1).x=n/5.0
next n
for n=0 to 4
v#=sin(n*90)*.2
motif(n*2).y=v#
motif(n*2+1).y=v#
next n
`--
`makes a "mySequence" that looks like:
` _
`_| |_ _
` |_|
`^^^coded so weirdly because I didn't want to have 20 lines of BLAH =some lame boring value
`remend
endfunction
function preset2() `koch curve
undim motif()
dim motif(4) as vec2
for n=0 to array count(motif())
motif(n).invx=1
motif(n).invy=1
next n
q as integer
q=0
motif(q).x=0
motif(q).y=0 : inc q
motif(q).x=.33333
motif(q).y=0 : inc q
motif(q).x=.5
motif(q).y=0.288675 : inc q
motif(q).x=.66666
motif(q).y=0 : inc q
motif(q).x=1
motif(q).y=0 : inc q
endfunction
function preset3() `right angle bracket
undim motif()
dim motif(3) as vec2
for n=0 to array count(motif())
motif(n).invx=1
motif(n).invy=1
next n
q as integer
q=0
motif(q).x=0
motif(q).y=0 : inc q
motif(q).x=.5
motif(q).y=.5 : inc q
motif(q).x=1
motif(q).y=0 : inc q
endfunction
function RightAngleSweep(a as integer) `the kind of sweep depends on a
undim motif()
dim motif(2) as vec2
q as integer
q=0
motif(q).x=0
motif(q).y=0 : inc q
motif(q).x=.5
motif(q).y=.5 : inc q
motif(q).x=1
motif(q).y=0 : inc q
for n=0 to array count(motif())-1
if getBit(a,n*2)=1
motif(n).invx=-1
else
motif(n).invx=1
endif
if getBit(a,n*2+1)=1
motif(n).invy=-1
else
motif(n).invy=1
endif
next n
endfunction
function AngleGeneralized(a as integer, offset as float,height as float)
undim motif()
dim motif(2) as vec2
q as integer
q=0
motif(q).x=0
motif(q).y=0 : inc q
motif(q).x=offset
motif(q).y=height : inc q
motif(q).x=1
motif(q).y=0 : inc q
for n=0 to array count(motif())-1
if getBit(a,n*2)=1
motif(n).invx=-1
else
motif(n).invx=1
endif
if getBit(a,n*2+1)=1
motif(n).invy=-1
else
motif(n).invy=1
endif
next n
endfunction
function kochGeneralized(a as integer, offset as float, width as float, height as float)
undim motif()
dim motif(4) as vec2
q as integer
q=0
motif(q).x=0
motif(q).y=0 : inc q
motif(q).x=offset-width/2.0
motif(q).y=0 : inc q
motif(q).x=offset
motif(q).y=height : inc q
motif(q).x=offset+width/2.0
motif(q).y=0 : inc q
motif(q).x=1
motif(q).y=0 : inc q
for n=0 to array count(motif())-1
if getBit(a,n*2)=1
motif(n).invx=-1
else
motif(n).invx=1
endif
if getBit(a,n*2+1)=1
motif(n).invy=-1
else
motif(n).invy=1
endif
next n
endfunction
function squiggleSweep(a as integer,offset as float, width as float, height as float)
undim motif()
dim motif(4) as vec2
q as integer
q=0
motif(q).x=0
motif(q).y=0 : inc q
motif(q).x=width
motif(q).y=height : inc q
motif(q).x=offset
motif(q).y=-height*.5 : inc q
motif(q).x=1.0-width
motif(q).y=height : inc q
motif(q).x=1
motif(q).y=0 : inc q
for n=0 to array count(motif())-1
if getBit(a,n*2)=1
motif(n).invx=-1
else
motif(n).invx=1
endif
if getBit(a,n*2+1)=1
motif(n).invy=-1
else
motif(n).invy=1
endif
next n
endfunction
function preset4()
undim motif()
dim motif(7) as vec2
q as integer
q=0
motif(q).x=0
motif(q).y=0
motif(q).invx=1
motif(q).invy=-1 : inc q
motif(q).x=1/6.0
motif(q).y=.288675135
motif(q).invx=-1
motif(q).invy=1 : inc q
motif(q).x=1/3.0
motif(q).y=.288675135*2
motif(q).invx=-1
motif(q).invy=1 : inc q
motif(q).x=2/3.0
motif(q).y=.288675135*2
motif(q).invx=-1
motif(q).invy=1 : inc q
motif(q).x=5/6.0
motif(q).y=.288675135
motif(q).invx=-1
motif(q).invy=-1 : inc q
motif(q).x=1/3.0
motif(q).y=0
motif(q).invx=1
motif(q).invy=-1 : inc q
motif(q).x=2/3.0
motif(q).y=0
motif(q).invx=-1
motif(q).invy=1 : inc q
motif(q).x=1
motif(q).y=0
motif(q).invx=1
motif(q).invy=1 : inc q
endfunction
function getBit(a as integer, n as integer)
retvalue as integer
retvalue=(a>>n) && 1
endfunction retvalue
function drawThickDot(x1 as float, y1 as float, color as dword,graph)
units_to_pix(x1,y1,graph)
x1=ret.x
y1=ret.y
for a=-3 to 3
for b=-3 to 3
dot x1+a,y1+b,color
next b
next a
endfunction
`what we're drawing is a *curve*. This curve is an *estimation* of the fractal. You draw
`lines from one point, to the next, to the next, to the next. As such, the last point
`of one transformed motif should be the first point of the next. That's why the first
`point is omitted in the code. It should be at (0,0) anyways.
function fillFinalCurve(its as integer,cutoff as float)
cutoff=cutoff*cutoff
temp as vec2
dim tempArray(-1) as vec2
empty array finalCurve()
for n=0 to array count(base())
array insert at bottom finalCurve()
finalCurve(n)=base(n)
next n
currentIterate as integer
for currentIterate=1 to its `every iteration, bump the information from tempArr to finalCurve, and repeat.
anyNewGeometry as integer
anyNewGeometry=0
`Okay, so for every line segment in finalCurve(), we replace it with the series of line segments
`in motif(), taking into account inverting each one. We then store these line segments in tempArray(),
`then clear finalcurve(), set finalCurve to tempArray, and unlink/clear tempArray.
a as vec2
b as vec2
for n=0 to array count(finalCurve())-1
a=finalCurve(n)
b=finalCurve(n+1)
invx as integer
invy as integer
invx=a.invx
invy=a.invy `inversions are determined by the first point.
a1#=a.x-b.x
b1#=a.y-b.y
if a1#*a1#+b1#*b1#>cutoff `usually cutoff is used to determine if the point is visible or not.
`basically, if the distance between a and b squared is less than cutoff, then we don't create
`new geometry, and instead just copy what we have over.
anyNewGeometry=1
for q=0 to array count(motif())-1
qq=q
if invx=-1
qq=array count(motif())-q
temp.x=1-motif(qq).x
else
temp.x=motif(qq).x
endif
temp.y=motif(qq).y*invy
array insert at bottom tempArray()
transformPointToSegment(temp,a,b)
if invx=-1 then qq=qq-1
ret.invx=motif(qq).invx*invx
ret.invy=motif(qq).invy*invy
tempArray(array count(tempArray()))=ret
next q
else `if the geometry isn't visible/is less than "cutoff"
array insert at bottom tempArray()
tempArray(array count(tempArray()))=a
endif
if keystate(23)=1 then exitfunction
next n
array insert at bottom tempArray()
tempArray(array count(tempArray()))=base(array count(base()))
`^^the last point of the curve is the last point of the base
empty array finalCurve()
for n=0 to array count(tempArray())
array insert at bottom finalCurve()
finalCurve(n)=tempArray(n)
next n
empty array tempArray()
if anyNewGeometry=0 then goto _skip
next
_skip:
undim temparray()
endfunction
function setBaseHex()
undim base()
dim base(6) as vec2
base(0).x=0
base(0).y=0
base(1).x=.25
base(1).y=0.433012702
base(2).x=.75
base(2).y=0.433012702
base(3).x=1
base(3).y=0
base(4).x=.75
base(4).y=-0.433012702
base(5).x=.25
base(5).y=-0.433012702
base(6).x=0
base(6).y=0
for n=0 to 6
base(n).invx=1
base(n).invy=1
next n
endfunction
function setBaseBox()
empty array base()
array insert at bottom base()
array insert at bottom base()
array insert at bottom base()
array insert at bottom base()
array insert at bottom base()
base(0).x=-.5
base(0).y=-.5
base(4)=base(0)
base(1).x=-.5
base(1).y=.5
base(2).x=.5
base(2).y=.5
base(3).x=.5
base(3).y=-.5
for n=0 to 4
base(n).invx=1
base(n).invy=1
base(n).x=base(n).x/2.0+.5
base(n).y=base(n).y/2.0
next n
endfunction
function setBaseTriangle()
empty array base()
array insert at bottom base()
array insert at bottom base()
array insert at bottom base()
array insert at bottom base()
base(0).x=0
base(0).y=-0.288675134
base(3)=base(0)
base(1).x=.5
base(1).y=.5656893
base(2).x=1
base(2).y=-0.288675134
for n=0 to 3
base(n).invx=1
base(n).invy=1
next n
`approximately an equilateral triangle centered at (.5,0), with side length 1.
endfunction
function drawThickBorder(x1,y1,x2,y2,thickness,color as dword)
ink color,color
a=floor(-thickness/2.0)
b=floor(thickness/2.0)
for n=a to b
line x1,y1+n,x2,y1+n
line x1,y2+n,x2,y2+n
line x1+n,y1,x1+n,y2
line x2+n,y1,x2+n,y2
next n
endfunction
function removeBasePoint(n)
if n>0 and n<array count(base())
array delete element base(),n
endif
endfunction
function addBaseSegment(a,P as vec2) `add a point between index a and a+1.
array insert at element base(),a+1
base(a+1)=P
base(a+1).invx=1
base(a+1).invy=1
endfunction
function clearbase()`reset base to the unit segment
empty array base()
array insert at bottom base()
array insert at bottom base()
base(0).x=0
base(0).y=0
base(1).x=1
base(1).y=0
for n=0 to 1
base(0).invx=1
base(0).invy=1
next n
endfunction
function removeMotifPoint(n)
if n>0 and n<array count(motif())
array delete element motif(),n
endif
endfunction
function addMotifSegment(a,P as vec2) `add a point between index a and a+1.
array insert at element motif(),a+1
motif(a+1)=P
motif(a+1).invx=1
motif(a+1).invy=1
endfunction
function clearmotif()`reset mySequence to the unit segment
empty array motif()
array insert at bottom motif()
array insert at bottom motif()
motif(0).x=0
motif(0).y=0
motif(1).x=1
motif(1).y=0
for n=0 to 1
motif(n).invx=1
motif(n).invy=1
next n
endfunction
function drawLine(A as vec2, B as vec2, color as dword,graph)
units_to_pix(A.x,A.y,graph)
x1=ret.x
y1=ret.y
units_to_pix(B.x,B.y,graph)
x2=ret.x
y2=ret.y
ink color,color
line x1,y1,x2,y2
endfunction
function pix_to_units(x,y,graph)
ret.x=(x-graphs(graph).c.x)*1.0/graphs(graph).ppu
ret.y=-(y-graphs(graph).c.y)*1.0/graphs(graph).ppu
endfunction
function units_to_pix(x as float, y as float,graph)
ret.x=x*graphs(graph).ppu+graphs(graph).c.x
ret.y=-y*graphs(graph).ppu+graphs(graph).c.y
endfunction
function transformPointToSegment(pt as vec2, a as vec2, b as vec2)
`where R is a rotation matrix giving a rotation 90 degrees CCW, and A, B, and P are column vectors
`P'=(B-A)*P.x+R*(B-A)*p.y+A
`this transformation scales (P-B) and (P-A) by a factor of ||B-A||, and rotates P by atan2(B-A)
`easier expressed in matrix form by:
` [P.x -P.y]
`P=[P.y P.x]*(B-A) + A
ret.x=pt.x*(b.x-a.x)-pt.y*(b.y-a.y)+a.x
ret.y=pt.y*(b.x-a.x)+pt.x*(b.y-a.y)+a.y
endfunction
`remend
[edit2]
If you want to customize more, mess with the array "Motif". This array handles the generating rules. The basic idea is that you replace every line segment with the set of line segments that is motif, every iteration. So, just by adding values and changing the X and Y coordinate of each array index of "motif", you can get any 2d base/motif array.
Then there are sweeps.
What if you flip each line segment horizontally or vertically? the set of line segments in "motif" would be put in, but backwards or upside down! as it turns out that lets us define a whoole 'nother set of fractals! So, if you change the invx and invy parameters of a motif, you get different sweeps.
*always* set invx and invy! to -1 or 1 only plz!
Also, parts of the program rely upon the fact that the first x,y, value of motif should be 0,0, and the last should be 1,0. Make sure this is the case!
anyways, there are some awesome fractals in here, and i hope to make some .gif animations soon!