Notifications
Clear all

[Closed] Interactive bitmaps

Cool little UI thingys. I can think of an immediate use for two of them as ui improvements here at focus… thanks so much for sharing… I think I’ll impliment test number five as a control for our network render file pathing… and see if anyone can find their renders… ha ha…

I am always in favor of making animators or other crew members go crazy because of
my script and tools just for fun. But I am not sure I understood exactly how are you
planing to use test 5 for whatever you said you would… :curious:

By the way, I would be glad to hear how and where did you implement these babies.

Thanks for the comments!

Well… I was thinking each grid opening could represent a path on the network… and of course the user would have no idea where it is, but could get there through this ui element… ha ha ha…( evil laugh ) Really, Either of the curves would look nice as soft selection ui elements…

This is awesome. Great job!

i love playing with bitmaps. So much fun and can really help illustrating scenarios. How fast are bitmaps generally for you guys to manipulate. Where do you guys begin to see the limitations? I haven’t really tried to push the limit with bitmaps.

/Andreas

nice!, didn’t know exactly how the softselection work, now i know thanks to you. A bezier line with both tangent points manipulated by the pinch and bubble values respectively

Test #7:


  try destroyDialog bitmapTest catch ()
  rollout bitmapTest "Bitmap test 7"
  (
  -- Local Variable Declerations
  ------------------------------------------
  
	  local dim = 200
	  
	  local bgMap
	local mousePressed = false
	
	  local bgColor = white
	local p1Color = red
	local p2Color = green
	local p3Color = blue
	
	local pt1 = [10,dim - 10]
	local pt2 = [dim / 2,10]
	local pt3 = [dim - 10,dim - 10]
	local jsPos = [0,0]
  
  -- User Interface
  ------------------------------------------
  
	  bitmap bmGraph "" width:dim height:dim
	spinner spSubD "SubD:" range:[1,100,6] type:#integer align:#center fieldWidth:30
	  
  -- Functions
  ------------------------------------------
	
	  fn createBGMap =
	  (
		  bgMap = bitmap bmGraph.width bmGraph.height color:bgColor
	  )
	
	fn lineLineIntersect pA pB pC pD = 
	(
		pA = [pA.x,pA.y,0]
		pB = [pB.x,pB.y,0]
		pC = [pC.x,pC.y,0]
		pD = [pD.x,pD.y,0]
		
		local a = pB - pA
		local b = pD - pC
		local c = pC - pA
		local cross1 = cross a b
		local cross2 = cross c b
		local n = pA + (a * ((dot cross2 cross1) / ((length cross1) ^ 2)))
		[n.x,n.y]
	)
	
	fn getSteps p1 p2 =
	(
		struct s_neighbor (
			pos,
			dist
		)
		
		local neighborsPos = #([-1,-1],[0,-1],[1,-1],[-1,0],[1,0],[-1,1],[0,1],[1,1])
		local neighbors = #()
		
		for p in neighborsPos do (
			local newNeighbor = s_neighbor p (distance p2 (p1 + p)) 
			append neighbors newNeighbor
		)
		
		fn sortFn v1 v2 =
		(
			if v1.dist > v2.dist then 1
			else if v1.dist < v2.dist then -1
			else 0
		)
		
		qsort neighbors sortFn
		#(neighbors[1].pos,neighbors[2].pos)
	)
	
	fn getStep p1 p2 pt steps =
	(
		local crossPt = lineLineIntersect p1 p2 (pt + steps[1]) (pt + steps[2])
		local dist1 = distance crossPt (pt + steps[1])
		
		if dist1 < 0.5 then (
			pt + steps[1]
		) else (
			pt + steps[2]
		)
	)
	
	fn drawEdge p1 p2 c1 c2 bMap =
	(
		setPixels bMap p1 #(c1)
		setPixels bMap p2 #(c2)
		
		local newPt = p1
		local steps = getSteps p1 p2
		local cnt = 0
		
		while newPt != p2 and cnt < dim * 2 do (
			newPt = getStep p1 p2 newPt steps
			local dist1 = distance p1 newPt
			local dist2 = distance p2 newPt
			local newColor = c1 * dist2 / (dist1 + dist2) + c2 * dist1 / (dist1 + dist2)
			setPixels bMap newPt #(newColor)
			cnt += 1
		)
		
		bMap
	)
	
	  fn updateGraph =
	  (
		  local newMap = copy bgMap
		local subD = spSubD.value + 1
		
		newMap = drawEdge pt1 pt2 p1color p2color newMap
		newMap = drawEdge pt1 pt3 p1color p3color newMap
		newMap = drawEdge pt2 pt3 p2color p3color newMap
		
		fn round val =
		(
			if val - (floor val) > 0.5 then (
				ceil val
			) else (
				floor val
			)
		)
		
		for i = 1.0 to subD - 1.0 do (
			local rat = i / subD
			local pt = pt3 + (pt2 - pt3) * rat
			pt = [round pt.x,round pt.y]
			newMap = drawEdge pt1 pt p1color (p2color * rat + p3color * (1 - rat)) newMap
		)
		
		for i = 1.0 to subD - 1.0 do (
			local rat = i / subD
			local pt = pt3 + (pt1 - pt3) * rat
			pt = [round pt.x,round pt.y]
			newMap = drawEdge pt2 pt p2color (p1color * rat + p3color * (1 - rat)) newMap
		)
		
		for i = 1.0 to subD - 1.0 do (
			local rat = i / subD
			local pt = pt2 + (pt1 - pt2) * rat
			pt = [round pt.x,round pt.y]
			newMap = drawEdge pt3 pt p3color (p1color * rat + p2color * (1 - rat)) newMap
		)
		  
		bmGraph.bitmap = newMap
	  ) -- end updateGraph fn
	  
	fn updateJS pos =
	(
		if mousePressed do (
			jsPos = pos - bmGraph.pos
			
			if jsPos.x >= 0 and jsPos.x <= dim then (
				if jsPos.y >= 0 and jsPos.y <= dim then (
					if distance jsPos pt1 < distance jsPos pt2 and distance jsPos pt1 < distance jsPos pt3 then (
						pt1 = jsPos
					) else (
						if distance jsPos pt2 < distance jsPos pt3 then (
							pt2 = jsPos
						) else (
							pt3 = jsPos
						)
					)
					
					updateGraph()
				)
			)
		)
	)
	
	  fn openDialog =
	  (
		  createDialog bitmapTest width:(dim + 10) style:#(#style_titlebar, #style_border, #style_sysmenu,#style_resizing)
	  )
	  
	  fn init =
	  (
		  createBGMap()
		  updateGraph()
	  )
  
	  fn done =
	  (
		  -- cleanup code
		  gc light:true
	  )
  
  -- Event Handlers
  ------------------------------------------
	  
	on bitmapTest lbuttondown pos do mousePressed = true
	on bitmapTest lbuttonup pos do mousePressed = false
	on bitmapTest mousemove pos do updateJS pos
	
	on spSubD changed val do updateGraph()
	
	  on bitmapTest open do init()
	  on bitmapTest close do done()
  
  ) -- end of rollout
  
  bitmapTest.openDialog()

