rob@100
|
1 ;(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
rob@100
|
2 // build script for generating processing.js
|
rob@100
|
3
|
rob@100
|
4 var Browser = {
|
rob@100
|
5 isDomPresent: true,
|
rob@100
|
6 navigator: navigator,
|
rob@100
|
7 window: window,
|
rob@100
|
8 document: document,
|
rob@100
|
9 ajax: function(url) {
|
rob@100
|
10 var xhr = new XMLHttpRequest();
|
rob@100
|
11 xhr.open("GET", url, false);
|
rob@100
|
12 if (xhr.overrideMimeType) {
|
rob@100
|
13 xhr.overrideMimeType("text/plain");
|
rob@100
|
14 }
|
rob@100
|
15 xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT");
|
rob@100
|
16 xhr.send(null);
|
rob@100
|
17 // failed request?
|
rob@100
|
18 if (xhr.status !== 200 && xhr.status !== 0) { throw ("XMLHttpRequest failed, status code " + xhr.status); }
|
rob@100
|
19 return xhr.responseText;
|
rob@100
|
20 }
|
rob@100
|
21 };
|
rob@100
|
22
|
rob@100
|
23 window.Processing = require('./src/')(Browser);
|
rob@100
|
24
|
rob@100
|
25 },{"./src/":27}],2:[function(require,module,exports){
|
rob@100
|
26 module.exports={
|
rob@100
|
27 "name": "Processing.js",
|
rob@100
|
28 "version": "1.4.8",
|
rob@100
|
29 "dependencies": {
|
rob@100
|
30 "argv": "~0.0.2",
|
rob@100
|
31 "browserify": "~2.18.1",
|
rob@100
|
32 "express": "~3.3.3",
|
rob@100
|
33 "node-minify": "~0.7.3",
|
rob@100
|
34 "nunjucks": "~0.1.9",
|
rob@100
|
35 "open": "0.0.3"
|
rob@100
|
36 },
|
rob@100
|
37 "devDependencies": {
|
rob@100
|
38 "grunt": "~0.4.1",
|
rob@100
|
39 "grunt-cli": "~0.1.8",
|
rob@100
|
40 "grunt-contrib-jshint": "~0.4.3"
|
rob@100
|
41 }
|
rob@100
|
42 }
|
rob@100
|
43
|
rob@100
|
44 },{}],3:[function(require,module,exports){
|
rob@100
|
45 /**
|
rob@100
|
46 * A ObjectIterator is an iterator wrapper for objects. If passed object contains
|
rob@100
|
47 * the iterator method, the object instance will be replaced by the result returned by
|
rob@100
|
48 * this method call. If passed object is an array, the ObjectIterator instance iterates
|
rob@100
|
49 * through its items.
|
rob@100
|
50 *
|
rob@100
|
51 * @param {Object} obj The object to be iterated.
|
rob@100
|
52 */
|
rob@100
|
53 module.exports = function ObjectIterator(obj) {
|
rob@100
|
54 if (obj instanceof Array) {
|
rob@100
|
55 // iterate through array items
|
rob@100
|
56 var index = -1;
|
rob@100
|
57 this.hasNext = function() {
|
rob@100
|
58 return ++index < obj.length;
|
rob@100
|
59 };
|
rob@100
|
60 this.next = function() {
|
rob@100
|
61 return obj[index];
|
rob@100
|
62 };
|
rob@100
|
63 } else if (obj.iterator instanceof Function) {
|
rob@100
|
64 return obj.iterator();
|
rob@100
|
65 } else {
|
rob@100
|
66 throw "Unable to iterate: " + obj;
|
rob@100
|
67 }
|
rob@100
|
68 };
|
rob@100
|
69
|
rob@100
|
70 },{}],4:[function(require,module,exports){
|
rob@100
|
71 /**
|
rob@100
|
72 * Processing.js environment constants
|
rob@100
|
73 */
|
rob@100
|
74 module.exports = {
|
rob@100
|
75 X: 0,
|
rob@100
|
76 Y: 1,
|
rob@100
|
77 Z: 2,
|
rob@100
|
78
|
rob@100
|
79 R: 3,
|
rob@100
|
80 G: 4,
|
rob@100
|
81 B: 5,
|
rob@100
|
82 A: 6,
|
rob@100
|
83
|
rob@100
|
84 U: 7,
|
rob@100
|
85 V: 8,
|
rob@100
|
86
|
rob@100
|
87 NX: 9,
|
rob@100
|
88 NY: 10,
|
rob@100
|
89 NZ: 11,
|
rob@100
|
90
|
rob@100
|
91 EDGE: 12,
|
rob@100
|
92
|
rob@100
|
93 // Stroke
|
rob@100
|
94 SR: 13,
|
rob@100
|
95 SG: 14,
|
rob@100
|
96 SB: 15,
|
rob@100
|
97 SA: 16,
|
rob@100
|
98
|
rob@100
|
99 SW: 17,
|
rob@100
|
100
|
rob@100
|
101 // Transformations (2D and 3D)
|
rob@100
|
102 TX: 18,
|
rob@100
|
103 TY: 19,
|
rob@100
|
104 TZ: 20,
|
rob@100
|
105
|
rob@100
|
106 VX: 21,
|
rob@100
|
107 VY: 22,
|
rob@100
|
108 VZ: 23,
|
rob@100
|
109 VW: 24,
|
rob@100
|
110
|
rob@100
|
111 // Material properties
|
rob@100
|
112 AR: 25,
|
rob@100
|
113 AG: 26,
|
rob@100
|
114 AB: 27,
|
rob@100
|
115
|
rob@100
|
116 DR: 3,
|
rob@100
|
117 DG: 4,
|
rob@100
|
118 DB: 5,
|
rob@100
|
119 DA: 6,
|
rob@100
|
120
|
rob@100
|
121 SPR: 28,
|
rob@100
|
122 SPG: 29,
|
rob@100
|
123 SPB: 30,
|
rob@100
|
124
|
rob@100
|
125 SHINE: 31,
|
rob@100
|
126
|
rob@100
|
127 ER: 32,
|
rob@100
|
128 EG: 33,
|
rob@100
|
129 EB: 34,
|
rob@100
|
130
|
rob@100
|
131 BEEN_LIT: 35,
|
rob@100
|
132
|
rob@100
|
133 VERTEX_FIELD_COUNT: 36,
|
rob@100
|
134
|
rob@100
|
135 // Renderers
|
rob@100
|
136 P2D: 1,
|
rob@100
|
137 JAVA2D: 1,
|
rob@100
|
138 WEBGL: 2,
|
rob@100
|
139 P3D: 2,
|
rob@100
|
140 OPENGL: 2,
|
rob@100
|
141 PDF: 0,
|
rob@100
|
142 DXF: 0,
|
rob@100
|
143
|
rob@100
|
144 // Platform IDs
|
rob@100
|
145 OTHER: 0,
|
rob@100
|
146 WINDOWS: 1,
|
rob@100
|
147 MAXOSX: 2,
|
rob@100
|
148 LINUX: 3,
|
rob@100
|
149
|
rob@100
|
150 EPSILON: 0.0001,
|
rob@100
|
151
|
rob@100
|
152 MAX_FLOAT: 3.4028235e+38,
|
rob@100
|
153 MIN_FLOAT: -3.4028235e+38,
|
rob@100
|
154 MAX_INT: 2147483647,
|
rob@100
|
155 MIN_INT: -2147483648,
|
rob@100
|
156
|
rob@100
|
157 PI: Math.PI,
|
rob@100
|
158 TWO_PI: 2 * Math.PI,
|
rob@100
|
159 TAU: 2 * Math.PI,
|
rob@100
|
160 HALF_PI: Math.PI / 2,
|
rob@100
|
161 THIRD_PI: Math.PI / 3,
|
rob@100
|
162 QUARTER_PI: Math.PI / 4,
|
rob@100
|
163
|
rob@100
|
164 DEG_TO_RAD: Math.PI / 180,
|
rob@100
|
165 RAD_TO_DEG: 180 / Math.PI,
|
rob@100
|
166
|
rob@100
|
167 WHITESPACE: " \t\n\r\f\u00A0",
|
rob@100
|
168
|
rob@100
|
169 // Color modes
|
rob@100
|
170 RGB: 1,
|
rob@100
|
171 ARGB: 2,
|
rob@100
|
172 HSB: 3,
|
rob@100
|
173 ALPHA: 4,
|
rob@100
|
174 CMYK: 5,
|
rob@100
|
175
|
rob@100
|
176 // Image file types
|
rob@100
|
177 TIFF: 0,
|
rob@100
|
178 TARGA: 1,
|
rob@100
|
179 JPEG: 2,
|
rob@100
|
180 GIF: 3,
|
rob@100
|
181
|
rob@100
|
182 // Filter/convert types
|
rob@100
|
183 BLUR: 11,
|
rob@100
|
184 GRAY: 12,
|
rob@100
|
185 INVERT: 13,
|
rob@100
|
186 OPAQUE: 14,
|
rob@100
|
187 POSTERIZE: 15,
|
rob@100
|
188 THRESHOLD: 16,
|
rob@100
|
189 ERODE: 17,
|
rob@100
|
190 DILATE: 18,
|
rob@100
|
191
|
rob@100
|
192 // Blend modes
|
rob@100
|
193 REPLACE: 0,
|
rob@100
|
194 BLEND: 1 << 0,
|
rob@100
|
195 ADD: 1 << 1,
|
rob@100
|
196 SUBTRACT: 1 << 2,
|
rob@100
|
197 LIGHTEST: 1 << 3,
|
rob@100
|
198 DARKEST: 1 << 4,
|
rob@100
|
199 DIFFERENCE: 1 << 5,
|
rob@100
|
200 EXCLUSION: 1 << 6,
|
rob@100
|
201 MULTIPLY: 1 << 7,
|
rob@100
|
202 SCREEN: 1 << 8,
|
rob@100
|
203 OVERLAY: 1 << 9,
|
rob@100
|
204 HARD_LIGHT: 1 << 10,
|
rob@100
|
205 SOFT_LIGHT: 1 << 11,
|
rob@100
|
206 DODGE: 1 << 12,
|
rob@100
|
207 BURN: 1 << 13,
|
rob@100
|
208
|
rob@100
|
209 // Color component bit masks
|
rob@100
|
210 ALPHA_MASK: 0xff000000,
|
rob@100
|
211 RED_MASK: 0x00ff0000,
|
rob@100
|
212 GREEN_MASK: 0x0000ff00,
|
rob@100
|
213 BLUE_MASK: 0x000000ff,
|
rob@100
|
214
|
rob@100
|
215 // Projection matrices
|
rob@100
|
216 CUSTOM: 0,
|
rob@100
|
217 ORTHOGRAPHIC: 2,
|
rob@100
|
218 PERSPECTIVE: 3,
|
rob@100
|
219
|
rob@100
|
220 // Shapes
|
rob@100
|
221 POINT: 2,
|
rob@100
|
222 POINTS: 2,
|
rob@100
|
223 LINE: 4,
|
rob@100
|
224 LINES: 4,
|
rob@100
|
225 TRIANGLE: 8,
|
rob@100
|
226 TRIANGLES: 9,
|
rob@100
|
227 TRIANGLE_STRIP: 10,
|
rob@100
|
228 TRIANGLE_FAN: 11,
|
rob@100
|
229 QUAD: 16,
|
rob@100
|
230 QUADS: 16,
|
rob@100
|
231 QUAD_STRIP: 17,
|
rob@100
|
232 POLYGON: 20,
|
rob@100
|
233 PATH: 21,
|
rob@100
|
234 RECT: 30,
|
rob@100
|
235 ELLIPSE: 31,
|
rob@100
|
236 ARC: 32,
|
rob@100
|
237 SPHERE: 40,
|
rob@100
|
238 BOX: 41,
|
rob@100
|
239
|
rob@100
|
240 GROUP: 0,
|
rob@100
|
241 PRIMITIVE: 1,
|
rob@100
|
242 //PATH: 21, // shared with Shape PATH
|
rob@100
|
243 GEOMETRY: 3,
|
rob@100
|
244
|
rob@100
|
245 // Shape Vertex
|
rob@100
|
246 VERTEX: 0,
|
rob@100
|
247 BEZIER_VERTEX: 1,
|
rob@100
|
248 CURVE_VERTEX: 2,
|
rob@100
|
249 BREAK: 3,
|
rob@100
|
250 CLOSESHAPE: 4,
|
rob@100
|
251
|
rob@100
|
252 // Shape closing modes
|
rob@100
|
253 OPEN: 1,
|
rob@100
|
254 CLOSE: 2,
|
rob@100
|
255
|
rob@100
|
256 // Shape drawing modes
|
rob@100
|
257 CORNER: 0, // Draw mode convention to use (x, y) to (width, height)
|
rob@100
|
258 CORNERS: 1, // Draw mode convention to use (x1, y1) to (x2, y2) coordinates
|
rob@100
|
259 RADIUS: 2, // Draw mode from the center, and using the radius
|
rob@100
|
260 CENTER_RADIUS: 2, // Deprecated! Use RADIUS instead
|
rob@100
|
261 CENTER: 3, // Draw from the center, using second pair of values as the diameter
|
rob@100
|
262 DIAMETER: 3, // Synonym for the CENTER constant. Draw from the center
|
rob@100
|
263 CENTER_DIAMETER: 3, // Deprecated! Use DIAMETER instead
|
rob@100
|
264
|
rob@100
|
265 // Text vertical alignment modes
|
rob@100
|
266 BASELINE: 0, // Default vertical alignment for text placement
|
rob@100
|
267 TOP: 101, // Align text to the top
|
rob@100
|
268 BOTTOM: 102, // Align text from the bottom, using the baseline
|
rob@100
|
269
|
rob@100
|
270 // UV Texture coordinate modes
|
rob@100
|
271 NORMAL: 1,
|
rob@100
|
272 NORMALIZED: 1,
|
rob@100
|
273 IMAGE: 2,
|
rob@100
|
274
|
rob@100
|
275 // Text placement modes
|
rob@100
|
276 MODEL: 4,
|
rob@100
|
277 SHAPE: 5,
|
rob@100
|
278
|
rob@100
|
279 // Stroke modes
|
rob@100
|
280 SQUARE: 'butt',
|
rob@100
|
281 ROUND: 'round',
|
rob@100
|
282 PROJECT: 'square',
|
rob@100
|
283 MITER: 'miter',
|
rob@100
|
284 BEVEL: 'bevel',
|
rob@100
|
285
|
rob@100
|
286 // Lighting modes
|
rob@100
|
287 AMBIENT: 0,
|
rob@100
|
288 DIRECTIONAL: 1,
|
rob@100
|
289 //POINT: 2, Shared with Shape constant
|
rob@100
|
290 SPOT: 3,
|
rob@100
|
291
|
rob@100
|
292 // Key constants
|
rob@100
|
293
|
rob@100
|
294 // Both key and keyCode will be equal to these values
|
rob@100
|
295 BACKSPACE: 8,
|
rob@100
|
296 TAB: 9,
|
rob@100
|
297 ENTER: 10,
|
rob@100
|
298 RETURN: 13,
|
rob@100
|
299 ESC: 27,
|
rob@100
|
300 DELETE: 127,
|
rob@100
|
301 CODED: 0xffff,
|
rob@100
|
302
|
rob@100
|
303 // p.key will be CODED and p.keyCode will be this value
|
rob@100
|
304 SHIFT: 16,
|
rob@100
|
305 CONTROL: 17,
|
rob@100
|
306 ALT: 18,
|
rob@100
|
307 CAPSLK: 20,
|
rob@100
|
308 PGUP: 33,
|
rob@100
|
309 PGDN: 34,
|
rob@100
|
310 END: 35,
|
rob@100
|
311 HOME: 36,
|
rob@100
|
312 LEFT: 37,
|
rob@100
|
313 UP: 38,
|
rob@100
|
314 RIGHT: 39,
|
rob@100
|
315 DOWN: 40,
|
rob@100
|
316 F1: 112,
|
rob@100
|
317 F2: 113,
|
rob@100
|
318 F3: 114,
|
rob@100
|
319 F4: 115,
|
rob@100
|
320 F5: 116,
|
rob@100
|
321 F6: 117,
|
rob@100
|
322 F7: 118,
|
rob@100
|
323 F8: 119,
|
rob@100
|
324 F9: 120,
|
rob@100
|
325 F10: 121,
|
rob@100
|
326 F11: 122,
|
rob@100
|
327 F12: 123,
|
rob@100
|
328 NUMLK: 144,
|
rob@100
|
329 META: 157,
|
rob@100
|
330 INSERT: 155,
|
rob@100
|
331
|
rob@100
|
332 // Cursor types
|
rob@100
|
333 ARROW: 'default',
|
rob@100
|
334 CROSS: 'crosshair',
|
rob@100
|
335 HAND: 'pointer',
|
rob@100
|
336 MOVE: 'move',
|
rob@100
|
337 TEXT: 'text',
|
rob@100
|
338 WAIT: 'wait',
|
rob@100
|
339 NOCURSOR: "url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto",
|
rob@100
|
340
|
rob@100
|
341 // Hints
|
rob@100
|
342 DISABLE_OPENGL_2X_SMOOTH: 1,
|
rob@100
|
343 ENABLE_OPENGL_2X_SMOOTH: -1,
|
rob@100
|
344 ENABLE_OPENGL_4X_SMOOTH: 2,
|
rob@100
|
345 ENABLE_NATIVE_FONTS: 3,
|
rob@100
|
346 DISABLE_DEPTH_TEST: 4,
|
rob@100
|
347 ENABLE_DEPTH_TEST: -4,
|
rob@100
|
348 ENABLE_DEPTH_SORT: 5,
|
rob@100
|
349 DISABLE_DEPTH_SORT: -5,
|
rob@100
|
350 DISABLE_OPENGL_ERROR_REPORT: 6,
|
rob@100
|
351 ENABLE_OPENGL_ERROR_REPORT: -6,
|
rob@100
|
352 ENABLE_ACCURATE_TEXTURES: 7,
|
rob@100
|
353 DISABLE_ACCURATE_TEXTURES: -7,
|
rob@100
|
354 HINT_COUNT: 10,
|
rob@100
|
355
|
rob@100
|
356 // PJS defined constants
|
rob@100
|
357 SINCOS_LENGTH: 720, // every half degree
|
rob@100
|
358 PRECISIONB: 15, // fixed point precision is limited to 15 bits!!
|
rob@100
|
359 PRECISIONF: 1 << 15,
|
rob@100
|
360 PREC_MAXVAL: (1 << 15) - 1,
|
rob@100
|
361 PREC_ALPHA_SHIFT: 24 - 15,
|
rob@100
|
362 PREC_RED_SHIFT: 16 - 15,
|
rob@100
|
363 NORMAL_MODE_AUTO: 0,
|
rob@100
|
364 NORMAL_MODE_SHAPE: 1,
|
rob@100
|
365 NORMAL_MODE_VERTEX: 2,
|
rob@100
|
366 MAX_LIGHTS: 8
|
rob@100
|
367 };
|
rob@100
|
368
|
rob@100
|
369 },{}],5:[function(require,module,exports){
|
rob@100
|
370 /**
|
rob@100
|
371 * Processing.js default scope
|
rob@100
|
372 */
|
rob@100
|
373 module.exports = function(options) {
|
rob@100
|
374
|
rob@100
|
375 // Building defaultScope. Changing of the prototype protects
|
rob@100
|
376 // internal Processing code from the changes in defaultScope
|
rob@100
|
377 function DefaultScope() {}
|
rob@100
|
378 DefaultScope.prototype = options.PConstants;
|
rob@100
|
379
|
rob@100
|
380 var defaultScope = new DefaultScope();
|
rob@100
|
381
|
rob@100
|
382 // copy over all known Object types and helper objects
|
rob@100
|
383 Object.keys(options).forEach(function(prop) {
|
rob@100
|
384 defaultScope[prop] = options[prop];
|
rob@100
|
385 });
|
rob@100
|
386
|
rob@100
|
387 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
388 // Class inheritance helper methods
|
rob@100
|
389 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
390
|
rob@100
|
391 defaultScope.defineProperty = function(obj, name, desc) {
|
rob@100
|
392 if("defineProperty" in Object) {
|
rob@100
|
393 Object.defineProperty(obj, name, desc);
|
rob@100
|
394 } else {
|
rob@100
|
395 if (desc.hasOwnProperty("get")) {
|
rob@100
|
396 obj.__defineGetter__(name, desc.get);
|
rob@100
|
397 }
|
rob@100
|
398 if (desc.hasOwnProperty("set")) {
|
rob@100
|
399 obj.__defineSetter__(name, desc.set);
|
rob@100
|
400 }
|
rob@100
|
401 }
|
rob@100
|
402 };
|
rob@100
|
403
|
rob@100
|
404 /**
|
rob@100
|
405 * class overloading, part 1
|
rob@100
|
406 */
|
rob@100
|
407 function overloadBaseClassFunction(object, name, basefn) {
|
rob@100
|
408 if (!object.hasOwnProperty(name) || typeof object[name] !== 'function') {
|
rob@100
|
409 // object method is not a function or just inherited from Object.prototype
|
rob@100
|
410 object[name] = basefn;
|
rob@100
|
411 return;
|
rob@100
|
412 }
|
rob@100
|
413 var fn = object[name];
|
rob@100
|
414 if ("$overloads" in fn) {
|
rob@100
|
415 // the object method already overloaded (see defaultScope.addMethod)
|
rob@100
|
416 // let's just change a fallback method
|
rob@100
|
417 fn.$defaultOverload = basefn;
|
rob@100
|
418 return;
|
rob@100
|
419 }
|
rob@100
|
420 if (!("$overloads" in basefn) && fn.length === basefn.length) {
|
rob@100
|
421 // special case when we just overriding the method
|
rob@100
|
422 return;
|
rob@100
|
423 }
|
rob@100
|
424 var overloads, defaultOverload;
|
rob@100
|
425 if ("$overloads" in basefn) {
|
rob@100
|
426 // let's inherit base class overloads to speed up things
|
rob@100
|
427 overloads = basefn.$overloads.slice(0);
|
rob@100
|
428 overloads[fn.length] = fn;
|
rob@100
|
429 defaultOverload = basefn.$defaultOverload;
|
rob@100
|
430 } else {
|
rob@100
|
431 overloads = [];
|
rob@100
|
432 overloads[basefn.length] = basefn;
|
rob@100
|
433 overloads[fn.length] = fn;
|
rob@100
|
434 defaultOverload = fn;
|
rob@100
|
435 }
|
rob@100
|
436 var hubfn = function() {
|
rob@100
|
437 var fn = hubfn.$overloads[arguments.length] ||
|
rob@100
|
438 ("$methodArgsIndex" in hubfn && arguments.length > hubfn.$methodArgsIndex ?
|
rob@100
|
439 hubfn.$overloads[hubfn.$methodArgsIndex] : null) ||
|
rob@100
|
440 hubfn.$defaultOverload;
|
rob@100
|
441 return fn.apply(this, arguments);
|
rob@100
|
442 };
|
rob@100
|
443 hubfn.$overloads = overloads;
|
rob@100
|
444 if ("$methodArgsIndex" in basefn) {
|
rob@100
|
445 hubfn.$methodArgsIndex = basefn.$methodArgsIndex;
|
rob@100
|
446 }
|
rob@100
|
447 hubfn.$defaultOverload = defaultOverload;
|
rob@100
|
448 hubfn.name = name;
|
rob@100
|
449 object[name] = hubfn;
|
rob@100
|
450 }
|
rob@100
|
451
|
rob@100
|
452 /**
|
rob@100
|
453 * class overloading, part 2
|
rob@100
|
454 */
|
rob@100
|
455
|
rob@100
|
456 function extendClass(subClass, baseClass) {
|
rob@100
|
457 function extendGetterSetter(propertyName) {
|
rob@100
|
458 defaultScope.defineProperty(subClass, propertyName, {
|
rob@100
|
459 get: function() {
|
rob@100
|
460 return baseClass[propertyName];
|
rob@100
|
461 },
|
rob@100
|
462 set: function(v) {
|
rob@100
|
463 baseClass[propertyName]=v;
|
rob@100
|
464 },
|
rob@100
|
465 enumerable: true
|
rob@100
|
466 });
|
rob@100
|
467 }
|
rob@100
|
468
|
rob@100
|
469 var properties = [];
|
rob@100
|
470 for (var propertyName in baseClass) {
|
rob@100
|
471 if (typeof baseClass[propertyName] === 'function') {
|
rob@100
|
472 overloadBaseClassFunction(subClass, propertyName, baseClass[propertyName]);
|
rob@100
|
473 } else if(propertyName.charAt(0) !== "$" && !(propertyName in subClass)) {
|
rob@100
|
474 // Delaying the properties extension due to the IE9 bug (see #918).
|
rob@100
|
475 properties.push(propertyName);
|
rob@100
|
476 }
|
rob@100
|
477 }
|
rob@100
|
478 while (properties.length > 0) {
|
rob@100
|
479 extendGetterSetter(properties.shift());
|
rob@100
|
480 }
|
rob@100
|
481
|
rob@100
|
482 subClass.$super = baseClass;
|
rob@100
|
483 }
|
rob@100
|
484
|
rob@100
|
485 /**
|
rob@100
|
486 * class overloading, part 3
|
rob@100
|
487 */
|
rob@100
|
488 defaultScope.extendClassChain = function(base) {
|
rob@100
|
489 var path = [base];
|
rob@100
|
490 for (var self = base.$upcast; self; self = self.$upcast) {
|
rob@100
|
491 extendClass(self, base);
|
rob@100
|
492 path.push(self);
|
rob@100
|
493 base = self;
|
rob@100
|
494 }
|
rob@100
|
495 while (path.length > 0) {
|
rob@100
|
496 path.pop().$self=base;
|
rob@100
|
497 }
|
rob@100
|
498 };
|
rob@100
|
499
|
rob@100
|
500 // static
|
rob@100
|
501 defaultScope.extendStaticMembers = function(derived, base) {
|
rob@100
|
502 extendClass(derived, base);
|
rob@100
|
503 };
|
rob@100
|
504
|
rob@100
|
505 // interface
|
rob@100
|
506 defaultScope.extendInterfaceMembers = function(derived, base) {
|
rob@100
|
507 extendClass(derived, base);
|
rob@100
|
508 };
|
rob@100
|
509
|
rob@100
|
510 /**
|
rob@100
|
511 * Java methods and JavaScript functions differ enough that
|
rob@100
|
512 * we need a special function to make sure it all links up
|
rob@100
|
513 * as classical hierarchical class chains.
|
rob@100
|
514 */
|
rob@100
|
515 defaultScope.addMethod = function(object, name, fn, hasMethodArgs) {
|
rob@100
|
516 var existingfn = object[name];
|
rob@100
|
517 if (existingfn || hasMethodArgs) {
|
rob@100
|
518 var args = fn.length;
|
rob@100
|
519 // builds the overload methods table
|
rob@100
|
520 if ("$overloads" in existingfn) {
|
rob@100
|
521 existingfn.$overloads[args] = fn;
|
rob@100
|
522 } else {
|
rob@100
|
523 var hubfn = function() {
|
rob@100
|
524 var fn = hubfn.$overloads[arguments.length] ||
|
rob@100
|
525 ("$methodArgsIndex" in hubfn && arguments.length > hubfn.$methodArgsIndex ?
|
rob@100
|
526 hubfn.$overloads[hubfn.$methodArgsIndex] : null) ||
|
rob@100
|
527 hubfn.$defaultOverload;
|
rob@100
|
528 return fn.apply(this, arguments);
|
rob@100
|
529 };
|
rob@100
|
530 var overloads = [];
|
rob@100
|
531 if (existingfn) {
|
rob@100
|
532 overloads[existingfn.length] = existingfn;
|
rob@100
|
533 }
|
rob@100
|
534 overloads[args] = fn;
|
rob@100
|
535 hubfn.$overloads = overloads;
|
rob@100
|
536 hubfn.$defaultOverload = existingfn || fn;
|
rob@100
|
537 if (hasMethodArgs) {
|
rob@100
|
538 hubfn.$methodArgsIndex = args;
|
rob@100
|
539 }
|
rob@100
|
540 hubfn.name = name;
|
rob@100
|
541 object[name] = hubfn;
|
rob@100
|
542 }
|
rob@100
|
543 } else {
|
rob@100
|
544 object[name] = fn;
|
rob@100
|
545 }
|
rob@100
|
546 };
|
rob@100
|
547
|
rob@100
|
548 // internal helper function
|
rob@100
|
549 function isNumericalJavaType(type) {
|
rob@100
|
550 if (typeof type !== "string") {
|
rob@100
|
551 return false;
|
rob@100
|
552 }
|
rob@100
|
553 return ["byte", "int", "char", "color", "float", "long", "double"].indexOf(type) !== -1;
|
rob@100
|
554 }
|
rob@100
|
555
|
rob@100
|
556 /**
|
rob@100
|
557 * Java's arrays are pre-filled when declared with
|
rob@100
|
558 * an initial size, but no content. JS arrays are not.
|
rob@100
|
559 */
|
rob@100
|
560 defaultScope.createJavaArray = function(type, bounds) {
|
rob@100
|
561 var result = null,
|
rob@100
|
562 defaultValue = null;
|
rob@100
|
563 if (typeof type === "string") {
|
rob@100
|
564 if (type === "boolean") {
|
rob@100
|
565 defaultValue = false;
|
rob@100
|
566 } else if (isNumericalJavaType(type)) {
|
rob@100
|
567 defaultValue = 0;
|
rob@100
|
568 }
|
rob@100
|
569 }
|
rob@100
|
570 if (typeof bounds[0] === 'number') {
|
rob@100
|
571 var itemsCount = 0 | bounds[0];
|
rob@100
|
572 if (bounds.length <= 1) {
|
rob@100
|
573 result = [];
|
rob@100
|
574 result.length = itemsCount;
|
rob@100
|
575 for (var i = 0; i < itemsCount; ++i) {
|
rob@100
|
576 result[i] = defaultValue;
|
rob@100
|
577 }
|
rob@100
|
578 } else {
|
rob@100
|
579 result = [];
|
rob@100
|
580 var newBounds = bounds.slice(1);
|
rob@100
|
581 for (var j = 0; j < itemsCount; ++j) {
|
rob@100
|
582 result.push(defaultScope.createJavaArray(type, newBounds));
|
rob@100
|
583 }
|
rob@100
|
584 }
|
rob@100
|
585 }
|
rob@100
|
586 return result;
|
rob@100
|
587 };
|
rob@100
|
588
|
rob@100
|
589 // screenWidth and screenHeight are shared by all instances.
|
rob@100
|
590 // and return the width/height of the browser's viewport.
|
rob@100
|
591 defaultScope.defineProperty(defaultScope, 'screenWidth',
|
rob@100
|
592 { get: function() { return window.innerWidth; } });
|
rob@100
|
593
|
rob@100
|
594 defaultScope.defineProperty(defaultScope, 'screenHeight',
|
rob@100
|
595 { get: function() { return window.innerHeight; } });
|
rob@100
|
596
|
rob@100
|
597 return defaultScope;
|
rob@100
|
598 };
|
rob@100
|
599
|
rob@100
|
600 },{}],6:[function(require,module,exports){
|
rob@100
|
601 /**
|
rob@100
|
602 * Finalise the Processing.js object.
|
rob@100
|
603 */
|
rob@100
|
604 module.exports = function finalizeProcessing(Processing, options) {
|
rob@100
|
605
|
rob@100
|
606 // unpack options
|
rob@100
|
607 var window = options.window,
|
rob@100
|
608 document = options.document,
|
rob@100
|
609 XMLHttpRequest = window.XMLHttpRequest,
|
rob@100
|
610 noop = options.noop,
|
rob@100
|
611 isDOMPresent = options.isDOMPresent,
|
rob@100
|
612 version = options.version,
|
rob@100
|
613 undef;
|
rob@100
|
614
|
rob@100
|
615 // versioning
|
rob@100
|
616 Processing.version = (version ? version : "@DEV-VERSION@");
|
rob@100
|
617
|
rob@100
|
618 // Share lib space
|
rob@100
|
619 Processing.lib = {};
|
rob@100
|
620
|
rob@100
|
621 /**
|
rob@100
|
622 * External libraries can be added to the global Processing
|
rob@100
|
623 * objects with the `registerLibrary` function.
|
rob@100
|
624 */
|
rob@100
|
625 Processing.registerLibrary = function(name, library) {
|
rob@100
|
626 Processing.lib[name] = library;
|
rob@100
|
627 if(library.hasOwnProperty("init")) {
|
rob@100
|
628 library.init(defaultScope);
|
rob@100
|
629 }
|
rob@100
|
630 };
|
rob@100
|
631
|
rob@100
|
632 /**
|
rob@100
|
633 * This is the object that acts as our version of PApplet.
|
rob@100
|
634 * This can be called as Processing.Sketch() or as
|
rob@100
|
635 * Processing.Sketch(function) in which case the function
|
rob@100
|
636 * must be an already-compiled-to-JS sketch function.
|
rob@100
|
637 */
|
rob@100
|
638 Processing.Sketch = function(attachFunction) {
|
rob@100
|
639 this.attachFunction = attachFunction;
|
rob@100
|
640 this.options = {
|
rob@100
|
641 pauseOnBlur: false,
|
rob@100
|
642 globalKeyEvents: false
|
rob@100
|
643 };
|
rob@100
|
644
|
rob@100
|
645 /* Optional Sketch event hooks:
|
rob@100
|
646 * onLoad - parsing/preloading is done, before sketch starts
|
rob@100
|
647 * onSetup - setup() has been called, before first draw()
|
rob@100
|
648 * onPause - noLoop() has been called, pausing draw loop
|
rob@100
|
649 * onLoop - loop() has been called, resuming draw loop
|
rob@100
|
650 * onFrameStart - draw() loop about to begin
|
rob@100
|
651 * onFrameEnd - draw() loop finished
|
rob@100
|
652 * onExit - exit() done being called
|
rob@100
|
653 */
|
rob@100
|
654 this.onLoad = noop;
|
rob@100
|
655 this.onSetup = noop;
|
rob@100
|
656 this.onPause = noop;
|
rob@100
|
657 this.onLoop = noop;
|
rob@100
|
658 this.onFrameStart = noop;
|
rob@100
|
659 this.onFrameEnd = noop;
|
rob@100
|
660 this.onExit = noop;
|
rob@100
|
661
|
rob@100
|
662 this.params = {};
|
rob@100
|
663 this.imageCache = {
|
rob@100
|
664 pending: 0,
|
rob@100
|
665 images: {},
|
rob@100
|
666 // Opera requires special administration for preloading
|
rob@100
|
667 operaCache: {},
|
rob@100
|
668 // Specify an optional img arg if the image is already loaded in the DOM,
|
rob@100
|
669 // otherwise href will get loaded.
|
rob@100
|
670 add: function(href, img) {
|
rob@100
|
671 // Prevent muliple loads for an image, in case it gets
|
rob@100
|
672 // preloaded more than once, or is added via JS and then preloaded.
|
rob@100
|
673 if (this.images[href]) {
|
rob@100
|
674 return;
|
rob@100
|
675 }
|
rob@100
|
676
|
rob@100
|
677 if (!isDOMPresent) {
|
rob@100
|
678 this.images[href] = null;
|
rob@100
|
679 }
|
rob@100
|
680
|
rob@100
|
681 // No image in the DOM, kick-off a background load
|
rob@100
|
682 if (!img) {
|
rob@100
|
683 img = new Image();
|
rob@100
|
684 img.onload = (function(owner) {
|
rob@100
|
685 return function() {
|
rob@100
|
686 owner.pending--;
|
rob@100
|
687 };
|
rob@100
|
688 }(this));
|
rob@100
|
689 this.pending++;
|
rob@100
|
690 img.src = href;
|
rob@100
|
691 }
|
rob@100
|
692
|
rob@100
|
693 this.images[href] = img;
|
rob@100
|
694
|
rob@100
|
695 // Opera will not load images until they are inserted into the DOM.
|
rob@100
|
696 if (window.opera) {
|
rob@100
|
697 var div = document.createElement("div");
|
rob@100
|
698 div.appendChild(img);
|
rob@100
|
699 // we can't use "display: none", since that makes it invisible, and thus not load
|
rob@100
|
700 div.style.position = "absolute";
|
rob@100
|
701 div.style.opacity = 0;
|
rob@100
|
702 div.style.width = "1px";
|
rob@100
|
703 div.style.height= "1px";
|
rob@100
|
704 if (!this.operaCache[href]) {
|
rob@100
|
705 document.body.appendChild(div);
|
rob@100
|
706 this.operaCache[href] = div;
|
rob@100
|
707 }
|
rob@100
|
708 }
|
rob@100
|
709 }
|
rob@100
|
710 };
|
rob@100
|
711
|
rob@100
|
712 this.sourceCode = undefined;
|
rob@100
|
713 this.attach = function(processing) {
|
rob@100
|
714 // either attachFunction or sourceCode must be present on attach
|
rob@100
|
715 if(typeof this.attachFunction === "function") {
|
rob@100
|
716 this.attachFunction(processing);
|
rob@100
|
717 } else if(this.sourceCode) {
|
rob@100
|
718 var func = ((new Function("return (" + this.sourceCode + ");"))());
|
rob@100
|
719 func(processing);
|
rob@100
|
720 this.attachFunction = func;
|
rob@100
|
721 } else {
|
rob@100
|
722 throw "Unable to attach sketch to the processing instance";
|
rob@100
|
723 }
|
rob@100
|
724 };
|
rob@100
|
725
|
rob@100
|
726 this.toString = function() {
|
rob@100
|
727 var i;
|
rob@100
|
728 var code = "((function(Sketch) {\n";
|
rob@100
|
729 code += "var sketch = new Sketch(\n" + this.sourceCode + ");\n";
|
rob@100
|
730 for(i in this.options) {
|
rob@100
|
731 if(this.options.hasOwnProperty(i)) {
|
rob@100
|
732 var value = this.options[i];
|
rob@100
|
733 code += "sketch.options." + i + " = " +
|
rob@100
|
734 (typeof value === 'string' ? '\"' + value + '\"' : "" + value) + ";\n";
|
rob@100
|
735 }
|
rob@100
|
736 }
|
rob@100
|
737 for(i in this.imageCache) {
|
rob@100
|
738 if(this.options.hasOwnProperty(i)) {
|
rob@100
|
739 code += "sketch.imageCache.add(\"" + i + "\");\n";
|
rob@100
|
740 }
|
rob@100
|
741 }
|
rob@100
|
742 // TODO serialize fonts
|
rob@100
|
743 code += "return sketch;\n})(Processing.Sketch))";
|
rob@100
|
744 return code;
|
rob@100
|
745 };
|
rob@100
|
746 };
|
rob@100
|
747
|
rob@100
|
748 /**
|
rob@100
|
749 * aggregate all source code into a single file, then rewrite that
|
rob@100
|
750 * source and bind to canvas via new Processing(canvas, sourcestring).
|
rob@100
|
751 * @param {CANVAS} canvas The html canvas element to bind to
|
rob@100
|
752 * @param {String[]} source The array of files that must be loaded
|
rob@100
|
753 */
|
rob@100
|
754 var loadSketchFromSources = Processing.loadSketchFromSources = function(canvas, sources) {
|
rob@100
|
755 var code = [], errors = [], sourcesCount = sources.length, loaded = 0;
|
rob@100
|
756
|
rob@100
|
757 function ajaxAsync(url, callback) {
|
rob@100
|
758 var xhr = new XMLHttpRequest();
|
rob@100
|
759 xhr.onreadystatechange = function() {
|
rob@100
|
760 if (xhr.readyState === 4) {
|
rob@100
|
761 var error;
|
rob@100
|
762 if (xhr.status !== 200 && xhr.status !== 0) {
|
rob@100
|
763 error = "Invalid XHR status " + xhr.status;
|
rob@100
|
764 } else if (xhr.responseText === "") {
|
rob@100
|
765 // Give a hint when loading fails due to same-origin issues on file:/// urls
|
rob@100
|
766 if ( ("withCredentials" in new XMLHttpRequest()) &&
|
rob@100
|
767 (new XMLHttpRequest()).withCredentials === false &&
|
rob@100
|
768 window.location.protocol === "file:" ) {
|
rob@100
|
769 error = "XMLHttpRequest failure, possibly due to a same-origin policy violation. You can try loading this page in another browser, or load it from http://localhost using a local webserver. See the Processing.js README for a more detailed explanation of this problem and solutions.";
|
rob@100
|
770 } else {
|
rob@100
|
771 error = "File is empty.";
|
rob@100
|
772 }
|
rob@100
|
773 }
|
rob@100
|
774
|
rob@100
|
775 callback(xhr.responseText, error);
|
rob@100
|
776 }
|
rob@100
|
777 };
|
rob@100
|
778 xhr.open("GET", url, true);
|
rob@100
|
779 if (xhr.overrideMimeType) {
|
rob@100
|
780 xhr.overrideMimeType("application/json");
|
rob@100
|
781 }
|
rob@100
|
782 xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT"); // no cache
|
rob@100
|
783 xhr.send(null);
|
rob@100
|
784 }
|
rob@100
|
785
|
rob@100
|
786 function loadBlock(index, filename) {
|
rob@100
|
787 function callback(block, error) {
|
rob@100
|
788 code[index] = block;
|
rob@100
|
789 ++loaded;
|
rob@100
|
790 if (error) {
|
rob@100
|
791 errors.push(filename + " ==> " + error);
|
rob@100
|
792 }
|
rob@100
|
793 if (loaded === sourcesCount) {
|
rob@100
|
794 if (errors.length === 0) {
|
rob@100
|
795 try {
|
rob@100
|
796 return new Processing(canvas, code.join("\n"));
|
rob@100
|
797 } catch(e) {
|
rob@100
|
798 console.log("Processing.js: Unable to execute pjs sketch.");
|
rob@100
|
799 throw e;
|
rob@100
|
800 }
|
rob@100
|
801 } else {
|
rob@100
|
802 throw "Processing.js: Unable to load pjs sketch files: " + errors.join("\n");
|
rob@100
|
803 }
|
rob@100
|
804 }
|
rob@100
|
805 }
|
rob@100
|
806 if (filename.charAt(0) === '#') {
|
rob@100
|
807 // trying to get script from the element
|
rob@100
|
808 var scriptElement = document.getElementById(filename.substring(1));
|
rob@100
|
809 if (scriptElement) {
|
rob@100
|
810 callback(scriptElement.text || scriptElement.textContent);
|
rob@100
|
811 } else {
|
rob@100
|
812 callback("", "Unable to load pjs sketch: element with id \'" + filename.substring(1) + "\' was not found");
|
rob@100
|
813 }
|
rob@100
|
814 return;
|
rob@100
|
815 }
|
rob@100
|
816
|
rob@100
|
817 ajaxAsync(filename, callback);
|
rob@100
|
818 }
|
rob@100
|
819
|
rob@100
|
820 for (var i = 0; i < sourcesCount; ++i) {
|
rob@100
|
821 loadBlock(i, sources[i]);
|
rob@100
|
822 }
|
rob@100
|
823 };
|
rob@100
|
824
|
rob@100
|
825 /**
|
rob@100
|
826 * Automatic initialization function.
|
rob@100
|
827 */
|
rob@100
|
828 var init = function() {
|
rob@100
|
829 document.removeEventListener('DOMContentLoaded', init, false);
|
rob@100
|
830
|
rob@100
|
831 // before running through init, clear the instances list, to prevent
|
rob@100
|
832 // sketch duplication when page content is dynamically swapped without
|
rob@100
|
833 // swapping out processing.js
|
rob@100
|
834 processingInstances = [];
|
rob@100
|
835 Processing.instances = processingInstances;
|
rob@100
|
836
|
rob@100
|
837 var canvas = document.getElementsByTagName('canvas'),
|
rob@100
|
838 filenames;
|
rob@100
|
839
|
rob@100
|
840 for (var i = 0, l = canvas.length; i < l; i++) {
|
rob@100
|
841 // datasrc and data-src are deprecated.
|
rob@100
|
842 var processingSources = canvas[i].getAttribute('data-processing-sources');
|
rob@100
|
843 if (processingSources === null) {
|
rob@100
|
844 // Temporary fallback for datasrc and data-src
|
rob@100
|
845 processingSources = canvas[i].getAttribute('data-src');
|
rob@100
|
846 if (processingSources === null) {
|
rob@100
|
847 processingSources = canvas[i].getAttribute('datasrc');
|
rob@100
|
848 }
|
rob@100
|
849 }
|
rob@100
|
850 if (processingSources) {
|
rob@100
|
851 filenames = processingSources.split(/\s+/g);
|
rob@100
|
852 for (var j = 0; j < filenames.length;) {
|
rob@100
|
853 if (filenames[j]) {
|
rob@100
|
854 j++;
|
rob@100
|
855 } else {
|
rob@100
|
856 filenames.splice(j, 1);
|
rob@100
|
857 }
|
rob@100
|
858 }
|
rob@100
|
859 loadSketchFromSources(canvas[i], filenames);
|
rob@100
|
860 }
|
rob@100
|
861 }
|
rob@100
|
862
|
rob@100
|
863 // also process all <script>-indicated sketches, if there are any
|
rob@100
|
864 var s, last, source, instance,
|
rob@100
|
865 nodelist = document.getElementsByTagName('script'),
|
rob@100
|
866 scripts=[];
|
rob@100
|
867
|
rob@100
|
868 // snapshot the DOM, as the nodelist is only a DOM view, and is
|
rob@100
|
869 // updated instantly when a script element is added or removed.
|
rob@100
|
870 for (s = nodelist.length - 1; s >= 0; s--) {
|
rob@100
|
871 scripts.push(nodelist[s]);
|
rob@100
|
872 }
|
rob@100
|
873
|
rob@100
|
874 // iterate over all script elements to see if they contain Processing code
|
rob@100
|
875 for (s = 0, last = scripts.length; s < last; s++) {
|
rob@100
|
876 var script = scripts[s];
|
rob@100
|
877 if (!script.getAttribute) {
|
rob@100
|
878 continue;
|
rob@100
|
879 }
|
rob@100
|
880
|
rob@100
|
881 var type = script.getAttribute("type");
|
rob@100
|
882 if (type && (type.toLowerCase() === "text/processing" || type.toLowerCase() === "application/processing")) {
|
rob@100
|
883 var target = script.getAttribute("data-processing-target");
|
rob@100
|
884 canvas = undef;
|
rob@100
|
885 if (target) {
|
rob@100
|
886 canvas = document.getElementById(target);
|
rob@100
|
887 } else {
|
rob@100
|
888 var nextSibling = script.nextSibling;
|
rob@100
|
889 while (nextSibling && nextSibling.nodeType !== 1) {
|
rob@100
|
890 nextSibling = nextSibling.nextSibling;
|
rob@100
|
891 }
|
rob@100
|
892 if (nextSibling && nextSibling.nodeName.toLowerCase() === "canvas") {
|
rob@100
|
893 canvas = nextSibling;
|
rob@100
|
894 }
|
rob@100
|
895 }
|
rob@100
|
896
|
rob@100
|
897 if (canvas) {
|
rob@100
|
898 if (script.getAttribute("src")) {
|
rob@100
|
899 filenames = script.getAttribute("src").split(/\s+/);
|
rob@100
|
900 loadSketchFromSources(canvas, filenames);
|
rob@100
|
901 continue;
|
rob@100
|
902 }
|
rob@100
|
903 source = script.textContent || script.text;
|
rob@100
|
904 instance = new Processing(canvas, source);
|
rob@100
|
905 }
|
rob@100
|
906 }
|
rob@100
|
907 }
|
rob@100
|
908 };
|
rob@100
|
909
|
rob@100
|
910 /**
|
rob@100
|
911 * automatic loading of all sketches on the page
|
rob@100
|
912 */
|
rob@100
|
913 document.addEventListener('DOMContentLoaded', init, false);
|
rob@100
|
914
|
rob@100
|
915 /**
|
rob@100
|
916 * Make Processing run through init after already having
|
rob@100
|
917 * been set up for a page. This function exists mostly for pages
|
rob@100
|
918 * that swap content in/out without reloading a page.
|
rob@100
|
919 */
|
rob@100
|
920 Processing.reload = function() {
|
rob@100
|
921 if (processingInstances.length > 0) {
|
rob@100
|
922 // unload sketches
|
rob@100
|
923 for (var i = processingInstances.length - 1; i >= 0; i--) {
|
rob@100
|
924 if (processingInstances[i]) {
|
rob@100
|
925 processingInstances[i].exit();
|
rob@100
|
926 }
|
rob@100
|
927 }
|
rob@100
|
928 }
|
rob@100
|
929 // rerun init() to scan the DOM for sketches
|
rob@100
|
930 init();
|
rob@100
|
931 };
|
rob@100
|
932
|
rob@100
|
933 /**
|
rob@100
|
934 * Disable the automatic loading of all sketches on the page.
|
rob@100
|
935 * This will work as long as it's issued before DOMContentLoaded.
|
rob@100
|
936 */
|
rob@100
|
937 Processing.disableInit = function() {
|
rob@100
|
938 document.removeEventListener('DOMContentLoaded', init, false);
|
rob@100
|
939 };
|
rob@100
|
940
|
rob@100
|
941 // done.
|
rob@100
|
942 return Processing;
|
rob@100
|
943 };
|
rob@100
|
944 },{}],7:[function(require,module,exports){
|
rob@100
|
945 /**
|
rob@100
|
946 * Returns Java equals() result for two objects. If the first object
|
rob@100
|
947 * has the "equals" function, it preforms the call of this function.
|
rob@100
|
948 * Otherwise the method uses the JavaScript === operator.
|
rob@100
|
949 *
|
rob@100
|
950 * @param {Object} obj The first object.
|
rob@100
|
951 * @param {Object} other The second object.
|
rob@100
|
952 *
|
rob@100
|
953 * @returns {boolean} true if the objects are equal.
|
rob@100
|
954 */
|
rob@100
|
955 module.exports = function virtEquals(obj, other) {
|
rob@100
|
956 if (obj === null || other === null) {
|
rob@100
|
957 return (obj === null) && (other === null);
|
rob@100
|
958 }
|
rob@100
|
959 if (typeof (obj) === "string") {
|
rob@100
|
960 return obj === other;
|
rob@100
|
961 }
|
rob@100
|
962 if (typeof(obj) !== "object") {
|
rob@100
|
963 return obj === other;
|
rob@100
|
964 }
|
rob@100
|
965 if (obj.equals instanceof Function) {
|
rob@100
|
966 return obj.equals(other);
|
rob@100
|
967 }
|
rob@100
|
968 return obj === other;
|
rob@100
|
969 };
|
rob@100
|
970
|
rob@100
|
971 },{}],8:[function(require,module,exports){
|
rob@100
|
972 /**
|
rob@100
|
973 * Returns Java hashCode() result for the object. If the object has the "hashCode" function,
|
rob@100
|
974 * it preforms the call of this function. Otherwise it uses/creates the "$id" property,
|
rob@100
|
975 * which is used as the hashCode.
|
rob@100
|
976 *
|
rob@100
|
977 * @param {Object} obj The object.
|
rob@100
|
978 * @returns {int} The object's hash code.
|
rob@100
|
979 */
|
rob@100
|
980 module.exports = function virtHashCode(obj, undef) {
|
rob@100
|
981 if (typeof(obj) === "string") {
|
rob@100
|
982 var hash = 0;
|
rob@100
|
983 for (var i = 0; i < obj.length; ++i) {
|
rob@100
|
984 hash = (hash * 31 + obj.charCodeAt(i)) & 0xFFFFFFFF;
|
rob@100
|
985 }
|
rob@100
|
986 return hash;
|
rob@100
|
987 }
|
rob@100
|
988 if (typeof(obj) !== "object") {
|
rob@100
|
989 return obj & 0xFFFFFFFF;
|
rob@100
|
990 }
|
rob@100
|
991 if (obj.hashCode instanceof Function) {
|
rob@100
|
992 return obj.hashCode();
|
rob@100
|
993 }
|
rob@100
|
994 if (obj.$id === undef) {
|
rob@100
|
995 obj.$id = ((Math.floor(Math.random() * 0x10000) - 0x8000) << 16) | Math.floor(Math.random() * 0x10000);
|
rob@100
|
996 }
|
rob@100
|
997 return obj.$id;
|
rob@100
|
998 };
|
rob@100
|
999
|
rob@100
|
1000 },{}],9:[function(require,module,exports){
|
rob@100
|
1001 /**
|
rob@100
|
1002 * An ArrayList stores a variable number of objects.
|
rob@100
|
1003 *
|
rob@100
|
1004 * @param {int} initialCapacity optional defines the initial capacity of the list, it's empty by default
|
rob@100
|
1005 *
|
rob@100
|
1006 * @returns {ArrayList} new ArrayList object
|
rob@100
|
1007 */
|
rob@100
|
1008 module.exports = function(options) {
|
rob@100
|
1009 var virtHashCode = options.virtHashCode,
|
rob@100
|
1010 virtEquals = options.virtEquals;
|
rob@100
|
1011
|
rob@100
|
1012 function Iterator(array) {
|
rob@100
|
1013 var index = -1;
|
rob@100
|
1014 this.hasNext = function() {
|
rob@100
|
1015 return (index + 1) < array.length;
|
rob@100
|
1016 };
|
rob@100
|
1017
|
rob@100
|
1018 this.next = function() {
|
rob@100
|
1019 return array[++index];
|
rob@100
|
1020 };
|
rob@100
|
1021
|
rob@100
|
1022 this.remove = function() {
|
rob@100
|
1023 array.splice(index--, 1);
|
rob@100
|
1024 };
|
rob@100
|
1025 }
|
rob@100
|
1026
|
rob@100
|
1027 function ArrayList(a) {
|
rob@100
|
1028 var array = [];
|
rob@100
|
1029
|
rob@100
|
1030 if (a && a.toArray) {
|
rob@100
|
1031 array = a.toArray();
|
rob@100
|
1032 }
|
rob@100
|
1033
|
rob@100
|
1034 /**
|
rob@100
|
1035 * @member ArrayList
|
rob@100
|
1036 * ArrayList.get() Returns the element at the specified position in this list.
|
rob@100
|
1037 *
|
rob@100
|
1038 * @param {int} i index of element to return
|
rob@100
|
1039 *
|
rob@100
|
1040 * @returns {Object} the element at the specified position in this list.
|
rob@100
|
1041 */
|
rob@100
|
1042 this.get = function(i) {
|
rob@100
|
1043 return array[i];
|
rob@100
|
1044 };
|
rob@100
|
1045 /**
|
rob@100
|
1046 * @member ArrayList
|
rob@100
|
1047 * ArrayList.contains() Returns true if this list contains the specified element.
|
rob@100
|
1048 *
|
rob@100
|
1049 * @param {Object} item element whose presence in this List is to be tested.
|
rob@100
|
1050 *
|
rob@100
|
1051 * @returns {boolean} true if the specified element is present; false otherwise.
|
rob@100
|
1052 */
|
rob@100
|
1053 this.contains = function(item) {
|
rob@100
|
1054 return this.indexOf(item)>-1;
|
rob@100
|
1055 };
|
rob@100
|
1056 /**
|
rob@100
|
1057 * @member ArrayList
|
rob@100
|
1058 * ArrayList.indexOf() Returns the position this element takes in the list, or -1 if the element is not found.
|
rob@100
|
1059 *
|
rob@100
|
1060 * @param {Object} item element whose position in this List is to be tested.
|
rob@100
|
1061 *
|
rob@100
|
1062 * @returns {int} the list position that the first match for this element holds in the list, or -1 if it is not in the list.
|
rob@100
|
1063 */
|
rob@100
|
1064 this.indexOf = function(item) {
|
rob@100
|
1065 for (var i = 0, len = array.length; i < len; ++i) {
|
rob@100
|
1066 if (virtEquals(item, array[i])) {
|
rob@100
|
1067 return i;
|
rob@100
|
1068 }
|
rob@100
|
1069 }
|
rob@100
|
1070 return -1;
|
rob@100
|
1071 };
|
rob@100
|
1072 /**
|
rob@100
|
1073 * @member ArrayList
|
rob@100
|
1074 * ArrayList.lastIndexOf() Returns the index of the last occurrence of the specified element in this list,
|
rob@100
|
1075 * or -1 if this list does not contain the element. More formally, returns the highest index i such that
|
rob@100
|
1076 * (o==null ? get(i)==null : o.equals(get(i))), or -1 if there is no such index.
|
rob@100
|
1077 *
|
rob@100
|
1078 * @param {Object} item element to search for.
|
rob@100
|
1079 *
|
rob@100
|
1080 * @returns {int} the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
|
rob@100
|
1081 */
|
rob@100
|
1082 this.lastIndexOf = function(item) {
|
rob@100
|
1083 for (var i = array.length-1; i >= 0; --i) {
|
rob@100
|
1084 if (virtEquals(item, array[i])) {
|
rob@100
|
1085 return i;
|
rob@100
|
1086 }
|
rob@100
|
1087 }
|
rob@100
|
1088 return -1;
|
rob@100
|
1089 };
|
rob@100
|
1090 /**
|
rob@100
|
1091 * @member ArrayList
|
rob@100
|
1092 * ArrayList.add() Adds the specified element to this list.
|
rob@100
|
1093 *
|
rob@100
|
1094 * @param {int} index optional index at which the specified element is to be inserted
|
rob@100
|
1095 * @param {Object} object element to be added to the list
|
rob@100
|
1096 */
|
rob@100
|
1097 this.add = function() {
|
rob@100
|
1098 if (arguments.length === 1) {
|
rob@100
|
1099 array.push(arguments[0]); // for add(Object)
|
rob@100
|
1100 } else if (arguments.length === 2) {
|
rob@100
|
1101 var arg0 = arguments[0];
|
rob@100
|
1102 if (typeof arg0 === 'number') {
|
rob@100
|
1103 if (arg0 >= 0 && arg0 <= array.length) {
|
rob@100
|
1104 array.splice(arg0, 0, arguments[1]); // for add(i, Object)
|
rob@100
|
1105 } else {
|
rob@100
|
1106 throw(arg0 + " is not a valid index");
|
rob@100
|
1107 }
|
rob@100
|
1108 } else {
|
rob@100
|
1109 throw(typeof arg0 + " is not a number");
|
rob@100
|
1110 }
|
rob@100
|
1111 } else {
|
rob@100
|
1112 throw("Please use the proper number of parameters.");
|
rob@100
|
1113 }
|
rob@100
|
1114 };
|
rob@100
|
1115 /**
|
rob@100
|
1116 * @member ArrayList
|
rob@100
|
1117 * ArrayList.addAll(collection) appends all of the elements in the specified
|
rob@100
|
1118 * Collection to the end of this list, in the order that they are returned by
|
rob@100
|
1119 * the specified Collection's Iterator.
|
rob@100
|
1120 *
|
rob@100
|
1121 * When called as addAll(index, collection) the elements are inserted into
|
rob@100
|
1122 * this list at the position indicated by index.
|
rob@100
|
1123 *
|
rob@100
|
1124 * @param {index} Optional; specifies the position the colletion should be inserted at
|
rob@100
|
1125 * @param {collection} Any iterable object (ArrayList, HashMap.keySet(), etc.)
|
rob@100
|
1126 * @throws out of bounds error for negative index, or index greater than list size.
|
rob@100
|
1127 */
|
rob@100
|
1128 this.addAll = function(arg1, arg2) {
|
rob@100
|
1129 // addAll(int, Collection)
|
rob@100
|
1130 var it;
|
rob@100
|
1131 if (typeof arg1 === "number") {
|
rob@100
|
1132 if (arg1 < 0 || arg1 > array.length) {
|
rob@100
|
1133 throw("Index out of bounds for addAll: " + arg1 + " greater or equal than " + array.length);
|
rob@100
|
1134 }
|
rob@100
|
1135 it = new ObjectIterator(arg2);
|
rob@100
|
1136 while (it.hasNext()) {
|
rob@100
|
1137 array.splice(arg1++, 0, it.next());
|
rob@100
|
1138 }
|
rob@100
|
1139 }
|
rob@100
|
1140 // addAll(Collection)
|
rob@100
|
1141 else {
|
rob@100
|
1142 it = new ObjectIterator(arg1);
|
rob@100
|
1143 while (it.hasNext()) {
|
rob@100
|
1144 array.push(it.next());
|
rob@100
|
1145 }
|
rob@100
|
1146 }
|
rob@100
|
1147 };
|
rob@100
|
1148 /**
|
rob@100
|
1149 * @member ArrayList
|
rob@100
|
1150 * ArrayList.set() Replaces the element at the specified position in this list with the specified element.
|
rob@100
|
1151 *
|
rob@100
|
1152 * @param {int} index index of element to replace
|
rob@100
|
1153 * @param {Object} object element to be stored at the specified position
|
rob@100
|
1154 */
|
rob@100
|
1155 this.set = function() {
|
rob@100
|
1156 if (arguments.length === 2) {
|
rob@100
|
1157 var arg0 = arguments[0];
|
rob@100
|
1158 if (typeof arg0 === 'number') {
|
rob@100
|
1159 if (arg0 >= 0 && arg0 < array.length) {
|
rob@100
|
1160 array.splice(arg0, 1, arguments[1]);
|
rob@100
|
1161 } else {
|
rob@100
|
1162 throw(arg0 + " is not a valid index.");
|
rob@100
|
1163 }
|
rob@100
|
1164 } else {
|
rob@100
|
1165 throw(typeof arg0 + " is not a number");
|
rob@100
|
1166 }
|
rob@100
|
1167 } else {
|
rob@100
|
1168 throw("Please use the proper number of parameters.");
|
rob@100
|
1169 }
|
rob@100
|
1170 };
|
rob@100
|
1171
|
rob@100
|
1172 /**
|
rob@100
|
1173 * @member ArrayList
|
rob@100
|
1174 * ArrayList.size() Returns the number of elements in this list.
|
rob@100
|
1175 *
|
rob@100
|
1176 * @returns {int} the number of elements in this list
|
rob@100
|
1177 */
|
rob@100
|
1178 this.size = function() {
|
rob@100
|
1179 return array.length;
|
rob@100
|
1180 };
|
rob@100
|
1181
|
rob@100
|
1182 /**
|
rob@100
|
1183 * @member ArrayList
|
rob@100
|
1184 * ArrayList.clear() Removes all of the elements from this list. The list will be empty after this call returns.
|
rob@100
|
1185 */
|
rob@100
|
1186 this.clear = function() {
|
rob@100
|
1187 array.length = 0;
|
rob@100
|
1188 };
|
rob@100
|
1189
|
rob@100
|
1190 /**
|
rob@100
|
1191 * @member ArrayList
|
rob@100
|
1192 * ArrayList.remove() Removes an element either based on index, if the argument is a number, or
|
rob@100
|
1193 * by equality check, if the argument is an object.
|
rob@100
|
1194 *
|
rob@100
|
1195 * @param {int|Object} item either the index of the element to be removed, or the element itself.
|
rob@100
|
1196 *
|
rob@100
|
1197 * @returns {Object|boolean} If removal is by index, the element that was removed, or null if nothing was removed. If removal is by object, true if removal occurred, otherwise false.
|
rob@100
|
1198 */
|
rob@100
|
1199 this.remove = function(item) {
|
rob@100
|
1200 if (typeof item === 'number') {
|
rob@100
|
1201 return array.splice(item, 1)[0];
|
rob@100
|
1202 }
|
rob@100
|
1203 item = this.indexOf(item);
|
rob@100
|
1204 if (item > -1) {
|
rob@100
|
1205 array.splice(item, 1);
|
rob@100
|
1206 return true;
|
rob@100
|
1207 }
|
rob@100
|
1208 return false;
|
rob@100
|
1209 };
|
rob@100
|
1210
|
rob@100
|
1211 /**
|
rob@100
|
1212 * @member ArrayList
|
rob@100
|
1213 * ArrayList.removeAll Removes from this List all of the elements from
|
rob@100
|
1214 * the current ArrayList which are present in the passed in paramater ArrayList 'c'.
|
rob@100
|
1215 * Shifts any succeeding elements to the left (reduces their index).
|
rob@100
|
1216 *
|
rob@100
|
1217 * @param {ArrayList} the ArrayList to compare to the current ArrayList
|
rob@100
|
1218 *
|
rob@100
|
1219 * @returns {boolean} true if the ArrayList had an element removed; false otherwise
|
rob@100
|
1220 */
|
rob@100
|
1221 this.removeAll = function(c) {
|
rob@100
|
1222 var i, x, item,
|
rob@100
|
1223 newList = new ArrayList();
|
rob@100
|
1224 newList.addAll(this);
|
rob@100
|
1225 this.clear();
|
rob@100
|
1226 // For every item that exists in the original ArrayList and not in the c ArrayList
|
rob@100
|
1227 // copy it into the empty 'this' ArrayList to create the new 'this' Array.
|
rob@100
|
1228 for (i = 0, x = 0; i < newList.size(); i++) {
|
rob@100
|
1229 item = newList.get(i);
|
rob@100
|
1230 if (!c.contains(item)) {
|
rob@100
|
1231 this.add(x++, item);
|
rob@100
|
1232 }
|
rob@100
|
1233 }
|
rob@100
|
1234 if (this.size() < newList.size()) {
|
rob@100
|
1235 return true;
|
rob@100
|
1236 }
|
rob@100
|
1237 return false;
|
rob@100
|
1238 };
|
rob@100
|
1239
|
rob@100
|
1240 /**
|
rob@100
|
1241 * @member ArrayList
|
rob@100
|
1242 * ArrayList.isEmpty() Tests if this list has no elements.
|
rob@100
|
1243 *
|
rob@100
|
1244 * @returns {boolean} true if this list has no elements; false otherwise
|
rob@100
|
1245 */
|
rob@100
|
1246 this.isEmpty = function() {
|
rob@100
|
1247 return !array.length;
|
rob@100
|
1248 };
|
rob@100
|
1249
|
rob@100
|
1250 /**
|
rob@100
|
1251 * @member ArrayList
|
rob@100
|
1252 * ArrayList.clone() Returns a shallow copy of this ArrayList instance. (The elements themselves are not copied.)
|
rob@100
|
1253 *
|
rob@100
|
1254 * @returns {ArrayList} a clone of this ArrayList instance
|
rob@100
|
1255 */
|
rob@100
|
1256 this.clone = function() {
|
rob@100
|
1257 return new ArrayList(this);
|
rob@100
|
1258 };
|
rob@100
|
1259
|
rob@100
|
1260 /**
|
rob@100
|
1261 * @member ArrayList
|
rob@100
|
1262 * ArrayList.toArray() Returns an array containing all of the elements in this list in the correct order.
|
rob@100
|
1263 *
|
rob@100
|
1264 * @returns {Object[]} Returns an array containing all of the elements in this list in the correct order
|
rob@100
|
1265 */
|
rob@100
|
1266 this.toArray = function() {
|
rob@100
|
1267 return array.slice(0);
|
rob@100
|
1268 };
|
rob@100
|
1269
|
rob@100
|
1270 this.iterator = function() {
|
rob@100
|
1271 return new Iterator(array);
|
rob@100
|
1272 };
|
rob@100
|
1273 }
|
rob@100
|
1274
|
rob@100
|
1275 return ArrayList;
|
rob@100
|
1276 };
|
rob@100
|
1277
|
rob@100
|
1278 },{}],10:[function(require,module,exports){
|
rob@100
|
1279 module.exports = (function(charMap, undef) {
|
rob@100
|
1280
|
rob@100
|
1281 var Char = function(chr) {
|
rob@100
|
1282 if (typeof chr === 'string' && chr.length === 1) {
|
rob@100
|
1283 this.code = chr.charCodeAt(0);
|
rob@100
|
1284 } else if (typeof chr === 'number') {
|
rob@100
|
1285 this.code = chr;
|
rob@100
|
1286 } else if (chr instanceof Char) {
|
rob@100
|
1287 this.code = chr;
|
rob@100
|
1288 } else {
|
rob@100
|
1289 this.code = NaN;
|
rob@100
|
1290 }
|
rob@100
|
1291 return (charMap[this.code] === undef) ? charMap[this.code] = this : charMap[this.code];
|
rob@100
|
1292 };
|
rob@100
|
1293
|
rob@100
|
1294 Char.prototype.toString = function() {
|
rob@100
|
1295 return String.fromCharCode(this.code);
|
rob@100
|
1296 };
|
rob@100
|
1297
|
rob@100
|
1298 Char.prototype.valueOf = function() {
|
rob@100
|
1299 return this.code;
|
rob@100
|
1300 };
|
rob@100
|
1301
|
rob@100
|
1302 return Char;
|
rob@100
|
1303 }({}));
|
rob@100
|
1304
|
rob@100
|
1305 },{}],11:[function(require,module,exports){
|
rob@100
|
1306 /**
|
rob@100
|
1307 * A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only
|
rob@100
|
1308 * instead of accessing elements with a numeric index, a String is used. (If you are familiar with
|
rob@100
|
1309 * associative arrays from other languages, this is the same idea.)
|
rob@100
|
1310 *
|
rob@100
|
1311 * @param {int} initialCapacity defines the initial capacity of the map, it's 16 by default
|
rob@100
|
1312 * @param {float} loadFactor the load factor for the map, the default is 0.75
|
rob@100
|
1313 * @param {Map} m gives the new HashMap the same mappings as this Map
|
rob@100
|
1314 */
|
rob@100
|
1315 module.exports = function(options) {
|
rob@100
|
1316 var virtHashCode = options.virtHashCode,
|
rob@100
|
1317 virtEquals = options.virtEquals;
|
rob@100
|
1318
|
rob@100
|
1319 /**
|
rob@100
|
1320 * @member HashMap
|
rob@100
|
1321 * A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only
|
rob@100
|
1322 * instead of accessing elements with a numeric index, a String is used. (If you are familiar with
|
rob@100
|
1323 * associative arrays from other languages, this is the same idea.)
|
rob@100
|
1324 *
|
rob@100
|
1325 * @param {int} initialCapacity defines the initial capacity of the map, it's 16 by default
|
rob@100
|
1326 * @param {float} loadFactor the load factor for the map, the default is 0.75
|
rob@100
|
1327 * @param {Map} m gives the new HashMap the same mappings as this Map
|
rob@100
|
1328 */
|
rob@100
|
1329 function HashMap() {
|
rob@100
|
1330 if (arguments.length === 1 && arguments[0] instanceof HashMap) {
|
rob@100
|
1331 return arguments[0].clone();
|
rob@100
|
1332 }
|
rob@100
|
1333
|
rob@100
|
1334 var initialCapacity = arguments.length > 0 ? arguments[0] : 16;
|
rob@100
|
1335 var loadFactor = arguments.length > 1 ? arguments[1] : 0.75;
|
rob@100
|
1336 var buckets = [];
|
rob@100
|
1337 buckets.length = initialCapacity;
|
rob@100
|
1338 var count = 0;
|
rob@100
|
1339 var hashMap = this;
|
rob@100
|
1340
|
rob@100
|
1341 function getBucketIndex(key) {
|
rob@100
|
1342 var index = virtHashCode(key) % buckets.length;
|
rob@100
|
1343 return index < 0 ? buckets.length + index : index;
|
rob@100
|
1344 }
|
rob@100
|
1345 function ensureLoad() {
|
rob@100
|
1346 if (count <= loadFactor * buckets.length) {
|
rob@100
|
1347 return;
|
rob@100
|
1348 }
|
rob@100
|
1349 var allEntries = [];
|
rob@100
|
1350 for (var i = 0; i < buckets.length; ++i) {
|
rob@100
|
1351 if (buckets[i] !== undefined) {
|
rob@100
|
1352 allEntries = allEntries.concat(buckets[i]);
|
rob@100
|
1353 }
|
rob@100
|
1354 }
|
rob@100
|
1355 var newBucketsLength = buckets.length * 2;
|
rob@100
|
1356 buckets = [];
|
rob@100
|
1357 buckets.length = newBucketsLength;
|
rob@100
|
1358 for (var j = 0; j < allEntries.length; ++j) {
|
rob@100
|
1359 var index = getBucketIndex(allEntries[j].key);
|
rob@100
|
1360 var bucket = buckets[index];
|
rob@100
|
1361 if (bucket === undefined) {
|
rob@100
|
1362 buckets[index] = bucket = [];
|
rob@100
|
1363 }
|
rob@100
|
1364 bucket.push(allEntries[j]);
|
rob@100
|
1365 }
|
rob@100
|
1366 }
|
rob@100
|
1367
|
rob@100
|
1368 function Iterator(conversion, removeItem) {
|
rob@100
|
1369 var bucketIndex = 0;
|
rob@100
|
1370 var itemIndex = -1;
|
rob@100
|
1371 var endOfBuckets = false;
|
rob@100
|
1372 var currentItem;
|
rob@100
|
1373
|
rob@100
|
1374 function findNext() {
|
rob@100
|
1375 while (!endOfBuckets) {
|
rob@100
|
1376 ++itemIndex;
|
rob@100
|
1377 if (bucketIndex >= buckets.length) {
|
rob@100
|
1378 endOfBuckets = true;
|
rob@100
|
1379 } else if (buckets[bucketIndex] === undefined || itemIndex >= buckets[bucketIndex].length) {
|
rob@100
|
1380 itemIndex = -1;
|
rob@100
|
1381 ++bucketIndex;
|
rob@100
|
1382 } else {
|
rob@100
|
1383 return;
|
rob@100
|
1384 }
|
rob@100
|
1385 }
|
rob@100
|
1386 }
|
rob@100
|
1387
|
rob@100
|
1388 /*
|
rob@100
|
1389 * @member Iterator
|
rob@100
|
1390 * Checks if the Iterator has more items
|
rob@100
|
1391 */
|
rob@100
|
1392 this.hasNext = function() {
|
rob@100
|
1393 return !endOfBuckets;
|
rob@100
|
1394 };
|
rob@100
|
1395
|
rob@100
|
1396 /*
|
rob@100
|
1397 * @member Iterator
|
rob@100
|
1398 * Return the next Item
|
rob@100
|
1399 */
|
rob@100
|
1400 this.next = function() {
|
rob@100
|
1401 currentItem = conversion(buckets[bucketIndex][itemIndex]);
|
rob@100
|
1402 findNext();
|
rob@100
|
1403 return currentItem;
|
rob@100
|
1404 };
|
rob@100
|
1405
|
rob@100
|
1406 /*
|
rob@100
|
1407 * @member Iterator
|
rob@100
|
1408 * Remove the current item
|
rob@100
|
1409 */
|
rob@100
|
1410 this.remove = function() {
|
rob@100
|
1411 if (currentItem !== undefined) {
|
rob@100
|
1412 removeItem(currentItem);
|
rob@100
|
1413 --itemIndex;
|
rob@100
|
1414 findNext();
|
rob@100
|
1415 }
|
rob@100
|
1416 };
|
rob@100
|
1417
|
rob@100
|
1418 findNext();
|
rob@100
|
1419 }
|
rob@100
|
1420
|
rob@100
|
1421 function Set(conversion, isIn, removeItem) {
|
rob@100
|
1422 this.clear = function() {
|
rob@100
|
1423 hashMap.clear();
|
rob@100
|
1424 };
|
rob@100
|
1425
|
rob@100
|
1426 this.contains = function(o) {
|
rob@100
|
1427 return isIn(o);
|
rob@100
|
1428 };
|
rob@100
|
1429
|
rob@100
|
1430 this.containsAll = function(o) {
|
rob@100
|
1431 var it = o.iterator();
|
rob@100
|
1432 while (it.hasNext()) {
|
rob@100
|
1433 if (!this.contains(it.next())) {
|
rob@100
|
1434 return false;
|
rob@100
|
1435 }
|
rob@100
|
1436 }
|
rob@100
|
1437 return true;
|
rob@100
|
1438 };
|
rob@100
|
1439
|
rob@100
|
1440 this.isEmpty = function() {
|
rob@100
|
1441 return hashMap.isEmpty();
|
rob@100
|
1442 };
|
rob@100
|
1443
|
rob@100
|
1444 this.iterator = function() {
|
rob@100
|
1445 return new Iterator(conversion, removeItem);
|
rob@100
|
1446 };
|
rob@100
|
1447
|
rob@100
|
1448 this.remove = function(o) {
|
rob@100
|
1449 if (this.contains(o)) {
|
rob@100
|
1450 removeItem(o);
|
rob@100
|
1451 return true;
|
rob@100
|
1452 }
|
rob@100
|
1453 return false;
|
rob@100
|
1454 };
|
rob@100
|
1455
|
rob@100
|
1456 this.removeAll = function(c) {
|
rob@100
|
1457 var it = c.iterator();
|
rob@100
|
1458 var changed = false;
|
rob@100
|
1459 while (it.hasNext()) {
|
rob@100
|
1460 var item = it.next();
|
rob@100
|
1461 if (this.contains(item)) {
|
rob@100
|
1462 removeItem(item);
|
rob@100
|
1463 changed = true;
|
rob@100
|
1464 }
|
rob@100
|
1465 }
|
rob@100
|
1466 return true;
|
rob@100
|
1467 };
|
rob@100
|
1468
|
rob@100
|
1469 this.retainAll = function(c) {
|
rob@100
|
1470 var it = this.iterator();
|
rob@100
|
1471 var toRemove = [];
|
rob@100
|
1472 while (it.hasNext()) {
|
rob@100
|
1473 var entry = it.next();
|
rob@100
|
1474 if (!c.contains(entry)) {
|
rob@100
|
1475 toRemove.push(entry);
|
rob@100
|
1476 }
|
rob@100
|
1477 }
|
rob@100
|
1478 for (var i = 0; i < toRemove.length; ++i) {
|
rob@100
|
1479 removeItem(toRemove[i]);
|
rob@100
|
1480 }
|
rob@100
|
1481 return toRemove.length > 0;
|
rob@100
|
1482 };
|
rob@100
|
1483
|
rob@100
|
1484 this.size = function() {
|
rob@100
|
1485 return hashMap.size();
|
rob@100
|
1486 };
|
rob@100
|
1487
|
rob@100
|
1488 this.toArray = function() {
|
rob@100
|
1489 var result = [];
|
rob@100
|
1490 var it = this.iterator();
|
rob@100
|
1491 while (it.hasNext()) {
|
rob@100
|
1492 result.push(it.next());
|
rob@100
|
1493 }
|
rob@100
|
1494 return result;
|
rob@100
|
1495 };
|
rob@100
|
1496 }
|
rob@100
|
1497
|
rob@100
|
1498 function Entry(pair) {
|
rob@100
|
1499 this._isIn = function(map) {
|
rob@100
|
1500 return map === hashMap && (pair.removed === undefined);
|
rob@100
|
1501 };
|
rob@100
|
1502
|
rob@100
|
1503 this.equals = function(o) {
|
rob@100
|
1504 return virtEquals(pair.key, o.getKey());
|
rob@100
|
1505 };
|
rob@100
|
1506
|
rob@100
|
1507 this.getKey = function() {
|
rob@100
|
1508 return pair.key;
|
rob@100
|
1509 };
|
rob@100
|
1510
|
rob@100
|
1511 this.getValue = function() {
|
rob@100
|
1512 return pair.value;
|
rob@100
|
1513 };
|
rob@100
|
1514
|
rob@100
|
1515 this.hashCode = function(o) {
|
rob@100
|
1516 return virtHashCode(pair.key);
|
rob@100
|
1517 };
|
rob@100
|
1518
|
rob@100
|
1519 this.setValue = function(value) {
|
rob@100
|
1520 var old = pair.value;
|
rob@100
|
1521 pair.value = value;
|
rob@100
|
1522 return old;
|
rob@100
|
1523 };
|
rob@100
|
1524 }
|
rob@100
|
1525
|
rob@100
|
1526 this.clear = function() {
|
rob@100
|
1527 count = 0;
|
rob@100
|
1528 buckets = [];
|
rob@100
|
1529 buckets.length = initialCapacity;
|
rob@100
|
1530 };
|
rob@100
|
1531
|
rob@100
|
1532 this.clone = function() {
|
rob@100
|
1533 var map = new HashMap();
|
rob@100
|
1534 map.putAll(this);
|
rob@100
|
1535 return map;
|
rob@100
|
1536 };
|
rob@100
|
1537
|
rob@100
|
1538 this.containsKey = function(key) {
|
rob@100
|
1539 var index = getBucketIndex(key);
|
rob@100
|
1540 var bucket = buckets[index];
|
rob@100
|
1541 if (bucket === undefined) {
|
rob@100
|
1542 return false;
|
rob@100
|
1543 }
|
rob@100
|
1544 for (var i = 0; i < bucket.length; ++i) {
|
rob@100
|
1545 if (virtEquals(bucket[i].key, key)) {
|
rob@100
|
1546 return true;
|
rob@100
|
1547 }
|
rob@100
|
1548 }
|
rob@100
|
1549 return false;
|
rob@100
|
1550 };
|
rob@100
|
1551
|
rob@100
|
1552 this.containsValue = function(value) {
|
rob@100
|
1553 for (var i = 0; i < buckets.length; ++i) {
|
rob@100
|
1554 var bucket = buckets[i];
|
rob@100
|
1555 if (bucket === undefined) {
|
rob@100
|
1556 continue;
|
rob@100
|
1557 }
|
rob@100
|
1558 for (var j = 0; j < bucket.length; ++j) {
|
rob@100
|
1559 if (virtEquals(bucket[j].value, value)) {
|
rob@100
|
1560 return true;
|
rob@100
|
1561 }
|
rob@100
|
1562 }
|
rob@100
|
1563 }
|
rob@100
|
1564 return false;
|
rob@100
|
1565 };
|
rob@100
|
1566
|
rob@100
|
1567 this.entrySet = function() {
|
rob@100
|
1568 return new Set(
|
rob@100
|
1569
|
rob@100
|
1570 function(pair) {
|
rob@100
|
1571 return new Entry(pair);
|
rob@100
|
1572 },
|
rob@100
|
1573
|
rob@100
|
1574 function(pair) {
|
rob@100
|
1575 return (pair instanceof Entry) && pair._isIn(hashMap);
|
rob@100
|
1576 },
|
rob@100
|
1577
|
rob@100
|
1578 function(pair) {
|
rob@100
|
1579 return hashMap.remove(pair.getKey());
|
rob@100
|
1580 });
|
rob@100
|
1581 };
|
rob@100
|
1582
|
rob@100
|
1583 this.get = function(key) {
|
rob@100
|
1584 var index = getBucketIndex(key);
|
rob@100
|
1585 var bucket = buckets[index];
|
rob@100
|
1586 if (bucket === undefined) {
|
rob@100
|
1587 return null;
|
rob@100
|
1588 }
|
rob@100
|
1589 for (var i = 0; i < bucket.length; ++i) {
|
rob@100
|
1590 if (virtEquals(bucket[i].key, key)) {
|
rob@100
|
1591 return bucket[i].value;
|
rob@100
|
1592 }
|
rob@100
|
1593 }
|
rob@100
|
1594 return null;
|
rob@100
|
1595 };
|
rob@100
|
1596
|
rob@100
|
1597 this.isEmpty = function() {
|
rob@100
|
1598 return count === 0;
|
rob@100
|
1599 };
|
rob@100
|
1600
|
rob@100
|
1601 this.keySet = function() {
|
rob@100
|
1602 return new Set(
|
rob@100
|
1603 // get key from pair
|
rob@100
|
1604 function(pair) {
|
rob@100
|
1605 return pair.key;
|
rob@100
|
1606 },
|
rob@100
|
1607 // is-in test
|
rob@100
|
1608 function(key) {
|
rob@100
|
1609 return hashMap.containsKey(key);
|
rob@100
|
1610 },
|
rob@100
|
1611 // remove from hashmap by key
|
rob@100
|
1612 function(key) {
|
rob@100
|
1613 return hashMap.remove(key);
|
rob@100
|
1614 }
|
rob@100
|
1615 );
|
rob@100
|
1616 };
|
rob@100
|
1617
|
rob@100
|
1618 this.values = function() {
|
rob@100
|
1619 return new Set(
|
rob@100
|
1620 // get value from pair
|
rob@100
|
1621 function(pair) {
|
rob@100
|
1622 return pair.value;
|
rob@100
|
1623 },
|
rob@100
|
1624 // is-in test
|
rob@100
|
1625 function(value) {
|
rob@100
|
1626 return hashMap.containsValue(value);
|
rob@100
|
1627 },
|
rob@100
|
1628 // remove from hashmap by value
|
rob@100
|
1629 function(value) {
|
rob@100
|
1630 return hashMap.removeByValue(value);
|
rob@100
|
1631 }
|
rob@100
|
1632 );
|
rob@100
|
1633 };
|
rob@100
|
1634
|
rob@100
|
1635 this.put = function(key, value) {
|
rob@100
|
1636 var index = getBucketIndex(key);
|
rob@100
|
1637 var bucket = buckets[index];
|
rob@100
|
1638 if (bucket === undefined) {
|
rob@100
|
1639 ++count;
|
rob@100
|
1640 buckets[index] = [{
|
rob@100
|
1641 key: key,
|
rob@100
|
1642 value: value
|
rob@100
|
1643 }];
|
rob@100
|
1644 ensureLoad();
|
rob@100
|
1645 return null;
|
rob@100
|
1646 }
|
rob@100
|
1647 for (var i = 0; i < bucket.length; ++i) {
|
rob@100
|
1648 if (virtEquals(bucket[i].key, key)) {
|
rob@100
|
1649 var previous = bucket[i].value;
|
rob@100
|
1650 bucket[i].value = value;
|
rob@100
|
1651 return previous;
|
rob@100
|
1652 }
|
rob@100
|
1653 }
|
rob@100
|
1654 ++count;
|
rob@100
|
1655 bucket.push({
|
rob@100
|
1656 key: key,
|
rob@100
|
1657 value: value
|
rob@100
|
1658 });
|
rob@100
|
1659 ensureLoad();
|
rob@100
|
1660 return null;
|
rob@100
|
1661 };
|
rob@100
|
1662
|
rob@100
|
1663 this.putAll = function(m) {
|
rob@100
|
1664 var it = m.entrySet().iterator();
|
rob@100
|
1665 while (it.hasNext()) {
|
rob@100
|
1666 var entry = it.next();
|
rob@100
|
1667 this.put(entry.getKey(), entry.getValue());
|
rob@100
|
1668 }
|
rob@100
|
1669 };
|
rob@100
|
1670
|
rob@100
|
1671 this.remove = function(key) {
|
rob@100
|
1672 var index = getBucketIndex(key);
|
rob@100
|
1673 var bucket = buckets[index];
|
rob@100
|
1674 if (bucket === undefined) {
|
rob@100
|
1675 return null;
|
rob@100
|
1676 }
|
rob@100
|
1677 for (var i = 0; i < bucket.length; ++i) {
|
rob@100
|
1678 if (virtEquals(bucket[i].key, key)) {
|
rob@100
|
1679 --count;
|
rob@100
|
1680 var previous = bucket[i].value;
|
rob@100
|
1681 bucket[i].removed = true;
|
rob@100
|
1682 if (bucket.length > 1) {
|
rob@100
|
1683 bucket.splice(i, 1);
|
rob@100
|
1684 } else {
|
rob@100
|
1685 buckets[index] = undefined;
|
rob@100
|
1686 }
|
rob@100
|
1687 return previous;
|
rob@100
|
1688 }
|
rob@100
|
1689 }
|
rob@100
|
1690 return null;
|
rob@100
|
1691 };
|
rob@100
|
1692
|
rob@100
|
1693 this.removeByValue = function(value) {
|
rob@100
|
1694 var bucket, i, ilen, pair;
|
rob@100
|
1695 for (bucket in buckets) {
|
rob@100
|
1696 if (buckets.hasOwnProperty(bucket)) {
|
rob@100
|
1697 for (i = 0, ilen = buckets[bucket].length; i < ilen; i++) {
|
rob@100
|
1698 pair = buckets[bucket][i];
|
rob@100
|
1699 // removal on values is based on identity, not equality
|
rob@100
|
1700 if (pair.value === value) {
|
rob@100
|
1701 buckets[bucket].splice(i, 1);
|
rob@100
|
1702 return true;
|
rob@100
|
1703 }
|
rob@100
|
1704 }
|
rob@100
|
1705 }
|
rob@100
|
1706 }
|
rob@100
|
1707 return false;
|
rob@100
|
1708 };
|
rob@100
|
1709
|
rob@100
|
1710 this.size = function() {
|
rob@100
|
1711 return count;
|
rob@100
|
1712 };
|
rob@100
|
1713 }
|
rob@100
|
1714
|
rob@100
|
1715 return HashMap;
|
rob@100
|
1716 };
|
rob@100
|
1717
|
rob@100
|
1718 },{}],12:[function(require,module,exports){
|
rob@100
|
1719 // module export
|
rob@100
|
1720 module.exports = function(options,undef) {
|
rob@100
|
1721 var window = options.Browser.window,
|
rob@100
|
1722 document = options.Browser.document,
|
rob@100
|
1723 noop = options.noop;
|
rob@100
|
1724
|
rob@100
|
1725 /**
|
rob@100
|
1726 * [internal function] computeFontMetrics() calculates various metrics for text
|
rob@100
|
1727 * placement. Currently this function computes the ascent, descent and leading
|
rob@100
|
1728 * (from "lead", used for vertical space) values for the currently active font.
|
rob@100
|
1729 */
|
rob@100
|
1730 function computeFontMetrics(pfont) {
|
rob@100
|
1731 var emQuad = 250,
|
rob@100
|
1732 correctionFactor = pfont.size / emQuad,
|
rob@100
|
1733 canvas = document.createElement("canvas");
|
rob@100
|
1734 canvas.width = 2*emQuad;
|
rob@100
|
1735 canvas.height = 2*emQuad;
|
rob@100
|
1736 canvas.style.opacity = 0;
|
rob@100
|
1737 var cfmFont = pfont.getCSSDefinition(emQuad+"px", "normal"),
|
rob@100
|
1738 ctx = canvas.getContext("2d");
|
rob@100
|
1739 ctx.font = cfmFont;
|
rob@100
|
1740
|
rob@100
|
1741 // Size the canvas using a string with common max-ascent and max-descent letters.
|
rob@100
|
1742 // Changing the canvas dimensions resets the context, so we must reset the font.
|
rob@100
|
1743 var protrusions = "dbflkhyjqpg";
|
rob@100
|
1744 canvas.width = ctx.measureText(protrusions).width;
|
rob@100
|
1745 ctx.font = cfmFont;
|
rob@100
|
1746
|
rob@100
|
1747 // for text lead values, we meaure a multiline text container.
|
rob@100
|
1748 var leadDiv = document.createElement("div");
|
rob@100
|
1749 leadDiv.style.position = "absolute";
|
rob@100
|
1750 leadDiv.style.opacity = 0;
|
rob@100
|
1751 leadDiv.style.fontFamily = '"' + pfont.name + '"';
|
rob@100
|
1752 leadDiv.style.fontSize = emQuad + "px";
|
rob@100
|
1753 leadDiv.innerHTML = protrusions + "<br/>" + protrusions;
|
rob@100
|
1754 document.body.appendChild(leadDiv);
|
rob@100
|
1755
|
rob@100
|
1756 var w = canvas.width,
|
rob@100
|
1757 h = canvas.height,
|
rob@100
|
1758 baseline = h/2;
|
rob@100
|
1759
|
rob@100
|
1760 // Set all canvas pixeldata values to 255, with all the content
|
rob@100
|
1761 // data being 0. This lets us scan for data[i] != 255.
|
rob@100
|
1762 ctx.fillStyle = "white";
|
rob@100
|
1763 ctx.fillRect(0, 0, w, h);
|
rob@100
|
1764 ctx.fillStyle = "black";
|
rob@100
|
1765 ctx.fillText(protrusions, 0, baseline);
|
rob@100
|
1766 var pixelData = ctx.getImageData(0, 0, w, h).data;
|
rob@100
|
1767
|
rob@100
|
1768 // canvas pixel data is w*4 by h*4, because R, G, B and A are separate,
|
rob@100
|
1769 // consecutive values in the array, rather than stored as 32 bit ints.
|
rob@100
|
1770 var i = 0,
|
rob@100
|
1771 w4 = w * 4,
|
rob@100
|
1772 len = pixelData.length;
|
rob@100
|
1773
|
rob@100
|
1774 // Finding the ascent uses a normal, forward scanline
|
rob@100
|
1775 while (++i < len && pixelData[i] === 255) {
|
rob@100
|
1776 noop();
|
rob@100
|
1777 }
|
rob@100
|
1778 var ascent = Math.round(i / w4);
|
rob@100
|
1779
|
rob@100
|
1780 // Finding the descent uses a reverse scanline
|
rob@100
|
1781 i = len - 1;
|
rob@100
|
1782 while (--i > 0 && pixelData[i] === 255) {
|
rob@100
|
1783 noop();
|
rob@100
|
1784 }
|
rob@100
|
1785 var descent = Math.round(i / w4);
|
rob@100
|
1786
|
rob@100
|
1787 // set font metrics
|
rob@100
|
1788 pfont.ascent = correctionFactor * (baseline - ascent);
|
rob@100
|
1789 pfont.descent = correctionFactor * (descent - baseline);
|
rob@100
|
1790
|
rob@100
|
1791 // Then we try to get the real value from the browser
|
rob@100
|
1792 if (document.defaultView.getComputedStyle) {
|
rob@100
|
1793 var leadDivHeight = document.defaultView.getComputedStyle(leadDiv,null).getPropertyValue("height");
|
rob@100
|
1794 leadDivHeight = correctionFactor * leadDivHeight.replace("px","");
|
rob@100
|
1795 if (leadDivHeight >= pfont.size * 2) {
|
rob@100
|
1796 pfont.leading = Math.round(leadDivHeight/2);
|
rob@100
|
1797 }
|
rob@100
|
1798 }
|
rob@100
|
1799 document.body.removeChild(leadDiv);
|
rob@100
|
1800
|
rob@100
|
1801 // if we're caching, cache the context used for this pfont
|
rob@100
|
1802 if (pfont.caching) {
|
rob@100
|
1803 return ctx;
|
rob@100
|
1804 }
|
rob@100
|
1805 }
|
rob@100
|
1806
|
rob@100
|
1807 /**
|
rob@100
|
1808 * Constructor for a system or from-file (non-SVG) font.
|
rob@100
|
1809 */
|
rob@100
|
1810 function PFont(name, size) {
|
rob@100
|
1811 // according to the P5 API, new PFont() is legal (albeit completely useless)
|
rob@100
|
1812 if (name === undef) {
|
rob@100
|
1813 name = "";
|
rob@100
|
1814 }
|
rob@100
|
1815 this.name = name;
|
rob@100
|
1816 if (size === undef) {
|
rob@100
|
1817 size = 0;
|
rob@100
|
1818 }
|
rob@100
|
1819 this.size = size;
|
rob@100
|
1820 this.glyph = false;
|
rob@100
|
1821 this.ascent = 0;
|
rob@100
|
1822 this.descent = 0;
|
rob@100
|
1823 // For leading, the "safe" value uses the standard TEX ratio
|
rob@100
|
1824 this.leading = 1.2 * size;
|
rob@100
|
1825
|
rob@100
|
1826 // Note that an italic, bold font must used "... Bold Italic"
|
rob@100
|
1827 // in P5. "... Italic Bold" is treated as normal/normal.
|
rob@100
|
1828 var illegalIndicator = name.indexOf(" Italic Bold");
|
rob@100
|
1829 if (illegalIndicator !== -1) {
|
rob@100
|
1830 name = name.substring(0, illegalIndicator);
|
rob@100
|
1831 }
|
rob@100
|
1832
|
rob@100
|
1833 // determine font style
|
rob@100
|
1834 this.style = "normal";
|
rob@100
|
1835 var italicsIndicator = name.indexOf(" Italic");
|
rob@100
|
1836 if (italicsIndicator !== -1) {
|
rob@100
|
1837 name = name.substring(0, italicsIndicator);
|
rob@100
|
1838 this.style = "italic";
|
rob@100
|
1839 }
|
rob@100
|
1840
|
rob@100
|
1841 // determine font weight
|
rob@100
|
1842 this.weight = "normal";
|
rob@100
|
1843 var boldIndicator = name.indexOf(" Bold");
|
rob@100
|
1844 if (boldIndicator !== -1) {
|
rob@100
|
1845 name = name.substring(0, boldIndicator);
|
rob@100
|
1846 this.weight = "bold";
|
rob@100
|
1847 }
|
rob@100
|
1848
|
rob@100
|
1849 // determine font-family name
|
rob@100
|
1850 this.family = "sans-serif";
|
rob@100
|
1851 if (name !== undef) {
|
rob@100
|
1852 switch(name) {
|
rob@100
|
1853 case "sans-serif":
|
rob@100
|
1854 case "serif":
|
rob@100
|
1855 case "monospace":
|
rob@100
|
1856 case "fantasy":
|
rob@100
|
1857 case "cursive":
|
rob@100
|
1858 this.family = name;
|
rob@100
|
1859 break;
|
rob@100
|
1860 default:
|
rob@100
|
1861 this.family = '"' + name + '", sans-serif';
|
rob@100
|
1862 break;
|
rob@100
|
1863 }
|
rob@100
|
1864 }
|
rob@100
|
1865 // Calculate the ascent/descent/leading value based on
|
rob@100
|
1866 // how the browser renders this font.
|
rob@100
|
1867 this.context2d = computeFontMetrics(this);
|
rob@100
|
1868 this.css = this.getCSSDefinition();
|
rob@100
|
1869 if (this.context2d) {
|
rob@100
|
1870 this.context2d.font = this.css;
|
rob@100
|
1871 }
|
rob@100
|
1872 }
|
rob@100
|
1873
|
rob@100
|
1874 /**
|
rob@100
|
1875 * regulates whether or not we're caching the canvas
|
rob@100
|
1876 * 2d context for quick text width computation.
|
rob@100
|
1877 */
|
rob@100
|
1878 PFont.prototype.caching = true;
|
rob@100
|
1879
|
rob@100
|
1880 /**
|
rob@100
|
1881 * This function generates the CSS "font" string for this PFont
|
rob@100
|
1882 */
|
rob@100
|
1883 PFont.prototype.getCSSDefinition = function(fontSize, lineHeight) {
|
rob@100
|
1884 if(fontSize===undef) {
|
rob@100
|
1885 fontSize = this.size + "px";
|
rob@100
|
1886 }
|
rob@100
|
1887 if(lineHeight===undef) {
|
rob@100
|
1888 lineHeight = this.leading + "px";
|
rob@100
|
1889 }
|
rob@100
|
1890 // CSS "font" definition: font-style font-variant font-weight font-size/line-height font-family
|
rob@100
|
1891 var components = [this.style, "normal", this.weight, fontSize + "/" + lineHeight, this.family];
|
rob@100
|
1892 return components.join(" ");
|
rob@100
|
1893 };
|
rob@100
|
1894
|
rob@100
|
1895 /**
|
rob@100
|
1896 * Rely on the cached context2d measureText function.
|
rob@100
|
1897 */
|
rob@100
|
1898 PFont.prototype.measureTextWidth = function(string) {
|
rob@100
|
1899 return this.context2d.measureText(string).width;
|
rob@100
|
1900 };
|
rob@100
|
1901
|
rob@100
|
1902 /**
|
rob@100
|
1903 * FALLBACK FUNCTION -- replaces Pfont.prototype.measureTextWidth
|
rob@100
|
1904 * when the font cache becomes too large. This contructs a new
|
rob@100
|
1905 * canvas 2d context object for calling measureText on.
|
rob@100
|
1906 */
|
rob@100
|
1907 PFont.prototype.measureTextWidthFallback = function(string) {
|
rob@100
|
1908 var canvas = document.createElement("canvas"),
|
rob@100
|
1909 ctx = canvas.getContext("2d");
|
rob@100
|
1910 ctx.font = this.css;
|
rob@100
|
1911 return ctx.measureText(string).width;
|
rob@100
|
1912 };
|
rob@100
|
1913
|
rob@100
|
1914 /**
|
rob@100
|
1915 * Global "loaded fonts" list, internal to PFont
|
rob@100
|
1916 */
|
rob@100
|
1917 PFont.PFontCache = { length: 0 };
|
rob@100
|
1918
|
rob@100
|
1919 /**
|
rob@100
|
1920 * This function acts as single access point for getting and caching
|
rob@100
|
1921 * fonts across all sketches handled by an instance of Processing.js
|
rob@100
|
1922 */
|
rob@100
|
1923 PFont.get = function(fontName, fontSize) {
|
rob@100
|
1924 // round fontSize to one decimal point
|
rob@100
|
1925 fontSize = ((fontSize*10)+0.5|0)/10;
|
rob@100
|
1926 var cache = PFont.PFontCache,
|
rob@100
|
1927 idx = fontName+"/"+fontSize;
|
rob@100
|
1928 if (!cache[idx]) {
|
rob@100
|
1929 cache[idx] = new PFont(fontName, fontSize);
|
rob@100
|
1930 cache.length++;
|
rob@100
|
1931
|
rob@100
|
1932 // FALLBACK FUNCTIONALITY 1:
|
rob@100
|
1933 // If the cache has become large, switch over from full caching
|
rob@100
|
1934 // to caching only the static metrics for each new font request.
|
rob@100
|
1935 if (cache.length === 50) {
|
rob@100
|
1936 PFont.prototype.measureTextWidth = PFont.prototype.measureTextWidthFallback;
|
rob@100
|
1937 PFont.prototype.caching = false;
|
rob@100
|
1938 // clear contexts stored for each cached font
|
rob@100
|
1939 var entry;
|
rob@100
|
1940 for (entry in cache) {
|
rob@100
|
1941 if (entry !== "length") {
|
rob@100
|
1942 cache[entry].context2d = null;
|
rob@100
|
1943 }
|
rob@100
|
1944 }
|
rob@100
|
1945 return new PFont(fontName, fontSize);
|
rob@100
|
1946 }
|
rob@100
|
1947
|
rob@100
|
1948 // FALLBACK FUNCTIONALITY 2:
|
rob@100
|
1949 // If the cache has become too large, switch off font caching entirely.
|
rob@100
|
1950 if (cache.length === 400) {
|
rob@100
|
1951 PFont.PFontCache = {};
|
rob@100
|
1952 PFont.get = PFont.getFallback;
|
rob@100
|
1953 return new PFont(fontName, fontSize);
|
rob@100
|
1954 }
|
rob@100
|
1955 }
|
rob@100
|
1956 return cache[idx];
|
rob@100
|
1957 };
|
rob@100
|
1958
|
rob@100
|
1959 /**
|
rob@100
|
1960 * FALLBACK FUNCTION -- replaces PFont.get when the font cache
|
rob@100
|
1961 * becomes too large. This function bypasses font caching entirely.
|
rob@100
|
1962 */
|
rob@100
|
1963 PFont.getFallback = function(fontName, fontSize) {
|
rob@100
|
1964 return new PFont(fontName, fontSize);
|
rob@100
|
1965 };
|
rob@100
|
1966
|
rob@100
|
1967 /**
|
rob@100
|
1968 * Lists all standard fonts. Due to browser limitations, this list is
|
rob@100
|
1969 * not the system font list, like in P5, but the CSS "genre" list.
|
rob@100
|
1970 */
|
rob@100
|
1971 PFont.list = function() {
|
rob@100
|
1972 return ["sans-serif", "serif", "monospace", "fantasy", "cursive"];
|
rob@100
|
1973 };
|
rob@100
|
1974
|
rob@100
|
1975 /**
|
rob@100
|
1976 * Loading external fonts through @font-face rules is handled by PFont,
|
rob@100
|
1977 * to ensure fonts loaded in this way are globally available.
|
rob@100
|
1978 */
|
rob@100
|
1979 PFont.preloading = {
|
rob@100
|
1980 // template element used to compare font sizes
|
rob@100
|
1981 template: {},
|
rob@100
|
1982 // indicates whether or not the reference tiny font has been loaded
|
rob@100
|
1983 initialized: false,
|
rob@100
|
1984 // load the reference tiny font via a css @font-face rule
|
rob@100
|
1985 initialize: function() {
|
rob@100
|
1986 var generateTinyFont = function() {
|
rob@100
|
1987 var encoded = "#E3KAI2wAgT1MvMg7Eo3VmNtYX7ABi3CxnbHlm" +
|
rob@100
|
1988 "7Abw3kaGVhZ7ACs3OGhoZWE7A53CRobXR47AY3" +
|
rob@100
|
1989 "AGbG9jYQ7G03Bm1heH7ABC3CBuYW1l7Ae3AgcG" +
|
rob@100
|
1990 "9zd7AI3AE#B3AQ2kgTY18PPPUACwAg3ALSRoo3" +
|
rob@100
|
1991 "#yld0xg32QAB77#E777773B#E3C#I#Q77773E#" +
|
rob@100
|
1992 "Q7777777772CMAIw7AB77732B#M#Q3wAB#g3B#" +
|
rob@100
|
1993 "E#E2BB//82BB////w#B7#gAEg3E77x2B32B#E#" +
|
rob@100
|
1994 "Q#MTcBAQ32gAe#M#QQJ#E32M#QQJ#I#g32Q77#";
|
rob@100
|
1995 var expand = function(input) {
|
rob@100
|
1996 return "AAAAAAAA".substr(~~input ? 7-input : 6);
|
rob@100
|
1997 };
|
rob@100
|
1998 return encoded.replace(/[#237]/g, expand);
|
rob@100
|
1999 };
|
rob@100
|
2000 var fontface = document.createElement("style");
|
rob@100
|
2001 fontface.setAttribute("type","text/css");
|
rob@100
|
2002 fontface.innerHTML = "@font-face {\n" +
|
rob@100
|
2003 ' font-family: "PjsEmptyFont";' + "\n" +
|
rob@100
|
2004 " src: url('data:application/x-font-ttf;base64,"+generateTinyFont()+"')\n" +
|
rob@100
|
2005 " format('truetype');\n" +
|
rob@100
|
2006 "}";
|
rob@100
|
2007 document.head.appendChild(fontface);
|
rob@100
|
2008
|
rob@100
|
2009 // set up the template element
|
rob@100
|
2010 var element = document.createElement("span");
|
rob@100
|
2011 element.style.cssText = 'position: absolute; top: 0; left: 0; opacity: 0; font-family: "PjsEmptyFont", fantasy;';
|
rob@100
|
2012 element.innerHTML = "AAAAAAAA";
|
rob@100
|
2013 document.body.appendChild(element);
|
rob@100
|
2014 this.template = element;
|
rob@100
|
2015
|
rob@100
|
2016 this.initialized = true;
|
rob@100
|
2017 },
|
rob@100
|
2018 // Shorthand function to get the computed width for an element.
|
rob@100
|
2019 getElementWidth: function(element) {
|
rob@100
|
2020 return document.defaultView.getComputedStyle(element,"").getPropertyValue("width");
|
rob@100
|
2021 },
|
rob@100
|
2022 // time taken so far in attempting to load a font
|
rob@100
|
2023 timeAttempted: 0,
|
rob@100
|
2024 // returns false if no fonts are pending load, or true otherwise.
|
rob@100
|
2025 pending: function(intervallength) {
|
rob@100
|
2026 if (!this.initialized) {
|
rob@100
|
2027 this.initialize();
|
rob@100
|
2028 }
|
rob@100
|
2029 var element,
|
rob@100
|
2030 computedWidthFont,
|
rob@100
|
2031 computedWidthRef = this.getElementWidth(this.template);
|
rob@100
|
2032 for (var i = 0; i < this.fontList.length; i++) {
|
rob@100
|
2033 // compares size of text in pixels. if equal, custom font is not yet loaded
|
rob@100
|
2034 element = this.fontList[i];
|
rob@100
|
2035 computedWidthFont = this.getElementWidth(element);
|
rob@100
|
2036 if (this.timeAttempted < 4000 && computedWidthFont === computedWidthRef) {
|
rob@100
|
2037 this.timeAttempted += intervallength;
|
rob@100
|
2038 return true;
|
rob@100
|
2039 } else {
|
rob@100
|
2040 document.body.removeChild(element);
|
rob@100
|
2041 this.fontList.splice(i--, 1);
|
rob@100
|
2042 this.timeAttempted = 0;
|
rob@100
|
2043 }
|
rob@100
|
2044 }
|
rob@100
|
2045 // if there are no more fonts to load, pending is false
|
rob@100
|
2046 if (this.fontList.length === 0) {
|
rob@100
|
2047 return false;
|
rob@100
|
2048 }
|
rob@100
|
2049 // We should have already returned before getting here.
|
rob@100
|
2050 // But, if we do get here, length!=0 so fonts are pending.
|
rob@100
|
2051 return true;
|
rob@100
|
2052 },
|
rob@100
|
2053 // fontList contains elements to compare font sizes against a template
|
rob@100
|
2054 fontList: [],
|
rob@100
|
2055 // addedList contains the fontnames of all the fonts loaded via @font-face
|
rob@100
|
2056 addedList: {},
|
rob@100
|
2057 // adds a font to the font cache
|
rob@100
|
2058 // creates an element using the font, to start loading the font,
|
rob@100
|
2059 // and compare against a default font to see if the custom font is loaded
|
rob@100
|
2060 add: function(fontSrc) {
|
rob@100
|
2061 if (!this.initialized) {
|
rob@100
|
2062 this.initialize();
|
rob@100
|
2063 }
|
rob@100
|
2064 // fontSrc can be a string or a javascript object
|
rob@100
|
2065 // acceptable fonts are .ttf, .otf, and data uri
|
rob@100
|
2066 var fontName = (typeof fontSrc === 'object' ? fontSrc.fontFace : fontSrc),
|
rob@100
|
2067 fontUrl = (typeof fontSrc === 'object' ? fontSrc.url : fontSrc);
|
rob@100
|
2068
|
rob@100
|
2069 // check whether we already created the @font-face rule for this font
|
rob@100
|
2070 if (this.addedList[fontName]) {
|
rob@100
|
2071 return;
|
rob@100
|
2072 }
|
rob@100
|
2073
|
rob@100
|
2074 // if we didn't, create the @font-face rule
|
rob@100
|
2075 var style = document.createElement("style");
|
rob@100
|
2076 style.setAttribute("type","text/css");
|
rob@100
|
2077 style.innerHTML = "@font-face{\n font-family: '" + fontName + "';\n src: url('" + fontUrl + "');\n}\n";
|
rob@100
|
2078 document.head.appendChild(style);
|
rob@100
|
2079 this.addedList[fontName] = true;
|
rob@100
|
2080
|
rob@100
|
2081 // also create the element to load and compare the new font
|
rob@100
|
2082 var element = document.createElement("span");
|
rob@100
|
2083 element.style.cssText = "position: absolute; top: 0; left: 0; opacity: 0;";
|
rob@100
|
2084 element.style.fontFamily = '"' + fontName + '", "PjsEmptyFont", fantasy';
|
rob@100
|
2085 element.innerHTML = "AAAAAAAA";
|
rob@100
|
2086 document.body.appendChild(element);
|
rob@100
|
2087 this.fontList.push(element);
|
rob@100
|
2088 }
|
rob@100
|
2089 };
|
rob@100
|
2090
|
rob@100
|
2091 return PFont;
|
rob@100
|
2092 };
|
rob@100
|
2093 },{}],13:[function(require,module,exports){
|
rob@100
|
2094 module.exports = function(options, undef) {
|
rob@100
|
2095
|
rob@100
|
2096 // FIXME: hack
|
rob@100
|
2097 var p = options.p;
|
rob@100
|
2098
|
rob@100
|
2099 /**
|
rob@100
|
2100 * PMatrix2D is a 3x2 affine matrix implementation. The constructor accepts another PMatrix2D or a list of six float elements.
|
rob@100
|
2101 * If no parameters are provided the matrix is set to the identity matrix.
|
rob@100
|
2102 *
|
rob@100
|
2103 * @param {PMatrix2D} matrix the initial matrix to set to
|
rob@100
|
2104 * @param {float} m00 the first element of the matrix
|
rob@100
|
2105 * @param {float} m01 the second element of the matrix
|
rob@100
|
2106 * @param {float} m02 the third element of the matrix
|
rob@100
|
2107 * @param {float} m10 the fourth element of the matrix
|
rob@100
|
2108 * @param {float} m11 the fifth element of the matrix
|
rob@100
|
2109 * @param {float} m12 the sixth element of the matrix
|
rob@100
|
2110 */
|
rob@100
|
2111 var PMatrix2D = function() {
|
rob@100
|
2112 if (arguments.length === 0) {
|
rob@100
|
2113 this.reset();
|
rob@100
|
2114 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
|
rob@100
|
2115 this.set(arguments[0].array());
|
rob@100
|
2116 } else if (arguments.length === 6) {
|
rob@100
|
2117 this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
|
rob@100
|
2118 }
|
rob@100
|
2119 };
|
rob@100
|
2120
|
rob@100
|
2121 /**
|
rob@100
|
2122 * PMatrix2D methods
|
rob@100
|
2123 */
|
rob@100
|
2124 PMatrix2D.prototype = {
|
rob@100
|
2125 /**
|
rob@100
|
2126 * @member PMatrix2D
|
rob@100
|
2127 * The set() function sets the matrix elements. The function accepts either another PMatrix2D, an array of elements, or a list of six floats.
|
rob@100
|
2128 *
|
rob@100
|
2129 * @param {PMatrix2D} matrix the matrix to set this matrix to
|
rob@100
|
2130 * @param {float[]} elements an array of elements to set this matrix to
|
rob@100
|
2131 * @param {float} m00 the first element of the matrix
|
rob@100
|
2132 * @param {float} m01 the third element of the matrix
|
rob@100
|
2133 * @param {float} m10 the fourth element of the matrix
|
rob@100
|
2134 * @param {float} m11 the fith element of the matrix
|
rob@100
|
2135 * @param {float} m12 the sixth element of the matrix
|
rob@100
|
2136 */
|
rob@100
|
2137 set: function() {
|
rob@100
|
2138 if (arguments.length === 6) {
|
rob@100
|
2139 var a = arguments;
|
rob@100
|
2140 this.set([a[0], a[1], a[2],
|
rob@100
|
2141 a[3], a[4], a[5]]);
|
rob@100
|
2142 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
|
rob@100
|
2143 this.elements = arguments[0].array();
|
rob@100
|
2144 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
rob@100
|
2145 this.elements = arguments[0].slice();
|
rob@100
|
2146 }
|
rob@100
|
2147 },
|
rob@100
|
2148 /**
|
rob@100
|
2149 * @member PMatrix2D
|
rob@100
|
2150 * The get() function returns a copy of this PMatrix2D.
|
rob@100
|
2151 *
|
rob@100
|
2152 * @return {PMatrix2D} a copy of this PMatrix2D
|
rob@100
|
2153 */
|
rob@100
|
2154 get: function() {
|
rob@100
|
2155 var outgoing = new PMatrix2D();
|
rob@100
|
2156 outgoing.set(this.elements);
|
rob@100
|
2157 return outgoing;
|
rob@100
|
2158 },
|
rob@100
|
2159 /**
|
rob@100
|
2160 * @member PMatrix2D
|
rob@100
|
2161 * The reset() function sets this PMatrix2D to the identity matrix.
|
rob@100
|
2162 */
|
rob@100
|
2163 reset: function() {
|
rob@100
|
2164 this.set([1, 0, 0, 0, 1, 0]);
|
rob@100
|
2165 },
|
rob@100
|
2166 /**
|
rob@100
|
2167 * @member PMatrix2D
|
rob@100
|
2168 * The array() function returns a copy of the element values.
|
rob@100
|
2169 * @addon
|
rob@100
|
2170 *
|
rob@100
|
2171 * @return {float[]} returns a copy of the element values
|
rob@100
|
2172 */
|
rob@100
|
2173 array: function array() {
|
rob@100
|
2174 return this.elements.slice();
|
rob@100
|
2175 },
|
rob@100
|
2176 /**
|
rob@100
|
2177 * @member PMatrix2D
|
rob@100
|
2178 * The translate() function translates this matrix by moving the current coordinates to the location specified by tx and ty.
|
rob@100
|
2179 *
|
rob@100
|
2180 * @param {float} tx the x-axis coordinate to move to
|
rob@100
|
2181 * @param {float} ty the y-axis coordinate to move to
|
rob@100
|
2182 */
|
rob@100
|
2183 translate: function(tx, ty) {
|
rob@100
|
2184 this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2];
|
rob@100
|
2185 this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5];
|
rob@100
|
2186 },
|
rob@100
|
2187 /**
|
rob@100
|
2188 * @member PMatrix2D
|
rob@100
|
2189 * The invTranslate() function translates this matrix by moving the current coordinates to the negative location specified by tx and ty.
|
rob@100
|
2190 *
|
rob@100
|
2191 * @param {float} tx the x-axis coordinate to move to
|
rob@100
|
2192 * @param {float} ty the y-axis coordinate to move to
|
rob@100
|
2193 */
|
rob@100
|
2194 invTranslate: function(tx, ty) {
|
rob@100
|
2195 this.translate(-tx, -ty);
|
rob@100
|
2196 },
|
rob@100
|
2197 /**
|
rob@100
|
2198 * @member PMatrix2D
|
rob@100
|
2199 * The transpose() function is not used in processingjs.
|
rob@100
|
2200 */
|
rob@100
|
2201 transpose: function() {
|
rob@100
|
2202 // Does nothing in Processing.
|
rob@100
|
2203 },
|
rob@100
|
2204 /**
|
rob@100
|
2205 * @member PMatrix2D
|
rob@100
|
2206 * The mult() function multiplied this matrix.
|
rob@100
|
2207 * If two array elements are passed in the function will multiply a two element vector against this matrix.
|
rob@100
|
2208 * If target is null or not length four, a new float array will be returned.
|
rob@100
|
2209 * The values for vec and target can be the same (though that's less efficient).
|
rob@100
|
2210 * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
|
rob@100
|
2211 *
|
rob@100
|
2212 * @param {PVector} source, target the PVectors used to multiply this matrix
|
rob@100
|
2213 * @param {float[]} source, target the arrays used to multiply this matrix
|
rob@100
|
2214 *
|
rob@100
|
2215 * @return {PVector|float[]} returns a PVector or an array representing the new matrix
|
rob@100
|
2216 */
|
rob@100
|
2217 mult: function(source, target) {
|
rob@100
|
2218 var x, y;
|
rob@100
|
2219 if (source instanceof PVector) {
|
rob@100
|
2220 x = source.x;
|
rob@100
|
2221 y = source.y;
|
rob@100
|
2222 if (!target) {
|
rob@100
|
2223 target = new PVector();
|
rob@100
|
2224 }
|
rob@100
|
2225 } else if (source instanceof Array) {
|
rob@100
|
2226 x = source[0];
|
rob@100
|
2227 y = source[1];
|
rob@100
|
2228 if (!target) {
|
rob@100
|
2229 target = [];
|
rob@100
|
2230 }
|
rob@100
|
2231 }
|
rob@100
|
2232 if (target instanceof Array) {
|
rob@100
|
2233 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2];
|
rob@100
|
2234 target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5];
|
rob@100
|
2235 } else if (target instanceof PVector) {
|
rob@100
|
2236 target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2];
|
rob@100
|
2237 target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5];
|
rob@100
|
2238 target.z = 0;
|
rob@100
|
2239 }
|
rob@100
|
2240 return target;
|
rob@100
|
2241 },
|
rob@100
|
2242 /**
|
rob@100
|
2243 * @member PMatrix2D
|
rob@100
|
2244 * The multX() function calculates the x component of a vector from a transformation.
|
rob@100
|
2245 *
|
rob@100
|
2246 * @param {float} x the x component of the vector being transformed
|
rob@100
|
2247 * @param {float} y the y component of the vector being transformed
|
rob@100
|
2248 *
|
rob@100
|
2249 * @return {float} returnes the result of the calculation
|
rob@100
|
2250 */
|
rob@100
|
2251 multX: function(x, y) {
|
rob@100
|
2252 return (x * this.elements[0] + y * this.elements[1] + this.elements[2]);
|
rob@100
|
2253 },
|
rob@100
|
2254 /**
|
rob@100
|
2255 * @member PMatrix2D
|
rob@100
|
2256 * The multY() function calculates the y component of a vector from a transformation.
|
rob@100
|
2257 *
|
rob@100
|
2258 * @param {float} x the x component of the vector being transformed
|
rob@100
|
2259 * @param {float} y the y component of the vector being transformed
|
rob@100
|
2260 *
|
rob@100
|
2261 * @return {float} returnes the result of the calculation
|
rob@100
|
2262 */
|
rob@100
|
2263 multY: function(x, y) {
|
rob@100
|
2264 return (x * this.elements[3] + y * this.elements[4] + this.elements[5]);
|
rob@100
|
2265 },
|
rob@100
|
2266 /**
|
rob@100
|
2267 * @member PMatrix2D
|
rob@100
|
2268 * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
|
rob@100
|
2269 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
2270 *
|
rob@100
|
2271 * @param {float} angle angle of skew specified in radians
|
rob@100
|
2272 */
|
rob@100
|
2273 skewX: function(angle) {
|
rob@100
|
2274 this.apply(1, 0, 1, angle, 0, 0);
|
rob@100
|
2275 },
|
rob@100
|
2276 /**
|
rob@100
|
2277 * @member PMatrix2D
|
rob@100
|
2278 * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
|
rob@100
|
2279 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
2280 *
|
rob@100
|
2281 * @param {float} angle angle of skew specified in radians
|
rob@100
|
2282 */
|
rob@100
|
2283 skewY: function(angle) {
|
rob@100
|
2284 this.apply(1, 0, 1, 0, angle, 0);
|
rob@100
|
2285 },
|
rob@100
|
2286 /**
|
rob@100
|
2287 * @member PMatrix2D
|
rob@100
|
2288 * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter.
|
rob@100
|
2289 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
2290 *
|
rob@100
|
2291 * @param {float} angle angle of skew specified in radians
|
rob@100
|
2292 */
|
rob@100
|
2293 shearX: function(angle) {
|
rob@100
|
2294 this.apply(1, 0, 1, Math.tan(angle) , 0, 0);
|
rob@100
|
2295 },
|
rob@100
|
2296 /**
|
rob@100
|
2297 * @member PMatrix2D
|
rob@100
|
2298 * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter.
|
rob@100
|
2299 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
2300 *
|
rob@100
|
2301 * @param {float} angle angle of skew specified in radians
|
rob@100
|
2302 */
|
rob@100
|
2303 shearY: function(angle) {
|
rob@100
|
2304 this.apply(1, 0, 1, 0, Math.tan(angle), 0);
|
rob@100
|
2305 },
|
rob@100
|
2306 /**
|
rob@100
|
2307 * @member PMatrix2D
|
rob@100
|
2308 * The determinant() function calvculates the determinant of this matrix.
|
rob@100
|
2309 *
|
rob@100
|
2310 * @return {float} the determinant of the matrix
|
rob@100
|
2311 */
|
rob@100
|
2312 determinant: function() {
|
rob@100
|
2313 return (this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3]);
|
rob@100
|
2314 },
|
rob@100
|
2315 /**
|
rob@100
|
2316 * @member PMatrix2D
|
rob@100
|
2317 * The invert() function inverts this matrix
|
rob@100
|
2318 *
|
rob@100
|
2319 * @return {boolean} true if successful
|
rob@100
|
2320 */
|
rob@100
|
2321 invert: function() {
|
rob@100
|
2322 var d = this.determinant();
|
rob@100
|
2323 if (Math.abs( d ) > PConstants.MIN_INT) {
|
rob@100
|
2324 var old00 = this.elements[0];
|
rob@100
|
2325 var old01 = this.elements[1];
|
rob@100
|
2326 var old02 = this.elements[2];
|
rob@100
|
2327 var old10 = this.elements[3];
|
rob@100
|
2328 var old11 = this.elements[4];
|
rob@100
|
2329 var old12 = this.elements[5];
|
rob@100
|
2330 this.elements[0] = old11 / d;
|
rob@100
|
2331 this.elements[3] = -old10 / d;
|
rob@100
|
2332 this.elements[1] = -old01 / d;
|
rob@100
|
2333 this.elements[4] = old00 / d;
|
rob@100
|
2334 this.elements[2] = (old01 * old12 - old11 * old02) / d;
|
rob@100
|
2335 this.elements[5] = (old10 * old02 - old00 * old12) / d;
|
rob@100
|
2336 return true;
|
rob@100
|
2337 }
|
rob@100
|
2338 return false;
|
rob@100
|
2339 },
|
rob@100
|
2340 /**
|
rob@100
|
2341 * @member PMatrix2D
|
rob@100
|
2342 * The scale() function increases or decreases the size of a shape by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
|
rob@100
|
2343 * This is equivalent to a two parameter call.
|
rob@100
|
2344 *
|
rob@100
|
2345 * @param {float} sx the amount to scale on the x-axis
|
rob@100
|
2346 * @param {float} sy the amount to scale on the y-axis
|
rob@100
|
2347 */
|
rob@100
|
2348 scale: function(sx, sy) {
|
rob@100
|
2349 if (sx && !sy) {
|
rob@100
|
2350 sy = sx;
|
rob@100
|
2351 }
|
rob@100
|
2352 if (sx && sy) {
|
rob@100
|
2353 this.elements[0] *= sx;
|
rob@100
|
2354 this.elements[1] *= sy;
|
rob@100
|
2355 this.elements[3] *= sx;
|
rob@100
|
2356 this.elements[4] *= sy;
|
rob@100
|
2357 }
|
rob@100
|
2358 },
|
rob@100
|
2359 /**
|
rob@100
|
2360 * @member PMatrix2D
|
rob@100
|
2361 * The invScale() function decreases or increases the size of a shape by contracting and expanding vertices. When only one parameter is specified scale will occur in all dimensions.
|
rob@100
|
2362 * This is equivalent to a two parameter call.
|
rob@100
|
2363 *
|
rob@100
|
2364 * @param {float} sx the amount to scale on the x-axis
|
rob@100
|
2365 * @param {float} sy the amount to scale on the y-axis
|
rob@100
|
2366 */
|
rob@100
|
2367 invScale: function(sx, sy) {
|
rob@100
|
2368 if (sx && !sy) {
|
rob@100
|
2369 sy = sx;
|
rob@100
|
2370 }
|
rob@100
|
2371 this.scale(1 / sx, 1 / sy);
|
rob@100
|
2372 },
|
rob@100
|
2373 /**
|
rob@100
|
2374 * @member PMatrix2D
|
rob@100
|
2375 * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix2D or a list of floats can be passed in.
|
rob@100
|
2376 *
|
rob@100
|
2377 * @param {PMatrix2D} matrix the matrix to apply this matrix to
|
rob@100
|
2378 * @param {float} m00 the first element of the matrix
|
rob@100
|
2379 * @param {float} m01 the third element of the matrix
|
rob@100
|
2380 * @param {float} m10 the fourth element of the matrix
|
rob@100
|
2381 * @param {float} m11 the fith element of the matrix
|
rob@100
|
2382 * @param {float} m12 the sixth element of the matrix
|
rob@100
|
2383 */
|
rob@100
|
2384 apply: function() {
|
rob@100
|
2385 var source;
|
rob@100
|
2386 if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
|
rob@100
|
2387 source = arguments[0].array();
|
rob@100
|
2388 } else if (arguments.length === 6) {
|
rob@100
|
2389 source = Array.prototype.slice.call(arguments);
|
rob@100
|
2390 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
rob@100
|
2391 source = arguments[0];
|
rob@100
|
2392 }
|
rob@100
|
2393
|
rob@100
|
2394 var result = [0, 0, this.elements[2],
|
rob@100
|
2395 0, 0, this.elements[5]];
|
rob@100
|
2396 var e = 0;
|
rob@100
|
2397 for (var row = 0; row < 2; row++) {
|
rob@100
|
2398 for (var col = 0; col < 3; col++, e++) {
|
rob@100
|
2399 result[e] += this.elements[row * 3 + 0] * source[col + 0] +
|
rob@100
|
2400 this.elements[row * 3 + 1] * source[col + 3];
|
rob@100
|
2401 }
|
rob@100
|
2402 }
|
rob@100
|
2403 this.elements = result.slice();
|
rob@100
|
2404 },
|
rob@100
|
2405 /**
|
rob@100
|
2406 * @member PMatrix2D
|
rob@100
|
2407 * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix2D or elements of a matrix can be passed in.
|
rob@100
|
2408 *
|
rob@100
|
2409 * @param {PMatrix2D} matrix the matrix to apply this matrix to
|
rob@100
|
2410 * @param {float} m00 the first element of the matrix
|
rob@100
|
2411 * @param {float} m01 the third element of the matrix
|
rob@100
|
2412 * @param {float} m10 the fourth element of the matrix
|
rob@100
|
2413 * @param {float} m11 the fith element of the matrix
|
rob@100
|
2414 * @param {float} m12 the sixth element of the matrix
|
rob@100
|
2415 */
|
rob@100
|
2416 preApply: function() {
|
rob@100
|
2417 var source;
|
rob@100
|
2418 if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
|
rob@100
|
2419 source = arguments[0].array();
|
rob@100
|
2420 } else if (arguments.length === 6) {
|
rob@100
|
2421 source = Array.prototype.slice.call(arguments);
|
rob@100
|
2422 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
rob@100
|
2423 source = arguments[0];
|
rob@100
|
2424 }
|
rob@100
|
2425 var result = [0, 0, source[2],
|
rob@100
|
2426 0, 0, source[5]];
|
rob@100
|
2427 result[2] = source[2] + this.elements[2] * source[0] + this.elements[5] * source[1];
|
rob@100
|
2428 result[5] = source[5] + this.elements[2] * source[3] + this.elements[5] * source[4];
|
rob@100
|
2429 result[0] = this.elements[0] * source[0] + this.elements[3] * source[1];
|
rob@100
|
2430 result[3] = this.elements[0] * source[3] + this.elements[3] * source[4];
|
rob@100
|
2431 result[1] = this.elements[1] * source[0] + this.elements[4] * source[1];
|
rob@100
|
2432 result[4] = this.elements[1] * source[3] + this.elements[4] * source[4];
|
rob@100
|
2433 this.elements = result.slice();
|
rob@100
|
2434 },
|
rob@100
|
2435 /**
|
rob@100
|
2436 * @member PMatrix2D
|
rob@100
|
2437 * The rotate() function rotates the matrix.
|
rob@100
|
2438 *
|
rob@100
|
2439 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
2440 */
|
rob@100
|
2441 rotate: function(angle) {
|
rob@100
|
2442 var c = Math.cos(angle);
|
rob@100
|
2443 var s = Math.sin(angle);
|
rob@100
|
2444 var temp1 = this.elements[0];
|
rob@100
|
2445 var temp2 = this.elements[1];
|
rob@100
|
2446 this.elements[0] = c * temp1 + s * temp2;
|
rob@100
|
2447 this.elements[1] = -s * temp1 + c * temp2;
|
rob@100
|
2448 temp1 = this.elements[3];
|
rob@100
|
2449 temp2 = this.elements[4];
|
rob@100
|
2450 this.elements[3] = c * temp1 + s * temp2;
|
rob@100
|
2451 this.elements[4] = -s * temp1 + c * temp2;
|
rob@100
|
2452 },
|
rob@100
|
2453 /**
|
rob@100
|
2454 * @member PMatrix2D
|
rob@100
|
2455 * The rotateZ() function rotates the matrix.
|
rob@100
|
2456 *
|
rob@100
|
2457 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
2458 */
|
rob@100
|
2459 rotateZ: function(angle) {
|
rob@100
|
2460 this.rotate(angle);
|
rob@100
|
2461 },
|
rob@100
|
2462 /**
|
rob@100
|
2463 * @member PMatrix2D
|
rob@100
|
2464 * The invRotateZ() function rotates the matrix in opposite direction.
|
rob@100
|
2465 *
|
rob@100
|
2466 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
2467 */
|
rob@100
|
2468 invRotateZ: function(angle) {
|
rob@100
|
2469 this.rotateZ(angle - Math.PI);
|
rob@100
|
2470 },
|
rob@100
|
2471 /**
|
rob@100
|
2472 * @member PMatrix2D
|
rob@100
|
2473 * The print() function prints out the elements of this matrix
|
rob@100
|
2474 */
|
rob@100
|
2475 print: function() {
|
rob@100
|
2476 var digits = printMatrixHelper(this.elements);
|
rob@100
|
2477 var output = "" + p.nfs(this.elements[0], digits, 4) + " " +
|
rob@100
|
2478 p.nfs(this.elements[1], digits, 4) + " " +
|
rob@100
|
2479 p.nfs(this.elements[2], digits, 4) + "\n" +
|
rob@100
|
2480 p.nfs(this.elements[3], digits, 4) + " " +
|
rob@100
|
2481 p.nfs(this.elements[4], digits, 4) + " " +
|
rob@100
|
2482 p.nfs(this.elements[5], digits, 4) + "\n\n";
|
rob@100
|
2483 p.println(output);
|
rob@100
|
2484 }
|
rob@100
|
2485 };
|
rob@100
|
2486
|
rob@100
|
2487 return PMatrix2D;
|
rob@100
|
2488 };
|
rob@100
|
2489
|
rob@100
|
2490 },{}],14:[function(require,module,exports){
|
rob@100
|
2491 module.exports = function(options, undef) {
|
rob@100
|
2492
|
rob@100
|
2493 // FIXME: hack
|
rob@100
|
2494 var p = options.p;
|
rob@100
|
2495
|
rob@100
|
2496 /**
|
rob@100
|
2497 * PMatrix3D is a 4x4 matrix implementation. The constructor accepts another PMatrix3D or a list of six or sixteen float elements.
|
rob@100
|
2498 * If no parameters are provided the matrix is set to the identity matrix.
|
rob@100
|
2499 */
|
rob@100
|
2500 var PMatrix3D = function() {
|
rob@100
|
2501 // When a matrix is created, it is set to an identity matrix
|
rob@100
|
2502 this.reset();
|
rob@100
|
2503 };
|
rob@100
|
2504
|
rob@100
|
2505 /**
|
rob@100
|
2506 * PMatrix3D methods
|
rob@100
|
2507 */
|
rob@100
|
2508 PMatrix3D.prototype = {
|
rob@100
|
2509 /**
|
rob@100
|
2510 * @member PMatrix2D
|
rob@100
|
2511 * The set() function sets the matrix elements. The function accepts either another PMatrix3D, an array of elements, or a list of six or sixteen floats.
|
rob@100
|
2512 *
|
rob@100
|
2513 * @param {PMatrix3D} matrix the initial matrix to set to
|
rob@100
|
2514 * @param {float[]} elements an array of elements to set this matrix to
|
rob@100
|
2515 * @param {float} m00 the first element of the matrix
|
rob@100
|
2516 * @param {float} m01 the second element of the matrix
|
rob@100
|
2517 * @param {float} m02 the third element of the matrix
|
rob@100
|
2518 * @param {float} m03 the fourth element of the matrix
|
rob@100
|
2519 * @param {float} m10 the fifth element of the matrix
|
rob@100
|
2520 * @param {float} m11 the sixth element of the matrix
|
rob@100
|
2521 * @param {float} m12 the seventh element of the matrix
|
rob@100
|
2522 * @param {float} m13 the eight element of the matrix
|
rob@100
|
2523 * @param {float} m20 the nineth element of the matrix
|
rob@100
|
2524 * @param {float} m21 the tenth element of the matrix
|
rob@100
|
2525 * @param {float} m22 the eleventh element of the matrix
|
rob@100
|
2526 * @param {float} m23 the twelveth element of the matrix
|
rob@100
|
2527 * @param {float} m30 the thirteenth element of the matrix
|
rob@100
|
2528 * @param {float} m31 the fourtheenth element of the matrix
|
rob@100
|
2529 * @param {float} m32 the fivetheenth element of the matrix
|
rob@100
|
2530 * @param {float} m33 the sixteenth element of the matrix
|
rob@100
|
2531 */
|
rob@100
|
2532 set: function() {
|
rob@100
|
2533 if (arguments.length === 16) {
|
rob@100
|
2534 this.elements = Array.prototype.slice.call(arguments);
|
rob@100
|
2535 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
|
rob@100
|
2536 this.elements = arguments[0].array();
|
rob@100
|
2537 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
rob@100
|
2538 this.elements = arguments[0].slice();
|
rob@100
|
2539 }
|
rob@100
|
2540 },
|
rob@100
|
2541 /**
|
rob@100
|
2542 * @member PMatrix3D
|
rob@100
|
2543 * The get() function returns a copy of this PMatrix3D.
|
rob@100
|
2544 *
|
rob@100
|
2545 * @return {PMatrix3D} a copy of this PMatrix3D
|
rob@100
|
2546 */
|
rob@100
|
2547 get: function() {
|
rob@100
|
2548 var outgoing = new PMatrix3D();
|
rob@100
|
2549 outgoing.set(this.elements);
|
rob@100
|
2550 return outgoing;
|
rob@100
|
2551 },
|
rob@100
|
2552 /**
|
rob@100
|
2553 * @member PMatrix3D
|
rob@100
|
2554 * The reset() function sets this PMatrix3D to the identity matrix.
|
rob@100
|
2555 */
|
rob@100
|
2556 reset: function() {
|
rob@100
|
2557 this.elements = [1,0,0,0,
|
rob@100
|
2558 0,1,0,0,
|
rob@100
|
2559 0,0,1,0,
|
rob@100
|
2560 0,0,0,1];
|
rob@100
|
2561 },
|
rob@100
|
2562 /**
|
rob@100
|
2563 * @member PMatrix3D
|
rob@100
|
2564 * The array() function returns a copy of the element values.
|
rob@100
|
2565 * @addon
|
rob@100
|
2566 *
|
rob@100
|
2567 * @return {float[]} returns a copy of the element values
|
rob@100
|
2568 */
|
rob@100
|
2569 array: function array() {
|
rob@100
|
2570 return this.elements.slice();
|
rob@100
|
2571 },
|
rob@100
|
2572 /**
|
rob@100
|
2573 * @member PMatrix3D
|
rob@100
|
2574 * The translate() function translates this matrix by moving the current coordinates to the location specified by tx, ty, and tz.
|
rob@100
|
2575 *
|
rob@100
|
2576 * @param {float} tx the x-axis coordinate to move to
|
rob@100
|
2577 * @param {float} ty the y-axis coordinate to move to
|
rob@100
|
2578 * @param {float} tz the z-axis coordinate to move to
|
rob@100
|
2579 */
|
rob@100
|
2580 translate: function(tx, ty, tz) {
|
rob@100
|
2581 if (tz === undef) {
|
rob@100
|
2582 tz = 0;
|
rob@100
|
2583 }
|
rob@100
|
2584
|
rob@100
|
2585 this.elements[3] += tx * this.elements[0] + ty * this.elements[1] + tz * this.elements[2];
|
rob@100
|
2586 this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6];
|
rob@100
|
2587 this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10];
|
rob@100
|
2588 this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14];
|
rob@100
|
2589 },
|
rob@100
|
2590 /**
|
rob@100
|
2591 * @member PMatrix3D
|
rob@100
|
2592 * The transpose() function transpose this matrix.
|
rob@100
|
2593 */
|
rob@100
|
2594 transpose: function() {
|
rob@100
|
2595 var temp = this.elements[4];
|
rob@100
|
2596 this.elements[4] = this.elements[1];
|
rob@100
|
2597 this.elements[1] = temp;
|
rob@100
|
2598
|
rob@100
|
2599 temp = this.elements[8];
|
rob@100
|
2600 this.elements[8] = this.elements[2];
|
rob@100
|
2601 this.elements[2] = temp;
|
rob@100
|
2602
|
rob@100
|
2603 temp = this.elements[6];
|
rob@100
|
2604 this.elements[6] = this.elements[9];
|
rob@100
|
2605 this.elements[9] = temp;
|
rob@100
|
2606
|
rob@100
|
2607 temp = this.elements[3];
|
rob@100
|
2608 this.elements[3] = this.elements[12];
|
rob@100
|
2609 this.elements[12] = temp;
|
rob@100
|
2610
|
rob@100
|
2611 temp = this.elements[7];
|
rob@100
|
2612 this.elements[7] = this.elements[13];
|
rob@100
|
2613 this.elements[13] = temp;
|
rob@100
|
2614
|
rob@100
|
2615 temp = this.elements[11];
|
rob@100
|
2616 this.elements[11] = this.elements[14];
|
rob@100
|
2617 this.elements[14] = temp;
|
rob@100
|
2618 },
|
rob@100
|
2619 /**
|
rob@100
|
2620 * @member PMatrix3D
|
rob@100
|
2621 * The mult() function multiplied this matrix.
|
rob@100
|
2622 * If two array elements are passed in the function will multiply a two element vector against this matrix.
|
rob@100
|
2623 * If target is null or not length four, a new float array will be returned.
|
rob@100
|
2624 * The values for vec and target can be the same (though that's less efficient).
|
rob@100
|
2625 * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
|
rob@100
|
2626 *
|
rob@100
|
2627 * @param {PVector} source, target the PVectors used to multiply this matrix
|
rob@100
|
2628 * @param {float[]} source, target the arrays used to multiply this matrix
|
rob@100
|
2629 *
|
rob@100
|
2630 * @return {PVector|float[]} returns a PVector or an array representing the new matrix
|
rob@100
|
2631 */
|
rob@100
|
2632 mult: function(source, target) {
|
rob@100
|
2633 var x, y, z, w;
|
rob@100
|
2634 if (source instanceof PVector) {
|
rob@100
|
2635 x = source.x;
|
rob@100
|
2636 y = source.y;
|
rob@100
|
2637 z = source.z;
|
rob@100
|
2638 w = 1;
|
rob@100
|
2639 if (!target) {
|
rob@100
|
2640 target = new PVector();
|
rob@100
|
2641 }
|
rob@100
|
2642 } else if (source instanceof Array) {
|
rob@100
|
2643 x = source[0];
|
rob@100
|
2644 y = source[1];
|
rob@100
|
2645 z = source[2];
|
rob@100
|
2646 w = source[3] || 1;
|
rob@100
|
2647
|
rob@100
|
2648 if ( !target || (target.length !== 3 && target.length !== 4) ) {
|
rob@100
|
2649 target = [0, 0, 0];
|
rob@100
|
2650 }
|
rob@100
|
2651 }
|
rob@100
|
2652
|
rob@100
|
2653 if (target instanceof Array) {
|
rob@100
|
2654 if (target.length === 3) {
|
rob@100
|
2655 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
|
rob@100
|
2656 target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
|
rob@100
|
2657 target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
|
rob@100
|
2658 } else if (target.length === 4) {
|
rob@100
|
2659 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
|
rob@100
|
2660 target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
|
rob@100
|
2661 target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
|
rob@100
|
2662 target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
|
rob@100
|
2663 }
|
rob@100
|
2664 }
|
rob@100
|
2665 if (target instanceof PVector) {
|
rob@100
|
2666 target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
|
rob@100
|
2667 target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
|
rob@100
|
2668 target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
|
rob@100
|
2669 }
|
rob@100
|
2670 return target;
|
rob@100
|
2671 },
|
rob@100
|
2672 /**
|
rob@100
|
2673 * @member PMatrix3D
|
rob@100
|
2674 * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix3D or elements of a matrix can be passed in.
|
rob@100
|
2675 *
|
rob@100
|
2676 * @param {PMatrix3D} matrix the matrix to apply this matrix to
|
rob@100
|
2677 * @param {float} m00 the first element of the matrix
|
rob@100
|
2678 * @param {float} m01 the second element of the matrix
|
rob@100
|
2679 * @param {float} m02 the third element of the matrix
|
rob@100
|
2680 * @param {float} m03 the fourth element of the matrix
|
rob@100
|
2681 * @param {float} m10 the fifth element of the matrix
|
rob@100
|
2682 * @param {float} m11 the sixth element of the matrix
|
rob@100
|
2683 * @param {float} m12 the seventh element of the matrix
|
rob@100
|
2684 * @param {float} m13 the eight element of the matrix
|
rob@100
|
2685 * @param {float} m20 the nineth element of the matrix
|
rob@100
|
2686 * @param {float} m21 the tenth element of the matrix
|
rob@100
|
2687 * @param {float} m22 the eleventh element of the matrix
|
rob@100
|
2688 * @param {float} m23 the twelveth element of the matrix
|
rob@100
|
2689 * @param {float} m30 the thirteenth element of the matrix
|
rob@100
|
2690 * @param {float} m31 the fourtheenth element of the matrix
|
rob@100
|
2691 * @param {float} m32 the fivetheenth element of the matrix
|
rob@100
|
2692 * @param {float} m33 the sixteenth element of the matrix
|
rob@100
|
2693 */
|
rob@100
|
2694 preApply: function() {
|
rob@100
|
2695 var source;
|
rob@100
|
2696 if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
|
rob@100
|
2697 source = arguments[0].array();
|
rob@100
|
2698 } else if (arguments.length === 16) {
|
rob@100
|
2699 source = Array.prototype.slice.call(arguments);
|
rob@100
|
2700 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
rob@100
|
2701 source = arguments[0];
|
rob@100
|
2702 }
|
rob@100
|
2703
|
rob@100
|
2704 var result = [0, 0, 0, 0,
|
rob@100
|
2705 0, 0, 0, 0,
|
rob@100
|
2706 0, 0, 0, 0,
|
rob@100
|
2707 0, 0, 0, 0];
|
rob@100
|
2708 var e = 0;
|
rob@100
|
2709 for (var row = 0; row < 4; row++) {
|
rob@100
|
2710 for (var col = 0; col < 4; col++, e++) {
|
rob@100
|
2711 result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] *
|
rob@100
|
2712 source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] +
|
rob@100
|
2713 this.elements[col + 12] * source[row * 4 + 3];
|
rob@100
|
2714 }
|
rob@100
|
2715 }
|
rob@100
|
2716 this.elements = result.slice();
|
rob@100
|
2717 },
|
rob@100
|
2718 /**
|
rob@100
|
2719 * @member PMatrix3D
|
rob@100
|
2720 * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix3D or a list of floats can be passed in.
|
rob@100
|
2721 *
|
rob@100
|
2722 * @param {PMatrix3D} matrix the matrix to apply this matrix to
|
rob@100
|
2723 * @param {float} m00 the first element of the matrix
|
rob@100
|
2724 * @param {float} m01 the second element of the matrix
|
rob@100
|
2725 * @param {float} m02 the third element of the matrix
|
rob@100
|
2726 * @param {float} m03 the fourth element of the matrix
|
rob@100
|
2727 * @param {float} m10 the fifth element of the matrix
|
rob@100
|
2728 * @param {float} m11 the sixth element of the matrix
|
rob@100
|
2729 * @param {float} m12 the seventh element of the matrix
|
rob@100
|
2730 * @param {float} m13 the eight element of the matrix
|
rob@100
|
2731 * @param {float} m20 the nineth element of the matrix
|
rob@100
|
2732 * @param {float} m21 the tenth element of the matrix
|
rob@100
|
2733 * @param {float} m22 the eleventh element of the matrix
|
rob@100
|
2734 * @param {float} m23 the twelveth element of the matrix
|
rob@100
|
2735 * @param {float} m30 the thirteenth element of the matrix
|
rob@100
|
2736 * @param {float} m31 the fourtheenth element of the matrix
|
rob@100
|
2737 * @param {float} m32 the fivetheenth element of the matrix
|
rob@100
|
2738 * @param {float} m33 the sixteenth element of the matrix
|
rob@100
|
2739 */
|
rob@100
|
2740 apply: function() {
|
rob@100
|
2741 var source;
|
rob@100
|
2742 if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
|
rob@100
|
2743 source = arguments[0].array();
|
rob@100
|
2744 } else if (arguments.length === 16) {
|
rob@100
|
2745 source = Array.prototype.slice.call(arguments);
|
rob@100
|
2746 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
rob@100
|
2747 source = arguments[0];
|
rob@100
|
2748 }
|
rob@100
|
2749
|
rob@100
|
2750 var result = [0, 0, 0, 0,
|
rob@100
|
2751 0, 0, 0, 0,
|
rob@100
|
2752 0, 0, 0, 0,
|
rob@100
|
2753 0, 0, 0, 0];
|
rob@100
|
2754 var e = 0;
|
rob@100
|
2755 for (var row = 0; row < 4; row++) {
|
rob@100
|
2756 for (var col = 0; col < 4; col++, e++) {
|
rob@100
|
2757 result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] *
|
rob@100
|
2758 source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] +
|
rob@100
|
2759 this.elements[row * 4 + 3] * source[col + 12];
|
rob@100
|
2760 }
|
rob@100
|
2761 }
|
rob@100
|
2762 this.elements = result.slice();
|
rob@100
|
2763 },
|
rob@100
|
2764 /**
|
rob@100
|
2765 * @member PMatrix3D
|
rob@100
|
2766 * The rotate() function rotates the matrix.
|
rob@100
|
2767 *
|
rob@100
|
2768 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
2769 */
|
rob@100
|
2770 rotate: function(angle, v0, v1, v2) {
|
rob@100
|
2771 if (!v1) {
|
rob@100
|
2772 this.rotateZ(angle);
|
rob@100
|
2773 } else {
|
rob@100
|
2774 // TODO should make sure this vector is normalized
|
rob@100
|
2775 var c = Math.cos(angle);
|
rob@100
|
2776 var s = Math.sin(angle);
|
rob@100
|
2777 var t = 1.0 - c;
|
rob@100
|
2778
|
rob@100
|
2779 this.apply((t * v0 * v0) + c,
|
rob@100
|
2780 (t * v0 * v1) - (s * v2),
|
rob@100
|
2781 (t * v0 * v2) + (s * v1),
|
rob@100
|
2782 0,
|
rob@100
|
2783 (t * v0 * v1) + (s * v2),
|
rob@100
|
2784 (t * v1 * v1) + c,
|
rob@100
|
2785 (t * v1 * v2) - (s * v0),
|
rob@100
|
2786 0,
|
rob@100
|
2787 (t * v0 * v2) - (s * v1),
|
rob@100
|
2788 (t * v1 * v2) + (s * v0),
|
rob@100
|
2789 (t * v2 * v2) + c,
|
rob@100
|
2790 0,
|
rob@100
|
2791 0, 0, 0, 1);
|
rob@100
|
2792 }
|
rob@100
|
2793 },
|
rob@100
|
2794 /**
|
rob@100
|
2795 * @member PMatrix3D
|
rob@100
|
2796 * The invApply() function applies the inverted matrix to this matrix.
|
rob@100
|
2797 *
|
rob@100
|
2798 * @param {float} m00 the first element of the matrix
|
rob@100
|
2799 * @param {float} m01 the second element of the matrix
|
rob@100
|
2800 * @param {float} m02 the third element of the matrix
|
rob@100
|
2801 * @param {float} m03 the fourth element of the matrix
|
rob@100
|
2802 * @param {float} m10 the fifth element of the matrix
|
rob@100
|
2803 * @param {float} m11 the sixth element of the matrix
|
rob@100
|
2804 * @param {float} m12 the seventh element of the matrix
|
rob@100
|
2805 * @param {float} m13 the eight element of the matrix
|
rob@100
|
2806 * @param {float} m20 the nineth element of the matrix
|
rob@100
|
2807 * @param {float} m21 the tenth element of the matrix
|
rob@100
|
2808 * @param {float} m22 the eleventh element of the matrix
|
rob@100
|
2809 * @param {float} m23 the twelveth element of the matrix
|
rob@100
|
2810 * @param {float} m30 the thirteenth element of the matrix
|
rob@100
|
2811 * @param {float} m31 the fourtheenth element of the matrix
|
rob@100
|
2812 * @param {float} m32 the fivetheenth element of the matrix
|
rob@100
|
2813 * @param {float} m33 the sixteenth element of the matrix
|
rob@100
|
2814 *
|
rob@100
|
2815 * @return {boolean} returns true if the operation was successful.
|
rob@100
|
2816 */
|
rob@100
|
2817 invApply: function() {
|
rob@100
|
2818 if (inverseCopy === undef) {
|
rob@100
|
2819 inverseCopy = new PMatrix3D();
|
rob@100
|
2820 }
|
rob@100
|
2821 var a = arguments;
|
rob@100
|
2822 inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8],
|
rob@100
|
2823 a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
|
rob@100
|
2824
|
rob@100
|
2825 if (!inverseCopy.invert()) {
|
rob@100
|
2826 return false;
|
rob@100
|
2827 }
|
rob@100
|
2828 this.preApply(inverseCopy);
|
rob@100
|
2829 return true;
|
rob@100
|
2830 },
|
rob@100
|
2831 /**
|
rob@100
|
2832 * @member PMatrix3D
|
rob@100
|
2833 * The rotateZ() function rotates the matrix.
|
rob@100
|
2834 *
|
rob@100
|
2835 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
2836 */
|
rob@100
|
2837 rotateX: function(angle) {
|
rob@100
|
2838 var c = Math.cos(angle);
|
rob@100
|
2839 var s = Math.sin(angle);
|
rob@100
|
2840 this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
|
rob@100
|
2841 },
|
rob@100
|
2842 /**
|
rob@100
|
2843 * @member PMatrix3D
|
rob@100
|
2844 * The rotateY() function rotates the matrix.
|
rob@100
|
2845 *
|
rob@100
|
2846 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
2847 */
|
rob@100
|
2848 rotateY: function(angle) {
|
rob@100
|
2849 var c = Math.cos(angle);
|
rob@100
|
2850 var s = Math.sin(angle);
|
rob@100
|
2851 this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
|
rob@100
|
2852 },
|
rob@100
|
2853 /**
|
rob@100
|
2854 * @member PMatrix3D
|
rob@100
|
2855 * The rotateZ() function rotates the matrix.
|
rob@100
|
2856 *
|
rob@100
|
2857 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
2858 */
|
rob@100
|
2859 rotateZ: function(angle) {
|
rob@100
|
2860 var c = Math.cos(angle);
|
rob@100
|
2861 var s = Math.sin(angle);
|
rob@100
|
2862 this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
rob@100
|
2863 },
|
rob@100
|
2864 /**
|
rob@100
|
2865 * @member PMatrix3D
|
rob@100
|
2866 * The scale() function increases or decreases the size of a matrix by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
|
rob@100
|
2867 * This is equivalent to a three parameter call.
|
rob@100
|
2868 *
|
rob@100
|
2869 * @param {float} sx the amount to scale on the x-axis
|
rob@100
|
2870 * @param {float} sy the amount to scale on the y-axis
|
rob@100
|
2871 * @param {float} sz the amount to scale on the z-axis
|
rob@100
|
2872 */
|
rob@100
|
2873 scale: function(sx, sy, sz) {
|
rob@100
|
2874 if (sx && !sy && !sz) {
|
rob@100
|
2875 sy = sz = sx;
|
rob@100
|
2876 } else if (sx && sy && !sz) {
|
rob@100
|
2877 sz = 1;
|
rob@100
|
2878 }
|
rob@100
|
2879
|
rob@100
|
2880 if (sx && sy && sz) {
|
rob@100
|
2881 this.elements[0] *= sx;
|
rob@100
|
2882 this.elements[1] *= sy;
|
rob@100
|
2883 this.elements[2] *= sz;
|
rob@100
|
2884 this.elements[4] *= sx;
|
rob@100
|
2885 this.elements[5] *= sy;
|
rob@100
|
2886 this.elements[6] *= sz;
|
rob@100
|
2887 this.elements[8] *= sx;
|
rob@100
|
2888 this.elements[9] *= sy;
|
rob@100
|
2889 this.elements[10] *= sz;
|
rob@100
|
2890 this.elements[12] *= sx;
|
rob@100
|
2891 this.elements[13] *= sy;
|
rob@100
|
2892 this.elements[14] *= sz;
|
rob@100
|
2893 }
|
rob@100
|
2894 },
|
rob@100
|
2895 /**
|
rob@100
|
2896 * @member PMatrix3D
|
rob@100
|
2897 * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
|
rob@100
|
2898 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
2899 *
|
rob@100
|
2900 * @param {float} angle angle of skew specified in radians
|
rob@100
|
2901 */
|
rob@100
|
2902 skewX: function(angle) {
|
rob@100
|
2903 var t = Math.tan(angle);
|
rob@100
|
2904 this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
rob@100
|
2905 },
|
rob@100
|
2906 /**
|
rob@100
|
2907 * @member PMatrix3D
|
rob@100
|
2908 * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
|
rob@100
|
2909 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
2910 *
|
rob@100
|
2911 * @param {float} angle angle of skew specified in radians
|
rob@100
|
2912 */
|
rob@100
|
2913 skewY: function(angle) {
|
rob@100
|
2914 var t = Math.tan(angle);
|
rob@100
|
2915 this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
rob@100
|
2916 },
|
rob@100
|
2917 /**
|
rob@100
|
2918 * @member PMatrix3D
|
rob@100
|
2919 * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter.
|
rob@100
|
2920 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
2921 *
|
rob@100
|
2922 * @param {float} angle angle of shear specified in radians
|
rob@100
|
2923 */
|
rob@100
|
2924 shearX: function(angle) {
|
rob@100
|
2925 var t = Math.tan(angle);
|
rob@100
|
2926 this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
rob@100
|
2927 },
|
rob@100
|
2928 /**
|
rob@100
|
2929 * @member PMatrix3D
|
rob@100
|
2930 * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter.
|
rob@100
|
2931 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
2932 *
|
rob@100
|
2933 * @param {float} angle angle of shear specified in radians
|
rob@100
|
2934 */
|
rob@100
|
2935 shearY: function(angle) {
|
rob@100
|
2936 var t = Math.tan(angle);
|
rob@100
|
2937 this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
rob@100
|
2938 },
|
rob@100
|
2939 multX: function(x, y, z, w) {
|
rob@100
|
2940 if (!z) {
|
rob@100
|
2941 return this.elements[0] * x + this.elements[1] * y + this.elements[3];
|
rob@100
|
2942 }
|
rob@100
|
2943 if (!w) {
|
rob@100
|
2944 return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
|
rob@100
|
2945 }
|
rob@100
|
2946 return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
|
rob@100
|
2947 },
|
rob@100
|
2948 multY: function(x, y, z, w) {
|
rob@100
|
2949 if (!z) {
|
rob@100
|
2950 return this.elements[4] * x + this.elements[5] * y + this.elements[7];
|
rob@100
|
2951 }
|
rob@100
|
2952 if (!w) {
|
rob@100
|
2953 return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
|
rob@100
|
2954 }
|
rob@100
|
2955 return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
|
rob@100
|
2956 },
|
rob@100
|
2957 multZ: function(x, y, z, w) {
|
rob@100
|
2958 if (!w) {
|
rob@100
|
2959 return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
|
rob@100
|
2960 }
|
rob@100
|
2961 return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
|
rob@100
|
2962 },
|
rob@100
|
2963 multW: function(x, y, z, w) {
|
rob@100
|
2964 if (!w) {
|
rob@100
|
2965 return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15];
|
rob@100
|
2966 }
|
rob@100
|
2967 return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
|
rob@100
|
2968 },
|
rob@100
|
2969 /**
|
rob@100
|
2970 * @member PMatrix3D
|
rob@100
|
2971 * The invert() function inverts this matrix
|
rob@100
|
2972 *
|
rob@100
|
2973 * @return {boolean} true if successful
|
rob@100
|
2974 */
|
rob@100
|
2975 invert: function() {
|
rob@100
|
2976 var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4];
|
rob@100
|
2977 var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4];
|
rob@100
|
2978 var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4];
|
rob@100
|
2979 var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5];
|
rob@100
|
2980 var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5];
|
rob@100
|
2981 var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6];
|
rob@100
|
2982 var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12];
|
rob@100
|
2983 var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12];
|
rob@100
|
2984 var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12];
|
rob@100
|
2985 var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13];
|
rob@100
|
2986 var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13];
|
rob@100
|
2987 var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14];
|
rob@100
|
2988
|
rob@100
|
2989 // Determinant
|
rob@100
|
2990 var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
|
rob@100
|
2991
|
rob@100
|
2992 // Account for a very small value
|
rob@100
|
2993 // return false if not successful.
|
rob@100
|
2994 if (Math.abs(fDet) <= 1e-9) {
|
rob@100
|
2995 return false;
|
rob@100
|
2996 }
|
rob@100
|
2997
|
rob@100
|
2998 var kInv = [];
|
rob@100
|
2999 kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3;
|
rob@100
|
3000 kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1;
|
rob@100
|
3001 kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0;
|
rob@100
|
3002 kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0;
|
rob@100
|
3003 kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3;
|
rob@100
|
3004 kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1;
|
rob@100
|
3005 kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0;
|
rob@100
|
3006 kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0;
|
rob@100
|
3007 kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3;
|
rob@100
|
3008 kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1;
|
rob@100
|
3009 kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0;
|
rob@100
|
3010 kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0;
|
rob@100
|
3011 kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3;
|
rob@100
|
3012 kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1;
|
rob@100
|
3013 kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0;
|
rob@100
|
3014 kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0;
|
rob@100
|
3015
|
rob@100
|
3016 // Inverse using Determinant
|
rob@100
|
3017 var fInvDet = 1.0 / fDet;
|
rob@100
|
3018 kInv[0] *= fInvDet;
|
rob@100
|
3019 kInv[1] *= fInvDet;
|
rob@100
|
3020 kInv[2] *= fInvDet;
|
rob@100
|
3021 kInv[3] *= fInvDet;
|
rob@100
|
3022 kInv[4] *= fInvDet;
|
rob@100
|
3023 kInv[5] *= fInvDet;
|
rob@100
|
3024 kInv[6] *= fInvDet;
|
rob@100
|
3025 kInv[7] *= fInvDet;
|
rob@100
|
3026 kInv[8] *= fInvDet;
|
rob@100
|
3027 kInv[9] *= fInvDet;
|
rob@100
|
3028 kInv[10] *= fInvDet;
|
rob@100
|
3029 kInv[11] *= fInvDet;
|
rob@100
|
3030 kInv[12] *= fInvDet;
|
rob@100
|
3031 kInv[13] *= fInvDet;
|
rob@100
|
3032 kInv[14] *= fInvDet;
|
rob@100
|
3033 kInv[15] *= fInvDet;
|
rob@100
|
3034
|
rob@100
|
3035 this.elements = kInv.slice();
|
rob@100
|
3036 return true;
|
rob@100
|
3037 },
|
rob@100
|
3038 toString: function() {
|
rob@100
|
3039 var str = "";
|
rob@100
|
3040 for (var i = 0; i < 15; i++) {
|
rob@100
|
3041 str += this.elements[i] + ", ";
|
rob@100
|
3042 }
|
rob@100
|
3043 str += this.elements[15];
|
rob@100
|
3044 return str;
|
rob@100
|
3045 },
|
rob@100
|
3046 /**
|
rob@100
|
3047 * @member PMatrix3D
|
rob@100
|
3048 * The print() function prints out the elements of this matrix
|
rob@100
|
3049 */
|
rob@100
|
3050 print: function() {
|
rob@100
|
3051 var digits = printMatrixHelper(this.elements);
|
rob@100
|
3052
|
rob@100
|
3053 var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) +
|
rob@100
|
3054 " " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) +
|
rob@100
|
3055 "\n" + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) +
|
rob@100
|
3056 " " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) +
|
rob@100
|
3057 "\n" + p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) +
|
rob@100
|
3058 " " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) +
|
rob@100
|
3059 "\n" + p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) +
|
rob@100
|
3060 " " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n";
|
rob@100
|
3061 p.println(output);
|
rob@100
|
3062 },
|
rob@100
|
3063 invTranslate: function(tx, ty, tz) {
|
rob@100
|
3064 this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1);
|
rob@100
|
3065 },
|
rob@100
|
3066 invRotateX: function(angle) {
|
rob@100
|
3067 var c = Math.cos(-angle);
|
rob@100
|
3068 var s = Math.sin(-angle);
|
rob@100
|
3069 this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
|
rob@100
|
3070 },
|
rob@100
|
3071 invRotateY: function(angle) {
|
rob@100
|
3072 var c = Math.cos(-angle);
|
rob@100
|
3073 var s = Math.sin(-angle);
|
rob@100
|
3074 this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
|
rob@100
|
3075 },
|
rob@100
|
3076 invRotateZ: function(angle) {
|
rob@100
|
3077 var c = Math.cos(-angle);
|
rob@100
|
3078 var s = Math.sin(-angle);
|
rob@100
|
3079 this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
rob@100
|
3080 },
|
rob@100
|
3081 invScale: function(x, y, z) {
|
rob@100
|
3082 this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]);
|
rob@100
|
3083 }
|
rob@100
|
3084 };
|
rob@100
|
3085
|
rob@100
|
3086 return PMatrix3D;
|
rob@100
|
3087 };
|
rob@100
|
3088 },{}],15:[function(require,module,exports){
|
rob@100
|
3089 module.exports = function(options) {
|
rob@100
|
3090 var PConstants = options.PConstants,
|
rob@100
|
3091 PMatrix2D = options.PMatrix2D,
|
rob@100
|
3092 PMatrix3D = options.PMatrix3D;
|
rob@100
|
3093
|
rob@100
|
3094 /**
|
rob@100
|
3095 * Datatype for storing shapes. Processing can currently load and display SVG (Scalable Vector Graphics) shapes.
|
rob@100
|
3096 * Before a shape is used, it must be loaded with the <b>loadShape()</b> function. The <b>shape()</b> function is used to draw the shape to the display window.
|
rob@100
|
3097 * The <b>PShape</b> object contain a group of methods, linked below, that can operate on the shape data.
|
rob@100
|
3098 * <br><br>The <b>loadShape()</b> method supports SVG files created with Inkscape and Adobe Illustrator.
|
rob@100
|
3099 * It is not a full SVG implementation, but offers some straightforward support for handling vector data.
|
rob@100
|
3100 *
|
rob@100
|
3101 * @param {int} family the shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY
|
rob@100
|
3102 *
|
rob@100
|
3103 * @see #shape()
|
rob@100
|
3104 * @see #loadShape()
|
rob@100
|
3105 * @see #shapeMode()
|
rob@100
|
3106 */
|
rob@100
|
3107 var PShape = function(family) {
|
rob@100
|
3108 this.family = family || PConstants.GROUP;
|
rob@100
|
3109 this.visible = true;
|
rob@100
|
3110 this.style = true;
|
rob@100
|
3111 this.children = [];
|
rob@100
|
3112 this.nameTable = [];
|
rob@100
|
3113 this.params = [];
|
rob@100
|
3114 this.name = "";
|
rob@100
|
3115 this.image = null; //type PImage
|
rob@100
|
3116 this.matrix = null;
|
rob@100
|
3117 this.kind = null;
|
rob@100
|
3118 this.close = null;
|
rob@100
|
3119 this.width = null;
|
rob@100
|
3120 this.height = null;
|
rob@100
|
3121 this.parent = null;
|
rob@100
|
3122 };
|
rob@100
|
3123 /**
|
rob@100
|
3124 * PShape methods
|
rob@100
|
3125 * missing: findChild(), apply(), contains(), findChild(), getPrimitive(), getParams(), getVertex() , getVertexCount(),
|
rob@100
|
3126 * getVertexCode() , getVertexCodes() , getVertexCodeCount(), getVertexX(), getVertexY(), getVertexZ()
|
rob@100
|
3127 */
|
rob@100
|
3128 PShape.prototype = {
|
rob@100
|
3129 /**
|
rob@100
|
3130 * @member PShape
|
rob@100
|
3131 * The isVisible() function returns a boolean value "true" if the image is set to be visible, "false" if not. This is modified with the <b>setVisible()</b> parameter.
|
rob@100
|
3132 * <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
|
rob@100
|
3133 * For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
|
rob@100
|
3134 *
|
rob@100
|
3135 * @return {boolean} returns "true" if the image is set to be visible, "false" if not
|
rob@100
|
3136 */
|
rob@100
|
3137 isVisible: function(){
|
rob@100
|
3138 return this.visible;
|
rob@100
|
3139 },
|
rob@100
|
3140 /**
|
rob@100
|
3141 * @member PShape
|
rob@100
|
3142 * The setVisible() function sets the shape to be visible or invisible. This is determined by the value of the <b>visible</b> parameter.
|
rob@100
|
3143 * <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
|
rob@100
|
3144 * For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
|
rob@100
|
3145 *
|
rob@100
|
3146 * @param {boolean} visible "false" makes the shape invisible and "true" makes it visible
|
rob@100
|
3147 */
|
rob@100
|
3148 setVisible: function (visible){
|
rob@100
|
3149 this.visible = visible;
|
rob@100
|
3150 },
|
rob@100
|
3151 /**
|
rob@100
|
3152 * @member PShape
|
rob@100
|
3153 * The disableStyle() function disables the shape's style data and uses Processing's current styles. Styles include attributes such as colors, stroke weight, and stroke joints.
|
rob@100
|
3154 * Overrides this shape's style information and uses PGraphics styles and colors. Identical to ignoreStyles(true). Also disables styles for all child shapes.
|
rob@100
|
3155 */
|
rob@100
|
3156 disableStyle: function(){
|
rob@100
|
3157 this.style = false;
|
rob@100
|
3158 for(var i = 0, j=this.children.length; i<j; i++) {
|
rob@100
|
3159 this.children[i].disableStyle();
|
rob@100
|
3160 }
|
rob@100
|
3161 },
|
rob@100
|
3162 /**
|
rob@100
|
3163 * @member PShape
|
rob@100
|
3164 * The enableStyle() function enables the shape's style data and ignores Processing's current styles. Styles include attributes such as colors, stroke weight, and stroke joints.
|
rob@100
|
3165 */
|
rob@100
|
3166 enableStyle: function(){
|
rob@100
|
3167 this.style = true;
|
rob@100
|
3168 for(var i = 0, j=this.children.length; i<j; i++) {
|
rob@100
|
3169 this.children[i].enableStyle();
|
rob@100
|
3170 }
|
rob@100
|
3171 },
|
rob@100
|
3172 /**
|
rob@100
|
3173 * @member PShape
|
rob@100
|
3174 * The getFamily function returns the shape type
|
rob@100
|
3175 *
|
rob@100
|
3176 * @return {int} the shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY
|
rob@100
|
3177 */
|
rob@100
|
3178 getFamily: function(){
|
rob@100
|
3179 return this.family;
|
rob@100
|
3180 },
|
rob@100
|
3181 /**
|
rob@100
|
3182 * @member PShape
|
rob@100
|
3183 * The getWidth() function gets the width of the drawing area (not necessarily the shape boundary).
|
rob@100
|
3184 */
|
rob@100
|
3185 getWidth: function(){
|
rob@100
|
3186 return this.width;
|
rob@100
|
3187 },
|
rob@100
|
3188 /**
|
rob@100
|
3189 * @member PShape
|
rob@100
|
3190 * The getHeight() function gets the height of the drawing area (not necessarily the shape boundary).
|
rob@100
|
3191 */
|
rob@100
|
3192 getHeight: function(){
|
rob@100
|
3193 return this.height;
|
rob@100
|
3194 },
|
rob@100
|
3195 /**
|
rob@100
|
3196 * @member PShape
|
rob@100
|
3197 * The setName() function sets the name of the shape
|
rob@100
|
3198 *
|
rob@100
|
3199 * @param {String} name the name of the shape
|
rob@100
|
3200 */
|
rob@100
|
3201 setName: function(name){
|
rob@100
|
3202 this.name = name;
|
rob@100
|
3203 },
|
rob@100
|
3204 /**
|
rob@100
|
3205 * @member PShape
|
rob@100
|
3206 * The getName() function returns the name of the shape
|
rob@100
|
3207 *
|
rob@100
|
3208 * @return {String} the name of the shape
|
rob@100
|
3209 */
|
rob@100
|
3210 getName: function(){
|
rob@100
|
3211 return this.name;
|
rob@100
|
3212 },
|
rob@100
|
3213 /**
|
rob@100
|
3214 * @member PShape
|
rob@100
|
3215 * Called by the following (the shape() command adds the g)
|
rob@100
|
3216 * PShape s = loadShapes("blah.svg");
|
rob@100
|
3217 * shape(s);
|
rob@100
|
3218 */
|
rob@100
|
3219 draw: function(renderContext) {
|
rob@100
|
3220 if(!renderContext) {
|
rob@100
|
3221 throw "render context missing for draw() in PShape";
|
rob@100
|
3222 }
|
rob@100
|
3223 if (this.visible) {
|
rob@100
|
3224 this.pre(renderContext);
|
rob@100
|
3225 this.drawImpl(renderContext);
|
rob@100
|
3226 this.post(renderContext);
|
rob@100
|
3227 }
|
rob@100
|
3228 },
|
rob@100
|
3229 /**
|
rob@100
|
3230 * @member PShape
|
rob@100
|
3231 * the drawImpl() function draws the SVG document.
|
rob@100
|
3232 */
|
rob@100
|
3233 drawImpl: function(renderContext) {
|
rob@100
|
3234 if (this.family === PConstants.GROUP) {
|
rob@100
|
3235 this.drawGroup(renderContext);
|
rob@100
|
3236 } else if (this.family === PConstants.PRIMITIVE) {
|
rob@100
|
3237 this.drawPrimitive(renderContext);
|
rob@100
|
3238 } else if (this.family === PConstants.GEOMETRY) {
|
rob@100
|
3239 this.drawGeometry(renderContext);
|
rob@100
|
3240 } else if (this.family === PConstants.PATH) {
|
rob@100
|
3241 this.drawPath(renderContext);
|
rob@100
|
3242 }
|
rob@100
|
3243 },
|
rob@100
|
3244 /**
|
rob@100
|
3245 * @member PShape
|
rob@100
|
3246 * The drawPath() function draws the <path> part of the SVG document.
|
rob@100
|
3247 */
|
rob@100
|
3248 drawPath: function(renderContext) {
|
rob@100
|
3249 var i, j;
|
rob@100
|
3250 if (this.vertices.length === 0) { return; }
|
rob@100
|
3251 renderContext.beginShape();
|
rob@100
|
3252 if (this.vertexCodes.length === 0) { // each point is a simple vertex
|
rob@100
|
3253 if (this.vertices[0].length === 2) { // drawing 2D vertices
|
rob@100
|
3254 for (i = 0, j = this.vertices.length; i < j; i++) {
|
rob@100
|
3255 renderContext.vertex(this.vertices[i][0], this.vertices[i][1]);
|
rob@100
|
3256 }
|
rob@100
|
3257 } else { // drawing 3D vertices
|
rob@100
|
3258 for (i = 0, j = this.vertices.length; i < j; i++) {
|
rob@100
|
3259 renderContext.vertex(this.vertices[i][0],
|
rob@100
|
3260 this.vertices[i][1],
|
rob@100
|
3261 this.vertices[i][2]);
|
rob@100
|
3262 }
|
rob@100
|
3263 }
|
rob@100
|
3264 } else { // coded set of vertices
|
rob@100
|
3265 var index = 0;
|
rob@100
|
3266 if (this.vertices[0].length === 2) { // drawing a 2D path
|
rob@100
|
3267 for (i = 0, j = this.vertexCodes.length; i < j; i++) {
|
rob@100
|
3268 if (this.vertexCodes[i] === PConstants.VERTEX) {
|
rob@100
|
3269 renderContext.vertex(this.vertices[index][0], this.vertices[index][1], this.vertices[index].moveTo);
|
rob@100
|
3270 renderContext.breakShape = false;
|
rob@100
|
3271 index++;
|
rob@100
|
3272 } else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
|
rob@100
|
3273 renderContext.bezierVertex(this.vertices[index+0][0],
|
rob@100
|
3274 this.vertices[index+0][1],
|
rob@100
|
3275 this.vertices[index+1][0],
|
rob@100
|
3276 this.vertices[index+1][1],
|
rob@100
|
3277 this.vertices[index+2][0],
|
rob@100
|
3278 this.vertices[index+2][1]);
|
rob@100
|
3279 index += 3;
|
rob@100
|
3280 } else if (this.vertexCodes[i] === PConstants.CURVE_VERTEX) {
|
rob@100
|
3281 renderContext.curveVertex(this.vertices[index][0],
|
rob@100
|
3282 this.vertices[index][1]);
|
rob@100
|
3283 index++;
|
rob@100
|
3284 } else if (this.vertexCodes[i] === PConstants.BREAK) {
|
rob@100
|
3285 renderContext.breakShape = true;
|
rob@100
|
3286 }
|
rob@100
|
3287 }
|
rob@100
|
3288 } else { // drawing a 3D path
|
rob@100
|
3289 for (i = 0, j = this.vertexCodes.length; i < j; i++) {
|
rob@100
|
3290 if (this.vertexCodes[i] === PConstants.VERTEX) {
|
rob@100
|
3291 renderContext.vertex(this.vertices[index][0],
|
rob@100
|
3292 this.vertices[index][1],
|
rob@100
|
3293 this.vertices[index][2]);
|
rob@100
|
3294 if (this.vertices[index].moveTo === true) {
|
rob@100
|
3295 vertArray[vertArray.length-1].moveTo = true;
|
rob@100
|
3296 } else if (this.vertices[index].moveTo === false) {
|
rob@100
|
3297 vertArray[vertArray.length-1].moveTo = false;
|
rob@100
|
3298 }
|
rob@100
|
3299 renderContext.breakShape = false;
|
rob@100
|
3300 } else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
|
rob@100
|
3301 renderContext.bezierVertex(this.vertices[index+0][0],
|
rob@100
|
3302 this.vertices[index+0][1],
|
rob@100
|
3303 this.vertices[index+0][2],
|
rob@100
|
3304 this.vertices[index+1][0],
|
rob@100
|
3305 this.vertices[index+1][1],
|
rob@100
|
3306 this.vertices[index+1][2],
|
rob@100
|
3307 this.vertices[index+2][0],
|
rob@100
|
3308 this.vertices[index+2][1],
|
rob@100
|
3309 this.vertices[index+2][2]);
|
rob@100
|
3310 index += 3;
|
rob@100
|
3311 } else if (this.vertexCodes[i] === PConstants.CURVE_VERTEX) {
|
rob@100
|
3312 renderContext.curveVertex(this.vertices[index][0],
|
rob@100
|
3313 this.vertices[index][1],
|
rob@100
|
3314 this.vertices[index][2]);
|
rob@100
|
3315 index++;
|
rob@100
|
3316 } else if (this.vertexCodes[i] === PConstants.BREAK) {
|
rob@100
|
3317 renderContext.breakShape = true;
|
rob@100
|
3318 }
|
rob@100
|
3319 }
|
rob@100
|
3320 }
|
rob@100
|
3321 }
|
rob@100
|
3322 renderContext.endShape(this.close ? PConstants.CLOSE : PConstants.OPEN);
|
rob@100
|
3323 },
|
rob@100
|
3324 /**
|
rob@100
|
3325 * @member PShape
|
rob@100
|
3326 * The drawGeometry() function draws the geometry part of the SVG document.
|
rob@100
|
3327 */
|
rob@100
|
3328 drawGeometry: function(renderContext) {
|
rob@100
|
3329 var i, j;
|
rob@100
|
3330 renderContext.beginShape(this.kind);
|
rob@100
|
3331 if (this.style) {
|
rob@100
|
3332 for (i = 0, j = this.vertices.length; i < j; i++) {
|
rob@100
|
3333 renderContext.vertex(this.vertices[i]);
|
rob@100
|
3334 }
|
rob@100
|
3335 } else {
|
rob@100
|
3336 for (i = 0, j = this.vertices.length; i < j; i++) {
|
rob@100
|
3337 var vert = this.vertices[i];
|
rob@100
|
3338 if (vert[2] === 0) {
|
rob@100
|
3339 renderContext.vertex(vert[0], vert[1]);
|
rob@100
|
3340 } else {
|
rob@100
|
3341 renderContext.vertex(vert[0], vert[1], vert[2]);
|
rob@100
|
3342 }
|
rob@100
|
3343 }
|
rob@100
|
3344 }
|
rob@100
|
3345 renderContext.endShape();
|
rob@100
|
3346 },
|
rob@100
|
3347 /**
|
rob@100
|
3348 * @member PShape
|
rob@100
|
3349 * The drawGroup() function draws the <g> part of the SVG document.
|
rob@100
|
3350 */
|
rob@100
|
3351 drawGroup: function(renderContext) {
|
rob@100
|
3352 for (var i = 0, j = this.children.length; i < j; i++) {
|
rob@100
|
3353 this.children[i].draw(renderContext);
|
rob@100
|
3354 }
|
rob@100
|
3355 },
|
rob@100
|
3356 /**
|
rob@100
|
3357 * @member PShape
|
rob@100
|
3358 * The drawPrimitive() function draws SVG document shape elements. These can be point, line, triangle, quad, rect, ellipse, arc, box, or sphere.
|
rob@100
|
3359 */
|
rob@100
|
3360 drawPrimitive: function(renderContext) {
|
rob@100
|
3361 if (this.kind === PConstants.POINT) {
|
rob@100
|
3362 renderContext.point(this.params[0], this.params[1]);
|
rob@100
|
3363 } else if (this.kind === PConstants.LINE) {
|
rob@100
|
3364 if (this.params.length === 4) { // 2D
|
rob@100
|
3365 renderContext.line(this.params[0], this.params[1],
|
rob@100
|
3366 this.params[2], this.params[3]);
|
rob@100
|
3367 } else { // 3D
|
rob@100
|
3368 renderContext.line(this.params[0], this.params[1], this.params[2],
|
rob@100
|
3369 this.params[3], this.params[4], this.params[5]);
|
rob@100
|
3370 }
|
rob@100
|
3371 } else if (this.kind === PConstants.TRIANGLE) {
|
rob@100
|
3372 renderContext.triangle(this.params[0], this.params[1],
|
rob@100
|
3373 this.params[2], this.params[3],
|
rob@100
|
3374 this.params[4], this.params[5]);
|
rob@100
|
3375 } else if (this.kind === PConstants.QUAD) {
|
rob@100
|
3376 renderContext.quad(this.params[0], this.params[1],
|
rob@100
|
3377 this.params[2], this.params[3],
|
rob@100
|
3378 this.params[4], this.params[5],
|
rob@100
|
3379 this.params[6], this.params[7]);
|
rob@100
|
3380 } else if (this.kind === PConstants.RECT) {
|
rob@100
|
3381 if (this.image !== null) {
|
rob@100
|
3382 var imMode = imageModeConvert;
|
rob@100
|
3383 renderContext.imageMode(PConstants.CORNER);
|
rob@100
|
3384 renderContext.image(this.image,
|
rob@100
|
3385 this.params[0],
|
rob@100
|
3386 this.params[1],
|
rob@100
|
3387 this.params[2],
|
rob@100
|
3388 this.params[3]);
|
rob@100
|
3389 imageModeConvert = imMode;
|
rob@100
|
3390 } else {
|
rob@100
|
3391 var rcMode = renderContext.curRectMode;
|
rob@100
|
3392 renderContext.rectMode(PConstants.CORNER);
|
rob@100
|
3393 renderContext.rect(this.params[0],
|
rob@100
|
3394 this.params[1],
|
rob@100
|
3395 this.params[2],
|
rob@100
|
3396 this.params[3]);
|
rob@100
|
3397 renderContext.curRectMode = rcMode;
|
rob@100
|
3398 }
|
rob@100
|
3399 } else if (this.kind === PConstants.ELLIPSE) {
|
rob@100
|
3400 var elMode = renderContext.curEllipseMode;
|
rob@100
|
3401 renderContext.ellipseMode(PConstants.CORNER);
|
rob@100
|
3402 renderContext.ellipse(this.params[0],
|
rob@100
|
3403 this.params[1],
|
rob@100
|
3404 this.params[2],
|
rob@100
|
3405 this.params[3]);
|
rob@100
|
3406 renderContext.curEllipseMode = elMode;
|
rob@100
|
3407 } else if (this.kind === PConstants.ARC) {
|
rob@100
|
3408 var eMode = curEllipseMode;
|
rob@100
|
3409 renderContext.ellipseMode(PConstants.CORNER);
|
rob@100
|
3410 renderContext.arc(this.params[0],
|
rob@100
|
3411 this.params[1],
|
rob@100
|
3412 this.params[2],
|
rob@100
|
3413 this.params[3],
|
rob@100
|
3414 this.params[4],
|
rob@100
|
3415 this.params[5]);
|
rob@100
|
3416 curEllipseMode = eMode;
|
rob@100
|
3417 } else if (this.kind === PConstants.BOX) {
|
rob@100
|
3418 if (this.params.length === 1) {
|
rob@100
|
3419 renderContext.box(this.params[0]);
|
rob@100
|
3420 } else {
|
rob@100
|
3421 renderContext.box(this.params[0], this.params[1], this.params[2]);
|
rob@100
|
3422 }
|
rob@100
|
3423 } else if (this.kind === PConstants.SPHERE) {
|
rob@100
|
3424 renderContext.sphere(this.params[0]);
|
rob@100
|
3425 }
|
rob@100
|
3426 },
|
rob@100
|
3427 /**
|
rob@100
|
3428 * @member PShape
|
rob@100
|
3429 * The pre() function performs the preparations before the SVG is drawn. This includes doing transformations and storing previous styles.
|
rob@100
|
3430 */
|
rob@100
|
3431 pre: function(renderContext) {
|
rob@100
|
3432 if (this.matrix) {
|
rob@100
|
3433 renderContext.pushMatrix();
|
rob@100
|
3434 renderContext.transform(this.matrix);
|
rob@100
|
3435 }
|
rob@100
|
3436 if (this.style) {
|
rob@100
|
3437 renderContext.pushStyle();
|
rob@100
|
3438 this.styles(renderContext);
|
rob@100
|
3439 }
|
rob@100
|
3440 },
|
rob@100
|
3441 /**
|
rob@100
|
3442 * @member PShape
|
rob@100
|
3443 * The post() function performs the necessary actions after the SVG is drawn. This includes removing transformations and removing added styles.
|
rob@100
|
3444 */
|
rob@100
|
3445 post: function(renderContext) {
|
rob@100
|
3446 if (this.matrix) {
|
rob@100
|
3447 renderContext.popMatrix();
|
rob@100
|
3448 }
|
rob@100
|
3449 if (this.style) {
|
rob@100
|
3450 renderContext.popStyle();
|
rob@100
|
3451 }
|
rob@100
|
3452 },
|
rob@100
|
3453 /**
|
rob@100
|
3454 * @member PShape
|
rob@100
|
3455 * The styles() function changes the Processing's current styles
|
rob@100
|
3456 */
|
rob@100
|
3457 styles: function(renderContext) {
|
rob@100
|
3458 if (this.stroke) {
|
rob@100
|
3459 renderContext.stroke(this.strokeColor);
|
rob@100
|
3460 renderContext.strokeWeight(this.strokeWeight);
|
rob@100
|
3461 renderContext.strokeCap(this.strokeCap);
|
rob@100
|
3462 renderContext.strokeJoin(this.strokeJoin);
|
rob@100
|
3463 } else {
|
rob@100
|
3464 renderContext.noStroke();
|
rob@100
|
3465 }
|
rob@100
|
3466
|
rob@100
|
3467 if (this.fill) {
|
rob@100
|
3468 renderContext.fill(this.fillColor);
|
rob@100
|
3469
|
rob@100
|
3470 } else {
|
rob@100
|
3471 renderContext.noFill();
|
rob@100
|
3472 }
|
rob@100
|
3473 },
|
rob@100
|
3474 /**
|
rob@100
|
3475 * @member PShape
|
rob@100
|
3476 * The getChild() function extracts a child shape from a parent shape. Specify the name of the shape with the <b>target</b> parameter or the
|
rob@100
|
3477 * layer position of the shape to get with the <b>index</b> parameter.
|
rob@100
|
3478 * The shape is returned as a <b>PShape</b> object, or <b>null</b> is returned if there is an error.
|
rob@100
|
3479 *
|
rob@100
|
3480 * @param {String} target the name of the shape to get
|
rob@100
|
3481 * @param {int} index the layer position of the shape to get
|
rob@100
|
3482 *
|
rob@100
|
3483 * @return {PShape} returns a child element of a shape as a PShape object or null if there is an error
|
rob@100
|
3484 */
|
rob@100
|
3485 getChild: function(child) {
|
rob@100
|
3486 var i, j;
|
rob@100
|
3487 if (typeof child === 'number') {
|
rob@100
|
3488 return this.children[child];
|
rob@100
|
3489 }
|
rob@100
|
3490 var found;
|
rob@100
|
3491 if(child === "" || this.name === child){
|
rob@100
|
3492 return this;
|
rob@100
|
3493 }
|
rob@100
|
3494 if(this.nameTable.length > 0) {
|
rob@100
|
3495 for(i = 0, j = this.nameTable.length; i < j || found; i++) {
|
rob@100
|
3496 if(this.nameTable[i].getName === child) {
|
rob@100
|
3497 found = this.nameTable[i];
|
rob@100
|
3498 break;
|
rob@100
|
3499 }
|
rob@100
|
3500 }
|
rob@100
|
3501 if (found) { return found; }
|
rob@100
|
3502 }
|
rob@100
|
3503 for(i = 0, j = this.children.length; i < j; i++) {
|
rob@100
|
3504 found = this.children[i].getChild(child);
|
rob@100
|
3505 if(found) { return found; }
|
rob@100
|
3506 }
|
rob@100
|
3507 return null;
|
rob@100
|
3508 },
|
rob@100
|
3509 /**
|
rob@100
|
3510 * @member PShape
|
rob@100
|
3511 * The getChildCount() returns the number of children
|
rob@100
|
3512 *
|
rob@100
|
3513 * @return {int} returns a count of children
|
rob@100
|
3514 */
|
rob@100
|
3515 getChildCount: function () {
|
rob@100
|
3516 return this.children.length;
|
rob@100
|
3517 },
|
rob@100
|
3518 /**
|
rob@100
|
3519 * @member PShape
|
rob@100
|
3520 * The addChild() adds a child to the PShape.
|
rob@100
|
3521 *
|
rob@100
|
3522 * @param {PShape} child the child to add
|
rob@100
|
3523 */
|
rob@100
|
3524 addChild: function( child ) {
|
rob@100
|
3525 this.children.push(child);
|
rob@100
|
3526 child.parent = this;
|
rob@100
|
3527 if (child.getName() !== null) {
|
rob@100
|
3528 this.addName(child.getName(), child);
|
rob@100
|
3529 }
|
rob@100
|
3530 },
|
rob@100
|
3531 /**
|
rob@100
|
3532 * @member PShape
|
rob@100
|
3533 * The addName() functions adds a shape to the name lookup table.
|
rob@100
|
3534 *
|
rob@100
|
3535 * @param {String} name the name to be added
|
rob@100
|
3536 * @param {PShape} shape the shape
|
rob@100
|
3537 */
|
rob@100
|
3538 addName: function(name, shape) {
|
rob@100
|
3539 if (this.parent !== null) {
|
rob@100
|
3540 this.parent.addName( name, shape );
|
rob@100
|
3541 } else {
|
rob@100
|
3542 this.nameTable.push( [name, shape] );
|
rob@100
|
3543 }
|
rob@100
|
3544 },
|
rob@100
|
3545 /**
|
rob@100
|
3546 * @member PShape
|
rob@100
|
3547 * The translate() function specifies an amount to displace the shape. The <b>x</b> parameter specifies left/right translation, the <b>y</b> parameter specifies up/down translation, and the <b>z</b> parameter specifies translations toward/away from the screen.
|
rob@100
|
3548 * Subsequent calls to the method accumulates the effect. For example, calling <b>translate(50, 0)</b> and then <b>translate(20, 0)</b> is the same as <b>translate(70, 0)</b>.
|
rob@100
|
3549 * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
|
rob@100
|
3550 * <br><br>Using this method with the <b>z</b> parameter requires using the P3D or OPENGL parameter in combination with size.
|
rob@100
|
3551 *
|
rob@100
|
3552 * @param {int|float} x left/right translation
|
rob@100
|
3553 * @param {int|float} y up/down translation
|
rob@100
|
3554 * @param {int|float} z forward/back translation
|
rob@100
|
3555 *
|
rob@100
|
3556 * @see PMatrix2D#translate
|
rob@100
|
3557 * @see PMatrix3D#translate
|
rob@100
|
3558 */
|
rob@100
|
3559 translate: function() {
|
rob@100
|
3560 if(arguments.length === 2)
|
rob@100
|
3561 {
|
rob@100
|
3562 this.checkMatrix(2);
|
rob@100
|
3563 this.matrix.translate(arguments[0], arguments[1]);
|
rob@100
|
3564 } else {
|
rob@100
|
3565 this.checkMatrix(3);
|
rob@100
|
3566 this.matrix.translate(arguments[0], arguments[1], 0);
|
rob@100
|
3567 }
|
rob@100
|
3568 },
|
rob@100
|
3569 /**
|
rob@100
|
3570 * @member PShape
|
rob@100
|
3571 * The checkMatrix() function makes sure that the shape's matrix is 1) not null, and 2) has a matrix
|
rob@100
|
3572 * that can handle <em>at least</em> the specified number of dimensions.
|
rob@100
|
3573 *
|
rob@100
|
3574 * @param {int} dimensions the specified number of dimensions
|
rob@100
|
3575 */
|
rob@100
|
3576 checkMatrix: function(dimensions) {
|
rob@100
|
3577 if(this.matrix === null) {
|
rob@100
|
3578 if(dimensions === 2) {
|
rob@100
|
3579 this.matrix = new PMatrix2D();
|
rob@100
|
3580 } else {
|
rob@100
|
3581 this.matrix = new PMatrix3D();
|
rob@100
|
3582 }
|
rob@100
|
3583 }else if(dimensions === 3 && this.matrix instanceof PMatrix2D) {
|
rob@100
|
3584 this.matrix = new PMatrix3D();
|
rob@100
|
3585 }
|
rob@100
|
3586 },
|
rob@100
|
3587 /**
|
rob@100
|
3588 * @member PShape
|
rob@100
|
3589 * The rotateX() function rotates a shape around the x-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
|
rob@100
|
3590 * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
|
rob@100
|
3591 * Subsequent calls to the method accumulates the effect. For example, calling <b>rotateX(HALF_PI)</b> and then <b>rotateX(HALF_PI)</b> is the same as <b>rotateX(PI)</b>.
|
rob@100
|
3592 * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
|
rob@100
|
3593 * <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
|
rob@100
|
3594 *
|
rob@100
|
3595 * @param {float}angle angle of rotation specified in radians
|
rob@100
|
3596 *
|
rob@100
|
3597 * @see PMatrix3D#rotateX
|
rob@100
|
3598 */
|
rob@100
|
3599 rotateX: function(angle) {
|
rob@100
|
3600 this.rotate(angle, 1, 0, 0);
|
rob@100
|
3601 },
|
rob@100
|
3602 /**
|
rob@100
|
3603 * @member PShape
|
rob@100
|
3604 * The rotateY() function rotates a shape around the y-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
|
rob@100
|
3605 * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
|
rob@100
|
3606 * Subsequent calls to the method accumulates the effect. For example, calling <b>rotateY(HALF_PI)</b> and then <b>rotateY(HALF_PI)</b> is the same as <b>rotateY(PI)</b>.
|
rob@100
|
3607 * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
|
rob@100
|
3608 * <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
|
rob@100
|
3609 *
|
rob@100
|
3610 * @param {float}angle angle of rotation specified in radians
|
rob@100
|
3611 *
|
rob@100
|
3612 * @see PMatrix3D#rotateY
|
rob@100
|
3613 */
|
rob@100
|
3614 rotateY: function(angle) {
|
rob@100
|
3615 this.rotate(angle, 0, 1, 0);
|
rob@100
|
3616 },
|
rob@100
|
3617 /**
|
rob@100
|
3618 * @member PShape
|
rob@100
|
3619 * The rotateZ() function rotates a shape around the z-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
|
rob@100
|
3620 * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
|
rob@100
|
3621 * Subsequent calls to the method accumulates the effect. For example, calling <b>rotateZ(HALF_PI)</b> and then <b>rotateZ(HALF_PI)</b> is the same as <b>rotateZ(PI)</b>.
|
rob@100
|
3622 * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
|
rob@100
|
3623 * <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
|
rob@100
|
3624 *
|
rob@100
|
3625 * @param {float}angle angle of rotation specified in radians
|
rob@100
|
3626 *
|
rob@100
|
3627 * @see PMatrix3D#rotateZ
|
rob@100
|
3628 */
|
rob@100
|
3629 rotateZ: function(angle) {
|
rob@100
|
3630 this.rotate(angle, 0, 0, 1);
|
rob@100
|
3631 },
|
rob@100
|
3632 /**
|
rob@100
|
3633 * @member PShape
|
rob@100
|
3634 * The rotate() function rotates a shape the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
|
rob@100
|
3635 * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
|
rob@100
|
3636 * Transformations apply to everything that happens after and subsequent calls to the method accumulates the effect.
|
rob@100
|
3637 * For example, calling <b>rotate(HALF_PI)</b> and then <b>rotate(HALF_PI)</b> is the same as <b>rotate(PI)</b>.
|
rob@100
|
3638 * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
|
rob@100
|
3639 * If optional parameters x,y,z are supplied, the rotate is about the point (x, y, z).
|
rob@100
|
3640 *
|
rob@100
|
3641 * @param {float}angle angle of rotation specified in radians
|
rob@100
|
3642 * @param {float}x x-coordinate of the point
|
rob@100
|
3643 * @param {float}y y-coordinate of the point
|
rob@100
|
3644 * @param {float}z z-coordinate of the point
|
rob@100
|
3645 * @see PMatrix2D#rotate
|
rob@100
|
3646 * @see PMatrix3D#rotate
|
rob@100
|
3647 */
|
rob@100
|
3648 rotate: function() {
|
rob@100
|
3649 if(arguments.length === 1){
|
rob@100
|
3650 this.checkMatrix(2);
|
rob@100
|
3651 this.matrix.rotate(arguments[0]);
|
rob@100
|
3652 } else {
|
rob@100
|
3653 this.checkMatrix(3);
|
rob@100
|
3654 this.matrix.rotate(arguments[0],
|
rob@100
|
3655 arguments[1],
|
rob@100
|
3656 arguments[2],
|
rob@100
|
3657 arguments[3]);
|
rob@100
|
3658 }
|
rob@100
|
3659 },
|
rob@100
|
3660 /**
|
rob@100
|
3661 * @member PShape
|
rob@100
|
3662 * The scale() function increases or decreases the size of a shape by expanding and contracting vertices. Shapes always scale from the relative origin of their bounding box.
|
rob@100
|
3663 * Scale values are specified as decimal percentages. For example, the method call <b>scale(2.0)</b> increases the dimension of a shape by 200%.
|
rob@100
|
3664 * Subsequent calls to the method multiply the effect. For example, calling <b>scale(2.0)</b> and then <b>scale(1.5)</b> is the same as <b>scale(3.0)</b>.
|
rob@100
|
3665 * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
|
rob@100
|
3666 * <br><br>Using this fuction with the <b>z</b> parameter requires passing P3D or OPENGL into the size() parameter.
|
rob@100
|
3667 *
|
rob@100
|
3668 * @param {float}s percentage to scale the object
|
rob@100
|
3669 * @param {float}x percentage to scale the object in the x-axis
|
rob@100
|
3670 * @param {float}y percentage to scale the object in the y-axis
|
rob@100
|
3671 * @param {float}z percentage to scale the object in the z-axis
|
rob@100
|
3672 *
|
rob@100
|
3673 * @see PMatrix2D#scale
|
rob@100
|
3674 * @see PMatrix3D#scale
|
rob@100
|
3675 */
|
rob@100
|
3676 scale: function() {
|
rob@100
|
3677 if(arguments.length === 2) {
|
rob@100
|
3678 this.checkMatrix(2);
|
rob@100
|
3679 this.matrix.scale(arguments[0], arguments[1]);
|
rob@100
|
3680 } else if (arguments.length === 3) {
|
rob@100
|
3681 this.checkMatrix(2);
|
rob@100
|
3682 this.matrix.scale(arguments[0], arguments[1], arguments[2]);
|
rob@100
|
3683 } else {
|
rob@100
|
3684 this.checkMatrix(2);
|
rob@100
|
3685 this.matrix.scale(arguments[0]);
|
rob@100
|
3686 }
|
rob@100
|
3687 },
|
rob@100
|
3688 /**
|
rob@100
|
3689 * @member PShape
|
rob@100
|
3690 * The resetMatrix() function resets the matrix
|
rob@100
|
3691 *
|
rob@100
|
3692 * @see PMatrix2D#reset
|
rob@100
|
3693 * @see PMatrix3D#reset
|
rob@100
|
3694 */
|
rob@100
|
3695 resetMatrix: function() {
|
rob@100
|
3696 this.checkMatrix(2);
|
rob@100
|
3697 this.matrix.reset();
|
rob@100
|
3698 },
|
rob@100
|
3699 /**
|
rob@100
|
3700 * @member PShape
|
rob@100
|
3701 * The applyMatrix() function multiplies this matrix by another matrix of type PMatrix3D or PMatrix2D.
|
rob@100
|
3702 * Individual elements can also be provided
|
rob@100
|
3703 *
|
rob@100
|
3704 * @param {PMatrix3D|PMatrix2D} matrix the matrix to multiply by
|
rob@100
|
3705 *
|
rob@100
|
3706 * @see PMatrix2D#apply
|
rob@100
|
3707 * @see PMatrix3D#apply
|
rob@100
|
3708 */
|
rob@100
|
3709 applyMatrix: function(matrix) {
|
rob@100
|
3710 if (arguments.length === 1) {
|
rob@100
|
3711 this.applyMatrix(matrix.elements[0],
|
rob@100
|
3712 matrix.elements[1], 0,
|
rob@100
|
3713 matrix.elements[2],
|
rob@100
|
3714 matrix.elements[3],
|
rob@100
|
3715 matrix.elements[4], 0,
|
rob@100
|
3716 matrix.elements[5],
|
rob@100
|
3717 0, 0, 1, 0,
|
rob@100
|
3718 0, 0, 0, 1);
|
rob@100
|
3719 } else if (arguments.length === 6) {
|
rob@100
|
3720 this.checkMatrix(2);
|
rob@100
|
3721 this.matrix.apply(arguments[0], arguments[1], arguments[2], 0,
|
rob@100
|
3722 arguments[3], arguments[4], arguments[5], 0,
|
rob@100
|
3723 0, 0, 1, 0,
|
rob@100
|
3724 0, 0, 0, 1);
|
rob@100
|
3725
|
rob@100
|
3726 } else if (arguments.length === 16) {
|
rob@100
|
3727 this.checkMatrix(3);
|
rob@100
|
3728 this.matrix.apply(arguments[0],
|
rob@100
|
3729 arguments[1],
|
rob@100
|
3730 arguments[2],
|
rob@100
|
3731 arguments[3],
|
rob@100
|
3732 arguments[4],
|
rob@100
|
3733 arguments[5],
|
rob@100
|
3734 arguments[6],
|
rob@100
|
3735 arguments[7],
|
rob@100
|
3736 arguments[8],
|
rob@100
|
3737 arguments[9],
|
rob@100
|
3738 arguments[10],
|
rob@100
|
3739 arguments[11],
|
rob@100
|
3740 arguments[12],
|
rob@100
|
3741 arguments[13],
|
rob@100
|
3742 arguments[14],
|
rob@100
|
3743 arguments[15]);
|
rob@100
|
3744 }
|
rob@100
|
3745 }
|
rob@100
|
3746 };
|
rob@100
|
3747
|
rob@100
|
3748 return PShape;
|
rob@100
|
3749 };
|
rob@100
|
3750 },{}],16:[function(require,module,exports){
|
rob@100
|
3751 /**
|
rob@100
|
3752 * SVG stands for Scalable Vector Graphics, a portable graphics format. It is
|
rob@100
|
3753 * a vector format so it allows for infinite resolution and relatively small
|
rob@100
|
3754 * file sizes. Most modern media software can view SVG files, including Adobe
|
rob@100
|
3755 * products, Firefox, etc. Illustrator and Inkscape can edit SVG files.
|
rob@100
|
3756 *
|
rob@100
|
3757 * @param {PApplet} parent typically use "this"
|
rob@100
|
3758 * @param {String} filename name of the SVG file to load
|
rob@100
|
3759 * @param {XMLElement} xml an XMLElement element
|
rob@100
|
3760 * @param {PShapeSVG} parent the parent PShapeSVG
|
rob@100
|
3761 *
|
rob@100
|
3762 * @see PShape
|
rob@100
|
3763 */
|
rob@100
|
3764 module.exports = function(options) {
|
rob@100
|
3765 var CommonFunctions = options.CommonFunctions,
|
rob@100
|
3766 PConstants = options.PConstants,
|
rob@100
|
3767 PShape = options.PShape,
|
rob@100
|
3768 XMLElement = options.XMLElement,
|
rob@100
|
3769 colors = options.colors;
|
rob@100
|
3770
|
rob@100
|
3771 var PShapeSVG = function() {
|
rob@100
|
3772 PShape.call(this); // PShape is the base class.
|
rob@100
|
3773 if (arguments.length === 1) { // xml element coming in
|
rob@100
|
3774 this.element = arguments[0];
|
rob@100
|
3775
|
rob@100
|
3776 // set values to their defaults according to the SVG spec
|
rob@100
|
3777 this.vertexCodes = [];
|
rob@100
|
3778 this.vertices = [];
|
rob@100
|
3779 this.opacity = 1;
|
rob@100
|
3780
|
rob@100
|
3781 this.stroke = false;
|
rob@100
|
3782 this.strokeColor = PConstants.ALPHA_MASK;
|
rob@100
|
3783 this.strokeWeight = 1;
|
rob@100
|
3784 this.strokeCap = PConstants.SQUARE; // BUTT in svg spec
|
rob@100
|
3785 this.strokeJoin = PConstants.MITER;
|
rob@100
|
3786 this.strokeGradient = null;
|
rob@100
|
3787 this.strokeGradientPaint = null;
|
rob@100
|
3788 this.strokeName = null;
|
rob@100
|
3789 this.strokeOpacity = 1;
|
rob@100
|
3790
|
rob@100
|
3791 this.fill = true;
|
rob@100
|
3792 this.fillColor = PConstants.ALPHA_MASK;
|
rob@100
|
3793 this.fillGradient = null;
|
rob@100
|
3794 this.fillGradientPaint = null;
|
rob@100
|
3795 this.fillName = null;
|
rob@100
|
3796 this.fillOpacity = 1;
|
rob@100
|
3797
|
rob@100
|
3798 if (this.element.getName() !== "svg") {
|
rob@100
|
3799 throw("root is not <svg>, it's <" + this.element.getName() + ">");
|
rob@100
|
3800 }
|
rob@100
|
3801 }
|
rob@100
|
3802 else if (arguments.length === 2) {
|
rob@100
|
3803 if (typeof arguments[1] === 'string') {
|
rob@100
|
3804 if (arguments[1].indexOf(".svg") > -1) { //its a filename
|
rob@100
|
3805 this.element = new XMLElement(true, arguments[1]);
|
rob@100
|
3806 // set values to their defaults according to the SVG spec
|
rob@100
|
3807 this.vertexCodes = [];
|
rob@100
|
3808 this.vertices = [];
|
rob@100
|
3809 this.opacity = 1;
|
rob@100
|
3810
|
rob@100
|
3811 this.stroke = false;
|
rob@100
|
3812 this.strokeColor = PConstants.ALPHA_MASK;
|
rob@100
|
3813 this.strokeWeight = 1;
|
rob@100
|
3814 this.strokeCap = PConstants.SQUARE; // BUTT in svg spec
|
rob@100
|
3815 this.strokeJoin = PConstants.MITER;
|
rob@100
|
3816 this.strokeGradient = "";
|
rob@100
|
3817 this.strokeGradientPaint = "";
|
rob@100
|
3818 this.strokeName = "";
|
rob@100
|
3819 this.strokeOpacity = 1;
|
rob@100
|
3820
|
rob@100
|
3821 this.fill = true;
|
rob@100
|
3822 this.fillColor = PConstants.ALPHA_MASK;
|
rob@100
|
3823 this.fillGradient = null;
|
rob@100
|
3824 this.fillGradientPaint = null;
|
rob@100
|
3825 this.fillOpacity = 1;
|
rob@100
|
3826
|
rob@100
|
3827 }
|
rob@100
|
3828 } else { // XMLElement
|
rob@100
|
3829 if (arguments[0]) { // PShapeSVG
|
rob@100
|
3830 this.element = arguments[1];
|
rob@100
|
3831 this.vertexCodes = arguments[0].vertexCodes.slice();
|
rob@100
|
3832 this.vertices = arguments[0].vertices.slice();
|
rob@100
|
3833
|
rob@100
|
3834 this.stroke = arguments[0].stroke;
|
rob@100
|
3835 this.strokeColor = arguments[0].strokeColor;
|
rob@100
|
3836 this.strokeWeight = arguments[0].strokeWeight;
|
rob@100
|
3837 this.strokeCap = arguments[0].strokeCap;
|
rob@100
|
3838 this.strokeJoin = arguments[0].strokeJoin;
|
rob@100
|
3839 this.strokeGradient = arguments[0].strokeGradient;
|
rob@100
|
3840 this.strokeGradientPaint = arguments[0].strokeGradientPaint;
|
rob@100
|
3841 this.strokeName = arguments[0].strokeName;
|
rob@100
|
3842
|
rob@100
|
3843 this.fill = arguments[0].fill;
|
rob@100
|
3844 this.fillColor = arguments[0].fillColor;
|
rob@100
|
3845 this.fillGradient = arguments[0].fillGradient;
|
rob@100
|
3846 this.fillGradientPaint = arguments[0].fillGradientPaint;
|
rob@100
|
3847 this.fillName = arguments[0].fillName;
|
rob@100
|
3848 this.strokeOpacity = arguments[0].strokeOpacity;
|
rob@100
|
3849 this.fillOpacity = arguments[0].fillOpacity;
|
rob@100
|
3850 this.opacity = arguments[0].opacity;
|
rob@100
|
3851 }
|
rob@100
|
3852 }
|
rob@100
|
3853 }
|
rob@100
|
3854
|
rob@100
|
3855 this.name = this.element.getStringAttribute("id");
|
rob@100
|
3856 var displayStr = this.element.getStringAttribute("display", "inline");
|
rob@100
|
3857 this.visible = displayStr !== "none";
|
rob@100
|
3858 var str = this.element.getAttribute("transform");
|
rob@100
|
3859 if (str) {
|
rob@100
|
3860 this.matrix = this.parseMatrix(str);
|
rob@100
|
3861 }
|
rob@100
|
3862 // not proper parsing of the viewBox, but will cover us for cases where
|
rob@100
|
3863 // the width and height of the object is not specified
|
rob@100
|
3864 var viewBoxStr = this.element.getStringAttribute("viewBox");
|
rob@100
|
3865 if ( viewBoxStr !== null ) {
|
rob@100
|
3866 var viewBox = viewBoxStr.split(" ");
|
rob@100
|
3867 this.width = viewBox[2];
|
rob@100
|
3868 this.height = viewBox[3];
|
rob@100
|
3869 }
|
rob@100
|
3870
|
rob@100
|
3871 // TODO if viewbox is not same as width/height, then use it to scale
|
rob@100
|
3872 // the original objects. for now, viewbox only used when width/height
|
rob@100
|
3873 // are empty values (which by the spec means w/h of "100%"
|
rob@100
|
3874 var unitWidth = this.element.getStringAttribute("width");
|
rob@100
|
3875 var unitHeight = this.element.getStringAttribute("height");
|
rob@100
|
3876 if (unitWidth !== null) {
|
rob@100
|
3877 this.width = this.parseUnitSize(unitWidth);
|
rob@100
|
3878 this.height = this.parseUnitSize(unitHeight);
|
rob@100
|
3879 } else {
|
rob@100
|
3880 if ((this.width === 0) || (this.height === 0)) {
|
rob@100
|
3881 // For the spec, the default is 100% and 100%. For purposes
|
rob@100
|
3882 // here, insert a dummy value because this is prolly just a
|
rob@100
|
3883 // font or something for which the w/h doesn't matter.
|
rob@100
|
3884 this.width = 1;
|
rob@100
|
3885 this.height = 1;
|
rob@100
|
3886
|
rob@100
|
3887 //show warning
|
rob@100
|
3888 throw("The width and/or height is not " +
|
rob@100
|
3889 "readable in the <svg> tag of this file.");
|
rob@100
|
3890 }
|
rob@100
|
3891 }
|
rob@100
|
3892 this.parseColors(this.element);
|
rob@100
|
3893 this.parseChildren(this.element);
|
rob@100
|
3894
|
rob@100
|
3895 };
|
rob@100
|
3896 /**
|
rob@100
|
3897 * PShapeSVG methods
|
rob@100
|
3898 * missing: getChild(), print(), parseStyleAttributes(), styles() - deals with strokeGradient and fillGradient
|
rob@100
|
3899 */
|
rob@100
|
3900 PShapeSVG.prototype = new PShape();
|
rob@100
|
3901 /**
|
rob@100
|
3902 * @member PShapeSVG
|
rob@100
|
3903 * The parseMatrix() function parses the specified SVG matrix into a PMatrix2D. Note that PMatrix2D
|
rob@100
|
3904 * is rotated relative to the SVG definition, so parameters are rearranged
|
rob@100
|
3905 * here. More about the transformation matrices in
|
rob@100
|
3906 * <a href="http://www.w3.org/TR/SVG/coords.html#TransformAttribute">this section</a>
|
rob@100
|
3907 * of the SVG documentation.
|
rob@100
|
3908 *
|
rob@100
|
3909 * @param {String} str text of the matrix param.
|
rob@100
|
3910 *
|
rob@100
|
3911 * @return {PMatrix2D} a PMatrix2D
|
rob@100
|
3912 */
|
rob@100
|
3913 PShapeSVG.prototype.parseMatrix = (function() {
|
rob@100
|
3914 function getCoords(s) {
|
rob@100
|
3915 var m = [];
|
rob@100
|
3916 s.replace(/\((.*?)\)/, (function() {
|
rob@100
|
3917 return function(all, params) {
|
rob@100
|
3918 // get the coordinates that can be separated by spaces or a comma
|
rob@100
|
3919 m = params.replace(/,+/g, " ").split(/\s+/);
|
rob@100
|
3920 };
|
rob@100
|
3921 }()));
|
rob@100
|
3922 return m;
|
rob@100
|
3923 }
|
rob@100
|
3924
|
rob@100
|
3925 return function(str) {
|
rob@100
|
3926 this.checkMatrix(2);
|
rob@100
|
3927 var pieces = [];
|
rob@100
|
3928 str.replace(/\s*(\w+)\((.*?)\)/g, function(all) {
|
rob@100
|
3929 // get a list of transform definitions
|
rob@100
|
3930 pieces.push(CommonFunctions.trim(all));
|
rob@100
|
3931 });
|
rob@100
|
3932 if (pieces.length === 0) {
|
rob@100
|
3933 return null;
|
rob@100
|
3934 }
|
rob@100
|
3935
|
rob@100
|
3936 for (var i = 0, j = pieces.length; i < j; i++) {
|
rob@100
|
3937 var m = getCoords(pieces[i]);
|
rob@100
|
3938
|
rob@100
|
3939 if (pieces[i].indexOf("matrix") !== -1) {
|
rob@100
|
3940 this.matrix.set(m[0], m[2], m[4], m[1], m[3], m[5]);
|
rob@100
|
3941 } else if (pieces[i].indexOf("translate") !== -1) {
|
rob@100
|
3942 var tx = m[0];
|
rob@100
|
3943 var ty = (m.length === 2) ? m[1] : 0;
|
rob@100
|
3944 this.matrix.translate(tx,ty);
|
rob@100
|
3945 } else if (pieces[i].indexOf("scale") !== -1) {
|
rob@100
|
3946 var sx = m[0];
|
rob@100
|
3947 var sy = (m.length === 2) ? m[1] : m[0];
|
rob@100
|
3948 this.matrix.scale(sx,sy);
|
rob@100
|
3949 } else if (pieces[i].indexOf("rotate") !== -1) {
|
rob@100
|
3950 var angle = m[0];
|
rob@100
|
3951 if (m.length === 1) {
|
rob@100
|
3952 this.matrix.rotate(CommonFunctions.radians(angle));
|
rob@100
|
3953 } else if (m.length === 3) {
|
rob@100
|
3954 this.matrix.translate(m[1], m[2]);
|
rob@100
|
3955 this.matrix.rotate(CommonFunctions.radians(m[0]));
|
rob@100
|
3956 this.matrix.translate(-m[1], -m[2]);
|
rob@100
|
3957 }
|
rob@100
|
3958 } else if (pieces[i].indexOf("skewX") !== -1) {
|
rob@100
|
3959 this.matrix.skewX(parseFloat(m[0]));
|
rob@100
|
3960 } else if (pieces[i].indexOf("skewY") !== -1) {
|
rob@100
|
3961 this.matrix.skewY(m[0]);
|
rob@100
|
3962 } else if (pieces[i].indexOf("shearX") !== -1) {
|
rob@100
|
3963 this.matrix.shearX(m[0]);
|
rob@100
|
3964 } else if (pieces[i].indexOf("shearY") !== -1) {
|
rob@100
|
3965 this.matrix.shearY(m[0]);
|
rob@100
|
3966 }
|
rob@100
|
3967 }
|
rob@100
|
3968 return this.matrix;
|
rob@100
|
3969 };
|
rob@100
|
3970 }());
|
rob@100
|
3971
|
rob@100
|
3972 /**
|
rob@100
|
3973 * @member PShapeSVG
|
rob@100
|
3974 * The parseChildren() function parses the specified XMLElement
|
rob@100
|
3975 *
|
rob@100
|
3976 * @param {XMLElement}element the XMLElement to parse
|
rob@100
|
3977 */
|
rob@100
|
3978 PShapeSVG.prototype.parseChildren = function(element) {
|
rob@100
|
3979 var newelement = element.getChildren();
|
rob@100
|
3980 var base = new PShape();
|
rob@100
|
3981 var i, j;
|
rob@100
|
3982 for (i = 0, j = newelement.length; i < j; i++) {
|
rob@100
|
3983 var kid = this.parseChild(newelement[i]);
|
rob@100
|
3984 if (kid) {
|
rob@100
|
3985 base.addChild(kid);
|
rob@100
|
3986 }
|
rob@100
|
3987 }
|
rob@100
|
3988 for (i = 0, j = base.children.length; i < j; i++) {
|
rob@100
|
3989 this.children.push(base.children[i]);
|
rob@100
|
3990 }
|
rob@100
|
3991 };
|
rob@100
|
3992 /**
|
rob@100
|
3993 * @member PShapeSVG
|
rob@100
|
3994 * The getName() function returns the name
|
rob@100
|
3995 *
|
rob@100
|
3996 * @return {String} the name
|
rob@100
|
3997 */
|
rob@100
|
3998 PShapeSVG.prototype.getName = function() {
|
rob@100
|
3999 return this.name;
|
rob@100
|
4000 };
|
rob@100
|
4001 /**
|
rob@100
|
4002 * @member PShapeSVG
|
rob@100
|
4003 * The parseChild() function parses a child XML element.
|
rob@100
|
4004 *
|
rob@100
|
4005 * @param {XMLElement} elem the element to parse
|
rob@100
|
4006 *
|
rob@100
|
4007 * @return {PShape} the newly created PShape
|
rob@100
|
4008 */
|
rob@100
|
4009 PShapeSVG.prototype.parseChild = function( elem ) {
|
rob@100
|
4010 var name = elem.getName();
|
rob@100
|
4011 var shape;
|
rob@100
|
4012 if (name === "g") {
|
rob@100
|
4013 shape = new PShapeSVG(this, elem);
|
rob@100
|
4014 } else if (name === "defs") {
|
rob@100
|
4015 // generally this will contain gradient info, so may
|
rob@100
|
4016 // as well just throw it into a group element for parsing
|
rob@100
|
4017 shape = new PShapeSVG(this, elem);
|
rob@100
|
4018 } else if (name === "line") {
|
rob@100
|
4019 shape = new PShapeSVG(this, elem);
|
rob@100
|
4020 shape.parseLine();
|
rob@100
|
4021 } else if (name === "circle") {
|
rob@100
|
4022 shape = new PShapeSVG(this, elem);
|
rob@100
|
4023 shape.parseEllipse(true);
|
rob@100
|
4024 } else if (name === "ellipse") {
|
rob@100
|
4025 shape = new PShapeSVG(this, elem);
|
rob@100
|
4026 shape.parseEllipse(false);
|
rob@100
|
4027 } else if (name === "rect") {
|
rob@100
|
4028 shape = new PShapeSVG(this, elem);
|
rob@100
|
4029 shape.parseRect();
|
rob@100
|
4030 } else if (name === "polygon") {
|
rob@100
|
4031 shape = new PShapeSVG(this, elem);
|
rob@100
|
4032 shape.parsePoly(true);
|
rob@100
|
4033 } else if (name === "polyline") {
|
rob@100
|
4034 shape = new PShapeSVG(this, elem);
|
rob@100
|
4035 shape.parsePoly(false);
|
rob@100
|
4036 } else if (name === "path") {
|
rob@100
|
4037 shape = new PShapeSVG(this, elem);
|
rob@100
|
4038 shape.parsePath();
|
rob@100
|
4039 } else if (name === "radialGradient") {
|
rob@100
|
4040 //return new RadialGradient(this, elem);
|
rob@100
|
4041 unimplemented('PShapeSVG.prototype.parseChild, name = radialGradient');
|
rob@100
|
4042 } else if (name === "linearGradient") {
|
rob@100
|
4043 //return new LinearGradient(this, elem);
|
rob@100
|
4044 unimplemented('PShapeSVG.prototype.parseChild, name = linearGradient');
|
rob@100
|
4045 } else if (name === "text") {
|
rob@100
|
4046 unimplemented('PShapeSVG.prototype.parseChild, name = text');
|
rob@100
|
4047 } else if (name === "filter") {
|
rob@100
|
4048 unimplemented('PShapeSVG.prototype.parseChild, name = filter');
|
rob@100
|
4049 } else if (name === "mask") {
|
rob@100
|
4050 unimplemented('PShapeSVG.prototype.parseChild, name = mask');
|
rob@100
|
4051 } else {
|
rob@100
|
4052 // ignoring
|
rob@100
|
4053 }
|
rob@100
|
4054 return shape;
|
rob@100
|
4055 };
|
rob@100
|
4056 /**
|
rob@100
|
4057 * @member PShapeSVG
|
rob@100
|
4058 * The parsePath() function parses the <path> element of the svg file
|
rob@100
|
4059 * A path is defined by including a path element which contains a d="(path data)" attribute, where the d attribute contains
|
rob@100
|
4060 * the moveto, line, curve (both cubic and quadratic Beziers), arc and closepath instructions.
|
rob@100
|
4061 **/
|
rob@100
|
4062 PShapeSVG.prototype.parsePath = function() {
|
rob@100
|
4063 this.family = PConstants.PATH;
|
rob@100
|
4064 this.kind = 0;
|
rob@100
|
4065 var pathDataChars = [];
|
rob@100
|
4066 var c;
|
rob@100
|
4067 //change multiple spaces and commas to single space
|
rob@100
|
4068 var pathData = CommonFunctions.trim(this.element.getStringAttribute("d").replace(/[\s,]+/g,' '));
|
rob@100
|
4069 if (pathData === null) {
|
rob@100
|
4070 return;
|
rob@100
|
4071 }
|
rob@100
|
4072 pathData = pathData.split('');
|
rob@100
|
4073 var cx = 0,
|
rob@100
|
4074 cy = 0,
|
rob@100
|
4075 ctrlX = 0,
|
rob@100
|
4076 ctrlY = 0,
|
rob@100
|
4077 ctrlX1 = 0,
|
rob@100
|
4078 ctrlX2 = 0,
|
rob@100
|
4079 ctrlY1 = 0,
|
rob@100
|
4080 ctrlY2 = 0,
|
rob@100
|
4081 endX = 0,
|
rob@100
|
4082 endY = 0,
|
rob@100
|
4083 ppx = 0,
|
rob@100
|
4084 ppy = 0,
|
rob@100
|
4085 px = 0,
|
rob@100
|
4086 py = 0,
|
rob@100
|
4087 i = 0,
|
rob@100
|
4088 valOf = 0;
|
rob@100
|
4089 var str = "";
|
rob@100
|
4090 var tmpArray = [];
|
rob@100
|
4091 var flag = false;
|
rob@100
|
4092 var lastInstruction;
|
rob@100
|
4093 var command;
|
rob@100
|
4094 var j, k;
|
rob@100
|
4095 while (i< pathData.length) {
|
rob@100
|
4096 valOf = pathData[i].charCodeAt(0);
|
rob@100
|
4097 if ((valOf >= 65 && valOf <= 90) || (valOf >= 97 && valOf <= 122)) {
|
rob@100
|
4098 // if it's a letter
|
rob@100
|
4099 // populate the tmpArray with coordinates
|
rob@100
|
4100 j = i;
|
rob@100
|
4101 i++;
|
rob@100
|
4102 if (i < pathData.length) { // don't go over boundary of array
|
rob@100
|
4103 tmpArray = [];
|
rob@100
|
4104 valOf = pathData[i].charCodeAt(0);
|
rob@100
|
4105 while (!((valOf >= 65 && valOf <= 90) ||
|
rob@100
|
4106 (valOf >= 97 && valOf <= 100) ||
|
rob@100
|
4107 (valOf >= 102 && valOf <= 122)) && flag === false) { // if its NOT a letter
|
rob@100
|
4108 if (valOf === 32) { //if its a space and the str isn't empty
|
rob@100
|
4109 // sometimes you get a space after the letter
|
rob@100
|
4110 if (str !== "") {
|
rob@100
|
4111 tmpArray.push(parseFloat(str));
|
rob@100
|
4112 str = "";
|
rob@100
|
4113 }
|
rob@100
|
4114 i++;
|
rob@100
|
4115 } else if (valOf === 45) { //if it's a -
|
rob@100
|
4116 // allow for 'e' notation in numbers, e.g. 2.10e-9
|
rob@100
|
4117 if (pathData[i-1].charCodeAt(0) === 101) {
|
rob@100
|
4118 str += pathData[i].toString();
|
rob@100
|
4119 i++;
|
rob@100
|
4120 } else {
|
rob@100
|
4121 // sometimes no space separator after (ex: 104.535-16.322)
|
rob@100
|
4122 if (str !== "") {
|
rob@100
|
4123 tmpArray.push(parseFloat(str));
|
rob@100
|
4124 }
|
rob@100
|
4125 str = pathData[i].toString();
|
rob@100
|
4126 i++;
|
rob@100
|
4127 }
|
rob@100
|
4128 } else {
|
rob@100
|
4129 str += pathData[i].toString();
|
rob@100
|
4130 i++;
|
rob@100
|
4131 }
|
rob@100
|
4132 if (i === pathData.length) { // don't go over boundary of array
|
rob@100
|
4133 flag = true;
|
rob@100
|
4134 } else {
|
rob@100
|
4135 valOf = pathData[i].charCodeAt(0);
|
rob@100
|
4136 }
|
rob@100
|
4137 }
|
rob@100
|
4138 }
|
rob@100
|
4139 if (str !== "") {
|
rob@100
|
4140 tmpArray.push(parseFloat(str));
|
rob@100
|
4141 str = "";
|
rob@100
|
4142 }
|
rob@100
|
4143 command = pathData[j];
|
rob@100
|
4144 valOf = command.charCodeAt(0);
|
rob@100
|
4145 if (valOf === 77) { // M - move to (absolute)
|
rob@100
|
4146 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
|
rob@100
|
4147 // need one+ pairs of co-ordinates
|
rob@100
|
4148 cx = tmpArray[0];
|
rob@100
|
4149 cy = tmpArray[1];
|
rob@100
|
4150 this.parsePathMoveto(cx, cy);
|
rob@100
|
4151 if (tmpArray.length > 2) {
|
rob@100
|
4152 for (j = 2, k = tmpArray.length; j < k; j+=2) {
|
rob@100
|
4153 // absolute line to
|
rob@100
|
4154 cx = tmpArray[j];
|
rob@100
|
4155 cy = tmpArray[j+1];
|
rob@100
|
4156 this.parsePathLineto(cx,cy);
|
rob@100
|
4157 }
|
rob@100
|
4158 }
|
rob@100
|
4159 }
|
rob@100
|
4160 } else if (valOf === 109) { // m - move to (relative)
|
rob@100
|
4161 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
|
rob@100
|
4162 // need one+ pairs of co-ordinates
|
rob@100
|
4163 cx += tmpArray[0];
|
rob@100
|
4164 cy += tmpArray[1];
|
rob@100
|
4165 this.parsePathMoveto(cx,cy);
|
rob@100
|
4166 if (tmpArray.length > 2) {
|
rob@100
|
4167 for (j = 2, k = tmpArray.length; j < k; j+=2) {
|
rob@100
|
4168 // relative line to
|
rob@100
|
4169 cx += tmpArray[j];
|
rob@100
|
4170 cy += tmpArray[j + 1];
|
rob@100
|
4171 this.parsePathLineto(cx,cy);
|
rob@100
|
4172 }
|
rob@100
|
4173 }
|
rob@100
|
4174 }
|
rob@100
|
4175 } else if (valOf === 76) { // L - lineto (absolute)
|
rob@100
|
4176 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
|
rob@100
|
4177 // need one+ pairs of co-ordinates
|
rob@100
|
4178 for (j = 0, k = tmpArray.length; j < k; j+=2) {
|
rob@100
|
4179 cx = tmpArray[j];
|
rob@100
|
4180 cy = tmpArray[j + 1];
|
rob@100
|
4181 this.parsePathLineto(cx,cy);
|
rob@100
|
4182 }
|
rob@100
|
4183 }
|
rob@100
|
4184 } else if (valOf === 108) { // l - lineto (relative)
|
rob@100
|
4185 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
|
rob@100
|
4186 // need one+ pairs of co-ordinates
|
rob@100
|
4187 for (j = 0, k = tmpArray.length; j < k; j+=2) {
|
rob@100
|
4188 cx += tmpArray[j];
|
rob@100
|
4189 cy += tmpArray[j+1];
|
rob@100
|
4190 this.parsePathLineto(cx,cy);
|
rob@100
|
4191 }
|
rob@100
|
4192 }
|
rob@100
|
4193 } else if (valOf === 72) { // H - horizontal lineto (absolute)
|
rob@100
|
4194 for (j = 0, k = tmpArray.length; j < k; j++) {
|
rob@100
|
4195 // multiple x co-ordinates can be provided
|
rob@100
|
4196 cx = tmpArray[j];
|
rob@100
|
4197 this.parsePathLineto(cx, cy);
|
rob@100
|
4198 }
|
rob@100
|
4199 } else if (valOf === 104) { // h - horizontal lineto (relative)
|
rob@100
|
4200 for (j = 0, k = tmpArray.length; j < k; j++) {
|
rob@100
|
4201 // multiple x co-ordinates can be provided
|
rob@100
|
4202 cx += tmpArray[j];
|
rob@100
|
4203 this.parsePathLineto(cx, cy);
|
rob@100
|
4204 }
|
rob@100
|
4205 } else if (valOf === 86) { // V - vertical lineto (absolute)
|
rob@100
|
4206 for (j = 0, k = tmpArray.length; j < k; j++) {
|
rob@100
|
4207 // multiple y co-ordinates can be provided
|
rob@100
|
4208 cy = tmpArray[j];
|
rob@100
|
4209 this.parsePathLineto(cx, cy);
|
rob@100
|
4210 }
|
rob@100
|
4211 } else if (valOf === 118) { // v - vertical lineto (relative)
|
rob@100
|
4212 for (j = 0, k = tmpArray.length; j < k; j++) {
|
rob@100
|
4213 // multiple y co-ordinates can be provided
|
rob@100
|
4214 cy += tmpArray[j];
|
rob@100
|
4215 this.parsePathLineto(cx, cy);
|
rob@100
|
4216 }
|
rob@100
|
4217 } else if (valOf === 67) { // C - curve to (absolute)
|
rob@100
|
4218 if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) {
|
rob@100
|
4219 // need one+ multiples of 6 co-ordinates
|
rob@100
|
4220 for (j = 0, k = tmpArray.length; j < k; j+=6) {
|
rob@100
|
4221 ctrlX1 = tmpArray[j];
|
rob@100
|
4222 ctrlY1 = tmpArray[j + 1];
|
rob@100
|
4223 ctrlX2 = tmpArray[j + 2];
|
rob@100
|
4224 ctrlY2 = tmpArray[j + 3];
|
rob@100
|
4225 endX = tmpArray[j + 4];
|
rob@100
|
4226 endY = tmpArray[j + 5];
|
rob@100
|
4227 this.parsePathCurveto(ctrlX1,
|
rob@100
|
4228 ctrlY1,
|
rob@100
|
4229 ctrlX2,
|
rob@100
|
4230 ctrlY2,
|
rob@100
|
4231 endX,
|
rob@100
|
4232 endY);
|
rob@100
|
4233 cx = endX;
|
rob@100
|
4234 cy = endY;
|
rob@100
|
4235 }
|
rob@100
|
4236 }
|
rob@100
|
4237 } else if (valOf === 99) { // c - curve to (relative)
|
rob@100
|
4238 if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) {
|
rob@100
|
4239 // need one+ multiples of 6 co-ordinates
|
rob@100
|
4240 for (j = 0, k = tmpArray.length; j < k; j+=6) {
|
rob@100
|
4241 ctrlX1 = cx + tmpArray[j];
|
rob@100
|
4242 ctrlY1 = cy + tmpArray[j + 1];
|
rob@100
|
4243 ctrlX2 = cx + tmpArray[j + 2];
|
rob@100
|
4244 ctrlY2 = cy + tmpArray[j + 3];
|
rob@100
|
4245 endX = cx + tmpArray[j + 4];
|
rob@100
|
4246 endY = cy + tmpArray[j + 5];
|
rob@100
|
4247 this.parsePathCurveto(ctrlX1,
|
rob@100
|
4248 ctrlY1,
|
rob@100
|
4249 ctrlX2,
|
rob@100
|
4250 ctrlY2,
|
rob@100
|
4251 endX,
|
rob@100
|
4252 endY);
|
rob@100
|
4253 cx = endX;
|
rob@100
|
4254 cy = endY;
|
rob@100
|
4255 }
|
rob@100
|
4256 }
|
rob@100
|
4257 } else if (valOf === 83) { // S - curve to shorthand (absolute)
|
rob@100
|
4258 if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
|
rob@100
|
4259 // need one+ multiples of 4 co-ordinates
|
rob@100
|
4260 for (j = 0, k = tmpArray.length; j < k; j+=4) {
|
rob@100
|
4261 if (lastInstruction.toLowerCase() === "c" ||
|
rob@100
|
4262 lastInstruction.toLowerCase() === "s") {
|
rob@100
|
4263 ppx = this.vertices[ this.vertices.length-2 ][0];
|
rob@100
|
4264 ppy = this.vertices[ this.vertices.length-2 ][1];
|
rob@100
|
4265 px = this.vertices[ this.vertices.length-1 ][0];
|
rob@100
|
4266 py = this.vertices[ this.vertices.length-1 ][1];
|
rob@100
|
4267 ctrlX1 = px + (px - ppx);
|
rob@100
|
4268 ctrlY1 = py + (py - ppy);
|
rob@100
|
4269 } else {
|
rob@100
|
4270 //If there is no previous curve,
|
rob@100
|
4271 //the current point will be used as the first control point.
|
rob@100
|
4272 ctrlX1 = this.vertices[this.vertices.length-1][0];
|
rob@100
|
4273 ctrlY1 = this.vertices[this.vertices.length-1][1];
|
rob@100
|
4274 }
|
rob@100
|
4275 ctrlX2 = tmpArray[j];
|
rob@100
|
4276 ctrlY2 = tmpArray[j + 1];
|
rob@100
|
4277 endX = tmpArray[j + 2];
|
rob@100
|
4278 endY = tmpArray[j + 3];
|
rob@100
|
4279 this.parsePathCurveto(ctrlX1,
|
rob@100
|
4280 ctrlY1,
|
rob@100
|
4281 ctrlX2,
|
rob@100
|
4282 ctrlY2,
|
rob@100
|
4283 endX,
|
rob@100
|
4284 endY);
|
rob@100
|
4285 cx = endX;
|
rob@100
|
4286 cy = endY;
|
rob@100
|
4287 }
|
rob@100
|
4288 }
|
rob@100
|
4289 } else if (valOf === 115) { // s - curve to shorthand (relative)
|
rob@100
|
4290 if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
|
rob@100
|
4291 // need one+ multiples of 4 co-ordinates
|
rob@100
|
4292 for (j = 0, k = tmpArray.length; j < k; j+=4) {
|
rob@100
|
4293 if (lastInstruction.toLowerCase() === "c" ||
|
rob@100
|
4294 lastInstruction.toLowerCase() === "s") {
|
rob@100
|
4295 ppx = this.vertices[this.vertices.length-2][0];
|
rob@100
|
4296 ppy = this.vertices[this.vertices.length-2][1];
|
rob@100
|
4297 px = this.vertices[this.vertices.length-1][0];
|
rob@100
|
4298 py = this.vertices[this.vertices.length-1][1];
|
rob@100
|
4299 ctrlX1 = px + (px - ppx);
|
rob@100
|
4300 ctrlY1 = py + (py - ppy);
|
rob@100
|
4301 } else {
|
rob@100
|
4302 //If there is no previous curve,
|
rob@100
|
4303 //the current point will be used as the first control point.
|
rob@100
|
4304 ctrlX1 = this.vertices[this.vertices.length-1][0];
|
rob@100
|
4305 ctrlY1 = this.vertices[this.vertices.length-1][1];
|
rob@100
|
4306 }
|
rob@100
|
4307 ctrlX2 = cx + tmpArray[j];
|
rob@100
|
4308 ctrlY2 = cy + tmpArray[j + 1];
|
rob@100
|
4309 endX = cx + tmpArray[j + 2];
|
rob@100
|
4310 endY = cy + tmpArray[j + 3];
|
rob@100
|
4311 this.parsePathCurveto(ctrlX1,
|
rob@100
|
4312 ctrlY1,
|
rob@100
|
4313 ctrlX2,
|
rob@100
|
4314 ctrlY2,
|
rob@100
|
4315 endX,
|
rob@100
|
4316 endY);
|
rob@100
|
4317 cx = endX;
|
rob@100
|
4318 cy = endY;
|
rob@100
|
4319 }
|
rob@100
|
4320 }
|
rob@100
|
4321 } else if (valOf === 81) { // Q - quadratic curve to (absolute)
|
rob@100
|
4322 if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
|
rob@100
|
4323 // need one+ multiples of 4 co-ordinates
|
rob@100
|
4324 for (j = 0, k = tmpArray.length; j < k; j+=4) {
|
rob@100
|
4325 ctrlX = tmpArray[j];
|
rob@100
|
4326 ctrlY = tmpArray[j + 1];
|
rob@100
|
4327 endX = tmpArray[j + 2];
|
rob@100
|
4328 endY = tmpArray[j + 3];
|
rob@100
|
4329 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
|
rob@100
|
4330 cx = endX;
|
rob@100
|
4331 cy = endY;
|
rob@100
|
4332 }
|
rob@100
|
4333 }
|
rob@100
|
4334 } else if (valOf === 113) { // q - quadratic curve to (relative)
|
rob@100
|
4335 if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
|
rob@100
|
4336 // need one+ multiples of 4 co-ordinates
|
rob@100
|
4337 for (j = 0, k = tmpArray.length; j < k; j+=4) {
|
rob@100
|
4338 ctrlX = cx + tmpArray[j];
|
rob@100
|
4339 ctrlY = cy + tmpArray[j + 1];
|
rob@100
|
4340 endX = cx + tmpArray[j + 2];
|
rob@100
|
4341 endY = cy + tmpArray[j + 3];
|
rob@100
|
4342 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
|
rob@100
|
4343 cx = endX;
|
rob@100
|
4344 cy = endY;
|
rob@100
|
4345 }
|
rob@100
|
4346 }
|
rob@100
|
4347 } else if (valOf === 84) {
|
rob@100
|
4348 // T - quadratic curve to shorthand (absolute)
|
rob@100
|
4349 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
|
rob@100
|
4350 // need one+ pairs of co-ordinates
|
rob@100
|
4351 for (j = 0, k = tmpArray.length; j < k; j+=2) {
|
rob@100
|
4352 if (lastInstruction.toLowerCase() === "q" ||
|
rob@100
|
4353 lastInstruction.toLowerCase() === "t") {
|
rob@100
|
4354 ppx = this.vertices[this.vertices.length-2][0];
|
rob@100
|
4355 ppy = this.vertices[this.vertices.length-2][1];
|
rob@100
|
4356 px = this.vertices[this.vertices.length-1][0];
|
rob@100
|
4357 py = this.vertices[this.vertices.length-1][1];
|
rob@100
|
4358 ctrlX = px + (px - ppx);
|
rob@100
|
4359 ctrlY = py + (py - ppy);
|
rob@100
|
4360 } else {
|
rob@100
|
4361 // If there is no previous command or if the previous command
|
rob@100
|
4362 // was not a Q, q, T or t, assume the control point is
|
rob@100
|
4363 // coincident with the current point.
|
rob@100
|
4364 ctrlX = cx;
|
rob@100
|
4365 ctrlY = cy;
|
rob@100
|
4366 }
|
rob@100
|
4367 endX = tmpArray[j];
|
rob@100
|
4368 endY = tmpArray[j + 1];
|
rob@100
|
4369 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
|
rob@100
|
4370 cx = endX;
|
rob@100
|
4371 cy = endY;
|
rob@100
|
4372 }
|
rob@100
|
4373 }
|
rob@100
|
4374 } else if (valOf === 116) {
|
rob@100
|
4375 // t - quadratic curve to shorthand (relative)
|
rob@100
|
4376 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
|
rob@100
|
4377 // need one+ pairs of co-ordinates
|
rob@100
|
4378 for (j = 0, k = tmpArray.length; j < k; j+=2) {
|
rob@100
|
4379 if (lastInstruction.toLowerCase() === "q" ||
|
rob@100
|
4380 lastInstruction.toLowerCase() === "t") {
|
rob@100
|
4381 ppx = this.vertices[this.vertices.length-2][0];
|
rob@100
|
4382 ppy = this.vertices[this.vertices.length-2][1];
|
rob@100
|
4383 px = this.vertices[this.vertices.length-1][0];
|
rob@100
|
4384 py = this.vertices[this.vertices.length-1][1];
|
rob@100
|
4385 ctrlX = px + (px - ppx);
|
rob@100
|
4386 ctrlY = py + (py - ppy);
|
rob@100
|
4387 } else {
|
rob@100
|
4388 // If there is no previous command or if the previous command
|
rob@100
|
4389 // was not a Q, q, T or t, assume the control point is
|
rob@100
|
4390 // coincident with the current point.
|
rob@100
|
4391 ctrlX = cx;
|
rob@100
|
4392 ctrlY = cy;
|
rob@100
|
4393 }
|
rob@100
|
4394 endX = cx + tmpArray[j];
|
rob@100
|
4395 endY = cy + tmpArray[j + 1];
|
rob@100
|
4396 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
|
rob@100
|
4397 cx = endX;
|
rob@100
|
4398 cy = endY;
|
rob@100
|
4399 }
|
rob@100
|
4400 }
|
rob@100
|
4401 } else if (valOf === 90 || valOf === 122) { // Z or z (these do the same thing)
|
rob@100
|
4402 this.close = true;
|
rob@100
|
4403 }
|
rob@100
|
4404 lastInstruction = command.toString();
|
rob@100
|
4405 } else { i++;}
|
rob@100
|
4406 }
|
rob@100
|
4407 };
|
rob@100
|
4408 /**
|
rob@100
|
4409 * @member PShapeSVG
|
rob@100
|
4410 * PShapeSVG.parsePath() helper function
|
rob@100
|
4411 *
|
rob@100
|
4412 * @see PShapeSVG#parsePath
|
rob@100
|
4413 */
|
rob@100
|
4414 PShapeSVG.prototype.parsePathQuadto = function(x1, y1, cx, cy, x2, y2) {
|
rob@100
|
4415 if (this.vertices.length > 0) {
|
rob@100
|
4416 this.parsePathCode(PConstants.BEZIER_VERTEX);
|
rob@100
|
4417 // x1/y1 already covered by last moveto, lineto, or curveto
|
rob@100
|
4418 this.parsePathVertex(x1 + ((cx-x1)*2/3), y1 + ((cy-y1)*2/3));
|
rob@100
|
4419 this.parsePathVertex(x2 + ((cx-x2)*2/3), y2 + ((cy-y2)*2/3));
|
rob@100
|
4420 this.parsePathVertex(x2, y2);
|
rob@100
|
4421 } else {
|
rob@100
|
4422 throw ("Path must start with M/m");
|
rob@100
|
4423 }
|
rob@100
|
4424 };
|
rob@100
|
4425 /**
|
rob@100
|
4426 * @member PShapeSVG
|
rob@100
|
4427 * PShapeSVG.parsePath() helper function
|
rob@100
|
4428 *
|
rob@100
|
4429 * @see PShapeSVG#parsePath
|
rob@100
|
4430 */
|
rob@100
|
4431 PShapeSVG.prototype.parsePathCurveto = function(x1, y1, x2, y2, x3, y3) {
|
rob@100
|
4432 if (this.vertices.length > 0) {
|
rob@100
|
4433 this.parsePathCode(PConstants.BEZIER_VERTEX );
|
rob@100
|
4434 this.parsePathVertex(x1, y1);
|
rob@100
|
4435 this.parsePathVertex(x2, y2);
|
rob@100
|
4436 this.parsePathVertex(x3, y3);
|
rob@100
|
4437 } else {
|
rob@100
|
4438 throw ("Path must start with M/m");
|
rob@100
|
4439 }
|
rob@100
|
4440 };
|
rob@100
|
4441 /**
|
rob@100
|
4442 * @member PShapeSVG
|
rob@100
|
4443 * PShapeSVG.parsePath() helper function
|
rob@100
|
4444 *
|
rob@100
|
4445 * @see PShapeSVG#parsePath
|
rob@100
|
4446 */
|
rob@100
|
4447 PShapeSVG.prototype.parsePathLineto = function(px, py) {
|
rob@100
|
4448 if (this.vertices.length > 0) {
|
rob@100
|
4449 this.parsePathCode(PConstants.VERTEX);
|
rob@100
|
4450 this.parsePathVertex(px, py);
|
rob@100
|
4451 // add property to distinguish between curContext.moveTo
|
rob@100
|
4452 // or curContext.lineTo
|
rob@100
|
4453 this.vertices[this.vertices.length-1].moveTo = false;
|
rob@100
|
4454 } else {
|
rob@100
|
4455 throw ("Path must start with M/m");
|
rob@100
|
4456 }
|
rob@100
|
4457 };
|
rob@100
|
4458
|
rob@100
|
4459 PShapeSVG.prototype.parsePathMoveto = function(px, py) {
|
rob@100
|
4460 if (this.vertices.length > 0) {
|
rob@100
|
4461 this.parsePathCode(PConstants.BREAK);
|
rob@100
|
4462 }
|
rob@100
|
4463 this.parsePathCode(PConstants.VERTEX);
|
rob@100
|
4464 this.parsePathVertex(px, py);
|
rob@100
|
4465 // add property to distinguish between curContext.moveTo
|
rob@100
|
4466 // or curContext.lineTo
|
rob@100
|
4467 this.vertices[this.vertices.length-1].moveTo = true;
|
rob@100
|
4468 };
|
rob@100
|
4469 /**
|
rob@100
|
4470 * @member PShapeSVG
|
rob@100
|
4471 * PShapeSVG.parsePath() helper function
|
rob@100
|
4472 *
|
rob@100
|
4473 * @see PShapeSVG#parsePath
|
rob@100
|
4474 */
|
rob@100
|
4475 PShapeSVG.prototype.parsePathVertex = function(x, y) {
|
rob@100
|
4476 var verts = [];
|
rob@100
|
4477 verts[0] = x;
|
rob@100
|
4478 verts[1] = y;
|
rob@100
|
4479 this.vertices.push(verts);
|
rob@100
|
4480 };
|
rob@100
|
4481 /**
|
rob@100
|
4482 * @member PShapeSVG
|
rob@100
|
4483 * PShapeSVG.parsePath() helper function
|
rob@100
|
4484 *
|
rob@100
|
4485 * @see PShapeSVG#parsePath
|
rob@100
|
4486 */
|
rob@100
|
4487 PShapeSVG.prototype.parsePathCode = function(what) {
|
rob@100
|
4488 this.vertexCodes.push(what);
|
rob@100
|
4489 };
|
rob@100
|
4490 /**
|
rob@100
|
4491 * @member PShapeSVG
|
rob@100
|
4492 * The parsePoly() function parses a polyline or polygon from an SVG file.
|
rob@100
|
4493 *
|
rob@100
|
4494 * @param {boolean}val true if shape is closed (polygon), false if not (polyline)
|
rob@100
|
4495 */
|
rob@100
|
4496 PShapeSVG.prototype.parsePoly = function(val) {
|
rob@100
|
4497 this.family = PConstants.PATH;
|
rob@100
|
4498 this.close = val;
|
rob@100
|
4499 var pointsAttr = CommonFunctions.trim(this.element.getStringAttribute("points").replace(/[,\s]+/g,' '));
|
rob@100
|
4500 if (pointsAttr !== null) {
|
rob@100
|
4501 //split into array
|
rob@100
|
4502 var pointsBuffer = pointsAttr.split(" ");
|
rob@100
|
4503 if (pointsBuffer.length % 2 === 0) {
|
rob@100
|
4504 for (var i = 0, j = pointsBuffer.length; i < j; i++) {
|
rob@100
|
4505 var verts = [];
|
rob@100
|
4506 verts[0] = pointsBuffer[i];
|
rob@100
|
4507 verts[1] = pointsBuffer[++i];
|
rob@100
|
4508 this.vertices.push(verts);
|
rob@100
|
4509 }
|
rob@100
|
4510 } else {
|
rob@100
|
4511 throw("Error parsing polygon points: odd number of coordinates provided");
|
rob@100
|
4512 }
|
rob@100
|
4513 }
|
rob@100
|
4514 };
|
rob@100
|
4515 /**
|
rob@100
|
4516 * @member PShapeSVG
|
rob@100
|
4517 * The parseRect() function parses a rect from an SVG file.
|
rob@100
|
4518 */
|
rob@100
|
4519 PShapeSVG.prototype.parseRect = function() {
|
rob@100
|
4520 this.kind = PConstants.RECT;
|
rob@100
|
4521 this.family = PConstants.PRIMITIVE;
|
rob@100
|
4522 this.params = [];
|
rob@100
|
4523 this.params[0] = this.element.getFloatAttribute("x");
|
rob@100
|
4524 this.params[1] = this.element.getFloatAttribute("y");
|
rob@100
|
4525 this.params[2] = this.element.getFloatAttribute("width");
|
rob@100
|
4526 this.params[3] = this.element.getFloatAttribute("height");
|
rob@100
|
4527 if (this.params[2] < 0 || this.params[3] < 0) {
|
rob@100
|
4528 throw("svg error: negative width or height found while parsing <rect>");
|
rob@100
|
4529 }
|
rob@100
|
4530 };
|
rob@100
|
4531 /**
|
rob@100
|
4532 * @member PShapeSVG
|
rob@100
|
4533 * The parseEllipse() function handles parsing ellipse and circle tags.
|
rob@100
|
4534 *
|
rob@100
|
4535 * @param {boolean}val true if this is a circle and not an ellipse
|
rob@100
|
4536 */
|
rob@100
|
4537 PShapeSVG.prototype.parseEllipse = function(val) {
|
rob@100
|
4538 this.kind = PConstants.ELLIPSE;
|
rob@100
|
4539 this.family = PConstants.PRIMITIVE;
|
rob@100
|
4540 this.params = [];
|
rob@100
|
4541
|
rob@100
|
4542 this.params[0] = this.element.getFloatAttribute("cx") | 0 ;
|
rob@100
|
4543 this.params[1] = this.element.getFloatAttribute("cy") | 0;
|
rob@100
|
4544
|
rob@100
|
4545 var rx, ry;
|
rob@100
|
4546 if (val) {
|
rob@100
|
4547 rx = ry = this.element.getFloatAttribute("r");
|
rob@100
|
4548 if (rx < 0) {
|
rob@100
|
4549 throw("svg error: negative radius found while parsing <circle>");
|
rob@100
|
4550 }
|
rob@100
|
4551 } else {
|
rob@100
|
4552 rx = this.element.getFloatAttribute("rx");
|
rob@100
|
4553 ry = this.element.getFloatAttribute("ry");
|
rob@100
|
4554 if (rx < 0 || ry < 0) {
|
rob@100
|
4555 throw("svg error: negative x-axis radius or y-axis radius found while parsing <ellipse>");
|
rob@100
|
4556 }
|
rob@100
|
4557 }
|
rob@100
|
4558 this.params[0] -= rx;
|
rob@100
|
4559 this.params[1] -= ry;
|
rob@100
|
4560
|
rob@100
|
4561 this.params[2] = rx*2;
|
rob@100
|
4562 this.params[3] = ry*2;
|
rob@100
|
4563 };
|
rob@100
|
4564 /**
|
rob@100
|
4565 * @member PShapeSVG
|
rob@100
|
4566 * The parseLine() function handles parsing line tags.
|
rob@100
|
4567 *
|
rob@100
|
4568 * @param {boolean}val true if this is a circle and not an ellipse
|
rob@100
|
4569 */
|
rob@100
|
4570 PShapeSVG.prototype.parseLine = function() {
|
rob@100
|
4571 this.kind = PConstants.LINE;
|
rob@100
|
4572 this.family = PConstants.PRIMITIVE;
|
rob@100
|
4573 this.params = [];
|
rob@100
|
4574 this.params[0] = this.element.getFloatAttribute("x1");
|
rob@100
|
4575 this.params[1] = this.element.getFloatAttribute("y1");
|
rob@100
|
4576 this.params[2] = this.element.getFloatAttribute("x2");
|
rob@100
|
4577 this.params[3] = this.element.getFloatAttribute("y2");
|
rob@100
|
4578 };
|
rob@100
|
4579 /**
|
rob@100
|
4580 * @member PShapeSVG
|
rob@100
|
4581 * The parseColors() function handles parsing the opacity, strijem stroke-width, stroke-linejoin,stroke-linecap, fill, and style attributes
|
rob@100
|
4582 *
|
rob@100
|
4583 * @param {XMLElement}element the element of which attributes to parse
|
rob@100
|
4584 */
|
rob@100
|
4585 PShapeSVG.prototype.parseColors = function(element) {
|
rob@100
|
4586 if (element.hasAttribute("opacity")) {
|
rob@100
|
4587 this.setOpacity(element.getAttribute("opacity"));
|
rob@100
|
4588 }
|
rob@100
|
4589 if (element.hasAttribute("stroke")) {
|
rob@100
|
4590 this.setStroke(element.getAttribute("stroke"));
|
rob@100
|
4591 }
|
rob@100
|
4592 if (element.hasAttribute("stroke-width")) {
|
rob@100
|
4593 // if NaN (i.e. if it's 'inherit') then default
|
rob@100
|
4594 // back to the inherit setting
|
rob@100
|
4595 this.setStrokeWeight(element.getAttribute("stroke-width"));
|
rob@100
|
4596 }
|
rob@100
|
4597 if (element.hasAttribute("stroke-linejoin") ) {
|
rob@100
|
4598 this.setStrokeJoin(element.getAttribute("stroke-linejoin"));
|
rob@100
|
4599 }
|
rob@100
|
4600 if (element.hasAttribute("stroke-linecap")) {
|
rob@100
|
4601 this.setStrokeCap(element.getStringAttribute("stroke-linecap"));
|
rob@100
|
4602 }
|
rob@100
|
4603 // fill defaults to black (though stroke defaults to "none")
|
rob@100
|
4604 // http://www.w3.org/TR/SVG/painting.html#FillProperties
|
rob@100
|
4605 if (element.hasAttribute("fill")) {
|
rob@100
|
4606 this.setFill(element.getStringAttribute("fill"));
|
rob@100
|
4607 }
|
rob@100
|
4608 if (element.hasAttribute("style")) {
|
rob@100
|
4609 var styleText = element.getStringAttribute("style");
|
rob@100
|
4610 var styleTokens = styleText.toString().split( ";" );
|
rob@100
|
4611
|
rob@100
|
4612 for (var i = 0, j = styleTokens.length; i < j; i++) {
|
rob@100
|
4613 var tokens = CommonFunctions.trim(styleTokens[i].split( ":" ));
|
rob@100
|
4614 if (tokens[0] === "fill") {
|
rob@100
|
4615 this.setFill(tokens[1]);
|
rob@100
|
4616 } else if (tokens[0] === "fill-opacity") {
|
rob@100
|
4617 this.setFillOpacity(tokens[1]);
|
rob@100
|
4618 } else if (tokens[0] === "stroke") {
|
rob@100
|
4619 this.setStroke(tokens[1]);
|
rob@100
|
4620 } else if (tokens[0] === "stroke-width") {
|
rob@100
|
4621 this.setStrokeWeight(tokens[1]);
|
rob@100
|
4622 } else if (tokens[0] === "stroke-linecap") {
|
rob@100
|
4623 this.setStrokeCap(tokens[1]);
|
rob@100
|
4624 } else if (tokens[0] === "stroke-linejoin") {
|
rob@100
|
4625 this.setStrokeJoin(tokens[1]);
|
rob@100
|
4626 } else if (tokens[0] === "stroke-opacity") {
|
rob@100
|
4627 this.setStrokeOpacity(tokens[1]);
|
rob@100
|
4628 } else if (tokens[0] === "opacity") {
|
rob@100
|
4629 this.setOpacity(tokens[1]);
|
rob@100
|
4630 } // Other attributes are not yet implemented
|
rob@100
|
4631 }
|
rob@100
|
4632 }
|
rob@100
|
4633 };
|
rob@100
|
4634 /**
|
rob@100
|
4635 * @member PShapeSVG
|
rob@100
|
4636 * PShapeSVG.parseColors() helper function
|
rob@100
|
4637 *
|
rob@100
|
4638 * @param {String} opacityText the value of fillOpacity
|
rob@100
|
4639 *
|
rob@100
|
4640 * @see PShapeSVG#parseColors
|
rob@100
|
4641 */
|
rob@100
|
4642 PShapeSVG.prototype.setFillOpacity = function(opacityText) {
|
rob@100
|
4643 this.fillOpacity = parseFloat(opacityText);
|
rob@100
|
4644 this.fillColor = this.fillOpacity * 255 << 24 |
|
rob@100
|
4645 this.fillColor & 0xFFFFFF;
|
rob@100
|
4646 };
|
rob@100
|
4647 /**
|
rob@100
|
4648 * @member PShapeSVG
|
rob@100
|
4649 * PShapeSVG.parseColors() helper function
|
rob@100
|
4650 *
|
rob@100
|
4651 * @param {String} fillText the value of fill
|
rob@100
|
4652 *
|
rob@100
|
4653 * @see PShapeSVG#parseColors
|
rob@100
|
4654 */
|
rob@100
|
4655 PShapeSVG.prototype.setFill = function (fillText) {
|
rob@100
|
4656 var opacityMask = this.fillColor & 0xFF000000;
|
rob@100
|
4657 if (fillText === "none") {
|
rob@100
|
4658 this.fill = false;
|
rob@100
|
4659 } else if (fillText.indexOf("#") === 0) {
|
rob@100
|
4660 this.fill = true;
|
rob@100
|
4661 if (fillText.length === 4) {
|
rob@100
|
4662 // convert #00F to #0000FF
|
rob@100
|
4663 fillText = fillText.replace(/#(.)(.)(.)/,"#$1$1$2$2$3$3");
|
rob@100
|
4664 }
|
rob@100
|
4665 this.fillColor = opacityMask |
|
rob@100
|
4666 (parseInt(fillText.substring(1), 16 )) &
|
rob@100
|
4667 0xFFFFFF;
|
rob@100
|
4668 } else if (fillText.indexOf("rgb") === 0) {
|
rob@100
|
4669 this.fill = true;
|
rob@100
|
4670 this.fillColor = opacityMask | this.parseRGB(fillText);
|
rob@100
|
4671 } else if (fillText.indexOf("url(#") === 0) {
|
rob@100
|
4672 this.fillName = fillText.substring(5, fillText.length - 1 );
|
rob@100
|
4673 } else if (colors[fillText]) {
|
rob@100
|
4674 this.fill = true;
|
rob@100
|
4675 this.fillColor = opacityMask |
|
rob@100
|
4676 (parseInt(colors[fillText].substring(1), 16)) &
|
rob@100
|
4677 0xFFFFFF;
|
rob@100
|
4678 }
|
rob@100
|
4679 };
|
rob@100
|
4680 /**
|
rob@100
|
4681 * @member PShapeSVG
|
rob@100
|
4682 * PShapeSVG.parseColors() helper function
|
rob@100
|
4683 *
|
rob@100
|
4684 * @param {String} opacity the value of opacity
|
rob@100
|
4685 *
|
rob@100
|
4686 * @see PShapeSVG#parseColors
|
rob@100
|
4687 */
|
rob@100
|
4688 PShapeSVG.prototype.setOpacity = function(opacity) {
|
rob@100
|
4689 this.strokeColor = parseFloat(opacity) * 255 << 24 |
|
rob@100
|
4690 this.strokeColor & 0xFFFFFF;
|
rob@100
|
4691 this.fillColor = parseFloat(opacity) * 255 << 24 |
|
rob@100
|
4692 this.fillColor & 0xFFFFFF;
|
rob@100
|
4693 };
|
rob@100
|
4694 /**
|
rob@100
|
4695 * @member PShapeSVG
|
rob@100
|
4696 * PShapeSVG.parseColors() helper function
|
rob@100
|
4697 *
|
rob@100
|
4698 * @param {String} strokeText the value to set stroke to
|
rob@100
|
4699 *
|
rob@100
|
4700 * @see PShapeSVG#parseColors
|
rob@100
|
4701 */
|
rob@100
|
4702 PShapeSVG.prototype.setStroke = function(strokeText) {
|
rob@100
|
4703 var opacityMask = this.strokeColor & 0xFF000000;
|
rob@100
|
4704 if (strokeText === "none") {
|
rob@100
|
4705 this.stroke = false;
|
rob@100
|
4706 } else if (strokeText.charAt( 0 ) === "#") {
|
rob@100
|
4707 this.stroke = true;
|
rob@100
|
4708 if (strokeText.length === 4) {
|
rob@100
|
4709 // convert #00F to #0000FF
|
rob@100
|
4710 strokeText = strokeText.replace(/#(.)(.)(.)/,"#$1$1$2$2$3$3");
|
rob@100
|
4711 }
|
rob@100
|
4712 this.strokeColor = opacityMask |
|
rob@100
|
4713 (parseInt( strokeText.substring( 1 ), 16 )) &
|
rob@100
|
4714 0xFFFFFF;
|
rob@100
|
4715 } else if (strokeText.indexOf( "rgb" ) === 0 ) {
|
rob@100
|
4716 this.stroke = true;
|
rob@100
|
4717 this.strokeColor = opacityMask | this.parseRGB(strokeText);
|
rob@100
|
4718 } else if (strokeText.indexOf( "url(#" ) === 0) {
|
rob@100
|
4719 this.strokeName = strokeText.substring(5, strokeText.length - 1);
|
rob@100
|
4720 } else if (colors[strokeText]) {
|
rob@100
|
4721 this.stroke = true;
|
rob@100
|
4722 this.strokeColor = opacityMask |
|
rob@100
|
4723 (parseInt(colors[strokeText].substring(1), 16)) &
|
rob@100
|
4724 0xFFFFFF;
|
rob@100
|
4725 }
|
rob@100
|
4726 };
|
rob@100
|
4727 /**
|
rob@100
|
4728 * @member PShapeSVG
|
rob@100
|
4729 * PShapeSVG.parseColors() helper function
|
rob@100
|
4730 *
|
rob@100
|
4731 * @param {String} weight the value to set strokeWeight to
|
rob@100
|
4732 *
|
rob@100
|
4733 * @see PShapeSVG#parseColors
|
rob@100
|
4734 */
|
rob@100
|
4735 PShapeSVG.prototype.setStrokeWeight = function(weight) {
|
rob@100
|
4736 this.strokeWeight = this.parseUnitSize(weight);
|
rob@100
|
4737 };
|
rob@100
|
4738 /**
|
rob@100
|
4739 * @member PShapeSVG
|
rob@100
|
4740 * PShapeSVG.parseColors() helper function
|
rob@100
|
4741 *
|
rob@100
|
4742 * @param {String} linejoin the value to set strokeJoin to
|
rob@100
|
4743 *
|
rob@100
|
4744 * @see PShapeSVG#parseColors
|
rob@100
|
4745 */
|
rob@100
|
4746 PShapeSVG.prototype.setStrokeJoin = function(linejoin) {
|
rob@100
|
4747 if (linejoin === "miter") {
|
rob@100
|
4748 this.strokeJoin = PConstants.MITER;
|
rob@100
|
4749
|
rob@100
|
4750 } else if (linejoin === "round") {
|
rob@100
|
4751 this.strokeJoin = PConstants.ROUND;
|
rob@100
|
4752
|
rob@100
|
4753 } else if (linejoin === "bevel") {
|
rob@100
|
4754 this.strokeJoin = PConstants.BEVEL;
|
rob@100
|
4755 }
|
rob@100
|
4756 };
|
rob@100
|
4757 /**
|
rob@100
|
4758 * @member PShapeSVG
|
rob@100
|
4759 * PShapeSVG.parseColors() helper function
|
rob@100
|
4760 *
|
rob@100
|
4761 * @param {String} linecap the value to set strokeCap to
|
rob@100
|
4762 *
|
rob@100
|
4763 * @see PShapeSVG#parseColors
|
rob@100
|
4764 */
|
rob@100
|
4765 PShapeSVG.prototype.setStrokeCap = function (linecap) {
|
rob@100
|
4766 if (linecap === "butt") {
|
rob@100
|
4767 this.strokeCap = PConstants.SQUARE;
|
rob@100
|
4768
|
rob@100
|
4769 } else if (linecap === "round") {
|
rob@100
|
4770 this.strokeCap = PConstants.ROUND;
|
rob@100
|
4771
|
rob@100
|
4772 } else if (linecap === "square") {
|
rob@100
|
4773 this.strokeCap = PConstants.PROJECT;
|
rob@100
|
4774 }
|
rob@100
|
4775 };
|
rob@100
|
4776 /**
|
rob@100
|
4777 * @member PShapeSVG
|
rob@100
|
4778 * PShapeSVG.parseColors() helper function
|
rob@100
|
4779 *
|
rob@100
|
4780 * @param {String} opacityText the value to set stroke opacity to
|
rob@100
|
4781 *
|
rob@100
|
4782 * @see PShapeSVG#parseColors
|
rob@100
|
4783 */
|
rob@100
|
4784 PShapeSVG.prototype.setStrokeOpacity = function (opacityText) {
|
rob@100
|
4785 this.strokeOpacity = parseFloat(opacityText);
|
rob@100
|
4786 this.strokeColor = this.strokeOpacity * 255 << 24 |
|
rob@100
|
4787 this.strokeColor &
|
rob@100
|
4788 0xFFFFFF;
|
rob@100
|
4789 };
|
rob@100
|
4790 /**
|
rob@100
|
4791 * @member PShapeSVG
|
rob@100
|
4792 * The parseRGB() function parses an rbg() color string and returns a color int
|
rob@100
|
4793 *
|
rob@100
|
4794 * @param {String} color the color to parse in rbg() format
|
rob@100
|
4795 *
|
rob@100
|
4796 * @return {int} the equivalent color int
|
rob@100
|
4797 */
|
rob@100
|
4798 PShapeSVG.prototype.parseRGB = function(color) {
|
rob@100
|
4799 var sub = color.substring(color.indexOf('(') + 1, color.indexOf(')'));
|
rob@100
|
4800 var values = sub.split(", ");
|
rob@100
|
4801 return (values[0] << 16) | (values[1] << 8) | (values[2]);
|
rob@100
|
4802 };
|
rob@100
|
4803 /**
|
rob@100
|
4804 * @member PShapeSVG
|
rob@100
|
4805 * The parseUnitSize() function parse a size that may have a suffix for its units.
|
rob@100
|
4806 * Ignoring cases where this could also be a percentage.
|
rob@100
|
4807 * The <A HREF="http://www.w3.org/TR/SVG/coords.html#Units">units</A> spec:
|
rob@100
|
4808 * <UL>
|
rob@100
|
4809 * <LI>"1pt" equals "1.25px" (and therefore 1.25 user units)
|
rob@100
|
4810 * <LI>"1pc" equals "15px" (and therefore 15 user units)
|
rob@100
|
4811 * <LI>"1mm" would be "3.543307px" (3.543307 user units)
|
rob@100
|
4812 * <LI>"1cm" equals "35.43307px" (and therefore 35.43307 user units)
|
rob@100
|
4813 * <LI>"1in" equals "90px" (and therefore 90 user units)
|
rob@100
|
4814 * </UL>
|
rob@100
|
4815 */
|
rob@100
|
4816 PShapeSVG.prototype.parseUnitSize = function (text) {
|
rob@100
|
4817 var len = text.length - 2;
|
rob@100
|
4818 if (len < 0) { return text; }
|
rob@100
|
4819 if (text.indexOf("pt") === len) {
|
rob@100
|
4820 return parseFloat(text.substring(0, len)) * 1.25;
|
rob@100
|
4821 }
|
rob@100
|
4822 if (text.indexOf("pc") === len) {
|
rob@100
|
4823 return parseFloat( text.substring( 0, len)) * 15;
|
rob@100
|
4824 }
|
rob@100
|
4825 if (text.indexOf("mm") === len) {
|
rob@100
|
4826 return parseFloat( text.substring(0, len)) * 3.543307;
|
rob@100
|
4827 }
|
rob@100
|
4828 if (text.indexOf("cm") === len) {
|
rob@100
|
4829 return parseFloat(text.substring(0, len)) * 35.43307;
|
rob@100
|
4830 }
|
rob@100
|
4831 if (text.indexOf("in") === len) {
|
rob@100
|
4832 return parseFloat(text.substring(0, len)) * 90;
|
rob@100
|
4833 }
|
rob@100
|
4834 if (text.indexOf("px") === len) {
|
rob@100
|
4835 return parseFloat(text.substring(0, len));
|
rob@100
|
4836 }
|
rob@100
|
4837 return parseFloat(text);
|
rob@100
|
4838 };
|
rob@100
|
4839
|
rob@100
|
4840 return PShapeSVG;
|
rob@100
|
4841 };
|
rob@100
|
4842
|
rob@100
|
4843 },{}],17:[function(require,module,exports){
|
rob@100
|
4844 module.exports = function(options, undef) {
|
rob@100
|
4845 var PConstants = options.PConstants;
|
rob@100
|
4846
|
rob@100
|
4847 function PVector(x, y, z) {
|
rob@100
|
4848 this.x = x || 0;
|
rob@100
|
4849 this.y = y || 0;
|
rob@100
|
4850 this.z = z || 0;
|
rob@100
|
4851 }
|
rob@100
|
4852
|
rob@100
|
4853 PVector.fromAngle = function(angle, v) {
|
rob@100
|
4854 if (v === undef || v === null) {
|
rob@100
|
4855 v = new PVector();
|
rob@100
|
4856 }
|
rob@100
|
4857 v.x = Math.cos(angle);
|
rob@100
|
4858 v.y = Math.sin(angle);
|
rob@100
|
4859 return v;
|
rob@100
|
4860 };
|
rob@100
|
4861
|
rob@100
|
4862 PVector.random2D = function(v) {
|
rob@100
|
4863 return PVector.fromAngle(Math.random() * PConstants.TWO_PI, v);
|
rob@100
|
4864 };
|
rob@100
|
4865
|
rob@100
|
4866 PVector.random3D = function(v) {
|
rob@100
|
4867 var angle = Math.random() * PConstants.TWO_PI;
|
rob@100
|
4868 var vz = Math.random() * 2 - 1;
|
rob@100
|
4869 var mult = Math.sqrt(1 - vz * vz);
|
rob@100
|
4870 var vx = mult * Math.cos(angle);
|
rob@100
|
4871 var vy = mult * Math.sin(angle);
|
rob@100
|
4872 if (v === undef || v === null) {
|
rob@100
|
4873 v = new PVector(vx, vy, vz);
|
rob@100
|
4874 } else {
|
rob@100
|
4875 v.set(vx, vy, vz);
|
rob@100
|
4876 }
|
rob@100
|
4877 return v;
|
rob@100
|
4878 };
|
rob@100
|
4879
|
rob@100
|
4880 PVector.dist = function(v1, v2) {
|
rob@100
|
4881 return v1.dist(v2);
|
rob@100
|
4882 };
|
rob@100
|
4883
|
rob@100
|
4884 PVector.dot = function(v1, v2) {
|
rob@100
|
4885 return v1.dot(v2);
|
rob@100
|
4886 };
|
rob@100
|
4887
|
rob@100
|
4888 PVector.cross = function(v1, v2) {
|
rob@100
|
4889 return v1.cross(v2);
|
rob@100
|
4890 };
|
rob@100
|
4891
|
rob@100
|
4892 PVector.sub = function(v1, v2) {
|
rob@100
|
4893 return new PVector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
|
rob@100
|
4894 };
|
rob@100
|
4895
|
rob@100
|
4896 PVector.angleBetween = function(v1, v2) {
|
rob@100
|
4897 return Math.acos(v1.dot(v2) / (v1.mag() * v2.mag()));
|
rob@100
|
4898 };
|
rob@100
|
4899
|
rob@100
|
4900 PVector.lerp = function(v1, v2, amt) {
|
rob@100
|
4901 // non-static lerp mutates object, but this version returns a new vector
|
rob@100
|
4902 var retval = new PVector(v1.x, v1.y, v1.z);
|
rob@100
|
4903 retval.lerp(v2, amt);
|
rob@100
|
4904 return retval;
|
rob@100
|
4905 };
|
rob@100
|
4906
|
rob@100
|
4907 // Common vector operations for PVector
|
rob@100
|
4908 PVector.prototype = {
|
rob@100
|
4909 set: function(v, y, z) {
|
rob@100
|
4910 if (arguments.length === 1) {
|
rob@100
|
4911 this.set(v.x || v[0] || 0,
|
rob@100
|
4912 v.y || v[1] || 0,
|
rob@100
|
4913 v.z || v[2] || 0);
|
rob@100
|
4914 } else {
|
rob@100
|
4915 this.x = v;
|
rob@100
|
4916 this.y = y;
|
rob@100
|
4917 this.z = z;
|
rob@100
|
4918 }
|
rob@100
|
4919 },
|
rob@100
|
4920 get: function() {
|
rob@100
|
4921 return new PVector(this.x, this.y, this.z);
|
rob@100
|
4922 },
|
rob@100
|
4923 mag: function() {
|
rob@100
|
4924 var x = this.x,
|
rob@100
|
4925 y = this.y,
|
rob@100
|
4926 z = this.z;
|
rob@100
|
4927 return Math.sqrt(x * x + y * y + z * z);
|
rob@100
|
4928 },
|
rob@100
|
4929 magSq: function() {
|
rob@100
|
4930 var x = this.x,
|
rob@100
|
4931 y = this.y,
|
rob@100
|
4932 z = this.z;
|
rob@100
|
4933 return (x * x + y * y + z * z);
|
rob@100
|
4934 },
|
rob@100
|
4935 setMag: function(v_or_len, len) {
|
rob@100
|
4936 if (len === undef) {
|
rob@100
|
4937 len = v_or_len;
|
rob@100
|
4938 this.normalize();
|
rob@100
|
4939 this.mult(len);
|
rob@100
|
4940 } else {
|
rob@100
|
4941 var v = v_or_len;
|
rob@100
|
4942 v.normalize();
|
rob@100
|
4943 v.mult(len);
|
rob@100
|
4944 return v;
|
rob@100
|
4945 }
|
rob@100
|
4946 },
|
rob@100
|
4947 add: function(v, y, z) {
|
rob@100
|
4948 if (arguments.length === 1) {
|
rob@100
|
4949 this.x += v.x;
|
rob@100
|
4950 this.y += v.y;
|
rob@100
|
4951 this.z += v.z;
|
rob@100
|
4952 } else {
|
rob@100
|
4953 this.x += v;
|
rob@100
|
4954 this.y += y;
|
rob@100
|
4955 this.z += z;
|
rob@100
|
4956 }
|
rob@100
|
4957 },
|
rob@100
|
4958 sub: function(v, y, z) {
|
rob@100
|
4959 if (arguments.length === 1) {
|
rob@100
|
4960 this.x -= v.x;
|
rob@100
|
4961 this.y -= v.y;
|
rob@100
|
4962 this.z -= v.z;
|
rob@100
|
4963 } else {
|
rob@100
|
4964 this.x -= v;
|
rob@100
|
4965 this.y -= y;
|
rob@100
|
4966 this.z -= z;
|
rob@100
|
4967 }
|
rob@100
|
4968 },
|
rob@100
|
4969 mult: function(v) {
|
rob@100
|
4970 if (typeof v === 'number') {
|
rob@100
|
4971 this.x *= v;
|
rob@100
|
4972 this.y *= v;
|
rob@100
|
4973 this.z *= v;
|
rob@100
|
4974 } else {
|
rob@100
|
4975 this.x *= v.x;
|
rob@100
|
4976 this.y *= v.y;
|
rob@100
|
4977 this.z *= v.z;
|
rob@100
|
4978 }
|
rob@100
|
4979 },
|
rob@100
|
4980 div: function(v) {
|
rob@100
|
4981 if (typeof v === 'number') {
|
rob@100
|
4982 this.x /= v;
|
rob@100
|
4983 this.y /= v;
|
rob@100
|
4984 this.z /= v;
|
rob@100
|
4985 } else {
|
rob@100
|
4986 this.x /= v.x;
|
rob@100
|
4987 this.y /= v.y;
|
rob@100
|
4988 this.z /= v.z;
|
rob@100
|
4989 }
|
rob@100
|
4990 },
|
rob@100
|
4991 rotate: function(angle) {
|
rob@100
|
4992 var prev_x = this.x;
|
rob@100
|
4993 var c = Math.cos(angle);
|
rob@100
|
4994 var s = Math.sin(angle);
|
rob@100
|
4995 this.x = c * this.x - s * this.y;
|
rob@100
|
4996 this.y = s * prev_x + c * this.y;
|
rob@100
|
4997 },
|
rob@100
|
4998 dist: function(v) {
|
rob@100
|
4999 var dx = this.x - v.x,
|
rob@100
|
5000 dy = this.y - v.y,
|
rob@100
|
5001 dz = this.z - v.z;
|
rob@100
|
5002 return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
rob@100
|
5003 },
|
rob@100
|
5004 dot: function(v, y, z) {
|
rob@100
|
5005 if (arguments.length === 1) {
|
rob@100
|
5006 return (this.x * v.x + this.y * v.y + this.z * v.z);
|
rob@100
|
5007 }
|
rob@100
|
5008 return (this.x * v + this.y * y + this.z * z);
|
rob@100
|
5009 },
|
rob@100
|
5010 cross: function(v) {
|
rob@100
|
5011 var x = this.x,
|
rob@100
|
5012 y = this.y,
|
rob@100
|
5013 z = this.z;
|
rob@100
|
5014 return new PVector(y * v.z - v.y * z,
|
rob@100
|
5015 z * v.x - v.z * x,
|
rob@100
|
5016 x * v.y - v.x * y);
|
rob@100
|
5017 },
|
rob@100
|
5018 lerp: function(v_or_x, amt_or_y, z, amt) {
|
rob@100
|
5019 var lerp_val = function(start, stop, amt) {
|
rob@100
|
5020 return start + (stop - start) * amt;
|
rob@100
|
5021 };
|
rob@100
|
5022 var x, y;
|
rob@100
|
5023 if (arguments.length === 2) {
|
rob@100
|
5024 // given vector and amt
|
rob@100
|
5025 amt = amt_or_y;
|
rob@100
|
5026 x = v_or_x.x;
|
rob@100
|
5027 y = v_or_x.y;
|
rob@100
|
5028 z = v_or_x.z;
|
rob@100
|
5029 } else {
|
rob@100
|
5030 // given x, y, z and amt
|
rob@100
|
5031 x = v_or_x;
|
rob@100
|
5032 y = amt_or_y;
|
rob@100
|
5033 }
|
rob@100
|
5034 this.x = lerp_val(this.x, x, amt);
|
rob@100
|
5035 this.y = lerp_val(this.y, y, amt);
|
rob@100
|
5036 this.z = lerp_val(this.z, z, amt);
|
rob@100
|
5037 },
|
rob@100
|
5038 normalize: function() {
|
rob@100
|
5039 var m = this.mag();
|
rob@100
|
5040 if (m > 0) {
|
rob@100
|
5041 this.div(m);
|
rob@100
|
5042 }
|
rob@100
|
5043 },
|
rob@100
|
5044 limit: function(high) {
|
rob@100
|
5045 if (this.mag() > high) {
|
rob@100
|
5046 this.normalize();
|
rob@100
|
5047 this.mult(high);
|
rob@100
|
5048 }
|
rob@100
|
5049 },
|
rob@100
|
5050 heading: function() {
|
rob@100
|
5051 return (-Math.atan2(-this.y, this.x));
|
rob@100
|
5052 },
|
rob@100
|
5053 heading2D: function() {
|
rob@100
|
5054 return this.heading();
|
rob@100
|
5055 },
|
rob@100
|
5056 toString: function() {
|
rob@100
|
5057 return "[" + this.x + ", " + this.y + ", " + this.z + "]";
|
rob@100
|
5058 },
|
rob@100
|
5059 array: function() {
|
rob@100
|
5060 return [this.x, this.y, this.z];
|
rob@100
|
5061 }
|
rob@100
|
5062 };
|
rob@100
|
5063
|
rob@100
|
5064 function createPVectorMethod(method) {
|
rob@100
|
5065 return function(v1, v2) {
|
rob@100
|
5066 var v = v1.get();
|
rob@100
|
5067 v[method](v2);
|
rob@100
|
5068 return v;
|
rob@100
|
5069 };
|
rob@100
|
5070 }
|
rob@100
|
5071
|
rob@100
|
5072 for (var method in PVector.prototype) {
|
rob@100
|
5073 if (PVector.prototype.hasOwnProperty(method) && !PVector.hasOwnProperty(method)) {
|
rob@100
|
5074 PVector[method] = createPVectorMethod(method);
|
rob@100
|
5075 }
|
rob@100
|
5076 }
|
rob@100
|
5077
|
rob@100
|
5078 return PVector;
|
rob@100
|
5079 };
|
rob@100
|
5080
|
rob@100
|
5081
|
rob@100
|
5082 },{}],18:[function(require,module,exports){
|
rob@100
|
5083 /**
|
rob@100
|
5084 * XMLAttribute is an attribute of a XML element.
|
rob@100
|
5085 *
|
rob@100
|
5086 * @param {String} fname the full name of the attribute
|
rob@100
|
5087 * @param {String} n the short name of the attribute
|
rob@100
|
5088 * @param {String} namespace the namespace URI of the attribute
|
rob@100
|
5089 * @param {String} v the value of the attribute
|
rob@100
|
5090 * @param {String }t the type of the attribute
|
rob@100
|
5091 *
|
rob@100
|
5092 * @see XMLElement
|
rob@100
|
5093 */
|
rob@100
|
5094 module.exports = function() {
|
rob@100
|
5095
|
rob@100
|
5096 var XMLAttribute = function (fname, n, nameSpace, v, t){
|
rob@100
|
5097 this.fullName = fname || "";
|
rob@100
|
5098 this.name = n || "";
|
rob@100
|
5099 this.namespace = nameSpace || "";
|
rob@100
|
5100 this.value = v;
|
rob@100
|
5101 this.type = t;
|
rob@100
|
5102 };
|
rob@100
|
5103
|
rob@100
|
5104 XMLAttribute.prototype = {
|
rob@100
|
5105 /**
|
rob@100
|
5106 * @member XMLAttribute
|
rob@100
|
5107 * The getName() function returns the short name of the attribute
|
rob@100
|
5108 *
|
rob@100
|
5109 * @return {String} the short name of the attribute
|
rob@100
|
5110 */
|
rob@100
|
5111 getName: function() {
|
rob@100
|
5112 return this.name;
|
rob@100
|
5113 },
|
rob@100
|
5114 /**
|
rob@100
|
5115 * @member XMLAttribute
|
rob@100
|
5116 * The getFullName() function returns the full name of the attribute
|
rob@100
|
5117 *
|
rob@100
|
5118 * @return {String} the full name of the attribute
|
rob@100
|
5119 */
|
rob@100
|
5120 getFullName: function() {
|
rob@100
|
5121 return this.fullName;
|
rob@100
|
5122 },
|
rob@100
|
5123 /**
|
rob@100
|
5124 * @member XMLAttribute
|
rob@100
|
5125 * The getNamespace() function returns the namespace of the attribute
|
rob@100
|
5126 *
|
rob@100
|
5127 * @return {String} the namespace of the attribute
|
rob@100
|
5128 */
|
rob@100
|
5129 getNamespace: function() {
|
rob@100
|
5130 return this.namespace;
|
rob@100
|
5131 },
|
rob@100
|
5132 /**
|
rob@100
|
5133 * @member XMLAttribute
|
rob@100
|
5134 * The getValue() function returns the value of the attribute
|
rob@100
|
5135 *
|
rob@100
|
5136 * @return {String} the value of the attribute
|
rob@100
|
5137 */
|
rob@100
|
5138 getValue: function() {
|
rob@100
|
5139 return this.value;
|
rob@100
|
5140 },
|
rob@100
|
5141 /**
|
rob@100
|
5142 * @member XMLAttribute
|
rob@100
|
5143 * The getValue() function returns the type of the attribute
|
rob@100
|
5144 *
|
rob@100
|
5145 * @return {String} the type of the attribute
|
rob@100
|
5146 */
|
rob@100
|
5147 getType: function() {
|
rob@100
|
5148 return this.type;
|
rob@100
|
5149 },
|
rob@100
|
5150 /**
|
rob@100
|
5151 * @member XMLAttribute
|
rob@100
|
5152 * The setValue() function sets the value of the attribute
|
rob@100
|
5153 *
|
rob@100
|
5154 * @param {String} newval the new value
|
rob@100
|
5155 */
|
rob@100
|
5156 setValue: function(newval) {
|
rob@100
|
5157 this.value = newval;
|
rob@100
|
5158 }
|
rob@100
|
5159 };
|
rob@100
|
5160
|
rob@100
|
5161 return XMLAttribute;
|
rob@100
|
5162 };
|
rob@100
|
5163
|
rob@100
|
5164 },{}],19:[function(require,module,exports){
|
rob@100
|
5165 /**
|
rob@100
|
5166 * XMLElement is a representation of an XML object. The object is able to parse XML code
|
rob@100
|
5167 *
|
rob@100
|
5168 * @param {PApplet} parent typically use "this"
|
rob@100
|
5169 * @param {String} filename name of the XML/SVG file to load
|
rob@100
|
5170 * @param {String} xml the xml/svg string
|
rob@100
|
5171 * @param {String} fullname the full name of the element
|
rob@100
|
5172 * @param {String} namespace the namespace of the URI
|
rob@100
|
5173 * @param {String} systemID the system ID of the XML data where the element starts
|
rob@100
|
5174 * @param {Integer }lineNr the line in the XML data where the element starts
|
rob@100
|
5175 */
|
rob@100
|
5176 module.exports = function(options, undef) {
|
rob@100
|
5177
|
rob@100
|
5178 var Browser = options.Browser,
|
rob@100
|
5179 ajax = Browser.ajax,
|
rob@100
|
5180 window = Browser.window,
|
rob@100
|
5181 XMLHttpRequest = window.XMLHttpRequest,
|
rob@100
|
5182 DOMParser = window.DOMParser,
|
rob@100
|
5183 XMLAttribute = options. XMLAttribute;
|
rob@100
|
5184
|
rob@100
|
5185 var XMLElement = function(selector, uri, sysid, line) {
|
rob@100
|
5186 this.attributes = [];
|
rob@100
|
5187 this.children = [];
|
rob@100
|
5188 this.fullName = null;
|
rob@100
|
5189 this.name = null;
|
rob@100
|
5190 this.namespace = "";
|
rob@100
|
5191 this.content = null;
|
rob@100
|
5192 this.parent = null;
|
rob@100
|
5193 this.lineNr = "";
|
rob@100
|
5194 this.systemID = "";
|
rob@100
|
5195 this.type = "ELEMENT";
|
rob@100
|
5196
|
rob@100
|
5197 if (selector) {
|
rob@100
|
5198 if (typeof selector === "string") {
|
rob@100
|
5199 if (uri === undef && selector.indexOf("<") > -1) {
|
rob@100
|
5200 // load XML from text string
|
rob@100
|
5201 this.parse(selector);
|
rob@100
|
5202 } else {
|
rob@100
|
5203 // XMLElement(fullname, namespace, sysid, line) format
|
rob@100
|
5204 this.fullName = selector;
|
rob@100
|
5205 this.namespace = uri;
|
rob@100
|
5206 this.systemId = sysid;
|
rob@100
|
5207 this.lineNr = line;
|
rob@100
|
5208 }
|
rob@100
|
5209 } else {
|
rob@100
|
5210 // XMLElement(this,file) format
|
rob@100
|
5211 this.parse(uri);
|
rob@100
|
5212 }
|
rob@100
|
5213 }
|
rob@100
|
5214 };
|
rob@100
|
5215 /**
|
rob@100
|
5216 * XMLElement methods
|
rob@100
|
5217 * missing: enumerateAttributeNames(), enumerateChildren(),
|
rob@100
|
5218 * NOTE: parse does not work when a url is passed in
|
rob@100
|
5219 */
|
rob@100
|
5220 XMLElement.prototype = {
|
rob@100
|
5221 /**
|
rob@100
|
5222 * @member XMLElement
|
rob@100
|
5223 * The parse() function retrieves the file via ajax() and uses DOMParser()
|
rob@100
|
5224 * parseFromString method to make an XML document
|
rob@100
|
5225 * @addon
|
rob@100
|
5226 *
|
rob@100
|
5227 * @param {String} filename name of the XML/SVG file to load
|
rob@100
|
5228 *
|
rob@100
|
5229 * @throws ExceptionType Error loading document
|
rob@100
|
5230 *
|
rob@100
|
5231 * @see XMLElement#parseChildrenRecursive
|
rob@100
|
5232 */
|
rob@100
|
5233 parse: function(textstring) {
|
rob@100
|
5234 var xmlDoc;
|
rob@100
|
5235 try {
|
rob@100
|
5236 var extension = textstring.substring(textstring.length-4);
|
rob@100
|
5237 if (extension === ".xml" || extension === ".svg") {
|
rob@100
|
5238 textstring = ajax(textstring);
|
rob@100
|
5239 }
|
rob@100
|
5240 xmlDoc = new DOMParser().parseFromString(textstring, "text/xml");
|
rob@100
|
5241 var elements = xmlDoc.documentElement;
|
rob@100
|
5242 if (elements) {
|
rob@100
|
5243 this.parseChildrenRecursive(null, elements);
|
rob@100
|
5244 } else {
|
rob@100
|
5245 throw ("Error loading document");
|
rob@100
|
5246 }
|
rob@100
|
5247 return this;
|
rob@100
|
5248 } catch(e) {
|
rob@100
|
5249 throw(e);
|
rob@100
|
5250 }
|
rob@100
|
5251 },
|
rob@100
|
5252 /**
|
rob@100
|
5253 * @member XMLElement
|
rob@100
|
5254 * Internal helper function for parse().
|
rob@100
|
5255 * Loops through the
|
rob@100
|
5256 * @addon
|
rob@100
|
5257 *
|
rob@100
|
5258 * @param {XMLElement} parent the parent node
|
rob@100
|
5259 * @param {XML document childNodes} elementpath the remaining nodes that need parsing
|
rob@100
|
5260 *
|
rob@100
|
5261 * @return {XMLElement} the new element and its children elements
|
rob@100
|
5262 */
|
rob@100
|
5263 parseChildrenRecursive: function (parent, elementpath){
|
rob@100
|
5264 var xmlelement,
|
rob@100
|
5265 xmlattribute,
|
rob@100
|
5266 tmpattrib,
|
rob@100
|
5267 l, m,
|
rob@100
|
5268 child;
|
rob@100
|
5269 if (!parent) { // this element is the root element
|
rob@100
|
5270 this.fullName = elementpath.localName;
|
rob@100
|
5271 this.name = elementpath.nodeName;
|
rob@100
|
5272 xmlelement = this;
|
rob@100
|
5273 } else { // this element has a parent
|
rob@100
|
5274 xmlelement = new XMLElement(elementpath.nodeName);
|
rob@100
|
5275 xmlelement.parent = parent;
|
rob@100
|
5276 }
|
rob@100
|
5277
|
rob@100
|
5278 // if this is a text node, return a PCData element (parsed character data)
|
rob@100
|
5279 if (elementpath.nodeType === 3 && elementpath.textContent !== "") {
|
rob@100
|
5280 return this.createPCDataElement(elementpath.textContent);
|
rob@100
|
5281 }
|
rob@100
|
5282
|
rob@100
|
5283 // if this is a CDATA node, return a CData element (unparsed character data)
|
rob@100
|
5284 if (elementpath.nodeType === 4) {
|
rob@100
|
5285 return this.createCDataElement(elementpath.textContent);
|
rob@100
|
5286 }
|
rob@100
|
5287
|
rob@100
|
5288 // bind all attributes, if there are any
|
rob@100
|
5289 if (elementpath.attributes) {
|
rob@100
|
5290 for (l = 0, m = elementpath.attributes.length; l < m; l++) {
|
rob@100
|
5291 tmpattrib = elementpath.attributes[l];
|
rob@100
|
5292 xmlattribute = new XMLAttribute(tmpattrib.getname,
|
rob@100
|
5293 tmpattrib.nodeName,
|
rob@100
|
5294 tmpattrib.namespaceURI,
|
rob@100
|
5295 tmpattrib.nodeValue,
|
rob@100
|
5296 tmpattrib.nodeType);
|
rob@100
|
5297 xmlelement.attributes.push(xmlattribute);
|
rob@100
|
5298 }
|
rob@100
|
5299 }
|
rob@100
|
5300
|
rob@100
|
5301 // bind all children, if there are any
|
rob@100
|
5302 if (elementpath.childNodes) {
|
rob@100
|
5303 for (l = 0, m = elementpath.childNodes.length; l < m; l++) {
|
rob@100
|
5304 var node = elementpath.childNodes[l];
|
rob@100
|
5305 child = xmlelement.parseChildrenRecursive(xmlelement, node);
|
rob@100
|
5306 if (child !== null) {
|
rob@100
|
5307 xmlelement.children.push(child);
|
rob@100
|
5308 }
|
rob@100
|
5309 }
|
rob@100
|
5310 }
|
rob@100
|
5311
|
rob@100
|
5312 return xmlelement;
|
rob@100
|
5313 },
|
rob@100
|
5314 /**
|
rob@100
|
5315 * @member XMLElement
|
rob@100
|
5316 * The createElement() function Creates an empty element
|
rob@100
|
5317 *
|
rob@100
|
5318 * @param {String} fullName the full name of the element
|
rob@100
|
5319 * @param {String} namespace the namespace URI
|
rob@100
|
5320 * @param {String} systemID the system ID of the XML data where the element starts
|
rob@100
|
5321 * @param {int} lineNr the line in the XML data where the element starts
|
rob@100
|
5322 */
|
rob@100
|
5323 createElement: function (fullname, namespaceuri, sysid, line) {
|
rob@100
|
5324 if (sysid === undef) {
|
rob@100
|
5325 return new XMLElement(fullname, namespaceuri);
|
rob@100
|
5326 }
|
rob@100
|
5327 return new XMLElement(fullname, namespaceuri, sysid, line);
|
rob@100
|
5328 },
|
rob@100
|
5329 /**
|
rob@100
|
5330 * @member XMLElement
|
rob@100
|
5331 * The createPCDataElement() function creates an element to be used for #PCDATA content.
|
rob@100
|
5332 * Because Processing discards whitespace TEXT nodes, this method will not build an element
|
rob@100
|
5333 * if the passed content is empty after trimming for whitespace.
|
rob@100
|
5334 *
|
rob@100
|
5335 * @return {XMLElement} new "pcdata" XMLElement, or null if content consists only of whitespace
|
rob@100
|
5336 */
|
rob@100
|
5337 createPCDataElement: function (content, isCDATA) {
|
rob@100
|
5338 if (content.replace(/^\s+$/g,"") === "") {
|
rob@100
|
5339 return null;
|
rob@100
|
5340 }
|
rob@100
|
5341 var pcdata = new XMLElement();
|
rob@100
|
5342 pcdata.type = "TEXT";
|
rob@100
|
5343 pcdata.content = content;
|
rob@100
|
5344 return pcdata;
|
rob@100
|
5345 },
|
rob@100
|
5346 /**
|
rob@100
|
5347 * @member XMLElement
|
rob@100
|
5348 * The createCDataElement() function creates an element to be used for CDATA content.
|
rob@100
|
5349 *
|
rob@100
|
5350 * @return {XMLElement} new "cdata" XMLElement, or null if content consists only of whitespace
|
rob@100
|
5351 */
|
rob@100
|
5352 createCDataElement: function (content) {
|
rob@100
|
5353 var cdata = this.createPCDataElement(content);
|
rob@100
|
5354 if (cdata === null) {
|
rob@100
|
5355 return null;
|
rob@100
|
5356 }
|
rob@100
|
5357
|
rob@100
|
5358 cdata.type = "CDATA";
|
rob@100
|
5359 var htmlentities = {"<": "<", ">": ">", "'": "'", '"': """},
|
rob@100
|
5360 entity;
|
rob@100
|
5361 for (entity in htmlentities) {
|
rob@100
|
5362 if (!Object.hasOwnProperty(htmlentities,entity)) {
|
rob@100
|
5363 content = content.replace(new RegExp(entity, "g"), htmlentities[entity]);
|
rob@100
|
5364 }
|
rob@100
|
5365 }
|
rob@100
|
5366 cdata.cdata = content;
|
rob@100
|
5367 return cdata;
|
rob@100
|
5368 },
|
rob@100
|
5369 /**
|
rob@100
|
5370 * @member XMLElement
|
rob@100
|
5371 * The hasAttribute() function returns whether an attribute exists
|
rob@100
|
5372 *
|
rob@100
|
5373 * @param {String} name name of the attribute
|
rob@100
|
5374 * @param {String} namespace the namespace URI of the attribute
|
rob@100
|
5375 *
|
rob@100
|
5376 * @return {boolean} true if the attribute exists
|
rob@100
|
5377 */
|
rob@100
|
5378 hasAttribute: function () {
|
rob@100
|
5379 if (arguments.length === 1) {
|
rob@100
|
5380 return this.getAttribute(arguments[0]) !== null;
|
rob@100
|
5381 }
|
rob@100
|
5382 if (arguments.length === 2) {
|
rob@100
|
5383 return this.getAttribute(arguments[0],arguments[1]) !== null;
|
rob@100
|
5384 }
|
rob@100
|
5385 },
|
rob@100
|
5386 /**
|
rob@100
|
5387 * @member XMLElement
|
rob@100
|
5388 * The equals() function checks to see if the XMLElement being passed in equals another XMLElement
|
rob@100
|
5389 *
|
rob@100
|
5390 * @param {XMLElement} rawElement the element to compare to
|
rob@100
|
5391 *
|
rob@100
|
5392 * @return {boolean} true if the element equals another element
|
rob@100
|
5393 */
|
rob@100
|
5394 equals: function(other) {
|
rob@100
|
5395 if (!(other instanceof XMLElement)) {
|
rob@100
|
5396 return false;
|
rob@100
|
5397 }
|
rob@100
|
5398 var i, j;
|
rob@100
|
5399 if (this.fullName !== other.fullName) { return false; }
|
rob@100
|
5400 if (this.attributes.length !== other.getAttributeCount()) { return false; }
|
rob@100
|
5401 // attributes may be ordered differently
|
rob@100
|
5402 if (this.attributes.length !== other.attributes.length) { return false; }
|
rob@100
|
5403 var attr_name, attr_ns, attr_value, attr_type, attr_other;
|
rob@100
|
5404 for (i = 0, j = this.attributes.length; i < j; i++) {
|
rob@100
|
5405 attr_name = this.attributes[i].getName();
|
rob@100
|
5406 attr_ns = this.attributes[i].getNamespace();
|
rob@100
|
5407 attr_other = other.findAttribute(attr_name, attr_ns);
|
rob@100
|
5408 if (attr_other === null) { return false; }
|
rob@100
|
5409 if (this.attributes[i].getValue() !== attr_other.getValue()) { return false; }
|
rob@100
|
5410 if (this.attributes[i].getType() !== attr_other.getType()) { return false; }
|
rob@100
|
5411 }
|
rob@100
|
5412 // children must be ordered identically
|
rob@100
|
5413 if (this.children.length !== other.getChildCount()) { return false; }
|
rob@100
|
5414 if (this.children.length>0) {
|
rob@100
|
5415 var child1, child2;
|
rob@100
|
5416 for (i = 0, j = this.children.length; i < j; i++) {
|
rob@100
|
5417 child1 = this.getChild(i);
|
rob@100
|
5418 child2 = other.getChild(i);
|
rob@100
|
5419 if (!child1.equals(child2)) { return false; }
|
rob@100
|
5420 }
|
rob@100
|
5421 return true;
|
rob@100
|
5422 }
|
rob@100
|
5423 return (this.content === other.content);
|
rob@100
|
5424 },
|
rob@100
|
5425 /**
|
rob@100
|
5426 * @member XMLElement
|
rob@100
|
5427 * The getContent() function returns the content of an element. If there is no such content, null is returned
|
rob@100
|
5428 *
|
rob@100
|
5429 * @return {String} the (possibly null) content
|
rob@100
|
5430 */
|
rob@100
|
5431 getContent: function(){
|
rob@100
|
5432 if (this.type === "TEXT" || this.type === "CDATA") {
|
rob@100
|
5433 return this.content;
|
rob@100
|
5434 }
|
rob@100
|
5435 var children = this.children;
|
rob@100
|
5436 if (children.length === 1 && (children[0].type === "TEXT" || children[0].type === "CDATA")) {
|
rob@100
|
5437 return children[0].content;
|
rob@100
|
5438 }
|
rob@100
|
5439 return null;
|
rob@100
|
5440 },
|
rob@100
|
5441 /**
|
rob@100
|
5442 * @member XMLElement
|
rob@100
|
5443 * The getAttribute() function returns the value of an attribute
|
rob@100
|
5444 *
|
rob@100
|
5445 * @param {String} name the non-null full name of the attribute
|
rob@100
|
5446 * @param {String} namespace the namespace URI, which may be null
|
rob@100
|
5447 * @param {String} defaultValue the default value of the attribute
|
rob@100
|
5448 *
|
rob@100
|
5449 * @return {String} the value, or defaultValue if the attribute does not exist
|
rob@100
|
5450 */
|
rob@100
|
5451 getAttribute: function (){
|
rob@100
|
5452 var attribute;
|
rob@100
|
5453 if (arguments.length === 2) {
|
rob@100
|
5454 attribute = this.findAttribute(arguments[0]);
|
rob@100
|
5455 if (attribute) {
|
rob@100
|
5456 return attribute.getValue();
|
rob@100
|
5457 }
|
rob@100
|
5458 return arguments[1];
|
rob@100
|
5459 } else if (arguments.length === 1) {
|
rob@100
|
5460 attribute = this.findAttribute(arguments[0]);
|
rob@100
|
5461 if (attribute) {
|
rob@100
|
5462 return attribute.getValue();
|
rob@100
|
5463 }
|
rob@100
|
5464 return null;
|
rob@100
|
5465 } else if (arguments.length === 3) {
|
rob@100
|
5466 attribute = this.findAttribute(arguments[0],arguments[1]);
|
rob@100
|
5467 if (attribute) {
|
rob@100
|
5468 return attribute.getValue();
|
rob@100
|
5469 }
|
rob@100
|
5470 return arguments[2];
|
rob@100
|
5471 }
|
rob@100
|
5472 },
|
rob@100
|
5473 /**
|
rob@100
|
5474 * @member XMLElement
|
rob@100
|
5475 * The getStringAttribute() function returns the string attribute of the element
|
rob@100
|
5476 * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
|
rob@100
|
5477 * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
|
rob@100
|
5478 *
|
rob@100
|
5479 * @param name the name of the attribute
|
rob@100
|
5480 * @param defaultValue value returned if the attribute is not found
|
rob@100
|
5481 *
|
rob@100
|
5482 * @return {String} the value, or defaultValue if the attribute does not exist
|
rob@100
|
5483 */
|
rob@100
|
5484 getStringAttribute: function() {
|
rob@100
|
5485 if (arguments.length === 1) {
|
rob@100
|
5486 return this.getAttribute(arguments[0]);
|
rob@100
|
5487 }
|
rob@100
|
5488 if (arguments.length === 2) {
|
rob@100
|
5489 return this.getAttribute(arguments[0], arguments[1]);
|
rob@100
|
5490 }
|
rob@100
|
5491 return this.getAttribute(arguments[0], arguments[1],arguments[2]);
|
rob@100
|
5492 },
|
rob@100
|
5493 /**
|
rob@100
|
5494 * Processing 1.5 XML API wrapper for the generic String
|
rob@100
|
5495 * attribute getter. This may only take one argument.
|
rob@100
|
5496 */
|
rob@100
|
5497 getString: function(attributeName) {
|
rob@100
|
5498 return this.getStringAttribute(attributeName);
|
rob@100
|
5499 },
|
rob@100
|
5500 /**
|
rob@100
|
5501 * @member XMLElement
|
rob@100
|
5502 * The getFloatAttribute() function returns the float attribute of the element.
|
rob@100
|
5503 * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
|
rob@100
|
5504 * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
|
rob@100
|
5505 *
|
rob@100
|
5506 * @param name the name of the attribute
|
rob@100
|
5507 * @param defaultValue value returned if the attribute is not found
|
rob@100
|
5508 *
|
rob@100
|
5509 * @return {float} the value, or defaultValue if the attribute does not exist
|
rob@100
|
5510 */
|
rob@100
|
5511 getFloatAttribute: function() {
|
rob@100
|
5512 if (arguments.length === 1 ) {
|
rob@100
|
5513 return parseFloat(this.getAttribute(arguments[0], 0));
|
rob@100
|
5514 }
|
rob@100
|
5515 if (arguments.length === 2 ) {
|
rob@100
|
5516 return this.getAttribute(arguments[0], arguments[1]);
|
rob@100
|
5517 }
|
rob@100
|
5518 return this.getAttribute(arguments[0], arguments[1],arguments[2]);
|
rob@100
|
5519 },
|
rob@100
|
5520 /**
|
rob@100
|
5521 * Processing 1.5 XML API wrapper for the generic float
|
rob@100
|
5522 * attribute getter. This may only take one argument.
|
rob@100
|
5523 */
|
rob@100
|
5524 getFloat: function(attributeName) {
|
rob@100
|
5525 return this.getFloatAttribute(attributeName);
|
rob@100
|
5526 },
|
rob@100
|
5527 /**
|
rob@100
|
5528 * @member XMLElement
|
rob@100
|
5529 * The getIntAttribute() function returns the integer attribute of the element.
|
rob@100
|
5530 * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
|
rob@100
|
5531 * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
|
rob@100
|
5532 *
|
rob@100
|
5533 * @param name the name of the attribute
|
rob@100
|
5534 * @param defaultValue value returned if the attribute is not found
|
rob@100
|
5535 *
|
rob@100
|
5536 * @return {int} the value, or defaultValue if the attribute does not exist
|
rob@100
|
5537 */
|
rob@100
|
5538 getIntAttribute: function () {
|
rob@100
|
5539 if (arguments.length === 1) {
|
rob@100
|
5540 return this.getAttribute( arguments[0], 0 );
|
rob@100
|
5541 }
|
rob@100
|
5542 if (arguments.length === 2) {
|
rob@100
|
5543 return this.getAttribute(arguments[0], arguments[1]);
|
rob@100
|
5544 }
|
rob@100
|
5545 return this.getAttribute(arguments[0], arguments[1],arguments[2]);
|
rob@100
|
5546 },
|
rob@100
|
5547 /**
|
rob@100
|
5548 * Processing 1.5 XML API wrapper for the generic int
|
rob@100
|
5549 * attribute getter. This may only take one argument.
|
rob@100
|
5550 */
|
rob@100
|
5551 getInt: function(attributeName) {
|
rob@100
|
5552 return this.getIntAttribute(attributeName);
|
rob@100
|
5553 },
|
rob@100
|
5554 /**
|
rob@100
|
5555 * @member XMLElement
|
rob@100
|
5556 * The hasChildren() function returns whether the element has children.
|
rob@100
|
5557 *
|
rob@100
|
5558 * @return {boolean} true if the element has children.
|
rob@100
|
5559 */
|
rob@100
|
5560 hasChildren: function () {
|
rob@100
|
5561 return this.children.length > 0 ;
|
rob@100
|
5562 },
|
rob@100
|
5563 /**
|
rob@100
|
5564 * @member XMLElement
|
rob@100
|
5565 * The addChild() function adds a child element
|
rob@100
|
5566 *
|
rob@100
|
5567 * @param {XMLElement} child the non-null child to add.
|
rob@100
|
5568 */
|
rob@100
|
5569 addChild: function (child) {
|
rob@100
|
5570 if (child !== null) {
|
rob@100
|
5571 child.parent = this;
|
rob@100
|
5572 this.children.push(child);
|
rob@100
|
5573 }
|
rob@100
|
5574 },
|
rob@100
|
5575 /**
|
rob@100
|
5576 * @member XMLElement
|
rob@100
|
5577 * The insertChild() function inserts a child element at the index provided
|
rob@100
|
5578 *
|
rob@100
|
5579 * @param {XMLElement} child the non-null child to add.
|
rob@100
|
5580 * @param {int} index where to put the child.
|
rob@100
|
5581 */
|
rob@100
|
5582 insertChild: function (child, index) {
|
rob@100
|
5583 if (child) {
|
rob@100
|
5584 if ((child.getLocalName() === null) && (! this.hasChildren())) {
|
rob@100
|
5585 var lastChild = this.children[this.children.length -1];
|
rob@100
|
5586 if (lastChild.getLocalName() === null) {
|
rob@100
|
5587 lastChild.setContent(lastChild.getContent() + child.getContent());
|
rob@100
|
5588 return;
|
rob@100
|
5589 }
|
rob@100
|
5590 }
|
rob@100
|
5591 child.parent = this;
|
rob@100
|
5592 this.children.splice(index,0,child);
|
rob@100
|
5593 }
|
rob@100
|
5594 },
|
rob@100
|
5595 /**
|
rob@100
|
5596 * @member XMLElement
|
rob@100
|
5597 * The getChild() returns the child XMLElement as specified by the <b>index</b> parameter.
|
rob@100
|
5598 * The value of the <b>index</b> parameter must be less than the total number of children to avoid going out of the array storing the child elements.
|
rob@100
|
5599 * When the <b>path</b> parameter is specified, then it will return all children that match that path. The path is a series of elements and sub-elements, separated by slashes.
|
rob@100
|
5600 *
|
rob@100
|
5601 * @param {int} index where to put the child.
|
rob@100
|
5602 * @param {String} path path to a particular element
|
rob@100
|
5603 *
|
rob@100
|
5604 * @return {XMLElement} the element
|
rob@100
|
5605 */
|
rob@100
|
5606 getChild: function (selector) {
|
rob@100
|
5607 if (typeof selector === "number") {
|
rob@100
|
5608 return this.children[selector];
|
rob@100
|
5609 }
|
rob@100
|
5610 if (selector.indexOf('/') !== -1) {
|
rob@100
|
5611 // path traversal is required
|
rob@100
|
5612 return this.getChildRecursive(selector.split("/"), 0);
|
rob@100
|
5613 }
|
rob@100
|
5614 var kid, kidName;
|
rob@100
|
5615 for (var i = 0, j = this.getChildCount(); i < j; i++) {
|
rob@100
|
5616 kid = this.getChild(i);
|
rob@100
|
5617 kidName = kid.getName();
|
rob@100
|
5618 if (kidName !== null && kidName === selector) {
|
rob@100
|
5619 return kid;
|
rob@100
|
5620 }
|
rob@100
|
5621 }
|
rob@100
|
5622 return null;
|
rob@100
|
5623 },
|
rob@100
|
5624 /**
|
rob@100
|
5625 * @member XMLElement
|
rob@100
|
5626 * The getChildren() returns all of the children as an XMLElement array.
|
rob@100
|
5627 * When the <b>path</b> parameter is specified, then it will return all children that match that path.
|
rob@100
|
5628 * The path is a series of elements and sub-elements, separated by slashes.
|
rob@100
|
5629 *
|
rob@100
|
5630 * @param {String} path element name or path/to/element
|
rob@100
|
5631 *
|
rob@100
|
5632 * @return {XMLElement} array of child elements that match
|
rob@100
|
5633 *
|
rob@100
|
5634 * @see XMLElement#getChildCount()
|
rob@100
|
5635 * @see XMLElement#getChild()
|
rob@100
|
5636 */
|
rob@100
|
5637 getChildren: function(){
|
rob@100
|
5638 if (arguments.length === 1) {
|
rob@100
|
5639 if (typeof arguments[0] === "number") {
|
rob@100
|
5640 return this.getChild( arguments[0]);
|
rob@100
|
5641 }
|
rob@100
|
5642 if (arguments[0].indexOf('/') !== -1) { // path was given
|
rob@100
|
5643 return this.getChildrenRecursive( arguments[0].split("/"), 0);
|
rob@100
|
5644 }
|
rob@100
|
5645 var matches = [];
|
rob@100
|
5646 var kid, kidName;
|
rob@100
|
5647 for (var i = 0, j = this.getChildCount(); i < j; i++) {
|
rob@100
|
5648 kid = this.getChild(i);
|
rob@100
|
5649 kidName = kid.getName();
|
rob@100
|
5650 if (kidName !== null && kidName === arguments[0]) {
|
rob@100
|
5651 matches.push(kid);
|
rob@100
|
5652 }
|
rob@100
|
5653 }
|
rob@100
|
5654 return matches;
|
rob@100
|
5655 }
|
rob@100
|
5656 return this.children;
|
rob@100
|
5657 },
|
rob@100
|
5658 /**
|
rob@100
|
5659 * @member XMLElement
|
rob@100
|
5660 * The getChildCount() returns the number of children for the element.
|
rob@100
|
5661 *
|
rob@100
|
5662 * @return {int} the count
|
rob@100
|
5663 *
|
rob@100
|
5664 * @see XMLElement#getChild()
|
rob@100
|
5665 * @see XMLElement#getChildren()
|
rob@100
|
5666 */
|
rob@100
|
5667 getChildCount: function() {
|
rob@100
|
5668 return this.children.length;
|
rob@100
|
5669 },
|
rob@100
|
5670 /**
|
rob@100
|
5671 * @member XMLElement
|
rob@100
|
5672 * Internal helper function for getChild().
|
rob@100
|
5673 *
|
rob@100
|
5674 * @param {String[]} items result of splitting the query on slashes
|
rob@100
|
5675 * @param {int} offset where in the items[] array we're currently looking
|
rob@100
|
5676 *
|
rob@100
|
5677 * @return {XMLElement} matching element or null if no match
|
rob@100
|
5678 */
|
rob@100
|
5679 getChildRecursive: function (items, offset) {
|
rob@100
|
5680 // terminating clause: we are the requested candidate
|
rob@100
|
5681 if (offset === items.length) {
|
rob@100
|
5682 return this;
|
rob@100
|
5683 }
|
rob@100
|
5684 // continuation clause
|
rob@100
|
5685 var kid, kidName, matchName = items[offset];
|
rob@100
|
5686 for(var i = 0, j = this.getChildCount(); i < j; i++) {
|
rob@100
|
5687 kid = this.getChild(i);
|
rob@100
|
5688 kidName = kid.getName();
|
rob@100
|
5689 if (kidName !== null && kidName === matchName) {
|
rob@100
|
5690 return kid.getChildRecursive(items, offset+1);
|
rob@100
|
5691 }
|
rob@100
|
5692 }
|
rob@100
|
5693 return null;
|
rob@100
|
5694 },
|
rob@100
|
5695 /**
|
rob@100
|
5696 * @member XMLElement
|
rob@100
|
5697 * Internal helper function for getChildren().
|
rob@100
|
5698 *
|
rob@100
|
5699 * @param {String[]} items result of splitting the query on slashes
|
rob@100
|
5700 * @param {int} offset where in the items[] array we're currently looking
|
rob@100
|
5701 *
|
rob@100
|
5702 * @return {XMLElement[]} matching elements or empty array if no match
|
rob@100
|
5703 */
|
rob@100
|
5704 getChildrenRecursive: function (items, offset) {
|
rob@100
|
5705 if (offset === items.length-1) {
|
rob@100
|
5706 return this.getChildren(items[offset]);
|
rob@100
|
5707 }
|
rob@100
|
5708 var matches = this.getChildren(items[offset]);
|
rob@100
|
5709 var kidMatches = [];
|
rob@100
|
5710 for (var i = 0; i < matches.length; i++) {
|
rob@100
|
5711 kidMatches = kidMatches.concat(matches[i].getChildrenRecursive(items, offset+1));
|
rob@100
|
5712 }
|
rob@100
|
5713 return kidMatches;
|
rob@100
|
5714 },
|
rob@100
|
5715 /**
|
rob@100
|
5716 * @member XMLElement
|
rob@100
|
5717 * The isLeaf() function returns whether the element is a leaf element.
|
rob@100
|
5718 *
|
rob@100
|
5719 * @return {boolean} true if the element has no children.
|
rob@100
|
5720 */
|
rob@100
|
5721 isLeaf: function() {
|
rob@100
|
5722 return !this.hasChildren();
|
rob@100
|
5723 },
|
rob@100
|
5724 /**
|
rob@100
|
5725 * @member XMLElement
|
rob@100
|
5726 * The listChildren() function put the names of all children into an array. Same as looping through
|
rob@100
|
5727 * each child and calling getName() on each XMLElement.
|
rob@100
|
5728 *
|
rob@100
|
5729 * @return {String[]} a list of element names.
|
rob@100
|
5730 */
|
rob@100
|
5731 listChildren: function() {
|
rob@100
|
5732 var arr = [];
|
rob@100
|
5733 for (var i = 0, j = this.children.length; i < j; i++) {
|
rob@100
|
5734 arr.push( this.getChild(i).getName());
|
rob@100
|
5735 }
|
rob@100
|
5736 return arr;
|
rob@100
|
5737 },
|
rob@100
|
5738 /**
|
rob@100
|
5739 * @member XMLElement
|
rob@100
|
5740 * The removeAttribute() function removes an attribute
|
rob@100
|
5741 *
|
rob@100
|
5742 * @param {String} name the non-null name of the attribute.
|
rob@100
|
5743 * @param {String} namespace the namespace URI of the attribute, which may be null.
|
rob@100
|
5744 */
|
rob@100
|
5745 removeAttribute: function (name , namespace) {
|
rob@100
|
5746 this.namespace = namespace || "";
|
rob@100
|
5747 for (var i = 0, j = this.attributes.length; i < j; i++) {
|
rob@100
|
5748 if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
|
rob@100
|
5749 this.attributes.splice(i, 1);
|
rob@100
|
5750 break;
|
rob@100
|
5751 }
|
rob@100
|
5752 }
|
rob@100
|
5753 },
|
rob@100
|
5754 /**
|
rob@100
|
5755 * @member XMLElement
|
rob@100
|
5756 * The removeChild() removes a child element.
|
rob@100
|
5757 *
|
rob@100
|
5758 * @param {XMLElement} child the the non-null child to be renoved
|
rob@100
|
5759 */
|
rob@100
|
5760 removeChild: function(child) {
|
rob@100
|
5761 if (child) {
|
rob@100
|
5762 for (var i = 0, j = this.children.length; i < j; i++) {
|
rob@100
|
5763 if (this.children[i].equals(child)) {
|
rob@100
|
5764 this.children.splice(i, 1);
|
rob@100
|
5765 break;
|
rob@100
|
5766 }
|
rob@100
|
5767 }
|
rob@100
|
5768 }
|
rob@100
|
5769 },
|
rob@100
|
5770 /**
|
rob@100
|
5771 * @member XMLElement
|
rob@100
|
5772 * The removeChildAtIndex() removes the child located at a certain index
|
rob@100
|
5773 *
|
rob@100
|
5774 * @param {int} index the index of the child, where the first child has index 0
|
rob@100
|
5775 */
|
rob@100
|
5776 removeChildAtIndex: function(index) {
|
rob@100
|
5777 if (this.children.length > index) { //make sure its not outofbounds
|
rob@100
|
5778 this.children.splice(index, 1);
|
rob@100
|
5779 }
|
rob@100
|
5780 },
|
rob@100
|
5781 /**
|
rob@100
|
5782 * @member XMLElement
|
rob@100
|
5783 * The findAttribute() function searches an attribute
|
rob@100
|
5784 *
|
rob@100
|
5785 * @param {String} name fullName the non-null full name of the attribute
|
rob@100
|
5786 * @param {String} namespace the name space, which may be null
|
rob@100
|
5787 *
|
rob@100
|
5788 * @return {XMLAttribute} the attribute, or null if the attribute does not exist.
|
rob@100
|
5789 */
|
rob@100
|
5790 findAttribute: function (name, namespace) {
|
rob@100
|
5791 this.namespace = namespace || "";
|
rob@100
|
5792 for (var i = 0, j = this.attributes.length; i < j; i++) {
|
rob@100
|
5793 if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
|
rob@100
|
5794 return this.attributes[i];
|
rob@100
|
5795 }
|
rob@100
|
5796 }
|
rob@100
|
5797 return null;
|
rob@100
|
5798 },
|
rob@100
|
5799 /**
|
rob@100
|
5800 * @member XMLElement
|
rob@100
|
5801 * The setAttribute() function sets an attribute.
|
rob@100
|
5802 *
|
rob@100
|
5803 * @param {String} name the non-null full name of the attribute
|
rob@100
|
5804 * @param {String} namespace the non-null value of the attribute
|
rob@100
|
5805 */
|
rob@100
|
5806 setAttribute: function() {
|
rob@100
|
5807 var attr;
|
rob@100
|
5808 if (arguments.length === 3) {
|
rob@100
|
5809 var index = arguments[0].indexOf(':');
|
rob@100
|
5810 var name = arguments[0].substring(index + 1);
|
rob@100
|
5811 attr = this.findAttribute(name, arguments[1]);
|
rob@100
|
5812 if (attr) {
|
rob@100
|
5813 attr.setValue(arguments[2]);
|
rob@100
|
5814 } else {
|
rob@100
|
5815 attr = new XMLAttribute(arguments[0], name, arguments[1], arguments[2], "CDATA");
|
rob@100
|
5816 this.attributes.push(attr);
|
rob@100
|
5817 }
|
rob@100
|
5818 } else {
|
rob@100
|
5819 attr = this.findAttribute(arguments[0]);
|
rob@100
|
5820 if (attr) {
|
rob@100
|
5821 attr.setValue(arguments[1]);
|
rob@100
|
5822 } else {
|
rob@100
|
5823 attr = new XMLAttribute(arguments[0], arguments[0], null, arguments[1], "CDATA");
|
rob@100
|
5824 this.attributes.push(attr);
|
rob@100
|
5825 }
|
rob@100
|
5826 }
|
rob@100
|
5827 },
|
rob@100
|
5828 /**
|
rob@100
|
5829 * Processing 1.5 XML API wrapper for the generic String
|
rob@100
|
5830 * attribute setter. This must take two arguments.
|
rob@100
|
5831 */
|
rob@100
|
5832 setString: function(attribute, value) {
|
rob@100
|
5833 this.setAttribute(attribute, value);
|
rob@100
|
5834 },
|
rob@100
|
5835 /**
|
rob@100
|
5836 * Processing 1.5 XML API wrapper for the generic int
|
rob@100
|
5837 * attribute setter. This must take two arguments.
|
rob@100
|
5838 */
|
rob@100
|
5839 setInt: function(attribute, value) {
|
rob@100
|
5840 this.setAttribute(attribute, value);
|
rob@100
|
5841 },
|
rob@100
|
5842 /**
|
rob@100
|
5843 * Processing 1.5 XML API wrapper for the generic float
|
rob@100
|
5844 * attribute setter. This must take two arguments.
|
rob@100
|
5845 */
|
rob@100
|
5846 setFloat: function(attribute, value) {
|
rob@100
|
5847 this.setAttribute(attribute, value);
|
rob@100
|
5848 },
|
rob@100
|
5849 /**
|
rob@100
|
5850 * @member XMLElement
|
rob@100
|
5851 * The setContent() function sets the #PCDATA content. It is an error to call this method with a
|
rob@100
|
5852 * non-null value if there are child objects.
|
rob@100
|
5853 *
|
rob@100
|
5854 * @param {String} content the (possibly null) content
|
rob@100
|
5855 */
|
rob@100
|
5856 setContent: function(content) {
|
rob@100
|
5857 if (this.children.length > 0) {
|
rob@100
|
5858 Processing.debug("Tried to set content for XMLElement with children"); }
|
rob@100
|
5859 this.content = content;
|
rob@100
|
5860 },
|
rob@100
|
5861 /**
|
rob@100
|
5862 * @member XMLElement
|
rob@100
|
5863 * The setName() function sets the full name. This method also sets the short name and clears the
|
rob@100
|
5864 * namespace URI.
|
rob@100
|
5865 *
|
rob@100
|
5866 * @param {String} name the non-null name
|
rob@100
|
5867 * @param {String} namespace the namespace URI, which may be null.
|
rob@100
|
5868 */
|
rob@100
|
5869 setName: function() {
|
rob@100
|
5870 if (arguments.length === 1) {
|
rob@100
|
5871 this.name = arguments[0];
|
rob@100
|
5872 this.fullName = arguments[0];
|
rob@100
|
5873 this.namespace = null;
|
rob@100
|
5874 } else {
|
rob@100
|
5875 var index = arguments[0].indexOf(':');
|
rob@100
|
5876 if ((arguments[1] === null) || (index < 0)) {
|
rob@100
|
5877 this.name = arguments[0];
|
rob@100
|
5878 } else {
|
rob@100
|
5879 this.name = arguments[0].substring(index + 1);
|
rob@100
|
5880 }
|
rob@100
|
5881 this.fullName = arguments[0];
|
rob@100
|
5882 this.namespace = arguments[1];
|
rob@100
|
5883 }
|
rob@100
|
5884 },
|
rob@100
|
5885 /**
|
rob@100
|
5886 * @member XMLElement
|
rob@100
|
5887 * The getName() function returns the full name (i.e. the name including an eventual namespace
|
rob@100
|
5888 * prefix) of the element.
|
rob@100
|
5889 *
|
rob@100
|
5890 * @return {String} the name, or null if the element only contains #PCDATA.
|
rob@100
|
5891 */
|
rob@100
|
5892 getName: function() {
|
rob@100
|
5893 return this.fullName;
|
rob@100
|
5894 },
|
rob@100
|
5895 /**
|
rob@100
|
5896 * @member XMLElement
|
rob@100
|
5897 * The getLocalName() function returns the local name (i.e. the name excluding an eventual namespace
|
rob@100
|
5898 * prefix) of the element.
|
rob@100
|
5899 *
|
rob@100
|
5900 * @return {String} the name, or null if the element only contains #PCDATA.
|
rob@100
|
5901 */
|
rob@100
|
5902 getLocalName: function() {
|
rob@100
|
5903 return this.name;
|
rob@100
|
5904 },
|
rob@100
|
5905 /**
|
rob@100
|
5906 * @member XMLElement
|
rob@100
|
5907 * The getAttributeCount() function returns the number of attributes for the node
|
rob@100
|
5908 * that this XMLElement represents.
|
rob@100
|
5909 *
|
rob@100
|
5910 * @return {int} the number of attributes in this XMLElement
|
rob@100
|
5911 */
|
rob@100
|
5912 getAttributeCount: function() {
|
rob@100
|
5913 return this.attributes.length;
|
rob@100
|
5914 },
|
rob@100
|
5915 /**
|
rob@100
|
5916 * @member XMLElement
|
rob@100
|
5917 * The toString() function returns the XML definition of an XMLElement.
|
rob@100
|
5918 *
|
rob@100
|
5919 * @return {String} the XML definition of this XMLElement
|
rob@100
|
5920 */
|
rob@100
|
5921 toString: function() {
|
rob@100
|
5922 // shortcut for text and cdata nodes
|
rob@100
|
5923 if (this.type === "TEXT") {
|
rob@100
|
5924 return this.content;
|
rob@100
|
5925 }
|
rob@100
|
5926
|
rob@100
|
5927 if (this.type === "CDATA") {
|
rob@100
|
5928 return this.cdata;
|
rob@100
|
5929 }
|
rob@100
|
5930
|
rob@100
|
5931 // real XMLElements
|
rob@100
|
5932 var tagstring = this.fullName;
|
rob@100
|
5933 var xmlstring = "<" + tagstring;
|
rob@100
|
5934 var a,c;
|
rob@100
|
5935
|
rob@100
|
5936 // serialize the attributes to XML string
|
rob@100
|
5937 for (a = 0; a<this.attributes.length; a++) {
|
rob@100
|
5938 var attr = this.attributes[a];
|
rob@100
|
5939 xmlstring += " " + attr.getName() + "=" + '"' + attr.getValue() + '"';
|
rob@100
|
5940 }
|
rob@100
|
5941
|
rob@100
|
5942 // serialize all children to XML string
|
rob@100
|
5943 if (this.children.length === 0) {
|
rob@100
|
5944 if (this.content==="") {
|
rob@100
|
5945 xmlstring += "/>";
|
rob@100
|
5946 } else {
|
rob@100
|
5947 xmlstring += ">" + this.content + "</"+tagstring+">";
|
rob@100
|
5948 }
|
rob@100
|
5949 } else {
|
rob@100
|
5950 xmlstring += ">";
|
rob@100
|
5951 for (c = 0; c<this.children.length; c++) {
|
rob@100
|
5952 xmlstring += this.children[c].toString();
|
rob@100
|
5953 }
|
rob@100
|
5954 xmlstring += "</" + tagstring + ">";
|
rob@100
|
5955 }
|
rob@100
|
5956 return xmlstring;
|
rob@100
|
5957 }
|
rob@100
|
5958 };
|
rob@100
|
5959
|
rob@100
|
5960 /**
|
rob@100
|
5961 * static Processing 1.5 XML API wrapper for the
|
rob@100
|
5962 * parse method. This may only take one argument.
|
rob@100
|
5963 */
|
rob@100
|
5964 XMLElement.parse = function(xmlstring) {
|
rob@100
|
5965 var element = new XMLElement();
|
rob@100
|
5966 element.parse(xmlstring);
|
rob@100
|
5967 return element;
|
rob@100
|
5968 };
|
rob@100
|
5969
|
rob@100
|
5970 return XMLElement;
|
rob@100
|
5971 };
|
rob@100
|
5972
|
rob@100
|
5973 },{}],20:[function(require,module,exports){
|
rob@100
|
5974 /**
|
rob@100
|
5975 * web colors, by name
|
rob@100
|
5976 */
|
rob@100
|
5977 module.exports = {
|
rob@100
|
5978 aliceblue: "#f0f8ff",
|
rob@100
|
5979 antiquewhite: "#faebd7",
|
rob@100
|
5980 aqua: "#00ffff",
|
rob@100
|
5981 aquamarine: "#7fffd4",
|
rob@100
|
5982 azure: "#f0ffff",
|
rob@100
|
5983 beige: "#f5f5dc",
|
rob@100
|
5984 bisque: "#ffe4c4",
|
rob@100
|
5985 black: "#000000",
|
rob@100
|
5986 blanchedalmond: "#ffebcd",
|
rob@100
|
5987 blue: "#0000ff",
|
rob@100
|
5988 blueviolet: "#8a2be2",
|
rob@100
|
5989 brown: "#a52a2a",
|
rob@100
|
5990 burlywood: "#deb887",
|
rob@100
|
5991 cadetblue: "#5f9ea0",
|
rob@100
|
5992 chartreuse: "#7fff00",
|
rob@100
|
5993 chocolate: "#d2691e",
|
rob@100
|
5994 coral: "#ff7f50",
|
rob@100
|
5995 cornflowerblue: "#6495ed",
|
rob@100
|
5996 cornsilk: "#fff8dc",
|
rob@100
|
5997 crimson: "#dc143c",
|
rob@100
|
5998 cyan: "#00ffff",
|
rob@100
|
5999 darkblue: "#00008b",
|
rob@100
|
6000 darkcyan: "#008b8b",
|
rob@100
|
6001 darkgoldenrod: "#b8860b",
|
rob@100
|
6002 darkgray: "#a9a9a9",
|
rob@100
|
6003 darkgreen: "#006400",
|
rob@100
|
6004 darkkhaki: "#bdb76b",
|
rob@100
|
6005 darkmagenta: "#8b008b",
|
rob@100
|
6006 darkolivegreen: "#556b2f",
|
rob@100
|
6007 darkorange: "#ff8c00",
|
rob@100
|
6008 darkorchid: "#9932cc",
|
rob@100
|
6009 darkred: "#8b0000",
|
rob@100
|
6010 darksalmon: "#e9967a",
|
rob@100
|
6011 darkseagreen: "#8fbc8f",
|
rob@100
|
6012 darkslateblue: "#483d8b",
|
rob@100
|
6013 darkslategray: "#2f4f4f",
|
rob@100
|
6014 darkturquoise: "#00ced1",
|
rob@100
|
6015 darkviolet: "#9400d3",
|
rob@100
|
6016 deeppink: "#ff1493",
|
rob@100
|
6017 deepskyblue: "#00bfff",
|
rob@100
|
6018 dimgray: "#696969",
|
rob@100
|
6019 dodgerblue: "#1e90ff",
|
rob@100
|
6020 firebrick: "#b22222",
|
rob@100
|
6021 floralwhite: "#fffaf0",
|
rob@100
|
6022 forestgreen: "#228b22",
|
rob@100
|
6023 fuchsia: "#ff00ff",
|
rob@100
|
6024 gainsboro: "#dcdcdc",
|
rob@100
|
6025 ghostwhite: "#f8f8ff",
|
rob@100
|
6026 gold: "#ffd700",
|
rob@100
|
6027 goldenrod: "#daa520",
|
rob@100
|
6028 gray: "#808080",
|
rob@100
|
6029 green: "#008000",
|
rob@100
|
6030 greenyellow: "#adff2f",
|
rob@100
|
6031 honeydew: "#f0fff0",
|
rob@100
|
6032 hotpink: "#ff69b4",
|
rob@100
|
6033 indianred: "#cd5c5c",
|
rob@100
|
6034 indigo: "#4b0082",
|
rob@100
|
6035 ivory: "#fffff0",
|
rob@100
|
6036 khaki: "#f0e68c",
|
rob@100
|
6037 lavender: "#e6e6fa",
|
rob@100
|
6038 lavenderblush: "#fff0f5",
|
rob@100
|
6039 lawngreen: "#7cfc00",
|
rob@100
|
6040 lemonchiffon: "#fffacd",
|
rob@100
|
6041 lightblue: "#add8e6",
|
rob@100
|
6042 lightcoral: "#f08080",
|
rob@100
|
6043 lightcyan: "#e0ffff",
|
rob@100
|
6044 lightgoldenrodyellow: "#fafad2",
|
rob@100
|
6045 lightgrey: "#d3d3d3",
|
rob@100
|
6046 lightgreen: "#90ee90",
|
rob@100
|
6047 lightpink: "#ffb6c1",
|
rob@100
|
6048 lightsalmon: "#ffa07a",
|
rob@100
|
6049 lightseagreen: "#20b2aa",
|
rob@100
|
6050 lightskyblue: "#87cefa",
|
rob@100
|
6051 lightslategray: "#778899",
|
rob@100
|
6052 lightsteelblue: "#b0c4de",
|
rob@100
|
6053 lightyellow: "#ffffe0",
|
rob@100
|
6054 lime: "#00ff00",
|
rob@100
|
6055 limegreen: "#32cd32",
|
rob@100
|
6056 linen: "#faf0e6",
|
rob@100
|
6057 magenta: "#ff00ff",
|
rob@100
|
6058 maroon: "#800000",
|
rob@100
|
6059 mediumaquamarine: "#66cdaa",
|
rob@100
|
6060 mediumblue: "#0000cd",
|
rob@100
|
6061 mediumorchid: "#ba55d3",
|
rob@100
|
6062 mediumpurple: "#9370d8",
|
rob@100
|
6063 mediumseagreen: "#3cb371",
|
rob@100
|
6064 mediumslateblue: "#7b68ee",
|
rob@100
|
6065 mediumspringgreen: "#00fa9a",
|
rob@100
|
6066 mediumturquoise: "#48d1cc",
|
rob@100
|
6067 mediumvioletred: "#c71585",
|
rob@100
|
6068 midnightblue: "#191970",
|
rob@100
|
6069 mintcream: "#f5fffa",
|
rob@100
|
6070 mistyrose: "#ffe4e1",
|
rob@100
|
6071 moccasin: "#ffe4b5",
|
rob@100
|
6072 navajowhite: "#ffdead",
|
rob@100
|
6073 navy: "#000080",
|
rob@100
|
6074 oldlace: "#fdf5e6",
|
rob@100
|
6075 olive: "#808000",
|
rob@100
|
6076 olivedrab: "#6b8e23",
|
rob@100
|
6077 orange: "#ffa500",
|
rob@100
|
6078 orangered: "#ff4500",
|
rob@100
|
6079 orchid: "#da70d6",
|
rob@100
|
6080 palegoldenrod: "#eee8aa",
|
rob@100
|
6081 palegreen: "#98fb98",
|
rob@100
|
6082 paleturquoise: "#afeeee",
|
rob@100
|
6083 palevioletred: "#d87093",
|
rob@100
|
6084 papayawhip: "#ffefd5",
|
rob@100
|
6085 peachpuff: "#ffdab9",
|
rob@100
|
6086 peru: "#cd853f",
|
rob@100
|
6087 pink: "#ffc0cb",
|
rob@100
|
6088 plum: "#dda0dd",
|
rob@100
|
6089 powderblue: "#b0e0e6",
|
rob@100
|
6090 purple: "#800080",
|
rob@100
|
6091 red: "#ff0000",
|
rob@100
|
6092 rosybrown: "#bc8f8f",
|
rob@100
|
6093 royalblue: "#4169e1",
|
rob@100
|
6094 saddlebrown: "#8b4513",
|
rob@100
|
6095 salmon: "#fa8072",
|
rob@100
|
6096 sandybrown: "#f4a460",
|
rob@100
|
6097 seagreen: "#2e8b57",
|
rob@100
|
6098 seashell: "#fff5ee",
|
rob@100
|
6099 sienna: "#a0522d",
|
rob@100
|
6100 silver: "#c0c0c0",
|
rob@100
|
6101 skyblue: "#87ceeb",
|
rob@100
|
6102 slateblue: "#6a5acd",
|
rob@100
|
6103 slategray: "#708090",
|
rob@100
|
6104 snow: "#fffafa",
|
rob@100
|
6105 springgreen: "#00ff7f",
|
rob@100
|
6106 steelblue: "#4682b4",
|
rob@100
|
6107 tan: "#d2b48c",
|
rob@100
|
6108 teal: "#008080",
|
rob@100
|
6109 thistle: "#d8bfd8",
|
rob@100
|
6110 tomato: "#ff6347",
|
rob@100
|
6111 turquoise: "#40e0d0",
|
rob@100
|
6112 violet: "#ee82ee",
|
rob@100
|
6113 wheat: "#f5deb3",
|
rob@100
|
6114 white: "#ffffff",
|
rob@100
|
6115 whitesmoke: "#f5f5f5",
|
rob@100
|
6116 yellow: "#ffff00",
|
rob@100
|
6117 yellowgreen: "#9acd32"
|
rob@100
|
6118 };
|
rob@100
|
6119
|
rob@100
|
6120 },{}],21:[function(require,module,exports){
|
rob@100
|
6121 module.exports = function(virtHashCode, virtEquals, undef) {
|
rob@100
|
6122
|
rob@100
|
6123 return function withProxyFunctions(p, removeFirstArgument) {
|
rob@100
|
6124 /**
|
rob@100
|
6125 * The contains(string) function returns true if the string passed in the parameter
|
rob@100
|
6126 * is a substring of this string. It returns false if the string passed
|
rob@100
|
6127 * in the parameter is not a substring of this string.
|
rob@100
|
6128 *
|
rob@100
|
6129 * @param {String} The string to look for in the current string
|
rob@100
|
6130 *
|
rob@100
|
6131 * @return {boolean} returns true if this string contains
|
rob@100
|
6132 * the string passed as parameter. returns false, otherwise.
|
rob@100
|
6133 *
|
rob@100
|
6134 */
|
rob@100
|
6135 p.__contains = function (subject, subStr) {
|
rob@100
|
6136 if (typeof subject !== "string") {
|
rob@100
|
6137 return subject.contains.apply(subject, removeFirstArgument(arguments));
|
rob@100
|
6138 }
|
rob@100
|
6139 //Parameter is not null AND
|
rob@100
|
6140 //The type of the parameter is the same as this object (string)
|
rob@100
|
6141 //The javascript function that finds a substring returns 0 or higher
|
rob@100
|
6142 return (
|
rob@100
|
6143 (subject !== null) &&
|
rob@100
|
6144 (subStr !== null) &&
|
rob@100
|
6145 (typeof subStr === "string") &&
|
rob@100
|
6146 (subject.indexOf(subStr) > -1)
|
rob@100
|
6147 );
|
rob@100
|
6148 };
|
rob@100
|
6149
|
rob@100
|
6150 /**
|
rob@100
|
6151 * The __replaceAll() function searches all matches between a substring (or regular expression) and a string,
|
rob@100
|
6152 * and replaces the matched substring with a new substring
|
rob@100
|
6153 *
|
rob@100
|
6154 * @param {String} subject a substring
|
rob@100
|
6155 * @param {String} regex a substring or a regular expression
|
rob@100
|
6156 * @param {String} replace the string to replace the found value
|
rob@100
|
6157 *
|
rob@100
|
6158 * @return {String} returns result
|
rob@100
|
6159 *
|
rob@100
|
6160 * @see #match
|
rob@100
|
6161 */
|
rob@100
|
6162 p.__replaceAll = function(subject, regex, replacement) {
|
rob@100
|
6163 if (typeof subject !== "string") {
|
rob@100
|
6164 return subject.replaceAll.apply(subject, removeFirstArgument(arguments));
|
rob@100
|
6165 }
|
rob@100
|
6166
|
rob@100
|
6167 return subject.replace(new RegExp(regex, "g"), replacement);
|
rob@100
|
6168 };
|
rob@100
|
6169
|
rob@100
|
6170 /**
|
rob@100
|
6171 * The __replaceFirst() function searches first matche between a substring (or regular expression) and a string,
|
rob@100
|
6172 * and replaces the matched substring with a new substring
|
rob@100
|
6173 *
|
rob@100
|
6174 * @param {String} subject a substring
|
rob@100
|
6175 * @param {String} regex a substring or a regular expression
|
rob@100
|
6176 * @param {String} replace the string to replace the found value
|
rob@100
|
6177 *
|
rob@100
|
6178 * @return {String} returns result
|
rob@100
|
6179 *
|
rob@100
|
6180 * @see #match
|
rob@100
|
6181 */
|
rob@100
|
6182 p.__replaceFirst = function(subject, regex, replacement) {
|
rob@100
|
6183 if (typeof subject !== "string") {
|
rob@100
|
6184 return subject.replaceFirst.apply(subject, removeFirstArgument(arguments));
|
rob@100
|
6185 }
|
rob@100
|
6186
|
rob@100
|
6187 return subject.replace(new RegExp(regex, ""), replacement);
|
rob@100
|
6188 };
|
rob@100
|
6189
|
rob@100
|
6190 /**
|
rob@100
|
6191 * The __replace() function searches all matches between a substring and a string,
|
rob@100
|
6192 * and replaces the matched substring with a new substring
|
rob@100
|
6193 *
|
rob@100
|
6194 * @param {String} subject a substring
|
rob@100
|
6195 * @param {String} what a substring to find
|
rob@100
|
6196 * @param {String} replacement the string to replace the found value
|
rob@100
|
6197 *
|
rob@100
|
6198 * @return {String} returns result
|
rob@100
|
6199 */
|
rob@100
|
6200 p.__replace = function(subject, what, replacement) {
|
rob@100
|
6201 if (typeof subject !== "string") {
|
rob@100
|
6202 return subject.replace.apply(subject, removeFirstArgument(arguments));
|
rob@100
|
6203 }
|
rob@100
|
6204 if (what instanceof RegExp) {
|
rob@100
|
6205 return subject.replace(what, replacement);
|
rob@100
|
6206 }
|
rob@100
|
6207
|
rob@100
|
6208 if (typeof what !== "string") {
|
rob@100
|
6209 what = what.toString();
|
rob@100
|
6210 }
|
rob@100
|
6211 if (what === "") {
|
rob@100
|
6212 return subject;
|
rob@100
|
6213 }
|
rob@100
|
6214
|
rob@100
|
6215 var i = subject.indexOf(what);
|
rob@100
|
6216 if (i < 0) {
|
rob@100
|
6217 return subject;
|
rob@100
|
6218 }
|
rob@100
|
6219
|
rob@100
|
6220 var j = 0, result = "";
|
rob@100
|
6221 do {
|
rob@100
|
6222 result += subject.substring(j, i) + replacement;
|
rob@100
|
6223 j = i + what.length;
|
rob@100
|
6224 } while ( (i = subject.indexOf(what, j)) >= 0);
|
rob@100
|
6225 return result + subject.substring(j);
|
rob@100
|
6226 };
|
rob@100
|
6227
|
rob@100
|
6228 /**
|
rob@100
|
6229 * The __equals() function compares two strings (or objects) to see if they are the same.
|
rob@100
|
6230 * This method is necessary because it's not possible to compare strings using the equality operator (==).
|
rob@100
|
6231 * Returns true if the strings are the same and false if they are not.
|
rob@100
|
6232 *
|
rob@100
|
6233 * @param {String} subject a string used for comparison
|
rob@100
|
6234 * @param {String} other a string used for comparison with
|
rob@100
|
6235 *
|
rob@100
|
6236 * @return {boolean} true is the strings are the same false otherwise
|
rob@100
|
6237 */
|
rob@100
|
6238 p.__equals = function(subject, other) {
|
rob@100
|
6239 if (subject.equals instanceof Function) {
|
rob@100
|
6240 return subject.equals.apply(subject, removeFirstArgument(arguments));
|
rob@100
|
6241 }
|
rob@100
|
6242
|
rob@100
|
6243 return virtEquals(subject, other);
|
rob@100
|
6244 };
|
rob@100
|
6245
|
rob@100
|
6246 /**
|
rob@100
|
6247 * The __equalsIgnoreCase() function compares two strings to see if they are the same.
|
rob@100
|
6248 * Returns true if the strings are the same, either when forced to all lower case or
|
rob@100
|
6249 * all upper case.
|
rob@100
|
6250 *
|
rob@100
|
6251 * @param {String} subject a string used for comparison
|
rob@100
|
6252 * @param {String} other a string used for comparison with
|
rob@100
|
6253 *
|
rob@100
|
6254 * @return {boolean} true is the strings are the same, ignoring case. false otherwise
|
rob@100
|
6255 */
|
rob@100
|
6256 p.__equalsIgnoreCase = function(subject, other) {
|
rob@100
|
6257 if (typeof subject !== "string") {
|
rob@100
|
6258 return subject.equalsIgnoreCase.apply(subject, removeFirstArgument(arguments));
|
rob@100
|
6259 }
|
rob@100
|
6260
|
rob@100
|
6261 return subject.toLowerCase() === other.toLowerCase();
|
rob@100
|
6262 };
|
rob@100
|
6263
|
rob@100
|
6264 /**
|
rob@100
|
6265 * The __toCharArray() function splits the string into a char array.
|
rob@100
|
6266 *
|
rob@100
|
6267 * @param {String} subject The string
|
rob@100
|
6268 *
|
rob@100
|
6269 * @return {Char[]} a char array
|
rob@100
|
6270 */
|
rob@100
|
6271 p.__toCharArray = function(subject) {
|
rob@100
|
6272 if (typeof subject !== "string") {
|
rob@100
|
6273 return subject.toCharArray.apply(subject, removeFirstArgument(arguments));
|
rob@100
|
6274 }
|
rob@100
|
6275
|
rob@100
|
6276 var chars = [];
|
rob@100
|
6277 for (var i = 0, len = subject.length; i < len; ++i) {
|
rob@100
|
6278 chars[i] = new Char(subject.charAt(i));
|
rob@100
|
6279 }
|
rob@100
|
6280 return chars;
|
rob@100
|
6281 };
|
rob@100
|
6282
|
rob@100
|
6283 /**
|
rob@100
|
6284 * The __split() function splits a string using the regex delimiter
|
rob@100
|
6285 * specified. If limit is specified, the resultant array will have number
|
rob@100
|
6286 * of elements equal to or less than the limit.
|
rob@100
|
6287 *
|
rob@100
|
6288 * @param {String} subject string to be split
|
rob@100
|
6289 * @param {String} regexp regex string used to split the subject
|
rob@100
|
6290 * @param {int} limit max number of tokens to be returned
|
rob@100
|
6291 *
|
rob@100
|
6292 * @return {String[]} an array of tokens from the split string
|
rob@100
|
6293 */
|
rob@100
|
6294 p.__split = function(subject, regex, limit) {
|
rob@100
|
6295 if (typeof subject !== "string") {
|
rob@100
|
6296 return subject.split.apply(subject, removeFirstArgument(arguments));
|
rob@100
|
6297 }
|
rob@100
|
6298
|
rob@100
|
6299 var pattern = new RegExp(regex);
|
rob@100
|
6300
|
rob@100
|
6301 // If limit is not specified, use JavaScript's built-in String.split.
|
rob@100
|
6302 if ((limit === undef) || (limit < 1)) {
|
rob@100
|
6303 return subject.split(pattern);
|
rob@100
|
6304 }
|
rob@100
|
6305
|
rob@100
|
6306 // If limit is specified, JavaScript's built-in String.split has a
|
rob@100
|
6307 // different behaviour than Java's. A Java-compatible implementation is
|
rob@100
|
6308 // provided here.
|
rob@100
|
6309 var result = [], currSubject = subject, pos;
|
rob@100
|
6310 while (((pos = currSubject.search(pattern)) !== -1) && (result.length < (limit - 1))) {
|
rob@100
|
6311 var match = pattern.exec(currSubject).toString();
|
rob@100
|
6312 result.push(currSubject.substring(0, pos));
|
rob@100
|
6313 currSubject = currSubject.substring(pos + match.length);
|
rob@100
|
6314 }
|
rob@100
|
6315 if ((pos !== -1) || (currSubject !== "")) {
|
rob@100
|
6316 result.push(currSubject);
|
rob@100
|
6317 }
|
rob@100
|
6318 return result;
|
rob@100
|
6319 };
|
rob@100
|
6320
|
rob@100
|
6321 /**
|
rob@100
|
6322 * The codePointAt() function returns the unicode value of the character at a given index of a string.
|
rob@100
|
6323 *
|
rob@100
|
6324 * @param {int} idx the index of the character
|
rob@100
|
6325 *
|
rob@100
|
6326 * @return {String} code the String containing the unicode value of the character
|
rob@100
|
6327 */
|
rob@100
|
6328 p.__codePointAt = function(subject, idx) {
|
rob@100
|
6329 var code = subject.charCodeAt(idx),
|
rob@100
|
6330 hi,
|
rob@100
|
6331 low;
|
rob@100
|
6332 if (0xD800 <= code && code <= 0xDBFF) {
|
rob@100
|
6333 hi = code;
|
rob@100
|
6334 low = subject.charCodeAt(idx + 1);
|
rob@100
|
6335 return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
|
rob@100
|
6336 }
|
rob@100
|
6337 return code;
|
rob@100
|
6338 };
|
rob@100
|
6339
|
rob@100
|
6340 /**
|
rob@100
|
6341 * The matches() function checks whether or not a string matches a given regular expression.
|
rob@100
|
6342 *
|
rob@100
|
6343 * @param {String} str the String on which the match is tested
|
rob@100
|
6344 * @param {String} regexp the regexp for which a match is tested
|
rob@100
|
6345 *
|
rob@100
|
6346 * @return {boolean} true if the string fits the regexp, false otherwise
|
rob@100
|
6347 */
|
rob@100
|
6348 p.__matches = function(str, regexp) {
|
rob@100
|
6349 return (new RegExp(regexp)).test(str);
|
rob@100
|
6350 };
|
rob@100
|
6351
|
rob@100
|
6352 /**
|
rob@100
|
6353 * The startsWith() function tests if a string starts with the specified prefix. If the prefix
|
rob@100
|
6354 * is the empty String or equal to the subject String, startsWith() will also return true.
|
rob@100
|
6355 *
|
rob@100
|
6356 * @param {String} prefix the String used to compare against the start of the subject String.
|
rob@100
|
6357 * @param {int} toffset (optional) an offset into the subject String where searching should begin.
|
rob@100
|
6358 *
|
rob@100
|
6359 * @return {boolean} true if the subject String starts with the prefix.
|
rob@100
|
6360 */
|
rob@100
|
6361 p.__startsWith = function(subject, prefix, toffset) {
|
rob@100
|
6362 if (typeof subject !== "string") {
|
rob@100
|
6363 return subject.startsWith.apply(subject, removeFirstArgument(arguments));
|
rob@100
|
6364 }
|
rob@100
|
6365
|
rob@100
|
6366 toffset = toffset || 0;
|
rob@100
|
6367 if (toffset < 0 || toffset > subject.length) {
|
rob@100
|
6368 return false;
|
rob@100
|
6369 }
|
rob@100
|
6370 return (prefix === '' || prefix === subject) ? true : (subject.indexOf(prefix) === toffset);
|
rob@100
|
6371 };
|
rob@100
|
6372
|
rob@100
|
6373 /**
|
rob@100
|
6374 * The endsWith() function tests if a string ends with the specified suffix. If the suffix
|
rob@100
|
6375 * is the empty String, endsWith() will also return true.
|
rob@100
|
6376 *
|
rob@100
|
6377 * @param {String} suffix the String used to compare against the end of the subject String.
|
rob@100
|
6378 *
|
rob@100
|
6379 * @return {boolean} true if the subject String starts with the prefix.
|
rob@100
|
6380 */
|
rob@100
|
6381 p.__endsWith = function(subject, suffix) {
|
rob@100
|
6382 if (typeof subject !== "string") {
|
rob@100
|
6383 return subject.endsWith.apply(subject, removeFirstArgument(arguments));
|
rob@100
|
6384 }
|
rob@100
|
6385
|
rob@100
|
6386 var suffixLen = suffix ? suffix.length : 0;
|
rob@100
|
6387 return (suffix === '' || suffix === subject) ? true :
|
rob@100
|
6388 (subject.indexOf(suffix) === subject.length - suffixLen);
|
rob@100
|
6389 };
|
rob@100
|
6390
|
rob@100
|
6391 /**
|
rob@100
|
6392 * The returns hash code of the.
|
rob@100
|
6393 *
|
rob@100
|
6394 * @param {Object} subject The string
|
rob@100
|
6395 *
|
rob@100
|
6396 * @return {int} a hash code
|
rob@100
|
6397 */
|
rob@100
|
6398 p.__hashCode = function(subject) {
|
rob@100
|
6399 if (subject.hashCode instanceof Function) {
|
rob@100
|
6400 return subject.hashCode.apply(subject, removeFirstArgument(arguments));
|
rob@100
|
6401 }
|
rob@100
|
6402 return virtHashCode(subject);
|
rob@100
|
6403 };
|
rob@100
|
6404
|
rob@100
|
6405 /**
|
rob@100
|
6406 * The __printStackTrace() prints stack trace to the console.
|
rob@100
|
6407 *
|
rob@100
|
6408 * @param {Exception} subject The error
|
rob@100
|
6409 */
|
rob@100
|
6410 p.__printStackTrace = function(subject) {
|
rob@100
|
6411 p.println("Exception: " + subject.toString() );
|
rob@100
|
6412 };
|
rob@100
|
6413 };
|
rob@100
|
6414
|
rob@100
|
6415 };
|
rob@100
|
6416
|
rob@100
|
6417 },{}],22:[function(require,module,exports){
|
rob@100
|
6418 /**
|
rob@100
|
6419 * For many "math" functions, we can delegate
|
rob@100
|
6420 * to the Math object. For others, we can't.
|
rob@100
|
6421 */
|
rob@100
|
6422 module.exports = function withMath(p, undef) {
|
rob@100
|
6423 var internalRandomGenerator = function() { return Math.random(); };
|
rob@100
|
6424
|
rob@100
|
6425 /**
|
rob@100
|
6426 * Calculates the absolute value (magnitude) of a number. The absolute value of a number is always positive.
|
rob@100
|
6427 *
|
rob@100
|
6428 * @param {int|float} value int or float
|
rob@100
|
6429 *
|
rob@100
|
6430 * @returns {int|float}
|
rob@100
|
6431 */
|
rob@100
|
6432 p.abs = Math.abs;
|
rob@100
|
6433
|
rob@100
|
6434 /**
|
rob@100
|
6435 * Calculates the closest int value that is greater than or equal to the value of the parameter.
|
rob@100
|
6436 * For example, ceil(9.03) returns the value 10.
|
rob@100
|
6437 *
|
rob@100
|
6438 * @param {float} value float
|
rob@100
|
6439 *
|
rob@100
|
6440 * @returns {int}
|
rob@100
|
6441 *
|
rob@100
|
6442 * @see floor
|
rob@100
|
6443 * @see round
|
rob@100
|
6444 */
|
rob@100
|
6445 p.ceil = Math.ceil;
|
rob@100
|
6446
|
rob@100
|
6447 /**
|
rob@100
|
6448 * Returns Euler's number e (2.71828...) raised to the power of the value parameter.
|
rob@100
|
6449 *
|
rob@100
|
6450 * @param {int|float} value int or float: the exponent to raise e to
|
rob@100
|
6451 *
|
rob@100
|
6452 * @returns {float}
|
rob@100
|
6453 */
|
rob@100
|
6454 p.exp = Math.exp;
|
rob@100
|
6455
|
rob@100
|
6456 /**
|
rob@100
|
6457 * Calculates the closest int value that is less than or equal to the value of the parameter.
|
rob@100
|
6458 *
|
rob@100
|
6459 * @param {int|float} value the value to floor
|
rob@100
|
6460 *
|
rob@100
|
6461 * @returns {int|float}
|
rob@100
|
6462 *
|
rob@100
|
6463 * @see ceil
|
rob@100
|
6464 * @see round
|
rob@100
|
6465 */
|
rob@100
|
6466 p.floor = Math.floor;
|
rob@100
|
6467
|
rob@100
|
6468 /**
|
rob@100
|
6469 * Calculates the natural logarithm (the base-e logarithm) of a number. This function
|
rob@100
|
6470 * expects the values greater than 0.0.
|
rob@100
|
6471 *
|
rob@100
|
6472 * @param {int|float} value int or float: number must be greater then 0.0
|
rob@100
|
6473 *
|
rob@100
|
6474 * @returns {float}
|
rob@100
|
6475 */
|
rob@100
|
6476 p.log = Math.log;
|
rob@100
|
6477
|
rob@100
|
6478 /**
|
rob@100
|
6479 * Facilitates exponential expressions. The pow() function is an efficient way of
|
rob@100
|
6480 * multiplying numbers by themselves (or their reciprocal) in large quantities.
|
rob@100
|
6481 * For example, pow(3, 5) is equivalent to the expression 3*3*3*3*3 and pow(3, -5)
|
rob@100
|
6482 * is equivalent to 1 / 3*3*3*3*3.
|
rob@100
|
6483 *
|
rob@100
|
6484 * @param {int|float} num base of the exponential expression
|
rob@100
|
6485 * @param {int|float} exponent power of which to raise the base
|
rob@100
|
6486 *
|
rob@100
|
6487 * @returns {float}
|
rob@100
|
6488 *
|
rob@100
|
6489 * @see sqrt
|
rob@100
|
6490 */
|
rob@100
|
6491 p.pow = Math.pow;
|
rob@100
|
6492
|
rob@100
|
6493 /**
|
rob@100
|
6494 * Calculates the integer closest to the value parameter. For example, round(9.2) returns the value 9.
|
rob@100
|
6495 *
|
rob@100
|
6496 * @param {float} value number to round
|
rob@100
|
6497 *
|
rob@100
|
6498 * @returns {int}
|
rob@100
|
6499 *
|
rob@100
|
6500 * @see floor
|
rob@100
|
6501 * @see ceil
|
rob@100
|
6502 */
|
rob@100
|
6503 p.round = Math.round;
|
rob@100
|
6504 /**
|
rob@100
|
6505 * Calculates the square root of a number. The square root of a number is always positive,
|
rob@100
|
6506 * even though there may be a valid negative root. The square root s of number a is such
|
rob@100
|
6507 * that s*s = a. It is the opposite of squaring.
|
rob@100
|
6508 *
|
rob@100
|
6509 * @param {float} value int or float, non negative
|
rob@100
|
6510 *
|
rob@100
|
6511 * @returns {float}
|
rob@100
|
6512 *
|
rob@100
|
6513 * @see pow
|
rob@100
|
6514 * @see sq
|
rob@100
|
6515 */
|
rob@100
|
6516
|
rob@100
|
6517 p.sqrt = Math.sqrt;
|
rob@100
|
6518
|
rob@100
|
6519 // Trigonometry
|
rob@100
|
6520 /**
|
rob@100
|
6521 * The inverse of cos(), returns the arc cosine of a value. This function expects the
|
rob@100
|
6522 * values in the range of -1 to 1 and values are returned in the range 0 to PI (3.1415927).
|
rob@100
|
6523 *
|
rob@100
|
6524 * @param {float} value the value whose arc cosine is to be returned
|
rob@100
|
6525 *
|
rob@100
|
6526 * @returns {float}
|
rob@100
|
6527 *
|
rob@100
|
6528 * @see cos
|
rob@100
|
6529 * @see asin
|
rob@100
|
6530 * @see atan
|
rob@100
|
6531 */
|
rob@100
|
6532 p.acos = Math.acos;
|
rob@100
|
6533
|
rob@100
|
6534 /**
|
rob@100
|
6535 * The inverse of sin(), returns the arc sine of a value. This function expects the values
|
rob@100
|
6536 * in the range of -1 to 1 and values are returned in the range -PI/2 to PI/2.
|
rob@100
|
6537 *
|
rob@100
|
6538 * @param {float} value the value whose arc sine is to be returned
|
rob@100
|
6539 *
|
rob@100
|
6540 * @returns {float}
|
rob@100
|
6541 *
|
rob@100
|
6542 * @see sin
|
rob@100
|
6543 * @see acos
|
rob@100
|
6544 * @see atan
|
rob@100
|
6545 */
|
rob@100
|
6546 p.asin = Math.asin;
|
rob@100
|
6547
|
rob@100
|
6548 /**
|
rob@100
|
6549 * The inverse of tan(), returns the arc tangent of a value. This function expects the values
|
rob@100
|
6550 * in the range of -Infinity to Infinity (exclusive) and values are returned in the range -PI/2 to PI/2 .
|
rob@100
|
6551 *
|
rob@100
|
6552 * @param {float} value -Infinity to Infinity (exclusive)
|
rob@100
|
6553 *
|
rob@100
|
6554 * @returns {float}
|
rob@100
|
6555 *
|
rob@100
|
6556 * @see tan
|
rob@100
|
6557 * @see asin
|
rob@100
|
6558 * @see acos
|
rob@100
|
6559 */
|
rob@100
|
6560 p.atan = Math.atan;
|
rob@100
|
6561
|
rob@100
|
6562 /**
|
rob@100
|
6563 * Calculates the angle (in radians) from a specified point to the coordinate origin as measured from
|
rob@100
|
6564 * the positive x-axis. Values are returned as a float in the range from PI to -PI. The atan2() function
|
rob@100
|
6565 * is most often used for orienting geometry to the position of the cursor. Note: The y-coordinate of the
|
rob@100
|
6566 * point is the first parameter and the x-coordinate is the second due the the structure of calculating the tangent.
|
rob@100
|
6567 *
|
rob@100
|
6568 * @param {float} y y-coordinate of the point
|
rob@100
|
6569 * @param {float} x x-coordinate of the point
|
rob@100
|
6570 *
|
rob@100
|
6571 * @returns {float}
|
rob@100
|
6572 *
|
rob@100
|
6573 * @see tan
|
rob@100
|
6574 */
|
rob@100
|
6575 p.atan2 = Math.atan2;
|
rob@100
|
6576
|
rob@100
|
6577 /**
|
rob@100
|
6578 * Calculates the cosine of an angle. This function expects the values of the angle parameter to be provided
|
rob@100
|
6579 * in radians (values from 0 to PI*2). Values are returned in the range -1 to 1.
|
rob@100
|
6580 *
|
rob@100
|
6581 * @param {float} value an angle in radians
|
rob@100
|
6582 *
|
rob@100
|
6583 * @returns {float}
|
rob@100
|
6584 *
|
rob@100
|
6585 * @see tan
|
rob@100
|
6586 * @see sin
|
rob@100
|
6587 */
|
rob@100
|
6588 p.cos = Math.cos;
|
rob@100
|
6589
|
rob@100
|
6590 /**
|
rob@100
|
6591 * Calculates the sine of an angle. This function expects the values of the angle parameter to be provided in
|
rob@100
|
6592 * radians (values from 0 to 6.28). Values are returned in the range -1 to 1.
|
rob@100
|
6593 *
|
rob@100
|
6594 * @param {float} value an angle in radians
|
rob@100
|
6595 *
|
rob@100
|
6596 * @returns {float}
|
rob@100
|
6597 *
|
rob@100
|
6598 * @see cos
|
rob@100
|
6599 * @see radians
|
rob@100
|
6600 */
|
rob@100
|
6601 p.sin = Math.sin;
|
rob@100
|
6602
|
rob@100
|
6603 /**
|
rob@100
|
6604 * Calculates the ratio of the sine and cosine of an angle. This function expects the values of the angle
|
rob@100
|
6605 * parameter to be provided in radians (values from 0 to PI*2). Values are returned in the range infinity to -infinity.
|
rob@100
|
6606 *
|
rob@100
|
6607 * @param {float} value an angle in radians
|
rob@100
|
6608 *
|
rob@100
|
6609 * @returns {float}
|
rob@100
|
6610 *
|
rob@100
|
6611 * @see cos
|
rob@100
|
6612 * @see sin
|
rob@100
|
6613 * @see radians
|
rob@100
|
6614 */
|
rob@100
|
6615 p.tan = Math.tan;
|
rob@100
|
6616
|
rob@100
|
6617 /**
|
rob@100
|
6618 * Constrains a value to not exceed a maximum and minimum value.
|
rob@100
|
6619 *
|
rob@100
|
6620 * @param {int|float} value the value to constrain
|
rob@100
|
6621 * @param {int|float} value minimum limit
|
rob@100
|
6622 * @param {int|float} value maximum limit
|
rob@100
|
6623 *
|
rob@100
|
6624 * @returns {int|float}
|
rob@100
|
6625 *
|
rob@100
|
6626 * @see max
|
rob@100
|
6627 * @see min
|
rob@100
|
6628 */
|
rob@100
|
6629 p.constrain = function(aNumber, aMin, aMax) {
|
rob@100
|
6630 return aNumber > aMax ? aMax : aNumber < aMin ? aMin : aNumber;
|
rob@100
|
6631 };
|
rob@100
|
6632
|
rob@100
|
6633 /**
|
rob@100
|
6634 * Calculates the distance between two points.
|
rob@100
|
6635 *
|
rob@100
|
6636 * @param {int|float} x1 int or float: x-coordinate of the first point
|
rob@100
|
6637 * @param {int|float} y1 int or float: y-coordinate of the first point
|
rob@100
|
6638 * @param {int|float} z1 int or float: z-coordinate of the first point
|
rob@100
|
6639 * @param {int|float} x2 int or float: x-coordinate of the second point
|
rob@100
|
6640 * @param {int|float} y2 int or float: y-coordinate of the second point
|
rob@100
|
6641 * @param {int|float} z2 int or float: z-coordinate of the second point
|
rob@100
|
6642 *
|
rob@100
|
6643 * @returns {float}
|
rob@100
|
6644 */
|
rob@100
|
6645 p.dist = function() {
|
rob@100
|
6646 var dx, dy, dz;
|
rob@100
|
6647 if (arguments.length === 4) {
|
rob@100
|
6648 dx = arguments[0] - arguments[2];
|
rob@100
|
6649 dy = arguments[1] - arguments[3];
|
rob@100
|
6650 return Math.sqrt(dx * dx + dy * dy);
|
rob@100
|
6651 }
|
rob@100
|
6652 if (arguments.length === 6) {
|
rob@100
|
6653 dx = arguments[0] - arguments[3];
|
rob@100
|
6654 dy = arguments[1] - arguments[4];
|
rob@100
|
6655 dz = arguments[2] - arguments[5];
|
rob@100
|
6656 return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
rob@100
|
6657 }
|
rob@100
|
6658 };
|
rob@100
|
6659
|
rob@100
|
6660 /**
|
rob@100
|
6661 * Calculates a number between two numbers at a specific increment. The amt parameter is the
|
rob@100
|
6662 * amount to interpolate between the two values where 0.0 equal to the first point, 0.1 is very
|
rob@100
|
6663 * near the first point, 0.5 is half-way in between, etc. The lerp function is convenient for
|
rob@100
|
6664 * creating motion along a straight path and for drawing dotted lines.
|
rob@100
|
6665 *
|
rob@100
|
6666 * @param {int|float} value1 float or int: first value
|
rob@100
|
6667 * @param {int|float} value2 float or int: second value
|
rob@100
|
6668 * @param {int|float} amt float: between 0.0 and 1.0
|
rob@100
|
6669 *
|
rob@100
|
6670 * @returns {float}
|
rob@100
|
6671 *
|
rob@100
|
6672 * @see curvePoint
|
rob@100
|
6673 * @see bezierPoint
|
rob@100
|
6674 */
|
rob@100
|
6675 p.lerp = function(value1, value2, amt) {
|
rob@100
|
6676 return ((value2 - value1) * amt) + value1;
|
rob@100
|
6677 };
|
rob@100
|
6678
|
rob@100
|
6679 /**
|
rob@100
|
6680 * Calculates the magnitude (or length) of a vector. A vector is a direction in space commonly
|
rob@100
|
6681 * used in computer graphics and linear algebra. Because it has no "start" position, the magnitude
|
rob@100
|
6682 * of a vector can be thought of as the distance from coordinate (0,0) to its (x,y) value.
|
rob@100
|
6683 * Therefore, mag() is a shortcut for writing "dist(0, 0, x, y)".
|
rob@100
|
6684 *
|
rob@100
|
6685 * @param {int|float} a float or int: first value
|
rob@100
|
6686 * @param {int|float} b float or int: second value
|
rob@100
|
6687 * @param {int|float} c float or int: third value
|
rob@100
|
6688 *
|
rob@100
|
6689 * @returns {float}
|
rob@100
|
6690 *
|
rob@100
|
6691 * @see dist
|
rob@100
|
6692 */
|
rob@100
|
6693 p.mag = function(a, b, c) {
|
rob@100
|
6694 if (c) {
|
rob@100
|
6695 return Math.sqrt(a * a + b * b + c * c);
|
rob@100
|
6696 }
|
rob@100
|
6697
|
rob@100
|
6698 return Math.sqrt(a * a + b * b);
|
rob@100
|
6699 };
|
rob@100
|
6700
|
rob@100
|
6701 /**
|
rob@100
|
6702 * Re-maps a number from one range to another. In the example above, the number '25' is converted from
|
rob@100
|
6703 * a value in the range 0..100 into a value that ranges from the left edge (0) to the right edge (width) of the screen.
|
rob@100
|
6704 * Numbers outside the range are not clamped to 0 and 1, because out-of-range values are often intentional and useful.
|
rob@100
|
6705 *
|
rob@100
|
6706 * @param {float} value The incoming value to be converted
|
rob@100
|
6707 * @param {float} istart Lower bound of the value's current range
|
rob@100
|
6708 * @param {float} istop Upper bound of the value's current range
|
rob@100
|
6709 * @param {float} ostart Lower bound of the value's target range
|
rob@100
|
6710 * @param {float} ostop Upper bound of the value's target range
|
rob@100
|
6711 *
|
rob@100
|
6712 * @returns {float}
|
rob@100
|
6713 *
|
rob@100
|
6714 * @see norm
|
rob@100
|
6715 * @see lerp
|
rob@100
|
6716 */
|
rob@100
|
6717 p.map = function(value, istart, istop, ostart, ostop) {
|
rob@100
|
6718 return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
|
rob@100
|
6719 };
|
rob@100
|
6720
|
rob@100
|
6721 /**
|
rob@100
|
6722 * Determines the largest value in a sequence of numbers.
|
rob@100
|
6723 *
|
rob@100
|
6724 * @param {int|float} value1 int or float
|
rob@100
|
6725 * @param {int|float} value2 int or float
|
rob@100
|
6726 * @param {int|float} value3 int or float
|
rob@100
|
6727 * @param {int|float} array int or float array
|
rob@100
|
6728 *
|
rob@100
|
6729 * @returns {int|float}
|
rob@100
|
6730 *
|
rob@100
|
6731 * @see min
|
rob@100
|
6732 */
|
rob@100
|
6733 p.max = function() {
|
rob@100
|
6734 if (arguments.length === 2) {
|
rob@100
|
6735 return arguments[0] < arguments[1] ? arguments[1] : arguments[0];
|
rob@100
|
6736 }
|
rob@100
|
6737 var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
|
rob@100
|
6738 if (! ("length" in numbers && numbers.length > 0)) {
|
rob@100
|
6739 throw "Non-empty array is expected";
|
rob@100
|
6740 }
|
rob@100
|
6741 var max = numbers[0],
|
rob@100
|
6742 count = numbers.length;
|
rob@100
|
6743 for (var i = 1; i < count; ++i) {
|
rob@100
|
6744 if (max < numbers[i]) {
|
rob@100
|
6745 max = numbers[i];
|
rob@100
|
6746 }
|
rob@100
|
6747 }
|
rob@100
|
6748 return max;
|
rob@100
|
6749 };
|
rob@100
|
6750
|
rob@100
|
6751 /**
|
rob@100
|
6752 * Determines the smallest value in a sequence of numbers.
|
rob@100
|
6753 *
|
rob@100
|
6754 * @param {int|float} value1 int or float
|
rob@100
|
6755 * @param {int|float} value2 int or float
|
rob@100
|
6756 * @param {int|float} value3 int or float
|
rob@100
|
6757 * @param {int|float} array int or float array
|
rob@100
|
6758 *
|
rob@100
|
6759 * @returns {int|float}
|
rob@100
|
6760 *
|
rob@100
|
6761 * @see max
|
rob@100
|
6762 */
|
rob@100
|
6763 p.min = function() {
|
rob@100
|
6764 if (arguments.length === 2) {
|
rob@100
|
6765 return arguments[0] < arguments[1] ? arguments[0] : arguments[1];
|
rob@100
|
6766 }
|
rob@100
|
6767 var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
|
rob@100
|
6768 if (! ("length" in numbers && numbers.length > 0)) {
|
rob@100
|
6769 throw "Non-empty array is expected";
|
rob@100
|
6770 }
|
rob@100
|
6771 var min = numbers[0],
|
rob@100
|
6772 count = numbers.length;
|
rob@100
|
6773 for (var i = 1; i < count; ++i) {
|
rob@100
|
6774 if (min > numbers[i]) {
|
rob@100
|
6775 min = numbers[i];
|
rob@100
|
6776 }
|
rob@100
|
6777 }
|
rob@100
|
6778 return min;
|
rob@100
|
6779 };
|
rob@100
|
6780
|
rob@100
|
6781 /**
|
rob@100
|
6782 * Normalizes a number from another range into a value between 0 and 1.
|
rob@100
|
6783 * Identical to map(value, low, high, 0, 1);
|
rob@100
|
6784 * Numbers outside the range are not clamped to 0 and 1, because out-of-range
|
rob@100
|
6785 * values are often intentional and useful.
|
rob@100
|
6786 *
|
rob@100
|
6787 * @param {float} aNumber The incoming value to be converted
|
rob@100
|
6788 * @param {float} low Lower bound of the value's current range
|
rob@100
|
6789 * @param {float} high Upper bound of the value's current range
|
rob@100
|
6790 *
|
rob@100
|
6791 * @returns {float}
|
rob@100
|
6792 *
|
rob@100
|
6793 * @see map
|
rob@100
|
6794 * @see lerp
|
rob@100
|
6795 */
|
rob@100
|
6796 p.norm = function(aNumber, low, high) {
|
rob@100
|
6797 return (aNumber - low) / (high - low);
|
rob@100
|
6798 };
|
rob@100
|
6799
|
rob@100
|
6800 /**
|
rob@100
|
6801 * Squares a number (multiplies a number by itself). The result is always a positive number,
|
rob@100
|
6802 * as multiplying two negative numbers always yields a positive result. For example, -1 * -1 = 1.
|
rob@100
|
6803 *
|
rob@100
|
6804 * @param {float} value int or float
|
rob@100
|
6805 *
|
rob@100
|
6806 * @returns {float}
|
rob@100
|
6807 *
|
rob@100
|
6808 * @see sqrt
|
rob@100
|
6809 */
|
rob@100
|
6810 p.sq = function(aNumber) {
|
rob@100
|
6811 return aNumber * aNumber;
|
rob@100
|
6812 };
|
rob@100
|
6813
|
rob@100
|
6814 /**
|
rob@100
|
6815 * Converts a radian measurement to its corresponding value in degrees. Radians and degrees are two ways of
|
rob@100
|
6816 * measuring the same thing. There are 360 degrees in a circle and 2*PI radians in a circle. For example,
|
rob@100
|
6817 * 90 degrees = PI/2 = 1.5707964. All trigonometric methods in Processing require their parameters to be specified in radians.
|
rob@100
|
6818 *
|
rob@100
|
6819 * @param {int|float} value an angle in radians
|
rob@100
|
6820 *
|
rob@100
|
6821 * @returns {float}
|
rob@100
|
6822 *
|
rob@100
|
6823 * @see radians
|
rob@100
|
6824 */
|
rob@100
|
6825 p.degrees = function(aAngle) {
|
rob@100
|
6826 return (aAngle * 180) / Math.PI;
|
rob@100
|
6827 };
|
rob@100
|
6828
|
rob@100
|
6829 /**
|
rob@100
|
6830 * Generates random numbers. Each time the random() function is called, it returns an unexpected value within
|
rob@100
|
6831 * the specified range. If one parameter is passed to the function it will return a float between zero and the
|
rob@100
|
6832 * value of the high parameter. The function call random(5) returns values between 0 and 5 (starting at zero,
|
rob@100
|
6833 * up to but not including 5). If two parameters are passed, it will return a float with a value between the
|
rob@100
|
6834 * parameters. The function call random(-5, 10.2) returns values starting at -5 up to (but not including) 10.2.
|
rob@100
|
6835 * To convert a floating-point random number to an integer, use the int() function.
|
rob@100
|
6836 *
|
rob@100
|
6837 * @param {int|float} value1 if one parameter is used, the top end to random from, if two params the low end
|
rob@100
|
6838 * @param {int|float} value2 the top end of the random range
|
rob@100
|
6839 *
|
rob@100
|
6840 * @returns {float}
|
rob@100
|
6841 *
|
rob@100
|
6842 * @see randomSeed
|
rob@100
|
6843 * @see noise
|
rob@100
|
6844 */
|
rob@100
|
6845 p.random = function() {
|
rob@100
|
6846 if(arguments.length === 0) {
|
rob@100
|
6847 return internalRandomGenerator();
|
rob@100
|
6848 }
|
rob@100
|
6849 if(arguments.length === 1) {
|
rob@100
|
6850 return internalRandomGenerator() * arguments[0];
|
rob@100
|
6851 }
|
rob@100
|
6852 var aMin = arguments[0], aMax = arguments[1];
|
rob@100
|
6853 return internalRandomGenerator() * (aMax - aMin) + aMin;
|
rob@100
|
6854 };
|
rob@100
|
6855
|
rob@100
|
6856 // Pseudo-random generator
|
rob@100
|
6857 function Marsaglia(i1, i2) {
|
rob@100
|
6858 // from http://www.math.uni-bielefeld.de/~sillke/ALGORITHMS/random/marsaglia-c
|
rob@100
|
6859 var z=i1 || 362436069, w= i2 || 521288629;
|
rob@100
|
6860 var intGenerator = function() {
|
rob@100
|
6861 z=(36969*(z&65535)+(z>>>16)) & 0xFFFFFFFF;
|
rob@100
|
6862 w=(18000*(w&65535)+(w>>>16)) & 0xFFFFFFFF;
|
rob@100
|
6863 return (((z&0xFFFF)<<16) | (w&0xFFFF)) & 0xFFFFFFFF;
|
rob@100
|
6864 };
|
rob@100
|
6865
|
rob@100
|
6866 this.doubleGenerator = function() {
|
rob@100
|
6867 var i = intGenerator() / 4294967296;
|
rob@100
|
6868 return i < 0 ? 1 + i : i;
|
rob@100
|
6869 };
|
rob@100
|
6870 this.intGenerator = intGenerator;
|
rob@100
|
6871 }
|
rob@100
|
6872
|
rob@100
|
6873 Marsaglia.createRandomized = function() {
|
rob@100
|
6874 var now = new Date();
|
rob@100
|
6875 return new Marsaglia((now / 60000) & 0xFFFFFFFF, now & 0xFFFFFFFF);
|
rob@100
|
6876 };
|
rob@100
|
6877
|
rob@100
|
6878 /**
|
rob@100
|
6879 * Sets the seed value for random(). By default, random() produces different results each time the
|
rob@100
|
6880 * program is run. Set the value parameter to a constant to return the same pseudo-random numbers
|
rob@100
|
6881 * each time the software is run.
|
rob@100
|
6882 *
|
rob@100
|
6883 * @param {int|float} seed int
|
rob@100
|
6884 *
|
rob@100
|
6885 * @see random
|
rob@100
|
6886 * @see noise
|
rob@100
|
6887 * @see noiseSeed
|
rob@100
|
6888 */
|
rob@100
|
6889 p.randomSeed = function(seed) {
|
rob@100
|
6890 internalRandomGenerator = (new Marsaglia(seed)).doubleGenerator;
|
rob@100
|
6891 this.haveNextNextGaussian = false;
|
rob@100
|
6892 };
|
rob@100
|
6893
|
rob@100
|
6894 /**
|
rob@100
|
6895 * Returns a float from a random series of numbers having a mean of 0 and standard deviation of 1. Each time
|
rob@100
|
6896 * the randomGaussian() function is called, it returns a number fitting a Gaussian, or normal, distribution.
|
rob@100
|
6897 * There is theoretically no minimum or maximum value that randomGaussian() might return. Rather, there is just a
|
rob@100
|
6898 * very low probability that values far from the mean will be returned; and a higher probability that numbers
|
rob@100
|
6899 * near the mean will be returned.
|
rob@100
|
6900 *
|
rob@100
|
6901 * @returns {float}
|
rob@100
|
6902 *
|
rob@100
|
6903 * @see random
|
rob@100
|
6904 * @see noise
|
rob@100
|
6905 */
|
rob@100
|
6906 p.randomGaussian = function() {
|
rob@100
|
6907 if (this.haveNextNextGaussian) {
|
rob@100
|
6908 this.haveNextNextGaussian = false;
|
rob@100
|
6909 return this.nextNextGaussian;
|
rob@100
|
6910 }
|
rob@100
|
6911 var v1, v2, s;
|
rob@100
|
6912 do {
|
rob@100
|
6913 v1 = 2 * internalRandomGenerator() - 1; // between -1.0 and 1.0
|
rob@100
|
6914 v2 = 2 * internalRandomGenerator() - 1; // between -1.0 and 1.0
|
rob@100
|
6915 s = v1 * v1 + v2 * v2;
|
rob@100
|
6916 }
|
rob@100
|
6917 while (s >= 1 || s === 0);
|
rob@100
|
6918
|
rob@100
|
6919 var multiplier = Math.sqrt(-2 * Math.log(s) / s);
|
rob@100
|
6920 this.nextNextGaussian = v2 * multiplier;
|
rob@100
|
6921 this.haveNextNextGaussian = true;
|
rob@100
|
6922
|
rob@100
|
6923 return v1 * multiplier;
|
rob@100
|
6924 };
|
rob@100
|
6925
|
rob@100
|
6926 // Noise functions and helpers
|
rob@100
|
6927 function PerlinNoise(seed) {
|
rob@100
|
6928 var rnd = seed !== undef ? new Marsaglia(seed) : Marsaglia.createRandomized();
|
rob@100
|
6929 var i, j;
|
rob@100
|
6930 // http://www.noisemachine.com/talk1/17b.html
|
rob@100
|
6931 // http://mrl.nyu.edu/~perlin/noise/
|
rob@100
|
6932 // generate permutation
|
rob@100
|
6933 var perm = new Uint8Array(512);
|
rob@100
|
6934 for(i=0;i<256;++i) { perm[i] = i; }
|
rob@100
|
6935 for(i=0;i<256;++i) { var t = perm[j = rnd.intGenerator() & 0xFF]; perm[j] = perm[i]; perm[i] = t; }
|
rob@100
|
6936 // copy to avoid taking mod in perm[0];
|
rob@100
|
6937 for(i=0;i<256;++i) { perm[i + 256] = perm[i]; }
|
rob@100
|
6938
|
rob@100
|
6939 function grad3d(i,x,y,z) {
|
rob@100
|
6940 var h = i & 15; // convert into 12 gradient directions
|
rob@100
|
6941 var u = h<8 ? x : y,
|
rob@100
|
6942 v = h<4 ? y : h===12||h===14 ? x : z;
|
rob@100
|
6943 return ((h&1) === 0 ? u : -u) + ((h&2) === 0 ? v : -v);
|
rob@100
|
6944 }
|
rob@100
|
6945
|
rob@100
|
6946 function grad2d(i,x,y) {
|
rob@100
|
6947 var v = (i & 1) === 0 ? x : y;
|
rob@100
|
6948 return (i&2) === 0 ? -v : v;
|
rob@100
|
6949 }
|
rob@100
|
6950
|
rob@100
|
6951 function grad1d(i,x) {
|
rob@100
|
6952 return (i&1) === 0 ? -x : x;
|
rob@100
|
6953 }
|
rob@100
|
6954
|
rob@100
|
6955 function lerp(t,a,b) { return a + t * (b - a); }
|
rob@100
|
6956
|
rob@100
|
6957 this.noise3d = function(x, y, z) {
|
rob@100
|
6958 var X = Math.floor(x)&255, Y = Math.floor(y)&255, Z = Math.floor(z)&255;
|
rob@100
|
6959 x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z);
|
rob@100
|
6960 var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y, fz = (3-2*z)*z*z;
|
rob@100
|
6961 var p0 = perm[X]+Y, p00 = perm[p0] + Z, p01 = perm[p0 + 1] + Z,
|
rob@100
|
6962 p1 = perm[X + 1] + Y, p10 = perm[p1] + Z, p11 = perm[p1 + 1] + Z;
|
rob@100
|
6963 return lerp(fz,
|
rob@100
|
6964 lerp(fy, lerp(fx, grad3d(perm[p00], x, y, z), grad3d(perm[p10], x-1, y, z)),
|
rob@100
|
6965 lerp(fx, grad3d(perm[p01], x, y-1, z), grad3d(perm[p11], x-1, y-1,z))),
|
rob@100
|
6966 lerp(fy, lerp(fx, grad3d(perm[p00 + 1], x, y, z-1), grad3d(perm[p10 + 1], x-1, y, z-1)),
|
rob@100
|
6967 lerp(fx, grad3d(perm[p01 + 1], x, y-1, z-1), grad3d(perm[p11 + 1], x-1, y-1,z-1))));
|
rob@100
|
6968 };
|
rob@100
|
6969
|
rob@100
|
6970 this.noise2d = function(x, y) {
|
rob@100
|
6971 var X = Math.floor(x)&255, Y = Math.floor(y)&255;
|
rob@100
|
6972 x -= Math.floor(x); y -= Math.floor(y);
|
rob@100
|
6973 var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y;
|
rob@100
|
6974 var p0 = perm[X]+Y, p1 = perm[X + 1] + Y;
|
rob@100
|
6975 return lerp(fy,
|
rob@100
|
6976 lerp(fx, grad2d(perm[p0], x, y), grad2d(perm[p1], x-1, y)),
|
rob@100
|
6977 lerp(fx, grad2d(perm[p0 + 1], x, y-1), grad2d(perm[p1 + 1], x-1, y-1)));
|
rob@100
|
6978 };
|
rob@100
|
6979
|
rob@100
|
6980 this.noise1d = function(x) {
|
rob@100
|
6981 var X = Math.floor(x)&255;
|
rob@100
|
6982 x -= Math.floor(x);
|
rob@100
|
6983 var fx = (3-2*x)*x*x;
|
rob@100
|
6984 return lerp(fx, grad1d(perm[X], x), grad1d(perm[X+1], x-1));
|
rob@100
|
6985 };
|
rob@100
|
6986 }
|
rob@100
|
6987
|
rob@100
|
6988 // processing defaults
|
rob@100
|
6989 var noiseProfile = { generator: undef, octaves: 4, fallout: 0.5, seed: undef};
|
rob@100
|
6990
|
rob@100
|
6991 /**
|
rob@100
|
6992 * Returns the Perlin noise value at specified coordinates. Perlin noise is a random sequence
|
rob@100
|
6993 * generator producing a more natural ordered, harmonic succession of numbers compared to the
|
rob@100
|
6994 * standard random() function. It was invented by Ken Perlin in the 1980s and been used since
|
rob@100
|
6995 * in graphical applications to produce procedural textures, natural motion, shapes, terrains etc.
|
rob@100
|
6996 * The main difference to the random() function is that Perlin noise is defined in an infinite
|
rob@100
|
6997 * n-dimensional space where each pair of coordinates corresponds to a fixed semi-random value
|
rob@100
|
6998 * (fixed only for the lifespan of the program). The resulting value will always be between 0.0
|
rob@100
|
6999 * and 1.0. Processing can compute 1D, 2D and 3D noise, depending on the number of coordinates
|
rob@100
|
7000 * given. The noise value can be animated by moving through the noise space as demonstrated in
|
rob@100
|
7001 * the example above. The 2nd and 3rd dimension can also be interpreted as time.
|
rob@100
|
7002 * The actual noise is structured similar to an audio signal, in respect to the function's use
|
rob@100
|
7003 * of frequencies. Similar to the concept of harmonics in physics, perlin noise is computed over
|
rob@100
|
7004 * several octaves which are added together for the final result.
|
rob@100
|
7005 * Another way to adjust the character of the resulting sequence is the scale of the input
|
rob@100
|
7006 * coordinates. As the function works within an infinite space the value of the coordinates
|
rob@100
|
7007 * doesn't matter as such, only the distance between successive coordinates does (eg. when using
|
rob@100
|
7008 * noise() within a loop). As a general rule the smaller the difference between coordinates, the
|
rob@100
|
7009 * smoother the resulting noise sequence will be. Steps of 0.005-0.03 work best for most applications,
|
rob@100
|
7010 * but this will differ depending on use.
|
rob@100
|
7011 *
|
rob@100
|
7012 * @param {float} x x coordinate in noise space
|
rob@100
|
7013 * @param {float} y y coordinate in noise space
|
rob@100
|
7014 * @param {float} z z coordinate in noise space
|
rob@100
|
7015 *
|
rob@100
|
7016 * @returns {float}
|
rob@100
|
7017 *
|
rob@100
|
7018 * @see random
|
rob@100
|
7019 * @see noiseDetail
|
rob@100
|
7020 */
|
rob@100
|
7021 p.noise = function(x, y, z) {
|
rob@100
|
7022 if(noiseProfile.generator === undef) {
|
rob@100
|
7023 // caching
|
rob@100
|
7024 noiseProfile.generator = new PerlinNoise(noiseProfile.seed);
|
rob@100
|
7025 }
|
rob@100
|
7026 var generator = noiseProfile.generator;
|
rob@100
|
7027 var effect = 1, k = 1, sum = 0;
|
rob@100
|
7028 for(var i=0; i<noiseProfile.octaves; ++i) {
|
rob@100
|
7029 effect *= noiseProfile.fallout;
|
rob@100
|
7030 switch (arguments.length) {
|
rob@100
|
7031 case 1:
|
rob@100
|
7032 sum += effect * (1 + generator.noise1d(k*x))/2; break;
|
rob@100
|
7033 case 2:
|
rob@100
|
7034 sum += effect * (1 + generator.noise2d(k*x, k*y))/2; break;
|
rob@100
|
7035 case 3:
|
rob@100
|
7036 sum += effect * (1 + generator.noise3d(k*x, k*y, k*z))/2; break;
|
rob@100
|
7037 }
|
rob@100
|
7038 k *= 2;
|
rob@100
|
7039 }
|
rob@100
|
7040 return sum;
|
rob@100
|
7041 };
|
rob@100
|
7042
|
rob@100
|
7043 /**
|
rob@100
|
7044 * Adjusts the character and level of detail produced by the Perlin noise function.
|
rob@100
|
7045 * Similar to harmonics in physics, noise is computed over several octaves. Lower octaves
|
rob@100
|
7046 * contribute more to the output signal and as such define the overal intensity of the noise,
|
rob@100
|
7047 * whereas higher octaves create finer grained details in the noise sequence. By default,
|
rob@100
|
7048 * noise is computed over 4 octaves with each octave contributing exactly half than its
|
rob@100
|
7049 * predecessor, starting at 50% strength for the 1st octave. This falloff amount can be
|
rob@100
|
7050 * changed by adding an additional function parameter. Eg. a falloff factor of 0.75 means
|
rob@100
|
7051 * each octave will now have 75% impact (25% less) of the previous lower octave. Any value
|
rob@100
|
7052 * between 0.0 and 1.0 is valid, however note that values greater than 0.5 might result in
|
rob@100
|
7053 * greater than 1.0 values returned by noise(). By changing these parameters, the signal
|
rob@100
|
7054 * created by the noise() function can be adapted to fit very specific needs and characteristics.
|
rob@100
|
7055 *
|
rob@100
|
7056 * @param {int} octaves number of octaves to be used by the noise() function
|
rob@100
|
7057 * @param {float} falloff falloff factor for each octave
|
rob@100
|
7058 *
|
rob@100
|
7059 * @see noise
|
rob@100
|
7060 */
|
rob@100
|
7061 p.noiseDetail = function(octaves, fallout) {
|
rob@100
|
7062 noiseProfile.octaves = octaves;
|
rob@100
|
7063 if(fallout !== undef) {
|
rob@100
|
7064 noiseProfile.fallout = fallout;
|
rob@100
|
7065 }
|
rob@100
|
7066 };
|
rob@100
|
7067
|
rob@100
|
7068 /**
|
rob@100
|
7069 * Sets the seed value for noise(). By default, noise() produces different results each
|
rob@100
|
7070 * time the program is run. Set the value parameter to a constant to return the same
|
rob@100
|
7071 * pseudo-random numbers each time the software is run.
|
rob@100
|
7072 *
|
rob@100
|
7073 * @param {int} seed int
|
rob@100
|
7074 *
|
rob@100
|
7075 * @returns {float}
|
rob@100
|
7076 *
|
rob@100
|
7077 * @see random
|
rob@100
|
7078 * @see radomSeed
|
rob@100
|
7079 * @see noise
|
rob@100
|
7080 * @see noiseDetail
|
rob@100
|
7081 */
|
rob@100
|
7082 p.noiseSeed = function(seed) {
|
rob@100
|
7083 noiseProfile.seed = seed;
|
rob@100
|
7084 noiseProfile.generator = undef;
|
rob@100
|
7085 };
|
rob@100
|
7086 };
|
rob@100
|
7087
|
rob@100
|
7088 },{}],23:[function(require,module,exports){
|
rob@100
|
7089 /**
|
rob@100
|
7090 * Common functions traditionally on "p" that should be class functions
|
rob@100
|
7091 * that get bound to "p" when an instance is actually built, instead.
|
rob@100
|
7092 */
|
rob@100
|
7093 module.exports = (function commonFunctions(undef) {
|
rob@100
|
7094
|
rob@100
|
7095 var CommonFunctions = {
|
rob@100
|
7096 /**
|
rob@100
|
7097 * Remove whitespace characters from the beginning and ending
|
rob@100
|
7098 * of a String or a String array. Works like String.trim() but includes the
|
rob@100
|
7099 * unicode nbsp character as well. If an array is passed in the function will return a new array not effecting the array passed in.
|
rob@100
|
7100 *
|
rob@100
|
7101 * @param {String} str the string to trim
|
rob@100
|
7102 * @param {String[]} str the string array to trim
|
rob@100
|
7103 *
|
rob@100
|
7104 * @return {String|String[]} retrurns a string or an array will removed whitespaces
|
rob@100
|
7105 */
|
rob@100
|
7106 trim: function(str) {
|
rob@100
|
7107 if (str instanceof Array) {
|
rob@100
|
7108 var arr = [];
|
rob@100
|
7109 for (var i = 0; i < str.length; i++) {
|
rob@100
|
7110 arr.push(str[i].replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, ''));
|
rob@100
|
7111 }
|
rob@100
|
7112 return arr;
|
rob@100
|
7113 }
|
rob@100
|
7114 return str.replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, '');
|
rob@100
|
7115 },
|
rob@100
|
7116
|
rob@100
|
7117 /**
|
rob@100
|
7118 * Converts a degree measurement to its corresponding value in radians. Radians and degrees are two ways of
|
rob@100
|
7119 * measuring the same thing. There are 360 degrees in a circle and 2*PI radians in a circle. For example,
|
rob@100
|
7120 * 90 degrees = PI/2 = 1.5707964. All trigonometric methods in Processing require their parameters to be specified in radians.
|
rob@100
|
7121 *
|
rob@100
|
7122 * @param {int|float} value an angle in radians
|
rob@100
|
7123 *
|
rob@100
|
7124 * @returns {float}
|
rob@100
|
7125 *
|
rob@100
|
7126 * @see degrees
|
rob@100
|
7127 */
|
rob@100
|
7128 radians: function(aAngle) {
|
rob@100
|
7129 return (aAngle / 180) * Math.PI;
|
rob@100
|
7130 },
|
rob@100
|
7131
|
rob@100
|
7132 /**
|
rob@100
|
7133 * Number-to-String formatting function. Prepends "plus" or "minus" depending
|
rob@100
|
7134 * on whether the value is positive or negative, respectively, after padding
|
rob@100
|
7135 * the value with zeroes on the left and right, the number of zeroes used dictated
|
rob@100
|
7136 * by the values 'leftDigits' and 'rightDigits'. 'value' cannot be an array.
|
rob@100
|
7137 *
|
rob@100
|
7138 * @param {int|float} value the number to format
|
rob@100
|
7139 * @param {String} plus the prefix for positive numbers
|
rob@100
|
7140 * @param {String} minus the prefix for negative numbers
|
rob@100
|
7141 * @param {int} left number of digits to the left of the decimal point
|
rob@100
|
7142 * @param {int} right number of digits to the right of the decimal point
|
rob@100
|
7143 * @param {String} group string delimited for groups, such as the comma in "1,000"
|
rob@100
|
7144 *
|
rob@100
|
7145 * @returns {String or String[]}
|
rob@100
|
7146 *
|
rob@100
|
7147 * @see nfCore
|
rob@100
|
7148 */
|
rob@100
|
7149 nfCoreScalar: function (value, plus, minus, leftDigits, rightDigits, group) {
|
rob@100
|
7150 var sign = (value < 0) ? minus : plus;
|
rob@100
|
7151 var autoDetectDecimals = rightDigits === 0;
|
rob@100
|
7152 var rightDigitsOfDefault = (rightDigits === undef || rightDigits < 0) ? 0 : rightDigits;
|
rob@100
|
7153
|
rob@100
|
7154 var absValue = Math.abs(value);
|
rob@100
|
7155 if (autoDetectDecimals) {
|
rob@100
|
7156 rightDigitsOfDefault = 1;
|
rob@100
|
7157 absValue *= 10;
|
rob@100
|
7158 while (Math.abs(Math.round(absValue) - absValue) > 1e-6 && rightDigitsOfDefault < 7) {
|
rob@100
|
7159 ++rightDigitsOfDefault;
|
rob@100
|
7160 absValue *= 10;
|
rob@100
|
7161 }
|
rob@100
|
7162 } else if (rightDigitsOfDefault !== 0) {
|
rob@100
|
7163 absValue *= Math.pow(10, rightDigitsOfDefault);
|
rob@100
|
7164 }
|
rob@100
|
7165
|
rob@100
|
7166 // Using Java's default rounding policy HALF_EVEN. This policy is based
|
rob@100
|
7167 // on the idea that 0.5 values round to the nearest even number, and
|
rob@100
|
7168 // everything else is rounded normally.
|
rob@100
|
7169 var number, doubled = absValue * 2;
|
rob@100
|
7170 if (Math.floor(absValue) === absValue) {
|
rob@100
|
7171 number = absValue;
|
rob@100
|
7172 } else if (Math.floor(doubled) === doubled) {
|
rob@100
|
7173 var floored = Math.floor(absValue);
|
rob@100
|
7174 number = floored + (floored % 2);
|
rob@100
|
7175 } else {
|
rob@100
|
7176 number = Math.round(absValue);
|
rob@100
|
7177 }
|
rob@100
|
7178
|
rob@100
|
7179 var buffer = "";
|
rob@100
|
7180 var totalDigits = leftDigits + rightDigitsOfDefault;
|
rob@100
|
7181 while (totalDigits > 0 || number > 0) {
|
rob@100
|
7182 totalDigits--;
|
rob@100
|
7183 buffer = "" + (number % 10) + buffer;
|
rob@100
|
7184 number = Math.floor(number / 10);
|
rob@100
|
7185 }
|
rob@100
|
7186 if (group !== undef) {
|
rob@100
|
7187 var i = buffer.length - 3 - rightDigitsOfDefault;
|
rob@100
|
7188 while(i > 0) {
|
rob@100
|
7189 buffer = buffer.substring(0,i) + group + buffer.substring(i);
|
rob@100
|
7190 i-=3;
|
rob@100
|
7191 }
|
rob@100
|
7192 }
|
rob@100
|
7193 if (rightDigitsOfDefault > 0) {
|
rob@100
|
7194 return sign + buffer.substring(0, buffer.length - rightDigitsOfDefault) +
|
rob@100
|
7195 "." + buffer.substring(buffer.length - rightDigitsOfDefault, buffer.length);
|
rob@100
|
7196 }
|
rob@100
|
7197 return sign + buffer;
|
rob@100
|
7198 },
|
rob@100
|
7199
|
rob@100
|
7200 /**
|
rob@100
|
7201 * Number-to-String formatting function. Prepends "plus" or "minus" depending
|
rob@100
|
7202 * on whether the value is positive or negative, respectively, after padding
|
rob@100
|
7203 * the value with zeroes on the left and right, the number of zeroes used dictated
|
rob@100
|
7204 * by the values 'leftDigits' and 'rightDigits'. 'value' can be an array;
|
rob@100
|
7205 * if the input is an array, each value in it is formatted separately, and
|
rob@100
|
7206 * an array with formatted values is returned.
|
rob@100
|
7207 *
|
rob@100
|
7208 * @param {int|int[]|float|float[]} value the number(s) to format
|
rob@100
|
7209 * @param {String} plus the prefix for positive numbers
|
rob@100
|
7210 * @param {String} minus the prefix for negative numbers
|
rob@100
|
7211 * @param {int} left number of digits to the left of the decimal point
|
rob@100
|
7212 * @param {int} right number of digits to the right of the decimal point
|
rob@100
|
7213 * @param {String} group string delimited for groups, such as the comma in "1,000"
|
rob@100
|
7214 *
|
rob@100
|
7215 * @returns {String or String[]}
|
rob@100
|
7216 *
|
rob@100
|
7217 * @see nfCoreScalar
|
rob@100
|
7218 */
|
rob@100
|
7219 nfCore: function(value, plus, minus, leftDigits, rightDigits, group) {
|
rob@100
|
7220 if (value instanceof Array) {
|
rob@100
|
7221 var arr = [];
|
rob@100
|
7222 for (var i = 0, len = value.length; i < len; i++) {
|
rob@100
|
7223 arr.push(CommonFunctions.nfCoreScalar(value[i], plus, minus, leftDigits, rightDigits, group));
|
rob@100
|
7224 }
|
rob@100
|
7225 return arr;
|
rob@100
|
7226 }
|
rob@100
|
7227 return CommonFunctions.nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group);
|
rob@100
|
7228 },
|
rob@100
|
7229
|
rob@100
|
7230 /**
|
rob@100
|
7231 * Utility function for formatting numbers into strings. There are two versions, one for
|
rob@100
|
7232 * formatting floats and one for formatting ints. The values for the digits, left, and
|
rob@100
|
7233 * right parameters should always be positive integers.
|
rob@100
|
7234 * As shown in the above example, nf() is used to add zeros to the left and/or right
|
rob@100
|
7235 * of a number. This is typically for aligning a list of numbers. To remove digits from
|
rob@100
|
7236 * a floating-point number, use the int(), ceil(), floor(), or round() functions.
|
rob@100
|
7237 *
|
rob@100
|
7238 * @param {int|int[]|float|float[]} value the number(s) to format
|
rob@100
|
7239 * @param {int} left number of digits to the left of the decimal point
|
rob@100
|
7240 * @param {int} right number of digits to the right of the decimal point
|
rob@100
|
7241 *
|
rob@100
|
7242 * @returns {String or String[]}
|
rob@100
|
7243 *
|
rob@100
|
7244 * @see nfs
|
rob@100
|
7245 * @see nfp
|
rob@100
|
7246 * @see nfc
|
rob@100
|
7247 */
|
rob@100
|
7248 nf: function(value, leftDigits, rightDigits) {
|
rob@100
|
7249 return CommonFunctions.nfCore(value, "", "-", leftDigits, rightDigits);
|
rob@100
|
7250 },
|
rob@100
|
7251
|
rob@100
|
7252 /**
|
rob@100
|
7253 * Utility function for formatting numbers into strings. Similar to nf() but leaves a blank space in front
|
rob@100
|
7254 * of positive numbers so they align with negative numbers in spite of the minus symbol. There are two
|
rob@100
|
7255 * versions, one for formatting floats and one for formatting ints. The values for the digits, left,
|
rob@100
|
7256 * and right parameters should always be positive integers.
|
rob@100
|
7257 *
|
rob@100
|
7258 * @param {int|int[]|float|float[]} value the number(s) to format
|
rob@100
|
7259 * @param {int} left number of digits to the left of the decimal point
|
rob@100
|
7260 * @param {int} right number of digits to the right of the decimal point
|
rob@100
|
7261 *
|
rob@100
|
7262 * @returns {String or String[]}
|
rob@100
|
7263 *
|
rob@100
|
7264 * @see nf
|
rob@100
|
7265 * @see nfp
|
rob@100
|
7266 * @see nfc
|
rob@100
|
7267 */
|
rob@100
|
7268 nfs: function(value, leftDigits, rightDigits) {
|
rob@100
|
7269 return CommonFunctions.nfCore(value, " ", "-", leftDigits, rightDigits);
|
rob@100
|
7270 },
|
rob@100
|
7271
|
rob@100
|
7272 /**
|
rob@100
|
7273 * Utility function for formatting numbers into strings. Similar to nf() but puts a "+" in front of
|
rob@100
|
7274 * positive numbers and a "-" in front of negative numbers. There are two versions, one for formatting
|
rob@100
|
7275 * floats and one for formatting ints. The values for the digits, left, and right parameters should
|
rob@100
|
7276 * always be positive integers.
|
rob@100
|
7277 *
|
rob@100
|
7278 * @param {int|int[]|float|float[]} value the number(s) to format
|
rob@100
|
7279 * @param {int} left number of digits to the left of the decimal point
|
rob@100
|
7280 * @param {int} right number of digits to the right of the decimal point
|
rob@100
|
7281 *
|
rob@100
|
7282 * @returns {String or String[]}
|
rob@100
|
7283 *
|
rob@100
|
7284 * @see nfs
|
rob@100
|
7285 * @see nf
|
rob@100
|
7286 * @see nfc
|
rob@100
|
7287 */
|
rob@100
|
7288 nfp: function(value, leftDigits, rightDigits) {
|
rob@100
|
7289 return CommonFunctions.nfCore(value, "+", "-", leftDigits, rightDigits);
|
rob@100
|
7290 },
|
rob@100
|
7291
|
rob@100
|
7292 /**
|
rob@100
|
7293 * Utility function for formatting numbers into strings and placing appropriate commas to mark
|
rob@100
|
7294 * units of 1000. There are two versions, one for formatting ints and one for formatting an array
|
rob@100
|
7295 * of ints. The value for the digits parameter should always be a positive integer.
|
rob@100
|
7296 *
|
rob@100
|
7297 * @param {int|int[]|float|float[]} value the number(s) to format
|
rob@100
|
7298 * @param {int} left number of digits to the left of the decimal point
|
rob@100
|
7299 * @param {int} right number of digits to the right of the decimal point
|
rob@100
|
7300 *
|
rob@100
|
7301 * @returns {String or String[]}
|
rob@100
|
7302 *
|
rob@100
|
7303 * @see nf
|
rob@100
|
7304 * @see nfs
|
rob@100
|
7305 * @see nfp
|
rob@100
|
7306 */
|
rob@100
|
7307 nfc: function(value, rightDigits) {
|
rob@100
|
7308 return CommonFunctions.nfCore(value, "", "-", 0, rightDigits, ",");
|
rob@100
|
7309 },
|
rob@100
|
7310
|
rob@100
|
7311 // used to bind all common functions to "p"
|
rob@100
|
7312 withCommonFunctions: function withCommonFunctions(p) {
|
rob@100
|
7313 ["trim", "radians", "nf", "nfs", "nfp", "nfc"].forEach(function(f){
|
rob@100
|
7314 p[f] = CommonFunctions[f];
|
rob@100
|
7315 });
|
rob@100
|
7316 }
|
rob@100
|
7317 };
|
rob@100
|
7318
|
rob@100
|
7319 return CommonFunctions;
|
rob@100
|
7320 }());
|
rob@100
|
7321
|
rob@100
|
7322 },{}],24:[function(require,module,exports){
|
rob@100
|
7323 /**
|
rob@100
|
7324 * Touch and Mouse event handling
|
rob@100
|
7325 */
|
rob@100
|
7326 module.exports = function withTouch(p, curElement, attachEventHandler, document, PConstants, undef) {
|
rob@100
|
7327
|
rob@100
|
7328 /**
|
rob@100
|
7329 * Determine the location of the (mouse) pointer.
|
rob@100
|
7330 */
|
rob@100
|
7331 function calculateOffset(curElement, event) {
|
rob@100
|
7332 var element = curElement,
|
rob@100
|
7333 offsetX = 0,
|
rob@100
|
7334 offsetY = 0;
|
rob@100
|
7335
|
rob@100
|
7336 p.pmouseX = p.mouseX;
|
rob@100
|
7337 p.pmouseY = p.mouseY;
|
rob@100
|
7338
|
rob@100
|
7339 // Find element offset
|
rob@100
|
7340 if (element.offsetParent) {
|
rob@100
|
7341 do {
|
rob@100
|
7342 offsetX += element.offsetLeft;
|
rob@100
|
7343 offsetY += element.offsetTop;
|
rob@100
|
7344 } while (!!(element = element.offsetParent));
|
rob@100
|
7345 }
|
rob@100
|
7346
|
rob@100
|
7347 // Find Scroll offset
|
rob@100
|
7348 element = curElement;
|
rob@100
|
7349 do {
|
rob@100
|
7350 offsetX -= element.scrollLeft || 0;
|
rob@100
|
7351 offsetY -= element.scrollTop || 0;
|
rob@100
|
7352 } while (!!(element = element.parentNode));
|
rob@100
|
7353
|
rob@100
|
7354 // Get padding and border style widths for mouse offsets
|
rob@100
|
7355 var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
|
rob@100
|
7356 if (document.defaultView && document.defaultView.getComputedStyle) {
|
rob@100
|
7357 stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(curElement, null).paddingLeft, 10) || 0;
|
rob@100
|
7358 stylePaddingTop = parseInt(document.defaultView.getComputedStyle(curElement, null).paddingTop, 10) || 0;
|
rob@100
|
7359 styleBorderLeft = parseInt(document.defaultView.getComputedStyle(curElement, null).borderLeftWidth, 10) || 0;
|
rob@100
|
7360 styleBorderTop = parseInt(document.defaultView.getComputedStyle(curElement, null).borderTopWidth, 10) || 0;
|
rob@100
|
7361 }
|
rob@100
|
7362
|
rob@100
|
7363 // Add padding and border style widths to offset
|
rob@100
|
7364 offsetX += stylePaddingLeft;
|
rob@100
|
7365 offsetY += stylePaddingTop;
|
rob@100
|
7366
|
rob@100
|
7367 offsetX += styleBorderLeft;
|
rob@100
|
7368 offsetY += styleBorderTop;
|
rob@100
|
7369
|
rob@100
|
7370 // Take into account any scrolling done
|
rob@100
|
7371 offsetX += window.pageXOffset;
|
rob@100
|
7372 offsetY += window.pageYOffset;
|
rob@100
|
7373
|
rob@100
|
7374 return {'X':offsetX,'Y':offsetY};
|
rob@100
|
7375 }
|
rob@100
|
7376
|
rob@100
|
7377 // simple relative position
|
rob@100
|
7378 function updateMousePosition(curElement, event) {
|
rob@100
|
7379 var offset = calculateOffset(curElement, event);
|
rob@100
|
7380 // Dropping support for IE clientX and clientY, switching to pageX and pageY
|
rob@100
|
7381 // so we don't have to calculate scroll offset.
|
rob@100
|
7382 // Removed in ticket #184. See rev: 2f106d1c7017fed92d045ba918db47d28e5c16f4
|
rob@100
|
7383 p.mouseX = event.pageX - offset.X;
|
rob@100
|
7384 p.mouseY = event.pageY - offset.Y;
|
rob@100
|
7385 }
|
rob@100
|
7386
|
rob@100
|
7387 /**
|
rob@100
|
7388 * Return a TouchEvent with canvas-specific x/y co-ordinates
|
rob@100
|
7389 */
|
rob@100
|
7390 function addTouchEventOffset(t) {
|
rob@100
|
7391 var offset = calculateOffset(t.changedTouches[0].target, t.changedTouches[0]),
|
rob@100
|
7392 i;
|
rob@100
|
7393
|
rob@100
|
7394 for (i = 0; i < t.touches.length; i++) {
|
rob@100
|
7395 var touch = t.touches[i];
|
rob@100
|
7396 touch.offsetX = touch.pageX - offset.X;
|
rob@100
|
7397 touch.offsetY = touch.pageY - offset.Y;
|
rob@100
|
7398 }
|
rob@100
|
7399 for (i = 0; i < t.targetTouches.length; i++) {
|
rob@100
|
7400 var targetTouch = t.targetTouches[i];
|
rob@100
|
7401 targetTouch.offsetX = targetTouch.pageX - offset.X;
|
rob@100
|
7402 targetTouch.offsetY = targetTouch.pageY - offset.Y;
|
rob@100
|
7403 }
|
rob@100
|
7404 for (i = 0; i < t.changedTouches.length; i++) {
|
rob@100
|
7405 var changedTouch = t.changedTouches[i];
|
rob@100
|
7406 changedTouch.offsetX = changedTouch.pageX - offset.X;
|
rob@100
|
7407 changedTouch.offsetY = changedTouch.pageY - offset.Y;
|
rob@100
|
7408 }
|
rob@100
|
7409
|
rob@100
|
7410 return t;
|
rob@100
|
7411 }
|
rob@100
|
7412
|
rob@100
|
7413 /**
|
rob@100
|
7414 * Touch event support.
|
rob@100
|
7415 */
|
rob@100
|
7416 attachEventHandler(curElement, "touchstart", function (t) {
|
rob@100
|
7417 // Removes unwanted behaviour of the canvas when touching canvas
|
rob@100
|
7418 curElement.setAttribute("style","-webkit-user-select: none");
|
rob@100
|
7419 curElement.setAttribute("onclick","void(0)");
|
rob@100
|
7420 curElement.setAttribute("style","-webkit-tap-highlight-color:rgba(0,0,0,0)");
|
rob@100
|
7421 // Loop though eventHandlers and remove mouse listeners
|
rob@100
|
7422 for (var i=0, ehl=eventHandlers.length; i<ehl; i++) {
|
rob@100
|
7423 var type = eventHandlers[i].type;
|
rob@100
|
7424 // Have this function remove itself from the eventHandlers list too
|
rob@100
|
7425 if (type === "mouseout" || type === "mousemove" ||
|
rob@100
|
7426 type === "mousedown" || type === "mouseup" ||
|
rob@100
|
7427 type === "DOMMouseScroll" || type === "mousewheel" || type === "touchstart") {
|
rob@100
|
7428 detachEventHandler(eventHandlers[i]);
|
rob@100
|
7429 }
|
rob@100
|
7430 }
|
rob@100
|
7431
|
rob@100
|
7432 // If there are any native touch events defined in the sketch, connect all of them
|
rob@100
|
7433 // Otherwise, connect all of the emulated mouse events
|
rob@100
|
7434 if (p.touchStart !== undef || p.touchMove !== undef ||
|
rob@100
|
7435 p.touchEnd !== undef || p.touchCancel !== undef) {
|
rob@100
|
7436 attachEventHandler(curElement, "touchstart", function(t) {
|
rob@100
|
7437 if (p.touchStart !== undef) {
|
rob@100
|
7438 t = addTouchEventOffset(t);
|
rob@100
|
7439 p.touchStart(t);
|
rob@100
|
7440 }
|
rob@100
|
7441 });
|
rob@100
|
7442
|
rob@100
|
7443 attachEventHandler(curElement, "touchmove", function(t) {
|
rob@100
|
7444 if (p.touchMove !== undef) {
|
rob@100
|
7445 t.preventDefault(); // Stop the viewport from scrolling
|
rob@100
|
7446 t = addTouchEventOffset(t);
|
rob@100
|
7447 p.touchMove(t);
|
rob@100
|
7448 }
|
rob@100
|
7449 });
|
rob@100
|
7450
|
rob@100
|
7451 attachEventHandler(curElement, "touchend", function(t) {
|
rob@100
|
7452 if (p.touchEnd !== undef) {
|
rob@100
|
7453 t = addTouchEventOffset(t);
|
rob@100
|
7454 p.touchEnd(t);
|
rob@100
|
7455 }
|
rob@100
|
7456 });
|
rob@100
|
7457
|
rob@100
|
7458 attachEventHandler(curElement, "touchcancel", function(t) {
|
rob@100
|
7459 if (p.touchCancel !== undef) {
|
rob@100
|
7460 t = addTouchEventOffset(t);
|
rob@100
|
7461 p.touchCancel(t);
|
rob@100
|
7462 }
|
rob@100
|
7463 });
|
rob@100
|
7464
|
rob@100
|
7465 } else {
|
rob@100
|
7466 // Emulated touch start/mouse down event
|
rob@100
|
7467 attachEventHandler(curElement, "touchstart", function(e) {
|
rob@100
|
7468 updateMousePosition(curElement, e.touches[0]);
|
rob@100
|
7469
|
rob@100
|
7470 p.__mousePressed = true;
|
rob@100
|
7471 p.mouseDragging = false;
|
rob@100
|
7472 p.mouseButton = PConstants.LEFT;
|
rob@100
|
7473
|
rob@100
|
7474 if (typeof p.mousePressed === "function") {
|
rob@100
|
7475 p.mousePressed();
|
rob@100
|
7476 }
|
rob@100
|
7477 });
|
rob@100
|
7478
|
rob@100
|
7479 // Emulated touch move/mouse move event
|
rob@100
|
7480 attachEventHandler(curElement, "touchmove", function(e) {
|
rob@100
|
7481 e.preventDefault();
|
rob@100
|
7482 updateMousePosition(curElement, e.touches[0]);
|
rob@100
|
7483
|
rob@100
|
7484 if (typeof p.mouseMoved === "function" && !p.__mousePressed) {
|
rob@100
|
7485 p.mouseMoved();
|
rob@100
|
7486 }
|
rob@100
|
7487 if (typeof p.mouseDragged === "function" && p.__mousePressed) {
|
rob@100
|
7488 p.mouseDragged();
|
rob@100
|
7489 p.mouseDragging = true;
|
rob@100
|
7490 }
|
rob@100
|
7491 });
|
rob@100
|
7492
|
rob@100
|
7493 // Emulated touch up/mouse up event
|
rob@100
|
7494 attachEventHandler(curElement, "touchend", function(e) {
|
rob@100
|
7495 p.__mousePressed = false;
|
rob@100
|
7496
|
rob@100
|
7497 if (typeof p.mouseClicked === "function" && !p.mouseDragging) {
|
rob@100
|
7498 p.mouseClicked();
|
rob@100
|
7499 }
|
rob@100
|
7500
|
rob@100
|
7501 if (typeof p.mouseReleased === "function") {
|
rob@100
|
7502 p.mouseReleased();
|
rob@100
|
7503 }
|
rob@100
|
7504 });
|
rob@100
|
7505 }
|
rob@100
|
7506
|
rob@100
|
7507 // Refire the touch start event we consumed in this function
|
rob@100
|
7508 curElement.dispatchEvent(t);
|
rob@100
|
7509 });
|
rob@100
|
7510
|
rob@100
|
7511 /**
|
rob@100
|
7512 * Context menu toggles. Most often you will not want the
|
rob@100
|
7513 * browser's context menu to show on a right click, but
|
rob@100
|
7514 * sometimes, you do, so we add two unofficial functions
|
rob@100
|
7515 * that can be used to trigger context menu behaviour.
|
rob@100
|
7516 */
|
rob@100
|
7517 (function() {
|
rob@100
|
7518 var enabled = true,
|
rob@100
|
7519 contextMenu = function(e) {
|
rob@100
|
7520 e.preventDefault();
|
rob@100
|
7521 e.stopPropagation();
|
rob@100
|
7522 };
|
rob@100
|
7523
|
rob@100
|
7524 p.disableContextMenu = function() {
|
rob@100
|
7525 if (!enabled) {
|
rob@100
|
7526 return;
|
rob@100
|
7527 }
|
rob@100
|
7528 attachEventHandler(curElement, 'contextmenu', contextMenu);
|
rob@100
|
7529 enabled = false;
|
rob@100
|
7530 };
|
rob@100
|
7531
|
rob@100
|
7532 p.enableContextMenu = function() {
|
rob@100
|
7533 if (enabled) {
|
rob@100
|
7534 return;
|
rob@100
|
7535 }
|
rob@100
|
7536 detachEventHandler({elem: curElement, type: 'contextmenu', fn: contextMenu});
|
rob@100
|
7537 enabled = true;
|
rob@100
|
7538 };
|
rob@100
|
7539 }());
|
rob@100
|
7540
|
rob@100
|
7541 /**
|
rob@100
|
7542 * Mouse moved or dragged
|
rob@100
|
7543 */
|
rob@100
|
7544 attachEventHandler(curElement, "mousemove", function(e) {
|
rob@100
|
7545 updateMousePosition(curElement, e);
|
rob@100
|
7546 if (typeof p.mouseMoved === "function" && !p.__mousePressed) {
|
rob@100
|
7547 p.mouseMoved();
|
rob@100
|
7548 }
|
rob@100
|
7549 if (typeof p.mouseDragged === "function" && p.__mousePressed) {
|
rob@100
|
7550 p.mouseDragged();
|
rob@100
|
7551 p.mouseDragging = true;
|
rob@100
|
7552 }
|
rob@100
|
7553 });
|
rob@100
|
7554
|
rob@100
|
7555 /**
|
rob@100
|
7556 * Unofficial mouse-out handling
|
rob@100
|
7557 */
|
rob@100
|
7558 attachEventHandler(curElement, "mouseout", function(e) {
|
rob@100
|
7559 if (typeof p.mouseOut === "function") {
|
rob@100
|
7560 p.mouseOut();
|
rob@100
|
7561 }
|
rob@100
|
7562 });
|
rob@100
|
7563
|
rob@100
|
7564 /**
|
rob@100
|
7565 * Mouse over
|
rob@100
|
7566 */
|
rob@100
|
7567 attachEventHandler(curElement, "mouseover", function(e) {
|
rob@100
|
7568 updateMousePosition(curElement, e);
|
rob@100
|
7569 if (typeof p.mouseOver === "function") {
|
rob@100
|
7570 p.mouseOver();
|
rob@100
|
7571 }
|
rob@100
|
7572 });
|
rob@100
|
7573
|
rob@100
|
7574 /**
|
rob@100
|
7575 * Disable browser's default handling for click-drag of a canvas.
|
rob@100
|
7576 */
|
rob@100
|
7577 curElement.onmousedown = function () {
|
rob@100
|
7578 // make sure focus happens, but nothing else
|
rob@100
|
7579 curElement.focus();
|
rob@100
|
7580 return false;
|
rob@100
|
7581 };
|
rob@100
|
7582
|
rob@100
|
7583 /**
|
rob@100
|
7584 * Mouse pressed or drag
|
rob@100
|
7585 */
|
rob@100
|
7586 attachEventHandler(curElement, "mousedown", function(e) {
|
rob@100
|
7587 p.__mousePressed = true;
|
rob@100
|
7588 p.mouseDragging = false;
|
rob@100
|
7589 switch (e.which) {
|
rob@100
|
7590 case 1:
|
rob@100
|
7591 p.mouseButton = PConstants.LEFT;
|
rob@100
|
7592 break;
|
rob@100
|
7593 case 2:
|
rob@100
|
7594 p.mouseButton = PConstants.CENTER;
|
rob@100
|
7595 break;
|
rob@100
|
7596 case 3:
|
rob@100
|
7597 p.mouseButton = PConstants.RIGHT;
|
rob@100
|
7598 break;
|
rob@100
|
7599 }
|
rob@100
|
7600
|
rob@100
|
7601 if (typeof p.mousePressed === "function") {
|
rob@100
|
7602 p.mousePressed();
|
rob@100
|
7603 }
|
rob@100
|
7604 });
|
rob@100
|
7605
|
rob@100
|
7606 /**
|
rob@100
|
7607 * Mouse clicked or released
|
rob@100
|
7608 */
|
rob@100
|
7609 attachEventHandler(curElement, "mouseup", function(e) {
|
rob@100
|
7610 p.__mousePressed = false;
|
rob@100
|
7611
|
rob@100
|
7612 if (typeof p.mouseClicked === "function" && !p.mouseDragging) {
|
rob@100
|
7613 p.mouseClicked();
|
rob@100
|
7614 }
|
rob@100
|
7615
|
rob@100
|
7616 if (typeof p.mouseReleased === "function") {
|
rob@100
|
7617 p.mouseReleased();
|
rob@100
|
7618 }
|
rob@100
|
7619 });
|
rob@100
|
7620
|
rob@100
|
7621 /**
|
rob@100
|
7622 * Unofficial scroll wheel handling.
|
rob@100
|
7623 */
|
rob@100
|
7624 var mouseWheelHandler = function(e) {
|
rob@100
|
7625 var delta = 0;
|
rob@100
|
7626
|
rob@100
|
7627 if (e.wheelDelta) {
|
rob@100
|
7628 delta = e.wheelDelta / 120;
|
rob@100
|
7629 if (window.opera) {
|
rob@100
|
7630 delta = -delta;
|
rob@100
|
7631 }
|
rob@100
|
7632 } else if (e.detail) {
|
rob@100
|
7633 delta = -e.detail / 3;
|
rob@100
|
7634 }
|
rob@100
|
7635
|
rob@100
|
7636 p.mouseScroll = delta;
|
rob@100
|
7637
|
rob@100
|
7638 if (delta && typeof p.mouseScrolled === 'function') {
|
rob@100
|
7639 p.mouseScrolled();
|
rob@100
|
7640 }
|
rob@100
|
7641 };
|
rob@100
|
7642
|
rob@100
|
7643 // Support Gecko and non-Gecko scroll events
|
rob@100
|
7644 attachEventHandler(document, 'DOMMouseScroll', mouseWheelHandler);
|
rob@100
|
7645 attachEventHandler(document, 'mousewheel', mouseWheelHandler);
|
rob@100
|
7646
|
rob@100
|
7647 };
|
rob@100
|
7648
|
rob@100
|
7649 },{}],25:[function(require,module,exports){
|
rob@100
|
7650 /**
|
rob@100
|
7651 * The parser for turning Processing syntax into Pjs JavaScript.
|
rob@100
|
7652 * This code is not trivial; unless you know what you're doing,
|
rob@100
|
7653 * you shouldn't be changing things in here =)
|
rob@100
|
7654 */
|
rob@100
|
7655 module.exports = function setupParser(Processing, options) {
|
rob@100
|
7656
|
rob@100
|
7657 var defaultScope = options.defaultScope,
|
rob@100
|
7658 PConstants = defaultScope.PConstants,
|
rob@100
|
7659 aFunctions = options.aFunctions,
|
rob@100
|
7660 Browser = options.Browser,
|
rob@100
|
7661 document = Browser.document,
|
rob@100
|
7662 undef;
|
rob@100
|
7663
|
rob@100
|
7664 // Processing global methods and constants for the parser
|
rob@100
|
7665 function getGlobalMembers() {
|
rob@100
|
7666 // The names array contains the names of everything that is inside "p."
|
rob@100
|
7667 // When something new is added to "p." it must also be added to this list.
|
rob@100
|
7668 var names = [ /* this code is generated by jsglobals.js */
|
rob@100
|
7669 "abs", "acos", "alpha", "ambient", "ambientLight", "append", "applyMatrix",
|
rob@100
|
7670 "arc", "arrayCopy", "asin", "atan", "atan2", "background", "beginCamera",
|
rob@100
|
7671 "beginDraw", "beginShape", "bezier", "bezierDetail", "bezierPoint",
|
rob@100
|
7672 "bezierTangent", "bezierVertex", "binary", "blend", "blendColor",
|
rob@100
|
7673 "blit_resize", "blue", "box", "breakShape", "brightness",
|
rob@100
|
7674 "camera", "ceil", "Character", "color", "colorMode",
|
rob@100
|
7675 "concat", "constrain", "copy", "cos", "createFont",
|
rob@100
|
7676 "createGraphics", "createImage", "cursor", "curve", "curveDetail",
|
rob@100
|
7677 "curvePoint", "curveTangent", "curveTightness", "curveVertex", "day",
|
rob@100
|
7678 "degrees", "directionalLight", "disableContextMenu",
|
rob@100
|
7679 "dist", "draw", "ellipse", "ellipseMode", "emissive", "enableContextMenu",
|
rob@100
|
7680 "endCamera", "endDraw", "endShape", "exit", "exp", "expand", "externals",
|
rob@100
|
7681 "fill", "filter", "floor", "focused", "frameCount", "frameRate", "frustum",
|
rob@100
|
7682 "get", "glyphLook", "glyphTable", "green", "height", "hex", "hint", "hour",
|
rob@100
|
7683 "hue", "image", "imageMode", "intersect", "join", "key",
|
rob@100
|
7684 "keyCode", "keyPressed", "keyReleased", "keyTyped", "lerp", "lerpColor",
|
rob@100
|
7685 "lightFalloff", "lights", "lightSpecular", "line", "link", "loadBytes",
|
rob@100
|
7686 "loadFont", "loadGlyphs", "loadImage", "loadPixels", "loadShape", "loadXML",
|
rob@100
|
7687 "loadStrings", "log", "loop", "mag", "map", "match", "matchAll", "max",
|
rob@100
|
7688 "millis", "min", "minute", "mix", "modelX", "modelY", "modelZ", "modes",
|
rob@100
|
7689 "month", "mouseButton", "mouseClicked", "mouseDragged", "mouseMoved",
|
rob@100
|
7690 "mouseOut", "mouseOver", "mousePressed", "mouseReleased", "mouseScroll",
|
rob@100
|
7691 "mouseScrolled", "mouseX", "mouseY", "name", "nf", "nfc", "nfp", "nfs",
|
rob@100
|
7692 "noCursor", "noFill", "noise", "noiseDetail", "noiseSeed", "noLights",
|
rob@100
|
7693 "noLoop", "norm", "normal", "noSmooth", "noStroke", "noTint", "ortho",
|
rob@100
|
7694 "param", "parseBoolean", "parseByte", "parseChar", "parseFloat",
|
rob@100
|
7695 "parseInt", "peg", "perspective", "PImage", "pixels", "PMatrix2D",
|
rob@100
|
7696 "PMatrix3D", "PMatrixStack", "pmouseX", "pmouseY", "point",
|
rob@100
|
7697 "pointLight", "popMatrix", "popStyle", "pow", "print", "printCamera",
|
rob@100
|
7698 "println", "printMatrix", "printProjection", "PShape", "PShapeSVG",
|
rob@100
|
7699 "pushMatrix", "pushStyle", "quad", "radians", "random", "randomGaussian",
|
rob@100
|
7700 "randomSeed", "rect", "rectMode", "red", "redraw", "requestImage",
|
rob@100
|
7701 "resetMatrix", "reverse", "rotate", "rotateX", "rotateY", "rotateZ",
|
rob@100
|
7702 "round", "saturation", "save", "saveFrame", "saveStrings", "scale",
|
rob@100
|
7703 "screenX", "screenY", "screenZ", "second", "set", "setup", "shape",
|
rob@100
|
7704 "shapeMode", "shared", "shearX", "shearY", "shininess", "shorten", "sin", "size", "smooth",
|
rob@100
|
7705 "sort", "specular", "sphere", "sphereDetail", "splice", "split",
|
rob@100
|
7706 "splitTokens", "spotLight", "sq", "sqrt", "status", "str", "stroke",
|
rob@100
|
7707 "strokeCap", "strokeJoin", "strokeWeight", "subset", "tan", "text",
|
rob@100
|
7708 "textAlign", "textAscent", "textDescent", "textFont", "textLeading",
|
rob@100
|
7709 "textMode", "textSize", "texture", "textureMode", "textWidth", "tint", "toImageData",
|
rob@100
|
7710 "touchCancel", "touchEnd", "touchMove", "touchStart", "translate", "transform",
|
rob@100
|
7711 "triangle", "trim", "unbinary", "unhex", "updatePixels", "use3DContext",
|
rob@100
|
7712 "vertex", "width", "XMLElement", "XML", "year", "__contains", "__equals",
|
rob@100
|
7713 "__equalsIgnoreCase", "__frameRate", "__hashCode", "__int_cast",
|
rob@100
|
7714 "__instanceof", "__keyPressed", "__mousePressed", "__printStackTrace",
|
rob@100
|
7715 "__replace", "__replaceAll", "__replaceFirst", "__toCharArray", "__split",
|
rob@100
|
7716 "__codePointAt", "__startsWith", "__endsWith", "__matches"];
|
rob@100
|
7717
|
rob@100
|
7718 // custom functions and properties are added here
|
rob@100
|
7719 if(aFunctions) {
|
rob@100
|
7720 Object.keys(aFunctions).forEach(function(name) {
|
rob@100
|
7721 names.push(name);
|
rob@100
|
7722 });
|
rob@100
|
7723 }
|
rob@100
|
7724
|
rob@100
|
7725 // custom libraries that were attached to Processing
|
rob@100
|
7726 var members = {};
|
rob@100
|
7727 var i, l;
|
rob@100
|
7728 for (i = 0, l = names.length; i < l ; ++i) {
|
rob@100
|
7729 members[names[i]] = null;
|
rob@100
|
7730 }
|
rob@100
|
7731 for (var lib in Processing.lib) {
|
rob@100
|
7732 if (Processing.lib.hasOwnProperty(lib)) {
|
rob@100
|
7733 if (Processing.lib[lib].exports) {
|
rob@100
|
7734 var exportedNames = Processing.lib[lib].exports;
|
rob@100
|
7735 for (i = 0, l = exportedNames.length; i < l; ++i) {
|
rob@100
|
7736 members[exportedNames[i]] = null;
|
rob@100
|
7737 }
|
rob@100
|
7738 }
|
rob@100
|
7739 }
|
rob@100
|
7740 }
|
rob@100
|
7741 return members;
|
rob@100
|
7742 }
|
rob@100
|
7743
|
rob@100
|
7744 /*
|
rob@100
|
7745
|
rob@100
|
7746 Parser converts Java-like syntax into JavaScript.
|
rob@100
|
7747 Creates an Abstract Syntax Tree -- "Light AST" from the Java-like code.
|
rob@100
|
7748
|
rob@100
|
7749 It is an object tree. The root object is created from the AstRoot class, which contains statements.
|
rob@100
|
7750
|
rob@100
|
7751 A statement object can be of type: AstForStatement, AstCatchStatement, AstPrefixStatement, AstMethod, AstClass,
|
rob@100
|
7752 AstInterface, AstFunction, AstStatementBlock and AstLabel.
|
rob@100
|
7753
|
rob@100
|
7754 AstPrefixStatement can be a statement of type: if, switch, while, with, do, else, finally, return, throw, try, break, and continue.
|
rob@100
|
7755
|
rob@100
|
7756 These object's toString function returns the JavaScript code for the statement.
|
rob@100
|
7757
|
rob@100
|
7758 Any processing calls need "processing." prepended to them.
|
rob@100
|
7759
|
rob@100
|
7760 Similarly, calls from inside classes need "$this_1.", prepended to them,
|
rob@100
|
7761 with 1 being the depth level for inner classes.
|
rob@100
|
7762 This includes members passed down from inheritance.
|
rob@100
|
7763
|
rob@100
|
7764 The resulting code is then eval'd and run.
|
rob@100
|
7765
|
rob@100
|
7766 */
|
rob@100
|
7767
|
rob@100
|
7768 function parseProcessing(code) {
|
rob@100
|
7769 var globalMembers = getGlobalMembers();
|
rob@100
|
7770
|
rob@100
|
7771 // masks parentheses, brackets and braces with '"A5"'
|
rob@100
|
7772 // where A is the bracket type, and 5 is the index in an array containing all brackets split into atoms
|
rob@100
|
7773 // 'while(true){}' -> 'while"B1""A2"'
|
rob@100
|
7774 // parentheses() = B, brackets[] = C and braces{} = A
|
rob@100
|
7775 function splitToAtoms(code) {
|
rob@100
|
7776 var atoms = [];
|
rob@100
|
7777 var items = code.split(/([\{\[\(\)\]\}])/);
|
rob@100
|
7778 var result = items[0];
|
rob@100
|
7779
|
rob@100
|
7780 var stack = [];
|
rob@100
|
7781 for(var i=1; i < items.length; i += 2) {
|
rob@100
|
7782 var item = items[i];
|
rob@100
|
7783 if(item === '[' || item === '{' || item === '(') {
|
rob@100
|
7784 stack.push(result); result = item;
|
rob@100
|
7785 } else if(item === ']' || item === '}' || item === ')') {
|
rob@100
|
7786 var kind = item === '}' ? 'A' : item === ')' ? 'B' : 'C';
|
rob@100
|
7787 var index = atoms.length; atoms.push(result + item);
|
rob@100
|
7788 result = stack.pop() + '"' + kind + (index + 1) + '"';
|
rob@100
|
7789 }
|
rob@100
|
7790 result += items[i + 1];
|
rob@100
|
7791 }
|
rob@100
|
7792 atoms.unshift(result);
|
rob@100
|
7793 return atoms;
|
rob@100
|
7794 }
|
rob@100
|
7795
|
rob@100
|
7796 // replaces strings and regexs keyed by index with an array of strings
|
rob@100
|
7797 function injectStrings(code, strings) {
|
rob@100
|
7798 return code.replace(/'(\d+)'/g, function(all, index) {
|
rob@100
|
7799 var val = strings[index];
|
rob@100
|
7800 if(val.charAt(0) === "/") {
|
rob@100
|
7801 return val;
|
rob@100
|
7802 }
|
rob@100
|
7803 return (/^'((?:[^'\\\n])|(?:\\.[0-9A-Fa-f]*))'$/).test(val) ? "(new $p.Character(" + val + "))" : val;
|
rob@100
|
7804 });
|
rob@100
|
7805 }
|
rob@100
|
7806
|
rob@100
|
7807 // trims off leading and trailing spaces
|
rob@100
|
7808 // returns an object. object.left, object.middle, object.right, object.untrim
|
rob@100
|
7809 function trimSpaces(string) {
|
rob@100
|
7810 var m1 = /^\s*/.exec(string), result;
|
rob@100
|
7811 if(m1[0].length === string.length) {
|
rob@100
|
7812 result = {left: m1[0], middle: "", right: ""};
|
rob@100
|
7813 } else {
|
rob@100
|
7814 var m2 = /\s*$/.exec(string);
|
rob@100
|
7815 result = {left: m1[0], middle: string.substring(m1[0].length, m2.index), right: m2[0]};
|
rob@100
|
7816 }
|
rob@100
|
7817 result.untrim = function(t) { return this.left + t + this.right; };
|
rob@100
|
7818 return result;
|
rob@100
|
7819 }
|
rob@100
|
7820
|
rob@100
|
7821 // simple trim of leading and trailing spaces
|
rob@100
|
7822 function trim(string) {
|
rob@100
|
7823 return string.replace(/^\s+/,'').replace(/\s+$/,'');
|
rob@100
|
7824 }
|
rob@100
|
7825
|
rob@100
|
7826 function appendToLookupTable(table, array) {
|
rob@100
|
7827 for(var i=0,l=array.length;i<l;++i) {
|
rob@100
|
7828 table[array[i]] = null;
|
rob@100
|
7829 }
|
rob@100
|
7830 return table;
|
rob@100
|
7831 }
|
rob@100
|
7832
|
rob@100
|
7833 function isLookupTableEmpty(table) {
|
rob@100
|
7834 for(var i in table) {
|
rob@100
|
7835 if(table.hasOwnProperty(i)) {
|
rob@100
|
7836 return false;
|
rob@100
|
7837 }
|
rob@100
|
7838 }
|
rob@100
|
7839 return true;
|
rob@100
|
7840 }
|
rob@100
|
7841
|
rob@100
|
7842 function getAtomIndex(templ) { return templ.substring(2, templ.length - 1); }
|
rob@100
|
7843
|
rob@100
|
7844 // remove carriage returns "\r"
|
rob@100
|
7845 var codeWoExtraCr = code.replace(/\r\n?|\n\r/g, "\n");
|
rob@100
|
7846
|
rob@100
|
7847 // masks strings and regexs with "'5'", where 5 is the index in an array containing all strings and regexs
|
rob@100
|
7848 // also removes all comments
|
rob@100
|
7849 var strings = [];
|
rob@100
|
7850 var codeWoStrings = codeWoExtraCr.replace(/("(?:[^"\\\n]|\\.)*")|('(?:[^'\\\n]|\\.)*')|(([\[\(=|&!\^:?]\s*)(\/(?![*\/])(?:[^\/\\\n]|\\.)*\/[gim]*)\b)|(\/\/[^\n]*\n)|(\/\*(?:(?!\*\/)(?:.|\n))*\*\/)/g,
|
rob@100
|
7851 function(all, quoted, aposed, regexCtx, prefix, regex, singleComment, comment) {
|
rob@100
|
7852 var index;
|
rob@100
|
7853 if(quoted || aposed) { // replace strings
|
rob@100
|
7854 index = strings.length; strings.push(all);
|
rob@100
|
7855 return "'" + index + "'";
|
rob@100
|
7856 }
|
rob@100
|
7857 if(regexCtx) { // replace RegExps
|
rob@100
|
7858 index = strings.length; strings.push(regex);
|
rob@100
|
7859 return prefix + "'" + index + "'";
|
rob@100
|
7860 }
|
rob@100
|
7861 // kill comments
|
rob@100
|
7862 return comment !== "" ? " " : "\n";
|
rob@100
|
7863 });
|
rob@100
|
7864
|
rob@100
|
7865 // protect character codes from namespace collision
|
rob@100
|
7866 codeWoStrings = codeWoStrings.replace(/__x([0-9A-F]{4})/g, function(all, hexCode) {
|
rob@100
|
7867 // $ = __x0024
|
rob@100
|
7868 // _ = __x005F
|
rob@100
|
7869 // this protects existing character codes from conversion
|
rob@100
|
7870 // __x0024 = __x005F_x0024
|
rob@100
|
7871 return "__x005F_x" + hexCode;
|
rob@100
|
7872 });
|
rob@100
|
7873
|
rob@100
|
7874 // convert dollar sign to character code
|
rob@100
|
7875 codeWoStrings = codeWoStrings.replace(/\$/g, "__x0024");
|
rob@100
|
7876
|
rob@100
|
7877 // Remove newlines after return statements
|
rob@100
|
7878 codeWoStrings = codeWoStrings.replace(/return\s*[\n\r]+/g, "return ");
|
rob@100
|
7879
|
rob@100
|
7880 // removes generics
|
rob@100
|
7881 var genericsWereRemoved;
|
rob@100
|
7882 var codeWoGenerics = codeWoStrings;
|
rob@100
|
7883 var replaceFunc = function(all, before, types, after) {
|
rob@100
|
7884 if(!!before || !!after) {
|
rob@100
|
7885 return all;
|
rob@100
|
7886 }
|
rob@100
|
7887 genericsWereRemoved = true;
|
rob@100
|
7888 return "";
|
rob@100
|
7889 };
|
rob@100
|
7890
|
rob@100
|
7891 do {
|
rob@100
|
7892 genericsWereRemoved = false;
|
rob@100
|
7893 codeWoGenerics = codeWoGenerics.replace(/([<]?)<\s*((?:\?|[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\[\])*(?:\s+(?:extends|super)\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)?(?:\s*,\s*(?:\?|[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\[\])*(?:\s+(?:extends|super)\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)?)*)\s*>([=]?)/g, replaceFunc);
|
rob@100
|
7894 } while (genericsWereRemoved);
|
rob@100
|
7895
|
rob@100
|
7896 var atoms = splitToAtoms(codeWoGenerics);
|
rob@100
|
7897 var replaceContext;
|
rob@100
|
7898 var declaredClasses = {}, currentClassId, classIdSeed = 0;
|
rob@100
|
7899
|
rob@100
|
7900 function addAtom(text, type) {
|
rob@100
|
7901 var lastIndex = atoms.length;
|
rob@100
|
7902 atoms.push(text);
|
rob@100
|
7903 return '"' + type + lastIndex + '"';
|
rob@100
|
7904 }
|
rob@100
|
7905
|
rob@100
|
7906 function generateClassId() {
|
rob@100
|
7907 return "class" + (++classIdSeed);
|
rob@100
|
7908 }
|
rob@100
|
7909
|
rob@100
|
7910 function appendClass(class_, classId, scopeId) {
|
rob@100
|
7911 class_.classId = classId;
|
rob@100
|
7912 class_.scopeId = scopeId;
|
rob@100
|
7913 declaredClasses[classId] = class_;
|
rob@100
|
7914 }
|
rob@100
|
7915
|
rob@100
|
7916 // functions defined below
|
rob@100
|
7917 var transformClassBody, transformInterfaceBody, transformStatementsBlock, transformStatements, transformMain, transformExpression;
|
rob@100
|
7918
|
rob@100
|
7919 var classesRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)(class|interface)\s+([A-Za-z_$][\w$]*\b)(\s+extends\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\b)*)?(\s+implements\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\b)*)?\s*("A\d+")/g;
|
rob@100
|
7920 var methodsRegex = /\b((?:(?:public|private|final|protected|static|abstract|synchronized)\s+)*)((?!(?:else|new|return|throw|function|public|private|protected)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*([A-Za-z_$][\w$]*\b)\s*("B\d+")(\s*throws\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)*)?\s*("A\d+"|;)/g;
|
rob@100
|
7921 var fieldTest = /^((?:(?:public|private|final|protected|static)\s+)*)((?!(?:else|new|return|throw)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*([A-Za-z_$][\w$]*\b)\s*(?:"C\d+"\s*)*([=,]|$)/;
|
rob@100
|
7922 var cstrsRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)((?!(?:new|return|throw)\b)[A-Za-z_$][\w$]*\b)\s*("B\d+")(\s*throws\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)*)?\s*("A\d+")/g;
|
rob@100
|
7923 var attrAndTypeRegex = /^((?:(?:public|private|final|protected|static)\s+)*)((?!(?:new|return|throw)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*/;
|
rob@100
|
7924 var functionsRegex = /\bfunction(?:\s+([A-Za-z_$][\w$]*))?\s*("B\d+")\s*("A\d+")/g;
|
rob@100
|
7925
|
rob@100
|
7926 // This converts classes, methods and functions into atoms, and adds them to the atoms array.
|
rob@100
|
7927 // classes = E, methods = D and functions = H
|
rob@100
|
7928 function extractClassesAndMethods(code) {
|
rob@100
|
7929 var s = code;
|
rob@100
|
7930 s = s.replace(classesRegex, function(all) {
|
rob@100
|
7931 return addAtom(all, 'E');
|
rob@100
|
7932 });
|
rob@100
|
7933 s = s.replace(methodsRegex, function(all) {
|
rob@100
|
7934 return addAtom(all, 'D');
|
rob@100
|
7935 });
|
rob@100
|
7936 s = s.replace(functionsRegex, function(all) {
|
rob@100
|
7937 return addAtom(all, 'H');
|
rob@100
|
7938 });
|
rob@100
|
7939 return s;
|
rob@100
|
7940 }
|
rob@100
|
7941
|
rob@100
|
7942 // This converts constructors into atoms, and adds them to the atoms array.
|
rob@100
|
7943 // constructors = G
|
rob@100
|
7944 function extractConstructors(code, className) {
|
rob@100
|
7945 var result = code.replace(cstrsRegex, function(all, attr, name, params, throws_, body) {
|
rob@100
|
7946 if(name !== className) {
|
rob@100
|
7947 return all;
|
rob@100
|
7948 }
|
rob@100
|
7949 return addAtom(all, 'G');
|
rob@100
|
7950 });
|
rob@100
|
7951 return result;
|
rob@100
|
7952 }
|
rob@100
|
7953
|
rob@100
|
7954 // AstParam contains the name of a parameter inside a function declaration
|
rob@100
|
7955 function AstParam(name) {
|
rob@100
|
7956 this.name = name;
|
rob@100
|
7957 }
|
rob@100
|
7958 AstParam.prototype.toString = function() {
|
rob@100
|
7959 return this.name;
|
rob@100
|
7960 };
|
rob@100
|
7961 // AstParams contains an array of AstParam objects
|
rob@100
|
7962 function AstParams(params, methodArgsParam) {
|
rob@100
|
7963 this.params = params;
|
rob@100
|
7964 this.methodArgsParam = methodArgsParam;
|
rob@100
|
7965 }
|
rob@100
|
7966 AstParams.prototype.getNames = function() {
|
rob@100
|
7967 var names = [];
|
rob@100
|
7968 for(var i=0,l=this.params.length;i<l;++i) {
|
rob@100
|
7969 names.push(this.params[i].name);
|
rob@100
|
7970 }
|
rob@100
|
7971 return names;
|
rob@100
|
7972 };
|
rob@100
|
7973 AstParams.prototype.prependMethodArgs = function(body) {
|
rob@100
|
7974 if (!this.methodArgsParam) {
|
rob@100
|
7975 return body;
|
rob@100
|
7976 }
|
rob@100
|
7977 return "{\nvar " + this.methodArgsParam.name +
|
rob@100
|
7978 " = Array.prototype.slice.call(arguments, " +
|
rob@100
|
7979 this.params.length + ");\n" + body.substring(1);
|
rob@100
|
7980 };
|
rob@100
|
7981 AstParams.prototype.toString = function() {
|
rob@100
|
7982 if(this.params.length === 0) {
|
rob@100
|
7983 return "()";
|
rob@100
|
7984 }
|
rob@100
|
7985 var result = "(";
|
rob@100
|
7986 for(var i=0,l=this.params.length;i<l;++i) {
|
rob@100
|
7987 result += this.params[i] + ", ";
|
rob@100
|
7988 }
|
rob@100
|
7989 return result.substring(0, result.length - 2) + ")";
|
rob@100
|
7990 };
|
rob@100
|
7991
|
rob@100
|
7992 function transformParams(params) {
|
rob@100
|
7993 var paramsWoPars = trim(params.substring(1, params.length - 1));
|
rob@100
|
7994 var result = [], methodArgsParam = null;
|
rob@100
|
7995 if(paramsWoPars !== "") {
|
rob@100
|
7996 var paramList = paramsWoPars.split(",");
|
rob@100
|
7997 for(var i=0; i < paramList.length; ++i) {
|
rob@100
|
7998 var param = /\b([A-Za-z_$][\w$]*\b)(\s*"[ABC][\d]*")*\s*$/.exec(paramList[i]);
|
rob@100
|
7999 if (i === paramList.length - 1 && paramList[i].indexOf('...') >= 0) {
|
rob@100
|
8000 methodArgsParam = new AstParam(param[1]);
|
rob@100
|
8001 break;
|
rob@100
|
8002 }
|
rob@100
|
8003 result.push(new AstParam(param[1]));
|
rob@100
|
8004 }
|
rob@100
|
8005 }
|
rob@100
|
8006 return new AstParams(result, methodArgsParam);
|
rob@100
|
8007 }
|
rob@100
|
8008
|
rob@100
|
8009 function preExpressionTransform(expr) {
|
rob@100
|
8010 var s = expr;
|
rob@100
|
8011 // new type[] {...} --> {...}
|
rob@100
|
8012 s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s*"C\d+")+\s*("A\d+")/g, function(all, type, init) {
|
rob@100
|
8013 return init;
|
rob@100
|
8014 });
|
rob@100
|
8015 // new Runnable() {...} --> "F???"
|
rob@100
|
8016 s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s*"B\d+")\s*("A\d+")/g, function(all, type, init) {
|
rob@100
|
8017 return addAtom(all, 'F');
|
rob@100
|
8018 });
|
rob@100
|
8019 // function(...) { } --> "H???"
|
rob@100
|
8020 s = s.replace(functionsRegex, function(all) {
|
rob@100
|
8021 return addAtom(all, 'H');
|
rob@100
|
8022 });
|
rob@100
|
8023 // new type[?] --> createJavaArray('type', [?])
|
rob@100
|
8024 s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)\s*("C\d+"(?:\s*"C\d+")*)/g, function(all, type, index) {
|
rob@100
|
8025 var args = index.replace(/"C(\d+)"/g, function(all, j) { return atoms[j]; })
|
rob@100
|
8026 .replace(/\[\s*\]/g, "[null]").replace(/\s*\]\s*\[\s*/g, ", ");
|
rob@100
|
8027 var arrayInitializer = "{" + args.substring(1, args.length - 1) + "}";
|
rob@100
|
8028 var createArrayArgs = "('" + type + "', " + addAtom(arrayInitializer, 'A') + ")";
|
rob@100
|
8029 return '$p.createJavaArray' + addAtom(createArrayArgs, 'B');
|
rob@100
|
8030 });
|
rob@100
|
8031 // .length() --> .length
|
rob@100
|
8032 s = s.replace(/(\.\s*length)\s*"B\d+"/g, "$1");
|
rob@100
|
8033 // #000000 --> 0x000000
|
rob@100
|
8034 s = s.replace(/#([0-9A-Fa-f]{6})\b/g, function(all, digits) {
|
rob@100
|
8035 return "0xFF" + digits;
|
rob@100
|
8036 });
|
rob@100
|
8037 // delete (type)???, except (int)???
|
rob@100
|
8038 s = s.replace(/"B(\d+)"(\s*(?:[\w$']|"B))/g, function(all, index, next) {
|
rob@100
|
8039 var atom = atoms[index];
|
rob@100
|
8040 if(!/^\(\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\s*(?:"C\d+"\s*)*\)$/.test(atom)) {
|
rob@100
|
8041 return all;
|
rob@100
|
8042 }
|
rob@100
|
8043 if(/^\(\s*int\s*\)$/.test(atom)) {
|
rob@100
|
8044 return "(int)" + next;
|
rob@100
|
8045 }
|
rob@100
|
8046 var indexParts = atom.split(/"C(\d+)"/g);
|
rob@100
|
8047 if(indexParts.length > 1) {
|
rob@100
|
8048 // even items contains atom numbers, can check only first
|
rob@100
|
8049 if(! /^\[\s*\]$/.test(atoms[indexParts[1]])) {
|
rob@100
|
8050 return all; // fallback - not a cast
|
rob@100
|
8051 }
|
rob@100
|
8052 }
|
rob@100
|
8053 return "" + next;
|
rob@100
|
8054 });
|
rob@100
|
8055 // (int)??? -> __int_cast(???)
|
rob@100
|
8056 s = s.replace(/\(int\)([^,\]\)\}\?\:\*\+\-\/\^\|\%\&\~<\>\=]+)/g, function(all, arg) {
|
rob@100
|
8057 var trimmed = trimSpaces(arg);
|
rob@100
|
8058 return trimmed.untrim("__int_cast(" + trimmed.middle + ")");
|
rob@100
|
8059 });
|
rob@100
|
8060 // super() -> $superCstr(), super. -> $super.;
|
rob@100
|
8061 s = s.replace(/\bsuper(\s*"B\d+")/g, "$$superCstr$1").replace(/\bsuper(\s*\.)/g, "$$super$1");
|
rob@100
|
8062 // 000.43->0.43 and 0010f->10, but not 0010
|
rob@100
|
8063 s = s.replace(/\b0+((\d*)(?:\.[\d*])?(?:[eE][\-\+]?\d+)?[fF]?)\b/, function(all, numberWo0, intPart) {
|
rob@100
|
8064 if( numberWo0 === intPart) {
|
rob@100
|
8065 return all;
|
rob@100
|
8066 }
|
rob@100
|
8067 return intPart === "" ? "0" + numberWo0 : numberWo0;
|
rob@100
|
8068 });
|
rob@100
|
8069 // 3.0f -> 3.0
|
rob@100
|
8070 s = s.replace(/\b(\.?\d+\.?)[fF]\b/g, "$1");
|
rob@100
|
8071 // Weird (?) parsing errors with %
|
rob@100
|
8072 s = s.replace(/([^\s])%([^=\s])/g, "$1 % $2");
|
rob@100
|
8073 // Since frameRate() and frameRate are different things,
|
rob@100
|
8074 // we need to differentiate them somehow. So when we parse
|
rob@100
|
8075 // the Processing.js source, replace frameRate so it isn't
|
rob@100
|
8076 // confused with frameRate(), as well as keyPressed and mousePressed
|
rob@100
|
8077 s = s.replace(/\b(frameRate|keyPressed|mousePressed)\b(?!\s*"B)/g, "__$1");
|
rob@100
|
8078 // "boolean", "byte", "int", etc. => "parseBoolean", "parseByte", "parseInt", etc.
|
rob@100
|
8079 s = s.replace(/\b(boolean|byte|char|float|int)\s*"B/g, function(all, name) {
|
rob@100
|
8080 return "parse" + name.substring(0, 1).toUpperCase() + name.substring(1) + "\"B";
|
rob@100
|
8081 });
|
rob@100
|
8082 // "pixels" replacements:
|
rob@100
|
8083 // pixels[i] = c => pixels.setPixel(i,c) | pixels[i] => pixels.getPixel(i)
|
rob@100
|
8084 // pixels.length => pixels.getLength()
|
rob@100
|
8085 // pixels = ar => pixels.set(ar) | pixels => pixels.toArray()
|
rob@100
|
8086 s = s.replace(/\bpixels\b\s*(("C(\d+)")|\.length)?(\s*=(?!=)([^,\]\)\}]+))?/g,
|
rob@100
|
8087 function(all, indexOrLength, index, atomIndex, equalsPart, rightSide) {
|
rob@100
|
8088 if(index) {
|
rob@100
|
8089 var atom = atoms[atomIndex];
|
rob@100
|
8090 if(equalsPart) {
|
rob@100
|
8091 return "pixels.setPixel" + addAtom("(" +atom.substring(1, atom.length - 1) +
|
rob@100
|
8092 "," + rightSide + ")", 'B');
|
rob@100
|
8093 }
|
rob@100
|
8094 return "pixels.getPixel" + addAtom("(" + atom.substring(1, atom.length - 1) +
|
rob@100
|
8095 ")", 'B');
|
rob@100
|
8096 }
|
rob@100
|
8097 if(indexOrLength) {
|
rob@100
|
8098 // length
|
rob@100
|
8099 return "pixels.getLength" + addAtom("()", 'B');
|
rob@100
|
8100 }
|
rob@100
|
8101 if(equalsPart) {
|
rob@100
|
8102 return "pixels.set" + addAtom("(" + rightSide + ")", 'B');
|
rob@100
|
8103 }
|
rob@100
|
8104 return "pixels.toArray" + addAtom("()", 'B');
|
rob@100
|
8105 });
|
rob@100
|
8106 // Java method replacements for: replace, replaceAll, replaceFirst, equals, hashCode, etc.
|
rob@100
|
8107 // xxx.replace(yyy) -> __replace(xxx, yyy)
|
rob@100
|
8108 // "xx".replace(yyy) -> __replace("xx", yyy)
|
rob@100
|
8109 var repeatJavaReplacement;
|
rob@100
|
8110 function replacePrototypeMethods(all, subject, method, atomIndex) {
|
rob@100
|
8111 var atom = atoms[atomIndex];
|
rob@100
|
8112 repeatJavaReplacement = true;
|
rob@100
|
8113 var trimmed = trimSpaces(atom.substring(1, atom.length - 1));
|
rob@100
|
8114 return "__" + method + ( trimmed.middle === "" ? addAtom("(" + subject.replace(/\.\s*$/, "") + ")", 'B') :
|
rob@100
|
8115 addAtom("(" + subject.replace(/\.\s*$/, "") + "," + trimmed.middle + ")", 'B') );
|
rob@100
|
8116 }
|
rob@100
|
8117 do {
|
rob@100
|
8118 repeatJavaReplacement = false;
|
rob@100
|
8119 s = s.replace(/((?:'\d+'|\b[A-Za-z_$][\w$]*\s*(?:"[BC]\d+")*)\s*\.\s*(?:[A-Za-z_$][\w$]*\s*(?:"[BC]\d+"\s*)*\.\s*)*)(replace|replaceAll|replaceFirst|contains|equals|equalsIgnoreCase|hashCode|toCharArray|printStackTrace|split|startsWith|endsWith|codePointAt|matches)\s*"B(\d+)"/g,
|
rob@100
|
8120 replacePrototypeMethods);
|
rob@100
|
8121 } while (repeatJavaReplacement);
|
rob@100
|
8122 // xxx instanceof yyy -> __instanceof(xxx, yyy)
|
rob@100
|
8123 function replaceInstanceof(all, subject, type) {
|
rob@100
|
8124 repeatJavaReplacement = true;
|
rob@100
|
8125 return "__instanceof" + addAtom("(" + subject + ", " + type + ")", 'B');
|
rob@100
|
8126 }
|
rob@100
|
8127 do {
|
rob@100
|
8128 repeatJavaReplacement = false;
|
rob@100
|
8129 s = s.replace(/((?:'\d+'|\b[A-Za-z_$][\w$]*\s*(?:"[BC]\d+")*)\s*(?:\.\s*[A-Za-z_$][\w$]*\s*(?:"[BC]\d+"\s*)*)*)instanceof\s+([A-Za-z_$][\w$]*\s*(?:\.\s*[A-Za-z_$][\w$]*)*)/g,
|
rob@100
|
8130 replaceInstanceof);
|
rob@100
|
8131 } while (repeatJavaReplacement);
|
rob@100
|
8132 // this() -> $constr()
|
rob@100
|
8133 s = s.replace(/\bthis(\s*"B\d+")/g, "$$constr$1");
|
rob@100
|
8134
|
rob@100
|
8135 return s;
|
rob@100
|
8136 }
|
rob@100
|
8137
|
rob@100
|
8138 function AstInlineClass(baseInterfaceName, body) {
|
rob@100
|
8139 this.baseInterfaceName = baseInterfaceName;
|
rob@100
|
8140 this.body = body;
|
rob@100
|
8141 body.owner = this;
|
rob@100
|
8142 }
|
rob@100
|
8143 AstInlineClass.prototype.toString = function() {
|
rob@100
|
8144 return "new (" + this.body + ")";
|
rob@100
|
8145 };
|
rob@100
|
8146
|
rob@100
|
8147 function transformInlineClass(class_) {
|
rob@100
|
8148 var m = new RegExp(/\bnew\s*([A-Za-z_$][\w$]*\s*(?:\.\s*[A-Za-z_$][\w$]*)*)\s*"B\d+"\s*"A(\d+)"/).exec(class_);
|
rob@100
|
8149 var oldClassId = currentClassId, newClassId = generateClassId();
|
rob@100
|
8150 currentClassId = newClassId;
|
rob@100
|
8151 var uniqueClassName = m[1] + "$" + newClassId;
|
rob@100
|
8152 var inlineClass = new AstInlineClass(uniqueClassName,
|
rob@100
|
8153 transformClassBody(atoms[m[2]], uniqueClassName, "", "implements " + m[1]));
|
rob@100
|
8154 appendClass(inlineClass, newClassId, oldClassId);
|
rob@100
|
8155 currentClassId = oldClassId;
|
rob@100
|
8156 return inlineClass;
|
rob@100
|
8157 }
|
rob@100
|
8158
|
rob@100
|
8159 function AstFunction(name, params, body) {
|
rob@100
|
8160 this.name = name;
|
rob@100
|
8161 this.params = params;
|
rob@100
|
8162 this.body = body;
|
rob@100
|
8163 }
|
rob@100
|
8164 AstFunction.prototype.toString = function() {
|
rob@100
|
8165 var oldContext = replaceContext;
|
rob@100
|
8166 // saving "this." and parameters
|
rob@100
|
8167 var names = appendToLookupTable({"this":null}, this.params.getNames());
|
rob@100
|
8168 replaceContext = function (subject) {
|
rob@100
|
8169 return names.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
|
rob@100
|
8170 };
|
rob@100
|
8171 var result = "function";
|
rob@100
|
8172 if(this.name) {
|
rob@100
|
8173 result += " " + this.name;
|
rob@100
|
8174 }
|
rob@100
|
8175 var body = this.params.prependMethodArgs(this.body.toString());
|
rob@100
|
8176 result += this.params + " " + body;
|
rob@100
|
8177 replaceContext = oldContext;
|
rob@100
|
8178 return result;
|
rob@100
|
8179 };
|
rob@100
|
8180
|
rob@100
|
8181 function transformFunction(class_) {
|
rob@100
|
8182 var m = new RegExp(/\b([A-Za-z_$][\w$]*)\s*"B(\d+)"\s*"A(\d+)"/).exec(class_);
|
rob@100
|
8183 return new AstFunction( m[1] !== "function" ? m[1] : null,
|
rob@100
|
8184 transformParams(atoms[m[2]]), transformStatementsBlock(atoms[m[3]]));
|
rob@100
|
8185 }
|
rob@100
|
8186
|
rob@100
|
8187 function AstInlineObject(members) {
|
rob@100
|
8188 this.members = members;
|
rob@100
|
8189 }
|
rob@100
|
8190 AstInlineObject.prototype.toString = function() {
|
rob@100
|
8191 var oldContext = replaceContext;
|
rob@100
|
8192 replaceContext = function (subject) {
|
rob@100
|
8193 return subject.name === "this" ? "this" : oldContext(subject); // saving "this."
|
rob@100
|
8194 };
|
rob@100
|
8195 var result = "";
|
rob@100
|
8196 for(var i=0,l=this.members.length;i<l;++i) {
|
rob@100
|
8197 if(this.members[i].label) {
|
rob@100
|
8198 result += this.members[i].label + ": ";
|
rob@100
|
8199 }
|
rob@100
|
8200 result += this.members[i].value.toString() + ", ";
|
rob@100
|
8201 }
|
rob@100
|
8202 replaceContext = oldContext;
|
rob@100
|
8203 return result.substring(0, result.length - 2);
|
rob@100
|
8204 };
|
rob@100
|
8205
|
rob@100
|
8206 function transformInlineObject(obj) {
|
rob@100
|
8207 var members = obj.split(',');
|
rob@100
|
8208 for(var i=0; i < members.length; ++i) {
|
rob@100
|
8209 var label = members[i].indexOf(':');
|
rob@100
|
8210 if(label < 0) {
|
rob@100
|
8211 members[i] = { value: transformExpression(members[i]) };
|
rob@100
|
8212 } else {
|
rob@100
|
8213 members[i] = { label: trim(members[i].substring(0, label)),
|
rob@100
|
8214 value: transformExpression( trim(members[i].substring(label + 1)) ) };
|
rob@100
|
8215 }
|
rob@100
|
8216 }
|
rob@100
|
8217 return new AstInlineObject(members);
|
rob@100
|
8218 }
|
rob@100
|
8219
|
rob@100
|
8220 function expandExpression(expr) {
|
rob@100
|
8221 if(expr.charAt(0) === '(' || expr.charAt(0) === '[') {
|
rob@100
|
8222 return expr.charAt(0) + expandExpression(expr.substring(1, expr.length - 1)) + expr.charAt(expr.length - 1);
|
rob@100
|
8223 }
|
rob@100
|
8224 if(expr.charAt(0) === '{') {
|
rob@100
|
8225 if(/^\{\s*(?:[A-Za-z_$][\w$]*|'\d+')\s*:/.test(expr)) {
|
rob@100
|
8226 return "{" + addAtom(expr.substring(1, expr.length - 1), 'I') + "}";
|
rob@100
|
8227 }
|
rob@100
|
8228 return "[" + expandExpression(expr.substring(1, expr.length - 1)) + "]";
|
rob@100
|
8229 }
|
rob@100
|
8230 var trimmed = trimSpaces(expr);
|
rob@100
|
8231 var result = preExpressionTransform(trimmed.middle);
|
rob@100
|
8232 result = result.replace(/"[ABC](\d+)"/g, function(all, index) {
|
rob@100
|
8233 return expandExpression(atoms[index]);
|
rob@100
|
8234 });
|
rob@100
|
8235 return trimmed.untrim(result);
|
rob@100
|
8236 }
|
rob@100
|
8237
|
rob@100
|
8238 function replaceContextInVars(expr) {
|
rob@100
|
8239 return expr.replace(/(\.\s*)?((?:\b[A-Za-z_]|\$)[\w$]*)(\s*\.\s*([A-Za-z_$][\w$]*)(\s*\()?)?/g,
|
rob@100
|
8240 function(all, memberAccessSign, identifier, suffix, subMember, callSign) {
|
rob@100
|
8241 if(memberAccessSign) {
|
rob@100
|
8242 return all;
|
rob@100
|
8243 }
|
rob@100
|
8244 var subject = { name: identifier, member: subMember, callSign: !!callSign };
|
rob@100
|
8245 return replaceContext(subject) + (suffix === undef ? "" : suffix);
|
rob@100
|
8246 });
|
rob@100
|
8247 }
|
rob@100
|
8248
|
rob@100
|
8249 function AstExpression(expr, transforms) {
|
rob@100
|
8250 this.expr = expr;
|
rob@100
|
8251 this.transforms = transforms;
|
rob@100
|
8252 }
|
rob@100
|
8253 AstExpression.prototype.toString = function() {
|
rob@100
|
8254 var transforms = this.transforms;
|
rob@100
|
8255 var expr = replaceContextInVars(this.expr);
|
rob@100
|
8256 return expr.replace(/"!(\d+)"/g, function(all, index) {
|
rob@100
|
8257 return transforms[index].toString();
|
rob@100
|
8258 });
|
rob@100
|
8259 };
|
rob@100
|
8260
|
rob@100
|
8261 transformExpression = function(expr) {
|
rob@100
|
8262 var transforms = [];
|
rob@100
|
8263 var s = expandExpression(expr);
|
rob@100
|
8264 s = s.replace(/"H(\d+)"/g, function(all, index) {
|
rob@100
|
8265 transforms.push(transformFunction(atoms[index]));
|
rob@100
|
8266 return '"!' + (transforms.length - 1) + '"';
|
rob@100
|
8267 });
|
rob@100
|
8268 s = s.replace(/"F(\d+)"/g, function(all, index) {
|
rob@100
|
8269 transforms.push(transformInlineClass(atoms[index]));
|
rob@100
|
8270 return '"!' + (transforms.length - 1) + '"';
|
rob@100
|
8271 });
|
rob@100
|
8272 s = s.replace(/"I(\d+)"/g, function(all, index) {
|
rob@100
|
8273 transforms.push(transformInlineObject(atoms[index]));
|
rob@100
|
8274 return '"!' + (transforms.length - 1) + '"';
|
rob@100
|
8275 });
|
rob@100
|
8276
|
rob@100
|
8277 return new AstExpression(s, transforms);
|
rob@100
|
8278 };
|
rob@100
|
8279
|
rob@100
|
8280 function AstVarDefinition(name, value, isDefault) {
|
rob@100
|
8281 this.name = name;
|
rob@100
|
8282 this.value = value;
|
rob@100
|
8283 this.isDefault = isDefault;
|
rob@100
|
8284 }
|
rob@100
|
8285 AstVarDefinition.prototype.toString = function() {
|
rob@100
|
8286 return this.name + ' = ' + this.value;
|
rob@100
|
8287 };
|
rob@100
|
8288
|
rob@100
|
8289 function transformVarDefinition(def, defaultTypeValue) {
|
rob@100
|
8290 var eqIndex = def.indexOf("=");
|
rob@100
|
8291 var name, value, isDefault;
|
rob@100
|
8292 if(eqIndex < 0) {
|
rob@100
|
8293 name = def;
|
rob@100
|
8294 value = defaultTypeValue;
|
rob@100
|
8295 isDefault = true;
|
rob@100
|
8296 } else {
|
rob@100
|
8297 name = def.substring(0, eqIndex);
|
rob@100
|
8298 value = transformExpression(def.substring(eqIndex + 1));
|
rob@100
|
8299 isDefault = false;
|
rob@100
|
8300 }
|
rob@100
|
8301 return new AstVarDefinition( trim(name.replace(/(\s*"C\d+")+/g, "")),
|
rob@100
|
8302 value, isDefault);
|
rob@100
|
8303 }
|
rob@100
|
8304
|
rob@100
|
8305 function getDefaultValueForType(type) {
|
rob@100
|
8306 if(type === "int" || type === "float") {
|
rob@100
|
8307 return "0";
|
rob@100
|
8308 }
|
rob@100
|
8309 if(type === "boolean") {
|
rob@100
|
8310 return "false";
|
rob@100
|
8311 }
|
rob@100
|
8312 if(type === "color") {
|
rob@100
|
8313 return "0x00000000";
|
rob@100
|
8314 }
|
rob@100
|
8315 return "null";
|
rob@100
|
8316 }
|
rob@100
|
8317
|
rob@100
|
8318 function AstVar(definitions, varType) {
|
rob@100
|
8319 this.definitions = definitions;
|
rob@100
|
8320 this.varType = varType;
|
rob@100
|
8321 }
|
rob@100
|
8322 AstVar.prototype.getNames = function() {
|
rob@100
|
8323 var names = [];
|
rob@100
|
8324 for(var i=0,l=this.definitions.length;i<l;++i) {
|
rob@100
|
8325 names.push(this.definitions[i].name);
|
rob@100
|
8326 }
|
rob@100
|
8327 return names;
|
rob@100
|
8328 };
|
rob@100
|
8329 AstVar.prototype.toString = function() {
|
rob@100
|
8330 return "var " + this.definitions.join(",");
|
rob@100
|
8331 };
|
rob@100
|
8332 function AstStatement(expression) {
|
rob@100
|
8333 this.expression = expression;
|
rob@100
|
8334 }
|
rob@100
|
8335 AstStatement.prototype.toString = function() {
|
rob@100
|
8336 return this.expression.toString();
|
rob@100
|
8337 };
|
rob@100
|
8338
|
rob@100
|
8339 function transformStatement(statement) {
|
rob@100
|
8340 if(fieldTest.test(statement)) {
|
rob@100
|
8341 var attrAndType = attrAndTypeRegex.exec(statement);
|
rob@100
|
8342 var definitions = statement.substring(attrAndType[0].length).split(",");
|
rob@100
|
8343 var defaultTypeValue = getDefaultValueForType(attrAndType[2]);
|
rob@100
|
8344 for(var i=0; i < definitions.length; ++i) {
|
rob@100
|
8345 definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue);
|
rob@100
|
8346 }
|
rob@100
|
8347 return new AstVar(definitions, attrAndType[2]);
|
rob@100
|
8348 }
|
rob@100
|
8349 return new AstStatement(transformExpression(statement));
|
rob@100
|
8350 }
|
rob@100
|
8351
|
rob@100
|
8352 function AstForExpression(initStatement, condition, step) {
|
rob@100
|
8353 this.initStatement = initStatement;
|
rob@100
|
8354 this.condition = condition;
|
rob@100
|
8355 this.step = step;
|
rob@100
|
8356 }
|
rob@100
|
8357 AstForExpression.prototype.toString = function() {
|
rob@100
|
8358 return "(" + this.initStatement + "; " + this.condition + "; " + this.step + ")";
|
rob@100
|
8359 };
|
rob@100
|
8360
|
rob@100
|
8361 function AstForInExpression(initStatement, container) {
|
rob@100
|
8362 this.initStatement = initStatement;
|
rob@100
|
8363 this.container = container;
|
rob@100
|
8364 }
|
rob@100
|
8365 AstForInExpression.prototype.toString = function() {
|
rob@100
|
8366 var init = this.initStatement.toString();
|
rob@100
|
8367 if(init.indexOf("=") >= 0) { // can be without var declaration
|
rob@100
|
8368 init = init.substring(0, init.indexOf("="));
|
rob@100
|
8369 }
|
rob@100
|
8370 return "(" + init + " in " + this.container + ")";
|
rob@100
|
8371 };
|
rob@100
|
8372
|
rob@100
|
8373 function AstForEachExpression(initStatement, container) {
|
rob@100
|
8374 this.initStatement = initStatement;
|
rob@100
|
8375 this.container = container;
|
rob@100
|
8376 }
|
rob@100
|
8377 AstForEachExpression.iteratorId = 0;
|
rob@100
|
8378 AstForEachExpression.prototype.toString = function() {
|
rob@100
|
8379 var init = this.initStatement.toString();
|
rob@100
|
8380 var iterator = "$it" + (AstForEachExpression.iteratorId++);
|
rob@100
|
8381 var variableName = init.replace(/^\s*var\s*/, "").split("=")[0];
|
rob@100
|
8382 var initIteratorAndVariable = "var " + iterator + " = new $p.ObjectIterator(" + this.container + "), " +
|
rob@100
|
8383 variableName + " = void(0)";
|
rob@100
|
8384 var nextIterationCondition = iterator + ".hasNext() && ((" +
|
rob@100
|
8385 variableName + " = " + iterator + ".next()) || true)";
|
rob@100
|
8386 return "(" + initIteratorAndVariable + "; " + nextIterationCondition + ";)";
|
rob@100
|
8387 };
|
rob@100
|
8388
|
rob@100
|
8389 function transformForExpression(expr) {
|
rob@100
|
8390 var content;
|
rob@100
|
8391 if (/\bin\b/.test(expr)) {
|
rob@100
|
8392 content = expr.substring(1, expr.length - 1).split(/\bin\b/g);
|
rob@100
|
8393 return new AstForInExpression( transformStatement(trim(content[0])),
|
rob@100
|
8394 transformExpression(content[1]));
|
rob@100
|
8395 }
|
rob@100
|
8396 if (expr.indexOf(":") >= 0 && expr.indexOf(";") < 0) {
|
rob@100
|
8397 content = expr.substring(1, expr.length - 1).split(":");
|
rob@100
|
8398 return new AstForEachExpression( transformStatement(trim(content[0])),
|
rob@100
|
8399 transformExpression(content[1]));
|
rob@100
|
8400 }
|
rob@100
|
8401 content = expr.substring(1, expr.length - 1).split(";");
|
rob@100
|
8402 return new AstForExpression( transformStatement(trim(content[0])),
|
rob@100
|
8403 transformExpression(content[1]), transformExpression(content[2]));
|
rob@100
|
8404 }
|
rob@100
|
8405
|
rob@100
|
8406 function sortByWeight(array) {
|
rob@100
|
8407 array.sort(function (a,b) {
|
rob@100
|
8408 return b.weight - a.weight;
|
rob@100
|
8409 });
|
rob@100
|
8410 }
|
rob@100
|
8411
|
rob@100
|
8412 function AstInnerInterface(name, body, isStatic) {
|
rob@100
|
8413 this.name = name;
|
rob@100
|
8414 this.body = body;
|
rob@100
|
8415 this.isStatic = isStatic;
|
rob@100
|
8416 body.owner = this;
|
rob@100
|
8417 }
|
rob@100
|
8418 AstInnerInterface.prototype.toString = function() {
|
rob@100
|
8419 return "" + this.body;
|
rob@100
|
8420 };
|
rob@100
|
8421 function AstInnerClass(name, body, isStatic) {
|
rob@100
|
8422 this.name = name;
|
rob@100
|
8423 this.body = body;
|
rob@100
|
8424 this.isStatic = isStatic;
|
rob@100
|
8425 body.owner = this;
|
rob@100
|
8426 }
|
rob@100
|
8427 AstInnerClass.prototype.toString = function() {
|
rob@100
|
8428 return "" + this.body;
|
rob@100
|
8429 };
|
rob@100
|
8430
|
rob@100
|
8431 function transformInnerClass(class_) {
|
rob@100
|
8432 var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body
|
rob@100
|
8433 classesRegex.lastIndex = 0;
|
rob@100
|
8434 var isStatic = m[1].indexOf("static") >= 0;
|
rob@100
|
8435 var body = atoms[getAtomIndex(m[6])], innerClass;
|
rob@100
|
8436 var oldClassId = currentClassId, newClassId = generateClassId();
|
rob@100
|
8437 currentClassId = newClassId;
|
rob@100
|
8438 if(m[2] === "interface") {
|
rob@100
|
8439 innerClass = new AstInnerInterface(m[3], transformInterfaceBody(body, m[3], m[4]), isStatic);
|
rob@100
|
8440 } else {
|
rob@100
|
8441 innerClass = new AstInnerClass(m[3], transformClassBody(body, m[3], m[4], m[5]), isStatic);
|
rob@100
|
8442 }
|
rob@100
|
8443 appendClass(innerClass, newClassId, oldClassId);
|
rob@100
|
8444 currentClassId = oldClassId;
|
rob@100
|
8445 return innerClass;
|
rob@100
|
8446 }
|
rob@100
|
8447
|
rob@100
|
8448 function AstClassMethod(name, params, body, isStatic) {
|
rob@100
|
8449 this.name = name;
|
rob@100
|
8450 this.params = params;
|
rob@100
|
8451 this.body = body;
|
rob@100
|
8452 this.isStatic = isStatic;
|
rob@100
|
8453 }
|
rob@100
|
8454 AstClassMethod.prototype.toString = function(){
|
rob@100
|
8455 var paramNames = appendToLookupTable({}, this.params.getNames());
|
rob@100
|
8456 var oldContext = replaceContext;
|
rob@100
|
8457 replaceContext = function (subject) {
|
rob@100
|
8458 return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
|
rob@100
|
8459 };
|
rob@100
|
8460 var body = this.params.prependMethodArgs(this.body.toString());
|
rob@100
|
8461 var result = "function " + this.methodId + this.params + " " + body +"\n";
|
rob@100
|
8462 replaceContext = oldContext;
|
rob@100
|
8463 return result;
|
rob@100
|
8464 };
|
rob@100
|
8465
|
rob@100
|
8466 function transformClassMethod(method) {
|
rob@100
|
8467 var m = methodsRegex.exec(method);
|
rob@100
|
8468 methodsRegex.lastIndex = 0;
|
rob@100
|
8469 var isStatic = m[1].indexOf("static") >= 0;
|
rob@100
|
8470 var body = m[6] !== ';' ? atoms[getAtomIndex(m[6])] : "{}";
|
rob@100
|
8471 return new AstClassMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]),
|
rob@100
|
8472 transformStatementsBlock(body), isStatic );
|
rob@100
|
8473 }
|
rob@100
|
8474
|
rob@100
|
8475 function AstClassField(definitions, fieldType, isStatic) {
|
rob@100
|
8476 this.definitions = definitions;
|
rob@100
|
8477 this.fieldType = fieldType;
|
rob@100
|
8478 this.isStatic = isStatic;
|
rob@100
|
8479 }
|
rob@100
|
8480 AstClassField.prototype.getNames = function() {
|
rob@100
|
8481 var names = [];
|
rob@100
|
8482 for(var i=0,l=this.definitions.length;i<l;++i) {
|
rob@100
|
8483 names.push(this.definitions[i].name);
|
rob@100
|
8484 }
|
rob@100
|
8485 return names;
|
rob@100
|
8486 };
|
rob@100
|
8487 AstClassField.prototype.toString = function() {
|
rob@100
|
8488 var thisPrefix = replaceContext({ name: "[this]" });
|
rob@100
|
8489 if(this.isStatic) {
|
rob@100
|
8490 var className = this.owner.name;
|
rob@100
|
8491 var staticDeclarations = [];
|
rob@100
|
8492 for(var i=0,l=this.definitions.length;i<l;++i) {
|
rob@100
|
8493 var definition = this.definitions[i];
|
rob@100
|
8494 var name = definition.name, staticName = className + "." + name;
|
rob@100
|
8495 var declaration = "if(" + staticName + " === void(0)) {\n" +
|
rob@100
|
8496 " " + staticName + " = " + definition.value + "; }\n" +
|
rob@100
|
8497 "$p.defineProperty(" + thisPrefix + ", " +
|
rob@100
|
8498 "'" + name + "', { get: function(){return " + staticName + ";}, " +
|
rob@100
|
8499 "set: function(val){" + staticName + " = val;} });\n";
|
rob@100
|
8500 staticDeclarations.push(declaration);
|
rob@100
|
8501 }
|
rob@100
|
8502 return staticDeclarations.join("");
|
rob@100
|
8503 }
|
rob@100
|
8504 return thisPrefix + "." + this.definitions.join("; " + thisPrefix + ".");
|
rob@100
|
8505 };
|
rob@100
|
8506
|
rob@100
|
8507 function transformClassField(statement) {
|
rob@100
|
8508 var attrAndType = attrAndTypeRegex.exec(statement);
|
rob@100
|
8509 var isStatic = attrAndType[1].indexOf("static") >= 0;
|
rob@100
|
8510 var definitions = statement.substring(attrAndType[0].length).split(/,\s*/g);
|
rob@100
|
8511 var defaultTypeValue = getDefaultValueForType(attrAndType[2]);
|
rob@100
|
8512 for(var i=0; i < definitions.length; ++i) {
|
rob@100
|
8513 definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue);
|
rob@100
|
8514 }
|
rob@100
|
8515 return new AstClassField(definitions, attrAndType[2], isStatic);
|
rob@100
|
8516 }
|
rob@100
|
8517
|
rob@100
|
8518 function AstConstructor(params, body) {
|
rob@100
|
8519 this.params = params;
|
rob@100
|
8520 this.body = body;
|
rob@100
|
8521 }
|
rob@100
|
8522 AstConstructor.prototype.toString = function() {
|
rob@100
|
8523 var paramNames = appendToLookupTable({}, this.params.getNames());
|
rob@100
|
8524 var oldContext = replaceContext;
|
rob@100
|
8525 replaceContext = function (subject) {
|
rob@100
|
8526 return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
|
rob@100
|
8527 };
|
rob@100
|
8528 var prefix = "function $constr_" + this.params.params.length + this.params.toString();
|
rob@100
|
8529 var body = this.params.prependMethodArgs(this.body.toString());
|
rob@100
|
8530 if(!/\$(superCstr|constr)\b/.test(body)) {
|
rob@100
|
8531 body = "{\n$superCstr();\n" + body.substring(1);
|
rob@100
|
8532 }
|
rob@100
|
8533 replaceContext = oldContext;
|
rob@100
|
8534 return prefix + body + "\n";
|
rob@100
|
8535 };
|
rob@100
|
8536
|
rob@100
|
8537 function transformConstructor(cstr) {
|
rob@100
|
8538 var m = new RegExp(/"B(\d+)"\s*"A(\d+)"/).exec(cstr);
|
rob@100
|
8539 var params = transformParams(atoms[m[1]]);
|
rob@100
|
8540
|
rob@100
|
8541 return new AstConstructor(params, transformStatementsBlock(atoms[m[2]]));
|
rob@100
|
8542 }
|
rob@100
|
8543
|
rob@100
|
8544 function AstInterfaceBody(name, interfacesNames, methodsNames, fields, innerClasses, misc) {
|
rob@100
|
8545 var i,l;
|
rob@100
|
8546 this.name = name;
|
rob@100
|
8547 this.interfacesNames = interfacesNames;
|
rob@100
|
8548 this.methodsNames = methodsNames;
|
rob@100
|
8549 this.fields = fields;
|
rob@100
|
8550 this.innerClasses = innerClasses;
|
rob@100
|
8551 this.misc = misc;
|
rob@100
|
8552 for(i=0,l=fields.length; i<l; ++i) {
|
rob@100
|
8553 fields[i].owner = this;
|
rob@100
|
8554 }
|
rob@100
|
8555 }
|
rob@100
|
8556 AstInterfaceBody.prototype.getMembers = function(classFields, classMethods, classInners) {
|
rob@100
|
8557 if(this.owner.base) {
|
rob@100
|
8558 this.owner.base.body.getMembers(classFields, classMethods, classInners);
|
rob@100
|
8559 }
|
rob@100
|
8560 var i, j, l, m;
|
rob@100
|
8561 for(i=0,l=this.fields.length;i<l;++i) {
|
rob@100
|
8562 var fieldNames = this.fields[i].getNames();
|
rob@100
|
8563 for(j=0,m=fieldNames.length;j<m;++j) {
|
rob@100
|
8564 classFields[fieldNames[j]] = this.fields[i];
|
rob@100
|
8565 }
|
rob@100
|
8566 }
|
rob@100
|
8567 for(i=0,l=this.methodsNames.length;i<l;++i) {
|
rob@100
|
8568 var methodName = this.methodsNames[i];
|
rob@100
|
8569 classMethods[methodName] = true;
|
rob@100
|
8570 }
|
rob@100
|
8571 for(i=0,l=this.innerClasses.length;i<l;++i) {
|
rob@100
|
8572 var innerClass = this.innerClasses[i];
|
rob@100
|
8573 classInners[innerClass.name] = innerClass;
|
rob@100
|
8574 }
|
rob@100
|
8575 };
|
rob@100
|
8576 AstInterfaceBody.prototype.toString = function() {
|
rob@100
|
8577 function getScopeLevel(p) {
|
rob@100
|
8578 var i = 0;
|
rob@100
|
8579 while(p) {
|
rob@100
|
8580 ++i;
|
rob@100
|
8581 p=p.scope;
|
rob@100
|
8582 }
|
rob@100
|
8583 return i;
|
rob@100
|
8584 }
|
rob@100
|
8585
|
rob@100
|
8586 var scopeLevel = getScopeLevel(this.owner);
|
rob@100
|
8587
|
rob@100
|
8588 var className = this.name;
|
rob@100
|
8589 var staticDefinitions = "";
|
rob@100
|
8590 var metadata = "";
|
rob@100
|
8591
|
rob@100
|
8592 var thisClassFields = {}, thisClassMethods = {}, thisClassInners = {};
|
rob@100
|
8593 this.getMembers(thisClassFields, thisClassMethods, thisClassInners);
|
rob@100
|
8594
|
rob@100
|
8595 var i, l, j, m;
|
rob@100
|
8596
|
rob@100
|
8597 if (this.owner.interfaces) {
|
rob@100
|
8598 // interface name can be present, but interface is not
|
rob@100
|
8599 var resolvedInterfaces = [], resolvedInterface;
|
rob@100
|
8600 for (i = 0, l = this.interfacesNames.length; i < l; ++i) {
|
rob@100
|
8601 if (!this.owner.interfaces[i]) {
|
rob@100
|
8602 continue;
|
rob@100
|
8603 }
|
rob@100
|
8604 resolvedInterface = replaceContext({name: this.interfacesNames[i]});
|
rob@100
|
8605 resolvedInterfaces.push(resolvedInterface);
|
rob@100
|
8606 staticDefinitions += "$p.extendInterfaceMembers(" + className + ", " + resolvedInterface + ");\n";
|
rob@100
|
8607 }
|
rob@100
|
8608 metadata += className + ".$interfaces = [" + resolvedInterfaces.join(", ") + "];\n";
|
rob@100
|
8609 }
|
rob@100
|
8610 metadata += className + ".$isInterface = true;\n";
|
rob@100
|
8611 metadata += className + ".$methods = [\'" + this.methodsNames.join("\', \'") + "\'];\n";
|
rob@100
|
8612
|
rob@100
|
8613 sortByWeight(this.innerClasses);
|
rob@100
|
8614 for (i = 0, l = this.innerClasses.length; i < l; ++i) {
|
rob@100
|
8615 var innerClass = this.innerClasses[i];
|
rob@100
|
8616 if (innerClass.isStatic) {
|
rob@100
|
8617 staticDefinitions += className + "." + innerClass.name + " = " + innerClass + ";\n";
|
rob@100
|
8618 }
|
rob@100
|
8619 }
|
rob@100
|
8620
|
rob@100
|
8621 for (i = 0, l = this.fields.length; i < l; ++i) {
|
rob@100
|
8622 var field = this.fields[i];
|
rob@100
|
8623 if (field.isStatic) {
|
rob@100
|
8624 staticDefinitions += className + "." + field.definitions.join(";\n" + className + ".") + ";\n";
|
rob@100
|
8625 }
|
rob@100
|
8626 }
|
rob@100
|
8627
|
rob@100
|
8628 return "(function() {\n" +
|
rob@100
|
8629 "function " + className + "() { throw \'Unable to create the interface\'; }\n" +
|
rob@100
|
8630 staticDefinitions +
|
rob@100
|
8631 metadata +
|
rob@100
|
8632 "return " + className + ";\n" +
|
rob@100
|
8633 "})()";
|
rob@100
|
8634 };
|
rob@100
|
8635
|
rob@100
|
8636 transformInterfaceBody = function(body, name, baseInterfaces) {
|
rob@100
|
8637 var declarations = body.substring(1, body.length - 1);
|
rob@100
|
8638 declarations = extractClassesAndMethods(declarations);
|
rob@100
|
8639 declarations = extractConstructors(declarations, name);
|
rob@100
|
8640 var methodsNames = [], classes = [];
|
rob@100
|
8641 declarations = declarations.replace(/"([DE])(\d+)"/g, function(all, type, index) {
|
rob@100
|
8642 if(type === 'D') { methodsNames.push(index); }
|
rob@100
|
8643 else if(type === 'E') { classes.push(index); }
|
rob@100
|
8644 return "";
|
rob@100
|
8645 });
|
rob@100
|
8646 var fields = declarations.split(/;(?:\s*;)*/g);
|
rob@100
|
8647 var baseInterfaceNames;
|
rob@100
|
8648 var i, l;
|
rob@100
|
8649
|
rob@100
|
8650 if(baseInterfaces !== undef) {
|
rob@100
|
8651 baseInterfaceNames = baseInterfaces.replace(/^\s*extends\s+(.+?)\s*$/g, "$1").split(/\s*,\s*/g);
|
rob@100
|
8652 }
|
rob@100
|
8653
|
rob@100
|
8654 for(i = 0, l = methodsNames.length; i < l; ++i) {
|
rob@100
|
8655 var method = transformClassMethod(atoms[methodsNames[i]]);
|
rob@100
|
8656 methodsNames[i] = method.name;
|
rob@100
|
8657 }
|
rob@100
|
8658 for(i = 0, l = fields.length - 1; i < l; ++i) {
|
rob@100
|
8659 var field = trimSpaces(fields[i]);
|
rob@100
|
8660 fields[i] = transformClassField(field.middle);
|
rob@100
|
8661 }
|
rob@100
|
8662 var tail = fields.pop();
|
rob@100
|
8663 for(i = 0, l = classes.length; i < l; ++i) {
|
rob@100
|
8664 classes[i] = transformInnerClass(atoms[classes[i]]);
|
rob@100
|
8665 }
|
rob@100
|
8666
|
rob@100
|
8667 return new AstInterfaceBody(name, baseInterfaceNames, methodsNames, fields, classes, { tail: tail });
|
rob@100
|
8668 };
|
rob@100
|
8669
|
rob@100
|
8670 function AstClassBody(name, baseClassName, interfacesNames, functions, methods, fields, cstrs, innerClasses, misc) {
|
rob@100
|
8671 var i,l;
|
rob@100
|
8672 this.name = name;
|
rob@100
|
8673 this.baseClassName = baseClassName;
|
rob@100
|
8674 this.interfacesNames = interfacesNames;
|
rob@100
|
8675 this.functions = functions;
|
rob@100
|
8676 this.methods = methods;
|
rob@100
|
8677 this.fields = fields;
|
rob@100
|
8678 this.cstrs = cstrs;
|
rob@100
|
8679 this.innerClasses = innerClasses;
|
rob@100
|
8680 this.misc = misc;
|
rob@100
|
8681 for(i=0,l=fields.length; i<l; ++i) {
|
rob@100
|
8682 fields[i].owner = this;
|
rob@100
|
8683 }
|
rob@100
|
8684 }
|
rob@100
|
8685 AstClassBody.prototype.getMembers = function(classFields, classMethods, classInners) {
|
rob@100
|
8686 if(this.owner.base) {
|
rob@100
|
8687 this.owner.base.body.getMembers(classFields, classMethods, classInners);
|
rob@100
|
8688 }
|
rob@100
|
8689 var i, j, l, m;
|
rob@100
|
8690 for(i=0,l=this.fields.length;i<l;++i) {
|
rob@100
|
8691 var fieldNames = this.fields[i].getNames();
|
rob@100
|
8692 for(j=0,m=fieldNames.length;j<m;++j) {
|
rob@100
|
8693 classFields[fieldNames[j]] = this.fields[i];
|
rob@100
|
8694 }
|
rob@100
|
8695 }
|
rob@100
|
8696 for(i=0,l=this.methods.length;i<l;++i) {
|
rob@100
|
8697 var method = this.methods[i];
|
rob@100
|
8698 classMethods[method.name] = method;
|
rob@100
|
8699 }
|
rob@100
|
8700 for(i=0,l=this.innerClasses.length;i<l;++i) {
|
rob@100
|
8701 var innerClass = this.innerClasses[i];
|
rob@100
|
8702 classInners[innerClass.name] = innerClass;
|
rob@100
|
8703 }
|
rob@100
|
8704 };
|
rob@100
|
8705 AstClassBody.prototype.toString = function() {
|
rob@100
|
8706 function getScopeLevel(p) {
|
rob@100
|
8707 var i = 0;
|
rob@100
|
8708 while(p) {
|
rob@100
|
8709 ++i;
|
rob@100
|
8710 p=p.scope;
|
rob@100
|
8711 }
|
rob@100
|
8712 return i;
|
rob@100
|
8713 }
|
rob@100
|
8714
|
rob@100
|
8715 var scopeLevel = getScopeLevel(this.owner);
|
rob@100
|
8716
|
rob@100
|
8717 var selfId = "$this_" + scopeLevel;
|
rob@100
|
8718 var className = this.name;
|
rob@100
|
8719 var result = "var " + selfId + " = this;\n";
|
rob@100
|
8720 var staticDefinitions = "";
|
rob@100
|
8721 var metadata = "";
|
rob@100
|
8722
|
rob@100
|
8723 var thisClassFields = {}, thisClassMethods = {}, thisClassInners = {};
|
rob@100
|
8724 this.getMembers(thisClassFields, thisClassMethods, thisClassInners);
|
rob@100
|
8725
|
rob@100
|
8726 var oldContext = replaceContext;
|
rob@100
|
8727 replaceContext = function (subject) {
|
rob@100
|
8728 var name = subject.name;
|
rob@100
|
8729 if(name === "this") {
|
rob@100
|
8730 // returns "$this_N.$self" pointer instead of "this" in cases:
|
rob@100
|
8731 // "this()", "this.XXX()", "this", but not for "this.XXX"
|
rob@100
|
8732 return subject.callSign || !subject.member ? selfId + ".$self" : selfId;
|
rob@100
|
8733 }
|
rob@100
|
8734 if(thisClassFields.hasOwnProperty(name)) {
|
rob@100
|
8735 return thisClassFields[name].isStatic ? className + "." + name : selfId + "." + name;
|
rob@100
|
8736 }
|
rob@100
|
8737 if(thisClassInners.hasOwnProperty(name)) {
|
rob@100
|
8738 return selfId + "." + name;
|
rob@100
|
8739 }
|
rob@100
|
8740 if(thisClassMethods.hasOwnProperty(name)) {
|
rob@100
|
8741 return thisClassMethods[name].isStatic ? className + "." + name : selfId + ".$self." + name;
|
rob@100
|
8742 }
|
rob@100
|
8743 return oldContext(subject);
|
rob@100
|
8744 };
|
rob@100
|
8745
|
rob@100
|
8746 var resolvedBaseClassName;
|
rob@100
|
8747 if (this.baseClassName) {
|
rob@100
|
8748 resolvedBaseClassName = oldContext({name: this.baseClassName});
|
rob@100
|
8749 result += "var $super = { $upcast: " + selfId + " };\n";
|
rob@100
|
8750 result += "function $superCstr(){" + resolvedBaseClassName +
|
rob@100
|
8751 ".apply($super,arguments);if(!('$self' in $super)) $p.extendClassChain($super)}\n";
|
rob@100
|
8752 metadata += className + ".$base = " + resolvedBaseClassName + ";\n";
|
rob@100
|
8753 } else {
|
rob@100
|
8754 result += "function $superCstr(){$p.extendClassChain("+ selfId +")}\n";
|
rob@100
|
8755 }
|
rob@100
|
8756
|
rob@100
|
8757 if (this.owner.base) {
|
rob@100
|
8758 // base class name can be present, but class is not
|
rob@100
|
8759 staticDefinitions += "$p.extendStaticMembers(" + className + ", " + resolvedBaseClassName + ");\n";
|
rob@100
|
8760 }
|
rob@100
|
8761
|
rob@100
|
8762 var i, l, j, m;
|
rob@100
|
8763
|
rob@100
|
8764 if (this.owner.interfaces) {
|
rob@100
|
8765 // interface name can be present, but interface is not
|
rob@100
|
8766 var resolvedInterfaces = [], resolvedInterface;
|
rob@100
|
8767 for (i = 0, l = this.interfacesNames.length; i < l; ++i) {
|
rob@100
|
8768 if (!this.owner.interfaces[i]) {
|
rob@100
|
8769 continue;
|
rob@100
|
8770 }
|
rob@100
|
8771 resolvedInterface = oldContext({name: this.interfacesNames[i]});
|
rob@100
|
8772 resolvedInterfaces.push(resolvedInterface);
|
rob@100
|
8773 staticDefinitions += "$p.extendInterfaceMembers(" + className + ", " + resolvedInterface + ");\n";
|
rob@100
|
8774 }
|
rob@100
|
8775 metadata += className + ".$interfaces = [" + resolvedInterfaces.join(", ") + "];\n";
|
rob@100
|
8776 }
|
rob@100
|
8777
|
rob@100
|
8778 if (this.functions.length > 0) {
|
rob@100
|
8779 result += this.functions.join('\n') + '\n';
|
rob@100
|
8780 }
|
rob@100
|
8781
|
rob@100
|
8782 sortByWeight(this.innerClasses);
|
rob@100
|
8783 for (i = 0, l = this.innerClasses.length; i < l; ++i) {
|
rob@100
|
8784 var innerClass = this.innerClasses[i];
|
rob@100
|
8785 if (innerClass.isStatic) {
|
rob@100
|
8786 staticDefinitions += className + "." + innerClass.name + " = " + innerClass + ";\n";
|
rob@100
|
8787 result += selfId + "." + innerClass.name + " = " + className + "." + innerClass.name + ";\n";
|
rob@100
|
8788 } else {
|
rob@100
|
8789 result += selfId + "." + innerClass.name + " = " + innerClass + ";\n";
|
rob@100
|
8790 }
|
rob@100
|
8791 }
|
rob@100
|
8792
|
rob@100
|
8793 for (i = 0, l = this.fields.length; i < l; ++i) {
|
rob@100
|
8794 var field = this.fields[i];
|
rob@100
|
8795 if (field.isStatic) {
|
rob@100
|
8796 staticDefinitions += className + "." + field.definitions.join(";\n" + className + ".") + ";\n";
|
rob@100
|
8797 for (j = 0, m = field.definitions.length; j < m; ++j) {
|
rob@100
|
8798 var fieldName = field.definitions[j].name, staticName = className + "." + fieldName;
|
rob@100
|
8799 result += "$p.defineProperty(" + selfId + ", '" + fieldName + "', {" +
|
rob@100
|
8800 "get: function(){return " + staticName + "}, " +
|
rob@100
|
8801 "set: function(val){" + staticName + " = val}});\n";
|
rob@100
|
8802 }
|
rob@100
|
8803 } else {
|
rob@100
|
8804 result += selfId + "." + field.definitions.join(";\n" + selfId + ".") + ";\n";
|
rob@100
|
8805 }
|
rob@100
|
8806 }
|
rob@100
|
8807 var methodOverloads = {};
|
rob@100
|
8808 for (i = 0, l = this.methods.length; i < l; ++i) {
|
rob@100
|
8809 var method = this.methods[i];
|
rob@100
|
8810 var overload = methodOverloads[method.name];
|
rob@100
|
8811 var methodId = method.name + "$" + method.params.params.length;
|
rob@100
|
8812 var hasMethodArgs = !!method.params.methodArgsParam;
|
rob@100
|
8813 if (overload) {
|
rob@100
|
8814 ++overload;
|
rob@100
|
8815 methodId += "_" + overload;
|
rob@100
|
8816 } else {
|
rob@100
|
8817 overload = 1;
|
rob@100
|
8818 }
|
rob@100
|
8819 method.methodId = methodId;
|
rob@100
|
8820 methodOverloads[method.name] = overload;
|
rob@100
|
8821 if (method.isStatic) {
|
rob@100
|
8822 staticDefinitions += method;
|
rob@100
|
8823 staticDefinitions += "$p.addMethod(" + className + ", '" + method.name + "', " + methodId + ", " + hasMethodArgs + ");\n";
|
rob@100
|
8824 result += "$p.addMethod(" + selfId + ", '" + method.name + "', " + methodId + ", " + hasMethodArgs + ");\n";
|
rob@100
|
8825 } else {
|
rob@100
|
8826 result += method;
|
rob@100
|
8827 result += "$p.addMethod(" + selfId + ", '" + method.name + "', " + methodId + ", " + hasMethodArgs + ");\n";
|
rob@100
|
8828 }
|
rob@100
|
8829 }
|
rob@100
|
8830 result += trim(this.misc.tail);
|
rob@100
|
8831
|
rob@100
|
8832 if (this.cstrs.length > 0) {
|
rob@100
|
8833 result += this.cstrs.join('\n') + '\n';
|
rob@100
|
8834 }
|
rob@100
|
8835
|
rob@100
|
8836 result += "function $constr() {\n";
|
rob@100
|
8837 var cstrsIfs = [];
|
rob@100
|
8838 for (i = 0, l = this.cstrs.length; i < l; ++i) {
|
rob@100
|
8839 var paramsLength = this.cstrs[i].params.params.length;
|
rob@100
|
8840 var methodArgsPresent = !!this.cstrs[i].params.methodArgsParam;
|
rob@100
|
8841 cstrsIfs.push("if(arguments.length " + (methodArgsPresent ? ">=" : "===") +
|
rob@100
|
8842 " " + paramsLength + ") { " +
|
rob@100
|
8843 "$constr_" + paramsLength + ".apply(" + selfId + ", arguments); }");
|
rob@100
|
8844 }
|
rob@100
|
8845 if(cstrsIfs.length > 0) {
|
rob@100
|
8846 result += cstrsIfs.join(" else ") + " else ";
|
rob@100
|
8847 }
|
rob@100
|
8848 // ??? add check if length is 0, otherwise fail
|
rob@100
|
8849 result += "$superCstr();\n}\n";
|
rob@100
|
8850 result += "$constr.apply(null, arguments);\n";
|
rob@100
|
8851
|
rob@100
|
8852 replaceContext = oldContext;
|
rob@100
|
8853 return "(function() {\n" +
|
rob@100
|
8854 "function " + className + "() {\n" + result + "}\n" +
|
rob@100
|
8855 staticDefinitions +
|
rob@100
|
8856 metadata +
|
rob@100
|
8857 "return " + className + ";\n" +
|
rob@100
|
8858 "})()";
|
rob@100
|
8859 };
|
rob@100
|
8860
|
rob@100
|
8861 transformClassBody = function(body, name, baseName, interfaces) {
|
rob@100
|
8862 var declarations = body.substring(1, body.length - 1);
|
rob@100
|
8863 declarations = extractClassesAndMethods(declarations);
|
rob@100
|
8864 declarations = extractConstructors(declarations, name);
|
rob@100
|
8865 var methods = [], classes = [], cstrs = [], functions = [];
|
rob@100
|
8866 declarations = declarations.replace(/"([DEGH])(\d+)"/g, function(all, type, index) {
|
rob@100
|
8867 if(type === 'D') { methods.push(index); }
|
rob@100
|
8868 else if(type === 'E') { classes.push(index); }
|
rob@100
|
8869 else if(type === 'H') { functions.push(index); }
|
rob@100
|
8870 else { cstrs.push(index); }
|
rob@100
|
8871 return "";
|
rob@100
|
8872 });
|
rob@100
|
8873 var fields = declarations.replace(/^(?:\s*;)+/, "").split(/;(?:\s*;)*/g);
|
rob@100
|
8874 var baseClassName, interfacesNames;
|
rob@100
|
8875 var i;
|
rob@100
|
8876
|
rob@100
|
8877 if(baseName !== undef) {
|
rob@100
|
8878 baseClassName = baseName.replace(/^\s*extends\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)\s*$/g, "$1");
|
rob@100
|
8879 }
|
rob@100
|
8880
|
rob@100
|
8881 if(interfaces !== undef) {
|
rob@100
|
8882 interfacesNames = interfaces.replace(/^\s*implements\s+(.+?)\s*$/g, "$1").split(/\s*,\s*/g);
|
rob@100
|
8883 }
|
rob@100
|
8884
|
rob@100
|
8885 for(i = 0; i < functions.length; ++i) {
|
rob@100
|
8886 functions[i] = transformFunction(atoms[functions[i]]);
|
rob@100
|
8887 }
|
rob@100
|
8888 for(i = 0; i < methods.length; ++i) {
|
rob@100
|
8889 methods[i] = transformClassMethod(atoms[methods[i]]);
|
rob@100
|
8890 }
|
rob@100
|
8891 for(i = 0; i < fields.length - 1; ++i) {
|
rob@100
|
8892 var field = trimSpaces(fields[i]);
|
rob@100
|
8893 fields[i] = transformClassField(field.middle);
|
rob@100
|
8894 }
|
rob@100
|
8895 var tail = fields.pop();
|
rob@100
|
8896 for(i = 0; i < cstrs.length; ++i) {
|
rob@100
|
8897 cstrs[i] = transformConstructor(atoms[cstrs[i]]);
|
rob@100
|
8898 }
|
rob@100
|
8899 for(i = 0; i < classes.length; ++i) {
|
rob@100
|
8900 classes[i] = transformInnerClass(atoms[classes[i]]);
|
rob@100
|
8901 }
|
rob@100
|
8902
|
rob@100
|
8903 return new AstClassBody(name, baseClassName, interfacesNames, functions, methods, fields, cstrs,
|
rob@100
|
8904 classes, { tail: tail });
|
rob@100
|
8905 };
|
rob@100
|
8906
|
rob@100
|
8907 function AstInterface(name, body) {
|
rob@100
|
8908 this.name = name;
|
rob@100
|
8909 this.body = body;
|
rob@100
|
8910 body.owner = this;
|
rob@100
|
8911 }
|
rob@100
|
8912 AstInterface.prototype.toString = function() {
|
rob@100
|
8913 return "var " + this.name + " = " + this.body + ";\n" +
|
rob@100
|
8914 "$p." + this.name + " = " + this.name + ";\n";
|
rob@100
|
8915 };
|
rob@100
|
8916 function AstClass(name, body) {
|
rob@100
|
8917 this.name = name;
|
rob@100
|
8918 this.body = body;
|
rob@100
|
8919 body.owner = this;
|
rob@100
|
8920 }
|
rob@100
|
8921 AstClass.prototype.toString = function() {
|
rob@100
|
8922 return "var " + this.name + " = " + this.body + ";\n" +
|
rob@100
|
8923 "$p." + this.name + " = " + this.name + ";\n";
|
rob@100
|
8924 };
|
rob@100
|
8925
|
rob@100
|
8926 function transformGlobalClass(class_) {
|
rob@100
|
8927 var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body
|
rob@100
|
8928 classesRegex.lastIndex = 0;
|
rob@100
|
8929 var body = atoms[getAtomIndex(m[6])];
|
rob@100
|
8930 var oldClassId = currentClassId, newClassId = generateClassId();
|
rob@100
|
8931 currentClassId = newClassId;
|
rob@100
|
8932 var globalClass;
|
rob@100
|
8933 if(m[2] === "interface") {
|
rob@100
|
8934 globalClass = new AstInterface(m[3], transformInterfaceBody(body, m[3], m[4]) );
|
rob@100
|
8935 } else {
|
rob@100
|
8936 globalClass = new AstClass(m[3], transformClassBody(body, m[3], m[4], m[5]) );
|
rob@100
|
8937 }
|
rob@100
|
8938 appendClass(globalClass, newClassId, oldClassId);
|
rob@100
|
8939 currentClassId = oldClassId;
|
rob@100
|
8940 return globalClass;
|
rob@100
|
8941 }
|
rob@100
|
8942
|
rob@100
|
8943 function AstMethod(name, params, body) {
|
rob@100
|
8944 this.name = name;
|
rob@100
|
8945 this.params = params;
|
rob@100
|
8946 this.body = body;
|
rob@100
|
8947 }
|
rob@100
|
8948 AstMethod.prototype.toString = function(){
|
rob@100
|
8949 var paramNames = appendToLookupTable({}, this.params.getNames());
|
rob@100
|
8950 var oldContext = replaceContext;
|
rob@100
|
8951 replaceContext = function (subject) {
|
rob@100
|
8952 return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
|
rob@100
|
8953 };
|
rob@100
|
8954 var body = this.params.prependMethodArgs(this.body.toString());
|
rob@100
|
8955 var result = "function " + this.name + this.params + " " + body + "\n" +
|
rob@100
|
8956 "$p." + this.name + " = " + this.name + ";\n" +
|
rob@100
|
8957 this.name + " = " + this.name + ".bind($p);";
|
rob@100
|
8958 // "$p." + this.name + " = " + this.name + ";";
|
rob@100
|
8959 replaceContext = oldContext;
|
rob@100
|
8960 return result;
|
rob@100
|
8961 };
|
rob@100
|
8962
|
rob@100
|
8963 function transformGlobalMethod(method) {
|
rob@100
|
8964 var m = methodsRegex.exec(method);
|
rob@100
|
8965 var result =
|
rob@100
|
8966 methodsRegex.lastIndex = 0;
|
rob@100
|
8967 return new AstMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]),
|
rob@100
|
8968 transformStatementsBlock(atoms[getAtomIndex(m[6])]));
|
rob@100
|
8969 }
|
rob@100
|
8970
|
rob@100
|
8971 function preStatementsTransform(statements) {
|
rob@100
|
8972 var s = statements;
|
rob@100
|
8973 // turns multiple catch blocks into one, because we have no way to properly get into them anyway.
|
rob@100
|
8974 s = s.replace(/\b(catch\s*"B\d+"\s*"A\d+")(\s*catch\s*"B\d+"\s*"A\d+")+/g, "$1");
|
rob@100
|
8975 return s;
|
rob@100
|
8976 }
|
rob@100
|
8977
|
rob@100
|
8978 function AstForStatement(argument, misc) {
|
rob@100
|
8979 this.argument = argument;
|
rob@100
|
8980 this.misc = misc;
|
rob@100
|
8981 }
|
rob@100
|
8982 AstForStatement.prototype.toString = function() {
|
rob@100
|
8983 return this.misc.prefix + this.argument.toString();
|
rob@100
|
8984 };
|
rob@100
|
8985 function AstCatchStatement(argument, misc) {
|
rob@100
|
8986 this.argument = argument;
|
rob@100
|
8987 this.misc = misc;
|
rob@100
|
8988 }
|
rob@100
|
8989 AstCatchStatement.prototype.toString = function() {
|
rob@100
|
8990 return this.misc.prefix + this.argument.toString();
|
rob@100
|
8991 };
|
rob@100
|
8992 function AstPrefixStatement(name, argument, misc) {
|
rob@100
|
8993 this.name = name;
|
rob@100
|
8994 this.argument = argument;
|
rob@100
|
8995 this.misc = misc;
|
rob@100
|
8996 }
|
rob@100
|
8997 AstPrefixStatement.prototype.toString = function() {
|
rob@100
|
8998 var result = this.misc.prefix;
|
rob@100
|
8999 if(this.argument !== undef) {
|
rob@100
|
9000 result += this.argument.toString();
|
rob@100
|
9001 }
|
rob@100
|
9002 return result;
|
rob@100
|
9003 };
|
rob@100
|
9004 function AstSwitchCase(expr) {
|
rob@100
|
9005 this.expr = expr;
|
rob@100
|
9006 }
|
rob@100
|
9007 AstSwitchCase.prototype.toString = function() {
|
rob@100
|
9008 return "case " + this.expr + ":";
|
rob@100
|
9009 };
|
rob@100
|
9010 function AstLabel(label) {
|
rob@100
|
9011 this.label = label;
|
rob@100
|
9012 }
|
rob@100
|
9013 AstLabel.prototype.toString = function() {
|
rob@100
|
9014 return this.label;
|
rob@100
|
9015 };
|
rob@100
|
9016
|
rob@100
|
9017 transformStatements = function(statements, transformMethod, transformClass) {
|
rob@100
|
9018 var nextStatement = new RegExp(/\b(catch|for|if|switch|while|with)\s*"B(\d+)"|\b(do|else|finally|return|throw|try|break|continue)\b|("[ADEH](\d+)")|\b(case)\s+([^:]+):|\b([A-Za-z_$][\w$]*\s*:)|(;)/g);
|
rob@100
|
9019 var res = [];
|
rob@100
|
9020 statements = preStatementsTransform(statements);
|
rob@100
|
9021 var lastIndex = 0, m, space;
|
rob@100
|
9022 // m contains the matches from the nextStatement regexp, null if there are no matches.
|
rob@100
|
9023 // nextStatement.exec starts searching at nextStatement.lastIndex.
|
rob@100
|
9024 while((m = nextStatement.exec(statements)) !== null) {
|
rob@100
|
9025 if(m[1] !== undef) { // catch, for ...
|
rob@100
|
9026 var i = statements.lastIndexOf('"B', nextStatement.lastIndex);
|
rob@100
|
9027 var statementsPrefix = statements.substring(lastIndex, i);
|
rob@100
|
9028 if(m[1] === "for") {
|
rob@100
|
9029 res.push(new AstForStatement(transformForExpression(atoms[m[2]]),
|
rob@100
|
9030 { prefix: statementsPrefix }) );
|
rob@100
|
9031 } else if(m[1] === "catch") {
|
rob@100
|
9032 res.push(new AstCatchStatement(transformParams(atoms[m[2]]),
|
rob@100
|
9033 { prefix: statementsPrefix }) );
|
rob@100
|
9034 } else {
|
rob@100
|
9035 res.push(new AstPrefixStatement(m[1], transformExpression(atoms[m[2]]),
|
rob@100
|
9036 { prefix: statementsPrefix }) );
|
rob@100
|
9037 }
|
rob@100
|
9038 } else if(m[3] !== undef) { // do, else, ...
|
rob@100
|
9039 res.push(new AstPrefixStatement(m[3], undef,
|
rob@100
|
9040 { prefix: statements.substring(lastIndex, nextStatement.lastIndex) }) );
|
rob@100
|
9041 } else if(m[4] !== undef) { // block, class and methods
|
rob@100
|
9042 space = statements.substring(lastIndex, nextStatement.lastIndex - m[4].length);
|
rob@100
|
9043 if(trim(space).length !== 0) { continue; } // avoiding new type[] {} construct
|
rob@100
|
9044 res.push(space);
|
rob@100
|
9045 var kind = m[4].charAt(1), atomIndex = m[5];
|
rob@100
|
9046 if(kind === 'D') {
|
rob@100
|
9047 res.push(transformMethod(atoms[atomIndex]));
|
rob@100
|
9048 } else if(kind === 'E') {
|
rob@100
|
9049 res.push(transformClass(atoms[atomIndex]));
|
rob@100
|
9050 } else if(kind === 'H') {
|
rob@100
|
9051 res.push(transformFunction(atoms[atomIndex]));
|
rob@100
|
9052 } else {
|
rob@100
|
9053 res.push(transformStatementsBlock(atoms[atomIndex]));
|
rob@100
|
9054 }
|
rob@100
|
9055 } else if(m[6] !== undef) { // switch case
|
rob@100
|
9056 res.push(new AstSwitchCase(transformExpression(trim(m[7]))));
|
rob@100
|
9057 } else if(m[8] !== undef) { // label
|
rob@100
|
9058 space = statements.substring(lastIndex, nextStatement.lastIndex - m[8].length);
|
rob@100
|
9059 if(trim(space).length !== 0) { continue; } // avoiding ?: construct
|
rob@100
|
9060 res.push(new AstLabel(statements.substring(lastIndex, nextStatement.lastIndex)) );
|
rob@100
|
9061 } else { // semicolon
|
rob@100
|
9062 var statement = trimSpaces(statements.substring(lastIndex, nextStatement.lastIndex - 1));
|
rob@100
|
9063 res.push(statement.left);
|
rob@100
|
9064 res.push(transformStatement(statement.middle));
|
rob@100
|
9065 res.push(statement.right + ";");
|
rob@100
|
9066 }
|
rob@100
|
9067 lastIndex = nextStatement.lastIndex;
|
rob@100
|
9068 }
|
rob@100
|
9069 var statementsTail = trimSpaces(statements.substring(lastIndex));
|
rob@100
|
9070 res.push(statementsTail.left);
|
rob@100
|
9071 if(statementsTail.middle !== "") {
|
rob@100
|
9072 res.push(transformStatement(statementsTail.middle));
|
rob@100
|
9073 res.push(";" + statementsTail.right);
|
rob@100
|
9074 }
|
rob@100
|
9075 return res;
|
rob@100
|
9076 };
|
rob@100
|
9077
|
rob@100
|
9078 function getLocalNames(statements) {
|
rob@100
|
9079 var localNames = [];
|
rob@100
|
9080 for(var i=0,l=statements.length;i<l;++i) {
|
rob@100
|
9081 var statement = statements[i];
|
rob@100
|
9082 if(statement instanceof AstVar) {
|
rob@100
|
9083 localNames = localNames.concat(statement.getNames());
|
rob@100
|
9084 } else if(statement instanceof AstForStatement &&
|
rob@100
|
9085 statement.argument.initStatement instanceof AstVar) {
|
rob@100
|
9086 localNames = localNames.concat(statement.argument.initStatement.getNames());
|
rob@100
|
9087 } else if(statement instanceof AstInnerInterface || statement instanceof AstInnerClass ||
|
rob@100
|
9088 statement instanceof AstInterface || statement instanceof AstClass ||
|
rob@100
|
9089 statement instanceof AstMethod || statement instanceof AstFunction) {
|
rob@100
|
9090 localNames.push(statement.name);
|
rob@100
|
9091 }
|
rob@100
|
9092 }
|
rob@100
|
9093 return appendToLookupTable({}, localNames);
|
rob@100
|
9094 }
|
rob@100
|
9095
|
rob@100
|
9096 function AstStatementsBlock(statements) {
|
rob@100
|
9097 this.statements = statements;
|
rob@100
|
9098 }
|
rob@100
|
9099 AstStatementsBlock.prototype.toString = function() {
|
rob@100
|
9100 var localNames = getLocalNames(this.statements);
|
rob@100
|
9101 var oldContext = replaceContext;
|
rob@100
|
9102
|
rob@100
|
9103 // replacing context only when necessary
|
rob@100
|
9104 if(!isLookupTableEmpty(localNames)) {
|
rob@100
|
9105 replaceContext = function (subject) {
|
rob@100
|
9106 return localNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
|
rob@100
|
9107 };
|
rob@100
|
9108 }
|
rob@100
|
9109
|
rob@100
|
9110 var result = "{\n" + this.statements.join('') + "\n}";
|
rob@100
|
9111 replaceContext = oldContext;
|
rob@100
|
9112 return result;
|
rob@100
|
9113 };
|
rob@100
|
9114
|
rob@100
|
9115 transformStatementsBlock = function(block) {
|
rob@100
|
9116 var content = trimSpaces(block.substring(1, block.length - 1));
|
rob@100
|
9117 return new AstStatementsBlock(transformStatements(content.middle));
|
rob@100
|
9118 };
|
rob@100
|
9119
|
rob@100
|
9120 function AstRoot(statements) {
|
rob@100
|
9121 this.statements = statements;
|
rob@100
|
9122 }
|
rob@100
|
9123 AstRoot.prototype.toString = function() {
|
rob@100
|
9124 var classes = [], otherStatements = [], statement;
|
rob@100
|
9125 for (var i = 0, len = this.statements.length; i < len; ++i) {
|
rob@100
|
9126 statement = this.statements[i];
|
rob@100
|
9127 if (statement instanceof AstClass || statement instanceof AstInterface) {
|
rob@100
|
9128 classes.push(statement);
|
rob@100
|
9129 } else {
|
rob@100
|
9130 otherStatements.push(statement);
|
rob@100
|
9131 }
|
rob@100
|
9132 }
|
rob@100
|
9133 sortByWeight(classes);
|
rob@100
|
9134
|
rob@100
|
9135 var localNames = getLocalNames(this.statements);
|
rob@100
|
9136 replaceContext = function (subject) {
|
rob@100
|
9137 var name = subject.name;
|
rob@100
|
9138 if(localNames.hasOwnProperty(name)) {
|
rob@100
|
9139 return name;
|
rob@100
|
9140 }
|
rob@100
|
9141 if(globalMembers.hasOwnProperty(name) ||
|
rob@100
|
9142 PConstants.hasOwnProperty(name) ||
|
rob@100
|
9143 defaultScope.hasOwnProperty(name)) {
|
rob@100
|
9144 return "$p." + name;
|
rob@100
|
9145 }
|
rob@100
|
9146 return name;
|
rob@100
|
9147 };
|
rob@100
|
9148 var result = "// this code was autogenerated from PJS\n" +
|
rob@100
|
9149 "(function($p) {\n" +
|
rob@100
|
9150 classes.join('') + "\n" +
|
rob@100
|
9151 otherStatements.join('') + "\n})";
|
rob@100
|
9152 replaceContext = null;
|
rob@100
|
9153 return result;
|
rob@100
|
9154 };
|
rob@100
|
9155
|
rob@100
|
9156 transformMain = function() {
|
rob@100
|
9157 var statements = extractClassesAndMethods(atoms[0]);
|
rob@100
|
9158 statements = statements.replace(/\bimport\s+[^;]+;/g, "");
|
rob@100
|
9159 return new AstRoot( transformStatements(statements,
|
rob@100
|
9160 transformGlobalMethod, transformGlobalClass) );
|
rob@100
|
9161 };
|
rob@100
|
9162
|
rob@100
|
9163 function generateMetadata(ast) {
|
rob@100
|
9164 var globalScope = {};
|
rob@100
|
9165 var id, class_;
|
rob@100
|
9166 for(id in declaredClasses) {
|
rob@100
|
9167 if(declaredClasses.hasOwnProperty(id)) {
|
rob@100
|
9168 class_ = declaredClasses[id];
|
rob@100
|
9169 var scopeId = class_.scopeId, name = class_.name;
|
rob@100
|
9170 if(scopeId) {
|
rob@100
|
9171 var scope = declaredClasses[scopeId];
|
rob@100
|
9172 class_.scope = scope;
|
rob@100
|
9173 if(scope.inScope === undef) {
|
rob@100
|
9174 scope.inScope = {};
|
rob@100
|
9175 }
|
rob@100
|
9176 scope.inScope[name] = class_;
|
rob@100
|
9177 } else {
|
rob@100
|
9178 globalScope[name] = class_;
|
rob@100
|
9179 }
|
rob@100
|
9180 }
|
rob@100
|
9181 }
|
rob@100
|
9182
|
rob@100
|
9183 function findInScopes(class_, name) {
|
rob@100
|
9184 var parts = name.split('.');
|
rob@100
|
9185 var currentScope = class_.scope, found;
|
rob@100
|
9186 while(currentScope) {
|
rob@100
|
9187 if(currentScope.hasOwnProperty(parts[0])) {
|
rob@100
|
9188 found = currentScope[parts[0]]; break;
|
rob@100
|
9189 }
|
rob@100
|
9190 currentScope = currentScope.scope;
|
rob@100
|
9191 }
|
rob@100
|
9192 if(found === undef) {
|
rob@100
|
9193 found = globalScope[parts[0]];
|
rob@100
|
9194 }
|
rob@100
|
9195 for(var i=1,l=parts.length;i<l && found;++i) {
|
rob@100
|
9196 found = found.inScope[parts[i]];
|
rob@100
|
9197 }
|
rob@100
|
9198 return found;
|
rob@100
|
9199 }
|
rob@100
|
9200
|
rob@100
|
9201 for(id in declaredClasses) {
|
rob@100
|
9202 if(declaredClasses.hasOwnProperty(id)) {
|
rob@100
|
9203 class_ = declaredClasses[id];
|
rob@100
|
9204 var baseClassName = class_.body.baseClassName;
|
rob@100
|
9205 if(baseClassName) {
|
rob@100
|
9206 var parent = findInScopes(class_, baseClassName);
|
rob@100
|
9207 if (parent) {
|
rob@100
|
9208 class_.base = parent;
|
rob@100
|
9209 if (!parent.derived) {
|
rob@100
|
9210 parent.derived = [];
|
rob@100
|
9211 }
|
rob@100
|
9212 parent.derived.push(class_);
|
rob@100
|
9213 }
|
rob@100
|
9214 }
|
rob@100
|
9215 var interfacesNames = class_.body.interfacesNames,
|
rob@100
|
9216 interfaces = [], i, l;
|
rob@100
|
9217 if (interfacesNames && interfacesNames.length > 0) {
|
rob@100
|
9218 for (i = 0, l = interfacesNames.length; i < l; ++i) {
|
rob@100
|
9219 var interface_ = findInScopes(class_, interfacesNames[i]);
|
rob@100
|
9220 interfaces.push(interface_);
|
rob@100
|
9221 if (!interface_) {
|
rob@100
|
9222 continue;
|
rob@100
|
9223 }
|
rob@100
|
9224 if (!interface_.derived) {
|
rob@100
|
9225 interface_.derived = [];
|
rob@100
|
9226 }
|
rob@100
|
9227 interface_.derived.push(class_);
|
rob@100
|
9228 }
|
rob@100
|
9229 if (interfaces.length > 0) {
|
rob@100
|
9230 class_.interfaces = interfaces;
|
rob@100
|
9231 }
|
rob@100
|
9232 }
|
rob@100
|
9233 }
|
rob@100
|
9234 }
|
rob@100
|
9235 }
|
rob@100
|
9236
|
rob@100
|
9237 function setWeight(ast) {
|
rob@100
|
9238 var queue = [], tocheck = {};
|
rob@100
|
9239 var id, scopeId, class_;
|
rob@100
|
9240 // queue most inner and non-inherited
|
rob@100
|
9241 for (id in declaredClasses) {
|
rob@100
|
9242 if (declaredClasses.hasOwnProperty(id)) {
|
rob@100
|
9243 class_ = declaredClasses[id];
|
rob@100
|
9244 if (!class_.inScope && !class_.derived) {
|
rob@100
|
9245 queue.push(id);
|
rob@100
|
9246 class_.weight = 0;
|
rob@100
|
9247 } else {
|
rob@100
|
9248 var dependsOn = [];
|
rob@100
|
9249 if (class_.inScope) {
|
rob@100
|
9250 for (scopeId in class_.inScope) {
|
rob@100
|
9251 if (class_.inScope.hasOwnProperty(scopeId)) {
|
rob@100
|
9252 dependsOn.push(class_.inScope[scopeId]);
|
rob@100
|
9253 }
|
rob@100
|
9254 }
|
rob@100
|
9255 }
|
rob@100
|
9256 if (class_.derived) {
|
rob@100
|
9257 dependsOn = dependsOn.concat(class_.derived);
|
rob@100
|
9258 }
|
rob@100
|
9259 tocheck[id] = dependsOn;
|
rob@100
|
9260 }
|
rob@100
|
9261 }
|
rob@100
|
9262 }
|
rob@100
|
9263 function removeDependentAndCheck(targetId, from) {
|
rob@100
|
9264 var dependsOn = tocheck[targetId];
|
rob@100
|
9265 if (!dependsOn) {
|
rob@100
|
9266 return false; // no need to process
|
rob@100
|
9267 }
|
rob@100
|
9268 var i = dependsOn.indexOf(from);
|
rob@100
|
9269 if (i < 0) {
|
rob@100
|
9270 return false;
|
rob@100
|
9271 }
|
rob@100
|
9272 dependsOn.splice(i, 1);
|
rob@100
|
9273 if (dependsOn.length > 0) {
|
rob@100
|
9274 return false;
|
rob@100
|
9275 }
|
rob@100
|
9276 delete tocheck[targetId];
|
rob@100
|
9277 return true;
|
rob@100
|
9278 }
|
rob@100
|
9279 while (queue.length > 0) {
|
rob@100
|
9280 id = queue.shift();
|
rob@100
|
9281 class_ = declaredClasses[id];
|
rob@100
|
9282 if (class_.scopeId && removeDependentAndCheck(class_.scopeId, class_)) {
|
rob@100
|
9283 queue.push(class_.scopeId);
|
rob@100
|
9284 declaredClasses[class_.scopeId].weight = class_.weight + 1;
|
rob@100
|
9285 }
|
rob@100
|
9286 if (class_.base && removeDependentAndCheck(class_.base.classId, class_)) {
|
rob@100
|
9287 queue.push(class_.base.classId);
|
rob@100
|
9288 class_.base.weight = class_.weight + 1;
|
rob@100
|
9289 }
|
rob@100
|
9290 if (class_.interfaces) {
|
rob@100
|
9291 var i, l;
|
rob@100
|
9292 for (i = 0, l = class_.interfaces.length; i < l; ++i) {
|
rob@100
|
9293 if (!class_.interfaces[i] ||
|
rob@100
|
9294 !removeDependentAndCheck(class_.interfaces[i].classId, class_)) {
|
rob@100
|
9295 continue;
|
rob@100
|
9296 }
|
rob@100
|
9297 queue.push(class_.interfaces[i].classId);
|
rob@100
|
9298 class_.interfaces[i].weight = class_.weight + 1;
|
rob@100
|
9299 }
|
rob@100
|
9300 }
|
rob@100
|
9301 }
|
rob@100
|
9302 }
|
rob@100
|
9303
|
rob@100
|
9304 var transformed = transformMain();
|
rob@100
|
9305 generateMetadata(transformed);
|
rob@100
|
9306 setWeight(transformed);
|
rob@100
|
9307
|
rob@100
|
9308 var redendered = transformed.toString();
|
rob@100
|
9309
|
rob@100
|
9310 // remove empty extra lines with space
|
rob@100
|
9311 redendered = redendered.replace(/\s*\n(?:[\t ]*\n)+/g, "\n\n");
|
rob@100
|
9312
|
rob@100
|
9313 // convert character codes to characters
|
rob@100
|
9314 redendered = redendered.replace(/__x([0-9A-F]{4})/g, function(all, hexCode) {
|
rob@100
|
9315 return String.fromCharCode(parseInt(hexCode,16));
|
rob@100
|
9316 });
|
rob@100
|
9317
|
rob@100
|
9318 return injectStrings(redendered, strings);
|
rob@100
|
9319 }// Parser ends
|
rob@100
|
9320
|
rob@100
|
9321 function preprocessCode(aCode, sketch) {
|
rob@100
|
9322 // Parse out @pjs directive, if any.
|
rob@100
|
9323 var dm = new RegExp(/\/\*\s*@pjs\s+((?:[^\*]|\*+[^\*\/])*)\*\//g).exec(aCode);
|
rob@100
|
9324 if (dm && dm.length === 2) {
|
rob@100
|
9325 // masks contents of a JSON to be replaced later
|
rob@100
|
9326 // to protect the contents from further parsing
|
rob@100
|
9327 var jsonItems = [],
|
rob@100
|
9328 directives = dm.splice(1, 2)[0].replace(/\{([\s\S]*?)\}/g, (function() {
|
rob@100
|
9329 return function(all, item) {
|
rob@100
|
9330 jsonItems.push(item);
|
rob@100
|
9331 return "{" + (jsonItems.length-1) + "}";
|
rob@100
|
9332 };
|
rob@100
|
9333 }())).replace('\n', '').replace('\r', '').split(";");
|
rob@100
|
9334
|
rob@100
|
9335 // We'll L/RTrim, and also remove any surrounding double quotes (e.g., just take string contents)
|
rob@100
|
9336 var clean = function(s) {
|
rob@100
|
9337 return s.replace(/^\s*["']?/, '').replace(/["']?\s*$/, '');
|
rob@100
|
9338 };
|
rob@100
|
9339
|
rob@100
|
9340 for (var i = 0, dl = directives.length; i < dl; i++) {
|
rob@100
|
9341 var pair = directives[i].split('=');
|
rob@100
|
9342 if (pair && pair.length === 2) {
|
rob@100
|
9343 var key = clean(pair[0]),
|
rob@100
|
9344 value = clean(pair[1]),
|
rob@100
|
9345 list = [];
|
rob@100
|
9346 // A few directives require work beyond storying key/value pairings
|
rob@100
|
9347 if (key === "preload") {
|
rob@100
|
9348 list = value.split(',');
|
rob@100
|
9349 // All pre-loaded images will get put in imageCache, keyed on filename
|
rob@100
|
9350 for (var j = 0, jl = list.length; j < jl; j++) {
|
rob@100
|
9351 var imageName = clean(list[j]);
|
rob@100
|
9352 sketch.imageCache.add(imageName);
|
rob@100
|
9353 }
|
rob@100
|
9354 // fonts can be declared as a string containing a url,
|
rob@100
|
9355 // or a JSON object, containing a font name, and a url
|
rob@100
|
9356 } else if (key === "font") {
|
rob@100
|
9357 list = value.split(",");
|
rob@100
|
9358 for (var x = 0, xl = list.length; x < xl; x++) {
|
rob@100
|
9359 var fontName = clean(list[x]),
|
rob@100
|
9360 index = /^\{(\d*?)\}$/.exec(fontName);
|
rob@100
|
9361 // if index is not null, send JSON, otherwise, send string
|
rob@100
|
9362 PFont.preloading.add(index ? JSON.parse("{" + jsonItems[index[1]] + "}") : fontName);
|
rob@100
|
9363 }
|
rob@100
|
9364 } else if (key === "pauseOnBlur") {
|
rob@100
|
9365 sketch.options.pauseOnBlur = value === "true";
|
rob@100
|
9366 } else if (key === "globalKeyEvents") {
|
rob@100
|
9367 sketch.options.globalKeyEvents = value === "true";
|
rob@100
|
9368 } else if (key.substring(0, 6) === "param-") {
|
rob@100
|
9369 sketch.params[key.substring(6)] = value;
|
rob@100
|
9370 } else {
|
rob@100
|
9371 sketch.options[key] = value;
|
rob@100
|
9372 }
|
rob@100
|
9373 }
|
rob@100
|
9374 }
|
rob@100
|
9375 }
|
rob@100
|
9376 return aCode;
|
rob@100
|
9377 }
|
rob@100
|
9378
|
rob@100
|
9379 // Parse/compiles Processing (Java-like) syntax to JavaScript syntax
|
rob@100
|
9380 Processing.compile = function(pdeCode) {
|
rob@100
|
9381 var sketch = new Processing.Sketch();
|
rob@100
|
9382 var code = preprocessCode(pdeCode, sketch);
|
rob@100
|
9383 var compiledPde = parseProcessing(code);
|
rob@100
|
9384 sketch.sourceCode = compiledPde;
|
rob@100
|
9385 return sketch;
|
rob@100
|
9386 };
|
rob@100
|
9387
|
rob@100
|
9388 // the logger for println()
|
rob@100
|
9389 var PjsConsole = function (document) {
|
rob@100
|
9390 var e = {}, added = false;
|
rob@100
|
9391 e.BufferMax = 200;
|
rob@100
|
9392 e.wrapper = document.createElement("div");
|
rob@100
|
9393 e.wrapper.setAttribute("style", "opacity:.75;display:block;position:fixed;bottom:0px;left:0px;right:0px;height:50px;background-color:#aaa");
|
rob@100
|
9394 e.dragger = document.createElement("div");
|
rob@100
|
9395 e.dragger.setAttribute("style", "display:block;border:3px black raised;cursor:n-resize;position:absolute;top:0px;left:0px;right:0px;height:5px;background-color:#333");
|
rob@100
|
9396 e.closer = document.createElement("div");
|
rob@100
|
9397 e.closer.onmouseover = function () {
|
rob@100
|
9398 e.closer.style.setProperty("background-color", "#ccc");
|
rob@100
|
9399 };
|
rob@100
|
9400 e.closer.onmouseout = function () {
|
rob@100
|
9401 e.closer.style.setProperty("background-color", "#ddd");
|
rob@100
|
9402 };
|
rob@100
|
9403 e.closer.innerHTML = "✖";
|
rob@100
|
9404 e.closer.setAttribute("style", "opacity:.5;display:block;border:3px black raised;position:absolute;top:10px;right:30px;height:20px;width:20px;background-color:#ddd;color:#000;line-height:20px;text-align:center;cursor:pointer;");
|
rob@100
|
9405 e.javaconsole = document.createElement("div");
|
rob@100
|
9406 e.javaconsole.setAttribute("style", "overflow-x: auto;display:block;position:absolute;left:10px;right:0px;bottom:5px;top:10px;overflow-y:scroll;height:40px;");
|
rob@100
|
9407 e.wrapper.appendChild(e.dragger);
|
rob@100
|
9408 e.wrapper.appendChild(e.javaconsole);
|
rob@100
|
9409 e.wrapper.appendChild(e.closer);
|
rob@100
|
9410 e.dragger.onmousedown = function (t) {
|
rob@100
|
9411 e.divheight = e.wrapper.style.height;
|
rob@100
|
9412 if (document.selection) document.selection.empty();
|
rob@100
|
9413 else window.getSelection().removeAllRanges();
|
rob@100
|
9414 var n = t.screenY;
|
rob@100
|
9415 window.onmousemove = function (t) {
|
rob@100
|
9416 e.wrapper.style.height = parseFloat(e.divheight) + (n - t.screenY) + "px";
|
rob@100
|
9417 e.javaconsole.style.height = parseFloat(e.divheight) + (n - t.screenY) - 10 + "px";
|
rob@100
|
9418 };
|
rob@100
|
9419 window.onmouseup = function (t) {
|
rob@100
|
9420 if (document.selection) document.selection.empty();
|
rob@100
|
9421 else window.getSelection().removeAllRanges();
|
rob@100
|
9422 e.wrapper.style.height = parseFloat(e.divheight) + (n - t.screenY) + "px";
|
rob@100
|
9423 e.javaconsole.style.height = parseFloat(e.divheight) + (n - t.screenY) - 10 + "px";
|
rob@100
|
9424 window.onmousemove = null;
|
rob@100
|
9425 window.onmouseup = null;
|
rob@100
|
9426 };
|
rob@100
|
9427 };
|
rob@100
|
9428 e.BufferArray = [];
|
rob@100
|
9429 e.print = e.log = function (t) {
|
rob@100
|
9430 // var oldheight = e.javaconsole.scrollHeight-e.javaconsole.scrollTop;
|
rob@100
|
9431 if (e.BufferArray[e.BufferArray.length - 1]) e.BufferArray[e.BufferArray.length - 1] += (t) + "";
|
rob@100
|
9432 else e.BufferArray.push(t);
|
rob@100
|
9433 e.javaconsole.innerHTML = e.BufferArray.join('');
|
rob@100
|
9434 if (e.wrapper.style.visibility === "hidden") {
|
rob@100
|
9435 e.wrapper.style.visibility = "visible";
|
rob@100
|
9436 }
|
rob@100
|
9437 //if (e.BufferArray.length > e.BufferMax) e.BufferArray.splice(0, 1);
|
rob@100
|
9438 //else e.javaconsole.scrollTop = oldheight;
|
rob@100
|
9439 if (e.wrapper.style.visibility === "hidden") {
|
rob@100
|
9440 e.wrapper.style.visibility = "visible";
|
rob@100
|
9441 }
|
rob@100
|
9442 };
|
rob@100
|
9443 e.println = function (t) {
|
rob@100
|
9444 if(!added) { document.body.appendChild(e.wrapper); }
|
rob@100
|
9445 e.print(t);
|
rob@100
|
9446 e.BufferArray.push('<br/>');
|
rob@100
|
9447 e.javaconsole.innerHTML = e.BufferArray.join('');
|
rob@100
|
9448 if (e.wrapper.style.visibility === "hidden") {
|
rob@100
|
9449 e.wrapper.style.visibility = "visible";
|
rob@100
|
9450 }
|
rob@100
|
9451 if (e.BufferArray.length > e.BufferMax) e.BufferArray.splice(0, 1);
|
rob@100
|
9452 else e.javaconsole.scrollTop = e.javaconsole.scrollHeight;
|
rob@100
|
9453 if (e.wrapper.style.visibility === "hidden") {
|
rob@100
|
9454 e.wrapper.style.visibility = "visible";
|
rob@100
|
9455 }
|
rob@100
|
9456 };
|
rob@100
|
9457 e.showconsole = function () {
|
rob@100
|
9458 e.wrapper.style.visibility = "visible";
|
rob@100
|
9459 };
|
rob@100
|
9460 e.hideconsole = function () {
|
rob@100
|
9461 e.wrapper.style.visibility = "hidden";
|
rob@100
|
9462 };
|
rob@100
|
9463 e.closer.onclick = function () {
|
rob@100
|
9464 e.hideconsole();
|
rob@100
|
9465 };
|
rob@100
|
9466 e.hideconsole();
|
rob@100
|
9467 return e;
|
rob@100
|
9468 };
|
rob@100
|
9469
|
rob@100
|
9470 Processing.logger = new PjsConsole(document);
|
rob@100
|
9471
|
rob@100
|
9472 // done
|
rob@100
|
9473 return Processing;
|
rob@100
|
9474 };
|
rob@100
|
9475
|
rob@100
|
9476 },{}],26:[function(require,module,exports){
|
rob@100
|
9477 /**
|
rob@100
|
9478 * Processing.js object
|
rob@100
|
9479 */
|
rob@100
|
9480 module.exports = function(options, undef) {
|
rob@100
|
9481 var defaultScope = options.defaultScope,
|
rob@100
|
9482 extend = options.extend,
|
rob@100
|
9483 Browser = options.Browser,
|
rob@100
|
9484 ajax = Browser.ajax,
|
rob@100
|
9485 navigator = Browser.navigator,
|
rob@100
|
9486 window = Browser.window,
|
rob@100
|
9487 XMLHttpRequest = window.XMLHttpRequest,
|
rob@100
|
9488 document = Browser.document,
|
rob@100
|
9489 noop = options.noop,
|
rob@100
|
9490
|
rob@100
|
9491 PConstants = defaultScope.PConstants;
|
rob@100
|
9492 PFont = defaultScope.PFont,
|
rob@100
|
9493 PShapeSVG = defaultScope.PShapeSVG,
|
rob@100
|
9494 PVector = defaultScope.PVector,
|
rob@100
|
9495 Char = Character = defaultScope.Char,
|
rob@100
|
9496 ObjectIterator = defaultScope.ObjectIterator,
|
rob@100
|
9497 XMLElement = defaultScope.XMLElement,
|
rob@100
|
9498 XML = defaultScope.XML;
|
rob@100
|
9499
|
rob@100
|
9500 // fascinating "read only" jshint error if we don't start a new var block here.
|
rob@100
|
9501 var HTMLCanvasElement = window.HTMLCanvasElement,
|
rob@100
|
9502 HTMLImageElement = window.HTMLImageElement,
|
rob@100
|
9503 localStorage = window.localStorage;
|
rob@100
|
9504
|
rob@100
|
9505 var isDOMPresent = ("document" in this) && !("fake" in this.document);
|
rob@100
|
9506
|
rob@100
|
9507 // document.head polyfill for the benefit of Firefox 3.6
|
rob@100
|
9508 if (!document.head) {
|
rob@100
|
9509 document.head = document.getElementsByTagName('head')[0];
|
rob@100
|
9510 }
|
rob@100
|
9511
|
rob@100
|
9512 var Float32Array = setupTypedArray("Float32Array", "WebGLFloatArray"),
|
rob@100
|
9513 Int32Array = setupTypedArray("Int32Array", "WebGLIntArray"),
|
rob@100
|
9514 Uint16Array = setupTypedArray("Uint16Array", "WebGLUnsignedShortArray"),
|
rob@100
|
9515 Uint8Array = setupTypedArray("Uint8Array", "WebGLUnsignedByteArray");
|
rob@100
|
9516
|
rob@100
|
9517 // Typed Arrays: fallback to WebGL arrays or Native JS arrays if unavailable
|
rob@100
|
9518 function setupTypedArray(name, fallback) {
|
rob@100
|
9519 // Check if TypedArray exists, and use if so.
|
rob@100
|
9520 if (name in window) {
|
rob@100
|
9521 return window[name];
|
rob@100
|
9522 }
|
rob@100
|
9523
|
rob@100
|
9524 // Check if WebGLArray exists
|
rob@100
|
9525 if (typeof window[fallback] === "function") {
|
rob@100
|
9526 return window[fallback];
|
rob@100
|
9527 }
|
rob@100
|
9528
|
rob@100
|
9529 // Use Native JS array
|
rob@100
|
9530 return function(obj) {
|
rob@100
|
9531 if (obj instanceof Array) {
|
rob@100
|
9532 return obj;
|
rob@100
|
9533 }
|
rob@100
|
9534 if (typeof obj === "number") {
|
rob@100
|
9535 var arr = [];
|
rob@100
|
9536 arr.length = obj;
|
rob@100
|
9537 return arr;
|
rob@100
|
9538 }
|
rob@100
|
9539 };
|
rob@100
|
9540 }
|
rob@100
|
9541
|
rob@100
|
9542 /* IE9+ quirks mode check - ticket #1606 */
|
rob@100
|
9543 if (document.documentMode >= 9 && !document.doctype) {
|
rob@100
|
9544 throw("The doctype directive is missing. The recommended doctype in Internet Explorer is the HTML5 doctype: <!DOCTYPE html>");
|
rob@100
|
9545 }
|
rob@100
|
9546
|
rob@100
|
9547 // Manage multiple Processing instances
|
rob@100
|
9548 var processingInstances = [];
|
rob@100
|
9549 var processingInstanceIds = {};
|
rob@100
|
9550
|
rob@100
|
9551 /**
|
rob@100
|
9552 * instance tracking - adding new instances
|
rob@100
|
9553 */
|
rob@100
|
9554 var addInstance = function(processing) {
|
rob@100
|
9555 if (processing.externals.canvas.id === undef || !processing.externals.canvas.id.length) {
|
rob@100
|
9556 processing.externals.canvas.id = "__processing" + processingInstances.length;
|
rob@100
|
9557 }
|
rob@100
|
9558 processingInstanceIds[processing.externals.canvas.id] = processingInstances.length;
|
rob@100
|
9559 processingInstances.push(processing);
|
rob@100
|
9560 };
|
rob@100
|
9561
|
rob@100
|
9562 /**
|
rob@100
|
9563 * instance tracking - removal
|
rob@100
|
9564 */
|
rob@100
|
9565 var removeInstance = function(id) {
|
rob@100
|
9566 processingInstances.splice(processingInstanceIds[id], 1);
|
rob@100
|
9567 delete processingInstanceIds[id];
|
rob@100
|
9568 };
|
rob@100
|
9569
|
rob@100
|
9570
|
rob@100
|
9571 /**
|
rob@100
|
9572 * The Processing object
|
rob@100
|
9573 */
|
rob@100
|
9574 var Processing = this.Processing = function(aCanvas, aCode, aFunctions) {
|
rob@100
|
9575
|
rob@100
|
9576 if (!(this instanceof Processing)) {
|
rob@100
|
9577 throw("called Processing constructor as if it were a function: missing 'new'.");
|
rob@100
|
9578 }
|
rob@100
|
9579
|
rob@100
|
9580 var curElement = {},
|
rob@100
|
9581 pgraphicsMode = (aCanvas === undef && aCode === undef);
|
rob@100
|
9582
|
rob@100
|
9583 if (pgraphicsMode) {
|
rob@100
|
9584 curElement = document.createElement("canvas");
|
rob@100
|
9585 } else {
|
rob@100
|
9586 // We'll take a canvas element or a string for a canvas element's id
|
rob@100
|
9587 curElement = typeof aCanvas === "string" ? document.getElementById(aCanvas) : aCanvas;
|
rob@100
|
9588 }
|
rob@100
|
9589
|
rob@100
|
9590 if (!('getContext' in curElement)) {
|
rob@100
|
9591 throw("called Processing constructor without passing canvas element reference or id.");
|
rob@100
|
9592 }
|
rob@100
|
9593
|
rob@100
|
9594 function unimplemented(s) {
|
rob@100
|
9595 Processing.debug('Unimplemented - ' + s);
|
rob@100
|
9596 }
|
rob@100
|
9597
|
rob@100
|
9598 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
9599 // JavaScript event binding and releasing
|
rob@100
|
9600 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
9601
|
rob@100
|
9602 var eventHandlers = [];
|
rob@100
|
9603
|
rob@100
|
9604 function attachEventHandler(elem, type, fn) {
|
rob@100
|
9605 if (elem.addEventListener) {
|
rob@100
|
9606 elem.addEventListener(type, fn, false);
|
rob@100
|
9607 } else {
|
rob@100
|
9608 elem.attachEvent("on" + type, fn);
|
rob@100
|
9609 }
|
rob@100
|
9610 eventHandlers.push({elem: elem, type: type, fn: fn});
|
rob@100
|
9611 }
|
rob@100
|
9612
|
rob@100
|
9613 function detachEventHandler(eventHandler) {
|
rob@100
|
9614 var elem = eventHandler.elem,
|
rob@100
|
9615 type = eventHandler.type,
|
rob@100
|
9616 fn = eventHandler.fn;
|
rob@100
|
9617 if (elem.removeEventListener) {
|
rob@100
|
9618 elem.removeEventListener(type, fn, false);
|
rob@100
|
9619 } else if (elem.detachEvent) {
|
rob@100
|
9620 elem.detachEvent("on" + type, fn);
|
rob@100
|
9621 }
|
rob@100
|
9622 }
|
rob@100
|
9623
|
rob@100
|
9624 function removeFirstArgument(args) {
|
rob@100
|
9625 return Array.prototype.slice.call(args, 1);
|
rob@100
|
9626 }
|
rob@100
|
9627
|
rob@100
|
9628 // When something new is added to "p." it must also be added to the "names" array.
|
rob@100
|
9629 // The names array contains the names of everything that is inside "p."
|
rob@100
|
9630 var p = this;
|
rob@100
|
9631
|
rob@100
|
9632 p.Char = p.Character = Char;
|
rob@100
|
9633
|
rob@100
|
9634 // add in the Processing API functions
|
rob@100
|
9635 extend.withCommonFunctions(p);
|
rob@100
|
9636 extend.withMath(p);
|
rob@100
|
9637 extend.withProxyFunctions(p, removeFirstArgument);
|
rob@100
|
9638 extend.withTouch(p, curElement, attachEventHandler, document, PConstants);
|
rob@100
|
9639
|
rob@100
|
9640 // custom functions and properties are added here
|
rob@100
|
9641 if(aFunctions) {
|
rob@100
|
9642 Object.keys(aFunctions).forEach(function(name) {
|
rob@100
|
9643 p[name] = aFunctions[name];
|
rob@100
|
9644 });
|
rob@100
|
9645 }
|
rob@100
|
9646
|
rob@100
|
9647 // PJS specific (non-p5) methods and properties to externalize
|
rob@100
|
9648 p.externals = {
|
rob@100
|
9649 canvas: curElement,
|
rob@100
|
9650 context: undef,
|
rob@100
|
9651 sketch: undef,
|
rob@100
|
9652 window: window
|
rob@100
|
9653 };
|
rob@100
|
9654
|
rob@100
|
9655 p.name = 'Processing.js Instance'; // Set Processing defaults / environment variables
|
rob@100
|
9656 p.use3DContext = false; // default '2d' canvas context
|
rob@100
|
9657
|
rob@100
|
9658 /**
|
rob@100
|
9659 * Confirms if a Processing program is "focused", meaning that it is
|
rob@100
|
9660 * active and will accept input from mouse or keyboard. This variable
|
rob@100
|
9661 * is "true" if it is focused and "false" if not. This variable is
|
rob@100
|
9662 * often used when you want to warn people they need to click on the
|
rob@100
|
9663 * browser before it will work.
|
rob@100
|
9664 */
|
rob@100
|
9665 p.focused = false;
|
rob@100
|
9666 p.breakShape = false;
|
rob@100
|
9667
|
rob@100
|
9668 // Glyph path storage for textFonts
|
rob@100
|
9669 p.glyphTable = {};
|
rob@100
|
9670
|
rob@100
|
9671 // Global vars for tracking mouse position
|
rob@100
|
9672 p.pmouseX = 0;
|
rob@100
|
9673 p.pmouseY = 0;
|
rob@100
|
9674 p.mouseX = 0;
|
rob@100
|
9675 p.mouseY = 0;
|
rob@100
|
9676 p.mouseButton = 0;
|
rob@100
|
9677 p.mouseScroll = 0;
|
rob@100
|
9678
|
rob@100
|
9679 // Undefined event handlers to be replaced by user when needed
|
rob@100
|
9680 p.mouseClicked = undef;
|
rob@100
|
9681 p.mouseDragged = undef;
|
rob@100
|
9682 p.mouseMoved = undef;
|
rob@100
|
9683 p.mousePressed = undef;
|
rob@100
|
9684 p.mouseReleased = undef;
|
rob@100
|
9685 p.mouseScrolled = undef;
|
rob@100
|
9686 p.mouseOver = undef;
|
rob@100
|
9687 p.mouseOut = undef;
|
rob@100
|
9688 p.touchStart = undef;
|
rob@100
|
9689 p.touchEnd = undef;
|
rob@100
|
9690 p.touchMove = undef;
|
rob@100
|
9691 p.touchCancel = undef;
|
rob@100
|
9692 p.key = undef;
|
rob@100
|
9693 p.keyCode = undef;
|
rob@100
|
9694 p.keyPressed = noop; // needed to remove function checks
|
rob@100
|
9695 p.keyReleased = noop;
|
rob@100
|
9696 p.keyTyped = noop;
|
rob@100
|
9697 p.draw = undef;
|
rob@100
|
9698 p.setup = undef;
|
rob@100
|
9699
|
rob@100
|
9700 // Remapped vars
|
rob@100
|
9701 p.__mousePressed = false;
|
rob@100
|
9702 p.__keyPressed = false;
|
rob@100
|
9703 p.__frameRate = 60;
|
rob@100
|
9704
|
rob@100
|
9705 // The current animation frame
|
rob@100
|
9706 p.frameCount = 0;
|
rob@100
|
9707
|
rob@100
|
9708 // The height/width of the canvas
|
rob@100
|
9709 p.width = 100;
|
rob@100
|
9710 p.height = 100;
|
rob@100
|
9711
|
rob@100
|
9712 // "Private" variables used to maintain state
|
rob@100
|
9713 var curContext,
|
rob@100
|
9714 curSketch,
|
rob@100
|
9715 drawing, // hold a Drawing2D or Drawing3D object
|
rob@100
|
9716 doFill = true,
|
rob@100
|
9717 fillStyle = [1.0, 1.0, 1.0, 1.0],
|
rob@100
|
9718 currentFillColor = 0xFFFFFFFF,
|
rob@100
|
9719 isFillDirty = true,
|
rob@100
|
9720 doStroke = true,
|
rob@100
|
9721 strokeStyle = [0.0, 0.0, 0.0, 1.0],
|
rob@100
|
9722 currentStrokeColor = 0xFF000000,
|
rob@100
|
9723 isStrokeDirty = true,
|
rob@100
|
9724 lineWidth = 1,
|
rob@100
|
9725 loopStarted = false,
|
rob@100
|
9726 renderSmooth = false,
|
rob@100
|
9727 doLoop = true,
|
rob@100
|
9728 looping = 0,
|
rob@100
|
9729 curRectMode = PConstants.CORNER,
|
rob@100
|
9730 curEllipseMode = PConstants.CENTER,
|
rob@100
|
9731 normalX = 0,
|
rob@100
|
9732 normalY = 0,
|
rob@100
|
9733 normalZ = 0,
|
rob@100
|
9734 normalMode = PConstants.NORMAL_MODE_AUTO,
|
rob@100
|
9735 curFrameRate = 60,
|
rob@100
|
9736 curMsPerFrame = 1000/curFrameRate,
|
rob@100
|
9737 curCursor = PConstants.ARROW,
|
rob@100
|
9738 oldCursor = curElement.style.cursor,
|
rob@100
|
9739 curShape = PConstants.POLYGON,
|
rob@100
|
9740 curShapeCount = 0,
|
rob@100
|
9741 curvePoints = [],
|
rob@100
|
9742 curTightness = 0,
|
rob@100
|
9743 curveDet = 20,
|
rob@100
|
9744 curveInited = false,
|
rob@100
|
9745 backgroundObj = -3355444, // rgb(204, 204, 204) is the default gray background colour
|
rob@100
|
9746 bezDetail = 20,
|
rob@100
|
9747 colorModeA = 255,
|
rob@100
|
9748 colorModeX = 255,
|
rob@100
|
9749 colorModeY = 255,
|
rob@100
|
9750 colorModeZ = 255,
|
rob@100
|
9751 pathOpen = false,
|
rob@100
|
9752 mouseDragging = false,
|
rob@100
|
9753 pmouseXLastFrame = 0,
|
rob@100
|
9754 pmouseYLastFrame = 0,
|
rob@100
|
9755 curColorMode = PConstants.RGB,
|
rob@100
|
9756 curTint = null,
|
rob@100
|
9757 curTint3d = null,
|
rob@100
|
9758 getLoaded = false,
|
rob@100
|
9759 start = Date.now(),
|
rob@100
|
9760 timeSinceLastFPS = start,
|
rob@100
|
9761 framesSinceLastFPS = 0,
|
rob@100
|
9762 textcanvas,
|
rob@100
|
9763 curveBasisMatrix,
|
rob@100
|
9764 curveToBezierMatrix,
|
rob@100
|
9765 curveDrawMatrix,
|
rob@100
|
9766 bezierDrawMatrix,
|
rob@100
|
9767 bezierBasisInverse,
|
rob@100
|
9768 bezierBasisMatrix,
|
rob@100
|
9769 curContextCache = { attributes: {}, locations: {} },
|
rob@100
|
9770 // Shaders
|
rob@100
|
9771 programObject3D,
|
rob@100
|
9772 programObject2D,
|
rob@100
|
9773 programObjectUnlitShape,
|
rob@100
|
9774 boxBuffer,
|
rob@100
|
9775 boxNormBuffer,
|
rob@100
|
9776 boxOutlineBuffer,
|
rob@100
|
9777 rectBuffer,
|
rob@100
|
9778 rectNormBuffer,
|
rob@100
|
9779 sphereBuffer,
|
rob@100
|
9780 lineBuffer,
|
rob@100
|
9781 fillBuffer,
|
rob@100
|
9782 fillColorBuffer,
|
rob@100
|
9783 strokeColorBuffer,
|
rob@100
|
9784 pointBuffer,
|
rob@100
|
9785 shapeTexVBO,
|
rob@100
|
9786 canTex, // texture for createGraphics
|
rob@100
|
9787 textTex, // texture for 3d tex
|
rob@100
|
9788 curTexture = {width:0,height:0},
|
rob@100
|
9789 curTextureMode = PConstants.IMAGE,
|
rob@100
|
9790 usingTexture = false,
|
rob@100
|
9791 textBuffer,
|
rob@100
|
9792 textureBuffer,
|
rob@100
|
9793 indexBuffer,
|
rob@100
|
9794 // Text alignment
|
rob@100
|
9795 horizontalTextAlignment = PConstants.LEFT,
|
rob@100
|
9796 verticalTextAlignment = PConstants.BASELINE,
|
rob@100
|
9797 textMode = PConstants.MODEL,
|
rob@100
|
9798 // Font state
|
rob@100
|
9799 curFontName = "Arial",
|
rob@100
|
9800 curTextSize = 12,
|
rob@100
|
9801 curTextAscent = 9,
|
rob@100
|
9802 curTextDescent = 2,
|
rob@100
|
9803 curTextLeading = 14,
|
rob@100
|
9804 curTextFont = PFont.get(curFontName, curTextSize),
|
rob@100
|
9805 // Pixels cache
|
rob@100
|
9806 originalContext,
|
rob@100
|
9807 proxyContext = null,
|
rob@100
|
9808 isContextReplaced = false,
|
rob@100
|
9809 setPixelsCached,
|
rob@100
|
9810 maxPixelsCached = 1000,
|
rob@100
|
9811 pressedKeysMap = [],
|
rob@100
|
9812 lastPressedKeyCode = null,
|
rob@100
|
9813 codedKeys = [ PConstants.SHIFT, PConstants.CONTROL, PConstants.ALT, PConstants.CAPSLK, PConstants.PGUP, PConstants.PGDN,
|
rob@100
|
9814 PConstants.END, PConstants.HOME, PConstants.LEFT, PConstants.UP, PConstants.RIGHT, PConstants.DOWN, PConstants.NUMLK,
|
rob@100
|
9815 PConstants.INSERT, PConstants.F1, PConstants.F2, PConstants.F3, PConstants.F4, PConstants.F5, PConstants.F6, PConstants.F7,
|
rob@100
|
9816 PConstants.F8, PConstants.F9, PConstants.F10, PConstants.F11, PConstants.F12, PConstants.META ];
|
rob@100
|
9817
|
rob@100
|
9818 // User can only have MAX_LIGHTS lights
|
rob@100
|
9819 var lightCount = 0;
|
rob@100
|
9820
|
rob@100
|
9821 //sphere stuff
|
rob@100
|
9822 var sphereDetailV = 0,
|
rob@100
|
9823 sphereDetailU = 0,
|
rob@100
|
9824 sphereX = [],
|
rob@100
|
9825 sphereY = [],
|
rob@100
|
9826 sphereZ = [],
|
rob@100
|
9827 sinLUT = new Float32Array(PConstants.SINCOS_LENGTH),
|
rob@100
|
9828 cosLUT = new Float32Array(PConstants.SINCOS_LENGTH),
|
rob@100
|
9829 sphereVerts,
|
rob@100
|
9830 sphereNorms;
|
rob@100
|
9831
|
rob@100
|
9832 // Camera defaults and settings
|
rob@100
|
9833 var cam,
|
rob@100
|
9834 cameraInv,
|
rob@100
|
9835 modelView,
|
rob@100
|
9836 modelViewInv,
|
rob@100
|
9837 userMatrixStack,
|
rob@100
|
9838 userReverseMatrixStack,
|
rob@100
|
9839 inverseCopy,
|
rob@100
|
9840 projection,
|
rob@100
|
9841 manipulatingCamera = false,
|
rob@100
|
9842 frustumMode = false,
|
rob@100
|
9843 cameraFOV = 60 * (Math.PI / 180),
|
rob@100
|
9844 cameraX = p.width / 2,
|
rob@100
|
9845 cameraY = p.height / 2,
|
rob@100
|
9846 cameraZ = cameraY / Math.tan(cameraFOV / 2),
|
rob@100
|
9847 cameraNear = cameraZ / 10,
|
rob@100
|
9848 cameraFar = cameraZ * 10,
|
rob@100
|
9849 cameraAspect = p.width / p.height;
|
rob@100
|
9850
|
rob@100
|
9851 var vertArray = [],
|
rob@100
|
9852 curveVertArray = [],
|
rob@100
|
9853 curveVertCount = 0,
|
rob@100
|
9854 isCurve = false,
|
rob@100
|
9855 isBezier = false,
|
rob@100
|
9856 firstVert = true;
|
rob@100
|
9857
|
rob@100
|
9858 //PShape stuff
|
rob@100
|
9859 var curShapeMode = PConstants.CORNER;
|
rob@100
|
9860
|
rob@100
|
9861 // Stores states for pushStyle() and popStyle().
|
rob@100
|
9862 var styleArray = [];
|
rob@100
|
9863
|
rob@100
|
9864 // The vertices for the box cannot be specified using a triangle strip since each
|
rob@100
|
9865 // side of the cube must have its own set of normals.
|
rob@100
|
9866 // Vertices are specified in a counter-clockwise order.
|
rob@100
|
9867 // Triangles are in this order: back, front, right, bottom, left, top.
|
rob@100
|
9868 var boxVerts = new Float32Array([
|
rob@100
|
9869 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5,
|
rob@100
|
9870 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5,
|
rob@100
|
9871 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5,
|
rob@100
|
9872 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5,
|
rob@100
|
9873 -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5,
|
rob@100
|
9874 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5]);
|
rob@100
|
9875
|
rob@100
|
9876 var boxOutlineVerts = new Float32Array([
|
rob@100
|
9877 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5,
|
rob@100
|
9878 -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5,
|
rob@100
|
9879 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
|
rob@100
|
9880 -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
|
rob@100
|
9881 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5,
|
rob@100
|
9882 -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5]);
|
rob@100
|
9883
|
rob@100
|
9884 var boxNorms = new Float32Array([
|
rob@100
|
9885 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
|
rob@100
|
9886 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
|
rob@100
|
9887 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
|
rob@100
|
9888 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
|
rob@100
|
9889 -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0,
|
rob@100
|
9890 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]);
|
rob@100
|
9891
|
rob@100
|
9892 // These verts are used for the fill and stroke using TRIANGLE_FAN and LINE_LOOP.
|
rob@100
|
9893 var rectVerts = new Float32Array([0,0,0, 0,1,0, 1,1,0, 1,0,0]);
|
rob@100
|
9894
|
rob@100
|
9895 var rectNorms = new Float32Array([0,0,1, 0,0,1, 0,0,1, 0,0,1]);
|
rob@100
|
9896
|
rob@100
|
9897 // Shader for points and lines in begin/endShape.
|
rob@100
|
9898 var vertexShaderSrcUnlitShape =
|
rob@100
|
9899 "varying vec4 vFrontColor;" +
|
rob@100
|
9900
|
rob@100
|
9901 "attribute vec3 aVertex;" +
|
rob@100
|
9902 "attribute vec4 aColor;" +
|
rob@100
|
9903
|
rob@100
|
9904 "uniform mat4 uView;" +
|
rob@100
|
9905 "uniform mat4 uProjection;" +
|
rob@100
|
9906 "uniform float uPointSize;" +
|
rob@100
|
9907
|
rob@100
|
9908 "void main(void) {" +
|
rob@100
|
9909 " vFrontColor = aColor;" +
|
rob@100
|
9910 " gl_PointSize = uPointSize;" +
|
rob@100
|
9911 " gl_Position = uProjection * uView * vec4(aVertex, 1.0);" +
|
rob@100
|
9912 "}";
|
rob@100
|
9913
|
rob@100
|
9914 var fragmentShaderSrcUnlitShape =
|
rob@100
|
9915 "#ifdef GL_ES\n" +
|
rob@100
|
9916 "precision highp float;\n" +
|
rob@100
|
9917 "#endif\n" +
|
rob@100
|
9918
|
rob@100
|
9919 "varying vec4 vFrontColor;" +
|
rob@100
|
9920 "uniform bool uSmooth;" +
|
rob@100
|
9921
|
rob@100
|
9922 "void main(void){" +
|
rob@100
|
9923 " if(uSmooth == true){" +
|
rob@100
|
9924 " float dist = distance(gl_PointCoord, vec2(0.5));" +
|
rob@100
|
9925 " if(dist > 0.5){" +
|
rob@100
|
9926 " discard;" +
|
rob@100
|
9927 " }" +
|
rob@100
|
9928 " }" +
|
rob@100
|
9929 " gl_FragColor = vFrontColor;" +
|
rob@100
|
9930 "}";
|
rob@100
|
9931
|
rob@100
|
9932 // Shader for rect, text, box outlines, sphere outlines, point() and line().
|
rob@100
|
9933 var vertexShaderSrc2D =
|
rob@100
|
9934 "varying vec4 vFrontColor;" +
|
rob@100
|
9935
|
rob@100
|
9936 "attribute vec3 aVertex;" +
|
rob@100
|
9937 "attribute vec2 aTextureCoord;" +
|
rob@100
|
9938 "uniform vec4 uColor;" +
|
rob@100
|
9939
|
rob@100
|
9940 "uniform mat4 uModel;" +
|
rob@100
|
9941 "uniform mat4 uView;" +
|
rob@100
|
9942 "uniform mat4 uProjection;" +
|
rob@100
|
9943 "uniform float uPointSize;" +
|
rob@100
|
9944 "varying vec2 vTextureCoord;"+
|
rob@100
|
9945
|
rob@100
|
9946 "void main(void) {" +
|
rob@100
|
9947 " gl_PointSize = uPointSize;" +
|
rob@100
|
9948 " vFrontColor = uColor;" +
|
rob@100
|
9949 " gl_Position = uProjection * uView * uModel * vec4(aVertex, 1.0);" +
|
rob@100
|
9950 " vTextureCoord = aTextureCoord;" +
|
rob@100
|
9951 "}";
|
rob@100
|
9952
|
rob@100
|
9953 var fragmentShaderSrc2D =
|
rob@100
|
9954 "#ifdef GL_ES\n" +
|
rob@100
|
9955 "precision highp float;\n" +
|
rob@100
|
9956 "#endif\n" +
|
rob@100
|
9957
|
rob@100
|
9958 "varying vec4 vFrontColor;" +
|
rob@100
|
9959 "varying vec2 vTextureCoord;"+
|
rob@100
|
9960
|
rob@100
|
9961 "uniform sampler2D uSampler;"+
|
rob@100
|
9962 "uniform int uIsDrawingText;"+
|
rob@100
|
9963 "uniform bool uSmooth;" +
|
rob@100
|
9964
|
rob@100
|
9965 "void main(void){" +
|
rob@100
|
9966 // WebGL does not support POINT_SMOOTH, so we do it ourselves
|
rob@100
|
9967 " if(uSmooth == true){" +
|
rob@100
|
9968 " float dist = distance(gl_PointCoord, vec2(0.5));" +
|
rob@100
|
9969 " if(dist > 0.5){" +
|
rob@100
|
9970 " discard;" +
|
rob@100
|
9971 " }" +
|
rob@100
|
9972 " }" +
|
rob@100
|
9973
|
rob@100
|
9974 " if(uIsDrawingText == 1){" +
|
rob@100
|
9975 " float alpha = texture2D(uSampler, vTextureCoord).a;"+
|
rob@100
|
9976 " gl_FragColor = vec4(vFrontColor.rgb * alpha, alpha);"+
|
rob@100
|
9977 " }" +
|
rob@100
|
9978 " else{" +
|
rob@100
|
9979 " gl_FragColor = vFrontColor;" +
|
rob@100
|
9980 " }" +
|
rob@100
|
9981 "}";
|
rob@100
|
9982
|
rob@100
|
9983 var webglMaxTempsWorkaround = /Windows/.test(navigator.userAgent);
|
rob@100
|
9984
|
rob@100
|
9985 // Vertex shader for boxes and spheres.
|
rob@100
|
9986 var vertexShaderSrc3D =
|
rob@100
|
9987 "varying vec4 vFrontColor;" +
|
rob@100
|
9988
|
rob@100
|
9989 "attribute vec3 aVertex;" +
|
rob@100
|
9990 "attribute vec3 aNormal;" +
|
rob@100
|
9991 "attribute vec4 aColor;" +
|
rob@100
|
9992 "attribute vec2 aTexture;" +
|
rob@100
|
9993 "varying vec2 vTexture;" +
|
rob@100
|
9994
|
rob@100
|
9995 "uniform vec4 uColor;" +
|
rob@100
|
9996
|
rob@100
|
9997 "uniform bool uUsingMat;" +
|
rob@100
|
9998 "uniform vec3 uSpecular;" +
|
rob@100
|
9999 "uniform vec3 uMaterialEmissive;" +
|
rob@100
|
10000 "uniform vec3 uMaterialAmbient;" +
|
rob@100
|
10001 "uniform vec3 uMaterialSpecular;" +
|
rob@100
|
10002 "uniform float uShininess;" +
|
rob@100
|
10003
|
rob@100
|
10004 "uniform mat4 uModel;" +
|
rob@100
|
10005 "uniform mat4 uView;" +
|
rob@100
|
10006 "uniform mat4 uProjection;" +
|
rob@100
|
10007 "uniform mat4 uNormalTransform;" +
|
rob@100
|
10008
|
rob@100
|
10009 "uniform int uLightCount;" +
|
rob@100
|
10010 "uniform vec3 uFalloff;" +
|
rob@100
|
10011
|
rob@100
|
10012 // Careful changing the order of these fields. Some cards
|
rob@100
|
10013 // have issues with memory alignment.
|
rob@100
|
10014 "struct Light {" +
|
rob@100
|
10015 " int type;" +
|
rob@100
|
10016 " vec3 color;" +
|
rob@100
|
10017 " vec3 position;" +
|
rob@100
|
10018 " vec3 direction;" +
|
rob@100
|
10019 " float angle;" +
|
rob@100
|
10020 " vec3 halfVector;" +
|
rob@100
|
10021 " float concentration;" +
|
rob@100
|
10022 "};" +
|
rob@100
|
10023
|
rob@100
|
10024 // nVidia cards have issues with arrays of structures
|
rob@100
|
10025 // so instead we create 8 instances of Light.
|
rob@100
|
10026 "uniform Light uLights0;" +
|
rob@100
|
10027 "uniform Light uLights1;" +
|
rob@100
|
10028 "uniform Light uLights2;" +
|
rob@100
|
10029 "uniform Light uLights3;" +
|
rob@100
|
10030 "uniform Light uLights4;" +
|
rob@100
|
10031 "uniform Light uLights5;" +
|
rob@100
|
10032 "uniform Light uLights6;" +
|
rob@100
|
10033 "uniform Light uLights7;" +
|
rob@100
|
10034
|
rob@100
|
10035 // GLSL does not support switch.
|
rob@100
|
10036 "Light getLight(int index){" +
|
rob@100
|
10037 " if(index == 0) return uLights0;" +
|
rob@100
|
10038 " if(index == 1) return uLights1;" +
|
rob@100
|
10039 " if(index == 2) return uLights2;" +
|
rob@100
|
10040 " if(index == 3) return uLights3;" +
|
rob@100
|
10041 " if(index == 4) return uLights4;" +
|
rob@100
|
10042 " if(index == 5) return uLights5;" +
|
rob@100
|
10043 " if(index == 6) return uLights6;" +
|
rob@100
|
10044 // Do not use a conditional for the last return statement
|
rob@100
|
10045 // because some video cards will fail and complain that
|
rob@100
|
10046 // "not all paths return".
|
rob@100
|
10047 " return uLights7;" +
|
rob@100
|
10048 "}" +
|
rob@100
|
10049
|
rob@100
|
10050 "void AmbientLight( inout vec3 totalAmbient, in vec3 ecPos, in Light light ) {" +
|
rob@100
|
10051 // Get the vector from the light to the vertex and
|
rob@100
|
10052 // get the distance from the current vector to the light position.
|
rob@100
|
10053 " float d = length( light.position - ecPos );" +
|
rob@100
|
10054 " float attenuation = 1.0 / ( uFalloff[0] + ( uFalloff[1] * d ) + ( uFalloff[2] * d * d ));" +
|
rob@100
|
10055 " totalAmbient += light.color * attenuation;" +
|
rob@100
|
10056 "}" +
|
rob@100
|
10057
|
rob@100
|
10058 /*
|
rob@100
|
10059 col - accumulated color
|
rob@100
|
10060 spec - accumulated specular highlight
|
rob@100
|
10061 vertNormal - Normal of the vertex
|
rob@100
|
10062 ecPos - eye coordinate position
|
rob@100
|
10063 light - light structure
|
rob@100
|
10064 */
|
rob@100
|
10065 "void DirectionalLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
|
rob@100
|
10066 " float powerFactor = 0.0;" +
|
rob@100
|
10067 " float nDotVP = max(0.0, dot( vertNormal, normalize(-light.position) ));" +
|
rob@100
|
10068 " float nDotVH = max(0.0, dot( vertNormal, normalize(-light.position-normalize(ecPos) )));" +
|
rob@100
|
10069
|
rob@100
|
10070 " if( nDotVP != 0.0 ){" +
|
rob@100
|
10071 " powerFactor = pow( nDotVH, uShininess );" +
|
rob@100
|
10072 " }" +
|
rob@100
|
10073
|
rob@100
|
10074 " col += light.color * nDotVP;" +
|
rob@100
|
10075 " spec += uSpecular * powerFactor;" +
|
rob@100
|
10076 "}" +
|
rob@100
|
10077
|
rob@100
|
10078 /*
|
rob@100
|
10079 col - accumulated color
|
rob@100
|
10080 spec - accumulated specular highlight
|
rob@100
|
10081 vertNormal - Normal of the vertex
|
rob@100
|
10082 ecPos - eye coordinate position
|
rob@100
|
10083 light - light structure
|
rob@100
|
10084 */
|
rob@100
|
10085 "void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
|
rob@100
|
10086 " float powerFactor;" +
|
rob@100
|
10087
|
rob@100
|
10088 // Get the vector from the light to the vertex.
|
rob@100
|
10089 " vec3 VP = light.position - ecPos;" +
|
rob@100
|
10090
|
rob@100
|
10091 // Get the distance from the current vector to the light position.
|
rob@100
|
10092 " float d = length( VP ); " +
|
rob@100
|
10093
|
rob@100
|
10094 // Normalize the light ray so it can be used in the dot product operation.
|
rob@100
|
10095 " VP = normalize( VP );" +
|
rob@100
|
10096
|
rob@100
|
10097 " float attenuation = 1.0 / ( uFalloff[0] + ( uFalloff[1] * d ) + ( uFalloff[2] * d * d ));" +
|
rob@100
|
10098
|
rob@100
|
10099 " float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
|
rob@100
|
10100 " vec3 halfVector = normalize( VP - normalize(ecPos) );" +
|
rob@100
|
10101 " float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
|
rob@100
|
10102
|
rob@100
|
10103 " if( nDotVP == 0.0 ) {" +
|
rob@100
|
10104 " powerFactor = 0.0;" +
|
rob@100
|
10105 " }" +
|
rob@100
|
10106 " else {" +
|
rob@100
|
10107 " powerFactor = pow( nDotHV, uShininess );" +
|
rob@100
|
10108 " }" +
|
rob@100
|
10109
|
rob@100
|
10110 " spec += uSpecular * powerFactor * attenuation;" +
|
rob@100
|
10111 " col += light.color * nDotVP * attenuation;" +
|
rob@100
|
10112 "}" +
|
rob@100
|
10113
|
rob@100
|
10114 /*
|
rob@100
|
10115 col - accumulated color
|
rob@100
|
10116 spec - accumulated specular highlight
|
rob@100
|
10117 vertNormal - Normal of the vertex
|
rob@100
|
10118 ecPos - eye coordinate position
|
rob@100
|
10119 light - light structure
|
rob@100
|
10120 */
|
rob@100
|
10121 "void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
|
rob@100
|
10122 " float spotAttenuation;" +
|
rob@100
|
10123 " float powerFactor = 0.0;" +
|
rob@100
|
10124
|
rob@100
|
10125 // Calculate the vector from the current vertex to the light.
|
rob@100
|
10126 " vec3 VP = light.position - ecPos;" +
|
rob@100
|
10127 " vec3 ldir = normalize( -light.direction );" +
|
rob@100
|
10128
|
rob@100
|
10129 // Get the distance from the spotlight and the vertex
|
rob@100
|
10130 " float d = length( VP );" +
|
rob@100
|
10131 " VP = normalize( VP );" +
|
rob@100
|
10132
|
rob@100
|
10133 " float attenuation = 1.0 / ( uFalloff[0] + ( uFalloff[1] * d ) + ( uFalloff[2] * d * d ) );" +
|
rob@100
|
10134
|
rob@100
|
10135 // Dot product of the vector from vertex to light and light direction.
|
rob@100
|
10136 " float spotDot = dot( VP, ldir );" +
|
rob@100
|
10137
|
rob@100
|
10138 // If the vertex falls inside the cone
|
rob@100
|
10139 (webglMaxTempsWorkaround ? // Windows reports max temps error if light.angle is used
|
rob@100
|
10140 " spotAttenuation = 1.0; " :
|
rob@100
|
10141 " if( spotDot > cos( light.angle ) ) {" +
|
rob@100
|
10142 " spotAttenuation = pow( spotDot, light.concentration );" +
|
rob@100
|
10143 " }" +
|
rob@100
|
10144 " else{" +
|
rob@100
|
10145 " spotAttenuation = 0.0;" +
|
rob@100
|
10146 " }" +
|
rob@100
|
10147 " attenuation *= spotAttenuation;" +
|
rob@100
|
10148 "") +
|
rob@100
|
10149
|
rob@100
|
10150 " float nDotVP = max( 0.0, dot( vertNormal, VP ) );" +
|
rob@100
|
10151 " vec3 halfVector = normalize( VP - normalize(ecPos) );" +
|
rob@100
|
10152 " float nDotHV = max( 0.0, dot( vertNormal, halfVector ) );" +
|
rob@100
|
10153
|
rob@100
|
10154 " if( nDotVP != 0.0 ) {" +
|
rob@100
|
10155 " powerFactor = pow( nDotHV, uShininess );" +
|
rob@100
|
10156 " }" +
|
rob@100
|
10157
|
rob@100
|
10158 " spec += uSpecular * powerFactor * attenuation;" +
|
rob@100
|
10159 " col += light.color * nDotVP * attenuation;" +
|
rob@100
|
10160 "}" +
|
rob@100
|
10161
|
rob@100
|
10162 "void main(void) {" +
|
rob@100
|
10163 " vec3 finalAmbient = vec3( 0.0 );" +
|
rob@100
|
10164 " vec3 finalDiffuse = vec3( 0.0 );" +
|
rob@100
|
10165 " vec3 finalSpecular = vec3( 0.0 );" +
|
rob@100
|
10166
|
rob@100
|
10167 " vec4 col = uColor;" +
|
rob@100
|
10168
|
rob@100
|
10169 " if ( uColor[0] == -1.0 ){" +
|
rob@100
|
10170 " col = aColor;" +
|
rob@100
|
10171 " }" +
|
rob@100
|
10172
|
rob@100
|
10173 // We use the sphere vertices as the normals when we create the sphere buffer.
|
rob@100
|
10174 // But this only works if the sphere vertices are unit length, so we
|
rob@100
|
10175 // have to normalize the normals here. Since this is only required for spheres
|
rob@100
|
10176 // we could consider placing this in a conditional later on.
|
rob@100
|
10177 " vec3 norm = normalize(vec3( uNormalTransform * vec4( aNormal, 0.0 ) ));" +
|
rob@100
|
10178
|
rob@100
|
10179 " vec4 ecPos4 = uView * uModel * vec4(aVertex, 1.0);" +
|
rob@100
|
10180 " vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" +
|
rob@100
|
10181
|
rob@100
|
10182 // If there were no lights this draw call, just use the
|
rob@100
|
10183 // assigned fill color of the shape and the specular value.
|
rob@100
|
10184 " if( uLightCount == 0 ) {" +
|
rob@100
|
10185 " vFrontColor = col + vec4(uMaterialSpecular, 1.0);" +
|
rob@100
|
10186 " }" +
|
rob@100
|
10187 " else {" +
|
rob@100
|
10188 // WebGL forces us to iterate over a constant value
|
rob@100
|
10189 // so we can't iterate using lightCount.
|
rob@100
|
10190 " for( int i = 0; i < 8; i++ ) {" +
|
rob@100
|
10191 " Light l = getLight(i);" +
|
rob@100
|
10192
|
rob@100
|
10193 // We can stop iterating if we know we have gone past
|
rob@100
|
10194 // the number of lights which are actually on. This gives us a
|
rob@100
|
10195 // significant performance increase with high vertex counts.
|
rob@100
|
10196 " if( i >= uLightCount ){" +
|
rob@100
|
10197 " break;" +
|
rob@100
|
10198 " }" +
|
rob@100
|
10199
|
rob@100
|
10200 " if( l.type == 0 ) {" +
|
rob@100
|
10201 " AmbientLight( finalAmbient, ecPos, l );" +
|
rob@100
|
10202 " }" +
|
rob@100
|
10203 " else if( l.type == 1 ) {" +
|
rob@100
|
10204 " DirectionalLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
|
rob@100
|
10205 " }" +
|
rob@100
|
10206 " else if( l.type == 2 ) {" +
|
rob@100
|
10207 " PointLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
|
rob@100
|
10208 " }" +
|
rob@100
|
10209 " else {" +
|
rob@100
|
10210 " SpotLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
|
rob@100
|
10211 " }" +
|
rob@100
|
10212 " }" +
|
rob@100
|
10213
|
rob@100
|
10214 " if( uUsingMat == false ) {" +
|
rob@100
|
10215 " vFrontColor = vec4(" +
|
rob@100
|
10216 " vec3( col ) * finalAmbient +" +
|
rob@100
|
10217 " vec3( col ) * finalDiffuse +" +
|
rob@100
|
10218 " vec3( col ) * finalSpecular," +
|
rob@100
|
10219 " col[3] );" +
|
rob@100
|
10220 " }" +
|
rob@100
|
10221 " else{" +
|
rob@100
|
10222 " vFrontColor = vec4( " +
|
rob@100
|
10223 " uMaterialEmissive + " +
|
rob@100
|
10224 " (vec3(col) * uMaterialAmbient * finalAmbient ) + " +
|
rob@100
|
10225 " (vec3(col) * finalDiffuse) + " +
|
rob@100
|
10226 " (uMaterialSpecular * finalSpecular), " +
|
rob@100
|
10227 " col[3] );" +
|
rob@100
|
10228 " }" +
|
rob@100
|
10229 " }" +
|
rob@100
|
10230
|
rob@100
|
10231 " vTexture.xy = aTexture.xy;" +
|
rob@100
|
10232 " gl_Position = uProjection * uView * uModel * vec4( aVertex, 1.0 );" +
|
rob@100
|
10233 "}";
|
rob@100
|
10234
|
rob@100
|
10235 var fragmentShaderSrc3D =
|
rob@100
|
10236 "#ifdef GL_ES\n" +
|
rob@100
|
10237 "precision highp float;\n" +
|
rob@100
|
10238 "#endif\n" +
|
rob@100
|
10239
|
rob@100
|
10240 "varying vec4 vFrontColor;" +
|
rob@100
|
10241
|
rob@100
|
10242 "uniform sampler2D uSampler;" +
|
rob@100
|
10243 "uniform bool uUsingTexture;" +
|
rob@100
|
10244 "varying vec2 vTexture;" +
|
rob@100
|
10245
|
rob@100
|
10246 // In Processing, when a texture is used, the fill color is ignored
|
rob@100
|
10247 // vec4(1.0,1.0,1.0,0.5)
|
rob@100
|
10248 "void main(void){" +
|
rob@100
|
10249 " if( uUsingTexture ){" +
|
rob@100
|
10250 " gl_FragColor = vec4(texture2D(uSampler, vTexture.xy)) * vFrontColor;" +
|
rob@100
|
10251 " }"+
|
rob@100
|
10252 " else{" +
|
rob@100
|
10253 " gl_FragColor = vFrontColor;" +
|
rob@100
|
10254 " }" +
|
rob@100
|
10255 "}";
|
rob@100
|
10256
|
rob@100
|
10257 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
10258 // 3D Functions
|
rob@100
|
10259 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
10260
|
rob@100
|
10261 /*
|
rob@100
|
10262 * Sets a uniform variable in a program object to a particular
|
rob@100
|
10263 * value. Before calling this function, ensure the correct
|
rob@100
|
10264 * program object has been installed as part of the current
|
rob@100
|
10265 * rendering state by calling useProgram.
|
rob@100
|
10266 *
|
rob@100
|
10267 * On some systems, if the variable exists in the shader but isn't used,
|
rob@100
|
10268 * the compiler will optimize it out and this function will fail.
|
rob@100
|
10269 *
|
rob@100
|
10270 * @param {String} cacheId
|
rob@100
|
10271 * @param {WebGLProgram} programObj program object returned from
|
rob@100
|
10272 * createProgramObject
|
rob@100
|
10273 * @param {String} varName the name of the variable in the shader
|
rob@100
|
10274 * @param {float | Array} varValue either a scalar value or an Array
|
rob@100
|
10275 *
|
rob@100
|
10276 * @returns none
|
rob@100
|
10277 *
|
rob@100
|
10278 * @see uniformi
|
rob@100
|
10279 * @see uniformMatrix
|
rob@100
|
10280 */
|
rob@100
|
10281 function uniformf(cacheId, programObj, varName, varValue) {
|
rob@100
|
10282 var varLocation = curContextCache.locations[cacheId];
|
rob@100
|
10283 if(varLocation === undef) {
|
rob@100
|
10284 varLocation = curContext.getUniformLocation(programObj, varName);
|
rob@100
|
10285 curContextCache.locations[cacheId] = varLocation;
|
rob@100
|
10286 }
|
rob@100
|
10287 // the variable won't be found if it was optimized out.
|
rob@100
|
10288 if (varLocation !== null) {
|
rob@100
|
10289 if (varValue.length === 4) {
|
rob@100
|
10290 curContext.uniform4fv(varLocation, varValue);
|
rob@100
|
10291 } else if (varValue.length === 3) {
|
rob@100
|
10292 curContext.uniform3fv(varLocation, varValue);
|
rob@100
|
10293 } else if (varValue.length === 2) {
|
rob@100
|
10294 curContext.uniform2fv(varLocation, varValue);
|
rob@100
|
10295 } else {
|
rob@100
|
10296 curContext.uniform1f(varLocation, varValue);
|
rob@100
|
10297 }
|
rob@100
|
10298 }
|
rob@100
|
10299 }
|
rob@100
|
10300
|
rob@100
|
10301 /**
|
rob@100
|
10302 * Sets a uniform int or int array in a program object to a particular
|
rob@100
|
10303 * value. Before calling this function, ensure the correct
|
rob@100
|
10304 * program object has been installed as part of the current
|
rob@100
|
10305 * rendering state.
|
rob@100
|
10306 *
|
rob@100
|
10307 * On some systems, if the variable exists in the shader but isn't used,
|
rob@100
|
10308 * the compiler will optimize it out and this function will fail.
|
rob@100
|
10309 *
|
rob@100
|
10310 * @param {String} cacheId
|
rob@100
|
10311 * @param {WebGLProgram} programObj program object returned from
|
rob@100
|
10312 * createProgramObject
|
rob@100
|
10313 * @param {String} varName the name of the variable in the shader
|
rob@100
|
10314 * @param {int | Array} varValue either a scalar value or an Array
|
rob@100
|
10315 *
|
rob@100
|
10316 * @returns none
|
rob@100
|
10317 *
|
rob@100
|
10318 * @see uniformf
|
rob@100
|
10319 * @see uniformMatrix
|
rob@100
|
10320 */
|
rob@100
|
10321 function uniformi(cacheId, programObj, varName, varValue) {
|
rob@100
|
10322 var varLocation = curContextCache.locations[cacheId];
|
rob@100
|
10323 if(varLocation === undef) {
|
rob@100
|
10324 varLocation = curContext.getUniformLocation(programObj, varName);
|
rob@100
|
10325 curContextCache.locations[cacheId] = varLocation;
|
rob@100
|
10326 }
|
rob@100
|
10327 // the variable won't be found if it was optimized out.
|
rob@100
|
10328 if (varLocation !== null) {
|
rob@100
|
10329 if (varValue.length === 4) {
|
rob@100
|
10330 curContext.uniform4iv(varLocation, varValue);
|
rob@100
|
10331 } else if (varValue.length === 3) {
|
rob@100
|
10332 curContext.uniform3iv(varLocation, varValue);
|
rob@100
|
10333 } else if (varValue.length === 2) {
|
rob@100
|
10334 curContext.uniform2iv(varLocation, varValue);
|
rob@100
|
10335 } else {
|
rob@100
|
10336 curContext.uniform1i(varLocation, varValue);
|
rob@100
|
10337 }
|
rob@100
|
10338 }
|
rob@100
|
10339 }
|
rob@100
|
10340
|
rob@100
|
10341 /**
|
rob@100
|
10342 * Sets the value of a uniform matrix variable in a program
|
rob@100
|
10343 * object. Before calling this function, ensure the correct
|
rob@100
|
10344 * program object has been installed as part of the current
|
rob@100
|
10345 * rendering state.
|
rob@100
|
10346 *
|
rob@100
|
10347 * On some systems, if the variable exists in the shader but
|
rob@100
|
10348 * isn't used, the compiler will optimize it out and this
|
rob@100
|
10349 * function will fail.
|
rob@100
|
10350 *
|
rob@100
|
10351 * @param {String} cacheId
|
rob@100
|
10352 * @param {WebGLProgram} programObj program object returned from
|
rob@100
|
10353 * createProgramObject
|
rob@100
|
10354 * @param {String} varName the name of the variable in the shader
|
rob@100
|
10355 * @param {boolean} transpose must be false
|
rob@100
|
10356 * @param {Array} matrix an array of 4, 9 or 16 values
|
rob@100
|
10357 *
|
rob@100
|
10358 * @returns none
|
rob@100
|
10359 *
|
rob@100
|
10360 * @see uniformi
|
rob@100
|
10361 * @see uniformf
|
rob@100
|
10362 */
|
rob@100
|
10363 function uniformMatrix(cacheId, programObj, varName, transpose, matrix) {
|
rob@100
|
10364 var varLocation = curContextCache.locations[cacheId];
|
rob@100
|
10365 if(varLocation === undef) {
|
rob@100
|
10366 varLocation = curContext.getUniformLocation(programObj, varName);
|
rob@100
|
10367 curContextCache.locations[cacheId] = varLocation;
|
rob@100
|
10368 }
|
rob@100
|
10369 // The variable won't be found if it was optimized out.
|
rob@100
|
10370 if (varLocation !== -1) {
|
rob@100
|
10371 if (matrix.length === 16) {
|
rob@100
|
10372 curContext.uniformMatrix4fv(varLocation, transpose, matrix);
|
rob@100
|
10373 } else if (matrix.length === 9) {
|
rob@100
|
10374 curContext.uniformMatrix3fv(varLocation, transpose, matrix);
|
rob@100
|
10375 } else {
|
rob@100
|
10376 curContext.uniformMatrix2fv(varLocation, transpose, matrix);
|
rob@100
|
10377 }
|
rob@100
|
10378 }
|
rob@100
|
10379 }
|
rob@100
|
10380
|
rob@100
|
10381 /**
|
rob@100
|
10382 * Binds the VBO, sets the vertex attribute data for the program
|
rob@100
|
10383 * object and enables the attribute.
|
rob@100
|
10384 *
|
rob@100
|
10385 * On some systems, if the attribute exists in the shader but
|
rob@100
|
10386 * isn't used, the compiler will optimize it out and this
|
rob@100
|
10387 * function will fail.
|
rob@100
|
10388 *
|
rob@100
|
10389 * @param {String} cacheId
|
rob@100
|
10390 * @param {WebGLProgram} programObj program object returned from
|
rob@100
|
10391 * createProgramObject
|
rob@100
|
10392 * @param {String} varName the name of the variable in the shader
|
rob@100
|
10393 * @param {int} size the number of components per vertex attribute
|
rob@100
|
10394 * @param {WebGLBuffer} VBO Vertex Buffer Object
|
rob@100
|
10395 *
|
rob@100
|
10396 * @returns none
|
rob@100
|
10397 *
|
rob@100
|
10398 * @see disableVertexAttribPointer
|
rob@100
|
10399 */
|
rob@100
|
10400 function vertexAttribPointer(cacheId, programObj, varName, size, VBO) {
|
rob@100
|
10401 var varLocation = curContextCache.attributes[cacheId];
|
rob@100
|
10402 if(varLocation === undef) {
|
rob@100
|
10403 varLocation = curContext.getAttribLocation(programObj, varName);
|
rob@100
|
10404 curContextCache.attributes[cacheId] = varLocation;
|
rob@100
|
10405 }
|
rob@100
|
10406 if (varLocation !== -1) {
|
rob@100
|
10407 curContext.bindBuffer(curContext.ARRAY_BUFFER, VBO);
|
rob@100
|
10408 curContext.vertexAttribPointer(varLocation, size, curContext.FLOAT, false, 0, 0);
|
rob@100
|
10409 curContext.enableVertexAttribArray(varLocation);
|
rob@100
|
10410 }
|
rob@100
|
10411 }
|
rob@100
|
10412
|
rob@100
|
10413 /**
|
rob@100
|
10414 * Disables a program object attribute from being sent to WebGL.
|
rob@100
|
10415 *
|
rob@100
|
10416 * @param {String} cacheId
|
rob@100
|
10417 * @param {WebGLProgram} programObj program object returned from
|
rob@100
|
10418 * createProgramObject
|
rob@100
|
10419 * @param {String} varName name of the attribute
|
rob@100
|
10420 *
|
rob@100
|
10421 * @returns none
|
rob@100
|
10422 *
|
rob@100
|
10423 * @see vertexAttribPointer
|
rob@100
|
10424 */
|
rob@100
|
10425 function disableVertexAttribPointer(cacheId, programObj, varName){
|
rob@100
|
10426 var varLocation = curContextCache.attributes[cacheId];
|
rob@100
|
10427 if(varLocation === undef) {
|
rob@100
|
10428 varLocation = curContext.getAttribLocation(programObj, varName);
|
rob@100
|
10429 curContextCache.attributes[cacheId] = varLocation;
|
rob@100
|
10430 }
|
rob@100
|
10431 if (varLocation !== -1) {
|
rob@100
|
10432 curContext.disableVertexAttribArray(varLocation);
|
rob@100
|
10433 }
|
rob@100
|
10434 }
|
rob@100
|
10435
|
rob@100
|
10436 /**
|
rob@100
|
10437 * Creates a WebGL program object.
|
rob@100
|
10438 *
|
rob@100
|
10439 * @param {String} vetexShaderSource
|
rob@100
|
10440 * @param {String} fragmentShaderSource
|
rob@100
|
10441 *
|
rob@100
|
10442 * @returns {WebGLProgram} A program object
|
rob@100
|
10443 */
|
rob@100
|
10444 var createProgramObject = function(curContext, vetexShaderSource, fragmentShaderSource) {
|
rob@100
|
10445 var vertexShaderObject = curContext.createShader(curContext.VERTEX_SHADER);
|
rob@100
|
10446 curContext.shaderSource(vertexShaderObject, vetexShaderSource);
|
rob@100
|
10447 curContext.compileShader(vertexShaderObject);
|
rob@100
|
10448 if (!curContext.getShaderParameter(vertexShaderObject, curContext.COMPILE_STATUS)) {
|
rob@100
|
10449 throw curContext.getShaderInfoLog(vertexShaderObject);
|
rob@100
|
10450 }
|
rob@100
|
10451
|
rob@100
|
10452 var fragmentShaderObject = curContext.createShader(curContext.FRAGMENT_SHADER);
|
rob@100
|
10453 curContext.shaderSource(fragmentShaderObject, fragmentShaderSource);
|
rob@100
|
10454 curContext.compileShader(fragmentShaderObject);
|
rob@100
|
10455 if (!curContext.getShaderParameter(fragmentShaderObject, curContext.COMPILE_STATUS)) {
|
rob@100
|
10456 throw curContext.getShaderInfoLog(fragmentShaderObject);
|
rob@100
|
10457 }
|
rob@100
|
10458
|
rob@100
|
10459 var programObject = curContext.createProgram();
|
rob@100
|
10460 curContext.attachShader(programObject, vertexShaderObject);
|
rob@100
|
10461 curContext.attachShader(programObject, fragmentShaderObject);
|
rob@100
|
10462 curContext.linkProgram(programObject);
|
rob@100
|
10463 if (!curContext.getProgramParameter(programObject, curContext.LINK_STATUS)) {
|
rob@100
|
10464 throw "Error linking shaders.";
|
rob@100
|
10465 }
|
rob@100
|
10466
|
rob@100
|
10467 return programObject;
|
rob@100
|
10468 };
|
rob@100
|
10469
|
rob@100
|
10470 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
10471 // 2D/3D drawing handling
|
rob@100
|
10472 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
10473 var imageModeCorner = function(x, y, w, h, whAreSizes) {
|
rob@100
|
10474 return {
|
rob@100
|
10475 x: x,
|
rob@100
|
10476 y: y,
|
rob@100
|
10477 w: w,
|
rob@100
|
10478 h: h
|
rob@100
|
10479 };
|
rob@100
|
10480 };
|
rob@100
|
10481 var imageModeConvert = imageModeCorner;
|
rob@100
|
10482
|
rob@100
|
10483 var imageModeCorners = function(x, y, w, h, whAreSizes) {
|
rob@100
|
10484 return {
|
rob@100
|
10485 x: x,
|
rob@100
|
10486 y: y,
|
rob@100
|
10487 w: whAreSizes ? w : w - x,
|
rob@100
|
10488 h: whAreSizes ? h : h - y
|
rob@100
|
10489 };
|
rob@100
|
10490 };
|
rob@100
|
10491
|
rob@100
|
10492 var imageModeCenter = function(x, y, w, h, whAreSizes) {
|
rob@100
|
10493 return {
|
rob@100
|
10494 x: x - w / 2,
|
rob@100
|
10495 y: y - h / 2,
|
rob@100
|
10496 w: w,
|
rob@100
|
10497 h: h
|
rob@100
|
10498 };
|
rob@100
|
10499 };
|
rob@100
|
10500
|
rob@100
|
10501 // Objects for shared, 2D and 3D contexts
|
rob@100
|
10502 var DrawingShared = function(){};
|
rob@100
|
10503 var Drawing2D = function(){};
|
rob@100
|
10504 var Drawing3D = function(){};
|
rob@100
|
10505 var DrawingPre = function(){};
|
rob@100
|
10506
|
rob@100
|
10507 // Setup the prototype chain
|
rob@100
|
10508 Drawing2D.prototype = new DrawingShared();
|
rob@100
|
10509 Drawing2D.prototype.constructor = Drawing2D;
|
rob@100
|
10510 Drawing3D.prototype = new DrawingShared();
|
rob@100
|
10511 Drawing3D.prototype.constructor = Drawing3D;
|
rob@100
|
10512 DrawingPre.prototype = new DrawingShared();
|
rob@100
|
10513 DrawingPre.prototype.constructor = DrawingPre;
|
rob@100
|
10514
|
rob@100
|
10515 // A no-op function for when the user calls 3D functions from a 2D sketch
|
rob@100
|
10516 // We can change this to a throw or console.error() later if we want
|
rob@100
|
10517 DrawingShared.prototype.a3DOnlyFunction = noop;
|
rob@100
|
10518
|
rob@100
|
10519 /**
|
rob@100
|
10520 * The shape() function displays shapes to the screen.
|
rob@100
|
10521 * Processing currently works with SVG shapes only.
|
rob@100
|
10522 * The <b>shape</b> parameter specifies the shape to display and the <b>x</b>
|
rob@100
|
10523 * and <b>y</b> parameters define the location of the shape from its
|
rob@100
|
10524 * upper-left corner.
|
rob@100
|
10525 * The shape is displayed at its original size unless the <b>width</b>
|
rob@100
|
10526 * and <b>height</b> parameters specify a different size.
|
rob@100
|
10527 * The <b>shapeMode()</b> function changes the way the parameters work.
|
rob@100
|
10528 * A call to <b>shapeMode(CORNERS)</b>, for example, will change the width
|
rob@100
|
10529 * and height parameters to define the x and y values of the opposite corner
|
rob@100
|
10530 * of the shape.
|
rob@100
|
10531 * <br><br>
|
rob@100
|
10532 * Note complex shapes may draw awkwardly with P2D, P3D, and OPENGL. Those
|
rob@100
|
10533 * renderers do not yet support shapes that have holes or complicated breaks.
|
rob@100
|
10534 *
|
rob@100
|
10535 * @param {PShape} shape the shape to display
|
rob@100
|
10536 * @param {int|float} x x-coordinate of the shape
|
rob@100
|
10537 * @param {int|float} y y-coordinate of the shape
|
rob@100
|
10538 * @param {int|float} width width to display the shape
|
rob@100
|
10539 * @param {int|float} height height to display the shape
|
rob@100
|
10540 *
|
rob@100
|
10541 * @see PShape
|
rob@100
|
10542 * @see loadShape()
|
rob@100
|
10543 * @see shapeMode()
|
rob@100
|
10544 */
|
rob@100
|
10545 p.shape = function(shape, x, y, width, height) {
|
rob@100
|
10546 if (arguments.length >= 1 && arguments[0] !== null) {
|
rob@100
|
10547 if (shape.isVisible()) {
|
rob@100
|
10548 p.pushMatrix();
|
rob@100
|
10549 if (curShapeMode === PConstants.CENTER) {
|
rob@100
|
10550 if (arguments.length === 5) {
|
rob@100
|
10551 p.translate(x - width/2, y - height/2);
|
rob@100
|
10552 p.scale(width / shape.getWidth(), height / shape.getHeight());
|
rob@100
|
10553 } else if (arguments.length === 3) {
|
rob@100
|
10554 p.translate(x - shape.getWidth()/2, - shape.getHeight()/2);
|
rob@100
|
10555 } else {
|
rob@100
|
10556 p.translate(-shape.getWidth()/2, -shape.getHeight()/2);
|
rob@100
|
10557 }
|
rob@100
|
10558 } else if (curShapeMode === PConstants.CORNER) {
|
rob@100
|
10559 if (arguments.length === 5) {
|
rob@100
|
10560 p.translate(x, y);
|
rob@100
|
10561 p.scale(width / shape.getWidth(), height / shape.getHeight());
|
rob@100
|
10562 } else if (arguments.length === 3) {
|
rob@100
|
10563 p.translate(x, y);
|
rob@100
|
10564 }
|
rob@100
|
10565 } else if (curShapeMode === PConstants.CORNERS) {
|
rob@100
|
10566 if (arguments.length === 5) {
|
rob@100
|
10567 width -= x;
|
rob@100
|
10568 height -= y;
|
rob@100
|
10569 p.translate(x, y);
|
rob@100
|
10570 p.scale(width / shape.getWidth(), height / shape.getHeight());
|
rob@100
|
10571 } else if (arguments.length === 3) {
|
rob@100
|
10572 p.translate(x, y);
|
rob@100
|
10573 }
|
rob@100
|
10574 }
|
rob@100
|
10575 shape.draw(p);
|
rob@100
|
10576 if ((arguments.length === 1 && curShapeMode === PConstants.CENTER ) || arguments.length > 1) {
|
rob@100
|
10577 p.popMatrix();
|
rob@100
|
10578 }
|
rob@100
|
10579 }
|
rob@100
|
10580 }
|
rob@100
|
10581 };
|
rob@100
|
10582
|
rob@100
|
10583 /**
|
rob@100
|
10584 * The shapeMode() function modifies the location from which shapes draw.
|
rob@100
|
10585 * The default mode is <b>shapeMode(CORNER)</b>, which specifies the
|
rob@100
|
10586 * location to be the upper left corner of the shape and uses the third
|
rob@100
|
10587 * and fourth parameters of <b>shape()</b> to specify the width and height.
|
rob@100
|
10588 * The syntax <b>shapeMode(CORNERS)</b> uses the first and second parameters
|
rob@100
|
10589 * of <b>shape()</b> to set the location of one corner and uses the third
|
rob@100
|
10590 * and fourth parameters to set the opposite corner.
|
rob@100
|
10591 * The syntax <b>shapeMode(CENTER)</b> draws the shape from its center point
|
rob@100
|
10592 * and uses the third and forth parameters of <b>shape()</b> to specify the
|
rob@100
|
10593 * width and height.
|
rob@100
|
10594 * The parameter must be written in "ALL CAPS" because Processing syntax
|
rob@100
|
10595 * is case sensitive.
|
rob@100
|
10596 *
|
rob@100
|
10597 * @param {int} mode One of CORNER, CORNERS, CENTER
|
rob@100
|
10598 *
|
rob@100
|
10599 * @see shape()
|
rob@100
|
10600 * @see rectMode()
|
rob@100
|
10601 */
|
rob@100
|
10602 p.shapeMode = function (mode) {
|
rob@100
|
10603 curShapeMode = mode;
|
rob@100
|
10604 };
|
rob@100
|
10605
|
rob@100
|
10606 /**
|
rob@100
|
10607 * The loadShape() function loads vector shapes into a variable of type PShape. Currently, only SVG files may be loaded.
|
rob@100
|
10608 * In most cases, <b>loadShape()</b> should be used inside <b>setup()</b> because loading shapes inside <b>draw()</b> will reduce the speed of a sketch.
|
rob@100
|
10609 *
|
rob@100
|
10610 * @param {String} filename an SVG file
|
rob@100
|
10611 *
|
rob@100
|
10612 * @return {PShape} a object of type PShape or null
|
rob@100
|
10613 * @see PShape
|
rob@100
|
10614 * @see PApplet#shape()
|
rob@100
|
10615 * @see PApplet#shapeMode()
|
rob@100
|
10616 */
|
rob@100
|
10617 p.loadShape = function (filename) {
|
rob@100
|
10618 if (arguments.length === 1) {
|
rob@100
|
10619 if (filename.indexOf(".svg") > -1) {
|
rob@100
|
10620 return new PShapeSVG(null, filename);
|
rob@100
|
10621 }
|
rob@100
|
10622 }
|
rob@100
|
10623 return null;
|
rob@100
|
10624 };
|
rob@100
|
10625
|
rob@100
|
10626 /**
|
rob@100
|
10627 * Processing 2.0 function for loading XML files.
|
rob@100
|
10628 *
|
rob@100
|
10629 * @param {String} uri The uri for the xml file to load.
|
rob@100
|
10630 *
|
rob@100
|
10631 * @return {XML} An XML object representing the xml data.
|
rob@100
|
10632 */
|
rob@100
|
10633 p.loadXML = function(uri) {
|
rob@100
|
10634 return new XML(p, uri);
|
rob@100
|
10635 };
|
rob@100
|
10636
|
rob@100
|
10637
|
rob@100
|
10638 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
10639 // 2D Matrix
|
rob@100
|
10640 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
10641
|
rob@100
|
10642 /**
|
rob@100
|
10643 * Helper function for printMatrix(). Finds the largest scalar
|
rob@100
|
10644 * in the matrix, then number of digits left of the decimal.
|
rob@100
|
10645 * Call from PMatrix2D and PMatrix3D's print() function.
|
rob@100
|
10646 */
|
rob@100
|
10647 var printMatrixHelper = function(elements) {
|
rob@100
|
10648 var big = 0;
|
rob@100
|
10649 for (var i = 0; i < elements.length; i++) {
|
rob@100
|
10650 if (i !== 0) {
|
rob@100
|
10651 big = Math.max(big, Math.abs(elements[i]));
|
rob@100
|
10652 } else {
|
rob@100
|
10653 big = Math.abs(elements[i]);
|
rob@100
|
10654 }
|
rob@100
|
10655 }
|
rob@100
|
10656
|
rob@100
|
10657 var digits = (big + "").indexOf(".");
|
rob@100
|
10658 if (digits === 0) {
|
rob@100
|
10659 digits = 1;
|
rob@100
|
10660 } else if (digits === -1) {
|
rob@100
|
10661 digits = (big + "").length;
|
rob@100
|
10662 }
|
rob@100
|
10663
|
rob@100
|
10664 return digits;
|
rob@100
|
10665 };
|
rob@100
|
10666 /**
|
rob@100
|
10667 * PMatrix2D is a 3x2 affine matrix implementation. The constructor accepts another PMatrix2D or a list of six float elements.
|
rob@100
|
10668 * If no parameters are provided the matrix is set to the identity matrix.
|
rob@100
|
10669 *
|
rob@100
|
10670 * @param {PMatrix2D} matrix the initial matrix to set to
|
rob@100
|
10671 * @param {float} m00 the first element of the matrix
|
rob@100
|
10672 * @param {float} m01 the second element of the matrix
|
rob@100
|
10673 * @param {float} m02 the third element of the matrix
|
rob@100
|
10674 * @param {float} m10 the fourth element of the matrix
|
rob@100
|
10675 * @param {float} m11 the fifth element of the matrix
|
rob@100
|
10676 * @param {float} m12 the sixth element of the matrix
|
rob@100
|
10677 */
|
rob@100
|
10678 var PMatrix2D = p.PMatrix2D = function() {
|
rob@100
|
10679 if (arguments.length === 0) {
|
rob@100
|
10680 this.reset();
|
rob@100
|
10681 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
|
rob@100
|
10682 this.set(arguments[0].array());
|
rob@100
|
10683 } else if (arguments.length === 6) {
|
rob@100
|
10684 this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
|
rob@100
|
10685 }
|
rob@100
|
10686 };
|
rob@100
|
10687 /**
|
rob@100
|
10688 * PMatrix2D methods
|
rob@100
|
10689 */
|
rob@100
|
10690 PMatrix2D.prototype = {
|
rob@100
|
10691 /**
|
rob@100
|
10692 * @member PMatrix2D
|
rob@100
|
10693 * The set() function sets the matrix elements. The function accepts either another PMatrix2D, an array of elements, or a list of six floats.
|
rob@100
|
10694 *
|
rob@100
|
10695 * @param {PMatrix2D} matrix the matrix to set this matrix to
|
rob@100
|
10696 * @param {float[]} elements an array of elements to set this matrix to
|
rob@100
|
10697 * @param {float} m00 the first element of the matrix
|
rob@100
|
10698 * @param {float} m01 the third element of the matrix
|
rob@100
|
10699 * @param {float} m10 the fourth element of the matrix
|
rob@100
|
10700 * @param {float} m11 the fith element of the matrix
|
rob@100
|
10701 * @param {float} m12 the sixth element of the matrix
|
rob@100
|
10702 */
|
rob@100
|
10703 set: function() {
|
rob@100
|
10704 if (arguments.length === 6) {
|
rob@100
|
10705 var a = arguments;
|
rob@100
|
10706 this.set([a[0], a[1], a[2],
|
rob@100
|
10707 a[3], a[4], a[5]]);
|
rob@100
|
10708 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
|
rob@100
|
10709 this.elements = arguments[0].array();
|
rob@100
|
10710 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
rob@100
|
10711 this.elements = arguments[0].slice();
|
rob@100
|
10712 }
|
rob@100
|
10713 },
|
rob@100
|
10714 /**
|
rob@100
|
10715 * @member PMatrix2D
|
rob@100
|
10716 * The get() function returns a copy of this PMatrix2D.
|
rob@100
|
10717 *
|
rob@100
|
10718 * @return {PMatrix2D} a copy of this PMatrix2D
|
rob@100
|
10719 */
|
rob@100
|
10720 get: function() {
|
rob@100
|
10721 var outgoing = new PMatrix2D();
|
rob@100
|
10722 outgoing.set(this.elements);
|
rob@100
|
10723 return outgoing;
|
rob@100
|
10724 },
|
rob@100
|
10725 /**
|
rob@100
|
10726 * @member PMatrix2D
|
rob@100
|
10727 * The reset() function sets this PMatrix2D to the identity matrix.
|
rob@100
|
10728 */
|
rob@100
|
10729 reset: function() {
|
rob@100
|
10730 this.set([1, 0, 0, 0, 1, 0]);
|
rob@100
|
10731 },
|
rob@100
|
10732 /**
|
rob@100
|
10733 * @member PMatrix2D
|
rob@100
|
10734 * The array() function returns a copy of the element values.
|
rob@100
|
10735 * @addon
|
rob@100
|
10736 *
|
rob@100
|
10737 * @return {float[]} returns a copy of the element values
|
rob@100
|
10738 */
|
rob@100
|
10739 array: function array() {
|
rob@100
|
10740 return this.elements.slice();
|
rob@100
|
10741 },
|
rob@100
|
10742 /**
|
rob@100
|
10743 * @member PMatrix2D
|
rob@100
|
10744 * The translate() function translates this matrix by moving the current coordinates to the location specified by tx and ty.
|
rob@100
|
10745 *
|
rob@100
|
10746 * @param {float} tx the x-axis coordinate to move to
|
rob@100
|
10747 * @param {float} ty the y-axis coordinate to move to
|
rob@100
|
10748 */
|
rob@100
|
10749 translate: function(tx, ty) {
|
rob@100
|
10750 this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2];
|
rob@100
|
10751 this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5];
|
rob@100
|
10752 },
|
rob@100
|
10753 /**
|
rob@100
|
10754 * @member PMatrix2D
|
rob@100
|
10755 * The invTranslate() function translates this matrix by moving the current coordinates to the negative location specified by tx and ty.
|
rob@100
|
10756 *
|
rob@100
|
10757 * @param {float} tx the x-axis coordinate to move to
|
rob@100
|
10758 * @param {float} ty the y-axis coordinate to move to
|
rob@100
|
10759 */
|
rob@100
|
10760 invTranslate: function(tx, ty) {
|
rob@100
|
10761 this.translate(-tx, -ty);
|
rob@100
|
10762 },
|
rob@100
|
10763 /**
|
rob@100
|
10764 * @member PMatrix2D
|
rob@100
|
10765 * The transpose() function is not used in processingjs.
|
rob@100
|
10766 */
|
rob@100
|
10767 transpose: function() {
|
rob@100
|
10768 // Does nothing in Processing.
|
rob@100
|
10769 },
|
rob@100
|
10770 /**
|
rob@100
|
10771 * @member PMatrix2D
|
rob@100
|
10772 * The mult() function multiplied this matrix.
|
rob@100
|
10773 * If two array elements are passed in the function will multiply a two element vector against this matrix.
|
rob@100
|
10774 * If target is null or not length four, a new float array will be returned.
|
rob@100
|
10775 * The values for vec and target can be the same (though that's less efficient).
|
rob@100
|
10776 * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
|
rob@100
|
10777 *
|
rob@100
|
10778 * @param {PVector} source, target the PVectors used to multiply this matrix
|
rob@100
|
10779 * @param {float[]} source, target the arrays used to multiply this matrix
|
rob@100
|
10780 *
|
rob@100
|
10781 * @return {PVector|float[]} returns a PVector or an array representing the new matrix
|
rob@100
|
10782 */
|
rob@100
|
10783 mult: function(source, target) {
|
rob@100
|
10784 var x, y;
|
rob@100
|
10785 if (source instanceof PVector) {
|
rob@100
|
10786 x = source.x;
|
rob@100
|
10787 y = source.y;
|
rob@100
|
10788 if (!target) {
|
rob@100
|
10789 target = new PVector();
|
rob@100
|
10790 }
|
rob@100
|
10791 } else if (source instanceof Array) {
|
rob@100
|
10792 x = source[0];
|
rob@100
|
10793 y = source[1];
|
rob@100
|
10794 if (!target) {
|
rob@100
|
10795 target = [];
|
rob@100
|
10796 }
|
rob@100
|
10797 }
|
rob@100
|
10798 if (target instanceof Array) {
|
rob@100
|
10799 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2];
|
rob@100
|
10800 target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5];
|
rob@100
|
10801 } else if (target instanceof PVector) {
|
rob@100
|
10802 target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2];
|
rob@100
|
10803 target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5];
|
rob@100
|
10804 target.z = 0;
|
rob@100
|
10805 }
|
rob@100
|
10806 return target;
|
rob@100
|
10807 },
|
rob@100
|
10808 /**
|
rob@100
|
10809 * @member PMatrix2D
|
rob@100
|
10810 * The multX() function calculates the x component of a vector from a transformation.
|
rob@100
|
10811 *
|
rob@100
|
10812 * @param {float} x the x component of the vector being transformed
|
rob@100
|
10813 * @param {float} y the y component of the vector being transformed
|
rob@100
|
10814 *
|
rob@100
|
10815 * @return {float} returnes the result of the calculation
|
rob@100
|
10816 */
|
rob@100
|
10817 multX: function(x, y) {
|
rob@100
|
10818 return (x * this.elements[0] + y * this.elements[1] + this.elements[2]);
|
rob@100
|
10819 },
|
rob@100
|
10820 /**
|
rob@100
|
10821 * @member PMatrix2D
|
rob@100
|
10822 * The multY() function calculates the y component of a vector from a transformation.
|
rob@100
|
10823 *
|
rob@100
|
10824 * @param {float} x the x component of the vector being transformed
|
rob@100
|
10825 * @param {float} y the y component of the vector being transformed
|
rob@100
|
10826 *
|
rob@100
|
10827 * @return {float} returnes the result of the calculation
|
rob@100
|
10828 */
|
rob@100
|
10829 multY: function(x, y) {
|
rob@100
|
10830 return (x * this.elements[3] + y * this.elements[4] + this.elements[5]);
|
rob@100
|
10831 },
|
rob@100
|
10832 /**
|
rob@100
|
10833 * @member PMatrix2D
|
rob@100
|
10834 * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
|
rob@100
|
10835 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
10836 *
|
rob@100
|
10837 * @param {float} angle angle of skew specified in radians
|
rob@100
|
10838 */
|
rob@100
|
10839 skewX: function(angle) {
|
rob@100
|
10840 this.apply(1, 0, 1, angle, 0, 0);
|
rob@100
|
10841 },
|
rob@100
|
10842 /**
|
rob@100
|
10843 * @member PMatrix2D
|
rob@100
|
10844 * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
|
rob@100
|
10845 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
10846 *
|
rob@100
|
10847 * @param {float} angle angle of skew specified in radians
|
rob@100
|
10848 */
|
rob@100
|
10849 skewY: function(angle) {
|
rob@100
|
10850 this.apply(1, 0, 1, 0, angle, 0);
|
rob@100
|
10851 },
|
rob@100
|
10852 /**
|
rob@100
|
10853 * @member PMatrix2D
|
rob@100
|
10854 * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter.
|
rob@100
|
10855 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
10856 *
|
rob@100
|
10857 * @param {float} angle angle of skew specified in radians
|
rob@100
|
10858 */
|
rob@100
|
10859 shearX: function(angle) {
|
rob@100
|
10860 this.apply(1, 0, 1, Math.tan(angle) , 0, 0);
|
rob@100
|
10861 },
|
rob@100
|
10862 /**
|
rob@100
|
10863 * @member PMatrix2D
|
rob@100
|
10864 * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter.
|
rob@100
|
10865 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
10866 *
|
rob@100
|
10867 * @param {float} angle angle of skew specified in radians
|
rob@100
|
10868 */
|
rob@100
|
10869 shearY: function(angle) {
|
rob@100
|
10870 this.apply(1, 0, 1, 0, Math.tan(angle), 0);
|
rob@100
|
10871 },
|
rob@100
|
10872 /**
|
rob@100
|
10873 * @member PMatrix2D
|
rob@100
|
10874 * The determinant() function calvculates the determinant of this matrix.
|
rob@100
|
10875 *
|
rob@100
|
10876 * @return {float} the determinant of the matrix
|
rob@100
|
10877 */
|
rob@100
|
10878 determinant: function() {
|
rob@100
|
10879 return (this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3]);
|
rob@100
|
10880 },
|
rob@100
|
10881 /**
|
rob@100
|
10882 * @member PMatrix2D
|
rob@100
|
10883 * The invert() function inverts this matrix
|
rob@100
|
10884 *
|
rob@100
|
10885 * @return {boolean} true if successful
|
rob@100
|
10886 */
|
rob@100
|
10887 invert: function() {
|
rob@100
|
10888 var d = this.determinant();
|
rob@100
|
10889 if (Math.abs( d ) > PConstants.MIN_INT) {
|
rob@100
|
10890 var old00 = this.elements[0];
|
rob@100
|
10891 var old01 = this.elements[1];
|
rob@100
|
10892 var old02 = this.elements[2];
|
rob@100
|
10893 var old10 = this.elements[3];
|
rob@100
|
10894 var old11 = this.elements[4];
|
rob@100
|
10895 var old12 = this.elements[5];
|
rob@100
|
10896 this.elements[0] = old11 / d;
|
rob@100
|
10897 this.elements[3] = -old10 / d;
|
rob@100
|
10898 this.elements[1] = -old01 / d;
|
rob@100
|
10899 this.elements[4] = old00 / d;
|
rob@100
|
10900 this.elements[2] = (old01 * old12 - old11 * old02) / d;
|
rob@100
|
10901 this.elements[5] = (old10 * old02 - old00 * old12) / d;
|
rob@100
|
10902 return true;
|
rob@100
|
10903 }
|
rob@100
|
10904 return false;
|
rob@100
|
10905 },
|
rob@100
|
10906 /**
|
rob@100
|
10907 * @member PMatrix2D
|
rob@100
|
10908 * The scale() function increases or decreases the size of a shape by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
|
rob@100
|
10909 * This is equivalent to a two parameter call.
|
rob@100
|
10910 *
|
rob@100
|
10911 * @param {float} sx the amount to scale on the x-axis
|
rob@100
|
10912 * @param {float} sy the amount to scale on the y-axis
|
rob@100
|
10913 */
|
rob@100
|
10914 scale: function(sx, sy) {
|
rob@100
|
10915 if (sx && !sy) {
|
rob@100
|
10916 sy = sx;
|
rob@100
|
10917 }
|
rob@100
|
10918 if (sx && sy) {
|
rob@100
|
10919 this.elements[0] *= sx;
|
rob@100
|
10920 this.elements[1] *= sy;
|
rob@100
|
10921 this.elements[3] *= sx;
|
rob@100
|
10922 this.elements[4] *= sy;
|
rob@100
|
10923 }
|
rob@100
|
10924 },
|
rob@100
|
10925 /**
|
rob@100
|
10926 * @member PMatrix2D
|
rob@100
|
10927 * The invScale() function decreases or increases the size of a shape by contracting and expanding vertices. When only one parameter is specified scale will occur in all dimensions.
|
rob@100
|
10928 * This is equivalent to a two parameter call.
|
rob@100
|
10929 *
|
rob@100
|
10930 * @param {float} sx the amount to scale on the x-axis
|
rob@100
|
10931 * @param {float} sy the amount to scale on the y-axis
|
rob@100
|
10932 */
|
rob@100
|
10933 invScale: function(sx, sy) {
|
rob@100
|
10934 if (sx && !sy) {
|
rob@100
|
10935 sy = sx;
|
rob@100
|
10936 }
|
rob@100
|
10937 this.scale(1 / sx, 1 / sy);
|
rob@100
|
10938 },
|
rob@100
|
10939 /**
|
rob@100
|
10940 * @member PMatrix2D
|
rob@100
|
10941 * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix2D or a list of floats can be passed in.
|
rob@100
|
10942 *
|
rob@100
|
10943 * @param {PMatrix2D} matrix the matrix to apply this matrix to
|
rob@100
|
10944 * @param {float} m00 the first element of the matrix
|
rob@100
|
10945 * @param {float} m01 the third element of the matrix
|
rob@100
|
10946 * @param {float} m10 the fourth element of the matrix
|
rob@100
|
10947 * @param {float} m11 the fith element of the matrix
|
rob@100
|
10948 * @param {float} m12 the sixth element of the matrix
|
rob@100
|
10949 */
|
rob@100
|
10950 apply: function() {
|
rob@100
|
10951 var source;
|
rob@100
|
10952 if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
|
rob@100
|
10953 source = arguments[0].array();
|
rob@100
|
10954 } else if (arguments.length === 6) {
|
rob@100
|
10955 source = Array.prototype.slice.call(arguments);
|
rob@100
|
10956 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
rob@100
|
10957 source = arguments[0];
|
rob@100
|
10958 }
|
rob@100
|
10959
|
rob@100
|
10960 var result = [0, 0, this.elements[2],
|
rob@100
|
10961 0, 0, this.elements[5]];
|
rob@100
|
10962 var e = 0;
|
rob@100
|
10963 for (var row = 0; row < 2; row++) {
|
rob@100
|
10964 for (var col = 0; col < 3; col++, e++) {
|
rob@100
|
10965 result[e] += this.elements[row * 3 + 0] * source[col + 0] +
|
rob@100
|
10966 this.elements[row * 3 + 1] * source[col + 3];
|
rob@100
|
10967 }
|
rob@100
|
10968 }
|
rob@100
|
10969 this.elements = result.slice();
|
rob@100
|
10970 },
|
rob@100
|
10971 /**
|
rob@100
|
10972 * @member PMatrix2D
|
rob@100
|
10973 * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix2D or elements of a matrix can be passed in.
|
rob@100
|
10974 *
|
rob@100
|
10975 * @param {PMatrix2D} matrix the matrix to apply this matrix to
|
rob@100
|
10976 * @param {float} m00 the first element of the matrix
|
rob@100
|
10977 * @param {float} m01 the third element of the matrix
|
rob@100
|
10978 * @param {float} m10 the fourth element of the matrix
|
rob@100
|
10979 * @param {float} m11 the fith element of the matrix
|
rob@100
|
10980 * @param {float} m12 the sixth element of the matrix
|
rob@100
|
10981 */
|
rob@100
|
10982 preApply: function() {
|
rob@100
|
10983 var source;
|
rob@100
|
10984 if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
|
rob@100
|
10985 source = arguments[0].array();
|
rob@100
|
10986 } else if (arguments.length === 6) {
|
rob@100
|
10987 source = Array.prototype.slice.call(arguments);
|
rob@100
|
10988 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
rob@100
|
10989 source = arguments[0];
|
rob@100
|
10990 }
|
rob@100
|
10991 var result = [0, 0, source[2],
|
rob@100
|
10992 0, 0, source[5]];
|
rob@100
|
10993 result[2] = source[2] + this.elements[2] * source[0] + this.elements[5] * source[1];
|
rob@100
|
10994 result[5] = source[5] + this.elements[2] * source[3] + this.elements[5] * source[4];
|
rob@100
|
10995 result[0] = this.elements[0] * source[0] + this.elements[3] * source[1];
|
rob@100
|
10996 result[3] = this.elements[0] * source[3] + this.elements[3] * source[4];
|
rob@100
|
10997 result[1] = this.elements[1] * source[0] + this.elements[4] * source[1];
|
rob@100
|
10998 result[4] = this.elements[1] * source[3] + this.elements[4] * source[4];
|
rob@100
|
10999 this.elements = result.slice();
|
rob@100
|
11000 },
|
rob@100
|
11001 /**
|
rob@100
|
11002 * @member PMatrix2D
|
rob@100
|
11003 * The rotate() function rotates the matrix.
|
rob@100
|
11004 *
|
rob@100
|
11005 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
11006 */
|
rob@100
|
11007 rotate: function(angle) {
|
rob@100
|
11008 var c = Math.cos(angle);
|
rob@100
|
11009 var s = Math.sin(angle);
|
rob@100
|
11010 var temp1 = this.elements[0];
|
rob@100
|
11011 var temp2 = this.elements[1];
|
rob@100
|
11012 this.elements[0] = c * temp1 + s * temp2;
|
rob@100
|
11013 this.elements[1] = -s * temp1 + c * temp2;
|
rob@100
|
11014 temp1 = this.elements[3];
|
rob@100
|
11015 temp2 = this.elements[4];
|
rob@100
|
11016 this.elements[3] = c * temp1 + s * temp2;
|
rob@100
|
11017 this.elements[4] = -s * temp1 + c * temp2;
|
rob@100
|
11018 },
|
rob@100
|
11019 /**
|
rob@100
|
11020 * @member PMatrix2D
|
rob@100
|
11021 * The rotateZ() function rotates the matrix.
|
rob@100
|
11022 *
|
rob@100
|
11023 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
11024 */
|
rob@100
|
11025 rotateZ: function(angle) {
|
rob@100
|
11026 this.rotate(angle);
|
rob@100
|
11027 },
|
rob@100
|
11028 /**
|
rob@100
|
11029 * @member PMatrix2D
|
rob@100
|
11030 * The invRotateZ() function rotates the matrix in opposite direction.
|
rob@100
|
11031 *
|
rob@100
|
11032 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
11033 */
|
rob@100
|
11034 invRotateZ: function(angle) {
|
rob@100
|
11035 this.rotateZ(angle - Math.PI);
|
rob@100
|
11036 },
|
rob@100
|
11037 /**
|
rob@100
|
11038 * @member PMatrix2D
|
rob@100
|
11039 * The print() function prints out the elements of this matrix
|
rob@100
|
11040 */
|
rob@100
|
11041 print: function() {
|
rob@100
|
11042 var digits = printMatrixHelper(this.elements);
|
rob@100
|
11043 var output = "" + p.nfs(this.elements[0], digits, 4) + " " +
|
rob@100
|
11044 p.nfs(this.elements[1], digits, 4) + " " +
|
rob@100
|
11045 p.nfs(this.elements[2], digits, 4) + "\n" +
|
rob@100
|
11046 p.nfs(this.elements[3], digits, 4) + " " +
|
rob@100
|
11047 p.nfs(this.elements[4], digits, 4) + " " +
|
rob@100
|
11048 p.nfs(this.elements[5], digits, 4) + "\n\n";
|
rob@100
|
11049 p.println(output);
|
rob@100
|
11050 }
|
rob@100
|
11051 };
|
rob@100
|
11052
|
rob@100
|
11053 /**
|
rob@100
|
11054 * PMatrix3D is a 4x4 matrix implementation. The constructor accepts another PMatrix3D or a list of six or sixteen float elements.
|
rob@100
|
11055 * If no parameters are provided the matrix is set to the identity matrix.
|
rob@100
|
11056 */
|
rob@100
|
11057 var PMatrix3D = p.PMatrix3D = function() {
|
rob@100
|
11058 // When a matrix is created, it is set to an identity matrix
|
rob@100
|
11059 this.reset();
|
rob@100
|
11060 };
|
rob@100
|
11061 /**
|
rob@100
|
11062 * PMatrix3D methods
|
rob@100
|
11063 */
|
rob@100
|
11064 PMatrix3D.prototype = {
|
rob@100
|
11065 /**
|
rob@100
|
11066 * @member PMatrix2D
|
rob@100
|
11067 * The set() function sets the matrix elements. The function accepts either another PMatrix3D, an array of elements, or a list of six or sixteen floats.
|
rob@100
|
11068 *
|
rob@100
|
11069 * @param {PMatrix3D} matrix the initial matrix to set to
|
rob@100
|
11070 * @param {float[]} elements an array of elements to set this matrix to
|
rob@100
|
11071 * @param {float} m00 the first element of the matrix
|
rob@100
|
11072 * @param {float} m01 the second element of the matrix
|
rob@100
|
11073 * @param {float} m02 the third element of the matrix
|
rob@100
|
11074 * @param {float} m03 the fourth element of the matrix
|
rob@100
|
11075 * @param {float} m10 the fifth element of the matrix
|
rob@100
|
11076 * @param {float} m11 the sixth element of the matrix
|
rob@100
|
11077 * @param {float} m12 the seventh element of the matrix
|
rob@100
|
11078 * @param {float} m13 the eight element of the matrix
|
rob@100
|
11079 * @param {float} m20 the nineth element of the matrix
|
rob@100
|
11080 * @param {float} m21 the tenth element of the matrix
|
rob@100
|
11081 * @param {float} m22 the eleventh element of the matrix
|
rob@100
|
11082 * @param {float} m23 the twelveth element of the matrix
|
rob@100
|
11083 * @param {float} m30 the thirteenth element of the matrix
|
rob@100
|
11084 * @param {float} m31 the fourtheenth element of the matrix
|
rob@100
|
11085 * @param {float} m32 the fivetheenth element of the matrix
|
rob@100
|
11086 * @param {float} m33 the sixteenth element of the matrix
|
rob@100
|
11087 */
|
rob@100
|
11088 set: function() {
|
rob@100
|
11089 if (arguments.length === 16) {
|
rob@100
|
11090 this.elements = Array.prototype.slice.call(arguments);
|
rob@100
|
11091 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
|
rob@100
|
11092 this.elements = arguments[0].array();
|
rob@100
|
11093 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
rob@100
|
11094 this.elements = arguments[0].slice();
|
rob@100
|
11095 }
|
rob@100
|
11096 },
|
rob@100
|
11097 /**
|
rob@100
|
11098 * @member PMatrix3D
|
rob@100
|
11099 * The get() function returns a copy of this PMatrix3D.
|
rob@100
|
11100 *
|
rob@100
|
11101 * @return {PMatrix3D} a copy of this PMatrix3D
|
rob@100
|
11102 */
|
rob@100
|
11103 get: function() {
|
rob@100
|
11104 var outgoing = new PMatrix3D();
|
rob@100
|
11105 outgoing.set(this.elements);
|
rob@100
|
11106 return outgoing;
|
rob@100
|
11107 },
|
rob@100
|
11108 /**
|
rob@100
|
11109 * @member PMatrix3D
|
rob@100
|
11110 * The reset() function sets this PMatrix3D to the identity matrix.
|
rob@100
|
11111 */
|
rob@100
|
11112 reset: function() {
|
rob@100
|
11113 this.elements = [1,0,0,0,
|
rob@100
|
11114 0,1,0,0,
|
rob@100
|
11115 0,0,1,0,
|
rob@100
|
11116 0,0,0,1];
|
rob@100
|
11117 },
|
rob@100
|
11118 /**
|
rob@100
|
11119 * @member PMatrix3D
|
rob@100
|
11120 * The array() function returns a copy of the element values.
|
rob@100
|
11121 * @addon
|
rob@100
|
11122 *
|
rob@100
|
11123 * @return {float[]} returns a copy of the element values
|
rob@100
|
11124 */
|
rob@100
|
11125 array: function array() {
|
rob@100
|
11126 return this.elements.slice();
|
rob@100
|
11127 },
|
rob@100
|
11128 /**
|
rob@100
|
11129 * @member PMatrix3D
|
rob@100
|
11130 * The translate() function translates this matrix by moving the current coordinates to the location specified by tx, ty, and tz.
|
rob@100
|
11131 *
|
rob@100
|
11132 * @param {float} tx the x-axis coordinate to move to
|
rob@100
|
11133 * @param {float} ty the y-axis coordinate to move to
|
rob@100
|
11134 * @param {float} tz the z-axis coordinate to move to
|
rob@100
|
11135 */
|
rob@100
|
11136 translate: function(tx, ty, tz) {
|
rob@100
|
11137 if (tz === undef) {
|
rob@100
|
11138 tz = 0;
|
rob@100
|
11139 }
|
rob@100
|
11140
|
rob@100
|
11141 this.elements[3] += tx * this.elements[0] + ty * this.elements[1] + tz * this.elements[2];
|
rob@100
|
11142 this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6];
|
rob@100
|
11143 this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10];
|
rob@100
|
11144 this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14];
|
rob@100
|
11145 },
|
rob@100
|
11146 /**
|
rob@100
|
11147 * @member PMatrix3D
|
rob@100
|
11148 * The transpose() function transpose this matrix.
|
rob@100
|
11149 */
|
rob@100
|
11150 transpose: function() {
|
rob@100
|
11151 var temp = this.elements[4];
|
rob@100
|
11152 this.elements[4] = this.elements[1];
|
rob@100
|
11153 this.elements[1] = temp;
|
rob@100
|
11154
|
rob@100
|
11155 temp = this.elements[8];
|
rob@100
|
11156 this.elements[8] = this.elements[2];
|
rob@100
|
11157 this.elements[2] = temp;
|
rob@100
|
11158
|
rob@100
|
11159 temp = this.elements[6];
|
rob@100
|
11160 this.elements[6] = this.elements[9];
|
rob@100
|
11161 this.elements[9] = temp;
|
rob@100
|
11162
|
rob@100
|
11163 temp = this.elements[3];
|
rob@100
|
11164 this.elements[3] = this.elements[12];
|
rob@100
|
11165 this.elements[12] = temp;
|
rob@100
|
11166
|
rob@100
|
11167 temp = this.elements[7];
|
rob@100
|
11168 this.elements[7] = this.elements[13];
|
rob@100
|
11169 this.elements[13] = temp;
|
rob@100
|
11170
|
rob@100
|
11171 temp = this.elements[11];
|
rob@100
|
11172 this.elements[11] = this.elements[14];
|
rob@100
|
11173 this.elements[14] = temp;
|
rob@100
|
11174 },
|
rob@100
|
11175 /**
|
rob@100
|
11176 * @member PMatrix3D
|
rob@100
|
11177 * The mult() function multiplied this matrix.
|
rob@100
|
11178 * If two array elements are passed in the function will multiply a two element vector against this matrix.
|
rob@100
|
11179 * If target is null or not length four, a new float array will be returned.
|
rob@100
|
11180 * The values for vec and target can be the same (though that's less efficient).
|
rob@100
|
11181 * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
|
rob@100
|
11182 *
|
rob@100
|
11183 * @param {PVector} source, target the PVectors used to multiply this matrix
|
rob@100
|
11184 * @param {float[]} source, target the arrays used to multiply this matrix
|
rob@100
|
11185 *
|
rob@100
|
11186 * @return {PVector|float[]} returns a PVector or an array representing the new matrix
|
rob@100
|
11187 */
|
rob@100
|
11188 mult: function(source, target) {
|
rob@100
|
11189 var x, y, z, w;
|
rob@100
|
11190 if (source instanceof PVector) {
|
rob@100
|
11191 x = source.x;
|
rob@100
|
11192 y = source.y;
|
rob@100
|
11193 z = source.z;
|
rob@100
|
11194 w = 1;
|
rob@100
|
11195 if (!target) {
|
rob@100
|
11196 target = new PVector();
|
rob@100
|
11197 }
|
rob@100
|
11198 } else if (source instanceof Array) {
|
rob@100
|
11199 x = source[0];
|
rob@100
|
11200 y = source[1];
|
rob@100
|
11201 z = source[2];
|
rob@100
|
11202 w = source[3] || 1;
|
rob@100
|
11203
|
rob@100
|
11204 if ( !target || (target.length !== 3 && target.length !== 4) ) {
|
rob@100
|
11205 target = [0, 0, 0];
|
rob@100
|
11206 }
|
rob@100
|
11207 }
|
rob@100
|
11208
|
rob@100
|
11209 if (target instanceof Array) {
|
rob@100
|
11210 if (target.length === 3) {
|
rob@100
|
11211 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
|
rob@100
|
11212 target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
|
rob@100
|
11213 target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
|
rob@100
|
11214 } else if (target.length === 4) {
|
rob@100
|
11215 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
|
rob@100
|
11216 target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
|
rob@100
|
11217 target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
|
rob@100
|
11218 target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
|
rob@100
|
11219 }
|
rob@100
|
11220 }
|
rob@100
|
11221 if (target instanceof PVector) {
|
rob@100
|
11222 target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
|
rob@100
|
11223 target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
|
rob@100
|
11224 target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
|
rob@100
|
11225 }
|
rob@100
|
11226 return target;
|
rob@100
|
11227 },
|
rob@100
|
11228 /**
|
rob@100
|
11229 * @member PMatrix3D
|
rob@100
|
11230 * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix3D or elements of a matrix can be passed in.
|
rob@100
|
11231 *
|
rob@100
|
11232 * @param {PMatrix3D} matrix the matrix to apply this matrix to
|
rob@100
|
11233 * @param {float} m00 the first element of the matrix
|
rob@100
|
11234 * @param {float} m01 the second element of the matrix
|
rob@100
|
11235 * @param {float} m02 the third element of the matrix
|
rob@100
|
11236 * @param {float} m03 the fourth element of the matrix
|
rob@100
|
11237 * @param {float} m10 the fifth element of the matrix
|
rob@100
|
11238 * @param {float} m11 the sixth element of the matrix
|
rob@100
|
11239 * @param {float} m12 the seventh element of the matrix
|
rob@100
|
11240 * @param {float} m13 the eight element of the matrix
|
rob@100
|
11241 * @param {float} m20 the nineth element of the matrix
|
rob@100
|
11242 * @param {float} m21 the tenth element of the matrix
|
rob@100
|
11243 * @param {float} m22 the eleventh element of the matrix
|
rob@100
|
11244 * @param {float} m23 the twelveth element of the matrix
|
rob@100
|
11245 * @param {float} m30 the thirteenth element of the matrix
|
rob@100
|
11246 * @param {float} m31 the fourtheenth element of the matrix
|
rob@100
|
11247 * @param {float} m32 the fivetheenth element of the matrix
|
rob@100
|
11248 * @param {float} m33 the sixteenth element of the matrix
|
rob@100
|
11249 */
|
rob@100
|
11250 preApply: function() {
|
rob@100
|
11251 var source;
|
rob@100
|
11252 if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
|
rob@100
|
11253 source = arguments[0].array();
|
rob@100
|
11254 } else if (arguments.length === 16) {
|
rob@100
|
11255 source = Array.prototype.slice.call(arguments);
|
rob@100
|
11256 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
rob@100
|
11257 source = arguments[0];
|
rob@100
|
11258 }
|
rob@100
|
11259
|
rob@100
|
11260 var result = [0, 0, 0, 0,
|
rob@100
|
11261 0, 0, 0, 0,
|
rob@100
|
11262 0, 0, 0, 0,
|
rob@100
|
11263 0, 0, 0, 0];
|
rob@100
|
11264 var e = 0;
|
rob@100
|
11265 for (var row = 0; row < 4; row++) {
|
rob@100
|
11266 for (var col = 0; col < 4; col++, e++) {
|
rob@100
|
11267 result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] *
|
rob@100
|
11268 source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] +
|
rob@100
|
11269 this.elements[col + 12] * source[row * 4 + 3];
|
rob@100
|
11270 }
|
rob@100
|
11271 }
|
rob@100
|
11272 this.elements = result.slice();
|
rob@100
|
11273 },
|
rob@100
|
11274 /**
|
rob@100
|
11275 * @member PMatrix3D
|
rob@100
|
11276 * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix3D or a list of floats can be passed in.
|
rob@100
|
11277 *
|
rob@100
|
11278 * @param {PMatrix3D} matrix the matrix to apply this matrix to
|
rob@100
|
11279 * @param {float} m00 the first element of the matrix
|
rob@100
|
11280 * @param {float} m01 the second element of the matrix
|
rob@100
|
11281 * @param {float} m02 the third element of the matrix
|
rob@100
|
11282 * @param {float} m03 the fourth element of the matrix
|
rob@100
|
11283 * @param {float} m10 the fifth element of the matrix
|
rob@100
|
11284 * @param {float} m11 the sixth element of the matrix
|
rob@100
|
11285 * @param {float} m12 the seventh element of the matrix
|
rob@100
|
11286 * @param {float} m13 the eight element of the matrix
|
rob@100
|
11287 * @param {float} m20 the nineth element of the matrix
|
rob@100
|
11288 * @param {float} m21 the tenth element of the matrix
|
rob@100
|
11289 * @param {float} m22 the eleventh element of the matrix
|
rob@100
|
11290 * @param {float} m23 the twelveth element of the matrix
|
rob@100
|
11291 * @param {float} m30 the thirteenth element of the matrix
|
rob@100
|
11292 * @param {float} m31 the fourtheenth element of the matrix
|
rob@100
|
11293 * @param {float} m32 the fivetheenth element of the matrix
|
rob@100
|
11294 * @param {float} m33 the sixteenth element of the matrix
|
rob@100
|
11295 */
|
rob@100
|
11296 apply: function() {
|
rob@100
|
11297 var source;
|
rob@100
|
11298 if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
|
rob@100
|
11299 source = arguments[0].array();
|
rob@100
|
11300 } else if (arguments.length === 16) {
|
rob@100
|
11301 source = Array.prototype.slice.call(arguments);
|
rob@100
|
11302 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
rob@100
|
11303 source = arguments[0];
|
rob@100
|
11304 }
|
rob@100
|
11305
|
rob@100
|
11306 var result = [0, 0, 0, 0,
|
rob@100
|
11307 0, 0, 0, 0,
|
rob@100
|
11308 0, 0, 0, 0,
|
rob@100
|
11309 0, 0, 0, 0];
|
rob@100
|
11310 var e = 0;
|
rob@100
|
11311 for (var row = 0; row < 4; row++) {
|
rob@100
|
11312 for (var col = 0; col < 4; col++, e++) {
|
rob@100
|
11313 result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] *
|
rob@100
|
11314 source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] +
|
rob@100
|
11315 this.elements[row * 4 + 3] * source[col + 12];
|
rob@100
|
11316 }
|
rob@100
|
11317 }
|
rob@100
|
11318 this.elements = result.slice();
|
rob@100
|
11319 },
|
rob@100
|
11320 /**
|
rob@100
|
11321 * @member PMatrix3D
|
rob@100
|
11322 * The rotate() function rotates the matrix.
|
rob@100
|
11323 *
|
rob@100
|
11324 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
11325 */
|
rob@100
|
11326 rotate: function(angle, v0, v1, v2) {
|
rob@100
|
11327 if (arguments.length < 4) {
|
rob@100
|
11328 this.rotateZ(angle);
|
rob@100
|
11329 } else {
|
rob@100
|
11330 var v = new PVector(v0, v1, v2);
|
rob@100
|
11331 var m = v.mag();
|
rob@100
|
11332 if (m === 0) {
|
rob@100
|
11333 return;
|
rob@100
|
11334 } else if (m != 1) {
|
rob@100
|
11335 v.normalize();
|
rob@100
|
11336 v0 = v.x;
|
rob@100
|
11337 v1 = v.y;
|
rob@100
|
11338 v2 = v.z;
|
rob@100
|
11339 }
|
rob@100
|
11340 var c = p.cos(angle);
|
rob@100
|
11341 var s = p.sin(angle);
|
rob@100
|
11342 var t = 1.0 - c;
|
rob@100
|
11343
|
rob@100
|
11344 this.apply((t * v0 * v0) + c,
|
rob@100
|
11345 (t * v0 * v1) - (s * v2),
|
rob@100
|
11346 (t * v0 * v2) + (s * v1),
|
rob@100
|
11347 0,
|
rob@100
|
11348 (t * v0 * v1) + (s * v2),
|
rob@100
|
11349 (t * v1 * v1) + c,
|
rob@100
|
11350 (t * v1 * v2) - (s * v0),
|
rob@100
|
11351 0,
|
rob@100
|
11352 (t * v0 * v2) - (s * v1),
|
rob@100
|
11353 (t * v1 * v2) + (s * v0),
|
rob@100
|
11354 (t * v2 * v2) + c,
|
rob@100
|
11355 0,
|
rob@100
|
11356 0, 0, 0, 1);
|
rob@100
|
11357 }
|
rob@100
|
11358 },
|
rob@100
|
11359 /**
|
rob@100
|
11360 * @member PMatrix3D
|
rob@100
|
11361 * The invApply() function applies the inverted matrix to this matrix.
|
rob@100
|
11362 *
|
rob@100
|
11363 * @param {float} m00 the first element of the matrix
|
rob@100
|
11364 * @param {float} m01 the second element of the matrix
|
rob@100
|
11365 * @param {float} m02 the third element of the matrix
|
rob@100
|
11366 * @param {float} m03 the fourth element of the matrix
|
rob@100
|
11367 * @param {float} m10 the fifth element of the matrix
|
rob@100
|
11368 * @param {float} m11 the sixth element of the matrix
|
rob@100
|
11369 * @param {float} m12 the seventh element of the matrix
|
rob@100
|
11370 * @param {float} m13 the eight element of the matrix
|
rob@100
|
11371 * @param {float} m20 the nineth element of the matrix
|
rob@100
|
11372 * @param {float} m21 the tenth element of the matrix
|
rob@100
|
11373 * @param {float} m22 the eleventh element of the matrix
|
rob@100
|
11374 * @param {float} m23 the twelveth element of the matrix
|
rob@100
|
11375 * @param {float} m30 the thirteenth element of the matrix
|
rob@100
|
11376 * @param {float} m31 the fourtheenth element of the matrix
|
rob@100
|
11377 * @param {float} m32 the fivetheenth element of the matrix
|
rob@100
|
11378 * @param {float} m33 the sixteenth element of the matrix
|
rob@100
|
11379 *
|
rob@100
|
11380 * @return {boolean} returns true if the operation was successful.
|
rob@100
|
11381 */
|
rob@100
|
11382 invApply: function() {
|
rob@100
|
11383 if (inverseCopy === undef) {
|
rob@100
|
11384 inverseCopy = new PMatrix3D();
|
rob@100
|
11385 }
|
rob@100
|
11386 var a = arguments;
|
rob@100
|
11387 inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8],
|
rob@100
|
11388 a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
|
rob@100
|
11389
|
rob@100
|
11390 if (!inverseCopy.invert()) {
|
rob@100
|
11391 return false;
|
rob@100
|
11392 }
|
rob@100
|
11393 this.preApply(inverseCopy);
|
rob@100
|
11394 return true;
|
rob@100
|
11395 },
|
rob@100
|
11396 /**
|
rob@100
|
11397 * @member PMatrix3D
|
rob@100
|
11398 * The rotateZ() function rotates the matrix.
|
rob@100
|
11399 *
|
rob@100
|
11400 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
11401 */
|
rob@100
|
11402 rotateX: function(angle) {
|
rob@100
|
11403 var c = p.cos(angle);
|
rob@100
|
11404 var s = p.sin(angle);
|
rob@100
|
11405 this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
|
rob@100
|
11406 },
|
rob@100
|
11407 /**
|
rob@100
|
11408 * @member PMatrix3D
|
rob@100
|
11409 * The rotateY() function rotates the matrix.
|
rob@100
|
11410 *
|
rob@100
|
11411 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
11412 */
|
rob@100
|
11413 rotateY: function(angle) {
|
rob@100
|
11414 var c = p.cos(angle);
|
rob@100
|
11415 var s = p.sin(angle);
|
rob@100
|
11416 this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
|
rob@100
|
11417 },
|
rob@100
|
11418 /**
|
rob@100
|
11419 * @member PMatrix3D
|
rob@100
|
11420 * The rotateZ() function rotates the matrix.
|
rob@100
|
11421 *
|
rob@100
|
11422 * @param {float} angle the angle of rotation in radiants
|
rob@100
|
11423 */
|
rob@100
|
11424 rotateZ: function(angle) {
|
rob@100
|
11425 var c = Math.cos(angle);
|
rob@100
|
11426 var s = Math.sin(angle);
|
rob@100
|
11427 this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
rob@100
|
11428 },
|
rob@100
|
11429 /**
|
rob@100
|
11430 * @member PMatrix3D
|
rob@100
|
11431 * The scale() function increases or decreases the size of a matrix by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
|
rob@100
|
11432 * This is equivalent to a three parameter call.
|
rob@100
|
11433 *
|
rob@100
|
11434 * @param {float} sx the amount to scale on the x-axis
|
rob@100
|
11435 * @param {float} sy the amount to scale on the y-axis
|
rob@100
|
11436 * @param {float} sz the amount to scale on the z-axis
|
rob@100
|
11437 */
|
rob@100
|
11438 scale: function(sx, sy, sz) {
|
rob@100
|
11439 if (sx && !sy && !sz) {
|
rob@100
|
11440 sy = sz = sx;
|
rob@100
|
11441 } else if (sx && sy && !sz) {
|
rob@100
|
11442 sz = 1;
|
rob@100
|
11443 }
|
rob@100
|
11444
|
rob@100
|
11445 if (sx && sy && sz) {
|
rob@100
|
11446 this.elements[0] *= sx;
|
rob@100
|
11447 this.elements[1] *= sy;
|
rob@100
|
11448 this.elements[2] *= sz;
|
rob@100
|
11449 this.elements[4] *= sx;
|
rob@100
|
11450 this.elements[5] *= sy;
|
rob@100
|
11451 this.elements[6] *= sz;
|
rob@100
|
11452 this.elements[8] *= sx;
|
rob@100
|
11453 this.elements[9] *= sy;
|
rob@100
|
11454 this.elements[10] *= sz;
|
rob@100
|
11455 this.elements[12] *= sx;
|
rob@100
|
11456 this.elements[13] *= sy;
|
rob@100
|
11457 this.elements[14] *= sz;
|
rob@100
|
11458 }
|
rob@100
|
11459 },
|
rob@100
|
11460 /**
|
rob@100
|
11461 * @member PMatrix3D
|
rob@100
|
11462 * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
|
rob@100
|
11463 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
11464 *
|
rob@100
|
11465 * @param {float} angle angle of skew specified in radians
|
rob@100
|
11466 */
|
rob@100
|
11467 skewX: function(angle) {
|
rob@100
|
11468 var t = Math.tan(angle);
|
rob@100
|
11469 this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
rob@100
|
11470 },
|
rob@100
|
11471 /**
|
rob@100
|
11472 * @member PMatrix3D
|
rob@100
|
11473 * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
|
rob@100
|
11474 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
11475 *
|
rob@100
|
11476 * @param {float} angle angle of skew specified in radians
|
rob@100
|
11477 */
|
rob@100
|
11478 skewY: function(angle) {
|
rob@100
|
11479 var t = Math.tan(angle);
|
rob@100
|
11480 this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
rob@100
|
11481 },
|
rob@100
|
11482 /**
|
rob@100
|
11483 * @member PMatrix3D
|
rob@100
|
11484 * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter.
|
rob@100
|
11485 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
11486 *
|
rob@100
|
11487 * @param {float} angle angle of shear specified in radians
|
rob@100
|
11488 */
|
rob@100
|
11489 shearX: function(angle) {
|
rob@100
|
11490 var t = Math.tan(angle);
|
rob@100
|
11491 this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
rob@100
|
11492 },
|
rob@100
|
11493 /**
|
rob@100
|
11494 * @member PMatrix3D
|
rob@100
|
11495 * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter.
|
rob@100
|
11496 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
rob@100
|
11497 *
|
rob@100
|
11498 * @param {float} angle angle of shear specified in radians
|
rob@100
|
11499 */
|
rob@100
|
11500 shearY: function(angle) {
|
rob@100
|
11501 var t = Math.tan(angle);
|
rob@100
|
11502 this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
rob@100
|
11503 },
|
rob@100
|
11504 multX: function(x, y, z, w) {
|
rob@100
|
11505 if (!z) {
|
rob@100
|
11506 return this.elements[0] * x + this.elements[1] * y + this.elements[3];
|
rob@100
|
11507 }
|
rob@100
|
11508 if (!w) {
|
rob@100
|
11509 return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
|
rob@100
|
11510 }
|
rob@100
|
11511 return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
|
rob@100
|
11512 },
|
rob@100
|
11513 multY: function(x, y, z, w) {
|
rob@100
|
11514 if (!z) {
|
rob@100
|
11515 return this.elements[4] * x + this.elements[5] * y + this.elements[7];
|
rob@100
|
11516 }
|
rob@100
|
11517 if (!w) {
|
rob@100
|
11518 return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
|
rob@100
|
11519 }
|
rob@100
|
11520 return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
|
rob@100
|
11521 },
|
rob@100
|
11522 multZ: function(x, y, z, w) {
|
rob@100
|
11523 if (!w) {
|
rob@100
|
11524 return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
|
rob@100
|
11525 }
|
rob@100
|
11526 return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
|
rob@100
|
11527 },
|
rob@100
|
11528 multW: function(x, y, z, w) {
|
rob@100
|
11529 if (!w) {
|
rob@100
|
11530 return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15];
|
rob@100
|
11531 }
|
rob@100
|
11532 return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
|
rob@100
|
11533 },
|
rob@100
|
11534 /**
|
rob@100
|
11535 * @member PMatrix3D
|
rob@100
|
11536 * The invert() function inverts this matrix
|
rob@100
|
11537 *
|
rob@100
|
11538 * @return {boolean} true if successful
|
rob@100
|
11539 */
|
rob@100
|
11540 invert: function() {
|
rob@100
|
11541 var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4];
|
rob@100
|
11542 var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4];
|
rob@100
|
11543 var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4];
|
rob@100
|
11544 var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5];
|
rob@100
|
11545 var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5];
|
rob@100
|
11546 var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6];
|
rob@100
|
11547 var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12];
|
rob@100
|
11548 var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12];
|
rob@100
|
11549 var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12];
|
rob@100
|
11550 var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13];
|
rob@100
|
11551 var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13];
|
rob@100
|
11552 var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14];
|
rob@100
|
11553
|
rob@100
|
11554 // Determinant
|
rob@100
|
11555 var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
|
rob@100
|
11556
|
rob@100
|
11557 // Account for a very small value
|
rob@100
|
11558 // return false if not successful.
|
rob@100
|
11559 if (Math.abs(fDet) <= 1e-9) {
|
rob@100
|
11560 return false;
|
rob@100
|
11561 }
|
rob@100
|
11562
|
rob@100
|
11563 var kInv = [];
|
rob@100
|
11564 kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3;
|
rob@100
|
11565 kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1;
|
rob@100
|
11566 kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0;
|
rob@100
|
11567 kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0;
|
rob@100
|
11568 kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3;
|
rob@100
|
11569 kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1;
|
rob@100
|
11570 kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0;
|
rob@100
|
11571 kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0;
|
rob@100
|
11572 kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3;
|
rob@100
|
11573 kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1;
|
rob@100
|
11574 kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0;
|
rob@100
|
11575 kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0;
|
rob@100
|
11576 kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3;
|
rob@100
|
11577 kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1;
|
rob@100
|
11578 kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0;
|
rob@100
|
11579 kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0;
|
rob@100
|
11580
|
rob@100
|
11581 // Inverse using Determinant
|
rob@100
|
11582 var fInvDet = 1.0 / fDet;
|
rob@100
|
11583 kInv[0] *= fInvDet;
|
rob@100
|
11584 kInv[1] *= fInvDet;
|
rob@100
|
11585 kInv[2] *= fInvDet;
|
rob@100
|
11586 kInv[3] *= fInvDet;
|
rob@100
|
11587 kInv[4] *= fInvDet;
|
rob@100
|
11588 kInv[5] *= fInvDet;
|
rob@100
|
11589 kInv[6] *= fInvDet;
|
rob@100
|
11590 kInv[7] *= fInvDet;
|
rob@100
|
11591 kInv[8] *= fInvDet;
|
rob@100
|
11592 kInv[9] *= fInvDet;
|
rob@100
|
11593 kInv[10] *= fInvDet;
|
rob@100
|
11594 kInv[11] *= fInvDet;
|
rob@100
|
11595 kInv[12] *= fInvDet;
|
rob@100
|
11596 kInv[13] *= fInvDet;
|
rob@100
|
11597 kInv[14] *= fInvDet;
|
rob@100
|
11598 kInv[15] *= fInvDet;
|
rob@100
|
11599
|
rob@100
|
11600 this.elements = kInv.slice();
|
rob@100
|
11601 return true;
|
rob@100
|
11602 },
|
rob@100
|
11603 toString: function() {
|
rob@100
|
11604 var str = "";
|
rob@100
|
11605 for (var i = 0; i < 15; i++) {
|
rob@100
|
11606 str += this.elements[i] + ", ";
|
rob@100
|
11607 }
|
rob@100
|
11608 str += this.elements[15];
|
rob@100
|
11609 return str;
|
rob@100
|
11610 },
|
rob@100
|
11611 /**
|
rob@100
|
11612 * @member PMatrix3D
|
rob@100
|
11613 * The print() function prints out the elements of this matrix
|
rob@100
|
11614 */
|
rob@100
|
11615 print: function() {
|
rob@100
|
11616 var digits = printMatrixHelper(this.elements);
|
rob@100
|
11617
|
rob@100
|
11618 var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) +
|
rob@100
|
11619 " " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) +
|
rob@100
|
11620 "\n" + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) +
|
rob@100
|
11621 " " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) +
|
rob@100
|
11622 "\n" + p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) +
|
rob@100
|
11623 " " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) +
|
rob@100
|
11624 "\n" + p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) +
|
rob@100
|
11625 " " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n";
|
rob@100
|
11626 p.println(output);
|
rob@100
|
11627 },
|
rob@100
|
11628 invTranslate: function(tx, ty, tz) {
|
rob@100
|
11629 this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1);
|
rob@100
|
11630 },
|
rob@100
|
11631 invRotateX: function(angle) {
|
rob@100
|
11632 var c = Math.cos(-angle);
|
rob@100
|
11633 var s = Math.sin(-angle);
|
rob@100
|
11634 this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
|
rob@100
|
11635 },
|
rob@100
|
11636 invRotateY: function(angle) {
|
rob@100
|
11637 var c = Math.cos(-angle);
|
rob@100
|
11638 var s = Math.sin(-angle);
|
rob@100
|
11639 this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
|
rob@100
|
11640 },
|
rob@100
|
11641 invRotateZ: function(angle) {
|
rob@100
|
11642 var c = Math.cos(-angle);
|
rob@100
|
11643 var s = Math.sin(-angle);
|
rob@100
|
11644 this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
rob@100
|
11645 },
|
rob@100
|
11646 invScale: function(x, y, z) {
|
rob@100
|
11647 this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]);
|
rob@100
|
11648 }
|
rob@100
|
11649 };
|
rob@100
|
11650
|
rob@100
|
11651 /**
|
rob@100
|
11652 * @private
|
rob@100
|
11653 * The matrix stack stores the transformations and translations that occur within the space.
|
rob@100
|
11654 */
|
rob@100
|
11655 var PMatrixStack = p.PMatrixStack = function() {
|
rob@100
|
11656 this.matrixStack = [];
|
rob@100
|
11657 };
|
rob@100
|
11658
|
rob@100
|
11659 /**
|
rob@100
|
11660 * @member PMatrixStack
|
rob@100
|
11661 * load pushes the matrix given in the function into the stack
|
rob@100
|
11662 *
|
rob@100
|
11663 * @param {Object | Array} matrix the matrix to be pushed into the stack
|
rob@100
|
11664 */
|
rob@100
|
11665 PMatrixStack.prototype.load = function() {
|
rob@100
|
11666 var tmpMatrix = drawing.$newPMatrix();
|
rob@100
|
11667
|
rob@100
|
11668 if (arguments.length === 1) {
|
rob@100
|
11669 tmpMatrix.set(arguments[0]);
|
rob@100
|
11670 } else {
|
rob@100
|
11671 tmpMatrix.set(arguments);
|
rob@100
|
11672 }
|
rob@100
|
11673 this.matrixStack.push(tmpMatrix);
|
rob@100
|
11674 };
|
rob@100
|
11675
|
rob@100
|
11676 Drawing2D.prototype.$newPMatrix = function() {
|
rob@100
|
11677 return new PMatrix2D();
|
rob@100
|
11678 };
|
rob@100
|
11679
|
rob@100
|
11680 Drawing3D.prototype.$newPMatrix = function() {
|
rob@100
|
11681 return new PMatrix3D();
|
rob@100
|
11682 };
|
rob@100
|
11683
|
rob@100
|
11684 /**
|
rob@100
|
11685 * @member PMatrixStack
|
rob@100
|
11686 * push adds a duplicate of the top of the stack onto the stack - uses the peek function
|
rob@100
|
11687 */
|
rob@100
|
11688 PMatrixStack.prototype.push = function() {
|
rob@100
|
11689 this.matrixStack.push(this.peek());
|
rob@100
|
11690 };
|
rob@100
|
11691
|
rob@100
|
11692 /**
|
rob@100
|
11693 * @member PMatrixStack
|
rob@100
|
11694 * pop removes returns the matrix at the top of the stack
|
rob@100
|
11695 *
|
rob@100
|
11696 * @returns {Object} the matrix at the top of the stack
|
rob@100
|
11697 */
|
rob@100
|
11698 PMatrixStack.prototype.pop = function() {
|
rob@100
|
11699 return this.matrixStack.pop();
|
rob@100
|
11700 };
|
rob@100
|
11701
|
rob@100
|
11702 /**
|
rob@100
|
11703 * @member PMatrixStack
|
rob@100
|
11704 * peek returns but doesn't remove the matrix at the top of the stack
|
rob@100
|
11705 *
|
rob@100
|
11706 * @returns {Object} the matrix at the top of the stack
|
rob@100
|
11707 */
|
rob@100
|
11708 PMatrixStack.prototype.peek = function() {
|
rob@100
|
11709 var tmpMatrix = drawing.$newPMatrix();
|
rob@100
|
11710
|
rob@100
|
11711 tmpMatrix.set(this.matrixStack[this.matrixStack.length - 1]);
|
rob@100
|
11712 return tmpMatrix;
|
rob@100
|
11713 };
|
rob@100
|
11714
|
rob@100
|
11715 /**
|
rob@100
|
11716 * @member PMatrixStack
|
rob@100
|
11717 * this function multiplies the matrix at the top of the stack with the matrix given as a parameter
|
rob@100
|
11718 *
|
rob@100
|
11719 * @param {Object | Array} matrix the matrix to be multiplied into the stack
|
rob@100
|
11720 */
|
rob@100
|
11721 PMatrixStack.prototype.mult = function(matrix) {
|
rob@100
|
11722 this.matrixStack[this.matrixStack.length - 1].apply(matrix);
|
rob@100
|
11723 };
|
rob@100
|
11724
|
rob@100
|
11725 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
11726 // Array handling
|
rob@100
|
11727 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
11728
|
rob@100
|
11729 /**
|
rob@100
|
11730 * The split() function breaks a string into pieces using a character or string
|
rob@100
|
11731 * as the divider. The delim parameter specifies the character or characters that
|
rob@100
|
11732 * mark the boundaries between each piece. A String[] array is returned that contains
|
rob@100
|
11733 * each of the pieces.
|
rob@100
|
11734 * If the result is a set of numbers, you can convert the String[] array to to a float[]
|
rob@100
|
11735 * or int[] array using the datatype conversion functions int() and float() (see example above).
|
rob@100
|
11736 * The splitTokens() function works in a similar fashion, except that it splits using a range
|
rob@100
|
11737 * of characters instead of a specific character or sequence.
|
rob@100
|
11738 *
|
rob@100
|
11739 * @param {String} str the String to be split
|
rob@100
|
11740 * @param {String} delim the character or String used to separate the data
|
rob@100
|
11741 *
|
rob@100
|
11742 * @returns {string[]} The new string array
|
rob@100
|
11743 *
|
rob@100
|
11744 * @see splitTokens
|
rob@100
|
11745 * @see join
|
rob@100
|
11746 * @see trim
|
rob@100
|
11747 */
|
rob@100
|
11748 p.split = function(str, delim) {
|
rob@100
|
11749 return str.split(delim);
|
rob@100
|
11750 };
|
rob@100
|
11751
|
rob@100
|
11752 /**
|
rob@100
|
11753 * The splitTokens() function splits a String at one or many character "tokens." The tokens
|
rob@100
|
11754 * parameter specifies the character or characters to be used as a boundary.
|
rob@100
|
11755 * If no tokens character is specified, any whitespace character is used to split.
|
rob@100
|
11756 * Whitespace characters include tab (\t), line feed (\n), carriage return (\r), form
|
rob@100
|
11757 * feed (\f), and space. To convert a String to an array of integers or floats, use the
|
rob@100
|
11758 * datatype conversion functions int() and float() to convert the array of Strings.
|
rob@100
|
11759 *
|
rob@100
|
11760 * @param {String} str the String to be split
|
rob@100
|
11761 * @param {Char[]} tokens list of individual characters that will be used as separators
|
rob@100
|
11762 *
|
rob@100
|
11763 * @returns {string[]} The new string array
|
rob@100
|
11764 *
|
rob@100
|
11765 * @see split
|
rob@100
|
11766 * @see join
|
rob@100
|
11767 * @see trim
|
rob@100
|
11768 */
|
rob@100
|
11769 p.splitTokens = function(str, tokens) {
|
rob@100
|
11770 if (tokens === undef) {
|
rob@100
|
11771 return str.split(/\s+/g);
|
rob@100
|
11772 }
|
rob@100
|
11773
|
rob@100
|
11774 var chars = tokens.split(/()/g),
|
rob@100
|
11775 buffer = "",
|
rob@100
|
11776 len = str.length,
|
rob@100
|
11777 i, c,
|
rob@100
|
11778 tokenized = [];
|
rob@100
|
11779
|
rob@100
|
11780 for (i = 0; i < len; i++) {
|
rob@100
|
11781 c = str[i];
|
rob@100
|
11782 if (chars.indexOf(c) > -1) {
|
rob@100
|
11783 if (buffer !== "") {
|
rob@100
|
11784 tokenized.push(buffer);
|
rob@100
|
11785 }
|
rob@100
|
11786 buffer = "";
|
rob@100
|
11787 } else {
|
rob@100
|
11788 buffer += c;
|
rob@100
|
11789 }
|
rob@100
|
11790 }
|
rob@100
|
11791
|
rob@100
|
11792 if (buffer !== "") {
|
rob@100
|
11793 tokenized.push(buffer);
|
rob@100
|
11794 }
|
rob@100
|
11795
|
rob@100
|
11796 return tokenized;
|
rob@100
|
11797 };
|
rob@100
|
11798
|
rob@100
|
11799 /**
|
rob@100
|
11800 * Expands an array by one element and adds data to the new position. The datatype of
|
rob@100
|
11801 * the element parameter must be the same as the datatype of the array.
|
rob@100
|
11802 * When using an array of objects, the data returned from the function must be cast to
|
rob@100
|
11803 * the object array's data type. For example: SomeClass[] items = (SomeClass[])
|
rob@100
|
11804 * append(originalArray, element).
|
rob@100
|
11805 *
|
rob@100
|
11806 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
|
rob@100
|
11807 * byte[], char[], int[], float[], or String[], or an array of objects
|
rob@100
|
11808 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} element new data for the array
|
rob@100
|
11809 *
|
rob@100
|
11810 * @returns Array (the same datatype as the input)
|
rob@100
|
11811 *
|
rob@100
|
11812 * @see shorten
|
rob@100
|
11813 * @see expand
|
rob@100
|
11814 */
|
rob@100
|
11815 p.append = function(array, element) {
|
rob@100
|
11816 array[array.length] = element;
|
rob@100
|
11817 return array;
|
rob@100
|
11818 };
|
rob@100
|
11819
|
rob@100
|
11820 /**
|
rob@100
|
11821 * Concatenates two arrays. For example, concatenating the array { 1, 2, 3 } and the
|
rob@100
|
11822 * array { 4, 5, 6 } yields { 1, 2, 3, 4, 5, 6 }. Both parameters must be arrays of the
|
rob@100
|
11823 * same datatype.
|
rob@100
|
11824 * When using an array of objects, the data returned from the function must be cast to the
|
rob@100
|
11825 * object array's data type. For example: SomeClass[] items = (SomeClass[]) concat(array1, array2).
|
rob@100
|
11826 *
|
rob@100
|
11827 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array1 boolean[],
|
rob@100
|
11828 * byte[], char[], int[], float[], String[], or an array of objects
|
rob@100
|
11829 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array2 boolean[],
|
rob@100
|
11830 * byte[], char[], int[], float[], String[], or an array of objects
|
rob@100
|
11831 *
|
rob@100
|
11832 * @returns Array (the same datatype as the input)
|
rob@100
|
11833 *
|
rob@100
|
11834 * @see splice
|
rob@100
|
11835 */
|
rob@100
|
11836 p.concat = function(array1, array2) {
|
rob@100
|
11837 return array1.concat(array2);
|
rob@100
|
11838 };
|
rob@100
|
11839
|
rob@100
|
11840 /**
|
rob@100
|
11841 * Sorts an array of numbers from smallest to largest and puts an array of
|
rob@100
|
11842 * words in alphabetical order. The original array is not modified, a
|
rob@100
|
11843 * re-ordered array is returned. The count parameter states the number of
|
rob@100
|
11844 * elements to sort. For example if there are 12 elements in an array and
|
rob@100
|
11845 * if count is the value 5, only the first five elements on the array will
|
rob@100
|
11846 * be sorted. Alphabetical ordering is case insensitive.
|
rob@100
|
11847 *
|
rob@100
|
11848 * @param {String[] | int[] | float[]} array Array of elements to sort
|
rob@100
|
11849 * @param {int} numElem Number of elements to sort
|
rob@100
|
11850 *
|
rob@100
|
11851 * @returns {String[] | int[] | float[]} Array (same datatype as the input)
|
rob@100
|
11852 *
|
rob@100
|
11853 * @see reverse
|
rob@100
|
11854 */
|
rob@100
|
11855 p.sort = function(array, numElem) {
|
rob@100
|
11856 var ret = [];
|
rob@100
|
11857
|
rob@100
|
11858 // depending on the type used (int, float) or string
|
rob@100
|
11859 // we'll need to use a different compare function
|
rob@100
|
11860 if (array.length > 0) {
|
rob@100
|
11861 // copy since we need to return another array
|
rob@100
|
11862 var elemsToCopy = numElem > 0 ? numElem : array.length;
|
rob@100
|
11863 for (var i = 0; i < elemsToCopy; i++) {
|
rob@100
|
11864 ret.push(array[i]);
|
rob@100
|
11865 }
|
rob@100
|
11866 if (typeof array[0] === "string") {
|
rob@100
|
11867 ret.sort();
|
rob@100
|
11868 }
|
rob@100
|
11869 // int or float
|
rob@100
|
11870 else {
|
rob@100
|
11871 ret.sort(function(a, b) {
|
rob@100
|
11872 return a - b;
|
rob@100
|
11873 });
|
rob@100
|
11874 }
|
rob@100
|
11875
|
rob@100
|
11876 // copy on the rest of the elements that were not sorted in case the user
|
rob@100
|
11877 // only wanted a subset of an array to be sorted.
|
rob@100
|
11878 if (numElem > 0) {
|
rob@100
|
11879 for (var j = ret.length; j < array.length; j++) {
|
rob@100
|
11880 ret.push(array[j]);
|
rob@100
|
11881 }
|
rob@100
|
11882 }
|
rob@100
|
11883 }
|
rob@100
|
11884 return ret;
|
rob@100
|
11885 };
|
rob@100
|
11886
|
rob@100
|
11887 /**
|
rob@100
|
11888 * Inserts a value or array of values into an existing array. The first two parameters must
|
rob@100
|
11889 * be of the same datatype. The array parameter defines the array which will be modified
|
rob@100
|
11890 * and the second parameter defines the data which will be inserted. When using an array
|
rob@100
|
11891 * of objects, the data returned from the function must be cast to the object array's data
|
rob@100
|
11892 * type. For example: SomeClass[] items = (SomeClass[]) splice(array1, array2, index).
|
rob@100
|
11893 *
|
rob@100
|
11894 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
|
rob@100
|
11895 * byte[], char[], int[], float[], String[], or an array of objects
|
rob@100
|
11896 * @param {boolean|byte|char|int|float|String|boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects}
|
rob@100
|
11897 * value boolean, byte, char, int, float, String, boolean[], byte[], char[], int[],
|
rob@100
|
11898 * float[], String[], or other Object: value or an array of objects to be spliced in
|
rob@100
|
11899 * @param {int} index position in the array from which to insert data
|
rob@100
|
11900 *
|
rob@100
|
11901 * @returns Array (the same datatype as the input)
|
rob@100
|
11902 *
|
rob@100
|
11903 * @see contract
|
rob@100
|
11904 * @see subset
|
rob@100
|
11905 */
|
rob@100
|
11906 p.splice = function(array, value, index) {
|
rob@100
|
11907
|
rob@100
|
11908 // Trying to splice an empty array into "array" in P5 won't do
|
rob@100
|
11909 // anything, just return the original.
|
rob@100
|
11910 if(value.length === 0)
|
rob@100
|
11911 {
|
rob@100
|
11912 return array;
|
rob@100
|
11913 }
|
rob@100
|
11914
|
rob@100
|
11915 // If the second argument was an array, we'll need to iterate over all
|
rob@100
|
11916 // the "value" elements and add one by one because
|
rob@100
|
11917 // array.splice(index, 0, value);
|
rob@100
|
11918 // would create a multi-dimensional array which isn't what we want.
|
rob@100
|
11919 if(value instanceof Array) {
|
rob@100
|
11920 for(var i = 0, j = index; i < value.length; j++,i++) {
|
rob@100
|
11921 array.splice(j, 0, value[i]);
|
rob@100
|
11922 }
|
rob@100
|
11923 } else {
|
rob@100
|
11924 array.splice(index, 0, value);
|
rob@100
|
11925 }
|
rob@100
|
11926
|
rob@100
|
11927 return array;
|
rob@100
|
11928 };
|
rob@100
|
11929
|
rob@100
|
11930 /**
|
rob@100
|
11931 * Extracts an array of elements from an existing array. The array parameter defines the
|
rob@100
|
11932 * array from which the elements will be copied and the offset and length parameters determine
|
rob@100
|
11933 * which elements to extract. If no length is given, elements will be extracted from the offset
|
rob@100
|
11934 * to the end of the array. When specifying the offset remember the first array element is 0.
|
rob@100
|
11935 * This function does not change the source array.
|
rob@100
|
11936 * When using an array of objects, the data returned from the function must be cast to the
|
rob@100
|
11937 * object array's data type.
|
rob@100
|
11938 *
|
rob@100
|
11939 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
|
rob@100
|
11940 * byte[], char[], int[], float[], String[], or an array of objects
|
rob@100
|
11941 * @param {int} offset position to begin
|
rob@100
|
11942 * @param {int} length number of values to extract
|
rob@100
|
11943 *
|
rob@100
|
11944 * @returns Array (the same datatype as the input)
|
rob@100
|
11945 *
|
rob@100
|
11946 * @see splice
|
rob@100
|
11947 */
|
rob@100
|
11948 p.subset = function(array, offset, length) {
|
rob@100
|
11949 var end = (length !== undef) ? offset + length : array.length;
|
rob@100
|
11950 return array.slice(offset, end);
|
rob@100
|
11951 };
|
rob@100
|
11952
|
rob@100
|
11953 /**
|
rob@100
|
11954 * Combines an array of Strings into one String, each separated by the character(s) used for
|
rob@100
|
11955 * the separator parameter. To join arrays of ints or floats, it's necessary to first convert
|
rob@100
|
11956 * them to strings using nf() or nfs().
|
rob@100
|
11957 *
|
rob@100
|
11958 * @param {Array} array array of Strings
|
rob@100
|
11959 * @param {char|String} separator char or String to be placed between each item
|
rob@100
|
11960 *
|
rob@100
|
11961 * @returns {String} The combined string
|
rob@100
|
11962 *
|
rob@100
|
11963 * @see split
|
rob@100
|
11964 * @see trim
|
rob@100
|
11965 * @see nf
|
rob@100
|
11966 * @see nfs
|
rob@100
|
11967 */
|
rob@100
|
11968 p.join = function(array, seperator) {
|
rob@100
|
11969 return array.join(seperator);
|
rob@100
|
11970 };
|
rob@100
|
11971
|
rob@100
|
11972 /**
|
rob@100
|
11973 * Decreases an array by one element and returns the shortened array. When using an
|
rob@100
|
11974 * array of objects, the data returned from the function must be cast to the object array's
|
rob@100
|
11975 * data type. For example: SomeClass[] items = (SomeClass[]) shorten(originalArray).
|
rob@100
|
11976 *
|
rob@100
|
11977 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array
|
rob@100
|
11978 * boolean[], byte[], char[], int[], float[], or String[], or an array of objects
|
rob@100
|
11979 *
|
rob@100
|
11980 * @returns Array (the same datatype as the input)
|
rob@100
|
11981 *
|
rob@100
|
11982 * @see append
|
rob@100
|
11983 * @see expand
|
rob@100
|
11984 */
|
rob@100
|
11985 p.shorten = function(ary) {
|
rob@100
|
11986 var newary = [];
|
rob@100
|
11987
|
rob@100
|
11988 // copy array into new array
|
rob@100
|
11989 var len = ary.length;
|
rob@100
|
11990 for (var i = 0; i < len; i++) {
|
rob@100
|
11991 newary[i] = ary[i];
|
rob@100
|
11992 }
|
rob@100
|
11993 newary.pop();
|
rob@100
|
11994
|
rob@100
|
11995 return newary;
|
rob@100
|
11996 };
|
rob@100
|
11997
|
rob@100
|
11998 /**
|
rob@100
|
11999 * Increases the size of an array. By default, this function doubles the size of the array,
|
rob@100
|
12000 * but the optional newSize parameter provides precise control over the increase in size.
|
rob@100
|
12001 * When using an array of objects, the data returned from the function must be cast to the
|
rob@100
|
12002 * object array's data type. For example: SomeClass[] items = (SomeClass[]) expand(originalArray).
|
rob@100
|
12003 *
|
rob@100
|
12004 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} ary
|
rob@100
|
12005 * boolean[], byte[], char[], int[], float[], String[], or an array of objects
|
rob@100
|
12006 * @param {int} newSize positive int: new size for the array
|
rob@100
|
12007 *
|
rob@100
|
12008 * @returns Array (the same datatype as the input)
|
rob@100
|
12009 *
|
rob@100
|
12010 * @see contract
|
rob@100
|
12011 */
|
rob@100
|
12012 p.expand = function(ary, targetSize) {
|
rob@100
|
12013 var temp = ary.slice(0),
|
rob@100
|
12014 newSize = targetSize || ary.length * 2;
|
rob@100
|
12015 temp.length = newSize;
|
rob@100
|
12016 return temp;
|
rob@100
|
12017 };
|
rob@100
|
12018
|
rob@100
|
12019 /**
|
rob@100
|
12020 * Copies an array (or part of an array) to another array. The src array is copied to the
|
rob@100
|
12021 * dst array, beginning at the position specified by srcPos and into the position specified
|
rob@100
|
12022 * by dstPos. The number of elements to copy is determined by length. The simplified version
|
rob@100
|
12023 * with two arguments copies an entire array to another of the same size. It is equivalent
|
rob@100
|
12024 * to "arrayCopy(src, 0, dst, 0, src.length)". This function is far more efficient for copying
|
rob@100
|
12025 * array data than iterating through a for and copying each element.
|
rob@100
|
12026 *
|
rob@100
|
12027 * @param {Array} src an array of any data type: the source array
|
rob@100
|
12028 * @param {Array} dest an array of any data type (as long as it's the same as src): the destination array
|
rob@100
|
12029 * @param {int} srcPos starting position in the source array
|
rob@100
|
12030 * @param {int} destPos starting position in the destination array
|
rob@100
|
12031 * @param {int} length number of array elements to be copied
|
rob@100
|
12032 *
|
rob@100
|
12033 * @returns none
|
rob@100
|
12034 */
|
rob@100
|
12035 p.arrayCopy = function() { // src, srcPos, dest, destPos, length) {
|
rob@100
|
12036 var src, srcPos = 0, dest, destPos = 0, length;
|
rob@100
|
12037
|
rob@100
|
12038 if (arguments.length === 2) {
|
rob@100
|
12039 // recall itself and copy src to dest from start index 0 to 0 of src.length
|
rob@100
|
12040 src = arguments[0];
|
rob@100
|
12041 dest = arguments[1];
|
rob@100
|
12042 length = src.length;
|
rob@100
|
12043 } else if (arguments.length === 3) {
|
rob@100
|
12044 // recall itself and copy src to dest from start index 0 to 0 of length
|
rob@100
|
12045 src = arguments[0];
|
rob@100
|
12046 dest = arguments[1];
|
rob@100
|
12047 length = arguments[2];
|
rob@100
|
12048 } else if (arguments.length === 5) {
|
rob@100
|
12049 src = arguments[0];
|
rob@100
|
12050 srcPos = arguments[1];
|
rob@100
|
12051 dest = arguments[2];
|
rob@100
|
12052 destPos = arguments[3];
|
rob@100
|
12053 length = arguments[4];
|
rob@100
|
12054 }
|
rob@100
|
12055
|
rob@100
|
12056 // copy src to dest from index srcPos to index destPos of length recursivly on objects
|
rob@100
|
12057 for (var i = srcPos, j = destPos; i < length + srcPos; i++, j++) {
|
rob@100
|
12058 if (dest[j] !== undef) {
|
rob@100
|
12059 dest[j] = src[i];
|
rob@100
|
12060 } else {
|
rob@100
|
12061 throw "array index out of bounds exception";
|
rob@100
|
12062 }
|
rob@100
|
12063 }
|
rob@100
|
12064 };
|
rob@100
|
12065
|
rob@100
|
12066 /**
|
rob@100
|
12067 * Reverses the order of an array.
|
rob@100
|
12068 *
|
rob@100
|
12069 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]} array
|
rob@100
|
12070 * boolean[], byte[], char[], int[], float[], or String[]
|
rob@100
|
12071 *
|
rob@100
|
12072 * @returns Array (the same datatype as the input)
|
rob@100
|
12073 *
|
rob@100
|
12074 * @see sort
|
rob@100
|
12075 */
|
rob@100
|
12076 p.reverse = function(array) {
|
rob@100
|
12077 return array.reverse();
|
rob@100
|
12078 };
|
rob@100
|
12079
|
rob@100
|
12080
|
rob@100
|
12081 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
12082 // Color functions
|
rob@100
|
12083 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
12084
|
rob@100
|
12085 // helper functions for internal blending modes
|
rob@100
|
12086 p.mix = function(a, b, f) {
|
rob@100
|
12087 return a + (((b - a) * f) >> 8);
|
rob@100
|
12088 };
|
rob@100
|
12089
|
rob@100
|
12090 p.peg = function(n) {
|
rob@100
|
12091 return (n < 0) ? 0 : ((n > 255) ? 255 : n);
|
rob@100
|
12092 };
|
rob@100
|
12093
|
rob@100
|
12094 // blending modes
|
rob@100
|
12095 /**
|
rob@100
|
12096 * These are internal blending modes used for BlendColor()
|
rob@100
|
12097 *
|
rob@100
|
12098 * @param {Color} c1 First Color to blend
|
rob@100
|
12099 * @param {Color} c2 Second Color to blend
|
rob@100
|
12100 *
|
rob@100
|
12101 * @returns {Color} The blended Color
|
rob@100
|
12102 *
|
rob@100
|
12103 * @see BlendColor
|
rob@100
|
12104 * @see Blend
|
rob@100
|
12105 */
|
rob@100
|
12106 p.modes = (function() {
|
rob@100
|
12107 var ALPHA_MASK = PConstants.ALPHA_MASK,
|
rob@100
|
12108 RED_MASK = PConstants.RED_MASK,
|
rob@100
|
12109 GREEN_MASK = PConstants.GREEN_MASK,
|
rob@100
|
12110 BLUE_MASK = PConstants.BLUE_MASK,
|
rob@100
|
12111 min = Math.min,
|
rob@100
|
12112 max = Math.max;
|
rob@100
|
12113
|
rob@100
|
12114 function applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) {
|
rob@100
|
12115 var a = min(((c1 & 0xff000000) >>> 24) + f, 0xff) << 24;
|
rob@100
|
12116
|
rob@100
|
12117 var r = (ar + (((cr - ar) * f) >> 8));
|
rob@100
|
12118 r = ((r < 0) ? 0 : ((r > 255) ? 255 : r)) << 16;
|
rob@100
|
12119
|
rob@100
|
12120 var g = (ag + (((cg - ag) * f) >> 8));
|
rob@100
|
12121 g = ((g < 0) ? 0 : ((g > 255) ? 255 : g)) << 8;
|
rob@100
|
12122
|
rob@100
|
12123 var b = ab + (((cb - ab) * f) >> 8);
|
rob@100
|
12124 b = (b < 0) ? 0 : ((b > 255) ? 255 : b);
|
rob@100
|
12125
|
rob@100
|
12126 return (a | r | g | b);
|
rob@100
|
12127 }
|
rob@100
|
12128
|
rob@100
|
12129 return {
|
rob@100
|
12130 replace: function(c1, c2) {
|
rob@100
|
12131 return c2;
|
rob@100
|
12132 },
|
rob@100
|
12133 blend: function(c1, c2) {
|
rob@100
|
12134 var f = (c2 & ALPHA_MASK) >>> 24,
|
rob@100
|
12135 ar = (c1 & RED_MASK),
|
rob@100
|
12136 ag = (c1 & GREEN_MASK),
|
rob@100
|
12137 ab = (c1 & BLUE_MASK),
|
rob@100
|
12138 br = (c2 & RED_MASK),
|
rob@100
|
12139 bg = (c2 & GREEN_MASK),
|
rob@100
|
12140 bb = (c2 & BLUE_MASK);
|
rob@100
|
12141
|
rob@100
|
12142 return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
|
rob@100
|
12143 (ar + (((br - ar) * f) >> 8)) & RED_MASK |
|
rob@100
|
12144 (ag + (((bg - ag) * f) >> 8)) & GREEN_MASK |
|
rob@100
|
12145 (ab + (((bb - ab) * f) >> 8)) & BLUE_MASK);
|
rob@100
|
12146 },
|
rob@100
|
12147 add: function(c1, c2) {
|
rob@100
|
12148 var f = (c2 & ALPHA_MASK) >>> 24;
|
rob@100
|
12149 return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
|
rob@100
|
12150 min(((c1 & RED_MASK) + ((c2 & RED_MASK) >> 8) * f), RED_MASK) & RED_MASK |
|
rob@100
|
12151 min(((c1 & GREEN_MASK) + ((c2 & GREEN_MASK) >> 8) * f), GREEN_MASK) & GREEN_MASK |
|
rob@100
|
12152 min((c1 & BLUE_MASK) + (((c2 & BLUE_MASK) * f) >> 8), BLUE_MASK));
|
rob@100
|
12153 },
|
rob@100
|
12154 subtract: function(c1, c2) {
|
rob@100
|
12155 var f = (c2 & ALPHA_MASK) >>> 24;
|
rob@100
|
12156 return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
|
rob@100
|
12157 max(((c1 & RED_MASK) - ((c2 & RED_MASK) >> 8) * f), GREEN_MASK) & RED_MASK |
|
rob@100
|
12158 max(((c1 & GREEN_MASK) - ((c2 & GREEN_MASK) >> 8) * f), BLUE_MASK) & GREEN_MASK |
|
rob@100
|
12159 max((c1 & BLUE_MASK) - (((c2 & BLUE_MASK) * f) >> 8), 0));
|
rob@100
|
12160 },
|
rob@100
|
12161 lightest: function(c1, c2) {
|
rob@100
|
12162 var f = (c2 & ALPHA_MASK) >>> 24;
|
rob@100
|
12163 return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
|
rob@100
|
12164 max(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f) & RED_MASK |
|
rob@100
|
12165 max(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f) & GREEN_MASK |
|
rob@100
|
12166 max(c1 & BLUE_MASK, ((c2 & BLUE_MASK) * f) >> 8));
|
rob@100
|
12167 },
|
rob@100
|
12168 darkest: function(c1, c2) {
|
rob@100
|
12169 var f = (c2 & ALPHA_MASK) >>> 24,
|
rob@100
|
12170 ar = (c1 & RED_MASK),
|
rob@100
|
12171 ag = (c1 & GREEN_MASK),
|
rob@100
|
12172 ab = (c1 & BLUE_MASK),
|
rob@100
|
12173 br = min(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f),
|
rob@100
|
12174 bg = min(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f),
|
rob@100
|
12175 bb = min(c1 & BLUE_MASK, ((c2 & BLUE_MASK) * f) >> 8);
|
rob@100
|
12176
|
rob@100
|
12177 return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
|
rob@100
|
12178 (ar + (((br - ar) * f) >> 8)) & RED_MASK |
|
rob@100
|
12179 (ag + (((bg - ag) * f) >> 8)) & GREEN_MASK |
|
rob@100
|
12180 (ab + (((bb - ab) * f) >> 8)) & BLUE_MASK);
|
rob@100
|
12181 },
|
rob@100
|
12182 difference: function(c1, c2) {
|
rob@100
|
12183 var f = (c2 & ALPHA_MASK) >>> 24,
|
rob@100
|
12184 ar = (c1 & RED_MASK) >> 16,
|
rob@100
|
12185 ag = (c1 & GREEN_MASK) >> 8,
|
rob@100
|
12186 ab = (c1 & BLUE_MASK),
|
rob@100
|
12187 br = (c2 & RED_MASK) >> 16,
|
rob@100
|
12188 bg = (c2 & GREEN_MASK) >> 8,
|
rob@100
|
12189 bb = (c2 & BLUE_MASK),
|
rob@100
|
12190 cr = (ar > br) ? (ar - br) : (br - ar),
|
rob@100
|
12191 cg = (ag > bg) ? (ag - bg) : (bg - ag),
|
rob@100
|
12192 cb = (ab > bb) ? (ab - bb) : (bb - ab);
|
rob@100
|
12193
|
rob@100
|
12194 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
rob@100
|
12195 },
|
rob@100
|
12196 exclusion: function(c1, c2) {
|
rob@100
|
12197 var f = (c2 & ALPHA_MASK) >>> 24,
|
rob@100
|
12198 ar = (c1 & RED_MASK) >> 16,
|
rob@100
|
12199 ag = (c1 & GREEN_MASK) >> 8,
|
rob@100
|
12200 ab = (c1 & BLUE_MASK),
|
rob@100
|
12201 br = (c2 & RED_MASK) >> 16,
|
rob@100
|
12202 bg = (c2 & GREEN_MASK) >> 8,
|
rob@100
|
12203 bb = (c2 & BLUE_MASK),
|
rob@100
|
12204 cr = ar + br - ((ar * br) >> 7),
|
rob@100
|
12205 cg = ag + bg - ((ag * bg) >> 7),
|
rob@100
|
12206 cb = ab + bb - ((ab * bb) >> 7);
|
rob@100
|
12207
|
rob@100
|
12208 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
rob@100
|
12209 },
|
rob@100
|
12210 multiply: function(c1, c2) {
|
rob@100
|
12211 var f = (c2 & ALPHA_MASK) >>> 24,
|
rob@100
|
12212 ar = (c1 & RED_MASK) >> 16,
|
rob@100
|
12213 ag = (c1 & GREEN_MASK) >> 8,
|
rob@100
|
12214 ab = (c1 & BLUE_MASK),
|
rob@100
|
12215 br = (c2 & RED_MASK) >> 16,
|
rob@100
|
12216 bg = (c2 & GREEN_MASK) >> 8,
|
rob@100
|
12217 bb = (c2 & BLUE_MASK),
|
rob@100
|
12218 cr = (ar * br) >> 8,
|
rob@100
|
12219 cg = (ag * bg) >> 8,
|
rob@100
|
12220 cb = (ab * bb) >> 8;
|
rob@100
|
12221
|
rob@100
|
12222 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
rob@100
|
12223 },
|
rob@100
|
12224 screen: function(c1, c2) {
|
rob@100
|
12225 var f = (c2 & ALPHA_MASK) >>> 24,
|
rob@100
|
12226 ar = (c1 & RED_MASK) >> 16,
|
rob@100
|
12227 ag = (c1 & GREEN_MASK) >> 8,
|
rob@100
|
12228 ab = (c1 & BLUE_MASK),
|
rob@100
|
12229 br = (c2 & RED_MASK) >> 16,
|
rob@100
|
12230 bg = (c2 & GREEN_MASK) >> 8,
|
rob@100
|
12231 bb = (c2 & BLUE_MASK),
|
rob@100
|
12232 cr = 255 - (((255 - ar) * (255 - br)) >> 8),
|
rob@100
|
12233 cg = 255 - (((255 - ag) * (255 - bg)) >> 8),
|
rob@100
|
12234 cb = 255 - (((255 - ab) * (255 - bb)) >> 8);
|
rob@100
|
12235
|
rob@100
|
12236 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
rob@100
|
12237 },
|
rob@100
|
12238 hard_light: function(c1, c2) {
|
rob@100
|
12239 var f = (c2 & ALPHA_MASK) >>> 24,
|
rob@100
|
12240 ar = (c1 & RED_MASK) >> 16,
|
rob@100
|
12241 ag = (c1 & GREEN_MASK) >> 8,
|
rob@100
|
12242 ab = (c1 & BLUE_MASK),
|
rob@100
|
12243 br = (c2 & RED_MASK) >> 16,
|
rob@100
|
12244 bg = (c2 & GREEN_MASK) >> 8,
|
rob@100
|
12245 bb = (c2 & BLUE_MASK),
|
rob@100
|
12246 cr = (br < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)),
|
rob@100
|
12247 cg = (bg < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)),
|
rob@100
|
12248 cb = (bb < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
|
rob@100
|
12249
|
rob@100
|
12250 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
rob@100
|
12251 },
|
rob@100
|
12252 soft_light: function(c1, c2) {
|
rob@100
|
12253 var f = (c2 & ALPHA_MASK) >>> 24,
|
rob@100
|
12254 ar = (c1 & RED_MASK) >> 16,
|
rob@100
|
12255 ag = (c1 & GREEN_MASK) >> 8,
|
rob@100
|
12256 ab = (c1 & BLUE_MASK),
|
rob@100
|
12257 br = (c2 & RED_MASK) >> 16,
|
rob@100
|
12258 bg = (c2 & GREEN_MASK) >> 8,
|
rob@100
|
12259 bb = (c2 & BLUE_MASK),
|
rob@100
|
12260 cr = ((ar * br) >> 7) + ((ar * ar) >> 8) - ((ar * ar * br) >> 15),
|
rob@100
|
12261 cg = ((ag * bg) >> 7) + ((ag * ag) >> 8) - ((ag * ag * bg) >> 15),
|
rob@100
|
12262 cb = ((ab * bb) >> 7) + ((ab * ab) >> 8) - ((ab * ab * bb) >> 15);
|
rob@100
|
12263
|
rob@100
|
12264 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
rob@100
|
12265 },
|
rob@100
|
12266 overlay: function(c1, c2) {
|
rob@100
|
12267 var f = (c2 & ALPHA_MASK) >>> 24,
|
rob@100
|
12268 ar = (c1 & RED_MASK) >> 16,
|
rob@100
|
12269 ag = (c1 & GREEN_MASK) >> 8,
|
rob@100
|
12270 ab = (c1 & BLUE_MASK),
|
rob@100
|
12271 br = (c2 & RED_MASK) >> 16,
|
rob@100
|
12272 bg = (c2 & GREEN_MASK) >> 8,
|
rob@100
|
12273 bb = (c2 & BLUE_MASK),
|
rob@100
|
12274 cr = (ar < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)),
|
rob@100
|
12275 cg = (ag < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)),
|
rob@100
|
12276 cb = (ab < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
|
rob@100
|
12277
|
rob@100
|
12278 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
rob@100
|
12279 },
|
rob@100
|
12280 dodge: function(c1, c2) {
|
rob@100
|
12281 var f = (c2 & ALPHA_MASK) >>> 24,
|
rob@100
|
12282 ar = (c1 & RED_MASK) >> 16,
|
rob@100
|
12283 ag = (c1 & GREEN_MASK) >> 8,
|
rob@100
|
12284 ab = (c1 & BLUE_MASK),
|
rob@100
|
12285 br = (c2 & RED_MASK) >> 16,
|
rob@100
|
12286 bg = (c2 & GREEN_MASK) >> 8,
|
rob@100
|
12287 bb = (c2 & BLUE_MASK);
|
rob@100
|
12288
|
rob@100
|
12289 var cr = 255;
|
rob@100
|
12290 if (br !== 255) {
|
rob@100
|
12291 cr = (ar << 8) / (255 - br);
|
rob@100
|
12292 cr = (cr < 0) ? 0 : ((cr > 255) ? 255 : cr);
|
rob@100
|
12293 }
|
rob@100
|
12294
|
rob@100
|
12295 var cg = 255;
|
rob@100
|
12296 if (bg !== 255) {
|
rob@100
|
12297 cg = (ag << 8) / (255 - bg);
|
rob@100
|
12298 cg = (cg < 0) ? 0 : ((cg > 255) ? 255 : cg);
|
rob@100
|
12299 }
|
rob@100
|
12300
|
rob@100
|
12301 var cb = 255;
|
rob@100
|
12302 if (bb !== 255) {
|
rob@100
|
12303 cb = (ab << 8) / (255 - bb);
|
rob@100
|
12304 cb = (cb < 0) ? 0 : ((cb > 255) ? 255 : cb);
|
rob@100
|
12305 }
|
rob@100
|
12306
|
rob@100
|
12307 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
rob@100
|
12308 },
|
rob@100
|
12309 burn: function(c1, c2) {
|
rob@100
|
12310 var f = (c2 & ALPHA_MASK) >>> 24,
|
rob@100
|
12311 ar = (c1 & RED_MASK) >> 16,
|
rob@100
|
12312 ag = (c1 & GREEN_MASK) >> 8,
|
rob@100
|
12313 ab = (c1 & BLUE_MASK),
|
rob@100
|
12314 br = (c2 & RED_MASK) >> 16,
|
rob@100
|
12315 bg = (c2 & GREEN_MASK) >> 8,
|
rob@100
|
12316 bb = (c2 & BLUE_MASK);
|
rob@100
|
12317
|
rob@100
|
12318 var cr = 0;
|
rob@100
|
12319 if (br !== 0) {
|
rob@100
|
12320 cr = ((255 - ar) << 8) / br;
|
rob@100
|
12321 cr = 255 - ((cr < 0) ? 0 : ((cr > 255) ? 255 : cr));
|
rob@100
|
12322 }
|
rob@100
|
12323
|
rob@100
|
12324 var cg = 0;
|
rob@100
|
12325 if (bg !== 0) {
|
rob@100
|
12326 cg = ((255 - ag) << 8) / bg;
|
rob@100
|
12327 cg = 255 - ((cg < 0) ? 0 : ((cg > 255) ? 255 : cg));
|
rob@100
|
12328 }
|
rob@100
|
12329
|
rob@100
|
12330 var cb = 0;
|
rob@100
|
12331 if (bb !== 0) {
|
rob@100
|
12332 cb = ((255 - ab) << 8) / bb;
|
rob@100
|
12333 cb = 255 - ((cb < 0) ? 0 : ((cb > 255) ? 255 : cb));
|
rob@100
|
12334 }
|
rob@100
|
12335
|
rob@100
|
12336 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
rob@100
|
12337 }
|
rob@100
|
12338 };
|
rob@100
|
12339 }());
|
rob@100
|
12340
|
rob@100
|
12341 function color$4(aValue1, aValue2, aValue3, aValue4) {
|
rob@100
|
12342 var r, g, b, a;
|
rob@100
|
12343
|
rob@100
|
12344 if (curColorMode === PConstants.HSB) {
|
rob@100
|
12345 var rgb = p.color.toRGB(aValue1, aValue2, aValue3);
|
rob@100
|
12346 r = rgb[0];
|
rob@100
|
12347 g = rgb[1];
|
rob@100
|
12348 b = rgb[2];
|
rob@100
|
12349 } else {
|
rob@100
|
12350 r = Math.round(255 * (aValue1 / colorModeX));
|
rob@100
|
12351 g = Math.round(255 * (aValue2 / colorModeY));
|
rob@100
|
12352 b = Math.round(255 * (aValue3 / colorModeZ));
|
rob@100
|
12353 }
|
rob@100
|
12354
|
rob@100
|
12355 a = Math.round(255 * (aValue4 / colorModeA));
|
rob@100
|
12356
|
rob@100
|
12357 // Limit values less than 0 and greater than 255
|
rob@100
|
12358 r = (r < 0) ? 0 : r;
|
rob@100
|
12359 g = (g < 0) ? 0 : g;
|
rob@100
|
12360 b = (b < 0) ? 0 : b;
|
rob@100
|
12361 a = (a < 0) ? 0 : a;
|
rob@100
|
12362 r = (r > 255) ? 255 : r;
|
rob@100
|
12363 g = (g > 255) ? 255 : g;
|
rob@100
|
12364 b = (b > 255) ? 255 : b;
|
rob@100
|
12365 a = (a > 255) ? 255 : a;
|
rob@100
|
12366
|
rob@100
|
12367 // Create color int
|
rob@100
|
12368 return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK;
|
rob@100
|
12369 }
|
rob@100
|
12370
|
rob@100
|
12371 function color$2(aValue1, aValue2) {
|
rob@100
|
12372 var a;
|
rob@100
|
12373
|
rob@100
|
12374 // Color int and alpha
|
rob@100
|
12375 if (aValue1 & PConstants.ALPHA_MASK) {
|
rob@100
|
12376 a = Math.round(255 * (aValue2 / colorModeA));
|
rob@100
|
12377 // Limit values less than 0 and greater than 255
|
rob@100
|
12378 a = (a > 255) ? 255 : a;
|
rob@100
|
12379 a = (a < 0) ? 0 : a;
|
rob@100
|
12380
|
rob@100
|
12381 return aValue1 - (aValue1 & PConstants.ALPHA_MASK) + ((a << 24) & PConstants.ALPHA_MASK);
|
rob@100
|
12382 }
|
rob@100
|
12383 // Grayscale and alpha
|
rob@100
|
12384 if (curColorMode === PConstants.RGB) {
|
rob@100
|
12385 return color$4(aValue1, aValue1, aValue1, aValue2);
|
rob@100
|
12386 }
|
rob@100
|
12387 if (curColorMode === PConstants.HSB) {
|
rob@100
|
12388 return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, aValue2);
|
rob@100
|
12389 }
|
rob@100
|
12390 }
|
rob@100
|
12391
|
rob@100
|
12392 function color$1(aValue1) {
|
rob@100
|
12393 // Grayscale
|
rob@100
|
12394 if (aValue1 <= colorModeX && aValue1 >= 0) {
|
rob@100
|
12395 if (curColorMode === PConstants.RGB) {
|
rob@100
|
12396 return color$4(aValue1, aValue1, aValue1, colorModeA);
|
rob@100
|
12397 }
|
rob@100
|
12398 if (curColorMode === PConstants.HSB) {
|
rob@100
|
12399 return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, colorModeA);
|
rob@100
|
12400 }
|
rob@100
|
12401 }
|
rob@100
|
12402 // Color int
|
rob@100
|
12403 if (aValue1) {
|
rob@100
|
12404 if (aValue1 > 2147483647) {
|
rob@100
|
12405 // Java Overflow
|
rob@100
|
12406 aValue1 -= 4294967296;
|
rob@100
|
12407 }
|
rob@100
|
12408 return aValue1;
|
rob@100
|
12409 }
|
rob@100
|
12410 }
|
rob@100
|
12411
|
rob@100
|
12412 /**
|
rob@100
|
12413 * Creates colors for storing in variables of the color datatype. The parameters are
|
rob@100
|
12414 * interpreted as RGB or HSB values depending on the current colorMode(). The default
|
rob@100
|
12415 * mode is RGB values from 0 to 255 and therefore, the function call color(255, 204, 0)
|
rob@100
|
12416 * will return a bright yellow color. More about how colors are stored can be found in
|
rob@100
|
12417 * the reference for the color datatype.
|
rob@100
|
12418 *
|
rob@100
|
12419 * @param {int|float} aValue1 red or hue or grey values relative to the current color range.
|
rob@100
|
12420 * Also can be color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
|
rob@100
|
12421 * @param {int|float} aValue2 green or saturation values relative to the current color range
|
rob@100
|
12422 * @param {int|float} aValue3 blue or brightness values relative to the current color range
|
rob@100
|
12423 * @param {int|float} aValue4 relative to current color range. Represents alpha
|
rob@100
|
12424 *
|
rob@100
|
12425 * @returns {color} the color
|
rob@100
|
12426 *
|
rob@100
|
12427 * @see colorMode
|
rob@100
|
12428 */
|
rob@100
|
12429 p.color = function(aValue1, aValue2, aValue3, aValue4) {
|
rob@100
|
12430
|
rob@100
|
12431 // 4 arguments: (R, G, B, A) or (H, S, B, A)
|
rob@100
|
12432 if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef && aValue4 !== undef) {
|
rob@100
|
12433 return color$4(aValue1, aValue2, aValue3, aValue4);
|
rob@100
|
12434 }
|
rob@100
|
12435
|
rob@100
|
12436 // 3 arguments: (R, G, B) or (H, S, B)
|
rob@100
|
12437 if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef) {
|
rob@100
|
12438 return color$4(aValue1, aValue2, aValue3, colorModeA);
|
rob@100
|
12439 }
|
rob@100
|
12440
|
rob@100
|
12441 // 2 arguments: (Color, A) or (Grayscale, A)
|
rob@100
|
12442 if (aValue1 !== undef && aValue2 !== undef) {
|
rob@100
|
12443 return color$2(aValue1, aValue2);
|
rob@100
|
12444 }
|
rob@100
|
12445
|
rob@100
|
12446 // 1 argument: (Grayscale) or (Color)
|
rob@100
|
12447 if (typeof aValue1 === "number") {
|
rob@100
|
12448 return color$1(aValue1);
|
rob@100
|
12449 }
|
rob@100
|
12450
|
rob@100
|
12451 // Default
|
rob@100
|
12452 return color$4(colorModeX, colorModeY, colorModeZ, colorModeA);
|
rob@100
|
12453 };
|
rob@100
|
12454
|
rob@100
|
12455 // Ease of use function to extract the colour bits into a string
|
rob@100
|
12456 p.color.toString = function(colorInt) {
|
rob@100
|
12457 return "rgba(" + ((colorInt & PConstants.RED_MASK) >>> 16) + "," + ((colorInt & PConstants.GREEN_MASK) >>> 8) +
|
rob@100
|
12458 "," + ((colorInt & PConstants.BLUE_MASK)) + "," + ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255 + ")";
|
rob@100
|
12459 };
|
rob@100
|
12460
|
rob@100
|
12461 // Easy of use function to pack rgba values into a single bit-shifted color int.
|
rob@100
|
12462 p.color.toInt = function(r, g, b, a) {
|
rob@100
|
12463 return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK;
|
rob@100
|
12464 };
|
rob@100
|
12465
|
rob@100
|
12466 // Creates a simple array in [R, G, B, A] format, [255, 255, 255, 255]
|
rob@100
|
12467 p.color.toArray = function(colorInt) {
|
rob@100
|
12468 return [(colorInt & PConstants.RED_MASK) >>> 16, (colorInt & PConstants.GREEN_MASK) >>> 8,
|
rob@100
|
12469 colorInt & PConstants.BLUE_MASK, (colorInt & PConstants.ALPHA_MASK) >>> 24];
|
rob@100
|
12470 };
|
rob@100
|
12471
|
rob@100
|
12472 // Creates a WebGL color array in [R, G, B, A] format. WebGL wants the color ranges between 0 and 1, [1, 1, 1, 1]
|
rob@100
|
12473 p.color.toGLArray = function(colorInt) {
|
rob@100
|
12474 return [((colorInt & PConstants.RED_MASK) >>> 16) / 255, ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255,
|
rob@100
|
12475 (colorInt & PConstants.BLUE_MASK) / 255, ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255];
|
rob@100
|
12476 };
|
rob@100
|
12477
|
rob@100
|
12478 // HSB conversion function from Mootools, MIT Licensed
|
rob@100
|
12479 p.color.toRGB = function(h, s, b) {
|
rob@100
|
12480 // Limit values greater than range
|
rob@100
|
12481 h = (h > colorModeX) ? colorModeX : h;
|
rob@100
|
12482 s = (s > colorModeY) ? colorModeY : s;
|
rob@100
|
12483 b = (b > colorModeZ) ? colorModeZ : b;
|
rob@100
|
12484
|
rob@100
|
12485 h = (h / colorModeX) * 360;
|
rob@100
|
12486 s = (s / colorModeY) * 100;
|
rob@100
|
12487 b = (b / colorModeZ) * 100;
|
rob@100
|
12488
|
rob@100
|
12489 var br = Math.round(b / 100 * 255);
|
rob@100
|
12490
|
rob@100
|
12491 if (s === 0) { // Grayscale
|
rob@100
|
12492 return [br, br, br];
|
rob@100
|
12493 }
|
rob@100
|
12494 var hue = h % 360;
|
rob@100
|
12495 var f = hue % 60;
|
rob@100
|
12496 var p = Math.round((b * (100 - s)) / 10000 * 255);
|
rob@100
|
12497 var q = Math.round((b * (6000 - s * f)) / 600000 * 255);
|
rob@100
|
12498 var t = Math.round((b * (6000 - s * (60 - f))) / 600000 * 255);
|
rob@100
|
12499 switch (Math.floor(hue / 60)) {
|
rob@100
|
12500 case 0:
|
rob@100
|
12501 return [br, t, p];
|
rob@100
|
12502 case 1:
|
rob@100
|
12503 return [q, br, p];
|
rob@100
|
12504 case 2:
|
rob@100
|
12505 return [p, br, t];
|
rob@100
|
12506 case 3:
|
rob@100
|
12507 return [p, q, br];
|
rob@100
|
12508 case 4:
|
rob@100
|
12509 return [t, p, br];
|
rob@100
|
12510 case 5:
|
rob@100
|
12511 return [br, p, q];
|
rob@100
|
12512 }
|
rob@100
|
12513 };
|
rob@100
|
12514
|
rob@100
|
12515 function colorToHSB(colorInt) {
|
rob@100
|
12516 var red, green, blue;
|
rob@100
|
12517
|
rob@100
|
12518 red = ((colorInt & PConstants.RED_MASK) >>> 16) / 255;
|
rob@100
|
12519 green = ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255;
|
rob@100
|
12520 blue = (colorInt & PConstants.BLUE_MASK) / 255;
|
rob@100
|
12521
|
rob@100
|
12522 var max = p.max(p.max(red,green), blue),
|
rob@100
|
12523 min = p.min(p.min(red,green), blue),
|
rob@100
|
12524 hue, saturation;
|
rob@100
|
12525
|
rob@100
|
12526 if (min === max) {
|
rob@100
|
12527 return [0, 0, max*colorModeZ];
|
rob@100
|
12528 }
|
rob@100
|
12529 saturation = (max - min) / max;
|
rob@100
|
12530
|
rob@100
|
12531 if (red === max) {
|
rob@100
|
12532 hue = (green - blue) / (max - min);
|
rob@100
|
12533 } else if (green === max) {
|
rob@100
|
12534 hue = 2 + ((blue - red) / (max - min));
|
rob@100
|
12535 } else {
|
rob@100
|
12536 hue = 4 + ((red - green) / (max - min));
|
rob@100
|
12537 }
|
rob@100
|
12538
|
rob@100
|
12539 hue /= 6;
|
rob@100
|
12540
|
rob@100
|
12541 if (hue < 0) {
|
rob@100
|
12542 hue += 1;
|
rob@100
|
12543 } else if (hue > 1) {
|
rob@100
|
12544 hue -= 1;
|
rob@100
|
12545 }
|
rob@100
|
12546 return [hue*colorModeX, saturation*colorModeY, max*colorModeZ];
|
rob@100
|
12547 }
|
rob@100
|
12548
|
rob@100
|
12549 /**
|
rob@100
|
12550 * Extracts the brightness value from a color.
|
rob@100
|
12551 *
|
rob@100
|
12552 * @param {color} colInt any value of the color datatype
|
rob@100
|
12553 *
|
rob@100
|
12554 * @returns {float} The brightness color value.
|
rob@100
|
12555 *
|
rob@100
|
12556 * @see red
|
rob@100
|
12557 * @see green
|
rob@100
|
12558 * @see blue
|
rob@100
|
12559 * @see hue
|
rob@100
|
12560 * @see saturation
|
rob@100
|
12561 */
|
rob@100
|
12562 p.brightness = function(colInt){
|
rob@100
|
12563 return colorToHSB(colInt)[2];
|
rob@100
|
12564 };
|
rob@100
|
12565
|
rob@100
|
12566 /**
|
rob@100
|
12567 * Extracts the saturation value from a color.
|
rob@100
|
12568 *
|
rob@100
|
12569 * @param {color} colInt any value of the color datatype
|
rob@100
|
12570 *
|
rob@100
|
12571 * @returns {float} The saturation color value.
|
rob@100
|
12572 *
|
rob@100
|
12573 * @see red
|
rob@100
|
12574 * @see green
|
rob@100
|
12575 * @see blue
|
rob@100
|
12576 * @see hue
|
rob@100
|
12577 * @see brightness
|
rob@100
|
12578 */
|
rob@100
|
12579 p.saturation = function(colInt){
|
rob@100
|
12580 return colorToHSB(colInt)[1];
|
rob@100
|
12581 };
|
rob@100
|
12582
|
rob@100
|
12583 /**
|
rob@100
|
12584 * Extracts the hue value from a color.
|
rob@100
|
12585 *
|
rob@100
|
12586 * @param {color} colInt any value of the color datatype
|
rob@100
|
12587 *
|
rob@100
|
12588 * @returns {float} The hue color value.
|
rob@100
|
12589 *
|
rob@100
|
12590 * @see red
|
rob@100
|
12591 * @see green
|
rob@100
|
12592 * @see blue
|
rob@100
|
12593 * @see saturation
|
rob@100
|
12594 * @see brightness
|
rob@100
|
12595 */
|
rob@100
|
12596 p.hue = function(colInt){
|
rob@100
|
12597 return colorToHSB(colInt)[0];
|
rob@100
|
12598 };
|
rob@100
|
12599
|
rob@100
|
12600 /**
|
rob@100
|
12601 * Extracts the red value from a color, scaled to match current colorMode().
|
rob@100
|
12602 * This value is always returned as a float so be careful not to assign it to an int value.
|
rob@100
|
12603 *
|
rob@100
|
12604 * @param {color} aColor any value of the color datatype
|
rob@100
|
12605 *
|
rob@100
|
12606 * @returns {float} The red color value.
|
rob@100
|
12607 *
|
rob@100
|
12608 * @see green
|
rob@100
|
12609 * @see blue
|
rob@100
|
12610 * @see alpha
|
rob@100
|
12611 * @see >> right shift
|
rob@100
|
12612 * @see hue
|
rob@100
|
12613 * @see saturation
|
rob@100
|
12614 * @see brightness
|
rob@100
|
12615 */
|
rob@100
|
12616 p.red = function(aColor) {
|
rob@100
|
12617 return ((aColor & PConstants.RED_MASK) >>> 16) / 255 * colorModeX;
|
rob@100
|
12618 };
|
rob@100
|
12619
|
rob@100
|
12620 /**
|
rob@100
|
12621 * Extracts the green value from a color, scaled to match current colorMode().
|
rob@100
|
12622 * This value is always returned as a float so be careful not to assign it to an int value.
|
rob@100
|
12623 *
|
rob@100
|
12624 * @param {color} aColor any value of the color datatype
|
rob@100
|
12625 *
|
rob@100
|
12626 * @returns {float} The green color value.
|
rob@100
|
12627 *
|
rob@100
|
12628 * @see red
|
rob@100
|
12629 * @see blue
|
rob@100
|
12630 * @see alpha
|
rob@100
|
12631 * @see >> right shift
|
rob@100
|
12632 * @see hue
|
rob@100
|
12633 * @see saturation
|
rob@100
|
12634 * @see brightness
|
rob@100
|
12635 */
|
rob@100
|
12636 p.green = function(aColor) {
|
rob@100
|
12637 return ((aColor & PConstants.GREEN_MASK) >>> 8) / 255 * colorModeY;
|
rob@100
|
12638 };
|
rob@100
|
12639
|
rob@100
|
12640 /**
|
rob@100
|
12641 * Extracts the blue value from a color, scaled to match current colorMode().
|
rob@100
|
12642 * This value is always returned as a float so be careful not to assign it to an int value.
|
rob@100
|
12643 *
|
rob@100
|
12644 * @param {color} aColor any value of the color datatype
|
rob@100
|
12645 *
|
rob@100
|
12646 * @returns {float} The blue color value.
|
rob@100
|
12647 *
|
rob@100
|
12648 * @see red
|
rob@100
|
12649 * @see green
|
rob@100
|
12650 * @see alpha
|
rob@100
|
12651 * @see >> right shift
|
rob@100
|
12652 * @see hue
|
rob@100
|
12653 * @see saturation
|
rob@100
|
12654 * @see brightness
|
rob@100
|
12655 */
|
rob@100
|
12656 p.blue = function(aColor) {
|
rob@100
|
12657 return (aColor & PConstants.BLUE_MASK) / 255 * colorModeZ;
|
rob@100
|
12658 };
|
rob@100
|
12659
|
rob@100
|
12660 /**
|
rob@100
|
12661 * Extracts the alpha value from a color, scaled to match current colorMode().
|
rob@100
|
12662 * This value is always returned as a float so be careful not to assign it to an int value.
|
rob@100
|
12663 *
|
rob@100
|
12664 * @param {color} aColor any value of the color datatype
|
rob@100
|
12665 *
|
rob@100
|
12666 * @returns {float} The alpha color value.
|
rob@100
|
12667 *
|
rob@100
|
12668 * @see red
|
rob@100
|
12669 * @see green
|
rob@100
|
12670 * @see blue
|
rob@100
|
12671 * @see >> right shift
|
rob@100
|
12672 * @see hue
|
rob@100
|
12673 * @see saturation
|
rob@100
|
12674 * @see brightness
|
rob@100
|
12675 */
|
rob@100
|
12676 p.alpha = function(aColor) {
|
rob@100
|
12677 return ((aColor & PConstants.ALPHA_MASK) >>> 24) / 255 * colorModeA;
|
rob@100
|
12678 };
|
rob@100
|
12679
|
rob@100
|
12680 /**
|
rob@100
|
12681 * Calculates a color or colors between two colors at a specific increment.
|
rob@100
|
12682 * The amt parameter is the amount to interpolate between the two values where 0.0
|
rob@100
|
12683 * equal to the first point, 0.1 is very near the first point, 0.5 is half-way in between, etc.
|
rob@100
|
12684 *
|
rob@100
|
12685 * @param {color} c1 interpolate from this color
|
rob@100
|
12686 * @param {color} c2 interpolate to this color
|
rob@100
|
12687 * @param {float} amt between 0.0 and 1.0
|
rob@100
|
12688 *
|
rob@100
|
12689 * @returns {float} The blended color.
|
rob@100
|
12690 *
|
rob@100
|
12691 * @see blendColor
|
rob@100
|
12692 * @see color
|
rob@100
|
12693 */
|
rob@100
|
12694 p.lerpColor = function(c1, c2, amt) {
|
rob@100
|
12695 var r, g, b, a, r1, g1, b1, a1, r2, g2, b2, a2;
|
rob@100
|
12696 var hsb1, hsb2, rgb, h, s;
|
rob@100
|
12697 var colorBits1 = p.color(c1);
|
rob@100
|
12698 var colorBits2 = p.color(c2);
|
rob@100
|
12699
|
rob@100
|
12700 if (curColorMode === PConstants.HSB) {
|
rob@100
|
12701 // Special processing for HSB mode.
|
rob@100
|
12702 // Get HSB and Alpha values for Color 1 and 2
|
rob@100
|
12703 hsb1 = colorToHSB(colorBits1);
|
rob@100
|
12704 a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
|
rob@100
|
12705 hsb2 = colorToHSB(colorBits2);
|
rob@100
|
12706 a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
|
rob@100
|
12707
|
rob@100
|
12708 // Return lerp value for each channel, for HSB components
|
rob@100
|
12709 h = p.lerp(hsb1[0], hsb2[0], amt);
|
rob@100
|
12710 s = p.lerp(hsb1[1], hsb2[1], amt);
|
rob@100
|
12711 b = p.lerp(hsb1[2], hsb2[2], amt);
|
rob@100
|
12712 rgb = p.color.toRGB(h, s, b);
|
rob@100
|
12713 // ... and for Alpha-range
|
rob@100
|
12714 a = p.lerp(a1, a2, amt) * colorModeA;
|
rob@100
|
12715
|
rob@100
|
12716 return (a << 24) & PConstants.ALPHA_MASK |
|
rob@100
|
12717 (rgb[0] << 16) & PConstants.RED_MASK |
|
rob@100
|
12718 (rgb[1] << 8) & PConstants.GREEN_MASK |
|
rob@100
|
12719 rgb[2] & PConstants.BLUE_MASK;
|
rob@100
|
12720 }
|
rob@100
|
12721
|
rob@100
|
12722 // Get RGBA values for Color 1 to floats
|
rob@100
|
12723 r1 = (colorBits1 & PConstants.RED_MASK) >>> 16;
|
rob@100
|
12724 g1 = (colorBits1 & PConstants.GREEN_MASK) >>> 8;
|
rob@100
|
12725 b1 = (colorBits1 & PConstants.BLUE_MASK);
|
rob@100
|
12726 a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
|
rob@100
|
12727
|
rob@100
|
12728 // Get RGBA values for Color 2 to floats
|
rob@100
|
12729 r2 = (colorBits2 & PConstants.RED_MASK) >>> 16;
|
rob@100
|
12730 g2 = (colorBits2 & PConstants.GREEN_MASK) >>> 8;
|
rob@100
|
12731 b2 = (colorBits2 & PConstants.BLUE_MASK);
|
rob@100
|
12732 a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
|
rob@100
|
12733
|
rob@100
|
12734 // Return lerp value for each channel, INT for color, Float for Alpha-range
|
rob@100
|
12735 r = p.lerp(r1, r2, amt) | 0;
|
rob@100
|
12736 g = p.lerp(g1, g2, amt) | 0;
|
rob@100
|
12737 b = p.lerp(b1, b2, amt) | 0;
|
rob@100
|
12738 a = p.lerp(a1, a2, amt) * colorModeA;
|
rob@100
|
12739
|
rob@100
|
12740 return (a << 24) & PConstants.ALPHA_MASK |
|
rob@100
|
12741 (r << 16) & PConstants.RED_MASK |
|
rob@100
|
12742 (g << 8) & PConstants.GREEN_MASK |
|
rob@100
|
12743 b & PConstants.BLUE_MASK;
|
rob@100
|
12744 };
|
rob@100
|
12745
|
rob@100
|
12746 /**
|
rob@100
|
12747 * Changes the way Processing interprets color data. By default, fill(), stroke(), and background()
|
rob@100
|
12748 * colors are set by values between 0 and 255 using the RGB color model. It is possible to change the
|
rob@100
|
12749 * numerical range used for specifying colors and to switch color systems. For example, calling colorMode(RGB, 1.0)
|
rob@100
|
12750 * will specify that values are specified between 0 and 1. The limits for defining colors are altered by setting the
|
rob@100
|
12751 * parameters range1, range2, range3, and range 4.
|
rob@100
|
12752 *
|
rob@100
|
12753 * @param {MODE} mode Either RGB or HSB, corresponding to Red/Green/Blue and Hue/Saturation/Brightness
|
rob@100
|
12754 * @param {int|float} range range for all color elements
|
rob@100
|
12755 * @param {int|float} range1 range for the red or hue depending on the current color mode
|
rob@100
|
12756 * @param {int|float} range2 range for the green or saturation depending on the current color mode
|
rob@100
|
12757 * @param {int|float} range3 range for the blue or brightness depending on the current color mode
|
rob@100
|
12758 * @param {int|float} range4 range for the alpha
|
rob@100
|
12759 *
|
rob@100
|
12760 * @returns none
|
rob@100
|
12761 *
|
rob@100
|
12762 * @see background
|
rob@100
|
12763 * @see fill
|
rob@100
|
12764 * @see stroke
|
rob@100
|
12765 */
|
rob@100
|
12766 p.colorMode = function() { // mode, range1, range2, range3, range4
|
rob@100
|
12767 curColorMode = arguments[0];
|
rob@100
|
12768 if (arguments.length > 1) {
|
rob@100
|
12769 colorModeX = arguments[1];
|
rob@100
|
12770 colorModeY = arguments[2] || arguments[1];
|
rob@100
|
12771 colorModeZ = arguments[3] || arguments[1];
|
rob@100
|
12772 colorModeA = arguments[4] || arguments[1];
|
rob@100
|
12773 }
|
rob@100
|
12774 };
|
rob@100
|
12775
|
rob@100
|
12776 /**
|
rob@100
|
12777 * Blends two color values together based on the blending mode given as the MODE parameter.
|
rob@100
|
12778 * The possible modes are described in the reference for the blend() function.
|
rob@100
|
12779 *
|
rob@100
|
12780 * @param {color} c1 color: the first color to blend
|
rob@100
|
12781 * @param {color} c2 color: the second color to blend
|
rob@100
|
12782 * @param {MODE} MODE Either BLEND, ADD, SUBTRACT, DARKEST, LIGHTEST, DIFFERENCE, EXCLUSION, MULTIPLY,
|
rob@100
|
12783 * SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, or BURN
|
rob@100
|
12784 *
|
rob@100
|
12785 * @returns {float} The blended color.
|
rob@100
|
12786 *
|
rob@100
|
12787 * @see blend
|
rob@100
|
12788 * @see color
|
rob@100
|
12789 */
|
rob@100
|
12790 p.blendColor = function(c1, c2, mode) {
|
rob@100
|
12791 if (mode === PConstants.REPLACE) {
|
rob@100
|
12792 return p.modes.replace(c1, c2);
|
rob@100
|
12793 } else if (mode === PConstants.BLEND) {
|
rob@100
|
12794 return p.modes.blend(c1, c2);
|
rob@100
|
12795 } else if (mode === PConstants.ADD) {
|
rob@100
|
12796 return p.modes.add(c1, c2);
|
rob@100
|
12797 } else if (mode === PConstants.SUBTRACT) {
|
rob@100
|
12798 return p.modes.subtract(c1, c2);
|
rob@100
|
12799 } else if (mode === PConstants.LIGHTEST) {
|
rob@100
|
12800 return p.modes.lightest(c1, c2);
|
rob@100
|
12801 } else if (mode === PConstants.DARKEST) {
|
rob@100
|
12802 return p.modes.darkest(c1, c2);
|
rob@100
|
12803 } else if (mode === PConstants.DIFFERENCE) {
|
rob@100
|
12804 return p.modes.difference(c1, c2);
|
rob@100
|
12805 } else if (mode === PConstants.EXCLUSION) {
|
rob@100
|
12806 return p.modes.exclusion(c1, c2);
|
rob@100
|
12807 } else if (mode === PConstants.MULTIPLY) {
|
rob@100
|
12808 return p.modes.multiply(c1, c2);
|
rob@100
|
12809 } else if (mode === PConstants.SCREEN) {
|
rob@100
|
12810 return p.modes.screen(c1, c2);
|
rob@100
|
12811 } else if (mode === PConstants.HARD_LIGHT) {
|
rob@100
|
12812 return p.modes.hard_light(c1, c2);
|
rob@100
|
12813 } else if (mode === PConstants.SOFT_LIGHT) {
|
rob@100
|
12814 return p.modes.soft_light(c1, c2);
|
rob@100
|
12815 } else if (mode === PConstants.OVERLAY) {
|
rob@100
|
12816 return p.modes.overlay(c1, c2);
|
rob@100
|
12817 } else if (mode === PConstants.DODGE) {
|
rob@100
|
12818 return p.modes.dodge(c1, c2);
|
rob@100
|
12819 } else if (mode === PConstants.BURN) {
|
rob@100
|
12820 return p.modes.burn(c1, c2);
|
rob@100
|
12821 }
|
rob@100
|
12822 };
|
rob@100
|
12823
|
rob@100
|
12824 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
12825 // Canvas-Matrix manipulation
|
rob@100
|
12826 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
12827
|
rob@100
|
12828 function saveContext() {
|
rob@100
|
12829 curContext.save();
|
rob@100
|
12830 }
|
rob@100
|
12831
|
rob@100
|
12832 function restoreContext() {
|
rob@100
|
12833 curContext.restore();
|
rob@100
|
12834 isStrokeDirty = true;
|
rob@100
|
12835 isFillDirty = true;
|
rob@100
|
12836 }
|
rob@100
|
12837
|
rob@100
|
12838 /**
|
rob@100
|
12839 * Prints the current matrix to the text window.
|
rob@100
|
12840 *
|
rob@100
|
12841 * @returns none
|
rob@100
|
12842 *
|
rob@100
|
12843 * @see pushMatrix
|
rob@100
|
12844 * @see popMatrix
|
rob@100
|
12845 * @see resetMatrix
|
rob@100
|
12846 * @see applyMatrix
|
rob@100
|
12847 */
|
rob@100
|
12848 p.printMatrix = function() {
|
rob@100
|
12849 modelView.print();
|
rob@100
|
12850 };
|
rob@100
|
12851
|
rob@100
|
12852 /**
|
rob@100
|
12853 * Specifies an amount to displace objects within the display window. The x parameter specifies left/right translation,
|
rob@100
|
12854 * the y parameter specifies up/down translation, and the z parameter specifies translations toward/away from the screen.
|
rob@100
|
12855 * Using this function with the z parameter requires using the P3D or OPENGL parameter in combination with size as shown
|
rob@100
|
12856 * in the above example. Transformations apply to everything that happens after and subsequent calls to the function
|
rob@100
|
12857 * accumulates the effect. For example, calling translate(50, 0) and then translate(20, 0) is the same as translate(70, 0).
|
rob@100
|
12858 * If translate() is called within draw(), the transformation is reset when the loop begins again.
|
rob@100
|
12859 * This function can be further controlled by the pushMatrix() and popMatrix().
|
rob@100
|
12860 *
|
rob@100
|
12861 * @param {int|float} x left/right translation
|
rob@100
|
12862 * @param {int|float} y up/down translation
|
rob@100
|
12863 * @param {int|float} z forward/back translation
|
rob@100
|
12864 *
|
rob@100
|
12865 * @returns none
|
rob@100
|
12866 *
|
rob@100
|
12867 * @see pushMatrix
|
rob@100
|
12868 * @see popMatrix
|
rob@100
|
12869 * @see scale
|
rob@100
|
12870 * @see rotate
|
rob@100
|
12871 * @see rotateX
|
rob@100
|
12872 * @see rotateY
|
rob@100
|
12873 * @see rotateZ
|
rob@100
|
12874 */
|
rob@100
|
12875 Drawing2D.prototype.translate = function(x, y) {
|
rob@100
|
12876 modelView.translate(x, y);
|
rob@100
|
12877 modelViewInv.invTranslate(x, y);
|
rob@100
|
12878 curContext.translate(x, y);
|
rob@100
|
12879 };
|
rob@100
|
12880
|
rob@100
|
12881 Drawing3D.prototype.translate = function(x, y, z) {
|
rob@100
|
12882 modelView.translate(x, y, z);
|
rob@100
|
12883 modelViewInv.invTranslate(x, y, z);
|
rob@100
|
12884 };
|
rob@100
|
12885
|
rob@100
|
12886 /**
|
rob@100
|
12887 * Increases or decreases the size of a shape by expanding and contracting vertices. Objects always scale from their
|
rob@100
|
12888 * relative origin to the coordinate system. Scale values are specified as decimal percentages. For example, the
|
rob@100
|
12889 * function call scale(2.0) increases the dimension of a shape by 200%. Transformations apply to everything that
|
rob@100
|
12890 * happens after and subsequent calls to the function multiply the effect. For example, calling scale(2.0) and
|
rob@100
|
12891 * then scale(1.5) is the same as scale(3.0). If scale() is called within draw(), the transformation is reset when
|
rob@100
|
12892 * the loop begins again. Using this fuction with the z parameter requires passing P3D or OPENGL into the size()
|
rob@100
|
12893 * parameter as shown in the example above. This function can be further controlled by pushMatrix() and popMatrix().
|
rob@100
|
12894 *
|
rob@100
|
12895 * @param {int|float} size percentage to scale the object
|
rob@100
|
12896 * @param {int|float} x percentage to scale the object in the x-axis
|
rob@100
|
12897 * @param {int|float} y percentage to scale the object in the y-axis
|
rob@100
|
12898 * @param {int|float} z percentage to scale the object in the z-axis
|
rob@100
|
12899 *
|
rob@100
|
12900 * @returns none
|
rob@100
|
12901 *
|
rob@100
|
12902 * @see pushMatrix
|
rob@100
|
12903 * @see popMatrix
|
rob@100
|
12904 * @see translate
|
rob@100
|
12905 * @see rotate
|
rob@100
|
12906 * @see rotateX
|
rob@100
|
12907 * @see rotateY
|
rob@100
|
12908 * @see rotateZ
|
rob@100
|
12909 */
|
rob@100
|
12910 Drawing2D.prototype.scale = function(x, y) {
|
rob@100
|
12911 modelView.scale(x, y);
|
rob@100
|
12912 modelViewInv.invScale(x, y);
|
rob@100
|
12913 curContext.scale(x, y || x);
|
rob@100
|
12914 };
|
rob@100
|
12915
|
rob@100
|
12916 Drawing3D.prototype.scale = function(x, y, z) {
|
rob@100
|
12917 modelView.scale(x, y, z);
|
rob@100
|
12918 modelViewInv.invScale(x, y, z);
|
rob@100
|
12919 };
|
rob@100
|
12920
|
rob@100
|
12921
|
rob@100
|
12922 /**
|
rob@100
|
12923 * helper function for applying a transfrom matrix to a 2D context.
|
rob@100
|
12924 */
|
rob@100
|
12925 Drawing2D.prototype.transform = function(pmatrix) {
|
rob@100
|
12926 var e = pmatrix.array();
|
rob@100
|
12927 curContext.transform(e[0],e[3],e[1],e[4],e[2],e[5]);
|
rob@100
|
12928 };
|
rob@100
|
12929
|
rob@100
|
12930 /**
|
rob@100
|
12931 * helper function for applying a transfrom matrix to a 3D context.
|
rob@100
|
12932 * not currently implemented.
|
rob@100
|
12933 */
|
rob@100
|
12934 Drawing3D.prototype.transformm = function(pmatrix3d) {
|
rob@100
|
12935 throw("p.transform is currently not supported in 3D mode");
|
rob@100
|
12936 };
|
rob@100
|
12937
|
rob@100
|
12938
|
rob@100
|
12939 /**
|
rob@100
|
12940 * Pushes the current transformation matrix onto the matrix stack. Understanding pushMatrix() and popMatrix()
|
rob@100
|
12941 * requires understanding the concept of a matrix stack. The pushMatrix() function saves the current coordinate
|
rob@100
|
12942 * system to the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are
|
rob@100
|
12943 * used in conjuction with the other transformation methods and may be embedded to control the scope of
|
rob@100
|
12944 * the transformations.
|
rob@100
|
12945 *
|
rob@100
|
12946 * @returns none
|
rob@100
|
12947 *
|
rob@100
|
12948 * @see popMatrix
|
rob@100
|
12949 * @see translate
|
rob@100
|
12950 * @see rotate
|
rob@100
|
12951 * @see rotateX
|
rob@100
|
12952 * @see rotateY
|
rob@100
|
12953 * @see rotateZ
|
rob@100
|
12954 */
|
rob@100
|
12955 Drawing2D.prototype.pushMatrix = function() {
|
rob@100
|
12956 userMatrixStack.load(modelView);
|
rob@100
|
12957 userReverseMatrixStack.load(modelViewInv);
|
rob@100
|
12958 saveContext();
|
rob@100
|
12959 };
|
rob@100
|
12960
|
rob@100
|
12961 Drawing3D.prototype.pushMatrix = function() {
|
rob@100
|
12962 userMatrixStack.load(modelView);
|
rob@100
|
12963 userReverseMatrixStack.load(modelViewInv);
|
rob@100
|
12964 };
|
rob@100
|
12965
|
rob@100
|
12966 /**
|
rob@100
|
12967 * Pops the current transformation matrix off the matrix stack. Understanding pushing and popping requires
|
rob@100
|
12968 * understanding the concept of a matrix stack. The pushMatrix() function saves the current coordinate system to
|
rob@100
|
12969 * the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are used in
|
rob@100
|
12970 * conjuction with the other transformation methods and may be embedded to control the scope of the transformations.
|
rob@100
|
12971 *
|
rob@100
|
12972 * @returns none
|
rob@100
|
12973 *
|
rob@100
|
12974 * @see popMatrix
|
rob@100
|
12975 * @see pushMatrix
|
rob@100
|
12976 */
|
rob@100
|
12977 Drawing2D.prototype.popMatrix = function() {
|
rob@100
|
12978 modelView.set(userMatrixStack.pop());
|
rob@100
|
12979 modelViewInv.set(userReverseMatrixStack.pop());
|
rob@100
|
12980 restoreContext();
|
rob@100
|
12981 };
|
rob@100
|
12982
|
rob@100
|
12983 Drawing3D.prototype.popMatrix = function() {
|
rob@100
|
12984 modelView.set(userMatrixStack.pop());
|
rob@100
|
12985 modelViewInv.set(userReverseMatrixStack.pop());
|
rob@100
|
12986 };
|
rob@100
|
12987
|
rob@100
|
12988 /**
|
rob@100
|
12989 * Replaces the current matrix with the identity matrix. The equivalent function in OpenGL is glLoadIdentity().
|
rob@100
|
12990 *
|
rob@100
|
12991 * @returns none
|
rob@100
|
12992 *
|
rob@100
|
12993 * @see popMatrix
|
rob@100
|
12994 * @see pushMatrix
|
rob@100
|
12995 * @see applyMatrix
|
rob@100
|
12996 * @see printMatrix
|
rob@100
|
12997 */
|
rob@100
|
12998 Drawing2D.prototype.resetMatrix = function() {
|
rob@100
|
12999 modelView.reset();
|
rob@100
|
13000 modelViewInv.reset();
|
rob@100
|
13001 curContext.setTransform(1,0,0,1,0,0);
|
rob@100
|
13002 };
|
rob@100
|
13003
|
rob@100
|
13004 Drawing3D.prototype.resetMatrix = function() {
|
rob@100
|
13005 modelView.reset();
|
rob@100
|
13006 modelViewInv.reset();
|
rob@100
|
13007 };
|
rob@100
|
13008
|
rob@100
|
13009 /**
|
rob@100
|
13010 * Multiplies the current matrix by the one specified through the parameters. This is very slow because it will
|
rob@100
|
13011 * try to calculate the inverse of the transform, so avoid it whenever possible. The equivalent function
|
rob@100
|
13012 * in OpenGL is glMultMatrix().
|
rob@100
|
13013 *
|
rob@100
|
13014 * @param {int|float} n00-n15 numbers which define the 4x4 matrix to be multiplied
|
rob@100
|
13015 *
|
rob@100
|
13016 * @returns none
|
rob@100
|
13017 *
|
rob@100
|
13018 * @see popMatrix
|
rob@100
|
13019 * @see pushMatrix
|
rob@100
|
13020 * @see resetMatrix
|
rob@100
|
13021 * @see printMatrix
|
rob@100
|
13022 */
|
rob@100
|
13023 DrawingShared.prototype.applyMatrix = function() {
|
rob@100
|
13024 var a = arguments;
|
rob@100
|
13025 modelView.apply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
|
rob@100
|
13026 modelViewInv.invApply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
|
rob@100
|
13027 };
|
rob@100
|
13028
|
rob@100
|
13029 Drawing2D.prototype.applyMatrix = function() {
|
rob@100
|
13030 var a = arguments;
|
rob@100
|
13031 for (var cnt = a.length; cnt < 16; cnt++) {
|
rob@100
|
13032 a[cnt] = 0;
|
rob@100
|
13033 }
|
rob@100
|
13034 a[10] = a[15] = 1;
|
rob@100
|
13035 DrawingShared.prototype.applyMatrix.apply(this, a);
|
rob@100
|
13036 };
|
rob@100
|
13037
|
rob@100
|
13038 /**
|
rob@100
|
13039 * Rotates a shape around the x-axis the amount specified by the angle parameter. Angles should be
|
rob@100
|
13040 * specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
|
rob@100
|
13041 * Objects are always rotated around their relative position to the origin and positive numbers
|
rob@100
|
13042 * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
|
rob@100
|
13043 * after and subsequent calls to the function accumulates the effect. For example, calling rotateX(PI/2)
|
rob@100
|
13044 * and then rotateX(PI/2) is the same as rotateX(PI). If rotateX() is called within the draw(), the
|
rob@100
|
13045 * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
|
rob@100
|
13046 * into the size() parameter as shown in the example above.
|
rob@100
|
13047 *
|
rob@100
|
13048 * @param {int|float} angleInRadians angle of rotation specified in radians
|
rob@100
|
13049 *
|
rob@100
|
13050 * @returns none
|
rob@100
|
13051 *
|
rob@100
|
13052 * @see rotateY
|
rob@100
|
13053 * @see rotateZ
|
rob@100
|
13054 * @see rotate
|
rob@100
|
13055 * @see translate
|
rob@100
|
13056 * @see scale
|
rob@100
|
13057 * @see popMatrix
|
rob@100
|
13058 * @see pushMatrix
|
rob@100
|
13059 */
|
rob@100
|
13060 p.rotateX = function(angleInRadians) {
|
rob@100
|
13061 modelView.rotateX(angleInRadians);
|
rob@100
|
13062 modelViewInv.invRotateX(angleInRadians);
|
rob@100
|
13063 };
|
rob@100
|
13064
|
rob@100
|
13065 /**
|
rob@100
|
13066 * Rotates a shape around the z-axis the amount specified by the angle parameter. Angles should be
|
rob@100
|
13067 * specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
|
rob@100
|
13068 * Objects are always rotated around their relative position to the origin and positive numbers
|
rob@100
|
13069 * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
|
rob@100
|
13070 * after and subsequent calls to the function accumulates the effect. For example, calling rotateZ(PI/2)
|
rob@100
|
13071 * and then rotateZ(PI/2) is the same as rotateZ(PI). If rotateZ() is called within the draw(), the
|
rob@100
|
13072 * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
|
rob@100
|
13073 * into the size() parameter as shown in the example above.
|
rob@100
|
13074 *
|
rob@100
|
13075 * @param {int|float} angleInRadians angle of rotation specified in radians
|
rob@100
|
13076 *
|
rob@100
|
13077 * @returns none
|
rob@100
|
13078 *
|
rob@100
|
13079 * @see rotateX
|
rob@100
|
13080 * @see rotateY
|
rob@100
|
13081 * @see rotate
|
rob@100
|
13082 * @see translate
|
rob@100
|
13083 * @see scale
|
rob@100
|
13084 * @see popMatrix
|
rob@100
|
13085 * @see pushMatrix
|
rob@100
|
13086 */
|
rob@100
|
13087 Drawing2D.prototype.rotateZ = function() {
|
rob@100
|
13088 throw "rotateZ() is not supported in 2D mode. Use rotate(float) instead.";
|
rob@100
|
13089 };
|
rob@100
|
13090
|
rob@100
|
13091 Drawing3D.prototype.rotateZ = function(angleInRadians) {
|
rob@100
|
13092 modelView.rotateZ(angleInRadians);
|
rob@100
|
13093 modelViewInv.invRotateZ(angleInRadians);
|
rob@100
|
13094 };
|
rob@100
|
13095
|
rob@100
|
13096 /**
|
rob@100
|
13097 * Rotates a shape around the y-axis the amount specified by the angle parameter. Angles should be
|
rob@100
|
13098 * specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
|
rob@100
|
13099 * Objects are always rotated around their relative position to the origin and positive numbers
|
rob@100
|
13100 * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
|
rob@100
|
13101 * after and subsequent calls to the function accumulates the effect. For example, calling rotateY(PI/2)
|
rob@100
|
13102 * and then rotateY(PI/2) is the same as rotateY(PI). If rotateY() is called within the draw(), the
|
rob@100
|
13103 * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
|
rob@100
|
13104 * into the size() parameter as shown in the example above.
|
rob@100
|
13105 *
|
rob@100
|
13106 * @param {int|float} angleInRadians angle of rotation specified in radians
|
rob@100
|
13107 *
|
rob@100
|
13108 * @returns none
|
rob@100
|
13109 *
|
rob@100
|
13110 * @see rotateX
|
rob@100
|
13111 * @see rotateZ
|
rob@100
|
13112 * @see rotate
|
rob@100
|
13113 * @see translate
|
rob@100
|
13114 * @see scale
|
rob@100
|
13115 * @see popMatrix
|
rob@100
|
13116 * @see pushMatrix
|
rob@100
|
13117 */
|
rob@100
|
13118 p.rotateY = function(angleInRadians) {
|
rob@100
|
13119 modelView.rotateY(angleInRadians);
|
rob@100
|
13120 modelViewInv.invRotateY(angleInRadians);
|
rob@100
|
13121 };
|
rob@100
|
13122
|
rob@100
|
13123 /**
|
rob@100
|
13124 * Rotates a shape the amount specified by the angle parameter. Angles should be specified in radians
|
rob@100
|
13125 * (values from 0 to TWO_PI) or converted to radians with the radians() function. Objects are always
|
rob@100
|
13126 * rotated around their relative position to the origin and positive numbers rotate objects in a
|
rob@100
|
13127 * clockwise direction. Transformations apply to everything that happens after and subsequent calls
|
rob@100
|
13128 * to the function accumulates the effect. For example, calling rotate(HALF_PI) and then rotate(HALF_PI)
|
rob@100
|
13129 * is the same as rotate(PI). All tranformations are reset when draw() begins again. Technically,
|
rob@100
|
13130 * rotate() multiplies the current transformation matrix by a rotation matrix. This function can be
|
rob@100
|
13131 * further controlled by the pushMatrix() and popMatrix().
|
rob@100
|
13132 *
|
rob@100
|
13133 * @param {int|float} angleInRadians angle of rotation specified in radians
|
rob@100
|
13134 *
|
rob@100
|
13135 * @returns none
|
rob@100
|
13136 *
|
rob@100
|
13137 * @see rotateX
|
rob@100
|
13138 * @see rotateY
|
rob@100
|
13139 * @see rotateZ
|
rob@100
|
13140 * @see rotate
|
rob@100
|
13141 * @see translate
|
rob@100
|
13142 * @see scale
|
rob@100
|
13143 * @see popMatrix
|
rob@100
|
13144 * @see pushMatrix
|
rob@100
|
13145 */
|
rob@100
|
13146 Drawing2D.prototype.rotate = function(angleInRadians) {
|
rob@100
|
13147 modelView.rotateZ(angleInRadians);
|
rob@100
|
13148 modelViewInv.invRotateZ(angleInRadians);
|
rob@100
|
13149 curContext.rotate(angleInRadians);
|
rob@100
|
13150 };
|
rob@100
|
13151
|
rob@100
|
13152 Drawing3D.prototype.rotate = function(angleInRadians) {
|
rob@100
|
13153 if (arguments.length < 4) {
|
rob@100
|
13154 p.rotateZ(angleInRadians);
|
rob@100
|
13155 } else {
|
rob@100
|
13156 modelView.rotate(angleInRadians, arguments[1], arguments[2], arguments[3]);
|
rob@100
|
13157 modelViewInv.rotate((-angleInRadians), arguments[1], arguments[2], arguments[3]);
|
rob@100
|
13158 }
|
rob@100
|
13159 };
|
rob@100
|
13160
|
rob@100
|
13161 /**
|
rob@100
|
13162 * Shears a shape around the x-axis the amount specified by the angle parameter.
|
rob@100
|
13163 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians
|
rob@100
|
13164 * with the radians() function. Objects are always sheared around their relative position
|
rob@100
|
13165 * to the origin and positive numbers shear objects in a clockwise direction. Transformations
|
rob@100
|
13166 * apply to everything that happens after and subsequent calls to the function accumulates the
|
rob@100
|
13167 * effect. For example, calling shearX(PI/2) and then shearX(PI/2) is the same as shearX(PI)
|
rob@100
|
13168 *
|
rob@100
|
13169 * @param {int|float} angleInRadians angle of rotation specified in radians
|
rob@100
|
13170 *
|
rob@100
|
13171 * @returns none
|
rob@100
|
13172 *
|
rob@100
|
13173 * @see rotateX
|
rob@100
|
13174 * @see rotateY
|
rob@100
|
13175 * @see rotateZ
|
rob@100
|
13176 * @see rotate
|
rob@100
|
13177 * @see translate
|
rob@100
|
13178 * @see scale
|
rob@100
|
13179 * @see popMatrix
|
rob@100
|
13180 * @see pushMatrix
|
rob@100
|
13181 */
|
rob@100
|
13182
|
rob@100
|
13183 Drawing2D.prototype.shearX = function(angleInRadians) {
|
rob@100
|
13184 modelView.shearX(angleInRadians);
|
rob@100
|
13185 curContext.transform(1,0,angleInRadians,1,0,0);
|
rob@100
|
13186 };
|
rob@100
|
13187
|
rob@100
|
13188 Drawing3D.prototype.shearX = function(angleInRadians) {
|
rob@100
|
13189 modelView.shearX(angleInRadians);
|
rob@100
|
13190 };
|
rob@100
|
13191
|
rob@100
|
13192 /**
|
rob@100
|
13193 * Shears a shape around the y-axis the amount specified by the angle parameter.
|
rob@100
|
13194 * Angles should be specified in radians (values from 0 to PI*2) or converted to
|
rob@100
|
13195 * radians with the radians() function. Objects are always sheared around their
|
rob@100
|
13196 * relative position to the origin and positive numbers shear objects in a
|
rob@100
|
13197 * clockwise direction. Transformations apply to everything that happens after
|
rob@100
|
13198 * and subsequent calls to the function accumulates the effect. For example,
|
rob@100
|
13199 * calling shearY(PI/2) and then shearY(PI/2) is the same as shearY(PI).
|
rob@100
|
13200 *
|
rob@100
|
13201 * @param {int|float} angleInRadians angle of rotation specified in radians
|
rob@100
|
13202 *
|
rob@100
|
13203 * @returns none
|
rob@100
|
13204 *
|
rob@100
|
13205 * @see rotateX
|
rob@100
|
13206 * @see rotateY
|
rob@100
|
13207 * @see rotateZ
|
rob@100
|
13208 * @see rotate
|
rob@100
|
13209 * @see translate
|
rob@100
|
13210 * @see scale
|
rob@100
|
13211 * @see popMatrix
|
rob@100
|
13212 * @see pushMatrix
|
rob@100
|
13213 * @see shearX
|
rob@100
|
13214 */
|
rob@100
|
13215
|
rob@100
|
13216 Drawing2D.prototype.shearY = function(angleInRadians) {
|
rob@100
|
13217 modelView.shearY(angleInRadians);
|
rob@100
|
13218 curContext.transform(1,angleInRadians,0,1,0,0);
|
rob@100
|
13219 };
|
rob@100
|
13220
|
rob@100
|
13221 Drawing3D.prototype.shearY = function(angleInRadians) {
|
rob@100
|
13222 modelView.shearY(angleInRadians);
|
rob@100
|
13223 };
|
rob@100
|
13224
|
rob@100
|
13225 /**
|
rob@100
|
13226 * The pushStyle() function saves the current style settings and popStyle() restores the prior settings.
|
rob@100
|
13227 * Note that these functions are always used together. They allow you to change the style settings and later
|
rob@100
|
13228 * return to what you had. When a new style is started with pushStyle(), it builds on the current style information.
|
rob@100
|
13229 * The pushStyle() and popStyle() functions can be embedded to provide more control (see the second example
|
rob@100
|
13230 * above for a demonstration.)
|
rob@100
|
13231 * The style information controlled by the following functions are included in the style: fill(), stroke(), tint(),
|
rob@100
|
13232 * strokeWeight(), strokeCap(), strokeJoin(), imageMode(), rectMode(), ellipseMode(), shapeMode(), colorMode(),
|
rob@100
|
13233 * textAlign(), textFont(), textMode(), textSize(), textLeading(), emissive(), specular(), shininess(), ambient()
|
rob@100
|
13234 *
|
rob@100
|
13235 * @returns none
|
rob@100
|
13236 *
|
rob@100
|
13237 * @see popStyle
|
rob@100
|
13238 */
|
rob@100
|
13239 p.pushStyle = function() {
|
rob@100
|
13240 // Save the canvas state.
|
rob@100
|
13241 saveContext();
|
rob@100
|
13242
|
rob@100
|
13243 p.pushMatrix();
|
rob@100
|
13244
|
rob@100
|
13245 var newState = {
|
rob@100
|
13246 'doFill': doFill,
|
rob@100
|
13247 'currentFillColor': currentFillColor,
|
rob@100
|
13248 'doStroke': doStroke,
|
rob@100
|
13249 'currentStrokeColor': currentStrokeColor,
|
rob@100
|
13250 'curTint': curTint,
|
rob@100
|
13251 'curRectMode': curRectMode,
|
rob@100
|
13252 'curColorMode': curColorMode,
|
rob@100
|
13253 'colorModeX': colorModeX,
|
rob@100
|
13254 'colorModeZ': colorModeZ,
|
rob@100
|
13255 'colorModeY': colorModeY,
|
rob@100
|
13256 'colorModeA': colorModeA,
|
rob@100
|
13257 'curTextFont': curTextFont,
|
rob@100
|
13258 'horizontalTextAlignment': horizontalTextAlignment,
|
rob@100
|
13259 'verticalTextAlignment': verticalTextAlignment,
|
rob@100
|
13260 'textMode': textMode,
|
rob@100
|
13261 'curFontName': curFontName,
|
rob@100
|
13262 'curTextSize': curTextSize,
|
rob@100
|
13263 'curTextAscent': curTextAscent,
|
rob@100
|
13264 'curTextDescent': curTextDescent,
|
rob@100
|
13265 'curTextLeading': curTextLeading
|
rob@100
|
13266 };
|
rob@100
|
13267
|
rob@100
|
13268 styleArray.push(newState);
|
rob@100
|
13269 };
|
rob@100
|
13270
|
rob@100
|
13271 /**
|
rob@100
|
13272 * The pushStyle() function saves the current style settings and popStyle() restores the prior settings; these
|
rob@100
|
13273 * functions are always used together. They allow you to change the style settings and later return to what you had.
|
rob@100
|
13274 * When a new style is started with pushStyle(), it builds on the current style information. The pushStyle() and
|
rob@100
|
13275 * popStyle() functions can be embedded to provide more control (see the second example above for a demonstration.)
|
rob@100
|
13276 *
|
rob@100
|
13277 * @returns none
|
rob@100
|
13278 *
|
rob@100
|
13279 * @see pushStyle
|
rob@100
|
13280 */
|
rob@100
|
13281 p.popStyle = function() {
|
rob@100
|
13282 var oldState = styleArray.pop();
|
rob@100
|
13283
|
rob@100
|
13284 if (oldState) {
|
rob@100
|
13285 restoreContext();
|
rob@100
|
13286
|
rob@100
|
13287 p.popMatrix();
|
rob@100
|
13288
|
rob@100
|
13289 doFill = oldState.doFill;
|
rob@100
|
13290 currentFillColor = oldState.currentFillColor;
|
rob@100
|
13291 doStroke = oldState.doStroke;
|
rob@100
|
13292 currentStrokeColor = oldState.currentStrokeColor;
|
rob@100
|
13293 curTint = oldState.curTint;
|
rob@100
|
13294 curRectMode = oldState.curRectMode;
|
rob@100
|
13295 curColorMode = oldState.curColorMode;
|
rob@100
|
13296 colorModeX = oldState.colorModeX;
|
rob@100
|
13297 colorModeZ = oldState.colorModeZ;
|
rob@100
|
13298 colorModeY = oldState.colorModeY;
|
rob@100
|
13299 colorModeA = oldState.colorModeA;
|
rob@100
|
13300 curTextFont = oldState.curTextFont;
|
rob@100
|
13301 curFontName = oldState.curFontName;
|
rob@100
|
13302 curTextSize = oldState.curTextSize;
|
rob@100
|
13303 horizontalTextAlignment = oldState.horizontalTextAlignment;
|
rob@100
|
13304 verticalTextAlignment = oldState.verticalTextAlignment;
|
rob@100
|
13305 textMode = oldState.textMode;
|
rob@100
|
13306 curTextAscent = oldState.curTextAscent;
|
rob@100
|
13307 curTextDescent = oldState.curTextDescent;
|
rob@100
|
13308 curTextLeading = oldState.curTextLeading;
|
rob@100
|
13309 } else {
|
rob@100
|
13310 throw "Too many popStyle() without enough pushStyle()";
|
rob@100
|
13311 }
|
rob@100
|
13312 };
|
rob@100
|
13313
|
rob@100
|
13314 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
13315 // Time based functions
|
rob@100
|
13316 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
13317
|
rob@100
|
13318 /**
|
rob@100
|
13319 * Processing communicates with the clock on your computer.
|
rob@100
|
13320 * The year() function returns the current year as an integer (2003, 2004, 2005, etc).
|
rob@100
|
13321 *
|
rob@100
|
13322 * @returns {float} The current year.
|
rob@100
|
13323 *
|
rob@100
|
13324 * @see millis
|
rob@100
|
13325 * @see second
|
rob@100
|
13326 * @see minute
|
rob@100
|
13327 * @see hour
|
rob@100
|
13328 * @see day
|
rob@100
|
13329 * @see month
|
rob@100
|
13330 */
|
rob@100
|
13331 p.year = function() {
|
rob@100
|
13332 return new Date().getFullYear();
|
rob@100
|
13333 };
|
rob@100
|
13334 /**
|
rob@100
|
13335 * Processing communicates with the clock on your computer.
|
rob@100
|
13336 * The month() function returns the current month as a value from 1 - 12.
|
rob@100
|
13337 *
|
rob@100
|
13338 * @returns {float} The current month.
|
rob@100
|
13339 *
|
rob@100
|
13340 * @see millis
|
rob@100
|
13341 * @see second
|
rob@100
|
13342 * @see minute
|
rob@100
|
13343 * @see hour
|
rob@100
|
13344 * @see day
|
rob@100
|
13345 * @see year
|
rob@100
|
13346 */
|
rob@100
|
13347 p.month = function() {
|
rob@100
|
13348 return new Date().getMonth() + 1;
|
rob@100
|
13349 };
|
rob@100
|
13350 /**
|
rob@100
|
13351 * Processing communicates with the clock on your computer.
|
rob@100
|
13352 * The day() function returns the current day as a value from 1 - 31.
|
rob@100
|
13353 *
|
rob@100
|
13354 * @returns {float} The current day.
|
rob@100
|
13355 *
|
rob@100
|
13356 * @see millis
|
rob@100
|
13357 * @see second
|
rob@100
|
13358 * @see minute
|
rob@100
|
13359 * @see hour
|
rob@100
|
13360 * @see month
|
rob@100
|
13361 * @see year
|
rob@100
|
13362 */
|
rob@100
|
13363 p.day = function() {
|
rob@100
|
13364 return new Date().getDate();
|
rob@100
|
13365 };
|
rob@100
|
13366 /**
|
rob@100
|
13367 * Processing communicates with the clock on your computer.
|
rob@100
|
13368 * The hour() function returns the current hour as a value from 0 - 23.
|
rob@100
|
13369 *
|
rob@100
|
13370 * @returns {float} The current hour.
|
rob@100
|
13371 *
|
rob@100
|
13372 * @see millis
|
rob@100
|
13373 * @see second
|
rob@100
|
13374 * @see minute
|
rob@100
|
13375 * @see month
|
rob@100
|
13376 * @see day
|
rob@100
|
13377 * @see year
|
rob@100
|
13378 */
|
rob@100
|
13379 p.hour = function() {
|
rob@100
|
13380 return new Date().getHours();
|
rob@100
|
13381 };
|
rob@100
|
13382 /**
|
rob@100
|
13383 * Processing communicates with the clock on your computer.
|
rob@100
|
13384 * The minute() function returns the current minute as a value from 0 - 59.
|
rob@100
|
13385 *
|
rob@100
|
13386 * @returns {float} The current minute.
|
rob@100
|
13387 *
|
rob@100
|
13388 * @see millis
|
rob@100
|
13389 * @see second
|
rob@100
|
13390 * @see month
|
rob@100
|
13391 * @see hour
|
rob@100
|
13392 * @see day
|
rob@100
|
13393 * @see year
|
rob@100
|
13394 */
|
rob@100
|
13395 p.minute = function() {
|
rob@100
|
13396 return new Date().getMinutes();
|
rob@100
|
13397 };
|
rob@100
|
13398 /**
|
rob@100
|
13399 * Processing communicates with the clock on your computer.
|
rob@100
|
13400 * The second() function returns the current second as a value from 0 - 59.
|
rob@100
|
13401 *
|
rob@100
|
13402 * @returns {float} The current minute.
|
rob@100
|
13403 *
|
rob@100
|
13404 * @see millis
|
rob@100
|
13405 * @see month
|
rob@100
|
13406 * @see minute
|
rob@100
|
13407 * @see hour
|
rob@100
|
13408 * @see day
|
rob@100
|
13409 * @see year
|
rob@100
|
13410 */
|
rob@100
|
13411 p.second = function() {
|
rob@100
|
13412 return new Date().getSeconds();
|
rob@100
|
13413 };
|
rob@100
|
13414 /**
|
rob@100
|
13415 * Returns the number of milliseconds (thousandths of a second) since starting a sketch.
|
rob@100
|
13416 * This information is often used for timing animation sequences.
|
rob@100
|
13417 *
|
rob@100
|
13418 * @returns {long} The number of milliseconds since starting the sketch.
|
rob@100
|
13419 *
|
rob@100
|
13420 * @see month
|
rob@100
|
13421 * @see second
|
rob@100
|
13422 * @see minute
|
rob@100
|
13423 * @see hour
|
rob@100
|
13424 * @see day
|
rob@100
|
13425 * @see year
|
rob@100
|
13426 */
|
rob@100
|
13427 p.millis = function() {
|
rob@100
|
13428 return Date.now() - start;
|
rob@100
|
13429 };
|
rob@100
|
13430
|
rob@100
|
13431 /**
|
rob@100
|
13432 * Executes the code within draw() one time. This functions allows the program to update
|
rob@100
|
13433 * the display window only when necessary, for example when an event registered by
|
rob@100
|
13434 * mousePressed() or keyPressed() occurs.
|
rob@100
|
13435 * In structuring a program, it only makes sense to call redraw() within events such as
|
rob@100
|
13436 * mousePressed(). This is because redraw() does not run draw() immediately (it only sets
|
rob@100
|
13437 * a flag that indicates an update is needed).
|
rob@100
|
13438 * Calling redraw() within draw() has no effect because draw() is continuously called anyway.
|
rob@100
|
13439 *
|
rob@100
|
13440 * @returns none
|
rob@100
|
13441 *
|
rob@100
|
13442 * @see noLoop
|
rob@100
|
13443 * @see loop
|
rob@100
|
13444 */
|
rob@100
|
13445 function redrawHelper() {
|
rob@100
|
13446 var sec = (Date.now() - timeSinceLastFPS) / 1000;
|
rob@100
|
13447 framesSinceLastFPS++;
|
rob@100
|
13448 var fps = framesSinceLastFPS / sec;
|
rob@100
|
13449
|
rob@100
|
13450 // recalculate FPS every half second for better accuracy.
|
rob@100
|
13451 if (sec > 0.5) {
|
rob@100
|
13452 timeSinceLastFPS = Date.now();
|
rob@100
|
13453 framesSinceLastFPS = 0;
|
rob@100
|
13454 p.__frameRate = fps;
|
rob@100
|
13455 }
|
rob@100
|
13456
|
rob@100
|
13457 p.frameCount++;
|
rob@100
|
13458 }
|
rob@100
|
13459
|
rob@100
|
13460 Drawing2D.prototype.redraw = function() {
|
rob@100
|
13461 redrawHelper();
|
rob@100
|
13462
|
rob@100
|
13463 curContext.lineWidth = lineWidth;
|
rob@100
|
13464 var pmouseXLastEvent = p.pmouseX,
|
rob@100
|
13465 pmouseYLastEvent = p.pmouseY;
|
rob@100
|
13466 p.pmouseX = pmouseXLastFrame;
|
rob@100
|
13467 p.pmouseY = pmouseYLastFrame;
|
rob@100
|
13468
|
rob@100
|
13469 saveContext();
|
rob@100
|
13470 p.draw();
|
rob@100
|
13471 restoreContext();
|
rob@100
|
13472
|
rob@100
|
13473 pmouseXLastFrame = p.mouseX;
|
rob@100
|
13474 pmouseYLastFrame = p.mouseY;
|
rob@100
|
13475 p.pmouseX = pmouseXLastEvent;
|
rob@100
|
13476 p.pmouseY = pmouseYLastEvent;
|
rob@100
|
13477 };
|
rob@100
|
13478
|
rob@100
|
13479 Drawing3D.prototype.redraw = function() {
|
rob@100
|
13480 redrawHelper();
|
rob@100
|
13481
|
rob@100
|
13482 var pmouseXLastEvent = p.pmouseX,
|
rob@100
|
13483 pmouseYLastEvent = p.pmouseY;
|
rob@100
|
13484 p.pmouseX = pmouseXLastFrame;
|
rob@100
|
13485 p.pmouseY = pmouseYLastFrame;
|
rob@100
|
13486 // even if the color buffer isn't cleared with background(),
|
rob@100
|
13487 // the depth buffer needs to be cleared regardless.
|
rob@100
|
13488 curContext.clear(curContext.DEPTH_BUFFER_BIT);
|
rob@100
|
13489 curContextCache = { attributes: {}, locations: {} };
|
rob@100
|
13490 // Delete all the lighting states and the materials the
|
rob@100
|
13491 // user set in the last draw() call.
|
rob@100
|
13492 p.noLights();
|
rob@100
|
13493 p.lightFalloff(1, 0, 0);
|
rob@100
|
13494 p.shininess(1);
|
rob@100
|
13495 p.ambient(255, 255, 255);
|
rob@100
|
13496 p.specular(0, 0, 0);
|
rob@100
|
13497 p.emissive(0, 0, 0);
|
rob@100
|
13498 p.camera();
|
rob@100
|
13499 p.draw();
|
rob@100
|
13500
|
rob@100
|
13501 pmouseXLastFrame = p.mouseX;
|
rob@100
|
13502 pmouseYLastFrame = p.mouseY;
|
rob@100
|
13503 p.pmouseX = pmouseXLastEvent;
|
rob@100
|
13504 p.pmouseY = pmouseYLastEvent;
|
rob@100
|
13505 };
|
rob@100
|
13506
|
rob@100
|
13507 /**
|
rob@100
|
13508 * Stops Processing from continuously executing the code within draw(). If loop() is
|
rob@100
|
13509 * called, the code in draw() begin to run continuously again. If using noLoop() in
|
rob@100
|
13510 * setup(), it should be the last line inside the block.
|
rob@100
|
13511 * When noLoop() is used, it's not possible to manipulate or access the screen inside event
|
rob@100
|
13512 * handling functions such as mousePressed() or keyPressed(). Instead, use those functions
|
rob@100
|
13513 * to call redraw() or loop(), which will run draw(), which can update the screen properly.
|
rob@100
|
13514 * This means that when noLoop() has been called, no drawing can happen, and functions like
|
rob@100
|
13515 * saveFrame() or loadPixels() may not be used.
|
rob@100
|
13516 * Note that if the sketch is resized, redraw() will be called to update the sketch, even
|
rob@100
|
13517 * after noLoop() has been specified. Otherwise, the sketch would enter an odd state until
|
rob@100
|
13518 * loop() was called.
|
rob@100
|
13519 *
|
rob@100
|
13520 * @returns none
|
rob@100
|
13521 *
|
rob@100
|
13522 * @see redraw
|
rob@100
|
13523 * @see draw
|
rob@100
|
13524 * @see loop
|
rob@100
|
13525 */
|
rob@100
|
13526 p.noLoop = function() {
|
rob@100
|
13527 doLoop = false;
|
rob@100
|
13528 loopStarted = false;
|
rob@100
|
13529 clearInterval(looping);
|
rob@100
|
13530 curSketch.onPause();
|
rob@100
|
13531 };
|
rob@100
|
13532
|
rob@100
|
13533 /**
|
rob@100
|
13534 * Causes Processing to continuously execute the code within draw(). If noLoop() is called,
|
rob@100
|
13535 * the code in draw() stops executing.
|
rob@100
|
13536 *
|
rob@100
|
13537 * @returns none
|
rob@100
|
13538 *
|
rob@100
|
13539 * @see noLoop
|
rob@100
|
13540 */
|
rob@100
|
13541 p.loop = function() {
|
rob@100
|
13542 if (loopStarted) {
|
rob@100
|
13543 return;
|
rob@100
|
13544 }
|
rob@100
|
13545
|
rob@100
|
13546 timeSinceLastFPS = Date.now();
|
rob@100
|
13547 framesSinceLastFPS = 0;
|
rob@100
|
13548
|
rob@100
|
13549 looping = window.setInterval(function() {
|
rob@100
|
13550 try {
|
rob@100
|
13551 curSketch.onFrameStart();
|
rob@100
|
13552 p.redraw();
|
rob@100
|
13553 curSketch.onFrameEnd();
|
rob@100
|
13554 } catch(e_loop) {
|
rob@100
|
13555 window.clearInterval(looping);
|
rob@100
|
13556 throw e_loop;
|
rob@100
|
13557 }
|
rob@100
|
13558 }, curMsPerFrame);
|
rob@100
|
13559 doLoop = true;
|
rob@100
|
13560 loopStarted = true;
|
rob@100
|
13561 curSketch.onLoop();
|
rob@100
|
13562 };
|
rob@100
|
13563
|
rob@100
|
13564 /**
|
rob@100
|
13565 * Specifies the number of frames to be displayed every second. If the processor is not
|
rob@100
|
13566 * fast enough to maintain the specified rate, it will not be achieved. For example, the
|
rob@100
|
13567 * function call frameRate(30) will attempt to refresh 30 times a second. It is recommended
|
rob@100
|
13568 * to set the frame rate within setup(). The default rate is 60 frames per second.
|
rob@100
|
13569 *
|
rob@100
|
13570 * @param {int} aRate number of frames per second.
|
rob@100
|
13571 *
|
rob@100
|
13572 * @returns none
|
rob@100
|
13573 *
|
rob@100
|
13574 * @see delay
|
rob@100
|
13575 */
|
rob@100
|
13576 p.frameRate = function(aRate) {
|
rob@100
|
13577 curFrameRate = aRate;
|
rob@100
|
13578 curMsPerFrame = 1000 / curFrameRate;
|
rob@100
|
13579
|
rob@100
|
13580 // clear and reset interval
|
rob@100
|
13581 if (doLoop) {
|
rob@100
|
13582 p.noLoop();
|
rob@100
|
13583 p.loop();
|
rob@100
|
13584 }
|
rob@100
|
13585 };
|
rob@100
|
13586
|
rob@100
|
13587 /**
|
rob@100
|
13588 * Quits/stops/exits the program.
|
rob@100
|
13589 * Rather than terminating immediately, exit() will cause the sketch to exit after draw()
|
rob@100
|
13590 * has completed (or after setup() completes if called during the setup() method).
|
rob@100
|
13591 *
|
rob@100
|
13592 * @returns none
|
rob@100
|
13593 */
|
rob@100
|
13594 p.exit = function() {
|
rob@100
|
13595 // cleanup
|
rob@100
|
13596 window.clearInterval(looping);
|
rob@100
|
13597 removeInstance(p.externals.canvas.id);
|
rob@100
|
13598 delete(curElement.onmousedown);
|
rob@100
|
13599
|
rob@100
|
13600 // Step through the libraries to detach them
|
rob@100
|
13601 for (var lib in Processing.lib) {
|
rob@100
|
13602 if (Processing.lib.hasOwnProperty(lib)) {
|
rob@100
|
13603 if (Processing.lib[lib].hasOwnProperty("detach")) {
|
rob@100
|
13604 Processing.lib[lib].detach(p);
|
rob@100
|
13605 }
|
rob@100
|
13606 }
|
rob@100
|
13607 }
|
rob@100
|
13608
|
rob@100
|
13609 // clean up all event handling
|
rob@100
|
13610 var i = eventHandlers.length;
|
rob@100
|
13611 while (i--) {
|
rob@100
|
13612 detachEventHandler(eventHandlers[i]);
|
rob@100
|
13613 }
|
rob@100
|
13614 curSketch.onExit();
|
rob@100
|
13615 };
|
rob@100
|
13616
|
rob@100
|
13617 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
13618 // MISC functions
|
rob@100
|
13619 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
13620
|
rob@100
|
13621 /**
|
rob@100
|
13622 * Sets the cursor to a predefined symbol, an image, or turns it on if already hidden.
|
rob@100
|
13623 * If you are trying to set an image as the cursor, it is recommended to make the size
|
rob@100
|
13624 * 16x16 or 32x32 pixels. It is not possible to load an image as the cursor if you are
|
rob@100
|
13625 * exporting your program for the Web. The values for parameters x and y must be less
|
rob@100
|
13626 * than the dimensions of the image.
|
rob@100
|
13627 *
|
rob@100
|
13628 * @param {MODE} MODE either ARROW, CROSS, HAND, MOVE, TEXT, WAIT
|
rob@100
|
13629 * @param {PImage} image any variable of type PImage
|
rob@100
|
13630 * @param {int} x the horizonal active spot of the cursor
|
rob@100
|
13631 * @param {int} y the vertical active spot of the cursor
|
rob@100
|
13632 *
|
rob@100
|
13633 * @returns none
|
rob@100
|
13634 *
|
rob@100
|
13635 * @see noCursor
|
rob@100
|
13636 */
|
rob@100
|
13637 p.cursor = function() {
|
rob@100
|
13638 if (arguments.length > 1 || (arguments.length === 1 && arguments[0] instanceof p.PImage)) {
|
rob@100
|
13639 var image = arguments[0],
|
rob@100
|
13640 x, y;
|
rob@100
|
13641 if (arguments.length >= 3) {
|
rob@100
|
13642 x = arguments[1];
|
rob@100
|
13643 y = arguments[2];
|
rob@100
|
13644 if (x < 0 || y < 0 || y >= image.height || x >= image.width) {
|
rob@100
|
13645 throw "x and y must be non-negative and less than the dimensions of the image";
|
rob@100
|
13646 }
|
rob@100
|
13647 } else {
|
rob@100
|
13648 x = image.width >>> 1;
|
rob@100
|
13649 y = image.height >>> 1;
|
rob@100
|
13650 }
|
rob@100
|
13651
|
rob@100
|
13652 // see https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
|
rob@100
|
13653 var imageDataURL = image.toDataURL();
|
rob@100
|
13654 var style = "url(\"" + imageDataURL + "\") " + x + " " + y + ", default";
|
rob@100
|
13655 curCursor = curElement.style.cursor = style;
|
rob@100
|
13656 } else if (arguments.length === 1) {
|
rob@100
|
13657 var mode = arguments[0];
|
rob@100
|
13658 curCursor = curElement.style.cursor = mode;
|
rob@100
|
13659 } else {
|
rob@100
|
13660 curCursor = curElement.style.cursor = oldCursor;
|
rob@100
|
13661 }
|
rob@100
|
13662 };
|
rob@100
|
13663
|
rob@100
|
13664 /**
|
rob@100
|
13665 * Hides the cursor from view.
|
rob@100
|
13666 *
|
rob@100
|
13667 * @returns none
|
rob@100
|
13668 *
|
rob@100
|
13669 * @see cursor
|
rob@100
|
13670 */
|
rob@100
|
13671 p.noCursor = function() {
|
rob@100
|
13672 curCursor = curElement.style.cursor = PConstants.NOCURSOR;
|
rob@100
|
13673 };
|
rob@100
|
13674
|
rob@100
|
13675 /**
|
rob@100
|
13676 * Links to a webpage either in the same window or in a new window. The complete URL
|
rob@100
|
13677 * must be specified.
|
rob@100
|
13678 *
|
rob@100
|
13679 * @param {String} href complete url as a String in quotes
|
rob@100
|
13680 * @param {String} target name of the window to load the URL as a string in quotes
|
rob@100
|
13681 *
|
rob@100
|
13682 * @returns none
|
rob@100
|
13683 */
|
rob@100
|
13684 p.link = function(href, target) {
|
rob@100
|
13685 if (target !== undef) {
|
rob@100
|
13686 window.open(href, target);
|
rob@100
|
13687 } else {
|
rob@100
|
13688 window.location = href;
|
rob@100
|
13689 }
|
rob@100
|
13690 };
|
rob@100
|
13691
|
rob@100
|
13692 // PGraphics methods
|
rob@100
|
13693 // These functions exist only for compatibility with P5
|
rob@100
|
13694 p.beginDraw = noop;
|
rob@100
|
13695 p.endDraw = noop;
|
rob@100
|
13696
|
rob@100
|
13697 /**
|
rob@100
|
13698 * This function takes content from a canvas and turns it into an ImageData object to be used with a PImage
|
rob@100
|
13699 *
|
rob@100
|
13700 * @returns {ImageData} ImageData object to attach to a PImage (1D array of pixel data)
|
rob@100
|
13701 *
|
rob@100
|
13702 * @see PImage
|
rob@100
|
13703 */
|
rob@100
|
13704 Drawing2D.prototype.toImageData = function(x, y, w, h) {
|
rob@100
|
13705 x = x !== undef ? x : 0;
|
rob@100
|
13706 y = y !== undef ? y : 0;
|
rob@100
|
13707 w = w !== undef ? w : p.width;
|
rob@100
|
13708 h = h !== undef ? h : p.height;
|
rob@100
|
13709 return curContext.getImageData(x, y, w, h);
|
rob@100
|
13710 };
|
rob@100
|
13711
|
rob@100
|
13712 Drawing3D.prototype.toImageData = function(x, y, w, h) {
|
rob@100
|
13713 x = x !== undef ? x : 0;
|
rob@100
|
13714 y = y !== undef ? y : 0;
|
rob@100
|
13715 w = w !== undef ? w : p.width;
|
rob@100
|
13716 h = h !== undef ? h : p.height;
|
rob@100
|
13717 var c = document.createElement("canvas"),
|
rob@100
|
13718 ctx = c.getContext("2d"),
|
rob@100
|
13719 obj = ctx.createImageData(w, h),
|
rob@100
|
13720 uBuff = new Uint8Array(w * h * 4);
|
rob@100
|
13721 curContext.readPixels(x, y, w, h, curContext.RGBA, curContext.UNSIGNED_BYTE, uBuff);
|
rob@100
|
13722 for (var i=0, ul=uBuff.length, obj_data=obj.data; i < ul; i++) {
|
rob@100
|
13723 obj_data[i] = uBuff[(h - 1 - Math.floor(i / 4 / w)) * w * 4 + (i % (w * 4))];
|
rob@100
|
13724 }
|
rob@100
|
13725 return obj;
|
rob@100
|
13726 };
|
rob@100
|
13727
|
rob@100
|
13728 /**
|
rob@100
|
13729 * Displays message in the browser's status area. This is the text area in the lower
|
rob@100
|
13730 * left corner of the browser. The status() function will only work when the
|
rob@100
|
13731 * Processing program is running in a web browser.
|
rob@100
|
13732 *
|
rob@100
|
13733 * @param {String} text any valid String
|
rob@100
|
13734 *
|
rob@100
|
13735 * @returns none
|
rob@100
|
13736 */
|
rob@100
|
13737 p.status = function(text) {
|
rob@100
|
13738 window.status = text;
|
rob@100
|
13739 };
|
rob@100
|
13740
|
rob@100
|
13741 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
13742 // Binary Functions
|
rob@100
|
13743 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
13744
|
rob@100
|
13745 /**
|
rob@100
|
13746 * Converts a byte, char, int, or color to a String containing the equivalent binary
|
rob@100
|
13747 * notation. For example color(0, 102, 153, 255) will convert to the String
|
rob@100
|
13748 * "11111111000000000110011010011001". This function can help make your geeky debugging
|
rob@100
|
13749 * sessions much happier.
|
rob@100
|
13750 *
|
rob@100
|
13751 * @param {byte|char|int|color} num byte, char, int, color: value to convert
|
rob@100
|
13752 * @param {int} numBits number of digits to return
|
rob@100
|
13753 *
|
rob@100
|
13754 * @returns {String}
|
rob@100
|
13755 *
|
rob@100
|
13756 * @see unhex
|
rob@100
|
13757 * @see hex
|
rob@100
|
13758 * @see unbinary
|
rob@100
|
13759 */
|
rob@100
|
13760 p.binary = function(num, numBits) {
|
rob@100
|
13761 var bit;
|
rob@100
|
13762 if (numBits > 0) {
|
rob@100
|
13763 bit = numBits;
|
rob@100
|
13764 } else if(num instanceof Char) {
|
rob@100
|
13765 bit = 16;
|
rob@100
|
13766 num |= 0; // making it int
|
rob@100
|
13767 } else {
|
rob@100
|
13768 // autodetect, skipping zeros
|
rob@100
|
13769 bit = 32;
|
rob@100
|
13770 while (bit > 1 && !((num >>> (bit - 1)) & 1)) {
|
rob@100
|
13771 bit--;
|
rob@100
|
13772 }
|
rob@100
|
13773 }
|
rob@100
|
13774 var result = "";
|
rob@100
|
13775 while (bit > 0) {
|
rob@100
|
13776 result += ((num >>> (--bit)) & 1) ? "1" : "0";
|
rob@100
|
13777 }
|
rob@100
|
13778 return result;
|
rob@100
|
13779 };
|
rob@100
|
13780
|
rob@100
|
13781 /**
|
rob@100
|
13782 * Converts a String representation of a binary number to its equivalent integer value.
|
rob@100
|
13783 * For example, unbinary("00001000") will return 8.
|
rob@100
|
13784 *
|
rob@100
|
13785 * @param {String} binaryString String
|
rob@100
|
13786 *
|
rob@100
|
13787 * @returns {Int}
|
rob@100
|
13788 *
|
rob@100
|
13789 * @see hex
|
rob@100
|
13790 * @see binary
|
rob@100
|
13791 * @see unbinary
|
rob@100
|
13792 */
|
rob@100
|
13793 p.unbinary = function(binaryString) {
|
rob@100
|
13794 var i = binaryString.length - 1, mask = 1, result = 0;
|
rob@100
|
13795 while (i >= 0) {
|
rob@100
|
13796 var ch = binaryString[i--];
|
rob@100
|
13797 if (ch !== '0' && ch !== '1') {
|
rob@100
|
13798 throw "the value passed into unbinary was not an 8 bit binary number";
|
rob@100
|
13799 }
|
rob@100
|
13800 if (ch === '1') {
|
rob@100
|
13801 result += mask;
|
rob@100
|
13802 }
|
rob@100
|
13803 mask <<= 1;
|
rob@100
|
13804 }
|
rob@100
|
13805 return result;
|
rob@100
|
13806 };
|
rob@100
|
13807
|
rob@100
|
13808 var decimalToHex = function(d, padding) {
|
rob@100
|
13809 //if there is no padding value added, default padding to 8 else go into while statement.
|
rob@100
|
13810 padding = (padding === undef || padding === null) ? padding = 8 : padding;
|
rob@100
|
13811 if (d < 0) {
|
rob@100
|
13812 d = 0xFFFFFFFF + d + 1;
|
rob@100
|
13813 }
|
rob@100
|
13814 var hex = Number(d).toString(16).toUpperCase();
|
rob@100
|
13815 while (hex.length < padding) {
|
rob@100
|
13816 hex = "0" + hex;
|
rob@100
|
13817 }
|
rob@100
|
13818 if (hex.length >= padding) {
|
rob@100
|
13819 hex = hex.substring(hex.length - padding, hex.length);
|
rob@100
|
13820 }
|
rob@100
|
13821 return hex;
|
rob@100
|
13822 };
|
rob@100
|
13823
|
rob@100
|
13824 // note: since we cannot keep track of byte, int types by default the returned string is 8 chars long
|
rob@100
|
13825 // if no 2nd argument is passed. closest compromise we can use to match java implementation Feb 5 2010
|
rob@100
|
13826 // also the char parser has issues with chars that are not digits or letters IE: !@#$%^&*
|
rob@100
|
13827 /**
|
rob@100
|
13828 * Converts a byte, char, int, or color to a String containing the equivalent hexadecimal notation.
|
rob@100
|
13829 * For example color(0, 102, 153, 255) will convert to the String "FF006699". This function can help
|
rob@100
|
13830 * make your geeky debugging sessions much happier.
|
rob@100
|
13831 *
|
rob@100
|
13832 * @param {byte|char|int|Color} value the value to turn into a hex string
|
rob@100
|
13833 * @param {int} digits the number of digits to return
|
rob@100
|
13834 *
|
rob@100
|
13835 * @returns {String}
|
rob@100
|
13836 *
|
rob@100
|
13837 * @see unhex
|
rob@100
|
13838 * @see binary
|
rob@100
|
13839 * @see unbinary
|
rob@100
|
13840 */
|
rob@100
|
13841 p.hex = function(value, len) {
|
rob@100
|
13842 if (arguments.length === 1) {
|
rob@100
|
13843 if (value instanceof Char) {
|
rob@100
|
13844 len = 4;
|
rob@100
|
13845 } else { // int or byte, indistinguishable at the moment, default to 8
|
rob@100
|
13846 len = 8;
|
rob@100
|
13847 }
|
rob@100
|
13848 }
|
rob@100
|
13849 return decimalToHex(value, len);
|
rob@100
|
13850 };
|
rob@100
|
13851
|
rob@100
|
13852 function unhexScalar(hex) {
|
rob@100
|
13853 var value = parseInt("0x" + hex, 16);
|
rob@100
|
13854
|
rob@100
|
13855 // correct for int overflow java expectation
|
rob@100
|
13856 if (value > 2147483647) {
|
rob@100
|
13857 value -= 4294967296;
|
rob@100
|
13858 }
|
rob@100
|
13859 return value;
|
rob@100
|
13860 }
|
rob@100
|
13861
|
rob@100
|
13862 /**
|
rob@100
|
13863 * Converts a String representation of a hexadecimal number to its equivalent integer value.
|
rob@100
|
13864 *
|
rob@100
|
13865 * @param {String} hex the hex string to convert to an int
|
rob@100
|
13866 *
|
rob@100
|
13867 * @returns {int}
|
rob@100
|
13868 *
|
rob@100
|
13869 * @see hex
|
rob@100
|
13870 * @see binary
|
rob@100
|
13871 * @see unbinary
|
rob@100
|
13872 */
|
rob@100
|
13873 p.unhex = function(hex) {
|
rob@100
|
13874 if (hex instanceof Array) {
|
rob@100
|
13875 var arr = [];
|
rob@100
|
13876 for (var i = 0; i < hex.length; i++) {
|
rob@100
|
13877 arr.push(unhexScalar(hex[i]));
|
rob@100
|
13878 }
|
rob@100
|
13879 return arr;
|
rob@100
|
13880 }
|
rob@100
|
13881 return unhexScalar(hex);
|
rob@100
|
13882 };
|
rob@100
|
13883
|
rob@100
|
13884 // Load a file or URL into strings
|
rob@100
|
13885 /**
|
rob@100
|
13886 * Reads the contents of a file or url and creates a String array of its individual lines.
|
rob@100
|
13887 * The filename parameter can also be a URL to a file found online. If the file is not available or an error occurs,
|
rob@100
|
13888 * null will be returned and an error message will be printed to the console. The error message does not halt
|
rob@100
|
13889 * the program.
|
rob@100
|
13890 *
|
rob@100
|
13891 * @param {String} filename name of the file or url to load
|
rob@100
|
13892 *
|
rob@100
|
13893 * @returns {String[]}
|
rob@100
|
13894 *
|
rob@100
|
13895 * @see loadBytes
|
rob@100
|
13896 * @see saveStrings
|
rob@100
|
13897 * @see saveBytes
|
rob@100
|
13898 */
|
rob@100
|
13899 p.loadStrings = function(filename) {
|
rob@100
|
13900 if (localStorage[filename]) {
|
rob@100
|
13901 return localStorage[filename].split("\n");
|
rob@100
|
13902 }
|
rob@100
|
13903
|
rob@100
|
13904 var filecontent = ajax(filename);
|
rob@100
|
13905 if(typeof filecontent !== "string" || filecontent === "") {
|
rob@100
|
13906 return [];
|
rob@100
|
13907 }
|
rob@100
|
13908
|
rob@100
|
13909 // deal with the fact that Windows uses \r\n, Unix uses \n,
|
rob@100
|
13910 // Mac uses \r, and we actually expect \n
|
rob@100
|
13911 filecontent = filecontent.replace(/(\r\n?)/g,"\n").replace(/\n$/,"");
|
rob@100
|
13912
|
rob@100
|
13913 return filecontent.split("\n");
|
rob@100
|
13914 };
|
rob@100
|
13915
|
rob@100
|
13916 // Writes an array of strings to a file, one line per string
|
rob@100
|
13917 /**
|
rob@100
|
13918 * Writes an array of strings to a file, one line per string. This file is saved to the localStorage.
|
rob@100
|
13919 *
|
rob@100
|
13920 * @param {String} filename name of the file to save to localStorage
|
rob@100
|
13921 * @param {String[]} strings string array to be written
|
rob@100
|
13922 *
|
rob@100
|
13923 * @see loadBytes
|
rob@100
|
13924 * @see loadStrings
|
rob@100
|
13925 * @see saveBytes
|
rob@100
|
13926 */
|
rob@100
|
13927 p.saveStrings = function(filename, strings) {
|
rob@100
|
13928 localStorage[filename] = strings.join('\n');
|
rob@100
|
13929 };
|
rob@100
|
13930
|
rob@100
|
13931 /**
|
rob@100
|
13932 * Reads the contents of a file or url and places it in a byte array. If a file is specified, it must be located in the localStorage.
|
rob@100
|
13933 * The filename parameter can also be a URL to a file found online.
|
rob@100
|
13934 *
|
rob@100
|
13935 * @param {String} filename name of a file in the localStorage or a URL.
|
rob@100
|
13936 *
|
rob@100
|
13937 * @returns {byte[]}
|
rob@100
|
13938 *
|
rob@100
|
13939 * @see loadStrings
|
rob@100
|
13940 * @see saveStrings
|
rob@100
|
13941 * @see saveBytes
|
rob@100
|
13942 */
|
rob@100
|
13943 p.loadBytes = function(url) {
|
rob@100
|
13944 var string = ajax(url);
|
rob@100
|
13945 var ret = [];
|
rob@100
|
13946
|
rob@100
|
13947 for (var i = 0; i < string.length; i++) {
|
rob@100
|
13948 ret.push(string.charCodeAt(i));
|
rob@100
|
13949 }
|
rob@100
|
13950
|
rob@100
|
13951 return ret;
|
rob@100
|
13952 };
|
rob@100
|
13953
|
rob@100
|
13954 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
13955 // String Functions
|
rob@100
|
13956 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
13957 /**
|
rob@100
|
13958 * The matchAll() function is identical to match(), except that it returns an array of all matches in
|
rob@100
|
13959 * the specified String, rather than just the first.
|
rob@100
|
13960 *
|
rob@100
|
13961 * @param {String} aString the String to search inside
|
rob@100
|
13962 * @param {String} aRegExp the regexp to be used for matching
|
rob@100
|
13963 *
|
rob@100
|
13964 * @return {String[]} returns an array of matches
|
rob@100
|
13965 *
|
rob@100
|
13966 * @see #match
|
rob@100
|
13967 */
|
rob@100
|
13968 p.matchAll = function(aString, aRegExp) {
|
rob@100
|
13969 var results = [],
|
rob@100
|
13970 latest;
|
rob@100
|
13971 var regexp = new RegExp(aRegExp, "g");
|
rob@100
|
13972 while ((latest = regexp.exec(aString)) !== null) {
|
rob@100
|
13973 results.push(latest);
|
rob@100
|
13974 if (latest[0].length === 0) {
|
rob@100
|
13975 ++regexp.lastIndex;
|
rob@100
|
13976 }
|
rob@100
|
13977 }
|
rob@100
|
13978 return results.length > 0 ? results : null;
|
rob@100
|
13979 };
|
rob@100
|
13980 /**
|
rob@100
|
13981 * The match() function matches a string with a regular expression, and returns the match as an
|
rob@100
|
13982 * array. The first index is the matching expression, and array elements
|
rob@100
|
13983 * [1] and higher represent each of the groups (sequences found in parens).
|
rob@100
|
13984 *
|
rob@100
|
13985 * @param {String} str the String to be searched
|
rob@100
|
13986 * @param {String} regexp the regexp to be used for matching
|
rob@100
|
13987 *
|
rob@100
|
13988 * @return {String[]} an array of matching strings
|
rob@100
|
13989 */
|
rob@100
|
13990 p.match = function(str, regexp) {
|
rob@100
|
13991 return str.match(regexp);
|
rob@100
|
13992 };
|
rob@100
|
13993
|
rob@100
|
13994 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
13995 // Other java specific functions
|
rob@100
|
13996 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
13997
|
rob@100
|
13998
|
rob@100
|
13999 var logBuffer = [];
|
rob@100
|
14000
|
rob@100
|
14001 /**
|
rob@100
|
14002 * The println() function writes to the console area of the Processing environment.
|
rob@100
|
14003 * Each call to this function creates a new line of output. Individual elements can be separated with quotes ("") and joined with the string concatenation operator (+).
|
rob@100
|
14004 *
|
rob@100
|
14005 * @param {String} message the string to write to the console
|
rob@100
|
14006 *
|
rob@100
|
14007 * @see #join
|
rob@100
|
14008 * @see #print
|
rob@100
|
14009 */
|
rob@100
|
14010 p.println = function(message) {
|
rob@100
|
14011 Processing.logger.println(message);
|
rob@100
|
14012 };
|
rob@100
|
14013 /**
|
rob@100
|
14014 * The print() function writes to the console area of the Processing environment.
|
rob@100
|
14015 *
|
rob@100
|
14016 * @param {String} message the string to write to the console
|
rob@100
|
14017 *
|
rob@100
|
14018 * @see #join
|
rob@100
|
14019 */
|
rob@100
|
14020 p.print = function(message) {
|
rob@100
|
14021 Processing.logger.print(message);
|
rob@100
|
14022 };
|
rob@100
|
14023
|
rob@100
|
14024 // Alphanumeric chars arguments automatically converted to numbers when
|
rob@100
|
14025 // passed in, and will come out as numbers.
|
rob@100
|
14026 p.str = function(val) {
|
rob@100
|
14027 if (val instanceof Array) {
|
rob@100
|
14028 var arr = [];
|
rob@100
|
14029 for (var i = 0; i < val.length; i++) {
|
rob@100
|
14030 arr.push(val[i].toString() + "");
|
rob@100
|
14031 }
|
rob@100
|
14032 return arr;
|
rob@100
|
14033 }
|
rob@100
|
14034 return (val.toString() + "");
|
rob@100
|
14035 };
|
rob@100
|
14036
|
rob@100
|
14037
|
rob@100
|
14038 // Conversion
|
rob@100
|
14039 function booleanScalar(val) {
|
rob@100
|
14040 if (typeof val === 'number') {
|
rob@100
|
14041 return val !== 0;
|
rob@100
|
14042 }
|
rob@100
|
14043 if (typeof val === 'boolean') {
|
rob@100
|
14044 return val;
|
rob@100
|
14045 }
|
rob@100
|
14046 if (typeof val === 'string') {
|
rob@100
|
14047 return val.toLowerCase() === 'true';
|
rob@100
|
14048 }
|
rob@100
|
14049 if (val instanceof Char) {
|
rob@100
|
14050 // 1, T or t
|
rob@100
|
14051 return val.code === 49 || val.code === 84 || val.code === 116;
|
rob@100
|
14052 }
|
rob@100
|
14053 }
|
rob@100
|
14054
|
rob@100
|
14055 /**
|
rob@100
|
14056 * Converts the passed parameter to the function to its boolean value.
|
rob@100
|
14057 * It will return an array of booleans if an array is passed in.
|
rob@100
|
14058 *
|
rob@100
|
14059 * @param {int, byte, string} val the parameter to be converted to boolean
|
rob@100
|
14060 * @param {int[], byte[], string[]} val the array to be converted to boolean[]
|
rob@100
|
14061 *
|
rob@100
|
14062 * @return {boolean|boolean[]} returns a boolean or an array of booleans
|
rob@100
|
14063 */
|
rob@100
|
14064 p.parseBoolean = function (val) {
|
rob@100
|
14065 if (val instanceof Array) {
|
rob@100
|
14066 var ret = [];
|
rob@100
|
14067 for (var i = 0; i < val.length; i++) {
|
rob@100
|
14068 ret.push(booleanScalar(val[i]));
|
rob@100
|
14069 }
|
rob@100
|
14070 return ret;
|
rob@100
|
14071 }
|
rob@100
|
14072 return booleanScalar(val);
|
rob@100
|
14073 };
|
rob@100
|
14074
|
rob@100
|
14075 /**
|
rob@100
|
14076 * Converts the passed parameter to the function to its byte value.
|
rob@100
|
14077 * A byte is a number between -128 and 127.
|
rob@100
|
14078 * It will return an array of bytes if an array is passed in.
|
rob@100
|
14079 *
|
rob@100
|
14080 * @param {int, char} what the parameter to be conveted to byte
|
rob@100
|
14081 * @param {int[], char[]} what the array to be converted to byte[]
|
rob@100
|
14082 *
|
rob@100
|
14083 * @return {byte|byte[]} returns a byte or an array of bytes
|
rob@100
|
14084 */
|
rob@100
|
14085 p.parseByte = function(what) {
|
rob@100
|
14086 if (what instanceof Array) {
|
rob@100
|
14087 var bytes = [];
|
rob@100
|
14088 for (var i = 0; i < what.length; i++) {
|
rob@100
|
14089 bytes.push((0 - (what[i] & 0x80)) | (what[i] & 0x7F));
|
rob@100
|
14090 }
|
rob@100
|
14091 return bytes;
|
rob@100
|
14092 }
|
rob@100
|
14093 return (0 - (what & 0x80)) | (what & 0x7F);
|
rob@100
|
14094 };
|
rob@100
|
14095
|
rob@100
|
14096 /**
|
rob@100
|
14097 * Converts the passed parameter to the function to its char value.
|
rob@100
|
14098 * It will return an array of chars if an array is passed in.
|
rob@100
|
14099 *
|
rob@100
|
14100 * @param {int, byte} key the parameter to be conveted to char
|
rob@100
|
14101 * @param {int[], byte[]} key the array to be converted to char[]
|
rob@100
|
14102 *
|
rob@100
|
14103 * @return {char|char[]} returns a char or an array of chars
|
rob@100
|
14104 */
|
rob@100
|
14105 p.parseChar = function(key) {
|
rob@100
|
14106 if (typeof key === "number") {
|
rob@100
|
14107 return new Char(String.fromCharCode(key & 0xFFFF));
|
rob@100
|
14108 }
|
rob@100
|
14109 if (key instanceof Array) {
|
rob@100
|
14110 var ret = [];
|
rob@100
|
14111 for (var i = 0; i < key.length; i++) {
|
rob@100
|
14112 ret.push(new Char(String.fromCharCode(key[i] & 0xFFFF)));
|
rob@100
|
14113 }
|
rob@100
|
14114 return ret;
|
rob@100
|
14115 }
|
rob@100
|
14116 throw "char() may receive only one argument of type int, byte, int[], or byte[].";
|
rob@100
|
14117 };
|
rob@100
|
14118
|
rob@100
|
14119 // Processing doc claims good argument types are: int, char, byte, boolean,
|
rob@100
|
14120 // String, int[], char[], byte[], boolean[], String[].
|
rob@100
|
14121 // floats should not work. However, floats with only zeroes right of the
|
rob@100
|
14122 // decimal will work because JS converts those to int.
|
rob@100
|
14123 function floatScalar(val) {
|
rob@100
|
14124 if (typeof val === 'number') {
|
rob@100
|
14125 return val;
|
rob@100
|
14126 }
|
rob@100
|
14127 if (typeof val === 'boolean') {
|
rob@100
|
14128 return val ? 1 : 0;
|
rob@100
|
14129 }
|
rob@100
|
14130 if (typeof val === 'string') {
|
rob@100
|
14131 return parseFloat(val);
|
rob@100
|
14132 }
|
rob@100
|
14133 if (val instanceof Char) {
|
rob@100
|
14134 return val.code;
|
rob@100
|
14135 }
|
rob@100
|
14136 }
|
rob@100
|
14137
|
rob@100
|
14138 /**
|
rob@100
|
14139 * Converts the passed parameter to the function to its float value.
|
rob@100
|
14140 * It will return an array of floats if an array is passed in.
|
rob@100
|
14141 *
|
rob@100
|
14142 * @param {int, char, boolean, string} val the parameter to be conveted to float
|
rob@100
|
14143 * @param {int[], char[], boolean[], string[]} val the array to be converted to float[]
|
rob@100
|
14144 *
|
rob@100
|
14145 * @return {float|float[]} returns a float or an array of floats
|
rob@100
|
14146 */
|
rob@100
|
14147 p.parseFloat = function(val) {
|
rob@100
|
14148 if (val instanceof Array) {
|
rob@100
|
14149 var ret = [];
|
rob@100
|
14150 for (var i = 0; i < val.length; i++) {
|
rob@100
|
14151 ret.push(floatScalar(val[i]));
|
rob@100
|
14152 }
|
rob@100
|
14153 return ret;
|
rob@100
|
14154 }
|
rob@100
|
14155 return floatScalar(val);
|
rob@100
|
14156 };
|
rob@100
|
14157
|
rob@100
|
14158 function intScalar(val, radix) {
|
rob@100
|
14159 if (typeof val === 'number') {
|
rob@100
|
14160 return val & 0xFFFFFFFF;
|
rob@100
|
14161 }
|
rob@100
|
14162 if (typeof val === 'boolean') {
|
rob@100
|
14163 return val ? 1 : 0;
|
rob@100
|
14164 }
|
rob@100
|
14165 if (typeof val === 'string') {
|
rob@100
|
14166 var number = parseInt(val, radix || 10); // Default to decimal radix.
|
rob@100
|
14167 return number & 0xFFFFFFFF;
|
rob@100
|
14168 }
|
rob@100
|
14169 if (val instanceof Char) {
|
rob@100
|
14170 return val.code;
|
rob@100
|
14171 }
|
rob@100
|
14172 }
|
rob@100
|
14173
|
rob@100
|
14174 /**
|
rob@100
|
14175 * Converts the passed parameter to the function to its int value.
|
rob@100
|
14176 * It will return an array of ints if an array is passed in.
|
rob@100
|
14177 *
|
rob@100
|
14178 * @param {string, char, boolean, float} val the parameter to be conveted to int
|
rob@100
|
14179 * @param {string[], char[], boolean[], float[]} val the array to be converted to int[]
|
rob@100
|
14180 * @param {int} radix optional the radix of the number (for js compatibility)
|
rob@100
|
14181 *
|
rob@100
|
14182 * @return {int|int[]} returns a int or an array of ints
|
rob@100
|
14183 */
|
rob@100
|
14184 p.parseInt = function(val, radix) {
|
rob@100
|
14185 if (val instanceof Array) {
|
rob@100
|
14186 var ret = [];
|
rob@100
|
14187 for (var i = 0; i < val.length; i++) {
|
rob@100
|
14188 if (typeof val[i] === 'string' && !/^\s*[+\-]?\d+\s*$/.test(val[i])) {
|
rob@100
|
14189 ret.push(0);
|
rob@100
|
14190 } else {
|
rob@100
|
14191 ret.push(intScalar(val[i], radix));
|
rob@100
|
14192 }
|
rob@100
|
14193 }
|
rob@100
|
14194 return ret;
|
rob@100
|
14195 }
|
rob@100
|
14196 return intScalar(val, radix);
|
rob@100
|
14197 };
|
rob@100
|
14198
|
rob@100
|
14199 p.__int_cast = function(val) {
|
rob@100
|
14200 return 0|val;
|
rob@100
|
14201 };
|
rob@100
|
14202
|
rob@100
|
14203 p.__instanceof = function(obj, type) {
|
rob@100
|
14204 if (typeof type !== "function") {
|
rob@100
|
14205 throw "Function is expected as type argument for instanceof operator";
|
rob@100
|
14206 }
|
rob@100
|
14207
|
rob@100
|
14208 if (typeof obj === "string") {
|
rob@100
|
14209 // special case for strings
|
rob@100
|
14210 return type === Object || type === String;
|
rob@100
|
14211 }
|
rob@100
|
14212
|
rob@100
|
14213 if (obj instanceof type) {
|
rob@100
|
14214 // fast check if obj is already of type instance
|
rob@100
|
14215 return true;
|
rob@100
|
14216 }
|
rob@100
|
14217
|
rob@100
|
14218 if (typeof obj !== "object" || obj === null) {
|
rob@100
|
14219 return false; // not an object or null
|
rob@100
|
14220 }
|
rob@100
|
14221
|
rob@100
|
14222 var objType = obj.constructor;
|
rob@100
|
14223 if (type.$isInterface) {
|
rob@100
|
14224 // expecting the interface
|
rob@100
|
14225 // queueing interfaces from type and its base classes
|
rob@100
|
14226 var interfaces = [];
|
rob@100
|
14227 while (objType) {
|
rob@100
|
14228 if (objType.$interfaces) {
|
rob@100
|
14229 interfaces = interfaces.concat(objType.$interfaces);
|
rob@100
|
14230 }
|
rob@100
|
14231 objType = objType.$base;
|
rob@100
|
14232 }
|
rob@100
|
14233 while (interfaces.length > 0) {
|
rob@100
|
14234 var i = interfaces.shift();
|
rob@100
|
14235 if (i === type) {
|
rob@100
|
14236 return true;
|
rob@100
|
14237 }
|
rob@100
|
14238 // wide search in base interfaces
|
rob@100
|
14239 if (i.$interfaces) {
|
rob@100
|
14240 interfaces = interfaces.concat(i.$interfaces);
|
rob@100
|
14241 }
|
rob@100
|
14242 }
|
rob@100
|
14243 return false;
|
rob@100
|
14244 }
|
rob@100
|
14245
|
rob@100
|
14246 while (objType.hasOwnProperty("$base")) {
|
rob@100
|
14247 objType = objType.$base;
|
rob@100
|
14248 if (objType === type) {
|
rob@100
|
14249 return true; // object was found
|
rob@100
|
14250 }
|
rob@100
|
14251 }
|
rob@100
|
14252
|
rob@100
|
14253 return false;
|
rob@100
|
14254 };
|
rob@100
|
14255
|
rob@100
|
14256 /**
|
rob@100
|
14257 * Defines the dimension of the display window in units of pixels. The size() function must
|
rob@100
|
14258 * be the first line in setup(). If size() is not called, the default size of the window is
|
rob@100
|
14259 * 100x100 pixels. The system variables width and height are set by the parameters passed to
|
rob@100
|
14260 * the size() function.
|
rob@100
|
14261 *
|
rob@100
|
14262 * @param {int} aWidth width of the display window in units of pixels
|
rob@100
|
14263 * @param {int} aHeight height of the display window in units of pixels
|
rob@100
|
14264 * @param {MODE} aMode Either P2D, P3D, JAVA2D, or OPENGL
|
rob@100
|
14265 *
|
rob@100
|
14266 * @see createGraphics
|
rob@100
|
14267 * @see screen
|
rob@100
|
14268 */
|
rob@100
|
14269 DrawingShared.prototype.size = function(aWidth, aHeight, aMode) {
|
rob@100
|
14270 if (doStroke) {
|
rob@100
|
14271 p.stroke(0);
|
rob@100
|
14272 }
|
rob@100
|
14273
|
rob@100
|
14274 if (doFill) {
|
rob@100
|
14275 p.fill(255);
|
rob@100
|
14276 }
|
rob@100
|
14277
|
rob@100
|
14278 // The default 2d context has already been created in the p.init() stage if
|
rob@100
|
14279 // a 3d context was not specified. This is so that a 2d context will be
|
rob@100
|
14280 // available if size() was not called.
|
rob@100
|
14281 var savedProperties = {
|
rob@100
|
14282 fillStyle: curContext.fillStyle,
|
rob@100
|
14283 strokeStyle: curContext.strokeStyle,
|
rob@100
|
14284 lineCap: curContext.lineCap,
|
rob@100
|
14285 lineJoin: curContext.lineJoin
|
rob@100
|
14286 };
|
rob@100
|
14287 // remove the style width and height properties to ensure that the canvas gets set to
|
rob@100
|
14288 // aWidth and aHeight coming in
|
rob@100
|
14289 if (curElement.style.length > 0 ) {
|
rob@100
|
14290 curElement.style.removeProperty("width");
|
rob@100
|
14291 curElement.style.removeProperty("height");
|
rob@100
|
14292 }
|
rob@100
|
14293
|
rob@100
|
14294 curElement.width = p.width = aWidth || 100;
|
rob@100
|
14295 curElement.height = p.height = aHeight || 100;
|
rob@100
|
14296
|
rob@100
|
14297 for (var prop in savedProperties) {
|
rob@100
|
14298 if (savedProperties.hasOwnProperty(prop)) {
|
rob@100
|
14299 curContext[prop] = savedProperties[prop];
|
rob@100
|
14300 }
|
rob@100
|
14301 }
|
rob@100
|
14302
|
rob@100
|
14303 // make sure to set the default font the first time round.
|
rob@100
|
14304 p.textFont(curTextFont);
|
rob@100
|
14305
|
rob@100
|
14306 // Set the background to whatever it was called last as if background() was called before size()
|
rob@100
|
14307 // If background() hasn't been called before, set background() to a light gray
|
rob@100
|
14308 p.background();
|
rob@100
|
14309
|
rob@100
|
14310 // set 5% for pixels to cache (or 1000)
|
rob@100
|
14311 maxPixelsCached = Math.max(1000, aWidth * aHeight * 0.05);
|
rob@100
|
14312
|
rob@100
|
14313 // Externalize the context
|
rob@100
|
14314 p.externals.context = curContext;
|
rob@100
|
14315
|
rob@100
|
14316 for (var i = 0; i < PConstants.SINCOS_LENGTH; i++) {
|
rob@100
|
14317 sinLUT[i] = p.sin(i * (PConstants.PI / 180) * 0.5);
|
rob@100
|
14318 cosLUT[i] = p.cos(i * (PConstants.PI / 180) * 0.5);
|
rob@100
|
14319 }
|
rob@100
|
14320 };
|
rob@100
|
14321
|
rob@100
|
14322 Drawing2D.prototype.size = function(aWidth, aHeight, aMode) {
|
rob@100
|
14323 if (curContext === undef) {
|
rob@100
|
14324 // size() was called without p.init() default context, i.e. p.createGraphics()
|
rob@100
|
14325 curContext = curElement.getContext("2d");
|
rob@100
|
14326 userMatrixStack = new PMatrixStack();
|
rob@100
|
14327 userReverseMatrixStack = new PMatrixStack();
|
rob@100
|
14328 modelView = new PMatrix2D();
|
rob@100
|
14329 modelViewInv = new PMatrix2D();
|
rob@100
|
14330 }
|
rob@100
|
14331
|
rob@100
|
14332 DrawingShared.prototype.size.apply(this, arguments);
|
rob@100
|
14333 };
|
rob@100
|
14334
|
rob@100
|
14335 Drawing3D.prototype.size = (function() {
|
rob@100
|
14336 var size3DCalled = false;
|
rob@100
|
14337
|
rob@100
|
14338 return function size(aWidth, aHeight, aMode) {
|
rob@100
|
14339 if (size3DCalled) {
|
rob@100
|
14340 throw "Multiple calls to size() for 3D renders are not allowed.";
|
rob@100
|
14341 }
|
rob@100
|
14342 size3DCalled = true;
|
rob@100
|
14343
|
rob@100
|
14344 function getGLContext(canvas) {
|
rob@100
|
14345 var ctxNames = ['experimental-webgl', 'webgl', 'webkit-3d'],
|
rob@100
|
14346 gl;
|
rob@100
|
14347
|
rob@100
|
14348 for (var i=0, l=ctxNames.length; i<l; i++) {
|
rob@100
|
14349 gl = canvas.getContext(ctxNames[i], {antialias: false, preserveDrawingBuffer: true});
|
rob@100
|
14350 if (gl) {
|
rob@100
|
14351 break;
|
rob@100
|
14352 }
|
rob@100
|
14353 }
|
rob@100
|
14354
|
rob@100
|
14355 return gl;
|
rob@100
|
14356 }
|
rob@100
|
14357
|
rob@100
|
14358 // Get the 3D rendering context.
|
rob@100
|
14359 try {
|
rob@100
|
14360 // If the HTML <canvas> dimensions differ from the
|
rob@100
|
14361 // dimensions specified in the size() call in the sketch, for
|
rob@100
|
14362 // 3D sketches, browsers will either not render or render the
|
rob@100
|
14363 // scene incorrectly. To fix this, we need to adjust the
|
rob@100
|
14364 // width and height attributes of the canvas.
|
rob@100
|
14365 curElement.width = p.width = aWidth || 100;
|
rob@100
|
14366 curElement.height = p.height = aHeight || 100;
|
rob@100
|
14367 curContext = getGLContext(curElement);
|
rob@100
|
14368 canTex = curContext.createTexture();
|
rob@100
|
14369 textTex = curContext.createTexture();
|
rob@100
|
14370 } catch(e_size) {
|
rob@100
|
14371 Processing.debug(e_size);
|
rob@100
|
14372 }
|
rob@100
|
14373
|
rob@100
|
14374 if (!curContext) {
|
rob@100
|
14375 throw "WebGL context is not supported on this browser.";
|
rob@100
|
14376 }
|
rob@100
|
14377
|
rob@100
|
14378 // Set defaults
|
rob@100
|
14379 curContext.viewport(0, 0, curElement.width, curElement.height);
|
rob@100
|
14380 curContext.enable(curContext.DEPTH_TEST);
|
rob@100
|
14381 curContext.enable(curContext.BLEND);
|
rob@100
|
14382 curContext.blendFunc(curContext.SRC_ALPHA, curContext.ONE_MINUS_SRC_ALPHA);
|
rob@100
|
14383
|
rob@100
|
14384 // Create the program objects to render 2D (points, lines) and
|
rob@100
|
14385 // 3D (spheres, boxes) shapes. Because 2D shapes are not lit,
|
rob@100
|
14386 // lighting calculations are ommitted from this program object.
|
rob@100
|
14387 programObject2D = createProgramObject(curContext, vertexShaderSrc2D, fragmentShaderSrc2D);
|
rob@100
|
14388
|
rob@100
|
14389 programObjectUnlitShape = createProgramObject(curContext, vertexShaderSrcUnlitShape, fragmentShaderSrcUnlitShape);
|
rob@100
|
14390
|
rob@100
|
14391 // Set the default point and line width for the 2D and unlit shapes.
|
rob@100
|
14392 p.strokeWeight(1);
|
rob@100
|
14393
|
rob@100
|
14394 // Now that the programs have been compiled, we can set the default
|
rob@100
|
14395 // states for the lights.
|
rob@100
|
14396 programObject3D = createProgramObject(curContext, vertexShaderSrc3D, fragmentShaderSrc3D);
|
rob@100
|
14397 curContext.useProgram(programObject3D);
|
rob@100
|
14398
|
rob@100
|
14399 // Assume we aren't using textures by default.
|
rob@100
|
14400 uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
|
rob@100
|
14401
|
rob@100
|
14402 // Set some defaults.
|
rob@100
|
14403 p.lightFalloff(1, 0, 0);
|
rob@100
|
14404 p.shininess(1);
|
rob@100
|
14405 p.ambient(255, 255, 255);
|
rob@100
|
14406 p.specular(0, 0, 0);
|
rob@100
|
14407 p.emissive(0, 0, 0);
|
rob@100
|
14408
|
rob@100
|
14409 // Create buffers for 3D primitives
|
rob@100
|
14410 boxBuffer = curContext.createBuffer();
|
rob@100
|
14411 curContext.bindBuffer(curContext.ARRAY_BUFFER, boxBuffer);
|
rob@100
|
14412 curContext.bufferData(curContext.ARRAY_BUFFER, boxVerts, curContext.STATIC_DRAW);
|
rob@100
|
14413
|
rob@100
|
14414 boxNormBuffer = curContext.createBuffer();
|
rob@100
|
14415 curContext.bindBuffer(curContext.ARRAY_BUFFER, boxNormBuffer);
|
rob@100
|
14416 curContext.bufferData(curContext.ARRAY_BUFFER, boxNorms, curContext.STATIC_DRAW);
|
rob@100
|
14417
|
rob@100
|
14418 boxOutlineBuffer = curContext.createBuffer();
|
rob@100
|
14419 curContext.bindBuffer(curContext.ARRAY_BUFFER, boxOutlineBuffer);
|
rob@100
|
14420 curContext.bufferData(curContext.ARRAY_BUFFER, boxOutlineVerts, curContext.STATIC_DRAW);
|
rob@100
|
14421
|
rob@100
|
14422 // used to draw the rectangle and the outline
|
rob@100
|
14423 rectBuffer = curContext.createBuffer();
|
rob@100
|
14424 curContext.bindBuffer(curContext.ARRAY_BUFFER, rectBuffer);
|
rob@100
|
14425 curContext.bufferData(curContext.ARRAY_BUFFER, rectVerts, curContext.STATIC_DRAW);
|
rob@100
|
14426
|
rob@100
|
14427 rectNormBuffer = curContext.createBuffer();
|
rob@100
|
14428 curContext.bindBuffer(curContext.ARRAY_BUFFER, rectNormBuffer);
|
rob@100
|
14429 curContext.bufferData(curContext.ARRAY_BUFFER, rectNorms, curContext.STATIC_DRAW);
|
rob@100
|
14430
|
rob@100
|
14431 // The sphere vertices are specified dynamically since the user
|
rob@100
|
14432 // can change the level of detail. Everytime the user does that
|
rob@100
|
14433 // using sphereDetail(), the new vertices are calculated.
|
rob@100
|
14434 sphereBuffer = curContext.createBuffer();
|
rob@100
|
14435
|
rob@100
|
14436 lineBuffer = curContext.createBuffer();
|
rob@100
|
14437
|
rob@100
|
14438 // Shape buffers
|
rob@100
|
14439 fillBuffer = curContext.createBuffer();
|
rob@100
|
14440 fillColorBuffer = curContext.createBuffer();
|
rob@100
|
14441 strokeColorBuffer = curContext.createBuffer();
|
rob@100
|
14442 shapeTexVBO = curContext.createBuffer();
|
rob@100
|
14443
|
rob@100
|
14444 pointBuffer = curContext.createBuffer();
|
rob@100
|
14445 curContext.bindBuffer(curContext.ARRAY_BUFFER, pointBuffer);
|
rob@100
|
14446 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0, 0, 0]), curContext.STATIC_DRAW);
|
rob@100
|
14447
|
rob@100
|
14448 textBuffer = curContext.createBuffer();
|
rob@100
|
14449 curContext.bindBuffer(curContext.ARRAY_BUFFER, textBuffer );
|
rob@100
|
14450 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([1,1,0,-1,1,0,-1,-1,0,1,-1,0]), curContext.STATIC_DRAW);
|
rob@100
|
14451
|
rob@100
|
14452 textureBuffer = curContext.createBuffer();
|
rob@100
|
14453 curContext.bindBuffer(curContext.ARRAY_BUFFER, textureBuffer);
|
rob@100
|
14454 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0,0,1,0,1,1,0,1]), curContext.STATIC_DRAW);
|
rob@100
|
14455
|
rob@100
|
14456 indexBuffer = curContext.createBuffer();
|
rob@100
|
14457 curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
rob@100
|
14458 curContext.bufferData(curContext.ELEMENT_ARRAY_BUFFER, new Uint16Array([0,1,2,2,3,0]), curContext.STATIC_DRAW);
|
rob@100
|
14459
|
rob@100
|
14460 cam = new PMatrix3D();
|
rob@100
|
14461 cameraInv = new PMatrix3D();
|
rob@100
|
14462 modelView = new PMatrix3D();
|
rob@100
|
14463 modelViewInv = new PMatrix3D();
|
rob@100
|
14464 projection = new PMatrix3D();
|
rob@100
|
14465 p.camera();
|
rob@100
|
14466 p.perspective();
|
rob@100
|
14467
|
rob@100
|
14468 userMatrixStack = new PMatrixStack();
|
rob@100
|
14469 userReverseMatrixStack = new PMatrixStack();
|
rob@100
|
14470 // used by both curve and bezier, so just init here
|
rob@100
|
14471 curveBasisMatrix = new PMatrix3D();
|
rob@100
|
14472 curveToBezierMatrix = new PMatrix3D();
|
rob@100
|
14473 curveDrawMatrix = new PMatrix3D();
|
rob@100
|
14474 bezierDrawMatrix = new PMatrix3D();
|
rob@100
|
14475 bezierBasisInverse = new PMatrix3D();
|
rob@100
|
14476 bezierBasisMatrix = new PMatrix3D();
|
rob@100
|
14477 bezierBasisMatrix.set(-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0);
|
rob@100
|
14478
|
rob@100
|
14479 DrawingShared.prototype.size.apply(this, arguments);
|
rob@100
|
14480 };
|
rob@100
|
14481 }());
|
rob@100
|
14482
|
rob@100
|
14483 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
14484 // Lights
|
rob@100
|
14485 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
14486
|
rob@100
|
14487 /**
|
rob@100
|
14488 * Adds an ambient light. Ambient light doesn't come from a specific direction,
|
rob@100
|
14489 * the rays have light have bounced around so much that objects are evenly lit
|
rob@100
|
14490 * from all sides. Ambient lights are almost always used in combination with
|
rob@100
|
14491 * other types of lights. Lights need to be included in the <b>draw()</b> to
|
rob@100
|
14492 * remain persistent in a looping program. Placing them in the <b>setup()</b>
|
rob@100
|
14493 * of a looping program will cause them to only have an effect the first time
|
rob@100
|
14494 * through the loop. The effect of the parameters is determined by the current
|
rob@100
|
14495 * color mode.
|
rob@100
|
14496 *
|
rob@100
|
14497 * @param {int | float} r red or hue value
|
rob@100
|
14498 * @param {int | float} g green or hue value
|
rob@100
|
14499 * @param {int | float} b blue or hue value
|
rob@100
|
14500 *
|
rob@100
|
14501 * @param {int | float} x x position of light (used for falloff)
|
rob@100
|
14502 * @param {int | float} y y position of light (used for falloff)
|
rob@100
|
14503 * @param {int | float} z z position of light (used for falloff)
|
rob@100
|
14504 *
|
rob@100
|
14505 * @returns none
|
rob@100
|
14506 *
|
rob@100
|
14507 * @see lights
|
rob@100
|
14508 * @see directionalLight
|
rob@100
|
14509 * @see pointLight
|
rob@100
|
14510 * @see spotLight
|
rob@100
|
14511 */
|
rob@100
|
14512 Drawing2D.prototype.ambientLight = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
14513
|
rob@100
|
14514 Drawing3D.prototype.ambientLight = function(r, g, b, x, y, z) {
|
rob@100
|
14515 if (lightCount === PConstants.MAX_LIGHTS) {
|
rob@100
|
14516 throw "can only create " + PConstants.MAX_LIGHTS + " lights";
|
rob@100
|
14517 }
|
rob@100
|
14518
|
rob@100
|
14519 var pos = new PVector(x, y, z);
|
rob@100
|
14520 var view = new PMatrix3D();
|
rob@100
|
14521 view.scale(1, -1, 1);
|
rob@100
|
14522 view.apply(modelView.array());
|
rob@100
|
14523 view.mult(pos, pos);
|
rob@100
|
14524
|
rob@100
|
14525 // Instead of calling p.color, we do the calculations ourselves to
|
rob@100
|
14526 // reduce property lookups.
|
rob@100
|
14527 var col = color$4(r, g, b, 0);
|
rob@100
|
14528 var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
|
rob@100
|
14529 ((col & PConstants.GREEN_MASK) >>> 8) / 255,
|
rob@100
|
14530 (col & PConstants.BLUE_MASK) / 255 ];
|
rob@100
|
14531
|
rob@100
|
14532 curContext.useProgram(programObject3D);
|
rob@100
|
14533 uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol);
|
rob@100
|
14534 uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", pos.array());
|
rob@100
|
14535 uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 0);
|
rob@100
|
14536 uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount);
|
rob@100
|
14537 };
|
rob@100
|
14538
|
rob@100
|
14539 /**
|
rob@100
|
14540 * Adds a directional light. Directional light comes from one direction and
|
rob@100
|
14541 * is stronger when hitting a surface squarely and weaker if it hits at a
|
rob@100
|
14542 * gentle angle. After hitting a surface, a directional lights scatters in
|
rob@100
|
14543 * all directions. Lights need to be included in the <b>draw()</b> to remain
|
rob@100
|
14544 * persistent in a looping program. Placing them in the <b>setup()</b> of a
|
rob@100
|
14545 * looping program will cause them to only have an effect the first time
|
rob@100
|
14546 * through the loop. The affect of the <br>r</b>, <br>g</b>, and <br>b</b>
|
rob@100
|
14547 * parameters is determined by the current color mode. The <b>nx</b>,
|
rob@100
|
14548 * <b>ny</b>, and <b>nz</b> parameters specify the direction the light is
|
rob@100
|
14549 * facing. For example, setting <b>ny</b> to -1 will cause the geometry to be
|
rob@100
|
14550 * lit from below (the light is facing directly upward).
|
rob@100
|
14551 *
|
rob@100
|
14552 * @param {int | float} r red or hue value
|
rob@100
|
14553 * @param {int | float} g green or hue value
|
rob@100
|
14554 * @param {int | float} b blue or hue value
|
rob@100
|
14555 *
|
rob@100
|
14556 * @param {int | float} nx direction along the x axis
|
rob@100
|
14557 * @param {int | float} ny direction along the y axis
|
rob@100
|
14558 * @param {int | float} nz direction along the z axis
|
rob@100
|
14559 *
|
rob@100
|
14560 * @returns none
|
rob@100
|
14561 *
|
rob@100
|
14562 * @see lights
|
rob@100
|
14563 * @see ambientLight
|
rob@100
|
14564 * @see pointLight
|
rob@100
|
14565 * @see spotLight
|
rob@100
|
14566 */
|
rob@100
|
14567 Drawing2D.prototype.directionalLight = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
14568
|
rob@100
|
14569 Drawing3D.prototype.directionalLight = function(r, g, b, nx, ny, nz) {
|
rob@100
|
14570 if (lightCount === PConstants.MAX_LIGHTS) {
|
rob@100
|
14571 throw "can only create " + PConstants.MAX_LIGHTS + " lights";
|
rob@100
|
14572 }
|
rob@100
|
14573
|
rob@100
|
14574 curContext.useProgram(programObject3D);
|
rob@100
|
14575
|
rob@100
|
14576 var mvm = new PMatrix3D();
|
rob@100
|
14577 mvm.scale(1, -1, 1);
|
rob@100
|
14578 mvm.apply(modelView.array());
|
rob@100
|
14579 mvm = mvm.array();
|
rob@100
|
14580
|
rob@100
|
14581 // We need to multiply the direction by the model view matrix, but
|
rob@100
|
14582 // the mult function checks the w component of the vector, if it isn't
|
rob@100
|
14583 // present, it uses 1, so we manually multiply.
|
rob@100
|
14584 var dir = [
|
rob@100
|
14585 mvm[0] * nx + mvm[4] * ny + mvm[8] * nz,
|
rob@100
|
14586 mvm[1] * nx + mvm[5] * ny + mvm[9] * nz,
|
rob@100
|
14587 mvm[2] * nx + mvm[6] * ny + mvm[10] * nz
|
rob@100
|
14588 ];
|
rob@100
|
14589
|
rob@100
|
14590 // Instead of calling p.color, we do the calculations ourselves to
|
rob@100
|
14591 // reduce property lookups.
|
rob@100
|
14592 var col = color$4(r, g, b, 0);
|
rob@100
|
14593 var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
|
rob@100
|
14594 ((col & PConstants.GREEN_MASK) >>> 8) / 255,
|
rob@100
|
14595 (col & PConstants.BLUE_MASK) / 255 ];
|
rob@100
|
14596
|
rob@100
|
14597 uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol);
|
rob@100
|
14598 uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", dir);
|
rob@100
|
14599 uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 1);
|
rob@100
|
14600 uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount);
|
rob@100
|
14601 };
|
rob@100
|
14602
|
rob@100
|
14603 /**
|
rob@100
|
14604 * Sets the falloff rates for point lights, spot lights, and ambient lights.
|
rob@100
|
14605 * The parameters are used to determine the falloff with the following equation:
|
rob@100
|
14606 *
|
rob@100
|
14607 * d = distance from light position to vertex position
|
rob@100
|
14608 * falloff = 1 / (CONSTANT + d * LINEAR + (d*d) * QUADRATIC)
|
rob@100
|
14609 *
|
rob@100
|
14610 * Like <b>fill()</b>, it affects only the elements which are created after it in the
|
rob@100
|
14611 * code. The default value if <b>LightFalloff(1.0, 0.0, 0.0)</b>. Thinking about an
|
rob@100
|
14612 * ambient light with a falloff can be tricky. It is used, for example, if you
|
rob@100
|
14613 * wanted a region of your scene to be lit ambiently one color and another region
|
rob@100
|
14614 * to be lit ambiently by another color, you would use an ambient light with location
|
rob@100
|
14615 * and falloff. You can think of it as a point light that doesn't care which direction
|
rob@100
|
14616 * a surface is facing.
|
rob@100
|
14617 *
|
rob@100
|
14618 * @param {int | float} constant constant value for determining falloff
|
rob@100
|
14619 * @param {int | float} linear linear value for determining falloff
|
rob@100
|
14620 * @param {int | float} quadratic quadratic value for determining falloff
|
rob@100
|
14621 *
|
rob@100
|
14622 * @returns none
|
rob@100
|
14623 *
|
rob@100
|
14624 * @see lights
|
rob@100
|
14625 * @see ambientLight
|
rob@100
|
14626 * @see pointLight
|
rob@100
|
14627 * @see spotLight
|
rob@100
|
14628 * @see lightSpecular
|
rob@100
|
14629 */
|
rob@100
|
14630 Drawing2D.prototype.lightFalloff = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
14631
|
rob@100
|
14632 Drawing3D.prototype.lightFalloff = function(constant, linear, quadratic) {
|
rob@100
|
14633 curContext.useProgram(programObject3D);
|
rob@100
|
14634 uniformf("uFalloff3d", programObject3D, "uFalloff", [constant, linear, quadratic]);
|
rob@100
|
14635 };
|
rob@100
|
14636
|
rob@100
|
14637 /**
|
rob@100
|
14638 * Sets the specular color for lights. Like <b>fill()</b>, it affects only the
|
rob@100
|
14639 * elements which are created after it in the code. Specular refers to light
|
rob@100
|
14640 * which bounces off a surface in a perferred direction (rather than bouncing
|
rob@100
|
14641 * in all directions like a diffuse light) and is used for creating highlights.
|
rob@100
|
14642 * The specular quality of a light interacts with the specular material qualities
|
rob@100
|
14643 * set through the <b>specular()</b> and <b>shininess()</b> functions.
|
rob@100
|
14644 *
|
rob@100
|
14645 * @param {int | float} r red or hue value
|
rob@100
|
14646 * @param {int | float} g green or hue value
|
rob@100
|
14647 * @param {int | float} b blue or hue value
|
rob@100
|
14648 *
|
rob@100
|
14649 * @returns none
|
rob@100
|
14650 *
|
rob@100
|
14651 * @see lights
|
rob@100
|
14652 * @see ambientLight
|
rob@100
|
14653 * @see pointLight
|
rob@100
|
14654 * @see spotLight
|
rob@100
|
14655 */
|
rob@100
|
14656 Drawing2D.prototype.lightSpecular = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
14657
|
rob@100
|
14658 Drawing3D.prototype.lightSpecular = function(r, g, b) {
|
rob@100
|
14659
|
rob@100
|
14660 // Instead of calling p.color, we do the calculations ourselves to
|
rob@100
|
14661 // reduce property lookups.
|
rob@100
|
14662 var col = color$4(r, g, b, 0);
|
rob@100
|
14663 var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
|
rob@100
|
14664 ((col & PConstants.GREEN_MASK) >>> 8) / 255,
|
rob@100
|
14665 (col & PConstants.BLUE_MASK) / 255 ];
|
rob@100
|
14666
|
rob@100
|
14667 curContext.useProgram(programObject3D);
|
rob@100
|
14668 uniformf("uSpecular3d", programObject3D, "uSpecular", normalizedCol);
|
rob@100
|
14669 };
|
rob@100
|
14670
|
rob@100
|
14671 /**
|
rob@100
|
14672 * Sets the default ambient light, directional light, falloff, and specular
|
rob@100
|
14673 * values. The defaults are ambientLight(128, 128, 128) and
|
rob@100
|
14674 * directionalLight(128, 128, 128, 0, 0, -1), lightFalloff(1, 0, 0), and
|
rob@100
|
14675 * lightSpecular(0, 0, 0). Lights need to be included in the draw() to remain
|
rob@100
|
14676 * persistent in a looping program. Placing them in the setup() of a looping
|
rob@100
|
14677 * program will cause them to only have an effect the first time through the
|
rob@100
|
14678 * loop.
|
rob@100
|
14679 *
|
rob@100
|
14680 * @returns none
|
rob@100
|
14681 *
|
rob@100
|
14682 * @see ambientLight
|
rob@100
|
14683 * @see directionalLight
|
rob@100
|
14684 * @see pointLight
|
rob@100
|
14685 * @see spotLight
|
rob@100
|
14686 * @see noLights
|
rob@100
|
14687 *
|
rob@100
|
14688 */
|
rob@100
|
14689 p.lights = function() {
|
rob@100
|
14690 p.ambientLight(128, 128, 128);
|
rob@100
|
14691 p.directionalLight(128, 128, 128, 0, 0, -1);
|
rob@100
|
14692 p.lightFalloff(1, 0, 0);
|
rob@100
|
14693 p.lightSpecular(0, 0, 0);
|
rob@100
|
14694 };
|
rob@100
|
14695
|
rob@100
|
14696 /**
|
rob@100
|
14697 * Adds a point light. Lights need to be included in the <b>draw()</b> to remain
|
rob@100
|
14698 * persistent in a looping program. Placing them in the <b>setup()</b> of a
|
rob@100
|
14699 * looping program will cause them to only have an effect the first time through
|
rob@100
|
14700 * the loop. The affect of the <b>r</b>, <b>g</b>, and <b>b</b> parameters
|
rob@100
|
14701 * is determined by the current color mode. The <b>x</b>, <b>y</b>, and <b>z</b>
|
rob@100
|
14702 * parameters set the position of the light.
|
rob@100
|
14703 *
|
rob@100
|
14704 * @param {int | float} r red or hue value
|
rob@100
|
14705 * @param {int | float} g green or hue value
|
rob@100
|
14706 * @param {int | float} b blue or hue value
|
rob@100
|
14707 * @param {int | float} x x coordinate of the light
|
rob@100
|
14708 * @param {int | float} y y coordinate of the light
|
rob@100
|
14709 * @param {int | float} z z coordinate of the light
|
rob@100
|
14710 *
|
rob@100
|
14711 * @returns none
|
rob@100
|
14712 *
|
rob@100
|
14713 * @see lights
|
rob@100
|
14714 * @see directionalLight
|
rob@100
|
14715 * @see ambientLight
|
rob@100
|
14716 * @see spotLight
|
rob@100
|
14717 */
|
rob@100
|
14718 Drawing2D.prototype.pointLight = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
14719
|
rob@100
|
14720 Drawing3D.prototype.pointLight = function(r, g, b, x, y, z) {
|
rob@100
|
14721 if (lightCount === PConstants.MAX_LIGHTS) {
|
rob@100
|
14722 throw "can only create " + PConstants.MAX_LIGHTS + " lights";
|
rob@100
|
14723 }
|
rob@100
|
14724
|
rob@100
|
14725 // Place the point in view space once instead of once per vertex
|
rob@100
|
14726 // in the shader.
|
rob@100
|
14727 var pos = new PVector(x, y, z);
|
rob@100
|
14728 var view = new PMatrix3D();
|
rob@100
|
14729 view.scale(1, -1, 1);
|
rob@100
|
14730 view.apply(modelView.array());
|
rob@100
|
14731 view.mult(pos, pos);
|
rob@100
|
14732
|
rob@100
|
14733 // Instead of calling p.color, we do the calculations ourselves to
|
rob@100
|
14734 // reduce property lookups.
|
rob@100
|
14735 var col = color$4(r, g, b, 0);
|
rob@100
|
14736 var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
|
rob@100
|
14737 ((col & PConstants.GREEN_MASK) >>> 8) / 255,
|
rob@100
|
14738 (col & PConstants.BLUE_MASK) / 255 ];
|
rob@100
|
14739
|
rob@100
|
14740 curContext.useProgram(programObject3D);
|
rob@100
|
14741 uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol);
|
rob@100
|
14742 uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", pos.array());
|
rob@100
|
14743 uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 2);
|
rob@100
|
14744 uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount);
|
rob@100
|
14745 };
|
rob@100
|
14746
|
rob@100
|
14747 /**
|
rob@100
|
14748 * Disable all lighting. Lighting is turned off by default and enabled with
|
rob@100
|
14749 * the lights() method. This function can be used to disable lighting so
|
rob@100
|
14750 * that 2D geometry (which does not require lighting) can be drawn after a
|
rob@100
|
14751 * set of lighted 3D geometry.
|
rob@100
|
14752 *
|
rob@100
|
14753 * @returns none
|
rob@100
|
14754 *
|
rob@100
|
14755 * @see lights
|
rob@100
|
14756 */
|
rob@100
|
14757 Drawing2D.prototype.noLights = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
14758
|
rob@100
|
14759 Drawing3D.prototype.noLights = function() {
|
rob@100
|
14760 lightCount = 0;
|
rob@100
|
14761 curContext.useProgram(programObject3D);
|
rob@100
|
14762 uniformi("uLightCount3d", programObject3D, "uLightCount", lightCount);
|
rob@100
|
14763 };
|
rob@100
|
14764
|
rob@100
|
14765 /**
|
rob@100
|
14766 * Adds a spot light. Lights need to be included in the <b>draw()</b> to
|
rob@100
|
14767 * remain persistent in a looping program. Placing them in the <b>setup()</b>
|
rob@100
|
14768 * of a looping program will cause them to only have an effect the first time
|
rob@100
|
14769 * through the loop. The affect of the <b>r</b>, <b>g</b>, and <b>b</b> parameters
|
rob@100
|
14770 * is determined by the current color mode. The <b>x</b>, <b>y</b>, and <b>z</b>
|
rob@100
|
14771 * parameters specify the position of the light and <b>nx</b>, <b>ny</b>, <b>nz</b>
|
rob@100
|
14772 * specify the direction or light. The angle parameter affects <b>angle</b> of the
|
rob@100
|
14773 * spotlight cone.
|
rob@100
|
14774 *
|
rob@100
|
14775 * @param {int | float} r red or hue value
|
rob@100
|
14776 * @param {int | float} g green or hue value
|
rob@100
|
14777 * @param {int | float} b blue or hue value
|
rob@100
|
14778 * @param {int | float} x coordinate of the light
|
rob@100
|
14779 * @param {int | float} y coordinate of the light
|
rob@100
|
14780 * @param {int | float} z coordinate of the light
|
rob@100
|
14781 * @param {int | float} nx direction along the x axis
|
rob@100
|
14782 * @param {int | float} ny direction along the y axis
|
rob@100
|
14783 * @param {int | float} nz direction along the z axis
|
rob@100
|
14784 * @param {float} angle angle of the spotlight cone
|
rob@100
|
14785 * @param {float} concentration exponent determining the center bias of the cone
|
rob@100
|
14786 *
|
rob@100
|
14787 * @returns none
|
rob@100
|
14788 *
|
rob@100
|
14789 * @see lights
|
rob@100
|
14790 * @see directionalLight
|
rob@100
|
14791 * @see ambientLight
|
rob@100
|
14792 * @see pointLight
|
rob@100
|
14793 */
|
rob@100
|
14794 Drawing2D.prototype.spotLight = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
14795
|
rob@100
|
14796 Drawing3D.prototype.spotLight = function(r, g, b, x, y, z, nx, ny, nz, angle, concentration) {
|
rob@100
|
14797 if (lightCount === PConstants.MAX_LIGHTS) {
|
rob@100
|
14798 throw "can only create " + PConstants.MAX_LIGHTS + " lights";
|
rob@100
|
14799 }
|
rob@100
|
14800
|
rob@100
|
14801 curContext.useProgram(programObject3D);
|
rob@100
|
14802
|
rob@100
|
14803 // multiply the position and direction by the model view matrix
|
rob@100
|
14804 // once per object rather than once per vertex.
|
rob@100
|
14805 var pos = new PVector(x, y, z);
|
rob@100
|
14806 var mvm = new PMatrix3D();
|
rob@100
|
14807 mvm.scale(1, -1, 1);
|
rob@100
|
14808 mvm.apply(modelView.array());
|
rob@100
|
14809 mvm.mult(pos, pos);
|
rob@100
|
14810
|
rob@100
|
14811 // Convert to array since we need to directly access the elements.
|
rob@100
|
14812 mvm = mvm.array();
|
rob@100
|
14813
|
rob@100
|
14814 // We need to multiply the direction by the model view matrix, but
|
rob@100
|
14815 // the mult function checks the w component of the vector, if it isn't
|
rob@100
|
14816 // present, it uses 1, so we use a very small value as a work around.
|
rob@100
|
14817 var dir = [
|
rob@100
|
14818 mvm[0] * nx + mvm[4] * ny + mvm[8] * nz,
|
rob@100
|
14819 mvm[1] * nx + mvm[5] * ny + mvm[9] * nz,
|
rob@100
|
14820 mvm[2] * nx + mvm[6] * ny + mvm[10] * nz
|
rob@100
|
14821 ];
|
rob@100
|
14822
|
rob@100
|
14823 // Instead of calling p.color, we do the calculations ourselves to
|
rob@100
|
14824 // reduce property lookups.
|
rob@100
|
14825 var col = color$4(r, g, b, 0);
|
rob@100
|
14826 var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
|
rob@100
|
14827 ((col & PConstants.GREEN_MASK) >>> 8) / 255,
|
rob@100
|
14828 (col & PConstants.BLUE_MASK) / 255 ];
|
rob@100
|
14829
|
rob@100
|
14830 uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol);
|
rob@100
|
14831 uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", pos.array());
|
rob@100
|
14832 uniformf("uLights.direction.3d." + lightCount, programObject3D, "uLights" + lightCount + ".direction", dir);
|
rob@100
|
14833 uniformf("uLights.concentration.3d." + lightCount, programObject3D, "uLights" + lightCount + ".concentration", concentration);
|
rob@100
|
14834 uniformf("uLights.angle.3d." + lightCount, programObject3D, "uLights" + lightCount + ".angle", angle);
|
rob@100
|
14835 uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 3);
|
rob@100
|
14836 uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount);
|
rob@100
|
14837 };
|
rob@100
|
14838
|
rob@100
|
14839 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
14840 // Camera functions
|
rob@100
|
14841 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
14842
|
rob@100
|
14843 /**
|
rob@100
|
14844 * The <b>beginCamera()</b> and <b>endCamera()</b> functions enable advanced customization of the camera space.
|
rob@100
|
14845 * The functions are useful if you want to more control over camera movement, however for most users, the <b>camera()</b>
|
rob@100
|
14846 * function will be sufficient.<br /><br />The camera functions will replace any transformations (such as <b>rotate()</b>
|
rob@100
|
14847 * or <b>translate()</b>) that occur before them in <b>draw()</b>, but they will not automatically replace the camera
|
rob@100
|
14848 * transform itself. For this reason, camera functions should be placed at the beginning of <b>draw()</b> (so that
|
rob@100
|
14849 * transformations happen afterwards), and the <b>camera()</b> function can be used after <b>beginCamera()</b> if
|
rob@100
|
14850 * you want to reset the camera before applying transformations.<br /><br />This function sets the matrix mode to the
|
rob@100
|
14851 * camera matrix so calls such as <b>translate()</b>, <b>rotate()</b>, applyMatrix() and resetMatrix() affect the camera.
|
rob@100
|
14852 * <b>beginCamera()</b> should always be used with a following <b>endCamera()</b> and pairs of <b>beginCamera()</b> and
|
rob@100
|
14853 * <b>endCamera()</b> cannot be nested.
|
rob@100
|
14854 *
|
rob@100
|
14855 * @see camera
|
rob@100
|
14856 * @see endCamera
|
rob@100
|
14857 * @see applyMatrix
|
rob@100
|
14858 * @see resetMatrix
|
rob@100
|
14859 * @see translate
|
rob@100
|
14860 * @see rotate
|
rob@100
|
14861 * @see scale
|
rob@100
|
14862 */
|
rob@100
|
14863 Drawing2D.prototype.beginCamera = function() {
|
rob@100
|
14864 throw ("beginCamera() is not available in 2D mode");
|
rob@100
|
14865 };
|
rob@100
|
14866
|
rob@100
|
14867 Drawing3D.prototype.beginCamera = function() {
|
rob@100
|
14868 if (manipulatingCamera) {
|
rob@100
|
14869 throw ("You cannot call beginCamera() again before calling endCamera()");
|
rob@100
|
14870 }
|
rob@100
|
14871 manipulatingCamera = true;
|
rob@100
|
14872 modelView = cameraInv;
|
rob@100
|
14873 modelViewInv = cam;
|
rob@100
|
14874 };
|
rob@100
|
14875
|
rob@100
|
14876 /**
|
rob@100
|
14877 * The <b>beginCamera()</b> and <b>endCamera()</b> functions enable advanced customization of the camera space.
|
rob@100
|
14878 * Please see the reference for <b>beginCamera()</b> for a description of how the functions are used.
|
rob@100
|
14879 *
|
rob@100
|
14880 * @see beginCamera
|
rob@100
|
14881 */
|
rob@100
|
14882 Drawing2D.prototype.endCamera = function() {
|
rob@100
|
14883 throw ("endCamera() is not available in 2D mode");
|
rob@100
|
14884 };
|
rob@100
|
14885
|
rob@100
|
14886 Drawing3D.prototype.endCamera = function() {
|
rob@100
|
14887 if (!manipulatingCamera) {
|
rob@100
|
14888 throw ("You cannot call endCamera() before calling beginCamera()");
|
rob@100
|
14889 }
|
rob@100
|
14890 modelView.set(cam);
|
rob@100
|
14891 modelViewInv.set(cameraInv);
|
rob@100
|
14892 manipulatingCamera = false;
|
rob@100
|
14893 };
|
rob@100
|
14894
|
rob@100
|
14895 /**
|
rob@100
|
14896 * Sets the position of the camera through setting the eye position, the center of the scene, and which axis is facing
|
rob@100
|
14897 * upward. Moving the eye position and the direction it is pointing (the center of the scene) allows the images to be
|
rob@100
|
14898 * seen from different angles. The version without any parameters sets the camera to the default position, pointing to
|
rob@100
|
14899 * the center of the display window with the Y axis as up. The default values are camera(width/2.0, height/2.0,
|
rob@100
|
14900 * (height/2.0) / tan(PI*60.0 / 360.0), width/2.0, height/2.0, 0, 0, 1, 0). This function is similar to gluLookAt()
|
rob@100
|
14901 * in OpenGL, but it first clears the current camera settings.
|
rob@100
|
14902 *
|
rob@100
|
14903 * @param {float} eyeX x-coordinate for the eye
|
rob@100
|
14904 * @param {float} eyeY y-coordinate for the eye
|
rob@100
|
14905 * @param {float} eyeZ z-coordinate for the eye
|
rob@100
|
14906 * @param {float} centerX x-coordinate for the center of the scene
|
rob@100
|
14907 * @param {float} centerY y-coordinate for the center of the scene
|
rob@100
|
14908 * @param {float} centerZ z-coordinate for the center of the scene
|
rob@100
|
14909 * @param {float} upX usually 0.0, 1.0, -1.0
|
rob@100
|
14910 * @param {float} upY usually 0.0, 1.0, -1.0
|
rob@100
|
14911 * @param {float} upZ usually 0.0, 1.0, -1.0
|
rob@100
|
14912 *
|
rob@100
|
14913 * @see beginCamera
|
rob@100
|
14914 * @see endCamera
|
rob@100
|
14915 * @see frustum
|
rob@100
|
14916 */
|
rob@100
|
14917 p.camera = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
|
rob@100
|
14918 if (eyeX === undef) {
|
rob@100
|
14919 // Workaround if createGraphics is used.
|
rob@100
|
14920 cameraX = p.width / 2;
|
rob@100
|
14921 cameraY = p.height / 2;
|
rob@100
|
14922 cameraZ = cameraY / Math.tan(cameraFOV / 2);
|
rob@100
|
14923 eyeX = cameraX;
|
rob@100
|
14924 eyeY = cameraY;
|
rob@100
|
14925 eyeZ = cameraZ;
|
rob@100
|
14926 centerX = cameraX;
|
rob@100
|
14927 centerY = cameraY;
|
rob@100
|
14928 centerZ = 0;
|
rob@100
|
14929 upX = 0;
|
rob@100
|
14930 upY = 1;
|
rob@100
|
14931 upZ = 0;
|
rob@100
|
14932 }
|
rob@100
|
14933
|
rob@100
|
14934 var z = new PVector(eyeX - centerX, eyeY - centerY, eyeZ - centerZ);
|
rob@100
|
14935 var y = new PVector(upX, upY, upZ);
|
rob@100
|
14936 z.normalize();
|
rob@100
|
14937 var x = PVector.cross(y, z);
|
rob@100
|
14938 y = PVector.cross(z, x);
|
rob@100
|
14939 x.normalize();
|
rob@100
|
14940 y.normalize();
|
rob@100
|
14941
|
rob@100
|
14942 var xX = x.x,
|
rob@100
|
14943 xY = x.y,
|
rob@100
|
14944 xZ = x.z;
|
rob@100
|
14945
|
rob@100
|
14946 var yX = y.x,
|
rob@100
|
14947 yY = y.y,
|
rob@100
|
14948 yZ = y.z;
|
rob@100
|
14949
|
rob@100
|
14950 var zX = z.x,
|
rob@100
|
14951 zY = z.y,
|
rob@100
|
14952 zZ = z.z;
|
rob@100
|
14953
|
rob@100
|
14954 cam.set(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1);
|
rob@100
|
14955
|
rob@100
|
14956 cam.translate(-eyeX, -eyeY, -eyeZ);
|
rob@100
|
14957
|
rob@100
|
14958 cameraInv.reset();
|
rob@100
|
14959 cameraInv.invApply(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1);
|
rob@100
|
14960
|
rob@100
|
14961 cameraInv.translate(eyeX, eyeY, eyeZ);
|
rob@100
|
14962
|
rob@100
|
14963 modelView.set(cam);
|
rob@100
|
14964 modelViewInv.set(cameraInv);
|
rob@100
|
14965 };
|
rob@100
|
14966
|
rob@100
|
14967 /**
|
rob@100
|
14968 * Sets a perspective projection applying foreshortening, making distant objects appear smaller than closer ones. The
|
rob@100
|
14969 * parameters define a viewing volume with the shape of truncated pyramid. Objects near to the front of the volume appear
|
rob@100
|
14970 * their actual size, while farther objects appear smaller. This projection simulates the perspective of the world more
|
rob@100
|
14971 * accurately than orthographic projection. The version of perspective without parameters sets the default perspective and
|
rob@100
|
14972 * the version with four parameters allows the programmer to set the area precisely. The default values are:
|
rob@100
|
14973 * perspective(PI/3.0, width/height, cameraZ/10.0, cameraZ*10.0) where cameraZ is ((height/2.0) / tan(PI*60.0/360.0));
|
rob@100
|
14974 *
|
rob@100
|
14975 * @param {float} fov field-of-view angle (in radians) for vertical direction
|
rob@100
|
14976 * @param {float} aspect ratio of width to height
|
rob@100
|
14977 * @param {float} zNear z-position of nearest clipping plane
|
rob@100
|
14978 * @param {float} zFar z-positions of farthest clipping plane
|
rob@100
|
14979 */
|
rob@100
|
14980 p.perspective = function(fov, aspect, near, far) {
|
rob@100
|
14981 if (arguments.length === 0) {
|
rob@100
|
14982 //in case canvas is resized
|
rob@100
|
14983 cameraY = curElement.height / 2;
|
rob@100
|
14984 cameraZ = cameraY / Math.tan(cameraFOV / 2);
|
rob@100
|
14985 cameraNear = cameraZ / 10;
|
rob@100
|
14986 cameraFar = cameraZ * 10;
|
rob@100
|
14987 cameraAspect = p.width / p.height;
|
rob@100
|
14988 fov = cameraFOV;
|
rob@100
|
14989 aspect = cameraAspect;
|
rob@100
|
14990 near = cameraNear;
|
rob@100
|
14991 far = cameraFar;
|
rob@100
|
14992 }
|
rob@100
|
14993
|
rob@100
|
14994 var yMax, yMin, xMax, xMin;
|
rob@100
|
14995 yMax = near * Math.tan(fov / 2);
|
rob@100
|
14996 yMin = -yMax;
|
rob@100
|
14997 xMax = yMax * aspect;
|
rob@100
|
14998 xMin = yMin * aspect;
|
rob@100
|
14999 p.frustum(xMin, xMax, yMin, yMax, near, far);
|
rob@100
|
15000 };
|
rob@100
|
15001
|
rob@100
|
15002 /**
|
rob@100
|
15003 * Sets a perspective matrix defined through the parameters. Works like glFrustum, except it wipes out the current
|
rob@100
|
15004 * perspective matrix rather than muliplying itself with it.
|
rob@100
|
15005 *
|
rob@100
|
15006 * @param {float} left left coordinate of the clipping plane
|
rob@100
|
15007 * @param {float} right right coordinate of the clipping plane
|
rob@100
|
15008 * @param {float} bottom bottom coordinate of the clipping plane
|
rob@100
|
15009 * @param {float} top top coordinate of the clipping plane
|
rob@100
|
15010 * @param {float} near near coordinate of the clipping plane
|
rob@100
|
15011 * @param {float} far far coordinate of the clipping plane
|
rob@100
|
15012 *
|
rob@100
|
15013 * @see beginCamera
|
rob@100
|
15014 * @see camera
|
rob@100
|
15015 * @see endCamera
|
rob@100
|
15016 * @see perspective
|
rob@100
|
15017 */
|
rob@100
|
15018 Drawing2D.prototype.frustum = function() {
|
rob@100
|
15019 throw("Processing.js: frustum() is not supported in 2D mode");
|
rob@100
|
15020 };
|
rob@100
|
15021
|
rob@100
|
15022 Drawing3D.prototype.frustum = function(left, right, bottom, top, near, far) {
|
rob@100
|
15023 frustumMode = true;
|
rob@100
|
15024 projection = new PMatrix3D();
|
rob@100
|
15025 projection.set((2 * near) / (right - left), 0, (right + left) / (right - left),
|
rob@100
|
15026 0, 0, (2 * near) / (top - bottom), (top + bottom) / (top - bottom),
|
rob@100
|
15027 0, 0, 0, -(far + near) / (far - near), -(2 * far * near) / (far - near),
|
rob@100
|
15028 0, 0, -1, 0);
|
rob@100
|
15029 var proj = new PMatrix3D();
|
rob@100
|
15030 proj.set(projection);
|
rob@100
|
15031 proj.transpose();
|
rob@100
|
15032 curContext.useProgram(programObject2D);
|
rob@100
|
15033 uniformMatrix("projection2d", programObject2D, "uProjection", false, proj.array());
|
rob@100
|
15034 curContext.useProgram(programObject3D);
|
rob@100
|
15035 uniformMatrix("projection3d", programObject3D, "uProjection", false, proj.array());
|
rob@100
|
15036 curContext.useProgram(programObjectUnlitShape);
|
rob@100
|
15037 uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array());
|
rob@100
|
15038 };
|
rob@100
|
15039
|
rob@100
|
15040 /**
|
rob@100
|
15041 * Sets an orthographic projection and defines a parallel clipping volume. All objects with the same dimension appear
|
rob@100
|
15042 * the same size, regardless of whether they are near or far from the camera. The parameters to this function specify
|
rob@100
|
15043 * the clipping volume where left and right are the minimum and maximum x values, top and bottom are the minimum and
|
rob@100
|
15044 * maximum y values, and near and far are the minimum and maximum z values. If no parameters are given, the default
|
rob@100
|
15045 * is used: ortho(0, width, 0, height, -10, 10).
|
rob@100
|
15046 *
|
rob@100
|
15047 * @param {float} left left plane of the clipping volume
|
rob@100
|
15048 * @param {float} right right plane of the clipping volume
|
rob@100
|
15049 * @param {float} bottom bottom plane of the clipping volume
|
rob@100
|
15050 * @param {float} top top plane of the clipping volume
|
rob@100
|
15051 * @param {float} near maximum distance from the origin to the viewer
|
rob@100
|
15052 * @param {float} far maximum distance from the origin away from the viewer
|
rob@100
|
15053 */
|
rob@100
|
15054 p.ortho = function(left, right, bottom, top, near, far) {
|
rob@100
|
15055 if (arguments.length === 0) {
|
rob@100
|
15056 left = 0;
|
rob@100
|
15057 right = p.width;
|
rob@100
|
15058 bottom = 0;
|
rob@100
|
15059 top = p.height;
|
rob@100
|
15060 near = -10;
|
rob@100
|
15061 far = 10;
|
rob@100
|
15062 }
|
rob@100
|
15063
|
rob@100
|
15064 var x = 2 / (right - left);
|
rob@100
|
15065 var y = 2 / (top - bottom);
|
rob@100
|
15066 var z = -2 / (far - near);
|
rob@100
|
15067
|
rob@100
|
15068 var tx = -(right + left) / (right - left);
|
rob@100
|
15069 var ty = -(top + bottom) / (top - bottom);
|
rob@100
|
15070 var tz = -(far + near) / (far - near);
|
rob@100
|
15071
|
rob@100
|
15072 projection = new PMatrix3D();
|
rob@100
|
15073 projection.set(x, 0, 0, tx, 0, y, 0, ty, 0, 0, z, tz, 0, 0, 0, 1);
|
rob@100
|
15074
|
rob@100
|
15075 var proj = new PMatrix3D();
|
rob@100
|
15076 proj.set(projection);
|
rob@100
|
15077 proj.transpose();
|
rob@100
|
15078 curContext.useProgram(programObject2D);
|
rob@100
|
15079 uniformMatrix("projection2d", programObject2D, "uProjection", false, proj.array());
|
rob@100
|
15080 curContext.useProgram(programObject3D);
|
rob@100
|
15081 uniformMatrix("projection3d", programObject3D, "uProjection", false, proj.array());
|
rob@100
|
15082 curContext.useProgram(programObjectUnlitShape);
|
rob@100
|
15083 uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array());
|
rob@100
|
15084 frustumMode = false;
|
rob@100
|
15085 };
|
rob@100
|
15086 /**
|
rob@100
|
15087 * The printProjection() prints the current projection matrix to the text window.
|
rob@100
|
15088 */
|
rob@100
|
15089 p.printProjection = function() {
|
rob@100
|
15090 projection.print();
|
rob@100
|
15091 };
|
rob@100
|
15092 /**
|
rob@100
|
15093 * The printCamera() function prints the current camera matrix.
|
rob@100
|
15094 */
|
rob@100
|
15095 p.printCamera = function() {
|
rob@100
|
15096 cam.print();
|
rob@100
|
15097 };
|
rob@100
|
15098
|
rob@100
|
15099 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
15100 // Shapes
|
rob@100
|
15101 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
15102 /**
|
rob@100
|
15103 * The box() function renders a box. A box is an extruded rectangle. A box with equal dimension on all sides is a cube.
|
rob@100
|
15104 * Calling this function with only one parameter will create a cube.
|
rob@100
|
15105 *
|
rob@100
|
15106 * @param {int|float} w dimension of the box in the x-dimension
|
rob@100
|
15107 * @param {int|float} h dimension of the box in the y-dimension
|
rob@100
|
15108 * @param {int|float} d dimension of the box in the z-dimension
|
rob@100
|
15109 */
|
rob@100
|
15110 Drawing2D.prototype.box = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
15111
|
rob@100
|
15112 Drawing3D.prototype.box = function(w, h, d) {
|
rob@100
|
15113 // user can uniformly scale the box by
|
rob@100
|
15114 // passing in only one argument.
|
rob@100
|
15115 if (!h || !d) {
|
rob@100
|
15116 h = d = w;
|
rob@100
|
15117 }
|
rob@100
|
15118
|
rob@100
|
15119 // Modeling transformation
|
rob@100
|
15120 var model = new PMatrix3D();
|
rob@100
|
15121 model.scale(w, h, d);
|
rob@100
|
15122
|
rob@100
|
15123 // Viewing transformation needs to have Y flipped
|
rob@100
|
15124 // becuase that's what Processing does.
|
rob@100
|
15125 var view = new PMatrix3D();
|
rob@100
|
15126 view.scale(1, -1, 1);
|
rob@100
|
15127 view.apply(modelView.array());
|
rob@100
|
15128 view.transpose();
|
rob@100
|
15129
|
rob@100
|
15130 if (doFill) {
|
rob@100
|
15131 curContext.useProgram(programObject3D);
|
rob@100
|
15132 uniformMatrix("model3d", programObject3D, "uModel", false, model.array());
|
rob@100
|
15133 uniformMatrix("view3d", programObject3D, "uView", false, view.array());
|
rob@100
|
15134 // Fix stitching problems. (lines get occluded by triangles
|
rob@100
|
15135 // since they share the same depth values). This is not entirely
|
rob@100
|
15136 // working, but it's a start for drawing the outline. So
|
rob@100
|
15137 // developers can start playing around with styles.
|
rob@100
|
15138 curContext.enable(curContext.POLYGON_OFFSET_FILL);
|
rob@100
|
15139 curContext.polygonOffset(1, 1);
|
rob@100
|
15140 uniformf("color3d", programObject3D, "uColor", fillStyle);
|
rob@100
|
15141
|
rob@100
|
15142 // Calculating the normal matrix can be expensive, so only
|
rob@100
|
15143 // do it if it's necessary.
|
rob@100
|
15144 if(lightCount > 0){
|
rob@100
|
15145 // Create the normal transformation matrix.
|
rob@100
|
15146 var v = new PMatrix3D();
|
rob@100
|
15147 v.set(view);
|
rob@100
|
15148
|
rob@100
|
15149 var m = new PMatrix3D();
|
rob@100
|
15150 m.set(model);
|
rob@100
|
15151
|
rob@100
|
15152 v.mult(m);
|
rob@100
|
15153
|
rob@100
|
15154 var normalMatrix = new PMatrix3D();
|
rob@100
|
15155 normalMatrix.set(v);
|
rob@100
|
15156 normalMatrix.invert();
|
rob@100
|
15157 normalMatrix.transpose();
|
rob@100
|
15158
|
rob@100
|
15159 uniformMatrix("uNormalTransform3d", programObject3D, "uNormalTransform", false, normalMatrix.array());
|
rob@100
|
15160 vertexAttribPointer("aNormal3d", programObject3D, "aNormal", 3, boxNormBuffer);
|
rob@100
|
15161 }
|
rob@100
|
15162 else{
|
rob@100
|
15163 disableVertexAttribPointer("aNormal3d", programObject3D, "aNormal");
|
rob@100
|
15164 }
|
rob@100
|
15165
|
rob@100
|
15166 vertexAttribPointer("aVertex3d", programObject3D, "aVertex", 3, boxBuffer);
|
rob@100
|
15167
|
rob@100
|
15168 // Turn off per vertex colors.
|
rob@100
|
15169 disableVertexAttribPointer("aColor3d", programObject3D, "aColor");
|
rob@100
|
15170 disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture");
|
rob@100
|
15171
|
rob@100
|
15172 curContext.drawArrays(curContext.TRIANGLES, 0, boxVerts.length / 3);
|
rob@100
|
15173 curContext.disable(curContext.POLYGON_OFFSET_FILL);
|
rob@100
|
15174 }
|
rob@100
|
15175
|
rob@100
|
15176 // Draw the box outline.
|
rob@100
|
15177 if (lineWidth > 0 && doStroke) {
|
rob@100
|
15178 curContext.useProgram(programObject2D);
|
rob@100
|
15179 uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
|
rob@100
|
15180 uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
|
rob@100
|
15181 uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
|
rob@100
|
15182 uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", false);
|
rob@100
|
15183 vertexAttribPointer("vertex2d", programObject2D, "aVertex", 3, boxOutlineBuffer);
|
rob@100
|
15184 disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
|
rob@100
|
15185 curContext.drawArrays(curContext.LINES, 0, boxOutlineVerts.length / 3);
|
rob@100
|
15186 }
|
rob@100
|
15187 };
|
rob@100
|
15188
|
rob@100
|
15189 /**
|
rob@100
|
15190 * The initSphere() function is a helper function used by <b>sphereDetail()</b>
|
rob@100
|
15191 * This function creates and stores sphere vertices every time the user changes sphere detail.
|
rob@100
|
15192 *
|
rob@100
|
15193 * @see #sphereDetail
|
rob@100
|
15194 */
|
rob@100
|
15195 var initSphere = function() {
|
rob@100
|
15196 var i;
|
rob@100
|
15197 sphereVerts = [];
|
rob@100
|
15198
|
rob@100
|
15199 for (i = 0; i < sphereDetailU; i++) {
|
rob@100
|
15200 sphereVerts.push(0);
|
rob@100
|
15201 sphereVerts.push(-1);
|
rob@100
|
15202 sphereVerts.push(0);
|
rob@100
|
15203 sphereVerts.push(sphereX[i]);
|
rob@100
|
15204 sphereVerts.push(sphereY[i]);
|
rob@100
|
15205 sphereVerts.push(sphereZ[i]);
|
rob@100
|
15206 }
|
rob@100
|
15207 sphereVerts.push(0);
|
rob@100
|
15208 sphereVerts.push(-1);
|
rob@100
|
15209 sphereVerts.push(0);
|
rob@100
|
15210 sphereVerts.push(sphereX[0]);
|
rob@100
|
15211 sphereVerts.push(sphereY[0]);
|
rob@100
|
15212 sphereVerts.push(sphereZ[0]);
|
rob@100
|
15213
|
rob@100
|
15214 var v1, v11, v2;
|
rob@100
|
15215
|
rob@100
|
15216 // middle rings
|
rob@100
|
15217 var voff = 0;
|
rob@100
|
15218 for (i = 2; i < sphereDetailV; i++) {
|
rob@100
|
15219 v1 = v11 = voff;
|
rob@100
|
15220 voff += sphereDetailU;
|
rob@100
|
15221 v2 = voff;
|
rob@100
|
15222 for (var j = 0; j < sphereDetailU; j++) {
|
rob@100
|
15223 sphereVerts.push(sphereX[v1]);
|
rob@100
|
15224 sphereVerts.push(sphereY[v1]);
|
rob@100
|
15225 sphereVerts.push(sphereZ[v1++]);
|
rob@100
|
15226 sphereVerts.push(sphereX[v2]);
|
rob@100
|
15227 sphereVerts.push(sphereY[v2]);
|
rob@100
|
15228 sphereVerts.push(sphereZ[v2++]);
|
rob@100
|
15229 }
|
rob@100
|
15230
|
rob@100
|
15231 // close each ring
|
rob@100
|
15232 v1 = v11;
|
rob@100
|
15233 v2 = voff;
|
rob@100
|
15234
|
rob@100
|
15235 sphereVerts.push(sphereX[v1]);
|
rob@100
|
15236 sphereVerts.push(sphereY[v1]);
|
rob@100
|
15237 sphereVerts.push(sphereZ[v1]);
|
rob@100
|
15238 sphereVerts.push(sphereX[v2]);
|
rob@100
|
15239 sphereVerts.push(sphereY[v2]);
|
rob@100
|
15240 sphereVerts.push(sphereZ[v2]);
|
rob@100
|
15241 }
|
rob@100
|
15242
|
rob@100
|
15243 // add the northern cap
|
rob@100
|
15244 for (i = 0; i < sphereDetailU; i++) {
|
rob@100
|
15245 v2 = voff + i;
|
rob@100
|
15246
|
rob@100
|
15247 sphereVerts.push(sphereX[v2]);
|
rob@100
|
15248 sphereVerts.push(sphereY[v2]);
|
rob@100
|
15249 sphereVerts.push(sphereZ[v2]);
|
rob@100
|
15250 sphereVerts.push(0);
|
rob@100
|
15251 sphereVerts.push(1);
|
rob@100
|
15252 sphereVerts.push(0);
|
rob@100
|
15253 }
|
rob@100
|
15254
|
rob@100
|
15255 sphereVerts.push(sphereX[voff]);
|
rob@100
|
15256 sphereVerts.push(sphereY[voff]);
|
rob@100
|
15257 sphereVerts.push(sphereZ[voff]);
|
rob@100
|
15258 sphereVerts.push(0);
|
rob@100
|
15259 sphereVerts.push(1);
|
rob@100
|
15260 sphereVerts.push(0);
|
rob@100
|
15261
|
rob@100
|
15262 //set the buffer data
|
rob@100
|
15263 curContext.bindBuffer(curContext.ARRAY_BUFFER, sphereBuffer);
|
rob@100
|
15264 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(sphereVerts), curContext.STATIC_DRAW);
|
rob@100
|
15265 };
|
rob@100
|
15266
|
rob@100
|
15267 /**
|
rob@100
|
15268 * The sphereDetail() function controls the detail used to render a sphere by adjusting the number of
|
rob@100
|
15269 * vertices of the sphere mesh. The default resolution is 30, which creates
|
rob@100
|
15270 * a fairly detailed sphere definition with vertices every 360/30 = 12
|
rob@100
|
15271 * degrees. If you're going to render a great number of spheres per frame,
|
rob@100
|
15272 * it is advised to reduce the level of detail using this function.
|
rob@100
|
15273 * The setting stays active until <b>sphereDetail()</b> is called again with
|
rob@100
|
15274 * a new parameter and so should <i>not</i> be called prior to every
|
rob@100
|
15275 * <b>sphere()</b> statement, unless you wish to render spheres with
|
rob@100
|
15276 * different settings, e.g. using less detail for smaller spheres or ones
|
rob@100
|
15277 * further away from the camera. To control the detail of the horizontal
|
rob@100
|
15278 * and vertical resolution independently, use the version of the functions
|
rob@100
|
15279 * with two parameters. Calling this function with one parameter sets the number of segments
|
rob@100
|
15280 *(minimum of 3) used per full circle revolution. This is equivalent to calling the function with
|
rob@100
|
15281 * two identical values.
|
rob@100
|
15282 *
|
rob@100
|
15283 * @param {int} ures number of segments used horizontally (longitudinally) per full circle revolution
|
rob@100
|
15284 * @param {int} vres number of segments used vertically (latitudinally) from top to bottom
|
rob@100
|
15285 *
|
rob@100
|
15286 * @see #sphere()
|
rob@100
|
15287 */
|
rob@100
|
15288 p.sphereDetail = function(ures, vres) {
|
rob@100
|
15289 var i;
|
rob@100
|
15290
|
rob@100
|
15291 if (arguments.length === 1) {
|
rob@100
|
15292 ures = vres = arguments[0];
|
rob@100
|
15293 }
|
rob@100
|
15294
|
rob@100
|
15295 if (ures < 3) {
|
rob@100
|
15296 ures = 3;
|
rob@100
|
15297 } // force a minimum res
|
rob@100
|
15298 if (vres < 2) {
|
rob@100
|
15299 vres = 2;
|
rob@100
|
15300 } // force a minimum res
|
rob@100
|
15301 // if it hasn't changed do nothing
|
rob@100
|
15302 if ((ures === sphereDetailU) && (vres === sphereDetailV)) {
|
rob@100
|
15303 return;
|
rob@100
|
15304 }
|
rob@100
|
15305
|
rob@100
|
15306 var delta = PConstants.SINCOS_LENGTH / ures;
|
rob@100
|
15307 var cx = new Float32Array(ures);
|
rob@100
|
15308 var cz = new Float32Array(ures);
|
rob@100
|
15309 // calc unit circle in XZ plane
|
rob@100
|
15310 for (i = 0; i < ures; i++) {
|
rob@100
|
15311 cx[i] = cosLUT[((i * delta) % PConstants.SINCOS_LENGTH) | 0];
|
rob@100
|
15312 cz[i] = sinLUT[((i * delta) % PConstants.SINCOS_LENGTH) | 0];
|
rob@100
|
15313 }
|
rob@100
|
15314
|
rob@100
|
15315 // computing vertexlist
|
rob@100
|
15316 // vertexlist starts at south pole
|
rob@100
|
15317 var vertCount = ures * (vres - 1) + 2;
|
rob@100
|
15318 var currVert = 0;
|
rob@100
|
15319
|
rob@100
|
15320 // re-init arrays to store vertices
|
rob@100
|
15321 sphereX = new Float32Array(vertCount);
|
rob@100
|
15322 sphereY = new Float32Array(vertCount);
|
rob@100
|
15323 sphereZ = new Float32Array(vertCount);
|
rob@100
|
15324
|
rob@100
|
15325 var angle_step = (PConstants.SINCOS_LENGTH * 0.5) / vres;
|
rob@100
|
15326 var angle = angle_step;
|
rob@100
|
15327
|
rob@100
|
15328 // step along Y axis
|
rob@100
|
15329 for (i = 1; i < vres; i++) {
|
rob@100
|
15330 var curradius = sinLUT[(angle % PConstants.SINCOS_LENGTH) | 0];
|
rob@100
|
15331 var currY = -cosLUT[(angle % PConstants.SINCOS_LENGTH) | 0];
|
rob@100
|
15332 for (var j = 0; j < ures; j++) {
|
rob@100
|
15333 sphereX[currVert] = cx[j] * curradius;
|
rob@100
|
15334 sphereY[currVert] = currY;
|
rob@100
|
15335 sphereZ[currVert++] = cz[j] * curradius;
|
rob@100
|
15336 }
|
rob@100
|
15337 angle += angle_step;
|
rob@100
|
15338 }
|
rob@100
|
15339 sphereDetailU = ures;
|
rob@100
|
15340 sphereDetailV = vres;
|
rob@100
|
15341
|
rob@100
|
15342 // make the sphere verts and norms
|
rob@100
|
15343 initSphere();
|
rob@100
|
15344 };
|
rob@100
|
15345
|
rob@100
|
15346 /**
|
rob@100
|
15347 * The sphere() function draws a sphere with radius r centered at coordinate 0, 0, 0.
|
rob@100
|
15348 * A sphere is a hollow ball made from tessellated triangles.
|
rob@100
|
15349 *
|
rob@100
|
15350 * @param {int|float} r the radius of the sphere
|
rob@100
|
15351 */
|
rob@100
|
15352 Drawing2D.prototype.sphere = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
15353
|
rob@100
|
15354 Drawing3D.prototype.sphere = function() {
|
rob@100
|
15355 var sRad = arguments[0];
|
rob@100
|
15356
|
rob@100
|
15357 if ((sphereDetailU < 3) || (sphereDetailV < 2)) {
|
rob@100
|
15358 p.sphereDetail(30);
|
rob@100
|
15359 }
|
rob@100
|
15360
|
rob@100
|
15361 // Modeling transformation.
|
rob@100
|
15362 var model = new PMatrix3D();
|
rob@100
|
15363 model.scale(sRad, sRad, sRad);
|
rob@100
|
15364
|
rob@100
|
15365 // viewing transformation needs to have Y flipped
|
rob@100
|
15366 // becuase that's what Processing does.
|
rob@100
|
15367 var view = new PMatrix3D();
|
rob@100
|
15368 view.scale(1, -1, 1);
|
rob@100
|
15369 view.apply(modelView.array());
|
rob@100
|
15370 view.transpose();
|
rob@100
|
15371
|
rob@100
|
15372 if (doFill) {
|
rob@100
|
15373 // Calculating the normal matrix can be expensive, so only
|
rob@100
|
15374 // do it if it's necessary.
|
rob@100
|
15375 if(lightCount > 0){
|
rob@100
|
15376 // Create a normal transformation matrix.
|
rob@100
|
15377 var v = new PMatrix3D();
|
rob@100
|
15378 v.set(view);
|
rob@100
|
15379
|
rob@100
|
15380 var m = new PMatrix3D();
|
rob@100
|
15381 m.set(model);
|
rob@100
|
15382
|
rob@100
|
15383 v.mult(m);
|
rob@100
|
15384
|
rob@100
|
15385 var normalMatrix = new PMatrix3D();
|
rob@100
|
15386 normalMatrix.set(v);
|
rob@100
|
15387 normalMatrix.invert();
|
rob@100
|
15388 normalMatrix.transpose();
|
rob@100
|
15389
|
rob@100
|
15390 uniformMatrix("uNormalTransform3d", programObject3D, "uNormalTransform", false, normalMatrix.array());
|
rob@100
|
15391 vertexAttribPointer("aNormal3d", programObject3D, "aNormal", 3, sphereBuffer);
|
rob@100
|
15392 }
|
rob@100
|
15393 else{
|
rob@100
|
15394 disableVertexAttribPointer("aNormal3d", programObject3D, "aNormal");
|
rob@100
|
15395 }
|
rob@100
|
15396
|
rob@100
|
15397 curContext.useProgram(programObject3D);
|
rob@100
|
15398 disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture");
|
rob@100
|
15399
|
rob@100
|
15400 uniformMatrix("uModel3d", programObject3D, "uModel", false, model.array());
|
rob@100
|
15401 uniformMatrix("uView3d", programObject3D, "uView", false, view.array());
|
rob@100
|
15402 vertexAttribPointer("aVertex3d", programObject3D, "aVertex", 3, sphereBuffer);
|
rob@100
|
15403
|
rob@100
|
15404 // Turn off per vertex colors.
|
rob@100
|
15405 disableVertexAttribPointer("aColor3d", programObject3D, "aColor");
|
rob@100
|
15406
|
rob@100
|
15407 // fix stitching problems. (lines get occluded by triangles
|
rob@100
|
15408 // since they share the same depth values). This is not entirely
|
rob@100
|
15409 // working, but it's a start for drawing the outline. So
|
rob@100
|
15410 // developers can start playing around with styles.
|
rob@100
|
15411 curContext.enable(curContext.POLYGON_OFFSET_FILL);
|
rob@100
|
15412 curContext.polygonOffset(1, 1);
|
rob@100
|
15413 uniformf("uColor3d", programObject3D, "uColor", fillStyle);
|
rob@100
|
15414 curContext.drawArrays(curContext.TRIANGLE_STRIP, 0, sphereVerts.length / 3);
|
rob@100
|
15415 curContext.disable(curContext.POLYGON_OFFSET_FILL);
|
rob@100
|
15416 }
|
rob@100
|
15417
|
rob@100
|
15418 // Draw the sphere outline.
|
rob@100
|
15419 if (lineWidth > 0 && doStroke) {
|
rob@100
|
15420 curContext.useProgram(programObject2D);
|
rob@100
|
15421 uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
|
rob@100
|
15422 uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
|
rob@100
|
15423 vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, sphereBuffer);
|
rob@100
|
15424 disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
|
rob@100
|
15425 uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
|
rob@100
|
15426 uniformi("uIsDrawingText", programObject2D, "uIsDrawingText", false);
|
rob@100
|
15427 curContext.drawArrays(curContext.LINE_STRIP, 0, sphereVerts.length / 3);
|
rob@100
|
15428 }
|
rob@100
|
15429 };
|
rob@100
|
15430
|
rob@100
|
15431 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
15432 // Coordinates
|
rob@100
|
15433 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
15434
|
rob@100
|
15435 /**
|
rob@100
|
15436 * Returns the three-dimensional X, Y, Z position in model space. This returns
|
rob@100
|
15437 * the X value for a given coordinate based on the current set of transformations
|
rob@100
|
15438 * (scale, rotate, translate, etc.) The X value can be used to place an object
|
rob@100
|
15439 * in space relative to the location of the original point once the transformations
|
rob@100
|
15440 * are no longer in use.<br />
|
rob@100
|
15441 * <br />
|
rob@100
|
15442 *
|
rob@100
|
15443 * @param {int | float} x 3D x coordinate to be mapped
|
rob@100
|
15444 * @param {int | float} y 3D y coordinate to be mapped
|
rob@100
|
15445 * @param {int | float} z 3D z coordinate to be mapped
|
rob@100
|
15446 *
|
rob@100
|
15447 * @returns {float}
|
rob@100
|
15448 *
|
rob@100
|
15449 * @see modelY
|
rob@100
|
15450 * @see modelZ
|
rob@100
|
15451 */
|
rob@100
|
15452 p.modelX = function(x, y, z) {
|
rob@100
|
15453 var mv = modelView.array();
|
rob@100
|
15454 var ci = cameraInv.array();
|
rob@100
|
15455
|
rob@100
|
15456 var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
|
rob@100
|
15457 var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
|
rob@100
|
15458 var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
|
rob@100
|
15459 var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
|
rob@100
|
15460
|
rob@100
|
15461 var ox = ci[0] * ax + ci[1] * ay + ci[2] * az + ci[3] * aw;
|
rob@100
|
15462 var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
|
rob@100
|
15463
|
rob@100
|
15464 return (ow !== 0) ? ox / ow : ox;
|
rob@100
|
15465 };
|
rob@100
|
15466
|
rob@100
|
15467 /**
|
rob@100
|
15468 * Returns the three-dimensional X, Y, Z position in model space. This returns
|
rob@100
|
15469 * the Y value for a given coordinate based on the current set of transformations
|
rob@100
|
15470 * (scale, rotate, translate, etc.) The Y value can be used to place an object in
|
rob@100
|
15471 * space relative to the location of the original point once the transformations
|
rob@100
|
15472 * are no longer in use.<br />
|
rob@100
|
15473 * <br />
|
rob@100
|
15474 *
|
rob@100
|
15475 * @param {int | float} x 3D x coordinate to be mapped
|
rob@100
|
15476 * @param {int | float} y 3D y coordinate to be mapped
|
rob@100
|
15477 * @param {int | float} z 3D z coordinate to be mapped
|
rob@100
|
15478 *
|
rob@100
|
15479 * @returns {float}
|
rob@100
|
15480 *
|
rob@100
|
15481 * @see modelX
|
rob@100
|
15482 * @see modelZ
|
rob@100
|
15483 */
|
rob@100
|
15484 p.modelY = function(x, y, z) {
|
rob@100
|
15485 var mv = modelView.array();
|
rob@100
|
15486 var ci = cameraInv.array();
|
rob@100
|
15487
|
rob@100
|
15488 var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
|
rob@100
|
15489 var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
|
rob@100
|
15490 var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
|
rob@100
|
15491 var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
|
rob@100
|
15492
|
rob@100
|
15493 var oy = ci[4] * ax + ci[5] * ay + ci[6] * az + ci[7] * aw;
|
rob@100
|
15494 var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
|
rob@100
|
15495
|
rob@100
|
15496 return (ow !== 0) ? oy / ow : oy;
|
rob@100
|
15497 };
|
rob@100
|
15498
|
rob@100
|
15499 /**
|
rob@100
|
15500 * Returns the three-dimensional X, Y, Z position in model space. This returns
|
rob@100
|
15501 * the Z value for a given coordinate based on the current set of transformations
|
rob@100
|
15502 * (scale, rotate, translate, etc.) The Z value can be used to place an object in
|
rob@100
|
15503 * space relative to the location of the original point once the transformations
|
rob@100
|
15504 * are no longer in use.
|
rob@100
|
15505 *
|
rob@100
|
15506 * @param {int | float} x 3D x coordinate to be mapped
|
rob@100
|
15507 * @param {int | float} y 3D y coordinate to be mapped
|
rob@100
|
15508 * @param {int | float} z 3D z coordinate to be mapped
|
rob@100
|
15509 *
|
rob@100
|
15510 * @returns {float}
|
rob@100
|
15511 *
|
rob@100
|
15512 * @see modelX
|
rob@100
|
15513 * @see modelY
|
rob@100
|
15514 */
|
rob@100
|
15515 p.modelZ = function(x, y, z) {
|
rob@100
|
15516 var mv = modelView.array();
|
rob@100
|
15517 var ci = cameraInv.array();
|
rob@100
|
15518
|
rob@100
|
15519 var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
|
rob@100
|
15520 var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
|
rob@100
|
15521 var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
|
rob@100
|
15522 var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
|
rob@100
|
15523
|
rob@100
|
15524 var oz = ci[8] * ax + ci[9] * ay + ci[10] * az + ci[11] * aw;
|
rob@100
|
15525 var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
|
rob@100
|
15526
|
rob@100
|
15527 return (ow !== 0) ? oz / ow : oz;
|
rob@100
|
15528 };
|
rob@100
|
15529
|
rob@100
|
15530 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
15531 // Material Properties
|
rob@100
|
15532 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
15533
|
rob@100
|
15534 /**
|
rob@100
|
15535 * Sets the ambient reflectance for shapes drawn to the screen. This is
|
rob@100
|
15536 * combined with the ambient light component of environment. The color
|
rob@100
|
15537 * components set through the parameters define the reflectance. For example in
|
rob@100
|
15538 * the default color mode, setting v1=255, v2=126, v3=0, would cause all the
|
rob@100
|
15539 * red light to reflect and half of the green light to reflect. Used in combination
|
rob@100
|
15540 * with <b>emissive()</b>, <b>specular()</b>, and <b>shininess()</b> in setting
|
rob@100
|
15541 * the materal properties of shapes.
|
rob@100
|
15542 *
|
rob@100
|
15543 * @param {int | float} gray
|
rob@100
|
15544 *
|
rob@100
|
15545 * @returns none
|
rob@100
|
15546 *
|
rob@100
|
15547 * @see emissive
|
rob@100
|
15548 * @see specular
|
rob@100
|
15549 * @see shininess
|
rob@100
|
15550 */
|
rob@100
|
15551 Drawing2D.prototype.ambient = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
15552
|
rob@100
|
15553 Drawing3D.prototype.ambient = function(v1, v2, v3) {
|
rob@100
|
15554 curContext.useProgram(programObject3D);
|
rob@100
|
15555 uniformi("uUsingMat3d", programObject3D, "uUsingMat", true);
|
rob@100
|
15556 var col = p.color(v1, v2, v3);
|
rob@100
|
15557 uniformf("uMaterialAmbient3d", programObject3D, "uMaterialAmbient", p.color.toGLArray(col).slice(0, 3));
|
rob@100
|
15558 };
|
rob@100
|
15559
|
rob@100
|
15560 /**
|
rob@100
|
15561 * Sets the emissive color of the material used for drawing shapes
|
rob@100
|
15562 * drawn to the screen. Used in combination with ambient(), specular(),
|
rob@100
|
15563 * and shininess() in setting the material properties of shapes.
|
rob@100
|
15564 *
|
rob@100
|
15565 * Can be called in the following ways:
|
rob@100
|
15566 *
|
rob@100
|
15567 * emissive(gray)
|
rob@100
|
15568 * @param {int | float} gray number specifying value between white and black
|
rob@100
|
15569 *
|
rob@100
|
15570 * emissive(color)
|
rob@100
|
15571 * @param {color} color any value of the color datatype
|
rob@100
|
15572 *
|
rob@100
|
15573 * emissive(v1, v2, v3)
|
rob@100
|
15574 * @param {int | float} v1 red or hue value
|
rob@100
|
15575 * @param {int | float} v2 green or saturation value
|
rob@100
|
15576 * @param {int | float} v3 blue or brightness value
|
rob@100
|
15577 *
|
rob@100
|
15578 * @returns none
|
rob@100
|
15579 *
|
rob@100
|
15580 * @see ambient
|
rob@100
|
15581 * @see specular
|
rob@100
|
15582 * @see shininess
|
rob@100
|
15583 */
|
rob@100
|
15584 Drawing2D.prototype.emissive = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
15585
|
rob@100
|
15586 Drawing3D.prototype.emissive = function(v1, v2, v3) {
|
rob@100
|
15587 curContext.useProgram(programObject3D);
|
rob@100
|
15588 uniformi("uUsingMat3d", programObject3D, "uUsingMat", true);
|
rob@100
|
15589 var col = p.color(v1, v2, v3);
|
rob@100
|
15590 uniformf("uMaterialEmissive3d", programObject3D, "uMaterialEmissive", p.color.toGLArray(col).slice(0, 3));
|
rob@100
|
15591 };
|
rob@100
|
15592
|
rob@100
|
15593 /**
|
rob@100
|
15594 * Sets the amount of gloss in the surface of shapes. Used in combination with
|
rob@100
|
15595 * <b>ambient()</b>, <b>specular()</b>, and <b>emissive()</b> in setting the
|
rob@100
|
15596 * material properties of shapes.
|
rob@100
|
15597 *
|
rob@100
|
15598 * @param {float} shine degree of shininess
|
rob@100
|
15599 *
|
rob@100
|
15600 * @returns none
|
rob@100
|
15601 */
|
rob@100
|
15602 Drawing2D.prototype.shininess = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
15603
|
rob@100
|
15604 Drawing3D.prototype.shininess = function(shine) {
|
rob@100
|
15605 curContext.useProgram(programObject3D);
|
rob@100
|
15606 uniformi("uUsingMat3d", programObject3D, "uUsingMat", true);
|
rob@100
|
15607 uniformf("uShininess3d", programObject3D, "uShininess", shine);
|
rob@100
|
15608 };
|
rob@100
|
15609
|
rob@100
|
15610 /**
|
rob@100
|
15611 * Sets the specular color of the materials used for shapes drawn to the screen,
|
rob@100
|
15612 * which sets the color of hightlights. Specular refers to light which bounces
|
rob@100
|
15613 * off a surface in a perferred direction (rather than bouncing in all directions
|
rob@100
|
15614 * like a diffuse light). Used in combination with emissive(), ambient(), and
|
rob@100
|
15615 * shininess() in setting the material properties of shapes.
|
rob@100
|
15616 *
|
rob@100
|
15617 * Can be called in the following ways:
|
rob@100
|
15618 *
|
rob@100
|
15619 * specular(gray)
|
rob@100
|
15620 * @param {int | float} gray number specifying value between white and black
|
rob@100
|
15621 *
|
rob@100
|
15622 * specular(gray, alpha)
|
rob@100
|
15623 * @param {int | float} gray number specifying value between white and black
|
rob@100
|
15624 * @param {int | float} alpha opacity
|
rob@100
|
15625 *
|
rob@100
|
15626 * specular(color)
|
rob@100
|
15627 * @param {color} color any value of the color datatype
|
rob@100
|
15628 *
|
rob@100
|
15629 * specular(v1, v2, v3)
|
rob@100
|
15630 * @param {int | float} v1 red or hue value
|
rob@100
|
15631 * @param {int | float} v2 green or saturation value
|
rob@100
|
15632 * @param {int | float} v3 blue or brightness value
|
rob@100
|
15633 *
|
rob@100
|
15634 * specular(v1, v2, v3, alpha)
|
rob@100
|
15635 * @param {int | float} v1 red or hue value
|
rob@100
|
15636 * @param {int | float} v2 green or saturation value
|
rob@100
|
15637 * @param {int | float} v3 blue or brightness value
|
rob@100
|
15638 * @param {int | float} alpha opacity
|
rob@100
|
15639 *
|
rob@100
|
15640 * @returns none
|
rob@100
|
15641 *
|
rob@100
|
15642 * @see ambient
|
rob@100
|
15643 * @see emissive
|
rob@100
|
15644 * @see shininess
|
rob@100
|
15645 */
|
rob@100
|
15646 Drawing2D.prototype.specular = DrawingShared.prototype.a3DOnlyFunction;
|
rob@100
|
15647
|
rob@100
|
15648 Drawing3D.prototype.specular = function(v1, v2, v3) {
|
rob@100
|
15649 curContext.useProgram(programObject3D);
|
rob@100
|
15650 uniformi("uUsingMat3d", programObject3D, "uUsingMat", true);
|
rob@100
|
15651 var col = p.color(v1, v2, v3);
|
rob@100
|
15652 uniformf("uMaterialSpecular3d", programObject3D, "uMaterialSpecular", p.color.toGLArray(col).slice(0, 3));
|
rob@100
|
15653 };
|
rob@100
|
15654
|
rob@100
|
15655 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
15656 // Coordinates
|
rob@100
|
15657 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
15658
|
rob@100
|
15659 /**
|
rob@100
|
15660 * Takes a three-dimensional X, Y, Z position and returns the X value for
|
rob@100
|
15661 * where it will appear on a (two-dimensional) screen.
|
rob@100
|
15662 *
|
rob@100
|
15663 * @param {int | float} x 3D x coordinate to be mapped
|
rob@100
|
15664 * @param {int | float} y 3D y coordinate to be mapped
|
rob@100
|
15665 * @param {int | float} z 3D z optional coordinate to be mapped
|
rob@100
|
15666 *
|
rob@100
|
15667 * @returns {float}
|
rob@100
|
15668 *
|
rob@100
|
15669 * @see screenY
|
rob@100
|
15670 * @see screenZ
|
rob@100
|
15671 */
|
rob@100
|
15672 p.screenX = function( x, y, z ) {
|
rob@100
|
15673 var mv = modelView.array();
|
rob@100
|
15674 if( mv.length === 16 )
|
rob@100
|
15675 {
|
rob@100
|
15676 var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
|
rob@100
|
15677 var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
|
rob@100
|
15678 var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
|
rob@100
|
15679 var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
|
rob@100
|
15680
|
rob@100
|
15681 var pj = projection.array();
|
rob@100
|
15682
|
rob@100
|
15683 var ox = pj[ 0]*ax + pj[ 1]*ay + pj[ 2]*az + pj[ 3]*aw;
|
rob@100
|
15684 var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
|
rob@100
|
15685
|
rob@100
|
15686 if ( ow !== 0 ){
|
rob@100
|
15687 ox /= ow;
|
rob@100
|
15688 }
|
rob@100
|
15689 return p.width * ( 1 + ox ) / 2.0;
|
rob@100
|
15690 }
|
rob@100
|
15691 // We assume that we're in 2D
|
rob@100
|
15692 return modelView.multX(x, y);
|
rob@100
|
15693 };
|
rob@100
|
15694
|
rob@100
|
15695 /**
|
rob@100
|
15696 * Takes a three-dimensional X, Y, Z position and returns the Y value for
|
rob@100
|
15697 * where it will appear on a (two-dimensional) screen.
|
rob@100
|
15698 *
|
rob@100
|
15699 * @param {int | float} x 3D x coordinate to be mapped
|
rob@100
|
15700 * @param {int | float} y 3D y coordinate to be mapped
|
rob@100
|
15701 * @param {int | float} z 3D z optional coordinate to be mapped
|
rob@100
|
15702 *
|
rob@100
|
15703 * @returns {float}
|
rob@100
|
15704 *
|
rob@100
|
15705 * @see screenX
|
rob@100
|
15706 * @see screenZ
|
rob@100
|
15707 */
|
rob@100
|
15708 p.screenY = function screenY( x, y, z ) {
|
rob@100
|
15709 var mv = modelView.array();
|
rob@100
|
15710 if( mv.length === 16 ) {
|
rob@100
|
15711 var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
|
rob@100
|
15712 var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
|
rob@100
|
15713 var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
|
rob@100
|
15714 var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
|
rob@100
|
15715
|
rob@100
|
15716 var pj = projection.array();
|
rob@100
|
15717
|
rob@100
|
15718 var oy = pj[ 4]*ax + pj[ 5]*ay + pj[ 6]*az + pj[ 7]*aw;
|
rob@100
|
15719 var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
|
rob@100
|
15720
|
rob@100
|
15721 if ( ow !== 0 ){
|
rob@100
|
15722 oy /= ow;
|
rob@100
|
15723 }
|
rob@100
|
15724 return p.height * ( 1 + oy ) / 2.0;
|
rob@100
|
15725 }
|
rob@100
|
15726 // We assume that we're in 2D
|
rob@100
|
15727 return modelView.multY(x, y);
|
rob@100
|
15728 };
|
rob@100
|
15729
|
rob@100
|
15730 /**
|
rob@100
|
15731 * Takes a three-dimensional X, Y, Z position and returns the Z value for
|
rob@100
|
15732 * where it will appear on a (two-dimensional) screen.
|
rob@100
|
15733 *
|
rob@100
|
15734 * @param {int | float} x 3D x coordinate to be mapped
|
rob@100
|
15735 * @param {int | float} y 3D y coordinate to be mapped
|
rob@100
|
15736 * @param {int | float} z 3D z coordinate to be mapped
|
rob@100
|
15737 *
|
rob@100
|
15738 * @returns {float}
|
rob@100
|
15739 *
|
rob@100
|
15740 * @see screenX
|
rob@100
|
15741 * @see screenY
|
rob@100
|
15742 */
|
rob@100
|
15743 p.screenZ = function screenZ( x, y, z ) {
|
rob@100
|
15744 var mv = modelView.array();
|
rob@100
|
15745 if( mv.length !== 16 ) {
|
rob@100
|
15746 return 0;
|
rob@100
|
15747 }
|
rob@100
|
15748
|
rob@100
|
15749 var pj = projection.array();
|
rob@100
|
15750
|
rob@100
|
15751 var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
|
rob@100
|
15752 var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
|
rob@100
|
15753 var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
|
rob@100
|
15754 var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
|
rob@100
|
15755
|
rob@100
|
15756 var oz = pj[ 8]*ax + pj[ 9]*ay + pj[10]*az + pj[11]*aw;
|
rob@100
|
15757 var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
|
rob@100
|
15758
|
rob@100
|
15759 if ( ow !== 0 ) {
|
rob@100
|
15760 oz /= ow;
|
rob@100
|
15761 }
|
rob@100
|
15762 return ( oz + 1 ) / 2.0;
|
rob@100
|
15763 };
|
rob@100
|
15764
|
rob@100
|
15765 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
15766 // Style functions
|
rob@100
|
15767 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
15768 /**
|
rob@100
|
15769 * The fill() function sets the color used to fill shapes. For example, if you run <b>fill(204, 102, 0)</b>, all subsequent shapes will be filled with orange.
|
rob@100
|
15770 * This color is either specified in terms of the RGB or HSB color depending on the current <b>colorMode()</b>
|
rob@100
|
15771 *(the default color space is RGB, with each value in the range from 0 to 255).
|
rob@100
|
15772 * <br><br>When using hexadecimal notation to specify a color, use "#" or "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA).
|
rob@100
|
15773 * The # syntax uses six digits to specify a color (the way colors are specified in HTML and CSS). When using the hexadecimal notation starting with "0x",
|
rob@100
|
15774 * the hexadecimal value must be specified with eight characters; the first two characters define the alpha component and the remainder the red, green, and blue components.
|
rob@100
|
15775 * <br><br>The value for the parameter "gray" must be less than or equal to the current maximum value as specified by <b>colorMode()</b>. The default maximum value is 255.
|
rob@100
|
15776 * <br><br>To change the color of an image (or a texture), use tint().
|
rob@100
|
15777 *
|
rob@100
|
15778 * @param {int|float} gray number specifying value between white and black
|
rob@100
|
15779 * @param {int|float} value1 red or hue value
|
rob@100
|
15780 * @param {int|float} value2 green or saturation value
|
rob@100
|
15781 * @param {int|float} value3 blue or brightness value
|
rob@100
|
15782 * @param {int|float} alpha opacity of the fill
|
rob@100
|
15783 * @param {Color} color any value of the color datatype
|
rob@100
|
15784 * @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
|
rob@100
|
15785 *
|
rob@100
|
15786 * @see #noFill()
|
rob@100
|
15787 * @see #stroke()
|
rob@100
|
15788 * @see #tint()
|
rob@100
|
15789 * @see #background()
|
rob@100
|
15790 * @see #colorMode()
|
rob@100
|
15791 */
|
rob@100
|
15792 DrawingShared.prototype.fill = function() {
|
rob@100
|
15793 var color = p.color.apply(this, arguments);
|
rob@100
|
15794 if(color === currentFillColor && doFill) {
|
rob@100
|
15795 return;
|
rob@100
|
15796 }
|
rob@100
|
15797 doFill = true;
|
rob@100
|
15798 currentFillColor = color;
|
rob@100
|
15799 };
|
rob@100
|
15800
|
rob@100
|
15801 Drawing2D.prototype.fill = function() {
|
rob@100
|
15802 DrawingShared.prototype.fill.apply(this, arguments);
|
rob@100
|
15803 isFillDirty = true;
|
rob@100
|
15804 };
|
rob@100
|
15805
|
rob@100
|
15806 Drawing3D.prototype.fill = function() {
|
rob@100
|
15807 DrawingShared.prototype.fill.apply(this, arguments);
|
rob@100
|
15808 fillStyle = p.color.toGLArray(currentFillColor);
|
rob@100
|
15809 };
|
rob@100
|
15810
|
rob@100
|
15811 function executeContextFill() {
|
rob@100
|
15812 if(doFill) {
|
rob@100
|
15813 if(isFillDirty) {
|
rob@100
|
15814 curContext.fillStyle = p.color.toString(currentFillColor);
|
rob@100
|
15815 isFillDirty = false;
|
rob@100
|
15816 }
|
rob@100
|
15817 curContext.fill();
|
rob@100
|
15818 }
|
rob@100
|
15819 }
|
rob@100
|
15820
|
rob@100
|
15821 /**
|
rob@100
|
15822 * The noFill() function disables filling geometry. If both <b>noStroke()</b> and <b>noFill()</b>
|
rob@100
|
15823 * are called, no shapes will be drawn to the screen.
|
rob@100
|
15824 *
|
rob@100
|
15825 * @see #fill()
|
rob@100
|
15826 *
|
rob@100
|
15827 */
|
rob@100
|
15828 p.noFill = function() {
|
rob@100
|
15829 doFill = false;
|
rob@100
|
15830 };
|
rob@100
|
15831
|
rob@100
|
15832 /**
|
rob@100
|
15833 * The stroke() function sets the color used to draw lines and borders around shapes. This color
|
rob@100
|
15834 * is either specified in terms of the RGB or HSB color depending on the
|
rob@100
|
15835 * current <b>colorMode()</b> (the default color space is RGB, with each
|
rob@100
|
15836 * value in the range from 0 to 255).
|
rob@100
|
15837 * <br><br>When using hexadecimal notation to specify a color, use "#" or
|
rob@100
|
15838 * "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
|
rob@100
|
15839 * digits to specify a color (the way colors are specified in HTML and CSS).
|
rob@100
|
15840 * When using the hexadecimal notation starting with "0x", the hexadecimal
|
rob@100
|
15841 * value must be specified with eight characters; the first two characters
|
rob@100
|
15842 * define the alpha component and the remainder the red, green, and blue
|
rob@100
|
15843 * components.
|
rob@100
|
15844 * <br><br>The value for the parameter "gray" must be less than or equal
|
rob@100
|
15845 * to the current maximum value as specified by <b>colorMode()</b>.
|
rob@100
|
15846 * The default maximum value is 255.
|
rob@100
|
15847 *
|
rob@100
|
15848 * @param {int|float} gray number specifying value between white and black
|
rob@100
|
15849 * @param {int|float} value1 red or hue value
|
rob@100
|
15850 * @param {int|float} value2 green or saturation value
|
rob@100
|
15851 * @param {int|float} value3 blue or brightness value
|
rob@100
|
15852 * @param {int|float} alpha opacity of the stroke
|
rob@100
|
15853 * @param {Color} color any value of the color datatype
|
rob@100
|
15854 * @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
|
rob@100
|
15855 *
|
rob@100
|
15856 * @see #fill()
|
rob@100
|
15857 * @see #noStroke()
|
rob@100
|
15858 * @see #tint()
|
rob@100
|
15859 * @see #background()
|
rob@100
|
15860 * @see #colorMode()
|
rob@100
|
15861 */
|
rob@100
|
15862 DrawingShared.prototype.stroke = function() {
|
rob@100
|
15863 var color = p.color.apply(this, arguments);
|
rob@100
|
15864 if(color === currentStrokeColor && doStroke) {
|
rob@100
|
15865 return;
|
rob@100
|
15866 }
|
rob@100
|
15867 doStroke = true;
|
rob@100
|
15868 currentStrokeColor = color;
|
rob@100
|
15869 };
|
rob@100
|
15870
|
rob@100
|
15871 Drawing2D.prototype.stroke = function() {
|
rob@100
|
15872 DrawingShared.prototype.stroke.apply(this, arguments);
|
rob@100
|
15873 isStrokeDirty = true;
|
rob@100
|
15874 };
|
rob@100
|
15875
|
rob@100
|
15876 Drawing3D.prototype.stroke = function() {
|
rob@100
|
15877 DrawingShared.prototype.stroke.apply(this, arguments);
|
rob@100
|
15878 strokeStyle = p.color.toGLArray(currentStrokeColor);
|
rob@100
|
15879 };
|
rob@100
|
15880
|
rob@100
|
15881 function executeContextStroke() {
|
rob@100
|
15882 if(doStroke) {
|
rob@100
|
15883 if(isStrokeDirty) {
|
rob@100
|
15884 curContext.strokeStyle = p.color.toString(currentStrokeColor);
|
rob@100
|
15885 isStrokeDirty = false;
|
rob@100
|
15886 }
|
rob@100
|
15887 curContext.stroke();
|
rob@100
|
15888 }
|
rob@100
|
15889 }
|
rob@100
|
15890
|
rob@100
|
15891 /**
|
rob@100
|
15892 * The noStroke() function disables drawing the stroke (outline). If both <b>noStroke()</b> and
|
rob@100
|
15893 * <b>noFill()</b> are called, no shapes will be drawn to the screen.
|
rob@100
|
15894 *
|
rob@100
|
15895 * @see #stroke()
|
rob@100
|
15896 */
|
rob@100
|
15897 p.noStroke = function() {
|
rob@100
|
15898 doStroke = false;
|
rob@100
|
15899 };
|
rob@100
|
15900
|
rob@100
|
15901 /**
|
rob@100
|
15902 * The strokeWeight() function sets the width of the stroke used for lines, points, and the border around shapes.
|
rob@100
|
15903 * All widths are set in units of pixels.
|
rob@100
|
15904 *
|
rob@100
|
15905 * @param {int|float} w the weight (in pixels) of the stroke
|
rob@100
|
15906 */
|
rob@100
|
15907 DrawingShared.prototype.strokeWeight = function(w) {
|
rob@100
|
15908 lineWidth = w;
|
rob@100
|
15909 };
|
rob@100
|
15910
|
rob@100
|
15911 Drawing2D.prototype.strokeWeight = function(w) {
|
rob@100
|
15912 DrawingShared.prototype.strokeWeight.apply(this, arguments);
|
rob@100
|
15913 curContext.lineWidth = w;
|
rob@100
|
15914 };
|
rob@100
|
15915
|
rob@100
|
15916 Drawing3D.prototype.strokeWeight = function(w) {
|
rob@100
|
15917 DrawingShared.prototype.strokeWeight.apply(this, arguments);
|
rob@100
|
15918
|
rob@100
|
15919 // Processing groups the weight of points and lines under this one function,
|
rob@100
|
15920 // but for WebGL, we need to set a uniform for points and call a function for line.
|
rob@100
|
15921
|
rob@100
|
15922 curContext.useProgram(programObject2D);
|
rob@100
|
15923 uniformf("pointSize2d", programObject2D, "uPointSize", w);
|
rob@100
|
15924
|
rob@100
|
15925 curContext.useProgram(programObjectUnlitShape);
|
rob@100
|
15926 uniformf("pointSizeUnlitShape", programObjectUnlitShape, "uPointSize", w);
|
rob@100
|
15927
|
rob@100
|
15928 curContext.lineWidth(w);
|
rob@100
|
15929 };
|
rob@100
|
15930
|
rob@100
|
15931 /**
|
rob@100
|
15932 * The strokeCap() function sets the style for rendering line endings. These ends are either squared, extended, or rounded and
|
rob@100
|
15933 * specified with the corresponding parameters SQUARE, PROJECT, and ROUND. The default cap is ROUND.
|
rob@100
|
15934 * This function is not available with the P2D, P3D, or OPENGL renderers
|
rob@100
|
15935 *
|
rob@100
|
15936 * @param {int} value Either SQUARE, PROJECT, or ROUND
|
rob@100
|
15937 */
|
rob@100
|
15938 p.strokeCap = function(value) {
|
rob@100
|
15939 drawing.$ensureContext().lineCap = value;
|
rob@100
|
15940 };
|
rob@100
|
15941
|
rob@100
|
15942 /**
|
rob@100
|
15943 * The strokeJoin() function sets the style of the joints which connect line segments.
|
rob@100
|
15944 * These joints are either mitered, beveled, or rounded and specified with the corresponding parameters MITER, BEVEL, and ROUND. The default joint is MITER.
|
rob@100
|
15945 * This function is not available with the P2D, P3D, or OPENGL renderers
|
rob@100
|
15946 *
|
rob@100
|
15947 * @param {int} value Either SQUARE, PROJECT, or ROUND
|
rob@100
|
15948 */
|
rob@100
|
15949 p.strokeJoin = function(value) {
|
rob@100
|
15950 drawing.$ensureContext().lineJoin = value;
|
rob@100
|
15951 };
|
rob@100
|
15952
|
rob@100
|
15953 /**
|
rob@100
|
15954 * The smooth() function draws all geometry with smooth (anti-aliased) edges. This will slow down the frame rate of the application,
|
rob@100
|
15955 * but will enhance the visual refinement. <br/><br/>
|
rob@100
|
15956 * Note that smooth() will also improve image quality of resized images, and noSmooth() will disable image (and font) smoothing altogether.
|
rob@100
|
15957 * When working with a 3D sketch, smooth will draw points as circles rather than squares.
|
rob@100
|
15958 *
|
rob@100
|
15959 * @see #noSmooth()
|
rob@100
|
15960 * @see #hint()
|
rob@100
|
15961 * @see #size()
|
rob@100
|
15962 */
|
rob@100
|
15963
|
rob@100
|
15964 Drawing2D.prototype.smooth = function() {
|
rob@100
|
15965 renderSmooth = true;
|
rob@100
|
15966 var style = curElement.style;
|
rob@100
|
15967 style.setProperty("image-rendering", "optimizeQuality", "important");
|
rob@100
|
15968 style.setProperty("-ms-interpolation-mode", "bicubic", "important");
|
rob@100
|
15969 if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) {
|
rob@100
|
15970 curContext.mozImageSmoothingEnabled = true;
|
rob@100
|
15971 }
|
rob@100
|
15972 };
|
rob@100
|
15973
|
rob@100
|
15974 Drawing3D.prototype.smooth = function(){
|
rob@100
|
15975 renderSmooth = true;
|
rob@100
|
15976 };
|
rob@100
|
15977
|
rob@100
|
15978 /**
|
rob@100
|
15979 * The noSmooth() function draws all geometry with jagged (aliased) edges.
|
rob@100
|
15980 *
|
rob@100
|
15981 * @see #smooth()
|
rob@100
|
15982 */
|
rob@100
|
15983
|
rob@100
|
15984 Drawing2D.prototype.noSmooth = function() {
|
rob@100
|
15985 renderSmooth = false;
|
rob@100
|
15986 var style = curElement.style;
|
rob@100
|
15987 style.setProperty("image-rendering", "optimizeSpeed", "important");
|
rob@100
|
15988 style.setProperty("image-rendering", "-moz-crisp-edges", "important");
|
rob@100
|
15989 style.setProperty("image-rendering", "-webkit-optimize-contrast", "important");
|
rob@100
|
15990 style.setProperty("image-rendering", "optimize-contrast", "important");
|
rob@100
|
15991 style.setProperty("-ms-interpolation-mode", "nearest-neighbor", "important");
|
rob@100
|
15992 if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) {
|
rob@100
|
15993 curContext.mozImageSmoothingEnabled = false;
|
rob@100
|
15994 }
|
rob@100
|
15995 };
|
rob@100
|
15996
|
rob@100
|
15997 Drawing3D.prototype.noSmooth = function(){
|
rob@100
|
15998 renderSmooth = false;
|
rob@100
|
15999 };
|
rob@100
|
16000
|
rob@100
|
16001 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
16002 // Vector drawing functions
|
rob@100
|
16003 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
16004 /**
|
rob@100
|
16005 * The point() function draws a point, a coordinate in space at the dimension of one pixel.
|
rob@100
|
16006 * The first parameter is the horizontal value for the point, the second
|
rob@100
|
16007 * value is the vertical value for the point, and the optional third value
|
rob@100
|
16008 * is the depth value. Drawing this shape in 3D using the <b>z</b>
|
rob@100
|
16009 * parameter requires the P3D or OPENGL parameter in combination with
|
rob@100
|
16010 * size as shown in the above example.
|
rob@100
|
16011 *
|
rob@100
|
16012 * @param {int|float} x x-coordinate of the point
|
rob@100
|
16013 * @param {int|float} y y-coordinate of the point
|
rob@100
|
16014 * @param {int|float} z z-coordinate of the point
|
rob@100
|
16015 *
|
rob@100
|
16016 * @see #beginShape()
|
rob@100
|
16017 */
|
rob@100
|
16018 Drawing2D.prototype.point = function(x, y) {
|
rob@100
|
16019 if (!doStroke) {
|
rob@100
|
16020 return;
|
rob@100
|
16021 }
|
rob@100
|
16022
|
rob@100
|
16023 x = Math.round(x);
|
rob@100
|
16024 y = Math.round(y);
|
rob@100
|
16025 curContext.fillStyle = p.color.toString(currentStrokeColor);
|
rob@100
|
16026 isFillDirty = true;
|
rob@100
|
16027 // Draw a circle for any point larger than 1px
|
rob@100
|
16028 if (lineWidth > 1) {
|
rob@100
|
16029 curContext.beginPath();
|
rob@100
|
16030 curContext.arc(x, y, lineWidth / 2, 0, PConstants.TWO_PI, false);
|
rob@100
|
16031 curContext.fill();
|
rob@100
|
16032 } else {
|
rob@100
|
16033 curContext.fillRect(x, y, 1, 1);
|
rob@100
|
16034 }
|
rob@100
|
16035 };
|
rob@100
|
16036
|
rob@100
|
16037 Drawing3D.prototype.point = function(x, y, z) {
|
rob@100
|
16038 var model = new PMatrix3D();
|
rob@100
|
16039
|
rob@100
|
16040 // move point to position
|
rob@100
|
16041 model.translate(x, y, z || 0);
|
rob@100
|
16042 model.transpose();
|
rob@100
|
16043
|
rob@100
|
16044 var view = new PMatrix3D();
|
rob@100
|
16045 view.scale(1, -1, 1);
|
rob@100
|
16046 view.apply(modelView.array());
|
rob@100
|
16047 view.transpose();
|
rob@100
|
16048
|
rob@100
|
16049 curContext.useProgram(programObject2D);
|
rob@100
|
16050 uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
|
rob@100
|
16051 uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
|
rob@100
|
16052
|
rob@100
|
16053 if (lineWidth > 0 && doStroke) {
|
rob@100
|
16054 // this will be replaced with the new bit shifting color code
|
rob@100
|
16055 uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
|
rob@100
|
16056 uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", false);
|
rob@100
|
16057 uniformi("uSmooth2d", programObject2D, "uSmooth", renderSmooth);
|
rob@100
|
16058 vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, pointBuffer);
|
rob@100
|
16059 disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
|
rob@100
|
16060 curContext.drawArrays(curContext.POINTS, 0, 1);
|
rob@100
|
16061 }
|
rob@100
|
16062 };
|
rob@100
|
16063
|
rob@100
|
16064 /**
|
rob@100
|
16065 * Using the <b>beginShape()</b> and <b>endShape()</b> functions allow creating more complex forms.
|
rob@100
|
16066 * <b>beginShape()</b> begins recording vertices for a shape and <b>endShape()</b> stops recording.
|
rob@100
|
16067 * The value of the <b>MODE</b> parameter tells it which types of shapes to create from the provided vertices.
|
rob@100
|
16068 * With no mode specified, the shape can be any irregular polygon. After calling the <b>beginShape()</b> function,
|
rob@100
|
16069 * a series of <b>vertex()</b> commands must follow. To stop drawing the shape, call <b>endShape()</b>.
|
rob@100
|
16070 * The <b>vertex()</b> function with two parameters specifies a position in 2D and the <b>vertex()</b>
|
rob@100
|
16071 * function with three parameters specifies a position in 3D. Each shape will be outlined with the current
|
rob@100
|
16072 * stroke color and filled with the fill color.
|
rob@100
|
16073 *
|
rob@100
|
16074 * @param {int} MODE either POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRIP.
|
rob@100
|
16075 *
|
rob@100
|
16076 * @see endShape
|
rob@100
|
16077 * @see vertex
|
rob@100
|
16078 * @see curveVertex
|
rob@100
|
16079 * @see bezierVertex
|
rob@100
|
16080 */
|
rob@100
|
16081 p.beginShape = function(type) {
|
rob@100
|
16082 curShape = type;
|
rob@100
|
16083 curvePoints = [];
|
rob@100
|
16084 vertArray = [];
|
rob@100
|
16085 };
|
rob@100
|
16086
|
rob@100
|
16087 /**
|
rob@100
|
16088 * All shapes are constructed by connecting a series of vertices. <b>vertex()</b> is used to specify the vertex
|
rob@100
|
16089 * coordinates for points, lines, triangles, quads, and polygons and is used exclusively within the <b>beginShape()</b>
|
rob@100
|
16090 * and <b>endShape()</b> function. <br /><br />Drawing a vertex in 3D using the <b>z</b> parameter requires the P3D or
|
rob@100
|
16091 * OPENGL parameter in combination with size as shown in the above example.<br /><br />This function is also used to map a
|
rob@100
|
16092 * texture onto the geometry. The <b>texture()</b> function declares the texture to apply to the geometry and the <b>u</b>
|
rob@100
|
16093 * and <b>v</b> coordinates set define the mapping of this texture to the form. By default, the coordinates used for
|
rob@100
|
16094 * <b>u</b> and <b>v</b> are specified in relation to the image's size in pixels, but this relation can be changed with
|
rob@100
|
16095 * <b>textureMode()</b>.
|
rob@100
|
16096 *
|
rob@100
|
16097 * @param {int | float} x x-coordinate of the vertex
|
rob@100
|
16098 * @param {int | float} y y-coordinate of the vertex
|
rob@100
|
16099 * @param {boolean} moveto flag to indicate whether this is a new subpath
|
rob@100
|
16100 *
|
rob@100
|
16101 * @see beginShape
|
rob@100
|
16102 * @see endShape
|
rob@100
|
16103 * @see bezierVertex
|
rob@100
|
16104 * @see curveVertex
|
rob@100
|
16105 * @see texture
|
rob@100
|
16106 */
|
rob@100
|
16107
|
rob@100
|
16108 Drawing2D.prototype.vertex = function(x, y, moveTo) {
|
rob@100
|
16109 var vert = [];
|
rob@100
|
16110
|
rob@100
|
16111 if (firstVert) { firstVert = false; }
|
rob@100
|
16112 vert.isVert = true;
|
rob@100
|
16113
|
rob@100
|
16114 vert[0] = x;
|
rob@100
|
16115 vert[1] = y;
|
rob@100
|
16116 vert[2] = 0;
|
rob@100
|
16117 vert[3] = 0;
|
rob@100
|
16118 vert[4] = 0;
|
rob@100
|
16119
|
rob@100
|
16120 // fill and stroke color
|
rob@100
|
16121 vert[5] = currentFillColor;
|
rob@100
|
16122 vert[6] = currentStrokeColor;
|
rob@100
|
16123
|
rob@100
|
16124 vertArray.push(vert);
|
rob@100
|
16125 if (moveTo) {
|
rob@100
|
16126 vertArray[vertArray.length-1].moveTo = moveTo;
|
rob@100
|
16127 }
|
rob@100
|
16128 };
|
rob@100
|
16129
|
rob@100
|
16130 Drawing3D.prototype.vertex = function(x, y, z, u, v) {
|
rob@100
|
16131 var vert = [];
|
rob@100
|
16132
|
rob@100
|
16133 if (firstVert) { firstVert = false; }
|
rob@100
|
16134 vert.isVert = true;
|
rob@100
|
16135
|
rob@100
|
16136 if (v === undef && usingTexture) {
|
rob@100
|
16137 v = u;
|
rob@100
|
16138 u = z;
|
rob@100
|
16139 z = 0;
|
rob@100
|
16140 }
|
rob@100
|
16141
|
rob@100
|
16142 // Convert u and v to normalized coordinates
|
rob@100
|
16143 if (u !== undef && v !== undef) {
|
rob@100
|
16144 if (curTextureMode === PConstants.IMAGE) {
|
rob@100
|
16145 u /= curTexture.width;
|
rob@100
|
16146 v /= curTexture.height;
|
rob@100
|
16147 }
|
rob@100
|
16148 u = u > 1 ? 1 : u;
|
rob@100
|
16149 u = u < 0 ? 0 : u;
|
rob@100
|
16150 v = v > 1 ? 1 : v;
|
rob@100
|
16151 v = v < 0 ? 0 : v;
|
rob@100
|
16152 }
|
rob@100
|
16153
|
rob@100
|
16154 vert[0] = x;
|
rob@100
|
16155 vert[1] = y;
|
rob@100
|
16156 vert[2] = z || 0;
|
rob@100
|
16157 vert[3] = u || 0;
|
rob@100
|
16158 vert[4] = v || 0;
|
rob@100
|
16159
|
rob@100
|
16160 // fill rgba
|
rob@100
|
16161 vert[5] = fillStyle[0];
|
rob@100
|
16162 vert[6] = fillStyle[1];
|
rob@100
|
16163 vert[7] = fillStyle[2];
|
rob@100
|
16164 vert[8] = fillStyle[3];
|
rob@100
|
16165 // stroke rgba
|
rob@100
|
16166 vert[9] = strokeStyle[0];
|
rob@100
|
16167 vert[10] = strokeStyle[1];
|
rob@100
|
16168 vert[11] = strokeStyle[2];
|
rob@100
|
16169 vert[12] = strokeStyle[3];
|
rob@100
|
16170 //normals
|
rob@100
|
16171 vert[13] = normalX;
|
rob@100
|
16172 vert[14] = normalY;
|
rob@100
|
16173 vert[15] = normalZ;
|
rob@100
|
16174
|
rob@100
|
16175 vertArray.push(vert);
|
rob@100
|
16176 };
|
rob@100
|
16177
|
rob@100
|
16178 /**
|
rob@100
|
16179 * @private
|
rob@100
|
16180 * Renders 3D points created from calls to vertex and beginShape/endShape
|
rob@100
|
16181 *
|
rob@100
|
16182 * @param {Array} vArray an array of vertex coordinate
|
rob@100
|
16183 * @param {Array} cArray an array of colours used for the vertices
|
rob@100
|
16184 *
|
rob@100
|
16185 * @see beginShape
|
rob@100
|
16186 * @see endShape
|
rob@100
|
16187 * @see vertex
|
rob@100
|
16188 */
|
rob@100
|
16189 var point3D = function(vArray, cArray){
|
rob@100
|
16190 var view = new PMatrix3D();
|
rob@100
|
16191 view.scale(1, -1, 1);
|
rob@100
|
16192 view.apply(modelView.array());
|
rob@100
|
16193 view.transpose();
|
rob@100
|
16194
|
rob@100
|
16195 curContext.useProgram(programObjectUnlitShape);
|
rob@100
|
16196
|
rob@100
|
16197 uniformMatrix("uViewUS", programObjectUnlitShape, "uView", false, view.array());
|
rob@100
|
16198 uniformi("uSmoothUS", programObjectUnlitShape, "uSmooth", renderSmooth);
|
rob@100
|
16199
|
rob@100
|
16200 vertexAttribPointer("aVertexUS", programObjectUnlitShape, "aVertex", 3, pointBuffer);
|
rob@100
|
16201 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW);
|
rob@100
|
16202
|
rob@100
|
16203 vertexAttribPointer("aColorUS", programObjectUnlitShape, "aColor", 4, fillColorBuffer);
|
rob@100
|
16204 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW);
|
rob@100
|
16205
|
rob@100
|
16206 curContext.drawArrays(curContext.POINTS, 0, vArray.length/3);
|
rob@100
|
16207 };
|
rob@100
|
16208
|
rob@100
|
16209 /**
|
rob@100
|
16210 * @private
|
rob@100
|
16211 * Renders 3D lines created from calls to beginShape/vertex/endShape - based on the mode specified LINES, LINE_LOOP, etc.
|
rob@100
|
16212 *
|
rob@100
|
16213 * @param {Array} vArray an array of vertex coordinate
|
rob@100
|
16214 * @param {String} mode either LINES, LINE_LOOP, or LINE_STRIP
|
rob@100
|
16215 * @param {Array} cArray an array of colours used for the vertices
|
rob@100
|
16216 *
|
rob@100
|
16217 * @see beginShape
|
rob@100
|
16218 * @see endShape
|
rob@100
|
16219 * @see vertex
|
rob@100
|
16220 */
|
rob@100
|
16221 var line3D = function(vArray, mode, cArray){
|
rob@100
|
16222 var ctxMode;
|
rob@100
|
16223 if (mode === "LINES"){
|
rob@100
|
16224 ctxMode = curContext.LINES;
|
rob@100
|
16225 }
|
rob@100
|
16226 else if(mode === "LINE_LOOP"){
|
rob@100
|
16227 ctxMode = curContext.LINE_LOOP;
|
rob@100
|
16228 }
|
rob@100
|
16229 else{
|
rob@100
|
16230 ctxMode = curContext.LINE_STRIP;
|
rob@100
|
16231 }
|
rob@100
|
16232
|
rob@100
|
16233 var view = new PMatrix3D();
|
rob@100
|
16234 view.scale(1, -1, 1);
|
rob@100
|
16235 view.apply(modelView.array());
|
rob@100
|
16236 view.transpose();
|
rob@100
|
16237
|
rob@100
|
16238 curContext.useProgram(programObjectUnlitShape);
|
rob@100
|
16239 uniformMatrix("uViewUS", programObjectUnlitShape, "uView", false, view.array());
|
rob@100
|
16240 vertexAttribPointer("aVertexUS", programObjectUnlitShape, "aVertex", 3, lineBuffer);
|
rob@100
|
16241 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW);
|
rob@100
|
16242 vertexAttribPointer("aColorUS", programObjectUnlitShape, "aColor", 4, strokeColorBuffer);
|
rob@100
|
16243 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW);
|
rob@100
|
16244 curContext.drawArrays(ctxMode, 0, vArray.length/3);
|
rob@100
|
16245 };
|
rob@100
|
16246
|
rob@100
|
16247 /**
|
rob@100
|
16248 * @private
|
rob@100
|
16249 * Render filled shapes created from calls to beginShape/vertex/endShape - based on the mode specified TRIANGLES, etc.
|
rob@100
|
16250 *
|
rob@100
|
16251 * @param {Array} vArray an array of vertex coordinate
|
rob@100
|
16252 * @param {String} mode either LINES, LINE_LOOP, or LINE_STRIP
|
rob@100
|
16253 * @param {Array} cArray an array of colours used for the vertices
|
rob@100
|
16254 * @param {Array} tArray an array of u,v coordinates for textures
|
rob@100
|
16255 *
|
rob@100
|
16256 * @see beginShape
|
rob@100
|
16257 * @see endShape
|
rob@100
|
16258 * @see vertex
|
rob@100
|
16259 */
|
rob@100
|
16260 var fill3D = function(vArray, mode, cArray, tArray){
|
rob@100
|
16261 var ctxMode;
|
rob@100
|
16262 if (mode === "TRIANGLES") {
|
rob@100
|
16263 ctxMode = curContext.TRIANGLES;
|
rob@100
|
16264 } else if(mode === "TRIANGLE_FAN") {
|
rob@100
|
16265 ctxMode = curContext.TRIANGLE_FAN;
|
rob@100
|
16266 } else {
|
rob@100
|
16267 ctxMode = curContext.TRIANGLE_STRIP;
|
rob@100
|
16268 }
|
rob@100
|
16269
|
rob@100
|
16270 var view = new PMatrix3D();
|
rob@100
|
16271 view.scale( 1, -1, 1 );
|
rob@100
|
16272 view.apply( modelView.array() );
|
rob@100
|
16273 view.transpose();
|
rob@100
|
16274
|
rob@100
|
16275 curContext.useProgram( programObject3D );
|
rob@100
|
16276 uniformMatrix( "model3d", programObject3D, "uModel", false, [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1] );
|
rob@100
|
16277 uniformMatrix( "view3d", programObject3D, "uView", false, view.array() );
|
rob@100
|
16278 curContext.enable( curContext.POLYGON_OFFSET_FILL );
|
rob@100
|
16279 curContext.polygonOffset( 1, 1 );
|
rob@100
|
16280 uniformf( "color3d", programObject3D, "uColor", [-1,0,0,0] );
|
rob@100
|
16281 vertexAttribPointer( "vertex3d", programObject3D, "aVertex", 3, fillBuffer );
|
rob@100
|
16282 curContext.bufferData( curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW );
|
rob@100
|
16283
|
rob@100
|
16284 // if we are using a texture and a tint, then overwrite the
|
rob@100
|
16285 // contents of the color buffer with the current tint
|
rob@100
|
16286 if ( usingTexture && curTint !== null ){
|
rob@100
|
16287 curTint3d( cArray );
|
rob@100
|
16288 }
|
rob@100
|
16289
|
rob@100
|
16290 vertexAttribPointer( "aColor3d", programObject3D, "aColor", 4, fillColorBuffer );
|
rob@100
|
16291 curContext.bufferData( curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW );
|
rob@100
|
16292
|
rob@100
|
16293 // No support for lights....yet
|
rob@100
|
16294 disableVertexAttribPointer( "aNormal3d", programObject3D, "aNormal" );
|
rob@100
|
16295
|
rob@100
|
16296 if ( usingTexture ) {
|
rob@100
|
16297 uniformi( "uUsingTexture3d", programObject3D, "uUsingTexture", usingTexture );
|
rob@100
|
16298 vertexAttribPointer( "aTexture3d", programObject3D, "aTexture", 2, shapeTexVBO );
|
rob@100
|
16299 curContext.bufferData( curContext.ARRAY_BUFFER, new Float32Array(tArray), curContext.STREAM_DRAW );
|
rob@100
|
16300 }
|
rob@100
|
16301
|
rob@100
|
16302 curContext.drawArrays( ctxMode, 0, vArray.length/3 );
|
rob@100
|
16303 curContext.disable( curContext.POLYGON_OFFSET_FILL );
|
rob@100
|
16304 };
|
rob@100
|
16305
|
rob@100
|
16306 /**
|
rob@100
|
16307 * this series of three operations is used a lot in Drawing2D.prototype.endShape
|
rob@100
|
16308 * and has been split off as its own function, to tighten the code and allow for
|
rob@100
|
16309 * fewer bugs.
|
rob@100
|
16310 */
|
rob@100
|
16311 function fillStrokeClose() {
|
rob@100
|
16312 executeContextFill();
|
rob@100
|
16313 executeContextStroke();
|
rob@100
|
16314 curContext.closePath();
|
rob@100
|
16315 }
|
rob@100
|
16316
|
rob@100
|
16317 /**
|
rob@100
|
16318 * The endShape() function is the companion to beginShape() and may only be called after beginShape().
|
rob@100
|
16319 * When endshape() is called, all of image data defined since the previous call to beginShape() is written
|
rob@100
|
16320 * into the image buffer.
|
rob@100
|
16321 *
|
rob@100
|
16322 * @param {int} MODE Use CLOSE to close the shape
|
rob@100
|
16323 *
|
rob@100
|
16324 * @see beginShape
|
rob@100
|
16325 */
|
rob@100
|
16326 Drawing2D.prototype.endShape = function(mode) {
|
rob@100
|
16327 // Duplicated in Drawing3D; too many variables used
|
rob@100
|
16328 if (vertArray.length === 0) { return; }
|
rob@100
|
16329
|
rob@100
|
16330 var closeShape = mode === PConstants.CLOSE;
|
rob@100
|
16331
|
rob@100
|
16332 // if the shape is closed, the first element is also the last element
|
rob@100
|
16333 if (closeShape) {
|
rob@100
|
16334 vertArray.push(vertArray[0]);
|
rob@100
|
16335 }
|
rob@100
|
16336
|
rob@100
|
16337 var lineVertArray = [];
|
rob@100
|
16338 var fillVertArray = [];
|
rob@100
|
16339 var colorVertArray = [];
|
rob@100
|
16340 var strokeVertArray = [];
|
rob@100
|
16341 var texVertArray = [];
|
rob@100
|
16342 var cachedVertArray;
|
rob@100
|
16343
|
rob@100
|
16344 firstVert = true;
|
rob@100
|
16345 var i, j, k;
|
rob@100
|
16346 var vertArrayLength = vertArray.length;
|
rob@100
|
16347
|
rob@100
|
16348 for (i = 0; i < vertArrayLength; i++) {
|
rob@100
|
16349 cachedVertArray = vertArray[i];
|
rob@100
|
16350 for (j = 0; j < 3; j++) {
|
rob@100
|
16351 fillVertArray.push(cachedVertArray[j]);
|
rob@100
|
16352 }
|
rob@100
|
16353 }
|
rob@100
|
16354
|
rob@100
|
16355 // 5,6,7,8
|
rob@100
|
16356 // R,G,B,A - fill colour
|
rob@100
|
16357 for (i = 0; i < vertArrayLength; i++) {
|
rob@100
|
16358 cachedVertArray = vertArray[i];
|
rob@100
|
16359 for (j = 5; j < 9; j++) {
|
rob@100
|
16360 colorVertArray.push(cachedVertArray[j]);
|
rob@100
|
16361 }
|
rob@100
|
16362 }
|
rob@100
|
16363
|
rob@100
|
16364 // 9,10,11,12
|
rob@100
|
16365 // R, G, B, A - stroke colour
|
rob@100
|
16366 for (i = 0; i < vertArrayLength; i++) {
|
rob@100
|
16367 cachedVertArray = vertArray[i];
|
rob@100
|
16368 for (j = 9; j < 13; j++) {
|
rob@100
|
16369 strokeVertArray.push(cachedVertArray[j]);
|
rob@100
|
16370 }
|
rob@100
|
16371 }
|
rob@100
|
16372
|
rob@100
|
16373 // texture u,v
|
rob@100
|
16374 for (i = 0; i < vertArrayLength; i++) {
|
rob@100
|
16375 cachedVertArray = vertArray[i];
|
rob@100
|
16376 texVertArray.push(cachedVertArray[3]);
|
rob@100
|
16377 texVertArray.push(cachedVertArray[4]);
|
rob@100
|
16378 }
|
rob@100
|
16379
|
rob@100
|
16380 // curveVertex
|
rob@100
|
16381 if ( isCurve && (curShape === PConstants.POLYGON || curShape === undef) ) {
|
rob@100
|
16382 if (vertArrayLength > 3) {
|
rob@100
|
16383 var b = [],
|
rob@100
|
16384 s = 1 - curTightness;
|
rob@100
|
16385 curContext.beginPath();
|
rob@100
|
16386 curContext.moveTo(vertArray[1][0], vertArray[1][1]);
|
rob@100
|
16387 /*
|
rob@100
|
16388 * Matrix to convert from Catmull-Rom to cubic Bezier
|
rob@100
|
16389 * where t = curTightness
|
rob@100
|
16390 * |0 1 0 0 |
|
rob@100
|
16391 * |(t-1)/6 1 (1-t)/6 0 |
|
rob@100
|
16392 * |0 (1-t)/6 1 (t-1)/6 |
|
rob@100
|
16393 * |0 0 0 0 |
|
rob@100
|
16394 */
|
rob@100
|
16395 for (i = 1; (i+2) < vertArrayLength; i++) {
|
rob@100
|
16396 cachedVertArray = vertArray[i];
|
rob@100
|
16397 b[0] = [cachedVertArray[0], cachedVertArray[1]];
|
rob@100
|
16398 b[1] = [cachedVertArray[0] + (s * vertArray[i+1][0] - s * vertArray[i-1][0]) / 6,
|
rob@100
|
16399 cachedVertArray[1] + (s * vertArray[i+1][1] - s * vertArray[i-1][1]) / 6];
|
rob@100
|
16400 b[2] = [vertArray[i+1][0] + (s * vertArray[i][0] - s * vertArray[i+2][0]) / 6,
|
rob@100
|
16401 vertArray[i+1][1] + (s * vertArray[i][1] - s * vertArray[i+2][1]) / 6];
|
rob@100
|
16402 b[3] = [vertArray[i+1][0], vertArray[i+1][1]];
|
rob@100
|
16403 curContext.bezierCurveTo(b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]);
|
rob@100
|
16404 }
|
rob@100
|
16405 fillStrokeClose();
|
rob@100
|
16406 }
|
rob@100
|
16407 }
|
rob@100
|
16408
|
rob@100
|
16409 // bezierVertex
|
rob@100
|
16410 else if ( isBezier && (curShape === PConstants.POLYGON || curShape === undef) ) {
|
rob@100
|
16411 curContext.beginPath();
|
rob@100
|
16412 for (i = 0; i < vertArrayLength; i++) {
|
rob@100
|
16413 cachedVertArray = vertArray[i];
|
rob@100
|
16414 if (vertArray[i].isVert) { //if it is a vertex move to the position
|
rob@100
|
16415 if (vertArray[i].moveTo) {
|
rob@100
|
16416 curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16417 } else {
|
rob@100
|
16418 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16419 }
|
rob@100
|
16420 } else { //otherwise continue drawing bezier
|
rob@100
|
16421 curContext.bezierCurveTo(vertArray[i][0], vertArray[i][1], vertArray[i][2], vertArray[i][3], vertArray[i][4], vertArray[i][5]);
|
rob@100
|
16422 }
|
rob@100
|
16423 }
|
rob@100
|
16424 fillStrokeClose();
|
rob@100
|
16425 }
|
rob@100
|
16426
|
rob@100
|
16427 // render the vertices provided
|
rob@100
|
16428 else {
|
rob@100
|
16429 if (curShape === PConstants.POINTS) {
|
rob@100
|
16430 for (i = 0; i < vertArrayLength; i++) {
|
rob@100
|
16431 cachedVertArray = vertArray[i];
|
rob@100
|
16432 if (doStroke) {
|
rob@100
|
16433 p.stroke(cachedVertArray[6]);
|
rob@100
|
16434 }
|
rob@100
|
16435 p.point(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16436 }
|
rob@100
|
16437 } else if (curShape === PConstants.LINES) {
|
rob@100
|
16438 for (i = 0; (i + 1) < vertArrayLength; i+=2) {
|
rob@100
|
16439 cachedVertArray = vertArray[i];
|
rob@100
|
16440 if (doStroke) {
|
rob@100
|
16441 p.stroke(vertArray[i+1][6]);
|
rob@100
|
16442 }
|
rob@100
|
16443 p.line(cachedVertArray[0], cachedVertArray[1], vertArray[i+1][0], vertArray[i+1][1]);
|
rob@100
|
16444 }
|
rob@100
|
16445 } else if (curShape === PConstants.TRIANGLES) {
|
rob@100
|
16446 for (i = 0; (i + 2) < vertArrayLength; i+=3) {
|
rob@100
|
16447 cachedVertArray = vertArray[i];
|
rob@100
|
16448 curContext.beginPath();
|
rob@100
|
16449 curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16450 curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
|
rob@100
|
16451 curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]);
|
rob@100
|
16452 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16453
|
rob@100
|
16454 if (doFill) {
|
rob@100
|
16455 p.fill(vertArray[i+2][5]);
|
rob@100
|
16456 executeContextFill();
|
rob@100
|
16457 }
|
rob@100
|
16458 if (doStroke) {
|
rob@100
|
16459 p.stroke(vertArray[i+2][6]);
|
rob@100
|
16460 executeContextStroke();
|
rob@100
|
16461 }
|
rob@100
|
16462
|
rob@100
|
16463 curContext.closePath();
|
rob@100
|
16464 }
|
rob@100
|
16465 } else if (curShape === PConstants.TRIANGLE_STRIP) {
|
rob@100
|
16466 for (i = 0; (i+1) < vertArrayLength; i++) {
|
rob@100
|
16467 cachedVertArray = vertArray[i];
|
rob@100
|
16468 curContext.beginPath();
|
rob@100
|
16469 curContext.moveTo(vertArray[i+1][0], vertArray[i+1][1]);
|
rob@100
|
16470 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16471
|
rob@100
|
16472 if (doStroke) {
|
rob@100
|
16473 p.stroke(vertArray[i+1][6]);
|
rob@100
|
16474 }
|
rob@100
|
16475 if (doFill) {
|
rob@100
|
16476 p.fill(vertArray[i+1][5]);
|
rob@100
|
16477 }
|
rob@100
|
16478
|
rob@100
|
16479 if (i + 2 < vertArrayLength) {
|
rob@100
|
16480 curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]);
|
rob@100
|
16481 if (doStroke) {
|
rob@100
|
16482 p.stroke(vertArray[i+2][6]);
|
rob@100
|
16483 }
|
rob@100
|
16484 if (doFill) {
|
rob@100
|
16485 p.fill(vertArray[i+2][5]);
|
rob@100
|
16486 }
|
rob@100
|
16487 }
|
rob@100
|
16488 fillStrokeClose();
|
rob@100
|
16489 }
|
rob@100
|
16490 } else if (curShape === PConstants.TRIANGLE_FAN) {
|
rob@100
|
16491 if (vertArrayLength > 2) {
|
rob@100
|
16492 curContext.beginPath();
|
rob@100
|
16493 curContext.moveTo(vertArray[0][0], vertArray[0][1]);
|
rob@100
|
16494 curContext.lineTo(vertArray[1][0], vertArray[1][1]);
|
rob@100
|
16495 curContext.lineTo(vertArray[2][0], vertArray[2][1]);
|
rob@100
|
16496
|
rob@100
|
16497 if (doFill) {
|
rob@100
|
16498 p.fill(vertArray[2][5]);
|
rob@100
|
16499 executeContextFill();
|
rob@100
|
16500 }
|
rob@100
|
16501 if (doStroke) {
|
rob@100
|
16502 p.stroke(vertArray[2][6]);
|
rob@100
|
16503 executeContextStroke();
|
rob@100
|
16504 }
|
rob@100
|
16505
|
rob@100
|
16506 curContext.closePath();
|
rob@100
|
16507 for (i = 3; i < vertArrayLength; i++) {
|
rob@100
|
16508 cachedVertArray = vertArray[i];
|
rob@100
|
16509 curContext.beginPath();
|
rob@100
|
16510 curContext.moveTo(vertArray[0][0], vertArray[0][1]);
|
rob@100
|
16511 curContext.lineTo(vertArray[i-1][0], vertArray[i-1][1]);
|
rob@100
|
16512 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16513
|
rob@100
|
16514 if (doFill) {
|
rob@100
|
16515 p.fill(cachedVertArray[5]);
|
rob@100
|
16516 executeContextFill();
|
rob@100
|
16517 }
|
rob@100
|
16518 if (doStroke) {
|
rob@100
|
16519 p.stroke(cachedVertArray[6]);
|
rob@100
|
16520 executeContextStroke();
|
rob@100
|
16521 }
|
rob@100
|
16522
|
rob@100
|
16523 curContext.closePath();
|
rob@100
|
16524 }
|
rob@100
|
16525 }
|
rob@100
|
16526 } else if (curShape === PConstants.QUADS) {
|
rob@100
|
16527 for (i = 0; (i + 3) < vertArrayLength; i+=4) {
|
rob@100
|
16528 cachedVertArray = vertArray[i];
|
rob@100
|
16529 curContext.beginPath();
|
rob@100
|
16530 curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16531 for (j = 1; j < 4; j++) {
|
rob@100
|
16532 curContext.lineTo(vertArray[i+j][0], vertArray[i+j][1]);
|
rob@100
|
16533 }
|
rob@100
|
16534 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16535
|
rob@100
|
16536 if (doFill) {
|
rob@100
|
16537 p.fill(vertArray[i+3][5]);
|
rob@100
|
16538 executeContextFill();
|
rob@100
|
16539 }
|
rob@100
|
16540 if (doStroke) {
|
rob@100
|
16541 p.stroke(vertArray[i+3][6]);
|
rob@100
|
16542 executeContextStroke();
|
rob@100
|
16543 }
|
rob@100
|
16544
|
rob@100
|
16545 curContext.closePath();
|
rob@100
|
16546 }
|
rob@100
|
16547 } else if (curShape === PConstants.QUAD_STRIP) {
|
rob@100
|
16548 if (vertArrayLength > 3) {
|
rob@100
|
16549 for (i = 0; (i+1) < vertArrayLength; i+=2) {
|
rob@100
|
16550 cachedVertArray = vertArray[i];
|
rob@100
|
16551 curContext.beginPath();
|
rob@100
|
16552 if (i+3 < vertArrayLength) {
|
rob@100
|
16553 curContext.moveTo(vertArray[i+2][0], vertArray[i+2][1]);
|
rob@100
|
16554 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16555 curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
|
rob@100
|
16556 curContext.lineTo(vertArray[i+3][0], vertArray[i+3][1]);
|
rob@100
|
16557
|
rob@100
|
16558 if (doFill) {
|
rob@100
|
16559 p.fill(vertArray[i+3][5]);
|
rob@100
|
16560 }
|
rob@100
|
16561 if (doStroke) {
|
rob@100
|
16562 p.stroke(vertArray[i+3][6]);
|
rob@100
|
16563 }
|
rob@100
|
16564 } else {
|
rob@100
|
16565 curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16566 curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
|
rob@100
|
16567 }
|
rob@100
|
16568 fillStrokeClose();
|
rob@100
|
16569 }
|
rob@100
|
16570 }
|
rob@100
|
16571 } else {
|
rob@100
|
16572 curContext.beginPath();
|
rob@100
|
16573 curContext.moveTo(vertArray[0][0], vertArray[0][1]);
|
rob@100
|
16574 for (i = 1; i < vertArrayLength; i++) {
|
rob@100
|
16575 cachedVertArray = vertArray[i];
|
rob@100
|
16576 if (cachedVertArray.isVert) { //if it is a vertex move to the position
|
rob@100
|
16577 if (cachedVertArray.moveTo) {
|
rob@100
|
16578 curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16579 } else {
|
rob@100
|
16580 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
rob@100
|
16581 }
|
rob@100
|
16582 }
|
rob@100
|
16583 }
|
rob@100
|
16584 fillStrokeClose();
|
rob@100
|
16585 }
|
rob@100
|
16586 }
|
rob@100
|
16587
|
rob@100
|
16588 // Reset some settings
|
rob@100
|
16589 isCurve = false;
|
rob@100
|
16590 isBezier = false;
|
rob@100
|
16591 curveVertArray = [];
|
rob@100
|
16592 curveVertCount = 0;
|
rob@100
|
16593
|
rob@100
|
16594 // If the shape is closed, the first element was added as last element.
|
rob@100
|
16595 // We must remove it again to prevent the list of vertices from growing
|
rob@100
|
16596 // over successive calls to endShape(CLOSE)
|
rob@100
|
16597 if (closeShape) {
|
rob@100
|
16598 vertArray.pop();
|
rob@100
|
16599 }
|
rob@100
|
16600 };
|
rob@100
|
16601
|
rob@100
|
16602 Drawing3D.prototype.endShape = function(mode) {
|
rob@100
|
16603 // Duplicated in Drawing3D; too many variables used
|
rob@100
|
16604 if (vertArray.length === 0) { return; }
|
rob@100
|
16605
|
rob@100
|
16606 var closeShape = mode === PConstants.CLOSE;
|
rob@100
|
16607 var lineVertArray = [];
|
rob@100
|
16608 var fillVertArray = [];
|
rob@100
|
16609 var colorVertArray = [];
|
rob@100
|
16610 var strokeVertArray = [];
|
rob@100
|
16611 var texVertArray = [];
|
rob@100
|
16612 var cachedVertArray;
|
rob@100
|
16613
|
rob@100
|
16614 firstVert = true;
|
rob@100
|
16615 var i, j, k;
|
rob@100
|
16616 var vertArrayLength = vertArray.length;
|
rob@100
|
16617
|
rob@100
|
16618 for (i = 0; i < vertArrayLength; i++) {
|
rob@100
|
16619 cachedVertArray = vertArray[i];
|
rob@100
|
16620 for (j = 0; j < 3; j++) {
|
rob@100
|
16621 fillVertArray.push(cachedVertArray[j]);
|
rob@100
|
16622 }
|
rob@100
|
16623 }
|
rob@100
|
16624
|
rob@100
|
16625 // 5,6,7,8
|
rob@100
|
16626 // R,G,B,A - fill colour
|
rob@100
|
16627 for (i = 0; i < vertArrayLength; i++) {
|
rob@100
|
16628 cachedVertArray = vertArray[i];
|
rob@100
|
16629 for (j = 5; j < 9; j++) {
|
rob@100
|
16630 colorVertArray.push(cachedVertArray[j]);
|
rob@100
|
16631 }
|
rob@100
|
16632 }
|
rob@100
|
16633
|
rob@100
|
16634 // 9,10,11,12
|
rob@100
|
16635 // R, G, B, A - stroke colour
|
rob@100
|
16636 for (i = 0; i < vertArrayLength; i++) {
|
rob@100
|
16637 cachedVertArray = vertArray[i];
|
rob@100
|
16638 for (j = 9; j < 13; j++) {
|
rob@100
|
16639 strokeVertArray.push(cachedVertArray[j]);
|
rob@100
|
16640 }
|
rob@100
|
16641 }
|
rob@100
|
16642
|
rob@100
|
16643 // texture u,v
|
rob@100
|
16644 for (i = 0; i < vertArrayLength; i++) {
|
rob@100
|
16645 cachedVertArray = vertArray[i];
|
rob@100
|
16646 texVertArray.push(cachedVertArray[3]);
|
rob@100
|
16647 texVertArray.push(cachedVertArray[4]);
|
rob@100
|
16648 }
|
rob@100
|
16649
|
rob@100
|
16650 // if shape is closed, push the first point into the last point (including colours)
|
rob@100
|
16651 if (closeShape) {
|
rob@100
|
16652 fillVertArray.push(vertArray[0][0]);
|
rob@100
|
16653 fillVertArray.push(vertArray[0][1]);
|
rob@100
|
16654 fillVertArray.push(vertArray[0][2]);
|
rob@100
|
16655
|
rob@100
|
16656 for (i = 5; i < 9; i++) {
|
rob@100
|
16657 colorVertArray.push(vertArray[0][i]);
|
rob@100
|
16658 }
|
rob@100
|
16659
|
rob@100
|
16660 for (i = 9; i < 13; i++) {
|
rob@100
|
16661 strokeVertArray.push(vertArray[0][i]);
|
rob@100
|
16662 }
|
rob@100
|
16663
|
rob@100
|
16664 texVertArray.push(vertArray[0][3]);
|
rob@100
|
16665 texVertArray.push(vertArray[0][4]);
|
rob@100
|
16666 }
|
rob@100
|
16667 // End duplication
|
rob@100
|
16668
|
rob@100
|
16669 // curveVertex
|
rob@100
|
16670 if ( isCurve && (curShape === PConstants.POLYGON || curShape === undef) ) {
|
rob@100
|
16671 lineVertArray = fillVertArray;
|
rob@100
|
16672 if (doStroke) {
|
rob@100
|
16673 line3D(lineVertArray, null, strokeVertArray);
|
rob@100
|
16674 }
|
rob@100
|
16675 if (doFill) {
|
rob@100
|
16676 fill3D(fillVertArray, null, colorVertArray);
|
rob@100
|
16677 }
|
rob@100
|
16678 }
|
rob@100
|
16679 // bezierVertex
|
rob@100
|
16680 else if ( isBezier && (curShape === PConstants.POLYGON || curShape === undef) ) {
|
rob@100
|
16681 lineVertArray = fillVertArray;
|
rob@100
|
16682 lineVertArray.splice(lineVertArray.length - 3);
|
rob@100
|
16683 strokeVertArray.splice(strokeVertArray.length - 4);
|
rob@100
|
16684 if (doStroke) {
|
rob@100
|
16685 line3D(lineVertArray, null, strokeVertArray);
|
rob@100
|
16686 }
|
rob@100
|
16687 if (doFill) {
|
rob@100
|
16688 fill3D(fillVertArray, "TRIANGLES", colorVertArray);
|
rob@100
|
16689 }
|
rob@100
|
16690 }
|
rob@100
|
16691
|
rob@100
|
16692 // render the vertices provided
|
rob@100
|
16693 else {
|
rob@100
|
16694 if (curShape === PConstants.POINTS) { // if POINTS was the specified parameter in beginShape
|
rob@100
|
16695 for (i = 0; i < vertArrayLength; i++) { // loop through and push the point location information to the array
|
rob@100
|
16696 cachedVertArray = vertArray[i];
|
rob@100
|
16697 for (j = 0; j < 3; j++) {
|
rob@100
|
16698 lineVertArray.push(cachedVertArray[j]);
|
rob@100
|
16699 }
|
rob@100
|
16700 }
|
rob@100
|
16701 point3D(lineVertArray, strokeVertArray); // render function for points
|
rob@100
|
16702 } else if (curShape === PConstants.LINES) { // if LINES was the specified parameter in beginShape
|
rob@100
|
16703 for (i = 0; i < vertArrayLength; i++) { // loop through and push the point location information to the array
|
rob@100
|
16704 cachedVertArray = vertArray[i];
|
rob@100
|
16705 for (j = 0; j < 3; j++) {
|
rob@100
|
16706 lineVertArray.push(cachedVertArray[j]);
|
rob@100
|
16707 }
|
rob@100
|
16708 }
|
rob@100
|
16709 for (i = 0; i < vertArrayLength; i++) { // loop through and push the color information to the array
|
rob@100
|
16710 cachedVertArray = vertArray[i];
|
rob@100
|
16711 for (j = 5; j < 9; j++) {
|
rob@100
|
16712 colorVertArray.push(cachedVertArray[j]);
|
rob@100
|
16713 }
|
rob@100
|
16714 }
|
rob@100
|
16715 line3D(lineVertArray, "LINES", strokeVertArray); // render function for lines
|
rob@100
|
16716 } else if (curShape === PConstants.TRIANGLES) { // if TRIANGLES was the specified parameter in beginShape
|
rob@100
|
16717 if (vertArrayLength > 2) {
|
rob@100
|
16718 for (i = 0; (i+2) < vertArrayLength; i+=3) { // loop through the array per triangle
|
rob@100
|
16719 fillVertArray = [];
|
rob@100
|
16720 texVertArray = [];
|
rob@100
|
16721 lineVertArray = [];
|
rob@100
|
16722 colorVertArray = [];
|
rob@100
|
16723 strokeVertArray = [];
|
rob@100
|
16724 for (j = 0; j < 3; j++) {
|
rob@100
|
16725 for (k = 0; k < 3; k++) { // loop through and push
|
rob@100
|
16726 lineVertArray.push(vertArray[i+j][k]); // the line point location information
|
rob@100
|
16727 fillVertArray.push(vertArray[i+j][k]); // and fill point location information
|
rob@100
|
16728 }
|
rob@100
|
16729 }
|
rob@100
|
16730 for (j = 0; j < 3; j++) { // loop through and push the texture information
|
rob@100
|
16731 for (k = 3; k < 5; k++) {
|
rob@100
|
16732 texVertArray.push(vertArray[i+j][k]);
|
rob@100
|
16733 }
|
rob@100
|
16734 }
|
rob@100
|
16735 for (j = 0; j < 3; j++) {
|
rob@100
|
16736 for (k = 5; k < 9; k++) { // loop through and push
|
rob@100
|
16737 colorVertArray.push(vertArray[i+j][k]); // the colour information
|
rob@100
|
16738 strokeVertArray.push(vertArray[i+j][k+4]);// and the stroke information
|
rob@100
|
16739 }
|
rob@100
|
16740 }
|
rob@100
|
16741 if (doStroke) {
|
rob@100
|
16742 line3D(lineVertArray, "LINE_LOOP", strokeVertArray ); // line render function
|
rob@100
|
16743 }
|
rob@100
|
16744 if (doFill || usingTexture) {
|
rob@100
|
16745 fill3D(fillVertArray, "TRIANGLES", colorVertArray, texVertArray); // fill shape render function
|
rob@100
|
16746 }
|
rob@100
|
16747 }
|
rob@100
|
16748 }
|
rob@100
|
16749 } else if (curShape === PConstants.TRIANGLE_STRIP) { // if TRIANGLE_STRIP was the specified parameter in beginShape
|
rob@100
|
16750 if (vertArrayLength > 2) {
|
rob@100
|
16751 for (i = 0; (i+2) < vertArrayLength; i++) {
|
rob@100
|
16752 lineVertArray = [];
|
rob@100
|
16753 fillVertArray = [];
|
rob@100
|
16754 strokeVertArray = [];
|
rob@100
|
16755 colorVertArray = [];
|
rob@100
|
16756 texVertArray = [];
|
rob@100
|
16757 for (j = 0; j < 3; j++) {
|
rob@100
|
16758 for (k = 0; k < 3; k++) {
|
rob@100
|
16759 lineVertArray.push(vertArray[i+j][k]);
|
rob@100
|
16760 fillVertArray.push(vertArray[i+j][k]);
|
rob@100
|
16761 }
|
rob@100
|
16762 }
|
rob@100
|
16763 for (j = 0; j < 3; j++) {
|
rob@100
|
16764 for (k = 3; k < 5; k++) {
|
rob@100
|
16765 texVertArray.push(vertArray[i+j][k]);
|
rob@100
|
16766 }
|
rob@100
|
16767 }
|
rob@100
|
16768 for (j = 0; j < 3; j++) {
|
rob@100
|
16769 for (k = 5; k < 9; k++) {
|
rob@100
|
16770 strokeVertArray.push(vertArray[i+j][k+4]);
|
rob@100
|
16771 colorVertArray.push(vertArray[i+j][k]);
|
rob@100
|
16772 }
|
rob@100
|
16773 }
|
rob@100
|
16774
|
rob@100
|
16775 if (doFill || usingTexture) {
|
rob@100
|
16776 fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
|
rob@100
|
16777 }
|
rob@100
|
16778 if (doStroke) {
|
rob@100
|
16779 line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
|
rob@100
|
16780 }
|
rob@100
|
16781 }
|
rob@100
|
16782 }
|
rob@100
|
16783 } else if (curShape === PConstants.TRIANGLE_FAN) {
|
rob@100
|
16784 if (vertArrayLength > 2) {
|
rob@100
|
16785 for (i = 0; i < 3; i++) {
|
rob@100
|
16786 cachedVertArray = vertArray[i];
|
rob@100
|
16787 for (j = 0; j < 3; j++) {
|
rob@100
|
16788 lineVertArray.push(cachedVertArray[j]);
|
rob@100
|
16789 }
|
rob@100
|
16790 }
|
rob@100
|
16791 for (i = 0; i < 3; i++) {
|
rob@100
|
16792 cachedVertArray = vertArray[i];
|
rob@100
|
16793 for (j = 9; j < 13; j++) {
|
rob@100
|
16794 strokeVertArray.push(cachedVertArray[j]);
|
rob@100
|
16795 }
|
rob@100
|
16796 }
|
rob@100
|
16797 if (doStroke) {
|
rob@100
|
16798 line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
|
rob@100
|
16799 }
|
rob@100
|
16800
|
rob@100
|
16801 for (i = 2; (i+1) < vertArrayLength; i++) {
|
rob@100
|
16802 lineVertArray = [];
|
rob@100
|
16803 strokeVertArray = [];
|
rob@100
|
16804 lineVertArray.push(vertArray[0][0]);
|
rob@100
|
16805 lineVertArray.push(vertArray[0][1]);
|
rob@100
|
16806 lineVertArray.push(vertArray[0][2]);
|
rob@100
|
16807
|
rob@100
|
16808 strokeVertArray.push(vertArray[0][9]);
|
rob@100
|
16809 strokeVertArray.push(vertArray[0][10]);
|
rob@100
|
16810 strokeVertArray.push(vertArray[0][11]);
|
rob@100
|
16811 strokeVertArray.push(vertArray[0][12]);
|
rob@100
|
16812
|
rob@100
|
16813 for (j = 0; j < 2; j++) {
|
rob@100
|
16814 for (k = 0; k < 3; k++) {
|
rob@100
|
16815 lineVertArray.push(vertArray[i+j][k]);
|
rob@100
|
16816 }
|
rob@100
|
16817 }
|
rob@100
|
16818 for (j = 0; j < 2; j++) {
|
rob@100
|
16819 for (k = 9; k < 13; k++) {
|
rob@100
|
16820 strokeVertArray.push(vertArray[i+j][k]);
|
rob@100
|
16821 }
|
rob@100
|
16822 }
|
rob@100
|
16823 if (doStroke) {
|
rob@100
|
16824 line3D(lineVertArray, "LINE_STRIP",strokeVertArray);
|
rob@100
|
16825 }
|
rob@100
|
16826 }
|
rob@100
|
16827 if (doFill || usingTexture) {
|
rob@100
|
16828 fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
|
rob@100
|
16829 }
|
rob@100
|
16830 }
|
rob@100
|
16831 } else if (curShape === PConstants.QUADS) {
|
rob@100
|
16832 for (i = 0; (i + 3) < vertArrayLength; i+=4) {
|
rob@100
|
16833 lineVertArray = [];
|
rob@100
|
16834 for (j = 0; j < 4; j++) {
|
rob@100
|
16835 cachedVertArray = vertArray[i+j];
|
rob@100
|
16836 for (k = 0; k < 3; k++) {
|
rob@100
|
16837 lineVertArray.push(cachedVertArray[k]);
|
rob@100
|
16838 }
|
rob@100
|
16839 }
|
rob@100
|
16840 if (doStroke) {
|
rob@100
|
16841 line3D(lineVertArray, "LINE_LOOP",strokeVertArray);
|
rob@100
|
16842 }
|
rob@100
|
16843
|
rob@100
|
16844 if (doFill) {
|
rob@100
|
16845 fillVertArray = [];
|
rob@100
|
16846 colorVertArray = [];
|
rob@100
|
16847 texVertArray = [];
|
rob@100
|
16848 for (j = 0; j < 3; j++) {
|
rob@100
|
16849 fillVertArray.push(vertArray[i][j]);
|
rob@100
|
16850 }
|
rob@100
|
16851 for (j = 5; j < 9; j++) {
|
rob@100
|
16852 colorVertArray.push(vertArray[i][j]);
|
rob@100
|
16853 }
|
rob@100
|
16854
|
rob@100
|
16855 for (j = 0; j < 3; j++) {
|
rob@100
|
16856 fillVertArray.push(vertArray[i+1][j]);
|
rob@100
|
16857 }
|
rob@100
|
16858 for (j = 5; j < 9; j++) {
|
rob@100
|
16859 colorVertArray.push(vertArray[i+1][j]);
|
rob@100
|
16860 }
|
rob@100
|
16861
|
rob@100
|
16862 for (j = 0; j < 3; j++) {
|
rob@100
|
16863 fillVertArray.push(vertArray[i+3][j]);
|
rob@100
|
16864 }
|
rob@100
|
16865 for (j = 5; j < 9; j++) {
|
rob@100
|
16866 colorVertArray.push(vertArray[i+3][j]);
|
rob@100
|
16867 }
|
rob@100
|
16868
|
rob@100
|
16869 for (j = 0; j < 3; j++) {
|
rob@100
|
16870 fillVertArray.push(vertArray[i+2][j]);
|
rob@100
|
16871 }
|
rob@100
|
16872 for (j = 5; j < 9; j++) {
|
rob@100
|
16873 colorVertArray.push(vertArray[i+2][j]);
|
rob@100
|
16874 }
|
rob@100
|
16875
|
rob@100
|
16876 if (usingTexture) {
|
rob@100
|
16877 texVertArray.push(vertArray[i+0][3]);
|
rob@100
|
16878 texVertArray.push(vertArray[i+0][4]);
|
rob@100
|
16879 texVertArray.push(vertArray[i+1][3]);
|
rob@100
|
16880 texVertArray.push(vertArray[i+1][4]);
|
rob@100
|
16881 texVertArray.push(vertArray[i+3][3]);
|
rob@100
|
16882 texVertArray.push(vertArray[i+3][4]);
|
rob@100
|
16883 texVertArray.push(vertArray[i+2][3]);
|
rob@100
|
16884 texVertArray.push(vertArray[i+2][4]);
|
rob@100
|
16885 }
|
rob@100
|
16886
|
rob@100
|
16887 fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
|
rob@100
|
16888 }
|
rob@100
|
16889 }
|
rob@100
|
16890 } else if (curShape === PConstants.QUAD_STRIP) {
|
rob@100
|
16891 var tempArray = [];
|
rob@100
|
16892 if (vertArrayLength > 3) {
|
rob@100
|
16893 for (i = 0; i < 2; i++) {
|
rob@100
|
16894 cachedVertArray = vertArray[i];
|
rob@100
|
16895 for (j = 0; j < 3; j++) {
|
rob@100
|
16896 lineVertArray.push(cachedVertArray[j]);
|
rob@100
|
16897 }
|
rob@100
|
16898 }
|
rob@100
|
16899
|
rob@100
|
16900 for (i = 0; i < 2; i++) {
|
rob@100
|
16901 cachedVertArray = vertArray[i];
|
rob@100
|
16902 for (j = 9; j < 13; j++) {
|
rob@100
|
16903 strokeVertArray.push(cachedVertArray[j]);
|
rob@100
|
16904 }
|
rob@100
|
16905 }
|
rob@100
|
16906
|
rob@100
|
16907 line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
|
rob@100
|
16908 if (vertArrayLength > 4 && vertArrayLength % 2 > 0) {
|
rob@100
|
16909 tempArray = fillVertArray.splice(fillVertArray.length - 3);
|
rob@100
|
16910 vertArray.pop();
|
rob@100
|
16911 }
|
rob@100
|
16912 for (i = 0; (i+3) < vertArrayLength; i+=2) {
|
rob@100
|
16913 lineVertArray = [];
|
rob@100
|
16914 strokeVertArray = [];
|
rob@100
|
16915 for (j = 0; j < 3; j++) {
|
rob@100
|
16916 lineVertArray.push(vertArray[i+1][j]);
|
rob@100
|
16917 }
|
rob@100
|
16918 for (j = 0; j < 3; j++) {
|
rob@100
|
16919 lineVertArray.push(vertArray[i+3][j]);
|
rob@100
|
16920 }
|
rob@100
|
16921 for (j = 0; j < 3; j++) {
|
rob@100
|
16922 lineVertArray.push(vertArray[i+2][j]);
|
rob@100
|
16923 }
|
rob@100
|
16924 for (j = 0; j < 3; j++) {
|
rob@100
|
16925 lineVertArray.push(vertArray[i+0][j]);
|
rob@100
|
16926 }
|
rob@100
|
16927 for (j = 9; j < 13; j++) {
|
rob@100
|
16928 strokeVertArray.push(vertArray[i+1][j]);
|
rob@100
|
16929 }
|
rob@100
|
16930 for (j = 9; j < 13; j++) {
|
rob@100
|
16931 strokeVertArray.push(vertArray[i+3][j]);
|
rob@100
|
16932 }
|
rob@100
|
16933 for (j = 9; j < 13; j++) {
|
rob@100
|
16934 strokeVertArray.push(vertArray[i+2][j]);
|
rob@100
|
16935 }
|
rob@100
|
16936 for (j = 9; j < 13; j++) {
|
rob@100
|
16937 strokeVertArray.push(vertArray[i+0][j]);
|
rob@100
|
16938 }
|
rob@100
|
16939 if (doStroke) {
|
rob@100
|
16940 line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
|
rob@100
|
16941 }
|
rob@100
|
16942 }
|
rob@100
|
16943
|
rob@100
|
16944 if (doFill || usingTexture) {
|
rob@100
|
16945 fill3D(fillVertArray, "TRIANGLE_LIST", colorVertArray, texVertArray);
|
rob@100
|
16946 }
|
rob@100
|
16947 }
|
rob@100
|
16948 }
|
rob@100
|
16949 // If the user didn't specify a type (LINES, TRIANGLES, etc)
|
rob@100
|
16950 else {
|
rob@100
|
16951 // If only one vertex was specified, it must be a point
|
rob@100
|
16952 if (vertArrayLength === 1) {
|
rob@100
|
16953 for (j = 0; j < 3; j++) {
|
rob@100
|
16954 lineVertArray.push(vertArray[0][j]);
|
rob@100
|
16955 }
|
rob@100
|
16956 for (j = 9; j < 13; j++) {
|
rob@100
|
16957 strokeVertArray.push(vertArray[0][j]);
|
rob@100
|
16958 }
|
rob@100
|
16959 point3D(lineVertArray,strokeVertArray);
|
rob@100
|
16960 } else {
|
rob@100
|
16961 for (i = 0; i < vertArrayLength; i++) {
|
rob@100
|
16962 cachedVertArray = vertArray[i];
|
rob@100
|
16963 for (j = 0; j < 3; j++) {
|
rob@100
|
16964 lineVertArray.push(cachedVertArray[j]);
|
rob@100
|
16965 }
|
rob@100
|
16966 for (j = 5; j < 9; j++) {
|
rob@100
|
16967 strokeVertArray.push(cachedVertArray[j]);
|
rob@100
|
16968 }
|
rob@100
|
16969 }
|
rob@100
|
16970 if (doStroke && closeShape) {
|
rob@100
|
16971 line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
|
rob@100
|
16972 } else if (doStroke && !closeShape) {
|
rob@100
|
16973 line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
|
rob@100
|
16974 }
|
rob@100
|
16975
|
rob@100
|
16976 // fill is ignored if textures are used
|
rob@100
|
16977 if (doFill || usingTexture) {
|
rob@100
|
16978 fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
|
rob@100
|
16979 }
|
rob@100
|
16980 }
|
rob@100
|
16981 }
|
rob@100
|
16982 // everytime beginShape is followed by a call to
|
rob@100
|
16983 // texture(), texturing it turned back on. We do this to
|
rob@100
|
16984 // figure out if the shape should be textured or filled
|
rob@100
|
16985 // with a color.
|
rob@100
|
16986 usingTexture = false;
|
rob@100
|
16987 curContext.useProgram(programObject3D);
|
rob@100
|
16988 uniformi("usingTexture3d", programObject3D, "uUsingTexture", usingTexture);
|
rob@100
|
16989 }
|
rob@100
|
16990
|
rob@100
|
16991 // Reset some settings
|
rob@100
|
16992 isCurve = false;
|
rob@100
|
16993 isBezier = false;
|
rob@100
|
16994 curveVertArray = [];
|
rob@100
|
16995 curveVertCount = 0;
|
rob@100
|
16996 };
|
rob@100
|
16997
|
rob@100
|
16998 /**
|
rob@100
|
16999 * The function splineForward() setup forward-differencing matrix to be used for speedy
|
rob@100
|
17000 * curve rendering. It's based on using a specific number
|
rob@100
|
17001 * of curve segments and just doing incremental adds for each
|
rob@100
|
17002 * vertex of the segment, rather than running the mathematically
|
rob@100
|
17003 * expensive cubic equation. This function is used by both curveDetail and bezierDetail.
|
rob@100
|
17004 *
|
rob@100
|
17005 * @param {int} segments number of curve segments to use when drawing
|
rob@100
|
17006 * @param {PMatrix3D} matrix target object for the new matrix
|
rob@100
|
17007 */
|
rob@100
|
17008 var splineForward = function(segments, matrix) {
|
rob@100
|
17009 var f = 1.0 / segments;
|
rob@100
|
17010 var ff = f * f;
|
rob@100
|
17011 var fff = ff * f;
|
rob@100
|
17012
|
rob@100
|
17013 matrix.set(0, 0, 0, 1, fff, ff, f, 0, 6 * fff, 2 * ff, 0, 0, 6 * fff, 0, 0, 0);
|
rob@100
|
17014 };
|
rob@100
|
17015
|
rob@100
|
17016 /**
|
rob@100
|
17017 * The curveInit() function set the number of segments to use when drawing a Catmull-Rom
|
rob@100
|
17018 * curve, and setting the s parameter, which defines how tightly
|
rob@100
|
17019 * the curve fits to each vertex. Catmull-Rom curves are actually
|
rob@100
|
17020 * a subset of this curve type where the s is set to zero.
|
rob@100
|
17021 * This in an internal function used by curveDetail() and curveTightness().
|
rob@100
|
17022 */
|
rob@100
|
17023 var curveInit = function() {
|
rob@100
|
17024 // allocate only if/when used to save startup time
|
rob@100
|
17025 if (!curveDrawMatrix) {
|
rob@100
|
17026 curveBasisMatrix = new PMatrix3D();
|
rob@100
|
17027 curveDrawMatrix = new PMatrix3D();
|
rob@100
|
17028 curveInited = true;
|
rob@100
|
17029 }
|
rob@100
|
17030
|
rob@100
|
17031 var s = curTightness;
|
rob@100
|
17032 curveBasisMatrix.set((s - 1) / 2, (s + 3) / 2, (-3 - s) / 2, (1 - s) / 2,
|
rob@100
|
17033 (1 - s), (-5 - s) / 2, (s + 2), (s - 1) / 2,
|
rob@100
|
17034 (s - 1) / 2, 0, (1 - s) / 2, 0, 0, 1, 0, 0);
|
rob@100
|
17035
|
rob@100
|
17036 splineForward(curveDet, curveDrawMatrix);
|
rob@100
|
17037
|
rob@100
|
17038 if (!bezierBasisInverse) {
|
rob@100
|
17039 //bezierBasisInverse = bezierBasisMatrix.get();
|
rob@100
|
17040 //bezierBasisInverse.invert();
|
rob@100
|
17041 curveToBezierMatrix = new PMatrix3D();
|
rob@100
|
17042 }
|
rob@100
|
17043
|
rob@100
|
17044 // TODO only needed for PGraphicsJava2D? if so, move it there
|
rob@100
|
17045 // actually, it's generally useful for other renderers, so keep it
|
rob@100
|
17046 // or hide the implementation elsewhere.
|
rob@100
|
17047 curveToBezierMatrix.set(curveBasisMatrix);
|
rob@100
|
17048 curveToBezierMatrix.preApply(bezierBasisInverse);
|
rob@100
|
17049
|
rob@100
|
17050 // multiply the basis and forward diff matrices together
|
rob@100
|
17051 // saves much time since this needn't be done for each curve
|
rob@100
|
17052 curveDrawMatrix.apply(curveBasisMatrix);
|
rob@100
|
17053 };
|
rob@100
|
17054
|
rob@100
|
17055 /**
|
rob@100
|
17056 * Specifies vertex coordinates for Bezier curves. Each call to <b>bezierVertex()</b> defines the position of two control
|
rob@100
|
17057 * points and one anchor point of a Bezier curve, adding a new segment to a line or shape. The first time
|
rob@100
|
17058 * <b>bezierVertex()</b> is used within a <b>beginShape()</b> call, it must be prefaced with a call to <b>vertex()</b>
|
rob@100
|
17059 * to set the first anchor point. This function must be used between <b>beginShape()</b> and <b>endShape()</b> and only
|
rob@100
|
17060 * when there is no MODE parameter specified to <b>beginShape()</b>. Using the 3D version of requires rendering with P3D
|
rob@100
|
17061 * or OPENGL (see the Environment reference for more information). <br /> <br /> <b>NOTE: </b> Fill does not work properly yet.
|
rob@100
|
17062 *
|
rob@100
|
17063 * @param {float | int} cx1 The x-coordinate of 1st control point
|
rob@100
|
17064 * @param {float | int} cy1 The y-coordinate of 1st control point
|
rob@100
|
17065 * @param {float | int} cz1 The z-coordinate of 1st control point
|
rob@100
|
17066 * @param {float | int} cx2 The x-coordinate of 2nd control point
|
rob@100
|
17067 * @param {float | int} cy2 The y-coordinate of 2nd control point
|
rob@100
|
17068 * @param {float | int} cz2 The z-coordinate of 2nd control point
|
rob@100
|
17069 * @param {float | int} x The x-coordinate of the anchor point
|
rob@100
|
17070 * @param {float | int} y The y-coordinate of the anchor point
|
rob@100
|
17071 * @param {float | int} z The z-coordinate of the anchor point
|
rob@100
|
17072 *
|
rob@100
|
17073 * @see curveVertex
|
rob@100
|
17074 * @see vertex
|
rob@100
|
17075 * @see bezier
|
rob@100
|
17076 */
|
rob@100
|
17077 Drawing2D.prototype.bezierVertex = function() {
|
rob@100
|
17078 isBezier = true;
|
rob@100
|
17079 var vert = [];
|
rob@100
|
17080 if (firstVert) {
|
rob@100
|
17081 throw ("vertex() must be used at least once before calling bezierVertex()");
|
rob@100
|
17082 }
|
rob@100
|
17083
|
rob@100
|
17084 for (var i = 0; i < arguments.length; i++) {
|
rob@100
|
17085 vert[i] = arguments[i];
|
rob@100
|
17086 }
|
rob@100
|
17087 vertArray.push(vert);
|
rob@100
|
17088 vertArray[vertArray.length -1].isVert = false;
|
rob@100
|
17089 };
|
rob@100
|
17090
|
rob@100
|
17091 Drawing3D.prototype.bezierVertex = function() {
|
rob@100
|
17092 isBezier = true;
|
rob@100
|
17093 var vert = [];
|
rob@100
|
17094 if (firstVert) {
|
rob@100
|
17095 throw ("vertex() must be used at least once before calling bezierVertex()");
|
rob@100
|
17096 }
|
rob@100
|
17097
|
rob@100
|
17098 if (arguments.length === 9) {
|
rob@100
|
17099 if (bezierDrawMatrix === undef) {
|
rob@100
|
17100 bezierDrawMatrix = new PMatrix3D();
|
rob@100
|
17101 }
|
rob@100
|
17102 // setup matrix for forward differencing to speed up drawing
|
rob@100
|
17103 var lastPoint = vertArray.length - 1;
|
rob@100
|
17104 splineForward( bezDetail, bezierDrawMatrix );
|
rob@100
|
17105 bezierDrawMatrix.apply( bezierBasisMatrix );
|
rob@100
|
17106 var draw = bezierDrawMatrix.array();
|
rob@100
|
17107 var x1 = vertArray[lastPoint][0],
|
rob@100
|
17108 y1 = vertArray[lastPoint][1],
|
rob@100
|
17109 z1 = vertArray[lastPoint][2];
|
rob@100
|
17110 var xplot1 = draw[4] * x1 + draw[5] * arguments[0] + draw[6] * arguments[3] + draw[7] * arguments[6];
|
rob@100
|
17111 var xplot2 = draw[8] * x1 + draw[9] * arguments[0] + draw[10]* arguments[3] + draw[11]* arguments[6];
|
rob@100
|
17112 var xplot3 = draw[12]* x1 + draw[13]* arguments[0] + draw[14]* arguments[3] + draw[15]* arguments[6];
|
rob@100
|
17113
|
rob@100
|
17114 var yplot1 = draw[4] * y1 + draw[5] * arguments[1] + draw[6] * arguments[4] + draw[7] * arguments[7];
|
rob@100
|
17115 var yplot2 = draw[8] * y1 + draw[9] * arguments[1] + draw[10]* arguments[4] + draw[11]* arguments[7];
|
rob@100
|
17116 var yplot3 = draw[12]* y1 + draw[13]* arguments[1] + draw[14]* arguments[4] + draw[15]* arguments[7];
|
rob@100
|
17117
|
rob@100
|
17118 var zplot1 = draw[4] * z1 + draw[5] * arguments[2] + draw[6] * arguments[5] + draw[7] * arguments[8];
|
rob@100
|
17119 var zplot2 = draw[8] * z1 + draw[9] * arguments[2] + draw[10]* arguments[5] + draw[11]* arguments[8];
|
rob@100
|
17120 var zplot3 = draw[12]* z1 + draw[13]* arguments[2] + draw[14]* arguments[5] + draw[15]* arguments[8];
|
rob@100
|
17121 for (var j = 0; j < bezDetail; j++) {
|
rob@100
|
17122 x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
|
rob@100
|
17123 y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
|
rob@100
|
17124 z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
|
rob@100
|
17125 p.vertex(x1, y1, z1);
|
rob@100
|
17126 }
|
rob@100
|
17127 p.vertex(arguments[6], arguments[7], arguments[8]);
|
rob@100
|
17128 }
|
rob@100
|
17129 };
|
rob@100
|
17130
|
rob@100
|
17131 /**
|
rob@100
|
17132 * Sets a texture to be applied to vertex points. The <b>texture()</b> function
|
rob@100
|
17133 * must be called between <b>beginShape()</b> and <b>endShape()</b> and before
|
rob@100
|
17134 * any calls to vertex().
|
rob@100
|
17135 *
|
rob@100
|
17136 * When textures are in use, the fill color is ignored. Instead, use tint() to
|
rob@100
|
17137 * specify the color of the texture as it is applied to the shape.
|
rob@100
|
17138 *
|
rob@100
|
17139 * @param {PImage} pimage the texture to apply
|
rob@100
|
17140 *
|
rob@100
|
17141 * @returns none
|
rob@100
|
17142 *
|
rob@100
|
17143 * @see textureMode
|
rob@100
|
17144 * @see beginShape
|
rob@100
|
17145 * @see endShape
|
rob@100
|
17146 * @see vertex
|
rob@100
|
17147 */
|
rob@100
|
17148 p.texture = function(pimage) {
|
rob@100
|
17149 var curContext = drawing.$ensureContext();
|
rob@100
|
17150
|
rob@100
|
17151 if (pimage.__texture) {
|
rob@100
|
17152 curContext.bindTexture(curContext.TEXTURE_2D, pimage.__texture);
|
rob@100
|
17153 } else if (pimage.localName === "canvas") {
|
rob@100
|
17154 curContext.bindTexture(curContext.TEXTURE_2D, canTex);
|
rob@100
|
17155 curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, pimage);
|
rob@100
|
17156 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
|
rob@100
|
17157 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR);
|
rob@100
|
17158 curContext.generateMipmap(curContext.TEXTURE_2D);
|
rob@100
|
17159 curTexture.width = pimage.width;
|
rob@100
|
17160 curTexture.height = pimage.height;
|
rob@100
|
17161 } else {
|
rob@100
|
17162 var texture = curContext.createTexture(),
|
rob@100
|
17163 cvs = document.createElement('canvas'),
|
rob@100
|
17164 cvsTextureCtx = cvs.getContext('2d'),
|
rob@100
|
17165 pot;
|
rob@100
|
17166
|
rob@100
|
17167 // WebGL requires power of two textures
|
rob@100
|
17168 if (pimage.width & (pimage.width-1) === 0) {
|
rob@100
|
17169 cvs.width = pimage.width;
|
rob@100
|
17170 } else {
|
rob@100
|
17171 pot = 1;
|
rob@100
|
17172 while (pot < pimage.width) {
|
rob@100
|
17173 pot *= 2;
|
rob@100
|
17174 }
|
rob@100
|
17175 cvs.width = pot;
|
rob@100
|
17176 }
|
rob@100
|
17177
|
rob@100
|
17178 if (pimage.height & (pimage.height-1) === 0) {
|
rob@100
|
17179 cvs.height = pimage.height;
|
rob@100
|
17180 } else {
|
rob@100
|
17181 pot = 1;
|
rob@100
|
17182 while (pot < pimage.height) {
|
rob@100
|
17183 pot *= 2;
|
rob@100
|
17184 }
|
rob@100
|
17185 cvs.height = pot;
|
rob@100
|
17186 }
|
rob@100
|
17187
|
rob@100
|
17188 cvsTextureCtx.drawImage(pimage.sourceImg, 0, 0, pimage.width, pimage.height, 0, 0, cvs.width, cvs.height);
|
rob@100
|
17189
|
rob@100
|
17190 curContext.bindTexture(curContext.TEXTURE_2D, texture);
|
rob@100
|
17191 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR_MIPMAP_LINEAR);
|
rob@100
|
17192 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
|
rob@100
|
17193 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE);
|
rob@100
|
17194 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE);
|
rob@100
|
17195 curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, cvs);
|
rob@100
|
17196 curContext.generateMipmap(curContext.TEXTURE_2D);
|
rob@100
|
17197
|
rob@100
|
17198 pimage.__texture = texture;
|
rob@100
|
17199 curTexture.width = pimage.width;
|
rob@100
|
17200 curTexture.height = pimage.height;
|
rob@100
|
17201 }
|
rob@100
|
17202
|
rob@100
|
17203 usingTexture = true;
|
rob@100
|
17204 curContext.useProgram(programObject3D);
|
rob@100
|
17205 uniformi("usingTexture3d", programObject3D, "uUsingTexture", usingTexture);
|
rob@100
|
17206 };
|
rob@100
|
17207
|
rob@100
|
17208 /**
|
rob@100
|
17209 * Sets the coordinate space for texture mapping. There are two options, IMAGE,
|
rob@100
|
17210 * which refers to the actual coordinates of the image, and NORMALIZED, which
|
rob@100
|
17211 * refers to a normalized space of values ranging from 0 to 1. The default mode
|
rob@100
|
17212 * is IMAGE. In IMAGE, if an image is 100 x 200 pixels, mapping the image onto
|
rob@100
|
17213 * the entire size of a quad would require the points (0,0) (0,100) (100,200) (0,200).
|
rob@100
|
17214 * The same mapping in NORMAL_SPACE is (0,0) (0,1) (1,1) (0,1).
|
rob@100
|
17215 *
|
rob@100
|
17216 * @param MODE either IMAGE or NORMALIZED
|
rob@100
|
17217 *
|
rob@100
|
17218 * @returns none
|
rob@100
|
17219 *
|
rob@100
|
17220 * @see texture
|
rob@100
|
17221 */
|
rob@100
|
17222 p.textureMode = function(mode){
|
rob@100
|
17223 curTextureMode = mode;
|
rob@100
|
17224 };
|
rob@100
|
17225 /**
|
rob@100
|
17226 * The curveVertexSegment() function handle emitting a specific segment of Catmull-Rom curve. Internal helper function used by <b>curveVertex()</b>.
|
rob@100
|
17227 */
|
rob@100
|
17228 var curveVertexSegment = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) {
|
rob@100
|
17229 var x0 = x2;
|
rob@100
|
17230 var y0 = y2;
|
rob@100
|
17231 var z0 = z2;
|
rob@100
|
17232
|
rob@100
|
17233 var draw = curveDrawMatrix.array();
|
rob@100
|
17234
|
rob@100
|
17235 var xplot1 = draw[4] * x1 + draw[5] * x2 + draw[6] * x3 + draw[7] * x4;
|
rob@100
|
17236 var xplot2 = draw[8] * x1 + draw[9] * x2 + draw[10] * x3 + draw[11] * x4;
|
rob@100
|
17237 var xplot3 = draw[12] * x1 + draw[13] * x2 + draw[14] * x3 + draw[15] * x4;
|
rob@100
|
17238
|
rob@100
|
17239 var yplot1 = draw[4] * y1 + draw[5] * y2 + draw[6] * y3 + draw[7] * y4;
|
rob@100
|
17240 var yplot2 = draw[8] * y1 + draw[9] * y2 + draw[10] * y3 + draw[11] * y4;
|
rob@100
|
17241 var yplot3 = draw[12] * y1 + draw[13] * y2 + draw[14] * y3 + draw[15] * y4;
|
rob@100
|
17242
|
rob@100
|
17243 var zplot1 = draw[4] * z1 + draw[5] * z2 + draw[6] * z3 + draw[7] * z4;
|
rob@100
|
17244 var zplot2 = draw[8] * z1 + draw[9] * z2 + draw[10] * z3 + draw[11] * z4;
|
rob@100
|
17245 var zplot3 = draw[12] * z1 + draw[13] * z2 + draw[14] * z3 + draw[15] * z4;
|
rob@100
|
17246
|
rob@100
|
17247 p.vertex(x0, y0, z0);
|
rob@100
|
17248 for (var j = 0; j < curveDet; j++) {
|
rob@100
|
17249 x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
|
rob@100
|
17250 y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
|
rob@100
|
17251 z0 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
|
rob@100
|
17252 p.vertex(x0, y0, z0);
|
rob@100
|
17253 }
|
rob@100
|
17254 };
|
rob@100
|
17255
|
rob@100
|
17256 /**
|
rob@100
|
17257 * Specifies vertex coordinates for curves. This function may only be used between <b>beginShape()</b> and
|
rob@100
|
17258 * <b>endShape()</b> and only when there is no MODE parameter specified to <b>beginShape()</b>. The first and last points
|
rob@100
|
17259 * in a series of <b>curveVertex()</b> lines will be used to guide the beginning and end of a the curve. A minimum of four
|
rob@100
|
17260 * points is required to draw a tiny curve between the second and third points. Adding a fifth point with
|
rob@100
|
17261 * <b>curveVertex()</b> will draw the curve between the second, third, and fourth points. The <b>curveVertex()</b> function
|
rob@100
|
17262 * is an implementation of Catmull-Rom splines. Using the 3D version of requires rendering with P3D or OPENGL (see the
|
rob@100
|
17263 * Environment reference for more information). <br /> <br /><b>NOTE: </b> Fill does not work properly yet.
|
rob@100
|
17264 *
|
rob@100
|
17265 * @param {float | int} x The x-coordinate of the vertex
|
rob@100
|
17266 * @param {float | int} y The y-coordinate of the vertex
|
rob@100
|
17267 * @param {float | int} z The z-coordinate of the vertex
|
rob@100
|
17268 *
|
rob@100
|
17269 * @see curve
|
rob@100
|
17270 * @see beginShape
|
rob@100
|
17271 * @see endShape
|
rob@100
|
17272 * @see vertex
|
rob@100
|
17273 * @see bezierVertex
|
rob@100
|
17274 */
|
rob@100
|
17275 Drawing2D.prototype.curveVertex = function(x, y) {
|
rob@100
|
17276 isCurve = true;
|
rob@100
|
17277
|
rob@100
|
17278 p.vertex(x, y);
|
rob@100
|
17279 };
|
rob@100
|
17280
|
rob@100
|
17281 Drawing3D.prototype.curveVertex = function(x, y, z) {
|
rob@100
|
17282 isCurve = true;
|
rob@100
|
17283
|
rob@100
|
17284 if (!curveInited) {
|
rob@100
|
17285 curveInit();
|
rob@100
|
17286 }
|
rob@100
|
17287 var vert = [];
|
rob@100
|
17288 vert[0] = x;
|
rob@100
|
17289 vert[1] = y;
|
rob@100
|
17290 vert[2] = z;
|
rob@100
|
17291 curveVertArray.push(vert);
|
rob@100
|
17292 curveVertCount++;
|
rob@100
|
17293
|
rob@100
|
17294 if (curveVertCount > 3) {
|
rob@100
|
17295 curveVertexSegment( curveVertArray[curveVertCount-4][0],
|
rob@100
|
17296 curveVertArray[curveVertCount-4][1],
|
rob@100
|
17297 curveVertArray[curveVertCount-4][2],
|
rob@100
|
17298 curveVertArray[curveVertCount-3][0],
|
rob@100
|
17299 curveVertArray[curveVertCount-3][1],
|
rob@100
|
17300 curveVertArray[curveVertCount-3][2],
|
rob@100
|
17301 curveVertArray[curveVertCount-2][0],
|
rob@100
|
17302 curveVertArray[curveVertCount-2][1],
|
rob@100
|
17303 curveVertArray[curveVertCount-2][2],
|
rob@100
|
17304 curveVertArray[curveVertCount-1][0],
|
rob@100
|
17305 curveVertArray[curveVertCount-1][1],
|
rob@100
|
17306 curveVertArray[curveVertCount-1][2] );
|
rob@100
|
17307 }
|
rob@100
|
17308 };
|
rob@100
|
17309
|
rob@100
|
17310 /**
|
rob@100
|
17311 * The curve() function draws a curved line on the screen. The first and second parameters
|
rob@100
|
17312 * specify the beginning control point and the last two parameters specify
|
rob@100
|
17313 * the ending control point. The middle parameters specify the start and
|
rob@100
|
17314 * stop of the curve. Longer curves can be created by putting a series of
|
rob@100
|
17315 * <b>curve()</b> functions together or using <b>curveVertex()</b>.
|
rob@100
|
17316 * An additional function called <b>curveTightness()</b> provides control
|
rob@100
|
17317 * for the visual quality of the curve. The <b>curve()</b> function is an
|
rob@100
|
17318 * implementation of Catmull-Rom splines. Using the 3D version of requires
|
rob@100
|
17319 * rendering with P3D or OPENGL (see the Environment reference for more
|
rob@100
|
17320 * information).
|
rob@100
|
17321 *
|
rob@100
|
17322 * @param {int|float} x1 coordinates for the beginning control point
|
rob@100
|
17323 * @param {int|float} y1 coordinates for the beginning control point
|
rob@100
|
17324 * @param {int|float} z1 coordinates for the beginning control point
|
rob@100
|
17325 * @param {int|float} x2 coordinates for the first point
|
rob@100
|
17326 * @param {int|float} y2 coordinates for the first point
|
rob@100
|
17327 * @param {int|float} z2 coordinates for the first point
|
rob@100
|
17328 * @param {int|float} x3 coordinates for the second point
|
rob@100
|
17329 * @param {int|float} y3 coordinates for the second point
|
rob@100
|
17330 * @param {int|float} z3 coordinates for the second point
|
rob@100
|
17331 * @param {int|float} x4 coordinates for the ending control point
|
rob@100
|
17332 * @param {int|float} y4 coordinates for the ending control point
|
rob@100
|
17333 * @param {int|float} z4 coordinates for the ending control point
|
rob@100
|
17334 *
|
rob@100
|
17335 * @see #curveVertex()
|
rob@100
|
17336 * @see #curveTightness()
|
rob@100
|
17337 * @see #bezier()
|
rob@100
|
17338 */
|
rob@100
|
17339 Drawing2D.prototype.curve = function(x1, y1, x2, y2, x3, y3, x4, y4) {
|
rob@100
|
17340 p.beginShape();
|
rob@100
|
17341 p.curveVertex(x1, y1);
|
rob@100
|
17342 p.curveVertex(x2, y2);
|
rob@100
|
17343 p.curveVertex(x3, y3);
|
rob@100
|
17344 p.curveVertex(x4, y4);
|
rob@100
|
17345 p.endShape();
|
rob@100
|
17346 };
|
rob@100
|
17347
|
rob@100
|
17348 Drawing3D.prototype.curve = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) {
|
rob@100
|
17349 if (z4 !== undef) {
|
rob@100
|
17350 p.beginShape();
|
rob@100
|
17351 p.curveVertex(x1, y1, z1);
|
rob@100
|
17352 p.curveVertex(x2, y2, z2);
|
rob@100
|
17353 p.curveVertex(x3, y3, z3);
|
rob@100
|
17354 p.curveVertex(x4, y4, z4);
|
rob@100
|
17355 p.endShape();
|
rob@100
|
17356 return;
|
rob@100
|
17357 }
|
rob@100
|
17358 p.beginShape();
|
rob@100
|
17359 p.curveVertex(x1, y1);
|
rob@100
|
17360 p.curveVertex(z1, x2);
|
rob@100
|
17361 p.curveVertex(y2, z2);
|
rob@100
|
17362 p.curveVertex(x3, y3);
|
rob@100
|
17363 p.endShape();
|
rob@100
|
17364 };
|
rob@100
|
17365
|
rob@100
|
17366 /**
|
rob@100
|
17367 * The curveTightness() function modifies the quality of forms created with <b>curve()</b> and
|
rob@100
|
17368 * <b>curveVertex()</b>. The parameter <b>squishy</b> determines how the
|
rob@100
|
17369 * curve fits to the vertex points. The value 0.0 is the default value for
|
rob@100
|
17370 * <b>squishy</b> (this value defines the curves to be Catmull-Rom splines)
|
rob@100
|
17371 * and the value 1.0 connects all the points with straight lines.
|
rob@100
|
17372 * Values within the range -5.0 and 5.0 will deform the curves but
|
rob@100
|
17373 * will leave them recognizable and as values increase in magnitude,
|
rob@100
|
17374 * they will continue to deform.
|
rob@100
|
17375 *
|
rob@100
|
17376 * @param {float} tightness amount of deformation from the original vertices
|
rob@100
|
17377 *
|
rob@100
|
17378 * @see #curve()
|
rob@100
|
17379 * @see #curveVertex()
|
rob@100
|
17380 *
|
rob@100
|
17381 */
|
rob@100
|
17382 p.curveTightness = function(tightness) {
|
rob@100
|
17383 curTightness = tightness;
|
rob@100
|
17384 };
|
rob@100
|
17385
|
rob@100
|
17386 /**
|
rob@100
|
17387 * The curveDetail() function sets the resolution at which curves display. The default value is 20.
|
rob@100
|
17388 * This function is only useful when using the P3D or OPENGL renderer.
|
rob@100
|
17389 *
|
rob@100
|
17390 * @param {int} detail resolution of the curves
|
rob@100
|
17391 *
|
rob@100
|
17392 * @see curve()
|
rob@100
|
17393 * @see curveVertex()
|
rob@100
|
17394 * @see curveTightness()
|
rob@100
|
17395 */
|
rob@100
|
17396 p.curveDetail = function(detail) {
|
rob@100
|
17397 curveDet = detail;
|
rob@100
|
17398 curveInit();
|
rob@100
|
17399 };
|
rob@100
|
17400
|
rob@100
|
17401 /**
|
rob@100
|
17402 * Modifies the location from which rectangles draw. The default mode is rectMode(CORNER), which
|
rob@100
|
17403 * specifies the location to be the upper left corner of the shape and uses the third and fourth
|
rob@100
|
17404 * parameters of rect() to specify the width and height. The syntax rectMode(CORNERS) uses the
|
rob@100
|
17405 * first and second parameters of rect() to set the location of one corner and uses the third and
|
rob@100
|
17406 * fourth parameters to set the opposite corner. The syntax rectMode(CENTER) draws the image from
|
rob@100
|
17407 * its center point and uses the third and forth parameters of rect() to specify the image's width
|
rob@100
|
17408 * and height. The syntax rectMode(RADIUS) draws the image from its center point and uses the third
|
rob@100
|
17409 * and forth parameters of rect() to specify half of the image's width and height. The parameter must
|
rob@100
|
17410 * be written in ALL CAPS because Processing is a case sensitive language. Note: In version 125, the
|
rob@100
|
17411 * mode named CENTER_RADIUS was shortened to RADIUS.
|
rob@100
|
17412 *
|
rob@100
|
17413 * @param {MODE} MODE Either CORNER, CORNERS, CENTER, or RADIUS
|
rob@100
|
17414 *
|
rob@100
|
17415 * @see rect
|
rob@100
|
17416 */
|
rob@100
|
17417 p.rectMode = function(aRectMode) {
|
rob@100
|
17418 curRectMode = aRectMode;
|
rob@100
|
17419 };
|
rob@100
|
17420
|
rob@100
|
17421 /**
|
rob@100
|
17422 * Modifies the location from which images draw. The default mode is imageMode(CORNER), which specifies
|
rob@100
|
17423 * the location to be the upper left corner and uses the fourth and fifth parameters of image() to set
|
rob@100
|
17424 * the image's width and height. The syntax imageMode(CORNERS) uses the second and third parameters of
|
rob@100
|
17425 * image() to set the location of one corner of the image and uses the fourth and fifth parameters to
|
rob@100
|
17426 * set the opposite corner. Use imageMode(CENTER) to draw images centered at the given x and y position.
|
rob@100
|
17427 * The parameter to imageMode() must be written in ALL CAPS because Processing is a case sensitive language.
|
rob@100
|
17428 *
|
rob@100
|
17429 * @param {MODE} MODE Either CORNER, CORNERS, or CENTER
|
rob@100
|
17430 *
|
rob@100
|
17431 * @see loadImage
|
rob@100
|
17432 * @see PImage
|
rob@100
|
17433 * @see image
|
rob@100
|
17434 * @see background
|
rob@100
|
17435 */
|
rob@100
|
17436 p.imageMode = function(mode) {
|
rob@100
|
17437 switch (mode) {
|
rob@100
|
17438 case PConstants.CORNER:
|
rob@100
|
17439 imageModeConvert = imageModeCorner;
|
rob@100
|
17440 break;
|
rob@100
|
17441 case PConstants.CORNERS:
|
rob@100
|
17442 imageModeConvert = imageModeCorners;
|
rob@100
|
17443 break;
|
rob@100
|
17444 case PConstants.CENTER:
|
rob@100
|
17445 imageModeConvert = imageModeCenter;
|
rob@100
|
17446 break;
|
rob@100
|
17447 default:
|
rob@100
|
17448 throw "Invalid imageMode";
|
rob@100
|
17449 }
|
rob@100
|
17450 };
|
rob@100
|
17451
|
rob@100
|
17452 /**
|
rob@100
|
17453 * The origin of the ellipse is modified by the ellipseMode() function. The default configuration is
|
rob@100
|
17454 * ellipseMode(CENTER), which specifies the location of the ellipse as the center of the shape. The RADIUS
|
rob@100
|
17455 * mode is the same, but the width and height parameters to ellipse() specify the radius of the ellipse,
|
rob@100
|
17456 * rather than the diameter. The CORNER mode draws the shape from the upper-left corner of its bounding box.
|
rob@100
|
17457 * The CORNERS mode uses the four parameters to ellipse() to set two opposing corners of the ellipse's bounding
|
rob@100
|
17458 * box. The parameter must be written in "ALL CAPS" because Processing is a case sensitive language.
|
rob@100
|
17459 *
|
rob@100
|
17460 * @param {MODE} MODE Either CENTER, RADIUS, CORNER, or CORNERS.
|
rob@100
|
17461 *
|
rob@100
|
17462 * @see ellipse
|
rob@100
|
17463 */
|
rob@100
|
17464 p.ellipseMode = function(aEllipseMode) {
|
rob@100
|
17465 curEllipseMode = aEllipseMode;
|
rob@100
|
17466 };
|
rob@100
|
17467
|
rob@100
|
17468 /**
|
rob@100
|
17469 * The arc() function draws an arc in the display window.
|
rob@100
|
17470 * Arcs are drawn along the outer edge of an ellipse defined by the
|
rob@100
|
17471 * <b>x</b>, <b>y</b>, <b>width</b> and <b>height</b> parameters.
|
rob@100
|
17472 * The origin or the arc's ellipse may be changed with the
|
rob@100
|
17473 * <b>ellipseMode()</b> function.
|
rob@100
|
17474 * The <b>start</b> and <b>stop</b> parameters specify the angles
|
rob@100
|
17475 * at which to draw the arc.
|
rob@100
|
17476 *
|
rob@100
|
17477 * @param {float} a x-coordinate of the arc's ellipse
|
rob@100
|
17478 * @param {float} b y-coordinate of the arc's ellipse
|
rob@100
|
17479 * @param {float} c width of the arc's ellipse
|
rob@100
|
17480 * @param {float} d height of the arc's ellipse
|
rob@100
|
17481 * @param {float} start angle to start the arc, specified in radians
|
rob@100
|
17482 * @param {float} stop angle to stop the arc, specified in radians
|
rob@100
|
17483 *
|
rob@100
|
17484 * @see #ellipseMode()
|
rob@100
|
17485 * @see #ellipse()
|
rob@100
|
17486 */
|
rob@100
|
17487 p.arc = function(x, y, width, height, start, stop) {
|
rob@100
|
17488 if (width <= 0 || stop < start) { return; }
|
rob@100
|
17489
|
rob@100
|
17490 if (curEllipseMode === PConstants.CORNERS) {
|
rob@100
|
17491 width = width - x;
|
rob@100
|
17492 height = height - y;
|
rob@100
|
17493 } else if (curEllipseMode === PConstants.RADIUS) {
|
rob@100
|
17494 x = x - width;
|
rob@100
|
17495 y = y - height;
|
rob@100
|
17496 width = width * 2;
|
rob@100
|
17497 height = height * 2;
|
rob@100
|
17498 } else if (curEllipseMode === PConstants.CENTER) {
|
rob@100
|
17499 x = x - width/2;
|
rob@100
|
17500 y = y - height/2;
|
rob@100
|
17501 }
|
rob@100
|
17502 // make sure that we're starting at a useful point
|
rob@100
|
17503 while (start < 0) {
|
rob@100
|
17504 start += PConstants.TWO_PI;
|
rob@100
|
17505 stop += PConstants.TWO_PI;
|
rob@100
|
17506 }
|
rob@100
|
17507 if (stop - start > PConstants.TWO_PI) {
|
rob@100
|
17508 start = 0;
|
rob@100
|
17509 stop = PConstants.TWO_PI;
|
rob@100
|
17510 }
|
rob@100
|
17511 var hr = width / 2,
|
rob@100
|
17512 vr = height / 2,
|
rob@100
|
17513 centerX = x + hr,
|
rob@100
|
17514 centerY = y + vr,
|
rob@100
|
17515 startLUT = 0 | (0.5 + start * p.RAD_TO_DEG * 2),
|
rob@100
|
17516 stopLUT = 0 | (0.5 + stop * p.RAD_TO_DEG * 2),
|
rob@100
|
17517 i, j;
|
rob@100
|
17518 if (doFill) {
|
rob@100
|
17519 // shut off stroke for a minute
|
rob@100
|
17520 var savedStroke = doStroke;
|
rob@100
|
17521 doStroke = false;
|
rob@100
|
17522 p.beginShape();
|
rob@100
|
17523 p.vertex(centerX, centerY);
|
rob@100
|
17524 for (i = startLUT; i <= stopLUT; i++) {
|
rob@100
|
17525 j = i % PConstants.SINCOS_LENGTH;
|
rob@100
|
17526 p.vertex(centerX + cosLUT[j] * hr, centerY + sinLUT[j] * vr);
|
rob@100
|
17527 }
|
rob@100
|
17528 p.endShape(PConstants.CLOSE);
|
rob@100
|
17529 doStroke = savedStroke;
|
rob@100
|
17530 }
|
rob@100
|
17531
|
rob@100
|
17532 if (doStroke) {
|
rob@100
|
17533 // and doesn't include the first (center) vertex.
|
rob@100
|
17534 var savedFill = doFill;
|
rob@100
|
17535 doFill = false;
|
rob@100
|
17536 p.beginShape();
|
rob@100
|
17537 for (i = startLUT; i <= stopLUT; i++) {
|
rob@100
|
17538 j = i % PConstants.SINCOS_LENGTH;
|
rob@100
|
17539 p.vertex(centerX + cosLUT[j] * hr, centerY + sinLUT[j] * vr);
|
rob@100
|
17540 }
|
rob@100
|
17541 p.endShape();
|
rob@100
|
17542 doFill = savedFill;
|
rob@100
|
17543 }
|
rob@100
|
17544 };
|
rob@100
|
17545
|
rob@100
|
17546 /**
|
rob@100
|
17547 * Draws a line (a direct path between two points) to the screen. The version of line() with four parameters
|
rob@100
|
17548 * draws the line in 2D. To color a line, use the stroke() function. A line cannot be filled, therefore the
|
rob@100
|
17549 * fill() method will not affect the color of a line. 2D lines are drawn with a width of one pixel by default,
|
rob@100
|
17550 * but this can be changed with the strokeWeight() function. The version with six parameters allows the line
|
rob@100
|
17551 * to be placed anywhere within XYZ space. Drawing this shape in 3D using the z parameter requires the P3D or
|
rob@100
|
17552 * OPENGL parameter in combination with size.
|
rob@100
|
17553 *
|
rob@100
|
17554 * @param {int|float} x1 x-coordinate of the first point
|
rob@100
|
17555 * @param {int|float} y1 y-coordinate of the first point
|
rob@100
|
17556 * @param {int|float} z1 z-coordinate of the first point
|
rob@100
|
17557 * @param {int|float} x2 x-coordinate of the second point
|
rob@100
|
17558 * @param {int|float} y2 y-coordinate of the second point
|
rob@100
|
17559 * @param {int|float} z2 z-coordinate of the second point
|
rob@100
|
17560 *
|
rob@100
|
17561 * @see strokeWeight
|
rob@100
|
17562 * @see strokeJoin
|
rob@100
|
17563 * @see strokeCap
|
rob@100
|
17564 * @see beginShape
|
rob@100
|
17565 */
|
rob@100
|
17566 Drawing2D.prototype.line = function(x1, y1, x2, y2) {
|
rob@100
|
17567 if (!doStroke) {
|
rob@100
|
17568 return;
|
rob@100
|
17569 }
|
rob@100
|
17570 x1 = Math.round(x1);
|
rob@100
|
17571 x2 = Math.round(x2);
|
rob@100
|
17572 y1 = Math.round(y1);
|
rob@100
|
17573 y2 = Math.round(y2);
|
rob@100
|
17574 // A line is only defined if it has different start and end coordinates.
|
rob@100
|
17575 // If they are the same, we call point instead.
|
rob@100
|
17576 if (x1 === x2 && y1 === y2) {
|
rob@100
|
17577 p.point(x1, y1);
|
rob@100
|
17578 return;
|
rob@100
|
17579 }
|
rob@100
|
17580
|
rob@100
|
17581 var swap = undef,
|
rob@100
|
17582 lineCap = undef,
|
rob@100
|
17583 drawCrisp = true,
|
rob@100
|
17584 currentModelView = modelView.array(),
|
rob@100
|
17585 identityMatrix = [1, 0, 0, 0, 1, 0];
|
rob@100
|
17586 // Test if any transformations have been applied to the sketch
|
rob@100
|
17587 for (var i = 0; i < 6 && drawCrisp; i++) {
|
rob@100
|
17588 drawCrisp = currentModelView[i] === identityMatrix[i];
|
rob@100
|
17589 }
|
rob@100
|
17590 /* Draw crisp lines if the line is vertical or horizontal with the following method
|
rob@100
|
17591 * If any transformations have been applied to the sketch, don't make the line crisp
|
rob@100
|
17592 * If the line is directed up or to the left, reverse it by swapping x1/x2 or y1/y2
|
rob@100
|
17593 * Make the line 1 pixel longer to work around cross-platform canvas implementations
|
rob@100
|
17594 * If the lineWidth is odd, translate the line by 0.5 in the perpendicular direction
|
rob@100
|
17595 * Even lineWidths do not need to be translated because the canvas will draw them on pixel boundaries
|
rob@100
|
17596 * Change the cap to butt-end to work around cross-platform canvas implementations
|
rob@100
|
17597 * Reverse the translate and lineCap canvas state changes after drawing the line
|
rob@100
|
17598 */
|
rob@100
|
17599 if (drawCrisp) {
|
rob@100
|
17600 if (x1 === x2) {
|
rob@100
|
17601 if (y1 > y2) {
|
rob@100
|
17602 swap = y1;
|
rob@100
|
17603 y1 = y2;
|
rob@100
|
17604 y2 = swap;
|
rob@100
|
17605 }
|
rob@100
|
17606 y2++;
|
rob@100
|
17607 if (lineWidth % 2 === 1) {
|
rob@100
|
17608 curContext.translate(0.5, 0.0);
|
rob@100
|
17609 }
|
rob@100
|
17610 } else if (y1 === y2) {
|
rob@100
|
17611 if (x1 > x2) {
|
rob@100
|
17612 swap = x1;
|
rob@100
|
17613 x1 = x2;
|
rob@100
|
17614 x2 = swap;
|
rob@100
|
17615 }
|
rob@100
|
17616 x2++;
|
rob@100
|
17617 if (lineWidth % 2 === 1) {
|
rob@100
|
17618 curContext.translate(0.0, 0.5);
|
rob@100
|
17619 }
|
rob@100
|
17620 }
|
rob@100
|
17621 if (lineWidth === 1) {
|
rob@100
|
17622 lineCap = curContext.lineCap;
|
rob@100
|
17623 curContext.lineCap = 'butt';
|
rob@100
|
17624 }
|
rob@100
|
17625 }
|
rob@100
|
17626 curContext.beginPath();
|
rob@100
|
17627 curContext.moveTo(x1 || 0, y1 || 0);
|
rob@100
|
17628 curContext.lineTo(x2 || 0, y2 || 0);
|
rob@100
|
17629 executeContextStroke();
|
rob@100
|
17630 if (drawCrisp) {
|
rob@100
|
17631 if (x1 === x2 && lineWidth % 2 === 1) {
|
rob@100
|
17632 curContext.translate(-0.5, 0.0);
|
rob@100
|
17633 } else if (y1 === y2 && lineWidth % 2 === 1) {
|
rob@100
|
17634 curContext.translate(0.0, -0.5);
|
rob@100
|
17635 }
|
rob@100
|
17636 if (lineWidth === 1) {
|
rob@100
|
17637 curContext.lineCap = lineCap;
|
rob@100
|
17638 }
|
rob@100
|
17639 }
|
rob@100
|
17640 };
|
rob@100
|
17641
|
rob@100
|
17642 Drawing3D.prototype.line = function(x1, y1, z1, x2, y2, z2) {
|
rob@100
|
17643 if (y2 === undef || z2 === undef) { // 2D line called in 3D context
|
rob@100
|
17644 z2 = 0;
|
rob@100
|
17645 y2 = x2;
|
rob@100
|
17646 x2 = z1;
|
rob@100
|
17647 z1 = 0;
|
rob@100
|
17648 }
|
rob@100
|
17649
|
rob@100
|
17650 // a line is only defined if it has different start and end coordinates.
|
rob@100
|
17651 // If they are the same, we call point instead.
|
rob@100
|
17652 if (x1===x2 && y1===y2 && z1===z2) {
|
rob@100
|
17653 p.point(x1,y1,z1);
|
rob@100
|
17654 return;
|
rob@100
|
17655 }
|
rob@100
|
17656
|
rob@100
|
17657 var lineVerts = [x1, y1, z1, x2, y2, z2];
|
rob@100
|
17658
|
rob@100
|
17659 var view = new PMatrix3D();
|
rob@100
|
17660 view.scale(1, -1, 1);
|
rob@100
|
17661 view.apply(modelView.array());
|
rob@100
|
17662 view.transpose();
|
rob@100
|
17663
|
rob@100
|
17664 if (lineWidth > 0 && doStroke) {
|
rob@100
|
17665 curContext.useProgram(programObject2D);
|
rob@100
|
17666
|
rob@100
|
17667 uniformMatrix("uModel2d", programObject2D, "uModel", false, [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]);
|
rob@100
|
17668 uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
|
rob@100
|
17669
|
rob@100
|
17670 uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
|
rob@100
|
17671 uniformi("uIsDrawingText", programObject2D, "uIsDrawingText", false);
|
rob@100
|
17672
|
rob@100
|
17673 vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, lineBuffer);
|
rob@100
|
17674 disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
|
rob@100
|
17675
|
rob@100
|
17676 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(lineVerts), curContext.STREAM_DRAW);
|
rob@100
|
17677 curContext.drawArrays(curContext.LINES, 0, 2);
|
rob@100
|
17678 }
|
rob@100
|
17679 };
|
rob@100
|
17680
|
rob@100
|
17681 /**
|
rob@100
|
17682 * Draws a Bezier curve on the screen. These curves are defined by a series of anchor and control points. The first
|
rob@100
|
17683 * two parameters specify the first anchor point and the last two parameters specify the other anchor point. The
|
rob@100
|
17684 * middle parameters specify the control points which define the shape of the curve. Bezier curves were developed
|
rob@100
|
17685 * by French engineer Pierre Bezier. Using the 3D version of requires rendering with P3D or OPENGL (see the
|
rob@100
|
17686 * Environment reference for more information).
|
rob@100
|
17687 *
|
rob@100
|
17688 * @param {int | float} x1,y1,z1 coordinates for the first anchor point
|
rob@100
|
17689 * @param {int | float} cx1,cy1,cz1 coordinates for the first control point
|
rob@100
|
17690 * @param {int | float} cx2,cy2,cz2 coordinates for the second control point
|
rob@100
|
17691 * @param {int | float} x2,y2,z2 coordinates for the second anchor point
|
rob@100
|
17692 *
|
rob@100
|
17693 * @see bezierVertex
|
rob@100
|
17694 * @see curve
|
rob@100
|
17695 */
|
rob@100
|
17696 Drawing2D.prototype.bezier = function() {
|
rob@100
|
17697 if (arguments.length !== 8) {
|
rob@100
|
17698 throw("You must use 8 parameters for bezier() in 2D mode");
|
rob@100
|
17699 }
|
rob@100
|
17700
|
rob@100
|
17701 p.beginShape();
|
rob@100
|
17702 p.vertex( arguments[0], arguments[1] );
|
rob@100
|
17703 p.bezierVertex( arguments[2], arguments[3],
|
rob@100
|
17704 arguments[4], arguments[5],
|
rob@100
|
17705 arguments[6], arguments[7] );
|
rob@100
|
17706 p.endShape();
|
rob@100
|
17707 };
|
rob@100
|
17708
|
rob@100
|
17709 Drawing3D.prototype.bezier = function() {
|
rob@100
|
17710 if (arguments.length !== 12) {
|
rob@100
|
17711 throw("You must use 12 parameters for bezier() in 3D mode");
|
rob@100
|
17712 }
|
rob@100
|
17713
|
rob@100
|
17714 p.beginShape();
|
rob@100
|
17715 p.vertex( arguments[0], arguments[1], arguments[2] );
|
rob@100
|
17716 p.bezierVertex( arguments[3], arguments[4], arguments[5],
|
rob@100
|
17717 arguments[6], arguments[7], arguments[8],
|
rob@100
|
17718 arguments[9], arguments[10], arguments[11] );
|
rob@100
|
17719 p.endShape();
|
rob@100
|
17720 };
|
rob@100
|
17721
|
rob@100
|
17722 /**
|
rob@100
|
17723 * Sets the resolution at which Beziers display. The default value is 20. This function is only useful when using the P3D
|
rob@100
|
17724 * or OPENGL renderer as the default (JAVA2D) renderer does not use this information.
|
rob@100
|
17725 *
|
rob@100
|
17726 * @param {int} detail resolution of the curves
|
rob@100
|
17727 *
|
rob@100
|
17728 * @see curve
|
rob@100
|
17729 * @see curveVertex
|
rob@100
|
17730 * @see curveTightness
|
rob@100
|
17731 */
|
rob@100
|
17732 p.bezierDetail = function( detail ){
|
rob@100
|
17733 bezDetail = detail;
|
rob@100
|
17734 };
|
rob@100
|
17735
|
rob@100
|
17736 /**
|
rob@100
|
17737 * The bezierPoint() function evalutes quadratic bezier at point t for points a, b, c, d.
|
rob@100
|
17738 * The parameter t varies between 0 and 1. The a and d parameters are the
|
rob@100
|
17739 * on-curve points, b and c are the control points. To make a two-dimensional
|
rob@100
|
17740 * curve, call this function once with the x coordinates and a second time
|
rob@100
|
17741 * with the y coordinates to get the location of a bezier curve at t.
|
rob@100
|
17742 *
|
rob@100
|
17743 * @param {float} a coordinate of first point on the curve
|
rob@100
|
17744 * @param {float} b coordinate of first control point
|
rob@100
|
17745 * @param {float} c coordinate of second control point
|
rob@100
|
17746 * @param {float} d coordinate of second point on the curve
|
rob@100
|
17747 * @param {float} t value between 0 and 1
|
rob@100
|
17748 *
|
rob@100
|
17749 * @see #bezier()
|
rob@100
|
17750 * @see #bezierVertex()
|
rob@100
|
17751 * @see #curvePoint()
|
rob@100
|
17752 */
|
rob@100
|
17753 p.bezierPoint = function(a, b, c, d, t) {
|
rob@100
|
17754 return (1 - t) * (1 - t) * (1 - t) * a + 3 * (1 - t) * (1 - t) * t * b + 3 * (1 - t) * t * t * c + t * t * t * d;
|
rob@100
|
17755 };
|
rob@100
|
17756
|
rob@100
|
17757 /**
|
rob@100
|
17758 * The bezierTangent() function calculates the tangent of a point on a Bezier curve. There is a good
|
rob@100
|
17759 * definition of "tangent" at Wikipedia: <a href="http://en.wikipedia.org/wiki/Tangent" target="new">http://en.wikipedia.org/wiki/Tangent</a>
|
rob@100
|
17760 *
|
rob@100
|
17761 * @param {float} a coordinate of first point on the curve
|
rob@100
|
17762 * @param {float} b coordinate of first control point
|
rob@100
|
17763 * @param {float} c coordinate of second control point
|
rob@100
|
17764 * @param {float} d coordinate of second point on the curve
|
rob@100
|
17765 * @param {float} t value between 0 and 1
|
rob@100
|
17766 *
|
rob@100
|
17767 * @see #bezier()
|
rob@100
|
17768 * @see #bezierVertex()
|
rob@100
|
17769 * @see #curvePoint()
|
rob@100
|
17770 */
|
rob@100
|
17771 p.bezierTangent = function(a, b, c, d, t) {
|
rob@100
|
17772 return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b));
|
rob@100
|
17773 };
|
rob@100
|
17774
|
rob@100
|
17775 /**
|
rob@100
|
17776 * The curvePoint() function evalutes the Catmull-Rom curve at point t for points a, b, c, d. The
|
rob@100
|
17777 * parameter t varies between 0 and 1, a and d are points on the curve,
|
rob@100
|
17778 * and b and c are the control points. This can be done once with the x
|
rob@100
|
17779 * coordinates and a second time with the y coordinates to get the
|
rob@100
|
17780 * location of a curve at t.
|
rob@100
|
17781 *
|
rob@100
|
17782 * @param {int|float} a coordinate of first point on the curve
|
rob@100
|
17783 * @param {int|float} b coordinate of second point on the curve
|
rob@100
|
17784 * @param {int|float} c coordinate of third point on the curve
|
rob@100
|
17785 * @param {int|float} d coordinate of fourth point on the curve
|
rob@100
|
17786 * @param {float} t value between 0 and 1
|
rob@100
|
17787 *
|
rob@100
|
17788 * @see #curve()
|
rob@100
|
17789 * @see #curveVertex()
|
rob@100
|
17790 * @see #bezierPoint()
|
rob@100
|
17791 */
|
rob@100
|
17792 p.curvePoint = function(a, b, c, d, t) {
|
rob@100
|
17793 return 0.5 * ((2 * b) + (-a + c) * t + (2 * a - 5 * b + 4 * c - d) * t * t + (-a + 3 * b - 3 * c + d) * t * t * t);
|
rob@100
|
17794 };
|
rob@100
|
17795
|
rob@100
|
17796 /**
|
rob@100
|
17797 * The curveTangent() function calculates the tangent of a point on a Catmull-Rom curve.
|
rob@100
|
17798 * There is a good definition of "tangent" at Wikipedia: <a href="http://en.wikipedia.org/wiki/Tangent" target="new">http://en.wikipedia.org/wiki/Tangent</a>.
|
rob@100
|
17799 *
|
rob@100
|
17800 * @param {int|float} a coordinate of first point on the curve
|
rob@100
|
17801 * @param {int|float} b coordinate of first control point
|
rob@100
|
17802 * @param {int|float} c coordinate of second control point
|
rob@100
|
17803 * @param {int|float} d coordinate of second point on the curve
|
rob@100
|
17804 * @param {float} t value between 0 and 1
|
rob@100
|
17805 *
|
rob@100
|
17806 * @see #curve()
|
rob@100
|
17807 * @see #curveVertex()
|
rob@100
|
17808 * @see #curvePoint()
|
rob@100
|
17809 * @see #bezierTangent()
|
rob@100
|
17810 */
|
rob@100
|
17811 p.curveTangent = function(a, b, c, d, t) {
|
rob@100
|
17812 return 0.5 * ((-a + c) + 2 * (2 * a - 5 * b + 4 * c - d) * t + 3 * (-a + 3 * b - 3 * c + d) * t * t);
|
rob@100
|
17813 };
|
rob@100
|
17814
|
rob@100
|
17815 /**
|
rob@100
|
17816 * A triangle is a plane created by connecting three points. The first two arguments specify the first point,
|
rob@100
|
17817 * the middle two arguments specify the second point, and the last two arguments specify the third point.
|
rob@100
|
17818 *
|
rob@100
|
17819 * @param {int | float} x1 x-coordinate of the first point
|
rob@100
|
17820 * @param {int | float} y1 y-coordinate of the first point
|
rob@100
|
17821 * @param {int | float} x2 x-coordinate of the second point
|
rob@100
|
17822 * @param {int | float} y2 y-coordinate of the second point
|
rob@100
|
17823 * @param {int | float} x3 x-coordinate of the third point
|
rob@100
|
17824 * @param {int | float} y3 y-coordinate of the third point
|
rob@100
|
17825 */
|
rob@100
|
17826 p.triangle = function(x1, y1, x2, y2, x3, y3) {
|
rob@100
|
17827 p.beginShape(PConstants.TRIANGLES);
|
rob@100
|
17828 p.vertex(x1, y1, 0);
|
rob@100
|
17829 p.vertex(x2, y2, 0);
|
rob@100
|
17830 p.vertex(x3, y3, 0);
|
rob@100
|
17831 p.endShape();
|
rob@100
|
17832 };
|
rob@100
|
17833
|
rob@100
|
17834 /**
|
rob@100
|
17835 * A quad is a quadrilateral, a four sided polygon. It is similar to a rectangle, but the angles between its
|
rob@100
|
17836 * edges are not constrained to ninety degrees. The first pair of parameters (x1,y1) sets the first vertex
|
rob@100
|
17837 * and the subsequent pairs should proceed clockwise or counter-clockwise around the defined shape.
|
rob@100
|
17838 *
|
rob@100
|
17839 * @param {float | int} x1 x-coordinate of the first corner
|
rob@100
|
17840 * @param {float | int} y1 y-coordinate of the first corner
|
rob@100
|
17841 * @param {float | int} x2 x-coordinate of the second corner
|
rob@100
|
17842 * @param {float | int} y2 y-coordinate of the second corner
|
rob@100
|
17843 * @param {float | int} x3 x-coordinate of the third corner
|
rob@100
|
17844 * @param {float | int} y3 y-coordinate of the third corner
|
rob@100
|
17845 * @param {float | int} x4 x-coordinate of the fourth corner
|
rob@100
|
17846 * @param {float | int} y4 y-coordinate of the fourth corner
|
rob@100
|
17847 */
|
rob@100
|
17848 p.quad = function(x1, y1, x2, y2, x3, y3, x4, y4) {
|
rob@100
|
17849 p.beginShape(PConstants.QUADS);
|
rob@100
|
17850 p.vertex(x1, y1, 0);
|
rob@100
|
17851 p.vertex(x2, y2, 0);
|
rob@100
|
17852 p.vertex(x3, y3, 0);
|
rob@100
|
17853 p.vertex(x4, y4, 0);
|
rob@100
|
17854 p.endShape();
|
rob@100
|
17855 };
|
rob@100
|
17856
|
rob@100
|
17857 var roundedRect$2d = function(x, y, width, height, tl, tr, br, bl) {
|
rob@100
|
17858 if (bl === undef) {
|
rob@100
|
17859 tr = tl;
|
rob@100
|
17860 br = tl;
|
rob@100
|
17861 bl = tl;
|
rob@100
|
17862 }
|
rob@100
|
17863 var halfWidth = width / 2,
|
rob@100
|
17864 halfHeight = height / 2;
|
rob@100
|
17865 if (tl > halfWidth || tl > halfHeight) {
|
rob@100
|
17866 tl = Math.min(halfWidth, halfHeight);
|
rob@100
|
17867 }
|
rob@100
|
17868 if (tr > halfWidth || tr > halfHeight) {
|
rob@100
|
17869 tr = Math.min(halfWidth, halfHeight);
|
rob@100
|
17870 }
|
rob@100
|
17871 if (br > halfWidth || br > halfHeight) {
|
rob@100
|
17872 br = Math.min(halfWidth, halfHeight);
|
rob@100
|
17873 }
|
rob@100
|
17874 if (bl > halfWidth || bl > halfHeight) {
|
rob@100
|
17875 bl = Math.min(halfWidth, halfHeight);
|
rob@100
|
17876 }
|
rob@100
|
17877 // Translate the stroke by (0.5, 0.5) to draw a crisp border
|
rob@100
|
17878 if (!doFill || doStroke) {
|
rob@100
|
17879 curContext.translate(0.5, 0.5);
|
rob@100
|
17880 }
|
rob@100
|
17881 curContext.beginPath();
|
rob@100
|
17882 curContext.moveTo(x + tl, y);
|
rob@100
|
17883 curContext.lineTo(x + width - tr, y);
|
rob@100
|
17884 curContext.quadraticCurveTo(x + width, y, x + width, y + tr);
|
rob@100
|
17885 curContext.lineTo(x + width, y + height - br);
|
rob@100
|
17886 curContext.quadraticCurveTo(x + width, y + height, x + width - br, y + height);
|
rob@100
|
17887 curContext.lineTo(x + bl, y + height);
|
rob@100
|
17888 curContext.quadraticCurveTo(x, y + height, x, y + height - bl);
|
rob@100
|
17889 curContext.lineTo(x, y + tl);
|
rob@100
|
17890 curContext.quadraticCurveTo(x, y, x + tl, y);
|
rob@100
|
17891 if (!doFill || doStroke) {
|
rob@100
|
17892 curContext.translate(-0.5, -0.5);
|
rob@100
|
17893 }
|
rob@100
|
17894 executeContextFill();
|
rob@100
|
17895 executeContextStroke();
|
rob@100
|
17896 };
|
rob@100
|
17897
|
rob@100
|
17898 /**
|
rob@100
|
17899 * Draws a rectangle to the screen. A rectangle is a four-sided shape with every angle at ninety
|
rob@100
|
17900 * degrees. The first two parameters set the location, the third sets the width, and the fourth
|
rob@100
|
17901 * sets the height. The origin is changed with the rectMode() function.
|
rob@100
|
17902 *
|
rob@100
|
17903 * @param {int|float} x x-coordinate of the rectangle
|
rob@100
|
17904 * @param {int|float} y y-coordinate of the rectangle
|
rob@100
|
17905 * @param {int|float} width width of the rectangle
|
rob@100
|
17906 * @param {int|float} height height of the rectangle
|
rob@100
|
17907 *
|
rob@100
|
17908 * @see rectMode
|
rob@100
|
17909 * @see quad
|
rob@100
|
17910 */
|
rob@100
|
17911 Drawing2D.prototype.rect = function(x, y, width, height, tl, tr, br, bl) {
|
rob@100
|
17912 if (!width && !height) {
|
rob@100
|
17913 return;
|
rob@100
|
17914 }
|
rob@100
|
17915
|
rob@100
|
17916 if (curRectMode === PConstants.CORNERS) {
|
rob@100
|
17917 width -= x;
|
rob@100
|
17918 height -= y;
|
rob@100
|
17919 } else if (curRectMode === PConstants.RADIUS) {
|
rob@100
|
17920 width *= 2;
|
rob@100
|
17921 height *= 2;
|
rob@100
|
17922 x -= width / 2;
|
rob@100
|
17923 y -= height / 2;
|
rob@100
|
17924 } else if (curRectMode === PConstants.CENTER) {
|
rob@100
|
17925 x -= width / 2;
|
rob@100
|
17926 y -= height / 2;
|
rob@100
|
17927 }
|
rob@100
|
17928
|
rob@100
|
17929 x = Math.round(x);
|
rob@100
|
17930 y = Math.round(y);
|
rob@100
|
17931 width = Math.round(width);
|
rob@100
|
17932 height = Math.round(height);
|
rob@100
|
17933 if (tl !== undef) {
|
rob@100
|
17934 roundedRect$2d(x, y, width, height, tl, tr, br, bl);
|
rob@100
|
17935 return;
|
rob@100
|
17936 }
|
rob@100
|
17937
|
rob@100
|
17938 // Translate the line by (0.5, 0.5) to draw a crisp rectangle border
|
rob@100
|
17939 if (doStroke && lineWidth % 2 === 1) {
|
rob@100
|
17940 curContext.translate(0.5, 0.5);
|
rob@100
|
17941 }
|
rob@100
|
17942 curContext.beginPath();
|
rob@100
|
17943 curContext.rect(x, y, width, height);
|
rob@100
|
17944 executeContextFill();
|
rob@100
|
17945 executeContextStroke();
|
rob@100
|
17946 if (doStroke && lineWidth % 2 === 1) {
|
rob@100
|
17947 curContext.translate(-0.5, -0.5);
|
rob@100
|
17948 }
|
rob@100
|
17949 };
|
rob@100
|
17950
|
rob@100
|
17951 Drawing3D.prototype.rect = function(x, y, width, height, tl, tr, br, bl) {
|
rob@100
|
17952 if (tl !== undef) {
|
rob@100
|
17953 throw "rect() with rounded corners is not supported in 3D mode";
|
rob@100
|
17954 }
|
rob@100
|
17955
|
rob@100
|
17956 if (curRectMode === PConstants.CORNERS) {
|
rob@100
|
17957 width -= x;
|
rob@100
|
17958 height -= y;
|
rob@100
|
17959 } else if (curRectMode === PConstants.RADIUS) {
|
rob@100
|
17960 width *= 2;
|
rob@100
|
17961 height *= 2;
|
rob@100
|
17962 x -= width / 2;
|
rob@100
|
17963 y -= height / 2;
|
rob@100
|
17964 } else if (curRectMode === PConstants.CENTER) {
|
rob@100
|
17965 x -= width / 2;
|
rob@100
|
17966 y -= height / 2;
|
rob@100
|
17967 }
|
rob@100
|
17968
|
rob@100
|
17969 // Modeling transformation
|
rob@100
|
17970 var model = new PMatrix3D();
|
rob@100
|
17971 model.translate(x, y, 0);
|
rob@100
|
17972 model.scale(width, height, 1);
|
rob@100
|
17973 model.transpose();
|
rob@100
|
17974
|
rob@100
|
17975 // viewing transformation needs to have Y flipped
|
rob@100
|
17976 // becuase that's what Processing does.
|
rob@100
|
17977 var view = new PMatrix3D();
|
rob@100
|
17978 view.scale(1, -1, 1);
|
rob@100
|
17979 view.apply(modelView.array());
|
rob@100
|
17980 view.transpose();
|
rob@100
|
17981
|
rob@100
|
17982 if (lineWidth > 0 && doStroke) {
|
rob@100
|
17983 curContext.useProgram(programObject2D);
|
rob@100
|
17984 uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
|
rob@100
|
17985 uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
|
rob@100
|
17986 uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
|
rob@100
|
17987 uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", false);
|
rob@100
|
17988 vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, rectBuffer);
|
rob@100
|
17989 disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
|
rob@100
|
17990 curContext.drawArrays(curContext.LINE_LOOP, 0, rectVerts.length / 3);
|
rob@100
|
17991 }
|
rob@100
|
17992
|
rob@100
|
17993 if (doFill) {
|
rob@100
|
17994 curContext.useProgram(programObject3D);
|
rob@100
|
17995 uniformMatrix("uModel3d", programObject3D, "uModel", false, model.array());
|
rob@100
|
17996 uniformMatrix("uView3d", programObject3D, "uView", false, view.array());
|
rob@100
|
17997
|
rob@100
|
17998 // fix stitching problems. (lines get occluded by triangles
|
rob@100
|
17999 // since they share the same depth values). This is not entirely
|
rob@100
|
18000 // working, but it's a start for drawing the outline. So
|
rob@100
|
18001 // developers can start playing around with styles.
|
rob@100
|
18002 curContext.enable(curContext.POLYGON_OFFSET_FILL);
|
rob@100
|
18003 curContext.polygonOffset(1, 1);
|
rob@100
|
18004
|
rob@100
|
18005 uniformf("color3d", programObject3D, "uColor", fillStyle);
|
rob@100
|
18006
|
rob@100
|
18007 if(lightCount > 0){
|
rob@100
|
18008 var v = new PMatrix3D();
|
rob@100
|
18009 v.set(view);
|
rob@100
|
18010
|
rob@100
|
18011 var m = new PMatrix3D();
|
rob@100
|
18012 m.set(model);
|
rob@100
|
18013
|
rob@100
|
18014 v.mult(m);
|
rob@100
|
18015
|
rob@100
|
18016 var normalMatrix = new PMatrix3D();
|
rob@100
|
18017 normalMatrix.set(v);
|
rob@100
|
18018 normalMatrix.invert();
|
rob@100
|
18019 normalMatrix.transpose();
|
rob@100
|
18020
|
rob@100
|
18021 uniformMatrix("uNormalTransform3d", programObject3D, "uNormalTransform", false, normalMatrix.array());
|
rob@100
|
18022 vertexAttribPointer("aNormal3d", programObject3D, "aNormal", 3, rectNormBuffer);
|
rob@100
|
18023 }
|
rob@100
|
18024 else{
|
rob@100
|
18025 disableVertexAttribPointer("normal3d", programObject3D, "aNormal");
|
rob@100
|
18026 }
|
rob@100
|
18027
|
rob@100
|
18028 vertexAttribPointer("vertex3d", programObject3D, "aVertex", 3, rectBuffer);
|
rob@100
|
18029
|
rob@100
|
18030 curContext.drawArrays(curContext.TRIANGLE_FAN, 0, rectVerts.length / 3);
|
rob@100
|
18031 curContext.disable(curContext.POLYGON_OFFSET_FILL);
|
rob@100
|
18032 }
|
rob@100
|
18033 };
|
rob@100
|
18034
|
rob@100
|
18035 /**
|
rob@100
|
18036 * Draws an ellipse (oval) in the display window. An ellipse with an equal <b>width</b> and <b>height</b> is a circle.
|
rob@100
|
18037 * The first two parameters set the location, the third sets the width, and the fourth sets the height. The origin may be
|
rob@100
|
18038 * changed with the <b>ellipseMode()</b> function.
|
rob@100
|
18039 *
|
rob@100
|
18040 * @param {float|int} x x-coordinate of the ellipse
|
rob@100
|
18041 * @param {float|int} y y-coordinate of the ellipse
|
rob@100
|
18042 * @param {float|int} width width of the ellipse
|
rob@100
|
18043 * @param {float|int} height height of the ellipse
|
rob@100
|
18044 *
|
rob@100
|
18045 * @see ellipseMode
|
rob@100
|
18046 */
|
rob@100
|
18047 Drawing2D.prototype.ellipse = function(x, y, width, height) {
|
rob@100
|
18048 x = x || 0;
|
rob@100
|
18049 y = y || 0;
|
rob@100
|
18050
|
rob@100
|
18051 if (width <= 0 && height <= 0) {
|
rob@100
|
18052 return;
|
rob@100
|
18053 }
|
rob@100
|
18054
|
rob@100
|
18055 if (curEllipseMode === PConstants.RADIUS) {
|
rob@100
|
18056 width *= 2;
|
rob@100
|
18057 height *= 2;
|
rob@100
|
18058 } else if (curEllipseMode === PConstants.CORNERS) {
|
rob@100
|
18059 width = width - x;
|
rob@100
|
18060 height = height - y;
|
rob@100
|
18061 x += width / 2;
|
rob@100
|
18062 y += height / 2;
|
rob@100
|
18063 } else if (curEllipseMode === PConstants.CORNER) {
|
rob@100
|
18064 x += width / 2;
|
rob@100
|
18065 y += height / 2;
|
rob@100
|
18066 }
|
rob@100
|
18067
|
rob@100
|
18068 // Shortcut for drawing a 2D circle
|
rob@100
|
18069 if (width === height) {
|
rob@100
|
18070 curContext.beginPath();
|
rob@100
|
18071 curContext.arc(x, y, width / 2, 0, PConstants.TWO_PI, false);
|
rob@100
|
18072 executeContextFill();
|
rob@100
|
18073 executeContextStroke();
|
rob@100
|
18074 } else {
|
rob@100
|
18075 var w = width / 2,
|
rob@100
|
18076 h = height / 2,
|
rob@100
|
18077 C = 0.5522847498307933,
|
rob@100
|
18078 c_x = C * w,
|
rob@100
|
18079 c_y = C * h;
|
rob@100
|
18080
|
rob@100
|
18081 p.beginShape();
|
rob@100
|
18082 p.vertex(x + w, y);
|
rob@100
|
18083 p.bezierVertex(x + w, y - c_y, x + c_x, y - h, x, y - h);
|
rob@100
|
18084 p.bezierVertex(x - c_x, y - h, x - w, y - c_y, x - w, y);
|
rob@100
|
18085 p.bezierVertex(x - w, y + c_y, x - c_x, y + h, x, y + h);
|
rob@100
|
18086 p.bezierVertex(x + c_x, y + h, x + w, y + c_y, x + w, y);
|
rob@100
|
18087 p.endShape();
|
rob@100
|
18088 }
|
rob@100
|
18089 };
|
rob@100
|
18090
|
rob@100
|
18091 Drawing3D.prototype.ellipse = function(x, y, width, height) {
|
rob@100
|
18092 x = x || 0;
|
rob@100
|
18093 y = y || 0;
|
rob@100
|
18094
|
rob@100
|
18095 if (width <= 0 && height <= 0) {
|
rob@100
|
18096 return;
|
rob@100
|
18097 }
|
rob@100
|
18098
|
rob@100
|
18099 if (curEllipseMode === PConstants.RADIUS) {
|
rob@100
|
18100 width *= 2;
|
rob@100
|
18101 height *= 2;
|
rob@100
|
18102 } else if (curEllipseMode === PConstants.CORNERS) {
|
rob@100
|
18103 width = width - x;
|
rob@100
|
18104 height = height - y;
|
rob@100
|
18105 x += width / 2;
|
rob@100
|
18106 y += height / 2;
|
rob@100
|
18107 } else if (curEllipseMode === PConstants.CORNER) {
|
rob@100
|
18108 x += width / 2;
|
rob@100
|
18109 y += height / 2;
|
rob@100
|
18110 }
|
rob@100
|
18111
|
rob@100
|
18112 var w = width / 2,
|
rob@100
|
18113 h = height / 2,
|
rob@100
|
18114 C = 0.5522847498307933,
|
rob@100
|
18115 c_x = C * w,
|
rob@100
|
18116 c_y = C * h;
|
rob@100
|
18117
|
rob@100
|
18118 p.beginShape();
|
rob@100
|
18119 p.vertex(x + w, y);
|
rob@100
|
18120 p.bezierVertex(x + w, y - c_y, 0, x + c_x, y - h, 0, x, y - h, 0);
|
rob@100
|
18121 p.bezierVertex(x - c_x, y - h, 0, x - w, y - c_y, 0, x - w, y, 0);
|
rob@100
|
18122 p.bezierVertex(x - w, y + c_y, 0, x - c_x, y + h, 0, x, y + h, 0);
|
rob@100
|
18123 p.bezierVertex(x + c_x, y + h, 0, x + w, y + c_y, 0, x + w, y, 0);
|
rob@100
|
18124 p.endShape();
|
rob@100
|
18125
|
rob@100
|
18126 if (doFill) {
|
rob@100
|
18127 //temporary workaround to not working fills for bezier -- will fix later
|
rob@100
|
18128 var xAv = 0, yAv = 0, i, j;
|
rob@100
|
18129 for (i = 0; i < vertArray.length; i++) {
|
rob@100
|
18130 xAv += vertArray[i][0];
|
rob@100
|
18131 yAv += vertArray[i][1];
|
rob@100
|
18132 }
|
rob@100
|
18133 xAv /= vertArray.length;
|
rob@100
|
18134 yAv /= vertArray.length;
|
rob@100
|
18135 var vert = [],
|
rob@100
|
18136 fillVertArray = [],
|
rob@100
|
18137 colorVertArray = [];
|
rob@100
|
18138 vert[0] = xAv;
|
rob@100
|
18139 vert[1] = yAv;
|
rob@100
|
18140 vert[2] = 0;
|
rob@100
|
18141 vert[3] = 0;
|
rob@100
|
18142 vert[4] = 0;
|
rob@100
|
18143 vert[5] = fillStyle[0];
|
rob@100
|
18144 vert[6] = fillStyle[1];
|
rob@100
|
18145 vert[7] = fillStyle[2];
|
rob@100
|
18146 vert[8] = fillStyle[3];
|
rob@100
|
18147 vert[9] = strokeStyle[0];
|
rob@100
|
18148 vert[10] = strokeStyle[1];
|
rob@100
|
18149 vert[11] = strokeStyle[2];
|
rob@100
|
18150 vert[12] = strokeStyle[3];
|
rob@100
|
18151 vert[13] = normalX;
|
rob@100
|
18152 vert[14] = normalY;
|
rob@100
|
18153 vert[15] = normalZ;
|
rob@100
|
18154 vertArray.unshift(vert);
|
rob@100
|
18155 for (i = 0; i < vertArray.length; i++) {
|
rob@100
|
18156 for (j = 0; j < 3; j++) {
|
rob@100
|
18157 fillVertArray.push(vertArray[i][j]);
|
rob@100
|
18158 }
|
rob@100
|
18159 for (j = 5; j < 9; j++) {
|
rob@100
|
18160 colorVertArray.push(vertArray[i][j]);
|
rob@100
|
18161 }
|
rob@100
|
18162 }
|
rob@100
|
18163 fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray);
|
rob@100
|
18164 }
|
rob@100
|
18165 };
|
rob@100
|
18166
|
rob@100
|
18167 /**
|
rob@100
|
18168 * Sets the current normal vector. This is for drawing three dimensional shapes and surfaces and
|
rob@100
|
18169 * specifies a vector perpendicular to the surface of the shape which determines how lighting affects
|
rob@100
|
18170 * it. Processing attempts to automatically assign normals to shapes, but since that's imperfect,
|
rob@100
|
18171 * this is a better option when you want more control. This function is identical to glNormal3f() in OpenGL.
|
rob@100
|
18172 *
|
rob@100
|
18173 * @param {float} nx x direction
|
rob@100
|
18174 * @param {float} ny y direction
|
rob@100
|
18175 * @param {float} nz z direction
|
rob@100
|
18176 *
|
rob@100
|
18177 * @see beginShape
|
rob@100
|
18178 * @see endShape
|
rob@100
|
18179 * @see lights
|
rob@100
|
18180 */
|
rob@100
|
18181 p.normal = function(nx, ny, nz) {
|
rob@100
|
18182 if (arguments.length !== 3 || !(typeof nx === "number" && typeof ny === "number" && typeof nz === "number")) {
|
rob@100
|
18183 throw "normal() requires three numeric arguments.";
|
rob@100
|
18184 }
|
rob@100
|
18185
|
rob@100
|
18186 normalX = nx;
|
rob@100
|
18187 normalY = ny;
|
rob@100
|
18188 normalZ = nz;
|
rob@100
|
18189
|
rob@100
|
18190 if (curShape !== 0) {
|
rob@100
|
18191 if (normalMode === PConstants.NORMAL_MODE_AUTO) {
|
rob@100
|
18192 normalMode = PConstants.NORMAL_MODE_SHAPE;
|
rob@100
|
18193 } else if (normalMode === PConstants.NORMAL_MODE_SHAPE) {
|
rob@100
|
18194 normalMode = PConstants.NORMAL_MODE_VERTEX;
|
rob@100
|
18195 }
|
rob@100
|
18196 }
|
rob@100
|
18197 };
|
rob@100
|
18198
|
rob@100
|
18199 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
18200 // Raster drawing functions
|
rob@100
|
18201 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
18202
|
rob@100
|
18203 /**
|
rob@100
|
18204 * Saves an image from the display window. Images are saved in TIFF, TARGA, JPEG, and PNG format
|
rob@100
|
18205 * depending on the extension within the filename parameter. For example, "image.tif" will have
|
rob@100
|
18206 * a TIFF image and "image.png" will save a PNG image. If no extension is included in the filename,
|
rob@100
|
18207 * the image will save in TIFF format and .tif will be added to the name. These files are saved to
|
rob@100
|
18208 * the sketch's folder, which may be opened by selecting "Show sketch folder" from the "Sketch" menu.
|
rob@100
|
18209 * It is not possible to use save() while running the program in a web browser. All images saved
|
rob@100
|
18210 * from the main drawing window will be opaque. To save images without a background, use createGraphics().
|
rob@100
|
18211 *
|
rob@100
|
18212 * @param {String} filename any sequence of letters and numbers
|
rob@100
|
18213 *
|
rob@100
|
18214 * @see saveFrame
|
rob@100
|
18215 * @see createGraphics
|
rob@100
|
18216 */
|
rob@100
|
18217 p.save = function(file, img) {
|
rob@100
|
18218 // file is unused at the moment
|
rob@100
|
18219 // may implement this differently in later release
|
rob@100
|
18220 if (img !== undef) {
|
rob@100
|
18221 return window.open(img.toDataURL(),"_blank");
|
rob@100
|
18222 }
|
rob@100
|
18223 return window.open(p.externals.canvas.toDataURL(),"_blank");
|
rob@100
|
18224 };
|
rob@100
|
18225
|
rob@100
|
18226 var saveNumber = 0;
|
rob@100
|
18227
|
rob@100
|
18228 p.saveFrame = function(file) {
|
rob@100
|
18229 if(file === undef) {
|
rob@100
|
18230 // use default name template if parameter is not specified
|
rob@100
|
18231 file = "screen-####.png";
|
rob@100
|
18232 }
|
rob@100
|
18233 // Increment changeable part: screen-0000.png, screen-0001.png, ...
|
rob@100
|
18234 var frameFilename = file.replace(/#+/, function(all) {
|
rob@100
|
18235 var s = "" + (saveNumber++);
|
rob@100
|
18236 while(s.length < all.length) {
|
rob@100
|
18237 s = "0" + s;
|
rob@100
|
18238 }
|
rob@100
|
18239 return s;
|
rob@100
|
18240 });
|
rob@100
|
18241 p.save(frameFilename);
|
rob@100
|
18242 };
|
rob@100
|
18243
|
rob@100
|
18244 var utilityContext2d = document.createElement("canvas").getContext("2d");
|
rob@100
|
18245
|
rob@100
|
18246 var canvasDataCache = [undef, undef, undef]; // we need three for now
|
rob@100
|
18247
|
rob@100
|
18248 function getCanvasData(obj, w, h) {
|
rob@100
|
18249 var canvasData = canvasDataCache.shift();
|
rob@100
|
18250
|
rob@100
|
18251 if (canvasData === undef) {
|
rob@100
|
18252 canvasData = {};
|
rob@100
|
18253 canvasData.canvas = document.createElement("canvas");
|
rob@100
|
18254 canvasData.context = canvasData.canvas.getContext('2d');
|
rob@100
|
18255 }
|
rob@100
|
18256
|
rob@100
|
18257 canvasDataCache.push(canvasData);
|
rob@100
|
18258
|
rob@100
|
18259 var canvas = canvasData.canvas, context = canvasData.context,
|
rob@100
|
18260 width = w || obj.width, height = h || obj.height;
|
rob@100
|
18261
|
rob@100
|
18262 canvas.width = width;
|
rob@100
|
18263 canvas.height = height;
|
rob@100
|
18264
|
rob@100
|
18265 if (!obj) {
|
rob@100
|
18266 context.clearRect(0, 0, width, height);
|
rob@100
|
18267 } else if ("data" in obj) { // ImageData
|
rob@100
|
18268 context.putImageData(obj, 0, 0);
|
rob@100
|
18269 } else {
|
rob@100
|
18270 context.clearRect(0, 0, width, height);
|
rob@100
|
18271 context.drawImage(obj, 0, 0, width, height);
|
rob@100
|
18272 }
|
rob@100
|
18273 return canvasData;
|
rob@100
|
18274 }
|
rob@100
|
18275
|
rob@100
|
18276 /**
|
rob@100
|
18277 * Handle the sketch code for pixels[] and pixels.length
|
rob@100
|
18278 * parser code converts pixels[] to getPixels()
|
rob@100
|
18279 * or setPixels(), .length becomes getLength()
|
rob@100
|
18280 */
|
rob@100
|
18281 function buildPixelsObject(pImage) {
|
rob@100
|
18282 return {
|
rob@100
|
18283
|
rob@100
|
18284 getLength: (function(aImg) {
|
rob@100
|
18285 return function() {
|
rob@100
|
18286 if (aImg.isRemote) {
|
rob@100
|
18287 throw "Image is loaded remotely. Cannot get length.";
|
rob@100
|
18288 } else {
|
rob@100
|
18289 return aImg.imageData.data.length ? aImg.imageData.data.length/4 : 0;
|
rob@100
|
18290 }
|
rob@100
|
18291 };
|
rob@100
|
18292 }(pImage)),
|
rob@100
|
18293
|
rob@100
|
18294 getPixel: (function(aImg) {
|
rob@100
|
18295 return function(i) {
|
rob@100
|
18296 var offset = i*4,
|
rob@100
|
18297 data = aImg.imageData.data;
|
rob@100
|
18298
|
rob@100
|
18299 if (aImg.isRemote) {
|
rob@100
|
18300 throw "Image is loaded remotely. Cannot get pixels.";
|
rob@100
|
18301 }
|
rob@100
|
18302
|
rob@100
|
18303 return (data[offset+3] << 24) & PConstants.ALPHA_MASK |
|
rob@100
|
18304 (data[offset] << 16) & PConstants.RED_MASK |
|
rob@100
|
18305 (data[offset+1] << 8) & PConstants.GREEN_MASK |
|
rob@100
|
18306 data[offset+2] & PConstants.BLUE_MASK;
|
rob@100
|
18307 };
|
rob@100
|
18308 }(pImage)),
|
rob@100
|
18309
|
rob@100
|
18310 setPixel: (function(aImg) {
|
rob@100
|
18311 return function(i, c) {
|
rob@100
|
18312 var offset = i*4,
|
rob@100
|
18313 data = aImg.imageData.data;
|
rob@100
|
18314
|
rob@100
|
18315 if (aImg.isRemote) {
|
rob@100
|
18316 throw "Image is loaded remotely. Cannot set pixel.";
|
rob@100
|
18317 }
|
rob@100
|
18318
|
rob@100
|
18319 data[offset+0] = (c & PConstants.RED_MASK) >>> 16;
|
rob@100
|
18320 data[offset+1] = (c & PConstants.GREEN_MASK) >>> 8;
|
rob@100
|
18321 data[offset+2] = (c & PConstants.BLUE_MASK);
|
rob@100
|
18322 data[offset+3] = (c & PConstants.ALPHA_MASK) >>> 24;
|
rob@100
|
18323 aImg.__isDirty = true;
|
rob@100
|
18324 };
|
rob@100
|
18325 }(pImage)),
|
rob@100
|
18326
|
rob@100
|
18327 toArray: (function(aImg) {
|
rob@100
|
18328 return function() {
|
rob@100
|
18329 var arr = [],
|
rob@100
|
18330 data = aImg.imageData.data,
|
rob@100
|
18331 length = aImg.width * aImg.height;
|
rob@100
|
18332
|
rob@100
|
18333 if (aImg.isRemote) {
|
rob@100
|
18334 throw "Image is loaded remotely. Cannot get pixels.";
|
rob@100
|
18335 }
|
rob@100
|
18336
|
rob@100
|
18337 for (var i = 0, offset = 0; i < length; i++, offset += 4) {
|
rob@100
|
18338 arr.push( (data[offset+3] << 24) & PConstants.ALPHA_MASK |
|
rob@100
|
18339 (data[offset] << 16) & PConstants.RED_MASK |
|
rob@100
|
18340 (data[offset+1] << 8) & PConstants.GREEN_MASK |
|
rob@100
|
18341 data[offset+2] & PConstants.BLUE_MASK );
|
rob@100
|
18342 }
|
rob@100
|
18343 return arr;
|
rob@100
|
18344 };
|
rob@100
|
18345 }(pImage)),
|
rob@100
|
18346
|
rob@100
|
18347 set: (function(aImg) {
|
rob@100
|
18348 return function(arr) {
|
rob@100
|
18349 var offset,
|
rob@100
|
18350 data,
|
rob@100
|
18351 c;
|
rob@100
|
18352 if (this.isRemote) {
|
rob@100
|
18353 throw "Image is loaded remotely. Cannot set pixels.";
|
rob@100
|
18354 }
|
rob@100
|
18355
|
rob@100
|
18356 data = aImg.imageData.data;
|
rob@100
|
18357 for (var i = 0, aL = arr.length; i < aL; i++) {
|
rob@100
|
18358 c = arr[i];
|
rob@100
|
18359 offset = i*4;
|
rob@100
|
18360
|
rob@100
|
18361 data[offset+0] = (c & PConstants.RED_MASK) >>> 16;
|
rob@100
|
18362 data[offset+1] = (c & PConstants.GREEN_MASK) >>> 8;
|
rob@100
|
18363 data[offset+2] = (c & PConstants.BLUE_MASK);
|
rob@100
|
18364 data[offset+3] = (c & PConstants.ALPHA_MASK) >>> 24;
|
rob@100
|
18365 }
|
rob@100
|
18366 aImg.__isDirty = true;
|
rob@100
|
18367 };
|
rob@100
|
18368 }(pImage))
|
rob@100
|
18369
|
rob@100
|
18370 };
|
rob@100
|
18371 }
|
rob@100
|
18372
|
rob@100
|
18373 /**
|
rob@100
|
18374 * Datatype for storing images. Processing can display .gif, .jpg, .tga, and .png images. Images may be
|
rob@100
|
18375 * displayed in 2D and 3D space. Before an image is used, it must be loaded with the loadImage() function.
|
rob@100
|
18376 * The PImage object contains fields for the width and height of the image, as well as an array called
|
rob@100
|
18377 * pixels[] which contains the values for every pixel in the image. A group of methods, described below,
|
rob@100
|
18378 * allow easy access to the image's pixels and alpha channel and simplify the process of compositing.
|
rob@100
|
18379 * Before using the pixels[] array, be sure to use the loadPixels() method on the image to make sure that the
|
rob@100
|
18380 * pixel data is properly loaded. To create a new image, use the createImage() function (do not use new PImage()).
|
rob@100
|
18381 *
|
rob@100
|
18382 * @param {int} width image width
|
rob@100
|
18383 * @param {int} height image height
|
rob@100
|
18384 * @param {MODE} format Either RGB, ARGB, ALPHA (grayscale alpha channel)
|
rob@100
|
18385 *
|
rob@100
|
18386 * @returns {PImage}
|
rob@100
|
18387 *
|
rob@100
|
18388 * @see loadImage
|
rob@100
|
18389 * @see imageMode
|
rob@100
|
18390 * @see createImage
|
rob@100
|
18391 */
|
rob@100
|
18392 var PImage = function(aWidth, aHeight, aFormat) {
|
rob@100
|
18393
|
rob@100
|
18394 // Keep track of whether or not the cached imageData has been touched.
|
rob@100
|
18395 this.__isDirty = false;
|
rob@100
|
18396
|
rob@100
|
18397 if (aWidth instanceof HTMLImageElement) {
|
rob@100
|
18398 // convert an <img> to a PImage
|
rob@100
|
18399 this.fromHTMLImageData(aWidth);
|
rob@100
|
18400 } else if (aHeight || aFormat) {
|
rob@100
|
18401 this.width = aWidth || 1;
|
rob@100
|
18402 this.height = aHeight || 1;
|
rob@100
|
18403
|
rob@100
|
18404 // Stuff a canvas into sourceImg so image() calls can use drawImage like an <img>
|
rob@100
|
18405 var canvas = this.sourceImg = document.createElement("canvas");
|
rob@100
|
18406 canvas.width = this.width;
|
rob@100
|
18407 canvas.height = this.height;
|
rob@100
|
18408
|
rob@100
|
18409 var imageData = this.imageData = canvas.getContext('2d').createImageData(this.width, this.height);
|
rob@100
|
18410 this.format = (aFormat === PConstants.ARGB || aFormat === PConstants.ALPHA) ? aFormat : PConstants.RGB;
|
rob@100
|
18411 if (this.format === PConstants.RGB) {
|
rob@100
|
18412 // Set the alpha channel of an RGB image to opaque.
|
rob@100
|
18413 for (var i = 3, data = this.imageData.data, len = data.length; i < len; i += 4) {
|
rob@100
|
18414 data[i] = 255;
|
rob@100
|
18415 }
|
rob@100
|
18416 }
|
rob@100
|
18417
|
rob@100
|
18418 this.__isDirty = true;
|
rob@100
|
18419 this.updatePixels();
|
rob@100
|
18420 } else {
|
rob@100
|
18421 this.width = 0;
|
rob@100
|
18422 this.height = 0;
|
rob@100
|
18423 this.imageData = utilityContext2d.createImageData(1, 1);
|
rob@100
|
18424 this.format = PConstants.ARGB;
|
rob@100
|
18425 }
|
rob@100
|
18426
|
rob@100
|
18427 this.pixels = buildPixelsObject(this);
|
rob@100
|
18428 };
|
rob@100
|
18429 PImage.prototype = {
|
rob@100
|
18430
|
rob@100
|
18431 /**
|
rob@100
|
18432 * Temporary hack to deal with cross-Processing-instance created PImage. See
|
rob@100
|
18433 * tickets #1623 and #1644.
|
rob@100
|
18434 */
|
rob@100
|
18435 __isPImage: true,
|
rob@100
|
18436
|
rob@100
|
18437 /**
|
rob@100
|
18438 * @member PImage
|
rob@100
|
18439 * Updates the image with the data in its pixels[] array. Use in conjunction with loadPixels(). If
|
rob@100
|
18440 * you're only reading pixels from the array, there's no need to call updatePixels().
|
rob@100
|
18441 * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule
|
rob@100
|
18442 * is that any time you want to manipulate the pixels[] array, you must first call loadPixels(), and
|
rob@100
|
18443 * after changes have been made, call updatePixels(). Even if the renderer may not seem to use this
|
rob@100
|
18444 * function in the current Processing release, this will always be subject to change.
|
rob@100
|
18445 * Currently, none of the renderers use the additional parameters to updatePixels().
|
rob@100
|
18446 */
|
rob@100
|
18447 updatePixels: function() {
|
rob@100
|
18448 var canvas = this.sourceImg;
|
rob@100
|
18449 if (canvas && canvas instanceof HTMLCanvasElement && this.__isDirty) {
|
rob@100
|
18450 canvas.getContext('2d').putImageData(this.imageData, 0, 0);
|
rob@100
|
18451 }
|
rob@100
|
18452 this.__isDirty = false;
|
rob@100
|
18453 },
|
rob@100
|
18454
|
rob@100
|
18455 fromHTMLImageData: function(htmlImg) {
|
rob@100
|
18456 // convert an <img> to a PImage
|
rob@100
|
18457 var canvasData = getCanvasData(htmlImg);
|
rob@100
|
18458 try {
|
rob@100
|
18459 var imageData = canvasData.context.getImageData(0, 0, htmlImg.width, htmlImg.height);
|
rob@100
|
18460 this.fromImageData(imageData);
|
rob@100
|
18461 } catch(e) {
|
rob@100
|
18462 if (htmlImg.width && htmlImg.height) {
|
rob@100
|
18463 this.isRemote = true;
|
rob@100
|
18464 this.width = htmlImg.width;
|
rob@100
|
18465 this.height = htmlImg.height;
|
rob@100
|
18466 }
|
rob@100
|
18467 }
|
rob@100
|
18468 this.sourceImg = htmlImg;
|
rob@100
|
18469 },
|
rob@100
|
18470
|
rob@100
|
18471 'get': function(x, y, w, h) {
|
rob@100
|
18472 if (!arguments.length) {
|
rob@100
|
18473 return p.get(this);
|
rob@100
|
18474 }
|
rob@100
|
18475 if (arguments.length === 2) {
|
rob@100
|
18476 return p.get(x, y, this);
|
rob@100
|
18477 }
|
rob@100
|
18478 if (arguments.length === 4) {
|
rob@100
|
18479 return p.get(x, y, w, h, this);
|
rob@100
|
18480 }
|
rob@100
|
18481 },
|
rob@100
|
18482
|
rob@100
|
18483 /**
|
rob@100
|
18484 * @member PImage
|
rob@100
|
18485 * Changes the color of any pixel or writes an image directly into the image. The x and y parameter
|
rob@100
|
18486 * specify the pixel or the upper-left corner of the image. The color parameter specifies the color value.
|
rob@100
|
18487 * Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data
|
rob@100
|
18488 * directly into pixels[]. The equivalent statement to "set(x, y, #000000)" using pixels[] is
|
rob@100
|
18489 * "pixels[y*width+x] = #000000". Processing requires calling loadPixels() to load the display window
|
rob@100
|
18490 * data into the pixels[] array before getting the values and calling updatePixels() to update the window.
|
rob@100
|
18491 *
|
rob@100
|
18492 * @param {int} x x-coordinate of the pixel or upper-left corner of the image
|
rob@100
|
18493 * @param {int} y y-coordinate of the pixel or upper-left corner of the image
|
rob@100
|
18494 * @param {color} color any value of the color datatype
|
rob@100
|
18495 *
|
rob@100
|
18496 * @see get
|
rob@100
|
18497 * @see pixels[]
|
rob@100
|
18498 * @see copy
|
rob@100
|
18499 */
|
rob@100
|
18500 'set': function(x, y, c) {
|
rob@100
|
18501 p.set(x, y, c, this);
|
rob@100
|
18502 this.__isDirty = true;
|
rob@100
|
18503 },
|
rob@100
|
18504
|
rob@100
|
18505 /**
|
rob@100
|
18506 * @member PImage
|
rob@100
|
18507 * Blends a region of pixels into the image specified by the img parameter. These copies utilize full
|
rob@100
|
18508 * alpha channel support and a choice of the following modes to blend the colors of source pixels (A)
|
rob@100
|
18509 * with the ones of pixels in the destination image (B):
|
rob@100
|
18510 * BLEND - linear interpolation of colours: C = A*factor + B
|
rob@100
|
18511 * ADD - additive blending with white clip: C = min(A*factor + B, 255)
|
rob@100
|
18512 * SUBTRACT - subtractive blending with black clip: C = max(B - A*factor, 0)
|
rob@100
|
18513 * DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
|
rob@100
|
18514 * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
|
rob@100
|
18515 * DIFFERENCE - subtract colors from underlying image.
|
rob@100
|
18516 * EXCLUSION - similar to DIFFERENCE, but less extreme.
|
rob@100
|
18517 * MULTIPLY - Multiply the colors, result will always be darker.
|
rob@100
|
18518 * SCREEN - Opposite multiply, uses inverse values of the colors.
|
rob@100
|
18519 * OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values, and screens light values.
|
rob@100
|
18520 * HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
|
rob@100
|
18521 * SOFT_LIGHT - Mix of DARKEST and LIGHTEST. Works like OVERLAY, but not as harsh.
|
rob@100
|
18522 * DODGE - Lightens light tones and increases contrast, ignores darks. Called "Color Dodge" in Illustrator and Photoshop.
|
rob@100
|
18523 * BURN - Darker areas are applied, increasing contrast, ignores lights. Called "Color Burn" in Illustrator and Photoshop.
|
rob@100
|
18524 * All modes use the alpha information (highest byte) of source image pixels as the blending factor.
|
rob@100
|
18525 * If the source and destination regions are different sizes, the image will be automatically resized to
|
rob@100
|
18526 * match the destination size. If the srcImg parameter is not used, the display window is used as the source image.
|
rob@100
|
18527 * This function ignores imageMode().
|
rob@100
|
18528 *
|
rob@100
|
18529 * @param {int} x X coordinate of the source's upper left corner
|
rob@100
|
18530 * @param {int} y Y coordinate of the source's upper left corner
|
rob@100
|
18531 * @param {int} width source image width
|
rob@100
|
18532 * @param {int} height source image height
|
rob@100
|
18533 * @param {int} dx X coordinate of the destinations's upper left corner
|
rob@100
|
18534 * @param {int} dy Y coordinate of the destinations's upper left corner
|
rob@100
|
18535 * @param {int} dwidth destination image width
|
rob@100
|
18536 * @param {int} dheight destination image height
|
rob@100
|
18537 * @param {PImage} srcImg an image variable referring to the source image
|
rob@100
|
18538 * @param {MODE} MODE Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION,
|
rob@100
|
18539 * MULTIPLY, SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
|
rob@100
|
18540 *
|
rob@100
|
18541 * @see alpha
|
rob@100
|
18542 * @see copy
|
rob@100
|
18543 */
|
rob@100
|
18544 blend: function(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE) {
|
rob@100
|
18545 if (arguments.length === 9) {
|
rob@100
|
18546 p.blend(this, srcImg, x, y, width, height, dx, dy, dwidth, dheight, this);
|
rob@100
|
18547 } else if (arguments.length === 10) {
|
rob@100
|
18548 p.blend(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE, this);
|
rob@100
|
18549 }
|
rob@100
|
18550 delete this.sourceImg;
|
rob@100
|
18551 },
|
rob@100
|
18552
|
rob@100
|
18553 /**
|
rob@100
|
18554 * @member PImage
|
rob@100
|
18555 * Copies a region of pixels from one image into another. If the source and destination regions
|
rob@100
|
18556 * aren't the same size, it will automatically resize source pixels to fit the specified target region.
|
rob@100
|
18557 * No alpha information is used in the process, however if the source image has an alpha channel set,
|
rob@100
|
18558 * it will be copied as well. This function ignores imageMode().
|
rob@100
|
18559 *
|
rob@100
|
18560 * @param {int} sx X coordinate of the source's upper left corner
|
rob@100
|
18561 * @param {int} sy Y coordinate of the source's upper left corner
|
rob@100
|
18562 * @param {int} swidth source image width
|
rob@100
|
18563 * @param {int} sheight source image height
|
rob@100
|
18564 * @param {int} dx X coordinate of the destinations's upper left corner
|
rob@100
|
18565 * @param {int} dy Y coordinate of the destinations's upper left corner
|
rob@100
|
18566 * @param {int} dwidth destination image width
|
rob@100
|
18567 * @param {int} dheight destination image height
|
rob@100
|
18568 * @param {PImage} srcImg an image variable referring to the source image
|
rob@100
|
18569 *
|
rob@100
|
18570 * @see alpha
|
rob@100
|
18571 * @see blend
|
rob@100
|
18572 */
|
rob@100
|
18573 copy: function(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight) {
|
rob@100
|
18574 if (arguments.length === 8) {
|
rob@100
|
18575 p.blend(this, srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, PConstants.REPLACE, this);
|
rob@100
|
18576 } else if (arguments.length === 9) {
|
rob@100
|
18577 p.blend(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight, PConstants.REPLACE, this);
|
rob@100
|
18578 }
|
rob@100
|
18579 delete this.sourceImg;
|
rob@100
|
18580 },
|
rob@100
|
18581
|
rob@100
|
18582 /**
|
rob@100
|
18583 * @member PImage
|
rob@100
|
18584 * Filters an image as defined by one of the following modes:
|
rob@100
|
18585 * THRESHOLD - converts the image to black and white pixels depending if they are above or below
|
rob@100
|
18586 * the threshold defined by the level parameter. The level must be between 0.0 (black) and 1.0(white).
|
rob@100
|
18587 * If no level is specified, 0.5 is used.
|
rob@100
|
18588 * GRAY - converts any colors in the image to grayscale equivalents
|
rob@100
|
18589 * INVERT - sets each pixel to its inverse value
|
rob@100
|
18590 * POSTERIZE - limits each channel of the image to the number of colors specified as the level parameter
|
rob@100
|
18591 * BLUR - executes a Guassian blur with the level parameter specifying the extent of the blurring.
|
rob@100
|
18592 * If no level parameter is used, the blur is equivalent to Guassian blur of radius 1.
|
rob@100
|
18593 * OPAQUE - sets the alpha channel to entirely opaque.
|
rob@100
|
18594 * ERODE - reduces the light areas with the amount defined by the level parameter.
|
rob@100
|
18595 * DILATE - increases the light areas with the amount defined by the level parameter
|
rob@100
|
18596 *
|
rob@100
|
18597 * @param {MODE} MODE Either THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, or DILATE
|
rob@100
|
18598 * @param {int|float} param in the range from 0 to 1
|
rob@100
|
18599 */
|
rob@100
|
18600 filter: function(mode, param) {
|
rob@100
|
18601 if (arguments.length === 2) {
|
rob@100
|
18602 p.filter(mode, param, this);
|
rob@100
|
18603 } else if (arguments.length === 1) {
|
rob@100
|
18604 // no param specified, send null to show its invalid
|
rob@100
|
18605 p.filter(mode, null, this);
|
rob@100
|
18606 }
|
rob@100
|
18607 delete this.sourceImg;
|
rob@100
|
18608 },
|
rob@100
|
18609
|
rob@100
|
18610 /**
|
rob@100
|
18611 * @member PImage
|
rob@100
|
18612 * Saves the image into a file. Images are saved in TIFF, TARGA, JPEG, and PNG format depending on
|
rob@100
|
18613 * the extension within the filename parameter. For example, "image.tif" will have a TIFF image and
|
rob@100
|
18614 * "image.png" will save a PNG image. If no extension is included in the filename, the image will save
|
rob@100
|
18615 * in TIFF format and .tif will be added to the name. These files are saved to the sketch's folder,
|
rob@100
|
18616 * which may be opened by selecting "Show sketch folder" from the "Sketch" menu. It is not possible to
|
rob@100
|
18617 * use save() while running the program in a web browser.
|
rob@100
|
18618 * To save an image created within the code, rather than through loading, it's necessary to make the
|
rob@100
|
18619 * image with the createImage() function so it is aware of the location of the program and can therefore
|
rob@100
|
18620 * save the file to the right place. See the createImage() reference for more information.
|
rob@100
|
18621 *
|
rob@100
|
18622 * @param {String} filename a sequence of letters and numbers
|
rob@100
|
18623 */
|
rob@100
|
18624 save: function(file){
|
rob@100
|
18625 p.save(file,this);
|
rob@100
|
18626 },
|
rob@100
|
18627
|
rob@100
|
18628 /**
|
rob@100
|
18629 * @member PImage
|
rob@100
|
18630 * Resize the image to a new width and height. To make the image scale proportionally, use 0 as the
|
rob@100
|
18631 * value for the wide or high parameter.
|
rob@100
|
18632 *
|
rob@100
|
18633 * @param {int} wide the resized image width
|
rob@100
|
18634 * @param {int} high the resized image height
|
rob@100
|
18635 *
|
rob@100
|
18636 * @see get
|
rob@100
|
18637 */
|
rob@100
|
18638 resize: function(w, h) {
|
rob@100
|
18639 if (this.isRemote) { // Remote images cannot access imageData
|
rob@100
|
18640 throw "Image is loaded remotely. Cannot resize.";
|
rob@100
|
18641 }
|
rob@100
|
18642 if (this.width !== 0 || this.height !== 0) {
|
rob@100
|
18643 // make aspect ratio if w or h is 0
|
rob@100
|
18644 if (w === 0 && h !== 0) {
|
rob@100
|
18645 w = Math.floor(this.width / this.height * h);
|
rob@100
|
18646 } else if (h === 0 && w !== 0) {
|
rob@100
|
18647 h = Math.floor(this.height / this.width * w);
|
rob@100
|
18648 }
|
rob@100
|
18649 // put 'this.imageData' into a new canvas
|
rob@100
|
18650 var canvas = getCanvasData(this.imageData).canvas;
|
rob@100
|
18651 // pull imageData object out of canvas into ImageData object
|
rob@100
|
18652 var imageData = getCanvasData(canvas, w, h).context.getImageData(0, 0, w, h);
|
rob@100
|
18653 // set this as new pimage
|
rob@100
|
18654 this.fromImageData(imageData);
|
rob@100
|
18655 }
|
rob@100
|
18656 },
|
rob@100
|
18657
|
rob@100
|
18658 /**
|
rob@100
|
18659 * @member PImage
|
rob@100
|
18660 * Masks part of an image from displaying by loading another image and using it as an alpha channel.
|
rob@100
|
18661 * This mask image should only contain grayscale data, but only the blue color channel is used. The
|
rob@100
|
18662 * mask image needs to be the same size as the image to which it is applied.
|
rob@100
|
18663 * In addition to using a mask image, an integer array containing the alpha channel data can be
|
rob@100
|
18664 * specified directly. This method is useful for creating dynamically generated alpha masks. This
|
rob@100
|
18665 * array must be of the same length as the target image's pixels array and should contain only grayscale
|
rob@100
|
18666 * data of values between 0-255.
|
rob@100
|
18667 *
|
rob@100
|
18668 * @param {PImage} maskImg any PImage object used as the alpha channel for "img", needs to be same
|
rob@100
|
18669 * size as "img"
|
rob@100
|
18670 * @param {int[]} maskArray any array of Integer numbers used as the alpha channel, needs to be same
|
rob@100
|
18671 * length as the image's pixel array
|
rob@100
|
18672 */
|
rob@100
|
18673 mask: function(mask) {
|
rob@100
|
18674 var obj = this.toImageData(),
|
rob@100
|
18675 i,
|
rob@100
|
18676 size;
|
rob@100
|
18677
|
rob@100
|
18678 if (mask instanceof PImage || mask.__isPImage) {
|
rob@100
|
18679 if (mask.width === this.width && mask.height === this.height) {
|
rob@100
|
18680 mask = mask.toImageData();
|
rob@100
|
18681
|
rob@100
|
18682 for (i = 2, size = this.width * this.height * 4; i < size; i += 4) {
|
rob@100
|
18683 // using it as an alpha channel
|
rob@100
|
18684 obj.data[i + 1] = mask.data[i];
|
rob@100
|
18685 // but only the blue color channel
|
rob@100
|
18686 }
|
rob@100
|
18687 } else {
|
rob@100
|
18688 throw "mask must have the same dimensions as PImage.";
|
rob@100
|
18689 }
|
rob@100
|
18690 } else if (mask instanceof Array) {
|
rob@100
|
18691 if (this.width * this.height === mask.length) {
|
rob@100
|
18692 for (i = 0, size = mask.length; i < size; ++i) {
|
rob@100
|
18693 obj.data[i * 4 + 3] = mask[i];
|
rob@100
|
18694 }
|
rob@100
|
18695 } else {
|
rob@100
|
18696 throw "mask array must be the same length as PImage pixels array.";
|
rob@100
|
18697 }
|
rob@100
|
18698 }
|
rob@100
|
18699
|
rob@100
|
18700 this.fromImageData(obj);
|
rob@100
|
18701 },
|
rob@100
|
18702
|
rob@100
|
18703 // These are intentionally left blank for PImages, we work live with pixels and draw as necessary
|
rob@100
|
18704 /**
|
rob@100
|
18705 * @member PImage
|
rob@100
|
18706 * Loads the pixel data for the image into its pixels[] array. This function must always be called
|
rob@100
|
18707 * before reading from or writing to pixels[].
|
rob@100
|
18708 * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the
|
rob@100
|
18709 * rule is that any time you want to manipulate the pixels[] array, you must first call loadPixels(),
|
rob@100
|
18710 * and after changes have been made, call updatePixels(). Even if the renderer may not seem to use
|
rob@100
|
18711 * this function in the current Processing release, this will always be subject to change.
|
rob@100
|
18712 */
|
rob@100
|
18713 loadPixels: noop,
|
rob@100
|
18714
|
rob@100
|
18715 toImageData: function() {
|
rob@100
|
18716 if (this.isRemote) {
|
rob@100
|
18717 return this.sourceImg;
|
rob@100
|
18718 }
|
rob@100
|
18719
|
rob@100
|
18720 if (!this.__isDirty) {
|
rob@100
|
18721 return this.imageData;
|
rob@100
|
18722 }
|
rob@100
|
18723
|
rob@100
|
18724 var canvasData = getCanvasData(this.sourceImg);
|
rob@100
|
18725 return canvasData.context.getImageData(0, 0, this.width, this.height);
|
rob@100
|
18726 },
|
rob@100
|
18727
|
rob@100
|
18728 toDataURL: function() {
|
rob@100
|
18729 if (this.isRemote) { // Remote images cannot access imageData
|
rob@100
|
18730 throw "Image is loaded remotely. Cannot create dataURI.";
|
rob@100
|
18731 }
|
rob@100
|
18732 var canvasData = getCanvasData(this.imageData);
|
rob@100
|
18733 return canvasData.canvas.toDataURL();
|
rob@100
|
18734 },
|
rob@100
|
18735
|
rob@100
|
18736 fromImageData: function(canvasImg) {
|
rob@100
|
18737 var w = canvasImg.width,
|
rob@100
|
18738 h = canvasImg.height,
|
rob@100
|
18739 canvas = document.createElement('canvas'),
|
rob@100
|
18740 ctx = canvas.getContext('2d');
|
rob@100
|
18741
|
rob@100
|
18742 this.width = canvas.width = w;
|
rob@100
|
18743 this.height = canvas.height = h;
|
rob@100
|
18744
|
rob@100
|
18745 ctx.putImageData(canvasImg, 0, 0);
|
rob@100
|
18746
|
rob@100
|
18747 // changed for 0.9
|
rob@100
|
18748 this.format = PConstants.ARGB;
|
rob@100
|
18749
|
rob@100
|
18750 this.imageData = canvasImg;
|
rob@100
|
18751 this.sourceImg = canvas;
|
rob@100
|
18752 }
|
rob@100
|
18753 };
|
rob@100
|
18754
|
rob@100
|
18755 p.PImage = PImage;
|
rob@100
|
18756
|
rob@100
|
18757 /**
|
rob@100
|
18758 * Creates a new PImage (the datatype for storing images). This provides a fresh buffer of pixels to play
|
rob@100
|
18759 * with. Set the size of the buffer with the width and height parameters. The format parameter defines how
|
rob@100
|
18760 * the pixels are stored. See the PImage reference for more information.
|
rob@100
|
18761 * Be sure to include all three parameters, specifying only the width and height (but no format) will
|
rob@100
|
18762 * produce a strange error.
|
rob@100
|
18763 * Advanced users please note that createImage() should be used instead of the syntax new PImage().
|
rob@100
|
18764 *
|
rob@100
|
18765 * @param {int} width image width
|
rob@100
|
18766 * @param {int} height image height
|
rob@100
|
18767 * @param {MODE} format Either RGB, ARGB, ALPHA (grayscale alpha channel)
|
rob@100
|
18768 *
|
rob@100
|
18769 * @returns {PImage}
|
rob@100
|
18770 *
|
rob@100
|
18771 * @see PImage
|
rob@100
|
18772 * @see PGraphics
|
rob@100
|
18773 */
|
rob@100
|
18774 p.createImage = function(w, h, mode) {
|
rob@100
|
18775 return new PImage(w,h,mode);
|
rob@100
|
18776 };
|
rob@100
|
18777
|
rob@100
|
18778 // Loads an image for display. Type is an extension. Callback is fired on load.
|
rob@100
|
18779 /**
|
rob@100
|
18780 * Loads an image into a variable of type PImage. Four types of images ( .gif, .jpg, .tga, .png) images may
|
rob@100
|
18781 * be loaded. To load correctly, images must be located in the data directory of the current sketch. In most
|
rob@100
|
18782 * cases, load all images in setup() to preload them at the start of the program. Loading images inside draw()
|
rob@100
|
18783 * will reduce the speed of a program.
|
rob@100
|
18784 * The filename parameter can also be a URL to a file found online. For security reasons, a Processing sketch
|
rob@100
|
18785 * found online can only download files from the same server from which it came. Getting around this restriction
|
rob@100
|
18786 * requires a signed applet.
|
rob@100
|
18787 * The extension parameter is used to determine the image type in cases where the image filename does not end
|
rob@100
|
18788 * with a proper extension. Specify the extension as the second parameter to loadImage(), as shown in the
|
rob@100
|
18789 * third example on this page.
|
rob@100
|
18790 * If an image is not loaded successfully, the null value is returned and an error message will be printed to
|
rob@100
|
18791 * the console. The error message does not halt the program, however the null value may cause a NullPointerException
|
rob@100
|
18792 * if your code does not check whether the value returned from loadImage() is null.
|
rob@100
|
18793 * Depending on the type of error, a PImage object may still be returned, but the width and height of the image
|
rob@100
|
18794 * will be set to -1. This happens if bad image data is returned or cannot be decoded properly. Sometimes this happens
|
rob@100
|
18795 * with image URLs that produce a 403 error or that redirect to a password prompt, because loadImage() will attempt
|
rob@100
|
18796 * to interpret the HTML as image data.
|
rob@100
|
18797 *
|
rob@100
|
18798 * @param {String} filename name of file to load, can be .gif, .jpg, .tga, or a handful of other image
|
rob@100
|
18799 * types depending on your platform.
|
rob@100
|
18800 * @param {String} extension the type of image to load, for example "png", "gif", "jpg"
|
rob@100
|
18801 *
|
rob@100
|
18802 * @returns {PImage}
|
rob@100
|
18803 *
|
rob@100
|
18804 * @see PImage
|
rob@100
|
18805 * @see image
|
rob@100
|
18806 * @see imageMode
|
rob@100
|
18807 * @see background
|
rob@100
|
18808 */
|
rob@100
|
18809 p.loadImage = function(file, type, callback) {
|
rob@100
|
18810 // if type is specified, we just ignore it
|
rob@100
|
18811
|
rob@100
|
18812 var pimg;
|
rob@100
|
18813 // if image is in the preloader cache return a new PImage
|
rob@100
|
18814 if (curSketch.imageCache.images[file]) {
|
rob@100
|
18815 pimg = new PImage(curSketch.imageCache.images[file]);
|
rob@100
|
18816 pimg.loaded = true;
|
rob@100
|
18817 return pimg;
|
rob@100
|
18818 }
|
rob@100
|
18819 // else async load it
|
rob@100
|
18820 pimg = new PImage();
|
rob@100
|
18821 var img = document.createElement('img');
|
rob@100
|
18822
|
rob@100
|
18823 pimg.sourceImg = img;
|
rob@100
|
18824
|
rob@100
|
18825 img.onload = (function(aImage, aPImage, aCallback) {
|
rob@100
|
18826 var image = aImage;
|
rob@100
|
18827 var pimg = aPImage;
|
rob@100
|
18828 var callback = aCallback;
|
rob@100
|
18829 return function() {
|
rob@100
|
18830 // change the <img> object into a PImage now that its loaded
|
rob@100
|
18831 pimg.fromHTMLImageData(image);
|
rob@100
|
18832 pimg.loaded = true;
|
rob@100
|
18833 if (callback) {
|
rob@100
|
18834 callback();
|
rob@100
|
18835 }
|
rob@100
|
18836 };
|
rob@100
|
18837 }(img, pimg, callback));
|
rob@100
|
18838
|
rob@100
|
18839 img.src = file; // needs to be called after the img.onload function is declared or it wont work in opera
|
rob@100
|
18840 return pimg;
|
rob@100
|
18841 };
|
rob@100
|
18842
|
rob@100
|
18843 // async loading of large images, same functionality as loadImage above
|
rob@100
|
18844 /**
|
rob@100
|
18845 * This function load images on a separate thread so that your sketch does not freeze while images load during
|
rob@100
|
18846 * setup(). While the image is loading, its width and height will be 0. If an error occurs while loading the image,
|
rob@100
|
18847 * its width and height will be set to -1. You'll know when the image has loaded properly because its width and
|
rob@100
|
18848 * height will be greater than 0. Asynchronous image loading (particularly when downloading from a server) can
|
rob@100
|
18849 * dramatically improve performance.
|
rob@100
|
18850 * The extension parameter is used to determine the image type in cases where the image filename does not end
|
rob@100
|
18851 * with a proper extension. Specify the extension as the second parameter to requestImage().
|
rob@100
|
18852 *
|
rob@100
|
18853 * @param {String} filename name of file to load, can be .gif, .jpg, .tga, or a handful of other image
|
rob@100
|
18854 * types depending on your platform.
|
rob@100
|
18855 * @param {String} extension the type of image to load, for example "png", "gif", "jpg"
|
rob@100
|
18856 *
|
rob@100
|
18857 * @returns {PImage}
|
rob@100
|
18858 *
|
rob@100
|
18859 * @see PImage
|
rob@100
|
18860 * @see loadImage
|
rob@100
|
18861 */
|
rob@100
|
18862 p.requestImage = p.loadImage;
|
rob@100
|
18863
|
rob@100
|
18864 function get$2(x,y) {
|
rob@100
|
18865 var data;
|
rob@100
|
18866 // return the color at x,y (int) of curContext
|
rob@100
|
18867 if (x >= p.width || x < 0 || y < 0 || y >= p.height) {
|
rob@100
|
18868 // x,y is outside image return transparent black
|
rob@100
|
18869 return 0;
|
rob@100
|
18870 }
|
rob@100
|
18871
|
rob@100
|
18872 // loadPixels() has been called
|
rob@100
|
18873 if (isContextReplaced) {
|
rob@100
|
18874 var offset = ((0|x) + p.width * (0|y)) * 4;
|
rob@100
|
18875 data = p.imageData.data;
|
rob@100
|
18876 return (data[offset + 3] << 24) & PConstants.ALPHA_MASK |
|
rob@100
|
18877 (data[offset] << 16) & PConstants.RED_MASK |
|
rob@100
|
18878 (data[offset + 1] << 8) & PConstants.GREEN_MASK |
|
rob@100
|
18879 data[offset + 2] & PConstants.BLUE_MASK;
|
rob@100
|
18880 }
|
rob@100
|
18881
|
rob@100
|
18882 // x,y is inside canvas space
|
rob@100
|
18883 data = p.toImageData(0|x, 0|y, 1, 1).data;
|
rob@100
|
18884 return (data[3] << 24) & PConstants.ALPHA_MASK |
|
rob@100
|
18885 (data[0] << 16) & PConstants.RED_MASK |
|
rob@100
|
18886 (data[1] << 8) & PConstants.GREEN_MASK |
|
rob@100
|
18887 data[2] & PConstants.BLUE_MASK;
|
rob@100
|
18888 }
|
rob@100
|
18889 function get$3(x,y,img) {
|
rob@100
|
18890 if (img.isRemote) { // Remote images cannot access imageData
|
rob@100
|
18891 throw "Image is loaded remotely. Cannot get x,y.";
|
rob@100
|
18892 }
|
rob@100
|
18893 // PImage.get(x,y) was called, return the color (int) at x,y of img
|
rob@100
|
18894 var offset = y * img.width * 4 + (x * 4),
|
rob@100
|
18895 data = img.imageData.data;
|
rob@100
|
18896 return (data[offset + 3] << 24) & PConstants.ALPHA_MASK |
|
rob@100
|
18897 (data[offset] << 16) & PConstants.RED_MASK |
|
rob@100
|
18898 (data[offset + 1] << 8) & PConstants.GREEN_MASK |
|
rob@100
|
18899 data[offset + 2] & PConstants.BLUE_MASK;
|
rob@100
|
18900 }
|
rob@100
|
18901 function get$4(x, y, w, h) {
|
rob@100
|
18902 // return a PImage of w and h from cood x,y of curContext
|
rob@100
|
18903 var c = new PImage(w, h, PConstants.ARGB);
|
rob@100
|
18904 c.fromImageData(p.toImageData(x, y, w, h));
|
rob@100
|
18905 return c;
|
rob@100
|
18906 }
|
rob@100
|
18907 function get$5(x, y, w, h, img) {
|
rob@100
|
18908 if (img.isRemote) { // Remote images cannot access imageData
|
rob@100
|
18909 throw "Image is loaded remotely. Cannot get x,y,w,h.";
|
rob@100
|
18910 }
|
rob@100
|
18911 // PImage.get(x,y,w,h) was called, return x,y,w,h PImage of img
|
rob@100
|
18912 // offset start point needs to be *4
|
rob@100
|
18913 var c = new PImage(w, h, PConstants.ARGB), cData = c.imageData.data,
|
rob@100
|
18914 imgWidth = img.width, imgHeight = img.height, imgData = img.imageData.data;
|
rob@100
|
18915 // Don't need to copy pixels from the image outside ranges.
|
rob@100
|
18916 var startRow = Math.max(0, -y), startColumn = Math.max(0, -x),
|
rob@100
|
18917 stopRow = Math.min(h, imgHeight - y), stopColumn = Math.min(w, imgWidth - x);
|
rob@100
|
18918 for (var i = startRow; i < stopRow; ++i) {
|
rob@100
|
18919 var sourceOffset = ((y + i) * imgWidth + (x + startColumn)) * 4;
|
rob@100
|
18920 var targetOffset = (i * w + startColumn) * 4;
|
rob@100
|
18921 for (var j = startColumn; j < stopColumn; ++j) {
|
rob@100
|
18922 cData[targetOffset++] = imgData[sourceOffset++];
|
rob@100
|
18923 cData[targetOffset++] = imgData[sourceOffset++];
|
rob@100
|
18924 cData[targetOffset++] = imgData[sourceOffset++];
|
rob@100
|
18925 cData[targetOffset++] = imgData[sourceOffset++];
|
rob@100
|
18926 }
|
rob@100
|
18927 }
|
rob@100
|
18928 c.__isDirty = true;
|
rob@100
|
18929 return c;
|
rob@100
|
18930 }
|
rob@100
|
18931
|
rob@100
|
18932 // Gets a single pixel or block of pixels from the current Canvas Context or a PImage
|
rob@100
|
18933 /**
|
rob@100
|
18934 * Reads the color of any pixel or grabs a section of an image. If no parameters are specified, the entire
|
rob@100
|
18935 * image is returned. Get the value of one pixel by specifying an x,y coordinate. Get a section of the display
|
rob@100
|
18936 * window by specifying an additional width and height parameter. If the pixel requested is outside of the image
|
rob@100
|
18937 * window, black is returned. The numbers returned are scaled according to the current color ranges, but only RGB
|
rob@100
|
18938 * values are returned by this function. For example, even though you may have drawn a shape with colorMode(HSB),
|
rob@100
|
18939 * the numbers returned will be in RGB.
|
rob@100
|
18940 * Getting the color of a single pixel with get(x, y) is easy, but not as fast as grabbing the data directly
|
rob@100
|
18941 * from pixels[]. The equivalent statement to "get(x, y)" using pixels[] is "pixels[y*width+x]". Processing
|
rob@100
|
18942 * requires calling loadPixels() to load the display window data into the pixels[] array before getting the values.
|
rob@100
|
18943 * This function ignores imageMode().
|
rob@100
|
18944 *
|
rob@100
|
18945 * @param {int} x x-coordinate of the pixel
|
rob@100
|
18946 * @param {int} y y-coordinate of the pixel
|
rob@100
|
18947 * @param {int} width width of pixel rectangle to get
|
rob@100
|
18948 * @param {int} height height of pixel rectangle to get
|
rob@100
|
18949 *
|
rob@100
|
18950 * @returns {Color|PImage}
|
rob@100
|
18951 *
|
rob@100
|
18952 * @see set
|
rob@100
|
18953 * @see pixels[]
|
rob@100
|
18954 * @see imageMode
|
rob@100
|
18955 */
|
rob@100
|
18956 p.get = function(x, y, w, h, img) {
|
rob@100
|
18957 // for 0 2 and 4 arguments use curContext, otherwise PImage.get was called
|
rob@100
|
18958 if (img !== undefined) {
|
rob@100
|
18959 return get$5(x, y, w, h, img);
|
rob@100
|
18960 }
|
rob@100
|
18961 if (h !== undefined) {
|
rob@100
|
18962 return get$4(x, y, w, h);
|
rob@100
|
18963 }
|
rob@100
|
18964 if (w !== undefined) {
|
rob@100
|
18965 return get$3(x, y, w);
|
rob@100
|
18966 }
|
rob@100
|
18967 if (y !== undefined) {
|
rob@100
|
18968 return get$2(x, y);
|
rob@100
|
18969 }
|
rob@100
|
18970 if (x !== undefined) {
|
rob@100
|
18971 // PImage.get() was called, return a new PImage
|
rob@100
|
18972 return get$5(0, 0, x.width, x.height, x);
|
rob@100
|
18973 }
|
rob@100
|
18974
|
rob@100
|
18975 return get$4(0, 0, p.width, p.height);
|
rob@100
|
18976 };
|
rob@100
|
18977
|
rob@100
|
18978 /**
|
rob@100
|
18979 * Creates and returns a new <b>PGraphics</b> object of the types P2D, P3D, and JAVA2D. Use this class if you need to draw
|
rob@100
|
18980 * into an off-screen graphics buffer. It's not possible to use <b>createGraphics()</b> with OPENGL, because it doesn't
|
rob@100
|
18981 * allow offscreen use. The DXF and PDF renderers require the filename parameter. <br /><br /> It's important to call
|
rob@100
|
18982 * any drawing commands between beginDraw() and endDraw() statements. This is also true for any commands that affect
|
rob@100
|
18983 * drawing, such as smooth() or colorMode().<br /><br /> Unlike the main drawing surface which is completely opaque,
|
rob@100
|
18984 * surfaces created with createGraphics() can have transparency. This makes it possible to draw into a graphics and
|
rob@100
|
18985 * maintain the alpha channel.
|
rob@100
|
18986 *
|
rob@100
|
18987 * @param {int} width width in pixels
|
rob@100
|
18988 * @param {int} height height in pixels
|
rob@100
|
18989 * @param {int} renderer Either P2D, P3D, JAVA2D, PDF, DXF
|
rob@100
|
18990 * @param {String} filename the name of the file (not supported yet)
|
rob@100
|
18991 */
|
rob@100
|
18992 p.createGraphics = function(w, h, render) {
|
rob@100
|
18993 var pg = new Processing();
|
rob@100
|
18994 pg.size(w, h, render);
|
rob@100
|
18995 pg.background(0,0);
|
rob@100
|
18996 return pg;
|
rob@100
|
18997 };
|
rob@100
|
18998
|
rob@100
|
18999 // pixels caching
|
rob@100
|
19000 function resetContext() {
|
rob@100
|
19001 if(isContextReplaced) {
|
rob@100
|
19002 curContext = originalContext;
|
rob@100
|
19003 isContextReplaced = false;
|
rob@100
|
19004
|
rob@100
|
19005 p.updatePixels();
|
rob@100
|
19006 }
|
rob@100
|
19007 }
|
rob@100
|
19008 function SetPixelContextWrapper() {
|
rob@100
|
19009 function wrapFunction(newContext, name) {
|
rob@100
|
19010 function wrapper() {
|
rob@100
|
19011 resetContext();
|
rob@100
|
19012 curContext[name].apply(curContext, arguments);
|
rob@100
|
19013 }
|
rob@100
|
19014 newContext[name] = wrapper;
|
rob@100
|
19015 }
|
rob@100
|
19016 function wrapProperty(newContext, name) {
|
rob@100
|
19017 function getter() {
|
rob@100
|
19018 resetContext();
|
rob@100
|
19019 return curContext[name];
|
rob@100
|
19020 }
|
rob@100
|
19021 function setter(value) {
|
rob@100
|
19022 resetContext();
|
rob@100
|
19023 curContext[name] = value;
|
rob@100
|
19024 }
|
rob@100
|
19025 p.defineProperty(newContext, name, { get: getter, set: setter });
|
rob@100
|
19026 }
|
rob@100
|
19027 for(var n in curContext) {
|
rob@100
|
19028 if(typeof curContext[n] === 'function') {
|
rob@100
|
19029 wrapFunction(this, n);
|
rob@100
|
19030 } else {
|
rob@100
|
19031 wrapProperty(this, n);
|
rob@100
|
19032 }
|
rob@100
|
19033 }
|
rob@100
|
19034 }
|
rob@100
|
19035 function replaceContext() {
|
rob@100
|
19036 if(isContextReplaced) {
|
rob@100
|
19037 return;
|
rob@100
|
19038 }
|
rob@100
|
19039 p.loadPixels();
|
rob@100
|
19040 if(proxyContext === null) {
|
rob@100
|
19041 originalContext = curContext;
|
rob@100
|
19042 proxyContext = new SetPixelContextWrapper();
|
rob@100
|
19043 }
|
rob@100
|
19044 isContextReplaced = true;
|
rob@100
|
19045 curContext = proxyContext;
|
rob@100
|
19046 setPixelsCached = 0;
|
rob@100
|
19047 }
|
rob@100
|
19048
|
rob@100
|
19049 function set$3(x, y, c) {
|
rob@100
|
19050 if (x < p.width && x >= 0 && y >= 0 && y < p.height) {
|
rob@100
|
19051 replaceContext();
|
rob@100
|
19052 p.pixels.setPixel((0|x)+p.width*(0|y), c);
|
rob@100
|
19053 if(++setPixelsCached > maxPixelsCached) {
|
rob@100
|
19054 resetContext();
|
rob@100
|
19055 }
|
rob@100
|
19056 }
|
rob@100
|
19057 }
|
rob@100
|
19058 function set$4(x, y, obj, img) {
|
rob@100
|
19059 if (img.isRemote) { // Remote images cannot access imageData
|
rob@100
|
19060 throw "Image is loaded remotely. Cannot set x,y.";
|
rob@100
|
19061 }
|
rob@100
|
19062 var c = p.color.toArray(obj);
|
rob@100
|
19063 var offset = y * img.width * 4 + (x*4);
|
rob@100
|
19064 var data = img.imageData.data;
|
rob@100
|
19065 data[offset] = c[0];
|
rob@100
|
19066 data[offset+1] = c[1];
|
rob@100
|
19067 data[offset+2] = c[2];
|
rob@100
|
19068 data[offset+3] = c[3];
|
rob@100
|
19069 }
|
rob@100
|
19070
|
rob@100
|
19071 // Paints a pixel array into the canvas
|
rob@100
|
19072 /**
|
rob@100
|
19073 * Changes the color of any pixel or writes an image directly into the display window. The x and y parameters
|
rob@100
|
19074 * specify the pixel to change and the color parameter specifies the color value. The color parameter is affected
|
rob@100
|
19075 * by the current color mode (the default is RGB values from 0 to 255). When setting an image, the x and y
|
rob@100
|
19076 * parameters define the coordinates for the upper-left corner of the image.
|
rob@100
|
19077 * Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data directly
|
rob@100
|
19078 * into pixels[]. The equivalent statement to "set(x, y, #000000)" using pixels[] is "pixels[y*width+x] = #000000".
|
rob@100
|
19079 * You must call loadPixels() to load the display window data into the pixels[] array before setting the values
|
rob@100
|
19080 * and calling updatePixels() to update the window with any changes. This function ignores imageMode().
|
rob@100
|
19081 *
|
rob@100
|
19082 * @param {int} x x-coordinate of the pixel
|
rob@100
|
19083 * @param {int} y y-coordinate of the pixel
|
rob@100
|
19084 * @param {Color} obj any value of the color datatype
|
rob@100
|
19085 * @param {PImage} img any valid variable of type PImage
|
rob@100
|
19086 *
|
rob@100
|
19087 * @see get
|
rob@100
|
19088 * @see pixels[]
|
rob@100
|
19089 * @see imageMode
|
rob@100
|
19090 */
|
rob@100
|
19091 p.set = function(x, y, obj, img) {
|
rob@100
|
19092 var color, oldFill;
|
rob@100
|
19093 if (arguments.length === 3) {
|
rob@100
|
19094 // called p.set(), was it with a color or a img ?
|
rob@100
|
19095 if (typeof obj === "number") {
|
rob@100
|
19096 set$3(x, y, obj);
|
rob@100
|
19097 } else if (obj instanceof PImage || obj.__isPImage) {
|
rob@100
|
19098 p.image(obj, x, y);
|
rob@100
|
19099 }
|
rob@100
|
19100 } else if (arguments.length === 4) {
|
rob@100
|
19101 // PImage.set(x,y,c) was called, set coordinate x,y color to c of img
|
rob@100
|
19102 set$4(x, y, obj, img);
|
rob@100
|
19103 }
|
rob@100
|
19104 };
|
rob@100
|
19105 p.imageData = {};
|
rob@100
|
19106
|
rob@100
|
19107 // handle the sketch code for pixels[]
|
rob@100
|
19108 // parser code converts pixels[] to getPixels() or setPixels(),
|
rob@100
|
19109 // .length becomes getLength()
|
rob@100
|
19110 /**
|
rob@100
|
19111 * Array containing the values for all the pixels in the display window. These values are of the color datatype.
|
rob@100
|
19112 * This array is the size of the display window. For example, if the image is 100x100 pixels, there will be 10000
|
rob@100
|
19113 * values and if the window is 200x300 pixels, there will be 60000 values. The index value defines the position
|
rob@100
|
19114 * of a value within the array. For example, the statment color b = pixels[230] will set the variable b to be
|
rob@100
|
19115 * equal to the value at that location in the array.
|
rob@100
|
19116 * Before accessing this array, the data must loaded with the loadPixels() function. After the array data has
|
rob@100
|
19117 * been modified, the updatePixels() function must be run to update the changes.
|
rob@100
|
19118 *
|
rob@100
|
19119 * @param {int} index must not exceed the size of the array
|
rob@100
|
19120 *
|
rob@100
|
19121 * @see loadPixels
|
rob@100
|
19122 * @see updatePixels
|
rob@100
|
19123 * @see get
|
rob@100
|
19124 * @see set
|
rob@100
|
19125 * @see PImage
|
rob@100
|
19126 */
|
rob@100
|
19127 p.pixels = {
|
rob@100
|
19128 getLength: function() { return p.imageData.data.length ? p.imageData.data.length/4 : 0; },
|
rob@100
|
19129 getPixel: function(i) {
|
rob@100
|
19130 var offset = i*4, data = p.imageData.data;
|
rob@100
|
19131 return (data[offset+3] << 24) & 0xff000000 |
|
rob@100
|
19132 (data[offset+0] << 16) & 0x00ff0000 |
|
rob@100
|
19133 (data[offset+1] << 8) & 0x0000ff00 |
|
rob@100
|
19134 data[offset+2] & 0x000000ff;
|
rob@100
|
19135 },
|
rob@100
|
19136 setPixel: function(i,c) {
|
rob@100
|
19137 var offset = i*4, data = p.imageData.data;
|
rob@100
|
19138 data[offset+0] = (c & 0x00ff0000) >>> 16; // RED_MASK
|
rob@100
|
19139 data[offset+1] = (c & 0x0000ff00) >>> 8; // GREEN_MASK
|
rob@100
|
19140 data[offset+2] = (c & 0x000000ff); // BLUE_MASK
|
rob@100
|
19141 data[offset+3] = (c & 0xff000000) >>> 24; // ALPHA_MASK
|
rob@100
|
19142 },
|
rob@100
|
19143 toArray: function() {
|
rob@100
|
19144 var arr = [], length = p.imageData.width * p.imageData.height, data = p.imageData.data;
|
rob@100
|
19145 for (var i = 0, offset = 0; i < length; i++, offset += 4) {
|
rob@100
|
19146 arr.push((data[offset+3] << 24) & 0xff000000 |
|
rob@100
|
19147 (data[offset+0] << 16) & 0x00ff0000 |
|
rob@100
|
19148 (data[offset+1] << 8) & 0x0000ff00 |
|
rob@100
|
19149 data[offset+2] & 0x000000ff);
|
rob@100
|
19150 }
|
rob@100
|
19151 return arr;
|
rob@100
|
19152 },
|
rob@100
|
19153 set: function(arr) {
|
rob@100
|
19154 for (var i = 0, aL = arr.length; i < aL; i++) {
|
rob@100
|
19155 this.setPixel(i, arr[i]);
|
rob@100
|
19156 }
|
rob@100
|
19157 }
|
rob@100
|
19158 };
|
rob@100
|
19159
|
rob@100
|
19160 // Gets a 1-Dimensional pixel array from Canvas
|
rob@100
|
19161 /**
|
rob@100
|
19162 * Loads the pixel data for the display window into the pixels[] array. This function must always be called
|
rob@100
|
19163 * before reading from or writing to pixels[].
|
rob@100
|
19164 * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule is that
|
rob@100
|
19165 * any time you want to manipulate the pixels[] array, you must first call loadPixels(), and after changes
|
rob@100
|
19166 * have been made, call updatePixels(). Even if the renderer may not seem to use this function in the current
|
rob@100
|
19167 * Processing release, this will always be subject to change.
|
rob@100
|
19168 *
|
rob@100
|
19169 * @see pixels[]
|
rob@100
|
19170 * @see updatePixels
|
rob@100
|
19171 */
|
rob@100
|
19172 p.loadPixels = function() {
|
rob@100
|
19173 p.imageData = drawing.$ensureContext().getImageData(0, 0, p.width, p.height);
|
rob@100
|
19174 };
|
rob@100
|
19175
|
rob@100
|
19176 // Draws a 1-Dimensional pixel array to Canvas
|
rob@100
|
19177 /**
|
rob@100
|
19178 * Updates the display window with the data in the pixels[] array. Use in conjunction with loadPixels(). If
|
rob@100
|
19179 * you're only reading pixels from the array, there's no need to call updatePixels() unless there are changes.
|
rob@100
|
19180 * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule is that
|
rob@100
|
19181 * any time you want to manipulate the pixels[] array, you must first call loadPixels(), and after changes
|
rob@100
|
19182 * have been made, call updatePixels(). Even if the renderer may not seem to use this function in the current
|
rob@100
|
19183 * Processing release, this will always be subject to change.
|
rob@100
|
19184 * Currently, none of the renderers use the additional parameters to updatePixels(), however this may be
|
rob@100
|
19185 * implemented in the future.
|
rob@100
|
19186 *
|
rob@100
|
19187 * @see loadPixels
|
rob@100
|
19188 * @see pixels[]
|
rob@100
|
19189 */
|
rob@100
|
19190 p.updatePixels = function() {
|
rob@100
|
19191 if (p.imageData) {
|
rob@100
|
19192 drawing.$ensureContext().putImageData(p.imageData, 0, 0);
|
rob@100
|
19193 }
|
rob@100
|
19194 };
|
rob@100
|
19195
|
rob@100
|
19196 /**
|
rob@100
|
19197 * Set various hints and hacks for the renderer. This is used to handle obscure rendering features that cannot be
|
rob@100
|
19198 * implemented in a consistent manner across renderers. Many options will often graduate to standard features
|
rob@100
|
19199 * instead of hints over time.
|
rob@100
|
19200 * hint(ENABLE_OPENGL_4X_SMOOTH) - Enable 4x anti-aliasing for OpenGL. This can help force anti-aliasing if
|
rob@100
|
19201 * it has not been enabled by the user. On some graphics cards, this can also be set by the graphics driver's
|
rob@100
|
19202 * control panel, however not all cards make this available. This hint must be called immediately after the
|
rob@100
|
19203 * size() command because it resets the renderer, obliterating any settings and anything drawn (and like size(),
|
rob@100
|
19204 * re-running the code that came before it again).
|
rob@100
|
19205 * hint(DISABLE_OPENGL_2X_SMOOTH) - In Processing 1.0, Processing always enables 2x smoothing when the OpenGL
|
rob@100
|
19206 * renderer is used. This hint disables the default 2x smoothing and returns the smoothing behavior found in
|
rob@100
|
19207 * earlier releases, where smooth() and noSmooth() could be used to enable and disable smoothing, though the
|
rob@100
|
19208 * quality was inferior.
|
rob@100
|
19209 * hint(ENABLE_NATIVE_FONTS) - Use the native version fonts when they are installed, rather than the bitmapped
|
rob@100
|
19210 * version from a .vlw file. This is useful with the JAVA2D renderer setting, as it will improve font rendering
|
rob@100
|
19211 * speed. This is not enabled by default, because it can be misleading while testing because the type will look
|
rob@100
|
19212 * great on your machine (because you have the font installed) but lousy on others' machines if the identical
|
rob@100
|
19213 * font is unavailable. This option can only be set per-sketch, and must be called before any use of textFont().
|
rob@100
|
19214 * hint(DISABLE_DEPTH_TEST) - Disable the zbuffer, allowing you to draw on top of everything at will. When depth
|
rob@100
|
19215 * testing is disabled, items will be drawn to the screen sequentially, like a painting. This hint is most often
|
rob@100
|
19216 * used to draw in 3D, then draw in 2D on top of it (for instance, to draw GUI controls in 2D on top of a 3D
|
rob@100
|
19217 * interface). Starting in release 0149, this will also clear the depth buffer. Restore the default with
|
rob@100
|
19218 * hint(ENABLE_DEPTH_TEST), but note that with the depth buffer cleared, any 3D drawing that happens later in
|
rob@100
|
19219 * draw() will ignore existing shapes on the screen.
|
rob@100
|
19220 * hint(ENABLE_DEPTH_SORT) - Enable primitive z-sorting of triangles and lines in P3D and OPENGL. This can slow
|
rob@100
|
19221 * performance considerably, and the algorithm is not yet perfect. Restore the default with hint(DISABLE_DEPTH_SORT).
|
rob@100
|
19222 * hint(DISABLE_OPENGL_ERROR_REPORT) - Speeds up the OPENGL renderer setting by not checking for errors while
|
rob@100
|
19223 * running. Undo with hint(ENABLE_OPENGL_ERROR_REPORT).
|
rob@100
|
19224 * As of release 0149, unhint() has been removed in favor of adding additional ENABLE/DISABLE constants to reset
|
rob@100
|
19225 * the default behavior. This prevents the double negatives, and also reinforces which hints can be enabled or disabled.
|
rob@100
|
19226 *
|
rob@100
|
19227 * @param {MODE} item constant: name of the hint to be enabled or disabled
|
rob@100
|
19228 *
|
rob@100
|
19229 * @see PGraphics
|
rob@100
|
19230 * @see createGraphics
|
rob@100
|
19231 * @see size
|
rob@100
|
19232 */
|
rob@100
|
19233 p.hint = function(which) {
|
rob@100
|
19234 var curContext = drawing.$ensureContext();
|
rob@100
|
19235 if (which === PConstants.DISABLE_DEPTH_TEST) {
|
rob@100
|
19236 curContext.disable(curContext.DEPTH_TEST);
|
rob@100
|
19237 curContext.depthMask(false);
|
rob@100
|
19238 curContext.clear(curContext.DEPTH_BUFFER_BIT);
|
rob@100
|
19239 }
|
rob@100
|
19240 else if (which === PConstants.ENABLE_DEPTH_TEST) {
|
rob@100
|
19241 curContext.enable(curContext.DEPTH_TEST);
|
rob@100
|
19242 curContext.depthMask(true);
|
rob@100
|
19243 }
|
rob@100
|
19244 else if (which === PConstants.ENABLE_OPENGL_2X_SMOOTH ||
|
rob@100
|
19245 which === PConstants.ENABLE_OPENGL_4X_SMOOTH){
|
rob@100
|
19246 renderSmooth = true;
|
rob@100
|
19247 }
|
rob@100
|
19248 else if (which === PConstants.DISABLE_OPENGL_2X_SMOOTH){
|
rob@100
|
19249 renderSmooth = false;
|
rob@100
|
19250 }
|
rob@100
|
19251 };
|
rob@100
|
19252
|
rob@100
|
19253 /**
|
rob@100
|
19254 * The background() function sets the color used for the background of the Processing window.
|
rob@100
|
19255 * The default background is light gray. In the <b>draw()</b> function, the background color is used to clear the display window at the beginning of each frame.
|
rob@100
|
19256 * An image can also be used as the background for a sketch, however its width and height must be the same size as the sketch window.
|
rob@100
|
19257 * To resize an image 'b' to the size of the sketch window, use b.resize(width, height).
|
rob@100
|
19258 * Images used as background will ignore the current <b>tint()</b> setting.
|
rob@100
|
19259 * For the main drawing surface, the alpha value will be ignored. However,
|
rob@100
|
19260 * alpha can be used on PGraphics objects from <b>createGraphics()</b>. This is
|
rob@100
|
19261 * the only way to set all the pixels partially transparent, for instance.
|
rob@100
|
19262 * If the 'gray' parameter is passed in the function sets the background to a grayscale value, based on the
|
rob@100
|
19263 * current colorMode.
|
rob@100
|
19264 * <p>
|
rob@100
|
19265 * Note that background() should be called before any transformations occur,
|
rob@100
|
19266 * because some implementations may require the current transformation matrix
|
rob@100
|
19267 * to be identity before drawing.
|
rob@100
|
19268 *
|
rob@100
|
19269 * @param {int|float} gray specifies a value between white and black
|
rob@100
|
19270 * @param {int|float} value1 red or hue value (depending on the current color mode)
|
rob@100
|
19271 * @param {int|float} value2 green or saturation value (depending on the current color mode)
|
rob@100
|
19272 * @param {int|float} value3 blue or brightness value (depending on the current color mode)
|
rob@100
|
19273 * @param {int|float} alpha opacity of the background
|
rob@100
|
19274 * @param {Color} color any value of the color datatype
|
rob@100
|
19275 * @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
|
rob@100
|
19276 * @param {PImage} image an instance of a PImage to use as a background
|
rob@100
|
19277 *
|
rob@100
|
19278 * @see #stroke()
|
rob@100
|
19279 * @see #fill()
|
rob@100
|
19280 * @see #tint()
|
rob@100
|
19281 * @see #colorMode()
|
rob@100
|
19282 */
|
rob@100
|
19283 var backgroundHelper = function(arg1, arg2, arg3, arg4) {
|
rob@100
|
19284 var obj;
|
rob@100
|
19285
|
rob@100
|
19286 if (arg1 instanceof PImage || arg1.__isPImage) {
|
rob@100
|
19287 obj = arg1;
|
rob@100
|
19288
|
rob@100
|
19289 if (!obj.loaded) {
|
rob@100
|
19290 throw "Error using image in background(): PImage not loaded.";
|
rob@100
|
19291 }
|
rob@100
|
19292 if(obj.width !== p.width || obj.height !== p.height){
|
rob@100
|
19293 throw "Background image must be the same dimensions as the canvas.";
|
rob@100
|
19294 }
|
rob@100
|
19295 } else {
|
rob@100
|
19296 obj = p.color(arg1, arg2, arg3, arg4);
|
rob@100
|
19297 }
|
rob@100
|
19298
|
rob@100
|
19299 backgroundObj = obj;
|
rob@100
|
19300 };
|
rob@100
|
19301
|
rob@100
|
19302 Drawing2D.prototype.background = function(arg1, arg2, arg3, arg4) {
|
rob@100
|
19303 if (arg1 !== undef) {
|
rob@100
|
19304 backgroundHelper(arg1, arg2, arg3, arg4);
|
rob@100
|
19305 }
|
rob@100
|
19306
|
rob@100
|
19307 if (backgroundObj instanceof PImage || backgroundObj.__isPImage) {
|
rob@100
|
19308 saveContext();
|
rob@100
|
19309 curContext.setTransform(1, 0, 0, 1, 0, 0);
|
rob@100
|
19310 p.image(backgroundObj, 0, 0);
|
rob@100
|
19311 restoreContext();
|
rob@100
|
19312 } else {
|
rob@100
|
19313 saveContext();
|
rob@100
|
19314 curContext.setTransform(1, 0, 0, 1, 0, 0);
|
rob@100
|
19315
|
rob@100
|
19316 // If the background is transparent
|
rob@100
|
19317 if (p.alpha(backgroundObj) !== colorModeA) {
|
rob@100
|
19318 curContext.clearRect(0,0, p.width, p.height);
|
rob@100
|
19319 }
|
rob@100
|
19320 curContext.fillStyle = p.color.toString(backgroundObj);
|
rob@100
|
19321 curContext.fillRect(0, 0, p.width, p.height);
|
rob@100
|
19322 isFillDirty = true;
|
rob@100
|
19323 restoreContext();
|
rob@100
|
19324 }
|
rob@100
|
19325 };
|
rob@100
|
19326
|
rob@100
|
19327 Drawing3D.prototype.background = function(arg1, arg2, arg3, arg4) {
|
rob@100
|
19328 if (arguments.length > 0) {
|
rob@100
|
19329 backgroundHelper(arg1, arg2, arg3, arg4);
|
rob@100
|
19330 }
|
rob@100
|
19331
|
rob@100
|
19332 var c = p.color.toGLArray(backgroundObj);
|
rob@100
|
19333 curContext.clearColor(c[0], c[1], c[2], c[3]);
|
rob@100
|
19334 curContext.clear(curContext.COLOR_BUFFER_BIT | curContext.DEPTH_BUFFER_BIT);
|
rob@100
|
19335
|
rob@100
|
19336 // An image as a background in 3D is not implemented yet
|
rob@100
|
19337 };
|
rob@100
|
19338
|
rob@100
|
19339 // Draws an image to the Canvas
|
rob@100
|
19340 /**
|
rob@100
|
19341 * Displays images to the screen. The images must be in the sketch's "data" directory to load correctly. Select "Add
|
rob@100
|
19342 * file..." from the "Sketch" menu to add the image. Processing currently works with GIF, JPEG, and Targa images. The
|
rob@100
|
19343 * color of an image may be modified with the tint() function and if a GIF has transparency, it will maintain its
|
rob@100
|
19344 * transparency. The img parameter specifies the image to display and the x and y parameters define the location of
|
rob@100
|
19345 * the image from its upper-left corner. The image is displayed at its original size unless the width and height
|
rob@100
|
19346 * parameters specify a different size. The imageMode() function changes the way the parameters work. A call to
|
rob@100
|
19347 * imageMode(CORNERS) will change the width and height parameters to define the x and y values of the opposite
|
rob@100
|
19348 * corner of the image.
|
rob@100
|
19349 *
|
rob@100
|
19350 * @param {PImage} img the image to display
|
rob@100
|
19351 * @param {int|float} x x-coordinate of the image
|
rob@100
|
19352 * @param {int|float} y y-coordinate of the image
|
rob@100
|
19353 * @param {int|float} width width to display the image
|
rob@100
|
19354 * @param {int|float} height height to display the image
|
rob@100
|
19355 *
|
rob@100
|
19356 * @see loadImage
|
rob@100
|
19357 * @see PImage
|
rob@100
|
19358 * @see imageMode
|
rob@100
|
19359 * @see tint
|
rob@100
|
19360 * @see background
|
rob@100
|
19361 * @see alpha
|
rob@100
|
19362 */
|
rob@100
|
19363 Drawing2D.prototype.image = function(img, x, y, w, h) {
|
rob@100
|
19364 // Fix fractional positions
|
rob@100
|
19365 x = Math.round(x);
|
rob@100
|
19366 y = Math.round(y);
|
rob@100
|
19367
|
rob@100
|
19368 if (img.width > 0) {
|
rob@100
|
19369 var wid = w || img.width;
|
rob@100
|
19370 var hgt = h || img.height;
|
rob@100
|
19371
|
rob@100
|
19372 var bounds = imageModeConvert(x || 0, y || 0, w || img.width, h || img.height, arguments.length < 4);
|
rob@100
|
19373 var fastImage = !!img.sourceImg && curTint === null;
|
rob@100
|
19374 if (fastImage) {
|
rob@100
|
19375 var htmlElement = img.sourceImg;
|
rob@100
|
19376 if (img.__isDirty) {
|
rob@100
|
19377 img.updatePixels();
|
rob@100
|
19378 }
|
rob@100
|
19379 // Using HTML element's width and height in case if the image was resized.
|
rob@100
|
19380 curContext.drawImage(htmlElement, 0, 0,
|
rob@100
|
19381 htmlElement.width, htmlElement.height, bounds.x, bounds.y, bounds.w, bounds.h);
|
rob@100
|
19382 } else {
|
rob@100
|
19383 var obj = img.toImageData();
|
rob@100
|
19384
|
rob@100
|
19385 // Tint the image
|
rob@100
|
19386 if (curTint !== null) {
|
rob@100
|
19387 curTint(obj);
|
rob@100
|
19388 img.__isDirty = true;
|
rob@100
|
19389 }
|
rob@100
|
19390
|
rob@100
|
19391 curContext.drawImage(getCanvasData(obj).canvas, 0, 0,
|
rob@100
|
19392 img.width, img.height, bounds.x, bounds.y, bounds.w, bounds.h);
|
rob@100
|
19393 }
|
rob@100
|
19394 }
|
rob@100
|
19395 };
|
rob@100
|
19396
|
rob@100
|
19397 Drawing3D.prototype.image = function(img, x, y, w, h) {
|
rob@100
|
19398 if (img.width > 0) {
|
rob@100
|
19399 // Fix fractional positions
|
rob@100
|
19400 x = Math.round(x);
|
rob@100
|
19401 y = Math.round(y);
|
rob@100
|
19402 w = w || img.width;
|
rob@100
|
19403 h = h || img.height;
|
rob@100
|
19404
|
rob@100
|
19405 p.beginShape(p.QUADS);
|
rob@100
|
19406 p.texture(img);
|
rob@100
|
19407 p.vertex(x, y, 0, 0, 0);
|
rob@100
|
19408 p.vertex(x, y+h, 0, 0, h);
|
rob@100
|
19409 p.vertex(x+w, y+h, 0, w, h);
|
rob@100
|
19410 p.vertex(x+w, y, 0, w, 0);
|
rob@100
|
19411 p.endShape();
|
rob@100
|
19412 }
|
rob@100
|
19413 };
|
rob@100
|
19414
|
rob@100
|
19415 /**
|
rob@100
|
19416 * The tint() function sets the fill value for displaying images. Images can be tinted to
|
rob@100
|
19417 * specified colors or made transparent by setting the alpha.
|
rob@100
|
19418 * <br><br>To make an image transparent, but not change it's color,
|
rob@100
|
19419 * use white as the tint color and specify an alpha value. For instance,
|
rob@100
|
19420 * tint(255, 128) will make an image 50% transparent (unless
|
rob@100
|
19421 * <b>colorMode()</b> has been used).
|
rob@100
|
19422 *
|
rob@100
|
19423 * <br><br>When using hexadecimal notation to specify a color, use "#" or
|
rob@100
|
19424 * "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
|
rob@100
|
19425 * digits to specify a color (the way colors are specified in HTML and CSS).
|
rob@100
|
19426 * When using the hexadecimal notation starting with "0x", the hexadecimal
|
rob@100
|
19427 * value must be specified with eight characters; the first two characters
|
rob@100
|
19428 * define the alpha component and the remainder the red, green, and blue
|
rob@100
|
19429 * components.
|
rob@100
|
19430 * <br><br>The value for the parameter "gray" must be less than or equal
|
rob@100
|
19431 * to the current maximum value as specified by <b>colorMode()</b>.
|
rob@100
|
19432 * The default maximum value is 255.
|
rob@100
|
19433 * <br><br>The tint() method is also used to control the coloring of
|
rob@100
|
19434 * textures in 3D.
|
rob@100
|
19435 *
|
rob@100
|
19436 * @param {int|float} gray any valid number
|
rob@100
|
19437 * @param {int|float} alpha opacity of the image
|
rob@100
|
19438 * @param {int|float} value1 red or hue value
|
rob@100
|
19439 * @param {int|float} value2 green or saturation value
|
rob@100
|
19440 * @param {int|float} value3 blue or brightness value
|
rob@100
|
19441 * @param {int|float} color any value of the color datatype
|
rob@100
|
19442 * @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
|
rob@100
|
19443 *
|
rob@100
|
19444 * @see #noTint()
|
rob@100
|
19445 * @see #image()
|
rob@100
|
19446 */
|
rob@100
|
19447 p.tint = function(a1, a2, a3, a4) {
|
rob@100
|
19448 var tintColor = p.color(a1, a2, a3, a4);
|
rob@100
|
19449 var r = p.red(tintColor) / colorModeX;
|
rob@100
|
19450 var g = p.green(tintColor) / colorModeY;
|
rob@100
|
19451 var b = p.blue(tintColor) / colorModeZ;
|
rob@100
|
19452 var a = p.alpha(tintColor) / colorModeA;
|
rob@100
|
19453 curTint = function(obj) {
|
rob@100
|
19454 var data = obj.data,
|
rob@100
|
19455 length = 4 * obj.width * obj.height;
|
rob@100
|
19456 for (var i = 0; i < length;) {
|
rob@100
|
19457 data[i++] *= r;
|
rob@100
|
19458 data[i++] *= g;
|
rob@100
|
19459 data[i++] *= b;
|
rob@100
|
19460 data[i++] *= a;
|
rob@100
|
19461 }
|
rob@100
|
19462 };
|
rob@100
|
19463 // for overriding the color buffer when 3d rendering
|
rob@100
|
19464 curTint3d = function(data){
|
rob@100
|
19465 for (var i = 0; i < data.length;) {
|
rob@100
|
19466 data[i++] = r;
|
rob@100
|
19467 data[i++] = g;
|
rob@100
|
19468 data[i++] = b;
|
rob@100
|
19469 data[i++] = a;
|
rob@100
|
19470 }
|
rob@100
|
19471 };
|
rob@100
|
19472 };
|
rob@100
|
19473
|
rob@100
|
19474 /**
|
rob@100
|
19475 * The noTint() function removes the current fill value for displaying images and reverts to displaying images with their original hues.
|
rob@100
|
19476 *
|
rob@100
|
19477 * @see #tint()
|
rob@100
|
19478 * @see #image()
|
rob@100
|
19479 */
|
rob@100
|
19480 p.noTint = function() {
|
rob@100
|
19481 curTint = null;
|
rob@100
|
19482 curTint3d = null;
|
rob@100
|
19483 };
|
rob@100
|
19484
|
rob@100
|
19485 /**
|
rob@100
|
19486 * Copies a region of pixels from the display window to another area of the display window and copies a region of pixels from an
|
rob@100
|
19487 * image used as the srcImg parameter into the display window. If the source and destination regions aren't the same size, it will
|
rob@100
|
19488 * automatically resize the source pixels to fit the specified target region. No alpha information is used in the process, however
|
rob@100
|
19489 * if the source image has an alpha channel set, it will be copied as well. This function ignores imageMode().
|
rob@100
|
19490 *
|
rob@100
|
19491 * @param {int} x X coordinate of the source's upper left corner
|
rob@100
|
19492 * @param {int} y Y coordinate of the source's upper left corner
|
rob@100
|
19493 * @param {int} width source image width
|
rob@100
|
19494 * @param {int} height source image height
|
rob@100
|
19495 * @param {int} dx X coordinate of the destination's upper left corner
|
rob@100
|
19496 * @param {int} dy Y coordinate of the destination's upper left corner
|
rob@100
|
19497 * @param {int} dwidth destination image width
|
rob@100
|
19498 * @param {int} dheight destination image height
|
rob@100
|
19499 * @param {PImage} srcImg image variable referring to the source image
|
rob@100
|
19500 *
|
rob@100
|
19501 * @see blend
|
rob@100
|
19502 * @see get
|
rob@100
|
19503 */
|
rob@100
|
19504 p.copy = function(src, sx, sy, sw, sh, dx, dy, dw, dh) {
|
rob@100
|
19505 if (dh === undef) {
|
rob@100
|
19506 // shift everything, and introduce p
|
rob@100
|
19507 dh = dw;
|
rob@100
|
19508 dw = dy;
|
rob@100
|
19509 dy = dx;
|
rob@100
|
19510 dx = sh;
|
rob@100
|
19511 sh = sw;
|
rob@100
|
19512 sw = sy;
|
rob@100
|
19513 sy = sx;
|
rob@100
|
19514 sx = src;
|
rob@100
|
19515 src = p;
|
rob@100
|
19516 }
|
rob@100
|
19517 p.blend(src, sx, sy, sw, sh, dx, dy, dw, dh, PConstants.REPLACE);
|
rob@100
|
19518 };
|
rob@100
|
19519
|
rob@100
|
19520 /**
|
rob@100
|
19521 * Blends a region of pixels from one image into another (or in itself again) with full alpha channel support. There
|
rob@100
|
19522 * is a choice of the following modes to blend the source pixels (A) with the ones of pixels in the destination image (B):
|
rob@100
|
19523 * BLEND - linear interpolation of colours: C = A*factor + B
|
rob@100
|
19524 * ADD - additive blending with white clip: C = min(A*factor + B, 255)
|
rob@100
|
19525 * SUBTRACT - subtractive blending with black clip: C = max(B - A*factor, 0)
|
rob@100
|
19526 * DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
|
rob@100
|
19527 * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
|
rob@100
|
19528 * DIFFERENCE - subtract colors from underlying image.
|
rob@100
|
19529 * EXCLUSION - similar to DIFFERENCE, but less extreme.
|
rob@100
|
19530 * MULTIPLY - Multiply the colors, result will always be darker.
|
rob@100
|
19531 * SCREEN - Opposite multiply, uses inverse values of the colors.
|
rob@100
|
19532 * OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values, and screens light values.
|
rob@100
|
19533 * HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
|
rob@100
|
19534 * SOFT_LIGHT - Mix of DARKEST and LIGHTEST. Works like OVERLAY, but not as harsh.
|
rob@100
|
19535 * DODGE - Lightens light tones and increases contrast, ignores darks. Called "Color Dodge" in Illustrator and Photoshop.
|
rob@100
|
19536 * BURN - Darker areas are applied, increasing contrast, ignores lights. Called "Color Burn" in Illustrator and Photoshop.
|
rob@100
|
19537 * All modes use the alpha information (highest byte) of source image pixels as the blending factor. If the source and
|
rob@100
|
19538 * destination regions are different sizes, the image will be automatically resized to match the destination size. If the
|
rob@100
|
19539 * srcImg parameter is not used, the display window is used as the source image. This function ignores imageMode().
|
rob@100
|
19540 *
|
rob@100
|
19541 * @param {int} x X coordinate of the source's upper left corner
|
rob@100
|
19542 * @param {int} y Y coordinate of the source's upper left corner
|
rob@100
|
19543 * @param {int} width source image width
|
rob@100
|
19544 * @param {int} height source image height
|
rob@100
|
19545 * @param {int} dx X coordinate of the destination's upper left corner
|
rob@100
|
19546 * @param {int} dy Y coordinate of the destination's upper left corner
|
rob@100
|
19547 * @param {int} dwidth destination image width
|
rob@100
|
19548 * @param {int} dheight destination image height
|
rob@100
|
19549 * @param {PImage} srcImg image variable referring to the source image
|
rob@100
|
19550 * @param {PImage} MODE Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION, MULTIPLY, SCREEN,
|
rob@100
|
19551 * OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
|
rob@100
|
19552 * @see filter
|
rob@100
|
19553 */
|
rob@100
|
19554 p.blend = function(src, sx, sy, sw, sh, dx, dy, dw, dh, mode, pimgdest) {
|
rob@100
|
19555 if (src.isRemote) {
|
rob@100
|
19556 throw "Image is loaded remotely. Cannot blend image.";
|
rob@100
|
19557 }
|
rob@100
|
19558
|
rob@100
|
19559 if (mode === undef) {
|
rob@100
|
19560 // shift everything, and introduce p
|
rob@100
|
19561 mode = dh;
|
rob@100
|
19562 dh = dw;
|
rob@100
|
19563 dw = dy;
|
rob@100
|
19564 dy = dx;
|
rob@100
|
19565 dx = sh;
|
rob@100
|
19566 sh = sw;
|
rob@100
|
19567 sw = sy;
|
rob@100
|
19568 sy = sx;
|
rob@100
|
19569 sx = src;
|
rob@100
|
19570 src = p;
|
rob@100
|
19571 }
|
rob@100
|
19572
|
rob@100
|
19573 var sx2 = sx + sw,
|
rob@100
|
19574 sy2 = sy + sh,
|
rob@100
|
19575 dx2 = dx + dw,
|
rob@100
|
19576 dy2 = dy + dh,
|
rob@100
|
19577 dest = pimgdest || p;
|
rob@100
|
19578
|
rob@100
|
19579 // check if pimgdest is there and pixels, if so this was a call from pimg.blend
|
rob@100
|
19580 if (pimgdest === undef || mode === undef) {
|
rob@100
|
19581 p.loadPixels();
|
rob@100
|
19582 }
|
rob@100
|
19583
|
rob@100
|
19584 src.loadPixels();
|
rob@100
|
19585
|
rob@100
|
19586 if (src === p && p.intersect(sx, sy, sx2, sy2, dx, dy, dx2, dy2)) {
|
rob@100
|
19587 p.blit_resize(p.get(sx, sy, sx2 - sx, sy2 - sy), 0, 0, sx2 - sx - 1, sy2 - sy - 1,
|
rob@100
|
19588 dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
|
rob@100
|
19589 } else {
|
rob@100
|
19590 p.blit_resize(src, sx, sy, sx2, sy2, dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
|
rob@100
|
19591 }
|
rob@100
|
19592
|
rob@100
|
19593 if (pimgdest === undef) {
|
rob@100
|
19594 p.updatePixels();
|
rob@100
|
19595 }
|
rob@100
|
19596 };
|
rob@100
|
19597
|
rob@100
|
19598 // helper function for filter()
|
rob@100
|
19599 var buildBlurKernel = function(r) {
|
rob@100
|
19600 var radius = p.floor(r * 3.5), i, radiusi;
|
rob@100
|
19601 radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248);
|
rob@100
|
19602 if (p.shared.blurRadius !== radius) {
|
rob@100
|
19603 p.shared.blurRadius = radius;
|
rob@100
|
19604 p.shared.blurKernelSize = 1 + (p.shared.blurRadius<<1);
|
rob@100
|
19605 p.shared.blurKernel = new Float32Array(p.shared.blurKernelSize);
|
rob@100
|
19606 var sharedBlurKernal = p.shared.blurKernel;
|
rob@100
|
19607 var sharedBlurKernelSize = p.shared.blurKernelSize;
|
rob@100
|
19608 var sharedBlurRadius = p.shared.blurRadius;
|
rob@100
|
19609 // init blurKernel
|
rob@100
|
19610 for (i = 0; i < sharedBlurKernelSize; i++) {
|
rob@100
|
19611 sharedBlurKernal[i] = 0;
|
rob@100
|
19612 }
|
rob@100
|
19613 var radiusiSquared = (radius - 1) * (radius - 1);
|
rob@100
|
19614 for (i = 1; i < radius; i++) {
|
rob@100
|
19615 sharedBlurKernal[radius + i] = sharedBlurKernal[radiusi] = radiusiSquared;
|
rob@100
|
19616 }
|
rob@100
|
19617 sharedBlurKernal[radius] = radius * radius;
|
rob@100
|
19618 }
|
rob@100
|
19619 };
|
rob@100
|
19620
|
rob@100
|
19621 var blurARGB = function(r, aImg) {
|
rob@100
|
19622 var sum, cr, cg, cb, ca, c, m;
|
rob@100
|
19623 var read, ri, ym, ymi, bk0;
|
rob@100
|
19624 var wh = aImg.pixels.getLength();
|
rob@100
|
19625 var r2 = new Float32Array(wh);
|
rob@100
|
19626 var g2 = new Float32Array(wh);
|
rob@100
|
19627 var b2 = new Float32Array(wh);
|
rob@100
|
19628 var a2 = new Float32Array(wh);
|
rob@100
|
19629 var yi = 0;
|
rob@100
|
19630 var x, y, i, offset;
|
rob@100
|
19631
|
rob@100
|
19632 buildBlurKernel(r);
|
rob@100
|
19633
|
rob@100
|
19634 var aImgHeight = aImg.height;
|
rob@100
|
19635 var aImgWidth = aImg.width;
|
rob@100
|
19636 var sharedBlurKernelSize = p.shared.blurKernelSize;
|
rob@100
|
19637 var sharedBlurRadius = p.shared.blurRadius;
|
rob@100
|
19638 var sharedBlurKernal = p.shared.blurKernel;
|
rob@100
|
19639 var pix = aImg.imageData.data;
|
rob@100
|
19640
|
rob@100
|
19641 for (y = 0; y < aImgHeight; y++) {
|
rob@100
|
19642 for (x = 0; x < aImgWidth; x++) {
|
rob@100
|
19643 cb = cg = cr = ca = sum = 0;
|
rob@100
|
19644 read = x - sharedBlurRadius;
|
rob@100
|
19645 if (read<0) {
|
rob@100
|
19646 bk0 = -read;
|
rob@100
|
19647 read = 0;
|
rob@100
|
19648 } else {
|
rob@100
|
19649 if (read >= aImgWidth) {
|
rob@100
|
19650 break;
|
rob@100
|
19651 }
|
rob@100
|
19652 bk0=0;
|
rob@100
|
19653 }
|
rob@100
|
19654 for (i = bk0; i < sharedBlurKernelSize; i++) {
|
rob@100
|
19655 if (read >= aImgWidth) {
|
rob@100
|
19656 break;
|
rob@100
|
19657 }
|
rob@100
|
19658 offset = (read + yi) *4;
|
rob@100
|
19659 m = sharedBlurKernal[i];
|
rob@100
|
19660 ca += m * pix[offset + 3];
|
rob@100
|
19661 cr += m * pix[offset];
|
rob@100
|
19662 cg += m * pix[offset + 1];
|
rob@100
|
19663 cb += m * pix[offset + 2];
|
rob@100
|
19664 sum += m;
|
rob@100
|
19665 read++;
|
rob@100
|
19666 }
|
rob@100
|
19667 ri = yi + x;
|
rob@100
|
19668 a2[ri] = ca / sum;
|
rob@100
|
19669 r2[ri] = cr / sum;
|
rob@100
|
19670 g2[ri] = cg / sum;
|
rob@100
|
19671 b2[ri] = cb / sum;
|
rob@100
|
19672 }
|
rob@100
|
19673 yi += aImgWidth;
|
rob@100
|
19674 }
|
rob@100
|
19675
|
rob@100
|
19676 yi = 0;
|
rob@100
|
19677 ym = -sharedBlurRadius;
|
rob@100
|
19678 ymi = ym*aImgWidth;
|
rob@100
|
19679
|
rob@100
|
19680 for (y = 0; y < aImgHeight; y++) {
|
rob@100
|
19681 for (x = 0; x < aImgWidth; x++) {
|
rob@100
|
19682 cb = cg = cr = ca = sum = 0;
|
rob@100
|
19683 if (ym<0) {
|
rob@100
|
19684 bk0 = ri = -ym;
|
rob@100
|
19685 read = x;
|
rob@100
|
19686 } else {
|
rob@100
|
19687 if (ym >= aImgHeight) {
|
rob@100
|
19688 break;
|
rob@100
|
19689 }
|
rob@100
|
19690 bk0 = 0;
|
rob@100
|
19691 ri = ym;
|
rob@100
|
19692 read = x + ymi;
|
rob@100
|
19693 }
|
rob@100
|
19694 for (i = bk0; i < sharedBlurKernelSize; i++) {
|
rob@100
|
19695 if (ri >= aImgHeight) {
|
rob@100
|
19696 break;
|
rob@100
|
19697 }
|
rob@100
|
19698 m = sharedBlurKernal[i];
|
rob@100
|
19699 ca += m * a2[read];
|
rob@100
|
19700 cr += m * r2[read];
|
rob@100
|
19701 cg += m * g2[read];
|
rob@100
|
19702 cb += m * b2[read];
|
rob@100
|
19703 sum += m;
|
rob@100
|
19704 ri++;
|
rob@100
|
19705 read += aImgWidth;
|
rob@100
|
19706 }
|
rob@100
|
19707 offset = (x + yi) *4;
|
rob@100
|
19708 pix[offset] = cr / sum;
|
rob@100
|
19709 pix[offset + 1] = cg / sum;
|
rob@100
|
19710 pix[offset + 2] = cb / sum;
|
rob@100
|
19711 pix[offset + 3] = ca / sum;
|
rob@100
|
19712 }
|
rob@100
|
19713 yi += aImgWidth;
|
rob@100
|
19714 ymi += aImgWidth;
|
rob@100
|
19715 ym++;
|
rob@100
|
19716 }
|
rob@100
|
19717 };
|
rob@100
|
19718
|
rob@100
|
19719 // helper funtion for ERODE and DILATE modes of filter()
|
rob@100
|
19720 var dilate = function(isInverted, aImg) {
|
rob@100
|
19721 var currIdx = 0;
|
rob@100
|
19722 var maxIdx = aImg.pixels.getLength();
|
rob@100
|
19723 var out = new Int32Array(maxIdx);
|
rob@100
|
19724 var currRowIdx, maxRowIdx, colOrig, colOut, currLum;
|
rob@100
|
19725 var idxRight, idxLeft, idxUp, idxDown,
|
rob@100
|
19726 colRight, colLeft, colUp, colDown,
|
rob@100
|
19727 lumRight, lumLeft, lumUp, lumDown;
|
rob@100
|
19728
|
rob@100
|
19729 if (!isInverted) {
|
rob@100
|
19730 // erosion (grow light areas)
|
rob@100
|
19731 while (currIdx<maxIdx) {
|
rob@100
|
19732 currRowIdx = currIdx;
|
rob@100
|
19733 maxRowIdx = currIdx + aImg.width;
|
rob@100
|
19734 while (currIdx < maxRowIdx) {
|
rob@100
|
19735 colOrig = colOut = aImg.pixels.getPixel(currIdx);
|
rob@100
|
19736 idxLeft = currIdx - 1;
|
rob@100
|
19737 idxRight = currIdx + 1;
|
rob@100
|
19738 idxUp = currIdx - aImg.width;
|
rob@100
|
19739 idxDown = currIdx + aImg.width;
|
rob@100
|
19740 if (idxLeft < currRowIdx) {
|
rob@100
|
19741 idxLeft = currIdx;
|
rob@100
|
19742 }
|
rob@100
|
19743 if (idxRight >= maxRowIdx) {
|
rob@100
|
19744 idxRight = currIdx;
|
rob@100
|
19745 }
|
rob@100
|
19746 if (idxUp < 0) {
|
rob@100
|
19747 idxUp = 0;
|
rob@100
|
19748 }
|
rob@100
|
19749 if (idxDown >= maxIdx) {
|
rob@100
|
19750 idxDown = currIdx;
|
rob@100
|
19751 }
|
rob@100
|
19752 colUp = aImg.pixels.getPixel(idxUp);
|
rob@100
|
19753 colLeft = aImg.pixels.getPixel(idxLeft);
|
rob@100
|
19754 colDown = aImg.pixels.getPixel(idxDown);
|
rob@100
|
19755 colRight = aImg.pixels.getPixel(idxRight);
|
rob@100
|
19756
|
rob@100
|
19757 // compute luminance
|
rob@100
|
19758 currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff);
|
rob@100
|
19759 lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
|
rob@100
|
19760 lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
|
rob@100
|
19761 lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
|
rob@100
|
19762 lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
|
rob@100
|
19763
|
rob@100
|
19764 if (lumLeft > currLum) {
|
rob@100
|
19765 colOut = colLeft;
|
rob@100
|
19766 currLum = lumLeft;
|
rob@100
|
19767 }
|
rob@100
|
19768 if (lumRight > currLum) {
|
rob@100
|
19769 colOut = colRight;
|
rob@100
|
19770 currLum = lumRight;
|
rob@100
|
19771 }
|
rob@100
|
19772 if (lumUp > currLum) {
|
rob@100
|
19773 colOut = colUp;
|
rob@100
|
19774 currLum = lumUp;
|
rob@100
|
19775 }
|
rob@100
|
19776 if (lumDown > currLum) {
|
rob@100
|
19777 colOut = colDown;
|
rob@100
|
19778 currLum = lumDown;
|
rob@100
|
19779 }
|
rob@100
|
19780 out[currIdx++] = colOut;
|
rob@100
|
19781 }
|
rob@100
|
19782 }
|
rob@100
|
19783 } else {
|
rob@100
|
19784 // dilate (grow dark areas)
|
rob@100
|
19785 while (currIdx < maxIdx) {
|
rob@100
|
19786 currRowIdx = currIdx;
|
rob@100
|
19787 maxRowIdx = currIdx + aImg.width;
|
rob@100
|
19788 while (currIdx < maxRowIdx) {
|
rob@100
|
19789 colOrig = colOut = aImg.pixels.getPixel(currIdx);
|
rob@100
|
19790 idxLeft = currIdx - 1;
|
rob@100
|
19791 idxRight = currIdx + 1;
|
rob@100
|
19792 idxUp = currIdx - aImg.width;
|
rob@100
|
19793 idxDown = currIdx + aImg.width;
|
rob@100
|
19794 if (idxLeft < currRowIdx) {
|
rob@100
|
19795 idxLeft = currIdx;
|
rob@100
|
19796 }
|
rob@100
|
19797 if (idxRight >= maxRowIdx) {
|
rob@100
|
19798 idxRight = currIdx;
|
rob@100
|
19799 }
|
rob@100
|
19800 if (idxUp < 0) {
|
rob@100
|
19801 idxUp = 0;
|
rob@100
|
19802 }
|
rob@100
|
19803 if (idxDown >= maxIdx) {
|
rob@100
|
19804 idxDown = currIdx;
|
rob@100
|
19805 }
|
rob@100
|
19806 colUp = aImg.pixels.getPixel(idxUp);
|
rob@100
|
19807 colLeft = aImg.pixels.getPixel(idxLeft);
|
rob@100
|
19808 colDown = aImg.pixels.getPixel(idxDown);
|
rob@100
|
19809 colRight = aImg.pixels.getPixel(idxRight);
|
rob@100
|
19810
|
rob@100
|
19811 // compute luminance
|
rob@100
|
19812 currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff);
|
rob@100
|
19813 lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
|
rob@100
|
19814 lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
|
rob@100
|
19815 lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
|
rob@100
|
19816 lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
|
rob@100
|
19817
|
rob@100
|
19818 if (lumLeft < currLum) {
|
rob@100
|
19819 colOut = colLeft;
|
rob@100
|
19820 currLum = lumLeft;
|
rob@100
|
19821 }
|
rob@100
|
19822 if (lumRight < currLum) {
|
rob@100
|
19823 colOut = colRight;
|
rob@100
|
19824 currLum = lumRight;
|
rob@100
|
19825 }
|
rob@100
|
19826 if (lumUp < currLum) {
|
rob@100
|
19827 colOut = colUp;
|
rob@100
|
19828 currLum = lumUp;
|
rob@100
|
19829 }
|
rob@100
|
19830 if (lumDown < currLum) {
|
rob@100
|
19831 colOut = colDown;
|
rob@100
|
19832 currLum = lumDown;
|
rob@100
|
19833 }
|
rob@100
|
19834 out[currIdx++]=colOut;
|
rob@100
|
19835 }
|
rob@100
|
19836 }
|
rob@100
|
19837 }
|
rob@100
|
19838 aImg.pixels.set(out);
|
rob@100
|
19839 //p.arraycopy(out,0,pixels,0,maxIdx);
|
rob@100
|
19840 };
|
rob@100
|
19841
|
rob@100
|
19842 /**
|
rob@100
|
19843 * Filters the display window as defined by one of the following modes:
|
rob@100
|
19844 * THRESHOLD - converts the image to black and white pixels depending if they are above or below the threshold
|
rob@100
|
19845 * defined by the level parameter. The level must be between 0.0 (black) and 1.0(white). If no level is specified, 0.5 is used.
|
rob@100
|
19846 * GRAY - converts any colors in the image to grayscale equivalents
|
rob@100
|
19847 * INVERT - sets each pixel to its inverse value
|
rob@100
|
19848 * POSTERIZE - limits each channel of the image to the number of colors specified as the level parameter
|
rob@100
|
19849 * BLUR - executes a Guassian blur with the level parameter specifying the extent of the blurring. If no level parameter is
|
rob@100
|
19850 * used, the blur is equivalent to Guassian blur of radius 1.
|
rob@100
|
19851 * OPAQUE - sets the alpha channel to entirely opaque.
|
rob@100
|
19852 * ERODE - reduces the light areas with the amount defined by the level parameter.
|
rob@100
|
19853 * DILATE - increases the light areas with the amount defined by the level parameter.
|
rob@100
|
19854 *
|
rob@100
|
19855 * @param {MODE} MODE Either THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, or DILATE
|
rob@100
|
19856 * @param {int|float} level defines the quality of the filter
|
rob@100
|
19857 *
|
rob@100
|
19858 * @see blend
|
rob@100
|
19859 */
|
rob@100
|
19860 p.filter = function(kind, param, aImg){
|
rob@100
|
19861 var img, col, lum, i;
|
rob@100
|
19862
|
rob@100
|
19863 if (arguments.length === 3) {
|
rob@100
|
19864 aImg.loadPixels();
|
rob@100
|
19865 img = aImg;
|
rob@100
|
19866 } else {
|
rob@100
|
19867 p.loadPixels();
|
rob@100
|
19868 img = p;
|
rob@100
|
19869 }
|
rob@100
|
19870
|
rob@100
|
19871 if (param === undef) {
|
rob@100
|
19872 param = null;
|
rob@100
|
19873 }
|
rob@100
|
19874 if (img.isRemote) { // Remote images cannot access imageData
|
rob@100
|
19875 throw "Image is loaded remotely. Cannot filter image.";
|
rob@100
|
19876 }
|
rob@100
|
19877 // begin filter process
|
rob@100
|
19878 var imglen = img.pixels.getLength();
|
rob@100
|
19879 switch (kind) {
|
rob@100
|
19880 case PConstants.BLUR:
|
rob@100
|
19881 var radius = param || 1; // if no param specified, use 1 (default for p5)
|
rob@100
|
19882 blurARGB(radius, img);
|
rob@100
|
19883 break;
|
rob@100
|
19884
|
rob@100
|
19885 case PConstants.GRAY:
|
rob@100
|
19886 if (img.format === PConstants.ALPHA) { //trouble
|
rob@100
|
19887 // for an alpha image, convert it to an opaque grayscale
|
rob@100
|
19888 for (i = 0; i < imglen; i++) {
|
rob@100
|
19889 col = 255 - img.pixels.getPixel(i);
|
rob@100
|
19890 img.pixels.setPixel(i,(0xff000000 | (col << 16) | (col << 8) | col));
|
rob@100
|
19891 }
|
rob@100
|
19892 img.format = PConstants.RGB; //trouble
|
rob@100
|
19893 } else {
|
rob@100
|
19894 for (i = 0; i < imglen; i++) {
|
rob@100
|
19895 col = img.pixels.getPixel(i);
|
rob@100
|
19896 lum = (77*(col>>16&0xff) + 151*(col>>8&0xff) + 28*(col&0xff))>>8;
|
rob@100
|
19897 img.pixels.setPixel(i,((col & PConstants.ALPHA_MASK) | lum<<16 | lum<<8 | lum));
|
rob@100
|
19898 }
|
rob@100
|
19899 }
|
rob@100
|
19900 break;
|
rob@100
|
19901
|
rob@100
|
19902 case PConstants.INVERT:
|
rob@100
|
19903 for (i = 0; i < imglen; i++) {
|
rob@100
|
19904 img.pixels.setPixel(i, (img.pixels.getPixel(i) ^ 0xffffff));
|
rob@100
|
19905 }
|
rob@100
|
19906 break;
|
rob@100
|
19907
|
rob@100
|
19908 case PConstants.POSTERIZE:
|
rob@100
|
19909 if (param === null) {
|
rob@100
|
19910 throw "Use filter(POSTERIZE, int levels) instead of filter(POSTERIZE)";
|
rob@100
|
19911 }
|
rob@100
|
19912 var levels = p.floor(param);
|
rob@100
|
19913 if ((levels < 2) || (levels > 255)) {
|
rob@100
|
19914 throw "Levels must be between 2 and 255 for filter(POSTERIZE, levels)";
|
rob@100
|
19915 }
|
rob@100
|
19916 var levels1 = levels - 1;
|
rob@100
|
19917 for (i = 0; i < imglen; i++) {
|
rob@100
|
19918 var rlevel = (img.pixels.getPixel(i) >> 16) & 0xff;
|
rob@100
|
19919 var glevel = (img.pixels.getPixel(i) >> 8) & 0xff;
|
rob@100
|
19920 var blevel = img.pixels.getPixel(i) & 0xff;
|
rob@100
|
19921 rlevel = (((rlevel * levels) >> 8) * 255) / levels1;
|
rob@100
|
19922 glevel = (((glevel * levels) >> 8) * 255) / levels1;
|
rob@100
|
19923 blevel = (((blevel * levels) >> 8) * 255) / levels1;
|
rob@100
|
19924 img.pixels.setPixel(i, ((0xff000000 & img.pixels.getPixel(i)) | (rlevel << 16) | (glevel << 8) | blevel));
|
rob@100
|
19925 }
|
rob@100
|
19926 break;
|
rob@100
|
19927
|
rob@100
|
19928 case PConstants.OPAQUE:
|
rob@100
|
19929 for (i = 0; i < imglen; i++) {
|
rob@100
|
19930 img.pixels.setPixel(i, (img.pixels.getPixel(i) | 0xff000000));
|
rob@100
|
19931 }
|
rob@100
|
19932 img.format = PConstants.RGB; //trouble
|
rob@100
|
19933 break;
|
rob@100
|
19934
|
rob@100
|
19935 case PConstants.THRESHOLD:
|
rob@100
|
19936 if (param === null) {
|
rob@100
|
19937 param = 0.5;
|
rob@100
|
19938 }
|
rob@100
|
19939 if ((param < 0) || (param > 1)) {
|
rob@100
|
19940 throw "Level must be between 0 and 1 for filter(THRESHOLD, level)";
|
rob@100
|
19941 }
|
rob@100
|
19942 var thresh = p.floor(param * 255);
|
rob@100
|
19943 for (i = 0; i < imglen; i++) {
|
rob@100
|
19944 var max = p.max((img.pixels.getPixel(i) & PConstants.RED_MASK) >> 16, p.max((img.pixels.getPixel(i) & PConstants.GREEN_MASK) >> 8, (img.pixels.getPixel(i) & PConstants.BLUE_MASK)));
|
rob@100
|
19945 img.pixels.setPixel(i, ((img.pixels.getPixel(i) & PConstants.ALPHA_MASK) | ((max < thresh) ? 0x000000 : 0xffffff)));
|
rob@100
|
19946 }
|
rob@100
|
19947 break;
|
rob@100
|
19948
|
rob@100
|
19949 case PConstants.ERODE:
|
rob@100
|
19950 dilate(true, img);
|
rob@100
|
19951 break;
|
rob@100
|
19952
|
rob@100
|
19953 case PConstants.DILATE:
|
rob@100
|
19954 dilate(false, img);
|
rob@100
|
19955 break;
|
rob@100
|
19956 }
|
rob@100
|
19957 img.updatePixels();
|
rob@100
|
19958 };
|
rob@100
|
19959
|
rob@100
|
19960
|
rob@100
|
19961 // shared variables for blit_resize(), filter_new_scanline(), filter_bilinear(), filter()
|
rob@100
|
19962 // change this in the future to not be exposed to p
|
rob@100
|
19963 p.shared = {
|
rob@100
|
19964 fracU: 0,
|
rob@100
|
19965 ifU: 0,
|
rob@100
|
19966 fracV: 0,
|
rob@100
|
19967 ifV: 0,
|
rob@100
|
19968 u1: 0,
|
rob@100
|
19969 u2: 0,
|
rob@100
|
19970 v1: 0,
|
rob@100
|
19971 v2: 0,
|
rob@100
|
19972 sX: 0,
|
rob@100
|
19973 sY: 0,
|
rob@100
|
19974 iw: 0,
|
rob@100
|
19975 iw1: 0,
|
rob@100
|
19976 ih1: 0,
|
rob@100
|
19977 ul: 0,
|
rob@100
|
19978 ll: 0,
|
rob@100
|
19979 ur: 0,
|
rob@100
|
19980 lr: 0,
|
rob@100
|
19981 cUL: 0,
|
rob@100
|
19982 cLL: 0,
|
rob@100
|
19983 cUR: 0,
|
rob@100
|
19984 cLR: 0,
|
rob@100
|
19985 srcXOffset: 0,
|
rob@100
|
19986 srcYOffset: 0,
|
rob@100
|
19987 r: 0,
|
rob@100
|
19988 g: 0,
|
rob@100
|
19989 b: 0,
|
rob@100
|
19990 a: 0,
|
rob@100
|
19991 srcBuffer: null,
|
rob@100
|
19992 blurRadius: 0,
|
rob@100
|
19993 blurKernelSize: 0,
|
rob@100
|
19994 blurKernel: null
|
rob@100
|
19995 };
|
rob@100
|
19996
|
rob@100
|
19997 p.intersect = function(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2) {
|
rob@100
|
19998 var sw = sx2 - sx1 + 1;
|
rob@100
|
19999 var sh = sy2 - sy1 + 1;
|
rob@100
|
20000 var dw = dx2 - dx1 + 1;
|
rob@100
|
20001 var dh = dy2 - dy1 + 1;
|
rob@100
|
20002 if (dx1 < sx1) {
|
rob@100
|
20003 dw += dx1 - sx1;
|
rob@100
|
20004 if (dw > sw) {
|
rob@100
|
20005 dw = sw;
|
rob@100
|
20006 }
|
rob@100
|
20007 } else {
|
rob@100
|
20008 var w = sw + sx1 - dx1;
|
rob@100
|
20009 if (dw > w) {
|
rob@100
|
20010 dw = w;
|
rob@100
|
20011 }
|
rob@100
|
20012 }
|
rob@100
|
20013 if (dy1 < sy1) {
|
rob@100
|
20014 dh += dy1 - sy1;
|
rob@100
|
20015 if (dh > sh) {
|
rob@100
|
20016 dh = sh;
|
rob@100
|
20017 }
|
rob@100
|
20018 } else {
|
rob@100
|
20019 var h = sh + sy1 - dy1;
|
rob@100
|
20020 if (dh > h) {
|
rob@100
|
20021 dh = h;
|
rob@100
|
20022 }
|
rob@100
|
20023 }
|
rob@100
|
20024 return ! (dw <= 0 || dh <= 0);
|
rob@100
|
20025 };
|
rob@100
|
20026
|
rob@100
|
20027 var blendFuncs = {};
|
rob@100
|
20028 blendFuncs[PConstants.BLEND] = p.modes.blend;
|
rob@100
|
20029 blendFuncs[PConstants.ADD] = p.modes.add;
|
rob@100
|
20030 blendFuncs[PConstants.SUBTRACT] = p.modes.subtract;
|
rob@100
|
20031 blendFuncs[PConstants.LIGHTEST] = p.modes.lightest;
|
rob@100
|
20032 blendFuncs[PConstants.DARKEST] = p.modes.darkest;
|
rob@100
|
20033 blendFuncs[PConstants.REPLACE] = p.modes.replace;
|
rob@100
|
20034 blendFuncs[PConstants.DIFFERENCE] = p.modes.difference;
|
rob@100
|
20035 blendFuncs[PConstants.EXCLUSION] = p.modes.exclusion;
|
rob@100
|
20036 blendFuncs[PConstants.MULTIPLY] = p.modes.multiply;
|
rob@100
|
20037 blendFuncs[PConstants.SCREEN] = p.modes.screen;
|
rob@100
|
20038 blendFuncs[PConstants.OVERLAY] = p.modes.overlay;
|
rob@100
|
20039 blendFuncs[PConstants.HARD_LIGHT] = p.modes.hard_light;
|
rob@100
|
20040 blendFuncs[PConstants.SOFT_LIGHT] = p.modes.soft_light;
|
rob@100
|
20041 blendFuncs[PConstants.DODGE] = p.modes.dodge;
|
rob@100
|
20042 blendFuncs[PConstants.BURN] = p.modes.burn;
|
rob@100
|
20043
|
rob@100
|
20044 p.blit_resize = function(img, srcX1, srcY1, srcX2, srcY2, destPixels,
|
rob@100
|
20045 screenW, screenH, destX1, destY1, destX2, destY2, mode) {
|
rob@100
|
20046 var x, y;
|
rob@100
|
20047 if (srcX1 < 0) {
|
rob@100
|
20048 srcX1 = 0;
|
rob@100
|
20049 }
|
rob@100
|
20050 if (srcY1 < 0) {
|
rob@100
|
20051 srcY1 = 0;
|
rob@100
|
20052 }
|
rob@100
|
20053 if (srcX2 >= img.width) {
|
rob@100
|
20054 srcX2 = img.width - 1;
|
rob@100
|
20055 }
|
rob@100
|
20056 if (srcY2 >= img.height) {
|
rob@100
|
20057 srcY2 = img.height - 1;
|
rob@100
|
20058 }
|
rob@100
|
20059 var srcW = srcX2 - srcX1;
|
rob@100
|
20060 var srcH = srcY2 - srcY1;
|
rob@100
|
20061 var destW = destX2 - destX1;
|
rob@100
|
20062 var destH = destY2 - destY1;
|
rob@100
|
20063
|
rob@100
|
20064 if (destW <= 0 || destH <= 0 || srcW <= 0 || srcH <= 0 || destX1 >= screenW ||
|
rob@100
|
20065 destY1 >= screenH || srcX1 >= img.width || srcY1 >= img.height) {
|
rob@100
|
20066 return;
|
rob@100
|
20067 }
|
rob@100
|
20068
|
rob@100
|
20069 var dx = Math.floor(srcW / destW * PConstants.PRECISIONF);
|
rob@100
|
20070 var dy = Math.floor(srcH / destH * PConstants.PRECISIONF);
|
rob@100
|
20071
|
rob@100
|
20072 var pshared = p.shared;
|
rob@100
|
20073
|
rob@100
|
20074 pshared.srcXOffset = Math.floor(destX1 < 0 ? -destX1 * dx : srcX1 * PConstants.PRECISIONF);
|
rob@100
|
20075 pshared.srcYOffset = Math.floor(destY1 < 0 ? -destY1 * dy : srcY1 * PConstants.PRECISIONF);
|
rob@100
|
20076 if (destX1 < 0) {
|
rob@100
|
20077 destW += destX1;
|
rob@100
|
20078 destX1 = 0;
|
rob@100
|
20079 }
|
rob@100
|
20080 if (destY1 < 0) {
|
rob@100
|
20081 destH += destY1;
|
rob@100
|
20082 destY1 = 0;
|
rob@100
|
20083 }
|
rob@100
|
20084 destW = Math.min(destW, screenW - destX1);
|
rob@100
|
20085 destH = Math.min(destH, screenH - destY1);
|
rob@100
|
20086
|
rob@100
|
20087 var destOffset = destY1 * screenW + destX1;
|
rob@100
|
20088 var destColor;
|
rob@100
|
20089
|
rob@100
|
20090 pshared.srcBuffer = img.imageData.data;
|
rob@100
|
20091 pshared.iw = img.width;
|
rob@100
|
20092 pshared.iw1 = img.width - 1;
|
rob@100
|
20093 pshared.ih1 = img.height - 1;
|
rob@100
|
20094
|
rob@100
|
20095 // cache for speed
|
rob@100
|
20096 var filterBilinear = p.filter_bilinear,
|
rob@100
|
20097 filterNewScanline = p.filter_new_scanline,
|
rob@100
|
20098 blendFunc = blendFuncs[mode],
|
rob@100
|
20099 blendedColor,
|
rob@100
|
20100 idx,
|
rob@100
|
20101 cULoffset,
|
rob@100
|
20102 cURoffset,
|
rob@100
|
20103 cLLoffset,
|
rob@100
|
20104 cLRoffset,
|
rob@100
|
20105 ALPHA_MASK = PConstants.ALPHA_MASK,
|
rob@100
|
20106 RED_MASK = PConstants.RED_MASK,
|
rob@100
|
20107 GREEN_MASK = PConstants.GREEN_MASK,
|
rob@100
|
20108 BLUE_MASK = PConstants.BLUE_MASK,
|
rob@100
|
20109 PREC_MAXVAL = PConstants.PREC_MAXVAL,
|
rob@100
|
20110 PRECISIONB = PConstants.PRECISIONB,
|
rob@100
|
20111 PREC_RED_SHIFT = PConstants.PREC_RED_SHIFT,
|
rob@100
|
20112 PREC_ALPHA_SHIFT = PConstants.PREC_ALPHA_SHIFT,
|
rob@100
|
20113 srcBuffer = pshared.srcBuffer,
|
rob@100
|
20114 min = Math.min;
|
rob@100
|
20115
|
rob@100
|
20116 for (y = 0; y < destH; y++) {
|
rob@100
|
20117
|
rob@100
|
20118 pshared.sX = pshared.srcXOffset;
|
rob@100
|
20119 pshared.fracV = pshared.srcYOffset & PREC_MAXVAL;
|
rob@100
|
20120 pshared.ifV = PREC_MAXVAL - pshared.fracV;
|
rob@100
|
20121 pshared.v1 = (pshared.srcYOffset >> PRECISIONB) * pshared.iw;
|
rob@100
|
20122 pshared.v2 = min((pshared.srcYOffset >> PRECISIONB) + 1, pshared.ih1) * pshared.iw;
|
rob@100
|
20123
|
rob@100
|
20124 for (x = 0; x < destW; x++) {
|
rob@100
|
20125 idx = (destOffset + x) * 4;
|
rob@100
|
20126
|
rob@100
|
20127 destColor = (destPixels[idx + 3] << 24) &
|
rob@100
|
20128 ALPHA_MASK | (destPixels[idx] << 16) &
|
rob@100
|
20129 RED_MASK | (destPixels[idx + 1] << 8) &
|
rob@100
|
20130 GREEN_MASK | destPixels[idx + 2] & BLUE_MASK;
|
rob@100
|
20131
|
rob@100
|
20132 pshared.fracU = pshared.sX & PREC_MAXVAL;
|
rob@100
|
20133 pshared.ifU = PREC_MAXVAL - pshared.fracU;
|
rob@100
|
20134 pshared.ul = (pshared.ifU * pshared.ifV) >> PRECISIONB;
|
rob@100
|
20135 pshared.ll = (pshared.ifU * pshared.fracV) >> PRECISIONB;
|
rob@100
|
20136 pshared.ur = (pshared.fracU * pshared.ifV) >> PRECISIONB;
|
rob@100
|
20137 pshared.lr = (pshared.fracU * pshared.fracV) >> PRECISIONB;
|
rob@100
|
20138 pshared.u1 = (pshared.sX >> PRECISIONB);
|
rob@100
|
20139 pshared.u2 = min(pshared.u1 + 1, pshared.iw1);
|
rob@100
|
20140
|
rob@100
|
20141 cULoffset = (pshared.v1 + pshared.u1) * 4;
|
rob@100
|
20142 cURoffset = (pshared.v1 + pshared.u2) * 4;
|
rob@100
|
20143 cLLoffset = (pshared.v2 + pshared.u1) * 4;
|
rob@100
|
20144 cLRoffset = (pshared.v2 + pshared.u2) * 4;
|
rob@100
|
20145
|
rob@100
|
20146 pshared.cUL = (srcBuffer[cULoffset + 3] << 24) &
|
rob@100
|
20147 ALPHA_MASK | (srcBuffer[cULoffset] << 16) &
|
rob@100
|
20148 RED_MASK | (srcBuffer[cULoffset + 1] << 8) &
|
rob@100
|
20149 GREEN_MASK | srcBuffer[cULoffset + 2] & BLUE_MASK;
|
rob@100
|
20150
|
rob@100
|
20151 pshared.cUR = (srcBuffer[cURoffset + 3] << 24) &
|
rob@100
|
20152 ALPHA_MASK | (srcBuffer[cURoffset] << 16) &
|
rob@100
|
20153 RED_MASK | (srcBuffer[cURoffset + 1] << 8) &
|
rob@100
|
20154 GREEN_MASK | srcBuffer[cURoffset + 2] & BLUE_MASK;
|
rob@100
|
20155
|
rob@100
|
20156 pshared.cLL = (srcBuffer[cLLoffset + 3] << 24) &
|
rob@100
|
20157 ALPHA_MASK | (srcBuffer[cLLoffset] << 16) &
|
rob@100
|
20158 RED_MASK | (srcBuffer[cLLoffset + 1] << 8) &
|
rob@100
|
20159 GREEN_MASK | srcBuffer[cLLoffset + 2] & BLUE_MASK;
|
rob@100
|
20160
|
rob@100
|
20161 pshared.cLR = (srcBuffer[cLRoffset + 3] << 24) &
|
rob@100
|
20162 ALPHA_MASK | (srcBuffer[cLRoffset] << 16) &
|
rob@100
|
20163 RED_MASK | (srcBuffer[cLRoffset + 1] << 8) &
|
rob@100
|
20164 GREEN_MASK | srcBuffer[cLRoffset + 2] & BLUE_MASK;
|
rob@100
|
20165
|
rob@100
|
20166 pshared.r = ((pshared.ul * ((pshared.cUL & RED_MASK) >> 16) +
|
rob@100
|
20167 pshared.ll * ((pshared.cLL & RED_MASK) >> 16) +
|
rob@100
|
20168 pshared.ur * ((pshared.cUR & RED_MASK) >> 16) +
|
rob@100
|
20169 pshared.lr * ((pshared.cLR & RED_MASK) >> 16)) << PREC_RED_SHIFT) & RED_MASK;
|
rob@100
|
20170 pshared.g = ((pshared.ul * (pshared.cUL & GREEN_MASK) +
|
rob@100
|
20171 pshared.ll * (pshared.cLL & GREEN_MASK) +
|
rob@100
|
20172 pshared.ur * (pshared.cUR & GREEN_MASK) +
|
rob@100
|
20173 pshared.lr * (pshared.cLR & GREEN_MASK)) >>> PRECISIONB) & GREEN_MASK;
|
rob@100
|
20174 pshared.b = (pshared.ul * (pshared.cUL & BLUE_MASK) +
|
rob@100
|
20175 pshared.ll * (pshared.cLL & BLUE_MASK) +
|
rob@100
|
20176 pshared.ur * (pshared.cUR & BLUE_MASK) +
|
rob@100
|
20177 pshared.lr * (pshared.cLR & BLUE_MASK)) >>> PRECISIONB;
|
rob@100
|
20178 pshared.a = ((pshared.ul * ((pshared.cUL & ALPHA_MASK) >>> 24) +
|
rob@100
|
20179 pshared.ll * ((pshared.cLL & ALPHA_MASK) >>> 24) +
|
rob@100
|
20180 pshared.ur * ((pshared.cUR & ALPHA_MASK) >>> 24) +
|
rob@100
|
20181 pshared.lr * ((pshared.cLR & ALPHA_MASK) >>> 24)) << PREC_ALPHA_SHIFT) & ALPHA_MASK;
|
rob@100
|
20182
|
rob@100
|
20183 blendedColor = blendFunc(destColor, (pshared.a | pshared.r | pshared.g | pshared.b));
|
rob@100
|
20184
|
rob@100
|
20185 destPixels[idx] = (blendedColor & RED_MASK) >>> 16;
|
rob@100
|
20186 destPixels[idx + 1] = (blendedColor & GREEN_MASK) >>> 8;
|
rob@100
|
20187 destPixels[idx + 2] = (blendedColor & BLUE_MASK);
|
rob@100
|
20188 destPixels[idx + 3] = (blendedColor & ALPHA_MASK) >>> 24;
|
rob@100
|
20189
|
rob@100
|
20190 pshared.sX += dx;
|
rob@100
|
20191 }
|
rob@100
|
20192 destOffset += screenW;
|
rob@100
|
20193 pshared.srcYOffset += dy;
|
rob@100
|
20194 }
|
rob@100
|
20195 };
|
rob@100
|
20196
|
rob@100
|
20197 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
20198 // Font handling
|
rob@100
|
20199 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
20200
|
rob@100
|
20201 /**
|
rob@100
|
20202 * loadFont() Loads a font into a variable of type PFont.
|
rob@100
|
20203 *
|
rob@100
|
20204 * @param {String} name filename of the font to load
|
rob@100
|
20205 * @param {int|float} size option font size (used internally)
|
rob@100
|
20206 *
|
rob@100
|
20207 * @returns {PFont} new PFont object
|
rob@100
|
20208 *
|
rob@100
|
20209 * @see #PFont
|
rob@100
|
20210 * @see #textFont
|
rob@100
|
20211 * @see #text
|
rob@100
|
20212 * @see #createFont
|
rob@100
|
20213 */
|
rob@100
|
20214 p.loadFont = function(name, size) {
|
rob@100
|
20215 if (name === undef) {
|
rob@100
|
20216 throw("font name required in loadFont.");
|
rob@100
|
20217 }
|
rob@100
|
20218 if (name.indexOf(".svg") === -1) {
|
rob@100
|
20219 if (size === undef) {
|
rob@100
|
20220 size = curTextFont.size;
|
rob@100
|
20221 }
|
rob@100
|
20222 return PFont.get(name, size);
|
rob@100
|
20223 }
|
rob@100
|
20224 // If the font is a glyph, calculate by SVG table
|
rob@100
|
20225 var font = p.loadGlyphs(name);
|
rob@100
|
20226
|
rob@100
|
20227 return {
|
rob@100
|
20228 name: name,
|
rob@100
|
20229 css: '12px sans-serif',
|
rob@100
|
20230 glyph: true,
|
rob@100
|
20231 units_per_em: font.units_per_em,
|
rob@100
|
20232 horiz_adv_x: 1 / font.units_per_em * font.horiz_adv_x,
|
rob@100
|
20233 ascent: font.ascent,
|
rob@100
|
20234 descent: font.descent,
|
rob@100
|
20235 width: function(str) {
|
rob@100
|
20236 var width = 0;
|
rob@100
|
20237 var len = str.length;
|
rob@100
|
20238 for (var i = 0; i < len; i++) {
|
rob@100
|
20239 try {
|
rob@100
|
20240 width += parseFloat(p.glyphLook(p.glyphTable[name], str[i]).horiz_adv_x);
|
rob@100
|
20241 }
|
rob@100
|
20242 catch(e) {
|
rob@100
|
20243 Processing.debug(e);
|
rob@100
|
20244 }
|
rob@100
|
20245 }
|
rob@100
|
20246 return width / p.glyphTable[name].units_per_em;
|
rob@100
|
20247 }
|
rob@100
|
20248 };
|
rob@100
|
20249 };
|
rob@100
|
20250
|
rob@100
|
20251 /**
|
rob@100
|
20252 * createFont() Loads a font into a variable of type PFont.
|
rob@100
|
20253 * Smooth and charset are ignored in Processing.js.
|
rob@100
|
20254 *
|
rob@100
|
20255 * @param {String} name filename of the font to load
|
rob@100
|
20256 * @param {int|float} size font size in pixels
|
rob@100
|
20257 * @param {boolean} smooth not used in Processing.js
|
rob@100
|
20258 * @param {char[]} charset not used in Processing.js
|
rob@100
|
20259 *
|
rob@100
|
20260 * @returns {PFont} new PFont object
|
rob@100
|
20261 *
|
rob@100
|
20262 * @see #PFont
|
rob@100
|
20263 * @see #textFont
|
rob@100
|
20264 * @see #text
|
rob@100
|
20265 * @see #loadFont
|
rob@100
|
20266 */
|
rob@100
|
20267 p.createFont = function(name, size) {
|
rob@100
|
20268 // because Processing.js only deals with real fonts,
|
rob@100
|
20269 // createFont is simply a wrapper for loadFont/2
|
rob@100
|
20270 return p.loadFont(name, size);
|
rob@100
|
20271 };
|
rob@100
|
20272
|
rob@100
|
20273 /**
|
rob@100
|
20274 * textFont() Sets the current font.
|
rob@100
|
20275 *
|
rob@100
|
20276 * @param {PFont} pfont the PFont to load as current text font
|
rob@100
|
20277 * @param {int|float} size optional font size in pixels
|
rob@100
|
20278 *
|
rob@100
|
20279 * @see #createFont
|
rob@100
|
20280 * @see #loadFont
|
rob@100
|
20281 * @see #PFont
|
rob@100
|
20282 * @see #text
|
rob@100
|
20283 */
|
rob@100
|
20284 p.textFont = function(pfont, size) {
|
rob@100
|
20285 if (size !== undef) {
|
rob@100
|
20286 // If we're using an SVG glyph font, don't load from cache
|
rob@100
|
20287 if (!pfont.glyph) {
|
rob@100
|
20288 pfont = PFont.get(pfont.name, size);
|
rob@100
|
20289 }
|
rob@100
|
20290 curTextSize = size;
|
rob@100
|
20291 }
|
rob@100
|
20292 curTextFont = pfont;
|
rob@100
|
20293 curFontName = curTextFont.name;
|
rob@100
|
20294 curTextAscent = curTextFont.ascent;
|
rob@100
|
20295 curTextDescent = curTextFont.descent;
|
rob@100
|
20296 curTextLeading = curTextFont.leading;
|
rob@100
|
20297 var curContext = drawing.$ensureContext();
|
rob@100
|
20298 curContext.font = curTextFont.css;
|
rob@100
|
20299 };
|
rob@100
|
20300
|
rob@100
|
20301 /**
|
rob@100
|
20302 * textSize() Sets the current font size in pixels.
|
rob@100
|
20303 *
|
rob@100
|
20304 * @param {int|float} size font size in pixels
|
rob@100
|
20305 *
|
rob@100
|
20306 * @see #textFont
|
rob@100
|
20307 * @see #loadFont
|
rob@100
|
20308 * @see #PFont
|
rob@100
|
20309 * @see #text
|
rob@100
|
20310 */
|
rob@100
|
20311 p.textSize = function(size) {
|
rob@100
|
20312 curTextFont = PFont.get(curFontName, size);
|
rob@100
|
20313 curTextSize = size;
|
rob@100
|
20314 // recache metrics
|
rob@100
|
20315 curTextAscent = curTextFont.ascent;
|
rob@100
|
20316 curTextDescent = curTextFont.descent;
|
rob@100
|
20317 curTextLeading = curTextFont.leading;
|
rob@100
|
20318 var curContext = drawing.$ensureContext();
|
rob@100
|
20319 curContext.font = curTextFont.css;
|
rob@100
|
20320 };
|
rob@100
|
20321
|
rob@100
|
20322 /**
|
rob@100
|
20323 * textAscent() returns the maximum height a character extends above the baseline of the
|
rob@100
|
20324 * current font at its current size, in pixels.
|
rob@100
|
20325 *
|
rob@100
|
20326 * @returns {float} height of the current font above the baseline, at its current size, in pixels
|
rob@100
|
20327 *
|
rob@100
|
20328 * @see #textDescent
|
rob@100
|
20329 */
|
rob@100
|
20330 p.textAscent = function() {
|
rob@100
|
20331 return curTextAscent;
|
rob@100
|
20332 };
|
rob@100
|
20333
|
rob@100
|
20334 /**
|
rob@100
|
20335 * textDescent() returns the maximum depth a character will protrude below the baseline of
|
rob@100
|
20336 * the current font at its current size, in pixels.
|
rob@100
|
20337 *
|
rob@100
|
20338 * @returns {float} depth of the current font below the baseline, at its current size, in pixels
|
rob@100
|
20339 *
|
rob@100
|
20340 * @see #textAscent
|
rob@100
|
20341 */
|
rob@100
|
20342 p.textDescent = function() {
|
rob@100
|
20343 return curTextDescent;
|
rob@100
|
20344 };
|
rob@100
|
20345
|
rob@100
|
20346 /**
|
rob@100
|
20347 * textLeading() Sets the current font's leading, which is the distance
|
rob@100
|
20348 * from baseline to baseline over consecutive lines, with additional vertical
|
rob@100
|
20349 * spacing taking into account. Usually this value is 1.2 or 1.25 times the
|
rob@100
|
20350 * textsize, but this value can be changed to effect vertically compressed
|
rob@100
|
20351 * or stretched text.
|
rob@100
|
20352 *
|
rob@100
|
20353 * @param {int|float} the desired baseline-to-baseline size in pixels
|
rob@100
|
20354 */
|
rob@100
|
20355 p.textLeading = function(leading) {
|
rob@100
|
20356 curTextLeading = leading;
|
rob@100
|
20357 };
|
rob@100
|
20358
|
rob@100
|
20359 /**
|
rob@100
|
20360 * textAlign() Sets the current alignment for drawing text.
|
rob@100
|
20361 *
|
rob@100
|
20362 * @param {int} ALIGN Horizontal alignment, either LEFT, CENTER, or RIGHT
|
rob@100
|
20363 * @param {int} YALIGN optional vertical alignment, either TOP, BOTTOM, CENTER, or BASELINE
|
rob@100
|
20364 *
|
rob@100
|
20365 * @see #loadFont
|
rob@100
|
20366 * @see #PFont
|
rob@100
|
20367 * @see #text
|
rob@100
|
20368 */
|
rob@100
|
20369 p.textAlign = function(xalign, yalign) {
|
rob@100
|
20370 horizontalTextAlignment = xalign;
|
rob@100
|
20371 verticalTextAlignment = yalign || PConstants.BASELINE;
|
rob@100
|
20372 };
|
rob@100
|
20373
|
rob@100
|
20374 /**
|
rob@100
|
20375 * toP5String converts things with arbitrary data type into
|
rob@100
|
20376 * string values, for text rendering.
|
rob@100
|
20377 *
|
rob@100
|
20378 * @param {any} any object that can be converted into a string
|
rob@100
|
20379 *
|
rob@100
|
20380 * @return {String} the string representation of the input
|
rob@100
|
20381 */
|
rob@100
|
20382 function toP5String(obj) {
|
rob@100
|
20383 if(obj instanceof String) {
|
rob@100
|
20384 return obj;
|
rob@100
|
20385 }
|
rob@100
|
20386 if(typeof obj === 'number') {
|
rob@100
|
20387 // check if an int
|
rob@100
|
20388 if(obj === (0 | obj)) {
|
rob@100
|
20389 return obj.toString();
|
rob@100
|
20390 }
|
rob@100
|
20391 return p.nf(obj, 0, 3);
|
rob@100
|
20392 }
|
rob@100
|
20393 if(obj === null || obj === undef) {
|
rob@100
|
20394 return "";
|
rob@100
|
20395 }
|
rob@100
|
20396 return obj.toString();
|
rob@100
|
20397 }
|
rob@100
|
20398
|
rob@100
|
20399 /**
|
rob@100
|
20400 * textWidth() Calculates and returns the width of any character or text string in pixels.
|
rob@100
|
20401 *
|
rob@100
|
20402 * @param {char|String} str char or String to be measured
|
rob@100
|
20403 *
|
rob@100
|
20404 * @return {float} width of char or String in pixels
|
rob@100
|
20405 *
|
rob@100
|
20406 * @see #loadFont
|
rob@100
|
20407 * @see #PFont
|
rob@100
|
20408 * @see #text
|
rob@100
|
20409 * @see #textFont
|
rob@100
|
20410 */
|
rob@100
|
20411 Drawing2D.prototype.textWidth = function(str) {
|
rob@100
|
20412 var lines = toP5String(str).split(/\r?\n/g), width = 0;
|
rob@100
|
20413 var i, linesCount = lines.length;
|
rob@100
|
20414
|
rob@100
|
20415 curContext.font = curTextFont.css;
|
rob@100
|
20416 for (i = 0; i < linesCount; ++i) {
|
rob@100
|
20417 width = Math.max(width, curTextFont.measureTextWidth(lines[i]));
|
rob@100
|
20418 }
|
rob@100
|
20419 return width | 0;
|
rob@100
|
20420 };
|
rob@100
|
20421
|
rob@100
|
20422 Drawing3D.prototype.textWidth = function(str) {
|
rob@100
|
20423 var lines = toP5String(str).split(/\r?\n/g), width = 0;
|
rob@100
|
20424 var i, linesCount = lines.length;
|
rob@100
|
20425 if (textcanvas === undef) {
|
rob@100
|
20426 textcanvas = document.createElement("canvas");
|
rob@100
|
20427 }
|
rob@100
|
20428
|
rob@100
|
20429 var textContext = textcanvas.getContext("2d");
|
rob@100
|
20430 textContext.font = curTextFont.css;
|
rob@100
|
20431
|
rob@100
|
20432 for (i = 0; i < linesCount; ++i) {
|
rob@100
|
20433 width = Math.max(width, textContext.measureText(lines[i]).width);
|
rob@100
|
20434 }
|
rob@100
|
20435 return width | 0;
|
rob@100
|
20436 };
|
rob@100
|
20437
|
rob@100
|
20438 // A lookup table for characters that can not be referenced by Object
|
rob@100
|
20439 p.glyphLook = function(font, chr) {
|
rob@100
|
20440 try {
|
rob@100
|
20441 switch (chr) {
|
rob@100
|
20442 case "1":
|
rob@100
|
20443 return font.one;
|
rob@100
|
20444 case "2":
|
rob@100
|
20445 return font.two;
|
rob@100
|
20446 case "3":
|
rob@100
|
20447 return font.three;
|
rob@100
|
20448 case "4":
|
rob@100
|
20449 return font.four;
|
rob@100
|
20450 case "5":
|
rob@100
|
20451 return font.five;
|
rob@100
|
20452 case "6":
|
rob@100
|
20453 return font.six;
|
rob@100
|
20454 case "7":
|
rob@100
|
20455 return font.seven;
|
rob@100
|
20456 case "8":
|
rob@100
|
20457 return font.eight;
|
rob@100
|
20458 case "9":
|
rob@100
|
20459 return font.nine;
|
rob@100
|
20460 case "0":
|
rob@100
|
20461 return font.zero;
|
rob@100
|
20462 case " ":
|
rob@100
|
20463 return font.space;
|
rob@100
|
20464 case "$":
|
rob@100
|
20465 return font.dollar;
|
rob@100
|
20466 case "!":
|
rob@100
|
20467 return font.exclam;
|
rob@100
|
20468 case '"':
|
rob@100
|
20469 return font.quotedbl;
|
rob@100
|
20470 case "#":
|
rob@100
|
20471 return font.numbersign;
|
rob@100
|
20472 case "%":
|
rob@100
|
20473 return font.percent;
|
rob@100
|
20474 case "&":
|
rob@100
|
20475 return font.ampersand;
|
rob@100
|
20476 case "'":
|
rob@100
|
20477 return font.quotesingle;
|
rob@100
|
20478 case "(":
|
rob@100
|
20479 return font.parenleft;
|
rob@100
|
20480 case ")":
|
rob@100
|
20481 return font.parenright;
|
rob@100
|
20482 case "*":
|
rob@100
|
20483 return font.asterisk;
|
rob@100
|
20484 case "+":
|
rob@100
|
20485 return font.plus;
|
rob@100
|
20486 case ",":
|
rob@100
|
20487 return font.comma;
|
rob@100
|
20488 case "-":
|
rob@100
|
20489 return font.hyphen;
|
rob@100
|
20490 case ".":
|
rob@100
|
20491 return font.period;
|
rob@100
|
20492 case "/":
|
rob@100
|
20493 return font.slash;
|
rob@100
|
20494 case "_":
|
rob@100
|
20495 return font.underscore;
|
rob@100
|
20496 case ":":
|
rob@100
|
20497 return font.colon;
|
rob@100
|
20498 case ";":
|
rob@100
|
20499 return font.semicolon;
|
rob@100
|
20500 case "<":
|
rob@100
|
20501 return font.less;
|
rob@100
|
20502 case "=":
|
rob@100
|
20503 return font.equal;
|
rob@100
|
20504 case ">":
|
rob@100
|
20505 return font.greater;
|
rob@100
|
20506 case "?":
|
rob@100
|
20507 return font.question;
|
rob@100
|
20508 case "@":
|
rob@100
|
20509 return font.at;
|
rob@100
|
20510 case "[":
|
rob@100
|
20511 return font.bracketleft;
|
rob@100
|
20512 case "\\":
|
rob@100
|
20513 return font.backslash;
|
rob@100
|
20514 case "]":
|
rob@100
|
20515 return font.bracketright;
|
rob@100
|
20516 case "^":
|
rob@100
|
20517 return font.asciicircum;
|
rob@100
|
20518 case "`":
|
rob@100
|
20519 return font.grave;
|
rob@100
|
20520 case "{":
|
rob@100
|
20521 return font.braceleft;
|
rob@100
|
20522 case "|":
|
rob@100
|
20523 return font.bar;
|
rob@100
|
20524 case "}":
|
rob@100
|
20525 return font.braceright;
|
rob@100
|
20526 case "~":
|
rob@100
|
20527 return font.asciitilde;
|
rob@100
|
20528 // If the character is not 'special', access it by object reference
|
rob@100
|
20529 default:
|
rob@100
|
20530 return font[chr];
|
rob@100
|
20531 }
|
rob@100
|
20532 } catch(e) {
|
rob@100
|
20533 Processing.debug(e);
|
rob@100
|
20534 }
|
rob@100
|
20535 };
|
rob@100
|
20536
|
rob@100
|
20537 // Print some text to the Canvas
|
rob@100
|
20538 Drawing2D.prototype.text$line = function(str, x, y, z, align) {
|
rob@100
|
20539 var textWidth = 0, xOffset = 0;
|
rob@100
|
20540 // If the font is a standard Canvas font...
|
rob@100
|
20541 if (!curTextFont.glyph) {
|
rob@100
|
20542 if (str && ("fillText" in curContext)) {
|
rob@100
|
20543 if (isFillDirty) {
|
rob@100
|
20544 curContext.fillStyle = p.color.toString(currentFillColor);
|
rob@100
|
20545 isFillDirty = false;
|
rob@100
|
20546 }
|
rob@100
|
20547
|
rob@100
|
20548 // horizontal offset/alignment
|
rob@100
|
20549 if(align === PConstants.RIGHT || align === PConstants.CENTER) {
|
rob@100
|
20550 textWidth = curTextFont.measureTextWidth(str);
|
rob@100
|
20551
|
rob@100
|
20552 if(align === PConstants.RIGHT) {
|
rob@100
|
20553 xOffset = -textWidth;
|
rob@100
|
20554 } else { // if(align === PConstants.CENTER)
|
rob@100
|
20555 xOffset = -textWidth/2;
|
rob@100
|
20556 }
|
rob@100
|
20557 }
|
rob@100
|
20558
|
rob@100
|
20559 curContext.fillText(str, x+xOffset, y);
|
rob@100
|
20560 }
|
rob@100
|
20561 } else {
|
rob@100
|
20562 // If the font is a Batik SVG font...
|
rob@100
|
20563 var font = p.glyphTable[curFontName];
|
rob@100
|
20564 saveContext();
|
rob@100
|
20565 curContext.translate(x, y + curTextSize);
|
rob@100
|
20566
|
rob@100
|
20567 // horizontal offset/alignment
|
rob@100
|
20568 if(align === PConstants.RIGHT || align === PConstants.CENTER) {
|
rob@100
|
20569 textWidth = font.width(str);
|
rob@100
|
20570
|
rob@100
|
20571 if(align === PConstants.RIGHT) {
|
rob@100
|
20572 xOffset = -textWidth;
|
rob@100
|
20573 } else { // if(align === PConstants.CENTER)
|
rob@100
|
20574 xOffset = -textWidth/2;
|
rob@100
|
20575 }
|
rob@100
|
20576 }
|
rob@100
|
20577
|
rob@100
|
20578 var upem = font.units_per_em,
|
rob@100
|
20579 newScale = 1 / upem * curTextSize;
|
rob@100
|
20580
|
rob@100
|
20581 curContext.scale(newScale, newScale);
|
rob@100
|
20582
|
rob@100
|
20583 for (var i=0, len=str.length; i < len; i++) {
|
rob@100
|
20584 // Test character against glyph table
|
rob@100
|
20585 try {
|
rob@100
|
20586 p.glyphLook(font, str[i]).draw();
|
rob@100
|
20587 } catch(e) {
|
rob@100
|
20588 Processing.debug(e);
|
rob@100
|
20589 }
|
rob@100
|
20590 }
|
rob@100
|
20591 restoreContext();
|
rob@100
|
20592 }
|
rob@100
|
20593 };
|
rob@100
|
20594
|
rob@100
|
20595 Drawing3D.prototype.text$line = function(str, x, y, z, align) {
|
rob@100
|
20596 // handle case for 3d text
|
rob@100
|
20597 if (textcanvas === undef) {
|
rob@100
|
20598 textcanvas = document.createElement("canvas");
|
rob@100
|
20599 }
|
rob@100
|
20600 var oldContext = curContext;
|
rob@100
|
20601 curContext = textcanvas.getContext("2d");
|
rob@100
|
20602 curContext.font = curTextFont.css;
|
rob@100
|
20603 var textWidth = curTextFont.measureTextWidth(str);
|
rob@100
|
20604 textcanvas.width = textWidth;
|
rob@100
|
20605 textcanvas.height = curTextSize;
|
rob@100
|
20606 curContext = textcanvas.getContext("2d"); // refreshes curContext
|
rob@100
|
20607 curContext.font = curTextFont.css;
|
rob@100
|
20608 curContext.textBaseline="top";
|
rob@100
|
20609
|
rob@100
|
20610 // paint on 2D canvas
|
rob@100
|
20611 Drawing2D.prototype.text$line(str,0,0,0,PConstants.LEFT);
|
rob@100
|
20612
|
rob@100
|
20613 // use it as a texture
|
rob@100
|
20614 var aspect = textcanvas.width/textcanvas.height;
|
rob@100
|
20615 curContext = oldContext;
|
rob@100
|
20616
|
rob@100
|
20617 curContext.bindTexture(curContext.TEXTURE_2D, textTex);
|
rob@100
|
20618 curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, textcanvas);
|
rob@100
|
20619 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
|
rob@100
|
20620 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR);
|
rob@100
|
20621 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE);
|
rob@100
|
20622 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE);
|
rob@100
|
20623 // If we don't have a power of two texture, we can't mipmap it.
|
rob@100
|
20624 // curContext.generateMipmap(curContext.TEXTURE_2D);
|
rob@100
|
20625
|
rob@100
|
20626 // horizontal offset/alignment
|
rob@100
|
20627 var xOffset = 0;
|
rob@100
|
20628 if (align === PConstants.RIGHT) {
|
rob@100
|
20629 xOffset = -textWidth;
|
rob@100
|
20630 } else if(align === PConstants.CENTER) {
|
rob@100
|
20631 xOffset = -textWidth/2;
|
rob@100
|
20632 }
|
rob@100
|
20633 var model = new PMatrix3D();
|
rob@100
|
20634 var scalefactor = curTextSize * 0.5;
|
rob@100
|
20635 model.translate(x+xOffset-scalefactor/2, y-scalefactor, z);
|
rob@100
|
20636 model.scale(-aspect*scalefactor, -scalefactor, scalefactor);
|
rob@100
|
20637 model.translate(-1, -1, -1);
|
rob@100
|
20638 model.transpose();
|
rob@100
|
20639
|
rob@100
|
20640 var view = new PMatrix3D();
|
rob@100
|
20641 view.scale(1, -1, 1);
|
rob@100
|
20642 view.apply(modelView.array());
|
rob@100
|
20643 view.transpose();
|
rob@100
|
20644
|
rob@100
|
20645 curContext.useProgram(programObject2D);
|
rob@100
|
20646 vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, textBuffer);
|
rob@100
|
20647 vertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord", 2, textureBuffer);
|
rob@100
|
20648 uniformi("uSampler2d", programObject2D, "uSampler", [0]);
|
rob@100
|
20649
|
rob@100
|
20650 uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", true);
|
rob@100
|
20651
|
rob@100
|
20652 uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
|
rob@100
|
20653 uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
|
rob@100
|
20654 uniformf("uColor2d", programObject2D, "uColor", fillStyle);
|
rob@100
|
20655 curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
rob@100
|
20656 curContext.drawElements(curContext.TRIANGLES, 6, curContext.UNSIGNED_SHORT, 0);
|
rob@100
|
20657 };
|
rob@100
|
20658
|
rob@100
|
20659
|
rob@100
|
20660 /**
|
rob@100
|
20661 * unbounded text function (z is an optional argument)
|
rob@100
|
20662 */
|
rob@100
|
20663 function text$4(str, x, y, z) {
|
rob@100
|
20664 var lines, linesCount;
|
rob@100
|
20665 if(str.indexOf('\n') < 0) {
|
rob@100
|
20666 lines = [str];
|
rob@100
|
20667 linesCount = 1;
|
rob@100
|
20668 } else {
|
rob@100
|
20669 lines = str.split(/\r?\n/g);
|
rob@100
|
20670 linesCount = lines.length;
|
rob@100
|
20671 }
|
rob@100
|
20672 // handle text line-by-line
|
rob@100
|
20673
|
rob@100
|
20674 var yOffset = 0;
|
rob@100
|
20675 if(verticalTextAlignment === PConstants.TOP) {
|
rob@100
|
20676 yOffset = curTextAscent + curTextDescent;
|
rob@100
|
20677 } else if(verticalTextAlignment === PConstants.CENTER) {
|
rob@100
|
20678 yOffset = curTextAscent/2 - (linesCount-1)*curTextLeading/2;
|
rob@100
|
20679 } else if(verticalTextAlignment === PConstants.BOTTOM) {
|
rob@100
|
20680 yOffset = -(curTextDescent + (linesCount-1)*curTextLeading);
|
rob@100
|
20681 }
|
rob@100
|
20682
|
rob@100
|
20683 for(var i=0;i<linesCount;++i) {
|
rob@100
|
20684 var line = lines[i];
|
rob@100
|
20685 drawing.text$line(line, x, y + yOffset, z, horizontalTextAlignment);
|
rob@100
|
20686 yOffset += curTextLeading;
|
rob@100
|
20687 }
|
rob@100
|
20688 }
|
rob@100
|
20689
|
rob@100
|
20690
|
rob@100
|
20691 /**
|
rob@100
|
20692 * box-bounded text function (z is an optional argument)
|
rob@100
|
20693 */
|
rob@100
|
20694 function text$6(str, x, y, width, height, z) {
|
rob@100
|
20695 // 'fail' on 0-valued dimensions
|
rob@100
|
20696 if (str.length === 0 || width === 0 || height === 0) {
|
rob@100
|
20697 return;
|
rob@100
|
20698 }
|
rob@100
|
20699 // also 'fail' if the text height is larger than the bounding height
|
rob@100
|
20700 if(curTextSize > height) {
|
rob@100
|
20701 return;
|
rob@100
|
20702 }
|
rob@100
|
20703
|
rob@100
|
20704 var spaceMark = -1;
|
rob@100
|
20705 var start = 0;
|
rob@100
|
20706 var lineWidth = 0;
|
rob@100
|
20707 var drawCommands = [];
|
rob@100
|
20708
|
rob@100
|
20709 // run through text, character-by-character
|
rob@100
|
20710 for (var charPos=0, len=str.length; charPos < len; charPos++)
|
rob@100
|
20711 {
|
rob@100
|
20712 var currentChar = str[charPos];
|
rob@100
|
20713 var spaceChar = (currentChar === " ");
|
rob@100
|
20714 var letterWidth = curTextFont.measureTextWidth(currentChar);
|
rob@100
|
20715
|
rob@100
|
20716 // if we aren't looking at a newline, and the text still fits, keep processing
|
rob@100
|
20717 if (currentChar !== "\n" && (lineWidth + letterWidth <= width)) {
|
rob@100
|
20718 if (spaceChar) { spaceMark = charPos; }
|
rob@100
|
20719 lineWidth += letterWidth;
|
rob@100
|
20720 }
|
rob@100
|
20721
|
rob@100
|
20722 // if we're looking at a newline, or the text no longer fits, push the section that fit into the drawcommand list
|
rob@100
|
20723 else
|
rob@100
|
20724 {
|
rob@100
|
20725 if (spaceMark + 1 === start) {
|
rob@100
|
20726 if(charPos>0) {
|
rob@100
|
20727 // Whole line without spaces so far.
|
rob@100
|
20728 spaceMark = charPos;
|
rob@100
|
20729 } else {
|
rob@100
|
20730 // 'fail', because the line can't even fit the first character
|
rob@100
|
20731 return;
|
rob@100
|
20732 }
|
rob@100
|
20733 }
|
rob@100
|
20734
|
rob@100
|
20735 if (currentChar === "\n") {
|
rob@100
|
20736 drawCommands.push({text:str.substring(start, charPos), width: lineWidth});
|
rob@100
|
20737 start = charPos + 1;
|
rob@100
|
20738 } else {
|
rob@100
|
20739 // current is not a newline, which means the line doesn't fit in box. push text.
|
rob@100
|
20740 // In Processing 1.5.1, the space is also pushed, so we push up to spaceMark+1,
|
rob@100
|
20741 // rather than up to spaceMark, as was the case for Processing 1.5 and earlier.
|
rob@100
|
20742 drawCommands.push({text:str.substring(start, spaceMark+1), width: lineWidth});
|
rob@100
|
20743 start = spaceMark + 1;
|
rob@100
|
20744 }
|
rob@100
|
20745
|
rob@100
|
20746 // newline + return
|
rob@100
|
20747 lineWidth = 0;
|
rob@100
|
20748 charPos = start - 1;
|
rob@100
|
20749 }
|
rob@100
|
20750 }
|
rob@100
|
20751
|
rob@100
|
20752 // push the remaining text
|
rob@100
|
20753 if (start < len) {
|
rob@100
|
20754 drawCommands.push({text:str.substring(start), width: lineWidth});
|
rob@100
|
20755 }
|
rob@100
|
20756
|
rob@100
|
20757 // resolve horizontal alignment
|
rob@100
|
20758 var xOffset = 1,
|
rob@100
|
20759 yOffset = curTextAscent;
|
rob@100
|
20760 if (horizontalTextAlignment === PConstants.CENTER) {
|
rob@100
|
20761 xOffset = width/2;
|
rob@100
|
20762 } else if (horizontalTextAlignment === PConstants.RIGHT) {
|
rob@100
|
20763 xOffset = width;
|
rob@100
|
20764 }
|
rob@100
|
20765
|
rob@100
|
20766 // resolve vertical alignment
|
rob@100
|
20767 var linesCount = drawCommands.length,
|
rob@100
|
20768 visibleLines = Math.min(linesCount, Math.floor(height/curTextLeading));
|
rob@100
|
20769 if(verticalTextAlignment === PConstants.TOP) {
|
rob@100
|
20770 yOffset = curTextAscent + curTextDescent;
|
rob@100
|
20771 } else if(verticalTextAlignment === PConstants.CENTER) {
|
rob@100
|
20772 yOffset = (height/2) - curTextLeading * (visibleLines/2 - 1);
|
rob@100
|
20773 } else if(verticalTextAlignment === PConstants.BOTTOM) {
|
rob@100
|
20774 yOffset = curTextDescent + curTextLeading;
|
rob@100
|
20775 }
|
rob@100
|
20776
|
rob@100
|
20777 var command,
|
rob@100
|
20778 drawCommand,
|
rob@100
|
20779 leading;
|
rob@100
|
20780 for (command = 0; command < linesCount; command++) {
|
rob@100
|
20781 leading = command * curTextLeading;
|
rob@100
|
20782 // stop if not enough space for one more line draw
|
rob@100
|
20783 if (yOffset + leading > height - curTextDescent) {
|
rob@100
|
20784 break;
|
rob@100
|
20785 }
|
rob@100
|
20786 drawCommand = drawCommands[command];
|
rob@100
|
20787 drawing.text$line(drawCommand.text, x + xOffset, y + yOffset + leading, z, horizontalTextAlignment);
|
rob@100
|
20788 }
|
rob@100
|
20789 }
|
rob@100
|
20790
|
rob@100
|
20791 /**
|
rob@100
|
20792 * text() Draws text to the screen.
|
rob@100
|
20793 *
|
rob@100
|
20794 * @param {String|char|int|float} data the alphanumeric symbols to be displayed
|
rob@100
|
20795 * @param {int|float} x x-coordinate of text
|
rob@100
|
20796 * @param {int|float} y y-coordinate of text
|
rob@100
|
20797 * @param {int|float} z optional z-coordinate of text
|
rob@100
|
20798 * @param {String} stringdata optional letters to be displayed
|
rob@100
|
20799 * @param {int|float} width optional width of text box
|
rob@100
|
20800 * @param {int|float} height optional height of text box
|
rob@100
|
20801 *
|
rob@100
|
20802 * @see #textAlign
|
rob@100
|
20803 * @see #textMode
|
rob@100
|
20804 * @see #loadFont
|
rob@100
|
20805 * @see #PFont
|
rob@100
|
20806 * @see #textFont
|
rob@100
|
20807 */
|
rob@100
|
20808 p.text = function() {
|
rob@100
|
20809 if (textMode === PConstants.SHAPE) {
|
rob@100
|
20810 // TODO: requires beginRaw function
|
rob@100
|
20811 return;
|
rob@100
|
20812 }
|
rob@100
|
20813 if (arguments.length === 3) { // for text( str, x, y)
|
rob@100
|
20814 text$4(toP5String(arguments[0]), arguments[1], arguments[2], 0);
|
rob@100
|
20815 } else if (arguments.length === 4) { // for text( str, x, y, z)
|
rob@100
|
20816 text$4(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3]);
|
rob@100
|
20817 } else if (arguments.length === 5) { // for text( str, x, y , width, height)
|
rob@100
|
20818 text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], 0);
|
rob@100
|
20819 } else if (arguments.length === 6) { // for text( stringdata, x, y , width, height, z)
|
rob@100
|
20820 text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
|
rob@100
|
20821 }
|
rob@100
|
20822 };
|
rob@100
|
20823
|
rob@100
|
20824 /**
|
rob@100
|
20825 * Sets the way text draws to the screen. In the default configuration (the MODEL mode), it's possible to rotate,
|
rob@100
|
20826 * scale, and place letters in two and three dimensional space. <br /><br /> Changing to SCREEN mode draws letters
|
rob@100
|
20827 * directly to the front of the window and greatly increases rendering quality and speed when used with the P2D and
|
rob@100
|
20828 * P3D renderers. textMode(SCREEN) with OPENGL and JAVA2D (the default) renderers will generally be slower, though
|
rob@100
|
20829 * pixel accurate with P2D and P3D. With textMode(SCREEN), the letters draw at the actual size of the font (in pixels)
|
rob@100
|
20830 * and therefore calls to <b>textSize()</b> will not affect the size of the letters. To create a font at the size you
|
rob@100
|
20831 * desire, use the "Create font..." option in the Tools menu, or use the createFont() function. When using textMode(SCREEN),
|
rob@100
|
20832 * any z-coordinate passed to a text() command will be ignored, because your computer screen is...flat!
|
rob@100
|
20833 *
|
rob@100
|
20834 * @param {int} MODE Either MODEL, SCREEN or SHAPE (not yet supported)
|
rob@100
|
20835 *
|
rob@100
|
20836 * @see loadFont
|
rob@100
|
20837 * @see PFont
|
rob@100
|
20838 * @see text
|
rob@100
|
20839 * @see textFont
|
rob@100
|
20840 * @see createFont
|
rob@100
|
20841 */
|
rob@100
|
20842 p.textMode = function(mode){
|
rob@100
|
20843 textMode = mode;
|
rob@100
|
20844 };
|
rob@100
|
20845
|
rob@100
|
20846 // Load Batik SVG Fonts and parse to pre-def objects for quick rendering
|
rob@100
|
20847 p.loadGlyphs = function(url) {
|
rob@100
|
20848 var x, y, cx, cy, nx, ny, d, a, lastCom, lenC, horiz_adv_x, getXY = '[0-9\\-]+', path;
|
rob@100
|
20849
|
rob@100
|
20850 // Return arrays of SVG commands and coords
|
rob@100
|
20851 // get this to use p.matchAll() - will need to work around the lack of null return
|
rob@100
|
20852 var regex = function(needle, hay) {
|
rob@100
|
20853 var i = 0,
|
rob@100
|
20854 results = [],
|
rob@100
|
20855 latest, regexp = new RegExp(needle, "g");
|
rob@100
|
20856 latest = results[i] = regexp.exec(hay);
|
rob@100
|
20857 while (latest) {
|
rob@100
|
20858 i++;
|
rob@100
|
20859 latest = results[i] = regexp.exec(hay);
|
rob@100
|
20860 }
|
rob@100
|
20861 return results;
|
rob@100
|
20862 };
|
rob@100
|
20863
|
rob@100
|
20864 var buildPath = function(d) {
|
rob@100
|
20865 var c = regex("[A-Za-z][0-9\\- ]+|Z", d);
|
rob@100
|
20866 var beforePathDraw = function() {
|
rob@100
|
20867 saveContext();
|
rob@100
|
20868 return drawing.$ensureContext();
|
rob@100
|
20869 };
|
rob@100
|
20870 var afterPathDraw = function() {
|
rob@100
|
20871 executeContextFill();
|
rob@100
|
20872 executeContextStroke();
|
rob@100
|
20873 restoreContext();
|
rob@100
|
20874 };
|
rob@100
|
20875
|
rob@100
|
20876 // Begin storing path object
|
rob@100
|
20877 path = "return {draw:function(){var curContext=beforePathDraw();curContext.beginPath();";
|
rob@100
|
20878
|
rob@100
|
20879 x = 0;
|
rob@100
|
20880 y = 0;
|
rob@100
|
20881 cx = 0;
|
rob@100
|
20882 cy = 0;
|
rob@100
|
20883 nx = 0;
|
rob@100
|
20884 ny = 0;
|
rob@100
|
20885 d = 0;
|
rob@100
|
20886 a = 0;
|
rob@100
|
20887 lastCom = "";
|
rob@100
|
20888 lenC = c.length - 1;
|
rob@100
|
20889
|
rob@100
|
20890 // Loop through SVG commands translating to canvas eqivs functions in path object
|
rob@100
|
20891 for (var j = 0; j < lenC; j++) {
|
rob@100
|
20892 var com = c[j][0], xy = regex(getXY, com);
|
rob@100
|
20893
|
rob@100
|
20894 switch (com[0]) {
|
rob@100
|
20895 case "M":
|
rob@100
|
20896 //curContext.moveTo(x,-y);
|
rob@100
|
20897 x = parseFloat(xy[0][0]);
|
rob@100
|
20898 y = parseFloat(xy[1][0]);
|
rob@100
|
20899 path += "curContext.moveTo(" + x + "," + (-y) + ");";
|
rob@100
|
20900 break;
|
rob@100
|
20901
|
rob@100
|
20902 case "L":
|
rob@100
|
20903 //curContext.lineTo(x,-y);
|
rob@100
|
20904 x = parseFloat(xy[0][0]);
|
rob@100
|
20905 y = parseFloat(xy[1][0]);
|
rob@100
|
20906 path += "curContext.lineTo(" + x + "," + (-y) + ");";
|
rob@100
|
20907 break;
|
rob@100
|
20908
|
rob@100
|
20909 case "H":
|
rob@100
|
20910 //curContext.lineTo(x,-y)
|
rob@100
|
20911 x = parseFloat(xy[0][0]);
|
rob@100
|
20912 path += "curContext.lineTo(" + x + "," + (-y) + ");";
|
rob@100
|
20913 break;
|
rob@100
|
20914
|
rob@100
|
20915 case "V":
|
rob@100
|
20916 //curContext.lineTo(x,-y);
|
rob@100
|
20917 y = parseFloat(xy[0][0]);
|
rob@100
|
20918 path += "curContext.lineTo(" + x + "," + (-y) + ");";
|
rob@100
|
20919 break;
|
rob@100
|
20920
|
rob@100
|
20921 case "T":
|
rob@100
|
20922 //curContext.quadraticCurveTo(cx,-cy,nx,-ny);
|
rob@100
|
20923 nx = parseFloat(xy[0][0]);
|
rob@100
|
20924 ny = parseFloat(xy[1][0]);
|
rob@100
|
20925
|
rob@100
|
20926 if (lastCom === "Q" || lastCom === "T") {
|
rob@100
|
20927 d = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(cy - y, 2));
|
rob@100
|
20928 a = Math.PI + Math.atan2(cx - x, cy - y);
|
rob@100
|
20929 cx = x + (Math.sin(a) * (d));
|
rob@100
|
20930 cy = y + (Math.cos(a) * (d));
|
rob@100
|
20931 } else {
|
rob@100
|
20932 cx = x;
|
rob@100
|
20933 cy = y;
|
rob@100
|
20934 }
|
rob@100
|
20935
|
rob@100
|
20936 path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
|
rob@100
|
20937 x = nx;
|
rob@100
|
20938 y = ny;
|
rob@100
|
20939 break;
|
rob@100
|
20940
|
rob@100
|
20941 case "Q":
|
rob@100
|
20942 //curContext.quadraticCurveTo(cx,-cy,nx,-ny);
|
rob@100
|
20943 cx = parseFloat(xy[0][0]);
|
rob@100
|
20944 cy = parseFloat(xy[1][0]);
|
rob@100
|
20945 nx = parseFloat(xy[2][0]);
|
rob@100
|
20946 ny = parseFloat(xy[3][0]);
|
rob@100
|
20947 path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
|
rob@100
|
20948 x = nx;
|
rob@100
|
20949 y = ny;
|
rob@100
|
20950 break;
|
rob@100
|
20951
|
rob@100
|
20952 case "Z":
|
rob@100
|
20953 //curContext.closePath();
|
rob@100
|
20954 path += "curContext.closePath();";
|
rob@100
|
20955 break;
|
rob@100
|
20956 }
|
rob@100
|
20957 lastCom = com[0];
|
rob@100
|
20958 }
|
rob@100
|
20959
|
rob@100
|
20960 path += "afterPathDraw();";
|
rob@100
|
20961 path += "curContext.translate(" + horiz_adv_x + ",0);";
|
rob@100
|
20962 path += "}}";
|
rob@100
|
20963
|
rob@100
|
20964 return ((new Function("beforePathDraw", "afterPathDraw", path))(beforePathDraw, afterPathDraw));
|
rob@100
|
20965 };
|
rob@100
|
20966
|
rob@100
|
20967 // Parse SVG font-file into block of Canvas commands
|
rob@100
|
20968 var parseSVGFont = function(svg) {
|
rob@100
|
20969 // Store font attributes
|
rob@100
|
20970 var font = svg.getElementsByTagName("font");
|
rob@100
|
20971 p.glyphTable[url].horiz_adv_x = font[0].getAttribute("horiz-adv-x");
|
rob@100
|
20972
|
rob@100
|
20973 var font_face = svg.getElementsByTagName("font-face")[0];
|
rob@100
|
20974 p.glyphTable[url].units_per_em = parseFloat(font_face.getAttribute("units-per-em"));
|
rob@100
|
20975 p.glyphTable[url].ascent = parseFloat(font_face.getAttribute("ascent"));
|
rob@100
|
20976 p.glyphTable[url].descent = parseFloat(font_face.getAttribute("descent"));
|
rob@100
|
20977
|
rob@100
|
20978 var glyph = svg.getElementsByTagName("glyph"),
|
rob@100
|
20979 len = glyph.length;
|
rob@100
|
20980
|
rob@100
|
20981 // Loop through each glyph in the SVG
|
rob@100
|
20982 for (var i = 0; i < len; i++) {
|
rob@100
|
20983 // Store attributes for this glyph
|
rob@100
|
20984 var unicode = glyph[i].getAttribute("unicode");
|
rob@100
|
20985 var name = glyph[i].getAttribute("glyph-name");
|
rob@100
|
20986 horiz_adv_x = glyph[i].getAttribute("horiz-adv-x");
|
rob@100
|
20987 if (horiz_adv_x === null) {
|
rob@100
|
20988 horiz_adv_x = p.glyphTable[url].horiz_adv_x;
|
rob@100
|
20989 }
|
rob@100
|
20990 d = glyph[i].getAttribute("d");
|
rob@100
|
20991 // Split path commands in glpyh
|
rob@100
|
20992 if (d !== undef) {
|
rob@100
|
20993 path = buildPath(d);
|
rob@100
|
20994 // Store glyph data to table object
|
rob@100
|
20995 p.glyphTable[url][name] = {
|
rob@100
|
20996 name: name,
|
rob@100
|
20997 unicode: unicode,
|
rob@100
|
20998 horiz_adv_x: horiz_adv_x,
|
rob@100
|
20999 draw: path.draw
|
rob@100
|
21000 };
|
rob@100
|
21001 }
|
rob@100
|
21002 } // finished adding glyphs to table
|
rob@100
|
21003 };
|
rob@100
|
21004
|
rob@100
|
21005 // Load and parse Batik SVG font as XML into a Processing Glyph object
|
rob@100
|
21006 var loadXML = function() {
|
rob@100
|
21007 var xmlDoc;
|
rob@100
|
21008
|
rob@100
|
21009 try {
|
rob@100
|
21010 xmlDoc = document.implementation.createDocument("", "", null);
|
rob@100
|
21011 }
|
rob@100
|
21012 catch(e_fx_op) {
|
rob@100
|
21013 Processing.debug(e_fx_op.message);
|
rob@100
|
21014 return;
|
rob@100
|
21015 }
|
rob@100
|
21016
|
rob@100
|
21017 try {
|
rob@100
|
21018 xmlDoc.async = false;
|
rob@100
|
21019 xmlDoc.load(url);
|
rob@100
|
21020 parseSVGFont(xmlDoc.getElementsByTagName("svg")[0]);
|
rob@100
|
21021 }
|
rob@100
|
21022 catch(e_sf_ch) {
|
rob@100
|
21023 // Google Chrome, Safari etc.
|
rob@100
|
21024 Processing.debug(e_sf_ch);
|
rob@100
|
21025 try {
|
rob@100
|
21026 var xmlhttp = new window.XMLHttpRequest();
|
rob@100
|
21027 xmlhttp.open("GET", url, false);
|
rob@100
|
21028 xmlhttp.send(null);
|
rob@100
|
21029 parseSVGFont(xmlhttp.responseXML.documentElement);
|
rob@100
|
21030 }
|
rob@100
|
21031 catch(e) {
|
rob@100
|
21032 Processing.debug(e_sf_ch);
|
rob@100
|
21033 }
|
rob@100
|
21034 }
|
rob@100
|
21035 };
|
rob@100
|
21036
|
rob@100
|
21037 // Create a new object in glyphTable to store this font
|
rob@100
|
21038 p.glyphTable[url] = {};
|
rob@100
|
21039
|
rob@100
|
21040 // Begin loading the Batik SVG font...
|
rob@100
|
21041 loadXML(url);
|
rob@100
|
21042
|
rob@100
|
21043 // Return the loaded font for attribute grabbing
|
rob@100
|
21044 return p.glyphTable[url];
|
rob@100
|
21045 };
|
rob@100
|
21046
|
rob@100
|
21047 /**
|
rob@100
|
21048 * Gets the sketch parameter value. The parameter can be defined as the canvas attribute with
|
rob@100
|
21049 * the "data-processing-" prefix or provided in the pjs directive (e.g. param-test="52").
|
rob@100
|
21050 * The function tries the canvas attributes, then the pjs directive content.
|
rob@100
|
21051 *
|
rob@100
|
21052 * @param {String} name The name of the param to read.
|
rob@100
|
21053 *
|
rob@100
|
21054 * @returns {String} The parameter value, or null if parameter is not defined.
|
rob@100
|
21055 */
|
rob@100
|
21056 p.param = function(name) {
|
rob@100
|
21057 // trying attribute that was specified in CANVAS
|
rob@100
|
21058 var attributeName = "data-processing-" + name;
|
rob@100
|
21059 if (curElement.hasAttribute(attributeName)) {
|
rob@100
|
21060 return curElement.getAttribute(attributeName);
|
rob@100
|
21061 }
|
rob@100
|
21062 // trying child PARAM elements of the CANVAS
|
rob@100
|
21063 for (var i = 0, len = curElement.childNodes.length; i < len; ++i) {
|
rob@100
|
21064 var item = curElement.childNodes.item(i);
|
rob@100
|
21065 if (item.nodeType !== 1 || item.tagName.toLowerCase() !== "param") {
|
rob@100
|
21066 continue;
|
rob@100
|
21067 }
|
rob@100
|
21068 if (item.getAttribute("name") === name) {
|
rob@100
|
21069 return item.getAttribute("value");
|
rob@100
|
21070 }
|
rob@100
|
21071 }
|
rob@100
|
21072 // fallback to default params
|
rob@100
|
21073 if (curSketch.params.hasOwnProperty(name)) {
|
rob@100
|
21074 return curSketch.params[name];
|
rob@100
|
21075 }
|
rob@100
|
21076 return null;
|
rob@100
|
21077 };
|
rob@100
|
21078
|
rob@100
|
21079 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
21080 // 2D/3D methods wiring utils
|
rob@100
|
21081 ////////////////////////////////////////////////////////////////////////////
|
rob@100
|
21082 function wireDimensionalFunctions(mode) {
|
rob@100
|
21083 // Drawing2D/Drawing3D
|
rob@100
|
21084 if (mode === '3D') {
|
rob@100
|
21085 drawing = new Drawing3D();
|
rob@100
|
21086 } else if (mode === '2D') {
|
rob@100
|
21087 drawing = new Drawing2D();
|
rob@100
|
21088 } else {
|
rob@100
|
21089 drawing = new DrawingPre();
|
rob@100
|
21090 }
|
rob@100
|
21091
|
rob@100
|
21092 // Wire up functions (Use DrawingPre properties names)
|
rob@100
|
21093 for (var i in DrawingPre.prototype) {
|
rob@100
|
21094 if (DrawingPre.prototype.hasOwnProperty(i) && i.indexOf("$") < 0) {
|
rob@100
|
21095 p[i] = drawing[i];
|
rob@100
|
21096 }
|
rob@100
|
21097 }
|
rob@100
|
21098
|
rob@100
|
21099 // Run initialization
|
rob@100
|
21100 drawing.$init();
|
rob@100
|
21101 }
|
rob@100
|
21102
|
rob@100
|
21103 function createDrawingPreFunction(name) {
|
rob@100
|
21104 return function() {
|
rob@100
|
21105 wireDimensionalFunctions("2D");
|
rob@100
|
21106 return drawing[name].apply(this, arguments);
|
rob@100
|
21107 };
|
rob@100
|
21108 }
|
rob@100
|
21109 DrawingPre.prototype.translate = createDrawingPreFunction("translate");
|
rob@100
|
21110 DrawingPre.prototype.transform = createDrawingPreFunction("transform");
|
rob@100
|
21111 DrawingPre.prototype.scale = createDrawingPreFunction("scale");
|
rob@100
|
21112 DrawingPre.prototype.pushMatrix = createDrawingPreFunction("pushMatrix");
|
rob@100
|
21113 DrawingPre.prototype.popMatrix = createDrawingPreFunction("popMatrix");
|
rob@100
|
21114 DrawingPre.prototype.resetMatrix = createDrawingPreFunction("resetMatrix");
|
rob@100
|
21115 DrawingPre.prototype.applyMatrix = createDrawingPreFunction("applyMatrix");
|
rob@100
|
21116 DrawingPre.prototype.rotate = createDrawingPreFunction("rotate");
|
rob@100
|
21117 DrawingPre.prototype.rotateZ = createDrawingPreFunction("rotateZ");
|
rob@100
|
21118 DrawingPre.prototype.shearX = createDrawingPreFunction("shearX");
|
rob@100
|
21119 DrawingPre.prototype.shearY = createDrawingPreFunction("shearY");
|
rob@100
|
21120 DrawingPre.prototype.redraw = createDrawingPreFunction("redraw");
|
rob@100
|
21121 DrawingPre.prototype.toImageData = createDrawingPreFunction("toImageData");
|
rob@100
|
21122 DrawingPre.prototype.ambientLight = createDrawingPreFunction("ambientLight");
|
rob@100
|
21123 DrawingPre.prototype.directionalLight = createDrawingPreFunction("directionalLight");
|
rob@100
|
21124 DrawingPre.prototype.lightFalloff = createDrawingPreFunction("lightFalloff");
|
rob@100
|
21125 DrawingPre.prototype.lightSpecular = createDrawingPreFunction("lightSpecular");
|
rob@100
|
21126 DrawingPre.prototype.pointLight = createDrawingPreFunction("pointLight");
|
rob@100
|
21127 DrawingPre.prototype.noLights = createDrawingPreFunction("noLights");
|
rob@100
|
21128 DrawingPre.prototype.spotLight = createDrawingPreFunction("spotLight");
|
rob@100
|
21129 DrawingPre.prototype.beginCamera = createDrawingPreFunction("beginCamera");
|
rob@100
|
21130 DrawingPre.prototype.endCamera = createDrawingPreFunction("endCamera");
|
rob@100
|
21131 DrawingPre.prototype.frustum = createDrawingPreFunction("frustum");
|
rob@100
|
21132 DrawingPre.prototype.box = createDrawingPreFunction("box");
|
rob@100
|
21133 DrawingPre.prototype.sphere = createDrawingPreFunction("sphere");
|
rob@100
|
21134 DrawingPre.prototype.ambient = createDrawingPreFunction("ambient");
|
rob@100
|
21135 DrawingPre.prototype.emissive = createDrawingPreFunction("emissive");
|
rob@100
|
21136 DrawingPre.prototype.shininess = createDrawingPreFunction("shininess");
|
rob@100
|
21137 DrawingPre.prototype.specular = createDrawingPreFunction("specular");
|
rob@100
|
21138 DrawingPre.prototype.fill = createDrawingPreFunction("fill");
|
rob@100
|
21139 DrawingPre.prototype.stroke = createDrawingPreFunction("stroke");
|
rob@100
|
21140 DrawingPre.prototype.strokeWeight = createDrawingPreFunction("strokeWeight");
|
rob@100
|
21141 DrawingPre.prototype.smooth = createDrawingPreFunction("smooth");
|
rob@100
|
21142 DrawingPre.prototype.noSmooth = createDrawingPreFunction("noSmooth");
|
rob@100
|
21143 DrawingPre.prototype.point = createDrawingPreFunction("point");
|
rob@100
|
21144 DrawingPre.prototype.vertex = createDrawingPreFunction("vertex");
|
rob@100
|
21145 DrawingPre.prototype.endShape = createDrawingPreFunction("endShape");
|
rob@100
|
21146 DrawingPre.prototype.bezierVertex = createDrawingPreFunction("bezierVertex");
|
rob@100
|
21147 DrawingPre.prototype.curveVertex = createDrawingPreFunction("curveVertex");
|
rob@100
|
21148 DrawingPre.prototype.curve = createDrawingPreFunction("curve");
|
rob@100
|
21149 DrawingPre.prototype.line = createDrawingPreFunction("line");
|
rob@100
|
21150 DrawingPre.prototype.bezier = createDrawingPreFunction("bezier");
|
rob@100
|
21151 DrawingPre.prototype.rect = createDrawingPreFunction("rect");
|
rob@100
|
21152 DrawingPre.prototype.ellipse = createDrawingPreFunction("ellipse");
|
rob@100
|
21153 DrawingPre.prototype.background = createDrawingPreFunction("background");
|
rob@100
|
21154 DrawingPre.prototype.image = createDrawingPreFunction("image");
|
rob@100
|
21155 DrawingPre.prototype.textWidth = createDrawingPreFunction("textWidth");
|
rob@100
|
21156 DrawingPre.prototype.text$line = createDrawingPreFunction("text$line");
|
rob@100
|
21157 DrawingPre.prototype.$ensureContext = createDrawingPreFunction("$ensureContext");
|
rob@100
|
21158 DrawingPre.prototype.$newPMatrix = createDrawingPreFunction("$newPMatrix");
|
rob@100
|
21159
|
rob@100
|
21160 DrawingPre.prototype.size = function(aWidth, aHeight, aMode) {
|
rob@100
|
21161 wireDimensionalFunctions(aMode === PConstants.WEBGL ? "3D" : "2D");
|
rob@100
|
21162 p.size(aWidth, aHeight, aMode);
|
rob@100
|
21163 };
|
rob@100
|
21164
|
rob@100
|
21165 DrawingPre.prototype.$init = noop;
|
rob@100
|
21166
|
rob@100
|
21167 Drawing2D.prototype.$init = function() {
|
rob@100
|
21168 // Setup default 2d canvas context.
|
rob@100
|
21169 // Moving this here removes the number of times we need to check the 3D variable
|
rob@100
|
21170 p.size(p.width, p.height);
|
rob@100
|
21171
|
rob@100
|
21172 curContext.lineCap = 'round';
|
rob@100
|
21173
|
rob@100
|
21174 // Set default stroke and fill color
|
rob@100
|
21175 p.noSmooth();
|
rob@100
|
21176 p.disableContextMenu();
|
rob@100
|
21177 };
|
rob@100
|
21178 Drawing3D.prototype.$init = function() {
|
rob@100
|
21179 // For ref/perf test compatibility until those are fixed
|
rob@100
|
21180 p.use3DContext = true;
|
rob@100
|
21181 p.disableContextMenu();
|
rob@100
|
21182 };
|
rob@100
|
21183
|
rob@100
|
21184 DrawingShared.prototype.$ensureContext = function() {
|
rob@100
|
21185 return curContext;
|
rob@100
|
21186 };
|
rob@100
|
21187
|
rob@100
|
21188 //////////////////////////////////////////////////////////////////////////
|
rob@100
|
21189 // Keyboard Events
|
rob@100
|
21190 //////////////////////////////////////////////////////////////////////////
|
rob@100
|
21191
|
rob@100
|
21192 // In order to catch key events in a canvas, it needs to be "specially focusable",
|
rob@100
|
21193 // by assigning it a tabindex. If no tabindex is specified on-page, set this to 0.
|
rob@100
|
21194 if (!curElement.getAttribute("tabindex")) {
|
rob@100
|
21195 curElement.setAttribute("tabindex", 0);
|
rob@100
|
21196 }
|
rob@100
|
21197
|
rob@100
|
21198 function getKeyCode(e) {
|
rob@100
|
21199 var code = e.which || e.keyCode;
|
rob@100
|
21200 switch (code) {
|
rob@100
|
21201 case 13: // ENTER
|
rob@100
|
21202 return 10;
|
rob@100
|
21203 case 91: // META L (Saf/Mac)
|
rob@100
|
21204 case 93: // META R (Saf/Mac)
|
rob@100
|
21205 case 224: // META (FF/Mac)
|
rob@100
|
21206 return 157;
|
rob@100
|
21207 case 57392: // CONTROL (Op/Mac)
|
rob@100
|
21208 return 17;
|
rob@100
|
21209 case 46: // DELETE
|
rob@100
|
21210 return 127;
|
rob@100
|
21211 case 45: // INSERT
|
rob@100
|
21212 return 155;
|
rob@100
|
21213 }
|
rob@100
|
21214 return code;
|
rob@100
|
21215 }
|
rob@100
|
21216
|
rob@100
|
21217 function getKeyChar(e) {
|
rob@100
|
21218 var c = e.which || e.keyCode;
|
rob@100
|
21219 var anyShiftPressed = e.shiftKey || e.ctrlKey || e.altKey || e.metaKey;
|
rob@100
|
21220 switch (c) {
|
rob@100
|
21221 case 13:
|
rob@100
|
21222 c = anyShiftPressed ? 13 : 10; // RETURN vs ENTER (Mac)
|
rob@100
|
21223 break;
|
rob@100
|
21224 case 8:
|
rob@100
|
21225 c = anyShiftPressed ? 127 : 8; // DELETE vs BACKSPACE (Mac)
|
rob@100
|
21226 break;
|
rob@100
|
21227 }
|
rob@100
|
21228 return new Char(c);
|
rob@100
|
21229 }
|
rob@100
|
21230
|
rob@100
|
21231 function suppressKeyEvent(e) {
|
rob@100
|
21232 if (typeof e.preventDefault === "function") {
|
rob@100
|
21233 e.preventDefault();
|
rob@100
|
21234 } else if (typeof e.stopPropagation === "function") {
|
rob@100
|
21235 e.stopPropagation();
|
rob@100
|
21236 }
|
rob@100
|
21237 return false;
|
rob@100
|
21238 }
|
rob@100
|
21239
|
rob@100
|
21240 function updateKeyPressed() {
|
rob@100
|
21241 var ch;
|
rob@100
|
21242 for (ch in pressedKeysMap) {
|
rob@100
|
21243 if (pressedKeysMap.hasOwnProperty(ch)) {
|
rob@100
|
21244 p.__keyPressed = true;
|
rob@100
|
21245 return;
|
rob@100
|
21246 }
|
rob@100
|
21247 }
|
rob@100
|
21248 p.__keyPressed = false;
|
rob@100
|
21249 }
|
rob@100
|
21250
|
rob@100
|
21251 function resetKeyPressed() {
|
rob@100
|
21252 p.__keyPressed = false;
|
rob@100
|
21253 pressedKeysMap = [];
|
rob@100
|
21254 lastPressedKeyCode = null;
|
rob@100
|
21255 }
|
rob@100
|
21256
|
rob@100
|
21257 function simulateKeyTyped(code, c) {
|
rob@100
|
21258 pressedKeysMap[code] = c;
|
rob@100
|
21259 lastPressedKeyCode = null;
|
rob@100
|
21260 p.key = c;
|
rob@100
|
21261 p.keyCode = code;
|
rob@100
|
21262 p.keyPressed();
|
rob@100
|
21263 p.keyCode = 0;
|
rob@100
|
21264 p.keyTyped();
|
rob@100
|
21265 updateKeyPressed();
|
rob@100
|
21266 }
|
rob@100
|
21267
|
rob@100
|
21268 function handleKeydown(e) {
|
rob@100
|
21269 var code = getKeyCode(e);
|
rob@100
|
21270 if (code === PConstants.DELETE) {
|
rob@100
|
21271 simulateKeyTyped(code, new Char(127));
|
rob@100
|
21272 return;
|
rob@100
|
21273 }
|
rob@100
|
21274 if (codedKeys.indexOf(code) < 0) {
|
rob@100
|
21275 lastPressedKeyCode = code;
|
rob@100
|
21276 return;
|
rob@100
|
21277 }
|
rob@100
|
21278 var c = new Char(PConstants.CODED);
|
rob@100
|
21279 p.key = c;
|
rob@100
|
21280 p.keyCode = code;
|
rob@100
|
21281 pressedKeysMap[code] = c;
|
rob@100
|
21282 p.keyPressed();
|
rob@100
|
21283 lastPressedKeyCode = null;
|
rob@100
|
21284 updateKeyPressed();
|
rob@100
|
21285 return suppressKeyEvent(e);
|
rob@100
|
21286 }
|
rob@100
|
21287
|
rob@100
|
21288 function handleKeypress(e) {
|
rob@100
|
21289 if (lastPressedKeyCode === null) {
|
rob@100
|
21290 return; // processed in handleKeydown
|
rob@100
|
21291 }
|
rob@100
|
21292 var code = lastPressedKeyCode, c = getKeyChar(e);
|
rob@100
|
21293 simulateKeyTyped(code, c);
|
rob@100
|
21294 return suppressKeyEvent(e);
|
rob@100
|
21295 }
|
rob@100
|
21296
|
rob@100
|
21297 function handleKeyup(e) {
|
rob@100
|
21298 var code = getKeyCode(e), c = pressedKeysMap[code];
|
rob@100
|
21299 if (c === undef) {
|
rob@100
|
21300 return; // no keyPressed event was generated.
|
rob@100
|
21301 }
|
rob@100
|
21302 p.key = c;
|
rob@100
|
21303 p.keyCode = code;
|
rob@100
|
21304 p.keyReleased();
|
rob@100
|
21305 delete pressedKeysMap[code];
|
rob@100
|
21306 updateKeyPressed();
|
rob@100
|
21307 }
|
rob@100
|
21308
|
rob@100
|
21309 // Send aCode Processing syntax to be converted to JavaScript
|
rob@100
|
21310 if (!pgraphicsMode) {
|
rob@100
|
21311 if (aCode instanceof Processing.Sketch) {
|
rob@100
|
21312 // Use sketch as is
|
rob@100
|
21313 curSketch = aCode;
|
rob@100
|
21314 } else if (typeof aCode === "function") {
|
rob@100
|
21315 // Wrap function with default sketch parameters
|
rob@100
|
21316 curSketch = new Processing.Sketch(aCode);
|
rob@100
|
21317 } else if (!aCode) {
|
rob@100
|
21318 // Empty sketch
|
rob@100
|
21319 curSketch = new Processing.Sketch(function (){});
|
rob@100
|
21320 } else {
|
rob@100
|
21321 //#if PARSER
|
rob@100
|
21322 // Compile the code
|
rob@100
|
21323 curSketch = Processing.compile(aCode);
|
rob@100
|
21324 //#else
|
rob@100
|
21325 // throw "PJS compile is not supported";
|
rob@100
|
21326 //#endif
|
rob@100
|
21327 }
|
rob@100
|
21328
|
rob@100
|
21329 // Expose internal field for diagnostics and testing
|
rob@100
|
21330 p.externals.sketch = curSketch;
|
rob@100
|
21331
|
rob@100
|
21332 wireDimensionalFunctions();
|
rob@100
|
21333
|
rob@100
|
21334 // the onfocus and onblur events are handled in two parts.
|
rob@100
|
21335 // 1) the p.focused property is handled per sketch
|
rob@100
|
21336 curElement.onfocus = function() {
|
rob@100
|
21337 p.focused = true;
|
rob@100
|
21338 };
|
rob@100
|
21339
|
rob@100
|
21340 curElement.onblur = function() {
|
rob@100
|
21341 p.focused = false;
|
rob@100
|
21342 if (!curSketch.options.globalKeyEvents) {
|
rob@100
|
21343 resetKeyPressed();
|
rob@100
|
21344 }
|
rob@100
|
21345 };
|
rob@100
|
21346
|
rob@100
|
21347 // 2) looping status is handled per page, based on the pauseOnBlur @pjs directive
|
rob@100
|
21348 if (curSketch.options.pauseOnBlur) {
|
rob@100
|
21349 attachEventHandler(window, 'focus', function() {
|
rob@100
|
21350 if (doLoop) {
|
rob@100
|
21351 p.loop();
|
rob@100
|
21352 }
|
rob@100
|
21353 });
|
rob@100
|
21354
|
rob@100
|
21355 attachEventHandler(window, 'blur', function() {
|
rob@100
|
21356 if (doLoop && loopStarted) {
|
rob@100
|
21357 p.noLoop();
|
rob@100
|
21358 doLoop = true; // make sure to keep this true after the noLoop call
|
rob@100
|
21359 }
|
rob@100
|
21360 resetKeyPressed();
|
rob@100
|
21361 });
|
rob@100
|
21362 }
|
rob@100
|
21363
|
rob@100
|
21364 // if keyboard events should be handled globally, the listeners should
|
rob@100
|
21365 // be bound to the document window, rather than to the current canvas
|
rob@100
|
21366 var keyTrigger = curSketch.options.globalKeyEvents ? window : curElement;
|
rob@100
|
21367 attachEventHandler(keyTrigger, "keydown", handleKeydown);
|
rob@100
|
21368 attachEventHandler(keyTrigger, "keypress", handleKeypress);
|
rob@100
|
21369 attachEventHandler(keyTrigger, "keyup", handleKeyup);
|
rob@100
|
21370
|
rob@100
|
21371 // Step through the libraries that were attached at doc load...
|
rob@100
|
21372 for (var i in Processing.lib) {
|
rob@100
|
21373 if (Processing.lib.hasOwnProperty(i)) {
|
rob@100
|
21374 if(Processing.lib[i].hasOwnProperty("attach")) {
|
rob@100
|
21375 // use attach function if present
|
rob@100
|
21376 Processing.lib[i].attach(p);
|
rob@100
|
21377 } else if(Processing.lib[i] instanceof Function) {
|
rob@100
|
21378 // Init the libraries in the context of this p_instance (legacy)
|
rob@100
|
21379 Processing.lib[i].call(this);
|
rob@100
|
21380 }
|
rob@100
|
21381 }
|
rob@100
|
21382 }
|
rob@100
|
21383
|
rob@100
|
21384 // sketch execute test interval, used to reschedule
|
rob@100
|
21385 // an execute when preloads have not yet finished.
|
rob@100
|
21386 var retryInterval = 100;
|
rob@100
|
21387
|
rob@100
|
21388 var executeSketch = function(processing) {
|
rob@100
|
21389 // Don't start until all specified images and fonts in the cache are preloaded
|
rob@100
|
21390 if (!(curSketch.imageCache.pending || PFont.preloading.pending(retryInterval))) {
|
rob@100
|
21391 // the opera preload cache can only be cleared once we start
|
rob@100
|
21392 if (window.opera) {
|
rob@100
|
21393 var link,
|
rob@100
|
21394 element,
|
rob@100
|
21395 operaCache=curSketch.imageCache.operaCache;
|
rob@100
|
21396 for (link in operaCache) {
|
rob@100
|
21397 if(operaCache.hasOwnProperty(link)) {
|
rob@100
|
21398 element = operaCache[link];
|
rob@100
|
21399 if (element !== null) {
|
rob@100
|
21400 document.body.removeChild(element);
|
rob@100
|
21401 }
|
rob@100
|
21402 delete(operaCache[link]);
|
rob@100
|
21403 }
|
rob@100
|
21404 }
|
rob@100
|
21405 }
|
rob@100
|
21406
|
rob@100
|
21407 curSketch.attach(processing, defaultScope);
|
rob@100
|
21408
|
rob@100
|
21409 // pass a reference to the p instance for this sketch.
|
rob@100
|
21410 curSketch.onLoad(processing);
|
rob@100
|
21411
|
rob@100
|
21412 // Run void setup()
|
rob@100
|
21413 if (processing.setup) {
|
rob@100
|
21414 processing.setup();
|
rob@100
|
21415 // if any transforms were performed in setup reset to identity matrix
|
rob@100
|
21416 // so draw loop is unpolluted
|
rob@100
|
21417 processing.resetMatrix();
|
rob@100
|
21418 curSketch.onSetup();
|
rob@100
|
21419 }
|
rob@100
|
21420
|
rob@100
|
21421 // some pixels can be cached, flushing
|
rob@100
|
21422 resetContext();
|
rob@100
|
21423
|
rob@100
|
21424 if (processing.draw) {
|
rob@100
|
21425 if (!doLoop) {
|
rob@100
|
21426 processing.redraw();
|
rob@100
|
21427 } else {
|
rob@100
|
21428 processing.loop();
|
rob@100
|
21429 }
|
rob@100
|
21430 }
|
rob@100
|
21431 } else {
|
rob@100
|
21432 window.setTimeout(function() { executeSketch(processing); }, retryInterval);
|
rob@100
|
21433 }
|
rob@100
|
21434 };
|
rob@100
|
21435
|
rob@100
|
21436 // Only store an instance of non-createGraphics instances.
|
rob@100
|
21437 addInstance(this);
|
rob@100
|
21438
|
rob@100
|
21439 // The parser adds custom methods to the processing context
|
rob@100
|
21440 // this renames p to processing so these methods will run
|
rob@100
|
21441 executeSketch(p);
|
rob@100
|
21442 } else {
|
rob@100
|
21443 // No executable sketch was specified
|
rob@100
|
21444 // or called via createGraphics
|
rob@100
|
21445 curSketch = new Processing.Sketch();
|
rob@100
|
21446
|
rob@100
|
21447 wireDimensionalFunctions();
|
rob@100
|
21448
|
rob@100
|
21449 // Hack to make PGraphics work again after splitting size()
|
rob@100
|
21450 p.size = function(w, h, render) {
|
rob@100
|
21451 if (render && render === PConstants.WEBGL) {
|
rob@100
|
21452 wireDimensionalFunctions('3D');
|
rob@100
|
21453 } else {
|
rob@100
|
21454 wireDimensionalFunctions('2D');
|
rob@100
|
21455 }
|
rob@100
|
21456
|
rob@100
|
21457 p.size(w, h, render);
|
rob@100
|
21458 };
|
rob@100
|
21459 }
|
rob@100
|
21460 };
|
rob@100
|
21461
|
rob@100
|
21462 // Place-holder for overridable debugging function
|
rob@100
|
21463 Processing.debug = (function() {
|
rob@100
|
21464 if ("console" in window) {
|
rob@100
|
21465 return function(msg) {
|
rob@100
|
21466 window.console.log('Processing.js: ' + msg);
|
rob@100
|
21467 };
|
rob@100
|
21468 }
|
rob@100
|
21469 return noop;
|
rob@100
|
21470 }());
|
rob@100
|
21471
|
rob@100
|
21472 // bind prototype
|
rob@100
|
21473 Processing.prototype = defaultScope;
|
rob@100
|
21474
|
rob@100
|
21475 /**
|
rob@100
|
21476 * instance store and lookup
|
rob@100
|
21477 */
|
rob@100
|
21478 Processing.instances = processingInstances;
|
rob@100
|
21479 Processing.getInstanceById = function(name) {
|
rob@100
|
21480 return processingInstances[processingInstanceIds[name]];
|
rob@100
|
21481 };
|
rob@100
|
21482
|
rob@100
|
21483 // Unsupported Processing File and I/O operations.
|
rob@100
|
21484 (function(Processing) {
|
rob@100
|
21485 var unsupportedP5 = ("open() createOutput() createInput() BufferedReader selectFolder() " +
|
rob@100
|
21486 "dataPath() createWriter() selectOutput() beginRecord() " +
|
rob@100
|
21487 "saveStream() endRecord() selectInput() saveBytes() createReader() " +
|
rob@100
|
21488 "beginRaw() endRaw() PrintWriter delay()").split(" "),
|
rob@100
|
21489 count = unsupportedP5.length,
|
rob@100
|
21490 prettyName,
|
rob@100
|
21491 p5Name;
|
rob@100
|
21492
|
rob@100
|
21493 function createUnsupportedFunc(n) {
|
rob@100
|
21494 return function() {
|
rob@100
|
21495 throw "Processing.js does not support " + n + ".";
|
rob@100
|
21496 };
|
rob@100
|
21497 }
|
rob@100
|
21498
|
rob@100
|
21499 while (count--) {
|
rob@100
|
21500 prettyName = unsupportedP5[count];
|
rob@100
|
21501 p5Name = prettyName.replace("()", "");
|
rob@100
|
21502 Processing[p5Name] = createUnsupportedFunc(prettyName);
|
rob@100
|
21503 }
|
rob@100
|
21504 }(defaultScope));
|
rob@100
|
21505
|
rob@100
|
21506 // we're done. Return our object.
|
rob@100
|
21507 return Processing;
|
rob@100
|
21508 };
|
rob@100
|
21509
|
rob@100
|
21510 },{}],27:[function(require,module,exports){
|
rob@100
|
21511 // Base source files
|
rob@100
|
21512 var source = {
|
rob@100
|
21513 virtEquals: require("./Helpers/virtEquals"),
|
rob@100
|
21514 virtHashCode: require("./Helpers/virtHashCode"),
|
rob@100
|
21515 ObjectIterator: require("./Helpers/ObjectIterator"),
|
rob@100
|
21516 PConstants: require("./Helpers/PConstants"),
|
rob@100
|
21517 ArrayList: require("./Objects/ArrayList"),
|
rob@100
|
21518 HashMap: require("./Objects/HashMap"),
|
rob@100
|
21519 PVector: require("./Objects/PVector"),
|
rob@100
|
21520 PFont: require("./Objects/PFont"),
|
rob@100
|
21521 Char: require("./Objects/Char"),
|
rob@100
|
21522 XMLAttribute: require("./Objects/XMLAttribute"),
|
rob@100
|
21523 XMLElement: require("./Objects/XMLElement"),
|
rob@100
|
21524 PMatrix2D: require("./Objects/PMatrix2D"),
|
rob@100
|
21525 PMatrix3D: require("./Objects/PMatrix3D"),
|
rob@100
|
21526 PShape: require("./Objects/PShape"),
|
rob@100
|
21527 colors: require("./Objects/webcolors"),
|
rob@100
|
21528 PShapeSVG: require("./Objects/PShapeSVG"),
|
rob@100
|
21529 CommonFunctions: require("./P5Functions/commonFunctions"),
|
rob@100
|
21530 defaultScope: require("./Helpers/defaultScope"),
|
rob@100
|
21531 Processing: require("./Processing"),
|
rob@100
|
21532 setupParser: require("./Parser/Parser"),
|
rob@100
|
21533 finalize: require("./Helpers/finalizeProcessing")
|
rob@100
|
21534 };
|
rob@100
|
21535
|
rob@100
|
21536 // Additional code that gets tacked onto "p" during
|
rob@100
|
21537 // instantiation of a Processing sketch.
|
rob@100
|
21538 source.extend = {
|
rob@100
|
21539 withMath: require("./P5Functions/Math.js"),
|
rob@100
|
21540 withProxyFunctions: require("./P5Functions/JavaProxyFunctions")(source.virtHashCode, source.virtEquals),
|
rob@100
|
21541 withTouch: require("./P5Functions/touchmouse"),
|
rob@100
|
21542 withCommonFunctions: source.CommonFunctions.withCommonFunctions
|
rob@100
|
21543 };
|
rob@100
|
21544
|
rob@100
|
21545 /**
|
rob@100
|
21546 * Processing.js building function
|
rob@100
|
21547 */
|
rob@100
|
21548 module.exports = function buildProcessingJS(Browser, testHarness) {
|
rob@100
|
21549 var noop = function(){},
|
rob@100
|
21550 virtEquals = source.virtEquals,
|
rob@100
|
21551 virtHashCode = source.virtHashCode,
|
rob@100
|
21552 PConstants = source.PConstants,
|
rob@100
|
21553 CommonFunctions = source.CommonFunctions,
|
rob@100
|
21554 ObjectIterator = source.ObjectIterator,
|
rob@100
|
21555 Char = source.Char,
|
rob@100
|
21556 XMLAttribute = source.XMLAttribute(),
|
rob@100
|
21557
|
rob@100
|
21558 ArrayList = source.ArrayList({
|
rob@100
|
21559 virtHashCode: virtHashCode,
|
rob@100
|
21560 virtEquals: virtEquals
|
rob@100
|
21561 }),
|
rob@100
|
21562
|
rob@100
|
21563 HashMap = source.HashMap({
|
rob@100
|
21564 virtHashCode: virtHashCode,
|
rob@100
|
21565 virtEquals: virtEquals
|
rob@100
|
21566 }),
|
rob@100
|
21567
|
rob@100
|
21568 PVector = source.PVector({
|
rob@100
|
21569 PConstants: PConstants
|
rob@100
|
21570 }),
|
rob@100
|
21571
|
rob@100
|
21572 PFont = source.PFont({
|
rob@100
|
21573 Browser: Browser,
|
rob@100
|
21574 noop: noop
|
rob@100
|
21575 }),
|
rob@100
|
21576
|
rob@100
|
21577 XMLElement = source.XMLElement({
|
rob@100
|
21578 Browser: Browser,
|
rob@100
|
21579 XMLAttribute: XMLAttribute
|
rob@100
|
21580 }),
|
rob@100
|
21581
|
rob@100
|
21582 PMatrix2D = source.PMatrix2D({
|
rob@100
|
21583 p:CommonFunctions
|
rob@100
|
21584 }),
|
rob@100
|
21585
|
rob@100
|
21586 PMatrix3D = source.PMatrix3D({
|
rob@100
|
21587 p:CommonFunctions
|
rob@100
|
21588 }),
|
rob@100
|
21589
|
rob@100
|
21590 PShape = source.PShape({
|
rob@100
|
21591 PConstants: PConstants,
|
rob@100
|
21592 PMatrix2D: PMatrix2D,
|
rob@100
|
21593 PMatrix3D: PMatrix3D
|
rob@100
|
21594 }),
|
rob@100
|
21595
|
rob@100
|
21596 PShapeSVG = source.PShapeSVG({
|
rob@100
|
21597 CommonFunctions: CommonFunctions,
|
rob@100
|
21598 PConstants: PConstants,
|
rob@100
|
21599 PShape: PShape,
|
rob@100
|
21600 XMLElement: XMLElement,
|
rob@100
|
21601 colors: source.colors
|
rob@100
|
21602 }),
|
rob@100
|
21603
|
rob@100
|
21604 defaultScope = source.defaultScope({
|
rob@100
|
21605 ArrayList: ArrayList,
|
rob@100
|
21606 HashMap: HashMap,
|
rob@100
|
21607 PVector: PVector,
|
rob@100
|
21608 PFont: PFont,
|
rob@100
|
21609 PShapeSVG: PShapeSVG,
|
rob@100
|
21610 ObjectIterator: ObjectIterator,
|
rob@100
|
21611 PConstants: PConstants,
|
rob@100
|
21612 Char: Char,
|
rob@100
|
21613 XMLElement: XMLElement,
|
rob@100
|
21614 XML: XMLElement
|
rob@100
|
21615 }),
|
rob@100
|
21616
|
rob@100
|
21617 Processing = source.Processing({
|
rob@100
|
21618 defaultScope: defaultScope,
|
rob@100
|
21619 Browser: Browser,
|
rob@100
|
21620 extend: source.extend,
|
rob@100
|
21621 noop: noop
|
rob@100
|
21622 });
|
rob@100
|
21623
|
rob@100
|
21624 // set up the Processing syntax parser
|
rob@100
|
21625 Processing = source.setupParser(Processing, {
|
rob@100
|
21626 Browser: Browser,
|
rob@100
|
21627 aFunctions: testHarness,
|
rob@100
|
21628 defaultScope: defaultScope
|
rob@100
|
21629 });
|
rob@100
|
21630
|
rob@100
|
21631 // finalise the Processing object
|
rob@100
|
21632 Processing = source.finalize(Processing, {
|
rob@100
|
21633 version: require('../package.json').version,
|
rob@100
|
21634 isDomPresent: false || Browser.isDomPresent,
|
rob@100
|
21635 window: Browser.window,
|
rob@100
|
21636 document: Browser.document,
|
rob@100
|
21637 noop: noop
|
rob@100
|
21638 });
|
rob@100
|
21639
|
rob@100
|
21640 // done.
|
rob@100
|
21641 return Processing;
|
rob@100
|
21642 };
|
rob@100
|
21643
|
rob@100
|
21644 },{"../package.json":2,"./Helpers/ObjectIterator":3,"./Helpers/PConstants":4,"./Helpers/defaultScope":5,"./Helpers/finalizeProcessing":6,"./Helpers/virtEquals":7,"./Helpers/virtHashCode":8,"./Objects/ArrayList":9,"./Objects/Char":10,"./Objects/HashMap":11,"./Objects/PFont":12,"./Objects/PMatrix2D":13,"./Objects/PMatrix3D":14,"./Objects/PShape":15,"./Objects/PShapeSVG":16,"./Objects/PVector":17,"./Objects/XMLAttribute":18,"./Objects/XMLElement":19,"./Objects/webcolors":20,"./P5Functions/JavaProxyFunctions":21,"./P5Functions/Math.js":22,"./P5Functions/commonFunctions":23,"./P5Functions/touchmouse":24,"./Parser/Parser":25,"./Processing":26}]},{},[1])
|
rob@100
|
21645 ; |