Hi guys,
How to make Bezier curve circle shaped with radius R?
This is my current code downloaded from https://gist.github.com/dantes2023/bcedb9b0ffd80d3682ba#file-main-lua-L31
I’m looking for not hardcoded bezier curve.
Thanks 
Hi guys,
How to make Bezier curve circle shaped with radius R?
This is my current code downloaded from https://gist.github.com/dantes2023/bcedb9b0ffd80d3682ba#file-main-lua-L31
I’m looking for not hardcoded bezier curve.
Thanks 
Did you look into this tutorial?
https://coronalabs.com/blog/2014/09/09/tutorial-working-with-curved-paths/
If you’re willing to stick with cubic curves (at least for the circle case), you might try splitting the circle up into four arcs at the points (R, 0), (0, R), (-R, 0), and (0, -R), then join them using Hermite curves (see about 3/4 of the way down this page). The tangents at those points would be (0, -1), (-1, 0), (0, 1), and (1, 0) respectively. (Or their negative counterparts.)
I have some code to convert from Hermite to Bezier form here. That would be called as
local cubic = require("cubic") -- stuff... cubic.HermiteToBezier(Point1, Point2, Tangent1, Tangent2, P1, Q1, Q2, P2)
where the last four would be your outputs, P1 and P2 being the endpoints on the Bezier curve, Q1 and Q2 the control points. (All of these are tables with x and y components.)
Is there any more solutions? It’s very complicated and I didn’t succeed.
Heh, I suppose that was a bit vague.
Anyhow, I puttered around with it and turns out what I suggested would give you a diamond.
Though you can normalize it, which tends to work fine.
Code, if you’re interested:
-- Tangent scale factor -- local Div = 1 / 3 --- Converts coefficients from Hermite (P1, P2, T1, T2) to Bézier (P1, Q1, Q2, P2) form. -- @tparam Vector src1 Vector #1 (i.e. P1)... -- @tparam Vector src2 ...#2 (P2)... -- @tparam Vector src3 ...#3 (T1)... -- @tparam Vector src4 ...and #4 (T2). -- @tparam[opt=src1] Vector dst1 Target vector #1 (i.e. will receive P1)... -- @tparam[opt=src2] Vector dst2 ...#2 (Q1)... -- @tparam[opt=src3] Vector dst3 ...#3 (Q2)... -- @tparam[opt=src4] Vector dst4 ...and #4 (P2). local function HermiteToBezier (src1, src2, src3, src4, dst1, dst2, dst3, dst4) dst1, dst2, dst3, dst4 = dst1 or src1, dst2 or src2, dst3 or src3, dst4 or src4 local q1x, q1y = src1.x + src3.x \* Div, src1.y + src3.y \* Div local q2x, q2y = src2.x - src4.x \* Div, src2.y - src4.y \* Div dst1.x, dst1.y = src1.x, src1.y dst4.x, dst4.y = src2.x, src2.y dst2.x, dst2.y = q1x, q1y dst3.x, dst3.y = q2x, q2y end -- Center of path -- local CX, CY = display.contentCenterX, display.contentCenterY -- Radius of path -- local R = .4 \* display.contentWidth -- Our object -- local Thing = display.newCircle(0, 0, 25) Thing:setFillColor(1, 0, 0) Thing.strokeWidth = 2 -- Our points -- local P1 = { x = CX + R, y = CY } local P2 = { x = CX, y = CY - R } local P3 = { x = CX - R, y = CY } local P4 = { x = CX, y = CY + R } -- Our tangents, for Hermite curves -- local T1 = { x = 0, y = -1 } local T2 = { x = -1, y = 0 } local T3 = { x = 0, y = 1 } local T4 = { x = 1, y = 0 } -- Our control points, for Bezier curves -- local A1, A2 = {}, {} local B1, B2 = {}, {} local C1, C2 = {}, {} local D1, D2 = {}, {} -- Convert! (note: endpoints should stay the same) HermiteToBezier(P1, P2, T1, T2, P1, A1, A2, P2) -- 0 to 90 degrees... HermiteToBezier(P2, P3, T2, T3, P2, B1, B2, P3) -- ...90 to 180... HermiteToBezier(P3, P4, T3, T4, P3, C1, C2, P4) -- ...180 to 270... HermiteToBezier(P4, P1, T4, T1, P4, D1, D2, P1) -- ...270 to 360 -- Evaluate the (cubic) Bezier curve at a time local function Bezier (a, b, c, d, t) local t2, t3 = t^2, t^3 local ka = .5 \* (-t + 2 \* t2 - t3) local kb = .5 \* (2 - 5 \* t2 + 3 \* t3) local kc = .5 \* (t + 4 \* t2 - 3 \* t3) local kd = .5 \* (-t2 + t3) local x = ka \* a.x + kb \* b.x + kc \* c.x + kd \* d.x local y = ka \* a.y + kb \* b.y + kc \* c.y + kd \* d.y return x, y end -- Put an object at a time along a curve local function PutAt (object, t) local scaled = (4 \* t) % 4 -- spread time across four arcs, looping at 360 degrees t = scaled % 1 -- time component local arc = (scaled - t) + 1 -- which arc are we in? local p1, p2, c1, c2 if arc == 1 then -- 0 to 90 p1, p2, c1, c2 = P1, P2, A1, A2 elseif arc == 2 then -- 90 to 180 p1, p2, c1, c2 = P2, P3, B1, B2 elseif arc == 3 then -- 180 to 270 p1, p2, c1, c2 = P3, P4, C1, C2 else -- 270 to 360 p1, p2, c1, c2 = P4, P1, D1, D2 end object.x, object.y = Bezier(p1, c1, c2, p2, t) end -- Set start position. PutAt(Thing, 0) -- Make a trail for a while to show path. local Trail = 0 -- Draw option: set false to use diamond -- local WantCircle = true Runtime:addEventListener("enterFrame", function(event) PutAt(Thing, event.time / 15000) -- Normalize the displacement from the center to follow a circle. if WantCircle then local dx, dy = Thing.x - CX, Thing.y - CY local scale = R / math.sqrt(dx^2 + dy^2) Thing.x, Thing.y = CX + scale \* dx, CY + scale \* dy end -- Leave a trail for X number of frames. if Trail \< 200 then local circ = display.newCircle(Thing.x, Thing.y, 5) circ:setFillColor(0, 0, 1) circ:toBack() Trail = Trail + 1 end end)
As for other solutions, what are you trying to do? There are certainly easier ways to traverse a circle, if you don’t need the curve.
This is worth a read too: Bezier circle
@StarCrunch thank you, this solutions is the closest I want.
I’m trying to have two dash(one above another) which is always rotating for 360 and -360 with specific speed.
And I trying to have a hollow circle that contains a small circle(which is on for example on (0,1) position) and also have constant rotating but small circle need to be fixed on hollow circle. -----Currently, I achieved this with physics newJoint, but I’m on searching solutions without physics.
On the second point, if you mean what I think (internally tangent circles, as for instance shown here, in particular the lower-left case), you can simply do the update like so:
Runtime:addEventListener("enterFrame", function(event) PutAt(Thing, event.time / 15000) -- Get unit radial vector. local dx, dy = Thing.x - CX, Thing.y - CY local len = math.sqrt(dx^2 + dy^2) dx, dy = dx / len, dy / len -- Normalize the displacement from the center to follow a circle. if WantCircle then local scale = R - Radius2 -- push the smaller circle away from the larger one Thing.x, Thing.y = CX + scale \* dx, CY + scale \* dy end -- Leave a trail for X number of frames. if Trail \< 200 then local circ = display.newCircle(CX + R \* dx, CY + R \* dy, 5) circ:setFillColor(0, 0, 1) circ:toBack() Trail = Trail + 1 end end)
and adjust the initialization slightly:
-- Our object -- local Radius2 = 25 local Thing = display.newCircle(0, 0, Radius2) -- same radius we use later
You can see that the circle hugs the traced part of the path.
Did you look into this tutorial?
https://coronalabs.com/blog/2014/09/09/tutorial-working-with-curved-paths/
If you’re willing to stick with cubic curves (at least for the circle case), you might try splitting the circle up into four arcs at the points (R, 0), (0, R), (-R, 0), and (0, -R), then join them using Hermite curves (see about 3/4 of the way down this page). The tangents at those points would be (0, -1), (-1, 0), (0, 1), and (1, 0) respectively. (Or their negative counterparts.)
I have some code to convert from Hermite to Bezier form here. That would be called as
local cubic = require("cubic") -- stuff... cubic.HermiteToBezier(Point1, Point2, Tangent1, Tangent2, P1, Q1, Q2, P2)
where the last four would be your outputs, P1 and P2 being the endpoints on the Bezier curve, Q1 and Q2 the control points. (All of these are tables with x and y components.)
Is there any more solutions? It’s very complicated and I didn’t succeed.
Heh, I suppose that was a bit vague.
Anyhow, I puttered around with it and turns out what I suggested would give you a diamond.
Though you can normalize it, which tends to work fine.
Code, if you’re interested:
-- Tangent scale factor -- local Div = 1 / 3 --- Converts coefficients from Hermite (P1, P2, T1, T2) to Bézier (P1, Q1, Q2, P2) form. -- @tparam Vector src1 Vector #1 (i.e. P1)... -- @tparam Vector src2 ...#2 (P2)... -- @tparam Vector src3 ...#3 (T1)... -- @tparam Vector src4 ...and #4 (T2). -- @tparam[opt=src1] Vector dst1 Target vector #1 (i.e. will receive P1)... -- @tparam[opt=src2] Vector dst2 ...#2 (Q1)... -- @tparam[opt=src3] Vector dst3 ...#3 (Q2)... -- @tparam[opt=src4] Vector dst4 ...and #4 (P2). local function HermiteToBezier (src1, src2, src3, src4, dst1, dst2, dst3, dst4) dst1, dst2, dst3, dst4 = dst1 or src1, dst2 or src2, dst3 or src3, dst4 or src4 local q1x, q1y = src1.x + src3.x \* Div, src1.y + src3.y \* Div local q2x, q2y = src2.x - src4.x \* Div, src2.y - src4.y \* Div dst1.x, dst1.y = src1.x, src1.y dst4.x, dst4.y = src2.x, src2.y dst2.x, dst2.y = q1x, q1y dst3.x, dst3.y = q2x, q2y end -- Center of path -- local CX, CY = display.contentCenterX, display.contentCenterY -- Radius of path -- local R = .4 \* display.contentWidth -- Our object -- local Thing = display.newCircle(0, 0, 25) Thing:setFillColor(1, 0, 0) Thing.strokeWidth = 2 -- Our points -- local P1 = { x = CX + R, y = CY } local P2 = { x = CX, y = CY - R } local P3 = { x = CX - R, y = CY } local P4 = { x = CX, y = CY + R } -- Our tangents, for Hermite curves -- local T1 = { x = 0, y = -1 } local T2 = { x = -1, y = 0 } local T3 = { x = 0, y = 1 } local T4 = { x = 1, y = 0 } -- Our control points, for Bezier curves -- local A1, A2 = {}, {} local B1, B2 = {}, {} local C1, C2 = {}, {} local D1, D2 = {}, {} -- Convert! (note: endpoints should stay the same) HermiteToBezier(P1, P2, T1, T2, P1, A1, A2, P2) -- 0 to 90 degrees... HermiteToBezier(P2, P3, T2, T3, P2, B1, B2, P3) -- ...90 to 180... HermiteToBezier(P3, P4, T3, T4, P3, C1, C2, P4) -- ...180 to 270... HermiteToBezier(P4, P1, T4, T1, P4, D1, D2, P1) -- ...270 to 360 -- Evaluate the (cubic) Bezier curve at a time local function Bezier (a, b, c, d, t) local t2, t3 = t^2, t^3 local ka = .5 \* (-t + 2 \* t2 - t3) local kb = .5 \* (2 - 5 \* t2 + 3 \* t3) local kc = .5 \* (t + 4 \* t2 - 3 \* t3) local kd = .5 \* (-t2 + t3) local x = ka \* a.x + kb \* b.x + kc \* c.x + kd \* d.x local y = ka \* a.y + kb \* b.y + kc \* c.y + kd \* d.y return x, y end -- Put an object at a time along a curve local function PutAt (object, t) local scaled = (4 \* t) % 4 -- spread time across four arcs, looping at 360 degrees t = scaled % 1 -- time component local arc = (scaled - t) + 1 -- which arc are we in? local p1, p2, c1, c2 if arc == 1 then -- 0 to 90 p1, p2, c1, c2 = P1, P2, A1, A2 elseif arc == 2 then -- 90 to 180 p1, p2, c1, c2 = P2, P3, B1, B2 elseif arc == 3 then -- 180 to 270 p1, p2, c1, c2 = P3, P4, C1, C2 else -- 270 to 360 p1, p2, c1, c2 = P4, P1, D1, D2 end object.x, object.y = Bezier(p1, c1, c2, p2, t) end -- Set start position. PutAt(Thing, 0) -- Make a trail for a while to show path. local Trail = 0 -- Draw option: set false to use diamond -- local WantCircle = true Runtime:addEventListener("enterFrame", function(event) PutAt(Thing, event.time / 15000) -- Normalize the displacement from the center to follow a circle. if WantCircle then local dx, dy = Thing.x - CX, Thing.y - CY local scale = R / math.sqrt(dx^2 + dy^2) Thing.x, Thing.y = CX + scale \* dx, CY + scale \* dy end -- Leave a trail for X number of frames. if Trail \< 200 then local circ = display.newCircle(Thing.x, Thing.y, 5) circ:setFillColor(0, 0, 1) circ:toBack() Trail = Trail + 1 end end)
As for other solutions, what are you trying to do? There are certainly easier ways to traverse a circle, if you don’t need the curve.
This is worth a read too: Bezier circle
@StarCrunch thank you, this solutions is the closest I want.
I’m trying to have two dash(one above another) which is always rotating for 360 and -360 with specific speed.
And I trying to have a hollow circle that contains a small circle(which is on for example on (0,1) position) and also have constant rotating but small circle need to be fixed on hollow circle. -----Currently, I achieved this with physics newJoint, but I’m on searching solutions without physics.
On the second point, if you mean what I think (internally tangent circles, as for instance shown here, in particular the lower-left case), you can simply do the update like so:
Runtime:addEventListener("enterFrame", function(event) PutAt(Thing, event.time / 15000) -- Get unit radial vector. local dx, dy = Thing.x - CX, Thing.y - CY local len = math.sqrt(dx^2 + dy^2) dx, dy = dx / len, dy / len -- Normalize the displacement from the center to follow a circle. if WantCircle then local scale = R - Radius2 -- push the smaller circle away from the larger one Thing.x, Thing.y = CX + scale \* dx, CY + scale \* dy end -- Leave a trail for X number of frames. if Trail \< 200 then local circ = display.newCircle(CX + R \* dx, CY + R \* dy, 5) circ:setFillColor(0, 0, 1) circ:toBack() Trail = Trail + 1 end end)
and adjust the initialization slightly:
-- Our object -- local Radius2 = 25 local Thing = display.newCircle(0, 0, Radius2) -- same radius we use later
You can see that the circle hugs the traced part of the path.