on d178521 help sign in
elk/core-js/Graphics/Collage.js
308 lines 9.0 kB history raw
2var Collage = function() {
4function tracePoints(ctx,points) {
5 var i = points.length - 1;
6 if (i <= 0) return;
7 ctx.moveTo(points[i][1], points[i][2]);
8 while (i--) { ctx.lineTo(points[i][1], points[i][2]); }
9}
11function solid(ctx,color,points) {
12 tracePoints(ctx,points);
13 ctx.strokeStyle = ElmCode.Graphics.Color.extract(color);
14 ctx.stroke();
15};
17function filled(ctx,color,points) {
18 tracePoints(ctx,points);
19 ctx.fillStyle = ElmCode.Graphics.Color.extract(color);
20 ctx.fill();
23function textured(redo,ctx,src,points) {
24 var img = new Image();
25 img.src = Foreign.JavaScript.castStringToJSString(src);
26 img.onload = redo;
28 tracePoints(ctx,points);
29 ctx.fillStyle = ctx.createPattern(img,'repeat');
30 ctx.fill();
33function customLine(pattern,ctx,color,points) {
34 if (pattern.length === 0) { pattern = [8,4]; }
35 customLineHelp(ctx, pattern, points);
36 ctx.strokeStyle = ElmCode.Graphics.Color.extract(color);
37 ctx.stroke();
38};
40var customLineHelp = function(ctx, pattern, points) {
41 var i = points.length - 1;
42 if (i <= 0) return;
43 var x0 = points[i][1], y0 = points[i][2];
44 var x1=0, y1=0, dx=0, dy=0, remaining=0, nx=0, ny=0;
45 var pindex = 0, plen = pattern.length;
46 var draw = true, segmentLength = pattern[0];
47 ctx.moveTo(x0,y0);
48 while (i--) {
49 x1 = points[i][1]; y1 = points[i][2];
50 dx = x1 - x0; dy = y1 - y0;
51 remaining = Math.sqrt(dx * dx + dy * dy);
52 while (segmentLength <= remaining) {
53 x0 += dx * segmentLength / remaining;
54 y0 += dy * segmentLength / remaining;
55 ctx[draw ? 'lineTo' : 'moveTo'](x0, y0);
56 // update starting position
57 dx = x1 - x0; dy = y1 - y0;
58 remaining = Math.sqrt(dx * dx + dy * dy);
59 // update pattern
60 draw = !draw;
61 pindex = (pindex + 1) % plen;
62 segmentLength = pattern[pindex];
63 }
64 if (remaining > 0) {
65 ctx[draw ? 'lineTo' : 'moveTo'](x1, y1);
66 segmentLength -= remaining;
67 }
68 x0 = x1; y0 = y1;
69 }
70};
72function drawLine(ctx,form) {
73 var points = form[3][1];
74 switch(form[1][0]) {
75 case "Solid" : return solid(ctx,form[2],points);
76 case "Dotted": return customLine([3,3],ctx,form[2],points);
77 case "Dashed": return customLine([8,4],ctx,form[2],points);
78 case "Custom": return customLine(form[1][1],ctx,form[2],points);
79 }
80};
82function drawShape(redo,ctx,shapeStyle,color,points) {
83 switch(shapeStyle[0]) {
84 case "Filled": return filled(ctx,color,points);
85 case "Outlined": return solid(ctx,color,points);
86 case "Textured": return textured(redo,ctx,shapeStyle[1],points);
87 case "CustomOutline":
88 return customLine(shapeStyle[1],ctx,color,points);
89 }
90};
92function drawImage(redo,ctx,w,h,src) {
93 var img = new Image();
94 img.onload = redo;
95 img.src = Foreign.JavaScript.castStringToJSString(src);
96 ctx.drawImage(img,-w/2,-h/2,w,h);
99function renderForm(redo,ctx,theta,scale,x,y,form) {
100 ctx.save();
101 if (x !== 0 || y !== 0) ctx.translate(x,y);
102 if (theta !== ~~theta) ctx.rotate(2*Math.PI*theta);
103 if (scale !== 1) ctx.scale(scale,scale);
104 ctx.beginPath();
105 switch(form[0]) {
106 case "FLine": drawLine(ctx,form); break;
107 case "FShape": drawShape(redo,ctx,form[1],form[2],form[3][1]); break;
108 case "FImage": drawImage(redo,ctx,form[1],form[2],form[3]); break;
109 }
110 ctx.restore();
111};
113function renderForms(redo,ctx,w,h,forms) {
114 ctx.clearRect(0,0,w,h);
115 for (var i = forms.length; i--; ) {
116 var f = forms[i];
117 renderForm(redo,ctx,f[1],f[2],f[3][1],f[3][2],f[4]);
118 }
121function collageForms(w,h,forms) {
122 var canvas = Render.newElement('canvas');
123 canvas.style.width = (~~w) + 'px';
124 canvas.style.height = (~~h) + 'px';
125 canvas.style.display = "block";
126 canvas.width = ~~w;
127 canvas.height = ~~h;
128 if (canvas.getContext) {
129 var ctx = canvas.getContext('2d');
130 var w = canvas.width, h = canvas.height;
131 function redo() { renderForms(this,ctx,w,h,forms); }
132 renderForms(redo,ctx,w,h,forms);
133 return canvas;
134 }
135 canvas.innerHTML = "Your browser does not support the canvas element.";
136 return canvas;
137};
139function collageElement(w,h,theta,scale,x,y,elem) {
140 var e = Render.render(elem);
141 var t = "translate(" + (x - elem[3] / 2) + "px,"+ (y - elem[4] / 2) + "px)";
142 var r = theta === (~~theta) ? "" : "rotate(" + theta*360 + "deg)";
143 var s = scale === 1 ? "" : "scale(" + scale + "," + scale + ")";
144 var transforms = t + " " + s + " " + r;
145 e.style.transform = transforms;
146 e.style.msTransform = transforms;
147 e.style.MozTransform = transforms;
148 e.style.webkitTransform = transforms;
149 e.style.OTransform = transforms;
150 var div = Render.newElement('div');
151 Render.addTo(div,e);
152 div.style.width = (~~w) + "px";
153 div.style.height = (~~h) + "px";
154 div.style.overflow = "hidden";
155 return div;
158function collage(w,h,formss) {
159 if (formss.length === 0) { return collageForms(w,h,[]); }
160 var elems = new Array(formss.length);
161 for (var i = formss.length; i--; ) {
162 var f = formss[i];
163 if (typeof f[0] === "string") {
164 elems[i] = collageElement(w,h,f[1],f[2],f[3][1],f[3][2],f[4][1]);
165 } else {
166 elems[i] = collageForms(w,h,f);
167 }
168 }
169 if (formss.length === 1) { return elems[0]; }
170 return Render.flowWith(Render.goIn,function(x){return x},elems);
173function updateFormSet(node,currSet,nextSet) {
174 if (Value.eq(nextSet,currSet)) return;
175 var w = node.style.width.slice(0,-2) - 0;
176 var h = node.style.height.slice(0,-2) - 0;
177 if (typeof nextSet[0] === "object") {
178 if (typeof currSet[0] === "object") {
179 if (node.getContext) {
180 var ctx = node.getContext('2d');
181 function redo() { renderForms(this,ctx,w,h,nextSet); }
182 return renderForms(redo,ctx,w,h,nextSet);
183 }
184 }
185 var newNode = collageForms(w,h,nextSet);
186 newNode.style.position = 'absolute';
187 return node.parentNode.replaceChild(newNode,node);
188 }
189 var f = nextSet;
190 var newNode = collageElement(w,h,f[1],f[2],f[3][1],f[3][2],f[4][1]);
191 newNode.style.position = 'absolute';
192 return node.parentNode.replaceChild(newNode,node);
195// assumes that the form sets are the same length.
196function updateCollage(node,currs,nexts) {
197 if (nexts.length === 1) {
198 return updateFormSet(node,currs[0],nexts[0]);
199 }
200 var kids = node.childNodes;
201 var len = kids.length;
202 for (var i = len; i--; ) {
203 updateFormSet(kids[len-i-1], currs[i], nexts[i]);
204 }
207function style(clr,n,list) {
208 return ["Tuple2",
209 '<span style="font-size:100%;color:' + clr + ';">' + n + '</span>',
210 list];
213function insideForm(point) { return function(form) {
214 if (!inBoundsOf(point[1],point[2],form)) return false;
215 var hw, hh;
216 switch (form[4][0]) {
217 case "FShape": return insideShape(point,form[1],form[2],form[3],form[4][3][1]);
218 case "FLine": return false;
219 case "FImage":
220 hw = form[4][1] / 2;
221 hh = form[4][2] / 2;
222 break;
223 case "FElement":
224 hw = form[4][1][3] / 2;
225 hh = form[4][1][4] / 2;
226 break;
227 }
228 return insideShape(point,form[1],form[2],form[3],
229 [ [null, hw, hh],
230 [null,-hw, hh],
231 [null,-hw,-hh],
232 [null, hw,-hh],
233 [null, hw, hh] ]);
234 };
237function inBoundsOf(px,py,form) {
238 if (form.length < 6) {
239 var fx = form[3][1], fy = form[3][2];
240 var radiusSquared = 0;
241 var scale = form[2];
242 switch (form[4][0]) {
243 case "FShape":
244 var points = form[4][3][1];
245 for (var i = points.length; --i; ) {
246 var p = points[i];
247 radiusSquared = Math.max(radiusSquared, p[1]*p[1] + p[2]*p[2]);
248 }
249 radiusSquared *= scale * scale;
250 break;
251 case "FLine":
252 break;
253 case "FImage":
254 var x = scale * form[4][1] / 2;
255 var y = scale * form[4][2] / 2;
256 radiusSquared = x*x + y*y;
257 break;
258 case "FElement":
259 var x = scale * form[4][1][3] / 2;
260 var y = scale * form[4][1][4] / 2;
261 radiusSquared = x*x + y*y;
262 break;
263 }
264 form.push(function(px,py) {
265 var dx = px - fx;
266 var dy = py - fy;
267 return dx*dx + dy*dy < radiusSquared + 1;
268 });
269 }
270 return form[5](px,py);
273function insideShape(point,theta,scale,pos,points) {
274 var counter = 0;
275 var list = ["Nil"];
276 var p1,p2;
278 var x = (point[1] - pos[1]) / scale;
279 var y = (point[2] - pos[2]) / scale;
280 if (theta !== 0) {
281 var t = -2 * Math.PI * theta;
282 var nx = x * Math.cos(t) - y * Math.sin(t);
283 y = x * Math.sin(t) + y * Math.cos(t);
284 x = nx;
285 }
287 if (points.length === 0) { return false; }
288 p1 = points[0];
289 for (var i = points.length - 1; i--; ) {
290 p2 = points[i];
291 var p1x = p1[1], p1y = p1[2], p2x = p2[1], p2y = p2[2];
293 if (p1y < p2y) {var ymin=p1y, ymax=p2y;} else {var ymin=p2y, ymax=p1y;}
294 if (p1x < p2x) {var xmin=p1x, xmax=p2x;} else {var xmin=p2x, xmax=p1x;}
296 if (ymin < y && y <= ymax && x <= xmax) {
297 if (x <= xmin || x <= ((y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x)) {
298 ++counter;
299 }
300 }
301 p1 = p2;
302 }
303 return (counter % 2) === 1;
306return {collage:collage, updateCollage:updateCollage, insideForm:insideForm};
308}();