diff --git a/config/dependencies.yaml b/config/dependencies.yaml index 8339ebe95a2..30c4486a416 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -55,6 +55,9 @@ bent: breanna: peer: '@freesewing/brian': *freesewing +brian: + peer: + '@freesewing/plugin-mirror': *freesewing carlita: peer: '@freesewing/brian': *freesewing diff --git a/packages/brian/config/index.js b/packages/brian/config/index.js index 63ede072a46..72e73f4ed97 100644 --- a/packages/brian/config/index.js +++ b/packages/brian/config/index.js @@ -18,6 +18,7 @@ export default { 'lengthBonus', 'sleeveLengthBonus', ], + style: [ 's3Collar', 's3Armhole' ], advanced: [ 'acrossBackFactor', 'armholeDepthFactor', @@ -91,6 +92,9 @@ export default { lengthBonus: { pct: 0, min: -4, max: 60 }, shoulderEase: { pct: 0, min: -2, max: 6 }, shoulderSlopeReduction: { pct: 0, min: 0, max: 80 }, + // s3 is short for Shoulder Seam Shift + s3Collar: { pct: 0, min: -100, max: 100 }, + s3Armhole: { pct: 0, min: -100, max: 100 }, sleevecapEase: { pct: 1, min: 0, max: 10 }, sleevecapTopFactorX: { pct: 50, min: 25, max: 75 }, sleevecapTopFactorY: { pct: 100, min: 35, max: 165 }, diff --git a/packages/brian/package.json b/packages/brian/package.json index b1081bb2652..0be3035d555 100644 --- a/packages/brian/package.json +++ b/packages/brian/package.json @@ -35,7 +35,8 @@ }, "peerDependencies": { "@freesewing/core": "^2.16.2", - "@freesewing/plugin-bundle": "^2.16.2" + "@freesewing/plugin-bundle": "^2.16.2", + "@freesewing/plugin-mirror": "^2.16.2" }, "dependencies": {}, "devDependencies": { diff --git a/packages/brian/src/back.js b/packages/brian/src/back.js index e0cf6c7ba36..eb938ba6eac 100644 --- a/packages/brian/src/back.js +++ b/packages/brian/src/back.js @@ -12,13 +12,108 @@ export default (part) => { complete, paperless, macro, + options, + Point, + utils } = part.shorthand() points.anchor = points.hps.clone() + // Adapt the shoulder seam according to the relevant options + // Note: s3 stands for Shoulder Seam Shift + if (options.s3Collar === 0) { + points.s3CollarSplit = points.hps + paths.backCollar = new Path() + .move(points.hps) + .curve_(points.neckCp2, points.cbNeck) + .setRender(false) + } + else if (options.s3Collar > 0) { + // Shift shoulder seam forward on the collar side + points.s3CollarSplit = utils.curveIntersectsY( + points.hps, + points.mirroredNeckCp2Front, + points.mirroredCfNeckCp1, + points.mirroredCfNeck, + store.get('s3CollarMaxFront') * -1 * options.s3Collar + ) + paths.backCollar = new Path() + .move(points.hps) + ._curve(points.mirroredNeckCp2Front, points.mirroredCfNeckCp1, points.mirroredCfNeck) + .split(points.s3CollarSplit)[0] + .reverse() + .join(new Path() + .move(points.hps) + .curve_(points.neckCp2, points.cbNeck) + ) + .setRender(false) + } + else if (options.s3Collar < 0) { + // Shift shoulder seam backward on the collar side + points.s3CollarSplit = utils.curveIntersectsY( + points.hps, points.neckCp2, points.cbNeck, points.cbNeck, + store.get('s3CollarMaxBack') * -1 * options.s3Collar + ) + paths.backCollar = new Path() + .move(points.cbNeck) + ._curve(points.neckCp2, points.neck) + .split(points.s3CollarSplit)[0] + .reverse() + .setRender(false) + } + if (options.s3Armhole === 0) { + points.s3ArmholeSplit = points.shoulder + paths.backArmhole = new Path() + .move(points.armholePitch) + .curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder) + .setRender(false) + } + else if (options.s3Armhole > 0) { + // Shift shoulder seam forward on the armhole side + points.s3ArmholeSplit = utils.curveIntersectsY( + points.shoulder, + points.mirroredShoulderCp1, + points.mirroredFrontArmholePitchCp2, + points.mirroredFrontArmholePitch, + store.get('s3ArmholeMax') * -1 * options.s3Armhole + points.shoulder.y + ) + paths.backArmhole = new Path() + .move(points.armholePitch) + .curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder) + .join(new Path() + .move(points.shoulder) + .curve(points.mirroredShoulderCp1, points.mirroredFrontArmholePitchCp2, points.mirroredFrontArmholePitch) + .split(points.s3ArmholeSplit)[0] + ) + .setRender(false) + } + else if (options.s3Armhole < 0) { + // Shift shoulder seam backward on the armhole side + points.s3ArmholeSplit = utils.curveIntersectsY( + points.shoulder, + points.shoulderCp1, + points.armholePitchCp2, + points.armholePitch, + store.get('s3ArmholeMax') * -1 * options.s3Armhole + points.shoulder.y + ) + paths.backArmhole = new Path() + .move(points.armholePitch) + .curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder) + .split(points.s3ArmholeSplit)[0] + .setRender(false) + } + // Seamline - paths.saBase = shared.saBase('back', points, Path) - paths.saBase.render = false + paths.saBase = new Path() + .move(points.cbHem) + .line(points.hem) + .line(points.armhole) + .curve(points.armholeCp2, points.armholeHollowCp1, points.armholeHollow) + .curve(points.armholeHollowCp2, points.armholePitchCp1, points.armholePitch) + .join(paths.backArmhole) + .line(points.s3CollarSplit) + .join(paths.backCollar) + .setRender(false) paths.seam = new Path() .move(points.cbNeck) .line(points.cbHips) @@ -48,11 +143,14 @@ export default (part) => { .move(points.cbHips) paths.sa.line(paths.sa.start()) } + + // Add notches if the shoulder seam is shifted + shared.s3Notches(part, 'bnotch') } // Paperless? if (paperless) { - shared.dimensions(macro, points, Path, sa) + shared.dimensions(part, 'back') macro('hd', { from: points.cbHips, to: points.hips, @@ -70,13 +168,13 @@ export default (part) => { }) macro('hd', { from: points.cbNeck, - to: points.neck, - y: points.neck.y - sa - 15, + to: points.s3CollarSplit, + y: points.s3CollarSplit.y - sa - 15, }) macro('hd', { from: points.cbNeck, - to: points.shoulder, - y: points.neck.y - sa - 30, + to: points.s3ArmholeSplit, + y: points.s3CollarSplit.y - sa - 30, }) } diff --git a/packages/brian/src/base.js b/packages/brian/src/base.js index bf4fda36182..94f2bbb38c7 100644 --- a/packages/brian/src/base.js +++ b/packages/brian/src/base.js @@ -13,6 +13,7 @@ export default (part) => { paths, utils, complete, + macro } = part.shorthand() store.set('shoulderEase', (measurements.shoulderToShoulder * options.shoulderEase) / 2) @@ -121,13 +122,38 @@ export default (part) => { // Anchor point for sampling points.gridAnchor = points.cbHem - // Seamline - paths.saBase = shared.saBase('back', points, Path) - paths.seam = new Path() - .move(points.cbNeck) - .line(points.cbHem) - .join(paths.saBase) - .attr('class', 'fabric') + /* + * People would like to have the option to shift the shoulder seam + * See https://github.com/freesewing/freesewing/issues/642 + * So let's make the people happy + */ + // Front armhole is a bit deeper, add those points + let deeper = measurements.chest * options.frontArmholeDeeper + for (const p of ['','Cp1','Cp2']) { + points[`frontArmholePitch${p}`] = points[`armholePitch${p}`].shift(180, deeper) + } + // Add points needed for the mirrored front&back neck/armhole path + macro('mirror', { + mirror: [points.hps, points.shoulder], + points: [ + points.neckCp2Front, + points.cfNeckCp1, + points.cfNeck, + points.cbNeck, + points.neckCp2, + points.frontArmholePitch, + points.frontArmholePitchCp2, + points.shoulderCp1, + ], + clone: true + }) + + // How much space do we have to work with here? + // s3 = ShoulderSeamShift + store.set('s3CollarMaxFront', points.hps.dy(points.cfNeck)/2) + store.set('s3CollarMaxBack', points.hps.dy(points.cbNeck)/2) + store.set('s3ArmholeMax', points.shoulder.dy(points.frontArmholePitch)/4) + // Let's leave the actual splitting the curves for the front/back parts // Complete pattern? if (complete) { diff --git a/packages/brian/src/front.js b/packages/brian/src/front.js index 4aa2b400db4..df99d44e05e 100644 --- a/packages/brian/src/front.js +++ b/packages/brian/src/front.js @@ -15,13 +15,91 @@ export default (part) => { complete, paperless, macro, + utils } = part.shorthand() - // Cut arm a bit deeper at the front + // Re-use points for deeper armhole at the front let deeper = measurements.chest * options.frontArmholeDeeper - points.armholePitchCp1.x -= deeper - points.armholePitch.x -= deeper - points.armholePitchCp2.x -= deeper + points.armholePitchCp1 = points.frontArmholePitchCp1 + points.armholePitch = points.frontArmholePitch + points.armholePitchCp2 = points.frontArmholePitchCp2 + + // Adapt the shoulder line according to the relevant options + if (options.s3Collar === 0) { + paths.frontCollar = new Path() + .move(points.hps) + .curve(points.neckCp2Front, points.cfNeckCp1, points.cfNeck) + .setRender(false) + } + else if (options.s3Collar > 0) { + // Shift shoulder seam forward on the collar side + points.s3CollarSplit = utils.curveIntersectsY( + points.hps, points.neckCp2Front, points.cfNeckCp1, points.cfNeck, + store.get('s3CollarMaxFront') * options.s3Collar + ) + paths.frontCollar = new Path() + .move(points.hps) + .curve(points.neckCp2Front, points.cfNeckCp1, points.cfNeck) + .split(points.s3CollarSplit)[1] + .setRender(false) + } + else if (options.s3Collar < 0) { + // Shift shoulder seam backward on the collar side + points.s3CollarSplit = utils.curveIntersectsY( + points.mirroredCbNeck, points.mirroredCbNeck, points.mirroredNeckCp2, points.hps, + store.get('s3CollarMaxBack') * options.s3Collar + ) + paths.frontCollar = new Path() + .move(points.hps) + .curve_(points.mirroredNeckCp2, points.mirroredCbNeck) + .split(points.s3CollarSplit)[0] + .reverse() + .join(new Path() + .move(points.hps) + .curve(points.neckCp2Front, points.cfNeckCp1, points.cfNeck) + ) + .setRender(false) + } + if (options.s3Armhole === 0) { + paths.frontArmhole = new Path() + .move(points.armholePitch) + .curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder) + .setRender(false) + } + else if (options.s3Armhole > 0) { + // Shift shoulder seam forward on the armhole side + points.s3ArmholeSplit = utils.curveIntersectsY( + points.shoulder, + points.shoulderCp1, + points.armholePitchCp2, + points.armholePitch, + store.get('s3ArmholeMax') * options.s3Armhole + points.shoulder.y + ) + paths.frontArmhole = new Path() + .move(points.armholePitch) + .curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder) + .split(points.s3ArmholeSplit)[0] + .setRender(false) + } + else if (options.s3Armhole < 0) { + // Shift shoulder seam forward on the armhole side + points.s3ArmholeSplit = utils.curveIntersectsY( + points.shoulder, + points.mirroredShoulderCp1, + points.mirroredFrontArmholePitchCp2, + points.mirroredFrontArmholePitch, + store.get('s3ArmholeMax') * options.s3Armhole + points.shoulder.y + ) + paths.frontArmhole = new Path() + .move(points.armholePitch) + .curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder) + .join(new Path() + .move(points.shoulder) + .curve(points.mirroredShoulderCp1, points.mirroredFrontArmholePitchCp2, points.mirroredFrontArmholePitch) + .split(points.s3ArmholeSplit)[0] + ) + .setRender(false) + } // Rename cb (center back) to cf (center front) for (let key of ['Shoulder', 'Armhole', 'Waist', 'Hips', 'Hem']) { @@ -32,7 +110,16 @@ export default (part) => { points.neckCp2 = new Point(points.neckCp2Front.x, points.neckCp2Front.y) // Seamline - paths.saBase = shared.saBase('front', points, Path) + paths.saBase = new Path() + .move(points.cfHem) + .line(points.hem) + .line(points.armhole) + .curve(points.armholeCp2, points.armholeHollowCp1, points.armholeHollow) + .curve(points.armholeHollowCp2, points.armholePitchCp1, points.armholePitch) + .join(paths.frontArmhole) + .line(points.s3CollarSplit) + .join(paths.frontCollar) + paths.saBase.render = false paths.seam = new Path() .move(points.cfNeck) @@ -62,11 +149,14 @@ export default (part) => { .move(points.cfHips) paths.sa.line(paths.sa.start()) } + + // Add notches if the shoulder seam is shifted + shared.s3Notches(part, 'notch') } // Paperless? if (paperless) { - shared.dimensions(macro, points, Path, sa) + shared.dimensions(part, 'front') macro('hd', { from: points.cfHips, to: points.hips, @@ -84,13 +174,13 @@ export default (part) => { }) macro('hd', { from: points.cfNeck, - to: points.neck, - y: points.neck.y - sa - 15, + to: points.s3CollarSplit, + y: points.s3CollarSplit.y - sa - 15, }) macro('hd', { from: points.cfNeck, - to: points.shoulder, - y: points.neck.y - sa - 30, + to: points.s3ArmholeSplit, + y: points.s3CollarSplit.y - sa - 30, }) } diff --git a/packages/brian/src/index.js b/packages/brian/src/index.js index c24f0e9d183..e4d00204e6d 100644 --- a/packages/brian/src/index.js +++ b/packages/brian/src/index.js @@ -1,5 +1,6 @@ import freesewing from '@freesewing/core' import plugins from '@freesewing/plugin-bundle' +import mirrorPlugin from '@freesewing/plugin-mirror' import config from '../config' // Parts import draftBase from './base' @@ -9,7 +10,7 @@ import draftSleevecap from './sleevecap' import draftSleeve from './sleeve' // Create design -const Pattern = new freesewing.Design(config, plugins) +const Pattern = new freesewing.Design(config, [plugins, mirrorPlugin]) // Attach draft methods to prototype Pattern.prototype.draftBase = draftBase diff --git a/packages/brian/src/shared.js b/packages/brian/src/shared.js index 03f322bc7c8..2448fe89b93 100644 --- a/packages/brian/src/shared.js +++ b/packages/brian/src/shared.js @@ -1,21 +1,7 @@ -export function saBase(side, points, Path) { - let path = new Path() - if (side === 'back') path.move(points.cbHem) - else path.move(points.cfHem) - path - .line(points.hem) - .line(points.armhole) - .curve(points.armholeCp2, points.armholeHollowCp1, points.armholeHollow) - .curve(points.armholeHollowCp2, points.armholePitchCp1, points.armholePitch) - .curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder) - .line(points.neck) - if (side === 'back') { - path.curve(points.neckCp2, points.cbNeck, points.cbNeck) - } else { - path.curve(points.neckCp2, points.cfNeckCp1, points.cfNeck) - } - - return path +export function s3Notches(part, type) { + const { snippets, Snippet, points, options } = part.shorthand() + if (options.s3Armhole !== 0) snippets.shoulderNotch = new Snippet(type, points.shoulder) + if (options.s3Collar !== 0) snippets.collarNotch = new Snippet(type, points.hps) } export function armholeLength(points, Path) { @@ -34,19 +20,18 @@ export function shoulderToArmholePitch(points, Path) { .length() } -export function dimensions(macro, points, Path, sa) { +export function dimensions(part, side) { + const { macro, points, Path, sa, paths } = part.shorthand() macro('pd', { path: new Path() .move(points.armhole) .curve(points.armholeCp2, points.armholeHollowCp1, points.armholeHollow) .curve(points.armholeHollowCp2, points.armholePitchCp1, points.armholePitch) - .curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder), + .join(paths[`${side}Armhole`]), d: sa + 15, }) macro('pd', { - path: new Path() - .move(points.armholePitch) - .curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder), + path: paths[`${side}Armhole`], d: -15, }) macro('vd', { @@ -61,13 +46,13 @@ export function dimensions(macro, points, Path, sa) { }) macro('vd', { from: points.hem, - to: points.shoulder, + to: points.s3ArmholeSplit, x: points.hips.x + sa + 45, }) macro('vd', { from: points.hem, - to: points.neck, + to: points.s3CollarSplit, x: points.hips.x + sa + 60, }) - macro('ld', { from: points.neck, to: points.shoulder, d: sa + 15 }) + macro('ld', { from: points.s3CollarSplit, to: points.s3ArmholeSplit, d: sa + 15 }) } diff --git a/packages/i18n/src/locales/en/options/brian.yml b/packages/i18n/src/locales/en/options/brian.yml index e6cdad3bb2d..be29eeebfd5 100644 --- a/packages/i18n/src/locales/en/options/brian.yml +++ b/packages/i18n/src/locales/en/options/brian.yml @@ -34,6 +34,14 @@ lengthBonus: title: Length bonus description: The amount to lengthen the garment. A negative value will shorten it. +s3Collar: + title: 'Shoulder seam shift: collar side' + description: Increase this option to shift the shoulder seam forward on the collar side. Decreasing it shifts it backwards. + +s3Armhole: + title: 'Shoulder seam shift: armhole side' + description: Increase this option to shift the shoulder seam forward on the armhole side. Decreasing it shifts it backwards. + shoulderEase: title: Shoulder ease description: The amount of ease at your shoulder. This increases the shoulder to shoulder distance to accommodate additional layers or thickness.