-
Notifications
You must be signed in to change notification settings - Fork 1
/
EllipseArc.hx
147 lines (147 loc) · 4.89 KB
/
EllipseArc.hx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package trilateral.justPath;
// http://www.petercollingridge.co.uk/blog/finding-angle-around-ellipse
// http://commons.oreilly.com/wiki/index.php/SVG_Essentials/Paths
// https://github.com/waldyrious/understand-svg-arcs
typedef EllipseArcData = {
public var cx: Float;
public var cy: Float;
var rx: Float;
var ry: Float;
var alpha: Float;
var omega: Float;
var delta: Float;
var phi: Float;
// pre-calculate to reduce point calculations
var phiSin: Float;
var phiCos: Float;
}
@:forward
class EllipseArc{
var arc: EllipseArcData;
public var x: Float;
public var y: Float;
public function new( arc_: EllipseArcData ){
arc = arc_;
}
public function alphaPoint(){ // mainly for testing
calculatePoint( arc.alpha );
}
public function omegaPoint(){ // mainly for testing
calculatePoint( arc.omega );
}
public function lineRender( moveTo: Float->Float->Void, lineTo: Float->Float->Void
, dA: Float, ?renderFirst: Bool = true){
var sign = ( arc.delta > 0 )? 1: -1;
var totalSteps = Math.ceil( Math.abs( arc.delta )/dA );
var theta = arc.alpha;
var step = arc.delta/totalSteps;
if( renderFirst ){
calculatePoint( theta );
moveTo( x, y );
}
for( i in 1...totalSteps ){
theta += step;
calculatePoint( theta );
lineTo( x, y );
}
calculatePoint( arc.omega );
lineTo( x, y );
}
public inline
function calculatePoint( theta: Float ){
var px = arc.cx + arc.rx*Math.cos( theta );
var py = arc.cy + arc.ry*Math.sin( theta );
px -= arc.cx;
py -= arc.cy;
var dx = px;
var dy = py;
px = dx*arc.phiCos - dy*arc.phiSin;
py = dx*arc.phiSin + dy*arc.phiCos;
x = px + arc.cx;
y = py + arc.cy;
//x = px;
//y = py;
}
}
abstract ConverterArc( EllipseArcData ) from EllipseArcData to EllipseArcData {
public inline function new( sx: Float, sy: Float, xr: Float, yr: Float, phi: Float, large: Int, sweep: Int, ex: Float, ey: Float ){
// mid point between start and end points.
var mx = ( sx - ex )/2;
var my = ( sy - ey )/2;
// average
var ax = ( sx + ex )/2;
var ay = ( sy + ey )/2;
// convert to radians.
// must check that % works with negative.
phi %= 360;
phi = phi*Math.PI/180;
var sin = Math.sin( phi );
var cos = Math.cos( phi );
// find x1, y1
var x1 = mx*cos + my*sin;
var y1 = -mx*sin + my*cos;
// check radii large enough
var rx = Math.abs( xr );
var ry = Math.abs( yr );
var rxx = rx*rx;
var ryy = ry*ry;
var xx1 = x1*x1;
var yy1 = y1*y1;
var check = xx1/rxx + yy1/ryy;
if( check > 1 ){
rx *= Math.sqrt( check );
ry *= Math.sqrt( check );
rxx = rx*rx;
ryy = ry*ry;
}
// find cx,cy
var sign = ( large == sweep ) ? -1: 1;
var sq = ( rxx*ryy - rxx * yy1 - ryy * xx1 ) / ( rxx * yy1 + ryy * xx1 );
sq = ( sq < 0 )? 0: sq;
var coef = sign * Math.sqrt( sq );
var cx1 = coef * rx * y1 / ry;
var cy1 = -coef * ry * x1 / rx;
var cx = ax + cx1*cos - cy1*sin;
var cy = ay + cx1*sin + cy1*cos;
// adjust sx, sy, ex, ey for phi
var phiSin = Math.sin( -phi );
var phiCos = Math.cos( -phi );
sx -= cx;
sy -= cy;
var dx = sx;
var dy = sy;
sx = dx*phiCos - dy*phiSin;
sy = dx*phiSin + dy*phiCos;
sx = sx + cx;
sy = sy + cy;
ex -= cx;
ey -= cy;
var dx = ex;
var dy = ey;
ex = dx*phiCos - dy*phiSin;
ey = dx*phiSin + dy*phiCos;
ex = ex + cx;
ey = ey + cy;
// calculate start and end angle taking into acount ellipse angle != eulclid angle.
var alpha = Math.atan2( rx*(cy-sy), ry*(cx-sx) ) - Math.PI;
var omega = Math.atan2( rx*(cy-ey), ry*(cx-ex) ) - Math.PI;
var delta = alpha - omega;
// change depending on sweep.
if( sweep == 1 && delta > 0) {
delta -= 2*Math.PI;
} else if( sweep == 0 && delta < 0 ){
delta += 2*Math.PI;
}
this = { cx: cx, cy: cy, rx: rx, ry: ry
, alpha: alpha, omega: omega, delta: -delta, phi: phi
, phiSin: Math.sin( phi ), phiCos: Math.cos( phi ) }
}
static inline function zeroto2pi( angle: Float ): Float {
return if( angle >= 0 && angle > Math.PI ){
angle; // don't really want any maths to touch it if it's within range as it may effect value slightly
} else {
var a = angle % ( 2 * Math.PI );
return ( a >= 0 )? a : ( a + 2 * Math.PI );
}
}
}