diff --git a/packages/charlie/config/index.js b/packages/charlie/config/index.js index b8a31cb9422..3a8835f7562 100644 --- a/packages/charlie/config/index.js +++ b/packages/charlie/config/index.js @@ -10,7 +10,7 @@ export default { difficulty: 3, tags: ['bottom', 'basics', 'trousers'], optionGroups: { - fit: ['seatEase', 'kneeEase', 'waistEase'], + fit: ['seatEase', 'kneeEase', 'waistEase', 'waistbandCurve'], style: ['waistHeight', 'waistbandWidth', 'lengthBonus', 'crotchDrop'], pockets: [ { @@ -78,7 +78,8 @@ export default { // The inheritance makes this a bit messy titanFront: 'titanBack', back: ['titanBack', 'titanFront', 'front'], - waistband: ['titanBack', 'titanFront', 'front', 'back'] + waistband: ['titanBack', 'titanFront', 'front', 'back'], + waistbandCurved: ['titanBack', 'titanFront', 'front', 'back'] }, options: { // Constants (from Titan) @@ -93,6 +94,9 @@ export default { waistbandReduction: 0.25, // See src/index.js waistbandFactor: 0.1, + // Fit (Charlie) + waistbandCurve: { pct: 0, min: 0, max: 35 }, + // Fit (from Titan) waistEase: { pct: 1, min: 0, max: 5 }, seatEase: { pct: 5, min: 0, max: 10 }, diff --git a/packages/charlie/src/index.js b/packages/charlie/src/index.js index 02d02235c06..fe8436de2c9 100644 --- a/packages/charlie/src/index.js +++ b/packages/charlie/src/index.js @@ -9,6 +9,7 @@ import config from '../config' import draftBack from './back' import draftFront from './front' import draftWaistband from './waistband' +import draftWaistbandCurved from './waistband-curved' import draftFrontPocket from './front-pocket' import draftFrontPocketFacing from './front-pocket-facing' import draftBackPocket from './back-pocket' @@ -33,6 +34,7 @@ for (let p of ['Front', 'Back']) { Pattern.prototype.draftBack = (part) => draftBack(part) Pattern.prototype.draftFront = (part) => draftFront(part) Pattern.prototype.draftWaistband = (part) => draftWaistband(part) +Pattern.prototype.draftWaistbandCurved = (part) => draftWaistbandCurved(part) Pattern.prototype.draftFrontPocket = (part) => draftFrontPocket(part) Pattern.prototype.draftFrontPocketFacing = (part) => draftFrontPocketFacing(part) Pattern.prototype.draftBackPocket = (part) => draftBackPocket(part) diff --git a/packages/charlie/src/waistband-curved.js b/packages/charlie/src/waistband-curved.js new file mode 100644 index 00000000000..b4a43755926 --- /dev/null +++ b/packages/charlie/src/waistband-curved.js @@ -0,0 +1,271 @@ +export default (part) => { + // Shorthand + let { + points, + Point, + paths, + Path, + options, + complete, + paperless, + store, + macro, + raise, + snippets, + Snippet, + sa, + units + } = part.shorthand() + + if (options.waistbandCurve == 0) { + return part; + } + + const fullWaist = 2 * (store.get('waistbandBack') + store.get('waistbandFront')); + const sideSeamFraction = 0.5 * store.get('waistbandFront') / (store.get('waistbandBack') + store.get('waistbandFront')) + const radius = fullWaist / (2 * Math.PI * options.waistbandCurve); + const angle = 360 * options.waistbandCurve; + + points.center = new Point(0, 0); + + points.cfLeftBottom = points.center.shift(0, radius); + points.cbBottom = points.cfLeftBottom.rotate(0.5 * angle, points.center); + points.cfRightBottom = points.cfLeftBottom.rotate(angle, points.center); + + points.cfLeftTop = points.cfLeftBottom.shiftTowards(points.center, options.waistbandWidth); + points.cbTop = points.cfLeftTop.rotate(0.5 * angle, points.center); + points.cfRightTop = points.cfRightBottom.shiftTowards(points.center, options.waistbandWidth); + + // Calculate control points for circle arc + // https://math.stackexchange.com/questions/873224/calculate-control-points-of-cubic-bezier-curve-approximating-a-part-of-a-circle + const a = (4 / 3) * Math.tan(2 * Math.PI * options.waistbandCurve / 4); + + points.cfLeftBottomCp = points.cfLeftBottom.shift(90, a * radius); + points.cfRightBottomCp = points.cfRightBottom.shift(angle - 90, a * radius); + + points.cfLeftTopCp = points.cfLeftTop.shift(90, a * (radius - options.waistbandWidth)) + points.cfRightTopCp = points.cfRightTop.shift(angle - 90, a * (radius - options.waistbandWidth)) + + // Add fly underlap + points.edgeRightTop = points.cfRightTop.shiftTowards(points.cfRightTopCp, - store.get('waistbandFly')) + points.edgeRightBottom = points.cfRightBottom.shiftTowards(points.cfRightBottomCp, - store.get('waistbandFly')) + + paths.waistbandTop = new Path() + .move(points.cfRightTop) + .curve(points.cfRightTopCp, points.cfLeftTopCp, points.cfLeftTop) + .setRender(false) + + paths.waistbandBottom = new Path() + .move(points.cfRightBottom) + .curve(points.cfRightBottomCp, points.cfLeftBottomCp, points.cfLeftBottom) + + points.ssRightBottom = paths.waistbandBottom.shiftFractionAlong(sideSeamFraction); + points.ssLeftBottom = paths.waistbandBottom.shiftFractionAlong(1 - sideSeamFraction); + + points.ssRightTop = paths.waistbandTop.shiftFractionAlong(sideSeamFraction); + points.ssLeftTop = paths.waistbandTop.shiftFractionAlong(1 - sideSeamFraction); + + + paths.saBase = new Path() + .move(points.cfLeftBottom) + .curve(points.cfLeftBottomCp, points.cfRightBottomCp, points.cfRightBottom) + .line(points.edgeRightBottom) + .line(points.edgeRightTop) + .line(points.cfRightTop) + .curve(points.cfRightTopCp, points.cfLeftTopCp, points.cfLeftTop) + .line(points.cfLeftBottom) + .close() + .setRender(false) + paths.seam = paths.saBase.clone().attr('class', 'fabric').setRender(true) + + if (complete) { + raise.info( + `Top of waistband: ${units(paths.waistbandTop.length())}` + ) + macro('sprinkle', { + snippet: 'notch', + on: ['cfRightBottom', 'cfRightTop', 'cbBottom', 'cbTop', 'ssLeftBottom', 'ssRightBottom', 'ssLeftTop', 'ssRightTop'] + }) + + points.titleAnchor = points.cfLeftBottom.shiftFractionTowards(points.ssLeftTop, 0.4) + macro('title', { + at: points.titleAnchor, + nr: 11, + title: 'waistband', + rotation: -90 + }) + + macro('grainline', { + from: points.cbTop, + to: points.cbBottom + }) + + paths.cf = new Path() + .move(points.cfRightTop) + .line(points.cfRightBottom) + .attr('class', 'dashed') + .attr('data-text', 'centerFront') + .attr('data-text-class', 'center') + paths.rs = new Path() + .move(points.ssRightTop) + .line(points.ssRightBottom) + .attr('class', 'dashed') + .attr('data-text', 'rightSide') + .attr('data-text-class', 'center') + paths.ls = new Path() + .move(points.ssLeftTop) + .line(points.ssLeftBottom) + .attr('class', 'dashed') + .attr('data-text', 'leftSide') + .attr('data-text-class', 'center') + + let buttonScale = options.waistbandWidth / 14 + points.button = points.edgeRightBottom.shiftFractionTowards(points.cfRightTop, 0.6) + snippets.button = new Snippet('button', points.button).attr('data-scale', buttonScale) + points.buttonhole = new Point(points.cfLeftTop.x + 0.4 * options.waistbandWidth, points.cfLeftTop.y - store.get('waistbandFly') * 0.4) + snippets.buttonhole = new Snippet('buttonhole-start', points.buttonhole).attr( + 'data-scale', + buttonScale + ) + if (sa) { + paths.sa = paths.saBase + .offset(sa) + .close() + .attr('class', 'fabric sa') + } + + if (paperless) { + // Lower waistband measurements + macro('hd', { + from: points.edgeRightBottom, + to: points.cfLeftBottom, + y: points.edgeRightBottom.y - sa - 30 + }) + macro('vd', { + from: points.edgeRightBottom, + to: points.cfLeftBottom, + x: points.cfLeftBottom.x + sa + 30 + }) + + macro('hd', { + from: points.edgeRightBottom, + to: points.ssRightBottom, + y: points.edgeRightBottom.y - sa - 15 + }) + macro('vd', { + from: points.edgeRightBottom, + to: points.ssRightBottom, + x: points.ssRightBottom.x + sa + 15 + }) + + macro('hd', { + from: points.ssRightBottom, + to: points.cbBottom, + y: points.ssRightBottom.y - sa - 15 + }) + macro('vd', { + from: points.ssRightBottom, + to: points.cbBottom, + x: points.cbBottom.x + sa + 15 + }) + + macro('hd', { + from: points.cbBottom, + to: points.ssLeftBottom, + y: points.cbBottom.y - sa - 15 + }) + macro('vd', { + from: points.cbBottom, + to: points.ssLeftBottom, + x: points.ssLeftBottom.x + sa + 15 + }) + + macro('hd', { + from: points.ssLeftBottom, + to: points.cfLeftBottom, + y: points.ssLeftBottom.y - sa - 15 + }) + macro('vd', { + from: points.ssLeftBottom, + to: points.cfLeftBottom, + x: points.cfLeftBottom.x + sa + 15 + }) + + macro('hd', { + from: points.cfLeftTop, + to: points.cfLeftBottom, + y: points.cfLeftTop.y + sa + 15 + }) + + macro('hd', { + from: points.edgeRightBottom, + to: points.edgeRightTop, + y: points.edgeRightBottom.y - sa - 15 + }) + macro('vd', { + from: points.edgeRightBottom, + to: points.edgeRightTop, + x: points.edgeRightTop.x - sa - 15 + }) + + // TOP OF WAISTBAND + macro('hd', { + from: points.edgeRightTop, + to: points.cfLeftTop, + y: points.edgeRightTop.y + sa + 30 + }) + macro('vd', { + from: points.edgeRightTop, + to: points.cfLeftTop, + x: points.edgeRightTop.x - sa - 30 + }) + + macro('hd', { + from: points.edgeRightTop, + to: points.ssRightTop, + y: points.edgeRightTop.y + sa + 15 + }) + macro('vd', { + from: points.edgeRightTop, + to: points.ssRightTop, + x: points.edgeRightTop.x - sa - 15 + }) + + macro('hd', { + from: points.ssRightTop, + to: points.cbTop, + y: points.ssRightTop.y + sa + 15 + }) + macro('vd', { + from: points.ssRightTop, + to: points.cbTop, + x: points.ssRightBottom.x - sa - 15 + }) + + macro('hd', { + from: points.cbTop, + to: points.ssLeftTop, + y: points.cbTop.y + sa + 15 + }) + macro('vd', { + from: points.cbTop, + to: points.ssLeftTop, + x: points.cbTop.x - sa - 15 + }) + + macro('hd', { + from: points.ssLeftTop, + to: points.cfLeftTop, + y: points.ssLeftTop.y + sa + 15 + }) + macro('vd', { + from: points.ssLeftTop, + to: points.cfLeftTop, + x: points.ssLeftTop.x - sa - 15 + }) + + } + } + + return part +} diff --git a/packages/charlie/src/waistband.js b/packages/charlie/src/waistband.js index 7ba8436284f..b1e1af934c5 100644 --- a/packages/charlie/src/waistband.js +++ b/packages/charlie/src/waistband.js @@ -15,6 +15,10 @@ export default (part) => { sa } = part.shorthand() + if (options.waistbandCurve > 0) { + return part; + } + points.topLeft = new Point(0, 0) points.top = new Point(options.waistbandWidth, 0) points.topRight = new Point(points.top.x * 2, 0) diff --git a/packages/i18n/src/locales/en/options/charlie.yml b/packages/i18n/src/locales/en/options/charlie.yml index ece6ca02cff..cb7a5839f07 100644 --- a/packages/i18n/src/locales/en/options/charlie.yml +++ b/packages/i18n/src/locales/en/options/charlie.yml @@ -43,3 +43,6 @@ flyLength: flyWidth: title: Fly width description: Controls how far the J-seam of offset from the fly edge +waistbandCurve: + title: Waistband Curve + description: Controls how curved the waistband is. \ No newline at end of file