pimath 0.1.39 → 0.1.40

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 (65) hide show
  1. package/dist/pimath.js +188 -159
  2. package/dist/pimath.js.map +1 -1
  3. package/package.json +4 -2
  4. package/src/algebra/equation.ts +556 -0
  5. package/src/algebra/equationSolver.ts +539 -0
  6. package/src/algebra/factor.ts +339 -0
  7. package/src/algebra/index.ts +11 -0
  8. package/src/algebra/linearSystem.ts +388 -0
  9. package/src/algebra/logicalset.ts +256 -0
  10. package/src/algebra/matrix.ts +474 -0
  11. package/src/algebra/monom.ts +1015 -0
  12. package/src/algebra/operations.ts +24 -0
  13. package/src/algebra/polyFactor.ts +668 -0
  14. package/src/algebra/polynom.ts +1394 -0
  15. package/src/analyze/solution.ts +115 -0
  16. package/src/analyze/tableOfSigns.ts +30 -0
  17. package/src/coefficients/fraction.ts +678 -0
  18. package/src/coefficients/index.ts +4 -0
  19. package/src/coefficients/nthRoot.ts +149 -0
  20. package/src/coefficients/root.ts +299 -0
  21. package/src/geometry/circle.ts +386 -0
  22. package/src/geometry/geomMath.ts +70 -0
  23. package/src/geometry/index.ts +10 -0
  24. package/src/geometry/line.ts +677 -0
  25. package/src/geometry/line3.ts +206 -0
  26. package/src/geometry/plane3.ts +170 -0
  27. package/src/geometry/point.ts +66 -0
  28. package/src/geometry/sphere3.ts +214 -0
  29. package/src/geometry/triangle.ts +354 -0
  30. package/src/geometry/vector.ts +341 -0
  31. package/src/helpers.ts +35 -0
  32. package/src/index.ts +60 -0
  33. package/src/numeric.ts +199 -0
  34. package/src/pimath.interface.ts +160 -0
  35. package/src/randomization/algebra/rndEquation.ts +41 -0
  36. package/src/randomization/algebra/rndMonom.ts +39 -0
  37. package/src/randomization/algebra/rndPolynom.ts +86 -0
  38. package/src/randomization/coefficient/rndFraction.ts +38 -0
  39. package/src/randomization/geometry/rndCircle.ts +27 -0
  40. package/src/randomization/geometry/rndLine.ts +37 -0
  41. package/src/randomization/geometry/rndLine3.ts +27 -0
  42. package/src/randomization/geometry/rndVector.ts +63 -0
  43. package/src/randomization/random.ts +91 -0
  44. package/src/randomization/rndHelpers.ts +102 -0
  45. package/src/randomization/rndTypes.ts +63 -0
  46. package/types/algebra/equationSolver.d.ts +3 -0
  47. package/types/algebra/equationSolver.d.ts.map +1 -1
  48. package/types/algebra/polyFactor.d.ts +5 -0
  49. package/types/algebra/polyFactor.d.ts.map +1 -1
  50. package/types/analyze/solution.d.ts +21 -0
  51. package/types/analyze/solution.d.ts.map +1 -0
  52. package/types/analyze/tableOfSigns.d.ts +9 -0
  53. package/types/analyze/tableOfSigns.d.ts.map +1 -0
  54. package/types/coefficients/root.d.ts +38 -0
  55. package/types/coefficients/root.d.ts.map +1 -0
  56. package/types/geometry/point.d.ts +1 -1
  57. package/types/geometry/point.d.ts.map +1 -1
  58. package/types/helpers.d.ts +1 -0
  59. package/types/helpers.d.ts.map +1 -1
  60. package/types/index.d.ts +1 -0
  61. package/types/index.d.ts.map +1 -1
  62. package/types/numeric.d.ts +2 -0
  63. package/types/numeric.d.ts.map +1 -1
  64. package/types/pimath.interface.d.ts +26 -26
  65. package/types/pimath.interface.d.ts.map +1 -1
@@ -0,0 +1,206 @@
1
+ /**
2
+ * This class works for 2d line in a plane.
3
+ */
4
+
5
+ import { Fraction } from "../coefficients/fraction"
6
+ import { Polynom } from "../algebra/polynom"
7
+ import { Monom } from "../algebra/monom"
8
+ import { randomIntSym } from "../randomization/rndHelpers"
9
+ import { Vector } from "./vector"
10
+ import { Point } from "./point"
11
+ import {Line3Propriety} from "../pimath.interface"
12
+
13
+
14
+ export class Line3 {
15
+ // A line is defined as the canonical form
16
+ static PERPENDICULAR = Line3Propriety.Perpendicular
17
+ static PARALLEL = Line3Propriety.Parallel
18
+ // ax + by + c = 0
19
+ #OA: Point = new Point()
20
+ #d: Vector = new Vector()
21
+
22
+ /**
23
+ * Value can be a mix of:
24
+ *
25
+ * @param values
26
+ */
27
+ constructor(A: Point, B: Point)
28
+ constructor(A: Point, d: Vector)
29
+ constructor(A: Point, d: Vector | Point) {
30
+ this.#OA = A.clone()
31
+ this.#d = d.asPoint ? new Vector(A, d) : d.clone()
32
+ return this
33
+ }
34
+
35
+ get OA(): Point {
36
+ return this.#OA
37
+ }
38
+
39
+ set OA(value: Point) {
40
+ this.#OA = value
41
+ }
42
+ get point(): Point {
43
+ return this.#OA.clone()
44
+ }
45
+
46
+ get d(): Vector {
47
+ return this.#d
48
+ }
49
+
50
+ set d(value: Vector) {
51
+ this.#d = value
52
+ }
53
+
54
+ get tex(): { parametric: string, system: string, cartesian: string } {
55
+ return {
56
+ parametric: `${Vector.asTex('x', 'y', 'z')} = ${Vector.asTex(this.#OA.x.tex, this.#OA.y.tex, this.#OA.z.tex)} + k\\cdot ${Vector.asTex(this.#d.x.tex, this.#d.y.tex, this.#d.z.tex)}`,
57
+ system: `\\left\\{\\begin{aligned}
58
+ x &= ${(new Polynom(this.#OA.x)
59
+ .add(new Monom(this.#d.x).multiply(new Monom('k'))))
60
+ .reorder('k', true)
61
+ .tex}\\\\
62
+ y &= ${(new Polynom(this.#OA.y)
63
+ .add(new Monom(this.#d.y).multiply(new Monom('k'))))
64
+ .reorder('k', true)
65
+ .tex}\\\\
66
+ z &= ${(new Polynom(this.#OA.z)
67
+ .add(new Monom(this.#d.z).multiply(new Monom('k'))))
68
+ .reorder('k', true)
69
+ .tex}
70
+ \\end{aligned}\\right.`,
71
+ cartesian: `\\frac{ ${new Polynom('x', 1, this.#OA.x.clone().opposite()).tex} }{ ${this.direction.x.tex} } = \\frac{ ${new Polynom('y', 1, this.#OA.y.clone().opposite()).tex} }{ ${this.direction.y.tex} } = \\frac{ ${new Polynom('z', 1, this.#OA.z.clone().opposite()).tex} }{ ${this.direction.z.tex} }`
72
+ }
73
+ }
74
+
75
+ get display(): { parametric: string, system: string, cartesian: string } {
76
+ const OAx = this.#OA.x.display
77
+ const OAy = this.#OA.y.display
78
+ const OAz = this.#OA.z.display
79
+ const n = this.direction.simplify()
80
+ const nx = n.x.display
81
+ const ny = n.y.display
82
+ const nz = n.z.display
83
+
84
+ return {
85
+ parametric: `${Vector.asDisplay('x', 'y', 'z')} = ${Vector.asDisplay(this.#OA.x.display, this.#OA.y.display, this.#OA.z.display)} + k\\cdot ${Vector.asDisplay(this.#d.x.display, this.#d.y.display, this.#d.z.display)}`,
86
+ system: '',
87
+ cartesian: `(x-${OAx})/${nx} = (y-${OAy})/${ny} = (z-${OAz})/${nz}`
88
+ }
89
+ }
90
+
91
+ get direction(): Vector {
92
+ return this.#d.clone()
93
+ }
94
+
95
+ clone = (): this => {
96
+ this.#d = this.#d.clone()
97
+ this.#OA = this.#OA.clone()
98
+
99
+ return this
100
+ }
101
+ // ------------------------------------------
102
+ // Mathematical operations
103
+ // ------------------------------------------
104
+ isOnLine = (pt: Point): boolean => {
105
+ return false
106
+ }
107
+
108
+ isParallelTo = (line: Line3): boolean => {
109
+ // Do they have the isSame direction ?
110
+ throw new Error('Method not implemented.')
111
+ }
112
+ isSameAs = (line: Line3): boolean => {
113
+ throw new Error('Method not implemented.')
114
+ }
115
+ isPerpendicularTo = (line: Line3): boolean => {
116
+ throw new Error('Method not implemented.')
117
+ }
118
+ isVertical = (): boolean => {
119
+ throw new Error('Method not implemented.')
120
+ }
121
+ simplify = (): this => {
122
+ throw new Error('Method not implemented.')
123
+ // const lcm = Numeric.lcm(this.#a.denominator, this.#b.denominator, this.#c.denominator),
124
+ // gcd = Numeric.gcd(this.#a.numerator, this.#b.numerator, this.#c.numerator)
125
+
126
+ // this.fromCoefficient(
127
+ // this.#a.clone().multiply(lcm).divide(gcd),
128
+ // this.#b.clone().multiply(lcm).divide(gcd),
129
+ // this.#c.clone().multiply(lcm).divide(gcd),
130
+ // )
131
+
132
+ // return this
133
+ }
134
+
135
+ intersection = (line: Line3): { point: Vector, hasIntersection: boolean, isParallel: boolean, isSame: boolean } => {
136
+
137
+ throw new Error('Method not implemented.')
138
+ }
139
+
140
+ distanceTo(pt: Point): { value: number, fraction: Fraction, tex: string } {
141
+ // Distance is:
142
+ // |(x - x0) x d| / |d|
143
+ const AP = new Vector(this.#OA, pt),
144
+ d = this.direction,
145
+ d2 = this.direction.normSquare,
146
+ num2 = AP.cross(d).normSquare,
147
+ num2d2 = num2.clone().divide(d2),
148
+ dnum = num2d2.clone().sqrt()
149
+
150
+ console.log('CROSS', AP.cross(d).display)
151
+ return {
152
+ value: Math.sqrt(num2d2.value),
153
+ fraction: num2d2.clone().sqrt(),
154
+ tex: dnum.isExact() ? dnum.tex : `\\sqrt{${num2d2.tex}}`
155
+ }
156
+ }
157
+
158
+ hitSegment(A: Point, B: Point): boolean {
159
+ const iPt = this.intersection(
160
+ new Line3(A, B)
161
+ )
162
+
163
+ // There is an intersection point
164
+ if (iPt.hasIntersection) {
165
+ return iPt.point.x.value >= Math.min(A.x.value, B.x.value)
166
+ && iPt.point.x.value <= Math.max(A.x.value, B.x.value)
167
+ && iPt.point.y.value >= Math.min(A.y.value, B.y.value)
168
+ && iPt.point.y.value <= Math.max(A.y.value, B.y.value)
169
+ && iPt.point.z.value >= Math.min(A.z.value, B.z.value)
170
+ && iPt.point.z.value <= Math.max(A.z.value, B.z.value)
171
+ }
172
+ return false
173
+ }
174
+
175
+ // getValueAtX = (value: Fraction | number): Fraction => {
176
+ // const equ = this.equation.clone().isolate('y'),
177
+ // F = new Fraction(value)
178
+
179
+ // if (equ instanceof Equation) {
180
+ // return equ.right.evaluate({ x: F }) as Fraction
181
+ // }
182
+ // return new Fraction().invalid()
183
+ // }
184
+
185
+ // getValueAtY = (value: Fraction | number): Fraction => {
186
+ // const equ = this.equation.clone().isolate('x'),
187
+ // F = new Fraction(value)
188
+
189
+ // if (equ instanceof Equation) {
190
+ // return equ.right.evaluate({ y: F }) as Fraction
191
+ // }
192
+
193
+ // return new Fraction().invalid()
194
+ // }
195
+
196
+ randomPoint = (max = 5): Point => {
197
+ const A = this.#OA.clone(),
198
+ k = new Fraction(randomIntSym(max, false))
199
+
200
+ return new Point(
201
+ A.x.clone().add(this.#d.x.clone().multiply(k)),
202
+ A.y.clone().add(this.#d.y.clone().multiply(k)),
203
+ A.z.clone().add(this.#d.z.clone().multiply(k))
204
+ )
205
+ }
206
+ }
@@ -0,0 +1,170 @@
1
+ import { Equation } from "../algebra/equation"
2
+ import { Polynom } from "../algebra/polynom"
3
+ import { Fraction } from "../coefficients/fraction"
4
+ import { Line3 } from "./line3"
5
+ import { Point } from "./point"
6
+ import { Vector } from "./vector"
7
+ import type {Plane3Config} from "../pimath.interface"
8
+
9
+
10
+
11
+ export class Plane3 {
12
+ #normal: Vector = new Vector(0, 0, 1)
13
+ #point: Point = new Point(0, 0, 0)
14
+
15
+ constructor(config?: Plane3Config) {
16
+ if (config) {
17
+ this.parse(config)
18
+ }
19
+
20
+ return this
21
+ }
22
+
23
+ get normal(): Vector {
24
+ return this.#normal
25
+ }
26
+ set normal(value: Vector) {
27
+ this.#normal = value
28
+ this.#normal.asPoint = false
29
+ }
30
+ get point(): Point {
31
+ return this.#point
32
+ }
33
+ set point(value: Point) {
34
+ this.#point = value
35
+ this.#point.asPoint = true
36
+ }
37
+
38
+ get a(): Fraction {
39
+ return this.#normal.x
40
+ }
41
+ get b(): Fraction {
42
+ return this.#normal.y
43
+ }
44
+ get c(): Fraction {
45
+ return this.#normal.z
46
+ }
47
+ get d(): Fraction {
48
+ return this.#normal.dot(this.#point).opposite()
49
+ }
50
+
51
+ get tex(): string {
52
+ // return the cartesian equation of the plane
53
+ return new Equation(
54
+ new Polynom('xyz', this.a, this.b, this.c, this.d),
55
+ new Polynom(0)
56
+ ).reduce().tex
57
+ }
58
+
59
+ get display(): string {
60
+ // return the cartesian equation of the plane
61
+ return new Equation(
62
+ new Polynom('xyz', this.a, this.b, this.c, this.d),
63
+ new Polynom(0)
64
+ ).reduce().display
65
+ }
66
+
67
+ parse(config: Plane3Config) {
68
+ if (config.point && config.normal) {
69
+ this.point = config.point
70
+ this.normal = config.normal
71
+ return
72
+ }
73
+
74
+ if (config.point && config.directions?.length === 2) {
75
+ this.point = config.point
76
+ const [v1, v2] = config.directions
77
+ this.normal = v1.cross(v2)
78
+ return
79
+ }
80
+
81
+ if (config.equation) {
82
+ const cartesian = config.equation.moveLeft().reduce().left
83
+
84
+ const a = cartesian.monomByLetter('x').coefficient
85
+ const b = cartesian.monomByLetter('y').coefficient
86
+ const c = cartesian.monomByLetter('z').coefficient
87
+ const d = cartesian.monomByDegree(0).coefficient
88
+
89
+ // Get the normal vector
90
+ this.normal = new Vector(a, b, c)
91
+
92
+ // Get a point on the plane
93
+ if (a.isNotZero()) {
94
+ this.point = new Point(d.clone().divide(a).opposite(), 0, 0)
95
+ } else if (b.isNotZero()) {
96
+ this.point = new Point(0, d.clone().divide(b).opposite(), 0)
97
+ } else {
98
+ this.point = new Point(0, 0, d.clone().divide(c).opposite())
99
+ }
100
+ // Make sure it's considered as point
101
+ return
102
+ }
103
+
104
+ if (config.points?.length === 3 && config.points.every(p => p instanceof Vector)) {
105
+ const A = config.points[0]
106
+ const B = config.points[1]
107
+ const C = config.points[2]
108
+
109
+ const AB = new Vector(A, B)
110
+ const AC = new Vector(A, C)
111
+ this.normal = AB.cross(AC)
112
+ this.point = A
113
+ return
114
+ }
115
+
116
+ if (config.coefficients?.length === 4) {
117
+ const [a, b, c, d] = config.coefficients
118
+ this.normal = new Vector(a, b, c)
119
+ this.point = new Point(0, 0, -d)
120
+ return
121
+ }
122
+ }
123
+
124
+ angle(vector: Vector, sharp?: boolean, radian?: boolean): number
125
+ angle(line: Line3, sharp?: boolean, radian?: boolean): number
126
+ angle(plane: Plane3, sharp?: boolean, radian?: boolean): number
127
+ angle(value: Plane3 | Line3 | Vector, sharp?: boolean, radian?: boolean): number {
128
+ if (value instanceof Plane3) {
129
+ return this.normal.angle(value.normal, sharp, radian)
130
+ }
131
+
132
+ let direction: Vector
133
+ if (value instanceof Vector) {
134
+ if (value.dimension !== 3) {
135
+ throw new Error('Vector is not 3D')
136
+ }
137
+
138
+ direction = value
139
+ } else {
140
+ direction = value.direction
141
+ }
142
+
143
+ const a90 = radian ? Math.PI / 2 : 90
144
+ return a90 - this.normal.angle(direction, true, radian)
145
+ }
146
+
147
+ distanceTo(point: Vector): number {
148
+ return this.normal.dot(point).add(this.d).abs().value / this.normal.norm
149
+ }
150
+
151
+ intersectWithLine(line: Line3): Point {
152
+ const { point, direction } = line
153
+ const t = this.normal.dot(point).add(this.d).divide(this.normal.dot(direction).opposite())
154
+ return point.clone().add(direction.clone().multiplyByScalar(t))
155
+ }
156
+
157
+ intersectWithPlane(plane: Plane3): Line3 {
158
+ const direction = this.normal.cross(plane.normal)
159
+
160
+ // Solve the system:
161
+ // p1 // p2 // z=0
162
+ const pt = new Point(0, 0, 0)
163
+ throw new Error('Intersection with plane not yet implemented !')
164
+ return new Line3(pt, direction)
165
+ }
166
+
167
+ isPointOnPlane(pt: Point): boolean {
168
+ return this.normal.dot(pt).add(this.d).isZero()
169
+ }
170
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Vector2D module contains everything necessary to handle 2d vectors.
3
+ * @module Point
4
+ */
5
+
6
+ import {Fraction} from "../coefficients"
7
+ import type {InputValue} from "../pimath.interface"
8
+ import {Vector} from "./vector"
9
+
10
+ export class Point extends Vector {
11
+
12
+ constructor()
13
+ constructor(value: Vector)
14
+ constructor(start: Vector, end: Vector)
15
+ constructor(...values: InputValue<Fraction>[])
16
+ constructor(...values: Vector[] | InputValue<Fraction>[]) {
17
+ super()
18
+
19
+ this.asPoint = true
20
+
21
+ // Initialize the vector
22
+ if (values.length > 0) {
23
+ this.parse(...values)
24
+ }
25
+ };
26
+
27
+ public override parse(...values: Vector[] | InputValue<Fraction>[]): this {
28
+ if (values.length === 1) {
29
+ if (values[0] instanceof Vector) {
30
+ this.array = values[0].copy()
31
+ return this
32
+ }
33
+
34
+ if (typeof values[0] === 'string') {
35
+ this.fromString(values[0])
36
+ return this
37
+ }
38
+ }
39
+
40
+
41
+ if (values.length > 1) {
42
+ if (values.some(x => x instanceof Vector)) {
43
+ throw new Error('Creating a point with multiple argument requires an input fraction')
44
+ }
45
+
46
+ const nbs: Fraction[] = values.map(x => new Fraction(x as InputValue<Fraction>))
47
+
48
+ if (nbs.some(x => x.isNaN())) {
49
+ throw new Error('The value is not a valid point sting (a,b): ' + values.join(','))
50
+ }
51
+
52
+ this.array = nbs
53
+ }
54
+
55
+ return this
56
+ }
57
+
58
+
59
+ public override clone(): Point {
60
+ const V = new Point()
61
+ V.array = this.copy()
62
+ V.asPoint = true
63
+ return V
64
+ }
65
+
66
+ }
@@ -0,0 +1,214 @@
1
+ import {Point} from "./point"
2
+ import {Fraction} from "../coefficients"
3
+ import {Equation, Polynom} from "../algebra"
4
+ import type {InputValue} from "../pimath.interface"
5
+
6
+ enum SPHERE3_FORMAT {
7
+ DEVELOPPED,
8
+ CENTER_RADIUS
9
+ }
10
+
11
+ export enum SPHERE3_RELATIVE_POSITION {
12
+ INTERIOR,
13
+ EXTERIOR,
14
+ SECANT,
15
+ TANGENT_INSIDE,
16
+ TANGENT_OUTSIDE,
17
+ SUPERPOSED,
18
+ CONCENTRIC
19
+ }
20
+
21
+ export class Sphere3 {
22
+ #center: Point | undefined = undefined
23
+ #squareRadius: Fraction | undefined = undefined
24
+ #equation: Equation | undefined = undefined
25
+ #format: SPHERE3_FORMAT = SPHERE3_FORMAT.CENTER_RADIUS
26
+
27
+ constructor(center?: Point, radius?: InputValue<Fraction>) {
28
+ if (center && radius) {
29
+ this.#center = center
30
+ this.#squareRadius = new Fraction(radius).clone().pow(2)
31
+ this.#computeEquation()
32
+ }
33
+ return this
34
+ }
35
+
36
+ fromEquation(equation: Equation | string): this {
37
+ const equ = new Equation(equation).moveLeft().reduce()
38
+
39
+ // Check that x, y, z has the same power and same coefficient.
40
+ const letters = ['x', 'y', 'z']
41
+
42
+ if (letters.some((letter) => equ.degree(letter).value !== 2)){
43
+ return this.makeUndefined()
44
+ }
45
+
46
+ const coefficient = equ.left.monomByDegree(2, 'x').coefficient
47
+ if (letters.some((letter) => equ.left.monomByDegree(2, letter).coefficient.isNotEqual(coefficient))) {
48
+ return this.makeUndefined()
49
+ }
50
+
51
+ this.#center = new Point(
52
+ equ.left.monomByDegree(1, 'x').coefficient.clone().opposite().divide(2),
53
+ equ.left.monomByDegree(1, 'y').coefficient.clone().opposite().divide(2),
54
+ equ.left.monomByDegree(1, 'z').coefficient.clone().opposite().divide(2)
55
+ )
56
+
57
+ this.#squareRadius = equ.left.monomByDegree(0)
58
+ .coefficient.clone().opposite()
59
+ .add(this.#center.x.clone().pow(2))
60
+ .add(this.#center.y.clone().pow(2))
61
+ .add(this.#center.z.clone().pow(2))
62
+
63
+ this.#computeEquation()
64
+ return this
65
+ }
66
+
67
+ get center(): Point {
68
+ if (this.#center === undefined) {
69
+ throw new Error('Sphere3 is undefined')
70
+ }
71
+ return this.#center
72
+ }
73
+
74
+ get squareRadius(): Fraction {
75
+ if (this.#squareRadius === undefined) {
76
+ throw new Error('Sphere3 is undefined')
77
+ }
78
+ return this.#squareRadius
79
+ }
80
+
81
+ get radius(): { tex: string, display: string, value: number } {
82
+ if (this.#squareRadius === undefined) {
83
+ throw new Error('Sphere3 is undefined')
84
+ }
85
+
86
+ if (this.#squareRadius.isSquare()) {
87
+ return {
88
+ tex: this.#squareRadius.clone().sqrt().tex,
89
+ display: this.#squareRadius.clone().sqrt().display,
90
+ value: this.#squareRadius.clone().sqrt().value
91
+ }
92
+ } else {
93
+ return {
94
+ tex: `\\sqrt{${this.#squareRadius.tex}}`,
95
+ display: `sqrt(${this.#squareRadius.display})`,
96
+ value: this.#squareRadius.clone().sqrt().value
97
+ }
98
+ }
99
+ }
100
+
101
+ get equation(): Equation {
102
+ if (this.#equation === undefined) {
103
+ throw new Error('Sphere3 is undefined')
104
+ }
105
+ return this.#equation
106
+ }
107
+
108
+ makeUndefined(): this {
109
+ this.#center = undefined
110
+ this.#squareRadius = undefined
111
+ this.#equation = undefined
112
+ return this
113
+ }
114
+
115
+ get centerRadius(): this {
116
+ this.#format = SPHERE3_FORMAT.CENTER_RADIUS
117
+ return this
118
+ }
119
+
120
+ get developped(): this {
121
+ this.#format = SPHERE3_FORMAT.DEVELOPPED
122
+ return this
123
+ }
124
+
125
+
126
+ get tex(): string {
127
+ return this.#output(true)
128
+ }
129
+
130
+ get display(): string {
131
+ return this.#output(false)
132
+ }
133
+
134
+ #output = (asTex: boolean): string => {
135
+ if (this.#equation === undefined) {
136
+ throw new Error('Sphere3 is undefined')
137
+ }
138
+
139
+ if (this.#format === SPHERE3_FORMAT.DEVELOPPED) {
140
+ return asTex ? this.#equation.tex : this.#equation.display
141
+ }
142
+
143
+ const output: string[] = []
144
+ const letters: ('x' | 'y' | 'z')[] = ['x', 'y', 'z']
145
+
146
+ letters.forEach((letter: 'x' | 'y' | 'z') => {
147
+ if (this.center[letter].isZero()) {
148
+ output.push(`${letter}^2`)
149
+ } else {
150
+ const P = new Polynom(letter).subtract(this.center[letter])
151
+ output.push(
152
+ asTex ?
153
+ `\\(${P.tex}\\)^2` :
154
+ `(${P.display})^2`
155
+ )
156
+ }
157
+ })
158
+
159
+ return output.join('+') + '=' + (asTex ? this.squareRadius.tex : this.squareRadius.display)
160
+
161
+ }
162
+
163
+ #computeEquation(): void {
164
+ this.#equation = new Equation(
165
+ new Polynom('x').subtract(this.center.x).pow(2)
166
+ .add(
167
+ new Polynom('y').subtract(this.center.y).pow(2)
168
+ )
169
+ .add(
170
+ new Polynom('z').subtract(this.center.z).pow(2)
171
+ ),
172
+ new Polynom(this.squareRadius)
173
+ ).reduce()
174
+ }
175
+
176
+ static RELATIVE_POSITION = SPHERE3_RELATIVE_POSITION
177
+ relativePosition = (S: Sphere3): SPHERE3_RELATIVE_POSITION => {
178
+ const distance = this.center.distanceTo(S.center).value
179
+ const r1 = this.radius.value
180
+ const r2 = S.radius.value
181
+
182
+ if (distance > r1 + r2) {
183
+ return SPHERE3_RELATIVE_POSITION.EXTERIOR
184
+ }
185
+
186
+ if (distance === r1 + r2) {
187
+ return SPHERE3_RELATIVE_POSITION.TANGENT_OUTSIDE
188
+ }
189
+
190
+ if(distance===0) {
191
+ return r1===r2 ? SPHERE3_RELATIVE_POSITION.SUPERPOSED : SPHERE3_RELATIVE_POSITION.CONCENTRIC
192
+ }
193
+
194
+ if (distance === Math.abs(r1 - r2)) {
195
+ return SPHERE3_RELATIVE_POSITION.TANGENT_INSIDE
196
+ }
197
+
198
+
199
+ if (distance < Math.abs(r1 - r2)) {
200
+ return SPHERE3_RELATIVE_POSITION.INTERIOR
201
+ }
202
+
203
+ return SPHERE3_RELATIVE_POSITION.SECANT
204
+
205
+ }
206
+
207
+ isPointOnSphere = (P: Point): boolean => {
208
+ return this.#equation?.test({
209
+ x: P.x,
210
+ y: P.y,
211
+ z: P.z
212
+ }) ?? false
213
+ }
214
+ }