Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Beziérs

Just figured this out today. To draw a bezier, call cubicBezier. The first and last arguments are start and end points of the curve, while middle arguments are control points.
Unfortunately, I don't understand derivatives and cannot make smooth meshes. Reducing the alpha of curveColor will demonstrate the messy issue I have encountered by using lines with round line caps.


--# Main -- Bézier Curves function setup() -- Tests tests = {Test1(),Test2()} updateTest(1) parameter.watch("Current_Test_Name") parameter.integer("Test_Number",1,#tests,1,updateTest) -- Colors parameter.color("curveColor",color(242, 32, 32, 255)) touchAreaColor = color(255,60) touchAreaColorOnTouch = color(255,100) end function draw() -- This sets a dark background color background(40, 40, 50) currentTest:draw() end function touched(touch) currentTest:touched(touch) end function updateTest(n) currentTest = tests[n] _G["Current_Test_Name"] = currentTest.name end --# Test1 Test1 = class() -- Test 1: Quadratic Béziers function Test1:init() -- Test name self.name = "Quadratic Béziers" -- Points self.p0 = vec2( 100, 100 ) self.p1 = vec2( WIDTH / 2, 300 ) self.p2 = vec2( WIDTH - 100, 100) -- Dragging handles self.h0 = handle(self.p0) self.h1 = handle(self.p1) self.h2 = handle(self.p2) end function Test1:draw() -- Draw curve fill(curveColor) stroke(curveColor) strokeWidth(10) quadBezier(self.p0,self.p1,self.p2) -- Draw touch areas noStroke() self.h0:draw() self.h1:draw() self.h2:draw() end function Test1:touched(touch) if self.h0:touched(touch) then self.h0:move("p0",self) return end if self.h1:touched(touch) then self.h1:move("p1",self) return end if self.h2:touched(touch) then self.h2:move("p2",self) return end end --# Test2 Test2 = class() -- Test 2: Cubic Béziers function Test2:init() -- Test name self.name = "Cubic Béziers" -- Points self.p0 = vec2( 100, 100 ) self.p1 = vec2( WIDTH * 1/3, 300 ) self.p2 = vec2( WIDTH * 2/3, 300 ) self.p3 = vec2( WIDTH - 100, 100 ) -- Dragging handles self.h0 = handle(self.p0) self.h1 = handle(self.p1) self.h2 = handle(self.p2) self.h3 = handle(self.p3) end function Test2:draw() -- Draw curve fill(curveColor) stroke(curveColor) strokeWidth(10) cubicBezier(self.p0,self.p1,self.p2,self.p3) -- Draw touch areas noStroke() self.h0:draw() self.h1:draw() self.h2:draw() self.h3:draw() end function Test2:touched(touch) if self.h0:touched(touch) then self.h0:move("p0",self) return end if self.h1:touched(touch) then self.h1:move("p1",self) return end if self.h2:touched(touch) then self.h2:move("p2",self) return end if self.h3:touched(touch) then self.h3:move("p3",self) return end end --# cubicCurves function cubicCurveAt(t,p0,p1,p2,p3) local vec = (1-t)*(1-t)*(1-t)*p0 + 3*(1-t)*(1-t)*t*p1 + 3*(1-t)*t*t*p2 + t*t*t*p3 return vec end function cubicBezier(p0,p1,p2,p3) local len = squareDist(p0,p1)+squareDist(p1,p2)+squareDist(p2,p3) -- get approximate length local divisions = math.floor(math.sqrt(len+6400)/16) -- calculate hyperbola local increment = 1/divisions for i = 1, divisions do local t = increment * i local c = cubicCurveAt(t,p0,p1,p2,p3) local n = cubicCurveAt(t-increment,p0,p1,p2,p3) line(c.x,c.y,n.x,n.y) end end --# quadCurves function quadCurveAt(t,p0,p1,p2) local vec = (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2 return vec end function quadBezier(p0,p1,p2) local len = squareDist(p0,p1)+squareDist(p1,p2) -- get approximate length local divisions = math.floor(math.sqrt(len+6400)/16) -- calculate hyperbola local increment = 1/divisions for i = 1, divisions do local t = increment * i local c = quadCurveAt(t,p0,p1,p2) local n = quadCurveAt(t-increment,p0,p1,p2) line(c.x,c.y,n.x,n.y) end end --# Misc function squareDist(a,b)local c=a.x-b.x;local d=a.y-b.y;return c*c+d*d end; handle=class()function handle:init(e)self.p=e;self.state="initial"self.handler=function()end;self.handlerArgs={}end function handle:draw()if self.state=="touching"or self.state=="dragging"then fill(touchAreaColorOnTouch)else fill(touchAreaColor)end;ellipse(self.p.x,self.p.y,60)ellipse(self.p.x,self.p.y,30)end function handle:touched(f)if f.state==ENDED or f.state==CANCELLED then self.state="initial"end;if squareDist(f,self.p)<225 then if f.state==BEGAN and self.state=="initial"then self.state="touching"return true end end;if f.state==MOVING and self.state=="touching"or self.state=="dragging"then self.state="dragging"self.p=vec2(f.x,f.y)return true end end;function handle:move(e,g)(g or _G)[e]=self.p end
Tagged:

Comments

  • @em2 Excellent! Beziér curves have been on my to do list for a long time, so thanks for this!

  • Posts: 505

    Whenever lines have transparency and intersect (regardless of the caps mode), you will see an overlay effect, which is normal. If you want to avoid this, you can draw all lines into an image() texture and draw that image at certain opacity.

  • dave1707dave1707 Mod
    Posts: 8,573

    I reduced the alpha and I like the effect that shows more than a solid line.

  • AnatolyAnatoly Mod
    edited September 2017 Posts: 889

    Good job.

  • em2em2
    edited September 2017 Posts: 194

    @se24vad the best solution would be to use a mesh (not with addRect) and manually create vertices by calculating normals at intersections. I only wish I knew how to calculate them, but calculus (which derives normals and tangents) is beyond me.
    For now, your suggestion will work, but I will probably have to create a Bézier class.

  • I did a mesh/shader version of Béziers a while ago. You can find it on github. It implements them both as a drawing command and as a class.

Sign In or Register to comment.