设计指南
Hi there and welcome to this guide that explains how open clothes designs work.
it's about what goes on under the hood each time a sewing pattern is generated by open clothes.
This illustration is a good starting point to gain a better understanding of the structure of a open clothes design:
- 预览
- 代码
- 透视
({ Point, points, Path, paths, options, part }) => {
// Draws a w*h box, returns a Path object
const box = (name, origin, width, height, classes='fabric') => {
let base = height
if (width < height) base = width
let t = base
points[name + 'TopLeft'] = new Point(origin.x, origin.y)
points[name + 'BottomLeft'] = new Point(origin.x, origin.y + height)
points[name + 'BottomRight'] = new Point(
origin.x + width,
origin.y + height
)
points[name + 'TopRight'] = new Point(origin.x + width, origin.y)
points[name + 'Mid'] = points[name + 'TopLeft'].shiftFractionTowards(
points[name + 'BottomRight'],
0.5
)
points[name + 'Mid'].y += 3
return new Path()
.move(points[name + 'TopLeft'])
.line(points[name + 'BottomLeft'])
.line(points[name + 'BottomRight'])
.line(points[name + 'TopRight'])
.line(points[name + 'TopLeft'])
.close()
.addClass(classes)
}
// Draws and arrow from to and places text
const arrow = (name, text = '') => {
let from = points[name + 'From']
let to = points[name + 'To']
from = from.shiftTowards(to, 2)
to = to.shiftTowards(from, 2)
const base = from.dist(to)
const r = 3
points[name + 'Tip1'] = to.shiftTowards(from, 3.5).rotate(r, from)
points[name + 'Tip2'] = to.shiftTowards(from, 3.5).rotate(r * -1, from)
const path = new Path()
.move(from)
.line(to)
.move(points[name + 'Tip1'])
.line(to)
.line(points[name + 'Tip2'])
.addClass('lining stroke-lg')
if (options.focus === name) path.addClass('注意')
return text
? path.addText(' ' + text, options.focus === name ? 'fill-注意 center' : 'center')
: path
}
// Draws a box and handled text placement
const drawBox = (name, x, y, width, height, text=true, classes, textClasses='') => {
points[name + 'Origin'] = new Point(x, y)
paths[name] = box(name, points[name + 'Origin'], width, height, classes)
if (text === 'b') {
paths[name+'_label'] = new Path()
.move(points[name+'BottomLeft'])
.line(points[name+'BottomRight'])
.addClass('hidden')
.addText(name, 'center ' + textClasses)
.attr('data-text-dy', -1)
}
else if (text === 't') {
paths[name+'_label'] = new Path()
.move(points[name+'TopLeft'])
.line(points[name+'TopRight'])
.addClass('hidden')
.addText(name, 'center ' + textClasses)
.attr('data-text-dy', 11)
}
else if (text === 'r') {
paths[name+'_label'] = new Path()
.move(points[name+'BottomRight'])
.line(points[name+'TopRight'])
.addClass('hidden')
.addText(name, 'center ' + textClasses)
.attr('data-text-dx', -5)
}
else if (text) points[name + 'Mid'].addText(name, 'center')
}
// 设置
drawBox('设置 Set 0', -140, -18, 50, 20, '设置 0', 'fabric fill-bg')
drawBox('设置 Set 1', -140, 6, 50, 20, '设置 0', 'fabric fill-bg')
const sname = '设置'
drawBox(sname, -145, -24, 61, 65, 'b', 'fabric', 'text-lg bold')
points.arrowDraftFrom = new Point(-84,15)
points.arrowDraftTo = new Point(-45, 15)
paths.arrowDraft = arrow('arrowDraft', 'draft()')
// Stacks
drawBox('Stack 0', -30, -33, 50, 169, 'b', 'mark fill-mark', 'fill-mark')
paths['Stack 0'].attr('fill-opacity', 0.2)
drawBox('Stack 1', 23, -33, 50, 169, 'b', 'mark fill-mark', 'fill-mark')
paths['Stack 1'].attr('fill-opacity', 0.2)
drawBox('Stack 2', 76, -33, 50, 169, 'b', 'mark fill-mark', 'fill-mark')
paths['Stack 2'].attr('fill-opacity', 0.2)
// Sets
drawBox('Set 0', -33, -30, 174, 76, 'r', 'contrast fill-contrast', 'fill-contrast')
paths['Set 0'].attr('fill-opacity', 0.2)
drawBox('Set 1', -33, 50, 174, 76, 'r', 'contrast fill-contrast', 'fill-contrast')
paths['Set 1'].attr('fill-opacity', 0.2)
// Parts set 0
drawBox('Part A (set 0)', -27, -27, 44, 70, 'b', '注意 fill-bg')
drawBox('points', -24, -24, 38, 12, true, '注意')
drawBox('paths', -24, -9, 38, 12, true, '注意')
drawBox('snippets', -24, 6, 38, 12, true, '注意')
drawBox('Part B (set 0)', 26, -27, 44, 70, 'b', '注意 fill-bg')
drawBox(' points ', 29, -24, 38, 12, true, '注意')
drawBox(' paths ', 29, -9, 38, 12, true, '注意')
drawBox(' snippets ', 29, 6, 38, 12, true, '注意')
drawBox('Part C (set 0)', 79, -27, 44, 70, 'b', '注意 fill-bg')
drawBox(' points ', 82, -24, 38, 12, true, '注意')
drawBox(' paths ', 82, -9, 38, 12, true, '注意')
drawBox(' snippets ', 82, 6, 38, 12, true, '注意')
drawBox('setStore 0', -24, 21, 144, 12, true, 'lining fill-lining dashed', 'fill-various')
paths['setStore 0'].attr('fill-opacity', 0.2)
// Parts set 1
drawBox('Part A (set 1)', -27, 53, 44, 70, 'b', '注意 fill-bg')
drawBox(' points', -24, 56, 38, 12, true, '注意')
drawBox(' paths', -24, 71, 38, 12, true, '注意')
drawBox(' snippets', -24, 86, 38, 12, true, '注意')
drawBox('Part B (set 1)', 26, 53, 44, 70, 'b', '注意 fill-bg')
drawBox(' points ', 29, 56, 38, 12, true, '注意')
drawBox(' paths ', 29, 71, 38, 12, true, '注意')
drawBox(' snippets ', 29, 86, 38, 12, true, '注意')
drawBox('Part C (set 1)', 79, 53, 44, 70, 'b', '注意 fill-bg')
drawBox(' points ', 82, 56, 38, 12, true, '注意')
drawBox(' paths ', 82, 71, 38, 12, true, '注意')
drawBox(' snippets ', 82, 86, 38, 12, true, '注意')
drawBox('setStore 1', -24, 101, 147, 12, true, 'lining fill-lining dashed', 'fill-various')
paths['setStore 1'].attr('fill-opacity', 0.2)
// Design
drawBox('Pattern Store', -30, -52, 155, 15, true, 'lining fill-lining')
paths['Pattern Store'].attr('fill-opacity', 0.2)
drawBox('Design', -43, -59, 195, 216, 'b', 'fabric stroke-lg', 'text-lg bold')
// Render
points.arrowSvgFrom = new Point(154,15)
points.arrowSvgTo = new Point(225, -15)
paths.arrowSvg = arrow('arrowSvg', 'render()')
points.arrowReactFrom = new Point(154,15)
points.arrowReactTo = new Point(225, 45)
paths.arrowReact = arrow('arrowReact', 'getRenderProps()')
drawBox('Render stage', 190, -52, 105, 175, 'b', 'interfacing lashed', 'text-lg bold')
// Render logos
points.svg = new Point(225, -15).addText('SVG', 'text-lg bold')
points.js = new Point(225, 45).addText("React\nSvelte\nVue\nJS\n...", 'text-lg')
// Prevent clipping
paths.unclip = new Path().move(new Point(260, -80))
return part
}
A schematic overview of what goes on inside a open clothes design
If it looks like a lot don't despair. I included all the info on the graphical overview, but most of it you can safely ignore and consider under the hood unless you want to do some really advanced things with open clothes.
If we look at our image through squinted eyes, we can identify three areas:
- The 设置 on the left
- The Render stage on the right
- The Pattern in the center
The left and right parts are all about how to integrate open clothes in your frontend. In other words, how you'll plug it into your website, or online store, or a mobile application.
We'll briefly cover those areas in this page. But a deep-dive into those topics is outside the scope of this guide.
The settings
On the left, we have the settings box. It represents the settings that are provided by the user.
Most of the settings are the same for all designs generated with open clothes as they are provided by the core library.
Noteworthy exceptions are the measurements
and options keys as they are defined by the pattern design.
In other words, different patterns will required different measurements and provide different options.
But all patterns will allow users to set the units (for example).
设置 are provided by passing them to the Pattern constructor
Multiset support
Since version 3 of open clothes, you can pass mul提示le sets of settings to Pattern.draft().
We call this multset support.
In 99% of the cases, you will only have a single set of settings, and you can mostly forget about this feature. It is typically used to compare different drafts or to draft for different sets of measurements (for example).
Rendering your pattern
By rendering the pattern, we mean to generate the output to show to the user.
This can be done in two different ways:
Render to SVG
The core library ships with a renderer that will render your pattern to SVG.
To use it, call Pattern.render() which will return an SVG string.
BYOR (bring your own renderer)
If you'd like more fine-grained control over the output, you can use your own renderer.
To do so, call Pattern.getRenderProps()
which will return all the data you need to render your pattern in the way you prefer.
This is what we use on these documentation pages to render with React
The pattern
Last but not least, we've arrived at the central item: the pattern itself. The pattern is a container that holds all your parts, along with a (pattern-wide) store.
In reality, your pattern is a that takes the user's settings as input and will return an instantiated pattern.
That pattern instance will have a draft() method which will do the actual work of
drafting the pattern. Once drafted, the pattern can be rendered.
While the pattern does a lot of heavy lifting behind the scenes, we can content ourselves by understanding its different building blocks: