just found this demo code to using parametric equation draw in iOS
you can see that key code is to generate this array of points
let points: [CGPoint] = 0.stride(to: M_PI * 2, by: 0.01).map
{
let x = pow(sin($0), 3)
var y = 13 * cos($0)
y -= 5 * cos(2 * $0)
y -= 2 * cos(3 * $0)
y -= cos(4 * $0)
y /= 16
return CGPoint(x: 320 + (x * 300), y: 280 + (y * -300))
}
which is just using this equation
now what I want to draw is more complex one mao curve
but the problem I encounter is there is a mathematic sign in the parametric equation provided by the site that I don't know how to convert to iOS code, this one
UPDATE: now I encounter a new problem: "Expression was too complex to be solved in reasonable time", any idea besides break it down into small expression like the heart curve did in its y above, because manually break it down it's too overwhelming
and the whole code is list below
func sgn(t: Double) -> Double{
switch t {
case _ where t < 0:
return -1.0
case _ where t > 0:
return 1.0
default:
return 0.0
}
}
func theta(t: Double) -> Double {
switch t {
case _ where t < 0:
return 0.0
case _ where t > 0:
return 1.0
default:
return 0.5
}
}
let pi = M_PI
let layer = CAShapeLayer()
layer.lineCap = kCALineCapRound
layer.lineJoin = kCALineCapRound
self.view.layer.addSublayer(layer)
let points: [CGPoint] = 0.stride(to: M_PI * 2, by: 0.01).map
{ t in
var x = ((-10/47*sin(30/41-17*t)-11/4*sin(83/58-15*t)-47/17*sin(79/53-13*t)+21080/33*sin(t+63/40)+179/32*sin(2*t+28/17)+263/25*sin(3*t+85/54)+1552/47*sin(4*t+146/31)+2015/77*sin(5*t+30/19)+286/31*sin(6*t+117/25)+1308/17*sin(7*t+67/42)+158/37*sin(8*t+77/17)+1017/26*sin(9*t+61/38)+107/18*sin(10*t+79/46)+863/39*sin(11*t+29/18)+74/23*sin(12*t+12/7)+110/63*sin(14*t+163/35)+29/31*sin(16*t+75/41)+2/7*sin(18*t+64/25)+305/37*sin(19*t+18/11)+9/34*sin(20*t+91/32)+316/45*sin(21*t+64/39)+16/15*sin(22*t+119/27)+179/35*sin(23*t+23/14)+38/51*sin(24*t+131/65)+1969/19)*theta(103*pi-t)*theta(t-99*pi)+(-44/31*sin(81/52-6*t)-47/19*sin(14/9-5*t)-206/75*sin(39/25-4*t)-77/16*sin(69/44-3*t)-416/25*sin(25/16-2*t)-1496/19*sin(47/30-t)-3315/19)*theta(99*pi-t)*theta(t-95*pi)+(732/7*sin(t+41/26)+111/17*sin(2*t+18/11)+187/20*sin(3*t+43/27)+37/24*sin(4*t+44/27)+157/45*sin(5*t+59/37)+5507/30)*theta(95*pi-t)*theta(t-91*pi)+(-19/42*sin(37/24-2*t)+3067/18*sin(t+11/7)+281/16*sin(3*t+96/61)+13/27*sin(4*t+159/34)+215/38*sin(5*t+30/19)+1121/34)*theta(91*pi-t)*theta(t-87*pi)+(-1/20*sin(17/14-16*t)+3995/36*sin(t+11/7)+353/25*sin(2*t+69/44)+209/18*sin(3*t+11/7)+349/66*sin(4*t+49/31)+67/17*sin(5*t+43/27)+84/43*sin(6*t+60/37)+79/30*sin(7*t+149/93)+93/38*sin(8*t+30/19)+37/22*sin(9*t+74/47)+91/57*sin(10*t+68/43)+43/29*sin(11*t+43/27)+42/23*sin(12*t+45/28)+28/31*sin(13*t+77/47)+17/36*sin(14*t+41/25)+17/86*sin(15*t+8/5)+12/29*sin(17*t+65/42)+11/31*sin(18*t+49/31)+31/47*sin(19*t+45/28)+13/31*sin(20*t+33/20)+5/43*sin(21*t+168/37)+9/46*sin(22*t+109/68)+26/29*sin(23*t+41/26)+31/47*sin(24*t+43/27)+5/21*sin(25*t+50/31)+2855/14)*theta(87*pi-t)*theta(t-83*pi)+(-5/33*sin(32/21-24*t)-16/17*sin(20/13-23*t)-64/39*sin(80/51-17*t)-1/28*sin(14/9-15*t)+3240/31*sin(t+11/7)+341/25*sin(2*t+52/33)+888/71*sin(3*t+63/40)+286/191*sin(4*t+31/19)+170/29*sin(5*t+68/43)+18/29*sin(6*t+80/17)+67/23*sin(7*t+91/58)+2/33*sin(8*t+36/31)+19/36*sin(9*t+51/32)+23/28*sin(10*t+21/13)+39/34*sin(11*t+65/41)+3/26*sin(12*t+39/20)+47/43*sin(13*t+37/23)+1/117*sin(14*t+825/206)+58/35*sin(16*t+47/30)+34/19*sin(18*t+62/39)+27/29*sin(19*t+127/27)+3/28*sin(20*t+245/52)+54/37*sin(21*t+53/33)+23/31*sin(22*t+21/13)-6998/27)*theta(83*pi-t)*theta(t-79*pi)+(2069/16*sin(t+11/7)+82/21*sin(2*t+14/3)+443/29*sin(3*t+19/12)+79/30*sin(4*t+30/19)+148/29*sin(5*t+19/12)+27/25*sin(6*t+33/20)+73/31*sin(7*t+27/17)+8893/37)*theta(79*pi-t)*theta(t-75*pi)+(-11/32*sin(31/22-2*t)+4451/49*sin(t+11/7)+515/68*sin(3*t+102/65)+1/20*sin(4*t+9/13)+79/30*sin(5*t+47/30)+17/37*sin(6*t+11/7)+47/30*sin(7*t+107/68)-10305/46)*theta(75*pi-t)*theta(t-71*pi)+(-5/24*sin(74/59-26*t)-22/25*sin(16/11-23*t)-5/29*sin(29/25-20*t)-62/41*sin(46/31-17*t)-48/25*sin(20/13-15*t)-113/24*sin(38/25-12*t)-231/40*sin(39/25-9*t)-89/39*sin(47/32-7*t)-104/19*sin(48/31-5*t)-182/15*sin(37/24-4*t)-140/19*sin(31/20-2*t)+3287/23*sin(t+107/68)+564/23*sin(3*t+113/24)+8/3*sin(6*t+13/8)+241/30*sin(8*t+37/23)+71/30*sin(10*t+14/9)+17/12*sin(11*t+44/27)+16/25*sin(13*t+67/39)+10/11*sin(14*t+81/52)+3*sin(16*t+21/13)+17/14*sin(18*t+127/27)+56/39*sin(19*t+117/73)+41/44*sin(21*t+159/34)+54/65*sin(22*t+27/17)+17/35*sin(24*t+159/34)+5/27*sin(25*t+44/29)-17/10)*theta(71*pi-t)*theta(t-67*pi)+(1987/31*sin(t+11/7)+305/28*sin(2*t+80/17)+751/28*sin(3*t+11/7)+191/30*sin(4*t+30/19)+313/33*sin(5*t+63/40)+122/73*sin(6*t+127/27)+113/34*sin(7*t+41/26)+23/30*sin(8*t+11/7)+73/27*sin(9*t+68/43)+3943/17)*theta(67*pi-t)*theta(t-63*pi)+(1593/29*sin(t+179/38)+191/29*sin(2*t+127/27)+678/97*sin(3*t+113/24)+44/19*sin(4*t+169/36)+63/34*sin(5*t+108/23)+17/16*sin(6*t+75/16)-6319/33)*theta(63*pi-t)*theta(t-59*pi)+(-9/29*sin(32/51-88*t)-39/29*sin(64/41-85*t)-19/27*sin(114/115-26*t)-30/43*sin(45/31-21*t)-14/11*sin(42/29-15*t)-13/38*sin(20/33-9*t)+15919/28*sin(t+39/25)+5859/34*sin(2*t+61/13)+1289/20*sin(3*t+229/49)+899/26*sin(4*t+26/17)+315/16*sin(5*t+145/31)+707/104*sin(6*t+26/17)+71/20*sin(7*t+3/2)+142/17*sin(8*t+188/41)+182/31*sin(10*t+28/19)+69/43*sin(11*t+356/79)+84/19*sin(12*t+55/39)+376/51*sin(13*t+87/62)+89/13*sin(14*t+67/46)+152/27*sin(16*t+69/47)+73/20*sin(17*t+57/43)+13/9*sin(18*t+3/2)+5/34*sin(19*t+70/41)+19/9*sin(20*t+47/36)+143/43*sin(22*t+43/33)+191/30*sin(23*t+31/24)+107/23*sin(24*t+35/27)+78/25*sin(25*t+32/25)+139/33*sin(27*t+43/33)+155/37*sin(28*t+43/33)+375/47*sin(29*t+141/106)+16/25*sin(30*t+145/38)+265/18*sin(31*t+232/53)+849/29*sin(32*t+61/14)+451/18*sin(33*t+204/47)+18/13*sin(34*t+327/77)+453/19*sin(35*t+6/5)+821/39*sin(36*t+43/37)+371/48*sin(37*t+37/32)+139/29*sin(38*t+107/25)+67/12*sin(39*t+343/78)+1033/100*sin(40*t+38/33)+435/59*sin(41*t+46/45)+327/43*sin(42*t+37/32)+264/53*sin(43*t+55/13)+19/35*sin(44*t+51/77)+771/71*sin(45*t+35/32)+85/24*sin(46*t+11/12)+30/23*sin(47*t+67/40)+139/22*sin(48*t+109/26)+170/41*sin(49*t+46/41)+195/32*sin(50*t+46/51)+154/71*sin(51*t+41/34)+63/17*sin(52*t+54/13)+42/25*sin(53*t+91/24)+185/77*sin(54*t+17/30)+773/103*sin(55*t+16/15)+68/11*sin(56*t+73/17)+113/24*sin(57*t+48/35)+351/53*sin(58*t+92/21)+643/60*sin(59*t+65/59)+153/23*sin(60*t+193/45)+62/25*sin(61*t+66/47)+37/33*sin(62*t+31/8)+79/20*sin(63*t+153/35)+213/25*sin(64*t+25/23)+721/68*sin(65*t+59/14)+237/28*sin(66*t+33/29)+20/7*sin(67*t+131/28)+242/41*sin(68*t+31/32)+57/86*sin(69*t+1/21)+86/21*sin(70*t+147/38)+203/102*sin(71*t+12/17)+159/47*sin(72*t+29/35)+23/16*sin(73*t+400/87)+28/25*sin(74*t+87/34)+377/113*sin(75*t+177/43)+173/61*sin(76*t+29/31)+25/21*sin(77*t+7/17)+69/19*sin(78*t+94/25)+34/33*sin(79*t+19/5)+9/28*sin(80*t+199/83)+140/93*sin(81*t+178/39)+339/127*sin(82*t+61/48)+107/20*sin(83*t+125/31)+81/20*sin(84*t+29/32)+27/26*sin(86*t+47/14)+9/25*sin(87*t+15/19)+149/75*sin(89*t+45/89)+29/21*sin(90*t+151/44)+75/26*sin(91*t+251/66)+50/21*sin(92*t+23/32)+62/55*sin(93*t+8/63)+23/11*sin(94*t+55/16)+51/26*sin(95*t+257/72)+38/25*sin(96*t+227/57)+314/209*sin(97*t+15/37)+11/9*sin(98*t+416/119)+11584/59)*theta(59*pi-t)*theta(t-55*pi)+(-43/31*sin(53/34-18*t)-58/35*sin(61/39-17*t)-55/19*sin(36/23-16*t)-155/59*sin(61/39-15*t)-45/19*sin(41/27-9*t)-46/13*sin(95/61-8*t)-360/23*sin(58/37-7*t)-77/15*sin(53/34-4*t)+27845/41*sin(t+91/58)+259/26*sin(2*t+212/45)+7843/86*sin(3*t+146/31)+2557/50*sin(5*t+146/31)+3931/786*sin(6*t+271/58)+31/23*sin(10*t+174/37)+79/24*sin(11*t+75/16)+86/47*sin(12*t+135/29)+97/24*sin(13*t+174/37)+76/29*sin(14*t+136/29)+30867/253)*theta(55*pi-t)*theta(t-51*pi)+(-5/31*sin(1/3-12*t)-23/52*sin(119/99-11*t)-166/33*sin(17/12-4*t)-196/41*sin(123/88-3*t)+327/34*sin(t+155/58)+297/40*sin(2*t+35/17)+35/44*sin(5*t+142/61)+3/5*sin(6*t+5/19)+11/19*sin(7*t+19/25)+23/58*sin(8*t+67/31)+12/31*sin(9*t+62/45)+5/12*sin(10*t+57/32)+27/13)*theta(51*pi-t)*theta(t-47*pi)+(2957/51*sin(t+7/22)+296/41)*theta(47*pi-t)*theta(t-43*pi)+(-1/2*sin(46/37-12*t)-233/35*sin(58/41-6*t)-491/47*sin(12/13-4*t)-227/12*sin(26/29-2*t)+7589/21*sin(t+23/18)+825/43*sin(3*t+19/24)+103/39*sin(5*t+7/12)+29/37*sin(7*t+43/14)+69/20*sin(8*t+130/31)+17/14*sin(9*t+137/56)+16/25*sin(10*t+129/37)+28/41*sin(11*t+71/29)+18979/130)*theta(43*pi-t)*theta(t-39*pi)+(-13/48*sin(31/24-7*t)-7/24*sin(29/25-6*t)-35/18*sin(59/69-2*t)+127/34*sin(t+7/55)+107/42*sin(3*t+94/21)+319/62*sin(4*t+569/122)+59/28*sin(5*t+22/13)+1/16*sin(8*t+139/37)+6/23*sin(9*t+4/27)+5/19*sin(10*t+119/41)+3/25*sin(11*t+44/19)+3/17*sin(12*t+338/89)-1419/7)*theta(39*pi-t)*theta(t-35*pi)+(-15/104*sin(31/38-10*t)-8/27*sin(26/29-8*t)-66/35*sin(4/23-4*t)-102/47*sin(41/36-t)+133/46*sin(2*t+31/13)+219/28*sin(3*t+93/35)+31/28*sin(5*t+182/67)+20/23*sin(6*t+5/38)+39/77*sin(7*t+119/53)+6/17*sin(9*t+95/28)+3/11*sin(11*t+73/24)+1/15*sin(12*t+26/29)+9908/49)*theta(35*pi-t)*theta(t-31*pi)+(-17/36*sin(29/32-3*t)-73/122*sin(53/38-2*t)+537/20*sin(t+5/4)+7/32*sin(4*t+184/41)+13/31*sin(5*t+21/5)+3/43*sin(6*t+9/22)+4507/22)*theta(31*pi-t)*theta(t-27*pi)+(-15/32*sin(40/31-6*t)-2/9*sin(2/7-5*t)-14/17*sin(73/55-4*t)-43/28*sin(50/37-2*t)+727/27*sin(t+20/17)+17/15*sin(3*t+4/35)-1416/7)*theta(27*pi-t)*theta(t-23*pi)+(-17/39*sin(35/33-16*t)-15/13*sin(5/8-14*t)-115/67*sin(52/35-10*t)-115/37*sin(11/45-7*t)-136/43*sin(26/25-6*t)-703/43*sin(41/28-4*t)-506/35*sin(1/114-3*t)-1405/34*sin(34/29-2*t)+413/48*sin(t+149/39)+241/32*sin(5*t+55/46)+195/44*sin(8*t+298/85)+73/27*sin(9*t+79/33)+59/28*sin(11*t+132/35)+13/22*sin(12*t+202/47)+11/20*sin(13*t+55/21)+99/74*sin(15*t+125/28)+5740/9)*theta(23*pi-t)*theta(t-19*pi)+(-23/26*sin(11/54-8*t)-112/29*sin(9/23-5*t)+137/33*sin(t+68/45)+33/19*sin(2*t+234/55)+604/57*sin(3*t+325/324)+541/47*sin(4*t+47/27)+36/19*sin(6*t+143/35)+37/28*sin(7*t+7/4)+26/33*sin(9*t+57/20)+24/49*sin(10*t+15/32)+1/8*sin(11*t+69/16)+17/42*sin(12*t+21/22)+901/40)*theta(19*pi-t)*theta(t-15*pi)+(4831/29*sin(t+46/29)+100/53*sin(2*t+38/29)+471/28*sin(3*t+49/29)+71/31*sin(4*t+95/31)+212/33*sin(5*t+38/25)+35/39*sin(6*t+164/55)+1163/291*sin(7*t+65/38)+14/23*sin(8*t+73/18)+467/36)*theta(15*pi-t)*theta(t-11*pi)+(-1/2*sin(4/25-8*t)-17/23*sin(1/20-6*t)-46/29*sin(1/12-4*t)-29/15*sin(36/37-2*t)+3308/37*sin(t+28/19)+576/67*sin(3*t+46/37)+87/25*sin(5*t+23/21)+78/47*sin(7*t+22/21)+3643/17)*theta(11*pi-t)*theta(t-7*pi)+(-41/20*sin(12/11-5*t)+9878/119*sin(t+76/33)+71/22*sin(2*t+133/29)+267/31*sin(3*t+76/21)+55/32*sin(4*t+3/31)+89/88*sin(6*t+23/24)+40/53*sin(7*t+3/25)+44/87*sin(8*t+34/11)+6/13*sin(9*t+40/27)-7131/31)*theta(7*pi-t)*theta(t-3*pi)+(-24/31*sin(16/17-16*t)-149/41*sin(50/37-9*t)-133/52*sin(23/32-8*t)-303/32*sin(1/734-7*t)+10523/20*sin(t+32/23)+431/20*sin(2*t+31/10)+1144/27*sin(3*t+139/36)+456/47*sin(4*t+81/34)+390/47*sin(5*t+146/81)+158/33*sin(6*t+14/27)+41/35*sin(10*t+43/20)+29/19*sin(11*t+82/33)+64/41*sin(12*t+42/31)+67/43*sin(13*t+47/43)+16/35*sin(14*t+41/20)+49/73*sin(15*t+59/88)+2388/37)*theta(3*pi-t)*theta(t+pi))*theta(sqrt(sgn(sin(t/2))))
var y = ((-407/57*sin(35/24-24*t)-42/31*sin(41/28-23*t)-49/16*sin(67/46-22*t)-26/37*sin(38/31-21*t)-76/35*sin(59/39-20*t)-310/73*sin(43/28-18*t)-11*sin(44/29-13*t)-580/41*sin(32/21-10*t)-3258/65*sin(63/41-9*t)-252/17*sin(56/37-8*t)-6254/51*sin(17/11-6*t)-1481/27*sin(71/46-5*t)-465/32*sin(73/47-3*t)+3651/50*sin(t+30/19)+8318/27*sin(2*t+30/19)+134/21*sin(4*t+127/27)+807/19*sin(7*t+19/12)+252/11*sin(11*t+155/97)+139/32*sin(12*t+49/30)+417/38*sin(14*t+51/31)+149/38*sin(15*t+117/70)+173/24*sin(16*t+13/8)+16/7*sin(17*t+12/7)+62/43*sin(19*t+87/56)-18494/25)*theta(103*pi-t)*theta(t-99*pi)+(-80/29*sin(47/30-5*t)-177/25*sin(113/72-3*t)-1901/33*sin(47/30-t)+599/31*sin(2*t+52/33)+77/41*sin(4*t+14/9)+11/9*sin(6*t+61/39)+2609/12)*theta(99*pi-t)*theta(t-95*pi)+(-1566/241*sin(58/37-3*t)-1857/50*sin(47/30-t)+464/31*sin(2*t+11/7)+59/24*sin(4*t+11/7)+20/17*sin(5*t+136/29)+7997/31)*theta(95*pi-t)*theta(t-91*pi)+(-15/29*sin(67/43-5*t)-129/25*sin(36/23-4*t)-148/61*sin(36/23-3*t)-103/33*sin(69/44-2*t)-29/17*sin(80/51-t)-4753/32)*theta(91*pi-t)*theta(t-87*pi)+(-24/19*sin(32/21-25*t)-229/98*sin(48/31-22*t)-83/31*sin(54/35-19*t)-37/9*sin(31/20-17*t)-46/19*sin(36/23-14*t)-71/23*sin(25/16-12*t)-29/19*sin(57/37-10*t)-49/15*sin(25/16-7*t)-396/113*sin(14/9-5*t)-181/36*sin(36/23-4*t)-176/25*sin(47/30-3*t)-1055/57*sin(80/51-2*t)-353/21*sin(113/72-t)+17/43*sin(6*t+63/40)+1/36*sin(8*t+1/22)+9/34*sin(9*t+50/33)+113/38*sin(11*t+47/30)+5/8*sin(13*t+27/17)+45/38*sin(15*t+174/37)+11/8*sin(16*t+61/39)+188/31*sin(18*t+19/12)+16/27*sin(20*t+61/13)+5/17*sin(21*t+23/13)+39/22*sin(23*t+68/43)+12/13*sin(24*t+25/16)+12966/29)*theta(87*pi-t)*theta(t-83*pi)+(-101/25*sin(23/15-23*t)-223/46*sin(25/16-19*t)-603/116*sin(80/51-17*t)-45/58*sin(14/9-15*t)-77/41*sin(65/42-11*t)-1/36*sin(59/98-10*t)-108/25*sin(47/30-9*t)-110/41*sin(39/25-7*t)-2479/826*sin(14/9-6*t)-166/33*sin(69/44-4*t)-266/23*sin(69/44-t)+62/33*sin(2*t+146/31)+1/7*sin(3*t+38/27)+1/64*sin(5*t+6/35)+27/23*sin(8*t+37/24)+49/40*sin(12*t+179/38)+39/11*sin(13*t+65/41)+53/71*sin(14*t+80/17)+255/98*sin(16*t+36/23)+110/17*sin(18*t+49/31)+8/33*sin(20*t+31/22)+38/11*sin(21*t+50/31)+35/32*sin(22*t+47/29)+11/23*sin(24*t+69/44)+52347/113)*theta(83*pi-t)*theta(t-79*pi)+(-128/59*sin(36/23-6*t)-75/29*sin(36/23-4*t)-853/40*sin(47/30-2*t)-1601/43*sin(102/65-t)+139/25*sin(3*t+39/25)+99/38*sin(5*t+19/12)+32/19*sin(7*t+81/52)+10547/30)*theta(79*pi-t)*theta(t-75*pi)+(-1/19*sin(57/37-7*t)+129/38*sin(t+41/26)+952/47*sin(2*t+212/45)+23/12*sin(3*t+169/36)+77/19*sin(4*t+146/31)+27/40*sin(5*t+35/22)+61/36*sin(6*t+146/31)+23311/66)*theta(75*pi-t)*theta(t-71*pi)+(-217/87*sin(49/32-8*t)+4673/114*sin(t+52/33)+1582/25*sin(2*t+30/19)+271/26*sin(3*t+65/41)+1036/29*sin(4*t+65/41)+151/43*sin(5*t+221/47)+240/19*sin(6*t+30/19)+277/31*sin(7*t+179/38)+346/35*sin(9*t+77/48)+136/23*sin(10*t+52/33)+41/13*sin(11*t+75/16)+29/23*sin(12*t+64/39)+23/35*sin(13*t+29/21)+5/27*sin(14*t+79/23)+12/31*sin(15*t+59/38)+41/16*sin(16*t+50/31)+17/30*sin(17*t+52/29)+5/17*sin(18*t+67/43)+23/25*sin(19*t+127/27)+43/38*sin(20*t+29/18)+11/17*sin(21*t+96/59)+23/45*sin(22*t+50/31)+13/44*sin(23*t+49/11)+20/33*sin(24*t+56/37)+1/12*sin(25*t+127/33)+4/27*sin(26*t+34/19)+3826/57)*theta(71*pi-t)*theta(t-67*pi)+(-67/36*sin(47/30-9*t)-629/118*sin(53/34-5*t)-49/30*sin(58/39-2*t)+7133/32*sin(t+245/52)+397/23*sin(3*t+146/31)+43/31*sin(4*t+48/31)+7/16*sin(6*t+50/33)+190/73*sin(7*t+245/52)+9/26*sin(8*t+18/11)-1822/15)*theta(67*pi-t)*theta(t-63*pi)+(4511/50*sin(t+179/38)+503/126*sin(2*t+65/41)+53/6*sin(3*t+179/38)+78/47*sin(4*t+51/32)+282/83*sin(5*t+80/17)+27/47*sin(6*t+51/32)+613/27)*theta(63*pi-t)*theta(t-59*pi)+(-8/9*sin(7/17-85*t)-12/17*sin(13/9-81*t)-22/31*sin(49/39-75*t)-29/41*sin(3/8-73*t)-22/19*sin(29/43-69*t)-31/47*sin(39/32-12*t)+353/3*sin(t+202/43)+4111/19*sin(2*t+61/13)+956/25*sin(3*t+14/9)+533/20*sin(4*t+89/19)+167/21*sin(5*t+183/40)+116/25*sin(6*t+131/28)+139/22*sin(7*t+55/36)+231/25*sin(8*t+149/32)+199/23*sin(9*t+70/47)+121/39*sin(10*t+649/139)+12/35*sin(11*t+66/35)+43/25*sin(13*t+17/11)+25/12*sin(14*t+79/17)+91/27*sin(15*t+41/29)+23/32*sin(16*t+205/44)+17/31*sin(17*t+121/27)+99/28*sin(18*t+114/25)+73/47*sin(19*t+19/13)+27/32*sin(20*t+193/43)+9/14*sin(21*t+64/33)+12/37*sin(22*t+197/46)+32/51*sin(23*t+119/29)+179/36*sin(24*t+190/43)+143/46*sin(25*t+131/30)+85/43*sin(26*t+146/33)+42/31*sin(27*t+536/119)+99/100*sin(28*t+108/23)+158/53*sin(29*t+76/65)+227/27*sin(30*t+27/23)+421/23*sin(31*t+17/14)+499/29*sin(32*t+65/54)+281/26*sin(33*t+73/61)+17/36*sin(34*t+78/17)+75/7*sin(35*t+178/41)+460/27*sin(36*t+125/29)+515/34*sin(37*t+73/17)+363/43*sin(38*t+179/42)+376/51*sin(39*t+412/97)+163/23*sin(40*t+38/9)+439/53*sin(41*t+174/41)+161/24*sin(42*t+127/30)+145/22*sin(43*t+106/25)+106/19*sin(44*t+135/32)+137/33*sin(45*t+67/16)+103/20*sin(46*t+245/57)+61/29*sin(47*t+94/23)+550/97*sin(48*t+171/40)+6/17*sin(49*t+187/72)+48/25*sin(50*t+102/23)+19/40*sin(51*t+41/12)+86/23*sin(52*t+101/24)+62/35*sin(53*t+157/40)+143/71*sin(54*t+251/57)+5/3*sin(55*t+41/29)+527/111*sin(56*t+47/11)+152/39*sin(57*t+47/39)+95/33*sin(58*t+49/11)+1381/345*sin(59*t+19/18)+167/125*sin(60*t+57/13)+19/31*sin(61*t+31/27)+37/32*sin(62*t+35/32)+63/94*sin(63*t+119/26)+16/13*sin(64*t+74/53)+29/6*sin(65*t+121/29)+57/47*sin(66*t+47/26)+61/27*sin(67*t+760/169)+241/39*sin(68*t+149/150)+31/12*sin(70*t+34/29)+49/37*sin(71*t+60/13)+73/37*sin(72*t+25/24)+25/28*sin(74*t+49/29)+42/23*sin(76*t+45/37)+69/25*sin(77*t+98/23)+41/27*sin(78*t+37/24)+29/15*sin(79*t+131/31)+16/11*sin(80*t+13/12)+29/28*sin(82*t+94/29)+11/21*sin(83*t+177/38)+32/17*sin(84*t+43/47)+31/28*sin(86*t+13/7)+145/42*sin(87*t+192/49)+49/32*sin(88*t+19/21)+67/48*sin(89*t+1/13)+33/28*sin(90*t+151/48)+40/21*sin(91*t+121/31)+33/20*sin(92*t+11/15)+106/49*sin(93*t+1/9)+17/19*sin(94*t+98/59)+7/3*sin(95*t+176/45)+97/29*sin(96*t+20/31)+50/27*sin(97*t+2/9)+17/7*sin(98*t+136/41)+16260/23)*theta(59*pi-t)*theta(t-55*pi)+(-142/41*sin(39/25-17*t)-68/33*sin(38/25-15*t)-25/12*sin(23/15-13*t)-23/12*sin(65/42-12*t)-497/33*sin(36/23-6*t)-105/19*sin(25/16-5*t)-1996/33*sin(47/30-4*t)-802/73*sin(113/72-t)+11015/34*sin(2*t+212/45)+354/35*sin(3*t+146/31)+109/14*sin(7*t+221/47)+16/15*sin(8*t+41/25)+217/69*sin(9*t+144/31)+17/13*sin(10*t+233/50)+82/29*sin(11*t+164/35)+3/7*sin(14*t+36/23)+224/79*sin(16*t+35/22)+43/14*sin(18*t+69/43)+7930/11)*theta(55*pi-t)*theta(t-51*pi)+(-13/25*sin(19/28-9*t)-16/33*sin(38/41-7*t)+181/15*sin(t+112/27)+159/32*sin(2*t+33/49)+259/38*sin(3*t+106/31)+57/13*sin(4*t+109/35)+8/27*sin(5*t+20/31)+35/22*sin(6*t+77/25)+41/32*sin(8*t+34/19)+23/42*sin(10*t+44/41)+22/35*sin(11*t+117/25)+17/32*sin(12*t+13/22)-9500/9)*theta(51*pi-t)*theta(t-47*pi)+(-2423/42*sin(36/29-t)-38027/36)*theta(47*pi-t)*theta(t-43*pi)+(-232/21*sin(59/47-5*t)-380/27*sin(2/17-4*t)-472/19*sin(19/18-3*t)+3307/21*sin(t+7/37)+1527/17*sin(2*t+76/65)+25/33*sin(6*t+23/20)+281/36*sin(7*t+134/33)+37/27*sin(8*t+57/25)+52/23*sin(9*t+54/13)+21/5*sin(10*t+19/9)+54/25*sin(11*t+172/51)+105/32*sin(12*t+53/46)-12945/26)*theta(43*pi-t)*theta(t-39*pi)+(-11/30*sin(36/43-11*t)-7/23*sin(3/11-7*t)-73/19*sin(1/49-3*t)-19/28*sin(4/31-t)+9/13*sin(2*t+21/62)+905/226*sin(4*t+4/25)+24/19*sin(5*t+111/40)+17/27*sin(6*t+41/23)+13/27*sin(8*t+57/20)+13/31*sin(9*t+5/27)+5/23*sin(10*t+59/18)+13/77*sin(12*t+123/37)+7606/23)*theta(39*pi-t)*theta(t-35*pi)+(-2/9*sin(7/25-11*t)-2/17*sin(17/32-9*t)-4/31*sin(25/17-7*t)-9/20*sin(41/39-5*t)+7/4*sin(t+53/28)+34/9*sin(2*t+109/26)+45/8*sin(3*t+217/51)+49/38*sin(4*t+37/36)+7/22*sin(6*t+90/181)+5/23*sin(8*t+11/25)+3/29*sin(10*t+169/41)+4/21*sin(12*t+305/76)+6727/20)*theta(35*pi-t)*theta(t-31*pi)+(-5/3*sin(23/34-3*t)-2989/130*sin(9/28-t)+13/30*sin(2*t+37/16)+5/6*sin(4*t+29/10)+15/41*sin(5*t+55/46)+5/23*sin(6*t+91/27)+9085/27)*theta(31*pi-t)*theta(t-27*pi)+(-56/45*sin(16/17-3*t)-2344/125*sin(19/43-t)+5/6*sin(2*t+20/19)+26/23*sin(4*t+75/31)+4/35*sin(5*t+654/187)+28/113*sin(6*t+118/37)+14511/44)*theta(27*pi-t)*theta(t-23*pi)+(-31/17*sin(29/48-14*t)-50/27*sin(33/98-13*t)-41/15*sin(11/39-10*t)-133/66*sin(5/18-8*t)-1171/19*sin(7/29-2*t)-1421/10*sin(27/22-t)+1347/59*sin(3*t+25/8)+942/43*sin(4*t+29/20)+740/49*sin(5*t+18/17)+47/8*sin(6*t+132/35)+167/13*sin(7*t+169/94)+67/19*sin(9*t+53/33)+16/81*sin(11*t+49/44)+35/18*sin(12*t+4/15)+9/23*sin(15*t+116/37)+19/11*sin(16*t+9/37)+2031/17)*theta(23*pi-t)*theta(t-19*pi)+(-23/30*sin(25/63-12*t)-163/109*sin(17/27-10*t)-sin(20/31-8*t)-104/11*sin(22/35-3*t)-98/27*sin(53/52-t)+138/55*sin(2*t+251/58)+177/17*sin(4*t+8/37)+269/84*sin(5*t+75/17)+28/29*sin(6*t+72/23)+20/19*sin(7*t+55/28)+68/45*sin(9*t+66/31)+31/37*sin(11*t+155/59)-6747/22)*theta(19*pi-t)*theta(t-15*pi)+(-103/38*sin(25/34-8*t)-439/219*sin(19/17-7*t)-159/31*sin(3/13-5*t)-35/26*sin(7/24-4*t)-387/83*sin(5/7-3*t)+944/19*sin(t+79/26)+329/31*sin(2*t+17/36)+25/12*sin(6*t+155/44)-4790/31)*theta(15*pi-t)*theta(t-11*pi)+(-84/31*sin(92/61-4*t)-3*sin(14/9-3*t)-381/31*sin(39/35-2*t)+925/46*sin(t+259/86)+48/43*sin(5*t+53/16)+27/29*sin(6*t+105/44)+11/21*sin(7*t+38/21)+7/31*sin(8*t+227/76)+11503/35)*theta(11*pi-t)*theta(t-7*pi)+(-16/21*sin(13/16-7*t)+245/12*sin(t+26/21)+199/20*sin(2*t+11/20)+47/26*sin(3*t+17/7)+70/51*sin(4*t+41/21)+22/9*sin(5*t+258/59)+29/31*sin(6*t+242/81)+7/24*sin(8*t+13/22)+26/37*sin(9*t+55/64)+8693/27)*theta(7*pi-t)*theta(t-3*pi)+(-16/9*sin(27/31-12*t)-103/38*sin(37/36-8*t)-63/25*sin(5/26-7*t)-58204/85*sin(4/21-t)+371/34*sin(2*t+109/27)+632/39*sin(3*t+71/27)+262/33*sin(4*t+13/16)+283/41*sin(5*t+43/24)+58/33*sin(6*t+79/32)+59/21*sin(9*t+77/20)+42/19*sin(10*t+81/35)+178/179*sin(11*t+17/19)+11/35*sin(13*t+67/16)+11/25*sin(14*t+14/13)+11/30*sin(15*t+135/29)+16/49*sin(16*t+70/43)+6851/32)*theta(3*pi-t)*theta(t+pi))*theta(sqrt(sgn(sin(t/2))))
return CGPoint(x: 320 + (x * 300), y: 280 + (y * -300))
}
let path = CGPathCreateMutable()
CGPathAddLines(path, nil, points, points.count)
layer.path = path
There a few issues here:
If look at the bottom of that formula on that web site, it defines θ to be the Heaviside step function.
You are doing integer division. You really want to be doing floating point division. E.g. rather than 10/47, you want 10.0/47.0.
You are striding from 0 to 2π, but you need to go from 0 to 104π.
These formulae are too complicated for Swift to compile. You'll want to split them up into separate lines.
In that equation you found online, there's something that looked curious, namely two occurrences of θ(sqrt(sgn(sin(t/2)))). Taking the square root of the sgn function is wrong (the is no real square root of -1). So I simplified those to just θ(sin(t/2)) and the curve behaved more reasonably.
Pulling this all together, I've taken your code sample, done some macros to split up those long formulae into separate statements, you end up with something like the code snippet here: https://gist.github.com/robertmryan/427f7e9b562ae153c7c1 (Stack Overflow is not letting me paste the full code here because it exceeds the maximum allowed length of a post.)
That yields the following image:
Note, all of those annoying lines back to the origin are a result of how this Mao curve is represented. It's not a single stroke, but rather a series of separate curves misleading stored as a single set of points. You could test for 0.0, 0.0, to eliminate that, but personally I'd break it up into separate arrays of points. I'll leave that for you.
Related
I'm looking for a way to calculate if a CGPoint is on a line segment. Are there any built-in methods in Swift or any of Apple's libraries?
I don't mind converting to a different type as long as it is part of Apple's code (e.g. similar to how instead of performing complex calculations on double4x4 matrix, you can convert it to an SCNNode).
I quickly came up with brute force solution, but it's neither elegant, nor accurate (given floating point comparisons everywhere):
extension CGPoint {
func isOnLineSegment(start: CGPoint, end: CGPoint) -> Bool {
func value(_ value: CGFloat, isBetween one: CGFloat, and two: CGFloat) -> Bool {
let a = [one, two].sorted()
return a[0] <= value && value <= a[1]
}
let slope = (end.y - start.y) / (end.x - start.x)
if slope.isInfinite {
let isOnLine = x.isNearlyEqual(to: start.x)
let isWithinYValues = value(y, isBetween: start.y, and: end.y)
return isOnLine && isWithinYValues
}
let isOnLine = y.isNearlyEqual(to: slope * (x - start.x) + start.y) // y = m * (x - x1) + y1
let isWithinXValues = value(x, isBetween: start.x, and: end.x)
return isOnLine && isWithinXValues
}
}
A possible approach is to transform the whole configuration such that the start point of the segment is transformed to the origin, and the end point is transformed to the point (1, 0). This can be done with a CGAffineTransformation. The problem then reduces to determining if the given point is on the line segment between (0, 0) and (1,0). Here is a possible implementation:
extension CGPoint {
func isOnLineSegment(start: CGPoint, end: CGPoint) -> Bool {
// Transformation which maps (0,0) to start and (1, 0) to end:
let t = CGAffineTransform(a: end.x - start.x, b: end.y - start.y,
c: start.y - end.y, d: end.x - start.x,
tx: start.x, ty: start.y)
// Apply the inverse transformation to our point:
let q = self.applying(t.inverted())
// Check (with some tolerance) if q is on the segment from (0, 0) to (1, 0):
let eps = CGFloat.ulpOfOne.squareRoot()
return q.x > -eps && q.x < 1.0 + eps && q.y.magnitude < eps
}
}
As tolerance I chose the square root of .ulpOfOne (as suggested here), but you can adjust it to your needs.
Another approach is to use the fact that a point P is on the line segment from P1 to P2 exactly if
| P - P1 | + | P - P2 | = | P1 - P2 |
where | . | denotes the (Euclidean) distance:
extension CGPoint {
func distance(to p: CGPoint) -> CGFloat {
return hypot(p.x - self.x, p.y - self.y)
}
func isOnLineSegment(start: CGPoint, end: CGPoint) -> Bool {
let eps = CGFloat.ulpOfOne.squareRoot()
return self.distance(to: start) + self.distance(to: end) <= start.distance(to: end) + eps
}
}
If you fear floating-point inaccuracies, you'd better compute the distance to the line segment and check if it falls under a tolerance. (I will not expand on how to select this tolerance.)
An efficient computation scheme is as follows, using complex numbers (I use them for convenience, but there are equivalent vector forms, or just scalar expressions):
translate one of the endpoints to the origin (now the segment is 0 to P1-P0 and the tested point is Q-P0);
rotate to make the segment horizontal; for this, you multiply by (R:= (P1-P0)*/|P1-P0|, where * denotes the complex conjugate.
now the real part of (Q-P0).R tells you if the point projects to the segment (when 0≤Im≤|P1-P0|).
if yes, the imaginary part is the distance to the segment;
if no, compute the distance to the nearest endpoint (you know which by looking at the above real part).
The picture illustrates the three different cases to get the distance to the segment (transformed points).
Technical notes:
Depending on your needs, you can adopt different strategies for the handling of the endpoints:
a point past the endpoint can be considered outside the segment;
a point past the endpoint horizontally by more than the tolerance can be considered outside the segment;
a point past the endpoint obliquely by more than the tolerance can be considered outside the segment.
These options correspond to acceptance domains with respective shapes being tight rectangle, larger rectangle and rounded rectangle.
This method preserves scale, making the tolerance an absolute distance criterion.
I've been playing around with the MDLVoxelArray functionality in iOS, to create a mesh algorithmically via voxels.
Currently I create the voxel array like this:
let minBounds = vector_float3(x: 0.0, y: 0.0, z: 0.0)
let maxBounds = vector_float3(x: Float(width), y: Float(height), z: Float(maxDepth))
let boundingBox = MDLAxisAlignedBoundingBox(maxBounds: maxBounds, minBounds: minBounds)
let voxels = MDLVoxelArray(data: Data(capacity: MemoryLayout<MDLVoxelIndex>.size * width * height * depth), boundingBox: boundingBox, voxelExtent: 1.0)
Then I populate it with indices:
var index = MDLVoxelIndex(x: 0, y: 0, z: 0, w: 0)
for i in 0 ..< height {
for j in 0 ..< width {
index.x = Int32(j)
index.y = Int32(i)
let depth = Int32(heightMap[j][i])
let midPoint = depth / 2
for k in 0 ... depth {
// w should be 0 at the surface and increase in magnitude towards the middle
let w = abs(k - midPoint) - midPoint
index.z = k
index.w = w
voxels.setVoxelAtIndex(index)
}
}
}
Later I then convert the MDLVoxelArray to a mesh and save it out to a USDZ file. It works (and produces pretty nice results, actually) but it's really slow and gets progressively slower and slower as it iterates further and further.
I'm finding very few examples / little documentation about MDLVoxelArrays online, so my question is:
Is this the right way to go about creating and populating the voxel array? I was just guessing at how to give it an initial capacity when populating the Data object, but am imagining that might not be correct.
Any tips would also be welcome on how to go about profiling why it's slow. I'm not sure what tools are available for diagnosing performance bottlenecks when they're inside Apple's internal APIs like this?
So I wrote the following function to take a frame, and polar coordinate function and to graph it out by generating the cartesian coordinates within that frame. Here's the code.
func cartesianCoordsForPolarFunc(frame: CGRect, thetaCoefficient:Double, cosScalar:Double, iPrecision:Double, largestScalar:Double) -> Array<CGPoint> {
// Frame: The frame in which to fit this curve.
// thetaCoefficient: The number to scale theta by in the cos.
// cosScalar: The number to multiply the cos by.
// largestScalar: Largest cosScalar used in this frame so that scaling is relative.
// iPrecision: The step for continuity. 0 < iPrecision <= 2.pi. Defaults to 0.1
// Clean inputs
var precision:Double = 0.1 // Default precision
if iPrecision != 0 {// Can't be 0.
precision = iPrecision
}
// This is ther polar function
// var theta: Double = 0 // 0 <= theta <= 2pi
// let r = cosScalar * cos(thetaCoefficient * theta)
var points:Array<CGPoint> = [] // We store the points here
for theta in stride(from: 0, to: Double.pi * 2 , by: precision) { //TODO: Try to recreate continuity. WHY IS IT NOT 2PI
let x = cosScalar * cos(thetaCoefficient * theta) * cos(theta) // Convert to cartesian
let y = cosScalar * cos(thetaCoefficient * theta) * sin(theta) // Convert to cartesian
// newvalue = (max'-min')/(max-min)*(value-max)+max'
let scaled_x = (Double(frame.width) - 0)/(largestScalar*2)*(x-largestScalar)+Double(frame.width) // Scale to the frame
let scaled_y = (Double(frame.height) - 0)/(largestScalar*2)*(y-largestScalar)+Double(frame.height) // Scale to the frame
points.append(CGPoint(x: scaled_x, y:scaled_y)) // Add the result
}
print("Done points")
return points
}
The polar function I'm passing is r = 100*cos(9/4*theta) which looks like this.
I'm wondering why my function returns the following when theta goes from 0 to 2. (Please note I'm in this image I'm drawing different sizes flowers hence the repetition of the pattern)
As you can see it's wrong. Weird thing is that when theta goes from 0 to 2Pi*100 (Also works for other random values such as 2Pi*4, 2Pi*20 but not 2Pi*2 or 2Pi*10)it works and I get this.
Why is this? Is the domain not 0 to 2Pi? I noticed that when going to 2Pi*100 it redraws some petals so there is a limit, but what is it?
PS: Precision here is 0.01 (enough to act like it's continuous). In my images I'm drawing the function in different sizes and overlapping (last image has 2 inner flowers).
No, the domain is not going to be 2π. Set up your code to draw slowly, taking 2 seconds for each 2π, and watch. It makes a whole series of full circles, and each time the local maxima and minima land at different points. That's what your petals are. It looks like your formula repeats after 8π.
It looks like the period is the denominator of the theta coefficient * 2π. Your theta coefficient is 9/4, the denominator is 4, so the coefficient is 4*2π, or 8π.
(That is based on playing in Wolfram Alpha and observing the results. I may be wrong.)
I'm trying to draw a simple Parabola shape using UIBezierPath. I have a maxPoint and a boundingRect of which I'm basing the width and stretch of the parabola.
Here's the function I made to draw the parabola (I draw the parabola in a container view, rect will be container.bounds):
func addParabolaWithMax(maxPoint: CGPoint, inRect boundingRect: CGRect) {
let path = UIBezierPath()
let p1 = CGPointMake(1, CGRectGetMaxY(boundingRect)-1)
let p3 = CGPointMake(CGRectGetMaxX(boundingRect)-1, CGRectGetMaxY(boundingRect)-1)
path.moveToPoint(p1)
path.addQuadCurveToPoint(p3, controlPoint: maxPoint)
// Drawing code
...
}
My problem is, that I want the maxPoint that I send in the function to be the actual extreme point in the parabola itself. So for example, if I send in (CGRectGetMidX(container.bounds), 0), The maximum point should be at the top-most center. But in using this function with this particular point, this is what the result looks like:
So what exactly the path does here? Or in other words, how can I get from the controlPoint to the actual max point that I need? I've tried adding and subtracting different values from the y value, based on the height of the boundingRect, but I couldn't quite find the right combination, as in different points with different y values it behaves differently. There seem to be some kind of multiplier being added in, how can I solve it?
For may applications adam.wulf's solution is fine, but it doesn't actually create a parabola. To create a parabola, we need to compute the control point given the midpoint of the quadratic curve. Bézier paths are just math; we can compute this quite easily. We just need to invert the Bézier function and solve it for t=0.5.
The Bézier solution at 0.5 (the midpoint) is derived nicely at Draw a quadratic Bézier curve through three given points.
2*Pc - P0/2 - P2/2
Where Pc is the point we want to go through and P0 and P2 are the end points.
(Computing the Bézier at other points is not very intuitive. The value at t=0.25 is not "a quarter of the way along the path." But luckily for our purposes, t=0.5 matches quite nicely to our intuition of "the midpoint" on a quadratic.)
Given our solution, we can write our code. Forgive the translation to Swift 3; my copy of Xcode 7.3 isn't very happy with iOS playgrounds, but it should be easy to convert to 2.2.
func addParabolaWithMax(maxPoint: CGPoint, inRect boundingRect: CGRect) -> UIBezierPath {
func halfPoint1D(p0: CGFloat, p2: CGFloat, control: CGFloat) -> CGFloat {
return 2 * control - p0 / 2 - p2 / 2
}
let path = UIBezierPath()
let p0 = CGPoint(x: 0, y: boundingRect.maxY)
let p2 = CGPoint(x: boundingRect.maxX, y: boundingRect.maxY)
let p1 = CGPoint(x: halfPoint1D(p0: p0.x, p2: p2.x, control: maxPoint.x),
y: halfPoint1D(p0: p0.y, p2: p2.y, control: maxPoint.y))
path.move(to: p0)
path.addQuadCurve(to: p2, controlPoint: p1)
return path
}
The halfPoint1D function is the the one-dimensional implementation of our solution. For our two-dimentional CGPoint, we just have to call it twice.
If I could recommend just one resource for understanding Bézier curves, it would probably be the "Constructing Bézier curves" section from Wikipedia. Studying the little animations that show how the curves come about I find very enlightening. The "Specific Cases" section is useful as well. For a deep exploration of the topic (and one that I recommend all developers have a passing familiarity with), I like A Primer on Bézier Curves. It's ok to skim it and just read the parts that interest you at the moment. But a basic understanding of this group of functions will go a long way to removing the magic from drawing in Core Graphics and make UIBezierPath a tool rather than a black box.
let path = UIBezierPath()
let p1 = CGPointMake(0,self.view.frame.height/2)
let p3 = CGPointMake(self.view.frame.width,self.view.frame.height/2)
path.moveToPoint(p1)
path.addQuadCurveToPoint(p3, controlPoint: CGPoint(x: self.view.frame.width/2, y: -self.view.frame.height/2))
let line = CAShapeLayer()
line.path = path.CGPath;
line.strokeColor = UIColor.blackColor().CGColor
line.fillColor = UIColor.redColor().CGColor
view.layer.addSublayer(line)
this is the reason: https://cdn.tutsplus.com/mobile/authors/legacy/Akiel%20Khan/2012/10/15/bezier.png you should have to consider the tangent concept
The trick is to split the curve into two pieces so that you can control which points the curve passes through. As mentioned in Eduardo's answer, control points handle tangent, and end points are on the curve. This lets you have a curve from the bottom left to top center, then from top center to bottom right:
let p1 = CGPointMake(0,self.view.frame.height/2)
let p3 = CGPointMake(self.view.frame.width,self.view.frame.height/2)
let ctrlRight = CGPointMake(self.view.frame.width,0)
let ctrlLeft = CGPointZero
let bezierPath = UIBezierPath()
bezierPath.moveToPoint(p1)
bezierPath.addCurveToPoint(maxPoint, controlPoint1: p1, controlPoint2: ctrlLeft)
bezierPath.addCurveToPoint(p3, controlPoint1: ctrlRight, controlPoint2: p3)
UIColor.blackColor().setStroke()
bezierPath.lineWidth = 1
bezierPath.stroke()
I needed to do something similar where I wanted to have a UIBezierPath that exactly matched a specific parabola definition. So I made this little class that creates a parabola based on the focus and directrix or the a, b, c of the general equation. I threw in a convenience init which can use your boundingRect and maxPoint concepts. Either adapt those or the init where the upper corners of the box are its 1 and 2 and the middle of the bottom edge is the vertex.
Use the xform to scale and translate as needed. You can create/draw the path based on any two points on the parabola. They don't have to have the same y-value. The resulting shape will still exactly match the specified parabola.
This is not completely general in terms of rotation but it's a start.
class Parabola
{
var focus: CGPoint
var directrix: CGFloat
var a, b, c: CGFloat
init(_ f: CGPoint, _ y: CGFloat)
{
focus = f
directrix = y
let dy = f.y - y
a = 1 / (2*dy)
b = -f.x / dy
c = (f.x*f.x + f.y*f.y - y*y) / (2*dy)
}
init(_ a: CGFloat, _ b: CGFloat, _ c: CGFloat)
{
self.a = a
self.b = b
self.c = c
focus = CGPoint(x: -b / (2*a), y: (4*a*c - b*b + 1) / (4*a))
directrix = (4*a*c - b*b - 1) / (4*a)
}
convenience init(_ v: CGPoint,
_ pt1: CGPoint,
_ pt2: CGPoint)
{
let a = (pt2.y - v.y) / (pt2.x - v.x) / (pt2.x - v.x)
self.init(CGPoint(x: v.x, y: v.y + 1/(4*a)),
v.y - 1/(4*a))
}
func f(of x: CGFloat) -> CGFloat
{
a*x*x + b*x + c
}
func path(_ x1: CGFloat, _ x2: CGFloat,
_ xform: CGAffineTransform? = .identity) -> UIBezierPath
{
let pt1 = CGPoint(x1, f(of: x1))
let pt2 = CGPoint(x2, f(of: x2))
let x = (x1 + x2) / 2
let y = (2*a * x1 + b) * (x - x1) + pt1.y
let path = UIBezierPath()
path.move(to: pt1)
path.addQuadCurve(to: pt2, controlPoint: CGPoint(x: x, y: y))
path.apply(xform!)
return path
}
}
I have tried to follow this answer
It works fine for creating the polygons, however I can see that it doesn't reach the edges of the containing rectangle.
The following gif shows what I mean. Especially for the 5 sided polygon it is clear that it doesn't "span" the rectangle which I would like it to do
This is the code I use for creating the vertices
func verticesForEdges(_edges: Int) -> [CGPoint] {
let offset = 1.0
var vertices: [CGPoint] = []
for i in 0..._edges {
let angle = M_PI + 2.0 * M_PI * Double(i) / Double(edges)
var x = (frame.width / 2.0) * CGFloat(sin(angle)) + (bounds.width / 2.0)
var y = (frame.height / 2.0) * CGFloat(cos(angle)) + (bounds.height / 2.0)
vertices.append(CGPoint(x: x, y: y))
}
return vertices
}
And this is the code that uses the the vertices
override func layoutSublayers() {
super.layoutSublayers()
var polygonPath = UIBezierPath()
let vertices = verticesForEdges(edges)
polygonPath.moveToPoint(vertices[0])
for v in vertices {
polygonPath.addLineToPoint(v)
}
polygonPath.closePath()
self.path = polygonPath.CGPath
}
So the question is. How do I make the the polygons fill out the rectangle
Update:
The rectangle is not necessarily a square. It can have a different height from its width. From the comments it seems that I am fitting the polygon in a circle, but what is intentioned is to fit it in a rectangle.
If the first (i=0) vertice is fixed at the middle of top rectangle edge, we can calculate minimal width and height of bounding rectangle:
The rightmost vertice index
ir = (N + 2) / 4 // N/4, rounded to the closest integer, not applicable to triangle
MinWidth = 2 * R * Sin(ir * 2 * Pi / N)
The bottom vertice index
ib = (N + 1) / 2 // N/2, rounded to the closest integer
MinHeight = R * (1 + Abs(Cos(ib * 2 * Pi / N)))
So for given rectangle dimensions we can calculate R parameter to inscribe polygon properly