diff --git a/config/dependencies.yaml b/config/dependencies.yaml index c437a916497..fba98bbc1d1 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -380,6 +380,7 @@ shared: 'lodash.clonedeep': '4.5.0' 'lodash.orderby': *_orderby 'lodash.unset': *_unset + 'lodash.get': *_get 'mdast-util-toc': '6.1.0' 'pdfkit': '0.13.0' 'postcss-for': '2.1.1' diff --git a/designs/bella/src/back.mjs b/designs/bella/src/back.mjs index adb64cd9590..9d4565bf127 100644 --- a/designs/bella/src/back.mjs +++ b/designs/bella/src/back.mjs @@ -65,6 +65,7 @@ export const back = { measurements, log, part, + addCut, }) => { // Get to work points.cbNeck = new Point(0, measurements.neck * options.backNeckCutout) @@ -269,6 +270,8 @@ export const back = { on: ['armholePitch', 'bustCenter'], }) + addCut() + if (sa) paths.sa = paths.saBase.offset(sa).attr('class', 'fabric sa') if (paperless) { diff --git a/designs/carlton/src/back.mjs b/designs/carlton/src/back.mjs index 75ae0808ca0..81181bab8dd 100644 --- a/designs/carlton/src/back.mjs +++ b/designs/carlton/src/back.mjs @@ -16,6 +16,7 @@ function draftCarltonBack({ paths, Path, part, + addCut, }) { calculateRatios(part) // Belt width @@ -95,6 +96,8 @@ function draftCarltonBack({ .line(points.bpStart) .attr('class', 'dashed') + addCut(2) + addCut(2, 'lining') if (complete) { macro('sprinkle', { snippet: 'bnotch', diff --git a/plugins/plugin-bundle/src/index.mjs b/plugins/plugin-bundle/src/index.mjs index d72b9f954fe..d7466568b35 100644 --- a/plugins/plugin-bundle/src/index.mjs +++ b/plugins/plugin-bundle/src/index.mjs @@ -12,6 +12,7 @@ import { roundPlugin } from '../../plugin-round/src/index.mjs' import { scaleboxPlugin } from '../../plugin-scalebox/src/index.mjs' import { sprinklePlugin } from '../../plugin-sprinkle/src/index.mjs' import { titlePlugin } from '../../plugin-title/src/index.mjs' +import { pluginCutlist } from '../../plugin-cutlist/src/index.mjs' import { name, version } from '../data.mjs' const bundledPlugins = [ @@ -29,38 +30,44 @@ const bundledPlugins = [ scaleboxPlugin, sprinklePlugin, titlePlugin, + pluginCutlist, ] -function bundleHooks() { - const hooks = {} - for (const plugin of bundledPlugins) { - for (const i in plugin.hooks) { - if (typeof hooks[i] === 'undefined') hooks[i] = [] - const hook = plugin.hooks[i] - if (typeof hook === 'function') hooks[i].push(hook) - else if (typeof hook === 'object') { - for (let method of hook) hooks[i].push(method) - } +const hooks = {} +const macros = {} +const store = [] + +function bundleHooks(plugin) { + for (const i in plugin.hooks) { + if (typeof hooks[i] === 'undefined') hooks[i] = [] + const hook = plugin.hooks[i] + if (typeof hook === 'function') hooks[i].push(hook) + else if (typeof hook === 'object') { + for (let method of hook) hooks[i].push(method) } } - - return hooks } -function bundleMacros() { - const macros = {} - for (const plugin of bundledPlugins) { - for (const i in plugin.macros) macros[i] = plugin.macros[i] - } +function bundleMacros(plugin) { + for (const i in plugin.macros) macros[i] = plugin.macros[i] +} - return macros +function bundleStore(plugin) { + if (plugin.store) store.push(...plugin.store) +} + +for (const plugin of bundledPlugins) { + bundleHooks(plugin, hooks) + bundleMacros(plugin, macros) + bundleStore(plugin, store) } export const plugin = { name, version, - hooks: bundleHooks(), - macros: bundleMacros(), + hooks, + macros, + store, } // More specifically named exports diff --git a/sites/shared/components/workbench/layout/cut/index.mjs b/sites/shared/components/workbench/layout/cut/index.mjs index eb5355d68ae..6727cf9db05 100644 --- a/sites/shared/components/workbench/layout/cut/index.mjs +++ b/sites/shared/components/workbench/layout/cut/index.mjs @@ -4,8 +4,10 @@ import { Draft } from '../draft/index.mjs' import { fabricPlugin } from '../plugin-layout-part.mjs' import { cutLayoutPlugin } from './plugin-cut-layout.mjs' import { pluginCutlist } from '@freesewing/plugin-cutlist' -import { useEffect } from 'react' import { measurementAsMm } from 'shared/utils.mjs' +import { useState, useEffect, useCallback, useRef } from 'react' +import { Tabs } from 'shared/components/mdx/tabs.mjs' +import get from 'lodash.get' export const CutLayout = (props) => { const { t } = useTranslation(['workbench']) @@ -15,46 +17,89 @@ export const CutLayout = (props) => { if (props.gist?._state?.xray?.enabled) props.updateGist(['_state', 'xray', 'enabled'], false) }) - const draft = props.draft const isImperial = props.gist.units === 'imperial' - const gistSettings = props.gist?._state?.layout?.forCutting?.fabric || {} - // add the pages plugin to the draft - const layoutSettings = { - sheetWidth: gistSettings.sheetWidth || measurementAsMm(isImperial ? 54 : 120, props.gist.units), - sheetHeight: - gistSettings.sheetHeight || measurementAsMm(isImperial ? 36 : 100, props.gist.units), - } - draft.use(fabricPlugin(layoutSettings)) - draft.use(pluginCutlist) - draft.use(cutLayoutPlugin) + const [patternProps, setPatternProps] = useState(undefined) + const [cutFabrics, setCutFabrics] = useState(['fabric']) + const [draft, setDraft] = useState() + const [cutFabric, setCutFabric] = useState('fabric') + + const gistSettings = get(props.gist, ['_state', 'layout', 'forCutting', 'fabric', cutFabric]) + const sheetWidth = + gistSettings?.sheetWidth || measurementAsMm(isImperial ? 54 : 120, props.gist.units) + const gist = props.gist + const sheetHeight = measurementAsMm(isImperial ? 36 : 100, props.gist.units) + + useEffect(() => { + try { + // get the appropriate layout for the view + const layout = gist.layouts?.[gist._state.view]?.[cutFabric] || gist.layout || true + // hand it separately to the design + const draft = new props.design({ ...gist, layout }) + + // add the pages plugin to the draft + const layoutSettings = { + sheetWidth, + sheetHeight, + } + draft.use(fabricPlugin(layoutSettings)) + draft.use(pluginCutlist) + draft.use(cutLayoutPlugin(cutFabric)) + // draft the pattern + draft.draft() + setPatternProps(draft.getRenderProps()) + + const cutList = draft.setStores[0].get('cutlist') + const cf = ['fabric'] + for (const partName in cutList) { + for (const matName in cutList[partName].materials) { + if (!cf.includes(matName)) cf.push(matName) + } + } + setCutFabrics(cf) + } catch (err) { + console.log(err, props.gist) + } + }, [cutFabric, isImperial, gist]) - let patternProps - try { - // draft the pattern - draft.draft() - patternProps = draft.getRenderProps() - } catch (err) { - console.log(err, props.gist) - } const bgProps = { fill: 'url(#page)' } let name = props.design.designConfig.data.name name = name.replace('@freesewing/', '') - return ( + + return patternProps ? (

{t('layoutThing', { thing: name }) + ': ' + t('forCutting')}

- - +
+
+ {cutFabrics.map((title) => ( + + ))} +
+ +
- ) + ) : null } diff --git a/sites/shared/components/workbench/layout/cut/plugin-cut-layout.mjs b/sites/shared/components/workbench/layout/cut/plugin-cut-layout.mjs index 46354c69600..34faf9c693b 100644 --- a/sites/shared/components/workbench/layout/cut/plugin-cut-layout.mjs +++ b/sites/shared/components/workbench/layout/cut/plugin-cut-layout.mjs @@ -1,70 +1,80 @@ const prefix = 'mirroredOnFold' const redraft = ({ part }) => part -export const cutLayoutPlugin = { - hooks: { - postPartDraft: (pattern) => { - const partCutlist = pattern.setStores[pattern.activeSet].get(['cutlist', pattern.activePart]) - if (!partCutlist) return +export const cutLayoutPlugin = function (material) { + return { + hooks: { + postPartDraft: (pattern) => { + if (pattern.activePart.startsWith('cut.') || pattern.activePart === 'fabric') return - const { macro } = pattern.parts[pattern.activeSet][pattern.activePart].shorthand() - if (partCutlist.cutOnFold) macro('mirrorOnFold', { fold: partCutlist.cutOnFold }) + const partCutlist = pattern.setStores[pattern.activeSet].get([ + 'cutlist', + pattern.activePart, + ]) - if (partCutlist.materials) { - for (const material in partCutlist.materials) { - for (var i = 1; i < partCutlist.materials[material]; i++) { - pattern.addPart({ - name: `${pattern.activePart}_${material}_${i}`, - from: pattern.activePart, - draft: redraft, - }) - } + if (!partCutlist?.materials?.[material] && material !== 'fabric') { + pattern.parts[pattern.activeSet][pattern.activePart].hide() + return } - } + + if (partCutlist?.cutOnFold) { + const { macro } = pattern.parts[pattern.activeSet][pattern.activePart].shorthand() + macro('mirrorOnFold', { fold: partCutlist.cutOnFold }) + } + + for (var i = 1; i < partCutlist?.materials?.[material].cut; i++) { + const dupPartName = `cut.${pattern.activePart}.${material}_${i + 1}` + pattern.addPart({ + name: dupPartName, + from: pattern.config.parts[pattern.activePart], + draft: redraft, + }) + } + }, }, - }, - macros: { - mirrorOnFold: ({ fold }, { paths, snippets, utils, macro, points }) => { - const mirrorPaths = [] - for (const p in paths) { - if (!paths[p].hidden && !p.startsWith(prefix)) mirrorPaths.push(paths[p]) - } + macros: { + mirrorOnFold: ({ fold }, { paths, snippets, utils, macro, points }) => { + const mirrorPaths = [] + for (const p in paths) { + if (!paths[p].hidden && !p.startsWith(prefix)) mirrorPaths.push(paths[p]) + } - const mirrorPoints = [] - const snippetsByType = {} - for (var s in snippets) { - const snip = snippets[s] - if (['logo'].indexOf(snip.def) > -1) continue + const mirrorPoints = [] + const snippetsByType = {} + for (var s in snippets) { + const snip = snippets[s] + if (['logo'].indexOf(snip.def) > -1) continue - snippetsByType[snip.def] = snippetsByType[snip.def] || [] + snippetsByType[snip.def] = snippetsByType[snip.def] || [] - mirrorPoints.push(snip.anchor) - for (var pName in points) { - if (points[pName] === snip.anchor) { - snippetsByType[snip.def].push(prefix + utils.capitalize(pName)) - break + mirrorPoints.push(snip.anchor) + for (var pName in points) { + if (points[pName] === snip.anchor) { + snippetsByType[snip.def].push(prefix + utils.capitalize(pName)) + break + } } } - } - let unnamed = 0 - macro('mirror', { - paths: Object.values(mirrorPaths), - points: mirrorPoints, - mirror: fold, - prefix, - nameFormat: (path) => { - unnamed++ - return `${prefix}_${unnamed}` - }, - }) - - for (var def in snippetsByType) { - macro('sprinkle', { - snippet: def, - on: snippetsByType[def], + let unnamed = 0 + macro('mirror', { + paths: Object.values(mirrorPaths), + points: mirrorPoints, + mirror: fold, + prefix, + nameFormat: (path) => { + unnamed++ + return `${prefix}_${unnamed}` + }, }) - } + + for (var def in snippetsByType) { + macro('sprinkle', { + snippet: def, + on: snippetsByType[def], + }) + } + }, }, - }, + } } diff --git a/sites/shared/components/workbench/layout/cut/settings.mjs b/sites/shared/components/workbench/layout/cut/settings.mjs index 0fe4c2153ed..7156e591e9b 100644 --- a/sites/shared/components/workbench/layout/cut/settings.mjs +++ b/sites/shared/components/workbench/layout/cut/settings.mjs @@ -1,39 +1,32 @@ -import { useMemo, useEffect, useState, useCallback } from 'react' -import { ClearIcon } from 'shared/components/icons.mjs' +import { useMemo, useEffect, useState, useCallback, useRef } from 'react' +import { ClearIcon, PageIcon } from 'shared/components/icons.mjs' import { useTranslation } from 'next-i18next' import { formatFraction128, measurementAsMm, round, formatMm } from 'shared/utils.mjs' +import get from 'lodash.get' -const FabricSizer = ({ gist, updateGist }) => { +const FabricSizer = ({ gist, updateGist, cutFabric, sheetWidth }) => { const { t } = useTranslation(['workbench']) - const [val, setVal] = useState(500) - - useEffect(() => { - setVal(formatMm(gist._state?.layout?.forCutting?.fabric.sheetWidth || 500, gist.units, 'none')) - }, [gist]) - const setFabricWidth = (width) => {} + let val = formatMm(sheetWidth, gist.units, 'none') // onChange - const update = useCallback( - (evt) => { - evt.stopPropagation() - let evtVal = evt.target.value - // set Val immediately so that the input reflects it - setVal(evtVal) + const update = (evt) => { + evt.stopPropagation() + let evtVal = evt.target.value + // set Val immediately so that the input reflects it + val = evtVal - let useVal = measurementAsMm(evtVal, gist.units) - // only set to the gist if it's valid - if (!isNaN(useVal)) { - updateGist(['_state', 'layout', 'forCutting', 'fabric', 'sheetWidth'], useVal) - } - }, - [gist.units] - ) + let useVal = measurementAsMm(evtVal, gist.units) + // only set to the gist if it's valid + if (!isNaN(useVal)) { + updateGist(['_state', 'layout', 'forCutting', 'fabric', cutFabric, 'sheetWidth'], useVal) + } + } return ( -
+