So... I've still been doing math and graphing stuff. One thing that annoys me is that I don't have a really nice looking graph paper function for presenting stuff. Also, that last graph paper function I made wasn't that great or efficient. I haven't gone through any lengths to make this more efficient, but it definitely is cleaner, it doesn't draw over itself (well, a couple pixels, but no whole identical lines), and it doesn't draw offscreen, so I think it should be faster. It also looks better - not only due to the color scheme, but also due to a nice random textured background.
That code can be found here.
move your mouse around and click to change preferences.
That's just the plain graph paper. I've since added some handy graphing utilities. It now requires D3D_func and matrix1utils (for function pointers, and the exp() function)
As is handy in graphing stuff, there is a distinction between graph space and screen space. You can convert between the two using the functions pix_to_units and units_to_pix. Pixels, as you know, start at 0,0 (top left of the screen), and go to screen width,screenheight (bottom right). In my program, with units, negative x units is left on the screen, positive is right. Negative y units is down on the screen, positive is up.
list of things to do:
graphs that attempt to not make supah long lines at asymptotes
dashed/dotted lines
anti-aliased lines
nice functions for different kinds of graphs (polar, parametric, f(x)=y)
utilities for graphs with many outputs per input
star = implemented
light bulb = I know how ta do it
coffee = not much thought put into it yet.
The magic happens in the function:
function drawF_x(func as string, xresolution as float, tolerance as float, flags as integer, color as dword)
where "flags" is formatted as follows:
flags:
24 bit nothing
1 bit remove asymptotes
2 bit graph type
0: f(x)=y
1: f(x in degrees)=r (polar graph)
2: f(t)=x,y (parametric graph) -U
3: null
1 bit anti-alias -U
2 bit line type
0: solid
1: dashed -U
3: plot -W
4: dotted-even -U
1 bit line interpolation
0: linear - linear interpolation between points
1: cubic - cubic interpolation between points -U
1 bit oddfunction
0: standard - 1 or 0 values per call -W
1: nonstandard - if "more"=1 then the program reads values from the array "nonstandard"
(-U means unimplemented)
anyways, here's the code. Press q/w/e/r/t to show different graphs. Going from q to t, the graphs are:
q: a parabola described in polar coordinates
w: a parabola described as f(x)=y
e: cos(x*360)=y
r: a cool polar graph (cos(x)*sin(x)*e^(cos(x))
t: the bifurcation diagram of the logistic map
The code describing those five graphs is only 47 lines long, and its easy to add more graphs or take away graphs. Just set the return values of the global "ret" (x,y for parametric or f(x)=y graphs, r for polar graph; draw=1 if it is to be drawn, 0 if not; more=1 if the graph has many values for each input and has the nonstandard flag set to 1), and call the function with the desired flags.
Anyways, heres the code/demo. Remember: it needs d3dfunc, and matrix1 function pointers and exp().
type graphRet
x as float
y as float
r as float
more as integer
draw as integer
endtype
global ret as graphRet
global dim nonstandard(-1) as graphRet
d3d_init
global centerX `graph center, in pixels
global centerY
global scWidth `screen dimensions, in pixels
global scHeight
global PPU `pixels per unit
global DPU `divisions per unit
scWidth=screen width()
scHeight=screen height()
centerX=scWidth/2
centerY=scHeight/2
PPU=80
DPU=10
initGraphPaper()
remstart
flags:
24 bit nothing
1 bit remove asymptotes
2 bit graph type
0: f(x)=y
1: f(x in degrees)=r (polar graph)
2: f(t)=x,y (parametric graph) -U
3: null
1 bit anti-alias -U
2 bit line type
0: solid
1: dashed -U
3: plot
4: dotted-even -U
1 bit line interpolation
0: linear - linear interpolation between points
1: cubic - cubic interpolation between points -U
1 bit oddfunction
0: standard - 1 or 0 values per call
1: nonstandard - if "more"=1 then the program reads values from the array "nonstandard"
remend
do
D3D_BATCH_SET_LINE2D 100000
D3D_BATCH_SET_DOT2D 100000
genGraphPaper()
paste image 1,0,0
if keystate(16) then drawF_x("parabolaPOL",.1,100,%10110000,rgb(255,0,0))
if keystate(17) then drawF_x("parabolaFX",.01,100,%10010000,rgb(255,0,0))
if keystate(18) then drawF_x("cosineFX",.01,100,%10010000,rgb(255,0,0))
if keystate(19) then drawF_x("coolPol",.1,100,%10110000,rgb(255,0,0))
if keystate(20) then drawF_x("logistic",.01,100,%00001001,RGB(255,0,0))
D3D_BATCH_DRAW_LINE2D 1
D3D_BATCH_DRAW_DOT2D 1
sync
loop
end
function logistic(r as float)
xss as float
xss=.5
for n=1 to 40
xss=xss*r*(1-xss)
next
empty array nonstandard()
for n=1 to 40
array insert at bottom nonstandard()
p=array count(nonstandard())
nonstandard(p).y=xss
nonstandard(p).x=r
nonstandard(p).draw=1
nonstandard(p).more=1
xss=xss*r*(1-xss)
next n
ret.x=0
ret.y=0
ret.draw=1
ret.more=1
endfunction
function coolPol(x as float)
ret.r=cos(x)*sin(x)*exp(cos(x))
ret.draw=1
ret.more=0
endfunction
function cosineFX(x as float)
ret.x=x
ret.y=cos(x*360.0)
ret.draw=1
ret.more=0
endfunction
function parabolaFX(x as float)
ret.x=x
ret.y=x*x
ret.draw=1
ret.more=0
temp as graphRet
temp=ret
units_to_pix(0,ret.y)
ret=temp
if ret.y<0 or ret.y>scHeight then ret.draw=0
endfunction
function parabolaPOL(x as float)
ret.r=sin(x)/(cos(x)*cos(x))
ret.draw=1
ret.more=0
endfunction
function drawDot(x1 as float, y1 as float, flags as byte, color as dword)
removeAsymptotes=(flags<<24)>>31
graphtype=(flags<<25)>>30
antiAlias=(flags<<27)>>31
lineType=(flags<<28)>>30
lineInterp=(flags<<30)>>31
oddFunction=(flags<<31)>>31
units_to_pix(x1,y1)
x1=ret.x
y1=ret.y
d3d_batch_add_dot2d x1,y1,color
endfunction
function drawLine(x1 as float, y1 as float,x2 as float, y2 as float, flags as byte, color as dword)
removeAsymptotes=(flags<<24)>>31
graphtype=(flags<<25)>>30
antiAlias=(flags<<27)>>31
lineType=(flags<<28)>>30
lineInterp=(flags<<30)>>31
oddFunction=(flags<<31)>>31
units_to_pix(x1,y1)
x1=ret.x
y1=ret.y
units_to_pix(x2,y2)
x2=ret.x
y2=ret.y
d3d_batch_add_line2d x1,y1,x2,y2,color
endfunction
function drawF_x(func as string, xresolution as float, tolerance as float, flags as integer, color as dword)
remstart
flags:
24 bit nothing
1 bit remove asymptotes
2 bit graph type
0: f(x)=y
1: f(x in degrees)=r (polar graph)
2: f(t)=x,y (parametric graph) -U
3: null
1 bit anti-alias -U
2 bit line type
0: solid
1: dashed -U
3: plot -W
4: dotted-even -U
1 bit line interpolation
0: linear - linear interpolation between points
1: cubic - cubic interpolation between points -U
1 bit oddfunction
0: standard - 1 or 0 values per call -W
1: nonstandard - if "more"=1 then the program reads values from the array "nonstandard"
remend
removeAsymptotes=(flags<<24)>>31
graphtype=(flags<<25)>>30
antiAlias=(flags<<27)>>31
lineType=(flags<<28)>>30
lineInterp=(flags<<30)>>31
oddFunction=(flags<<31)>>31
if graphtype=0
lx as float
ly as float
stepd as integer
stepd=0
fromx as float
tox as float
maxy as float
miny as float
pix_to_units(0,0)
fromx=ret.x
maxy=ret.y
pix_to_units(scWidth,scHeight)
tox=ret.x
miny=ret.y
x as float
for x=fromx to tox step xresolution
call function name func,x
y#=ret.y
if oddfunction=1 and ret.more=1
zzz as dword
for zzz=0 to array count(nonstandard())
ret=nonstandard(zzz)
y#=ret.y
if ret.draw=1
if linetype=2
drawDot(x,y#,flags,color)
else
if stepd=0
stepd=1
lx=x
ly=y#
else
dist#=abs(y#-ly)
if removeAsymptotes=0 or dist#<tolerance
drawLine(lx,ly,x,y#,flags, color)
endif
endif
endif
else
stepd=0
endif
next n
else
if ret.draw=1
if linetype=2
drawDot(x,y#,flags,color)
else
if stepd=0
stepd=1
lx=x
ly=y#
else
dist#=abs(y#-ly)
if removeAsymptotes=0 or dist#<tolerance
drawLine(lx,ly,x,y#,flags, color)
endif
endif
endif
else
stepd=0
endif
endif
lx=x
ly=y#
next x
endif
if graphtype=1
lx as float
ly as float
stepd as integer
stepd=0
fromx as float
tox as float
maxy as float
miny as float
startang=-360
endang=360
n as float
for n=startang to endang step xresolution
call function name func,n
r as float
r=ret.r
x#=r*cos(n)
y#=r*sin(n)
if ret.draw=1
if stepd=0
stepd=1
lx=x#
ly=y#
else
a#=lx-x#
b#=ly-y#
dist#=a#*a#+b#*b#
if removeAsymptotes=0 or dist#<tolerance*tolerance
drawLine(lx,ly,x#,y#,flags, color)
endif
lx=x#
ly=y#
endif
else
stepd=0
endif
next x
endif
endfunction
function pix_to_units(x,y)
ret.x=(x-centerX)*1.0/PPU
ret.y=-(y-centerY)*1.0/PPU
endfunction
function units_to_pix(x as float, y as float)
ret.x=x*PPU+centerX
ret.y=-y*PPU+centerY
endfunction
function genGraphPaper()
copy memblock 2,1,0,0,get memblock size(1)
left=floor(-centerX*DPU*1.0/PPU)+1
right=ceil((scWidth-centerX)*DPU*1.0/PPU)-1
for x=left to right
if x mod DPU = 0 `don't bother drawing, cuz a major line willl be drawn over it anyways.
`skip
else
x2=x*1.0/DPU*PPU+centerX
vertLine(1,x2,rgb(175,220,220))
endif
next x
left=ceil((centerY-scHeight)*DPU*1.0/PPU)-1
right=floor(centerY*DPU*1.0/PPU)+1
for y=left to right
if y mod DPU = 0 `don't bother drawing, cuz a major line willl be drawn over it anyways.
`skip
else
y2=-y*1.0/DPU*PPU+centerY
horizLine(1,y2,rgb(175,220,220))
endif
next x
left=floor(-centerX*1.0/PPU)+1
right=ceil((scWidth-centerX)*1.0/PPU)-1
for x=left to right
x2=x*PPU+centerX
if x=0
vertLine(1,x2-1,rgb(80,80,80))
vertLine(1,x2,rgb(80,80,80))
vertLine(1,x2+1,rgb(80,80,80))
else
vertLine(1,x2,rgb(80,80,80))
endif
next x
left=ceil((centerY-scHeight)*1.0/PPU)-1
right=floor(centerY*1.0/PPU)+1
for y=left to right
y2=-y*PPU+centerY
if y=0
horizLine(1,y2-1,rgb(80,80,80))
horizLine(1,y2,rgb(80,80,80))
horizLine(1,y2+1,rgb(80,80,80))
else
horizLine(1,y2,rgb(80,80,80))
endif
next y
make image from memblock 1,1
endfunction
function initGraphPaper()
makeTexture(2,scWidth,scHeight,rgb(220,220,220),rgb(240,240,255),10)
makeMemImg(1,scWidth,scHeight)
endfunction
function pasteMem(mem as integer, srcMem as integer,strtx,strty, x, y)
//width and height of destination memblock
width = memblock dword(mem,0)
height = memblock dword(mem,4)
widthsrc = memblock dword(srcMem,0)
heightsrc = memblock dword(srcMem,4)
for px = strtx to (widthsrc-1)
for py = strty to (heightsrc-1)
if px+x<width and py+y<height
col = getPix(srcMem,px,py)
setPix(mem,x+px,y+py,col)
endif
next py
next px
endfunction
function horizLine(mem,y,color as dword)
height = memblock dword(mem,4)
width = memblock dword(mem,0)
if y<0 or y>=height then exitfunction
for x=0 to width-1
setPix(mem,x,y,color)
next x
endfunction
function vertLine(mem,x,color as dword)
height = memblock dword(mem,4)
width = memblock dword(mem,0)
if x<0 or x>=width then exitfunction
for y=0 to height-1
setPix(mem,x,y,color)
next y
endfunction
function makeTexture(mem,width,height,colfrom as dword, colto as dword,rand as integer)
makeMemImg(mem,width,height)
r1=rgbr(colfrom)
g1=rgbg(colfrom)
b1=rgbb(colfrom)
r2=rgbr(colto)
g2=rgbg(colto)
b2=rgbb(colto)
r as byte
g as byte
b as byte
col as dword
for x=0 to width-1
for y=0 to height-1
t#=rnd(10000)/10000.0
r=r1+t#*(r2-r1)
g=g1+t#*(g2-g1)
b=b1+t#*(b2-b1)
if rnd(1)
r=inc2(r,rnd(rand))
else
r=dec2(r,rnd(rand))
endif
if rnd(1)
g=inc2(g,rnd(rand))
else
g=dec2(g,rnd(rand))
endif
if rnd(1)
b=inc2(b,rnd(rand))
else
b=dec2(b,rnd(rand))
endif
col=rgb(r,g,b)
setPix(mem,x,y,col)
next y
next x
endfunction
function makeMemImg(mem,width, height)
make memblock mem,12+4*width*height
write memblock dword mem,0,width
write memblock dword mem,4,height
write memblock dword mem,8,32
endfunction mem
function setPix(mem, x as integer, y as integer, color as dword)
write memblock dword mem,4*(y*memblock dword(mem,0)+x)+12,color
endfunction
function getPix(memblock, x, y)
pixel as dword
pixel = memblock dword(memblock,(4*((y*memblock dword(memblock,0))+x))+12)
endfunction pixel
function dec2(a as byte, b as byte) `a-b
q as byte
ret as byte
ret=0
q=a-b
if difference(a,q)=b then ret=q
endfunction ret
function inc2(a as byte, b as byte) `a+b
q as byte
ret as byte
ret=255
q=a+b
if difference(a,q)=b then ret=q `if q has not rolled over.
endfunction ret
function difference(a as byte, b as byte)
ret as byte
if a>b
ret=a-b
else
ret=b-a
endif
endfunction ret
REM ********************************
REM * Title: Anti-aliased lines *
REM * Author: Phaelax *
REM * Date: Nov 22, 2009 *
REM ********************************
REM Xiaolin Wu's line algorithm
function wuline(x1,y1,x2,y2)
local dx as float
local dy as float
local xend as float
local yend as float
local xgap as float
local ygap as float
local xpxl1 as float
local ypxl1 as float
local xpxl2 as float
local ypxl2 as float
local intery as float
local interx as float
dx = x2-x1
dy = y2-y1
if abs(dx) > abs(dy)
rem handle horizontal lines
if x2 < x1
ax = x1
x1 = x2
x2 = ax
ay = y1
y1 = y2
y2 = ay
endif
gradient# = dy / dx
rem handle first endpoint
xend = ceil(x1)
yend = y1 + gradient# * (xend-x1)
xgap = 1.0 - fract#(x1 + 0.5)
xpxl1 = xend : `used in main loop
ypxl1 = int(yend)
f# = 1-fract#(yend)*xgap : `brightness
c = 255*f#
ink rgb(c,c,c),0
dot xpxl1, ypxl1
rem brightness: fract#(yend)*xgap
f# = fract#(yend)*xgap : `brightness
c = 255*f#
ink rgb(c,c,c),0
dot xpxl1, ypxl1+1
intery = yend + gradient# : `first y-intersection for main loop
rem handle second endpoint
xend = ceil(x2)
yend = y2 + gradient# * (xend-x2)
xgap = 1.0 - fract#(x2 + 0.5)
xpxl2 = xend : `used in main loop
ypxl2 = int(yend)
f# = 1-fract#(yend)*xgap : `brightness
c = 255*f#
ink rgb(c,c,c),0
dot xpxl2, ypxl2
f# = fract#(yend)*xgap : `brightness
c = 255*f#
ink rgb(c,c,c),0
dot xpxl2, ypxl2+1
rem main loop
a = xpxl1+1
b = xpxl2-1
for x = a to b
f# = 1-fract#(intery) : `brightness
c = 255*f#
ink rgb(c,c,c),0
dot x, intery
f# = fract#(intery) : `brightness
c = 255*f#
ink rgb(c,c,c),0
dot x, intery+1
intery = intery + gradient#
next x
else
rem handle vertical lines
if y2 < y1
ax = x1
x1 = x2
x2 = ax
ay = y1
y1 = y2
y2 = ay
endif
gradient# = dx / dy
ink rgb(255,0,0),0
rem handle first endpoint
yend = ceil(y1)
xend = x1 + gradient# * (yend-y1)
ygap = 1.0 - fract#(y1 + 0.5)
xpxl1 = int(xend)
ypxl1 = yend
f# = 1-fract#(xend)*ygap : `brightness
c = 255*f#
ink rgb(c,c,c),0
dot xpxl1, ypxl1
f# = fract#(xend)*ygap : `brightness
c = 255*f#
ink rgb(c,c,c),0
dot xpxl1, ypxl1+1
interx = xend + gradient# : `first y-intersection for main loop
rem handle second endpoint
yend = ceil(y2)
xend = x2 + gradient# * (yend-y2)
ygap = fract#(y2 + 0.5)
xpxl2 = int(xend)
ypxl2 = yend
f# = 1-fract#(xend)*ygap : `brightness
c = 255*f#
ink rgb(c,c,c),0
dot xpxl2, ypxl2
f# = fract#(xend)*ygap : `brightness
c = 255*f#
ink rgb(c,c,c),0
dot xpxl2, ypxl2+1
rem main loop
a = ypxl1+1
b = ypxl2-1
for y = a to b
f# = 1-fract#(interx) : `brightness
c = 255*f#
ink rgb(c,c,c),0
dot interx, y
f# = fract#(interx) : `brightness
c = 255*f#
ink rgb(c,c,c),0
dot interx+1, y
interx = interx + gradient#
next y
endif
endfunction
function fract#(x as float)
a# = x - int(x)
endfunction a#
aaand a pic. The code implementing the actual line (red) is only 20 lines long, + 1 line in the loop to draw it.