1
0
Fork 0

🚧 Bin packing integration

This commit is contained in:
Joost De Cock 2018-08-01 18:18:29 +02:00
parent 6f52ccfd2e
commit 9a60dc5756
6 changed files with 130 additions and 2 deletions

5
package-lock.json generated
View file

@ -836,6 +836,11 @@
"live-server": "1.2.0"
}
},
"bin-pack": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bin-pack/-/bin-pack-1.0.2.tgz",
"integrity": "sha1-wqAU7b8L7XCjKSBi7UZXe5YSBnk="
},
"binary-extensions": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",

View file

@ -43,6 +43,7 @@
},
"dependencies": {
"bezier-js": "^2.2.13",
"bin-pack": "1.0.2",
"hooks": "^0.3.2"
},
"devDependencies": {

View file

@ -13,6 +13,8 @@ function part(id) {
this.snippets = {};
this.id = id;
this.freeId = 0;
this.topLeft = false;
this.bottomRight = false;
this.render = id.substr(0, 1) === "_" ? false : true;
this.points.origin = new point(0, 0);
for (let k in hooklib) this[k] = hooklib[k];
@ -50,8 +52,52 @@ part.prototype.getUid = function() {
return "" + this.freeId;
};
/** Returns a value formatted for units provided in settings */
part.prototype.units = function(value) {
return units(value, this.context.settings.units);
};
/** Calculates the part's bounding box and sets it */
part.prototype.boundary = function() {
if (this.topLeft) return this; // Cached
let topLeft = new point(Infinity, Infinity);
let bottomRight = new point(-Infinity, -Infinity);
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;
}
}
// Add 10mm margin
this.topLeft = new point(topLeft.x - 10, topLeft.y - 10);
this.bottomRight = new point(bottomRight.x + 10, bottomRight.y + 10);
return this;
};
/** Stacks part so that its top left corner is in (0,0) */
part.prototype.stack = function() {
if (this.topLeft.x === 0 && this.topLeft.y === 0) return this;
this.boundary().attr(
"transform",
`translate(${this.topLeft.x * -1}, ${this.topLeft.y * -1})`
);
return this;
};
/** Adds an attribute. This is here to make this call chainable in assignment */
part.prototype.attr = function(name, value) {
this.attributes.add(name, value);
return this;
};
export default part;

View file

@ -1,9 +1,12 @@
import attributes from "./attributes";
import point from "./point";
import Bezier from "bezier-js";
import { pathOffset, pathLength } from "./utils";
function path() {
this.render = true;
this.topLeft = false;
this.bottomRight = false;
this.attributes = new attributes();
this.ops = [];
}
@ -114,4 +117,39 @@ path.prototype.end = function() {
if (op.type === "close") return this.start();
else return op.to;
};
/** Finds the bounding box of a path */
path.prototype.boundary = function() {
if (this.topLeft) return this; // Cached
let current;
let topLeft = new point(Infinity, Infinity);
let bottomRight = new point(-Infinity, -Infinity);
for (let i in this.ops) {
let op = this.ops[i];
if (op.type === "move" || op.type === "line") {
if (op.to.x < topLeft.x) topLeft.x = op.to.x;
if (op.to.y < topLeft.y) topLeft.y = op.to.y;
if (op.to.x > bottomRight.x) bottomRight.x = op.to.x;
if (op.to.y > bottomRight.y) bottomRight.y = op.to.y;
} else if (op.type === "curve") {
let bb = new Bezier(
{ x: current.x, y: current.y },
{ x: op.cp1.x, y: op.cp1.y },
{ x: op.cp2.x, y: op.cp2.y },
{ x: op.to.x, y: op.to.y }
).bbox();
if (bb.x.min < topLeft.x) topLeft.x = bb.x.min;
if (bb.y.min < topLeft.y) topLeft.y = bb.y.min;
if (bb.x.max > bottomRight.x) bottomRight.x = bb.x.max;
if (bb.y.max > bottomRight.y) bottomRight.y = bb.y.max;
}
if (op.to) current = op.to;
}
this.topLeft = topLeft;
this.bottomRight = bottomRight;
return this;
};
export default path;

View file

@ -1,3 +1,4 @@
import attributes from "./attributes";
import { macroName } from "./utils";
import part from "./part";
import point from "./point";
@ -5,6 +6,7 @@ import path from "./path";
import snippet from "./snippet";
import svg from "./svg";
import hooks from "./hooks";
import pack from "bin-pack";
export default function pattern(config = false) {
// Allow no-config patterns
@ -26,6 +28,8 @@ export default function pattern(config = false) {
throw "Could not create pattern: You should define at least one part in your pattern config";
}
this.width = false;
this.height = false;
// Constructors
this.point = point;
this.path = path;
@ -77,6 +81,7 @@ pattern.prototype.draft = function() {
pattern.prototype.render = function() {
this.hooks.attach("preRenderSvg", this.svg);
this.hooks.attach("postRenderSvg", this.svg);
//this.hooks.attach('insertText', this.svg);
@ -122,3 +127,29 @@ pattern.prototype.macro = function(key, method) {
this.hooks.attach(name, part);
}
};
/** Packs parts in a 2D space and sets pattern size */
pattern.prototype.pack = function() {
let bins = [];
for (let key in this.parts) {
let part = this.parts[key];
if (part.render) {
part.stack();
bins.push({
id: part.id,
width: part.bottomRight.x - part.topLeft.x,
height: part.bottomRight.y - part.topLeft.y
});
}
}
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})`);
}
this.width = size.width;
this.height = size.height;
return this;
};

View file

@ -41,6 +41,11 @@ svg.prototype.insertText = function() {};
/** Renders a draft object as SVG */
svg.prototype.render = function(pattern) {
this.preRenderSvg();
// this needs to run after the preSvgRender hook as it might add stuff
pattern.pack();
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.svg = this.prefix;
this.svg += this.renderComments(this.header);
this.svg += this.renderSvgTag(pattern);
@ -204,11 +209,13 @@ svg.prototype.renderSnippet = function(snippet) {
};
/** Returns SVG code to open a group */
svg.prototype.openGroup = function(id) {
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}">`;
svg += `<g id="${id}"`;
if (attributes) svg += ` ${attributes.render()}`;
svg += ">";
this.indent();
this.openGroups.push(id);