2018-08-10 19:04:25 +02:00
|
|
|
import * as utils from "./utils";
|
2018-08-05 18:19:48 +02:00
|
|
|
import Point from "./point";
|
|
|
|
import Path from "./path";
|
|
|
|
import Snippet from "./snippet";
|
|
|
|
import Attributes from "./attributes";
|
2018-12-18 15:35:07 +01:00
|
|
|
import hooks from "./hooks";
|
2018-07-23 11:12:06 +00:00
|
|
|
|
2018-08-05 18:19:48 +02:00
|
|
|
function Part() {
|
|
|
|
this.attributes = new Attributes();
|
2018-07-23 11:12:06 +00:00
|
|
|
this.points = {};
|
|
|
|
this.paths = {};
|
|
|
|
this.snippets = {};
|
2018-08-01 14:55:54 +02:00
|
|
|
this.freeId = 0;
|
2018-08-01 18:18:29 +02:00
|
|
|
this.topLeft = false;
|
|
|
|
this.bottomRight = false;
|
2018-08-03 14:20:28 +02:00
|
|
|
this.width = false;
|
|
|
|
this.height = false;
|
2018-08-05 14:04:15 +02:00
|
|
|
this.render = true;
|
2018-08-10 19:04:25 +02:00
|
|
|
this.utils = utils;
|
2018-07-23 11:12:06 +00:00
|
|
|
|
2018-07-24 14:38:03 +00:00
|
|
|
// Constructors so macros can create objects
|
2018-08-05 18:19:48 +02:00
|
|
|
this.Point = Point;
|
|
|
|
this.Path = Path;
|
|
|
|
this.Snippet = Snippet;
|
2018-07-24 14:38:03 +00:00
|
|
|
|
2018-12-18 15:35:07 +01:00
|
|
|
this.hooks = hooks; // Hooks container
|
|
|
|
|
2018-07-23 20:14:32 +02:00
|
|
|
return this;
|
|
|
|
}
|
2018-07-23 11:12:06 +00:00
|
|
|
|
2018-08-08 14:02:06 +02:00
|
|
|
Part.prototype.macroClosure = function(args) {
|
2018-07-23 20:14:32 +02:00
|
|
|
let self = this;
|
2018-08-13 11:04:22 +02:00
|
|
|
let method = function(key, args) {
|
2018-08-10 19:04:25 +02:00
|
|
|
let macro = utils.macroName(key);
|
2018-07-23 20:14:32 +02:00
|
|
|
if (typeof self[macro] === "function") {
|
2018-08-13 11:04:22 +02:00
|
|
|
self[macro](args);
|
2018-07-24 14:38:03 +00:00
|
|
|
} else {
|
2018-12-18 15:35:07 +01:00
|
|
|
self.debug({
|
|
|
|
type: "warning",
|
|
|
|
label: "🚨 Macro not found",
|
|
|
|
msg: `Macro ${key} is not registered`
|
|
|
|
});
|
2018-07-23 20:14:32 +02:00
|
|
|
}
|
2018-07-23 11:44:34 +00:00
|
|
|
};
|
2018-07-23 17:35:06 +00:00
|
|
|
|
2018-07-23 20:14:32 +02:00
|
|
|
return method;
|
|
|
|
};
|
2018-07-23 11:12:06 +00:00
|
|
|
|
2018-08-08 14:02:06 +02:00
|
|
|
Part.prototype.debugClosure = function() {
|
|
|
|
let self = this;
|
2018-12-18 15:35:07 +01:00
|
|
|
let method = function(data) {
|
|
|
|
self.debug(data);
|
2018-08-08 14:02:06 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
return method;
|
|
|
|
};
|
|
|
|
|
2018-12-18 15:35:07 +01:00
|
|
|
Part.prototype.runHooks = function(hookName, data = false) {
|
|
|
|
if (data === false) data = this;
|
|
|
|
let hooks = this.hooks[hookName];
|
|
|
|
if (hooks.length > 0) {
|
|
|
|
for (let hook of hooks) {
|
|
|
|
hook.method(data, hook.data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Debug method */
|
|
|
|
Part.prototype.debug = function(data) {
|
|
|
|
this.runHooks("debug", data);
|
|
|
|
};
|
2018-08-07 13:46:38 +02:00
|
|
|
|
2018-08-01 14:55:54 +02:00
|
|
|
/** Returns an unused ID */
|
2018-08-23 15:57:23 +02:00
|
|
|
Part.prototype.getId = function() {
|
2018-08-01 14:55:54 +02:00
|
|
|
this.freeId += 1;
|
|
|
|
|
|
|
|
return "" + this.freeId;
|
|
|
|
};
|
|
|
|
|
2018-08-01 18:18:29 +02:00
|
|
|
/** Returns a value formatted for units provided in settings */
|
2018-08-08 14:02:06 +02:00
|
|
|
Part.prototype.unitsClosure = function(value) {
|
|
|
|
let self = this;
|
|
|
|
let method = function(value) {
|
2018-08-10 19:04:25 +02:00
|
|
|
return utils.units(value, self.context.settings.units);
|
2018-08-08 14:02:06 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
return method;
|
2018-08-01 14:55:54 +02:00
|
|
|
};
|
|
|
|
|
2018-08-01 18:18:29 +02:00
|
|
|
/** Calculates the part's bounding box and sets it */
|
2018-08-05 18:19:48 +02:00
|
|
|
Part.prototype.boundary = function() {
|
2018-08-01 18:18:29 +02:00
|
|
|
if (this.topLeft) return this; // Cached
|
|
|
|
|
2018-08-05 18:19:48 +02:00
|
|
|
let topLeft = new Point(Infinity, Infinity);
|
|
|
|
let bottomRight = new Point(-Infinity, -Infinity);
|
2018-08-01 18:18:29 +02:00
|
|
|
for (let key in this.paths) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2018-08-22 11:34:08 +02:00
|
|
|
for (let key in this.points) {
|
|
|
|
let point = this.points[key];
|
2018-09-05 16:52:06 +02:00
|
|
|
let radius = point.attributes.get("data-circle");
|
2018-08-22 11:34:08 +02:00
|
|
|
if (radius) {
|
2018-09-05 16:52:06 +02:00
|
|
|
radius = parseFloat(radius);
|
2018-08-22 11:34:08 +02:00
|
|
|
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
|
2018-08-12 16:19:04 +02:00
|
|
|
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;
|
2018-09-11 11:39:50 +02:00
|
|
|
// 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);
|
2018-08-03 14:20:28 +02:00
|
|
|
this.width = this.bottomRight.x - this.topLeft.x;
|
|
|
|
this.height = this.bottomRight.y - this.topLeft.y;
|
2018-08-01 18:18:29 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Stacks part so that its top left corner is in (0,0) */
|
2018-08-05 18:19:48 +02:00
|
|
|
Part.prototype.stack = function() {
|
2018-08-16 11:54:34 +02:00
|
|
|
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})`
|
|
|
|
);
|
2018-08-01 18:18:29 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Adds an attribute. This is here to make this call chainable in assignment */
|
2018-08-05 18:19:48 +02:00
|
|
|
Part.prototype.attr = function(name, value, overwrite = false) {
|
2018-08-05 15:52:37 +02:00
|
|
|
if (overwrite) this.attributes.set(name, value);
|
|
|
|
else this.attributes.add(name, value);
|
2018-08-01 18:18:29 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2018-08-05 15:52:37 +02:00
|
|
|
/** Copies point/path/snippet data from part orig into this */
|
2018-12-16 18:06:01 +01:00
|
|
|
Part.prototype.inject = function(orig) {
|
2018-08-05 15:52:37 +02:00
|
|
|
for (let type of ["points", "paths", "snippets"]) {
|
|
|
|
for (let i in orig[type]) {
|
|
|
|
if (typeof this[type][i] === "undefined") {
|
|
|
|
this[type][i] = orig[type][i].clone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
2018-08-03 14:20:28 +02:00
|
|
|
};
|
2018-08-05 15:52:37 +02:00
|
|
|
|
2018-08-08 14:02:06 +02:00
|
|
|
Part.prototype.units = function(input) {
|
2018-08-11 08:58:40 +02:00
|
|
|
return utils.units(input, this.context.settings.units);
|
2018-08-08 14:02:06 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/** Returns an object with shorthand access for pattern design */
|
|
|
|
Part.prototype.shorthand = function() {
|
2018-09-12 17:34:53 +02:00
|
|
|
let complete = this.context.settings.complete ? true : false;
|
2018-08-08 14:02:06 +02:00
|
|
|
let paperless = this.context.settings.paperless === true ? true : false;
|
2018-09-12 17:34:53 +02:00
|
|
|
let sa = this.context.settings.complete ? this.context.settings.sa || 0 : 0;
|
2018-08-08 14:02:06 +02:00
|
|
|
return {
|
2018-09-12 17:34:53 +02:00
|
|
|
sa,
|
2018-08-08 14:02:06 +02:00
|
|
|
measurements: this.context.settings.measurements || {},
|
2018-12-11 18:49:00 +01:00
|
|
|
options: this.context.settings.options || {},
|
2018-08-08 14:02:06 +02:00
|
|
|
store: this.context.store,
|
|
|
|
points: this.points || {},
|
|
|
|
paths: this.paths || {},
|
|
|
|
snippets: this.snippets || {},
|
|
|
|
macro: this.macroClosure(),
|
|
|
|
units: this.unitsClosure(),
|
2018-08-14 16:16:46 +02:00
|
|
|
utils: utils,
|
2018-08-08 14:02:06 +02:00
|
|
|
Point: this.Point,
|
|
|
|
Path: this.Path,
|
|
|
|
Snippet: this.Snippet,
|
2018-09-12 17:34:53 +02:00
|
|
|
complete,
|
2018-08-08 14:02:06 +02:00
|
|
|
paperless,
|
|
|
|
debug: this.debugClosure()
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2018-08-05 18:19:48 +02:00
|
|
|
export default Part;
|