🎨 Updated prettier config
This commit is contained in:
parent
b8e632998b
commit
6710d76b08
401 changed files with 13193 additions and 15620 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -8,5 +8,5 @@ export default function Hooks() {
|
|||
postRender: [],
|
||||
insertText: [],
|
||||
debug: []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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: {}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue