Get sprites working in collage. temporary fix for docs.json issue. Looks like git thinks we're in the past, so a bunch of changes that have already happened as well.

This commit is contained in:
evancz 2013-03-21 02:29:23 -07:00
parent 7dbb052b22
commit 131a2b6818
18 changed files with 349 additions and 467 deletions

View file

@ -81,7 +81,8 @@ Library
indents,
filepath,
template-haskell,
json
json,
directory
Executable elm
Main-is: Compiler.hs
@ -132,7 +133,8 @@ Executable elm
indents,
filepath,
template-haskell,
json
json,
directory
Executable elm-doc
Main-is: Docs.hs

View file

@ -50,7 +50,7 @@ appendJS file = do
appendFile rts str
appendElm lbi file = do
system ((show $ elm lbi) ++ " --only-js " ++ file)
system (elm lbi ++ " --only-js " ++ file)
let jsFile = replaceExtension file ".js"
appendJS jsFile
removeFile jsFile
@ -68,5 +68,5 @@ buildRuntime lbi = do
buildTypes lbi = do
files <- getFiles ".elm" "libraries"
system ((show $ elm_doc lbi) ++ " " ++ unwords files ++ " > " ++ types)
system (elm_doc lbi ++ " " ++ unwords files ++ " > " ++ types)
putStrLn "Custom build step completed: elm-doc"

View file

@ -19,31 +19,44 @@ main = do
Left err -> putStrLn err >> exitFailure
Right ms -> putStrLn (toModules ms)
toModules ms =
"{ \"modules\" : [\n " ++ intercalate ",\n " (map toModule ms) ++ "\n ]\n}"
toModules ms = wrap (intercalate ",\n " (map toModule ms))
where wrap s = "{ \"modules\" : [\n " ++ s ++ "\n ]\n}"
toModule (name, values) =
"{ \"name\" : " ++ show name ++ ",\n \"values\" : [\n " ++ vs ++ "\n ]\n }"
where vs = intercalate ",\n " (map toValue values)
"{ \"name\" : " ++ show name ++ ",\n " ++
"\"values\" : [\n " ++ vs ++ "\n ]\n }"
where vs = intercalate ",\n " (map toValue values)
toValue (name, tipe) =
"{ \"name\" : " ++ show name ++ ",\n \"type\" : \"" ++ show tipe ++ "\"\n }"
toValue (name, tipe, desc) =
"{ \"name\" : " ++ show name ++
",\n \"type\" : \"" ++ show tipe ++
",\n \"desc\" : " ++ show desc ++ "\n }"
docParse :: String -> Either String (String, [(String, Type)])
docParse :: String -> Either String (String, [(String, Type, String)])
docParse = setupParser $ do
optional freshLine
(,) <$> option "Main" moduleName <*> types
where
skip = manyTill anyChar simpleNewline >> return []
end = many1 anyChar >> return []
tipe = get <$> try typeAnnotation
get stmt = case stmt of { TypeAnnotation n t -> [(n,t)] ; _ -> [] }
types = concat <$> many (tipe <|> try skip <|> end)
types = concat <$> many (docs <|> try skip <|> end)
getName = intercalate "." . fst
moduleName = do optional freshLine
getName <$> moduleDef `followedBy` freshLine
docs :: IParser [(String, Type, String)]
docs = (tipe <$> try typeAnnotation) <|> commentTipe
where
clip str = case str of { ' ':rest -> rest ; _ -> str }
tipe stmt = case stmt of { TypeAnnotation n t -> [(n,t,"")] ; _ -> [] }
commentTipe = do
cs <- map clip <$> many1 lineComment
typ <- optionMaybe (try typeAnnotation)
return $ case typ of
Just (TypeAnnotation n t) -> [(n, t, intercalate "\n" cs)]
_ -> []
setupParser p source =
case iParse p "" source of
Right result -> Right result

View file

@ -62,11 +62,11 @@ jsModule (Module names exports imports stmts) =
defs = assign "$op" "{}"
program = "\nvar " ++ usefulFuncs ++ ";" ++ defs ++ includes ++ body ++
setup ("elm":"Native":modNames) ++
assign "_" ("elm.Native." ++ modName ++ " || {}") ++
assign "_" ("elm.Native." ++ modName ++ "||{}") ++
getExports exports stmts ++ setup ("elm":modNames) ++
ret (assign' ("elm." ++ modName) "_") ++ "\n"
setup modNames = concatMap (\n -> globalAssign n $ n ++ " || {}") .
map (intercalate ".") . tail . inits $ init modNames
setup modNames = concatMap (\n -> globalAssign n $ n ++ "||{}") .
map (intercalate ".") . drop 2 . inits $ init modNames
usefulFuncs = intercalate ", " (map (uncurry assign') internalImports)
getExports names stmts = "\n"++ intercalate ";\n" (op : map fnPair fns)
@ -95,13 +95,25 @@ jsImport (modul, how) =
case how of
As name -> assign name ("Elm." ++ modul ++ parens "elm")
Importing vs ->
"\nvar $ = Elm." ++ modul ++ parens "elm" ++ ";" ++
"\nvar $ = Elm." ++ modul ++ parens "elm" ++ ";" ++ setup modul ++
if null vs then "\nfor (var k in $) {eval('var '+k+'=$[\"'+k+'\"]')}"
else "\nvar " ++ intercalate ", " (map def vs) ++ ";"
where
imprt v = assign' v ("$." ++ v)
def (o:p) =
imprt (if isOp o then "$op['" ++ o:p ++ "']" else deprime (o:p))
def (o:p) = imprt (if isOp o then "$op['" ++ o:p ++ "']" else deprime (o:p))
setup moduleName =
"\nvar " ++ concatMap (++";") (defs ++ [assign' moduleName "$"])
where
defs = map (\n -> assign' n (n ++ "||{}")) (subnames moduleName)
subnames = map (intercalate ".") . tail . inits . init . split
split names = case go [] names of
(name, []) -> [name]
(name, ns) -> name : split ns
go name str = case str of
'.':rest -> (reverse name, rest)
c:rest -> go (c:name) rest
[] -> (reverse name, [])
stmtsToJS :: [Statement] -> String

View file

@ -14,7 +14,7 @@
at <https://github.com/tazjin/Elm/tree/master/Examples>
-}
module Language.Elm (
ElmSource (..),
compile, toHtml,
runtimeLocation
) where
@ -30,52 +30,21 @@ import Paths_Elm
import qualified Data.Text as TS
import qualified Data.Text.Lazy as TL
-- |This function compiles Elm code to three separate parts: HTML, CSS,
-- and JavaScript. The HTML is only the contents of the body, so the three
-- parts must be combined in a basic HTML skeleton.
compile :: String -> String
compile source = either showErr jsModule modul
where modul = buildFromSource source
-- |This function compiles Elm code into a full HTML page.
toHtml :: String -- ^ Location of elm-min.js as expected by the browser
-> String -- ^ The page title
-> String -- ^ The elm source code
-> Html
toHtml = generateHtml
-- |The absolute path to Elm's runtime system.
runtimeLocation :: IO FilePath
runtimeLocation = getDataFileName "elm-runtime.js"
class ElmSource a where
-- |This function compiles Elm code to three separate parts: HTML, CSS,
-- and JavaScript. The HTML is only the contents of the body, so the three
-- parts must be combined in a basic HTML skeleton.
compile :: a -> String
-- |This function compiles Elm code into a full HTML page.
toHtml :: String -- ^ Location of elm-min.js as expected by the browser
-> String -- ^ The page title
-> a -- ^ The elm source code
-> Html
instance ElmSource String where
compile = compileHelper
toHtml = generateHtml
instance ElmSource Elm where
compile = compileHelper . TL.unpack . renderElm
toHtml elmL title = generateHtml elmL title . TL.unpack . renderElm
-- |Strict text
instance ElmSource TS.Text where
compile = compileHelper . TS.unpack
toHtml elmL title = generateHtml elmL title . TS.unpack
-- |Lazy text
instance ElmSource TL.Text where
compile = compileHelper . TL.unpack
toHtml elmL title = generateHtml elmL title . TL.unpack
-- | (urlRenderFn, urlRenderFn -> Elm)
instance ElmSource (t, t -> Elm) where
compile (f, s) = compileHelper $ TL.unpack $ renderElm $ s f
toHtml elmL title (f, s) = generateHtml elmL title $ TL.unpack $ renderElm $ s f
-- | to be used without URL interpolation
instance ElmSource (t -> Elm) where
compile s = compileHelper $ TL.unpack $ renderElm $ s undefined
toHtml l t s = generateHtml l t $ TL.unpack $ renderElm $ s undefined
-- build helper to avoid boilerplate repetition
compileHelper :: String -> String
compileHelper source = either showErr jsModule modul
where modul = buildFromSource source

View file

@ -13,7 +13,7 @@ libraries = case getLibs of
getLibs :: Result (Map.Map String (Map.Map String String))
getLibs = do
obj <- decodeStrict $(docs) :: Result (JSObject JSValue)
obj <- decodeStrict "{\"modules\":[]}" -- $(docs) :: Result (JSObject JSValue)
modules <- valFromObj "modules" obj :: Result [JSObject JSValue]
Map.fromList `fmap` mapM getValues modules

View file

@ -1,13 +1,22 @@
module LoadLibraries where
module LoadLibraries (docs) where
import Language.Haskell.TH.Syntax
import Paths_Elm
import System.Directory
docs :: Q Exp
docs = liftString =<< qRunIO (do
s <- readFile =<< getDataFileName "docs.json"
putStrLn s
return s
)
docs = liftString =<< qRunIO readDocs
readDocs :: IO String
readDocs = do
name <- getDataFileName "docs.json"
exists <- doesFileExist name
if exists then readFile name
else putStrLn warning >> return "{\"modules\":[]}"
warning = "Warning! Types for standard library not loaded properly!\n\
\Run the following commands after compilation:\n\
\\n\
\ touch compiler/Model/LoadLibraries.hs\n\
\ cabal install\n\n"

View file

@ -5,12 +5,19 @@ import Either
import Graphics.LineStyle as LS
import Graphics.Geometry
import Native.Graphics.Matrix as Matrix
import Native.Graphics.Collage as N
import Graphics.Element
type Form = { transform : Matrix, form : BasicForm }
type Form = {
theta : Float,
scale : Float,
x : Float,
y : Float,
form : BasicForm
}
data FillStyle
= NoFill
| Solid Color
= Solid Color
| Texture String
| Gradient Gradient
@ -21,9 +28,9 @@ data BasicForm
| FElement Element
| FGroup [Form]
ident = Matrix.identity
form f = { theta = 0, scale = 1, x = 0, y = 0, form = f }
fill style shape = Form ident (FShape (Right style) shape)
fill style shape = form (FShape (Right style) shape)
filled : Color -> Shape -> Form
filled color shape = fill (Solid color) shape
@ -35,26 +42,27 @@ gradient : Gradient -> Shape -> Form
gradient grad shape = fill (Gradient grad) shape
outline : LineStyle -> Shape -> Form
outline style shape = Form ident (FShape (Left style) shape)
outline style shape = form (FShape (Left style) shape)
trace : LineStyle -> Path -> Form
trace style path = Form ident (FPath style path)
trace style path = form (FPath style path)
sprite : Int -> Int -> (Int,Int) -> String -> Form
sprite w h pos src = Form ident (FImage w h pos src)
sprite w h pos src = form (FImage w h pos src)
toForm : Element -> Form
toForm e = Form ident (FElement e)
toForm e = form (FElement e)
group fs = Form ident (FGroup fs)
group : [Form] -> Form
group fs = form (FGroup fs)
rotate t f = { form = f.form, transform = Matrix.rotate t f.transform }
scale s f = { form = f.form, transform = Matrix.scale s s f.transform }
scaleX s f = { form = f.form, transform = Matrix.scale s 1 f.transform }
scaleY s f = { form = f.form, transform = Matrix.scale 1 s f.transform }
move x y f = { form = f.form, transform = Matrix.move x y f.transform }
moveX x f = { form = f.form, transform = Matrix.move x 0 f.transform }
moveY y f = { form = f.form, transform = Matrix.move 0 y f.transform }
rotate : Float -> Form -> Form
rotate t f = { f | theta <- f.theta + t }
transform u v w x y z f =
{ form = f.form, transform = Matrix.matrix u v w x y z f.transform }
scale s f = { f | scale <- f.scale * s }
move x y f = { f | x <- f.x + x, y <- f.y + y }
moveX x f = { f | x <- f.x + x }
moveY y f = { f | y <- f.y + y }
collage : Int -> Int -> [Form] -> Element

View file

@ -63,7 +63,7 @@ data ElementPrim
| Flow Direction [Element]
| Spacer
| RawHtml JSString
| DomNode JSElement
| Custom -- for custom Elements implemented in JS, see collage for example
data ImageStyle = Plain | Fitted | Cropped (Int,Int)
@ -94,9 +94,18 @@ flow dir es =
DIn -> newFlow (List.maximum ws) (List.maximum hs)
DOut -> newFlow (List.maximum ws) (List.maximum hs)
above hi lo = newElement (max (widthOf hi) (widthOf lo)) (heightOf hi + heightOf lo) (Flow DDown [hi,lo])
below lo hi = newElement (max (widthOf hi) (widthOf lo)) (heightOf hi + heightOf lo) (Flow DDown [hi,lo])
beside lft rht = newElement (widthOf lft + widthOf rht) (max (heightOf lft) (heightOf rht)) (Flow right [lft,rht])
above hi lo =
newElement (max (widthOf hi) (widthOf lo))
(heightOf hi + heightOf lo)
(Flow DDown [hi,lo])
below lo hi =
newElement (max (widthOf hi) (widthOf lo))
(heightOf hi + heightOf lo)
(Flow DDown [hi,lo])
beside lft rht =
newElement (widthOf lft + widthOf rht)
(max (heightOf lft) (heightOf rht))
(Flow right [lft,rht])
layers es =
let ws = List.map widthOf es

View file

@ -2,12 +2,14 @@
-- These structures are purely geometric forms, no colors or styles.
-- Could be the basis of some cool geometry stuff :)
module Geometry where
module Graphics.Geometry where
data Path = Path [(Float,Float)]
import List
path = Path
segment p1 p2 = Path [p1,p2]
type Path = [(Float,Float)]
path ps = ps
segment p1 p2 = [p1,p2]
type Shape = [(Float,Float)]
@ -17,12 +19,12 @@ rect w h = [ (0-w/2,0-h/2), (0-w/2,h/2), (w/2,h/2), (w/2,0-h/2) ]
oval w h =
let n = 50
f i = (w/2 * cos (2*pi/n * i), h/2 * sin (2*pi/n * i))
f i = (w/2 * Math.cos (2*Math.pi/n * i), h/2 * Math.sin (2*Math.pi/n * i))
in map f [0..n-1]
circle r = oval (2*r) (2*r)
ngon n r =
let m = toFloat n
f i = ( r * cos (2*pi/m * i), r * sin (2*pi/m * i))
f i = ( r * Math.cos (2*Math.pi/m * i), r * Math.sin (2*Math.pi/m * i))
in map f [0..n-1]

View file

@ -4,7 +4,7 @@ module Graphics.LineStyle where
import Graphics.Color as Color
data LineCap = Butt | Round | Square
data LineJoin = Soft | Sharp | Clip
data LineJoin = Smooth | Sharp | Clipped
type LineStyle = {
color : Color,
@ -17,10 +17,10 @@ type LineStyle = {
}
default = {
color = black,
color = Color.black,
width = 1,
cap = Butt,
join = Miter,
join = Sharp,
dashing = [],
dashOffset = 0,
miterLimit = 10

View file

@ -50,14 +50,13 @@ unzip pairs =
[] -> ([],[])
(x,y)::ps -> let (xs,ys) = (unzip ps) in (x::xs,y::ys)
split : [a] -> [a] -> [[a]]
join : [a] -> [[a]] -> [a]
intersperse : a -> [a] -> [a]
intersperse sep xs =
case xs of
a::b::cs -> a :: sep :: intersperse sep (b::cs)
[a] -> [a]
[] -> []
intercalate sep xs =
case xs of
a::b::cs -> a ++ sep ++ intercalate sep (b::cs)
[a] -> a
[] -> []

View file

@ -27,15 +27,15 @@ Elm.Native.Graphics.Matrix = function(elm) {
return new A([m0, m1, m0*x + m1*y + m[2],
m3, m4, m3*x + m4*y + m[5]]);
}
function matrix(a,b,c,d,e,f,m) {
function matrix(n11, n12, n21, n22, dx, dy, m) {
var m0 = m[0], m1 = m[1], m3 = m[3], m4 = m[4];
return new A([m0*a + m1*d, m0*b + m1*e, m0*c + m1*f + m[2],
m3*a + m4*d, m3*b + m4*e, m3*c + m4*f + m[5]]);
return new A([m0*n11 + m1*n12, m0*n21 + m1*n22, m0*dx + m1*dy + m[2],
m3*n11 + m4*n12, m3*n21 + m4*n22, m3*dx + m4*dy + m[5]]);
}
return elm.Native.Graphics.Matrix = {
identity:identity,
rotation:F2(rotation),
rotate:F2(rotate),
scale:F3(scale),
move:F3(move),
matrix:F7(matrix)

View file

@ -223,6 +223,50 @@ Elm.Native.List = function(elm) {
return xs;
}
function join(sep, xss) {
if (xss.ctor === 'Nil') return {ctor:'Nil'}
var s = toArray(sep);
var out = toArray(xss._0);
xss = xss._1;
while (xss.ctor === "Cons") {
out = out.concat(s, toArray(xss._0));
xss = xss._1;
}
return fromArray(out);
}
function split(sep, xs) {
var out = {ctor:'Nil'};
var array = toArray(xs);
var s = toArray(sep);
var slen = s.length;
if (slen === 0) {
for (var i = array.length; i--; ) {
out = Cons(Cons(array[i],Nil), out);
}
return out;
}
var temp = {ctor:'Nil'};
for (var i = array.length - slen; i >= 0; --i) {
var match = true;
for (var j = slen; j--; ) {
if (!Utils.eq(array[i+j], s[j])) { match = false; break; }
}
if (match) {
out = {ctor:'Cons', _0:temp, _1:out};
temp = {ctor:'Nil'};
i -= slen - 1;
} else {
temp = {ctor:'Cons', _0:array[i+j], _1:temp};
}
}
for (var j = slen-1; j--; ) {
temp = {ctor:'Cons', _0:array[j], _1:temp};
}
out = {ctor:'Cons', _0:temp, _1:out};
return out;
}
return elm.Native.List = {
Nil:Nil,
Cons:Cons,
@ -255,7 +299,10 @@ Elm.Native.List = function(elm) {
zip:F2(zip),
sort:sort,
take:F2(take),
drop:F2(drop)
drop:F2(drop),
join:F2(join),
split:F2(split)
};
};

View file

@ -56,14 +56,19 @@ Elm.Native.Show = function(elm) {
} else if ('ctor' in v) {
if (v.ctor.substring(0,5) === "Tuple") {
var output = [];
for (var k in v) { if (k === 'ctor') continue; output.push(toString(v[k])); }
for (var k in v) {
if (k === 'ctor') continue;
output.push(toString(v[k]));
}
return "(" + output.join(",") + ")";
} else if (v.ctor === "Cons") {
var isStr = typeof v._0 === "string";
var start = isStr ? '"' : "[";
var end = isStr ? '"' : "]";
var sep = isStr ? "" : ",";
var f = isStr ? function(x){return x} : toString;
var isStr = typeof v._0 === "string",
start = isStr ? '"' : "[",
end = isStr ? '"' : "]",
sep = isStr ? "" : ",",
f = !isStr ? toString : function(x){
return x === '\n' ? '\\n' : x;
};
var output = start + f(v._0);
v = v._1;
while (v.ctor === "Cons") {

View file

@ -19,9 +19,9 @@ Elm.init = function(module, baseNode) {
}
// ensure that baseNode exists and is properly formatted.
var Render = ElmRuntime.Render.Element;
if (typeof baseNode === 'undefined') {
baseNode = Render.newElement('div');
var newElement = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
baseNode = newElement('div');
document.body.appendChild(baseNode);
baseNode.style.width = window.innerWidth + 'px';
baseNode.style.height = window.innerHeight + 'px';
@ -31,7 +31,7 @@ Elm.init = function(module, baseNode) {
baseNode.style.height = window.innerHeight + 'px';
}, true);
var style = Render.newElement('style');
var style = newElement('style');
style.type = 'text/css';
style.innerHTML = "html,head,body { padding:0; margin:0; }" +
"body { font-family: calibri, helvetica, arial, sans-serif; }";
@ -40,21 +40,38 @@ Elm.init = function(module, baseNode) {
// create the actuall RTS. Any impure modules will attach themselves to this
// object. This permits many Elm programs to be embedded per document.
var elm = { notify: notify, node: baseNode, id: ElmRuntime.guid(), inputs: inputs };
var elm = {notify: notify, node: baseNode, id: ElmRuntime.guid(), inputs: inputs};
// Set up methods to communicate with Elm program from JS.
function send(name, value) {
var e = document.createEvent('Event');
e.initEvent(name + '_' + elm.id, true, true);
e.value = value;
document.dispatchEvent(e);
}
function recv(name, handler) {
document.addEventListener(name + '_' + elm.id, handler);
}
// If graphics are not enabled, escape early, skip over setting up DOM stuff.
if (baseNode === null) return { send : send, recv : recv };
var Render = ElmRuntime.use(ElmRuntime.Render.Element);
// evaluate the given module and extract its 'main' value.
signalGraph = module(elm).main;
// make sure the signal graph is actually a signal, extract the visual model,
// and filter out any unused inputs.
var Signal = Elm.Signal(elm);
if (!('recv' in signalGraph)) signalGraph = Signal.constant(signalGraph);
visualModel = signalGraph.value;
inputs = ElmRuntime.filterDeadInputs(inputs);
// Add the visualModel to the DOM
baseNode.appendChild(Render.render(visualModel));
if ('Window' in elm) {
var w = baseNode.clientWidth;
if (w !== elm.Window.dimensions.value._0) {
@ -70,44 +87,8 @@ Elm.init = function(module, baseNode) {
visualModel = value;
return value;
}
signalGraph = A2(Signal.lift, domUpdate, signalGraph);
function send(name, value) {
var e = document.createEvent('Event');
e.initEvent(name + '_' + elm.id, true, true);
e.value = value;
document.dispatchEvent(e);
}
function recv(name, handler) {
document.addEventListener(name + '_' + elm.id, handler);
}
return { send : send, recv : recv };
};
// Helper function to filter dead inputs. Not specific to initialization.
ElmRuntime = ElmRuntime || {};
ElmRuntime.counter = 0;
ElmRuntime.guid = function() { ++ElmRuntime.counter; return ElmRuntime.counter; }
ElmRuntime.filterDeadInputs = function() {
'use strict';
function isAlive(input) {
if (!('defaultNumberOfKids' in input)) return true;
var len = input.kids.length;
if (len == 0) return false;
if (len > input.defaultNumberOfKids) return true;
var alive = false;
for (var i = len; i--; ) {
alive = alive || isAlive(input.kids[i]);
}
return alive;
}
return function filterDeadInputs(inputs) {
var temp = [];
for (var i = inputs.length; i--; ) {
if (isAlive(inputs[i])) temp.push(inputs[i]);
}
return temp;
};
}();

View file

@ -1,45 +1,29 @@
ElmRuntime.Render.Collage = function(rts) {
/*
var JS = Elm.JavaScript;
ElmRuntime.Render.Collage = function() {
'use strict';
function tracePoints(ctx,points) {
var Render = ElmRuntime.use(ElmRuntime.Render.Element);
var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
var newElement = Utils.newElement, addTo = Utils.addTo,
extract = Utils.extract, fromList = Utils.fromList,
fromString = Utils.fromString, addTransform = Utils.addTransform;
function trace(ctx, path) {
var points = fromList(path);
console.log(points);
var i = points.length - 1;
if (i <= 0) return;
ctx.moveTo(points[i][1], points[i][2]);
while (i--) { ctx.lineTo(points[i][1], points[i][2]); }
ctx.moveTo(points[i]._0, points[i]._1);
while (i--) { ctx.lineTo(points[i]._0, points[i]._1); }
}
function solid(ctx,color,points) {
tracePoints(ctx,points);
ctx.strokeStyle = Elm.Color.extract(color);
function line(ctx,style,path) {
var isSolid = style.dashing.ctor === 'Nil';
isSolid ? trace(ctx, path) : customLineHelp(ctx, style, path);
ctx.stroke();
};
function filled(ctx,color,points) {
tracePoints(ctx,points);
ctx.fillStyle = Elm.Color.extract(color);
ctx.fill();
}
function textured(redo,ctx,src,points) {
var img = new Image();
img.src = JS.castStringToJSString(src);
img.onload = redo;
tracePoints(ctx,points);
ctx.fillStyle = ctx.createPattern(img,'repeat');
ctx.fill();
}
function customLine(pattern,ctx,color,points) {
if (pattern.length === 0) { pattern = [8,4]; }
customLineHelp(ctx, pattern, points);
ctx.strokeStyle = Elm.Color.extract(color);
ctx.stroke();
};
var customLineHelp = function(ctx, pattern, points) {
function customLineHelp(ctx, pattern, points) {
var i = points.length - 1;
if (i <= 0) return;
var x0 = points[i][1], y0 = points[i][2];
@ -69,249 +53,126 @@ var customLineHelp = function(ctx, pattern, points) {
}
x0 = x1; y0 = y1;
}
};
function drawLine(ctx,form) {
var points = form[3][1];
switch(form[1][0]) {
case "Solid" : return solid(ctx,form[2],points);
case "Dotted": return customLine([3,3],ctx,form[2],points);
case "Dashed": return customLine([8,4],ctx,form[2],points);
case "Custom": return customLine(form[1][1],ctx,form[2],points);
}
};
function drawShape(redo,ctx,shapeStyle,color,points) {
switch(shapeStyle[0]) {
case "Filled": return filled(ctx,color,points);
case "Outlined": return solid(ctx,color,points);
case "Textured": return textured(redo,ctx,shapeStyle[1],points);
case "CustomOutline":
return customLine(shapeStyle[1],ctx,color,points);
}
};
function drawImage(redo,ctx,w,h,src) {
var img = new Image();
img.onload = redo;
img.src = JS.castStringToJSString(src);
ctx.drawImage(img,-w/2,-h/2,w,h);
}
function renderForm(redo,ctx,theta,scale,x,y,form) {
function drawLine(ctx, style, path) {
ctx.lineWidth = style.width;
ctx.lineCap = style.cap.ctor.toLowerCase();
ctx.lineJoin = style.join.ctor.toLowerCase();
ctx.miterLimit = style.miterLimit;
ctx.strokeStyle = extract(style.color);
return line(ctx, style, path);
}
function texture(redo, ctx, src) {
var img = new Image();
img.src = fromString(src);
img.onload = redo;
return ctx.createPattern(img, 'repeat');
}
function gradient(ctx, grad) {
var g;
if (grad.ctor === 'Linear') {
var p1 = grad._1, p2 = grad._2;
g = ctx.createLinearGradient(p1._0, p1._1, p2._0, p2._1);
} else {
var p1 = grad._1, p2 = grad._3;
g = ctx.createRadialGradient(p1._0, p1._1, grad._2, p2._0, p2._1, grad._4);
}
var stops = fromList(grad._0);
for (var i = stops.length; i--; ) {
var stop = stops[i];
g.addColorStop(stop._0, extract(stop._1));
}
return g;
}
function drawShape(redo, ctx, style, path) {
console.log(style, path);
trace(ctx, path);
var sty = style.ctor;
ctx.fillStyle =
sty === 'Solid' ? extract(style._0) :
sty === 'Texture' ? texture(redo, ctx, style._0) : gradient(ctx, style._0);
ctx.fill();
}
function drawImage(redo, ctx, form) {
var img = new Image();
img.onload = redo;
img.src = fromString(form._3);
var w = form._0, h = form._1, pos = form._2;
console.log(w,h,pos);
ctx.drawImage(img, pos._0, pos._1, w, h, -w/2, -h/2, w, h);
}
function renderForm(redo,ctx,form) {
ctx.save();
if (x !== 0 || y !== 0) ctx.translate(x,y);
if (theta !== ~~theta) ctx.rotate(2*Math.PI*theta);
if (scale !== 1) ctx.scale(scale,scale);
if (form.x !== 0 || form.y !== 0) ctx.translate(form.x, form.y);
if (form.theta !== 0) ctx.rotate(form.theta);
if (form.scale !== 1) ctx.scale(form.scale, form.scale);
ctx.beginPath();
switch(form[0]) {
case "FLine": drawLine(ctx,form); break;
case "FShape": drawShape(redo,ctx,form[1],form[2],form[3][1]); break;
case "FImage": drawImage(redo,ctx,form[1],form[2],form[3]); break;
var f = form.form;
console.log(f);
switch(f.ctor) {
case 'FPath' : drawLine(ctx, f._0, f._1); break;
case 'FShape':
if (form.form._0.ctor === 'Left') drawLine(ctx, f._0._0, f._1);
else drawShape(redo, ctx, f._0._0, f._1);
break;
case 'FImage': drawImage(redo, ctx, f); break;
case 'FGroup': renderForms(redo, ctx, f._0); break;
}
ctx.restore();
};
}
function renderForms(redo,ctx,w,h,forms) {
ctx.clearRect(0,0,w,h);
for (var i = forms.length; i--; ) {
var f = forms[i];
renderForm(redo,ctx,f[1],f[2],f[3][1],f[3][2],f[4]);
function renderForms(redo, ctx, forms) {
var fs = fromList(forms);
console.log(fs);
for (var i = 0, len = fs.length; i < len; ++i) {
renderForm(redo,ctx,fs[i]);
}
}
function collageForms(w,h,forms) {
var canvas = Render.newElement('canvas');
w = ~~w;
h = ~~h;
var canvas = newElement('canvas');
w = w|0;
h = h|0;
canvas.style.width = w + 'px';
canvas.style.height = h + 'px';
canvas.style.display = "block";
canvas.width = w;
canvas.height = h;
function redo() { renderForms(this,ctx,forms); }
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
function redo() { renderForms(this,ctx,w,h,forms); }
renderForms(redo,ctx,w,h,forms);
console.log(forms);
renderForms(redo,ctx,forms);
return canvas;
}
canvas.innerHTML = "Your browser does not support the canvas element.";
canvas.innerHTML = "Your browser does not support canvas.";
return canvas;
}
function applyTransforms(theta,scale,x,y,w,h,e) {
var t = "translate(" + (x - w / 2) + "px,"+ (y - h / 2) + "px)";
var r = theta === (~~theta) ? "" : "rotate(" + theta*360 + "deg)";
var s = scale === 1 ? "" : "scale(" + scale + "," + scale + ")";
var transforms = t + " " + s + " " + r;
e.style.transform = transforms;
e.style.msTransform = transforms;
e.style.MozTransform = transforms;
e.style.webkitTransform = transforms;
e.style.OTransform = transforms;
function collageElement(w, h, m, elem) {
var e = Render.render(elem);
var matrix = 'matrix(' + m[0] + ',' + m[3] + ',' + m[1] + ',' +
m[4] + ',' + m[2] + ',' + m[5] + ')';
addTransform(e.style, matrix);
var div = newElement('div');
addTo(div,e);
div.style.width = (w|0) + "px";
div.style.height = (h|0) + "px";
div.style.overflow = "hidden";
return div;
}
function collageElement(w,h,theta,scale,x,y,elem) {
var e = Render.render(elem);
applyTransforms(theta,scale,x,y,elem[3],elem[4],e);
var div = Render.newElement('div');
Render.addTo(div,e);
div.style.width = (~~w) + "px";
div.style.height = (~~h) + "px";
div.style.overflow = "hidden";
return div;
function render(model) {
console.log(model);
return collageForms(model.w, model.h, model.forms);
}
function collage(w,h,formss) {
if (formss.length === 0) { return collageForms(w,h,[]); }
var elems = new Array(formss.length);
for (var i = formss.length; i--; ) {
var f = formss[i];
if (typeof f[0] === "string") {
elems[i] = collageElement(w,h,f[1],f[2],f[3][1],f[3][2],f[4][1]);
} else {
elems[i] = collageForms(w,h,f);
}
}
if (formss.length === 1) { return elems[0]; }
return Render.flowWith(Render.goIn,function(x){return x},elems);
}
return { render:render };
function updateFormSet(node,currSet,nextSet) {
if (Value.eq(nextSet,currSet)) return;
var w = node.style.width.slice(0,-2) - 0;
var h = node.style.height.slice(0,-2) - 0;
if (typeof nextSet[0] === "object") {
if (typeof currSet[0] === "object") {
if (node.getContext) {
var ctx = node.getContext('2d');
function redo() { renderForms(this,ctx,w,h,nextSet); }
return renderForms(redo,ctx,w,h,nextSet);
}
}
var newNode = collageForms(w,h,nextSet);
newNode.style.position = 'absolute';
return node.parentNode.replaceChild(newNode,node);
}
node.style.width = (~~w) + "px";
node.style.height = (~~h) + "px";
var f = nextSet;
var next = nextSet[4][1];
Render.update(node.firstChild, currSet[4][1], next);
applyTransforms(f[1],f[2],f[3][1],f[3][2],next[3],next[4],node.firstChild);
}
// assumes that the form sets are the same length.
function updateCollage(node,currs,nexts) {
if (nexts.length === 1) {
return updateFormSet(node,currs[0],nexts[0]);
}
var kids = node.childNodes;
var len = kids.length;
for (var i = len; i--; ) {
updateFormSet(kids[len-i-1], currs[i], nexts[i]);
}
}
function style(clr,n,list) {
return ["Tuple2",
'<span style="font-size:100%;color:' + clr + ';">' + n + '</span>',
list];
}
function insideForm(point) { return function(form) {
if (!inBoundsOf(point[1],point[2],form)) return false;
var hw, hh;
switch (form[4][0]) {
case "FShape": return insideShape(point,form[1],form[2],form[3],form[4][3][1]);
case "FLine": return false;
case "FImage":
hw = form[4][1] / 2;
hh = form[4][2] / 2;
break;
case "FElement":
hw = form[4][1][3] / 2;
hh = form[4][1][4] / 2;
break;
}
return insideShape(point,form[1],form[2],form[3],
[ [null, hw, hh],
[null,-hw, hh],
[null,-hw,-hh],
[null, hw,-hh],
[null, hw, hh] ]);
};
}
function inBoundsOf(px,py,form) {
if (form.length < 6) {
var fx = form[3][1], fy = form[3][2];
var radiusSquared = 0;
var scale = form[2];
switch (form[4][0]) {
case "FShape":
var points = form[4][3][1];
for (var i = points.length; --i; ) {
var p = points[i];
radiusSquared = Math.max(radiusSquared, p[1]*p[1] + p[2]*p[2]);
}
radiusSquared *= scale * scale;
break;
case "FLine":
break;
case "FImage":
var x = scale * form[4][1] / 2;
var y = scale * form[4][2] / 2;
radiusSquared = x*x + y*y;
break;
case "FElement":
var x = scale * form[4][1][3] / 2;
var y = scale * form[4][1][4] / 2;
radiusSquared = x*x + y*y;
break;
}
form.push(function(px,py) {
var dx = px - fx;
var dy = py - fy;
return dx*dx + dy*dy < radiusSquared + 1;
});
}
return form[5](px,py);
}
function insideShape(point,theta,scale,pos,points) {
var counter = 0;
var list = ["Nil"];
var p1,p2;
var x = (point[1] - pos[1]) / scale;
var y = (point[2] - pos[2]) / scale;
if (theta !== 0) {
var t = -2 * Math.PI * theta;
var nx = x * Math.cos(t) - y * Math.sin(t);
y = x * Math.sin(t) + y * Math.cos(t);
x = nx;
}
if (points.length === 0) { return false; }
p1 = points[0];
for (var i = points.length - 1; i--; ) {
p2 = points[i];
var p1x = p1[1], p1y = p1[2], p2x = p2[1], p2y = p2[2];
if (p1y < p2y) {var ymin=p1y, ymax=p2y;} else {var ymin=p2y, ymax=p1y;}
if (p1x < p2x) {var xmin=p1x, xmax=p2x;} else {var xmin=p2x, xmax=p1x;}
if (ymin < y && y <= ymax && x <= xmax) {
if (x <= xmin || x <= ((y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x)) {
++counter;
}
}
p1 = p2;
}
return (counter % 2) === 1;
}
return {collage:collage, updateCollage:updateCollage, insideForm:insideForm};
*/
};

View file

@ -2,24 +2,9 @@
ElmRuntime.Render.Element = function() {
'use strict';
var fromList = Elm.JavaScript({}).fromList;
var eq = Elm.Native.Utils({}).eq;
function newElement(elementType) {
var e = document.createElement(elementType);
e.style.padding = "0";
e.style.margin = "0";
return e;
}
function addTo(container, elem) {
container.appendChild(elem);
}
function extract(c) {
if (c._3 === 1) { return 'rgb(' + c._0 + ',' + c._1 + ',' + c._2 + ')'; }
return 'rgba(' + c._0 + ',' + c._1 + ',' + c._2 + ',' + c._3 + ')';
}
var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
var newElement = Utils.newElement, addTo = Utils.addTo, extract = Utils.extract,
addTransform = Utils.addTransform, removeTransform = Utils.removeTransform;
function setProps(props, e) {
e.style.width = (props.width |0) + 'px';
@ -133,22 +118,6 @@ function toPos(pos) {
}
}
function addTransform(style, trans) {
style.transform = trans;
style.msTransform = trans;
style.MozTransform = trans;
style.webkitTransform = trans;
style.OTransform = trans;
}
function removeTransform(style) {
style.transform = 'none';
style.msTransform = 'none';
style.MozTransform = 'none';
style.webkitTransform = 'none';
style.OTransform = 'none';
}
function setPos(pos,w,h,e) {
e.style.position = 'absolute';
e.style.margin = 'auto';
@ -185,13 +154,14 @@ function rawHtml(html) {
function render(elem) { return setProps(elem.props, makeElement(elem)); }
function makeElement(e) {
switch(e.element.ctor) {
case 'Image': return image(e.props, e.element);
case 'Flow': return flow(e.element._0, e.element._1);
case 'Container': return container(e.element._0, e.element._1);
var elem = e.element;
switch(elem.ctor) {
case 'Image': return image(e.props, elem);
case 'Flow': return flow(elem._0, elem._1);
case 'Container': return container(elem._0, elem._1);
case 'Spacer': return newElement('div');
case 'RawHtml': return rawHtml(e.element._0);
case 'DomNode': return e.element._0;
case 'RawHtml': return rawHtml(elem._0);
case 'Custom': return elem.render(elem.model);
}
}
@ -253,9 +223,9 @@ function update(node, curr, next) {
}
setPos(nextE._0, next.props.width, next.props.height, node.firstChild);
break;
case "DomNode":
if (next._0 !== curr._0) node.parentNode.replaceChild(render(next),node);
break;
case "Custom":
node.parentNode.replaceChild(render(next),node);
return true;
}
var props = next.props, currP = curr.props, e = node;
if (props.width !== currP.width) e.style.width = (props.width |0) + 'px';
@ -279,11 +249,6 @@ function update(node, curr, next) {
}
}
return {
render:render,
update:update,
addTo:addTo,
newElement:newElement
};
return { render:render, update:update };
}();
};