1+ /**
2+ * Module assumption:
3+ * - Coordinate system of canvas html element
4+ */
5+
16export const PI = Math . PI ;
27export const PI2 = 2 * PI ;
38export const PId2 = PI / 2 ;
@@ -24,7 +29,7 @@ export function rad2deg(rad: number) {
2429 return ( ( rad % PI2 ) / PI2 ) * 360 ;
2530}
2631
27- class Coordinate < T extends typeof Coordinate = typeof Coordinate > {
32+ class XY {
2833 x : number = 0 ;
2934 y : number = 0 ;
3035
@@ -38,6 +43,10 @@ class Coordinate<T extends typeof Coordinate = typeof Coordinate> {
3843 return this ;
3944 }
4045
46+ clone ( ) {
47+ return new XY ( this . x , this . y ) ;
48+ }
49+
4150 toString ( ) {
4251 return `{${ this . x } , ${ this . y } }` ;
4352 }
@@ -58,12 +67,12 @@ class Coordinate<T extends typeof Coordinate = typeof Coordinate> {
5867 return this ;
5968 }
6069
61- equalXY ( x : number , y : number ) {
70+ hasSameXY ( x : number , y : number ) {
6271 return ( x === this . x && y === this . y ) ;
6372 }
6473
65- equal ( c : Coordinate ) {
66- return this . equalXY ( c . x , c . y ) ;
74+ isEqualTo ( xy : XY ) {
75+ return this . hasSameXY ( xy . x , xy . y ) ;
6776 }
6877
6978 round ( precision ?: number ) {
@@ -74,10 +83,7 @@ class Coordinate<T extends typeof Coordinate = typeof Coordinate> {
7483 }
7584}
7685
77- /**
78- * Point {x,y}
79- */
80- export class Point extends Coordinate {
86+ export class Point extends XY {
8187 constructor ( x : number , y : number ) {
8288 super ( x , y ) ;
8389 }
@@ -90,33 +96,32 @@ export class Point extends Coordinate {
9096 * Get distance between two points
9197 */
9298 proximity ( p : Point ) {
93- return this . toVector ( p ) . length ( ) ;
99+ return this . vectorTo ( p ) . length ;
94100 }
95101
96102 /**
97103 * Convert point to vector
98104 * Assuming this(x,y) is a v(0,0)
99105 * @note this(0,0) is at top left corner
100106 */
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 ) ;
103109 }
104110
105111 /**
106112 * Rotate point over center `axis` point by an angle
107- * @note : positive angle value means counterclockwise
113+ * @note : positive `radAngle` means counterclockwise
108114 */
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 ) ;
111118 }
112119}
113120
114121/**
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}
118123 */
119- export class Vector extends Coordinate {
124+ export class Vector extends XY {
120125 constructor ( x : number , y : number ) {
121126 super ( x , y ) ;
122127 }
@@ -126,121 +131,85 @@ export class Vector extends Coordinate {
126131 }
127132
128133 /**
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
132136 */
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 ) ;
135139 }
136140
137141 /**
138- * Create vector rotated on specific angle
142+ * Rotate at specific angle
139143 * produced vector may contain float epsilon errors
140- * @note : Positive angle means counterclockwise
144+ * @note : positive `radAngle` means counterclockwise
141145 */
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 (
146151 this . x * cos - this . y * sin ,
147152 this . x * sin + this . y * cos ,
148153 ) ;
149154 }
150155
151- /**
152- * Create vector rotated to left
153- */
154156 rotateLeft ( ) {
155- return new Vector ( - this . y , this . x ) ;
157+ return this . set ( - this . y , this . x ) ;
156158 }
157159
158- /**
159- * Create vector rotated to right
160- */
161160 rotateRight ( ) {
162- return new Vector ( this . y , - this . x ) ;
161+ return this . set ( this . y , - this . x ) ;
163162 }
164163
165- /**
166- * Create vector rotated backwards
167- */
168164 rotateBack ( ) {
169- return new Vector ( - this . x , - this . y ) ;
165+ return this . set ( - this . x , - this . y ) ;
170166 }
171167
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 ;
177170 const k = delta >= 0 ? - 2 : 2 ;
178171 return this . rotate ( k * this . angle ( axis ) ) ;
179172 }
180173
181- length ( ) {
174+ get length ( ) {
182175 return Math . sqrt ( this . dot ( this ) ) ;
183176 }
184177
185- /**
186- * Return new vector with new vector length
187- */
188178 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 ) ;
190181 }
191182
192- /**
193- * Return new vector halved in length
194- */
195183 half ( ) {
196- return new Vector ( this . x / 2 , this . y / 2 ) ;
184+ return this . set ( this . x / 2 , this . y / 2 ) ;
197185 }
198186
199187 /**
200- * Vector normalization
188+ * Get angle of vector relative to X axis in range [0 ... α ... PI2] counterclockwise
201189 */
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 ) ;
206192
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 ;
212194 }
213195
214196 angle ( v : Vector ) {
215197 return Math . acos ( this . normalize ( ) . dot ( v . normalize ( ) ) ) ;
216198 }
217199
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 ) ;
232206 }
233207
234208 /**
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
237210 */
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 ) ;
244213 }
245214}
246215
@@ -280,9 +249,10 @@ export class Box {
280249
281250 toString ( ) {
282251 return JSON . stringify ( {
283- tl : this . tl . toString ( ) ,
284252 w : this . w ,
285253 h : this . h ,
254+ tl : this . tl . toString ( ) ,
255+ c : this . c . toString ( ) ,
286256 } ) ;
287257 }
288258
0 commit comments