|
26
|
1 |
|
|
|
2 |
(function(window, document, Math, undef) {
|
|
|
3 |
|
|
|
4 |
var nop = function(){};
|
|
|
5 |
|
|
|
6 |
var debug = (function() {
|
|
|
7 |
if ("console" in window) {
|
|
|
8 |
return function(msg) {
|
|
|
9 |
window.console.log('Processing.js: ' + msg);
|
|
|
10 |
};
|
|
|
11 |
}
|
|
|
12 |
return nop();
|
|
|
13 |
}());
|
|
|
14 |
|
|
|
15 |
var ajax = function(url) {
|
|
|
16 |
var xhr = new XMLHttpRequest();
|
|
|
17 |
xhr.open("GET", url, false);
|
|
|
18 |
if (xhr.overrideMimeType) {
|
|
|
19 |
xhr.overrideMimeType("text/plain");
|
|
|
20 |
}
|
|
|
21 |
xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT");
|
|
|
22 |
xhr.send(null);
|
|
|
23 |
// failed request?
|
|
|
24 |
if (xhr.status !== 200 && xhr.status !== 0) { throw ("XMLHttpRequest failed, status code " + xhr.status); }
|
|
|
25 |
return xhr.responseText;
|
|
|
26 |
};
|
|
|
27 |
|
|
|
28 |
var isDOMPresent = ("document" in this) && !("fake" in this.document);
|
|
|
29 |
|
|
|
30 |
// document.head polyfill for the benefit of Firefox 3.6
|
|
|
31 |
document.head = document.head || document.getElementsByTagName('head')[0];
|
|
|
32 |
|
|
|
33 |
// Typed Arrays: fallback to WebGL arrays or Native JS arrays if unavailable
|
|
|
34 |
function setupTypedArray(name, fallback) {
|
|
|
35 |
// Check if TypedArray exists, and use if so.
|
|
|
36 |
if (name in window) {
|
|
|
37 |
return window[name];
|
|
|
38 |
}
|
|
|
39 |
|
|
|
40 |
// Check if WebGLArray exists
|
|
|
41 |
if (typeof window[fallback] === "function") {
|
|
|
42 |
return window[fallback];
|
|
|
43 |
}
|
|
|
44 |
|
|
|
45 |
// Use Native JS array
|
|
|
46 |
return function(obj) {
|
|
|
47 |
if (obj instanceof Array) {
|
|
|
48 |
return obj;
|
|
|
49 |
}
|
|
|
50 |
if (typeof obj === "number") {
|
|
|
51 |
var arr = [];
|
|
|
52 |
arr.length = obj;
|
|
|
53 |
return arr;
|
|
|
54 |
}
|
|
|
55 |
};
|
|
|
56 |
}
|
|
|
57 |
|
|
|
58 |
var Float32Array = setupTypedArray("Float32Array", "WebGLFloatArray"),
|
|
|
59 |
Int32Array = setupTypedArray("Int32Array", "WebGLIntArray"),
|
|
|
60 |
Uint16Array = setupTypedArray("Uint16Array", "WebGLUnsignedShortArray"),
|
|
|
61 |
Uint8Array = setupTypedArray("Uint8Array", "WebGLUnsignedByteArray");
|
|
|
62 |
|
|
|
63 |
/* Browsers fixes end */
|
|
|
64 |
|
|
|
65 |
/**
|
|
|
66 |
* NOTE: in releases we replace symbolic PConstants.* names with their values.
|
|
|
67 |
* Using PConstants.* in code below is fine. See tools/rewrite-pconstants.js.
|
|
|
68 |
*/
|
|
|
69 |
var PConstants = {
|
|
|
70 |
X: 0,
|
|
|
71 |
Y: 1,
|
|
|
72 |
Z: 2,
|
|
|
73 |
|
|
|
74 |
R: 3,
|
|
|
75 |
G: 4,
|
|
|
76 |
B: 5,
|
|
|
77 |
A: 6,
|
|
|
78 |
|
|
|
79 |
U: 7,
|
|
|
80 |
V: 8,
|
|
|
81 |
|
|
|
82 |
NX: 9,
|
|
|
83 |
NY: 10,
|
|
|
84 |
NZ: 11,
|
|
|
85 |
|
|
|
86 |
EDGE: 12,
|
|
|
87 |
|
|
|
88 |
// Stroke
|
|
|
89 |
SR: 13,
|
|
|
90 |
SG: 14,
|
|
|
91 |
SB: 15,
|
|
|
92 |
SA: 16,
|
|
|
93 |
|
|
|
94 |
SW: 17,
|
|
|
95 |
|
|
|
96 |
// Transformations (2D and 3D)
|
|
|
97 |
TX: 18,
|
|
|
98 |
TY: 19,
|
|
|
99 |
TZ: 20,
|
|
|
100 |
|
|
|
101 |
VX: 21,
|
|
|
102 |
VY: 22,
|
|
|
103 |
VZ: 23,
|
|
|
104 |
VW: 24,
|
|
|
105 |
|
|
|
106 |
// Material properties
|
|
|
107 |
AR: 25,
|
|
|
108 |
AG: 26,
|
|
|
109 |
AB: 27,
|
|
|
110 |
|
|
|
111 |
DR: 3,
|
|
|
112 |
DG: 4,
|
|
|
113 |
DB: 5,
|
|
|
114 |
DA: 6,
|
|
|
115 |
|
|
|
116 |
SPR: 28,
|
|
|
117 |
SPG: 29,
|
|
|
118 |
SPB: 30,
|
|
|
119 |
|
|
|
120 |
SHINE: 31,
|
|
|
121 |
|
|
|
122 |
ER: 32,
|
|
|
123 |
EG: 33,
|
|
|
124 |
EB: 34,
|
|
|
125 |
|
|
|
126 |
BEEN_LIT: 35,
|
|
|
127 |
|
|
|
128 |
VERTEX_FIELD_COUNT: 36,
|
|
|
129 |
|
|
|
130 |
// Renderers
|
|
|
131 |
P2D: 1,
|
|
|
132 |
JAVA2D: 1,
|
|
|
133 |
WEBGL: 2,
|
|
|
134 |
P3D: 2,
|
|
|
135 |
OPENGL: 2,
|
|
|
136 |
PDF: 0,
|
|
|
137 |
DXF: 0,
|
|
|
138 |
|
|
|
139 |
// Platform IDs
|
|
|
140 |
OTHER: 0,
|
|
|
141 |
WINDOWS: 1,
|
|
|
142 |
MAXOSX: 2,
|
|
|
143 |
LINUX: 3,
|
|
|
144 |
|
|
|
145 |
EPSILON: 0.0001,
|
|
|
146 |
|
|
|
147 |
MAX_FLOAT: 3.4028235e+38,
|
|
|
148 |
MIN_FLOAT: -3.4028235e+38,
|
|
|
149 |
MAX_INT: 2147483647,
|
|
|
150 |
MIN_INT: -2147483648,
|
|
|
151 |
|
|
|
152 |
PI: Math.PI,
|
|
|
153 |
TWO_PI: 2 * Math.PI,
|
|
|
154 |
HALF_PI: Math.PI / 2,
|
|
|
155 |
THIRD_PI: Math.PI / 3,
|
|
|
156 |
QUARTER_PI: Math.PI / 4,
|
|
|
157 |
|
|
|
158 |
DEG_TO_RAD: Math.PI / 180,
|
|
|
159 |
RAD_TO_DEG: 180 / Math.PI,
|
|
|
160 |
|
|
|
161 |
WHITESPACE: " \t\n\r\f\u00A0",
|
|
|
162 |
|
|
|
163 |
// Color modes
|
|
|
164 |
RGB: 1,
|
|
|
165 |
ARGB: 2,
|
|
|
166 |
HSB: 3,
|
|
|
167 |
ALPHA: 4,
|
|
|
168 |
CMYK: 5,
|
|
|
169 |
|
|
|
170 |
// Image file types
|
|
|
171 |
TIFF: 0,
|
|
|
172 |
TARGA: 1,
|
|
|
173 |
JPEG: 2,
|
|
|
174 |
GIF: 3,
|
|
|
175 |
|
|
|
176 |
// Filter/convert types
|
|
|
177 |
BLUR: 11,
|
|
|
178 |
GRAY: 12,
|
|
|
179 |
INVERT: 13,
|
|
|
180 |
OPAQUE: 14,
|
|
|
181 |
POSTERIZE: 15,
|
|
|
182 |
THRESHOLD: 16,
|
|
|
183 |
ERODE: 17,
|
|
|
184 |
DILATE: 18,
|
|
|
185 |
|
|
|
186 |
// Blend modes
|
|
|
187 |
REPLACE: 0,
|
|
|
188 |
BLEND: 1 << 0,
|
|
|
189 |
ADD: 1 << 1,
|
|
|
190 |
SUBTRACT: 1 << 2,
|
|
|
191 |
LIGHTEST: 1 << 3,
|
|
|
192 |
DARKEST: 1 << 4,
|
|
|
193 |
DIFFERENCE: 1 << 5,
|
|
|
194 |
EXCLUSION: 1 << 6,
|
|
|
195 |
MULTIPLY: 1 << 7,
|
|
|
196 |
SCREEN: 1 << 8,
|
|
|
197 |
OVERLAY: 1 << 9,
|
|
|
198 |
HARD_LIGHT: 1 << 10,
|
|
|
199 |
SOFT_LIGHT: 1 << 11,
|
|
|
200 |
DODGE: 1 << 12,
|
|
|
201 |
BURN: 1 << 13,
|
|
|
202 |
|
|
|
203 |
// Color component bit masks
|
|
|
204 |
ALPHA_MASK: 0xff000000,
|
|
|
205 |
RED_MASK: 0x00ff0000,
|
|
|
206 |
GREEN_MASK: 0x0000ff00,
|
|
|
207 |
BLUE_MASK: 0x000000ff,
|
|
|
208 |
|
|
|
209 |
// Projection matrices
|
|
|
210 |
CUSTOM: 0,
|
|
|
211 |
ORTHOGRAPHIC: 2,
|
|
|
212 |
PERSPECTIVE: 3,
|
|
|
213 |
|
|
|
214 |
// Shapes
|
|
|
215 |
POINT: 2,
|
|
|
216 |
POINTS: 2,
|
|
|
217 |
LINE: 4,
|
|
|
218 |
LINES: 4,
|
|
|
219 |
TRIANGLE: 8,
|
|
|
220 |
TRIANGLES: 9,
|
|
|
221 |
TRIANGLE_STRIP: 10,
|
|
|
222 |
TRIANGLE_FAN: 11,
|
|
|
223 |
QUAD: 16,
|
|
|
224 |
QUADS: 16,
|
|
|
225 |
QUAD_STRIP: 17,
|
|
|
226 |
POLYGON: 20,
|
|
|
227 |
PATH: 21,
|
|
|
228 |
RECT: 30,
|
|
|
229 |
ELLIPSE: 31,
|
|
|
230 |
ARC: 32,
|
|
|
231 |
SPHERE: 40,
|
|
|
232 |
BOX: 41,
|
|
|
233 |
|
|
|
234 |
GROUP: 0,
|
|
|
235 |
PRIMITIVE: 1,
|
|
|
236 |
//PATH: 21, // shared with Shape PATH
|
|
|
237 |
GEOMETRY: 3,
|
|
|
238 |
|
|
|
239 |
// Shape Vertex
|
|
|
240 |
VERTEX: 0,
|
|
|
241 |
BEZIER_VERTEX: 1,
|
|
|
242 |
CURVE_VERTEX: 2,
|
|
|
243 |
BREAK: 3,
|
|
|
244 |
CLOSESHAPE: 4,
|
|
|
245 |
|
|
|
246 |
// Shape closing modes
|
|
|
247 |
OPEN: 1,
|
|
|
248 |
CLOSE: 2,
|
|
|
249 |
|
|
|
250 |
// Shape drawing modes
|
|
|
251 |
CORNER: 0, // Draw mode convention to use (x, y) to (width, height)
|
|
|
252 |
CORNERS: 1, // Draw mode convention to use (x1, y1) to (x2, y2) coordinates
|
|
|
253 |
RADIUS: 2, // Draw mode from the center, and using the radius
|
|
|
254 |
CENTER_RADIUS: 2, // Deprecated! Use RADIUS instead
|
|
|
255 |
CENTER: 3, // Draw from the center, using second pair of values as the diameter
|
|
|
256 |
DIAMETER: 3, // Synonym for the CENTER constant. Draw from the center
|
|
|
257 |
CENTER_DIAMETER: 3, // Deprecated! Use DIAMETER instead
|
|
|
258 |
|
|
|
259 |
// Text vertical alignment modes
|
|
|
260 |
BASELINE: 0, // Default vertical alignment for text placement
|
|
|
261 |
TOP: 101, // Align text to the top
|
|
|
262 |
BOTTOM: 102, // Align text from the bottom, using the baseline
|
|
|
263 |
|
|
|
264 |
// UV Texture coordinate modes
|
|
|
265 |
NORMAL: 1,
|
|
|
266 |
NORMALIZED: 1,
|
|
|
267 |
IMAGE: 2,
|
|
|
268 |
|
|
|
269 |
// Text placement modes
|
|
|
270 |
MODEL: 4,
|
|
|
271 |
SHAPE: 5,
|
|
|
272 |
|
|
|
273 |
// Stroke modes
|
|
|
274 |
SQUARE: 'butt',
|
|
|
275 |
ROUND: 'round',
|
|
|
276 |
PROJECT: 'square',
|
|
|
277 |
MITER: 'miter',
|
|
|
278 |
BEVEL: 'bevel',
|
|
|
279 |
|
|
|
280 |
// Lighting modes
|
|
|
281 |
AMBIENT: 0,
|
|
|
282 |
DIRECTIONAL: 1,
|
|
|
283 |
//POINT: 2, Shared with Shape constant
|
|
|
284 |
SPOT: 3,
|
|
|
285 |
|
|
|
286 |
// Key constants
|
|
|
287 |
|
|
|
288 |
// Both key and keyCode will be equal to these values
|
|
|
289 |
BACKSPACE: 8,
|
|
|
290 |
TAB: 9,
|
|
|
291 |
ENTER: 10,
|
|
|
292 |
RETURN: 13,
|
|
|
293 |
ESC: 27,
|
|
|
294 |
DELETE: 127,
|
|
|
295 |
CODED: 0xffff,
|
|
|
296 |
|
|
|
297 |
// p.key will be CODED and p.keyCode will be this value
|
|
|
298 |
SHIFT: 16,
|
|
|
299 |
CONTROL: 17,
|
|
|
300 |
ALT: 18,
|
|
|
301 |
CAPSLK: 20,
|
|
|
302 |
PGUP: 33,
|
|
|
303 |
PGDN: 34,
|
|
|
304 |
END: 35,
|
|
|
305 |
HOME: 36,
|
|
|
306 |
LEFT: 37,
|
|
|
307 |
UP: 38,
|
|
|
308 |
RIGHT: 39,
|
|
|
309 |
DOWN: 40,
|
|
|
310 |
F1: 112,
|
|
|
311 |
F2: 113,
|
|
|
312 |
F3: 114,
|
|
|
313 |
F4: 115,
|
|
|
314 |
F5: 116,
|
|
|
315 |
F6: 117,
|
|
|
316 |
F7: 118,
|
|
|
317 |
F8: 119,
|
|
|
318 |
F9: 120,
|
|
|
319 |
F10: 121,
|
|
|
320 |
F11: 122,
|
|
|
321 |
F12: 123,
|
|
|
322 |
NUMLK: 144,
|
|
|
323 |
META: 157,
|
|
|
324 |
INSERT: 155,
|
|
|
325 |
|
|
|
326 |
// Cursor types
|
|
|
327 |
ARROW: 'default',
|
|
|
328 |
CROSS: 'crosshair',
|
|
|
329 |
HAND: 'pointer',
|
|
|
330 |
MOVE: 'move',
|
|
|
331 |
TEXT: 'text',
|
|
|
332 |
WAIT: 'wait',
|
|
|
333 |
NOCURSOR: "url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto",
|
|
|
334 |
|
|
|
335 |
// Hints
|
|
|
336 |
DISABLE_OPENGL_2X_SMOOTH: 1,
|
|
|
337 |
ENABLE_OPENGL_2X_SMOOTH: -1,
|
|
|
338 |
ENABLE_OPENGL_4X_SMOOTH: 2,
|
|
|
339 |
ENABLE_NATIVE_FONTS: 3,
|
|
|
340 |
DISABLE_DEPTH_TEST: 4,
|
|
|
341 |
ENABLE_DEPTH_TEST: -4,
|
|
|
342 |
ENABLE_DEPTH_SORT: 5,
|
|
|
343 |
DISABLE_DEPTH_SORT: -5,
|
|
|
344 |
DISABLE_OPENGL_ERROR_REPORT: 6,
|
|
|
345 |
ENABLE_OPENGL_ERROR_REPORT: -6,
|
|
|
346 |
ENABLE_ACCURATE_TEXTURES: 7,
|
|
|
347 |
DISABLE_ACCURATE_TEXTURES: -7,
|
|
|
348 |
HINT_COUNT: 10,
|
|
|
349 |
|
|
|
350 |
// PJS defined constants
|
|
|
351 |
SINCOS_LENGTH: 720, // every half degree
|
|
|
352 |
PRECISIONB: 15, // fixed point precision is limited to 15 bits!!
|
|
|
353 |
PRECISIONF: 1 << 15,
|
|
|
354 |
PREC_MAXVAL: (1 << 15) - 1,
|
|
|
355 |
PREC_ALPHA_SHIFT: 24 - 15,
|
|
|
356 |
PREC_RED_SHIFT: 16 - 15,
|
|
|
357 |
NORMAL_MODE_AUTO: 0,
|
|
|
358 |
NORMAL_MODE_SHAPE: 1,
|
|
|
359 |
NORMAL_MODE_VERTEX: 2,
|
|
|
360 |
MAX_LIGHTS: 8
|
|
|
361 |
};
|
|
|
362 |
|
|
|
363 |
/**
|
|
|
364 |
* Returns Java hashCode() result for the object. If the object has the "hashCode" function,
|
|
|
365 |
* it preforms the call of this function. Otherwise it uses/creates the "$id" property,
|
|
|
366 |
* which is used as the hashCode.
|
|
|
367 |
*
|
|
|
368 |
* @param {Object} obj The object.
|
|
|
369 |
* @returns {int} The object's hash code.
|
|
|
370 |
*/
|
|
|
371 |
function virtHashCode(obj) {
|
|
|
372 |
if (typeof(obj) === "string") {
|
|
|
373 |
var hash = 0;
|
|
|
374 |
for (var i = 0; i < obj.length; ++i) {
|
|
|
375 |
hash = (hash * 31 + obj.charCodeAt(i)) & 0xFFFFFFFF;
|
|
|
376 |
}
|
|
|
377 |
return hash;
|
|
|
378 |
}
|
|
|
379 |
if (typeof(obj) !== "object") {
|
|
|
380 |
return obj & 0xFFFFFFFF;
|
|
|
381 |
}
|
|
|
382 |
if (obj.hashCode instanceof Function) {
|
|
|
383 |
return obj.hashCode();
|
|
|
384 |
}
|
|
|
385 |
if (obj.$id === undef) {
|
|
|
386 |
obj.$id = ((Math.floor(Math.random() * 0x10000) - 0x8000) << 16) | Math.floor(Math.random() * 0x10000);
|
|
|
387 |
}
|
|
|
388 |
return obj.$id;
|
|
|
389 |
}
|
|
|
390 |
|
|
|
391 |
/**
|
|
|
392 |
* Returns Java equals() result for two objects. If the first object
|
|
|
393 |
* has the "equals" function, it preforms the call of this function.
|
|
|
394 |
* Otherwise the method uses the JavaScript === operator.
|
|
|
395 |
*
|
|
|
396 |
* @param {Object} obj The first object.
|
|
|
397 |
* @param {Object} other The second object.
|
|
|
398 |
*
|
|
|
399 |
* @returns {boolean} true if the objects are equal.
|
|
|
400 |
*/
|
|
|
401 |
function virtEquals(obj, other) {
|
|
|
402 |
if (obj === null || other === null) {
|
|
|
403 |
return (obj === null) && (other === null);
|
|
|
404 |
}
|
|
|
405 |
if (typeof (obj) === "string") {
|
|
|
406 |
return obj === other;
|
|
|
407 |
}
|
|
|
408 |
if (typeof(obj) !== "object") {
|
|
|
409 |
return obj === other;
|
|
|
410 |
}
|
|
|
411 |
if (obj.equals instanceof Function) {
|
|
|
412 |
return obj.equals(other);
|
|
|
413 |
}
|
|
|
414 |
return obj === other;
|
|
|
415 |
}
|
|
|
416 |
|
|
|
417 |
/**
|
|
|
418 |
* A ObjectIterator is an iterator wrapper for objects. If passed object contains
|
|
|
419 |
* the iterator method, the object instance will be replaced by the result returned by
|
|
|
420 |
* this method call. If passed object is an array, the ObjectIterator instance iterates
|
|
|
421 |
* through its items.
|
|
|
422 |
*
|
|
|
423 |
* @param {Object} obj The object to be iterated.
|
|
|
424 |
*/
|
|
|
425 |
var ObjectIterator = function(obj) {
|
|
|
426 |
if (obj.iterator instanceof Function) {
|
|
|
427 |
return obj.iterator();
|
|
|
428 |
}
|
|
|
429 |
if (obj instanceof Array) {
|
|
|
430 |
// iterate through array items
|
|
|
431 |
var index = -1;
|
|
|
432 |
this.hasNext = function() {
|
|
|
433 |
return ++index < obj.length;
|
|
|
434 |
};
|
|
|
435 |
this.next = function() {
|
|
|
436 |
return obj[index];
|
|
|
437 |
};
|
|
|
438 |
} else {
|
|
|
439 |
throw "Unable to iterate: " + obj;
|
|
|
440 |
}
|
|
|
441 |
};
|
|
|
442 |
|
|
|
443 |
/**
|
|
|
444 |
* An ArrayList stores a variable number of objects.
|
|
|
445 |
*
|
|
|
446 |
* @param {int} initialCapacity optional defines the initial capacity of the list, it's empty by default
|
|
|
447 |
*
|
|
|
448 |
* @returns {ArrayList} new ArrayList object
|
|
|
449 |
*/
|
|
|
450 |
var ArrayList = (function() {
|
|
|
451 |
function Iterator(array) {
|
|
|
452 |
var index = 0;
|
|
|
453 |
this.hasNext = function() {
|
|
|
454 |
return index < array.length;
|
|
|
455 |
};
|
|
|
456 |
|
|
|
457 |
this.next = function() {
|
|
|
458 |
return array[index++];
|
|
|
459 |
};
|
|
|
460 |
|
|
|
461 |
this.remove = function() {
|
|
|
462 |
array.splice(index, 1);
|
|
|
463 |
};
|
|
|
464 |
}
|
|
|
465 |
|
|
|
466 |
function ArrayList() {
|
|
|
467 |
var array;
|
|
|
468 |
if (arguments.length === 0) {
|
|
|
469 |
array = [];
|
|
|
470 |
} else if (arguments.length > 0 && typeof arguments[0] !== 'number') {
|
|
|
471 |
array = arguments[0].toArray();
|
|
|
472 |
} else {
|
|
|
473 |
array = [];
|
|
|
474 |
array.length = 0 | arguments[0];
|
|
|
475 |
}
|
|
|
476 |
|
|
|
477 |
/**
|
|
|
478 |
* @member ArrayList
|
|
|
479 |
* ArrayList.get() Returns the element at the specified position in this list.
|
|
|
480 |
*
|
|
|
481 |
* @param {int} i index of element to return
|
|
|
482 |
*
|
|
|
483 |
* @returns {Object} the element at the specified position in this list.
|
|
|
484 |
*/
|
|
|
485 |
this.get = function(i) {
|
|
|
486 |
return array[i];
|
|
|
487 |
};
|
|
|
488 |
/**
|
|
|
489 |
* @member ArrayList
|
|
|
490 |
* ArrayList.contains() Returns true if this list contains the specified element.
|
|
|
491 |
*
|
|
|
492 |
* @param {Object} item element whose presence in this List is to be tested.
|
|
|
493 |
*
|
|
|
494 |
* @returns {boolean} true if the specified element is present; false otherwise.
|
|
|
495 |
*/
|
|
|
496 |
this.contains = function(item) {
|
|
|
497 |
return this.indexOf(item)>-1;
|
|
|
498 |
};
|
|
|
499 |
/**
|
|
|
500 |
* @member ArrayList
|
|
|
501 |
* ArrayList.indexOf() Returns the position this element takes in the list, or -1 if the element is not found.
|
|
|
502 |
*
|
|
|
503 |
* @param {Object} item element whose position in this List is to be tested.
|
|
|
504 |
*
|
|
|
505 |
* @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.
|
|
|
506 |
*/
|
|
|
507 |
this.indexOf = function(item) {
|
|
|
508 |
for (var i = 0, len = array.length; i < len; ++i) {
|
|
|
509 |
if (virtEquals(item, array[i])) {
|
|
|
510 |
return i;
|
|
|
511 |
}
|
|
|
512 |
}
|
|
|
513 |
return -1;
|
|
|
514 |
};
|
|
|
515 |
/**
|
|
|
516 |
* @member ArrayList
|
|
|
517 |
* ArrayList.add() Adds the specified element to this list.
|
|
|
518 |
*
|
|
|
519 |
* @param {int} index optional index at which the specified element is to be inserted
|
|
|
520 |
* @param {Object} object element to be added to the list
|
|
|
521 |
*/
|
|
|
522 |
this.add = function() {
|
|
|
523 |
if (arguments.length === 1) {
|
|
|
524 |
array.push(arguments[0]); // for add(Object)
|
|
|
525 |
} else if (arguments.length === 2) {
|
|
|
526 |
var arg0 = arguments[0];
|
|
|
527 |
if (typeof arg0 === 'number') {
|
|
|
528 |
if (arg0 >= 0 && arg0 <= array.length) {
|
|
|
529 |
array.splice(arg0, 0, arguments[1]); // for add(i, Object)
|
|
|
530 |
} else {
|
|
|
531 |
throw(arg0 + " is not a valid index");
|
|
|
532 |
}
|
|
|
533 |
} else {
|
|
|
534 |
throw(typeof arg0 + " is not a number");
|
|
|
535 |
}
|
|
|
536 |
} else {
|
|
|
537 |
throw("Please use the proper number of parameters.");
|
|
|
538 |
}
|
|
|
539 |
};
|
|
|
540 |
/**
|
|
|
541 |
* @member ArrayList
|
|
|
542 |
* ArrayList.addAll(collection) appends all of the elements in the specified
|
|
|
543 |
* Collection to the end of this list, in the order that they are returned by
|
|
|
544 |
* the specified Collection's Iterator.
|
|
|
545 |
*
|
|
|
546 |
* When called as addAll(index, collection) the elements are inserted into
|
|
|
547 |
* this list at the position indicated by index.
|
|
|
548 |
*
|
|
|
549 |
* @param {index} Optional; specifies the position the colletion should be inserted at
|
|
|
550 |
* @param {collection} Any iterable object (ArrayList, HashMap.keySet(), etc.)
|
|
|
551 |
* @throws out of bounds error for negative index, or index greater than list size.
|
|
|
552 |
*/
|
|
|
553 |
this.addAll = function(arg1, arg2) {
|
|
|
554 |
// addAll(int, Collection)
|
|
|
555 |
var it;
|
|
|
556 |
if (typeof arg1 === "number") {
|
|
|
557 |
if (arg1 < 0 || arg1 > array.length) {
|
|
|
558 |
throw("Index out of bounds for addAll: " + arg1 + " greater or equal than " + array.length);
|
|
|
559 |
}
|
|
|
560 |
it = new ObjectIterator(arg2);
|
|
|
561 |
while (it.hasNext()) {
|
|
|
562 |
array.splice(arg1++, 0, it.next());
|
|
|
563 |
}
|
|
|
564 |
}
|
|
|
565 |
// addAll(Collection)
|
|
|
566 |
else {
|
|
|
567 |
it = new ObjectIterator(arg1);
|
|
|
568 |
while (it.hasNext()) {
|
|
|
569 |
array.push(it.next());
|
|
|
570 |
}
|
|
|
571 |
}
|
|
|
572 |
};
|
|
|
573 |
/**
|
|
|
574 |
* @member ArrayList
|
|
|
575 |
* ArrayList.set() Replaces the element at the specified position in this list with the specified element.
|
|
|
576 |
*
|
|
|
577 |
* @param {int} index index of element to replace
|
|
|
578 |
* @param {Object} object element to be stored at the specified position
|
|
|
579 |
*/
|
|
|
580 |
this.set = function() {
|
|
|
581 |
if (arguments.length === 2) {
|
|
|
582 |
var arg0 = arguments[0];
|
|
|
583 |
if (typeof arg0 === 'number') {
|
|
|
584 |
if (arg0 >= 0 && arg0 < array.length) {
|
|
|
585 |
array.splice(arg0, 1, arguments[1]);
|
|
|
586 |
} else {
|
|
|
587 |
throw(arg0 + " is not a valid index.");
|
|
|
588 |
}
|
|
|
589 |
} else {
|
|
|
590 |
throw(typeof arg0 + " is not a number");
|
|
|
591 |
}
|
|
|
592 |
} else {
|
|
|
593 |
throw("Please use the proper number of parameters.");
|
|
|
594 |
}
|
|
|
595 |
};
|
|
|
596 |
|
|
|
597 |
/**
|
|
|
598 |
* @member ArrayList
|
|
|
599 |
* ArrayList.size() Returns the number of elements in this list.
|
|
|
600 |
*
|
|
|
601 |
* @returns {int} the number of elements in this list
|
|
|
602 |
*/
|
|
|
603 |
this.size = function() {
|
|
|
604 |
return array.length;
|
|
|
605 |
};
|
|
|
606 |
|
|
|
607 |
/**
|
|
|
608 |
* @member ArrayList
|
|
|
609 |
* ArrayList.clear() Removes all of the elements from this list. The list will be empty after this call returns.
|
|
|
610 |
*/
|
|
|
611 |
this.clear = function() {
|
|
|
612 |
array.length = 0;
|
|
|
613 |
};
|
|
|
614 |
|
|
|
615 |
/**
|
|
|
616 |
* @member ArrayList
|
|
|
617 |
* ArrayList.remove() Removes an element either based on index, if the argument is a number, or
|
|
|
618 |
* by equality check, if the argument is an object.
|
|
|
619 |
*
|
|
|
620 |
* @param {int|Object} item either the index of the element to be removed, or the element itself.
|
|
|
621 |
*
|
|
|
622 |
* @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.
|
|
|
623 |
*/
|
|
|
624 |
this.remove = function(item) {
|
|
|
625 |
if (typeof item === 'number') {
|
|
|
626 |
return array.splice(item, 1)[0];
|
|
|
627 |
}
|
|
|
628 |
item = this.indexOf(item);
|
|
|
629 |
if (item > -1) {
|
|
|
630 |
array.splice(item, 1);
|
|
|
631 |
return true;
|
|
|
632 |
}
|
|
|
633 |
return false;
|
|
|
634 |
};
|
|
|
635 |
|
|
|
636 |
/**
|
|
|
637 |
* @member ArrayList
|
|
|
638 |
* ArrayList.isEmpty() Tests if this list has no elements.
|
|
|
639 |
*
|
|
|
640 |
* @returns {boolean} true if this list has no elements; false otherwise
|
|
|
641 |
*/
|
|
|
642 |
this.isEmpty = function() {
|
|
|
643 |
return !array.length;
|
|
|
644 |
};
|
|
|
645 |
|
|
|
646 |
/**
|
|
|
647 |
* @member ArrayList
|
|
|
648 |
* ArrayList.clone() Returns a shallow copy of this ArrayList instance. (The elements themselves are not copied.)
|
|
|
649 |
*
|
|
|
650 |
* @returns {ArrayList} a clone of this ArrayList instance
|
|
|
651 |
*/
|
|
|
652 |
this.clone = function() {
|
|
|
653 |
return new ArrayList(this);
|
|
|
654 |
};
|
|
|
655 |
|
|
|
656 |
/**
|
|
|
657 |
* @member ArrayList
|
|
|
658 |
* ArrayList.toArray() Returns an array containing all of the elements in this list in the correct order.
|
|
|
659 |
*
|
|
|
660 |
* @returns {Object[]} Returns an array containing all of the elements in this list in the correct order
|
|
|
661 |
*/
|
|
|
662 |
this.toArray = function() {
|
|
|
663 |
return array.slice(0);
|
|
|
664 |
};
|
|
|
665 |
|
|
|
666 |
this.iterator = function() {
|
|
|
667 |
return new Iterator(array);
|
|
|
668 |
};
|
|
|
669 |
}
|
|
|
670 |
|
|
|
671 |
return ArrayList;
|
|
|
672 |
}());
|
|
|
673 |
|
|
|
674 |
/**
|
|
|
675 |
* A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only
|
|
|
676 |
* instead of accessing elements with a numeric index, a String is used. (If you are familiar with
|
|
|
677 |
* associative arrays from other languages, this is the same idea.)
|
|
|
678 |
*
|
|
|
679 |
* @param {int} initialCapacity defines the initial capacity of the map, it's 16 by default
|
|
|
680 |
* @param {float} loadFactor the load factor for the map, the default is 0.75
|
|
|
681 |
* @param {Map} m gives the new HashMap the same mappings as this Map
|
|
|
682 |
*/
|
|
|
683 |
var HashMap = (function() {
|
|
|
684 |
/**
|
|
|
685 |
* @member HashMap
|
|
|
686 |
* A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only
|
|
|
687 |
* instead of accessing elements with a numeric index, a String is used. (If you are familiar with
|
|
|
688 |
* associative arrays from other languages, this is the same idea.)
|
|
|
689 |
*
|
|
|
690 |
* @param {int} initialCapacity defines the initial capacity of the map, it's 16 by default
|
|
|
691 |
* @param {float} loadFactor the load factor for the map, the default is 0.75
|
|
|
692 |
* @param {Map} m gives the new HashMap the same mappings as this Map
|
|
|
693 |
*/
|
|
|
694 |
function HashMap() {
|
|
|
695 |
if (arguments.length === 1 && arguments[0] instanceof HashMap) {
|
|
|
696 |
return arguments[0].clone();
|
|
|
697 |
}
|
|
|
698 |
|
|
|
699 |
var initialCapacity = arguments.length > 0 ? arguments[0] : 16;
|
|
|
700 |
var loadFactor = arguments.length > 1 ? arguments[1] : 0.75;
|
|
|
701 |
var buckets = [];
|
|
|
702 |
buckets.length = initialCapacity;
|
|
|
703 |
var count = 0;
|
|
|
704 |
var hashMap = this;
|
|
|
705 |
|
|
|
706 |
function getBucketIndex(key) {
|
|
|
707 |
var index = virtHashCode(key) % buckets.length;
|
|
|
708 |
return index < 0 ? buckets.length + index : index;
|
|
|
709 |
}
|
|
|
710 |
function ensureLoad() {
|
|
|
711 |
if (count <= loadFactor * buckets.length) {
|
|
|
712 |
return;
|
|
|
713 |
}
|
|
|
714 |
var allEntries = [];
|
|
|
715 |
for (var i = 0; i < buckets.length; ++i) {
|
|
|
716 |
if (buckets[i] !== undef) {
|
|
|
717 |
allEntries = allEntries.concat(buckets[i]);
|
|
|
718 |
}
|
|
|
719 |
}
|
|
|
720 |
var newBucketsLength = buckets.length * 2;
|
|
|
721 |
buckets = [];
|
|
|
722 |
buckets.length = newBucketsLength;
|
|
|
723 |
for (var j = 0; j < allEntries.length; ++j) {
|
|
|
724 |
var index = getBucketIndex(allEntries[j].key);
|
|
|
725 |
var bucket = buckets[index];
|
|
|
726 |
if (bucket === undef) {
|
|
|
727 |
buckets[index] = bucket = [];
|
|
|
728 |
}
|
|
|
729 |
bucket.push(allEntries[j]);
|
|
|
730 |
}
|
|
|
731 |
}
|
|
|
732 |
|
|
|
733 |
function Iterator(conversion, removeItem) {
|
|
|
734 |
var bucketIndex = 0;
|
|
|
735 |
var itemIndex = -1;
|
|
|
736 |
var endOfBuckets = false;
|
|
|
737 |
|
|
|
738 |
function findNext() {
|
|
|
739 |
while (!endOfBuckets) {
|
|
|
740 |
++itemIndex;
|
|
|
741 |
if (bucketIndex >= buckets.length) {
|
|
|
742 |
endOfBuckets = true;
|
|
|
743 |
} else if (buckets[bucketIndex] === undef || itemIndex >= buckets[bucketIndex].length) {
|
|
|
744 |
itemIndex = -1;
|
|
|
745 |
++bucketIndex;
|
|
|
746 |
} else {
|
|
|
747 |
return;
|
|
|
748 |
}
|
|
|
749 |
}
|
|
|
750 |
}
|
|
|
751 |
|
|
|
752 |
/*
|
|
|
753 |
* @member Iterator
|
|
|
754 |
* Checks if the Iterator has more items
|
|
|
755 |
*/
|
|
|
756 |
this.hasNext = function() {
|
|
|
757 |
return !endOfBuckets;
|
|
|
758 |
};
|
|
|
759 |
|
|
|
760 |
/*
|
|
|
761 |
* @member Iterator
|
|
|
762 |
* Return the next Item
|
|
|
763 |
*/
|
|
|
764 |
this.next = function() {
|
|
|
765 |
var result = conversion(buckets[bucketIndex][itemIndex]);
|
|
|
766 |
findNext();
|
|
|
767 |
return result;
|
|
|
768 |
};
|
|
|
769 |
|
|
|
770 |
/*
|
|
|
771 |
* @member Iterator
|
|
|
772 |
* Remove the current item
|
|
|
773 |
*/
|
|
|
774 |
this.remove = function() {
|
|
|
775 |
removeItem(this.next());
|
|
|
776 |
--itemIndex;
|
|
|
777 |
};
|
|
|
778 |
|
|
|
779 |
findNext();
|
|
|
780 |
}
|
|
|
781 |
|
|
|
782 |
function Set(conversion, isIn, removeItem) {
|
|
|
783 |
this.clear = function() {
|
|
|
784 |
hashMap.clear();
|
|
|
785 |
};
|
|
|
786 |
|
|
|
787 |
this.contains = function(o) {
|
|
|
788 |
return isIn(o);
|
|
|
789 |
};
|
|
|
790 |
|
|
|
791 |
this.containsAll = function(o) {
|
|
|
792 |
var it = o.iterator();
|
|
|
793 |
while (it.hasNext()) {
|
|
|
794 |
if (!this.contains(it.next())) {
|
|
|
795 |
return false;
|
|
|
796 |
}
|
|
|
797 |
}
|
|
|
798 |
return true;
|
|
|
799 |
};
|
|
|
800 |
|
|
|
801 |
this.isEmpty = function() {
|
|
|
802 |
return hashMap.isEmpty();
|
|
|
803 |
};
|
|
|
804 |
|
|
|
805 |
this.iterator = function() {
|
|
|
806 |
return new Iterator(conversion, removeItem);
|
|
|
807 |
};
|
|
|
808 |
|
|
|
809 |
this.remove = function(o) {
|
|
|
810 |
if (this.contains(o)) {
|
|
|
811 |
removeItem(o);
|
|
|
812 |
return true;
|
|
|
813 |
}
|
|
|
814 |
return false;
|
|
|
815 |
};
|
|
|
816 |
|
|
|
817 |
this.removeAll = function(c) {
|
|
|
818 |
var it = c.iterator();
|
|
|
819 |
var changed = false;
|
|
|
820 |
while (it.hasNext()) {
|
|
|
821 |
var item = it.next();
|
|
|
822 |
if (this.contains(item)) {
|
|
|
823 |
removeItem(item);
|
|
|
824 |
changed = true;
|
|
|
825 |
}
|
|
|
826 |
}
|
|
|
827 |
return true;
|
|
|
828 |
};
|
|
|
829 |
|
|
|
830 |
this.retainAll = function(c) {
|
|
|
831 |
var it = this.iterator();
|
|
|
832 |
var toRemove = [];
|
|
|
833 |
while (it.hasNext()) {
|
|
|
834 |
var entry = it.next();
|
|
|
835 |
if (!c.contains(entry)) {
|
|
|
836 |
toRemove.push(entry);
|
|
|
837 |
}
|
|
|
838 |
}
|
|
|
839 |
for (var i = 0; i < toRemove.length; ++i) {
|
|
|
840 |
removeItem(toRemove[i]);
|
|
|
841 |
}
|
|
|
842 |
return toRemove.length > 0;
|
|
|
843 |
};
|
|
|
844 |
|
|
|
845 |
this.size = function() {
|
|
|
846 |
return hashMap.size();
|
|
|
847 |
};
|
|
|
848 |
|
|
|
849 |
this.toArray = function() {
|
|
|
850 |
var result = [];
|
|
|
851 |
var it = this.iterator();
|
|
|
852 |
while (it.hasNext()) {
|
|
|
853 |
result.push(it.next());
|
|
|
854 |
}
|
|
|
855 |
return result;
|
|
|
856 |
};
|
|
|
857 |
}
|
|
|
858 |
|
|
|
859 |
function Entry(pair) {
|
|
|
860 |
this._isIn = function(map) {
|
|
|
861 |
return map === hashMap && (pair.removed === undef);
|
|
|
862 |
};
|
|
|
863 |
|
|
|
864 |
this.equals = function(o) {
|
|
|
865 |
return virtEquals(pair.key, o.getKey());
|
|
|
866 |
};
|
|
|
867 |
|
|
|
868 |
this.getKey = function() {
|
|
|
869 |
return pair.key;
|
|
|
870 |
};
|
|
|
871 |
|
|
|
872 |
this.getValue = function() {
|
|
|
873 |
return pair.value;
|
|
|
874 |
};
|
|
|
875 |
|
|
|
876 |
this.hashCode = function(o) {
|
|
|
877 |
return virtHashCode(pair.key);
|
|
|
878 |
};
|
|
|
879 |
|
|
|
880 |
this.setValue = function(value) {
|
|
|
881 |
var old = pair.value;
|
|
|
882 |
pair.value = value;
|
|
|
883 |
return old;
|
|
|
884 |
};
|
|
|
885 |
}
|
|
|
886 |
|
|
|
887 |
this.clear = function() {
|
|
|
888 |
count = 0;
|
|
|
889 |
buckets = [];
|
|
|
890 |
buckets.length = initialCapacity;
|
|
|
891 |
};
|
|
|
892 |
|
|
|
893 |
this.clone = function() {
|
|
|
894 |
var map = new HashMap();
|
|
|
895 |
map.putAll(this);
|
|
|
896 |
return map;
|
|
|
897 |
};
|
|
|
898 |
|
|
|
899 |
this.containsKey = function(key) {
|
|
|
900 |
var index = getBucketIndex(key);
|
|
|
901 |
var bucket = buckets[index];
|
|
|
902 |
if (bucket === undef) {
|
|
|
903 |
return false;
|
|
|
904 |
}
|
|
|
905 |
for (var i = 0; i < bucket.length; ++i) {
|
|
|
906 |
if (virtEquals(bucket[i].key, key)) {
|
|
|
907 |
return true;
|
|
|
908 |
}
|
|
|
909 |
}
|
|
|
910 |
return false;
|
|
|
911 |
};
|
|
|
912 |
|
|
|
913 |
this.containsValue = function(value) {
|
|
|
914 |
for (var i = 0; i < buckets.length; ++i) {
|
|
|
915 |
var bucket = buckets[i];
|
|
|
916 |
if (bucket === undef) {
|
|
|
917 |
continue;
|
|
|
918 |
}
|
|
|
919 |
for (var j = 0; j < bucket.length; ++j) {
|
|
|
920 |
if (virtEquals(bucket[j].value, value)) {
|
|
|
921 |
return true;
|
|
|
922 |
}
|
|
|
923 |
}
|
|
|
924 |
}
|
|
|
925 |
return false;
|
|
|
926 |
};
|
|
|
927 |
|
|
|
928 |
this.entrySet = function() {
|
|
|
929 |
return new Set(
|
|
|
930 |
|
|
|
931 |
function(pair) {
|
|
|
932 |
return new Entry(pair);
|
|
|
933 |
},
|
|
|
934 |
|
|
|
935 |
function(pair) {
|
|
|
936 |
return (pair instanceof Entry) && pair._isIn(hashMap);
|
|
|
937 |
},
|
|
|
938 |
|
|
|
939 |
function(pair) {
|
|
|
940 |
return hashMap.remove(pair.getKey());
|
|
|
941 |
});
|
|
|
942 |
};
|
|
|
943 |
|
|
|
944 |
this.get = function(key) {
|
|
|
945 |
var index = getBucketIndex(key);
|
|
|
946 |
var bucket = buckets[index];
|
|
|
947 |
if (bucket === undef) {
|
|
|
948 |
return null;
|
|
|
949 |
}
|
|
|
950 |
for (var i = 0; i < bucket.length; ++i) {
|
|
|
951 |
if (virtEquals(bucket[i].key, key)) {
|
|
|
952 |
return bucket[i].value;
|
|
|
953 |
}
|
|
|
954 |
}
|
|
|
955 |
return null;
|
|
|
956 |
};
|
|
|
957 |
|
|
|
958 |
this.isEmpty = function() {
|
|
|
959 |
return count === 0;
|
|
|
960 |
};
|
|
|
961 |
|
|
|
962 |
this.keySet = function() {
|
|
|
963 |
return new Set(
|
|
|
964 |
// get key from pair
|
|
|
965 |
function(pair) {
|
|
|
966 |
return pair.key;
|
|
|
967 |
},
|
|
|
968 |
// is-in test
|
|
|
969 |
function(key) {
|
|
|
970 |
return hashMap.containsKey(key);
|
|
|
971 |
},
|
|
|
972 |
// remove from hashmap by key
|
|
|
973 |
function(key) {
|
|
|
974 |
return hashMap.remove(key);
|
|
|
975 |
}
|
|
|
976 |
);
|
|
|
977 |
};
|
|
|
978 |
|
|
|
979 |
this.values = function() {
|
|
|
980 |
return new Set(
|
|
|
981 |
// get value from pair
|
|
|
982 |
function(pair) {
|
|
|
983 |
return pair.value;
|
|
|
984 |
},
|
|
|
985 |
// is-in test
|
|
|
986 |
function(value) {
|
|
|
987 |
return hashMap.containsValue(value);
|
|
|
988 |
},
|
|
|
989 |
// remove from hashmap by value
|
|
|
990 |
function(value) {
|
|
|
991 |
return hashMap.removeByValue(value);
|
|
|
992 |
}
|
|
|
993 |
);
|
|
|
994 |
};
|
|
|
995 |
|
|
|
996 |
this.put = function(key, value) {
|
|
|
997 |
var index = getBucketIndex(key);
|
|
|
998 |
var bucket = buckets[index];
|
|
|
999 |
if (bucket === undef) {
|
|
|
1000 |
++count;
|
|
|
1001 |
buckets[index] = [{
|
|
|
1002 |
key: key,
|
|
|
1003 |
value: value
|
|
|
1004 |
}];
|
|
|
1005 |
ensureLoad();
|
|
|
1006 |
return null;
|
|
|
1007 |
}
|
|
|
1008 |
for (var i = 0; i < bucket.length; ++i) {
|
|
|
1009 |
if (virtEquals(bucket[i].key, key)) {
|
|
|
1010 |
var previous = bucket[i].value;
|
|
|
1011 |
bucket[i].value = value;
|
|
|
1012 |
return previous;
|
|
|
1013 |
}
|
|
|
1014 |
}
|
|
|
1015 |
++count;
|
|
|
1016 |
bucket.push({
|
|
|
1017 |
key: key,
|
|
|
1018 |
value: value
|
|
|
1019 |
});
|
|
|
1020 |
ensureLoad();
|
|
|
1021 |
return null;
|
|
|
1022 |
};
|
|
|
1023 |
|
|
|
1024 |
this.putAll = function(m) {
|
|
|
1025 |
var it = m.entrySet().iterator();
|
|
|
1026 |
while (it.hasNext()) {
|
|
|
1027 |
var entry = it.next();
|
|
|
1028 |
this.put(entry.getKey(), entry.getValue());
|
|
|
1029 |
}
|
|
|
1030 |
};
|
|
|
1031 |
|
|
|
1032 |
this.remove = function(key) {
|
|
|
1033 |
var index = getBucketIndex(key);
|
|
|
1034 |
var bucket = buckets[index];
|
|
|
1035 |
if (bucket === undef) {
|
|
|
1036 |
return null;
|
|
|
1037 |
}
|
|
|
1038 |
for (var i = 0; i < bucket.length; ++i) {
|
|
|
1039 |
if (virtEquals(bucket[i].key, key)) {
|
|
|
1040 |
--count;
|
|
|
1041 |
var previous = bucket[i].value;
|
|
|
1042 |
bucket[i].removed = true;
|
|
|
1043 |
if (bucket.length > 1) {
|
|
|
1044 |
bucket.splice(i, 1);
|
|
|
1045 |
} else {
|
|
|
1046 |
buckets[index] = undef;
|
|
|
1047 |
}
|
|
|
1048 |
return previous;
|
|
|
1049 |
}
|
|
|
1050 |
}
|
|
|
1051 |
return null;
|
|
|
1052 |
};
|
|
|
1053 |
|
|
|
1054 |
this.removeByValue = function(value) {
|
|
|
1055 |
var bucket, i, ilen, pair;
|
|
|
1056 |
for (bucket in buckets) {
|
|
|
1057 |
if (buckets.hasOwnProperty(bucket)) {
|
|
|
1058 |
for (i = 0, ilen = buckets[bucket].length; i < ilen; i++) {
|
|
|
1059 |
pair = buckets[bucket][i];
|
|
|
1060 |
// removal on values is based on identity, not equality
|
|
|
1061 |
if (pair.value === value) {
|
|
|
1062 |
buckets[bucket].splice(i, 1);
|
|
|
1063 |
return true;
|
|
|
1064 |
}
|
|
|
1065 |
}
|
|
|
1066 |
}
|
|
|
1067 |
}
|
|
|
1068 |
return false;
|
|
|
1069 |
};
|
|
|
1070 |
|
|
|
1071 |
this.size = function() {
|
|
|
1072 |
return count;
|
|
|
1073 |
};
|
|
|
1074 |
}
|
|
|
1075 |
|
|
|
1076 |
return HashMap;
|
|
|
1077 |
}());
|
|
|
1078 |
|
|
|
1079 |
var PVector = (function() {
|
|
|
1080 |
function PVector(x, y, z) {
|
|
|
1081 |
this.x = x || 0;
|
|
|
1082 |
this.y = y || 0;
|
|
|
1083 |
this.z = z || 0;
|
|
|
1084 |
}
|
|
|
1085 |
|
|
|
1086 |
PVector.dist = function(v1, v2) {
|
|
|
1087 |
return v1.dist(v2);
|
|
|
1088 |
};
|
|
|
1089 |
|
|
|
1090 |
PVector.dot = function(v1, v2) {
|
|
|
1091 |
return v1.dot(v2);
|
|
|
1092 |
};
|
|
|
1093 |
|
|
|
1094 |
PVector.cross = function(v1, v2) {
|
|
|
1095 |
return v1.cross(v2);
|
|
|
1096 |
};
|
|
|
1097 |
|
|
|
1098 |
PVector.angleBetween = function(v1, v2) {
|
|
|
1099 |
return Math.acos(v1.dot(v2) / (v1.mag() * v2.mag()));
|
|
|
1100 |
};
|
|
|
1101 |
|
|
|
1102 |
// Common vector operations for PVector
|
|
|
1103 |
PVector.prototype = {
|
|
|
1104 |
set: function(v, y, z) {
|
|
|
1105 |
if (arguments.length === 1) {
|
|
|
1106 |
this.set(v.x || v[0] || 0,
|
|
|
1107 |
v.y || v[1] || 0,
|
|
|
1108 |
v.z || v[2] || 0);
|
|
|
1109 |
} else {
|
|
|
1110 |
this.x = v;
|
|
|
1111 |
this.y = y;
|
|
|
1112 |
this.z = z;
|
|
|
1113 |
}
|
|
|
1114 |
},
|
|
|
1115 |
get: function() {
|
|
|
1116 |
return new PVector(this.x, this.y, this.z);
|
|
|
1117 |
},
|
|
|
1118 |
mag: function() {
|
|
|
1119 |
var x = this.x,
|
|
|
1120 |
y = this.y,
|
|
|
1121 |
z = this.z;
|
|
|
1122 |
return Math.sqrt(x * x + y * y + z * z);
|
|
|
1123 |
},
|
|
|
1124 |
add: function(v, y, z) {
|
|
|
1125 |
if (arguments.length === 1) {
|
|
|
1126 |
this.x += v.x;
|
|
|
1127 |
this.y += v.y;
|
|
|
1128 |
this.z += v.z;
|
|
|
1129 |
} else {
|
|
|
1130 |
this.x += v;
|
|
|
1131 |
this.y += y;
|
|
|
1132 |
this.z += z;
|
|
|
1133 |
}
|
|
|
1134 |
},
|
|
|
1135 |
sub: function(v, y, z) {
|
|
|
1136 |
if (arguments.length === 1) {
|
|
|
1137 |
this.x -= v.x;
|
|
|
1138 |
this.y -= v.y;
|
|
|
1139 |
this.z -= v.z;
|
|
|
1140 |
} else {
|
|
|
1141 |
this.x -= v;
|
|
|
1142 |
this.y -= y;
|
|
|
1143 |
this.z -= z;
|
|
|
1144 |
}
|
|
|
1145 |
},
|
|
|
1146 |
mult: function(v) {
|
|
|
1147 |
if (typeof v === 'number') {
|
|
|
1148 |
this.x *= v;
|
|
|
1149 |
this.y *= v;
|
|
|
1150 |
this.z *= v;
|
|
|
1151 |
} else {
|
|
|
1152 |
this.x *= v.x;
|
|
|
1153 |
this.y *= v.y;
|
|
|
1154 |
this.z *= v.z;
|
|
|
1155 |
}
|
|
|
1156 |
},
|
|
|
1157 |
div: function(v) {
|
|
|
1158 |
if (typeof v === 'number') {
|
|
|
1159 |
this.x /= v;
|
|
|
1160 |
this.y /= v;
|
|
|
1161 |
this.z /= v;
|
|
|
1162 |
} else {
|
|
|
1163 |
this.x /= v.x;
|
|
|
1164 |
this.y /= v.y;
|
|
|
1165 |
this.z /= v.z;
|
|
|
1166 |
}
|
|
|
1167 |
},
|
|
|
1168 |
dist: function(v) {
|
|
|
1169 |
var dx = this.x - v.x,
|
|
|
1170 |
dy = this.y - v.y,
|
|
|
1171 |
dz = this.z - v.z;
|
|
|
1172 |
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
|
1173 |
},
|
|
|
1174 |
dot: function(v, y, z) {
|
|
|
1175 |
if (arguments.length === 1) {
|
|
|
1176 |
return (this.x * v.x + this.y * v.y + this.z * v.z);
|
|
|
1177 |
}
|
|
|
1178 |
return (this.x * v + this.y * y + this.z * z);
|
|
|
1179 |
},
|
|
|
1180 |
cross: function(v) {
|
|
|
1181 |
var x = this.x,
|
|
|
1182 |
y = this.y,
|
|
|
1183 |
z = this.z;
|
|
|
1184 |
return new PVector(y * v.z - v.y * z,
|
|
|
1185 |
z * v.x - v.z * x,
|
|
|
1186 |
x * v.y - v.x * y);
|
|
|
1187 |
},
|
|
|
1188 |
normalize: function() {
|
|
|
1189 |
var m = this.mag();
|
|
|
1190 |
if (m > 0) {
|
|
|
1191 |
this.div(m);
|
|
|
1192 |
}
|
|
|
1193 |
},
|
|
|
1194 |
limit: function(high) {
|
|
|
1195 |
if (this.mag() > high) {
|
|
|
1196 |
this.normalize();
|
|
|
1197 |
this.mult(high);
|
|
|
1198 |
}
|
|
|
1199 |
},
|
|
|
1200 |
heading2D: function() {
|
|
|
1201 |
return (-Math.atan2(-this.y, this.x));
|
|
|
1202 |
},
|
|
|
1203 |
toString: function() {
|
|
|
1204 |
return "[" + this.x + ", " + this.y + ", " + this.z + "]";
|
|
|
1205 |
},
|
|
|
1206 |
array: function() {
|
|
|
1207 |
return [this.x, this.y, this.z];
|
|
|
1208 |
}
|
|
|
1209 |
};
|
|
|
1210 |
|
|
|
1211 |
function createPVectorMethod(method) {
|
|
|
1212 |
return function(v1, v2) {
|
|
|
1213 |
var v = v1.get();
|
|
|
1214 |
v[method](v2);
|
|
|
1215 |
return v;
|
|
|
1216 |
};
|
|
|
1217 |
}
|
|
|
1218 |
|
|
|
1219 |
for (var method in PVector.prototype) {
|
|
|
1220 |
if (PVector.prototype.hasOwnProperty(method) && !PVector.hasOwnProperty(method)) {
|
|
|
1221 |
PVector[method] = createPVectorMethod(method);
|
|
|
1222 |
}
|
|
|
1223 |
}
|
|
|
1224 |
|
|
|
1225 |
return PVector;
|
|
|
1226 |
}());
|
|
|
1227 |
|
|
|
1228 |
// Building defaultScope. Changing of the prototype protects
|
|
|
1229 |
// internal Processing code from the changes in defaultScope
|
|
|
1230 |
function DefaultScope() {}
|
|
|
1231 |
DefaultScope.prototype = PConstants;
|
|
|
1232 |
|
|
|
1233 |
var defaultScope = new DefaultScope();
|
|
|
1234 |
defaultScope.ArrayList = ArrayList;
|
|
|
1235 |
defaultScope.HashMap = HashMap;
|
|
|
1236 |
defaultScope.PVector = PVector;
|
|
|
1237 |
defaultScope.ObjectIterator = ObjectIterator;
|
|
|
1238 |
defaultScope.PConstants = PConstants;
|
|
|
1239 |
//defaultScope.PImage = PImage; // TODO
|
|
|
1240 |
//defaultScope.PShape = PShape; // TODO
|
|
|
1241 |
//defaultScope.PShapeSVG = PShapeSVG; // TODO
|
|
|
1242 |
|
|
|
1243 |
////////////////////////////////////////////////////////////////////////////
|
|
|
1244 |
// Class inheritance helper methods
|
|
|
1245 |
////////////////////////////////////////////////////////////////////////////
|
|
|
1246 |
|
|
|
1247 |
defaultScope.defineProperty = function(obj, name, desc) {
|
|
|
1248 |
if("defineProperty" in Object) {
|
|
|
1249 |
Object.defineProperty(obj, name, desc);
|
|
|
1250 |
} else {
|
|
|
1251 |
if (desc.hasOwnProperty("get")) {
|
|
|
1252 |
obj.__defineGetter__(name, desc.get);
|
|
|
1253 |
}
|
|
|
1254 |
if (desc.hasOwnProperty("set")) {
|
|
|
1255 |
obj.__defineSetter__(name, desc.set);
|
|
|
1256 |
}
|
|
|
1257 |
}
|
|
|
1258 |
};
|
|
|
1259 |
|
|
|
1260 |
function extendClass(subClass, baseClass) {
|
|
|
1261 |
function extendGetterSetter(propertyName) {
|
|
|
1262 |
defaultScope.defineProperty(subClass, propertyName, {
|
|
|
1263 |
get: function() {
|
|
|
1264 |
return baseClass[propertyName];
|
|
|
1265 |
},
|
|
|
1266 |
set: function(v) {
|
|
|
1267 |
baseClass[propertyName]=v;
|
|
|
1268 |
},
|
|
|
1269 |
enumerable: true
|
|
|
1270 |
});
|
|
|
1271 |
}
|
|
|
1272 |
|
|
|
1273 |
var properties = [];
|
|
|
1274 |
for (var propertyName in baseClass) {
|
|
|
1275 |
if (typeof baseClass[propertyName] === 'function') {
|
|
|
1276 |
// Overriding all non-overriden functions
|
|
|
1277 |
if (!subClass.hasOwnProperty(propertyName)) {
|
|
|
1278 |
subClass[propertyName] = baseClass[propertyName];
|
|
|
1279 |
}
|
|
|
1280 |
} else if(propertyName.charAt(0) !== "$" && !(propertyName in subClass)) {
|
|
|
1281 |
// Delaying the properties extension due to the IE9 bug (see #918).
|
|
|
1282 |
properties.push(propertyName);
|
|
|
1283 |
}
|
|
|
1284 |
}
|
|
|
1285 |
while (properties.length > 0) {
|
|
|
1286 |
extendGetterSetter(properties.shift());
|
|
|
1287 |
}
|
|
|
1288 |
}
|
|
|
1289 |
|
|
|
1290 |
defaultScope.extendClassChain = function(base) {
|
|
|
1291 |
var path = [base];
|
|
|
1292 |
for (var self = base.$upcast; self; self = self.$upcast) {
|
|
|
1293 |
extendClass(self, base);
|
|
|
1294 |
path.push(self);
|
|
|
1295 |
base = self;
|
|
|
1296 |
}
|
|
|
1297 |
while (path.length > 0) {
|
|
|
1298 |
path.pop().$self=base;
|
|
|
1299 |
}
|
|
|
1300 |
};
|
|
|
1301 |
|
|
|
1302 |
defaultScope.extendStaticMembers = function(derived, base) {
|
|
|
1303 |
extendClass(derived, base);
|
|
|
1304 |
};
|
|
|
1305 |
|
|
|
1306 |
defaultScope.extendInterfaceMembers = function(derived, base) {
|
|
|
1307 |
extendClass(derived, base);
|
|
|
1308 |
};
|
|
|
1309 |
|
|
|
1310 |
defaultScope.addMethod = function(object, name, fn, superAccessor) {
|
|
|
1311 |
if (object[name]) {
|
|
|
1312 |
var args = fn.length,
|
|
|
1313 |
oldfn = object[name];
|
|
|
1314 |
|
|
|
1315 |
object[name] = function() {
|
|
|
1316 |
if (arguments.length === args) {
|
|
|
1317 |
return fn.apply(this, arguments);
|
|
|
1318 |
}
|
|
|
1319 |
return oldfn.apply(this, arguments);
|
|
|
1320 |
};
|
|
|
1321 |
} else {
|
|
|
1322 |
object[name] = fn;
|
|
|
1323 |
}
|
|
|
1324 |
};
|
|
|
1325 |
|
|
|
1326 |
defaultScope.createJavaArray = function(type, bounds) {
|
|
|
1327 |
var result = null;
|
|
|
1328 |
if (typeof bounds[0] === 'number') {
|
|
|
1329 |
var itemsCount = 0 | bounds[0];
|
|
|
1330 |
if (bounds.length <= 1) {
|
|
|
1331 |
result = [];
|
|
|
1332 |
result.length = itemsCount;
|
|
|
1333 |
for (var i = 0; i < itemsCount; ++i) {
|
|
|
1334 |
result[i] = 0;
|
|
|
1335 |
}
|
|
|
1336 |
} else {
|
|
|
1337 |
result = [];
|
|
|
1338 |
var newBounds = bounds.slice(1);
|
|
|
1339 |
for (var j = 0; j < itemsCount; ++j) {
|
|
|
1340 |
result.push(defaultScope.createJavaArray(type, newBounds));
|
|
|
1341 |
}
|
|
|
1342 |
}
|
|
|
1343 |
}
|
|
|
1344 |
return result;
|
|
|
1345 |
};
|
|
|
1346 |
|
|
|
1347 |
var colors = {
|
|
|
1348 |
aliceblue: "#f0f8ff",
|
|
|
1349 |
antiquewhite: "#faebd7",
|
|
|
1350 |
aqua: "#00ffff",
|
|
|
1351 |
aquamarine: "#7fffd4",
|
|
|
1352 |
azure: "#f0ffff",
|
|
|
1353 |
beige: "#f5f5dc",
|
|
|
1354 |
bisque: "#ffe4c4",
|
|
|
1355 |
black: "#000000",
|
|
|
1356 |
blanchedalmond: "#ffebcd",
|
|
|
1357 |
blue: "#0000ff",
|
|
|
1358 |
blueviolet: "#8a2be2",
|
|
|
1359 |
brown: "#a52a2a",
|
|
|
1360 |
burlywood: "#deb887",
|
|
|
1361 |
cadetblue: "#5f9ea0",
|
|
|
1362 |
chartreuse: "#7fff00",
|
|
|
1363 |
chocolate: "#d2691e",
|
|
|
1364 |
coral: "#ff7f50",
|
|
|
1365 |
cornflowerblue: "#6495ed",
|
|
|
1366 |
cornsilk: "#fff8dc",
|
|
|
1367 |
crimson: "#dc143c",
|
|
|
1368 |
cyan: "#00ffff",
|
|
|
1369 |
darkblue: "#00008b",
|
|
|
1370 |
darkcyan: "#008b8b",
|
|
|
1371 |
darkgoldenrod: "#b8860b",
|
|
|
1372 |
darkgray: "#a9a9a9",
|
|
|
1373 |
darkgreen: "#006400",
|
|
|
1374 |
darkkhaki: "#bdb76b",
|
|
|
1375 |
darkmagenta: "#8b008b",
|
|
|
1376 |
darkolivegreen: "#556b2f",
|
|
|
1377 |
darkorange: "#ff8c00",
|
|
|
1378 |
darkorchid: "#9932cc",
|
|
|
1379 |
darkred: "#8b0000",
|
|
|
1380 |
darksalmon: "#e9967a",
|
|
|
1381 |
darkseagreen: "#8fbc8f",
|
|
|
1382 |
darkslateblue: "#483d8b",
|
|
|
1383 |
darkslategray: "#2f4f4f",
|
|
|
1384 |
darkturquoise: "#00ced1",
|
|
|
1385 |
darkviolet: "#9400d3",
|
|
|
1386 |
deeppink: "#ff1493",
|
|
|
1387 |
deepskyblue: "#00bfff",
|
|
|
1388 |
dimgray: "#696969",
|
|
|
1389 |
dodgerblue: "#1e90ff",
|
|
|
1390 |
firebrick: "#b22222",
|
|
|
1391 |
floralwhite: "#fffaf0",
|
|
|
1392 |
forestgreen: "#228b22",
|
|
|
1393 |
fuchsia: "#ff00ff",
|
|
|
1394 |
gainsboro: "#dcdcdc",
|
|
|
1395 |
ghostwhite: "#f8f8ff",
|
|
|
1396 |
gold: "#ffd700",
|
|
|
1397 |
goldenrod: "#daa520",
|
|
|
1398 |
gray: "#808080",
|
|
|
1399 |
green: "#008000",
|
|
|
1400 |
greenyellow: "#adff2f",
|
|
|
1401 |
honeydew: "#f0fff0",
|
|
|
1402 |
hotpink: "#ff69b4",
|
|
|
1403 |
indianred: "#cd5c5c",
|
|
|
1404 |
indigo: "#4b0082",
|
|
|
1405 |
ivory: "#fffff0",
|
|
|
1406 |
khaki: "#f0e68c",
|
|
|
1407 |
lavender: "#e6e6fa",
|
|
|
1408 |
lavenderblush: "#fff0f5",
|
|
|
1409 |
lawngreen: "#7cfc00",
|
|
|
1410 |
lemonchiffon: "#fffacd",
|
|
|
1411 |
lightblue: "#add8e6",
|
|
|
1412 |
lightcoral: "#f08080",
|
|
|
1413 |
lightcyan: "#e0ffff",
|
|
|
1414 |
lightgoldenrodyellow: "#fafad2",
|
|
|
1415 |
lightgrey: "#d3d3d3",
|
|
|
1416 |
lightgreen: "#90ee90",
|
|
|
1417 |
lightpink: "#ffb6c1",
|
|
|
1418 |
lightsalmon: "#ffa07a",
|
|
|
1419 |
lightseagreen: "#20b2aa",
|
|
|
1420 |
lightskyblue: "#87cefa",
|
|
|
1421 |
lightslategray: "#778899",
|
|
|
1422 |
lightsteelblue: "#b0c4de",
|
|
|
1423 |
lightyellow: "#ffffe0",
|
|
|
1424 |
lime: "#00ff00",
|
|
|
1425 |
limegreen: "#32cd32",
|
|
|
1426 |
linen: "#faf0e6",
|
|
|
1427 |
magenta: "#ff00ff",
|
|
|
1428 |
maroon: "#800000",
|
|
|
1429 |
mediumaquamarine: "#66cdaa",
|
|
|
1430 |
mediumblue: "#0000cd",
|
|
|
1431 |
mediumorchid: "#ba55d3",
|
|
|
1432 |
mediumpurple: "#9370d8",
|
|
|
1433 |
mediumseagreen: "#3cb371",
|
|
|
1434 |
mediumslateblue: "#7b68ee",
|
|
|
1435 |
mediumspringgreen: "#00fa9a",
|
|
|
1436 |
mediumturquoise: "#48d1cc",
|
|
|
1437 |
mediumvioletred: "#c71585",
|
|
|
1438 |
midnightblue: "#191970",
|
|
|
1439 |
mintcream: "#f5fffa",
|
|
|
1440 |
mistyrose: "#ffe4e1",
|
|
|
1441 |
moccasin: "#ffe4b5",
|
|
|
1442 |
navajowhite: "#ffdead",
|
|
|
1443 |
navy: "#000080",
|
|
|
1444 |
oldlace: "#fdf5e6",
|
|
|
1445 |
olive: "#808000",
|
|
|
1446 |
olivedrab: "#6b8e23",
|
|
|
1447 |
orange: "#ffa500",
|
|
|
1448 |
orangered: "#ff4500",
|
|
|
1449 |
orchid: "#da70d6",
|
|
|
1450 |
palegoldenrod: "#eee8aa",
|
|
|
1451 |
palegreen: "#98fb98",
|
|
|
1452 |
paleturquoise: "#afeeee",
|
|
|
1453 |
palevioletred: "#d87093",
|
|
|
1454 |
papayawhip: "#ffefd5",
|
|
|
1455 |
peachpuff: "#ffdab9",
|
|
|
1456 |
peru: "#cd853f",
|
|
|
1457 |
pink: "#ffc0cb",
|
|
|
1458 |
plum: "#dda0dd",
|
|
|
1459 |
powderblue: "#b0e0e6",
|
|
|
1460 |
purple: "#800080",
|
|
|
1461 |
red: "#ff0000",
|
|
|
1462 |
rosybrown: "#bc8f8f",
|
|
|
1463 |
royalblue: "#4169e1",
|
|
|
1464 |
saddlebrown: "#8b4513",
|
|
|
1465 |
salmon: "#fa8072",
|
|
|
1466 |
sandybrown: "#f4a460",
|
|
|
1467 |
seagreen: "#2e8b57",
|
|
|
1468 |
seashell: "#fff5ee",
|
|
|
1469 |
sienna: "#a0522d",
|
|
|
1470 |
silver: "#c0c0c0",
|
|
|
1471 |
skyblue: "#87ceeb",
|
|
|
1472 |
slateblue: "#6a5acd",
|
|
|
1473 |
slategray: "#708090",
|
|
|
1474 |
snow: "#fffafa",
|
|
|
1475 |
springgreen: "#00ff7f",
|
|
|
1476 |
steelblue: "#4682b4",
|
|
|
1477 |
tan: "#d2b48c",
|
|
|
1478 |
teal: "#008080",
|
|
|
1479 |
thistle: "#d8bfd8",
|
|
|
1480 |
tomato: "#ff6347",
|
|
|
1481 |
turquoise: "#40e0d0",
|
|
|
1482 |
violet: "#ee82ee",
|
|
|
1483 |
wheat: "#f5deb3",
|
|
|
1484 |
white: "#ffffff",
|
|
|
1485 |
whitesmoke: "#f5f5f5",
|
|
|
1486 |
yellow: "#ffff00",
|
|
|
1487 |
yellowgreen: "#9acd32"
|
|
|
1488 |
};
|
|
|
1489 |
|
|
|
1490 |
// Unsupported Processing File and I/O operations.
|
|
|
1491 |
(function(Processing) {
|
|
|
1492 |
var unsupportedP5 = ("open() createOutput() createInput() BufferedReader selectFolder() " +
|
|
|
1493 |
"dataPath() createWriter() selectOutput() beginRecord() " +
|
|
|
1494 |
"saveStream() endRecord() selectInput() saveBytes() createReader() " +
|
|
|
1495 |
"beginRaw() endRaw() PrintWriter delay()").split(" "),
|
|
|
1496 |
count = unsupportedP5.length,
|
|
|
1497 |
prettyName,
|
|
|
1498 |
p5Name;
|
|
|
1499 |
|
|
|
1500 |
function createUnsupportedFunc(n) {
|
|
|
1501 |
return function() {
|
|
|
1502 |
throw "Processing.js does not support " + n + ".";
|
|
|
1503 |
};
|
|
|
1504 |
}
|
|
|
1505 |
|
|
|
1506 |
while (count--) {
|
|
|
1507 |
prettyName = unsupportedP5[count];
|
|
|
1508 |
p5Name = prettyName.replace("()", "");
|
|
|
1509 |
|
|
|
1510 |
Processing[p5Name] = createUnsupportedFunc(prettyName);
|
|
|
1511 |
}
|
|
|
1512 |
}(defaultScope));
|
|
|
1513 |
|
|
|
1514 |
// screenWidth and screenHeight are shared by all instances.
|
|
|
1515 |
// and return the width/height of the browser's viewport.
|
|
|
1516 |
defaultScope.defineProperty(defaultScope, 'screenWidth',
|
|
|
1517 |
{ get: function() { return window.innerWidth; } });
|
|
|
1518 |
|
|
|
1519 |
defaultScope.defineProperty(defaultScope, 'screenHeight',
|
|
|
1520 |
{ get: function() { return window.innerHeight; } });
|
|
|
1521 |
|
|
|
1522 |
// Manage multiple Processing instances
|
|
|
1523 |
var processingInstances = [];
|
|
|
1524 |
var processingInstanceIds = {};
|
|
|
1525 |
|
|
|
1526 |
var removeInstance = function(id) {
|
|
|
1527 |
processingInstances.splice(processingInstanceIds[id], 1);
|
|
|
1528 |
delete processingInstanceIds[id];
|
|
|
1529 |
};
|
|
|
1530 |
|
|
|
1531 |
var addInstance = function(processing) {
|
|
|
1532 |
if (processing.externals.canvas.id === undef || !processing.externals.canvas.id.length) {
|
|
|
1533 |
processing.externals.canvas.id = "__processing" + processingInstances.length;
|
|
|
1534 |
}
|
|
|
1535 |
processingInstanceIds[processing.externals.canvas.id] = processingInstances.length;
|
|
|
1536 |
processingInstances.push(processing);
|
|
|
1537 |
};
|
|
|
1538 |
|
|
|
1539 |
|
|
|
1540 |
////////////////////////////////////////////////////////////////////////////
|
|
|
1541 |
// PFONT.JS START
|
|
|
1542 |
////////////////////////////////////////////////////////////////////////////
|
|
|
1543 |
|
|
|
1544 |
/**
|
|
|
1545 |
* [internal function] computeFontMetrics() calculates various metrics for text
|
|
|
1546 |
* placement. Currently this function computes the ascent, descent and leading
|
|
|
1547 |
* (from "lead", used for vertical space) values for the currently active font.
|
|
|
1548 |
*/
|
|
|
1549 |
function computeFontMetrics(pfont) {
|
|
|
1550 |
var emQuad = 250,
|
|
|
1551 |
correctionFactor = pfont.size / emQuad,
|
|
|
1552 |
canvas = document.createElement("canvas");
|
|
|
1553 |
canvas.width = 2*emQuad;
|
|
|
1554 |
canvas.height = 2*emQuad;
|
|
|
1555 |
canvas.style.opacity = 0;
|
|
|
1556 |
var cfmFont = pfont.getCSSDefinition(emQuad+"px", "normal"),
|
|
|
1557 |
ctx = canvas.getContext("2d");
|
|
|
1558 |
ctx.font = cfmFont;
|
|
|
1559 |
pfont.context2d = ctx;
|
|
|
1560 |
|
|
|
1561 |
// Size the canvas using a string with common max-ascent and max-descent letters.
|
|
|
1562 |
// Changing the canvas dimensions resets the context, so we must reset the font.
|
|
|
1563 |
var protrusions = "dbflkhyjqpg";
|
|
|
1564 |
canvas.width = ctx.measureText(protrusions).width;
|
|
|
1565 |
ctx.font = cfmFont;
|
|
|
1566 |
|
|
|
1567 |
// for text lead values, we meaure a multiline text container.
|
|
|
1568 |
var leadDiv = document.createElement("div");
|
|
|
1569 |
leadDiv.style.position = "absolute";
|
|
|
1570 |
leadDiv.style.opacity = 0;
|
|
|
1571 |
leadDiv.style.fontFamily = '"' + pfont.name + '"';
|
|
|
1572 |
leadDiv.style.fontSize = emQuad + "px";
|
|
|
1573 |
leadDiv.innerHTML = protrusions + "<br/>" + protrusions;
|
|
|
1574 |
document.body.appendChild(leadDiv);
|
|
|
1575 |
|
|
|
1576 |
var w = canvas.width,
|
|
|
1577 |
h = canvas.height,
|
|
|
1578 |
baseline = h/2;
|
|
|
1579 |
|
|
|
1580 |
// Set all canvas pixeldata values to 255, with all the content
|
|
|
1581 |
// data being 0. This lets us scan for data[i] != 255.
|
|
|
1582 |
ctx.fillStyle = "white";
|
|
|
1583 |
ctx.fillRect(0, 0, w, h);
|
|
|
1584 |
ctx.fillStyle = "black";
|
|
|
1585 |
ctx.fillText(protrusions, 0, baseline);
|
|
|
1586 |
var pixelData = ctx.getImageData(0, 0, w, h).data;
|
|
|
1587 |
|
|
|
1588 |
// canvas pixel data is w*4 by h*4, because R, G, B and A are separate,
|
|
|
1589 |
// consecutive values in the array, rather than stored as 32 bit ints.
|
|
|
1590 |
var i = 0,
|
|
|
1591 |
w4 = w * 4,
|
|
|
1592 |
len = pixelData.length;
|
|
|
1593 |
|
|
|
1594 |
// Finding the ascent uses a normal, forward scanline
|
|
|
1595 |
while (++i < len && pixelData[i] === 255) {
|
|
|
1596 |
nop();
|
|
|
1597 |
}
|
|
|
1598 |
var ascent = Math.round(i / w4);
|
|
|
1599 |
|
|
|
1600 |
// Finding the descent uses a reverse scanline
|
|
|
1601 |
i = len - 1;
|
|
|
1602 |
while (--i > 0 && pixelData[i] === 255) {
|
|
|
1603 |
nop();
|
|
|
1604 |
}
|
|
|
1605 |
var descent = Math.round(i / w4);
|
|
|
1606 |
|
|
|
1607 |
// set font metrics
|
|
|
1608 |
pfont.ascent = correctionFactor * (baseline - ascent);
|
|
|
1609 |
pfont.descent = correctionFactor * (descent - baseline);
|
|
|
1610 |
|
|
|
1611 |
// Then we try to get the real value from the browser
|
|
|
1612 |
if (document.defaultView.getComputedStyle) {
|
|
|
1613 |
var leadDivHeight = document.defaultView.getComputedStyle(leadDiv,null).getPropertyValue("height");
|
|
|
1614 |
leadDivHeight = correctionFactor * leadDivHeight.replace("px","");
|
|
|
1615 |
if (leadDivHeight >= pfont.size * 2) {
|
|
|
1616 |
pfont.leading = Math.round(leadDivHeight/2);
|
|
|
1617 |
}
|
|
|
1618 |
}
|
|
|
1619 |
document.body.removeChild(leadDiv);
|
|
|
1620 |
}
|
|
|
1621 |
|
|
|
1622 |
// Defines system (non-SVG) font.
|
|
|
1623 |
function PFont(name, size) {
|
|
|
1624 |
// according to the P5 API, new PFont() is legal (albeit completely useless)
|
|
|
1625 |
if (name === undef) {
|
|
|
1626 |
name = "";
|
|
|
1627 |
}
|
|
|
1628 |
this.name = name;
|
|
|
1629 |
if (size === undef) {
|
|
|
1630 |
size = 0;
|
|
|
1631 |
}
|
|
|
1632 |
this.size = size;
|
|
|
1633 |
this.glyph = false;
|
|
|
1634 |
this.ascent = 0;
|
|
|
1635 |
this.descent = 0;
|
|
|
1636 |
// For leading, the "safe" value uses the standard TEX ratio
|
|
|
1637 |
this.leading = 1.2 * size;
|
|
|
1638 |
|
|
|
1639 |
// Note that an italic, bold font must used "... Bold Italic"
|
|
|
1640 |
// in P5. "... Italic Bold" is treated as normal/normal.
|
|
|
1641 |
var illegalIndicator = name.indexOf(" Italic Bold");
|
|
|
1642 |
if (illegalIndicator !== -1) {
|
|
|
1643 |
name = name.substring(0, illegalIndicator);
|
|
|
1644 |
}
|
|
|
1645 |
|
|
|
1646 |
// determine font style
|
|
|
1647 |
this.style = "normal";
|
|
|
1648 |
var italicsIndicator = name.indexOf(" Italic");
|
|
|
1649 |
if (italicsIndicator !== -1) {
|
|
|
1650 |
name = name.substring(0, italicsIndicator);
|
|
|
1651 |
this.style = "italic";
|
|
|
1652 |
}
|
|
|
1653 |
|
|
|
1654 |
// determine font weight
|
|
|
1655 |
this.weight = "normal";
|
|
|
1656 |
var boldIndicator = name.indexOf(" Bold");
|
|
|
1657 |
if (boldIndicator !== -1) {
|
|
|
1658 |
name = name.substring(0, boldIndicator);
|
|
|
1659 |
this.weight = "bold";
|
|
|
1660 |
}
|
|
|
1661 |
|
|
|
1662 |
// determine font-family name
|
|
|
1663 |
this.family = "sans-serif";
|
|
|
1664 |
if (name !== undef) {
|
|
|
1665 |
switch(name) {
|
|
|
1666 |
case "sans-serif":
|
|
|
1667 |
case "serif":
|
|
|
1668 |
case "monospace":
|
|
|
1669 |
case "fantasy":
|
|
|
1670 |
case "cursive":
|
|
|
1671 |
this.family = name;
|
|
|
1672 |
break;
|
|
|
1673 |
default:
|
|
|
1674 |
this.family = '"' + name + '", sans-serif';
|
|
|
1675 |
break;
|
|
|
1676 |
}
|
|
|
1677 |
}
|
|
|
1678 |
// Calculate the ascent/descent/leading value based on
|
|
|
1679 |
// how the browser renders this font.
|
|
|
1680 |
this.context2d = null;
|
|
|
1681 |
computeFontMetrics(this);
|
|
|
1682 |
this.css = this.getCSSDefinition();
|
|
|
1683 |
this.context2d.font = this.css;
|
|
|
1684 |
}
|
|
|
1685 |
|
|
|
1686 |
/**
|
|
|
1687 |
* This function generates the CSS "font" string for this PFont
|
|
|
1688 |
*/
|
|
|
1689 |
PFont.prototype.getCSSDefinition = function(fontSize, lineHeight) {
|
|
|
1690 |
if(fontSize===undef) {
|
|
|
1691 |
fontSize = this.size + "px";
|
|
|
1692 |
}
|
|
|
1693 |
if(lineHeight===undef) {
|
|
|
1694 |
lineHeight = this.leading + "px";
|
|
|
1695 |
}
|
|
|
1696 |
// CSS "font" definition: font-style font-variant font-weight font-size/line-height font-family
|
|
|
1697 |
var components = [this.style, "normal", this.weight, fontSize + "/" + lineHeight, this.family];
|
|
|
1698 |
return components.join(" ");
|
|
|
1699 |
};
|
|
|
1700 |
|
|
|
1701 |
/**
|
|
|
1702 |
* We cannot rely on there being a 2d context available,
|
|
|
1703 |
* because we support OPENGL sketches, and canvas3d has
|
|
|
1704 |
* no "measureText" function in the API.
|
|
|
1705 |
*/
|
|
|
1706 |
PFont.prototype.measureTextWidth = function(string) {
|
|
|
1707 |
return this.context2d.measureText(string).width;
|
|
|
1708 |
};
|
|
|
1709 |
|
|
|
1710 |
/**
|
|
|
1711 |
* Global "loaded fonts" list, internal to PFont
|
|
|
1712 |
*/
|
|
|
1713 |
PFont.PFontCache = {};
|
|
|
1714 |
|
|
|
1715 |
/**
|
|
|
1716 |
* This function acts as single access point for getting and caching
|
|
|
1717 |
* fonts across all sketches handled by an instance of Processing.js
|
|
|
1718 |
*/
|
|
|
1719 |
PFont.get = function(fontName, fontSize) {
|
|
|
1720 |
var cache = PFont.PFontCache;
|
|
|
1721 |
var idx = fontName+"/"+fontSize;
|
|
|
1722 |
if (!cache[idx]) {
|
|
|
1723 |
cache[idx] = new PFont(fontName, fontSize);
|
|
|
1724 |
}
|
|
|
1725 |
return cache[idx];
|
|
|
1726 |
};
|
|
|
1727 |
|
|
|
1728 |
/**
|
|
|
1729 |
* Lists all standard fonts. Due to browser limitations, this list is
|
|
|
1730 |
* not the system font list, like in P5, but the CSS "genre" list.
|
|
|
1731 |
*/
|
|
|
1732 |
PFont.list = function() {
|
|
|
1733 |
return ["sans-serif", "serif", "monospace", "fantasy", "cursive"];
|
|
|
1734 |
};
|
|
|
1735 |
|
|
|
1736 |
/**
|
|
|
1737 |
* Loading external fonts through @font-face rules is handled by PFont,
|
|
|
1738 |
* to ensure fonts loaded in this way are globally available.
|
|
|
1739 |
*/
|
|
|
1740 |
PFont.preloading = {
|
|
|
1741 |
// template element used to compare font sizes
|
|
|
1742 |
template: {},
|
|
|
1743 |
// indicates whether or not the reference tiny font has been loaded
|
|
|
1744 |
initialized: false,
|
|
|
1745 |
// load the reference tiny font via a css @font-face rule
|
|
|
1746 |
initialize: function() {
|
|
|
1747 |
var generateTinyFont = function() {
|
|
|
1748 |
var encoded = "#E3KAI2wAgT1MvMg7Eo3VmNtYX7ABi3CxnbHlm" +
|
|
|
1749 |
"7Abw3kaGVhZ7ACs3OGhoZWE7A53CRobXR47AY3" +
|
|
|
1750 |
"AGbG9jYQ7G03Bm1heH7ABC3CBuYW1l7Ae3AgcG" +
|
|
|
1751 |
"9zd7AI3AE#B3AQ2kgTY18PPPUACwAg3ALSRoo3" +
|
|
|
1752 |
"#yld0xg32QAB77#E777773B#E3C#I#Q77773E#" +
|
|
|
1753 |
"Q7777777772CMAIw7AB77732B#M#Q3wAB#g3B#" +
|
|
|
1754 |
"E#E2BB//82BB////w#B7#gAEg3E77x2B32B#E#" +
|
|
|
1755 |
"Q#MTcBAQ32gAe#M#QQJ#E32M#QQJ#I#g32Q77#";
|
|
|
1756 |
var expand = function(input) {
|
|
|
1757 |
return "AAAAAAAA".substr(~~input ? 7-input : 6);
|
|
|
1758 |
};
|
|
|
1759 |
return encoded.replace(/[#237]/g, expand);
|
|
|
1760 |
};
|
|
|
1761 |
var fontface = document.createElement("style");
|
|
|
1762 |
fontface.setAttribute("type","text/css");
|
|
|
1763 |
fontface.innerHTML = "@font-face {\n" +
|
|
|
1764 |
' font-family: "PjsEmptyFont";' + "\n" +
|
|
|
1765 |
" src: url('data:application/x-font-ttf;base64,"+generateTinyFont()+"')\n" +
|
|
|
1766 |
" format('truetype');\n" +
|
|
|
1767 |
"}";
|
|
|
1768 |
document.head.appendChild(fontface);
|
|
|
1769 |
|
|
|
1770 |
// set up the template element
|
|
|
1771 |
var element = document.createElement("span");
|
|
|
1772 |
element.style.cssText = 'position: absolute; top: 0; left: 0; opacity: 0; font-family: "PjsEmptyFont", fantasy;';
|
|
|
1773 |
element.innerHTML = "AAAAAAAA";
|
|
|
1774 |
document.body.appendChild(element);
|
|
|
1775 |
this.template = element;
|
|
|
1776 |
|
|
|
1777 |
this.initialized = true;
|
|
|
1778 |
},
|
|
|
1779 |
// Shorthand function to get the computed width for an element.
|
|
|
1780 |
getElementWidth: function(element) {
|
|
|
1781 |
return document.defaultView.getComputedStyle(element,"").getPropertyValue("width");
|
|
|
1782 |
},
|
|
|
1783 |
// time taken so far in attempting to load a font
|
|
|
1784 |
timeAttempted: 0,
|
|
|
1785 |
// returns false if no fonts are pending load, or true otherwise.
|
|
|
1786 |
pending: function(intervallength) {
|
|
|
1787 |
if (!this.initialized) {
|
|
|
1788 |
this.initialize();
|
|
|
1789 |
}
|
|
|
1790 |
var element,
|
|
|
1791 |
computedWidthFont,
|
|
|
1792 |
computedWidthRef = this.getElementWidth(this.template);
|
|
|
1793 |
for (var i = 0; i < this.fontList.length; i++) {
|
|
|
1794 |
// compares size of text in pixels. if equal, custom font is not yet loaded
|
|
|
1795 |
element = this.fontList[i];
|
|
|
1796 |
computedWidthFont = this.getElementWidth(element);
|
|
|
1797 |
if (this.timeAttempted < 4000 && computedWidthFont === computedWidthRef) {
|
|
|
1798 |
this.timeAttempted += intervallength;
|
|
|
1799 |
return true;
|
|
|
1800 |
} else {
|
|
|
1801 |
document.body.removeChild(element);
|
|
|
1802 |
this.fontList.splice(i--, 1);
|
|
|
1803 |
this.timeAttempted = 0;
|
|
|
1804 |
}
|
|
|
1805 |
}
|
|
|
1806 |
// if there are no more fonts to load, pending is false
|
|
|
1807 |
if (this.fontList.length === 0) {
|
|
|
1808 |
return false;
|
|
|
1809 |
}
|
|
|
1810 |
// We should have already returned before getting here.
|
|
|
1811 |
// But, if we do get here, length!=0 so fonts are pending.
|
|
|
1812 |
return true;
|
|
|
1813 |
},
|
|
|
1814 |
// fontList contains elements to compare font sizes against a template
|
|
|
1815 |
fontList: [],
|
|
|
1816 |
// addedList contains the fontnames of all the fonts loaded via @font-face
|
|
|
1817 |
addedList: {},
|
|
|
1818 |
// adds a font to the font cache
|
|
|
1819 |
// creates an element using the font, to start loading the font,
|
|
|
1820 |
// and compare against a default font to see if the custom font is loaded
|
|
|
1821 |
add: function(fontSrc) {
|
|
|
1822 |
if (!this.initialized) {
|
|
|
1823 |
this.initialize();
|
|
|
1824 |
}
|
|
|
1825 |
// fontSrc can be a string or a javascript object
|
|
|
1826 |
// acceptable fonts are .ttf, .otf, and data uri
|
|
|
1827 |
var fontName = (typeof fontSrc === 'object' ? fontSrc.fontFace : fontSrc),
|
|
|
1828 |
fontUrl = (typeof fontSrc === 'object' ? fontSrc.url : fontSrc);
|
|
|
1829 |
|
|
|
1830 |
// check whether we already created the @font-face rule for this font
|
|
|
1831 |
if (this.addedList[fontName]) {
|
|
|
1832 |
return;
|
|
|
1833 |
}
|
|
|
1834 |
|
|
|
1835 |
// if we didn't, create the @font-face rule
|
|
|
1836 |
var style = document.createElement("style");
|
|
|
1837 |
style.setAttribute("type","text/css");
|
|
|
1838 |
style.innerHTML = "@font-face{\n font-family: '" + fontName + "';\n src: url('" + fontUrl + "');\n}\n";
|
|
|
1839 |
document.head.appendChild(style);
|
|
|
1840 |
this.addedList[fontName] = true;
|
|
|
1841 |
|
|
|
1842 |
// also create the element to load and compare the new font
|
|
|
1843 |
var element = document.createElement("span");
|
|
|
1844 |
element.style.cssText = "position: absolute; top: 0; left: 0; opacity: 0;";
|
|
|
1845 |
element.style.fontFamily = '"' + fontName + '", "PjsEmptyFont", fantasy';
|
|
|
1846 |
element.innerHTML = "AAAAAAAA";
|
|
|
1847 |
document.body.appendChild(element);
|
|
|
1848 |
this.fontList.push(element);
|
|
|
1849 |
}
|
|
|
1850 |
};
|
|
|
1851 |
|
|
|
1852 |
|
|
|
1853 |
// add to the default scope
|
|
|
1854 |
defaultScope.PFont = PFont;
|
|
|
1855 |
|
|
|
1856 |
|
|
|
1857 |
////////////////////////////////////////////////////////////////////////////
|
|
|
1858 |
// PFONT.JS END
|
|
|
1859 |
////////////////////////////////////////////////////////////////////////////
|
|
|
1860 |
|
|
|
1861 |
|
|
|
1862 |
var Processing = this.Processing = function(aCanvas, aCode) {
|
|
|
1863 |
// Previously we allowed calling Processing as a func instead of ctor, but no longer.
|
|
|
1864 |
if (!(this instanceof Processing)) {
|
|
|
1865 |
throw("called Processing constructor as if it were a function: missing 'new'.");
|
|
|
1866 |
}
|
|
|
1867 |
|
|
|
1868 |
var curElement,
|
|
|
1869 |
pgraphicsMode = (aCanvas === undef && aCode === undef);
|
|
|
1870 |
|
|
|
1871 |
if (pgraphicsMode) {
|
|
|
1872 |
curElement = document.createElement("canvas");
|
|
|
1873 |
} else {
|
|
|
1874 |
// We'll take a canvas element or a string for a canvas element's id
|
|
|
1875 |
curElement = typeof aCanvas === "string" ? document.getElementById(aCanvas) : aCanvas;
|
|
|
1876 |
}
|
|
|
1877 |
|
|
|
1878 |
if (!(curElement instanceof HTMLCanvasElement)) {
|
|
|
1879 |
throw("called Processing constructor without passing canvas element reference or id.");
|
|
|
1880 |
}
|
|
|
1881 |
|
|
|
1882 |
function unimplemented(s) {
|
|
|
1883 |
Processing.debug('Unimplemented - ' + s);
|
|
|
1884 |
}
|
|
|
1885 |
|
|
|
1886 |
// When something new is added to "p." it must also be added to the "names" array.
|
|
|
1887 |
// The names array contains the names of everything that is inside "p."
|
|
|
1888 |
var p = this;
|
|
|
1889 |
|
|
|
1890 |
// PJS specific (non-p5) methods and properties to externalize
|
|
|
1891 |
p.externals = {
|
|
|
1892 |
canvas: curElement,
|
|
|
1893 |
context: undef,
|
|
|
1894 |
sketch: undef
|
|
|
1895 |
};
|
|
|
1896 |
|
|
|
1897 |
p.name = 'Processing.js Instance'; // Set Processing defaults / environment variables
|
|
|
1898 |
p.use3DContext = false; // default '2d' canvas context
|
|
|
1899 |
|
|
|
1900 |
/**
|
|
|
1901 |
* Confirms if a Processing program is "focused", meaning that it is
|
|
|
1902 |
* active and will accept input from mouse or keyboard. This variable
|
|
|
1903 |
* is "true" if it is focused and "false" if not. This variable is
|
|
|
1904 |
* often used when you want to warn people they need to click on the
|
|
|
1905 |
* browser before it will work.
|
|
|
1906 |
*/
|
|
|
1907 |
p.focused = false;
|
|
|
1908 |
p.breakShape = false;
|
|
|
1909 |
|
|
|
1910 |
// Glyph path storage for textFonts
|
|
|
1911 |
p.glyphTable = {};
|
|
|
1912 |
|
|
|
1913 |
// Global vars for tracking mouse position
|
|
|
1914 |
p.pmouseX = 0;
|
|
|
1915 |
p.pmouseY = 0;
|
|
|
1916 |
p.mouseX = 0;
|
|
|
1917 |
p.mouseY = 0;
|
|
|
1918 |
p.mouseButton = 0;
|
|
|
1919 |
p.mouseScroll = 0;
|
|
|
1920 |
|
|
|
1921 |
// Undefined event handlers to be replaced by user when needed
|
|
|
1922 |
p.mouseClicked = undef;
|
|
|
1923 |
p.mouseDragged = undef;
|
|
|
1924 |
p.mouseMoved = undef;
|
|
|
1925 |
p.mousePressed = undef;
|
|
|
1926 |
p.mouseReleased = undef;
|
|
|
1927 |
p.mouseScrolled = undef;
|
|
|
1928 |
p.mouseOver = undef;
|
|
|
1929 |
p.mouseOut = undef;
|
|
|
1930 |
p.touchStart = undef;
|
|
|
1931 |
p.touchEnd = undef;
|
|
|
1932 |
p.touchMove = undef;
|
|
|
1933 |
p.touchCancel = undef;
|
|
|
1934 |
p.key = undef;
|
|
|
1935 |
p.keyCode = undef;
|
|
|
1936 |
p.keyPressed = nop; // needed to remove function checks
|
|
|
1937 |
p.keyReleased = nop;
|
|
|
1938 |
p.keyTyped = nop;
|
|
|
1939 |
p.draw = undef;
|
|
|
1940 |
p.setup = undef;
|
|
|
1941 |
|
|
|
1942 |
// Remapped vars
|
|
|
1943 |
p.__mousePressed = false;
|
|
|
1944 |
p.__keyPressed = false;
|
|
|
1945 |
p.__frameRate = 60;
|
|
|
1946 |
|
|
|
1947 |
// The current animation frame
|
|
|
1948 |
p.frameCount = 0;
|
|
|
1949 |
|
|
|
1950 |
// The height/width of the canvas
|
|
|
1951 |
p.width = 100;
|
|
|
1952 |
p.height = 100;
|
|
|
1953 |
|
|
|
1954 |
// "Private" variables used to maintain state
|
|
|
1955 |
var curContext,
|
|
|
1956 |
curSketch,
|
|
|
1957 |
drawing, // hold a Drawing2D or Drawing3D object
|
|
|
1958 |
online = true,
|
|
|
1959 |
doFill = true,
|
|
|
1960 |
fillStyle = [1.0, 1.0, 1.0, 1.0],
|
|
|
1961 |
currentFillColor = 0xFFFFFFFF,
|
|
|
1962 |
isFillDirty = true,
|
|
|
1963 |
doStroke = true,
|
|
|
1964 |
strokeStyle = [0.0, 0.0, 0.0, 1.0],
|
|
|
1965 |
currentStrokeColor = 0xFF000000,
|
|
|
1966 |
isStrokeDirty = true,
|
|
|
1967 |
lineWidth = 1,
|
|
|
1968 |
loopStarted = false,
|
|
|
1969 |
renderSmooth = false,
|
|
|
1970 |
doLoop = true,
|
|
|
1971 |
looping = 0,
|
|
|
1972 |
curRectMode = PConstants.CORNER,
|
|
|
1973 |
curEllipseMode = PConstants.CENTER,
|
|
|
1974 |
normalX = 0,
|
|
|
1975 |
normalY = 0,
|
|
|
1976 |
normalZ = 0,
|
|
|
1977 |
normalMode = PConstants.NORMAL_MODE_AUTO,
|
|
|
1978 |
curFrameRate = 60,
|
|
|
1979 |
curMsPerFrame = 1000/curFrameRate,
|
|
|
1980 |
curCursor = PConstants.ARROW,
|
|
|
1981 |
oldCursor = curElement.style.cursor,
|
|
|
1982 |
curShape = PConstants.POLYGON,
|
|
|
1983 |
curShapeCount = 0,
|
|
|
1984 |
curvePoints = [],
|
|
|
1985 |
curTightness = 0,
|
|
|
1986 |
curveDet = 20,
|
|
|
1987 |
curveInited = false,
|
|
|
1988 |
backgroundObj = -3355444, // rgb(204, 204, 204) is the default gray background colour
|
|
|
1989 |
bezDetail = 20,
|
|
|
1990 |
colorModeA = 255,
|
|
|
1991 |
colorModeX = 255,
|
|
|
1992 |
colorModeY = 255,
|
|
|
1993 |
colorModeZ = 255,
|
|
|
1994 |
pathOpen = false,
|
|
|
1995 |
mouseDragging = false,
|
|
|
1996 |
pmouseXLastFrame = 0,
|
|
|
1997 |
pmouseYLastFrame = 0,
|
|
|
1998 |
curColorMode = PConstants.RGB,
|
|
|
1999 |
curTint = null,
|
|
|
2000 |
curTint3d = null,
|
|
|
2001 |
getLoaded = false,
|
|
|
2002 |
start = Date.now(),
|
|
|
2003 |
timeSinceLastFPS = start,
|
|
|
2004 |
framesSinceLastFPS = 0,
|
|
|
2005 |
textcanvas,
|
|
|
2006 |
curveBasisMatrix,
|
|
|
2007 |
curveToBezierMatrix,
|
|
|
2008 |
curveDrawMatrix,
|
|
|
2009 |
bezierDrawMatrix,
|
|
|
2010 |
bezierBasisInverse,
|
|
|
2011 |
bezierBasisMatrix,
|
|
|
2012 |
curContextCache = { attributes: {}, locations: {} },
|
|
|
2013 |
// Shaders
|
|
|
2014 |
programObject3D,
|
|
|
2015 |
programObject2D,
|
|
|
2016 |
programObjectUnlitShape,
|
|
|
2017 |
boxBuffer,
|
|
|
2018 |
boxNormBuffer,
|
|
|
2019 |
boxOutlineBuffer,
|
|
|
2020 |
rectBuffer,
|
|
|
2021 |
rectNormBuffer,
|
|
|
2022 |
sphereBuffer,
|
|
|
2023 |
lineBuffer,
|
|
|
2024 |
fillBuffer,
|
|
|
2025 |
fillColorBuffer,
|
|
|
2026 |
strokeColorBuffer,
|
|
|
2027 |
pointBuffer,
|
|
|
2028 |
shapeTexVBO,
|
|
|
2029 |
canTex, // texture for createGraphics
|
|
|
2030 |
textTex, // texture for 3d tex
|
|
|
2031 |
curTexture = {width:0,height:0},
|
|
|
2032 |
curTextureMode = PConstants.IMAGE,
|
|
|
2033 |
usingTexture = false,
|
|
|
2034 |
textBuffer,
|
|
|
2035 |
textureBuffer,
|
|
|
2036 |
indexBuffer,
|
|
|
2037 |
// Text alignment
|
|
|
2038 |
horizontalTextAlignment = PConstants.LEFT,
|
|
|
2039 |
verticalTextAlignment = PConstants.BASELINE,
|
|
|
2040 |
textMode = PConstants.MODEL,
|
|
|
2041 |
// Font state
|
|
|
2042 |
curFontName = "Arial",
|
|
|
2043 |
curTextSize = 12,
|
|
|
2044 |
curTextAscent = 9,
|
|
|
2045 |
curTextDescent = 2,
|
|
|
2046 |
curTextLeading = 14,
|
|
|
2047 |
curTextFont = PFont.get(curFontName, curTextSize),
|
|
|
2048 |
// Pixels cache
|
|
|
2049 |
originalContext,
|
|
|
2050 |
proxyContext = null,
|
|
|
2051 |
isContextReplaced = false,
|
|
|
2052 |
setPixelsCached,
|
|
|
2053 |
maxPixelsCached = 1000,
|
|
|
2054 |
pressedKeysMap = [],
|
|
|
2055 |
lastPressedKeyCode = null,
|
|
|
2056 |
codedKeys = [ PConstants.SHIFT, PConstants.CONTROL, PConstants.ALT, PConstants.CAPSLK, PConstants.PGUP, PConstants.PGDN,
|
|
|
2057 |
PConstants.END, PConstants.HOME, PConstants.LEFT, PConstants.UP, PConstants.RIGHT, PConstants.DOWN, PConstants.NUMLK,
|
|
|
2058 |
PConstants.INSERT, PConstants.F1, PConstants.F2, PConstants.F3, PConstants.F4, PConstants.F5, PConstants.F6, PConstants.F7,
|
|
|
2059 |
PConstants.F8, PConstants.F9, PConstants.F10, PConstants.F11, PConstants.F12, PConstants.META ];
|
|
|
2060 |
|
|
|
2061 |
// Get padding and border style widths for mouse offsets
|
|
|
2062 |
var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
|
|
|
2063 |
|
|
|
2064 |
if (document.defaultView && document.defaultView.getComputedStyle) {
|
|
|
2065 |
stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(curElement, null)['paddingLeft'], 10) || 0;
|
|
|
2066 |
stylePaddingTop = parseInt(document.defaultView.getComputedStyle(curElement, null)['paddingTop'], 10) || 0;
|
|
|
2067 |
styleBorderLeft = parseInt(document.defaultView.getComputedStyle(curElement, null)['borderLeftWidth'], 10) || 0;
|
|
|
2068 |
styleBorderTop = parseInt(document.defaultView.getComputedStyle(curElement, null)['borderTopWidth'], 10) || 0;
|
|
|
2069 |
}
|
|
|
2070 |
|
|
|
2071 |
// User can only have MAX_LIGHTS lights
|
|
|
2072 |
var lightCount = 0;
|
|
|
2073 |
|
|
|
2074 |
//sphere stuff
|
|
|
2075 |
var sphereDetailV = 0,
|
|
|
2076 |
sphereDetailU = 0,
|
|
|
2077 |
sphereX = [],
|
|
|
2078 |
sphereY = [],
|
|
|
2079 |
sphereZ = [],
|
|
|
2080 |
sinLUT = new Float32Array(PConstants.SINCOS_LENGTH),
|
|
|
2081 |
cosLUT = new Float32Array(PConstants.SINCOS_LENGTH),
|
|
|
2082 |
sphereVerts,
|
|
|
2083 |
sphereNorms;
|
|
|
2084 |
|
|
|
2085 |
// Camera defaults and settings
|
|
|
2086 |
var cam,
|
|
|
2087 |
cameraInv,
|
|
|
2088 |
modelView,
|
|
|
2089 |
modelViewInv,
|
|
|
2090 |
userMatrixStack,
|
|
|
2091 |
userReverseMatrixStack,
|
|
|
2092 |
inverseCopy,
|
|
|
2093 |
projection,
|
|
|
2094 |
manipulatingCamera = false,
|
|
|
2095 |
frustumMode = false,
|
|
|
2096 |
cameraFOV = 60 * (Math.PI / 180),
|
|
|
2097 |
cameraX = p.width / 2,
|
|
|
2098 |
cameraY = p.height / 2,
|
|
|
2099 |
cameraZ = cameraY / Math.tan(cameraFOV / 2),
|
|
|
2100 |
cameraNear = cameraZ / 10,
|
|
|
2101 |
cameraFar = cameraZ * 10,
|
|
|
2102 |
cameraAspect = p.width / p.height;
|
|
|
2103 |
|
|
|
2104 |
var vertArray = [],
|
|
|
2105 |
curveVertArray = [],
|
|
|
2106 |
curveVertCount = 0,
|
|
|
2107 |
isCurve = false,
|
|
|
2108 |
isBezier = false,
|
|
|
2109 |
firstVert = true;
|
|
|
2110 |
|
|
|
2111 |
//PShape stuff
|
|
|
2112 |
var curShapeMode = PConstants.CORNER;
|
|
|
2113 |
|
|
|
2114 |
// Stores states for pushStyle() and popStyle().
|
|
|
2115 |
var styleArray = [];
|
|
|
2116 |
|
|
|
2117 |
// Vertices are specified in a counter-clockwise order
|
|
|
2118 |
// triangles are in this order: back, front, right, bottom, left, top
|
|
|
2119 |
var boxVerts = new Float32Array([
|
|
|
2120 |
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,
|
|
|
2121 |
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,
|
|
|
2122 |
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,
|
|
|
2123 |
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,
|
|
|
2124 |
-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,
|
|
|
2125 |
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]);
|
|
|
2126 |
|
|
|
2127 |
var boxOutlineVerts = new Float32Array([
|
|
|
2128 |
0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5,
|
|
|
2129 |
-0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5,
|
|
|
2130 |
0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
|
|
|
2131 |
-0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
|
|
|
2132 |
0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5,
|
|
|
2133 |
-0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5]);
|
|
|
2134 |
|
|
|
2135 |
var boxNorms = new Float32Array([
|
|
|
2136 |
0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
|
|
|
2137 |
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
|
|
|
2138 |
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
|
|
|
2139 |
0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
|
|
|
2140 |
-1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0,
|
|
|
2141 |
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]);
|
|
|
2142 |
|
|
|
2143 |
// These verts are used for the fill and stroke using TRIANGLE_FAN and LINE_LOOP
|
|
|
2144 |
var rectVerts = new Float32Array([0,0,0, 0,1,0, 1,1,0, 1,0,0]);
|
|
|
2145 |
|
|
|
2146 |
var rectNorms = new Float32Array([0,0,1, 0,0,1, 0,0,1, 0,0,1]);
|
|
|
2147 |
|
|
|
2148 |
|
|
|
2149 |
// Shader for points and lines in begin/endShape
|
|
|
2150 |
var vShaderSrcUnlitShape =
|
|
|
2151 |
"varying vec4 frontColor;" +
|
|
|
2152 |
|
|
|
2153 |
"attribute vec3 aVertex;" +
|
|
|
2154 |
"attribute vec4 aColor;" +
|
|
|
2155 |
|
|
|
2156 |
"uniform mat4 uView;" +
|
|
|
2157 |
"uniform mat4 uProjection;" +
|
|
|
2158 |
"uniform float pointSize;" +
|
|
|
2159 |
|
|
|
2160 |
"void main(void) {" +
|
|
|
2161 |
" frontColor = aColor;" +
|
|
|
2162 |
" gl_PointSize = pointSize;" +
|
|
|
2163 |
" gl_Position = uProjection * uView * vec4(aVertex, 1.0);" +
|
|
|
2164 |
"}";
|
|
|
2165 |
|
|
|
2166 |
var fShaderSrcUnlitShape =
|
|
|
2167 |
"#ifdef GL_ES\n" +
|
|
|
2168 |
"precision highp float;\n" +
|
|
|
2169 |
"#endif\n" +
|
|
|
2170 |
|
|
|
2171 |
"varying vec4 frontColor;" +
|
|
|
2172 |
|
|
|
2173 |
"void main(void){" +
|
|
|
2174 |
" gl_FragColor = frontColor;" +
|
|
|
2175 |
"}";
|
|
|
2176 |
|
|
|
2177 |
// Shader for rect, text, box outlines, sphere outlines, point() and line()
|
|
|
2178 |
var vertexShaderSource2D =
|
|
|
2179 |
"varying vec4 frontColor;" +
|
|
|
2180 |
|
|
|
2181 |
"attribute vec3 Vertex;" +
|
|
|
2182 |
"attribute vec2 aTextureCoord;" +
|
|
|
2183 |
"uniform vec4 color;" +
|
|
|
2184 |
|
|
|
2185 |
"uniform mat4 model;" +
|
|
|
2186 |
"uniform mat4 view;" +
|
|
|
2187 |
"uniform mat4 projection;" +
|
|
|
2188 |
"uniform float pointSize;" +
|
|
|
2189 |
"varying vec2 vTextureCoord;"+
|
|
|
2190 |
|
|
|
2191 |
"void main(void) {" +
|
|
|
2192 |
" gl_PointSize = pointSize;" +
|
|
|
2193 |
" frontColor = color;" +
|
|
|
2194 |
" gl_Position = projection * view * model * vec4(Vertex, 1.0);" +
|
|
|
2195 |
" vTextureCoord = aTextureCoord;" +
|
|
|
2196 |
"}";
|
|
|
2197 |
|
|
|
2198 |
var fragmentShaderSource2D =
|
|
|
2199 |
"#ifdef GL_ES\n" +
|
|
|
2200 |
"precision highp float;\n" +
|
|
|
2201 |
"#endif\n" +
|
|
|
2202 |
|
|
|
2203 |
"varying vec4 frontColor;" +
|
|
|
2204 |
"varying vec2 vTextureCoord;"+
|
|
|
2205 |
|
|
|
2206 |
"uniform sampler2D uSampler;"+
|
|
|
2207 |
"uniform int picktype;"+
|
|
|
2208 |
|
|
|
2209 |
"void main(void){" +
|
|
|
2210 |
" if(picktype == 0){"+
|
|
|
2211 |
" gl_FragColor = frontColor;" +
|
|
|
2212 |
" }" +
|
|
|
2213 |
" else if(picktype == 1){"+
|
|
|
2214 |
" float alpha = texture2D(uSampler, vTextureCoord).a;"+
|
|
|
2215 |
" gl_FragColor = vec4(frontColor.rgb*alpha, alpha);\n"+
|
|
|
2216 |
" }"+
|
|
|
2217 |
"}";
|
|
|
2218 |
|
|
|
2219 |
var webglMaxTempsWorkaround = /Windows/.test(navigator.userAgent);
|
|
|
2220 |
|
|
|
2221 |
// Vertex shader for boxes and spheres
|
|
|
2222 |
var vertexShaderSource3D =
|
|
|
2223 |
"varying vec4 frontColor;" +
|
|
|
2224 |
|
|
|
2225 |
"attribute vec3 Vertex;" +
|
|
|
2226 |
"attribute vec3 Normal;" +
|
|
|
2227 |
"attribute vec4 aColor;" +
|
|
|
2228 |
"attribute vec2 aTexture;" +
|
|
|
2229 |
"varying vec2 vTexture;" +
|
|
|
2230 |
|
|
|
2231 |
"uniform vec4 color;" +
|
|
|
2232 |
|
|
|
2233 |
"uniform bool usingMat;" +
|
|
|
2234 |
"uniform vec3 specular;" +
|
|
|
2235 |
"uniform vec3 mat_emissive;" +
|
|
|
2236 |
"uniform vec3 mat_ambient;" +
|
|
|
2237 |
"uniform vec3 mat_specular;" +
|
|
|
2238 |
"uniform float shininess;" +
|
|
|
2239 |
|
|
|
2240 |
"uniform mat4 model;" +
|
|
|
2241 |
"uniform mat4 view;" +
|
|
|
2242 |
"uniform mat4 projection;" +
|
|
|
2243 |
"uniform mat4 normalTransform;" +
|
|
|
2244 |
|
|
|
2245 |
"uniform int lightCount;" +
|
|
|
2246 |
"uniform vec3 falloff;" +
|
|
|
2247 |
|
|
|
2248 |
// careful changing the order of these fields. Some cards
|
|
|
2249 |
// have issues with memory alignment
|
|
|
2250 |
"struct Light {" +
|
|
|
2251 |
" int type;" +
|
|
|
2252 |
" vec3 color;" +
|
|
|
2253 |
" vec3 position;" +
|
|
|
2254 |
" vec3 direction;" +
|
|
|
2255 |
" float angle;" +
|
|
|
2256 |
" vec3 halfVector;" +
|
|
|
2257 |
" float concentration;" +
|
|
|
2258 |
"};" +
|
|
|
2259 |
|
|
|
2260 |
// nVidia cards have issues with arrays of structures
|
|
|
2261 |
// so instead we create 8 instances of Light
|
|
|
2262 |
"uniform Light lights0;" +
|
|
|
2263 |
"uniform Light lights1;" +
|
|
|
2264 |
"uniform Light lights2;" +
|
|
|
2265 |
"uniform Light lights3;" +
|
|
|
2266 |
"uniform Light lights4;" +
|
|
|
2267 |
"uniform Light lights5;" +
|
|
|
2268 |
"uniform Light lights6;" +
|
|
|
2269 |
"uniform Light lights7;" +
|
|
|
2270 |
|
|
|
2271 |
// GLSL does not support switch
|
|
|
2272 |
"Light getLight(int index){" +
|
|
|
2273 |
" if(index == 0) return lights0;" +
|
|
|
2274 |
" if(index == 1) return lights1;" +
|
|
|
2275 |
" if(index == 2) return lights2;" +
|
|
|
2276 |
" if(index == 3) return lights3;" +
|
|
|
2277 |
" if(index == 4) return lights4;" +
|
|
|
2278 |
" if(index == 5) return lights5;" +
|
|
|
2279 |
" if(index == 6) return lights6;" +
|
|
|
2280 |
// Do not use a conditional for the last return statement
|
|
|
2281 |
// because some video cards will fail and complain that
|
|
|
2282 |
// "not all paths return"
|
|
|
2283 |
" return lights7;" +
|
|
|
2284 |
"}" +
|
|
|
2285 |
|
|
|
2286 |
"void AmbientLight( inout vec3 totalAmbient, in vec3 ecPos, in Light light ) {" +
|
|
|
2287 |
// Get the vector from the light to the vertex
|
|
|
2288 |
// Get the distance from the current vector to the light position
|
|
|
2289 |
" float d = length( light.position - ecPos );" +
|
|
|
2290 |
" float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" +
|
|
|
2291 |
" totalAmbient += light.color * attenuation;" +
|
|
|
2292 |
"}" +
|
|
|
2293 |
|
|
|
2294 |
"void DirectionalLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
|
|
|
2295 |
" float powerfactor = 0.0;" +
|
|
|
2296 |
" float nDotVP = max(0.0, dot( vertNormal, normalize(-light.position) ));" +
|
|
|
2297 |
" float nDotVH = max(0.0, dot( vertNormal, normalize(-light.position-normalize(ecPos) )));" +
|
|
|
2298 |
|
|
|
2299 |
" if( nDotVP != 0.0 ){" +
|
|
|
2300 |
" powerfactor = pow( nDotVH, shininess );" +
|
|
|
2301 |
" }" +
|
|
|
2302 |
|
|
|
2303 |
" col += light.color * nDotVP;" +
|
|
|
2304 |
" spec += specular * powerfactor;" +
|
|
|
2305 |
"}" +
|
|
|
2306 |
|
|
|
2307 |
"void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
|
|
|
2308 |
" float powerfactor;" +
|
|
|
2309 |
|
|
|
2310 |
// Get the vector from the light to the vertex
|
|
|
2311 |
" vec3 VP = light.position - ecPos;" +
|
|
|
2312 |
|
|
|
2313 |
// Get the distance from the current vector to the light position
|
|
|
2314 |
" float d = length( VP ); " +
|
|
|
2315 |
|
|
|
2316 |
// Normalize the light ray so it can be used in the dot product operation.
|
|
|
2317 |
" VP = normalize( VP );" +
|
|
|
2318 |
|
|
|
2319 |
" float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" +
|
|
|
2320 |
|
|
|
2321 |
" float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
|
|
|
2322 |
" vec3 halfVector = normalize( VP - normalize(ecPos) );" +
|
|
|
2323 |
" float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
|
|
|
2324 |
|
|
|
2325 |
" if( nDotVP == 0.0) {" +
|
|
|
2326 |
" powerfactor = 0.0;" +
|
|
|
2327 |
" }" +
|
|
|
2328 |
" else{" +
|
|
|
2329 |
" powerfactor = pow( nDotHV, shininess );" +
|
|
|
2330 |
" }" +
|
|
|
2331 |
|
|
|
2332 |
" spec += specular * powerfactor * attenuation;" +
|
|
|
2333 |
" col += light.color * nDotVP * attenuation;" +
|
|
|
2334 |
"}" +
|
|
|
2335 |
|
|
|
2336 |
/*
|
|
|
2337 |
*/
|
|
|
2338 |
"void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
|
|
|
2339 |
" float spotAttenuation;" +
|
|
|
2340 |
" float powerfactor;" +
|
|
|
2341 |
|
|
|
2342 |
// calculate the vector from the current vertex to the light.
|
|
|
2343 |
" vec3 VP = light.position - ecPos; " +
|
|
|
2344 |
" vec3 ldir = normalize( -light.direction );" +
|
|
|
2345 |
|
|
|
2346 |
// get the distance from the spotlight and the vertex
|
|
|
2347 |
" float d = length( VP );" +
|
|
|
2348 |
" VP = normalize( VP );" +
|
|
|
2349 |
|
|
|
2350 |
" float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ) );" +
|
|
|
2351 |
|
|
|
2352 |
// dot product of the vector from vertex to light and light direction.
|
|
|
2353 |
" float spotDot = dot( VP, ldir );" +
|
|
|
2354 |
|
|
|
2355 |
// if the vertex falls inside the cone
|
|
|
2356 |
(webglMaxTempsWorkaround ? // Windows reports max temps error if light.angle is used
|
|
|
2357 |
" spotAttenuation = 1.0; " :
|
|
|
2358 |
" if( spotDot > cos( light.angle ) ) {" +
|
|
|
2359 |
" spotAttenuation = pow( spotDot, light.concentration );" +
|
|
|
2360 |
" }" +
|
|
|
2361 |
" else{" +
|
|
|
2362 |
" spotAttenuation = 0.0;" +
|
|
|
2363 |
" }" +
|
|
|
2364 |
" attenuation *= spotAttenuation;" +
|
|
|
2365 |
"") +
|
|
|
2366 |
|
|
|
2367 |
" float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
|
|
|
2368 |
" vec3 halfVector = normalize( VP - normalize(ecPos) );" +
|
|
|
2369 |
" float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
|
|
|
2370 |
|
|
|
2371 |
" if( nDotVP == 0.0 ) {" +
|
|
|
2372 |
" powerfactor = 0.0;" +
|
|
|
2373 |
" }" +
|
|
|
2374 |
" else {" +
|
|
|
2375 |
" powerfactor = pow( nDotHV, shininess );" +
|
|
|
2376 |
" }" +
|
|
|
2377 |
|
|
|
2378 |
" spec += specular * powerfactor * attenuation;" +
|
|
|
2379 |
" col += light.color * nDotVP * attenuation;" +
|
|
|
2380 |
"}" +
|
|
|
2381 |
|
|
|
2382 |
"void main(void) {" +
|
|
|
2383 |
" vec3 finalAmbient = vec3( 0.0, 0.0, 0.0 );" +
|
|
|
2384 |
" vec3 finalDiffuse = vec3( 0.0, 0.0, 0.0 );" +
|
|
|
2385 |
" vec3 finalSpecular = vec3( 0.0, 0.0, 0.0 );" +
|
|
|
2386 |
|
|
|
2387 |
" vec4 col = color;" +
|
|
|
2388 |
|
|
|
2389 |
" if(color[0] == -1.0){" +
|
|
|
2390 |
" col = aColor;" +
|
|
|
2391 |
" }" +
|
|
|
2392 |
|
|
|
2393 |
// We use the sphere vertices as the normals when we create the sphere buffer.
|
|
|
2394 |
// But this only works if the sphere vertices are unit length, so we
|
|
|
2395 |
// have to normalize the normals here. Since this is only required for spheres
|
|
|
2396 |
// we could consider placing this in a conditional later on.
|
|
|
2397 |
" vec3 norm = normalize(vec3( normalTransform * vec4( Normal, 0.0 ) ));" +
|
|
|
2398 |
|
|
|
2399 |
" vec4 ecPos4 = view * model * vec4(Vertex,1.0);" +
|
|
|
2400 |
" vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" +
|
|
|
2401 |
|
|
|
2402 |
// If there were no lights this draw call, just use the
|
|
|
2403 |
// assigned fill color of the shape and the specular value
|
|
|
2404 |
" if( lightCount == 0 ) {" +
|
|
|
2405 |
" frontColor = col + vec4(mat_specular,1.0);" +
|
|
|
2406 |
" }" +
|
|
|
2407 |
" else {" +
|
|
|
2408 |
// WebGL forces us to iterate over a constant value
|
|
|
2409 |
// so we can't iterate using lightCount
|
|
|
2410 |
" for( int i = 0; i < 8; i++ ) {" +
|
|
|
2411 |
" Light l = getLight(i);" +
|
|
|
2412 |
|
|
|
2413 |
// We can stop iterating if we know we have gone past
|
|
|
2414 |
// the number of lights which are on
|
|
|
2415 |
" if( i >= lightCount ){" +
|
|
|
2416 |
" break;" +
|
|
|
2417 |
" }" +
|
|
|
2418 |
|
|
|
2419 |
" if( l.type == 0 ) {" +
|
|
|
2420 |
" AmbientLight( finalAmbient, ecPos, l );" +
|
|
|
2421 |
" }" +
|
|
|
2422 |
" else if( l.type == 1 ) {" +
|
|
|
2423 |
" DirectionalLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
|
|
|
2424 |
" }" +
|
|
|
2425 |
" else if( l.type == 2 ) {" +
|
|
|
2426 |
" PointLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
|
|
|
2427 |
" }" +
|
|
|
2428 |
" else {" +
|
|
|
2429 |
" SpotLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
|
|
|
2430 |
" }" +
|
|
|
2431 |
" }" +
|
|
|
2432 |
|
|
|
2433 |
" if( usingMat == false ) {" +
|
|
|
2434 |
" frontColor = vec4(" +
|
|
|
2435 |
" vec3(col) * finalAmbient +" +
|
|
|
2436 |
" vec3(col) * finalDiffuse +" +
|
|
|
2437 |
" vec3(col) * finalSpecular," +
|
|
|
2438 |
" col[3] );" +
|
|
|
2439 |
" }" +
|
|
|
2440 |
" else{" +
|
|
|
2441 |
" frontColor = vec4( " +
|
|
|
2442 |
" mat_emissive + " +
|
|
|
2443 |
" (vec3(col) * mat_ambient * finalAmbient) + " +
|
|
|
2444 |
" (vec3(col) * finalDiffuse) + " +
|
|
|
2445 |
" (mat_specular * finalSpecular), " +
|
|
|
2446 |
" col[3] );" +
|
|
|
2447 |
" }" +
|
|
|
2448 |
" }" +
|
|
|
2449 |
|
|
|
2450 |
" vTexture.xy = aTexture.xy;" +
|
|
|
2451 |
" gl_Position = projection * view * model * vec4( Vertex, 1.0 );" +
|
|
|
2452 |
"}";
|
|
|
2453 |
|
|
|
2454 |
var fragmentShaderSource3D =
|
|
|
2455 |
"#ifdef GL_ES\n" +
|
|
|
2456 |
"precision highp float;\n" +
|
|
|
2457 |
"#endif\n" +
|
|
|
2458 |
|
|
|
2459 |
"varying vec4 frontColor;" +
|
|
|
2460 |
|
|
|
2461 |
"uniform sampler2D sampler;" +
|
|
|
2462 |
"uniform bool usingTexture;" +
|
|
|
2463 |
"varying vec2 vTexture;" +
|
|
|
2464 |
|
|
|
2465 |
// In Processing, when a texture is used, the fill color is ignored
|
|
|
2466 |
// vec4(1.0,1.0,1.0,0.5)
|
|
|
2467 |
"void main(void){" +
|
|
|
2468 |
" if(usingTexture){" +
|
|
|
2469 |
" gl_FragColor = vec4(texture2D(sampler, vTexture.xy)) * frontColor;" +
|
|
|
2470 |
" }"+
|
|
|
2471 |
" else{" +
|
|
|
2472 |
" gl_FragColor = frontColor;" +
|
|
|
2473 |
" }" +
|
|
|
2474 |
"}";
|
|
|
2475 |
|
|
|
2476 |
////////////////////////////////////////////////////////////////////////////
|
|
|
2477 |
// 3D Functions
|
|
|
2478 |
////////////////////////////////////////////////////////////////////////////
|
|
|
2479 |
|
|
|
2480 |
/*
|
|
|
2481 |
* Sets a uniform variable in a program object to a particular
|
|
|
2482 |
* value. Before calling this function, ensure the correct
|
|
|
2483 |
* program object has been installed as part of the current
|
|
|
2484 |
* rendering state by calling useProgram.
|
|
|
2485 |
*
|
|
|
2486 |
* On some systems, if the variable exists in the shader but isn't used,
|
|
|
2487 |
* the compiler will optimize it out and this function will fail.
|
|
|
2488 |
*
|
|
|
2489 |
* @param {WebGLProgram} programObj program object returned from
|
|
|
2490 |
* createProgramObject
|
|
|
2491 |
* @param {String} varName the name of the variable in the shader
|
|
|
2492 |
* @param {float | Array} varValue either a scalar value or an Array
|
|
|
2493 |
*
|
|
|
2494 |
* @returns none
|
|
|
2495 |
*
|
|
|
2496 |
* @see uniformi
|
|
|
2497 |
* @see uniformMatrix
|
|
|
2498 |
*/
|
|
|
2499 |
function uniformf(cacheId, programObj, varName, varValue) {
|
|
|
2500 |
var varLocation = curContextCache.locations[cacheId];
|
|
|
2501 |
if(varLocation === undef) {
|
|
|
2502 |
varLocation = curContext.getUniformLocation(programObj, varName);
|
|
|
2503 |
curContextCache.locations[cacheId] = varLocation;
|
|
|
2504 |
}
|
|
|
2505 |
// the variable won't be found if it was optimized out.
|
|
|
2506 |
if (varLocation !== null) {
|
|
|
2507 |
if (varValue.length === 4) {
|
|
|
2508 |
curContext.uniform4fv(varLocation, varValue);
|
|
|
2509 |
} else if (varValue.length === 3) {
|
|
|
2510 |
curContext.uniform3fv(varLocation, varValue);
|
|
|
2511 |
} else if (varValue.length === 2) {
|
|
|
2512 |
curContext.uniform2fv(varLocation, varValue);
|
|
|
2513 |
} else {
|
|
|
2514 |
curContext.uniform1f(varLocation, varValue);
|
|
|
2515 |
}
|
|
|
2516 |
}
|
|
|
2517 |
}
|
|
|
2518 |
|
|
|
2519 |
/**
|
|
|
2520 |
* Sets a uniform int or int array in a program object to a particular
|
|
|
2521 |
* value. Before calling this function, ensure the correct
|
|
|
2522 |
* program object has been installed as part of the current
|
|
|
2523 |
* rendering state.
|
|
|
2524 |
*
|
|
|
2525 |
* On some systems, if the variable exists in the shader but isn't used,
|
|
|
2526 |
* the compiler will optimize it out and this function will fail.
|
|
|
2527 |
*
|
|
|
2528 |
* @param {WebGLProgram} programObj program object returned from
|
|
|
2529 |
* createProgramObject
|
|
|
2530 |
* @param {String} varName the name of the variable in the shader
|
|
|
2531 |
* @param {int | Array} varValue either a scalar value or an Array
|
|
|
2532 |
*
|
|
|
2533 |
* @returns none
|
|
|
2534 |
*
|
|
|
2535 |
* @see uniformf
|
|
|
2536 |
* @see uniformMatrix
|
|
|
2537 |
*/
|
|
|
2538 |
function uniformi(cacheId, programObj, varName, varValue) {
|
|
|
2539 |
var varLocation = curContextCache.locations[cacheId];
|
|
|
2540 |
if(varLocation === undef) {
|
|
|
2541 |
varLocation = curContext.getUniformLocation(programObj, varName);
|
|
|
2542 |
curContextCache.locations[cacheId] = varLocation;
|
|
|
2543 |
}
|
|
|
2544 |
// the variable won't be found if it was optimized out.
|
|
|
2545 |
if (varLocation !== null) {
|
|
|
2546 |
if (varValue.length === 4) {
|
|
|
2547 |
curContext.uniform4iv(varLocation, varValue);
|
|
|
2548 |
} else if (varValue.length === 3) {
|
|
|
2549 |
curContext.uniform3iv(varLocation, varValue);
|
|
|
2550 |
} else if (varValue.length === 2) {
|
|
|
2551 |
curContext.uniform2iv(varLocation, varValue);
|
|
|
2552 |
} else {
|
|
|
2553 |
curContext.uniform1i(varLocation, varValue);
|
|
|
2554 |
}
|
|
|
2555 |
}
|
|
|
2556 |
}
|
|
|
2557 |
|
|
|
2558 |
/**
|
|
|
2559 |
* Sets the value of a uniform matrix variable in a program
|
|
|
2560 |
* object. Before calling this function, ensure the correct
|
|
|
2561 |
* program object has been installed as part of the current
|
|
|
2562 |
* rendering state.
|
|
|
2563 |
*
|
|
|
2564 |
* On some systems, if the variable exists in the shader but
|
|
|
2565 |
* isn't used, the compiler will optimize it out and this
|
|
|
2566 |
* function will fail.
|
|
|
2567 |
*
|
|
|
2568 |
* @param {WebGLProgram} programObj program object returned from
|
|
|
2569 |
* createProgramObject
|
|
|
2570 |
* @param {String} varName the name of the variable in the shader
|
|
|
2571 |
* @param {boolean} transpose must be false
|
|
|
2572 |
* @param {Array} matrix an array of 4, 9 or 16 values
|
|
|
2573 |
*
|
|
|
2574 |
* @returns none
|
|
|
2575 |
*
|
|
|
2576 |
* @see uniformi
|
|
|
2577 |
* @see uniformf
|
|
|
2578 |
*/
|
|
|
2579 |
function uniformMatrix(cacheId, programObj, varName, transpose, matrix) {
|
|
|
2580 |
var varLocation = curContextCache.locations[cacheId];
|
|
|
2581 |
if(varLocation === undef) {
|
|
|
2582 |
varLocation = curContext.getUniformLocation(programObj, varName);
|
|
|
2583 |
curContextCache.locations[cacheId] = varLocation;
|
|
|
2584 |
}
|
|
|
2585 |
// the variable won't be found if it was optimized out.
|
|
|
2586 |
if (varLocation !== -1) {
|
|
|
2587 |
if (matrix.length === 16) {
|
|
|
2588 |
curContext.uniformMatrix4fv(varLocation, transpose, matrix);
|
|
|
2589 |
} else if (matrix.length === 9) {
|
|
|
2590 |
curContext.uniformMatrix3fv(varLocation, transpose, matrix);
|
|
|
2591 |
} else {
|
|
|
2592 |
curContext.uniformMatrix2fv(varLocation, transpose, matrix);
|
|
|
2593 |
}
|
|
|
2594 |
}
|
|
|
2595 |
}
|
|
|
2596 |
|
|
|
2597 |
/**
|
|
|
2598 |
* Binds the VBO, sets the vertex attribute data for the program
|
|
|
2599 |
* object and enables the attribute.
|
|
|
2600 |
*
|
|
|
2601 |
* On some systems, if the attribute exists in the shader but
|
|
|
2602 |
* isn't used, the compiler will optimize it out and this
|
|
|
2603 |
* function will fail.
|
|
|
2604 |
*
|
|
|
2605 |
* @param {WebGLProgram} programObj program object returned from
|
|
|
2606 |
* createProgramObject
|
|
|
2607 |
* @param {String} varName the name of the variable in the shader
|
|
|
2608 |
* @param {int} size the number of components per vertex attribute
|
|
|
2609 |
* @param {WebGLBuffer} VBO Vertex Buffer Object
|
|
|
2610 |
*
|
|
|
2611 |
* @returns none
|
|
|
2612 |
*
|
|
|
2613 |
* @see disableVertexAttribPointer
|
|
|
2614 |
*/
|
|
|
2615 |
function vertexAttribPointer(cacheId, programObj, varName, size, VBO) {
|
|
|
2616 |
var varLocation = curContextCache.attributes[cacheId];
|
|
|
2617 |
if(varLocation === undef) {
|
|
|
2618 |
varLocation = curContext.getAttribLocation(programObj, varName);
|
|
|
2619 |
curContextCache.attributes[cacheId] = varLocation;
|
|
|
2620 |
}
|
|
|
2621 |
if (varLocation !== -1) {
|
|
|
2622 |
curContext.bindBuffer(curContext.ARRAY_BUFFER, VBO);
|
|
|
2623 |
curContext.vertexAttribPointer(varLocation, size, curContext.FLOAT, false, 0, 0);
|
|
|
2624 |
curContext.enableVertexAttribArray(varLocation);
|
|
|
2625 |
}
|
|
|
2626 |
}
|
|
|
2627 |
|
|
|
2628 |
/**
|
|
|
2629 |
* Disables a program object attribute from being sent to WebGL.
|
|
|
2630 |
*
|
|
|
2631 |
* @param {WebGLProgram} programObj program object returned from
|
|
|
2632 |
* createProgramObject
|
|
|
2633 |
* @param {String} varName name of the attribute
|
|
|
2634 |
*
|
|
|
2635 |
* @returns none
|
|
|
2636 |
*
|
|
|
2637 |
* @see vertexAttribPointer
|
|
|
2638 |
*/
|
|
|
2639 |
function disableVertexAttribPointer(cacheId, programObj, varName){
|
|
|
2640 |
var varLocation = curContextCache.attributes[cacheId];
|
|
|
2641 |
if(varLocation === undef) {
|
|
|
2642 |
varLocation = curContext.getAttribLocation(programObj, varName);
|
|
|
2643 |
curContextCache.attributes[cacheId] = varLocation;
|
|
|
2644 |
}
|
|
|
2645 |
if (varLocation !== -1) {
|
|
|
2646 |
curContext.disableVertexAttribArray(varLocation);
|
|
|
2647 |
}
|
|
|
2648 |
}
|
|
|
2649 |
|
|
|
2650 |
/**
|
|
|
2651 |
* Creates a WebGL program object.
|
|
|
2652 |
*
|
|
|
2653 |
* @param {String} vetexShaderSource
|
|
|
2654 |
* @param {String} fragmentShaderSource
|
|
|
2655 |
*
|
|
|
2656 |
* @returns {WebGLProgram} A program object
|
|
|
2657 |
*/
|
|
|
2658 |
var createProgramObject = function(curContext, vetexShaderSource, fragmentShaderSource) {
|
|
|
2659 |
var vertexShaderObject = curContext.createShader(curContext.VERTEX_SHADER);
|
|
|
2660 |
curContext.shaderSource(vertexShaderObject, vetexShaderSource);
|
|
|
2661 |
curContext.compileShader(vertexShaderObject);
|
|
|
2662 |
if (!curContext.getShaderParameter(vertexShaderObject, curContext.COMPILE_STATUS)) {
|
|
|
2663 |
throw curContext.getShaderInfoLog(vertexShaderObject);
|
|
|
2664 |
}
|
|
|
2665 |
|
|
|
2666 |
var fragmentShaderObject = curContext.createShader(curContext.FRAGMENT_SHADER);
|
|
|
2667 |
curContext.shaderSource(fragmentShaderObject, fragmentShaderSource);
|
|
|
2668 |
curContext.compileShader(fragmentShaderObject);
|
|
|
2669 |
if (!curContext.getShaderParameter(fragmentShaderObject, curContext.COMPILE_STATUS)) {
|
|
|
2670 |
throw curContext.getShaderInfoLog(fragmentShaderObject);
|
|
|
2671 |
}
|
|
|
2672 |
|
|
|
2673 |
var programObject = curContext.createProgram();
|
|
|
2674 |
curContext.attachShader(programObject, vertexShaderObject);
|
|
|
2675 |
curContext.attachShader(programObject, fragmentShaderObject);
|
|
|
2676 |
curContext.linkProgram(programObject);
|
|
|
2677 |
if (!curContext.getProgramParameter(programObject, curContext.LINK_STATUS)) {
|
|
|
2678 |
throw "Error linking shaders.";
|
|
|
2679 |
}
|
|
|
2680 |
|
|
|
2681 |
return programObject;
|
|
|
2682 |
};
|
|
|
2683 |
|
|
|
2684 |
////////////////////////////////////////////////////////////////////////////
|
|
|
2685 |
// 2D/3D drawing handling
|
|
|
2686 |
////////////////////////////////////////////////////////////////////////////
|
|
|
2687 |
var imageModeCorner = function(x, y, w, h, whAreSizes) {
|
|
|
2688 |
return {
|
|
|
2689 |
x: x,
|
|
|
2690 |
y: y,
|
|
|
2691 |
w: w,
|
|
|
2692 |
h: h
|
|
|
2693 |
};
|
|
|
2694 |
};
|
|
|
2695 |
var imageModeConvert = imageModeCorner;
|
|
|
2696 |
|
|
|
2697 |
var imageModeCorners = function(x, y, w, h, whAreSizes) {
|
|
|
2698 |
return {
|
|
|
2699 |
x: x,
|
|
|
2700 |
y: y,
|
|
|
2701 |
w: whAreSizes ? w : w - x,
|
|
|
2702 |
h: whAreSizes ? h : h - y
|
|
|
2703 |
};
|
|
|
2704 |
};
|
|
|
2705 |
|
|
|
2706 |
var imageModeCenter = function(x, y, w, h, whAreSizes) {
|
|
|
2707 |
return {
|
|
|
2708 |
x: x - w / 2,
|
|
|
2709 |
y: y - h / 2,
|
|
|
2710 |
w: w,
|
|
|
2711 |
h: h
|
|
|
2712 |
};
|
|
|
2713 |
};
|
|
|
2714 |
|
|
|
2715 |
// Objects for shared, 2D and 3D contexts
|
|
|
2716 |
var DrawingShared = function(){};
|
|
|
2717 |
var Drawing2D = function(){};
|
|
|
2718 |
var Drawing3D = function(){};
|
|
|
2719 |
var DrawingPre = function(){};
|
|
|
2720 |
|
|
|
2721 |
// Setup the prototype chain
|
|
|
2722 |
Drawing2D.prototype = new DrawingShared();
|
|
|
2723 |
Drawing2D.prototype.constructor = Drawing2D;
|
|
|
2724 |
Drawing3D.prototype = new DrawingShared();
|
|
|
2725 |
Drawing3D.prototype.constructor = Drawing3D;
|
|
|
2726 |
DrawingPre.prototype = new DrawingShared();
|
|
|
2727 |
DrawingPre.prototype.constructor = DrawingPre;
|
|
|
2728 |
|
|
|
2729 |
// A no-op function for when the user calls 3D functions from a 2D sketch
|
|
|
2730 |
// We can change this to a throw or console.error() later if we want
|
|
|
2731 |
DrawingShared.prototype.a3DOnlyFunction = nop;
|
|
|
2732 |
|
|
|
2733 |
////////////////////////////////////////////////////////////////////////////
|
|
|
2734 |
// Char handling
|
|
|
2735 |
////////////////////////////////////////////////////////////////////////////
|
|
|
2736 |
var charMap = {};
|
|
|
2737 |
|
|
|
2738 |
var Char = p.Character = function(chr) {
|
|
|
2739 |
if (typeof chr === 'string' && chr.length === 1) {
|
|
|
2740 |
this.code = chr.charCodeAt(0);
|
|
|
2741 |
} else if (typeof chr === 'number') {
|
|
|
2742 |
this.code = chr;
|
|
|
2743 |
} else if (chr instanceof Char) {
|
|
|
2744 |
this.code = chr;
|
|
|
2745 |
} else {
|
|
|
2746 |
this.code = NaN;
|
|
|
2747 |
}
|
|
|
2748 |
|
|
|
2749 |
return (charMap[this.code] === undef) ? charMap[this.code] = this : charMap[this.code];
|
|
|
2750 |
};
|
|
|
2751 |
|
|
|
2752 |
Char.prototype.toString = function() {
|
|
|
2753 |
return String.fromCharCode(this.code);
|
|
|
2754 |
};
|
|
|
2755 |
|
|
|
2756 |
Char.prototype.valueOf = function() {
|
|
|
2757 |
return this.code;
|
|
|
2758 |
};
|
|
|
2759 |
|
|
|
2760 |
/**
|
|
|
2761 |
* Datatype for storing shapes. Processing can currently load and display SVG (Scalable Vector Graphics) shapes.
|
|
|
2762 |
* 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.
|
|
|
2763 |
* The <b>PShape</b> object contain a group of methods, linked below, that can operate on the shape data.
|
|
|
2764 |
* <br><br>The <b>loadShape()</b> method supports SVG files created with Inkscape and Adobe Illustrator.
|
|
|
2765 |
* It is not a full SVG implementation, but offers some straightforward support for handling vector data.
|
|
|
2766 |
*
|
|
|
2767 |
* @param {int} family the shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY
|
|
|
2768 |
*
|
|
|
2769 |
* @see #shape()
|
|
|
2770 |
* @see #loadShape()
|
|
|
2771 |
* @see #shapeMode()
|
|
|
2772 |
*/
|
|
|
2773 |
var PShape = p.PShape = function(family) {
|
|
|
2774 |
this.family = family || PConstants.GROUP;
|
|
|
2775 |
this.visible = true;
|
|
|
2776 |
this.style = true;
|
|
|
2777 |
this.children = [];
|
|
|
2778 |
this.nameTable = [];
|
|
|
2779 |
this.params = [];
|
|
|
2780 |
this.name = "";
|
|
|
2781 |
this.image = null; //type PImage
|
|
|
2782 |
this.matrix = null;
|
|
|
2783 |
this.kind = null;
|
|
|
2784 |
this.close = null;
|
|
|
2785 |
this.width = null;
|
|
|
2786 |
this.height = null;
|
|
|
2787 |
this.parent = null;
|
|
|
2788 |
};
|
|
|
2789 |
/**
|
|
|
2790 |
* PShape methods
|
|
|
2791 |
* missing: findChild(), apply(), contains(), findChild(), getPrimitive(), getParams(), getVertex() , getVertexCount(),
|
|
|
2792 |
* getVertexCode() , getVertexCodes() , getVertexCodeCount(), getVertexX(), getVertexY(), getVertexZ()
|
|
|
2793 |
*/
|
|
|
2794 |
PShape.prototype = {
|
|
|
2795 |
/**
|
|
|
2796 |
* @member PShape
|
|
|
2797 |
* 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.
|
|
|
2798 |
* <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
|
|
|
2799 |
* For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
|
|
|
2800 |
*
|
|
|
2801 |
* @return {boolean} returns "true" if the image is set to be visible, "false" if not
|
|
|
2802 |
*/
|
|
|
2803 |
isVisible: function(){
|
|
|
2804 |
return this.visible;
|
|
|
2805 |
},
|
|
|
2806 |
/**
|
|
|
2807 |
* @member PShape
|
|
|
2808 |
* The setVisible() function sets the shape to be visible or invisible. This is determined by the value of the <b>visible</b> parameter.
|
|
|
2809 |
* <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
|
|
|
2810 |
* For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
|
|
|
2811 |
*
|
|
|
2812 |
* @param {boolean} visible "false" makes the shape invisible and "true" makes it visible
|
|
|
2813 |
*/
|
|
|
2814 |
setVisible: function (visible){
|
|
|
2815 |
this.visible = visible;
|
|
|
2816 |
},
|
|
|
2817 |
/**
|
|
|
2818 |
* @member PShape
|
|
|
2819 |
* 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.
|
|
|
2820 |
* Overrides this shape's style information and uses PGraphics styles and colors. Identical to ignoreStyles(true). Also disables styles for all child shapes.
|
|
|
2821 |
*/
|
|
|
2822 |
disableStyle: function(){
|
|
|
2823 |
this.style = false;
|
|
|
2824 |
for(var i = 0, j=this.children.length; i<j; i++) {
|
|
|
2825 |
this.children[i].disableStyle();
|
|
|
2826 |
}
|
|
|
2827 |
},
|
|
|
2828 |
/**
|
|
|
2829 |
* @member PShape
|
|
|
2830 |
* 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.
|
|
|
2831 |
*/
|
|
|
2832 |
enableStyle: function(){
|
|
|
2833 |
this.style = true;
|
|
|
2834 |
for(var i = 0, j=this.children.length; i<j; i++) {
|
|
|
2835 |
this.children[i].enableStyle();
|
|
|
2836 |
}
|
|
|
2837 |
},
|
|
|
2838 |
/**
|
|
|
2839 |
* @member PShape
|
|
|
2840 |
* The getFamily function returns the shape type
|
|
|
2841 |
*
|
|
|
2842 |
* @return {int} the shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY
|
|
|
2843 |
*/
|
|
|
2844 |
getFamily: function(){
|
|
|
2845 |
return this.family;
|
|
|
2846 |
},
|
|
|
2847 |
/**
|
|
|
2848 |
* @member PShape
|
|
|
2849 |
* The getWidth() function gets the width of the drawing area (not necessarily the shape boundary).
|
|
|
2850 |
*/
|
|
|
2851 |
getWidth: function(){
|
|
|
2852 |
return this.width;
|
|
|
2853 |
},
|
|
|
2854 |
/**
|
|
|
2855 |
* @member PShape
|
|
|
2856 |
* The getHeight() function gets the height of the drawing area (not necessarily the shape boundary).
|
|
|
2857 |
*/
|
|
|
2858 |
getHeight: function(){
|
|
|
2859 |
return this.height;
|
|
|
2860 |
},
|
|
|
2861 |
/**
|
|
|
2862 |
* @member PShape
|
|
|
2863 |
* The setName() function sets the name of the shape
|
|
|
2864 |
*
|
|
|
2865 |
* @param {String} name the name of the shape
|
|
|
2866 |
*/
|
|
|
2867 |
setName: function(name){
|
|
|
2868 |
this.name = name;
|
|
|
2869 |
},
|
|
|
2870 |
/**
|
|
|
2871 |
* @member PShape
|
|
|
2872 |
* The getName() function returns the name of the shape
|
|
|
2873 |
*
|
|
|
2874 |
* @return {String} the name of the shape
|
|
|
2875 |
*/
|
|
|
2876 |
getName: function(){
|
|
|
2877 |
return this.name;
|
|
|
2878 |
},
|
|
|
2879 |
/**
|
|
|
2880 |
* @member PShape
|
|
|
2881 |
* Called by the following (the shape() command adds the g)
|
|
|
2882 |
* PShape s = loadShapes("blah.svg");
|
|
|
2883 |
* shape(s);
|
|
|
2884 |
*/
|
|
|
2885 |
draw: function(){
|
|
|
2886 |
if (this.visible) {
|
|
|
2887 |
this.pre();
|
|
|
2888 |
this.drawImpl();
|
|
|
2889 |
this.post();
|
|
|
2890 |
}
|
|
|
2891 |
},
|
|
|
2892 |
/**
|
|
|
2893 |
* @member PShape
|
|
|
2894 |
* the drawImpl() function draws the SVG document.
|
|
|
2895 |
*/
|
|
|
2896 |
drawImpl: function(){
|
|
|
2897 |
if (this.family === PConstants.GROUP) {
|
|
|
2898 |
this.drawGroup();
|
|
|
2899 |
} else if (this.family === PConstants.PRIMITIVE) {
|
|
|
2900 |
this.drawPrimitive();
|
|
|
2901 |
} else if (this.family === PConstants.GEOMETRY) {
|
|
|
2902 |
this.drawGeometry();
|
|
|
2903 |
} else if (this.family === PConstants.PATH) {
|
|
|
2904 |
this.drawPath();
|
|
|
2905 |
}
|
|
|
2906 |
},
|
|
|
2907 |
/**
|
|
|
2908 |
* @member PShape
|
|
|
2909 |
* The drawPath() function draws the <path> part of the SVG document.
|
|
|
2910 |
*/
|
|
|
2911 |
drawPath: function(){
|
|
|
2912 |
var i, j;
|
|
|
2913 |
if (this.vertices.length === 0) { return; }
|
|
|
2914 |
p.beginShape();
|
|
|
2915 |
if (this.vertexCodes.length === 0) { // each point is a simple vertex
|
|
|
2916 |
if (this.vertices[0].length === 2) { // drawing 2D vertices
|
|
|
2917 |
for (i = 0, j = this.vertices.length; i < j; i++) {
|
|
|
2918 |
p.vertex(this.vertices[i][0], this.vertices[i][1]);
|
|
|
2919 |
}
|
|
|
2920 |
} else { // drawing 3D vertices
|
|
|
2921 |
for (i = 0, j = this.vertices.length; i < j; i++) {
|
|
|
2922 |
p.vertex(this.vertices[i][0],
|
|
|
2923 |
this.vertices[i][1],
|
|
|
2924 |
this.vertices[i][2]);
|
|
|
2925 |
}
|
|
|
2926 |
}
|
|
|
2927 |
} else { // coded set of vertices
|
|
|
2928 |
var index = 0;
|
|
|
2929 |
if (this.vertices[0].length === 2) { // drawing a 2D path
|
|
|
2930 |
for (i = 0, j = this.vertexCodes.length; i < j; i++) {
|
|
|
2931 |
if (this.vertexCodes[i] === PConstants.VERTEX) {
|
|
|
2932 |
p.vertex(this.vertices[index][0], this.vertices[index][1]);
|
|
|
2933 |
if ( this.vertices[index]["moveTo"] === true) {
|
|
|
2934 |
vertArray[vertArray.length-1]["moveTo"] = true;
|
|
|
2935 |
} else if ( this.vertices[index]["moveTo"] === false) {
|
|
|
2936 |
vertArray[vertArray.length-1]["moveTo"] = false;
|
|
|
2937 |
}
|
|
|
2938 |
p.breakShape = false;
|
|
|
2939 |
index++;
|
|
|
2940 |
} else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
|
|
|
2941 |
p.bezierVertex(this.vertices[index+0][0],
|
|
|
2942 |
this.vertices[index+0][1],
|
|
|
2943 |
this.vertices[index+1][0],
|
|
|
2944 |
this.vertices[index+1][1],
|
|
|
2945 |
this.vertices[index+2][0],
|
|
|
2946 |
this.vertices[index+2][1]);
|
|
|
2947 |
index += 3;
|
|
|
2948 |
} else if (this.vertexCodes[i] === PConstants.CURVE_VERTEX) {
|
|
|
2949 |
p.curveVertex(this.vertices[index][0],
|
|
|
2950 |
this.vertices[index][1]);
|
|
|
2951 |
index++;
|
|
|
2952 |
} else if (this.vertexCodes[i] === PConstants.BREAK) {
|
|
|
2953 |
p.breakShape = true;
|
|
|
2954 |
}
|
|
|
2955 |
}
|
|
|
2956 |
} else { // drawing a 3D path
|
|
|
2957 |
for (i = 0, j = this.vertexCodes.length; i < j; i++) {
|
|
|
2958 |
if (this.vertexCodes[i] === PConstants.VERTEX) {
|
|
|
2959 |
p.vertex(this.vertices[index][0],
|
|
|
2960 |
this.vertices[index][1],
|
|
|
2961 |
this.vertices[index][2]);
|
|
|
2962 |
if (this.vertices[index]["moveTo"] === true) {
|
|
|
2963 |
vertArray[vertArray.length-1]["moveTo"] = true;
|
|
|
2964 |
} else if (this.vertices[index]["moveTo"] === false) {
|
|
|
2965 |
vertArray[vertArray.length-1]["moveTo"] = false;
|
|
|
2966 |
}
|
|
|
2967 |
p.breakShape = false;
|
|
|
2968 |
} else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
|
|
|
2969 |
p.bezierVertex(this.vertices[index+0][0],
|
|
|
2970 |
this.vertices[index+0][1],
|
|
|
2971 |
this.vertices[index+0][2],
|
|
|
2972 |
this.vertices[index+1][0],
|
|
|
2973 |
this.vertices[index+1][1],
|
|
|
2974 |
this.vertices[index+1][2],
|
|
|
2975 |
this.vertices[index+2][0],
|
|
|
2976 |
this.vertices[index+2][1],
|
|
|
2977 |
this.vertices[index+2][2]);
|
|
|
2978 |
index += 3;
|
|
|
2979 |
} else if (this.vertexCodes[i] === PConstants.CURVE_VERTEX) {
|
|
|
2980 |
p.curveVertex(this.vertices[index][0],
|
|
|
2981 |
this.vertices[index][1],
|
|
|
2982 |
this.vertices[index][2]);
|
|
|
2983 |
index++;
|
|
|
2984 |
} else if (this.vertexCodes[i] === PConstants.BREAK) {
|
|
|
2985 |
p.breakShape = true;
|
|
|
2986 |
}
|
|
|
2987 |
}
|
|
|
2988 |
}
|
|
|
2989 |
}
|
|
|
2990 |
p.endShape(this.close ? PConstants.CLOSE : PConstants.OPEN);
|
|
|
2991 |
},
|
|
|
2992 |
/**
|
|
|
2993 |
* @member PShape
|
|
|
2994 |
* The drawGeometry() function draws the geometry part of the SVG document.
|
|
|
2995 |
*/
|
|
|
2996 |
drawGeometry: function() {
|
|
|
2997 |
var i, j;
|
|
|
2998 |
p.beginShape(this.kind);
|
|
|
2999 |
if (this.style) {
|
|
|
3000 |
for (i = 0, j = this.vertices.length; i < j; i++) {
|
|
|
3001 |
p.vertex(this.vertices[i]);
|
|
|
3002 |
}
|
|
|
3003 |
} else {
|
|
|
3004 |
for (i = 0, j = this.vertices.length; i < j; i++) {
|
|
|
3005 |
var vert = this.vertices[i];
|
|
|
3006 |
if (vert[2] === 0) {
|
|
|
3007 |
p.vertex(vert[0], vert[1]);
|
|
|
3008 |
} else {
|
|
|
3009 |
p.vertex(vert[0], vert[1], vert[2]);
|
|
|
3010 |
}
|
|
|
3011 |
}
|
|
|
3012 |
}
|
|
|
3013 |
p.endShape();
|
|
|
3014 |
},
|
|
|
3015 |
/**
|
|
|
3016 |
* @member PShape
|
|
|
3017 |
* The drawGroup() function draws the <g> part of the SVG document.
|
|
|
3018 |
*/
|
|
|
3019 |
drawGroup: function() {
|
|
|
3020 |
for (var i = 0, j = this.children.length; i < j; i++) {
|
|
|
3021 |
this.children[i].draw();
|
|
|
3022 |
}
|
|
|
3023 |
},
|
|
|
3024 |
/**
|
|
|
3025 |
* @member PShape
|
|
|
3026 |
* The drawPrimitive() function draws SVG document shape elements. These can be point, line, triangle, quad, rect, ellipse, arc, box, or sphere.
|
|
|
3027 |
*/
|
|
|
3028 |
drawPrimitive: function() {
|
|
|
3029 |
if (this.kind === PConstants.POINT) {
|
|
|
3030 |
p.point(this.params[0], this.params[1]);
|
|
|
3031 |
} else if (this.kind === PConstants.LINE) {
|
|
|
3032 |
if (this.params.length === 4) { // 2D
|
|
|
3033 |
p.line(this.params[0], this.params[1],
|
|
|
3034 |
this.params[2], this.params[3]);
|
|
|
3035 |
} else { // 3D
|
|
|
3036 |
p.line(this.params[0], this.params[1], this.params[2],
|
|
|
3037 |
this.params[3], this.params[4], this.params[5]);
|
|
|
3038 |
}
|
|
|
3039 |
} else if (this.kind === PConstants.TRIANGLE) {
|
|
|
3040 |
p.triangle(this.params[0], this.params[1],
|
|
|
3041 |
this.params[2], this.params[3],
|
|
|
3042 |
this.params[4], this.params[5]);
|
|
|
3043 |
} else if (this.kind === PConstants.QUAD) {
|
|
|
3044 |
p.quad(this.params[0], this.params[1],
|
|
|
3045 |
this.params[2], this.params[3],
|
|
|
3046 |
this.params[4], this.params[5],
|
|
|
3047 |
this.params[6], this.params[7]);
|
|
|
3048 |
} else if (this.kind === PConstants.RECT) {
|
|
|
3049 |
if (this.image !== null) {
|
|
|
3050 |
p.imageMode(PConstants.CORNER);
|
|
|
3051 |
p.image(this.image,
|
|
|
3052 |
this.params[0],
|
|
|
3053 |
this.params[1],
|
|
|
3054 |
this.params[2],
|
|
|
3055 |
this.params[3]);
|
|
|
3056 |
} else {
|
|
|
3057 |
p.rectMode(PConstants.CORNER);
|
|
|
3058 |
p.rect(this.params[0],
|
|
|
3059 |
this.params[1],
|
|
|
3060 |
this.params[2],
|
|
|
3061 |
this.params[3]);
|
|
|
3062 |
}
|
|
|
3063 |
} else if (this.kind === PConstants.ELLIPSE) {
|
|
|
3064 |
p.ellipseMode(PConstants.CORNER);
|
|
|
3065 |
p.ellipse(this.params[0],
|
|
|
3066 |
this.params[1],
|
|
|
3067 |
this.params[2],
|
|
|
3068 |
this.params[3]);
|
|
|
3069 |
} else if (this.kind === PConstants.ARC) {
|
|
|
3070 |
p.ellipseMode(PConstants.CORNER);
|
|
|
3071 |
p.arc(this.params[0],
|
|
|
3072 |
this.params[1],
|
|
|
3073 |
this.params[2],
|
|
|
3074 |
this.params[3],
|
|
|
3075 |
this.params[4],
|
|
|
3076 |
this.params[5]);
|
|
|
3077 |
} else if (this.kind === PConstants.BOX) {
|
|
|
3078 |
if (this.params.length === 1) {
|
|
|
3079 |
p.box(this.params[0]);
|
|
|
3080 |
} else {
|
|
|
3081 |
p.box(this.params[0], this.params[1], this.params[2]);
|
|
|
3082 |
}
|
|
|
3083 |
} else if (this.kind === PConstants.SPHERE) {
|
|
|
3084 |
p.sphere(this.params[0]);
|
|
|
3085 |
}
|
|
|
3086 |
},
|
|
|
3087 |
/**
|
|
|
3088 |
* @member PShape
|
|
|
3089 |
* The pre() function performs the preparations before the SVG is drawn. This includes doing transformations and storing previous styles.
|
|
|
3090 |
*/
|
|
|
3091 |
pre: function() {
|
|
|
3092 |
if (this.matrix) {
|
|
|
3093 |
p.pushMatrix();
|
|
|
3094 |
curContext.transform(this.matrix.elements[0],
|
|
|
3095 |
this.matrix.elements[3],
|
|
|
3096 |
this.matrix.elements[1],
|
|
|
3097 |
this.matrix.elements[4],
|
|
|
3098 |
this.matrix.elements[2],
|
|
|
3099 |
this.matrix.elements[5]);
|
|
|
3100 |
//p.applyMatrix(this.matrix.elements[0],this.matrix.elements[0]);
|
|
|
3101 |
}
|
|
|
3102 |
if (this.style) {
|
|
|
3103 |
p.pushStyle();
|
|
|
3104 |
this.styles();
|
|
|
3105 |
}
|
|
|
3106 |
},
|
|
|
3107 |
/**
|
|
|
3108 |
* @member PShape
|
|
|
3109 |
* The post() function performs the necessary actions after the SVG is drawn. This includes removing transformations and removing added styles.
|
|
|
3110 |
*/
|
|
|
3111 |
post: function() {
|
|
|
3112 |
if (this.matrix) {
|
|
|
3113 |
p.popMatrix();
|
|
|
3114 |
}
|
|
|
3115 |
if (this.style) {
|
|
|
3116 |
p.popStyle();
|
|
|
3117 |
}
|
|
|
3118 |
},
|
|
|
3119 |
/**
|
|
|
3120 |
* @member PShape
|
|
|
3121 |
* The styles() function changes the Processing's current styles
|
|
|
3122 |
*/
|
|
|
3123 |
styles: function() {
|
|
|
3124 |
if (this.stroke) {
|
|
|
3125 |
p.stroke(this.strokeColor);
|
|
|
3126 |
p.strokeWeight(this.strokeWeight);
|
|
|
3127 |
p.strokeCap(this.strokeCap);
|
|
|
3128 |
p.strokeJoin(this.strokeJoin);
|
|
|
3129 |
} else {
|
|
|
3130 |
p.noStroke();
|
|
|
3131 |
}
|
|
|
3132 |
|
|
|
3133 |
if (this.fill) {
|
|
|
3134 |
p.fill(this.fillColor);
|
|
|
3135 |
|
|
|
3136 |
} else {
|
|
|
3137 |
p.noFill();
|
|
|
3138 |
}
|
|
|
3139 |
},
|
|
|
3140 |
/**
|
|
|
3141 |
* @member PShape
|
|
|
3142 |
* 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
|
|
|
3143 |
* layer position of the shape to get with the <b>index</b> parameter.
|
|
|
3144 |
* The shape is returned as a <b>PShape</b> object, or <b>null</b> is returned if there is an error.
|
|
|
3145 |
*
|
|
|
3146 |
* @param {String} target the name of the shape to get
|
|
|
3147 |
* @param {int} index the layer position of the shape to get
|
|
|
3148 |
*
|
|
|
3149 |
* @return {PShape} returns a child element of a shape as a PShape object or null if there is an error
|
|
|
3150 |
*/
|
|
|
3151 |
getChild: function(child) {
|
|
|
3152 |
var i, j;
|
|
|
3153 |
if (typeof child === 'number') {
|
|
|
3154 |
return this.children[child];
|
|
|
3155 |
}
|
|
|
3156 |
var found;
|
|
|
3157 |
if(child === "" || this.name === child){
|
|
|
3158 |
return this;
|
|
|
3159 |
}
|
|
|
3160 |
if(this.nameTable.length > 0) {
|
|
|
3161 |
for(i = 0, j = this.nameTable.length; i < j || found; i++) {
|
|
|
3162 |
if(this.nameTable[i].getName === child) {
|
|
|
3163 |
found = this.nameTable[i];
|
|
|
3164 |
break;
|
|
|
3165 |
}
|
|
|
3166 |
}
|
|
|
3167 |
if (found) { return found; }
|
|
|
3168 |
}
|
|
|
3169 |
for(i = 0, j = this.children.length; i < j; i++) {
|
|
|
3170 |
found = this.children[i].getChild(child);
|
|
|
3171 |
if(found) { return found; }
|
|
|
3172 |
}
|
|
|
3173 |
return null;
|
|
|
3174 |
},
|
|
|
3175 |
/**
|
|
|
3176 |
* @member PShape
|
|
|
3177 |
* The getChildCount() returns the number of children
|
|
|
3178 |
*
|
|
|
3179 |
* @return {int} returns a count of children
|
|
|
3180 |
*/
|
|
|
3181 |
getChildCount: function () {
|
|
|
3182 |
return this.children.length;
|
|
|
3183 |
},
|
|
|
3184 |
/**
|
|
|
3185 |
* @member PShape
|
|
|
3186 |
* The addChild() adds a child to the PShape.
|
|
|
3187 |
*
|
|
|
3188 |
* @param {PShape} child the child to add
|
|
|
3189 |
*/
|
|
|
3190 |
addChild: function( child ) {
|
|
|
3191 |
this.children.push(child);
|
|
|
3192 |
child.parent = this;
|
|
|
3193 |
if (child.getName() !== null) {
|
|
|
3194 |
this.addName(child.getName(), child);
|
|
|
3195 |
}
|
|
|
3196 |
},
|
|
|
3197 |
/**
|
|
|
3198 |
* @member PShape
|
|
|
3199 |
* The addName() functions adds a shape to the name lookup table.
|
|
|
3200 |
*
|
|
|
3201 |
* @param {String} name the name to be added
|
|
|
3202 |
* @param {PShape} shape the shape
|
|
|
3203 |
*/
|
|
|
3204 |
addName: function(name, shape) {
|
|
|
3205 |
if (this.parent !== null) {
|
|
|
3206 |
this.parent.addName( name, shape );
|
|
|
3207 |
} else {
|
|
|
3208 |
this.nameTable.push( [name, shape] );
|
|
|
3209 |
}
|
|
|
3210 |
},
|
|
|
3211 |
/**
|
|
|
3212 |
* @member PShape
|
|
|
3213 |
* 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.
|
|
|
3214 |
* 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>.
|
|
|
3215 |
* This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
|
|
|
3216 |
* <br><br>Using this method with the <b>z</b> parameter requires using the P3D or OPENGL parameter in combination with size.
|
|
|
3217 |
*
|
|
|
3218 |
* @param {int|float} x left/right translation
|
|
|
3219 |
* @param {int|float} y up/down translation
|
|
|
3220 |
* @param {int|float} z forward/back translation
|
|
|
3221 |
*
|
|
|
3222 |
* @see PMatrix2D#translate
|
|
|
3223 |
* @see PMatrix3D#translate
|
|
|
3224 |
*/
|
|
|
3225 |
translate: function() {
|
|
|
3226 |
if(arguments.length === 2)
|
|
|
3227 |
{
|
|
|
3228 |
this.checkMatrix(2);
|
|
|
3229 |
this.matrix.translate(arguments[0], arguments[1]);
|
|
|
3230 |
} else {
|
|
|
3231 |
this.checkMatrix(3);
|
|
|
3232 |
this.matrix.translate(arguments[0], arguments[1], 0);
|
|
|
3233 |
}
|
|
|
3234 |
},
|
|
|
3235 |
/**
|
|
|
3236 |
* @member PShape
|
|
|
3237 |
* The checkMatrix() function makes sure that the shape's matrix is 1) not null, and 2) has a matrix
|
|
|
3238 |
* that can handle <em>at least</em> the specified number of dimensions.
|
|
|
3239 |
*
|
|
|
3240 |
* @param {int} dimensions the specified number of dimensions
|
|
|
3241 |
*/
|
|
|
3242 |
checkMatrix: function(dimensions) {
|
|
|
3243 |
if(this.matrix === null) {
|
|
|
3244 |
if(dimensions === 2) {
|
|
|
3245 |
this.matrix = new p.PMatrix2D();
|
|
|
3246 |
} else {
|
|
|
3247 |
this.matrix = new p.PMatrix3D();
|
|
|
3248 |
}
|
|
|
3249 |
}else if(dimensions === 3 && this.matrix instanceof p.PMatrix2D) {
|
|
|
3250 |
this.matrix = new p.PMatrix3D();
|
|
|
3251 |
}
|
|
|
3252 |
},
|
|
|
3253 |
/**
|
|
|
3254 |
* @member PShape
|
|
|
3255 |
* 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.
|
|
|
3256 |
* <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
|
|
|
3257 |
* 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>.
|
|
|
3258 |
* This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
|
|
|
3259 |
* <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.
|
|
|
3260 |
*
|
|
|
3261 |
* @param {float}angle angle of rotation specified in radians
|
|
|
3262 |
*
|
|
|
3263 |
* @see PMatrix3D#rotateX
|
|
|
3264 |
*/
|
|
|
3265 |
rotateX: function(angle) {
|
|
|
3266 |
this.rotate(angle, 1, 0, 0);
|
|
|
3267 |
},
|
|
|
3268 |
/**
|
|
|
3269 |
* @member PShape
|
|
|
3270 |
* 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.
|
|
|
3271 |
* <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
|
|
|
3272 |
* 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>.
|
|
|
3273 |
* This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
|
|
|
3274 |
* <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.
|
|
|
3275 |
*
|
|
|
3276 |
* @param {float}angle angle of rotation specified in radians
|
|
|
3277 |
*
|
|
|
3278 |
* @see PMatrix3D#rotateY
|
|
|
3279 |
*/
|
|
|
3280 |
rotateY: function(angle) {
|
|
|
3281 |
this.rotate(angle, 0, 1, 0);
|
|
|
3282 |
},
|
|
|
3283 |
/**
|
|
|
3284 |
* @member PShape
|
|
|
3285 |
* 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.
|
|
|
3286 |
* <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
|
|
|
3287 |
* 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>.
|
|
|
3288 |
* This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
|
|
|
3289 |
* <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.
|
|
|
3290 |
*
|
|
|
3291 |
* @param {float}angle angle of rotation specified in radians
|
|
|
3292 |
*
|
|
|
3293 |
* @see PMatrix3D#rotateZ
|
|
|
3294 |
*/
|
|
|
3295 |
rotateZ: function(angle) {
|
|
|
3296 |
this.rotate(angle, 0, 0, 1);
|
|
|
3297 |
},
|
|
|
3298 |
/**
|
|
|
3299 |
* @member PShape
|
|
|
3300 |
* 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.
|
|
|
3301 |
* <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
|
|
|
3302 |
* Transformations apply to everything that happens after and subsequent calls to the method accumulates the effect.
|
|
|
3303 |
* For example, calling <b>rotate(HALF_PI)</b> and then <b>rotate(HALF_PI)</b> is the same as <b>rotate(PI)</b>.
|
|
|
3304 |
* This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
|
|
|
3305 |
* If optional parameters x,y,z are supplied, the rotate is about the point (x, y, z).
|
|
|
3306 |
*
|
|
|
3307 |
* @param {float}angle angle of rotation specified in radians
|
|
|
3308 |
* @param {float}x x-coordinate of the point
|
|
|
3309 |
* @param {float}y y-coordinate of the point
|
|
|
3310 |
* @param {float}z z-coordinate of the point
|
|
|
3311 |
* @see PMatrix2D#rotate
|
|
|
3312 |
* @see PMatrix3D#rotate
|
|
|
3313 |
*/
|
|
|
3314 |
rotate: function() {
|
|
|
3315 |
if(arguments.length === 1){
|
|
|
3316 |
this.checkMatrix(2);
|
|
|
3317 |
this.matrix.rotate(arguments[0]);
|
|
|
3318 |
} else {
|
|
|
3319 |
this.checkMatrix(3);
|
|
|
3320 |
this.matrix.rotate(arguments[0],
|
|
|
3321 |
arguments[1],
|
|
|
3322 |
arguments[2],
|
|
|
3323 |
arguments[3]);
|
|
|
3324 |
}
|
|
|
3325 |
},
|
|
|
3326 |
/**
|
|
|
3327 |
* @member PShape
|
|
|
3328 |
* 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.
|
|
|
3329 |
* 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%.
|
|
|
3330 |
* 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>.
|
|
|
3331 |
* This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
|
|
|
3332 |
* <br><br>Using this fuction with the <b>z</b> parameter requires passing P3D or OPENGL into the size() parameter.
|
|
|
3333 |
*
|
|
|
3334 |
* @param {float}s percentage to scale the object
|
|
|
3335 |
* @param {float}x percentage to scale the object in the x-axis
|
|
|
3336 |
* @param {float}y percentage to scale the object in the y-axis
|
|
|
3337 |
* @param {float}z percentage to scale the object in the z-axis
|
|
|
3338 |
*
|
|
|
3339 |
* @see PMatrix2D#scale
|
|
|
3340 |
* @see PMatrix3D#scale
|
|
|
3341 |
*/
|
|
|
3342 |
scale: function() {
|
|
|
3343 |
if(arguments.length === 2) {
|
|
|
3344 |
this.checkMatrix(2);
|
|
|
3345 |
this.matrix.scale(arguments[0], arguments[1]);
|
|
|
3346 |
} else if (arguments.length === 3) {
|
|
|
3347 |
this.checkMatrix(2);
|
|
|
3348 |
this.matrix.scale(arguments[0], arguments[1], arguments[2]);
|
|
|
3349 |
} else {
|
|
|
3350 |
this.checkMatrix(2);
|
|
|
3351 |
this.matrix.scale(arguments[0]);
|
|
|
3352 |
}
|
|
|
3353 |
},
|
|
|
3354 |
/**
|
|
|
3355 |
* @member PShape
|
|
|
3356 |
* The resetMatrix() function resets the matrix
|
|
|
3357 |
*
|
|
|
3358 |
* @see PMatrix2D#reset
|
|
|
3359 |
* @see PMatrix3D#reset
|
|
|
3360 |
*/
|
|
|
3361 |
resetMatrix: function() {
|
|
|
3362 |
this.checkMatrix(2);
|
|
|
3363 |
this.matrix.reset();
|
|
|
3364 |
},
|
|
|
3365 |
/**
|
|
|
3366 |
* @member PShape
|
|
|
3367 |
* The applyMatrix() function multiplies this matrix by another matrix of type PMatrix3D or PMatrix2D.
|
|
|
3368 |
* Individual elements can also be provided
|
|
|
3369 |
*
|
|
|
3370 |
* @param {PMatrix3D|PMatrix2D} matrix the matrix to multiply by
|
|
|
3371 |
*
|
|
|
3372 |
* @see PMatrix2D#apply
|
|
|
3373 |
* @see PMatrix3D#apply
|
|
|
3374 |
*/
|
|
|
3375 |
applyMatrix: function(matrix) {
|
|
|
3376 |
if (arguments.length === 1) {
|
|
|
3377 |
this.applyMatrix(matrix.elements[0],
|
|
|
3378 |
matrix.elements[1], 0,
|
|
|
3379 |
matrix.elements[2],
|
|
|
3380 |
matrix.elements[3],
|
|
|
3381 |
matrix.elements[4], 0,
|
|
|
3382 |
matrix.elements[5],
|
|
|
3383 |
0, 0, 1, 0,
|
|
|
3384 |
0, 0, 0, 1);
|
|
|
3385 |
} else if (arguments.length === 6) {
|
|
|
3386 |
this.checkMatrix(2);
|
|
|
3387 |
this.matrix.apply(arguments[0], arguments[1], arguments[2], 0,
|
|
|
3388 |
arguments[3], arguments[4], arguments[5], 0,
|
|
|
3389 |
0, 0, 1, 0,
|
|
|
3390 |
0, 0, 0, 1);
|
|
|
3391 |
|
|
|
3392 |
} else if (arguments.length === 16) {
|
|
|
3393 |
this.checkMatrix(3);
|
|
|
3394 |
this.matrix.apply(arguments[0],
|
|
|
3395 |
arguments[1],
|
|
|
3396 |
arguments[2],
|
|
|
3397 |
arguments[3],
|
|
|
3398 |
arguments[4],
|
|
|
3399 |
arguments[5],
|
|
|
3400 |
arguments[6],
|
|
|
3401 |
arguments[7],
|
|
|
3402 |
arguments[8],
|
|
|
3403 |
arguments[9],
|
|
|
3404 |
arguments[10],
|
|
|
3405 |
arguments[11],
|
|
|
3406 |
arguments[12],
|
|
|
3407 |
arguments[13],
|
|
|
3408 |
arguments[14],
|
|
|
3409 |
arguments[15]);
|
|
|
3410 |
}
|
|
|
3411 |
}
|
|
|
3412 |
};
|
|
|
3413 |
|
|
|
3414 |
/**
|
|
|
3415 |
* SVG stands for Scalable Vector Graphics, a portable graphics format. It is
|
|
|
3416 |
* a vector format so it allows for infinite resolution and relatively small
|
|
|
3417 |
* file sizes. Most modern media software can view SVG files, including Adobe
|
|
|
3418 |
* products, Firefox, etc. Illustrator and Inkscape can edit SVG files.
|
|
|
3419 |
*
|
|
|
3420 |
* @param {PApplet} parent typically use "this"
|
|
|
3421 |
* @param {String} filename name of the SVG file to load
|
|
|
3422 |
* @param {XMLElement} xml an XMLElement element
|
|
|
3423 |
* @param {PShapeSVG} parent the parent PShapeSVG
|
|
|
3424 |
*
|
|
|
3425 |
* @see PShape
|
|
|
3426 |
*/
|
|
|
3427 |
var PShapeSVG = p.PShapeSVG = function() {
|
|
|
3428 |
p.PShape.call( this ); // PShape is the base class.
|
|
|
3429 |
if (arguments.length === 1) { //xml element coming in
|
|
|
3430 |
this.element = arguments[0] ;//new p.XMLElement(null, arguments[0]);
|
|
|
3431 |
// set values to their defaults according to the SVG spec
|
|
|
3432 |
this.vertexCodes = [];
|
|
|
3433 |
this.vertices = [];
|
|
|
3434 |
this.opacity = 1;
|
|
|
3435 |
|
|
|
3436 |
this.stroke = false;
|
|
|
3437 |
this.strokeColor = PConstants.ALPHA_MASK;
|
|
|
3438 |
this.strokeWeight = 1;
|
|
|
3439 |
this.strokeCap = PConstants.SQUARE; // BUTT in svg spec
|
|
|
3440 |
this.strokeJoin = PConstants.MITER;
|
|
|
3441 |
this.strokeGradient = null;
|
|
|
3442 |
this.strokeGradientPaint = null;
|
|
|
3443 |
this.strokeName = null;
|
|
|
3444 |
this.strokeOpacity = 1;
|
|
|
3445 |
|
|
|
3446 |
this.fill = true;
|
|
|
3447 |
this.fillColor = PConstants.ALPHA_MASK;
|
|
|
3448 |
this.fillGradient = null;
|
|
|
3449 |
this.fillGradientPaint = null;
|
|
|
3450 |
this.fillName = null;
|
|
|
3451 |
this.fillOpacity = 1;
|
|
|
3452 |
|
|
|
3453 |
if (this.element.getName() !== "svg") {
|
|
|
3454 |
throw("root is not <svg>, it's <" + this.element.getName() + ">");
|
|
|
3455 |
}
|
|
|
3456 |
}
|
|
|
3457 |
else if (arguments.length === 2) {
|
|
|
3458 |
if (typeof arguments[1] === 'string') {
|
|
|
3459 |
if (arguments[1].indexOf(".svg") > -1) { //its a filename
|
|
|
3460 |
this.element = new p.XMLElement(null, arguments[1]);
|
|
|
3461 |
// set values to their defaults according to the SVG spec
|
|
|
3462 |
this.vertexCodes = [];
|
|
|
3463 |
this.vertices = [];
|
|
|
3464 |
this.opacity = 1;
|
|
|
3465 |
|
|
|
3466 |
this.stroke = false;
|
|
|
3467 |
this.strokeColor = PConstants.ALPHA_MASK;
|
|
|
3468 |
this.strokeWeight = 1;
|
|
|
3469 |
this.strokeCap = PConstants.SQUARE; // BUTT in svg spec
|
|
|
3470 |
this.strokeJoin = PConstants.MITER;
|
|
|
3471 |
this.strokeGradient = "";
|
|
|
3472 |
this.strokeGradientPaint = "";
|
|
|
3473 |
this.strokeName = "";
|
|
|
3474 |
this.strokeOpacity = 1;
|
|
|
3475 |
|
|
|
3476 |
this.fill = true;
|
|
|
3477 |
this.fillColor = PConstants.ALPHA_MASK;
|
|
|
3478 |
this.fillGradient = null;
|
|
|
3479 |
this.fillGradientPaint = null;
|
|
|
3480 |
this.fillOpacity = 1;
|
|
|
3481 |
|
|
|
3482 |
}
|
|
|
3483 |
} else { // XMLElement
|
|
|
3484 |
if (arguments[0]) { // PShapeSVG
|
|
|
3485 |
this.element = arguments[1];
|
|
|
3486 |
this.vertexCodes = arguments[0].vertexCodes.slice();
|
|
|
3487 |
this.vertices = arguments[0].vertices.slice();
|
|
|
3488 |
|
|
|
3489 |
this.stroke = arguments[0].stroke;
|
|
|
3490 |
this.strokeColor = arguments[0].strokeColor;
|
|
|
3491 |
this.strokeWeight = arguments[0].strokeWeight;
|
|
|
3492 |
this.strokeCap = arguments[0].strokeCap;
|
|
|
3493 |
this.strokeJoin = arguments[0].strokeJoin;
|
|
|
3494 |
this.strokeGradient = arguments[0].strokeGradient;
|
|
|
3495 |
this.strokeGradientPaint = arguments[0].strokeGradientPaint;
|
|
|
3496 |
this.strokeName = arguments[0].strokeName;
|
|
|
3497 |
|
|
|
3498 |
this.fill = arguments[0].fill;
|
|
|
3499 |
this.fillColor = arguments[0].fillColor;
|
|
|
3500 |
this.fillGradient = arguments[0].fillGradient;
|
|
|
3501 |
this.fillGradientPaint = arguments[0].fillGradientPaint;
|
|
|
3502 |
this.fillName = arguments[0].fillName;
|
|
|
3503 |
this.strokeOpacity = arguments[0].strokeOpacity;
|
|
|
3504 |
this.fillOpacity = arguments[0].fillOpacity;
|
|
|
3505 |
this.opacity = arguments[0].opacity;
|
|
|
3506 |
}
|
|
|
3507 |
}
|
|
|
3508 |
}
|
|
|
3509 |
|
|
|
3510 |
this.name = this.element.getStringAttribute("id");
|
|
|
3511 |
var displayStr = this.element.getStringAttribute("display", "inline");
|
|
|
3512 |
this.visible = displayStr !== "none";
|
|
|
3513 |
var str = this.element.getAttribute("transform");
|
|
|
3514 |
if (str) {
|
|
|
3515 |
this.matrix = this.parseMatrix(str);
|
|
|
3516 |
}
|
|
|
3517 |
// not proper parsing of the viewBox, but will cover us for cases where
|
|
|
3518 |
// the width and height of the object is not specified
|
|
|
3519 |
var viewBoxStr = this.element.getStringAttribute("viewBox");
|
|
|
3520 |
if ( viewBoxStr !== null ) {
|
|
|
3521 |
var viewBox = viewBoxStr.split(" ");
|
|
|
3522 |
this.width = viewBox[2];
|
|
|
3523 |
this.height = viewBox[3];
|
|
|
3524 |
}
|
|
|
3525 |
|
|
|
3526 |
// TODO if viewbox is not same as width/height, then use it to scale
|
|
|
3527 |
// the original objects. for now, viewbox only used when width/height
|
|
|
3528 |
// are empty values (which by the spec means w/h of "100%"
|
|
|
3529 |
var unitWidth = this.element.getStringAttribute("width");
|
|
|
3530 |
var unitHeight = this.element.getStringAttribute("height");
|
|
|
3531 |
if (unitWidth !== null) {
|
|
|
3532 |
this.width = this.parseUnitSize(unitWidth);
|
|
|
3533 |
this.height = this.parseUnitSize(unitHeight);
|
|
|
3534 |
} else {
|
|
|
3535 |
if ((this.width === 0) || (this.height === 0)) {
|
|
|
3536 |
// For the spec, the default is 100% and 100%. For purposes
|
|
|
3537 |
// here, insert a dummy value because this is prolly just a
|
|
|
3538 |
// font or something for which the w/h doesn't matter.
|
|
|
3539 |
this.width = 1;
|
|
|
3540 |
this.height = 1;
|
|
|
3541 |
|
|
|
3542 |
//show warning
|
|
|
3543 |
throw("The width and/or height is not " +
|
|
|
3544 |
"readable in the <svg> tag of this file.");
|
|
|
3545 |
}
|
|
|
3546 |
}
|
|
|
3547 |
this.parseColors(this.element);
|
|
|
3548 |
this.parseChildren(this.element);
|
|
|
3549 |
|
|
|
3550 |
};
|
|
|
3551 |
/**
|
|
|
3552 |
* PShapeSVG methods
|
|
|
3553 |
* missing: getChild(), print(), parseStyleAttributes(), styles() - deals with strokeGradient and fillGradient
|
|
|
3554 |
*/
|
|
|
3555 |
PShapeSVG.prototype = new PShape();
|
|
|
3556 |
/**
|
|
|
3557 |
* @member PShapeSVG
|
|
|
3558 |
* The parseMatrix() function parses the specified SVG matrix into a PMatrix2D. Note that PMatrix2D
|
|
|
3559 |
* is rotated relative to the SVG definition, so parameters are rearranged
|
|
|
3560 |
* here. More about the transformation matrices in
|
|
|
3561 |
* <a href="http://www.w3.org/TR/SVG/coords.html#TransformAttribute">this section</a>
|
|
|
3562 |
* of the SVG documentation.
|
|
|
3563 |
*
|
|
|
3564 |
* @param {String} str text of the matrix param.
|
|
|
3565 |
*
|
|
|
3566 |
* @return {PMatrix2D} a PMatrix2D
|
|
|
3567 |
*/
|
|
|
3568 |
PShapeSVG.prototype.parseMatrix = (function() {
|
|
|
3569 |
function getCoords(s) {
|
|
|
3570 |
var m = [];
|
|
|
3571 |
s.replace(/\((.*?)\)/, (function() {
|
|
|
3572 |
return function(all, params) {
|
|
|
3573 |
// get the coordinates that can be separated by spaces or a comma
|
|
|
3574 |
m = params.replace(/,+/g, " ").split(/\s+/);
|
|
|
3575 |
};
|
|
|
3576 |
}()));
|
|
|
3577 |
return m;
|
|
|
3578 |
}
|
|
|
3579 |
|
|
|
3580 |
return function(str) {
|
|
|
3581 |
this.checkMatrix(2);
|
|
|
3582 |
var pieces = [];
|
|
|
3583 |
str.replace(/\s*(\w+)\((.*?)\)/g, function(all) {
|
|
|
3584 |
// get a list of transform definitions
|
|
|
3585 |
pieces.push(p.trim(all));
|
|
|
3586 |
});
|
|
|
3587 |
if (pieces.length === 0) {
|
|
|
3588 |
return null;
|
|
|
3589 |
}
|
|
|
3590 |
|
|
|
3591 |
for (var i = 0, j = pieces.length; i < j; i++) {
|
|
|
3592 |
var m = getCoords(pieces[i]);
|
|
|
3593 |
|
|
|
3594 |
if (pieces[i].indexOf("matrix") !== -1) {
|
|
|
3595 |
this.matrix.set(m[0], m[2], m[4], m[1], m[3], m[5]);
|
|
|
3596 |
} else if (pieces[i].indexOf("translate") !== -1) {
|
|
|
3597 |
var tx = m[0];
|
|
|
3598 |
var ty = (m.length === 2) ? m[1] : 0;
|
|
|
3599 |
this.matrix.translate(tx,ty);
|
|
|
3600 |
} else if (pieces[i].indexOf("scale") !== -1) {
|
|
|
3601 |
var sx = m[0];
|
|
|
3602 |
var sy = (m.length === 2) ? m[1] : m[0];
|
|
|
3603 |
this.matrix.scale(sx,sy);
|
|
|
3604 |
} else if (pieces[i].indexOf("rotate") !== -1) {
|
|
|
3605 |
var angle = m[0];
|
|
|
3606 |
if (m.length === 1) {
|
|
|
3607 |
this.matrix.rotate(p.radians(angle));
|
|
|
3608 |
} else if (m.length === 3) {
|
|
|
3609 |
this.matrix.translate(m[1], m[2]);
|
|
|
3610 |
this.matrix.rotate(p.radians(m[0]));
|
|
|
3611 |
this.matrix.translate(-m[1], -m[2]);
|
|
|
3612 |
}
|
|
|
3613 |
} else if (pieces[i].indexOf("skewX") !== -1) {
|
|
|
3614 |
this.matrix.skewX(parseFloat(m[0]));
|
|
|
3615 |
} else if (pieces[i].indexOf("skewY") !== -1) {
|
|
|
3616 |
this.matrix.skewY(m[0]);
|
|
|
3617 |
}
|
|
|
3618 |
}
|
|
|
3619 |
return this.matrix;
|
|
|
3620 |
};
|
|
|
3621 |
}());
|
|
|
3622 |
|
|
|
3623 |
/**
|
|
|
3624 |
* @member PShapeSVG
|
|
|
3625 |
* The parseChildren() function parses the specified XMLElement
|
|
|
3626 |
*
|
|
|
3627 |
* @param {XMLElement}element the XMLElement to parse
|
|
|
3628 |
*/
|
|
|
3629 |
PShapeSVG.prototype.parseChildren = function(element) {
|
|
|
3630 |
var newelement = element.getChildren();
|
|
|
3631 |
var children = new p.PShape();
|
|
|
3632 |
for (var i = 0, j = newelement.length; i < j; i++) {
|
|
|
3633 |
var kid = this.parseChild(newelement[i]);
|
|
|
3634 |
if (kid) {
|
|
|
3635 |
children.addChild(kid);
|
|
|
3636 |
}
|
|
|
3637 |
}
|
|
|
3638 |
this.children.push(children);
|
|
|
3639 |
};
|
|
|
3640 |
/**
|
|
|
3641 |
* @member PShapeSVG
|
|
|
3642 |
* The getName() function returns the name
|
|
|
3643 |
*
|
|
|
3644 |
* @return {String} the name
|
|
|
3645 |
*/
|
|
|
3646 |
PShapeSVG.prototype.getName = function() {
|
|
|
3647 |
return this.name;
|
|
|
3648 |
};
|
|
|
3649 |
/**
|
|
|
3650 |
* @member PShapeSVG
|
|
|
3651 |
* The parseChild() function parses a child XML element.
|
|
|
3652 |
*
|
|
|
3653 |
* @param {XMLElement} elem the element to parse
|
|
|
3654 |
*
|
|
|
3655 |
* @return {PShape} the newly created PShape
|
|
|
3656 |
*/
|
|
|
3657 |
PShapeSVG.prototype.parseChild = function( elem ) {
|
|
|
3658 |
var name = elem.getName();
|
|
|
3659 |
var shape;
|
|
|
3660 |
if (name === "g") {
|
|
|
3661 |
shape = new PShapeSVG(this, elem);
|
|
|
3662 |
} else if (name === "defs") {
|
|
|
3663 |
// generally this will contain gradient info, so may
|
|
|
3664 |
// as well just throw it into a group element for parsing
|
|
|
3665 |
shape = new PShapeSVG(this, elem);
|
|
|
3666 |
} else if (name === "line") {
|
|
|
3667 |
shape = new PShapeSVG(this, elem);
|
|
|
3668 |
shape.parseLine();
|
|
|
3669 |
} else if (name === "circle") {
|
|
|
3670 |
shape = new PShapeSVG(this, elem);
|
|
|
3671 |
shape.parseEllipse(true);
|
|
|
3672 |
} else if (name === "ellipse") {
|
|
|
3673 |
shape = new PShapeSVG(this, elem);
|
|
|
3674 |
shape.parseEllipse(false);
|
|
|
3675 |
} else if (name === "rect") {
|
|
|
3676 |
shape = new PShapeSVG(this, elem);
|
|
|
3677 |
shape.parseRect();
|
|
|
3678 |
} else if (name === "polygon") {
|
|
|
3679 |
shape = new PShapeSVG(this, elem);
|
|
|
3680 |
shape.parsePoly(true);
|
|
|
3681 |
} else if (name === "polyline") {
|
|
|
3682 |
shape = new PShapeSVG(this, elem);
|
|
|
3683 |
shape.parsePoly(false);
|
|
|
3684 |
} else if (name === "path") {
|
|
|
3685 |
shape = new PShapeSVG(this, elem);
|
|
|
3686 |
shape.parsePath();
|
|
|
3687 |
} else if (name === "radialGradient") {
|
|
|
3688 |
//return new RadialGradient(this, elem);
|
|
|
3689 |
unimplemented('PShapeSVG.prototype.parseChild, name = radialGradient');
|
|
|
3690 |
} else if (name === "linearGradient") {
|
|
|
3691 |
//return new LinearGradient(this, elem);
|
|
|
3692 |
unimplemented('PShapeSVG.prototype.parseChild, name = linearGradient');
|
|
|
3693 |
} else if (name === "text") {
|
|
|
3694 |
unimplemented('PShapeSVG.prototype.parseChild, name = text');
|
|
|
3695 |
} else if (name === "filter") {
|
|
|
3696 |
unimplemented('PShapeSVG.prototype.parseChild, name = filter');
|
|
|
3697 |
} else if (name === "mask") {
|
|
|
3698 |
unimplemented('PShapeSVG.prototype.parseChild, name = mask');
|
|
|
3699 |
} else {
|
|
|
3700 |
// ignoring
|
|
|
3701 |
nop();
|
|
|
3702 |
}
|
|
|
3703 |
return shape;
|
|
|
3704 |
};
|
|
|
3705 |
/**
|
|
|
3706 |
* @member PShapeSVG
|
|
|
3707 |
* The parsePath() function parses the <path> element of the svg file
|
|
|
3708 |
* A path is defined by including a path element which contains a d="(path data)" attribute, where the d attribute contains
|
|
|
3709 |
* the moveto, line, curve (both cubic and quadratic Beziers), arc and closepath instructions.
|
|
|
3710 |
**/
|
|
|
3711 |
PShapeSVG.prototype.parsePath = function() {
|
|
|
3712 |
this.family = PConstants.PATH;
|
|
|
3713 |
this.kind = 0;
|
|
|
3714 |
var pathDataChars = [];
|
|
|
3715 |
var c;
|
|
|
3716 |
//change multiple spaces and commas to single space
|
|
|
3717 |
var pathData = p.trim(this.element.getStringAttribute("d")
|
|
|
3718 |
.replace(/[\s,]+/g,' '));
|
|
|
3719 |
if (pathData === null) {
|
|
|
3720 |
return;
|
|
|
3721 |
}
|
|
|
3722 |
pathData = p.__toCharArray(pathData);
|
|
|
3723 |
var cx = 0,
|
|
|
3724 |
cy = 0,
|
|
|
3725 |
ctrlX = 0,
|
|
|
3726 |
ctrlY = 0,
|
|
|
3727 |
ctrlX1 = 0,
|
|
|
3728 |
ctrlX2 = 0,
|
|
|
3729 |
ctrlY1 = 0,
|
|
|
3730 |
ctrlY2 = 0,
|
|
|
3731 |
endX = 0,
|
|
|
3732 |
endY = 0,
|
|
|
3733 |
ppx = 0,
|
|
|
3734 |
ppy = 0,
|
|
|
3735 |
px = 0,
|
|
|
3736 |
py = 0,
|
|
|
3737 |
i = 0,
|
|
|
3738 |
valOf = 0;
|
|
|
3739 |
var str = "";
|
|
|
3740 |
var tmpArray =[];
|
|
|
3741 |
var flag = false;
|
|
|
3742 |
var lastInstruction;
|
|
|
3743 |
var command;
|
|
|
3744 |
var j, k;
|
|
|
3745 |
while (i< pathData.length) {
|
|
|
3746 |
valOf = pathData[i].valueOf();
|
|
|
3747 |
if ((valOf >= 65 && valOf <= 90) || (valOf >= 97 && valOf <= 122)) {
|
|
|
3748 |
// if it's a letter
|
|
|
3749 |
// populate the tmpArray with coordinates
|
|
|
3750 |
j = i;
|
|
|
3751 |
i++;
|
|
|
3752 |
if (i < pathData.length) { // don't go over boundary of array
|
|
|
3753 |
tmpArray = [];
|
|
|
3754 |
valOf = pathData[i].valueOf();
|
|
|
3755 |
while (!((valOf >= 65 && valOf <= 90) ||
|
|
|
3756 |
(valOf >= 97 && valOf <= 100) ||
|
|
|
3757 |
(valOf >= 102 && valOf <= 122))
|
|
|
3758 |
&& flag === false) { // if its NOT a letter
|
|
|
3759 |
if (valOf === 32) { //if its a space and the str isn't empty
|
|
|
3760 |
// sometimes you get a space after the letter
|
|
|
3761 |
if (str !== "") {
|
|
|
3762 |
tmpArray.push(parseFloat(str));
|
|
|
3763 |
str = "";
|
|
|
3764 |
}
|
|
|
3765 |
i++;
|
|
|
3766 |
} else if (valOf === 45) { //if it's a -
|
|
|
3767 |
// allow for 'e' notation in numbers, e.g. 2.10e-9
|
|
|
3768 |
if (pathData[i-1].valueOf() === 101) {
|
|
|
3769 |
str += pathData[i].toString();
|
|
|
3770 |
i++;
|
|
|
3771 |
} else {
|
|
|
3772 |
// sometimes no space separator after (ex: 104.535-16.322)
|
|
|
3773 |
if (str !== "") {
|
|
|
3774 |
tmpArray.push(parseFloat(str));
|
|
|
3775 |
}
|
|
|
3776 |
str = pathData[i].toString();
|
|
|
3777 |
i++;
|
|
|
3778 |
}
|
|
|
3779 |
} else {
|
|
|
3780 |
str += pathData[i].toString();
|
|
|
3781 |
i++;
|
|
|
3782 |
}
|
|
|
3783 |
if (i === pathData.length) { // don't go over boundary of array
|
|
|
3784 |
flag = true;
|
|
|
3785 |
} else {
|
|
|
3786 |
valOf = pathData[i].valueOf();
|
|
|
3787 |
}
|
|
|
3788 |
}
|
|
|
3789 |
}
|
|
|
3790 |
if (str !== "") {
|
|
|
3791 |
tmpArray.push(parseFloat(str));
|
|
|
3792 |
str = "";
|
|
|
3793 |
}
|
|
|
3794 |
command = pathData[j];
|
|
|
3795 |
valOf = command.valueOf();
|
|
|
3796 |
if (valOf === 77) { // M - move to (absolute)
|
|
|
3797 |
if (tmpArray.length >= 2 && tmpArray.length % 2 ===0) {
|
|
|
3798 |
// need one+ pairs of co-ordinates
|
|
|
3799 |
cx = tmpArray[0];
|
|
|
3800 |
cy = tmpArray[1];
|
|
|
3801 |
this.parsePathMoveto(cx, cy);
|
|
|
3802 |
if (tmpArray.length > 2) {
|
|
|
3803 |
for (j = 2, k = tmpArray.length; j < k; j+=2) {
|
|
|
3804 |
// absolute line to
|
|
|
3805 |
cx = tmpArray[j];
|
|
|
3806 |
cy = tmpArray[j+1];
|
|
|
3807 |
this.parsePathLineto(cx,cy);
|
|
|
3808 |
}
|
|
|
3809 |
}
|
|
|
3810 |
}
|
|
|
3811 |
} else if (valOf === 109) { // m - move to (relative)
|
|
|
3812 |
if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
|
|
|
3813 |
// need one+ pairs of co-ordinates
|
|
|
3814 |
cx += tmpArray[0];
|
|
|
3815 |
cy += tmpArray[1];
|
|
|
3816 |
this.parsePathMoveto(cx,cy);
|
|
|
3817 |
if (tmpArray.length > 2) {
|
|
|
3818 |
for (j = 2, k = tmpArray.length; j < k; j+=2) {
|
|
|
3819 |
// relative line to
|
|
|
3820 |
cx += tmpArray[j];
|
|
|
3821 |
cy += tmpArray[j + 1];
|
|
|
3822 |
this.parsePathLineto(cx,cy);
|
|
|
3823 |
}
|
|
|
3824 |
}
|
|
|
3825 |
}
|
|
|
3826 |
} else if (valOf === 76) { // L - lineto (absolute)
|
|
|
3827 |
if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
|
|
|
3828 |
// need one+ pairs of co-ordinates
|
|
|
3829 |
for (j = 0, k = tmpArray.length; j < k; j+=2) {
|
|
|
3830 |
cx = tmpArray[j];
|
|
|
3831 |
cy = tmpArray[j + 1];
|
|
|
3832 |
this.parsePathLineto(cx,cy);
|
|
|
3833 |
}
|
|
|
3834 |
}
|
|
|
3835 |
} else if (valOf === 108) { // l - lineto (relative)
|
|
|
3836 |
if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
|
|
|
3837 |
// need one+ pairs of co-ordinates
|
|
|
3838 |
for (j = 0, k = tmpArray.length; j < k; j+=2) {
|
|
|
3839 |
cx += tmpArray[j];
|
|
|
3840 |
cy += tmpArray[j+1];
|
|
|
3841 |
this.parsePathLineto(cx,cy);
|
|
|
3842 |
}
|
|
|
3843 |
}
|
|
|
3844 |
} else if (valOf === 72) { // H - horizontal lineto (absolute)
|
|
|
3845 |
for (j = 0, k = tmpArray.length; j < k; j++) {
|
|
|
3846 |
// multiple x co-ordinates can be provided
|
|
|
3847 |
cx = tmpArray[j];
|
|
|
3848 |
this.parsePathLineto(cx, cy);
|
|
|
3849 |
}
|
|
|
3850 |
} else if (valOf === 104) { // h - horizontal lineto (relative)
|
|
|
3851 |
for (j = 0, k = tmpArray.length; j < k; j++) {
|
|
|
3852 |
// multiple x co-ordinates can be provided
|
|
|
3853 |
cx += tmpArray[j];
|
|
|
3854 |
this.parsePathLineto(cx, cy);
|
|
|
3855 |
}
|
|
|
3856 |
} else if (valOf === 86) { // V - vertical lineto (absolute)
|
|
|
3857 |
for (j = 0, k = tmpArray.length; j < k; j++) {
|
|
|
3858 |
// multiple y co-ordinates can be provided
|
|
|
3859 |
cy = tmpArray[j];
|
|
|
3860 |
this.parsePathLineto(cx, cy);
|
|
|
3861 |
}
|
|
|
3862 |
} else if (valOf === 118) { // v - vertical lineto (relative)
|
|
|
3863 |
for (j = 0, k = tmpArray.length; j < k; j++) {
|
|
|
3864 |
// multiple y co-ordinates can be provided
|
|
|
3865 |
cy += tmpArray[j];
|
|
|
3866 |
this.parsePathLineto(cx, cy);
|
|
|
3867 |
}
|
|
|
3868 |
} else if (valOf === 67) { // C - curve to (absolute)
|
|
|
3869 |
if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) {
|
|
|
3870 |
// need one+ multiples of 6 co-ordinates
|
|
|
3871 |
for (j = 0, k = tmpArray.length; j < k; j+=6) {
|
|
|
3872 |
ctrlX1 = tmpArray[j];
|
|
|
3873 |
ctrlY1 = tmpArray[j + 1];
|
|
|
3874 |
ctrlX2 = tmpArray[j + 2];
|
|
|
3875 |
ctrlY2 = tmpArray[j + 3];
|
|
|
3876 |
endX = tmpArray[j + 4];
|
|
|
3877 |
endY = tmpArray[j + 5];
|
|
|
3878 |
this.parsePathCurveto(ctrlX1,
|
|
|
3879 |
ctrlY1,
|
|
|
3880 |
ctrlX2,
|
|
|
3881 |
ctrlY2,
|
|
|
3882 |
endX,
|
|
|
3883 |
endY);
|
|
|
3884 |
cx = endX;
|
|
|
3885 |
cy = endY;
|
|
|
3886 |
}
|
|
|
3887 |
}
|
|
|
3888 |
} else if (valOf === 99) { // c - curve to (relative)
|
|
|
3889 |
if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) {
|
|
|
3890 |
// need one+ multiples of 6 co-ordinates
|
|
|
3891 |
for (j = 0, k = tmpArray.length; j < k; j+=6) {
|
|
|
3892 |
ctrlX1 = cx + tmpArray[j];
|
|
|
3893 |
ctrlY1 = cy + tmpArray[j + 1];
|
|
|
3894 |
ctrlX2 = cx + tmpArray[j + 2];
|
|
|
3895 |
ctrlY2 = cy + tmpArray[j + 3];
|
|
|
3896 |
endX = cx + tmpArray[j + 4];
|
|
|
3897 |
endY = cy + tmpArray[j + 5];
|
|
|
3898 |
this.parsePathCurveto(ctrlX1,
|
|
|
3899 |
ctrlY1,
|
|
|
3900 |
ctrlX2,
|
|
|
3901 |
ctrlY2,
|
|
|
3902 |
endX,
|
|
|
3903 |
endY);
|
|
|
3904 |
cx = endX;
|
|
|
3905 |
cy = endY;
|
|
|
3906 |
}
|
|
|
3907 |
}
|
|
|
3908 |
} else if (valOf === 83) { // S - curve to shorthand (absolute)
|
|
|
3909 |
if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
|
|
|
3910 |
// need one+ multiples of 4 co-ordinates
|
|
|
3911 |
for (j = 0, k = tmpArray.length; j < k; j+=4) {
|
|
|
3912 |
if (lastInstruction.toLowerCase() === "c" ||
|
|
|
3913 |
lastInstruction.toLowerCase() === "s") {
|
|
|
3914 |
ppx = this.vertices[ this.vertices.length-2 ][0];
|
|
|
3915 |
ppy = this.vertices[ this.vertices.length-2 ][1];
|
|
|
3916 |
px = this.vertices[ this.vertices.length-1 ][0];
|
|
|
3917 |
py = this.vertices[ this.vertices.length-1 ][1];
|
|
|
3918 |
ctrlX1 = px + (px - ppx);
|
|
|
3919 |
ctrlY1 = py + (py - ppy);
|
|
|
3920 |
} else {
|
|
|
3921 |
//If there is no previous curve,
|
|
|
3922 |
//the current point will be used as the first control point.
|
|
|
3923 |
ctrlX1 = this.vertices[this.vertices.length-1][0];
|
|
|
3924 |
ctrlY1 = this.vertices[this.vertices.length-1][1];
|
|
|
3925 |
}
|
|
|
3926 |
ctrlX2 = tmpArray[j];
|
|
|
3927 |
ctrlY2 = tmpArray[j + 1];
|
|
|
3928 |
endX = tmpArray[j + 2];
|
|
|
3929 |
endY = tmpArray[j + 3];
|
|
|
3930 |
this.parsePathCurveto(ctrlX1,
|
|
|
3931 |
ctrlY1,
|
|
|
3932 |
ctrlX2,
|
|
|
3933 |
ctrlY2,
|
|
|
3934 |
endX,
|
|
|
3935 |
endY);
|
|
|
3936 |
cx = endX;
|
|
|
3937 |
cy = endY;
|
|
|
3938 |
}
|
|
|
3939 |
}
|
|
|
3940 |
} else if (valOf === 115) { // s - curve to shorthand (relative)
|
|
|
3941 |
if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
|
|
|
3942 |
// need one+ multiples of 4 co-ordinates
|
|
|
3943 |
for (j = 0, k = tmpArray.length; j < k; j+=4) {
|
|
|
3944 |
if (lastInstruction.toLowerCase() === "c" ||
|
|
|
3945 |
lastInstruction.toLowerCase() === "s") {
|
|
|
3946 |
ppx = this.vertices[this.vertices.length-2][0];
|
|
|
3947 |
ppy = this.vertices[this.vertices.length-2][1];
|
|
|
3948 |
px = this.vertices[this.vertices.length-1][0];
|
|
|
3949 |
py = this.vertices[this.vertices.length-1][1];
|
|
|
3950 |
ctrlX1 = px + (px - ppx);
|
|
|
3951 |
ctrlY1 = py + (py - ppy);
|
|
|
3952 |
} else {
|
|
|
3953 |
//If there is no previous curve,
|
|
|
3954 |
//the current point will be used as the first control point.
|
|
|
3955 |
ctrlX1 = this.vertices[this.vertices.length-1][0];
|
|
|
3956 |
ctrlY1 = this.vertices[this.vertices.length-1][1];
|
|
|
3957 |
}
|
|
|
3958 |
ctrlX2 = cx + tmpArray[j];
|
|
|
3959 |
ctrlY2 = cy + tmpArray[j + 1];
|
|
|
3960 |
endX = cx + tmpArray[j + 2];
|
|
|
3961 |
endY = cy + tmpArray[j + 3];
|
|
|
3962 |
this.parsePathCurveto(ctrlX1,
|
|
|
3963 |
ctrlY1,
|
|
|
3964 |
ctrlX2,
|
|
|
3965 |
ctrlY2,
|
|
|
3966 |
endX,
|
|
|
3967 |
endY);
|
|
|
3968 |
cx = endX;
|
|
|
3969 |
cy = endY;
|
|
|
3970 |
}
|
|
|
3971 |
}
|
|
|
3972 |
} else if (valOf === 81) { // Q - quadratic curve to (absolute)
|
|
|
3973 |
if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
|
|
|
3974 |
// need one+ multiples of 4 co-ordinates
|
|
|
3975 |
for (j = 0, k = tmpArray.length; j < k; j+=4) {
|
|
|
3976 |
ctrlX = tmpArray[j];
|
|
|
3977 |
ctrlY = tmpArray[j + 1];
|
|
|
3978 |
endX = tmpArray[j + 2];
|
|
|
3979 |
endY = tmpArray[j + 3];
|
|
|
3980 |
this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
|
|
|
3981 |
cx = endX;
|
|
|
3982 |
cy = endY;
|
|
|
3983 |
}
|
|
|
3984 |
}
|
|
|
3985 |
} else if (valOf === 113) { // q - quadratic curve to (relative)
|
|
|
3986 |
if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
|
|
|
3987 |
// need one+ multiples of 4 co-ordinates
|
|
|
3988 |
for (j = 0, k = tmpArray.length; j < k; j+=4) {
|
|
|
3989 |
ctrlX = cx + tmpArray[j];
|
|
|
3990 |
ctrlY = cy + tmpArray[j + 1];
|
|
|
3991 |
endX = cx + tmpArray[j + 2];
|
|
|
3992 |
endY = cy + tmpArray[j + 3];
|
|
|
3993 |
this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
|
|
|
3994 |
cx = endX;
|
|
|
3995 |
cy = endY;
|
|
|
3996 |
}
|
|
|
3997 |
}
|
|
|
3998 |
} else if (valOf === 84) {
|
|
|
3999 |
// T - quadratic curve to shorthand (absolute)
|
|
|
4000 |
if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
|
|
|
4001 |
// need one+ pairs of co-ordinates
|
|
|
4002 |
for (j = 0, k = tmpArray.length; j < k; j+=2) {
|
|
|
4003 |
if (lastInstruction.toLowerCase() === "q" ||
|
|
|
4004 |
lastInstruction.toLowerCase() === "t") {
|
|
|
4005 |
ppx = this.vertices[this.vertices.length-2][0];
|
|
|
4006 |
ppy = this.vertices[this.vertices.length-2][1];
|
|
|
4007 |
px = this.vertices[this.vertices.length-1][0];
|
|
|
4008 |
py = this.vertices[this.vertices.length-1][1];
|
|
|
4009 |
ctrlX = px + (px - ppx);
|
|
|
4010 |
ctrlY = py + (py - ppy);
|
|
|
4011 |
} else {
|
|
|
4012 |
// If there is no previous command or if the previous command
|
|
|
4013 |
// was not a Q, q, T or t, assume the control point is
|
|
|
4014 |
// coincident with the current point.
|
|
|
4015 |
ctrlX = cx;
|
|
|
4016 |
ctrlY = cy;
|
|
|
4017 |
}
|
|
|
4018 |
endX = tmpArray[j];
|
|
|
4019 |
endY = tmpArray[j + 1];
|
|
|
4020 |
this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
|
|
|
4021 |
cx = endX;
|
|
|
4022 |
cy = endY;
|
|
|
4023 |
}
|
|
|
4024 |
}
|
|
|
4025 |
} else if (valOf === 116) {
|
|
|
4026 |
// t - quadratic curve to shorthand (relative)
|
|
|
4027 |
if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
|
|
|
4028 |
// need one+ pairs of co-ordinates
|
|
|
4029 |
for (j = 0, k = tmpArray.length; j < k; j+=2) {
|
|
|
4030 |
if (lastInstruction.toLowerCase() === "q" ||
|
|
|
4031 |
lastInstruction.toLowerCase() === "t") {
|
|
|
4032 |
ppx = this.vertices[this.vertices.length-2][0];
|
|
|
4033 |
ppy = this.vertices[this.vertices.length-2][1];
|
|
|
4034 |
px = this.vertices[this.vertices.length-1][0];
|
|
|
4035 |
py = this.vertices[this.vertices.length-1][1];
|
|
|
4036 |
ctrlX = px + (px - ppx);
|
|
|
4037 |
ctrlY = py + (py - ppy);
|
|
|
4038 |
} else {
|
|
|
4039 |
// If there is no previous command or if the previous command
|
|
|
4040 |
// was not a Q, q, T or t, assume the control point is
|
|
|
4041 |
// coincident with the current point.
|
|
|
4042 |
ctrlX = cx;
|
|
|
4043 |
ctrlY = cy;
|
|
|
4044 |
}
|
|
|
4045 |
endX = cx + tmpArray[j];
|
|
|
4046 |
endY = cy + tmpArray[j + 1];
|
|
|
4047 |
this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
|
|
|
4048 |
cx = endX;
|
|
|
4049 |
cy = endY;
|
|
|
4050 |
}
|
|
|
4051 |
}
|
|
|
4052 |
} else if (valOf === 90 || valOf === 122) { // Z or z (these do the same thing)
|
|
|
4053 |
this.close = true;
|
|
|
4054 |
}
|
|
|
4055 |
lastInstruction = command.toString();
|
|
|
4056 |
} else { i++;}
|
|
|
4057 |
}
|
|
|
4058 |
};
|
|
|
4059 |
/**
|
|
|
4060 |
* @member PShapeSVG
|
|
|
4061 |
* PShapeSVG.parsePath() helper function
|
|
|
4062 |
*
|
|
|
4063 |
* @see PShapeSVG#parsePath
|
|
|
4064 |
*/
|
|
|
4065 |
PShapeSVG.prototype.parsePathQuadto = function(x1, y1, cx, cy, x2, y2) {
|
|
|
4066 |
if (this.vertices.length > 0) {
|
|
|
4067 |
this.parsePathCode(PConstants.BEZIER_VERTEX);
|
|
|
4068 |
// x1/y1 already covered by last moveto, lineto, or curveto
|
|
|
4069 |
this.parsePathVertex(x1 + ((cx-x1)*2/3), y1 + ((cy-y1)*2/3));
|
|
|
4070 |
this.parsePathVertex(x2 + ((cx-x2)*2/3), y2 + ((cy-y2)*2/3));
|
|
|
4071 |
this.parsePathVertex(x2, y2);
|
|
|
4072 |
} else {
|
|
|
4073 |
throw ("Path must start with M/m");
|
|
|
4074 |
}
|
|
|
4075 |
};
|
|
|
4076 |
/**
|
|
|
4077 |
* @member PShapeSVG
|
|
|
4078 |
* PShapeSVG.parsePath() helper function
|
|
|
4079 |
*
|
|
|
4080 |
* @see PShapeSVG#parsePath
|
|
|
4081 |
*/
|
|
|
4082 |
PShapeSVG.prototype.parsePathCurveto = function(x1, y1, x2, y2, x3, y3) {
|
|
|
4083 |
if (this.vertices.length > 0) {
|
|
|
4084 |
this.parsePathCode(PConstants.BEZIER_VERTEX );
|
|
|
4085 |
this.parsePathVertex(x1, y1);
|
|
|
4086 |
this.parsePathVertex(x2, y2);
|
|
|
4087 |
this.parsePathVertex(x3, y3);
|
|
|
4088 |
} else {
|
|
|
4089 |
throw ("Path must start with M/m");
|
|
|
4090 |
}
|
|
|
4091 |
};
|
|
|
4092 |
/**
|
|
|
4093 |
* @member PShapeSVG
|
|
|
4094 |
* PShapeSVG.parsePath() helper function
|
|
|
4095 |
*
|
|
|
4096 |
* @see PShapeSVG#parsePath
|
|
|
4097 |
*/
|
|
|
4098 |
PShapeSVG.prototype.parsePathLineto = function(px, py) {
|
|
|
4099 |
if (this.vertices.length > 0) {
|
|
|
4100 |
this.parsePathCode(PConstants.VERTEX);
|
|
|
4101 |
this.parsePathVertex(px, py);
|
|
|
4102 |
// add property to distinguish between curContext.moveTo
|
|
|
4103 |
// or curContext.lineTo
|
|
|
4104 |
this.vertices[this.vertices.length-1]["moveTo"] = false;
|
|
|
4105 |
} else {
|
|
|
4106 |
throw ("Path must start with M/m");
|
|
|
4107 |
}
|
|
|
4108 |
};
|
|
|
4109 |
|
|
|
4110 |
PShapeSVG.prototype.parsePathMoveto = function(px, py) {
|
|
|
4111 |
if (this.vertices.length > 0) {
|
|
|
4112 |
this.parsePathCode(PConstants.BREAK);
|
|
|
4113 |
}
|
|
|
4114 |
this.parsePathCode(PConstants.VERTEX);
|
|
|
4115 |
this.parsePathVertex(px, py);
|
|
|
4116 |
// add property to distinguish between curContext.moveTo
|
|
|
4117 |
// or curContext.lineTo
|
|
|
4118 |
this.vertices[this.vertices.length-1]["moveTo"] = true;
|
|
|
4119 |
};
|
|
|
4120 |
/**
|
|
|
4121 |
* @member PShapeSVG
|
|
|
4122 |
* PShapeSVG.parsePath() helper function
|
|
|
4123 |
*
|
|
|
4124 |
* @see PShapeSVG#parsePath
|
|
|
4125 |
*/
|
|
|
4126 |
PShapeSVG.prototype.parsePathVertex = function(x, y) {
|
|
|
4127 |
var verts = [];
|
|
|
4128 |
verts[0] = x;
|
|
|
4129 |
verts[1] = y;
|
|
|
4130 |
this.vertices.push(verts);
|
|
|
4131 |
};
|
|
|
4132 |
/**
|
|
|
4133 |
* @member PShapeSVG
|
|
|
4134 |
* PShapeSVG.parsePath() helper function
|
|
|
4135 |
*
|
|
|
4136 |
* @see PShapeSVG#parsePath
|
|
|
4137 |
*/
|
|
|
4138 |
PShapeSVG.prototype.parsePathCode = function(what) {
|
|
|
4139 |
this.vertexCodes.push(what);
|
|
|
4140 |
};
|
|
|
4141 |
/**
|
|
|
4142 |
* @member PShapeSVG
|
|
|
4143 |
* The parsePoly() function parses a polyline or polygon from an SVG file.
|
|
|
4144 |
*
|
|
|
4145 |
* @param {boolean}val true if shape is closed (polygon), false if not (polyline)
|
|
|
4146 |
*/
|
|
|
4147 |
PShapeSVG.prototype.parsePoly = function(val) {
|
|
|
4148 |
this.family = PConstants.PATH;
|
|
|
4149 |
this.close = val;
|
|
|
4150 |
var pointsAttr = p.trim(this.element.getStringAttribute("points")
|
|
|
4151 |
.replace(/[,\s]+/g,' '));
|
|
|
4152 |
if (pointsAttr !== null) {
|
|
|
4153 |
//split into array
|
|
|
4154 |
var pointsBuffer = pointsAttr.split(" ");
|
|
|
4155 |
if (pointsBuffer.length % 2 === 0) {
|
|
|
4156 |
for (var i = 0, j = pointsBuffer.length; i < j; i++) {
|
|
|
4157 |
var verts = [];
|
|
|
4158 |
verts[0] = pointsBuffer[i];
|
|
|
4159 |
verts[1] = pointsBuffer[++i];
|
|
|
4160 |
this.vertices.push(verts);
|
|
|
4161 |
}
|
|
|
4162 |
} else {
|
|
|
4163 |
throw("Error parsing polygon points: odd number of coordinates provided");
|
|
|
4164 |
}
|
|
|
4165 |
}
|
|
|
4166 |
};
|
|
|
4167 |
/**
|
|
|
4168 |
* @member PShapeSVG
|
|
|
4169 |
* The parseRect() function parses a rect from an SVG file.
|
|
|
4170 |
*/
|
|
|
4171 |
PShapeSVG.prototype.parseRect = function() {
|
|
|
4172 |
this.kind = PConstants.RECT;
|
|
|
4173 |
this.family = PConstants.PRIMITIVE;
|
|
|
4174 |
this.params = [];
|
|
|
4175 |
this.params[0] = this.element.getFloatAttribute("x");
|
|
|
4176 |
this.params[1] = this.element.getFloatAttribute("y");
|
|
|
4177 |
this.params[2] = this.element.getFloatAttribute("width");
|
|
|
4178 |
this.params[3] = this.element.getFloatAttribute("height");
|
|
|
4179 |
if (this.params[2] < 0 || this.params[3] < 0) {
|
|
|
4180 |
throw("svg error: negative width or height found while parsing <rect>");
|
|
|
4181 |
}
|
|
|
4182 |
};
|
|
|
4183 |
/**
|
|
|
4184 |
* @member PShapeSVG
|
|
|
4185 |
* The parseEllipse() function handles parsing ellipse and circle tags.
|
|
|
4186 |
*
|
|
|
4187 |
* @param {boolean}val true if this is a circle and not an ellipse
|
|
|
4188 |
*/
|
|
|
4189 |
PShapeSVG.prototype.parseEllipse = function(val) {
|
|
|
4190 |
this.kind = PConstants.ELLIPSE;
|
|
|
4191 |
this.family = PConstants.PRIMITIVE;
|
|
|
4192 |
this.params = [];
|
|
|
4193 |
|
|
|
4194 |
this.params[0] = this.element.getFloatAttribute("cx") | 0 ;
|
|
|
4195 |
this.params[1] = this.element.getFloatAttribute("cy") | 0;
|
|
|
4196 |
|
|
|
4197 |
var rx, ry;
|
|
|
4198 |
if (val) {
|
|
|
4199 |
rx = ry = this.element.getFloatAttribute("r");
|
|
|
4200 |
if (rx < 0) {
|
|
|
4201 |
throw("svg error: negative radius found while parsing <circle>");
|
|
|
4202 |
}
|
|
|
4203 |
} else {
|
|
|
4204 |
rx = this.element.getFloatAttribute("rx");
|
|
|
4205 |
ry = this.element.getFloatAttribute("ry");
|
|
|
4206 |
if (rx < 0 || ry < 0) {
|
|
|
4207 |
throw("svg error: negative x-axis radius or y-axis radius found while parsing <ellipse>");
|
|
|
4208 |
}
|
|
|
4209 |
}
|
|
|
4210 |
this.params[0] -= rx;
|
|
|
4211 |
this.params[1] -= ry;
|
|
|
4212 |
|
|
|
4213 |
this.params[2] = rx*2;
|
|
|
4214 |
this.params[3] = ry*2;
|
|
|
4215 |
};
|
|
|
4216 |
/**
|
|
|
4217 |
* @member PShapeSVG
|
|
|
4218 |
* The parseLine() function handles parsing line tags.
|
|
|
4219 |
*
|
|
|
4220 |
* @param {boolean}val true if this is a circle and not an ellipse
|
|
|
4221 |
*/
|
|
|
4222 |
PShapeSVG.prototype.parseLine = function() {
|
|
|
4223 |
this.kind = PConstants.LINE;
|
|
|
4224 |
this.family = PConstants.PRIMITIVE;
|
|
|
4225 |
this.params = [];
|
|
|
4226 |
this.params[0] = this.element.getFloatAttribute("x1");
|
|
|
4227 |
this.params[1] = this.element.getFloatAttribute("y1");
|
|
|
4228 |
this.params[2] = this.element.getFloatAttribute("x2");
|
|
|
4229 |
this.params[3] = this.element.getFloatAttribute("y2");
|
|
|
4230 |
};
|
|
|
4231 |
/**
|
|
|
4232 |
* @member PShapeSVG
|
|
|
4233 |
* The parseColors() function handles parsing the opacity, strijem stroke-width, stroke-linejoin,stroke-linecap, fill, and style attributes
|
|
|
4234 |
*
|
|
|
4235 |
* @param {XMLElement}element the element of which attributes to parse
|
|
|
4236 |
*/
|
|
|
4237 |
PShapeSVG.prototype.parseColors = function(element) {
|
|
|
4238 |
if (element.hasAttribute("opacity")) {
|
|
|
4239 |
this.setOpacity(element.getAttribute("opacity"));
|
|
|
4240 |
}
|
|
|
4241 |
if (element.hasAttribute("stroke")) {
|
|
|
4242 |
this.setStroke(element.getAttribute("stroke"));
|
|
|
4243 |
}
|
|
|
4244 |
if (element.hasAttribute("stroke-width")) {
|
|
|
4245 |
// if NaN (i.e. if it's 'inherit') then default
|
|
|
4246 |
// back to the inherit setting
|
|
|
4247 |
this.setStrokeWeight(element.getAttribute("stroke-width"));
|
|
|
4248 |
}
|
|
|
4249 |
if (element.hasAttribute("stroke-linejoin") ) {
|
|
|
4250 |
this.setStrokeJoin(element.getAttribute("stroke-linejoin"));
|
|
|
4251 |
}
|
|
|
4252 |
if (element.hasAttribute("stroke-linecap")) {
|
|
|
4253 |
this.setStrokeCap(element.getStringAttribute("stroke-linecap"));
|
|
|
4254 |
}
|
|
|
4255 |
// fill defaults to black (though stroke defaults to "none")
|
|
|
4256 |
// http://www.w3.org/TR/SVG/painting.html#FillProperties
|
|
|
4257 |
if (element.hasAttribute("fill")) {
|
|
|
4258 |
this.setFill(element.getStringAttribute("fill"));
|
|
|
4259 |
}
|
|
|
4260 |
if (element.hasAttribute("style")) {
|
|
|
4261 |
var styleText = element.getStringAttribute("style");
|
|
|
4262 |
var styleTokens = styleText.toString().split( ";" );
|
|
|
4263 |
|
|
|
4264 |
for (var i = 0, j = styleTokens.length; i < j; i++) {
|
|
|
4265 |
var tokens = p.trim(styleTokens[i].split( ":" ));
|
|
|
4266 |
if (tokens[0] === "fill") {
|
|
|
4267 |
this.setFill(tokens[1]);
|
|
|
4268 |
} else if (tokens[0] === "fill-opacity") {
|
|
|
4269 |
this.setFillOpacity(tokens[1]);
|
|
|
4270 |
} else if (tokens[0] === "stroke") {
|
|
|
4271 |
this.setStroke(tokens[1]);
|
|
|
4272 |
} else if (tokens[0] === "stroke-width") {
|
|
|
4273 |
this.setStrokeWeight(tokens[1]);
|
|
|
4274 |
} else if (tokens[0] === "stroke-linecap") {
|
|
|
4275 |
this.setStrokeCap(tokens[1]);
|
|
|
4276 |
} else if (tokens[0] === "stroke-linejoin") {
|
|
|
4277 |
this.setStrokeJoin(tokens[1]);
|
|
|
4278 |
} else if (tokens[0] === "stroke-opacity") {
|
|
|
4279 |
this.setStrokeOpacity(tokens[1]);
|
|
|
4280 |
} else if (tokens[0] === "opacity") {
|
|
|
4281 |
this.setOpacity(tokens[1]);
|
|
|
4282 |
} // Other attributes are not yet implemented
|
|
|
4283 |
}
|
|
|
4284 |
}
|
|
|
4285 |
};
|
|
|
4286 |
/**
|
|
|
4287 |
* @member PShapeSVG
|
|
|
4288 |
* PShapeSVG.parseColors() helper function
|
|
|
4289 |
*
|
|
|
4290 |
* @param {String} opacityText the value of fillOpacity
|
|
|
4291 |
*
|
|
|
4292 |
* @see PShapeSVG#parseColors
|
|
|
4293 |
*/
|
|
|
4294 |
PShapeSVG.prototype.setFillOpacity = function(opacityText) {
|
|
|
4295 |
this.fillOpacity = parseFloat(opacityText);
|
|
|
4296 |
this.fillColor = this.fillOpacity * 255 << 24 |
|
|
|
4297 |
this.fillColor & 0xFFFFFF;
|
|
|
4298 |
};
|
|
|
4299 |
/**
|
|
|
4300 |
* @member PShapeSVG
|
|
|
4301 |
* PShapeSVG.parseColors() helper function
|
|
|
4302 |
*
|
|
|
4303 |
* @param {String} fillText the value of fill
|
|
|
4304 |
*
|
|
|
4305 |
* @see PShapeSVG#parseColors
|
|
|
4306 |
*/
|
|
|
4307 |
PShapeSVG.prototype.setFill = function (fillText) {
|
|
|
4308 |
var opacityMask = this.fillColor & 0xFF000000;
|
|
|
4309 |
if (fillText === "none") {
|
|
|
4310 |
this.fill = false;
|
|
|
4311 |
} else if (fillText.indexOf("#") === 0) {
|
|
|
4312 |
this.fill = true;
|
|
|
4313 |
if (fillText.length === 4) {
|
|
|
4314 |
// convert #00F to #0000FF
|
|
|
4315 |
fillText = fillText.replace(/#(.)(.)(.)/,"#$1$1$2$2$3$3");
|
|
|
4316 |
}
|
|
|
4317 |
this.fillColor = opacityMask |
|
|
|
4318 |
(parseInt(fillText.substring(1), 16 )) &
|
|
|
4319 |
0xFFFFFF;
|
|
|
4320 |
} else if (fillText.indexOf("rgb") === 0) {
|
|
|
4321 |
this.fill = true;
|
|
|
4322 |
this.fillColor = opacityMask | this.parseRGB(fillText);
|
|
|
4323 |
} else if (fillText.indexOf("url(#") === 0) {
|
|
|
4324 |
this.fillName = fillText.substring(5, fillText.length - 1 );
|
|
|
4325 |
} else if (colors[fillText]) {
|
|
|
4326 |
this.fill = true;
|
|
|
4327 |
this.fillColor = opacityMask |
|
|
|
4328 |
(parseInt(colors[fillText].substring(1), 16)) &
|
|
|
4329 |
0xFFFFFF;
|
|
|
4330 |
}
|
|
|
4331 |
};
|
|
|
4332 |
/**
|
|
|
4333 |
* @member PShapeSVG
|
|
|
4334 |
* PShapeSVG.parseColors() helper function
|
|
|
4335 |
*
|
|
|
4336 |
* @param {String} opacity the value of opacity
|
|
|
4337 |
*
|
|
|
4338 |
* @see PShapeSVG#parseColors
|
|
|
4339 |
*/
|
|
|
4340 |
PShapeSVG.prototype.setOpacity = function(opacity) {
|
|
|
4341 |
this.strokeColor = parseFloat(opacity) * 255 << 24 |
|
|
|
4342 |
this.strokeColor & 0xFFFFFF;
|
|
|
4343 |
this.fillColor = parseFloat(opacity) * 255 << 24 |
|
|
|
4344 |
this.fillColor & 0xFFFFFF;
|
|
|
4345 |
};
|
|
|
4346 |
/**
|
|
|
4347 |
* @member PShapeSVG
|
|
|
4348 |
* PShapeSVG.parseColors() helper function
|
|
|
4349 |
*
|
|
|
4350 |
* @param {String} strokeText the value to set stroke to
|
|
|
4351 |
*
|
|
|
4352 |
* @see PShapeSVG#parseColors
|
|
|
4353 |
*/
|
|
|
4354 |
PShapeSVG.prototype.setStroke = function(strokeText) {
|
|
|
4355 |
var opacityMask = this.strokeColor & 0xFF000000;
|
|
|
4356 |
if (strokeText === "none") {
|
|
|
4357 |
this.stroke = false;
|
|
|
4358 |
} else if (strokeText.charAt( 0 ) === "#") {
|
|
|
4359 |
this.stroke = true;
|
|
|
4360 |
if (strokeText.length === 4) {
|
|
|
4361 |
// convert #00F to #0000FF
|
|
|
4362 |
strokeText = strokeText.replace(/#(.)(.)(.)/,"#$1$1$2$2$3$3");
|
|
|
4363 |
}
|
|
|
4364 |
this.strokeColor = opacityMask |
|
|
|
4365 |
(parseInt( strokeText.substring( 1 ), 16 )) &
|
|
|
4366 |
0xFFFFFF;
|
|
|
4367 |
} else if (strokeText.indexOf( "rgb" ) === 0 ) {
|
|
|
4368 |
this.stroke = true;
|
|
|
4369 |
this.strokeColor = opacityMask | this.parseRGB(strokeText);
|
|
|
4370 |
} else if (strokeText.indexOf( "url(#" ) === 0) {
|
|
|
4371 |
this.strokeName = strokeText.substring(5, strokeText.length - 1);
|
|
|
4372 |
} else if (colors[strokeText]) {
|
|
|
4373 |
this.stroke = true;
|
|
|
4374 |
this.strokeColor = opacityMask |
|
|
|
4375 |
(parseInt(colors[strokeText].substring(1), 16)) &
|
|
|
4376 |
0xFFFFFF;
|
|
|
4377 |
}
|
|
|
4378 |
};
|
|
|
4379 |
/**
|
|
|
4380 |
* @member PShapeSVG
|
|
|
4381 |
* PShapeSVG.parseColors() helper function
|
|
|
4382 |
*
|
|
|
4383 |
* @param {String} weight the value to set strokeWeight to
|
|
|
4384 |
*
|
|
|
4385 |
* @see PShapeSVG#parseColors
|
|
|
4386 |
*/
|
|
|
4387 |
PShapeSVG.prototype.setStrokeWeight = function(weight) {
|
|
|
4388 |
this.strokeWeight = this.parseUnitSize(weight);
|
|
|
4389 |
};
|
|
|
4390 |
/**
|
|
|
4391 |
* @member PShapeSVG
|
|
|
4392 |
* PShapeSVG.parseColors() helper function
|
|
|
4393 |
*
|
|
|
4394 |
* @param {String} linejoin the value to set strokeJoin to
|
|
|
4395 |
*
|
|
|
4396 |
* @see PShapeSVG#parseColors
|
|
|
4397 |
*/
|
|
|
4398 |
PShapeSVG.prototype.setStrokeJoin = function(linejoin) {
|
|
|
4399 |
if (linejoin === "miter") {
|
|
|
4400 |
this.strokeJoin = PConstants.MITER;
|
|
|
4401 |
|
|
|
4402 |
} else if (linejoin === "round") {
|
|
|
4403 |
this.strokeJoin = PConstants.ROUND;
|
|
|
4404 |
|
|
|
4405 |
} else if (linejoin === "bevel") {
|
|
|
4406 |
this.strokeJoin = PConstants.BEVEL;
|
|
|
4407 |
}
|
|
|
4408 |
};
|
|
|
4409 |
/**
|
|
|
4410 |
* @member PShapeSVG
|
|
|
4411 |
* PShapeSVG.parseColors() helper function
|
|
|
4412 |
*
|
|
|
4413 |
* @param {String} linecap the value to set strokeCap to
|
|
|
4414 |
*
|
|
|
4415 |
* @see PShapeSVG#parseColors
|
|
|
4416 |
*/
|
|
|
4417 |
PShapeSVG.prototype.setStrokeCap = function (linecap) {
|
|
|
4418 |
if (linecap === "butt") {
|
|
|
4419 |
this.strokeCap = PConstants.SQUARE;
|
|
|
4420 |
|
|
|
4421 |
} else if (linecap === "round") {
|
|
|
4422 |
this.strokeCap = PConstants.ROUND;
|
|
|
4423 |
|
|
|
4424 |
} else if (linecap === "square") {
|
|
|
4425 |
this.strokeCap = PConstants.PROJECT;
|
|
|
4426 |
}
|
|
|
4427 |
};
|
|
|
4428 |
/**
|
|
|
4429 |
* @member PShapeSVG
|
|
|
4430 |
* PShapeSVG.parseColors() helper function
|
|
|
4431 |
*
|
|
|
4432 |
* @param {String} opacityText the value to set stroke opacity to
|
|
|
4433 |
*
|
|
|
4434 |
* @see PShapeSVG#parseColors
|
|
|
4435 |
*/
|
|
|
4436 |
PShapeSVG.prototype.setStrokeOpacity = function (opacityText) {
|
|
|
4437 |
this.strokeOpacity = parseFloat(opacityText);
|
|
|
4438 |
this.strokeColor = this.strokeOpacity * 255 << 24 |
|
|
|
4439 |
this.strokeColor &
|
|
|
4440 |
0xFFFFFF;
|
|
|
4441 |
};
|
|
|
4442 |
/**
|
|
|
4443 |
* @member PShapeSVG
|
|
|
4444 |
* The parseRGB() function parses an rbg() color string and returns a color int
|
|
|
4445 |
*
|
|
|
4446 |
* @param {String} color the color to parse in rbg() format
|
|
|
4447 |
*
|
|
|
4448 |
* @return {int} the equivalent color int
|
|
|
4449 |
*/
|
|
|
4450 |
PShapeSVG.prototype.parseRGB = function(color) {
|
|
|
4451 |
var sub = color.substring(color.indexOf('(') + 1, color.indexOf(')'));
|
|
|
4452 |
var values = sub.split(", ");
|
|
|
4453 |
return (values[0] << 16) | (values[1] << 8) | (values[2]);
|
|
|
4454 |
};
|
|
|
4455 |
/**
|
|
|
4456 |
* @member PShapeSVG
|
|
|
4457 |
* The parseUnitSize() function parse a size that may have a suffix for its units.
|
|
|
4458 |
* Ignoring cases where this could also be a percentage.
|
|
|
4459 |
* The <A HREF="http://www.w3.org/TR/SVG/coords.html#Units">units</A> spec:
|
|
|
4460 |
* <UL>
|
|
|
4461 |
* <LI>"1pt" equals "1.25px" (and therefore 1.25 user units)
|
|
|
4462 |
* <LI>"1pc" equals "15px" (and therefore 15 user units)
|
|
|
4463 |
* <LI>"1mm" would be "3.543307px" (3.543307 user units)
|
|
|
4464 |
* <LI>"1cm" equals "35.43307px" (and therefore 35.43307 user units)
|
|
|
4465 |
* <LI>"1in" equals "90px" (and therefore 90 user units)
|
|
|
4466 |
* </UL>
|
|
|
4467 |
*/
|
|
|
4468 |
PShapeSVG.prototype.parseUnitSize = function (text) {
|
|
|
4469 |
var len = text.length - 2;
|
|
|
4470 |
if (len < 0) { return text; }
|
|
|
4471 |
if (text.indexOf("pt") === len) {
|
|
|
4472 |
return parseFloat(text.substring(0, len)) * 1.25;
|
|
|
4473 |
}
|
|
|
4474 |
if (text.indexOf("pc") === len) {
|
|
|
4475 |
return parseFloat( text.substring( 0, len)) * 15;
|
|
|
4476 |
}
|
|
|
4477 |
if (text.indexOf("mm") === len) {
|
|
|
4478 |
return parseFloat( text.substring(0, len)) * 3.543307;
|
|
|
4479 |
}
|
|
|
4480 |
if (text.indexOf("cm") === len) {
|
|
|
4481 |
return parseFloat(text.substring(0, len)) * 35.43307;
|
|
|
4482 |
}
|
|
|
4483 |
if (text.indexOf("in") === len) {
|
|
|
4484 |
return parseFloat(text.substring(0, len)) * 90;
|
|
|
4485 |
}
|
|
|
4486 |
if (text.indexOf("px") === len) {
|
|
|
4487 |
return parseFloat(text.substring(0, len));
|
|
|
4488 |
}
|
|
|
4489 |
return parseFloat(text);
|
|
|
4490 |
};
|
|
|
4491 |
/**
|
|
|
4492 |
* The shape() function displays shapes to the screen.
|
|
|
4493 |
* Processing currently works with SVG shapes only.
|
|
|
4494 |
* The <b>shape</b> parameter specifies the shape to display and the <b>x</b>
|
|
|
4495 |
* and <b>y</b> parameters define the location of the shape from its
|
|
|
4496 |
* upper-left corner.
|
|
|
4497 |
* The shape is displayed at its original size unless the <b>width</b>
|
|
|
4498 |
* and <b>height</b> parameters specify a different size.
|
|
|
4499 |
* The <b>shapeMode()</b> function changes the way the parameters work.
|
|
|
4500 |
* A call to <b>shapeMode(CORNERS)</b>, for example, will change the width
|
|
|
4501 |
* and height parameters to define the x and y values of the opposite corner
|
|
|
4502 |
* of the shape.
|
|
|
4503 |
* <br><br>
|
|
|
4504 |
* Note complex shapes may draw awkwardly with P2D, P3D, and OPENGL. Those
|
|
|
4505 |
* renderers do not yet support shapes that have holes or complicated breaks.
|
|
|
4506 |
*
|
|
|
4507 |
* @param {PShape} shape the shape to display
|
|
|
4508 |
* @param {int|float} x x-coordinate of the shape
|
|
|
4509 |
* @param {int|float} y y-coordinate of the shape
|
|
|
4510 |
* @param {int|float} width width to display the shape
|
|
|
4511 |
* @param {int|float} height height to display the shape
|
|
|
4512 |
*
|
|
|
4513 |
* @see PShape
|
|
|
4514 |
* @see loadShape()
|
|
|
4515 |
* @see shapeMode()
|
|
|
4516 |
*/
|
|
|
4517 |
p.shape = function(shape, x, y, width, height) {
|
|
|
4518 |
if (arguments.length >= 1 && arguments[0] !== null) {
|
|
|
4519 |
if (shape.isVisible()) {
|
|
|
4520 |
p.pushMatrix();
|
|
|
4521 |
if (curShapeMode === PConstants.CENTER) {
|
|
|
4522 |
if (arguments.length === 5) {
|
|
|
4523 |
p.translate(x - width/2, y - height/2);
|
|
|
4524 |
p.scale(width / shape.getWidth(), height / shape.getHeight());
|
|
|
4525 |
} else if (arguments.length === 3) {
|
|
|
4526 |
p.translate(x - shape.getWidth()/2, - shape.getHeight()/2);
|
|
|
4527 |
} else {
|
|
|
4528 |
p.translate(-shape.getWidth()/2, -shape.getHeight()/2);
|
|
|
4529 |
}
|
|
|
4530 |
} else if (curShapeMode === PConstants.CORNER) {
|
|
|
4531 |
if (arguments.length === 5) {
|
|
|
4532 |
p.translate(x, y);
|
|
|
4533 |
p.scale(width / shape.getWidth(), height / shape.getHeight());
|
|
|
4534 |
} else if (arguments.length === 3) {
|
|
|
4535 |
p.translate(x, y);
|
|
|
4536 |
}
|
|
|
4537 |
} else if (curShapeMode === PConstants.CORNERS) {
|
|
|
4538 |
if (arguments.length === 5) {
|
|
|
4539 |
width -= x;
|
|
|
4540 |
height -= y;
|
|
|
4541 |
p.translate(x, y);
|
|
|
4542 |
p.scale(width / shape.getWidth(), height / shape.getHeight());
|
|
|
4543 |
} else if (arguments.length === 3) {
|
|
|
4544 |
p.translate(x, y);
|
|
|
4545 |
}
|
|
|
4546 |
}
|
|
|
4547 |
shape.draw();
|
|
|
4548 |
if ((arguments.length === 1 && curShapeMode === PConstants.CENTER ) || arguments.length > 1) {
|
|
|
4549 |
p.popMatrix();
|
|
|
4550 |
}
|
|
|
4551 |
}
|
|
|
4552 |
}
|
|
|
4553 |
};
|
|
|
4554 |
|
|
|
4555 |
/**
|
|
|
4556 |
* The shapeMode() function modifies the location from which shapes draw.
|
|
|
4557 |
* The default mode is <b>shapeMode(CORNER)</b>, which specifies the
|
|
|
4558 |
* location to be the upper left corner of the shape and uses the third
|
|
|
4559 |
* and fourth parameters of <b>shape()</b> to specify the width and height.
|
|
|
4560 |
* The syntax <b>shapeMode(CORNERS)</b> uses the first and second parameters
|
|
|
4561 |
* of <b>shape()</b> to set the location of one corner and uses the third
|
|
|
4562 |
* and fourth parameters to set the opposite corner.
|
|
|
4563 |
* The syntax <b>shapeMode(CENTER)</b> draws the shape from its center point
|
|
|
4564 |
* and uses the third and forth parameters of <b>shape()</b> to specify the
|
|
|
4565 |
* width and height.
|
|
|
4566 |
* The parameter must be written in "ALL CAPS" because Processing syntax
|
|
|
4567 |
* is case sensitive.
|
|
|
4568 |
*
|
|
|
4569 |
* @param {int} mode One of CORNER, CORNERS, CENTER
|
|
|
4570 |
*
|
|
|
4571 |
* @see shape()
|
|
|
4572 |
* @see rectMode()
|
|
|
4573 |
*/
|
|
|
4574 |
p.shapeMode = function (mode) {
|
|
|
4575 |
curShapeMode = mode;
|
|
|
4576 |
};
|
|
|
4577 |
|
|
|
4578 |
/**
|
|
|
4579 |
* The loadShape() function loads vector shapes into a variable of type PShape. Currently, only SVG files may be loaded.
|
|
|
4580 |
* 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.
|
|
|
4581 |
*
|
|
|
4582 |
* @param {String} filename an SVG file
|
|
|
4583 |
*
|
|
|
4584 |
* @return {PShape} a object of type PShape or null
|
|
|
4585 |
* @see PShape
|
|
|
4586 |
* @see PApplet#shape()
|
|
|
4587 |
* @see PApplet#shapeMode()
|
|
|
4588 |
*/
|
|
|
4589 |
p.loadShape = function (filename) {
|
|
|
4590 |
if (arguments.length === 1) {
|
|
|
4591 |
if (filename.indexOf(".svg") > -1) {
|
|
|
4592 |
return new PShapeSVG(null, filename);
|
|
|
4593 |
}
|
|
|
4594 |
}
|
|
|
4595 |
return null;
|
|
|
4596 |
};
|
|
|
4597 |
|
|
|
4598 |
/**
|
|
|
4599 |
* XMLAttribute is an attribute of a XML element. This is an internal class
|
|
|
4600 |
*
|
|
|
4601 |
* @param {String} fname the full name of the attribute
|
|
|
4602 |
* @param {String} n the short name of the attribute
|
|
|
4603 |
* @param {String} namespace the namespace URI of the attribute
|
|
|
4604 |
* @param {String} v the value of the attribute
|
|
|
4605 |
* @param {String }t the type of the attribute
|
|
|
4606 |
*
|
|
|
4607 |
* @see XMLElement
|
|
|
4608 |
*/
|
|
|
4609 |
var XMLAttribute = function(fname, n, nameSpace, v, t){
|
|
|
4610 |
this.fullName = fname || "";
|
|
|
4611 |
this.name = n || "";
|
|
|
4612 |
this.namespace = nameSpace || "";
|
|
|
4613 |
this.value = v;
|
|
|
4614 |
this.type = t;
|
|
|
4615 |
};
|
|
|
4616 |
/**
|
|
|
4617 |
* XMLAttribute methods
|
|
|
4618 |
*/
|
|
|
4619 |
XMLAttribute.prototype = {
|
|
|
4620 |
/**
|
|
|
4621 |
* @member XMLAttribute
|
|
|
4622 |
* The getName() function returns the short name of the attribute
|
|
|
4623 |
*
|
|
|
4624 |
* @return {String} the short name of the attribute
|
|
|
4625 |
*/
|
|
|
4626 |
getName: function() {
|
|
|
4627 |
return this.name;
|
|
|
4628 |
},
|
|
|
4629 |
/**
|
|
|
4630 |
* @member XMLAttribute
|
|
|
4631 |
* The getFullName() function returns the full name of the attribute
|
|
|
4632 |
*
|
|
|
4633 |
* @return {String} the full name of the attribute
|
|
|
4634 |
*/
|
|
|
4635 |
getFullName: function() {
|
|
|
4636 |
return this.fullName;
|
|
|
4637 |
},
|
|
|
4638 |
/**
|
|
|
4639 |
* @member XMLAttribute
|
|
|
4640 |
* The getNamespace() function returns the namespace of the attribute
|
|
|
4641 |
*
|
|
|
4642 |
* @return {String} the namespace of the attribute
|
|
|
4643 |
*/
|
|
|
4644 |
getNamespace: function() {
|
|
|
4645 |
return this.namespace;
|
|
|
4646 |
},
|
|
|
4647 |
/**
|
|
|
4648 |
* @member XMLAttribute
|
|
|
4649 |
* The getValue() function returns the value of the attribute
|
|
|
4650 |
*
|
|
|
4651 |
* @return {String} the value of the attribute
|
|
|
4652 |
*/
|
|
|
4653 |
getValue: function() {
|
|
|
4654 |
return this.value;
|
|
|
4655 |
},
|
|
|
4656 |
/**
|
|
|
4657 |
* @member XMLAttribute
|
|
|
4658 |
* The getValue() function returns the type of the attribute
|
|
|
4659 |
*
|
|
|
4660 |
* @return {String} the type of the attribute
|
|
|
4661 |
*/
|
|
|
4662 |
getType: function() {
|
|
|
4663 |
return this.type;
|
|
|
4664 |
},
|
|
|
4665 |
/**
|
|
|
4666 |
* @member XMLAttribute
|
|
|
4667 |
* The setValue() function sets the value of the attribute
|
|
|
4668 |
*
|
|
|
4669 |
* @param {String} newval the new value
|
|
|
4670 |
*/
|
|
|
4671 |
setValue: function(newval) {
|
|
|
4672 |
this.value = newval;
|
|
|
4673 |
}
|
|
|
4674 |
};
|
|
|
4675 |
|
|
|
4676 |
/**
|
|
|
4677 |
* XMLElement is a representation of an XML object. The object is able to parse XML code
|
|
|
4678 |
*
|
|
|
4679 |
* @param {PApplet} parent typically use "this"
|
|
|
4680 |
* @param {String} filename name of the XML/SVG file to load
|
|
|
4681 |
* @param {String} xml the xml/svg string
|
|
|
4682 |
* @param {String} fullname the full name of the element
|
|
|
4683 |
* @param {String} namespace the namespace of the URI
|
|
|
4684 |
* @param {String} systemID the system ID of the XML data where the element starts
|
|
|
4685 |
* @param {Integer }lineNr the line in the XML data where the element starts
|
|
|
4686 |
*/
|
|
|
4687 |
var XMLElement = p.XMLElement = function() {
|
|
|
4688 |
this.attributes = [];
|
|
|
4689 |
this.children = [];
|
|
|
4690 |
this.fullName = null;
|
|
|
4691 |
this.name = null;
|
|
|
4692 |
this.namespace = "";
|
|
|
4693 |
this.content = null;
|
|
|
4694 |
this.parent = null;
|
|
|
4695 |
this.lineNr = "";
|
|
|
4696 |
this.systemID = "";
|
|
|
4697 |
this.type = "ELEMENT";
|
|
|
4698 |
|
|
|
4699 |
if (arguments.length === 4) {
|
|
|
4700 |
this.fullName = arguments[0] || "";
|
|
|
4701 |
if (arguments[1]) {
|
|
|
4702 |
this.name = arguments[1];
|
|
|
4703 |
} else {
|
|
|
4704 |
var index = this.fullName.indexOf(':');
|
|
|
4705 |
if (index >= 0) {
|
|
|
4706 |
this.name = this.fullName.substring(index + 1);
|
|
|
4707 |
} else {
|
|
|
4708 |
this.name = this.fullName;
|
|
|
4709 |
}
|
|
|
4710 |
}
|
|
|
4711 |
this.namespace = arguments[1];
|
|
|
4712 |
this.lineNr = arguments[3];
|
|
|
4713 |
this.systemID = arguments[2];
|
|
|
4714 |
}
|
|
|
4715 |
else if ((arguments.length === 2 && arguments[1].indexOf(".") > -1) ) {
|
|
|
4716 |
// filename or svg xml element
|
|
|
4717 |
this.parse(arguments[arguments.length -1]);
|
|
|
4718 |
} else if (arguments.length === 1 && typeof arguments[0] === "string"){
|
|
|
4719 |
this.parse(arguments[0]);
|
|
|
4720 |
}
|
|
|
4721 |
};
|
|
|
4722 |
/**
|
|
|
4723 |
* XMLElement methods
|
|
|
4724 |
* missing: enumerateAttributeNames(), enumerateChildren(),
|
|
|
4725 |
* NOTE: parse does not work when a url is passed in
|
|
|
4726 |
*/
|
|
|
4727 |
XMLElement.prototype = {
|
|
|
4728 |
/**
|
|
|
4729 |
* @member XMLElement
|
|
|
4730 |
* The parse() function retrieves the file via ajax() and uses DOMParser()
|
|
|
4731 |
* parseFromString method to make an XML document
|
|
|
4732 |
* @addon
|
|
|
4733 |
*
|
|
|
4734 |
* @param {String} filename name of the XML/SVG file to load
|
|
|
4735 |
*
|
|
|
4736 |
* @throws ExceptionType Error loading document
|
|
|
4737 |
*
|
|
|
4738 |
* @see XMLElement#parseChildrenRecursive
|
|
|
4739 |
*/
|
|
|
4740 |
parse: function(textstring) {
|
|
|
4741 |
var xmlDoc;
|
|
|
4742 |
try {
|
|
|
4743 |
var extension = textstring.substring(textstring.length-4);
|
|
|
4744 |
if (extension === ".xml" || extension === ".svg") {
|
|
|
4745 |
textstring = ajax(textstring);
|
|
|
4746 |
}
|
|
|
4747 |
xmlDoc = new DOMParser().parseFromString(textstring, "text/xml");
|
|
|
4748 |
var elements = xmlDoc.documentElement;
|
|
|
4749 |
if (elements) {
|
|
|
4750 |
this.parseChildrenRecursive(null, elements);
|
|
|
4751 |
} else {
|
|
|
4752 |
throw ("Error loading document");
|
|
|
4753 |
}
|
|
|
4754 |
return this;
|
|
|
4755 |
} catch(e) {
|
|
|
4756 |
throw(e);
|
|
|
4757 |
}
|
|
|
4758 |
},
|
|
|
4759 |
/**
|
|
|
4760 |
* @member XMLElement
|
|
|
4761 |
* Internal helper function for parse().
|
|
|
4762 |
* Loops through the
|
|
|
4763 |
* @addon
|
|
|
4764 |
*
|
|
|
4765 |
* @param {XMLElement} parent the parent node
|
|
|
4766 |
* @param {XML document childNodes} elementpath the remaining nodes that need parsing
|
|
|
4767 |
*
|
|
|
4768 |
* @return {XMLElement} the new element and its children elements
|
|
|
4769 |
*/
|
|
|
4770 |
parseChildrenRecursive: function (parent , elementpath){
|
|
|
4771 |
var xmlelement,
|
|
|
4772 |
xmlattribute,
|
|
|
4773 |
tmpattrib,
|
|
|
4774 |
l, m,
|
|
|
4775 |
child;
|
|
|
4776 |
if (!parent) { // this element is the root element
|
|
|
4777 |
this.fullName = elementpath.localName;
|
|
|
4778 |
this.name = elementpath.nodeName;
|
|
|
4779 |
xmlelement = this;
|
|
|
4780 |
} else { // this element has a parent
|
|
|
4781 |
xmlelement = new XMLElement(elementpath.localName, elementpath.nodeName, "", "");
|
|
|
4782 |
xmlelement.parent = parent;
|
|
|
4783 |
}
|
|
|
4784 |
|
|
|
4785 |
// if this is a text node, return a PCData element, instead of an XML element.
|
|
|
4786 |
if(elementpath.nodeType === 3 && elementpath.textContent !== "") {
|
|
|
4787 |
return this.createPCDataElement(elementpath.textContent);
|
|
|
4788 |
}
|
|
|
4789 |
|
|
|
4790 |
// bind all attributes
|
|
|
4791 |
for (l = 0, m = elementpath.attributes.length; l < m; l++) {
|
|
|
4792 |
tmpattrib = elementpath.attributes[l];
|
|
|
4793 |
xmlattribute = new XMLAttribute(tmpattrib.getname,
|
|
|
4794 |
tmpattrib.nodeName,
|
|
|
4795 |
tmpattrib.namespaceURI,
|
|
|
4796 |
tmpattrib.nodeValue,
|
|
|
4797 |
tmpattrib.nodeType);
|
|
|
4798 |
xmlelement.attributes.push(xmlattribute);
|
|
|
4799 |
}
|
|
|
4800 |
|
|
|
4801 |
// bind all children
|
|
|
4802 |
for (l = 0, m = elementpath.childNodes.length; l < m; l++) {
|
|
|
4803 |
var node = elementpath.childNodes[l];
|
|
|
4804 |
if (node.nodeType === 1 || node.nodeType === 3) { // ELEMENT_NODE or TEXT_NODE
|
|
|
4805 |
child = xmlelement.parseChildrenRecursive(xmlelement, node);
|
|
|
4806 |
if (child !== null) {
|
|
|
4807 |
xmlelement.children.push(child);
|
|
|
4808 |
}
|
|
|
4809 |
}
|
|
|
4810 |
}
|
|
|
4811 |
|
|
|
4812 |
return xmlelement;
|
|
|
4813 |
},
|
|
|
4814 |
/**
|
|
|
4815 |
* @member XMLElement
|
|
|
4816 |
* The createElement() function Creates an empty element
|
|
|
4817 |
*
|
|
|
4818 |
* @param {String} fullName the full name of the element
|
|
|
4819 |
* @param {String} namespace the namespace URI
|
|
|
4820 |
* @param {String} systemID the system ID of the XML data where the element starts
|
|
|
4821 |
* @param {int} lineNr the line in the XML data where the element starts
|
|
|
4822 |
*/
|
|
|
4823 |
createElement: function () {
|
|
|
4824 |
if (arguments.length === 2) {
|
|
|
4825 |
return new XMLElement(arguments[0], arguments[1], null, null);
|
|
|
4826 |
}
|
|
|
4827 |
return new XMLElement(arguments[0], arguments[1], arguments[2], arguments[3]);
|
|
|
4828 |
},
|
|
|
4829 |
/**
|
|
|
4830 |
* @member XMLElement
|
|
|
4831 |
* The createPCDataElement() function creates an element to be used for #PCDATA content.
|
|
|
4832 |
* Because Processing discards whitespace TEXT nodes, this method will not build an element
|
|
|
4833 |
* if the passed content is empty after trimming for whitespace.
|
|
|
4834 |
*
|
|
|
4835 |
* @return {XMLElement} new "test" XMLElement, or null if content consists only of whitespace
|
|
|
4836 |
*/
|
|
|
4837 |
createPCDataElement: function (content) {
|
|
|
4838 |
if(content.replace(/^\s+$/g,"") === "") {
|
|
|
4839 |
return null;
|
|
|
4840 |
}
|
|
|
4841 |
var pcdata = new XMLElement();
|
|
|
4842 |
pcdata.content = content;
|
|
|
4843 |
pcdata.type = "TEXT";
|
|
|
4844 |
return pcdata;
|
|
|
4845 |
},
|
|
|
4846 |
/**
|
|
|
4847 |
* @member XMLElement
|
|
|
4848 |
* The hasAttribute() function returns whether an attribute exists
|
|
|
4849 |
*
|
|
|
4850 |
* @param {String} name name of the attribute
|
|
|
4851 |
* @param {String} namespace the namespace URI of the attribute
|
|
|
4852 |
*
|
|
|
4853 |
* @return {boolean} true if the attribute exists
|
|
|
4854 |
*/
|
|
|
4855 |
hasAttribute: function () {
|
|
|
4856 |
if (arguments.length === 1) {
|
|
|
4857 |
return this.getAttribute(arguments[0]) !== null;
|
|
|
4858 |
}
|
|
|
4859 |
if (arguments.length === 2) {
|
|
|
4860 |
return this.getAttribute(arguments[0],arguments[1]) !== null;
|
|
|
4861 |
}
|
|
|
4862 |
},
|
|
|
4863 |
/**
|
|
|
4864 |
* @member XMLElement
|
|
|
4865 |
* The equals() function checks to see if the XMLElement being passed in equals another XMLElement
|
|
|
4866 |
*
|
|
|
4867 |
* @param {XMLElement} rawElement the element to compare to
|
|
|
4868 |
*
|
|
|
4869 |
* @return {boolean} true if the element equals another element
|
|
|
4870 |
*/
|
|
|
4871 |
equals: function(other) {
|
|
|
4872 |
if (!(other instanceof XMLElement)) {
|
|
|
4873 |
return false;
|
|
|
4874 |
}
|
|
|
4875 |
var i, j;
|
|
|
4876 |
if (this.name !== other.getLocalName()) { return false; }
|
|
|
4877 |
if (this.attributes.length !== other.getAttributeCount()) { return false; }
|
|
|
4878 |
// attributes may be ordered differently
|
|
|
4879 |
if (this.attributes.length !== other.attributes.length) { return false; }
|
|
|
4880 |
var attr_name, attr_ns, attr_value, attr_type, attr_other;
|
|
|
4881 |
for (i = 0, j = this.attributes.length; i < j; i++) {
|
|
|
4882 |
attr_name = this.attributes[i].getName();
|
|
|
4883 |
attr_ns = this.attributes[i].getNamespace();
|
|
|
4884 |
attr_other = other.findAttribute(attr_name, attr_ns);
|
|
|
4885 |
if (attr_other === null) { return false; }
|
|
|
4886 |
if (this.attributes[i].getValue() !== attr_other.getValue()) { return false; }
|
|
|
4887 |
if (this.attributes[i].getType() !== attr_other.getType()) { return false; }
|
|
|
4888 |
}
|
|
|
4889 |
// children must be ordered identically
|
|
|
4890 |
if (this.children.length !== other.getChildCount()) { return false; }
|
|
|
4891 |
if (this.children.length>0) {
|
|
|
4892 |
var child1, child2;
|
|
|
4893 |
for (i = 0, j = this.children.length; i < j; i++) {
|
|
|
4894 |
child1 = this.getChild(i);
|
|
|
4895 |
child2 = other.getChild(i);
|
|
|
4896 |
if (!child1.equals(child2)) { return false; }
|
|
|
4897 |
}
|
|
|
4898 |
return true;
|
|
|
4899 |
}
|
|
|
4900 |
return (this.content === other.content);
|
|
|
4901 |
},
|
|
|
4902 |
/**
|
|
|
4903 |
* @member XMLElement
|
|
|
4904 |
* The getContent() function returns the content of an element. If there is no such content, null is returned
|
|
|
4905 |
*
|
|
|
4906 |
* @return {String} the (possibly null) content
|
|
|
4907 |
*/
|
|
|
4908 |
getContent: function(){
|
|
|
4909 |
if (this.type === "TEXT") {
|
|
|
4910 |
return this.content;
|
|
|
4911 |
}
|
|
|
4912 |
var children = this.children;
|
|
|
4913 |
if (children.length === 1 && children[0].type === "TEXT") {
|
|
|
4914 |
return children[0].content;
|
|
|
4915 |
}
|
|
|
4916 |
return null;
|
|
|
4917 |
},
|
|
|
4918 |
/**
|
|
|
4919 |
* @member XMLElement
|
|
|
4920 |
* The getAttribute() function returns the value of an attribute
|
|
|
4921 |
*
|
|
|
4922 |
* @param {String} name the non-null full name of the attribute
|
|
|
4923 |
* @param {String} namespace the namespace URI, which may be null
|
|
|
4924 |
* @param {String} defaultValue the default value of the attribute
|
|
|
4925 |
*
|
|
|
4926 |
* @return {String} the value, or defaultValue if the attribute does not exist
|
|
|
4927 |
*/
|
|
|
4928 |
getAttribute: function (){
|
|
|
4929 |
var attribute;
|
|
|
4930 |
if( arguments.length === 2 ){
|
|
|
4931 |
attribute = this.findAttribute(arguments[0]);
|
|
|
4932 |
if (attribute) {
|
|
|
4933 |
return attribute.getValue();
|
|
|
4934 |
}
|
|
|
4935 |
return arguments[1];
|
|
|
4936 |
} else if (arguments.length === 1) {
|
|
|
4937 |
attribute = this.findAttribute(arguments[0]);
|
|
|
4938 |
if (attribute) {
|
|
|
4939 |
return attribute.getValue();
|
|
|
4940 |
}
|
|
|
4941 |
return null;
|
|
|
4942 |
} else if (arguments.length === 3) {
|
|
|
4943 |
attribute = this.findAttribute(arguments[0],arguments[1]);
|
|
|
4944 |
if (attribute) {
|
|
|
4945 |
return attribute.getValue();
|
|
|
4946 |
}
|
|
|
4947 |
return arguments[2];
|
|
|
4948 |
}
|
|
|
4949 |
},
|
|
|
4950 |
/**
|
|
|
4951 |
* @member XMLElement
|
|
|
4952 |
* The getStringAttribute() function returns the string attribute of the element
|
|
|
4953 |
* If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
|
|
|
4954 |
* When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
|
|
|
4955 |
*
|
|
|
4956 |
* @param name the name of the attribute
|
|
|
4957 |
* @param defaultValue value returned if the attribute is not found
|
|
|
4958 |
*
|
|
|
4959 |
* @return {String} the value, or defaultValue if the attribute does not exist
|
|
|
4960 |
*/
|
|
|
4961 |
getStringAttribute: function() {
|
|
|
4962 |
if (arguments.length === 1) {
|
|
|
4963 |
return this.getAttribute(arguments[0]);
|
|
|
4964 |
}
|
|
|
4965 |
if (arguments.length === 2){
|
|
|
4966 |
return this.getAttribute(arguments[0], arguments[1]);
|
|
|
4967 |
}
|
|
|
4968 |
return this.getAttribute(arguments[0], arguments[1],arguments[2]);
|
|
|
4969 |
},
|
|
|
4970 |
/**
|
|
|
4971 |
* Processing 1.5 XML API wrapper for the generic String
|
|
|
4972 |
* attribute getter. This may only take one argument.
|
|
|
4973 |
*/
|
|
|
4974 |
getString: function(attributeName) {
|
|
|
4975 |
return this.getStringAttribute(attributeName);
|
|
|
4976 |
},
|
|
|
4977 |
/**
|
|
|
4978 |
* @member XMLElement
|
|
|
4979 |
* The getFloatAttribute() function returns the float attribute of the element.
|
|
|
4980 |
* If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
|
|
|
4981 |
* When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
|
|
|
4982 |
*
|
|
|
4983 |
* @param name the name of the attribute
|
|
|
4984 |
* @param defaultValue value returned if the attribute is not found
|
|
|
4985 |
*
|
|
|
4986 |
* @return {float} the value, or defaultValue if the attribute does not exist
|
|
|
4987 |
*/
|
|
|
4988 |
getFloatAttribute: function() {
|
|
|
4989 |
if (arguments.length === 1 ) {
|
|
|
4990 |
return parseFloat(this.getAttribute(arguments[0], 0));
|
|
|
4991 |
}
|
|
|
4992 |
if (arguments.length === 2 ){
|
|
|
4993 |
return this.getAttribute(arguments[0], arguments[1]);
|
|
|
4994 |
}
|
|
|
4995 |
return this.getAttribute(arguments[0], arguments[1],arguments[2]);
|
|
|
4996 |
},
|
|
|
4997 |
/**
|
|
|
4998 |
* Processing 1.5 XML API wrapper for the generic float
|
|
|
4999 |
* attribute getter. This may only take one argument.
|
|
|
5000 |
*/
|
|
|
5001 |
getFloat: function(attributeName) {
|
|
|
5002 |
return this.getFloatAttribute(attributeName);
|
|
|
5003 |
},
|
|
|
5004 |
/**
|
|
|
5005 |
* @member XMLElement
|
|
|
5006 |
* The getIntAttribute() function returns the integer attribute of the element.
|
|
|
5007 |
* If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
|
|
|
5008 |
* When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
|
|
|
5009 |
*
|
|
|
5010 |
* @param name the name of the attribute
|
|
|
5011 |
* @param defaultValue value returned if the attribute is not found
|
|
|
5012 |
*
|
|
|
5013 |
* @return {int} the value, or defaultValue if the attribute does not exist
|
|
|
5014 |
*/
|
|
|
5015 |
getIntAttribute: function () {
|
|
|
5016 |
if (arguments.length === 1) {
|
|
|
5017 |
return this.getAttribute( arguments[0], 0 );
|
|
|
5018 |
}
|
|
|
5019 |
if (arguments.length === 2) {
|
|
|
5020 |
return this.getAttribute(arguments[0], arguments[1]);
|
|
|
5021 |
}
|
|
|
5022 |
return this.getAttribute(arguments[0], arguments[1],arguments[2]);
|
|
|
5023 |
},
|
|
|
5024 |
/**
|
|
|
5025 |
* Processing 1.5 XML API wrapper for the generic int
|
|
|
5026 |
* attribute getter. This may only take one argument.
|
|
|
5027 |
*/
|
|
|
5028 |
getInt: function(attributeName) {
|
|
|
5029 |
return this.getIntAttribute(attributeName);
|
|
|
5030 |
},
|
|
|
5031 |
/**
|
|
|
5032 |
* @member XMLElement
|
|
|
5033 |
* The hasChildren() function returns whether the element has children.
|
|
|
5034 |
*
|
|
|
5035 |
* @return {boolean} true if the element has children.
|
|
|
5036 |
*/
|
|
|
5037 |
hasChildren: function () {
|
|
|
5038 |
return this.children.length > 0 ;
|
|
|
5039 |
},
|
|
|
5040 |
/**
|
|
|
5041 |
* @member XMLElement
|
|
|
5042 |
* The addChild() function adds a child element
|
|
|
5043 |
*
|
|
|
5044 |
* @param {XMLElement} child the non-null child to add.
|
|
|
5045 |
*/
|
|
|
5046 |
addChild: function (child) {
|
|
|
5047 |
if (child !== null) {
|
|
|
5048 |
child.parent = this;
|
|
|
5049 |
this.children.push(child);
|
|
|
5050 |
}
|
|
|
5051 |
},
|
|
|
5052 |
/**
|
|
|
5053 |
* @member XMLElement
|
|
|
5054 |
* The insertChild() function inserts a child element at the index provided
|
|
|
5055 |
*
|
|
|
5056 |
* @param {XMLElement} child the non-null child to add.
|
|
|
5057 |
* @param {int} index where to put the child.
|
|
|
5058 |
*/
|
|
|
5059 |
insertChild: function (child, index) {
|
|
|
5060 |
if (child) {
|
|
|
5061 |
if ((child.getLocalName() === null) && (! this.hasChildren())) {
|
|
|
5062 |
var lastChild = this.children[this.children.length -1];
|
|
|
5063 |
if (lastChild.getLocalName() === null) {
|
|
|
5064 |
lastChild.setContent(lastChild.getContent() + child.getContent());
|
|
|
5065 |
return;
|
|
|
5066 |
}
|
|
|
5067 |
}
|
|
|
5068 |
child.parent = this;
|
|
|
5069 |
this.children.splice(index,0,child);
|
|
|
5070 |
}
|
|
|
5071 |
},
|
|
|
5072 |
/**
|
|
|
5073 |
* @member XMLElement
|
|
|
5074 |
* The getChild() returns the child XMLElement as specified by the <b>index</b> parameter.
|
|
|
5075 |
* 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.
|
|
|
5076 |
* 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.
|
|
|
5077 |
*
|
|
|
5078 |
* @param {int} index where to put the child.
|
|
|
5079 |
* @param {String} path path to a particular element
|
|
|
5080 |
*
|
|
|
5081 |
* @return {XMLElement} the element
|
|
|
5082 |
*/
|
|
|
5083 |
getChild: function (){
|
|
|
5084 |
if (typeof arguments[0] === "number") {
|
|
|
5085 |
return this.children[arguments[0]];
|
|
|
5086 |
}
|
|
|
5087 |
if (arguments[0].indexOf('/') !== -1) { // path was given
|
|
|
5088 |
this.getChildRecursive(arguments[0].split("/"), 0);
|
|
|
5089 |
return null;
|
|
|
5090 |
}
|
|
|
5091 |
var kid, kidName;
|
|
|
5092 |
for (var i = 0, j = this.getChildCount(); i < j; i++) {
|
|
|
5093 |
kid = this.getChild(i);
|
|
|
5094 |
kidName = kid.getName();
|
|
|
5095 |
if (kidName !== null && kidName === arguments[0]) {
|
|
|
5096 |
return kid;
|
|
|
5097 |
}
|
|
|
5098 |
}
|
|
|
5099 |
return null;
|
|
|
5100 |
},
|
|
|
5101 |
/**
|
|
|
5102 |
* @member XMLElement
|
|
|
5103 |
* The getChildren() returns all of the children as an XMLElement array.
|
|
|
5104 |
* When the <b>path</b> parameter is specified, then it will return all children that match that path.
|
|
|
5105 |
* The path is a series of elements and sub-elements, separated by slashes.
|
|
|
5106 |
*
|
|
|
5107 |
* @param {String} path element name or path/to/element
|
|
|
5108 |
*
|
|
|
5109 |
* @return {XMLElement} array of child elements that match
|
|
|
5110 |
*
|
|
|
5111 |
* @see XMLElement#getChildCount()
|
|
|
5112 |
* @see XMLElement#getChild()
|
|
|
5113 |
*/
|
|
|
5114 |
getChildren: function(){
|
|
|
5115 |
if (arguments.length === 1) {
|
|
|
5116 |
if (typeof arguments[0] === "number") {
|
|
|
5117 |
return this.getChild( arguments[0]);
|
|
|
5118 |
}
|
|
|
5119 |
if (arguments[0].indexOf('/') !== -1) { // path was given
|
|
|
5120 |
return this.getChildrenRecursive( arguments[0].split("/"), 0);
|
|
|
5121 |
}
|
|
|
5122 |
var matches = [];
|
|
|
5123 |
var kid, kidName;
|
|
|
5124 |
for (var i = 0, j = this.getChildCount(); i < j; i++) {
|
|
|
5125 |
kid = this.getChild(i);
|
|
|
5126 |
kidName = kid.getName();
|
|
|
5127 |
if (kidName !== null && kidName === arguments[0]) {
|
|
|
5128 |
matches.push(kid);
|
|
|
5129 |
}
|
|
|
5130 |
}
|
|
|
5131 |
return matches;
|
|
|
5132 |
}
|
|
|
5133 |
return this.children;
|
|
|
5134 |
},
|
|
|
5135 |
/**
|
|
|
5136 |
* @member XMLElement
|
|
|
5137 |
* The getChildCount() returns the number of children for the element.
|
|
|
5138 |
*
|
|
|
5139 |
* @return {int} the count
|
|
|
5140 |
*
|
|
|
5141 |
* @see XMLElement#getChild()
|
|
|
5142 |
* @see XMLElement#getChildren()
|
|
|
5143 |
*/
|
|
|
5144 |
getChildCount: function(){
|
|
|
5145 |
return this.children.length;
|
|
|
5146 |
},
|
|
|
5147 |
/**
|
|
|
5148 |
* @member XMLElement
|
|
|
5149 |
* Internal helper function for getChild().
|
|
|
5150 |
*
|
|
|
5151 |
* @param {String[]} items result of splitting the query on slashes
|
|
|
5152 |
* @param {int} offset where in the items[] array we're currently looking
|
|
|
5153 |
*
|
|
|
5154 |
* @return {XMLElement} matching element or null if no match
|
|
|
5155 |
*/
|
|
|
5156 |
getChildRecursive: function (items, offset) {
|
|
|
5157 |
var kid, kidName;
|
|
|
5158 |
for(var i = 0, j = this.getChildCount(); i < j; i++) {
|
|
|
5159 |
kid = this.getChild(i);
|
|
|
5160 |
kidName = kid.getName();
|
|
|
5161 |
if (kidName !== null && kidName === items[offset]) {
|
|
|
5162 |
if (offset === items.length-1) {
|
|
|
5163 |
return kid;
|
|
|
5164 |
}
|
|
|
5165 |
offset += 1;
|
|
|
5166 |
return kid.getChildRecursive(items, offset);
|
|
|
5167 |
}
|
|
|
5168 |
}
|
|
|
5169 |
return null;
|
|
|
5170 |
},
|
|
|
5171 |
/**
|
|
|
5172 |
* @member XMLElement
|
|
|
5173 |
* Internal helper function for getChildren().
|
|
|
5174 |
*
|
|
|
5175 |
* @param {String[]} items result of splitting the query on slashes
|
|
|
5176 |
* @param {int} offset where in the items[] array we're currently looking
|
|
|
5177 |
*
|
|
|
5178 |
* @return {XMLElement[]} matching elements or empty array if no match
|
|
|
5179 |
*/
|
|
|
5180 |
getChildrenRecursive: function (items, offset) {
|
|
|
5181 |
if (offset === items.length-1) {
|
|
|
5182 |
return this.getChildren(items[offset]);
|
|
|
5183 |
}
|
|
|
5184 |
var matches = this.getChildren(items[offset]);
|
|
|
5185 |
var kidMatches = [];
|
|
|
5186 |
for (var i = 0; i < matches.length; i++) {
|
|
|
5187 |
kidMatches = kidMatches.concat(matches[i].getChildrenRecursive(items, offset+1));
|
|
|
5188 |
}
|
|
|
5189 |
return kidMatches;
|
|
|
5190 |
},
|
|
|
5191 |
/**
|
|
|
5192 |
* @member XMLElement
|
|
|
5193 |
* The isLeaf() function returns whether the element is a leaf element.
|
|
|
5194 |
*
|
|
|
5195 |
* @return {boolean} true if the element has no children.
|
|
|
5196 |
*/
|
|
|
5197 |
isLeaf: function(){
|
|
|
5198 |
return !this.hasChildren();
|
|
|
5199 |
},
|
|
|
5200 |
/**
|
|
|
5201 |
* @member XMLElement
|
|
|
5202 |
* The listChildren() function put the names of all children into an array. Same as looping through
|
|
|
5203 |
* each child and calling getName() on each XMLElement.
|
|
|
5204 |
*
|
|
|
5205 |
* @return {String[]} a list of element names.
|
|
|
5206 |
*/
|
|
|
5207 |
listChildren: function() {
|
|
|
5208 |
var arr = [];
|
|
|
5209 |
for (var i = 0, j = this.children.length; i < j; i++) {
|
|
|
5210 |
arr.push( this.getChild(i).getName());
|
|
|
5211 |
}
|
|
|
5212 |
return arr;
|
|
|
5213 |
},
|
|
|
5214 |
/**
|
|
|
5215 |
* @member XMLElement
|
|
|
5216 |
* The removeAttribute() function removes an attribute
|
|
|
5217 |
*
|
|
|
5218 |
* @param {String} name the non-null name of the attribute.
|
|
|
5219 |
* @param {String} namespace the namespace URI of the attribute, which may be null.
|
|
|
5220 |
*/
|
|
|
5221 |
removeAttribute: function (name , namespace) {
|
|
|
5222 |
this.namespace = namespace || "";
|
|
|
5223 |
for (var i = 0, j = this.attributes.length; i < j; i++) {
|
|
|
5224 |
if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
|
|
|
5225 |
this.attributes.splice(i, 1);
|
|
|
5226 |
break;
|
|
|
5227 |
}
|
|
|
5228 |
}
|
|
|
5229 |
},
|
|
|
5230 |
/**
|
|
|
5231 |
* @member XMLElement
|
|
|
5232 |
* The removeChild() removes a child element.
|
|
|
5233 |
*
|
|
|
5234 |
* @param {XMLElement} child the the non-null child to be renoved
|
|
|
5235 |
*/
|
|
|
5236 |
removeChild: function(child) {
|
|
|
5237 |
if (child) {
|
|
|
5238 |
for (var i = 0, j = this.children.length; i < j; i++) {
|
|
|
5239 |
if (this.children[i].equals(child)) {
|
|
|
5240 |
this.children.splice(i, 1);
|
|
|
5241 |
break;
|
|
|
5242 |
}
|
|
|
5243 |
}
|
|
|
5244 |
}
|
|
|
5245 |
},
|
|
|
5246 |
/**
|
|
|
5247 |
* @member XMLElement
|
|
|
5248 |
* The removeChildAtIndex() removes the child located at a certain index
|
|
|
5249 |
*
|
|
|
5250 |
* @param {int} index the index of the child, where the first child has index 0
|
|
|
5251 |
*/
|
|
|
5252 |
removeChildAtIndex: function(index) {
|
|
|
5253 |
if (this.children.length > index) { //make sure its not outofbounds
|
|
|
5254 |
this.children.splice(index, 1);
|
|
|
5255 |
}
|
|
|
5256 |
},
|
|
|
5257 |
/**
|
|
|
5258 |
* @member XMLElement
|
|
|
5259 |
* The findAttribute() function searches an attribute
|
|
|
5260 |
*
|
|
|
5261 |
* @param {String} name fullName the non-null full name of the attribute
|
|
|
5262 |
* @param {String} namespace the name space, which may be null
|
|
|
5263 |
*
|
|
|
5264 |
* @return {XMLAttribute} the attribute, or null if the attribute does not exist.
|
|
|
5265 |
*/
|
|
|
5266 |
findAttribute: function (name, namespace) {
|
|
|
5267 |
this.namespace = namespace || "";
|
|
|
5268 |
for (var i = 0, j = this.attributes.length; i < j; i++) {
|
|
|
5269 |
if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
|
|
|
5270 |
return this.attributes[i];
|
|
|
5271 |
}
|
|
|
5272 |
}
|
|
|
5273 |
return null;
|
|
|
5274 |
},
|
|
|
5275 |
/**
|
|
|
5276 |
* @member XMLElement
|
|
|
5277 |
* The setAttribute() function sets an attribute.
|
|
|
5278 |
*
|
|
|
5279 |
* @param {String} name the non-null full name of the attribute
|
|
|
5280 |
* @param {String} namespace the non-null value of the attribute
|
|
|
5281 |
*/
|
|
|
5282 |
setAttribute: function() {
|
|
|
5283 |
var attr;
|
|
|
5284 |
if (arguments.length === 3) {
|
|
|
5285 |
var index = arguments[0].indexOf(':');
|
|
|
5286 |
var name = arguments[0].substring(index + 1);
|
|
|
5287 |
attr = this.findAttribute(name, arguments[1]);
|
|
|
5288 |
if (attr) {
|
|
|
5289 |
attr.setValue(arguments[2]);
|
|
|
5290 |
} else {
|
|
|
5291 |
attr = new XMLAttribute(arguments[0], name, arguments[1], arguments[2], "CDATA");
|
|
|
5292 |
this.attributes.push(attr);
|
|
|
5293 |
}
|
|
|
5294 |
} else {
|
|
|
5295 |
attr = this.findAttribute(arguments[0]);
|
|
|
5296 |
if (attr) {
|
|
|
5297 |
attr.setValue(arguments[1]);
|
|
|
5298 |
} else {
|
|
|
5299 |
attr = new XMLAttribute(arguments[0], arguments[0], null, arguments[1], "CDATA");
|
|
|
5300 |
this.attributes.push(attr);
|
|
|
5301 |
}
|
|
|
5302 |
}
|
|
|
5303 |
},
|
|
|
5304 |
/**
|
|
|
5305 |
* Processing 1.5 XML API wrapper for the generic String
|
|
|
5306 |
* attribute setter. This must take two arguments.
|
|
|
5307 |
*/
|
|
|
5308 |
setString: function(attribute, value) {
|
|
|
5309 |
this.setAttribute(attribute, value);
|
|
|
5310 |
},
|
|
|
5311 |
/**
|
|
|
5312 |
* Processing 1.5 XML API wrapper for the generic int
|
|
|
5313 |
* attribute setter. This must take two arguments.
|
|
|
5314 |
*/
|
|
|
5315 |
setInt: function(attribute, value) {
|
|
|
5316 |
this.setAttribute(attribute, value);
|
|
|
5317 |
},
|
|
|
5318 |
/**
|
|
|
5319 |
* Processing 1.5 XML API wrapper for the generic float
|
|
|
5320 |
* attribute setter. This must take two arguments.
|
|
|
5321 |
*/
|
|
|
5322 |
setFloat: function(attribute, value) {
|
|
|
5323 |
this.setAttribute(attribute, value);
|
|
|
5324 |
},
|
|
|
5325 |
/**
|
|
|
5326 |
* @member XMLElement
|
|
|
5327 |
* The setContent() function sets the #PCDATA content. It is an error to call this method with a
|
|
|
5328 |
* non-null value if there are child objects.
|
|
|
5329 |
*
|
|
|
5330 |
* @param {String} content the (possibly null) content
|
|
|
5331 |
*/
|
|
|
5332 |
setContent: function(content) {
|
|
|
5333 |
if (this.children.length>0) {
|
|
|
5334 |
Processing.debug("Tried to set content for XMLElement with children"); }
|
|
|
5335 |
this.content = content;
|
|
|
5336 |
},
|
|
|
5337 |
/**
|
|
|
5338 |
* @member XMLElement
|
|
|
5339 |
* The setName() function sets the full name. This method also sets the short name and clears the
|
|
|
5340 |
* namespace URI.
|
|
|
5341 |
*
|
|
|
5342 |
* @param {String} name the non-null name
|
|
|
5343 |
* @param {String} namespace the namespace URI, which may be null.
|
|
|
5344 |
*/
|
|
|
5345 |
setName: function() {
|
|
|
5346 |
if (arguments.length === 1) {
|
|
|
5347 |
this.name = arguments[0];
|
|
|
5348 |
this.fullName = arguments[0];
|
|
|
5349 |
this.namespace = null;
|
|
|
5350 |
} else {
|
|
|
5351 |
var index = arguments[0].indexOf(':');
|
|
|
5352 |
if ((arguments[1] === null) || (index < 0)) {
|
|
|
5353 |
this.name = arguments[0];
|
|
|
5354 |
} else {
|
|
|
5355 |
this.name = arguments[0].substring(index + 1);
|
|
|
5356 |
}
|
|
|
5357 |
this.fullName = arguments[0];
|
|
|
5358 |
this.namespace = arguments[1];
|
|
|
5359 |
}
|
|
|
5360 |
},
|
|
|
5361 |
/**
|
|
|
5362 |
* @member XMLElement
|
|
|
5363 |
* The getName() function returns the full name (i.e. the name including an eventual namespace
|
|
|
5364 |
* prefix) of the element.
|
|
|
5365 |
*
|
|
|
5366 |
* @return {String} the name, or null if the element only contains #PCDATA.
|
|
|
5367 |
*/
|
|
|
5368 |
getName: function() {
|
|
|
5369 |
return this.fullName;
|
|
|
5370 |
},
|
|
|
5371 |
/**
|
|
|
5372 |
* @member XMLElement
|
|
|
5373 |
* The getLocalName() function returns the local name (i.e. the name excluding an eventual namespace
|
|
|
5374 |
* prefix) of the element.
|
|
|
5375 |
*
|
|
|
5376 |
* @return {String} the name, or null if the element only contains #PCDATA.
|
|
|
5377 |
*/
|
|
|
5378 |
getLocalName: function() {
|
|
|
5379 |
return this.name;
|
|
|
5380 |
},
|
|
|
5381 |
/**
|
|
|
5382 |
* @member XMLElement
|
|
|
5383 |
* The getAttributeCount() function returns the number of attributes for the node
|
|
|
5384 |
* that this XMLElement represents.
|
|
|
5385 |
*
|
|
|
5386 |
* @return {int} the number of attributes in this XMLElement
|
|
|
5387 |
*/
|
|
|
5388 |
getAttributeCount: function() {
|
|
|
5389 |
return this.attributes.length;
|
|
|
5390 |
},
|
|
|
5391 |
/**
|
|
|
5392 |
* @member XMLElement
|
|
|
5393 |
* The toString() function returns the XML definition of an XMLElement.
|
|
|
5394 |
*
|
|
|
5395 |
* @return {String} the XML definition of this XMLElement
|
|
|
5396 |
*/
|
|
|
5397 |
toString: function() {
|
|
|
5398 |
// shortcut for text nodes
|
|
|
5399 |
if(this.type==="TEXT") { return this.content; }
|
|
|
5400 |
|
|
|
5401 |
// real XMLElements
|
|
|
5402 |
var tagstring = (this.namespace !== "" && this.namespace !== this.name ? this.namespace + ":" : "") + this.name;
|
|
|
5403 |
var xmlstring = "<" + tagstring;
|
|
|
5404 |
var a,c;
|
|
|
5405 |
|
|
|
5406 |
// serialize the attributes to XML string
|
|
|
5407 |
for (a = 0; a<this.attributes.length; a++) {
|
|
|
5408 |
var attr = this.attributes[a];
|
|
|
5409 |
xmlstring += " " + attr.getName() + "=" + '"' + attr.getValue() + '"';
|
|
|
5410 |
}
|
|
|
5411 |
|
|
|
5412 |
// serialize all children to XML string
|
|
|
5413 |
if (this.children.length === 0) {
|
|
|
5414 |
if (this.content==="") {
|
|
|
5415 |
xmlstring += "/>";
|
|
|
5416 |
} else {
|
|
|
5417 |
xmlstring += ">" + this.content + "</"+tagstring+">";
|
|
|
5418 |
}
|
|
|
5419 |
} else {
|
|
|
5420 |
xmlstring += ">";
|
|
|
5421 |
for (c = 0; c<this.children.length; c++) {
|
|
|
5422 |
xmlstring += this.children[c].toString();
|
|
|
5423 |
}
|
|
|
5424 |
xmlstring += "</" + tagstring + ">";
|
|
|
5425 |
}
|
|
|
5426 |
return xmlstring;
|
|
|
5427 |
}
|
|
|
5428 |
};
|
|
|
5429 |
|
|
|
5430 |
/**
|
|
|
5431 |
* static Processing 1.5 XML API wrapper for the
|
|
|
5432 |
* parse method. This may only take one argument.
|
|
|
5433 |
*/
|
|
|
5434 |
XMLElement.parse = function(xmlstring) {
|
|
|
5435 |
var element = new XMLElement();
|
|
|
5436 |
element.parse(xmlstring);
|
|
|
5437 |
return element;
|
|
|
5438 |
};
|
|
|
5439 |
|
|
|
5440 |
////////////////////////////////////////////////////////////////////////////
|
|
|
5441 |
// 2D Matrix
|
|
|
5442 |
////////////////////////////////////////////////////////////////////////////
|
|
|
5443 |
/**
|
|
|
5444 |
* Helper function for printMatrix(). Finds the largest scalar
|
|
|
5445 |
* in the matrix, then number of digits left of the decimal.
|
|
|
5446 |
* Call from PMatrix2D and PMatrix3D's print() function.
|
|
|
5447 |
*/
|
|
|
5448 |
var printMatrixHelper = function(elements) {
|
|
|
5449 |
var big = 0;
|
|
|
5450 |
for (var i = 0; i < elements.length; i++) {
|
|
|
5451 |
if (i !== 0) {
|
|
|
5452 |
big = Math.max(big, Math.abs(elements[i]));
|
|
|
5453 |
} else {
|
|
|
5454 |
big = Math.abs(elements[i]);
|
|
|
5455 |
}
|
|
|
5456 |
}
|
|
|
5457 |
|
|
|
5458 |
var digits = (big + "").indexOf(".");
|
|
|
5459 |
if (digits === 0) {
|
|
|
5460 |
digits = 1;
|
|
|
5461 |
} else if (digits === -1) {
|
|
|
5462 |
digits = (big + "").length;
|
|
|
5463 |
}
|
|
|
5464 |
|
|
|
5465 |
return digits;
|
|
|
5466 |
};
|
|
|
5467 |
/**
|
|
|
5468 |
* PMatrix2D is a 3x2 affine matrix implementation. The constructor accepts another PMatrix2D or a list of six float elements.
|
|
|
5469 |
* If no parameters are provided the matrix is set to the identity matrix.
|
|
|
5470 |
*
|
|
|
5471 |
* @param {PMatrix2D} matrix the initial matrix to set to
|
|
|
5472 |
* @param {float} m00 the first element of the matrix
|
|
|
5473 |
* @param {float} m01 the second element of the matrix
|
|
|
5474 |
* @param {float} m02 the third element of the matrix
|
|
|
5475 |
* @param {float} m10 the fourth element of the matrix
|
|
|
5476 |
* @param {float} m11 the fifth element of the matrix
|
|
|
5477 |
* @param {float} m12 the sixth element of the matrix
|
|
|
5478 |
*/
|
|
|
5479 |
var PMatrix2D = p.PMatrix2D = function() {
|
|
|
5480 |
if (arguments.length === 0) {
|
|
|
5481 |
this.reset();
|
|
|
5482 |
} else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
|
|
|
5483 |
this.set(arguments[0].array());
|
|
|
5484 |
} else if (arguments.length === 6) {
|
|
|
5485 |
this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
|
|
|
5486 |
}
|
|
|
5487 |
};
|
|
|
5488 |
/**
|
|
|
5489 |
* PMatrix2D methods
|
|
|
5490 |
*/
|
|
|
5491 |
PMatrix2D.prototype = {
|
|
|
5492 |
/**
|
|
|
5493 |
* @member PMatrix2D
|
|
|
5494 |
* The set() function sets the matrix elements. The function accepts either another PMatrix2D, an array of elements, or a list of six floats.
|
|
|
5495 |
*
|
|
|
5496 |
* @param {PMatrix2D} matrix the matrix to set this matrix to
|
|
|
5497 |
* @param {float[]} elements an array of elements to set this matrix to
|
|
|
5498 |
* @param {float} m00 the first element of the matrix
|
|
|
5499 |
* @param {float} m01 the third element of the matrix
|
|
|
5500 |
* @param {float} m10 the fourth element of the matrix
|
|
|
5501 |
* @param {float} m11 the fith element of the matrix
|
|
|
5502 |
* @param {float} m12 the sixth element of the matrix
|
|
|
5503 |
*/
|
|
|
5504 |
set: function() {
|
|
|
5505 |
if (arguments.length === 6) {
|
|
|
5506 |
var a = arguments;
|
|
|
5507 |
this.set([a[0], a[1], a[2],
|
|
|
5508 |
a[3], a[4], a[5]]);
|
|
|
5509 |
} else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
|
|
|
5510 |
this.elements = arguments[0].array();
|
|
|
5511 |
} else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
|
|
5512 |
this.elements = arguments[0].slice();
|
|
|
5513 |
}
|
|
|
5514 |
},
|
|
|
5515 |
/**
|
|
|
5516 |
* @member PMatrix2D
|
|
|
5517 |
* The get() function returns a copy of this PMatrix2D.
|
|
|
5518 |
*
|
|
|
5519 |
* @return {PMatrix2D} a copy of this PMatrix2D
|
|
|
5520 |
*/
|
|
|
5521 |
get: function() {
|
|
|
5522 |
var outgoing = new PMatrix2D();
|
|
|
5523 |
outgoing.set(this.elements);
|
|
|
5524 |
return outgoing;
|
|
|
5525 |
},
|
|
|
5526 |
/**
|
|
|
5527 |
* @member PMatrix2D
|
|
|
5528 |
* The reset() function sets this PMatrix2D to the identity matrix.
|
|
|
5529 |
*/
|
|
|
5530 |
reset: function() {
|
|
|
5531 |
this.set([1, 0, 0, 0, 1, 0]);
|
|
|
5532 |
},
|
|
|
5533 |
/**
|
|
|
5534 |
* @member PMatrix2D
|
|
|
5535 |
* The array() function returns a copy of the element values.
|
|
|
5536 |
* @addon
|
|
|
5537 |
*
|
|
|
5538 |
* @return {float[]} returns a copy of the element values
|
|
|
5539 |
*/
|
|
|
5540 |
array: function array() {
|
|
|
5541 |
return this.elements.slice();
|
|
|
5542 |
},
|
|
|
5543 |
/**
|
|
|
5544 |
* @member PMatrix2D
|
|
|
5545 |
* The translate() function translates this matrix by moving the current coordinates to the location specified by tx and ty.
|
|
|
5546 |
*
|
|
|
5547 |
* @param {float} tx the x-axis coordinate to move to
|
|
|
5548 |
* @param {float} ty the y-axis coordinate to move to
|
|
|
5549 |
*/
|
|
|
5550 |
translate: function(tx, ty) {
|
|
|
5551 |
this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2];
|
|
|
5552 |
this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5];
|
|
|
5553 |
},
|
|
|
5554 |
/**
|
|
|
5555 |
* @member PMatrix2D
|
|
|
5556 |
* The invTranslate() function translates this matrix by moving the current coordinates to the negative location specified by tx and ty.
|
|
|
5557 |
*
|
|
|
5558 |
* @param {float} tx the x-axis coordinate to move to
|
|
|
5559 |
* @param {float} ty the y-axis coordinate to move to
|
|
|
5560 |
*/
|
|
|
5561 |
invTranslate: function(tx, ty) {
|
|
|
5562 |
this.translate(-tx, -ty);
|
|
|
5563 |
},
|
|
|
5564 |
/**
|
|
|
5565 |
* @member PMatrix2D
|
|
|
5566 |
* The transpose() function is not used in processingjs.
|
|
|
5567 |
*/
|
|
|
5568 |
transpose: function() {
|
|
|
5569 |
// Does nothing in Processing.
|
|
|
5570 |
},
|
|
|
5571 |
/**
|
|
|
5572 |
* @member PMatrix2D
|
|
|
5573 |
* The mult() function multiplied this matrix.
|
|
|
5574 |
* If two array elements are passed in the function will multiply a two element vector against this matrix.
|
|
|
5575 |
* If target is null or not length four, a new float array will be returned.
|
|
|
5576 |
* The values for vec and target can be the same (though that's less efficient).
|
|
|
5577 |
* If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
|
|
|
5578 |
*
|
|
|
5579 |
* @param {PVector} source, target the PVectors used to multiply this matrix
|
|
|
5580 |
* @param {float[]} source, target the arrays used to multiply this matrix
|
|
|
5581 |
*
|
|
|
5582 |
* @return {PVector|float[]} returns a PVector or an array representing the new matrix
|
|
|
5583 |
*/
|
|
|
5584 |
mult: function(source, target) {
|
|
|
5585 |
var x, y;
|
|
|
5586 |
if (source instanceof PVector) {
|
|
|
5587 |
x = source.x;
|
|
|
5588 |
y = source.y;
|
|
|
5589 |
if (!target) {
|
|
|
5590 |
target = new PVector();
|
|
|
5591 |
}
|
|
|
5592 |
} else if (source instanceof Array) {
|
|
|
5593 |
x = source[0];
|
|
|
5594 |
y = source[1];
|
|
|
5595 |
if (!target) {
|
|
|
5596 |
target = [];
|
|
|
5597 |
}
|
|
|
5598 |
}
|
|
|
5599 |
if (target instanceof Array) {
|
|
|
5600 |
target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2];
|
|
|
5601 |
target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5];
|
|
|
5602 |
} else if (target instanceof PVector) {
|
|
|
5603 |
target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2];
|
|
|
5604 |
target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5];
|
|
|
5605 |
target.z = 0;
|
|
|
5606 |
}
|
|
|
5607 |
return target;
|
|
|
5608 |
},
|
|
|
5609 |
/**
|
|
|
5610 |
* @member PMatrix2D
|
|
|
5611 |
* The multX() function calculates the x component of a vector from a transformation.
|
|
|
5612 |
*
|
|
|
5613 |
* @param {float} x the x component of the vector being transformed
|
|
|
5614 |
* @param {float} y the y component of the vector being transformed
|
|
|
5615 |
*
|
|
|
5616 |
* @return {float} returnes the result of the calculation
|
|
|
5617 |
*/
|
|
|
5618 |
multX: function(x, y) {
|
|
|
5619 |
return (x * this.elements[0] + y * this.elements[1] + this.elements[2]);
|
|
|
5620 |
},
|
|
|
5621 |
/**
|
|
|
5622 |
* @member PMatrix2D
|
|
|
5623 |
* The multY() function calculates the y component of a vector from a transformation.
|
|
|
5624 |
*
|
|
|
5625 |
* @param {float} x the x component of the vector being transformed
|
|
|
5626 |
* @param {float} y the y component of the vector being transformed
|
|
|
5627 |
*
|
|
|
5628 |
* @return {float} returnes the result of the calculation
|
|
|
5629 |
*/
|
|
|
5630 |
multY: function(x, y) {
|
|
|
5631 |
return (x * this.elements[3] + y * this.elements[4] + this.elements[5]);
|
|
|
5632 |
},
|
|
|
5633 |
/**
|
|
|
5634 |
* @member PMatrix2D
|
|
|
5635 |
* The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
|
|
|
5636 |
* Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
|
|
5637 |
*
|
|
|
5638 |
* @param {float} angle angle of skew specified in radians
|
|
|
5639 |
*/
|
|
|
5640 |
skewX: function(angle) {
|
|
|
5641 |
this.apply(1, 0, 1, angle, 0, 0);
|
|
|
5642 |
},
|
|
|
5643 |
/**
|
|
|
5644 |
* @member PMatrix2D
|
|
|
5645 |
* The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
|
|
|
5646 |
* Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
|
|
5647 |
*
|
|
|
5648 |
* @param {float} angle angle of skew specified in radians
|
|
|
5649 |
*/
|
|
|
5650 |
skewY: function(angle) {
|
|
|
5651 |
this.apply(1, 0, 1, 0, angle, 0);
|
|
|
5652 |
},
|
|
|
5653 |
/**
|
|
|
5654 |
* @member PMatrix2D
|
|
|
5655 |
* The determinant() function calvculates the determinant of this matrix.
|
|
|
5656 |
*
|
|
|
5657 |
* @return {float} the determinant of the matrix
|
|
|
5658 |
*/
|
|
|
5659 |
determinant: function() {
|
|
|
5660 |
return (this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3]);
|
|
|
5661 |
},
|
|
|
5662 |
/**
|
|
|
5663 |
* @member PMatrix2D
|
|
|
5664 |
* The invert() function inverts this matrix
|
|
|
5665 |
*
|
|
|
5666 |
* @return {boolean} true if successful
|
|
|
5667 |
*/
|
|
|
5668 |
invert: function() {
|
|
|
5669 |
var d = this.determinant();
|
|
|
5670 |
if (Math.abs( d ) > PConstants.MIN_INT) {
|
|
|
5671 |
var old00 = this.elements[0];
|
|
|
5672 |
var old01 = this.elements[1];
|
|
|
5673 |
var old02 = this.elements[2];
|
|
|
5674 |
var old10 = this.elements[3];
|
|
|
5675 |
var old11 = this.elements[4];
|
|
|
5676 |
var old12 = this.elements[5];
|
|
|
5677 |
this.elements[0] = old11 / d;
|
|
|
5678 |
this.elements[3] = -old10 / d;
|
|
|
5679 |
this.elements[1] = -old01 / d;
|
|
|
5680 |
this.elements[4] = old00 / d;
|
|
|
5681 |
this.elements[2] = (old01 * old12 - old11 * old02) / d;
|
|
|
5682 |
this.elements[5] = (old10 * old02 - old00 * old12) / d;
|
|
|
5683 |
return true;
|
|
|
5684 |
}
|
|
|
5685 |
return false;
|
|
|
5686 |
},
|
|
|
5687 |
/**
|
|
|
5688 |
* @member PMatrix2D
|
|
|
5689 |
* 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.
|
|
|
5690 |
* This is equivalent to a two parameter call.
|
|
|
5691 |
*
|
|
|
5692 |
* @param {float} sx the amount to scale on the x-axis
|
|
|
5693 |
* @param {float} sy the amount to scale on the y-axis
|
|
|
5694 |
*/
|
|
|
5695 |
scale: function(sx, sy) {
|
|
|
5696 |
if (sx && !sy) {
|
|
|
5697 |
sy = sx;
|
|
|
5698 |
}
|
|
|
5699 |
if (sx && sy) {
|
|
|
5700 |
this.elements[0] *= sx;
|
|
|
5701 |
this.elements[1] *= sy;
|
|
|
5702 |
this.elements[3] *= sx;
|
|
|
5703 |
this.elements[4] *= sy;
|
|
|
5704 |
}
|
|
|
5705 |
},
|
|
|
5706 |
/**
|
|
|
5707 |
* @member PMatrix2D
|
|
|
5708 |
* 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.
|
|
|
5709 |
* This is equivalent to a two parameter call.
|
|
|
5710 |
*
|
|
|
5711 |
* @param {float} sx the amount to scale on the x-axis
|
|
|
5712 |
* @param {float} sy the amount to scale on the y-axis
|
|
|
5713 |
*/
|
|
|
5714 |
invScale: function(sx, sy) {
|
|
|
5715 |
if (sx && !sy) {
|
|
|
5716 |
sy = sx;
|
|
|
5717 |
}
|
|
|
5718 |
this.scale(1 / sx, 1 / sy);
|
|
|
5719 |
},
|
|
|
5720 |
/**
|
|
|
5721 |
* @member PMatrix2D
|
|
|
5722 |
* 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.
|
|
|
5723 |
*
|
|
|
5724 |
* @param {PMatrix2D} matrix the matrix to apply this matrix to
|
|
|
5725 |
* @param {float} m00 the first element of the matrix
|
|
|
5726 |
* @param {float} m01 the third element of the matrix
|
|
|
5727 |
* @param {float} m10 the fourth element of the matrix
|
|
|
5728 |
* @param {float} m11 the fith element of the matrix
|
|
|
5729 |
* @param {float} m12 the sixth element of the matrix
|
|
|
5730 |
*/
|
|
|
5731 |
apply: function() {
|
|
|
5732 |
var source;
|
|
|
5733 |
if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
|
|
|
5734 |
source = arguments[0].array();
|
|
|
5735 |
} else if (arguments.length === 6) {
|
|
|
5736 |
source = Array.prototype.slice.call(arguments);
|
|
|
5737 |
} else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
|
|
5738 |
source = arguments[0];
|
|
|
5739 |
}
|
|
|
5740 |
|
|
|
5741 |
var result = [0, 0, this.elements[2],
|
|
|
5742 |
0, 0, this.elements[5]];
|
|
|
5743 |
var e = 0;
|
|
|
5744 |
for (var row = 0; row < 2; row++) {
|
|
|
5745 |
for (var col = 0; col < 3; col++, e++) {
|
|
|
5746 |
result[e] += this.elements[row * 3 + 0] * source[col + 0] +
|
|
|
5747 |
this.elements[row * 3 + 1] * source[col + 3];
|
|
|
5748 |
}
|
|
|
5749 |
}
|
|
|
5750 |
this.elements = result.slice();
|
|
|
5751 |
},
|
|
|
5752 |
/**
|
|
|
5753 |
* @member PMatrix2D
|
|
|
5754 |
* 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.
|
|
|
5755 |
*
|
|
|
5756 |
* @param {PMatrix2D} matrix the matrix to apply this matrix to
|
|
|
5757 |
* @param {float} m00 the first element of the matrix
|
|
|
5758 |
* @param {float} m01 the third element of the matrix
|
|
|
5759 |
* @param {float} m10 the fourth element of the matrix
|
|
|
5760 |
* @param {float} m11 the fith element of the matrix
|
|
|
5761 |
* @param {float} m12 the sixth element of the matrix
|
|
|
5762 |
*/
|
|
|
5763 |
preApply: function() {
|
|
|
5764 |
var source;
|
|
|
5765 |
if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
|
|
|
5766 |
source = arguments[0].array();
|
|
|
5767 |
} else if (arguments.length === 6) {
|
|
|
5768 |
source = Array.prototype.slice.call(arguments);
|
|
|
5769 |
} else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
|
|
5770 |
source = arguments[0];
|
|
|
5771 |
}
|
|
|
5772 |
var result = [0, 0, source[2],
|
|
|
5773 |
0, 0, source[5]];
|
|
|
5774 |
result[2] = source[2] + this.elements[2] * source[0] + this.elements[5] * source[1];
|
|
|
5775 |
result[5] = source[5] + this.elements[2] * source[3] + this.elements[5] * source[4];
|
|
|
5776 |
result[0] = this.elements[0] * source[0] + this.elements[3] * source[1];
|
|
|
5777 |
result[3] = this.elements[0] * source[3] + this.elements[3] * source[4];
|
|
|
5778 |
result[1] = this.elements[1] * source[0] + this.elements[4] * source[1];
|
|
|
5779 |
result[4] = this.elements[1] * source[3] + this.elements[4] * source[4];
|
|
|
5780 |
this.elements = result.slice();
|
|
|
5781 |
},
|
|
|
5782 |
/**
|
|
|
5783 |
* @member PMatrix2D
|
|
|
5784 |
* The rotate() function rotates the matrix.
|
|
|
5785 |
*
|
|
|
5786 |
* @param {float} angle the angle of rotation in radiants
|
|
|
5787 |
*/
|
|
|
5788 |
rotate: function(angle) {
|
|
|
5789 |
var c = Math.cos(angle);
|
|
|
5790 |
var s = Math.sin(angle);
|
|
|
5791 |
var temp1 = this.elements[0];
|
|
|
5792 |
var temp2 = this.elements[1];
|
|
|
5793 |
this.elements[0] = c * temp1 + s * temp2;
|
|
|
5794 |
this.elements[1] = -s * temp1 + c * temp2;
|
|
|
5795 |
temp1 = this.elements[3];
|
|
|
5796 |
temp2 = this.elements[4];
|
|
|
5797 |
this.elements[3] = c * temp1 + s * temp2;
|
|
|
5798 |
this.elements[4] = -s * temp1 + c * temp2;
|
|
|
5799 |
},
|
|
|
5800 |
/**
|
|
|
5801 |
* @member PMatrix2D
|
|
|
5802 |
* The rotateZ() function rotates the matrix.
|
|
|
5803 |
*
|
|
|
5804 |
* @param {float} angle the angle of rotation in radiants
|
|
|
5805 |
*/
|
|
|
5806 |
rotateZ: function(angle) {
|
|
|
5807 |
this.rotate(angle);
|
|
|
5808 |
},
|
|
|
5809 |
/**
|
|
|
5810 |
* @member PMatrix2D
|
|
|
5811 |
* The invRotateZ() function rotates the matrix in opposite direction.
|
|
|
5812 |
*
|
|
|
5813 |
* @param {float} angle the angle of rotation in radiants
|
|
|
5814 |
*/
|
|
|
5815 |
invRotateZ: function(angle) {
|
|
|
5816 |
this.rotateZ(angle - Math.PI);
|
|
|
5817 |
},
|
|
|
5818 |
/**
|
|
|
5819 |
* @member PMatrix2D
|
|
|
5820 |
* The print() function prints out the elements of this matrix
|
|
|
5821 |
*/
|
|
|
5822 |
print: function() {
|
|
|
5823 |
var digits = printMatrixHelper(this.elements);
|
|
|
5824 |
var output = "" + p.nfs(this.elements[0], digits, 4) + " " +
|
|
|
5825 |
p.nfs(this.elements[1], digits, 4) + " " +
|
|
|
5826 |
p.nfs(this.elements[2], digits, 4) + "\n" +
|
|
|
5827 |
p.nfs(this.elements[3], digits, 4) + " " +
|
|
|
5828 |
p.nfs(this.elements[4], digits, 4) + " " +
|
|
|
5829 |
p.nfs(this.elements[5], digits, 4) + "\n\n";
|
|
|
5830 |
p.println(output);
|
|
|
5831 |
}
|
|
|
5832 |
};
|
|
|
5833 |
|
|
|
5834 |
/**
|
|
|
5835 |
* PMatrix3D is a 4x4 matrix implementation. The constructor accepts another PMatrix3D or a list of six or sixteen float elements.
|
|
|
5836 |
* If no parameters are provided the matrix is set to the identity matrix.
|
|
|
5837 |
*/
|
|
|
5838 |
var PMatrix3D = p.PMatrix3D = function() {
|
|
|
5839 |
// When a matrix is created, it is set to an identity matrix
|
|
|
5840 |
this.reset();
|
|
|
5841 |
};
|
|
|
5842 |
/**
|
|
|
5843 |
* PMatrix3D methods
|
|
|
5844 |
*/
|
|
|
5845 |
PMatrix3D.prototype = {
|
|
|
5846 |
/**
|
|
|
5847 |
* @member PMatrix2D
|
|
|
5848 |
* 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.
|
|
|
5849 |
*
|
|
|
5850 |
* @param {PMatrix3D} matrix the initial matrix to set to
|
|
|
5851 |
* @param {float[]} elements an array of elements to set this matrix to
|
|
|
5852 |
* @param {float} m00 the first element of the matrix
|
|
|
5853 |
* @param {float} m01 the second element of the matrix
|
|
|
5854 |
* @param {float} m02 the third element of the matrix
|
|
|
5855 |
* @param {float} m03 the fourth element of the matrix
|
|
|
5856 |
* @param {float} m10 the fifth element of the matrix
|
|
|
5857 |
* @param {float} m11 the sixth element of the matrix
|
|
|
5858 |
* @param {float} m12 the seventh element of the matrix
|
|
|
5859 |
* @param {float} m13 the eight element of the matrix
|
|
|
5860 |
* @param {float} m20 the nineth element of the matrix
|
|
|
5861 |
* @param {float} m21 the tenth element of the matrix
|
|
|
5862 |
* @param {float} m22 the eleventh element of the matrix
|
|
|
5863 |
* @param {float} m23 the twelveth element of the matrix
|
|
|
5864 |
* @param {float} m30 the thirteenth element of the matrix
|
|
|
5865 |
* @param {float} m31 the fourtheenth element of the matrix
|
|
|
5866 |
* @param {float} m32 the fivetheenth element of the matrix
|
|
|
5867 |
* @param {float} m33 the sixteenth element of the matrix
|
|
|
5868 |
*/
|
|
|
5869 |
set: function() {
|
|
|
5870 |
if (arguments.length === 16) {
|
|
|
5871 |
this.elements = Array.prototype.slice.call(arguments);
|
|
|
5872 |
} else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
|
|
|
5873 |
this.elements = arguments[0].array();
|
|
|
5874 |
} else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
|
|
5875 |
this.elements = arguments[0].slice();
|
|
|
5876 |
}
|
|
|
5877 |
},
|
|
|
5878 |
/**
|
|
|
5879 |
* @member PMatrix3D
|
|
|
5880 |
* The get() function returns a copy of this PMatrix3D.
|
|
|
5881 |
*
|
|
|
5882 |
* @return {PMatrix3D} a copy of this PMatrix3D
|
|
|
5883 |
*/
|
|
|
5884 |
get: function() {
|
|
|
5885 |
var outgoing = new PMatrix3D();
|
|
|
5886 |
outgoing.set(this.elements);
|
|
|
5887 |
return outgoing;
|
|
|
5888 |
},
|
|
|
5889 |
/**
|
|
|
5890 |
* @member PMatrix3D
|
|
|
5891 |
* The reset() function sets this PMatrix3D to the identity matrix.
|
|
|
5892 |
*/
|
|
|
5893 |
reset: function() {
|
|
|
5894 |
this.elements = [1,0,0,0,
|
|
|
5895 |
0,1,0,0,
|
|
|
5896 |
0,0,1,0,
|
|
|
5897 |
0,0,0,1];
|
|
|
5898 |
},
|
|
|
5899 |
/**
|
|
|
5900 |
* @member PMatrix3D
|
|
|
5901 |
* The array() function returns a copy of the element values.
|
|
|
5902 |
* @addon
|
|
|
5903 |
*
|
|
|
5904 |
* @return {float[]} returns a copy of the element values
|
|
|
5905 |
*/
|
|
|
5906 |
array: function array() {
|
|
|
5907 |
return this.elements.slice();
|
|
|
5908 |
},
|
|
|
5909 |
/**
|
|
|
5910 |
* @member PMatrix3D
|
|
|
5911 |
* The translate() function translates this matrix by moving the current coordinates to the location specified by tx, ty, and tz.
|
|
|
5912 |
*
|
|
|
5913 |
* @param {float} tx the x-axis coordinate to move to
|
|
|
5914 |
* @param {float} ty the y-axis coordinate to move to
|
|
|
5915 |
* @param {float} tz the z-axis coordinate to move to
|
|
|
5916 |
*/
|
|
|
5917 |
translate: function(tx, ty, tz) {
|
|
|
5918 |
if (tz === undef) {
|
|
|
5919 |
tz = 0;
|
|
|
5920 |
}
|
|
|
5921 |
|
|
|
5922 |
this.elements[3] += tx * this.elements[0] + ty * this.elements[1] + tz * this.elements[2];
|
|
|
5923 |
this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6];
|
|
|
5924 |
this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10];
|
|
|
5925 |
this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14];
|
|
|
5926 |
},
|
|
|
5927 |
/**
|
|
|
5928 |
* @member PMatrix3D
|
|
|
5929 |
* The transpose() function transpose this matrix.
|
|
|
5930 |
*/
|
|
|
5931 |
transpose: function() {
|
|
|
5932 |
var temp = this.elements[4];
|
|
|
5933 |
this.elements[4] = this.elements[1];
|
|
|
5934 |
this.elements[1] = temp;
|
|
|
5935 |
|
|
|
5936 |
temp = this.elements[8];
|
|
|
5937 |
this.elements[8] = this.elements[2];
|
|
|
5938 |
this.elements[2] = temp;
|
|
|
5939 |
|
|
|
5940 |
temp = this.elements[6];
|
|
|
5941 |
this.elements[6] = this.elements[9];
|
|
|
5942 |
this.elements[9] = temp;
|
|
|
5943 |
|
|
|
5944 |
temp = this.elements[3];
|
|
|
5945 |
this.elements[3] = this.elements[12];
|
|
|
5946 |
this.elements[12] = temp;
|
|
|
5947 |
|
|
|
5948 |
temp = this.elements[7];
|
|
|
5949 |
this.elements[7] = this.elements[13];
|
|
|
5950 |
this.elements[13] = temp;
|
|
|
5951 |
|
|
|
5952 |
temp = this.elements[11];
|
|
|
5953 |
this.elements[11] = this.elements[14];
|
|
|
5954 |
this.elements[14] = temp;
|
|
|
5955 |
},
|
|
|
5956 |
/**
|
|
|
5957 |
* @member PMatrix3D
|
|
|
5958 |
* The mult() function multiplied this matrix.
|
|
|
5959 |
* If two array elements are passed in the function will multiply a two element vector against this matrix.
|
|
|
5960 |
* If target is null or not length four, a new float array will be returned.
|
|
|
5961 |
* The values for vec and target can be the same (though that's less efficient).
|
|
|
5962 |
* If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
|
|
|
5963 |
*
|
|
|
5964 |
* @param {PVector} source, target the PVectors used to multiply this matrix
|
|
|
5965 |
* @param {float[]} source, target the arrays used to multiply this matrix
|
|
|
5966 |
*
|
|
|
5967 |
* @return {PVector|float[]} returns a PVector or an array representing the new matrix
|
|
|
5968 |
*/
|
|
|
5969 |
mult: function(source, target) {
|
|
|
5970 |
var x, y, z, w;
|
|
|
5971 |
if (source instanceof PVector) {
|
|
|
5972 |
x = source.x;
|
|
|
5973 |
y = source.y;
|
|
|
5974 |
z = source.z;
|
|
|
5975 |
w = 1;
|
|
|
5976 |
if (!target) {
|
|
|
5977 |
target = new PVector();
|
|
|
5978 |
}
|
|
|
5979 |
} else if (source instanceof Array) {
|
|
|
5980 |
x = source[0];
|
|
|
5981 |
y = source[1];
|
|
|
5982 |
z = source[2];
|
|
|
5983 |
w = source[3] || 1;
|
|
|
5984 |
|
|
|
5985 |
if ( !target || (target.length !== 3 && target.length !== 4) ) {
|
|
|
5986 |
target = [0, 0, 0];
|
|
|
5987 |
}
|
|
|
5988 |
}
|
|
|
5989 |
|
|
|
5990 |
if (target instanceof Array) {
|
|
|
5991 |
if (target.length === 3) {
|
|
|
5992 |
target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
|
|
|
5993 |
target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
|
|
|
5994 |
target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
|
|
|
5995 |
} else if (target.length === 4) {
|
|
|
5996 |
target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
|
|
|
5997 |
target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
|
|
|
5998 |
target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
|
|
|
5999 |
target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
|
|
|
6000 |
}
|
|
|
6001 |
}
|
|
|
6002 |
if (target instanceof PVector) {
|
|
|
6003 |
target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
|
|
|
6004 |
target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
|
|
|
6005 |
target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
|
|
|
6006 |
}
|
|
|
6007 |
return target;
|
|
|
6008 |
},
|
|
|
6009 |
/**
|
|
|
6010 |
* @member PMatrix3D
|
|
|
6011 |
* 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.
|
|
|
6012 |
*
|
|
|
6013 |
* @param {PMatrix3D} matrix the matrix to apply this matrix to
|
|
|
6014 |
* @param {float} m00 the first element of the matrix
|
|
|
6015 |
* @param {float} m01 the second element of the matrix
|
|
|
6016 |
* @param {float} m02 the third element of the matrix
|
|
|
6017 |
* @param {float} m03 the fourth element of the matrix
|
|
|
6018 |
* @param {float} m10 the fifth element of the matrix
|
|
|
6019 |
* @param {float} m11 the sixth element of the matrix
|
|
|
6020 |
* @param {float} m12 the seventh element of the matrix
|
|
|
6021 |
* @param {float} m13 the eight element of the matrix
|
|
|
6022 |
* @param {float} m20 the nineth element of the matrix
|
|
|
6023 |
* @param {float} m21 the tenth element of the matrix
|
|
|
6024 |
* @param {float} m22 the eleventh element of the matrix
|
|
|
6025 |
* @param {float} m23 the twelveth element of the matrix
|
|
|
6026 |
* @param {float} m30 the thirteenth element of the matrix
|
|
|
6027 |
* @param {float} m31 the fourtheenth element of the matrix
|
|
|
6028 |
* @param {float} m32 the fivetheenth element of the matrix
|
|
|
6029 |
* @param {float} m33 the sixteenth element of the matrix
|
|
|
6030 |
*/
|
|
|
6031 |
preApply: function() {
|
|
|
6032 |
var source;
|
|
|
6033 |
if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
|
|
|
6034 |
source = arguments[0].array();
|
|
|
6035 |
} else if (arguments.length === 16) {
|
|
|
6036 |
source = Array.prototype.slice.call(arguments);
|
|
|
6037 |
} else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
|
|
6038 |
source = arguments[0];
|
|
|
6039 |
}
|
|
|
6040 |
|
|
|
6041 |
var result = [0, 0, 0, 0,
|
|
|
6042 |
0, 0, 0, 0,
|
|
|
6043 |
0, 0, 0, 0,
|
|
|
6044 |
0, 0, 0, 0];
|
|
|
6045 |
var e = 0;
|
|
|
6046 |
for (var row = 0; row < 4; row++) {
|
|
|
6047 |
for (var col = 0; col < 4; col++, e++) {
|
|
|
6048 |
result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] *
|
|
|
6049 |
source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] +
|
|
|
6050 |
this.elements[col + 12] * source[row * 4 + 3];
|
|
|
6051 |
}
|
|
|
6052 |
}
|
|
|
6053 |
this.elements = result.slice();
|
|
|
6054 |
},
|
|
|
6055 |
/**
|
|
|
6056 |
* @member PMatrix3D
|
|
|
6057 |
* 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.
|
|
|
6058 |
*
|
|
|
6059 |
* @param {PMatrix3D} matrix the matrix to apply this matrix to
|
|
|
6060 |
* @param {float} m00 the first element of the matrix
|
|
|
6061 |
* @param {float} m01 the second element of the matrix
|
|
|
6062 |
* @param {float} m02 the third element of the matrix
|
|
|
6063 |
* @param {float} m03 the fourth element of the matrix
|
|
|
6064 |
* @param {float} m10 the fifth element of the matrix
|
|
|
6065 |
* @param {float} m11 the sixth element of the matrix
|
|
|
6066 |
* @param {float} m12 the seventh element of the matrix
|
|
|
6067 |
* @param {float} m13 the eight element of the matrix
|
|
|
6068 |
* @param {float} m20 the nineth element of the matrix
|
|
|
6069 |
* @param {float} m21 the tenth element of the matrix
|
|
|
6070 |
* @param {float} m22 the eleventh element of the matrix
|
|
|
6071 |
* @param {float} m23 the twelveth element of the matrix
|
|
|
6072 |
* @param {float} m30 the thirteenth element of the matrix
|
|
|
6073 |
* @param {float} m31 the fourtheenth element of the matrix
|
|
|
6074 |
* @param {float} m32 the fivetheenth element of the matrix
|
|
|
6075 |
* @param {float} m33 the sixteenth element of the matrix
|
|
|
6076 |
*/
|
|
|
6077 |
apply: function() {
|
|
|
6078 |
var source;
|
|
|
6079 |
if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
|
|
|
6080 |
source = arguments[0].array();
|
|
|
6081 |
} else if (arguments.length === 16) {
|
|
|
6082 |
source = Array.prototype.slice.call(arguments);
|
|
|
6083 |
} else if (arguments.length === 1 && arguments[0] instanceof Array) {
|
|
|
6084 |
source = arguments[0];
|
|
|
6085 |
}
|
|
|
6086 |
|
|
|
6087 |
var result = [0, 0, 0, 0,
|
|
|
6088 |
0, 0, 0, 0,
|
|
|
6089 |
0, 0, 0, 0,
|
|
|
6090 |
0, 0, 0, 0];
|
|
|
6091 |
var e = 0;
|
|
|
6092 |
for (var row = 0; row < 4; row++) {
|
|
|
6093 |
for (var col = 0; col < 4; col++, e++) {
|
|
|
6094 |
result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] *
|
|
|
6095 |
source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] +
|
|
|
6096 |
this.elements[row * 4 + 3] * source[col + 12];
|
|
|
6097 |
}
|
|
|
6098 |
}
|
|
|
6099 |
this.elements = result.slice();
|
|
|
6100 |
},
|
|
|
6101 |
/**
|
|
|
6102 |
* @member PMatrix3D
|
|
|
6103 |
* The rotate() function rotates the matrix.
|
|
|
6104 |
*
|
|
|
6105 |
* @param {float} angle the angle of rotation in radiants
|
|
|
6106 |
*/
|
|
|
6107 |
rotate: function(angle, v0, v1, v2) {
|
|
|
6108 |
if (!v1) {
|
|
|
6109 |
this.rotateZ(angle);
|
|
|
6110 |
} else {
|
|
|
6111 |
// TODO should make sure this vector is normalized
|
|
|
6112 |
var c = p.cos(angle);
|
|
|
6113 |
var s = p.sin(angle);
|
|
|
6114 |
var t = 1.0 - c;
|
|
|
6115 |
|
|
|
6116 |
this.apply((t * v0 * v0) + c,
|
|
|
6117 |
(t * v0 * v1) - (s * v2),
|
|
|
6118 |
(t * v0 * v2) + (s * v1),
|
|
|
6119 |
0,
|
|
|
6120 |
(t * v0 * v1) + (s * v2),
|
|
|
6121 |
(t * v1 * v1) + c,
|
|
|
6122 |
(t * v1 * v2) - (s * v0),
|
|
|
6123 |
0,
|
|
|
6124 |
(t * v0 * v2) - (s * v1),
|
|
|
6125 |
(t * v1 * v2) + (s * v0),
|
|
|
6126 |
(t * v2 * v2) + c,
|
|
|
6127 |
0,
|
|
|
6128 |
0, 0, 0, 1);
|
|
|
6129 |
}
|
|
|
6130 |
},
|
|
|
6131 |
/**
|
|
|
6132 |
* @member PMatrix3D
|
|
|
6133 |
* The invApply() function applies the inverted matrix to this matrix.
|
|
|
6134 |
*
|
|
|
6135 |
* @param {float} m00 the first element of the matrix
|
|
|
6136 |
* @param {float} m01 the second element of the matrix
|
|
|
6137 |
* @param {float} m02 the third element of the matrix
|
|
|
6138 |
* @param {float} m03 the fourth element of the matrix
|
|
|
6139 |
* @param {float} m10 the fifth element of the matrix
|
|
|
6140 |
* @param {float} m11 the sixth element of the matrix
|
|
|
6141 |
* @param {float} m12 the seventh element of the matrix
|
|
|
6142 |
* @param {float} m13 the eight element of the matrix
|
|
|
6143 |
* @param {float} m20 the nineth element of the matrix
|
|
|
6144 |
* @param {float} m21 the tenth element of the matrix
|
|
|
6145 |
* @param {float} m22 the eleventh element of the matrix
|
|
|
6146 |
* @param {float} m23 the twelveth element of the matrix
|
|
|
6147 |
* @param {float} m30 the thirteenth element of the matrix
|
|
|
6148 |
* @param {float} m31 the fourtheenth element of the matrix
|
|
|
6149 |
* @param {float} m32 the fivetheenth element of the matrix
|
|
|
6150 |
* @param {float} m33 the sixteenth element of the matrix
|
|
|
6151 |
*
|
|
|
6152 |
* @return {boolean} returns true if the operation was successful.
|
|
|
6153 |
*/
|
|
|
6154 |
invApply: function() {
|
|
|
6155 |
if (inverseCopy === undef) {
|
|
|
6156 |
inverseCopy = new PMatrix3D();
|
|
|
6157 |
}
|
|
|
6158 |
var a = arguments;
|
|
|
6159 |
inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8],
|
|
|
6160 |
a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
|
|
|
6161 |
|
|
|
6162 |
if (!inverseCopy.invert()) {
|
|
|
6163 |
return false;
|
|
|
6164 |
}
|
|
|
6165 |
this.preApply(inverseCopy);
|
|
|
6166 |
return true;
|
|
|
6167 |
},
|
|
|
6168 |
/**
|
|
|
6169 |
* @member PMatrix3D
|
|
|
6170 |
* The rotateZ() function rotates the matrix.
|
|
|
6171 |
*
|
|
|
6172 |
* @param {float} angle the angle of rotation in radiants
|
|
|
6173 |
*/
|
|
|
6174 |
rotateX: function(angle) {
|
|
|
6175 |
var c = p.cos(angle);
|
|
|
6176 |
var s = p.sin(angle);
|
|
|
6177 |
this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
|
|
|
6178 |
},
|
|
|
6179 |
/**
|
|
|
6180 |
* @member PMatrix3D
|
|
|
6181 |
* The rotateY() function rotates the matrix.
|
|
|
6182 |
*
|
|
|
6183 |
* @param {float} angle the angle of rotation in radiants
|
|
|
6184 |
*/
|
|
|
6185 |
rotateY: function(angle) {
|
|
|
6186 |
var c = p.cos(angle);
|
|
|
6187 |
var s = p.sin(angle);
|
|
|
6188 |
this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
|
|
|
6189 |
},
|
|
|
6190 |
/**
|
|
|
6191 |
* @member PMatrix3D
|
|
|
6192 |
* The rotateZ() function rotates the matrix.
|
|
|
6193 |
*
|
|
|
6194 |
* @param {float} angle the angle of rotation in radiants
|
|
|
6195 |
*/
|
|
|
6196 |
rotateZ: function(angle) {
|
|
|
6197 |
var c = Math.cos(angle);
|
|
|
6198 |
var s = Math.sin(angle);
|
|
|
6199 |
this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
|
6200 |
},
|
|
|
6201 |
/**
|
|
|
6202 |
* @member PMatrix3D
|
|
|
6203 |
* 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.
|
|
|
6204 |
* This is equivalent to a three parameter call.
|
|
|
6205 |
*
|
|
|
6206 |
* @param {float} sx the amount to scale on the x-axis
|
|
|
6207 |
* @param {float} sy the amount to scale on the y-axis
|
|
|
6208 |
* @param {float} sz the amount to scale on the z-axis
|
|
|
6209 |
*/
|
|
|
6210 |
scale: function(sx, sy, sz) {
|
|
|
6211 |
if (sx && !sy && !sz) {
|
|
|
6212 |
sy = sz = sx;
|
|
|
6213 |
} else if (sx && sy && !sz) {
|
|
|
6214 |
sz = 1;
|
|
|
6215 |
}
|
|
|
6216 |
|
|
|
6217 |
if (sx && sy && sz) {
|
|
|
6218 |
this.elements[0] *= sx;
|
|
|
6219 |
this.elements[1] *= sy;
|
|
|
6220 |
this.elements[2] *= sz;
|
|
|
6221 |
this.elements[4] *= sx;
|
|
|
6222 |
this.elements[5] *= sy;
|
|
|
6223 |
this.elements[6] *= sz;
|
|
|
6224 |
this.elements[8] *= sx;
|
|
|
6225 |
this.elements[9] *= sy;
|
|
|
6226 |
this.elements[10] *= sz;
|
|
|
6227 |
this.elements[12] *= sx;
|
|
|
6228 |
this.elements[13] *= sy;
|
|
|
6229 |
this.elements[14] *= sz;
|
|
|
6230 |
}
|
|
|
6231 |
},
|
|
|
6232 |
/**
|
|
|
6233 |
* @member PMatrix3D
|
|
|
6234 |
* The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
|
|
|
6235 |
* Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
|
|
6236 |
*
|
|
|
6237 |
* @param {float} angle angle of skew specified in radians
|
|
|
6238 |
*/
|
|
|
6239 |
skewX: function(angle) {
|
|
|
6240 |
var t = Math.tan(angle);
|
|
|
6241 |
this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
|
|
6242 |
},
|
|
|
6243 |
/**
|
|
|
6244 |
* @member PMatrix3D
|
|
|
6245 |
* The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
|
|
|
6246 |
* Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
|
|
|
6247 |
*
|
|
|
6248 |
* @param {float} angle angle of skew specified in radians
|
|
|
6249 |
*/
|
|
|
6250 |
skewY: function(angle) {
|
|
|
6251 |
var t = Math.tan(angle);
|
|
|
6252 |
this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
|
|
6253 |
},
|
|
|
6254 |
multX: function(x, y, z, w) {
|
|
|
6255 |
if (!z) {
|
|
|
6256 |
return this.elements[0] * x + this.elements[1] * y + this.elements[3];
|
|
|
6257 |
}
|
|
|
6258 |
if (!w) {
|
|
|
6259 |
return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
|
|
|
6260 |
}
|
|
|
6261 |
return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
|
|
|
6262 |
},
|
|
|
6263 |
multY: function(x, y, z, w) {
|
|
|
6264 |
if (!z) {
|
|
|
6265 |
return this.elements[4] * x + this.elements[5] * y + this.elements[7];
|
|
|
6266 |
}
|
|
|
6267 |
if (!w) {
|
|
|
6268 |
return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
|
|
|
6269 |
}
|
|
|
6270 |
return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
|
|
|
6271 |
},
|
|
|
6272 |
multZ: function(x, y, z, w) {
|
|
|
6273 |
if (!w) {
|
|
|
6274 |
return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
|
|
|
6275 |
}
|
|
|
6276 |
return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
|
|
|
6277 |
},
|
|
|
6278 |
multW: function(x, y, z, w) {
|
|
|
6279 |
if (!w) {
|
|
|
6280 |
return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15];
|
|
|
6281 |
}
|
|
|
6282 |
return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
|
|
|
6283 |
},
|
|
|
6284 |
/**
|
|
|
6285 |
* @member PMatrix3D
|
|
|
6286 |
* The invert() function inverts this matrix
|
|
|
6287 |
*
|
|
|
6288 |
* @return {boolean} true if successful
|
|
|
6289 |
*/
|
|
|
6290 |
invert: function() {
|
|
|
6291 |
var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4];
|
|
|
6292 |
var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4];
|
|
|
6293 |
var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4];
|
|
|
6294 |
var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5];
|
|
|
6295 |
var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5];
|
|
|
6296 |
var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6];
|
|
|
6297 |
var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12];
|
|
|
6298 |
var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12];
|
|
|
6299 |
var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12];
|
|
|
6300 |
var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13];
|
|
|
6301 |
var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13];
|
|
|
6302 |
var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14];
|
|
|
6303 |
|
|
|
6304 |
// Determinant
|
|
|
6305 |
var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
|
|
|
6306 |
|
|
|
6307 |
// Account for a very small value
|
|
|
6308 |
// return false if not successful.
|
|
|
6309 |
if (Math.abs(fDet) <= 1e-9) {
|
|
|
6310 |
return false;
|
|
|
6311 |
}
|
|
|
6312 |
|
|
|
6313 |
var kInv = [];
|
|
|
6314 |
kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3;
|
|
|
6315 |
kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1;
|
|
|
6316 |
kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0;
|
|
|
6317 |
kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0;
|
|
|
6318 |
kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3;
|
|
|
6319 |
kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1;
|
|
|
6320 |
kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0;
|
|
|
6321 |
kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0;
|
|
|
6322 |
kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3;
|
|
|
6323 |
kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1;
|
|
|
6324 |
kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0;
|
|
|
6325 |
kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0;
|
|
|
6326 |
kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3;
|
|
|
6327 |
kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1;
|
|
|
6328 |
kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0;
|
|
|
6329 |
kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0;
|
|
|
6330 |
|
|
|
6331 |
// Inverse using Determinant
|
|
|
6332 |
var fInvDet = 1.0 / fDet;
|
|
|
6333 |
kInv[0] *= fInvDet;
|
|
|
6334 |
kInv[1] *= fInvDet;
|
|
|
6335 |
kInv[2] *= fInvDet;
|
|
|
6336 |
kInv[3] *= fInvDet;
|
|
|
6337 |
kInv[4] *= fInvDet;
|
|
|
6338 |
kInv[5] *= fInvDet;
|
|
|
6339 |
kInv[6] *= fInvDet;
|
|
|
6340 |
kInv[7] *= fInvDet;
|
|
|
6341 |
kInv[8] *= fInvDet;
|
|
|
6342 |
kInv[9] *= fInvDet;
|
|
|
6343 |
kInv[10] *= fInvDet;
|
|
|
6344 |
kInv[11] *= fInvDet;
|
|
|
6345 |
kInv[12] *= fInvDet;
|
|
|
6346 |
kInv[13] *= fInvDet;
|
|
|
6347 |
kInv[14] *= fInvDet;
|
|
|
6348 |
kInv[15] *= fInvDet;
|
|
|
6349 |
|
|
|
6350 |
this.elements = kInv.slice();
|
|
|
6351 |
return true;
|
|
|
6352 |
},
|
|
|
6353 |
toString: function() {
|
|
|
6354 |
var str = "";
|
|
|
6355 |
for (var i = 0; i < 15; i++) {
|
|
|
6356 |
str += this.elements[i] + ", ";
|
|
|
6357 |
}
|
|
|
6358 |
str += this.elements[15];
|
|
|
6359 |
return str;
|
|
|
6360 |
},
|
|
|
6361 |
/**
|
|
|
6362 |
* @member PMatrix3D
|
|
|
6363 |
* The print() function prints out the elements of this matrix
|
|
|
6364 |
*/
|
|
|
6365 |
print: function() {
|
|
|
6366 |
var digits = printMatrixHelper(this.elements);
|
|
|
6367 |
|
|
|
6368 |
var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) +
|
|
|
6369 |
" " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) +
|
|
|
6370 |
"\n" + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) +
|
|
|
6371 |
" " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) +
|
|
|
6372 |
"\n" + p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) +
|
|
|
6373 |
" " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) +
|
|
|
6374 |
"\n" + p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) +
|
|
|
6375 |
" " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n";
|
|
|
6376 |
p.println(output);
|
|
|
6377 |
},
|
|
|
6378 |
invTranslate: function(tx, ty, tz) {
|
|
|
6379 |
this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1);
|
|
|
6380 |
},
|
|
|
6381 |
invRotateX: function(angle) {
|
|
|
6382 |
var c = Math.cos(-angle);
|
|
|
6383 |
var s = Math.sin(-angle);
|
|
|
6384 |
this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
|
|
|
6385 |
},
|
|
|
6386 |
invRotateY: function(angle) {
|
|
|
6387 |
var c = Math.cos(-angle);
|
|
|
6388 |
var s = Math.sin(-angle);
|
|
|
6389 |
this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
|
|
|
6390 |
},
|
|
|
6391 |
invRotateZ: function(angle) {
|
|
|
6392 |
var c = Math.cos(-angle);
|
|
|
6393 |
var s = Math.sin(-angle);
|
|
|
6394 |
this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
|
6395 |
},
|
|
|
6396 |
invScale: function(x, y, z) {
|
|
|
6397 |
this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]);
|
|
|
6398 |
}
|
|
|
6399 |
};
|
|
|
6400 |
|
|
|
6401 |
/**
|
|
|
6402 |
* @private
|
|
|
6403 |
* The matrix stack stores the transformations and translations that occur within the space.
|
|
|
6404 |
*/
|
|
|
6405 |
var PMatrixStack = p.PMatrixStack = function() {
|
|
|
6406 |
this.matrixStack = [];
|
|
|
6407 |
};
|
|
|
6408 |
|
|
|
6409 |
/**
|
|
|
6410 |
* @member PMatrixStack
|
|
|
6411 |
* load pushes the matrix given in the function into the stack
|
|
|
6412 |
*
|
|
|
6413 |
* @param {Object | Array} matrix the matrix to be pushed into the stack
|
|
|
6414 |
*/
|
|
|
6415 |
PMatrixStack.prototype.load = function() {
|
|
|
6416 |
var tmpMatrix = drawing.$newPMatrix();
|
|
|
6417 |
|
|
|
6418 |
if (arguments.length === 1) {
|
|
|
6419 |
tmpMatrix.set(arguments[0]);
|
|
|
6420 |
} else {
|
|
|
6421 |
tmpMatrix.set(arguments);
|
|
|
6422 |
}
|
|
|
6423 |
this.matrixStack.push(tmpMatrix);
|
|
|
6424 |
};
|
|
|
6425 |
|
|
|
6426 |
Drawing2D.prototype.$newPMatrix = function() {
|
|
|
6427 |
return new PMatrix2D();
|
|
|
6428 |
};
|
|
|
6429 |
|
|
|
6430 |
Drawing3D.prototype.$newPMatrix = function() {
|
|
|
6431 |
return new PMatrix3D();
|
|
|
6432 |
};
|
|
|
6433 |
|
|
|
6434 |
/**
|
|
|
6435 |
* @member PMatrixStack
|
|
|
6436 |
* push adds a duplicate of the top of the stack onto the stack - uses the peek function
|
|
|
6437 |
*/
|
|
|
6438 |
PMatrixStack.prototype.push = function() {
|
|
|
6439 |
this.matrixStack.push(this.peek());
|
|
|
6440 |
};
|
|
|
6441 |
|
|
|
6442 |
/**
|
|
|
6443 |
* @member PMatrixStack
|
|
|
6444 |
* pop removes returns the matrix at the top of the stack
|
|
|
6445 |
*
|
|
|
6446 |
* @returns {Object} the matrix at the top of the stack
|
|
|
6447 |
*/
|
|
|
6448 |
PMatrixStack.prototype.pop = function() {
|
|
|
6449 |
return this.matrixStack.pop();
|
|
|
6450 |
};
|
|
|
6451 |
|
|
|
6452 |
/**
|
|
|
6453 |
* @member PMatrixStack
|
|
|
6454 |
* peek returns but doesn't remove the matrix at the top of the stack
|
|
|
6455 |
*
|
|
|
6456 |
* @returns {Object} the matrix at the top of the stack
|
|
|
6457 |
*/
|
|
|
6458 |
PMatrixStack.prototype.peek = function() {
|
|
|
6459 |
var tmpMatrix = drawing.$newPMatrix();
|
|
|
6460 |
|
|
|
6461 |
tmpMatrix.set(this.matrixStack[this.matrixStack.length - 1]);
|
|
|
6462 |
return tmpMatrix;
|
|
|
6463 |
};
|
|
|
6464 |
|
|
|
6465 |
/**
|
|
|
6466 |
* @member PMatrixStack
|
|
|
6467 |
* this function multiplies the matrix at the top of the stack with the matrix given as a parameter
|
|
|
6468 |
*
|
|
|
6469 |
* @param {Object | Array} matrix the matrix to be multiplied into the stack
|
|
|
6470 |
*/
|
|
|
6471 |
PMatrixStack.prototype.mult = function(matrix) {
|
|
|
6472 |
this.matrixStack[this.matrixStack.length - 1].apply(matrix);
|
|
|
6473 |
};
|
|
|
6474 |
|
|
|
6475 |
////////////////////////////////////////////////////////////////////////////
|
|
|
6476 |
// Array handling
|
|
|
6477 |
////////////////////////////////////////////////////////////////////////////
|
|
|
6478 |
|
|
|
6479 |
/**
|
|
|
6480 |
* The split() function breaks a string into pieces using a character or string
|
|
|
6481 |
* as the divider. The delim parameter specifies the character or characters that
|
|
|
6482 |
* mark the boundaries between each piece. A String[] array is returned that contains
|
|
|
6483 |
* each of the pieces.
|
|
|
6484 |
* If the result is a set of numbers, you can convert the String[] array to to a float[]
|
|
|
6485 |
* or int[] array using the datatype conversion functions int() and float() (see example above).
|
|
|
6486 |
* The splitTokens() function works in a similar fashion, except that it splits using a range
|
|
|
6487 |
* of characters instead of a specific character or sequence.
|
|
|
6488 |
*
|
|
|
6489 |
* @param {String} str the String to be split
|
|
|
6490 |
* @param {String} delim the character or String used to separate the data
|
|
|
6491 |
*
|
|
|
6492 |
* @returns {string[]} The new string array
|
|
|
6493 |
*
|
|
|
6494 |
* @see splitTokens
|
|
|
6495 |
* @see join
|
|
|
6496 |
* @see trim
|
|
|
6497 |
*/
|
|
|
6498 |
p.split = function(str, delim) {
|
|
|
6499 |
return str.split(delim);
|
|
|
6500 |
};
|
|
|
6501 |
|
|
|
6502 |
/**
|
|
|
6503 |
* The splitTokens() function splits a String at one or many character "tokens." The tokens
|
|
|
6504 |
* parameter specifies the character or characters to be used as a boundary.
|
|
|
6505 |
* If no tokens character is specified, any whitespace character is used to split.
|
|
|
6506 |
* Whitespace characters include tab (\t), line feed (\n), carriage return (\r), form
|
|
|
6507 |
* feed (\f), and space. To convert a String to an array of integers or floats, use the
|
|
|
6508 |
* datatype conversion functions int() and float() to convert the array of Strings.
|
|
|
6509 |
*
|
|
|
6510 |
* @param {String} str the String to be split
|
|
|
6511 |
* @param {Char[]} tokens list of individual characters that will be used as separators
|
|
|
6512 |
*
|
|
|
6513 |
* @returns {string[]} The new string array
|
|
|
6514 |
*
|
|
|
6515 |
* @see split
|
|
|
6516 |
* @see join
|
|
|
6517 |
* @see trim
|
|
|
6518 |
*/
|
|
|
6519 |
p.splitTokens = function(str, tokens) {
|
|
|
6520 |
if (arguments.length === 1) {
|
|
|
6521 |
tokens = "\n\t\r\f ";
|
|
|
6522 |
}
|
|
|
6523 |
|
|
|
6524 |
tokens = "[" + tokens + "]";
|
|
|
6525 |
|
|
|
6526 |
var ary = [];
|
|
|
6527 |
var index = 0;
|
|
|
6528 |
var pos = str.search(tokens);
|
|
|
6529 |
|
|
|
6530 |
while (pos >= 0) {
|
|
|
6531 |
if (pos === 0) {
|
|
|
6532 |
str = str.substring(1);
|
|
|
6533 |
} else {
|
|
|
6534 |
ary[index] = str.substring(0, pos);
|
|
|
6535 |
index++;
|
|
|
6536 |
str = str.substring(pos);
|
|
|
6537 |
}
|
|
|
6538 |
pos = str.search(tokens);
|
|
|
6539 |
}
|
|
|
6540 |
|
|
|
6541 |
if (str.length > 0) {
|
|
|
6542 |
ary[index] = str;
|
|
|
6543 |
}
|
|
|
6544 |
|
|
|
6545 |
if (ary.length === 0) {
|
|
|
6546 |
ary = undef;
|
|
|
6547 |
}
|
|
|
6548 |
|
|
|
6549 |
return ary;
|
|
|
6550 |
};
|
|
|
6551 |
|
|
|
6552 |
/**
|
|
|
6553 |
* Expands an array by one element and adds data to the new position. The datatype of
|
|
|
6554 |
* the element parameter must be the same as the datatype of the array.
|
|
|
6555 |
* When using an array of objects, the data returned from the function must be cast to
|
|
|
6556 |
* the object array's data type. For example: SomeClass[] items = (SomeClass[])
|
|
|
6557 |
* append(originalArray, element).
|
|
|
6558 |
*
|
|
|
6559 |
* @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
|
|
|
6560 |
* byte[], char[], int[], float[], or String[], or an array of objects
|
|
|
6561 |
* @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} element new data for the array
|
|
|
6562 |
*
|
|
|
6563 |
* @returns Array (the same datatype as the input)
|
|
|
6564 |
*
|
|
|
6565 |
* @see shorten
|
|
|
6566 |
* @see expand
|
|
|
6567 |
*/
|
|
|
6568 |
p.append = function(array, element) {
|
|
|
6569 |
array[array.length] = element;
|
|
|
6570 |
return array;
|
|
|
6571 |
};
|
|
|
6572 |
|
|
|
6573 |
/**
|
|
|
6574 |
* Concatenates two arrays. For example, concatenating the array { 1, 2, 3 } and the
|
|
|
6575 |
* array { 4, 5, 6 } yields { 1, 2, 3, 4, 5, 6 }. Both parameters must be arrays of the
|
|
|
6576 |
* same datatype.
|
|
|
6577 |
* When using an array of objects, the data returned from the function must be cast to the
|
|
|
6578 |
* object array's data type. For example: SomeClass[] items = (SomeClass[]) concat(array1, array2).
|
|
|
6579 |
*
|
|
|
6580 |
* @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array1 boolean[],
|
|
|
6581 |
* byte[], char[], int[], float[], String[], or an array of objects
|
|
|
6582 |
* @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array2 boolean[],
|
|
|
6583 |
* byte[], char[], int[], float[], String[], or an array of objects
|
|
|
6584 |
*
|
|
|
6585 |
* @returns Array (the same datatype as the input)
|
|
|
6586 |
*
|
|
|
6587 |
* @see splice
|
|
|
6588 |
*/
|
|
|
6589 |
p.concat = function(array1, array2) {
|
|
|
6590 |
return array1.concat(array2);
|
|
|
6591 |
};
|
|
|
6592 |
|
|
|
6593 |
/**
|
|
|
6594 |
* Sorts an array of numbers from smallest to largest and puts an array of
|
|
|
6595 |
* words in alphabetical order. The original array is not modified, a
|
|
|
6596 |
* re-ordered array is returned. The count parameter states the number of
|
|
|
6597 |
* elements to sort. For example if there are 12 elements in an array and
|
|
|
6598 |
* if count is the value 5, only the first five elements on the array will
|
|
|
6599 |
* be sorted. Alphabetical ordering is case insensitive.
|
|
|
6600 |
*
|
|
|
6601 |
* @param {String[] | int[] | float[]} array Array of elements to sort
|
|
|
6602 |
* @param {int} numElem Number of elements to sort
|
|
|
6603 |
*
|
|
|
6604 |
* @returns {String[] | int[] | float[]} Array (same datatype as the input)
|
|
|
6605 |
*
|
|
|
6606 |
* @see reverse
|
|
|
6607 |
*/
|
|
|
6608 |
p.sort = function(array, numElem) {
|
|
|
6609 |
var ret = [];
|
|
|
6610 |
|
|
|
6611 |
// depending on the type used (int, float) or string
|
|
|
6612 |
// we'll need to use a different compare function
|
|
|
6613 |
if (array.length > 0) {
|
|
|
6614 |
// copy since we need to return another array
|
|
|
6615 |
var elemsToCopy = numElem > 0 ? numElem : array.length;
|
|
|
6616 |
for (var i = 0; i < elemsToCopy; i++) {
|
|
|
6617 |
ret.push(array[i]);
|
|
|
6618 |
}
|
|
|
6619 |
if (typeof array[0] === "string") {
|
|
|
6620 |
ret.sort();
|
|
|
6621 |
}
|
|
|
6622 |
// int or float
|
|
|
6623 |
else {
|
|
|
6624 |
ret.sort(function(a, b) {
|
|
|
6625 |
return a - b;
|
|
|
6626 |
});
|
|
|
6627 |
}
|
|
|
6628 |
|
|
|
6629 |
// copy on the rest of the elements that were not sorted in case the user
|
|
|
6630 |
// only wanted a subset of an array to be sorted.
|
|
|
6631 |
if (numElem > 0) {
|
|
|
6632 |
for (var j = ret.length; j < array.length; j++) {
|
|
|
6633 |
ret.push(array[j]);
|
|
|
6634 |
}
|
|
|
6635 |
}
|
|
|
6636 |
}
|
|
|
6637 |
return ret;
|
|
|
6638 |
};
|
|
|
6639 |
|
|
|
6640 |
/**
|
|
|
6641 |
* Inserts a value or array of values into an existing array. The first two parameters must
|
|
|
6642 |
* be of the same datatype. The array parameter defines the array which will be modified
|
|
|
6643 |
* and the second parameter defines the data which will be inserted. When using an array
|
|
|
6644 |
* of objects, the data returned from the function must be cast to the object array's data
|
|
|
6645 |
* type. For example: SomeClass[] items = (SomeClass[]) splice(array1, array2, index).
|
|
|
6646 |
*
|
|
|
6647 |
* @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
|
|
|
6648 |
* byte[], char[], int[], float[], String[], or an array of objects
|
|
|
6649 |
* @param {boolean|byte|char|int|float|String|boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects}
|
|
|
6650 |
* value boolean, byte, char, int, float, String, boolean[], byte[], char[], int[],
|
|
|
6651 |
* float[], String[], or other Object: value or an array of objects to be spliced in
|
|
|
6652 |
* @param {int} index position in the array from which to insert data
|
|
|
6653 |
*
|
|
|
6654 |
* @returns Array (the same datatype as the input)
|
|
|
6655 |
*
|
|
|
6656 |
* @see contract
|
|
|
6657 |
* @see subset
|
|
|
6658 |
*/
|
|
|
6659 |
p.splice = function(array, value, index) {
|
|
|
6660 |
|
|
|
6661 |
// Trying to splice an empty array into "array" in P5 won't do
|
|
|
6662 |
// anything, just return the original.
|
|
|
6663 |
if(value.length === 0)
|
|
|
6664 |
{
|
|
|
6665 |
return array;
|
|
|
6666 |
}
|
|
|
6667 |
|
|
|
6668 |
// If the second argument was an array, we'll need to iterate over all
|
|
|
6669 |
// the "value" elements and add one by one because
|
|
|
6670 |
// array.splice(index, 0, value);
|
|
|
6671 |
// would create a multi-dimensional array which isn't what we want.
|
|
|
6672 |
if(value instanceof Array) {
|
|
|
6673 |
for(var i = 0, j = index; i < value.length; j++,i++) {
|
|
|
6674 |
array.splice(j, 0, value[i]);
|
|
|
6675 |
}
|
|
|
6676 |
} else {
|
|
|
6677 |
array.splice(index, 0, value);
|
|
|
6678 |
}
|
|
|
6679 |
|
|
|
6680 |
return array;
|
|
|
6681 |
};
|
|
|
6682 |
|
|
|
6683 |
/**
|
|
|
6684 |
* Extracts an array of elements from an existing array. The array parameter defines the
|
|
|
6685 |
* array from which the elements will be copied and the offset and length parameters determine
|
|
|
6686 |
* which elements to extract. If no length is given, elements will be extracted from the offset
|
|
|
6687 |
* to the end of the array. When specifying the offset remember the first array element is 0.
|
|
|
6688 |
* This function does not change the source array.
|
|
|
6689 |
* When using an array of objects, the data returned from the function must be cast to the
|
|
|
6690 |
* object array's data type.
|
|
|
6691 |
*
|
|
|
6692 |
* @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
|
|
|
6693 |
* byte[], char[], int[], float[], String[], or an array of objects
|
|
|
6694 |
* @param {int} offset position to begin
|
|
|
6695 |
* @param {int} length number of values to extract
|
|
|
6696 |
*
|
|
|
6697 |
* @returns Array (the same datatype as the input)
|
|
|
6698 |
*
|
|
|
6699 |
* @see splice
|
|
|
6700 |
*/
|
|
|
6701 |
p.subset = function(array, offset, length) {
|
|
|
6702 |
var end = (length !== undef) ? offset + length : array.length;
|
|
|
6703 |
return array.slice(offset, end);
|
|
|
6704 |
};
|
|
|
6705 |
|
|
|
6706 |
/**
|
|
|
6707 |
* Combines an array of Strings into one String, each separated by the character(s) used for
|
|
|
6708 |
* the separator parameter. To join arrays of ints or floats, it's necessary to first convert
|
|
|
6709 |
* them to strings using nf() or nfs().
|
|
|
6710 |
*
|
|
|
6711 |
* @param {Array} array array of Strings
|
|
|
6712 |
* @param {char|String} separator char or String to be placed between each item
|
|
|
6713 |
*
|
|
|
6714 |
* @returns {String} The combined string
|
|
|
6715 |
*
|
|
|
6716 |
* @see split
|
|
|
6717 |
* @see trim
|
|
|
6718 |
* @see nf
|
|
|
6719 |
* @see nfs
|
|
|
6720 |
*/
|
|
|
6721 |
p.join = function(array, seperator) {
|
|
|
6722 |
return array.join(seperator);
|
|
|
6723 |
};
|
|
|
6724 |
|
|
|
6725 |
/**
|
|
|
6726 |
* Decreases an array by one element and returns the shortened array. When using an
|
|
|
6727 |
* array of objects, the data returned from the function must be cast to the object array's
|
|
|
6728 |
* data type. For example: SomeClass[] items = (SomeClass[]) shorten(originalArray).
|
|
|
6729 |
*
|
|
|
6730 |
* @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array
|
|
|
6731 |
* boolean[], byte[], char[], int[], float[], or String[], or an array of objects
|
|
|
6732 |
*
|
|
|
6733 |
* @returns Array (the same datatype as the input)
|
|
|
6734 |
*
|
|
|
6735 |
* @see append
|
|
|
6736 |
* @see expand
|
|
|
6737 |
*/
|
|
|
6738 |
p.shorten = function(ary) {
|
|
|
6739 |
var newary = [];
|
|
|
6740 |
|
|
|
6741 |
// copy array into new array
|
|
|
6742 |
var len = ary.length;
|
|
|
6743 |
for (var i = 0; i < len; i++) {
|
|
|
6744 |
newary[i] = ary[i];
|
|
|
6745 |
}
|
|
|
6746 |
newary.pop();
|
|
|
6747 |
|
|
|
6748 |
return newary;
|
|
|
6749 |
};
|
|
|
6750 |
|
|
|
6751 |
/**
|
|
|
6752 |
* Increases the size of an array. By default, this function doubles the size of the array,
|
|
|
6753 |
* but the optional newSize parameter provides precise control over the increase in size.
|
|
|
6754 |
* When using an array of objects, the data returned from the function must be cast to the
|
|
|
6755 |
* object array's data type. For example: SomeClass[] items = (SomeClass[]) expand(originalArray).
|
|
|
6756 |
*
|
|
|
6757 |
* @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} ary
|
|
|
6758 |
* boolean[], byte[], char[], int[], float[], String[], or an array of objects
|
|
|
6759 |
* @param {int} newSize positive int: new size for the array
|
|
|
6760 |
*
|
|
|
6761 |
* @returns Array (the same datatype as the input)
|
|
|
6762 |
*
|
|
|
6763 |
* @see contract
|
|
|
6764 |
*/
|
|
|
6765 |
p.expand = function(ary, targetSize) {
|
|
|
6766 |
var temp = ary.slice(0),
|
|
|
6767 |
newSize = targetSize || ary.length * 2;
|
|
|
6768 |
temp.length = newSize;
|
|
|
6769 |
return temp;
|
|
|
6770 |
};
|
|
|
6771 |
|
|
|
6772 |
/**
|
|
|
6773 |
* Copies an array (or part of an array) to another array. The src array is copied to the
|
|
|
6774 |
* dst array, beginning at the position specified by srcPos and into the position specified
|
|
|
6775 |
* by dstPos. The number of elements to copy is determined by length. The simplified version
|
|
|
6776 |
* with two arguments copies an entire array to another of the same size. It is equivalent
|
|
|
6777 |
* to "arrayCopy(src, 0, dst, 0, src.length)". This function is far more efficient for copying
|
|
|
6778 |
* array data than iterating through a for and copying each element.
|
|
|
6779 |
*
|
|
|
6780 |
* @param {Array} src an array of any data type: the source array
|
|
|
6781 |
* @param {Array} dest an array of any data type (as long as it's the same as src): the destination array
|
|
|
6782 |
* @param {int} srcPos starting position in the source array
|
|
|
6783 |
* @param {int} destPos starting position in the destination array
|
|
|
6784 |
* @param {int} length number of array elements to be copied
|
|
|
6785 |
*
|
|
|
6786 |
* @returns none
|
|
|
6787 |
*/
|
|
|
6788 |
p.arrayCopy = function() { // src, srcPos, dest, destPos, length) {
|
|
|
6789 |
var src, srcPos = 0, dest, destPos = 0, length;
|
|
|
6790 |
|
|
|
6791 |
if (arguments.length === 2) {
|
|
|
6792 |
// recall itself and copy src to dest from start index 0 to 0 of src.length
|
|
|
6793 |
src = arguments[0];
|
|
|
6794 |
dest = arguments[1];
|
|
|
6795 |
length = src.length;
|
|
|
6796 |
} else if (arguments.length === 3) {
|
|
|
6797 |
// recall itself and copy src to dest from start index 0 to 0 of length
|
|
|
6798 |
src = arguments[0];
|
|
|
6799 |
dest = arguments[1];
|
|
|
6800 |
length = arguments[2];
|
|
|
6801 |
} else if (arguments.length === 5) {
|
|
|
6802 |
src = arguments[0];
|
|
|
6803 |
srcPos = arguments[1];
|
|
|
6804 |
dest = arguments[2];
|
|
|
6805 |
destPos = arguments[3];
|
|
|
6806 |
length = arguments[4];
|
|
|
6807 |
}
|
|
|
6808 |
|
|
|
6809 |
// copy src to dest from index srcPos to index destPos of length recursivly on objects
|
|
|
6810 |
for (var i = srcPos, j = destPos; i < length + srcPos; i++, j++) {
|
|
|
6811 |
if (dest[j] !== undef) {
|
|
|
6812 |
dest[j] = src[i];
|
|
|
6813 |
} else {
|
|
|
6814 |
throw "array index out of bounds exception";
|
|
|
6815 |
}
|
|
|
6816 |
}
|
|
|
6817 |
};
|
|
|
6818 |
|
|
|
6819 |
/**
|
|
|
6820 |
* Reverses the order of an array.
|
|
|
6821 |
*
|
|
|
6822 |
* @param {boolean[]|byte[]|char[]|int[]|float[]|String[]} array
|
|
|
6823 |
* boolean[], byte[], char[], int[], float[], or String[]
|
|
|
6824 |
*
|
|
|
6825 |
* @returns Array (the same datatype as the input)
|
|
|
6826 |
*
|
|
|
6827 |
* @see sort
|
|
|
6828 |
*/
|
|
|
6829 |
p.reverse = function(array) {
|
|
|
6830 |
return array.reverse();
|
|
|
6831 |
};
|
|
|
6832 |
|
|
|
6833 |
|
|
|
6834 |
////////////////////////////////////////////////////////////////////////////
|
|
|
6835 |
// Color functions
|
|
|
6836 |
////////////////////////////////////////////////////////////////////////////
|
|
|
6837 |
|
|
|
6838 |
// helper functions for internal blending modes
|
|
|
6839 |
p.mix = function(a, b, f) {
|
|
|
6840 |
return a + (((b - a) * f) >> 8);
|
|
|
6841 |
};
|
|
|
6842 |
|
|
|
6843 |
p.peg = function(n) {
|
|
|
6844 |
return (n < 0) ? 0 : ((n > 255) ? 255 : n);
|
|
|
6845 |
};
|
|
|
6846 |
|
|
|
6847 |
// blending modes
|
|
|
6848 |
/**
|
|
|
6849 |
* These are internal blending modes used for BlendColor()
|
|
|
6850 |
*
|
|
|
6851 |
* @param {Color} c1 First Color to blend
|
|
|
6852 |
* @param {Color} c2 Second Color to blend
|
|
|
6853 |
*
|
|
|
6854 |
* @returns {Color} The blended Color
|
|
|
6855 |
*
|
|
|
6856 |
* @see BlendColor
|
|
|
6857 |
* @see Blend
|
|
|
6858 |
*/
|
|
|
6859 |
p.modes = (function() {
|
|
|
6860 |
var ALPHA_MASK = PConstants.ALPHA_MASK,
|
|
|
6861 |
RED_MASK = PConstants.RED_MASK,
|
|
|
6862 |
GREEN_MASK = PConstants.GREEN_MASK,
|
|
|
6863 |
BLUE_MASK = PConstants.BLUE_MASK,
|
|
|
6864 |
min = Math.min,
|
|
|
6865 |
max = Math.max;
|
|
|
6866 |
|
|
|
6867 |
function applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) {
|
|
|
6868 |
var a = min(((c1 & 0xff000000) >>> 24) + f, 0xff) << 24;
|
|
|
6869 |
|
|
|
6870 |
var r = (ar + (((cr - ar) * f) >> 8));
|
|
|
6871 |
r = ((r < 0) ? 0 : ((r > 255) ? 255 : r)) << 16;
|
|
|
6872 |
|
|
|
6873 |
var g = (ag + (((cg - ag) * f) >> 8));
|
|
|
6874 |
g = ((g < 0) ? 0 : ((g > 255) ? 255 : g)) << 8;
|
|
|
6875 |
|
|
|
6876 |
var b = ab + (((cb - ab) * f) >> 8);
|
|
|
6877 |
b = (b < 0) ? 0 : ((b > 255) ? 255 : b);
|
|
|
6878 |
|
|
|
6879 |
return (a | r | g | b);
|
|
|
6880 |
}
|
|
|
6881 |
|
|
|
6882 |
return {
|
|
|
6883 |
replace: function(c1, c2) {
|
|
|
6884 |
return c2;
|
|
|
6885 |
},
|
|
|
6886 |
blend: function(c1, c2) {
|
|
|
6887 |
var f = (c2 & ALPHA_MASK) >>> 24,
|
|
|
6888 |
ar = (c1 & RED_MASK),
|
|
|
6889 |
ag = (c1 & GREEN_MASK),
|
|
|
6890 |
ab = (c1 & BLUE_MASK),
|
|
|
6891 |
br = (c2 & RED_MASK),
|
|
|
6892 |
bg = (c2 & GREEN_MASK),
|
|
|
6893 |
bb = (c2 & BLUE_MASK);
|
|
|
6894 |
|
|
|
6895 |
return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
|
|
|
6896 |
(ar + (((br - ar) * f) >> 8)) & RED_MASK |
|
|
|
6897 |
(ag + (((bg - ag) * f) >> 8)) & GREEN_MASK |
|
|
|
6898 |
(ab + (((bb - ab) * f) >> 8)) & BLUE_MASK);
|
|
|
6899 |
},
|
|
|
6900 |
add: function(c1, c2) {
|
|
|
6901 |
var f = (c2 & ALPHA_MASK) >>> 24;
|
|
|
6902 |
return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
|
|
|
6903 |
min(((c1 & RED_MASK) + ((c2 & RED_MASK) >> 8) * f), RED_MASK) & RED_MASK |
|
|
|
6904 |
min(((c1 & GREEN_MASK) + ((c2 & GREEN_MASK) >> 8) * f), GREEN_MASK) & GREEN_MASK |
|
|
|
6905 |
min((c1 & BLUE_MASK) + (((c2 & BLUE_MASK) * f) >> 8), BLUE_MASK));
|
|
|
6906 |
},
|
|
|
6907 |
subtract: function(c1, c2) {
|
|
|
6908 |
var f = (c2 & ALPHA_MASK) >>> 24;
|
|
|
6909 |
return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
|
|
|
6910 |
max(((c1 & RED_MASK) - ((c2 & RED_MASK) >> 8) * f), GREEN_MASK) & RED_MASK |
|
|
|
6911 |
max(((c1 & GREEN_MASK) - ((c2 & GREEN_MASK) >> 8) * f), BLUE_MASK) & GREEN_MASK |
|
|
|
6912 |
max((c1 & BLUE_MASK) - (((c2 & BLUE_MASK) * f) >> 8), 0));
|
|
|
6913 |
},
|
|
|
6914 |
lightest: function(c1, c2) {
|
|
|
6915 |
var f = (c2 & ALPHA_MASK) >>> 24;
|
|
|
6916 |
return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
|
|
|
6917 |
max(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f) & RED_MASK |
|
|
|
6918 |
max(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f) & GREEN_MASK |
|
|
|
6919 |
max(c1 & BLUE_MASK, ((c2 & BLUE_MASK) * f) >> 8));
|
|
|
6920 |
},
|
|
|
6921 |
darkest: function(c1, c2) {
|
|
|
6922 |
var f = (c2 & ALPHA_MASK) >>> 24,
|
|
|
6923 |
ar = (c1 & RED_MASK),
|
|
|
6924 |
ag = (c1 & GREEN_MASK),
|
|
|
6925 |
ab = (c1 & BLUE_MASK),
|
|
|
6926 |
br = min(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f),
|
|
|
6927 |
bg = min(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f),
|
|
|
6928 |
bb = min(c1 & BLUE_MASK, ((c2 & BLUE_MASK) * f) >> 8);
|
|
|
6929 |
|
|
|
6930 |
return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
|
|
|
6931 |
(ar + (((br - ar) * f) >> 8)) & RED_MASK |
|
|
|
6932 |
(ag + (((bg - ag) * f) >> 8)) & GREEN_MASK |
|
|
|
6933 |
(ab + (((bb - ab) * f) >> 8)) & BLUE_MASK);
|
|
|
6934 |
},
|
|
|
6935 |
difference: function(c1, c2) {
|
|
|
6936 |
var f = (c2 & ALPHA_MASK) >>> 24,
|
|
|
6937 |
ar = (c1 & RED_MASK) >> 16,
|
|
|
6938 |
ag = (c1 & GREEN_MASK) >> 8,
|
|
|
6939 |
ab = (c1 & BLUE_MASK),
|
|
|
6940 |
br = (c2 & RED_MASK) >> 16,
|
|
|
6941 |
bg = (c2 & GREEN_MASK) >> 8,
|
|
|
6942 |
bb = (c2 & BLUE_MASK),
|
|
|
6943 |
cr = (ar > br) ? (ar - br) : (br - ar),
|
|
|
6944 |
cg = (ag > bg) ? (ag - bg) : (bg - ag),
|
|
|
6945 |
cb = (ab > bb) ? (ab - bb) : (bb - ab);
|
|
|
6946 |
|
|
|
6947 |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
|
|
6948 |
},
|
|
|
6949 |
exclusion: function(c1, c2) {
|
|
|
6950 |
var f = (c2 & ALPHA_MASK) >>> 24,
|
|
|
6951 |
ar = (c1 & RED_MASK) >> 16,
|
|
|
6952 |
ag = (c1 & GREEN_MASK) >> 8,
|
|
|
6953 |
ab = (c1 & BLUE_MASK),
|
|
|
6954 |
br = (c2 & RED_MASK) >> 16,
|
|
|
6955 |
bg = (c2 & GREEN_MASK) >> 8,
|
|
|
6956 |
bb = (c2 & BLUE_MASK),
|
|
|
6957 |
cr = ar + br - ((ar * br) >> 7),
|
|
|
6958 |
cg = ag + bg - ((ag * bg) >> 7),
|
|
|
6959 |
cb = ab + bb - ((ab * bb) >> 7);
|
|
|
6960 |
|
|
|
6961 |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
|
|
6962 |
},
|
|
|
6963 |
multiply: function(c1, c2) {
|
|
|
6964 |
var f = (c2 & ALPHA_MASK) >>> 24,
|
|
|
6965 |
ar = (c1 & RED_MASK) >> 16,
|
|
|
6966 |
ag = (c1 & GREEN_MASK) >> 8,
|
|
|
6967 |
ab = (c1 & BLUE_MASK),
|
|
|
6968 |
br = (c2 & RED_MASK) >> 16,
|
|
|
6969 |
bg = (c2 & GREEN_MASK) >> 8,
|
|
|
6970 |
bb = (c2 & BLUE_MASK),
|
|
|
6971 |
cr = (ar * br) >> 8,
|
|
|
6972 |
cg = (ag * bg) >> 8,
|
|
|
6973 |
cb = (ab * bb) >> 8;
|
|
|
6974 |
|
|
|
6975 |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
|
|
6976 |
},
|
|
|
6977 |
screen: function(c1, c2) {
|
|
|
6978 |
var f = (c2 & ALPHA_MASK) >>> 24,
|
|
|
6979 |
ar = (c1 & RED_MASK) >> 16,
|
|
|
6980 |
ag = (c1 & GREEN_MASK) >> 8,
|
|
|
6981 |
ab = (c1 & BLUE_MASK),
|
|
|
6982 |
br = (c2 & RED_MASK) >> 16,
|
|
|
6983 |
bg = (c2 & GREEN_MASK) >> 8,
|
|
|
6984 |
bb = (c2 & BLUE_MASK),
|
|
|
6985 |
cr = 255 - (((255 - ar) * (255 - br)) >> 8),
|
|
|
6986 |
cg = 255 - (((255 - ag) * (255 - bg)) >> 8),
|
|
|
6987 |
cb = 255 - (((255 - ab) * (255 - bb)) >> 8);
|
|
|
6988 |
|
|
|
6989 |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
|
|
6990 |
},
|
|
|
6991 |
hard_light: function(c1, c2) {
|
|
|
6992 |
var f = (c2 & ALPHA_MASK) >>> 24,
|
|
|
6993 |
ar = (c1 & RED_MASK) >> 16,
|
|
|
6994 |
ag = (c1 & GREEN_MASK) >> 8,
|
|
|
6995 |
ab = (c1 & BLUE_MASK),
|
|
|
6996 |
br = (c2 & RED_MASK) >> 16,
|
|
|
6997 |
bg = (c2 & GREEN_MASK) >> 8,
|
|
|
6998 |
bb = (c2 & BLUE_MASK),
|
|
|
6999 |
cr = (br < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)),
|
|
|
7000 |
cg = (bg < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)),
|
|
|
7001 |
cb = (bb < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
|
|
|
7002 |
|
|
|
7003 |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
|
|
7004 |
},
|
|
|
7005 |
soft_light: function(c1, c2) {
|
|
|
7006 |
var f = (c2 & ALPHA_MASK) >>> 24,
|
|
|
7007 |
ar = (c1 & RED_MASK) >> 16,
|
|
|
7008 |
ag = (c1 & GREEN_MASK) >> 8,
|
|
|
7009 |
ab = (c1 & BLUE_MASK),
|
|
|
7010 |
br = (c2 & RED_MASK) >> 16,
|
|
|
7011 |
bg = (c2 & GREEN_MASK) >> 8,
|
|
|
7012 |
bb = (c2 & BLUE_MASK),
|
|
|
7013 |
cr = ((ar * br) >> 7) + ((ar * ar) >> 8) - ((ar * ar * br) >> 15),
|
|
|
7014 |
cg = ((ag * bg) >> 7) + ((ag * ag) >> 8) - ((ag * ag * bg) >> 15),
|
|
|
7015 |
cb = ((ab * bb) >> 7) + ((ab * ab) >> 8) - ((ab * ab * bb) >> 15);
|
|
|
7016 |
|
|
|
7017 |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
|
|
7018 |
},
|
|
|
7019 |
overlay: function(c1, c2) {
|
|
|
7020 |
var f = (c2 & ALPHA_MASK) >>> 24,
|
|
|
7021 |
ar = (c1 & RED_MASK) >> 16,
|
|
|
7022 |
ag = (c1 & GREEN_MASK) >> 8,
|
|
|
7023 |
ab = (c1 & BLUE_MASK),
|
|
|
7024 |
br = (c2 & RED_MASK) >> 16,
|
|
|
7025 |
bg = (c2 & GREEN_MASK) >> 8,
|
|
|
7026 |
bb = (c2 & BLUE_MASK),
|
|
|
7027 |
cr = (ar < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)),
|
|
|
7028 |
cg = (ag < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)),
|
|
|
7029 |
cb = (ab < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
|
|
|
7030 |
|
|
|
7031 |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
|
|
7032 |
},
|
|
|
7033 |
dodge: function(c1, c2) {
|
|
|
7034 |
var f = (c2 & ALPHA_MASK) >>> 24,
|
|
|
7035 |
ar = (c1 & RED_MASK) >> 16,
|
|
|
7036 |
ag = (c1 & GREEN_MASK) >> 8,
|
|
|
7037 |
ab = (c1 & BLUE_MASK),
|
|
|
7038 |
br = (c2 & RED_MASK) >> 16,
|
|
|
7039 |
bg = (c2 & GREEN_MASK) >> 8,
|
|
|
7040 |
bb = (c2 & BLUE_MASK);
|
|
|
7041 |
|
|
|
7042 |
var cr = 255;
|
|
|
7043 |
if (br !== 255) {
|
|
|
7044 |
cr = (ar << 8) / (255 - br);
|
|
|
7045 |
cr = (cr < 0) ? 0 : ((cr > 255) ? 255 : cr);
|
|
|
7046 |
}
|
|
|
7047 |
|
|
|
7048 |
var cg = 255;
|
|
|
7049 |
if (bg !== 255) {
|
|
|
7050 |
cg = (ag << 8) / (255 - bg);
|
|
|
7051 |
cg = (cg < 0) ? 0 : ((cg > 255) ? 255 : cg);
|
|
|
7052 |
}
|
|
|
7053 |
|
|
|
7054 |
var cb = 255;
|
|
|
7055 |
if (bb !== 255) {
|
|
|
7056 |
cb = (ab << 8) / (255 - bb);
|
|
|
7057 |
cb = (cb < 0) ? 0 : ((cb > 255) ? 255 : cb);
|
|
|
7058 |
}
|
|
|
7059 |
|
|
|
7060 |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
|
|
7061 |
},
|
|
|
7062 |
burn: function(c1, c2) {
|
|
|
7063 |
var f = (c2 & ALPHA_MASK) >>> 24,
|
|
|
7064 |
ar = (c1 & RED_MASK) >> 16,
|
|
|
7065 |
ag = (c1 & GREEN_MASK) >> 8,
|
|
|
7066 |
ab = (c1 & BLUE_MASK),
|
|
|
7067 |
br = (c2 & RED_MASK) >> 16,
|
|
|
7068 |
bg = (c2 & GREEN_MASK) >> 8,
|
|
|
7069 |
bb = (c2 & BLUE_MASK);
|
|
|
7070 |
|
|
|
7071 |
var cr = 0;
|
|
|
7072 |
if (br !== 0) {
|
|
|
7073 |
cr = ((255 - ar) << 8) / br;
|
|
|
7074 |
cr = 255 - ((cr < 0) ? 0 : ((cr > 255) ? 255 : cr));
|
|
|
7075 |
}
|
|
|
7076 |
|
|
|
7077 |
var cg = 0;
|
|
|
7078 |
if (bg !== 0) {
|
|
|
7079 |
cg = ((255 - ag) << 8) / bg;
|
|
|
7080 |
cg = 255 - ((cg < 0) ? 0 : ((cg > 255) ? 255 : cg));
|
|
|
7081 |
}
|
|
|
7082 |
|
|
|
7083 |
var cb = 0;
|
|
|
7084 |
if (bb !== 0) {
|
|
|
7085 |
cb = ((255 - ab) << 8) / bb;
|
|
|
7086 |
cb = 255 - ((cb < 0) ? 0 : ((cb > 255) ? 255 : cb));
|
|
|
7087 |
}
|
|
|
7088 |
|
|
|
7089 |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
|
|
|
7090 |
}
|
|
|
7091 |
};
|
|
|
7092 |
}());
|
|
|
7093 |
|
|
|
7094 |
function color$4(aValue1, aValue2, aValue3, aValue4) {
|
|
|
7095 |
var r, g, b, a;
|
|
|
7096 |
|
|
|
7097 |
if (curColorMode === PConstants.HSB) {
|
|
|
7098 |
var rgb = p.color.toRGB(aValue1, aValue2, aValue3);
|
|
|
7099 |
r = rgb[0];
|
|
|
7100 |
g = rgb[1];
|
|
|
7101 |
b = rgb[2];
|
|
|
7102 |
} else {
|
|
|
7103 |
r = Math.round(255 * (aValue1 / colorModeX));
|
|
|
7104 |
g = Math.round(255 * (aValue2 / colorModeY));
|
|
|
7105 |
b = Math.round(255 * (aValue3 / colorModeZ));
|
|
|
7106 |
}
|
|
|
7107 |
|
|
|
7108 |
a = Math.round(255 * (aValue4 / colorModeA));
|
|
|
7109 |
|
|
|
7110 |
// Limit values less than 0 and greater than 255
|
|
|
7111 |
r = (r < 0) ? 0 : r;
|
|
|
7112 |
g = (g < 0) ? 0 : g;
|
|
|
7113 |
b = (b < 0) ? 0 : b;
|
|
|
7114 |
a = (a < 0) ? 0 : a;
|
|
|
7115 |
r = (r > 255) ? 255 : r;
|
|
|
7116 |
g = (g > 255) ? 255 : g;
|
|
|
7117 |
b = (b > 255) ? 255 : b;
|
|
|
7118 |
a = (a > 255) ? 255 : a;
|
|
|
7119 |
|
|
|
7120 |
// Create color int
|
|
|
7121 |
return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK;
|
|
|
7122 |
}
|
|
|
7123 |
|
|
|
7124 |
function color$2(aValue1, aValue2) {
|
|
|
7125 |
var a;
|
|
|
7126 |
|
|
|
7127 |
// Color int and alpha
|
|
|
7128 |
if (aValue1 & PConstants.ALPHA_MASK) {
|
|
|
7129 |
a = Math.round(255 * (aValue2 / colorModeA));
|
|
|
7130 |
// Limit values less than 0 and greater than 255
|
|
|
7131 |
a = (a > 255) ? 255 : a;
|
|
|
7132 |
a = (a < 0) ? 0 : a;
|
|
|
7133 |
|
|
|
7134 |
return aValue1 - (aValue1 & PConstants.ALPHA_MASK) + ((a << 24) & PConstants.ALPHA_MASK);
|
|
|
7135 |
}
|
|
|
7136 |
// Grayscale and alpha
|
|
|
7137 |
if (curColorMode === PConstants.RGB) {
|
|
|
7138 |
return color$4(aValue1, aValue1, aValue1, aValue2);
|
|
|
7139 |
}
|
|
|
7140 |
if (curColorMode === PConstants.HSB) {
|
|
|
7141 |
return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, aValue2);
|
|
|
7142 |
}
|
|
|
7143 |
}
|
|
|
7144 |
|
|
|
7145 |
function color$1(aValue1) {
|
|
|
7146 |
// Grayscale
|
|
|
7147 |
if (aValue1 <= colorModeX && aValue1 >= 0) {
|
|
|
7148 |
if (curColorMode === PConstants.RGB) {
|
|
|
7149 |
return color$4(aValue1, aValue1, aValue1, colorModeA);
|
|
|
7150 |
}
|
|
|
7151 |
if (curColorMode === PConstants.HSB) {
|
|
|
7152 |
return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, colorModeA);
|
|
|
7153 |
}
|
|
|
7154 |
}
|
|
|
7155 |
// Color int
|
|
|
7156 |
if (aValue1) {
|
|
|
7157 |
if (aValue1 > 2147483647) {
|
|
|
7158 |
// Java Overflow
|
|
|
7159 |
aValue1 -= 4294967296;
|
|
|
7160 |
}
|
|
|
7161 |
return aValue1;
|
|
|
7162 |
}
|
|
|
7163 |
}
|
|
|
7164 |
|
|
|
7165 |
/**
|
|
|
7166 |
* Creates colors for storing in variables of the color datatype. The parameters are
|
|
|
7167 |
* interpreted as RGB or HSB values depending on the current colorMode(). The default
|
|
|
7168 |
* mode is RGB values from 0 to 255 and therefore, the function call color(255, 204, 0)
|
|
|
7169 |
* will return a bright yellow color. More about how colors are stored can be found in
|
|
|
7170 |
* the reference for the color datatype.
|
|
|
7171 |
*
|
|
|
7172 |
* @param {int|float} aValue1 red or hue or grey values relative to the current color range.
|
|
|
7173 |
* Also can be color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
|
|
|
7174 |
* @param {int|float} aValue2 green or saturation values relative to the current color range
|
|
|
7175 |
* @param {int|float} aValue3 blue or brightness values relative to the current color range
|
|
|
7176 |
* @param {int|float} aValue4 relative to current color range. Represents alpha
|
|
|
7177 |
*
|
|
|
7178 |
* @returns {color} the color
|
|
|
7179 |
*
|
|
|
7180 |
* @see colorMode
|
|
|
7181 |
*/
|
|
|
7182 |
p.color = function(aValue1, aValue2, aValue3, aValue4) {
|
|
|
7183 |
|
|
|
7184 |
// 4 arguments: (R, G, B, A) or (H, S, B, A)
|
|
|
7185 |
if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef && aValue4 !== undef) {
|
|
|
7186 |
return color$4(aValue1, aValue2, aValue3, aValue4);
|
|
|
7187 |
}
|
|
|
7188 |
|
|
|
7189 |
// 3 arguments: (R, G, B) or (H, S, B)
|
|
|
7190 |
if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef) {
|
|
|
7191 |
return color$4(aValue1, aValue2, aValue3, colorModeA);
|
|
|
7192 |
}
|
|
|
7193 |
|
|
|
7194 |
// 2 arguments: (Color, A) or (Grayscale, A)
|
|
|
7195 |
if (aValue1 !== undef && aValue2 !== undef) {
|
|
|
7196 |
return color$2(aValue1, aValue2);
|
|
|
7197 |
}
|
|
|
7198 |
|
|
|
7199 |
// 1 argument: (Grayscale) or (Color)
|
|
|
7200 |
if (typeof aValue1 === "number") {
|
|
|
7201 |
return color$1(aValue1);
|
|
|
7202 |
}
|
|
|
7203 |
|
|
|
7204 |
// Default
|
|
|
7205 |
return color$4(colorModeX, colorModeY, colorModeZ, colorModeA);
|
|
|
7206 |
};
|
|
|
7207 |
|
|
|
7208 |
// Ease of use function to extract the colour bits into a string
|
|
|
7209 |
p.color.toString = function(colorInt) {
|
|
|
7210 |
return "rgba(" + ((colorInt & PConstants.RED_MASK) >>> 16) + "," + ((colorInt & PConstants.GREEN_MASK) >>> 8) +
|
|
|
7211 |
"," + ((colorInt & PConstants.BLUE_MASK)) + "," + ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255 + ")";
|
|
|
7212 |
};
|
|
|
7213 |
|
|
|
7214 |
// Easy of use function to pack rgba values into a single bit-shifted color int.
|
|
|
7215 |
p.color.toInt = function(r, g, b, a) {
|
|
|
7216 |
return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK;
|
|
|
7217 |
};
|
|
|
7218 |
|
|
|
7219 |
// Creates a simple array in [R, G, B, A] format, [255, 255, 255, 255]
|
|
|
7220 |
p.color.toArray = function(colorInt) {
|
|
|
7221 |
return [(colorInt & PConstants.RED_MASK) >>> 16, (colorInt & PConstants.GREEN_MASK) >>> 8,
|
|
|
7222 |
colorInt & PConstants.BLUE_MASK, (colorInt & PConstants.ALPHA_MASK) >>> 24];
|
|
|
7223 |
};
|
|
|
7224 |
|
|
|
7225 |
// Creates a WebGL color array in [R, G, B, A] format. WebGL wants the color ranges between 0 and 1, [1, 1, 1, 1]
|
|
|
7226 |
p.color.toGLArray = function(colorInt) {
|
|
|
7227 |
return [((colorInt & PConstants.RED_MASK) >>> 16) / 255, ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255,
|
|
|
7228 |
(colorInt & PConstants.BLUE_MASK) / 255, ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255];
|
|
|
7229 |
};
|
|
|
7230 |
|
|
|
7231 |
// HSB conversion function from Mootools, MIT Licensed
|
|
|
7232 |
p.color.toRGB = function(h, s, b) {
|
|
|
7233 |
// Limit values greater than range
|
|
|
7234 |
h = (h > colorModeX) ? colorModeX : h;
|
|
|
7235 |
s = (s > colorModeY) ? colorModeY : s;
|
|
|
7236 |
b = (b > colorModeZ) ? colorModeZ : b;
|
|
|
7237 |
|
|
|
7238 |
h = (h / colorModeX) * 360;
|
|
|
7239 |
s = (s / colorModeY) * 100;
|
|
|
7240 |
b = (b / colorModeZ) * 100;
|
|
|
7241 |
|
|
|
7242 |
var br = Math.round(b / 100 * 255);
|
|
|
7243 |
|
|
|
7244 |
if (s === 0) { // Grayscale
|
|
|
7245 |
return [br, br, br];
|
|
|
7246 |
}
|
|
|
7247 |
var hue = h % 360;
|
|
|
7248 |
var f = hue % 60;
|
|
|
7249 |
var p = Math.round((b * (100 - s)) / 10000 * 255);
|
|
|
7250 |
var q = Math.round((b * (6000 - s * f)) / 600000 * 255);
|
|
|
7251 |
var t = Math.round((b * (6000 - s * (60 - f))) / 600000 * 255);
|
|
|
7252 |
switch (Math.floor(hue / 60)) {
|
|
|
7253 |
case 0:
|
|
|
7254 |
return [br, t, p];
|
|
|
7255 |
case 1:
|
|
|
7256 |
return [q, br, p];
|
|
|
7257 |
case 2:
|
|
|
7258 |
return [p, br, t];
|
|
|
7259 |
case 3:
|
|
|
7260 |
return [p, q, br];
|
|
|
7261 |
case 4:
|
|
|
7262 |
return [t, p, br];
|
|
|
7263 |
case 5:
|
|
|
7264 |
return [br, p, q];
|
|
|
7265 |
}
|
|
|
7266 |
};
|
|
|
7267 |
|
|
|
7268 |
function colorToHSB(colorInt) {
|
|
|
7269 |
var red, green, blue;
|
|
|
7270 |
|
|
|
7271 |
red = ((colorInt & PConstants.RED_MASK) >>> 16) / 255;
|
|
|
7272 |
green = ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255;
|
|
|
7273 |
blue = (colorInt & PConstants.BLUE_MASK) / 255;
|
|
|
7274 |
|
|
|
7275 |
var max = p.max(p.max(red,green), blue),
|
|
|
7276 |
min = p.min(p.min(red,green), blue),
|
|
|
7277 |
hue, saturation;
|
|
|
7278 |
|
|
|
7279 |
if (min === max) {
|
|
|
7280 |
return [0, 0, max*colorModeZ];
|
|
|
7281 |
}
|
|
|
7282 |
saturation = (max - min) / max;
|
|
|
7283 |
|
|
|
7284 |
if (red === max) {
|
|
|
7285 |
hue = (green - blue) / (max - min);
|
|
|
7286 |
} else if (green === max) {
|
|
|
7287 |
hue = 2 + ((blue - red) / (max - min));
|
|
|
7288 |
} else {
|
|
|
7289 |
hue = 4 + ((red - green) / (max - min));
|
|
|
7290 |
}
|
|
|
7291 |
|
|
|
7292 |
hue /= 6;
|
|
|
7293 |
|
|
|
7294 |
if (hue < 0) {
|
|
|
7295 |
hue += 1;
|
|
|
7296 |
} else if (hue > 1) {
|
|
|
7297 |
hue -= 1;
|
|
|
7298 |
}
|
|
|
7299 |
return [hue*colorModeX, saturation*colorModeY, max*colorModeZ];
|
|
|
7300 |
}
|
|
|
7301 |
|
|
|
7302 |
/**
|
|
|
7303 |
* Extracts the brightness value from a color.
|
|
|
7304 |
*
|
|
|
7305 |
* @param {color} colInt any value of the color datatype
|
|
|
7306 |
*
|
|
|
7307 |
* @returns {float} The brightness color value.
|
|
|
7308 |
*
|
|
|
7309 |
* @see red
|
|
|
7310 |
* @see green
|
|
|
7311 |
* @see blue
|
|
|
7312 |
* @see hue
|
|
|
7313 |
* @see saturation
|
|
|
7314 |
*/
|
|
|
7315 |
p.brightness = function(colInt){
|
|
|
7316 |
return colorToHSB(colInt)[2];
|
|
|
7317 |
};
|
|
|
7318 |
|
|
|
7319 |
/**
|
|
|
7320 |
* Extracts the saturation value from a color.
|
|
|
7321 |
*
|
|
|
7322 |
* @param {color} colInt any value of the color datatype
|
|
|
7323 |
*
|
|
|
7324 |
* @returns {float} The saturation color value.
|
|
|
7325 |
*
|
|
|
7326 |
* @see red
|
|
|
7327 |
* @see green
|
|
|
7328 |
* @see blue
|
|
|
7329 |
* @see hue
|
|
|
7330 |
* @see brightness
|
|
|
7331 |
*/
|
|
|
7332 |
p.saturation = function(colInt){
|
|
|
7333 |
return colorToHSB(colInt)[1];
|
|
|
7334 |
};
|
|
|
7335 |
|
|
|
7336 |
/**
|
|
|
7337 |
* Extracts the hue value from a color.
|
|
|
7338 |
*
|
|
|
7339 |
* @param {color} colInt any value of the color datatype
|
|
|
7340 |
*
|
|
|
7341 |
* @returns {float} The hue color value.
|
|
|
7342 |
*
|
|
|
7343 |
* @see red
|
|
|
7344 |
* @see green
|
|
|
7345 |
* @see blue
|
|
|
7346 |
* @see saturation
|
|
|
7347 |
* @see brightness
|
|
|
7348 |
*/
|
|
|
7349 |
p.hue = function(colInt){
|
|
|
7350 |
return colorToHSB(colInt)[0];
|
|
|
7351 |
};
|
|
|
7352 |
|
|
|
7353 |
/**
|
|
|
7354 |
* Extracts the red value from a color, scaled to match current colorMode().
|
|
|
7355 |
* This value is always returned as a float so be careful not to assign it to an int value.
|
|
|
7356 |
*
|
|
|
7357 |
* @param {color} aColor any value of the color datatype
|
|
|
7358 |
*
|
|
|
7359 |
* @returns {float} The red color value.
|
|
|
7360 |
*
|
|
|
7361 |
* @see green
|
|
|
7362 |
* @see blue
|
|
|
7363 |
* @see alpha
|
|
|
7364 |
* @see >> right shift
|
|
|
7365 |
* @see hue
|
|
|
7366 |
* @see saturation
|
|
|
7367 |
* @see brightness
|
|
|
7368 |
*/
|
|
|
7369 |
p.red = function(aColor) {
|
|
|
7370 |
return ((aColor & PConstants.RED_MASK) >>> 16) / 255 * colorModeX;
|
|
|
7371 |
};
|
|
|
7372 |
|
|
|
7373 |
/**
|
|
|
7374 |
* Extracts the green value from a color, scaled to match current colorMode().
|
|
|
7375 |
* This value is always returned as a float so be careful not to assign it to an int value.
|
|
|
7376 |
*
|
|
|
7377 |
* @param {color} aColor any value of the color datatype
|
|
|
7378 |
*
|
|
|
7379 |
* @returns {float} The green color value.
|
|
|
7380 |
*
|
|
|
7381 |
* @see red
|
|
|
7382 |
* @see blue
|
|
|
7383 |
* @see alpha
|
|
|
7384 |
* @see >> right shift
|
|
|
7385 |
* @see hue
|
|
|
7386 |
* @see saturation
|
|
|
7387 |
* @see brightness
|
|
|
7388 |
*/
|
|
|
7389 |
p.green = function(aColor) {
|
|
|
7390 |
return ((aColor & PConstants.GREEN_MASK) >>> 8) / 255 * colorModeY;
|
|
|
7391 |
};
|
|
|
7392 |
|
|
|
7393 |
/**
|
|
|
7394 |
* Extracts the blue value from a color, scaled to match current colorMode().
|
|
|
7395 |
* This value is always returned as a float so be careful not to assign it to an int value.
|
|
|
7396 |
*
|
|
|
7397 |
* @param {color} aColor any value of the color datatype
|
|
|
7398 |
*
|
|
|
7399 |
* @returns {float} The blue color value.
|
|
|
7400 |
*
|
|
|
7401 |
* @see red
|
|
|
7402 |
* @see green
|
|
|
7403 |
* @see alpha
|
|
|
7404 |
* @see >> right shift
|
|
|
7405 |
* @see hue
|
|
|
7406 |
* @see saturation
|
|
|
7407 |
* @see brightness
|
|
|
7408 |
*/
|
|
|
7409 |
p.blue = function(aColor) {
|
|
|
7410 |
return (aColor & PConstants.BLUE_MASK) / 255 * colorModeZ;
|
|
|
7411 |
};
|
|
|
7412 |
|
|
|
7413 |
/**
|
|
|
7414 |
* Extracts the alpha value from a color, scaled to match current colorMode().
|
|
|
7415 |
* This value is always returned as a float so be careful not to assign it to an int value.
|
|
|
7416 |
*
|
|
|
7417 |
* @param {color} aColor any value of the color datatype
|
|
|
7418 |
*
|
|
|
7419 |
* @returns {float} The alpha color value.
|
|
|
7420 |
*
|
|
|
7421 |
* @see red
|
|
|
7422 |
* @see green
|
|
|
7423 |
* @see blue
|
|
|
7424 |
* @see >> right shift
|
|
|
7425 |
* @see hue
|
|
|
7426 |
* @see saturation
|
|
|
7427 |
* @see brightness
|
|
|
7428 |
*/
|
|
|
7429 |
p.alpha = function(aColor) {
|
|
|
7430 |
return ((aColor & PConstants.ALPHA_MASK) >>> 24) / 255 * colorModeA;
|
|
|
7431 |
};
|
|
|
7432 |
|
|
|
7433 |
/**
|
|
|
7434 |
* Calculates a color or colors between two colors at a specific increment.
|
|
|
7435 |
* The amt parameter is the amount to interpolate between the two values where 0.0
|
|
|
7436 |
* equal to the first point, 0.1 is very near the first point, 0.5 is half-way in between, etc.
|
|
|
7437 |
*
|
|
|
7438 |
* @param {color} c1 interpolate from this color
|
|
|
7439 |
* @param {color} c2 interpolate to this color
|
|
|
7440 |
* @param {float} amt between 0.0 and 1.0
|
|
|
7441 |
*
|
|
|
7442 |
* @returns {float} The blended color.
|
|
|
7443 |
*
|
|
|
7444 |
* @see blendColor
|
|
|
7445 |
* @see color
|
|
|
7446 |
*/
|
|
|
7447 |
p.lerpColor = function(c1, c2, amt) {
|
|
|
7448 |
var r, g, b, a, r1, g1, b1, a1, r2, g2, b2, a2;
|
|
|
7449 |
var hsb1, hsb2, rgb, h, s;
|
|
|
7450 |
var colorBits1 = p.color(c1);
|
|
|
7451 |
var colorBits2 = p.color(c2);
|
|
|
7452 |
|
|
|
7453 |
if (curColorMode === PConstants.HSB) {
|
|
|
7454 |
// Special processing for HSB mode.
|
|
|
7455 |
// Get HSB and Alpha values for Color 1 and 2
|
|
|
7456 |
hsb1 = colorToHSB(colorBits1);
|
|
|
7457 |
a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
|
|
|
7458 |
hsb2 = colorToHSB(colorBits2);
|
|
|
7459 |
a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
|
|
|
7460 |
|
|
|
7461 |
// Return lerp value for each channel, for HSB components
|
|
|
7462 |
h = p.lerp(hsb1[0], hsb2[0], amt);
|
|
|
7463 |
s = p.lerp(hsb1[1], hsb2[1], amt);
|
|
|
7464 |
b = p.lerp(hsb1[2], hsb2[2], amt);
|
|
|
7465 |
rgb = p.color.toRGB(h, s, b);
|
|
|
7466 |
// ... and for Alpha-range
|
|
|
7467 |
a = p.lerp(a1, a2, amt) * colorModeA;
|
|
|
7468 |
|
|
|
7469 |
return (a << 24) & PConstants.ALPHA_MASK |
|
|
|
7470 |
(rgb[0] << 16) & PConstants.RED_MASK |
|
|
|
7471 |
(rgb[1] << 8) & PConstants.GREEN_MASK |
|
|
|
7472 |
rgb[2] & PConstants.BLUE_MASK;
|
|
|
7473 |
}
|
|
|
7474 |
|
|
|
7475 |
// Get RGBA values for Color 1 to floats
|
|
|
7476 |
r1 = (colorBits1 & PConstants.RED_MASK) >>> 16;
|
|
|
7477 |
g1 = (colorBits1 & PConstants.GREEN_MASK) >>> 8;
|
|
|
7478 |
b1 = (colorBits1 & PConstants.BLUE_MASK);
|
|
|
7479 |
a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
|
|
|
7480 |
|
|
|
7481 |
// Get RGBA values for Color 2 to floats
|
|
|
7482 |
r2 = (colorBits2 & PConstants.RED_MASK) >>> 16;
|
|
|
7483 |
g2 = (colorBits2 & PConstants.GREEN_MASK) >>> 8;
|
|
|
7484 |
b2 = (colorBits2 & PConstants.BLUE_MASK);
|
|
|
7485 |
a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
|
|
|
7486 |
|
|
|
7487 |
// Return lerp value for each channel, INT for color, Float for Alpha-range
|
|
|
7488 |
r = p.lerp(r1, r2, amt) | 0;
|
|
|
7489 |
g = p.lerp(g1, g2, amt) | 0;
|
|
|
7490 |
b = p.lerp(b1, b2, amt) | 0;
|
|
|
7491 |
a = p.lerp(a1, a2, amt) * colorModeA;
|
|
|
7492 |
|
|
|
7493 |
return (a << 24) & PConstants.ALPHA_MASK |
|
|
|
7494 |
(r << 16) & PConstants.RED_MASK |
|
|
|
7495 |
(g << 8) & PConstants.GREEN_MASK |
|
|
|
7496 |
b & PConstants.BLUE_MASK;
|
|
|
7497 |
};
|
|
|
7498 |
|
|
|
7499 |
/**
|
|
|
7500 |
* Changes the way Processing interprets color data. By default, fill(), stroke(), and background()
|
|
|
7501 |
* colors are set by values between 0 and 255 using the RGB color model. It is possible to change the
|
|
|
7502 |
* numerical range used for specifying colors and to switch color systems. For example, calling colorMode(RGB, 1.0)
|
|
|
7503 |
* will specify that values are specified between 0 and 1. The limits for defining colors are altered by setting the
|
|
|
7504 |
* parameters range1, range2, range3, and range 4.
|
|
|
7505 |
*
|
|
|
7506 |
* @param {MODE} mode Either RGB or HSB, corresponding to Red/Green/Blue and Hue/Saturation/Brightness
|
|
|
7507 |
* @param {int|float} range range for all color elements
|
|
|
7508 |
* @param {int|float} range1 range for the red or hue depending on the current color mode
|
|
|
7509 |
* @param {int|float} range2 range for the green or saturation depending on the current color mode
|
|
|
7510 |
* @param {int|float} range3 range for the blue or brightness depending on the current color mode
|
|
|
7511 |
* @param {int|float} range4 range for the alpha
|
|
|
7512 |
*
|
|
|
7513 |
* @returns none
|
|
|
7514 |
*
|
|
|
7515 |
* @see background
|
|
|
7516 |
* @see fill
|
|
|
7517 |
* @see stroke
|
|
|
7518 |
*/
|
|
|
7519 |
p.colorMode = function() { // mode, range1, range2, range3, range4
|
|
|
7520 |
curColorMode = arguments[0];
|
|
|
7521 |
if (arguments.length > 1) {
|
|
|
7522 |
colorModeX = arguments[1];
|
|
|
7523 |
colorModeY = arguments[2] || arguments[1];
|
|
|
7524 |
colorModeZ = arguments[3] || arguments[1];
|
|
|
7525 |
colorModeA = arguments[4] || arguments[1];
|
|
|
7526 |
}
|
|
|
7527 |
};
|
|
|
7528 |
|
|
|
7529 |
/**
|
|
|
7530 |
* Blends two color values together based on the blending mode given as the MODE parameter.
|
|
|
7531 |
* The possible modes are described in the reference for the blend() function.
|
|
|
7532 |
*
|
|
|
7533 |
* @param {color} c1 color: the first color to blend
|
|
|
7534 |
* @param {color} c2 color: the second color to blend
|
|
|
7535 |
* @param {MODE} MODE Either BLEND, ADD, SUBTRACT, DARKEST, LIGHTEST, DIFFERENCE, EXCLUSION, MULTIPLY,
|
|
|
7536 |
* SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, or BURN
|
|
|
7537 |
*
|
|
|
7538 |
* @returns {float} The blended color.
|
|
|
7539 |
*
|
|
|
7540 |
* @see blend
|
|
|
7541 |
* @see color
|
|
|
7542 |
*/
|
|
|
7543 |
p.blendColor = function(c1, c2, mode) {
|
|
|
7544 |
if (mode === PConstants.REPLACE) {
|
|
|
7545 |
return p.modes.replace(c1, c2);
|
|
|
7546 |
} else if (mode === PConstants.BLEND) {
|
|
|
7547 |
return p.modes.blend(c1, c2);
|
|
|
7548 |
} else if (mode === PConstants.ADD) {
|
|
|
7549 |
return p.modes.add(c1, c2);
|
|
|
7550 |
} else if (mode === PConstants.SUBTRACT) {
|
|
|
7551 |
return p.modes.subtract(c1, c2);
|
|
|
7552 |
} else if (mode === PConstants.LIGHTEST) {
|
|
|
7553 |
return p.modes.lightest(c1, c2);
|
|
|
7554 |
} else if (mode === PConstants.DARKEST) {
|
|
|
7555 |
return p.modes.darkest(c1, c2);
|
|
|
7556 |
} else if (mode === PConstants.DIFFERENCE) {
|
|
|
7557 |
return p.modes.difference(c1, c2);
|
|
|
7558 |
} else if (mode === PConstants.EXCLUSION) {
|
|
|
7559 |
return p.modes.exclusion(c1, c2);
|
|
|
7560 |
} else if (mode === PConstants.MULTIPLY) {
|
|
|
7561 |
return p.modes.multiply(c1, c2);
|
|
|
7562 |
} else if (mode === PConstants.SCREEN) {
|
|
|
7563 |
return p.modes.screen(c1, c2);
|
|
|
7564 |
} else if (mode === PConstants.HARD_LIGHT) {
|
|
|
7565 |
return p.modes.hard_light(c1, c2);
|
|
|
7566 |
} else if (mode === PConstants.SOFT_LIGHT) {
|
|
|
7567 |
return p.modes.soft_light(c1, c2);
|
|
|
7568 |
} else if (mode === PConstants.OVERLAY) {
|
|
|
7569 |
return p.modes.overlay(c1, c2);
|
|
|
7570 |
} else if (mode === PConstants.DODGE) {
|
|
|
7571 |
return p.modes.dodge(c1, c2);
|
|
|
7572 |
} else if (mode === PConstants.BURN) {
|
|
|
7573 |
return p.modes.burn(c1, c2);
|
|
|
7574 |
}
|
|
|
7575 |
};
|
|
|
7576 |
|
|
|
7577 |
////////////////////////////////////////////////////////////////////////////
|
|
|
7578 |
// Canvas-Matrix manipulation
|
|
|
7579 |
////////////////////////////////////////////////////////////////////////////
|
|
|
7580 |
|
|
|
7581 |
function saveContext() {
|
|
|
7582 |
curContext.save();
|
|
|
7583 |
}
|
|
|
7584 |
|
|
|
7585 |
function restoreContext() {
|
|
|
7586 |
curContext.restore();
|
|
|
7587 |
isStrokeDirty = true;
|
|
|
7588 |
isFillDirty = true;
|
|
|
7589 |
}
|
|
|
7590 |
|
|
|
7591 |
/**
|
|
|
7592 |
* Prints the current matrix to the text window.
|
|
|
7593 |
*
|
|
|
7594 |
* @returns none
|
|
|
7595 |
*
|
|
|
7596 |
* @see pushMatrix
|
|
|
7597 |
* @see popMatrix
|
|
|
7598 |
* @see resetMatrix
|
|
|
7599 |
* @see applyMatrix
|
|
|
7600 |
*/
|
|
|
7601 |
p.printMatrix = function() {
|
|
|
7602 |
modelView.print();
|
|
|
7603 |
};
|
|
|
7604 |
|
|
|
7605 |
/**
|
|
|
7606 |
* Specifies an amount to displace objects within the display window. The x parameter specifies left/right translation,
|
|
|
7607 |
* the y parameter specifies up/down translation, and the z parameter specifies translations toward/away from the screen.
|
|
|
7608 |
* Using this function with the z parameter requires using the P3D or OPENGL parameter in combination with size as shown
|
|
|
7609 |
* in the above example. Transformations apply to everything that happens after and subsequent calls to the function
|
|
|
7610 |
* accumulates the effect. For example, calling translate(50, 0) and then translate(20, 0) is the same as translate(70, 0).
|
|
|
7611 |
* If translate() is called within draw(), the transformation is reset when the loop begins again.
|
|
|
7612 |
* This function can be further controlled by the pushMatrix() and popMatrix().
|
|
|
7613 |
*
|
|
|
7614 |
* @param {int|float} x left/right translation
|
|
|
7615 |
* @param {int|float} y up/down translation
|
|
|
7616 |
* @param {int|float} z forward/back translation
|
|
|
7617 |
*
|
|
|
7618 |
* @returns none
|
|
|
7619 |
*
|
|
|
7620 |
* @see pushMatrix
|
|
|
7621 |
* @see popMatrix
|
|
|
7622 |
* @see scale
|
|
|
7623 |
* @see rotate
|
|
|
7624 |
* @see rotateX
|
|
|
7625 |
* @see rotateY
|
|
|
7626 |
* @see rotateZ
|
|
|
7627 |
*/
|
|
|
7628 |
Drawing2D.prototype.translate = function(x, y) {
|
|
|
7629 |
modelView.translate(x, y);
|
|
|
7630 |
modelViewInv.invTranslate(x, y);
|
|
|
7631 |
curContext.translate(x, y);
|
|
|
7632 |
};
|
|
|
7633 |
|
|
|
7634 |
Drawing3D.prototype.translate = function(x, y, z) {
|
|
|
7635 |
modelView.translate(x, y, z);
|
|
|
7636 |
modelViewInv.invTranslate(x, y, z);
|
|
|
7637 |
};
|
|
|
7638 |
|
|
|
7639 |
/**
|
|
|
7640 |
* Increases or decreases the size of a shape by expanding and contracting vertices. Objects always scale from their
|
|
|
7641 |
* relative origin to the coordinate system. Scale values are specified as decimal percentages. For example, the
|
|
|
7642 |
* function call scale(2.0) increases the dimension of a shape by 200%. Transformations apply to everything that
|
|
|
7643 |
* happens after and subsequent calls to the function multiply the effect. For example, calling scale(2.0) and
|
|
|
7644 |
* then scale(1.5) is the same as scale(3.0). If scale() is called within draw(), the transformation is reset when
|
|
|
7645 |
* the loop begins again. Using this fuction with the z parameter requires passing P3D or OPENGL into the size()
|
|
|
7646 |
* parameter as shown in the example above. This function can be further controlled by pushMatrix() and popMatrix().
|
|
|
7647 |
*
|
|
|
7648 |
* @param {int|float} size percentage to scale the object
|
|
|
7649 |
* @param {int|float} x percentage to scale the object in the x-axis
|
|
|
7650 |
* @param {int|float} y percentage to scale the object in the y-axis
|
|
|
7651 |
* @param {int|float} z percentage to scale the object in the z-axis
|
|
|
7652 |
*
|
|
|
7653 |
* @returns none
|
|
|
7654 |
*
|
|
|
7655 |
* @see pushMatrix
|
|
|
7656 |
* @see popMatrix
|
|
|
7657 |
* @see translate
|
|
|
7658 |
* @see rotate
|
|
|
7659 |
* @see rotateX
|
|
|
7660 |
* @see rotateY
|
|
|
7661 |
* @see rotateZ
|
|
|
7662 |
*/
|
|
|
7663 |
Drawing2D.prototype.scale = function(x, y) {
|
|
|
7664 |
modelView.scale(x, y);
|
|
|
7665 |
modelViewInv.invScale(x, y);
|
|
|
7666 |
curContext.scale(x, y || x);
|
|
|
7667 |
};
|
|
|
7668 |
|
|
|
7669 |
Drawing3D.prototype.scale = function(x, y, z) {
|
|
|
7670 |
modelView.scale(x, y, z);
|
|
|
7671 |
modelViewInv.invScale(x, y, z);
|
|
|
7672 |
};
|
|
|
7673 |
|
|
|
7674 |
/**
|
|
|
7675 |
* Pushes the current transformation matrix onto the matrix stack. Understanding pushMatrix() and popMatrix()
|
|
|
7676 |
* requires understanding the concept of a matrix stack. The pushMatrix() function saves the current coordinate
|
|
|
7677 |
* system to the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are
|
|
|
7678 |
* used in conjuction with the other transformation methods and may be embedded to control the scope of
|
|
|
7679 |
* the transformations.
|
|
|
7680 |
*
|
|
|
7681 |
* @returns none
|
|
|
7682 |
*
|
|
|
7683 |
* @see popMatrix
|
|
|
7684 |
* @see translate
|
|
|
7685 |
* @see rotate
|
|
|
7686 |
* @see rotateX
|
|
|
7687 |
* @see rotateY
|
|
|
7688 |
* @see rotateZ
|
|
|
7689 |
*/
|
|
|
7690 |
Drawing2D.prototype.pushMatrix = function() {
|
|
|
7691 |
userMatrixStack.load(modelView);
|
|
|
7692 |
userReverseMatrixStack.load(modelViewInv);
|
|
|
7693 |
saveContext();
|
|
|
7694 |
};
|
|
|
7695 |
|
|
|
7696 |
Drawing3D.prototype.pushMatrix = function() {
|
|
|
7697 |
userMatrixStack.load(modelView);
|
|
|
7698 |
userReverseMatrixStack.load(modelViewInv);
|
|
|
7699 |
};
|
|
|
7700 |
|
|
|
7701 |
/**
|
|
|
7702 |
* Pops the current transformation matrix off the matrix stack. Understanding pushing and popping requires
|
|
|
7703 |
* understanding the concept of a matrix stack. The pushMatrix() function saves the current coordinate system to
|
|
|
7704 |
* the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are used in
|
|
|
7705 |
* conjuction with the other transformation methods and may be embedded to control the scope of the transformations.
|
|
|
7706 |
*
|
|
|
7707 |
* @returns none
|
|
|
7708 |
*
|
|
|
7709 |
* @see popMatrix
|
|
|
7710 |
* @see pushMatrix
|
|
|
7711 |
*/
|
|
|
7712 |
Drawing2D.prototype.popMatrix = function() {
|
|
|
7713 |
modelView.set(userMatrixStack.pop());
|
|
|
7714 |
modelViewInv.set(userReverseMatrixStack.pop());
|
|
|
7715 |
restoreContext();
|
|
|
7716 |
};
|
|
|
7717 |
|
|
|
7718 |
Drawing3D.prototype.popMatrix = function() {
|
|
|
7719 |
modelView.set(userMatrixStack.pop());
|
|
|
7720 |
modelViewInv.set(userReverseMatrixStack.pop());
|
|
|
7721 |
};
|
|
|
7722 |
|
|
|
7723 |
/**
|
|
|
7724 |
* Replaces the current matrix with the identity matrix. The equivalent function in OpenGL is glLoadIdentity().
|
|
|
7725 |
*
|
|
|
7726 |
* @returns none
|
|
|
7727 |
*
|
|
|
7728 |
* @see popMatrix
|
|
|
7729 |
* @see pushMatrix
|
|
|
7730 |
* @see applyMatrix
|
|
|
7731 |
* @see printMatrix
|
|
|
7732 |
*/
|
|
|
7733 |
Drawing2D.prototype.resetMatrix = function() {
|
|
|
7734 |
modelView.reset();
|
|
|
7735 |
modelViewInv.reset();
|
|
|
7736 |
curContext.setTransform(1,0,0,1,0,0);
|
|
|
7737 |
};
|
|
|
7738 |
|
|
|
7739 |
Drawing3D.prototype.resetMatrix = function() {
|
|
|
7740 |
modelView.reset();
|
|
|
7741 |
modelViewInv.reset();
|
|
|
7742 |
};
|
|
|
7743 |
|
|
|
7744 |
/**
|
|
|
7745 |
* Multiplies the current matrix by the one specified through the parameters. This is very slow because it will
|
|
|
7746 |
* try to calculate the inverse of the transform, so avoid it whenever possible. The equivalent function
|
|
|
7747 |
* in OpenGL is glMultMatrix().
|
|
|
7748 |
*
|
|
|
7749 |
* @param {int|float} n00-n15 numbers which define the 4x4 matrix to be multiplied
|
|
|
7750 |
*
|
|
|
7751 |
* @returns none
|
|
|
7752 |
*
|
|
|
7753 |
* @see popMatrix
|
|
|
7754 |
* @see pushMatrix
|
|
|
7755 |
* @see resetMatrix
|
|
|
7756 |
* @see printMatrix
|
|
|
7757 |
*/
|
|
|
7758 |
DrawingShared.prototype.applyMatrix = function() {
|
|
|
7759 |
var a = arguments;
|
|
|
7760 |
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]);
|
|
|
7761 |
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]);
|
|
|
7762 |
};
|
|
|
7763 |
|
|
|
7764 |
Drawing2D.prototype.applyMatrix = function() {
|
|
|
7765 |
var a = arguments;
|
|
|
7766 |
for (var cnt = a.length; cnt < 16; cnt++) {
|
|
|
7767 |
a[cnt] = 0;
|
|
|
7768 |
}
|
|
|
7769 |
a[10] = a[15] = 1;
|
|
|
7770 |
DrawingShared.prototype.applyMatrix.apply(this, a);
|
|
|
7771 |
};
|
|
|
7772 |
|
|
|
7773 |
/**
|
|
|
7774 |
* Rotates a shape around the x-axis the amount specified by the angle parameter. Angles should be
|
|
|
7775 |
* specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
|
|
|
7776 |
* Objects are always rotated around their relative position to the origin and positive numbers
|
|
|
7777 |
* rotate objects in a counterclockwise direction. Transformations apply to everything that happens
|
|
|
7778 |
* after and subsequent calls to the function accumulates the effect. For example, calling rotateX(PI/2)
|
|
|
7779 |
* and then rotateX(PI/2) is the same as rotateX(PI). If rotateX() is called within the draw(), the
|
|
|
7780 |
* transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
|
|
|
7781 |
* into the size() parameter as shown in the example above.
|
|
|
7782 |
*
|
|
|
7783 |
* @param {int|float} angleInRadians angle of rotation specified in radians
|
|
|
7784 |
*
|
|
|
7785 |
* @returns none
|
|
|
7786 |
*
|
|
|
7787 |
* @see rotateY
|
|
|
7788 |
* @see rotateZ
|
|
|
7789 |
* @see rotate
|
|
|
7790 |
* @see translate
|
|
|
7791 |
* @see scale
|
|
|
7792 |
* @see popMatrix
|
|
|
7793 |
* @see pushMatrix
|
|
|
7794 |
*/
|
|
|
7795 |
p.rotateX = function(angleInRadians) {
|
|
|
7796 |
modelView.rotateX(angleInRadians);
|
|
|
7797 |
modelViewInv.invRotateX(angleInRadians);
|
|
|
7798 |
};
|
|
|
7799 |
|
|
|
7800 |
/**
|
|
|
7801 |
* Rotates a shape around the z-axis the amount specified by the angle parameter. Angles should be
|
|
|
7802 |
* specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
|
|
|
7803 |
* Objects are always rotated around their relative position to the origin and positive numbers
|
|
|
7804 |
* rotate objects in a counterclockwise direction. Transformations apply to everything that happens
|
|
|
7805 |
* after and subsequent calls to the function accumulates the effect. For example, calling rotateZ(PI/2)
|
|
|
7806 |
* and then rotateZ(PI/2) is the same as rotateZ(PI). If rotateZ() is called within the draw(), the
|
|
|
7807 |
* transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
|
|
|
7808 |
* into the size() parameter as shown in the example above.
|
|
|
7809 |
*
|
|
|
7810 |
* @param {int|float} angleInRadians angle of rotation specified in radians
|
|
|
7811 |
*
|
|
|
7812 |
* @returns none
|
|
|
7813 |
*
|
|
|
7814 |
* @see rotateX
|
|
|
7815 |
* @see rotateY
|
|
|
7816 |
* @see rotate
|
|
|
7817 |
* @see translate
|
|
|
7818 |
* @see scale
|
|
|
7819 |
* @see popMatrix
|
|
|
7820 |
* @see pushMatrix
|
|
|
7821 |
*/
|
|
|
7822 |
Drawing2D.prototype.rotateZ = function() {
|
|
|
7823 |
throw "rotateZ() is not supported in 2D mode. Use rotate(float) instead.";
|
|
|
7824 |
};
|
|
|
7825 |
|
|
|
7826 |
Drawing3D.prototype.rotateZ = function(angleInRadians) {
|
|
|
7827 |
modelView.rotateZ(angleInRadians);
|
|
|
7828 |
modelViewInv.invRotateZ(angleInRadians);
|
|
|
7829 |
};
|
|
|
7830 |
|
|
|
7831 |
/**
|
|
|
7832 |
* Rotates a shape around the y-axis the amount specified by the angle parameter. Angles should be
|
|
|
7833 |
* specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
|
|
|
7834 |
* Objects are always rotated around their relative position to the origin and positive numbers
|
|
|
7835 |
* rotate objects in a counterclockwise direction. Transformations apply to everything that happens
|
|
|
7836 |
* after and subsequent calls to the function accumulates the effect. For example, calling rotateY(PI/2)
|
|
|
7837 |
* and then rotateY(PI/2) is the same as rotateY(PI). If rotateY() is called within the draw(), the
|
|
|
7838 |
* transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
|
|
|
7839 |
* into the size() parameter as shown in the example above.
|
|
|
7840 |
*
|
|
|
7841 |
* @param {int|float} angleInRadians angle of rotation specified in radians
|
|
|
7842 |
*
|
|
|
7843 |
* @returns none
|
|
|
7844 |
*
|
|
|
7845 |
* @see rotateX
|
|
|
7846 |
* @see rotateZ
|
|
|
7847 |
* @see rotate
|
|
|
7848 |
* @see translate
|
|
|
7849 |
* @see scale
|
|
|
7850 |
* @see popMatrix
|
|
|
7851 |
* @see pushMatrix
|
|
|
7852 |
*/
|
|
|
7853 |
p.rotateY = function(angleInRadians) {
|
|
|
7854 |
modelView.rotateY(angleInRadians);
|
|
|
7855 |
modelViewInv.invRotateY(angleInRadians);
|
|
|
7856 |
};
|
|
|
7857 |
|
|
|
7858 |
/**
|
|
|
7859 |
* Rotates a shape the amount specified by the angle parameter. Angles should be specified in radians
|
|
|
7860 |
* (values from 0 to TWO_PI) or converted to radians with the radians() function. Objects are always
|
|
|
7861 |
* rotated around their relative position to the origin and positive numbers rotate objects in a
|
|
|
7862 |
* clockwise direction. Transformations apply to everything that happens after and subsequent calls
|
|
|
7863 |
* to the function accumulates the effect. For example, calling rotate(HALF_PI) and then rotate(HALF_PI)
|
|
|
7864 |
* is the same as rotate(PI). All tranformations are reset when draw() begins again. Technically,
|
|
|
7865 |
* rotate() multiplies the current transformation matrix by a rotation matrix. This function can be
|
|
|
7866 |
* further controlled by the pushMatrix() and popMatrix().
|
|
|
7867 |
*
|
|
|
7868 |
* @param {int|float} angleInRadians angle of rotation specified in radians
|
|
|
7869 |
*
|
|
|
7870 |
* @returns none
|
|
|
7871 |
*
|
|
|
7872 |
* @see rotateX
|
|
|
7873 |
* @see rotateY
|
|
|
7874 |
* @see rotateZ
|
|
|
7875 |
* @see rotate
|
|
|
7876 |
* @see translate
|
|
|
7877 |
* @see scale
|
|
|
7878 |
* @see popMatrix
|
|
|
7879 |
* @see pushMatrix
|
|
|
7880 |
*/
|
|
|
7881 |
Drawing2D.prototype.rotate = function(angleInRadians) {
|
|
|
7882 |
modelView.rotateZ(angleInRadians);
|
|
|
7883 |
modelViewInv.invRotateZ(angleInRadians);
|
|
|
7884 |
curContext.rotate(angleInRadians);
|
|
|
7885 |
};
|
|
|
7886 |
|
|
|
7887 |
Drawing3D.prototype.rotate = function(angleInRadians) {
|
|
|
7888 |
p.rotateZ(angleInRadians);
|
|
|
7889 |
};
|
|
|
7890 |
|
|
|
7891 |
/**
|
|
|
7892 |
* The pushStyle() function saves the current style settings and popStyle() restores the prior settings.
|
|
|
7893 |
* Note that these functions are always used together. They allow you to change the style settings and later
|
|
|
7894 |
* return to what you had. When a new style is started with pushStyle(), it builds on the current style information.
|
|
|
7895 |
* The pushStyle() and popStyle() functions can be embedded to provide more control (see the second example
|
|
|
7896 |
* above for a demonstration.)
|
|
|
7897 |
* The style information controlled by the following functions are included in the style: fill(), stroke(), tint(),
|
|
|
7898 |
* strokeWeight(), strokeCap(), strokeJoin(), imageMode(), rectMode(), ellipseMode(), shapeMode(), colorMode(),
|
|
|
7899 |
* textAlign(), textFont(), textMode(), textSize(), textLeading(), emissive(), specular(), shininess(), ambient()
|
|
|
7900 |
*
|
|
|
7901 |
* @returns none
|
|
|
7902 |
*
|
|
|
7903 |
* @see popStyle
|
|
|
7904 |
*/
|
|
|
7905 |
p.pushStyle = function() {
|
|
|
7906 |
// Save the canvas state.
|
|
|
7907 |
saveContext();
|
|
|
7908 |
|
|
|
7909 |
p.pushMatrix();
|
|
|
7910 |
|
|
|
7911 |
var newState = {
|
|
|
7912 |
'doFill': doFill,
|
|
|
7913 |
'currentFillColor': currentFillColor,
|
|
|
7914 |
'doStroke': doStroke,
|
|
|
7915 |
'currentStrokeColor': currentStrokeColor,
|
|
|
7916 |
'curTint': curTint,
|
|
|
7917 |
'curRectMode': curRectMode,
|
|
|
7918 |
'curColorMode': curColorMode,
|
|
|
7919 |
'colorModeX': colorModeX,
|
|
|
7920 |
'colorModeZ': colorModeZ,
|
|
|
7921 |
'colorModeY': colorModeY,
|
|
|
7922 |
'colorModeA': colorModeA,
|
|
|
7923 |
'curTextFont': curTextFont,
|
|
|
7924 |
'horizontalTextAlignment': horizontalTextAlignment,
|
|
|
7925 |
'verticalTextAlignment': verticalTextAlignment,
|
|
|
7926 |
'textMode': textMode,
|
|
|
7927 |
'curFontName': curFontName,
|
|
|
7928 |
'curTextSize': curTextSize,
|
|
|
7929 |
'curTextAscent': curTextAscent,
|
|
|
7930 |
'curTextDescent': curTextDescent,
|
|
|
7931 |
'curTextLeading': curTextLeading
|
|
|
7932 |
};
|
|
|
7933 |
|
|
|
7934 |
styleArray.push(newState);
|
|
|
7935 |
};
|
|
|
7936 |
|
|
|
7937 |
/**
|
|
|
7938 |
* The pushStyle() function saves the current style settings and popStyle() restores the prior settings; these
|
|
|
7939 |
* functions are always used together. They allow you to change the style settings and later return to what you had.
|
|
|
7940 |
* When a new style is started with pushStyle(), it builds on the current style information. The pushStyle() and
|
|
|
7941 |
* popStyle() functions can be embedded to provide more control (see the second example above for a demonstration.)
|
|
|
7942 |
*
|
|
|
7943 |
* @returns none
|
|
|
7944 |
*
|
|
|
7945 |
* @see pushStyle
|
|
|
7946 |
*/
|
|
|
7947 |
p.popStyle = function() {
|
|
|
7948 |
var oldState = styleArray.pop();
|
|
|
7949 |
|
|
|
7950 |
if (oldState) {
|
|
|
7951 |
restoreContext();
|
|
|
7952 |
|
|
|
7953 |
p.popMatrix();
|
|
|
7954 |
|
|
|
7955 |
doFill = oldState.doFill;
|
|
|
7956 |
currentFillColor = oldState.currentFillColor;
|
|
|
7957 |
doStroke = oldState.doStroke;
|
|
|
7958 |
currentStrokeColor = oldState.currentStrokeColor;
|
|
|
7959 |
curTint = oldState.curTint;
|
|
|
7960 |
curRectMode = oldState.curRectmode;
|
|
|
7961 |
curColorMode = oldState.curColorMode;
|
|
|
7962 |
colorModeX = oldState.colorModeX;
|
|
|
7963 |
colorModeZ = oldState.colorModeZ;
|
|
|
7964 |
colorModeY = oldState.colorModeY;
|
|
|
7965 |
colorModeA = oldState.colorModeA;
|
|
|
7966 |
curTextFont = oldState.curTextFont;
|
|
|
7967 |
curFontName = oldState.curFontName;
|
|
|
7968 |
curTextSize = oldState.curTextSize;
|
|
|
7969 |
horizontalTextAlignment = oldState.horizontalTextAlignment;
|
|
|
7970 |
verticalTextAlignment = oldState.verticalTextAlignment;
|
|
|
7971 |
textMode = oldState.textMode;
|
|
|
7972 |
curTextAscent = oldState.curTextAscent;
|
|
|
7973 |
curTextDescent = oldState.curTextDescent;
|
|
|
7974 |
curTextLeading = oldState.curTextLeading;
|
|
|
7975 |
} else {
|
|
|
7976 |
throw "Too many popStyle() without enough pushStyle()";
|
|
|
7977 |
}
|
|
|
7978 |
};
|
|
|
7979 |
|
|
|
7980 |
////////////////////////////////////////////////////////////////////////////
|
|
|
7981 |
// Time based functions
|
|
|
7982 |
////////////////////////////////////////////////////////////////////////////
|
|
|
7983 |
|
|
|
7984 |
/**
|
|
|
7985 |
* Processing communicates with the clock on your computer.
|
|
|
7986 |
* The year() function returns the current year as an integer (2003, 2004, 2005, etc).
|
|
|
7987 |
*
|
|
|
7988 |
* @returns {float} The current year.
|
|
|
7989 |
*
|
|
|
7990 |
* @see millis
|
|
|
7991 |
* @see second
|
|
|
7992 |
* @see minute
|
|
|
7993 |
* @see hour
|
|
|
7994 |
* @see day
|
|
|
7995 |
* @see month
|
|
|
7996 |
*/
|
|
|
7997 |
p.year = function() {
|
|
|
7998 |
return new Date().getFullYear();
|
|
|
7999 |
};
|
|
|
8000 |
/**
|
|
|
8001 |
* Processing communicates with the clock on your computer.
|
|
|
8002 |
* The month() function returns the current month as a value from 1 - 12.
|
|
|
8003 |
*
|
|
|
8004 |
* @returns {float} The current month.
|
|
|
8005 |
*
|
|
|
8006 |
* @see millis
|
|
|
8007 |
* @see second
|
|
|
8008 |
* @see minute
|
|
|
8009 |
* @see hour
|
|
|
8010 |
* @see day
|
|
|
8011 |
* @see year
|
|
|
8012 |
*/
|
|
|
8013 |
p.month = function() {
|
|
|
8014 |
return new Date().getMonth() + 1;
|
|
|
8015 |
};
|
|
|
8016 |
/**
|
|
|
8017 |
* Processing communicates with the clock on your computer.
|
|
|
8018 |
* The day() function returns the current day as a value from 1 - 31.
|
|
|
8019 |
*
|
|
|
8020 |
* @returns {float} The current day.
|
|
|
8021 |
*
|
|
|
8022 |
* @see millis
|
|
|
8023 |
* @see second
|
|
|
8024 |
* @see minute
|
|
|
8025 |
* @see hour
|
|
|
8026 |
* @see month
|
|
|
8027 |
* @see year
|
|
|
8028 |
*/
|
|
|
8029 |
p.day = function() {
|
|
|
8030 |
return new Date().getDate();
|
|
|
8031 |
};
|
|
|
8032 |
/**
|
|
|
8033 |
* Processing communicates with the clock on your computer.
|
|
|
8034 |
* The hour() function returns the current hour as a value from 0 - 23.
|
|
|
8035 |
*
|
|
|
8036 |
* @returns {float} The current hour.
|
|
|
8037 |
*
|
|
|
8038 |
* @see millis
|
|
|
8039 |
* @see second
|
|
|
8040 |
* @see minute
|
|
|
8041 |
* @see month
|
|
|
8042 |
* @see day
|
|
|
8043 |
* @see year
|
|
|
8044 |
*/
|
|
|
8045 |
p.hour = function() {
|
|
|
8046 |
return new Date().getHours();
|
|
|
8047 |
};
|
|
|
8048 |
/**
|
|
|
8049 |
* Processing communicates with the clock on your computer.
|
|
|
8050 |
* The minute() function returns the current minute as a value from 0 - 59.
|
|
|
8051 |
*
|
|
|
8052 |
* @returns {float} The current minute.
|
|
|
8053 |
*
|
|
|
8054 |
* @see millis
|
|
|
8055 |
* @see second
|
|
|
8056 |
* @see month
|
|
|
8057 |
* @see hour
|
|
|
8058 |
* @see day
|
|
|
8059 |
* @see year
|
|
|
8060 |
*/
|
|
|
8061 |
p.minute = function() {
|
|
|
8062 |
return new Date().getMinutes();
|
|
|
8063 |
};
|
|
|
8064 |
/**
|
|
|
8065 |
* Processing communicates with the clock on your computer.
|
|
|
8066 |
* The second() function returns the current second as a value from 0 - 59.
|
|
|
8067 |
*
|
|
|
8068 |
* @returns {float} The current minute.
|
|
|
8069 |
*
|
|
|
8070 |
* @see millis
|
|
|
8071 |
* @see month
|
|
|
8072 |
* @see minute
|
|
|
8073 |
* @see hour
|
|
|
8074 |
* @see day
|
|
|
8075 |
* @see year
|
|
|
8076 |
*/
|
|
|
8077 |
p.second = function() {
|
|
|
8078 |
return new Date().getSeconds();
|
|
|
8079 |
};
|
|
|
8080 |
/**
|
|
|
8081 |
* Returns the number of milliseconds (thousandths of a second) since starting a sketch.
|
|
|
8082 |
* This information is often used for timing animation sequences.
|
|
|
8083 |
*
|
|
|
8084 |
* @returns {long} The number of milliseconds since starting the sketch.
|
|
|
8085 |
*
|
|
|
8086 |
* @see month
|
|
|
8087 |
* @see second
|
|
|
8088 |
* @see minute
|
|
|
8089 |
* @see hour
|
|
|
8090 |
* @see day
|
|
|
8091 |
* @see year
|
|
|
8092 |
*/
|
|
|
8093 |
p.millis = function() {
|
|
|
8094 |
return Date.now() - start;
|
|
|
8095 |
};
|
|
|
8096 |
|
|
|
8097 |
/**
|
|
|
8098 |
* Executes the code within draw() one time. This functions allows the program to update
|
|
|
8099 |
* the display window only when necessary, for example when an event registered by
|
|
|
8100 |
* mousePressed() or keyPressed() occurs.
|
|
|
8101 |
* In structuring a program, it only makes sense to call redraw() within events such as
|
|
|
8102 |
* mousePressed(). This is because redraw() does not run draw() immediately (it only sets
|
|
|
8103 |
* a flag that indicates an update is needed).
|
|
|
8104 |
* Calling redraw() within draw() has no effect because draw() is continuously called anyway.
|
|
|
8105 |
*
|
|
|
8106 |
* @returns none
|
|
|
8107 |
*
|
|
|
8108 |
* @see noLoop
|
|
|
8109 |
* @see loop
|
|
|
8110 |
*/
|
|
|
8111 |
function redrawHelper() {
|
|
|
8112 |
var sec = (Date.now() - timeSinceLastFPS) / 1000;
|
|
|
8113 |
framesSinceLastFPS++;
|
|
|
8114 |
var fps = framesSinceLastFPS / sec;
|
|
|
8115 |
|
|
|
8116 |
// recalculate FPS every half second for better accuracy.
|
|
|
8117 |
if (sec > 0.5) {
|
|
|
8118 |
timeSinceLastFPS = Date.now();
|
|
|
8119 |
framesSinceLastFPS = 0;
|
|
|
8120 |
p.__frameRate = fps;
|
|
|
8121 |
}
|
|
|
8122 |
|
|
|
8123 |
p.frameCount++;
|
|
|
8124 |
}
|
|
|
8125 |
|
|
|
8126 |
Drawing2D.prototype.redraw = function() {
|
|
|
8127 |
redrawHelper();
|
|
|
8128 |
|
|
|
8129 |
curContext.lineWidth = lineWidth;
|
|
|
8130 |
var pmouseXLastEvent = p.pmouseX,
|
|
|
8131 |
pmouseYLastEvent = p.pmouseY;
|
|
|
8132 |
p.pmouseX = pmouseXLastFrame;
|
|
|
8133 |
p.pmouseY = pmouseYLastFrame;
|
|
|
8134 |
|
|
|
8135 |
saveContext();
|
|
|
8136 |
p.draw();
|
|
|
8137 |
restoreContext();
|
|
|
8138 |
|
|
|
8139 |
pmouseXLastFrame = p.mouseX;
|
|
|
8140 |
pmouseYLastFrame = p.mouseY;
|
|
|
8141 |
p.pmouseX = pmouseXLastEvent;
|
|
|
8142 |
p.pmouseY = pmouseYLastEvent;
|
|
|
8143 |
};
|
|
|
8144 |
|
|
|
8145 |
Drawing3D.prototype.redraw = function() {
|
|
|
8146 |
redrawHelper();
|
|
|
8147 |
|
|
|
8148 |
var pmouseXLastEvent = p.pmouseX,
|
|
|
8149 |
pmouseYLastEvent = p.pmouseY;
|
|
|
8150 |
p.pmouseX = pmouseXLastFrame;
|
|
|
8151 |
p.pmouseY = pmouseYLastFrame;
|
|
|
8152 |
// even if the color buffer isn't cleared with background(),
|
|
|
8153 |
// the depth buffer needs to be cleared regardless.
|
|
|
8154 |
curContext.clear(curContext.DEPTH_BUFFER_BIT);
|
|
|
8155 |
curContextCache = { attributes: {}, locations: {} };
|
|
|
8156 |
// Delete all the lighting states and the materials the
|
|
|
8157 |
// user set in the last draw() call.
|
|
|
8158 |
p.noLights();
|
|
|
8159 |
p.lightFalloff(1, 0, 0);
|
|
|
8160 |
p.shininess(1);
|
|
|
8161 |
p.ambient(255, 255, 255);
|
|
|
8162 |
p.specular(0, 0, 0);
|
|
|
8163 |
p.emissive(0, 0, 0);
|
|
|
8164 |
p.camera();
|
|
|
8165 |
p.draw();
|
|
|
8166 |
|
|
|
8167 |
pmouseXLastFrame = p.mouseX;
|
|
|
8168 |
pmouseYLastFrame = p.mouseY;
|
|
|
8169 |
p.pmouseX = pmouseXLastEvent;
|
|
|
8170 |
p.pmouseY = pmouseYLastEvent;
|
|
|
8171 |
};
|
|
|
8172 |
|
|
|
8173 |
/**
|
|
|
8174 |
* Stops Processing from continuously executing the code within draw(). If loop() is
|
|
|
8175 |
* called, the code in draw() begin to run continuously again. If using noLoop() in
|
|
|
8176 |
* setup(), it should be the last line inside the block.
|
|
|
8177 |
* When noLoop() is used, it's not possible to manipulate or access the screen inside event
|
|
|
8178 |
* handling functions such as mousePressed() or keyPressed(). Instead, use those functions
|
|
|
8179 |
* to call redraw() or loop(), which will run draw(), which can update the screen properly.
|
|
|
8180 |
* This means that when noLoop() has been called, no drawing can happen, and functions like
|
|
|
8181 |
* saveFrame() or loadPixels() may not be used.
|
|
|
8182 |
* Note that if the sketch is resized, redraw() will be called to update the sketch, even
|
|
|
8183 |
* after noLoop() has been specified. Otherwise, the sketch would enter an odd state until
|
|
|
8184 |
* loop() was called.
|
|
|
8185 |
*
|
|
|
8186 |
* @returns none
|
|
|
8187 |
*
|
|
|
8188 |
* @see redraw
|
|
|
8189 |
* @see draw
|
|
|
8190 |
* @see loop
|
|
|
8191 |
*/
|
|
|
8192 |
p.noLoop = function() {
|
|
|
8193 |
doLoop = false;
|
|
|
8194 |
loopStarted = false;
|
|
|
8195 |
clearInterval(looping);
|
|
|
8196 |
curSketch.onPause();
|
|
|
8197 |
};
|
|
|
8198 |
|
|
|
8199 |
/**
|
|
|
8200 |
* Causes Processing to continuously execute the code within draw(). If noLoop() is called,
|
|
|
8201 |
* the code in draw() stops executing.
|
|
|
8202 |
*
|
|
|
8203 |
* @returns none
|
|
|
8204 |
*
|
|
|
8205 |
* @see noLoop
|
|
|
8206 |
*/
|
|
|
8207 |
p.loop = function() {
|
|
|
8208 |
if (loopStarted) {
|
|
|
8209 |
return;
|
|
|
8210 |
}
|
|
|
8211 |
|
|
|
8212 |
timeSinceLastFPS = Date.now();
|
|
|
8213 |
framesSinceLastFPS = 0;
|
|
|
8214 |
|
|
|
8215 |
looping = window.setInterval(function() {
|
|
|
8216 |
try {
|
|
|
8217 |
curSketch.onFrameStart();
|
|
|
8218 |
p.redraw();
|
|
|
8219 |
curSketch.onFrameEnd();
|
|
|
8220 |
} catch(e_loop) {
|
|
|
8221 |
window.clearInterval(looping);
|
|
|
8222 |
throw e_loop;
|
|
|
8223 |
}
|
|
|
8224 |
}, curMsPerFrame);
|
|
|
8225 |
doLoop = true;
|
|
|
8226 |
loopStarted = true;
|
|
|
8227 |
curSketch.onLoop();
|
|
|
8228 |
};
|
|
|
8229 |
|
|
|
8230 |
/**
|
|
|
8231 |
* Specifies the number of frames to be displayed every second. If the processor is not
|
|
|
8232 |
* fast enough to maintain the specified rate, it will not be achieved. For example, the
|
|
|
8233 |
* function call frameRate(30) will attempt to refresh 30 times a second. It is recommended
|
|
|
8234 |
* to set the frame rate within setup(). The default rate is 60 frames per second.
|
|
|
8235 |
*
|
|
|
8236 |
* @param {int} aRate number of frames per second.
|
|
|
8237 |
*
|
|
|
8238 |
* @returns none
|
|
|
8239 |
*
|
|
|
8240 |
* @see delay
|
|
|
8241 |
*/
|
|
|
8242 |
p.frameRate = function(aRate) {
|
|
|
8243 |
curFrameRate = aRate;
|
|
|
8244 |
curMsPerFrame = 1000 / curFrameRate;
|
|
|
8245 |
|
|
|
8246 |
// clear and reset interval
|
|
|
8247 |
if (doLoop) {
|
|
|
8248 |
p.noLoop();
|
|
|
8249 |
p.loop();
|
|
|
8250 |
}
|
|
|
8251 |
};
|
|
|
8252 |
|
|
|
8253 |
////////////////////////////////////////////////////////////////////////////
|
|
|
8254 |
// JavaScript event binding and releasing
|
|
|
8255 |
////////////////////////////////////////////////////////////////////////////
|
|
|
8256 |
|
|
|
8257 |
var eventHandlers = [];
|
|
|
8258 |
|
|
|
8259 |
function attachEventHandler(elem, type, fn) {
|
|
|
8260 |
if (elem.addEventListener) {
|
|
|
8261 |
elem.addEventListener(type, fn, false);
|
|
|
8262 |
} else {
|
|
|
8263 |
elem.attachEvent("on" + type, fn);
|
|
|
8264 |
}
|
|
|
8265 |
eventHandlers.push({elem: elem, type: type, fn: fn});
|
|
|
8266 |
}
|
|
|
8267 |
|
|
|
8268 |
function detachEventHandler(eventHandler) {
|
|
|
8269 |
var elem = eventHandler.elem,
|
|
|
8270 |
type = eventHandler.type,
|
|
|
8271 |
fn = eventHandler.fn;
|
|
|
8272 |
if (elem.removeEventListener) {
|
|
|
8273 |
elem.removeEventListener(type, fn, false);
|
|
|
8274 |
} else if (elem.detachEvent) {
|
|
|
8275 |
elem.detachEvent("on" + type, fn);
|
|
|
8276 |
}
|
|
|
8277 |
}
|
|
|
8278 |
|
|
|
8279 |
/**
|
|
|
8280 |
* Quits/stops/exits the program. Programs without a draw() function exit automatically
|
|
|
8281 |
* after the last line has run, but programs with draw() run continuously until the
|
|
|
8282 |
* program is manually stopped or exit() is run.
|
|
|
8283 |
* Rather than terminating immediately, exit() will cause the sketch to exit after draw()
|
|
|
8284 |
* has completed (or after setup() completes if called during the setup() method).
|
|
|
8285 |
*
|
|
|
8286 |
* @returns none
|
|
|
8287 |
*/
|
|
|
8288 |
p.exit = function() {
|
|
|
8289 |
window.clearInterval(looping);
|
|
|
8290 |
|
|
|
8291 |
removeInstance(p.externals.canvas.id);
|
|
|
8292 |
|
|
|
8293 |
// Step through the libraries to detach them
|
|
|
8294 |
for (var lib in Processing.lib) {
|
|
|
8295 |
if (Processing.lib.hasOwnProperty(lib)) {
|
|
|
8296 |
if (Processing.lib[lib].hasOwnProperty("detach")) {
|
|
|
8297 |
Processing.lib[lib].detach(p);
|
|
|
8298 |
}
|
|
|
8299 |
}
|
|
|
8300 |
}
|
|
|
8301 |
|
|
|
8302 |
var i = eventHandlers.length;
|
|
|
8303 |
while (i--) {
|
|
|
8304 |
detachEventHandler(eventHandlers[i]);
|
|
|
8305 |
}
|
|
|
8306 |
curSketch.onExit();
|
|
|
8307 |
};
|
|
|
8308 |
|
|
|
8309 |
////////////////////////////////////////////////////////////////////////////
|
|
|
8310 |
// MISC functions
|
|
|
8311 |
////////////////////////////////////////////////////////////////////////////
|
|
|
8312 |
|
|
|
8313 |
/**
|
|
|
8314 |
* Sets the cursor to a predefined symbol, an image, or turns it on if already hidden.
|
|
|
8315 |
* If you are trying to set an image as the cursor, it is recommended to make the size
|
|
|
8316 |
* 16x16 or 32x32 pixels. It is not possible to load an image as the cursor if you are
|
|
|
8317 |
* exporting your program for the Web. The values for parameters x and y must be less
|
|
|
8318 |
* than the dimensions of the image.
|
|
|
8319 |
*
|
|
|
8320 |
* @param {MODE} MODE either ARROW, CROSS, HAND, MOVE, TEXT, WAIT
|
|
|
8321 |
* @param {PImage} image any variable of type PImage
|
|
|
8322 |
* @param {int} x the horizonal active spot of the cursor
|
|
|
8323 |
* @param {int} y the vertical active spot of the cursor
|
|
|
8324 |
*
|
|
|
8325 |
* @returns none
|
|
|
8326 |
*
|
|
|
8327 |
* @see noCursor
|
|
|
8328 |
*/
|
|
|
8329 |
p.cursor = function() {
|
|
|
8330 |
if (arguments.length > 1 || (arguments.length === 1 && arguments[0] instanceof p.PImage)) {
|
|
|
8331 |
var image = arguments[0],
|
|
|
8332 |
x, y;
|
|
|
8333 |
if (arguments.length >= 3) {
|
|
|
8334 |
x = arguments[1];
|
|
|
8335 |
y = arguments[2];
|
|
|
8336 |
if (x < 0 || y < 0 || y >= image.height || x >= image.width) {
|
|
|
8337 |
throw "x and y must be non-negative and less than the dimensions of the image";
|
|
|
8338 |
}
|
|
|
8339 |
} else {
|
|
|
8340 |
x = image.width >>> 1;
|
|
|
8341 |
y = image.height >>> 1;
|
|
|
8342 |
}
|
|
|
8343 |
|
|
|
8344 |
// see https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
|
|
|
8345 |
var imageDataURL = image.toDataURL();
|
|
|
8346 |
var style = "url(\"" + imageDataURL + "\") " + x + " " + y + ", default";
|
|
|
8347 |
curCursor = curElement.style.cursor = style;
|
|
|
8348 |
} else if (arguments.length === 1) {
|
|
|
8349 |
var mode = arguments[0];
|
|
|
8350 |
curCursor = curElement.style.cursor = mode;
|
|
|
8351 |
} else {
|
|
|
8352 |
curCursor = curElement.style.cursor = oldCursor;
|
|
|
8353 |
}
|
|
|
8354 |
};
|
|
|
8355 |
|
|
|
8356 |
/**
|
|
|
8357 |
* Hides the cursor from view.
|
|
|
8358 |
*
|
|
|
8359 |
* @returns none
|
|
|
8360 |
*
|
|
|
8361 |
* @see cursor
|
|
|
8362 |
*/
|
|
|
8363 |
p.noCursor = function() {
|
|
|
8364 |
curCursor = curElement.style.cursor = PConstants.NOCURSOR;
|
|
|
8365 |
};
|
|
|
8366 |
|
|
|
8367 |
/**
|
|
|
8368 |
* Links to a webpage either in the same window or in a new window. The complete URL
|
|
|
8369 |
* must be specified.
|
|
|
8370 |
*
|
|
|
8371 |
* @param {String} href complete url as a String in quotes
|
|
|
8372 |
* @param {String} target name of the window to load the URL as a string in quotes
|
|
|
8373 |
*
|
|
|
8374 |
* @returns none
|
|
|
8375 |
*/
|
|
|
8376 |
p.link = function(href, target) {
|
|
|
8377 |
if (target !== undef) {
|
|
|
8378 |
window.open(href, target);
|
|
|
8379 |
} else {
|
|
|
8380 |
window.location = href;
|
|
|
8381 |
}
|
|
|
8382 |
};
|
|
|
8383 |
|
|
|
8384 |
// PGraphics methods
|
|
|
8385 |
// These functions exist only for compatibility with P5
|
|
|
8386 |
p.beginDraw = nop;
|
|
|
8387 |
p.endDraw = nop;
|
|
|
8388 |
|
|
|
8389 |
/**
|
|
|
8390 |
* This function takes content from a canvas and turns it into an ImageData object to be used with a PImage
|
|
|
8391 |
*
|
|
|
8392 |
* @returns {ImageData} ImageData object to attach to a PImage (1D array of pixel data)
|
|
|
8393 |
*
|
|
|
8394 |
* @see PImage
|
|
|
8395 |
*/
|
|
|
8396 |
Drawing2D.prototype.toImageData = function(x, y, w, h) {
|
|
|
8397 |
x = x !== undef ? x : 0;
|
|
|
8398 |
y = y !== undef ? y : 0;
|
|
|
8399 |
w = w !== undef ? w : p.width;
|
|
|
8400 |
h = h !== undef ? h : p.height;
|
|
|
8401 |
return curContext.getImageData(x, y, w, h);
|
|
|
8402 |
};
|
|
|
8403 |
|
|
|
8404 |
Drawing3D.prototype.toImageData = function(x, y, w, h) {
|
|
|
8405 |
x = x !== undef ? x : 0;
|
|
|
8406 |
y = y !== undef ? y : 0;
|
|
|
8407 |
w = w !== undef ? w : p.width;
|
|
|
8408 |
h = h !== undef ? h : p.height;
|
|
|
8409 |
var c = document.createElement("canvas"),
|
|
|
8410 |
ctx = c.getContext("2d"),
|
|
|
8411 |
obj = ctx.createImageData(w, h),
|
|
|
8412 |
uBuff = new Uint8Array(w * h * 4);
|
|
|
8413 |
curContext.readPixels(x, y, w, h, curContext.RGBA, curContext.UNSIGNED_BYTE, uBuff);
|
|
|
8414 |
for (var i=0, ul=uBuff.length, obj_data=obj.data; i < ul; i++) {
|
|
|
8415 |
obj_data[i] = uBuff[(h - 1 - Math.floor(i / 4 / w)) * w * 4 + (i % (w * 4))];
|
|
|
8416 |
}
|
|
|
8417 |
return obj;
|
|
|
8418 |
};
|
|
|
8419 |
|
|
|
8420 |
/**
|
|
|
8421 |
* Displays message in the browser's status area. This is the text area in the lower
|
|
|
8422 |
* left corner of the browser. The status() function will only work when the
|
|
|
8423 |
* Processing program is running in a web browser.
|
|
|
8424 |
*
|
|
|
8425 |
* @param {String} text any valid String
|
|
|
8426 |
*
|
|
|
8427 |
* @returns none
|
|
|
8428 |
*/
|
|
|
8429 |
p.status = function(text) {
|
|
|
8430 |
window.status = text;
|
|
|
8431 |
};
|
|
|
8432 |
|
|
|
8433 |
////////////////////////////////////////////////////////////////////////////
|
|
|
8434 |
// Binary Functions
|
|
|
8435 |
////////////////////////////////////////////////////////////////////////////
|
|
|
8436 |
|
|
|
8437 |
/**
|
|
|
8438 |
* Converts a byte, char, int, or color to a String containing the equivalent binary
|
|
|
8439 |
* notation. For example color(0, 102, 153, 255) will convert to the String
|
|
|
8440 |
* "11111111000000000110011010011001". This function can help make your geeky debugging
|
|
|
8441 |
* sessions much happier.
|
|
|
8442 |
*
|
|
|
8443 |
* @param {byte|char|int|color} num byte, char, int, color: value to convert
|
|
|
8444 |
* @param {int} numBits number of digits to return
|
|
|
8445 |
*
|
|
|
8446 |
* @returns {String}
|
|
|
8447 |
*
|
|
|
8448 |
* @see unhex
|
|
|
8449 |
* @see hex
|
|
|
8450 |
* @see unbinary
|
|
|
8451 |
*/
|
|
|
8452 |
p.binary = function(num, numBits) {
|
|
|
8453 |
var bit;
|
|
|
8454 |
if (numBits > 0) {
|
|
|
8455 |
bit = numBits;
|
|
|
8456 |
} else if(num instanceof Char) {
|
|
|
8457 |
bit = 16;
|
|
|
8458 |
num |= 0; // making it int
|
|
|
8459 |
} else {
|
|
|
8460 |
// autodetect, skipping zeros
|
|
|
8461 |
bit = 32;
|
|
|
8462 |
while (bit > 1 && !((num >>> (bit - 1)) & 1)) {
|
|
|
8463 |
bit--;
|
|
|
8464 |
}
|
|
|
8465 |
}
|
|
|
8466 |
var result = "";
|
|
|
8467 |
while (bit > 0) {
|
|
|
8468 |
result += ((num >>> (--bit)) & 1) ? "1" : "0";
|
|
|
8469 |
}
|
|
|
8470 |
return result;
|
|
|
8471 |
};
|
|
|
8472 |
|
|
|
8473 |
/**
|
|
|
8474 |
* Converts a String representation of a binary number to its equivalent integer value.
|
|
|
8475 |
* For example, unbinary("00001000") will return 8.
|
|
|
8476 |
*
|
|
|
8477 |
* @param {String} binaryString String
|
|
|
8478 |
*
|
|
|
8479 |
* @returns {Int}
|
|
|
8480 |
*
|
|
|
8481 |
* @see hex
|
|
|
8482 |
* @see binary
|
|
|
8483 |
* @see unbinary
|
|
|
8484 |
*/
|
|
|
8485 |
p.unbinary = function(binaryString) {
|
|
|
8486 |
var i = binaryString.length - 1, mask = 1, result = 0;
|
|
|
8487 |
while (i >= 0) {
|
|
|
8488 |
var ch = binaryString[i--];
|
|
|
8489 |
if (ch !== '0' && ch !== '1') {
|
|
|
8490 |
throw "the value passed into unbinary was not an 8 bit binary number";
|
|
|
8491 |
}
|
|
|
8492 |
if (ch === '1') {
|
|
|
8493 |
result += mask;
|
|
|
8494 |
}
|
|
|
8495 |
mask <<= 1;
|
|
|
8496 |
}
|
|
|
8497 |
return result;
|
|
|
8498 |
};
|
|
|
8499 |
|
|
|
8500 |
/**
|
|
|
8501 |
* Number-to-String formatting function. Prepends "plus" or "minus" depending
|
|
|
8502 |
* on whether the value is positive or negative, respectively, after padding
|
|
|
8503 |
* the value with zeroes on the left and right, the number of zeroes used dictated
|
|
|
8504 |
* by the values 'leftDigits' and 'rightDigits'. 'value' cannot be an array.
|
|
|
8505 |
*
|
|
|
8506 |
* @param {int|float} value the number to format
|
|
|
8507 |
* @param {String} plus the prefix for positive numbers
|
|
|
8508 |
* @param {String} minus the prefix for negative numbers
|
|
|
8509 |
* @param {int} left number of digits to the left of the decimal point
|
|
|
8510 |
* @param {int} right number of digits to the right of the decimal point
|
|
|
8511 |
* @param {String} group string delimited for groups, such as the comma in "1,000"
|
|
|
8512 |
*
|
|
|
8513 |
* @returns {String or String[]}
|
|
|
8514 |
*
|
|
|
8515 |
* @see nfCore
|
|
|
8516 |
*/
|
|
|
8517 |
function nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group) {
|
|
|
8518 |
var sign = (value < 0) ? minus : plus;
|
|
|
8519 |
var autoDetectDecimals = rightDigits === 0;
|
|
|
8520 |
var rightDigitsOfDefault = (rightDigits === undef || rightDigits < 0) ? 0 : rightDigits;
|
|
|
8521 |
|
|
|
8522 |
var absValue = Math.abs(value);
|
|
|
8523 |
if (autoDetectDecimals) {
|
|
|
8524 |
rightDigitsOfDefault = 1;
|
|
|
8525 |
absValue *= 10;
|
|
|
8526 |
while (Math.abs(Math.round(absValue) - absValue) > 1e-6 && rightDigitsOfDefault < 7) {
|
|
|
8527 |
++rightDigitsOfDefault;
|
|
|
8528 |
absValue *= 10;
|
|
|
8529 |
}
|
|
|
8530 |
} else if (rightDigitsOfDefault !== 0) {
|
|
|
8531 |
absValue *= Math.pow(10, rightDigitsOfDefault);
|
|
|
8532 |
}
|
|
|
8533 |
|
|
|
8534 |
// Using Java's default rounding policy HALF_EVEN. This policy is based
|
|
|
8535 |
// on the idea that 0.5 values round to the nearest even number, and
|
|
|
8536 |
// everything else is rounded normally.
|
|
|
8537 |
var number, doubled = absValue * 2;
|
|
|
8538 |
if (Math.floor(absValue) === absValue) {
|
|
|
8539 |
number = absValue;
|
|
|
8540 |
} else if (Math.floor(doubled) === doubled) {
|
|
|
8541 |
var floored = Math.floor(absValue);
|
|
|
8542 |
number = floored + (floored % 2);
|
|
|
8543 |
} else {
|
|
|
8544 |
number = Math.round(absValue);
|
|
|
8545 |
}
|
|
|
8546 |
|
|
|
8547 |
var buffer = "";
|
|
|
8548 |
var totalDigits = leftDigits + rightDigitsOfDefault;
|
|
|
8549 |
while (totalDigits > 0 || number > 0) {
|
|
|
8550 |
totalDigits--;
|
|
|
8551 |
buffer = "" + (number % 10) + buffer;
|
|
|
8552 |
number = Math.floor(number / 10);
|
|
|
8553 |
}
|
|
|
8554 |
if (group !== undef) {
|
|
|
8555 |
var i = buffer.length - 3 - rightDigitsOfDefault;
|
|
|
8556 |
while(i > 0) {
|
|
|
8557 |
buffer = buffer.substring(0,i) + group + buffer.substring(i);
|
|
|
8558 |
i-=3;
|
|
|
8559 |
}
|
|
|
8560 |
}
|
|
|
8561 |
if (rightDigitsOfDefault > 0) {
|
|
|
8562 |
return sign + buffer.substring(0, buffer.length - rightDigitsOfDefault) +
|
|
|
8563 |
"." + buffer.substring(buffer.length - rightDigitsOfDefault, buffer.length);
|
|
|
8564 |
}
|
|
|
8565 |
return sign + buffer;
|
|
|
8566 |
}
|
|
|
8567 |
|
|
|
8568 |
/**
|
|
|
8569 |
* Number-to-String formatting function. Prepends "plus" or "minus" depending
|
|
|
8570 |
* on whether the value is positive or negative, respectively, after padding
|
|
|
8571 |
* the value with zeroes on the left and right, the number of zeroes used dictated
|
|
|
8572 |
* by the values 'leftDigits' and 'rightDigits'. 'value' can be an array;
|
|
|
8573 |
* if the input is an array, each value in it is formatted separately, and
|
|
|
8574 |
* an array with formatted values is returned.
|
|
|
8575 |
*
|
|
|
8576 |
* @param {int|int[]|float|float[]} value the number(s) to format
|
|
|
8577 |
* @param {String} plus the prefix for positive numbers
|
|
|
8578 |
* @param {String} minus the prefix for negative numbers
|
|
|
8579 |
* @param {int} left number of digits to the left of the decimal point
|
|
|
8580 |
* @param {int} right number of digits to the right of the decimal point
|
|
|
8581 |
* @param {String} group string delimited for groups, such as the comma in "1,000"
|
|
|
8582 |
*
|
|
|
8583 |
* @returns {String or String[]}
|
|
|
8584 |
*
|
|
|
8585 |
* @see nfCoreScalar
|
|
|
8586 |
*/
|
|
|
8587 |
function nfCore(value, plus, minus, leftDigits, rightDigits, group) {
|
|
|
8588 |
if (value instanceof Array) {
|
|
|
8589 |
var arr = [];
|
|
|
8590 |
for (var i = 0, len = value.length; i < len; i++) {
|
|
|
8591 |
arr.push(nfCoreScalar(value[i], plus, minus, leftDigits, rightDigits, group));
|
|
|
8592 |
}
|
|
|
8593 |
return arr;
|
|
|
8594 |
}
|
|
|
8595 |
return nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group);
|
|
|
8596 |
}
|
|
|
8597 |
|
|
|
8598 |
/**
|
|
|
8599 |
* Utility function for formatting numbers into strings. There are two versions, one for
|
|
|
8600 |
* formatting floats and one for formatting ints. The values for the digits, left, and
|
|
|
8601 |
* right parameters should always be positive integers.
|
|
|
8602 |
* As shown in the above example, nf() is used to add zeros to the left and/or right
|
|
|
8603 |
* of a number. This is typically for aligning a list of numbers. To remove digits from
|
|
|
8604 |
* a floating-point number, use the int(), ceil(), floor(), or round() functions.
|
|
|
8605 |
*
|
|
|
8606 |
* @param {int|int[]|float|float[]} value the number(s) to format
|
|
|
8607 |
* @param {int} left number of digits to the left of the decimal point
|
|
|
8608 |
* @param {int} right number of digits to the right of the decimal point
|
|
|
8609 |
*
|
|
|
8610 |
* @returns {String or String[]}
|
|
|
8611 |
*
|
|
|
8612 |
* @see nfs
|
|
|
8613 |
* @see nfp
|
|
|
8614 |
* @see nfc
|
|
|
8615 |
*/
|
|
|
8616 |
p.nf = function(value, leftDigits, rightDigits) { return nfCore(value, "", "-", leftDigits, rightDigits); };
|
|
|
8617 |
|
|
|
8618 |
/**
|
|
|
8619 |
* Utility function for formatting numbers into strings. Similar to nf() but leaves a blank space in front
|
|
|
8620 |
* of positive numbers so they align with negative numbers in spite of the minus symbol. There are two
|
|
|
8621 |
* versions, one for formatting floats and one for formatting ints. The values for the digits, left,
|
|
|
8622 |
* and right parameters should always be positive integers.
|
|
|
8623 |
*
|
|
|
8624 |
* @param {int|int[]|float|float[]} value the number(s) to format
|
|
|
8625 |
* @param {int} left number of digits to the left of the decimal point
|
|
|
8626 |
* @param {int} right number of digits to the right of the decimal point
|
|
|
8627 |
*
|
|
|
8628 |
* @returns {String or String[]}
|
|
|
8629 |
*
|
|
|
8630 |
* @see nf
|
|
|
8631 |
* @see nfp
|
|
|
8632 |
* @see nfc
|
|
|
8633 |
*/
|
|
|
8634 |
p.nfs = function(value, leftDigits, rightDigits) { return nfCore(value, " ", "-", leftDigits, rightDigits); };
|
|
|
8635 |
|
|
|
8636 |
/**
|
|
|
8637 |
* Utility function for formatting numbers into strings. Similar to nf() but puts a "+" in front of
|
|
|
8638 |
* positive numbers and a "-" in front of negative numbers. There are two versions, one for formatting
|
|
|
8639 |
* floats and one for formatting ints. The values for the digits, left, and right parameters should
|
|
|
8640 |
* always be positive integers.
|
|
|
8641 |
*
|
|
|
8642 |
* @param {int|int[]|float|float[]} value the number(s) to format
|
|
|
8643 |
* @param {int} left number of digits to the left of the decimal point
|
|
|
8644 |
* @param {int} right number of digits to the right of the decimal point
|
|
|
8645 |
*
|
|
|
8646 |
* @returns {String or String[]}
|
|
|
8647 |
*
|
|
|
8648 |
* @see nfs
|
|
|
8649 |
* @see nf
|
|
|
8650 |
* @see nfc
|
|
|
8651 |
*/
|
|
|
8652 |
p.nfp = function(value, leftDigits, rightDigits) { return nfCore(value, "+", "-", leftDigits, rightDigits); };
|
|
|
8653 |
|
|
|
8654 |
/**
|
|
|
8655 |
* Utility function for formatting numbers into strings and placing appropriate commas to mark
|
|
|
8656 |
* units of 1000. There are two versions, one for formatting ints and one for formatting an array
|
|
|
8657 |
* of ints. The value for the digits parameter should always be a positive integer.
|
|
|
8658 |
*
|
|
|
8659 |
* @param {int|int[]|float|float[]} value the number(s) to format
|
|
|
8660 |
* @param {int} left number of digits to the left of the decimal point
|
|
|
8661 |
* @param {int} right number of digits to the right of the decimal point
|
|
|
8662 |
*
|
|
|
8663 |
* @returns {String or String[]}
|
|
|
8664 |
*
|
|
|
8665 |
* @see nf
|
|
|
8666 |
* @see nfs
|
|
|
8667 |
* @see nfp
|
|
|
8668 |
*/
|
|
|
8669 |
p.nfc = function(value, leftDigits, rightDigits) { return nfCore(value, "", "-", leftDigits, rightDigits, ","); };
|
|
|
8670 |
|
|
|
8671 |
var decimalToHex = function(d, padding) {
|
|
|
8672 |
//if there is no padding value added, default padding to 8 else go into while statement.
|
|
|
8673 |
padding = (padding === undef || padding === null) ? padding = 8 : padding;
|
|
|
8674 |
if (d < 0) {
|
|
|
8675 |
d = 0xFFFFFFFF + d + 1;
|
|
|
8676 |
}
|
|
|
8677 |
var hex = Number(d).toString(16).toUpperCase();
|
|
|
8678 |
while (hex.length < padding) {
|
|
|
8679 |
hex = "0" + hex;
|
|
|
8680 |
}
|
|
|
8681 |
if (hex.length >= padding) {
|
|
|
8682 |
hex = hex.substring(hex.length - padding, hex.length);
|
|
|
8683 |
}
|
|
|
8684 |
return hex;
|
|
|
8685 |
};
|
|
|
8686 |
|
|
|
8687 |
// note: since we cannot keep track of byte, int types by default the returned string is 8 chars long
|
|
|
8688 |
// if no 2nd argument is passed. closest compromise we can use to match java implementation Feb 5 2010
|
|
|
8689 |
// also the char parser has issues with chars that are not digits or letters IE: !@#$%^&*
|
|
|
8690 |
/**
|
|
|
8691 |
* Converts a byte, char, int, or color to a String containing the equivalent hexadecimal notation.
|
|
|
8692 |
* For example color(0, 102, 153, 255) will convert to the String "FF006699". This function can help
|
|
|
8693 |
* make your geeky debugging sessions much happier.
|
|
|
8694 |
*
|
|
|
8695 |
* @param {byte|char|int|Color} value the value to turn into a hex string
|
|
|
8696 |
* @param {int} digits the number of digits to return
|
|
|
8697 |
*
|
|
|
8698 |
* @returns {String}
|
|
|
8699 |
*
|
|
|
8700 |
* @see unhex
|
|
|
8701 |
* @see binary
|
|
|
8702 |
* @see unbinary
|
|
|
8703 |
*/
|
|
|
8704 |
p.hex = function(value, len) {
|
|
|
8705 |
if (arguments.length === 1) {
|
|
|
8706 |
if (value instanceof Char) {
|
|
|
8707 |
len = 4;
|
|
|
8708 |
} else { // int or byte, indistinguishable at the moment, default to 8
|
|
|
8709 |
len = 8;
|
|
|
8710 |
}
|
|
|
8711 |
}
|
|
|
8712 |
return decimalToHex(value, len);
|
|
|
8713 |
};
|
|
|
8714 |
|
|
|
8715 |
function unhexScalar(hex) {
|
|
|
8716 |
var value = parseInt("0x" + hex, 16);
|
|
|
8717 |
|
|
|
8718 |
// correct for int overflow java expectation
|
|
|
8719 |
if (value > 2147483647) {
|
|
|
8720 |
value -= 4294967296;
|
|
|
8721 |
}
|
|
|
8722 |
return value;
|
|
|
8723 |
}
|
|
|
8724 |
|
|
|
8725 |
/**
|
|
|
8726 |
* Converts a String representation of a hexadecimal number to its equivalent integer value.
|
|
|
8727 |
*
|
|
|
8728 |
* @param {String} hex the hex string to convert to an int
|
|
|
8729 |
*
|
|
|
8730 |
* @returns {int}
|
|
|
8731 |
*
|
|
|
8732 |
* @see hex
|
|
|
8733 |
* @see binary
|
|
|
8734 |
* @see unbinary
|
|
|
8735 |
*/
|
|
|
8736 |
p.unhex = function(hex) {
|
|
|
8737 |
if (hex instanceof Array) {
|
|
|
8738 |
var arr = [];
|
|
|
8739 |
for (var i = 0; i < hex.length; i++) {
|
|
|
8740 |
arr.push(unhexScalar(hex[i]));
|
|
|
8741 |
}
|
|
|
8742 |
return arr;
|
|
|
8743 |
}
|
|
|
8744 |
return unhexScalar(hex);
|
|
|
8745 |
};
|
|
|
8746 |
|
|
|
8747 |
// Load a file or URL into strings
|
|
|
8748 |
/**
|
|
|
8749 |
* Reads the contents of a file or url and creates a String array of its individual lines.
|
|
|
8750 |
* The filename parameter can also be a URL to a file found online. If the file is not available or an error occurs,
|
|
|
8751 |
* null will be returned and an error message will be printed to the console. The error message does not halt
|
|
|
8752 |
* the program.
|
|
|
8753 |
*
|
|
|
8754 |
* @param {String} filename name of the file or url to load
|
|
|
8755 |
*
|
|
|
8756 |
* @returns {String[]}
|
|
|
8757 |
*
|
|
|
8758 |
* @see loadBytes
|
|
|
8759 |
* @see saveStrings
|
|
|
8760 |
* @see saveBytes
|
|
|
8761 |
*/
|
|
|
8762 |
p.loadStrings = function(filename) {
|
|
|
8763 |
if (localStorage[filename]) {
|
|
|
8764 |
return localStorage[filename].split("\n");
|
|
|
8765 |
}
|
|
|
8766 |
|
|
|
8767 |
var filecontent = ajax(filename);
|
|
|
8768 |
if(typeof filecontent !== "string" || filecontent === "") {
|
|
|
8769 |
return [];
|
|
|
8770 |
}
|
|
|
8771 |
|
|
|
8772 |
// deal with the fact that Windows uses \r\n, Unix uses \n,
|
|
|
8773 |
// Mac uses \r, and we actually expect \n
|
|
|
8774 |
filecontent = filecontent.replace(/(\r\n?)/g,"\n").replace(/\n$/,"");
|
|
|
8775 |
|
|
|
8776 |
return filecontent.split("\n");
|
|
|
8777 |
};
|
|
|
8778 |
|
|
|
8779 |
// Writes an array of strings to a file, one line per string
|
|
|
8780 |
/**
|
|
|
8781 |
* Writes an array of strings to a file, one line per string. This file is saved to the localStorage.
|
|
|
8782 |
*
|
|
|
8783 |
* @param {String} filename name of the file to save to localStorage
|
|
|
8784 |
* @param {String[]} strings string array to be written
|
|
|
8785 |
*
|
|
|
8786 |
* @see loadBytes
|
|
|
8787 |
* @see loadStrings
|
|
|
8788 |
* @see saveBytes
|
|
|
8789 |
*/
|
|
|
8790 |
p.saveStrings = function(filename, strings) {
|
|
|
8791 |
localStorage[filename] = strings.join('\n');
|
|
|
8792 |
};
|
|
|
8793 |
|
|
|
8794 |
/**
|
|
|
8795 |
* 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.
|
|
|
8796 |
* The filename parameter can also be a URL to a file found online.
|
|
|
8797 |
*
|
|
|
8798 |
* @param {String} filename name of a file in the localStorage or a URL.
|
|
|
8799 |
*
|
|
|
8800 |
* @returns {byte[]}
|
|
|
8801 |
*
|
|
|
8802 |
* @see loadStrings
|
|
|
8803 |
* @see saveStrings
|
|
|
8804 |
* @see saveBytes
|
|
|
8805 |
*/
|
|
|
8806 |
p.loadBytes = function(url) {
|
|
|
8807 |
var string = ajax(url);
|
|
|
8808 |
var ret = [];
|
|
|
8809 |
|
|
|
8810 |
for (var i = 0; i < string.length; i++) {
|
|
|
8811 |
ret.push(string.charCodeAt(i));
|
|
|
8812 |
}
|
|
|
8813 |
|
|
|
8814 |
return ret;
|
|
|
8815 |
};
|
|
|
8816 |
|
|
|
8817 |
/**
|
|
|
8818 |
* Removes the first argument from the arguments set -- shifts.
|
|
|
8819 |
*
|
|
|
8820 |
* @param {Arguments} args The Arguments object.
|
|
|
8821 |
*
|
|
|
8822 |
* @return {Object[]} Returns an array of arguments except first one.
|
|
|
8823 |
*
|
|
|
8824 |
* @see #match
|
|
|
8825 |
*/
|
|
|
8826 |
function removeFirstArgument(args) {
|
|
|
8827 |
return Array.prototype.slice.call(args, 1);
|
|
|
8828 |
}
|
|
|
8829 |
|
|
|
8830 |
////////////////////////////////////////////////////////////////////////////
|
|
|
8831 |
// String Functions
|
|
|
8832 |
////////////////////////////////////////////////////////////////////////////
|
|
|
8833 |
/**
|
|
|
8834 |
* The matchAll() function is identical to match(), except that it returns an array of all matches in
|
|
|
8835 |
* the specified String, rather than just the first.
|
|
|
8836 |
*
|
|
|
8837 |
* @param {String} aString the String to search inside
|
|
|
8838 |
* @param {String} aRegExp the regexp to be used for matching
|
|
|
8839 |
*
|
|
|
8840 |
* @return {String[]} returns an array of matches
|
|
|
8841 |
*
|
|
|
8842 |
* @see #match
|
|
|
8843 |
*/
|
|
|
8844 |
p.matchAll = function(aString, aRegExp) {
|
|
|
8845 |
var results = [],
|
|
|
8846 |
latest;
|
|
|
8847 |
var regexp = new RegExp(aRegExp, "g");
|
|
|
8848 |
while ((latest = regexp.exec(aString)) !== null) {
|
|
|
8849 |
results.push(latest);
|
|
|
8850 |
if (latest[0].length === 0) {
|
|
|
8851 |
++regexp.lastIndex;
|
|
|
8852 |
}
|
|
|
8853 |
}
|
|
|
8854 |
return results.length > 0 ? results : null;
|
|
|
8855 |
};
|
|
|
8856 |
/**
|
|
|
8857 |
* The contains(string) function returns true if the string passed in the parameter
|
|
|
8858 |
* is a substring of this string. It returns false if the string passed
|
|
|
8859 |
* in the parameter is not a substring of this string.
|
|
|
8860 |
*
|
|
|
8861 |
* @param {String} The string to look for in the current string
|
|
|
8862 |
*
|
|
|
8863 |
* @return {boolean} returns true if this string contains
|
|
|
8864 |
* the string passed as parameter. returns false, otherwise.
|
|
|
8865 |
*
|
|
|
8866 |
*/
|
|
|
8867 |
p.__contains = function (subject, subStr) {
|
|
|
8868 |
if (typeof subject !== "string") {
|
|
|
8869 |
return subject.contains.apply(subject, removeFirstArgument(arguments));
|
|
|
8870 |
}
|
|
|
8871 |
//Parameter is not null AND
|
|
|
8872 |
//The type of the parameter is the same as this object (string)
|
|
|
8873 |
//The javascript function that finds a substring returns 0 or higher
|
|
|
8874 |
return (
|
|
|
8875 |
(subject !== null) &&
|
|
|
8876 |
(subStr !== null) &&
|
|
|
8877 |
(typeof subStr === "string") &&
|
|
|
8878 |
(subject.indexOf(subStr) > -1)
|
|
|
8879 |
);
|
|
|
8880 |
};
|
|
|
8881 |
/**
|
|
|
8882 |
* The __replaceAll() function searches all matches between a substring (or regular expression) and a string,
|
|
|
8883 |
* and replaces the matched substring with a new substring
|
|
|
8884 |
*
|
|
|
8885 |
* @param {String} subject a substring
|
|
|
8886 |
* @param {String} regex a substring or a regular expression
|
|
|
8887 |
* @param {String} replace the string to replace the found value
|
|
|
8888 |
*
|
|
|
8889 |
* @return {String} returns result
|
|
|
8890 |
*
|
|
|
8891 |
* @see #match
|
|
|
8892 |
*/
|
|
|
8893 |
p.__replaceAll = function(subject, regex, replacement) {
|
|
|
8894 |
if (typeof subject !== "string") {
|
|
|
8895 |
return subject.replaceAll.apply(subject, removeFirstArgument(arguments));
|
|
|
8896 |
}
|
|
|
8897 |
|
|
|
8898 |
return subject.replace(new RegExp(regex, "g"), replacement);
|
|
|
8899 |
};
|
|
|
8900 |
/**
|
|
|
8901 |
* The __replaceFirst() function searches first matche between a substring (or regular expression) and a string,
|
|
|
8902 |
* and replaces the matched substring with a new substring
|
|
|
8903 |
*
|
|
|
8904 |
* @param {String} subject a substring
|
|
|
8905 |
* @param {String} regex a substring or a regular expression
|
|
|
8906 |
* @param {String} replace the string to replace the found value
|
|
|
8907 |
*
|
|
|
8908 |
* @return {String} returns result
|
|
|
8909 |
*
|
|
|
8910 |
* @see #match
|
|
|
8911 |
*/
|
|
|
8912 |
p.__replaceFirst = function(subject, regex, replacement) {
|
|
|
8913 |
if (typeof subject !== "string") {
|
|
|
8914 |
return subject.replaceFirst.apply(subject, removeFirstArgument(arguments));
|
|
|
8915 |
}
|
|
|
8916 |
|
|
|
8917 |
return subject.replace(new RegExp(regex, ""), replacement);
|
|
|
8918 |
};
|
|
|
8919 |
/**
|
|
|
8920 |
* The __replace() function searches all matches between a substring and a string,
|
|
|
8921 |
* and replaces the matched substring with a new substring
|
|
|
8922 |
*
|
|
|
8923 |
* @param {String} subject a substring
|
|
|
8924 |
* @param {String} what a substring to find
|
|
|
8925 |
* @param {String} replacement the string to replace the found value
|
|
|
8926 |
*
|
|
|
8927 |
* @return {String} returns result
|
|
|
8928 |
*/
|
|
|
8929 |
p.__replace = function(subject, what, replacement) {
|
|
|
8930 |
if (typeof subject !== "string") {
|
|
|
8931 |
return subject.replace.apply(subject, removeFirstArgument(arguments));
|
|
|
8932 |
}
|
|
|
8933 |
if (what instanceof RegExp) {
|
|
|
8934 |
return subject.replace(what, replacement);
|
|
|
8935 |
}
|
|
|
8936 |
|
|
|
8937 |
if (typeof what !== "string") {
|
|
|
8938 |
what = what.toString();
|
|
|
8939 |
}
|
|
|
8940 |
if (what === "") {
|
|
|
8941 |
return subject;
|
|
|
8942 |
}
|
|
|
8943 |
|
|
|
8944 |
var i = subject.indexOf(what);
|
|
|
8945 |
if (i < 0) {
|
|
|
8946 |
return subject;
|
|
|
8947 |
}
|
|
|
8948 |
|
|
|
8949 |
var j = 0, result = "";
|
|
|
8950 |
do {
|
|
|
8951 |
result += subject.substring(j, i) + replacement;
|
|
|
8952 |
j = i + what.length;
|
|
|
8953 |
} while ( (i = subject.indexOf(what, j)) >= 0);
|
|
|
8954 |
return result + subject.substring(j);
|
|
|
8955 |
};
|
|
|
8956 |
/**
|
|
|
8957 |
* The __equals() function compares two strings (or objects) to see if they are the same.
|
|
|
8958 |
* This method is necessary because it's not possible to compare strings using the equality operator (==).
|
|
|
8959 |
* Returns true if the strings are the same and false if they are not.
|
|
|
8960 |
*
|
|
|
8961 |
* @param {String} subject a string used for comparison
|
|
|
8962 |
* @param {String} other a string used for comparison with
|
|
|
8963 |
*
|
|
|
8964 |
* @return {boolean} true is the strings are the same false otherwise
|
|
|
8965 |
*/
|
|
|
8966 |
p.__equals = function(subject, other) {
|
|
|
8967 |
if (subject.equals instanceof Function) {
|
|
|
8968 |
return subject.equals.apply(subject, removeFirstArgument(arguments));
|
|
|
8969 |
}
|
|
|
8970 |
|
|
|
8971 |
// TODO use virtEquals for HashMap here
|
|
|
8972 |
return subject.valueOf() === other.valueOf();
|
|
|
8973 |
};
|
|
|
8974 |
/**
|
|
|
8975 |
* The __equalsIgnoreCase() function compares two strings to see if they are the same.
|
|
|
8976 |
* Returns true if the strings are the same, either when forced to all lower case or
|
|
|
8977 |
* all upper case.
|
|
|
8978 |
*
|
|
|
8979 |
* @param {String} subject a string used for comparison
|
|
|
8980 |
* @param {String} other a string used for comparison with
|
|
|
8981 |
*
|
|
|
8982 |
* @return {boolean} true is the strings are the same, ignoring case. false otherwise
|
|
|
8983 |
*/
|
|
|
8984 |
p.__equalsIgnoreCase = function(subject, other) {
|
|
|
8985 |
if (typeof subject !== "string") {
|
|
|
8986 |
return subject.equalsIgnoreCase.apply(subject, removeFirstArgument(arguments));
|
|
|
8987 |
}
|
|
|
8988 |
|
|
|
8989 |
return subject.toLowerCase() === other.toLowerCase();
|
|
|
8990 |
};
|
|
|
8991 |
/**
|
|
|
8992 |
* The __toCharArray() function splits the string into a char array.
|
|
|
8993 |
*
|
|
|
8994 |
* @param {String} subject The string
|
|
|
8995 |
*
|
|
|
8996 |
* @return {Char[]} a char array
|
|
|
8997 |
*/
|
|
|
8998 |
p.__toCharArray = function(subject) {
|
|
|
8999 |
if (typeof subject !== "string") {
|
|
|
9000 |
return subject.toCharArray.apply(subject, removeFirstArgument(arguments));
|
|
|
9001 |
}
|
|
|
9002 |
|
|
|
9003 |
var chars = [];
|
|
|
9004 |
for (var i = 0, len = subject.length; i < len; ++i) {
|
|
|
9005 |
chars[i] = new Char(subject.charAt(i));
|
|
|
9006 |
}
|
|
|
9007 |
return chars;
|
|
|
9008 |
};
|
|
|
9009 |
/**
|
|
|
9010 |
* The __split() function splits a string using the regex delimiter
|
|
|
9011 |
* specified. If limit is specified, the resultant array will have number
|
|
|
9012 |
* of elements equal to or less than the limit.
|
|
|
9013 |
*
|
|
|
9014 |
* @param {String} subject string to be split
|
|
|
9015 |
* @param {String} regexp regex string used to split the subject
|
|
|
9016 |
* @param {int} limit max number of tokens to be returned
|
|
|
9017 |
*
|
|
|
9018 |
* @return {String[]} an array of tokens from the split string
|
|
|
9019 |
*/
|
|
|
9020 |
p.__split = function(subject, regex, limit) {
|
|
|
9021 |
if (typeof subject !== "string") {
|
|
|
9022 |
return subject.split.apply(subject, removeFirstArgument(arguments));
|
|
|
9023 |
}
|
|
|
9024 |
|
|
|
9025 |
var pattern = new RegExp(regex);
|
|
|
9026 |
|
|
|
9027 |
// If limit is not specified, use JavaScript's built-in String.split.
|
|
|
9028 |
if ((limit === undef) || (limit < 1)) {
|
|
|
9029 |
return subject.split(pattern);
|
|
|
9030 |
}
|
|
|
9031 |
|
|
|
9032 |
// If limit is specified, JavaScript's built-in String.split has a
|
|
|
9033 |
// different behaviour than Java's. A Java-compatible implementation is
|
|
|
9034 |
// provided here.
|
|
|
9035 |
var result = [], currSubject = subject, pos;
|
|
|
9036 |
while (((pos = currSubject.search(pattern)) !== -1)
|
|
|
9037 |
&& (result.length < (limit - 1))) {
|
|
|
9038 |
var match = pattern.exec(currSubject).toString();
|
|
|
9039 |
result.push(currSubject.substring(0, pos));
|
|
|
9040 |
currSubject = currSubject.substring(pos + match.length);
|
|
|
9041 |
}
|
|
|
9042 |
if ((pos !== -1) || (currSubject !== "")) {
|
|
|
9043 |
result.push(currSubject);
|
|
|
9044 |
}
|
|
|
9045 |
return result;
|
|
|
9046 |
};
|
|
|
9047 |
/**
|
|
|
9048 |
* The codePointAt() function returns the unicode value of the character at a given index of a string.
|
|
|
9049 |
*
|
|
|
9050 |
* @param {int} idx the index of the character
|
|
|
9051 |
*
|
|
|
9052 |
* @return {String} code the String containing the unicode value of the character
|
|
|
9053 |
*/
|
|
|
9054 |
p.__codePointAt = function(subject, idx) {
|
|
|
9055 |
var code = subject.charCodeAt(idx),
|
|
|
9056 |
hi,
|
|
|
9057 |
low;
|
|
|
9058 |
if (0xD800 <= code && code <= 0xDBFF) {
|
|
|
9059 |
hi = code;
|
|
|
9060 |
low = subject.charCodeAt(idx + 1);
|
|
|
9061 |
return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
|
|
|
9062 |
}
|
|
|
9063 |
return code;
|
|
|
9064 |
};
|
|
|
9065 |
/**
|
|
|
9066 |
* The match() function matches a string with a regular expression, and returns the match as an
|
|
|
9067 |
* array. The first index is the matching expression, and array elements
|
|
|
9068 |
* [1] and higher represent each of the groups (sequences found in parens).
|
|
|
9069 |
*
|
|
|
9070 |
* @param {String} str the String to be searched
|
|
|
9071 |
* @param {String} regexp the regexp to be used for matching
|
|
|
9072 |
*
|
|
|
9073 |
* @return {String[]} an array of matching strings
|
|
|
9074 |
*/
|
|
|
9075 |
p.match = function(str, regexp) {
|
|
|
9076 |
return str.match(regexp);
|
|
|
9077 |
};
|
|
|
9078 |
/**
|
|
|
9079 |
* The startsWith() function tests if a string starts with the specified prefix. If the prefix
|
|
|
9080 |
* is the empty String or equal to the subject String, startsWith() will also return true.
|
|
|
9081 |
*
|
|
|
9082 |
* @param {String} prefix the String used to compare against the start of the subject String.
|
|
|
9083 |
* @param {int} toffset (optional) an offset into the subject String where searching should begin.
|
|
|
9084 |
*
|
|
|
9085 |
* @return {boolean} true if the subject String starts with the prefix.
|
|
|
9086 |
*/
|
|
|
9087 |
p.__startsWith = function(subject, prefix, toffset) {
|
|
|
9088 |
if (typeof subject !== "string") {
|
|
|
9089 |
return subject.startsWith.apply(subject, removeFirstArgument(arguments));
|
|
|
9090 |
}
|
|
|
9091 |
|
|
|
9092 |
toffset = toffset || 0;
|
|
|
9093 |
if (toffset < 0 || toffset > subject.length) {
|
|
|
9094 |
return false;
|
|
|
9095 |
}
|
|
|
9096 |
return (prefix === '' || prefix === subject) ? true : (subject.indexOf(prefix) === toffset);
|
|
|
9097 |
};
|
|
|
9098 |
/**
|
|
|
9099 |
* The endsWith() function tests if a string ends with the specified suffix. If the suffix
|
|
|
9100 |
* is the empty String, endsWith() will also return true.
|
|
|
9101 |
*
|
|
|
9102 |
* @param {String} suffix the String used to compare against the end of the subject String.
|
|
|
9103 |
*
|
|
|
9104 |
* @return {boolean} true if the subject String starts with the prefix.
|
|
|
9105 |
*/
|
|
|
9106 |
p.__endsWith = function(subject, suffix) {
|
|
|
9107 |
if (typeof subject !== "string") {
|
|
|
9108 |
return subject.endsWith.apply(subject, removeFirstArgument(arguments));
|
|
|
9109 |
}
|
|
|
9110 |
|
|
|
9111 |
var suffixLen = suffix ? suffix.length : 0;
|
|
|
9112 |
return (suffix === '' || suffix === subject) ? true :
|
|
|
9113 |
(subject.indexOf(suffix) === subject.length - suffixLen);
|
|
|
9114 |
};
|
|
|
9115 |
|
|
|
9116 |
////////////////////////////////////////////////////////////////////////////
|
|
|
9117 |
// Other java specific functions
|
|
|
9118 |
////////////////////////////////////////////////////////////////////////////
|
|
|
9119 |
|
|
|
9120 |
/**
|
|
|
9121 |
* The returns hash code of the.
|
|
|
9122 |
*
|
|
|
9123 |
* @param {Object} subject The string
|
|
|
9124 |
*
|
|
|
9125 |
* @return {int} a hash code
|
|
|
9126 |
*/
|
|
|
9127 |
p.__hashCode = function(subject) {
|
|
|
9128 |
if (subject.hashCode instanceof Function) {
|
|
|
9129 |
return subject.hashCode.apply(subject, removeFirstArgument(arguments));
|
|
|
9130 |
}
|
|
|
9131 |
return virtHashCode(subject);
|
|
|
9132 |
};
|
|
|
9133 |
/**
|
|
|
9134 |
* The __printStackTrace() prints stack trace to the console.
|
|
|
9135 |
*
|
|
|
9136 |
* @param {Exception} subject The error
|
|
|
9137 |
*/
|
|
|
9138 |
p.__printStackTrace = function(subject) {
|
|
|
9139 |
p.println("Exception: " + subject.toString() );
|
|
|
9140 |
};
|
|
|
9141 |
|
|
|
9142 |
var logBuffer = [];
|
|
|
9143 |
|
|
|
9144 |
/**
|
|
|
9145 |
* The println() function writes to the console area of the Processing environment.
|
|
|
9146 |
* 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 (+).
|
|
|
9147 |
*
|
|
|
9148 |
* @param {String} message the string to write to the console
|
|
|
9149 |
*
|
|
|
9150 |
* @see #join
|
|
|
9151 |
* @see #print
|
|
|
9152 |
*/
|
|
|
9153 |
p.println = function(message) {
|
|
|
9154 |
var bufferLen = logBuffer.length;
|
|
|
9155 |
if (bufferLen) {
|
|
|
9156 |
Processing.logger.log(logBuffer.join(""));
|
|
|
9157 |
logBuffer.length = 0; // clear log buffer
|
|
|
9158 |
}
|
|
|
9159 |
|
|
|
9160 |
if (arguments.length === 0 && bufferLen === 0) {
|
|
|
9161 |
Processing.logger.log("");
|
|
|
9162 |
} else if (arguments.length !== 0) {
|
|
|
9163 |
Processing.logger.log(message);
|
|
|
9164 |
}
|
|
|
9165 |
};
|
|
|
9166 |
/**
|
|
|
9167 |
* The print() function writes to the console area of the Processing environment.
|
|
|
9168 |
*
|
|
|
9169 |
* @param {String} message the string to write to the console
|
|
|
9170 |
*
|
|
|
9171 |
* @see #join
|
|
|
9172 |
*/
|
|
|
9173 |
p.print = function(message) {
|
|
|
9174 |
logBuffer.push(message);
|
|
|
9175 |
};
|
|
|
9176 |
|
|
|
9177 |
// Alphanumeric chars arguments automatically converted to numbers when
|
|
|
9178 |
// passed in, and will come out as numbers.
|
|
|
9179 |
p.str = function(val) {
|
|
|
9180 |
if (val instanceof Array) {
|
|
|
9181 |
var arr = [];
|
|
|
9182 |
for (var i = 0; i < val.length; i++) {
|
|
|
9183 |
arr.push(val[i].toString() + "");
|
|
|
9184 |
}
|
|
|
9185 |
return arr;
|
|
|
9186 |
}
|
|
|
9187 |
return (val.toString() + "");
|
|
|
9188 |
};
|
|
|
9189 |
/**
|
|
|
9190 |
* Remove whitespace characters from the beginning and ending
|
|
|
9191 |
* of a String or a String array. Works like String.trim() but includes the
|
|
|
9192 |
* unicode nbsp character as well. If an array is passed in the function will return a new array not effecting the array passed in.
|
|
|
9193 |
*
|
|
|
9194 |
* @param {String} str the string to trim
|
|
|
9195 |
* @param {String[]} str the string array to trim
|
|
|
9196 |
*
|
|
|
9197 |
* @return {String|String[]} retrurns a string or an array will removed whitespaces
|
|
|
9198 |
*/
|
|
|
9199 |
p.trim = function(str) {
|
|
|
9200 |
if (str instanceof Array) {
|
|
|
9201 |
var arr = [];
|
|
|
9202 |
for (var i = 0; i < str.length; i++) {
|
|
|
9203 |
arr.push(str[i].replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, ''));
|
|
|
9204 |
}
|
|
|
9205 |
return arr;
|
|
|
9206 |
}
|
|
|
9207 |
return str.replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, '');
|
|
|
9208 |
};
|
|
|
9209 |
|
|
|
9210 |
// Conversion
|
|
|
9211 |
function booleanScalar(val) {
|
|
|
9212 |
if (typeof val === 'number') {
|
|
|
9213 |
return val !== 0;
|
|
|
9214 |
}
|
|
|
9215 |
if (typeof val === 'boolean') {
|
|
|
9216 |
return val;
|
|
|
9217 |
}
|
|
|
9218 |
if (typeof val === 'string') {
|
|
|
9219 |
return val.toLowerCase() === 'true';
|
|
|
9220 |
}
|
|
|
9221 |
if (val instanceof Char) {
|
|
|
9222 |
// 1, T or t
|
|
|
9223 |
return val.code === 49 || val.code === 84 || val.code === 116;
|
|
|
9224 |
}
|
|
|
9225 |
}
|
|
|
9226 |
|
|
|
9227 |
/**
|
|
|
9228 |
* Converts the passed parameter to the function to its boolean value.
|
|
|
9229 |
* It will return an array of booleans if an array is passed in.
|
|
|
9230 |
*
|
|
|
9231 |
* @param {int, byte, string} val the parameter to be converted to boolean
|
|
|
9232 |
* @param {int[], byte[], string[]} val the array to be converted to boolean[]
|
|
|
9233 |
*
|
|
|
9234 |
* @return {boolean|boolean[]} returns a boolean or an array of booleans
|
|
|
9235 |
*/
|
|
|
9236 |
p.parseBoolean = function (val) {
|
|
|
9237 |
if (val instanceof Array) {
|
|
|
9238 |
var ret = [];
|
|
|
9239 |
for (var i = 0; i < val.length; i++) {
|
|
|
9240 |
ret.push(booleanScalar(val[i]));
|
|
|
9241 |
}
|
|
|
9242 |
return ret;
|
|
|
9243 |
}
|
|
|
9244 |
return booleanScalar(val);
|
|
|
9245 |
};
|
|
|
9246 |
|
|
|
9247 |
/**
|
|
|
9248 |
* Converts the passed parameter to the function to its byte value.
|
|
|
9249 |
* A byte is a number between -128 and 127.
|
|
|
9250 |
* It will return an array of bytes if an array is passed in.
|
|
|
9251 |
*
|
|
|
9252 |
* @param {int, char} what the parameter to be conveted to byte
|
|
|
9253 |
* @param {int[], char[]} what the array to be converted to byte[]
|
|
|
9254 |
*
|
|
|
9255 |
* @return {byte|byte[]} returns a byte or an array of bytes
|
|
|
9256 |
*/
|
|
|
9257 |
p.parseByte = function(what) {
|
|
|
9258 |
if (what instanceof Array) {
|
|
|
9259 |
var bytes = [];
|
|
|
9260 |
for (var i = 0; i < what.length; i++) {
|
|
|
9261 |
bytes.push((0 - (what[i] & 0x80)) | (what[i] & 0x7F));
|
|
|
9262 |
}
|
|
|
9263 |
return bytes;
|
|
|
9264 |
}
|
|
|
9265 |
return (0 - (what & 0x80)) | (what & 0x7F);
|
|
|
9266 |
};
|
|
|
9267 |
|
|
|
9268 |
/**
|
|
|
9269 |
* Converts the passed parameter to the function to its char value.
|
|
|
9270 |
* It will return an array of chars if an array is passed in.
|
|
|
9271 |
*
|
|
|
9272 |
* @param {int, byte} key the parameter to be conveted to char
|
|
|
9273 |
* @param {int[], byte[]} key the array to be converted to char[]
|
|
|
9274 |
*
|
|
|
9275 |
* @return {char|char[]} returns a char or an array of chars
|
|
|
9276 |
*/
|
|
|
9277 |
p.parseChar = function(key) {
|
|
|
9278 |
if (typeof key === "number") {
|
|
|
9279 |
return new Char(String.fromCharCode(key & 0xFFFF));
|
|
|
9280 |
}
|
|
|
9281 |
if (key instanceof Array) {
|
|
|
9282 |
var ret = [];
|
|
|
9283 |
for (var i = 0; i < key.length; i++) {
|
|
|
9284 |
ret.push(new Char(String.fromCharCode(key[i] & 0xFFFF)));
|
|
|
9285 |
}
|
|
|
9286 |
return ret;
|
|
|
9287 |
}
|
|
|
9288 |
throw "char() may receive only one argument of type int, byte, int[], or byte[].";
|
|
|
9289 |
};
|
|
|
9290 |
|
|
|
9291 |
// Processing doc claims good argument types are: int, char, byte, boolean,
|
|
|
9292 |
// String, int[], char[], byte[], boolean[], String[].
|
|
|
9293 |
// floats should not work. However, floats with only zeroes right of the
|
|
|
9294 |
// decimal will work because JS converts those to int.
|
|
|
9295 |
function floatScalar(val) {
|
|
|
9296 |
if (typeof val === 'number') {
|
|
|
9297 |
return val;
|
|
|
9298 |
}
|
|
|
9299 |
if (typeof val === 'boolean') {
|
|
|
9300 |
return val ? 1 : 0;
|
|
|
9301 |
}
|
|
|
9302 |
if (typeof val === 'string') {
|
|
|
9303 |
return parseFloat(val);
|
|
|
9304 |
}
|
|
|
9305 |
if (val instanceof Char) {
|
|
|
9306 |
return val.code;
|
|
|
9307 |
}
|
|
|
9308 |
}
|
|
|
9309 |
|
|
|
9310 |
/**
|
|
|
9311 |
* Converts the passed parameter to the function to its float value.
|
|
|
9312 |
* It will return an array of floats if an array is passed in.
|
|
|
9313 |
*
|
|
|
9314 |
* @param {int, char, boolean, string} val the parameter to be conveted to float
|
|
|
9315 |
* @param {int[], char[], boolean[], string[]} val the array to be converted to float[]
|
|
|
9316 |
*
|
|
|
9317 |
* @return {float|float[]} returns a float or an array of floats
|
|
|
9318 |
*/
|
|
|
9319 |
p.parseFloat = function(val) {
|
|
|
9320 |
if (val instanceof Array) {
|
|
|
9321 |
var ret = [];
|
|
|
9322 |
for (var i = 0; i < val.length; i++) {
|
|
|
9323 |
ret.push(floatScalar(val[i]));
|
|
|
9324 |
}
|
|
|
9325 |
return ret;
|
|
|
9326 |
}
|
|
|
9327 |
return floatScalar(val);
|
|
|
9328 |
};
|
|
|
9329 |
|
|
|
9330 |
function intScalar(val, radix) {
|
|
|
9331 |
if (typeof val === 'number') {
|
|
|
9332 |
return val & 0xFFFFFFFF;
|
|
|
9333 |
}
|
|
|
9334 |
if (typeof val === 'boolean') {
|
|
|
9335 |
return val ? 1 : 0;
|
|
|
9336 |
}
|
|
|
9337 |
if (typeof val === 'string') {
|
|
|
9338 |
var number = parseInt(val, radix || 10); // Default to decimal radix.
|
|
|
9339 |
return number & 0xFFFFFFFF;
|
|
|
9340 |
}
|
|
|
9341 |
if (val instanceof Char) {
|
|
|
9342 |
return val.code;
|
|
|
9343 |
}
|
|
|
9344 |
}
|
|
|
9345 |
|
|
|
9346 |
/**
|
|
|
9347 |
* Converts the passed parameter to the function to its int value.
|
|
|
9348 |
* It will return an array of ints if an array is passed in.
|
|
|
9349 |
*
|
|
|
9350 |
* @param {string, char, boolean, float} val the parameter to be conveted to int
|
|
|
9351 |
* @param {string[], char[], boolean[], float[]} val the array to be converted to int[]
|
|
|
9352 |
* @param {int} radix optional the radix of the number (for js compatibility)
|
|
|
9353 |
*
|
|
|
9354 |
* @return {int|int[]} returns a int or an array of ints
|
|
|
9355 |
*/
|
|
|
9356 |
p.parseInt = function(val, radix) {
|
|
|
9357 |
if (val instanceof Array) {
|
|
|
9358 |
var ret = [];
|
|
|
9359 |
for (var i = 0; i < val.length; i++) {
|
|
|
9360 |
if (typeof val[i] === 'string' && !/^\s*[+\-]?\d+\s*$/.test(val[i])) {
|
|
|
9361 |
ret.push(0);
|
|
|
9362 |
} else {
|
|
|
9363 |
ret.push(intScalar(val[i], radix));
|
|
|
9364 |
}
|
|
|
9365 |
}
|
|
|
9366 |
return ret;
|
|
|
9367 |
}
|
|
|
9368 |
return intScalar(val, radix);
|
|
|
9369 |
};
|
|
|
9370 |
|
|
|
9371 |
p.__int_cast = function(val) {
|
|
|
9372 |
return 0|val;
|
|
|
9373 |
};
|
|
|
9374 |
|
|
|
9375 |
p.__instanceof = function(obj, type) {
|
|
|
9376 |
if (typeof type !== "function") {
|
|
|
9377 |
throw "Function is expected as type argument for instanceof operator";
|
|
|
9378 |
}
|
|
|
9379 |
|
|
|
9380 |
if (typeof obj === "string") {
|
|
|
9381 |
// special case for strings
|
|
|
9382 |
return type === Object || type === String;
|
|
|
9383 |
}
|
|
|
9384 |
|
|
|
9385 |
if (obj instanceof type) {
|
|
|
9386 |
// fast check if obj is already of type instance
|
|
|
9387 |
return true;
|
|
|
9388 |
}
|
|
|
9389 |
|
|
|
9390 |
if (typeof obj !== "object" || obj === null) {
|
|
|
9391 |
return false; // not an object or null
|
|
|
9392 |
}
|
|
|
9393 |
|
|
|
9394 |
var objType = obj.constructor;
|
|
|
9395 |
if (type.$isInterface) {
|
|
|
9396 |
// expecting the interface
|
|
|
9397 |
// queueing interfaces from type and its base classes
|
|
|
9398 |
var interfaces = [];
|
|
|
9399 |
while (objType) {
|
|
|
9400 |
if (objType.$interfaces) {
|
|
|
9401 |
interfaces = interfaces.concat(objType.$interfaces);
|
|
|
9402 |
}
|
|
|
9403 |
objType = objType.$base;
|
|
|
9404 |
}
|
|
|
9405 |
while (interfaces.length > 0) {
|
|
|
9406 |
var i = interfaces.shift();
|
|
|
9407 |
if (i === type) {
|
|
|
9408 |
return true;
|
|
|
9409 |
}
|
|
|
9410 |
// wide search in base interfaces
|
|
|
9411 |
if (i.$interfaces) {
|
|
|
9412 |
interfaces = interfaces.concat(i.$interfaces);
|
|
|
9413 |
}
|
|
|
9414 |
}
|
|
|
9415 |
return false;
|
|
|
9416 |
}
|
|
|
9417 |
|
|
|
9418 |
while (objType.hasOwnProperty("$base")) {
|
|
|
9419 |
objType = objType.$base;
|
|
|
9420 |
if (objType === type) {
|
|
|
9421 |
return true; // object was found
|
|
|
9422 |
}
|
|
|
9423 |
}
|
|
|
9424 |
|
|
|
9425 |
return false;
|
|
|
9426 |
};
|
|
|
9427 |
|
|
|
9428 |
////////////////////////////////////////////////////////////////////////////
|
|
|
9429 |
// Math functions
|
|
|
9430 |
////////////////////////////////////////////////////////////////////////////
|
|
|
9431 |
|
|
|
9432 |
// Calculation
|
|
|
9433 |
/**
|
|
|
9434 |
* Calculates the absolute value (magnitude) of a number. The absolute value of a number is always positive.
|
|
|
9435 |
*
|
|
|
9436 |
* @param {int|float} value int or float
|
|
|
9437 |
*
|
|
|
9438 |
* @returns {int|float}
|
|
|
9439 |
*/
|
|
|
9440 |
p.abs = Math.abs;
|
|
|
9441 |
|
|
|
9442 |
/**
|
|
|
9443 |
* Calculates the closest int value that is greater than or equal to the value of the parameter.
|
|
|
9444 |
* For example, ceil(9.03) returns the value 10.
|
|
|
9445 |
*
|
|
|
9446 |
* @param {float} value float
|
|
|
9447 |
*
|
|
|
9448 |
* @returns {int}
|
|
|
9449 |
*
|
|
|
9450 |
* @see floor
|
|
|
9451 |
* @see round
|
|
|
9452 |
*/
|
|
|
9453 |
p.ceil = Math.ceil;
|
|
|
9454 |
|
|
|
9455 |
/**
|
|
|
9456 |
* Constrains a value to not exceed a maximum and minimum value.
|
|
|
9457 |
*
|
|
|
9458 |
* @param {int|float} value the value to constrain
|
|
|
9459 |
* @param {int|float} value minimum limit
|
|
|
9460 |
* @param {int|float} value maximum limit
|
|
|
9461 |
*
|
|
|
9462 |
* @returns {int|float}
|
|
|
9463 |
*
|
|
|
9464 |
* @see max
|
|
|
9465 |
* @see min
|
|
|
9466 |
*/
|
|
|
9467 |
p.constrain = function(aNumber, aMin, aMax) {
|
|
|
9468 |
return aNumber > aMax ? aMax : aNumber < aMin ? aMin : aNumber;
|
|
|
9469 |
};
|
|
|
9470 |
|
|
|
9471 |
/**
|
|
|
9472 |
* Calculates the distance between two points.
|
|
|
9473 |
*
|
|
|
9474 |
* @param {int|float} x1 int or float: x-coordinate of the first point
|
|
|
9475 |
* @param {int|float} y1 int or float: y-coordinate of the first point
|
|
|
9476 |
* @param {int|float} z1 int or float: z-coordinate of the first point
|
|
|
9477 |
* @param {int|float} x2 int or float: x-coordinate of the second point
|
|
|
9478 |
* @param {int|float} y2 int or float: y-coordinate of the second point
|
|
|
9479 |
* @param {int|float} z2 int or float: z-coordinate of the second point
|
|
|
9480 |
*
|
|
|
9481 |
* @returns {float}
|
|
|
9482 |
*/
|
|
|
9483 |
p.dist = function() {
|
|
|
9484 |
var dx, dy, dz;
|
|
|
9485 |
if (arguments.length === 4) {
|
|
|
9486 |
dx = arguments[0] - arguments[2];
|
|
|
9487 |
dy = arguments[1] - arguments[3];
|
|
|
9488 |
return Math.sqrt(dx * dx + dy * dy);
|
|
|
9489 |
}
|
|
|
9490 |
if (arguments.length === 6) {
|
|
|
9491 |
dx = arguments[0] - arguments[3];
|
|
|
9492 |
dy = arguments[1] - arguments[4];
|
|
|
9493 |
dz = arguments[2] - arguments[5];
|
|
|
9494 |
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
|
9495 |
}
|
|
|
9496 |
};
|
|
|
9497 |
|
|
|
9498 |
/**
|
|
|
9499 |
* Returns Euler's number e (2.71828...) raised to the power of the value parameter.
|
|
|
9500 |
*
|
|
|
9501 |
* @param {int|float} value int or float: the exponent to raise e to
|
|
|
9502 |
*
|
|
|
9503 |
* @returns {float}
|
|
|
9504 |
*/
|
|
|
9505 |
p.exp = Math.exp;
|
|
|
9506 |
|
|
|
9507 |
/**
|
|
|
9508 |
* Calculates the closest int value that is less than or equal to the value of the parameter.
|
|
|
9509 |
*
|
|
|
9510 |
* @param {int|float} value the value to floor
|
|
|
9511 |
*
|
|
|
9512 |
* @returns {int|float}
|
|
|
9513 |
*
|
|
|
9514 |
* @see ceil
|
|
|
9515 |
* @see round
|
|
|
9516 |
*/
|
|
|
9517 |
p.floor = Math.floor;
|
|
|
9518 |
|
|
|
9519 |
/**
|
|
|
9520 |
* Calculates a number between two numbers at a specific increment. The amt parameter is the
|
|
|
9521 |
* amount to interpolate between the two values where 0.0 equal to the first point, 0.1 is very
|
|
|
9522 |
* near the first point, 0.5 is half-way in between, etc. The lerp function is convenient for
|
|
|
9523 |
* creating motion along a straight path and for drawing dotted lines.
|
|
|
9524 |
*
|
|
|
9525 |
* @param {int|float} value1 float or int: first value
|
|
|
9526 |
* @param {int|float} value2 float or int: second value
|
|
|
9527 |
* @param {int|float} amt float: between 0.0 and 1.0
|
|
|
9528 |
*
|
|
|
9529 |
* @returns {float}
|
|
|
9530 |
*
|
|
|
9531 |
* @see curvePoint
|
|
|
9532 |
* @see bezierPoint
|
|
|
9533 |
*/
|
|
|
9534 |
p.lerp = function(value1, value2, amt) {
|
|
|
9535 |
return ((value2 - value1) * amt) + value1;
|
|
|
9536 |
};
|
|
|
9537 |
|
|
|
9538 |
/**
|
|
|
9539 |
* Calculates the natural logarithm (the base-e logarithm) of a number. This function
|
|
|
9540 |
* expects the values greater than 0.0.
|
|
|
9541 |
*
|
|
|
9542 |
* @param {int|float} value int or float: number must be greater then 0.0
|
|
|
9543 |
*
|
|
|
9544 |
* @returns {float}
|
|
|
9545 |
*/
|
|
|
9546 |
p.log = Math.log;
|
|
|
9547 |
|
|
|
9548 |
/**
|
|
|
9549 |
* Calculates the magnitude (or length) of a vector. A vector is a direction in space commonly
|
|
|
9550 |
* used in computer graphics and linear algebra. Because it has no "start" position, the magnitude
|
|
|
9551 |
* of a vector can be thought of as the distance from coordinate (0,0) to its (x,y) value.
|
|
|
9552 |
* Therefore, mag() is a shortcut for writing "dist(0, 0, x, y)".
|
|
|
9553 |
*
|
|
|
9554 |
* @param {int|float} a float or int: first value
|
|
|
9555 |
* @param {int|float} b float or int: second value
|
|
|
9556 |
* @param {int|float} c float or int: third value
|
|
|
9557 |
*
|
|
|
9558 |
* @returns {float}
|
|
|
9559 |
*
|
|
|
9560 |
* @see dist
|
|
|
9561 |
*/
|
|
|
9562 |
p.mag = function(a, b, c) {
|
|
|
9563 |
if (c) {
|
|
|
9564 |
return Math.sqrt(a * a + b * b + c * c);
|
|
|
9565 |
}
|
|
|
9566 |
|
|
|
9567 |
return Math.sqrt(a * a + b * b);
|
|
|
9568 |
};
|
|
|
9569 |
|
|
|
9570 |
/**
|
|
|
9571 |
* Re-maps a number from one range to another. In the example above, the number '25' is converted from
|
|
|
9572 |
* 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.
|
|
|
9573 |
* Numbers outside the range are not clamped to 0 and 1, because out-of-range values are often intentional and useful.
|
|
|
9574 |
*
|
|
|
9575 |
* @param {float} value The incoming value to be converted
|
|
|
9576 |
* @param {float} istart Lower bound of the value's current range
|
|
|
9577 |
* @param {float} istop Upper bound of the value's current range
|
|
|
9578 |
* @param {float} ostart Lower bound of the value's target range
|
|
|
9579 |
* @param {float} ostop Upper bound of the value's target range
|
|
|
9580 |
*
|
|
|
9581 |
* @returns {float}
|
|
|
9582 |
*
|
|
|
9583 |
* @see norm
|
|
|
9584 |
* @see lerp
|
|
|
9585 |
*/
|
|
|
9586 |
p.map = function(value, istart, istop, ostart, ostop) {
|
|
|
9587 |
return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
|
|
|
9588 |
};
|
|
|
9589 |
|
|
|
9590 |
/**
|
|
|
9591 |
* Determines the largest value in a sequence of numbers.
|
|
|
9592 |
*
|
|
|
9593 |
* @param {int|float} value1 int or float
|
|
|
9594 |
* @param {int|float} value2 int or float
|
|
|
9595 |
* @param {int|float} value3 int or float
|
|
|
9596 |
* @param {int|float} array int or float array
|
|
|
9597 |
*
|
|
|
9598 |
* @returns {int|float}
|
|
|
9599 |
*
|
|
|
9600 |
* @see min
|
|
|
9601 |
*/
|
|
|
9602 |
p.max = function() {
|
|
|
9603 |
if (arguments.length === 2) {
|
|
|
9604 |
return arguments[0] < arguments[1] ? arguments[1] : arguments[0];
|
|
|
9605 |
}
|
|
|
9606 |
var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
|
|
|
9607 |
if (! ("length" in numbers && numbers.length > 0)) {
|
|
|
9608 |
throw "Non-empty array is expected";
|
|
|
9609 |
}
|
|
|
9610 |
var max = numbers[0],
|
|
|
9611 |
count = numbers.length;
|
|
|
9612 |
for (var i = 1; i < count; ++i) {
|
|
|
9613 |
if (max < numbers[i]) {
|
|
|
9614 |
max = numbers[i];
|
|
|
9615 |
}
|
|
|
9616 |
}
|
|
|
9617 |
return max;
|
|
|
9618 |
};
|
|
|
9619 |
|
|
|
9620 |
/**
|
|
|
9621 |
* Determines the smallest value in a sequence of numbers.
|
|
|
9622 |
*
|
|
|
9623 |
* @param {int|float} value1 int or float
|
|
|
9624 |
* @param {int|float} value2 int or float
|
|
|
9625 |
* @param {int|float} value3 int or float
|
|
|
9626 |
* @param {int|float} array int or float array
|
|
|
9627 |
*
|
|
|
9628 |
* @returns {int|float}
|
|
|
9629 |
*
|
|
|
9630 |
* @see max
|
|
|
9631 |
*/
|
|
|
9632 |
p.min = function() {
|
|
|
9633 |
if (arguments.length === 2) {
|
|
|
9634 |
return arguments[0] < arguments[1] ? arguments[0] : arguments[1];
|
|
|
9635 |
}
|
|
|
9636 |
var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
|
|
|
9637 |
if (! ("length" in numbers && numbers.length > 0)) {
|
|
|
9638 |
throw "Non-empty array is expected";
|
|
|
9639 |
}
|
|
|
9640 |
var min = numbers[0],
|
|
|
9641 |
count = numbers.length;
|
|
|
9642 |
for (var i = 1; i < count; ++i) {
|
|
|
9643 |
if (min > numbers[i]) {
|
|
|
9644 |
min = numbers[i];
|
|
|
9645 |
}
|
|
|
9646 |
}
|
|
|
9647 |
return min;
|
|
|
9648 |
};
|
|
|
9649 |
|
|
|
9650 |
/**
|
|
|
9651 |
* Normalizes a number from another range into a value between 0 and 1.
|
|
|
9652 |
* Identical to map(value, low, high, 0, 1);
|
|
|
9653 |
* Numbers outside the range are not clamped to 0 and 1, because out-of-range
|
|
|
9654 |
* values are often intentional and useful.
|
|
|
9655 |
*
|
|
|
9656 |
* @param {float} aNumber The incoming value to be converted
|
|
|
9657 |
* @param {float} low Lower bound of the value's current range
|
|
|
9658 |
* @param {float} high Upper bound of the value's current range
|
|
|
9659 |
*
|
|
|
9660 |
* @returns {float}
|
|
|
9661 |
*
|
|
|
9662 |
* @see map
|
|
|
9663 |
* @see lerp
|
|
|
9664 |
*/
|
|
|
9665 |
p.norm = function(aNumber, low, high) {
|
|
|
9666 |
return (aNumber - low) / (high - low);
|
|
|
9667 |
};
|
|
|
9668 |
|
|
|
9669 |
/**
|
|
|
9670 |
* Facilitates exponential expressions. The pow() function is an efficient way of
|
|
|
9671 |
* multiplying numbers by themselves (or their reciprocal) in large quantities.
|
|
|
9672 |
* For example, pow(3, 5) is equivalent to the expression 3*3*3*3*3 and pow(3, -5)
|
|
|
9673 |
* is equivalent to 1 / 3*3*3*3*3.
|
|
|
9674 |
*
|
|
|
9675 |
* @param {int|float} num base of the exponential expression
|
|
|
9676 |
* @param {int|float} exponent power of which to raise the base
|
|
|
9677 |
*
|
|
|
9678 |
* @returns {float}
|
|
|
9679 |
*
|
|
|
9680 |
* @see sqrt
|
|
|
9681 |
*/
|
|
|
9682 |
p.pow = Math.pow;
|
|
|
9683 |
|
|
|
9684 |
/**
|
|
|
9685 |
* Calculates the integer closest to the value parameter. For example, round(9.2) returns the value 9.
|
|
|
9686 |
*
|
|
|
9687 |
* @param {float} value number to round
|
|
|
9688 |
*
|
|
|
9689 |
* @returns {int}
|
|
|
9690 |
*
|
|
|
9691 |
* @see floor
|
|
|
9692 |
* @see ceil
|
|
|
9693 |
*/
|
|
|
9694 |
p.round = Math.round;
|
|
|
9695 |
|
|
|
9696 |
/**
|
|
|
9697 |
* Squares a number (multiplies a number by itself). The result is always a positive number,
|
|
|
9698 |
* as multiplying two negative numbers always yields a positive result. For example, -1 * -1 = 1.
|
|
|
9699 |
*
|
|
|
9700 |
* @param {float} value int or float
|
|
|
9701 |
*
|
|
|
9702 |
* @returns {float}
|
|
|
9703 |
*
|
|
|
9704 |
* @see sqrt
|
|
|
9705 |
*/
|
|
|
9706 |
p.sq = function(aNumber) {
|
|
|
9707 |
return aNumber * aNumber;
|
|
|
9708 |
};
|
|
|
9709 |
|
|
|
9710 |
/**
|
|
|
9711 |
* Calculates the square root of a number. The square root of a number is always positive,
|
|
|
9712 |
* even though there may be a valid negative root. The square root s of number a is such
|
|
|
9713 |
* that s*s = a. It is the opposite of squaring.
|
|
|
9714 |
*
|
|
|
9715 |
* @param {float} value int or float, non negative
|
|
|
9716 |
*
|
|
|
9717 |
* @returns {float}
|
|
|
9718 |
*
|
|
|
9719 |
* @see pow
|
|
|
9720 |
* @see sq
|
|
|
9721 |
*/
|
|
|
9722 |
p.sqrt = Math.sqrt;
|
|
|
9723 |
|
|
|
9724 |
// Trigonometry
|
|
|
9725 |
/**
|
|
|
9726 |
* The inverse of cos(), returns the arc cosine of a value. This function expects the
|
|
|
9727 |
* values in the range of -1 to 1 and values are returned in the range 0 to PI (3.1415927).
|
|
|
9728 |
*
|
|
|
9729 |
* @param {float} value the value whose arc cosine is to be returned
|
|
|
9730 |
*
|
|
|
9731 |
* @returns {float}
|
|
|
9732 |
*
|
|
|
9733 |
* @see cos
|
|
|
9734 |
* @see asin
|
|
|
9735 |
* @see atan
|
|
|
9736 |
*/
|
|
|
9737 |
p.acos = Math.acos;
|
|
|
9738 |
|
|
|
9739 |
/**
|
|
|
9740 |
* The inverse of sin(), returns the arc sine of a value. This function expects the values
|
|
|
9741 |
* in the range of -1 to 1 and values are returned in the range -PI/2 to PI/2.
|
|
|
9742 |
*
|
|
|
9743 |
* @param {float} value the value whose arc sine is to be returned
|
|
|
9744 |
*
|
|
|
9745 |
* @returns {float}
|
|
|
9746 |
*
|
|
|
9747 |
* @see sin
|
|
|
9748 |
* @see acos
|
|
|
9749 |
* @see atan
|
|
|
9750 |
*/
|
|
|
9751 |
p.asin = Math.asin;
|
|
|
9752 |
|
|
|
9753 |
/**
|
|
|
9754 |
* The inverse of tan(), returns the arc tangent of a value. This function expects the values
|
|
|
9755 |
* in the range of -Infinity to Infinity (exclusive) and values are returned in the range -PI/2 to PI/2 .
|
|
|
9756 |
*
|
|
|
9757 |
* @param {float} value -Infinity to Infinity (exclusive)
|
|
|
9758 |
*
|
|
|
9759 |
* @returns {float}
|
|
|
9760 |
*
|
|
|
9761 |
* @see tan
|
|
|
9762 |
* @see asin
|
|
|
9763 |
* @see acos
|
|
|
9764 |
*/
|
|
|
9765 |
p.atan = Math.atan;
|
|
|
9766 |
|
|
|
9767 |
/**
|
|
|
9768 |
* Calculates the angle (in radians) from a specified point to the coordinate origin as measured from
|
|
|
9769 |
* the positive x-axis. Values are returned as a float in the range from PI to -PI. The atan2() function
|
|
|
9770 |
* is most often used for orienting geometry to the position of the cursor. Note: The y-coordinate of the
|
|
|
9771 |
* point is the first parameter and the x-coordinate is the second due the the structure of calculating the tangent.
|
|
|
9772 |
*
|
|
|
9773 |
* @param {float} y y-coordinate of the point
|
|
|
9774 |
* @param {float} x x-coordinate of the point
|
|
|
9775 |
*
|
|
|
9776 |
* @returns {float}
|
|
|
9777 |
*
|
|
|
9778 |
* @see tan
|
|
|
9779 |
*/
|
|
|
9780 |
p.atan2 = Math.atan2;
|
|
|
9781 |
|
|
|
9782 |
/**
|
|
|
9783 |
* Calculates the cosine of an angle. This function expects the values of the angle parameter to be provided
|
|
|
9784 |
* in radians (values from 0 to PI*2). Values are returned in the range -1 to 1.
|
|
|
9785 |
*
|
|
|
9786 |
* @param {float} value an angle in radians
|
|
|
9787 |
*
|
|
|
9788 |
* @returns {float}
|
|
|
9789 |
*
|
|
|
9790 |
* @see tan
|
|
|
9791 |
* @see sin
|
|
|
9792 |
*/
|
|
|
9793 |
p.cos = Math.cos;
|
|
|
9794 |
|
|
|
9795 |
/**
|
|
|
9796 |
* Converts a radian measurement to its corresponding value in degrees. Radians and degrees are two ways of
|
|
|
9797 |
* measuring the same thing. There are 360 degrees in a circle and 2*PI radians in a circle. For example,
|
|
|
9798 |
* 90 degrees = PI/2 = 1.5707964. All trigonometric methods in Processing require their parameters to be specified in radians.
|
|
|
9799 |
*
|
|
|
9800 |
* @param {int|float} value an angle in radians
|
|
|
9801 |
*
|
|
|
9802 |
* @returns {float}
|
|
|
9803 |
*
|
|
|
9804 |
* @see radians
|
|
|
9805 |
*/
|
|
|
9806 |
p.degrees = function(aAngle) {
|
|
|
9807 |
return (aAngle * 180) / Math.PI;
|
|
|
9808 |
};
|
|
|
9809 |
|
|
|
9810 |
/**
|
|
|
9811 |
* Converts a degree measurement to its corresponding value in radians. Radians and degrees are two ways of
|
|
|
9812 |
* measuring the same thing. There are 360 degrees in a circle and 2*PI radians in a circle. For example,
|
|
|
9813 |
* 90 degrees = PI/2 = 1.5707964. All trigonometric methods in Processing require their parameters to be specified in radians.
|
|
|
9814 |
*
|
|
|
9815 |
* @param {int|float} value an angle in radians
|
|
|
9816 |
*
|
|
|
9817 |
* @returns {float}
|
|
|
9818 |
*
|
|
|
9819 |
* @see degrees
|
|
|
9820 |
*/
|
|
|
9821 |
p.radians = function(aAngle) {
|
|
|
9822 |
return (aAngle / 180) * Math.PI;
|
|
|
9823 |
};
|
|
|
9824 |
|
|
|
9825 |
/**
|
|
|
9826 |
* Calculates the sine of an angle. This function expects the values of the angle parameter to be provided in
|
|
|
9827 |
* radians (values from 0 to 6.28). Values are returned in the range -1 to 1.
|
|
|
9828 |
*
|
|
|
9829 |
* @param {float} value an angle in radians
|
|
|
9830 |
*
|
|
|
9831 |
* @returns {float}
|
|
|
9832 |
*
|
|
|
9833 |
* @see cos
|
|
|
9834 |
* @see radians
|
|
|
9835 |
*/
|
|
|
9836 |
p.sin = Math.sin;
|
|
|
9837 |
|
|
|
9838 |
/**
|
|
|
9839 |
* Calculates the ratio of the sine and cosine of an angle. This function expects the values of the angle
|
|
|
9840 |
* parameter to be provided in radians (values from 0 to PI*2). Values are returned in the range infinity to -infinity.
|
|
|
9841 |
*
|
|
|
9842 |
* @param {float} value an angle in radians
|
|
|
9843 |
*
|
|
|
9844 |
* @returns {float}
|
|
|
9845 |
*
|
|
|
9846 |
* @see cos
|
|
|
9847 |
* @see sin
|
|
|
9848 |
* @see radians
|
|
|
9849 |
*/
|
|
|
9850 |
p.tan = Math.tan;
|
|
|
9851 |
|
|
|
9852 |
var currentRandom = Math.random;
|
|
|
9853 |
|
|
|
9854 |
/**
|
|
|
9855 |
* Generates random numbers. Each time the random() function is called, it returns an unexpected value within
|
|
|
9856 |
* the specified range. If one parameter is passed to the function it will return a float between zero and the
|
|
|
9857 |
* value of the high parameter. The function call random(5) returns values between 0 and 5 (starting at zero,
|
|
|
9858 |
* up to but not including 5). If two parameters are passed, it will return a float with a value between the
|
|
|
9859 |
* parameters. The function call random(-5, 10.2) returns values starting at -5 up to (but not including) 10.2.
|
|
|
9860 |
* To convert a floating-point random number to an integer, use the int() function.
|
|
|
9861 |
*
|
|
|
9862 |
* @param {int|float} value1 if one parameter is used, the top end to random from, if two params the low end
|
|
|
9863 |
* @param {int|float} value2 the top end of the random range
|
|
|
9864 |
*
|
|
|
9865 |
* @returns {float}
|
|
|
9866 |
*
|
|
|
9867 |
* @see randomSeed
|
|
|
9868 |
* @see noise
|
|
|
9869 |
*/
|
|
|
9870 |
p.random = function() {
|
|
|
9871 |
if(arguments.length === 0) {
|
|
|
9872 |
return currentRandom();
|
|
|
9873 |
}
|
|
|
9874 |
if(arguments.length === 1) {
|
|
|
9875 |
return currentRandom() * arguments[0];
|
|
|
9876 |
}
|
|
|
9877 |
var aMin = arguments[0], aMax = arguments[1];
|
|
|
9878 |
return currentRandom() * (aMax - aMin) + aMin;
|
|
|
9879 |
};
|
|
|
9880 |
|
|
|
9881 |
// Pseudo-random generator
|
|
|
9882 |
function Marsaglia(i1, i2) {
|
|
|
9883 |
// from http://www.math.uni-bielefeld.de/~sillke/ALGORITHMS/random/marsaglia-c
|
|
|
9884 |
var z=i1 || 362436069, w= i2 || 521288629;
|
|
|
9885 |
var nextInt = function() {
|
|
|
9886 |
z=(36969*(z&65535)+(z>>>16)) & 0xFFFFFFFF;
|
|
|
9887 |
w=(18000*(w&65535)+(w>>>16)) & 0xFFFFFFFF;
|
|
|
9888 |
return (((z&0xFFFF)<<16) | (w&0xFFFF)) & 0xFFFFFFFF;
|
|
|
9889 |
};
|
|
|
9890 |
|
|
|
9891 |
this.nextDouble = function() {
|
|
|
9892 |
var i = nextInt() / 4294967296;
|
|
|
9893 |
return i < 0 ? 1 + i : i;
|
|
|
9894 |
};
|
|
|
9895 |
this.nextInt = nextInt;
|
|
|
9896 |
}
|
|
|
9897 |
Marsaglia.createRandomized = function() {
|
|
|
9898 |
var now = new Date();
|
|
|
9899 |
return new Marsaglia((now / 60000) & 0xFFFFFFFF, now & 0xFFFFFFFF);
|
|
|
9900 |
};
|
|
|
9901 |
|
|
|
9902 |
/**
|
|
|
9903 |
* Sets the seed value for random(). By default, random() produces different results each time the
|
|
|
9904 |
* program is run. Set the value parameter to a constant to return the same pseudo-random numbers
|
|
|
9905 |
* each time the software is run.
|
|
|
9906 |
*
|
|
|
9907 |
* @param {int|float} seed int
|
|
|
9908 |
*
|
|
|
9909 |
* @see random
|
|
|
9910 |
* @see noise
|
|
|
9911 |
* @see noiseSeed
|
|
|
9912 |
*/
|
|
|
9913 |
p.randomSeed = function(seed) {
|
|
|
9914 |
currentRandom = (new Marsaglia(seed)).nextDouble;
|
|
|
9915 |
};
|
|
|
9916 |
|
|
|
9917 |
// Random
|
|
|
9918 |
// We have two random()'s in the code... what does this do ? and which one is current ?
|
|
|
9919 |
p.Random = function(seed) {
|
|
|
9920 |
var haveNextNextGaussian = false, nextNextGaussian, random;
|
|
|
9921 |
|
|
|
9922 |
this.nextGaussian = function() {
|
|
|
9923 |
if (haveNextNextGaussian) {
|
|
|
9924 |
haveNextNextGaussian = false;
|
|
|
9925 |
return nextNextGaussian;
|
|
|
9926 |
}
|
|
|
9927 |
var v1, v2, s;
|
|
|
9928 |
do {
|
|
|
9929 |
v1 = 2 * random() - 1; // between -1.0 and 1.0
|
|
|
9930 |
v2 = 2 * random() - 1; // between -1.0 and 1.0
|
|
|
9931 |
s = v1 * v1 + v2 * v2;
|
|
|
9932 |
}
|
|
|
9933 |
while (s >= 1 || s === 0);
|
|
|
9934 |
|
|
|
9935 |
var multiplier = Math.sqrt(-2 * Math.log(s) / s);
|
|
|
9936 |
nextNextGaussian = v2 * multiplier;
|
|
|
9937 |
haveNextNextGaussian = true;
|
|
|
9938 |
|
|
|
9939 |
return v1 * multiplier;
|
|
|
9940 |
};
|
|
|
9941 |
|
|
|
9942 |
// by default use standard random, otherwise seeded
|
|
|
9943 |
random = (seed === undef) ? Math.random : (new Marsaglia(seed)).nextDouble;
|
|
|
9944 |
};
|
|
|
9945 |
|
|
|
9946 |
// Noise functions and helpers
|
|
|
9947 |
function PerlinNoise(seed) {
|
|
|
9948 |
var rnd = seed !== undef ? new Marsaglia(seed) : Marsaglia.createRandomized();
|
|
|
9949 |
var i, j;
|
|
|
9950 |
// http://www.noisemachine.com/talk1/17b.html
|
|
|
9951 |
// http://mrl.nyu.edu/~perlin/noise/
|
|
|
9952 |
// generate permutation
|
|
|
9953 |
var perm = new Uint8Array(512);
|
|
|
9954 |
for(i=0;i<256;++i) { perm[i] = i; }
|
|
|
9955 |
for(i=0;i<256;++i) { var t = perm[j = rnd.nextInt() & 0xFF]; perm[j] = perm[i]; perm[i] = t; }
|
|
|
9956 |
// copy to avoid taking mod in perm[0];
|
|
|
9957 |
for(i=0;i<256;++i) { perm[i + 256] = perm[i]; }
|
|
|
9958 |
|
|
|
9959 |
function grad3d(i,x,y,z) {
|
|
|
9960 |
var h = i & 15; // convert into 12 gradient directions
|
|
|
9961 |
var u = h<8 ? x : y,
|
|
|
9962 |
v = h<4 ? y : h===12||h===14 ? x : z;
|
|
|
9963 |
return ((h&1) === 0 ? u : -u) + ((h&2) === 0 ? v : -v);
|
|
|
9964 |
}
|
|
|
9965 |
|
|
|
9966 |
function grad2d(i,x,y) {
|
|
|
9967 |
var v = (i & 1) === 0 ? x : y;
|
|
|
9968 |
return (i&2) === 0 ? -v : v;
|
|
|
9969 |
}
|
|
|
9970 |
|
|
|
9971 |
function grad1d(i,x) {
|
|
|
9972 |
return (i&1) === 0 ? -x : x;
|
|
|
9973 |
}
|
|
|
9974 |
|
|
|
9975 |
function lerp(t,a,b) { return a + t * (b - a); }
|
|
|
9976 |
|
|
|
9977 |
this.noise3d = function(x, y, z) {
|
|
|
9978 |
var X = Math.floor(x)&255, Y = Math.floor(y)&255, Z = Math.floor(z)&255;
|
|
|
9979 |
x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z);
|
|
|
9980 |
var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y, fz = (3-2*z)*z*z;
|
|
|
9981 |
var p0 = perm[X]+Y, p00 = perm[p0] + Z, p01 = perm[p0 + 1] + Z,
|
|
|
9982 |
p1 = perm[X + 1] + Y, p10 = perm[p1] + Z, p11 = perm[p1 + 1] + Z;
|
|
|
9983 |
return lerp(fz,
|
|
|
9984 |
lerp(fy, lerp(fx, grad3d(perm[p00], x, y, z), grad3d(perm[p10], x-1, y, z)),
|
|
|
9985 |
lerp(fx, grad3d(perm[p01], x, y-1, z), grad3d(perm[p11], x-1, y-1,z))),
|
|
|
9986 |
lerp(fy, lerp(fx, grad3d(perm[p00 + 1], x, y, z-1), grad3d(perm[p10 + 1], x-1, y, z-1)),
|
|
|
9987 |
lerp(fx, grad3d(perm[p01 + 1], x, y-1, z-1), grad3d(perm[p11 + 1], x-1, y-1,z-1))));
|
|
|
9988 |
};
|
|
|
9989 |
|
|
|
9990 |
this.noise2d = function(x, y) {
|
|
|
9991 |
var X = Math.floor(x)&255, Y = Math.floor(y)&255;
|
|
|
9992 |
x -= Math.floor(x); y -= Math.floor(y);
|
|
|
9993 |
var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y;
|
|
|
9994 |
var p0 = perm[X]+Y, p1 = perm[X + 1] + Y;
|
|
|
9995 |
return lerp(fy,
|
|
|
9996 |
lerp(fx, grad2d(perm[p0], x, y), grad2d(perm[p1], x-1, y)),
|
|
|
9997 |
lerp(fx, grad2d(perm[p0 + 1], x, y-1), grad2d(perm[p1 + 1], x-1, y-1)));
|
|
|
9998 |
};
|
|
|
9999 |
|
|
|
10000 |
this.noise1d = function(x) {
|
|
|
10001 |
var X = Math.floor(x)&255;
|
|
|
10002 |
x -= Math.floor(x);
|
|
|
10003 |
var fx = (3-2*x)*x*x;
|
|
|
10004 |
return lerp(fx, grad1d(perm[X], x), grad1d(perm[X+1], x-1));
|
|
|
10005 |
};
|
|
|
10006 |
}
|
|
|
10007 |
|
|
|
10008 |
// processing defaults
|
|
|
10009 |
var noiseProfile = { generator: undef, octaves: 4, fallout: 0.5, seed: undef};
|
|
|
10010 |
|
|
|
10011 |
/**
|
|
|
10012 |
* Returns the Perlin noise value at specified coordinates. Perlin noise is a random sequence
|
|
|
10013 |
* generator producing a more natural ordered, harmonic succession of numbers compared to the
|
|
|
10014 |
* standard random() function. It was invented by Ken Perlin in the 1980s and been used since
|
|
|
10015 |
* in graphical applications to produce procedural textures, natural motion, shapes, terrains etc.
|
|
|
10016 |
* The main difference to the random() function is that Perlin noise is defined in an infinite
|
|
|
10017 |
* n-dimensional space where each pair of coordinates corresponds to a fixed semi-random value
|
|
|
10018 |
* (fixed only for the lifespan of the program). The resulting value will always be between 0.0
|
|
|
10019 |
* and 1.0. Processing can compute 1D, 2D and 3D noise, depending on the number of coordinates
|
|
|
10020 |
* given. The noise value can be animated by moving through the noise space as demonstrated in
|
|
|
10021 |
* the example above. The 2nd and 3rd dimension can also be interpreted as time.
|
|
|
10022 |
* The actual noise is structured similar to an audio signal, in respect to the function's use
|
|
|
10023 |
* of frequencies. Similar to the concept of harmonics in physics, perlin noise is computed over
|
|
|
10024 |
* several octaves which are added together for the final result.
|
|
|
10025 |
* Another way to adjust the character of the resulting sequence is the scale of the input
|
|
|
10026 |
* coordinates. As the function works within an infinite space the value of the coordinates
|
|
|
10027 |
* doesn't matter as such, only the distance between successive coordinates does (eg. when using
|
|
|
10028 |
* noise() within a loop). As a general rule the smaller the difference between coordinates, the
|
|
|
10029 |
* smoother the resulting noise sequence will be. Steps of 0.005-0.03 work best for most applications,
|
|
|
10030 |
* but this will differ depending on use.
|
|
|
10031 |
*
|
|
|
10032 |
* @param {float} x x coordinate in noise space
|
|
|
10033 |
* @param {float} y y coordinate in noise space
|
|
|
10034 |
* @param {float} z z coordinate in noise space
|
|
|
10035 |
*
|
|
|
10036 |
* @returns {float}
|
|
|
10037 |
*
|
|
|
10038 |
* @see random
|
|
|
10039 |
* @see noiseDetail
|
|
|
10040 |
*/
|
|
|
10041 |
p.noise = function(x, y, z) {
|
|
|
10042 |
if(noiseProfile.generator === undef) {
|
|
|
10043 |
// caching
|
|
|
10044 |
noiseProfile.generator = new PerlinNoise(noiseProfile.seed);
|
|
|
10045 |
}
|
|
|
10046 |
var generator = noiseProfile.generator;
|
|
|
10047 |
var effect = 1, k = 1, sum = 0;
|
|
|
10048 |
for(var i=0; i<noiseProfile.octaves; ++i) {
|
|
|
10049 |
effect *= noiseProfile.fallout;
|
|
|
10050 |
switch (arguments.length) {
|
|
|
10051 |
case 1:
|
|
|
10052 |
sum += effect * (1 + generator.noise1d(k*x))/2; break;
|
|
|
10053 |
case 2:
|
|
|
10054 |
sum += effect * (1 + generator.noise2d(k*x, k*y))/2; break;
|
|
|
10055 |
case 3:
|
|
|
10056 |
sum += effect * (1 + generator.noise3d(k*x, k*y, k*z))/2; break;
|
|
|
10057 |
}
|
|
|
10058 |
k *= 2;
|
|
|
10059 |
}
|
|
|
10060 |
return sum;
|
|
|
10061 |
};
|
|
|
10062 |
|
|
|
10063 |
/**
|
|
|
10064 |
* Adjusts the character and level of detail produced by the Perlin noise function.
|
|
|
10065 |
* Similar to harmonics in physics, noise is computed over several octaves. Lower octaves
|
|
|
10066 |
* contribute more to the output signal and as such define the overal intensity of the noise,
|
|
|
10067 |
* whereas higher octaves create finer grained details in the noise sequence. By default,
|
|
|
10068 |
* noise is computed over 4 octaves with each octave contributing exactly half than its
|
|
|
10069 |
* predecessor, starting at 50% strength for the 1st octave. This falloff amount can be
|
|
|
10070 |
* changed by adding an additional function parameter. Eg. a falloff factor of 0.75 means
|
|
|
10071 |
* each octave will now have 75% impact (25% less) of the previous lower octave. Any value
|
|
|
10072 |
* between 0.0 and 1.0 is valid, however note that values greater than 0.5 might result in
|
|
|
10073 |
* greater than 1.0 values returned by noise(). By changing these parameters, the signal
|
|
|
10074 |
* created by the noise() function can be adapted to fit very specific needs and characteristics.
|
|
|
10075 |
*
|
|
|
10076 |
* @param {int} octaves number of octaves to be used by the noise() function
|
|
|
10077 |
* @param {float} falloff falloff factor for each octave
|
|
|
10078 |
*
|
|
|
10079 |
* @see noise
|
|
|
10080 |
*/
|
|
|
10081 |
p.noiseDetail = function(octaves, fallout) {
|
|
|
10082 |
noiseProfile.octaves = octaves;
|
|
|
10083 |
if(fallout !== undef) {
|
|
|
10084 |
noiseProfile.fallout = fallout;
|
|
|
10085 |
}
|
|
|
10086 |
};
|
|
|
10087 |
|
|
|
10088 |
/**
|
|
|
10089 |
* Sets the seed value for noise(). By default, noise() produces different results each
|
|
|
10090 |
* time the program is run. Set the value parameter to a constant to return the same
|
|
|
10091 |
* pseudo-random numbers each time the software is run.
|
|
|
10092 |
*
|
|
|
10093 |
* @param {int} seed int
|
|
|
10094 |
*
|
|
|
10095 |
* @returns {float}
|
|
|
10096 |
*
|
|
|
10097 |
* @see random
|
|
|
10098 |
* @see radomSeed
|
|
|
10099 |
* @see noise
|
|
|
10100 |
* @see noiseDetail
|
|
|
10101 |
*/
|
|
|
10102 |
p.noiseSeed = function(seed) {
|
|
|
10103 |
noiseProfile.seed = seed;
|
|
|
10104 |
noiseProfile.generator = undef;
|
|
|
10105 |
};
|
|
|
10106 |
|
|
|
10107 |
/**
|
|
|
10108 |
* Defines the dimension of the display window in units of pixels. The size() function must
|
|
|
10109 |
* be the first line in setup(). If size() is not called, the default size of the window is
|
|
|
10110 |
* 100x100 pixels. The system variables width and height are set by the parameters passed to
|
|
|
10111 |
* the size() function.
|
|
|
10112 |
*
|
|
|
10113 |
* @param {int} aWidth width of the display window in units of pixels
|
|
|
10114 |
* @param {int} aHeight height of the display window in units of pixels
|
|
|
10115 |
* @param {MODE} aMode Either P2D, P3D, JAVA2D, or OPENGL
|
|
|
10116 |
*
|
|
|
10117 |
* @see createGraphics
|
|
|
10118 |
* @see screen
|
|
|
10119 |
*/
|
|
|
10120 |
DrawingShared.prototype.size = function(aWidth, aHeight, aMode) {
|
|
|
10121 |
if (doStroke) {
|
|
|
10122 |
p.stroke(0);
|
|
|
10123 |
}
|
|
|
10124 |
|
|
|
10125 |
if (doFill) {
|
|
|
10126 |
p.fill(255);
|
|
|
10127 |
}
|
|
|
10128 |
|
|
|
10129 |
// The default 2d context has already been created in the p.init() stage if
|
|
|
10130 |
// a 3d context was not specified. This is so that a 2d context will be
|
|
|
10131 |
// available if size() was not called.
|
|
|
10132 |
var savedProperties = {
|
|
|
10133 |
fillStyle: curContext.fillStyle,
|
|
|
10134 |
strokeStyle: curContext.strokeStyle,
|
|
|
10135 |
lineCap: curContext.lineCap,
|
|
|
10136 |
lineJoin: curContext.lineJoin
|
|
|
10137 |
};
|
|
|
10138 |
// remove the style width and height properties to ensure that the canvas gets set to
|
|
|
10139 |
// aWidth and aHeight coming in
|
|
|
10140 |
if (curElement.style.length > 0 ) {
|
|
|
10141 |
curElement.style.removeProperty("width");
|
|
|
10142 |
curElement.style.removeProperty("height");
|
|
|
10143 |
}
|
|
|
10144 |
|
|
|
10145 |
curElement.width = p.width = aWidth || 100;
|
|
|
10146 |
curElement.height = p.height = aHeight || 100;
|
|
|
10147 |
|
|
|
10148 |
for (var prop in savedProperties) {
|
|
|
10149 |
if (savedProperties.hasOwnProperty(prop)) {
|
|
|
10150 |
curContext[prop] = savedProperties[prop];
|
|
|
10151 |
}
|
|
|
10152 |
}
|
|
|
10153 |
|
|
|
10154 |
// make sure to set the default font the first time round.
|
|
|
10155 |
p.textFont(curTextFont);
|
|
|
10156 |
|
|
|
10157 |
// Set the background to whatever it was called last as if background() was called before size()
|
|
|
10158 |
// If background() hasn't been called before, set background() to a light gray
|
|
|
10159 |
p.background();
|
|
|
10160 |
|
|
|
10161 |
// set 5% for pixels to cache (or 1000)
|
|
|
10162 |
maxPixelsCached = Math.max(1000, aWidth * aHeight * 0.05);
|
|
|
10163 |
|
|
|
10164 |
// Externalize the context
|
|
|
10165 |
p.externals.context = curContext;
|
|
|
10166 |
|
|
|
10167 |
for (var i = 0; i < PConstants.SINCOS_LENGTH; i++) {
|
|
|
10168 |
sinLUT[i] = p.sin(i * (PConstants.PI / 180) * 0.5);
|
|
|
10169 |
cosLUT[i] = p.cos(i * (PConstants.PI / 180) * 0.5);
|
|
|
10170 |
}
|
|
|
10171 |
};
|
|
|
10172 |
|
|
|
10173 |
Drawing2D.prototype.size = function(aWidth, aHeight, aMode) {
|
|
|
10174 |
if (curContext === undef) {
|
|
|
10175 |
// size() was called without p.init() default context, i.e. p.createGraphics()
|
|
|
10176 |
curContext = curElement.getContext("2d");
|
|
|
10177 |
userMatrixStack = new PMatrixStack();
|
|
|
10178 |
userReverseMatrixStack = new PMatrixStack();
|
|
|
10179 |
modelView = new PMatrix2D();
|
|
|
10180 |
modelViewInv = new PMatrix2D();
|
|
|
10181 |
}
|
|
|
10182 |
|
|
|
10183 |
DrawingShared.prototype.size.apply(this, arguments);
|
|
|
10184 |
};
|
|
|
10185 |
|
|
|
10186 |
Drawing3D.prototype.size = (function() {
|
|
|
10187 |
var size3DCalled = false;
|
|
|
10188 |
|
|
|
10189 |
return function size(aWidth, aHeight, aMode) {
|
|
|
10190 |
if (size3DCalled) {
|
|
|
10191 |
throw "Multiple calls to size() for 3D renders are not allowed.";
|
|
|
10192 |
}
|
|
|
10193 |
size3DCalled = true;
|
|
|
10194 |
|
|
|
10195 |
function getGLContext(canvas) {
|
|
|
10196 |
var ctxNames = ['experimental-webgl', 'webgl', 'webkit-3d'],
|
|
|
10197 |
gl;
|
|
|
10198 |
|
|
|
10199 |
for (var i=0, l=ctxNames.length; i<l; i++) {
|
|
|
10200 |
gl = canvas.getContext(ctxNames[i], {antialias: false});
|
|
|
10201 |
if (gl) {
|
|
|
10202 |
break;
|
|
|
10203 |
}
|
|
|
10204 |
}
|
|
|
10205 |
|
|
|
10206 |
return gl;
|
|
|
10207 |
}
|
|
|
10208 |
|
|
|
10209 |
// get the 3D rendering context
|
|
|
10210 |
try {
|
|
|
10211 |
// If the HTML <canvas> dimensions differ from the
|
|
|
10212 |
// dimensions specified in the size() call in the sketch, for
|
|
|
10213 |
// 3D sketches, browsers will either not render or render the
|
|
|
10214 |
// scene incorrectly. To fix this, we need to adjust the
|
|
|
10215 |
// width and height attributes of the canvas.
|
|
|
10216 |
curElement.width = p.width = aWidth || 100;
|
|
|
10217 |
curElement.height = p.height = aHeight || 100;
|
|
|
10218 |
curContext = getGLContext(curElement);
|
|
|
10219 |
canTex = curContext.createTexture(); // texture
|
|
|
10220 |
textTex = curContext.createTexture(); // texture
|
|
|
10221 |
} catch(e_size) {
|
|
|
10222 |
Processing.debug(e_size);
|
|
|
10223 |
}
|
|
|
10224 |
|
|
|
10225 |
if (!curContext) {
|
|
|
10226 |
throw "WebGL context is not supported on this browser.";
|
|
|
10227 |
}
|
|
|
10228 |
|
|
|
10229 |
// Set defaults
|
|
|
10230 |
curContext.viewport(0, 0, curElement.width, curElement.height);
|
|
|
10231 |
curContext.enable(curContext.DEPTH_TEST);
|
|
|
10232 |
curContext.enable(curContext.BLEND);
|
|
|
10233 |
curContext.blendFunc(curContext.SRC_ALPHA, curContext.ONE_MINUS_SRC_ALPHA);
|
|
|
10234 |
|
|
|
10235 |
// Create the program objects to render 2D (points, lines) and
|
|
|
10236 |
// 3D (spheres, boxes) shapes. Because 2D shapes are not lit,
|
|
|
10237 |
// lighting calculations could be ommitted from that program object.
|
|
|
10238 |
programObject2D = createProgramObject(curContext, vertexShaderSource2D, fragmentShaderSource2D);
|
|
|
10239 |
|
|
|
10240 |
programObjectUnlitShape = createProgramObject(curContext, vShaderSrcUnlitShape, fShaderSrcUnlitShape);
|
|
|
10241 |
|
|
|
10242 |
// Set the default point and line width for the 2D and unlit shapes.
|
|
|
10243 |
p.strokeWeight(1.0);
|
|
|
10244 |
|
|
|
10245 |
// Now that the programs have been compiled, we can set the default
|
|
|
10246 |
// states for the lights.
|
|
|
10247 |
programObject3D = createProgramObject(curContext, vertexShaderSource3D, fragmentShaderSource3D);
|
|
|
10248 |
curContext.useProgram(programObject3D);
|
|
|
10249 |
|
|
|
10250 |
// assume we aren't using textures by default
|
|
|
10251 |
uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
|
|
|
10252 |
// assume that we arn't tinting by default
|
|
|
10253 |
p.lightFalloff(1, 0, 0);
|
|
|
10254 |
p.shininess(1);
|
|
|
10255 |
p.ambient(255, 255, 255);
|
|
|
10256 |
p.specular(0, 0, 0);
|
|
|
10257 |
p.emissive(0, 0, 0);
|
|
|
10258 |
|
|
|
10259 |
// Create buffers for 3D primitives
|
|
|
10260 |
boxBuffer = curContext.createBuffer();
|
|
|
10261 |
curContext.bindBuffer(curContext.ARRAY_BUFFER, boxBuffer);
|
|
|
10262 |
curContext.bufferData(curContext.ARRAY_BUFFER, boxVerts, curContext.STATIC_DRAW);
|
|
|
10263 |
|
|
|
10264 |
boxNormBuffer = curContext.createBuffer();
|
|
|
10265 |
curContext.bindBuffer(curContext.ARRAY_BUFFER, boxNormBuffer);
|
|
|
10266 |
curContext.bufferData(curContext.ARRAY_BUFFER, boxNorms, curContext.STATIC_DRAW);
|
|
|
10267 |
|
|
|
10268 |
boxOutlineBuffer = curContext.createBuffer();
|
|
|
10269 |
curContext.bindBuffer(curContext.ARRAY_BUFFER, boxOutlineBuffer);
|
|
|
10270 |
curContext.bufferData(curContext.ARRAY_BUFFER, boxOutlineVerts, curContext.STATIC_DRAW);
|
|
|
10271 |
|
|
|
10272 |
// used to draw the rectangle and the outline
|
|
|
10273 |
rectBuffer = curContext.createBuffer();
|
|
|
10274 |
curContext.bindBuffer(curContext.ARRAY_BUFFER, rectBuffer);
|
|
|
10275 |
curContext.bufferData(curContext.ARRAY_BUFFER, rectVerts, curContext.STATIC_DRAW);
|
|
|
10276 |
|
|
|
10277 |
rectNormBuffer = curContext.createBuffer();
|
|
|
10278 |
curContext.bindBuffer(curContext.ARRAY_BUFFER, rectNormBuffer);
|
|
|
10279 |
curContext.bufferData(curContext.ARRAY_BUFFER, rectNorms, curContext.STATIC_DRAW);
|
|
|
10280 |
|
|
|
10281 |
// The sphere vertices are specified dynamically since the user
|
|
|
10282 |
// can change the level of detail. Everytime the user does that
|
|
|
10283 |
// using sphereDetail(), the new vertices are calculated.
|
|
|
10284 |
sphereBuffer = curContext.createBuffer();
|
|
|
10285 |
|
|
|
10286 |
lineBuffer = curContext.createBuffer();
|
|
|
10287 |
|
|
|
10288 |
// Shape buffers
|
|
|
10289 |
fillBuffer = curContext.createBuffer();
|
|
|
10290 |
fillColorBuffer = curContext.createBuffer();
|
|
|
10291 |
strokeColorBuffer = curContext.createBuffer();
|
|
|
10292 |
shapeTexVBO = curContext.createBuffer();
|
|
|
10293 |
|
|
|
10294 |
pointBuffer = curContext.createBuffer();
|
|
|
10295 |
curContext.bindBuffer(curContext.ARRAY_BUFFER, pointBuffer);
|
|
|
10296 |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0, 0, 0]), curContext.STATIC_DRAW);
|
|
|
10297 |
|
|
|
10298 |
textBuffer = curContext.createBuffer();
|
|
|
10299 |
curContext.bindBuffer(curContext.ARRAY_BUFFER, textBuffer );
|
|
|
10300 |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([1,1,0,-1,1,0,-1,-1,0,1,-1,0]), curContext.STATIC_DRAW);
|
|
|
10301 |
|
|
|
10302 |
textureBuffer = curContext.createBuffer();
|
|
|
10303 |
curContext.bindBuffer(curContext.ARRAY_BUFFER, textureBuffer);
|
|
|
10304 |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0,0,1,0,1,1,0,1]), curContext.STATIC_DRAW);
|
|
|
10305 |
|
|
|
10306 |
indexBuffer = curContext.createBuffer();
|
|
|
10307 |
curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
|
|
10308 |
curContext.bufferData(curContext.ELEMENT_ARRAY_BUFFER, new Uint16Array([0,1,2,2,3,0]), curContext.STATIC_DRAW);
|
|
|
10309 |
|
|
|
10310 |
cam = new PMatrix3D();
|
|
|
10311 |
cameraInv = new PMatrix3D();
|
|
|
10312 |
modelView = new PMatrix3D();
|
|
|
10313 |
modelViewInv = new PMatrix3D();
|
|
|
10314 |
projection = new PMatrix3D();
|
|
|
10315 |
p.camera();
|
|
|
10316 |
p.perspective();
|
|
|
10317 |
|
|
|
10318 |
userMatrixStack = new PMatrixStack();
|
|
|
10319 |
userReverseMatrixStack = new PMatrixStack();
|
|
|
10320 |
// used by both curve and bezier, so just init here
|
|
|
10321 |
curveBasisMatrix = new PMatrix3D();
|
|
|
10322 |
curveToBezierMatrix = new PMatrix3D();
|
|
|
10323 |
curveDrawMatrix = new PMatrix3D();
|
|
|
10324 |
bezierDrawMatrix = new PMatrix3D();
|
|
|
10325 |
bezierBasisInverse = new PMatrix3D();
|
|
|
10326 |
bezierBasisMatrix = new PMatrix3D();
|
|
|
10327 |
bezierBasisMatrix.set(-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0);
|
|
|
10328 |
|
|
|
10329 |
DrawingShared.prototype.size.apply(this, arguments);
|
|
|
10330 |
};
|
|
|
10331 |
}());
|
|
|
10332 |
|
|
|
10333 |
////////////////////////////////////////////////////////////////////////////
|
|
|
10334 |
// Lights
|
|
|
10335 |
////////////////////////////////////////////////////////////////////////////
|
|
|
10336 |
|
|
|
10337 |
/**
|
|
|
10338 |
* Adds an ambient light. Ambient light doesn't come from a specific direction,
|
|
|
10339 |
* the rays have light have bounced around so much that objects are evenly lit
|
|
|
10340 |
* from all sides. Ambient lights are almost always used in combination with
|
|
|
10341 |
* other types of lights. Lights need to be included in the <b>draw()</b> to
|
|
|
10342 |
* remain persistent in a looping program. Placing them in the <b>setup()</b>
|
|
|
10343 |
* of a looping program will cause them to only have an effect the first time
|
|
|
10344 |
* through the loop. The effect of the parameters is determined by the current
|
|
|
10345 |
* color mode.
|
|
|
10346 |
*
|
|
|
10347 |
* @param {int | float} r red or hue value
|
|
|
10348 |
* @param {int | float} g green or hue value
|
|
|
10349 |
* @param {int | float} b blue or hue value
|
|
|
10350 |
*
|
|
|
10351 |
* @param {int | float} x x position of light (used for falloff)
|
|
|
10352 |
* @param {int | float} y y position of light (used for falloff)
|
|
|
10353 |
* @param {int | float} z z position of light (used for falloff)
|
|
|
10354 |
*
|
|
|
10355 |
* @returns none
|
|
|
10356 |
*
|
|
|
10357 |
* @see lights
|
|
|
10358 |
* @see directionalLight
|
|
|
10359 |
* @see pointLight
|
|
|
10360 |
* @see spotLight
|
|
|
10361 |
*/
|
|
|
10362 |
Drawing2D.prototype.ambientLight = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
10363 |
|
|
|
10364 |
Drawing3D.prototype.ambientLight = function(r, g, b, x, y, z) {
|
|
|
10365 |
if (lightCount === PConstants.MAX_LIGHTS) {
|
|
|
10366 |
throw "can only create " + PConstants.MAX_LIGHTS + " lights";
|
|
|
10367 |
}
|
|
|
10368 |
|
|
|
10369 |
var pos = new PVector(x, y, z);
|
|
|
10370 |
var view = new PMatrix3D();
|
|
|
10371 |
view.scale(1, -1, 1);
|
|
|
10372 |
view.apply(modelView.array());
|
|
|
10373 |
view.mult(pos, pos);
|
|
|
10374 |
|
|
|
10375 |
// Instead of calling p.color, we do the calculations ourselves to
|
|
|
10376 |
// reduce property lookups.
|
|
|
10377 |
var col = color$4(r, g, b, 0);
|
|
|
10378 |
var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
|
|
|
10379 |
((col & PConstants.GREEN_MASK) >>> 8) / 255,
|
|
|
10380 |
(col & PConstants.BLUE_MASK) / 255 ];
|
|
|
10381 |
|
|
|
10382 |
curContext.useProgram(programObject3D);
|
|
|
10383 |
uniformf("lights.color.3d." + lightCount, programObject3D, "lights" + lightCount + ".color", normalizedCol);
|
|
|
10384 |
uniformf("lights.position.3d." + lightCount, programObject3D, "lights" + lightCount + ".position", pos.array());
|
|
|
10385 |
uniformi("lights.type.3d." + lightCount, programObject3D, "lights" + lightCount + ".type", 0);
|
|
|
10386 |
uniformi("lightCount3d", programObject3D, "lightCount", ++lightCount);
|
|
|
10387 |
};
|
|
|
10388 |
|
|
|
10389 |
/**
|
|
|
10390 |
* Adds a directional light. Directional light comes from one direction and
|
|
|
10391 |
* is stronger when hitting a surface squarely and weaker if it hits at a
|
|
|
10392 |
* gentle angle. After hitting a surface, a directional lights scatters in
|
|
|
10393 |
* all directions. Lights need to be included in the <b>draw()</b> to remain
|
|
|
10394 |
* persistent in a looping program. Placing them in the <b>setup()</b> of a
|
|
|
10395 |
* looping program will cause them to only have an effect the first time
|
|
|
10396 |
* through the loop. The affect of the <br>r</b>, <br>g</b>, and <br>b</b>
|
|
|
10397 |
* parameters is determined by the current color mode. The <b>nx</b>,
|
|
|
10398 |
* <b>ny</b>, and <b>nz</b> parameters specify the direction the light is
|
|
|
10399 |
* facing. For example, setting <b>ny</b> to -1 will cause the geometry to be
|
|
|
10400 |
* lit from below (the light is facing directly upward).
|
|
|
10401 |
*
|
|
|
10402 |
* @param {int | float} r red or hue value
|
|
|
10403 |
* @param {int | float} g green or hue value
|
|
|
10404 |
* @param {int | float} b blue or hue value
|
|
|
10405 |
*
|
|
|
10406 |
* @param {int | float} nx direction along the x axis
|
|
|
10407 |
* @param {int | float} ny direction along the y axis
|
|
|
10408 |
* @param {int | float} nz direction along the z axis
|
|
|
10409 |
*
|
|
|
10410 |
* @returns none
|
|
|
10411 |
*
|
|
|
10412 |
* @see lights
|
|
|
10413 |
* @see ambientLight
|
|
|
10414 |
* @see pointLight
|
|
|
10415 |
* @see spotLight
|
|
|
10416 |
*/
|
|
|
10417 |
Drawing2D.prototype.directionalLight = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
10418 |
|
|
|
10419 |
Drawing3D.prototype.directionalLight = function(r, g, b, nx, ny, nz) {
|
|
|
10420 |
if (lightCount === PConstants.MAX_LIGHTS) {
|
|
|
10421 |
throw "can only create " + PConstants.MAX_LIGHTS + " lights";
|
|
|
10422 |
}
|
|
|
10423 |
|
|
|
10424 |
curContext.useProgram(programObject3D);
|
|
|
10425 |
|
|
|
10426 |
var mvm = new PMatrix3D();
|
|
|
10427 |
mvm.scale(1, -1, 1);
|
|
|
10428 |
mvm.apply(modelView.array());
|
|
|
10429 |
mvm = mvm.array();
|
|
|
10430 |
|
|
|
10431 |
// We need to multiply the direction by the model view matrix, but
|
|
|
10432 |
// the mult function checks the w component of the vector, if it isn't
|
|
|
10433 |
// present, it uses 1, so we manually multiply.
|
|
|
10434 |
var dir = [
|
|
|
10435 |
mvm[0] * nx + mvm[4] * ny + mvm[8] * nz,
|
|
|
10436 |
mvm[1] * nx + mvm[5] * ny + mvm[9] * nz,
|
|
|
10437 |
mvm[2] * nx + mvm[6] * ny + mvm[10] * nz
|
|
|
10438 |
];
|
|
|
10439 |
|
|
|
10440 |
// Instead of calling p.color, we do the calculations ourselves to
|
|
|
10441 |
// reduce property lookups.
|
|
|
10442 |
var col = color$4(r, g, b, 0);
|
|
|
10443 |
var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
|
|
|
10444 |
((col & PConstants.GREEN_MASK) >>> 8) / 255,
|
|
|
10445 |
(col & PConstants.BLUE_MASK) / 255 ];
|
|
|
10446 |
|
|
|
10447 |
uniformf("lights.color.3d." + lightCount, programObject3D, "lights" + lightCount + ".color", normalizedCol);
|
|
|
10448 |
uniformf("lights.position.3d." + lightCount, programObject3D, "lights" + lightCount + ".position", dir);
|
|
|
10449 |
uniformi("lights.type.3d." + lightCount, programObject3D, "lights" + lightCount + ".type", 1);
|
|
|
10450 |
uniformi("lightCount3d", programObject3D, "lightCount", ++lightCount);
|
|
|
10451 |
};
|
|
|
10452 |
|
|
|
10453 |
/**
|
|
|
10454 |
* Sets the falloff rates for point lights, spot lights, and ambient lights.
|
|
|
10455 |
* The parameters are used to determine the falloff with the following equation:
|
|
|
10456 |
*
|
|
|
10457 |
* d = distance from light position to vertex position
|
|
|
10458 |
* falloff = 1 / (CONSTANT + d * LINEAR + (d*d) * QUADRATIC)
|
|
|
10459 |
*
|
|
|
10460 |
* Like <b>fill()</b>, it affects only the elements which are created after it in the
|
|
|
10461 |
* code. The default value if <b>LightFalloff(1.0, 0.0, 0.0)</b>. Thinking about an
|
|
|
10462 |
* ambient light with a falloff can be tricky. It is used, for example, if you
|
|
|
10463 |
* wanted a region of your scene to be lit ambiently one color and another region
|
|
|
10464 |
* to be lit ambiently by another color, you would use an ambient light with location
|
|
|
10465 |
* and falloff. You can think of it as a point light that doesn't care which direction
|
|
|
10466 |
* a surface is facing.
|
|
|
10467 |
*
|
|
|
10468 |
* @param {int | float} constant constant value for determining falloff
|
|
|
10469 |
* @param {int | float} linear linear value for determining falloff
|
|
|
10470 |
* @param {int | float} quadratic quadratic value for determining falloff
|
|
|
10471 |
*
|
|
|
10472 |
* @returns none
|
|
|
10473 |
*
|
|
|
10474 |
* @see lights
|
|
|
10475 |
* @see ambientLight
|
|
|
10476 |
* @see pointLight
|
|
|
10477 |
* @see spotLight
|
|
|
10478 |
* @see lightSpecular
|
|
|
10479 |
*/
|
|
|
10480 |
Drawing2D.prototype.lightFalloff = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
10481 |
|
|
|
10482 |
Drawing3D.prototype.lightFalloff = function(constant, linear, quadratic) {
|
|
|
10483 |
curContext.useProgram(programObject3D);
|
|
|
10484 |
uniformf("falloff3d", programObject3D, "falloff", [constant, linear, quadratic]);
|
|
|
10485 |
};
|
|
|
10486 |
|
|
|
10487 |
/**
|
|
|
10488 |
* Sets the specular color for lights. Like <b>fill()</b>, it affects only the
|
|
|
10489 |
* elements which are created after it in the code. Specular refers to light
|
|
|
10490 |
* which bounces off a surface in a perferred direction (rather than bouncing
|
|
|
10491 |
* in all directions like a diffuse light) and is used for creating highlights.
|
|
|
10492 |
* The specular quality of a light interacts with the specular material qualities
|
|
|
10493 |
* set through the <b>specular()</b> and <b>shininess()</b> functions.
|
|
|
10494 |
*
|
|
|
10495 |
* @param {int | float} r red or hue value
|
|
|
10496 |
* @param {int | float} g green or hue value
|
|
|
10497 |
* @param {int | float} b blue or hue value
|
|
|
10498 |
*
|
|
|
10499 |
* @returns none
|
|
|
10500 |
*
|
|
|
10501 |
* @see lights
|
|
|
10502 |
* @see ambientLight
|
|
|
10503 |
* @see pointLight
|
|
|
10504 |
* @see spotLight
|
|
|
10505 |
*/
|
|
|
10506 |
Drawing2D.prototype.lightSpecular = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
10507 |
|
|
|
10508 |
Drawing3D.prototype.lightSpecular = function(r, g, b) {
|
|
|
10509 |
|
|
|
10510 |
// Instead of calling p.color, we do the calculations ourselves to
|
|
|
10511 |
// reduce property lookups.
|
|
|
10512 |
var col = color$4(r, g, b, 0);
|
|
|
10513 |
var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
|
|
|
10514 |
((col & PConstants.GREEN_MASK) >>> 8) / 255,
|
|
|
10515 |
(col & PConstants.BLUE_MASK) / 255 ];
|
|
|
10516 |
|
|
|
10517 |
curContext.useProgram(programObject3D);
|
|
|
10518 |
uniformf("specular3d", programObject3D, "specular", normalizedCol);
|
|
|
10519 |
};
|
|
|
10520 |
|
|
|
10521 |
/**
|
|
|
10522 |
* Sets the default ambient light, directional light, falloff, and specular
|
|
|
10523 |
* values. The defaults are ambientLight(128, 128, 128) and
|
|
|
10524 |
* directionalLight(128, 128, 128, 0, 0, -1), lightFalloff(1, 0, 0), and
|
|
|
10525 |
* lightSpecular(0, 0, 0). Lights need to be included in the draw() to remain
|
|
|
10526 |
* persistent in a looping program. Placing them in the setup() of a looping
|
|
|
10527 |
* program will cause them to only have an effect the first time through the
|
|
|
10528 |
* loop.
|
|
|
10529 |
*
|
|
|
10530 |
* @returns none
|
|
|
10531 |
*
|
|
|
10532 |
* @see ambientLight
|
|
|
10533 |
* @see directionalLight
|
|
|
10534 |
* @see pointLight
|
|
|
10535 |
* @see spotLight
|
|
|
10536 |
* @see noLights
|
|
|
10537 |
*
|
|
|
10538 |
*/
|
|
|
10539 |
p.lights = function() {
|
|
|
10540 |
p.ambientLight(128, 128, 128);
|
|
|
10541 |
p.directionalLight(128, 128, 128, 0, 0, -1);
|
|
|
10542 |
p.lightFalloff(1, 0, 0);
|
|
|
10543 |
p.lightSpecular(0, 0, 0);
|
|
|
10544 |
};
|
|
|
10545 |
|
|
|
10546 |
/**
|
|
|
10547 |
* Adds a point light. Lights need to be included in the <b>draw()</b> to remain
|
|
|
10548 |
* persistent in a looping program. Placing them in the <b>setup()</b> of a
|
|
|
10549 |
* looping program will cause them to only have an effect the first time through
|
|
|
10550 |
* the loop. The affect of the <b>r</b>, <b>g</b>, and <b>b</b> parameters
|
|
|
10551 |
* is determined by the current color mode. The <b>x</b>, <b>y</b>, and <b>z</b>
|
|
|
10552 |
* parameters set the position of the light.
|
|
|
10553 |
*
|
|
|
10554 |
* @param {int | float} r red or hue value
|
|
|
10555 |
* @param {int | float} g green or hue value
|
|
|
10556 |
* @param {int | float} b blue or hue value
|
|
|
10557 |
* @param {int | float} x x coordinate of the light
|
|
|
10558 |
* @param {int | float} y y coordinate of the light
|
|
|
10559 |
* @param {int | float} z z coordinate of the light
|
|
|
10560 |
*
|
|
|
10561 |
* @returns none
|
|
|
10562 |
*
|
|
|
10563 |
* @see lights
|
|
|
10564 |
* @see directionalLight
|
|
|
10565 |
* @see ambientLight
|
|
|
10566 |
* @see spotLight
|
|
|
10567 |
*/
|
|
|
10568 |
Drawing2D.prototype.pointLight = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
10569 |
|
|
|
10570 |
Drawing3D.prototype.pointLight = function(r, g, b, x, y, z) {
|
|
|
10571 |
if (lightCount === PConstants.MAX_LIGHTS) {
|
|
|
10572 |
throw "can only create " + PConstants.MAX_LIGHTS + " lights";
|
|
|
10573 |
}
|
|
|
10574 |
|
|
|
10575 |
// Place the point in view space once instead of once per vertex
|
|
|
10576 |
// in the shader.
|
|
|
10577 |
var pos = new PVector(x, y, z);
|
|
|
10578 |
var view = new PMatrix3D();
|
|
|
10579 |
view.scale(1, -1, 1);
|
|
|
10580 |
view.apply(modelView.array());
|
|
|
10581 |
view.mult(pos, pos);
|
|
|
10582 |
|
|
|
10583 |
// Instead of calling p.color, we do the calculations ourselves to
|
|
|
10584 |
// reduce property lookups.
|
|
|
10585 |
var col = color$4(r, g, b, 0);
|
|
|
10586 |
var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
|
|
|
10587 |
((col & PConstants.GREEN_MASK) >>> 8) / 255,
|
|
|
10588 |
(col & PConstants.BLUE_MASK) / 255 ];
|
|
|
10589 |
|
|
|
10590 |
curContext.useProgram(programObject3D);
|
|
|
10591 |
uniformf("lights.color.3d." + lightCount, programObject3D, "lights" + lightCount + ".color", normalizedCol);
|
|
|
10592 |
uniformf("lights.position.3d." + lightCount, programObject3D, "lights" + lightCount + ".position", pos.array());
|
|
|
10593 |
uniformi("lights.type.3d." + lightCount, programObject3D, "lights" + lightCount + ".type", 2);
|
|
|
10594 |
uniformi("lightCount3d", programObject3D, "lightCount", ++lightCount);
|
|
|
10595 |
};
|
|
|
10596 |
|
|
|
10597 |
/**
|
|
|
10598 |
* Disable all lighting. Lighting is turned off by default and enabled with
|
|
|
10599 |
* the lights() method. This function can be used to disable lighting so
|
|
|
10600 |
* that 2D geometry (which does not require lighting) can be drawn after a
|
|
|
10601 |
* set of lighted 3D geometry.
|
|
|
10602 |
*
|
|
|
10603 |
* @returns none
|
|
|
10604 |
*
|
|
|
10605 |
* @see lights
|
|
|
10606 |
*/
|
|
|
10607 |
Drawing2D.prototype.noLights = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
10608 |
|
|
|
10609 |
Drawing3D.prototype.noLights = function() {
|
|
|
10610 |
lightCount = 0;
|
|
|
10611 |
curContext.useProgram(programObject3D);
|
|
|
10612 |
uniformi("lightCount3d", programObject3D, "lightCount", lightCount);
|
|
|
10613 |
};
|
|
|
10614 |
|
|
|
10615 |
/**
|
|
|
10616 |
* Adds a spot light. Lights need to be included in the <b>draw()</b> to
|
|
|
10617 |
* remain persistent in a looping program. Placing them in the <b>setup()</b>
|
|
|
10618 |
* of a looping program will cause them to only have an effect the first time
|
|
|
10619 |
* through the loop. The affect of the <b>r</b>, <b>g</b>, and <b>b</b> parameters
|
|
|
10620 |
* is determined by the current color mode. The <b>x</b>, <b>y</b>, and <b>z</b>
|
|
|
10621 |
* parameters specify the position of the light and <b>nx</b>, <b>ny</b>, <b>nz</b>
|
|
|
10622 |
* specify the direction or light. The angle parameter affects <b>angle</b> of the
|
|
|
10623 |
* spotlight cone.
|
|
|
10624 |
*
|
|
|
10625 |
* @param {int | float} r red or hue value
|
|
|
10626 |
* @param {int | float} g green or hue value
|
|
|
10627 |
* @param {int | float} b blue or hue value
|
|
|
10628 |
* @param {int | float} x coordinate of the light
|
|
|
10629 |
* @param {int | float} y coordinate of the light
|
|
|
10630 |
* @param {int | float} z coordinate of the light
|
|
|
10631 |
* @param {int | float} nx direction along the x axis
|
|
|
10632 |
* @param {int | float} ny direction along the y axis
|
|
|
10633 |
* @param {int | float} nz direction along the z axis
|
|
|
10634 |
* @param {float} angle angle of the spotlight cone
|
|
|
10635 |
* @param {float} concentration exponent determining the center bias of the cone
|
|
|
10636 |
*
|
|
|
10637 |
* @returns none
|
|
|
10638 |
*
|
|
|
10639 |
* @see lights
|
|
|
10640 |
* @see directionalLight
|
|
|
10641 |
* @see ambientLight
|
|
|
10642 |
* @see pointLight
|
|
|
10643 |
*/
|
|
|
10644 |
Drawing2D.prototype.spotLight = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
10645 |
|
|
|
10646 |
Drawing3D.prototype.spotLight = function(r, g, b, x, y, z, nx, ny, nz, angle, concentration) {
|
|
|
10647 |
if (lightCount === PConstants.MAX_LIGHTS) {
|
|
|
10648 |
throw "can only create " + PConstants.MAX_LIGHTS + " lights";
|
|
|
10649 |
}
|
|
|
10650 |
|
|
|
10651 |
curContext.useProgram(programObject3D);
|
|
|
10652 |
|
|
|
10653 |
// multiply the position and direction by the model view matrix
|
|
|
10654 |
// once per object rather than once per vertex.
|
|
|
10655 |
var pos = new PVector(x, y, z);
|
|
|
10656 |
var mvm = new PMatrix3D();
|
|
|
10657 |
mvm.scale(1, -1, 1);
|
|
|
10658 |
mvm.apply(modelView.array());
|
|
|
10659 |
mvm.mult(pos, pos);
|
|
|
10660 |
|
|
|
10661 |
// Convert to array since we need to directly access the elements.
|
|
|
10662 |
mvm = mvm.array();
|
|
|
10663 |
|
|
|
10664 |
// We need to multiply the direction by the model view matrix, but
|
|
|
10665 |
// the mult function checks the w component of the vector, if it isn't
|
|
|
10666 |
// present, it uses 1, so we use a very small value as a work around.
|
|
|
10667 |
var dir = [
|
|
|
10668 |
mvm[0] * nx + mvm[4] * ny + mvm[8] * nz,
|
|
|
10669 |
mvm[1] * nx + mvm[5] * ny + mvm[9] * nz,
|
|
|
10670 |
mvm[2] * nx + mvm[6] * ny + mvm[10] * nz
|
|
|
10671 |
];
|
|
|
10672 |
|
|
|
10673 |
// Instead of calling p.color, we do the calculations ourselves to
|
|
|
10674 |
// reduce property lookups.
|
|
|
10675 |
var col = color$4(r, g, b, 0);
|
|
|
10676 |
var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
|
|
|
10677 |
((col & PConstants.GREEN_MASK) >>> 8) / 255,
|
|
|
10678 |
(col & PConstants.BLUE_MASK) / 255 ];
|
|
|
10679 |
|
|
|
10680 |
uniformf("lights.color.3d." + lightCount, programObject3D, "lights" + lightCount + ".color", normalizedCol);
|
|
|
10681 |
uniformf("lights.position.3d." + lightCount, programObject3D, "lights" + lightCount + ".position", pos.array());
|
|
|
10682 |
uniformf("lights.direction.3d." + lightCount, programObject3D, "lights" + lightCount + ".direction", dir);
|
|
|
10683 |
uniformf("lights.concentration.3d." + lightCount, programObject3D, "lights" + lightCount + ".concentration", concentration);
|
|
|
10684 |
uniformf("lights.angle.3d." + lightCount, programObject3D, "lights" + lightCount + ".angle", angle);
|
|
|
10685 |
uniformi("lights.type.3d." + lightCount, programObject3D, "lights" + lightCount + ".type", 3);
|
|
|
10686 |
uniformi("lightCount3d", programObject3D, "lightCount", ++lightCount);
|
|
|
10687 |
};
|
|
|
10688 |
|
|
|
10689 |
////////////////////////////////////////////////////////////////////////////
|
|
|
10690 |
// Camera functions
|
|
|
10691 |
////////////////////////////////////////////////////////////////////////////
|
|
|
10692 |
|
|
|
10693 |
/**
|
|
|
10694 |
* The <b>beginCamera()</b> and <b>endCamera()</b> functions enable advanced customization of the camera space.
|
|
|
10695 |
* The functions are useful if you want to more control over camera movement, however for most users, the <b>camera()</b>
|
|
|
10696 |
* function will be sufficient.<br /><br />The camera functions will replace any transformations (such as <b>rotate()</b>
|
|
|
10697 |
* or <b>translate()</b>) that occur before them in <b>draw()</b>, but they will not automatically replace the camera
|
|
|
10698 |
* transform itself. For this reason, camera functions should be placed at the beginning of <b>draw()</b> (so that
|
|
|
10699 |
* transformations happen afterwards), and the <b>camera()</b> function can be used after <b>beginCamera()</b> if
|
|
|
10700 |
* you want to reset the camera before applying transformations.<br /><br />This function sets the matrix mode to the
|
|
|
10701 |
* camera matrix so calls such as <b>translate()</b>, <b>rotate()</b>, applyMatrix() and resetMatrix() affect the camera.
|
|
|
10702 |
* <b>beginCamera()</b> should always be used with a following <b>endCamera()</b> and pairs of <b>beginCamera()</b> and
|
|
|
10703 |
* <b>endCamera()</b> cannot be nested.
|
|
|
10704 |
*
|
|
|
10705 |
* @see camera
|
|
|
10706 |
* @see endCamera
|
|
|
10707 |
* @see applyMatrix
|
|
|
10708 |
* @see resetMatrix
|
|
|
10709 |
* @see translate
|
|
|
10710 |
* @see rotate
|
|
|
10711 |
* @see scale
|
|
|
10712 |
*/
|
|
|
10713 |
Drawing2D.prototype.beginCamera = function() {
|
|
|
10714 |
throw ("beginCamera() is not available in 2D mode");
|
|
|
10715 |
};
|
|
|
10716 |
|
|
|
10717 |
Drawing3D.prototype.beginCamera = function() {
|
|
|
10718 |
if (manipulatingCamera) {
|
|
|
10719 |
throw ("You cannot call beginCamera() again before calling endCamera()");
|
|
|
10720 |
}
|
|
|
10721 |
manipulatingCamera = true;
|
|
|
10722 |
modelView = cameraInv;
|
|
|
10723 |
modelViewInv = cam;
|
|
|
10724 |
};
|
|
|
10725 |
|
|
|
10726 |
/**
|
|
|
10727 |
* The <b>beginCamera()</b> and <b>endCamera()</b> functions enable advanced customization of the camera space.
|
|
|
10728 |
* Please see the reference for <b>beginCamera()</b> for a description of how the functions are used.
|
|
|
10729 |
*
|
|
|
10730 |
* @see beginCamera
|
|
|
10731 |
*/
|
|
|
10732 |
Drawing2D.prototype.endCamera = function() {
|
|
|
10733 |
throw ("endCamera() is not available in 2D mode");
|
|
|
10734 |
};
|
|
|
10735 |
|
|
|
10736 |
Drawing3D.prototype.endCamera = function() {
|
|
|
10737 |
if (!manipulatingCamera) {
|
|
|
10738 |
throw ("You cannot call endCamera() before calling beginCamera()");
|
|
|
10739 |
}
|
|
|
10740 |
modelView.set(cam);
|
|
|
10741 |
modelViewInv.set(cameraInv);
|
|
|
10742 |
manipulatingCamera = false;
|
|
|
10743 |
};
|
|
|
10744 |
|
|
|
10745 |
/**
|
|
|
10746 |
* Sets the position of the camera through setting the eye position, the center of the scene, and which axis is facing
|
|
|
10747 |
* upward. Moving the eye position and the direction it is pointing (the center of the scene) allows the images to be
|
|
|
10748 |
* seen from different angles. The version without any parameters sets the camera to the default position, pointing to
|
|
|
10749 |
* the center of the display window with the Y axis as up. The default values are camera(width/2.0, height/2.0,
|
|
|
10750 |
* (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()
|
|
|
10751 |
* in OpenGL, but it first clears the current camera settings.
|
|
|
10752 |
*
|
|
|
10753 |
* @param {float} eyeX x-coordinate for the eye
|
|
|
10754 |
* @param {float} eyeY y-coordinate for the eye
|
|
|
10755 |
* @param {float} eyeZ z-coordinate for the eye
|
|
|
10756 |
* @param {float} centerX x-coordinate for the center of the scene
|
|
|
10757 |
* @param {float} centerY y-coordinate for the center of the scene
|
|
|
10758 |
* @param {float} centerZ z-coordinate for the center of the scene
|
|
|
10759 |
* @param {float} upX usually 0.0, 1.0, -1.0
|
|
|
10760 |
* @param {float} upY usually 0.0, 1.0, -1.0
|
|
|
10761 |
* @param {float} upZ usually 0.0, 1.0, -1.0
|
|
|
10762 |
*
|
|
|
10763 |
* @see beginCamera
|
|
|
10764 |
* @see endCamera
|
|
|
10765 |
* @see frustum
|
|
|
10766 |
*/
|
|
|
10767 |
p.camera = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
|
|
|
10768 |
if (eyeX === undef) {
|
|
|
10769 |
// Workaround if createGraphics is used.
|
|
|
10770 |
cameraX = p.width / 2;
|
|
|
10771 |
cameraY = p.height / 2;
|
|
|
10772 |
cameraZ = cameraY / Math.tan(cameraFOV / 2);
|
|
|
10773 |
eyeX = cameraX;
|
|
|
10774 |
eyeY = cameraY;
|
|
|
10775 |
eyeZ = cameraZ;
|
|
|
10776 |
centerX = cameraX;
|
|
|
10777 |
centerY = cameraY;
|
|
|
10778 |
centerZ = 0;
|
|
|
10779 |
upX = 0;
|
|
|
10780 |
upY = 1;
|
|
|
10781 |
upZ = 0;
|
|
|
10782 |
}
|
|
|
10783 |
|
|
|
10784 |
var z = new PVector(eyeX - centerX, eyeY - centerY, eyeZ - centerZ);
|
|
|
10785 |
var y = new PVector(upX, upY, upZ);
|
|
|
10786 |
z.normalize();
|
|
|
10787 |
var x = PVector.cross(y, z);
|
|
|
10788 |
y = PVector.cross(z, x);
|
|
|
10789 |
x.normalize();
|
|
|
10790 |
y.normalize();
|
|
|
10791 |
|
|
|
10792 |
var xX = x.x,
|
|
|
10793 |
xY = x.y,
|
|
|
10794 |
xZ = x.z;
|
|
|
10795 |
|
|
|
10796 |
var yX = y.x,
|
|
|
10797 |
yY = y.y,
|
|
|
10798 |
yZ = y.z;
|
|
|
10799 |
|
|
|
10800 |
var zX = z.x,
|
|
|
10801 |
zY = z.y,
|
|
|
10802 |
zZ = z.z;
|
|
|
10803 |
|
|
|
10804 |
cam.set(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1);
|
|
|
10805 |
|
|
|
10806 |
cam.translate(-eyeX, -eyeY, -eyeZ);
|
|
|
10807 |
|
|
|
10808 |
cameraInv.reset();
|
|
|
10809 |
cameraInv.invApply(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1);
|
|
|
10810 |
|
|
|
10811 |
cameraInv.translate(eyeX, eyeY, eyeZ);
|
|
|
10812 |
|
|
|
10813 |
modelView.set(cam);
|
|
|
10814 |
modelViewInv.set(cameraInv);
|
|
|
10815 |
};
|
|
|
10816 |
|
|
|
10817 |
/**
|
|
|
10818 |
* Sets a perspective projection applying foreshortening, making distant objects appear smaller than closer ones. The
|
|
|
10819 |
* parameters define a viewing volume with the shape of truncated pyramid. Objects near to the front of the volume appear
|
|
|
10820 |
* their actual size, while farther objects appear smaller. This projection simulates the perspective of the world more
|
|
|
10821 |
* accurately than orthographic projection. The version of perspective without parameters sets the default perspective and
|
|
|
10822 |
* the version with four parameters allows the programmer to set the area precisely. The default values are:
|
|
|
10823 |
* perspective(PI/3.0, width/height, cameraZ/10.0, cameraZ*10.0) where cameraZ is ((height/2.0) / tan(PI*60.0/360.0));
|
|
|
10824 |
*
|
|
|
10825 |
* @param {float} fov field-of-view angle (in radians) for vertical direction
|
|
|
10826 |
* @param {float} aspect ratio of width to height
|
|
|
10827 |
* @param {float} zNear z-position of nearest clipping plane
|
|
|
10828 |
* @param {float} zFar z-positions of farthest clipping plane
|
|
|
10829 |
*/
|
|
|
10830 |
p.perspective = function(fov, aspect, near, far) {
|
|
|
10831 |
if (arguments.length === 0) {
|
|
|
10832 |
//in case canvas is resized
|
|
|
10833 |
cameraY = curElement.height / 2;
|
|
|
10834 |
cameraZ = cameraY / Math.tan(cameraFOV / 2);
|
|
|
10835 |
cameraNear = cameraZ / 10;
|
|
|
10836 |
cameraFar = cameraZ * 10;
|
|
|
10837 |
cameraAspect = p.width / p.height;
|
|
|
10838 |
fov = cameraFOV;
|
|
|
10839 |
aspect = cameraAspect;
|
|
|
10840 |
near = cameraNear;
|
|
|
10841 |
far = cameraFar;
|
|
|
10842 |
}
|
|
|
10843 |
|
|
|
10844 |
var yMax, yMin, xMax, xMin;
|
|
|
10845 |
yMax = near * Math.tan(fov / 2);
|
|
|
10846 |
yMin = -yMax;
|
|
|
10847 |
xMax = yMax * aspect;
|
|
|
10848 |
xMin = yMin * aspect;
|
|
|
10849 |
p.frustum(xMin, xMax, yMin, yMax, near, far);
|
|
|
10850 |
};
|
|
|
10851 |
|
|
|
10852 |
/**
|
|
|
10853 |
* Sets a perspective matrix defined through the parameters. Works like glFrustum, except it wipes out the current
|
|
|
10854 |
* perspective matrix rather than muliplying itself with it.
|
|
|
10855 |
*
|
|
|
10856 |
* @param {float} left left coordinate of the clipping plane
|
|
|
10857 |
* @param {float} right right coordinate of the clipping plane
|
|
|
10858 |
* @param {float} bottom bottom coordinate of the clipping plane
|
|
|
10859 |
* @param {float} top top coordinate of the clipping plane
|
|
|
10860 |
* @param {float} near near coordinate of the clipping plane
|
|
|
10861 |
* @param {float} far far coordinate of the clipping plane
|
|
|
10862 |
*
|
|
|
10863 |
* @see beginCamera
|
|
|
10864 |
* @see camera
|
|
|
10865 |
* @see endCamera
|
|
|
10866 |
* @see perspective
|
|
|
10867 |
*/
|
|
|
10868 |
Drawing2D.prototype.frustum = function() {
|
|
|
10869 |
throw("Processing.js: frustum() is not supported in 2D mode");
|
|
|
10870 |
};
|
|
|
10871 |
|
|
|
10872 |
Drawing3D.prototype.frustum = function(left, right, bottom, top, near, far) {
|
|
|
10873 |
frustumMode = true;
|
|
|
10874 |
projection = new PMatrix3D();
|
|
|
10875 |
projection.set((2 * near) / (right - left), 0, (right + left) / (right - left),
|
|
|
10876 |
0, 0, (2 * near) / (top - bottom), (top + bottom) / (top - bottom),
|
|
|
10877 |
0, 0, 0, -(far + near) / (far - near), -(2 * far * near) / (far - near),
|
|
|
10878 |
0, 0, -1, 0);
|
|
|
10879 |
var proj = new PMatrix3D();
|
|
|
10880 |
proj.set(projection);
|
|
|
10881 |
proj.transpose();
|
|
|
10882 |
curContext.useProgram(programObject2D);
|
|
|
10883 |
uniformMatrix("projection2d", programObject2D, "projection", false, proj.array());
|
|
|
10884 |
curContext.useProgram(programObject3D);
|
|
|
10885 |
uniformMatrix("projection3d", programObject3D, "projection", false, proj.array());
|
|
|
10886 |
curContext.useProgram(programObjectUnlitShape);
|
|
|
10887 |
uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array());
|
|
|
10888 |
};
|
|
|
10889 |
|
|
|
10890 |
/**
|
|
|
10891 |
* Sets an orthographic projection and defines a parallel clipping volume. All objects with the same dimension appear
|
|
|
10892 |
* the same size, regardless of whether they are near or far from the camera. The parameters to this function specify
|
|
|
10893 |
* the clipping volume where left and right are the minimum and maximum x values, top and bottom are the minimum and
|
|
|
10894 |
* maximum y values, and near and far are the minimum and maximum z values. If no parameters are given, the default
|
|
|
10895 |
* is used: ortho(0, width, 0, height, -10, 10).
|
|
|
10896 |
*
|
|
|
10897 |
* @param {float} left left plane of the clipping volume
|
|
|
10898 |
* @param {float} right right plane of the clipping volume
|
|
|
10899 |
* @param {float} bottom bottom plane of the clipping volume
|
|
|
10900 |
* @param {float} top top plane of the clipping volume
|
|
|
10901 |
* @param {float} near maximum distance from the origin to the viewer
|
|
|
10902 |
* @param {float} far maximum distance from the origin away from the viewer
|
|
|
10903 |
*/
|
|
|
10904 |
p.ortho = function(left, right, bottom, top, near, far) {
|
|
|
10905 |
if (arguments.length === 0) {
|
|
|
10906 |
left = 0;
|
|
|
10907 |
right = p.width;
|
|
|
10908 |
bottom = 0;
|
|
|
10909 |
top = p.height;
|
|
|
10910 |
near = -10;
|
|
|
10911 |
far = 10;
|
|
|
10912 |
}
|
|
|
10913 |
|
|
|
10914 |
var x = 2 / (right - left);
|
|
|
10915 |
var y = 2 / (top - bottom);
|
|
|
10916 |
var z = -2 / (far - near);
|
|
|
10917 |
|
|
|
10918 |
var tx = -(right + left) / (right - left);
|
|
|
10919 |
var ty = -(top + bottom) / (top - bottom);
|
|
|
10920 |
var tz = -(far + near) / (far - near);
|
|
|
10921 |
|
|
|
10922 |
projection = new PMatrix3D();
|
|
|
10923 |
projection.set(x, 0, 0, tx, 0, y, 0, ty, 0, 0, z, tz, 0, 0, 0, 1);
|
|
|
10924 |
|
|
|
10925 |
var proj = new PMatrix3D();
|
|
|
10926 |
proj.set(projection);
|
|
|
10927 |
proj.transpose();
|
|
|
10928 |
curContext.useProgram(programObject2D);
|
|
|
10929 |
uniformMatrix("projection2d", programObject2D, "projection", false, proj.array());
|
|
|
10930 |
curContext.useProgram(programObject3D);
|
|
|
10931 |
uniformMatrix("projection3d", programObject3D, "projection", false, proj.array());
|
|
|
10932 |
curContext.useProgram(programObjectUnlitShape);
|
|
|
10933 |
uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array());
|
|
|
10934 |
frustumMode = false;
|
|
|
10935 |
};
|
|
|
10936 |
/**
|
|
|
10937 |
* The printProjection() prints the current projection matrix to the text window.
|
|
|
10938 |
*/
|
|
|
10939 |
p.printProjection = function() {
|
|
|
10940 |
projection.print();
|
|
|
10941 |
};
|
|
|
10942 |
/**
|
|
|
10943 |
* The printCamera() function prints the current camera matrix.
|
|
|
10944 |
*/
|
|
|
10945 |
p.printCamera = function() {
|
|
|
10946 |
cam.print();
|
|
|
10947 |
};
|
|
|
10948 |
|
|
|
10949 |
////////////////////////////////////////////////////////////////////////////
|
|
|
10950 |
// Shapes
|
|
|
10951 |
////////////////////////////////////////////////////////////////////////////
|
|
|
10952 |
/**
|
|
|
10953 |
* The box() function renders a box. A box is an extruded rectangle. A box with equal dimension on all sides is a cube.
|
|
|
10954 |
* Calling this function with only one parameter will create a cube.
|
|
|
10955 |
*
|
|
|
10956 |
* @param {int|float} w dimension of the box in the x-dimension
|
|
|
10957 |
* @param {int|float} h dimension of the box in the y-dimension
|
|
|
10958 |
* @param {int|float} d dimension of the box in the z-dimension
|
|
|
10959 |
*/
|
|
|
10960 |
Drawing2D.prototype.box = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
10961 |
|
|
|
10962 |
Drawing3D.prototype.box = function(w, h, d) {
|
|
|
10963 |
// user can uniformly scale the box by
|
|
|
10964 |
// passing in only one argument.
|
|
|
10965 |
if (!h || !d) {
|
|
|
10966 |
h = d = w;
|
|
|
10967 |
}
|
|
|
10968 |
|
|
|
10969 |
// Modeling transformation
|
|
|
10970 |
var model = new PMatrix3D();
|
|
|
10971 |
model.scale(w, h, d);
|
|
|
10972 |
|
|
|
10973 |
// viewing transformation needs to have Y flipped
|
|
|
10974 |
// becuase that's what Processing does.
|
|
|
10975 |
var view = new PMatrix3D();
|
|
|
10976 |
view.scale(1, -1, 1);
|
|
|
10977 |
view.apply(modelView.array());
|
|
|
10978 |
view.transpose();
|
|
|
10979 |
|
|
|
10980 |
if (doFill) {
|
|
|
10981 |
curContext.useProgram(programObject3D);
|
|
|
10982 |
uniformMatrix("model3d", programObject3D, "model", false, model.array());
|
|
|
10983 |
uniformMatrix("view3d", programObject3D, "view", false, view.array());
|
|
|
10984 |
// fix stitching problems. (lines get occluded by triangles
|
|
|
10985 |
// since they share the same depth values). This is not entirely
|
|
|
10986 |
// working, but it's a start for drawing the outline. So
|
|
|
10987 |
// developers can start playing around with styles.
|
|
|
10988 |
curContext.enable(curContext.POLYGON_OFFSET_FILL);
|
|
|
10989 |
curContext.polygonOffset(1, 1);
|
|
|
10990 |
uniformf("color3d", programObject3D, "color", fillStyle);
|
|
|
10991 |
|
|
|
10992 |
// Calculating the normal matrix can be expensive, so only
|
|
|
10993 |
// do it if it's necessary
|
|
|
10994 |
if(lightCount > 0){
|
|
|
10995 |
// Create the normal transformation matrix
|
|
|
10996 |
var v = new PMatrix3D();
|
|
|
10997 |
v.set(view);
|
|
|
10998 |
|
|
|
10999 |
var m = new PMatrix3D();
|
|
|
11000 |
m.set(model);
|
|
|
11001 |
|
|
|
11002 |
v.mult(m);
|
|
|
11003 |
|
|
|
11004 |
var normalMatrix = new PMatrix3D();
|
|
|
11005 |
normalMatrix.set(v);
|
|
|
11006 |
normalMatrix.invert();
|
|
|
11007 |
normalMatrix.transpose();
|
|
|
11008 |
|
|
|
11009 |
uniformMatrix("normalTransform3d", programObject3D, "normalTransform", false, normalMatrix.array());
|
|
|
11010 |
vertexAttribPointer("normal3d", programObject3D, "Normal", 3, boxNormBuffer);
|
|
|
11011 |
}
|
|
|
11012 |
else{
|
|
|
11013 |
disableVertexAttribPointer("normal3d", programObject3D, "Normal");
|
|
|
11014 |
}
|
|
|
11015 |
|
|
|
11016 |
vertexAttribPointer("vertex3d", programObject3D, "Vertex", 3, boxBuffer);
|
|
|
11017 |
|
|
|
11018 |
// Turn off per vertex colors
|
|
|
11019 |
disableVertexAttribPointer("aColor3d", programObject3D, "aColor");
|
|
|
11020 |
disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture");
|
|
|
11021 |
|
|
|
11022 |
curContext.drawArrays(curContext.TRIANGLES, 0, boxVerts.length / 3);
|
|
|
11023 |
curContext.disable(curContext.POLYGON_OFFSET_FILL);
|
|
|
11024 |
}
|
|
|
11025 |
|
|
|
11026 |
if (lineWidth > 0 && doStroke) {
|
|
|
11027 |
curContext.useProgram(programObject2D);
|
|
|
11028 |
uniformMatrix("model2d", programObject2D, "model", false, model.array());
|
|
|
11029 |
uniformMatrix("view2d", programObject2D, "view", false, view.array());
|
|
|
11030 |
uniformf("color2d", programObject2D, "color", strokeStyle);
|
|
|
11031 |
uniformi("picktype2d", programObject2D, "picktype", 0);
|
|
|
11032 |
vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, boxOutlineBuffer);
|
|
|
11033 |
disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
|
|
|
11034 |
curContext.drawArrays(curContext.LINES, 0, boxOutlineVerts.length / 3);
|
|
|
11035 |
}
|
|
|
11036 |
};
|
|
|
11037 |
|
|
|
11038 |
/**
|
|
|
11039 |
* The initSphere() function is a helper function used by <b>sphereDetail()</b>
|
|
|
11040 |
* This function creates and stores sphere vertices every time the user changes sphere detail.
|
|
|
11041 |
*
|
|
|
11042 |
* @see #sphereDetail
|
|
|
11043 |
*/
|
|
|
11044 |
var initSphere = function() {
|
|
|
11045 |
var i;
|
|
|
11046 |
sphereVerts = [];
|
|
|
11047 |
|
|
|
11048 |
for (i = 0; i < sphereDetailU; i++) {
|
|
|
11049 |
sphereVerts.push(0);
|
|
|
11050 |
sphereVerts.push(-1);
|
|
|
11051 |
sphereVerts.push(0);
|
|
|
11052 |
sphereVerts.push(sphereX[i]);
|
|
|
11053 |
sphereVerts.push(sphereY[i]);
|
|
|
11054 |
sphereVerts.push(sphereZ[i]);
|
|
|
11055 |
}
|
|
|
11056 |
sphereVerts.push(0);
|
|
|
11057 |
sphereVerts.push(-1);
|
|
|
11058 |
sphereVerts.push(0);
|
|
|
11059 |
sphereVerts.push(sphereX[0]);
|
|
|
11060 |
sphereVerts.push(sphereY[0]);
|
|
|
11061 |
sphereVerts.push(sphereZ[0]);
|
|
|
11062 |
|
|
|
11063 |
var v1, v11, v2;
|
|
|
11064 |
|
|
|
11065 |
// middle rings
|
|
|
11066 |
var voff = 0;
|
|
|
11067 |
for (i = 2; i < sphereDetailV; i++) {
|
|
|
11068 |
v1 = v11 = voff;
|
|
|
11069 |
voff += sphereDetailU;
|
|
|
11070 |
v2 = voff;
|
|
|
11071 |
for (var j = 0; j < sphereDetailU; j++) {
|
|
|
11072 |
sphereVerts.push(sphereX[v1]);
|
|
|
11073 |
sphereVerts.push(sphereY[v1]);
|
|
|
11074 |
sphereVerts.push(sphereZ[v1++]);
|
|
|
11075 |
sphereVerts.push(sphereX[v2]);
|
|
|
11076 |
sphereVerts.push(sphereY[v2]);
|
|
|
11077 |
sphereVerts.push(sphereZ[v2++]);
|
|
|
11078 |
}
|
|
|
11079 |
|
|
|
11080 |
// close each ring
|
|
|
11081 |
v1 = v11;
|
|
|
11082 |
v2 = voff;
|
|
|
11083 |
|
|
|
11084 |
sphereVerts.push(sphereX[v1]);
|
|
|
11085 |
sphereVerts.push(sphereY[v1]);
|
|
|
11086 |
sphereVerts.push(sphereZ[v1]);
|
|
|
11087 |
sphereVerts.push(sphereX[v2]);
|
|
|
11088 |
sphereVerts.push(sphereY[v2]);
|
|
|
11089 |
sphereVerts.push(sphereZ[v2]);
|
|
|
11090 |
}
|
|
|
11091 |
|
|
|
11092 |
// add the northern cap
|
|
|
11093 |
for (i = 0; i < sphereDetailU; i++) {
|
|
|
11094 |
v2 = voff + i;
|
|
|
11095 |
|
|
|
11096 |
sphereVerts.push(sphereX[v2]);
|
|
|
11097 |
sphereVerts.push(sphereY[v2]);
|
|
|
11098 |
sphereVerts.push(sphereZ[v2]);
|
|
|
11099 |
sphereVerts.push(0);
|
|
|
11100 |
sphereVerts.push(1);
|
|
|
11101 |
sphereVerts.push(0);
|
|
|
11102 |
}
|
|
|
11103 |
|
|
|
11104 |
sphereVerts.push(sphereX[voff]);
|
|
|
11105 |
sphereVerts.push(sphereY[voff]);
|
|
|
11106 |
sphereVerts.push(sphereZ[voff]);
|
|
|
11107 |
sphereVerts.push(0);
|
|
|
11108 |
sphereVerts.push(1);
|
|
|
11109 |
sphereVerts.push(0);
|
|
|
11110 |
|
|
|
11111 |
//set the buffer data
|
|
|
11112 |
curContext.bindBuffer(curContext.ARRAY_BUFFER, sphereBuffer);
|
|
|
11113 |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(sphereVerts), curContext.STATIC_DRAW);
|
|
|
11114 |
};
|
|
|
11115 |
|
|
|
11116 |
/**
|
|
|
11117 |
* The sphereDetail() function controls the detail used to render a sphere by adjusting the number of
|
|
|
11118 |
* vertices of the sphere mesh. The default resolution is 30, which creates
|
|
|
11119 |
* a fairly detailed sphere definition with vertices every 360/30 = 12
|
|
|
11120 |
* degrees. If you're going to render a great number of spheres per frame,
|
|
|
11121 |
* it is advised to reduce the level of detail using this function.
|
|
|
11122 |
* The setting stays active until <b>sphereDetail()</b> is called again with
|
|
|
11123 |
* a new parameter and so should <i>not</i> be called prior to every
|
|
|
11124 |
* <b>sphere()</b> statement, unless you wish to render spheres with
|
|
|
11125 |
* different settings, e.g. using less detail for smaller spheres or ones
|
|
|
11126 |
* further away from the camera. To control the detail of the horizontal
|
|
|
11127 |
* and vertical resolution independently, use the version of the functions
|
|
|
11128 |
* with two parameters. Calling this function with one parameter sets the number of segments
|
|
|
11129 |
*(minimum of 3) used per full circle revolution. This is equivalent to calling the function with
|
|
|
11130 |
* two identical values.
|
|
|
11131 |
*
|
|
|
11132 |
* @param {int} ures number of segments used horizontally (longitudinally) per full circle revolution
|
|
|
11133 |
* @param {int} vres number of segments used vertically (latitudinally) from top to bottom
|
|
|
11134 |
*
|
|
|
11135 |
* @see #sphere()
|
|
|
11136 |
*/
|
|
|
11137 |
p.sphereDetail = function(ures, vres) {
|
|
|
11138 |
var i;
|
|
|
11139 |
|
|
|
11140 |
if (arguments.length === 1) {
|
|
|
11141 |
ures = vres = arguments[0];
|
|
|
11142 |
}
|
|
|
11143 |
|
|
|
11144 |
if (ures < 3) {
|
|
|
11145 |
ures = 3;
|
|
|
11146 |
} // force a minimum res
|
|
|
11147 |
if (vres < 2) {
|
|
|
11148 |
vres = 2;
|
|
|
11149 |
} // force a minimum res
|
|
|
11150 |
// if it hasn't changed do nothing
|
|
|
11151 |
if ((ures === sphereDetailU) && (vres === sphereDetailV)) {
|
|
|
11152 |
return;
|
|
|
11153 |
}
|
|
|
11154 |
|
|
|
11155 |
var delta = PConstants.SINCOS_LENGTH / ures;
|
|
|
11156 |
var cx = new Float32Array(ures);
|
|
|
11157 |
var cz = new Float32Array(ures);
|
|
|
11158 |
// calc unit circle in XZ plane
|
|
|
11159 |
for (i = 0; i < ures; i++) {
|
|
|
11160 |
cx[i] = cosLUT[((i * delta) % PConstants.SINCOS_LENGTH) | 0];
|
|
|
11161 |
cz[i] = sinLUT[((i * delta) % PConstants.SINCOS_LENGTH) | 0];
|
|
|
11162 |
}
|
|
|
11163 |
|
|
|
11164 |
// computing vertexlist
|
|
|
11165 |
// vertexlist starts at south pole
|
|
|
11166 |
var vertCount = ures * (vres - 1) + 2;
|
|
|
11167 |
var currVert = 0;
|
|
|
11168 |
|
|
|
11169 |
// re-init arrays to store vertices
|
|
|
11170 |
sphereX = new Float32Array(vertCount);
|
|
|
11171 |
sphereY = new Float32Array(vertCount);
|
|
|
11172 |
sphereZ = new Float32Array(vertCount);
|
|
|
11173 |
|
|
|
11174 |
var angle_step = (PConstants.SINCOS_LENGTH * 0.5) / vres;
|
|
|
11175 |
var angle = angle_step;
|
|
|
11176 |
|
|
|
11177 |
// step along Y axis
|
|
|
11178 |
for (i = 1; i < vres; i++) {
|
|
|
11179 |
var curradius = sinLUT[(angle % PConstants.SINCOS_LENGTH) | 0];
|
|
|
11180 |
var currY = -cosLUT[(angle % PConstants.SINCOS_LENGTH) | 0];
|
|
|
11181 |
for (var j = 0; j < ures; j++) {
|
|
|
11182 |
sphereX[currVert] = cx[j] * curradius;
|
|
|
11183 |
sphereY[currVert] = currY;
|
|
|
11184 |
sphereZ[currVert++] = cz[j] * curradius;
|
|
|
11185 |
}
|
|
|
11186 |
angle += angle_step;
|
|
|
11187 |
}
|
|
|
11188 |
sphereDetailU = ures;
|
|
|
11189 |
sphereDetailV = vres;
|
|
|
11190 |
|
|
|
11191 |
// make the sphere verts and norms
|
|
|
11192 |
initSphere();
|
|
|
11193 |
};
|
|
|
11194 |
|
|
|
11195 |
/**
|
|
|
11196 |
* The sphere() function draws a sphere with radius r centered at coordinate 0, 0, 0.
|
|
|
11197 |
* A sphere is a hollow ball made from tessellated triangles.
|
|
|
11198 |
*
|
|
|
11199 |
* @param {int|float} r the radius of the sphere
|
|
|
11200 |
*/
|
|
|
11201 |
Drawing2D.prototype.sphere = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
11202 |
|
|
|
11203 |
Drawing3D.prototype.sphere = function() {
|
|
|
11204 |
var sRad = arguments[0];
|
|
|
11205 |
|
|
|
11206 |
if ((sphereDetailU < 3) || (sphereDetailV < 2)) {
|
|
|
11207 |
p.sphereDetail(30);
|
|
|
11208 |
}
|
|
|
11209 |
|
|
|
11210 |
// Modeling transformation
|
|
|
11211 |
var model = new PMatrix3D();
|
|
|
11212 |
model.scale(sRad, sRad, sRad);
|
|
|
11213 |
|
|
|
11214 |
// viewing transformation needs to have Y flipped
|
|
|
11215 |
// becuase that's what Processing does.
|
|
|
11216 |
var view = new PMatrix3D();
|
|
|
11217 |
view.scale(1, -1, 1);
|
|
|
11218 |
view.apply(modelView.array());
|
|
|
11219 |
view.transpose();
|
|
|
11220 |
|
|
|
11221 |
if (doFill) {
|
|
|
11222 |
// Calculating the normal matrix can be expensive, so only
|
|
|
11223 |
// do it if it's necessary
|
|
|
11224 |
if(lightCount > 0){
|
|
|
11225 |
// Create a normal transformation matrix
|
|
|
11226 |
var v = new PMatrix3D();
|
|
|
11227 |
v.set(view);
|
|
|
11228 |
|
|
|
11229 |
var m = new PMatrix3D();
|
|
|
11230 |
m.set(model);
|
|
|
11231 |
|
|
|
11232 |
v.mult(m);
|
|
|
11233 |
|
|
|
11234 |
var normalMatrix = new PMatrix3D();
|
|
|
11235 |
normalMatrix.set(v);
|
|
|
11236 |
normalMatrix.invert();
|
|
|
11237 |
normalMatrix.transpose();
|
|
|
11238 |
|
|
|
11239 |
uniformMatrix("normalTransform3d", programObject3D, "normalTransform", false, normalMatrix.array());
|
|
|
11240 |
vertexAttribPointer("normal3d", programObject3D, "Normal", 3, sphereBuffer);
|
|
|
11241 |
}
|
|
|
11242 |
else{
|
|
|
11243 |
disableVertexAttribPointer("normal3d", programObject3D, "Normal");
|
|
|
11244 |
}
|
|
|
11245 |
|
|
|
11246 |
curContext.useProgram(programObject3D);
|
|
|
11247 |
disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture");
|
|
|
11248 |
|
|
|
11249 |
uniformMatrix("model3d", programObject3D, "model", false, model.array());
|
|
|
11250 |
uniformMatrix("view3d", programObject3D, "view", false, view.array());
|
|
|
11251 |
vertexAttribPointer("vertex3d", programObject3D, "Vertex", 3, sphereBuffer);
|
|
|
11252 |
|
|
|
11253 |
// Turn off per vertex colors
|
|
|
11254 |
disableVertexAttribPointer("aColor3d", programObject3D, "aColor");
|
|
|
11255 |
|
|
|
11256 |
// fix stitching problems. (lines get occluded by triangles
|
|
|
11257 |
// since they share the same depth values). This is not entirely
|
|
|
11258 |
// working, but it's a start for drawing the outline. So
|
|
|
11259 |
// developers can start playing around with styles.
|
|
|
11260 |
curContext.enable(curContext.POLYGON_OFFSET_FILL);
|
|
|
11261 |
curContext.polygonOffset(1, 1);
|
|
|
11262 |
uniformf("color3d", programObject3D, "color", fillStyle);
|
|
|
11263 |
curContext.drawArrays(curContext.TRIANGLE_STRIP, 0, sphereVerts.length / 3);
|
|
|
11264 |
curContext.disable(curContext.POLYGON_OFFSET_FILL);
|
|
|
11265 |
}
|
|
|
11266 |
|
|
|
11267 |
if (lineWidth > 0 && doStroke) {
|
|
|
11268 |
curContext.useProgram(programObject2D);
|
|
|
11269 |
uniformMatrix("model2d", programObject2D, "model", false, model.array());
|
|
|
11270 |
uniformMatrix("view2d", programObject2D, "view", false, view.array());
|
|
|
11271 |
vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, sphereBuffer);
|
|
|
11272 |
disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
|
|
|
11273 |
uniformf("color2d", programObject2D, "color", strokeStyle);
|
|
|
11274 |
uniformi("picktype2d", programObject2D, "picktype", 0);
|
|
|
11275 |
curContext.drawArrays(curContext.LINE_STRIP, 0, sphereVerts.length / 3);
|
|
|
11276 |
}
|
|
|
11277 |
};
|
|
|
11278 |
|
|
|
11279 |
////////////////////////////////////////////////////////////////////////////
|
|
|
11280 |
// Coordinates
|
|
|
11281 |
////////////////////////////////////////////////////////////////////////////
|
|
|
11282 |
|
|
|
11283 |
/**
|
|
|
11284 |
* Returns the three-dimensional X, Y, Z position in model space. This returns
|
|
|
11285 |
* the X value for a given coordinate based on the current set of transformations
|
|
|
11286 |
* (scale, rotate, translate, etc.) The X value can be used to place an object
|
|
|
11287 |
* in space relative to the location of the original point once the transformations
|
|
|
11288 |
* are no longer in use.<br />
|
|
|
11289 |
* <br />
|
|
|
11290 |
*
|
|
|
11291 |
* @param {int | float} x 3D x coordinate to be mapped
|
|
|
11292 |
* @param {int | float} y 3D y coordinate to be mapped
|
|
|
11293 |
* @param {int | float} z 3D z coordinate to be mapped
|
|
|
11294 |
*
|
|
|
11295 |
* @returns {float}
|
|
|
11296 |
*
|
|
|
11297 |
* @see modelY
|
|
|
11298 |
* @see modelZ
|
|
|
11299 |
*/
|
|
|
11300 |
p.modelX = function(x, y, z) {
|
|
|
11301 |
var mv = modelView.array();
|
|
|
11302 |
var ci = cameraInv.array();
|
|
|
11303 |
|
|
|
11304 |
var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
|
|
|
11305 |
var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
|
|
|
11306 |
var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
|
|
|
11307 |
var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
|
|
|
11308 |
|
|
|
11309 |
var ox = ci[0] * ax + ci[1] * ay + ci[2] * az + ci[3] * aw;
|
|
|
11310 |
var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
|
|
|
11311 |
|
|
|
11312 |
return (ow !== 0) ? ox / ow : ox;
|
|
|
11313 |
};
|
|
|
11314 |
|
|
|
11315 |
/**
|
|
|
11316 |
* Returns the three-dimensional X, Y, Z position in model space. This returns
|
|
|
11317 |
* the Y value for a given coordinate based on the current set of transformations
|
|
|
11318 |
* (scale, rotate, translate, etc.) The Y value can be used to place an object in
|
|
|
11319 |
* space relative to the location of the original point once the transformations
|
|
|
11320 |
* are no longer in use.<br />
|
|
|
11321 |
* <br />
|
|
|
11322 |
*
|
|
|
11323 |
* @param {int | float} x 3D x coordinate to be mapped
|
|
|
11324 |
* @param {int | float} y 3D y coordinate to be mapped
|
|
|
11325 |
* @param {int | float} z 3D z coordinate to be mapped
|
|
|
11326 |
*
|
|
|
11327 |
* @returns {float}
|
|
|
11328 |
*
|
|
|
11329 |
* @see modelX
|
|
|
11330 |
* @see modelZ
|
|
|
11331 |
*/
|
|
|
11332 |
p.modelY = function(x, y, z) {
|
|
|
11333 |
var mv = modelView.array();
|
|
|
11334 |
var ci = cameraInv.array();
|
|
|
11335 |
|
|
|
11336 |
var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
|
|
|
11337 |
var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
|
|
|
11338 |
var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
|
|
|
11339 |
var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
|
|
|
11340 |
|
|
|
11341 |
var oy = ci[4] * ax + ci[5] * ay + ci[6] * az + ci[7] * aw;
|
|
|
11342 |
var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
|
|
|
11343 |
|
|
|
11344 |
return (ow !== 0) ? oy / ow : oy;
|
|
|
11345 |
};
|
|
|
11346 |
|
|
|
11347 |
/**
|
|
|
11348 |
* Returns the three-dimensional X, Y, Z position in model space. This returns
|
|
|
11349 |
* the Z value for a given coordinate based on the current set of transformations
|
|
|
11350 |
* (scale, rotate, translate, etc.) The Z value can be used to place an object in
|
|
|
11351 |
* space relative to the location of the original point once the transformations
|
|
|
11352 |
* are no longer in use.
|
|
|
11353 |
*
|
|
|
11354 |
* @param {int | float} x 3D x coordinate to be mapped
|
|
|
11355 |
* @param {int | float} y 3D y coordinate to be mapped
|
|
|
11356 |
* @param {int | float} z 3D z coordinate to be mapped
|
|
|
11357 |
*
|
|
|
11358 |
* @returns {float}
|
|
|
11359 |
*
|
|
|
11360 |
* @see modelX
|
|
|
11361 |
* @see modelY
|
|
|
11362 |
*/
|
|
|
11363 |
p.modelZ = function(x, y, z) {
|
|
|
11364 |
var mv = modelView.array();
|
|
|
11365 |
var ci = cameraInv.array();
|
|
|
11366 |
|
|
|
11367 |
var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
|
|
|
11368 |
var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
|
|
|
11369 |
var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
|
|
|
11370 |
var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
|
|
|
11371 |
|
|
|
11372 |
var oz = ci[8] * ax + ci[9] * ay + ci[10] * az + ci[11] * aw;
|
|
|
11373 |
var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
|
|
|
11374 |
|
|
|
11375 |
return (ow !== 0) ? oz / ow : oz;
|
|
|
11376 |
};
|
|
|
11377 |
|
|
|
11378 |
////////////////////////////////////////////////////////////////////////////
|
|
|
11379 |
// Material Properties
|
|
|
11380 |
////////////////////////////////////////////////////////////////////////////
|
|
|
11381 |
|
|
|
11382 |
/**
|
|
|
11383 |
* Sets the ambient reflectance for shapes drawn to the screen. This is
|
|
|
11384 |
* combined with the ambient light component of environment. The color
|
|
|
11385 |
* components set through the parameters define the reflectance. For example in
|
|
|
11386 |
* the default color mode, setting v1=255, v2=126, v3=0, would cause all the
|
|
|
11387 |
* red light to reflect and half of the green light to reflect. Used in combination
|
|
|
11388 |
* with <b>emissive()</b>, <b>specular()</b>, and <b>shininess()</b> in setting
|
|
|
11389 |
* the materal properties of shapes.
|
|
|
11390 |
*
|
|
|
11391 |
* @param {int | float} gray
|
|
|
11392 |
*
|
|
|
11393 |
* @returns none
|
|
|
11394 |
*
|
|
|
11395 |
* @see emissive
|
|
|
11396 |
* @see specular
|
|
|
11397 |
* @see shininess
|
|
|
11398 |
*/
|
|
|
11399 |
Drawing2D.prototype.ambient = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
11400 |
|
|
|
11401 |
Drawing3D.prototype.ambient = function(v1, v2, v3) {
|
|
|
11402 |
curContext.useProgram(programObject3D);
|
|
|
11403 |
uniformi("usingMat3d", programObject3D, "usingMat", true);
|
|
|
11404 |
var col = p.color(v1, v2, v3);
|
|
|
11405 |
uniformf("mat_ambient3d", programObject3D, "mat_ambient", p.color.toGLArray(col).slice(0, 3));
|
|
|
11406 |
};
|
|
|
11407 |
|
|
|
11408 |
/**
|
|
|
11409 |
* Sets the emissive color of the material used for drawing shapes
|
|
|
11410 |
* drawn to the screen. Used in combination with ambient(), specular(),
|
|
|
11411 |
* and shininess() in setting the material properties of shapes.
|
|
|
11412 |
*
|
|
|
11413 |
* Can be called in the following ways:
|
|
|
11414 |
*
|
|
|
11415 |
* emissive(gray)
|
|
|
11416 |
* @param {int | float} gray number specifying value between white and black
|
|
|
11417 |
*
|
|
|
11418 |
* emissive(color)
|
|
|
11419 |
* @param {color} color any value of the color datatype
|
|
|
11420 |
*
|
|
|
11421 |
* emissive(v1, v2, v3)
|
|
|
11422 |
* @param {int | float} v1 red or hue value
|
|
|
11423 |
* @param {int | float} v2 green or saturation value
|
|
|
11424 |
* @param {int | float} v3 blue or brightness value
|
|
|
11425 |
*
|
|
|
11426 |
* @returns none
|
|
|
11427 |
*
|
|
|
11428 |
* @see ambient
|
|
|
11429 |
* @see specular
|
|
|
11430 |
* @see shininess
|
|
|
11431 |
*/
|
|
|
11432 |
Drawing2D.prototype.emissive = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
11433 |
|
|
|
11434 |
Drawing3D.prototype.emissive = function(v1, v2, v3) {
|
|
|
11435 |
curContext.useProgram(programObject3D);
|
|
|
11436 |
uniformi("usingMat3d", programObject3D, "usingMat", true);
|
|
|
11437 |
var col = p.color(v1, v2, v3);
|
|
|
11438 |
uniformf("mat_emissive3d", programObject3D, "mat_emissive", p.color.toGLArray(col).slice(0, 3));
|
|
|
11439 |
};
|
|
|
11440 |
|
|
|
11441 |
/**
|
|
|
11442 |
* Sets the amount of gloss in the surface of shapes. Used in combination with
|
|
|
11443 |
* <b>ambient()</b>, <b>specular()</b>, and <b>emissive()</b> in setting the
|
|
|
11444 |
* material properties of shapes.
|
|
|
11445 |
*
|
|
|
11446 |
* @param {float} shine degree of shininess
|
|
|
11447 |
*
|
|
|
11448 |
* @returns none
|
|
|
11449 |
*/
|
|
|
11450 |
Drawing2D.prototype.shininess = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
11451 |
|
|
|
11452 |
Drawing3D.prototype.shininess = function(shine) {
|
|
|
11453 |
curContext.useProgram(programObject3D);
|
|
|
11454 |
uniformi("usingMat3d", programObject3D, "usingMat", true);
|
|
|
11455 |
uniformf("shininess3d", programObject3D, "shininess", shine);
|
|
|
11456 |
};
|
|
|
11457 |
|
|
|
11458 |
/**
|
|
|
11459 |
* Sets the specular color of the materials used for shapes drawn to the screen,
|
|
|
11460 |
* which sets the color of hightlights. Specular refers to light which bounces
|
|
|
11461 |
* off a surface in a perferred direction (rather than bouncing in all directions
|
|
|
11462 |
* like a diffuse light). Used in combination with emissive(), ambient(), and
|
|
|
11463 |
* shininess() in setting the material properties of shapes.
|
|
|
11464 |
*
|
|
|
11465 |
* Can be called in the following ways:
|
|
|
11466 |
*
|
|
|
11467 |
* specular(gray)
|
|
|
11468 |
* @param {int | float} gray number specifying value between white and black
|
|
|
11469 |
*
|
|
|
11470 |
* specular(gray, alpha)
|
|
|
11471 |
* @param {int | float} gray number specifying value between white and black
|
|
|
11472 |
* @param {int | float} alpha opacity
|
|
|
11473 |
*
|
|
|
11474 |
* specular(color)
|
|
|
11475 |
* @param {color} color any value of the color datatype
|
|
|
11476 |
*
|
|
|
11477 |
* specular(v1, v2, v3)
|
|
|
11478 |
* @param {int | float} v1 red or hue value
|
|
|
11479 |
* @param {int | float} v2 green or saturation value
|
|
|
11480 |
* @param {int | float} v3 blue or brightness value
|
|
|
11481 |
*
|
|
|
11482 |
* specular(v1, v2, v3, alpha)
|
|
|
11483 |
* @param {int | float} v1 red or hue value
|
|
|
11484 |
* @param {int | float} v2 green or saturation value
|
|
|
11485 |
* @param {int | float} v3 blue or brightness value
|
|
|
11486 |
* @param {int | float} alpha opacity
|
|
|
11487 |
*
|
|
|
11488 |
* @returns none
|
|
|
11489 |
*
|
|
|
11490 |
* @see ambient
|
|
|
11491 |
* @see emissive
|
|
|
11492 |
* @see shininess
|
|
|
11493 |
*/
|
|
|
11494 |
Drawing2D.prototype.specular = DrawingShared.prototype.a3DOnlyFunction;
|
|
|
11495 |
|
|
|
11496 |
Drawing3D.prototype.specular = function(v1, v2, v3) {
|
|
|
11497 |
curContext.useProgram(programObject3D);
|
|
|
11498 |
uniformi("usingMat3d", programObject3D, "usingMat", true);
|
|
|
11499 |
var col = p.color(v1, v2, v3);
|
|
|
11500 |
uniformf("mat_specular3d", programObject3D, "mat_specular", p.color.toGLArray(col).slice(0, 3));
|
|
|
11501 |
};
|
|
|
11502 |
|
|
|
11503 |
////////////////////////////////////////////////////////////////////////////
|
|
|
11504 |
// Coordinates
|
|
|
11505 |
////////////////////////////////////////////////////////////////////////////
|
|
|
11506 |
|
|
|
11507 |
/**
|
|
|
11508 |
* Takes a three-dimensional X, Y, Z position and returns the X value for
|
|
|
11509 |
* where it will appear on a (two-dimensional) screen.
|
|
|
11510 |
*
|
|
|
11511 |
* @param {int | float} x 3D x coordinate to be mapped
|
|
|
11512 |
* @param {int | float} y 3D y coordinate to be mapped
|
|
|
11513 |
* @param {int | float} z 3D z optional coordinate to be mapped
|
|
|
11514 |
*
|
|
|
11515 |
* @returns {float}
|
|
|
11516 |
*
|
|
|
11517 |
* @see screenY
|
|
|
11518 |
* @see screenZ
|
|
|
11519 |
*/
|
|
|
11520 |
p.screenX = function( x, y, z ) {
|
|
|
11521 |
var mv = modelView.array();
|
|
|
11522 |
if( mv.length === 16 )
|
|
|
11523 |
{
|
|
|
11524 |
var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
|
|
|
11525 |
var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
|
|
|
11526 |
var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
|
|
|
11527 |
var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
|
|
|
11528 |
|
|
|
11529 |
var pj = projection.array();
|
|
|
11530 |
|
|
|
11531 |
var ox = pj[ 0]*ax + pj[ 1]*ay + pj[ 2]*az + pj[ 3]*aw;
|
|
|
11532 |
var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
|
|
|
11533 |
|
|
|
11534 |
if ( ow !== 0 ){
|
|
|
11535 |
ox /= ow;
|
|
|
11536 |
}
|
|
|
11537 |
return p.width * ( 1 + ox ) / 2.0;
|
|
|
11538 |
}
|
|
|
11539 |
// We assume that we're in 2D
|
|
|
11540 |
return modelView.multX(x, y);
|
|
|
11541 |
};
|
|
|
11542 |
|
|
|
11543 |
/**
|
|
|
11544 |
* Takes a three-dimensional X, Y, Z position and returns the Y value for
|
|
|
11545 |
* where it will appear on a (two-dimensional) screen.
|
|
|
11546 |
*
|
|
|
11547 |
* @param {int | float} x 3D x coordinate to be mapped
|
|
|
11548 |
* @param {int | float} y 3D y coordinate to be mapped
|
|
|
11549 |
* @param {int | float} z 3D z optional coordinate to be mapped
|
|
|
11550 |
*
|
|
|
11551 |
* @returns {float}
|
|
|
11552 |
*
|
|
|
11553 |
* @see screenX
|
|
|
11554 |
* @see screenZ
|
|
|
11555 |
*/
|
|
|
11556 |
p.screenY = function screenY( x, y, z ) {
|
|
|
11557 |
var mv = modelView.array();
|
|
|
11558 |
if( mv.length === 16 ) {
|
|
|
11559 |
var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
|
|
|
11560 |
var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
|
|
|
11561 |
var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
|
|
|
11562 |
var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
|
|
|
11563 |
|
|
|
11564 |
var pj = projection.array();
|
|
|
11565 |
|
|
|
11566 |
var oy = pj[ 4]*ax + pj[ 5]*ay + pj[ 6]*az + pj[ 7]*aw;
|
|
|
11567 |
var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
|
|
|
11568 |
|
|
|
11569 |
if ( ow !== 0 ){
|
|
|
11570 |
oy /= ow;
|
|
|
11571 |
}
|
|
|
11572 |
return p.height * ( 1 + oy ) / 2.0;
|
|
|
11573 |
}
|
|
|
11574 |
// We assume that we're in 2D
|
|
|
11575 |
return modelView.multY(x, y);
|
|
|
11576 |
};
|
|
|
11577 |
|
|
|
11578 |
/**
|
|
|
11579 |
* Takes a three-dimensional X, Y, Z position and returns the Z value for
|
|
|
11580 |
* where it will appear on a (two-dimensional) screen.
|
|
|
11581 |
*
|
|
|
11582 |
* @param {int | float} x 3D x coordinate to be mapped
|
|
|
11583 |
* @param {int | float} y 3D y coordinate to be mapped
|
|
|
11584 |
* @param {int | float} z 3D z coordinate to be mapped
|
|
|
11585 |
*
|
|
|
11586 |
* @returns {float}
|
|
|
11587 |
*
|
|
|
11588 |
* @see screenX
|
|
|
11589 |
* @see screenY
|
|
|
11590 |
*/
|
|
|
11591 |
p.screenZ = function screenZ( x, y, z ) {
|
|
|
11592 |
var mv = modelView.array();
|
|
|
11593 |
if( mv.length !== 16 ) {
|
|
|
11594 |
return 0;
|
|
|
11595 |
}
|
|
|
11596 |
|
|
|
11597 |
var pj = projection.array();
|
|
|
11598 |
|
|
|
11599 |
var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
|
|
|
11600 |
var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
|
|
|
11601 |
var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
|
|
|
11602 |
var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
|
|
|
11603 |
|
|
|
11604 |
var oz = pj[ 8]*ax + pj[ 9]*ay + pj[10]*az + pj[11]*aw;
|
|
|
11605 |
var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
|
|
|
11606 |
|
|
|
11607 |
if ( ow !== 0 ) {
|
|
|
11608 |
oz /= ow;
|
|
|
11609 |
}
|
|
|
11610 |
return ( oz + 1 ) / 2.0;
|
|
|
11611 |
};
|
|
|
11612 |
|
|
|
11613 |
////////////////////////////////////////////////////////////////////////////
|
|
|
11614 |
// Style functions
|
|
|
11615 |
////////////////////////////////////////////////////////////////////////////
|
|
|
11616 |
/**
|
|
|
11617 |
* 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.
|
|
|
11618 |
* This color is either specified in terms of the RGB or HSB color depending on the current <b>colorMode()</b>
|
|
|
11619 |
*(the default color space is RGB, with each value in the range from 0 to 255).
|
|
|
11620 |
* <br><br>When using hexadecimal notation to specify a color, use "#" or "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA).
|
|
|
11621 |
* 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",
|
|
|
11622 |
* 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.
|
|
|
11623 |
* <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.
|
|
|
11624 |
* <br><br>To change the color of an image (or a texture), use tint().
|
|
|
11625 |
*
|
|
|
11626 |
* @param {int|float} gray number specifying value between white and black
|
|
|
11627 |
* @param {int|float} value1 red or hue value
|
|
|
11628 |
* @param {int|float} value2 green or saturation value
|
|
|
11629 |
* @param {int|float} value3 blue or brightness value
|
|
|
11630 |
* @param {int|float} alpha opacity of the fill
|
|
|
11631 |
* @param {Color} color any value of the color datatype
|
|
|
11632 |
* @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
|
|
|
11633 |
*
|
|
|
11634 |
* @see #noFill()
|
|
|
11635 |
* @see #stroke()
|
|
|
11636 |
* @see #tint()
|
|
|
11637 |
* @see #background()
|
|
|
11638 |
* @see #colorMode()
|
|
|
11639 |
*/
|
|
|
11640 |
DrawingShared.prototype.fill = function() {
|
|
|
11641 |
var color = p.color(arguments[0], arguments[1], arguments[2], arguments[3]);
|
|
|
11642 |
if(color === currentFillColor && doFill) {
|
|
|
11643 |
return;
|
|
|
11644 |
}
|
|
|
11645 |
doFill = true;
|
|
|
11646 |
currentFillColor = color;
|
|
|
11647 |
};
|
|
|
11648 |
|
|
|
11649 |
Drawing2D.prototype.fill = function() {
|
|
|
11650 |
DrawingShared.prototype.fill.apply(this, arguments);
|
|
|
11651 |
isFillDirty = true;
|
|
|
11652 |
};
|
|
|
11653 |
|
|
|
11654 |
Drawing3D.prototype.fill = function() {
|
|
|
11655 |
DrawingShared.prototype.fill.apply(this, arguments);
|
|
|
11656 |
fillStyle = p.color.toGLArray(currentFillColor);
|
|
|
11657 |
};
|
|
|
11658 |
|
|
|
11659 |
function executeContextFill() {
|
|
|
11660 |
if(doFill) {
|
|
|
11661 |
if(isFillDirty) {
|
|
|
11662 |
curContext.fillStyle = p.color.toString(currentFillColor);
|
|
|
11663 |
isFillDirty = false;
|
|
|
11664 |
}
|
|
|
11665 |
curContext.fill();
|
|
|
11666 |
}
|
|
|
11667 |
}
|
|
|
11668 |
|
|
|
11669 |
/**
|
|
|
11670 |
* The noFill() function disables filling geometry. If both <b>noStroke()</b> and <b>noFill()</b>
|
|
|
11671 |
* are called, no shapes will be drawn to the screen.
|
|
|
11672 |
*
|
|
|
11673 |
* @see #fill()
|
|
|
11674 |
*
|
|
|
11675 |
*/
|
|
|
11676 |
p.noFill = function() {
|
|
|
11677 |
doFill = false;
|
|
|
11678 |
};
|
|
|
11679 |
|
|
|
11680 |
/**
|
|
|
11681 |
* The stroke() function sets the color used to draw lines and borders around shapes. This color
|
|
|
11682 |
* is either specified in terms of the RGB or HSB color depending on the
|
|
|
11683 |
* current <b>colorMode()</b> (the default color space is RGB, with each
|
|
|
11684 |
* value in the range from 0 to 255).
|
|
|
11685 |
* <br><br>When using hexadecimal notation to specify a color, use "#" or
|
|
|
11686 |
* "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
|
|
|
11687 |
* digits to specify a color (the way colors are specified in HTML and CSS).
|
|
|
11688 |
* When using the hexadecimal notation starting with "0x", the hexadecimal
|
|
|
11689 |
* value must be specified with eight characters; the first two characters
|
|
|
11690 |
* define the alpha component and the remainder the red, green, and blue
|
|
|
11691 |
* components.
|
|
|
11692 |
* <br><br>The value for the parameter "gray" must be less than or equal
|
|
|
11693 |
* to the current maximum value as specified by <b>colorMode()</b>.
|
|
|
11694 |
* The default maximum value is 255.
|
|
|
11695 |
*
|
|
|
11696 |
* @param {int|float} gray number specifying value between white and black
|
|
|
11697 |
* @param {int|float} value1 red or hue value
|
|
|
11698 |
* @param {int|float} value2 green or saturation value
|
|
|
11699 |
* @param {int|float} value3 blue or brightness value
|
|
|
11700 |
* @param {int|float} alpha opacity of the stroke
|
|
|
11701 |
* @param {Color} color any value of the color datatype
|
|
|
11702 |
* @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
|
|
|
11703 |
*
|
|
|
11704 |
* @see #fill()
|
|
|
11705 |
* @see #noStroke()
|
|
|
11706 |
* @see #tint()
|
|
|
11707 |
* @see #background()
|
|
|
11708 |
* @see #colorMode()
|
|
|
11709 |
*/
|
|
|
11710 |
DrawingShared.prototype.stroke = function() {
|
|
|
11711 |
var color = p.color(arguments[0], arguments[1], arguments[2], arguments[3]);
|
|
|
11712 |
if(color === currentStrokeColor && doStroke) {
|
|
|
11713 |
return;
|
|
|
11714 |
}
|
|
|
11715 |
doStroke = true;
|
|
|
11716 |
currentStrokeColor = color;
|
|
|
11717 |
};
|
|
|
11718 |
|
|
|
11719 |
Drawing2D.prototype.stroke = function() {
|
|
|
11720 |
DrawingShared.prototype.stroke.apply(this, arguments);
|
|
|
11721 |
isStrokeDirty = true;
|
|
|
11722 |
};
|
|
|
11723 |
|
|
|
11724 |
Drawing3D.prototype.stroke = function() {
|
|
|
11725 |
DrawingShared.prototype.stroke.apply(this, arguments);
|
|
|
11726 |
strokeStyle = p.color.toGLArray(currentStrokeColor);
|
|
|
11727 |
};
|
|
|
11728 |
|
|
|
11729 |
function executeContextStroke() {
|
|
|
11730 |
if(doStroke) {
|
|
|
11731 |
if(isStrokeDirty) {
|
|
|
11732 |
curContext.strokeStyle = p.color.toString(currentStrokeColor);
|
|
|
11733 |
isStrokeDirty = false;
|
|
|
11734 |
}
|
|
|
11735 |
curContext.stroke();
|
|
|
11736 |
}
|
|
|
11737 |
}
|
|
|
11738 |
|
|
|
11739 |
/**
|
|
|
11740 |
* The noStroke() function disables drawing the stroke (outline). If both <b>noStroke()</b> and
|
|
|
11741 |
* <b>noFill()</b> are called, no shapes will be drawn to the screen.
|
|
|
11742 |
*
|
|
|
11743 |
* @see #stroke()
|
|
|
11744 |
*/
|
|
|
11745 |
p.noStroke = function() {
|
|
|
11746 |
doStroke = false;
|
|
|
11747 |
};
|
|
|
11748 |
|
|
|
11749 |
/**
|
|
|
11750 |
* The strokeWeight() function sets the width of the stroke used for lines, points, and the border around shapes.
|
|
|
11751 |
* All widths are set in units of pixels.
|
|
|
11752 |
*
|
|
|
11753 |
* @param {int|float} w the weight (in pixels) of the stroke
|
|
|
11754 |
*/
|
|
|
11755 |
DrawingShared.prototype.strokeWeight = function(w) {
|
|
|
11756 |
lineWidth = w;
|
|
|
11757 |
};
|
|
|
11758 |
|
|
|
11759 |
Drawing2D.prototype.strokeWeight = function(w) {
|
|
|
11760 |
DrawingShared.prototype.strokeWeight.apply(this, arguments);
|
|
|
11761 |
curContext.lineWidth = w;
|
|
|
11762 |
};
|
|
|
11763 |
|
|
|
11764 |
Drawing3D.prototype.strokeWeight = function(w) {
|
|
|
11765 |
DrawingShared.prototype.strokeWeight.apply(this, arguments);
|
|
|
11766 |
|
|
|
11767 |
// Processing groups the weight of points and lines under this one function,
|
|
|
11768 |
// but for WebGL, we need to set a uniform for points and call a function for line.
|
|
|
11769 |
|
|
|
11770 |
curContext.useProgram(programObject2D);
|
|
|
11771 |
uniformf("pointSize2d", programObject2D, "pointSize", w);
|
|
|
11772 |
|
|
|
11773 |
curContext.useProgram(programObjectUnlitShape);
|
|
|
11774 |
uniformf("pointSizeUnlitShape", programObjectUnlitShape, "pointSize", w);
|
|
|
11775 |
|
|
|
11776 |
curContext.lineWidth(w);
|
|
|
11777 |
};
|
|
|
11778 |
|
|
|
11779 |
/**
|
|
|
11780 |
* The strokeCap() function sets the style for rendering line endings. These ends are either squared, extended, or rounded and
|
|
|
11781 |
* specified with the corresponding parameters SQUARE, PROJECT, and ROUND. The default cap is ROUND.
|
|
|
11782 |
* This function is not available with the P2D, P3D, or OPENGL renderers
|
|
|
11783 |
*
|
|
|
11784 |
* @param {int} value Either SQUARE, PROJECT, or ROUND
|
|
|
11785 |
*/
|
|
|
11786 |
p.strokeCap = function(value) {
|
|
|
11787 |
drawing.$ensureContext().lineCap = value;
|
|
|
11788 |
};
|
|
|
11789 |
|
|
|
11790 |
/**
|
|
|
11791 |
* The strokeJoin() function sets the style of the joints which connect line segments.
|
|
|
11792 |
* These joints are either mitered, beveled, or rounded and specified with the corresponding parameters MITER, BEVEL, and ROUND. The default joint is MITER.
|
|
|
11793 |
* This function is not available with the P2D, P3D, or OPENGL renderers
|
|
|
11794 |
*
|
|
|
11795 |
* @param {int} value Either SQUARE, PROJECT, or ROUND
|
|
|
11796 |
*/
|
|
|
11797 |
p.strokeJoin = function(value) {
|
|
|
11798 |
drawing.$ensureContext().lineJoin = value;
|
|
|
11799 |
};
|
|
|
11800 |
|
|
|
11801 |
/**
|
|
|
11802 |
* The smooth() function draws all geometry with smooth (anti-aliased) edges. This will slow down the frame rate of the application,
|
|
|
11803 |
* but will enhance the visual refinement. <br/><br/>
|
|
|
11804 |
* Note that smooth() will also improve image quality of resized images, and noSmooth() will disable image (and font) smoothing altogether.
|
|
|
11805 |
*
|
|
|
11806 |
* @see #noSmooth()
|
|
|
11807 |
* @see #hint()
|
|
|
11808 |
* @see #size()
|
|
|
11809 |
*/
|
|
|
11810 |
|
|
|
11811 |
Drawing2D.prototype.smooth = function() {
|
|
|
11812 |
renderSmooth = true;
|
|
|
11813 |
var style = curElement.style;
|
|
|
11814 |
style.setProperty("image-rendering", "optimizeQuality", "important");
|
|
|
11815 |
style.setProperty("-ms-interpolation-mode", "bicubic", "important");
|
|
|
11816 |
if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) {
|
|
|
11817 |
curContext.mozImageSmoothingEnabled = true;
|
|
|
11818 |
}
|
|
|
11819 |
};
|
|
|
11820 |
|
|
|
11821 |
Drawing3D.prototype.smooth = nop;
|
|
|
11822 |
|
|
|
11823 |
/**
|
|
|
11824 |
* The noSmooth() function draws all geometry with jagged (aliased) edges.
|
|
|
11825 |
*
|
|
|
11826 |
* @see #smooth()
|
|
|
11827 |
*/
|
|
|
11828 |
|
|
|
11829 |
Drawing2D.prototype.noSmooth = function() {
|
|
|
11830 |
renderSmooth = false;
|
|
|
11831 |
var style = curElement.style;
|
|
|
11832 |
style.setProperty("image-rendering", "optimizeSpeed", "important");
|
|
|
11833 |
style.setProperty("image-rendering", "-moz-crisp-edges", "important");
|
|
|
11834 |
style.setProperty("image-rendering", "-webkit-optimize-contrast", "important");
|
|
|
11835 |
style.setProperty("image-rendering", "optimize-contrast", "important");
|
|
|
11836 |
style.setProperty("-ms-interpolation-mode", "nearest-neighbor", "important");
|
|
|
11837 |
if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) {
|
|
|
11838 |
curContext.mozImageSmoothingEnabled = false;
|
|
|
11839 |
}
|
|
|
11840 |
};
|
|
|
11841 |
|
|
|
11842 |
Drawing3D.prototype.noSmooth = nop;
|
|
|
11843 |
|
|
|
11844 |
////////////////////////////////////////////////////////////////////////////
|
|
|
11845 |
// Vector drawing functions
|
|
|
11846 |
////////////////////////////////////////////////////////////////////////////
|
|
|
11847 |
/**
|
|
|
11848 |
* The point() function draws a point, a coordinate in space at the dimension of one pixel.
|
|
|
11849 |
* The first parameter is the horizontal value for the point, the second
|
|
|
11850 |
* value is the vertical value for the point, and the optional third value
|
|
|
11851 |
* is the depth value. Drawing this shape in 3D using the <b>z</b>
|
|
|
11852 |
* parameter requires the P3D or OPENGL parameter in combination with
|
|
|
11853 |
* size as shown in the above example.
|
|
|
11854 |
*
|
|
|
11855 |
* @param {int|float} x x-coordinate of the point
|
|
|
11856 |
* @param {int|float} y y-coordinate of the point
|
|
|
11857 |
* @param {int|float} z z-coordinate of the point
|
|
|
11858 |
*
|
|
|
11859 |
* @see #beginShape()
|
|
|
11860 |
*/
|
|
|
11861 |
Drawing2D.prototype.point = function(x, y) {
|
|
|
11862 |
if (!doStroke) {
|
|
|
11863 |
return;
|
|
|
11864 |
}
|
|
|
11865 |
|
|
|
11866 |
x = Math.round(x);
|
|
|
11867 |
y = Math.round(y);
|
|
|
11868 |
curContext.fillStyle = p.color.toString(currentStrokeColor);
|
|
|
11869 |
isFillDirty = true;
|
|
|
11870 |
// Draw a circle for any point larger than 1px
|
|
|
11871 |
if (lineWidth > 1) {
|
|
|
11872 |
curContext.beginPath();
|
|
|
11873 |
curContext.arc(x, y, lineWidth / 2, 0, PConstants.TWO_PI, false);
|
|
|
11874 |
curContext.fill();
|
|
|
11875 |
} else {
|
|
|
11876 |
curContext.fillRect(x, y, 1, 1);
|
|
|
11877 |
}
|
|
|
11878 |
};
|
|
|
11879 |
|
|
|
11880 |
Drawing3D.prototype.point = function(x, y, z) {
|
|
|
11881 |
var model = new PMatrix3D();
|
|
|
11882 |
|
|
|
11883 |
// move point to position
|
|
|
11884 |
model.translate(x, y, z || 0);
|
|
|
11885 |
model.transpose();
|
|
|
11886 |
|
|
|
11887 |
var view = new PMatrix3D();
|
|
|
11888 |
view.scale(1, -1, 1);
|
|
|
11889 |
view.apply(modelView.array());
|
|
|
11890 |
view.transpose();
|
|
|
11891 |
|
|
|
11892 |
curContext.useProgram(programObject2D);
|
|
|
11893 |
uniformMatrix("model2d", programObject2D, "model", false, model.array());
|
|
|
11894 |
uniformMatrix("view2d", programObject2D, "view", false, view.array());
|
|
|
11895 |
|
|
|
11896 |
if (lineWidth > 0 && doStroke) {
|
|
|
11897 |
// this will be replaced with the new bit shifting color code
|
|
|
11898 |
uniformf("color2d", programObject2D, "color", strokeStyle);
|
|
|
11899 |
uniformi("picktype2d", programObject2D, "picktype", 0);
|
|
|
11900 |
vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, pointBuffer);
|
|
|
11901 |
disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
|
|
|
11902 |
curContext.drawArrays(curContext.POINTS, 0, 1);
|
|
|
11903 |
}
|
|
|
11904 |
};
|
|
|
11905 |
|
|
|
11906 |
/**
|
|
|
11907 |
* Using the <b>beginShape()</b> and <b>endShape()</b> functions allow creating more complex forms.
|
|
|
11908 |
* <b>beginShape()</b> begins recording vertices for a shape and <b>endShape()</b> stops recording.
|
|
|
11909 |
* The value of the <b>MODE</b> parameter tells it which types of shapes to create from the provided vertices.
|
|
|
11910 |
* With no mode specified, the shape can be any irregular polygon. After calling the <b>beginShape()</b> function,
|
|
|
11911 |
* a series of <b>vertex()</b> commands must follow. To stop drawing the shape, call <b>endShape()</b>.
|
|
|
11912 |
* The <b>vertex()</b> function with two parameters specifies a position in 2D and the <b>vertex()</b>
|
|
|
11913 |
* function with three parameters specifies a position in 3D. Each shape will be outlined with the current
|
|
|
11914 |
* stroke color and filled with the fill color.
|
|
|
11915 |
*
|
|
|
11916 |
* @param {int} MODE either POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRIP.
|
|
|
11917 |
*
|
|
|
11918 |
* @see endShape
|
|
|
11919 |
* @see vertex
|
|
|
11920 |
* @see curveVertex
|
|
|
11921 |
* @see bezierVertex
|
|
|
11922 |
*/
|
|
|
11923 |
p.beginShape = function(type) {
|
|
|
11924 |
curShape = type;
|
|
|
11925 |
curvePoints = [];
|
|
|
11926 |
vertArray = [];
|
|
|
11927 |
};
|
|
|
11928 |
|
|
|
11929 |
/**
|
|
|
11930 |
* All shapes are constructed by connecting a series of vertices. <b>vertex()</b> is used to specify the vertex
|
|
|
11931 |
* coordinates for points, lines, triangles, quads, and polygons and is used exclusively within the <b>beginShape()</b>
|
|
|
11932 |
* and <b>endShape()</b> function. <br /><br />Drawing a vertex in 3D using the <b>z</b> parameter requires the P3D or
|
|
|
11933 |
* OPENGL parameter in combination with size as shown in the above example.<br /><br />This function is also used to map a
|
|
|
11934 |
* texture onto the geometry. The <b>texture()</b> function declares the texture to apply to the geometry and the <b>u</b>
|
|
|
11935 |
* and <b>v</b> coordinates set define the mapping of this texture to the form. By default, the coordinates used for
|
|
|
11936 |
* <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
|
|
|
11937 |
* <b>textureMode()</b>.
|
|
|
11938 |
*
|
|
|
11939 |
* @param {int | float} x x-coordinate of the vertex
|
|
|
11940 |
* @param {int | float} y y-coordinate of the vertex
|
|
|
11941 |
* @param {int | float} z z-coordinate of the vertex
|
|
|
11942 |
* @param {int | float} u horizontal coordinate for the texture mapping
|
|
|
11943 |
* @param {int | float} v vertical coordinate for the texture mapping
|
|
|
11944 |
*
|
|
|
11945 |
* @see beginShape
|
|
|
11946 |
* @see endShape
|
|
|
11947 |
* @see bezierVertex
|
|
|
11948 |
* @see curveVertex
|
|
|
11949 |
* @see texture
|
|
|
11950 |
*/
|
|
|
11951 |
|
|
|
11952 |
Drawing2D.prototype.vertex = function(x, y, u, v) {
|
|
|
11953 |
var vert = [];
|
|
|
11954 |
|
|
|
11955 |
if (firstVert) { firstVert = false; }
|
|
|
11956 |
vert["isVert"] = true;
|
|
|
11957 |
|
|
|
11958 |
vert[0] = x;
|
|
|
11959 |
vert[1] = y;
|
|
|
11960 |
vert[2] = 0;
|
|
|
11961 |
vert[3] = u;
|
|
|
11962 |
vert[4] = v;
|
|
|
11963 |
|
|
|
11964 |
// fill and stroke color
|
|
|
11965 |
vert[5] = currentFillColor;
|
|
|
11966 |
vert[6] = currentStrokeColor;
|
|
|
11967 |
|
|
|
11968 |
vertArray.push(vert);
|
|
|
11969 |
};
|
|
|
11970 |
|
|
|
11971 |
Drawing3D.prototype.vertex = function(x, y, z, u, v) {
|
|
|
11972 |
var vert = [];
|
|
|
11973 |
|
|
|
11974 |
if (firstVert) { firstVert = false; }
|
|
|
11975 |
vert["isVert"] = true;
|
|
|
11976 |
|
|
|
11977 |
if (v === undef && usingTexture) {
|
|
|
11978 |
v = u;
|
|
|
11979 |
u = z;
|
|
|
11980 |
z = 0;
|
|
|
11981 |
}
|
|
|
11982 |
|
|
|
11983 |
// Convert u and v to normalized coordinates
|
|
|
11984 |
if (u !== undef && v !== undef) {
|
|
|
11985 |
if (curTextureMode === PConstants.IMAGE) {
|
|
|
11986 |
u /= curTexture.width;
|
|
|
11987 |
v /= curTexture.height;
|
|
|
11988 |
}
|
|
|
11989 |
u = u > 1 ? 1 : u;
|
|
|
11990 |
u = u < 0 ? 0 : u;
|
|
|
11991 |
v = v > 1 ? 1 : v;
|
|
|
11992 |
v = v < 0 ? 0 : v;
|
|
|
11993 |
}
|
|
|
11994 |
|
|
|
11995 |
vert[0] = x;
|
|
|
11996 |
vert[1] = y;
|
|
|
11997 |
vert[2] = z || 0;
|
|
|
11998 |
vert[3] = u || 0;
|
|
|
11999 |
vert[4] = v || 0;
|
|
|
12000 |
|
|
|
12001 |
// fill rgba
|
|
|
12002 |
vert[5] = fillStyle[0];
|
|
|
12003 |
vert[6] = fillStyle[1];
|
|
|
12004 |
vert[7] = fillStyle[2];
|
|
|
12005 |
vert[8] = fillStyle[3];
|
|
|
12006 |
// stroke rgba
|
|
|
12007 |
vert[9] = strokeStyle[0];
|
|
|
12008 |
vert[10] = strokeStyle[1];
|
|
|
12009 |
vert[11] = strokeStyle[2];
|
|
|
12010 |
vert[12] = strokeStyle[3];
|
|
|
12011 |
//normals
|
|
|
12012 |
vert[13] = normalX;
|
|
|
12013 |
vert[14] = normalY;
|
|
|
12014 |
vert[15] = normalZ;
|
|
|
12015 |
|
|
|
12016 |
vertArray.push(vert);
|
|
|
12017 |
};
|
|
|
12018 |
|
|
|
12019 |
/**
|
|
|
12020 |
* @private
|
|
|
12021 |
* Renders 3D points created from calls to vertex and beginShape/endShape
|
|
|
12022 |
*
|
|
|
12023 |
* @param {Array} vArray an array of vertex coordinate
|
|
|
12024 |
* @param {Array} cArray an array of colours used for the vertices
|
|
|
12025 |
*
|
|
|
12026 |
* @see beginShape
|
|
|
12027 |
* @see endShape
|
|
|
12028 |
* @see vertex
|
|
|
12029 |
*/
|
|
|
12030 |
var point3D = function(vArray, cArray){
|
|
|
12031 |
var view = new PMatrix3D();
|
|
|
12032 |
view.scale(1, -1, 1);
|
|
|
12033 |
view.apply(modelView.array());
|
|
|
12034 |
view.transpose();
|
|
|
12035 |
|
|
|
12036 |
curContext.useProgram(programObjectUnlitShape);
|
|
|
12037 |
|
|
|
12038 |
uniformMatrix("uViewUS", programObjectUnlitShape, "uView", false, view.array());
|
|
|
12039 |
|
|
|
12040 |
vertexAttribPointer("aVertexUS", programObjectUnlitShape, "aVertex", 3, pointBuffer);
|
|
|
12041 |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW);
|
|
|
12042 |
|
|
|
12043 |
vertexAttribPointer("aColorUS", programObjectUnlitShape, "aColor", 4, fillColorBuffer);
|
|
|
12044 |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW);
|
|
|
12045 |
|
|
|
12046 |
curContext.drawArrays(curContext.POINTS, 0, vArray.length/3);
|
|
|
12047 |
};
|
|
|
12048 |
|
|
|
12049 |
/**
|
|
|
12050 |
* @private
|
|
|
12051 |
* Renders 3D lines created from calls to beginShape/vertex/endShape - based on the mode specified LINES, LINE_LOOP, etc.
|
|
|
12052 |
*
|
|
|
12053 |
* @param {Array} vArray an array of vertex coordinate
|
|
|
12054 |
* @param {String} mode either LINES, LINE_LOOP, or LINE_STRIP
|
|
|
12055 |
* @param {Array} cArray an array of colours used for the vertices
|
|
|
12056 |
*
|
|
|
12057 |
* @see beginShape
|
|
|
12058 |
* @see endShape
|
|
|
12059 |
* @see vertex
|
|
|
12060 |
*/
|
|
|
12061 |
var line3D = function(vArray, mode, cArray){
|
|
|
12062 |
var ctxMode;
|
|
|
12063 |
if (mode === "LINES"){
|
|
|
12064 |
ctxMode = curContext.LINES;
|
|
|
12065 |
}
|
|
|
12066 |
else if(mode === "LINE_LOOP"){
|
|
|
12067 |
ctxMode = curContext.LINE_LOOP;
|
|
|
12068 |
}
|
|
|
12069 |
else{
|
|
|
12070 |
ctxMode = curContext.LINE_STRIP;
|
|
|
12071 |
}
|
|
|
12072 |
|
|
|
12073 |
var view = new PMatrix3D();
|
|
|
12074 |
view.scale(1, -1, 1);
|
|
|
12075 |
view.apply(modelView.array());
|
|
|
12076 |
view.transpose();
|
|
|
12077 |
|
|
|
12078 |
curContext.useProgram(programObjectUnlitShape);
|
|
|
12079 |
uniformMatrix("uViewUS", programObjectUnlitShape, "uView", false, view.array());
|
|
|
12080 |
vertexAttribPointer("aVertexUS", programObjectUnlitShape, "aVertex", 3, lineBuffer);
|
|
|
12081 |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW);
|
|
|
12082 |
vertexAttribPointer("aColorUS", programObjectUnlitShape, "aColor", 4, strokeColorBuffer);
|
|
|
12083 |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW);
|
|
|
12084 |
curContext.drawArrays(ctxMode, 0, vArray.length/3);
|
|
|
12085 |
};
|
|
|
12086 |
|
|
|
12087 |
/**
|
|
|
12088 |
* @private
|
|
|
12089 |
* Render filled shapes created from calls to beginShape/vertex/endShape - based on the mode specified TRIANGLES, etc.
|
|
|
12090 |
*
|
|
|
12091 |
* @param {Array} vArray an array of vertex coordinate
|
|
|
12092 |
* @param {String} mode either LINES, LINE_LOOP, or LINE_STRIP
|
|
|
12093 |
* @param {Array} cArray an array of colours used for the vertices
|
|
|
12094 |
* @param {Array} tArray an array of u,v coordinates for textures
|
|
|
12095 |
*
|
|
|
12096 |
* @see beginShape
|
|
|
12097 |
* @see endShape
|
|
|
12098 |
* @see vertex
|
|
|
12099 |
*/
|
|
|
12100 |
var fill3D = function(vArray, mode, cArray, tArray){
|
|
|
12101 |
var ctxMode;
|
|
|
12102 |
if (mode === "TRIANGLES") {
|
|
|
12103 |
ctxMode = curContext.TRIANGLES;
|
|
|
12104 |
} else if(mode === "TRIANGLE_FAN") {
|
|
|
12105 |
ctxMode = curContext.TRIANGLE_FAN;
|
|
|
12106 |
} else {
|
|
|
12107 |
ctxMode = curContext.TRIANGLE_STRIP;
|
|
|
12108 |
}
|
|
|
12109 |
|
|
|
12110 |
var view = new PMatrix3D();
|
|
|
12111 |
view.scale(1, -1, 1);
|
|
|
12112 |
view.apply(modelView.array());
|
|
|
12113 |
view.transpose();
|
|
|
12114 |
|
|
|
12115 |
curContext.useProgram( programObject3D );
|
|
|
12116 |
uniformMatrix("model3d", programObject3D, "model", false, [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1] );
|
|
|
12117 |
uniformMatrix("view3d", programObject3D, "view", false, view.array() );
|
|
|
12118 |
curContext.enable( curContext.POLYGON_OFFSET_FILL );
|
|
|
12119 |
curContext.polygonOffset( 1, 1 );
|
|
|
12120 |
uniformf("color3d", programObject3D, "color", [-1,0,0,0]);
|
|
|
12121 |
vertexAttribPointer("vertex3d", programObject3D, "Vertex", 3, fillBuffer);
|
|
|
12122 |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW);
|
|
|
12123 |
|
|
|
12124 |
// if we are using a texture and a tint, then overwrite the
|
|
|
12125 |
// contents of the color buffer with the current tint
|
|
|
12126 |
if (usingTexture && curTint !== null){
|
|
|
12127 |
curTint3d(cArray);
|
|
|
12128 |
}
|
|
|
12129 |
|
|
|
12130 |
vertexAttribPointer("aColor3d", programObject3D, "aColor", 4, fillColorBuffer);
|
|
|
12131 |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW);
|
|
|
12132 |
|
|
|
12133 |
// No support for lights....yet
|
|
|
12134 |
disableVertexAttribPointer("normal3d", programObject3D, "Normal");
|
|
|
12135 |
|
|
|
12136 |
if (usingTexture) {
|
|
|
12137 |
uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
|
|
|
12138 |
vertexAttribPointer("aTexture3d", programObject3D, "aTexture", 2, shapeTexVBO);
|
|
|
12139 |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(tArray), curContext.STREAM_DRAW);
|
|
|
12140 |
}
|
|
|
12141 |
|
|
|
12142 |
curContext.drawArrays( ctxMode, 0, vArray.length/3 );
|
|
|
12143 |
curContext.disable( curContext.POLYGON_OFFSET_FILL );
|
|
|
12144 |
};
|
|
|
12145 |
|
|
|
12146 |
/**
|
|
|
12147 |
* this series of three operations is used a lot in Drawing2D.prototype.endShape
|
|
|
12148 |
* and has been split off as its own function, to tighten the code and allow for
|
|
|
12149 |
* fewer bugs.
|
|
|
12150 |
*/
|
|
|
12151 |
function fillStrokeClose() {
|
|
|
12152 |
executeContextFill();
|
|
|
12153 |
executeContextStroke();
|
|
|
12154 |
curContext.closePath();
|
|
|
12155 |
}
|
|
|
12156 |
|
|
|
12157 |
/**
|
|
|
12158 |
* The endShape() function is the companion to beginShape() and may only be called after beginShape().
|
|
|
12159 |
* When endshape() is called, all of image data defined since the previous call to beginShape() is written
|
|
|
12160 |
* into the image buffer.
|
|
|
12161 |
*
|
|
|
12162 |
* @param {int} MODE Use CLOSE to close the shape
|
|
|
12163 |
*
|
|
|
12164 |
* @see beginShape
|
|
|
12165 |
*/
|
|
|
12166 |
Drawing2D.prototype.endShape = function(mode) {
|
|
|
12167 |
// Duplicated in Drawing3D; too many variables used
|
|
|
12168 |
if (vertArray.length === 0) { return; }
|
|
|
12169 |
|
|
|
12170 |
var closeShape = mode === PConstants.CLOSE;
|
|
|
12171 |
|
|
|
12172 |
// if the shape is closed, the first element is also the last element
|
|
|
12173 |
if (closeShape) {
|
|
|
12174 |
vertArray.push(vertArray[0]);
|
|
|
12175 |
}
|
|
|
12176 |
|
|
|
12177 |
var lineVertArray = [];
|
|
|
12178 |
var fillVertArray = [];
|
|
|
12179 |
var colorVertArray = [];
|
|
|
12180 |
var strokeVertArray = [];
|
|
|
12181 |
var texVertArray = [];
|
|
|
12182 |
var cachedVertArray;
|
|
|
12183 |
|
|
|
12184 |
firstVert = true;
|
|
|
12185 |
var i, j, k;
|
|
|
12186 |
var vertArrayLength = vertArray.length;
|
|
|
12187 |
|
|
|
12188 |
for (i = 0; i < vertArrayLength; i++) {
|
|
|
12189 |
cachedVertArray = vertArray[i];
|
|
|
12190 |
for (j = 0; j < 3; j++) {
|
|
|
12191 |
fillVertArray.push(cachedVertArray[j]);
|
|
|
12192 |
}
|
|
|
12193 |
}
|
|
|
12194 |
|
|
|
12195 |
// 5,6,7,8
|
|
|
12196 |
// R,G,B,A - fill colour
|
|
|
12197 |
for (i = 0; i < vertArrayLength; i++) {
|
|
|
12198 |
cachedVertArray = vertArray[i];
|
|
|
12199 |
for (j = 5; j < 9; j++) {
|
|
|
12200 |
colorVertArray.push(cachedVertArray[j]);
|
|
|
12201 |
}
|
|
|
12202 |
}
|
|
|
12203 |
|
|
|
12204 |
// 9,10,11,12
|
|
|
12205 |
// R, G, B, A - stroke colour
|
|
|
12206 |
for (i = 0; i < vertArrayLength; i++) {
|
|
|
12207 |
cachedVertArray = vertArray[i];
|
|
|
12208 |
for (j = 9; j < 13; j++) {
|
|
|
12209 |
strokeVertArray.push(cachedVertArray[j]);
|
|
|
12210 |
}
|
|
|
12211 |
}
|
|
|
12212 |
|
|
|
12213 |
// texture u,v
|
|
|
12214 |
for (i = 0; i < vertArrayLength; i++) {
|
|
|
12215 |
cachedVertArray = vertArray[i];
|
|
|
12216 |
texVertArray.push(cachedVertArray[3]);
|
|
|
12217 |
texVertArray.push(cachedVertArray[4]);
|
|
|
12218 |
}
|
|
|
12219 |
|
|
|
12220 |
// curveVertex
|
|
|
12221 |
if ( isCurve && (curShape === PConstants.POLYGON || curShape === undef) ) {
|
|
|
12222 |
if (vertArrayLength > 3) {
|
|
|
12223 |
var b = [],
|
|
|
12224 |
s = 1 - curTightness;
|
|
|
12225 |
curContext.beginPath();
|
|
|
12226 |
curContext.moveTo(vertArray[1][0], vertArray[1][1]);
|
|
|
12227 |
/*
|
|
|
12228 |
* Matrix to convert from Catmull-Rom to cubic Bezier
|
|
|
12229 |
* where t = curTightness
|
|
|
12230 |
* |0 1 0 0 |
|
|
|
12231 |
* |(t-1)/6 1 (1-t)/6 0 |
|
|
|
12232 |
* |0 (1-t)/6 1 (t-1)/6 |
|
|
|
12233 |
* |0 0 0 0 |
|
|
|
12234 |
*/
|
|
|
12235 |
for (i = 1; (i+2) < vertArrayLength; i++) {
|
|
|
12236 |
cachedVertArray = vertArray[i];
|
|
|
12237 |
b[0] = [cachedVertArray[0], cachedVertArray[1]];
|
|
|
12238 |
b[1] = [cachedVertArray[0] + (s * vertArray[i+1][0] - s * vertArray[i-1][0]) / 6,
|
|
|
12239 |
cachedVertArray[1] + (s * vertArray[i+1][1] - s * vertArray[i-1][1]) / 6];
|
|
|
12240 |
b[2] = [vertArray[i+1][0] + (s * vertArray[i][0] - s * vertArray[i+2][0]) / 6,
|
|
|
12241 |
vertArray[i+1][1] + (s * vertArray[i][1] - s * vertArray[i+2][1]) / 6];
|
|
|
12242 |
b[3] = [vertArray[i+1][0], vertArray[i+1][1]];
|
|
|
12243 |
curContext.bezierCurveTo(b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]);
|
|
|
12244 |
}
|
|
|
12245 |
fillStrokeClose();
|
|
|
12246 |
}
|
|
|
12247 |
}
|
|
|
12248 |
|
|
|
12249 |
// bezierVertex
|
|
|
12250 |
else if ( isBezier && (curShape === PConstants.POLYGON || curShape === undef) ) {
|
|
|
12251 |
curContext.beginPath();
|
|
|
12252 |
for (i = 0; i < vertArrayLength; i++) {
|
|
|
12253 |
cachedVertArray = vertArray[i];
|
|
|
12254 |
if (vertArray[i]["isVert"]) { //if it is a vertex move to the position
|
|
|
12255 |
if (vertArray[i]["moveTo"]) {
|
|
|
12256 |
curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12257 |
} else {
|
|
|
12258 |
curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12259 |
}
|
|
|
12260 |
} else { //otherwise continue drawing bezier
|
|
|
12261 |
curContext.bezierCurveTo(vertArray[i][0], vertArray[i][1], vertArray[i][2], vertArray[i][3], vertArray[i][4], vertArray[i][5]);
|
|
|
12262 |
}
|
|
|
12263 |
}
|
|
|
12264 |
fillStrokeClose();
|
|
|
12265 |
}
|
|
|
12266 |
|
|
|
12267 |
// render the vertices provided
|
|
|
12268 |
else {
|
|
|
12269 |
if (curShape === PConstants.POINTS) {
|
|
|
12270 |
for (i = 0; i < vertArrayLength; i++) {
|
|
|
12271 |
cachedVertArray = vertArray[i];
|
|
|
12272 |
if (doStroke) {
|
|
|
12273 |
p.stroke(cachedVertArray[6]);
|
|
|
12274 |
}
|
|
|
12275 |
p.point(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12276 |
}
|
|
|
12277 |
} else if (curShape === PConstants.LINES) {
|
|
|
12278 |
for (i = 0; (i + 1) < vertArrayLength; i+=2) {
|
|
|
12279 |
cachedVertArray = vertArray[i];
|
|
|
12280 |
if (doStroke) {
|
|
|
12281 |
p.stroke(vertArray[i+1][6]);
|
|
|
12282 |
}
|
|
|
12283 |
p.line(cachedVertArray[0], cachedVertArray[1], vertArray[i+1][0], vertArray[i+1][1]);
|
|
|
12284 |
}
|
|
|
12285 |
} else if (curShape === PConstants.TRIANGLES) {
|
|
|
12286 |
for (i = 0; (i + 2) < vertArrayLength; i+=3) {
|
|
|
12287 |
cachedVertArray = vertArray[i];
|
|
|
12288 |
curContext.beginPath();
|
|
|
12289 |
curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12290 |
curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
|
|
|
12291 |
curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]);
|
|
|
12292 |
curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12293 |
|
|
|
12294 |
if (doFill) {
|
|
|
12295 |
p.fill(vertArray[i+2][5]);
|
|
|
12296 |
executeContextFill();
|
|
|
12297 |
}
|
|
|
12298 |
if (doStroke) {
|
|
|
12299 |
p.stroke(vertArray[i+2][6]);
|
|
|
12300 |
executeContextStroke();
|
|
|
12301 |
}
|
|
|
12302 |
|
|
|
12303 |
curContext.closePath();
|
|
|
12304 |
}
|
|
|
12305 |
} else if (curShape === PConstants.TRIANGLE_STRIP) {
|
|
|
12306 |
for (i = 0; (i+1) < vertArrayLength; i++) {
|
|
|
12307 |
cachedVertArray = vertArray[i];
|
|
|
12308 |
curContext.beginPath();
|
|
|
12309 |
curContext.moveTo(vertArray[i+1][0], vertArray[i+1][1]);
|
|
|
12310 |
curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12311 |
|
|
|
12312 |
if (doStroke) {
|
|
|
12313 |
p.stroke(vertArray[i+1][6]);
|
|
|
12314 |
}
|
|
|
12315 |
if (doFill) {
|
|
|
12316 |
p.fill(vertArray[i+1][5]);
|
|
|
12317 |
}
|
|
|
12318 |
|
|
|
12319 |
if (i + 2 < vertArrayLength) {
|
|
|
12320 |
curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]);
|
|
|
12321 |
if (doStroke) {
|
|
|
12322 |
p.stroke(vertArray[i+2][6]);
|
|
|
12323 |
}
|
|
|
12324 |
if (doFill) {
|
|
|
12325 |
p.fill(vertArray[i+2][5]);
|
|
|
12326 |
}
|
|
|
12327 |
}
|
|
|
12328 |
fillStrokeClose();
|
|
|
12329 |
}
|
|
|
12330 |
} else if (curShape === PConstants.TRIANGLE_FAN) {
|
|
|
12331 |
if (vertArrayLength > 2) {
|
|
|
12332 |
curContext.beginPath();
|
|
|
12333 |
curContext.moveTo(vertArray[0][0], vertArray[0][1]);
|
|
|
12334 |
curContext.lineTo(vertArray[1][0], vertArray[1][1]);
|
|
|
12335 |
curContext.lineTo(vertArray[2][0], vertArray[2][1]);
|
|
|
12336 |
|
|
|
12337 |
if (doFill) {
|
|
|
12338 |
p.fill(vertArray[2][5]);
|
|
|
12339 |
executeContextFill();
|
|
|
12340 |
}
|
|
|
12341 |
if (doStroke) {
|
|
|
12342 |
p.stroke(vertArray[2][6]);
|
|
|
12343 |
executeContextStroke();
|
|
|
12344 |
}
|
|
|
12345 |
|
|
|
12346 |
curContext.closePath();
|
|
|
12347 |
for (i = 3; i < vertArrayLength; i++) {
|
|
|
12348 |
cachedVertArray = vertArray[i];
|
|
|
12349 |
curContext.beginPath();
|
|
|
12350 |
curContext.moveTo(vertArray[0][0], vertArray[0][1]);
|
|
|
12351 |
curContext.lineTo(vertArray[i-1][0], vertArray[i-1][1]);
|
|
|
12352 |
curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12353 |
|
|
|
12354 |
if (doFill) {
|
|
|
12355 |
p.fill(cachedVertArray[5]);
|
|
|
12356 |
executeContextFill();
|
|
|
12357 |
}
|
|
|
12358 |
if (doStroke) {
|
|
|
12359 |
p.stroke(cachedVertArray[6]);
|
|
|
12360 |
executeContextStroke();
|
|
|
12361 |
}
|
|
|
12362 |
|
|
|
12363 |
curContext.closePath();
|
|
|
12364 |
}
|
|
|
12365 |
}
|
|
|
12366 |
} else if (curShape === PConstants.QUADS) {
|
|
|
12367 |
for (i = 0; (i + 3) < vertArrayLength; i+=4) {
|
|
|
12368 |
cachedVertArray = vertArray[i];
|
|
|
12369 |
curContext.beginPath();
|
|
|
12370 |
curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12371 |
for (j = 1; j < 4; j++) {
|
|
|
12372 |
curContext.lineTo(vertArray[i+j][0], vertArray[i+j][1]);
|
|
|
12373 |
}
|
|
|
12374 |
curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12375 |
|
|
|
12376 |
if (doFill) {
|
|
|
12377 |
p.fill(vertArray[i+3][5]);
|
|
|
12378 |
executeContextFill();
|
|
|
12379 |
}
|
|
|
12380 |
if (doStroke) {
|
|
|
12381 |
p.stroke(vertArray[i+3][6]);
|
|
|
12382 |
executeContextStroke();
|
|
|
12383 |
}
|
|
|
12384 |
|
|
|
12385 |
curContext.closePath();
|
|
|
12386 |
}
|
|
|
12387 |
} else if (curShape === PConstants.QUAD_STRIP) {
|
|
|
12388 |
if (vertArrayLength > 3) {
|
|
|
12389 |
for (i = 0; (i+1) < vertArrayLength; i+=2) {
|
|
|
12390 |
cachedVertArray = vertArray[i];
|
|
|
12391 |
curContext.beginPath();
|
|
|
12392 |
if (i+3 < vertArrayLength) {
|
|
|
12393 |
curContext.moveTo(vertArray[i+2][0], vertArray[i+2][1]);
|
|
|
12394 |
curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12395 |
curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
|
|
|
12396 |
curContext.lineTo(vertArray[i+3][0], vertArray[i+3][1]);
|
|
|
12397 |
|
|
|
12398 |
if (doFill) {
|
|
|
12399 |
p.fill(vertArray[i+3][5]);
|
|
|
12400 |
}
|
|
|
12401 |
if (doStroke) {
|
|
|
12402 |
p.stroke(vertArray[i+3][6]);
|
|
|
12403 |
}
|
|
|
12404 |
} else {
|
|
|
12405 |
curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12406 |
curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
|
|
|
12407 |
}
|
|
|
12408 |
fillStrokeClose();
|
|
|
12409 |
}
|
|
|
12410 |
}
|
|
|
12411 |
} else {
|
|
|
12412 |
curContext.beginPath();
|
|
|
12413 |
curContext.moveTo(vertArray[0][0], vertArray[0][1]);
|
|
|
12414 |
for (i = 1; i < vertArrayLength; i++) {
|
|
|
12415 |
cachedVertArray = vertArray[i];
|
|
|
12416 |
if (cachedVertArray["isVert"]) { //if it is a vertex move to the position
|
|
|
12417 |
if (cachedVertArray["moveTo"]) {
|
|
|
12418 |
curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12419 |
} else {
|
|
|
12420 |
curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
|
|
|
12421 |
}
|
|
|
12422 |
}
|
|
|
12423 |
}
|
|
|
12424 |
fillStrokeClose();
|
|
|
12425 |
}
|
|
|
12426 |
}
|
|
|
12427 |
|
|
|
12428 |
// Reset some settings
|
|
|
12429 |
isCurve = false;
|
|
|
12430 |
isBezier = false;
|
|
|
12431 |
curveVertArray = [];
|
|
|
12432 |
curveVertCount = 0;
|
|
|
12433 |
|
|
|
12434 |
// If the shape is closed, the first element was added as last element.
|
|
|
12435 |
// We must remove it again to prevent the list of vertices from growing
|
|
|
12436 |
// over successive calls to endShape(CLOSE)
|
|
|
12437 |
if (closeShape) {
|
|
|
12438 |
vertArray.pop();
|
|
|
12439 |
}
|
|
|
12440 |
};
|
|
|
12441 |
|
|
|
12442 |
Drawing3D.prototype.endShape = function(mode) {
|
|
|
12443 |
// Duplicated in Drawing3D; too many variables used
|
|
|
12444 |
if (vertArray.length === 0) { return; }
|
|
|
12445 |
|
|
|
12446 |
var closeShape = mode === PConstants.CLOSE;
|
|
|
12447 |
var lineVertArray = [];
|
|
|
12448 |
var fillVertArray = [];
|
|
|
12449 |
var colorVertArray = [];
|
|
|
12450 |
var strokeVertArray = [];
|
|
|
12451 |
var texVertArray = [];
|
|
|
12452 |
var cachedVertArray;
|
|
|
12453 |
|
|
|
12454 |
firstVert = true;
|
|
|
12455 |
var i, j, k;
|
|
|
12456 |
var vertArrayLength = vertArray.length;
|
|
|
12457 |
|
|
|
12458 |
for (i = 0; i < vertArrayLength; i++) {
|
|
|
12459 |
cachedVertArray = vertArray[i];
|
|
|
12460 |
for (j = 0; j < 3; j++) {
|
|
|
12461 |
fillVertArray.push(cachedVertArray[j]);
|
|
|
12462 |
}
|
|
|
12463 |
}
|
|
|
12464 |
|
|
|
12465 |
// 5,6,7,8
|
|
|
12466 |
// R,G,B,A - fill colour
|
|
|
12467 |
for (i = 0; i < vertArrayLength; i++) {
|
|
|
12468 |
cachedVertArray = vertArray[i];
|
|
|
12469 |
for (j = 5; j < 9; j++) {
|
|
|
12470 |
colorVertArray.push(cachedVertArray[j]);
|
|
|
12471 |
}
|
|
|
12472 |
}
|
|
|
12473 |
|
|
|
12474 |
// 9,10,11,12
|
|
|
12475 |
// R, G, B, A - stroke colour
|
|
|
12476 |
for (i = 0; i < vertArrayLength; i++) {
|
|
|
12477 |
cachedVertArray = vertArray[i];
|
|
|
12478 |
for (j = 9; j < 13; j++) {
|
|
|
12479 |
strokeVertArray.push(cachedVertArray[j]);
|
|
|
12480 |
}
|
|
|
12481 |
}
|
|
|
12482 |
|
|
|
12483 |
// texture u,v
|
|
|
12484 |
for (i = 0; i < vertArrayLength; i++) {
|
|
|
12485 |
cachedVertArray = vertArray[i];
|
|
|
12486 |
texVertArray.push(cachedVertArray[3]);
|
|
|
12487 |
texVertArray.push(cachedVertArray[4]);
|
|
|
12488 |
}
|
|
|
12489 |
|
|
|
12490 |
// if shape is closed, push the first point into the last point (including colours)
|
|
|
12491 |
if (closeShape) {
|
|
|
12492 |
fillVertArray.push(vertArray[0][0]);
|
|
|
12493 |
fillVertArray.push(vertArray[0][1]);
|
|
|
12494 |
fillVertArray.push(vertArray[0][2]);
|
|
|
12495 |
|
|
|
12496 |
for (i = 5; i < 9; i++) {
|
|
|
12497 |
colorVertArray.push(vertArray[0][i]);
|
|
|
12498 |
}
|
|
|
12499 |
|
|
|
12500 |
for (i = 9; i < 13; i++) {
|
|
|
12501 |
strokeVertArray.push(vertArray[0][i]);
|
|
|
12502 |
}
|
|
|
12503 |
|
|
|
12504 |
texVertArray.push(vertArray[0][3]);
|
|
|
12505 |
texVertArray.push(vertArray[0][4]);
|
|
|
12506 |
}
|
|
|
12507 |
// End duplication
|
|
|
12508 |
|
|
|
12509 |
// curveVertex
|
|
|
12510 |
if ( isCurve && (curShape === PConstants.POLYGON || curShape === undef) ) {
|
|
|
12511 |
lineVertArray = fillVertArray;
|
|
|
12512 |
if (doStroke) {
|
|
|
12513 |
line3D(lineVertArray, null, strokeVertArray);
|
|
|
12514 |
}
|
|
|
12515 |
if (doFill) {
|
|
|
12516 |
fill3D(fillVertArray, null, colorVertArray);
|
|
|
12517 |
}
|
|
|
12518 |
}
|
|
|
12519 |
// bezierVertex
|
|
|
12520 |
else if ( isBezier && (curShape === PConstants.POLYGON || curShape === undef) ) {
|
|
|
12521 |
lineVertArray = fillVertArray;
|
|
|
12522 |
lineVertArray.splice(lineVertArray.length - 3);
|
|
|
12523 |
strokeVertArray.splice(strokeVertArray.length - 4);
|
|
|
12524 |
if (doStroke) {
|
|
|
12525 |
line3D(lineVertArray, null, strokeVertArray);
|
|
|
12526 |
}
|
|
|
12527 |
if (doFill) {
|
|
|
12528 |
fill3D(fillVertArray, "TRIANGLES", colorVertArray);
|
|
|
12529 |
}
|
|
|
12530 |
}
|
|
|
12531 |
|
|
|
12532 |
// render the vertices provided
|
|
|
12533 |
else {
|
|
|
12534 |
if (curShape === PConstants.POINTS) { // if POINTS was the specified parameter in beginShape
|
|
|
12535 |
for (i = 0; i < vertArrayLength; i++) { // loop through and push the point location information to the array
|
|
|
12536 |
cachedVertArray = vertArray[i];
|
|
|
12537 |
for (j = 0; j < 3; j++) {
|
|
|
12538 |
lineVertArray.push(cachedVertArray[j]);
|
|
|
12539 |
}
|
|
|
12540 |
}
|
|
|
12541 |
point3D(lineVertArray, strokeVertArray); // render function for points
|
|
|
12542 |
} else if (curShape === PConstants.LINES) { // if LINES was the specified parameter in beginShape
|
|
|
12543 |
for (i = 0; i < vertArrayLength; i++) { // loop through and push the point location information to the array
|
|
|
12544 |
cachedVertArray = vertArray[i];
|
|
|
12545 |
for (j = 0; j < 3; j++) {
|
|
|
12546 |
lineVertArray.push(cachedVertArray[j]);
|
|
|
12547 |
}
|
|
|
12548 |
}
|
|
|
12549 |
for (i = 0; i < vertArrayLength; i++) { // loop through and push the color information to the array
|
|
|
12550 |
cachedVertArray = vertArray[i];
|
|
|
12551 |
for (j = 5; j < 9; j++) {
|
|
|
12552 |
colorVertArray.push(cachedVertArray[j]);
|
|
|
12553 |
}
|
|
|
12554 |
}
|
|
|
12555 |
line3D(lineVertArray, "LINES", strokeVertArray); // render function for lines
|
|
|
12556 |
} else if (curShape === PConstants.TRIANGLES) { // if TRIANGLES was the specified parameter in beginShape
|
|
|
12557 |
if (vertArrayLength > 2) {
|
|
|
12558 |
for (i = 0; (i+2) < vertArrayLength; i+=3) { // loop through the array per triangle
|
|
|
12559 |
fillVertArray = [];
|
|
|
12560 |
texVertArray = [];
|
|
|
12561 |
lineVertArray = [];
|
|
|
12562 |
colorVertArray = [];
|
|
|
12563 |
strokeVertArray = [];
|
|
|
12564 |
for (j = 0; j < 3; j++) {
|
|
|
12565 |
for (k = 0; k < 3; k++) { // loop through and push
|
|
|
12566 |
lineVertArray.push(vertArray[i+j][k]); // the line point location information
|
|
|
12567 |
fillVertArray.push(vertArray[i+j][k]); // and fill point location information
|
|
|
12568 |
}
|
|
|
12569 |
}
|
|
|
12570 |
for (j = 0; j < 3; j++) { // loop through and push the texture information
|
|
|
12571 |
for (k = 3; k < 5; k++) {
|
|
|
12572 |
texVertArray.push(vertArray[i+j][k]);
|
|
|
12573 |
}
|
|
|
12574 |
}
|
|
|
12575 |
for (j = 0; j < 3; j++) {
|
|
|
12576 |
for (k = 5; k < 9; k++) { // loop through and push
|
|
|
12577 |
colorVertArray.push(vertArray[i+j][k]); // the colour information
|
|
|
12578 |
strokeVertArray.push(vertArray[i+j][k+4]);// and the stroke information
|
|
|
12579 |
}
|
|
|
12580 |
}
|
|
|
12581 |
if (doStroke) {
|
|
|
12582 |
line3D(lineVertArray, "LINE_LOOP", strokeVertArray ); // line render function
|
|
|
12583 |
}
|
|
|
12584 |
if (doFill || usingTexture) {
|
|
|
12585 |
fill3D(fillVertArray, "TRIANGLES", colorVertArray, texVertArray); // fill shape render function
|
|
|
12586 |
}
|
|
|
12587 |
}
|
|
|
12588 |
}
|
|
|
12589 |
} else if (curShape === PConstants.TRIANGLE_STRIP) { // if TRIANGLE_STRIP was the specified parameter in beginShape
|
|
|
12590 |
if (vertArrayLength > 2) {
|
|
|
12591 |
for (i = 0; (i+2) < vertArrayLength; i++) {
|
|
|
12592 |
lineVertArray = [];
|
|
|
12593 |
fillVertArray = [];
|
|
|
12594 |
strokeVertArray = [];
|
|
|
12595 |
colorVertArray = [];
|
|
|
12596 |
texVertArray = [];
|
|
|
12597 |
for (j = 0; j < 3; j++) {
|
|
|
12598 |
for (k = 0; k < 3; k++) {
|
|
|
12599 |
lineVertArray.push(vertArray[i+j][k]);
|
|
|
12600 |
fillVertArray.push(vertArray[i+j][k]);
|
|
|
12601 |
}
|
|
|
12602 |
}
|
|
|
12603 |
for (j = 0; j < 3; j++) {
|
|
|
12604 |
for (k = 3; k < 5; k++) {
|
|
|
12605 |
texVertArray.push(vertArray[i+j][k]);
|
|
|
12606 |
}
|
|
|
12607 |
}
|
|
|
12608 |
for (j = 0; j < 3; j++) {
|
|
|
12609 |
for (k = 5; k < 9; k++) {
|
|
|
12610 |
strokeVertArray.push(vertArray[i+j][k+4]);
|
|
|
12611 |
colorVertArray.push(vertArray[i+j][k]);
|
|
|
12612 |
}
|
|
|
12613 |
}
|
|
|
12614 |
|
|
|
12615 |
if (doFill || usingTexture) {
|
|
|
12616 |
fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
|
|
|
12617 |
}
|
|
|
12618 |
if (doStroke) {
|
|
|
12619 |
line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
|
|
|
12620 |
}
|
|
|
12621 |
}
|
|
|
12622 |
}
|
|
|
12623 |
} else if (curShape === PConstants.TRIANGLE_FAN) {
|
|
|
12624 |
if (vertArrayLength > 2) {
|
|
|
12625 |
for (i = 0; i < 3; i++) {
|
|
|
12626 |
cachedVertArray = vertArray[i];
|
|
|
12627 |
for (j = 0; j < 3; j++) {
|
|
|
12628 |
lineVertArray.push(cachedVertArray[j]);
|
|
|
12629 |
}
|
|
|
12630 |
}
|
|
|
12631 |
for (i = 0; i < 3; i++) {
|
|
|
12632 |
cachedVertArray = vertArray[i];
|
|
|
12633 |
for (j = 9; j < 13; j++) {
|
|
|
12634 |
strokeVertArray.push(cachedVertArray[j]);
|
|
|
12635 |
}
|
|
|
12636 |
}
|
|
|
12637 |
if (doStroke) {
|
|
|
12638 |
line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
|
|
|
12639 |
}
|
|
|
12640 |
|
|
|
12641 |
for (i = 2; (i+1) < vertArrayLength; i++) {
|
|
|
12642 |
lineVertArray = [];
|
|
|
12643 |
strokeVertArray = [];
|
|
|
12644 |
lineVertArray.push(vertArray[0][0]);
|
|
|
12645 |
lineVertArray.push(vertArray[0][1]);
|
|
|
12646 |
lineVertArray.push(vertArray[0][2]);
|
|
|
12647 |
|
|
|
12648 |
strokeVertArray.push(vertArray[0][9]);
|
|
|
12649 |
strokeVertArray.push(vertArray[0][10]);
|
|
|
12650 |
strokeVertArray.push(vertArray[0][11]);
|
|
|
12651 |
strokeVertArray.push(vertArray[0][12]);
|
|
|
12652 |
|
|
|
12653 |
for (j = 0; j < 2; j++) {
|
|
|
12654 |
for (k = 0; k < 3; k++) {
|
|
|
12655 |
lineVertArray.push(vertArray[i+j][k]);
|
|
|
12656 |
}
|
|
|
12657 |
}
|
|
|
12658 |
for (j = 0; j < 2; j++) {
|
|
|
12659 |
for (k = 9; k < 13; k++) {
|
|
|
12660 |
strokeVertArray.push(vertArray[i+j][k]);
|
|
|
12661 |
}
|
|
|
12662 |
}
|
|
|
12663 |
if (doStroke) {
|
|
|
12664 |
line3D(lineVertArray, "LINE_STRIP",strokeVertArray);
|
|
|
12665 |
}
|
|
|
12666 |
}
|
|
|
12667 |
if (doFill || usingTexture) {
|
|
|
12668 |
fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
|
|
|
12669 |
}
|
|
|
12670 |
}
|
|
|
12671 |
} else if (curShape === PConstants.QUADS) {
|
|
|
12672 |
for (i = 0; (i + 3) < vertArrayLength; i+=4) {
|
|
|
12673 |
lineVertArray = [];
|
|
|
12674 |
for (j = 0; j < 4; j++) {
|
|
|
12675 |
cachedVertArray = vertArray[i+j];
|
|
|
12676 |
for (k = 0; k < 3; k++) {
|
|
|
12677 |
lineVertArray.push(cachedVertArray[k]);
|
|
|
12678 |
}
|
|
|
12679 |
}
|
|
|
12680 |
if (doStroke) {
|
|
|
12681 |
line3D(lineVertArray, "LINE_LOOP",strokeVertArray);
|
|
|
12682 |
}
|
|
|
12683 |
|
|
|
12684 |
if (doFill) {
|
|
|
12685 |
fillVertArray = [];
|
|
|
12686 |
colorVertArray = [];
|
|
|
12687 |
texVertArray = [];
|
|
|
12688 |
for (j = 0; j < 3; j++) {
|
|
|
12689 |
fillVertArray.push(vertArray[i][j]);
|
|
|
12690 |
}
|
|
|
12691 |
for (j = 5; j < 9; j++) {
|
|
|
12692 |
colorVertArray.push(vertArray[i][j]);
|
|
|
12693 |
}
|
|
|
12694 |
|
|
|
12695 |
for (j = 0; j < 3; j++) {
|
|
|
12696 |
fillVertArray.push(vertArray[i+1][j]);
|
|
|
12697 |
}
|
|
|
12698 |
for (j = 5; j < 9; j++) {
|
|
|
12699 |
colorVertArray.push(vertArray[i+1][j]);
|
|
|
12700 |
}
|
|
|
12701 |
|
|
|
12702 |
for (j = 0; j < 3; j++) {
|
|
|
12703 |
fillVertArray.push(vertArray[i+3][j]);
|
|
|
12704 |
}
|
|
|
12705 |
for (j = 5; j < 9; j++) {
|
|
|
12706 |
colorVertArray.push(vertArray[i+3][j]);
|
|
|
12707 |
}
|
|
|
12708 |
|
|
|
12709 |
for (j = 0; j < 3; j++) {
|
|
|
12710 |
fillVertArray.push(vertArray[i+2][j]);
|
|
|
12711 |
}
|
|
|
12712 |
for (j = 5; j < 9; j++) {
|
|
|
12713 |
colorVertArray.push(vertArray[i+2][j]);
|
|
|
12714 |
}
|
|
|
12715 |
|
|
|
12716 |
if (usingTexture) {
|
|
|
12717 |
texVertArray.push(vertArray[i+0][3]);
|
|
|
12718 |
texVertArray.push(vertArray[i+0][4]);
|
|
|
12719 |
texVertArray.push(vertArray[i+1][3]);
|
|
|
12720 |
texVertArray.push(vertArray[i+1][4]);
|
|
|
12721 |
texVertArray.push(vertArray[i+3][3]);
|
|
|
12722 |
texVertArray.push(vertArray[i+3][4]);
|
|
|
12723 |
texVertArray.push(vertArray[i+2][3]);
|
|
|
12724 |
texVertArray.push(vertArray[i+2][4]);
|
|
|
12725 |
}
|
|
|
12726 |
|
|
|
12727 |
fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
|
|
|
12728 |
}
|
|
|
12729 |
}
|
|
|
12730 |
} else if (curShape === PConstants.QUAD_STRIP) {
|
|
|
12731 |
var tempArray = [];
|
|
|
12732 |
if (vertArrayLength > 3) {
|
|
|
12733 |
for (i = 0; i < 2; i++) {
|
|
|
12734 |
cachedVertArray = vertArray[i];
|
|
|
12735 |
for (j = 0; j < 3; j++) {
|
|
|
12736 |
lineVertArray.push(cachedVertArray[j]);
|
|
|
12737 |
}
|
|
|
12738 |
}
|
|
|
12739 |
|
|
|
12740 |
for (i = 0; i < 2; i++) {
|
|
|
12741 |
cachedVertArray = vertArray[i];
|
|
|
12742 |
for (j = 9; j < 13; j++) {
|
|
|
12743 |
strokeVertArray.push(cachedVertArray[j]);
|
|
|
12744 |
}
|
|
|
12745 |
}
|
|
|
12746 |
|
|
|
12747 |
line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
|
|
|
12748 |
if (vertArrayLength > 4 && vertArrayLength % 2 > 0) {
|
|
|
12749 |
tempArray = fillVertArray.splice(fillVertArray.length - 3);
|
|
|
12750 |
vertArray.pop();
|
|
|
12751 |
}
|
|
|
12752 |
for (i = 0; (i+3) < vertArrayLength; i+=2) {
|
|
|
12753 |
lineVertArray = [];
|
|
|
12754 |
strokeVertArray = [];
|
|
|
12755 |
for (j = 0; j < 3; j++) {
|
|
|
12756 |
lineVertArray.push(vertArray[i+1][j]);
|
|
|
12757 |
}
|
|
|
12758 |
for (j = 0; j < 3; j++) {
|
|
|
12759 |
lineVertArray.push(vertArray[i+3][j]);
|
|
|
12760 |
}
|
|
|
12761 |
for (j = 0; j < 3; j++) {
|
|
|
12762 |
lineVertArray.push(vertArray[i+2][j]);
|
|
|
12763 |
}
|
|
|
12764 |
for (j = 0; j < 3; j++) {
|
|
|
12765 |
lineVertArray.push(vertArray[i+0][j]);
|
|
|
12766 |
}
|
|
|
12767 |
for (j = 9; j < 13; j++) {
|
|
|
12768 |
strokeVertArray.push(vertArray[i+1][j]);
|
|
|
12769 |
}
|
|
|
12770 |
for (j = 9; j < 13; j++) {
|
|
|
12771 |
strokeVertArray.push(vertArray[i+3][j]);
|
|
|
12772 |
}
|
|
|
12773 |
for (j = 9; j < 13; j++) {
|
|
|
12774 |
strokeVertArray.push(vertArray[i+2][j]);
|
|
|
12775 |
}
|
|
|
12776 |
for (j = 9; j < 13; j++) {
|
|
|
12777 |
strokeVertArray.push(vertArray[i+0][j]);
|
|
|
12778 |
}
|
|
|
12779 |
if (doStroke) {
|
|
|
12780 |
line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
|
|
|
12781 |
}
|
|
|
12782 |
}
|
|
|
12783 |
|
|
|
12784 |
if (doFill || usingTexture) {
|
|
|
12785 |
fill3D(fillVertArray, "TRIANGLE_LIST", colorVertArray, texVertArray);
|
|
|
12786 |
}
|
|
|
12787 |
}
|
|
|
12788 |
}
|
|
|
12789 |
// If the user didn't specify a type (LINES, TRIANGLES, etc)
|
|
|
12790 |
else {
|
|
|
12791 |
// If only one vertex was specified, it must be a point
|
|
|
12792 |
if (vertArrayLength === 1) {
|
|
|
12793 |
for (j = 0; j < 3; j++) {
|
|
|
12794 |
lineVertArray.push(vertArray[0][j]);
|
|
|
12795 |
}
|
|
|
12796 |
for (j = 9; j < 13; j++) {
|
|
|
12797 |
strokeVertArray.push(vertArray[0][j]);
|
|
|
12798 |
}
|
|
|
12799 |
point3D(lineVertArray,strokeVertArray);
|
|
|
12800 |
} else {
|
|
|
12801 |
for (i = 0; i < vertArrayLength; i++) {
|
|
|
12802 |
cachedVertArray = vertArray[i];
|
|
|
12803 |
for (j = 0; j < 3; j++) {
|
|
|
12804 |
lineVertArray.push(cachedVertArray[j]);
|
|
|
12805 |
}
|
|
|
12806 |
for (j = 5; j < 9; j++) {
|
|
|
12807 |
strokeVertArray.push(cachedVertArray[j]);
|
|
|
12808 |
}
|
|
|
12809 |
}
|
|
|
12810 |
if (doStroke && closeShape) {
|
|
|
12811 |
line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
|
|
|
12812 |
} else if (doStroke && !closeShape) {
|
|
|
12813 |
line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
|
|
|
12814 |
}
|
|
|
12815 |
|
|
|
12816 |
// fill is ignored if textures are used
|
|
|
12817 |
if (doFill || usingTexture) {
|
|
|
12818 |
fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
|
|
|
12819 |
}
|
|
|
12820 |
}
|
|
|
12821 |
}
|
|
|
12822 |
// everytime beginShape is followed by a call to
|
|
|
12823 |
// texture(), texturing it turned back on. We do this to
|
|
|
12824 |
// figure out if the shape should be textured or filled
|
|
|
12825 |
// with a color.
|
|
|
12826 |
usingTexture = false;
|
|
|
12827 |
curContext.useProgram(programObject3D);
|
|
|
12828 |
uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
|
|
|
12829 |
}
|
|
|
12830 |
|
|
|
12831 |
// Reset some settings
|
|
|
12832 |
isCurve = false;
|
|
|
12833 |
isBezier = false;
|
|
|
12834 |
curveVertArray = [];
|
|
|
12835 |
curveVertCount = 0;
|
|
|
12836 |
};
|
|
|
12837 |
|
|
|
12838 |
/**
|
|
|
12839 |
* The function splineForward() setup forward-differencing matrix to be used for speedy
|
|
|
12840 |
* curve rendering. It's based on using a specific number
|
|
|
12841 |
* of curve segments and just doing incremental adds for each
|
|
|
12842 |
* vertex of the segment, rather than running the mathematically
|
|
|
12843 |
* expensive cubic equation. This function is used by both curveDetail and bezierDetail.
|
|
|
12844 |
*
|
|
|
12845 |
* @param {int} segments number of curve segments to use when drawing
|
|
|
12846 |
* @param {PMatrix3D} matrix target object for the new matrix
|
|
|
12847 |
*/
|
|
|
12848 |
var splineForward = function(segments, matrix) {
|
|
|
12849 |
var f = 1.0 / segments;
|
|
|
12850 |
var ff = f * f;
|
|
|
12851 |
var fff = ff * f;
|
|
|
12852 |
|
|
|
12853 |
matrix.set(0, 0, 0, 1, fff, ff, f, 0, 6 * fff, 2 * ff, 0, 0, 6 * fff, 0, 0, 0);
|
|
|
12854 |
};
|
|
|
12855 |
|
|
|
12856 |
/**
|
|
|
12857 |
* The curveInit() function set the number of segments to use when drawing a Catmull-Rom
|
|
|
12858 |
* curve, and setting the s parameter, which defines how tightly
|
|
|
12859 |
* the curve fits to each vertex. Catmull-Rom curves are actually
|
|
|
12860 |
* a subset of this curve type where the s is set to zero.
|
|
|
12861 |
* This in an internal function used by curveDetail() and curveTightness().
|
|
|
12862 |
*/
|
|
|
12863 |
var curveInit = function() {
|
|
|
12864 |
// allocate only if/when used to save startup time
|
|
|
12865 |
if (!curveDrawMatrix) {
|
|
|
12866 |
curveBasisMatrix = new PMatrix3D();
|
|
|
12867 |
curveDrawMatrix = new PMatrix3D();
|
|
|
12868 |
curveInited = true;
|
|
|
12869 |
}
|
|
|
12870 |
|
|
|
12871 |
var s = curTightness;
|
|
|
12872 |
curveBasisMatrix.set((s - 1) / 2, (s + 3) / 2, (-3 - s) / 2, (1 - s) / 2,
|
|
|
12873 |
(1 - s), (-5 - s) / 2, (s + 2), (s - 1) / 2,
|
|
|
12874 |
(s - 1) / 2, 0, (1 - s) / 2, 0, 0, 1, 0, 0);
|
|
|
12875 |
|
|
|
12876 |
splineForward(curveDet, curveDrawMatrix);
|
|
|
12877 |
|
|
|
12878 |
if (!bezierBasisInverse) {
|
|
|
12879 |
//bezierBasisInverse = bezierBasisMatrix.get();
|
|
|
12880 |
//bezierBasisInverse.invert();
|
|
|
12881 |
curveToBezierMatrix = new PMatrix3D();
|
|
|
12882 |
}
|
|
|
12883 |
|
|
|
12884 |
// TODO only needed for PGraphicsJava2D? if so, move it there
|
|
|
12885 |
// actually, it's generally useful for other renderers, so keep it
|
|
|
12886 |
// or hide the implementation elsewhere.
|
|
|
12887 |
curveToBezierMatrix.set(curveBasisMatrix);
|
|
|
12888 |
curveToBezierMatrix.preApply(bezierBasisInverse);
|
|
|
12889 |
|
|
|
12890 |
// multiply the basis and forward diff matrices together
|
|
|
12891 |
// saves much time since this needn't be done for each curve
|
|
|
12892 |
curveDrawMatrix.apply(curveBasisMatrix);
|
|
|
12893 |
};
|
|
|
12894 |
|
|
|
12895 |
/**
|
|
|
12896 |
* Specifies vertex coordinates for Bezier curves. Each call to <b>bezierVertex()</b> defines the position of two control
|
|
|
12897 |
* points and one anchor point of a Bezier curve, adding a new segment to a line or shape. The first time
|
|
|
12898 |
* <b>bezierVertex()</b> is used within a <b>beginShape()</b> call, it must be prefaced with a call to <b>vertex()</b>
|
|
|
12899 |
* to set the first anchor point. This function must be used between <b>beginShape()</b> and <b>endShape()</b> and only
|
|
|
12900 |
* when there is no MODE parameter specified to <b>beginShape()</b>. Using the 3D version of requires rendering with P3D
|
|
|
12901 |
* or OPENGL (see the Environment reference for more information). <br /> <br /> <b>NOTE: </b> Fill does not work properly yet.
|
|
|
12902 |
*
|
|
|
12903 |
* @param {float | int} cx1 The x-coordinate of 1st control point
|
|
|
12904 |
* @param {float | int} cy1 The y-coordinate of 1st control point
|
|
|
12905 |
* @param {float | int} cz1 The z-coordinate of 1st control point
|
|
|
12906 |
* @param {float | int} cx2 The x-coordinate of 2nd control point
|
|
|
12907 |
* @param {float | int} cy2 The y-coordinate of 2nd control point
|
|
|
12908 |
* @param {float | int} cz2 The z-coordinate of 2nd control point
|
|
|
12909 |
* @param {float | int} x The x-coordinate of the anchor point
|
|
|
12910 |
* @param {float | int} y The y-coordinate of the anchor point
|
|
|
12911 |
* @param {float | int} z The z-coordinate of the anchor point
|
|
|
12912 |
*
|
|
|
12913 |
* @see curveVertex
|
|
|
12914 |
* @see vertex
|
|
|
12915 |
* @see bezier
|
|
|
12916 |
*/
|
|
|
12917 |
Drawing2D.prototype.bezierVertex = function() {
|
|
|
12918 |
isBezier = true;
|
|
|
12919 |
var vert = [];
|
|
|
12920 |
if (firstVert) {
|
|
|
12921 |
throw ("vertex() must be used at least once before calling bezierVertex()");
|
|
|
12922 |
}
|
|
|
12923 |
|
|
|
12924 |
for (var i = 0; i < arguments.length; i++) {
|
|
|
12925 |
vert[i] = arguments[i];
|
|
|
12926 |
}
|
|
|
12927 |
vertArray.push(vert);
|
|
|
12928 |
vertArray[vertArray.length -1]["isVert"] = false;
|
|
|
12929 |
};
|
|
|
12930 |
|
|
|
12931 |
Drawing3D.prototype.bezierVertex = function() {
|
|
|
12932 |
isBezier = true;
|
|
|
12933 |
var vert = [];
|
|
|
12934 |
if (firstVert) {
|
|
|
12935 |
throw ("vertex() must be used at least once before calling bezierVertex()");
|
|
|
12936 |
}
|
|
|
12937 |
|
|
|
12938 |
if (arguments.length === 9) {
|
|
|
12939 |
if (bezierDrawMatrix === undef) {
|
|
|
12940 |
bezierDrawMatrix = new PMatrix3D();
|
|
|
12941 |
}
|
|
|
12942 |
// setup matrix for forward differencing to speed up drawing
|
|
|
12943 |
var lastPoint = vertArray.length - 1;
|
|
|
12944 |
splineForward( bezDetail, bezierDrawMatrix );
|
|
|
12945 |
bezierDrawMatrix.apply( bezierBasisMatrix );
|
|
|
12946 |
var draw = bezierDrawMatrix.array();
|
|
|
12947 |
var x1 = vertArray[lastPoint][0],
|
|
|
12948 |
y1 = vertArray[lastPoint][1],
|
|
|
12949 |
z1 = vertArray[lastPoint][2];
|
|
|
12950 |
var xplot1 = draw[4] * x1 + draw[5] * arguments[0] + draw[6] * arguments[3] + draw[7] * arguments[6];
|
|
|
12951 |
var xplot2 = draw[8] * x1 + draw[9] * arguments[0] + draw[10]* arguments[3] + draw[11]* arguments[6];
|
|
|
12952 |
var xplot3 = draw[12]* x1 + draw[13]* arguments[0] + draw[14]* arguments[3] + draw[15]* arguments[6];
|
|
|
12953 |
|
|
|
12954 |
var yplot1 = draw[4] * y1 + draw[5] * arguments[1] + draw[6] * arguments[4] + draw[7] * arguments[7];
|
|
|
12955 |
var yplot2 = draw[8] * y1 + draw[9] * arguments[1] + draw[10]* arguments[4] + draw[11]* arguments[7];
|
|
|
12956 |
var yplot3 = draw[12]* y1 + draw[13]* arguments[1] + draw[14]* arguments[4] + draw[15]* arguments[7];
|
|
|
12957 |
|
|
|
12958 |
var zplot1 = draw[4] * z1 + draw[5] * arguments[2] + draw[6] * arguments[5] + draw[7] * arguments[8];
|
|
|
12959 |
var zplot2 = draw[8] * z1 + draw[9] * arguments[2] + draw[10]* arguments[5] + draw[11]* arguments[8];
|
|
|
12960 |
var zplot3 = draw[12]* z1 + draw[13]* arguments[2] + draw[14]* arguments[5] + draw[15]* arguments[8];
|
|
|
12961 |
for (var j = 0; j < bezDetail; j++) {
|
|
|
12962 |
x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
|
|
|
12963 |
y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
|
|
|
12964 |
z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
|
|
|
12965 |
p.vertex(x1, y1, z1);
|
|
|
12966 |
}
|
|
|
12967 |
p.vertex(arguments[6], arguments[7], arguments[8]);
|
|
|
12968 |
}
|
|
|
12969 |
};
|
|
|
12970 |
|
|
|
12971 |
/**
|
|
|
12972 |
* Sets a texture to be applied to vertex points. The <b>texture()</b> function
|
|
|
12973 |
* must be called between <b>beginShape()</b> and <b>endShape()</b> and before
|
|
|
12974 |
* any calls to vertex().
|
|
|
12975 |
*
|
|
|
12976 |
* When textures are in use, the fill color is ignored. Instead, use tint() to
|
|
|
12977 |
* specify the color of the texture as it is applied to the shape.
|
|
|
12978 |
*
|
|
|
12979 |
* @param {PImage} pimage the texture to apply
|
|
|
12980 |
*
|
|
|
12981 |
* @returns none
|
|
|
12982 |
*
|
|
|
12983 |
* @see textureMode
|
|
|
12984 |
* @see beginShape
|
|
|
12985 |
* @see endShape
|
|
|
12986 |
* @see vertex
|
|
|
12987 |
*/
|
|
|
12988 |
p.texture = function(pimage) {
|
|
|
12989 |
var curContext = drawing.$ensureContext();
|
|
|
12990 |
|
|
|
12991 |
if (pimage.__texture) {
|
|
|
12992 |
curContext.bindTexture(curContext.TEXTURE_2D, pimage.__texture);
|
|
|
12993 |
} else if (pimage.localName === "canvas") {
|
|
|
12994 |
curContext.bindTexture(curContext.TEXTURE_2D, canTex);
|
|
|
12995 |
curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, pimage);
|
|
|
12996 |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
|
|
|
12997 |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR);
|
|
|
12998 |
curContext.generateMipmap(curContext.TEXTURE_2D);
|
|
|
12999 |
curTexture.width = pimage.width;
|
|
|
13000 |
curTexture.height = pimage.height;
|
|
|
13001 |
} else {
|
|
|
13002 |
var texture = curContext.createTexture(),
|
|
|
13003 |
cvs = document.createElement('canvas'),
|
|
|
13004 |
cvsTextureCtx = cvs.getContext('2d'),
|
|
|
13005 |
pot;
|
|
|
13006 |
|
|
|
13007 |
// WebGL requires power of two textures
|
|
|
13008 |
if (pimage.width & (pimage.width-1) === 0) {
|
|
|
13009 |
cvs.width = pimage.width;
|
|
|
13010 |
} else {
|
|
|
13011 |
pot = 1;
|
|
|
13012 |
while (pot < pimage.width) {
|
|
|
13013 |
pot *= 2;
|
|
|
13014 |
}
|
|
|
13015 |
cvs.width = pot;
|
|
|
13016 |
}
|
|
|
13017 |
|
|
|
13018 |
if (pimage.height & (pimage.height-1) === 0) {
|
|
|
13019 |
cvs.height = pimage.height;
|
|
|
13020 |
} else {
|
|
|
13021 |
pot = 1;
|
|
|
13022 |
while (pot < pimage.height) {
|
|
|
13023 |
pot *= 2;
|
|
|
13024 |
}
|
|
|
13025 |
cvs.height = pot;
|
|
|
13026 |
}
|
|
|
13027 |
|
|
|
13028 |
cvsTextureCtx.drawImage(pimage.sourceImg, 0, 0, pimage.width, pimage.height, 0, 0, cvs.width, cvs.height);
|
|
|
13029 |
|
|
|
13030 |
curContext.bindTexture(curContext.TEXTURE_2D, texture);
|
|
|
13031 |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR_MIPMAP_LINEAR);
|
|
|
13032 |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
|
|
|
13033 |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE);
|
|
|
13034 |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE);
|
|
|
13035 |
curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, cvs);
|
|
|
13036 |
curContext.generateMipmap(curContext.TEXTURE_2D);
|
|
|
13037 |
|
|
|
13038 |
pimage.__texture = texture;
|
|
|
13039 |
curTexture.width = pimage.width;
|
|
|
13040 |
curTexture.height = pimage.height;
|
|
|
13041 |
}
|
|
|
13042 |
|
|
|
13043 |
usingTexture = true;
|
|
|
13044 |
curContext.useProgram(programObject3D);
|
|
|
13045 |
uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
|
|
|
13046 |
};
|
|
|
13047 |
|
|
|
13048 |
/**
|
|
|
13049 |
* Sets the coordinate space for texture mapping. There are two options, IMAGE,
|
|
|
13050 |
* which refers to the actual coordinates of the image, and NORMALIZED, which
|
|
|
13051 |
* refers to a normalized space of values ranging from 0 to 1. The default mode
|
|
|
13052 |
* is IMAGE. In IMAGE, if an image is 100 x 200 pixels, mapping the image onto
|
|
|
13053 |
* the entire size of a quad would require the points (0,0) (0,100) (100,200) (0,200).
|
|
|
13054 |
* The same mapping in NORMAL_SPACE is (0,0) (0,1) (1,1) (0,1).
|
|
|
13055 |
*
|
|
|
13056 |
* @param MODE either IMAGE or NORMALIZED
|
|
|
13057 |
*
|
|
|
13058 |
* @returns none
|
|
|
13059 |
*
|
|
|
13060 |
* @see texture
|
|
|
13061 |
*/
|
|
|
13062 |
p.textureMode = function(mode){
|
|
|
13063 |
curTextureMode = mode;
|
|
|
13064 |
};
|
|
|
13065 |
/**
|
|
|
13066 |
* The curveVertexSegment() function handle emitting a specific segment of Catmull-Rom curve. Internal helper function used by <b>curveVertex()</b>.
|
|
|
13067 |
*/
|
|
|
13068 |
var curveVertexSegment = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) {
|
|
|
13069 |
var x0 = x2;
|
|
|
13070 |
var y0 = y2;
|
|
|
13071 |
var z0 = z2;
|
|
|
13072 |
|
|
|
13073 |
var draw = curveDrawMatrix.array();
|
|
|
13074 |
|
|
|
13075 |
var xplot1 = draw[4] * x1 + draw[5] * x2 + draw[6] * x3 + draw[7] * x4;
|
|
|
13076 |
var xplot2 = draw[8] * x1 + draw[9] * x2 + draw[10] * x3 + draw[11] * x4;
|
|
|
13077 |
var xplot3 = draw[12] * x1 + draw[13] * x2 + draw[14] * x3 + draw[15] * x4;
|
|
|
13078 |
|
|
|
13079 |
var yplot1 = draw[4] * y1 + draw[5] * y2 + draw[6] * y3 + draw[7] * y4;
|
|
|
13080 |
var yplot2 = draw[8] * y1 + draw[9] * y2 + draw[10] * y3 + draw[11] * y4;
|
|
|
13081 |
var yplot3 = draw[12] * y1 + draw[13] * y2 + draw[14] * y3 + draw[15] * y4;
|
|
|
13082 |
|
|
|
13083 |
var zplot1 = draw[4] * z1 + draw[5] * z2 + draw[6] * z3 + draw[7] * z4;
|
|
|
13084 |
var zplot2 = draw[8] * z1 + draw[9] * z2 + draw[10] * z3 + draw[11] * z4;
|
|
|
13085 |
var zplot3 = draw[12] * z1 + draw[13] * z2 + draw[14] * z3 + draw[15] * z4;
|
|
|
13086 |
|
|
|
13087 |
p.vertex(x0, y0, z0);
|
|
|
13088 |
for (var j = 0; j < curveDet; j++) {
|
|
|
13089 |
x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
|
|
|
13090 |
y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
|
|
|
13091 |
z0 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
|
|
|
13092 |
p.vertex(x0, y0, z0);
|
|
|
13093 |
}
|
|
|
13094 |
};
|
|
|
13095 |
|
|
|
13096 |
/**
|
|
|
13097 |
* Specifies vertex coordinates for curves. This function may only be used between <b>beginShape()</b> and
|
|
|
13098 |
* <b>endShape()</b> and only when there is no MODE parameter specified to <b>beginShape()</b>. The first and last points
|
|
|
13099 |
* 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
|
|
|
13100 |
* points is required to draw a tiny curve between the second and third points. Adding a fifth point with
|
|
|
13101 |
* <b>curveVertex()</b> will draw the curve between the second, third, and fourth points. The <b>curveVertex()</b> function
|
|
|
13102 |
* is an implementation of Catmull-Rom splines. Using the 3D version of requires rendering with P3D or OPENGL (see the
|
|
|
13103 |
* Environment reference for more information). <br /> <br /><b>NOTE: </b> Fill does not work properly yet.
|
|
|
13104 |
*
|
|
|
13105 |
* @param {float | int} x The x-coordinate of the vertex
|
|
|
13106 |
* @param {float | int} y The y-coordinate of the vertex
|
|
|
13107 |
* @param {float | int} z The z-coordinate of the vertex
|
|
|
13108 |
*
|
|
|
13109 |
* @see curve
|
|
|
13110 |
* @see beginShape
|
|
|
13111 |
* @see endShape
|
|
|
13112 |
* @see vertex
|
|
|
13113 |
* @see bezierVertex
|
|
|
13114 |
*/
|
|
|
13115 |
Drawing2D.prototype.curveVertex = function(x, y) {
|
|
|
13116 |
isCurve = true;
|
|
|
13117 |
|
|
|
13118 |
p.vertex(x, y);
|
|
|
13119 |
};
|
|
|
13120 |
|
|
|
13121 |
Drawing3D.prototype.curveVertex = function(x, y, z) {
|
|
|
13122 |
isCurve = true;
|
|
|
13123 |
|
|
|
13124 |
if (!curveInited) {
|
|
|
13125 |
curveInit();
|
|
|
13126 |
}
|
|
|
13127 |
var vert = [];
|
|
|
13128 |
vert[0] = x;
|
|
|
13129 |
vert[1] = y;
|
|
|
13130 |
vert[2] = z;
|
|
|
13131 |
curveVertArray.push(vert);
|
|
|
13132 |
curveVertCount++;
|
|
|
13133 |
|
|
|
13134 |
if (curveVertCount > 3) {
|
|
|
13135 |
curveVertexSegment( curveVertArray[curveVertCount-4][0],
|
|
|
13136 |
curveVertArray[curveVertCount-4][1],
|
|
|
13137 |
curveVertArray[curveVertCount-4][2],
|
|
|
13138 |
curveVertArray[curveVertCount-3][0],
|
|
|
13139 |
curveVertArray[curveVertCount-3][1],
|
|
|
13140 |
curveVertArray[curveVertCount-3][2],
|
|
|
13141 |
curveVertArray[curveVertCount-2][0],
|
|
|
13142 |
curveVertArray[curveVertCount-2][1],
|
|
|
13143 |
curveVertArray[curveVertCount-2][2],
|
|
|
13144 |
curveVertArray[curveVertCount-1][0],
|
|
|
13145 |
curveVertArray[curveVertCount-1][1],
|
|
|
13146 |
curveVertArray[curveVertCount-1][2] );
|
|
|
13147 |
}
|
|
|
13148 |
};
|
|
|
13149 |
|
|
|
13150 |
/**
|
|
|
13151 |
* The curve() function draws a curved line on the screen. The first and second parameters
|
|
|
13152 |
* specify the beginning control point and the last two parameters specify
|
|
|
13153 |
* the ending control point. The middle parameters specify the start and
|
|
|
13154 |
* stop of the curve. Longer curves can be created by putting a series of
|
|
|
13155 |
* <b>curve()</b> functions together or using <b>curveVertex()</b>.
|
|
|
13156 |
* An additional function called <b>curveTightness()</b> provides control
|
|
|
13157 |
* for the visual quality of the curve. The <b>curve()</b> function is an
|
|
|
13158 |
* implementation of Catmull-Rom splines. Using the 3D version of requires
|
|
|
13159 |
* rendering with P3D or OPENGL (see the Environment reference for more
|
|
|
13160 |
* information).
|
|
|
13161 |
*
|
|
|
13162 |
* @param {int|float} x1 coordinates for the beginning control point
|
|
|
13163 |
* @param {int|float} y1 coordinates for the beginning control point
|
|
|
13164 |
* @param {int|float} z1 coordinates for the beginning control point
|
|
|
13165 |
* @param {int|float} x2 coordinates for the first point
|
|
|
13166 |
* @param {int|float} y2 coordinates for the first point
|
|
|
13167 |
* @param {int|float} z2 coordinates for the first point
|
|
|
13168 |
* @param {int|float} x3 coordinates for the second point
|
|
|
13169 |
* @param {int|float} y3 coordinates for the second point
|
|
|
13170 |
* @param {int|float} z3 coordinates for the second point
|
|
|
13171 |
* @param {int|float} x4 coordinates for the ending control point
|
|
|
13172 |
* @param {int|float} y4 coordinates for the ending control point
|
|
|
13173 |
* @param {int|float} z4 coordinates for the ending control point
|
|
|
13174 |
*
|
|
|
13175 |
* @see #curveVertex()
|
|
|
13176 |
* @see #curveTightness()
|
|
|
13177 |
* @see #bezier()
|
|
|
13178 |
*/
|
|
|
13179 |
Drawing2D.prototype.curve = function() {
|
|
|
13180 |
if (arguments.length === 8) { // curve(x1, y1, x2, y2, x3, y3, x4, y4)
|
|
|
13181 |
p.beginShape();
|
|
|
13182 |
p.curveVertex(arguments[0], arguments[1]);
|
|
|
13183 |
p.curveVertex(arguments[2], arguments[3]);
|
|
|
13184 |
p.curveVertex(arguments[4], arguments[5]);
|
|
|
13185 |
p.curveVertex(arguments[6], arguments[7]);
|
|
|
13186 |
p.endShape();
|
|
|
13187 |
}
|
|
|
13188 |
};
|
|
|
13189 |
|
|
|
13190 |
Drawing3D.prototype.curve = function() {
|
|
|
13191 |
if (arguments.length === 12) { // curve( x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4);
|
|
|
13192 |
p.beginShape();
|
|
|
13193 |
p.curveVertex(arguments[0], arguments[1], arguments[2]);
|
|
|
13194 |
p.curveVertex(arguments[3], arguments[4], arguments[5]);
|
|
|
13195 |
p.curveVertex(arguments[6], arguments[7], arguments[8]);
|
|
|
13196 |
p.curveVertex(arguments[9], arguments[10], arguments[11]);
|
|
|
13197 |
p.endShape();
|
|
|
13198 |
}
|
|
|
13199 |
};
|
|
|
13200 |
|
|
|
13201 |
/**
|
|
|
13202 |
* The curveTightness() function modifies the quality of forms created with <b>curve()</b> and
|
|
|
13203 |
* <b>curveVertex()</b>. The parameter <b>squishy</b> determines how the
|
|
|
13204 |
* curve fits to the vertex points. The value 0.0 is the default value for
|
|
|
13205 |
* <b>squishy</b> (this value defines the curves to be Catmull-Rom splines)
|
|
|
13206 |
* and the value 1.0 connects all the points with straight lines.
|
|
|
13207 |
* Values within the range -5.0 and 5.0 will deform the curves but
|
|
|
13208 |
* will leave them recognizable and as values increase in magnitude,
|
|
|
13209 |
* they will continue to deform.
|
|
|
13210 |
*
|
|
|
13211 |
* @param {float} tightness amount of deformation from the original vertices
|
|
|
13212 |
*
|
|
|
13213 |
* @see #curve()
|
|
|
13214 |
* @see #curveVertex()
|
|
|
13215 |
*
|
|
|
13216 |
*/
|
|
|
13217 |
p.curveTightness = function(tightness) {
|
|
|
13218 |
curTightness = tightness;
|
|
|
13219 |
};
|
|
|
13220 |
|
|
|
13221 |
/**
|
|
|
13222 |
* The curveDetail() function sets the resolution at which curves display. The default value is 20.
|
|
|
13223 |
* This function is only useful when using the P3D or OPENGL renderer.
|
|
|
13224 |
*
|
|
|
13225 |
* @param {int} detail resolution of the curves
|
|
|
13226 |
*
|
|
|
13227 |
* @see curve()
|
|
|
13228 |
* @see curveVertex()
|
|
|
13229 |
* @see curveTightness()
|
|
|
13230 |
*/
|
|
|
13231 |
p.curveDetail = function(detail) {
|
|
|
13232 |
curveDet = detail;
|
|
|
13233 |
curveInit();
|
|
|
13234 |
};
|
|
|
13235 |
|
|
|
13236 |
/**
|
|
|
13237 |
* Modifies the location from which rectangles draw. The default mode is rectMode(CORNER), which
|
|
|
13238 |
* specifies the location to be the upper left corner of the shape and uses the third and fourth
|
|
|
13239 |
* parameters of rect() to specify the width and height. The syntax rectMode(CORNERS) uses the
|
|
|
13240 |
* first and second parameters of rect() to set the location of one corner and uses the third and
|
|
|
13241 |
* fourth parameters to set the opposite corner. The syntax rectMode(CENTER) draws the image from
|
|
|
13242 |
* its center point and uses the third and forth parameters of rect() to specify the image's width
|
|
|
13243 |
* and height. The syntax rectMode(RADIUS) draws the image from its center point and uses the third
|
|
|
13244 |
* and forth parameters of rect() to specify half of the image's width and height. The parameter must
|
|
|
13245 |
* be written in ALL CAPS because Processing is a case sensitive language. Note: In version 125, the
|
|
|
13246 |
* mode named CENTER_RADIUS was shortened to RADIUS.
|
|
|
13247 |
*
|
|
|
13248 |
* @param {MODE} MODE Either CORNER, CORNERS, CENTER, or RADIUS
|
|
|
13249 |
*
|
|
|
13250 |
* @see rect
|
|
|
13251 |
*/
|
|
|
13252 |
p.rectMode = function(aRectMode) {
|
|
|
13253 |
curRectMode = aRectMode;
|
|
|
13254 |
};
|
|
|
13255 |
|
|
|
13256 |
/**
|
|
|
13257 |
* Modifies the location from which images draw. The default mode is imageMode(CORNER), which specifies
|
|
|
13258 |
* the location to be the upper left corner and uses the fourth and fifth parameters of image() to set
|
|
|
13259 |
* the image's width and height. The syntax imageMode(CORNERS) uses the second and third parameters of
|
|
|
13260 |
* image() to set the location of one corner of the image and uses the fourth and fifth parameters to
|
|
|
13261 |
* set the opposite corner. Use imageMode(CENTER) to draw images centered at the given x and y position.
|
|
|
13262 |
* The parameter to imageMode() must be written in ALL CAPS because Processing is a case sensitive language.
|
|
|
13263 |
*
|
|
|
13264 |
* @param {MODE} MODE Either CORNER, CORNERS, or CENTER
|
|
|
13265 |
*
|
|
|
13266 |
* @see loadImage
|
|
|
13267 |
* @see PImage
|
|
|
13268 |
* @see image
|
|
|
13269 |
* @see background
|
|
|
13270 |
*/
|
|
|
13271 |
p.imageMode = function(mode) {
|
|
|
13272 |
switch (mode) {
|
|
|
13273 |
case PConstants.CORNER:
|
|
|
13274 |
imageModeConvert = imageModeCorner;
|
|
|
13275 |
break;
|
|
|
13276 |
case PConstants.CORNERS:
|
|
|
13277 |
imageModeConvert = imageModeCorners;
|
|
|
13278 |
break;
|
|
|
13279 |
case PConstants.CENTER:
|
|
|
13280 |
imageModeConvert = imageModeCenter;
|
|
|
13281 |
break;
|
|
|
13282 |
default:
|
|
|
13283 |
throw "Invalid imageMode";
|
|
|
13284 |
}
|
|
|
13285 |
};
|
|
|
13286 |
|
|
|
13287 |
/**
|
|
|
13288 |
* The origin of the ellipse is modified by the ellipseMode() function. The default configuration is
|
|
|
13289 |
* ellipseMode(CENTER), which specifies the location of the ellipse as the center of the shape. The RADIUS
|
|
|
13290 |
* mode is the same, but the width and height parameters to ellipse() specify the radius of the ellipse,
|
|
|
13291 |
* rather than the diameter. The CORNER mode draws the shape from the upper-left corner of its bounding box.
|
|
|
13292 |
* The CORNERS mode uses the four parameters to ellipse() to set two opposing corners of the ellipse's bounding
|
|
|
13293 |
* box. The parameter must be written in "ALL CAPS" because Processing is a case sensitive language.
|
|
|
13294 |
*
|
|
|
13295 |
* @param {MODE} MODE Either CENTER, RADIUS, CORNER, or CORNERS.
|
|
|
13296 |
*
|
|
|
13297 |
* @see ellipse
|
|
|
13298 |
*/
|
|
|
13299 |
p.ellipseMode = function(aEllipseMode) {
|
|
|
13300 |
curEllipseMode = aEllipseMode;
|
|
|
13301 |
};
|
|
|
13302 |
|
|
|
13303 |
/**
|
|
|
13304 |
* The arc() function draws an arc in the display window.
|
|
|
13305 |
* Arcs are drawn along the outer edge of an ellipse defined by the
|
|
|
13306 |
* <b>x</b>, <b>y</b>, <b>width</b> and <b>height</b> parameters.
|
|
|
13307 |
* The origin or the arc's ellipse may be changed with the
|
|
|
13308 |
* <b>ellipseMode()</b> function.
|
|
|
13309 |
* The <b>start</b> and <b>stop</b> parameters specify the angles
|
|
|
13310 |
* at which to draw the arc.
|
|
|
13311 |
*
|
|
|
13312 |
* @param {float} a x-coordinate of the arc's ellipse
|
|
|
13313 |
* @param {float} b y-coordinate of the arc's ellipse
|
|
|
13314 |
* @param {float} c width of the arc's ellipse
|
|
|
13315 |
* @param {float} d height of the arc's ellipse
|
|
|
13316 |
* @param {float} start angle to start the arc, specified in radians
|
|
|
13317 |
* @param {float} stop angle to stop the arc, specified in radians
|
|
|
13318 |
*
|
|
|
13319 |
* @see #ellipseMode()
|
|
|
13320 |
* @see #ellipse()
|
|
|
13321 |
*/
|
|
|
13322 |
p.arc = function(x, y, width, height, start, stop) {
|
|
|
13323 |
if (width <= 0 || stop < start) { return; }
|
|
|
13324 |
|
|
|
13325 |
if (curEllipseMode === PConstants.CORNERS) {
|
|
|
13326 |
width = width - x;
|
|
|
13327 |
height = height - y;
|
|
|
13328 |
} else if (curEllipseMode === PConstants.RADIUS) {
|
|
|
13329 |
x = x - width;
|
|
|
13330 |
y = y - height;
|
|
|
13331 |
width = width * 2;
|
|
|
13332 |
height = height * 2;
|
|
|
13333 |
} else if (curEllipseMode === PConstants.CENTER) {
|
|
|
13334 |
x = x - width/2;
|
|
|
13335 |
y = y - height/2;
|
|
|
13336 |
}
|
|
|
13337 |
// make sure that we're starting at a useful point
|
|
|
13338 |
while (start < 0) {
|
|
|
13339 |
start += PConstants.TWO_PI;
|
|
|
13340 |
stop += PConstants.TWO_PI;
|
|
|
13341 |
}
|
|
|
13342 |
if (stop - start > PConstants.TWO_PI) {
|
|
|
13343 |
start = 0;
|
|
|
13344 |
stop = PConstants.TWO_PI;
|
|
|
13345 |
}
|
|
|
13346 |
var hr = width / 2;
|
|
|
13347 |
var vr = height / 2;
|
|
|
13348 |
var centerX = x + hr;
|
|
|
13349 |
var centerY = y + vr;
|
|
|
13350 |
var startLUT = 0 | (-0.5 + start * p.RAD_TO_DEG * 2);
|
|
|
13351 |
var stopLUT = 0 | (0.5 + stop * p.RAD_TO_DEG * 2);
|
|
|
13352 |
var i, j;
|
|
|
13353 |
if (doFill) {
|
|
|
13354 |
// shut off stroke for a minute
|
|
|
13355 |
var savedStroke = doStroke;
|
|
|
13356 |
doStroke = false;
|
|
|
13357 |
p.beginShape();
|
|
|
13358 |
p.vertex(centerX, centerY);
|
|
|
13359 |
for (i = startLUT; i <= stopLUT; i++) {
|
|
|
13360 |
j = i % PConstants.SINCOS_LENGTH;
|
|
|
13361 |
p.vertex(centerX + cosLUT[j] * hr, centerY + sinLUT[j] * vr);
|
|
|
13362 |
}
|
|
|
13363 |
p.endShape(PConstants.CLOSE);
|
|
|
13364 |
doStroke = savedStroke;
|
|
|
13365 |
}
|
|
|
13366 |
|
|
|
13367 |
if (doStroke) {
|
|
|
13368 |
// and doesn't include the first (center) vertex.
|
|
|
13369 |
var savedFill = doFill;
|
|
|
13370 |
doFill = false;
|
|
|
13371 |
p.beginShape();
|
|
|
13372 |
for (i = startLUT; i <= stopLUT; i++) {
|
|
|
13373 |
j = i % PConstants.SINCOS_LENGTH;
|
|
|
13374 |
p.vertex(centerX + cosLUT[j] * hr, centerY + sinLUT[j] * vr);
|
|
|
13375 |
}
|
|
|
13376 |
p.endShape();
|
|
|
13377 |
doFill = savedFill;
|
|
|
13378 |
}
|
|
|
13379 |
};
|
|
|
13380 |
|
|
|
13381 |
/**
|
|
|
13382 |
* Draws a line (a direct path between two points) to the screen. The version of line() with four parameters
|
|
|
13383 |
* draws the line in 2D. To color a line, use the stroke() function. A line cannot be filled, therefore the
|
|
|
13384 |
* fill() method will not affect the color of a line. 2D lines are drawn with a width of one pixel by default,
|
|
|
13385 |
* but this can be changed with the strokeWeight() function. The version with six parameters allows the line
|
|
|
13386 |
* to be placed anywhere within XYZ space. Drawing this shape in 3D using the z parameter requires the P3D or
|
|
|
13387 |
* OPENGL parameter in combination with size.
|
|
|
13388 |
*
|
|
|
13389 |
* @param {int|float} x1 x-coordinate of the first point
|
|
|
13390 |
* @param {int|float} y1 y-coordinate of the first point
|
|
|
13391 |
* @param {int|float} z1 z-coordinate of the first point
|
|
|
13392 |
* @param {int|float} x2 x-coordinate of the second point
|
|
|
13393 |
* @param {int|float} y2 y-coordinate of the second point
|
|
|
13394 |
* @param {int|float} z2 z-coordinate of the second point
|
|
|
13395 |
*
|
|
|
13396 |
* @see strokeWeight
|
|
|
13397 |
* @see strokeJoin
|
|
|
13398 |
* @see strokeCap
|
|
|
13399 |
* @see beginShape
|
|
|
13400 |
*/
|
|
|
13401 |
Drawing2D.prototype.line = function(x1, y1, x2, y2) {
|
|
|
13402 |
if (!doStroke) {
|
|
|
13403 |
return;
|
|
|
13404 |
}
|
|
|
13405 |
x1 = Math.round(x1);
|
|
|
13406 |
x2 = Math.round(x2);
|
|
|
13407 |
y1 = Math.round(y1);
|
|
|
13408 |
y2 = Math.round(y2);
|
|
|
13409 |
// A line is only defined if it has different start and end coordinates.
|
|
|
13410 |
// If they are the same, we call point instead.
|
|
|
13411 |
if (x1 === x2 && y1 === y2) {
|
|
|
13412 |
p.point(x1, y1);
|
|
|
13413 |
return;
|
|
|
13414 |
}
|
|
|
13415 |
|
|
|
13416 |
var swap = undef,
|
|
|
13417 |
lineCap = undef,
|
|
|
13418 |
drawCrisp = true,
|
|
|
13419 |
currentModelView = modelView.array(),
|
|
|
13420 |
identityMatrix = [1, 0, 0, 0, 1, 0];
|
|
|
13421 |
// Test if any transformations have been applied to the sketch
|
|
|
13422 |
for (var i = 0; i < 6 && drawCrisp; i++) {
|
|
|
13423 |
drawCrisp = currentModelView[i] === identityMatrix[i];
|
|
|
13424 |
}
|
|
|
13425 |
/* Draw crisp lines if the line is vertical or horizontal with the following method
|
|
|
13426 |
* If any transformations have been applied to the sketch, don't make the line crisp
|
|
|
13427 |
* If the line is directed up or to the left, reverse it by swapping x1/x2 or y1/y2
|
|
|
13428 |
* Make the line 1 pixel longer to work around cross-platform canvas implementations
|
|
|
13429 |
* If the lineWidth is odd, translate the line by 0.5 in the perpendicular direction
|
|
|
13430 |
* Even lineWidths do not need to be translated because the canvas will draw them on pixel boundaries
|
|
|
13431 |
* Change the cap to butt-end to work around cross-platform canvas implementations
|
|
|
13432 |
* Reverse the translate and lineCap canvas state changes after drawing the line
|
|
|
13433 |
*/
|
|
|
13434 |
if (drawCrisp) {
|
|
|
13435 |
if (x1 === x2) {
|
|
|
13436 |
if (y1 > y2) {
|
|
|
13437 |
swap = y1;
|
|
|
13438 |
y1 = y2;
|
|
|
13439 |
y2 = swap;
|
|
|
13440 |
}
|
|
|
13441 |
y2++;
|
|
|
13442 |
if (lineWidth % 2 === 1) {
|
|
|
13443 |
curContext.translate(0.5, 0.0);
|
|
|
13444 |
}
|
|
|
13445 |
} else if (y1 === y2) {
|
|
|
13446 |
if (x1 > x2) {
|
|
|
13447 |
swap = x1;
|
|
|
13448 |
x1 = x2;
|
|
|
13449 |
x2 = swap;
|
|
|
13450 |
}
|
|
|
13451 |
x2++;
|
|
|
13452 |
if (lineWidth % 2 === 1) {
|
|
|
13453 |
curContext.translate(0.0, 0.5);
|
|
|
13454 |
}
|
|
|
13455 |
}
|
|
|
13456 |
if (lineWidth === 1) {
|
|
|
13457 |
lineCap = curContext.lineCap;
|
|
|
13458 |
curContext.lineCap = 'butt';
|
|
|
13459 |
}
|
|
|
13460 |
}
|
|
|
13461 |
curContext.beginPath();
|
|
|
13462 |
curContext.moveTo(x1 || 0, y1 || 0);
|
|
|
13463 |
curContext.lineTo(x2 || 0, y2 || 0);
|
|
|
13464 |
executeContextStroke();
|
|
|
13465 |
if (drawCrisp) {
|
|
|
13466 |
if (x1 === x2 && lineWidth % 2 === 1) {
|
|
|
13467 |
curContext.translate(-0.5, 0.0);
|
|
|
13468 |
} else if (y1 === y2 && lineWidth % 2 === 1) {
|
|
|
13469 |
curContext.translate(0.0, -0.5);
|
|
|
13470 |
}
|
|
|
13471 |
if (lineWidth === 1) {
|
|
|
13472 |
curContext.lineCap = lineCap;
|
|
|
13473 |
}
|
|
|
13474 |
}
|
|
|
13475 |
};
|
|
|
13476 |
|
|
|
13477 |
Drawing3D.prototype.line = function(x1, y1, z1, x2, y2, z2) {
|
|
|
13478 |
if (y2 === undef || z2 === undef) { // 2D line called in 3D context
|
|
|
13479 |
z2 = 0;
|
|
|
13480 |
y2 = x2;
|
|
|
13481 |
x2 = z1;
|
|
|
13482 |
z1 = 0;
|
|
|
13483 |
}
|
|
|
13484 |
|
|
|
13485 |
// a line is only defined if it has different start and end coordinates.
|
|
|
13486 |
// If they are the same, we call point instead.
|
|
|
13487 |
if (x1===x2 && y1===y2 && z1===z2) {
|
|
|
13488 |
p.point(x1,y1,z1);
|
|
|
13489 |
return;
|
|
|
13490 |
}
|
|
|
13491 |
|
|
|
13492 |
var lineVerts = [x1, y1, z1, x2, y2, z2];
|
|
|
13493 |
|
|
|
13494 |
var view = new PMatrix3D();
|
|
|
13495 |
view.scale(1, -1, 1);
|
|
|
13496 |
view.apply(modelView.array());
|
|
|
13497 |
view.transpose();
|
|
|
13498 |
|
|
|
13499 |
if (lineWidth > 0 && doStroke) {
|
|
|
13500 |
curContext.useProgram(programObject2D);
|
|
|
13501 |
|
|
|
13502 |
uniformMatrix("model2d", programObject2D, "model", false, [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]);
|
|
|
13503 |
uniformMatrix("view2d", programObject2D, "view", false, view.array());
|
|
|
13504 |
|
|
|
13505 |
uniformf("color2d", programObject2D, "color", strokeStyle);
|
|
|
13506 |
uniformi("picktype2d", programObject2D, "picktype", 0);
|
|
|
13507 |
|
|
|
13508 |
vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, lineBuffer);
|
|
|
13509 |
disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
|
|
|
13510 |
|
|
|
13511 |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(lineVerts), curContext.STREAM_DRAW);
|
|
|
13512 |
curContext.drawArrays(curContext.LINES, 0, 2);
|
|
|
13513 |
}
|
|
|
13514 |
};
|
|
|
13515 |
|
|
|
13516 |
/**
|
|
|
13517 |
* Draws a Bezier curve on the screen. These curves are defined by a series of anchor and control points. The first
|
|
|
13518 |
* two parameters specify the first anchor point and the last two parameters specify the other anchor point. The
|
|
|
13519 |
* middle parameters specify the control points which define the shape of the curve. Bezier curves were developed
|
|
|
13520 |
* by French engineer Pierre Bezier. Using the 3D version of requires rendering with P3D or OPENGL (see the
|
|
|
13521 |
* Environment reference for more information).
|
|
|
13522 |
*
|
|
|
13523 |
* @param {int | float} x1,y1,z1 coordinates for the first anchor point
|
|
|
13524 |
* @param {int | float} cx1,cy1,cz1 coordinates for the first control point
|
|
|
13525 |
* @param {int | float} cx2,cy2,cz2 coordinates for the second control point
|
|
|
13526 |
* @param {int | float} x2,y2,z2 coordinates for the second anchor point
|
|
|
13527 |
*
|
|
|
13528 |
* @see bezierVertex
|
|
|
13529 |
* @see curve
|
|
|
13530 |
*/
|
|
|
13531 |
Drawing2D.prototype.bezier = function() {
|
|
|
13532 |
if (arguments.length !== 8) {
|
|
|
13533 |
throw("You must use 8 parameters for bezier() in 2D mode");
|
|
|
13534 |
}
|
|
|
13535 |
|
|
|
13536 |
p.beginShape();
|
|
|
13537 |
p.vertex( arguments[0], arguments[1] );
|
|
|
13538 |
p.bezierVertex( arguments[2], arguments[3],
|
|
|
13539 |
arguments[4], arguments[5],
|
|
|
13540 |
arguments[6], arguments[7] );
|
|
|
13541 |
p.endShape();
|
|
|
13542 |
};
|
|
|
13543 |
|
|
|
13544 |
Drawing3D.prototype.bezier = function() {
|
|
|
13545 |
if (arguments.length !== 12) {
|
|
|
13546 |
throw("You must use 12 parameters for bezier() in 3D mode");
|
|
|
13547 |
}
|
|
|
13548 |
|
|
|
13549 |
p.beginShape();
|
|
|
13550 |
p.vertex( arguments[0], arguments[1], arguments[2] );
|
|
|
13551 |
p.bezierVertex( arguments[3], arguments[4], arguments[5],
|
|
|
13552 |
arguments[6], arguments[7], arguments[8],
|
|
|
13553 |
arguments[9], arguments[10], arguments[11] );
|
|
|
13554 |
p.endShape();
|
|
|
13555 |
};
|
|
|
13556 |
|
|
|
13557 |
/**
|
|
|
13558 |
* Sets the resolution at which Beziers display. The default value is 20. This function is only useful when using the P3D
|
|
|
13559 |
* or OPENGL renderer as the default (JAVA2D) renderer does not use this information.
|
|
|
13560 |
*
|
|
|
13561 |
* @param {int} detail resolution of the curves
|
|
|
13562 |
*
|
|
|
13563 |
* @see curve
|
|
|
13564 |
* @see curveVertex
|
|
|
13565 |
* @see curveTightness
|
|
|
13566 |
*/
|
|
|
13567 |
p.bezierDetail = function( detail ){
|
|
|
13568 |
bezDetail = detail;
|
|
|
13569 |
};
|
|
|
13570 |
|
|
|
13571 |
/**
|
|
|
13572 |
* The bezierPoint() function evalutes quadratic bezier at point t for points a, b, c, d.
|
|
|
13573 |
* The parameter t varies between 0 and 1. The a and d parameters are the
|
|
|
13574 |
* on-curve points, b and c are the control points. To make a two-dimensional
|
|
|
13575 |
* curve, call this function once with the x coordinates and a second time
|
|
|
13576 |
* with the y coordinates to get the location of a bezier curve at t.
|
|
|
13577 |
*
|
|
|
13578 |
* @param {float} a coordinate of first point on the curve
|
|
|
13579 |
* @param {float} b coordinate of first control point
|
|
|
13580 |
* @param {float} c coordinate of second control point
|
|
|
13581 |
* @param {float} d coordinate of second point on the curve
|
|
|
13582 |
* @param {float} t value between 0 and 1
|
|
|
13583 |
*
|
|
|
13584 |
* @see #bezier()
|
|
|
13585 |
* @see #bezierVertex()
|
|
|
13586 |
* @see #curvePoint()
|
|
|
13587 |
*/
|
|
|
13588 |
p.bezierPoint = function(a, b, c, d, t) {
|
|
|
13589 |
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;
|
|
|
13590 |
};
|
|
|
13591 |
|
|
|
13592 |
/**
|
|
|
13593 |
* The bezierTangent() function calculates the tangent of a point on a Bezier curve. There is a good
|
|
|
13594 |
* definition of "tangent" at Wikipedia: <a href="http://en.wikipedia.org/wiki/Tangent" target="new">http://en.wikipedia.org/wiki/Tangent</a>
|
|
|
13595 |
*
|
|
|
13596 |
* @param {float} a coordinate of first point on the curve
|
|
|
13597 |
* @param {float} b coordinate of first control point
|
|
|
13598 |
* @param {float} c coordinate of second control point
|
|
|
13599 |
* @param {float} d coordinate of second point on the curve
|
|
|
13600 |
* @param {float} t value between 0 and 1
|
|
|
13601 |
*
|
|
|
13602 |
* @see #bezier()
|
|
|
13603 |
* @see #bezierVertex()
|
|
|
13604 |
* @see #curvePoint()
|
|
|
13605 |
*/
|
|
|
13606 |
p.bezierTangent = function(a, b, c, d, t) {
|
|
|
13607 |
return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b));
|
|
|
13608 |
};
|
|
|
13609 |
|
|
|
13610 |
/**
|
|
|
13611 |
* The curvePoint() function evalutes the Catmull-Rom curve at point t for points a, b, c, d. The
|
|
|
13612 |
* parameter t varies between 0 and 1, a and d are points on the curve,
|
|
|
13613 |
* and b and c are the control points. This can be done once with the x
|
|
|
13614 |
* coordinates and a second time with the y coordinates to get the
|
|
|
13615 |
* location of a curve at t.
|
|
|
13616 |
*
|
|
|
13617 |
* @param {int|float} a coordinate of first point on the curve
|
|
|
13618 |
* @param {int|float} b coordinate of second point on the curve
|
|
|
13619 |
* @param {int|float} c coordinate of third point on the curve
|
|
|
13620 |
* @param {int|float} d coordinate of fourth point on the curve
|
|
|
13621 |
* @param {float} t value between 0 and 1
|
|
|
13622 |
*
|
|
|
13623 |
* @see #curve()
|
|
|
13624 |
* @see #curveVertex()
|
|
|
13625 |
* @see #bezierPoint()
|
|
|
13626 |
*/
|
|
|
13627 |
p.curvePoint = function(a, b, c, d, t) {
|
|
|
13628 |
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);
|
|
|
13629 |
};
|
|
|
13630 |
|
|
|
13631 |
/**
|
|
|
13632 |
* The curveTangent() function calculates the tangent of a point on a Catmull-Rom curve.
|
|
|
13633 |
* 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>.
|
|
|
13634 |
*
|
|
|
13635 |
* @param {int|float} a coordinate of first point on the curve
|
|
|
13636 |
* @param {int|float} b coordinate of first control point
|
|
|
13637 |
* @param {int|float} c coordinate of second control point
|
|
|
13638 |
* @param {int|float} d coordinate of second point on the curve
|
|
|
13639 |
* @param {float} t value between 0 and 1
|
|
|
13640 |
*
|
|
|
13641 |
* @see #curve()
|
|
|
13642 |
* @see #curveVertex()
|
|
|
13643 |
* @see #curvePoint()
|
|
|
13644 |
* @see #bezierTangent()
|
|
|
13645 |
*/
|
|
|
13646 |
p.curveTangent = function(a, b, c, d, t) {
|
|
|
13647 |
return 0.5 * ((-a + c) + 2 * (2 * a - 5 * b + 4 * c - d) * t + 3 * (-a + 3 * b - 3 * c + d) * t * t);
|
|
|
13648 |
};
|
|
|
13649 |
|
|
|
13650 |
/**
|
|
|
13651 |
* A triangle is a plane created by connecting three points. The first two arguments specify the first point,
|
|
|
13652 |
* the middle two arguments specify the second point, and the last two arguments specify the third point.
|
|
|
13653 |
*
|
|
|
13654 |
* @param {int | float} x1 x-coordinate of the first point
|
|
|
13655 |
* @param {int | float} y1 y-coordinate of the first point
|
|
|
13656 |
* @param {int | float} x2 x-coordinate of the second point
|
|
|
13657 |
* @param {int | float} y2 y-coordinate of the second point
|
|
|
13658 |
* @param {int | float} x3 x-coordinate of the third point
|
|
|
13659 |
* @param {int | float} y3 y-coordinate of the third point
|
|
|
13660 |
*/
|
|
|
13661 |
p.triangle = function(x1, y1, x2, y2, x3, y3) {
|
|
|
13662 |
p.beginShape(PConstants.TRIANGLES);
|
|
|
13663 |
p.vertex(x1, y1, 0);
|
|
|
13664 |
p.vertex(x2, y2, 0);
|
|
|
13665 |
p.vertex(x3, y3, 0);
|
|
|
13666 |
p.endShape();
|
|
|
13667 |
};
|
|
|
13668 |
|
|
|
13669 |
/**
|
|
|
13670 |
* A quad is a quadrilateral, a four sided polygon. It is similar to a rectangle, but the angles between its
|
|
|
13671 |
* edges are not constrained to ninety degrees. The first pair of parameters (x1,y1) sets the first vertex
|
|
|
13672 |
* and the subsequent pairs should proceed clockwise or counter-clockwise around the defined shape.
|
|
|
13673 |
*
|
|
|
13674 |
* @param {float | int} x1 x-coordinate of the first corner
|
|
|
13675 |
* @param {float | int} y1 y-coordinate of the first corner
|
|
|
13676 |
* @param {float | int} x2 x-coordinate of the second corner
|
|
|
13677 |
* @param {float | int} y2 y-coordinate of the second corner
|
|
|
13678 |
* @param {float | int} x3 x-coordinate of the third corner
|
|
|
13679 |
* @param {float | int} y3 y-coordinate of the third corner
|
|
|
13680 |
* @param {float | int} x4 x-coordinate of the fourth corner
|
|
|
13681 |
* @param {float | int} y4 y-coordinate of the fourth corner
|
|
|
13682 |
*/
|
|
|
13683 |
p.quad = function(x1, y1, x2, y2, x3, y3, x4, y4) {
|
|
|
13684 |
p.beginShape(PConstants.QUADS);
|
|
|
13685 |
p.vertex(x1, y1, 0);
|
|
|
13686 |
p.vertex(x2, y2, 0);
|
|
|
13687 |
p.vertex(x3, y3, 0);
|
|
|
13688 |
p.vertex(x4, y4, 0);
|
|
|
13689 |
p.endShape();
|
|
|
13690 |
};
|
|
|
13691 |
|
|
|
13692 |
var roundedRect$2d = function(x, y, width, height, tl, tr, br, bl) {
|
|
|
13693 |
if (bl === undef) {
|
|
|
13694 |
tr = tl;
|
|
|
13695 |
br = tl;
|
|
|
13696 |
bl = tl;
|
|
|
13697 |
}
|
|
|
13698 |
var halfWidth = width / 2,
|
|
|
13699 |
halfHeight = height / 2;
|
|
|
13700 |
if (tl > halfWidth || tl > halfHeight) {
|
|
|
13701 |
tl = Math.min(halfWidth, halfHeight);
|
|
|
13702 |
}
|
|
|
13703 |
if (tr > halfWidth || tr > halfHeight) {
|
|
|
13704 |
tr = Math.min(halfWidth, halfHeight);
|
|
|
13705 |
}
|
|
|
13706 |
if (br > halfWidth || br > halfHeight) {
|
|
|
13707 |
br = Math.min(halfWidth, halfHeight);
|
|
|
13708 |
}
|
|
|
13709 |
if (bl > halfWidth || bl > halfHeight) {
|
|
|
13710 |
bl = Math.min(halfWidth, halfHeight);
|
|
|
13711 |
}
|
|
|
13712 |
// Translate the stroke by (0.5, 0.5) to draw a crisp border
|
|
|
13713 |
if (!doFill || doStroke) {
|
|
|
13714 |
curContext.translate(0.5, 0.5);
|
|
|
13715 |
}
|
|
|
13716 |
curContext.beginPath();
|
|
|
13717 |
curContext.moveTo(x + tl, y);
|
|
|
13718 |
curContext.lineTo(x + width - tr, y);
|
|
|
13719 |
curContext.quadraticCurveTo(x + width, y, x + width, y + tr);
|
|
|
13720 |
curContext.lineTo(x + width, y + height - br);
|
|
|
13721 |
curContext.quadraticCurveTo(x + width, y + height, x + width - br, y + height);
|
|
|
13722 |
curContext.lineTo(x + bl, y + height);
|
|
|
13723 |
curContext.quadraticCurveTo(x, y + height, x, y + height - bl);
|
|
|
13724 |
curContext.lineTo(x, y + tl);
|
|
|
13725 |
curContext.quadraticCurveTo(x, y, x + tl, y);
|
|
|
13726 |
if (!doFill || doStroke) {
|
|
|
13727 |
curContext.translate(-0.5, -0.5);
|
|
|
13728 |
}
|
|
|
13729 |
executeContextFill();
|
|
|
13730 |
executeContextStroke();
|
|
|
13731 |
};
|
|
|
13732 |
|
|
|
13733 |
/**
|
|
|
13734 |
* Draws a rectangle to the screen. A rectangle is a four-sided shape with every angle at ninety
|
|
|
13735 |
* degrees. The first two parameters set the location, the third sets the width, and the fourth
|
|
|
13736 |
* sets the height. The origin is changed with the rectMode() function.
|
|
|
13737 |
*
|
|
|
13738 |
* @param {int|float} x x-coordinate of the rectangle
|
|
|
13739 |
* @param {int|float} y y-coordinate of the rectangle
|
|
|
13740 |
* @param {int|float} width width of the rectangle
|
|
|
13741 |
* @param {int|float} height height of the rectangle
|
|
|
13742 |
*
|
|
|
13743 |
* @see rectMode
|
|
|
13744 |
* @see quad
|
|
|
13745 |
*/
|
|
|
13746 |
Drawing2D.prototype.rect = function(x, y, width, height, tl, tr, br, bl) {
|
|
|
13747 |
if (!width && !height) {
|
|
|
13748 |
return;
|
|
|
13749 |
}
|
|
|
13750 |
|
|
|
13751 |
if (curRectMode === PConstants.CORNERS) {
|
|
|
13752 |
width -= x;
|
|
|
13753 |
height -= y;
|
|
|
13754 |
} else if (curRectMode === PConstants.RADIUS) {
|
|
|
13755 |
width *= 2;
|
|
|
13756 |
height *= 2;
|
|
|
13757 |
x -= width / 2;
|
|
|
13758 |
y -= height / 2;
|
|
|
13759 |
} else if (curRectMode === PConstants.CENTER) {
|
|
|
13760 |
x -= width / 2;
|
|
|
13761 |
y -= height / 2;
|
|
|
13762 |
}
|
|
|
13763 |
|
|
|
13764 |
x = Math.round(x);
|
|
|
13765 |
y = Math.round(y);
|
|
|
13766 |
width = Math.round(width);
|
|
|
13767 |
height = Math.round(height);
|
|
|
13768 |
if (tl !== undef) {
|
|
|
13769 |
roundedRect$2d(x, y, width, height, tl, tr, br, bl);
|
|
|
13770 |
return;
|
|
|
13771 |
}
|
|
|
13772 |
|
|
|
13773 |
// Translate the line by (0.5, 0.5) to draw a crisp rectangle border
|
|
|
13774 |
if (doStroke && lineWidth % 2 === 1) {
|
|
|
13775 |
curContext.translate(0.5, 0.5);
|
|
|
13776 |
}
|
|
|
13777 |
curContext.beginPath();
|
|
|
13778 |
curContext.rect(x, y, width, height);
|
|
|
13779 |
executeContextFill();
|
|
|
13780 |
executeContextStroke();
|
|
|
13781 |
if (doStroke && lineWidth % 2 === 1) {
|
|
|
13782 |
curContext.translate(-0.5, -0.5);
|
|
|
13783 |
}
|
|
|
13784 |
};
|
|
|
13785 |
|
|
|
13786 |
Drawing3D.prototype.rect = function(x, y, width, height, tl, tr, br, bl) {
|
|
|
13787 |
if (tl !== undef) {
|
|
|
13788 |
throw "rect() with rounded corners is not supported in 3D mode";
|
|
|
13789 |
}
|
|
|
13790 |
|
|
|
13791 |
if (curRectMode === PConstants.CORNERS) {
|
|
|
13792 |
width -= x;
|
|
|
13793 |
height -= y;
|
|
|
13794 |
} else if (curRectMode === PConstants.RADIUS) {
|
|
|
13795 |
width *= 2;
|
|
|
13796 |
height *= 2;
|
|
|
13797 |
x -= width / 2;
|
|
|
13798 |
y -= height / 2;
|
|
|
13799 |
} else if (curRectMode === PConstants.CENTER) {
|
|
|
13800 |
x -= width / 2;
|
|
|
13801 |
y -= height / 2;
|
|
|
13802 |
}
|
|
|
13803 |
|
|
|
13804 |
// Modeling transformation
|
|
|
13805 |
var model = new PMatrix3D();
|
|
|
13806 |
model.translate(x, y, 0);
|
|
|
13807 |
model.scale(width, height, 1);
|
|
|
13808 |
model.transpose();
|
|
|
13809 |
|
|
|
13810 |
// viewing transformation needs to have Y flipped
|
|
|
13811 |
// becuase that's what Processing does.
|
|
|
13812 |
var view = new PMatrix3D();
|
|
|
13813 |
view.scale(1, -1, 1);
|
|
|
13814 |
view.apply(modelView.array());
|
|
|
13815 |
view.transpose();
|
|
|
13816 |
|
|
|
13817 |
if (lineWidth > 0 && doStroke) {
|
|
|
13818 |
curContext.useProgram(programObject2D);
|
|
|
13819 |
uniformMatrix("model2d", programObject2D, "model", false, model.array());
|
|
|
13820 |
uniformMatrix("view2d", programObject2D, "view", false, view.array());
|
|
|
13821 |
uniformf("color2d", programObject2D, "color", strokeStyle);
|
|
|
13822 |
uniformi("picktype2d", programObject2D, "picktype", 0);
|
|
|
13823 |
vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, rectBuffer);
|
|
|
13824 |
disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
|
|
|
13825 |
curContext.drawArrays(curContext.LINE_LOOP, 0, rectVerts.length / 3);
|
|
|
13826 |
}
|
|
|
13827 |
|
|
|
13828 |
if (doFill) {
|
|
|
13829 |
curContext.useProgram(programObject3D);
|
|
|
13830 |
uniformMatrix("model3d", programObject3D, "model", false, model.array());
|
|
|
13831 |
uniformMatrix("view3d", programObject3D, "view", false, view.array());
|
|
|
13832 |
|
|
|
13833 |
// fix stitching problems. (lines get occluded by triangles
|
|
|
13834 |
// since they share the same depth values). This is not entirely
|
|
|
13835 |
// working, but it's a start for drawing the outline. So
|
|
|
13836 |
// developers can start playing around with styles.
|
|
|
13837 |
curContext.enable(curContext.POLYGON_OFFSET_FILL);
|
|
|
13838 |
curContext.polygonOffset(1, 1);
|
|
|
13839 |
|
|
|
13840 |
uniformf("color3d", programObject3D, "color", fillStyle);
|
|
|
13841 |
|
|
|
13842 |
if(lightCount > 0){
|
|
|
13843 |
var v = new PMatrix3D();
|
|
|
13844 |
v.set(view);
|
|
|
13845 |
|
|
|
13846 |
var m = new PMatrix3D();
|
|
|
13847 |
m.set(model);
|
|
|
13848 |
|
|
|
13849 |
v.mult(m);
|
|
|
13850 |
|
|
|
13851 |
var normalMatrix = new PMatrix3D();
|
|
|
13852 |
normalMatrix.set(v);
|
|
|
13853 |
normalMatrix.invert();
|
|
|
13854 |
normalMatrix.transpose();
|
|
|
13855 |
|
|
|
13856 |
uniformMatrix("normalTransform3d", programObject3D, "normalTransform", false, normalMatrix.array());
|
|
|
13857 |
vertexAttribPointer("normal3d", programObject3D, "Normal", 3, rectNormBuffer);
|
|
|
13858 |
}
|
|
|
13859 |
else{
|
|
|
13860 |
disableVertexAttribPointer("normal3d", programObject3D, "Normal");
|
|
|
13861 |
}
|
|
|
13862 |
|
|
|
13863 |
vertexAttribPointer("vertex3d", programObject3D, "Vertex", 3, rectBuffer);
|
|
|
13864 |
|
|
|
13865 |
curContext.drawArrays(curContext.TRIANGLE_FAN, 0, rectVerts.length / 3);
|
|
|
13866 |
curContext.disable(curContext.POLYGON_OFFSET_FILL);
|
|
|
13867 |
}
|
|
|
13868 |
};
|
|
|
13869 |
|
|
|
13870 |
/**
|
|
|
13871 |
* Draws an ellipse (oval) in the display window. An ellipse with an equal <b>width</b> and <b>height</b> is a circle.
|
|
|
13872 |
* The first two parameters set the location, the third sets the width, and the fourth sets the height. The origin may be
|
|
|
13873 |
* changed with the <b>ellipseMode()</b> function.
|
|
|
13874 |
*
|
|
|
13875 |
* @param {float|int} x x-coordinate of the ellipse
|
|
|
13876 |
* @param {float|int} y y-coordinate of the ellipse
|
|
|
13877 |
* @param {float|int} width width of the ellipse
|
|
|
13878 |
* @param {float|int} height height of the ellipse
|
|
|
13879 |
*
|
|
|
13880 |
* @see ellipseMode
|
|
|
13881 |
*/
|
|
|
13882 |
Drawing2D.prototype.ellipse = function(x, y, width, height) {
|
|
|
13883 |
x = x || 0;
|
|
|
13884 |
y = y || 0;
|
|
|
13885 |
|
|
|
13886 |
if (width <= 0 && height <= 0) {
|
|
|
13887 |
return;
|
|
|
13888 |
}
|
|
|
13889 |
|
|
|
13890 |
if (curEllipseMode === PConstants.RADIUS) {
|
|
|
13891 |
width *= 2;
|
|
|
13892 |
height *= 2;
|
|
|
13893 |
} else if (curEllipseMode === PConstants.CORNERS) {
|
|
|
13894 |
width = width - x;
|
|
|
13895 |
height = height - y;
|
|
|
13896 |
x += width / 2;
|
|
|
13897 |
y += height / 2;
|
|
|
13898 |
} else if (curEllipseMode === PConstants.CORNER) {
|
|
|
13899 |
x += width / 2;
|
|
|
13900 |
y += height / 2;
|
|
|
13901 |
}
|
|
|
13902 |
|
|
|
13903 |
// Shortcut for drawing a 2D circle
|
|
|
13904 |
if (width === height) {
|
|
|
13905 |
curContext.beginPath();
|
|
|
13906 |
curContext.arc(x, y, width / 2, 0, PConstants.TWO_PI, false);
|
|
|
13907 |
executeContextFill();
|
|
|
13908 |
executeContextStroke();
|
|
|
13909 |
} else {
|
|
|
13910 |
var w = width / 2,
|
|
|
13911 |
h = height / 2,
|
|
|
13912 |
C = 0.5522847498307933,
|
|
|
13913 |
c_x = C * w,
|
|
|
13914 |
c_y = C * h;
|
|
|
13915 |
|
|
|
13916 |
p.beginShape();
|
|
|
13917 |
p.vertex(x + w, y);
|
|
|
13918 |
p.bezierVertex(x + w, y - c_y, x + c_x, y - h, x, y - h);
|
|
|
13919 |
p.bezierVertex(x - c_x, y - h, x - w, y - c_y, x - w, y);
|
|
|
13920 |
p.bezierVertex(x - w, y + c_y, x - c_x, y + h, x, y + h);
|
|
|
13921 |
p.bezierVertex(x + c_x, y + h, x + w, y + c_y, x + w, y);
|
|
|
13922 |
p.endShape();
|
|
|
13923 |
}
|
|
|
13924 |
};
|
|
|
13925 |
|
|
|
13926 |
Drawing3D.prototype.ellipse = function(x, y, width, height) {
|
|
|
13927 |
x = x || 0;
|
|
|
13928 |
y = y || 0;
|
|
|
13929 |
|
|
|
13930 |
if (width <= 0 && height <= 0) {
|
|
|
13931 |
return;
|
|
|
13932 |
}
|
|
|
13933 |
|
|
|
13934 |
if (curEllipseMode === PConstants.RADIUS) {
|
|
|
13935 |
width *= 2;
|
|
|
13936 |
height *= 2;
|
|
|
13937 |
} else if (curEllipseMode === PConstants.CORNERS) {
|
|
|
13938 |
width = width - x;
|
|
|
13939 |
height = height - y;
|
|
|
13940 |
x += width / 2;
|
|
|
13941 |
y += height / 2;
|
|
|
13942 |
} else if (curEllipseMode === PConstants.CORNER) {
|
|
|
13943 |
x += width / 2;
|
|
|
13944 |
y += height / 2;
|
|
|
13945 |
}
|
|
|
13946 |
|
|
|
13947 |
var w = width / 2,
|
|
|
13948 |
h = height / 2,
|
|
|
13949 |
C = 0.5522847498307933,
|
|
|
13950 |
c_x = C * w,
|
|
|
13951 |
c_y = C * h;
|
|
|
13952 |
|
|
|
13953 |
p.beginShape();
|
|
|
13954 |
p.vertex(x + w, y);
|
|
|
13955 |
p.bezierVertex(x + w, y - c_y, 0, x + c_x, y - h, 0, x, y - h, 0);
|
|
|
13956 |
p.bezierVertex(x - c_x, y - h, 0, x - w, y - c_y, 0, x - w, y, 0);
|
|
|
13957 |
p.bezierVertex(x - w, y + c_y, 0, x - c_x, y + h, 0, x, y + h, 0);
|
|
|
13958 |
p.bezierVertex(x + c_x, y + h, 0, x + w, y + c_y, 0, x + w, y, 0);
|
|
|
13959 |
p.endShape();
|
|
|
13960 |
|
|
|
13961 |
if (doFill) {
|
|
|
13962 |
//temporary workaround to not working fills for bezier -- will fix later
|
|
|
13963 |
var xAv = 0, yAv = 0, i, j;
|
|
|
13964 |
for (i = 0; i < vertArray.length; i++) {
|
|
|
13965 |
xAv += vertArray[i][0];
|
|
|
13966 |
yAv += vertArray[i][1];
|
|
|
13967 |
}
|
|
|
13968 |
xAv /= vertArray.length;
|
|
|
13969 |
yAv /= vertArray.length;
|
|
|
13970 |
var vert = [],
|
|
|
13971 |
fillVertArray = [],
|
|
|
13972 |
colorVertArray = [];
|
|
|
13973 |
vert[0] = xAv;
|
|
|
13974 |
vert[1] = yAv;
|
|
|
13975 |
vert[2] = 0;
|
|
|
13976 |
vert[3] = 0;
|
|
|
13977 |
vert[4] = 0;
|
|
|
13978 |
vert[5] = fillStyle[0];
|
|
|
13979 |
vert[6] = fillStyle[1];
|
|
|
13980 |
vert[7] = fillStyle[2];
|
|
|
13981 |
vert[8] = fillStyle[3];
|
|
|
13982 |
vert[9] = strokeStyle[0];
|
|
|
13983 |
vert[10] = strokeStyle[1];
|
|
|
13984 |
vert[11] = strokeStyle[2];
|
|
|
13985 |
vert[12] = strokeStyle[3];
|
|
|
13986 |
vert[13] = normalX;
|
|
|
13987 |
vert[14] = normalY;
|
|
|
13988 |
vert[15] = normalZ;
|
|
|
13989 |
vertArray.unshift(vert);
|
|
|
13990 |
for (i = 0; i < vertArray.length; i++) {
|
|
|
13991 |
for (j = 0; j < 3; j++) {
|
|
|
13992 |
fillVertArray.push(vertArray[i][j]);
|
|
|
13993 |
}
|
|
|
13994 |
for (j = 5; j < 9; j++) {
|
|
|
13995 |
colorVertArray.push(vertArray[i][j]);
|
|
|
13996 |
}
|
|
|
13997 |
}
|
|
|
13998 |
fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray);
|
|
|
13999 |
}
|
|
|
14000 |
};
|
|
|
14001 |
|
|
|
14002 |
/**
|
|
|
14003 |
* Sets the current normal vector. This is for drawing three dimensional shapes and surfaces and
|
|
|
14004 |
* specifies a vector perpendicular to the surface of the shape which determines how lighting affects
|
|
|
14005 |
* it. Processing attempts to automatically assign normals to shapes, but since that's imperfect,
|
|
|
14006 |
* this is a better option when you want more control. This function is identical to glNormal3f() in OpenGL.
|
|
|
14007 |
*
|
|
|
14008 |
* @param {float} nx x direction
|
|
|
14009 |
* @param {float} ny y direction
|
|
|
14010 |
* @param {float} nz z direction
|
|
|
14011 |
*
|
|
|
14012 |
* @see beginShape
|
|
|
14013 |
* @see endShape
|
|
|
14014 |
* @see lights
|
|
|
14015 |
*/
|
|
|
14016 |
p.normal = function(nx, ny, nz) {
|
|
|
14017 |
if (arguments.length !== 3 || !(typeof nx === "number" && typeof ny === "number" && typeof nz === "number")) {
|
|
|
14018 |
throw "normal() requires three numeric arguments.";
|
|
|
14019 |
}
|
|
|
14020 |
|
|
|
14021 |
normalX = nx;
|
|
|
14022 |
normalY = ny;
|
|
|
14023 |
normalZ = nz;
|
|
|
14024 |
|
|
|
14025 |
if (curShape !== 0) {
|
|
|
14026 |
if (normalMode === PConstants.NORMAL_MODE_AUTO) {
|
|
|
14027 |
normalMode = PConstants.NORMAL_MODE_SHAPE;
|
|
|
14028 |
} else if (normalMode === PConstants.NORMAL_MODE_SHAPE) {
|
|
|
14029 |
normalMode = PConstants.NORMAL_MODE_VERTEX;
|
|
|
14030 |
}
|
|
|
14031 |
}
|
|
|
14032 |
};
|
|
|
14033 |
|
|
|
14034 |
////////////////////////////////////////////////////////////////////////////
|
|
|
14035 |
// Raster drawing functions
|
|
|
14036 |
////////////////////////////////////////////////////////////////////////////
|
|
|
14037 |
|
|
|
14038 |
/**
|
|
|
14039 |
* Saves an image from the display window. Images are saved in TIFF, TARGA, JPEG, and PNG format
|
|
|
14040 |
* depending on the extension within the filename parameter. For example, "image.tif" will have
|
|
|
14041 |
* a TIFF image and "image.png" will save a PNG image. If no extension is included in the filename,
|
|
|
14042 |
* the image will save in TIFF format and .tif will be added to the name. These files are saved to
|
|
|
14043 |
* the sketch's folder, which may be opened by selecting "Show sketch folder" from the "Sketch" menu.
|
|
|
14044 |
* It is not possible to use save() while running the program in a web browser. All images saved
|
|
|
14045 |
* from the main drawing window will be opaque. To save images without a background, use createGraphics().
|
|
|
14046 |
*
|
|
|
14047 |
* @param {String} filename any sequence of letters and numbers
|
|
|
14048 |
*
|
|
|
14049 |
* @see saveFrame
|
|
|
14050 |
* @see createGraphics
|
|
|
14051 |
*/
|
|
|
14052 |
p.save = function(file, img) {
|
|
|
14053 |
// file is unused at the moment
|
|
|
14054 |
// may implement this differently in later release
|
|
|
14055 |
if (img !== undef) {
|
|
|
14056 |
return window.open(img.toDataURL(),"_blank");
|
|
|
14057 |
}
|
|
|
14058 |
return window.open(p.externals.canvas.toDataURL(),"_blank");
|
|
|
14059 |
};
|
|
|
14060 |
|
|
|
14061 |
var saveNumber = 0;
|
|
|
14062 |
|
|
|
14063 |
p.saveFrame = function(file) {
|
|
|
14064 |
if(file === undef) {
|
|
|
14065 |
// use default name template if parameter is not specified
|
|
|
14066 |
file = "screen-####.png";
|
|
|
14067 |
}
|
|
|
14068 |
// Increment changeable part: screen-0000.png, screen-0001.png, ...
|
|
|
14069 |
var frameFilename = file.replace(/#+/, function(all) {
|
|
|
14070 |
var s = "" + (saveNumber++);
|
|
|
14071 |
while(s.length < all.length) {
|
|
|
14072 |
s = "0" + s;
|
|
|
14073 |
}
|
|
|
14074 |
return s;
|
|
|
14075 |
});
|
|
|
14076 |
p.save(frameFilename);
|
|
|
14077 |
};
|
|
|
14078 |
|
|
|
14079 |
var utilityContext2d = document.createElement("canvas").getContext("2d");
|
|
|
14080 |
|
|
|
14081 |
var canvasDataCache = [undef, undef, undef]; // we need three for now
|
|
|
14082 |
|
|
|
14083 |
function getCanvasData(obj, w, h) {
|
|
|
14084 |
var canvasData = canvasDataCache.shift();
|
|
|
14085 |
|
|
|
14086 |
if (canvasData === undef) {
|
|
|
14087 |
canvasData = {};
|
|
|
14088 |
canvasData.canvas = document.createElement("canvas");
|
|
|
14089 |
canvasData.context = canvasData.canvas.getContext('2d');
|
|
|
14090 |
}
|
|
|
14091 |
|
|
|
14092 |
canvasDataCache.push(canvasData);
|
|
|
14093 |
|
|
|
14094 |
var canvas = canvasData.canvas, context = canvasData.context,
|
|
|
14095 |
width = w || obj.width, height = h || obj.height;
|
|
|
14096 |
|
|
|
14097 |
canvas.width = width;
|
|
|
14098 |
canvas.height = height;
|
|
|
14099 |
|
|
|
14100 |
if (!obj) {
|
|
|
14101 |
context.clearRect(0, 0, width, height);
|
|
|
14102 |
} else if ("data" in obj) { // ImageData
|
|
|
14103 |
context.putImageData(obj, 0, 0);
|
|
|
14104 |
} else {
|
|
|
14105 |
context.clearRect(0, 0, width, height);
|
|
|
14106 |
context.drawImage(obj, 0, 0, width, height);
|
|
|
14107 |
}
|
|
|
14108 |
return canvasData;
|
|
|
14109 |
}
|
|
|
14110 |
|
|
|
14111 |
/**
|
|
|
14112 |
* Handle the sketch code for pixels[] and pixels.length
|
|
|
14113 |
* parser code converts pixels[] to getPixels()
|
|
|
14114 |
* or setPixels(), .length becomes getLength()
|
|
|
14115 |
*/
|
|
|
14116 |
function buildPixelsObject(pImage) {
|
|
|
14117 |
return {
|
|
|
14118 |
|
|
|
14119 |
getLength: (function(aImg) {
|
|
|
14120 |
return function() {
|
|
|
14121 |
if (aImg.isRemote) {
|
|
|
14122 |
throw "Image is loaded remotely. Cannot get length.";
|
|
|
14123 |
} else {
|
|
|
14124 |
return aImg.imageData.data.length ? aImg.imageData.data.length/4 : 0;
|
|
|
14125 |
}
|
|
|
14126 |
};
|
|
|
14127 |
}(pImage)),
|
|
|
14128 |
|
|
|
14129 |
getPixel: (function(aImg) {
|
|
|
14130 |
return function(i) {
|
|
|
14131 |
var offset = i*4,
|
|
|
14132 |
data = aImg.imageData.data;
|
|
|
14133 |
|
|
|
14134 |
if (aImg.isRemote) {
|
|
|
14135 |
throw "Image is loaded remotely. Cannot get pixels.";
|
|
|
14136 |
}
|
|
|
14137 |
|
|
|
14138 |
return (data[offset+3] << 24) & PConstants.ALPHA_MASK |
|
|
|
14139 |
(data[offset] << 16) & PConstants.RED_MASK |
|
|
|
14140 |
(data[offset+1] << 8) & PConstants.GREEN_MASK |
|
|
|
14141 |
data[offset+2] & PConstants.BLUE_MASK;
|
|
|
14142 |
};
|
|
|
14143 |
}(pImage)),
|
|
|
14144 |
|
|
|
14145 |
setPixel: (function(aImg) {
|
|
|
14146 |
return function(i, c) {
|
|
|
14147 |
var offset = i*4,
|
|
|
14148 |
data = aImg.imageData.data;
|
|
|
14149 |
|
|
|
14150 |
if (aImg.isRemote) {
|
|
|
14151 |
throw "Image is loaded remotely. Cannot set pixel.";
|
|
|
14152 |
}
|
|
|
14153 |
|
|
|
14154 |
data[offset+0] = (c & PConstants.RED_MASK) >>> 16;
|
|
|
14155 |
data[offset+1] = (c & PConstants.GREEN_MASK) >>> 8;
|
|
|
14156 |
data[offset+2] = (c & PConstants.BLUE_MASK);
|
|
|
14157 |
data[offset+3] = (c & PConstants.ALPHA_MASK) >>> 24;
|
|
|
14158 |
aImg.__isDirty = true;
|
|
|
14159 |
};
|
|
|
14160 |
}(pImage)),
|
|
|
14161 |
|
|
|
14162 |
toArray: (function(aImg) {
|
|
|
14163 |
return function() {
|
|
|
14164 |
var arr = [],
|
|
|
14165 |
data = aImg.imageData.data,
|
|
|
14166 |
length = aImg.width * aImg.height;
|
|
|
14167 |
|
|
|
14168 |
if (aImg.isRemote) {
|
|
|
14169 |
throw "Image is loaded remotely. Cannot get pixels.";
|
|
|
14170 |
}
|
|
|
14171 |
|
|
|
14172 |
for (var i = 0, offset = 0; i < length; i++, offset += 4) {
|
|
|
14173 |
arr.push( (data[offset+3] << 24) & PConstants.ALPHA_MASK |
|
|
|
14174 |
(data[offset] << 16) & PConstants.RED_MASK |
|
|
|
14175 |
(data[offset+1] << 8) & PConstants.GREEN_MASK |
|
|
|
14176 |
data[offset+2] & PConstants.BLUE_MASK );
|
|
|
14177 |
}
|
|
|
14178 |
return arr;
|
|
|
14179 |
};
|
|
|
14180 |
}(pImage)),
|
|
|
14181 |
|
|
|
14182 |
set: (function(aImg) {
|
|
|
14183 |
return function(arr) {
|
|
|
14184 |
var offset,
|
|
|
14185 |
data,
|
|
|
14186 |
c;
|
|
|
14187 |
if (this.isRemote) {
|
|
|
14188 |
throw "Image is loaded remotely. Cannot set pixels.";
|
|
|
14189 |
}
|
|
|
14190 |
|
|
|
14191 |
data = aImg.imageData.data;
|
|
|
14192 |
for (var i = 0, aL = arr.length; i < aL; i++) {
|
|
|
14193 |
c = arr[i];
|
|
|
14194 |
offset = i*4;
|
|
|
14195 |
|
|
|
14196 |
data[offset+0] = (c & PConstants.RED_MASK) >>> 16;
|
|
|
14197 |
data[offset+1] = (c & PConstants.GREEN_MASK) >>> 8;
|
|
|
14198 |
data[offset+2] = (c & PConstants.BLUE_MASK);
|
|
|
14199 |
data[offset+3] = (c & PConstants.ALPHA_MASK) >>> 24;
|
|
|
14200 |
}
|
|
|
14201 |
aImg.__isDirty = true;
|
|
|
14202 |
};
|
|
|
14203 |
}(pImage))
|
|
|
14204 |
|
|
|
14205 |
};
|
|
|
14206 |
}
|
|
|
14207 |
|
|
|
14208 |
/**
|
|
|
14209 |
* Datatype for storing images. Processing can display .gif, .jpg, .tga, and .png images. Images may be
|
|
|
14210 |
* displayed in 2D and 3D space. Before an image is used, it must be loaded with the loadImage() function.
|
|
|
14211 |
* The PImage object contains fields for the width and height of the image, as well as an array called
|
|
|
14212 |
* pixels[] which contains the values for every pixel in the image. A group of methods, described below,
|
|
|
14213 |
* allow easy access to the image's pixels and alpha channel and simplify the process of compositing.
|
|
|
14214 |
* Before using the pixels[] array, be sure to use the loadPixels() method on the image to make sure that the
|
|
|
14215 |
* pixel data is properly loaded. To create a new image, use the createImage() function (do not use new PImage()).
|
|
|
14216 |
*
|
|
|
14217 |
* @param {int} width image width
|
|
|
14218 |
* @param {int} height image height
|
|
|
14219 |
* @param {MODE} format Either RGB, ARGB, ALPHA (grayscale alpha channel)
|
|
|
14220 |
*
|
|
|
14221 |
* @returns {PImage}
|
|
|
14222 |
*
|
|
|
14223 |
* @see loadImage
|
|
|
14224 |
* @see imageMode
|
|
|
14225 |
* @see createImage
|
|
|
14226 |
*/
|
|
|
14227 |
var PImage = function(aWidth, aHeight, aFormat) {
|
|
|
14228 |
|
|
|
14229 |
// Keep track of whether or not the cached imageData has been touched.
|
|
|
14230 |
this.__isDirty = false;
|
|
|
14231 |
|
|
|
14232 |
if (aWidth instanceof HTMLImageElement) {
|
|
|
14233 |
// convert an <img> to a PImage
|
|
|
14234 |
this.fromHTMLImageData(aWidth);
|
|
|
14235 |
} else if (aHeight || aFormat) {
|
|
|
14236 |
this.width = aWidth || 1;
|
|
|
14237 |
this.height = aHeight || 1;
|
|
|
14238 |
|
|
|
14239 |
// Stuff a canvas into sourceImg so image() calls can use drawImage like an <img>
|
|
|
14240 |
var canvas = this.sourceImg = document.createElement("canvas");
|
|
|
14241 |
canvas.width = this.width;
|
|
|
14242 |
canvas.height = this.height;
|
|
|
14243 |
|
|
|
14244 |
var imageData = this.imageData = canvas.getContext('2d').createImageData(this.width, this.height);
|
|
|
14245 |
this.format = (aFormat === PConstants.ARGB || aFormat === PConstants.ALPHA) ? aFormat : PConstants.RGB;
|
|
|
14246 |
if (this.format === PConstants.RGB) {
|
|
|
14247 |
// Set the alpha channel of an RGB image to opaque.
|
|
|
14248 |
for (var i = 3, data = this.imageData.data, len = data.length; i < len; i += 4) {
|
|
|
14249 |
data[i] = 255;
|
|
|
14250 |
}
|
|
|
14251 |
}
|
|
|
14252 |
|
|
|
14253 |
this.__isDirty = true;
|
|
|
14254 |
this.updatePixels();
|
|
|
14255 |
} else {
|
|
|
14256 |
this.width = 0;
|
|
|
14257 |
this.height = 0;
|
|
|
14258 |
this.imageData = utilityContext2d.createImageData(1, 1);
|
|
|
14259 |
this.format = PConstants.ARGB;
|
|
|
14260 |
}
|
|
|
14261 |
|
|
|
14262 |
this.pixels = buildPixelsObject(this);
|
|
|
14263 |
};
|
|
|
14264 |
PImage.prototype = {
|
|
|
14265 |
|
|
|
14266 |
/**
|
|
|
14267 |
* Temporary hack to deal with cross-Processing-instance created PImage. See
|
|
|
14268 |
* tickets #1623 and #1644.
|
|
|
14269 |
*/
|
|
|
14270 |
__isPImage: true,
|
|
|
14271 |
|
|
|
14272 |
/**
|
|
|
14273 |
* @member PImage
|
|
|
14274 |
* Updates the image with the data in its pixels[] array. Use in conjunction with loadPixels(). If
|
|
|
14275 |
* you're only reading pixels from the array, there's no need to call updatePixels().
|
|
|
14276 |
* Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule
|
|
|
14277 |
* is that any time you want to manipulate the pixels[] array, you must first call loadPixels(), and
|
|
|
14278 |
* after changes have been made, call updatePixels(). Even if the renderer may not seem to use this
|
|
|
14279 |
* function in the current Processing release, this will always be subject to change.
|
|
|
14280 |
* Currently, none of the renderers use the additional parameters to updatePixels().
|
|
|
14281 |
*/
|
|
|
14282 |
updatePixels: function() {
|
|
|
14283 |
var canvas = this.sourceImg;
|
|
|
14284 |
if (canvas && canvas instanceof HTMLCanvasElement && this.__isDirty) {
|
|
|
14285 |
canvas.getContext('2d').putImageData(this.imageData, 0, 0);
|
|
|
14286 |
}
|
|
|
14287 |
this.__isDirty = false;
|
|
|
14288 |
},
|
|
|
14289 |
|
|
|
14290 |
fromHTMLImageData: function(htmlImg) {
|
|
|
14291 |
// convert an <img> to a PImage
|
|
|
14292 |
var canvasData = getCanvasData(htmlImg);
|
|
|
14293 |
try {
|
|
|
14294 |
var imageData = canvasData.context.getImageData(0, 0, htmlImg.width, htmlImg.height);
|
|
|
14295 |
this.fromImageData(imageData);
|
|
|
14296 |
} catch(e) {
|
|
|
14297 |
if (htmlImg.width && htmlImg.height) {
|
|
|
14298 |
this.isRemote = true;
|
|
|
14299 |
this.width = htmlImg.width;
|
|
|
14300 |
this.height = htmlImg.height;
|
|
|
14301 |
}
|
|
|
14302 |
}
|
|
|
14303 |
this.sourceImg = htmlImg;
|
|
|
14304 |
},
|
|
|
14305 |
|
|
|
14306 |
'get': function(x, y, w, h) {
|
|
|
14307 |
if (!arguments.length) {
|
|
|
14308 |
return p.get(this);
|
|
|
14309 |
}
|
|
|
14310 |
if (arguments.length === 2) {
|
|
|
14311 |
return p.get(x, y, this);
|
|
|
14312 |
}
|
|
|
14313 |
if (arguments.length === 4) {
|
|
|
14314 |
return p.get(x, y, w, h, this);
|
|
|
14315 |
}
|
|
|
14316 |
},
|
|
|
14317 |
|
|
|
14318 |
/**
|
|
|
14319 |
* @member PImage
|
|
|
14320 |
* Changes the color of any pixel or writes an image directly into the image. The x and y parameter
|
|
|
14321 |
* specify the pixel or the upper-left corner of the image. The color parameter specifies the color value.
|
|
|
14322 |
* Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data
|
|
|
14323 |
* directly into pixels[]. The equivalent statement to "set(x, y, #000000)" using pixels[] is
|
|
|
14324 |
* "pixels[y*width+x] = #000000". Processing requires calling loadPixels() to load the display window
|
|
|
14325 |
* data into the pixels[] array before getting the values and calling updatePixels() to update the window.
|
|
|
14326 |
*
|
|
|
14327 |
* @param {int} x x-coordinate of the pixel or upper-left corner of the image
|
|
|
14328 |
* @param {int} y y-coordinate of the pixel or upper-left corner of the image
|
|
|
14329 |
* @param {color} color any value of the color datatype
|
|
|
14330 |
*
|
|
|
14331 |
* @see get
|
|
|
14332 |
* @see pixels[]
|
|
|
14333 |
* @see copy
|
|
|
14334 |
*/
|
|
|
14335 |
'set': function(x, y, c) {
|
|
|
14336 |
p.set(x, y, c, this);
|
|
|
14337 |
this.__isDirty = true;
|
|
|
14338 |
},
|
|
|
14339 |
|
|
|
14340 |
/**
|
|
|
14341 |
* @member PImage
|
|
|
14342 |
* Blends a region of pixels into the image specified by the img parameter. These copies utilize full
|
|
|
14343 |
* alpha channel support and a choice of the following modes to blend the colors of source pixels (A)
|
|
|
14344 |
* with the ones of pixels in the destination image (B):
|
|
|
14345 |
* BLEND - linear interpolation of colours: C = A*factor + B
|
|
|
14346 |
* ADD - additive blending with white clip: C = min(A*factor + B, 255)
|
|
|
14347 |
* SUBTRACT - subtractive blending with black clip: C = max(B - A*factor, 0)
|
|
|
14348 |
* DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
|
|
|
14349 |
* LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
|
|
|
14350 |
* DIFFERENCE - subtract colors from underlying image.
|
|
|
14351 |
* EXCLUSION - similar to DIFFERENCE, but less extreme.
|
|
|
14352 |
* MULTIPLY - Multiply the colors, result will always be darker.
|
|
|
14353 |
* SCREEN - Opposite multiply, uses inverse values of the colors.
|
|
|
14354 |
* OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values, and screens light values.
|
|
|
14355 |
* HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
|
|
|
14356 |
* SOFT_LIGHT - Mix of DARKEST and LIGHTEST. Works like OVERLAY, but not as harsh.
|
|
|
14357 |
* DODGE - Lightens light tones and increases contrast, ignores darks. Called "Color Dodge" in Illustrator and Photoshop.
|
|
|
14358 |
* BURN - Darker areas are applied, increasing contrast, ignores lights. Called "Color Burn" in Illustrator and Photoshop.
|
|
|
14359 |
* All modes use the alpha information (highest byte) of source image pixels as the blending factor.
|
|
|
14360 |
* If the source and destination regions are different sizes, the image will be automatically resized to
|
|
|
14361 |
* match the destination size. If the srcImg parameter is not used, the display window is used as the source image.
|
|
|
14362 |
* This function ignores imageMode().
|
|
|
14363 |
*
|
|
|
14364 |
* @param {int} x X coordinate of the source's upper left corner
|
|
|
14365 |
* @param {int} y Y coordinate of the source's upper left corner
|
|
|
14366 |
* @param {int} width source image width
|
|
|
14367 |
* @param {int} height source image height
|
|
|
14368 |
* @param {int} dx X coordinate of the destinations's upper left corner
|
|
|
14369 |
* @param {int} dy Y coordinate of the destinations's upper left corner
|
|
|
14370 |
* @param {int} dwidth destination image width
|
|
|
14371 |
* @param {int} dheight destination image height
|
|
|
14372 |
* @param {PImage} srcImg an image variable referring to the source image
|
|
|
14373 |
* @param {MODE} MODE Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION,
|
|
|
14374 |
* MULTIPLY, SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
|
|
|
14375 |
*
|
|
|
14376 |
* @see alpha
|
|
|
14377 |
* @see copy
|
|
|
14378 |
*/
|
|
|
14379 |
blend: function(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE) {
|
|
|
14380 |
if (arguments.length === 9) {
|
|
|
14381 |
p.blend(this, srcImg, x, y, width, height, dx, dy, dwidth, dheight, this);
|
|
|
14382 |
} else if (arguments.length === 10) {
|
|
|
14383 |
p.blend(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE, this);
|
|
|
14384 |
}
|
|
|
14385 |
delete this.sourceImg;
|
|
|
14386 |
},
|
|
|
14387 |
|
|
|
14388 |
/**
|
|
|
14389 |
* @member PImage
|
|
|
14390 |
* Copies a region of pixels from one image into another. If the source and destination regions
|
|
|
14391 |
* aren't the same size, it will automatically resize source pixels to fit the specified target region.
|
|
|
14392 |
* No alpha information is used in the process, however if the source image has an alpha channel set,
|
|
|
14393 |
* it will be copied as well. This function ignores imageMode().
|
|
|
14394 |
*
|
|
|
14395 |
* @param {int} sx X coordinate of the source's upper left corner
|
|
|
14396 |
* @param {int} sy Y coordinate of the source's upper left corner
|
|
|
14397 |
* @param {int} swidth source image width
|
|
|
14398 |
* @param {int} sheight source image height
|
|
|
14399 |
* @param {int} dx X coordinate of the destinations's upper left corner
|
|
|
14400 |
* @param {int} dy Y coordinate of the destinations's upper left corner
|
|
|
14401 |
* @param {int} dwidth destination image width
|
|
|
14402 |
* @param {int} dheight destination image height
|
|
|
14403 |
* @param {PImage} srcImg an image variable referring to the source image
|
|
|
14404 |
*
|
|
|
14405 |
* @see alpha
|
|
|
14406 |
* @see blend
|
|
|
14407 |
*/
|
|
|
14408 |
copy: function(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight) {
|
|
|
14409 |
if (arguments.length === 8) {
|
|
|
14410 |
p.blend(this, srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, PConstants.REPLACE, this);
|
|
|
14411 |
} else if (arguments.length === 9) {
|
|
|
14412 |
p.blend(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight, PConstants.REPLACE, this);
|
|
|
14413 |
}
|
|
|
14414 |
delete this.sourceImg;
|
|
|
14415 |
},
|
|
|
14416 |
|
|
|
14417 |
/**
|
|
|
14418 |
* @member PImage
|
|
|
14419 |
* Filters an image as defined by one of the following modes:
|
|
|
14420 |
* THRESHOLD - converts the image to black and white pixels depending if they are above or below
|
|
|
14421 |
* the threshold defined by the level parameter. The level must be between 0.0 (black) and 1.0(white).
|
|
|
14422 |
* If no level is specified, 0.5 is used.
|
|
|
14423 |
* GRAY - converts any colors in the image to grayscale equivalents
|
|
|
14424 |
* INVERT - sets each pixel to its inverse value
|
|
|
14425 |
* POSTERIZE - limits each channel of the image to the number of colors specified as the level parameter
|
|
|
14426 |
* BLUR - executes a Guassian blur with the level parameter specifying the extent of the blurring.
|
|
|
14427 |
* If no level parameter is used, the blur is equivalent to Guassian blur of radius 1.
|
|
|
14428 |
* OPAQUE - sets the alpha channel to entirely opaque.
|
|
|
14429 |
* ERODE - reduces the light areas with the amount defined by the level parameter.
|
|
|
14430 |
* DILATE - increases the light areas with the amount defined by the level parameter
|
|
|
14431 |
*
|
|
|
14432 |
* @param {MODE} MODE Either THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, or DILATE
|
|
|
14433 |
* @param {int|float} param in the range from 0 to 1
|
|
|
14434 |
*/
|
|
|
14435 |
filter: function(mode, param) {
|
|
|
14436 |
if (arguments.length === 2) {
|
|
|
14437 |
p.filter(mode, param, this);
|
|
|
14438 |
} else if (arguments.length === 1) {
|
|
|
14439 |
// no param specified, send null to show its invalid
|
|
|
14440 |
p.filter(mode, null, this);
|
|
|
14441 |
}
|
|
|
14442 |
delete this.sourceImg;
|
|
|
14443 |
},
|
|
|
14444 |
|
|
|
14445 |
/**
|
|
|
14446 |
* @member PImage
|
|
|
14447 |
* Saves the image into a file. Images are saved in TIFF, TARGA, JPEG, and PNG format depending on
|
|
|
14448 |
* the extension within the filename parameter. For example, "image.tif" will have a TIFF image and
|
|
|
14449 |
* "image.png" will save a PNG image. If no extension is included in the filename, the image will save
|
|
|
14450 |
* in TIFF format and .tif will be added to the name. These files are saved to the sketch's folder,
|
|
|
14451 |
* which may be opened by selecting "Show sketch folder" from the "Sketch" menu. It is not possible to
|
|
|
14452 |
* use save() while running the program in a web browser.
|
|
|
14453 |
* To save an image created within the code, rather than through loading, it's necessary to make the
|
|
|
14454 |
* image with the createImage() function so it is aware of the location of the program and can therefore
|
|
|
14455 |
* save the file to the right place. See the createImage() reference for more information.
|
|
|
14456 |
*
|
|
|
14457 |
* @param {String} filename a sequence of letters and numbers
|
|
|
14458 |
*/
|
|
|
14459 |
save: function(file){
|
|
|
14460 |
p.save(file,this);
|
|
|
14461 |
},
|
|
|
14462 |
|
|
|
14463 |
/**
|
|
|
14464 |
* @member PImage
|
|
|
14465 |
* Resize the image to a new width and height. To make the image scale proportionally, use 0 as the
|
|
|
14466 |
* value for the wide or high parameter.
|
|
|
14467 |
*
|
|
|
14468 |
* @param {int} wide the resized image width
|
|
|
14469 |
* @param {int} high the resized image height
|
|
|
14470 |
*
|
|
|
14471 |
* @see get
|
|
|
14472 |
*/
|
|
|
14473 |
resize: function(w, h) {
|
|
|
14474 |
if (this.isRemote) { // Remote images cannot access imageData
|
|
|
14475 |
throw "Image is loaded remotely. Cannot resize.";
|
|
|
14476 |
}
|
|
|
14477 |
if (this.width !== 0 || this.height !== 0) {
|
|
|
14478 |
// make aspect ratio if w or h is 0
|
|
|
14479 |
if (w === 0 && h !== 0) {
|
|
|
14480 |
w = Math.floor(this.width / this.height * h);
|
|
|
14481 |
} else if (h === 0 && w !== 0) {
|
|
|
14482 |
h = Math.floor(this.height / this.width * w);
|
|
|
14483 |
}
|
|
|
14484 |
// put 'this.imageData' into a new canvas
|
|
|
14485 |
var canvas = getCanvasData(this.imageData).canvas;
|
|
|
14486 |
// pull imageData object out of canvas into ImageData object
|
|
|
14487 |
var imageData = getCanvasData(canvas, w, h).context.getImageData(0, 0, w, h);
|
|
|
14488 |
// set this as new pimage
|
|
|
14489 |
this.fromImageData(imageData);
|
|
|
14490 |
}
|
|
|
14491 |
},
|
|
|
14492 |
|
|
|
14493 |
/**
|
|
|
14494 |
* @member PImage
|
|
|
14495 |
* Masks part of an image from displaying by loading another image and using it as an alpha channel.
|
|
|
14496 |
* This mask image should only contain grayscale data, but only the blue color channel is used. The
|
|
|
14497 |
* mask image needs to be the same size as the image to which it is applied.
|
|
|
14498 |
* In addition to using a mask image, an integer array containing the alpha channel data can be
|
|
|
14499 |
* specified directly. This method is useful for creating dynamically generated alpha masks. This
|
|
|
14500 |
* array must be of the same length as the target image's pixels array and should contain only grayscale
|
|
|
14501 |
* data of values between 0-255.
|
|
|
14502 |
*
|
|
|
14503 |
* @param {PImage} maskImg any PImage object used as the alpha channel for "img", needs to be same
|
|
|
14504 |
* size as "img"
|
|
|
14505 |
* @param {int[]} maskArray any array of Integer numbers used as the alpha channel, needs to be same
|
|
|
14506 |
* length as the image's pixel array
|
|
|
14507 |
*/
|
|
|
14508 |
mask: function(mask) {
|
|
|
14509 |
var obj = this.toImageData(),
|
|
|
14510 |
i,
|
|
|
14511 |
size;
|
|
|
14512 |
|
|
|
14513 |
if (mask instanceof PImage || mask.__isPImage) {
|
|
|
14514 |
if (mask.width === this.width && mask.height === this.height) {
|
|
|
14515 |
mask = mask.toImageData();
|
|
|
14516 |
|
|
|
14517 |
for (i = 2, size = this.width * this.height * 4; i < size; i += 4) {
|
|
|
14518 |
// using it as an alpha channel
|
|
|
14519 |
obj.data[i + 1] = mask.data[i];
|
|
|
14520 |
// but only the blue color channel
|
|
|
14521 |
}
|
|
|
14522 |
} else {
|
|
|
14523 |
throw "mask must have the same dimensions as PImage.";
|
|
|
14524 |
}
|
|
|
14525 |
} else if (mask instanceof Array) {
|
|
|
14526 |
if (this.width * this.height === mask.length) {
|
|
|
14527 |
for (i = 0, size = mask.length; i < size; ++i) {
|
|
|
14528 |
obj.data[i * 4 + 3] = mask[i];
|
|
|
14529 |
}
|
|
|
14530 |
} else {
|
|
|
14531 |
throw "mask array must be the same length as PImage pixels array.";
|
|
|
14532 |
}
|
|
|
14533 |
}
|
|
|
14534 |
|
|
|
14535 |
this.fromImageData(obj);
|
|
|
14536 |
},
|
|
|
14537 |
|
|
|
14538 |
// These are intentionally left blank for PImages, we work live with pixels and draw as necessary
|
|
|
14539 |
/**
|
|
|
14540 |
* @member PImage
|
|
|
14541 |
* Loads the pixel data for the image into its pixels[] array. This function must always be called
|
|
|
14542 |
* before reading from or writing to pixels[].
|
|
|
14543 |
* Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the
|
|
|
14544 |
* rule is that any time you want to manipulate the pixels[] array, you must first call loadPixels(),
|
|
|
14545 |
* and after changes have been made, call updatePixels(). Even if the renderer may not seem to use
|
|
|
14546 |
* this function in the current Processing release, this will always be subject to change.
|
|
|
14547 |
*/
|
|
|
14548 |
loadPixels: nop,
|
|
|
14549 |
|
|
|
14550 |
toImageData: function() {
|
|
|
14551 |
if (this.isRemote) {
|
|
|
14552 |
return this.sourceImg;
|
|
|
14553 |
}
|
|
|
14554 |
|
|
|
14555 |
if (!this.__isDirty) {
|
|
|
14556 |
return this.imageData;
|
|
|
14557 |
}
|
|
|
14558 |
|
|
|
14559 |
var canvasData = getCanvasData(this.imageData);
|
|
|
14560 |
return canvasData.context.getImageData(0, 0, this.width, this.height);
|
|
|
14561 |
},
|
|
|
14562 |
|
|
|
14563 |
toDataURL: function() {
|
|
|
14564 |
if (this.isRemote) { // Remote images cannot access imageData
|
|
|
14565 |
throw "Image is loaded remotely. Cannot create dataURI.";
|
|
|
14566 |
}
|
|
|
14567 |
var canvasData = getCanvasData(this.imageData);
|
|
|
14568 |
return canvasData.canvas.toDataURL();
|
|
|
14569 |
},
|
|
|
14570 |
|
|
|
14571 |
fromImageData: function(canvasImg) {
|
|
|
14572 |
var w = canvasImg.width,
|
|
|
14573 |
h = canvasImg.height,
|
|
|
14574 |
canvas = document.createElement('canvas'),
|
|
|
14575 |
ctx = canvas.getContext('2d');
|
|
|
14576 |
|
|
|
14577 |
this.width = canvas.width = w;
|
|
|
14578 |
this.height = canvas.height = h;
|
|
|
14579 |
|
|
|
14580 |
ctx.putImageData(canvasImg, 0, 0);
|
|
|
14581 |
|
|
|
14582 |
// changed for 0.9
|
|
|
14583 |
this.format = PConstants.ARGB;
|
|
|
14584 |
|
|
|
14585 |
this.imageData = canvasImg;
|
|
|
14586 |
this.sourceImg = canvas;
|
|
|
14587 |
}
|
|
|
14588 |
};
|
|
|
14589 |
|
|
|
14590 |
p.PImage = PImage;
|
|
|
14591 |
|
|
|
14592 |
/**
|
|
|
14593 |
* Creates a new PImage (the datatype for storing images). This provides a fresh buffer of pixels to play
|
|
|
14594 |
* with. Set the size of the buffer with the width and height parameters. The format parameter defines how
|
|
|
14595 |
* the pixels are stored. See the PImage reference for more information.
|
|
|
14596 |
* Be sure to include all three parameters, specifying only the width and height (but no format) will
|
|
|
14597 |
* produce a strange error.
|
|
|
14598 |
* Advanced users please note that createImage() should be used instead of the syntax new PImage().
|
|
|
14599 |
*
|
|
|
14600 |
* @param {int} width image width
|
|
|
14601 |
* @param {int} height image height
|
|
|
14602 |
* @param {MODE} format Either RGB, ARGB, ALPHA (grayscale alpha channel)
|
|
|
14603 |
*
|
|
|
14604 |
* @returns {PImage}
|
|
|
14605 |
*
|
|
|
14606 |
* @see PImage
|
|
|
14607 |
* @see PGraphics
|
|
|
14608 |
*/
|
|
|
14609 |
p.createImage = function(w, h, mode) {
|
|
|
14610 |
return new PImage(w,h,mode);
|
|
|
14611 |
};
|
|
|
14612 |
|
|
|
14613 |
// Loads an image for display. Type is an extension. Callback is fired on load.
|
|
|
14614 |
/**
|
|
|
14615 |
* Loads an image into a variable of type PImage. Four types of images ( .gif, .jpg, .tga, .png) images may
|
|
|
14616 |
* be loaded. To load correctly, images must be located in the data directory of the current sketch. In most
|
|
|
14617 |
* cases, load all images in setup() to preload them at the start of the program. Loading images inside draw()
|
|
|
14618 |
* will reduce the speed of a program.
|
|
|
14619 |
* The filename parameter can also be a URL to a file found online. For security reasons, a Processing sketch
|
|
|
14620 |
* found online can only download files from the same server from which it came. Getting around this restriction
|
|
|
14621 |
* requires a signed applet.
|
|
|
14622 |
* The extension parameter is used to determine the image type in cases where the image filename does not end
|
|
|
14623 |
* with a proper extension. Specify the extension as the second parameter to loadImage(), as shown in the
|
|
|
14624 |
* third example on this page.
|
|
|
14625 |
* If an image is not loaded successfully, the null value is returned and an error message will be printed to
|
|
|
14626 |
* the console. The error message does not halt the program, however the null value may cause a NullPointerException
|
|
|
14627 |
* if your code does not check whether the value returned from loadImage() is null.
|
|
|
14628 |
* Depending on the type of error, a PImage object may still be returned, but the width and height of the image
|
|
|
14629 |
* will be set to -1. This happens if bad image data is returned or cannot be decoded properly. Sometimes this happens
|
|
|
14630 |
* with image URLs that produce a 403 error or that redirect to a password prompt, because loadImage() will attempt
|
|
|
14631 |
* to interpret the HTML as image data.
|
|
|
14632 |
*
|
|
|
14633 |
* @param {String} filename name of file to load, can be .gif, .jpg, .tga, or a handful of other image
|
|
|
14634 |
* types depending on your platform.
|
|
|
14635 |
* @param {String} extension the type of image to load, for example "png", "gif", "jpg"
|
|
|
14636 |
*
|
|
|
14637 |
* @returns {PImage}
|
|
|
14638 |
*
|
|
|
14639 |
* @see PImage
|
|
|
14640 |
* @see image
|
|
|
14641 |
* @see imageMode
|
|
|
14642 |
* @see background
|
|
|
14643 |
*/
|
|
|
14644 |
p.loadImage = function(file, type, callback) {
|
|
|
14645 |
// if type is specified add it with a . to file to make the filename
|
|
|
14646 |
if (type) {
|
|
|
14647 |
file = file + "." + type;
|
|
|
14648 |
}
|
|
|
14649 |
var pimg;
|
|
|
14650 |
// if image is in the preloader cache return a new PImage
|
|
|
14651 |
if (curSketch.imageCache.images[file]) {
|
|
|
14652 |
pimg = new PImage(curSketch.imageCache.images[file]);
|
|
|
14653 |
pimg.loaded = true;
|
|
|
14654 |
return pimg;
|
|
|
14655 |
}
|
|
|
14656 |
// else async load it
|
|
|
14657 |
pimg = new PImage();
|
|
|
14658 |
var img = document.createElement('img');
|
|
|
14659 |
|
|
|
14660 |
pimg.sourceImg = img;
|
|
|
14661 |
|
|
|
14662 |
img.onload = (function(aImage, aPImage, aCallback) {
|
|
|
14663 |
var image = aImage;
|
|
|
14664 |
var pimg = aPImage;
|
|
|
14665 |
var callback = aCallback;
|
|
|
14666 |
return function() {
|
|
|
14667 |
// change the <img> object into a PImage now that its loaded
|
|
|
14668 |
pimg.fromHTMLImageData(image);
|
|
|
14669 |
pimg.loaded = true;
|
|
|
14670 |
if (callback) {
|
|
|
14671 |
callback();
|
|
|
14672 |
}
|
|
|
14673 |
};
|
|
|
14674 |
}(img, pimg, callback));
|
|
|
14675 |
|
|
|
14676 |
img.src = file; // needs to be called after the img.onload function is declared or it wont work in opera
|
|
|
14677 |
return pimg;
|
|
|
14678 |
};
|
|
|
14679 |
|
|
|
14680 |
// async loading of large images, same functionality as loadImage above
|
|
|
14681 |
/**
|
|
|
14682 |
* This function load images on a separate thread so that your sketch does not freeze while images load during
|
|
|
14683 |
* setup(). While the image is loading, its width and height will be 0. If an error occurs while loading the image,
|
|
|
14684 |
* its width and height will be set to -1. You'll know when the image has loaded properly because its width and
|
|
|
14685 |
* height will be greater than 0. Asynchronous image loading (particularly when downloading from a server) can
|
|
|
14686 |
* dramatically improve performance.
|
|
|
14687 |
* The extension parameter is used to determine the image type in cases where the image filename does not end
|
|
|
14688 |
* with a proper extension. Specify the extension as the second parameter to requestImage().
|
|
|
14689 |
*
|
|
|
14690 |
* @param {String} filename name of file to load, can be .gif, .jpg, .tga, or a handful of other image
|
|
|
14691 |
* types depending on your platform.
|
|
|
14692 |
* @param {String} extension the type of image to load, for example "png", "gif", "jpg"
|
|
|
14693 |
*
|
|
|
14694 |
* @returns {PImage}
|
|
|
14695 |
*
|
|
|
14696 |
* @see PImage
|
|
|
14697 |
* @see loadImage
|
|
|
14698 |
*/
|
|
|
14699 |
p.requestImage = p.loadImage;
|
|
|
14700 |
|
|
|
14701 |
function get$2(x,y) {
|
|
|
14702 |
var data;
|
|
|
14703 |
// return the color at x,y (int) of curContext
|
|
|
14704 |
if (x >= p.width || x < 0 || y < 0 || y >= p.height) {
|
|
|
14705 |
// x,y is outside image return transparent black
|
|
|
14706 |
return 0;
|
|
|
14707 |
}
|
|
|
14708 |
|
|
|
14709 |
// loadPixels() has been called
|
|
|
14710 |
if (isContextReplaced) {
|
|
|
14711 |
var offset = ((0|x) + p.width * (0|y)) * 4;
|
|
|
14712 |
data = p.imageData.data;
|
|
|
14713 |
return (data[offset + 3] << 24) & PConstants.ALPHA_MASK |
|
|
|
14714 |
(data[offset] << 16) & PConstants.RED_MASK |
|
|
|
14715 |
(data[offset + 1] << 8) & PConstants.GREEN_MASK |
|
|
|
14716 |
data[offset + 2] & PConstants.BLUE_MASK;
|
|
|
14717 |
}
|
|
|
14718 |
|
|
|
14719 |
// x,y is inside canvas space
|
|
|
14720 |
data = p.toImageData(0|x, 0|y, 1, 1).data;
|
|
|
14721 |
return (data[3] << 24) & PConstants.ALPHA_MASK |
|
|
|
14722 |
(data[0] << 16) & PConstants.RED_MASK |
|
|
|
14723 |
(data[1] << 8) & PConstants.GREEN_MASK |
|
|
|
14724 |
data[2] & PConstants.BLUE_MASK;
|
|
|
14725 |
}
|
|
|
14726 |
function get$3(x,y,img) {
|
|
|
14727 |
if (img.isRemote) { // Remote images cannot access imageData
|
|
|
14728 |
throw "Image is loaded remotely. Cannot get x,y.";
|
|
|
14729 |
}
|
|
|
14730 |
// PImage.get(x,y) was called, return the color (int) at x,y of img
|
|
|
14731 |
var offset = y * img.width * 4 + (x * 4),
|
|
|
14732 |
data = img.imageData.data;
|
|
|
14733 |
return (data[offset + 3] << 24) & PConstants.ALPHA_MASK |
|
|
|
14734 |
(data[offset] << 16) & PConstants.RED_MASK |
|
|
|
14735 |
(data[offset + 1] << 8) & PConstants.GREEN_MASK |
|
|
|
14736 |
data[offset + 2] & PConstants.BLUE_MASK;
|
|
|
14737 |
}
|
|
|
14738 |
function get$4(x, y, w, h) {
|
|
|
14739 |
// return a PImage of w and h from cood x,y of curContext
|
|
|
14740 |
var c = new PImage(w, h, PConstants.ARGB);
|
|
|
14741 |
c.fromImageData(p.toImageData(x, y, w, h));
|
|
|
14742 |
return c;
|
|
|
14743 |
}
|
|
|
14744 |
function get$5(x, y, w, h, img) {
|
|
|
14745 |
if (img.isRemote) { // Remote images cannot access imageData
|
|
|
14746 |
throw "Image is loaded remotely. Cannot get x,y,w,h.";
|
|
|
14747 |
}
|
|
|
14748 |
// PImage.get(x,y,w,h) was called, return x,y,w,h PImage of img
|
|
|
14749 |
// offset start point needs to be *4
|
|
|
14750 |
var c = new PImage(w, h, PConstants.ARGB), cData = c.imageData.data,
|
|
|
14751 |
imgWidth = img.width, imgHeight = img.height, imgData = img.imageData.data;
|
|
|
14752 |
// Don't need to copy pixels from the image outside ranges.
|
|
|
14753 |
var startRow = Math.max(0, -y), startColumn = Math.max(0, -x),
|
|
|
14754 |
stopRow = Math.min(h, imgHeight - y), stopColumn = Math.min(w, imgWidth - x);
|
|
|
14755 |
for (var i = startRow; i < stopRow; ++i) {
|
|
|
14756 |
var sourceOffset = ((y + i) * imgWidth + (x + startColumn)) * 4;
|
|
|
14757 |
var targetOffset = (i * w + startColumn) * 4;
|
|
|
14758 |
for (var j = startColumn; j < stopColumn; ++j) {
|
|
|
14759 |
cData[targetOffset++] = imgData[sourceOffset++];
|
|
|
14760 |
cData[targetOffset++] = imgData[sourceOffset++];
|
|
|
14761 |
cData[targetOffset++] = imgData[sourceOffset++];
|
|
|
14762 |
cData[targetOffset++] = imgData[sourceOffset++];
|
|
|
14763 |
}
|
|
|
14764 |
}
|
|
|
14765 |
c.__isDirty = true;
|
|
|
14766 |
return c;
|
|
|
14767 |
}
|
|
|
14768 |
|
|
|
14769 |
// Gets a single pixel or block of pixels from the current Canvas Context or a PImage
|
|
|
14770 |
/**
|
|
|
14771 |
* Reads the color of any pixel or grabs a section of an image. If no parameters are specified, the entire
|
|
|
14772 |
* image is returned. Get the value of one pixel by specifying an x,y coordinate. Get a section of the display
|
|
|
14773 |
* window by specifying an additional width and height parameter. If the pixel requested is outside of the image
|
|
|
14774 |
* window, black is returned. The numbers returned are scaled according to the current color ranges, but only RGB
|
|
|
14775 |
* values are returned by this function. For example, even though you may have drawn a shape with colorMode(HSB),
|
|
|
14776 |
* the numbers returned will be in RGB.
|
|
|
14777 |
* Getting the color of a single pixel with get(x, y) is easy, but not as fast as grabbing the data directly
|
|
|
14778 |
* from pixels[]. The equivalent statement to "get(x, y)" using pixels[] is "pixels[y*width+x]". Processing
|
|
|
14779 |
* requires calling loadPixels() to load the display window data into the pixels[] array before getting the values.
|
|
|
14780 |
* This function ignores imageMode().
|
|
|
14781 |
*
|
|
|
14782 |
* @param {int} x x-coordinate of the pixel
|
|
|
14783 |
* @param {int} y y-coordinate of the pixel
|
|
|
14784 |
* @param {int} width width of pixel rectangle to get
|
|
|
14785 |
* @param {int} height height of pixel rectangle to get
|
|
|
14786 |
*
|
|
|
14787 |
* @returns {Color|PImage}
|
|
|
14788 |
*
|
|
|
14789 |
* @see set
|
|
|
14790 |
* @see pixels[]
|
|
|
14791 |
* @see imageMode
|
|
|
14792 |
*/
|
|
|
14793 |
p.get = function(x, y, w, h, img) {
|
|
|
14794 |
// for 0 2 and 4 arguments use curContext, otherwise PImage.get was called
|
|
|
14795 |
if (img !== undefined) {
|
|
|
14796 |
return get$5(x, y, w, h, img);
|
|
|
14797 |
}
|
|
|
14798 |
if (h !== undefined) {
|
|
|
14799 |
return get$4(x, y, w, h);
|
|
|
14800 |
}
|
|
|
14801 |
if (w !== undefined) {
|
|
|
14802 |
return get$3(x, y, w);
|
|
|
14803 |
}
|
|
|
14804 |
if (y !== undefined) {
|
|
|
14805 |
return get$2(x, y);
|
|
|
14806 |
}
|
|
|
14807 |
if (x !== undefined) {
|
|
|
14808 |
// PImage.get() was called, return a new PImage
|
|
|
14809 |
return get$5(0, 0, x.width, x.height, x);
|
|
|
14810 |
}
|
|
|
14811 |
|
|
|
14812 |
return get$4(0, 0, p.width, p.height);
|
|
|
14813 |
};
|
|
|
14814 |
|
|
|
14815 |
/**
|
|
|
14816 |
* Creates and returns a new <b>PGraphics</b> object of the types P2D, P3D, and JAVA2D. Use this class if you need to draw
|
|
|
14817 |
* into an off-screen graphics buffer. It's not possible to use <b>createGraphics()</b> with OPENGL, because it doesn't
|
|
|
14818 |
* allow offscreen use. The DXF and PDF renderers require the filename parameter. <br /><br /> It's important to call
|
|
|
14819 |
* any drawing commands between beginDraw() and endDraw() statements. This is also true for any commands that affect
|
|
|
14820 |
* drawing, such as smooth() or colorMode().<br /><br /> Unlike the main drawing surface which is completely opaque,
|
|
|
14821 |
* surfaces created with createGraphics() can have transparency. This makes it possible to draw into a graphics and
|
|
|
14822 |
* maintain the alpha channel.
|
|
|
14823 |
*
|
|
|
14824 |
* @param {int} width width in pixels
|
|
|
14825 |
* @param {int} height height in pixels
|
|
|
14826 |
* @param {int} renderer Either P2D, P3D, JAVA2D, PDF, DXF
|
|
|
14827 |
* @param {String} filename the name of the file (not supported yet)
|
|
|
14828 |
*/
|
|
|
14829 |
p.createGraphics = function(w, h, render) {
|
|
|
14830 |
var pg = new Processing();
|
|
|
14831 |
pg.size(w, h, render);
|
|
|
14832 |
return pg;
|
|
|
14833 |
};
|
|
|
14834 |
|
|
|
14835 |
// pixels caching
|
|
|
14836 |
function resetContext() {
|
|
|
14837 |
if(isContextReplaced) {
|
|
|
14838 |
curContext = originalContext;
|
|
|
14839 |
isContextReplaced = false;
|
|
|
14840 |
|
|
|
14841 |
p.updatePixels();
|
|
|
14842 |
}
|
|
|
14843 |
}
|
|
|
14844 |
function SetPixelContextWrapper() {
|
|
|
14845 |
function wrapFunction(newContext, name) {
|
|
|
14846 |
function wrapper() {
|
|
|
14847 |
resetContext();
|
|
|
14848 |
curContext[name].apply(curContext, arguments);
|
|
|
14849 |
}
|
|
|
14850 |
newContext[name] = wrapper;
|
|
|
14851 |
}
|
|
|
14852 |
function wrapProperty(newContext, name) {
|
|
|
14853 |
function getter() {
|
|
|
14854 |
resetContext();
|
|
|
14855 |
return curContext[name];
|
|
|
14856 |
}
|
|
|
14857 |
function setter(value) {
|
|
|
14858 |
resetContext();
|
|
|
14859 |
curContext[name] = value;
|
|
|
14860 |
}
|
|
|
14861 |
p.defineProperty(newContext, name, { get: getter, set: setter });
|
|
|
14862 |
}
|
|
|
14863 |
for(var n in curContext) {
|
|
|
14864 |
if(typeof curContext[n] === 'function') {
|
|
|
14865 |
wrapFunction(this, n);
|
|
|
14866 |
} else {
|
|
|
14867 |
wrapProperty(this, n);
|
|
|
14868 |
}
|
|
|
14869 |
}
|
|
|
14870 |
}
|
|
|
14871 |
function replaceContext() {
|
|
|
14872 |
if(isContextReplaced) {
|
|
|
14873 |
return;
|
|
|
14874 |
}
|
|
|
14875 |
p.loadPixels();
|
|
|
14876 |
if(proxyContext === null) {
|
|
|
14877 |
originalContext = curContext;
|
|
|
14878 |
proxyContext = new SetPixelContextWrapper();
|
|
|
14879 |
}
|
|
|
14880 |
isContextReplaced = true;
|
|
|
14881 |
curContext = proxyContext;
|
|
|
14882 |
setPixelsCached = 0;
|
|
|
14883 |
}
|
|
|
14884 |
|
|
|
14885 |
function set$3(x, y, c) {
|
|
|
14886 |
if (x < p.width && x >= 0 && y >= 0 && y < p.height) {
|
|
|
14887 |
replaceContext();
|
|
|
14888 |
p.pixels.setPixel((0|x)+p.width*(0|y), c);
|
|
|
14889 |
if(++setPixelsCached > maxPixelsCached) {
|
|
|
14890 |
resetContext();
|
|
|
14891 |
}
|
|
|
14892 |
}
|
|
|
14893 |
}
|
|
|
14894 |
function set$4(x, y, obj, img) {
|
|
|
14895 |
if (img.isRemote) { // Remote images cannot access imageData
|
|
|
14896 |
throw "Image is loaded remotely. Cannot set x,y.";
|
|
|
14897 |
}
|
|
|
14898 |
var c = p.color.toArray(obj);
|
|
|
14899 |
var offset = y * img.width * 4 + (x*4);
|
|
|
14900 |
var data = img.imageData.data;
|
|
|
14901 |
data[offset] = c[0];
|
|
|
14902 |
data[offset+1] = c[1];
|
|
|
14903 |
data[offset+2] = c[2];
|
|
|
14904 |
data[offset+3] = c[3];
|
|
|
14905 |
}
|
|
|
14906 |
|
|
|
14907 |
// Paints a pixel array into the canvas
|
|
|
14908 |
/**
|
|
|
14909 |
* Changes the color of any pixel or writes an image directly into the display window. The x and y parameters
|
|
|
14910 |
* specify the pixel to change and the color parameter specifies the color value. The color parameter is affected
|
|
|
14911 |
* by the current color mode (the default is RGB values from 0 to 255). When setting an image, the x and y
|
|
|
14912 |
* parameters define the coordinates for the upper-left corner of the image.
|
|
|
14913 |
* Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data directly
|
|
|
14914 |
* into pixels[]. The equivalent statement to "set(x, y, #000000)" using pixels[] is "pixels[y*width+x] = #000000".
|
|
|
14915 |
* You must call loadPixels() to load the display window data into the pixels[] array before setting the values
|
|
|
14916 |
* and calling updatePixels() to update the window with any changes. This function ignores imageMode().
|
|
|
14917 |
*
|
|
|
14918 |
* @param {int} x x-coordinate of the pixel
|
|
|
14919 |
* @param {int} y y-coordinate of the pixel
|
|
|
14920 |
* @param {Color} obj any value of the color datatype
|
|
|
14921 |
* @param {PImage} img any valid variable of type PImage
|
|
|
14922 |
*
|
|
|
14923 |
* @see get
|
|
|
14924 |
* @see pixels[]
|
|
|
14925 |
* @see imageMode
|
|
|
14926 |
*/
|
|
|
14927 |
p.set = function(x, y, obj, img) {
|
|
|
14928 |
var color, oldFill;
|
|
|
14929 |
if (arguments.length === 3) {
|
|
|
14930 |
// called p.set(), was it with a color or a img ?
|
|
|
14931 |
if (typeof obj === "number") {
|
|
|
14932 |
set$3(x, y, obj);
|
|
|
14933 |
} else if (obj instanceof PImage || obj.__isPImage) {
|
|
|
14934 |
p.image(obj, x, y);
|
|
|
14935 |
}
|
|
|
14936 |
} else if (arguments.length === 4) {
|
|
|
14937 |
// PImage.set(x,y,c) was called, set coordinate x,y color to c of img
|
|
|
14938 |
set$4(x, y, obj, img);
|
|
|
14939 |
}
|
|
|
14940 |
};
|
|
|
14941 |
p.imageData = {};
|
|
|
14942 |
|
|
|
14943 |
// handle the sketch code for pixels[]
|
|
|
14944 |
// parser code converts pixels[] to getPixels() or setPixels(),
|
|
|
14945 |
// .length becomes getLength()
|
|
|
14946 |
/**
|
|
|
14947 |
* Array containing the values for all the pixels in the display window. These values are of the color datatype.
|
|
|
14948 |
* This array is the size of the display window. For example, if the image is 100x100 pixels, there will be 10000
|
|
|
14949 |
* values and if the window is 200x300 pixels, there will be 60000 values. The index value defines the position
|
|
|
14950 |
* of a value within the array. For example, the statment color b = pixels[230] will set the variable b to be
|
|
|
14951 |
* equal to the value at that location in the array.
|
|
|
14952 |
* Before accessing this array, the data must loaded with the loadPixels() function. After the array data has
|
|
|
14953 |
* been modified, the updatePixels() function must be run to update the changes.
|
|
|
14954 |
*
|
|
|
14955 |
* @param {int} index must not exceed the size of the array
|
|
|
14956 |
*
|
|
|
14957 |
* @see loadPixels
|
|
|
14958 |
* @see updatePixels
|
|
|
14959 |
* @see get
|
|
|
14960 |
* @see set
|
|
|
14961 |
* @see PImage
|
|
|
14962 |
*/
|
|
|
14963 |
p.pixels = {
|
|
|
14964 |
getLength: function() { return p.imageData.data.length ? p.imageData.data.length/4 : 0; },
|
|
|
14965 |
getPixel: function(i) {
|
|
|
14966 |
var offset = i*4, data = p.imageData.data;
|
|
|
14967 |
return (data[offset+3] << 24) & 0xff000000 |
|
|
|
14968 |
(data[offset+0] << 16) & 0x00ff0000 |
|
|
|
14969 |
(data[offset+1] << 8) & 0x0000ff00 |
|
|
|
14970 |
data[offset+2] & 0x000000ff;
|
|
|
14971 |
},
|
|
|
14972 |
setPixel: function(i,c) {
|
|
|
14973 |
var offset = i*4, data = p.imageData.data;
|
|
|
14974 |
data[offset+0] = (c & 0x00ff0000) >>> 16; // RED_MASK
|
|
|
14975 |
data[offset+1] = (c & 0x0000ff00) >>> 8; // GREEN_MASK
|
|
|
14976 |
data[offset+2] = (c & 0x000000ff); // BLUE_MASK
|
|
|
14977 |
data[offset+3] = (c & 0xff000000) >>> 24; // ALPHA_MASK
|
|
|
14978 |
},
|
|
|
14979 |
toArray: function() {
|
|
|
14980 |
var arr = [], length = p.imageData.width * p.imageData.height, data = p.imageData.data;
|
|
|
14981 |
for (var i = 0, offset = 0; i < length; i++, offset += 4) {
|
|
|
14982 |
arr.push((data[offset+3] << 24) & 0xff000000 |
|
|
|
14983 |
(data[offset+0] << 16) & 0x00ff0000 |
|
|
|
14984 |
(data[offset+1] << 8) & 0x0000ff00 |
|
|
|
14985 |
data[offset+2] & 0x000000ff);
|
|
|
14986 |
}
|
|
|
14987 |
return arr;
|
|
|
14988 |
},
|
|
|
14989 |
set: function(arr) {
|
|
|
14990 |
for (var i = 0, aL = arr.length; i < aL; i++) {
|
|
|
14991 |
this.setPixel(i, arr[i]);
|
|
|
14992 |
}
|
|
|
14993 |
}
|
|
|
14994 |
};
|
|
|
14995 |
|
|
|
14996 |
// Gets a 1-Dimensional pixel array from Canvas
|
|
|
14997 |
/**
|
|
|
14998 |
* Loads the pixel data for the display window into the pixels[] array. This function must always be called
|
|
|
14999 |
* before reading from or writing to pixels[].
|
|
|
15000 |
* Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule is that
|
|
|
15001 |
* any time you want to manipulate the pixels[] array, you must first call loadPixels(), and after changes
|
|
|
15002 |
* have been made, call updatePixels(). Even if the renderer may not seem to use this function in the current
|
|
|
15003 |
* Processing release, this will always be subject to change.
|
|
|
15004 |
*
|
|
|
15005 |
* @see pixels[]
|
|
|
15006 |
* @see updatePixels
|
|
|
15007 |
*/
|
|
|
15008 |
p.loadPixels = function() {
|
|
|
15009 |
p.imageData = drawing.$ensureContext().getImageData(0, 0, p.width, p.height);
|
|
|
15010 |
};
|
|
|
15011 |
|
|
|
15012 |
// Draws a 1-Dimensional pixel array to Canvas
|
|
|
15013 |
/**
|
|
|
15014 |
* Updates the display window with the data in the pixels[] array. Use in conjunction with loadPixels(). If
|
|
|
15015 |
* you're only reading pixels from the array, there's no need to call updatePixels() unless there are changes.
|
|
|
15016 |
* Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule is that
|
|
|
15017 |
* any time you want to manipulate the pixels[] array, you must first call loadPixels(), and after changes
|
|
|
15018 |
* have been made, call updatePixels(). Even if the renderer may not seem to use this function in the current
|
|
|
15019 |
* Processing release, this will always be subject to change.
|
|
|
15020 |
* Currently, none of the renderers use the additional parameters to updatePixels(), however this may be
|
|
|
15021 |
* implemented in the future.
|
|
|
15022 |
*
|
|
|
15023 |
* @see loadPixels
|
|
|
15024 |
* @see pixels[]
|
|
|
15025 |
*/
|
|
|
15026 |
p.updatePixels = function() {
|
|
|
15027 |
if (p.imageData) {
|
|
|
15028 |
drawing.$ensureContext().putImageData(p.imageData, 0, 0);
|
|
|
15029 |
}
|
|
|
15030 |
};
|
|
|
15031 |
|
|
|
15032 |
/**
|
|
|
15033 |
* Set various hints and hacks for the renderer. This is used to handle obscure rendering features that cannot be
|
|
|
15034 |
* implemented in a consistent manner across renderers. Many options will often graduate to standard features
|
|
|
15035 |
* instead of hints over time.
|
|
|
15036 |
* hint(ENABLE_OPENGL_4X_SMOOTH) - Enable 4x anti-aliasing for OpenGL. This can help force anti-aliasing if
|
|
|
15037 |
* it has not been enabled by the user. On some graphics cards, this can also be set by the graphics driver's
|
|
|
15038 |
* control panel, however not all cards make this available. This hint must be called immediately after the
|
|
|
15039 |
* size() command because it resets the renderer, obliterating any settings and anything drawn (and like size(),
|
|
|
15040 |
* re-running the code that came before it again).
|
|
|
15041 |
* hint(DISABLE_OPENGL_2X_SMOOTH) - In Processing 1.0, Processing always enables 2x smoothing when the OpenGL
|
|
|
15042 |
* renderer is used. This hint disables the default 2x smoothing and returns the smoothing behavior found in
|
|
|
15043 |
* earlier releases, where smooth() and noSmooth() could be used to enable and disable smoothing, though the
|
|
|
15044 |
* quality was inferior.
|
|
|
15045 |
* hint(ENABLE_NATIVE_FONTS) - Use the native version fonts when they are installed, rather than the bitmapped
|
|
|
15046 |
* version from a .vlw file. This is useful with the JAVA2D renderer setting, as it will improve font rendering
|
|
|
15047 |
* speed. This is not enabled by default, because it can be misleading while testing because the type will look
|
|
|
15048 |
* great on your machine (because you have the font installed) but lousy on others' machines if the identical
|
|
|
15049 |
* font is unavailable. This option can only be set per-sketch, and must be called before any use of textFont().
|
|
|
15050 |
* hint(DISABLE_DEPTH_TEST) - Disable the zbuffer, allowing you to draw on top of everything at will. When depth
|
|
|
15051 |
* testing is disabled, items will be drawn to the screen sequentially, like a painting. This hint is most often
|
|
|
15052 |
* 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
|
|
|
15053 |
* interface). Starting in release 0149, this will also clear the depth buffer. Restore the default with
|
|
|
15054 |
* hint(ENABLE_DEPTH_TEST), but note that with the depth buffer cleared, any 3D drawing that happens later in
|
|
|
15055 |
* draw() will ignore existing shapes on the screen.
|
|
|
15056 |
* hint(ENABLE_DEPTH_SORT) - Enable primitive z-sorting of triangles and lines in P3D and OPENGL. This can slow
|
|
|
15057 |
* performance considerably, and the algorithm is not yet perfect. Restore the default with hint(DISABLE_DEPTH_SORT).
|
|
|
15058 |
* hint(DISABLE_OPENGL_ERROR_REPORT) - Speeds up the OPENGL renderer setting by not checking for errors while
|
|
|
15059 |
* running. Undo with hint(ENABLE_OPENGL_ERROR_REPORT).
|
|
|
15060 |
* As of release 0149, unhint() has been removed in favor of adding additional ENABLE/DISABLE constants to reset
|
|
|
15061 |
* the default behavior. This prevents the double negatives, and also reinforces which hints can be enabled or disabled.
|
|
|
15062 |
*
|
|
|
15063 |
* @param {MODE} item constant: name of the hint to be enabled or disabled
|
|
|
15064 |
*
|
|
|
15065 |
* @see PGraphics
|
|
|
15066 |
* @see createGraphics
|
|
|
15067 |
* @see size
|
|
|
15068 |
*/
|
|
|
15069 |
p.hint = function(which) {
|
|
|
15070 |
var curContext = drawing.$ensureContext();
|
|
|
15071 |
if (which === PConstants.DISABLE_DEPTH_TEST) {
|
|
|
15072 |
curContext.disable(curContext.DEPTH_TEST);
|
|
|
15073 |
curContext.depthMask(false);
|
|
|
15074 |
curContext.clear(curContext.DEPTH_BUFFER_BIT);
|
|
|
15075 |
}
|
|
|
15076 |
else if (which === PConstants.ENABLE_DEPTH_TEST) {
|
|
|
15077 |
curContext.enable(curContext.DEPTH_TEST);
|
|
|
15078 |
curContext.depthMask(true);
|
|
|
15079 |
}
|
|
|
15080 |
};
|
|
|
15081 |
|
|
|
15082 |
/**
|
|
|
15083 |
* The background() function sets the color used for the background of the Processing window.
|
|
|
15084 |
* 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.
|
|
|
15085 |
* 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.
|
|
|
15086 |
* To resize an image 'b' to the size of the sketch window, use b.resize(width, height).
|
|
|
15087 |
* Images used as background will ignore the current <b>tint()</b> setting.
|
|
|
15088 |
* For the main drawing surface, the alpha value will be ignored. However,
|
|
|
15089 |
* alpha can be used on PGraphics objects from <b>createGraphics()</b>. This is
|
|
|
15090 |
* the only way to set all the pixels partially transparent, for instance.
|
|
|
15091 |
* If the 'gray' parameter is passed in the function sets the background to a grayscale value, based on the
|
|
|
15092 |
* current colorMode.
|
|
|
15093 |
* <p>
|
|
|
15094 |
* Note that background() should be called before any transformations occur,
|
|
|
15095 |
* because some implementations may require the current transformation matrix
|
|
|
15096 |
* to be identity before drawing.
|
|
|
15097 |
*
|
|
|
15098 |
* @param {int|float} gray specifies a value between white and black
|
|
|
15099 |
* @param {int|float} value1 red or hue value (depending on the current color mode)
|
|
|
15100 |
* @param {int|float} value2 green or saturation value (depending on the current color mode)
|
|
|
15101 |
* @param {int|float} value3 blue or brightness value (depending on the current color mode)
|
|
|
15102 |
* @param {int|float} alpha opacity of the background
|
|
|
15103 |
* @param {Color} color any value of the color datatype
|
|
|
15104 |
* @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
|
|
|
15105 |
* @param {PImage} image an instance of a PImage to use as a background
|
|
|
15106 |
*
|
|
|
15107 |
* @see #stroke()
|
|
|
15108 |
* @see #fill()
|
|
|
15109 |
* @see #tint()
|
|
|
15110 |
* @see #colorMode()
|
|
|
15111 |
*/
|
|
|
15112 |
var backgroundHelper = function(arg1, arg2, arg3, arg4) {
|
|
|
15113 |
var obj;
|
|
|
15114 |
|
|
|
15115 |
if (arg1 instanceof PImage || arg1.__isPImage) {
|
|
|
15116 |
obj = arg1;
|
|
|
15117 |
|
|
|
15118 |
if (!obj.loaded) {
|
|
|
15119 |
throw "Error using image in background(): PImage not loaded.";
|
|
|
15120 |
}
|
|
|
15121 |
if(obj.width !== p.width || obj.height !== p.height){
|
|
|
15122 |
throw "Background image must be the same dimensions as the canvas.";
|
|
|
15123 |
}
|
|
|
15124 |
} else {
|
|
|
15125 |
obj = p.color(arg1, arg2, arg3, arg4);
|
|
|
15126 |
}
|
|
|
15127 |
|
|
|
15128 |
backgroundObj = obj;
|
|
|
15129 |
};
|
|
|
15130 |
|
|
|
15131 |
Drawing2D.prototype.background = function(arg1, arg2, arg3, arg4) {
|
|
|
15132 |
if (arg1 !== undef) {
|
|
|
15133 |
backgroundHelper(arg1, arg2, arg3, arg4);
|
|
|
15134 |
}
|
|
|
15135 |
|
|
|
15136 |
if (backgroundObj instanceof PImage || backgroundObj.__isPImage) {
|
|
|
15137 |
saveContext();
|
|
|
15138 |
curContext.setTransform(1, 0, 0, 1, 0, 0);
|
|
|
15139 |
p.image(backgroundObj, 0, 0);
|
|
|
15140 |
restoreContext();
|
|
|
15141 |
} else {
|
|
|
15142 |
saveContext();
|
|
|
15143 |
curContext.setTransform(1, 0, 0, 1, 0, 0);
|
|
|
15144 |
|
|
|
15145 |
// If the background is transparent
|
|
|
15146 |
if (p.alpha(backgroundObj) !== colorModeA) {
|
|
|
15147 |
curContext.clearRect(0,0, p.width, p.height);
|
|
|
15148 |
}
|
|
|
15149 |
curContext.fillStyle = p.color.toString(backgroundObj);
|
|
|
15150 |
curContext.fillRect(0, 0, p.width, p.height);
|
|
|
15151 |
isFillDirty = true;
|
|
|
15152 |
restoreContext();
|
|
|
15153 |
}
|
|
|
15154 |
};
|
|
|
15155 |
|
|
|
15156 |
Drawing3D.prototype.background = function(arg1, arg2, arg3, arg4) {
|
|
|
15157 |
if (arguments.length > 0) {
|
|
|
15158 |
backgroundHelper(arg1, arg2, arg3, arg4);
|
|
|
15159 |
}
|
|
|
15160 |
|
|
|
15161 |
var c = p.color.toGLArray(backgroundObj);
|
|
|
15162 |
curContext.clearColor(c[0], c[1], c[2], c[3]);
|
|
|
15163 |
curContext.clear(curContext.COLOR_BUFFER_BIT | curContext.DEPTH_BUFFER_BIT);
|
|
|
15164 |
|
|
|
15165 |
// An image as a background in 3D is not implemented yet
|
|
|
15166 |
};
|
|
|
15167 |
|
|
|
15168 |
// Draws an image to the Canvas
|
|
|
15169 |
/**
|
|
|
15170 |
* Displays images to the screen. The images must be in the sketch's "data" directory to load correctly. Select "Add
|
|
|
15171 |
* file..." from the "Sketch" menu to add the image. Processing currently works with GIF, JPEG, and Targa images. The
|
|
|
15172 |
* color of an image may be modified with the tint() function and if a GIF has transparency, it will maintain its
|
|
|
15173 |
* transparency. The img parameter specifies the image to display and the x and y parameters define the location of
|
|
|
15174 |
* the image from its upper-left corner. The image is displayed at its original size unless the width and height
|
|
|
15175 |
* parameters specify a different size. The imageMode() function changes the way the parameters work. A call to
|
|
|
15176 |
* imageMode(CORNERS) will change the width and height parameters to define the x and y values of the opposite
|
|
|
15177 |
* corner of the image.
|
|
|
15178 |
*
|
|
|
15179 |
* @param {PImage} img the image to display
|
|
|
15180 |
* @param {int|float} x x-coordinate of the image
|
|
|
15181 |
* @param {int|float} y y-coordinate of the image
|
|
|
15182 |
* @param {int|float} width width to display the image
|
|
|
15183 |
* @param {int|float} height height to display the image
|
|
|
15184 |
*
|
|
|
15185 |
* @see loadImage
|
|
|
15186 |
* @see PImage
|
|
|
15187 |
* @see imageMode
|
|
|
15188 |
* @see tint
|
|
|
15189 |
* @see background
|
|
|
15190 |
* @see alpha
|
|
|
15191 |
*/
|
|
|
15192 |
Drawing2D.prototype.image = function(img, x, y, w, h) {
|
|
|
15193 |
// Fix fractional positions
|
|
|
15194 |
x = Math.round(x);
|
|
|
15195 |
y = Math.round(y);
|
|
|
15196 |
|
|
|
15197 |
if (img.width > 0) {
|
|
|
15198 |
var wid = w || img.width;
|
|
|
15199 |
var hgt = h || img.height;
|
|
|
15200 |
|
|
|
15201 |
var bounds = imageModeConvert(x || 0, y || 0, w || img.width, h || img.height, arguments.length < 4);
|
|
|
15202 |
var fastImage = !!img.sourceImg && curTint === null;
|
|
|
15203 |
if (fastImage) {
|
|
|
15204 |
var htmlElement = img.sourceImg;
|
|
|
15205 |
if (img.__isDirty) {
|
|
|
15206 |
img.updatePixels();
|
|
|
15207 |
}
|
|
|
15208 |
// Using HTML element's width and height in case if the image was resized.
|
|
|
15209 |
curContext.drawImage(htmlElement, 0, 0,
|
|
|
15210 |
htmlElement.width, htmlElement.height, bounds.x, bounds.y, bounds.w, bounds.h);
|
|
|
15211 |
} else {
|
|
|
15212 |
var obj = img.toImageData();
|
|
|
15213 |
|
|
|
15214 |
// Tint the image
|
|
|
15215 |
if (curTint !== null) {
|
|
|
15216 |
curTint(obj);
|
|
|
15217 |
img.__isDirty = true;
|
|
|
15218 |
}
|
|
|
15219 |
|
|
|
15220 |
curContext.drawImage(getCanvasData(obj).canvas, 0, 0,
|
|
|
15221 |
img.width, img.height, bounds.x, bounds.y, bounds.w, bounds.h);
|
|
|
15222 |
}
|
|
|
15223 |
}
|
|
|
15224 |
};
|
|
|
15225 |
|
|
|
15226 |
Drawing3D.prototype.image = function(img, x, y, w, h) {
|
|
|
15227 |
if (img.width > 0) {
|
|
|
15228 |
// Fix fractional positions
|
|
|
15229 |
x = Math.round(x);
|
|
|
15230 |
y = Math.round(y);
|
|
|
15231 |
w = w || img.width;
|
|
|
15232 |
h = h || img.height;
|
|
|
15233 |
|
|
|
15234 |
p.beginShape(p.QUADS);
|
|
|
15235 |
p.texture(img);
|
|
|
15236 |
p.vertex(x, y, 0, 0, 0);
|
|
|
15237 |
p.vertex(x, y+h, 0, 0, h);
|
|
|
15238 |
p.vertex(x+w, y+h, 0, w, h);
|
|
|
15239 |
p.vertex(x+w, y, 0, w, 0);
|
|
|
15240 |
p.endShape();
|
|
|
15241 |
}
|
|
|
15242 |
};
|
|
|
15243 |
|
|
|
15244 |
/**
|
|
|
15245 |
* The tint() function sets the fill value for displaying images. Images can be tinted to
|
|
|
15246 |
* specified colors or made transparent by setting the alpha.
|
|
|
15247 |
* <br><br>To make an image transparent, but not change it's color,
|
|
|
15248 |
* use white as the tint color and specify an alpha value. For instance,
|
|
|
15249 |
* tint(255, 128) will make an image 50% transparent (unless
|
|
|
15250 |
* <b>colorMode()</b> has been used).
|
|
|
15251 |
*
|
|
|
15252 |
* <br><br>When using hexadecimal notation to specify a color, use "#" or
|
|
|
15253 |
* "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
|
|
|
15254 |
* digits to specify a color (the way colors are specified in HTML and CSS).
|
|
|
15255 |
* When using the hexadecimal notation starting with "0x", the hexadecimal
|
|
|
15256 |
* value must be specified with eight characters; the first two characters
|
|
|
15257 |
* define the alpha component and the remainder the red, green, and blue
|
|
|
15258 |
* components.
|
|
|
15259 |
* <br><br>The value for the parameter "gray" must be less than or equal
|
|
|
15260 |
* to the current maximum value as specified by <b>colorMode()</b>.
|
|
|
15261 |
* The default maximum value is 255.
|
|
|
15262 |
* <br><br>The tint() method is also used to control the coloring of
|
|
|
15263 |
* textures in 3D.
|
|
|
15264 |
*
|
|
|
15265 |
* @param {int|float} gray any valid number
|
|
|
15266 |
* @param {int|float} alpha opacity of the image
|
|
|
15267 |
* @param {int|float} value1 red or hue value
|
|
|
15268 |
* @param {int|float} value2 green or saturation value
|
|
|
15269 |
* @param {int|float} value3 blue or brightness value
|
|
|
15270 |
* @param {int|float} color any value of the color datatype
|
|
|
15271 |
* @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
|
|
|
15272 |
*
|
|
|
15273 |
* @see #noTint()
|
|
|
15274 |
* @see #image()
|
|
|
15275 |
*/
|
|
|
15276 |
p.tint = function(a1, a2, a3, a4) {
|
|
|
15277 |
var tintColor = p.color(a1, a2, a3, a4);
|
|
|
15278 |
var r = p.red(tintColor) / colorModeX;
|
|
|
15279 |
var g = p.green(tintColor) / colorModeY;
|
|
|
15280 |
var b = p.blue(tintColor) / colorModeZ;
|
|
|
15281 |
var a = p.alpha(tintColor) / colorModeA;
|
|
|
15282 |
curTint = function(obj) {
|
|
|
15283 |
var data = obj.data,
|
|
|
15284 |
length = 4 * obj.width * obj.height;
|
|
|
15285 |
for (var i = 0; i < length;) {
|
|
|
15286 |
data[i++] *= r;
|
|
|
15287 |
data[i++] *= g;
|
|
|
15288 |
data[i++] *= b;
|
|
|
15289 |
data[i++] *= a;
|
|
|
15290 |
}
|
|
|
15291 |
};
|
|
|
15292 |
// for overriding the color buffer when 3d rendering
|
|
|
15293 |
curTint3d = function(data){
|
|
|
15294 |
for (var i = 0; i < data.length;) {
|
|
|
15295 |
data[i++] = r;
|
|
|
15296 |
data[i++] = g;
|
|
|
15297 |
data[i++] = b;
|
|
|
15298 |
data[i++] = a;
|
|
|
15299 |
}
|
|
|
15300 |
};
|
|
|
15301 |
};
|
|
|
15302 |
|
|
|
15303 |
/**
|
|
|
15304 |
* The noTint() function removes the current fill value for displaying images and reverts to displaying images with their original hues.
|
|
|
15305 |
*
|
|
|
15306 |
* @see #tint()
|
|
|
15307 |
* @see #image()
|
|
|
15308 |
*/
|
|
|
15309 |
p.noTint = function() {
|
|
|
15310 |
curTint = null;
|
|
|
15311 |
curTint3d = null;
|
|
|
15312 |
};
|
|
|
15313 |
|
|
|
15314 |
/**
|
|
|
15315 |
* Copies a region of pixels from the display window to another area of the display window and copies a region of pixels from an
|
|
|
15316 |
* image used as the srcImg parameter into the display window. If the source and destination regions aren't the same size, it will
|
|
|
15317 |
* automatically resize the source pixels to fit the specified target region. No alpha information is used in the process, however
|
|
|
15318 |
* if the source image has an alpha channel set, it will be copied as well. This function ignores imageMode().
|
|
|
15319 |
*
|
|
|
15320 |
* @param {int} x X coordinate of the source's upper left corner
|
|
|
15321 |
* @param {int} y Y coordinate of the source's upper left corner
|
|
|
15322 |
* @param {int} width source image width
|
|
|
15323 |
* @param {int} height source image height
|
|
|
15324 |
* @param {int} dx X coordinate of the destination's upper left corner
|
|
|
15325 |
* @param {int} dy Y coordinate of the destination's upper left corner
|
|
|
15326 |
* @param {int} dwidth destination image width
|
|
|
15327 |
* @param {int} dheight destination image height
|
|
|
15328 |
* @param {PImage} srcImg image variable referring to the source image
|
|
|
15329 |
*
|
|
|
15330 |
* @see blend
|
|
|
15331 |
* @see get
|
|
|
15332 |
*/
|
|
|
15333 |
p.copy = function(src, sx, sy, sw, sh, dx, dy, dw, dh) {
|
|
|
15334 |
if (dh === undef) {
|
|
|
15335 |
// shift everything, and introduce p
|
|
|
15336 |
dh = dw;
|
|
|
15337 |
dw = dy;
|
|
|
15338 |
dy = dx;
|
|
|
15339 |
dx = sh;
|
|
|
15340 |
sh = sw;
|
|
|
15341 |
sw = sy;
|
|
|
15342 |
sy = sx;
|
|
|
15343 |
sx = src;
|
|
|
15344 |
src = p;
|
|
|
15345 |
}
|
|
|
15346 |
p.blend(src, sx, sy, sw, sh, dx, dy, dw, dh, PConstants.REPLACE);
|
|
|
15347 |
};
|
|
|
15348 |
|
|
|
15349 |
/**
|
|
|
15350 |
* Blends a region of pixels from one image into another (or in itself again) with full alpha channel support. There
|
|
|
15351 |
* is a choice of the following modes to blend the source pixels (A) with the ones of pixels in the destination image (B):
|
|
|
15352 |
* BLEND - linear interpolation of colours: C = A*factor + B
|
|
|
15353 |
* ADD - additive blending with white clip: C = min(A*factor + B, 255)
|
|
|
15354 |
* SUBTRACT - subtractive blending with black clip: C = max(B - A*factor, 0)
|
|
|
15355 |
* DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
|
|
|
15356 |
* LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
|
|
|
15357 |
* DIFFERENCE - subtract colors from underlying image.
|
|
|
15358 |
* EXCLUSION - similar to DIFFERENCE, but less extreme.
|
|
|
15359 |
* MULTIPLY - Multiply the colors, result will always be darker.
|
|
|
15360 |
* SCREEN - Opposite multiply, uses inverse values of the colors.
|
|
|
15361 |
* OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values, and screens light values.
|
|
|
15362 |
* HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
|
|
|
15363 |
* SOFT_LIGHT - Mix of DARKEST and LIGHTEST. Works like OVERLAY, but not as harsh.
|
|
|
15364 |
* DODGE - Lightens light tones and increases contrast, ignores darks. Called "Color Dodge" in Illustrator and Photoshop.
|
|
|
15365 |
* BURN - Darker areas are applied, increasing contrast, ignores lights. Called "Color Burn" in Illustrator and Photoshop.
|
|
|
15366 |
* All modes use the alpha information (highest byte) of source image pixels as the blending factor. If the source and
|
|
|
15367 |
* destination regions are different sizes, the image will be automatically resized to match the destination size. If the
|
|
|
15368 |
* srcImg parameter is not used, the display window is used as the source image. This function ignores imageMode().
|
|
|
15369 |
*
|
|
|
15370 |
* @param {int} x X coordinate of the source's upper left corner
|
|
|
15371 |
* @param {int} y Y coordinate of the source's upper left corner
|
|
|
15372 |
* @param {int} width source image width
|
|
|
15373 |
* @param {int} height source image height
|
|
|
15374 |
* @param {int} dx X coordinate of the destination's upper left corner
|
|
|
15375 |
* @param {int} dy Y coordinate of the destination's upper left corner
|
|
|
15376 |
* @param {int} dwidth destination image width
|
|
|
15377 |
* @param {int} dheight destination image height
|
|
|
15378 |
* @param {PImage} srcImg image variable referring to the source image
|
|
|
15379 |
* @param {PImage} MODE Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION, MULTIPLY, SCREEN,
|
|
|
15380 |
* OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
|
|
|
15381 |
* @see filter
|
|
|
15382 |
*/
|
|
|
15383 |
p.blend = function(src, sx, sy, sw, sh, dx, dy, dw, dh, mode, pimgdest) {
|
|
|
15384 |
if (src.isRemote) {
|
|
|
15385 |
throw "Image is loaded remotely. Cannot blend image.";
|
|
|
15386 |
}
|
|
|
15387 |
|
|
|
15388 |
if (mode === undef) {
|
|
|
15389 |
// shift everything, and introduce p
|
|
|
15390 |
mode = dh;
|
|
|
15391 |
dh = dw;
|
|
|
15392 |
dw = dy;
|
|
|
15393 |
dy = dx;
|
|
|
15394 |
dx = sh;
|
|
|
15395 |
sh = sw;
|
|
|
15396 |
sw = sy;
|
|
|
15397 |
sy = sx;
|
|
|
15398 |
sx = src;
|
|
|
15399 |
src = p;
|
|
|
15400 |
}
|
|
|
15401 |
|
|
|
15402 |
var sx2 = sx + sw,
|
|
|
15403 |
sy2 = sy + sh,
|
|
|
15404 |
dx2 = dx + dw,
|
|
|
15405 |
dy2 = dy + dh,
|
|
|
15406 |
dest = pimgdest || p;
|
|
|
15407 |
|
|
|
15408 |
// check if pimgdest is there and pixels, if so this was a call from pimg.blend
|
|
|
15409 |
if (pimgdest === undef || mode === undef) {
|
|
|
15410 |
p.loadPixels();
|
|
|
15411 |
}
|
|
|
15412 |
|
|
|
15413 |
src.loadPixels();
|
|
|
15414 |
|
|
|
15415 |
if (src === p && p.intersect(sx, sy, sx2, sy2, dx, dy, dx2, dy2)) {
|
|
|
15416 |
p.blit_resize(p.get(sx, sy, sx2 - sx, sy2 - sy), 0, 0, sx2 - sx - 1, sy2 - sy - 1,
|
|
|
15417 |
dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
|
|
|
15418 |
} else {
|
|
|
15419 |
p.blit_resize(src, sx, sy, sx2, sy2, dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
|
|
|
15420 |
}
|
|
|
15421 |
|
|
|
15422 |
if (pimgdest === undef) {
|
|
|
15423 |
p.updatePixels();
|
|
|
15424 |
}
|
|
|
15425 |
};
|
|
|
15426 |
|
|
|
15427 |
// helper function for filter()
|
|
|
15428 |
var buildBlurKernel = function(r) {
|
|
|
15429 |
var radius = p.floor(r * 3.5), i, radiusi;
|
|
|
15430 |
radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248);
|
|
|
15431 |
if (p.shared.blurRadius !== radius) {
|
|
|
15432 |
p.shared.blurRadius = radius;
|
|
|
15433 |
p.shared.blurKernelSize = 1 + (p.shared.blurRadius<<1);
|
|
|
15434 |
p.shared.blurKernel = new Float32Array(p.shared.blurKernelSize);
|
|
|
15435 |
var sharedBlurKernal = p.shared.blurKernel;
|
|
|
15436 |
var sharedBlurKernelSize = p.shared.blurKernelSize;
|
|
|
15437 |
var sharedBlurRadius = p.shared.blurRadius;
|
|
|
15438 |
// init blurKernel
|
|
|
15439 |
for (i = 0; i < sharedBlurKernelSize; i++) {
|
|
|
15440 |
sharedBlurKernal[i] = 0;
|
|
|
15441 |
}
|
|
|
15442 |
var radiusiSquared = (radius - 1) * (radius - 1);
|
|
|
15443 |
for (i = 1; i < radius; i++) {
|
|
|
15444 |
sharedBlurKernal[radius + i] = sharedBlurKernal[radiusi] = radiusiSquared;
|
|
|
15445 |
}
|
|
|
15446 |
sharedBlurKernal[radius] = radius * radius;
|
|
|
15447 |
}
|
|
|
15448 |
};
|
|
|
15449 |
|
|
|
15450 |
var blurARGB = function(r, aImg) {
|
|
|
15451 |
var sum, cr, cg, cb, ca, c, m;
|
|
|
15452 |
var read, ri, ym, ymi, bk0;
|
|
|
15453 |
var wh = aImg.pixels.getLength();
|
|
|
15454 |
var r2 = new Float32Array(wh);
|
|
|
15455 |
var g2 = new Float32Array(wh);
|
|
|
15456 |
var b2 = new Float32Array(wh);
|
|
|
15457 |
var a2 = new Float32Array(wh);
|
|
|
15458 |
var yi = 0;
|
|
|
15459 |
var x, y, i, offset;
|
|
|
15460 |
|
|
|
15461 |
buildBlurKernel(r);
|
|
|
15462 |
|
|
|
15463 |
var aImgHeight = aImg.height;
|
|
|
15464 |
var aImgWidth = aImg.width;
|
|
|
15465 |
var sharedBlurKernelSize = p.shared.blurKernelSize;
|
|
|
15466 |
var sharedBlurRadius = p.shared.blurRadius;
|
|
|
15467 |
var sharedBlurKernal = p.shared.blurKernel;
|
|
|
15468 |
var pix = aImg.imageData.data;
|
|
|
15469 |
|
|
|
15470 |
for (y = 0; y < aImgHeight; y++) {
|
|
|
15471 |
for (x = 0; x < aImgWidth; x++) {
|
|
|
15472 |
cb = cg = cr = ca = sum = 0;
|
|
|
15473 |
read = x - sharedBlurRadius;
|
|
|
15474 |
if (read<0) {
|
|
|
15475 |
bk0 = -read;
|
|
|
15476 |
read = 0;
|
|
|
15477 |
} else {
|
|
|
15478 |
if (read >= aImgWidth) {
|
|
|
15479 |
break;
|
|
|
15480 |
}
|
|
|
15481 |
bk0=0;
|
|
|
15482 |
}
|
|
|
15483 |
for (i = bk0; i < sharedBlurKernelSize; i++) {
|
|
|
15484 |
if (read >= aImgWidth) {
|
|
|
15485 |
break;
|
|
|
15486 |
}
|
|
|
15487 |
offset = (read + yi) *4;
|
|
|
15488 |
m = sharedBlurKernal[i];
|
|
|
15489 |
ca += m * pix[offset + 3];
|
|
|
15490 |
cr += m * pix[offset];
|
|
|
15491 |
cg += m * pix[offset + 1];
|
|
|
15492 |
cb += m * pix[offset + 2];
|
|
|
15493 |
sum += m;
|
|
|
15494 |
read++;
|
|
|
15495 |
}
|
|
|
15496 |
ri = yi + x;
|
|
|
15497 |
a2[ri] = ca / sum;
|
|
|
15498 |
r2[ri] = cr / sum;
|
|
|
15499 |
g2[ri] = cg / sum;
|
|
|
15500 |
b2[ri] = cb / sum;
|
|
|
15501 |
}
|
|
|
15502 |
yi += aImgWidth;
|
|
|
15503 |
}
|
|
|
15504 |
|
|
|
15505 |
yi = 0;
|
|
|
15506 |
ym = -sharedBlurRadius;
|
|
|
15507 |
ymi = ym*aImgWidth;
|
|
|
15508 |
|
|
|
15509 |
for (y = 0; y < aImgHeight; y++) {
|
|
|
15510 |
for (x = 0; x < aImgWidth; x++) {
|
|
|
15511 |
cb = cg = cr = ca = sum = 0;
|
|
|
15512 |
if (ym<0) {
|
|
|
15513 |
bk0 = ri = -ym;
|
|
|
15514 |
read = x;
|
|
|
15515 |
} else {
|
|
|
15516 |
if (ym >= aImgHeight) {
|
|
|
15517 |
break;
|
|
|
15518 |
}
|
|
|
15519 |
bk0 = 0;
|
|
|
15520 |
ri = ym;
|
|
|
15521 |
read = x + ymi;
|
|
|
15522 |
}
|
|
|
15523 |
for (i = bk0; i < sharedBlurKernelSize; i++) {
|
|
|
15524 |
if (ri >= aImgHeight) {
|
|
|
15525 |
break;
|
|
|
15526 |
}
|
|
|
15527 |
m = sharedBlurKernal[i];
|
|
|
15528 |
ca += m * a2[read];
|
|
|
15529 |
cr += m * r2[read];
|
|
|
15530 |
cg += m * g2[read];
|
|
|
15531 |
cb += m * b2[read];
|
|
|
15532 |
sum += m;
|
|
|
15533 |
ri++;
|
|
|
15534 |
read += aImgWidth;
|
|
|
15535 |
}
|
|
|
15536 |
offset = (x + yi) *4;
|
|
|
15537 |
pix[offset] = cr / sum;
|
|
|
15538 |
pix[offset + 1] = cg / sum;
|
|
|
15539 |
pix[offset + 2] = cb / sum;
|
|
|
15540 |
pix[offset + 3] = ca / sum;
|
|
|
15541 |
}
|
|
|
15542 |
yi += aImgWidth;
|
|
|
15543 |
ymi += aImgWidth;
|
|
|
15544 |
ym++;
|
|
|
15545 |
}
|
|
|
15546 |
};
|
|
|
15547 |
|
|
|
15548 |
// helper funtion for ERODE and DILATE modes of filter()
|
|
|
15549 |
var dilate = function(isInverted, aImg) {
|
|
|
15550 |
var currIdx = 0;
|
|
|
15551 |
var maxIdx = aImg.pixels.getLength();
|
|
|
15552 |
var out = new Int32Array(maxIdx);
|
|
|
15553 |
var currRowIdx, maxRowIdx, colOrig, colOut, currLum;
|
|
|
15554 |
var idxRight, idxLeft, idxUp, idxDown,
|
|
|
15555 |
colRight, colLeft, colUp, colDown,
|
|
|
15556 |
lumRight, lumLeft, lumUp, lumDown;
|
|
|
15557 |
|
|
|
15558 |
if (!isInverted) {
|
|
|
15559 |
// erosion (grow light areas)
|
|
|
15560 |
while (currIdx<maxIdx) {
|
|
|
15561 |
currRowIdx = currIdx;
|
|
|
15562 |
maxRowIdx = currIdx + aImg.width;
|
|
|
15563 |
while (currIdx < maxRowIdx) {
|
|
|
15564 |
colOrig = colOut = aImg.pixels.getPixel(currIdx);
|
|
|
15565 |
idxLeft = currIdx - 1;
|
|
|
15566 |
idxRight = currIdx + 1;
|
|
|
15567 |
idxUp = currIdx - aImg.width;
|
|
|
15568 |
idxDown = currIdx + aImg.width;
|
|
|
15569 |
if (idxLeft < currRowIdx) {
|
|
|
15570 |
idxLeft = currIdx;
|
|
|
15571 |
}
|
|
|
15572 |
if (idxRight >= maxRowIdx) {
|
|
|
15573 |
idxRight = currIdx;
|
|
|
15574 |
}
|
|
|
15575 |
if (idxUp < 0) {
|
|
|
15576 |
idxUp = 0;
|
|
|
15577 |
}
|
|
|
15578 |
if (idxDown >= maxIdx) {
|
|
|
15579 |
idxDown = currIdx;
|
|
|
15580 |
}
|
|
|
15581 |
colUp = aImg.pixels.getPixel(idxUp);
|
|
|
15582 |
colLeft = aImg.pixels.getPixel(idxLeft);
|
|
|
15583 |
colDown = aImg.pixels.getPixel(idxDown);
|
|
|
15584 |
colRight = aImg.pixels.getPixel(idxRight);
|
|
|
15585 |
|
|
|
15586 |
// compute luminance
|
|
|
15587 |
currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff);
|
|
|
15588 |
lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
|
|
|
15589 |
lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
|
|
|
15590 |
lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
|
|
|
15591 |
lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
|
|
|
15592 |
|
|
|
15593 |
if (lumLeft > currLum) {
|
|
|
15594 |
colOut = colLeft;
|
|
|
15595 |
currLum = lumLeft;
|
|
|
15596 |
}
|
|
|
15597 |
if (lumRight > currLum) {
|
|
|
15598 |
colOut = colRight;
|
|
|
15599 |
currLum = lumRight;
|
|
|
15600 |
}
|
|
|
15601 |
if (lumUp > currLum) {
|
|
|
15602 |
colOut = colUp;
|
|
|
15603 |
currLum = lumUp;
|
|
|
15604 |
}
|
|
|
15605 |
if (lumDown > currLum) {
|
|
|
15606 |
colOut = colDown;
|
|
|
15607 |
currLum = lumDown;
|
|
|
15608 |
}
|
|
|
15609 |
out[currIdx++] = colOut;
|
|
|
15610 |
}
|
|
|
15611 |
}
|
|
|
15612 |
} else {
|
|
|
15613 |
// dilate (grow dark areas)
|
|
|
15614 |
while (currIdx < maxIdx) {
|
|
|
15615 |
currRowIdx = currIdx;
|
|
|
15616 |
maxRowIdx = currIdx + aImg.width;
|
|
|
15617 |
while (currIdx < maxRowIdx) {
|
|
|
15618 |
colOrig = colOut = aImg.pixels.getPixel(currIdx);
|
|
|
15619 |
idxLeft = currIdx - 1;
|
|
|
15620 |
idxRight = currIdx + 1;
|
|
|
15621 |
idxUp = currIdx - aImg.width;
|
|
|
15622 |
idxDown = currIdx + aImg.width;
|
|
|
15623 |
if (idxLeft < currRowIdx) {
|
|
|
15624 |
idxLeft = currIdx;
|
|
|
15625 |
}
|
|
|
15626 |
if (idxRight >= maxRowIdx) {
|
|
|
15627 |
idxRight = currIdx;
|
|
|
15628 |
}
|
|
|
15629 |
if (idxUp < 0) {
|
|
|
15630 |
idxUp = 0;
|
|
|
15631 |
}
|
|
|
15632 |
if (idxDown >= maxIdx) {
|
|
|
15633 |
idxDown = currIdx;
|
|
|
15634 |
}
|
|
|
15635 |
colUp = aImg.pixels.getPixel(idxUp);
|
|
|
15636 |
colLeft = aImg.pixels.getPixel(idxLeft);
|
|
|
15637 |
colDown = aImg.pixels.getPixel(idxDown);
|
|
|
15638 |
colRight = aImg.pixels.getPixel(idxRight);
|
|
|
15639 |
|
|
|
15640 |
// compute luminance
|
|
|
15641 |
currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff);
|
|
|
15642 |
lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
|
|
|
15643 |
lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
|
|
|
15644 |
lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
|
|
|
15645 |
lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
|
|
|
15646 |
|
|
|
15647 |
if (lumLeft < currLum) {
|
|
|
15648 |
colOut = colLeft;
|
|
|
15649 |
currLum = lumLeft;
|
|
|
15650 |
}
|
|
|
15651 |
if (lumRight < currLum) {
|
|
|
15652 |
colOut = colRight;
|
|
|
15653 |
currLum = lumRight;
|
|
|
15654 |
}
|
|
|
15655 |
if (lumUp < currLum) {
|
|
|
15656 |
colOut = colUp;
|
|
|
15657 |
currLum = lumUp;
|
|
|
15658 |
}
|
|
|
15659 |
if (lumDown < currLum) {
|
|
|
15660 |
colOut = colDown;
|
|
|
15661 |
currLum = lumDown;
|
|
|
15662 |
}
|
|
|
15663 |
out[currIdx++]=colOut;
|
|
|
15664 |
}
|
|
|
15665 |
}
|
|
|
15666 |
}
|
|
|
15667 |
aImg.pixels.set(out);
|
|
|
15668 |
//p.arraycopy(out,0,pixels,0,maxIdx);
|
|
|
15669 |
};
|
|
|
15670 |
|
|
|
15671 |
/**
|
|
|
15672 |
* Filters the display window as defined by one of the following modes:
|
|
|
15673 |
* THRESHOLD - converts the image to black and white pixels depending if they are above or below the threshold
|
|
|
15674 |
* 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.
|
|
|
15675 |
* GRAY - converts any colors in the image to grayscale equivalents
|
|
|
15676 |
* INVERT - sets each pixel to its inverse value
|
|
|
15677 |
* POSTERIZE - limits each channel of the image to the number of colors specified as the level parameter
|
|
|
15678 |
* BLUR - executes a Guassian blur with the level parameter specifying the extent of the blurring. If no level parameter is
|
|
|
15679 |
* used, the blur is equivalent to Guassian blur of radius 1.
|
|
|
15680 |
* OPAQUE - sets the alpha channel to entirely opaque.
|
|
|
15681 |
* ERODE - reduces the light areas with the amount defined by the level parameter.
|
|
|
15682 |
* DILATE - increases the light areas with the amount defined by the level parameter.
|
|
|
15683 |
*
|
|
|
15684 |
* @param {MODE} MODE Either THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, or DILATE
|
|
|
15685 |
* @param {int|float} level defines the quality of the filter
|
|
|
15686 |
*
|
|
|
15687 |
* @see blend
|
|
|
15688 |
*/
|
|
|
15689 |
p.filter = function(kind, param, aImg){
|
|
|
15690 |
var img, col, lum, i;
|
|
|
15691 |
|
|
|
15692 |
if (arguments.length === 3) {
|
|
|
15693 |
aImg.loadPixels();
|
|
|
15694 |
img = aImg;
|
|
|
15695 |
} else {
|
|
|
15696 |
p.loadPixels();
|
|
|
15697 |
img = p;
|
|
|
15698 |
}
|
|
|
15699 |
|
|
|
15700 |
if (param === undef) {
|
|
|
15701 |
param = null;
|
|
|
15702 |
}
|
|
|
15703 |
if (img.isRemote) { // Remote images cannot access imageData
|
|
|
15704 |
throw "Image is loaded remotely. Cannot filter image.";
|
|
|
15705 |
}
|
|
|
15706 |
// begin filter process
|
|
|
15707 |
var imglen = img.pixels.getLength();
|
|
|
15708 |
switch (kind) {
|
|
|
15709 |
case PConstants.BLUR:
|
|
|
15710 |
var radius = param || 1; // if no param specified, use 1 (default for p5)
|
|
|
15711 |
blurARGB(radius, img);
|
|
|
15712 |
break;
|
|
|
15713 |
|
|
|
15714 |
case PConstants.GRAY:
|
|
|
15715 |
if (img.format === PConstants.ALPHA) { //trouble
|
|
|
15716 |
// for an alpha image, convert it to an opaque grayscale
|
|
|
15717 |
for (i = 0; i < imglen; i++) {
|
|
|
15718 |
col = 255 - img.pixels.getPixel(i);
|
|
|
15719 |
img.pixels.setPixel(i,(0xff000000 | (col << 16) | (col << 8) | col));
|
|
|
15720 |
}
|
|
|
15721 |
img.format = PConstants.RGB; //trouble
|
|
|
15722 |
} else {
|
|
|
15723 |
for (i = 0; i < imglen; i++) {
|
|
|
15724 |
col = img.pixels.getPixel(i);
|
|
|
15725 |
lum = (77*(col>>16&0xff) + 151*(col>>8&0xff) + 28*(col&0xff))>>8;
|
|
|
15726 |
img.pixels.setPixel(i,((col & PConstants.ALPHA_MASK) | lum<<16 | lum<<8 | lum));
|
|
|
15727 |
}
|
|
|
15728 |
}
|
|
|
15729 |
break;
|
|
|
15730 |
|
|
|
15731 |
case PConstants.INVERT:
|
|
|
15732 |
for (i = 0; i < imglen; i++) {
|
|
|
15733 |
img.pixels.setPixel(i, (img.pixels.getPixel(i) ^ 0xffffff));
|
|
|
15734 |
}
|
|
|
15735 |
break;
|
|
|
15736 |
|
|
|
15737 |
case PConstants.POSTERIZE:
|
|
|
15738 |
if (param === null) {
|
|
|
15739 |
throw "Use filter(POSTERIZE, int levels) instead of filter(POSTERIZE)";
|
|
|
15740 |
}
|
|
|
15741 |
var levels = p.floor(param);
|
|
|
15742 |
if ((levels < 2) || (levels > 255)) {
|
|
|
15743 |
throw "Levels must be between 2 and 255 for filter(POSTERIZE, levels)";
|
|
|
15744 |
}
|
|
|
15745 |
var levels1 = levels - 1;
|
|
|
15746 |
for (i = 0; i < imglen; i++) {
|
|
|
15747 |
var rlevel = (img.pixels.getPixel(i) >> 16) & 0xff;
|
|
|
15748 |
var glevel = (img.pixels.getPixel(i) >> 8) & 0xff;
|
|
|
15749 |
var blevel = img.pixels.getPixel(i) & 0xff;
|
|
|
15750 |
rlevel = (((rlevel * levels) >> 8) * 255) / levels1;
|
|
|
15751 |
glevel = (((glevel * levels) >> 8) * 255) / levels1;
|
|
|
15752 |
blevel = (((blevel * levels) >> 8) * 255) / levels1;
|
|
|
15753 |
img.pixels.setPixel(i, ((0xff000000 & img.pixels.getPixel(i)) | (rlevel << 16) | (glevel << 8) | blevel));
|
|
|
15754 |
}
|
|
|
15755 |
break;
|
|
|
15756 |
|
|
|
15757 |
case PConstants.OPAQUE:
|
|
|
15758 |
for (i = 0; i < imglen; i++) {
|
|
|
15759 |
img.pixels.setPixel(i, (img.pixels.getPixel(i) | 0xff000000));
|
|
|
15760 |
}
|
|
|
15761 |
img.format = PConstants.RGB; //trouble
|
|
|
15762 |
break;
|
|
|
15763 |
|
|
|
15764 |
case PConstants.THRESHOLD:
|
|
|
15765 |
if (param === null) {
|
|
|
15766 |
param = 0.5;
|
|
|
15767 |
}
|
|
|
15768 |
if ((param < 0) || (param > 1)) {
|
|
|
15769 |
throw "Level must be between 0 and 1 for filter(THRESHOLD, level)";
|
|
|
15770 |
}
|
|
|
15771 |
var thresh = p.floor(param * 255);
|
|
|
15772 |
for (i = 0; i < imglen; i++) {
|
|
|
15773 |
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)));
|
|
|
15774 |
img.pixels.setPixel(i, ((img.pixels.getPixel(i) & PConstants.ALPHA_MASK) | ((max < thresh) ? 0x000000 : 0xffffff)));
|
|
|
15775 |
}
|
|
|
15776 |
break;
|
|
|
15777 |
|
|
|
15778 |
case PConstants.ERODE:
|
|
|
15779 |
dilate(true, img);
|
|
|
15780 |
break;
|
|
|
15781 |
|
|
|
15782 |
case PConstants.DILATE:
|
|
|
15783 |
dilate(false, img);
|
|
|
15784 |
break;
|
|
|
15785 |
}
|
|
|
15786 |
img.updatePixels();
|
|
|
15787 |
};
|
|
|
15788 |
|
|
|
15789 |
|
|
|
15790 |
// shared variables for blit_resize(), filter_new_scanline(), filter_bilinear(), filter()
|
|
|
15791 |
// change this in the future to not be exposed to p
|
|
|
15792 |
p.shared = {
|
|
|
15793 |
fracU: 0,
|
|
|
15794 |
ifU: 0,
|
|
|
15795 |
fracV: 0,
|
|
|
15796 |
ifV: 0,
|
|
|
15797 |
u1: 0,
|
|
|
15798 |
u2: 0,
|
|
|
15799 |
v1: 0,
|
|
|
15800 |
v2: 0,
|
|
|
15801 |
sX: 0,
|
|
|
15802 |
sY: 0,
|
|
|
15803 |
iw: 0,
|
|
|
15804 |
iw1: 0,
|
|
|
15805 |
ih1: 0,
|
|
|
15806 |
ul: 0,
|
|
|
15807 |
ll: 0,
|
|
|
15808 |
ur: 0,
|
|
|
15809 |
lr: 0,
|
|
|
15810 |
cUL: 0,
|
|
|
15811 |
cLL: 0,
|
|
|
15812 |
cUR: 0,
|
|
|
15813 |
cLR: 0,
|
|
|
15814 |
srcXOffset: 0,
|
|
|
15815 |
srcYOffset: 0,
|
|
|
15816 |
r: 0,
|
|
|
15817 |
g: 0,
|
|
|
15818 |
b: 0,
|
|
|
15819 |
a: 0,
|
|
|
15820 |
srcBuffer: null,
|
|
|
15821 |
blurRadius: 0,
|
|
|
15822 |
blurKernelSize: 0,
|
|
|
15823 |
blurKernel: null
|
|
|
15824 |
};
|
|
|
15825 |
|
|
|
15826 |
p.intersect = function(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2) {
|
|
|
15827 |
var sw = sx2 - sx1 + 1;
|
|
|
15828 |
var sh = sy2 - sy1 + 1;
|
|
|
15829 |
var dw = dx2 - dx1 + 1;
|
|
|
15830 |
var dh = dy2 - dy1 + 1;
|
|
|
15831 |
if (dx1 < sx1) {
|
|
|
15832 |
dw += dx1 - sx1;
|
|
|
15833 |
if (dw > sw) {
|
|
|
15834 |
dw = sw;
|
|
|
15835 |
}
|
|
|
15836 |
} else {
|
|
|
15837 |
var w = sw + sx1 - dx1;
|
|
|
15838 |
if (dw > w) {
|
|
|
15839 |
dw = w;
|
|
|
15840 |
}
|
|
|
15841 |
}
|
|
|
15842 |
if (dy1 < sy1) {
|
|
|
15843 |
dh += dy1 - sy1;
|
|
|
15844 |
if (dh > sh) {
|
|
|
15845 |
dh = sh;
|
|
|
15846 |
}
|
|
|
15847 |
} else {
|
|
|
15848 |
var h = sh + sy1 - dy1;
|
|
|
15849 |
if (dh > h) {
|
|
|
15850 |
dh = h;
|
|
|
15851 |
}
|
|
|
15852 |
}
|
|
|
15853 |
return ! (dw <= 0 || dh <= 0);
|
|
|
15854 |
};
|
|
|
15855 |
|
|
|
15856 |
var blendFuncs = {};
|
|
|
15857 |
blendFuncs[PConstants.BLEND] = p.modes.blend;
|
|
|
15858 |
blendFuncs[PConstants.ADD] = p.modes.add;
|
|
|
15859 |
blendFuncs[PConstants.SUBTRACT] = p.modes.subtract;
|
|
|
15860 |
blendFuncs[PConstants.LIGHTEST] = p.modes.lightest;
|
|
|
15861 |
blendFuncs[PConstants.DARKEST] = p.modes.darkest;
|
|
|
15862 |
blendFuncs[PConstants.REPLACE] = p.modes.replace;
|
|
|
15863 |
blendFuncs[PConstants.DIFFERENCE] = p.modes.difference;
|
|
|
15864 |
blendFuncs[PConstants.EXCLUSION] = p.modes.exclusion;
|
|
|
15865 |
blendFuncs[PConstants.MULTIPLY] = p.modes.multiply;
|
|
|
15866 |
blendFuncs[PConstants.SCREEN] = p.modes.screen;
|
|
|
15867 |
blendFuncs[PConstants.OVERLAY] = p.modes.overlay;
|
|
|
15868 |
blendFuncs[PConstants.HARD_LIGHT] = p.modes.hard_light;
|
|
|
15869 |
blendFuncs[PConstants.SOFT_LIGHT] = p.modes.soft_light;
|
|
|
15870 |
blendFuncs[PConstants.DODGE] = p.modes.dodge;
|
|
|
15871 |
blendFuncs[PConstants.BURN] = p.modes.burn;
|
|
|
15872 |
|
|
|
15873 |
p.blit_resize = function(img, srcX1, srcY1, srcX2, srcY2, destPixels,
|
|
|
15874 |
screenW, screenH, destX1, destY1, destX2, destY2, mode) {
|
|
|
15875 |
var x, y;
|
|
|
15876 |
if (srcX1 < 0) {
|
|
|
15877 |
srcX1 = 0;
|
|
|
15878 |
}
|
|
|
15879 |
if (srcY1 < 0) {
|
|
|
15880 |
srcY1 = 0;
|
|
|
15881 |
}
|
|
|
15882 |
if (srcX2 >= img.width) {
|
|
|
15883 |
srcX2 = img.width - 1;
|
|
|
15884 |
}
|
|
|
15885 |
if (srcY2 >= img.height) {
|
|
|
15886 |
srcY2 = img.height - 1;
|
|
|
15887 |
}
|
|
|
15888 |
var srcW = srcX2 - srcX1;
|
|
|
15889 |
var srcH = srcY2 - srcY1;
|
|
|
15890 |
var destW = destX2 - destX1;
|
|
|
15891 |
var destH = destY2 - destY1;
|
|
|
15892 |
|
|
|
15893 |
if (destW <= 0 || destH <= 0 || srcW <= 0 || srcH <= 0 || destX1 >= screenW ||
|
|
|
15894 |
destY1 >= screenH || srcX1 >= img.width || srcY1 >= img.height) {
|
|
|
15895 |
return;
|
|
|
15896 |
}
|
|
|
15897 |
|
|
|
15898 |
var dx = Math.floor(srcW / destW * PConstants.PRECISIONF);
|
|
|
15899 |
var dy = Math.floor(srcH / destH * PConstants.PRECISIONF);
|
|
|
15900 |
|
|
|
15901 |
var pshared = p.shared;
|
|
|
15902 |
|
|
|
15903 |
pshared.srcXOffset = Math.floor(destX1 < 0 ? -destX1 * dx : srcX1 * PConstants.PRECISIONF);
|
|
|
15904 |
pshared.srcYOffset = Math.floor(destY1 < 0 ? -destY1 * dy : srcY1 * PConstants.PRECISIONF);
|
|
|
15905 |
if (destX1 < 0) {
|
|
|
15906 |
destW += destX1;
|
|
|
15907 |
destX1 = 0;
|
|
|
15908 |
}
|
|
|
15909 |
if (destY1 < 0) {
|
|
|
15910 |
destH += destY1;
|
|
|
15911 |
destY1 = 0;
|
|
|
15912 |
}
|
|
|
15913 |
destW = Math.min(destW, screenW - destX1);
|
|
|
15914 |
destH = Math.min(destH, screenH - destY1);
|
|
|
15915 |
|
|
|
15916 |
var destOffset = destY1 * screenW + destX1;
|
|
|
15917 |
var destColor;
|
|
|
15918 |
|
|
|
15919 |
pshared.srcBuffer = img.imageData.data;
|
|
|
15920 |
pshared.iw = img.width;
|
|
|
15921 |
pshared.iw1 = img.width - 1;
|
|
|
15922 |
pshared.ih1 = img.height - 1;
|
|
|
15923 |
|
|
|
15924 |
// cache for speed
|
|
|
15925 |
var filterBilinear = p.filter_bilinear,
|
|
|
15926 |
filterNewScanline = p.filter_new_scanline,
|
|
|
15927 |
blendFunc = blendFuncs[mode],
|
|
|
15928 |
blendedColor,
|
|
|
15929 |
idx,
|
|
|
15930 |
cULoffset,
|
|
|
15931 |
cURoffset,
|
|
|
15932 |
cLLoffset,
|
|
|
15933 |
cLRoffset,
|
|
|
15934 |
ALPHA_MASK = PConstants.ALPHA_MASK,
|
|
|
15935 |
RED_MASK = PConstants.RED_MASK,
|
|
|
15936 |
GREEN_MASK = PConstants.GREEN_MASK,
|
|
|
15937 |
BLUE_MASK = PConstants.BLUE_MASK,
|
|
|
15938 |
PREC_MAXVAL = PConstants.PREC_MAXVAL,
|
|
|
15939 |
PRECISIONB = PConstants.PRECISIONB,
|
|
|
15940 |
PREC_RED_SHIFT = PConstants.PREC_RED_SHIFT,
|
|
|
15941 |
PREC_ALPHA_SHIFT = PConstants.PREC_ALPHA_SHIFT,
|
|
|
15942 |
srcBuffer = pshared.srcBuffer,
|
|
|
15943 |
min = Math.min;
|
|
|
15944 |
|
|
|
15945 |
for (y = 0; y < destH; y++) {
|
|
|
15946 |
|
|
|
15947 |
pshared.sX = pshared.srcXOffset;
|
|
|
15948 |
pshared.fracV = pshared.srcYOffset & PREC_MAXVAL;
|
|
|
15949 |
pshared.ifV = PREC_MAXVAL - pshared.fracV;
|
|
|
15950 |
pshared.v1 = (pshared.srcYOffset >> PRECISIONB) * pshared.iw;
|
|
|
15951 |
pshared.v2 = min((pshared.srcYOffset >> PRECISIONB) + 1, pshared.ih1) * pshared.iw;
|
|
|
15952 |
|
|
|
15953 |
for (x = 0; x < destW; x++) {
|
|
|
15954 |
idx = (destOffset + x) * 4;
|
|
|
15955 |
|
|
|
15956 |
destColor = (destPixels[idx + 3] << 24) &
|
|
|
15957 |
ALPHA_MASK | (destPixels[idx] << 16) &
|
|
|
15958 |
RED_MASK | (destPixels[idx + 1] << 8) &
|
|
|
15959 |
GREEN_MASK | destPixels[idx + 2] & BLUE_MASK;
|
|
|
15960 |
|
|
|
15961 |
pshared.fracU = pshared.sX & PREC_MAXVAL;
|
|
|
15962 |
pshared.ifU = PREC_MAXVAL - pshared.fracU;
|
|
|
15963 |
pshared.ul = (pshared.ifU * pshared.ifV) >> PRECISIONB;
|
|
|
15964 |
pshared.ll = (pshared.ifU * pshared.fracV) >> PRECISIONB;
|
|
|
15965 |
pshared.ur = (pshared.fracU * pshared.ifV) >> PRECISIONB;
|
|
|
15966 |
pshared.lr = (pshared.fracU * pshared.fracV) >> PRECISIONB;
|
|
|
15967 |
pshared.u1 = (pshared.sX >> PRECISIONB);
|
|
|
15968 |
pshared.u2 = min(pshared.u1 + 1, pshared.iw1);
|
|
|
15969 |
|
|
|
15970 |
cULoffset = (pshared.v1 + pshared.u1) * 4;
|
|
|
15971 |
cURoffset = (pshared.v1 + pshared.u2) * 4;
|
|
|
15972 |
cLLoffset = (pshared.v2 + pshared.u1) * 4;
|
|
|
15973 |
cLRoffset = (pshared.v2 + pshared.u2) * 4;
|
|
|
15974 |
|
|
|
15975 |
pshared.cUL = (srcBuffer[cULoffset + 3] << 24) &
|
|
|
15976 |
ALPHA_MASK | (srcBuffer[cULoffset] << 16) &
|
|
|
15977 |
RED_MASK | (srcBuffer[cULoffset + 1] << 8) &
|
|
|
15978 |
GREEN_MASK | srcBuffer[cULoffset + 2] & BLUE_MASK;
|
|
|
15979 |
|
|
|
15980 |
pshared.cUR = (srcBuffer[cURoffset + 3] << 24) &
|
|
|
15981 |
ALPHA_MASK | (srcBuffer[cURoffset] << 16) &
|
|
|
15982 |
RED_MASK | (srcBuffer[cURoffset + 1] << 8) &
|
|
|
15983 |
GREEN_MASK | srcBuffer[cURoffset + 2] & BLUE_MASK;
|
|
|
15984 |
|
|
|
15985 |
pshared.cLL = (srcBuffer[cLLoffset + 3] << 24) &
|
|
|
15986 |
ALPHA_MASK | (srcBuffer[cLLoffset] << 16) &
|
|
|
15987 |
RED_MASK | (srcBuffer[cLLoffset + 1] << 8) &
|
|
|
15988 |
GREEN_MASK | srcBuffer[cLLoffset + 2] & BLUE_MASK;
|
|
|
15989 |
|
|
|
15990 |
pshared.cLR = (srcBuffer[cLRoffset + 3] << 24) &
|
|
|
15991 |
ALPHA_MASK | (srcBuffer[cLRoffset] << 16) &
|
|
|
15992 |
RED_MASK | (srcBuffer[cLRoffset + 1] << 8) &
|
|
|
15993 |
GREEN_MASK | srcBuffer[cLRoffset + 2] & BLUE_MASK;
|
|
|
15994 |
|
|
|
15995 |
pshared.r = ((pshared.ul * ((pshared.cUL & RED_MASK) >> 16) +
|
|
|
15996 |
pshared.ll * ((pshared.cLL & RED_MASK) >> 16) +
|
|
|
15997 |
pshared.ur * ((pshared.cUR & RED_MASK) >> 16) +
|
|
|
15998 |
pshared.lr * ((pshared.cLR & RED_MASK) >> 16)) << PREC_RED_SHIFT) & RED_MASK;
|
|
|
15999 |
pshared.g = ((pshared.ul * (pshared.cUL & GREEN_MASK) +
|
|
|
16000 |
pshared.ll * (pshared.cLL & GREEN_MASK) +
|
|
|
16001 |
pshared.ur * (pshared.cUR & GREEN_MASK) +
|
|
|
16002 |
pshared.lr * (pshared.cLR & GREEN_MASK)) >>> PRECISIONB) & GREEN_MASK;
|
|
|
16003 |
pshared.b = (pshared.ul * (pshared.cUL & BLUE_MASK) +
|
|
|
16004 |
pshared.ll * (pshared.cLL & BLUE_MASK) +
|
|
|
16005 |
pshared.ur * (pshared.cUR & BLUE_MASK) +
|
|
|
16006 |
pshared.lr * (pshared.cLR & BLUE_MASK)) >>> PRECISIONB;
|
|
|
16007 |
pshared.a = ((pshared.ul * ((pshared.cUL & ALPHA_MASK) >>> 24) +
|
|
|
16008 |
pshared.ll * ((pshared.cLL & ALPHA_MASK) >>> 24) +
|
|
|
16009 |
pshared.ur * ((pshared.cUR & ALPHA_MASK) >>> 24) +
|
|
|
16010 |
pshared.lr * ((pshared.cLR & ALPHA_MASK) >>> 24)) << PREC_ALPHA_SHIFT) & ALPHA_MASK;
|
|
|
16011 |
|
|
|
16012 |
blendedColor = blendFunc(destColor, (pshared.a | pshared.r | pshared.g | pshared.b));
|
|
|
16013 |
|
|
|
16014 |
destPixels[idx] = (blendedColor & RED_MASK) >>> 16;
|
|
|
16015 |
destPixels[idx + 1] = (blendedColor & GREEN_MASK) >>> 8;
|
|
|
16016 |
destPixels[idx + 2] = (blendedColor & BLUE_MASK);
|
|
|
16017 |
destPixels[idx + 3] = (blendedColor & ALPHA_MASK) >>> 24;
|
|
|
16018 |
|
|
|
16019 |
pshared.sX += dx;
|
|
|
16020 |
}
|
|
|
16021 |
destOffset += screenW;
|
|
|
16022 |
pshared.srcYOffset += dy;
|
|
|
16023 |
}
|
|
|
16024 |
};
|
|
|
16025 |
|
|
|
16026 |
////////////////////////////////////////////////////////////////////////////
|
|
|
16027 |
// Font handling
|
|
|
16028 |
////////////////////////////////////////////////////////////////////////////
|
|
|
16029 |
|
|
|
16030 |
/**
|
|
|
16031 |
* loadFont() Loads a font into a variable of type PFont.
|
|
|
16032 |
*
|
|
|
16033 |
* @param {String} name filename of the font to load
|
|
|
16034 |
* @param {int|float} size option font size (used internally)
|
|
|
16035 |
*
|
|
|
16036 |
* @returns {PFont} new PFont object
|
|
|
16037 |
*
|
|
|
16038 |
* @see #PFont
|
|
|
16039 |
* @see #textFont
|
|
|
16040 |
* @see #text
|
|
|
16041 |
* @see #createFont
|
|
|
16042 |
*/
|
|
|
16043 |
p.loadFont = function(name, size) {
|
|
|
16044 |
if (name === undef) {
|
|
|
16045 |
throw("font name required in loadFont.");
|
|
|
16046 |
}
|
|
|
16047 |
if (name.indexOf(".svg") === -1) {
|
|
|
16048 |
if (size === undef) {
|
|
|
16049 |
size = curTextFont.size;
|
|
|
16050 |
}
|
|
|
16051 |
return PFont.get(name, size);
|
|
|
16052 |
}
|
|
|
16053 |
// If the font is a glyph, calculate by SVG table
|
|
|
16054 |
var font = p.loadGlyphs(name);
|
|
|
16055 |
|
|
|
16056 |
return {
|
|
|
16057 |
name: name,
|
|
|
16058 |
css: '12px sans-serif',
|
|
|
16059 |
glyph: true,
|
|
|
16060 |
units_per_em: font.units_per_em,
|
|
|
16061 |
horiz_adv_x: 1 / font.units_per_em * font.horiz_adv_x,
|
|
|
16062 |
ascent: font.ascent,
|
|
|
16063 |
descent: font.descent,
|
|
|
16064 |
width: function(str) {
|
|
|
16065 |
var width = 0;
|
|
|
16066 |
var len = str.length;
|
|
|
16067 |
for (var i = 0; i < len; i++) {
|
|
|
16068 |
try {
|
|
|
16069 |
width += parseFloat(p.glyphLook(p.glyphTable[name], str[i]).horiz_adv_x);
|
|
|
16070 |
}
|
|
|
16071 |
catch(e) {
|
|
|
16072 |
Processing.debug(e);
|
|
|
16073 |
}
|
|
|
16074 |
}
|
|
|
16075 |
return width / p.glyphTable[name].units_per_em;
|
|
|
16076 |
}
|
|
|
16077 |
};
|
|
|
16078 |
};
|
|
|
16079 |
|
|
|
16080 |
/**
|
|
|
16081 |
* createFont() Loads a font into a variable of type PFont.
|
|
|
16082 |
* Smooth and charset are ignored in Processing.js.
|
|
|
16083 |
*
|
|
|
16084 |
* @param {String} name filename of the font to load
|
|
|
16085 |
* @param {int|float} size font size in pixels
|
|
|
16086 |
* @param {boolean} smooth not used in Processing.js
|
|
|
16087 |
* @param {char[]} charset not used in Processing.js
|
|
|
16088 |
*
|
|
|
16089 |
* @returns {PFont} new PFont object
|
|
|
16090 |
*
|
|
|
16091 |
* @see #PFont
|
|
|
16092 |
* @see #textFont
|
|
|
16093 |
* @see #text
|
|
|
16094 |
* @see #loadFont
|
|
|
16095 |
*/
|
|
|
16096 |
p.createFont = function(name, size) {
|
|
|
16097 |
// because Processing.js only deals with real fonts,
|
|
|
16098 |
// createFont is simply a wrapper for loadFont/2
|
|
|
16099 |
return p.loadFont(name, size);
|
|
|
16100 |
};
|
|
|
16101 |
|
|
|
16102 |
/**
|
|
|
16103 |
* textFont() Sets the current font.
|
|
|
16104 |
*
|
|
|
16105 |
* @param {PFont} pfont the PFont to load as current text font
|
|
|
16106 |
* @param {int|float} size optional font size in pixels
|
|
|
16107 |
*
|
|
|
16108 |
* @see #createFont
|
|
|
16109 |
* @see #loadFont
|
|
|
16110 |
* @see #PFont
|
|
|
16111 |
* @see #text
|
|
|
16112 |
*/
|
|
|
16113 |
p.textFont = function(pfont, size) {
|
|
|
16114 |
if (size !== undef) {
|
|
|
16115 |
// If we're using an SVG glyph font, don't load from cache
|
|
|
16116 |
if (!pfont.glyph) {
|
|
|
16117 |
pfont = PFont.get(pfont.name, size);
|
|
|
16118 |
}
|
|
|
16119 |
curTextSize = size;
|
|
|
16120 |
}
|
|
|
16121 |
curTextFont = pfont;
|
|
|
16122 |
curFontName = curTextFont.name;
|
|
|
16123 |
curTextAscent = curTextFont.ascent;
|
|
|
16124 |
curTextDescent = curTextFont.descent;
|
|
|
16125 |
curTextLeading = curTextFont.leading;
|
|
|
16126 |
var curContext = drawing.$ensureContext();
|
|
|
16127 |
curContext.font = curTextFont.css;
|
|
|
16128 |
};
|
|
|
16129 |
|
|
|
16130 |
/**
|
|
|
16131 |
* textSize() Sets the current font size in pixels.
|
|
|
16132 |
*
|
|
|
16133 |
* @param {int|float} size font size in pixels
|
|
|
16134 |
*
|
|
|
16135 |
* @see #textFont
|
|
|
16136 |
* @see #loadFont
|
|
|
16137 |
* @see #PFont
|
|
|
16138 |
* @see #text
|
|
|
16139 |
*/
|
|
|
16140 |
p.textSize = function(size) {
|
|
|
16141 |
if (size !== curTextSize) {
|
|
|
16142 |
curTextFont = PFont.get(curFontName, size);
|
|
|
16143 |
curTextSize = size;
|
|
|
16144 |
// recache metrics
|
|
|
16145 |
curTextAscent = curTextFont.ascent;
|
|
|
16146 |
curTextDescent = curTextFont.descent;
|
|
|
16147 |
curTextLeading = curTextFont.leading;
|
|
|
16148 |
var curContext = drawing.$ensureContext();
|
|
|
16149 |
curContext.font = curTextFont.css;
|
|
|
16150 |
}
|
|
|
16151 |
};
|
|
|
16152 |
|
|
|
16153 |
/**
|
|
|
16154 |
* textAscent() returns the maximum height a character extends above the baseline of the
|
|
|
16155 |
* current font at its current size, in pixels.
|
|
|
16156 |
*
|
|
|
16157 |
* @returns {float} height of the current font above the baseline, at its current size, in pixels
|
|
|
16158 |
*
|
|
|
16159 |
* @see #textDescent
|
|
|
16160 |
*/
|
|
|
16161 |
p.textAscent = function() {
|
|
|
16162 |
return curTextAscent;
|
|
|
16163 |
};
|
|
|
16164 |
|
|
|
16165 |
/**
|
|
|
16166 |
* textDescent() returns the maximum depth a character will protrude below the baseline of
|
|
|
16167 |
* the current font at its current size, in pixels.
|
|
|
16168 |
*
|
|
|
16169 |
* @returns {float} depth of the current font below the baseline, at its current size, in pixels
|
|
|
16170 |
*
|
|
|
16171 |
* @see #textAscent
|
|
|
16172 |
*/
|
|
|
16173 |
p.textDescent = function() {
|
|
|
16174 |
return curTextDescent;
|
|
|
16175 |
};
|
|
|
16176 |
|
|
|
16177 |
/**
|
|
|
16178 |
* textLeading() Sets the current font's leading, which is the distance
|
|
|
16179 |
* from baseline to baseline over consecutive lines, with additional vertical
|
|
|
16180 |
* spacing taking into account. Usually this value is 1.2 or 1.25 times the
|
|
|
16181 |
* textsize, but this value can be changed to effect vertically compressed
|
|
|
16182 |
* or stretched text.
|
|
|
16183 |
*
|
|
|
16184 |
* @param {int|float} the desired baseline-to-baseline size in pixels
|
|
|
16185 |
*/
|
|
|
16186 |
p.textLeading = function(leading) {
|
|
|
16187 |
curTextLeading = leading;
|
|
|
16188 |
};
|
|
|
16189 |
|
|
|
16190 |
/**
|
|
|
16191 |
* textAlign() Sets the current alignment for drawing text.
|
|
|
16192 |
*
|
|
|
16193 |
* @param {int} ALIGN Horizontal alignment, either LEFT, CENTER, or RIGHT
|
|
|
16194 |
* @param {int} YALIGN optional vertical alignment, either TOP, BOTTOM, CENTER, or BASELINE
|
|
|
16195 |
*
|
|
|
16196 |
* @see #loadFont
|
|
|
16197 |
* @see #PFont
|
|
|
16198 |
* @see #text
|
|
|
16199 |
*/
|
|
|
16200 |
p.textAlign = function(xalign, yalign) {
|
|
|
16201 |
horizontalTextAlignment = xalign;
|
|
|
16202 |
verticalTextAlignment = yalign || PConstants.BASELINE;
|
|
|
16203 |
};
|
|
|
16204 |
|
|
|
16205 |
/**
|
|
|
16206 |
* toP5String converts things with arbitrary data type into
|
|
|
16207 |
* string values, for text rendering.
|
|
|
16208 |
*
|
|
|
16209 |
* @param {any} any object that can be converted into a string
|
|
|
16210 |
*
|
|
|
16211 |
* @return {String} the string representation of the input
|
|
|
16212 |
*/
|
|
|
16213 |
function toP5String(obj) {
|
|
|
16214 |
if(obj instanceof String) {
|
|
|
16215 |
return obj;
|
|
|
16216 |
}
|
|
|
16217 |
if(typeof obj === 'number') {
|
|
|
16218 |
// check if an int
|
|
|
16219 |
if(obj === (0 | obj)) {
|
|
|
16220 |
return obj.toString();
|
|
|
16221 |
}
|
|
|
16222 |
return p.nf(obj, 0, 3);
|
|
|
16223 |
}
|
|
|
16224 |
if(obj === null || obj === undef) {
|
|
|
16225 |
return "";
|
|
|
16226 |
}
|
|
|
16227 |
return obj.toString();
|
|
|
16228 |
}
|
|
|
16229 |
|
|
|
16230 |
/**
|
|
|
16231 |
* textWidth() Calculates and returns the width of any character or text string in pixels.
|
|
|
16232 |
*
|
|
|
16233 |
* @param {char|String} str char or String to be measured
|
|
|
16234 |
*
|
|
|
16235 |
* @return {float} width of char or String in pixels
|
|
|
16236 |
*
|
|
|
16237 |
* @see #loadFont
|
|
|
16238 |
* @see #PFont
|
|
|
16239 |
* @see #text
|
|
|
16240 |
* @see #textFont
|
|
|
16241 |
*/
|
|
|
16242 |
Drawing2D.prototype.textWidth = function(str) {
|
|
|
16243 |
var lines = toP5String(str).split(/\r?\n/g), width = 0;
|
|
|
16244 |
var i, linesCount = lines.length;
|
|
|
16245 |
|
|
|
16246 |
curContext.font = curTextFont.css;
|
|
|
16247 |
for (i = 0; i < linesCount; ++i) {
|
|
|
16248 |
width = Math.max(width, curTextFont.measureTextWidth(lines[i]));
|
|
|
16249 |
}
|
|
|
16250 |
return width | 0;
|
|
|
16251 |
};
|
|
|
16252 |
|
|
|
16253 |
Drawing3D.prototype.textWidth = function(str) {
|
|
|
16254 |
var lines = toP5String(str).split(/\r?\n/g), width = 0;
|
|
|
16255 |
var i, linesCount = lines.length;
|
|
|
16256 |
if (textcanvas === undef) {
|
|
|
16257 |
textcanvas = document.createElement("canvas");
|
|
|
16258 |
}
|
|
|
16259 |
|
|
|
16260 |
var textContext = textcanvas.getContext("2d");
|
|
|
16261 |
textContext.font = curTextFont.css;
|
|
|
16262 |
|
|
|
16263 |
for (i = 0; i < linesCount; ++i) {
|
|
|
16264 |
width = Math.max(width, textContext.measureText(lines[i]).width);
|
|
|
16265 |
}
|
|
|
16266 |
return width | 0;
|
|
|
16267 |
};
|
|
|
16268 |
|
|
|
16269 |
// A lookup table for characters that can not be referenced by Object
|
|
|
16270 |
p.glyphLook = function(font, chr) {
|
|
|
16271 |
try {
|
|
|
16272 |
switch (chr) {
|
|
|
16273 |
case "1":
|
|
|
16274 |
return font.one;
|
|
|
16275 |
case "2":
|
|
|
16276 |
return font.two;
|
|
|
16277 |
case "3":
|
|
|
16278 |
return font.three;
|
|
|
16279 |
case "4":
|
|
|
16280 |
return font.four;
|
|
|
16281 |
case "5":
|
|
|
16282 |
return font.five;
|
|
|
16283 |
case "6":
|
|
|
16284 |
return font.six;
|
|
|
16285 |
case "7":
|
|
|
16286 |
return font.seven;
|
|
|
16287 |
case "8":
|
|
|
16288 |
return font.eight;
|
|
|
16289 |
case "9":
|
|
|
16290 |
return font.nine;
|
|
|
16291 |
case "0":
|
|
|
16292 |
return font.zero;
|
|
|
16293 |
case " ":
|
|
|
16294 |
return font.space;
|
|
|
16295 |
case "$":
|
|
|
16296 |
return font.dollar;
|
|
|
16297 |
case "!":
|
|
|
16298 |
return font.exclam;
|
|
|
16299 |
case '"':
|
|
|
16300 |
return font.quotedbl;
|
|
|
16301 |
case "#":
|
|
|
16302 |
return font.numbersign;
|
|
|
16303 |
case "%":
|
|
|
16304 |
return font.percent;
|
|
|
16305 |
case "&":
|
|
|
16306 |
return font.ampersand;
|
|
|
16307 |
case "'":
|
|
|
16308 |
return font.quotesingle;
|
|
|
16309 |
case "(":
|
|
|
16310 |
return font.parenleft;
|
|
|
16311 |
case ")":
|
|
|
16312 |
return font.parenright;
|
|
|
16313 |
case "*":
|
|
|
16314 |
return font.asterisk;
|
|
|
16315 |
case "+":
|
|
|
16316 |
return font.plus;
|
|
|
16317 |
case ",":
|
|
|
16318 |
return font.comma;
|
|
|
16319 |
case "-":
|
|
|
16320 |
return font.hyphen;
|
|
|
16321 |
case ".":
|
|
|
16322 |
return font.period;
|
|
|
16323 |
case "/":
|
|
|
16324 |
return font.slash;
|
|
|
16325 |
case "_":
|
|
|
16326 |
return font.underscore;
|
|
|
16327 |
case ":":
|
|
|
16328 |
return font.colon;
|
|
|
16329 |
case ";":
|
|
|
16330 |
return font.semicolon;
|
|
|
16331 |
case "<":
|
|
|
16332 |
return font.less;
|
|
|
16333 |
case "=":
|
|
|
16334 |
return font.equal;
|
|
|
16335 |
case ">":
|
|
|
16336 |
return font.greater;
|
|
|
16337 |
case "?":
|
|
|
16338 |
return font.question;
|
|
|
16339 |
case "@":
|
|
|
16340 |
return font.at;
|
|
|
16341 |
case "[":
|
|
|
16342 |
return font.bracketleft;
|
|
|
16343 |
case "\\":
|
|
|
16344 |
return font.backslash;
|
|
|
16345 |
case "]":
|
|
|
16346 |
return font.bracketright;
|
|
|
16347 |
case "^":
|
|
|
16348 |
return font.asciicircum;
|
|
|
16349 |
case "`":
|
|
|
16350 |
return font.grave;
|
|
|
16351 |
case "{":
|
|
|
16352 |
return font.braceleft;
|
|
|
16353 |
case "|":
|
|
|
16354 |
return font.bar;
|
|
|
16355 |
case "}":
|
|
|
16356 |
return font.braceright;
|
|
|
16357 |
case "~":
|
|
|
16358 |
return font.asciitilde;
|
|
|
16359 |
// If the character is not 'special', access it by object reference
|
|
|
16360 |
default:
|
|
|
16361 |
return font[chr];
|
|
|
16362 |
}
|
|
|
16363 |
} catch(e) {
|
|
|
16364 |
Processing.debug(e);
|
|
|
16365 |
}
|
|
|
16366 |
};
|
|
|
16367 |
|
|
|
16368 |
// Print some text to the Canvas
|
|
|
16369 |
Drawing2D.prototype.text$line = function(str, x, y, z, align) {
|
|
|
16370 |
var textWidth = 0, xOffset = 0;
|
|
|
16371 |
// If the font is a standard Canvas font...
|
|
|
16372 |
if (!curTextFont.glyph) {
|
|
|
16373 |
if (str && ("fillText" in curContext)) {
|
|
|
16374 |
if (isFillDirty) {
|
|
|
16375 |
curContext.fillStyle = p.color.toString(currentFillColor);
|
|
|
16376 |
isFillDirty = false;
|
|
|
16377 |
}
|
|
|
16378 |
|
|
|
16379 |
// horizontal offset/alignment
|
|
|
16380 |
if(align === PConstants.RIGHT || align === PConstants.CENTER) {
|
|
|
16381 |
textWidth = curTextFont.measureTextWidth(str);
|
|
|
16382 |
|
|
|
16383 |
if(align === PConstants.RIGHT) {
|
|
|
16384 |
xOffset = -textWidth;
|
|
|
16385 |
} else { // if(align === PConstants.CENTER)
|
|
|
16386 |
xOffset = -textWidth/2;
|
|
|
16387 |
}
|
|
|
16388 |
}
|
|
|
16389 |
|
|
|
16390 |
curContext.fillText(str, x+xOffset, y);
|
|
|
16391 |
}
|
|
|
16392 |
} else {
|
|
|
16393 |
// If the font is a Batik SVG font...
|
|
|
16394 |
var font = p.glyphTable[curFontName];
|
|
|
16395 |
saveContext();
|
|
|
16396 |
curContext.translate(x, y + curTextSize);
|
|
|
16397 |
|
|
|
16398 |
// horizontal offset/alignment
|
|
|
16399 |
if(align === PConstants.RIGHT || align === PConstants.CENTER) {
|
|
|
16400 |
textWidth = font.width(str);
|
|
|
16401 |
|
|
|
16402 |
if(align === PConstants.RIGHT) {
|
|
|
16403 |
xOffset = -textWidth;
|
|
|
16404 |
} else { // if(align === PConstants.CENTER)
|
|
|
16405 |
xOffset = -textWidth/2;
|
|
|
16406 |
}
|
|
|
16407 |
}
|
|
|
16408 |
|
|
|
16409 |
var upem = font.units_per_em,
|
|
|
16410 |
newScale = 1 / upem * curTextSize;
|
|
|
16411 |
|
|
|
16412 |
curContext.scale(newScale, newScale);
|
|
|
16413 |
|
|
|
16414 |
for (var i=0, len=str.length; i < len; i++) {
|
|
|
16415 |
// Test character against glyph table
|
|
|
16416 |
try {
|
|
|
16417 |
p.glyphLook(font, str[i]).draw();
|
|
|
16418 |
} catch(e) {
|
|
|
16419 |
Processing.debug(e);
|
|
|
16420 |
}
|
|
|
16421 |
}
|
|
|
16422 |
restoreContext();
|
|
|
16423 |
}
|
|
|
16424 |
};
|
|
|
16425 |
|
|
|
16426 |
Drawing3D.prototype.text$line = function(str, x, y, z, align) {
|
|
|
16427 |
// handle case for 3d text
|
|
|
16428 |
if (textcanvas === undef) {
|
|
|
16429 |
textcanvas = document.createElement("canvas");
|
|
|
16430 |
}
|
|
|
16431 |
var oldContext = curContext;
|
|
|
16432 |
curContext = textcanvas.getContext("2d");
|
|
|
16433 |
curContext.font = curTextFont.css;
|
|
|
16434 |
var textWidth = curTextFont.measureTextWidth(str);
|
|
|
16435 |
textcanvas.width = textWidth;
|
|
|
16436 |
textcanvas.height = curTextSize;
|
|
|
16437 |
curContext = textcanvas.getContext("2d"); // refreshes curContext
|
|
|
16438 |
curContext.font = curTextFont.css;
|
|
|
16439 |
curContext.textBaseline="top";
|
|
|
16440 |
|
|
|
16441 |
// paint on 2D canvas
|
|
|
16442 |
Drawing2D.prototype.text$line(str,0,0,0,PConstants.LEFT);
|
|
|
16443 |
|
|
|
16444 |
// use it as a texture
|
|
|
16445 |
var aspect = textcanvas.width/textcanvas.height;
|
|
|
16446 |
curContext = oldContext;
|
|
|
16447 |
|
|
|
16448 |
curContext.bindTexture(curContext.TEXTURE_2D, textTex);
|
|
|
16449 |
curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, textcanvas);
|
|
|
16450 |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
|
|
|
16451 |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR);
|
|
|
16452 |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE);
|
|
|
16453 |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE);
|
|
|
16454 |
// If we don't have a power of two texture, we can't mipmap it.
|
|
|
16455 |
// curContext.generateMipmap(curContext.TEXTURE_2D);
|
|
|
16456 |
|
|
|
16457 |
// horizontal offset/alignment
|
|
|
16458 |
var xOffset = 0;
|
|
|
16459 |
if (align === PConstants.RIGHT) {
|
|
|
16460 |
xOffset = -textWidth;
|
|
|
16461 |
} else if(align === PConstants.CENTER) {
|
|
|
16462 |
xOffset = -textWidth/2;
|
|
|
16463 |
}
|
|
|
16464 |
var model = new PMatrix3D();
|
|
|
16465 |
var scalefactor = curTextSize * 0.5;
|
|
|
16466 |
model.translate(x+xOffset-scalefactor/2, y-scalefactor, z);
|
|
|
16467 |
model.scale(-aspect*scalefactor, -scalefactor, scalefactor);
|
|
|
16468 |
model.translate(-1, -1, -1);
|
|
|
16469 |
model.transpose();
|
|
|
16470 |
|
|
|
16471 |
var view = new PMatrix3D();
|
|
|
16472 |
view.scale(1, -1, 1);
|
|
|
16473 |
view.apply(modelView.array());
|
|
|
16474 |
view.transpose();
|
|
|
16475 |
|
|
|
16476 |
curContext.useProgram(programObject2D);
|
|
|
16477 |
vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, textBuffer);
|
|
|
16478 |
vertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord", 2, textureBuffer);
|
|
|
16479 |
uniformi("uSampler2d", programObject2D, "uSampler", [0]);
|
|
|
16480 |
uniformi("picktype2d", programObject2D, "picktype", 1);
|
|
|
16481 |
uniformMatrix("model2d", programObject2D, "model", false, model.array());
|
|
|
16482 |
uniformMatrix("view2d", programObject2D, "view", false, view.array());
|
|
|
16483 |
uniformf("color2d", programObject2D, "color", fillStyle);
|
|
|
16484 |
curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
|
|
16485 |
curContext.drawElements(curContext.TRIANGLES, 6, curContext.UNSIGNED_SHORT, 0);
|
|
|
16486 |
};
|
|
|
16487 |
|
|
|
16488 |
|
|
|
16489 |
/**
|
|
|
16490 |
* unbounded text function (z is an optional argument)
|
|
|
16491 |
*/
|
|
|
16492 |
function text$4(str, x, y, z) {
|
|
|
16493 |
var lines, linesCount;
|
|
|
16494 |
if(str.indexOf('\n') < 0) {
|
|
|
16495 |
lines = [str];
|
|
|
16496 |
linesCount = 1;
|
|
|
16497 |
} else {
|
|
|
16498 |
lines = str.split(/\r?\n/g);
|
|
|
16499 |
linesCount = lines.length;
|
|
|
16500 |
}
|
|
|
16501 |
// handle text line-by-line
|
|
|
16502 |
|
|
|
16503 |
var yOffset = 0;
|
|
|
16504 |
if(verticalTextAlignment === PConstants.TOP) {
|
|
|
16505 |
yOffset = curTextAscent + curTextDescent;
|
|
|
16506 |
} else if(verticalTextAlignment === PConstants.CENTER) {
|
|
|
16507 |
yOffset = curTextAscent/2 - (linesCount-1)*curTextLeading/2;
|
|
|
16508 |
} else if(verticalTextAlignment === PConstants.BOTTOM) {
|
|
|
16509 |
yOffset = -(curTextDescent + (linesCount-1)*curTextLeading);
|
|
|
16510 |
}
|
|
|
16511 |
|
|
|
16512 |
for(var i=0;i<linesCount;++i) {
|
|
|
16513 |
var line = lines[i];
|
|
|
16514 |
drawing.text$line(line, x, y + yOffset, z, horizontalTextAlignment);
|
|
|
16515 |
yOffset += curTextLeading;
|
|
|
16516 |
}
|
|
|
16517 |
}
|
|
|
16518 |
|
|
|
16519 |
|
|
|
16520 |
/**
|
|
|
16521 |
* box-bounded text function (z is an optional argument)
|
|
|
16522 |
*/
|
|
|
16523 |
function text$6(str, x, y, width, height, z) {
|
|
|
16524 |
// 'fail' on 0-valued dimensions
|
|
|
16525 |
if (str.length === 0 || width === 0 || height === 0) {
|
|
|
16526 |
return;
|
|
|
16527 |
}
|
|
|
16528 |
// also 'fail' if the text height is larger than the bounding height
|
|
|
16529 |
if(curTextSize > height) {
|
|
|
16530 |
return;
|
|
|
16531 |
}
|
|
|
16532 |
|
|
|
16533 |
var spaceMark = -1;
|
|
|
16534 |
var start = 0;
|
|
|
16535 |
var lineWidth = 0;
|
|
|
16536 |
var drawCommands = [];
|
|
|
16537 |
|
|
|
16538 |
// run through text, character-by-character
|
|
|
16539 |
for (var charPos=0, len=str.length; charPos < len; charPos++)
|
|
|
16540 |
{
|
|
|
16541 |
var currentChar = str[charPos];
|
|
|
16542 |
var spaceChar = (currentChar === " ");
|
|
|
16543 |
var letterWidth = curTextFont.measureTextWidth(currentChar);
|
|
|
16544 |
|
|
|
16545 |
// if we aren't looking at a newline, and the text still fits, keep processing
|
|
|
16546 |
if (currentChar !== "\n" && (lineWidth + letterWidth <= width)) {
|
|
|
16547 |
if (spaceChar) { spaceMark = charPos; }
|
|
|
16548 |
lineWidth += letterWidth;
|
|
|
16549 |
}
|
|
|
16550 |
|
|
|
16551 |
// if we're looking at a newline, or the text no longer fits, push the section that fit into the drawcommand list
|
|
|
16552 |
else
|
|
|
16553 |
{
|
|
|
16554 |
if (spaceMark + 1 === start) {
|
|
|
16555 |
if(charPos>0) {
|
|
|
16556 |
// Whole line without spaces so far.
|
|
|
16557 |
spaceMark = charPos;
|
|
|
16558 |
} else {
|
|
|
16559 |
// 'fail', because the line can't even fit the first character
|
|
|
16560 |
return;
|
|
|
16561 |
}
|
|
|
16562 |
}
|
|
|
16563 |
|
|
|
16564 |
if (currentChar === "\n") {
|
|
|
16565 |
drawCommands.push({text:str.substring(start, charPos), width: lineWidth});
|
|
|
16566 |
start = charPos + 1;
|
|
|
16567 |
} else {
|
|
|
16568 |
// current is not a newline, which means the line doesn't fit in box. push text.
|
|
|
16569 |
// In Processing 1.5.1, the space is also pushed, so we push up to spaceMark+1,
|
|
|
16570 |
// rather than up to spaceMark, as was the case for Processing 1.5 and earlier.
|
|
|
16571 |
drawCommands.push({text:str.substring(start, spaceMark+1), width: lineWidth});
|
|
|
16572 |
start = spaceMark + 1;
|
|
|
16573 |
}
|
|
|
16574 |
|
|
|
16575 |
// newline + return
|
|
|
16576 |
lineWidth = 0;
|
|
|
16577 |
charPos = start - 1;
|
|
|
16578 |
}
|
|
|
16579 |
}
|
|
|
16580 |
|
|
|
16581 |
// push the remaining text
|
|
|
16582 |
if (start < len) {
|
|
|
16583 |
drawCommands.push({text:str.substring(start), width: lineWidth});
|
|
|
16584 |
}
|
|
|
16585 |
|
|
|
16586 |
// resolve horizontal alignment
|
|
|
16587 |
var xOffset = 1,
|
|
|
16588 |
yOffset = curTextAscent;
|
|
|
16589 |
if (horizontalTextAlignment === PConstants.CENTER) {
|
|
|
16590 |
xOffset = width/2;
|
|
|
16591 |
} else if (horizontalTextAlignment === PConstants.RIGHT) {
|
|
|
16592 |
xOffset = width;
|
|
|
16593 |
}
|
|
|
16594 |
|
|
|
16595 |
// resolve vertical alignment
|
|
|
16596 |
var linesCount = drawCommands.length,
|
|
|
16597 |
visibleLines = Math.min(linesCount, Math.floor(height/curTextLeading));
|
|
|
16598 |
if(verticalTextAlignment === PConstants.TOP) {
|
|
|
16599 |
yOffset = curTextAscent + curTextDescent;
|
|
|
16600 |
} else if(verticalTextAlignment === PConstants.CENTER) {
|
|
|
16601 |
yOffset = (height/2) - curTextLeading * (visibleLines/2 - 1);
|
|
|
16602 |
} else if(verticalTextAlignment === PConstants.BOTTOM) {
|
|
|
16603 |
yOffset = curTextDescent + curTextLeading;
|
|
|
16604 |
}
|
|
|
16605 |
|
|
|
16606 |
var command,
|
|
|
16607 |
drawCommand,
|
|
|
16608 |
leading;
|
|
|
16609 |
for (command = 0; command < linesCount; command++) {
|
|
|
16610 |
leading = command * curTextLeading;
|
|
|
16611 |
// stop if not enough space for one more line draw
|
|
|
16612 |
if (yOffset + leading > height - curTextDescent) {
|
|
|
16613 |
break;
|
|
|
16614 |
}
|
|
|
16615 |
drawCommand = drawCommands[command];
|
|
|
16616 |
drawing.text$line(drawCommand.text, x + xOffset, y + yOffset + leading, z, horizontalTextAlignment);
|
|
|
16617 |
}
|
|
|
16618 |
}
|
|
|
16619 |
|
|
|
16620 |
/**
|
|
|
16621 |
* text() Draws text to the screen.
|
|
|
16622 |
*
|
|
|
16623 |
* @param {String|char|int|float} data the alphanumeric symbols to be displayed
|
|
|
16624 |
* @param {int|float} x x-coordinate of text
|
|
|
16625 |
* @param {int|float} y y-coordinate of text
|
|
|
16626 |
* @param {int|float} z optional z-coordinate of text
|
|
|
16627 |
* @param {String} stringdata optional letters to be displayed
|
|
|
16628 |
* @param {int|float} width optional width of text box
|
|
|
16629 |
* @param {int|float} height optional height of text box
|
|
|
16630 |
*
|
|
|
16631 |
* @see #textAlign
|
|
|
16632 |
* @see #textMode
|
|
|
16633 |
* @see #loadFont
|
|
|
16634 |
* @see #PFont
|
|
|
16635 |
* @see #textFont
|
|
|
16636 |
*/
|
|
|
16637 |
p.text = function() {
|
|
|
16638 |
if (textMode === PConstants.SHAPE) {
|
|
|
16639 |
// TODO: requires beginRaw function
|
|
|
16640 |
return;
|
|
|
16641 |
}
|
|
|
16642 |
if (arguments.length === 3) { // for text( str, x, y)
|
|
|
16643 |
text$4(toP5String(arguments[0]), arguments[1], arguments[2], 0);
|
|
|
16644 |
} else if (arguments.length === 4) { // for text( str, x, y, z)
|
|
|
16645 |
text$4(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3]);
|
|
|
16646 |
} else if (arguments.length === 5) { // for text( str, x, y , width, height)
|
|
|
16647 |
text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], 0);
|
|
|
16648 |
} else if (arguments.length === 6) { // for text( stringdata, x, y , width, height, z)
|
|
|
16649 |
text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
|
|
|
16650 |
}
|
|
|
16651 |
};
|
|
|
16652 |
|
|
|
16653 |
/**
|
|
|
16654 |
* Sets the way text draws to the screen. In the default configuration (the MODEL mode), it's possible to rotate,
|
|
|
16655 |
* scale, and place letters in two and three dimensional space. <br /><br /> Changing to SCREEN mode draws letters
|
|
|
16656 |
* directly to the front of the window and greatly increases rendering quality and speed when used with the P2D and
|
|
|
16657 |
* P3D renderers. textMode(SCREEN) with OPENGL and JAVA2D (the default) renderers will generally be slower, though
|
|
|
16658 |
* pixel accurate with P2D and P3D. With textMode(SCREEN), the letters draw at the actual size of the font (in pixels)
|
|
|
16659 |
* and therefore calls to <b>textSize()</b> will not affect the size of the letters. To create a font at the size you
|
|
|
16660 |
* desire, use the "Create font..." option in the Tools menu, or use the createFont() function. When using textMode(SCREEN),
|
|
|
16661 |
* any z-coordinate passed to a text() command will be ignored, because your computer screen is...flat!
|
|
|
16662 |
*
|
|
|
16663 |
* @param {int} MODE Either MODEL, SCREEN or SHAPE (not yet supported)
|
|
|
16664 |
*
|
|
|
16665 |
* @see loadFont
|
|
|
16666 |
* @see PFont
|
|
|
16667 |
* @see text
|
|
|
16668 |
* @see textFont
|
|
|
16669 |
* @see createFont
|
|
|
16670 |
*/
|
|
|
16671 |
p.textMode = function(mode){
|
|
|
16672 |
textMode = mode;
|
|
|
16673 |
};
|
|
|
16674 |
|
|
|
16675 |
// Load Batik SVG Fonts and parse to pre-def objects for quick rendering
|
|
|
16676 |
p.loadGlyphs = function(url) {
|
|
|
16677 |
var x, y, cx, cy, nx, ny, d, a, lastCom, lenC, horiz_adv_x, getXY = '[0-9\\-]+', path;
|
|
|
16678 |
|
|
|
16679 |
// Return arrays of SVG commands and coords
|
|
|
16680 |
// get this to use p.matchAll() - will need to work around the lack of null return
|
|
|
16681 |
var regex = function(needle, hay) {
|
|
|
16682 |
var i = 0,
|
|
|
16683 |
results = [],
|
|
|
16684 |
latest, regexp = new RegExp(needle, "g");
|
|
|
16685 |
latest = results[i] = regexp.exec(hay);
|
|
|
16686 |
while (latest) {
|
|
|
16687 |
i++;
|
|
|
16688 |
latest = results[i] = regexp.exec(hay);
|
|
|
16689 |
}
|
|
|
16690 |
return results;
|
|
|
16691 |
};
|
|
|
16692 |
|
|
|
16693 |
var buildPath = function(d) {
|
|
|
16694 |
var c = regex("[A-Za-z][0-9\\- ]+|Z", d);
|
|
|
16695 |
var beforePathDraw = function() {
|
|
|
16696 |
saveContext();
|
|
|
16697 |
return drawing.$ensureContext();
|
|
|
16698 |
};
|
|
|
16699 |
var afterPathDraw = function() {
|
|
|
16700 |
executeContextFill();
|
|
|
16701 |
executeContextStroke();
|
|
|
16702 |
restoreContext();
|
|
|
16703 |
};
|
|
|
16704 |
|
|
|
16705 |
// Begin storing path object
|
|
|
16706 |
path = "return {draw:function(){var curContext=beforePathDraw();curContext.beginPath();";
|
|
|
16707 |
|
|
|
16708 |
x = 0;
|
|
|
16709 |
y = 0;
|
|
|
16710 |
cx = 0;
|
|
|
16711 |
cy = 0;
|
|
|
16712 |
nx = 0;
|
|
|
16713 |
ny = 0;
|
|
|
16714 |
d = 0;
|
|
|
16715 |
a = 0;
|
|
|
16716 |
lastCom = "";
|
|
|
16717 |
lenC = c.length - 1;
|
|
|
16718 |
|
|
|
16719 |
// Loop through SVG commands translating to canvas eqivs functions in path object
|
|
|
16720 |
for (var j = 0; j < lenC; j++) {
|
|
|
16721 |
var com = c[j][0], xy = regex(getXY, com);
|
|
|
16722 |
|
|
|
16723 |
switch (com[0]) {
|
|
|
16724 |
case "M":
|
|
|
16725 |
//curContext.moveTo(x,-y);
|
|
|
16726 |
x = parseFloat(xy[0][0]);
|
|
|
16727 |
y = parseFloat(xy[1][0]);
|
|
|
16728 |
path += "curContext.moveTo(" + x + "," + (-y) + ");";
|
|
|
16729 |
break;
|
|
|
16730 |
|
|
|
16731 |
case "L":
|
|
|
16732 |
//curContext.lineTo(x,-y);
|
|
|
16733 |
x = parseFloat(xy[0][0]);
|
|
|
16734 |
y = parseFloat(xy[1][0]);
|
|
|
16735 |
path += "curContext.lineTo(" + x + "," + (-y) + ");";
|
|
|
16736 |
break;
|
|
|
16737 |
|
|
|
16738 |
case "H":
|
|
|
16739 |
//curContext.lineTo(x,-y)
|
|
|
16740 |
x = parseFloat(xy[0][0]);
|
|
|
16741 |
path += "curContext.lineTo(" + x + "," + (-y) + ");";
|
|
|
16742 |
break;
|
|
|
16743 |
|
|
|
16744 |
case "V":
|
|
|
16745 |
//curContext.lineTo(x,-y);
|
|
|
16746 |
y = parseFloat(xy[0][0]);
|
|
|
16747 |
path += "curContext.lineTo(" + x + "," + (-y) + ");";
|
|
|
16748 |
break;
|
|
|
16749 |
|
|
|
16750 |
case "T":
|
|
|
16751 |
//curContext.quadraticCurveTo(cx,-cy,nx,-ny);
|
|
|
16752 |
nx = parseFloat(xy[0][0]);
|
|
|
16753 |
ny = parseFloat(xy[1][0]);
|
|
|
16754 |
|
|
|
16755 |
if (lastCom === "Q" || lastCom === "T") {
|
|
|
16756 |
d = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(cy - y, 2));
|
|
|
16757 |
a = Math.PI + Math.atan2(cx - x, cy - y);
|
|
|
16758 |
cx = x + (Math.sin(a) * (d));
|
|
|
16759 |
cy = y + (Math.cos(a) * (d));
|
|
|
16760 |
} else {
|
|
|
16761 |
cx = x;
|
|
|
16762 |
cy = y;
|
|
|
16763 |
}
|
|
|
16764 |
|
|
|
16765 |
path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
|
|
|
16766 |
x = nx;
|
|
|
16767 |
y = ny;
|
|
|
16768 |
break;
|
|
|
16769 |
|
|
|
16770 |
case "Q":
|
|
|
16771 |
//curContext.quadraticCurveTo(cx,-cy,nx,-ny);
|
|
|
16772 |
cx = parseFloat(xy[0][0]);
|
|
|
16773 |
cy = parseFloat(xy[1][0]);
|
|
|
16774 |
nx = parseFloat(xy[2][0]);
|
|
|
16775 |
ny = parseFloat(xy[3][0]);
|
|
|
16776 |
path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
|
|
|
16777 |
x = nx;
|
|
|
16778 |
y = ny;
|
|
|
16779 |
break;
|
|
|
16780 |
|
|
|
16781 |
case "Z":
|
|
|
16782 |
//curContext.closePath();
|
|
|
16783 |
path += "curContext.closePath();";
|
|
|
16784 |
break;
|
|
|
16785 |
}
|
|
|
16786 |
lastCom = com[0];
|
|
|
16787 |
}
|
|
|
16788 |
|
|
|
16789 |
path += "afterPathDraw();";
|
|
|
16790 |
path += "curContext.translate(" + horiz_adv_x + ",0);";
|
|
|
16791 |
path += "}}";
|
|
|
16792 |
|
|
|
16793 |
return ((new Function("beforePathDraw", "afterPathDraw", path))(beforePathDraw, afterPathDraw));
|
|
|
16794 |
};
|
|
|
16795 |
|
|
|
16796 |
// Parse SVG font-file into block of Canvas commands
|
|
|
16797 |
var parseSVGFont = function(svg) {
|
|
|
16798 |
// Store font attributes
|
|
|
16799 |
var font = svg.getElementsByTagName("font");
|
|
|
16800 |
p.glyphTable[url].horiz_adv_x = font[0].getAttribute("horiz-adv-x");
|
|
|
16801 |
|
|
|
16802 |
var font_face = svg.getElementsByTagName("font-face")[0];
|
|
|
16803 |
p.glyphTable[url].units_per_em = parseFloat(font_face.getAttribute("units-per-em"));
|
|
|
16804 |
p.glyphTable[url].ascent = parseFloat(font_face.getAttribute("ascent"));
|
|
|
16805 |
p.glyphTable[url].descent = parseFloat(font_face.getAttribute("descent"));
|
|
|
16806 |
|
|
|
16807 |
var glyph = svg.getElementsByTagName("glyph"),
|
|
|
16808 |
len = glyph.length;
|
|
|
16809 |
|
|
|
16810 |
// Loop through each glyph in the SVG
|
|
|
16811 |
for (var i = 0; i < len; i++) {
|
|
|
16812 |
// Store attributes for this glyph
|
|
|
16813 |
var unicode = glyph[i].getAttribute("unicode");
|
|
|
16814 |
var name = glyph[i].getAttribute("glyph-name");
|
|
|
16815 |
horiz_adv_x = glyph[i].getAttribute("horiz-adv-x");
|
|
|
16816 |
if (horiz_adv_x === null) {
|
|
|
16817 |
horiz_adv_x = p.glyphTable[url].horiz_adv_x;
|
|
|
16818 |
}
|
|
|
16819 |
d = glyph[i].getAttribute("d");
|
|
|
16820 |
// Split path commands in glpyh
|
|
|
16821 |
if (d !== undef) {
|
|
|
16822 |
path = buildPath(d);
|
|
|
16823 |
// Store glyph data to table object
|
|
|
16824 |
p.glyphTable[url][name] = {
|
|
|
16825 |
name: name,
|
|
|
16826 |
unicode: unicode,
|
|
|
16827 |
horiz_adv_x: horiz_adv_x,
|
|
|
16828 |
draw: path.draw
|
|
|
16829 |
};
|
|
|
16830 |
}
|
|
|
16831 |
} // finished adding glyphs to table
|
|
|
16832 |
};
|
|
|
16833 |
|
|
|
16834 |
// Load and parse Batik SVG font as XML into a Processing Glyph object
|
|
|
16835 |
var loadXML = function() {
|
|
|
16836 |
var xmlDoc;
|
|
|
16837 |
|
|
|
16838 |
try {
|
|
|
16839 |
xmlDoc = document.implementation.createDocument("", "", null);
|
|
|
16840 |
}
|
|
|
16841 |
catch(e_fx_op) {
|
|
|
16842 |
Processing.debug(e_fx_op.message);
|
|
|
16843 |
return;
|
|
|
16844 |
}
|
|
|
16845 |
|
|
|
16846 |
try {
|
|
|
16847 |
xmlDoc.async = false;
|
|
|
16848 |
xmlDoc.load(url);
|
|
|
16849 |
parseSVGFont(xmlDoc.getElementsByTagName("svg")[0]);
|
|
|
16850 |
}
|
|
|
16851 |
catch(e_sf_ch) {
|
|
|
16852 |
// Google Chrome, Safari etc.
|
|
|
16853 |
Processing.debug(e_sf_ch);
|
|
|
16854 |
try {
|
|
|
16855 |
var xmlhttp = new window.XMLHttpRequest();
|
|
|
16856 |
xmlhttp.open("GET", url, false);
|
|
|
16857 |
xmlhttp.send(null);
|
|
|
16858 |
parseSVGFont(xmlhttp.responseXML.documentElement);
|
|
|
16859 |
}
|
|
|
16860 |
catch(e) {
|
|
|
16861 |
Processing.debug(e_sf_ch);
|
|
|
16862 |
}
|
|
|
16863 |
}
|
|
|
16864 |
};
|
|
|
16865 |
|
|
|
16866 |
// Create a new object in glyphTable to store this font
|
|
|
16867 |
p.glyphTable[url] = {};
|
|
|
16868 |
|
|
|
16869 |
// Begin loading the Batik SVG font...
|
|
|
16870 |
loadXML(url);
|
|
|
16871 |
|
|
|
16872 |
// Return the loaded font for attribute grabbing
|
|
|
16873 |
return p.glyphTable[url];
|
|
|
16874 |
};
|
|
|
16875 |
|
|
|
16876 |
/**
|
|
|
16877 |
* Gets the sketch parameter value. The parameter can be defined as the canvas attribute with
|
|
|
16878 |
* the "data-processing-" prefix or provided in the pjs directive (e.g. param-test="52").
|
|
|
16879 |
* The function tries the canvas attributes, then the pjs directive content.
|
|
|
16880 |
*
|
|
|
16881 |
* @param {String} name The name of the param to read.
|
|
|
16882 |
*
|
|
|
16883 |
* @returns {String} The parameter value, or null if parameter is not defined.
|
|
|
16884 |
*/
|
|
|
16885 |
p.param = function(name) {
|
|
|
16886 |
// trying attribute that was specified in CANVAS
|
|
|
16887 |
var attributeName = "data-processing-" + name;
|
|
|
16888 |
if (curElement.hasAttribute(attributeName)) {
|
|
|
16889 |
return curElement.getAttribute(attributeName);
|
|
|
16890 |
}
|
|
|
16891 |
// trying child PARAM elements of the CANVAS
|
|
|
16892 |
for (var i = 0, len = curElement.childNodes.length; i < len; ++i) {
|
|
|
16893 |
var item = curElement.childNodes.item(i);
|
|
|
16894 |
if (item.nodeType !== 1 || item.tagName.toLowerCase() !== "param") {
|
|
|
16895 |
continue;
|
|
|
16896 |
}
|
|
|
16897 |
if (item.getAttribute("name") === name) {
|
|
|
16898 |
return item.getAttribute("value");
|
|
|
16899 |
}
|
|
|
16900 |
}
|
|
|
16901 |
// fallback to default params
|
|
|
16902 |
if (curSketch.params.hasOwnProperty(name)) {
|
|
|
16903 |
return curSketch.params[name];
|
|
|
16904 |
}
|
|
|
16905 |
return null;
|
|
|
16906 |
};
|
|
|
16907 |
|
|
|
16908 |
////////////////////////////////////////////////////////////////////////////
|
|
|
16909 |
// 2D/3D methods wiring utils
|
|
|
16910 |
////////////////////////////////////////////////////////////////////////////
|
|
|
16911 |
function wireDimensionalFunctions(mode) {
|
|
|
16912 |
// Drawing2D/Drawing3D
|
|
|
16913 |
if (mode === '3D') {
|
|
|
16914 |
drawing = new Drawing3D();
|
|
|
16915 |
} else if (mode === '2D') {
|
|
|
16916 |
drawing = new Drawing2D();
|
|
|
16917 |
} else {
|
|
|
16918 |
drawing = new DrawingPre();
|
|
|
16919 |
}
|
|
|
16920 |
|
|
|
16921 |
// Wire up functions (Use DrawingPre properties names)
|
|
|
16922 |
for (var i in DrawingPre.prototype) {
|
|
|
16923 |
if (DrawingPre.prototype.hasOwnProperty(i) && i.indexOf("$") < 0) {
|
|
|
16924 |
p[i] = drawing[i];
|
|
|
16925 |
}
|
|
|
16926 |
}
|
|
|
16927 |
|
|
|
16928 |
// Run initialization
|
|
|
16929 |
drawing.$init();
|
|
|
16930 |
}
|
|
|
16931 |
|
|
|
16932 |
function createDrawingPreFunction(name) {
|
|
|
16933 |
return function() {
|
|
|
16934 |
wireDimensionalFunctions("2D");
|
|
|
16935 |
return drawing[name].apply(this, arguments);
|
|
|
16936 |
};
|
|
|
16937 |
}
|
|
|
16938 |
DrawingPre.prototype.translate = createDrawingPreFunction("translate");
|
|
|
16939 |
DrawingPre.prototype.scale = createDrawingPreFunction("scale");
|
|
|
16940 |
DrawingPre.prototype.pushMatrix = createDrawingPreFunction("pushMatrix");
|
|
|
16941 |
DrawingPre.prototype.popMatrix = createDrawingPreFunction("popMatrix");
|
|
|
16942 |
DrawingPre.prototype.resetMatrix = createDrawingPreFunction("resetMatrix");
|
|
|
16943 |
DrawingPre.prototype.applyMatrix = createDrawingPreFunction("applyMatrix");
|
|
|
16944 |
DrawingPre.prototype.rotate = createDrawingPreFunction("rotate");
|
|
|
16945 |
DrawingPre.prototype.rotateZ = createDrawingPreFunction("rotateZ");
|
|
|
16946 |
DrawingPre.prototype.redraw = createDrawingPreFunction("redraw");
|
|
|
16947 |
DrawingPre.prototype.toImageData = createDrawingPreFunction("toImageData");
|
|
|
16948 |
DrawingPre.prototype.ambientLight = createDrawingPreFunction("ambientLight");
|
|
|
16949 |
DrawingPre.prototype.directionalLight = createDrawingPreFunction("directionalLight");
|
|
|
16950 |
DrawingPre.prototype.lightFalloff = createDrawingPreFunction("lightFalloff");
|
|
|
16951 |
DrawingPre.prototype.lightSpecular = createDrawingPreFunction("lightSpecular");
|
|
|
16952 |
DrawingPre.prototype.pointLight = createDrawingPreFunction("pointLight");
|
|
|
16953 |
DrawingPre.prototype.noLights = createDrawingPreFunction("noLights");
|
|
|
16954 |
DrawingPre.prototype.spotLight = createDrawingPreFunction("spotLight");
|
|
|
16955 |
DrawingPre.prototype.beginCamera = createDrawingPreFunction("beginCamera");
|
|
|
16956 |
DrawingPre.prototype.endCamera = createDrawingPreFunction("endCamera");
|
|
|
16957 |
DrawingPre.prototype.frustum = createDrawingPreFunction("frustum");
|
|
|
16958 |
DrawingPre.prototype.box = createDrawingPreFunction("box");
|
|
|
16959 |
DrawingPre.prototype.sphere = createDrawingPreFunction("sphere");
|
|
|
16960 |
DrawingPre.prototype.ambient = createDrawingPreFunction("ambient");
|
|
|
16961 |
DrawingPre.prototype.emissive = createDrawingPreFunction("emissive");
|
|
|
16962 |
DrawingPre.prototype.shininess = createDrawingPreFunction("shininess");
|
|
|
16963 |
DrawingPre.prototype.specular = createDrawingPreFunction("specular");
|
|
|
16964 |
DrawingPre.prototype.fill = createDrawingPreFunction("fill");
|
|
|
16965 |
DrawingPre.prototype.stroke = createDrawingPreFunction("stroke");
|
|
|
16966 |
DrawingPre.prototype.strokeWeight = createDrawingPreFunction("strokeWeight");
|
|
|
16967 |
DrawingPre.prototype.smooth = createDrawingPreFunction("smooth");
|
|
|
16968 |
DrawingPre.prototype.noSmooth = createDrawingPreFunction("noSmooth");
|
|
|
16969 |
DrawingPre.prototype.point = createDrawingPreFunction("point");
|
|
|
16970 |
DrawingPre.prototype.vertex = createDrawingPreFunction("vertex");
|
|
|
16971 |
DrawingPre.prototype.endShape = createDrawingPreFunction("endShape");
|
|
|
16972 |
DrawingPre.prototype.bezierVertex = createDrawingPreFunction("bezierVertex");
|
|
|
16973 |
DrawingPre.prototype.curveVertex = createDrawingPreFunction("curveVertex");
|
|
|
16974 |
DrawingPre.prototype.curve = createDrawingPreFunction("curve");
|
|
|
16975 |
DrawingPre.prototype.line = createDrawingPreFunction("line");
|
|
|
16976 |
DrawingPre.prototype.bezier = createDrawingPreFunction("bezier");
|
|
|
16977 |
DrawingPre.prototype.rect = createDrawingPreFunction("rect");
|
|
|
16978 |
DrawingPre.prototype.ellipse = createDrawingPreFunction("ellipse");
|
|
|
16979 |
DrawingPre.prototype.background = createDrawingPreFunction("background");
|
|
|
16980 |
DrawingPre.prototype.image = createDrawingPreFunction("image");
|
|
|
16981 |
DrawingPre.prototype.textWidth = createDrawingPreFunction("textWidth");
|
|
|
16982 |
DrawingPre.prototype.text$line = createDrawingPreFunction("text$line");
|
|
|
16983 |
DrawingPre.prototype.$ensureContext = createDrawingPreFunction("$ensureContext");
|
|
|
16984 |
DrawingPre.prototype.$newPMatrix = createDrawingPreFunction("$newPMatrix");
|
|
|
16985 |
|
|
|
16986 |
DrawingPre.prototype.size = function(aWidth, aHeight, aMode) {
|
|
|
16987 |
wireDimensionalFunctions(aMode === PConstants.WEBGL ? "3D" : "2D");
|
|
|
16988 |
p.size(aWidth, aHeight, aMode);
|
|
|
16989 |
};
|
|
|
16990 |
|
|
|
16991 |
DrawingPre.prototype.$init = nop;
|
|
|
16992 |
|
|
|
16993 |
Drawing2D.prototype.$init = function() {
|
|
|
16994 |
// Setup default 2d canvas context.
|
|
|
16995 |
// Moving this here removes the number of times we need to check the 3D variable
|
|
|
16996 |
p.size(p.width, p.height);
|
|
|
16997 |
|
|
|
16998 |
curContext.lineCap = 'round';
|
|
|
16999 |
|
|
|
17000 |
// Set default stroke and fill color
|
|
|
17001 |
p.noSmooth();
|
|
|
17002 |
p.disableContextMenu();
|
|
|
17003 |
};
|
|
|
17004 |
Drawing3D.prototype.$init = function() {
|
|
|
17005 |
// For ref/perf test compatibility until those are fixed
|
|
|
17006 |
p.use3DContext = true;
|
|
|
17007 |
};
|
|
|
17008 |
|
|
|
17009 |
DrawingShared.prototype.$ensureContext = function() {
|
|
|
17010 |
return curContext;
|
|
|
17011 |
};
|
|
|
17012 |
|
|
|
17013 |
//////////////////////////////////////////////////////////////////////////
|
|
|
17014 |
// Touch and Mouse event handling
|
|
|
17015 |
//////////////////////////////////////////////////////////////////////////
|
|
|
17016 |
|
|
|
17017 |
function calculateOffset(curElement, event) {
|
|
|
17018 |
var element = curElement,
|
|
|
17019 |
offsetX = 0,
|
|
|
17020 |
offsetY = 0;
|
|
|
17021 |
|
|
|
17022 |
p.pmouseX = p.mouseX;
|
|
|
17023 |
p.pmouseY = p.mouseY;
|
|
|
17024 |
|
|
|
17025 |
// Find element offset
|
|
|
17026 |
if (element.offsetParent) {
|
|
|
17027 |
do {
|
|
|
17028 |
offsetX += element.offsetLeft;
|
|
|
17029 |
offsetY += element.offsetTop;
|
|
|
17030 |
} while (!!(element = element.offsetParent));
|
|
|
17031 |
}
|
|
|
17032 |
|
|
|
17033 |
// Find Scroll offset
|
|
|
17034 |
element = curElement;
|
|
|
17035 |
do {
|
|
|
17036 |
offsetX -= element.scrollLeft || 0;
|
|
|
17037 |
offsetY -= element.scrollTop || 0;
|
|
|
17038 |
} while (!!(element = element.parentNode));
|
|
|
17039 |
|
|
|
17040 |
// Add padding and border style widths to offset
|
|
|
17041 |
offsetX += stylePaddingLeft;
|
|
|
17042 |
offsetY += stylePaddingTop;
|
|
|
17043 |
|
|
|
17044 |
offsetX += styleBorderLeft;
|
|
|
17045 |
offsetY += styleBorderTop;
|
|
|
17046 |
|
|
|
17047 |
// Take into account any scrolling done
|
|
|
17048 |
offsetX += window.pageXOffset;
|
|
|
17049 |
offsetY += window.pageYOffset;
|
|
|
17050 |
|
|
|
17051 |
return {'X':offsetX,'Y':offsetY};
|
|
|
17052 |
}
|
|
|
17053 |
|
|
|
17054 |
function updateMousePosition(curElement, event) {
|
|
|
17055 |
var offset = calculateOffset(curElement, event);
|
|
|
17056 |
|
|
|
17057 |
// Dropping support for IE clientX and clientY, switching to pageX and pageY so we don't have to calculate scroll offset.
|
|
|
17058 |
// Removed in ticket #184. See rev: 2f106d1c7017fed92d045ba918db47d28e5c16f4
|
|
|
17059 |
p.mouseX = event.pageX - offset.X;
|
|
|
17060 |
p.mouseY = event.pageY - offset.Y;
|
|
|
17061 |
}
|
|
|
17062 |
|
|
|
17063 |
// Return a TouchEvent with canvas-specific x/y co-ordinates
|
|
|
17064 |
function addTouchEventOffset(t) {
|
|
|
17065 |
var offset = calculateOffset(t.changedTouches[0].target, t.changedTouches[0]),
|
|
|
17066 |
i;
|
|
|
17067 |
|
|
|
17068 |
for (i = 0; i < t.touches.length; i++) {
|
|
|
17069 |
var touch = t.touches[i];
|
|
|
17070 |
touch.offsetX = touch.pageX - offset.X;
|
|
|
17071 |
touch.offsetY = touch.pageY - offset.Y;
|
|
|
17072 |
}
|
|
|
17073 |
for (i = 0; i < t.targetTouches.length; i++) {
|
|
|
17074 |
var targetTouch = t.targetTouches[i];
|
|
|
17075 |
targetTouch.offsetX = targetTouch.pageX - offset.X;
|
|
|
17076 |
targetTouch.offsetY = targetTouch.pageY - offset.Y;
|
|
|
17077 |
}
|
|
|
17078 |
for (i = 0; i < t.changedTouches.length; i++) {
|
|
|
17079 |
var changedTouch = t.changedTouches[i];
|
|
|
17080 |
changedTouch.offsetX = changedTouch.pageX - offset.X;
|
|
|
17081 |
changedTouch.offsetY = changedTouch.pageY - offset.Y;
|
|
|
17082 |
}
|
|
|
17083 |
|
|
|
17084 |
return t;
|
|
|
17085 |
}
|
|
|
17086 |
|
|
|
17087 |
attachEventHandler(curElement, "touchstart", function (t) {
|
|
|
17088 |
// Removes unwanted behaviour of the canvas when touching canvas
|
|
|
17089 |
curElement.setAttribute("style","-webkit-user-select: none");
|
|
|
17090 |
curElement.setAttribute("onclick","void(0)");
|
|
|
17091 |
curElement.setAttribute("style","-webkit-tap-highlight-color:rgba(0,0,0,0)");
|
|
|
17092 |
// Loop though eventHandlers and remove mouse listeners
|
|
|
17093 |
for (var i=0, ehl=eventHandlers.length; i<ehl; i++) {
|
|
|
17094 |
var type = eventHandlers[i].type;
|
|
|
17095 |
// Have this function remove itself from the eventHandlers list too
|
|
|
17096 |
if (type === "mouseout" || type === "mousemove" ||
|
|
|
17097 |
type === "mousedown" || type === "mouseup" ||
|
|
|
17098 |
type === "DOMMouseScroll" || type === "mousewheel" || type === "touchstart") {
|
|
|
17099 |
detachEventHandler(eventHandlers[i]);
|
|
|
17100 |
}
|
|
|
17101 |
}
|
|
|
17102 |
|
|
|
17103 |
// If there are any native touch events defined in the sketch, connect all of them
|
|
|
17104 |
// Otherwise, connect all of the emulated mouse events
|
|
|
17105 |
if (p.touchStart !== undef || p.touchMove !== undef ||
|
|
|
17106 |
p.touchEnd !== undef || p.touchCancel !== undef) {
|
|
|
17107 |
attachEventHandler(curElement, "touchstart", function(t) {
|
|
|
17108 |
if (p.touchStart !== undef) {
|
|
|
17109 |
t = addTouchEventOffset(t);
|
|
|
17110 |
p.touchStart(t);
|
|
|
17111 |
}
|
|
|
17112 |
});
|
|
|
17113 |
|
|
|
17114 |
attachEventHandler(curElement, "touchmove", function(t) {
|
|
|
17115 |
if (p.touchMove !== undef) {
|
|
|
17116 |
t.preventDefault(); // Stop the viewport from scrolling
|
|
|
17117 |
t = addTouchEventOffset(t);
|
|
|
17118 |
p.touchMove(t);
|
|
|
17119 |
}
|
|
|
17120 |
});
|
|
|
17121 |
|
|
|
17122 |
attachEventHandler(curElement, "touchend", function(t) {
|
|
|
17123 |
if (p.touchEnd !== undef) {
|
|
|
17124 |
t = addTouchEventOffset(t);
|
|
|
17125 |
p.touchEnd(t);
|
|
|
17126 |
}
|
|
|
17127 |
});
|
|
|
17128 |
|
|
|
17129 |
attachEventHandler(curElement, "touchcancel", function(t) {
|
|
|
17130 |
if (p.touchCancel !== undef) {
|
|
|
17131 |
t = addTouchEventOffset(t);
|
|
|
17132 |
p.touchCancel(t);
|
|
|
17133 |
}
|
|
|
17134 |
});
|
|
|
17135 |
|
|
|
17136 |
} else {
|
|
|
17137 |
// Emulated touch start/mouse down event
|
|
|
17138 |
attachEventHandler(curElement, "touchstart", function(e) {
|
|
|
17139 |
updateMousePosition(curElement, e.touches[0]);
|
|
|
17140 |
|
|
|
17141 |
p.__mousePressed = true;
|
|
|
17142 |
p.mouseDragging = false;
|
|
|
17143 |
p.mouseButton = PConstants.LEFT;
|
|
|
17144 |
|
|
|
17145 |
if (typeof p.mousePressed === "function") {
|
|
|
17146 |
p.mousePressed();
|
|
|
17147 |
}
|
|
|
17148 |
});
|
|
|
17149 |
|
|
|
17150 |
// Emulated touch move/mouse move event
|
|
|
17151 |
attachEventHandler(curElement, "touchmove", function(e) {
|
|
|
17152 |
e.preventDefault();
|
|
|
17153 |
updateMousePosition(curElement, e.touches[0]);
|
|
|
17154 |
|
|
|
17155 |
if (typeof p.mouseMoved === "function" && !p.__mousePressed) {
|
|
|
17156 |
p.mouseMoved();
|
|
|
17157 |
}
|
|
|
17158 |
if (typeof p.mouseDragged === "function" && p.__mousePressed) {
|
|
|
17159 |
p.mouseDragged();
|
|
|
17160 |
p.mouseDragging = true;
|
|
|
17161 |
}
|
|
|
17162 |
});
|
|
|
17163 |
|
|
|
17164 |
// Emulated touch up/mouse up event
|
|
|
17165 |
attachEventHandler(curElement, "touchend", function(e) {
|
|
|
17166 |
p.__mousePressed = false;
|
|
|
17167 |
|
|
|
17168 |
if (typeof p.mouseClicked === "function" && !p.mouseDragging) {
|
|
|
17169 |
p.mouseClicked();
|
|
|
17170 |
}
|
|
|
17171 |
|
|
|
17172 |
if (typeof p.mouseReleased === "function") {
|
|
|
17173 |
p.mouseReleased();
|
|
|
17174 |
}
|
|
|
17175 |
});
|
|
|
17176 |
}
|
|
|
17177 |
|
|
|
17178 |
// Refire the touch start event we consumed in this function
|
|
|
17179 |
curElement.dispatchEvent(t);
|
|
|
17180 |
});
|
|
|
17181 |
|
|
|
17182 |
(function() {
|
|
|
17183 |
var enabled = true,
|
|
|
17184 |
contextMenu = function(e) {
|
|
|
17185 |
e.preventDefault();
|
|
|
17186 |
e.stopPropagation();
|
|
|
17187 |
};
|
|
|
17188 |
|
|
|
17189 |
p.disableContextMenu = function() {
|
|
|
17190 |
if (!enabled) {
|
|
|
17191 |
return;
|
|
|
17192 |
}
|
|
|
17193 |
attachEventHandler(curElement, 'contextmenu', contextMenu);
|
|
|
17194 |
enabled = false;
|
|
|
17195 |
};
|
|
|
17196 |
|
|
|
17197 |
p.enableContextMenu = function() {
|
|
|
17198 |
if (enabled) {
|
|
|
17199 |
return;
|
|
|
17200 |
}
|
|
|
17201 |
detachEventHandler({elem: curElement, type: 'contextmenu', fn: contextMenu});
|
|
|
17202 |
enabled = true;
|
|
|
17203 |
};
|
|
|
17204 |
}());
|
|
|
17205 |
|
|
|
17206 |
attachEventHandler(curElement, "mousemove", function(e) {
|
|
|
17207 |
updateMousePosition(curElement, e);
|
|
|
17208 |
if (typeof p.mouseMoved === "function" && !p.__mousePressed) {
|
|
|
17209 |
p.mouseMoved();
|
|
|
17210 |
}
|
|
|
17211 |
if (typeof p.mouseDragged === "function" && p.__mousePressed) {
|
|
|
17212 |
p.mouseDragged();
|
|
|
17213 |
p.mouseDragging = true;
|
|
|
17214 |
}
|
|
|
17215 |
});
|
|
|
17216 |
|
|
|
17217 |
attachEventHandler(curElement, "mouseout", function(e) {
|
|
|
17218 |
if (typeof p.mouseOut === "function") {
|
|
|
17219 |
p.mouseOut();
|
|
|
17220 |
}
|
|
|
17221 |
});
|
|
|
17222 |
|
|
|
17223 |
attachEventHandler(curElement, "mouseover", function(e) {
|
|
|
17224 |
updateMousePosition(curElement, e);
|
|
|
17225 |
if (typeof p.mouseOver === "function") {
|
|
|
17226 |
p.mouseOver();
|
|
|
17227 |
}
|
|
|
17228 |
});
|
|
|
17229 |
|
|
|
17230 |
attachEventHandler(curElement, "mousedown", function(e) {
|
|
|
17231 |
p.__mousePressed = true;
|
|
|
17232 |
p.mouseDragging = false;
|
|
|
17233 |
switch (e.which) {
|
|
|
17234 |
case 1:
|
|
|
17235 |
p.mouseButton = PConstants.LEFT;
|
|
|
17236 |
break;
|
|
|
17237 |
case 2:
|
|
|
17238 |
p.mouseButton = PConstants.CENTER;
|
|
|
17239 |
break;
|
|
|
17240 |
case 3:
|
|
|
17241 |
p.mouseButton = PConstants.RIGHT;
|
|
|
17242 |
break;
|
|
|
17243 |
}
|
|
|
17244 |
|
|
|
17245 |
if (typeof p.mousePressed === "function") {
|
|
|
17246 |
p.mousePressed();
|
|
|
17247 |
}
|
|
|
17248 |
});
|
|
|
17249 |
|
|
|
17250 |
attachEventHandler(curElement, "mouseup", function(e) {
|
|
|
17251 |
p.__mousePressed = false;
|
|
|
17252 |
|
|
|
17253 |
if (typeof p.mouseClicked === "function" && !p.mouseDragging) {
|
|
|
17254 |
p.mouseClicked();
|
|
|
17255 |
}
|
|
|
17256 |
|
|
|
17257 |
if (typeof p.mouseReleased === "function") {
|
|
|
17258 |
p.mouseReleased();
|
|
|
17259 |
}
|
|
|
17260 |
});
|
|
|
17261 |
|
|
|
17262 |
var mouseWheelHandler = function(e) {
|
|
|
17263 |
var delta = 0;
|
|
|
17264 |
|
|
|
17265 |
if (e.wheelDelta) {
|
|
|
17266 |
delta = e.wheelDelta / 120;
|
|
|
17267 |
if (window.opera) {
|
|
|
17268 |
delta = -delta;
|
|
|
17269 |
}
|
|
|
17270 |
} else if (e.detail) {
|
|
|
17271 |
delta = -e.detail / 3;
|
|
|
17272 |
}
|
|
|
17273 |
|
|
|
17274 |
p.mouseScroll = delta;
|
|
|
17275 |
|
|
|
17276 |
if (delta && typeof p.mouseScrolled === 'function') {
|
|
|
17277 |
p.mouseScrolled();
|
|
|
17278 |
}
|
|
|
17279 |
};
|
|
|
17280 |
|
|
|
17281 |
// Support Gecko and non-Gecko scroll events
|
|
|
17282 |
attachEventHandler(document, 'DOMMouseScroll', mouseWheelHandler);
|
|
|
17283 |
attachEventHandler(document, 'mousewheel', mouseWheelHandler);
|
|
|
17284 |
|
|
|
17285 |
//////////////////////////////////////////////////////////////////////////
|
|
|
17286 |
// Keyboard Events
|
|
|
17287 |
//////////////////////////////////////////////////////////////////////////
|
|
|
17288 |
|
|
|
17289 |
// Get the DOM element if string was passed
|
|
|
17290 |
if (typeof curElement === "string") {
|
|
|
17291 |
curElement = document.getElementById(curElement);
|
|
|
17292 |
}
|
|
|
17293 |
|
|
|
17294 |
// In order to catch key events in a canvas, it needs to be "specially focusable",
|
|
|
17295 |
// by assigning it a tabindex. If no tabindex is specified on-page, set this to 0.
|
|
|
17296 |
if (!curElement.getAttribute("tabindex")) {
|
|
|
17297 |
curElement.setAttribute("tabindex", 0);
|
|
|
17298 |
}
|
|
|
17299 |
|
|
|
17300 |
function getKeyCode(e) {
|
|
|
17301 |
var code = e.which || e.keyCode;
|
|
|
17302 |
switch (code) {
|
|
|
17303 |
case 13: // ENTER
|
|
|
17304 |
return 10;
|
|
|
17305 |
case 91: // META L (Saf/Mac)
|
|
|
17306 |
case 93: // META R (Saf/Mac)
|
|
|
17307 |
case 224: // META (FF/Mac)
|
|
|
17308 |
return 157;
|
|
|
17309 |
case 57392: // CONTROL (Op/Mac)
|
|
|
17310 |
return 17;
|
|
|
17311 |
case 46: // DELETE
|
|
|
17312 |
return 127;
|
|
|
17313 |
case 45: // INSERT
|
|
|
17314 |
return 155;
|
|
|
17315 |
}
|
|
|
17316 |
return code;
|
|
|
17317 |
}
|
|
|
17318 |
|
|
|
17319 |
function getKeyChar(e) {
|
|
|
17320 |
var c = e.which || e.keyCode;
|
|
|
17321 |
var anyShiftPressed = e.shiftKey || e.ctrlKey || e.altKey || e.metaKey;
|
|
|
17322 |
switch (c) {
|
|
|
17323 |
case 13:
|
|
|
17324 |
c = anyShiftPressed ? 13 : 10; // RETURN vs ENTER (Mac)
|
|
|
17325 |
break;
|
|
|
17326 |
case 8:
|
|
|
17327 |
c = anyShiftPressed ? 127 : 8; // DELETE vs BACKSPACE (Mac)
|
|
|
17328 |
break;
|
|
|
17329 |
}
|
|
|
17330 |
return new Char(c);
|
|
|
17331 |
}
|
|
|
17332 |
|
|
|
17333 |
function suppressKeyEvent(e) {
|
|
|
17334 |
if (typeof e.preventDefault === "function") {
|
|
|
17335 |
e.preventDefault();
|
|
|
17336 |
} else if (typeof e.stopPropagation === "function") {
|
|
|
17337 |
e.stopPropagation();
|
|
|
17338 |
}
|
|
|
17339 |
return false;
|
|
|
17340 |
}
|
|
|
17341 |
|
|
|
17342 |
function updateKeyPressed() {
|
|
|
17343 |
var ch;
|
|
|
17344 |
for (ch in pressedKeysMap) {
|
|
|
17345 |
if (pressedKeysMap.hasOwnProperty(ch)) {
|
|
|
17346 |
p.__keyPressed = true;
|
|
|
17347 |
return;
|
|
|
17348 |
}
|
|
|
17349 |
}
|
|
|
17350 |
p.__keyPressed = false;
|
|
|
17351 |
}
|
|
|
17352 |
|
|
|
17353 |
function resetKeyPressed() {
|
|
|
17354 |
p.__keyPressed = false;
|
|
|
17355 |
pressedKeysMap = [];
|
|
|
17356 |
lastPressedKeyCode = null;
|
|
|
17357 |
}
|
|
|
17358 |
|
|
|
17359 |
function simulateKeyTyped(code, c) {
|
|
|
17360 |
pressedKeysMap[code] = c;
|
|
|
17361 |
lastPressedKeyCode = null;
|
|
|
17362 |
p.key = c;
|
|
|
17363 |
p.keyCode = code;
|
|
|
17364 |
p.keyPressed();
|
|
|
17365 |
p.keyCode = 0;
|
|
|
17366 |
p.keyTyped();
|
|
|
17367 |
updateKeyPressed();
|
|
|
17368 |
}
|
|
|
17369 |
|
|
|
17370 |
function handleKeydown(e) {
|
|
|
17371 |
var code = getKeyCode(e);
|
|
|
17372 |
if (code === PConstants.DELETE) {
|
|
|
17373 |
simulateKeyTyped(code, new Char(127));
|
|
|
17374 |
return;
|
|
|
17375 |
}
|
|
|
17376 |
if (codedKeys.indexOf(code) < 0) {
|
|
|
17377 |
lastPressedKeyCode = code;
|
|
|
17378 |
return;
|
|
|
17379 |
}
|
|
|
17380 |
var c = new Char(PConstants.CODED);
|
|
|
17381 |
p.key = c;
|
|
|
17382 |
p.keyCode = code;
|
|
|
17383 |
pressedKeysMap[code] = c;
|
|
|
17384 |
p.keyPressed();
|
|
|
17385 |
lastPressedKeyCode = null;
|
|
|
17386 |
updateKeyPressed();
|
|
|
17387 |
return suppressKeyEvent(e);
|
|
|
17388 |
}
|
|
|
17389 |
|
|
|
17390 |
function handleKeypress(e) {
|
|
|
17391 |
if (lastPressedKeyCode === null) {
|
|
|
17392 |
return; // processed in handleKeydown
|
|
|
17393 |
}
|
|
|
17394 |
var code = lastPressedKeyCode, c = getKeyChar(e);
|
|
|
17395 |
simulateKeyTyped(code, c);
|
|
|
17396 |
return suppressKeyEvent(e);
|
|
|
17397 |
}
|
|
|
17398 |
|
|
|
17399 |
function handleKeyup(e) {
|
|
|
17400 |
var code = getKeyCode(e), c = pressedKeysMap[code];
|
|
|
17401 |
if (c === undef) {
|
|
|
17402 |
return; // no keyPressed event was generated.
|
|
|
17403 |
}
|
|
|
17404 |
p.key = c;
|
|
|
17405 |
p.keyCode = code;
|
|
|
17406 |
p.keyReleased();
|
|
|
17407 |
delete pressedKeysMap[code];
|
|
|
17408 |
updateKeyPressed();
|
|
|
17409 |
}
|
|
|
17410 |
|
|
|
17411 |
// Send aCode Processing syntax to be converted to JavaScript
|
|
|
17412 |
if (!pgraphicsMode) {
|
|
|
17413 |
if (aCode instanceof Processing.Sketch) {
|
|
|
17414 |
// Use sketch as is
|
|
|
17415 |
curSketch = aCode;
|
|
|
17416 |
} else if (typeof aCode === "function") {
|
|
|
17417 |
// Wrap function with default sketch parameters
|
|
|
17418 |
curSketch = new Processing.Sketch(aCode);
|
|
|
17419 |
} else if (!aCode) {
|
|
|
17420 |
// Empty sketch
|
|
|
17421 |
curSketch = new Processing.Sketch(function (){});
|
|
|
17422 |
} else {
|
|
|
17423 |
//#if PARSER
|
|
|
17424 |
// Compile the code
|
|
|
17425 |
curSketch = Processing.compile(aCode);
|
|
|
17426 |
//#else
|
|
|
17427 |
// throw "PJS compile is not supported";
|
|
|
17428 |
//#endif
|
|
|
17429 |
}
|
|
|
17430 |
|
|
|
17431 |
// Expose internal field for diagnostics and testing
|
|
|
17432 |
p.externals.sketch = curSketch;
|
|
|
17433 |
|
|
|
17434 |
wireDimensionalFunctions();
|
|
|
17435 |
|
|
|
17436 |
// the onfocus and onblur events are handled in two parts.
|
|
|
17437 |
// 1) the p.focused property is handled per sketch
|
|
|
17438 |
curElement.onfocus = function() {
|
|
|
17439 |
p.focused = true;
|
|
|
17440 |
};
|
|
|
17441 |
|
|
|
17442 |
curElement.onblur = function() {
|
|
|
17443 |
p.focused = false;
|
|
|
17444 |
if (!curSketch.options.globalKeyEvents) {
|
|
|
17445 |
resetKeyPressed();
|
|
|
17446 |
}
|
|
|
17447 |
};
|
|
|
17448 |
|
|
|
17449 |
// 2) looping status is handled per page, based on the pauseOnBlur @pjs directive
|
|
|
17450 |
if (curSketch.options.pauseOnBlur) {
|
|
|
17451 |
attachEventHandler(window, 'focus', function() {
|
|
|
17452 |
if (doLoop) {
|
|
|
17453 |
p.loop();
|
|
|
17454 |
}
|
|
|
17455 |
});
|
|
|
17456 |
|
|
|
17457 |
attachEventHandler(window, 'blur', function() {
|
|
|
17458 |
if (doLoop && loopStarted) {
|
|
|
17459 |
p.noLoop();
|
|
|
17460 |
doLoop = true; // make sure to keep this true after the noLoop call
|
|
|
17461 |
}
|
|
|
17462 |
resetKeyPressed();
|
|
|
17463 |
});
|
|
|
17464 |
}
|
|
|
17465 |
|
|
|
17466 |
// if keyboard events should be handled globally, the listeners should
|
|
|
17467 |
// be bound to the document window, rather than to the current canvas
|
|
|
17468 |
var keyTrigger = curSketch.options.globalKeyEvents ? window : curElement;
|
|
|
17469 |
attachEventHandler(keyTrigger, "keydown", handleKeydown);
|
|
|
17470 |
attachEventHandler(keyTrigger, "keypress", handleKeypress);
|
|
|
17471 |
attachEventHandler(keyTrigger, "keyup", handleKeyup);
|
|
|
17472 |
|
|
|
17473 |
// Step through the libraries that were attached at doc load...
|
|
|
17474 |
for (var i in Processing.lib) {
|
|
|
17475 |
if (Processing.lib.hasOwnProperty(i)) {
|
|
|
17476 |
if(Processing.lib[i].hasOwnProperty("attach")) {
|
|
|
17477 |
// use attach function if present
|
|
|
17478 |
Processing.lib[i].attach(p);
|
|
|
17479 |
} else if(Processing.lib[i] instanceof Function) {
|
|
|
17480 |
// Init the libraries in the context of this p_instance (legacy)
|
|
|
17481 |
Processing.lib[i].call(this);
|
|
|
17482 |
}
|
|
|
17483 |
}
|
|
|
17484 |
}
|
|
|
17485 |
|
|
|
17486 |
// sketch execute test interval, used to reschedule
|
|
|
17487 |
// an execute when preloads have not yet finished.
|
|
|
17488 |
var retryInterval = 100;
|
|
|
17489 |
|
|
|
17490 |
var executeSketch = function(processing) {
|
|
|
17491 |
// Don't start until all specified images and fonts in the cache are preloaded
|
|
|
17492 |
if (!(curSketch.imageCache.pending || PFont.preloading.pending(retryInterval))) {
|
|
|
17493 |
// the opera preload cache can only be cleared once we start
|
|
|
17494 |
if (window.opera) {
|
|
|
17495 |
var link,
|
|
|
17496 |
element,
|
|
|
17497 |
operaCache=curSketch.imageCache.operaCache;
|
|
|
17498 |
for (link in operaCache) {
|
|
|
17499 |
if(operaCache.hasOwnProperty(link)) {
|
|
|
17500 |
element = operaCache[link];
|
|
|
17501 |
if (element !== null) {
|
|
|
17502 |
document.body.removeChild(element);
|
|
|
17503 |
}
|
|
|
17504 |
delete(operaCache[link]);
|
|
|
17505 |
}
|
|
|
17506 |
}
|
|
|
17507 |
}
|
|
|
17508 |
|
|
|
17509 |
curSketch.attach(processing, defaultScope);
|
|
|
17510 |
|
|
|
17511 |
// pass a reference to the p instance for this sketch.
|
|
|
17512 |
curSketch.onLoad(processing);
|
|
|
17513 |
|
|
|
17514 |
// Run void setup()
|
|
|
17515 |
if (processing.setup) {
|
|
|
17516 |
processing.setup();
|
|
|
17517 |
// if any transforms were performed in setup reset to identity matrix
|
|
|
17518 |
// so draw loop is unpolluted
|
|
|
17519 |
processing.resetMatrix();
|
|
|
17520 |
curSketch.onSetup();
|
|
|
17521 |
}
|
|
|
17522 |
|
|
|
17523 |
// some pixels can be cached, flushing
|
|
|
17524 |
resetContext();
|
|
|
17525 |
|
|
|
17526 |
if (processing.draw) {
|
|
|
17527 |
if (!doLoop) {
|
|
|
17528 |
processing.redraw();
|
|
|
17529 |
} else {
|
|
|
17530 |
processing.loop();
|
|
|
17531 |
}
|
|
|
17532 |
}
|
|
|
17533 |
} else {
|
|
|
17534 |
window.setTimeout(function() { executeSketch(processing); }, retryInterval);
|
|
|
17535 |
}
|
|
|
17536 |
};
|
|
|
17537 |
|
|
|
17538 |
// Only store an instance of non-createGraphics instances.
|
|
|
17539 |
addInstance(this);
|
|
|
17540 |
|
|
|
17541 |
// The parser adds custom methods to the processing context
|
|
|
17542 |
// this renames p to processing so these methods will run
|
|
|
17543 |
executeSketch(p);
|
|
|
17544 |
} else {
|
|
|
17545 |
// No executable sketch was specified
|
|
|
17546 |
// or called via createGraphics
|
|
|
17547 |
curSketch = new Processing.Sketch();
|
|
|
17548 |
|
|
|
17549 |
wireDimensionalFunctions();
|
|
|
17550 |
|
|
|
17551 |
// Hack to make PGraphics work again after splitting size()
|
|
|
17552 |
p.size = function(w, h, render) {
|
|
|
17553 |
if (render && render === PConstants.WEBGL) {
|
|
|
17554 |
wireDimensionalFunctions('3D');
|
|
|
17555 |
} else {
|
|
|
17556 |
wireDimensionalFunctions('2D');
|
|
|
17557 |
}
|
|
|
17558 |
|
|
|
17559 |
p.size(w, h, render);
|
|
|
17560 |
};
|
|
|
17561 |
}
|
|
|
17562 |
}; // Processing() ends
|
|
|
17563 |
|
|
|
17564 |
// Place-holder for overridable debugging function
|
|
|
17565 |
Processing.debug = debug;
|
|
|
17566 |
|
|
|
17567 |
Processing.prototype = defaultScope;
|
|
|
17568 |
|
|
|
17569 |
//#if PARSER
|
|
|
17570 |
// Processing global methods and constants for the parser
|
|
|
17571 |
function getGlobalMembers() {
|
|
|
17572 |
// The names array contains the names of everything that is inside "p."
|
|
|
17573 |
// When something new is added to "p." it must also be added to this list.
|
|
|
17574 |
var names = [ /* this code is generated by jsglobals.js */
|
|
|
17575 |
"abs", "acos", "alpha", "ambient", "ambientLight", "append", "applyMatrix",
|
|
|
17576 |
"arc", "arrayCopy", "asin", "atan", "atan2", "background", "beginCamera",
|
|
|
17577 |
"beginDraw", "beginShape", "bezier", "bezierDetail", "bezierPoint",
|
|
|
17578 |
"bezierTangent", "bezierVertex", "binary", "blend", "blendColor",
|
|
|
17579 |
"blit_resize", "blue", "box", "breakShape", "brightness",
|
|
|
17580 |
"camera", "ceil", "Character", "color", "colorMode",
|
|
|
17581 |
"concat", "constrain", "copy", "cos", "createFont",
|
|
|
17582 |
"createGraphics", "createImage", "cursor", "curve", "curveDetail",
|
|
|
17583 |
"curvePoint", "curveTangent", "curveTightness", "curveVertex", "day",
|
|
|
17584 |
"degrees", "directionalLight", "disableContextMenu",
|
|
|
17585 |
"dist", "draw", "ellipse", "ellipseMode", "emissive", "enableContextMenu",
|
|
|
17586 |
"endCamera", "endDraw", "endShape", "exit", "exp", "expand", "externals",
|
|
|
17587 |
"fill", "filter", "floor", "focused", "frameCount", "frameRate", "frustum",
|
|
|
17588 |
"get", "glyphLook", "glyphTable", "green", "height", "hex", "hint", "hour",
|
|
|
17589 |
"hue", "image", "imageMode", "intersect", "join", "key",
|
|
|
17590 |
"keyCode", "keyPressed", "keyReleased", "keyTyped", "lerp", "lerpColor",
|
|
|
17591 |
"lightFalloff", "lights", "lightSpecular", "line", "link", "loadBytes",
|
|
|
17592 |
"loadFont", "loadGlyphs", "loadImage", "loadPixels", "loadShape",
|
|
|
17593 |
"loadStrings", "log", "loop", "mag", "map", "match", "matchAll", "max",
|
|
|
17594 |
"millis", "min", "minute", "mix", "modelX", "modelY", "modelZ", "modes",
|
|
|
17595 |
"month", "mouseButton", "mouseClicked", "mouseDragged", "mouseMoved",
|
|
|
17596 |
"mouseOut", "mouseOver", "mousePressed", "mouseReleased", "mouseScroll",
|
|
|
17597 |
"mouseScrolled", "mouseX", "mouseY", "name", "nf", "nfc", "nfp", "nfs",
|
|
|
17598 |
"noCursor", "noFill", "noise", "noiseDetail", "noiseSeed", "noLights",
|
|
|
17599 |
"noLoop", "norm", "normal", "noSmooth", "noStroke", "noTint", "ortho",
|
|
|
17600 |
"param", "parseBoolean", "parseByte", "parseChar", "parseFloat",
|
|
|
17601 |
"parseInt", "peg", "perspective", "PImage", "pixels", "PMatrix2D",
|
|
|
17602 |
"PMatrix3D", "PMatrixStack", "pmouseX", "pmouseY", "point",
|
|
|
17603 |
"pointLight", "popMatrix", "popStyle", "pow", "print", "printCamera",
|
|
|
17604 |
"println", "printMatrix", "printProjection", "PShape", "PShapeSVG",
|
|
|
17605 |
"pushMatrix", "pushStyle", "quad", "radians", "random", "Random",
|
|
|
17606 |
"randomSeed", "rect", "rectMode", "red", "redraw", "requestImage",
|
|
|
17607 |
"resetMatrix", "reverse", "rotate", "rotateX", "rotateY", "rotateZ",
|
|
|
17608 |
"round", "saturation", "save", "saveFrame", "saveStrings", "scale",
|
|
|
17609 |
"screenX", "screenY", "screenZ", "second", "set", "setup", "shape",
|
|
|
17610 |
"shapeMode", "shared", "shininess", "shorten", "sin", "size", "smooth",
|
|
|
17611 |
"sort", "specular", "sphere", "sphereDetail", "splice", "split",
|
|
|
17612 |
"splitTokens", "spotLight", "sq", "sqrt", "status", "str", "stroke",
|
|
|
17613 |
"strokeCap", "strokeJoin", "strokeWeight", "subset", "tan", "text",
|
|
|
17614 |
"textAlign", "textAscent", "textDescent", "textFont", "textLeading",
|
|
|
17615 |
"textMode", "textSize", "texture", "textureMode", "textWidth", "tint", "toImageData",
|
|
|
17616 |
"touchCancel", "touchEnd", "touchMove", "touchStart", "translate",
|
|
|
17617 |
"triangle", "trim", "unbinary", "unhex", "updatePixels", "use3DContext",
|
|
|
17618 |
"vertex", "width", "XMLElement", "year", "__contains", "__equals",
|
|
|
17619 |
"__equalsIgnoreCase", "__frameRate", "__hashCode", "__int_cast",
|
|
|
17620 |
"__instanceof", "__keyPressed", "__mousePressed", "__printStackTrace",
|
|
|
17621 |
"__replace", "__replaceAll", "__replaceFirst", "__toCharArray", "__split",
|
|
|
17622 |
"__codePointAt", "__startsWith", "__endsWith"];
|
|
|
17623 |
|
|
|
17624 |
var members = {};
|
|
|
17625 |
var i, l;
|
|
|
17626 |
for (i = 0, l = names.length; i < l ; ++i) {
|
|
|
17627 |
members[names[i]] = null;
|
|
|
17628 |
}
|
|
|
17629 |
for (var lib in Processing.lib) {
|
|
|
17630 |
if (Processing.lib.hasOwnProperty(lib)) {
|
|
|
17631 |
if (Processing.lib[lib].exports) {
|
|
|
17632 |
var exportedNames = Processing.lib[lib].exports;
|
|
|
17633 |
for (i = 0, l = exportedNames.length; i < l; ++i) {
|
|
|
17634 |
members[exportedNames[i]] = null;
|
|
|
17635 |
}
|
|
|
17636 |
}
|
|
|
17637 |
}
|
|
|
17638 |
}
|
|
|
17639 |
return members;
|
|
|
17640 |
}
|
|
|
17641 |
|
|
|
17642 |
/*
|
|
|
17643 |
|
|
|
17644 |
Parser converts Java-like syntax into JavaScript.
|
|
|
17645 |
Creates an Abstract Syntax Tree -- "Light AST" from the Java-like code.
|
|
|
17646 |
|
|
|
17647 |
It is an object tree. The root object is created from the AstRoot class, which contains statements.
|
|
|
17648 |
|
|
|
17649 |
A statement object can be of type: AstForStatement, AstCatchStatement, AstPrefixStatement, AstMethod, AstClass,
|
|
|
17650 |
AstInterface, AstFunction, AstStatementBlock and AstLabel.
|
|
|
17651 |
|
|
|
17652 |
AstPrefixStatement can be a statement of type: if, switch, while, with, do, else, finally, return, throw, try, break, and continue.
|
|
|
17653 |
|
|
|
17654 |
These object's toString function returns the JavaScript code for the statement.
|
|
|
17655 |
|
|
|
17656 |
Any processing calls need "processing." prepended to them.
|
|
|
17657 |
|
|
|
17658 |
Similarly, calls from inside classes need "$this_1.", prepended to them,
|
|
|
17659 |
with 1 being the depth level for inner classes.
|
|
|
17660 |
This includes members passed down from inheritance.
|
|
|
17661 |
|
|
|
17662 |
The resulting code is then eval'd and run.
|
|
|
17663 |
|
|
|
17664 |
*/
|
|
|
17665 |
|
|
|
17666 |
function parseProcessing(code) {
|
|
|
17667 |
var globalMembers = getGlobalMembers();
|
|
|
17668 |
|
|
|
17669 |
// masks parentheses, brackets and braces with '"A5"'
|
|
|
17670 |
// where A is the bracket type, and 5 is the index in an array containing all brackets split into atoms
|
|
|
17671 |
// 'while(true){}' -> 'while"B1""A2"'
|
|
|
17672 |
// parentheses() = B, brackets[] = C and braces{} = A
|
|
|
17673 |
function splitToAtoms(code) {
|
|
|
17674 |
var atoms = [];
|
|
|
17675 |
var items = code.split(/([\{\[\(\)\]\}])/);
|
|
|
17676 |
var result = items[0];
|
|
|
17677 |
|
|
|
17678 |
var stack = [];
|
|
|
17679 |
for(var i=1; i < items.length; i += 2) {
|
|
|
17680 |
var item = items[i];
|
|
|
17681 |
if(item === '[' || item === '{' || item === '(') {
|
|
|
17682 |
stack.push(result); result = item;
|
|
|
17683 |
} else if(item === ']' || item === '}' || item === ')') {
|
|
|
17684 |
var kind = item === '}' ? 'A' : item === ')' ? 'B' : 'C';
|
|
|
17685 |
var index = atoms.length; atoms.push(result + item);
|
|
|
17686 |
result = stack.pop() + '"' + kind + (index + 1) + '"';
|
|
|
17687 |
}
|
|
|
17688 |
result += items[i + 1];
|
|
|
17689 |
}
|
|
|
17690 |
atoms.unshift(result);
|
|
|
17691 |
return atoms;
|
|
|
17692 |
}
|
|
|
17693 |
|
|
|
17694 |
// replaces strings and regexs keyed by index with an array of strings
|
|
|
17695 |
function injectStrings(code, strings) {
|
|
|
17696 |
return code.replace(/'(\d+)'/g, function(all, index) {
|
|
|
17697 |
var val = strings[index];
|
|
|
17698 |
if(val.charAt(0) === "/") {
|
|
|
17699 |
return val;
|
|
|
17700 |
}
|
|
|
17701 |
return (/^'((?:[^'\\\n])|(?:\\.[0-9A-Fa-f]*))'$/).test(val) ? "(new $p.Character(" + val + "))" : val;
|
|
|
17702 |
});
|
|
|
17703 |
}
|
|
|
17704 |
|
|
|
17705 |
// trims off leading and trailing spaces
|
|
|
17706 |
// returns an object. object.left, object.middle, object.right, object.untrim
|
|
|
17707 |
function trimSpaces(string) {
|
|
|
17708 |
var m1 = /^\s*/.exec(string), result;
|
|
|
17709 |
if(m1[0].length === string.length) {
|
|
|
17710 |
result = {left: m1[0], middle: "", right: ""};
|
|
|
17711 |
} else {
|
|
|
17712 |
var m2 = /\s*$/.exec(string);
|
|
|
17713 |
result = {left: m1[0], middle: string.substring(m1[0].length, m2.index), right: m2[0]};
|
|
|
17714 |
}
|
|
|
17715 |
result.untrim = function(t) { return this.left + t + this.right; };
|
|
|
17716 |
return result;
|
|
|
17717 |
}
|
|
|
17718 |
|
|
|
17719 |
// simple trim of leading and trailing spaces
|
|
|
17720 |
function trim(string) {
|
|
|
17721 |
return string.replace(/^\s+/,'').replace(/\s+$/,'');
|
|
|
17722 |
}
|
|
|
17723 |
|
|
|
17724 |
function appendToLookupTable(table, array) {
|
|
|
17725 |
for(var i=0,l=array.length;i<l;++i) {
|
|
|
17726 |
table[array[i]] = null;
|
|
|
17727 |
}
|
|
|
17728 |
return table;
|
|
|
17729 |
}
|
|
|
17730 |
|
|
|
17731 |
function isLookupTableEmpty(table) {
|
|
|
17732 |
for(var i in table) {
|
|
|
17733 |
if(table.hasOwnProperty(i)) {
|
|
|
17734 |
return false;
|
|
|
17735 |
}
|
|
|
17736 |
}
|
|
|
17737 |
return true;
|
|
|
17738 |
}
|
|
|
17739 |
|
|
|
17740 |
function getAtomIndex(templ) { return templ.substring(2, templ.length - 1); }
|
|
|
17741 |
|
|
|
17742 |
// remove carriage returns "\r"
|
|
|
17743 |
var codeWoExtraCr = code.replace(/\r\n?|\n\r/g, "\n");
|
|
|
17744 |
|
|
|
17745 |
// masks strings and regexs with "'5'", where 5 is the index in an array containing all strings and regexs
|
|
|
17746 |
// also removes all comments
|
|
|
17747 |
var strings = [];
|
|
|
17748 |
var codeWoStrings = codeWoExtraCr.replace(/("(?:[^"\\\n]|\\.)*")|('(?:[^'\\\n]|\\.)*')|(([\[\(=|&!\^:?]\s*)(\/(?![*\/])(?:[^\/\\\n]|\\.)*\/[gim]*)\b)|(\/\/[^\n]*\n)|(\/\*(?:(?!\*\/)(?:.|\n))*\*\/)/g,
|
|
|
17749 |
function(all, quoted, aposed, regexCtx, prefix, regex, singleComment, comment) {
|
|
|
17750 |
var index;
|
|
|
17751 |
if(quoted || aposed) { // replace strings
|
|
|
17752 |
index = strings.length; strings.push(all);
|
|
|
17753 |
return "'" + index + "'";
|
|
|
17754 |
}
|
|
|
17755 |
if(regexCtx) { // replace RegExps
|
|
|
17756 |
index = strings.length; strings.push(regex);
|
|
|
17757 |
return prefix + "'" + index + "'";
|
|
|
17758 |
}
|
|
|
17759 |
// kill comments
|
|
|
17760 |
return comment !== "" ? " " : "\n";
|
|
|
17761 |
});
|
|
|
17762 |
|
|
|
17763 |
// removes generics
|
|
|
17764 |
var genericsWereRemoved;
|
|
|
17765 |
var codeWoGenerics = codeWoStrings;
|
|
|
17766 |
var replaceFunc = function(all, before, types, after) {
|
|
|
17767 |
if(!!before || !!after) {
|
|
|
17768 |
return all;
|
|
|
17769 |
}
|
|
|
17770 |
genericsWereRemoved = true;
|
|
|
17771 |
return "";
|
|
|
17772 |
};
|
|
|
17773 |
|
|
|
17774 |
do {
|
|
|
17775 |
genericsWereRemoved = false;
|
|
|
17776 |
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);
|
|
|
17777 |
} while (genericsWereRemoved);
|
|
|
17778 |
|
|
|
17779 |
var atoms = splitToAtoms(codeWoGenerics);
|
|
|
17780 |
var replaceContext;
|
|
|
17781 |
var declaredClasses = {}, currentClassId, classIdSeed = 0;
|
|
|
17782 |
|
|
|
17783 |
function addAtom(text, type) {
|
|
|
17784 |
var lastIndex = atoms.length;
|
|
|
17785 |
atoms.push(text);
|
|
|
17786 |
return '"' + type + lastIndex + '"';
|
|
|
17787 |
}
|
|
|
17788 |
|
|
|
17789 |
function generateClassId() {
|
|
|
17790 |
return "class" + (++classIdSeed);
|
|
|
17791 |
}
|
|
|
17792 |
|
|
|
17793 |
function appendClass(class_, classId, scopeId) {
|
|
|
17794 |
class_.classId = classId;
|
|
|
17795 |
class_.scopeId = scopeId;
|
|
|
17796 |
declaredClasses[classId] = class_;
|
|
|
17797 |
}
|
|
|
17798 |
|
|
|
17799 |
// functions defined below
|
|
|
17800 |
var transformClassBody, transformInterfaceBody, transformStatementsBlock, transformStatements, transformMain, transformExpression;
|
|
|
17801 |
|
|
|
17802 |
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;
|
|
|
17803 |
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;
|
|
|
17804 |
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*)*([=,]|$)/;
|
|
|
17805 |
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;
|
|
|
17806 |
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*/;
|
|
|
17807 |
var functionsRegex = /\bfunction(?:\s+([A-Za-z_$][\w$]*))?\s*("B\d+")\s*("A\d+")/g;
|
|
|
17808 |
|
|
|
17809 |
// This converts classes, methods and functions into atoms, and adds them to the atoms array.
|
|
|
17810 |
// classes = E, methods = D and functions = H
|
|
|
17811 |
function extractClassesAndMethods(code) {
|
|
|
17812 |
var s = code;
|
|
|
17813 |
s = s.replace(classesRegex, function(all) {
|
|
|
17814 |
return addAtom(all, 'E');
|
|
|
17815 |
});
|
|
|
17816 |
s = s.replace(methodsRegex, function(all) {
|
|
|
17817 |
return addAtom(all, 'D');
|
|
|
17818 |
});
|
|
|
17819 |
s = s.replace(functionsRegex, function(all) {
|
|
|
17820 |
return addAtom(all, 'H');
|
|
|
17821 |
});
|
|
|
17822 |
return s;
|
|
|
17823 |
}
|
|
|
17824 |
|
|
|
17825 |
// This converts constructors into atoms, and adds them to the atoms array.
|
|
|
17826 |
// constructors = G
|
|
|
17827 |
function extractConstructors(code, className) {
|
|
|
17828 |
var result = code.replace(cstrsRegex, function(all, attr, name, params, throws_, body) {
|
|
|
17829 |
if(name !== className) {
|
|
|
17830 |
return all;
|
|
|
17831 |
}
|
|
|
17832 |
return addAtom(all, 'G');
|
|
|
17833 |
});
|
|
|
17834 |
return result;
|
|
|
17835 |
}
|
|
|
17836 |
|
|
|
17837 |
// AstParam contains the name of a parameter inside a function declaration
|
|
|
17838 |
function AstParam(name) {
|
|
|
17839 |
this.name = name;
|
|
|
17840 |
}
|
|
|
17841 |
AstParam.prototype.toString = function() {
|
|
|
17842 |
return this.name;
|
|
|
17843 |
};
|
|
|
17844 |
// AstParams contains an array of AstParam objects
|
|
|
17845 |
function AstParams(params) {
|
|
|
17846 |
this.params = params;
|
|
|
17847 |
}
|
|
|
17848 |
AstParams.prototype.getNames = function() {
|
|
|
17849 |
var names = [];
|
|
|
17850 |
for(var i=0,l=this.params.length;i<l;++i) {
|
|
|
17851 |
names.push(this.params[i].name);
|
|
|
17852 |
}
|
|
|
17853 |
return names;
|
|
|
17854 |
};
|
|
|
17855 |
AstParams.prototype.toString = function() {
|
|
|
17856 |
if(this.params.length === 0) {
|
|
|
17857 |
return "()";
|
|
|
17858 |
}
|
|
|
17859 |
var result = "(";
|
|
|
17860 |
for(var i=0,l=this.params.length;i<l;++i) {
|
|
|
17861 |
result += this.params[i] + ", ";
|
|
|
17862 |
}
|
|
|
17863 |
return result.substring(0, result.length - 2) + ")";
|
|
|
17864 |
};
|
|
|
17865 |
|
|
|
17866 |
function transformParams(params) {
|
|
|
17867 |
var paramsWoPars = trim(params.substring(1, params.length - 1));
|
|
|
17868 |
var result = [];
|
|
|
17869 |
if(paramsWoPars !== "") {
|
|
|
17870 |
var paramList = paramsWoPars.split(",");
|
|
|
17871 |
for(var i=0; i < paramList.length; ++i) {
|
|
|
17872 |
var param = /\b([A-Za-z_$][\w$]*\b)(\s*"[ABC][\d]*")*\s*$/.exec(paramList[i]);
|
|
|
17873 |
result.push(new AstParam(param[1]));
|
|
|
17874 |
}
|
|
|
17875 |
}
|
|
|
17876 |
return new AstParams(result);
|
|
|
17877 |
}
|
|
|
17878 |
|
|
|
17879 |
function preExpressionTransform(expr) {
|
|
|
17880 |
var s = expr;
|
|
|
17881 |
// new type[] {...} --> {...}
|
|
|
17882 |
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) {
|
|
|
17883 |
return init;
|
|
|
17884 |
});
|
|
|
17885 |
// new Runnable() {...} --> "F???"
|
|
|
17886 |
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) {
|
|
|
17887 |
return addAtom(all, 'F');
|
|
|
17888 |
});
|
|
|
17889 |
// function(...) { } --> "H???"
|
|
|
17890 |
s = s.replace(functionsRegex, function(all) {
|
|
|
17891 |
return addAtom(all, 'H');
|
|
|
17892 |
});
|
|
|
17893 |
// new type[?] --> createJavaArray('type', [?])
|
|
|
17894 |
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) {
|
|
|
17895 |
var args = index.replace(/"C(\d+)"/g, function(all, j) { return atoms[j]; })
|
|
|
17896 |
.replace(/\[\s*\]/g, "[null]").replace(/\s*\]\s*\[\s*/g, ", ");
|
|
|
17897 |
var arrayInitializer = "{" + args.substring(1, args.length - 1) + "}";
|
|
|
17898 |
var createArrayArgs = "('" + type + "', " + addAtom(arrayInitializer, 'A') + ")";
|
|
|
17899 |
return '$p.createJavaArray' + addAtom(createArrayArgs, 'B');
|
|
|
17900 |
});
|
|
|
17901 |
// .length() --> .length
|
|
|
17902 |
s = s.replace(/(\.\s*length)\s*"B\d+"/g, "$1");
|
|
|
17903 |
// #000000 --> 0x000000
|
|
|
17904 |
s = s.replace(/#([0-9A-Fa-f]{6})\b/g, function(all, digits) {
|
|
|
17905 |
return "0xFF" + digits;
|
|
|
17906 |
});
|
|
|
17907 |
// delete (type)???, except (int)???
|
|
|
17908 |
s = s.replace(/"B(\d+)"(\s*(?:[\w$']|"B))/g, function(all, index, next) {
|
|
|
17909 |
var atom = atoms[index];
|
|
|
17910 |
if(!/^\(\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\s*(?:"C\d+"\s*)*\)$/.test(atom)) {
|
|
|
17911 |
return all;
|
|
|
17912 |
}
|
|
|
17913 |
if(/^\(\s*int\s*\)$/.test(atom)) {
|
|
|
17914 |
return "(int)" + next;
|
|
|
17915 |
}
|
|
|
17916 |
var indexParts = atom.split(/"C(\d+)"/g);
|
|
|
17917 |
if(indexParts.length > 1) {
|
|
|
17918 |
// even items contains atom numbers, can check only first
|
|
|
17919 |
if(! /^\[\s*\]$/.test(atoms[indexParts[1]])) {
|
|
|
17920 |
return all; // fallback - not a cast
|
|
|
17921 |
}
|
|
|
17922 |
}
|
|
|
17923 |
return "" + next;
|
|
|
17924 |
});
|
|
|
17925 |
// (int)??? -> __int_cast(???)
|
|
|
17926 |
s = s.replace(/\(int\)([^,\]\)\}\?\:\*\+\-\/\^\|\%\&\~<\>\=]+)/g, function(all, arg) {
|
|
|
17927 |
var trimmed = trimSpaces(arg);
|
|
|
17928 |
return trimmed.untrim("__int_cast(" + trimmed.middle + ")");
|
|
|
17929 |
});
|
|
|
17930 |
// super() -> $superCstr(), super. -> $super.;
|
|
|
17931 |
s = s.replace(/\bsuper(\s*"B\d+")/g, "$$superCstr$1").replace(/\bsuper(\s*\.)/g, "$$super$1");
|
|
|
17932 |
// 000.43->0.43 and 0010f->10, but not 0010
|
|
|
17933 |
s = s.replace(/\b0+((\d*)(?:\.[\d*])?(?:[eE][\-\+]?\d+)?[fF]?)\b/, function(all, numberWo0, intPart) {
|
|
|
17934 |
if( numberWo0 === intPart) {
|
|
|
17935 |
return all;
|
|
|
17936 |
}
|
|
|
17937 |
return intPart === "" ? "0" + numberWo0 : numberWo0;
|
|
|
17938 |
});
|
|
|
17939 |
// 3.0f -> 3.0
|
|
|
17940 |
s = s.replace(/\b(\.?\d+\.?)[fF]\b/g, "$1");
|
|
|
17941 |
// Weird (?) parsing errors with %
|
|
|
17942 |
s = s.replace(/([^\s])%([^=\s])/g, "$1 % $2");
|
|
|
17943 |
// Since frameRate() and frameRate are different things,
|
|
|
17944 |
// we need to differentiate them somehow. So when we parse
|
|
|
17945 |
// the Processing.js source, replace frameRate so it isn't
|
|
|
17946 |
// confused with frameRate(), as well as keyPressed and mousePressed
|
|
|
17947 |
s = s.replace(/\b(frameRate|keyPressed|mousePressed)\b(?!\s*"B)/g, "__$1");
|
|
|
17948 |
// "boolean", "byte", "int", etc. => "parseBoolean", "parseByte", "parseInt", etc.
|
|
|
17949 |
s = s.replace(/\b(boolean|byte|char|float|int)\s*"B/g, function(all, name) {
|
|
|
17950 |
return "parse" + name.substring(0, 1).toUpperCase() + name.substring(1) + "\"B";
|
|
|
17951 |
});
|
|
|
17952 |
// "pixels" replacements:
|
|
|
17953 |
// pixels[i] = c => pixels.setPixel(i,c) | pixels[i] => pixels.getPixel(i)
|
|
|
17954 |
// pixels.length => pixels.getLength()
|
|
|
17955 |
// pixels = ar => pixels.set(ar) | pixels => pixels.toArray()
|
|
|
17956 |
s = s.replace(/\bpixels\b\s*(("C(\d+)")|\.length)?(\s*=(?!=)([^,\]\)\}]+))?/g,
|
|
|
17957 |
function(all, indexOrLength, index, atomIndex, equalsPart, rightSide) {
|
|
|
17958 |
if(index) {
|
|
|
17959 |
var atom = atoms[atomIndex];
|
|
|
17960 |
if(equalsPart) {
|
|
|
17961 |
return "pixels.setPixel" + addAtom("(" +atom.substring(1, atom.length - 1) +
|
|
|
17962 |
"," + rightSide + ")", 'B');
|
|
|
17963 |
}
|
|
|
17964 |
return "pixels.getPixel" + addAtom("(" + atom.substring(1, atom.length - 1) +
|
|
|
17965 |
")", 'B');
|
|
|
17966 |
}
|
|
|
17967 |
if(indexOrLength) {
|
|
|
17968 |
// length
|
|
|
17969 |
return "pixels.getLength" + addAtom("()", 'B');
|
|
|
17970 |
}
|
|
|
17971 |
if(equalsPart) {
|
|
|
17972 |
return "pixels.set" + addAtom("(" + rightSide + ")", 'B');
|
|
|
17973 |
}
|
|
|
17974 |
return "pixels.toArray" + addAtom("()", 'B');
|
|
|
17975 |
});
|
|
|
17976 |
// Java method replacements for: replace, replaceAll, replaceFirst, equals, hashCode, etc.
|
|
|
17977 |
// xxx.replace(yyy) -> __replace(xxx, yyy)
|
|
|
17978 |
// "xx".replace(yyy) -> __replace("xx", yyy)
|
|
|
17979 |
var repeatJavaReplacement;
|
|
|
17980 |
function replacePrototypeMethods(all, subject, method, atomIndex) {
|
|
|
17981 |
var atom = atoms[atomIndex];
|
|
|
17982 |
repeatJavaReplacement = true;
|
|
|
17983 |
var trimmed = trimSpaces(atom.substring(1, atom.length - 1));
|
|
|
17984 |
return "__" + method + ( trimmed.middle === "" ? addAtom("(" + subject.replace(/\.\s*$/, "") + ")", 'B') :
|
|
|
17985 |
addAtom("(" + subject.replace(/\.\s*$/, "") + "," + trimmed.middle + ")", 'B') );
|
|
|
17986 |
}
|
|
|
17987 |
do {
|
|
|
17988 |
repeatJavaReplacement = false;
|
|
|
17989 |
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)\s*"B(\d+)"/g,
|
|
|
17990 |
replacePrototypeMethods);
|
|
|
17991 |
} while (repeatJavaReplacement);
|
|
|
17992 |
// xxx instanceof yyy -> __instanceof(xxx, yyy)
|
|
|
17993 |
function replaceInstanceof(all, subject, type) {
|
|
|
17994 |
repeatJavaReplacement = true;
|
|
|
17995 |
return "__instanceof" + addAtom("(" + subject + ", " + type + ")", 'B');
|
|
|
17996 |
}
|
|
|
17997 |
do {
|
|
|
17998 |
repeatJavaReplacement = false;
|
|
|
17999 |
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,
|
|
|
18000 |
replaceInstanceof);
|
|
|
18001 |
} while (repeatJavaReplacement);
|
|
|
18002 |
// this() -> $constr()
|
|
|
18003 |
s = s.replace(/\bthis(\s*"B\d+")/g, "$$constr$1");
|
|
|
18004 |
|
|
|
18005 |
return s;
|
|
|
18006 |
}
|
|
|
18007 |
|
|
|
18008 |
function AstInlineClass(baseInterfaceName, body) {
|
|
|
18009 |
this.baseInterfaceName = baseInterfaceName;
|
|
|
18010 |
this.body = body;
|
|
|
18011 |
body.owner = this;
|
|
|
18012 |
}
|
|
|
18013 |
AstInlineClass.prototype.toString = function() {
|
|
|
18014 |
return "new (" + this.body + ")";
|
|
|
18015 |
};
|
|
|
18016 |
|
|
|
18017 |
function transformInlineClass(class_) {
|
|
|
18018 |
var m = new RegExp(/\bnew\s*([A-Za-z_$][\w$]*\s*(?:\.\s*[A-Za-z_$][\w$]*)*)\s*"B\d+"\s*"A(\d+)"/).exec(class_);
|
|
|
18019 |
var oldClassId = currentClassId, newClassId = generateClassId();
|
|
|
18020 |
currentClassId = newClassId;
|
|
|
18021 |
var uniqueClassName = m[1] + "$" + newClassId;
|
|
|
18022 |
var inlineClass = new AstInlineClass(uniqueClassName,
|
|
|
18023 |
transformClassBody(atoms[m[2]], uniqueClassName, "", "implements " + m[1]));
|
|
|
18024 |
appendClass(inlineClass, newClassId, oldClassId);
|
|
|
18025 |
currentClassId = oldClassId;
|
|
|
18026 |
return inlineClass;
|
|
|
18027 |
}
|
|
|
18028 |
|
|
|
18029 |
function AstFunction(name, params, body) {
|
|
|
18030 |
this.name = name;
|
|
|
18031 |
this.params = params;
|
|
|
18032 |
this.body = body;
|
|
|
18033 |
}
|
|
|
18034 |
AstFunction.prototype.toString = function() {
|
|
|
18035 |
var oldContext = replaceContext;
|
|
|
18036 |
// saving "this." and parameters
|
|
|
18037 |
var names = appendToLookupTable({"this":null}, this.params.getNames());
|
|
|
18038 |
replaceContext = function (subject) {
|
|
|
18039 |
return names.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
|
|
|
18040 |
};
|
|
|
18041 |
var result = "function";
|
|
|
18042 |
if(this.name) {
|
|
|
18043 |
result += " " + this.name;
|
|
|
18044 |
}
|
|
|
18045 |
result += this.params + " " + this.body;
|
|
|
18046 |
replaceContext = oldContext;
|
|
|
18047 |
return result;
|
|
|
18048 |
};
|
|
|
18049 |
|
|
|
18050 |
function transformFunction(class_) {
|
|
|
18051 |
var m = new RegExp(/\b([A-Za-z_$][\w$]*)\s*"B(\d+)"\s*"A(\d+)"/).exec(class_);
|
|
|
18052 |
return new AstFunction( m[1] !== "function" ? m[1] : null,
|
|
|
18053 |
transformParams(atoms[m[2]]), transformStatementsBlock(atoms[m[3]]));
|
|
|
18054 |
}
|
|
|
18055 |
|
|
|
18056 |
function AstInlineObject(members) {
|
|
|
18057 |
this.members = members;
|
|
|
18058 |
}
|
|
|
18059 |
AstInlineObject.prototype.toString = function() {
|
|
|
18060 |
var oldContext = replaceContext;
|
|
|
18061 |
replaceContext = function (subject) {
|
|
|
18062 |
return subject.name === "this" ? "this" : oldContext(subject); // saving "this."
|
|
|
18063 |
};
|
|
|
18064 |
var result = "";
|
|
|
18065 |
for(var i=0,l=this.members.length;i<l;++i) {
|
|
|
18066 |
if(this.members[i].label) {
|
|
|
18067 |
result += this.members[i].label + ": ";
|
|
|
18068 |
}
|
|
|
18069 |
result += this.members[i].value.toString() + ", ";
|
|
|
18070 |
}
|
|
|
18071 |
replaceContext = oldContext;
|
|
|
18072 |
return result.substring(0, result.length - 2);
|
|
|
18073 |
};
|
|
|
18074 |
|
|
|
18075 |
function transformInlineObject(obj) {
|
|
|
18076 |
var members = obj.split(',');
|
|
|
18077 |
for(var i=0; i < members.length; ++i) {
|
|
|
18078 |
var label = members[i].indexOf(':');
|
|
|
18079 |
if(label < 0) {
|
|
|
18080 |
members[i] = { value: transformExpression(members[i]) };
|
|
|
18081 |
} else {
|
|
|
18082 |
members[i] = { label: trim(members[i].substring(0, label)),
|
|
|
18083 |
value: transformExpression( trim(members[i].substring(label + 1)) ) };
|
|
|
18084 |
}
|
|
|
18085 |
}
|
|
|
18086 |
return new AstInlineObject(members);
|
|
|
18087 |
}
|
|
|
18088 |
|
|
|
18089 |
function expandExpression(expr) {
|
|
|
18090 |
if(expr.charAt(0) === '(' || expr.charAt(0) === '[') {
|
|
|
18091 |
return expr.charAt(0) + expandExpression(expr.substring(1, expr.length - 1)) + expr.charAt(expr.length - 1);
|
|
|
18092 |
}
|
|
|
18093 |
if(expr.charAt(0) === '{') {
|
|
|
18094 |
if(/^\{\s*(?:[A-Za-z_$][\w$]*|'\d+')\s*:/.test(expr)) {
|
|
|
18095 |
return "{" + addAtom(expr.substring(1, expr.length - 1), 'I') + "}";
|
|
|
18096 |
}
|
|
|
18097 |
return "[" + expandExpression(expr.substring(1, expr.length - 1)) + "]";
|
|
|
18098 |
}
|
|
|
18099 |
var trimmed = trimSpaces(expr);
|
|
|
18100 |
var result = preExpressionTransform(trimmed.middle);
|
|
|
18101 |
result = result.replace(/"[ABC](\d+)"/g, function(all, index) {
|
|
|
18102 |
return expandExpression(atoms[index]);
|
|
|
18103 |
});
|
|
|
18104 |
return trimmed.untrim(result);
|
|
|
18105 |
}
|
|
|
18106 |
|
|
|
18107 |
function replaceContextInVars(expr) {
|
|
|
18108 |
return expr.replace(/(\.\s*)?((?:\b[A-Za-z_]|\$)[\w$]*)(\s*\.\s*([A-Za-z_$][\w$]*)(\s*\()?)?/g,
|
|
|
18109 |
function(all, memberAccessSign, identifier, suffix, subMember, callSign) {
|
|
|
18110 |
if(memberAccessSign) {
|
|
|
18111 |
return all;
|
|
|
18112 |
}
|
|
|
18113 |
var subject = { name: identifier, member: subMember, callSign: !!callSign };
|
|
|
18114 |
return replaceContext(subject) + (suffix === undef ? "" : suffix);
|
|
|
18115 |
});
|
|
|
18116 |
}
|
|
|
18117 |
|
|
|
18118 |
function AstExpression(expr, transforms) {
|
|
|
18119 |
this.expr = expr;
|
|
|
18120 |
this.transforms = transforms;
|
|
|
18121 |
}
|
|
|
18122 |
AstExpression.prototype.toString = function() {
|
|
|
18123 |
var transforms = this.transforms;
|
|
|
18124 |
var expr = replaceContextInVars(this.expr);
|
|
|
18125 |
return expr.replace(/"!(\d+)"/g, function(all, index) {
|
|
|
18126 |
return transforms[index].toString();
|
|
|
18127 |
});
|
|
|
18128 |
};
|
|
|
18129 |
|
|
|
18130 |
transformExpression = function(expr) {
|
|
|
18131 |
var transforms = [];
|
|
|
18132 |
var s = expandExpression(expr);
|
|
|
18133 |
s = s.replace(/"H(\d+)"/g, function(all, index) {
|
|
|
18134 |
transforms.push(transformFunction(atoms[index]));
|
|
|
18135 |
return '"!' + (transforms.length - 1) + '"';
|
|
|
18136 |
});
|
|
|
18137 |
s = s.replace(/"F(\d+)"/g, function(all, index) {
|
|
|
18138 |
transforms.push(transformInlineClass(atoms[index]));
|
|
|
18139 |
return '"!' + (transforms.length - 1) + '"';
|
|
|
18140 |
});
|
|
|
18141 |
s = s.replace(/"I(\d+)"/g, function(all, index) {
|
|
|
18142 |
transforms.push(transformInlineObject(atoms[index]));
|
|
|
18143 |
return '"!' + (transforms.length - 1) + '"';
|
|
|
18144 |
});
|
|
|
18145 |
|
|
|
18146 |
return new AstExpression(s, transforms);
|
|
|
18147 |
};
|
|
|
18148 |
|
|
|
18149 |
function AstVarDefinition(name, value, isDefault) {
|
|
|
18150 |
this.name = name;
|
|
|
18151 |
this.value = value;
|
|
|
18152 |
this.isDefault = isDefault;
|
|
|
18153 |
}
|
|
|
18154 |
AstVarDefinition.prototype.toString = function() {
|
|
|
18155 |
return this.name + ' = ' + this.value;
|
|
|
18156 |
};
|
|
|
18157 |
|
|
|
18158 |
function transformVarDefinition(def, defaultTypeValue) {
|
|
|
18159 |
var eqIndex = def.indexOf("=");
|
|
|
18160 |
var name, value, isDefault;
|
|
|
18161 |
if(eqIndex < 0) {
|
|
|
18162 |
name = def;
|
|
|
18163 |
value = defaultTypeValue;
|
|
|
18164 |
isDefault = true;
|
|
|
18165 |
} else {
|
|
|
18166 |
name = def.substring(0, eqIndex);
|
|
|
18167 |
value = transformExpression(def.substring(eqIndex + 1));
|
|
|
18168 |
isDefault = false;
|
|
|
18169 |
}
|
|
|
18170 |
return new AstVarDefinition( trim(name.replace(/(\s*"C\d+")+/g, "")),
|
|
|
18171 |
value, isDefault);
|
|
|
18172 |
}
|
|
|
18173 |
|
|
|
18174 |
function getDefaultValueForType(type) {
|
|
|
18175 |
if(type === "int" || type === "float") {
|
|
|
18176 |
return "0";
|
|
|
18177 |
}
|
|
|
18178 |
if(type === "boolean") {
|
|
|
18179 |
return "false";
|
|
|
18180 |
}
|
|
|
18181 |
if(type === "color") {
|
|
|
18182 |
return "0x00000000";
|
|
|
18183 |
}
|
|
|
18184 |
return "null";
|
|
|
18185 |
}
|
|
|
18186 |
|
|
|
18187 |
function AstVar(definitions, varType) {
|
|
|
18188 |
this.definitions = definitions;
|
|
|
18189 |
this.varType = varType;
|
|
|
18190 |
}
|
|
|
18191 |
AstVar.prototype.getNames = function() {
|
|
|
18192 |
var names = [];
|
|
|
18193 |
for(var i=0,l=this.definitions.length;i<l;++i) {
|
|
|
18194 |
names.push(this.definitions[i].name);
|
|
|
18195 |
}
|
|
|
18196 |
return names;
|
|
|
18197 |
};
|
|
|
18198 |
AstVar.prototype.toString = function() {
|
|
|
18199 |
return "var " + this.definitions.join(",");
|
|
|
18200 |
};
|
|
|
18201 |
function AstStatement(expression) {
|
|
|
18202 |
this.expression = expression;
|
|
|
18203 |
}
|
|
|
18204 |
AstStatement.prototype.toString = function() {
|
|
|
18205 |
return this.expression.toString();
|
|
|
18206 |
};
|
|
|
18207 |
|
|
|
18208 |
function transformStatement(statement) {
|
|
|
18209 |
if(fieldTest.test(statement)) {
|
|
|
18210 |
var attrAndType = attrAndTypeRegex.exec(statement);
|
|
|
18211 |
var definitions = statement.substring(attrAndType[0].length).split(",");
|
|
|
18212 |
var defaultTypeValue = getDefaultValueForType(attrAndType[2]);
|
|
|
18213 |
for(var i=0; i < definitions.length; ++i) {
|
|
|
18214 |
definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue);
|
|
|
18215 |
}
|
|
|
18216 |
return new AstVar(definitions, attrAndType[2]);
|
|
|
18217 |
}
|
|
|
18218 |
return new AstStatement(transformExpression(statement));
|
|
|
18219 |
}
|
|
|
18220 |
|
|
|
18221 |
function AstForExpression(initStatement, condition, step) {
|
|
|
18222 |
this.initStatement = initStatement;
|
|
|
18223 |
this.condition = condition;
|
|
|
18224 |
this.step = step;
|
|
|
18225 |
}
|
|
|
18226 |
AstForExpression.prototype.toString = function() {
|
|
|
18227 |
return "(" + this.initStatement + "; " + this.condition + "; " + this.step + ")";
|
|
|
18228 |
};
|
|
|
18229 |
|
|
|
18230 |
function AstForInExpression(initStatement, container) {
|
|
|
18231 |
this.initStatement = initStatement;
|
|
|
18232 |
this.container = container;
|
|
|
18233 |
}
|
|
|
18234 |
AstForInExpression.prototype.toString = function() {
|
|
|
18235 |
var init = this.initStatement.toString();
|
|
|
18236 |
if(init.indexOf("=") >= 0) { // can be without var declaration
|
|
|
18237 |
init = init.substring(0, init.indexOf("="));
|
|
|
18238 |
}
|
|
|
18239 |
return "(" + init + " in " + this.container + ")";
|
|
|
18240 |
};
|
|
|
18241 |
|
|
|
18242 |
function AstForEachExpression(initStatement, container) {
|
|
|
18243 |
this.initStatement = initStatement;
|
|
|
18244 |
this.container = container;
|
|
|
18245 |
}
|
|
|
18246 |
AstForEachExpression.iteratorId = 0;
|
|
|
18247 |
AstForEachExpression.prototype.toString = function() {
|
|
|
18248 |
var init = this.initStatement.toString();
|
|
|
18249 |
var iterator = "$it" + (AstForEachExpression.iteratorId++);
|
|
|
18250 |
var variableName = init.replace(/^\s*var\s*/, "").split("=")[0];
|
|
|
18251 |
var initIteratorAndVariable = "var " + iterator + " = new $p.ObjectIterator(" + this.container + "), " +
|
|
|
18252 |
variableName + " = void(0)";
|
|
|
18253 |
var nextIterationCondition = iterator + ".hasNext() && ((" +
|
|
|
18254 |
variableName + " = " + iterator + ".next()) || true)";
|
|
|
18255 |
return "(" + initIteratorAndVariable + "; " + nextIterationCondition + ";)";
|
|
|
18256 |
};
|
|
|
18257 |
|
|
|
18258 |
function transformForExpression(expr) {
|
|
|
18259 |
var content;
|
|
|
18260 |
if (/\bin\b/.test(expr)) {
|
|
|
18261 |
content = expr.substring(1, expr.length - 1).split(/\bin\b/g);
|
|
|
18262 |
return new AstForInExpression( transformStatement(trim(content[0])),
|
|
|
18263 |
transformExpression(content[1]));
|
|
|
18264 |
}
|
|
|
18265 |
if (expr.indexOf(":") >= 0 && expr.indexOf(";") < 0) {
|
|
|
18266 |
content = expr.substring(1, expr.length - 1).split(":");
|
|
|
18267 |
return new AstForEachExpression( transformStatement(trim(content[0])),
|
|
|
18268 |
transformExpression(content[1]));
|
|
|
18269 |
}
|
|
|
18270 |
content = expr.substring(1, expr.length - 1).split(";");
|
|
|
18271 |
return new AstForExpression( transformStatement(trim(content[0])),
|
|
|
18272 |
transformExpression(content[1]), transformExpression(content[2]));
|
|
|
18273 |
}
|
|
|
18274 |
|
|
|
18275 |
function sortByWeight(array) {
|
|
|
18276 |
array.sort(function (a,b) {
|
|
|
18277 |
return b.weight - a.weight;
|
|
|
18278 |
});
|
|
|
18279 |
}
|
|
|
18280 |
|
|
|
18281 |
function AstInnerInterface(name, body, isStatic) {
|
|
|
18282 |
this.name = name;
|
|
|
18283 |
this.body = body;
|
|
|
18284 |
this.isStatic = isStatic;
|
|
|
18285 |
body.owner = this;
|
|
|
18286 |
}
|
|
|
18287 |
AstInnerInterface.prototype.toString = function() {
|
|
|
18288 |
return "" + this.body;
|
|
|
18289 |
};
|
|
|
18290 |
function AstInnerClass(name, body, isStatic) {
|
|
|
18291 |
this.name = name;
|
|
|
18292 |
this.body = body;
|
|
|
18293 |
this.isStatic = isStatic;
|
|
|
18294 |
body.owner = this;
|
|
|
18295 |
}
|
|
|
18296 |
AstInnerClass.prototype.toString = function() {
|
|
|
18297 |
return "" + this.body;
|
|
|
18298 |
};
|
|
|
18299 |
|
|
|
18300 |
function transformInnerClass(class_) {
|
|
|
18301 |
var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body
|
|
|
18302 |
classesRegex.lastIndex = 0;
|
|
|
18303 |
var isStatic = m[1].indexOf("static") >= 0;
|
|
|
18304 |
var body = atoms[getAtomIndex(m[6])], innerClass;
|
|
|
18305 |
var oldClassId = currentClassId, newClassId = generateClassId();
|
|
|
18306 |
currentClassId = newClassId;
|
|
|
18307 |
if(m[2] === "interface") {
|
|
|
18308 |
innerClass = new AstInnerInterface(m[3], transformInterfaceBody(body, m[3], m[4]), isStatic);
|
|
|
18309 |
} else {
|
|
|
18310 |
innerClass = new AstInnerClass(m[3], transformClassBody(body, m[3], m[4], m[5]), isStatic);
|
|
|
18311 |
}
|
|
|
18312 |
appendClass(innerClass, newClassId, oldClassId);
|
|
|
18313 |
currentClassId = oldClassId;
|
|
|
18314 |
return innerClass;
|
|
|
18315 |
}
|
|
|
18316 |
|
|
|
18317 |
function AstClassMethod(name, params, body, isStatic) {
|
|
|
18318 |
this.name = name;
|
|
|
18319 |
this.params = params;
|
|
|
18320 |
this.body = body;
|
|
|
18321 |
this.isStatic = isStatic;
|
|
|
18322 |
}
|
|
|
18323 |
AstClassMethod.prototype.toString = function(){
|
|
|
18324 |
var paramNames = appendToLookupTable({}, this.params.getNames());
|
|
|
18325 |
var oldContext = replaceContext;
|
|
|
18326 |
replaceContext = function (subject) {
|
|
|
18327 |
return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
|
|
|
18328 |
};
|
|
|
18329 |
var result = "function " + this.methodId + this.params + " " + this.body +"\n";
|
|
|
18330 |
replaceContext = oldContext;
|
|
|
18331 |
return result;
|
|
|
18332 |
};
|
|
|
18333 |
|
|
|
18334 |
function transformClassMethod(method) {
|
|
|
18335 |
var m = methodsRegex.exec(method);
|
|
|
18336 |
methodsRegex.lastIndex = 0;
|
|
|
18337 |
var isStatic = m[1].indexOf("static") >= 0;
|
|
|
18338 |
var body = m[6] !== ';' ? atoms[getAtomIndex(m[6])] : "{}";
|
|
|
18339 |
return new AstClassMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]),
|
|
|
18340 |
transformStatementsBlock(body), isStatic );
|
|
|
18341 |
}
|
|
|
18342 |
|
|
|
18343 |
function AstClassField(definitions, fieldType, isStatic) {
|
|
|
18344 |
this.definitions = definitions;
|
|
|
18345 |
this.fieldType = fieldType;
|
|
|
18346 |
this.isStatic = isStatic;
|
|
|
18347 |
}
|
|
|
18348 |
AstClassField.prototype.getNames = function() {
|
|
|
18349 |
var names = [];
|
|
|
18350 |
for(var i=0,l=this.definitions.length;i<l;++i) {
|
|
|
18351 |
names.push(this.definitions[i].name);
|
|
|
18352 |
}
|
|
|
18353 |
return names;
|
|
|
18354 |
};
|
|
|
18355 |
AstClassField.prototype.toString = function() {
|
|
|
18356 |
var thisPrefix = replaceContext({ name: "[this]" });
|
|
|
18357 |
if(this.isStatic) {
|
|
|
18358 |
var className = this.owner.name;
|
|
|
18359 |
var staticDeclarations = [];
|
|
|
18360 |
for(var i=0,l=this.definitions.length;i<l;++i) {
|
|
|
18361 |
var definition = this.definitions[i];
|
|
|
18362 |
var name = definition.name, staticName = className + "." + name;
|
|
|
18363 |
var declaration = "if(" + staticName + " === void(0)) {\n" +
|
|
|
18364 |
" " + staticName + " = " + definition.value + "; }\n" +
|
|
|
18365 |
"$p.defineProperty(" + thisPrefix + ", " +
|
|
|
18366 |
"'" + name + "', { get: function(){return " + staticName + ";}, " +
|
|
|
18367 |
"set: function(val){" + staticName + " = val;} });\n";
|
|
|
18368 |
staticDeclarations.push(declaration);
|
|
|
18369 |
}
|
|
|
18370 |
return staticDeclarations.join("");
|
|
|
18371 |
}
|
|
|
18372 |
return thisPrefix + "." + this.definitions.join("; " + thisPrefix + ".");
|
|
|
18373 |
};
|
|
|
18374 |
|
|
|
18375 |
function transformClassField(statement) {
|
|
|
18376 |
var attrAndType = attrAndTypeRegex.exec(statement);
|
|
|
18377 |
var isStatic = attrAndType[1].indexOf("static") >= 0;
|
|
|
18378 |
var definitions = statement.substring(attrAndType[0].length).split(/,\s*/g);
|
|
|
18379 |
var defaultTypeValue = getDefaultValueForType(attrAndType[2]);
|
|
|
18380 |
for(var i=0; i < definitions.length; ++i) {
|
|
|
18381 |
definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue);
|
|
|
18382 |
}
|
|
|
18383 |
return new AstClassField(definitions, attrAndType[2], isStatic);
|
|
|
18384 |
}
|
|
|
18385 |
|
|
|
18386 |
function AstConstructor(params, body) {
|
|
|
18387 |
this.params = params;
|
|
|
18388 |
this.body = body;
|
|
|
18389 |
}
|
|
|
18390 |
AstConstructor.prototype.toString = function() {
|
|
|
18391 |
var paramNames = appendToLookupTable({}, this.params.getNames());
|
|
|
18392 |
var oldContext = replaceContext;
|
|
|
18393 |
replaceContext = function (subject) {
|
|
|
18394 |
return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
|
|
|
18395 |
};
|
|
|
18396 |
var prefix = "function $constr_" + this.params.params.length + this.params.toString();
|
|
|
18397 |
var body = this.body.toString();
|
|
|
18398 |
if(!/\$(superCstr|constr)\b/.test(body)) {
|
|
|
18399 |
body = "{\n$superCstr();\n" + body.substring(1);
|
|
|
18400 |
}
|
|
|
18401 |
replaceContext = oldContext;
|
|
|
18402 |
return prefix + body + "\n";
|
|
|
18403 |
};
|
|
|
18404 |
|
|
|
18405 |
function transformConstructor(cstr) {
|
|
|
18406 |
var m = new RegExp(/"B(\d+)"\s*"A(\d+)"/).exec(cstr);
|
|
|
18407 |
var params = transformParams(atoms[m[1]]);
|
|
|
18408 |
|
|
|
18409 |
return new AstConstructor(params, transformStatementsBlock(atoms[m[2]]));
|
|
|
18410 |
}
|
|
|
18411 |
|
|
|
18412 |
function AstInterfaceBody(name, interfacesNames, methodsNames, fields, innerClasses, misc) {
|
|
|
18413 |
var i,l;
|
|
|
18414 |
this.name = name;
|
|
|
18415 |
this.interfacesNames = interfacesNames;
|
|
|
18416 |
this.methodsNames = methodsNames;
|
|
|
18417 |
this.fields = fields;
|
|
|
18418 |
this.innerClasses = innerClasses;
|
|
|
18419 |
this.misc = misc;
|
|
|
18420 |
for(i=0,l=fields.length; i<l; ++i) {
|
|
|
18421 |
fields[i].owner = this;
|
|
|
18422 |
}
|
|
|
18423 |
}
|
|
|
18424 |
AstInterfaceBody.prototype.getMembers = function(classFields, classMethods, classInners) {
|
|
|
18425 |
if(this.owner.base) {
|
|
|
18426 |
this.owner.base.body.getMembers(classFields, classMethods, classInners);
|
|
|
18427 |
}
|
|
|
18428 |
var i, j, l, m;
|
|
|
18429 |
for(i=0,l=this.fields.length;i<l;++i) {
|
|
|
18430 |
var fieldNames = this.fields[i].getNames();
|
|
|
18431 |
for(j=0,m=fieldNames.length;j<m;++j) {
|
|
|
18432 |
classFields[fieldNames[j]] = this.fields[i];
|
|
|
18433 |
}
|
|
|
18434 |
}
|
|
|
18435 |
for(i=0,l=this.methodsNames.length;i<l;++i) {
|
|
|
18436 |
var methodName = this.methodsNames[i];
|
|
|
18437 |
classMethods[methodName] = true;
|
|
|
18438 |
}
|
|
|
18439 |
for(i=0,l=this.innerClasses.length;i<l;++i) {
|
|
|
18440 |
var innerClass = this.innerClasses[i];
|
|
|
18441 |
classInners[innerClass.name] = innerClass;
|
|
|
18442 |
}
|
|
|
18443 |
};
|
|
|
18444 |
AstInterfaceBody.prototype.toString = function() {
|
|
|
18445 |
function getScopeLevel(p) {
|
|
|
18446 |
var i = 0;
|
|
|
18447 |
while(p) {
|
|
|
18448 |
++i;
|
|
|
18449 |
p=p.scope;
|
|
|
18450 |
}
|
|
|
18451 |
return i;
|
|
|
18452 |
}
|
|
|
18453 |
|
|
|
18454 |
var scopeLevel = getScopeLevel(this.owner);
|
|
|
18455 |
|
|
|
18456 |
var className = this.name;
|
|
|
18457 |
var staticDefinitions = "";
|
|
|
18458 |
var metadata = "";
|
|
|
18459 |
|
|
|
18460 |
var thisClassFields = {}, thisClassMethods = {}, thisClassInners = {};
|
|
|
18461 |
this.getMembers(thisClassFields, thisClassMethods, thisClassInners);
|
|
|
18462 |
|
|
|
18463 |
var i, l, j, m;
|
|
|
18464 |
|
|
|
18465 |
if (this.owner.interfaces) {
|
|
|
18466 |
// interface name can be present, but interface is not
|
|
|
18467 |
var resolvedInterfaces = [], resolvedInterface;
|
|
|
18468 |
for (i = 0, l = this.interfacesNames.length; i < l; ++i) {
|
|
|
18469 |
if (!this.owner.interfaces[i]) {
|
|
|
18470 |
continue;
|
|
|
18471 |
}
|
|
|
18472 |
resolvedInterface = replaceContext({name: this.interfacesNames[i]});
|
|
|
18473 |
resolvedInterfaces.push(resolvedInterface);
|
|
|
18474 |
staticDefinitions += "$p.extendInterfaceMembers(" + className + ", " + resolvedInterface + ");\n";
|
|
|
18475 |
}
|
|
|
18476 |
metadata += className + ".$interfaces = [" + resolvedInterfaces.join(", ") + "];\n";
|
|
|
18477 |
}
|
|
|
18478 |
metadata += className + ".$isInterface = true;\n";
|
|
|
18479 |
metadata += className + ".$methods = [\'" + this.methodsNames.join("\', \'") + "\'];\n";
|
|
|
18480 |
|
|
|
18481 |
sortByWeight(this.innerClasses);
|
|
|
18482 |
for (i = 0, l = this.innerClasses.length; i < l; ++i) {
|
|
|
18483 |
var innerClass = this.innerClasses[i];
|
|
|
18484 |
if (innerClass.isStatic) {
|
|
|
18485 |
staticDefinitions += className + "." + innerClass.name + " = " + innerClass + ";\n";
|
|
|
18486 |
}
|
|
|
18487 |
}
|
|
|
18488 |
|
|
|
18489 |
for (i = 0, l = this.fields.length; i < l; ++i) {
|
|
|
18490 |
var field = this.fields[i];
|
|
|
18491 |
if (field.isStatic) {
|
|
|
18492 |
staticDefinitions += className + "." + field.definitions.join(";\n" + className + ".") + ";\n";
|
|
|
18493 |
}
|
|
|
18494 |
}
|
|
|
18495 |
|
|
|
18496 |
return "(function() {\n" +
|
|
|
18497 |
"function " + className + "() { throw \'Unable to create the interface\'; }\n" +
|
|
|
18498 |
staticDefinitions +
|
|
|
18499 |
metadata +
|
|
|
18500 |
"return " + className + ";\n" +
|
|
|
18501 |
"})()";
|
|
|
18502 |
};
|
|
|
18503 |
|
|
|
18504 |
transformInterfaceBody = function(body, name, baseInterfaces) {
|
|
|
18505 |
var declarations = body.substring(1, body.length - 1);
|
|
|
18506 |
declarations = extractClassesAndMethods(declarations);
|
|
|
18507 |
declarations = extractConstructors(declarations, name);
|
|
|
18508 |
var methodsNames = [], classes = [];
|
|
|
18509 |
declarations = declarations.replace(/"([DE])(\d+)"/g, function(all, type, index) {
|
|
|
18510 |
if(type === 'D') { methodsNames.push(index); }
|
|
|
18511 |
else if(type === 'E') { classes.push(index); }
|
|
|
18512 |
return "";
|
|
|
18513 |
});
|
|
|
18514 |
var fields = declarations.split(/;(?:\s*;)*/g);
|
|
|
18515 |
var baseInterfaceNames;
|
|
|
18516 |
var i, l;
|
|
|
18517 |
|
|
|
18518 |
if(baseInterfaces !== undef) {
|
|
|
18519 |
baseInterfaceNames = baseInterfaces.replace(/^\s*extends\s+(.+?)\s*$/g, "$1").split(/\s*,\s*/g);
|
|
|
18520 |
}
|
|
|
18521 |
|
|
|
18522 |
for(i = 0, l = methodsNames.length; i < l; ++i) {
|
|
|
18523 |
var method = transformClassMethod(atoms[methodsNames[i]]);
|
|
|
18524 |
methodsNames[i] = method.name;
|
|
|
18525 |
}
|
|
|
18526 |
for(i = 0, l = fields.length - 1; i < l; ++i) {
|
|
|
18527 |
var field = trimSpaces(fields[i]);
|
|
|
18528 |
fields[i] = transformClassField(field.middle);
|
|
|
18529 |
}
|
|
|
18530 |
var tail = fields.pop();
|
|
|
18531 |
for(i = 0, l = classes.length; i < l; ++i) {
|
|
|
18532 |
classes[i] = transformInnerClass(atoms[classes[i]]);
|
|
|
18533 |
}
|
|
|
18534 |
|
|
|
18535 |
return new AstInterfaceBody(name, baseInterfaceNames, methodsNames, fields, classes, { tail: tail });
|
|
|
18536 |
};
|
|
|
18537 |
|
|
|
18538 |
function AstClassBody(name, baseClassName, interfacesNames, functions, methods, fields, cstrs, innerClasses, misc) {
|
|
|
18539 |
var i,l;
|
|
|
18540 |
this.name = name;
|
|
|
18541 |
this.baseClassName = baseClassName;
|
|
|
18542 |
this.interfacesNames = interfacesNames;
|
|
|
18543 |
this.functions = functions;
|
|
|
18544 |
this.methods = methods;
|
|
|
18545 |
this.fields = fields;
|
|
|
18546 |
this.cstrs = cstrs;
|
|
|
18547 |
this.innerClasses = innerClasses;
|
|
|
18548 |
this.misc = misc;
|
|
|
18549 |
for(i=0,l=fields.length; i<l; ++i) {
|
|
|
18550 |
fields[i].owner = this;
|
|
|
18551 |
}
|
|
|
18552 |
}
|
|
|
18553 |
AstClassBody.prototype.getMembers = function(classFields, classMethods, classInners) {
|
|
|
18554 |
if(this.owner.base) {
|
|
|
18555 |
this.owner.base.body.getMembers(classFields, classMethods, classInners);
|
|
|
18556 |
}
|
|
|
18557 |
var i, j, l, m;
|
|
|
18558 |
for(i=0,l=this.fields.length;i<l;++i) {
|
|
|
18559 |
var fieldNames = this.fields[i].getNames();
|
|
|
18560 |
for(j=0,m=fieldNames.length;j<m;++j) {
|
|
|
18561 |
classFields[fieldNames[j]] = this.fields[i];
|
|
|
18562 |
}
|
|
|
18563 |
}
|
|
|
18564 |
for(i=0,l=this.methods.length;i<l;++i) {
|
|
|
18565 |
var method = this.methods[i];
|
|
|
18566 |
classMethods[method.name] = method;
|
|
|
18567 |
}
|
|
|
18568 |
for(i=0,l=this.innerClasses.length;i<l;++i) {
|
|
|
18569 |
var innerClass = this.innerClasses[i];
|
|
|
18570 |
classInners[innerClass.name] = innerClass;
|
|
|
18571 |
}
|
|
|
18572 |
};
|
|
|
18573 |
AstClassBody.prototype.toString = function() {
|
|
|
18574 |
function getScopeLevel(p) {
|
|
|
18575 |
var i = 0;
|
|
|
18576 |
while(p) {
|
|
|
18577 |
++i;
|
|
|
18578 |
p=p.scope;
|
|
|
18579 |
}
|
|
|
18580 |
return i;
|
|
|
18581 |
}
|
|
|
18582 |
|
|
|
18583 |
var scopeLevel = getScopeLevel(this.owner);
|
|
|
18584 |
|
|
|
18585 |
var selfId = "$this_" + scopeLevel;
|
|
|
18586 |
var className = this.name;
|
|
|
18587 |
var result = "var " + selfId + " = this;\n";
|
|
|
18588 |
var staticDefinitions = "";
|
|
|
18589 |
var metadata = "";
|
|
|
18590 |
|
|
|
18591 |
var thisClassFields = {}, thisClassMethods = {}, thisClassInners = {};
|
|
|
18592 |
this.getMembers(thisClassFields, thisClassMethods, thisClassInners);
|
|
|
18593 |
|
|
|
18594 |
var oldContext = replaceContext;
|
|
|
18595 |
replaceContext = function (subject) {
|
|
|
18596 |
var name = subject.name;
|
|
|
18597 |
if(name === "this") {
|
|
|
18598 |
// returns "$this_N.$self" pointer instead of "this" in cases:
|
|
|
18599 |
// "this()", "this.XXX()", "this", but not for "this.XXX"
|
|
|
18600 |
return subject.callSign || !subject.member ? selfId + ".$self" : selfId;
|
|
|
18601 |
}
|
|
|
18602 |
if(thisClassFields.hasOwnProperty(name)) {
|
|
|
18603 |
return thisClassFields[name].isStatic ? className + "." + name : selfId + "." + name;
|
|
|
18604 |
}
|
|
|
18605 |
if(thisClassInners.hasOwnProperty(name)) {
|
|
|
18606 |
return selfId + "." + name;
|
|
|
18607 |
}
|
|
|
18608 |
if(thisClassMethods.hasOwnProperty(name)) {
|
|
|
18609 |
return thisClassMethods[name].isStatic ? className + "." + name : selfId + ".$self." + name;
|
|
|
18610 |
}
|
|
|
18611 |
return oldContext(subject);
|
|
|
18612 |
};
|
|
|
18613 |
|
|
|
18614 |
var resolvedBaseClassName;
|
|
|
18615 |
if (this.baseClassName) {
|
|
|
18616 |
resolvedBaseClassName = oldContext({name: this.baseClassName});
|
|
|
18617 |
result += "var $super = { $upcast: " + selfId + " };\n";
|
|
|
18618 |
result += "function $superCstr(){" + resolvedBaseClassName +
|
|
|
18619 |
".apply($super,arguments);if(!('$self' in $super)) $p.extendClassChain($super)}\n";
|
|
|
18620 |
metadata += className + ".$base = " + resolvedBaseClassName + ";\n";
|
|
|
18621 |
} else {
|
|
|
18622 |
result += "function $superCstr(){$p.extendClassChain("+ selfId +")}\n";
|
|
|
18623 |
}
|
|
|
18624 |
|
|
|
18625 |
if (this.owner.base) {
|
|
|
18626 |
// base class name can be present, but class is not
|
|
|
18627 |
staticDefinitions += "$p.extendStaticMembers(" + className + ", " + resolvedBaseClassName + ");\n";
|
|
|
18628 |
}
|
|
|
18629 |
|
|
|
18630 |
var i, l, j, m;
|
|
|
18631 |
|
|
|
18632 |
if (this.owner.interfaces) {
|
|
|
18633 |
// interface name can be present, but interface is not
|
|
|
18634 |
var resolvedInterfaces = [], resolvedInterface;
|
|
|
18635 |
for (i = 0, l = this.interfacesNames.length; i < l; ++i) {
|
|
|
18636 |
if (!this.owner.interfaces[i]) {
|
|
|
18637 |
continue;
|
|
|
18638 |
}
|
|
|
18639 |
resolvedInterface = oldContext({name: this.interfacesNames[i]});
|
|
|
18640 |
resolvedInterfaces.push(resolvedInterface);
|
|
|
18641 |
staticDefinitions += "$p.extendInterfaceMembers(" + className + ", " + resolvedInterface + ");\n";
|
|
|
18642 |
}
|
|
|
18643 |
metadata += className + ".$interfaces = [" + resolvedInterfaces.join(", ") + "];\n";
|
|
|
18644 |
}
|
|
|
18645 |
|
|
|
18646 |
if (this.functions.length > 0) {
|
|
|
18647 |
result += this.functions.join('\n') + '\n';
|
|
|
18648 |
}
|
|
|
18649 |
|
|
|
18650 |
sortByWeight(this.innerClasses);
|
|
|
18651 |
for (i = 0, l = this.innerClasses.length; i < l; ++i) {
|
|
|
18652 |
var innerClass = this.innerClasses[i];
|
|
|
18653 |
if (innerClass.isStatic) {
|
|
|
18654 |
staticDefinitions += className + "." + innerClass.name + " = " + innerClass + ";\n";
|
|
|
18655 |
result += selfId + "." + innerClass.name + " = " + className + "." + innerClass.name + ";\n";
|
|
|
18656 |
} else {
|
|
|
18657 |
result += selfId + "." + innerClass.name + " = " + innerClass + ";\n";
|
|
|
18658 |
}
|
|
|
18659 |
}
|
|
|
18660 |
|
|
|
18661 |
for (i = 0, l = this.fields.length; i < l; ++i) {
|
|
|
18662 |
var field = this.fields[i];
|
|
|
18663 |
if (field.isStatic) {
|
|
|
18664 |
staticDefinitions += className + "." + field.definitions.join(";\n" + className + ".") + ";\n";
|
|
|
18665 |
for (j = 0, m = field.definitions.length; j < m; ++j) {
|
|
|
18666 |
var fieldName = field.definitions[j].name, staticName = className + "." + fieldName;
|
|
|
18667 |
result += "$p.defineProperty(" + selfId + ", '" + fieldName + "', {" +
|
|
|
18668 |
"get: function(){return " + staticName + "}, " +
|
|
|
18669 |
"set: function(val){" + staticName + " = val}});\n";
|
|
|
18670 |
}
|
|
|
18671 |
} else {
|
|
|
18672 |
result += selfId + "." + field.definitions.join(";\n" + selfId + ".") + ";\n";
|
|
|
18673 |
}
|
|
|
18674 |
}
|
|
|
18675 |
var methodOverloads = {};
|
|
|
18676 |
for (i = 0, l = this.methods.length; i < l; ++i) {
|
|
|
18677 |
var method = this.methods[i];
|
|
|
18678 |
var overload = methodOverloads[method.name];
|
|
|
18679 |
var methodId = method.name + "$" + method.params.params.length;
|
|
|
18680 |
if (overload) {
|
|
|
18681 |
++overload;
|
|
|
18682 |
methodId += "_" + overload;
|
|
|
18683 |
} else {
|
|
|
18684 |
overload = 1;
|
|
|
18685 |
}
|
|
|
18686 |
method.methodId = methodId;
|
|
|
18687 |
methodOverloads[method.name] = overload;
|
|
|
18688 |
if (method.isStatic) {
|
|
|
18689 |
staticDefinitions += method;
|
|
|
18690 |
staticDefinitions += "$p.addMethod(" + className + ", '" + method.name + "', " + methodId + ");\n";
|
|
|
18691 |
result += "$p.addMethod(" + selfId + ", '" + method.name + "', " + methodId + ");\n";
|
|
|
18692 |
} else {
|
|
|
18693 |
result += method;
|
|
|
18694 |
result += "$p.addMethod(" + selfId + ", '" + method.name + "', " + methodId + ");\n";
|
|
|
18695 |
}
|
|
|
18696 |
}
|
|
|
18697 |
result += trim(this.misc.tail);
|
|
|
18698 |
|
|
|
18699 |
if (this.cstrs.length > 0) {
|
|
|
18700 |
result += this.cstrs.join('\n') + '\n';
|
|
|
18701 |
}
|
|
|
18702 |
|
|
|
18703 |
result += "function $constr() {\n";
|
|
|
18704 |
var cstrsIfs = [];
|
|
|
18705 |
for (i = 0, l = this.cstrs.length; i < l; ++i) {
|
|
|
18706 |
var paramsLength = this.cstrs[i].params.params.length;
|
|
|
18707 |
cstrsIfs.push("if(arguments.length === " + paramsLength + ") { " +
|
|
|
18708 |
"$constr_" + paramsLength + ".apply(" + selfId + ", arguments); }");
|
|
|
18709 |
}
|
|
|
18710 |
if(cstrsIfs.length > 0) {
|
|
|
18711 |
result += cstrsIfs.join(" else ") + " else ";
|
|
|
18712 |
}
|
|
|
18713 |
// ??? add check if length is 0, otherwise fail
|
|
|
18714 |
result += "$superCstr();\n}\n";
|
|
|
18715 |
result += "$constr.apply(null, arguments);\n";
|
|
|
18716 |
|
|
|
18717 |
replaceContext = oldContext;
|
|
|
18718 |
return "(function() {\n" +
|
|
|
18719 |
"function " + className + "() {\n" + result + "}\n" +
|
|
|
18720 |
staticDefinitions +
|
|
|
18721 |
metadata +
|
|
|
18722 |
"return " + className + ";\n" +
|
|
|
18723 |
"})()";
|
|
|
18724 |
};
|
|
|
18725 |
|
|
|
18726 |
transformClassBody = function(body, name, baseName, interfaces) {
|
|
|
18727 |
var declarations = body.substring(1, body.length - 1);
|
|
|
18728 |
declarations = extractClassesAndMethods(declarations);
|
|
|
18729 |
declarations = extractConstructors(declarations, name);
|
|
|
18730 |
var methods = [], classes = [], cstrs = [], functions = [];
|
|
|
18731 |
declarations = declarations.replace(/"([DEGH])(\d+)"/g, function(all, type, index) {
|
|
|
18732 |
if(type === 'D') { methods.push(index); }
|
|
|
18733 |
else if(type === 'E') { classes.push(index); }
|
|
|
18734 |
else if(type === 'H') { functions.push(index); }
|
|
|
18735 |
else { cstrs.push(index); }
|
|
|
18736 |
return "";
|
|
|
18737 |
});
|
|
|
18738 |
var fields = declarations.replace(/^(?:\s*;)+/, "").split(/;(?:\s*;)*/g);
|
|
|
18739 |
var baseClassName, interfacesNames;
|
|
|
18740 |
var i;
|
|
|
18741 |
|
|
|
18742 |
if(baseName !== undef) {
|
|
|
18743 |
baseClassName = baseName.replace(/^\s*extends\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)\s*$/g, "$1");
|
|
|
18744 |
}
|
|
|
18745 |
|
|
|
18746 |
if(interfaces !== undef) {
|
|
|
18747 |
interfacesNames = interfaces.replace(/^\s*implements\s+(.+?)\s*$/g, "$1").split(/\s*,\s*/g);
|
|
|
18748 |
}
|
|
|
18749 |
|
|
|
18750 |
for(i = 0; i < functions.length; ++i) {
|
|
|
18751 |
functions[i] = transformFunction(atoms[functions[i]]);
|
|
|
18752 |
}
|
|
|
18753 |
for(i = 0; i < methods.length; ++i) {
|
|
|
18754 |
methods[i] = transformClassMethod(atoms[methods[i]]);
|
|
|
18755 |
}
|
|
|
18756 |
for(i = 0; i < fields.length - 1; ++i) {
|
|
|
18757 |
var field = trimSpaces(fields[i]);
|
|
|
18758 |
fields[i] = transformClassField(field.middle);
|
|
|
18759 |
}
|
|
|
18760 |
var tail = fields.pop();
|
|
|
18761 |
for(i = 0; i < cstrs.length; ++i) {
|
|
|
18762 |
cstrs[i] = transformConstructor(atoms[cstrs[i]]);
|
|
|
18763 |
}
|
|
|
18764 |
for(i = 0; i < classes.length; ++i) {
|
|
|
18765 |
classes[i] = transformInnerClass(atoms[classes[i]]);
|
|
|
18766 |
}
|
|
|
18767 |
|
|
|
18768 |
return new AstClassBody(name, baseClassName, interfacesNames, functions, methods, fields, cstrs,
|
|
|
18769 |
classes, { tail: tail });
|
|
|
18770 |
};
|
|
|
18771 |
|
|
|
18772 |
function AstInterface(name, body) {
|
|
|
18773 |
this.name = name;
|
|
|
18774 |
this.body = body;
|
|
|
18775 |
body.owner = this;
|
|
|
18776 |
}
|
|
|
18777 |
AstInterface.prototype.toString = function() {
|
|
|
18778 |
return "var " + this.name + " = " + this.body + ";\n" +
|
|
|
18779 |
"$p." + this.name + " = " + this.name + ";\n";
|
|
|
18780 |
};
|
|
|
18781 |
function AstClass(name, body) {
|
|
|
18782 |
this.name = name;
|
|
|
18783 |
this.body = body;
|
|
|
18784 |
body.owner = this;
|
|
|
18785 |
}
|
|
|
18786 |
AstClass.prototype.toString = function() {
|
|
|
18787 |
return "var " + this.name + " = " + this.body + ";\n" +
|
|
|
18788 |
"$p." + this.name + " = " + this.name + ";\n";
|
|
|
18789 |
};
|
|
|
18790 |
|
|
|
18791 |
function transformGlobalClass(class_) {
|
|
|
18792 |
var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body
|
|
|
18793 |
classesRegex.lastIndex = 0;
|
|
|
18794 |
var body = atoms[getAtomIndex(m[6])];
|
|
|
18795 |
var oldClassId = currentClassId, newClassId = generateClassId();
|
|
|
18796 |
currentClassId = newClassId;
|
|
|
18797 |
var globalClass;
|
|
|
18798 |
if(m[2] === "interface") {
|
|
|
18799 |
globalClass = new AstInterface(m[3], transformInterfaceBody(body, m[3], m[4]) );
|
|
|
18800 |
} else {
|
|
|
18801 |
globalClass = new AstClass(m[3], transformClassBody(body, m[3], m[4], m[5]) );
|
|
|
18802 |
}
|
|
|
18803 |
appendClass(globalClass, newClassId, oldClassId);
|
|
|
18804 |
currentClassId = oldClassId;
|
|
|
18805 |
return globalClass;
|
|
|
18806 |
}
|
|
|
18807 |
|
|
|
18808 |
function AstMethod(name, params, body) {
|
|
|
18809 |
this.name = name;
|
|
|
18810 |
this.params = params;
|
|
|
18811 |
this.body = body;
|
|
|
18812 |
}
|
|
|
18813 |
AstMethod.prototype.toString = function(){
|
|
|
18814 |
var paramNames = appendToLookupTable({}, this.params.getNames());
|
|
|
18815 |
var oldContext = replaceContext;
|
|
|
18816 |
replaceContext = function (subject) {
|
|
|
18817 |
return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
|
|
|
18818 |
};
|
|
|
18819 |
var result = "function " + this.name + this.params + " " + this.body + "\n" +
|
|
|
18820 |
"$p." + this.name + " = " + this.name + ";";
|
|
|
18821 |
replaceContext = oldContext;
|
|
|
18822 |
return result;
|
|
|
18823 |
};
|
|
|
18824 |
|
|
|
18825 |
function transformGlobalMethod(method) {
|
|
|
18826 |
var m = methodsRegex.exec(method);
|
|
|
18827 |
var result =
|
|
|
18828 |
methodsRegex.lastIndex = 0;
|
|
|
18829 |
return new AstMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]),
|
|
|
18830 |
transformStatementsBlock(atoms[getAtomIndex(m[6])]));
|
|
|
18831 |
}
|
|
|
18832 |
|
|
|
18833 |
function preStatementsTransform(statements) {
|
|
|
18834 |
var s = statements;
|
|
|
18835 |
// turns multiple catch blocks into one, because we have no way to properly get into them anyway.
|
|
|
18836 |
s = s.replace(/\b(catch\s*"B\d+"\s*"A\d+")(\s*catch\s*"B\d+"\s*"A\d+")+/g, "$1");
|
|
|
18837 |
return s;
|
|
|
18838 |
}
|
|
|
18839 |
|
|
|
18840 |
function AstForStatement(argument, misc) {
|
|
|
18841 |
this.argument = argument;
|
|
|
18842 |
this.misc = misc;
|
|
|
18843 |
}
|
|
|
18844 |
AstForStatement.prototype.toString = function() {
|
|
|
18845 |
return this.misc.prefix + this.argument.toString();
|
|
|
18846 |
};
|
|
|
18847 |
function AstCatchStatement(argument, misc) {
|
|
|
18848 |
this.argument = argument;
|
|
|
18849 |
this.misc = misc;
|
|
|
18850 |
}
|
|
|
18851 |
AstCatchStatement.prototype.toString = function() {
|
|
|
18852 |
return this.misc.prefix + this.argument.toString();
|
|
|
18853 |
};
|
|
|
18854 |
function AstPrefixStatement(name, argument, misc) {
|
|
|
18855 |
this.name = name;
|
|
|
18856 |
this.argument = argument;
|
|
|
18857 |
this.misc = misc;
|
|
|
18858 |
}
|
|
|
18859 |
AstPrefixStatement.prototype.toString = function() {
|
|
|
18860 |
var result = this.misc.prefix;
|
|
|
18861 |
if(this.argument !== undef) {
|
|
|
18862 |
result += this.argument.toString();
|
|
|
18863 |
}
|
|
|
18864 |
return result;
|
|
|
18865 |
};
|
|
|
18866 |
function AstSwitchCase(expr) {
|
|
|
18867 |
this.expr = expr;
|
|
|
18868 |
}
|
|
|
18869 |
AstSwitchCase.prototype.toString = function() {
|
|
|
18870 |
return "case " + this.expr + ":";
|
|
|
18871 |
};
|
|
|
18872 |
function AstLabel(label) {
|
|
|
18873 |
this.label = label;
|
|
|
18874 |
}
|
|
|
18875 |
AstLabel.prototype.toString = function() {
|
|
|
18876 |
return this.label;
|
|
|
18877 |
};
|
|
|
18878 |
|
|
|
18879 |
transformStatements = function(statements, transformMethod, transformClass) {
|
|
|
18880 |
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);
|
|
|
18881 |
var res = [];
|
|
|
18882 |
statements = preStatementsTransform(statements);
|
|
|
18883 |
var lastIndex = 0, m, space;
|
|
|
18884 |
// m contains the matches from the nextStatement regexp, null if there are no matches.
|
|
|
18885 |
// nextStatement.exec starts searching at nextStatement.lastIndex.
|
|
|
18886 |
while((m = nextStatement.exec(statements)) !== null) {
|
|
|
18887 |
if(m[1] !== undef) { // catch, for ...
|
|
|
18888 |
var i = statements.lastIndexOf('"B', nextStatement.lastIndex);
|
|
|
18889 |
var statementsPrefix = statements.substring(lastIndex, i);
|
|
|
18890 |
if(m[1] === "for") {
|
|
|
18891 |
res.push(new AstForStatement(transformForExpression(atoms[m[2]]),
|
|
|
18892 |
{ prefix: statementsPrefix }) );
|
|
|
18893 |
} else if(m[1] === "catch") {
|
|
|
18894 |
res.push(new AstCatchStatement(transformParams(atoms[m[2]]),
|
|
|
18895 |
{ prefix: statementsPrefix }) );
|
|
|
18896 |
} else {
|
|
|
18897 |
res.push(new AstPrefixStatement(m[1], transformExpression(atoms[m[2]]),
|
|
|
18898 |
{ prefix: statementsPrefix }) );
|
|
|
18899 |
}
|
|
|
18900 |
} else if(m[3] !== undef) { // do, else, ...
|
|
|
18901 |
res.push(new AstPrefixStatement(m[3], undef,
|
|
|
18902 |
{ prefix: statements.substring(lastIndex, nextStatement.lastIndex) }) );
|
|
|
18903 |
} else if(m[4] !== undef) { // block, class and methods
|
|
|
18904 |
space = statements.substring(lastIndex, nextStatement.lastIndex - m[4].length);
|
|
|
18905 |
if(trim(space).length !== 0) { continue; } // avoiding new type[] {} construct
|
|
|
18906 |
res.push(space);
|
|
|
18907 |
var kind = m[4].charAt(1), atomIndex = m[5];
|
|
|
18908 |
if(kind === 'D') {
|
|
|
18909 |
res.push(transformMethod(atoms[atomIndex]));
|
|
|
18910 |
} else if(kind === 'E') {
|
|
|
18911 |
res.push(transformClass(atoms[atomIndex]));
|
|
|
18912 |
} else if(kind === 'H') {
|
|
|
18913 |
res.push(transformFunction(atoms[atomIndex]));
|
|
|
18914 |
} else {
|
|
|
18915 |
res.push(transformStatementsBlock(atoms[atomIndex]));
|
|
|
18916 |
}
|
|
|
18917 |
} else if(m[6] !== undef) { // switch case
|
|
|
18918 |
res.push(new AstSwitchCase(transformExpression(trim(m[7]))));
|
|
|
18919 |
} else if(m[8] !== undef) { // label
|
|
|
18920 |
space = statements.substring(lastIndex, nextStatement.lastIndex - m[8].length);
|
|
|
18921 |
if(trim(space).length !== 0) { continue; } // avoiding ?: construct
|
|
|
18922 |
res.push(new AstLabel(statements.substring(lastIndex, nextStatement.lastIndex)) );
|
|
|
18923 |
} else { // semicolon
|
|
|
18924 |
var statement = trimSpaces(statements.substring(lastIndex, nextStatement.lastIndex - 1));
|
|
|
18925 |
res.push(statement.left);
|
|
|
18926 |
res.push(transformStatement(statement.middle));
|
|
|
18927 |
res.push(statement.right + ";");
|
|
|
18928 |
}
|
|
|
18929 |
lastIndex = nextStatement.lastIndex;
|
|
|
18930 |
}
|
|
|
18931 |
var statementsTail = trimSpaces(statements.substring(lastIndex));
|
|
|
18932 |
res.push(statementsTail.left);
|
|
|
18933 |
if(statementsTail.middle !== "") {
|
|
|
18934 |
res.push(transformStatement(statementsTail.middle));
|
|
|
18935 |
res.push(";" + statementsTail.right);
|
|
|
18936 |
}
|
|
|
18937 |
return res;
|
|
|
18938 |
};
|
|
|
18939 |
|
|
|
18940 |
function getLocalNames(statements) {
|
|
|
18941 |
var localNames = [];
|
|
|
18942 |
for(var i=0,l=statements.length;i<l;++i) {
|
|
|
18943 |
var statement = statements[i];
|
|
|
18944 |
if(statement instanceof AstVar) {
|
|
|
18945 |
localNames = localNames.concat(statement.getNames());
|
|
|
18946 |
} else if(statement instanceof AstForStatement &&
|
|
|
18947 |
statement.argument.initStatement instanceof AstVar) {
|
|
|
18948 |
localNames = localNames.concat(statement.argument.initStatement.getNames());
|
|
|
18949 |
} else if(statement instanceof AstInnerInterface || statement instanceof AstInnerClass ||
|
|
|
18950 |
statement instanceof AstInterface || statement instanceof AstClass ||
|
|
|
18951 |
statement instanceof AstMethod || statement instanceof AstFunction) {
|
|
|
18952 |
localNames.push(statement.name);
|
|
|
18953 |
}
|
|
|
18954 |
}
|
|
|
18955 |
return appendToLookupTable({}, localNames);
|
|
|
18956 |
}
|
|
|
18957 |
|
|
|
18958 |
function AstStatementsBlock(statements) {
|
|
|
18959 |
this.statements = statements;
|
|
|
18960 |
}
|
|
|
18961 |
AstStatementsBlock.prototype.toString = function() {
|
|
|
18962 |
var localNames = getLocalNames(this.statements);
|
|
|
18963 |
var oldContext = replaceContext;
|
|
|
18964 |
|
|
|
18965 |
// replacing context only when necessary
|
|
|
18966 |
if(!isLookupTableEmpty(localNames)) {
|
|
|
18967 |
replaceContext = function (subject) {
|
|
|
18968 |
return localNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
|
|
|
18969 |
};
|
|
|
18970 |
}
|
|
|
18971 |
|
|
|
18972 |
var result = "{\n" + this.statements.join('') + "\n}";
|
|
|
18973 |
replaceContext = oldContext;
|
|
|
18974 |
return result;
|
|
|
18975 |
};
|
|
|
18976 |
|
|
|
18977 |
transformStatementsBlock = function(block) {
|
|
|
18978 |
var content = trimSpaces(block.substring(1, block.length - 1));
|
|
|
18979 |
return new AstStatementsBlock(transformStatements(content.middle));
|
|
|
18980 |
};
|
|
|
18981 |
|
|
|
18982 |
function AstRoot(statements) {
|
|
|
18983 |
this.statements = statements;
|
|
|
18984 |
}
|
|
|
18985 |
AstRoot.prototype.toString = function() {
|
|
|
18986 |
var classes = [], otherStatements = [], statement;
|
|
|
18987 |
for (var i = 0, len = this.statements.length; i < len; ++i) {
|
|
|
18988 |
statement = this.statements[i];
|
|
|
18989 |
if (statement instanceof AstClass || statement instanceof AstInterface) {
|
|
|
18990 |
classes.push(statement);
|
|
|
18991 |
} else {
|
|
|
18992 |
otherStatements.push(statement);
|
|
|
18993 |
}
|
|
|
18994 |
}
|
|
|
18995 |
sortByWeight(classes);
|
|
|
18996 |
|
|
|
18997 |
var localNames = getLocalNames(this.statements);
|
|
|
18998 |
replaceContext = function (subject) {
|
|
|
18999 |
var name = subject.name;
|
|
|
19000 |
if(localNames.hasOwnProperty(name)) {
|
|
|
19001 |
return name;
|
|
|
19002 |
}
|
|
|
19003 |
if(globalMembers.hasOwnProperty(name) ||
|
|
|
19004 |
PConstants.hasOwnProperty(name) ||
|
|
|
19005 |
defaultScope.hasOwnProperty(name)) {
|
|
|
19006 |
return "$p." + name;
|
|
|
19007 |
}
|
|
|
19008 |
return name;
|
|
|
19009 |
};
|
|
|
19010 |
var result = "// this code was autogenerated from PJS\n" +
|
|
|
19011 |
"(function($p) {\n" +
|
|
|
19012 |
classes.join('') + "\n" +
|
|
|
19013 |
otherStatements.join('') + "\n})";
|
|
|
19014 |
replaceContext = null;
|
|
|
19015 |
return result;
|
|
|
19016 |
};
|
|
|
19017 |
|
|
|
19018 |
transformMain = function() {
|
|
|
19019 |
var statements = extractClassesAndMethods(atoms[0]);
|
|
|
19020 |
statements = statements.replace(/\bimport\s+[^;]+;/g, "");
|
|
|
19021 |
return new AstRoot( transformStatements(statements,
|
|
|
19022 |
transformGlobalMethod, transformGlobalClass) );
|
|
|
19023 |
};
|
|
|
19024 |
|
|
|
19025 |
function generateMetadata(ast) {
|
|
|
19026 |
var globalScope = {};
|
|
|
19027 |
var id, class_;
|
|
|
19028 |
for(id in declaredClasses) {
|
|
|
19029 |
if(declaredClasses.hasOwnProperty(id)) {
|
|
|
19030 |
class_ = declaredClasses[id];
|
|
|
19031 |
var scopeId = class_.scopeId, name = class_.name;
|
|
|
19032 |
if(scopeId) {
|
|
|
19033 |
var scope = declaredClasses[scopeId];
|
|
|
19034 |
class_.scope = scope;
|
|
|
19035 |
if(scope.inScope === undef) {
|
|
|
19036 |
scope.inScope = {};
|
|
|
19037 |
}
|
|
|
19038 |
scope.inScope[name] = class_;
|
|
|
19039 |
} else {
|
|
|
19040 |
globalScope[name] = class_;
|
|
|
19041 |
}
|
|
|
19042 |
}
|
|
|
19043 |
}
|
|
|
19044 |
|
|
|
19045 |
function findInScopes(class_, name) {
|
|
|
19046 |
var parts = name.split('.');
|
|
|
19047 |
var currentScope = class_.scope, found;
|
|
|
19048 |
while(currentScope) {
|
|
|
19049 |
if(currentScope.hasOwnProperty(parts[0])) {
|
|
|
19050 |
found = currentScope[parts[0]]; break;
|
|
|
19051 |
}
|
|
|
19052 |
currentScope = currentScope.scope;
|
|
|
19053 |
}
|
|
|
19054 |
if(found === undef) {
|
|
|
19055 |
found = globalScope[parts[0]];
|
|
|
19056 |
}
|
|
|
19057 |
for(var i=1,l=parts.length;i<l && found;++i) {
|
|
|
19058 |
found = found.inScope[parts[i]];
|
|
|
19059 |
}
|
|
|
19060 |
return found;
|
|
|
19061 |
}
|
|
|
19062 |
|
|
|
19063 |
for(id in declaredClasses) {
|
|
|
19064 |
if(declaredClasses.hasOwnProperty(id)) {
|
|
|
19065 |
class_ = declaredClasses[id];
|
|
|
19066 |
var baseClassName = class_.body.baseClassName;
|
|
|
19067 |
if(baseClassName) {
|
|
|
19068 |
var parent = findInScopes(class_, baseClassName);
|
|
|
19069 |
if (parent) {
|
|
|
19070 |
class_.base = parent;
|
|
|
19071 |
if (!parent.derived) {
|
|
|
19072 |
parent.derived = [];
|
|
|
19073 |
}
|
|
|
19074 |
parent.derived.push(class_);
|
|
|
19075 |
}
|
|
|
19076 |
}
|
|
|
19077 |
var interfacesNames = class_.body.interfacesNames,
|
|
|
19078 |
interfaces = [], i, l;
|
|
|
19079 |
if (interfacesNames && interfacesNames.length > 0) {
|
|
|
19080 |
for (i = 0, l = interfacesNames.length; i < l; ++i) {
|
|
|
19081 |
var interface_ = findInScopes(class_, interfacesNames[i]);
|
|
|
19082 |
interfaces.push(interface_);
|
|
|
19083 |
if (!interface_) {
|
|
|
19084 |
continue;
|
|
|
19085 |
}
|
|
|
19086 |
if (!interface_.derived) {
|
|
|
19087 |
interface_.derived = [];
|
|
|
19088 |
}
|
|
|
19089 |
interface_.derived.push(class_);
|
|
|
19090 |
}
|
|
|
19091 |
if (interfaces.length > 0) {
|
|
|
19092 |
class_.interfaces = interfaces;
|
|
|
19093 |
}
|
|
|
19094 |
}
|
|
|
19095 |
}
|
|
|
19096 |
}
|
|
|
19097 |
}
|
|
|
19098 |
|
|
|
19099 |
function setWeight(ast) {
|
|
|
19100 |
var queue = [], tocheck = {};
|
|
|
19101 |
var id, scopeId, class_;
|
|
|
19102 |
// queue most inner and non-inherited
|
|
|
19103 |
for (id in declaredClasses) {
|
|
|
19104 |
if (declaredClasses.hasOwnProperty(id)) {
|
|
|
19105 |
class_ = declaredClasses[id];
|
|
|
19106 |
if (!class_.inScope && !class_.derived) {
|
|
|
19107 |
queue.push(id);
|
|
|
19108 |
class_.weight = 0;
|
|
|
19109 |
} else {
|
|
|
19110 |
var dependsOn = [];
|
|
|
19111 |
if (class_.inScope) {
|
|
|
19112 |
for (scopeId in class_.inScope) {
|
|
|
19113 |
if (class_.inScope.hasOwnProperty(scopeId)) {
|
|
|
19114 |
dependsOn.push(class_.inScope[scopeId]);
|
|
|
19115 |
}
|
|
|
19116 |
}
|
|
|
19117 |
}
|
|
|
19118 |
if (class_.derived) {
|
|
|
19119 |
dependsOn = dependsOn.concat(class_.derived);
|
|
|
19120 |
}
|
|
|
19121 |
tocheck[id] = dependsOn;
|
|
|
19122 |
}
|
|
|
19123 |
}
|
|
|
19124 |
}
|
|
|
19125 |
function removeDependentAndCheck(targetId, from) {
|
|
|
19126 |
var dependsOn = tocheck[targetId];
|
|
|
19127 |
if (!dependsOn) {
|
|
|
19128 |
return false; // no need to process
|
|
|
19129 |
}
|
|
|
19130 |
var i = dependsOn.indexOf(from);
|
|
|
19131 |
if (i < 0) {
|
|
|
19132 |
return false;
|
|
|
19133 |
}
|
|
|
19134 |
dependsOn.splice(i, 1);
|
|
|
19135 |
if (dependsOn.length > 0) {
|
|
|
19136 |
return false;
|
|
|
19137 |
}
|
|
|
19138 |
delete tocheck[targetId];
|
|
|
19139 |
return true;
|
|
|
19140 |
}
|
|
|
19141 |
while (queue.length > 0) {
|
|
|
19142 |
id = queue.shift();
|
|
|
19143 |
class_ = declaredClasses[id];
|
|
|
19144 |
if (class_.scopeId && removeDependentAndCheck(class_.scopeId, class_)) {
|
|
|
19145 |
queue.push(class_.scopeId);
|
|
|
19146 |
declaredClasses[class_.scopeId].weight = class_.weight + 1;
|
|
|
19147 |
}
|
|
|
19148 |
if (class_.base && removeDependentAndCheck(class_.base.classId, class_)) {
|
|
|
19149 |
queue.push(class_.base.classId);
|
|
|
19150 |
class_.base.weight = class_.weight + 1;
|
|
|
19151 |
}
|
|
|
19152 |
if (class_.interfaces) {
|
|
|
19153 |
var i, l;
|
|
|
19154 |
for (i = 0, l = class_.interfaces.length; i < l; ++i) {
|
|
|
19155 |
if (!class_.interfaces[i] ||
|
|
|
19156 |
!removeDependentAndCheck(class_.interfaces[i].classId, class_)) {
|
|
|
19157 |
continue;
|
|
|
19158 |
}
|
|
|
19159 |
queue.push(class_.interfaces[i].classId);
|
|
|
19160 |
class_.interfaces[i].weight = class_.weight + 1;
|
|
|
19161 |
}
|
|
|
19162 |
}
|
|
|
19163 |
}
|
|
|
19164 |
}
|
|
|
19165 |
|
|
|
19166 |
var transformed = transformMain();
|
|
|
19167 |
generateMetadata(transformed);
|
|
|
19168 |
setWeight(transformed);
|
|
|
19169 |
|
|
|
19170 |
var redendered = transformed.toString();
|
|
|
19171 |
|
|
|
19172 |
// remove empty extra lines with space
|
|
|
19173 |
redendered = redendered.replace(/\s*\n(?:[\t ]*\n)+/g, "\n\n");
|
|
|
19174 |
|
|
|
19175 |
return injectStrings(redendered, strings);
|
|
|
19176 |
}// Parser ends
|
|
|
19177 |
|
|
|
19178 |
function preprocessCode(aCode, sketch) {
|
|
|
19179 |
// Parse out @pjs directive, if any.
|
|
|
19180 |
var dm = new RegExp(/\/\*\s*@pjs\s+((?:[^\*]|\*+[^\*\/])*)\*\//g).exec(aCode);
|
|
|
19181 |
if (dm && dm.length === 2) {
|
|
|
19182 |
// masks contents of a JSON to be replaced later
|
|
|
19183 |
// to protect the contents from further parsing
|
|
|
19184 |
var jsonItems = [],
|
|
|
19185 |
directives = dm.splice(1, 2)[0].replace(/\{([\s\S]*?)\}/g, (function() {
|
|
|
19186 |
return function(all, item) {
|
|
|
19187 |
jsonItems.push(item);
|
|
|
19188 |
return "{" + (jsonItems.length-1) + "}";
|
|
|
19189 |
};
|
|
|
19190 |
}())).replace('\n', '').replace('\r', '').split(";");
|
|
|
19191 |
|
|
|
19192 |
// We'll L/RTrim, and also remove any surrounding double quotes (e.g., just take string contents)
|
|
|
19193 |
var clean = function(s) {
|
|
|
19194 |
return s.replace(/^\s*["']?/, '').replace(/["']?\s*$/, '');
|
|
|
19195 |
};
|
|
|
19196 |
|
|
|
19197 |
for (var i = 0, dl = directives.length; i < dl; i++) {
|
|
|
19198 |
var pair = directives[i].split('=');
|
|
|
19199 |
if (pair && pair.length === 2) {
|
|
|
19200 |
var key = clean(pair[0]),
|
|
|
19201 |
value = clean(pair[1]),
|
|
|
19202 |
list = [];
|
|
|
19203 |
// A few directives require work beyond storying key/value pairings
|
|
|
19204 |
if (key === "preload") {
|
|
|
19205 |
list = value.split(',');
|
|
|
19206 |
// All pre-loaded images will get put in imageCache, keyed on filename
|
|
|
19207 |
for (var j = 0, jl = list.length; j < jl; j++) {
|
|
|
19208 |
var imageName = clean(list[j]);
|
|
|
19209 |
sketch.imageCache.add(imageName);
|
|
|
19210 |
}
|
|
|
19211 |
// fonts can be declared as a string containing a url,
|
|
|
19212 |
// or a JSON object, containing a font name, and a url
|
|
|
19213 |
} else if (key === "font") {
|
|
|
19214 |
list = value.split(",");
|
|
|
19215 |
for (var x = 0, xl = list.length; x < xl; x++) {
|
|
|
19216 |
var fontName = clean(list[x]),
|
|
|
19217 |
index = /^\{(\d*?)\}$/.exec(fontName);
|
|
|
19218 |
// if index is not null, send JSON, otherwise, send string
|
|
|
19219 |
PFont.preloading.add(index ? JSON.parse("{" + jsonItems[index[1]] + "}") : fontName);
|
|
|
19220 |
}
|
|
|
19221 |
} else if (key === "pauseOnBlur") {
|
|
|
19222 |
sketch.options.pauseOnBlur = value === "true";
|
|
|
19223 |
} else if (key === "globalKeyEvents") {
|
|
|
19224 |
sketch.options.globalKeyEvents = value === "true";
|
|
|
19225 |
} else if (key.substring(0, 6) === "param-") {
|
|
|
19226 |
sketch.params[key.substring(6)] = value;
|
|
|
19227 |
} else {
|
|
|
19228 |
sketch.options[key] = value;
|
|
|
19229 |
}
|
|
|
19230 |
}
|
|
|
19231 |
}
|
|
|
19232 |
}
|
|
|
19233 |
return aCode;
|
|
|
19234 |
}
|
|
|
19235 |
|
|
|
19236 |
// Parse/compiles Processing (Java-like) syntax to JavaScript syntax
|
|
|
19237 |
Processing.compile = function(pdeCode) {
|
|
|
19238 |
var sketch = new Processing.Sketch();
|
|
|
19239 |
var code = preprocessCode(pdeCode, sketch);
|
|
|
19240 |
var compiledPde = parseProcessing(code);
|
|
|
19241 |
sketch.sourceCode = compiledPde;
|
|
|
19242 |
return sketch;
|
|
|
19243 |
};
|
|
|
19244 |
//#endif
|
|
|
19245 |
|
|
|
19246 |
// tinylog lite JavaScript library
|
|
|
19247 |
// http://purl.eligrey.com/tinylog/lite
|
|
|
19248 |
/*global tinylog,print*/
|
|
|
19249 |
var tinylogLite = (function() {
|
|
|
19250 |
"use strict";
|
|
|
19251 |
|
|
|
19252 |
var tinylogLite = {},
|
|
|
19253 |
undef = "undefined",
|
|
|
19254 |
func = "function",
|
|
|
19255 |
False = !1,
|
|
|
19256 |
True = !0,
|
|
|
19257 |
logLimit = 512,
|
|
|
19258 |
log = "log";
|
|
|
19259 |
|
|
|
19260 |
if (typeof tinylog !== undef && typeof tinylog[log] === func) {
|
|
|
19261 |
// pre-existing tinylog present
|
|
|
19262 |
tinylogLite[log] = tinylog[log];
|
|
|
19263 |
} else if (typeof document !== undef && !document.fake) {
|
|
|
19264 |
(function() {
|
|
|
19265 |
// DOM document
|
|
|
19266 |
var doc = document,
|
|
|
19267 |
|
|
|
19268 |
$div = "div",
|
|
|
19269 |
$style = "style",
|
|
|
19270 |
$title = "title",
|
|
|
19271 |
|
|
|
19272 |
containerStyles = {
|
|
|
19273 |
zIndex: 10000,
|
|
|
19274 |
position: "fixed",
|
|
|
19275 |
bottom: "0px",
|
|
|
19276 |
width: "100%",
|
|
|
19277 |
height: "15%",
|
|
|
19278 |
fontFamily: "sans-serif",
|
|
|
19279 |
color: "#ccc",
|
|
|
19280 |
backgroundColor: "black"
|
|
|
19281 |
},
|
|
|
19282 |
outputStyles = {
|
|
|
19283 |
position: "relative",
|
|
|
19284 |
fontFamily: "monospace",
|
|
|
19285 |
overflow: "auto",
|
|
|
19286 |
height: "100%",
|
|
|
19287 |
paddingTop: "5px"
|
|
|
19288 |
},
|
|
|
19289 |
resizerStyles = {
|
|
|
19290 |
height: "5px",
|
|
|
19291 |
marginTop: "-5px",
|
|
|
19292 |
cursor: "n-resize",
|
|
|
19293 |
backgroundColor: "darkgrey"
|
|
|
19294 |
},
|
|
|
19295 |
closeButtonStyles = {
|
|
|
19296 |
position: "absolute",
|
|
|
19297 |
top: "5px",
|
|
|
19298 |
right: "20px",
|
|
|
19299 |
color: "#111",
|
|
|
19300 |
MozBorderRadius: "4px",
|
|
|
19301 |
webkitBorderRadius: "4px",
|
|
|
19302 |
borderRadius: "4px",
|
|
|
19303 |
cursor: "pointer",
|
|
|
19304 |
fontWeight: "normal",
|
|
|
19305 |
textAlign: "center",
|
|
|
19306 |
padding: "3px 5px",
|
|
|
19307 |
backgroundColor: "#333",
|
|
|
19308 |
fontSize: "12px"
|
|
|
19309 |
},
|
|
|
19310 |
entryStyles = {
|
|
|
19311 |
//borderBottom: "1px solid #d3d3d3",
|
|
|
19312 |
minHeight: "16px"
|
|
|
19313 |
},
|
|
|
19314 |
entryTextStyles = {
|
|
|
19315 |
fontSize: "12px",
|
|
|
19316 |
margin: "0 8px 0 8px",
|
|
|
19317 |
maxWidth: "100%",
|
|
|
19318 |
whiteSpace: "pre-wrap",
|
|
|
19319 |
overflow: "auto"
|
|
|
19320 |
},
|
|
|
19321 |
|
|
|
19322 |
view = doc.defaultView,
|
|
|
19323 |
docElem = doc.documentElement,
|
|
|
19324 |
docElemStyle = docElem[$style],
|
|
|
19325 |
|
|
|
19326 |
setStyles = function() {
|
|
|
19327 |
var i = arguments.length,
|
|
|
19328 |
elemStyle, styles, style;
|
|
|
19329 |
|
|
|
19330 |
while (i--) {
|
|
|
19331 |
styles = arguments[i--];
|
|
|
19332 |
elemStyle = arguments[i][$style];
|
|
|
19333 |
|
|
|
19334 |
for (style in styles) {
|
|
|
19335 |
if (styles.hasOwnProperty(style)) {
|
|
|
19336 |
elemStyle[style] = styles[style];
|
|
|
19337 |
}
|
|
|
19338 |
}
|
|
|
19339 |
}
|
|
|
19340 |
},
|
|
|
19341 |
|
|
|
19342 |
observer = function(obj, event, handler) {
|
|
|
19343 |
if (obj.addEventListener) {
|
|
|
19344 |
obj.addEventListener(event, handler, False);
|
|
|
19345 |
} else if (obj.attachEvent) {
|
|
|
19346 |
obj.attachEvent("on" + event, handler);
|
|
|
19347 |
}
|
|
|
19348 |
return [obj, event, handler];
|
|
|
19349 |
},
|
|
|
19350 |
unobserve = function(obj, event, handler) {
|
|
|
19351 |
if (obj.removeEventListener) {
|
|
|
19352 |
obj.removeEventListener(event, handler, False);
|
|
|
19353 |
} else if (obj.detachEvent) {
|
|
|
19354 |
obj.detachEvent("on" + event, handler);
|
|
|
19355 |
}
|
|
|
19356 |
},
|
|
|
19357 |
clearChildren = function(node) {
|
|
|
19358 |
var children = node.childNodes,
|
|
|
19359 |
child = children.length;
|
|
|
19360 |
|
|
|
19361 |
while (child--) {
|
|
|
19362 |
node.removeChild(children.item(0));
|
|
|
19363 |
}
|
|
|
19364 |
},
|
|
|
19365 |
append = function(to, elem) {
|
|
|
19366 |
return to.appendChild(elem);
|
|
|
19367 |
},
|
|
|
19368 |
createElement = function(localName) {
|
|
|
19369 |
return doc.createElement(localName);
|
|
|
19370 |
},
|
|
|
19371 |
createTextNode = function(text) {
|
|
|
19372 |
return doc.createTextNode(text);
|
|
|
19373 |
},
|
|
|
19374 |
|
|
|
19375 |
createLog = tinylogLite[log] = function(message) {
|
|
|
19376 |
// don't show output log until called once
|
|
|
19377 |
var uninit,
|
|
|
19378 |
originalPadding = docElemStyle.paddingBottom,
|
|
|
19379 |
container = createElement($div),
|
|
|
19380 |
containerStyle = container[$style],
|
|
|
19381 |
resizer = append(container, createElement($div)),
|
|
|
19382 |
output = append(container, createElement($div)),
|
|
|
19383 |
closeButton = append(container, createElement($div)),
|
|
|
19384 |
resizingLog = False,
|
|
|
19385 |
previousHeight = False,
|
|
|
19386 |
previousScrollTop = False,
|
|
|
19387 |
messages = 0,
|
|
|
19388 |
|
|
|
19389 |
updateSafetyMargin = function() {
|
|
|
19390 |
// have a blank space large enough to fit the output box at the page bottom
|
|
|
19391 |
docElemStyle.paddingBottom = container.clientHeight + "px";
|
|
|
19392 |
},
|
|
|
19393 |
setContainerHeight = function(height) {
|
|
|
19394 |
var viewHeight = view.innerHeight,
|
|
|
19395 |
resizerHeight = resizer.clientHeight;
|
|
|
19396 |
|
|
|
19397 |
// constrain the container inside the viewport's dimensions
|
|
|
19398 |
if (height < 0) {
|
|
|
19399 |
height = 0;
|
|
|
19400 |
} else if (height + resizerHeight > viewHeight) {
|
|
|
19401 |
height = viewHeight - resizerHeight;
|
|
|
19402 |
}
|
|
|
19403 |
|
|
|
19404 |
containerStyle.height = height / viewHeight * 100 + "%";
|
|
|
19405 |
|
|
|
19406 |
updateSafetyMargin();
|
|
|
19407 |
},
|
|
|
19408 |
observers = [
|
|
|
19409 |
observer(doc, "mousemove", function(evt) {
|
|
|
19410 |
if (resizingLog) {
|
|
|
19411 |
setContainerHeight(view.innerHeight - evt.clientY);
|
|
|
19412 |
output.scrollTop = previousScrollTop;
|
|
|
19413 |
}
|
|
|
19414 |
}),
|
|
|
19415 |
|
|
|
19416 |
observer(doc, "mouseup", function() {
|
|
|
19417 |
if (resizingLog) {
|
|
|
19418 |
resizingLog = previousScrollTop = False;
|
|
|
19419 |
}
|
|
|
19420 |
}),
|
|
|
19421 |
|
|
|
19422 |
observer(resizer, "dblclick", function(evt) {
|
|
|
19423 |
evt.preventDefault();
|
|
|
19424 |
|
|
|
19425 |
if (previousHeight) {
|
|
|
19426 |
setContainerHeight(previousHeight);
|
|
|
19427 |
previousHeight = False;
|
|
|
19428 |
} else {
|
|
|
19429 |
previousHeight = container.clientHeight;
|
|
|
19430 |
containerStyle.height = "0px";
|
|
|
19431 |
}
|
|
|
19432 |
}),
|
|
|
19433 |
|
|
|
19434 |
observer(resizer, "mousedown", function(evt) {
|
|
|
19435 |
evt.preventDefault();
|
|
|
19436 |
resizingLog = True;
|
|
|
19437 |
previousScrollTop = output.scrollTop;
|
|
|
19438 |
}),
|
|
|
19439 |
|
|
|
19440 |
observer(resizer, "contextmenu", function() {
|
|
|
19441 |
resizingLog = False;
|
|
|
19442 |
}),
|
|
|
19443 |
|
|
|
19444 |
observer(closeButton, "click", function() {
|
|
|
19445 |
uninit();
|
|
|
19446 |
})
|
|
|
19447 |
];
|
|
|
19448 |
|
|
|
19449 |
uninit = function() {
|
|
|
19450 |
// remove observers
|
|
|
19451 |
var i = observers.length;
|
|
|
19452 |
|
|
|
19453 |
while (i--) {
|
|
|
19454 |
unobserve.apply(tinylogLite, observers[i]);
|
|
|
19455 |
}
|
|
|
19456 |
|
|
|
19457 |
// remove tinylog lite from the DOM
|
|
|
19458 |
docElem.removeChild(container);
|
|
|
19459 |
docElemStyle.paddingBottom = originalPadding;
|
|
|
19460 |
|
|
|
19461 |
clearChildren(output);
|
|
|
19462 |
clearChildren(container);
|
|
|
19463 |
|
|
|
19464 |
tinylogLite[log] = createLog;
|
|
|
19465 |
};
|
|
|
19466 |
|
|
|
19467 |
setStyles(
|
|
|
19468 |
container, containerStyles, output, outputStyles, resizer, resizerStyles, closeButton, closeButtonStyles);
|
|
|
19469 |
|
|
|
19470 |
closeButton[$title] = "Close Log";
|
|
|
19471 |
append(closeButton, createTextNode("\u2716"));
|
|
|
19472 |
|
|
|
19473 |
resizer[$title] = "Double-click to toggle log minimization";
|
|
|
19474 |
|
|
|
19475 |
docElem.insertBefore(container, docElem.firstChild);
|
|
|
19476 |
|
|
|
19477 |
tinylogLite[log] = function(message) {
|
|
|
19478 |
if (messages === logLimit) {
|
|
|
19479 |
output.removeChild(output.firstChild);
|
|
|
19480 |
} else {
|
|
|
19481 |
messages++;
|
|
|
19482 |
}
|
|
|
19483 |
|
|
|
19484 |
var entry = append(output, createElement($div)),
|
|
|
19485 |
entryText = append(entry, createElement($div));
|
|
|
19486 |
|
|
|
19487 |
entry[$title] = (new Date()).toLocaleTimeString();
|
|
|
19488 |
|
|
|
19489 |
setStyles(
|
|
|
19490 |
entry, entryStyles, entryText, entryTextStyles);
|
|
|
19491 |
|
|
|
19492 |
append(entryText, createTextNode(message));
|
|
|
19493 |
output.scrollTop = output.scrollHeight;
|
|
|
19494 |
};
|
|
|
19495 |
|
|
|
19496 |
tinylogLite[log](message);
|
|
|
19497 |
updateSafetyMargin();
|
|
|
19498 |
};
|
|
|
19499 |
}());
|
|
|
19500 |
} else if (typeof print === func) { // JS shell
|
|
|
19501 |
tinylogLite[log] = print;
|
|
|
19502 |
}
|
|
|
19503 |
|
|
|
19504 |
return tinylogLite;
|
|
|
19505 |
}());
|
|
|
19506 |
// end of tinylog lite JavaScript library
|
|
|
19507 |
|
|
|
19508 |
Processing.logger = tinylogLite;
|
|
|
19509 |
|
|
|
19510 |
Processing.version = "@VERSION@";
|
|
|
19511 |
|
|
|
19512 |
// Share lib space
|
|
|
19513 |
Processing.lib = {};
|
|
|
19514 |
|
|
|
19515 |
Processing.registerLibrary = function(name, desc) {
|
|
|
19516 |
Processing.lib[name] = desc;
|
|
|
19517 |
|
|
|
19518 |
if(desc.hasOwnProperty("init")) {
|
|
|
19519 |
desc.init(defaultScope);
|
|
|
19520 |
}
|
|
|
19521 |
};
|
|
|
19522 |
|
|
|
19523 |
// Store Processing instances. Only Processing.instances,
|
|
|
19524 |
// Processing.getInstanceById are exposed.
|
|
|
19525 |
Processing.instances = processingInstances;
|
|
|
19526 |
|
|
|
19527 |
Processing.getInstanceById = function(name) {
|
|
|
19528 |
return processingInstances[processingInstanceIds[name]];
|
|
|
19529 |
};
|
|
|
19530 |
|
|
|
19531 |
Processing.Sketch = function(attachFunction) {
|
|
|
19532 |
this.attachFunction = attachFunction; // can be optional
|
|
|
19533 |
this.options = {
|
|
|
19534 |
pauseOnBlur: false,
|
|
|
19535 |
globalKeyEvents: false
|
|
|
19536 |
};
|
|
|
19537 |
|
|
|
19538 |
/* Optional Sketch event hooks:
|
|
|
19539 |
* onLoad - parsing/preloading is done, before sketch starts
|
|
|
19540 |
* onSetup - setup() has been called, before first draw()
|
|
|
19541 |
* onPause - noLoop() has been called, pausing draw loop
|
|
|
19542 |
* onLoop - loop() has been called, resuming draw loop
|
|
|
19543 |
* onFrameStart - draw() loop about to begin
|
|
|
19544 |
* onFrameEnd - draw() loop finished
|
|
|
19545 |
* onExit - exit() done being called
|
|
|
19546 |
*/
|
|
|
19547 |
this.onLoad = nop;
|
|
|
19548 |
this.onSetup = nop;
|
|
|
19549 |
this.onPause = nop;
|
|
|
19550 |
this.onLoop = nop;
|
|
|
19551 |
this.onFrameStart = nop;
|
|
|
19552 |
this.onFrameEnd = nop;
|
|
|
19553 |
this.onExit = nop;
|
|
|
19554 |
|
|
|
19555 |
this.params = {};
|
|
|
19556 |
this.imageCache = {
|
|
|
19557 |
pending: 0,
|
|
|
19558 |
images: {},
|
|
|
19559 |
// Opera requires special administration for preloading
|
|
|
19560 |
operaCache: {},
|
|
|
19561 |
// Specify an optional img arg if the image is already loaded in the DOM,
|
|
|
19562 |
// otherwise href will get loaded.
|
|
|
19563 |
add: function(href, img) {
|
|
|
19564 |
// Prevent muliple loads for an image, in case it gets
|
|
|
19565 |
// preloaded more than once, or is added via JS and then preloaded.
|
|
|
19566 |
if (this.images[href]) {
|
|
|
19567 |
return;
|
|
|
19568 |
}
|
|
|
19569 |
|
|
|
19570 |
if (!isDOMPresent) {
|
|
|
19571 |
this.images[href] = null;
|
|
|
19572 |
}
|
|
|
19573 |
|
|
|
19574 |
// No image in the DOM, kick-off a background load
|
|
|
19575 |
if (!img) {
|
|
|
19576 |
img = new Image();
|
|
|
19577 |
img.onload = (function(owner) {
|
|
|
19578 |
return function() {
|
|
|
19579 |
owner.pending--;
|
|
|
19580 |
};
|
|
|
19581 |
}(this));
|
|
|
19582 |
this.pending++;
|
|
|
19583 |
img.src = href;
|
|
|
19584 |
}
|
|
|
19585 |
|
|
|
19586 |
this.images[href] = img;
|
|
|
19587 |
|
|
|
19588 |
// Opera will not load images until they are inserted into the DOM.
|
|
|
19589 |
if (window.opera) {
|
|
|
19590 |
var div = document.createElement("div");
|
|
|
19591 |
div.appendChild(img);
|
|
|
19592 |
// we can't use "display: none", since that makes it invisible, and thus not load
|
|
|
19593 |
div.style.position = "absolute";
|
|
|
19594 |
div.style.opacity = 0;
|
|
|
19595 |
div.style.width = "1px";
|
|
|
19596 |
div.style.height= "1px";
|
|
|
19597 |
if (!this.operaCache[href]) {
|
|
|
19598 |
document.body.appendChild(div);
|
|
|
19599 |
this.operaCache[href] = div;
|
|
|
19600 |
}
|
|
|
19601 |
}
|
|
|
19602 |
}
|
|
|
19603 |
};
|
|
|
19604 |
this.sourceCode = undefined;
|
|
|
19605 |
this.attach = function(processing) {
|
|
|
19606 |
// either attachFunction or sourceCode must be present on attach
|
|
|
19607 |
if(typeof this.attachFunction === "function") {
|
|
|
19608 |
this.attachFunction(processing);
|
|
|
19609 |
} else if(this.sourceCode) {
|
|
|
19610 |
var func = ((new Function("return (" + this.sourceCode + ");"))());
|
|
|
19611 |
func(processing);
|
|
|
19612 |
this.attachFunction = func;
|
|
|
19613 |
} else {
|
|
|
19614 |
throw "Unable to attach sketch to the processing instance";
|
|
|
19615 |
}
|
|
|
19616 |
};
|
|
|
19617 |
//#if PARSER
|
|
|
19618 |
this.toString = function() {
|
|
|
19619 |
var i;
|
|
|
19620 |
var code = "((function(Sketch) {\n";
|
|
|
19621 |
code += "var sketch = new Sketch(\n" + this.sourceCode + ");\n";
|
|
|
19622 |
for(i in this.options) {
|
|
|
19623 |
if(this.options.hasOwnProperty(i)) {
|
|
|
19624 |
var value = this.options[i];
|
|
|
19625 |
code += "sketch.options." + i + " = " +
|
|
|
19626 |
(typeof value === 'string' ? '\"' + value + '\"' : "" + value) + ";\n";
|
|
|
19627 |
}
|
|
|
19628 |
}
|
|
|
19629 |
for(i in this.imageCache) {
|
|
|
19630 |
if(this.options.hasOwnProperty(i)) {
|
|
|
19631 |
code += "sketch.imageCache.add(\"" + i + "\");\n";
|
|
|
19632 |
}
|
|
|
19633 |
}
|
|
|
19634 |
// TODO serialize fonts
|
|
|
19635 |
code += "return sketch;\n})(Processing.Sketch))";
|
|
|
19636 |
return code;
|
|
|
19637 |
};
|
|
|
19638 |
//#endif
|
|
|
19639 |
};
|
|
|
19640 |
|
|
|
19641 |
//#if PARSER
|
|
|
19642 |
/**
|
|
|
19643 |
* aggregate all source code into a single file, then rewrite that
|
|
|
19644 |
* source and bind to canvas via new Processing(canvas, sourcestring).
|
|
|
19645 |
* @param {CANVAS} canvas The html canvas element to bind to
|
|
|
19646 |
* @param {String[]} source The array of files that must be loaded
|
|
|
19647 |
*/
|
|
|
19648 |
var loadSketchFromSources = function(canvas, sources) {
|
|
|
19649 |
var code = [], errors = [], sourcesCount = sources.length, loaded = 0;
|
|
|
19650 |
|
|
|
19651 |
function ajaxAsync(url, callback) {
|
|
|
19652 |
var xhr = new XMLHttpRequest();
|
|
|
19653 |
xhr.onreadystatechange = function() {
|
|
|
19654 |
if (xhr.readyState === 4) {
|
|
|
19655 |
var error;
|
|
|
19656 |
if (xhr.status !== 200 && xhr.status !== 0) {
|
|
|
19657 |
error = "Invalid XHR status " + xhr.status;
|
|
|
19658 |
} else if (xhr.responseText === "") {
|
|
|
19659 |
// Give a hint when loading fails due to same-origin issues on file:/// urls
|
|
|
19660 |
if ( ("withCredentials" in new XMLHttpRequest()) &&
|
|
|
19661 |
(new XMLHttpRequest()).withCredentials === false &&
|
|
|
19662 |
window.location.protocol === "file:" ) {
|
|
|
19663 |
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.";
|
|
|
19664 |
} else {
|
|
|
19665 |
error = "File is empty.";
|
|
|
19666 |
}
|
|
|
19667 |
}
|
|
|
19668 |
|
|
|
19669 |
callback(xhr.responseText, error);
|
|
|
19670 |
}
|
|
|
19671 |
};
|
|
|
19672 |
xhr.open("GET", url, true);
|
|
|
19673 |
if (xhr.overrideMimeType) {
|
|
|
19674 |
xhr.overrideMimeType("application/json");
|
|
|
19675 |
}
|
|
|
19676 |
xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT"); // no cache
|
|
|
19677 |
xhr.send(null);
|
|
|
19678 |
}
|
|
|
19679 |
|
|
|
19680 |
function loadBlock(index, filename) {
|
|
|
19681 |
function callback(block, error) {
|
|
|
19682 |
code[index] = block;
|
|
|
19683 |
++loaded;
|
|
|
19684 |
if (error) {
|
|
|
19685 |
errors.push(filename + " ==> " + error);
|
|
|
19686 |
}
|
|
|
19687 |
if (loaded === sourcesCount) {
|
|
|
19688 |
if (errors.length === 0) {
|
|
|
19689 |
try {
|
|
|
19690 |
return new Processing(canvas, code.join("\n"));
|
|
|
19691 |
} catch(e) {
|
|
|
19692 |
throw "Processing.js: Unable to execute pjs sketch: " + e;
|
|
|
19693 |
}
|
|
|
19694 |
} else {
|
|
|
19695 |
throw "Processing.js: Unable to load pjs sketch files: " + errors.join("\n");
|
|
|
19696 |
}
|
|
|
19697 |
}
|
|
|
19698 |
}
|
|
|
19699 |
if (filename.charAt(0) === '#') {
|
|
|
19700 |
// trying to get script from the element
|
|
|
19701 |
var scriptElement = document.getElementById(filename.substring(1));
|
|
|
19702 |
if (scriptElement) {
|
|
|
19703 |
callback(scriptElement.text || scriptElement.textContent);
|
|
|
19704 |
} else {
|
|
|
19705 |
callback("", "Unable to load pjs sketch: element with id \'" + filename.substring(1) + "\' was not found");
|
|
|
19706 |
}
|
|
|
19707 |
return;
|
|
|
19708 |
}
|
|
|
19709 |
|
|
|
19710 |
ajaxAsync(filename, callback);
|
|
|
19711 |
}
|
|
|
19712 |
|
|
|
19713 |
for (var i = 0; i < sourcesCount; ++i) {
|
|
|
19714 |
loadBlock(i, sources[i]);
|
|
|
19715 |
}
|
|
|
19716 |
};
|
|
|
19717 |
|
|
|
19718 |
/**
|
|
|
19719 |
* Automatic initialization function.
|
|
|
19720 |
*/
|
|
|
19721 |
var init = function() {
|
|
|
19722 |
document.removeEventListener('DOMContentLoaded', init, false);
|
|
|
19723 |
|
|
|
19724 |
var canvas = document.getElementsByTagName('canvas'),
|
|
|
19725 |
filenames;
|
|
|
19726 |
|
|
|
19727 |
for (var i = 0, l = canvas.length; i < l; i++) {
|
|
|
19728 |
// datasrc and data-src are deprecated.
|
|
|
19729 |
var processingSources = canvas[i].getAttribute('data-processing-sources');
|
|
|
19730 |
if (processingSources === null) {
|
|
|
19731 |
// Temporary fallback for datasrc and data-src
|
|
|
19732 |
processingSources = canvas[i].getAttribute('data-src');
|
|
|
19733 |
if (processingSources === null) {
|
|
|
19734 |
processingSources = canvas[i].getAttribute('datasrc');
|
|
|
19735 |
}
|
|
|
19736 |
}
|
|
|
19737 |
if (processingSources) {
|
|
|
19738 |
filenames = processingSources.split(' ');
|
|
|
19739 |
for (var j = 0; j < filenames.length;) {
|
|
|
19740 |
if (filenames[j]) {
|
|
|
19741 |
j++;
|
|
|
19742 |
} else {
|
|
|
19743 |
filenames.splice(j, 1);
|
|
|
19744 |
}
|
|
|
19745 |
}
|
|
|
19746 |
loadSketchFromSources(canvas[i], filenames);
|
|
|
19747 |
}
|
|
|
19748 |
}
|
|
|
19749 |
|
|
|
19750 |
// also process all <script>-indicated sketches, if there are any
|
|
|
19751 |
var scripts = document.getElementsByTagName('script');
|
|
|
19752 |
var s, source, instance;
|
|
|
19753 |
for (s = 0; s < scripts.length; s++) {
|
|
|
19754 |
var script = scripts[s];
|
|
|
19755 |
if (!script.getAttribute) {
|
|
|
19756 |
continue;
|
|
|
19757 |
}
|
|
|
19758 |
|
|
|
19759 |
var type = script.getAttribute("type");
|
|
|
19760 |
if (type && (type.toLowerCase() === "text/processing" || type.toLowerCase() === "application/processing")) {
|
|
|
19761 |
var target = script.getAttribute("data-processing-target");
|
|
|
19762 |
canvas = undef;
|
|
|
19763 |
if (target) {
|
|
|
19764 |
canvas = document.getElementById(target);
|
|
|
19765 |
} else {
|
|
|
19766 |
var nextSibling = script.nextSibling;
|
|
|
19767 |
while (nextSibling && nextSibling.nodeType !== 1) {
|
|
|
19768 |
nextSibling = nextSibling.nextSibling;
|
|
|
19769 |
}
|
|
|
19770 |
if (nextSibling.nodeName.toLowerCase() === "canvas") {
|
|
|
19771 |
canvas = nextSibling;
|
|
|
19772 |
}
|
|
|
19773 |
}
|
|
|
19774 |
|
|
|
19775 |
if (canvas) {
|
|
|
19776 |
if (script.getAttribute("src")) {
|
|
|
19777 |
filenames = script.getAttribute("src").split(/\s+/);
|
|
|
19778 |
loadSketchFromSources(canvas, filenames);
|
|
|
19779 |
continue;
|
|
|
19780 |
}
|
|
|
19781 |
source = script.textContent || script.text;
|
|
|
19782 |
instance = new Processing(canvas, source);
|
|
|
19783 |
}
|
|
|
19784 |
}
|
|
|
19785 |
}
|
|
|
19786 |
};
|
|
|
19787 |
|
|
|
19788 |
/**
|
|
|
19789 |
* Make loadSketchFromSources publically visible
|
|
|
19790 |
*/
|
|
|
19791 |
Processing.loadSketchFromSources = loadSketchFromSources;
|
|
|
19792 |
|
|
|
19793 |
/**
|
|
|
19794 |
* Disable the automatic loading of all sketches on the page
|
|
|
19795 |
*/
|
|
|
19796 |
Processing.disableInit = function() {
|
|
|
19797 |
if(isDOMPresent) {
|
|
|
19798 |
document.removeEventListener('DOMContentLoaded', init, false);
|
|
|
19799 |
}
|
|
|
19800 |
};
|
|
|
19801 |
//#endif
|
|
|
19802 |
|
|
|
19803 |
if(isDOMPresent) {
|
|
|
19804 |
window['Processing'] = Processing;
|
|
|
19805 |
//#if PARSER
|
|
|
19806 |
document.addEventListener('DOMContentLoaded', init, false);
|
|
|
19807 |
//#endif
|
|
|
19808 |
} else {
|
|
|
19809 |
// DOM is not found
|
|
|
19810 |
this.Processing = Processing;
|
|
|
19811 |
}
|
|
|
19812 |
}(window, window.document, Math));
|