For a while I was curious about how to draw a line between to point in a bitmap, the quickest way. Yesterday I got an idea. This is the first demo using the new technique.
The basic idea is to test the neighbor pixels of the pixel located on one end of the line and pick the two pixels that are closest to the other end. Than all you need to do is to progress along the line by moving from neighbor to neighbor and checking which neighbor should be next until you reach the other end.

prettyPixel – thanks for that wonderful thread:
http://forums.cgsociety.org/showthread.php?f=98&t=295257
I used the Line-Line Intersection function to determine which neighbor to pick.

I would also like to thank Dave (Opeth) for helping me to develop the idea.

Test #8:


    try destroyDialog bitmapTest catch ()
    rollout bitmapTest "Bitmap test 6"
    (
    -- Local Variable Declerations
    ------------------------------------------
    
    	local width = 200
    	local height = 200
    	local boarder = 20
    	
    	local bgColor = white
    	local p1Color = red
    	local p2Color = green
    	local p3Color = blue
    	
    	local stripedLinesColor = bgColor / 2
    	local graphColor = blue
    	local bgMap
  	local mousePressed = false
  	local jsPos = [0,0]
    
    -- User Interface
    ------------------------------------------
    
    	bitmap bmGraph "" width:(width * 2) height:(height + 2 * boarder)
    	spinner spMax "Max:" range:[-10,10,1] align:#left offset:[-10,0] fieldwidth:35 scale:0.01
    	spinner spMin "Min:" range:[-10,10,0] align:#right offset:[10,-22] fieldwidth:35 scale:0.01
    	spinner spPrec "precision:" range:[0.01,1,0.5] align:#center fieldwidth:40 scale:0.01
    	
    -- Functions
    ------------------------------------------
    	
    	fn createBGMap =
    	(
    		bgMap = bitmap bmGraph.width bmGraph.height color:bgColor
    		
    		-- Draw Horizontal striped lines:
    		local linePixels = #()
    		for x = 0 to (bmGraph.width - 1) do (
    			append linePixels (if mod x 10 > 5 then stripedLinesColor else bgColor)
    		)
    		setPixels bgMap [0,boarder] linePixels
    		setPixels bgMap [0,boarder + height] linePixels
    		
    		-- Draw Vertical striped lines:
    		for y = 0 to (bmGraph.height - 1) do (
    			if mod y 10 > 5 then (
    				setPixels bgMap [width,y] #(stripedLinesColor)
    			)
    		)
    	)
    	
    	fn lineLineIntersect pA pB pC pD = 
    	(
    		pA = [pA.x,pA.y,0]
    		pB = [pB.x,pB.y,0]
    		pC = [pC.x,pC.y,0]
    		pD = [pD.x,pD.y,0]
    		
    		local a = pB - pA
    		local b = pD - pC
    		local c = pC - pA
    		local cross1 = cross a b
    		local cross2 = cross c b
    		local n = pA + (a * ((dot cross2 cross1) / ((length cross1) ^ 2)))
    		[n.x,n.y]
    	)
    	
    	fn getSteps p1 p2 =
    	(
    		struct s_neighbor (
    			pos,
    			dist
    		)
    		
    		local neighborsPos = #([-1,-1],[0,-1],[1,-1],[-1,0],[1,0],[-1,1],[0,1],[1,1])
    		local neighbors = #()
    		
    		for p in neighborsPos do (
    			local newNeighbor = s_neighbor p (distance p2 (p1 + p)) 
    			append neighbors newNeighbor
    		)
    		
    		fn sortFn v1 v2 =
    		(
    			if v1.dist > v2.dist then 1
    			else if v1.dist < v2.dist then -1
    			else 0
    		)
    		
    		qsort neighbors sortFn
    		#(neighbors[1].pos,neighbors[2].pos)
    	)
    	
    	fn getStep p1 p2 pt steps =
    	(
    		local crossPt = lineLineIntersect p1 p2 (pt + steps[1]) (pt + steps[2])
    		local dist1 = distance crossPt (pt + steps[1])
    		
    		if dist1 < 0.5 then (
    			pt + steps[1]
    		) else (
    			pt + steps[2]
    		)
    	)
    	
    	fn drawEdge p1 p2 bMap =
    	(
    		local newPt = p1
    		local steps = getSteps p1 p2
    		local cnt = 0
    		
    		while newPt != p2 and cnt < 200 do (
    			newPt = getStep p1 p2 newPt steps
    			setPixels bMap newPt #(graphColor)
    			cnt += 1
    		)
    		
    		bMap
    	)
    	
    	fn updateGraph =
    	(
    		-- Draw Bezier Curve:
    		local newMap = copy bgMap
    		local P0 = [0,height + boarder]
    		local P1 = [width / 3,boarder + height * (1 - spMin.value)]
    		local P2 = [width * 2 / 3,boarder + height * (1 - spMax.value)]
    		local P3 = [width,boarder]
    		
    		local lastPt = [0,0]
    		for t = 0.0 to 1.0 by spPrec.value do (
    			local Pt = (1-t) ^ 3 * P0 + 3 * (1-t) ^ 2 * t * P1 + 3 * (1-t) * t ^ 2 * P2 + t ^ 3 * P3
    			for i = 1 to 2 do (
    				Pt[i] = if Pt[i] - (floor Pt[i]) > 0.5 then ceil Pt[i] else floor Pt[i]
    			)
    			if Pt != lastPt then (
    				newMap = drawEdge lastPt Pt newMap
    				newMap = drawEdge [2 * width - lastPt.x,lastPt.y] [2 * width - Pt.x,Pt.y] newMap
    				lastPt = Pt
    			)
    		)
    		newMap = drawEdge [width,boarder] lastPt newMap
    		newMap = drawEdge [width,boarder] [2 * width - lastPt.x,lastPt.y] newMap
    		bmGraph.bitmap = newMap
    	) -- end updateGraph fn
    	
    	fn openDialog =
    	(
    		createDialog bitmapTest width:((width + 5) * 2) style:#(#style_titlebar, #style_border, #style_sysmenu,#style_resizing)
    	)
    	
    	fn init =
    	(
    		createBGMap()
    		updateGraph()
    	)
    
    	fn done =
    	(
    		-- cleanup code
    		gc light:true
    	)
    
    -- Event Handlers
    ------------------------------------------
    
    	on spPrec changed val do updateGraph()
    	on spMax changed val do updateGraph()
    	on spMin changed val do updateGraph()
    	
    	on bitmapTest open do init()
    	on bitmapTest close do done()
    
    ) -- end of rollout
    
    bitmapTest.openDialog()
    
A Bezier Curve with the Draw Edge Function.

Test #9:


 try destroyDialog bitmapTest catch ()
rollout bitmapTest "Bitmap test 9"
(
-- Local Variable Declerations
------------------------------------------
  
	local dim = 200
	local knotRad = 2.4
	
	local bgMap
	local mousePressed = false
	local lastKnotInd = [0,0]
	
	local bgColor = white
	local knotColor = red
	local knotVecColor = green
	local lineColor = blue
	
	local knotsArr = #()
	local jsPos = [0,0]
  
-- User Interface
------------------------------------------
  
	bitmap bmGraph "" width:dim height:dim
	radioButtons rbMode "" labels:#("Draw","Edit") align:#center
	radioButtons rbBezierMode "" labels:#("Bezier","Bezier Corner") align:#center
	checkBox cbHideKnots "Hide Knots" align:#center
	spinner spSteps "Steps:" range:[1,20,6] type:#integer align:#center fieldWidth:30
	button bnClear "Clear" width:100
	
-- Functions
------------------------------------------
	
	fn createBGMap =
	(
		bgMap = bitmap bmGraph.width bmGraph.height color:bgColor
	)
	
	fn round val =
	(
		if val - (floor val) < 0.5 then floor val else ceil val
	)
	
	fn lineLineIntersect pA pB pC pD = 
	(
		pA = [pA.x,pA.y,0]
		pB = [pB.x,pB.y,0]
		pC = [pC.x,pC.y,0]
		pD = [pD.x,pD.y,0]
		
		local a = pB - pA
		local b = pD - pC
		local c = pC - pA
		local cross1 = cross a b
		local cross2 = cross c b
		local n = pA + (a * ((dot cross2 cross1) / ((length cross1) ^ 2)))
		[n.x,n.y]
	)
	
	fn getSteps p1 p2 =
	(
		struct s_neighbor (
			pos,
			dist
		)
		
		local neighborsPos = #([-1,-1],[0,-1],[1,-1],[-1,0],[1,0],[-1,1],[0,1],[1,1])
		local neighbors = #()
		
		for p in neighborsPos do (
			local newNeighbor = s_neighbor p (distance p2 (p1 + p)) 
			append neighbors newNeighbor
		)
		
		fn sortFn v1 v2 =
		(
			if v1.dist > v2.dist then 1
			else if v1.dist < v2.dist then -1
			else 0
		)
		
		qsort neighbors sortFn
		#(neighbors[1].pos,neighbors[2].pos)
	)
	
	fn getStep p1 p2 pt steps =
	(
		local crossPt = lineLineIntersect p1 p2 (pt + steps[1]) (pt + steps[2])
		local dist1 = distance crossPt (pt + steps[1])
		
		if dist1 < 0.5 then (
			pt + steps[1]
		) else (
			pt + steps[2]
		)
	)
	
	fn drawDot pos rad bMap color =
	(
		for y = 0 to dim - 1 where y >= pos.y - rad and y <= pos.y + rad do (
			for x = 0 to dim - 1 where distance pos [x,y] <= rad do (
				setPixels bMap [x,y] #(color)
			)
		)
	)
	
	fn drawEdge p1 p2 color bMap =
	(
		local newPt = p1
		local steps = getSteps p1 p2
		local cnt = 0
		
		setPixels bMap newPt #(color)
		
		while newPt != p2 and cnt < dim * 2 do (
			newPt = getStep p1 p2 newPt steps
			setPixels bMap newPt #(color)
			cnt += 1
		)
		
		bMap
	)
	
	fn drawKnots bmap =
	(
		if not cbHideKnots.state and knotsArr.count > 0 then (
			for k = 1 to knotsArr.count do (
				drawDot knotsArr[k][1] knotRad bmap knotVecColor
				drawDot knotsArr[k][2] knotRad bmap knotColor
				drawDot knotsArr[k][3] knotRad bmap knotVecColor
			)
			if mousePressed and rbMode.state == 2 then (
				drawDot knotsArr[lastKnotInd.x][lastKnotInd.y] (knotRad * 1.5) bmap orange
			)
		)
		bmap
	)
	
	fn updateGraph =
	(
		local newMap = copy bgMap
		
		if knotsArr.count > 0 then (
			for k = 1 to knotsArr.count do (
				if knotsArr.count > 1 and k < knotsArr.count then (
					local P0 = knotsArr[k][2]
					local P1 = knotsArr[k][3]
					local P2 = knotsArr[k + 1][1]
					local P3 = knotsArr[k + 1][2]
					local Pt0 = P0
					for t = 0.0 to 1.0 by 1.0 / spSteps.value do (
						local Pt = (1-t) ^ 3 * P0 + 3 * (1-t) ^ 2 * t * P1 + 3 * (1-t) * t ^ 2 * P2 + t ^ 3 * P3
						Pt = [round Pt.x,round Pt.y]
						newMap = drawEdge Pt0 Pt lineColor newMap
						Pt0 = Pt
					)
				)
				if not cbHideKnots.state then (
					newMap = drawEdge knotsArr[k][1] knotsArr[k][2] knotVecColor newMap
					newMap = drawEdge knotsArr[k][2] knotsArr[k][3] knotVecColor newMap
				)
			)
		)
		
		newMap = drawKnots newMap
		  
		bmGraph.bitmap = newMap
	) -- end updateGraph fn
	
	fn mPressed pos =
	(
		mousePressed = true
		if rbMode.state == 1 then (
			local newKnot = #()
			append newKnot (pos - bmGraph.pos - [10,0])
			append newKnot (pos - bmGraph.pos)
			append newKnot (pos - bmGraph.pos + [10,0])
			append knotsArr newKnot
			updateGraph()
		) else (
			if knotsArr.count > 0 then (
				local lastDist = dim * 2
				for k = 1 to knotsArr.count do (
					for i = 1 to 3 do (
						local newDist = distance (pos - bmGraph.pos) knotsArr[k][i]
						if newDist < lastDist then (
							lastDist = newDist
							lastKnotInd = [k,i]
						)
					)
				)
			)
		)
	)
	
	fn updateJS pos =
	(
		if mousePressed do (
			jsPos = pos - bmGraph.pos
			
			if rbMode.state == 1 then (
				knotsArr[knotsArr.count][2] = jsPos
				knotsArr[knotsArr.count][1] = jsPos - [10,0]
				knotsArr[knotsArr.count][3] = jsPos + [10,0]
			) else (
				if lastKnotInd.y == 2 then (
					local oldPos = knotsArr[lastKnotInd.x][lastKnotInd.y]
					knotsArr[lastKnotInd.x][1] += jsPos - oldPos
					knotsArr[lastKnotInd.x][3] += jsPos - oldPos
				) else (
					if rbBezierMode.state == 1 then (
						local otherHandleIndex = 4 - lastKnotInd.y
						local otherHandleDist = distance knotsArr[lastKnotInd.x][2] knotsArr[lastKnotInd.x][otherHandleIndex]
						local dir = normalize (jsPos - knotsArr[lastKnotInd.x][2])
						local otherHandlePos = knotsArr[lastKnotInd.x][2] - dir * otherHandleDist
						knotsArr[lastKnotInd.x][otherHandleIndex] = [round otherHandlePos.x,round otherHandlePos.y]
					)
				)
				knotsArr[lastKnotInd.x][lastKnotInd.y] = jsPos
			)
		)
		
		updateGraph()
	)
	
	fn clearVP =
	(
		knotsArr = #()
		updateGraph()
		rbMode.state = 1
	)
	
	fn openDialog =
	(
		createDialog bitmapTest width:(dim + 10) style:#(#style_titlebar, #style_border, #style_sysmenu,#style_resizing)
	)
	  
	fn init =
	(
		createBGMap()
		updateGraph()
	)
  
	fn done =
	(
		-- cleanup code
		gc light:true
	)
  
-- Event Handlers
------------------------------------------
	  
	on bitmapTest lbuttondown pos do mPressed pos
	on bitmapTest lbuttonup pos do mousePressed = false
	on bitmapTest mousemove pos do updateJS pos
	
	on bnClear pressed do clearVP()
	on cbHideKnots changed state do updateGraph()
	on spSteps changed val do updateGraph()
	
	on bitmapTest open do init()
	on bitmapTest close do done()
  
) -- end of rollout
  
