Flat, round, designer-friendly pseudo-3D engine for canvas & SVG
1/**
2 * Vector
3 */
4
5( function( root, factory ) {
6 // module definition
7 if ( typeof module == 'object' && module.exports ) {
8 // CommonJS
9 module.exports = factory( require('./boilerplate') );
10 } else {
11 // browser global
12 var Zdog = root.Zdog;
13 Zdog.Vector = factory( Zdog );
14 }
15
16}( this, function factory( utils ) {
17
18function Vector( position ) {
19 this.set( position );
20}
21
22var TAU = utils.TAU;
23
24// 'pos' = 'position'
25Vector.prototype.set = function( pos ) {
26 this.x = pos && pos.x || 0;
27 this.y = pos && pos.y || 0;
28 this.z = pos && pos.z || 0;
29 return this;
30};
31
32// set coordinates without sanitizing
33// vec.write({ y: 2 }) only sets y coord
34Vector.prototype.write = function( pos ) {
35 if ( !pos ) {
36 return this;
37 }
38 this.x = pos.x != undefined ? pos.x : this.x;
39 this.y = pos.y != undefined ? pos.y : this.y;
40 this.z = pos.z != undefined ? pos.z : this.z;
41 return this;
42};
43
44Vector.prototype.rotate = function( rotation ) {
45 if ( !rotation ) {
46 return;
47 }
48 this.rotateZ( rotation.z );
49 this.rotateY( rotation.y );
50 this.rotateX( rotation.x );
51 return this;
52};
53
54Vector.prototype.rotateZ = function( angle ) {
55 rotateProperty( this, angle, 'x', 'y' );
56};
57
58Vector.prototype.rotateX = function( angle ) {
59 rotateProperty( this, angle, 'y', 'z' );
60};
61
62Vector.prototype.rotateY = function( angle ) {
63 rotateProperty( this, angle, 'x', 'z' );
64};
65
66function rotateProperty( vec, angle, propA, propB ) {
67 if ( !angle || angle % TAU === 0 ) {
68 return;
69 }
70 var cos = Math.cos( angle );
71 var sin = Math.sin( angle );
72 var a = vec[ propA ];
73 var b = vec[ propB ];
74 vec[ propA ] = a * cos - b * sin;
75 vec[ propB ] = b * cos + a * sin;
76}
77
78Vector.prototype.isSame = function( pos ) {
79 if ( !pos ) {
80 return false;
81 }
82 return this.x === pos.x && this.y === pos.y && this.z === pos.z;
83};
84
85Vector.prototype.add = function( pos ) {
86 if ( !pos ) {
87 return this;
88 }
89 this.x += pos.x || 0;
90 this.y += pos.y || 0;
91 this.z += pos.z || 0;
92 return this;
93};
94
95Vector.prototype.subtract = function( pos ) {
96 if ( !pos ) {
97 return this;
98 }
99 this.x -= pos.x || 0;
100 this.y -= pos.y || 0;
101 this.z -= pos.z || 0;
102 return this;
103};
104
105Vector.prototype.multiply = function( pos ) {
106 if ( pos == undefined ) {
107 return this;
108 }
109 // multiple all values by same number
110 if ( typeof pos == 'number' ) {
111 this.x *= pos;
112 this.y *= pos;
113 this.z *= pos;
114 } else {
115 // multiply object
116 this.x *= pos.x != undefined ? pos.x : 1;
117 this.y *= pos.y != undefined ? pos.y : 1;
118 this.z *= pos.z != undefined ? pos.z : 1;
119 }
120 return this;
121};
122
123Vector.prototype.transform = function( translation, rotation, scale ) {
124 this.multiply( scale );
125 this.rotate( rotation );
126 this.add( translation );
127 return this;
128};
129
130Vector.prototype.lerp = function( pos, alpha ) {
131 this.x = utils.lerp( this.x, pos.x || 0, alpha );
132 this.y = utils.lerp( this.y, pos.y || 0, alpha );
133 this.z = utils.lerp( this.z, pos.z || 0, alpha );
134 return this;
135};
136
137Vector.prototype.magnitude = function() {
138 var sum = this.x * this.x + this.y * this.y + this.z * this.z;
139 return getMagnitudeSqrt( sum );
140};
141
142function getMagnitudeSqrt( sum ) {
143 // PERF: check if sum ~= 1 and skip sqrt
144 if ( Math.abs( sum - 1 ) < 0.00000001 ) {
145 return 1;
146 }
147 return Math.sqrt( sum );
148}
149
150Vector.prototype.magnitude2d = function() {
151 var sum = this.x * this.x + this.y * this.y;
152 return getMagnitudeSqrt( sum );
153};
154
155Vector.prototype.copy = function() {
156 return new Vector( this );
157};
158
159function round( num ) {
160 return Math.round( num * 1000 ) / 1000;
161}
162
163Vector.prototype.toJSON = function() {
164 var x = this.x;
165 var y = this.y;
166 var z = this.z;
167
168 if ( x === y && y === z ) {
169 return x !== 0 ? round( x ) : undefined;
170 }
171
172 var obj = { x: x, y: y, z: z };
173 var result = {};
174
175 Object.keys( obj ).forEach( function( key ) {
176 var value = obj[ key ];
177 if ( value !== 0 ) {
178 result[ key ] = round( value );
179 }
180 });
181
182 return Object.keys( result ).length ? result : undefined;
183};
184
185return Vector;
186
187} ) );