1
0
Fork 0

🎨 Updated prettier config

This commit is contained in:
Joost De Cock 2019-08-03 15:03:33 +02:00
parent b8e632998b
commit 6710d76b08
401 changed files with 13193 additions and 15620 deletions

View file

@ -1,99 +1,99 @@
function Attributes() {
this.list = {};
this.list = {}
}
/** Adds an attribute */
Attributes.prototype.add = function(name, value) {
if (typeof this.list[name] === "undefined") {
this.list[name] = [];
if (typeof this.list[name] === 'undefined') {
this.list[name] = []
}
this.list[name].push(value);
this.list[name].push(value)
return this;
};
return this
}
/** Sets an attribute, overwriting existing value */
Attributes.prototype.set = function(name, value) {
this.list[name] = [value];
this.list[name] = [value]
return this;
};
return this
}
/** Removes an attribute */
Attributes.prototype.remove = function(name) {
delete this.list[name];
delete this.list[name]
return this;
};
return this
}
/** Retrieves an attribute */
Attributes.prototype.get = function(name) {
if (typeof this.list[name] === "undefined") return false;
else return this.list[name].join(" ");
};
if (typeof this.list[name] === 'undefined') return false
else return this.list[name].join(' ')
}
/** Retrieves an attribute as array*/
Attributes.prototype.getAsArray = function(name) {
if (typeof this.list[name] === "undefined") return false;
else return this.list[name];
};
if (typeof this.list[name] === 'undefined') return false
else return this.list[name]
}
/** Returns SVG code for attributes */
Attributes.prototype.render = function() {
let svg = "";
let svg = ''
for (let key in this.list) {
svg += ` ${key}="${this.list[key].join(" ")}"`;
svg += ` ${key}="${this.list[key].join(' ')}"`
}
return svg;
};
return svg
}
/** Returns CSS code for attributes */
Attributes.prototype.renderAsCss = function() {
let css = "";
let css = ''
for (let key in this.list) {
css += ` ${key}:${this.list[key].join(" ")};`;
css += ` ${key}:${this.list[key].join(' ')};`
}
return css;
};
return css
}
/** Returns SVG code for attributes with a fiven prefix
* typically used for data-text*/
Attributes.prototype.renderIfPrefixIs = function(prefix = "") {
let svg = "";
let prefixLen = prefix.length;
Attributes.prototype.renderIfPrefixIs = function(prefix = '') {
let svg = ''
let prefixLen = prefix.length
for (let key in this.list) {
if (key.substr(0, prefixLen) === prefix) {
svg += ` ${key.substr(prefixLen)}="${this.list[key].join(" ")}"`;
svg += ` ${key.substr(prefixLen)}="${this.list[key].join(' ')}"`
}
}
return svg;
};
return svg
}
/** Returns a props object for attributes with a fiven prefix
* typically used for data-text*/
Attributes.prototype.asPropsIfPrefixIs = function(prefix = "") {
let props = {};
let prefixLen = prefix.length;
Attributes.prototype.asPropsIfPrefixIs = function(prefix = '') {
let props = {}
let prefixLen = prefix.length
for (let key in this.list) {
if (key.substr(0, prefixLen) === prefix) {
let propKey = key.substr(prefixLen);
if (propKey === "class") propKey = "className";
props[propKey] = this.get(key);
let propKey = key.substr(prefixLen)
if (propKey === 'class') propKey = 'className'
props[propKey] = this.get(key)
}
}
return props;
};
return props
}
/** Returns a deep copy of this */
Attributes.prototype.clone = function() {
let clone = new Attributes();
clone.list = JSON.parse(JSON.stringify(this.list));
let clone = new Attributes()
clone.list = JSON.parse(JSON.stringify(this.list))
return clone;
};
return clone
}
export default Attributes;
export default Attributes

View file

@ -1,18 +1,18 @@
import Pattern from "./pattern";
import Pattern from './pattern'
export default function Design(config, plugins = false) {
const pattern = function(settings) {
Pattern.call(this, config);
if (Array.isArray(plugins)) for (let plugin of plugins) this.use(plugin);
else if (plugins) this.use(plugins);
this.apply(settings);
Pattern.call(this, config)
if (Array.isArray(plugins)) for (let plugin of plugins) this.use(plugin)
else if (plugins) this.use(plugins)
this.apply(settings)
return this;
};
return this
}
// Set up inheritance
pattern.prototype = Object.create(Pattern.prototype);
pattern.prototype.constructor = pattern;
pattern.prototype = Object.create(Pattern.prototype)
pattern.prototype.constructor = pattern
return pattern;
return pattern
}

View file

@ -8,5 +8,5 @@ export default function Hooks() {
postRender: [],
insertText: [],
debug: []
};
}
}

View file

@ -1,10 +1,10 @@
import Design from "./design";
import Pattern from "./pattern";
import Point from "./point";
import Path from "./path";
import Snippet from "./snippet";
import * as utils from "./utils";
import { version } from "../package.json";
import Design from './design'
import Pattern from './pattern'
import Point from './point'
import Path from './path'
import Snippet from './snippet'
import * as utils from './utils'
import { version } from '../package.json'
export default {
version,
@ -16,4 +16,4 @@ export default {
utils,
patterns: {},
plugins: {}
};
}

View file

@ -1,9 +1,9 @@
function Option(config) {
this.id = config.id;
this.config = config;
this.val = config.val;
this.id = config.id
this.config = config
this.val = config.val
return this;
return this
}
export default Option;
export default Option

View file

@ -1,202 +1,196 @@
import * as utils from "./utils";
import Point from "./point";
import Path from "./path";
import Snippet from "./snippet";
import Attributes from "./attributes";
import Hooks from "./hooks";
import * as utils from './utils'
import Point from './point'
import Path from './path'
import Snippet from './snippet'
import Attributes from './attributes'
import Hooks from './hooks'
function Part() {
this.attributes = new Attributes();
this.points = {};
this.paths = {};
this.snippets = {};
this.freeId = 0;
this.topLeft = false;
this.bottomRight = false;
this.width = false;
this.height = false;
this.render = true;
this.utils = utils;
this.attributes = new Attributes()
this.points = {}
this.paths = {}
this.snippets = {}
this.freeId = 0
this.topLeft = false
this.bottomRight = false
this.width = false
this.height = false
this.render = true
this.utils = utils
// Constructors so macros can create objects
this.Point = Point;
this.Path = Path;
this.Snippet = Snippet;
this.Point = Point
this.Path = Path
this.Snippet = Snippet
this.hooks = new Hooks(); // Hooks container
this.hooks = new Hooks() // Hooks container
return this;
return this
}
Part.prototype.macroClosure = function(args) {
let self = this;
let self = this
let method = function(key, args) {
let macro = utils.macroName(key);
if (typeof self[macro] === "function") {
self[macro](args);
let macro = utils.macroName(key)
if (typeof self[macro] === 'function') {
self[macro](args)
} else {
self.debug({
type: "warning",
label: "🚨 Macro not found",
type: 'warning',
label: '🚨 Macro not found',
msg: `Macro ${key} is not registered`
});
}
};
return method;
};
Part.prototype.debugClosure = function() {
let self = this;
let method = function(data) {
self.debug(data);
};
return method;
};
Part.prototype.runHooks = function(hookName, data = false) {
if (data === false) data = this;
let hooks = this.hooks[hookName];
if (hooks && hooks.length > 0) {
for (let hook of hooks) {
hook.method(data, hook.data);
})
}
}
};
return method
}
Part.prototype.debugClosure = function() {
let self = this
let method = function(data) {
self.debug(data)
}
return method
}
Part.prototype.runHooks = function(hookName, data = false) {
if (data === false) data = this
let hooks = this.hooks[hookName]
if (hooks && hooks.length > 0) {
for (let hook of hooks) {
hook.method(data, hook.data)
}
}
}
/** Debug method */
Part.prototype.debug = function(data) {
this.runHooks("debug", data);
};
this.runHooks('debug', data)
}
/** Returns an unused ID */
Part.prototype.getId = function() {
this.freeId += 1;
this.freeId += 1
return "" + this.freeId;
};
return '' + this.freeId
}
/** Returns a value formatted for units provided in settings */
Part.prototype.unitsClosure = function(value) {
let self = this;
let self = this
let method = function(value) {
return utils.units(value, self.context.settings.units);
};
return utils.units(value, self.context.settings.units)
}
return method;
};
return method
}
/** Calculates the part's bounding box and sets it */
Part.prototype.boundary = function() {
if (this.topLeft) return this; // Cached
if (this.topLeft) return this // Cached
let topLeft = new Point(Infinity, Infinity);
let bottomRight = new Point(-Infinity, -Infinity);
let topLeft = new Point(Infinity, Infinity)
let bottomRight = new Point(-Infinity, -Infinity)
for (let key in this.paths) {
let path = this.paths[key].boundary();
let path = this.paths[key].boundary()
if (path.render) {
if (path.topLeft.x < topLeft.x) topLeft.x = path.topLeft.x;
if (path.topLeft.y < topLeft.y) topLeft.y = path.topLeft.y;
if (path.bottomRight.x > bottomRight.x)
bottomRight.x = path.bottomRight.x;
if (path.bottomRight.y > bottomRight.y)
bottomRight.y = path.bottomRight.y;
if (path.topLeft.x < topLeft.x) topLeft.x = path.topLeft.x
if (path.topLeft.y < topLeft.y) topLeft.y = path.topLeft.y
if (path.bottomRight.x > bottomRight.x) bottomRight.x = path.bottomRight.x
if (path.bottomRight.y > bottomRight.y) bottomRight.y = path.bottomRight.y
}
}
for (let key in this.points) {
let point = this.points[key];
let radius = point.attributes.get("data-circle");
let point = this.points[key]
let radius = point.attributes.get('data-circle')
if (radius) {
radius = parseFloat(radius);
if (point.x - radius < topLeft.x) topLeft.x = point.x - radius;
if (point.y - radius < topLeft.y) topLeft.y = point.y - radius;
if (point.x + radius > bottomRight.x) bottomRight.x = point.x + radius;
if (point.y + radius > bottomRight.y) bottomRight.y = point.y + radius;
radius = parseFloat(radius)
if (point.x - radius < topLeft.x) topLeft.x = point.x - radius
if (point.y - radius < topLeft.y) topLeft.y = point.y - radius
if (point.x + radius > bottomRight.x) bottomRight.x = point.x + radius
if (point.y + radius > bottomRight.y) bottomRight.y = point.y + radius
}
}
// Fix infinity if part has no paths
if (topLeft.x === Infinity) topLeft.x = 0;
if (topLeft.y === Infinity) topLeft.y = 0;
if (bottomRight.x === -Infinity) bottomRight.x = 0;
if (bottomRight.y === -Infinity) bottomRight.y = 0;
if (topLeft.x === Infinity) topLeft.x = 0
if (topLeft.y === Infinity) topLeft.y = 0
if (bottomRight.x === -Infinity) bottomRight.x = 0
if (bottomRight.y === -Infinity) bottomRight.y = 0
// Add margin
let margin = this.context.settings.margin;
if (this.context.settings.paperless && margin < 10) margin = 10;
this.topLeft = new Point(topLeft.x - margin, topLeft.y - margin);
this.bottomRight = new Point(bottomRight.x + margin, bottomRight.y + margin);
this.width = this.bottomRight.x - this.topLeft.x;
this.height = this.bottomRight.y - this.topLeft.y;
let margin = this.context.settings.margin
if (this.context.settings.paperless && margin < 10) margin = 10
this.topLeft = new Point(topLeft.x - margin, topLeft.y - margin)
this.bottomRight = new Point(bottomRight.x + margin, bottomRight.y + margin)
this.width = this.bottomRight.x - this.topLeft.x
this.height = this.bottomRight.y - this.topLeft.y
return this;
};
return this
}
/** Stacks part so that its top left corner is in (0,0) */
Part.prototype.stack = function() {
if (this.topLeft !== false) return this;
else this.boundary();
if (this.topLeft.x == 0 && this.topLeft.y == 0) return this;
else
this.attr(
"transform",
`translate(${this.topLeft.x * -1}, ${this.topLeft.y * -1})`
);
if (this.topLeft !== false) return this
else this.boundary()
if (this.topLeft.x == 0 && this.topLeft.y == 0) return this
else this.attr('transform', `translate(${this.topLeft.x * -1}, ${this.topLeft.y * -1})`)
return this;
};
return this
}
/** Adds an attribute. This is here to make this call chainable in assignment */
Part.prototype.attr = function(name, value, overwrite = false) {
if (overwrite) this.attributes.set(name, value);
else this.attributes.add(name, value);
if (overwrite) this.attributes.set(name, value)
else this.attributes.add(name, value)
return this;
};
return this
}
/** Copies point/path/snippet data from part orig into this */
Part.prototype.inject = function(orig) {
const findBasePoint = p => {
for (let i in orig.points) {
if (orig.points[i] === p) return i;
if (orig.points[i] === p) return i
}
return false;
};
return false
}
for (let i in orig.points) this.points[i] = orig.points[i].clone();
for (let i in orig.points) this.points[i] = orig.points[i].clone()
for (let i in orig.paths) {
this.paths[i] = orig.paths[i].clone();
this.paths[i] = orig.paths[i].clone()
// Keep link between points and path ops where possible
for (let j in orig.paths[i].ops) {
let op = orig.paths[i].ops[j];
if (op.type !== "close") {
let toPoint = findBasePoint(op.to);
if (toPoint) this.paths[i].ops[j].to = this.points[toPoint];
let op = orig.paths[i].ops[j]
if (op.type !== 'close') {
let toPoint = findBasePoint(op.to)
if (toPoint) this.paths[i].ops[j].to = this.points[toPoint]
}
if (op.type === "curve") {
let cp1Point = findBasePoint(op.cp1);
if (cp1Point) this.paths[i].ops[j].cp1 = this.points[cp1Point];
let cp2Point = findBasePoint(op.cp2);
if (cp2Point) this.paths[i].ops[j].cp2 = this.points[cp2Point];
if (op.type === 'curve') {
let cp1Point = findBasePoint(op.cp1)
if (cp1Point) this.paths[i].ops[j].cp1 = this.points[cp1Point]
let cp2Point = findBasePoint(op.cp2)
if (cp2Point) this.paths[i].ops[j].cp2 = this.points[cp2Point]
}
}
}
for (let i in orig.snippets) {
this.snippets[i] = orig.snippets[i].clone();
this.snippets[i] = orig.snippets[i].clone()
}
return this;
};
return this
}
Part.prototype.units = function(input) {
return utils.units(input, this.context.settings.units);
};
return utils.units(input, this.context.settings.units)
}
/** Returns an object with shorthand access for pattern design */
Part.prototype.shorthand = function() {
let complete = this.context.settings.complete ? true : false;
let paperless = this.context.settings.paperless === true ? true : false;
let sa = this.context.settings.complete ? this.context.settings.sa || 0 : 0;
let complete = this.context.settings.complete ? true : false
let paperless = this.context.settings.paperless === true ? true : false
let sa = this.context.settings.complete ? this.context.settings.sa || 0 : 0
return {
sa,
measurements: this.context.settings.measurements || {},
@ -214,7 +208,7 @@ Part.prototype.shorthand = function() {
complete,
paperless,
debug: this.debugClosure()
};
};
}
}
export default Part;
export default Part

File diff suppressed because it is too large Load diff

View file

@ -1,72 +1,63 @@
import { macroName, round, sampleStyle, capitalize } from "./utils";
import Part from "./part";
import Point from "./point";
import Path from "./path";
import Snippet from "./snippet";
import Svg from "./svg";
import pack from "bin-pack";
import Store from "./store";
import Hooks from "./hooks";
import Attributes from "./attributes";
import { macroName, round, sampleStyle, capitalize } from './utils'
import Part from './part'
import Point from './point'
import Path from './path'
import Snippet from './snippet'
import Svg from './svg'
import pack from 'bin-pack'
import Store from './store'
import Hooks from './hooks'
import Attributes from './attributes'
export default function Pattern(config = { options: {} }) {
this.config = config; // Pattern configuration
this.width = 0; // Will be set after render
this.height = 0; // Will be set after render
this.is = ""; // Will be set when drafting/sampling
this.config = config // Pattern configuration
this.width = 0 // Will be set after render
this.height = 0 // Will be set after render
this.is = '' // Will be set when drafting/sampling
this.store = new Store(); // Store for sharing data across parts
this.parts = {}; // Parts container
this.hooks = new Hooks(); // Hooks container
this.Point = Point; // Point constructor
this.Path = Path; // Path constructor
this.Snippet = Snippet; // Snippet constructor
this.Attributes = Attributes; // Attributes constructor
this.store = new Store() // Store for sharing data across parts
this.parts = {} // Parts container
this.hooks = new Hooks() // Hooks container
this.Point = Point // Point constructor
this.Path = Path // Path constructor
this.Snippet = Snippet // Snippet constructor
this.Attributes = Attributes // Attributes constructor
// Default settings
this.settings = {
complete: true,
idPrefix: "fs-",
locale: "en",
units: "metric",
idPrefix: 'fs-',
locale: 'en',
units: 'metric',
margin: 2,
layout: true,
options: {}
};
}
if (typeof this.config.dependencies === "undefined")
this.config.dependencies = {};
if (typeof this.config.inject === "undefined") this.config.inject = {};
if (typeof this.config.hide === "undefined") this.config.hide = [];
this.config.resolvedDependencies = this.resolveDependencies(
this.config.dependencies
);
this.config.draftOrder = this.draftOrder(this.config.resolvedDependencies);
if (typeof this.config.dependencies === 'undefined') this.config.dependencies = {}
if (typeof this.config.inject === 'undefined') this.config.inject = {}
if (typeof this.config.hide === 'undefined') this.config.hide = []
this.config.resolvedDependencies = this.resolveDependencies(this.config.dependencies)
this.config.draftOrder = this.draftOrder(this.config.resolvedDependencies)
// Convert options
for (let i in config.options) {
let option = config.options[i];
if (typeof option === "object") {
if (typeof option.pct !== "undefined")
this.settings.options[i] = option.pct / 100;
else if (typeof option.mm !== "undefined")
this.settings.options[i] = option.mm;
else if (typeof option.deg !== "undefined")
this.settings.options[i] = option.deg;
else if (typeof option.count !== "undefined")
this.settings.options[i] = option.count;
else if (typeof option.bool !== "undefined")
this.settings.options[i] = option.bool;
else if (typeof option.dflt !== "undefined")
this.settings.options[i] = option.dflt;
else throw new Error("Unknown option type: " + JSON.stringify(option));
let option = config.options[i]
if (typeof option === 'object') {
if (typeof option.pct !== 'undefined') this.settings.options[i] = option.pct / 100
else if (typeof option.mm !== 'undefined') this.settings.options[i] = option.mm
else if (typeof option.deg !== 'undefined') this.settings.options[i] = option.deg
else if (typeof option.count !== 'undefined') this.settings.options[i] = option.count
else if (typeof option.bool !== 'undefined') this.settings.options[i] = option.bool
else if (typeof option.dflt !== 'undefined') this.settings.options[i] = option.dflt
else throw new Error('Unknown option type: ' + JSON.stringify(option))
} else {
this.settings.options[i] = option;
this.settings.options[i] = option
}
}
// Macros
this.macros = {};
this.macros = {}
// Context object to add to Part closure
const context = {
@ -75,406 +66,385 @@ export default function Pattern(config = { options: {} }) {
settings: this.settings,
store: this.store,
macros: this.macros
};
}
// Part closure
this.Part = function() {
let part = new Part();
part.context = context;
let part = new Part()
part.context = context
for (let macro in context.macros) {
part[macroName(macro)] = context.macros[macro];
part[macroName(macro)] = context.macros[macro]
}
return part;
};
return part
}
}
// Merges settings object with this.settings
Pattern.prototype.apply = function(settings) {
if (typeof settings !== "object") return this;
if (typeof settings !== 'object') return this
for (let key of Object.keys(settings)) {
if (Array.isArray(settings[key])) {
if (Array.isArray(this.settings[key])) {
for (let entry of settings[key]) this.settings[key].push(entry);
} else this.settings[key] = settings[key];
} else if (typeof settings[key] === "object") {
for (let entry of settings[key]) this.settings[key].push(entry)
} else this.settings[key] = settings[key]
} else if (typeof settings[key] === 'object') {
this.settings[key] = {
...this.settings[key],
...settings[key]
};
} else this.settings[key] = settings[key];
}
} else this.settings[key] = settings[key]
}
return this;
};
return this
}
Pattern.prototype.runHooks = function(hookName, data = false) {
if (data === false) data = this;
let hooks = this.hooks[hookName];
if (data === false) data = this
let hooks = this.hooks[hookName]
if (hooks.length > 0) {
for (let hook of hooks) {
hook.method(data, hook.data);
hook.method(data, hook.data)
}
}
};
}
/**
* The default draft method with pre- and postDraft hooks
*/
Pattern.prototype.draft = function() {
if (this.is !== "sample") this.is = "draft";
this.runHooks("preDraft");
if (this.is !== 'sample') this.is = 'draft'
this.runHooks('preDraft')
for (let partName of this.config.draftOrder) {
this.parts[partName] = new this.Part();
if (typeof this.config.inject[partName] === "string") {
this.parts[partName].inject(this.parts[this.config.inject[partName]]);
this.parts[partName] = new this.Part()
if (typeof this.config.inject[partName] === 'string') {
this.parts[partName].inject(this.parts[this.config.inject[partName]])
}
if (this.needs(partName)) {
let method = "draft" + capitalize(partName);
if (typeof this[method] !== "function")
let method = 'draft' + capitalize(partName)
if (typeof this[method] !== 'function')
throw new Error('Method "' + method + '" on pattern object is not callable')
this.parts[partName] = this[method](this.parts[partName])
if (typeof this.parts[partName] === 'undefined')
throw new Error(
'Method "' + method + '" on pattern object is not callable'
);
this.parts[partName] = this[method](this.parts[partName]);
if (typeof this.parts[partName] === "undefined")
throw new Error(
"Result of " +
method +
"() was undefined. Did you forget to return the Part object?"
);
'Result of ' + method + '() was undefined. Did you forget to return the Part object?'
)
this.parts[partName].render =
this.parts[partName].render === false ? false : this.wants(partName);
this.parts[partName].render === false ? false : this.wants(partName)
} else {
this.parts[partName].render = false;
this.parts[partName].render = false
}
}
this.runHooks("postDraft");
this.runHooks('postDraft')
return this;
};
return this
}
/**
* Handles pattern sampling
*/
Pattern.prototype.sample = function() {
if (this.settings.sample.type === "option") {
return this.sampleOption(this.settings.sample.option);
} else if (this.settings.sample.type === "measurement") {
return this.sampleMeasurement(this.settings.sample.measurement);
} else if (this.settings.sample.type === "models") {
return this.sampleModels(
this.settings.sample.models,
this.settings.sample.focus || false
);
if (this.settings.sample.type === 'option') {
return this.sampleOption(this.settings.sample.option)
} else if (this.settings.sample.type === 'measurement') {
return this.sampleMeasurement(this.settings.sample.measurement)
} else if (this.settings.sample.type === 'models') {
return this.sampleModels(this.settings.sample.models, this.settings.sample.focus || false)
}
};
}
Pattern.prototype.sampleParts = function() {
let parts = {};
this.settings.complete = false;
this.settings.paperless = false;
this.draft();
let parts = {}
this.settings.complete = false
this.settings.paperless = false
this.draft()
for (let i in this.parts) {
parts[i] = new this.Part();
parts[i].render = this.parts[i].render;
parts[i] = new this.Part()
parts[i].render = this.parts[i].render
}
return parts;
};
return parts
}
Pattern.prototype.sampleRun = function(
parts,
anchors,
run,
runs,
extraClass = false
) {
this.draft();
Pattern.prototype.sampleRun = function(parts, anchors, run, runs, extraClass = false) {
this.draft()
for (let i in this.parts) {
let anchor = false;
let dx = 0;
let dy = 0;
let anchor = false
let dx = 0
let dy = 0
if (this.parts[i].points.anchor) {
if (typeof anchors[i] === "undefined")
anchors[i] = this.parts[i].points.anchor;
if (typeof anchors[i] === 'undefined') anchors[i] = this.parts[i].points.anchor
else {
if (!anchors[i].sitsOn(this.parts[i].points.anchor)) {
dx = this.parts[i].points.anchor.dx(anchors[i]);
dy = this.parts[i].points.anchor.dy(anchors[i]);
dx = this.parts[i].points.anchor.dx(anchors[i])
dy = this.parts[i].points.anchor.dy(anchors[i])
}
}
}
for (let j in this.parts[i].paths) {
parts[i].paths[j + "_" + run] = this.parts[i].paths[j]
parts[i].paths[j + '_' + run] = this.parts[i].paths[j]
.clone()
.attr("style", sampleStyle(run, runs));
.attr('style', sampleStyle(run, runs))
if (this.parts[i].points.anchor)
parts[i].paths[j + "_" + run] = parts[i].paths[j + "_" + run].translate(
dx,
dy
);
if (extraClass !== false)
parts[i].paths[j + "_" + run].attributes.add("class", extraClass);
parts[i].paths[j + '_' + run] = parts[i].paths[j + '_' + run].translate(dx, dy)
if (extraClass !== false) parts[i].paths[j + '_' + run].attributes.add('class', extraClass)
}
}
};
}
/**
* Handles option sampling
*/
Pattern.prototype.sampleOption = function(optionName) {
this.is = "sample";
this.runHooks("preSample");
let step, val;
let factor = 1;
let anchors = {};
let parts = this.sampleParts();
let option = this.config.options[optionName];
if (typeof option.list === "object") {
return this.sampleListOption(optionName);
this.is = 'sample'
this.runHooks('preSample')
let step, val
let factor = 1
let anchors = {}
let parts = this.sampleParts()
let option = this.config.options[optionName]
if (typeof option.list === 'object') {
return this.sampleListOption(optionName)
}
if (typeof option.min === "undefined" || typeof option.max === "undefined") {
let min = option * 0.9;
let max = option * 1.1;
option = { min, max };
if (typeof option.min === 'undefined' || typeof option.max === 'undefined') {
let min = option * 0.9
let max = option * 1.1
option = { min, max }
}
if (typeof option.pct !== "undefined") factor = 100;
val = option.min / factor;
step = (option.max / factor - val) / 9;
if (typeof option.pct !== 'undefined') factor = 100
val = option.min / factor
step = (option.max / factor - val) / 9
for (let run = 1; run < 11; run++) {
this.settings.options[optionName] = val;
this.settings.options[optionName] = val
this.debug({
type: "info",
label: "🏃🏿‍♀️ Sample run",
type: 'info',
label: '🏃🏿‍♀️ Sample run',
msg: `Sampling option ${optionName} with value ${round(val)}`
});
this.sampleRun(parts, anchors, run, 10);
val += step;
})
this.sampleRun(parts, anchors, run, 10)
val += step
}
this.parts = parts;
this.runHooks("postSample");
this.parts = parts
this.runHooks('postSample')
return this;
};
return this
}
Pattern.prototype.sampleListOption = function(optionName) {
let parts = this.sampleParts();
let option = this.config.options[optionName];
let anchors = {};
let run = 1;
let runs = option.list.length;
let parts = this.sampleParts()
let option = this.config.options[optionName]
let anchors = {}
let run = 1
let runs = option.list.length
for (let val of option.list) {
this.settings.options[optionName] = val;
this.settings.options[optionName] = val
this.debug({
type: "info",
label: "🏃🏿‍♀️ Sample run",
type: 'info',
label: '🏃🏿‍♀️ Sample run',
msg: `Sampling option ${optionName} with value ${round(val)}`
});
this.sampleRun(parts, anchors, run, runs);
run++;
})
this.sampleRun(parts, anchors, run, runs)
run++
}
this.parts = parts;
this.parts = parts
return this;
};
return this
}
/**
* Handles measurement sampling
*/
Pattern.prototype.sampleMeasurement = function(measurementName) {
this.is = "sample";
this.runHooks("preSample");
let anchors = {};
let parts = this.sampleParts();
let val = this.settings.measurements[measurementName];
this.is = 'sample'
this.runHooks('preSample')
let anchors = {}
let parts = this.sampleParts()
let val = this.settings.measurements[measurementName]
if (val === undefined)
throw new Error(
"Cannot sample a measurement that is undefined: " + measurementName
);
let step = val / 50;
val = val * 0.9;
throw new Error('Cannot sample a measurement that is undefined: ' + measurementName)
let step = val / 50
val = val * 0.9
for (let run = 1; run < 11; run++) {
this.settings.measurements[measurementName] = val;
this.settings.measurements[measurementName] = val
this.debug({
type: "info",
label: "🏃🏿‍♀️ Sample run",
type: 'info',
label: '🏃🏿‍♀️ Sample run',
msg: `Sampling option ${measurementName} with value ${round(val)}`
});
this.sampleRun(parts, anchors, run, 10);
val += step;
})
this.sampleRun(parts, anchors, run, 10)
val += step
}
this.parts = parts;
this.runHooks("postSample");
this.parts = parts
this.runHooks('postSample')
return this;
};
return this
}
/**
* Handles models sampling
*/
Pattern.prototype.sampleModels = function(models, focus = false) {
this.is = "sample";
this.runHooks("preSample");
let anchors = {};
let parts = this.sampleParts();
let run = 0;
let runs = Object.keys(models).length;
this.is = 'sample'
this.runHooks('preSample')
let anchors = {}
let parts = this.sampleParts()
let run = 0
let runs = Object.keys(models).length
for (let l in models) {
run++;
this.settings.measurements = models[l];
run++
this.settings.measurements = models[l]
this.debug({
type: "info",
label: "🏃🏿‍♀️ Sample run",
type: 'info',
label: '🏃🏿‍♀️ Sample run',
msg: `Sampling model ${l}`
});
let className = l === focus ? "sample-focus" : "";
this.sampleRun(parts, anchors, run, runs, className);
})
let className = l === focus ? 'sample-focus' : ''
this.sampleRun(parts, anchors, run, runs, className)
}
this.parts = parts;
this.runHooks("postSample");
this.parts = parts
this.runHooks('postSample')
return this;
};
return this
}
/** Debug method, exposes debug hook */
Pattern.prototype.debug = function(data) {
this.runHooks("debug", data);
};
this.runHooks('debug', data)
}
Pattern.prototype.render = function() {
this.svg = new Svg(this);
this.svg.hooks = this.hooks;
this.svg = new Svg(this)
this.svg.hooks = this.hooks
return this.pack().svg.render(this);
};
return this.pack().svg.render(this)
}
Pattern.prototype.on = function(hook, method, data) {
this.hooks[hook].push({ method, data });
};
this.hooks[hook].push({ method, data })
}
Pattern.prototype.use = function(plugin, data = false) {
this.debug({
type: "success",
label: "🔌 Plugin loaded",
type: 'success',
label: '🔌 Plugin loaded',
msg: `${plugin.name} v${plugin.version}`
});
if (plugin.hooks) this.loadPluginHooks(plugin, data);
if (plugin.macros) this.loadPluginMacros(plugin);
})
if (plugin.hooks) this.loadPluginHooks(plugin, data)
if (plugin.macros) this.loadPluginMacros(plugin)
return this;
};
return this
}
Pattern.prototype.loadPluginHooks = function(plugin, data) {
for (let hook of Object.keys(this.hooks)) {
if (typeof plugin.hooks[hook] === "function") {
this.on(hook, plugin.hooks[hook], data);
if (typeof plugin.hooks[hook] === 'function') {
this.on(hook, plugin.hooks[hook], data)
} else if (Array.isArray(plugin.hooks[hook])) {
for (let method of plugin.hooks[hook]) {
this.on(hook, method, data);
this.on(hook, method, data)
}
}
}
};
}
Pattern.prototype.loadPluginMacros = function(plugin) {
for (let macro in plugin.macros) {
if (typeof plugin.macros[macro] === "function") {
this.macro(macro, plugin.macros[macro]);
if (typeof plugin.macros[macro] === 'function') {
this.macro(macro, plugin.macros[macro])
}
}
};
}
Pattern.prototype.macro = function(key, method) {
this.macros[key] = method;
};
this.macros[key] = method
}
/** Packs parts in a 2D space and sets pattern size */
Pattern.prototype.pack = function() {
let bins = [];
let bins = []
for (let key in this.parts) {
let part = this.parts[key];
let part = this.parts[key]
// Avoid multiple render calls to cause stacking of transforms
part.attributes.remove("transform");
part.attributes.remove('transform')
if (part.render) {
part.stack();
let width = part.bottomRight.x - part.topLeft.x;
let height = part.bottomRight.y - part.topLeft.y;
if (this.settings.layout === true) bins.push({ id: key, width, height });
part.stack()
let width = part.bottomRight.x - part.topLeft.x
let height = part.bottomRight.y - part.topLeft.y
if (this.settings.layout === true) bins.push({ id: key, width, height })
else {
if (this.width < width) this.width = width;
if (this.height < height) this.height = height;
if (this.width < width) this.width = width
if (this.height < height) this.height = height
}
}
}
if (this.settings.layout === true) {
let size = pack(bins, { inPlace: true });
let size = pack(bins, { inPlace: true })
for (let bin of bins) {
let part = this.parts[bin.id];
if (bin.x !== 0 || bin.y !== 0)
part.attr("transform", `translate (${bin.x}, ${bin.y})`);
let part = this.parts[bin.id]
if (bin.x !== 0 || bin.y !== 0) part.attr('transform', `translate (${bin.x}, ${bin.y})`)
}
this.width = size.width;
this.height = size.height;
} else if (typeof this.settings.layout === "object") {
this.width = this.settings.layout.width;
this.height = this.settings.layout.height;
this.width = size.width
this.height = size.height
} else if (typeof this.settings.layout === 'object') {
this.width = this.settings.layout.width
this.height = this.settings.layout.height
for (let partId of Object.keys(this.settings.layout.parts)) {
let transforms = this.settings.layout.parts[partId];
let transforms = this.settings.layout.parts[partId]
// Moving
if (typeof transforms.move === "object") {
if (typeof transforms.move === 'object') {
this.parts[partId].attributes.set(
"transform",
"translate(" + transforms.move.x + ", " + transforms.move.y + ")"
);
'transform',
'translate(' + transforms.move.x + ', ' + transforms.move.y + ')'
)
}
// Mirrorring
let center = this.parts[partId].topLeft.shiftFractionTowards(
this.parts[partId].bottomRight,
0.5
);
let anchor = { x: 0, y: 0 };
)
let anchor = { x: 0, y: 0 }
if (transforms.flipX) {
let dx = anchor.x - center.x;
let transform = `translate(${center.x * -1}, ${center.y * -1})`;
transform += " scale(-1, 1)";
transform += ` translate(${center.x * -1 + 2 * dx}, ${center.y})`;
this.parts[partId].attributes.add("transform", transform);
let dx = anchor.x - center.x
let transform = `translate(${center.x * -1}, ${center.y * -1})`
transform += ' scale(-1, 1)'
transform += ` translate(${center.x * -1 + 2 * dx}, ${center.y})`
this.parts[partId].attributes.add('transform', transform)
}
if (transforms.flipY) {
let dy = anchor.y - center.y;
let transform = `translate(${center.x * -1}, ${center.y * -1})`;
transform += " scale(1, -1)";
transform += ` translate(${center.x}, ${center.y * -1 + 2 * dy})`;
this.parts[partId].attributes.add("transform", transform);
let dy = anchor.y - center.y
let transform = `translate(${center.x * -1}, ${center.y * -1})`
transform += ' scale(1, -1)'
transform += ` translate(${center.x}, ${center.y * -1 + 2 * dy})`
this.parts[partId].attributes.add('transform', transform)
}
if (transforms.rotate) {
let transform = `rotate(${transforms.rotate}, ${center.x -
anchor.x}, ${center.y - anchor.y})`;
this.parts[partId].attributes.add("transform", transform);
let transform = `rotate(${transforms.rotate}, ${center.x - anchor.x}, ${center.y -
anchor.y})`
this.parts[partId].attributes.add('transform', transform)
}
}
}
return this;
};
return this
}
/** Determines the order to draft parts in, based on dependencies */
Pattern.prototype.draftOrder = function(graph = this.resolveDependencies()) {
let sorted = [];
let visited = {};
let sorted = []
let visited = {}
Object.keys(graph).forEach(function visit(name, ancestors) {
if (!Array.isArray(ancestors)) ancestors = [];
ancestors.push(name);
visited[name] = true;
if (typeof graph[name] !== "undefined") {
if (!Array.isArray(ancestors)) ancestors = []
ancestors.push(name)
visited[name] = true
if (typeof graph[name] !== 'undefined') {
graph[name].forEach(function(dep) {
if (visited[dep]) return;
visit(dep, ancestors.slice(0));
});
if (visited[dep]) return
visit(dep, ancestors.slice(0))
})
}
if (sorted.indexOf(name) < 0) sorted.push(name);
});
if (sorted.indexOf(name) < 0) sorted.push(name)
})
return sorted;
};
return sorted
}
/** Recursively solves part dependencies for a part */
Pattern.prototype.resolveDependency = function(
@ -483,135 +453,119 @@ Pattern.prototype.resolveDependency = function(
graph = this.config.dependencies,
deps = []
) {
if (typeof seen[part] === "undefined") seen[part] = true;
if (typeof graph[part] === "string") {
if (deps.indexOf(graph[part]) === -1) deps.push(graph[part]);
return this.resolveDependency(seen, graph[part], graph, deps);
if (typeof seen[part] === 'undefined') seen[part] = true
if (typeof graph[part] === 'string') {
if (deps.indexOf(graph[part]) === -1) deps.push(graph[part])
return this.resolveDependency(seen, graph[part], graph, deps)
} else if (Array.isArray(graph[part])) {
if (graph[part].length === 0) return [];
if (graph[part].length === 0) return []
else {
if (deps.length === 0) deps = graph[part];
for (let apart of graph[part])
deps.concat(this.resolveDependency(seen, apart, graph, deps));
if (deps.length === 0) deps = graph[part]
for (let apart of graph[part]) deps.concat(this.resolveDependency(seen, apart, graph, deps))
}
}
return deps;
};
return deps
}
/** Resolves part dependencies into a flat array */
Pattern.prototype.resolveDependencies = function(
graph = this.config.dependencies
) {
Pattern.prototype.resolveDependencies = function(graph = this.config.dependencies) {
for (let i in this.config.inject) {
let dependency = this.config.inject[i];
if (typeof this.config.dependencies[i] === "undefined")
this.config.dependencies[i] = dependency;
let dependency = this.config.inject[i]
if (typeof this.config.dependencies[i] === 'undefined') this.config.dependencies[i] = dependency
else if (this.config.dependencies[i] !== dependency) {
if (typeof this.config.dependencies[i] === "string")
this.config.dependencies[i] = [this.config.dependencies[i], dependency];
if (typeof this.config.dependencies[i] === 'string')
this.config.dependencies[i] = [this.config.dependencies[i], dependency]
else if (Array.isArray(this.config.dependencies[i])) {
if (this.config.dependencies[i].indexOf(dependency) === -1)
this.config.dependencies[i].push(dependency);
} else
throw new Error(
"Part dependencies should be a string or an array of strings"
);
this.config.dependencies[i].push(dependency)
} else throw new Error('Part dependencies should be a string or an array of strings')
}
// Parts both in the parts and dependencies array trip up the dependency resolver
if (Array.isArray(this.config.parts)) {
let pos = this.config.parts.indexOf(this.config.inject[i]);
if (pos !== -1) this.config.parts.splice(pos, 1);
let pos = this.config.parts.indexOf(this.config.inject[i])
if (pos !== -1) this.config.parts.splice(pos, 1)
}
}
// Include parts outside the dependency graph
if (Array.isArray(this.config.parts)) {
for (let part of this.config.parts) {
if (typeof this.config.dependencies[part] === "undefined")
this.config.dependencies[part] = [];
if (typeof this.config.dependencies[part] === 'undefined') this.config.dependencies[part] = []
}
}
let resolved = {};
let seen = {};
for (let part in graph)
resolved[part] = this.resolveDependency(seen, part, graph);
for (let part in seen)
if (typeof resolved[part] === "undefined") resolved[part] = [];
let resolved = {}
let seen = {}
for (let part in graph) resolved[part] = this.resolveDependency(seen, part, graph)
for (let part in seen) if (typeof resolved[part] === 'undefined') resolved[part] = []
return resolved;
};
return resolved
}
/** Determines whether a part is needed
* This depends on the 'only' setting and the
* configured dependencies.
*/
Pattern.prototype.needs = function(partName) {
if (typeof this.settings.only === "undefined" || this.settings.only === false)
return true;
else if (typeof this.settings.only === "string") {
if (this.settings.only === partName) return true;
if (typeof this.settings.only === 'undefined' || this.settings.only === false) return true
else if (typeof this.settings.only === 'string') {
if (this.settings.only === partName) return true
if (Array.isArray(this.config.resolvedDependencies[this.settings.only])) {
for (let dependency of this.config.resolvedDependencies[
this.settings.only
]) {
if (dependency === partName) return true;
for (let dependency of this.config.resolvedDependencies[this.settings.only]) {
if (dependency === partName) return true
}
}
} else if (Array.isArray(this.settings.only)) {
for (let part of this.settings.only) {
if (part === partName) return true;
if (part === partName) return true
for (let dependency of this.config.resolvedDependencies[part]) {
if (dependency === partName) return true;
if (dependency === partName) return true
}
}
}
return false;
};
return false
}
/* Checks whether a part is hidden in the config */
Pattern.prototype.isHidden = function(partName) {
if (Array.isArray(this.config.hide)) {
if (this.config.hide.indexOf(partName) !== -1) return true;
if (this.config.hide.indexOf(partName) !== -1) return true
}
return false;
};
return false
}
/** Determines whether a part is wanted by the user
* This depends on the 'only' setting
*/
Pattern.prototype.wants = function(partName) {
if (
typeof this.settings.only === "undefined" ||
this.settings.only === false
) {
if (this.isHidden(partName)) return false;
} else if (typeof this.settings.only === "string") {
if (this.settings.only === partName) return true;
return false;
if (typeof this.settings.only === 'undefined' || this.settings.only === false) {
if (this.isHidden(partName)) return false
} else if (typeof this.settings.only === 'string') {
if (this.settings.only === partName) return true
return false
} else if (Array.isArray(this.settings.only)) {
for (let part of this.settings.only) {
if (part === partName) return true;
if (part === partName) return true
}
return false;
return false
}
return true;
};
return true
}
/** Returns props required to render this pattern through
* an external renderer (eg. a React component)
*/
Pattern.prototype.getRenderProps = function() {
this.pack();
let props = {};
props.width = this.width;
props.height = this.height;
props.settings = this.settings;
props.parts = {};
this.pack()
let props = {}
props.width = this.width
props.height = this.height
props.settings = this.settings
props.parts = {}
for (let p in this.parts) {
if (this.parts[p].render) {
props.parts[p] = {
@ -623,9 +577,9 @@ Pattern.prototype.getRenderProps = function() {
width: this.parts[p].width,
bottomRight: this.parts[p].bottomRight,
topLeft: this.parts[p].topLeft
};
}
}
}
return props;
};
return props
}

