pimath 0.1.39 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pimath.js +3127 -2865
- package/dist/pimath.js.map +1 -1
- package/package.json +16 -12
- package/src/algebra/equation.ts +558 -0
- package/src/algebra/equationSolver.ts +488 -0
- package/src/algebra/factor.ts +338 -0
- package/src/algebra/index.ts +11 -0
- package/src/algebra/linearSystem.ts +439 -0
- package/src/algebra/logicalset.ts +255 -0
- package/src/algebra/matrix.ts +474 -0
- package/src/algebra/monom.ts +977 -0
- package/src/algebra/operations.ts +23 -0
- package/src/algebra/polyFactor.ts +668 -0
- package/src/algebra/polynom.ts +1247 -0
- package/src/analyze/index.ts +4 -0
- package/src/analyze/solution.ts +178 -0
- package/src/analyze/tableOfSigns.ts +30 -0
- package/src/coefficients/fraction.ts +718 -0
- package/src/coefficients/index.ts +4 -0
- package/src/coefficients/root.ts +346 -0
- package/src/geometry/TupleN.ts +128 -0
- package/src/geometry/circle.ts +456 -0
- package/src/geometry/geomMath.ts +71 -0
- package/src/geometry/index.ts +11 -0
- package/src/geometry/line.ts +653 -0
- package/src/geometry/line3.ts +211 -0
- package/src/geometry/plane3.ts +179 -0
- package/src/geometry/point.ts +104 -0
- package/src/geometry/sphere3.ts +214 -0
- package/src/geometry/triangle.ts +482 -0
- package/src/geometry/vector.ts +225 -0
- package/src/helpers.ts +35 -0
- package/src/index.ts +61 -0
- package/src/numeric.ts +196 -0
- package/src/pimath.interface.ts +162 -0
- package/src/randomization/algebra/rndEquation.ts +41 -0
- package/src/randomization/algebra/rndMonom.ts +39 -0
- package/src/randomization/algebra/rndPolynom.ts +100 -0
- package/src/randomization/coefficient/rndFraction.ts +38 -0
- package/src/randomization/geometry/rndCircle.ts +27 -0
- package/src/randomization/geometry/rndLine.ts +35 -0
- package/src/randomization/geometry/rndLine3.ts +27 -0
- package/src/randomization/geometry/rndVector.ts +63 -0
- package/src/randomization/random.ts +89 -0
- package/src/randomization/rndHelpers.ts +102 -0
- package/src/randomization/rndTypes.ts +67 -0
- package/types/algebra/equation.d.ts +18 -17
- package/types/algebra/equation.d.ts.map +1 -1
- package/types/algebra/equationSolver.d.ts +7 -3
- 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 +9 -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 +27 -0
- package/types/analyze/solution.d.ts.map +1 -0
- package/types/analyze/tableOfSigns.d.ts +9 -0
- package/types/analyze/tableOfSigns.d.ts.map +1 -0
- 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 +41 -0
- package/types/coefficients/root.d.ts.map +1 -0
- 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 +9 -9
- package/types/geometry/plane3.d.ts.map +1 -1
- package/types/geometry/point.d.ts +12 -7
- 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/helpers.d.ts +1 -0
- package/types/helpers.d.ts.map +1 -1
- package/types/index.d.ts +6 -4
- package/types/index.d.ts.map +1 -1
- package/types/numeric.d.ts +2 -0
- package/types/numeric.d.ts.map +1 -1
- package/types/pimath.interface.d.ts +38 -44
- 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
|
@@ -0,0 +1,456 @@
|
|
|
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
|
+
}
|
|
15
|
+
|
|
16
|
+
export class Circle
|
|
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
|
|
23
|
+
|
|
24
|
+
constructor()
|
|
25
|
+
constructor(equation: string | Equation)
|
|
26
|
+
constructor(circle: Circle)
|
|
27
|
+
constructor(center: Point, radius: Fraction | number, square?: boolean)
|
|
28
|
+
constructor(center: Point, pointThrough: Point)
|
|
29
|
+
constructor(A: Point, B: Point, C: Point)
|
|
30
|
+
constructor(...values: unknown[]) {
|
|
31
|
+
if (values.length > 0) {
|
|
32
|
+
this.parse(...values)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
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
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
clone(): Circle {
|
|
79
|
+
return new Circle().fromCenterRadius(
|
|
80
|
+
this.center.clone(),
|
|
81
|
+
this.squareRadius.clone(),
|
|
82
|
+
true
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
copy(circle: Circle): this {
|
|
87
|
+
this.#center = circle.center.clone()
|
|
88
|
+
this.#squareRadius = circle.squareRadius.clone()
|
|
89
|
+
|
|
90
|
+
this.#calculateCartesian()
|
|
91
|
+
|
|
92
|
+
return this
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get tex(): string {
|
|
96
|
+
if (this.#output_style === CIRCLE_DISPLAY.CANONICAL) {
|
|
97
|
+
return this.equation.moveLeft().reduce().tex
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let cx, cy
|
|
101
|
+
if (this.center.x.isZero()) {
|
|
102
|
+
cx = 'x^2'
|
|
103
|
+
} else {
|
|
104
|
+
cx = `\\left(x${this.center.x.isNegative() ? '+' : '-'}${this.center.x.clone().abs().tex}\\right)^2`
|
|
105
|
+
}
|
|
106
|
+
if (this.center.y.isZero()) {
|
|
107
|
+
cy = 'y^2'
|
|
108
|
+
} else {
|
|
109
|
+
cy = `\\left(y${this.center.y.isNegative() ? '+' : '-'}${this.center.y.clone().abs().tex}\\right)^2`
|
|
110
|
+
}
|
|
111
|
+
return `${cx}+${cy}=${this.squareRadius.tex}`
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
get display(): string {
|
|
115
|
+
if (this.#output_style === CIRCLE_DISPLAY.CANONICAL) {
|
|
116
|
+
return this.equation.moveLeft().reduce().display
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let cx, cy
|
|
120
|
+
if (this.center.x.isZero()) {
|
|
121
|
+
cx = 'x^2'
|
|
122
|
+
} else {
|
|
123
|
+
cx = `(x${this.center.x.isNegative() ? '+' : '-'}${this.center.x.clone().abs().tex})^2`
|
|
124
|
+
}
|
|
125
|
+
if (this.center.y.isZero()) {
|
|
126
|
+
cy = 'y^2'
|
|
127
|
+
} else {
|
|
128
|
+
cy = `(y${this.center.y.isNegative() ? '+' : '-'}${this.center.y.clone().abs().tex})^2`
|
|
129
|
+
}
|
|
130
|
+
return `${cx}+${cy}=${this.squareRadius.display}`
|
|
131
|
+
}
|
|
132
|
+
|
|
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
|
+
}
|
|
142
|
+
|
|
143
|
+
get center(): Point {
|
|
144
|
+
return this.#center ?? new Point()
|
|
145
|
+
}
|
|
146
|
+
|
|
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))
|
|
164
|
+
} else {
|
|
165
|
+
this.#squareRadius = new Fraction(radius).pow(2)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.#calculateCartesian()
|
|
169
|
+
|
|
170
|
+
return this
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fromEquation(equ: Equation): this {
|
|
174
|
+
// Move everything to the left.
|
|
175
|
+
equ.moveLeft()
|
|
176
|
+
|
|
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
|
|
181
|
+
|
|
182
|
+
// Both square monoms must have the same coefficient.
|
|
183
|
+
if (x2.coefficient.isEqual(y2.coefficient)) {
|
|
184
|
+
equ.divide(x2.coefficient)
|
|
185
|
+
|
|
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))
|
|
196
|
+
|
|
197
|
+
this.#calculateCartesian()
|
|
198
|
+
|
|
199
|
+
return this
|
|
200
|
+
} else {
|
|
201
|
+
// The circle is not a valid circle
|
|
202
|
+
this.#reset()
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return this
|
|
207
|
+
}
|
|
208
|
+
|
|
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
|
|
215
|
+
}
|
|
216
|
+
|
|
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
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
fromString(str: string): this {
|
|
227
|
+
return this.fromEquation(new Equation(str))
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
getPointsOnCircle(): Point[] {
|
|
231
|
+
// It means searching for pythagorician triples that make a perfect square.
|
|
232
|
+
// (x-4)^2 + (y+3)^2 = 15
|
|
233
|
+
const triplets = Numeric.pythagoreanTripletsWithTarget(this.squareRadius.value, true)
|
|
234
|
+
|
|
235
|
+
const points: Point[] = []
|
|
236
|
+
|
|
237
|
+
triplets.forEach(triplet => {
|
|
238
|
+
// Allow positive / negative values
|
|
239
|
+
// x-a = t => x = a + t
|
|
240
|
+
// x-a = -t => x = a - t
|
|
241
|
+
for (const k of [[1, 1], [-1, 1], [-1, -1], [1, -1]]) {
|
|
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
|
+
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
return points
|
|
253
|
+
}
|
|
254
|
+
|
|
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
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
setRadius(radius: Fraction | number, square?: boolean): this {
|
|
357
|
+
if (square) {
|
|
358
|
+
this.#squareRadius = new Fraction(radius)
|
|
359
|
+
} else {
|
|
360
|
+
this.#squareRadius = new Fraction(radius).pow(2)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
this.#calculateCartesian()
|
|
364
|
+
return this
|
|
365
|
+
}
|
|
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
|
+
|
|
401
|
+
#tangentsThroughOnePointOnTheCircle = (P: Point): Line[] => {
|
|
402
|
+
const CT = new Vector(this.center, P)
|
|
403
|
+
return [new Line().fromPointAndNormal(P, CT)]
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
#tangentsThroughOnePointOutsideTheCircle = (P: Point): Line[] => {
|
|
407
|
+
// y = mx + h
|
|
408
|
+
// px, py => h = -m px + py => mx - y -m.px + py = 0 =>
|
|
409
|
+
// Centre: cx, cy, radius: r
|
|
410
|
+
// (m.cx - cy -m.px + py)^2 = r^2 * (m^2 + 1)
|
|
411
|
+
// (m(cx-py) - (cy - py))^2 = r^2 * (m^2 + 1)
|
|
412
|
+
|
|
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')
|
|
417
|
+
|
|
418
|
+
polyLeft.multiply(cx_px).subtract(cy_py).pow(2)
|
|
419
|
+
polyRight.multiply(this.squareRadius)
|
|
420
|
+
|
|
421
|
+
const equ = new Equation(polyLeft, polyRight)
|
|
422
|
+
|
|
423
|
+
const solutions = equ.solve()
|
|
424
|
+
|
|
425
|
+
return solutions.map(sol => {
|
|
426
|
+
// h = -m px + py
|
|
427
|
+
let h: Fraction
|
|
428
|
+
const equ = new Equation('y', 'x')
|
|
429
|
+
|
|
430
|
+
if (sol.exact) {
|
|
431
|
+
h = P.x.clone().opposite().multiply(sol.fraction).add(P.y)
|
|
432
|
+
equ.right.multiply(sol.fraction).add(h)
|
|
433
|
+
} else {
|
|
434
|
+
h = P.x.clone().opposite().multiply(sol.value).add(P.y)
|
|
435
|
+
equ.right.multiply(sol.value).add(h)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return new Line(equ)
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
#tangentsWithSlope = (slope: Fraction): Line[] => {
|
|
444
|
+
// d(C;t)=r => ac1+bc2 + x = +- sqrt(a^2 + b^2)*r
|
|
445
|
+
// x = -ac1-bc2 +- sqrt(a^2 + b^2)*r
|
|
446
|
+
// y = a/bx + h => ax-by + H = 0
|
|
447
|
+
|
|
448
|
+
const a = slope.numerator, b = -slope.denominator, c1 = this.center.x.clone(), c2 = this.center.y.clone()
|
|
449
|
+
|
|
450
|
+
const sq = this.squareRadius.clone().multiply(slope.numerator ** 2 + slope.denominator ** 2),
|
|
451
|
+
x1 = c1.clone().multiply(a).opposite().subtract(c2.clone().multiply(b)).add(sq.clone().sqrt()),
|
|
452
|
+
x2 = c1.clone().multiply(a).opposite().subtract(c2.clone().multiply(b)).subtract(sq.clone().sqrt())
|
|
453
|
+
|
|
454
|
+
return [new Line(a, b, x1), new Line(a, b, x2)]
|
|
455
|
+
}
|
|
456
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {Fraction} from "../coefficients"
|
|
2
|
+
import type {Vector} from "./vector"
|
|
3
|
+
import type {Point} from "./point"
|
|
4
|
+
|
|
5
|
+
type V = Vector | Point
|
|
6
|
+
export function areVectorsEquals(v1: V, v2: V): boolean {
|
|
7
|
+
return v1.dimension === v2.dimension &&
|
|
8
|
+
v1.array.every(
|
|
9
|
+
(value, index) => {
|
|
10
|
+
return v2.array[index].isEqual(value)
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function areVectorsColinears(v1: V, v2: V): boolean {
|
|
15
|
+
if (v1.dimension !== v2.dimension) { return false }
|
|
16
|
+
|
|
17
|
+
// Constant of proportionality
|
|
18
|
+
const k = v2.array[0].value / v1.array[0].value
|
|
19
|
+
return v1.array.every(
|
|
20
|
+
(value, index) => {
|
|
21
|
+
return v2.array[index].value === value.value * k
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
export function dotProduct(v1: V, v2: V): Fraction {
|
|
27
|
+
if (v1.dimension !== v2.dimension) { return new Fraction().invalid() }
|
|
28
|
+
|
|
29
|
+
// Calculate the dot product
|
|
30
|
+
// Why does the reduce not add the last element?
|
|
31
|
+
|
|
32
|
+
return v1.array.reduce(
|
|
33
|
+
(acc, value, index) => {
|
|
34
|
+
return acc.add(value.clone().multiply(v2.array[index]))
|
|
35
|
+
}, new Fraction(0))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function determinantFromVectors(...values: V[]): Fraction {
|
|
39
|
+
// TODO: Make it work for vectors of dimension n
|
|
40
|
+
// Check if the vectors are in the same dimension
|
|
41
|
+
if (values.some((value) => value.dimension !== values[0].dimension)) {
|
|
42
|
+
throw new Error('All vectors must have the same dimension')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check if the vectors are in dimension 2 or 3 and that the number of values is correct
|
|
46
|
+
if (values[0].dimension !== values.length ) {
|
|
47
|
+
throw new Error(`The determinant of dimension ${values[0].dimension} must have the same number of vectors (${values.length} given)`)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Calculate the determinant 2x2
|
|
51
|
+
if (values[0].dimension === 2) {
|
|
52
|
+
return values[0].array[0].clone().multiply(values[1].array[1])
|
|
53
|
+
.subtract(values[0].array[1].clone().multiply(values[1].array[0]))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Calculate the determinant 3x3
|
|
57
|
+
return values[0].array[0].clone()
|
|
58
|
+
.multiply(
|
|
59
|
+
values[1].array[1].clone().multiply(values[2].array[2])
|
|
60
|
+
.subtract(values[1].array[2].clone().multiply(values[2].array[1]))
|
|
61
|
+
)
|
|
62
|
+
.subtract(values[0].array[1].clone()
|
|
63
|
+
.multiply(
|
|
64
|
+
values[1].array[0].clone().multiply(values[2].array[2])
|
|
65
|
+
.subtract(values[1].array[2].clone().multiply(values[2].array[0]))
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
.add(values[0].array[2].clone()
|
|
69
|
+
.multiply(values[1].array[0].clone().multiply(values[2].array[1])
|
|
70
|
+
.subtract(values[1].array[1].clone().multiply(values[2].array[0]))))
|
|
71
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Import / export every files from the geometry folder
|
|
2
|
+
|
|
3
|
+
export * from './geomMath'
|
|
4
|
+
export * from './circle'
|
|
5
|
+
export * from './line'
|
|
6
|
+
export * from './line3'
|
|
7
|
+
export * from './plane3'
|
|
8
|
+
export * from './point'
|
|
9
|
+
export * from './triangle'
|
|
10
|
+
export * from './vector'
|
|
11
|
+
export * from './sphere3'
|