@@ -5,7 +5,7 @@ ElmRuntime.Render.Collage = function() {
5var Render = ElmRuntime.use(ElmRuntime.Render.Element);
5var Render = ElmRuntime.use(ElmRuntime.Render.Element);
6var Matrix = Elm.Matrix2D({});
6var Matrix = Elm.Matrix2D({});
7var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
7var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
8−var newElement = Utils.newElement, addTo = Utils.addTo,
8+var newElement = Utils.newElement,
9 extract = Utils.extract, fromList = Utils.fromList,
9 extract = Utils.extract, fromList = Utils.fromList,
10 fromString = Utils.fromString, addTransform = Utils.addTransform;
10 fromString = Utils.fromString, addTransform = Utils.addTransform;
@@ -136,75 +136,68 @@ function renderForm(redo, ctx, form) {
136 ctx.restore();
136 ctx.restore();
139−function makeTransform(form, matrix) {
139+function makeTransform(w, h, form, matrices) {
140+ var props = form.form._0.props;
140− var m = matrix;
141+ var m = A6( Matrix.matrix, 1, 0, 0, 1,
142+ (w - props.width)/2,
143+ (props.height - h)/2 );
144+ var len = matrices.length;
145+ for (var i = 0; i < len; ++i) { m = A2( Matrix.multiply, m, matrices[i] ); }
141 var x = form.x, y = form.y, theta = form.theta, scale = form.scale;
147 var x = form.x, y = form.y, theta = form.theta, scale = form.scale;
142 if (x !== 0 || y !== 0) m = A3( Matrix.move, x, y, m );
148 if (x !== 0 || y !== 0) m = A3( Matrix.move, x, y, m );
143 if (theta !== 0) m = A2( Matrix.rotate, theta, m );
149 if (theta !== 0) m = A2( Matrix.rotate, theta, m );
144 if (scale !== 1) m = Matrix.scale( scale );
150 if (scale !== 1) m = Matrix.scale( scale );
148−function collageElement(width, height, matrices, form) {
149− var elem = form.form._0,
150− e = Render.render(elem),
151− m = A6( Matrix.matrix, 1, 0, 0, 1,
152− (width - elem.props.width)/2,
153− (elem.props.height - height)/2 );
154− var len = matrices.length;
155− for (var i = 0; i < len; ++i) { m = A2( Matrix.multiply, m, matrices[i] ); }
156− m = makeTransform(form, m);
157− var mtrx = 'matrix(' + m[0] + ',' + m[3] + ',' + m[1] + ',' +
151+ return 'matrix(' + m[0] + ',' + m[3] + ',' + m[1] + ',' +
158− m[4] + ',' + m[2] + ',' + (-m[5]) + ')';
152+ m[4] + ',' + m[2] + ',' + (-m[5]) + ')';
159− addTransform(e.style, mtrx);
160− var div = newElement('div');
162− div.style.width = width + 'px';
163− div.style.height = height + 'px';
164− div.style.overflow = 'hidden';
165− div.style.position = 'absolute';
169function stepperHelp(list) {
155function stepperHelp(list) {
170 var arr = fromList(list);
156 var arr = fromList(list);
171 var i = 0;
157 var i = 0;
158+ function peekNext() {
172− function hasNext() { return i < arr.length; }
159+ return i < arr.length ? arr[i].form.ctor : '';
173 // assumes that there is a next element
161 // assumes that there is a next element
174 function next() {
162 function next() {
175 var out = arr[i];
163 var out = arr[i];
177 return out;
165 return out;
179− return { hasNext:hasNext, next:next };
167+ return { peekNext:peekNext, next:next };
182function stepper(forms) {
170function stepper(forms) {
183 var ps = [stepperHelp(forms)];
171 var ps = [stepperHelp(forms)];
184 var matrices = [];
172 var matrices = [];
185− function hasNext() {
173+ function peekNext() {
186 var len = ps.length;
174 var len = ps.length;
187 for (var i = 0; i < len; ++i ) {
176 for (var i = 0; i < len; ++i ) {
188− if (ps[i].hasNext()) return true;
177+ if (formType = ps[i].peekNext()) return formType;
190− return false;
179+ return '';
192 // assumes that there is a next element
181 // assumes that there is a next element
193 function next(ctx) {
182 function next(ctx) {
194− while (!ps[0].hasNext()) { ps.shift(); matrices.pop(); ctx.restore(); }
183+ while (!ps[0].peekNext()) { ps.shift(); matrices.pop(); ctx.restore(); }
195 var out = ps[0].next();
184 var out = ps[0].next();
196 var f = out.form;
185 var f = out.form;
197 if (f.ctor === 'FGroup') {
186 if (f.ctor === 'FGroup') {
198 ps.unshift(stepperHelp(f._1));
187 ps.unshift(stepperHelp(f._1));
199 var m = f._0;
188 var m = f._0;
200 matrices.push(m);
189 matrices.push(m);
190+ var x = out.x, y = out.y, theta = out.theta, scale = out.scale;
191+ if (x !== 0 || y !== 0) ctx.translate(x, -y);
192+ if (theta !== 0) ctx.rotate(theta);
193+ if (scale !== 1) ctx.scale(scale, scale);
201 ctx.save();
194 ctx.save();
202 ctx.transform(m[0], m[3], m[1], m[4], m[2], -m[5]);
195 ctx.transform(m[0], m[3], m[1], m[4], m[2], -m[5]);
204 return out;
197 return out;
206 function transforms() { return matrices; }
199 function transforms() { return matrices; }
207− return { hasNext:hasNext, next:next, transforms:transforms };
200+ return { peekNext:peekNext, next:next, transforms:transforms };
210function makeCanvas(w,h) {
203function makeCanvas(w,h) {
@@ -219,45 +212,102 @@ function makeCanvas(w,h) {
221function render(model) {
214function render(model) {
224− div = newElement('div'),
215+ var div = newElement('div');
225− canvas = makeCanvas(w,h),
216+ update(div, model, model);
226− ctx = canvas.getContext('2d');
217+ return div;
227− div.appendChild(canvas);
218+}
228− ctx.translate(w/2, h/2);
230− var stpr = stepper(model.forms);
220+function updateTracker(w,h,div) {
231− while (stpr.hasNext()) {
221+ var kids = div.childNodes;
232− var form = stpr.next(ctx);
222+ var i = 0;
233− if (form.form.ctor === 'FElement') {
223+ function transform(transforms, ctx) {
234− div.appendChild(collageElement(w, h, stpr.transforms(), form));
235− if (!stpr.hasNext()) return div;
236− canvas = makeCanvas(w,h);
237− div.appendChild(canvas);
238− ctx = canvas.getContext('2d');
239− ctx.translate(w/2, h/2);
224+ ctx.translate(w/2, h/2);
240− var transforms = stpr.transforms();
241− var len = transforms.length;
225+ var len = transforms.length;
242− for (var i = 0; i < len; ++i) {
226+ for (var i = 0; i < len; ++i) {
243− var m = transforms[i];
227+ var m = transforms[i];
244− ctx.save();
228+ ctx.save();
245− ctx.transform(m[0], m[3], m[1], m[4], m[2], m[5]);
229+ ctx.transform(m[0], m[3], m[1], m[4], m[2], m[5]);
233+ function getContext(transforms) {
234+ while (i < kids.length) {
235+ var node = kids[i++];
236+ if (node.getContext) {
239+ return transform(transforms, node.getContext('2d'));
247− } else if (form.form.ctor !== 'FGroup') {
241+ div.removeChild(node);
248− renderForm(function() { update(div, model, model); }, ctx, form);
243+ var canvas = makeCanvas(w,h);
244+ div.appendChild(canvas);
245+ // we have added a new node, so we must step our position
247+ return transform(transforms, canvas.getContext('2d'));
251− return div;
249+ function element(matrices, form) {
250+ var container = kids[i];
252+ container = newElement('div');
253+ container.style.overflow = 'hidden';
254+ container.style.position = 'absolute';
255+ div.appendChild(container);
256+ } else if (container.getContext) {
257+ container = newElement('div');
258+ container.style.overflow = 'hidden';
259+ container.style.position = 'absolute';
260+ div.insertBefore(container, kids[i]);
262+ // we have added a new node, so we must step our position
265+ container.style.width = w + 'px';
266+ container.style.height = h + 'px';
268+ var elem = form.form._0;
269+ var node = container.firstChild;
271+ Render.update(node, node.oldElement, elem);
273+ node = Render.render(elem);
274+ container.appendChild(node);
276+ node.oldElement = elem;
277+ addTransform(node.style, makeTransform(w, h, form, matrices));
279+ return { getContext:getContext, element:element };
254−function update(node, oldModel, newModel) {
283+function update(div, _, model) {
255− if (!node.getContext) {
284+ var w = model.w;
256− return node.parentNode.replaceChild(render(newModel), node);
285+ var h = model.h;
286+ div.style.width = w + 'px';
287+ div.style.height = h + 'px';
288+ if (model.forms.ctor === 'Nil') {
289+ while (div.hasChildNodes()) {
290+ div.removeChild(div.lastChild);
258− var ctx = node.getContext('2d');
293+ var stpr = stepper(model.forms);
259− var w = newModel.w, h = newModel.h;
294+ var tracker = updateTracker(w,h,div);
260− ctx.clearRect(-w/2, -h/2, w, h);
295+ var ctx = null;
298+ while (formType = stpr.peekNext()) {
299+ if (ctx === null && formType !== 'FElement') {
300+ ctx = tracker.getContext(stpr.transforms());
302+ var form = stpr.next(ctx);
303+ if (formType === 'FElement') {
304+ tracker.element(stpr.transforms(), form);
306+ } else if (formType !== 'FGroup') {
307+ renderForm(function() { update(div, model, model); }, ctx, form);
263return { render:render, update:update };
313return { render:render, update:update };