Add Bust-aligned buttons option to Simone.
This commit is contained in:
parent
c2ae5ba5d6
commit
c36a151ced
13 changed files with 140 additions and 11 deletions
|
@ -54,6 +54,11 @@
|
||||||
|
|
||||||
### simone
|
### simone
|
||||||
|
|
||||||
|
#### Added
|
||||||
|
|
||||||
|
- Added Bust-aligned buttons option and functionality. Closes [#2154] (https://github.com/freesewing/freesewing/issues/2154)
|
||||||
|
- Added a notch at the center front bustline.
|
||||||
|
|
||||||
#### Fixed
|
#### Fixed
|
||||||
|
|
||||||
- Don't do a negative FBA from there's no need for an FBA Fixes [#2121](https://github.com/freesewing/freesewing/issues/2121)
|
- Don't do a negative FBA from there's no need for an FBA Fixes [#2121](https://github.com/freesewing/freesewing/issues/2121)
|
||||||
|
|
|
@ -8,6 +8,10 @@ Unreleased:
|
||||||
plugin-title:
|
plugin-title:
|
||||||
- Added support for removing the title via a macro call
|
- Added support for removing the title via a macro call
|
||||||
- Added a render timestamp to the title
|
- Added a render timestamp to the title
|
||||||
|
simone:
|
||||||
|
- Added Bust-aligned buttons option and functionality.
|
||||||
|
Closes [#2154] (https://github.com/freesewing/freesewing/issues/2154)
|
||||||
|
- Added a notch at the center front bustline.
|
||||||
|
|
||||||
Changed:
|
Changed:
|
||||||
charlie:
|
charlie:
|
||||||
|
|
|
@ -131,6 +131,10 @@ const config = {
|
||||||
extraTopButton: { bool: true },
|
extraTopButton: { bool: true },
|
||||||
seperateButtonPlacket: { bool: false },
|
seperateButtonPlacket: { bool: false },
|
||||||
seperateButtonholePlacket: { bool: false },
|
seperateButtonholePlacket: { bool: false },
|
||||||
|
// Not used in Simon but needed for Simone
|
||||||
|
bustAlignedButtons: {
|
||||||
|
dflt: 'Disabled',
|
||||||
|
list: ['Even spacing', 'Split spacing', 'Disabled'], },
|
||||||
|
|
||||||
// Collar
|
// Collar
|
||||||
collarEase: { pct: 2, min: 0, max: 10 },
|
collarEase: { pct: 2, min: 0, max: 10 },
|
||||||
|
|
|
@ -25,7 +25,7 @@ export default (part) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const id in paths) delete part.paths[id]
|
for (const id in paths) delete part.paths[id]
|
||||||
for (const i of ['waist', 'armholePitch', 'hips', 'armhole']) {
|
for (const i of ['waist', 'armholePitch', 'hips', 'armhole', 'bust',]) {
|
||||||
delete snippets[i + '-notch']
|
delete snippets[i + '-notch']
|
||||||
}
|
}
|
||||||
const width = store.get('buttonholePlacketWidth')
|
const width = store.get('buttonholePlacketWidth')
|
||||||
|
@ -98,6 +98,12 @@ export default (part) => {
|
||||||
// Notches
|
// Notches
|
||||||
snippets['cfArmhole-notch'].anchor.x = points.cfArmhole.x - fold * 2
|
snippets['cfArmhole-notch'].anchor.x = points.cfArmhole.x - fold * 2
|
||||||
snippets['cfWaist-notch'].anchor.x = points.cfArmhole.x - fold * 2
|
snippets['cfWaist-notch'].anchor.x = points.cfArmhole.x - fold * 2
|
||||||
|
// Not available in Simon
|
||||||
|
if (typeof snippets['cfBust-notch'] !== 'undefined')
|
||||||
|
snippets['cfBust-notch'].anchor.x = points.cfArmhole - fold * 2
|
||||||
|
// Not available in Simon
|
||||||
|
if (typeof snippets['cfHem-notch'] !== 'undefined')
|
||||||
|
snippets['cfHem-notch'].anchor.x = points.cfArmhole.x - fold * 2
|
||||||
// This notch is not available in Simone
|
// This notch is not available in Simone
|
||||||
if (typeof snippets['cfHips-notch'] !== 'undefined')
|
if (typeof snippets['cfHips-notch'] !== 'undefined')
|
||||||
snippets['cfHips-notch'].anchor.x = points.cfArmhole.x - fold * 2
|
snippets['cfHips-notch'].anchor.x = points.cfArmhole.x - fold * 2
|
||||||
|
|
|
@ -27,8 +27,10 @@ export default (part) => {
|
||||||
for (const id in paths) {
|
for (const id in paths) {
|
||||||
if (id !== 'seam') delete part.paths[id]
|
if (id !== 'seam') delete part.paths[id]
|
||||||
}
|
}
|
||||||
for (const i in snippets) {
|
let notchesToKeep = ['cfBust-notch', 'cfArmhole-notch', 'cfWaist-notch',
|
||||||
if (i.indexOf('notch')) delete snippets[i]
|
'cfHem-notch']
|
||||||
|
for (const id in snippets) {
|
||||||
|
if (!notchesToKeep.includes(id)) delete snippets[id]
|
||||||
}
|
}
|
||||||
macro('flip')
|
macro('flip')
|
||||||
const width = store.get('buttonPlacketWidth')
|
const width = store.get('buttonPlacketWidth')
|
||||||
|
|
|
@ -22,6 +22,8 @@ export default (part) => {
|
||||||
points.placketBottomOuterEdgeUnder = points.placketCfHem.shift(180, width / 2 + fold)
|
points.placketBottomOuterEdgeUnder = points.placketCfHem.shift(180, width / 2 + fold)
|
||||||
points.placketTopEdge = points.placketTopOuterEdgeFold.shift(180, width)
|
points.placketTopEdge = points.placketTopOuterEdgeFold.shift(180, width)
|
||||||
points.placketBottomEdge = points.placketBottomOuterEdgeFold.shift(180, width)
|
points.placketBottomEdge = points.placketBottomOuterEdgeFold.shift(180, width)
|
||||||
|
if (typeof points.cfBust !== 'undefined')
|
||||||
|
points.cfBust = points.cfBust.shift(180, fold * 2)
|
||||||
|
|
||||||
paths.seam.line(points.placketTopEdge).line(points.placketBottomEdge).close()
|
paths.seam.line(points.placketTopEdge).line(points.placketBottomEdge).close()
|
||||||
|
|
||||||
|
@ -63,6 +65,8 @@ export default (part) => {
|
||||||
points.placketEdgeWaist = new Point(points.placketBottomEdge.x, points.waist.y)
|
points.placketEdgeWaist = new Point(points.placketBottomEdge.x, points.waist.y)
|
||||||
points.placketEdgeArmhole = new Point(points.placketBottomEdge.x, points.armhole.y)
|
points.placketEdgeArmhole = new Point(points.placketBottomEdge.x, points.armhole.y)
|
||||||
points.placketEdgeHips = new Point(points.placketBottomEdge.x, points.hips.y)
|
points.placketEdgeHips = new Point(points.placketBottomEdge.x, points.hips.y)
|
||||||
|
// Delete old cfBust location notch, so we can re-add in new location.
|
||||||
|
delete snippets['cfBust-notch']
|
||||||
macro('sprinkle', {
|
macro('sprinkle', {
|
||||||
snippet: 'notch',
|
snippet: 'notch',
|
||||||
on: [
|
on: [
|
||||||
|
@ -83,6 +87,7 @@ export default (part) => {
|
||||||
'placketBottomOuterEdgeFold',
|
'placketBottomOuterEdgeFold',
|
||||||
'placketBottomOuterEdgeOver',
|
'placketBottomOuterEdgeOver',
|
||||||
'placketBottomOuterEdgeUnder',
|
'placketBottomOuterEdgeUnder',
|
||||||
|
'cfBust'
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
delete snippets['cfWaist-notch']
|
delete snippets['cfWaist-notch']
|
||||||
|
|
|
@ -25,6 +25,7 @@ export default (part) => {
|
||||||
delete snippets['cfWaist-notch']
|
delete snippets['cfWaist-notch']
|
||||||
delete snippets['cfHips-notch']
|
delete snippets['cfHips-notch']
|
||||||
delete snippets['cfArmhole-notch']
|
delete snippets['cfArmhole-notch']
|
||||||
|
delete snippets['cfBust-notch']
|
||||||
points.edgeArmhole = new Point(points.neckEdge.x, points.armhole.y)
|
points.edgeArmhole = new Point(points.neckEdge.x, points.armhole.y)
|
||||||
points.edgeWaist = new Point(points.neckEdge.x, points.waist.y)
|
points.edgeWaist = new Point(points.neckEdge.x, points.waist.y)
|
||||||
points.edgeHips = new Point(points.neckEdge.x, points.hips.y)
|
points.edgeHips = new Point(points.neckEdge.x, points.hips.y)
|
||||||
|
|
|
@ -23,6 +23,7 @@ export default (part) => {
|
||||||
delete snippets['cfWaist-notch']
|
delete snippets['cfWaist-notch']
|
||||||
delete snippets['cfHips-notch']
|
delete snippets['cfHips-notch']
|
||||||
delete snippets['cfArmhole-notch']
|
delete snippets['cfArmhole-notch']
|
||||||
|
delete snippets['cfBust-notch']
|
||||||
points.edgeArmhole = new Point(points.placketTopIn.x, points.armhole.y)
|
points.edgeArmhole = new Point(points.placketTopIn.x, points.armhole.y)
|
||||||
points.edgeWaist = new Point(points.placketTopIn.x, points.waist.y)
|
points.edgeWaist = new Point(points.placketTopIn.x, points.waist.y)
|
||||||
points.edgeHips = new Point(points.placketTopIn.x, points.hips.y)
|
points.edgeHips = new Point(points.placketTopIn.x, points.hips.y)
|
||||||
|
|
|
@ -27,15 +27,82 @@ export const calculateReduction = function (part) {
|
||||||
|
|
||||||
export const addButtons = function (part, origin = 'cfNeck', snippet = 'button') {
|
export const addButtons = function (part, origin = 'cfNeck', snippet = 'button') {
|
||||||
const { points, options, snippets, Snippet } = part.shorthand()
|
const { points, options, snippets, Snippet } = part.shorthand()
|
||||||
const len = points.cfNeck.dist(points.cfHips) * (1 - options.buttonFreeLength)
|
const full_len = points.cfNeck.dist(points.cfHips)
|
||||||
for (let i = 1; i <= options.buttons; i++) {
|
const adjusted_len = full_len * (1 - options.buttonFreeLength)
|
||||||
points['button' + i] = points[origin].shift(-90, (len / options.buttons) * i)
|
const total_buttons = options.buttons
|
||||||
snippets[snippet + i] = new Snippet(snippet, points['button' + i])
|
|
||||||
|
switch (options.bustAlignedButtons) {
|
||||||
|
case 'Even spacing': {
|
||||||
|
// Strategy: Even button spacing,
|
||||||
|
// - Determine the correct spacing above the bustline and use that
|
||||||
|
// spacing for all buttons.
|
||||||
|
// - The bottom button position is variable, and it ignores the "Button
|
||||||
|
// free length" setting.
|
||||||
|
const top_len = points.cfNeck.dist(points.cfBust)
|
||||||
|
const top_percentage = top_len / full_len
|
||||||
|
const top_number_buttons = Math.round(total_buttons * top_percentage)
|
||||||
|
const top_spacing = top_len / top_number_buttons
|
||||||
|
const even_spacing = top_spacing
|
||||||
|
for (let i = 1; i <= total_buttons; i++) {
|
||||||
|
points['button' + i] = points[origin].shift(-90, (even_spacing * i))
|
||||||
|
snippets[snippet + i] = new Snippet(snippet, points['button' + i])
|
||||||
|
}
|
||||||
|
break }
|
||||||
|
case 'Split spacing': {
|
||||||
|
// Strategy: Different spacings above and below.
|
||||||
|
// - Calculate the number of buttons that should be above and below
|
||||||
|
// the bustline by proportion.
|
||||||
|
// - Calculate the correct spacings to be used above and below the
|
||||||
|
// bustline, adhering to the "Button free length" setting.
|
||||||
|
// - For the first and last bottom buttons, slightly shift their
|
||||||
|
// positions to make the difference in spacings less noticeable
|
||||||
|
// at the bustline.
|
||||||
|
const top_len = points.cfNeck.dist(points.cfBust)
|
||||||
|
const bot_len = adjusted_len - top_len
|
||||||
|
const top_percentage = top_len / adjusted_len
|
||||||
|
const top_number_buttons = Math.round(total_buttons * top_percentage)
|
||||||
|
const bot_number_buttons = total_buttons - top_number_buttons
|
||||||
|
const top_spacing = top_len / top_number_buttons
|
||||||
|
const bot_spacing = bot_len / bot_number_buttons
|
||||||
|
// Top buttons
|
||||||
|
for (let i = 1; i <= top_number_buttons; i++) {
|
||||||
|
points['button' + i] = points[origin].shift(-90, top_spacing * i)
|
||||||
|
snippets[snippet + i] = new Snippet(snippet, points['button' + i])
|
||||||
|
}
|
||||||
|
// Bottom buttons
|
||||||
|
const adjustment = (top_spacing - bot_spacing) / 2
|
||||||
|
points.currentpoint = points['cfBust'].clone()
|
||||||
|
for (let i = top_number_buttons + 1; i <= total_buttons; i++) {
|
||||||
|
points.currentpoint = points.currentpoint.shift(-90, bot_spacing)
|
||||||
|
if (i == top_number_buttons + 1) {
|
||||||
|
// Adjust first button position
|
||||||
|
points.currentpoint = points.currentpoint.shift(-90, adjustment)
|
||||||
|
} else if (i == total_buttons) {
|
||||||
|
// Adjust last button position in opposite direction.
|
||||||
|
points.currentpoint = points.currentpoint.shift(90, adjustment)
|
||||||
|
}
|
||||||
|
points['button' + i] = points.currentpoint.clone()
|
||||||
|
snippets[snippet + i] = new Snippet(snippet, points['button' + i])
|
||||||
|
}
|
||||||
|
break }
|
||||||
|
case 'Disabled':
|
||||||
|
default: {
|
||||||
|
// Strategy: The default strategy.
|
||||||
|
// - Buttons are evenly spaced without regard to the bustline.
|
||||||
|
// - The "Button free length" setting is obeyed.
|
||||||
|
const default_spacing = adjusted_len / total_buttons
|
||||||
|
for (let i = 1; i <= total_buttons; i++) {
|
||||||
|
points['button' + i] = points[origin].shift(-90, default_spacing * i)
|
||||||
|
snippets[snippet + i] = new Snippet(snippet, points['button' + i])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add optional extra top button
|
||||||
if (options.extraTopButton)
|
if (options.extraTopButton)
|
||||||
snippets['top' + snippet] = new Snippet(
|
snippets['top' + snippet] = new Snippet(
|
||||||
snippet,
|
snippet,
|
||||||
points[origin].shift(-90, len / options.buttons / 2)
|
points[origin].shift(-90, adjusted_len / total_buttons / 2)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,12 @@ const config = {
|
||||||
name: 'simone',
|
name: 'simone',
|
||||||
optionGroups: {
|
optionGroups: {
|
||||||
...simonConfig.optionGroups,
|
...simonConfig.optionGroups,
|
||||||
style: [...simonConfig.optionGroups.style, 'frontDarts', 'contour'],
|
style: [
|
||||||
|
...simonConfig.optionGroups.style,
|
||||||
|
'frontDarts',
|
||||||
|
'contour',
|
||||||
|
'bustAlignedButtons',
|
||||||
|
],
|
||||||
advanced: [
|
advanced: [
|
||||||
...simonConfig.optionGroups.advanced,
|
...simonConfig.optionGroups.advanced,
|
||||||
'bustDartAngle',
|
'bustDartAngle',
|
||||||
|
@ -38,6 +43,9 @@ const config = {
|
||||||
frontDarts: { bool: false },
|
frontDarts: { bool: false },
|
||||||
frontDartLength: { pct: 45, min: 30, max: 60 },
|
frontDartLength: { pct: 45, min: 30, max: 60 },
|
||||||
contour: { pct: 50, min: 30, max: 75 },
|
contour: { pct: 50, min: 30, max: 75 },
|
||||||
|
bustAlignedButtons: {
|
||||||
|
dflt: 'Disabled',
|
||||||
|
list: ['Even spacing', 'Split spacing', 'Disabled'], },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,11 @@ export default (part) => {
|
||||||
* it's based on the model's measurements. (bust span and high point shoulder (HPS) to bust).
|
* it's based on the model's measurements. (bust span and high point shoulder (HPS) to bust).
|
||||||
* So we need to find the bust point that would end up in the right place AFTER we do the FBA
|
* So we need to find the bust point that would end up in the right place AFTER we do the FBA
|
||||||
* For this, we'll just rotate it FBARot in the other direction
|
* For this, we'll just rotate it FBARot in the other direction
|
||||||
|
* In other words, we are pre-rotating points.bust now, so it gets rotated
|
||||||
|
* back to its original position during the FBA procedure.
|
||||||
|
* For convenience and clarity, we're defining points.realBustPoint here.
|
||||||
|
* However, points.bust will eventually be identical to points.realBustPoint
|
||||||
|
* after the FBA procedure.
|
||||||
*/
|
*/
|
||||||
points.realBustPoint = points.bust.clone()
|
points.realBustPoint = points.bust.clone()
|
||||||
points.bust = points.bust.rotate(FBARot * -1, points.armholePitch)
|
points.bust = points.bust.rotate(FBARot * -1, points.armholePitch)
|
||||||
|
@ -276,7 +281,7 @@ export default (part) => {
|
||||||
'armholePitchCp1',
|
'armholePitchCp1',
|
||||||
]
|
]
|
||||||
for (let p of clone1) points[p] = points[`${p}_rot1`].clone()
|
for (let p of clone1) points[p] = points[`${p}_rot1`].clone()
|
||||||
let clone2 = ['hem', 'hips', 'hipsCp2', 'waistCp1', 'waist']
|
let clone2 = ['hem', 'hips', 'hipsCp2', 'waistCp1', 'waist', 'bust']
|
||||||
for (let p of clone2) points[p] = points[`${p}_rot2`].clone()
|
for (let p of clone2) points[p] = points[`${p}_rot2`].clone()
|
||||||
points.cfHem = new Point(points.cfHem.x, points.bustHem_rot2.y)
|
points.cfHem = new Point(points.cfHem.x, points.bustHem_rot2.y)
|
||||||
points.waistCp2 = points.belowDartCpBottom_rot2.clone()
|
points.waistCp2 = points.belowDartCpBottom_rot2.clone()
|
||||||
|
@ -287,6 +292,7 @@ export default (part) => {
|
||||||
points.cfArmhole = new Point(0, points.armhole.y)
|
points.cfArmhole = new Point(0, points.armhole.y)
|
||||||
points.cfWaist = new Point(0, points.waist.y)
|
points.cfWaist = new Point(0, points.waist.y)
|
||||||
points.cfHips = new Point(0, points.hips.y)
|
points.cfHips = new Point(0, points.hips.y)
|
||||||
|
points.cfBust = new Point(0, points.bust.y)
|
||||||
|
|
||||||
//
|
//
|
||||||
// Smooth out the armhole to avoid a kink where we rotated
|
// Smooth out the armhole to avoid a kink where we rotated
|
||||||
|
@ -311,7 +317,8 @@ export default (part) => {
|
||||||
for (let s in snippets) delete snippets[s]
|
for (let s in snippets) delete snippets[s]
|
||||||
macro('sprinkle', {
|
macro('sprinkle', {
|
||||||
snippet: 'notch',
|
snippet: 'notch',
|
||||||
on: ['armhole', 'armholePitch', 'cfArmhole', 'cfWaist', 'cfHem', 'hips', 'waist', 'bust_rot2'],
|
on: ['armhole', 'armholePitch', 'cfArmhole', 'cfWaist', 'cfHem', 'hips',
|
||||||
|
'waist', 'bust', 'cfBust',],
|
||||||
})
|
})
|
||||||
points.logo = new Point(points.armhole.x / 2, points.armhole.y)
|
points.logo = new Point(points.armhole.x / 2, points.armhole.y)
|
||||||
snippets.logo = new Snippet('logo', points.logo)
|
snippets.logo = new Snippet('logo', points.logo)
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
title: "Bust-aligned buttons"
|
||||||
|
---
|
||||||
|
|
||||||
|
Select an optional bust-aligned button spacing strategy.
|
||||||
|
|
||||||
|
- Even spacing
|
||||||
|
- Split spacing
|
||||||
|
- Disabled
|
||||||
|
|
||||||
|
By default bust-aligned button spacing is "Disabled", and center front buttons are spaced without regard to the bustline. Choosing "Even spacing" or "Split spacing" will ensure that there is a button positioned on the bustline to prevent possible gaping.
|
||||||
|
|
||||||
|
- Even spacing: Button spacing is calculated for buttons above the bustline, and this spacing is used for all buttons. The `Button free length` setting is ignored for this option. This option might cause the bottommost button to be positioned in a non-optimal location. If this occurs, you may want to experiment with adding or subtracting a button to see if it produces a better design.
|
||||||
|
- Split spacing: Different button spacings are calculated and used for buttons above and below the bustline. Buttons above the bustline are spaced evenly. Buttons below the bustline are also spaced evenly, except for the topmost and bottommost buttons. The spacings for those buttons are shifted slightly to make the transition between the top and bottom spacings less noticeable. The `Button free length` setting is obeyed for this option. This option might cause non-optimal, visibly different spacings above and below the bustline. If this occurs, you may want to experiment with adding or subtracting a button to see if it produces a better design.
|
||||||
|
- Disabled: Even button spacing is calculated and used, without regard to the bustline. The `Button free length` setting is obeyed for this option.
|
|
@ -1,3 +1,7 @@
|
||||||
|
bustAlignedButtons:
|
||||||
|
title: Bust-aligned buttons
|
||||||
|
description: Optional button spacing strategies to ensure a button at the bustline
|
||||||
|
|
||||||
bustDartAngle:
|
bustDartAngle:
|
||||||
title: Bust dart angle
|
title: Bust dart angle
|
||||||
description: Controls the angle by which the (side) bust dart slopes downward
|
description: Controls the angle by which the (side) bust dart slopes downward
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue