@@ -3,8 +3,9 @@ ElmRuntime.Render.Collage = function() {
3'use strict';
3'use strict';
5var Render = ElmRuntime.use(ElmRuntime.Render.Element);
5var Render = ElmRuntime.use(ElmRuntime.Render.Element);
6+var Matrix = Elm.Matrix2D({});
6var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
7var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
7−var newElement = Utils.newElement, addTo = Utils.addTo,
8+var newElement = Utils.newElement,
8 extract = Utils.extract, fromList = Utils.fromList,
9 extract = Utils.extract, fromList = Utils.fromList,
9 fromString = Utils.fromString, addTransform = Utils.addTransform;
10 fromString = Utils.fromString, addTransform = Utils.addTransform;
@@ -79,20 +80,23 @@ function texture(redo, ctx, src) {
81function gradient(ctx, grad) {
82function gradient(ctx, grad) {
82− var g, p1, p2;
83+ var g;
83− if (grad.ctor === 'Linear') {
85+ if (grad.ctor === 'Linear') {
84− p1 = grad._1; p2 = grad._2;
86+ var p0 = grad._0, p1 = grad._1;
85− g = ctx.createLinearGradient(p1._0, p1._1, p2._0, p2._1);
87+ g = ctx.createLinearGradient(p0._0, -p0._1, p1._0, -p1._1);
88+ stops = fromList(grad._2);
87− p1 = grad._1; p2 = grad._3;
90+ var p0 = grad._0, p2 = grad._2;
88− g = ctx.createRadialGradient(p1._0, p1._1, grad._2, p2._0, p2._1, grad._4);
91+ g = ctx.createRadialGradient(p0._0, -p0._1, grad._1, p2._0, -p2._1, grad._3);
92+ stops = fromList(grad._4);
90− var stops = fromList(grad._0);
91− for (var i = stops.length; i--; ) {
94+ var len = stops.length;
95+ for (var i = 0; i < len; ++i) {
92− var stop = stops[i];
96+ var stop = stops[i];
93− g.addColorStop(stop._0, extract(stop._1));
97+ g.addColorStop(stop._0, extract(stop._1));
95− return g;
99+ return g;
98function drawShape(redo, ctx, style, path) {
102function drawShape(redo, ctx, style, path) {
@@ -123,7 +127,7 @@ function drawImage(redo, ctx, form) {
123 ctx.drawImage(img, srcX, srcY, srcW, srcH, destX, destY, destW, destH);
127 ctx.drawImage(img, srcX, srcY, srcW, srcH, destX, destY, destW, destH);
126−function renderForm(stack, redo, ctx, form) {
130+function renderForm(redo, ctx, form) {
127 ctx.save();
131 ctx.save();
128 var x = form.x, y = form.y, theta = form.theta, scale = form.scale;
132 var x = form.x, y = form.y, theta = form.theta, scale = form.scale;
129 if (x !== 0 || y !== 0) ctx.translate(x, -y);
133 if (x !== 0 || y !== 0) ctx.translate(x, -y);
@@ -132,94 +136,194 @@ function renderForm(stack, redo, ctx, form) {
132 ctx.beginPath();
136 ctx.beginPath();
133 var f = form.form;
137 var f = form.form;
134 switch(f.ctor) {
138 switch(f.ctor) {
135− case 'FPath' : drawLine(ctx, f._0, f._1); break;
139+ case 'FPath' : drawLine(ctx, f._0, f._1); break;
140+ case 'FImage': drawImage(redo, ctx, f); break;
136− case 'FShape':
141+ case 'FShape':
137− if (f._0.ctor === 'Left') {
142+ if (f._0.ctor === 'Left') {
138− f._1.closed = true;
143+ f._1.closed = true;
139− drawLine(ctx, f._0._0, f._1);
144+ drawLine(ctx, f._0._0, f._1);
140− } else {
145+ } else {
141− drawShape(redo, ctx, f._0._0, f._1);
146+ drawShape(redo, ctx, f._0._0, f._1);
144− case 'FImage': drawImage(redo, ctx, f); break;
148− // stack.push(makeTransform(x, y, theta, scale, m));
149− ctx.transform(m[0], m[3], m[1], m[4], m[2], m[5]);
150− renderForms(stack, redo, ctx, f._1);
155 ctx.restore();
150 ctx.restore();
158−function makeTransform(x, y, theta, scale, matrix) {
153+function makeTransform(w, h, form, matrices) {
159− return function(m) {
154+ var props = form.form._0.props;
155+ var m = A6( Matrix.matrix, 1, 0, 0, 1,
156+ (w - props.width)/2,
157+ (props.height - h)/2 );
158+ var len = matrices.length;
159+ for (var i = 0; i < len; ++i) { m = A2( Matrix.multiply, m, matrices[i] ); }
161+ var x = form.x, y = form.y, theta = form.theta, scale = form.scale;
160− if (x !== 0 || y !== 0) m = A3( Matrix.move, x, y, m );
162+ if (x !== 0 || y !== 0) m = A3( Matrix.move, x, y, m );
161− if (theta !== 0) m = A2( Matrix.rotate, theta, m );
163+ if (theta !== 0) m = A2( Matrix.rotate, theta, m );
162− if (scale !== 1) m = Matrix.scale( scale );
164+ if (scale !== 1) m = Matrix.scale( scale );
163− return Matrix.multiply( m, matrix );
165+ return 'matrix(' + m[0] + ',' + m[3] + ',' + m[1] + ',' +
166+ m[4] + ',' + m[2] + ',' + (-m[5]) + ')';
169+function stepperHelp(list) {
170+ var arr = fromList(list);
172+ function peekNext() {
173+ return i < arr.length ? arr[i].form.ctor : '';
175+ // assumes that there is a next element
181+ return { peekNext:peekNext, next:next };
167−function renderForms(stack, redo, ctx, forms) {
184+function stepper(forms) {
168− var fs = fromList(forms);
185+ var ps = [stepperHelp(forms)];
187+ function peekNext() {
169− for (var i = 0, len = fs.length; i < len; ++i) {
188+ var len = ps.length;
170− renderForm(stack, redo, ctx, fs[i]);
189+ var formType = '';
190+ for (var i = 0; i < len; ++i ) {
191+ if (formType = ps[i].peekNext()) return formType;
195+ // assumes that there is a next element
196+ function next(ctx) {
197+ while (!ps[0].peekNext()) { ps.shift(); matrices.pop(); ctx.restore(); }
198+ var out = ps[0].next();
200+ if (f.ctor === 'FGroup') {
201+ ps.unshift(stepperHelp(f._1));
204+ var x = out.x, y = out.y, theta = out.theta, scale = out.scale;
205+ if (x !== 0 || y !== 0) ctx.translate(x, -y);
206+ if (theta !== 0) ctx.rotate(theta);
207+ if (scale !== 1) ctx.scale(scale, scale);
209+ ctx.transform(m[0], m[3], m[1], m[4], m[2], -m[5]);
213+ function transforms() { return matrices; }
214+ return { peekNext:peekNext, next:next, transforms:transforms };
174−function collageForms(w,h,forms) {
217+function makeCanvas(w,h) {
175 var canvas = newElement('canvas');
218 var canvas = newElement('canvas');
176 canvas.style.width = w + 'px';
219 canvas.style.width = w + 'px';
177 canvas.style.height = h + 'px';
220 canvas.style.height = h + 'px';
178 canvas.style.display = "block";
221 canvas.style.display = "block";
222+ canvas.style.position = "absolute";
179 canvas.width = w;
223 canvas.width = w;
180 canvas.height = h;
224 canvas.height = h;
181− function redo() { renderForms([], this, ctx, forms); }
182− if (canvas.getContext) {
183− var ctx = canvas.getContext('2d');
184− ctx.translate(w/2, h/2);
185− renderForms([], redo, ctx, forms);
188− canvas.innerHTML = "Your browser does not support canvas.";
189 return canvas;
225 return canvas;
192−function collageElement(stack, width, height, elem) {
228+function render(model) {
193− var e = Render.render(elem),
194− w = elem.props.width,
195− h = elem.props.height,
196− m = Matrix.matrix(1,0,0,1,width/2,height/2),
198− for (var i = 0; i < len; ++i) { m = stack[i](m); }
199− var mtrx = 'matrix(' + m[0] + ',' + m[3] + ',' + m[1] + ',' +
200− m[4] + ',' + m[2] + ',' + m[5] + ')';
201− addTransform(e.style, 'translate(' + (-w/2) + 'px,'+ (-h/2) + 'px) ' + mtrx);
202− var div = newElement('div');
229+ var div = newElement('div');
203− addTo(div,e);
230+ update(div, model, model);
204− div.style.width = width + 'px';
205− div.style.height = height + 'px';
206− div.style.overflow = 'hidden';
207− return div;
231+ return div;
210−function render(model) {
234+function updateTracker(w,h,div) {
211− return collageForms(model.w, model.h, model.forms);
235+ var kids = div.childNodes;
237+ function transform(transforms, ctx) {
238+ ctx.translate(w/2, h/2);
239+ var len = transforms.length;
240+ for (var i = 0; i < len; ++i) {
241+ var m = transforms[i];
243+ ctx.transform(m[0], m[3], m[1], m[4], m[2], m[5]);
247+ function getContext(transforms) {
248+ while (i < kids.length) {
249+ var node = kids[i++];
250+ if (node.getContext) {
253+ node.style.width = w + 'px';
254+ node.style.height = h + 'px';
255+ return transform(transforms, node.getContext('2d'));
257+ div.removeChild(node);
259+ var canvas = makeCanvas(w,h);
260+ div.appendChild(canvas);
261+ // we have added a new node, so we must step our position
263+ return transform(transforms, canvas.getContext('2d'));
265+ function element(matrices, form) {
266+ var container = kids[i];
268+ container = newElement('div');
269+ container.style.overflow = 'hidden';
270+ container.style.position = 'absolute';
271+ div.appendChild(container);
272+ } else if (container.getContext) {
273+ container = newElement('div');
274+ container.style.overflow = 'hidden';
275+ container.style.position = 'absolute';
276+ div.insertBefore(container, kids[i]);
278+ // we have added a new node, so we must step our position
281+ container.style.width = w + 'px';
282+ container.style.height = h + 'px';
284+ var elem = form.form._0;
285+ var node = container.firstChild;
287+ Render.update(node, node.oldElement, elem);
289+ node = Render.render(elem);
290+ container.appendChild(node);
292+ node.oldElement = elem;
293+ addTransform(node.style, makeTransform(w, h, form, matrices));
295+ return { getContext:getContext, element:element };
214−function update(node, oldModel, newModel) {
299+function update(div, _, model) {
302+ div.style.width = w + 'px';
303+ div.style.height = h + 'px';
304+ if (model.forms.ctor === 'Nil') {
305+ while (div.hasChildNodes()) {
306+ div.removeChild(div.lastChild);
309+ var stpr = stepper(model.forms);
310+ var tracker = updateTracker(w,h,div);
314+ while (formType = stpr.peekNext()) {
315+ if (ctx === null && formType !== 'FElement') {
215− if (!node.getContext) {
316+ ctx = tracker.getContext(stpr.transforms());
216− return node.parentNode.replaceChild(render(newModel), node);
317+ }
318+ var form = stpr.next(ctx);
319+ if (formType === 'FElement') {
320+ tracker.element(stpr.transforms(), form);
322+ } else if (formType !== 'FGroup') {
323+ renderForm(function() { update(div, model, model); }, ctx, form);
218− var ctx = node.getContext('2d');
326+ return div;
219− var w = newModel.w, h = newModel.h;
220− ctx.clearRect(-w/2, -h/2, w, h);
221− var redo = function(){ renderForms( [], this, ctx, newModel.forms ); };
222− renderForms( [], redo, ctx, newModel.forms );
225return { render:render, update:update };
329return { render:render, update:update };