bitmapTest.openDialog()
  

Draw a Spline using the bezier function and draw edge technique.
Edit (05.07.2008) – Added Bezier / Bezier Corner Handles radio button.

 PEN

Fantastic!

Test #10:


try destroyDialog bitmapTest catch ()
rollout bitmapTest "Bitmap test 9"
(
-- Local Variable Declerations
------------------------------------------
  
	local dim = 200
	local knotRad = 2.4
	
	local bgMap, newMap
	local mousePressed = false
	local lastKnotInd = [0,0]
	local knotsArr = #()
	local jsPos = [0,0]
	
	local bgColor = white
	local knotColor = red
	local knotVecColor = green
	local lineColor = blue
  
-- User Interface
------------------------------------------
  
	bitmap bmGraph "" width:dim height:dim
	radioButtons rbMode "" labels:#("Draw","Edit") align:#center
	radioButtons rbBezierMode "" labels:#("Bezier","Bezier Corner") align:#center
	spinner spThickness "Thickness:" range:[0.1,10,0.3] align:#center scale:0.1 fieldWidth:40
	checkBox cbHideKnots "Hide Knots" align:#center
	spinner spSteps "Steps:" range:[1,20,6] type:#integer align:#center fieldWidth:30
	button bnClear "Clear" width:100
	
-- Functions
------------------------------------------
	
	fn createBGMap =
	(
		bgMap = bitmap bmGraph.width bmGraph.height color:bgColor
	)
	
	fn round val =
	(
		if val - (floor val) < 0.5 then floor val else ceil val
	)
	
	fn lineLineIntersect pA pB pC pD = 
	(
		pA = [pA.x,pA.y,0]
		pB = [pB.x,pB.y,0]
		pC = [pC.x,pC.y,0]
		pD = [pD.x,pD.y,0]
		
		local a = pB - pA
		local b = pD - pC
		local c = pC - pA
		local cross1 = cross a b
		local cross2 = cross c b
		local n = pA + (a * ((dot cross2 cross1) / ((length cross1) ^ 2)))
		[n.x,n.y]
	)
	
	fn getSteps p1 p2 =
	(
		struct s_neighbor (
			pos,
			dist
		)
		
		local neighborsPos = #([-1,-1],[0,-1],[1,-1],[-1,0],[1,0],[-1,1],[0,1],[1,1])
		local neighbors = #()
		
		for p in neighborsPos do (
			local newNeighbor = s_neighbor p (distance p2 (p1 + p)) 
			append neighbors newNeighbor
		)
		
		fn sortFn v1 v2 =
		(
			if v1.dist > v2.dist then 1
			else if v1.dist < v2.dist then -1
			else 0
		)
		
		qsort neighbors sortFn
		#(neighbors[1].pos,neighbors[2].pos)
	)
	
	fn getStep p1 p2 pt steps =
	(
		local crossPt = lineLineIntersect p1 p2 (pt + steps[1]) (pt + steps[2])
		local dist1 = distance crossPt (pt + steps[1])
		
		if dist1 < 0.5 then (
			pt + steps[1]
		) else (
			pt + steps[2]
		)
	)
	
	fn drawDot pos rad color =
	(
		for y = 0 to rad do (
			local colorArr = #()
			for x = 0 to rad do (
				local dist = sqrt (x ^ 2 + y ^ 2)
				if dist <= rad then (
					append colorArr color
					insertItem color colorArr 1
				)
			)
			setPixels newMap (pos - [colorArr.count / 2,y]) colorArr
			setPixels newMap (pos - [colorArr.count / 2,- y]) colorArr
		)
	)
	
	fn drawEdge p1 p2 color rad =
	(
		local newPt = p1
		local steps = getSteps p1 p2
		local cnt = 0
		
		while newPt != p2 and cnt < dim * 2 do (
			newPt = getStep p1 p2 newPt steps
			drawDot newPt rad color
			cnt += 1
		)
	)
	
	fn drawKnots =
	(
		if not cbHideKnots.state and knotsArr.count > 0 then (
			for k = 1 to knotsArr.count do (
				drawDot knotsArr[k][1] knotRad knotVecColor
				drawDot knotsArr[k][2] knotRad knotColor
				drawDot knotsArr[k][3] knotRad knotVecColor
			)
			if mousePressed and rbMode.state == 2 then (
				drawDot knotsArr[lastKnotInd.x][lastKnotInd.y] (knotRad * 1.5) orange
			)
		)
	)
	
	fn updateGraph =
	(
		newMap = copy bgMap
		
		if knotsArr.count > 0 then (
			for k = 1 to knotsArr.count do (
				if knotsArr.count > 1 and k < knotsArr.count then (
					local P0 = knotsArr[k][2]
					local P1 = knotsArr[k][3]
					local P2 = knotsArr[k + 1][1]
					local P3 = knotsArr[k + 1][2]
					local Pt0 = P0
					for t = 0.0 to 1.0 by 1.0 / spSteps.value do (
						local Pt = (1-t) ^ 3 * P0 + 3 * (1-t) ^ 2 * t * P1 + 3 * (1-t) * t ^ 2 * P2 + t ^ 3 * P3
						Pt = [round Pt.x,round Pt.y]
						local newRad = spThickness.value
						drawEdge Pt0 Pt lineColor newRad
						Pt0 = Pt
					)
				)
				if not cbHideKnots.state then (
					drawEdge knotsArr[k][1] knotsArr[k][2] knotVecColor 0.3
					drawEdge knotsArr[k][2] knotsArr[k][3] knotVecColor 0.3
				)
			)
		)
		
		drawKnots()
		  
		bmGraph.bitmap = newMap
	) -- end updateGraph fn
	
	fn mPressed pos =
	(
		mousePressed = true
		if rbMode.state == 1 then (
			local newKnot = #()
			append newKnot (pos - bmGraph.pos - [10,0])
			append newKnot (pos - bmGraph.pos)
			append newKnot (pos - bmGraph.pos + [10,0])
			append knotsArr newKnot
			updateGraph()
		) else (
			if knotsArr.count > 0 then (
				local lastDist = dim * 2
				for k = 1 to knotsArr.count do (
					for i = 1 to 3 do (
						local newDist = distance (pos - bmGraph.pos) knotsArr[k][i]
						if newDist < lastDist then (
							lastDist = newDist
							lastKnotInd = [k,i]
						)
					)
				)
			)
		)
	)
	
	fn updateJS pos =
	(
		if mousePressed do (
			jsPos = pos - bmGraph.pos
			
			if rbMode.state == 1 then (
				knotsArr[knotsArr.count][2] = jsPos
				knotsArr[knotsArr.count][1] = jsPos - [10,0]
				knotsArr[knotsArr.count][3] = jsPos + [10,0]
			) else (
				if lastKnotInd.y == 2 then (
					local oldPos = knotsArr[lastKnotInd.x][lastKnotInd.y]
					knotsArr[lastKnotInd.x][1] += jsPos - oldPos
					knotsArr[lastKnotInd.x][3] += jsPos - oldPos
				) else (
					if rbBezierMode.state == 1 then (
						local otherHandleIndex = 4 - lastKnotInd.y
						local otherHandleDist = distance knotsArr[lastKnotInd.x][2] knotsArr[lastKnotInd.x][otherHandleIndex]
						local dir = normalize (jsPos - knotsArr[lastKnotInd.x][2])
						local otherHandlePos = knotsArr[lastKnotInd.x][2] - dir * otherHandleDist
						knotsArr[lastKnotInd.x][otherHandleIndex] = [round otherHandlePos.x,round otherHandlePos.y]
					)
				)
				knotsArr[lastKnotInd.x][lastKnotInd.y] = jsPos
			)
			updateGraph()
		)
	)
	
	fn clearVP =
	(
		knotsArr = #()
		updateGraph()
		rbMode.state = 1
	)
	
	fn openDialog =
	(
		createDialog bitmapTest width:(dim + 10) style:#(#style_titlebar, #style_border, #style_sysmenu,#style_resizing)
	)
	  
	fn init =
	(
		createBGMap()
		updateGraph()
	)
  
	fn done =
	(
		-- cleanup code
		gc light:true
	)
  
-- Event Handlers
------------------------------------------
	  
	on bitmapTest lbuttondown pos do mPressed pos
	on bitmapTest lbuttonup pos do (
		mousePressed = false
		updateGraph()
	)
	on bitmapTest mousemove pos do updateJS pos
	
	on bnClear pressed do clearVP()
	on spThickness changed val do updateGraph()
	on spPower changed val do updateGraph()
	on cbHideKnots changed state do updateGraph()
	on spSteps changed val do updateGraph()
	
	on bitmapTest open do init()
	on bitmapTest close do done()
  
) -- end of rollout
  
bitmapTest.openDialog()

An improved version of the ‘Draw Dot’ function.
Using this technique I was able to set the spline thickness as well.

Page 3 / 4