]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | Ext.define('KitchenSink.view.draw.Protractor', {\r |
2 | // Typically, you'd want to extend the Composite sprite instead of using it directly.\r | |
3 | extend: 'Ext.draw.sprite.Composite',\r | |
4 | alias: 'sprite.protractor',\r | |
5 | \r | |
6 | inheritableStatics: {\r | |
7 | def: {\r | |
8 | // And define your own attributes on the composite that abstract away\r | |
9 | // the actual implementation.\r | |
10 | processors: {\r | |
11 | // The first four attributes (start and end point coordinates)\r | |
12 | // is all we really need for this sprite to work.\r | |
13 | fromX: 'number',\r | |
14 | fromY: 'number',\r | |
15 | toX: 'number',\r | |
16 | toY: 'number',\r | |
17 | // The rest of the attributes is just to allow customization.\r | |
18 | baseLineLength: 'number',\r | |
19 | arcRadius: 'number',\r | |
20 | arrowLength: 'number',\r | |
21 | arrowAngle: 'number'\r | |
22 | },\r | |
23 | // Changes to composite attributes will then trigger the recalculation of\r | |
24 | // attributes of composite's children sprites.\r | |
25 | // Here we define which composite's attributes should trigger such recalculation.\r | |
26 | // In this case we use a single updater function called 'recalculate', but it's\r | |
27 | // possible to specify and use different updaters for different attributes.\r | |
28 | dirtyTriggers: {\r | |
29 | fromX: 'recalculate',\r | |
30 | fromY: 'recalculate',\r | |
31 | toX: 'recalculate',\r | |
32 | toY: 'recalculate',\r | |
33 | baseLineLength: 'recalculate',\r | |
34 | arcRadius: 'recalculate',\r | |
35 | arrowLength: 'recalculate',\r | |
36 | arrowAngle: 'recalculate'\r | |
37 | },\r | |
38 | // Default values of composite's attributes.\r | |
39 | defaults: {\r | |
40 | fromX: 0,\r | |
41 | fromY: 0,\r | |
42 | toX: 100,\r | |
43 | toY: 100,\r | |
44 | baseLineLength: 50,\r | |
45 | arcRadius: 40,\r | |
46 | arrowLength: 10,\r | |
47 | arrowAngle: Math.PI / 8\r | |
48 | },\r | |
49 | updaters: {\r | |
50 | // This updater function is called every time the attributes\r | |
51 | // of the composite change, including animations.\r | |
52 | // Inside this updater we calculate and set the values of the attributes\r | |
53 | // of the children of the composite based on the values of the composite's\r | |
54 | // attributes.\r | |
55 | recalculate: function (attr) {\r | |
56 | // Please see this ticket https://sencha.jira.com/browse/EXTJS-15521\r | |
57 | // for a graphical representation of what's going on in this function.\r | |
58 | var me = this,\r | |
59 | fromX = attr.fromX,\r | |
60 | fromY = attr.fromY,\r | |
61 | toX = attr.toX,\r | |
62 | toY = attr.toY,\r | |
63 | dx = toX - fromX,\r | |
64 | dy = toY - fromY,\r | |
65 | PI = Math.PI,\r | |
66 | radius = Math.sqrt(dx*dx + dy*dy);\r | |
67 | \r | |
68 | if (dx === 0 || dy === 0) {\r | |
69 | return;\r | |
70 | }\r | |
71 | \r | |
72 | var alpha = Math.atan2(dy, dx),\r | |
73 | sin = Math.sin,\r | |
74 | cos = Math.cos,\r | |
75 | arcRadius = attr.arcRadius,\r | |
76 | beta = PI - attr.arrowAngle,\r | |
77 | x = attr.arrowLength * cos(beta),\r | |
78 | y = attr.arrowLength * sin(beta),\r | |
79 | // Coordinates of the arc arrow tip.\r | |
80 | ax = arcRadius * cos(alpha) + fromX,\r | |
81 | ay = arcRadius * sin(alpha) + fromY,\r | |
82 | mat = Ext.draw.Matrix.fly([cos(alpha), sin(alpha), -sin(alpha), cos(alpha), toX, toY]),\r | |
83 | angleArrowThreshold = Ext.draw.Draw.radian * me.getAngleArrowThreshold(),\r | |
84 | isSmallAngle = alpha < angleArrowThreshold && alpha > -angleArrowThreshold,\r | |
85 | angleTextRadius = arcRadius * 1.2,\r | |
86 | isSmallRadius = radius < angleTextRadius,\r | |
87 | radiusTextFlip, fontSize,\r | |
88 | theta = 0;\r | |
89 | \r | |
90 | if (alpha > 0) {\r | |
91 | theta = alpha + PI / 2 - attr.arrowAngle / (arcRadius * 0.1);\r | |
92 | } else if (alpha < 0) {\r | |
93 | theta = alpha - PI / 2 + attr.arrowAngle / (arcRadius * 0.1);\r | |
94 | }\r | |
95 | \r | |
96 | me.createSprites();\r | |
97 | \r | |
98 | me.baseLine.setAttributes({\r | |
99 | fromX: fromX,\r | |
100 | fromY: fromY,\r | |
101 | toX: fromX + attr.baseLineLength,\r | |
102 | toY: fromY,\r | |
103 | hidden: isSmallRadius\r | |
104 | });\r | |
105 | me.radiusLine.setAttributes({\r | |
106 | fromX: fromX,\r | |
107 | fromY: fromY,\r | |
108 | toX: toX,\r | |
109 | toY: toY,\r | |
110 | strokeStyle: attr.strokeStyle\r | |
111 | });\r | |
112 | me.radiusArrowLeft.setAttributes({\r | |
113 | fromX: toX,\r | |
114 | fromY: toY,\r | |
115 | toX: mat.x(x, y),\r | |
116 | toY: mat.y(x, y),\r | |
117 | strokeStyle: attr.strokeStyle\r | |
118 | });\r | |
119 | me.radiusArrowRight.setAttributes({\r | |
120 | fromX: toX,\r | |
121 | fromY: toY,\r | |
122 | toX: mat.x(x, -y),\r | |
123 | toY: mat.y(x, -y),\r | |
124 | strokeStyle: attr.strokeStyle\r | |
125 | });\r | |
126 | \r | |
127 | mat = Ext.draw.Matrix.fly([cos(theta), sin(theta), -sin(theta), cos(theta), ax, ay]);\r | |
128 | \r | |
129 | me.angleLine.setAttributes({\r | |
130 | startAngle: 0,\r | |
131 | endAngle: alpha,\r | |
132 | cx: fromX,\r | |
133 | cy: fromY,\r | |
134 | r: arcRadius,\r | |
135 | anticlockwise: alpha < 0,\r | |
136 | hidden: isSmallRadius\r | |
137 | });\r | |
138 | me.angleArrowLeft.setAttributes({\r | |
139 | fromX: ax,\r | |
140 | fromY: ay,\r | |
141 | toX: mat.x(x, y),\r | |
142 | toY: mat.y(x, y),\r | |
143 | hidden: isSmallAngle || isSmallRadius\r | |
144 | });\r | |
145 | me.angleArrowRight.setAttributes({\r | |
146 | fromX: ax,\r | |
147 | fromY: ay,\r | |
148 | toX: mat.x(x, -y),\r | |
149 | toY: mat.y(x, -y),\r | |
150 | hidden: isSmallAngle || isSmallRadius\r | |
151 | });\r | |
152 | me.angleText.setAttributes({\r | |
153 | x: angleTextRadius * cos(alpha / 2) + fromX,\r | |
154 | y: angleTextRadius * sin(alpha / 2) + fromY,\r | |
155 | text: me.getAngleText() + ': ' + (alpha * 180 / PI).toFixed(me.getPrecision()) + '°',\r | |
156 | hidden: isSmallRadius\r | |
157 | });\r | |
158 | radiusTextFlip = ((alpha > -0.5 * PI && alpha < 0.5 * PI) || (alpha > 1.5 * PI && alpha < 2 * PI)) ? 1 : -1;\r | |
159 | fontSize = parseInt(me.radiusText.attr.fontSize, 10);\r | |
160 | x = 0.5 * radius * cos(alpha) + fromX + radiusTextFlip * fontSize * sin(alpha);\r | |
161 | y = 0.5 * radius * sin(alpha) + fromY - radiusTextFlip * fontSize * cos(alpha);\r | |
162 | me.radiusText.setAttributes({\r | |
163 | x: x,\r | |
164 | y: y,\r | |
165 | rotationRads: alpha,\r | |
166 | rotationRads: radiusTextFlip === 1 ? alpha : alpha - PI,\r | |
167 | rotationCenterX: x,\r | |
168 | rotationCenterY: y,\r | |
169 | text: me.getRadiusText() + ': ' + radius.toFixed(me.getPrecision()),\r | |
170 | hidden: isSmallRadius\r | |
171 | });\r | |
172 | }\r | |
173 | }\r | |
174 | }\r | |
175 | },\r | |
176 | \r | |
177 | // Additional configuration options that are meant to be used once during setup time.\r | |
178 | // These need not be attributes, because we don't need them to animate\r | |
179 | // or trigger changes in other attributes.\r | |
180 | config: {\r | |
181 | radiusText: 'length',\r | |
182 | angleText: 'angle',\r | |
183 | precision: 1,\r | |
184 | angleArrowThreshold: 15\r | |
185 | },\r | |
186 | \r | |
187 | // The 'recalculate' updater will be called at construction time.\r | |
188 | // But the children sprites have not been created and added to the composite yet.\r | |
189 | // We can't add children to the composite before the parent constructor call,\r | |
190 | // because the composite hasn't been initialized yet.\r | |
191 | // And adding them after construction is too late, because the 'recalculate'\r | |
192 | // updater needs them.\r | |
193 | // So we define the 'createSprites' function that is called inside the 'recalculate'\r | |
194 | // updater before the sprites are used.\r | |
195 | createSprites: function () {\r | |
196 | var me = this;\r | |
197 | \r | |
198 | // Only create sprites if they haven't been created yet.\r | |
199 | if (!me.baseLine) {\r | |
200 | me.baseLine = me.add({\r | |
201 | type: 'line',\r | |
202 | lineDash: [2, 2]\r | |
203 | });\r | |
204 | me.radiusLine = me.add({\r | |
205 | type: 'line'\r | |
206 | });\r | |
207 | // Left line of the radius arrow.\r | |
208 | me.radiusArrowLeft = me.add({\r | |
209 | type: 'line'\r | |
210 | });\r | |
211 | // Right line of the radius arrow.\r | |
212 | me.radiusArrowRight = me.add({\r | |
213 | type: 'line'\r | |
214 | });\r | |
215 | me.angleLine = me.add({\r | |
216 | type: 'arc',\r | |
217 | strokeStyle: 'black',\r | |
218 | lineDash: [2, 2]\r | |
219 | });\r | |
220 | // Left line of the angle arrow.\r | |
221 | me.angleArrowLeft = me.add({\r | |
222 | type: 'line',\r | |
223 | lineDash: [2, 2]\r | |
224 | });\r | |
225 | // Right line of the angle arrow.\r | |
226 | me.angleArrowRight = me.add({\r | |
227 | type: 'line',\r | |
228 | lineDash: [2, 2]\r | |
229 | });\r | |
230 | me.radiusText = me.add({\r | |
231 | type: 'text',\r | |
232 | textAlign: 'center',\r | |
233 | textBaseline: 'middle',\r | |
234 | font: '12px'\r | |
235 | });\r | |
236 | me.angleText = me.add({\r | |
237 | type: 'text',\r | |
238 | textBaseline: 'middle',\r | |
239 | font: '12px'\r | |
240 | });\r | |
241 | }\r | |
242 | }\r | |
243 | \r | |
244 | }); |