1
0
Fork 0

cutting layout page has tabs for each fabric type. parts duplication

This commit is contained in:
Enoch Riese 2023-02-27 17:47:34 -06:00
parent cd034a7cf3
commit 29e3ba6323
12 changed files with 224 additions and 158 deletions

View file

@ -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'

View file

@ -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) {

View file

@ -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',

View file

@ -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,11 +30,14 @@ const bundledPlugins = [
scaleboxPlugin,
sprinklePlugin,
titlePlugin,
pluginCutlist,
]
function bundleHooks() {
const hooks = {}
for (const plugin of bundledPlugins) {
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]
@ -44,23 +48,26 @@ function bundleHooks() {
}
}
return hooks
}
function bundleMacros() {
const macros = {}
for (const plugin of bundledPlugins) {
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

View file

@ -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,36 +17,78 @@ 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 || {}
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: gistSettings.sheetWidth || measurementAsMm(isImperial ? 54 : 120, props.gist.units),
sheetHeight:
gistSettings.sheetHeight || measurementAsMm(isImperial ? 36 : 100, props.gist.units),
sheetWidth,
sheetHeight,
}
draft.use(fabricPlugin(layoutSettings))
draft.use(pluginCutlist)
draft.use(cutLayoutPlugin)
let patternProps
try {
draft.use(cutLayoutPlugin(cutFabric))
// draft the pattern
draft.draft()
patternProps = draft.getRenderProps()
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])
const bgProps = { fill: 'url(#page)' }
let name = props.design.designConfig.data.name
name = name.replace('@freesewing/', '')
return (
return patternProps ? (
<div>
<h2 className="capitalize">{t('layoutThing', { thing: name }) + ': ' + t('forCutting')}</h2>
<CutLayoutSettings {...props} patternProps={patternProps} />
<CutLayoutSettings
{...{ ...props, patternProps, cutFabric, sheetWidth }}
patternProps={patternProps}
cutFabric={cutFabric}
/>
<div className="my-4">
<div className="tabs">
{cutFabrics.map((title) => (
<button
key={title}
className={`text-xl font-bold capitalize tab tab-bordered grow ${
cutFabric === title ? 'tab-active' : ''
}`}
onClick={() => setCutFabric(title)}
>
{title}
</button>
))}
</div>
<Draft
draft={draft}
gist={props.gist}
@ -53,8 +97,9 @@ export const CutLayout = (props) => {
bgProps={bgProps}
gistReady={props.gistReady}
layoutPart="fabric"
layoutType="cuttingLayout"
layoutType={['cuttingLayout', cutFabric]}
/>
</div>
)
</div>
) : null
}

View file

