2018-08-06 16:19:12 +02:00
|
|
|
import freesewing from "freesewing";
|
|
|
|
|
2018-08-08 14:38:19 +02:00
|
|
|
/** Calculates the differece between actual and optimal sleevecap length
|
|
|
|
* Positive values mean sleevecap is longer than armhole
|
|
|
|
*/
|
2018-08-07 16:42:48 +02:00
|
|
|
function sleevecapDelta(store) {
|
2018-08-08 14:38:19 +02:00
|
|
|
return store.get("sleevecapLength") - store.get("sleevecapTarget");
|
2018-08-07 16:42:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function sleevecapAdjust(store) {
|
|
|
|
let delta = sleevecapDelta(store);
|
|
|
|
let factor = store.get("sleeveFactor");
|
2018-08-08 14:38:19 +02:00
|
|
|
if (delta > 0) factor = factor * 0.98;
|
|
|
|
else factor = factor * 1.02;
|
2018-08-07 16:42:48 +02:00
|
|
|
store.set("sleeveFactor", factor);
|
|
|
|
}
|
|
|
|
|
2018-08-08 14:38:19 +02:00
|
|
|
function draftSleevecap(part, run) {
|
2018-08-07 16:42:48 +02:00
|
|
|
// prettier-ignore
|
2018-08-08 14:38:19 +02:00
|
|
|
let {debug, units, store, measurements, options, Point, points, Path, paths} = part.shorthand();
|
2018-08-07 16:42:48 +02:00
|
|
|
// Sleeve center axis
|
2018-08-08 14:56:30 +02:00
|
|
|
points.centerBiceps = new Point(0, 0);
|
|
|
|
points.centerCap = points.centerBiceps.shift(
|
|
|
|
90,
|
|
|
|
measurements.bicepsCircumference *
|
|
|
|
(1 + options.bicepsEase) *
|
|
|
|
options.armholeDepthFactor *
|
|
|
|
store.get("sleeveFactor")
|
2018-08-07 16:42:48 +02:00
|
|
|
);
|
|
|
|
|
2018-08-08 16:12:56 +02:00
|
|
|
// Left and right biceps points, limit impact of sleeveFactor to 25%
|
|
|
|
let halfWidth =
|
|
|
|
(measurements.bicepsCircumference * (1 + options.bicepsEase)) / 2;
|
2018-08-09 11:44:24 +02:00
|
|
|
points.bicepsLeft = points.centerBiceps.shift(
|
2018-08-07 16:42:48 +02:00
|
|
|
180,
|
2018-08-08 16:12:56 +02:00
|
|
|
halfWidth * options.sleeveWidthGuarantee +
|
|
|
|
halfWidth * (1 - options.sleeveWidthGuarantee) * store.get("sleeveFactor")
|
2018-08-07 16:42:48 +02:00
|
|
|
);
|
2018-08-09 11:44:24 +02:00
|
|
|
points.bicepsRight = points.bicepsLeft.flipX(points.centerBiceps);
|
2018-08-07 16:42:48 +02:00
|
|
|
|
|
|
|
// Pitch points
|
2018-08-09 11:44:24 +02:00
|
|
|
let width = points.bicepsRight.x;
|
2018-08-08 14:56:30 +02:00
|
|
|
let height = points.centerCap.y;
|
2018-08-07 16:42:48 +02:00
|
|
|
points.backPitch = new Point(
|
2018-08-08 14:56:30 +02:00
|
|
|
-1 * width * options.sleevecapBackFactorX,
|
|
|
|
height * options.sleevecapBackFactorY
|
2018-08-07 16:42:48 +02:00
|
|
|
);
|
|
|
|
points.frontPitch = new Point(
|
2018-08-08 14:56:30 +02:00
|
|
|
width * options.sleevecapFrontFactorX,
|
|
|
|
height * options.sleevecapFrontFactorY
|
2018-08-07 16:42:48 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
// 4 sleevecap quadrants
|
|
|
|
// Base points
|
|
|
|
points.capQ1Base = points.frontPitch.shiftFractionTowards(
|
2018-08-09 11:44:24 +02:00
|
|
|
points.bicepsRight,
|
2018-08-07 16:42:48 +02:00
|
|
|
0.5
|
|
|
|
);
|
|
|
|
points.capQ2Base = points.frontPitch.shiftFractionTowards(
|
|
|
|
points.centerCap,
|
|
|
|
0.5
|
|
|
|
);
|
|
|
|
points.capQ3Base = points.backPitch.shiftFractionTowards(
|
|
|
|
points.centerCap,
|
|
|
|
0.5
|
|
|
|
);
|
|
|
|
points.capQ4Base = points.backPitch.shiftFractionTowards(
|
2018-08-09 11:44:24 +02:00
|
|
|
points.bicepsLeft,
|
2018-08-07 16:42:48 +02:00
|
|
|
0.5
|
|
|
|
);
|
|
|
|
// Offset points
|
|
|
|
let baseOffset = measurements.bicepsCircumference * (1 + options.bicepsEase);
|
|
|
|
points.capQ1 = points.capQ1Base.shift(
|
2018-08-09 11:44:24 +02:00
|
|
|
points.bicepsRight.angle(points.frontPitch) + 90,
|
2018-08-07 16:42:48 +02:00
|
|
|
baseOffset * options.sleevecapQ1Offset
|
|
|
|
);
|
|
|
|
points.capQ2 = points.capQ2Base.shift(
|
|
|
|
points.centerCap.angle(points.frontPitch) + 90,
|
|
|
|
baseOffset * options.sleevecapQ2Offset
|
|
|
|
);
|
|
|
|
points.capQ3 = points.capQ3Base.shift(
|
|
|
|
points.centerCap.angle(points.backPitch) - 90,
|
|
|
|
baseOffset * options.sleevecapQ3Offset
|
|
|
|
);
|
|
|
|
points.capQ4 = points.capQ4Base.shift(
|
2018-08-09 11:44:24 +02:00
|
|
|
points.bicepsLeft.angle(points.backPitch) - 90,
|
2018-08-07 16:42:48 +02:00
|
|
|
baseOffset * options.sleevecapQ4Offset
|
|
|
|
);
|
|
|
|
// Control points
|
|
|
|
points.capQ1Cp1 = points.capQ1.shift(
|
2018-08-09 11:44:24 +02:00
|
|
|
points.frontPitch.angle(points.bicepsRight),
|
2018-08-07 16:42:48 +02:00
|
|
|
baseOffset * options.sleevecapQ1Spread1
|
|
|
|
);
|
|
|
|
points.capQ1Cp2 = points.capQ1.shift(
|
2018-08-09 11:44:24 +02:00
|
|
|
points.frontPitch.angle(points.bicepsRight),
|
2018-08-07 16:42:48 +02:00
|
|
|
baseOffset * options.sleevecapQ1Spread2 * -1
|
|
|
|
);
|
|
|
|
points.capQ2Cp1 = points.capQ2.shift(
|
|
|
|
points.centerCap.angle(points.frontPitch),
|
|
|
|
baseOffset * options.sleevecapQ2Spread1
|
|
|
|
);
|
|
|
|
points.capQ2Cp2 = points.capQ2.shift(
|
|
|
|
points.centerCap.angle(points.frontPitch),
|
|
|
|
baseOffset * options.sleevecapQ2Spread2 * -1
|
|
|
|
);
|
|
|
|
points.capQ3Cp1 = points.capQ3.shift(
|
|
|
|
points.backPitch.angle(points.centerCap),
|
|
|
|
baseOffset * options.sleevecapQ3Spread1
|
|
|
|
);
|
|
|
|
points.capQ3Cp2 = points.capQ3.shift(
|
|
|
|
points.backPitch.angle(points.centerCap),
|
|
|
|
baseOffset * options.sleevecapQ3Spread2 * -1
|
|
|
|
);
|
|
|
|
points.capQ4Cp1 = points.capQ4.shift(
|
2018-08-09 11:44:24 +02:00
|
|
|
points.bicepsLeft.angle(points.backPitch),
|
2018-08-07 16:42:48 +02:00
|
|
|
baseOffset * options.sleevecapQ4Spread1
|
|
|
|
);
|
|
|
|
points.capQ4Cp2 = points.capQ4.shift(
|
2018-08-09 11:44:24 +02:00
|
|
|
points.bicepsLeft.angle(points.backPitch),
|
2018-08-07 16:42:48 +02:00
|
|
|
baseOffset * options.sleevecapQ4Spread2 * -1
|
|
|
|
);
|
|
|
|
|
2018-08-08 16:12:56 +02:00
|
|
|
// Sleevecap seamline
|
|
|
|
paths.sleevecap = new Path()
|
2018-08-09 11:44:24 +02:00
|
|
|
.move(points.bicepsRight)
|
|
|
|
.curve(points.bicepsRight, points.capQ1Cp1, points.capQ1)
|
2018-08-07 16:42:48 +02:00
|
|
|
.curve(points.capQ1Cp2, points.capQ2Cp1, points.capQ2)
|
|
|
|
.curve(points.capQ2Cp2, points.capQ3Cp1, points.capQ3)
|
|
|
|
.curve(points.capQ3Cp2, points.capQ4Cp1, points.capQ4)
|
2018-08-09 11:44:24 +02:00
|
|
|
.curve(points.capQ4Cp2, points.bicepsLeft, points.bicepsLeft);
|
2018-08-07 16:42:48 +02:00
|
|
|
|
|
|
|
// Store sleevecap length
|
2018-08-08 16:12:56 +02:00
|
|
|
store.set("sleevecapLength", paths.sleevecap.length());
|
2018-08-08 14:38:19 +02:00
|
|
|
if (run === 1) {
|
|
|
|
let armholeLength =
|
|
|
|
store.get("frontArmholeLength") + store.get("backArmholeLength");
|
|
|
|
let sleevecapEase = armholeLength * options.sleevecapEase;
|
|
|
|
store.set("sleevecapEase", sleevecapEase);
|
|
|
|
store.set("sleevecapTarget", armholeLength + sleevecapEase);
|
|
|
|
debug("Sleevecap ease is", units(sleevecapEase));
|
2018-08-08 16:12:56 +02:00
|
|
|
|
|
|
|
// Uncomment this line to see all sleevecap iterations
|
|
|
|
//paths[run] = paths.sleevecap;
|
2018-08-08 14:38:19 +02:00
|
|
|
}
|
2018-08-07 16:42:48 +02:00
|
|
|
}
|
|
|
|
|
2018-08-06 16:19:12 +02:00
|
|
|
var sleeve = {
|
2018-08-11 14:13:40 +02:00
|
|
|
draft: function(part) {
|
2018-08-06 16:19:12 +02:00
|
|
|
// prettier-ignore
|
2018-08-08 14:38:19 +02:00
|
|
|
let {debug, store, units, sa, measurements, options, Point, points, Path, paths, Snippet, snippets, final, paperless, macro} = part.shorthand();
|
2018-08-06 16:19:12 +02:00
|
|
|
|
|
|
|
store.set("sleeveFactor", 1);
|
2018-08-07 16:42:48 +02:00
|
|
|
let run = 1;
|
|
|
|
do {
|
2018-08-08 14:38:19 +02:00
|
|
|
draftSleevecap(part, run);
|
|
|
|
debug(
|
|
|
|
`Sleevecap draft ${run}, sleevecap delta is ${units(
|
|
|
|
sleevecapDelta(store)
|
|
|
|
)}`
|
2018-08-07 16:42:48 +02:00
|
|
|
);
|
|
|
|
sleevecapAdjust(store);
|
|
|
|
run++;
|
2018-08-08 16:12:56 +02:00
|
|
|
} while (Math.abs(sleevecapDelta(store)) > 2 && run < 100);
|
|
|
|
|
|
|
|
// Wrist
|
|
|
|
let top = paths.sleevecap.bbox().topLeft.y;
|
|
|
|
debug("Sleevecap height is ", units(Math.abs(top)));
|
2018-08-09 11:44:24 +02:00
|
|
|
debug("Sleeve width is ", units(points.bicepsRight.x * 2));
|
2018-08-08 16:12:56 +02:00
|
|
|
points.centerWrist = new Point(
|
|
|
|
0,
|
|
|
|
top + measurements.shoulderToWrist * (1 + options.sleeveLengthBonus)
|
|
|
|
);
|
|
|
|
points.wristRight = points.centerWrist.shift(
|
|
|
|
0,
|
|
|
|
(measurements.wristCircumference * (1 + options.cuffEase)) / 2
|
|
|
|
);
|
|
|
|
points.wristLeft = points.wristRight.rotate(180, points.centerWrist);
|
|
|
|
|
2018-08-08 16:41:16 +02:00
|
|
|
// Paths
|
|
|
|
paths.sleevecap.render = false;
|
2018-08-08 16:12:56 +02:00
|
|
|
paths.seam = new Path()
|
2018-08-09 11:44:24 +02:00
|
|
|
.move(points.bicepsLeft)
|
2018-08-08 16:12:56 +02:00
|
|
|
.move(points.wristLeft)
|
|
|
|
.move(points.wristRight)
|
2018-08-09 11:44:24 +02:00
|
|
|
.line(points.bicepsRight)
|
2018-08-08 16:12:56 +02:00
|
|
|
.join(paths.sleevecap)
|
2018-08-09 11:44:24 +02:00
|
|
|
.close()
|
|
|
|
.attr("class", "fabric");
|
2018-08-06 16:19:12 +02:00
|
|
|
|
|
|
|
// Anchor point for sampling
|
|
|
|
points.gridAnchor = points.origin;
|
|
|
|
points.test = new Point(10, 10);
|
|
|
|
|
|
|
|
// Final?
|
|
|
|
if (final) {
|
2018-08-08 16:41:16 +02:00
|
|
|
points.logo = points.centerBiceps.shiftFractionTowards(
|
2018-08-07 16:42:48 +02:00
|
|
|
points.centerWrist,
|
|
|
|
0.3
|
|
|
|
);
|
2018-08-08 16:41:16 +02:00
|
|
|
snippets.logo = new Snippet("logo", points.logo);
|
|
|
|
macro("title", { at: points.centerBiceps, nr: 3, title: "sleeve" });
|
2018-08-10 18:51:44 +02:00
|
|
|
macro("grainline", { from: points.centerWrist, to: points.centerBiceps });
|
2018-08-08 16:41:16 +02:00
|
|
|
|
|
|
|
points.sleeveTip = paths.sleevecap.shiftFractionAlong(0.5);
|
|
|
|
points.frontNotch = paths.sleevecap.shiftAlong(
|
|
|
|
paths.sleevecap.length() / 2 -
|
|
|
|
store.get("frontShoulderToArmholePitch") -
|
|
|
|
store.get("sleevecapEase") / 2
|
|
|
|
);
|
|
|
|
points.backNotch = paths.sleevecap.shiftAlong(
|
|
|
|
paths.sleevecap.length() / 2 +
|
|
|
|
store.get("backShoulderToArmholePitch") +
|
|
|
|
store.get("sleevecapEase") / 2
|
|
|
|
);
|
|
|
|
snippets.frontNotch = new Snippet("notch", points.frontNotch);
|
|
|
|
snippets.backNotch = new Snippet("bnotch", points.backNotch);
|
|
|
|
if (sa) paths.sa = paths.seam.offset(sa).attr("class", "fabric sa");
|
2018-08-06 16:19:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Paperless?
|
|
|
|
if (paperless) {
|
2018-08-09 11:44:24 +02:00
|
|
|
macro("vd", {
|
|
|
|
from: points.wristLeft,
|
|
|
|
to: points.bicepsLeft,
|
|
|
|
x: points.bicepsLeft.x - sa - 15
|
|
|
|
});
|
|
|
|
macro("vd", {
|
|
|
|
from: points.wristLeft,
|
|
|
|
to: points.sleeveTip,
|
|
|
|
x: points.bicepsLeft.x - sa - 30
|
|
|
|
});
|
|
|
|
macro("hd", {
|
|
|
|
from: points.bicepsLeft,
|
|
|
|
to: points.bicepsRight,
|
|
|
|
y: points.sleeveTip.y - sa - 30
|
|
|
|
});
|
|
|
|
macro("pd", {
|
|
|
|
path: paths.sleevecap.reverse(),
|
|
|
|
d: -1 * sa - 15
|
|
|
|
});
|
2018-08-06 16:19:12 +02:00
|
|
|
}
|
|
|
|
return part;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
export default sleeve;
|