@js-draw/math 1.26.0 → 1.27.1
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/cjs/Color4.d.ts +2 -0
- package/dist/cjs/Color4.js +4 -0
- package/dist/cjs/shapes/Path.d.ts +5 -1
- package/dist/cjs/shapes/Path.js +25 -2
- package/dist/mjs/Color4.d.ts +2 -0
- package/dist/mjs/Color4.mjs +4 -0
- package/dist/mjs/shapes/Path.d.ts +5 -1
- package/dist/mjs/shapes/Path.mjs +25 -2
- package/package.json +3 -3
- package/src/Color4.ts +5 -0
- package/src/shapes/Path.test.ts +17 -0
- package/src/shapes/Path.ts +27 -2
package/dist/cjs/Color4.d.ts
CHANGED
@@ -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
|
* $$
|
package/dist/cjs/Color4.js
CHANGED
@@ -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;
|
package/dist/cjs/shapes/Path.js
CHANGED
@@ -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
|
-
|
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,
|
package/dist/mjs/Color4.d.ts
CHANGED
@@ -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
|
* $$
|
package/dist/mjs/Color4.mjs
CHANGED
@@ -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;
|
package/dist/mjs/shapes/Path.mjs
CHANGED
@@ -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
|
-
|
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.
|
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.
|
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": "
|
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
|
* $$
|
package/src/shapes/Path.test.ts
CHANGED
@@ -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
|
});
|
package/src/shapes/Path.ts
CHANGED
@@ -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
|
-
|
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
|