1
+ /**
2
+ * Module assumption:
3
+ * - Coordinate system of canvas html element
4
+ */
5
+
1
6
export const PI = Math . PI ;
2
7
export const PI2 = 2 * PI ;
3
8
export const PId2 = PI / 2 ;
@@ -24,7 +29,7 @@ export function rad2deg(rad: number) {
24
29
return ( ( rad % PI2 ) / PI2 ) * 360 ;
25
30
}
26
31
27
- class Coordinate < T extends typeof Coordinate = typeof Coordinate > {
32
+ class XY {
28
33
x : number = 0 ;
29
34
y : number = 0 ;
30
35
@@ -38,6 +43,10 @@ class Coordinate<T extends typeof Coordinate = typeof Coordinate> {
38
43
return this ;
39
44
}
40
45
46
+ clone ( ) {
47
+ return new XY ( this . x , this . y ) ;
48
+ }
49
+
41
50
toString ( ) {
42
51
return `{${ this . x } , ${ this . y } }` ;
43
52
}
@@ -58,12 +67,12 @@ class Coordinate<T extends typeof Coordinate = typeof Coordinate> {
58
67
return this ;
59
68
}
60
69
61
- equalXY ( x : number , y : number ) {
70
+ hasSameXY ( x : number , y : number ) {
62
71
return ( x === this . x && y === this . y ) ;
63
72
}
64
73
65
- equal ( c : Coordinate ) {
66
- return this . equalXY ( c . x , c . y ) ;
74
+ isEqualTo ( xy : XY ) {
75
+ return this . hasSameXY ( xy . x , xy . y ) ;
67
76
}
68
77
69
78
round ( precision ?: number ) {
@@ -74,10 +83,7 @@ class Coordinate<T extends typeof Coordinate = typeof Coordinate> {
74
83
}
75
84
}
76
85
77
- /**
78
- * Point {x,y}
79
- */
80
- export class Point extends Coordinate {
86
+ export class Point extends XY {
81
87
constructor ( x : number , y : number ) {
82
88
super ( x , y ) ;
83
89
}
@@ -90,33 +96,32 @@ export class Point extends Coordinate {
90
96
* Get distance between two points
91
97
*/
92
98
proximity ( p : Point ) {
93
- return this . toVector ( p ) . length ( ) ;
99
+ return this . vectorTo ( p ) . length ;
94
100
}
95
101
96
102
/**
97
103
* Convert point to vector
98
104
* Assuming this(x,y) is a v(0,0)
99
105
* @note this(0,0) is at top left corner
100
106
*/
101
- toVector ( to : Point ) {
102
- return new Vector ( to . x - this . x , this . y - to . y ) ;
107
+ vectorTo ( to : XY ) {
108
+ return new Vector ( to . x - this . x , to . y - this . y ) ;
103
109
}
104
110
105
111
/**
106
112
* Rotate point over center `axis` point by an angle
107
- * @note : positive angle value means counterclockwise
113
+ * @note : positive `radAngle` means counterclockwise
108
114
*/
109
- rotate ( angle : number , axis : Point ) {
110
- return axis . toVector ( this ) . rotate ( angle ) . toPoint ( axis ) ;
115
+ rotate ( radAngle : number , axis : Point ) {
116
+ const p = axis . vectorTo ( this ) . rotate ( radAngle ) . atBase ( axis ) ;
117
+ return this . set ( p . x , p . y ) ;
111
118
}
112
119
}
113
120
114
121
/**
115
- * Vector with virtual base of {0,0}
116
- * and assumed position at top-left corner
117
- * that points to {this.x, this.y}
122
+ * Vector with virtual base of {0,0} that points to {x,y}
118
123
*/
119
- export class Vector extends Coordinate {
124
+ export class Vector extends XY {
120
125
constructor ( x : number , y : number ) {
121
126
super ( x , y ) ;
122
127
}
@@ -126,121 +131,85 @@ export class Vector extends Coordinate {
126
131
}
127
132
128
133
/**
129
- * Convert vector to point
130
- * Assuming pBase(x,y) refers to v(0,0)
131
- * @note : base(0,0) is at top left corner
134
+ * Get point where vector points from `base` point of view
135
+ * Assuming base(x,y) refers to v(0,0) of `this` vector
132
136
*/
133
- toPoint ( base : Point ) {
134
- return new Point ( base . x + this . x , base . y - this . y ) ;
137
+ atBase ( base : Point ) {
138
+ return new Point ( base . x + this . x , base . y + this . y ) ;
135
139
}
136
140
137
141
/**
138
- * Create vector rotated on specific angle
142
+ * Rotate at specific angle
139
143
* produced vector may contain float epsilon errors
140
- * @note : Positive angle means counterclockwise
144
+ * @note : positive `radAngle` means counterclockwise
141
145
*/
142
- rotate ( angle : number ) {
143
- const cos = Math . cos ( angle ) ;
144
- const sin = Math . sin ( angle ) ;
145
- return new Vector (
146
+ rotate ( radAngle : number ) {
147
+ const cos = Math . cos ( - radAngle ) ;
148
+ const sin = Math . sin ( - radAngle ) ;
149
+
150
+ return this . set (
146
151
this . x * cos - this . y * sin ,
147
152
this . x * sin + this . y * cos ,
148
153
) ;
149
154
}
150
155
151
- /**
152
- * Create vector rotated to left
153
- */
154
156
rotateLeft ( ) {
155
- return new Vector ( - this . y , this . x ) ;
157
+ return this . set ( - this . y , this . x ) ;
156
158
}
157
159
158
- /**
159
- * Create vector rotated to right
160
- */
161
160
rotateRight ( ) {
162
- return new Vector ( this . y , - this . x ) ;
161
+ return this . set ( this . y , - this . x ) ;
163
162
}
164
163
165
- /**
166
- * Create vector rotated backwards
167
- */
168
164
rotateBack ( ) {
169
- return new Vector ( - this . x , - this . y ) ;
165
+ return this . set ( - this . x , - this . y ) ;
170
166
}
171
167
172
- /**
173
- * Return new vector mirrored over another vector interpreted as rotation axis
174
- */
175
- mirror ( axis : Vector ) {
176
- const delta = this . xAxisAngle ( ) - axis . xAxisAngle ( ) ;
168
+ mirrorOver ( axis : Vector ) {
169
+ const delta = this . angleWithX - axis . angleWithX ;
177
170
const k = delta >= 0 ? - 2 : 2 ;
178
171
return this . rotate ( k * this . angle ( axis ) ) ;
179
172
}
180
173
181
- length ( ) {
174
+ get length ( ) {
182
175
return Math . sqrt ( this . dot ( this ) ) ;
183
176
}
184
177
185
- /**
186
- * Return new vector with new vector length
187
- */
188
178
setLength ( newLength : number ) {
189
- return ( new Vector ( newLength , 0 ) ) . rotate ( this . xAxisAngle ( ) ) ;
179
+ const v = new Vector ( newLength , 0 ) . rotate ( this . angleWithX ) ;
180
+ return this . set ( v . x , v . y ) ;
190
181
}
191
182
192
- /**
193
- * Return new vector halved in length
194
- */
195
183
half ( ) {
196
- return new Vector ( this . x / 2 , this . y / 2 ) ;
184
+ return this . set ( this . x / 2 , this . y / 2 ) ;
197
185
}
198
186
199
187
/**
200
- * Vector normalization
188
+ * Get angle of vector relative to X axis in range [0 ... α ... PI2] counterclockwise
201
189
*/
202
- normalize ( ) {
203
- const length = this . length ( ) ;
204
- return new Vector ( this . x / length , this . y / length ) ;
205
- }
190
+ get angleWithX ( ) {
191
+ const angle = Math . atan2 ( - this . y , this . x ) ;
206
192
207
- /**
208
- * Dot product of two vectors
209
- */
210
- dot ( v : Vector ) {
211
- return ( this . x * v . x + this . y * v . y ) ;
193
+ return ( angle < 0 ) ? angle + PI2 : angle ;
212
194
}
213
195
214
196
angle ( v : Vector ) {
215
197
return Math . acos ( this . normalize ( ) . dot ( v . normalize ( ) ) ) ;
216
198
}
217
199
218
- angleWithNorth ( ) {
219
- return this . angle ( new Vector ( 0 , 1 ) ) ;
220
- }
221
-
222
- angleWithEast ( ) {
223
- return this . angle ( new Vector ( 1 , 0 ) ) ;
224
- }
225
-
226
- angleWithSought ( ) {
227
- return this . angle ( new Vector ( 0 , - 1 ) ) ;
228
- }
229
-
230
- angleWithWest ( ) {
231
- return this . angle ( new Vector ( - 1 , 0 ) ) ;
200
+ /**
201
+ * Return normalized vector
202
+ */
203
+ normalize ( ) {
204
+ const length = this . length ;
205
+ return new Vector ( this . x / length , this . y / length ) ;
232
206
}
233
207
234
208
/**
235
- * Get angle of vector relative to OX in range [0 ... α ... XY.PI2]
236
- * @note produced angle will be always positive radian
209
+ * Dot product of two vectors
237
210
*/
238
- xAxisAngle ( ) {
239
- let angle = Math . atan2 ( this . y , this . x ) ;
240
- if ( angle < 0 ) {
241
- angle += PI2 ;
242
- }
243
- return angle ;
211
+ dot ( v : Vector ) {
212
+ return ( this . x * v . x + this . y * v . y ) ;
244
213
}
245
214
}
246
215
@@ -280,9 +249,10 @@ export class Box {
280
249
281
250
toString ( ) {
282
251
return JSON . stringify ( {
283
- tl : this . tl . toString ( ) ,
284
252
w : this . w ,
285
253
h : this . h ,
254
+ tl : this . tl . toString ( ) ,
255
+ c : this . c . toString ( ) ,
286
256
} ) ;
287
257
}
288
258
0 commit comments