1 | define(["./_base", "dojo/_base/lang", "./matrix"], |
---|
2 | function(g, lang, m){ |
---|
3 | var twoPI = 2 * Math.PI, pi4 = Math.PI / 4, pi8 = Math.PI / 8, |
---|
4 | pi48 = pi4 + pi8, curvePI4 = unitArcAsBezier(pi8); |
---|
5 | |
---|
6 | function unitArcAsBezier(alpha){ |
---|
7 | // summary: |
---|
8 | // return a start point, 1st and 2nd control points, and an end point of |
---|
9 | // a an arc, which is reflected on the x axis |
---|
10 | // alpha: Number |
---|
11 | // angle in radians, the arc will be 2 * angle size |
---|
12 | var cosa = Math.cos(alpha), sina = Math.sin(alpha), |
---|
13 | p2 = {x: cosa + (4 / 3) * (1 - cosa), y: sina - (4 / 3) * cosa * (1 - cosa) / sina}; |
---|
14 | return { // Object |
---|
15 | s: {x: cosa, y: -sina}, |
---|
16 | c1: {x: p2.x, y: -p2.y}, |
---|
17 | c2: p2, |
---|
18 | e: {x: cosa, y: sina} |
---|
19 | }; |
---|
20 | } |
---|
21 | |
---|
22 | var arc = g.arc = { |
---|
23 | // summary: |
---|
24 | // This module contains the core graphics Arc functions. |
---|
25 | |
---|
26 | unitArcAsBezier: unitArcAsBezier, |
---|
27 | /*===== |
---|
28 | unitArcAsBezier: function(alpha) { |
---|
29 | // summary: |
---|
30 | // return a start point, 1st and 2nd control points, and an end point of |
---|
31 | // a an arc, which is reflected on the x axis |
---|
32 | // alpha: Number |
---|
33 | // angle in radians, the arc will be 2 * angle size |
---|
34 | }, |
---|
35 | =====*/ |
---|
36 | |
---|
37 | // curvePI4: Object |
---|
38 | // an object with properties of an arc around a unit circle from 0 to pi/4 |
---|
39 | curvePI4: curvePI4, |
---|
40 | |
---|
41 | arcAsBezier: function(last, rx, ry, xRotg, large, sweep, x, y){ |
---|
42 | // summary: |
---|
43 | // calculates an arc as a series of Bezier curves |
---|
44 | // given the last point and a standard set of SVG arc parameters, |
---|
45 | // it returns an array of arrays of parameters to form a series of |
---|
46 | // absolute Bezier curves. |
---|
47 | // last: Object |
---|
48 | // a point-like object as a start of the arc |
---|
49 | // rx: Number |
---|
50 | // a horizontal radius for the virtual ellipse |
---|
51 | // ry: Number |
---|
52 | // a vertical radius for the virtual ellipse |
---|
53 | // xRotg: Number |
---|
54 | // a rotation of an x axis of the virtual ellipse in degrees |
---|
55 | // large: Boolean |
---|
56 | // which part of the ellipse will be used (the larger arc if true) |
---|
57 | // sweep: Boolean |
---|
58 | // direction of the arc (CW if true) |
---|
59 | // x: Number |
---|
60 | // the x coordinate of the end point of the arc |
---|
61 | // y: Number |
---|
62 | // the y coordinate of the end point of the arc |
---|
63 | |
---|
64 | // calculate parameters |
---|
65 | large = Boolean(large); |
---|
66 | sweep = Boolean(sweep); |
---|
67 | var xRot = m._degToRad(xRotg), |
---|
68 | rx2 = rx * rx, ry2 = ry * ry, |
---|
69 | pa = m.multiplyPoint( |
---|
70 | m.rotate(-xRot), |
---|
71 | {x: (last.x - x) / 2, y: (last.y - y) / 2} |
---|
72 | ), |
---|
73 | pax2 = pa.x * pa.x, pay2 = pa.y * pa.y, |
---|
74 | c1 = Math.sqrt((rx2 * ry2 - rx2 * pay2 - ry2 * pax2) / (rx2 * pay2 + ry2 * pax2)); |
---|
75 | if(isNaN(c1)){ c1 = 0; } |
---|
76 | var ca = { |
---|
77 | x: c1 * rx * pa.y / ry, |
---|
78 | y: -c1 * ry * pa.x / rx |
---|
79 | }; |
---|
80 | if(large == sweep){ |
---|
81 | ca = {x: -ca.x, y: -ca.y}; |
---|
82 | } |
---|
83 | // the center |
---|
84 | var c = m.multiplyPoint( |
---|
85 | [ |
---|
86 | m.translate( |
---|
87 | (last.x + x) / 2, |
---|
88 | (last.y + y) / 2 |
---|
89 | ), |
---|
90 | m.rotate(xRot) |
---|
91 | ], |
---|
92 | ca |
---|
93 | ); |
---|
94 | // calculate the elliptic transformation |
---|
95 | var elliptic_transform = m.normalize([ |
---|
96 | m.translate(c.x, c.y), |
---|
97 | m.rotate(xRot), |
---|
98 | m.scale(rx, ry) |
---|
99 | ]); |
---|
100 | // start, end, and size of our arc |
---|
101 | var inversed = m.invert(elliptic_transform), |
---|
102 | sp = m.multiplyPoint(inversed, last), |
---|
103 | ep = m.multiplyPoint(inversed, x, y), |
---|
104 | startAngle = Math.atan2(sp.y, sp.x), |
---|
105 | endAngle = Math.atan2(ep.y, ep.x), |
---|
106 | theta = startAngle - endAngle; // size of our arc in radians |
---|
107 | if(sweep){ theta = -theta; } |
---|
108 | if(theta < 0){ |
---|
109 | theta += twoPI; |
---|
110 | }else if(theta > twoPI){ |
---|
111 | theta -= twoPI; |
---|
112 | } |
---|
113 | |
---|
114 | // draw curve chunks |
---|
115 | var alpha = pi8, curve = curvePI4, step = sweep ? alpha : -alpha, |
---|
116 | result = []; |
---|
117 | for(var angle = theta; angle > 0; angle -= pi4){ |
---|
118 | if(angle < pi48){ |
---|
119 | alpha = angle / 2; |
---|
120 | curve = unitArcAsBezier(alpha); |
---|
121 | step = sweep ? alpha : -alpha; |
---|
122 | angle = 0; // stop the loop |
---|
123 | } |
---|
124 | var c2, e, M = m.normalize([elliptic_transform, m.rotate(startAngle + step)]); |
---|
125 | if(sweep){ |
---|
126 | c1 = m.multiplyPoint(M, curve.c1); |
---|
127 | c2 = m.multiplyPoint(M, curve.c2); |
---|
128 | e = m.multiplyPoint(M, curve.e ); |
---|
129 | }else{ |
---|
130 | c1 = m.multiplyPoint(M, curve.c2); |
---|
131 | c2 = m.multiplyPoint(M, curve.c1); |
---|
132 | e = m.multiplyPoint(M, curve.s ); |
---|
133 | } |
---|
134 | // draw the curve |
---|
135 | result.push([c1.x, c1.y, c2.x, c2.y, e.x, e.y]); |
---|
136 | startAngle += 2 * step; |
---|
137 | } |
---|
138 | return result; // Array |
---|
139 | } |
---|
140 | }; |
---|
141 | |
---|
142 | return arc; |
---|
143 | }); |
---|