pimath 0.1.40 → 0.2.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.
Files changed (99) hide show
  1. package/dist/pimath.js +3106 -2873
  2. package/dist/pimath.js.map +1 -1
  3. package/package.json +13 -11
  4. package/src/algebra/equation.ts +113 -111
  5. package/src/algebra/equationSolver.ts +69 -120
  6. package/src/algebra/factor.ts +6 -7
  7. package/src/algebra/linearSystem.ts +97 -46
  8. package/src/algebra/logicalset.ts +51 -52
  9. package/src/algebra/monom.ts +23 -61
  10. package/src/algebra/operations.ts +0 -1
  11. package/src/algebra/polyFactor.ts +5 -5
  12. package/src/algebra/polynom.ts +69 -216
  13. package/src/analyze/index.ts +4 -0
  14. package/src/analyze/solution.ts +92 -29
  15. package/src/analyze/tableOfSigns.ts +1 -1
  16. package/src/coefficients/fraction.ts +189 -149
  17. package/src/coefficients/index.ts +1 -1
  18. package/src/coefficients/root.ts +66 -19
  19. package/src/geometry/TupleN.ts +128 -0
  20. package/src/geometry/circle.ts +308 -238
  21. package/src/geometry/geomMath.ts +4 -3
  22. package/src/geometry/index.ts +1 -0
  23. package/src/geometry/line.ts +221 -245
  24. package/src/geometry/line3.ts +78 -73
  25. package/src/geometry/plane3.ts +64 -55
  26. package/src/geometry/point.ts +57 -19
  27. package/src/geometry/triangle.ts +376 -248
  28. package/src/geometry/vector.ts +113 -229
  29. package/src/index.ts +13 -12
  30. package/src/numeric.ts +6 -9
  31. package/src/pimath.interface.ts +30 -28
  32. package/src/randomization/algebra/rndPolynom.ts +29 -15
  33. package/src/randomization/coefficient/rndFraction.ts +3 -3
  34. package/src/randomization/geometry/rndLine.ts +8 -10
  35. package/src/randomization/random.ts +11 -13
  36. package/src/randomization/rndTypes.ts +16 -12
  37. package/types/algebra/equation.d.ts +18 -17
  38. package/types/algebra/equation.d.ts.map +1 -1
  39. package/types/algebra/equationSolver.d.ts +5 -4
  40. package/types/algebra/equationSolver.d.ts.map +1 -1
  41. package/types/algebra/factor.d.ts +1 -1
  42. package/types/algebra/factor.d.ts.map +1 -1
  43. package/types/algebra/linearSystem.d.ts +23 -6
  44. package/types/algebra/linearSystem.d.ts.map +1 -1
  45. package/types/algebra/logicalset.d.ts +1 -1
  46. package/types/algebra/logicalset.d.ts.map +1 -1
  47. package/types/algebra/monom.d.ts +1 -6
  48. package/types/algebra/monom.d.ts.map +1 -1
  49. package/types/algebra/operations.d.ts.map +1 -1
  50. package/types/algebra/polyFactor.d.ts +4 -4
  51. package/types/algebra/polyFactor.d.ts.map +1 -1
  52. package/types/algebra/polynom.d.ts +10 -7
  53. package/types/algebra/polynom.d.ts.map +1 -1
  54. package/types/analyze/index.d.ts +2 -0
  55. package/types/analyze/index.d.ts.map +1 -0
  56. package/types/analyze/solution.d.ts +14 -8
  57. package/types/analyze/solution.d.ts.map +1 -1
  58. package/types/coefficients/fraction.d.ts +14 -12
  59. package/types/coefficients/fraction.d.ts.map +1 -1
  60. package/types/coefficients/index.d.ts +1 -1
  61. package/types/coefficients/index.d.ts.map +1 -1
  62. package/types/coefficients/root.d.ts +3 -0
  63. package/types/coefficients/root.d.ts.map +1 -1
  64. package/types/geometry/TupleAbstract.d.ts +22 -0
  65. package/types/geometry/TupleAbstract.d.ts.map +1 -0
  66. package/types/geometry/TupleN.d.ts +24 -0
  67. package/types/geometry/TupleN.d.ts.map +1 -0
  68. package/types/geometry/circle.d.ts +26 -17
  69. package/types/geometry/circle.d.ts.map +1 -1
  70. package/types/geometry/geomMath.d.ts +2 -1
  71. package/types/geometry/geomMath.d.ts.map +1 -1
  72. package/types/geometry/index.d.ts.map +1 -1
  73. package/types/geometry/line.d.ts +21 -30
  74. package/types/geometry/line.d.ts.map +1 -1
  75. package/types/geometry/line3.d.ts +19 -19
  76. package/types/geometry/line3.d.ts.map +1 -1
  77. package/types/geometry/matrix.d.ts +11 -11
  78. package/types/geometry/plane3.d.ts +10 -10
  79. package/types/geometry/plane3.d.ts.map +1 -1
  80. package/types/geometry/point.d.ts +11 -6
  81. package/types/geometry/point.d.ts.map +1 -1
  82. package/types/geometry/triangle.d.ts +68 -23
  83. package/types/geometry/triangle.d.ts.map +1 -1
  84. package/types/geometry/vector.d.ts +24 -44
  85. package/types/geometry/vector.d.ts.map +1 -1
  86. package/types/index.d.ts +5 -4
  87. package/types/index.d.ts.map +1 -1
  88. package/types/numeric.d.ts.map +1 -1
  89. package/types/pimath.interface.d.ts +18 -24
  90. package/types/pimath.interface.d.ts.map +1 -1
  91. package/types/randomization/algebra/rndPolynom.d.ts.map +1 -1
  92. package/types/randomization/coefficient/rndFraction.d.ts +1 -1
  93. package/types/randomization/coefficient/rndFraction.d.ts.map +1 -1
  94. package/types/randomization/geometry/rndLine.d.ts.map +1 -1
  95. package/types/randomization/random.d.ts +3 -2
  96. package/types/randomization/random.d.ts.map +1 -1
  97. package/types/randomization/rndTypes.d.ts +15 -10
  98. package/types/randomization/rndTypes.d.ts.map +1 -1
  99. package/src/coefficients/nthRoot.ts +0 -149
