[483] | 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 | }); |
---|