@js-draw/math 1.26.0 → 1.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -69,6 +69,8 @@ export declare class Color4 {
69
69
  * ```
70
70
  */
71
71
  mix(other: Color4, fractionTo: number): Color4;
72
+ /** Returns a new color with a different opacity. */
73
+ withAlpha(a: number): Color4;
72
74
  /**
73
75
  * Ignoring this color's alpha component, returns a vector with components,
74
76
  * $$
@@ -183,6 +183,10 @@ class Color4 {
183
183
  const fractionOfThis = 1 - fractionTo;
184
184
  return new Color4(this.r * fractionOfThis + other.r * fractionTo, this.g * fractionOfThis + other.g * fractionTo, this.b * fractionOfThis + other.b * fractionTo, this.a * fractionOfThis + other.a * fractionTo);
185
185
  }
186
+ /** Returns a new color with a different opacity. */
187
+ withAlpha(a) {
188
+ return new Color4(this.r, this.g, this.b, a);
189
+ }
186
190
  /**
187
191
  * Ignoring this color's alpha component, returns a vector with components,
188
192
  * $$
@@ -185,9 +185,13 @@ export declare class Path {
185
185
  mapPoints(mapping: (point: Point2) => Point2): Path;
186
186
  transformedBy(affineTransfm: Mat33): Path;
187
187
  /**
188
- * @internal
188
+ * @internal -- TODO: This method may have incorrect output in some cases.
189
189
  */
190
190
  closedContainsPoint(point: Point2): boolean;
191
+ /**
192
+ * @returns `true` if this path (interpreted as a closed path) contains the given rectangle.
193
+ */
194
+ closedContainsRect(rect: Rect2): boolean;
191
195
  union(other: Path | PathCommand[] | null, options?: {
192
196
  allowReverse?: boolean;
193
197
  }): Path;
@@ -751,7 +751,7 @@ class Path {
751
751
  return this.mapPoints((point) => affineTransfm.transformVec2(point));
752
752
  }
753
753
  /**
754
- * @internal
754
+ * @internal -- TODO: This method may have incorrect output in some cases.
755
755
  */
756
756
  closedContainsPoint(point) {
757
757
  const bbox = this.getExactBBox();
@@ -761,7 +761,30 @@ class Path {
761
761
  const pointOutside = point.plus(Vec2_1.Vec2.of(bbox.width, 0));
762
762
  const asClosed = this.asClosed();
763
763
  const lineToOutside = new LineSegment2_1.default(point, pointOutside);
764
- return asClosed.intersection(lineToOutside).length % 2 === 1;
764
+ const intersections = asClosed.intersection(lineToOutside);
765
+ const filteredIntersections = intersections.filter((intersection, index) => {
766
+ if (index === 0)
767
+ return true; // No previous
768
+ const previousIntersection = intersections[index - 1];
769
+ const isRepeatedIntersection = previousIntersection.parameterValue >= 1 && intersection.parameterValue <= 0;
770
+ return !isRepeatedIntersection;
771
+ });
772
+ return filteredIntersections.length % 2 === 1;
773
+ }
774
+ /**
775
+ * @returns `true` if this path (interpreted as a closed path) contains the given rectangle.
776
+ */
777
+ closedContainsRect(rect) {
778
+ if (!this.bbox.containsRect(rect))
779
+ return false;
780
+ if (!rect.corners.every((corner) => this.closedContainsPoint(corner)))
781
+ return false;
782
+ for (const edge of rect.getEdges()) {
783
+ if (this.intersection(edge).length) {
784
+ return false;
785
+ }
786
+ }
787
+ return true;
765
788
  }
766
789
  // Creates a new path by joining [other] to the end of this path
767
790
  union(other,
@@ -69,6 +69,8 @@ export declare class Color4 {
69
69
  * ```
70
70
  */
71
71
  mix(other: Color4, fractionTo: number): Color4;
72
+ /** Returns a new color with a different opacity. */
73
+ withAlpha(a: number): Color4;
72
74
  /**
73
75
  * Ignoring this color's alpha component, returns a vector with components,
74
76
  * $$
@@ -177,6 +177,10 @@ export class Color4 {
177
177
  const fractionOfThis = 1 - fractionTo;
178
178
  return new Color4(this.r * fractionOfThis + other.r * fractionTo, this.g * fractionOfThis + other.g * fractionTo, this.b * fractionOfThis + other.b * fractionTo, this.a * fractionOfThis + other.a * fractionTo);
179
179
  }
180
+ /** Returns a new color with a different opacity. */
181
+ withAlpha(a) {
182
+ return new Color4(this.r, this.g, this.b, a);
183
+ }
180
184
  /**
181
185
  * Ignoring this color's alpha component, returns a vector with components,
182
186
  * $$
@@ -185,9 +185,13 @@ export declare class Path {
185
185
  mapPoints(mapping: (point: Point2) => Point2): Path;
186
186
  transformedBy(affineTransfm: Mat33): Path;
187
187
  /**
188
- * @internal
188
+ * @internal -- TODO: This method may have incorrect output in some cases.
189
189
  */
190
190
  closedContainsPoint(point: Point2): boolean;
191
+ /**
192
+ * @returns `true` if this path (interpreted as a closed path) contains the given rectangle.
193
+ */
194
+ closedContainsRect(rect: Rect2): boolean;
191
195
  union(other: Path | PathCommand[] | null, options?: {
192
196
  allowReverse?: boolean;
193
197
  }): Path;
@@ -743,7 +743,7 @@ export class Path {
743
743
  return this.mapPoints((point) => affineTransfm.transformVec2(point));
744
744
  }
745
745
  /**
746
- * @internal
746
+ * @internal -- TODO: This method may have incorrect output in some cases.
747
747
  */
748
748
  closedContainsPoint(point) {
749
749
  const bbox = this.getExactBBox();
@@ -753,7 +753,30 @@ export class Path {
753
753
  const pointOutside = point.plus(Vec2.of(bbox.width, 0));
754
754
  const asClosed = this.asClosed();
755
755
  const lineToOutside = new LineSegment2(point, pointOutside);
756
- return asClosed.intersection(lineToOutside).length % 2 === 1;
756
+ const intersections = asClosed.intersection(lineToOutside);
757
+ const filteredIntersections = intersections.filter((intersection, index) => {
758
+ if (index === 0)
759
+ return true; // No previous
760
+ const previousIntersection = intersections[index - 1];
761
+ const isRepeatedIntersection = previousIntersection.parameterValue >= 1 && intersection.parameterValue <= 0;
762
+ return !isRepeatedIntersection;
763
+ });
764
+ return filteredIntersections.length % 2 === 1;
765
+ }
766
+ /**
767
+ * @returns `true` if this path (interpreted as a closed path) contains the given rectangle.
768
+ */
769
+ closedContainsRect(rect) {
770
+ if (!this.bbox.containsRect(rect))
771
+ return false;
772
+ if (!rect.corners.every((corner) => this.closedContainsPoint(corner)))
773
+ return false;
774
+ for (const edge of rect.getEdges()) {
775
+ if (this.intersection(edge).length) {
776
+ return false;
777
+ }
778
+ }
779
+ return true;
757
780
  }
758
781
  // Creates a new path by joining [other] to the end of this path
759
782
  union(other,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@js-draw/math",
3
- "version": "1.26.0",
3
+ "version": "1.27.1",
4
4
  "description": "A math library for js-draw. ",
5
5
  "types": "./dist/mjs/lib.d.ts",
6
6
  "main": "./dist/cjs/lib.js",
@@ -27,7 +27,7 @@
27
27
  "bezier-js": "6.1.3"
28
28
  },
29
29
  "devDependencies": {
30
- "@js-draw/build-tool": "^1.26.0",
30
+ "@js-draw/build-tool": "^1.27.1",
31
31
  "@types/bezier-js": "4.1.0",
32
32
  "@types/jest": "29.5.5",
33
33
  "@types/jsdom": "21.1.3"
@@ -44,5 +44,5 @@
44
44
  "svg",
45
45
  "math"
46
46
  ],
47
- "gitHead": "6529cad584ca93a992f2576a43f25af48da3d707"
47
+ "gitHead": "e11574b7b329e7867358c400457a3d99b1d2c469"
48
48
  }
package/src/Color4.ts CHANGED
@@ -224,6 +224,11 @@ export class Color4 {
224
224
  );
225
225
  }
