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
@@ -3,35 +3,40 @@
3
3
  */
4
4
 
5
5
  import {Numeric} from "../numeric"
6
- import {Fraction} from "../coefficients/fraction"
7
- import {Equation} from "../algebra/equation"
8
- import {Polynom} from "../algebra/polynom"
9
- import {Monom} from "../algebra/monom"
6
+ import {Fraction} from "../coefficients"
7
+ import {Equation, Monom, Polynom} from "../algebra"
10
8
  import {Vector} from "./vector"
11
9
  import {type InputValue, type IPiMathObject, LinePropriety} from "../pimath.interface"
12
10
  import {randomIntSym} from "../randomization/rndHelpers"
13
11
  import {Point} from "./point"
12
+ import {Root} from "../coefficients/root"
13
+
14
+ enum LINE_DISPLAY {
15
+ CARTESIAN,
16
+ CANONICAL,
17
+ MXH,
18
+ PARAMETRIC,
19
+ SYSTEM
20
+ }
14
21
 
15
- export interface LineConfig {
16
- direction?: Vector,
17
- normal?: Vector
18
- point?: Point,
19
- points?: Point[],
22
+ enum LINE_DIRECTION {
23
+ LEFT_TO_RIGHT = 'lr',
24
+ RIGHT_TO_LEFT = 'rl'
20
25
  }
21
26
 
22
27
  export class Line implements IPiMathObject<Line> {
23
28
  static PARALLEL = LinePropriety.Parallel
24
- // A line is defined as the canonical form
25
29
  static PERPENDICULAR = LinePropriety.Perpendicular
26
30
  #OA: Vector
27
31
  // ax + by + c = 0
28
32
  #a: Fraction
33
+
34
+ // A line is defined as the canonical form
29
35
  #b: Fraction
30
36
  #c: Fraction
31
- #d: Vector
32
- #n: Vector
33
- #outputMode: 'canonical' | 'equation' | 'mxh' | 'parametric' | 'system' = "canonical"
34
- #reduceBeforeDisplay: boolean
37
+ #orientation: LINE_DIRECTION
38
+ // output mode.
39
+ #outputMode: LINE_DISPLAY = LINE_DISPLAY.CANONICAL
35
40
 
36
41
  /**
37
42
  * Value can be a mix of:
@@ -43,10 +48,7 @@ export class Line implements IPiMathObject<Line> {
43
48
  this.#b = new Fraction().zero()
44
49
  this.#c = new Fraction().zero()
45
50
  this.#OA = new Vector()
46
- this.#d = new Vector()
47
- this.#n = new Vector()
48
-
49
- this.#reduceBeforeDisplay = true
51
+ this.#orientation = LINE_DIRECTION.LEFT_TO_RIGHT
50
52
 
51
53
  if (values.length > 0) {
52
54
  this.parse(...values)
@@ -72,53 +74,36 @@ export class Line implements IPiMathObject<Line> {
72
74
  if (values[0] instanceof Line) {
73
75
  // Already a Line
74
76
  return this.fromCoefficient(values[0].a, values[0].b, values[0].c)
75
- } else if (values[0] instanceof Equation) {
77
+ }
78
+
79
+ if (values[0] instanceof Equation) {
76
80
  // It's an Equation
77
81
  return this.fromEquation(values[0])
78
- } else if (typeof values[0] === "string") {
82
+ }
83
+
84
+ if (typeof values[0] === "string") {
79
85
  // It's a string - create an Equation from it.
80
86
  try {
81
87
  const E = new Equation(values[0])
82
88
  return this.parse(E)
83
89
  } catch (e) {
90
+ console.warn(e)
84
91
  return this
85
92
  }
86
93
  }
87
94
  }
88
95
 
89
- // Two values are given: two vectors
90
- if (values.length === 2 && values.every(x=>x instanceof Vector)) {
91
- const formattedValues: Vector[] = values
92
-
93
- if (formattedValues[0].asPoint && formattedValues[1].asPoint) {
94
- // Two points
95
- return this.fromPointAndDirection(formattedValues[0], new Vector(formattedValues[0], formattedValues[1]))
96
+ if (values.length === 2) {
97
+ if (values[0] instanceof Point && values[1] instanceof Point) {
98
+ return this.fromPoints(values[0], values[1])
96
99
  }
97
100
 
98
- if (formattedValues[0].asPoint && !formattedValues[1].asPoint) {
99
- // One point and one vector director
100
- return this.fromPointAndDirection(formattedValues[0], formattedValues[1])
101
+ if (values[0] instanceof Point && values[1] instanceof Vector) {
102
+ return this.fromPointAndDirection(values[0], values[1])
101
103
  }
102
-
103
104
  }
104
105
 
105
- if (values.length === 3) {
106
- if (values[0] instanceof Vector && values[1] instanceof Vector) {
107
- if (values[2] === LinePropriety.Perpendicular) {
108
- return this.fromPointAndNormal(values[0], values[1])
109
- } else if (values[2] === LinePropriety.Parallel) {
110
- return this.fromPointAndDirection(values[0], values[1])
111
- }
112
- }
113
-
114
- if (values[0] instanceof Vector && values[1] instanceof Line) {
115
- if (values[2] === LinePropriety.Parallel || values[2] === null) {
116
- return this.fromPointAndLine(values[0], values[1], LinePropriety.Parallel)
117
- } else {
118
- return this.fromPointAndLine(values[0], values[1], LinePropriety.Perpendicular)
119
- }
120
- }
121
-
106
+ if (values.length === 3 && values.every(x => Fraction.isFraction(x as InputValue<Fraction>))) {
122
107
  return this.fromCoefficient(
123
108
  values[0] as InputValue<Fraction>,
124
109
  values[1] as InputValue<Fraction>,
@@ -126,24 +111,16 @@ export class Line implements IPiMathObject<Line> {
126
111
  )
127
112
  }
128
113
 
129
- console.log('Something wrong happened while creating the line')
130
- console.log(values)
114
+ console.warn('Something wrong happened while creating the line')
115
+ console.warn(values)
131
116
  return this
132
117
  }
133
118
 
134
119
  // ------------------------------------------
135
120
  // Getter and setter
136
121
 
137
- clone = (): this => {
138
- this.#a = this.#a.clone()
139
- this.#b = this.#b.clone()
140
- this.#c = this.#c.clone()
141
-
142
- this.#d = this.#d.clone()
143
- this.#OA = this.#OA.clone()
144
- this.#n = this.#n.clone()
145
-
146
- return this
122
+ clone = (): Line => {
123
+ return new Line().fromCoefficient(this.#a, this.#b, this.#c)
147
124
  }
148
125
 
149
126
  get tex(): string {
@@ -151,45 +128,41 @@ export class Line implements IPiMathObject<Line> {
151
128
  // mxh => y = -a/b x - c/b
152
129
  // parametric => (xy) = OA + k*d
153
130
  // equation => ax + by = -c
131
+ // system => \begin{}...
132
+
154
133
  const output = this.#outputMode
155
- this.#outputMode = 'canonical'
134
+ this.#outputMode = LINE_DISPLAY.CANONICAL
135
+
156
136
  switch (output) {
157
- case 'equation':
137
+ case LINE_DISPLAY.CARTESIAN:
158
138
  return this.getEquation().reorder().tex
159
- case 'mxh':
139
+ case LINE_DISPLAY.MXH:
160
140
  return this.slope.isInfinity() ?
161
141
  'x=' + this.OA.x.tex :
162
142
  'y=' + new Polynom().parse('x', this.slope, this.height).tex
163
- case 'parametric':
164
- case 'system': {
165
- const d = this.#d.clone()
166
- if (this.#reduceBeforeDisplay) {
167
- d.simplify()
168
- }
143
+ case LINE_DISPLAY.PARAMETRIC:
144
+ case LINE_DISPLAY.SYSTEM: {
145
+ const d = this.d.clone().simplify()
169
146
 
170
- if (output === 'parametric') {
147
+ if (output === LINE_DISPLAY.PARAMETRIC) {
171
148
  return `${Vector.asTex('x', 'y')} = ${Vector.asTex(this.#OA.x.tex, this.#OA.y.tex)} + k\\cdot ${Vector.asTex(d.x.tex, d.y.tex)}`
172
149
  } else {
173
- return `\\left\\{\\begin{aligned}
174
- x &= ${(new Polynom(this.#OA.x)
175
- .add(new Monom(this.#d.x).multiply(new Monom('k'))))
176
- .reorder('k', true)
177
- .tex}\\\\
178
- y &= ${(new Polynom(this.#OA.y)
179
- .add(new Monom(this.#d.y).multiply(new Monom('k'))))
180
- .reorder('k', true)
181
- .tex}
182
- \\end{aligned}\\right.`
150
+ return `\\left\\{\\begin{aligned}\n\tx &= ${(new Polynom(this.#OA.x)
151
+ .add(new Monom(d.x).multiply(new Monom('k'))))
152
+ .reorder('k', true)
153
+ .tex}\\\\\n\ty &= ${(new Polynom(this.#OA.y)
154
+ .add(new Monom(d.y).multiply(new Monom('k'))))
155
+ .reorder('k', true)
156
+ .tex}\n\\end{aligned}\\right.`
183
157
  }
184
158
  }
185
- default:
186
- {
187
- const canonical = this.getEquation()
188
- if (this.#a.isNegative()) {
189
- canonical.multiply(-1)
190
- }
191
- return canonical.tex
159
+ default: {
160
+ const canonical = this.getEquation()
161
+ if (canonical.left.monoms[0].coefficient.isNegative()) {
162
+ canonical.multiply(-1)
192
163
  }
164
+ return canonical.tex
165
+ }
193
166
  }
194
167
 
195
168
  }
@@ -199,27 +172,33 @@ export class Line implements IPiMathObject<Line> {
199
172
  // mxh => y = -a/b x - c/b
200
173
  // parametric => (xy) = OA + k*d // not relevant in display mode.
201
174
  const output = this.#outputMode
202
- this.#outputMode = 'canonical'
175
+ this.#outputMode = LINE_DISPLAY.CANONICAL
203
176
 
204
177
  switch (output) {
205
- case 'equation':
178
+ case LINE_DISPLAY.CARTESIAN:
206
179
  return this.getEquation().reorder().display
207
- case 'mxh':
180
+ case LINE_DISPLAY.MXH:
208
181
  return this.slope.isInfinity() ?
209
182
  'x=' + this.OA.x.display :
210
183
  'y=' + new Polynom().parse('x', this.slope, this.height).display
211
- case 'parametric': {
212
- const d = this.#d.clone()
213
- if (this.#reduceBeforeDisplay) {
214
- d.simplify()
215
- }
216
-
184
+ case LINE_DISPLAY.PARAMETRIC: {
185
+ const d = this.d.clone().simplify()
217
186
  return `((x,y))=((${this.#OA.x.display},${this.#OA.y.display}))+k((${d.x.display},${d.y.display}))`
218
187
  }
188
+ case LINE_DISPLAY.SYSTEM: {
189
+ const d = this.d.clone().simplify()
190
+ // {(2x,+,17y,=,23),(x,-,y,=,5):}
191
+ const px = (new Polynom(this.#OA.x).add(new Monom(d.x).multiply(new Monom('k'))))
192
+ .reorder('k', true)
193
+ const py = (new Polynom(this.#OA.y).add(new Monom(d.y).multiply(new Monom('k'))))
194
+ .reorder('k', true)
195
+
196
+ return `{(x,=,${px.display}),(y,=,${py.display}):}`
197
+ }
219
198
  default: {
220
199
  const canonical = this.getEquation()
221
200
  // Make sur the first item is positive.
222
- if (this.#a.isNegative()) {
201
+ if (canonical.left.monoms[0].coefficient.isNegative()) {
223
202
  canonical.multiply(-1)
224
203
  }
225
204
  return canonical.display
@@ -232,8 +211,8 @@ export class Line implements IPiMathObject<Line> {
232
211
  return this.#OA
233
212
  }
234
213
 
235
- set OA(value: Vector) {
236
- this.#OA = value
214
+ set OA(value: Vector | Point) {
215
+ this.fromPointAndNormal(value, this.n)
237
216
  }
238
217
 
239
218
  get a(): Fraction {
@@ -244,6 +223,31 @@ export class Line implements IPiMathObject<Line> {
244
223
  this.#a = value
245
224
  }
246
225
 
226
+ get asCanonical(): this {
227
+ this.#outputMode = LINE_DISPLAY.CANONICAL
228
+ return this
229
+ }
230
+
231
+ get asCartesian(): this {
232
+ this.#outputMode = LINE_DISPLAY.CARTESIAN
233
+ return this
234
+ }
235
+
236
+ get asMxh(): this {
237
+ this.#outputMode = LINE_DISPLAY.MXH
238
+ return this
239
+ }
240
+
241
+ get asParametric(): this {
242
+ this.#outputMode = LINE_DISPLAY.PARAMETRIC
243
+ return this
244
+ }
245
+
246
+ get asSystem(): this {
247
+ this.#outputMode = LINE_DISPLAY.SYSTEM
248
+ return this
249
+ }
250
+
247
251
  get b(): Fraction {
248
252
  return this.#b
249
253
  }
@@ -260,32 +264,8 @@ export class Line implements IPiMathObject<Line> {
260
264
  this.#c = value
261
265
  }
262
266
 
263
- // get system(): { x: Equation, y: Equation } {
264
- // const e1 = new Equation(
265
- // new Polynom('x'),
266
- // new Polynom(this.#OA.x)
267
- // .add(new Monom('k').multiply(this.#d.x))
268
- // ),
269
- // e2 = new Equation(
270
- // new Polynom('y'),
271
- // new Polynom(this.#OA.y)
272
- // .add(new Monom('k').multiply(this.#d.y))
273
- // )
274
-
275
- // return { x: e1, y: e2 }
276
- // }
277
-
278
- get canonical(): this {
279
- this.#outputMode = 'canonical'
280
- return this
281
- }
282
-
283
267
  // ------------------------------------------
284
- canonicalAsFloatCoefficient(decimals?: number): string {
285
- if (decimals === undefined) {
286
- decimals = 2
287
- }
288
-
268
+ canonicalAsFloatCoefficient(decimals = 2): string {
289
269
  let canonical = ''
290
270
 
291
271
  if (!this.#a.isZero()) {
@@ -317,69 +297,104 @@ export class Line implements IPiMathObject<Line> {
317
297
  }
318
298
 
319
299
  get d(): Vector {
320
- return this.#d
300
+ const director = new Vector(this.#b.clone(), this.#a.clone().opposite())
301
+
302
+ if (
303
+ this.#orientation === LINE_DIRECTION.LEFT_TO_RIGHT && director.x.isNegative() ||
304
+ this.#orientation === LINE_DIRECTION.RIGHT_TO_LEFT && director.x.isPositive()
305
+ ) {
306
+ director.opposite()
307
+ }
308
+
309
+ return director
321
310
  }
322
311
 
323
312
  set d(value: Vector) {
324
- this.#d = value
313
+ this.fromPointAndDirection(this.OA, value)
325
314
  }
326
315
 
327
316
  get director(): Vector {
328
- return this.#d.clone()
317
+ return this.d
329
318
  }
330
319
 
331
- distanceTo(pt: Point): { value: number, fraction: Fraction, tex: string } {
320
+ distanceTo(pt: Point): Root {
332
321
  const numerator = pt.x.clone().multiply(this.#a)
333
322
  .add(pt.y.clone().multiply(this.#b))
334
- .add(this.#c).abs(),
335
- d2 = this.normal.normSquare
323
+ .add(this.#c).abs()
324
+ const d2 = this.normal.normSquare
336
325
 
337
326
  // The denominator is null - shouldn't be possible
338
327
  if (d2.isZero()) {
339
- return {
340
- value: NaN,
341
- tex: 'Not a line',
342
- fraction: new Fraction().infinite()
343
- }
344
- }
345
- // The denominator is a perfect square - simplify the tex result
346
- const value = numerator.value / Math.sqrt(d2.value),
347
- F = numerator.clone().divide(d2.clone().sqrt())
348
-
349
- // The denominator is a perfect square.
350
- if (d2.isSquare()) {
351
- return {
352
- value,
353
- tex: F.tex,
354
- fraction: F
355
- }
328
+ return new Root(0)
356
329
  }
357
- // Complete answer...
358
- return {
359
- value,
360
- tex: `\\frac{${numerator.tex}}{\\sqrt{${d2.tex}}}`,
361
- fraction: F
362
- }
363
- }
364
330
 
365
- get equation(): this {
366
- this.#outputMode = 'equation'
367
- return this
368
- }
331
+ return new Root().from(2, d2.inverse(), numerator)
332
+
333
+ // The denominator is a perfect square - simplify the tex result
334
+ // const value = numerator.value / Math.sqrt(d2.value)
335
+ // const F = numerator.clone().divide(d2.clone().sqrt())
369
336
 
370
- fromCoefficient = (a: InputValue<Fraction>, b: InputValue<Fraction>, c: InputValue<Fraction>): this => {
337
+ //
338
+ // if (d2.isSquare()) {
339
+ //
340
+ // return {
341
+ // value,
342
+ // tex: F.tex,
343
+ // fraction: F
344
+ // }
345
+ // }
346
+ // // Complete answer...
347
+ // return {
348
+ // value,
349
+ // tex: `\\frac{${numerator.tex}}{\\sqrt{${d2.tex}}}`,
350
+ // fraction: F
351
+ // }
352
+ }
353
+
354
+ fromCoefficient(a: InputValue<Fraction>, b: InputValue<Fraction>, c: InputValue<Fraction>): this {
371
355
  this.#a = new Fraction(a)
372
356
  this.#b = new Fraction(b)
373
357
  this.#c = new Fraction(c)
374
358
 
375
- this.#d = new Vector(this.#b.clone(), this.#a.clone().opposite())
376
- this.#OA = new Vector(new Fraction().zero(), this.#c.clone())
377
- this.#n = this.#d.clone().normal()
359
+ // make sure the coefficients are relative...
360
+ const lcm = [this.#a, this.#b, this.#c].every(k => k.exact)
361
+ ? Numeric.lcm(this.#a.denominator, this.#b.denominator, this.#c.denominator)
362
+ : 1
363
+
364
+ if (lcm > 1) {
365
+ this.#a.multiply(lcm).reduce()
366
+ this.#b.multiply(lcm).reduce()
367
+ this.#c.multiply(lcm).reduce()
368
+ }
369
+
370
+ if (this.#b.isZero()) {
371
+ // ax+c=0 => x = -c/a
372
+ this.#OA = new Vector(this.#c.clone().divide(this.#a).opposite(), 0)
373
+ return this
374
+ }
375
+
376
+ // ax+by+c=0 => x=0 => y = a/b x - c/b = (ax-c)/b
377
+ for (let x = 0; x < this.#b.value; x++) {
378
+ const y = this.#a.clone().divide(this.#b)
379
+ .multiply(x)
380
+ .subtract(this.#c.clone().divide(this.#b))
381
+ .reduce()
382
+
383
+ this.#OA = new Vector(x, y)
384
+
385
+ if (y.isRelative()) {
386
+ return this
387
+ }
388
+ }
389
+
390
+ // no "nice" point... do it with 'x=0'
391
+ const y = this.#c.clone().divide(this.#b).opposite().reduce()
392
+ this.#OA = new Vector(0, y)
378
393
 
379
394
  return this
380
395
  }
381
396
 
382
- fromEquation = (equ: Equation): this => {
397
+ fromEquation(equ: Equation): this {
383
398
  // Reorder the equation
384
399
  equ.reorder(true)
385
400
 
@@ -410,66 +425,60 @@ export class Line implements IPiMathObject<Line> {
410
425
  )
411
426
  }
412
427
 
413
- fromPointAndDirection = (P: Point, d: Vector): this => {
414
- // OX = OP + k*d
415
- // x = px + kdx * dy
416
- // y = py + kdy * dx
417
- // ------------------
418
- // dy * x = px * dy + kdxdy
419
- // dx * y = py * dx + kdxdy
420
- // ------------------
421
- // dy * x - dx * y = px * dy - py * dx
422
- // dy * x - dx * y - (px * dy - py * dx) = 0
423
- this.fromCoefficient(
424
- d.y,
425
- d.x.clone().opposite(),
426
- P.x.clone().multiply(d.y).subtract(P.y.clone().multiply(d.x)).opposite()
427
- )
428
-
429
- // Choose the current values as point and direction vector instead of the automatic version.
430
- this.#OA = P.clone()
431
- this.#d = d.clone()
432
- this.#n = this.#d.clone().normal()
428
+ fromParallel(parallel: Line, point: Point): this {
429
+ return this.fromPointAndNormal(point, parallel.n)
430
+ }
433
431
 
434
- return this
432
+ fromPerpendicular(perpendicular: Line, point: Point): this {
433
+ return this.fromPointAndNormal(point, perpendicular.d)
435
434
  }
436
435
 
437
- fromPointAndLine = (P: Vector, L: Line, orientation?: LinePropriety): this => {
436
+ fromPointAndDirection(P: Point | Vector, d: Vector): this {
437
+ this.#orientation = d.x.isPositive()
438
+ ? LINE_DIRECTION.LEFT_TO_RIGHT
439
+ : LINE_DIRECTION.RIGHT_TO_LEFT
438
440
 
439
- if (orientation === undefined) {
440
- orientation = LinePropriety.Parallel
441
- }
441
+ return this.fromPointAndNormal(P, d.clone().normal())
442
+ }
443
+
444
+ fromPointAndLine(P: Vector, L: Line, orientation: LinePropriety = LinePropriety.Parallel): this {
442
445
 
443
- if (orientation === LinePropriety.Parallel) {
444
- return this.fromPointAndNormal(P, L.normal)
445
- } else if (orientation === LinePropriety.Perpendicular) {
446
+ if (orientation === LinePropriety.Perpendicular) {
446
447
  return this.fromPointAndNormal(P, L.director)
447
448
  }
448
449
 
449
- return this
450
+ return this.fromPointAndNormal(P, L.normal)
450
451
  }
451
452
 
452
- fromPointAndNormal = (P: Point, n: Vector): this => {
453
- return this.fromCoefficient(
453
+ fromPointAndNormal = (P: Point | Vector, n: Vector): this => {
454
+ if (n.isZero()) {
455
+ console.warn('Normal vector is null.')
456
+ return this
457
+ }
458
+
459
+ this.fromCoefficient(
454
460
  n.x,
455
461
  n.y,
456
462
  P.x.clone().multiply(n.x)
457
463
  .add(P.y.clone().multiply(n.y)).opposite()
458
464
  )
465
+
466
+ this.#OA = new Vector(P.clone())
467
+
468
+ return this
459
469
  }
460
470
 
461
- fromPoints(pt1: Point, pt2: Point){
462
- return this.fromPointAndDirection(pt1, new Vector(pt1, pt2))
471
+ // ------------------------------------------
472
+ // Creation / parsing functions
473
+
474
+ fromPoints(A: Point, B: Point) {
475
+ return this.fromPointAndNormal(A, new Vector(A, B).normal())
463
476
  }
464
477
 
465
478
  // ------------------------------------------
466
479
  getEquation(): Equation {
467
480
  const equ = new Equation(new Polynom().parse('xy', this.#a, this.#b, this.#c), new Polynom('0'))
468
- if (this.#reduceBeforeDisplay) {
469
- return equ.simplify()
470
- } else {
471
- return equ
472
- }
481
+ return equ.simplify()
473
482
  }
474
483
 
475
484
  getValueAtX = (value: Fraction | number): Fraction => {
@@ -477,20 +486,17 @@ export class Line implements IPiMathObject<Line> {
477
486
  F = new Fraction(value)
478
487
 
479
488
  if (equ instanceof Equation) {
480
- return equ.right.evaluate({ x: F }) as Fraction
489
+ return equ.right.evaluate({x: F}) as Fraction
481
490
  }
482
491
  return new Fraction().invalid()
483
492
  }
484
493
 
485
- // ------------------------------------------
486
- // Creation / parsing functions
487
-
488
494
  getValueAtY = (value: Fraction | number): Fraction => {
489
495
  const equ = this.getEquation().isolate('x'),
490
496
  F = new Fraction(value)
491
497
 
492
498
  if (equ instanceof Equation) {
493
- return equ.right.evaluate({ y: F }) as Fraction
499
+ return equ.right.evaluate({y: F}) as Fraction
494
500
  }
495
501
 
496
502
  return new Fraction().invalid()
@@ -516,6 +522,7 @@ export class Line implements IPiMathObject<Line> {
516
522
  }
517
523
 
518
524
  intersection = (line: Line): { point: Point, hasIntersection: boolean, isParallel: boolean, isSame: boolean } => {
525
+ // TODO: rework Line.intersection
519
526
  const Pt = new Point()
520
527
  let isParallel = false, isSame = false
521
528
 
@@ -562,8 +569,12 @@ export class Line implements IPiMathObject<Line> {
562
569
  }
563
570
  }
564
571
 
572
+ isHorizontal(): boolean {
573
+ return this.slope.value === 0
574
+ }
575
+
565
576
  // ------------------------------------------
566
- isOnLine = (pt: Vector): boolean => {
577
+ isOnLine(pt: Point): boolean {
567
578
  return this.#a.clone()
568
579
  .multiply(pt.x)
569
580
  .add(
@@ -586,31 +597,19 @@ export class Line implements IPiMathObject<Line> {
586
597
  isSameAs = (line: Line): boolean => {
587
598
  return this.slope.isEqual(line.slope) && this.height.isEqual(line.height)
588
599
  }
589
- // ------------------------------------------
590
- // Mathematical operations
591
600
 
592
601
  isVertical = (): boolean => {
593
602
  return this.slope.isInfinity()
594
603
  }
595
604
 
596
- get mxh(): this {
597
- this.#outputMode = 'mxh'
598
- return this
599
- }
600
-
601
605
  get n(): Vector {
602
- return this.#n
606
+ return this.d.normal()
603
607
  }
604
608
 
605
609
  get normal(): Vector {
606
610
  return new Vector(this.#a, this.#b)
607
611
  }
608
612
 
609
- get parametric(): this {
610
- this.#outputMode = 'parametric'
611
- return this
612
- }
613
-
614
613
  randomNearPoint = (k?: number): Point => {
615
614
  const pt = this.randomPoint(k)
616
615
 
@@ -627,22 +626,12 @@ export class Line implements IPiMathObject<Line> {
627
626
 
628
627
  randomPoint = (k?: number): Point => {
629
628
  // Return a random point on the line.
630
- const pt = this.#d
629
+ const pt = this.d
631
630
  .clone()
632
631
  .multiplyByScalar(randomIntSym((k === undefined || k <= 1) ? 3 : k, false))
633
632
  .add(this.#OA)
634
633
 
635
- pt.asPoint = true
636
-
637
- return pt
638
- }
639
-
640
- get reduceBeforeDisplay(): boolean {
641
- return this.#reduceBeforeDisplay
642
- }
643
-
644
- set reduceBeforeDisplay(value: boolean) {
645
- this.#reduceBeforeDisplay = value
634
+ return new Point(pt)
646
635
  }
647
636
 
648
637
  simplify = (): this => {
@@ -658,20 +647,7 @@ export class Line implements IPiMathObject<Line> {
658
647
  return this
659
648
  }
660
649
 
661
- simplifyDirection = (): this => {
662
- this.#d.simplify()
663
- return this
664
- }
665
-
666
650
  get slope(): Fraction {
667
651
  return this.#a.clone().opposite().divide(this.#b)
668
652
  }
669
-
670
- // ------------------------------------------
671
- // Special functions
672
-
673
- get system(): this {
674
- this.#outputMode = 'system'
675
- return this
676
- }
677
653
  }