Dragon curve: Difference between revisions

m
→‎{{header|JavaScript}}: Added a variant in pure ES6 JS, without use of HTML or DOM (defines an SVG file)
m (→‎{{header|JavaScript}}: Added a variant in pure ES6 JS, without use of HTML or DOM (defines an SVG file))
Line 2,246:
 
=={{header|JavaScript}}==
===VersionES5 #1.plus HTML DOM===
====Version #1.====
{{works with|Chrome 8.0}}
I'm sure this can be simplified further, but I have this working [http://kevincantu.org/code/dragon/dragon.html here]!
Line 2,361 ⟶ 2,362:
...</lang>
 
====Version #2.====
{{works with|Chrome}}
[[File:DC11.png|200px|right|thumb|Output DC11.png]]
Line 2,429 ⟶ 2,430:
<pre>
Page with different plotted Dragon curves. Right-clicking on the canvas you can save each of them
as a png-file. </pre>
 
</pre>
===ES6===
 
Declarative definition of an SVG file, in terms of functional primitives.
(To test, generate and save SVG as file, and open in a browser or graphics application).
 
(Pure JS, without HTML or DOM)
<lang javascript>(() => {
'use strict';
 
// dragonCurve :: [[Int]] -> [[Int]]
const dragonCurve = xs => {
const
pivot = op => map(
zipWith(op)(last(xs))
),
r90 = [
[0, 1],
[-1, 0]
];
return compose(
append(xs),
pivot(add),
flip(matrixMultiply)(r90),
pivot(subtract),
reverse,
init
)(xs);
};
 
 
// ------------------------TEST------------------------
const main = () =>
console.log(
svgPathsFromPointLists(512)(512)(
take(14)(
iterate(dragonCurve)([
[0, 0],
[0, 1]
])
)
)
);
 
 
// ------------------------SVG-------------------------
 
// svgPathsFromPointLists :: Int -> Int ->
// [[(Int, Int)]] -> String
const svgPathsFromPointLists = cw => ch =>
xyss => {
const
polyline = xs =>
`<polyline points="${unwords(concat(xs).map(showJSON))}"/>`,
[x, y, mx, my] = apList([minimum, maximum])(
Array.from(unzip(concat(xyss)))
),
[w, h] = map(x => Math.floor(1.2 * x))([
mx - x, my - y
]);
return unlines([
'<?xml version="1.0" encoding="UTF-8"?>',
unwords([
'<svg',
`width="${cw}" height="${ch}"`,
`viewBox="${x} ${y} ${w} ${h}"`,
'xmlns="http://www.w3.org/2000/svg">'
]),
'<g stroke-width="0.2" stroke="red" fill="none">',
unlines(map(polyline)(xyss)),
'</g>',
'</svg>'
]);
};
 
 
// -----------------GENERIC FUNCTIONS------------------
 
// Just :: a -> Maybe a
const Just = x => ({
type: 'Maybe',
Nothing: false,
Just: x
});
 
 
// Nothing :: Maybe a
const Nothing = () => ({
type: 'Maybe',
Nothing: true,
});
 
 
// Tuple (,) :: a -> b -> (a, b)
const Tuple = a =>
b => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
 
 
// add (+) :: Num a => a -> a -> a
const add = a =>
// Curried addition.
b => a + b;
 
 
// apList (<*>) :: [(a -> b)] -> [a] -> [b]
const apList = fs =>
// The sequential application of each of a list
// of functions to each of a list of values.
xs => fs.flatMap(
f => xs.map(f)
);
 
// append (++) :: [a] -> [a] -> [a]
// append (++) :: String -> String -> String
const append = xs =>
// A list or string composed by
// the concatenation of two others.
ys => xs.concat(ys);
 
 
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (...fs) =>
fs.reduce(
(f, g) => x => f(g(x)),
x => x
);
 
 
// concat :: [[a]] -> [a]
// concat :: [String] -> String
const concat = xs =>
0 < xs.length ? (
xs.every(x => 'string' === typeof x) ? (
''
) : []
).concat(...xs) : xs;
 
 
// dotProduct :: Num a => [[a]] -> [[a]] -> [[a]]
const dotProduct = xs =>
compose(sum, zipWith(mul)(xs));
 
 
// flip :: (a -> b -> c) -> b -> a -> c
const flip = f =>
1 < f.length ? (
(a, b) => f(b, a)
) : (x => y => f(y)(x));
 
 
// init :: [a] -> [a]
const init = xs =>
// All elements of a list except the last.
0 < xs.length ? (
xs.slice(0, -1)
) : undefined;
 
 
// iterate :: (a -> a) -> a -> Gen [a]
const iterate = f =>
function*(x) {
let v = x;
while (true) {
yield(v);
v = f(v);
}
};
 
 
// last :: [a] -> a
const last = xs =>
// The last item of a list.
0 < xs.length ? xs.slice(-1)[0] : undefined;
 
 
// length :: [a] -> Int
const length = xs =>
// Returns Infinity over objects without finite
// length. This enables zip and zipWith to choose
// the shorter argument when one is non-finite,
// like cycle, repeat etc
(Array.isArray(xs) || 'string' === typeof xs) ? (
xs.length
) : Infinity;
 
 
// map :: (a -> b) -> [a] -> [b]
const map = f =>
// The list obtained by applying f
// to each element of xs.
// (The image of xs under f).
xs => (
Array.isArray(xs) ? (
xs
) : xs.split('')
).map(f);
 
 
// matrixMultiply :: Num a => [[a]] -> [[a]] -> [[a]]
const matrixMultiply = a =>
b => {
const cols = transpose(b);
return map(
compose(
flip(map)(cols),
dotProduct
)
)(a);
};
 
// minimum :: Ord a => [a] -> a
const minimum = xs =>
0 < xs.length ? (
xs.slice(1)
.reduce((a, x) => x < a ? x : a, xs[0])
) : undefined;
 
 
// maximum :: Ord a => [a] -> a
const maximum = xs =>
// The largest value in a non-empty list.
0 < xs.length ? (
xs.slice(1).reduce(
(a, x) => x > a ? (
x
) : a, xs[0]
)
) : undefined;
 
// mul (*) :: Num a => a -> a -> a
const mul = a =>
b => a * b;
 
 
// reverse :: [a] -> [a]
const reverse = xs =>
'string' !== typeof xs ? (
xs.slice(0).reverse()
) : xs.split('').reverse().join('');
 
 
// showJSON :: a -> String
const showJSON = x =>
// Indented JSON representation of the value x.
JSON.stringify(x, null, 2);
 
 
// subtract :: Num -> Num -> Num
const subtract = x =>
y => y - x;
 
 
// sum :: [Num] -> Num
const sum = xs =>
// The numeric sum of all values in xs.
xs.reduce((a, x) => a + x, 0);
 
 
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = n =>
// The first n elements of a list,
// string of characters, or stream.
xs => 'GeneratorFunction' !== xs
.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat.apply([], Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
 
 
// transpose :: [[a]] -> [[a]]
const transpose = rows =>
// The columns of the input transposed
// into new rows.
// Simpler version of transpose, assuming input
// rows of even length.
0 < rows.length ? rows[0].map(
(x, i) => rows.flatMap(
x => x[i]
)
) : [];
 
 
// unlines :: [String] -> String
const unlines = xs =>
// A single string formed by the intercalation
// of a list of strings with the newline character.
xs.join('\n');
 
 
// unwords :: [String] -> String
const unwords = xs =>
// A space-separated string derived
// from a list of words.
xs.join(' ');
 
 
// unzip :: [(a,b)] -> ([a],[b])
const unzip = xys =>
xys.reduce(
(ab, xy) => Tuple(ab[0].concat(xy[0]))(
ab[1].concat(xy[1])
),
Tuple([])([])
);
 
 
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = f =>
// A list constructed by zipping with a
// custom function, rather than with the
// default tuple constructor.
xs => ys => {
const
lng = Math.min(length(xs), length(ys)),
vs = take(lng)(ys);
return take(lng)(xs)
.map((x, i) => f(x)(vs[i]));
};
 
// MAIN ---
return main();
})();</lang>
 
=={{header|jq}}==
9,659

edits