diff --git a/designs/examples/config/index.js b/designs/examples/config/index.js
index c7219e59e22..fa985ed1100 100644
--- a/designs/examples/config/index.js
+++ b/designs/examples/config/index.js
@@ -96,6 +96,7 @@ export default {
'utils_beamsintersect',
'utils_beamintersectsx',
'utils_beamintersectsy',
+ 'utils_beamintersectscurve',
'utils_lineintersectscurve',
'utils_curvesintersect',
'utils_pointonbeam',
diff --git a/designs/examples/src/utils.mjs b/designs/examples/src/utils.mjs
index 201fa7d40f0..e75dd50834f 100644
--- a/designs/examples/src/utils.mjs
+++ b/designs/examples/src/utils.mjs
@@ -84,6 +84,38 @@ export const utils_beamintersectsy = {
},
}
+export const utils_beamintersectscurve = {
+ name: 'examples.utils_beamintersectscurve',
+ draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
+ points.A = new Point(10, 10)
+ points.Acp = new Point(10, 40)
+ points.B = new Point(110, 10)
+ points.Bcp = new Point(110, 40)
+ points.E = new Point(45, 25)
+ points.D = new Point(65, 25)
+ paths.curve = new Path().move(points.A).curve(points.Acp, points.Bcp, points.B)
+ paths.line = new Path().move(points.E).line(points.D)
+
+ for (let p of utils.beamIntersectsCurve(
+ points.D,
+ points.E,
+ points.A,
+ points.Acp,
+ points.Bcp,
+ points.B
+ )) {
+ snippets[part.getId()] = new Snippet('notch', p)
+ }
+
+ paths.help = new Path()
+ .move(new Point(0, 30))
+ .line(new Point(50, 30))
+ .attr('class', 'note dashed')
+
+ return part
+ },
+}
+
export const utils_beamsintersect = {
name: 'examples.utils_beamsintersect',
draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
diff --git a/markdown/dev/reference/api/en.md b/markdown/dev/reference/api/en.md
index 27eb0559bda..22e71c3ee43 100644
--- a/markdown/dev/reference/api/en.md
+++ b/markdown/dev/reference/api/en.md
@@ -60,6 +60,7 @@ The following named exports are **utility methods**:
| Named export | Description |
| ------------ | ------------|
| `beamIntersectsCircle` | See the [beamIntersectsCircle](/reference/api/utils/beamintersectscircle) documentation |
+| `beamIntersectsCurve` | See the [beamIntersectsCurve](/reference/api/utils/beamintersectscurve) documentation |
| `beamIntersectsX` | See the [beamIntersectsX](/reference/api/utils/beamintersectsx) documentation |
| `beamIntersectsY` | See the [beamIntersectsY](/reference/api/utils//beamintersectsy) documentation |
| `beamsIntersect` | See the [beamsIntersect](/reference/api/utils/beamsintersect) documentation |
diff --git a/markdown/dev/reference/api/utils/beamintersectscurve/en.md b/markdown/dev/reference/api/utils/beamintersectscurve/en.md
new file mode 100644
index 00000000000..16d4d102319
--- /dev/null
+++ b/markdown/dev/reference/api/utils/beamintersectscurve/en.md
@@ -0,0 +1,62 @@
+---
+title: utils.beamIntersectsCurve()
+---
+
+The `utils.beamIntersectsCurve()` function finds the intersection between an endless
+line and a curve described by points
+`start`, `cp1`, `cp2, and `end\`.
+
+
+
+This function can sometimes fail to find intersections in some curves
+due to a limitation in an underlying Bézier library.
+Please see [Bug #3367](https://github.com/freesewing/freesewing/issues/3367)
+for more information.
+
+
+
+## Signature
+
+```js
+array | false utils.beamIntersectsCurve(
+ Point from,
+ Point to,
+ Point start,
+ Point cp1,
+ Point cp2,
+ Point end
+)
+```
+
+## Example
+
+
+```js
+({ Point, points, Path, paths, Snippet, snippets, getId, utils, part }) => {
+
+ points.A = new Point(10, 10)
+ points.Acp = new Point(10, 40)
+ points.B = new Point(110, 10)
+ points.Bcp = new Point(110, 40)
+ points.E = new Point(45, 25)
+ points.D = new Point(65, 25)
+ paths.curve = new Path()
+ .move(points.A)
+ .curve(points.Acp, points.Bcp, points.B)
+ paths.line = new Path().move(points.E).line(points.D)
+
+ for (let p of utils.beamIntersectsCurve(
+ points.D,
+ points.E,
+ points.A,
+ points.Acp,
+ points.Bcp,
+ points.B
+ )) {
+ snippets[getId()] = new Snippet("notch", p)
+ }
+
+ return part
+}
+```
+
diff --git a/packages/core/src/index.mjs b/packages/core/src/index.mjs
index fa5d133c7a5..8735fe5411b 100644
--- a/packages/core/src/index.mjs
+++ b/packages/core/src/index.mjs
@@ -13,6 +13,7 @@ import {
beamIntersectsX,
beamIntersectsY,
beamsIntersect,
+ beamIntersectsCurve,
capitalize,
circlesIntersect,
curveEdge,
@@ -56,6 +57,7 @@ export {
beamIntersectsX,
beamIntersectsY,
beamsIntersect,
+ beamIntersectsCurve,
capitalize,
circlesIntersect,
curveEdge,
diff --git a/packages/core/src/utils.mjs b/packages/core/src/utils.mjs
index 5b8b68c0a20..7fbc23f4e5e 100644
--- a/packages/core/src/utils.mjs
+++ b/packages/core/src/utils.mjs
@@ -111,6 +111,24 @@ export function beamsIntersect(a1, a2, b1, b2) {
}
}
+/**
+ * Find the intersections between an endless line (beam) and a curve
+ *
+ *
+ * @param {Point} start - Start Point of the line
+ * @param {Point} end - End Point of the line
+ * @param {Point} from - Start Point of the curve
+ * @param {Point} cp1 - Control Point at the start of the curve
+ * @param {Point} cp2 - Control Point at the end of the curve
+ * @param {Point} to - End Point of the curve
+ * @return {Array} intersections - An array of Points at the intersections
+ */
+export function beamIntersectsCurve(start, end, from, cp1, cp2, to) {
+ let _start = new Point(start.x + (start.x - end.x) * 1000, start.y + (start.y - end.y) * 1000)
+ let _end = new Point(end.x + (end.x - start.x) * 1000, end.y + (end.y - start.y) * 1000)
+ return lineIntersectsCurve(_start, _end, from, cp1, cp2, to)
+}
+
/**
* Returns the string you pass with with the first character converted to uppercase
*
diff --git a/packages/core/tests/utils.test.mjs b/packages/core/tests/utils.test.mjs
index 729ef21522a..85ca172fe39 100644
--- a/packages/core/tests/utils.test.mjs
+++ b/packages/core/tests/utils.test.mjs
@@ -11,6 +11,7 @@ import {
splitCurve,
beamIntersectsX,
beamIntersectsY,
+ beamIntersectsCurve,
units,
lineIntersectsCurve,
curveIntersectsX,
@@ -70,6 +71,42 @@ describe('Utils', () => {
expect(round(X.x)).to.equal(7.14)
expect(round(X.y)).to.equal(40)
})
+ it('Should find no intersections between a curve and a beam', () => {
+ let A = new Point(10, 10)
+ let Acp = new Point(10, 30)
+ let B = new Point(110, 10)
+ let Bcp = new Point(110, 30)
+ let E = new Point(10, 40)
+ let D = new Point(20, 40)
+
+ let hit = beamIntersectsCurve(E, D, A, Acp, Bcp, B)
+ expect(hit).to.equal(false)
+ })
+
+ it('Should find one intersections between a curve and a beam', () => {
+ let A = new Point(10, 10)
+ let Acp = new Point(10, 30)
+ let B = new Point(110, 10)
+ let Bcp = new Point(110, 30)
+ let E = new Point(50, 14)
+ let D = new Point(55, 16)
+
+ let hit = beamIntersectsCurve(E, D, A, Acp, Bcp, B)
+ expect(round(hit.x)).to.equal(75.79)
+ expect(round(hit.y)).to.equal(24.31)
+ })
+
+ it('Should find two intersections between a curve and a beam', () => {
+ let A = new Point(10, 10)
+ let Acp = new Point(10, 30)
+ let B = new Point(110, 10)
+ let Bcp = new Point(110, 30)
+ let E = new Point(0, 14)
+ let D = new Point(5, 15)
+
+ let hits = beamIntersectsCurve(E, D, A, Acp, Bcp, B)
+ expect(hits.length).to.equal(2)
+ })
it("Should return false when two lines don't intersect", () => {
let a = new Point(10, 20)