@@ -0,0 +1,317 @@
1+
2+ElmRuntime.Render.Collage = function(rts) {
3+ /*
4+var JS = Elm.JavaScript;
5+
6+function 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]); }
11+}
12+
13+function solid(ctx,color,points) {
14+ tracePoints(ctx,points);
15+ ctx.strokeStyle = Elm.Color.extract(color);
16+ ctx.stroke();
17+};
18+
19+function filled(ctx,color,points) {
20+ tracePoints(ctx,points);
21+ ctx.fillStyle = Elm.Color.extract(color);
22+ ctx.fill();
23+}
24+
25+function textured(redo,ctx,src,points) {
26+ var img = new Image();
27+ img.src = JS.castStringToJSString(src);
28+ img.onload = redo;
29+
30+ tracePoints(ctx,points);
31+ ctx.fillStyle = ctx.createPattern(img,'repeat');
32+ ctx.fill();
33+}
34+
35+function 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+};
41+
42+var 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+};
73+
74+function 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+};
83+
84+function 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+};
93+
94+function 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);
99+}
100+
101+function 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+};
114+
115+function 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+ }
121+}
122+
123+function 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+}
141+
142+function 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;
152+}
153+
154+function 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;
163+}
164+
165+function 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);
178+}
179+
180+function 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);
202+}
203+
204+// assumes that the form sets are the same length.
205+function 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+ }
214+}
215+
216+function style(clr,n,list) {
217+ return ["Tuple2",
218+ '<span style="font-size:100%;color:' + clr + ';">' + n + '</span>',
219+ list];
220+}
221+
222+function 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+ };
244+}
245+
246+function 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);
280+}
281+
282+function insideShape(point,theta,scale,pos,points) {
283+ var counter = 0;
284+ var list = ["Nil"];
285+ var p1,p2;
286+
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+ }
295+
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];
301+
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;}
304+
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;
313+}
314+
315+return {collage:collage, updateCollage:updateCollage, insideForm:insideForm};
316+ */
317+};