@ -1,26 +1,35 @@
const prefix = 'mirroredOnFold'
const redraft = ({ part }) => part
export const cutLayoutPlugin = {
export const cutLayoutPlugin = function (material) {
return {
hooks: {
postPartDraft: (pattern) => {
const partCutlist = pattern.setStores[pattern.activeSet].get(['cutlist', pattern.activePart])
if (!partCutlist) return
if (pattern.activePart.startsWith('cut.') || pattern.activePart === 'fabric') return
const partCutlist = pattern.setStores[pattern.activeSet].get([
'cutlist',
pattern.activePart,
])
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()
if (partCutlist.cutOnFold) macro('mirrorOnFold', { fold: partCutlist.cutOnFold })
macro('mirrorOnFold', { fold: partCutlist.cutOnFold })
}
if (partCutlist.materials) {
for (const material in partCutlist.materials) {
for (var i = 1; i < partCutlist.materials[material]; i++) {
for (var i = 1; i < partCutlist?.materials?.[material].cut; i++) {
const dupPartName = `cut.${pattern.activePart}.${material}_${i + 1}`
pattern.addPart({
name: `${pattern.activePart}_${material}_${i}`,
from: pattern.activePart,
name: dupPartName,
from: pattern.config.parts[pattern.activePart],
draft: redraft,
})
}
}
}
},
},
macros: {
@ -68,3 +77,4 @@ export const cutLayoutPlugin = {
},
},
}
}

View file

@ -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) => {
const update = (evt) => {
evt.stopPropagation()
let evtVal = evt.target.value
// set Val immediately so that the input reflects it
setVal(evtVal)
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)
updateGist(['_state', 'layout', 'forCutting', 'fabric', cutFabric, 'sheetWidth'], useVal)
}
}
},
[gist.units]
)
return (
<div className="flex gap-4">
<div className="flex gap-4 px-0 font-bold items-center">
<div className="form-control mb-2 flex flex-row" key="wrap-fabricWidth">
<label className="input-group input-group-xs">
<span className="label-text font-bold">{t('fabricWidth')}</span>
<span className="label-text font-bold">{`${t(cutFabric)} ${t('width')}`}</span>
<input
key="input-fabricWidth"
type="text"
@ -68,30 +61,33 @@ const useFabricLength = (isImperial, height) => {
return `${count}${isImperial ? 'yds' : 'm'}`
}
export const CutLayoutSettings = ({ gist, patternProps, unsetGist, updateGist }) => {
export const CutLayoutSettings = ({
gist,
patternProps,
unsetGist,
updateGist,
cutFabric,
sheetWidth,
}) => {
const { t } = useTranslation(['workbench'])
const fabricLength = useFabricLength(gist.units === 'imperial', patternProps.height)
return (
<div className="flex flex-row justify-between mb-2 items-baseline">
<FabricSizer {...{ gist, updateGist, cutFabric, sheetWidth }} />
<div>
<div
className="flex flex-row justify-between
mb-2"
>
<PageIcon className="h-6 w-6 mr-2 inline align-middle" />
<span className="text-xl font-bold align-middle">{fabricLength}</span>
</div>
<button
key="reset"
onClick={() => unsetGist(['layouts', 'cuttingLayout'])}
onClick={() => unsetGist(['layouts', 'cuttingLayout', cutFabric])}
className="btn btn-primary btn-outline"
>
<ClearIcon className="h-6 w-6 mr-2" />
{t('reset')}
</button>
</div>
<div className="flex flex-row font-bold items-center px-0">
<FabricSizer {...{ gist, updateGist }} />
<span className="ml-2">{fabricLength}</span>
</div>
</div>
)
}

View file

@ -2,10 +2,10 @@ import { useRef } from 'react'
import { Stack } from './stack.mjs'
import { SvgWrapper } from '../../draft/svg.mjs'
import { PartInner } from '../../draft/part.mjs'
import get from 'lodash.get'
export const Draft = (props) => {
const {
draft,
patternProps,
gist,
updateGist,
@ -18,7 +18,8 @@ export const Draft = (props) => {
const svgRef = useRef(null)
if (!patternProps) return null
// keep a fresh copy of the layout because we might manipulate it without saving to the gist
let layout = draft.settings[0].layouts?.[layoutType] || {
const layoutPath = ['layouts'].concat(layoutType)
let layout = get(patternProps.settings[0], layoutPath) || {
...patternProps.autoLayout,
width: patternProps.width,
height: patternProps.height,
@ -53,7 +54,7 @@ export const Draft = (props) => {
newLayout.topLeft = topLeft
if (history) {
updateGist(['layouts', layoutType], newLayout, history)
updateGist(layoutPath, newLayout, history)
} else {
// we don't put it in the gist if it shouldn't contribute to history because we need some of the data calculated here for rendering purposes on the initial layout, but we don't want to actually save a layout until the user manipulates it. This is what allows the layout to respond appropriately to settings changes. Once the user has starting playing with the layout, all bets are off
layout = newLayout

View file

@ -117,6 +117,9 @@ const basePlugin = ({
version,
hooks: {
preLayout: function (pattern) {
if (!responsiveColumns) {
pattern.settings[0].maxWidth = sheetWidth
}
// Add part
pattern.addPart({
name: partName,

View file

@ -4,7 +4,7 @@ import { useTranslation } from 'next-i18next'
export const CoreSettingOnly = (props) => {
const { t } = useTranslation(['app', 'parts', 'settings'])
const list = props.draft.config.draftOrder
const list = props.design.patternConfig.draftOrder
const partNames = list.map((part) => ({ id: part, name: t(`parts:${part}`) }))
const togglePart = (part) => {

View file

@ -21,7 +21,7 @@ export const OptionComponent = (props) => {
const Value = values[capitalize(type)]
try {
const hide = opt.hide && opt.hide(props.draft.settings)
const hide = opt.hide && opt.hide(props.gist)
if (hide) return null
} catch (e) {

View file

@ -119,9 +119,7 @@ export const WorkbenchWrapper = ({
// Generate the draft here so we can pass it down to both the view and the options menu
let draft = false
if (
['draft', 'logs', 'test', 'printingLayout', 'cuttingLayout'].indexOf(gist._state?.view) !== -1
) {
if (['draft', 'logs', 'test', 'printingLayout'].indexOf(gist._state?.view) !== -1) {
gist.embed = true
// get the appropriate layout for the view
const layout = gist.layouts?.[gist._state.view] || gist.layout || true
@ -148,7 +146,6 @@ export const WorkbenchWrapper = ({
updateGist: updateWBGist,
unsetGist,
setGist,
draft,
feedback,
gistReady,
showInfo: setPopup,
@ -178,7 +175,7 @@ export const WorkbenchWrapper = ({
<LayoutComponent {...layoutProps}>
{messages}
<ErrorBoundary {...errorProps}>
<Component {...componentProps} />
<Component {...componentProps} draft={draft} />
{popup && <Modal cancel={() => setPopup(false)}>{popup}</Modal>}
</ErrorBoundary>
</LayoutComponent>