@@ -1,27 +1,27 @@
1
- import { Fraction } from "../coefficients/fraction"
2
- import { Line } from "./line"
3
- import { Vector } from "./vector"
4
- import { Point } from "./point"
5
- import type {remarquableLines} from "../pimath.interface"
1
+ import {Fraction} from "../coefficients"
2
+ import {Line} from "./line"
3
+ import {Vector} from "./vector"
4
+ import {Point} from "./point"
5
+ import type {InputValue, remarquableLines} from "../pimath.interface"
6
+ import {Numeric} from "../numeric"
6
7
 
8
+ type TRIANGLE_SIDES = 'AB' | 'AC' | 'BC'
9
+
10
+ // TODO: add a check if it's a triangle or not.
7
11
  export class Triangle {
12
+ // This defines the triangle
8
13
  #A: Point = new Point()
9
14
  #B: Point = new Point()
10
15
  #C: Point = new Point()
11
- #lines: { 'AB': Line, 'AC': Line, 'BC': Line } = {
12
- 'AB': new Line(),
13
- 'AC': new Line(),
14
- 'BC': new Line()
15
- }
16
- #middles: { 'AB': Point, 'AC': Point, 'BC': Point } = {
17
- 'AB': new Point(),
18
- 'AC': new Point(),
19
- 'BC': new Point()
16
+ #isValid = false
17
+ // This is calculated
18
+ #lines: Record<TRIANGLE_SIDES, Line> | null = null
19
+ #radians = true
20
+ #remarquables: remarquableLines = {
21
+ mediators: null, medians: null, heights: null, externalBisectors: null, bisectors: null
20
22
  }
21
- #remarquables: remarquableLines | null = null
22
23
 
