on master help sign in
- Break common functions into Utils files.

- Add support for no-graphics mode (e.g. for use with node.js) - Change order of arguments for matrix transformms - Begin getting Collage.js in order.

commit4587d3d3e4f402112a4f5885ae6636cf0cb42df9 authorevancz <info@elm-lang.org> dateSun 17 Mar 2013, 21:36 (14 years ago) parentb1d0c74
7 files changed+159−136
libraries/Graphics/Element.elm+13−4 libraries/Native/Graphics/Matrix.js+3−3 runtime/Init.js+26−47 runtime/Render/Collage.js+50−57 runtime/Render/Element.js+4−25 runtime/Render/Utils.js+30 runtime/Utils.js+33
libraries/Graphics/Element.elm+13−4
@@ -63,7 +63,7 @@ data ElementPrim
6363 | Flow Direction [Element]
6464 | Spacer
6565 | RawHtml JSString
66− | DomNode JSElement
66+ | Custom -- for custom Elements implemented in JS, see collage for example
6767
6868data ImageStyle = Plain | Fitted | Cropped (Int,Int)
6969
@@ -94,9 +94,18 @@ flow dir es =
9494 DIn -> newFlow (List.maximum ws) (List.maximum hs)
9595 DOut -> newFlow (List.maximum ws) (List.maximum hs)
9696
97−above hi lo = newElement (max (widthOf hi) (widthOf lo)) (heightOf hi + heightOf lo) (Flow DDown [hi,lo])
98−below lo hi = newElement (max (widthOf hi) (widthOf lo)) (heightOf hi + heightOf lo) (Flow DDown [hi,lo])
99−beside lft rht = newElement (widthOf lft + widthOf rht) (max (heightOf lft) (heightOf rht)) (Flow right [lft,rht])
97+above hi lo =
98+ newElement (max (widthOf hi) (widthOf lo))
99+ (heightOf hi + heightOf lo)
100+ (Flow DDown [hi,lo])
101+below lo hi =
102+ newElement (max (widthOf hi) (widthOf lo))
103+ (heightOf hi + heightOf lo)
104+ (Flow DDown [hi,lo])
105+beside lft rht =
106+ newElement (widthOf lft + widthOf rht)
107+ (max (heightOf lft) (heightOf rht))
108+ (Flow right [lft,rht])
100109
101110layers es =
102111 let ws = List.map widthOf es
libraries/Native/Graphics/Matrix.js+3−3
@@ -27,10 +27,10 @@ Elm.Native.Graphics.Matrix = function(elm) {
2727 return new A([m0, m1, m0*x + m1*y + m[2],
2828 m3, m4, m3*x + m4*y + m[5]]);
2929 }
30− function matrix(a,b,c,d,e,f,m) {
30+ function matrix(n11, n12, n21, n22, dx, dy, m) {
3131 var m0 = m[0], m1 = m[1], m3 = m[3], m4 = m[4];
32− return new A([m0*a + m1*d, m0*b + m1*e, m0*c + m1*f + m[2],
33− m3*a + m4*d, m3*b + m4*e, m3*c + m4*f + m[5]]);
32+ return new A([m0*n11 + m1*n12, m0*n21 + m1*n22, m0*dx + m1*dy + m[2],
33+ m3*n11 + m4*n12, m3*n21 + m4*n22, m3*dx + m4*dy + m[5]]);
3434 }
3535
3636 return elm.Native.Graphics.Matrix = {
runtime/Init.js+26−47
@@ -19,9 +19,9 @@ Elm.init = function(module, baseNode) {
1919 }
2020
2121 // ensure that baseNode exists and is properly formatted.
22− var Render = ElmRuntime.Render.Element;
2322 if (typeof baseNode === 'undefined') {
24− baseNode = Render.newElement('div');
23+ var newElement = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
24+ baseNode = newElement('div');
2525 document.body.appendChild(baseNode);
2626 baseNode.style.width = window.innerWidth + 'px';
2727 baseNode.style.height = window.innerHeight + 'px';
@@ -31,7 +31,7 @@ Elm.init = function(module, baseNode) {
3131 baseNode.style.height = window.innerHeight + 'px';
3232 }, true);
3333
34− var style = Render.newElement('style');
34+ var style = newElement('style');
3535 style.type = 'text/css';
3636 style.innerHTML = "html,head,body { padding:0; margin:0; }" +
3737 "body { font-family: calibri, helvetica, arial, sans-serif; }";
@@ -40,38 +40,43 @@ Elm.init = function(module, baseNode) {
4040
4141 // create the actuall RTS. Any impure modules will attach themselves to this
4242 // object. This permits many Elm programs to be embedded per document.
43− var elm = { notify: notify, node: baseNode, id: ElmRuntime.guid(), inputs: inputs };
43+ var elm = {notify: notify, node: baseNode, id: ElmRuntime.guid(), inputs: inputs};
4444
45− // evaluate the given module and extract its 'main' value.
46− signalGraph = module(elm).main;
45+ // If graphics are enabled, set up the `main` value in baseNode.
46+ if (baseNode !== null) {
47+ var Render = ElmRuntime.use(ElmRuntime.Render.Element);
4748
48− // make sure the signal graph is actually a signal, extract the visual model,
49− // and filter out any unused inputs.
50− var Signal = Elm.Signal(elm);
51− if (!('recv' in signalGraph)) signalGraph = Signal.constant(signalGraph);
52− visualModel = signalGraph.value;
53− inputs = ElmRuntime.filterDeadInputs(inputs);
49+ // evaluate the given module and extract its 'main' value.
50+ signalGraph = module(elm).main;
5451
55− // Add the visualModel to the DOM
56− baseNode.appendChild(Render.render(visualModel));
52+ // make sure the signal graph is actually a signal, extract the visual model,
53+ // and filter out any unused inputs.
54+ var Signal = Elm.Signal(elm);
55+ if (!('recv' in signalGraph)) signalGraph = Signal.constant(signalGraph);
56+ visualModel = signalGraph.value;
57+ inputs = ElmRuntime.filterDeadInputs(inputs);
5758
58− if ('Window' in elm) {
59+ // Add the visualModel to the DOM
60+ baseNode.appendChild(Render.render(visualModel));
61+
62+ if ('Window' in elm) {
5963 var w = baseNode.clientWidth;
6064 if (w !== elm.Window.dimensions.value._0) {
6165 notify(elm.Window.dimensions.id,
6266 Elm.Native.Utils(elm).Tuple2(w, baseNode.clientHeight));
6367 }
64− }
68+ }
6569
66− // set up updates so that the DOM is adjusted as necessary.
67− var update = Render.update;
68− function domUpdate(value) {
70+ // set up updates so that the DOM is adjusted as necessary.
71+ var update = Render.update;
72+ function domUpdate(value) {
6973 update(baseNode.firstChild, visualModel, value);
7074 visualModel = value;
7175 return value;
72− }
76+ }
7377
74− signalGraph = A2(Signal.lift, domUpdate, signalGraph);
78+ signalGraph = A2(Signal.lift, domUpdate, signalGraph);
79+ }
7580
7681 function send(name, value) {
7782 var e = document.createEvent('Event');
@@ -85,29 +90,3 @@ Elm.init = function(module, baseNode) {
8590
8691 return { send : send, recv : recv };
8792};
88−
89−// Helper function to filter dead inputs. Not specific to initialization.
90−ElmRuntime = ElmRuntime || {};
91−ElmRuntime.counter = 0;
92−ElmRuntime.guid = function() { ++ElmRuntime.counter; return ElmRuntime.counter; }
93−ElmRuntime.filterDeadInputs = function() {
94− 'use strict';
95− function isAlive(input) {
96− if (!('defaultNumberOfKids' in input)) return true;
97− var len = input.kids.length;
98− if (len == 0) return false;
99− if (len > input.defaultNumberOfKids) return true;
100− var alive = false;
101− for (var i = len; i--; ) {
102− alive = alive || isAlive(input.kids[i]);
103− }
104− return alive;
105− }
106− return function filterDeadInputs(inputs) {
107− var temp = [];
108− for (var i = inputs.length; i--; ) {
109− if (isAlive(inputs[i])) temp.push(inputs[i]);
110− }
111− return temp;
112− };
113−}();
runtime/Render/Collage.js+50−57
@@ -1,45 +1,24 @@
11
2−ElmRuntime.Render.Collage = function(rts) {
3− /*
4−var JS = Elm.JavaScript;
2+ElmRuntime.Render.Collage = function() {
3+'use strict';
54
6−function tracePoints(ctx,points) {
5+var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
6+var newElement = Utils.newElement, addTo = Utils.addTo, extract = Utils.extract;
7+
8+function trace(ctx, path) {
79 var i = points.length - 1;
810 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+ ctx.moveTo(points[i]._0, points[i]._1);
12+ while (i--) { ctx.lineTo(points[i]._0, points[i]._1); }
1113}
1214
13−function solid(ctx,color,points) {
14− tracePoints(ctx,points);
15− ctx.strokeStyle = Elm.Color.extract(color);
15+function line(ctx,style,path) {
16+ var isSolid = style.dashing.ctor === 'Nil';
17+ isSolid ? trace(ctx, path) : customLineHelp(ctx, style, path);
1618 ctx.stroke();
17−};
18−
19−function filled(ctx,color,points) {
20− tracePoints(ctx,points);
21− ctx.fillStyle = Elm.Color.extract(color);
22− ctx.fill();
2319}
2420
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) {
21+function customLineHelp(ctx, pattern, points) {
4322 var i = points.length - 1;
4423 if (i <= 0) return;
4524 var x0 = points[i][1], y0 = points[i][2];
@@ -69,17 +48,32 @@ var customLineHelp = function(ctx, pattern, points) {
6948 }
7049 x0 = x1; y0 = y1;
7150 }
72−};
51+}
7352
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−};
53+function drawLine(ctx, style, path) {
54+ ctx.lineWidth = style.width;
55+ ctx.lineCap = style.cap.ctor.toLowerCase();
56+ ctx.lineJoin = style.join.ctor.toLowerCase();
57+ ctx.miterLimit = style.miterLimit;
58+ ctx.strokeStyle = extract(style.color);
59+ return line(ctx, style, path);
60+}
61+
62+function filled(ctx,color,points) {
63+ tracePoints(ctx,points);
64+ ctx.fillStyle = extract(color);
65+ ctx.fill();
66+}
67+
68+function textured(redo,ctx,src,points) {
69+ var img = new Image();
70+ img.src = JS.castStringToJSString(src);
71+ img.onload = redo;
72+
73+ tracePoints(ctx,points);
74+ ctx.fillStyle = ctx.createPattern(img,'repeat');
75+ ctx.fill();
76+}
8377
8478function drawShape(redo,ctx,shapeStyle,color,points) {
8579 switch(shapeStyle[0]) {
@@ -98,25 +92,24 @@ function drawImage(redo,ctx,w,h,src) {
9892 ctx.drawImage(img,-w/2,-h/2,w,h);
9993}
10094
101−function renderForm(redo,ctx,theta,scale,x,y,form) {
95+function renderForm(redo,ctx,form) {
10296 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);
97+ var m = form.transform;
98+ ctx.transform(m[0], m[3], m[1], m[4], m[2], m[5]);
10699 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;
100+ switch(form.form.ctor) {
101+ case 'FPath' : drawLine(ctx, form._0, form._1); break;
102+ case 'FShape': drawShape(redo, ctx, form._0, form._1); break;
103+ case 'FImage': drawImage(redo, ctx, form._0, form._1, form._2, form._3); break;
104+ case 'FGroup': renderForms(redo, ctx, form.form._0); break;
111105 }
112106 ctx.restore();
113−};
107+}
114108
115−function renderForms(redo,ctx,w,h,forms) {
116− ctx.clearRect(0,0,w,h);
109+function renderForms(redo, ctx, forms) {
110+ forms = toArray(forms);
117111 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]);
112+ renderForm(redo,ctx,forms[i]);
120113 }
121114}
122115
@@ -313,5 +306,5 @@ function insideShape(point,theta,scale,pos,points) {
313306}
314307
315308return {collage:collage, updateCollage:updateCollage, insideForm:insideForm};
316− */
309+
317310};
runtime/Render/Element.js+4−25
@@ -2,24 +2,8 @@
22ElmRuntime.Render.Element = function() {
33'use strict';
44
5−var fromList = Elm.JavaScript({}).fromList;
6−var eq = Elm.Native.Utils({}).eq;
7−
8−function newElement(elementType) {
9− var e = document.createElement(elementType);
10− e.style.padding = "0";
11− e.style.margin = "0";
12− return e;
13−}
14−
15−function addTo(container, elem) {
16− container.appendChild(elem);
17−}
18−
19−function extract(c) {
20− if (c._3 === 1) { return 'rgb(' + c._0 + ',' + c._1 + ',' + c._2 + ')'; }
21− return 'rgba(' + c._0 + ',' + c._1 + ',' + c._2 + ',' + c._3 + ')';
22−}
5+var Utils = use(ElmRuntime.Render.Utils);
6+var newElement = Utils.newElement, addTo = Utils.addTo, extract = Utils.extract;
237
248function setProps(props, e) {
259 e.style.width = (props.width |0) + 'px';
@@ -279,11 +263,6 @@ function update(node, curr, next) {
279263 }
280264}
281265
282−return {
283− render:render,
284− update:update,
285− addTo:addTo,
286− newElement:newElement
287−};
266+return { render:render, update:update };
288267
289−}();
268+};
runtime/Render/Utils.jsnew file+30
@@ -0,0 +1,30 @@
1+
2+ElmRuntime.Render.Utils = function() {
3+'use strict';
4+
5+function newElement(elementType) {
6+ var e = document.createElement(elementType);
7+ e.style.padding = "0";
8+ e.style.margin = "0";
9+ return e;
10+}
11+
12+function addTo(container, elem) {
13+ container.appendChild(elem);
14+}
15+
16+function extract(c) {
17+ if (c._3 === 1) { return 'rgb(' + c._0 + ',' + c._1 + ',' + c._2 + ')'; }
18+ return 'rgba(' + c._0 + ',' + c._1 + ',' + c._2 + ',' + c._3 + ')';
19+}
20+
21+var fromList = Elm.JavaScript({}).fromList;
22+var eq = Elm.Native.Utils({}).eq;
23+
24+return {addTo:addTo,
25+ newElement:newElement,
26+ extract:extract,
27+ fromList:fromList,
28+ eq:eq
29+ };
30+};
runtime/Utils.jsnew file+33
@@ -0,0 +1,33 @@
1+
2+(function() {
3+'use strict';
4+
5+ElmRuntime.counter = 0;
6+ElmRuntime.guid = function() { return ElmRuntime.counter++; }
7+
8+ElmRuntime.use = function(M) {
9+ if (typeof M === 'function') M = M();
10+ return M;
11+};
12+
13+function isAlive(input) {
14+ if (!('defaultNumberOfKids' in input)) return true;
15+ var len = input.kids.length;
16+ if (len == 0) return false;
17+ if (len > input.defaultNumberOfKids) return true;
18+ var alive = false;
19+ for (var i = len; i--; ) {
20+ alive = alive || isAlive(input.kids[i]);
21+ }
22+ return alive;
23+}
24+
25+ElmRuntime.filterDeadInputs = function(inputs) {
26+ var temp = [];
27+ for (var i = inputs.length; i--; ) {
28+ if (isAlive(inputs[i])) temp.push(inputs[i]);
29+ }
30+ return temp;
31+};
32+
33+}());