UNPKG

@freesewing/bruce

Version:

A FreeSewing pattern for boxer briefs

402 lines (373 loc) 12.1 kB
import { inset } from './inset.mjs' import { init } from './init.mjs' function tuskDelta(Path, points, store) { const len = new Path() .move(points.midRight) .curve(points.curveRightCpTop, points.curveRightCpBottom, points.rightTuskRight) .length() return len - store.get('curve') } function tweakTusk(delta, points) { let factor if (Math.abs(delta) > 2) factor = 3 else factor = 5 points.rightTuskRight = points.rightTuskRight.shift(90, delta / factor) points.rightTuskLeft = points.rightTuskLeft.shift(90, delta / factor) points.curveRightCpBottom = points.curveRightCpBottom.shift(90, delta / factor) } function draftBruceFront({ store, sa, Point, points, Path, paths, options, complete, macro, snippets, Snippet, log, part, }) { let adjustment_warning = false // Initialize init(part) points.topRight = new Point(store.get('hipsFront') / 2, 0) points.topLeft = points.topRight.flipX() points.midMid = new Point(0, store.get('heightFront')) points.midRight = new Point(points.topRight.x + store.get('heightFront') * 0.05, points.midMid.y) points.midLeft = points.midRight.flipX() // Store this length for a notch on the side part store.set('frontNotch', points.topRight.dist(points.midRight)) points.bottomMid = new Point(0, store.get('riseLength')) points.rightTuskRight = new Point( store.get('gusset') * store.get('xScale') * (1 - store.get('gussetInsetRatio')), points.bottomMid.y ) points.rightTuskLeft = points.bottomMid.clone() points.curveRightCpTop = new Point( points.midRight.x - store.get('gusset') * 1.3, points.midRight.y ) points.curveRightCpBottom = new Point( points.rightTuskRight.x, points.rightTuskRight.y - store.get('gusset') * 1.3 ) // Adjust tusk length to fit inset curve let delta = tuskDelta(Path, points, store) let previous_delta const points_to_save = ['rightTuskRight', 'rightTuskLeft', 'curveRightCpBottom'] let saved = [] let stop = false if (Math.abs(delta) <= 1) { // We started below the 1mm threshold. No adjustment needed. stop = true } let count = 0 while (!stop) { for (const i of points_to_save) { saved[i] = points[i] } previous_delta = delta tweakTusk(delta, points) delta = tuskDelta(Path, points, store) count++ if (Math.abs(delta) <= 1) { // Below 1mm threshold is good enough. stop = true } else if (Math.abs(delta) > Math.abs(previous_delta) || count >= 150) { // The adjustments have failed, either starting to produce // worse results or exceeding the maximum number of runs. // Stop the adjustment process, print a warning message, // and revert back to the last good set of points if appropriate. stop = true log.warn( 'Unable to adjust the front tusk length to fit the given measurements, after ' + count + ' iterations.' ) adjustment_warning = true if (Math.abs(delta) > Math.abs(previous_delta)) for (const i of points_to_save) points[i] = saved[i] } } // Adjust midMid to new length points.bottomMid = new Point(0, points.rightTuskLeft.y) // Front dart only if bulge > 0 if (options.bulge > 0) { // Rotate tusk according to bulge option for (let pid of ['curveRightCpTop', 'curveRightCpBottom', 'rightTuskRight', 'rightTuskLeft']) { points[pid] = points[pid].rotate(options.bulge, points.midRight) } // Dart join point points.dartJoin = new Point(0, points.midMid.y + 0.65 * points.midMid.dist(points.bottomMid)) // Dart control point points.dartCpRight = new Point( 0, points.dartJoin.y + points.dartJoin.dist(points.bottomMid) * (options.bulge / 30) ) points.dartCpRight = points.dartCpRight.rotate(options.bulge, points.dartJoin) // Flip control point to left side points.dartCpLeft = points.dartCpRight.flipX() } else { points.dartJoin = points.rightTuskLeft } // Flip points to left side points.leftTuskRight = points.rightTuskLeft.flipX() points.leftTuskLeft = points.rightTuskRight.flipX() points.curveLeftCpBottom = points.curveRightCpBottom.flipX() points.curveLeftCpTop = points.curveRightCpTop.flipX() // Handle back rise points.topMid = new Point(0, points.topLeft.y) points.topLeft = points.topLeft.shift(90, store.get('frontRise')) points.topRight = points.topRight.shift(90, store.get('frontRise')) points.topMidCpRight = new Point(points.topRight.x / 2, points.topMid.y) points.topMidCpLeft = points.topMidCpRight.flipX() if (options.bulge > 0) { paths.trimBase = new Path() .move(points.rightTuskLeft) .curve(points.rightTuskLeft, points.dartCpRight, points.dartJoin) .curve(points.dartCpLeft, points.leftTuskRight, points.leftTuskRight) paths.seamStart = new Path() .move(points.midLeft) .line(points.topLeft) .curve(points.topLeft, points.topMidCpLeft, points.topMid) .curve(points.topMidCpRight, points.topRight, points.topRight) .line(points.midRight) .curve(points.curveRightCpTop, points.curveRightCpBottom, points.rightTuskRight) .line(points.rightTuskLeft) paths.seamEnd = new Path() .move(points.leftTuskRight) .line(points.leftTuskLeft) .curve(points.curveLeftCpBottom, points.curveLeftCpTop, points.midLeft) paths.seamStart.hide() paths.trimBase.hide() paths.seamEnd.hide() paths.seam = paths.seamStart.join(paths.trimBase).join(paths.seamEnd) } else { paths.seam = new Path() .move(points.midLeft) .line(points.topLeft) .curve(points.topLeft, points.topMidCpLeft, points.topMid) .curve(points.topMidCpRight, points.topRight, points.topRight) .line(points.midRight) .curve(points.curveRightCpTop, points.curveRightCpBottom, points.rightTuskRight) .line(points.leftTuskLeft) .curve(points.curveLeftCpBottom, points.curveLeftCpTop, points.midLeft) } paths.seam.close().attr('class', 'fabric') if (sa) { if (options.bulge > 0) { let saStart = paths.seamStart.offset(sa * -1) let saTrim = paths.trimBase.offset(sa * -1).trim() let saEnd = paths.seamEnd.offset(sa * -1) paths.sa = saStart.join(saTrim).join(saEnd).close().attr('class', 'fabric sa') } else { paths.sa = paths.seam .offset(sa * -1) .trim() .close() .attr('class', 'fabric sa') } } /* * Annotations */ // Cut list store.cutlist.addCut({ cut: 2, from: 'fabric' }) // Title macro('title', { at: points.midMid, nr: 2, title: 'front', }) // Grainline macro('grainline', { from: points.dartJoin, to: points.topMid, }) // Notches snippets.sideNotch = new Snippet('notch', points.midRight) points.curveNotch = new Path() .move(points.midRight) .curve(points.curveRightCpTop, points.curveRightCpBottom, points.rightTuskRight) .shiftFractionAlong(0.5) snippets.curveNotch1 = new Snippet('notch', points.curveNotch) snippets.curveNotch2 = new Snippet('notch', points.curveNotch.flipX()) if (complete) { paths.sideNoteLeft = new Path().move(points.midLeft).line(points.topLeft).addClass('hidden') paths.sideNoteRight = new Path().move(points.topRight).line(points.midRight).addClass('hidden') paths.tuskNoteLeft = new Path() .move(points.leftTuskLeft) .line(points.leftTuskRight) .addClass('hidden') paths.tuskNoteRight = new Path() .move(points.rightTuskLeft) .line(points.rightTuskRight) .addClass('hidden') paths.curveNoteRight = new Path() .move(points.rightTuskRight) .curve(points.curveRightCpBottom, points.curveRightCpTop, points.midRight) .addClass('hidden') paths.curveNoteLeft = new Path() .move(points.midLeft) .curve(points.curveLeftCpTop, points.curveLeftCpBottom, points.leftTuskLeft) .addClass('hidden') macro('banner', { id: 'sideLeft', path: paths.sideNoteLeft, text: '#', dy: 7, classes: 'text-sm fill-note center', }) macro('banner', { id: 'sideRight', path: paths.sideNoteRight, text: '#', dy: 7, classes: 'text-sm fill-note center', }) macro('banner', { id: 'tuskLeft', path: paths.tuskNoteLeft, text: '*', spaces: 2, classes: 'text-sm fill-note center', }) macro('banner', { id: 'tuskRight', path: paths.tuskNoteRight, text: '*', spaces: 2, classes: 'text-sm fill-note center', }) macro('banner', { id: 'curveLeft', path: paths.curveNoteLeft, text: '~', classes: 'text-sm fill-note center', }) macro('banner', { id: 'curveRight', path: paths.curveNoteRight, text: '~', classes: 'text-sm fill-note center', }) } // Dimensions macro('hd', { id: 'wWaist', from: points.topLeft, to: points.topRight, y: points.topLeft.y - 15 - sa, }) macro('hd', { id: 'wFull', from: points.midLeft, to: points.midRight, y: points.topLeft.y - 30 - sa, }) macro('vd', { id: 'hInsetPointToCfWaist', from: points.midLeft, to: points.topMid, x: points.midLeft.x - 15 - sa, }) macro('vd', { id: 'hInsetPointToTop', from: points.midLeft, to: points.topLeft, x: points.midLeft.x - 30 - sa, }) if (options.bulge === 0) { macro('hd', { id: 'wTusk', from: points.leftTuskLeft, to: points.rightTuskRight, y: points.leftTuskLeft.y + 15 + sa, }) macro('vd', { id: 'hFull', from: points.leftTuskLeft, to: points.topLeft, x: points.midLeft.x - 45 - sa, }) } else { macro('vd', { id: 'hTuskOuterToTop', from: points.leftTuskLeft, to: points.topLeft, x: points.midLeft.x - 45 - sa, }) macro('vd', { id: 'hFull', from: points.leftTuskRight, to: points.topLeft, x: points.midLeft.x - 60 - sa, }) points.narrowRight = new Path() .move(points.midRight) .curve(points.curveRightCpTop, points.curveRightCpBottom, points.rightTuskRight) .edge('left') points.narrowLeft = new Path() .move(points.midLeft) .curve(points.curveLeftCpTop, points.curveLeftCpBottom, points.leftTuskLeft) .attr('class', 'various stroke-xl lashed') .edge('right') macro('hd', { id: 'wBetweenInsets', from: points.narrowLeft, to: points.narrowRight, y: points.narrowLeft.y, }) macro('hd', { id: 'wBetweenTustInnerTips', from: points.leftTuskRight, to: points.rightTuskLeft, y: points.rightTuskLeft.y + 15 + sa, }) macro('hd', { id: 'wBetweenTustOuterTips', from: points.leftTuskLeft, to: points.rightTuskRight, y: points.rightTuskLeft.y + 30 + sa, }) macro('ld', { id: 'lTustBottom', from: points.rightTuskLeft, to: points.rightTuskRight, d: -15 - sa, }) macro('vd', { id: 'hInflectionToTop', from: points.narrowRight, to: points.topRight, x: points.topRight.x + 15 + sa, }) macro('vd', { id: 'hTustJointToTop', from: points.dartJoin, to: points.topRight, x: points.topRight.x + 30 + sa, }) } if (adjustment_warning) { log.warn( 'We were not able to generate the Front pattern piece correctly. ' + 'Manual fitting and alteration of this and other pattern pieces ' + 'are likely to be needed. ' + 'First, please retake your measurements and generate a new pattern ' + 'using the new measurements. ' + 'If you still see this warning with the new pattern, then please ' + 'make a test garment, check fit, and make alterations as necessary ' + 'before trying to make the final garment.' ) } return part } export const front = { name: 'bruce.front', after: inset, draft: draftBruceFront, }