23
24
  constructor(...values: unknown[]) {
24
-
25
25
  if (values.length > 0) {
26
26
  this.parse(...values)
27
27
  }
@@ -29,26 +29,99 @@ export class Triangle {
29
29
  return this
30
30
  }
31
31
 
32
- // ------------------------------------------
33
- // Getter and setters
34
- // ------------------------------------------
32
+ parse = (...values: unknown[]): this => {
35
33
 
36
- get A(): Point {
37
- return this.#A
34
+ if (values.length === 1) {
35
+ if (values[0] instanceof Triangle) {
36
+ return this.copy(values[0])
37
+ }
38
+ }
39
+
40
+ if (values.length === 3) {
41
+ // Possibilities:
42
+ // - Three points (or part of points, only dict for example, or array
43
+ // - Three lines
44
+ // - Three lines as text.
45
+ if (values.every((x: unknown) => typeof x === 'string')) {
46
+ // Three lines as text.
47
+ const [a, b, c] = values
48
+ return this.fromLines(a, b, c)
49
+ }
50
+
51
+ if (values.every((x: unknown) => x instanceof Line)) {
52
+ // We have three lines
53
+ return this.fromLines(values[0], values[1], values[2])
54
+ }
55
+
56
+ if (values.every((x: unknown) => (x instanceof Point))) {
57
+ return this.fromPoints(values[0], values[1], values[2])
58
+ }
59
+ }
60
+
61
+ if (values.length === 6) {
62
+ const v: Fraction[] = values.map((x: unknown) => new Fraction(x as string))
63
+
64
+ if (v.some(x => x.isNaN())) {
65
+ throw new Error('One of the values is not a valid number')
66
+ }
67
+
68
+ return this.fromCoordinates(v[0], v[1], v[2], v[3], v[4], v[5])
69
+ }
70
+
71
+ return this
38
72
  }
39
73
 
40
- get B(): Point {
41
- return this.#B
74
+ /**
75
+ * Clone the Triangle class
76
+ */
77
+ clone = (): Triangle => {
78
+ return new Triangle(
79
+ this.#A.clone(),
80
+ this.#B.clone(),
81
+ this.#C.clone()
82
+ )
42
83
  }
43
84
 
44
- get C(): Point {
45
- return this.#C
85
+ /**
86
+ * Copy the values from another triangle
87
+ * @param value
88
+ */
89
+ copy(value: Triangle): this {
90
+ this.#A = value.A.clone()
91
+ this.#B = value.B.clone()
92
+ this.#C = value.C.clone()
93
+
94
+ return this
95
+ }
96
+
97
+ get A(): Point {
98
+ return this.#A
99
+ }
100
+
101
+ set A(value: Point) {
102
+ this.#A = value
103
+ this.#A.onChange = () => this.reset()
104
+ this.reset()
46
105
  }
47
106
 
48
107
  get AB(): Vector {
49
108
  return this.#getSegment('A', 'B')
50
109
  }
51
110
 
111
+ get AC(): Vector {
112
+ return this.#getSegment('A', 'C')
113
+ }
114
+
115
+ get B(): Point {
116
+ return this.#B
117
+ }
118
+
119
+ set B(value: Point) {
120
+ this.#B = value
121
+ this.#B.onChange = () => this.reset()
122
+ this.reset()
123
+ }
124
+
52
125
  get BA(): Vector {
53
126
  return this.#getSegment('B', 'A')
54
127
  }
@@ -57,298 +130,353 @@ export class Triangle {
57
130
  return this.#getSegment('B', 'C')
58
131
  }
59
132
 
60
- get CB(): Vector {
61
- return this.#getSegment('C', 'B')
133
+ get C(): Point {
134
+ return this.#C
62
135
  }
63
136
 
64
- get AC(): Vector {
65
- return this.#getSegment('A', 'C')
137
+ set C(value: Point) {
138
+ this.#C = value
139
+ this.#C.onChange = () => this.reset()
140
+ this.reset()
66
141
  }
67
142
 
68
143
  get CA(): Vector {
69
144
  return this.#getSegment('C', 'A')
70
145
  }
71
146
 
72
- get isRectangle(): boolean {
73
- if (this.AB.isNormalTo(this.BC)) {
74
- return true
147
+ get CB(): Vector {
148
+ return this.#getSegment('C', 'B')
149
+ }
150
+
151
+ get asDegree(): this {
152
+ this.#radians = false
153
+ return this
154
+ }
155
+
156
+ get asRadians(): this {
157
+ this.#radians = true
158
+ return this
159
+ }
160
+
161
+ fromCoordinates(
162
+ x1: InputValue<Fraction>, y1: InputValue<Fraction>,
163
+ x2: InputValue<Fraction>, y2: InputValue<Fraction>,
164
+ x3: InputValue<Fraction>, y3: InputValue<Fraction>): this {
165
+
166
+ return this.fromPoints(
167
+ new Point(x1, y1),
168
+ new Point(x2, y2),
169
+ new Point(x3, y3),
170
+ )
171
+ }
172
+
173
+ fromLines(line1: Line | string, line2: Line | string, line3: Line | string): this {
174
+ const AB: Line = new Line(line1).clone()
175
+ const BC: Line = new Line(line2).clone()
176
+ const AC: Line = new Line(line3).clone()
177
+
178
+ // Get the intersection points -> build the triangle using these intersection points.
179
+ let intersect = AB.intersection(BC)
180
+ if (intersect.hasIntersection) {
181
+ this.#B = intersect.point
182
+ } else {
183
+ this.#isValid = false
184
+ return this
75
185
  }
76
- if (this.AB.isNormalTo(this.AC)) {
77
- return true
186
+
187
+ intersect = BC.intersection(AC)
188
+ if (intersect.hasIntersection) {
189
+ this.#C = intersect.point
190
+ } else {
191
+ this.#isValid = false
192
+ return this
78
193
  }
79
- if (this.BC.isNormalTo(this.AC)) {
80
- return true
194
+
195
+ intersect = AC.intersection(AB)
196
+ if (intersect.hasIntersection) {
197
+ this.#A = intersect.point
198
+ } else {
199
+ this.#isValid = false
200
+ return this
81
201
  }
82
202
 
83
- return false
84
- }
203
+ // reset the remarquables lines.
204
+ this.reset()
85
205
 
86
- get isEquilateral(): boolean {
87
- return this.AB.normSquare.isEqual(this.BC.normSquare) &&
88
- this.AB.normSquare.isEqual(this.AC.normSquare)
89
- }
206
+ // Force the use of the given lines.
207
+ this.#lines = {AB, AC, BC}
208
+
209
+ return this
90
210
 
91
- get isIsocele(): boolean {
92
- return this.AB.normSquare.isEqual(this.BC.normSquare) ||
93
- this.AB.normSquare.isEqual(this.AC.normSquare) ||
94
- this.BC.normSquare.isEqual(this.AC.normSquare)
95
211
  }
96
212
 
97
- get lines(): { 'AB': Line, 'BC': Line, 'AC': Line } {
98
- return this.#lines
213
+ fromPoints(A: Point, B: Point, C: Point): this {
214
+ // We have three points.
215
+ this.#A = A.clone()
216
+ this.#B = B.clone()
217
+ this.#C = C.clone()
218
+
219
+ this.reset()
220
+
221
+ return this
99
222
  }
100
223
 
101
- get remarquables(): remarquableLines | null {
102
- return this.#remarquables
224
+ getAngle(name: 'A' | 'B' | 'C'): number {
225
+ const a = this.BC.norm
226
+ const b = this.AC.norm
227
+ const c = this.AB.norm
228
+
229
+ if (name === 'A') {
230
+ return this.#cosThm(a, b, c)
231
+ }
232
+
233
+ if (name === 'C') {
234
+ return this.#cosThm(c, b, a)
235
+ }
236
+
237
+ return this.#cosThm(b, a, c)
103
238
  }
104
239
 
105
- // ------------------------------------------
106
- // Creation / parsing functions
107
- // ------------------------------------------
108
240
 
109
- /**
110
- * Parse values to a triangle. Supported formats:
111
- * Vector2D, Vector2D, Vector2D
112
- * x1, y1, x2, y2, x3, y3
113
- * @param values
114
- */
115
- parse = (...values: unknown[]): Triangle => {
116
- if (values.length === 6) {
117
- // Check if all values are number or fractions.
118
- const v: Fraction[] = values.map((x: unknown) => new Fraction(x as string))
241
+ getBisectors(internal = true): { 'A': Line, 'B': Line, 'C': Line, 'intersection': Point | null } {
119
242
 
120
- if (v.some(x => x.isNaN())) {
121
- throw new Error('One of the values is not a valid number')
122
- }
243
+ if (!this.#remarquables.bisectors) {
244
+ const A = this.#calculateBisectors('A', internal)
245
+ const B = this.#calculateBisectors('B', internal)
246
+ const C = this.#calculateBisectors('C', internal)
123
247
 
124
- return this.parse(
125
- new Vector(v[0], v[1]),
126
- new Vector(v[2], v[3]),
127
- new Vector(v[4], v[5]),
128
- )
129
- } else if (values.length === 3) {
130
- // Possibilities:
131
- // - Three points (or part of points, only dict for example, or array
132
- // - Three lines
133
- // - Three lines as text.
134
- if (values.every((x: unknown) => typeof x === 'string')) {
135
- // Three lines as text.
136
- return this.parse(
137
- ...values.map((x) => {
138
- return new Line(x)
139
- })
140
- )
141
- } else if (values.every((x: unknown) => x instanceof Line)) {
142
- // We have three lines
143
- const AB: Line = (values[0]).clone()
144
- const BC: Line = (values[1]).clone()
145
- const AC: Line = (values[2]).clone()
146
- this.#lines = { AB, BC, AC }
147
-
148
- // Get the intersection points -> build the triangle using these intersection points.
149
- let intersect = AB.intersection(BC)
150
- if (intersect.hasIntersection) {
151
- this.#B = intersect.point.clone()
152
- } else {
153
- throw new Error('Lines do not intersect !')
154
- }
155
-
156
- intersect = BC.intersection(AC)
157
- if (intersect.hasIntersection) {
158
- this.#C = intersect.point.clone()
159
- } else {
160
- throw new Error('Lines do not intersect !')
161
- }
162
-
163
- intersect = AC.intersection(AB)
164
- if (intersect.hasIntersection) {
165
- this.#A = intersect.point.clone()
166
- } else {
167
- throw new Error('Lines do not intersect !')
168
- }
169
-
170
- } else if (values.every((x: unknown) => (x instanceof Point))) {
171
- // We have three points.
172
- this.#A = (values[0]).clone()
173
- this.#B = (values[1]).clone()
174
- this.#C = (values[2]).clone()
175
- this.#lines = {
176
- 'AB': new Line(this.#A, this.#B),
177
- 'BC': new Line(this.#B, this.#C),
178
- 'AC': new Line(this.#A, this.#C)
179
- }
180
- }
181
- } else if (values.length === 1) {
182
- if (values[0] instanceof Triangle) {
183
- return values[0].clone()
184
- }
248
+ const intersection = A.intersection(B).point
249
+ this.#remarquables.bisectors = {A, B, C, intersection}
185
250
  }
186
251
 
187
- this.#updateTriangle()
188
- return this
252
+ return this.#remarquables.bisectors
189
253
  }
190
254
 
191
- /**
192
- * Clone the Triangle class
193
- */
194
- clone = (): Triangle => {
195
- return new Triangle(
196
- this.#A.clone(),
197
- this.#B.clone(),
198
- this.#C.clone()
199
- )
255
+ getHeights(): { 'A': Line, 'B': Line, 'C': Line, 'intersection': Point | null } {
256
+
257
+ if (!this.#remarquables.heights) {
258
+ const A = new Line().fromPointAndNormal(this.#A, new Vector(this.#B, this.#C))
259
+ const B = new Line().fromPointAndNormal(this.#B, new Vector(this.#A, this.#C))
260
+ const C = new Line().fromPointAndNormal(this.#C, new Vector(this.#A, this.#B))
261
+
262
+ const intersection = A.intersection(B).point
263
+ this.#remarquables.heights = {A, B, C, intersection}
264
+ }
265
+
266
+ return this.#remarquables.heights
200
267
  }
201
268
 
269
+ getMedians(): { 'A': Line, 'B': Line, 'C': Line, 'intersection': Point | null } {
270
+ const middles = this.getMiddles()
202
271
 
203
- // ------------------------------------------
204
- // Triangle operations and properties
205
- // ------------------------------------------
272
+ if (!this.#remarquables.medians) {
273
+ const A = new Line().fromPoints(this.#A, middles.BC)
274
+ const B = new Line().fromPoints(this.#B, middles.AC)
275
+ const C = new Line().fromPoints(this.#C, middles.AB)
206
276
 
207
- /**
208
- * Generate the Line object for the three segments of the triangle
209
- */
210
- #updateTriangle = () => {
211
- this.#A.asPoint = true
212
- this.#B.asPoint = true
213
- this.#C.asPoint = true
277
+ const intersection = A.intersection(B).point
278
+ this.#remarquables.medians = {A, B, C, intersection}
279
+ }
280
+
281
+ return this.#remarquables.medians
282
+ }
283
+
284
+ getMediators(): { 'a': Line, 'b': Line, 'c': Line, 'intersection': Point | null } {
285
+ const middles = this.getMiddles()
214
286
 
215
- this.#middles = {
287
+ if (!this.#remarquables.mediators) {
288
+ const a = new Line().fromPointAndNormal(middles.BC, new Vector(this.#B, this.#C))
289
+ const b = new Line().fromPointAndNormal(middles.AC, new Vector(this.#A, this.#C))
290
+ const c = new Line().fromPointAndNormal(middles.AB, new Vector(this.#A, this.#B))
291
+
292
+ const intersection = a.intersection(b).point
293
+ this.#remarquables.mediators = {a, b, c, intersection}
294
+ }
295
+
296
+ return this.#remarquables.mediators
297
+ }
298
+
299
+ getMiddles() {
300
+ return {
216
301
  'AB': new Point().middleOf(this.#A, this.#B),
217
302
  'AC': new Point().middleOf(this.#A, this.#C),
218
303
  'BC': new Point().middleOf(this.#B, this.#C)
219
304
  }
305
+ }
220
306
 
221
- this.#remarquables = this.#calculateRemarquableLines()
307
+ getPoints(): Point[] {
308
+ return [this.A, this.B, this.C]
222
309
  }
223
310
 
311
+ getSortedPoints(): Point[] {
312
+ return this.getPoints().sort((a, b) => {
313
+ return a.x.value === b.x.value
314
+ ? a.y.value - b.y.value
315
+ : a.x.value - b.x.value
316
+ })
317
+ }
224
318
 
225
- /**
226
- * Get the Vector2D class for the given name
227
- * @param ptName
228
- */
229
- #getPointByName = (ptName: string): Point => {
230
- switch (ptName.toUpperCase()) {
231
- case 'A':
232
- return this.#A
233
- case 'B':
234
- return this.#B
235
- case 'C':
236
- return this.#C
237
- }
319
+ isEqual(T: Triangle): boolean {
320
+ if (!this.#isValid || !T.isValid) return false
321
+
322
+ // TODO: compare points in a particular order.
323
+ const tri1 = this.getSortedPoints()
324
+ const tri2 = T.getSortedPoints()
325
+
326
+ return tri1.every((_, index) => tri1[index].isEqual(tri2[index]))
238
327
 
239
- // Something went wrong ! Return the first point
240
- return this.#A
241
328
  }
242
- /**
243
- * Get the vector for the segment given by name.
244
- * @param ptName1
245
- * @param ptName2
246
- */
247
- #getSegment = (ptName1: string, ptName2: string): Vector => {
248
- return new Vector(
249
- this.#getPointByName(ptName1),
250
- this.#getPointByName(ptName2)
251
- )
329
+
330
+ isEquilateral(): boolean {
331
+ if (!this.#isValid) return false
332
+
333
+ const dAB = this.AB.normSquare.value
334
+ const dBC = this.BC.normSquare.value
335
+ const dAC = this.AC.normSquare.value
336
+
337
+ return (dAB === dBC) && (dAB === dAC)
252
338
  }
253
339
 
254
- #calculateRemarquableLines = (): remarquableLines => {
340
+ isIsocele(): boolean {
341
+ if (!this.#isValid) return false
255
342
 
256
- const medians = {
257
- 'A': new Line().fromPoints(this.#A, this.#middles.BC),
258
- 'B': new Line().fromPoints(this.#B, this.#middles.AC),
259
- 'C': new Line().fromPoints(this.#C, this.#middles.AB),
260
- 'intersection': null
261
- }
343
+ const dAB = this.AB.normSquare.value
344
+ const dBC = this.BC.normSquare.value
345
+ const dAC = this.AC.normSquare.value
262
346
 
263
- const mediators = {
264
- 'AB': new Line().fromPointAndNormal(this.#middles.AB, new Vector(this.#A, this.#B).normal()),
265
- 'AC': new Line().fromPointAndNormal(this.#middles.AC, new Vector(this.#A, this.#C).normal()),
266
- 'BC': new Line().fromPointAndNormal(this.#middles.BC, new Vector(this.#B, this.#C).normal()),
267
- 'intersection': null
268
- }
347
+ return dAB === dBC ||
348
+ dAB === dAC ||
349
+ dBC === dAC
350
+ }
269
351
 
270
- const heights = {
271
- 'A': new Line().fromPointAndNormal(this.#A, new Vector(this.#B, this.#C).normal()),
272
- 'B': new Line().fromPointAndNormal(this.#B, new Vector(this.#A, this.#C).normal()),
273
- 'C': new Line().fromPointAndNormal(this.#C, new Vector(this.#A, this.#B).normal()),
274
- 'intersection': null
275
- }
352
+ isRectangle(): boolean {
353
+ if (!this.#isValid) return false
276
354
 
277
- const bA = this.#calculateBisectors('A'),
278
- bB = this.#calculateBisectors('B'),
279
- bC = this.#calculateBisectors('C')
355
+ return this.AB.isNormalTo(this.BC) ||
356
+ this.AB.isNormalTo(this.AC) ||
357
+ this.BC.isNormalTo(this.AC)
358
+ }
280
359
 
281
- const bisectors = {
282
- 'A': bA.internal,
283
- 'B': bB.internal,
284
- 'C': bB.internal,
285
- 'intersection': null
286
- }
360
+ get isValid(): boolean {
361
+ return this.#isValid
362
+ }
287
363
 
288
- const externalBisectors = {
289
- 'A': bA.external,
290
- 'B': bB.external,
291
- 'C': bC.external,
292
- 'intersection': null
293
- }
364
+ set isValid(value: boolean) {
365
+ this.#isValid = value
366
+ }
294
367
 
295
- const remarquables: remarquableLines = {
296
- medians,
297
- mediators,
298
- heights,
299
- bisectors,
300
- externalBisectors
368
+ get lines(): Record<TRIANGLE_SIDES, Line> {
369
+ if (this.#lines === null) {
370
+ this.#lines = {
371
+ 'AB': new Line(this.#A, this.#B),
372
+ 'BC': new Line(this.#B, this.#C),
373
+ 'AC': new Line(this.#A, this.#C)
374
+ }
301
375
  }
302
376
 
303
- // As it's a triangle, we assume the lines are intersecting and aren't parallel or superposed.
304
- remarquables.medians.intersection = remarquables.medians.A.intersection(remarquables.medians.B).point
305
- remarquables.mediators.intersection = remarquables.mediators.AB.intersection(remarquables.mediators.BC).point
306
- remarquables.heights.intersection = remarquables.heights.A.intersection(remarquables.heights.B).point
307
- remarquables.bisectors.intersection = remarquables.bisectors.A.intersection(remarquables.bisectors.B).point
377
+ return this.#lines
378
+ }
308
379
 
309
- // Everything was calculated for the remarquable lines.
310
- return remarquables
380
+ medianA(): Line {
381
+ return this.getMedians().A
311
382
  }
312
383
 
313
- #calculateBisectors = (pt: string): { internal: Line, external: Line } => {
384
+ medianB(): Line {
385
+ return this.getMedians().B
386
+ }
387
+
388
+ medianC(): Line {
389
+ return this.getMedians().C
390
+ }
391
+
392
+ get remarquables(): remarquableLines | null {
393
+ return this.#remarquables
394
+ }
395
+
396
+ public reset(): this {
397
+ // Check if the triangle is valid
398
+ // the three points must NOT be aligned.
399
+ this.#isValid = !this.AB.isColinearTo(this.AC)
400
+
401
+ this.#lines = null
402
+ this.#remarquables = {
403
+ mediators: null,
404
+ medians: null,
405
+ heights: null,
406
+ externalBisectors: null,
407
+ bisectors: null
408
+ }
409
+
410
+ return this
411
+ }
412
+
413
+ #calculateBisectors(pt: string, internal = true): Line {
314
414
  const tlines = this.lines
315
- let d1, d2
415
+ let d1: Vector = new Vector()
416
+ let d2: Vector = new Vector()
417
+ let P: Point = new Point()
316
418
 
317
419
  if (pt === 'A') {
318
- d1 = tlines.AB
319
- d2 = tlines.AC
420
+ P = this.A.clone()
421
+ d1 = tlines.AB.clone().d
422
+ d2 = tlines.AC.clone().d
320
423
  } else if (pt === 'B') {
321
- d1 = tlines.AB
322
- d2 = tlines.BC
424
+ P = this.B.clone()
425
+ d1 = tlines.AB.clone().d.opposite()
426
+ d2 = tlines.BC.clone().d
323
427
  } else if (pt === 'C') {
324
- d1 = tlines.BC
325
- d2 = tlines.AC
428
+ P = this.C.clone()
429
+ d1 = tlines.BC.clone().d.opposite()
430
+ d2 = tlines.AC.clone().d.opposite()
326
431
  }
327
432
 
328
433
  if (d1 === undefined || d2 === undefined) {
329
434
  throw new Error(`The point ${pt} does not exist`)
330
435
  }
331
436
 
332
- const d1n = d1.n.simplify().norm
333
- const d2n = d2.n.simplify().norm
334
- const d1Equ = d1.getEquation().multiply(d2n)
335
- const d2Equ = d2.getEquation().multiply(d1n)
437
+ const director = internal
438
+ ? d1.unit().add(d2.unit())
439
+ : d1.unit().subtract(d2.unit())
336
440
 
337
- const b1: Line = new Line(d1Equ.clone().subtract(d2Equ).simplify())
338
- const b2: Line = new Line(d2Equ.clone().subtract(d1Equ).simplify())
441
+ return new Line().fromPointAndDirection(P, director)
442
+ }
339
443
 
340
- // Must determine which bisectors is in the triangle
341
- if (pt === 'A') {
342
- return b1.hitSegment(this.B, this.C) ? { internal: b1, external: b2 } : { internal: b2, external: b1 }
343
- }
344
- if (pt === 'B') {
345
- return b1.hitSegment(this.A, this.C) ? { internal: b1, external: b2 } : { internal: b2, external: b1 }
346
- }
347
- if (pt === 'C') {
348
- return b1.hitSegment(this.B, this.A) ? { internal: b1, external: b2 } : { internal: b2, external: b1 }
444
+ #cosThm(opposite: number, adjacent1: number, adjacent2: number): number {
445
+ const ratio = ((adjacent1 ** 2 + adjacent2 ** 2) - opposite ** 2) / (2 * adjacent1 * adjacent2)
446
+
447
+ return this.#radians
448
+ ? Math.acos(ratio)
449
+ : Numeric.numberCorrection(Math.acos(ratio) * 180 / Math.PI)
450
+ }
451
+
452
+ /**
453
+ * Get the Point class for the given name
454
+ * @param ptName
455
+ */
456
+ #getPointByName = (ptName: string): Point => {
457
+ switch (ptName.toUpperCase()) {
458
+ case 'A':
459
+ return this.#A
460
+ case 'B':
461
+ return this.#B
462
+ case 'C':
463
+ return this.#C
349
464
  }
350
465
 
351
- // Default returns the first bisector
352
- return { internal: b1, external: b2 }
466
+ // Something went wrong ! Return the first point
467
+ return this.#A
468
+ }
469
+
470
+ /**
471
+ * Get the vector for the segment given by name.
472
+ * @param ptName1
473
+ * @param ptName2
474
+ */
475
+ #getSegment = (ptName1: string, ptName2: string): Vector => {
476
+ return new Vector(
477
+ this.#getPointByName(ptName1),
478
+ this.#getPointByName(ptName2)
479
+ )
353
480
  }
354
- }
481
+
482
+ }