View file

@ -1,142 +1,139 @@
import Attributes from "./attributes";
import { round } from "./utils";
import Attributes from './attributes'
import { round } from './utils'
function Point(x, y) {
this.x = round(x);
this.y = round(y);
this.attributes = new Attributes();
this.x = round(x)
this.y = round(y)
this.attributes = new Attributes()
}
/** Radians to degrees */
Point.prototype.rad2deg = function(radians) {
return radians * 57.29577951308232;
};
return radians * 57.29577951308232
}
/** Degrees to radians */
Point.prototype.deg2rad = function(degrees) {
return degrees / 57.29577951308232;
};
return degrees / 57.29577951308232
}
/** Adds an attribute. This is here to make this call chainable in assignment */
Point.prototype.attr = function(name, value, overwrite = false) {
if (overwrite) this.attributes.set(name, value);
else this.attributes.add(name, value);
if (overwrite) this.attributes.set(name, value)
else this.attributes.add(name, value)
return this;
};
return this
}
/** Returns the distance between this point and that point */
Point.prototype.dist = function(that) {
let dx = this.x - that.x;
let dy = this.y - that.y;
let dx = this.x - that.x
let dy = this.y - that.y
return round(Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)));
};
return round(Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)))
}
/** Returns slope of a line made by this point and that point */
Point.prototype.slope = function(that) {
return (that.y - this.y) / (that.x - this.x);
};
return (that.y - this.y) / (that.x - this.x)
}
/** Returns the x-delta between this point and that point */
Point.prototype.dx = function(that) {
return that.x - this.x;
};
return that.x - this.x
}
/** Returns the y-delta between this point and that point */
Point.prototype.dy = function(that) {
return that.y - this.y;
};
return that.y - this.y
}
/** Returns the angle between this point and that point */
Point.prototype.angle = function(that) {
let rad = Math.atan2(-1 * this.dy(that), this.dx(that));
while (rad < 0) rad += 2 * Math.PI;
let rad = Math.atan2(-1 * this.dy(that), this.dx(that))
while (rad < 0) rad += 2 * Math.PI
return this.rad2deg(rad);
};
return this.rad2deg(rad)
}
/** Rotate this point deg around that point */
Point.prototype.rotate = function(deg, that) {
let radius = this.dist(that);
let angle = this.angle(that);
let x = that.x + radius * Math.cos(this.deg2rad(angle + deg)) * -1;
let y = that.y + radius * Math.sin(this.deg2rad(angle + deg));
let radius = this.dist(that)
let angle = this.angle(that)
let x = that.x + radius * Math.cos(this.deg2rad(angle + deg)) * -1
let y = that.y + radius * Math.sin(this.deg2rad(angle + deg))
return new Point(x, y);
};
return new Point(x, y)
}
/** returns an identical copy of this point */
Point.prototype.copy = function() {
return new Point(this.x, this.y);
};
return new Point(this.x, this.y)
}
/** Mirrors this point around X value of that point */
Point.prototype.flipX = function(that = false) {
if (that === false || that.x === 0) return new Point(this.x * -1, this.y);
else return new Point(that.x + this.dx(that), this.y);
};
if (that === false || that.x === 0) return new Point(this.x * -1, this.y)
else return new Point(that.x + this.dx(that), this.y)
}
/** Mirrors this point around Y value of that point */
Point.prototype.flipY = function(that = false) {
if (that === false || that.y === 0) return new Point(this.x, this.y * -1);
else return new Point(this.x, that.y + this.dy(that));
};
if (that === false || that.y === 0) return new Point(this.x, this.y * -1)
else return new Point(this.x, that.y + this.dy(that))
}
/** Shifts this point distance in the deg direction */
Point.prototype.shift = function(deg, distance) {
let p = this.copy();
p.x += distance;
let p = this.copy()
p.x += distance
return p.rotate(deg, this);
};
return p.rotate(deg, this)
}
/** Shifts this point distance in the direction of that point */
Point.prototype.shiftTowards = function(that, distance) {
return this.shift(this.angle(that), distance);
};
return this.shift(this.angle(that), distance)
}
/** Checks whether this has the same coordinates as that */
Point.prototype.sitsOn = function(that) {
if (this.x === that.x && this.y === that.y) return true;
else return false;
};
if (this.x === that.x && this.y === that.y) return true
else return false
}
/** Checks whether this has roughly the same coordinates as that */
Point.prototype.sitsRoughlyOn = function(that) {
if (
Math.round(this.x) === Math.round(that.x) &&
Math.round(this.y) === Math.round(that.y)
)
return true;
else return false;
};
if (Math.round(this.x) === Math.round(that.x) && Math.round(this.y) === Math.round(that.y))
return true
else return false
}
/** Shifts this point fraction of the distance towards that point */
Point.prototype.shiftFractionTowards = function(that, fraction) {
return this.shiftTowards(that, this.dist(that) * fraction);
};
return this.shiftTowards(that, this.dist(that) * fraction)
}
/** Shifts this point distance beyond that point */
Point.prototype.shiftOutwards = function(that, distance) {
return this.shiftTowards(that, this.dist(that) + distance);
};
return this.shiftTowards(that, this.dist(that) + distance)
}
/** Returns a deep copy of this */
Point.prototype.clone = function() {
let clone = new Point(this.x, this.y);
clone.attributes = this.attributes.clone();
let clone = new Point(this.x, this.y)
clone.attributes = this.attributes.clone()
return clone;
};
return clone
}
/** Applies a translate transform */
Point.prototype.translate = function(x, y) {
let p = this.copy();
p.x += x;
p.y += y;
let p = this.copy()
p.x += x
p.y += y
return p;
};
return p
}
export default Point;
export default Point

