@js-draw/math 1.10.0 → 1.16.0
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.
- package/dist/cjs/lib.d.ts +1 -1
- package/dist/cjs/lib.js +16 -3
- package/dist/cjs/rounding/cleanUpNumber.d.ts +3 -0
- package/dist/cjs/rounding/cleanUpNumber.js +35 -0
- package/dist/cjs/rounding/constants.d.ts +1 -0
- package/dist/cjs/rounding/constants.js +4 -0
- package/dist/cjs/rounding/getLenAfterDecimal.d.ts +10 -0
- package/dist/cjs/rounding/getLenAfterDecimal.js +30 -0
- package/dist/cjs/rounding/lib.d.ts +1 -0
- package/dist/cjs/rounding/lib.js +5 -0
- package/dist/cjs/{rounding.d.ts → rounding/toRoundedString.d.ts} +1 -3
- package/dist/cjs/rounding/toRoundedString.js +54 -0
- package/dist/cjs/rounding/toStringOfSamePrecision.d.ts +2 -0
- package/dist/cjs/rounding/toStringOfSamePrecision.js +58 -0
- package/dist/cjs/rounding/toStringOfSamePrecision.test.d.ts +1 -0
- package/dist/cjs/shapes/Path.js +8 -7
- package/dist/cjs/shapes/Triangle.js +2 -2
- package/dist/mjs/lib.d.ts +1 -1
- package/dist/mjs/lib.mjs +1 -1
- package/dist/mjs/rounding/cleanUpNumber.d.ts +3 -0
- package/dist/mjs/rounding/cleanUpNumber.mjs +31 -0
- package/dist/mjs/rounding/cleanUpNumber.test.d.ts +1 -0
- package/dist/mjs/rounding/constants.d.ts +1 -0
- package/dist/mjs/rounding/constants.mjs +1 -0
- package/dist/mjs/rounding/getLenAfterDecimal.d.ts +10 -0
- package/dist/mjs/rounding/getLenAfterDecimal.mjs +26 -0
- package/dist/mjs/rounding/lib.d.ts +1 -0
- package/dist/mjs/rounding/lib.mjs +1 -0
- package/dist/mjs/{rounding.d.ts → rounding/toRoundedString.d.ts} +1 -3
- package/dist/mjs/rounding/toRoundedString.mjs +47 -0
- package/dist/mjs/rounding/toRoundedString.test.d.ts +1 -0
- package/dist/mjs/rounding/toStringOfSamePrecision.d.ts +2 -0
- package/dist/mjs/rounding/toStringOfSamePrecision.mjs +51 -0
- package/dist/mjs/rounding/toStringOfSamePrecision.test.d.ts +1 -0
- package/dist/mjs/shapes/Path.mjs +2 -1
- package/dist/mjs/shapes/Triangle.mjs +2 -2
- package/package.json +3 -3
- package/src/lib.ts +1 -1
- package/src/rounding/cleanUpNumber.test.ts +15 -0
- package/src/rounding/cleanUpNumber.ts +38 -0
- package/src/rounding/constants.ts +3 -0
- package/src/rounding/getLenAfterDecimal.ts +29 -0
- package/src/rounding/lib.ts +2 -0
- package/src/rounding/toRoundedString.test.ts +32 -0
- package/src/rounding/toRoundedString.ts +57 -0
- package/src/rounding/toStringOfSamePrecision.test.ts +21 -0
- package/src/rounding/toStringOfSamePrecision.ts +63 -0
- package/src/shapes/Path.ts +2 -1
- package/src/shapes/Triangle.ts +2 -2
- package/dist/cjs/rounding.js +0 -146
- package/dist/mjs/rounding.mjs +0 -139
- package/src/rounding.test.ts +0 -65
- package/src/rounding.ts +0 -168
- /package/dist/cjs/{rounding.test.d.ts → rounding/cleanUpNumber.test.d.ts} +0 -0
- /package/dist/{mjs/rounding.test.d.ts → cjs/rounding/toRoundedString.test.d.ts} +0 -0
package/dist/cjs/lib.d.ts
CHANGED
package/dist/cjs/lib.js
CHANGED
@@ -17,8 +17,22 @@
|
|
17
17
|
*
|
18
18
|
* @packageDocumentation
|
19
19
|
*/
|
20
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
21
|
+
if (k2 === undefined) k2 = k;
|
22
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
23
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
24
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
25
|
+
}
|
26
|
+
Object.defineProperty(o, k2, desc);
|
27
|
+
}) : (function(o, m, k, k2) {
|
28
|
+
if (k2 === undefined) k2 = k;
|
29
|
+
o[k2] = m[k];
|
30
|
+
}));
|
31
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
32
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
33
|
+
};
|
20
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
21
|
-
exports.
|
35
|
+
exports.Color4 = exports.Vec3 = exports.Vec2 = exports.Mat33 = exports.Abstract2DShape = exports.QuadraticBezier = exports.Rect2 = exports.PathCommandType = exports.Path = exports.LineSegment2 = void 0;
|
22
36
|
var LineSegment2_1 = require("./shapes/LineSegment2");
|
23
37
|
Object.defineProperty(exports, "LineSegment2", { enumerable: true, get: function () { return LineSegment2_1.LineSegment2; } });
|
24
38
|
var Path_1 = require("./shapes/Path");
|
@@ -38,7 +52,6 @@ var Vec3_1 = require("./Vec3");
|
|
38
52
|
Object.defineProperty(exports, "Vec3", { enumerable: true, get: function () { return Vec3_1.Vec3; } });
|
39
53
|
var Color4_1 = require("./Color4");
|
40
54
|
Object.defineProperty(exports, "Color4", { enumerable: true, get: function () { return Color4_1.Color4; } });
|
41
|
-
|
42
|
-
Object.defineProperty(exports, "toRoundedString", { enumerable: true, get: function () { return rounding_1.toRoundedString; } });
|
55
|
+
__exportStar(require("./rounding/lib"), exports);
|
43
56
|
// Note: All above exports cannot use `export { default as ... } from "..."` because this
|
44
57
|
// breaks TypeDoc -- TypeDoc otherwise labels any imports of these classes as `default`.
|
@@ -0,0 +1,35 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.cleanUpNumber = void 0;
|
4
|
+
/** Cleans up stringified numbers */
|
5
|
+
const cleanUpNumber = (text) => {
|
6
|
+
// Regular expression substitions can be somewhat expensive. Only do them
|
7
|
+
// if necessary.
|
8
|
+
if (text.indexOf('e') > 0) {
|
9
|
+
// Round to zero.
|
10
|
+
if (text.match(/[eE][-]\d{2,}$/)) {
|
11
|
+
return '0';
|
12
|
+
}
|
13
|
+
}
|
14
|
+
const lastChar = text.charAt(text.length - 1);
|
15
|
+
if (lastChar === '0' || lastChar === '.') {
|
16
|
+
// Remove trailing zeroes
|
17
|
+
text = text.replace(/([.]\d*[^0]+)0+$/, '$1');
|
18
|
+
text = text.replace(/[.]0+$/, '.');
|
19
|
+
// Remove trailing period
|
20
|
+
text = text.replace(/[.]$/, '');
|
21
|
+
}
|
22
|
+
const firstChar = text.charAt(0);
|
23
|
+
if (firstChar === '0' || firstChar === '-') {
|
24
|
+
// Remove unnecessary leading zeroes.
|
25
|
+
text = text.replace(/^(0+)[.]/, '.');
|
26
|
+
text = text.replace(/^-(0+)[.]/, '-.');
|
27
|
+
text = text.replace(/^(-?)0+$/, '$10');
|
28
|
+
}
|
29
|
+
if (text === '-0') {
|
30
|
+
return '0';
|
31
|
+
}
|
32
|
+
return text;
|
33
|
+
};
|
34
|
+
exports.cleanUpNumber = cleanUpNumber;
|
35
|
+
exports.default = exports.cleanUpNumber;
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare const numberRegex: RegExp;
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/**
|
2
|
+
* Returns the length of `numberAsString` after a decimal point.
|
3
|
+
*
|
4
|
+
* For example,
|
5
|
+
* ```ts
|
6
|
+
* getLenAfterDecimal('1.001') // -> 3
|
7
|
+
* ```
|
8
|
+
*/
|
9
|
+
export declare const getLenAfterDecimal: (numberAsString: string) => number;
|
10
|
+
export default getLenAfterDecimal;
|
@@ -0,0 +1,30 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.getLenAfterDecimal = void 0;
|
4
|
+
const constants_1 = require("./constants");
|
5
|
+
/**
|
6
|
+
* Returns the length of `numberAsString` after a decimal point.
|
7
|
+
*
|
8
|
+
* For example,
|
9
|
+
* ```ts
|
10
|
+
* getLenAfterDecimal('1.001') // -> 3
|
11
|
+
* ```
|
12
|
+
*/
|
13
|
+
const getLenAfterDecimal = (numberAsString) => {
|
14
|
+
const numberMatch = constants_1.numberRegex.exec(numberAsString);
|
15
|
+
if (!numberMatch) {
|
16
|
+
// If not a match, either the number is exponential notation (or is something
|
17
|
+
// like NaN or Infinity)
|
18
|
+
if (numberAsString.search(/[eE]/) !== -1 || /^[a-zA-Z]+$/.exec(numberAsString)) {
|
19
|
+
return -1;
|
20
|
+
// Or it has no decimal point
|
21
|
+
}
|
22
|
+
else {
|
23
|
+
return 0;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
const afterDecimalLen = numberMatch[3].length;
|
27
|
+
return afterDecimalLen;
|
28
|
+
};
|
29
|
+
exports.getLenAfterDecimal = getLenAfterDecimal;
|
30
|
+
exports.default = exports.getLenAfterDecimal;
|
@@ -0,0 +1 @@
|
|
1
|
+
export { toRoundedString } from './toRoundedString';
|
@@ -0,0 +1,5 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.toRoundedString = void 0;
|
4
|
+
var toRoundedString_1 = require("./toRoundedString");
|
5
|
+
Object.defineProperty(exports, "toRoundedString", { enumerable: true, get: function () { return toRoundedString_1.toRoundedString; } });
|
@@ -1,4 +1,3 @@
|
|
1
|
-
export declare const cleanUpNumber: (text: string) => string;
|
2
1
|
/**
|
3
2
|
* Converts `num` to a string, removing trailing digits that were likely caused by
|
4
3
|
* precision errors.
|
@@ -11,5 +10,4 @@ export declare const cleanUpNumber: (text: string) => string;
|
|
11
10
|
* ```
|
12
11
|
*/
|
13
12
|
export declare const toRoundedString: (num: number) => string;
|
14
|
-
export
|
15
|
-
export declare const toStringOfSamePrecision: (num: number, ...references: string[]) => string;
|
13
|
+
export default toRoundedString;
|
@@ -0,0 +1,54 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.toRoundedString = void 0;
|
7
|
+
const cleanUpNumber_1 = __importDefault(require("./cleanUpNumber"));
|
8
|
+
/**
|
9
|
+
* Converts `num` to a string, removing trailing digits that were likely caused by
|
10
|
+
* precision errors.
|
11
|
+
*
|
12
|
+
* @example
|
13
|
+
* ```ts,runnable,console
|
14
|
+
* import { toRoundedString } from '@js-draw/math';
|
15
|
+
*
|
16
|
+
* console.log('Rounded: ', toRoundedString(1.000000011));
|
17
|
+
* ```
|
18
|
+
*/
|
19
|
+
const toRoundedString = (num) => {
|
20
|
+
// Try to remove rounding errors. If the number ends in at least three/four zeroes
|
21
|
+
// (or nines) just one or two digits, it's probably a rounding error.
|
22
|
+
const fixRoundingUpExp = /^([-]?\d*\.\d{3,})0{4,}\d{1,4}$/;
|
23
|
+
const hasRoundingDownExp = /^([-]?)(\d*)\.(\d{3,}9{4,})\d{1,4}$/;
|
24
|
+
let text = num.toString(10);
|
25
|
+
if (text.indexOf('.') === -1) {
|
26
|
+
return text;
|
27
|
+
}
|
28
|
+
const roundingDownMatch = hasRoundingDownExp.exec(text);
|
29
|
+
if (roundingDownMatch) {
|
30
|
+
const negativeSign = roundingDownMatch[1];
|
31
|
+
const postDecimalString = roundingDownMatch[3];
|
32
|
+
const lastDigit = parseInt(postDecimalString.charAt(postDecimalString.length - 1), 10);
|
33
|
+
const postDecimal = parseInt(postDecimalString, 10);
|
34
|
+
const preDecimal = parseInt(roundingDownMatch[2], 10);
|
35
|
+
const origPostDecimalString = roundingDownMatch[3];
|
36
|
+
let newPostDecimal = (postDecimal + 10 - lastDigit).toString();
|
37
|
+
let carry = 0;
|
38
|
+
if (newPostDecimal.length > postDecimal.toString().length) {
|
39
|
+
// Left-shift
|
40
|
+
newPostDecimal = newPostDecimal.substring(1);
|
41
|
+
carry = 1;
|
42
|
+
}
|
43
|
+
// parseInt(...).toString() removes leading zeroes. Add them back.
|
44
|
+
while (newPostDecimal.length < origPostDecimalString.length) {
|
45
|
+
newPostDecimal = carry.toString(10) + newPostDecimal;
|
46
|
+
carry = 0;
|
47
|
+
}
|
48
|
+
text = `${negativeSign + (preDecimal + carry).toString()}.${newPostDecimal}`;
|
49
|
+
}
|
50
|
+
text = text.replace(fixRoundingUpExp, '$1');
|
51
|
+
return (0, cleanUpNumber_1.default)(text);
|
52
|
+
};
|
53
|
+
exports.toRoundedString = toRoundedString;
|
54
|
+
exports.default = exports.toRoundedString;
|
@@ -0,0 +1,58 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.toStringOfSamePrecision = void 0;
|
7
|
+
const cleanUpNumber_1 = __importDefault(require("./cleanUpNumber"));
|
8
|
+
const constants_1 = require("./constants");
|
9
|
+
const getLenAfterDecimal_1 = __importDefault(require("./getLenAfterDecimal"));
|
10
|
+
const toRoundedString_1 = __importDefault(require("./toRoundedString"));
|
11
|
+
// [reference] should be a string representation of a base-10 number (no exponential (e.g. 10e10))
|
12
|
+
const toStringOfSamePrecision = (num, ...references) => {
|
13
|
+
const text = num.toString(10);
|
14
|
+
const textMatch = constants_1.numberRegex.exec(text);
|
15
|
+
if (!textMatch) {
|
16
|
+
return text;
|
17
|
+
}
|
18
|
+
let decimalPlaces = -1;
|
19
|
+
for (const reference of references) {
|
20
|
+
decimalPlaces = Math.max((0, getLenAfterDecimal_1.default)(reference), decimalPlaces);
|
21
|
+
}
|
22
|
+
if (decimalPlaces === -1) {
|
23
|
+
return (0, toRoundedString_1.default)(num);
|
24
|
+
}
|
25
|
+
// Make text's after decimal length match [afterDecimalLen].
|
26
|
+
let postDecimal = textMatch[3].substring(0, decimalPlaces);
|
27
|
+
let preDecimal = textMatch[2];
|
28
|
+
const nextDigit = textMatch[3].charAt(decimalPlaces);
|
29
|
+
if (nextDigit !== '') {
|
30
|
+
const asNumber = parseInt(nextDigit, 10);
|
31
|
+
if (asNumber >= 5) {
|
32
|
+
// Don't attempt to parseInt() an empty string.
|
33
|
+
if (postDecimal.length > 0) {
|
34
|
+
const leadingZeroMatch = /^(0+)(\d*)$/.exec(postDecimal);
|
35
|
+
let leadingZeroes = '';
|
36
|
+
let postLeading = postDecimal;
|
37
|
+
if (leadingZeroMatch) {
|
38
|
+
leadingZeroes = leadingZeroMatch[1];
|
39
|
+
postLeading = leadingZeroMatch[2];
|
40
|
+
}
|
41
|
+
postDecimal = (parseInt(postDecimal) + 1).toString();
|
42
|
+
// If postDecimal got longer, remove leading zeroes if possible
|
43
|
+
if (postDecimal.length > postLeading.length && leadingZeroes.length > 0) {
|
44
|
+
leadingZeroes = leadingZeroes.substring(1);
|
45
|
+
}
|
46
|
+
postDecimal = leadingZeroes + postDecimal;
|
47
|
+
}
|
48
|
+
if (postDecimal.length === 0 || postDecimal.length > decimalPlaces) {
|
49
|
+
preDecimal = (parseInt(preDecimal) + 1).toString();
|
50
|
+
postDecimal = postDecimal.substring(1);
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
const negativeSign = textMatch[1];
|
55
|
+
return (0, cleanUpNumber_1.default)(`${negativeSign}${preDecimal}.${postDecimal}`);
|
56
|
+
};
|
57
|
+
exports.toStringOfSamePrecision = toStringOfSamePrecision;
|
58
|
+
exports.default = exports.toStringOfSamePrecision;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
package/dist/cjs/shapes/Path.js
CHANGED
@@ -4,13 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
6
|
exports.Path = exports.PathCommandType = void 0;
|
7
|
-
const rounding_1 = require("../rounding");
|
8
7
|
const LineSegment2_1 = __importDefault(require("./LineSegment2"));
|
9
8
|
const Rect2_1 = __importDefault(require("./Rect2"));
|
10
9
|
const Vec2_1 = require("../Vec2");
|
11
10
|
const CubicBezier_1 = __importDefault(require("./CubicBezier"));
|
12
11
|
const QuadraticBezier_1 = __importDefault(require("./QuadraticBezier"));
|
13
12
|
const PointShape2D_1 = __importDefault(require("./PointShape2D"));
|
13
|
+
const toRoundedString_1 = __importDefault(require("../rounding/toRoundedString"));
|
14
|
+
const toStringOfSamePrecision_1 = __importDefault(require("../rounding/toStringOfSamePrecision"));
|
14
15
|
var PathCommandType;
|
15
16
|
(function (PathCommandType) {
|
16
17
|
PathCommandType[PathCommandType["LineTo"] = 0] = "LineTo";
|
@@ -591,15 +592,15 @@ class Path {
|
|
591
592
|
const absoluteCommandParts = [];
|
592
593
|
const relativeCommandParts = [];
|
593
594
|
const makeAbsCommand = !prevPoint || onlyAbsCommands;
|
594
|
-
const roundedPrevX = prevPoint ? (0,
|
595
|
-
const roundedPrevY = prevPoint ? (0,
|
595
|
+
const roundedPrevX = prevPoint ? (0, toRoundedString_1.default)(prevPoint.x) : '';
|
596
|
+
const roundedPrevY = prevPoint ? (0, toRoundedString_1.default)(prevPoint.y) : '';
|
596
597
|
for (const point of points) {
|
597
|
-
const xComponent = (0,
|
598
|
-
const yComponent = (0,
|
598
|
+
const xComponent = (0, toRoundedString_1.default)(point.x);
|
599
|
+
const yComponent = (0, toRoundedString_1.default)(point.y);
|
599
600
|
// Relative commands are often shorter as strings than absolute commands.
|
600
601
|
if (!makeAbsCommand) {
|
601
|
-
const xComponentRelative = (0,
|
602
|
-
const yComponentRelative = (0,
|
602
|
+
const xComponentRelative = (0, toStringOfSamePrecision_1.default)(point.x - prevPoint.x, xComponent, roundedPrevX, roundedPrevY);
|
603
|
+
const yComponentRelative = (0, toStringOfSamePrecision_1.default)(point.y - prevPoint.y, yComponent, roundedPrevX, roundedPrevY);
|
603
604
|
// No need for an additional separator if it starts with a '-'
|
604
605
|
if (yComponentRelative.charAt(0) === '-') {
|
605
606
|
relativeCommandParts.push(`${xComponentRelative}${yComponentRelative}`);
|
@@ -44,12 +44,12 @@ class Triangle extends Abstract2DShape_1.default {
|
|
44
44
|
}
|
45
45
|
// Transform, treating this as composed of 2D points.
|
46
46
|
transformed2DBy(affineTransform) {
|
47
|
-
return this.map(affineTransform.transformVec2);
|
47
|
+
return this.map(vertex => affineTransform.transformVec2(vertex));
|
48
48
|
}
|
49
49
|
// Transforms this by a linear transform --- verticies are treated as
|
50
50
|
// 3D points.
|
51
51
|
transformedBy(linearTransform) {
|
52
|
-
return this.map(linearTransform.transformVec3);
|
52
|
+
return this.map(vertex => linearTransform.transformVec3(vertex));
|
53
53
|
}
|
54
54
|
/**
|
55
55
|
* Returns the sides of this triangle, as an array of `LineSegment2`s.
|
package/dist/mjs/lib.d.ts
CHANGED
package/dist/mjs/lib.mjs
CHANGED
@@ -25,6 +25,6 @@ export { Mat33 } from './Mat33.mjs';
|
|
25
25
|
export { Vec2 } from './Vec2.mjs';
|
26
26
|
export { Vec3 } from './Vec3.mjs';
|
27
27
|
export { Color4 } from './Color4.mjs';
|
28
|
-
export
|
28
|
+
export * from './rounding/lib.mjs';
|
29
29
|
// Note: All above exports cannot use `export { default as ... } from "..."` because this
|
30
30
|
// breaks TypeDoc -- TypeDoc otherwise labels any imports of these classes as `default`.
|
@@ -0,0 +1,31 @@
|
|
1
|
+
/** Cleans up stringified numbers */
|
2
|
+
export const cleanUpNumber = (text) => {
|
3
|
+
// Regular expression substitions can be somewhat expensive. Only do them
|
4
|
+
// if necessary.
|
5
|
+
if (text.indexOf('e') > 0) {
|
6
|
+
// Round to zero.
|
7
|
+
if (text.match(/[eE][-]\d{2,}$/)) {
|
8
|
+
return '0';
|
9
|
+
}
|
10
|
+
}
|
11
|
+
const lastChar = text.charAt(text.length - 1);
|
12
|
+
if (lastChar === '0' || lastChar === '.') {
|
13
|
+
// Remove trailing zeroes
|
14
|
+
text = text.replace(/([.]\d*[^0]+)0+$/, '$1');
|
15
|
+
text = text.replace(/[.]0+$/, '.');
|
16
|
+
// Remove trailing period
|
17
|
+
text = text.replace(/[.]$/, '');
|
18
|
+
}
|
19
|
+
const firstChar = text.charAt(0);
|
20
|
+
if (firstChar === '0' || firstChar === '-') {
|
21
|
+
// Remove unnecessary leading zeroes.
|
22
|
+
text = text.replace(/^(0+)[.]/, '.');
|
23
|
+
text = text.replace(/^-(0+)[.]/, '-.');
|
24
|
+
text = text.replace(/^(-?)0+$/, '$10');
|
25
|
+
}
|
26
|
+
if (text === '-0') {
|
27
|
+
return '0';
|
28
|
+
}
|
29
|
+
return text;
|
30
|
+
};
|
31
|
+
export default cleanUpNumber;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare const numberRegex: RegExp;
|
@@ -0,0 +1 @@
|
|
1
|
+
export const numberRegex = /^([-]?)(\d*)[.](\d+)$/;
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/**
|
2
|
+
* Returns the length of `numberAsString` after a decimal point.
|
3
|
+
*
|
4
|
+
* For example,
|
5
|
+
* ```ts
|
6
|
+
* getLenAfterDecimal('1.001') // -> 3
|
7
|
+
* ```
|
8
|
+
*/
|
9
|
+
export declare const getLenAfterDecimal: (numberAsString: string) => number;
|
10
|
+
export default getLenAfterDecimal;
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { numberRegex } from './constants.mjs';
|
2
|
+
/**
|
3
|
+
* Returns the length of `numberAsString` after a decimal point.
|
4
|
+
*
|
5
|
+
* For example,
|
6
|
+
* ```ts
|
7
|
+
* getLenAfterDecimal('1.001') // -> 3
|
8
|
+
* ```
|
9
|
+
*/
|
10
|
+
export const getLenAfterDecimal = (numberAsString) => {
|
11
|
+
const numberMatch = numberRegex.exec(numberAsString);
|
12
|
+
if (!numberMatch) {
|
13
|
+
// If not a match, either the number is exponential notation (or is something
|
14
|
+
// like NaN or Infinity)
|
15
|
+
if (numberAsString.search(/[eE]/) !== -1 || /^[a-zA-Z]+$/.exec(numberAsString)) {
|
16
|
+
return -1;
|
17
|
+
// Or it has no decimal point
|
18
|
+
}
|
19
|
+
else {
|
20
|
+
return 0;
|
21
|
+
}
|
22
|
+
}
|
23
|
+
const afterDecimalLen = numberMatch[3].length;
|
24
|
+
return afterDecimalLen;
|
25
|
+
};
|
26
|
+
export default getLenAfterDecimal;
|
@@ -0,0 +1 @@
|
|
1
|
+
export { toRoundedString } from './toRoundedString';
|
@@ -0,0 +1 @@
|
|
1
|
+
export { toRoundedString } from './toRoundedString.mjs';
|
@@ -1,4 +1,3 @@
|
|
1
|
-
export declare const cleanUpNumber: (text: string) => string;
|
2
1
|
/**
|
3
2
|
* Converts `num` to a string, removing trailing digits that were likely caused by
|
4
3
|
* precision errors.
|
@@ -11,5 +10,4 @@ export declare const cleanUpNumber: (text: string) => string;
|
|
11
10
|
* ```
|
12
11
|
*/
|
13
12
|
export declare const toRoundedString: (num: number) => string;
|
14
|
-
export
|
15
|
-
export declare const toStringOfSamePrecision: (num: number, ...references: string[]) => string;
|
13
|
+
export default toRoundedString;
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import cleanUpNumber from './cleanUpNumber.mjs';
|
2
|
+
/**
|
3
|
+
* Converts `num` to a string, removing trailing digits that were likely caused by
|
4
|
+
* precision errors.
|
5
|
+
*
|
6
|
+
* @example
|
7
|
+
* ```ts,runnable,console
|
8
|
+
* import { toRoundedString } from '@js-draw/math';
|
9
|
+
*
|
10
|
+
* console.log('Rounded: ', toRoundedString(1.000000011));
|
11
|
+
* ```
|
12
|
+
*/
|
13
|
+
export const toRoundedString = (num) => {
|
14
|
+
// Try to remove rounding errors. If the number ends in at least three/four zeroes
|
15
|
+
// (or nines) just one or two digits, it's probably a rounding error.
|
16
|
+
const fixRoundingUpExp = /^([-]?\d*\.\d{3,})0{4,}\d{1,4}$/;
|
17
|
+
const hasRoundingDownExp = /^([-]?)(\d*)\.(\d{3,}9{4,})\d{1,4}$/;
|
18
|
+
let text = num.toString(10);
|
19
|
+
if (text.indexOf('.') === -1) {
|
20
|
+
return text;
|
21
|
+
}
|
22
|
+
const roundingDownMatch = hasRoundingDownExp.exec(text);
|
23
|
+
if (roundingDownMatch) {
|
24
|
+
const negativeSign = roundingDownMatch[1];
|
25
|
+
const postDecimalString = roundingDownMatch[3];
|
26
|
+
const lastDigit = parseInt(postDecimalString.charAt(postDecimalString.length - 1), 10);
|
27
|
+
const postDecimal = parseInt(postDecimalString, 10);
|
28
|
+
const preDecimal = parseInt(roundingDownMatch[2], 10);
|
29
|
+
const origPostDecimalString = roundingDownMatch[3];
|
30
|
+
let newPostDecimal = (postDecimal + 10 - lastDigit).toString();
|
31
|
+
let carry = 0;
|
32
|
+
if (newPostDecimal.length > postDecimal.toString().length) {
|
33
|
+
// Left-shift
|
34
|
+
newPostDecimal = newPostDecimal.substring(1);
|
35
|
+
carry = 1;
|
36
|
+
}
|
37
|
+
// parseInt(...).toString() removes leading zeroes. Add them back.
|
38
|
+
while (newPostDecimal.length < origPostDecimalString.length) {
|
39
|
+
newPostDecimal = carry.toString(10) + newPostDecimal;
|
40
|
+
carry = 0;
|
41
|
+
}
|
42
|
+
text = `${negativeSign + (preDecimal + carry).toString()}.${newPostDecimal}`;
|
43
|
+
}
|
44
|
+
text = text.replace(fixRoundingUpExp, '$1');
|
45
|
+
return cleanUpNumber(text);
|
46
|
+
};
|
47
|
+
export default toRoundedString;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import cleanUpNumber from './cleanUpNumber.mjs';
|
2
|
+
import { numberRegex } from './constants.mjs';
|
3
|
+
import getLenAfterDecimal from './getLenAfterDecimal.mjs';
|
4
|
+
import toRoundedString from './toRoundedString.mjs';
|
5
|
+
// [reference] should be a string representation of a base-10 number (no exponential (e.g. 10e10))
|
6
|
+
export const toStringOfSamePrecision = (num, ...references) => {
|
7
|
+
const text = num.toString(10);
|
8
|
+
const textMatch = numberRegex.exec(text);
|
9
|
+
if (!textMatch) {
|
10
|
+
return text;
|
11
|
+
}
|
12
|
+
let decimalPlaces = -1;
|
13
|
+
for (const reference of references) {
|
14
|
+
decimalPlaces = Math.max(getLenAfterDecimal(reference), decimalPlaces);
|
15
|
+
}
|
16
|
+
if (decimalPlaces === -1) {
|
17
|
+
return toRoundedString(num);
|
18
|
+
}
|
19
|
+
// Make text's after decimal length match [afterDecimalLen].
|
20
|
+
let postDecimal = textMatch[3].substring(0, decimalPlaces);
|
21
|
+
let preDecimal = textMatch[2];
|
22
|
+
const nextDigit = textMatch[3].charAt(decimalPlaces);
|
23
|
+
if (nextDigit !== '') {
|
24
|
+
const asNumber = parseInt(nextDigit, 10);
|
25
|
+
if (asNumber >= 5) {
|
26
|
+
// Don't attempt to parseInt() an empty string.
|
27
|
+
if (postDecimal.length > 0) {
|
28
|
+
const leadingZeroMatch = /^(0+)(\d*)$/.exec(postDecimal);
|
29
|
+
let leadingZeroes = '';
|
30
|
+
let postLeading = postDecimal;
|
31
|
+
if (leadingZeroMatch) {
|
32
|
+
leadingZeroes = leadingZeroMatch[1];
|
33
|
+
postLeading = leadingZeroMatch[2];
|
34
|
+
}
|
35
|
+
postDecimal = (parseInt(postDecimal) + 1).toString();
|
36
|
+
// If postDecimal got longer, remove leading zeroes if possible
|
37
|
+
if (postDecimal.length > postLeading.length && leadingZeroes.length > 0) {
|
38
|
+
leadingZeroes = leadingZeroes.substring(1);
|
39
|
+
}
|
40
|
+
postDecimal = leadingZeroes + postDecimal;
|
41
|
+
}
|
42
|
+
if (postDecimal.length === 0 || postDecimal.length > decimalPlaces) {
|
43
|
+
preDecimal = (parseInt(preDecimal) + 1).toString();
|
44
|
+
postDecimal = postDecimal.substring(1);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
const negativeSign = textMatch[1];
|
49
|
+
return cleanUpNumber(`${negativeSign}${preDecimal}.${postDecimal}`);
|
50
|
+
};
|
51
|
+
export default toStringOfSamePrecision;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
package/dist/mjs/shapes/Path.mjs
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
import { toRoundedString, toStringOfSamePrecision } from '../rounding.mjs';
|
2
1
|
import LineSegment2 from './LineSegment2.mjs';
|
3
2
|
import Rect2 from './Rect2.mjs';
|
4
3
|
import { Vec2 } from '../Vec2.mjs';
|
5
4
|
import CubicBezier from './CubicBezier.mjs';
|
6
5
|
import QuadraticBezier from './QuadraticBezier.mjs';
|
7
6
|
import PointShape2D from './PointShape2D.mjs';
|
7
|
+
import toRoundedString from '../rounding/toRoundedString.mjs';
|
8
|
+
import toStringOfSamePrecision from '../rounding/toStringOfSamePrecision.mjs';
|
8
9
|
export var PathCommandType;
|
9
10
|
(function (PathCommandType) {
|
10
11
|
PathCommandType[PathCommandType["LineTo"] = 0] = "LineTo";
|
@@ -39,12 +39,12 @@ class Triangle extends Abstract2DShape {
|
|
39
39
|
}
|
40
40
|
// Transform, treating this as composed of 2D points.
|
41
41
|
transformed2DBy(affineTransform) {
|
42
|
-
return this.map(affineTransform.transformVec2);
|
42
|
+
return this.map(vertex => affineTransform.transformVec2(vertex));
|
43
43
|
}
|
44
44
|
// Transforms this by a linear transform --- verticies are treated as
|
45
45
|
// 3D points.
|
46
46
|
transformedBy(linearTransform) {
|
47
|
-
return this.map(linearTransform.transformVec3);
|
47
|
+
return this.map(vertex => linearTransform.transformVec3(vertex));
|
48
48
|
}
|
49
49
|
/**
|
50
50
|
* Returns the sides of this triangle, as an array of `LineSegment2`s.
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@js-draw/math",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.16.0",
|
4
4
|
"description": "A math library for js-draw. ",
|
5
5
|
"types": "./dist/mjs/lib.d.ts",
|
6
6
|
"main": "./dist/cjs/lib.js",
|
@@ -28,7 +28,7 @@
|
|
28
28
|
"bezier-js": "6.1.3"
|
29
29
|
},
|
30
30
|
"devDependencies": {
|
31
|
-
"@js-draw/build-tool": "^1.
|
31
|
+
"@js-draw/build-tool": "^1.11.1",
|
32
32
|
"@types/bezier-js": "4.1.0",
|
33
33
|
"@types/jest": "29.5.5",
|
34
34
|
"@types/jsdom": "21.1.3"
|
@@ -45,5 +45,5 @@
|
|
45
45
|
"svg",
|
46
46
|
"math"
|
47
47
|
],
|
48
|
-
"gitHead": "
|
48
|
+
"gitHead": "b0b6d7165d76582e1c197d0f56a10bfe6b46e2bc"
|
49
49
|
}
|
package/src/lib.ts
CHANGED
@@ -36,7 +36,7 @@ export { Mat33, Mat33Array } from './Mat33';
|
|
36
36
|
export { Point2, Vec2 } from './Vec2';
|
37
37
|
export { Vec3 } from './Vec3';
|
38
38
|
export { Color4 } from './Color4';
|
39
|
-
export
|
39
|
+
export * from './rounding/lib';
|
40
40
|
|
41
41
|
|
42
42
|
// Note: All above exports cannot use `export { default as ... } from "..."` because this
|