-
Notifications
You must be signed in to change notification settings - Fork 0
/
canvas.js-e
489 lines (456 loc) · 15.5 KB
/
canvas.js-e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# Copyright (c) 2013, Empirical Modelling Group
# All rights reserved.
#
# See LICENSE.txt
action plugins {
action canvas {
action canvas {
#Initialize observables
mouseButton = "";
mouseButtons = "";
mouseCapture = false;
mouseCaptured = false;
mouseContextMenuEnabled = false;
mouseDoubleClicks = 0;
mouseDown = @;
mouseDownView = @;
mouseDownZone = @;
mouseFollow = false;
mousePosition = @;
mousePressed = false;
mouseUp = @;
mouseWheel = 0;
mouseWheelEnabled = false;
mouseWheelVelocity = 0;
mouseView = @;
mouseX is mousePosition == @? @ : mousePosition["x"];
mouseY is mousePosition == @? @ : mousePosition["y"];
mouseZone = @;
pixel is 1 / view_picture_scale;
touchPanX = 0;
touchPanXSpeed = 0;
touchPinch = 0;
touchPinchEnabled = false;
${{
root.lookup("mouseCapture").addJSObserver("releaseCapture", function (obs, lock) {
if (!lock && document.exitPointerLock) {
document.exitPointerLock();
}
});
}}$;
/**
* Attempts to capture the mouse pointer so the it cannot leave the current canvas window. This
* allows the mouse to be used as a pure sensor and the mousePosition observable becomes able to take
* on any value, free from the constraints of the size of the window. Note that this procedure will
* only work if the window has already been in mouse capture mode previously and the user did not
* exit capture mode using the escape key.
* The sequence of events:
* 1) The model sets the mouseCapture observable to true, meaning that mouse capture is permitted.
* 1) The user clicks in the canvas window to initiate capture mode. The mouseCaptured observable
* automatically gets set to true.
* 1) Capture mode is ended either by (a) the user pressing the escape key, or, (b) the model
* setting the mouseCapture observable to false.
* 1) Calling attemptMouseCapture after case (3b) has occurred will succeed in the mouse pointer
* becoming captured again immediately. However, if case (3a) occurred then calling
* attemptMouseCapture won't do anything. If this situation occurs then the model can still set
* mouseCapture to true and wait for the user to re-grant permission to capture mouse movements
* by clicking in the window again.
*
* Note that at the time of writing (5/2/2015) although the underlying API has received the W3C's
* Candidate Recommendation approval it is currently only implemented in recent versions of Chrome.
* Access to mouse capturing capabilities can be restricted based upon a browser preference setting.
* (In Chrome this is under Settings->Advanced settings->Content settings->Mouse cursor.)
*/
proc attemptMouseCapture {
${{
var canvasName = root.lookup("mouseView").value();
if (canvasName !== undefined) {
canvases[canvasName].requestPointerLock();
}
}}$;
}
/**
* Creates a canvas and a triggered procedure so that canvas display is kept in
* sync with the value of a given observable at all times. The first parameter
* is the name of the observable that provides the list of drawables. The
* second, optional parameter, is the text to place into the window's title bar.
*
*
* If three parameters are provided, the first is the viewName, the second is the
* observable that stores the list of drawables. The third parameter is the text to place
* into the window's title bar.
*
* @param [viewName]
* @param observableName
* @param viewTitle
*
*/
proc createCanvas {
para p1, p2, p3;
auto obsName, viewTitle, viewName;
auto titleSymName, contentSymName, addJSObserver;
auto argc;
argc = ${{ arguments.length }}$;
viewName = @;
viewTitle = @;
if (argc == 1) {
obsName = p1;
} else if (argc == 2) {
obsName = p1;
viewTitle = p2;
} else if (argc == 3) {
viewName = p1;
obsName = p2;
viewTitle = p3;
if (!isString(viewName)) {
error("createCanvas: The first argument must be a string, not a " // type(viewName));
return;
}
} else {
error("createCanvas: This procedure requires at least 1 argument and at most 3 arguments.");
return;
}
if (isPointer(obsName)) {
obsName = nameof(obsName);
} else if (!isString(obsName)) {
error("createCanvas: The observable name must be expressed as a string or a pointer, not a " // type(obsName));
return;
}
if (viewName == @) {
viewName = obsName;
}
if (viewTitle != @ && !isString(viewTitle)) {
error("createCanvas: The window title must be a string, not a " // type(viewTitle));
return;
}
if (`obsName` == @) {
`obsName` = [];
}
createView(viewName, "Canvas2D");
execute2("view_" // viewName // "_content is " // obsName // ";", "*Default");
if (viewTitle == @) {
# don't attempt to make this update by dependency on redefinition of _view_viewName_content.
# Severe performance problems result.
viewTitle = "Canvas " // obsName;
}
`"view_" // viewName // "_title"` = viewTitle;
}
/**
* Returns a string that can be used as the src attribute for an <img> tag inside the content of a
* HTML view. The image with the special URL returned by this function is a snapshot of the contents
* of the canvas at the time this function was called.
* Optional parameter can be used to select which canvas should be used to take the snapshot.
*
* @param [canvasName]
*
*/
func canvasURL {
${{
var canvasName;
if (arguments.length > 1) {
eden.error(new Error("canvasURL: This function requires at most one argument."), "error");
return "";
} else if (arguments.length == 0) {
canvasName = "picture";
} else {
canvasName = arguments[0];
if (canvasName === undefined) {
return "";
} else if (typeof(canvasName) == "string") {
if (!(canvasName in canvases)) {
return "";
}
} else {
eden.error(new Error("canvasURL: The first argument must be a string, not a " + typeof(canvasName)), "error");
return "";
}
}
return canvases[canvasName].toDataURL();
}}$;
}
/**
* Finds the topmost shape object at a given pair of canvas coordinates.
* @param view_name_or_ptr_to_picture
* @param point_or_xy_argument_pair (two parameters in the latter case)
* @param [search_all] Include in the search any shapes that do not have a mouse zone name set (N.B. the default is false!)
*/
func shapeOnTopAt {
${{
var target = arguments[0];
var x, y, testAll;
if (arguments.length < 2 || arguments.length > 4) {
eden.error(new Error("shapeOnTopAt: This function requires at least 2 arguments and at most 4 arguments."), "error");
return undefined;
}
if (arguments[1] instanceof Point) {
x = arguments[1].x;
y = arguments[1].y;
testAll = arguments[2];
} else {
x = arguments[1];
y = arguments[2];
testAll = arguments[3];
}
if (target === undefined || x === undefined || y === undefined) {
return undefined;
}
if (target instanceof Symbol) {
var match = target.name.match(/^\/_view_(.*)_content$/);
if (match === null) {
eden.error(new Error("shapeOnTopAt: The first argument must be either a string or a pointer to a _view_xxx_content observable, not " +
Eden.edenCodeForValue(target)), "error");
return undefined;
} else {
target = match[1];
}
} else if (typeof(target) != "string") {
eden.error(new Error("shapeOnTopAt: The first argument must be either a string or a pointer, not a " + typeof(target)), "error");
return undefined;
}
if (typeof(x) != "number") {
eden.error(new Error("shapeOnTopAt: The x-coordinate must be a number, not a " + typeof(x)), "error");
return undefined;
} else if (typeof(y) != "number") {
eden.error(new Error("shapeOnTopAt: The y-coordinate must be a number, not a " + typeof(y)), "error");
return undefined;
}
return edenUI.plugins.Canvas2D.findDrawableHit(target, x, y, false, testAll);
}}$;
}
/**
* Finds the bottommost shape object at a given pair of canvas coordinates.
* @param view_name_or_ptr_to_picture
* @param point_or_xy_argument_pair (two parameters in the latter case)
* @param [search_all] Include in the search any shapes that do not have a mouse zone name set (N.B. the default is false!)
*/
func shapeOnBottomAt {
${{
var target = arguments[0];
var x, y, testAll;
if (arguments.length < 2 || arguments.length > 4) {
eden.error(new Error("shapeOnBottomAt: This function requires at least 2 arguments and at most 4 arguments."), "error");
return undefined;
}
if (arguments[1] instanceof Point) {
x = arguments[1].x;
y = arguments[1].y;
testAll = arguments[2];
} else {
x = arguments[1];
y = arguments[2];
testAll = arguments[3];
}
if (target === undefined || x === undefined || y === undefined) {
return undefined;
}
if (target instanceof Symbol) {
var match = target.name.match(/^\/_view_(.*)_content$/);
if (match === null) {
eden.error(new Error("shapeOnBottomAt: The first argument must be either a string or a pointer to a _view_xxx_content observable, not " +
Eden.edenCodeForValue(target)), "error");
return undefined;
} else {
target = match[1];
}
} else if (typeof(target) != "string") {
eden.error(new Error("shapeOnBottomAt: The first argument must be either a string or a pointer, not a " + typeof(target)), "error");
return undefined;
}
if (typeof(x) != "number") {
eden.error(new Error("shapeOnBottomAt: The x-coordinate must be a number, not a " + typeof(x)), "error");
return undefined;
} else if (typeof(y) != "number") {
eden.error(new Error("shapeOnBottomAt: The y-coordinate must be a number, not a " + typeof(y)), "error");
return undefined;
}
return edenUI.plugins.Canvas2D.findDrawableHit(target, x, y, true, testAll);
}}$;
}
/**
* Finds the name of the mouse zone of the topmost shape at a given pair of canvas coordinates.
* @param view_name_or_ptr_to_picture
* @param point_or_xy_argument_pair (two parameters in the latter case)
*/
func zoneOnTopAt {
auto drawable;
drawable = apply(shapeOnTopAt, ${{ arguments }}$);
if (drawable == @) {
return @;
} else {
return drawable["name"];
}
}
/**
* Finds the name of the mouse zone of the bottommost shape at a given pair of canvas coordinates.
* @param view_name_or_ptr_to_picture
* @param point_or_xy_argument_pair (two parameters in the latter case)
*/
func zoneOnBottomAt {
auto drawable;
drawable = apply(shapeOnBottomAt, ${{ arguments }}$);
if (drawable == @) {
return @;
} else {
return drawable["name"];
}
}
/**
* Obtains the observable name associated with a given canvas shape (if there is one).
* @param shape
*/
func observableForShape {
${{
var drawable = arguments[0];
if (drawable === undefined) {
return undefined;
} else if ("obsName" in drawable) {
return drawable["obsName"];
} else {
return drawable["name"];
}
}}$;
}
/**
* Finds the observable name associated with the topmost shape at a given pair of canvas coordinates (if there is one).
* @param view_name_or_ptr_to_picture
* @param point_or_xy_argument_pair (two parameters in the latter case)
* @param [search_all] Include in the search any shapes that do not have a mouse zone name set (N.B. the default is false!)
*/
func observableOnTopAt {
auto drawable;
drawable = apply(shapeOnTopAt, ${{ arguments }}$);
return observableForShape(drawable);
}
/**
* Finds the observable name associated with the bottommost shape at a given pair of canvas coordinates (if there is one).
* @param view_name_or_ptr_to_picture
* @param point_or_xy_argument_pair (two parameters in the latter case)
* @param [search_all] Include in the search any shapes that do not have a mouse zone name set (N.B. the default is false!)
*/
func observableOnBottomAt {
auto drawable;
drawable = apply(shapeOnBottomAt, ${{ arguments }}$);
return observableForShape(drawable);
}
/**
* Finds all of the shape objects present at a given pair of canvas coordinates and returns them as a list.
* @param view_name_or_ptr_to_picture
* @param point_or_xy_argument_pair (two parameters in the latter case)
* @param [search_all] If false then only shapes which have a mouse zone name set are returned (N.B. the default is false!)
*/
func shapesAt {
${{
var target = arguments[0];
var x, y, testAll;
if (arguments.length < 2 || arguments.length > 4) {
eden.error(new Error("shapesAt: This function requires at least 2 arguments and at most 4 arguments."), "error");
return undefined;
}
if (arguments[1] instanceof Point) {
x = arguments[1].x;
y = arguments[1].y;
testAll = arguments[2];
} else {
x = arguments[1];
y = arguments[2];
testAll = arguments[3];
}
if (target === undefined || x === undefined || y === undefined) {
return undefined;
}
if (target instanceof Symbol) {
var match = target.name.match(/^\/_view_(.*)_content$/);
if (match === null) {
eden.error(new Error("shapesAt: The first argument must be either a string or a pointer to a _view_xxx_content observable, not " +
Eden.edenCodeForValue(target)), "error");
return undefined;
} else {
target = match[1];
}
} else if (typeof(target) != "string") {
eden.error(new Error("shapesAt: The first argument must be either a string or a pointer, not a " + typeof(target)), "error");
return undefined;
}
if (typeof(x) != "number") {
eden.error(new Error("shapesAt: The x-coordinate must be a number, not a " + typeof(x)), "error");
return undefined;
} else if (typeof(y) != "number") {
eden.error(new Error("shapesAt: The y-coordinate must be a number, not a " + typeof(y)), "error");
return undefined;
}
return edenUI.plugins.Canvas2D.findAllDrawablesHit(target, x, y, testAll);
}}$;
}
/**
* Finds all of the mouse zone names of the shapes intersecting at a given pair of canvas coordinates and returns them as a list.
* @param view_name_or_ptr_to_picture
* @param point_or_xy_argument_pair (two parameters in the latter case)
*/
func zonesAt {
auto drawables, zones, i;
drawables = apply(shapesAt, ${{ arguments }}$);
zones = [];
for (i = 1; i <= drawables#; i++) {
append zones, drawables[i]["name"];
}
return zones;
}
/**
* Finds all of observable names associated with shapes intersecting at a given pair of canvas coordinates and returns them as a list.
* @param view_name_or_ptr_to_picture
* @param point_or_xy_argument_pair (two parameters in the latter case)
* @param [search_all] If false then only shapes which have a mouse zone name set are considered (N.B. the default is false!)
*/
func observablesAt {
auto drawables, obsList, obsName, i;
drawables = apply(shapesAt, ${{ arguments }}$);
obsList = [];
for (i = 1; i <= drawables#; i++) {
obsName = observableForShape(drawables[i]);
if (obsName != @) {
append obsList, obsName;
}
}
return obsList;
}
do plugins > canvas > arc;
do plugins > canvas > arrowhead;
do plugins > canvas > button;
do plugins > canvas > checkbox;
do plugins > canvas > circle;
do plugins > canvas > combobox;
do plugins > canvas > curve;
do plugins > canvas > div;
do plugins > canvas > dropdownlist;
do plugins > canvas > ellipse;
do plugins > canvas > fillpattern;
do plugins > canvas > flowlayout;
do plugins > canvas > greypixellist;
do plugins > canvas > canvasimage;
do plugins > canvas > htmlimage;
do plugins > canvas > line;
do plugins > canvas > lineargradient;
do plugins > canvas > linesequence;
do plugins > canvas > polygon;
do plugins > canvas > rectangle;
do plugins > canvas > slider;
do plugins > canvas > text;
do plugins > canvas > pixel;
do plugins > canvas > pixellist;
do plugins > canvas > radialgradient;
do plugins > canvas > radiobuttons;
do plugins > canvas > regularpolygon;
do plugins > canvas > rotation;
do plugins > canvas > roundedrectangle;
do plugins > canvas > scaling;
do plugins > canvas > sector;
do plugins > canvas > shadow;
do plugins > canvas > tablelayout;
do plugins > canvas > textbox;
do plugins > canvas > translation;
do plugins > canvas > video;
do plugins > canvas > view;
}
}
}
do plugins > canvas > canvas;