
The grid you see is a direction vector grid. The color is determined by the sine of the log of its magnitude (basically, color is logarithmic, but cyclical). The white line is of a particle following this velocity field (NOT taking magnitude into account, speed is always 1).
Other animations (links, because they're animated gifs, and are like 1mb each)
http://www.neurofuzzydev.com/img/fract/juliaSpiral.gif
http://www.neurofuzzydev.com/img/fract/mBrotVert.gif
http://www.neurofuzzydev.com/img/fract/myBrotHoriz.gif
http://www.neurofuzzydev.com/img/fract/juliaspiral2.gif
Basically, blah blah blah fractal. The method is explained in the first paragraph of this page:
http://freymanart.com/VectorFields/index.htm
You need matrix utils for the log() function, maybe others, and d3dfunc for the line drawing.
type vec2
x as float
y as float
endtype
global ret as vec2
global gridres as integer
global size as integer
global arrowlength as float
global margin as integer
margin=25
gridres=100
size=screen height()
if screen width()<screen height() then size=screen width()
size=size-margin*2
arrowLength=size*1.0/gridres-2
dim vf(gridres,gridres) as vec2 `VelocityField direction
dim vfMag(gridres,gridres) as float `magnitude
global FLOAT_MAXVAL as float
global FLOAT_MINVAL as float
global FLOAT_PINF as float
global FLOAT_NINF as float
x as dword
make memblock 1,32
x=%01111111100000000000000000000000`= infinity
write memblock dword 1,0,x
FLOAT_PINF=memblock float(1,0)
x=%11111111100000000000000000000000`= +infinity
write memblock dword 1,0,x
FLOAT_NINF=memblock float(1,0)
x=%01111111011111111111111111111111`=maxval?
write memblock dword 1,0,x
FLOAT_MAXVAL=memblock float(1,0)
x=%11111111011111111111111111111111`=minval?
write memblock dword 1,0,x
FLOAT_MINVAL=memblock float(1,0)
`if image exist(2) then delete image 2
`make image from memblock 2,2
`paste image 2,0,0
n=0
do
if spacekey()=1 then inc n,1
n=wrapvalue(n)
ang#=n*2
rad#=cos(n)
calc_juliavelfield(rad#*cos(ang#),rad#*sin(ang#),0,0,4,5)
cls
d3d_batch_set_line2d 100000
text 0,0,str$(modif)
pixels_to_grid(mousex(),mousey())
thisx#=ret.x
thisy#=ret.y
screenx#=mousex()
screeny#=mousey()
newx#=0.0
newy#=0.0
for num=0 to 1200
getVelAtPoint(thisx#,thisy#)
newx#=thisx#+ret.x*.1
newy#=thisy#+ret.y*.1
gridToPixels(newx#,newy#)
d3d_batch_add_line2d screenx#,screeny#,ret.x,ret.y,rgb(255,255,255)
screenx#=ret.x
screeny#=ret.y
thisx#=newx#
thisy#=newy#
next num
pixels_to_grid(mousex(),mousey())
getVelAtPoint(ret.x,ret.y)
drawArrow(mousex(),mousey(),mousex()+ret.x*30,mousey()+ret.y*30,5, rgb(0,255,0))
drawVecField()
d3d_batch_draw_line2d 1
sync
sleep 10
loop
end
function mdrawVecField()
mCLS(2,rgb(0,0,0))
for x=0 to gridres
for y=0 to gridres
ret.x=x*1.0/gridRes*4-2
ret.y=y*1.0/gridRes*4-2
retToScreenPixels()
x1#=ret.x
y1#=ret.y
x2#=x1#+vf(x,y).x*arrowlength
y2#=y1#+vf(x,y).y*arrowlength
mdrawArrow(2,x1#,y1#,x2#,y2#,3,rgb(255,255,255))
next y
next x
endfunction
function drawVecField()
for x=0 to gridres
for y=0 to gridres
ret.x=x*1.0/gridRes*4-2
ret.y=y*1.0/gridRes*4-2
retToScreenPixels()
x1#=ret.x
y1#=ret.y
x2#=x1#+vf(x,y).x*arrowlength
y2#=y1#+vf(x,y).y*arrowlength
nnn#=log(vfMag(x,y))*20
color as dword
color=rgb((cos(nnn#)+1)*100+40,(cos(nnn#*2)+1)*100+40,(sin(nnn#)+1)*100+40)
drawArrow(x1#,y1#,x2#,y2#,3, color)
next y
next x
endfunction
function gridToPixels(x as float, y as float)
`ret.x=(x-margin)*1.0/size*gridres
`ret.y=(y-margin)*1.0/size*gridres
ret.x=x*size*1.0/gridres+margin
ret.y=y*size*1.0/gridres+margin
endfunction
function getVelAtPoint(x1 as float, y1 as float)
x2 as integer
y2 as integer
x2=int(x1)
y2=int(y1)
dim tmp1(3) as vec2
dim tmp2(3) as vec2
for x=-1 to 2
indx=x2+x
if indx<0 then indx=0
if indx>gridres then indx=gridres
for y=-1 to 2
indy=y2+y
if indy<0 then indy=0
if indy>gridres then indy=gridres
tmp1(y+1)=vf(indx,indy)
next y
tmp2(x+1).x=cubeterpolate(y1 mod 1.0,tmp1(0).x,tmp1(1).x,tmp1(2).x,tmp1(3).x)
tmp2(x+1).y=cubeterpolate(y1 mod 1.0,tmp1(0).y,tmp1(1).y,tmp1(2).y,tmp1(3).y)
next x
ret.x=cubeterpolate(x1 mod 1.0,tmp2(0).x,tmp2(1).x,tmp2(2).x,tmp2(3).x)
ret.y=cubeterpolate(x1 mod 1.0,tmp2(0).y,tmp2(1).y,tmp2(2).y,tmp2(3).y)
normalizeret()
endfunction
function cubeterpolate(t#,y0#,y1#,y2#,y3#)
t2# = t#*t#
a0# = y3# - y2# - y0# + y1#
a1# = y0# - y1# - a0#
a2# = y2# - y0#
a3# = y1#
r#=a0#*t#*t2#+a1#*t2#+a2#*t#+a3#
endfunction r#
function retToScreenPixels()
ret.x=(ret.x+2)*size/4.0+margin
ret.y=(ret.y+2)*size/4.0+margin
endfunction
function pixels_to_grid(x,y)
ret.x=(x-margin)*1.0/size*gridres
ret.y=(y-margin)*1.0/size*gridres
endfunction
function normalizeRet()
n#=1.0/sqrt(ret.x*ret.x+ret.y*ret.y)
ret.x=ret.x*n#
ret.y=ret.y*n#
endfunction n#
function Calc_brotvelfield(modfx as float, modfy as float, itstrt, itend)
for x=0 to gridRes
for y=0 to gridRes
x2#=x*1.0/gridRes*4-2
y2#=y*1.0/gridRes*4-2
mandelbrotModVec(x2#,y2#,modfx,modfy,itstrt,itend)
vfMag(x,y)=normalizeRet()
if float_a_number(ret.x)=1 then ret.x=0.0
if float_a_number(ret.y)=1 then ret.y=0.0
vf(x,y)=ret
next
next
endfunction
function Calc_juliavelfield(cx as float, cy as float,modfx as float, modfy as float, itstrt, itend)
for x=0 to gridRes
for y=0 to gridRes
x2#=x*1.0/gridRes*4-2
y2#=y*1.0/gridRes*4-2
juliaModVec(x2#,y2#,cx,cy,modfx,modfy,itstrt,itend)
vfMag(x,y)=normalizeRet()
if float_a_number(ret.x)=1 then ret.x=0.0
if float_a_number(ret.y)=1 then ret.y=0.0
vf(x,y)=ret
next
next
endfunction
function mandelbrotModVec(x as float, y as float, modfx as float, modfy as float, itstrt, itend)
`modified mandelbrot set: Z |-> Z^2+(x+y*i) + modification
`first, go to itstart iterations, and get that vector, A.
`Then go to itend iterations, call that vector B, and return B-A
vecA as vec2
a as float
b as float
a=x
b=y
`(0^2)+x+y*i+m
`((x+m)+y*i)^2+(x+m)+y*i=(x+m)^2-y^2+2(x+m)y*i + (x+m)+y*i
for n=1 to itstrt
temp#=a*a-b*b+x+modfx
b=2*a*b+y+modfy
a=temp#
next n
vecA.x=a
vecA.y=b
for n=itstrt+1 to itend
temp#=a*a-b*b+x+modfx
b=2*a*b+y+modfy
a=temp#
next n
ret.x=a-vecA.x
ret.y=b-vecA.y
endfunction
function juliaModVec(x as float, y as float,cx as float, cy as float modfx as float, modfy as float, itstrt, itend)
`modified julia set: Z |-> Z^2+(x+y*i) + modification
`first, go to itstart iterations, and get that vector, A.
`Then go to itend iterations, call that vector B, and return B-A
vecA as vec2
a as float
b as float
a=x
b=y
`(0^2)+x+y*i+m
`((x+m)+y*i)^2+(x+m)+y*i=(x+m)^2-y^2+2(x+m)y*i + (x+m)+y*i
for n=1 to itstrt
temp#=a*a-b*b+cx+modfx
b=2*a*b+cy+modfy
a=temp#
next n
vecA.x=a
vecA.y=b
for n=itstrt+1 to itend
temp#=a*a-b*b+cx+modfx
b=2*a*b+cy+modfy
a=temp#
next n
ret.x=a-vecA.x
ret.y=b-vecA.y
endfunction
function drawArrow(fromx as float,fromy as float,tox as float,toy as float,size, color as dword)
d3d_batch_add_line2d fromx,fromy,tox,toy,color
//type 1, box
`box tox-size/2,toy-size/2,tox+size/2,toy+size/2
//type 2, arrow
`x#=fromx-tox
`y#=fromy-toy
`d#=sqrt(x#*x#+y#*y#)
`x2#=x#*1.0/d#
`y2#=y#*1.0/d# `<x1#,x2#> is just the direction from <tox,toy> to <fromx,fromy>
`d3d_batch_add_line2d tox,toy,tox+size*0.707106781*(x2#-y2#),toy+size*0.707106781*(x2#+y2#) `trig? I don't need no stinkin trig!
`d3d_batch_add_line2d tox,toy,tox+size*0.707106781*(y2#+x2#),toy+size*0.707106781*(y2#-x2#)
//type 3, circle
`circle tox,toy,size/2
endfunction
function FLOAT_a_number(n as float)`returns a 1 if n is not a number. If it's a number, or positive/neg infinity it returns 0.
write memblock float 1,0,n
x as dword
x=(memblock dword(1,0)<<1)>>24
if x=255 `float is #INF or #NAN
exitfunction 1
endif
endfunction 0
function mdrawArrow(mem as integer,fromx as float,fromy as float,tox as float,toy as float,size as float,color as dword)
mLine(mem,fromx,fromy,tox,toy,color)
//type 1, box
`box tox-size/2,toy-size/2,tox+size/2,toy+size/2
//type 2, arrow
x#=fromx-tox
y#=fromy-toy
d#=sqrt(x#*x#+y#*y#)
x2#=x#*1.0/d#
y2#=y#*1.0/d# `<x1#,x2#> is just the direction from <tox,toy> to <fromx,fromy>
mLine(mem, tox,toy,tox+size*0.707106781*(x2#-y2#),toy+size*0.707106781*(x2#+y2#),color) `trig? I don't need no stinkin trig!
mLine(mem, tox,toy,tox+size*0.707106781*(y2#+x2#),toy+size*0.707106781*(y2#-x2#),color)
//type 3, circle
`circle tox,toy,size/2
endfunction
function mMake(mem1,width as integer height as integer)
make memblock mem1,(width*height*4)+12
write memblock dword mem1,0,width
write memblock dword mem1,4,height
write memblock dword mem1,8,32
endfunction
function mDot(mem as byte,x as integer, y as integer, color as dword)
if x>0 and y>0 and x<memblock dword(mem,0) and y<memblock dword(mem,4) then write memblock dword mem,((y*memblock dword(mem,0))+x)*4+12,color
endfunction
function mCls(mem as byte, color as dword)
for x=0 to memblock dword(mem,0)
for y=0 to memblock dword(mem,4)
mDot(mem,x,y,color)
next
next
endfunction
function mBox(mem as byte, x1 as integer,y1 as integer, x2 as integer,y2 as integer, color as dword)
for x=x1 to x2
for y=y1 to y2
mDot(mem,x,y,color)
next
next
endfunction
REM =========================
REM x1,y1 = starting point
REM x2,y2 = ending poing
REM thick = line thickness
REM draws a line
function mLine(mem as integer,x1,y1,x2,y2, color as dword)
dx = x2-x1
dy = y2-y1
`dot x1, y1
mDot(mem,x1,y1,color)
if abs(dx) > abs(dy)
m# = (0.0 + dy)/dx
b# = y1 - m#*x1
if dx < 0
dx = -1
else
dx = 1
endif
while x1 <> x2
x1 = x1 + dx
`dot x1, m#*x1+b#
mDot(mem,x1, m#*x1+b# ,color)
endwhile
else
if dy <> 0
m# = (0.0 + dx)/dy
b# = x1 - m#*y1
if dy < 0
dy = -1
else
dy = 1
endif
while y1 <> y2
y1 = y1 + dy
`dot m#*y1+b#, y1
mDot(mem,m#*y1+b#,y1,color)
endwhile
endif
endif
endfunction
Hold the space bar to check out a different part of the julia set.
If you want to start messing with the values, lemme tell ya how:
size changes the size of the whole box. Margin adjusts the margin around the edges. gridres is the grid resolution (how many arrows there are on the screen). ArrowLength adjusts the length of the arrows on the vectors (usually +5 to -5 from the value that's there is more than enough). The array vf holds the vector/direction information on the grid (this is what is displayed directly by the arrows). The array vfMag holds the magnitude of each vector in the corresponding vf element. calc_juliavelfield (calculate julia set velocity field) fills vf and vfMag, given a complex number (the first two values) to define the julia set, an offset value (added to the first complex value), the first iteration number, and the second iteration number. (where Z_n is the vector of the nth iteration of the julia set, a is the first iteration number, and b is the second iteration number, the function returns Z_b-Z_a)
You can also replace calc_juliavelfield with calc_brotvelfield. This is where those "modx/mody" values come in handy. They kind of offset the set. Anyways, just swap the functions out and put some changing variable in them for awesome stuffz.