diff --git a/packages/benjamin/LICENSE b/packages/benjamin/LICENSE
new file mode 100644
index 00000000000..5f07b8da297
--- /dev/null
+++ b/packages/benjamin/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 freesewing
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/benjamin/README.md b/packages/benjamin/README.md
new file mode 100644
index 00000000000..20f4855f490
--- /dev/null
+++ b/packages/benjamin/README.md
@@ -0,0 +1,2 @@
+# benjamin
+Port of the Benjamin bowtie pattern to JavaScript
diff --git a/packages/benjamin/index.html b/packages/benjamin/index.html
new file mode 100644
index 00000000000..ca0bd5f2927
--- /dev/null
+++ b/packages/benjamin/index.html
@@ -0,0 +1,32 @@
+
+
+
+
+ Pattern Template
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/benjamin/package.json b/packages/benjamin/package.json
new file mode 100644
index 00000000000..60309fc0114
--- /dev/null
+++ b/packages/benjamin/package.json
@@ -0,0 +1,78 @@
+{
+ "version": "0.2.0",
+ "name": "@freesewing/benjamin",
+ "description": "Freesewing pattern for benjamin",
+ "author": "Wouter van Wageningen (https://github.com/woutervdub)",
+ "license": "MIT",
+ "homepage": "https://github.com/freesewing/benjamin#readme",
+ "repository": "github:freesewing/benjamin",
+ "bugs": {
+ "url": "https://github.com/freesewing/benjamin/issues"
+ },
+ "keywords": [
+ "freesewing",
+ "pattern",
+ "sewing"
+ ],
+ "main": "dist/index.js",
+ "module": "dist/index.mjs",
+ "unpkg": "dist/browser.js",
+ "scripts": {
+ "precommit": "npm run pretty && lint-staged",
+ "patch": "npm version patch -m ':bookmark: v%s' && npm run build",
+ "minor": "npm version minor -m ':bookmark: v%s' && npm run build",
+ "major": "npm version major -m ':bookmark: v%s' && npm run build",
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "clean": "rimraf dist",
+ "pretty": "npx prettier --write 'src/*.js'",
+ "lint": "eslint --fix 'src/*.js'",
+ "watch": "nodemon -w src -w config --exec 'rollup -c rollup.config.js -o dist/browser.js -f iife -m true -n freesewing_patterns_florent --footer \"freesewing.patterns.benjamin=freesewing_patterns_template;\"'",
+ "browserbuild": "rollup -c rollup.config.js -o dist/browser.js -f iife -m true -n freesewing_patterns_benjamin --footer \"freesewing.patterns.template = freesewing_patterns_benjamin;\"",
+ "nodebuild": "rollup -c rollup.config.js -o dist/index.js -f cjs -m true",
+ "modulebuild": "rollup -c rollup.config.js -o dist/index.mjs -f es -m true",
+ "build": "npm run clean && npm run browserbuild && npm run nodebuild && npm run modulebuild"
+ },
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
+ },
+ "lint-staged": {
+ "*.{js,json}": [
+ "prettier --write",
+ "git add"
+ ]
+ },
+ "dependencies": {
+ "@freesewing/plugin-bundle": "0.8.0",
+ "freesewing": "latest"
+ },
+ "devDependencies": {
+ "@babel/core": "7.4.0",
+ "@freesewing/antman": "latest",
+ "@freesewing/models": "latest",
+ "@freesewing/plugin-debug": "latest",
+ "@freesewing/plugin-designer": "latest",
+ "@freesewing/plugin-i18n": "latest",
+ "@freesewing/plugin-theme": "latest",
+ "@freesewing/plugin-validate": "latest",
+ "eslint": "5.15.3",
+ "eslint-config-prettier": "4.1.0",
+ "eslint-plugin-prettier": "latest",
+ "husky": "latest",
+ "lint-staged": "8.1.5",
+ "prettier": "latest",
+ "rimraf": "latest",
+ "rollup": "1.7.2",
+ "rollup-plugin-babel": "latest",
+ "rollup-plugin-babel-minify": "8.0.0",
+ "rollup-plugin-json": "4.0.0",
+ "rollup-plugin-node-resolve": "latest"
+ },
+ "files": [
+ "dist/*",
+ "README.md",
+ "package-lock.json",
+ "package.json"
+ ]
+}
diff --git a/packages/benjamin/rollup.config.js b/packages/benjamin/rollup.config.js
new file mode 100644
index 00000000000..7ad9e644642
--- /dev/null
+++ b/packages/benjamin/rollup.config.js
@@ -0,0 +1,27 @@
+import babel from "rollup-plugin-babel";
+import resolve from "rollup-plugin-node-resolve";
+import json from "rollup-plugin-json";
+import minify from "rollup-plugin-babel-minify";
+//import peerDepsExternal from "rollup-plugin-peer-deps-external";
+import { name, version, description, author, license } from "./package.json";
+
+export default {
+ input: "src/index.js",
+ output: {
+ sourcemap: true
+ },
+ plugins: [
+ //peerDepsExternal(),
+ resolve({ browser: true }),
+ json()
+ //babel({
+ // exclude: "node_modules/**",
+ // plugins: ["@babel/plugin-proposal-object-rest-spread"]
+ //})
+ //minify({
+ // comments: false,
+ // sourceMap: true,
+ // banner: `/**\n * ${name} | v${version}\n * ${description}\n * (c) ${new Date().getFullYear()} ${author}\n * @license ${license}\n */`
+ //})
+ ]
+};
diff --git a/packages/benjamin/src/front.js b/packages/benjamin/src/front.js
new file mode 100644
index 00000000000..68a27b3538b
--- /dev/null
+++ b/packages/benjamin/src/front.js
@@ -0,0 +1,293 @@
+function FlipAllPointsY(points, mirror, start, end) {
+ for (var point in points) {
+ if (point.substr(0, start.length) == start) {
+ console.log(points[point]);
+ points[end + point.substr(start.length)] = points[point].flipY(mirror);
+ }
+ }
+}
+
+export default function(part) {
+ let {
+ store,
+ sa,
+ Point,
+ points,
+ Path,
+ paths,
+ Snippet,
+ snippets,
+ options,
+ measurements,
+ final,
+ paperless,
+ macro
+ } = part.shorthand();
+
+ // Remove from here
+ //options.bowStyle = 'butterfly';
+ //options.bowStyle = 'square';
+ //options.bowStyle = 'widesquare';
+ //options.bowStyle = 'diamond';
+ //options.endStyle = 'straight' ;
+ //options.endStyle = 'rounded' ;
+ //options.endStyle = 'pointed' ;
+
+ //final = true;
+ //paperless = true;
+ //sa = 10;
+ // Remove to here
+
+ if (options.bowStyle == "square") options.tipWidth = options.knotWidth;
+
+ let backWidth = 24;
+ let bandLength = 70;
+ let transitionLength = 50;
+ let halfBackWidth = backWidth / 2;
+ let halfTipWidth = options.tipWidth / 2;
+ let halfBowLength = options.bowLength / 2;
+ let halfKnotWidth = options.knotWidth / 2;
+
+ console.log(options.bowStyle);
+ console.log(options.knotWidth);
+
+ let butterfly =
+ options.bowStyle == "butterfly" || options.bowStyle == "diamond";
+
+ let tipAdjustment = 0;
+ switch (options.endStyle) {
+ case "pointed":
+ tipAdjustment = halfTipWidth / 1.3;
+ break;
+ case "rounded":
+ tipAdjustment = halfTipWidth;
+ break;
+ }
+
+ let hhbl /*halfHalfBowLength */ = halfBowLength / 2;
+ let bcAdjust = options.bowStyle == "diamond" ? 10 : hhbl - 5;
+
+ let tieStart = new Path();
+ let tieLowKnot = new Path();
+ let tieTip = new Path();
+ let tieTopKnot = new Path();
+ let tieEnd = new Path();
+
+ points.Origin = new Point(0, 0);
+
+ points.LowBandEdge = points.Origin.shift(-90, halfBackWidth);
+ points.LowKnotEdge = points.Origin.shift(-90, halfKnotWidth);
+ points.LowTipEdge = points.Origin.shift(-90, halfTipWidth);
+
+ points.LowBandStart = points.LowBandEdge.clone();
+ points.LowBandEnd = points.LowBandEdge.shift(0, bandLength);
+ points.LowTransitionEnd = points.LowKnotEdge.shift(
+ 0,
+ bandLength + transitionLength
+ );
+
+ console.log(points["LowBandEdge"].x);
+
+ tieStart
+ .move(points.Origin)
+ .line(points.LowBandStart)
+ .line(points.LowBandEnd)
+ .line(points.LowTransitionEnd);
+
+ points.LowTip = points.LowTipEdge.shift(
+ 0,
+ bandLength + transitionLength + halfBowLength * 3
+ );
+ points.Tip = points.Origin.shift(
+ 0,
+ bandLength + transitionLength + halfBowLength * 3 + tipAdjustment
+ );
+
+ if (butterfly) {
+ points.LowTransitionEndCp2 = points.LowBandEnd.shiftOutwards(
+ points.LowTransitionEnd,
+ bcAdjust
+ );
+ points.LowHump = points.LowTipEdge.shift(
+ 0,
+ bandLength + transitionLength + halfBowLength
+ );
+ points.LowHumpCp1 = points.LowHump.shift(180, bcAdjust);
+ points.LowHumpCp2 = points.LowHump.shift(0, bcAdjust);
+ points.LowValley = points.LowKnotEdge.shift(
+ 0,
+ bandLength + transitionLength + halfBowLength * 2
+ );
+ points.LowValleyCp1 = points.LowValley.shift(180, bcAdjust);
+ points.LowValleyCp2 = points.LowValley.shift(0, bcAdjust);
+ points.LowTipCp1 = points.LowTip.shift(180, bcAdjust);
+ }
+
+ points.LowTipCp2 = points.LowTip.shiftTowards(
+ points.LowTipEdge,
+ -tipAdjustment
+ );
+ points.TipCp1 = points.Tip.shift(-90, halfTipWidth / 4);
+ points.TipCp2 = points.Tip.shift(90, halfTipWidth / 4);
+
+ FlipAllPointsY(points, points.Origin, "Low", "Top");
+
+ if (options.endStyle == "rounded") {
+ tieTip
+ .move(points.LowTip)
+ .curve(points.LowTipCp2, points.TipCp1, points.Tip)
+ .curve(points.TipCp2, points.TopTipCp2, points.TopTip);
+ } else {
+ tieTip
+ .move(points.LowTip)
+ .line(points.Tip)
+ .line(points.TopTip);
+ }
+
+ if (butterfly) {
+ tieLowKnot
+ .move(points.LowTransitionEnd)
+ .curve(points.LowTransitionEndCp2, points.LowHumpCp1, points.LowHump)
+ .curve(points.LowHumpCp2, points.LowValleyCp1, points.LowValley)
+ .curve(points.LowValleyCp2, points.LowTipCp1, points.LowTip);
+ tieTopKnot = tieTopKnot
+ .move(points.TopTransitionEnd)
+ .curve(points.TopTransitionEndCp2, points.TopHumpCp1, points.TopHump)
+ .curve(points.TopHumpCp2, points.TopValleyCp1, points.TopValley)
+ .curve(points.TopValleyCp2, points.TopTipCp1, points.TopTip)
+ .reverse();
+ }
+
+ tieEnd
+ .move(points.TopTransitionEnd)
+ .line(points.TopBandEnd)
+ .line(points.TopBandStart)
+ .line(points.Origin);
+
+ paths.tie = new Path()
+ .move(points.Origin)
+ .join(tieStart)
+ .join(tieLowKnot)
+ .join(tieTip)
+ .join(tieTopKnot)
+ .join(tieEnd)
+ .close()
+ .attr("class", "fabric");
+
+ // Final?
+ if (final) {
+ points.grainlineLeft = points.Origin.shift(0, 30);
+ points.grainlineRight = points.Origin.shift(
+ 0,
+ bandLength + transitionLength
+ );
+ macro("grainline", {
+ from: points.grainlineLeft,
+ to: points.grainlineRight
+ });
+
+ points.titleAnchor = points.Origin.shift(
+ 0,
+ bandLength + transitionLength + halfBowLength + 6
+ );
+ macro("title", { at: points.titleAnchor, nr: 1, title: "Bow Tie" });
+
+ if (!paperless) {
+ let scaleboxAnchor = points.LowTipEdge.shift(-90, 30).shift(0, 50);
+ macro("scalebox", { at: scaleboxAnchor });
+ }
+
+ let logoAnchor = points.Tip.shift(0, 40);
+ snippets.logo = new Snippet("logo", logoAnchor);
+
+ // Dummy line
+ let h1 = logoAnchor.shift(0, 50);
+ let h2 = h1.shift(0, 1);
+
+ paths.hidden = new Path()
+ .move(h1)
+ .line(h2)
+ .attr("class", "hidden");
+ }
+
+ if (sa) {
+ paths.sa = paths.tie.offset(sa).attr("class", "fabric sa");
+ }
+
+ // Paperless?
+ if (paperless) {
+ macro("hd", {
+ from: points.LowBandStart,
+ to: points.LowBandEnd,
+ y: points.LowBandEdge.y + 15
+ });
+ macro("hd", {
+ from: points.LowBandEnd,
+ to: points.LowTransitionEnd,
+ y: points.LowBandEdge.y + 15
+ });
+ macro("vd", {
+ from: points.LowBandStart,
+ to: points.TopBandStart,
+ x: points.LowBandStart.x - 15
+ });
+ macro("vd", {
+ from: points.LowTip,
+ to: points.TopTip,
+ x: points.Tip.x + 15
+ });
+
+ if (butterfly) {
+ macro("hd", {
+ from: points.LowTransitionEnd,
+ to: points.LowHump,
+ y: points.LowTip.y + 15
+ });
+ macro("hd", {
+ from: points.LowHump,
+ to: points.LowValley,
+ y: points.LowTip.y + 15
+ });
+ macro("hd", {
+ from: points.LowValley,
+ to: points.LowTip,
+ y: points.LowTip.y + 15
+ });
+
+ //macro('vd', { from: points.LowTransitionEnd, to: points.TopTransitionEnd, x: points.LowTransitionEnd.x });
+ macro("vd", {
+ from: points.LowHump,
+ to: points.TopHump,
+ x: points.LowHump.x
+ });
+ macro("vd", {
+ from: points.LowValley,
+ to: points.TopValley,
+ x: points.LowValley.x
+ });
+ } else {
+ if (options.bowStyle == "widesquare") {
+ macro("vd", {
+ from: points.LowTransitionEnd,
+ to: points.TopTransitionEnd,
+ x: points.LowTransitionEnd.x
+ });
+ }
+ macro("hd", {
+ from: points.LowTransitionEnd,
+ to: points.LowTip,
+ y: points.LowTip.y + 15
+ });
+ }
+
+ if (tipAdjustment) {
+ macro("hd", {
+ from: points.LowTip,
+ to: points.Tip,
+ y: points.LowTip.y + 15
+ });
+ }
+ }
+
+ return part;
+}
diff --git a/packages/benjamin/src/index.js b/packages/benjamin/src/index.js
new file mode 100644
index 00000000000..e1c6a79926b
--- /dev/null
+++ b/packages/benjamin/src/index.js
@@ -0,0 +1,13 @@
+import freesewing from "freesewing";
+import plugins from "@freesewing/plugin-bundle";
+import config from "../config";
+// Parts
+import draftFront from "./front";
+
+// Create new design
+const benjamin = new freesewing.Design(config, plugins);
+
+// Attach draft methods to prototype
+benjamin.prototype.draftFront = draftFront;
+
+export default benjamin;