View file

@ -1,27 +1,27 @@
import Attributes from "./attributes";
import Attributes from './attributes'
function Snippet(def, anchor) {
this.def = def;
this.anchor = anchor;
this.attributes = new Attributes();
this.def = def
this.anchor = anchor
this.attributes = new Attributes()
return this;
return this
}
/** Adds an attribute. This is here to make this call chainable in assignment */
Snippet.prototype.attr = function(name, value, overwrite = false) {
if (overwrite) this.attributes.set(name, value);
else this.attributes.add(name, value);
if (overwrite) this.attributes.set(name, value)
else this.attributes.add(name, value)
return this;
};
return this
}
/** Returns a deep copy of this */
Snippet.prototype.clone = function() {
let clone = new Snippet(this.def, this.anchor.clone());
clone.attributes = this.attributes.clone();
let clone = new Snippet(this.def, this.anchor.clone())
clone.attributes = this.attributes.clone()
return clone;
};
return clone
}
export default Snippet;
export default Snippet

View file

@ -1,20 +1,20 @@
function Store() {
this.data = new Map();
this.data = new Map()
}
/** Sets a value under index key */
Store.prototype.set = function(key, value) {
this.data.set(key, value);
};
this.data.set(key, value)
}
/** Sets a value under index key */
Store.prototype.setIfUnset = function(key, value) {
if (!this.data.has(key)) this.data.set(key, value);
};
if (!this.data.has(key)) this.data.set(key, value)
}
/** Gets a value under index key */
Store.prototype.get = function(key) {
return this.data.get(key);
};
return this.data.get(key)
}
export default Store;
export default Store

View file

@ -1,319 +1,306 @@
import Attributes from "./attributes";
import Attributes from './attributes'
import { version } from "../package.json";
import { version } from '../package.json'
function Svg(pattern) {
this.openGroups = [];
this.layout = {};
this.freeId = 0;
this.body = "";
this.style = "";
this.script = "";
this.defs = "";
this.pattern = pattern; // Needed to expose pattern to hooks
this.prefix = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>';
this.attributes = new Attributes();
this.attributes.add("xmlns", "http://www.w3.org/2000/svg");
this.attributes.add("xmlns:svg", "http://www.w3.org/2000/svg");
this.attributes.add("xmlns:xlink", "http://www.w3.org/1999/xlink");
this.attributes.add("xml:lang", pattern.settings.locale);
this.attributes.add(
"xmlns:freesewing",
"http://freesewing.org/namespaces/freesewing"
);
this.attributes.add("freesewing", version);
this.openGroups = []
this.layout = {}
this.freeId = 0
this.body = ''
this.style = ''
this.script = ''
this.defs = ''
this.pattern = pattern // Needed to expose pattern to hooks
this.prefix = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
this.attributes = new Attributes()
this.attributes.add('xmlns', 'http://www.w3.org/2000/svg')
this.attributes.add('xmlns:svg', 'http://www.w3.org/2000/svg')
this.attributes.add('xmlns:xlink', 'http://www.w3.org/1999/xlink')
this.attributes.add('xml:lang', pattern.settings.locale)
this.attributes.add('xmlns:freesewing', 'http://freesewing.org/namespaces/freesewing')
this.attributes.add('freesewing', version)
}
Svg.prototype.runHooks = function(hookName, data = false) {
if (data === false) data = this;
let hooks = this.hooks[hookName];
if (data === false) data = this
let hooks = this.hooks[hookName]
if (hooks.length > 0) {
for (let hook of hooks) {
hook.method(data, hook.data);
hook.method(data, hook.data)
}
}
};
}
/** Runs insertText hooks */
Svg.prototype.insertText = function(text) {
if (this.hooks.insertText.length > 0) {
for (let hook of this.hooks.insertText)
text = hook.method(this.pattern.settings.locale, text, hook.data);
text = hook.method(this.pattern.settings.locale, text, hook.data)
}
return text;
};
return text
}
/** Debug method, exposes debug hook */
Svg.prototype.debug = function() {};
Svg.prototype.debug = function() {}
/** Renders a draft object as SVG */
Svg.prototype.render = function(pattern) {
this.idPrefix = pattern.settings.idPrefix;
this.runHooks("preRender");
this.idPrefix = pattern.settings.idPrefix
this.runHooks('preRender')
if (!pattern.settings.embed) {
this.attributes.add("width", pattern.width + "mm");
this.attributes.add("height", pattern.height + "mm");
this.attributes.add('width', pattern.width + 'mm')
this.attributes.add('height', pattern.height + 'mm')
}
this.attributes.add("viewBox", `0 0 ${pattern.width} ${pattern.height}`);
this.head = this.renderHead();
this.tail = this.renderTail();
this.svg = "";
this.layout = {}; // Reset layout
this.attributes.add('viewBox', `0 0 ${pattern.width} ${pattern.height}`)
this.head = this.renderHead()
this.tail = this.renderTail()
this.svg = ''
this.layout = {} // Reset layout
for (let partId in pattern.parts) {
let part = pattern.parts[partId];
let part = pattern.parts[partId]
if (part.render) {
let partSvg = this.renderPart(part);
let partSvg = this.renderPart(part)
this.layout[partId] = {
svg: partSvg,
transform: part.attributes.getAsArray("transform")
};
this.svg += this.openGroup(
`${this.idPrefix}part-${partId}`,
part.attributes
);
this.svg += partSvg;
this.svg += this.closeGroup();
transform: part.attributes.getAsArray('transform')
}
this.svg += this.openGroup(`${this.idPrefix}part-${partId}`, part.attributes)
this.svg += partSvg
this.svg += this.closeGroup()
}
}
this.svg =
this.prefix + this.renderSvgTag() + this.head + this.svg + this.tail;
this.runHooks("postRender");
this.svg = this.prefix + this.renderSvgTag() + this.head + this.svg + this.tail
this.runHooks('postRender')
return this.svg;
};
return this.svg
}
/** Renders SVG head section */
Svg.prototype.renderHead = function() {
let svg = this.renderStyle();
svg += this.renderScript();
svg += this.renderDefs();
svg += this.openGroup(this.idPrefix + "container");
let svg = this.renderStyle()
svg += this.renderScript()
svg += this.renderDefs()
svg += this.openGroup(this.idPrefix + 'container')
return svg;
};
return svg
}
/** Renders SVG closing section */
Svg.prototype.renderTail = function() {
let svg = "";
svg += this.closeGroup();
svg += this.nl() + "</svg>";
let svg = ''
svg += this.closeGroup()
svg += this.nl() + '</svg>'
return svg;
};
return svg
}
/** Returns SVG code for the opening SVG tag */
Svg.prototype.renderSvgTag = function() {
let svg = "<svg";
this.indent();
svg += this.nl() + this.attributes.render();
this.outdent();
svg += this.nl() + ">" + this.nl();
let svg = '<svg'
this.indent()
svg += this.nl() + this.attributes.render()
this.outdent()
svg += this.nl() + '>' + this.nl()
return svg;
};
return svg
}
/** Returns SVG code for the style block */
Svg.prototype.renderStyle = function() {
let svg = '<style type="text/css"> <![CDATA[ ';
this.indent();
svg += this.nl() + this.style;
this.outdent();
svg += this.nl() + "]]>" + this.nl() + "</style>" + this.nl();
return svg;
};
let svg = '<style type="text/css"> <![CDATA[ '
this.indent()
svg += this.nl() + this.style
this.outdent()
svg += this.nl() + ']]>' + this.nl() + '</style>' + this.nl()
return svg
}
/** Returns SVG code for the script block */
Svg.prototype.renderScript = function() {
let svg = '<script type="text/javascript"> <![CDATA[';
this.indent();
svg += this.nl() + this.script;
this.outdent();
svg += this.nl() + "]]>" + this.nl() + "</script>" + this.nl();
let svg = '<script type="text/javascript"> <![CDATA['
this.indent()
svg += this.nl() + this.script
this.outdent()
svg += this.nl() + ']]>' + this.nl() + '</script>' + this.nl()
return svg;
};
return svg
}
/** Returns SVG code for the defs block */
Svg.prototype.renderDefs = function() {
let svg = "<defs>";
this.indent();
svg += this.nl() + this.defs;
this.outdent();
svg += this.nl() + "</defs>" + this.nl();
let svg = '<defs>'
this.indent()
svg += this.nl() + this.defs
this.outdent()
svg += this.nl() + '</defs>' + this.nl()
return svg;
};
return svg
}
/** Returns SVG code for a Part object */
Svg.prototype.renderPart = function(part) {
let svg = "";
let svg = ''
for (let key in part.paths) {
let path = part.paths[key];
if (path.render) svg += this.renderPath(path);
let path = part.paths[key]
if (path.render) svg += this.renderPath(path)
}
for (let key in part.points) {
if (part.points[key].attributes.get("data-text")) {
svg += this.renderText(part.points[key]);
if (part.points[key].attributes.get('data-text')) {
svg += this.renderText(part.points[key])
}
if (part.points[key].attributes.get("data-circle")) {
svg += this.renderCircle(part.points[key]);
if (part.points[key].attributes.get('data-circle')) {
svg += this.renderCircle(part.points[key])
}
}
for (let key in part.snippets) {
let snippet = part.snippets[key];
svg += this.renderSnippet(snippet, part);
let snippet = part.snippets[key]
svg += this.renderSnippet(snippet, part)
}
return svg;
};
return svg
}
/** Returns SVG code for a Path object */
Svg.prototype.renderPath = function(path) {
if (!path.attributes.get("id"))
path.attributes.add("id", this.idPrefix + this.getId());
path.attributes.set("d", path.asPathstring());
if (!path.attributes.get('id')) path.attributes.add('id', this.idPrefix + this.getId())
path.attributes.set('d', path.asPathstring())
return `${this.nl()}<path ${path.attributes.render()} />${this.renderPathText(
path
)}`;
};
return `${this.nl()}<path ${path.attributes.render()} />${this.renderPathText(path)}`
}
Svg.prototype.renderPathText = function(path) {
let text = path.attributes.get("data-text");
if (!text) return "";
else this.text = this.insertText(text);
let attributes = path.attributes.renderIfPrefixIs("data-text-");
let text = path.attributes.get('data-text')
if (!text) return ''
else this.text = this.insertText(text)
let attributes = path.attributes.renderIfPrefixIs('data-text-')
// Sadly aligning text along a patch can't be done in CSS only
let offset = "";
let align = path.attributes.get("data-text-class");
if (align && align.indexOf("center") > -1) offset = ' startOffset="50%" ';
else if (align && align.indexOf("right") > -1)
offset = ' startOffset="100%" ';
let svg = this.nl() + "<text>";
this.indent();
svg += `<textPath xlink:href="#${path.attributes.get(
"id"
)}" ${offset}><tspan ${attributes}>${this.text}</tspan></textPath>`;
this.outdent();
svg += this.nl() + "</text>";
let offset = ''
let align = path.attributes.get('data-text-class')
if (align && align.indexOf('center') > -1) offset = ' startOffset="50%" '
else if (align && align.indexOf('right') > -1) offset = ' startOffset="100%" '
let svg = this.nl() + '<text>'
this.indent()
svg += `<textPath xlink:href="#${path.attributes.get('id')}" ${offset}><tspan ${attributes}>${
this.text
}</tspan></textPath>`
this.outdent()
svg += this.nl() + '</text>'
return svg;
};
return svg
}
Svg.prototype.renderText = function(point) {
let text = point.attributes.getAsArray("data-text");
let text = point.attributes.getAsArray('data-text')
if (text !== false) {
let joint = "";
let joint = ''
for (let string of text) {
this.text = this.insertText(string);
joint += this.text + " ";
this.text = this.insertText(string)
joint += this.text + ' '
}
this.text = this.insertText(joint);
this.text = this.insertText(joint)
}
point.attributes.set("data-text-x", point.x);
point.attributes.set("data-text-y", point.y);
let lineHeight = point.attributes.get("data-text-lineheight") || 12;
point.attributes.remove("data-text-lineheight");
let svg = `${this.nl()}<text ${point.attributes.renderIfPrefixIs(
"data-text-"
)}>`;
this.indent();
point.attributes.set('data-text-x', point.x)
point.attributes.set('data-text-y', point.y)
let lineHeight = point.attributes.get('data-text-lineheight') || 12
point.attributes.remove('data-text-lineheight')
let svg = `${this.nl()}<text ${point.attributes.renderIfPrefixIs('data-text-')}>`
this.indent()
// Multi-line text?
if (this.text.indexOf("\n") !== -1) {
let lines = this.text.split("\n");
svg += `<tspan>${lines.shift()}</tspan>`;
if (this.text.indexOf('\n') !== -1) {
let lines = this.text.split('\n')
svg += `<tspan>${lines.shift()}</tspan>`
for (let line of lines) {
svg += `<tspan x="${point.x}" dy="${lineHeight}">${line}</tspan>`;
svg += `<tspan x="${point.x}" dy="${lineHeight}">${line}</tspan>`
}
} else {
svg += `<tspan>${this.text}</tspan>`;
svg += `<tspan>${this.text}</tspan>`
}
this.outdent();
svg += this.nl() + "</text>";
this.outdent()
svg += this.nl() + '</text>'
return svg;
};
return svg
}
Svg.prototype.renderCircle = function(point) {
return `<circle cx="${point.x}" cy="${point.y}" r="${point.attributes.get(
"data-circle"
)}" ${point.attributes.renderIfPrefixIs("data-circle-")}></circle>`;
};
'data-circle'
)}" ${point.attributes.renderIfPrefixIs('data-circle-')}></circle>`
}
/** Returns SVG code for a snippet */
Svg.prototype.renderSnippet = function(snippet, part) {
let x = snippet.anchor.x;
let y = snippet.anchor.y;
let scale = snippet.attributes.get("data-scale");
let x = snippet.anchor.x
let y = snippet.anchor.y
let scale = snippet.attributes.get('data-scale')
if (scale) {
snippet.attributes.add("transform", `translate(${x}, ${y})`);
snippet.attributes.add("transform", `scale(${scale})`);
snippet.attributes.add("transform", `translate(${x * -1}, ${y * -1})`);
snippet.attributes.add('transform', `translate(${x}, ${y})`)
snippet.attributes.add('transform', `scale(${scale})`)
snippet.attributes.add('transform', `translate(${x * -1}, ${y * -1})`)
}
let rotate = snippet.attributes.get("data-rotate");
let rotate = snippet.attributes.get('data-rotate')
if (rotate) {
snippet.attributes.add("transform", `rotate(${rotate}, ${x}, ${y})`);
snippet.attributes.add('transform', `rotate(${rotate}, ${x}, ${y})`)
}
let svg = this.nl();
svg += `<use x="${x}" y="${y}" `;
svg += `xlink:href="#${snippet.def}" ${snippet.attributes.render()}>`;
svg += "</use>";
let svg = this.nl()
svg += `<use x="${x}" y="${y}" `
svg += `xlink:href="#${snippet.def}" ${snippet.attributes.render()}>`
svg += '</use>'
return svg;
};
return svg
}
/** Returns SVG code to open a group */
Svg.prototype.openGroup = function(id, attributes = false) {
let svg = this.nl() + this.nl();
svg += `<!-- Start of group #${id} -->`;
svg += this.nl();
svg += `<g id="${id}"`;
if (attributes) svg += ` ${attributes.render()}`;
svg += ">";
this.indent();
this.openGroups.push(id);
let svg = this.nl() + this.nl()
svg += `<!-- Start of group #${id} -->`
svg += this.nl()
svg += `<g id="${id}"`
if (attributes) svg += ` ${attributes.render()}`
svg += '>'
this.indent()
this.openGroups.push(id)
return svg;
};
return svg
}
/** Returns SVG code to close a group */
Svg.prototype.closeGroup = function() {
this.outdent();
this.outdent()
return `${this.nl()}</g>${this.nl()}<!-- end of group #${this.openGroups.pop()} -->`;
};
return `${this.nl()}</g>${this.nl()}<!-- end of group #${this.openGroups.pop()} -->`
}
/** Returns a linebreak + identation */
Svg.prototype.nl = function() {
return "\n" + this.tab();
};
return '\n' + this.tab()
}
/** Returns indentation */
Svg.prototype.tab = function() {
let space = "";
let space = ''
for (let i = 0; i < this.tabs; i++) {
space += " ";
space += ' '
}
return space;
};
return space
}
/** Increases indentation by 1 */
Svg.prototype.indent = function() {
this.tabs += 1;
};
this.tabs += 1
}
/** Decreases indentation by 1 */
Svg.prototype.outdent = function() {
this.tabs -= 1;
};
this.tabs -= 1
}
/** Returns an unused ID */
Svg.prototype.getId = function() {
this.freeId += 1;
this.freeId += 1
return "" + this.freeId;
};
return '' + this.freeId
}
export default Svg;
export default Svg

