Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

fauna

Package Overview
Dependencies
Maintainers
1
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fauna - npm Package Compare versions

Comparing version 0.3.0 to 0.4.0

configs/hilbert.json

3

package.json
{
"name": "fauna",
"version": "0.3.0",
"version": "0.4.0",
"description": "Generate and render animated L-Systems",

@@ -10,2 +10,3 @@ "main": "src/index.js",

"test:single": "mocha src/*.spec.js",
"generate-testdata": "node src/util.js",
"semantic-release": "semantic-release pre && npm publish && semantic-release post"

@@ -12,0 +13,0 @@ },

@@ -24,1 +24,5 @@ # fauna

TODO: Test `git tag -a <semver> -m "my version 1.4"` to test if it creates the release message in Github
- [ ] Add function to ingest and run config
- [ ] Documentation
- [ ] Landing page
- [ ] Finished egghead.io publishing guide

@@ -0,1 +1,5 @@

const xml = require('xml');
// Takes an array of commands (stack) and returns a corresponding
// svg path string.
function pathString(stack) {

@@ -9,13 +13,16 @@ let path = [];

function boundingBox(stack) {
// Calculates the bounding box of an array of commands.
function boundingBox(stacks) {
let x = y = 0;
let minX = minY = Infinity;
let maxX = maxY = -Infinity;
stack.forEach(function(p) {
x = (p.c === 'M') ? p.x : x + p.x;
y = (p.c === 'M') ? p.y : x + p.y;
minX = Math.min(minX, x);
minY = Math.min(minY, y);
maxX = Math.max(maxX, x);
maxY = Math.max(maxY, y);
stacks.forEach(function(stack) {
stack.forEach(function(p) {
x = (p.c === 'M') ? p.x : x + p.x;
y = (p.c === 'M') ? p.y : y + p.y;
minX = Math.min(minX, x);
minY = Math.min(minY, y);
maxX = Math.max(maxX, x);
maxY = Math.max(maxY, y);
});
});

@@ -25,4 +32,87 @@ return {'minX': minX, 'minY': minY, 'maxX': maxX, 'maxY': maxY};

// Calculates the overall length of array of commands.
// TODO(bradleybossard) : Handle M's.
function pathLength(stack) {
let length = 0.0;
let x = y = 0;
stack.forEach(function(p) {
if (p.c == 'l') {
let xDiff = p.x - x;
let yDiff = p.y - y;
length += Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2));
}
({x, y} = p);
});
return length;
}
// Returns an object respresnting an svg <animate> element.
function animateElement(fromPath, toPath, duration) {
const valuesPath = `${fromPath};${toPath};${fromPath};`;
return [{animate: {_attr:{
attributeName: 'd',
begin: '0s',
dur: duration,
values: valuesPath,
repeatCount: 'indefinite'
}}}];
}
// Returns an object respresenting an svg <path> element.
function pathElement(path, name, minX, minY, animateEls) {
const attrs = {
d : path,
id: name,
transform: `translate(${-minX},${-minY})`,
class: name
};
let root = [
{_attr: attrs},
];
animateEls.forEach(function(el) {
root.push({'animate': el});
});
return root;
}
// Shuffles a stack of commands.
function shufflePath(stack) {
// TODO(bradleybossard) : Implment this function
}
// Returns an object respresenting an svg <path> element.
function renderPath(stacks, pathName) {
let animateEls = [];
const fromStack = stacks[0];
const box = boundingBox(stacks);
const fromPath = pathString(fromStack);
const fromLength = pathLength(fromStack);
if (stacks.length > 1) {
const toStack = stacks[1];
const animateEl = animateElement(fromPath, toPath, 10);
animateEls.push(animateEl);
}
const pathSvg = pathElement(fromPath, pathName, box.minX, box.minY, animateEls);
return {path: pathSvg, box: box, length: fromLength};
}
// Returns an object respresenting an svg <style> element.
function styleElement(props, pathName) {
return `.${pathName} {
stroke: ${props.stroke};
stroke-linecap: ${props['stroke-linecap']};
stroke-linejoin: ${props['stroke-linejoin']};
stroke-width: ${props['stroke-width']};
stroke-opacity: ${props['stroke-opacity']};
stroke-dasharray: ${props['stroke-dasharray']};
stroke-dashoffset: ${props['stroke-dashoffset']};
}`;
}
// Generates the iterated rules string.
exports.iterate = function(axiom, rules, iterations) {
for (var i = 0; i < iterations; i++) {
for (let i = 0; i < iterations; i++) {
axiom = axiom.replace(/\w/g, function(c) {

@@ -35,2 +125,3 @@ return rules[c] || c;

// Loops through iterated rules string to produce svg-like path commands.
exports.toCommands = function(length, alpha, lengthGrowth, alphaGrowth, stream) {

@@ -44,3 +135,3 @@ let point = {'x': 0, 'y': 0};

for(var i = 0, c =''; c = stream.charAt(i); i++){
for(let i = 0, c =''; c = stream.charAt(i); i++) {
switch(c) {

@@ -60,5 +151,7 @@ case '(':

case 'F':
const deltaX = lineLength * Math.cos(Math.PI / 180 * angle)
const deltaY = lineLength * Math.sin(Math.PI / 180 * angle)
stack.push({'c': 'l', 'x': 0, 'y': 0});
let deltaX = lineLength * Math.cos(Math.PI / 180 * angle)
let deltaY = lineLength * Math.sin(Math.PI / 180 * angle)
deltaX = Math.abs(deltaX) < 0.000001 ? 0 : deltaX;
deltaY = Math.abs(deltaY) < 0.000001 ? 0 : deltaY;
stack.push({'c': 'l', 'x': deltaX, 'y': deltaY});
point.x += deltaX;

@@ -77,3 +170,4 @@ point.y += deltaY;

case ']':
angle, point, alpha = tempStack.pop()
({angle, point, alpha} = tempStack.pop());
stack.push({'c': 'M', 'x': point.x, 'y': point.y});
break;

@@ -91,7 +185,53 @@ case '!':

exports.toPaths = function(stack) {
const xml = require('xml');
return pathString(stack);
// Takes an array of command stacks, a name and style to create
// a finished svg string.
exports.toSvg = function(stacks, pathName, props) {
const path = renderPath(stacks, pathName);
const style = styleElement(props, pathName);
const svgWidth = path.box.maxX - path.box.minX;
const svgHeight = path.box.maxY - path.box.minY;
const attrs = {
'viewBox': `0 0 ${svgWidth} ${svgHeight}`,
'width': svgWidth,
'height': svgHeight,
'xmlns:cc': 'http://creativecommons.org/ns#',
'xmlns:dc': 'http://purl.org/dc/elements/1.1',
'xmlns:rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
'xmlns:svg': 'http://www.w3.org/2000/svg',
'xmlns:xlink' : 'http://www.w3.org/1999/xlink',
'xmlns': 'http://www.w3.org/2000/svg',
};
const use = {'_attr': {
'x': 0,
'y': 0,
'class': pathName,
'xlink:href': '#' + pathName
}};
const defs = [{path: path.path}];
let root = [{svg: [
{_attr: attrs},
{style: style},
{use: use},
{defs: defs}
]}];
return xml(root);
}
// Produces an svg based on a config file.
exports.runConfig = function(config) {
// TODO(bradleybossard): Validate fields of config
let stacks = [];
config.patterns.forEach(function(pattern) {
const stream = exports.iterate(pattern.axiom, pattern.rules, pattern.iterations);
const stack = exports.toCommands(pattern.length, pattern.alpha, pattern.lengthGrowth, pattern.alphaGrowth, stream);
stacks.push(stack);
});
return exports.toSvg(stacks, config.meta.name, config.style);
}
const expect = require('chai').expect;
const rewire = require('rewire');
const fauna = rewire('./index.js');
const util = require('util');
const fs = require('fs');
const pathString = fauna.__get__('pathString');
const boundingBox = fauna.__get__('boundingBox');
const animateElement = fauna.__get__('animateElement');
const pathElement = fauna.__get__('pathElement');
const pathLength = fauna.__get__('pathLength');
const renderPath = fauna.__get__('renderPath');
const styleElement = fauna.__get__('styleElement');
describe('iterate test', function() {
it('simple iteration', function(done) {
const expandedString = fauna.iterate('L', {'L': 'LFLR+'}, 2);
expect(expandedString).to.be.equal('LFLR+FLFLR+R+');
it('should iterate properly 1', function(done) {
const actual = fauna.iterate('L', {'L': 'LFLR+'}, 2);
const expected = 'LFLR+FLFLR+R+';
expect(actual).to.be.equal(expected);
done();
});
it('simple iteration', function(done) {
const expandedString = fauna.iterate('a', {'a': 'bac', 'c': 'ddd'}, 4);
expect(expandedString).to.be.equal('bbbbacddddddddd');
it('should iterate properly 2', function(done) {
const actual = fauna.iterate('a', {'a': 'bac', 'c': 'ddd'}, 4);
const expected = 'bbbbacddddddddd';
expect(actual).to.be.equal(expected);
done();

@@ -24,8 +33,8 @@ });

it('covert stream', function(done) {
const expected = [{ c: 'M', x: 0, y: 0 },
{ c: 'l', x: 0, y: 0 },
{ c: 'l', x: 0, y: 0 },
{ c: 'l', x: 0, y: 0 }];
const commands = fauna.toCommands(1, 30, 0.1, 0.1, 'LFLR+FLFLR+R+');
expect(commands).to.be.deep.equal(expected);
const expected = [ { c: 'M', x: 0, y: 0 },
{ c: 'l', x: 0, y: -1 },
{ c: 'l', x: 1, y: 0 },
{ c: 'l', x: 1, y: 0 } ];
const actual = fauna.toCommands(1, 90, 0.1, 0.1, 'LFLR+FLFLR+R+');
expect(actual).to.be.deep.equal(expected);
done();

@@ -42,4 +51,4 @@ });

const expected = 'M 0 0 l 0 0 l 0 0 l 0 0';
const path = pathString(stack);
expect(path).to.be.equal(expected);
const actual = pathString(stack);
expect(actual).to.be.equal(expected);
done();

@@ -51,9 +60,14 @@ });

it('should calculate the bounding box properly', function(done) {
const stack = [{ c: 'M', x: -4, y: 0 },
{ c: 'M', x: 0, y: -4 },
{ c: 'M', x: 4, y: 0 },
{ c: 'M', x: 0, y: 4 }];
const expected = {'minX': -4, 'minY': -4, 'maxX': 4, 'maxY': 4};
const box = boundingBox(stack);
expect(box).to.be.deep.equal(expected);
const stack1 = [{ c: 'M', x: -4, y: 0 },
{ c: 'M', x: 0, y: -4 },
{ c: 'M', x: 4, y: 0 },
{ c: 'M', x: 0, y: 4 }];
const stack2 = [{ c: 'M', x: -8, y: 0 },
{ c: 'M', x: 0, y: -8 },
{ c: 'M', x: 0, y: 0 },
{ c: 'M', x: 0, y: 0 }];
const stacks = [stack1, stack2];
const expected = {'minX': -8, 'minY': -8, 'maxX': 4, 'maxY': 4};
const actual = boundingBox(stacks);
expect(actual).to.be.deep.equal(expected);
done();

@@ -63,4 +77,102 @@ });

describe('animationElement test', function() {
it('should produce an animate xml element properly', function(done) {
const fromPath = '1 2 1';
const toPath = '3 4 3';
const duration = 20;
const expected = [{animate: {_attr:{
attributeName: 'd',
begin: '0s',
dur: 20,
values: '1 2 1;3 4 3;1 2 1;',
repeatCount: 'indefinite'
}}}];
const actual = animateElement(fromPath, toPath, duration);
expect(actual).to.be.deep.equal(expected);
done();
});
});
describe('pathElement test', function() {
it('should produce an path xml element properly', function(done) {
const path = '1 2 1';
const name = 'pathname';
const minX = minY = -10;
const animateEls = [{animate: {_attr:{
attributeName: 'd',
begin: '0s',
dur: 20,
values: '1 2 1;3 4 3;1 2 1;',
repeatCount: 'indefinite'
}}}];
const expected = [ { _attr: { d: '1 2 1', id: 'pathname', transform: 'translate(10,10)', class: 'pathname' } }, { animate: { animate: { _attr: { attributeName: 'd', begin: '0s', dur: 20, values: '1 2 1;3 4 3;1 2 1;', repeatCount: 'indefinite' } } } } ];
const actual = pathElement(path, name, minX, minY, animateEls);
//console.log(util.inspect(actual, false, null));
expect(actual).to.be.deep.equal(expected);
done();
});
});
describe('pathLength test', function() {
it('should calculate the length of a path', function(done) {
const stack = [{c: 'M', x:0, y:0}, {c: 'l', x:3, y:3}];
const expected = Math.sqrt(18);
const actual = pathLength(stack);
expect(actual).to.be.equal(expected);
done();
});
});
describe('renderPath test', function() {
it('should render path correctly', function(done) {
const pathName = 'test1';
const stack1 = [{c: 'M', x:0, y:0}, {c: 'l', x:3, y:3}];
const stacks = [stack1];
const expected = { path: [ { _attr: { d: 'M 0 0 l 3 3', id: 'test1',transform: 'translate(0,0)',class: 'test1' } } ],box: { minX: 0, minY: 0, maxX: 3, maxY: 3 },length: 4.242640687119285 };
const actual = renderPath(stacks, pathName);
//console.log(util.inspect(actual, false, null));
expect(actual).to.be.deep.equal(expected);
done();
});
});
describe('styleElement test', function() {
it('should produce style element correctly', function(done) {
const props = {
'stroke': '#FFF',
'stroke-linecap': 'butt',
'stroke-linejoin': 'miter',
'stroke-width': '1px',
'stroke-opacity': '1.0',
'stroke-dasharray': '20 20',
'stroke-dashoffset': '10.0',
};
const pathName = 'test1';
const expected = '.test1 {\n stroke: #FFF;\n stroke-linecap: butt;\n stroke-linejoin: miter;\n stroke-width: 1px;\n stroke-opacity: 1.0;\n stroke-dasharray: 20 20;\n stroke-dashoffset: 10.0;\n }';
const actual = styleElement(props, pathName);
expect(actual).to.be.deep.equal(expected);
done();
});
});
describe('toSvg test', function() {
it('should produce an SVG', function(done) {
const pathName = 'path1';
const props = JSON.parse(fs.readFileSync('./src/testdata/props.json', 'utf8'));
const stacks = JSON.parse(fs.readFileSync('./src/testdata/stacks.json', 'utf8'));
const expected = fs.readFileSync('./src/testdata/expected.svg', 'utf8');
const actual = fauna.toSvg(stacks, pathName, props);
expect(actual).to.be.equal(expected);
done();
});
});
describe('runConfig test', function() {
it('should produce an SVG', function(done) {
const config = JSON.parse(fs.readFileSync('./configs/hilbert.json', 'utf8'));
const expected = fs.readFileSync('./src/testdata/hilbert-expected.svg', 'utf8');
const actual = fauna.runConfig(config);
expect(actual).to.be.equal(expected);
done();
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc