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.
- package/dist/pimath.js +3106 -2873
- package/dist/pimath.js.map +1 -1
- package/package.json +13 -11
- package/src/algebra/equation.ts +113 -111
- package/src/algebra/equationSolver.ts +69 -120
- package/src/algebra/factor.ts +6 -7
- package/src/algebra/linearSystem.ts +97 -46
- package/src/algebra/logicalset.ts +51 -52
- package/src/algebra/monom.ts +23 -61
- package/src/algebra/operations.ts +0 -1
- package/src/algebra/polyFactor.ts +5 -5
- package/src/algebra/polynom.ts +69 -216
- package/src/analyze/index.ts +4 -0
- package/src/analyze/solution.ts +92 -29
- package/src/analyze/tableOfSigns.ts +1 -1
- package/src/coefficients/fraction.ts +189 -149
- package/src/coefficients/index.ts +1 -1
- package/src/coefficients/root.ts +66 -19
- package/src/geometry/TupleN.ts +128 -0
- package/src/geometry/circle.ts +308 -238
- package/src/geometry/geomMath.ts +4 -3
- package/src/geometry/index.ts +1 -0
- package/src/geometry/line.ts +221 -245
- package/src/geometry/line3.ts +78 -73
- package/src/geometry/plane3.ts +64 -55
- package/src/geometry/point.ts +57 -19
- package/src/geometry/triangle.ts +376 -248
- package/src/geometry/vector.ts +113 -229
- package/src/index.ts +13 -12
- package/src/numeric.ts +6 -9
- package/src/pimath.interface.ts +30 -28
- package/src/randomization/algebra/rndPolynom.ts +29 -15
- package/src/randomization/coefficient/rndFraction.ts +3 -3
- package/src/randomization/geometry/rndLine.ts +8 -10
- package/src/randomization/random.ts +11 -13
- package/src/randomization/rndTypes.ts +16 -12
- package/types/algebra/equation.d.ts +18 -17
- package/types/algebra/equation.d.ts.map +1 -1
- package/types/algebra/equationSolver.d.ts +5 -4
- package/types/algebra/equationSolver.d.ts.map +1 -1
- package/types/algebra/factor.d.ts +1 -1
- package/types/algebra/factor.d.ts.map +1 -1
- package/types/algebra/linearSystem.d.ts +23 -6
- package/types/algebra/linearSystem.d.ts.map +1 -1
- package/types/algebra/logicalset.d.ts +1 -1
- package/types/algebra/logicalset.d.ts.map +1 -1
- package/types/algebra/monom.d.ts +1 -6
- package/types/algebra/monom.d.ts.map +1 -1
- package/types/algebra/operations.d.ts.map +1 -1
- package/types/algebra/polyFactor.d.ts +4 -4
- package/types/algebra/polyFactor.d.ts.map +1 -1
- package/types/algebra/polynom.d.ts +10 -7
- package/types/algebra/polynom.d.ts.map +1 -1
- package/types/analyze/index.d.ts +2 -0
- package/types/analyze/index.d.ts.map +1 -0
- package/types/analyze/solution.d.ts +14 -8
- package/types/analyze/solution.d.ts.map +1 -1
- package/types/coefficients/fraction.d.ts +14 -12
- package/types/coefficients/fraction.d.ts.map +1 -1
- package/types/coefficients/index.d.ts +1 -1
- package/types/coefficients/index.d.ts.map +1 -1
- package/types/coefficients/root.d.ts +3 -0
- package/types/coefficients/root.d.ts.map +1 -1
- package/types/geometry/TupleAbstract.d.ts +22 -0
- package/types/geometry/TupleAbstract.d.ts.map +1 -0
- package/types/geometry/TupleN.d.ts +24 -0
- package/types/geometry/TupleN.d.ts.map +1 -0
- package/types/geometry/circle.d.ts +26 -17
- package/types/geometry/circle.d.ts.map +1 -1
- package/types/geometry/geomMath.d.ts +2 -1
- package/types/geometry/geomMath.d.ts.map +1 -1
- package/types/geometry/index.d.ts.map +1 -1
- package/types/geometry/line.d.ts +21 -30
- package/types/geometry/line.d.ts.map +1 -1
- package/types/geometry/line3.d.ts +19 -19
- package/types/geometry/line3.d.ts.map +1 -1
- package/types/geometry/matrix.d.ts +11 -11
- package/types/geometry/plane3.d.ts +10 -10
- package/types/geometry/plane3.d.ts.map +1 -1
- package/types/geometry/point.d.ts +11 -6
- package/types/geometry/point.d.ts.map +1 -1
- package/types/geometry/triangle.d.ts +68 -23
- package/types/geometry/triangle.d.ts.map +1 -1
- package/types/geometry/vector.d.ts +24 -44
- package/types/geometry/vector.d.ts.map +1 -1
- package/types/index.d.ts +5 -4
- package/types/index.d.ts.map +1 -1
- package/types/numeric.d.ts.map +1 -1
- package/types/pimath.interface.d.ts +18 -24
- package/types/pimath.interface.d.ts.map +1 -1
- package/types/randomization/algebra/rndPolynom.d.ts.map +1 -1
- package/types/randomization/coefficient/rndFraction.d.ts +1 -1
- package/types/randomization/coefficient/rndFraction.d.ts.map +1 -1
- package/types/randomization/geometry/rndLine.d.ts.map +1 -1
- package/types/randomization/random.d.ts +3 -2
- package/types/randomization/random.d.ts.map +1 -1
- package/types/randomization/rndTypes.d.ts +15 -10
- package/types/randomization/rndTypes.d.ts.map +1 -1
- package/src/coefficients/nthRoot.ts +0 -149
package/src/geometry/circle.ts
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
1
|
+
import {Line} from "./line"
|
|
2
|
+
import {Vector} from "./vector"
|
|
3
|
+
import {Numeric} from "../numeric"
|
|
4
|
+
import {Fraction, Root} from "../coefficients"
|
|
5
|
+
import {Equation, Monom, Polynom} from "../algebra"
|
|
6
|
+
import {type IPiMathObject} from "../pimath.interface"
|
|
7
|
+
import {Point} from "./point"
|
|
8
|
+
import {Triangle} from "./triangle"
|
|
9
|
+
import {Solution} from "../analyze"
|
|
10
|
+
|
|
11
|
+
enum CIRCLE_DISPLAY {
|
|
12
|
+
CANONICAL,
|
|
13
|
+
CENTER_RADIUS,
|
|
14
|
+
}
|
|
10
15
|
|
|
11
16
|
export class Circle
|
|
12
|
-
implements
|
|
13
|
-
|
|
14
|
-
#center: Point |
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
+
implements IPiMathObject<Circle> {
|
|
18
|
+
|
|
19
|
+
#center: Point | null = null
|
|
20
|
+
#equation: Equation | null = null
|
|
21
|
+
#output_style: CIRCLE_DISPLAY = CIRCLE_DISPLAY.CENTER_RADIUS
|
|
22
|
+
#squareRadius: Fraction | null = null
|
|
17
23
|
|
|
18
24
|
constructor()
|
|
19
25
|
constructor(equation: string | Equation)
|
|
@@ -27,38 +33,69 @@ export class Circle
|
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
parse(...values: unknown[]): this {
|
|
37
|
+
// Data can be given in these formats:
|
|
38
|
+
// one value, a string -> make it an Equation
|
|
39
|
+
// one value, an Equation
|
|
40
|
+
// one value, a circle -> clone it
|
|
41
|
+
// two values: two points (center and pointThrough)
|
|
42
|
+
// two values: point and Fraction (center and radius)
|
|
43
|
+
// three values: Vector2D, Fraction, Boolean (center, square radius, true)
|
|
44
|
+
|
|
45
|
+
if (typeof values[0] === 'string') {
|
|
46
|
+
return this.fromString(values[0])
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (values[0] instanceof Equation) {
|
|
50
|
+
return this.fromEquation(values[0])
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (values[0] instanceof Circle) {
|
|
54
|
+
return this.copy(values[0])
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (values.length === 2 &&
|
|
58
|
+
values[0] instanceof Point && values[1] instanceof Point
|
|
59
|
+
) {
|
|
60
|
+
// Circle center through one point
|
|
61
|
+
return this.fromCenterPoint(values[0], values[1])
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (values.length >= 2 && values[0]
|
|
65
|
+
instanceof Point &&
|
|
66
|
+
(values[1] instanceof Fraction || typeof values[1] === 'number')
|
|
67
|
+
) {
|
|
68
|
+
// Circle center through one point
|
|
69
|
+
return this.fromCenterRadius(
|
|
70
|
+
values[0], values[1],
|
|
71
|
+
(typeof values[2] === "boolean") ? values[2] : false
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return this
|
|
32
76
|
}
|
|
33
77
|
|
|
34
|
-
|
|
35
|
-
return
|
|
78
|
+
clone(): Circle {
|
|
79
|
+
return new Circle().fromCenterRadius(
|
|
80
|
+
this.center.clone(),
|
|
81
|
+
this.squareRadius.clone(),
|
|
82
|
+
true
|
|
83
|
+
)
|
|
36
84
|
}
|
|
37
85
|
|
|
38
|
-
|
|
39
|
-
|
|
86
|
+
copy(circle: Circle): this {
|
|
87
|
+
this.#center = circle.center.clone()
|
|
88
|
+
this.#squareRadius = circle.squareRadius.clone()
|
|
40
89
|
|
|
41
|
-
|
|
42
|
-
}
|
|
90
|
+
this.#calculateCartesian()
|
|
43
91
|
|
|
44
|
-
|
|
45
|
-
if (this.#squareRadius === undefined) { return { tex: '', display: '', value: 0 } }
|
|
46
|
-
if (this.#squareRadius.isSquare()) {
|
|
47
|
-
return {
|
|
48
|
-
tex: this.#squareRadius.clone().sqrt().tex,
|
|
49
|
-
display: this.#squareRadius.clone().sqrt().display,
|
|
50
|
-
value: this.#squareRadius.clone().sqrt().value
|
|
51
|
-
}
|
|
52
|
-
} else {
|
|
53
|
-
return {
|
|
54
|
-
tex: `\\sqrt{${this.#squareRadius.tex}}`,
|
|
55
|
-
display: `sqrt(${this.#squareRadius.display})`,
|
|
56
|
-
value: this.#squareRadius.clone().sqrt().value
|
|
57
|
-
}
|
|
58
|
-
}
|
|
92
|
+
return this
|
|
59
93
|
}
|
|
60
94
|
|
|
61
95
|
get tex(): string {
|
|
96
|
+
if (this.#output_style === CIRCLE_DISPLAY.CANONICAL) {
|
|
97
|
+
return this.equation.moveLeft().reduce().tex
|
|
98
|
+
}
|
|
62
99
|
|
|
63
100
|
let cx, cy
|
|
64
101
|
if (this.center.x.isZero()) {
|
|
@@ -74,11 +111,11 @@ export class Circle
|
|
|
74
111
|
return `${cx}+${cy}=${this.squareRadius.tex}`
|
|
75
112
|
}
|
|
76
113
|
|
|
77
|
-
get developed(): string {
|
|
78
|
-
return this.cartesian.tex
|
|
79
|
-
}
|
|
80
|
-
|
|
81
114
|
get display(): string {
|
|
115
|
+
if (this.#output_style === CIRCLE_DISPLAY.CANONICAL) {
|
|
116
|
+
return this.equation.moveLeft().reduce().display
|
|
117
|
+
}
|
|
118
|
+
|
|
82
119
|
let cx, cy
|
|
83
120
|
if (this.center.x.isZero()) {
|
|
84
121
|
cx = 'x^2'
|
|
@@ -93,77 +130,106 @@ export class Circle
|
|
|
93
130
|
return `${cx}+${cy}=${this.squareRadius.display}`
|
|
94
131
|
}
|
|
95
132
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
133
|
+
get asCanonical(): this {
|
|
134
|
+
this.#output_style = CIRCLE_DISPLAY.CANONICAL
|
|
135
|
+
return this
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
get asCenterRadius(): this {
|
|
139
|
+
this.#output_style = CIRCLE_DISPLAY.CENTER_RADIUS
|
|
140
|
+
return this
|
|
141
|
+
}
|
|
103
142
|
|
|
104
|
-
|
|
143
|
+
get center(): Point {
|
|
144
|
+
return this.#center ?? new Point()
|
|
145
|
+
}
|
|
105
146
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
147
|
+
get equation(): Equation {
|
|
148
|
+
return this.#equation?.clone() ?? new Equation('0=0')
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fromCenterPoint(center: Point, pointThrough: Point): this {
|
|
152
|
+
this.#center = center.clone()
|
|
153
|
+
this.#squareRadius = new Vector(this.#center, pointThrough).normSquare
|
|
154
|
+
|
|
155
|
+
this.#calculateCartesian()
|
|
156
|
+
|
|
157
|
+
return this
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
fromCenterRadius(center: Point, radius: Fraction | number, square?: boolean): this {
|
|
161
|
+
this.#center = center.clone()
|
|
162
|
+
if (square) {
|
|
163
|
+
this.#squareRadius = (new Fraction(radius))
|
|
110
164
|
} else {
|
|
111
|
-
|
|
165
|
+
this.#squareRadius = new Fraction(radius).pow(2)
|
|
112
166
|
}
|
|
167
|
+
|
|
168
|
+
this.#calculateCartesian()
|
|
169
|
+
|
|
170
|
+
return this
|
|
113
171
|
}
|
|
114
172
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
173
|
+
fromEquation(equ: Equation): this {
|
|
174
|
+
// Move everything to the left.
|
|
175
|
+
equ.moveLeft()
|
|
118
176
|
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
177
|
+
if (equ.degree('x').value === 2 && equ.degree('y').value === 2) {
|
|
178
|
+
// Both must be of degree 2.
|
|
179
|
+
const x2 = equ.left.monomByDegree(2, 'x'), y2 = equ.left.monomByDegree(2, 'y')
|
|
180
|
+
let x1: Monom, y1: Monom, c: Monom
|
|
122
181
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
182
|
+
// Both square monoms must have the same coefficient.
|
|
183
|
+
if (x2.coefficient.isEqual(y2.coefficient)) {
|
|
184
|
+
equ.divide(x2.coefficient)
|
|
126
185
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
186
|
+
x1 = equ.left.monomByDegree(1, 'x')
|
|
187
|
+
y1 = equ.left.monomByDegree(1, 'y')
|
|
188
|
+
|
|
189
|
+
c = equ.left.monomByDegree(0)
|
|
190
|
+
|
|
191
|
+
this.#center = new Point(x1.coefficient.clone().divide(2).opposite(), y1.coefficient.clone().divide(2).opposite())
|
|
192
|
+
|
|
193
|
+
this.#squareRadius = c.coefficient.clone().opposite()
|
|
194
|
+
.add(this.#center.x.clone().pow(2))
|
|
195
|
+
.add(this.#center.y.clone().pow(2))
|
|
132
196
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
197
|
+
this.#calculateCartesian()
|
|
198
|
+
|
|
199
|
+
return this
|
|
200
|
+
} else {
|
|
201
|
+
// The circle is not a valid circle
|
|
202
|
+
this.#reset()
|
|
203
|
+
}
|
|
136
204
|
}
|
|
137
205
|
|
|
138
|
-
return
|
|
206
|
+
return this
|
|
139
207
|
}
|
|
140
208
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
//TODO: Must check it's outside the circle
|
|
148
|
-
return this.#tangentsThroughOnePointOutsideTheCircle(P)
|
|
149
|
-
} else {
|
|
150
|
-
console.log('No tangents as the point is inside !')
|
|
209
|
+
fromPoints(A: Point, B: Point, C: Point): this {
|
|
210
|
+
const T = new Triangle(A, B, C)
|
|
211
|
+
|
|
212
|
+
if (!T.isValid || !T.remarquables) {
|
|
213
|
+
this.#reset()
|
|
214
|
+
return this
|
|
151
215
|
}
|
|
152
|
-
return []
|
|
153
|
-
}
|
|
154
216
|
|
|
155
|
-
|
|
156
|
-
|
|
217
|
+
const mAB = T.getMediators().c.clone()
|
|
218
|
+
const mAC = T.getMediators().b.clone()
|
|
219
|
+
|
|
220
|
+
return this.fromCenterPoint(
|
|
221
|
+
mAB.intersection(mAC).point,
|
|
222
|
+
A
|
|
223
|
+
)
|
|
157
224
|
}
|
|
158
225
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
226
|
+
fromString(str: string): this {
|
|
227
|
+
return this.fromEquation(new Equation(str))
|
|
228
|
+
}
|
|
163
229
|
|
|
230
|
+
getPointsOnCircle(): Point[] {
|
|
164
231
|
// It means searching for pythagorician triples that make a perfect square.
|
|
165
232
|
// (x-4)^2 + (y+3)^2 = 15
|
|
166
|
-
|
|
167
233
|
const triplets = Numeric.pythagoreanTripletsWithTarget(this.squareRadius.value, true)
|
|
168
234
|
|
|
169
235
|
const points: Point[] = []
|
|
@@ -172,33 +238,119 @@ export class Circle
|
|
|
172
238
|
// Allow positive / negative values
|
|
173
239
|
// x-a = t => x = a + t
|
|
174
240
|
// x-a = -t => x = a - t
|
|
175
|
-
|
|
176
241
|
for (const k of [[1, 1], [-1, 1], [-1, -1], [1, -1]]) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
// points.push(pt)
|
|
186
|
-
// }
|
|
242
|
+
// check if the point is not already here.
|
|
243
|
+
const x = this.center.x.clone().add(k[0] * triplet[0])
|
|
244
|
+
const y = this.center.y.clone().add(k[1] * triplet[1])
|
|
245
|
+
|
|
246
|
+
if (points.every(pt => !pt.isEqualXY(x, y))) {
|
|
247
|
+
points.push(new Point(x, y))
|
|
248
|
+
}
|
|
249
|
+
|
|
187
250
|
}
|
|
188
251
|
})
|
|
189
252
|
return points
|
|
190
253
|
}
|
|
191
254
|
|
|
192
|
-
|
|
193
|
-
return
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
255
|
+
isPointOnCircle = (P: Point): boolean => {
|
|
256
|
+
return this.#equation?.test({x: P.x, y: P.y}) ?? false
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
public isSame(circ: Circle): boolean {
|
|
260
|
+
return circ.center.isEqual(this.center)
|
|
261
|
+
&& circ.radius.isEqual(this.radius)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Find the intersection points between the circle and a line. It can be 0, 1 or 2 points.
|
|
266
|
+
* The points are sorted depending on the direction vector of the line.
|
|
267
|
+
* @param L
|
|
268
|
+
*/
|
|
269
|
+
lineIntersection(L: Line): Point[] {
|
|
270
|
+
if (this.#equation === null) return []
|
|
271
|
+
|
|
272
|
+
const center = this.center
|
|
273
|
+
const d = L.d
|
|
274
|
+
const OP = L.OA
|
|
275
|
+
|
|
276
|
+
// A = dx^2+dy^2
|
|
277
|
+
const A = d.normSquare
|
|
278
|
+
|
|
279
|
+
// B = 2 ( dx (x0-cx) + dy(y0-cy) )
|
|
280
|
+
const B = OP.x.clone().subtract(center.x).multiply(d.x)
|
|
281
|
+
.add(OP.y.clone().subtract(center.y).multiply(d.y))
|
|
282
|
+
.multiply(2)
|
|
283
|
+
|
|
284
|
+
// C = (x0-cx)^2 + (y0-cy)^2 - r^2
|
|
285
|
+
const C = OP.x.clone().subtract(center.x).pow(2)
|
|
286
|
+
.add(OP.y.clone().subtract(center.y).pow(2))
|
|
287
|
+
.subtract(this.squareRadius)
|
|
288
|
+
|
|
289
|
+
const sol = Solution.fromQuadratic(A, B, C)
|
|
290
|
+
|
|
291
|
+
if (sol.length === 0) return []
|
|
292
|
+
|
|
293
|
+
// One intersection point
|
|
294
|
+
if (sol.length === 1) { // means exact answer
|
|
295
|
+
const OX = OP.add(d.clone().multiplyByScalar(sol[0].fraction))
|
|
296
|
+
return [
|
|
297
|
+
new Point(OX.x, OX.y)
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Two intersection points
|
|
302
|
+
const OX1 = sol[0].exact
|
|
303
|
+
? OP.clone().add(d.clone().multiplyByScalar(sol[0].fraction))
|
|
304
|
+
: OP.clone().add(d.clone().multiplyByScalar(sol[0].value))
|
|
305
|
+
|
|
306
|
+
const OX2 = sol[1].exact
|
|
307
|
+
? OP.clone().add(d.clone().multiplyByScalar(sol[1].fraction))
|
|
308
|
+
: OP.clone().add(d.clone().multiplyByScalar(sol[1].value))
|
|
309
|
+
|
|
310
|
+
// sort the points depending on the direction vector.
|
|
311
|
+
return [
|
|
312
|
+
new Point(OX1.x, OX1.y),
|
|
313
|
+
new Point(OX2.x, OX2.y),
|
|
314
|
+
].sort((a, b) => {
|
|
315
|
+
// <0 => a before
|
|
316
|
+
if (d.x.isZero()) {
|
|
317
|
+
return d.y.isPositive()
|
|
318
|
+
? a.y.value - b.y.value
|
|
319
|
+
: b.y.value - a.y.value
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return d.x.isPositive()
|
|
323
|
+
? a.x.value - b.x.value
|
|
324
|
+
: b.x.value - a.x.value
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
get radius(): Root {
|
|
329
|
+
return new Root().from(2, this.#squareRadius ?? 0)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Get the relative position between circle and line. It corresponds to the number of intersection.
|
|
334
|
+
* @param {Line} L
|
|
335
|
+
* @returns {number}
|
|
336
|
+
*/
|
|
337
|
+
public relativePosition(L: Line): number {
|
|
338
|
+
if (this.#center === null || this.#squareRadius === null) {
|
|
339
|
+
return -1
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const distance = L.distanceTo(this.#center).pow(2).value
|
|
343
|
+
const radius = this.#squareRadius.value
|
|
344
|
+
|
|
345
|
+
if (distance - radius > 0.0000000001) {
|
|
346
|
+
return 0 // external
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (Math.abs(distance - radius) < 0.0000000001) {
|
|
350
|
+
return 1 // tangent
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return 2 // secant
|
|
202
354
|
}
|
|
203
355
|
|
|
204
356
|
setRadius(radius: Fraction | number, square?: boolean): this {
|
|
@@ -207,13 +359,48 @@ export class Circle
|
|
|
207
359
|
} else {
|
|
208
360
|
this.#squareRadius = new Fraction(radius).pow(2)
|
|
209
361
|
}
|
|
362
|
+
|
|
210
363
|
this.#calculateCartesian()
|
|
211
364
|
return this
|
|
212
365
|
}
|
|
213
366
|
|
|
367
|
+
get squareRadius(): Fraction {
|
|
368
|
+
return this.#squareRadius?.clone() ?? new Fraction(-1)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
tangents = (P: Point | Fraction): Line[] => {
|
|
372
|
+
if (P instanceof Fraction) {
|
|
373
|
+
return this.#tangentsWithSlope(P)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (this.isPointOnCircle(P)) {
|
|
377
|
+
return this.#tangentsThroughOnePointOnTheCircle(P)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (this.#center !== null && this.#center.distanceTo(P).value > this.radius.value) {
|
|
381
|
+
return this.#tangentsThroughOnePointOutsideTheCircle(P)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return []
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
#calculateCartesian(): void {
|
|
388
|
+
this.#equation = (
|
|
389
|
+
new Equation(
|
|
390
|
+
new Polynom(`(x-(${this.center.x.display}))^2+(y-(${this.center.y.display}))^2`),
|
|
391
|
+
new Polynom(this.squareRadius.display))
|
|
392
|
+
).moveLeft()
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
#reset() {
|
|
396
|
+
this.#center = null
|
|
397
|
+
this.#squareRadius = null
|
|
398
|
+
this.#equation = null
|
|
399
|
+
}
|
|
400
|
+
|
|
214
401
|
#tangentsThroughOnePointOnTheCircle = (P: Point): Line[] => {
|
|
215
402
|
const CT = new Vector(this.center, P)
|
|
216
|
-
return [new Line(P, CT
|
|
403
|
+
return [new Line().fromPointAndNormal(P, CT)]
|
|
217
404
|
}
|
|
218
405
|
|
|
219
406
|
#tangentsThroughOnePointOutsideTheCircle = (P: Point): Line[] => {
|
|
@@ -223,13 +410,16 @@ export class Circle
|
|
|
223
410
|
// (m.cx - cy -m.px + py)^2 = r^2 * (m^2 + 1)
|
|
224
411
|
// (m(cx-py) - (cy - py))^2 = r^2 * (m^2 + 1)
|
|
225
412
|
|
|
226
|
-
const cx_px = this.center.x.clone().subtract(P.x)
|
|
227
|
-
|
|
413
|
+
const cx_px = this.center.x.clone().subtract(P.x)
|
|
414
|
+
const cy_py = this.center.y.clone().subtract(P.y)
|
|
415
|
+
const polyLeft = new Polynom('x')
|
|
416
|
+
const polyRight = new Polynom('x^2+1')
|
|
228
417
|
|
|
229
418
|
polyLeft.multiply(cx_px).subtract(cy_py).pow(2)
|
|
230
419
|
polyRight.multiply(this.squareRadius)
|
|
231
420
|
|
|
232
421
|
const equ = new Equation(polyLeft, polyRight)
|
|
422
|
+
|
|
233
423
|
const solutions = equ.solve()
|
|
234
424
|
|
|
235
425
|
return solutions.map(sol => {
|
|
@@ -237,9 +427,9 @@ export class Circle
|
|
|
237
427
|
let h: Fraction
|
|
238
428
|
const equ = new Equation('y', 'x')
|
|
239
429
|
|
|
240
|
-
if (sol.exact
|
|
241
|
-
h = P.x.clone().opposite().multiply(sol.
|
|
242
|
-
equ.right.multiply(sol.
|
|
430
|
+
if (sol.exact) {
|
|
431
|
+
h = P.x.clone().opposite().multiply(sol.fraction).add(P.y)
|
|
432
|
+
equ.right.multiply(sol.fraction).add(h)
|
|
243
433
|
} else {
|
|
244
434
|
h = P.x.clone().opposite().multiply(sol.value).add(P.y)
|
|
245
435
|
equ.right.multiply(sol.value).add(h)
|
|
@@ -263,124 +453,4 @@ export class Circle
|
|
|
263
453
|
|
|
264
454
|
return [new Line(a, b, x1), new Line(a, b, x2)]
|
|
265
455
|
}
|
|
266
|
-
|
|
267
|
-
#reset(): this {
|
|
268
|
-
this.#center = undefined
|
|
269
|
-
this.#squareRadius = undefined
|
|
270
|
-
this.#cartesian = undefined
|
|
271
|
-
|
|
272
|
-
return this
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
parse(...values: unknown[]): this {
|
|
276
|
-
// Data can be given in these formats:
|
|
277
|
-
// one value, a string -> make it an Equation
|
|
278
|
-
// one value, an Equation
|
|
279
|
-
// one value, a circle -> clone it
|
|
280
|
-
// two values: two points (center and pointThrough)
|
|
281
|
-
// two values: point and Fraction (center and radius)
|
|
282
|
-
// three values: Vector2D, Fraction, Boolean (center, square radius, true)
|
|
283
|
-
|
|
284
|
-
this.#reset()
|
|
285
|
-
|
|
286
|
-
if (typeof values[0] === 'string') {
|
|
287
|
-
this.#parseEquation(new Equation(values[0]))
|
|
288
|
-
} else if (values[0] instanceof Equation) {
|
|
289
|
-
this.#parseEquation(values[0])
|
|
290
|
-
} else if (values[0] instanceof Circle) {
|
|
291
|
-
this.#parseCopyCircle(values[0])
|
|
292
|
-
} else if (values[0] instanceof Point && values.length > 1) {
|
|
293
|
-
if (values[1] instanceof Point) {
|
|
294
|
-
if (values[2] instanceof Point) {
|
|
295
|
-
// TODO: Add the method to parse through three points
|
|
296
|
-
// this._parseThroughtThreePoints(values[0], values[1], values[2])
|
|
297
|
-
} else {
|
|
298
|
-
this.#parseCenterAndPointThrough(values[0], values[1])
|
|
299
|
-
}
|
|
300
|
-
} else if (values[1] instanceof Fraction || typeof values[1] === 'number') {
|
|
301
|
-
this.#parseCenterAndRadius(values[0], values[1], (typeof values[2] === "boolean") ? values[2] : false)
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Calculate once the different values.
|
|
306
|
-
this.#calculateCartesian()
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
return this
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
#calculateCartesian() {
|
|
313
|
-
this.#cartesian = (
|
|
314
|
-
new Equation(
|
|
315
|
-
new Polynom(`(x-(${this.center.x.display}))^2+(y-(${this.center.y.display}))^2`),
|
|
316
|
-
new Polynom(this.squareRadius.display))
|
|
317
|
-
).moveLeft()
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
#parseCopyCircle(circle: Circle): this {
|
|
321
|
-
this.#center = circle.center.clone()
|
|
322
|
-
this.#squareRadius = circle.squareRadius.clone()
|
|
323
|
-
this.#calculateCartesian()
|
|
324
|
-
return this
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
#parseCenterAndRadius(center: Point, radius: Fraction | number, square?: boolean): this {
|
|
328
|
-
this.#center = center.clone()
|
|
329
|
-
if (square) {
|
|
330
|
-
this.#squareRadius = (new Fraction(radius))
|
|
331
|
-
} else {
|
|
332
|
-
this.#squareRadius = new Fraction(radius).pow(2)
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
return this
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
#parseCenterAndPointThrough(center: Point, pointThrough: Point): this {
|
|
339
|
-
this.#center = center.clone()
|
|
340
|
-
this.#squareRadius = new Vector(this.#center, pointThrough).normSquare
|
|
341
|
-
return this
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
#parseEquation(equ: Equation): this {
|
|
345
|
-
|
|
346
|
-
// Move everything to the left.
|
|
347
|
-
equ.moveLeft()
|
|
348
|
-
|
|
349
|
-
if (equ.degree('x').value === 2 && equ.degree('y').value === 2) {
|
|
350
|
-
// Both must be of degree 2.
|
|
351
|
-
const x2 = equ.left.monomByDegree(2, 'x'), y2 = equ.left.monomByDegree(2, 'y')
|
|
352
|
-
let x1: Monom, y1: Monom, c: Monom
|
|
353
|
-
|
|
354
|
-
// Both square monoms must have the same coefficient.
|
|
355
|
-
if (x2.coefficient.isEqual(y2.coefficient)) {
|
|
356
|
-
equ.divide(x2.coefficient)
|
|
357
|
-
|
|
358
|
-
x1 = equ.left.monomByDegree(1, 'x')
|
|
359
|
-
y1 = equ.left.monomByDegree(1, 'y')
|
|
360
|
-
|
|
361
|
-
c = equ.left.monomByDegree(0)
|
|
362
|
-
|
|
363
|
-
this.#center = new Point(x1.coefficient.clone().divide(2).opposite(), y1.coefficient.clone().divide(2).opposite())
|
|
364
|
-
|
|
365
|
-
this.#squareRadius = c.coefficient.clone().opposite()
|
|
366
|
-
.add(this.#center.x.clone().pow(2))
|
|
367
|
-
.add(this.#center.y.clone().pow(2))
|
|
368
|
-
|
|
369
|
-
} else {
|
|
370
|
-
// The circle is not a valid circle
|
|
371
|
-
this.#center = undefined
|
|
372
|
-
this.#squareRadius = undefined
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
return this
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// private _parseThroughtThreePoints(A: Point, B: Point, C: Point): this {
|
|
379
|
-
// const T = new Triangle(A, B, C), mAB = T.remarquables.mediators.AB.clone(),
|
|
380
|
-
// mAC = T.remarquables.mediators.AC.clone()
|
|
381
|
-
// this.parse(mAB.intersection(mAC).point, A)
|
|
382
|
-
|
|
383
|
-
// return this
|
|
384
|
-
// }
|
|
385
|
-
|
|
386
456
|
}
|
package/src/geometry/geomMath.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
1
|
+
import {Fraction} from "../coefficients"
|
|
2
|
+
import type {Vector} from "./vector"
|
|
3
|
+
import type {Point} from "./point"
|
|
3
4
|
|
|
4
|
-
type V = Vector
|
|
5
|
+
type V = Vector | Point
|
|
5
6
|
export function areVectorsEquals(v1: V, v2: V): boolean {
|
|
6
7
|
return v1.dimension === v2.dimension &&
|
|
7
8
|
v1.array.every(
|