View file

@ -1,95 +1,89 @@
import Path from "./path";
import Point from "./point";
import Bezier from "bezier-js";
import Path from './path'
import Point from './point'
import Bezier from 'bezier-js'
export function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
return string.charAt(0).toUpperCase() + string.slice(1)
}
/** Returns internal hook name for a macro */
export function macroName(name) {
return `_macro_${name}`;
return `_macro_${name}`
}
/** Find intersection of two (endless) lines */
export function beamsIntersect(a1, a2, b1, b2) {
let slopeA = a1.slope(a2);
let slopeB = b1.slope(b2);
if (slopeA === slopeB) return false; // Parallel lines
let slopeA = a1.slope(a2)
let slopeB = b1.slope(b2)
if (slopeA === slopeB) return false // Parallel lines
if (a1.x === a2.x)
return new Point(a1.x, slopeB * a1.x + (b1.y - slopeB * b1.x));
if (a1.x === a2.x) return new Point(a1.x, slopeB * a1.x + (b1.y - slopeB * b1.x))
// Vertical line A
else if (b1.x === b2.x)
return new Point(b1.x, slopeA * b1.x + (a1.y - slopeA * a1.x));
else if (b1.x === b2.x) return new Point(b1.x, slopeA * b1.x + (a1.y - slopeA * a1.x))
// Vertical line B
else {
// Swap points if line A or B goes from right to left
if (a1.x > a2.x) a1 = a2.copy();
if (b1.x > b2.x) b1 = b2.copy();
if (a1.x > a2.x) a1 = a2.copy()
if (b1.x > b2.x) b1 = b2.copy()
// Find y intercept
let iA = a1.y - slopeA * a1.x;
let iB = b1.y - slopeB * b1.x;
let iA = a1.y - slopeA * a1.x
let iB = b1.y - slopeB * b1.x
// Find intersection
let x = (iB - iA) / (slopeA - slopeB);
let y = slopeA * x + iA;
let x = (iB - iA) / (slopeA - slopeB)
let y = slopeA * x + iA
return new Point(x, y);
return new Point(x, y)
}
}
/** Find intersection of two line segments */
export function linesIntersect(a1, a2, b1, b2) {
let p = beamsIntersect(a1, a2, b1, b2);
if (!p) return false;
let lenA = a1.dist(a2);
let lenB = b1.dist(b2);
let lenC = a1.dist(p) + p.dist(a2);
let lenD = b1.dist(p) + p.dist(b2);
if (
Math.round(lenA) == Math.round(lenC) &&
Math.round(lenB) == Math.round(lenD)
)
return p;
else return false;
let p = beamsIntersect(a1, a2, b1, b2)
if (!p) return false
let lenA = a1.dist(a2)
let lenB = b1.dist(b2)
let lenC = a1.dist(p) + p.dist(a2)
let lenD = b1.dist(p) + p.dist(b2)
if (Math.round(lenA) == Math.round(lenC) && Math.round(lenB) == Math.round(lenD)) return p
else return false
}
/** Finds out whether a point lies on an endless line */
export function pointOnBeam(from, to, check, precision = 1e6) {
if (from.sitsOn(check)) return true;
if (to.sitsOn(check)) return true;
let cross = check.dx(from) * to.dy(from) - check.dy(from) * to.dx(from);
if (from.sitsOn(check)) return true
if (to.sitsOn(check)) return true
let cross = check.dx(from) * to.dy(from) - check.dy(from) * to.dx(from)
if (Math.abs(Math.round(cross * precision) / precision) === 0) return true;
else return false;
if (Math.abs(Math.round(cross * precision) / precision) === 0) return true
else return false
}
/** Finds out whether a point lies on a line segment */
export function pointOnLine(from, to, check, precision = 1e6) {
if (!pointOnBeam(from, to, check, precision)) return false;
let lenA = from.dist(to);
let lenB = from.dist(check) + check.dist(to);
if (Math.round(lenA) == Math.round(lenB)) return true;
else return false;
if (!pointOnBeam(from, to, check, precision)) return false
let lenA = from.dist(to)
let lenB = from.dist(check) + check.dist(to)
if (Math.round(lenA) == Math.round(lenB)) return true
else return false
}
/** Finds out whether a point lies on a curve */
export function pointOnCurve(start, cp1, cp2, end, check) {
if (start.sitsOn(check)) return true;
if (end.sitsOn(check)) return true;
if (start.sitsOn(check)) return true
if (end.sitsOn(check)) return true
let curve = new Bezier(
{ x: start.x, y: start.y },
{ x: cp1.x, y: cp1.y },
{ x: cp2.x, y: cp2.y },
{ x: end.x, y: end.y }
);
)
let intersections = curve.intersects({
p1: { x: check.x - 1, y: check.y },
p2: { x: check.x + 1, y: check.y }
});
if (intersections.length > 0) return intersections.shift();
else return false;
})
if (intersections.length > 0) return intersections.shift()
else return false
}
/** Splits a curve on a point */
@ -97,7 +91,7 @@ export function splitCurve(start, cp1, cp2, end, split) {
let [c1, c2] = new Path()
.move(start)
.curve(cp1, cp2, end)
.split(split);
.split(split)
return [
{
@ -112,215 +106,201 @@ export function splitCurve(start, cp1, cp2, end, split) {
cp2: c2.ops[1].cp2,
end: c2.ops[1].to
}
];
]
}
/** Find where an (endless) line intersects with a certain X-value */
export function beamIntersectsX(from, to, x) {
if (from.x === to.x) return false; // Vertical line
let top = new Point(x, -10);
let bottom = new Point(x, 10);
if (from.x === to.x) return false // Vertical line
let top = new Point(x, -10)
let bottom = new Point(x, 10)
return beamsIntersect(from, to, top, bottom);
return beamsIntersect(from, to, top, bottom)
}
/** Find where an (endless) line intersects with a certain Y-value */
export function beamIntersectsY(from, to, y) {
if (from.y === to.y) return false; // Horizontal line
let left = new Point(-10, y);
let right = new Point(10, y);
if (from.y === to.y) return false // Horizontal line
let left = new Point(-10, y)
let right = new Point(10, y)
return beamsIntersect(from, to, left, right);
return beamsIntersect(from, to, left, right)
}
/** Convert value in mm to cm or imperial units */
export function units(value, to = "metric") {
if (to === "imperial") return round(value / 25.4) + '"';
else return round(value / 10) + "cm";
export function units(value, to = 'metric') {
if (to === 'imperial') return round(value / 25.4) + '"'
else return round(value / 10) + 'cm'
}
/** Find where a curve intersects with line */
export function lineIntersectsCurve(start, end, from, cp1, cp2, to) {
let intersections = [];
let intersections = []
let bz = new Bezier(
{ x: from.x, y: from.y },
{ x: cp1.x, y: cp1.y },
{ x: cp2.x, y: cp2.y },
{ x: to.x, y: to.y }
);
)
let line = {
p1: { x: start.x, y: start.y },
p2: { x: end.x, y: end.y }
};
}
for (let t of bz.intersects(line)) {
let isect = bz.get(t);
intersections.push(new Point(isect.x, isect.y));
let isect = bz.get(t)
intersections.push(new Point(isect.x, isect.y))
}
if (intersections.length === 0) return false;
else if (intersections.length === 1) return intersections[0];
else return intersections;
if (intersections.length === 0) return false
else if (intersections.length === 1) return intersections[0]
else return intersections
}
/** Find where a curve intersects with a given X-value */
export function curveIntersectsX(from, cp1, cp2, to, x) {
let start = new Point(x, -10000);
let end = new Point(x, 10000);
return lineIntersectsCurve(start, end, from, cp1, cp2, to);
let start = new Point(x, -10000)
let end = new Point(x, 10000)
return lineIntersectsCurve(start, end, from, cp1, cp2, to)
}
/** Find where a curve intersects with a given Y-value */
export function curveIntersectsY(from, cp1, cp2, to, y) {
let start = new Point(-10000, y);
let end = new Point(10000, y);
return lineIntersectsCurve(start, end, from, cp1, cp2, to);
let start = new Point(-10000, y)
let end = new Point(10000, y)
return lineIntersectsCurve(start, end, from, cp1, cp2, to)
}
/** Find where a curve intersects with another curve */
export function curvesIntersect(
fromA,
cp1A,
cp2A,
toA,
fromB,
cp1B,
cp2B,
toB
) {
let precision = 0.005; // See https://github.com/Pomax/bezierjs/issues/99
let intersections = [];
export function curvesIntersect(fromA, cp1A, cp2A, toA, fromB, cp1B, cp2B, toB) {
let precision = 0.005 // See https://github.com/Pomax/bezierjs/issues/99
let intersections = []
let curveA = new Bezier(
{ x: fromA.x, y: fromA.y },
{ x: cp1A.x, y: cp1A.y },
{ x: cp2A.x, y: cp2A.y },
{ x: toA.x, y: toA.y }
);
)
let curveB = new Bezier(
{ x: fromB.x, y: fromB.y },
{ x: cp1B.x, y: cp1B.y },
{ x: cp2B.x, y: cp2B.y },
{ x: toB.x, y: toB.y }
);
)
for (let tvalues of curveA.intersects(curveB, precision)) {
let intersection = curveA.get(tvalues.substr(0, tvalues.indexOf("/")));
intersections.push(new Point(intersection.x, intersection.y));
let intersection = curveA.get(tvalues.substr(0, tvalues.indexOf('/')))
intersections.push(new Point(intersection.x, intersection.y))
}
if (intersections.length === 0) return false;
else if (intersections.length === 1) return intersections.shift();
if (intersections.length === 0) return false
else if (intersections.length === 1) return intersections.shift()
else {
let unique = [];
let unique = []
for (let i of intersections) {
let dupe = false;
let dupe = false
for (let u of unique) {
if (i.sitsRoughlyOn(u)) dupe = true;
if (i.sitsRoughlyOn(u)) dupe = true
}
if (!dupe) unique.push(i);
if (!dupe) unique.push(i)
}
return unique;
return unique
}
}
/** Find the intersections between two circles */
export function circlesIntersect(c1, r1, c2, r2, sort = "x") {
let dx = c1.dx(c2);
let dy = c1.dy(c2);
let dist = c1.dist(c2);
export function circlesIntersect(c1, r1, c2, r2, sort = 'x') {
let dx = c1.dx(c2)
let dy = c1.dy(c2)
let dist = c1.dist(c2)
// Check for edge cases
if (dist > parseFloat(r1) + parseFloat(r2)) return false; // Circles do not intersect
if (dist < parseFloat(r2) - parseFloat(r1)) return false; // One circle is contained in the other
if (dist === 0 && r1 === r2) return false; // Two circles are identical
let chorddistance =
(Math.pow(r1, 2) - Math.pow(r2, 2) + Math.pow(dist, 2)) / (2 * dist);
let halfchordlength = Math.sqrt(Math.pow(r1, 2) - Math.pow(chorddistance, 2));
let chordmidpointx = c1.x + (chorddistance * dx) / dist;
let chordmidpointy = c1.y + (chorddistance * dy) / dist;
if (dist > parseFloat(r1) + parseFloat(r2)) return false // Circles do not intersect
if (dist < parseFloat(r2) - parseFloat(r1)) return false // One circle is contained in the other
if (dist === 0 && r1 === r2) return false // Two circles are identical
let chorddistance = (Math.pow(r1, 2) - Math.pow(r2, 2) + Math.pow(dist, 2)) / (2 * dist)
let halfchordlength = Math.sqrt(Math.pow(r1, 2) - Math.pow(chorddistance, 2))
let chordmidpointx = c1.x + (chorddistance * dx) / dist
let chordmidpointy = c1.y + (chorddistance * dy) / dist
let i1 = new Point(
chordmidpointx + (halfchordlength * dy) / dist,
chordmidpointy - (halfchordlength * dx) / dist
);
)
let i2 = new Point(
chordmidpointx - (halfchordlength * dy) / dist,
chordmidpointy + (halfchordlength * dx) / dist
);
)
if ((sort === "x" && i1.x <= i2.x) || (sort === "y" && i1.y <= i2.y))
return [i1, i2];
else return [i2, i1];
if ((sort === 'x' && i1.x <= i2.x) || (sort === 'y' && i1.y <= i2.y)) return [i1, i2]
else return [i2, i1]
}
/** Find the intersections between a beam and a circle */
export function beamIntersectsCircle(c, r, p1, p2, sort = "x") {
let dx = p2.x - p1.x;
let dy = p2.y - p1.y;
let A = Math.pow(dx, 2) + Math.pow(dy, 2);
let B = 2 * (dx * (p1.x - c.x) + dy * (p1.y - c.y));
let C = Math.pow(p1.x - c.x, 2) + Math.pow(p1.y - c.y, 2) - Math.pow(r, 2);
export function beamIntersectsCircle(c, r, p1, p2, sort = 'x') {
let dx = p2.x - p1.x
let dy = p2.y - p1.y
let A = Math.pow(dx, 2) + Math.pow(dy, 2)
let B = 2 * (dx * (p1.x - c.x) + dy * (p1.y - c.y))
let C = Math.pow(p1.x - c.x, 2) + Math.pow(p1.y - c.y, 2) - Math.pow(r, 2)
let det = Math.pow(B, 2) - 4 * A * C;
let det = Math.pow(B, 2) - 4 * A * C
if (A <= 0.0000001 || det < 0) return false;
if (A <= 0.0000001 || det < 0) return false
// No real solutions
else if (det === 0) {
// One solution
let t = (-1 * B) / (2 * A);
let i1 = new Point(p1.x + t * dx, p1.y + t * dy);
return [i1];
let t = (-1 * B) / (2 * A)
let i1 = new Point(p1.x + t * dx, p1.y + t * dy)
return [i1]
} else {
// Two solutions
let t = (-1 * B + Math.sqrt(det)) / (2 * A);
let i1 = new Point(p1.x + t * dx, p1.y + t * dy);
t = (-1 * B - Math.sqrt(det)) / (2 * A);
let i2 = new Point(p1.x + t * dx, p1.y + t * dy);
if ((sort === "x" && i1.x <= i2.x) || (sort === "y" && i1.y <= i2.y))
return [i1, i2];
else return [i2, i1];
let t = (-1 * B + Math.sqrt(det)) / (2 * A)
let i1 = new Point(p1.x + t * dx, p1.y + t * dy)
t = (-1 * B - Math.sqrt(det)) / (2 * A)
let i2 = new Point(p1.x + t * dx, p1.y + t * dy)
if ((sort === 'x' && i1.x <= i2.x) || (sort === 'y' && i1.y <= i2.y)) return [i1, i2]
else return [i2, i1]
}
}
/** Find the intersections between a line and a circle */
export function lineIntersectsCircle(c, r, p1, p2, sort = "x") {
let intersections = beamIntersectsCircle(c, r, p1, p2, sort);
if (intersections === false) return false;
export function lineIntersectsCircle(c, r, p1, p2, sort = 'x') {
let intersections = beamIntersectsCircle(c, r, p1, p2, sort)
if (intersections === false) return false
else {
if (intersections.length === 1) {
if (pointOnLine(p1, p2, intersections[0])) return intersections;
else return false;
if (pointOnLine(p1, p2, intersections[0])) return intersections
else return false
} else {
let i1 = intersections[0];
let i2 = intersections[1];
if (!pointOnLine(p1, p2, i1, 5) && !pointOnLine(p1, p2, i2, 5))
return false;
let i1 = intersections[0]
let i2 = intersections[1]
if (!pointOnLine(p1, p2, i1, 5) && !pointOnLine(p1, p2, i2, 5)) return false
else if (pointOnLine(p1, p2, i1, 5) && pointOnLine(p1, p2, i2, 5)) {
if ((sort === "x" && i1.x <= i2.x) || (sort === "y" && i1.y <= i2.y))
return [i1, i2];
else return [i2, i1];
} else if (pointOnLine(p1, p2, i1, 5)) return [i1];
else if (pointOnLine(p1, p2, i2, 5)) return [i2];
if ((sort === 'x' && i1.x <= i2.x) || (sort === 'y' && i1.y <= i2.y)) return [i1, i2]
else return [i2, i1]
} else if (pointOnLine(p1, p2, i1, 5)) return [i1]
else if (pointOnLine(p1, p2, i2, 5)) return [i2]
}
}
}
export function curveEdge(curve, edge, steps = 500) {
let x = Infinity;
let y = Infinity;
let p;
if (edge === "bottom") y = -Infinity;
if (edge === "right") x = -Infinity;
let x = Infinity
let y = Infinity
let p
if (edge === 'bottom') y = -Infinity
if (edge === 'right') x = -Infinity
for (let i = 0; i < steps; i++) {
p = curve.get(i / steps);
p = curve.get(i / steps)
if (
(edge === "top" && p.y < y) ||
(edge === "bottom" && p.y > y) ||
(edge === "right" && p.x > x) ||
(edge === "left" && p.x < x)
(edge === 'top' && p.y < y) ||
(edge === 'bottom' && p.y > y) ||
(edge === 'right' && p.x > x) ||
(edge === 'left' && p.x < x)
) {
x = p.x;
y = p.y;
x = p.x
y = p.y
}
}
return new Point(x, y);
return new Point(x, y)
}
/**
@ -335,23 +315,23 @@ export function curveEdge(curve, edge, steps = 500) {
* This method does that calculation.
*/
export function stretchToScale(stretch) {
return 1 / (1 + parseFloat(stretch));
return 1 / (1 + parseFloat(stretch))
}
export function round(value) {
return Math.round(value * 1e2) / 1e2;
return Math.round(value * 1e2) / 1e2
}
export function sampleStyle(run, runs) {
let hue = (run - 1) * (330 / runs);
let hue = (run - 1) * (330 / runs)
return `stroke: hsl(${hue}, 100%, 35%);`;
return `stroke: hsl(${hue}, 100%, 35%);`
}
export function deg2rad(degrees) {
return degrees * (Math.PI / 180);
return degrees * (Math.PI / 180)
}
export function rad2deg(radians) {
return (radians / Math.PI) * 180;
return (radians / Math.PI) * 180
}