2021-10-17 18:26:00 +02:00
|
|
|
---
|
2021-08-25 16:09:31 +02:00
|
|
|
title: Avoiding overlap
|
2023-09-30 14:04:18 +02:00
|
|
|
order: 92
|
2021-10-17 18:26:00 +02:00
|
|
|
---
|
2021-08-25 16:09:31 +02:00
|
|
|
|
2023-08-08 13:20:46 -05:00
|
|
|
While we've only drawn the end of one strap, it's pretty obvious they overlap,
|
|
|
|
which makes it impossible to cut out, so we're going to have to address
|
2022-10-11 01:37:09 +02:00
|
|
|
that.
|
2021-08-25 16:09:31 +02:00
|
|
|
|
|
|
|
Specifically, we're going to rotate our strap out of the way until it no longer overlaps.
|
2023-01-06 19:29:29 -08:00
|
|
|
The rest of our bib should stay as it is, so let's start by making a list of points we need
|
2022-10-11 01:37:09 +02:00
|
|
|
to rotate.
|
2021-08-25 16:09:31 +02:00
|
|
|
|
2023-09-30 14:04:18 +02:00
|
|
|
However, there is a catch.
|
|
|
|
|
2024-01-21 11:16:40 -08:00
|
|
|
## Macros and auto-generated IDs
|
2023-09-30 14:04:18 +02:00
|
|
|
We have used the `round` macro to help us round the corners
|
|
|
|
of our strap, and it added a bunch of auto-generated points to our pattern. We need to
|
|
|
|
rotate these points too, but what are their names?
|
|
|
|
|
|
|
|
A macro will return the names of the things it created. So far, we have not captured
|
|
|
|
that return value, but if we did, it would look like this:
|
|
|
|
|
|
|
|
```mjs
|
|
|
|
{
|
|
|
|
"tipRightTop": {
|
|
|
|
"points": {
|
|
|
|
"start": "__macro_@freesewing/plugin-round_tipRightTop_start",
|
|
|
|
"cp1": "__macro_@freesewing/plugin-round_tipRightTop_cp1",
|
|
|
|
"cp2": "__macro_@freesewing/plugin-round_tipRightTop_cp2",
|
|
|
|
"end": "__macro_@freesewing/plugin-round_tipRightTop_end"
|
|
|
|
},
|
|
|
|
"paths": {
|
|
|
|
"path": "__macro_@freesewing/plugin-round_tipRightTop_path"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"tipRightBottom": {
|
|
|
|
"points": {
|
|
|
|
"start": "__macro_@freesewing/plugin-round_tipRightBottom_start",
|
|
|
|
"cp1": "__macro_@freesewing/plugin-round_tipRightBottom_cp1",
|
|
|
|
"cp2": "__macro_@freesewing/plugin-round_tipRightBottom_cp2",
|
|
|
|
"end": "__macro_@freesewing/plugin-round_tipRightBottom_end"
|
|
|
|
},
|
|
|
|
"paths": {
|
|
|
|
"path": "__macro_@freesewing/plugin-round_tipRightBottom_path"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Those names aren't very handy to remember. So I will rewrite this code a bit to
|
|
|
|
we'll capture these return values from the `round` macros and create
|
|
|
|
easy-to-remember points from them:
|
|
|
|
|
2024-03-14 01:40:21 +00:00
|
|
|
<Example tutorial caption="It looks the same as before, but now those macro points are accessible to us">
|
2024-03-16 21:06:19 +00:00
|
|
|
```design/src/bib.mjs
|
2023-09-30 14:04:18 +02:00
|
|
|
function draftBib({
|
|
|
|
Path,
|
|
|
|
Point,
|
|
|
|
paths,
|
|
|
|
points,
|
|
|
|
measurements,
|
|
|
|
options,
|
2024-03-16 21:06:19 +00:00
|
|
|
// highlight-start
|
2023-09-30 14:04:18 +02:00
|
|
|
utils,
|
2024-03-16 21:06:19 +00:00
|
|
|
// highlight-end
|
2023-09-30 14:04:18 +02:00
|
|
|
macro,
|
|
|
|
part,
|
|
|
|
}) {
|
|
|
|
|
2024-03-14 01:40:21 +00:00
|
|
|
/*
|
|
|
|
* Construct the quarter neck opening
|
|
|
|
*/
|
2023-09-30 14:04:18 +02:00
|
|
|
let tweak = 1
|
|
|
|
let target = (measurements.head * options.neckRatio) /4
|
|
|
|
let delta
|
|
|
|
do {
|
2024-03-14 01:40:21 +00:00
|
|
|
points.right = new Point(
|
|
|
|
tweak * measurements.head / 10,
|
|
|
|
0
|
|
|
|
)
|
|
|
|
points.bottom = new Point(
|
|
|
|
0,
|
|
|
|
tweak * measurements.head / 12
|
|
|
|
)
|
|
|
|
|
|
|
|
points.rightCp1 = points.right.shift(
|
|
|
|
90,
|
|
|
|
points.bottom.dy(points.right) / 2
|
|
|
|
)
|
|
|
|
points.bottomCp2 = points.bottom.shift(
|
|
|
|
0,
|
|
|
|
points.bottom.dx(points.right) / 2
|
|
|
|
)
|
|
|
|
|
2023-09-30 14:04:18 +02:00
|
|
|
paths.quarterNeck = new Path()
|
|
|
|
.move(points.right)
|
2024-03-14 01:40:21 +00:00
|
|
|
.curve(
|
|
|
|
points.rightCp1,
|
|
|
|
points.bottomCp2,
|
|
|
|
points.bottom
|
|
|
|
)
|
|
|
|
.hide()
|
2023-09-30 14:04:18 +02:00
|
|
|
|
|
|
|
delta = paths.quarterNeck.length() - target
|
|
|
|
if (delta > 0) tweak = tweak * 0.99
|
|
|
|
else tweak = tweak * 1.02
|
|
|
|
} while (Math.abs(delta) > 1)
|
|
|
|
|
2024-03-14 01:40:21 +00:00
|
|
|
/*
|
|
|
|
* Construct the complete neck opening
|
|
|
|
*/
|
2023-09-30 14:04:18 +02:00
|
|
|
points.rightCp2 = points.rightCp1.flipY()
|
|
|
|
points.bottomCp1 = points.bottomCp2.flipX()
|
|
|
|
points.left = points.right.flipX()
|
|
|
|
points.leftCp1 = points.rightCp2.flipX()
|
|
|
|
points.leftCp2 = points.rightCp1.flipX()
|
|
|
|
points.top = points.bottom.flipY()
|
|
|
|
points.topCp1 = points.bottomCp2.flipY()
|
|
|
|
points.topCp2 = points.bottomCp1.flipY()
|
|
|
|
|
|
|
|
paths.neck = new Path()
|
|
|
|
.move(points.top)
|
|
|
|
.curve(points.topCp2, points.leftCp1, points.left)
|
|
|
|
.curve(points.leftCp2, points.bottomCp1, points.bottom)
|
|
|
|
.curve(points.bottomCp2, points.rightCp1, points.right)
|
|
|
|
.curve(points.rightCp2, points.topCp1, points.top)
|
|
|
|
.close()
|
|
|
|
.addClass('fabric')
|
|
|
|
|
2024-03-16 21:06:19 +00:00
|
|
|
/*
|
|
|
|
* Drawing the bib outline
|
|
|
|
*/
|
2023-09-30 14:04:18 +02:00
|
|
|
const width = measurements.head * options.widthRatio
|
|
|
|
const length = measurements.head * options.lengthRatio
|
|
|
|
|
|
|
|
points.topLeft = new Point(
|
|
|
|
width / -2,
|
|
|
|
points.top.y - (width / 2 - points.right.x)
|
|
|
|
)
|
|
|
|
points.topRight = points.topLeft.shift(0, width)
|
|
|
|
points.bottomLeft = points.topLeft.shift(-90, length)
|
|
|
|
points.bottomRight = points.topRight.shift(-90, length)
|
|
|
|
|
2024-03-14 01:40:21 +00:00
|
|
|
/*
|
|
|
|
* Shape the straps
|
|
|
|
*/
|
2023-09-30 14:04:18 +02:00
|
|
|
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
|
|
|
|
points.edgeRight = new Point(points.topRight.x, points.right.y)
|
|
|
|
points.edgeTop = new Point(0, points.topLeft.y)
|
|
|
|
|
|
|
|
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
|
|
|
|
points.edgeRightCp = points.edgeLeftCp.flipX()
|
|
|
|
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(
|
|
|
|
points.topLeft,
|
|
|
|
0.5
|
|
|
|
)
|
|
|
|
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
|
|
|
|
|
|
|
|
// Round the straps
|
|
|
|
const strap = points.edgeTop.dy(points.top)
|
|
|
|
|
|
|
|
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
|
|
|
|
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
|
|
|
|
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
|
|
|
|
|
2024-03-16 21:06:19 +00:00
|
|
|
// highlight-start
|
2023-09-30 14:04:18 +02:00
|
|
|
/*
|
|
|
|
* Macros will return the auto-generated IDs
|
|
|
|
*/
|
|
|
|
const ids1 = {
|
|
|
|
tipRightTop: macro("round", {
|
|
|
|
id: "tipRightTop",
|
|
|
|
from: points.edgeTop,
|
|
|
|
to: points.tipRight,
|
|
|
|
via: points.tipRightTop,
|
|
|
|
hide: false
|
|
|
|
}),
|
|
|
|
tipRightBottom: macro("round", {
|
|
|
|
id: "tipRightBottom",
|
|
|
|
from: points.tipRight,
|
|
|
|
to: points.top,
|
|
|
|
via: points.tipRightBottom,
|
|
|
|
hide: false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create points from them with easy names
|
|
|
|
*/
|
|
|
|
for (const side in ids1) {
|
|
|
|
for (const id of ['start', 'cp1', 'cp2', 'end']) {
|
|
|
|
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
|
|
|
|
}
|
|
|
|
}
|
2024-03-16 21:06:19 +00:00
|
|
|
// highlight-end
|
2024-03-20 00:49:55 +00:00
|
|
|
|
2024-03-14 01:40:21 +00:00
|
|
|
/*
|
|
|
|
* Now, adapt our `rect` path so it's no longer a rectangle:
|
|
|
|
*/
|
2023-09-30 14:04:18 +02:00
|
|
|
paths.rect = new Path()
|
|
|
|
.move(points.edgeTop)
|
|
|
|
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
|
|
|
|
.line(points.bottomLeft)
|
|
|
|
.line(points.bottomRight)
|
|
|
|
.line(points.edgeRight)
|
|
|
|
.curve(points.edgeRightCp, points.edgeTopRightCp, points.edgeTop)
|
|
|
|
.close()
|
|
|
|
|
|
|
|
return part
|
|
|
|
}
|
|
|
|
```
|
|
|
|
</Example>
|
|
|
|
|
2022-10-11 01:37:09 +02:00
|
|
|
Once we have our list of points to rotate, we can rotate them. How far? Until the strap no longer overlaps.
|
|
|
|
|
|
|
|
<Example tutorial caption="It is looking pretty wonky now, but we'll deal with that next">
|
2024-03-20 00:49:55 +00:00
|
|
|
```design/src/bib.mjs
|
2023-01-06 19:29:29 -08:00
|
|
|
function draftBib({
|
|
|
|
Path,
|
|
|
|
Point,
|
|
|
|
paths,
|
|
|
|
points,
|
2022-10-11 01:37:09 +02:00
|
|
|
measurements,
|
|
|
|
options,
|
2023-09-30 14:04:18 +02:00
|
|
|
utils,
|
2022-10-11 01:37:09 +02:00
|
|
|
macro,
|
|
|
|
part,
|
|
|
|
}) {
|
2021-08-25 16:09:31 +02:00
|
|
|
|
2024-03-14 01:40:21 +00:00
|
|
|
/*
|
|
|
|
* Construct the quarter neck opening
|
|
|
|
*/
|
2022-10-11 01:37:09 +02:00
|
|
|
let tweak = 1
|
|
|
|
let target = (measurements.head * options.neckRatio) /4
|
|
|
|
let delta
|
|
|
|
do {
|
2024-03-14 01:40:21 +00:00
|
|
|
points.right = new Point(
|
|
|
|
tweak * measurements.head / 10,
|
|
|
|
0
|
|
|
|
)
|
|
|
|
points.bottom = new Point(
|
|
|
|
0,
|
|
|
|
tweak * measurements.head / 12
|
|
|
|
)
|
|
|
|
|
|
|
|
points.rightCp1 = points.right.shift(
|
|
|
|
90,
|
|
|
|
points.bottom.dy(points.right) / 2
|
|
|
|
)
|
|
|
|
points.bottomCp2 = points.bottom.shift(
|
|
|
|
0,
|
|
|
|
points.bottom.dx(points.right) / 2
|
|
|
|
)
|
|
|
|
|
2023-08-08 13:20:46 -05:00
|
|
|
paths.quarterNeck = new Path()
|
|
|
|
.move(points.right)
|
2024-03-14 01:40:21 +00:00
|
|
|
.curve(
|
|
|
|
points.rightCp1,
|
|
|
|
points.bottomCp2,
|
|
|
|
points.bottom
|
|
|
|
)
|
|
|
|
.hide()
|
|
|
|
|
|
|
|
delta = paths.quarterNeck.length() - target
|
|
|
|
if (delta > 0) tweak = tweak * 0.99
|
|
|
|
else tweak = tweak * 1.02
|
|
|
|
} while (Math.abs(delta) > 1)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct the complete neck opening
|
|
|
|
*/
|
2022-10-11 01:37:09 +02:00
|
|
|
points.rightCp2 = points.rightCp1.flipY()
|
|
|
|
points.bottomCp1 = points.bottomCp2.flipX()
|
|
|
|
points.left = points.right.flipX()
|
|
|
|
points.leftCp1 = points.rightCp2.flipX()
|
|
|
|
points.leftCp2 = points.rightCp1.flipX()
|
|
|
|
points.top = points.bottom.flipY()
|
|
|
|
points.topCp1 = points.bottomCp2.flipY()
|
|
|
|
points.topCp2 = points.bottomCp1.flipY()
|
2021-08-25 16:09:31 +02:00
|
|
|
|
2022-10-11 01:37:09 +02:00
|
|
|
paths.neck = new Path()
|
|
|
|
.move(points.top)
|
|
|
|
.curve(points.topCp2, points.leftCp1, points.left)
|
|
|
|
.curve(points.leftCp2, points.bottomCp1, points.bottom)
|
|
|
|
.curve(points.bottomCp2, points.rightCp1, points.right)
|
|
|
|
.curve(points.rightCp2, points.topCp1, points.top)
|
|
|
|
.close()
|
|
|
|
.addClass('fabric')
|
2021-08-25 16:09:31 +02:00
|
|
|
|
2023-01-06 19:29:29 -08:00
|
|
|
// Drawing the bib outline
|
2022-10-11 01:37:09 +02:00
|
|
|
const width = measurements.head * options.widthRatio
|
|
|
|
const length = measurements.head * options.lengthRatio
|
2023-01-06 19:29:29 -08:00
|
|
|
|
2022-10-11 01:37:09 +02:00
|
|
|
points.topLeft = new Point(
|
|
|
|
width / -2,
|
|
|
|
points.top.y - (width / 2 - points.right.x)
|
|
|
|
)
|
|
|
|
points.topRight = points.topLeft.shift(0, width)
|
|
|
|
points.bottomLeft = points.topLeft.shift(-90, length)
|
|
|
|
points.bottomRight = points.topRight.shift(-90, length)
|
2023-01-06 19:29:29 -08:00
|
|
|
|
2024-03-14 01:40:21 +00:00
|
|
|
/*
|
|
|
|
* Shape the straps
|
|
|
|
*/
|
2022-10-11 01:37:09 +02:00
|
|
|
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
|
|
|
|
points.edgeRight = new Point(points.topRight.x, points.right.y)
|
|
|
|
points.edgeTop = new Point(0, points.topLeft.y)
|
2023-01-06 19:29:29 -08:00
|
|
|
|
2022-10-11 01:37:09 +02:00
|
|
|
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
|
|
|
|
points.edgeRightCp = points.edgeLeftCp.flipX()
|
|
|
|
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(
|
|
|
|
points.topLeft,
|
|
|
|
0.5
|
|
|
|
)
|
|
|
|
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
|
2023-01-06 19:29:29 -08:00
|
|
|
|
2022-10-11 01:37:09 +02:00
|
|
|
// Round the straps
|
|
|
|
const strap = points.edgeTop.dy(points.top)
|
2023-01-06 19:29:29 -08:00
|
|
|
|
2022-10-11 01:37:09 +02:00
|
|
|
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
|
|
|
|
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
|
|
|
|
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
|
2023-01-06 19:29:29 -08:00
|
|
|
|
2023-09-30 14:04:18 +02:00
|
|
|
/*
|
|
|
|
* Macros will return the auto-generated IDs
|
|
|
|
*/
|
|
|
|
const ids1 = {
|
|
|
|
tipRightTop: macro("round", {
|
|
|
|
id: "tipRightTop",
|
|
|
|
from: points.edgeTop,
|
|
|
|
to: points.tipRight,
|
|
|
|
via: points.tipRightTop,
|
|
|
|
hide: false
|
|
|
|
}),
|
|
|
|
tipRightBottom: macro("round", {
|
|
|
|
id: "tipRightBottom",
|
|
|
|
from: points.tipRight,
|
|
|
|
to: points.top,
|
|
|
|
via: points.tipRightBottom,
|
|
|
|
hide: false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create points from them with easy names
|
|
|
|
*/
|
|
|
|
for (const side in ids1) {
|
|
|
|
for (const id of ['start', 'cp1', 'cp2', 'end']) {
|
|
|
|
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
|
|
|
|
}
|
|
|
|
}
|
2021-08-25 16:09:31 +02:00
|
|
|
|
2024-03-20 00:49:55 +00:00
|
|
|
// highlight-start
|
2022-10-11 01:37:09 +02:00
|
|
|
/*
|
|
|
|
* This is the list of points we need to rotate
|
|
|
|
* to move our strap out of the way
|
|
|
|
*/
|
|
|
|
const rotateThese = [
|
|
|
|
"edgeTopLeftCp",
|
|
|
|
"edgeTop",
|
|
|
|
"tipRight",
|
|
|
|
"tipRightTop",
|
|
|
|
"tipRightTopStart",
|
|
|
|
"tipRightTopCp1",
|
|
|
|
"tipRightTopCp2",
|
|
|
|
"tipRightTopEnd",
|
|
|
|
"tipRightBottomStart",
|
|
|
|
"tipRightBottomCp1",
|
|
|
|
"tipRightBottomCp2",
|
|
|
|
"tipRightBottomEnd",
|
|
|
|
"tipRightBottom",
|
|
|
|
"top",
|
|
|
|
"topCp2"
|
|
|
|
]
|
|
|
|
/*
|
2023-01-06 19:29:29 -08:00
|
|
|
* We're rotating all the points in
|
|
|
|
* the `rotateThese` array around
|
2022-10-11 01:37:09 +02:00
|
|
|
* the `edgeLeft` point.
|
|
|
|
*
|
2023-01-06 19:29:29 -08:00
|
|
|
* We're using increments of 1 degree
|
|
|
|
* until the `tipRightBottomStart` point
|
2022-12-29 06:25:01 -08:00
|
|
|
* is 1 mm beyond the center of our bib.
|
2022-10-11 01:37:09 +02:00
|
|
|
*/
|
|
|
|
while (points.tipRightBottomStart.x > -1) {
|
|
|
|
for (const p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
|
|
|
|
}
|
2022-01-19 11:31:39 +01:00
|
|
|
|
2022-10-11 01:37:09 +02:00
|
|
|
/*
|
|
|
|
* This is not needed
|
|
|
|
* we are merely adding it to show
|
|
|
|
* what the rotated path looks like
|
|
|
|
*/
|
|
|
|
macro("round", {
|
2023-09-30 14:04:18 +02:00
|
|
|
id: "showRightTop",
|
2022-10-11 01:37:09 +02:00
|
|
|
from: points.edgeTop,
|
|
|
|
to: points.tipRight,
|
|
|
|
via: points.tipRightTop,
|
|
|
|
hide: false,
|
2023-09-30 14:04:18 +02:00
|
|
|
classes: 'contrast dotted',
|
2022-10-11 01:37:09 +02:00
|
|
|
})
|
|
|
|
macro("round", {
|
2023-09-30 14:04:18 +02:00
|
|
|
id: "showRightBottom",
|
2022-10-11 01:37:09 +02:00
|
|
|
from: points.tipRight,
|
|
|
|
to: points.top,
|
|
|
|
via: points.tipRightBottom,
|
|
|
|
hide: false,
|
2023-09-30 14:04:18 +02:00
|
|
|
classes: 'contrast dotted',
|
2022-10-11 01:37:09 +02:00
|
|
|
})
|
2024-03-20 00:49:55 +00:00
|
|
|
// highlight-end
|
2022-10-11 01:37:09 +02:00
|
|
|
/*
|
2024-03-14 01:40:21 +00:00
|
|
|
* Now, adapt our `rect` path so it's no longer a rectangle:
|
|
|
|
*/
|
2022-10-11 01:37:09 +02:00
|
|
|
paths.rect = new Path()
|
|
|
|
.move(points.edgeTop)
|
|
|
|
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
|
|
|
|
.line(points.bottomLeft)
|
|
|
|
.line(points.bottomRight)
|
|
|
|
.line(points.edgeRight)
|
|
|
|
.curve(points.edgeRightCp, points.edgeTopRightCp, points.edgeTop)
|
|
|
|
.close()
|
|
|
|
|
|
|
|
return part
|
|
|
|
}
|
|
|
|
```
|
|
|
|
</Example>
|