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