226
226
 
227
+ /** Returns a new color with a different opacity. */
228
+ public withAlpha(a: number) {
229
+ return new Color4(this.r, this.g, this.b, a);
230
+ }
231
+
227
232
  /**
228
233
  * Ignoring this color's alpha component, returns a vector with components,
229
234
  * $$
@@ -536,4 +536,21 @@ describe('Path', () => {
536
536
  expect(path.tangentAt(at)).objEq(expected);
537
537
  },
538
538
  );
539
+
540
+ it.each([
541
+ // A rectangle completely contained
542
+ ['m0,0 l10,0 l0,10 l-10,0', new Rect2(3, 3, 3, 3), true],
543
+ // A rectangle partially contained
544
+ ['m0,0 l10,0 l0,10 l-10,0', new Rect2(3, 3, 33, 3), false],
545
+ // A rectangle not contained
546
+ ['m0,0 l10,0 l0,10 l-10,0', new Rect2(13, 3, 1, 1), false],
547
+ // More complicated path containing a rectangle
548
+ ['M0,0 Q10,15 10,5', new Rect2(5, 5, 1, 1), true],
549
+ ['M0,0 Q10,15 10,5', new Rect2(15, 5, 1, 1), false],
550
+ ])(
551
+ '.closedContainsRect should return whether a rectangle is contained within a path (case %#: path(%s), rect(%s))',
552
+ (pathString, rect, expected) => {
553
+ expect(Path.fromString(pathString).closedContainsRect(rect)).toBe(expected);
554
+ },
555
+ );
539
556
  });
@@ -969,7 +969,7 @@ export class Path {
969
969
  }
970
970
 
971
971
  /**
972
- * @internal
972
+ * @internal -- TODO: This method may have incorrect output in some cases.
973
973
  */
974
974
  public closedContainsPoint(point: Point2) {
975
975
  const bbox = this.getExactBBox();
@@ -981,7 +981,32 @@ export class Path {
981
981
  const asClosed = this.asClosed();
982
982
 
983
983
  const lineToOutside = new LineSegment2(point, pointOutside);
984
- return asClosed.intersection(lineToOutside).length % 2 === 1;
984
+
985
+ const intersections = asClosed.intersection(lineToOutside);
986
+ const filteredIntersections = intersections.filter((intersection, index) => {
987
+ if (index === 0) return true; // No previous
988
+ const previousIntersection = intersections[index - 1];
989
+ const isRepeatedIntersection =
990
+ previousIntersection.parameterValue >= 1 && intersection.parameterValue <= 0;
991
+ return !isRepeatedIntersection;
992
+ });
993
+ return filteredIntersections.length % 2 === 1;
994
+ }
995
+
996
+ /**
997
+ * @returns `true` if this path (interpreted as a closed path) contains the given rectangle.
998
+ */
999
+ public closedContainsRect(rect: Rect2) {
1000
+ if (!this.bbox.containsRect(rect)) return false;
1001
+ if (!rect.corners.every((corner) => this.closedContainsPoint(corner))) return false;
1002
+
1003
+ for (const edge of rect.getEdges()) {
1004
+ if (this.intersection(edge).length) {
1005
+ return false;
1006
+ }
1007
+ }
1008
+
1009
+ return true;
985
1010
  }
986
1011
 
987
1012
  // Creates a new path by joining [other] to the end of this path