zimjs 20.0.3 → 20.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/zim.js CHANGED
@@ -2747,41 +2747,99 @@ color1 - |ZIM VEE| (default null) the first color as an HTML string or hex color
2747
2747
  color2 - |ZIM VEE| (default black) the second color as an HTML string or hex color (case insensitive)
2748
2748
  ratio - (default .5) the ratio where 0 is the first color and 1 the second color
2749
2749
 
2750
- RETURNS a hex color string
2750
+ RETURNS a hex color string or rgba if alpha is not 1
2751
2751
  --*///+27.6
2752
- zim.colorRange = function(color1, color2, ratio) {
2752
+ zim.colorRange = function(c1, c2, ratio) {
2753
2753
  if (!zim.colorRangeCheck) {z_d("27.6"); zim.colorRangeCheck=true;}
2754
- // thanks Chris Dolphin - StackOverflow
2755
- // modified by Dan Zen to use hex input and output
2756
- // possibly converting and converting back - but not quite...
2757
- if (zot(ratio)) ratio = .5;
2754
+ if (zot(c1)) c1 = "white";
2755
+ if (zot(c2)) c2 = "black";
2756
+ c1 = zik(c1);
2757
+ c2 = zik(c2);
2758
+ if (ratio == null) ratio = 0.5;
2758
2759
  ratio = Math.max(0, Math.min(1, ratio));
2759
- if (zot(color1)) color1 = "white";
2760
- if (zot(color2)) color2 = "black";
2761
- color1 = zik(color1);
2762
- color2 = zik(color2);
2763
- var color1O = color1;
2764
- var color2O = color2;
2765
- var c1 = zim.convertColor(color1, "rgb");
2766
- var c2 = zim.convertColor(color2, "rgb");
2767
- color1 = c1.substring(4, c1.length - 1).split(',');
2768
- color2 = c2.substring(4, c2.length - 1).split(',');
2769
- var difference;
2770
- var newColor = "#";
2771
- var c;
2772
- if (ratio==0) {
2773
- newColor = color1O;
2774
- } else if (ratio==1) {
2775
- newColor = color2O;
2776
- } else {
2777
- for (var i=0; i<color1.length; i++) {
2778
- difference = color2[i] - color1[i];
2779
- c = Math.floor(parseInt(color1[i], 10) + difference * ratio).toString(16);
2780
- if (c.length < 2) c = "0"+c;
2781
- newColor += c;
2760
+
2761
+ function parseColor(c) {
2762
+ if (!c) return [0, 0, 0, 1];
2763
+ if (typeof c !== "string") c = String(c);
2764
+ c = c.trim();
2765
+
2766
+ // 1. Match rgba(r, g, b, a) or rgb(r, g, b)
2767
+ var rgbaMatch = c.match(/rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)/i);
2768
+ if (rgbaMatch) {
2769
+ return [
2770
+ parseFloat(rgbaMatch[1]),
2771
+ parseFloat(rgbaMatch[2]),
2772
+ parseFloat(rgbaMatch[3]),
2773
+ rgbaMatch[4] != null ? parseFloat(rgbaMatch[4]) : 1
2774
+ ];
2775
+ }
2776
+
2777
+ // 2. Special ZIM / CSS keywords with built-in alpha
2778
+ if (c === "clear" || c === "transparent") return [0, 0, 0, 0];
2779
+ if (c === "faint") return [0, 0, 0, 0.05];
2780
+
2781
+ // 3. Hex formats (#rgb, #rgba, #rrggbb, #rrggbbaa)
2782
+ if (c.charAt(0) === "#") {
2783
+ var hex = c.slice(1);
2784
+ if (hex.length === 3) {
2785
+ return [
2786
+ parseInt(hex[0] + hex[0], 16),
2787
+ parseInt(hex[1] + hex[1], 16),
2788
+ parseInt(hex[2] + hex[2], 16),
2789
+ 1
2790
+ ];
2791
+ } else if (hex.length === 4) {
2792
+ return [
2793
+ parseInt(hex[0] + hex[0], 16),
2794
+ parseInt(hex[1] + hex[1], 16),
2795
+ parseInt(hex[2] + hex[2], 16),
2796
+ parseInt(hex[3] + hex[3], 16) / 255
2797
+ ];
2798
+ } else if (hex.length === 6) {
2799
+ return [
2800
+ parseInt(hex.substr(0, 2), 16),
2801
+ parseInt(hex.substr(2, 2), 16),
2802
+ parseInt(hex.substr(4, 2), 16),
2803
+ 1
2804
+ ];
2805
+ } else if (hex.length === 8) {
2806
+ return [
2807
+ parseInt(hex.substr(0, 2), 16),
2808
+ parseInt(hex.substr(2, 2), 16),
2809
+ parseInt(hex.substr(4, 2), 16),
2810
+ parseInt(hex.substr(6, 2), 16) / 255
2811
+ ];
2812
+ }
2813
+ }
2814
+
2815
+ // 4. Fallback for named CSS colors via temporary canvas context or lookup
2816
+ // If zim.color / zim.toColor is available, resolve it first
2817
+ if (zim.toColor && c in zim) c = zim[c];
2818
+
2819
+ // Simple 3-byte fallback if hex number without hash
2820
+ var num = parseInt(c, 16);
2821
+ if (!isNaN(num) && c.length === 6) {
2822
+ return [(num >> 16) & 255, (num >> 8) & 255, num & 255, 1];
2782
2823
  }
2824
+
2825
+ return [0, 0, 0, 1];
2826
+ }
2827
+
2828
+ var col1 = parseColor(c1);
2829
+ var col2 = parseColor(c2);
2830
+
2831
+ var r = Math.round(col1[0] + (col2[0] - col1[0]) * ratio);
2832
+ var g = Math.round(col1[1] + (col2[1] - col1[1]) * ratio);
2833
+ var b = Math.round(col1[2] + (col2[2] - col1[2]) * ratio);
2834
+ var a = col1[3] + (col2[3] - col1[3]) * ratio;
2835
+ a = Math.round(a * 1000) / 1000;
2836
+
2837
+ if (a >= 0.999) {
2838
+ // Return standard 6-character hex for opaque colors
2839
+ return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
2783
2840
  }
2784
- return newColor;
2841
+ // Return rgba only when alpha is fractional
2842
+ return "rgba(" + r + "," + g + "," + b + "," + a + ")";
2785
2843
  };//-27.6
2786
2844
 
2787
2845
  /*--
@@ -18277,6 +18335,7 @@ lastSelected - access to the last selected (or created) control container
18277
18335
  lastindex - the index number of the last selected controls
18278
18336
  controlsVisible - get or set the visibility of the controls - or use showControls() and hideControls()
18279
18337
  toggled - read-only Boolean property as to whether picker is showing
18338
+ controlType - get or set control types for new points
18280
18339
  types - get or set the general array for the types ["mirror", "straight", "free", "none"]
18281
18340
  changing this or removing a type will adjust the order when the user double clicks the points to change their type
18282
18341
  this is not an array of types for each point - see the points property to access the types of each point
@@ -18291,11 +18350,13 @@ selectionManager - access to the SelectionManager
18291
18350
  selectedBalls - access to the SelectionSet of control circles
18292
18351
  selectedRect1s - access to the SelectionSet of control rectangles
18293
18352
  selectedRect2s - access to the SelectionSet of control rectangles
18353
+ editPoints - get or set whether points can be added and removed
18354
+ holdPoints - get or set whether points can be held to be removed (default is editPoints)
18294
18355
  interactive - get or set whether the shape is interactive - toggle, move, change or add controls, etc.
18295
18356
  keyFocus - get or set the keyboard focus on the DisplayObject - see also zim.KEYFOCUS
18296
- will be set to true if this DisplayObject is the first made or DisplayObject is the last to be used with keyboard
18357
+ will be set to true if this DisplayObject is the first made or DisplayObject is the last to be used with keyboard
18297
18358
  veeObj - an object with ZIM VEE original parameters:value allowing the ZIM VEE values to be referenced
18298
- for instance, obj.prop = Pick.choose(obj.veeObj.prop); will reset the the prop to the result of the original ZIM VEE value
18359
+ for instance, obj.prop = Pick.choose(obj.veeObj.prop); will reset the the prop to the result of the original ZIM VEE value
18299
18360
 
18300
18361
  ALSO: see ZIM Container for properties such as:
18301
18362
  width, height, widthOnly, heightOnly, draggable, level, depth, group
@@ -18318,7 +18379,7 @@ See the CreateJS Easel Docs for Container events such as:
18318
18379
  added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmove, pressup, removed, rollout, rollover
18319
18380
 
18320
18381
  MORE
18321
- https://zimjs.com/squiggle
18382
+ https://zimjs.com/zapp/E_4QEJ4
18322
18383
  https://www.youtube.com/watch?v=BA1bGBU4itI&list=PLCIzupgRt1pYtMlYPtNTKCtztFBeOtyc0
18323
18384
  Note the points property has been split into points and pointObjects (and there have been a few property changes) since the time of the video
18324
18385
  --*///+53.2
@@ -18391,7 +18452,7 @@ Note the points property has been split into points and pointObjects (and there
18391
18452
  this.num = num;
18392
18453
  this.onTop = onTop;
18393
18454
  this.move = move;
18394
- this.editPoints = editPoints;
18455
+ this.editPoints = this.holdPoints = editPoints;
18395
18456
  this.allowToggle = allowToggle;
18396
18457
  this.lockControlType = lockControlType;
18397
18458
  this.selectPoints = selectPoints;
@@ -18459,6 +18520,52 @@ Note the points property has been split into points and pointObjects (and there
18459
18520
  that.selectionManager = new zim.SelectionManager(null, "ctrl");
18460
18521
  }
18461
18522
 
18523
+ that.selectionManager.on("keydown", function (e) {
18524
+ if (!that.selectPoints) return;
18525
+ if (!that.keyFocus) return;
18526
+ if (e.keyCode >= 37 && e.keyCode <= 40) {
18527
+ var currentSelected = getCurrentSelected();
18528
+ if (currentSelected.length > 0) {
18529
+ for(var i=0; i<currentSelected.length; i++) {
18530
+ var pointObj = currentSelected[i];
18531
+ if (e.keyCode == 37) pointObj.x -= that.selectionManager.shiftKey?10:1;
18532
+ else if (e.keyCode == 39) pointObj.x += that.selectionManager.shiftKey?10:1;
18533
+ else if (e.keyCode == 38) pointObj.y -= that.selectionManager.shiftKey?10:1;
18534
+ else if (e.keyCode == 40) pointObj.y += that.selectionManager.shiftKey?10:1;
18535
+ mapMove(pointObj);
18536
+ }
18537
+ that.drawShape();
18538
+ that.dispatchEvent("update");
18539
+ if (that.stage) that.stage.update();
18540
+ }
18541
+ }
18542
+ }, null, null, null, null, mID);
18543
+
18544
+ that.selectionManager.on("keyup", function (e) {
18545
+ if (!that.selectPoints) return;
18546
+ if (!that.keyFocus) return;
18547
+ if (e.keyCode >= 37 && e.keyCode <= 40) {
18548
+ var currentSelected = getCurrentSelected();
18549
+ if (currentSelected.length > 0) {
18550
+ for(var i=0; i<currentSelected.length; i++) {
18551
+ replaceControls(currentSelected[i]);
18552
+ }
18553
+ }
18554
+ }
18555
+ }, null, null, null, null, mID);
18556
+
18557
+ that.selectionManager.on("undo", function () {
18558
+ if (!that.selectPoints) return;
18559
+ if (!that.keyFocus) return;
18560
+ if (that.lastPoints) {
18561
+ var tempPoints = zim.copy(that.lastPoints);
18562
+ that.lastPoints = zim.copy(that.points);
18563
+ that.points = tempPoints;
18564
+ if (that.stage) that.stage.update();
18565
+ }
18566
+ }, null, null, null, null, mID);
18567
+ // }
18568
+
18462
18569
  var mobile = zim.mobile();
18463
18570
 
18464
18571
  sets = that.controls = new zim.Container({style:false}).addTo(that); // sets - a set contains a ball and two rects
@@ -19356,34 +19463,42 @@ Note the points property has been split into points and pointObjects (and there
19356
19463
  }, null, null, null, null, mID);
19357
19464
 
19358
19465
  // remove point
19359
- that.controls.on("click", function (e) {
19466
+ that.controlsClickEvent = that.controls.on("click", function (e) {
19360
19467
  that.lastSelected = e.target.parent;
19361
19468
  that.lastindex = that.controls.getChildIndex(e.target.parent);
19362
19469
  if (!that.editPoints) return;
19363
19470
  if (that.selectionManager.shiftKey) { // remove
19364
- // if (that.selectionManager.currentSet == that.selectedBalls && that.selectedBalls.selections.length > 0) return;
19471
+ if (that.controls.noHold) that.controls.noHold();
19365
19472
  removeControl(e);
19366
19473
  }
19367
19474
  }, null, null, null, null, mID);
19368
19475
 
19369
- function removeControl(e) {
19370
- if (e.target.type == "Circle") {
19371
- var index = that.lastindex = that.controls.getChildIndex(e.target.parent);
19372
- if (that.controls.numChildren <= 2) return;
19373
- var points = that.points;
19374
- if (that.selectPoints) that.lastPoints = zim.copy(points);
19375
- points.splice(index, 1); // remove the point at the index
19376
- that.points = points;
19377
- that.stage.update();
19378
- that.lastSelected = that.lastindex = null;
19379
- }
19380
- }
19381
- if (that.editPoints) that.controls.hold(removeControl);
19476
+ that.controls.hold(removeControl);
19382
19477
 
19383
19478
  if (!_controls) that.hideControls();
19384
19479
  that.dispatchEvent("update");
19385
19480
  } // end of init()
19386
19481
 
19482
+ function removeControl(e) {
19483
+ if (!that.editPoints || !that.holdPoints || !e || !e.target) return;
19484
+ if (e.target.type == "Circle" && e.target.parent && e.target.parent.parent == that.controls) {
19485
+ var index = that.lastindex = that.controls.getChildIndex(e.target.parent);
19486
+ if (index < 0 || that.controls.numChildren <= 2) return;
19487
+ if (that.controls.noHold) that.controls.noHold();
19488
+ var points = that.points;
19489
+ if (that.selectPoints) that.lastPoints = zim.copy(points);
19490
+ points.splice(index, 1); // remove the point at the index
19491
+ that.points = points;
19492
+ var ev = new createjs.Event("change");
19493
+ ev.controlType = "pointDelete";
19494
+ ev.index = index;
19495
+ that.dispatchEvent(ev);
19496
+ that.dispatchEvent("pointdelete");
19497
+ if (that.stage) that.stage.update();
19498
+ that.lastSelected = that.lastindex = null;
19499
+ }
19500
+ }
19501
+
19387
19502
  // if (that.selectPoints) {
19388
19503
  function getCurrentSelected() {
19389
19504
  var answer = [];
@@ -19424,54 +19539,10 @@ Note the points property has been split into points and pointObjects (and there
19424
19539
  ball.y = 0;
19425
19540
  }
19426
19541
 
19427
- that.selectionManager.on("keydown", function (e) {
19428
- if (!that.selectPoints) return;
19429
- if (!that.keyFocus) return;
19430
- if (e.keyCode >= 37 && e.keyCode <= 40) {
19431
- var currentSelected = getCurrentSelected();
19432
- if (currentSelected.length > 0) {
19433
- for(var i=0; i<currentSelected.length; i++) {
19434
- var pointObj = currentSelected[i];
19435
- if (e.keyCode == 37) pointObj.x -= that.selectionManager.shiftKey?10:1;
19436
- else if (e.keyCode == 39) pointObj.x += that.selectionManager.shiftKey?10:1;
19437
- else if (e.keyCode == 38) pointObj.y -= that.selectionManager.shiftKey?10:1;
19438
- else if (e.keyCode == 40) pointObj.y += that.selectionManager.shiftKey?10:1;
19439
- mapMove(pointObj);
19440
- }
19441
- that.drawShape();
19442
- that.dispatchEvent("update");
19443
- if (that.stage) that.stage.update();
19444
- }
19445
- }
19446
- }, null, null, null, null, mID);
19447
-
19448
- that.selectionManager.on("keyup", function (e) {
19449
- if (!that.selectPoints) return;
19450
- if (!that.keyFocus) return;
19451
- if (e.keyCode >= 37 && e.keyCode <= 40) {
19452
- var currentSelected = getCurrentSelected();
19453
- if (currentSelected.length > 0) {
19454
- for(var i=0; i<currentSelected.length; i++) {
19455
- replaceControls(currentSelected[i]);
19456
- }
19457
- }
19458
- }
19459
- }, null, null, null, null, mID);
19460
-
19461
- that.selectionManager.on("undo", function () {
19462
- if (!that.selectPoints) return;
19463
- if (!that.keyFocus) return;
19464
- if (that.lastPoints) {
19465
- var tempPoints = zim.copy(that.lastPoints);
19466
- that.lastPoints = zim.copy(that.points);
19467
- that.points = tempPoints;
19468
- if (that.stage) that.stage.update();
19469
- }
19470
- }, null, null, null, null, mID);
19471
- // }
19542
+
19472
19543
 
19473
19544
 
19474
- mapMove = function (target) {
19545
+ function mapMove(target) {
19475
19546
  if (that.lockControls) return;
19476
19547
  var ball;
19477
19548
  if (target.rect1) { // pressmove on ball
@@ -19641,7 +19712,8 @@ Note the points property has been split into points and pointObjects (and there
19641
19712
  zim.decimals(p[3].x),
19642
19713
  zim.decimals(p[3].y)
19643
19714
  ];
19644
- if (p[4] && p[4]!=="mirror") point.push(p[4]);
19715
+ // if (p[4] && p[4]!=="mirror") point.push(p[4]);
19716
+ if (p[4]) point.push(p[4]);
19645
19717
  points.push(point);
19646
19718
  }
19647
19719
  return points;
@@ -19757,6 +19829,16 @@ Note the points property has been split into points and pointObjects (and there
19757
19829
  }
19758
19830
  });
19759
19831
 
19832
+ Object.defineProperty(that, 'controlType', {
19833
+ get: function() {
19834
+ return controlType;
19835
+ },
19836
+ set: function(value) {
19837
+ controlType = value;
19838
+ originalControlType = value;
19839
+ }
19840
+ });
19841
+
19760
19842
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19761
19843
  // path manipulation and animating to path methods
19762
19844
 
@@ -19996,17 +20078,28 @@ Note the points property has been split into points and pointObjects (and there
19996
20078
  var index = insertPointData(points, controls, ratios, percent, controlType);
19997
20079
  that.points = points;
19998
20080
  that.num = points.length;
20081
+ var ev = new createjs.Event("change");
20082
+ ev.controlType = "pointAdd";
20083
+ ev.index = index;
20084
+ that.dispatchEvent(ev);
20085
+ that.dispatchEvent("pointadd");
20086
+ if (that.stage) that.stage.update();
19999
20087
  return index;
20000
20088
  };
20001
20089
 
20002
20090
  this.removePoint = function(index) {
20003
- if (zot(index) || index < 0) return;
20091
+ if (zot(index) || index < 0 || that.points.length <= 2 || index >= that.points.length) return that;
20004
20092
  var currentPoints = zim.copy(that.points);
20005
20093
  currentPoints.splice(index, 1);
20006
20094
  that.points = currentPoints;
20095
+ var ev = new createjs.Event("change");
20096
+ ev.controlType = "pointDelete";
20097
+ ev.index = index;
20098
+ that.dispatchEvent(ev);
20099
+ that.dispatchEvent("pointdelete");
20007
20100
  if (that.stage) that.stage.update();
20008
20101
  return that;
20009
- }
20102
+ };
20010
20103
 
20011
20104
  this.addPoints = function(num, controlType, startPoint, spread, dataOnly, points, even) {
20012
20105
  var sig = "num, controlType, startPoint, spread, dataOnly, points, even";
@@ -20464,10 +20557,12 @@ segmentRatios - a read-only array of cumulative ratio lengths of segments
20464
20557
  used internally to animate to the path and attribute proportional time to each segment
20465
20558
  controls - access to the container that holds the sets of controls
20466
20559
  each control is given a read-only num property
20560
+ use blob.controls.noHold() to remove holding down a point to delete it
20467
20561
  sticks - access to the Shape that has the control sticks
20468
20562
  lastSelected - access to the last selected (or created) control container
20469
20563
  lastindex - the index number of the last selected controls
20470
20564
  controlsVisible - get or set the visibility of the controls - or use showControls() and hideControls()
20565
+ controlType - get or set control types for new points
20471
20566
  types - get or set the general array for the types ["mirror", "straight", "free", "none"]
20472
20567
  changing this or removing a type will adjust the order when the user double clicks the points to change their type
20473
20568
  this is not an array of types for each point - see the points property to access the types of each point
@@ -20482,11 +20577,13 @@ selectionManager - access to the SelectionManager
20482
20577
  selectedBalls - access to the SelectionSet of control circles
20483
20578
  selectedRect1s - access to the SelectionSet of control rectangles
20484
20579
  selectedRect2s - access to the SelectionSet of control rectangles
20580
+ editPoints - get or set whether points can be added and removed
20581
+ holdPoints - get or set whether points can be held to be removed (default is editPoints)
20485
20582
  interactive - get or set whether the shape is interactive - toggle, move, change or add controls, etc.
20486
20583
  keyFocus - get or set the keyboard focus on the DisplayObject - see also zim.KEYFOCUS
20487
- will be set to true if this DisplayObject is the first made or DisplayObject is the last to be used with keyboard
20584
+ will be set to true if this DisplayObject is the first made or DisplayObject is the last to be used with keyboard
20488
20585
  veeObj - an object with ZIM VEE original parameters:value allowing the ZIM VEE values to be referenced
20489
- for instance, obj.prop = Pick.choose(obj.veeObj.prop); will reset the the prop to the result of the original ZIM VEE value
20586
+ for instance, obj.prop = Pick.choose(obj.veeObj.prop); will reset the the prop to the result of the original ZIM VEE value
20490
20587
 
20491
20588
  ALSO: see ZIM Container for properties such as:
20492
20589
  width, height, widthOnly, heightOnly, draggable, level, depth, group
@@ -20572,7 +20669,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
20572
20669
  var that = this;
20573
20670
  this.interactive = interactive;
20574
20671
  this.num = num;
20575
- this.editPoints = editPoints;
20672
+ this.editPoints = this.holdPoints = editPoints;
20576
20673
  this.selectPoints = selectPoints;
20577
20674
  this.lockControls = lockControls;
20578
20675
  this.onTop = onTop;
@@ -20601,9 +20698,10 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
20601
20698
  var min = 2; // distance within which to count as click to add point
20602
20699
 
20603
20700
  var mapMove;
20604
- var sets;
20701
+ var sets;
20605
20702
 
20606
20703
  points = checkForShape(points);
20704
+
20607
20705
  function checkForShape(shape) {
20608
20706
  if (shape=="circle" || shape == "rectangle" || shape =="triangle") {
20609
20707
  if (shape == "circle") shape = new zim.Circle(radius);
@@ -20734,6 +20832,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
20734
20832
  points.pop();
20735
20833
  }
20736
20834
  }
20835
+
20737
20836
 
20738
20837
  if (originalControlType && typeof points != "number") {
20739
20838
  // override controlType
@@ -20743,7 +20842,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
20743
20842
  point[4]=point[5]=point[6]=point[7]=0;
20744
20843
  }
20745
20844
  });
20746
- }
20845
+ }
20747
20846
 
20748
20847
  init();
20749
20848
  function init() {
@@ -20763,6 +20862,51 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
20763
20862
  that.selectionManager = new zim.SelectionManager(null, "ctrl");
20764
20863
  }
20765
20864
 
20865
+ that.selectionManager.on("keydown", function (e) {
20866
+ if (!that.selectPoints) return;
20867
+ if (!that.keyFocus) return;
20868
+ if (e.keyCode >= 37 && e.keyCode <= 40) {
20869
+ var currentSelected = getCurrentSelected();
20870
+ if (currentSelected.length > 0) {
20871
+ for(var i=0; i<currentSelected.length; i++) {
20872
+ var pointObj = currentSelected[i];
20873
+ if (e.keyCode == 37) pointObj.x -= that.selectionManager.shiftKey?10:1;
20874
+ else if (e.keyCode == 39) pointObj.x += that.selectionManager.shiftKey?10:1;
20875
+ else if (e.keyCode == 38) pointObj.y -= that.selectionManager.shiftKey?10:1;
20876
+ else if (e.keyCode == 40) pointObj.y += that.selectionManager.shiftKey?10:1;
20877
+ mapMove(pointObj);
20878
+ }
20879
+ that.drawShape();
20880
+ that.dispatchEvent("update");
20881
+ if (that.stage) that.stage.update();
20882
+ }
20883
+ }
20884
+ }, null, null, null, null, mID);
20885
+
20886
+ that.selectionManager.on("keyup", function (e) {
20887
+ if (!that.selectPoints) return;
20888
+ if (!that.keyFocus) return;
20889
+ if (e.keyCode >= 37 && e.keyCode <= 40) {
20890
+ var currentSelected = getCurrentSelected();
20891
+ if (currentSelected.length > 0) {
20892
+ for(var i=0; i<currentSelected.length; i++) {
20893
+ replaceControls(currentSelected[i]);
20894
+ }
20895
+ }
20896
+ }
20897
+ }, null, null, null, null, mID);
20898
+
20899
+ that.selectionManager.on("undo", function () {
20900
+ if (!that.selectPoints) return;
20901
+ if (!that.keyFocus) return;
20902
+ if (that.lastPoints) {
20903
+ var tempPoints = zim.copy(that.lastPoints);
20904
+ that.lastPoints = zim.copy(that.points);
20905
+ that.points = tempPoints;
20906
+ if (that.stage) that.stage.update();
20907
+ }
20908
+ }, null, null, null, null, mID);
20909
+
20766
20910
  num = typeof points == "number" ? points : points.length;
20767
20911
  if (num <= 0) return;
20768
20912
  if (zot(controlLengthOriginal)) controlLength = radius * 4 / num;
@@ -20788,6 +20932,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
20788
20932
  _pointCircles = [];
20789
20933
 
20790
20934
  var i, point, temp, set, rect1, rect2, ball, type, setInfo;
20935
+
20791
20936
 
20792
20937
  for (i=0; i<num; i++) {
20793
20938
  set = new zim.Container({style:false}).addTo(sets);
@@ -20837,7 +20982,6 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
20837
20982
 
20838
20983
  // balls are relative to blob but handles are relative to ball
20839
20984
  // points is an array of [[setX, setY, ballX, ballY, handleX, handleY, handle2X, handle2Y, type], etc.]
20840
-
20841
20985
  setInfo = points[i];
20842
20986
  type = setInfo[8] ? setInfo[8] : controlType;
20843
20987
  set.loc({x:setInfo[0], y:setInfo[1]});
@@ -20886,7 +21030,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
20886
21030
  point = [set, ball, rect1, rect2, setInfo?setInfo[8]:controlType];
20887
21031
  _points.push(point);
20888
21032
  _pointCircles.push(ball);
20889
- _pointControls.push(set);
21033
+ _pointControls.push(set);
20890
21034
  }
20891
21035
 
20892
21036
  var tappedTwice = false;
@@ -21668,6 +21812,11 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
21668
21812
  points.splice(pointBefore+1, 0, [point.x, point.y, 0,0, 0,0, 0,0]);
21669
21813
  that.points = points;
21670
21814
  that.changeControl({index:pointBefore+1, type:"mirror", update:true});
21815
+ var ev = new createjs.Event("change");
21816
+ ev.controlType = "pointAdd";
21817
+ ev.index = pointBefore+1;
21818
+ that.dispatchEvent(ev);
21819
+ that.dispatchEvent("pointadd");
21671
21820
  } else { // only on edge
21672
21821
  // test close enough to edge otherwise return
21673
21822
  var p = that.pointsAdjusted;
@@ -21706,28 +21855,38 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
21706
21855
  that.lastindex = that.controls.getChildIndex(e.target.parent);
21707
21856
  if (!that.editPoints) return;
21708
21857
  if (that.selectionManager.shiftKey) { // remove
21858
+ if (that.controls.noHold) that.controls.noHold();
21709
21859
  removeControl(e);
21710
21860
  }
21711
21861
  }, null, null, null, null, mID);
21712
21862
 
21713
- function removeControl(e) {
21714
- if (e.target.type == "Circle") {
21715
- var index = that.lastindex = that.controls.getChildIndex(e.target.parent);
21716
- if (that.controls.numChildren <= 2) return;
21717
- var points = that.points;
21718
- if (that.selectPoints) that.lastPoints = zim.copy(points);
21719
- points.splice(index, 1); // remove the point at the index
21720
- that.points = points;
21721
- that.stage.update();
21722
- that.lastSelected = that.lastindex = null;
21723
- }
21724
- }
21725
- if (that.editPoints) that.controls.hold(removeControl);
21863
+ that.controls.hold(removeControl);
21726
21864
 
21727
21865
  if (!_controls) that.hideControls();
21728
21866
  that.dispatchEvent("update");
21729
21867
  } // end of init()
21730
21868
 
21869
+ function removeControl(e) {
21870
+ if (!that.editPoints || !that.holdPoints || !e || !e.target) return;
21871
+ if (e.target.type == "Circle" && e.target.parent && e.target.parent.parent == that.controls) {
21872
+ var index = that.lastindex = that.controls.getChildIndex(e.target.parent);
21873
+ if (index < 0 || that.controls.numChildren <= 2) return;
21874
+ if (that.controls.noHold) that.controls.noHold();
21875
+ var points = that.points;
21876
+ if (that.selectPoints) that.lastPoints = zim.copy(points);
21877
+ points.splice(index, 1); // remove the point at the index
21878
+ that.points = points;
21879
+ var ev = new createjs.Event("change");
21880
+ ev.controlType = "pointDelete";
21881
+ ev.index = index;
21882
+ that.dispatchEvent(ev);
21883
+ that.dispatchEvent("pointdelete");
21884
+ if (that.stage) that.stage.update();
21885
+ that.lastSelected = that.lastindex = null;
21886
+ }
21887
+ }
21888
+
21889
+
21731
21890
  function getCurrentSelected() {
21732
21891
  var answer = [];
21733
21892
  var currentSet = that.selectionManager.currentSet;
@@ -21767,53 +21926,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
21767
21926
  ball.y = 0;
21768
21927
  }
21769
21928
 
21770
- that.selectionManager.on("keydown", function (e) {
21771
- if (!that.selectPoints) return;
21772
- if (!that.keyFocus) return;
21773
- if (e.keyCode >= 37 && e.keyCode <= 40) {
21774
- var currentSelected = getCurrentSelected();
21775
- if (currentSelected.length > 0) {
21776
- for(var i=0; i<currentSelected.length; i++) {
21777
- var pointObj = currentSelected[i];
21778
- if (e.keyCode == 37) pointObj.x -= that.selectionManager.shiftKey?10:1;
21779
- else if (e.keyCode == 39) pointObj.x += that.selectionManager.shiftKey?10:1;
21780
- else if (e.keyCode == 38) pointObj.y -= that.selectionManager.shiftKey?10:1;
21781
- else if (e.keyCode == 40) pointObj.y += that.selectionManager.shiftKey?10:1;
21782
- mapMove(pointObj);
21783
- }
21784
- that.drawShape();
21785
- that.dispatchEvent("update");
21786
- if (that.stage) that.stage.update();
21787
- }
21788
- }
21789
- }, null, null, null, null, mID);
21790
-
21791
- that.selectionManager.on("keyup", function (e) {
21792
- if (!that.selectPoints) return;
21793
- if (!that.keyFocus) return;
21794
- if (e.keyCode >= 37 && e.keyCode <= 40) {
21795
- var currentSelected = getCurrentSelected();
21796
- if (currentSelected.length > 0) {
21797
- for(var i=0; i<currentSelected.length; i++) {
21798
- replaceControls(currentSelected[i]);
21799
- }
21800
- }
21801
- }
21802
- }, null, null, null, null, mID);
21803
-
21804
- that.selectionManager.on("undo", function () {
21805
- if (!that.selectPoints) return;
21806
- if (!that.keyFocus) return;
21807
- if (that.lastPoints) {
21808
- var tempPoints = zim.copy(that.lastPoints);
21809
- that.lastPoints = zim.copy(that.points);
21810
- that.points = tempPoints;
21811
- if (that.stage) that.stage.update();
21812
- }
21813
- }, null, null, null, null, mID);
21814
-
21815
-
21816
- mapMove = function (target) {
21929
+ function mapMove (target) {
21817
21930
  if (that.lockControls) return;
21818
21931
  var ball;
21819
21932
  if (target.rect1) { // pressmove on ball
@@ -21948,6 +22061,16 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
21948
22061
  });
21949
22062
  that.lockControls = _lockControls;
21950
22063
 
22064
+ Object.defineProperty(that, 'controlType', {
22065
+ get: function() {
22066
+ return controlType;
22067
+ },
22068
+ set: function(value) {
22069
+ controlType = value;
22070
+ originalControlType = value;
22071
+ }
22072
+ });
22073
+
21951
22074
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21952
22075
  // NOTE: extends ZIM CustomShape for more properties and a few functions.
21953
22076
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -21963,7 +22086,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
21963
22086
  });
21964
22087
  if (that.selectPoints && !zim.KEYFOCUS) setFocus();
21965
22088
  function setFocus() {that.keyFocus = true; var d=document.activeElement; if (d) d.blur();}
21966
-
22089
+
21967
22090
 
21968
22091
  Object.defineProperty(that, 'points', {
21969
22092
  get: function() {
@@ -21981,9 +22104,10 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
21981
22104
  zim.decimals(p[3].x),
21982
22105
  zim.decimals(p[3].y)
21983
22106
  ];
21984
- if (p[4] && p[4]!=="straight") point.push(p[4]);
22107
+ if (p[4]) point.push(p[4]);
22108
+ // if (p[4] && p[4]!=="straight") point.push(p[4]);
21985
22109
  points.push(point);
21986
- }
22110
+ }
21987
22111
  return points;
21988
22112
  },
21989
22113
  set: function(value) {
@@ -22137,6 +22261,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
22137
22261
  if (zon) {zogy("Blob() - segmentRatios is read only");}
22138
22262
  }
22139
22263
  });
22264
+
22140
22265
 
22141
22266
  that.approximateBounds = function(num, showPoints) {
22142
22267
  if (zot(num)) num = 80;
@@ -22339,17 +22464,28 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
22339
22464
  var index = insertPointData(points, controls, ratios, percent, controlType);
22340
22465
  that.points = points;
22341
22466
  that.num = points.length;
22467
+ var ev = new createjs.Event("change");
22468
+ ev.controlType = "pointAdd";
22469
+ ev.index = index;
22470
+ that.dispatchEvent(ev);
22471
+ that.dispatchEvent("pointadd");
22472
+ if (that.stage) that.stage.update();
22342
22473
  return index;
22343
22474
  };
22344
22475
 
22345
22476
  this.removePoint = function(index) {
22346
- if (zot(index) || index < 0) return;
22477
+ if (zot(index) || index < 0 || that.points.length <= 2 || index >= that.points.length) return that;
22347
22478
  var currentPoints = zim.copy(that.points);
22348
22479
  currentPoints.splice(index, 1);
22349
22480
  that.points = currentPoints;
22481
+ var ev = new createjs.Event("change");
22482
+ ev.controlType = "pointDelete";
22483
+ ev.index = index;
22484
+ that.dispatchEvent(ev);
22485
+ that.dispatchEvent("pointdelete");
22350
22486
  if (that.stage) that.stage.update();
22351
22487
  return that;
22352
- }
22488
+ };
22353
22489
 
22354
22490
  this.addPoints = function(num, controlType, startPoint, spread, dataOnly, points, even) {
22355
22491
  var sig = "num, controlType, startPoint, spread, dataOnly, points, even";
@@ -29425,7 +29561,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
29425
29561
  that.toggled = false;
29426
29562
  var ch,i;
29427
29563
  var tar = that.content||that.contentContainer||that;
29428
- if (tar.type != "Container") tar = that.contentContainer||that;
29564
+ if (tar.type != "Container" && tar.type != "Tile") tar = that.contentContainer||that;
29429
29565
  for (i=0; i<tar.numChildren; i++) { // record depths first
29430
29566
  ch = tar.getChildAt(i);
29431
29567
  if (ch.type == "TextArea" || ch.type == "Loader" || ch.type == "Tag") {
@@ -29513,7 +29649,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
29513
29649
  this.on("added", show2, null, null, null, null, mID);
29514
29650
  function show2() {
29515
29651
  var tar = that.content||that.contentContainer||that;
29516
- if (tar.type != "Container") tar = that.contentContainer||that;
29652
+ if (tar.type != "Container" && tar.type != "Tile") tar = that.contentContainer||that;
29517
29653
  for (var i=0; i<htmlList.length; i++) {
29518
29654
  tar.addChildAt(htmlList.values[i].obj, htmlList.values[i].depth);
29519
29655
  }
@@ -29727,7 +29863,6 @@ panelHeight - get and set the height of the panel without scaling it as height d
29727
29863
  titleBar - access to the titleBar container - which also has a backing property
29728
29864
  titleBarlabel - access to the label of the current panel
29729
29865
  text - access to the text of the current panel
29730
- titleBarLabel - gives access to the titleBar label
29731
29866
  closeIcon - access to the close button
29732
29867
  collapseIcon - access to the ZIM Shape if there is a collapse triangle
29733
29868
  collapsed - get or set whether the panel is collapsed - must start with collapse parameter set to true
@@ -29907,7 +30042,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
29907
30042
  that.drag({rect:boundary, currentTarget:true, onTop:onTop});
29908
30043
  var ch,i;
29909
30044
  var tar = that.content||that.contentContainer||that;
29910
- if (tar.type != "Container") tar = that.contentContainer||that;
30045
+ if (tar.type != "Container" && tar.type != "Tile") tar = that.contentContainer||that;
29911
30046
  for (i=0; i<tar.numChildren; i++) { // record depths first
29912
30047
  ch = tar.getChildAt(i);
29913
30048
  if (ch.type == "TextArea" || ch.type == "Loader" || ch.type == "Tag") {
@@ -30009,7 +30144,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
30009
30144
  function hide2() {
30010
30145
  var ch,i;
30011
30146
  var tar = that.content||that.contentContainer||that;
30012
- if (tar.type != "Container") tar = that.contentContainer||that;
30147
+ if (tar.type != "Container" && tar.type != "Tile") tar = that.contentContainer||that;
30013
30148
  for (i=0; i<tar.numChildren; i++) { // record depths first
30014
30149
  ch = tar.getChildAt(i);
30015
30150
  if (ch.type == "TextArea" || ch.type == "Loader" || ch.type == "Tag") {
@@ -30904,7 +31039,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
30904
31039
  function hide2() {
30905
31040
  var ch,i;
30906
31041
  var tar = that.content||that.contentContainer||that;
30907
- if (tar.type != "Container") tar = that.contentContainer||that;
31042
+ if (tar.type != "Container" && tar.type != "Tile") tar = that.contentContainer||that;
30908
31043
  for (i=0; i<tar.numChildren; i++) { // record depths first
30909
31044
  ch = tar.getChildAt(i);
30910
31045
  if (ch.type == "TextArea" || ch.type == "Loader" || ch.type == "Tag") {
@@ -37115,49 +37250,49 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
37115
37250
  setLabel(stepperType=="number"?numVal:list[index], stepperType=="number"?numVal:index);
37116
37251
 
37117
37252
  function doStep(n) {
37118
- var nextIndex;
37119
- if (stepperType == "number") {
37120
- numVal = Number(numVal);
37121
- var lastNumVal = numVal;
37122
- numVal += Number(actualStep * n * numDir);
37123
- numVal = zim.decimals(numVal, decimals);
37124
- if (!continuous) {
37125
- if (numVal > that.max) {
37126
- numVal = step==1?that.max:lastNumVal;
37127
- if (display) box.cur("default");
37128
- } else {
37129
- if (display) box.cur("pointer");
37130
- }
37131
- if (numVal < that.min) {
37132
- numVal = step==1?that.min:lastNumVal;
37133
- }
37253
+ var lastValue = that.value; // <-- CAPTURE CURRENT VALUE BEFORE STEPPING
37254
+ var nextIndex;
37255
+ if (stepperType == "number") {
37256
+ numVal = Number(numVal);
37257
+ var lastNumVal = numVal;
37258
+ numVal += Number(actualStep * n * numDir);
37259
+ numVal = zim.decimals(numVal, decimals);
37260
+ if (!continuous) {
37261
+ if (numVal > that.max) {
37262
+ numVal = step==1?that.max:lastNumVal;
37263
+ if (display) box.cur("default");
37134
37264
  } else {
37135
- if (numVal > that.max) {
37136
- numVal = that.min;
37137
- } else if (numVal < that.min) {
37138
- numVal = that.max;
37139
- }
37265
+ if (display) box.cur("pointer");
37266
+ }
37267
+ if (numVal < that.min) {
37268
+ numVal = step==1?that.min:lastNumVal;
37140
37269
  }
37141
37270
  } else {
37142
- nextIndex = index + n;
37143
- if (!continuous) {
37144
- if (nextIndex > list.length-1) {
37145
- if (display) box.cur("default");
37146
- return;
37147
- } else {
37148
- if (display) box.cur("pointer");
37149
- }
37150
- if (nextIndex < 0) return;
37271
+ if (numVal > that.max) {
37272
+ numVal = that.min;
37273
+ } else if (numVal < that.min) {
37274
+ numVal = that.max;
37275
+ }
37276
+ }
37277
+ } else {
37278
+ nextIndex = index + n;
37279
+ if (!continuous) {
37280
+ if (nextIndex > list.length-1) {
37281
+ if (display) box.cur("default");
37282
+ return;
37151
37283
  } else {
37152
- if (nextIndex > list.length-1) nextIndex = 0;
37153
- if (nextIndex < 0) nextIndex = list.length-1;
37284
+ if (display) box.cur("pointer");
37154
37285
  }
37155
- index = nextIndex;
37286
+ if (nextIndex < 0) return;
37287
+ } else {
37288
+ if (nextIndex > list.length-1) nextIndex = 0;
37289
+ if (nextIndex < 0) nextIndex = list.length-1;
37156
37290
  }
37157
- setLabel(stepperType=="number"?numVal:list[index], stepperType=="number"?numVal:index);
37158
- if (that.value != lastValue) that.dispatchEvent("change");
37159
- lastValue = that.value;
37291
+ index = nextIndex;
37160
37292
  }
37293
+ setLabel(stepperType=="number"?numVal:list[index], stepperType=="number"?numVal:index);
37294
+ if (that.value != lastValue) that.dispatchEvent("change");
37295
+ }
37161
37296
 
37162
37297
 
37163
37298
  Object.defineProperty(this, 'index', {
@@ -37395,6 +37530,7 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
37395
37530
 
37396
37531
  function setLabel(text, n, update) {
37397
37532
  index = n;
37533
+ lastValue = that.value; // <-- KEEPS OUTER lastValue IN SYNC ON PROGRAMMATIC SET
37398
37534
  if (zot(update)) update = true;
37399
37535
  if (display) {
37400
37536
  if (stepperType == "number") {
@@ -37404,8 +37540,6 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
37404
37540
  }
37405
37541
  label.text = text;
37406
37542
  label.center(that);
37407
- // label.x = box.x+box.getBounds().width/2;
37408
- // label.y = box.y+(box.getBounds().height-label.getBounds().height)/2;
37409
37543
  }
37410
37544
  if (arrows) setArrows();
37411
37545
  if (update && next && ((!zim.OPTIMIZE&&(zns||!WW.OPTIMIZE)) && next.stage)) {
@@ -57641,7 +57775,7 @@ zim.noGesture = function(obj, move, scale, rotate) {
57641
57775
  };//-34.6
57642
57776
 
57643
57777
  /*--
57644
- obj.gestureBoundary = function(boundary, new)
57778
+ obj.gestureBoundary = function(boundary, update)
57645
57779
 
57646
57780
  gestureBoundary
57647
57781
  zim DisplayObject method
@@ -57682,7 +57816,291 @@ RETURNS obj for chaining
57682
57816
  return obj;
57683
57817
  };//-34.7
57684
57818
 
57685
- //
57819
+
57820
+ /*--
57821
+ obj.tilt = function(boundary, damp, factor, mode, type, call)
57822
+
57823
+ tilt
57824
+ zim DisplayObject method
57825
+
57826
+ DESCRIPTION
57827
+ Moves or positions an object based on device sensors (deviceorientation or devicemotion).
57828
+ Automatically manages user sensor permission via ZIM PermissionAsk if permission has not yet been requested.
57829
+
57830
+ NOTE
57831
+ If permission has already been granted, tilt activates immediately.
57832
+ If multiple objects call tilt() before permission is granted, they are queued and activated together once the user accepts.
57833
+ ALSO see noTilt() to remove tilt behaviors.
57834
+ ALSO see tiltBoundary() to dynamically change or clear the boundary rectangle.
57835
+
57836
+ EXAMPLE
57837
+ // Basic rolling ball constrained to stage bounds
57838
+ const ball = new Circle(30, red)
57839
+ .center()
57840
+ .tilt(S);
57841
+ END EXAMPLE
57842
+
57843
+ EXAMPLE
57844
+ // Custom damping, sensitivity factor, and boundary
57845
+ new Rectangle(80, 80, blue)
57846
+ .center()
57847
+ .tilt({
57848
+ boundary: new Boundary(50, 50, W - 100, H - 100),
57849
+ damp: 0.08,
57850
+ factor: 0.75,
57851
+ call: (success, obj) => {
57852
+ if (!success) new Pane("Device orientation not available", yellow).show();
57853
+ }
57854
+ });
57855
+ END EXAMPLE
57856
+
57857
+ PARAMETERS
57858
+ ** supports DUO - parameters or single object with properties below
57859
+ boundary - (default null) a ZIM Boundary object, an Array of [x, y, width, height], or true for the Stage dimensions
57860
+ damp - (default 0.05) smoothing factor via ZIM Damp, or null / false for immediate direct tracking
57861
+ factor - (default 0.5) sensitivity factor multiplier, or an Object with {x, y} for independent axis sensitivity
57862
+ mode - (default "move") "move" for continuous velocity / rolling behavior, or "position" for tilt offset relative to starting coordinates
57863
+ type - (default "deviceorientation") the sensor event type ("deviceorientation" or "devicemotion")
57864
+ call - (default null) callback function called when permission resolves, receiving (success:Boolean, obj:DisplayObject)
57865
+
57866
+ RETURNS obj for chaining
57867
+ --*///+34.75
57868
+ zim.tilt = function(obj, boundary, damp, factor, mode, type, call) {
57869
+ var sig = "obj, boundary, damp, factor, mode, type, call";
57870
+ var duo; if (duo = zob(zim.tilt, arguments, sig)) return duo;
57871
+ if ((obj.type=="AC"||(obj.type=="Emoji"&&obj.svg&&obj.svg.type=="AC"))&&WW.zdf) {WW.zdf.ac("tilt", arguments); return obj;}
57872
+ z_d("34.75");
57873
+
57874
+ // MONITOR
57875
+ var mID = "z~tilt";
57876
+ if (obj && obj.mID && ((obj.mID[2] && obj.mID[2]=="-") || obj.mID[0]=="-")) mID = "z~-";
57877
+
57878
+ if (zot(obj)) return;
57879
+
57880
+ // Clean up any existing tilt on this object first
57881
+ if (obj._tiltTicker) zim.noTilt(obj);
57882
+
57883
+ var f = obj.frame || (typeof zdf != "undefined" ? zdf : WW.zdf);
57884
+ var s = obj.stage || (f ? f.stage : null);
57885
+
57886
+ // Set initial boundary
57887
+ zim.tiltBoundary(obj, boundary);
57888
+
57889
+ if (zot(damp)) damp = 0.05;
57890
+ if (zot(factor)) factor = 0.5;
57891
+ var factorX = typeof factor === "object" ? factor.x : factor;
57892
+ var factorY = typeof factor === "object" ? factor.y : factor;
57893
+ if (zot(mode)) mode = "move"; // "move" | "position" | "rotation"
57894
+ if (zot(type)) type = "deviceorientation";
57895
+
57896
+ // Damp angles for "move" mode (starts at 0) or positions for "position" mode
57897
+ var dampX = damp ? new zim.Damp(mode == "position" ? obj.x : 0, damp) : null;
57898
+ var dampY = damp ? new zim.Damp(mode == "position" ? obj.y : 0, damp) : null;
57899
+ var currentRot = {x: 0, y: 0, z: 0};
57900
+
57901
+ function updateTilt() {
57902
+ if (mode == "move") {
57903
+ // Damping the rotation angle gives smooth acceleration & deceleration
57904
+ var rotY = dampX ? dampX.convert(currentRot.y) : currentRot.y;
57905
+ var rotX = dampY ? dampY.convert(currentRot.x) : currentRot.x;
57906
+ obj.x += rotY * factorX;
57907
+ obj.y += rotX * factorY;
57908
+ } else if (mode == "position") {
57909
+ var targetX = (obj._tiltStartX != null ? obj._tiltStartX : obj.x) + currentRot.y * factorX * 10;
57910
+ var targetY = (obj._tiltStartY != null ? obj._tiltStartY : obj.y) + currentRot.x * factorY * 10;
57911
+ obj.x = dampX ? dampX.convert(targetX) : targetX;
57912
+ obj.y = dampY ? dampY.convert(targetY) : targetY;
57913
+ }
57914
+
57915
+ // Dynamically applies boundary constraint
57916
+ if (obj._tiltBoundary) {
57917
+ obj.x = zim.constrain(obj.x, obj._tiltBoundary.x, obj._tiltBoundary.x + obj._tiltBoundary.width);
57918
+ obj.y = zim.constrain(obj.y, obj._tiltBoundary.y, obj._tiltBoundary.y + obj._tiltBoundary.height);
57919
+ }
57920
+
57921
+ if (s) s.update();
57922
+ }
57923
+
57924
+ function activate() {
57925
+ obj._tiltStartX = obj.x;
57926
+ obj._tiltStartY = obj.y;
57927
+ if (mode == "position" && dampX && dampY) {
57928
+ dampX.immediate(obj.x);
57929
+ dampY.immediate(obj.y);
57930
+ }
57931
+
57932
+ obj._tiltType = type;
57933
+ obj._tiltListener = function(e) {
57934
+ if (e.rotation) {
57935
+ currentRot.x = e.rotation.x || 0;
57936
+ currentRot.y = e.rotation.y || 0;
57937
+ currentRot.z = e.rotation.z || 0;
57938
+ } else {
57939
+ currentRot.x = e.beta || 0;
57940
+ currentRot.y = e.gamma || 0;
57941
+ currentRot.z = e.alpha || 0;
57942
+ }
57943
+ };
57944
+ f.on(type, obj._tiltListener);
57945
+
57946
+ obj._tiltTicker = zim.Ticker.add(updateTilt);
57947
+ if (call) call(true, obj);
57948
+ }
57949
+
57950
+ // Permission check & queueing
57951
+ if (f._permissionGranted) {
57952
+ activate();
57953
+ } else if (f._permissionPending) {
57954
+ f._permissionQueue.push(activate);
57955
+ } else {
57956
+ f._permissionPending = true;
57957
+ f._permissionQueue = [activate];
57958
+
57959
+ new zim.PermissionAsk(function(yes) {
57960
+ f._permissionPending = false;
57961
+ if (yes) {
57962
+ f._permissionGranted = true;
57963
+ zim.loop(f._permissionQueue, function(fn) { fn(); });
57964
+ f._permissionQueue = [];
57965
+ } else {
57966
+ if (call) call(false, obj);
57967
+ }
57968
+ }, type);
57969
+ }
57970
+
57971
+ return obj;
57972
+ };//-34.75
57973
+
57974
+
57975
+ /*--
57976
+ obj.noTilt = function()
57977
+
57978
+ noTilt
57979
+ zim DisplayObject method
57980
+
57981
+ DESCRIPTION
57982
+ Removes tilt listeners, removes internal ticker updates, and clears tilt boundary properties from the object.
57983
+
57984
+ EXAMPLE
57985
+ const player = new Circle(25, green).center().tilt(true);
57986
+
57987
+ // Stop tilt control when game ends or is paused
57988
+ player.noTilt();
57989
+ END EXAMPLE
57990
+
57991
+ RETURNS obj for chaining
57992
+ --*///+34.76
57993
+ zim.noTilt = function(obj) {
57994
+ var sig = "obj";
57995
+ var duo; if (duo = zob(zim.noTilt, arguments, sig)) return duo;
57996
+ z_d("34.76");
57997
+
57998
+ if (zot(obj) || !obj.on || !obj.zimTouch) return;
57999
+
58000
+ // MONITOR
58001
+ var mID = "z~noTilt";
58002
+ if (obj && obj.mID && ((obj.mID[2] && obj.mID[2]=="-") || obj.mID[0]=="-")) mID = "z~-";
58003
+
58004
+ if (zot(obj)) return;
58005
+ var f = obj.frame || (typeof zdf != "undefined" ? zdf : WW.zdf);
58006
+
58007
+ if (obj._tiltListener && f) {
58008
+ f.off(obj._tiltType || "deviceorientation", obj._tiltListener);
58009
+ obj._tiltListener = null;
58010
+ }
58011
+ if (obj._tiltTicker) {
58012
+ zim.Ticker.remove(obj._tiltTicker);
58013
+ obj._tiltTicker = null;
58014
+ }
58015
+ obj._tiltBoundary = null;
58016
+
58017
+ return obj;
58018
+
58019
+ };//-34.76
58020
+
58021
+ /*--
58022
+ obj.tiltBoundary = function(boundary, y, width, height)
58023
+
58024
+ tiltBoundary
58025
+ zim DisplayObject method
58026
+
58027
+ DESCRIPTION
58028
+ Dynamically sets, updates, or removes the boundary rectangle for an object with tilt().
58029
+
58030
+ EXAMPLE
58031
+ const marble = new Circle(20, purple).center().tilt(true);
58032
+
58033
+ // Update boundary dynamically later (e.g., on resize or game level change)
58034
+ marble.tiltBoundary(new Boundary(50, 50, W - 100, H - 100));
58035
+
58036
+ // Or clear the boundary
58037
+ // marble.tiltBoundary(null);
58038
+ END EXAMPLE
58039
+
58040
+ PARAMETERS
58041
+ ** supports DUO - parameters or single object with properties below
58042
+ boundary - (default null) a ZIM Boundary object, an Array of [x, y, width, height], true for Frame bounds, null / false to remove boundary, or the x position if passing (x, y, width, height) positionally
58043
+ y - (default null) y coordinate if boundary parameter is used as x
58044
+ width - (default null) width if boundary parameter is used as x
58045
+ height - (default null) height if boundary parameter is used as x
58046
+
58047
+ RETURNS obj for chaining
58048
+ --*///+34.77
58049
+ zim.tiltBoundary = function(obj, boundary, y, width, height) {
58050
+ var sig = "obj, boundary, y, width, height";
58051
+ var duo; if (duo = zob(zim.tiltBoundary, arguments, sig)) return duo;
58052
+ z_d("34.77");
58053
+ if (zot(obj) || !obj.on) return;
58054
+ var f = obj.frame || (typeof zdf != "undefined" ? zdf : WW.zdf);
58055
+ var s = obj.stage || (f ? f.stage : null);
58056
+
58057
+ // Helper to calculate bounded limits keeping obj inside container bounds
58058
+ function getContainedBoundary(bx, by, bw, bh) {
58059
+ var b = obj.getBounds ? obj.getBounds() : null;
58060
+ if (b) {
58061
+ return new zim.Boundary(
58062
+ bx - b.x * obj.scaleX,
58063
+ by - b.y * obj.scaleY,
58064
+ Math.max(0, bw - b.width * obj.scaleX),
58065
+ Math.max(0, bh - b.height * obj.scaleY)
58066
+ );
58067
+ }
58068
+ return new zim.Boundary(bx, by, bw, bh);
58069
+ }
58070
+
58071
+ if (zot(boundary) || boundary === false || boundary === null) {
58072
+ obj._tiltBoundary = null;
58073
+ } else if (boundary === true) {
58074
+ // True -> Stage dimensions
58075
+ var sw = f ? f.width : (s ? s.width : 0);
58076
+ var sh = f ? f.height : (s ? s.height : 0);
58077
+ obj._tiltBoundary = getContainedBoundary(0, 0, sw, sh);
58078
+ } else if (boundary && (boundary.type == "Stage" || boundary == s || (s && boundary == s.canvas))) {
58079
+ // Stage passed as boundary
58080
+ var sw = boundary.width || (f ? f.width : 0);
58081
+ var sh = boundary.height || (f ? f.height : 0);
58082
+ obj._tiltBoundary = getContainedBoundary(0, 0, sw, sh);
58083
+ } else if (boundary && boundary.type != "Boundary" && !Array.isArray(boundary) && (boundary.getBounds || boundary.width != null)) {
58084
+ // DisplayObject (Container, Rectangle, etc.) passed as boundary
58085
+ var b = boundary.getBounds ? boundary.getBounds() : null;
58086
+ var bw = boundary.width != null ? boundary.width : (b ? b.width : 0);
58087
+ var bh = boundary.height != null ? boundary.height : (b ? b.height : 0);
58088
+ var bx = boundary.x || 0;
58089
+ var by = boundary.y || 0;
58090
+ obj._tiltBoundary = getContainedBoundary(bx, by, bw, bh);
58091
+ } else if (Array.isArray(boundary)) {
58092
+ obj._tiltBoundary = new zim.Boundary(boundary[0], boundary[1], boundary[2], boundary[3]);
58093
+ } else if (typeof boundary === "number" && !zot(y)) {
58094
+ obj._tiltBoundary = new zim.Boundary(boundary, y, width, height);
58095
+ } else if (boundary && boundary.type == "Boundary") {
58096
+ obj._tiltBoundary = boundary;
58097
+ } else {
58098
+ obj._tiltBoundary = null;
58099
+ }
58100
+
58101
+ return obj;
58102
+ };//-34.77
58103
+
57686
58104
  /*--
57687
58105
  obj.effect = function(effect, x, y, width, height)
57688
58106
 
@@ -75313,6 +75731,7 @@ multiple - true if the multipleSelect key is being pressed otherwise false
75313
75731
  ctrlKey - true if the ctrlKey key is being pressed otherwise false
75314
75732
  shiftKey - true if the shiftKey key is being pressed otherwise false
75315
75733
  metaKey - true if the metaKey key is being pressed otherwise false
75734
+ undoKey - true set to false to not handle undo
75316
75735
  enabled - set to false to disable and true to enable - will remember selections when enabled
75317
75736
 
75318
75737
  EVENTS
@@ -75326,6 +75745,7 @@ dispatches an "undo" event if a CTRL or META plus the U key is pressed
75326
75745
  // MONITOR
75327
75746
  this.mID = "z~"+this.type;
75328
75747
 
75748
+
75329
75749
  if (zot(sets)) sets = [];
75330
75750
  if (zot(multipleSets)) multipleSets = true;
75331
75751
  if (!Array.isArray(sets)) sets = [sets];
@@ -75333,6 +75753,7 @@ dispatches an "undo" event if a CTRL or META plus the U key is pressed
75333
75753
  this.multipleKey = multipleKey;
75334
75754
  this.multipleSets = multipleSets;
75335
75755
  this.multiple = false;
75756
+ this.undoKey = true;
75336
75757
  var that = this;
75337
75758
  var _enabled = true;
75338
75759
  for (var i=0; i<sets.length; i++) {
@@ -75361,7 +75782,7 @@ dispatches an "undo" event if a CTRL or META plus the U key is pressed
75361
75782
  that.metaKey = e.metaKey;
75362
75783
  if (!_enabled) return;
75363
75784
  that.dispatchEvent(e);
75364
- if (e.keyCode==90 && (that.ctrlKey || that.metaKey)) {
75785
+ if (that.undoKey && e.keyCode==90 && (that.ctrlKey || that.metaKey)) {
75365
75786
  that.dispatchEvent("undo");
75366
75787
  }
75367
75788
  };
@@ -81218,7 +81639,7 @@ alpha, cursor, shadow, name, mouseChildren, mouseEnabled, parent, numChildren, e
81218
81639
  //-69.9747
81219
81640
 
81220
81641
  /*--
81221
- zim.Perspective = function(obj, points, interactive, showControls, allowToggle, move, borderColor, borderWidth, dashed, plane, fade, dynamic, frame, resolution, maskShape, feather, onTop, overlay, clipping, passthrough, passthroughScale, passthroughShiftX, passthroughShiftY, style, group, inherit)
81642
+ zim.Perspective = function(obj, points, interactive, showControls, allowToggle, move, borderColor, borderWidth, dashed, plane, fade, dynamic, frame, resolution, maskShape, feather, onTop, overlay, clipping, passthrough, passthroughScale, passthroughShiftX, passthroughShiftY, passthroughRotation, style, group, inherit)
81222
81643
 
81223
81644
  Perspective
81224
81645
  zim class extends ZIM Container which extends CreateJS Container
@@ -81357,8 +81778,9 @@ fade - (default true) if a plane is set, this will fade the distance
81357
81778
  dynamic - (default true unless obj is Pic, then false) set to false if a Container, etc. does not change or animate
81358
81779
  frame - (default zimDefaultFrame) set the zim Frame the perspective is in if in a different frame than the default frame
81359
81780
  resolution - (default 1) the resolution multiplier for the internal WebGL shader canvas (e.g. 0.5 for half-resolution to boost GPU performance on laptops and mobile).
81360
- maskShape - (default "rect") the cutout mask shape: "rect" (standard 4-corner quad), "circle" (1:1 true circle with aspect-ratio correction), or "ellipse" (stretches to the quad bounds)
81781
+ maskShape - (default eye) the cutout mask shape: "rect" (standard 4-corner quad), "circle" (1:1 true circle with aspect-ratio correction), or "ellipse" (stretches to the quad bounds)
81361
81782
  or a ZIM DisplayObject (such as a Blob, Circle, or Container) to use as a dynamic GPU alpha mask texture (passed into iChannel1 with multi-tap blur spread).
81783
+ or true to get a Blob in eye shape points radius min of width and height / 2
81362
81784
  feather - (default 0) edge softness from 0 to 1 applied to the quad edges, circle/ellipse borders, or texture mask in the GPU shader.
81363
81785
  onTop - (default true) set to false to not bring the object to the top of its container when pressed
81364
81786
  overlay - (default false) set to true to use a DOM ShaderOverlay (Tag) rather than an in-canvas Shader (Bitmap)
@@ -81372,6 +81794,7 @@ passthrough - (default false) set to true to display the asset unwarped (at orig
81372
81794
  passthroughScale - (default 1) scale multiplier for the asset when passthrough is true, scaling from the center of the asset.
81373
81795
  passthroughShiftX - (default 0) horizontal pixel offset for the asset beneath the crop window when passthrough is true.
81374
81796
  passthroughShiftY - (default 0) vertical pixel offset for the asset beneath the crop window when passthrough is true.
81797
+ passthroughRotation - (default 0) rotation for the asset beneath the crop window when passthrough is true.
81375
81798
  style - (default true) set to false to ignore styles set with the STYLE - will receive original parameter defaults
81376
81799
  group - (default null) set to String (or comma delimited String) so STYLE can set default styles to the group(s) (like a CSS class)
81377
81800
  inherit - (default null) used internally but can receive an {} of styles directly
@@ -81417,7 +81840,6 @@ toggled - get control state - true if showing and false if not - see toggle() me
81417
81840
  maskShape - get or set the cutout shape "rect", "circle", "ellipse", or the DisplayObject provided
81418
81841
  feather - get or set the edge softness / feathering amount (0 to 1).
81419
81842
  resolution - the resolution multiplier of the shader canvas.
81420
- mask - reference to the mask DisplayObject container if a mask was provided.
81421
81843
  onTop - get or set whether the perspective comes to the top of its container when pressed
81422
81844
  overlay - get or set whether to render as a DOM ShaderOverlay - see overlay parameter
81423
81845
  clipping - get or set whether the overlay canvas bounding box is clamped to the stage boundaries
@@ -81435,8 +81857,8 @@ x, y, rotation, scaleX, scaleY, regX, regY, skewX, skewY,
81435
81857
  alpha, cursor, shadow, name, mouseChildren, mouseEnabled, parent, numChildren, etc.
81436
81858
 
81437
81859
  --*///+69.9748
81438
- zim.Perspective = function(obj, points, interactive, showControls, allowToggle, move, borderColor, borderWidth, dashed, plane, fade, dynamic, frame, resolution, maskShape, feather, onTop, overlay, clipping, passthrough, passthroughScale, passthroughShiftX, passthroughShiftY, style, group, inherit) {
81439
- var sig = "obj, points, interactive, showControls, allowToggle, move, borderColor, borderWidth, dashed, plane, fade, dynamic, frame, resolution, maskShape, feather, onTop, overlay, clipping, passthrough, passthroughScale, passthroughShiftX, passthroughShiftY, style, group, inherit";
81860
+ zim.Perspective = function(obj, points, interactive, showControls, allowToggle, move, borderColor, borderWidth, dashed, plane, fade, dynamic, frame, resolution, maskShape, feather, onTop, overlay, clipping, passthrough, passthroughScale, passthroughShiftX, passthroughShiftY, passthroughRotation, style, group, inherit) {
81861
+ var sig = "obj, points, interactive, showControls, allowToggle, move, borderColor, borderWidth, dashed, plane, fade, dynamic, frame, resolution, maskShape, feather, onTop, overlay, clipping, passthrough, passthroughScale, passthroughShiftX, passthroughShiftY, passthroughRotation, style, group, inherit";
81440
81862
  var duo; if (duo = zob(zim.Perspective, arguments, sig, this)) return duo;
81441
81863
  z_d("69.9770");
81442
81864
  this.group = group;
@@ -81459,6 +81881,28 @@ zim.Perspective = function(obj, points, interactive, showControls, allowToggle,
81459
81881
  if (zot(frame)) frame = DS.frame != null ? DS.frame : zdf;
81460
81882
  if (zot(resolution)) resolution = DS.resolution != null ? DS.resolution : 1;
81461
81883
  if (zot(maskShape)) maskShape = DS.maskShape != null ? DS.maskShape : "rect";
81884
+ if (maskShape === true) {
81885
+ maskShape = new zim.Blob({
81886
+ points:[
81887
+ [0,90,0,0,-80,0,80,0,"straight"],
81888
+ [150,0,0,0,0,0,0,0,"none"],
81889
+ [0,-90,0,0,80,0,-80,0,"straight"],
81890
+ [-150,0,0,0,0,0,0,0,"none"]
81891
+ ],
81892
+ interactive: false
81893
+ })
81894
+ if (obj && obj.width) {
81895
+ var targetW = obj.width;
81896
+ var targetH = obj.height || 1;
81897
+ var factor = Math.min(targetW / maskShape.width, targetH / maskShape.height);
81898
+ maskShape.transformPoints("scale", factor);
81899
+ }
81900
+ } else if (maskShape && maskShape.type=="Blob" && maskShape.scale != 1) {
81901
+ var factor = maskShape.scale;
81902
+ maskShape.scale = 1;
81903
+ maskShape.transformPoints("scale", factor);
81904
+ }
81905
+ if (maskShape && maskShape.type=="Blob") maskShape.hideControls();
81462
81906
  if (zot(feather)) feather = DS.feather != null ? DS.feather : 0;
81463
81907
  if (zot(onTop)) onTop = DS.onTop != null ? DS.onTop : true;
81464
81908
  if (zot(overlay)) overlay = DS.overlay != null ? DS.overlay : false;
@@ -81467,6 +81911,7 @@ zim.Perspective = function(obj, points, interactive, showControls, allowToggle,
81467
81911
  if (zot(passthroughScale)) passthroughScale = DS.passthroughScale != null ? DS.passthroughScale : 1;
81468
81912
  if (zot(passthroughShiftX)) passthroughShiftX = DS.passthroughShiftX != null ? DS.passthroughShiftX : 0;
81469
81913
  if (zot(passthroughShiftY)) passthroughShiftY = DS.passthroughShiftY != null ? DS.passthroughShiftY : 0;
81914
+ if (zot(passthroughRotation)) passthroughRotation = DS.passthroughRotation != null ? DS.passthroughRotation : 0;
81470
81915
 
81471
81916
  var size = Math.max(frame.width, frame.height);
81472
81917
 
@@ -81484,7 +81929,7 @@ zim.Perspective = function(obj, points, interactive, showControls, allowToggle,
81484
81929
  var frag = "uniform vec2 a; uniform vec2 b; uniform vec2 c; uniform vec2 d;\n";
81485
81930
  frag += "uniform float plane;\nuniform float fade;\n";
81486
81931
  frag += "uniform float shape;\nuniform float feather;\nuniform float aspect;\nuniform float useMaskTex;\n";
81487
- frag += "uniform float passthrough;\nuniform float passthroughScale;\nuniform vec2 passthroughCenter;\nuniform vec2 passthroughShift;\nuniform vec2 assetNormSize;\n";
81932
+ frag += "uniform float passthrough;\nuniform float passthroughScale;\nuniform float passthroughRotation;\nuniform vec2 passthroughCenter;\nuniform vec2 passthroughShift;\nuniform vec2 assetNormSize;\n";
81488
81933
  frag += "#define map(screenpos) ((2.0*(screenpos)-iResolution.xy)/iResolution.y)\n";
81489
81934
  frag += "mat3 rproject(vec3 p0, vec3 p1, vec3 p2, vec3 p3) {\n";
81490
81935
  frag += " mat3 m = inverse(mat3(p0,p1,p2));\n";
@@ -81516,7 +81961,12 @@ zim.Perspective = function(obj, points, interactive, showControls, allowToggle,
81516
81961
  frag += " vec2 texUV;\n";
81517
81962
  frag += " if (passthrough > 0.5) {\n";
81518
81963
  frag += " vec2 pScale = max(vec2(0.0001), assetNormSize * passthroughScale);\n";
81519
- frag += " texUV = (p - passthroughCenter - passthroughShift) / pScale + vec2(0.5);\n";
81964
+ frag += " vec2 pDiff = p - passthroughCenter - passthroughShift;\n";
81965
+ frag += " float rad = radians(-passthroughRotation);\n";
81966
+ frag += " float cosA = cos(rad);\n";
81967
+ frag += " float sinA = sin(rad);\n";
81968
+ frag += " pDiff = mat2(cosA, -sinA, sinA, cosA) * pDiff;\n";
81969
+ frag += " texUV = pDiff / pScale + vec2(0.5);\n";
81520
81970
  frag += " if (texUV.x < 0.0 || texUV.x > 1.0 || texUV.y < 0.0 || texUV.y > 1.0) {\n";
81521
81971
  frag += " fragColor = vec4(0.0);\n";
81522
81972
  frag += " return;\n";
@@ -81525,9 +81975,13 @@ zim.Perspective = function(obj, points, interactive, showControls, allowToggle,
81525
81975
  frag += " texUV = tileUV;\n";
81526
81976
  frag += " }\n";
81527
81977
  frag += " vec4 texColor = texture(iChannel0, texUV);\n";
81978
+ frag += " float alpha = texColor.a;\n";
81979
+ frag += " if (alpha <= 0.0) {\n";
81980
+ frag += " fragColor = vec4(0.0);\n";
81981
+ frag += " return;\n";
81982
+ frag += " }\n";
81528
81983
  frag += " vec3 color = pow(texColor.rgb, vec3(2.2));\n";
81529
81984
  frag += " color = pow(color, vec3(0.4545));\n";
81530
- frag += " float alpha = texColor.a;\n";
81531
81985
  frag += " // 3. Circle / Ellipse Feather (applied to tileUV)\n";
81532
81986
  frag += " if (shape > 0.5) {\n";
81533
81987
  frag += " vec2 centerCoord = (tileUV - vec2(0.5)) * 2.0;\n";
@@ -81578,7 +82032,7 @@ zim.Perspective = function(obj, points, interactive, showControls, allowToggle,
81578
82032
  frag += " color *= distFade;\n";
81579
82033
  frag += " alpha *= distFade;\n";
81580
82034
  frag += " }\n";
81581
- frag += " fragColor = vec4(color, alpha);\n";
82035
+ frag += " fragColor = vec4(color * alpha, alpha);\n";
81582
82036
  frag += "}\n";
81583
82037
 
81584
82038
  this.zimContainer_constructor(size, size, null, null, false);
@@ -81594,6 +82048,7 @@ zim.Perspective = function(obj, points, interactive, showControls, allowToggle,
81594
82048
  var _maskShape = maskShape;
81595
82049
  var _passthrough = Boolean(passthrough);
81596
82050
  var _passthroughScale = Number(passthroughScale) || 1;
82051
+ var _passthroughRotation = Number(passthroughRotation) || 0;
81597
82052
  var _passthroughShiftX = Number(passthroughShiftX) || 0;
81598
82053
  var _passthroughShiftY = Number(passthroughShiftY) || 0;
81599
82054
  var _passthroughCenter = null;
@@ -81744,6 +82199,10 @@ zim.Perspective = function(obj, points, interactive, showControls, allowToggle,
81744
82199
  var objH = obj.height || (obj.getBounds ? (obj.getBounds() ? obj.getBounds().height : size) : size);
81745
82200
  var aspect = objW / objH;
81746
82201
 
82202
+ // FIX: Shift registration to the internal buffer center and set bounds to the actual object size
82203
+ that.reg(size/2, size/2);
82204
+ that.setBounds(size/2 - objW/2, size/2 - objH/2, objW, objH);
82205
+
81747
82206
  var shapeVal = 0;
81748
82207
  var useMaskTexVal = 0;
81749
82208
  if (typeof _maskShape === "string") {
@@ -81769,6 +82228,7 @@ zim.Perspective = function(obj, points, interactive, showControls, allowToggle,
81769
82228
  useMaskTex: useMaskTexVal,
81770
82229
  passthrough: _passthrough ? 1 : 0,
81771
82230
  passthroughScale: _passthroughScale,
82231
+ passthroughRotation: _passthroughRotation,
81772
82232
  passthroughCenter: [initCx, initCy],
81773
82233
  passthroughShift: [_passthroughShiftX / size, -_passthroughShiftY / size],
81774
82234
  assetNormSize: [objW / size, objH / size]
@@ -81936,6 +82396,9 @@ zim.Perspective = function(obj, points, interactive, showControls, allowToggle,
81936
82396
  uniforms.useMaskTex = 1;
81937
82397
  setupMaskSource(value);
81938
82398
  if (that.shader) that.shader.replaceChannel(maskContainer, 1, false);
82399
+ } else {
82400
+ uniforms.shape = 0;
82401
+ uniforms.useMaskTex = 0;
81939
82402
  }
81940
82403
  }
81941
82404
  });
@@ -81970,6 +82433,16 @@ zim.Perspective = function(obj, points, interactive, showControls, allowToggle,
81970
82433
  }
81971
82434
  });
81972
82435
 
82436
+ Object.defineProperty(this, "passthroughRotation", {
82437
+ get: function() {
82438
+ return _passthroughRotation;
82439
+ },
82440
+ set: function(value) {
82441
+ _passthroughRotation = Number(value) || 0;
82442
+ uniforms.passthroughRotation = _passthroughRotation;
82443
+ }
82444
+ });
82445
+
81973
82446
  Object.defineProperty(this, "passthroughShiftX", {
81974
82447
  get: function() {
81975
82448
  return _passthroughShiftX;
@@ -82296,7 +82769,7 @@ zim.Perspective = function(obj, points, interactive, showControls, allowToggle,
82296
82769
  if (style !== false) zim.styleTransforms(this, DS);
82297
82770
 
82298
82771
  this.clone = function(exact) {
82299
- return that.cloneProps(new zim.Perspective((exact || !zim.isPick(oa[0])) ? obj : oa[0], points, interactive, showControls, allowToggle, move, (exact || !zim.isPick(oa[1])) ? borderColor : oa[1], (exact || !zim.isPick(oa[2])) ? borderWidth : oa[2], (exact || !zim.isPick(oa[3])) ? dashed : oa[3], plane, fade, dynamic, frame, resolution, _maskShape, feather, _onTop, _overlay, _clipping, _passthrough, _passthroughScale, _passthroughShiftX, _passthroughShiftY, style, this.group, inherit));
82772
+ return that.cloneProps(new zim.Perspective((exact || !zim.isPick(oa[0])) ? obj : oa[0], points, interactive, showControls, allowToggle, move, (exact || !zim.isPick(oa[1])) ? borderColor : oa[1], (exact || !zim.isPick(oa[2])) ? borderWidth : oa[2], (exact || !zim.isPick(oa[3])) ? dashed : oa[3], plane, fade, dynamic, frame, resolution, _maskShape, feather, _onTop, _overlay, _clipping, _passthrough, _passthroughScale, _passthroughShiftX, _passthroughShiftY, _passthroughRotation, style, this.group, inherit));
82300
82773
  };
82301
82774
  };
82302
82775
  zim.extend(zim.Perspective, zim.Container, ["clone", "dispose"], "zimContainer", false);
@@ -93556,6 +94029,13 @@ zim.Frame = function(scaling, width, height, color, outerColor, ready, assets, p
93556
94029
  var imagesNoCORS = [];
93557
94030
  var mainCount = 0;
93558
94031
  var firstSoundCheck = true;
94032
+
94033
+ var videoRegex = /\.(mp4|mkv|webm|avi|mov|wmv|flv|m4v|3gp|ts)(?=[?#]|$)/i;
94034
+ function isVideoUrl(url) {
94035
+ if (url && url.src) url = url.src;
94036
+ if (typeof url !== 'string') return false;
94037
+ return videoRegex.test(url);
94038
+ }
93559
94039
 
93560
94040
  // 018 TRYING TO FIX CREATEJS ERROR IF SOUND LOADED AGAIN - BUT BREAKS LAZY LOAD
93561
94041
  // var emptyAssets = false;
@@ -93570,7 +94050,7 @@ zim.Frame = function(scaling, width, height, color, outerColor, ready, assets, p
93570
94050
  // // }
93571
94051
  // }
93572
94052
 
93573
-
94053
+
93574
94054
 
93575
94055
  var match;
93576
94056
  if (a.match) match = a.match(/^noto_(.*)/);
@@ -93586,6 +94066,7 @@ zim.Frame = function(scaling, width, height, color, outerColor, ready, assets, p
93586
94066
  for (j=0; j<a.assets.length; j++) {
93587
94067
  var aj = a.assets[j];
93588
94068
  match = null;
94069
+ if (isVideoUrl(aj)) continue;
93589
94070
  if (aj.match) match = aj.match(/^noto_(.*)/);
93590
94071
  if (match) {
93591
94072
  aj = {id:aj, src:"https://raw.githubusercontent.com/googlefonts/noto-emoji/main/svg/" + getNotoFilename(match[1])}
@@ -93619,7 +94100,8 @@ zim.Frame = function(scaling, width, height, color, outerColor, ready, assets, p
93619
94100
  }
93620
94101
  a = assetMulti[0];
93621
94102
  if (a==undefined) continue;
93622
- }
94103
+ }
94104
+ if (isVideoUrl(a))continue;
93623
94105
 
93624
94106
  // catch {src} without id and just replace it with src unless google font
93625
94107
  if (a.constructor == {}.constructor) {
@@ -94005,6 +94487,11 @@ zim.Frame = function(scaling, width, height, color, outerColor, ready, assets, p
94005
94487
  if (!queueOnly) that.dispatchEvent(completeEventObject);
94006
94488
  }, time*(timeType=="s"?1000:1));
94007
94489
  }
94490
+
94491
+ if (queue.loadAssetsCount == 0) {
94492
+ endAssetLoad();
94493
+ }
94494
+
94008
94495
  return queue;
94009
94496
  };
94010
94497
 
@@ -96366,11 +96853,12 @@ bitmap - (default true) this will show the SVG as a Bitmap (still nicely scalabl
96366
96853
  or set to false to use an SVGContainer() which converts the SVG to Blob, Squiggle and ZIM Shapes with transforms.
96367
96854
  This allows for editable paths - but perhaps not all aspects of SVGs are supported such as CSS styles on SVGs.
96368
96855
  Alternatively, a single SVG path can be passed to a ZIM Blob() or Squiggle() and SVG() can be avoided.
96856
+ Note: when color, borderColor, or order is set then bitmap is set to false
96369
96857
  ** these parameters are NOT for the bitmap option but rather for the SVGContainer
96370
96858
  splitTypes - (default false) - set to true to split different types of paths into separate objects
96371
96859
  geometric - (default true) - set to false to load Rectangle and Circle objects as Blob objects
96372
96860
  showControls - (default true) set to false to start with controls not showing
96373
- interactive - (default true) for bitmap:false, set to false to turn off controls, move, toggle, select, edit - leaving just the shapes
96861
+ interactive - (default true or false if bitmap or colored below) set to false to turn off controls, move, toggle, select, edit - leaving just the shapes
96374
96862
  color - |ZIM VEE| (default null) for bitmap:false, set to a color or an array of colors to apply to fill of children - in order of the children
96375
96863
  usually a series() would be used here for colors in order, or an array for random colors
96376
96864
  borderColor - |ZIM VEE| (default null) set to a color or an array of colors to apply to border of children - in order of the children
@@ -96437,10 +96925,16 @@ added, click, dblclick, mousedown, mouseout, mouseover, pressdown (ZIM), pressmo
96437
96925
  if (zot(splitTypes)) splitTypes = DS.splitTypes!=null?DS.splitTypes:false;
96438
96926
  if (zot(geometric)) geometric = DS.geometric!=null?DS.geometric:true;
96439
96927
  if (zot(showControls)) showControls = DS.showControls!=null?DS.showControls:null;
96440
- if (zot(interactive)) interactive = DS.interactive!=null?DS.interactive:true;
96441
96928
  if (zot(color)) color = DS.color!=null?DS.color:null;
96442
96929
  if (zot(borderColor)) borderColor = DS.borderColor!=null?DS.borderColor:null;
96443
96930
  if (zot(order)) order = DS.order!=null?DS.order:null;
96931
+
96932
+ var colored = false;
96933
+ if (color || borderColor || order) {
96934
+ bitmap = false;
96935
+ colored = true;
96936
+ }
96937
+ if (zot(interactive)) interactive = DS.interactive!=null?DS.interactive:!colored; // don't set to interactive if colored
96444
96938
 
96445
96939
  var setColors = [];
96446
96940
  var setBorderColors = [];
@@ -96871,277 +97365,606 @@ Note: there are more features to the Web Speech API - see the HTML docs
96871
97365
  //-83.095
96872
97366
 
96873
97367
  /*--
96874
- Hardware = function(callback, baudRate, delimiter)
97368
+ Hardware = function(preset, callback, baudRate, delimiter, mode, autoParse)
96875
97369
 
96876
97370
  Hardware
96877
97371
  zim class - extends a createjs.EventDispatcher
97372
+ Supports DUO - pass parameters positionally or as a single configuration object {preset: "microblocks", callback: ...}
96878
97373
 
96879
97374
  DESCRIPTION
96880
- Reads from and writes to a device such as an Ardiuno
96881
- Wraps the JS Web Serial API
96882
- https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API
96883
- Coded primarily by Gemini AI
97375
+ Connects interactive ZIM canvas applications directly to physical microcontrollers, sensors, dials, and actuators.
97376
+ Communicates via standard USB cables using the JS Web Serial API or wirelessly via the JS Web Bluetooth (BLE UART) API.
97377
+ Works with MicroBlocks, BBC micro:bit, Raspberry Pi Pico / RP2040, Arduino, Adafruit Circuit Playground / Feather (CircuitPython / MicroPython), ESP32, and LEGO SPIKE Prime.
97378
+ Automatically parses incoming data into JavaScript numbers, key-value pairs, or JSON objects.
96884
97379
 
96885
97380
  See: https://zimjs.com/hardware/
96886
97381
 
96887
- NOTE: as of ZIM 5.5.0 the zim namespace is no longer required (unless zns is set to true before running zim)
97382
+ NOTE: Browser security requires that hardware.connect() be called inside a user gesture (such as a Button tap or Pane close). Supported in Chromium-based browsers (Chrome, Edge, Opera) on desktop, Android, and Chromebooks.
96888
97383
 
96889
97384
  EXAMPLE
96890
- // USING ZIM as INPUT to ARDUINO
97385
+ // -------------------------------------------------------
97386
+ // ---- EXAMPLE 1: ARDUINO (Bidirectional LED & Dial) ----
97387
+ // -------------------------------------------------------
97388
+ // On Arduino Uno/Nano: connect a potentiometer to A0 and built-in LED on Pin 13
97389
+ // Upload the following Arduino C++ code:
96891
97390
 
96892
- // would need an arduino connected
96893
- // with the following .ino file loaded onto it
96894
-
96895
- // ------------------------
96896
- // ---- Arduinin Code -----
96897
-
96898
- const int LED_PIN = 13; // Built-in LED on your Uno R3 board
97391
+ const int potPin = A0;
97392
+ const int ledPin = 13;
96899
97393
  void setup() {
96900
- Serial.begin(9600); // Initialize background channel at matching speed
96901
- pinMode(LED_PIN, OUTPUT); // Configure Pin 13 as an output light source
97394
+ Serial.begin(9600);
97395
+ pinMode(ledPin, OUTPUT);
96902
97396
  }
96903
97397
  void loop() {
96904
- // Check if ZIM has sent a data character over the USB cable
97398
+ // Read Potentiometer and send to ZIM
97399
+ int potValue = analogRead(potPin);
97400
+ Serial.println(potValue); // sends number + \n
97401
+
97402
+ // Receive LED command from ZIM
96905
97403
  if (Serial.available() > 0) {
96906
- char incomingChar = Serial.read(); // Read the character data fragment
96907
- if (incomingChar == '1') {
96908
- digitalWrite(LED_PIN, HIGH); // Turn physical light ON
96909
- }
96910
- else if (incomingChar == '0') {
96911
- digitalWrite(LED_PIN, LOW); // Turn physical light OFF
96912
- }
97404
+ char c = Serial.read();
97405
+ if (c == '1') digitalWrite(ledPin, HIGH);
97406
+ else if (c == '0') digitalWrite(ledPin, LOW);
96913
97407
  }
97408
+ delay(30);
96914
97409
  }
96915
97410
 
96916
- // -------------------
96917
- // ---- ZIM Code -----
97411
+ // ZIM Front-End Code:
97412
+ // Preset "arduino" sets baud rate to 9600 automatically
97413
+ const device = new Hardware("arduino", data => {
97414
+ dial.currentValue = data; // data automatically parsed as Number (0-1023)
97415
+ S.update();
97416
+ });
96918
97417
 
96919
- const device = new Hardware();
97418
+ const dial = new Dial({min: 0, max: 1023}).center();
96920
97419
 
96921
- const toggle = new Toggle({
96922
- label: "LED POWER",
96923
- toggleBackgroundColor: green,
96924
- color: white
96925
- })
96926
- .center()
97420
+ const toggle = new Toggle({label: "ARDUINO LED", toggleBackgroundColor: green})
97421
+ .pos(0, 100, CENTER, BOTTOM)
96927
97422
  .change(() => {
96928
- if (toggle.toggled) {
96929
- device.write("1"); // note a \n will be automatically added if missing
96930
- } else {
96931
- device.write("0");
96932
- }
97423
+ device.write(toggle.toggled ? "1" : "0");
96933
97424
  });
96934
97425
 
96935
- // must interact to connect
96936
- const pane = new Pane("CONNECT", yellow).show(() => {
96937
- device.connect()
96938
- });
96939
-
96940
- device.on("disconnected", () => {
96941
- pane.show(() => { // remember to add the callback again
97426
+ // Connect on user interaction
97427
+ new Button({label: "CONNECT", width:AUTO})
97428
+ .pos(30, 30, RIGHT, TOP)
97429
+ .tap(e => {
96942
97430
  device.connect();
97431
+ e.target.removeFrom();
97432
+ S.update();
96943
97433
  });
97434
+ END EXAMPLE
97435
+
97436
+ EXAMPLE
97437
+ // ----------------------------------------------------
97438
+ // ---- EXAMPLE 2: MICROBLOCKS (Live Block Scripting) -
97439
+ // ----------------------------------------------------
97440
+ // MicroBlocks (https://microblocks.fun) runs live on micro:bit, ESP32, RP2040, etc.
97441
+ // In MicroBlocks, use the Serial library blocks:
97442
+ // [when started]
97443
+ // [forever]
97444
+ // [serial write line (join "pot:" (analog pin 1))]
97445
+ // [wait 50 ms]
97446
+ // [when serial received]
97447
+ // [if ((last serial line) == "beep")]
97448
+ // [play tone 440 for 200 ms]
97449
+
97450
+ // ZIM Front-End Code:
97451
+ // Default preset is "microblocks" (115200 baud)
97452
+ const hw = new Hardware("microblocks", data => {
97453
+ // Key-value pairs like "pot:512" automatically parse into hw.data.pot or hw.get("pot")
97454
+ circle.sca(map(hw.get("pot"), 0, 1023, 0.5, 3));
97455
+ S.update();
97456
+ });
97457
+
97458
+ const circle = new Circle(60, orange).center();
97459
+
97460
+ circle.tap(() => {
97461
+ hw.write("beep"); // Triggers tone on the microcontroller
97462
+ });
97463
+
97464
+ new Button({label: "CONNECT", width:AUTO}).pos(30, 30, RIGHT, TOP).tap(e => {
97465
+ hw.connect();
97466
+ e.target.removeFrom();
97467
+ S.update();
96944
97468
  });
96945
97469
  END EXAMPLE
96946
97470
 
96947
97471
  EXAMPLE
96948
- // USING ZIM AS OUTPUT FROM ARDUINO
96949
- See: https://zimjs.com/hardware/
97472
+ // ----------------------------------------------------
97473
+ // ---- EXAMPLE 3: BBC MICRO:BIT (MakeCode Serial) ----
97474
+ // ----------------------------------------------------
97475
+ // In MakeCode (https://makecode.microbit.org):
97476
+ // basic.forever(function () {
97477
+ // serial.writeLine("x:" + input.acceleration(Dimension.X))
97478
+ // basic.pause(50)
97479
+ // })
97480
+ // serial.onDataReceived(serial.delimiters(Delimiters.NewLine), function () {
97481
+ // let msg = serial.readString()
97482
+ // if (msg.includes("icon")) basic.showIcon(IconNames.Heart)
97483
+ // })
97484
+
97485
+ // ZIM Front-End Code:
97486
+ const mb = new Hardware("microbit");
97487
+ const pd = new ProportionDamp(-1023, 1023, 0, W);
97488
+ mb.on("data", e => {
97489
+ if (e.key === "x") {
97490
+ character.x = pd.convert(e.val);
97491
+ S.update();
97492
+ }
97493
+ });
96950
97494
 
96951
- // would need an arduino connected
96952
- // with a potentiometer (pot) dial
96953
- // (probably on a breadboard)
96954
- // with outside pins goint to ground and 5V
96955
- // and middle pin goint to analogue A0
96956
- // with the following .ino file loaded onto it
97495
+ const character = new Rectangle(60, 60, purple).centerReg();
96957
97496
 
96958
- // ------------------------
96959
- // ---- Arduinin Code -----
97497
+ character.tap(() => {
97498
+ mb.write("icon");
97499
+ });
96960
97500
 
96961
- const int potPin = A0;
96962
- void setup() {
96963
- Serial.begin(9600);
96964
- }
96965
- void loop() {
96966
- int potValue = analogRead(potPin);
96967
- Serial.println(potValue);
96968
- delay(20);
96969
- }
97501
+ new Button({label: "CONNECT MICRO:BIT", width:AUTO}).center().tap(e => {
97502
+ mb.connect();
97503
+ e.target.removeFrom();
97504
+ S.update();
97505
+ });
97506
+ END EXAMPLE
97507
+
97508
+ EXAMPLE
97509
+ // ----------------------------------------------------
97510
+ // ---- EXAMPLE 4: CIRCUITPYTHON / MICROPYTHON (PICO) -
97511
+ // ----------------------------------------------------
97512
+ // On Raspberry Pi Pico / Adafruit boards running CircuitPython or MicroPython (code.py):
97513
+
97514
+ import time
97515
+ import json
97516
+ import board
97517
+ import analogio
97518
+
97519
+ pot = analogio.AnalogIn(board.A0)
97520
+ while True:
97521
+ # Send JSON packet
97522
+ data = {"pot": pot.value, "time": time.monotonic()}
97523
+ print(json.dumps(data))
97524
+ time.sleep(0.05)
96970
97525
 
96971
- // -------------------
96972
- // ---- ZIM Code -----
96973
97526
 
96974
- const device = new Hardware(data => {
96975
- zog(data); // gives values from 0-1023
97527
+ // ZIM Front-End Code:
97528
+ const pico = new Hardware("pico", data => {
97529
+ // JSON is automatically parsed into a native object:
97530
+ slider.currentValue = data.pot;
97531
+ S.update();
96976
97532
  });
96977
97533
 
96978
- // must interact to connect
96979
- const pane = new Pane("CONNECT", yellow).show(()=>{
96980
- device.connect();
97534
+ const slider = new Slider({min: 0, max: 65535}).center();
97535
+
97536
+ new Button({label: "CONNECT PICO", width:AUTO}).center().tap(e => {
97537
+ pico.connect();
97538
+ e.target.removeFrom();
97539
+ S.update();
96981
97540
  });
97541
+ END EXAMPLE
96982
97542
 
96983
- device.on("disconnected", () => {
96984
- pane.show(()=>{ // remember to add the callback again
96985
- device.connect();
96986
- });
97543
+ EXAMPLE
97544
+ // ----------------------------------------------------
97545
+ // ---- EXAMPLE 5: WIRELESS BLUETOOTH (BLE UART) ------
97546
+ // ----------------------------------------------------
97547
+ // Connects wirelessly to BBC micro:bit, ESP32, or Adafruit Bluefruit boards advertising Nordic UART Service
97548
+ const wireless = new Hardware("ble", data => {
97549
+ label.text = "Received: " + data;
97550
+ S.update();
96987
97551
  });
96988
- END EXAMPLE
96989
97552
 
96990
- PARAMETERS
96991
- callback - the function to call when data is received and will receive the data in its single parameter
96992
- baudRate - (default 9600) Port speed matching the microcontroller
96993
- delimiter - (default \n) Character used to split data lines
97553
+ const label = new Label("Waiting for BLE...").center();
97554
+
97555
+ new Button({label: "CONNECT BLUETOOTH", width:AUTO}).pos(0, 100, CENTER, BOTTOM).tap(e => {
97556
+ wireless.connect();
97557
+ e.target.removeFrom();
97558
+ S.update();
97559
+ });
97560
+ END EXAMPLE
97561
+
97562
+ PARAMETERS
97563
+ ** supports DUO - parameters or single object with properties below
97564
+ preset - (default "arduino") target hardware profile ("arduino", "microblocks", "microbit", "pico", "circuitpython", "micropython", "lego", "spike", "ble", "bluetooth")
97565
+ Note: if a function is passed as the first parameter, it is treated as the callback. If a number is passed, it is treated as baudRate.
97566
+ callback - (default null) function called when data arrives: callback(data, raw, event)
97567
+ baudRate - (default 9600 for "arduino", 115200 for modern platforms) serial transmission speed
97568
+ delimiter - (default "\n") character or regex string used to split incoming messages and appended to outgoing writes
97569
+ mode - (default "serial") transport layer: "serial" (Web Serial via USB) or "bluetooth" (Web Bluetooth BLE UART)
97570
+ autoParse - (default true) automatically parses incoming lines into Numbers, Key-Value objects, or JSON objects
96994
97571
 
96995
97572
  METHODS
96996
- connect() - must interact before connecting - will receive a connected event
96997
- write(data) - write to the device - also see the data event for reading from the device
96998
- disconnect() - disconnect from the device
97573
+ connect() - triggers browser connection dialog to choose device.
97574
+ Must be called inside user interaction
97575
+ For example, use a ZIM Pane().show(()={// put connect here})
97576
+ Dispatches "connected" event. Returns this for chaining.
97577
+ write(data) - sends string or number to hardware. Automatically appends delimiter if missing. Returns this for chaining.
97578
+ disconnect() - cleanly shuts down streams and closes connection. Dispatches "disconnected" event. Returns this for chaining.
97579
+ get(name) - returns latest value received for a given key name (e.g. hw.get("pot") or hw.get("x")).
96999
97580
 
97000
97581
  PROPERTIES
97001
- type - holds the class name as a String
97002
- port - readonly the port the hardware is connected on
97003
- reader - readonly the textDecoder.readable.getReader()
97004
- writer - readonly the textEncoder.writable.getWriter()
97005
- textDecoder - readonly the JavaScript TextEncoderStream()
97006
- textEncoder - readonly the JavaScript TextEncoderStream()
97007
- isListening - whether actively reading on port
97008
- baudRate - readonly baud rate - see parameter
97009
- delimiter - end character for outgoing data
97582
+ type - holds the class name as a String ("Hardware")
97583
+ preset - the hardware profile name in use
97584
+ callback - the function assigned to receive incoming data
97585
+ baudRate - the baud rate in use
97586
+ delimiter - the delimiter string
97587
+ mode - "serial" or "bluetooth"
97588
+ autoParse - boolean indicating if auto-parsing is active
97589
+ connected - boolean indicating if currently connected to hardware
97590
+ isListening - boolean indicating if reading loop is active
97591
+ data - object holding latest key-value state received
97592
+ raw - String of the most recent raw unparsed line
97593
+ value - most recent parsed value (Number, Object, or String)
97594
+ port - readonly Web Serial port instance (serial mode)
97595
+ reader - readonly stream reader instance (serial mode)
97596
+ writer - readonly stream writer instance (serial mode)
97597
+ bleDevice - readonly Web Bluetooth device instance (bluetooth mode)
97010
97598
 
97011
97599
  EVENTS
97012
- dispatches "connected" when connected
97013
- dispatches "disconnected" when disconnected
97600
+ dispatches "connected" when hardware connection opens successfully
97601
+ dispatches "disconnected" when hardware is manually disconnected or cable is pulled
97602
+ dispatches "data" when a line/packet of data is received. Event object contains:
97603
+ e.data (or e.value) - parsed data (Number, Object, String)
97604
+ e.raw - original raw line String
97605
+ e.key - key String if received as key:value or key=value
97606
+ e.val - value if received as key:value or key=value
97607
+ dispatches "error" if connection or transmission fails
97014
97608
  --*///+83.097
97015
- zim.Hardware = function(callback, baudRate, delimiter) {
97016
- z_d("83.097");
97017
- this.cjsEventDispatcher_constructor();
97018
- this.type = "Hardware";
97019
- var that = this;
97609
+ zim.Hardware = function(preset, callback, baudRate, delimiter, mode, autoParse) {
97610
+ var sig = "preset, callback, baudRate, delimiter, mode, autoParse";
97611
+ var duo; if (duo = zot(arguments, sig, this)) return duo;
97020
97612
 
97021
- // MONITOR
97022
- this.mID = "z~"+this.type;
97613
+ this.cjsEventDispatcher_constructor();
97614
+ this.type = "Hardware";
97615
+ var that = this;
97023
97616
 
97024
- // Core Properties
97025
- that.baudRate = baudRate || 9600;
97026
- that.delimiter = delimiter || "\n";
97027
-
97028
- that.port = null
97029
- that.reader = null
97030
- that.writer = null
97031
- that.textDecoder = null
97032
- that.isListening = false
97033
-
97034
- // GLOBAL HARDWARE DISCONNECT MONITOR
97035
- if (navigator.serial) {
97036
- navigator.serial.addEventListener("disconnect", function (event) {
97037
- if (event.target === that.port) {
97038
- zogy("ZIM Harware - hardware physically disconnected!")
97039
- that.isListening = false;
97617
+ // MONITOR
97618
+ this.mID = "z~" + this.type;
97619
+
97620
+ // Backward Compatibility & Flexible Parameter Detection
97621
+ if (typeof preset === "function") {
97622
+ callback = preset;
97623
+ preset = "arduino";
97624
+ } else if (typeof preset === "number") {
97625
+ baudRate = preset;
97626
+ preset = "custom";
97627
+ }
97628
+ if (typeof callback === "number") {
97629
+ baudRate = callback;
97630
+ callback = null;
97631
+ }
97040
97632
 
97041
- // Clean break: release locks and clear active streams
97042
- if (that.reader) {
97043
- try { that.reader.releaseLock() } catch (e) { }
97044
- that.reader = null
97045
- }
97046
- if (that.writer) {
97047
- try { that.writer.releaseLock() } catch (e) { }
97048
- that.writer = null
97049
- }
97050
- that.port = null;
97051
- that.dispatchEvent("disconnected");
97052
- }
97633
+ // Platform Presets (Default baud rates & transport modes)
97634
+ var presets = {
97635
+ "microblocks": { baudRate: 115200, delimiter: "\n", mode: "serial" },
97636
+ "microbit": { baudRate: 115200, delimiter: "\n", mode: "serial" },
97637
+ "pico": { baudRate: 115200, delimiter: "\n", mode: "serial" },
97638
+ "circuitpython": { baudRate: 115200, delimiter: "\n", mode: "serial" },
97639
+ "micropython": { baudRate: 115200, delimiter: "\n", mode: "serial" },
97640
+ "lego": { baudRate: 115200, delimiter: "\n", mode: "serial" },
97641
+ "spike": { baudRate: 115200, delimiter: "\n", mode: "serial" },
97642
+ "arduino": { baudRate: 9600, delimiter: "\n", mode: "serial" },
97643
+ "ble": { baudRate: null, delimiter: "\n", mode: "bluetooth" },
97644
+ "bluetooth": { baudRate: null, delimiter: "\n", mode: "bluetooth" }
97645
+ };
97646
+
97647
+ var pKey = preset ? String(preset).toLowerCase() : "arduino";
97648
+ var profile = presets[pKey] || presets["arduino"];
97649
+
97650
+ // Core Properties
97651
+ that.preset = preset || "arduino";
97652
+ that.callback = callback;
97653
+ that.baudRate = baudRate || profile.baudRate || 115200;
97654
+ that.delimiter = delimiter != null ? delimiter : profile.delimiter;
97655
+ that.mode = (mode || profile.mode || "serial").toLowerCase();
97656
+ that.autoParse = autoParse != null ? autoParse : true;
97657
+
97658
+ // Connection State
97659
+ that.port = null;
97660
+ that.reader = null;
97661
+ that.writer = null;
97662
+ that.textDecoder = null;
97663
+ that.isListening = false;
97664
+ that.connected = false;
97665
+
97666
+ // Bluetooth Properties
97667
+ that.bleDevice = null;
97668
+ that.bleServer = null;
97669
+ that.bleRxCharacteristic = null; // write
97670
+ that.bleTxCharacteristic = null; // notify/read
97671
+
97672
+ // Data Cache
97673
+ that.data = {};
97674
+ that.raw = "";
97675
+ that.value = null;
97676
+ that._buffer = "";
97677
+
97678
+ // Universal Nordic UART Service UUIDs (micro:bit, Adafruit Bluefruit, ESP32, Arduino BLE)
97679
+ var UART_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
97680
+ var UART_RX_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
97681
+ var UART_TX_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
97682
+
97683
+ // GLOBAL HARDWARE DISCONNECT MONITOR (Web Serial)
97684
+ if (navigator.serial) {
97685
+ navigator.serial.addEventListener("disconnect", function(event) {
97686
+ if (event.target === that.port) {
97687
+ zogy("ZIM Hardware: serial device physically disconnected");
97688
+ that._handleDisconnect();
97689
+ }
97690
+ });
97691
+ }
97692
+
97693
+ // CONNECT METHOD (Web Serial or Web Bluetooth)
97694
+ that.connect = function() {
97695
+ if (that.mode === "bluetooth" || that.mode === "ble") {
97696
+ that._connectBluetooth();
97697
+ } else {
97698
+ that._connectSerial();
97699
+ }
97700
+ return that;
97701
+ };
97702
+
97703
+ // SERIAL CONNECTION
97704
+ that._connectSerial = function() {
97705
+ if (!navigator.serial) {
97706
+ console.error("ZIM Hardware: Web Serial API is not supported in this browser. Please use Chrome, Edge, or Opera.");
97707
+ that.dispatchEvent({ type: "error", error: "Web Serial not supported" });
97708
+ return;
97709
+ }
97710
+
97711
+ navigator.serial.requestPort()
97712
+ .then(function(selectedPort) {
97713
+ that.port = selectedPort;
97714
+ return that.port.open({ baudRate: that.baudRate });
97715
+ })
97716
+ .then(function() {
97717
+ zogy("ZIM Hardware: serial connected (" + that.preset + " @ " + that.baudRate + " baud)");
97718
+ that.isListening = true;
97719
+ that.connected = true;
97720
+ that._buffer = "";
97721
+
97722
+ // Output Stream (Browser to Microcontroller)
97723
+ var textEncoder = new TextEncoderStream();
97724
+ textEncoder.readable.pipeTo(that.port.writable);
97725
+ that.writer = textEncoder.writable.getWriter();
97726
+
97727
+ // Input Stream (Microcontroller to Browser)
97728
+ that.textDecoder = new TextDecoderStream();
97729
+ that.port.readable.pipeTo(that.textDecoder.writable);
97730
+ that.reader = that.textDecoder.readable.getReader();
97731
+
97732
+ // Start background reader
97733
+ that._readLoop();
97734
+ that.dispatchEvent("connected");
97735
+ })
97736
+ .catch(function(error) {
97737
+ console.error("ZIM Hardware: serial connection failed:", error);
97738
+ that.dispatchEvent({ type: "error", error: error });
97053
97739
  });
97740
+ };
97741
+
97742
+ // BLUETOOTH (BLE UART) CONNECTION
97743
+ that._connectBluetooth = function() {
97744
+ if (!navigator.bluetooth) {
97745
+ console.error("ZIM Hardware: Web Bluetooth API is not supported in this browser. Please use Chrome or Edge.");
97746
+ that.dispatchEvent({ type: "error", error: "Web Bluetooth not supported" });
97747
+ return;
97054
97748
  }
97055
97749
 
97056
- // CONNECT METHOD
97057
- that.connect = function () {
97058
- if (!navigator.serial) {
97059
- console.error("Web Serial API is not supported in this browser. Please use Chrome or Edge.")
97060
- return;
97061
- }
97750
+ navigator.bluetooth.requestDevice({
97751
+ filters: [
97752
+ { services: [UART_SERVICE_UUID] },
97753
+ { namePrefix: "BBC micro:bit" },
97754
+ { namePrefix: "micro:bit" },
97755
+ { namePrefix: "MicroBlocks" },
97756
+ { namePrefix: "CircuitPlayground" }
97757
+ ],
97758
+ optionalServices: [UART_SERVICE_UUID, 0xffe0]
97759
+ })
97760
+ .then(function(device) {
97761
+ that.bleDevice = device;
97762
+ that.bleDevice.addEventListener("gattserverdisconnected", function() {
97763
+ zogy("ZIM Hardware: Bluetooth device disconnected");
97764
+ that._handleDisconnect();
97765
+ });
97766
+ return device.gatt.connect();
97767
+ })
97768
+ .then(function(server) {
97769
+ that.bleServer = server;
97770
+ return server.getPrimaryService(UART_SERVICE_UUID);
97771
+ })
97772
+ .then(function(service) {
97773
+ return Promise.all([
97774
+ service.getCharacteristic(UART_RX_UUID),
97775
+ service.getCharacteristic(UART_TX_UUID)
97776
+ ]);
97777
+ })
97778
+ .then(function(characteristics) {
97779
+ that.bleRxCharacteristic = characteristics[0];
97780
+ that.bleTxCharacteristic = characteristics[1];
97781
+ return that.bleTxCharacteristic.startNotifications();
97782
+ })
97783
+ .then(function() {
97784
+ that.bleTxCharacteristic.addEventListener("characteristicvaluechanged", function(event) {
97785
+ var decoder = new TextDecoder();
97786
+ var chunk = decoder.decode(event.target.value);
97787
+ that._processChunk(chunk);
97788
+ });
97062
97789
 
97063
- navigator.serial.requestPort()
97064
- .then(function (selectedPort) {
97065
- that.port = selectedPort;
97066
- return that.port.open({ baudRate: that.baudRate });s
97067
- })
97068
- .then(function () {
97069
- zogy("ZIM Hardware - connected successfully!")
97070
- that.isListening = true
97071
-
97072
- // Set up Output Stream (Browser to Microcontroller)
97073
- var textEncoder = new TextEncoderStream()
97074
- textEncoder.readable.pipeTo(that.port.writable)
97075
- that.writer = textEncoder.writable.getWriter()
97076
-
97077
- // Set up Input Stream (Microcontroller to Browser)
97078
- that.textDecoder = new TextDecoderStream()
97079
- that.port.readable.pipeTo(that.textDecoder.writable)
97080
- that.reader = that.textDecoder.readable.getReader()
97081
-
97082
- // Start recursive background reader
97083
- that._readLoop("");
97084
- that.dispatchEvent("connected");
97085
- })
97086
- .catch(function (error) {
97087
- console.error("Hardware connection failed:", error)
97088
- })
97790
+ zogy("ZIM Hardware: Bluetooth connected successfully");
97791
+ that.isListening = true;
97792
+ that.connected = true;
97793
+ that._buffer = "";
97794
+ that.dispatchEvent("connected");
97795
+ })
97796
+ .catch(function(error) {
97797
+ console.error("ZIM Hardware: Bluetooth connection failed:", error);
97798
+ that.dispatchEvent({ type: "error", error: error });
97799
+ });
97800
+ };
97801
+
97802
+ // TRANSMIT DATA METHOD (Hardware.write)
97803
+ that.write = function(data) {
97804
+ var message = String(data);
97805
+ if (typeof that.delimiter === "string" && !message.endsWith(that.delimiter)) {
97806
+ message += that.delimiter;
97089
97807
  }
97090
97808
 
97091
- that.write = function (data) {
97092
- if (that.writer && that.isListening) {
97093
- var message = String(data);
97094
- if (message.charAt(str.length - 1) != that.delimeter) {
97095
- message += that.delimiter;
97096
- }
97097
- that.writer.write(message)
97098
- .catch(function (error) {
97099
- console.error("Failed to transmit data to hardware:", error);
97100
- })
97101
- } else {
97102
- console.warn("Cannot write data: Hardware is not connected.");
97103
- }
97809
+ if (that.mode === "bluetooth" && that.bleRxCharacteristic) {
97810
+ var encoder = new TextEncoder();
97811
+ that.bleRxCharacteristic.writeValue(encoder.encode(message))
97812
+ .catch(function(error) {
97813
+ console.error("ZIM Hardware: failed to transmit BLE data:", error);
97814
+ });
97815
+ } else if (that.writer && that.isListening) {
97816
+ that.writer.write(message)
97817
+ .catch(function(error) {
97818
+ console.error("ZIM Hardware: failed to transmit serial data:", error);
97819
+ });
97820
+ } else {
97821
+ console.warn("ZIM Hardware: cannot write data, device is not connected.");
97104
97822
  }
97823
+ return that;
97824
+ };
97105
97825
 
97106
- // DISCONNECT METHOD (Manual Shutdown)
97107
- that.disconnect = function () {
97108
- that.isListening = false
97109
- if (that.reader && that.writer) {
97110
- that.reader.cancel()
97111
- .then(function () { return that.writer.close() })
97112
- .then(function () { if (that.port) return that.port.close() })
97113
- .then(function () { zogy("ZIM Hardware: cleanly disconnected.") })
97114
- .catch(function (err) { console.error("Error during disconnect process:", err) })
97826
+ // DISCONNECT METHOD (Manual Shutdown)
97827
+ that.disconnect = function() {
97828
+ that.isListening = false;
97829
+ that.connected = false;
97830
+
97831
+ if (that.mode === "bluetooth" && that.bleDevice) {
97832
+ if (that.bleDevice.gatt && that.bleDevice.gatt.connected) {
97833
+ that.bleDevice.gatt.disconnect();
97115
97834
  }
97835
+ that._handleDisconnect();
97836
+ return that;
97116
97837
  }
97117
97838
 
97118
- // INTERNAL RECURSIVE LOOP METHOD
97119
- that._readLoop = function (buffer) {
97120
- if (!that.isListening) return;
97839
+ if (that.reader && that.writer) {
97840
+ that.reader.cancel()
97841
+ .then(function() { return that.writer.close(); })
97842
+ .then(function() { if (that.port) return that.port.close(); })
97843
+ .then(function() {
97844
+ that._handleDisconnect();
97845
+ zogy("ZIM Hardware: cleanly disconnected");
97846
+ })
97847
+ .catch(function(err) {
97848
+ console.error("ZIM Hardware: error during disconnect:", err);
97849
+ that._handleDisconnect();
97850
+ });
97851
+ }
97852
+ return that;
97853
+ };
97121
97854
 
97122
- that.reader.read()
97123
- .then(function (result) {
97124
- if (result.done) {
97125
- that.isListening = false;
97126
- return;
97855
+ // DISCONNECT CLEANUP HELPER
97856
+ that._handleDisconnect = function() {
97857
+ that.isListening = false;
97858
+ that.connected = false;
97859
+
97860
+ if (that.reader) {
97861
+ try { that.reader.releaseLock(); } catch (e) {}
97862
+ that.reader = null;
97863
+ }
97864
+ if (that.writer) {
97865
+ try { that.writer.releaseLock(); } catch (e) {}
97866
+ that.writer = null;
97867
+ }
97868
+ that.port = null;
97869
+ that.bleDevice = null;
97870
+ that.bleServer = null;
97871
+ that.bleRxCharacteristic = null;
97872
+ that.bleTxCharacteristic = null;
97873
+ that._buffer = "";
97874
+
97875
+ that.dispatchEvent("disconnected");
97876
+ };
97877
+
97878
+ // SERIAL RECURSIVE STREAM READER
97879
+ that._readLoop = function() {
97880
+ if (!that.isListening || !that.reader) return;
97881
+
97882
+ that.reader.read()
97883
+ .then(function(result) {
97884
+ if (result.done) {
97885
+ that.isListening = false;
97886
+ return;
97887
+ }
97888
+ that._processChunk(result.value);
97889
+ that._readLoop();
97890
+ })
97891
+ .catch(function(error) {
97892
+ that.isListening = false;
97893
+ });
97894
+ };
97895
+
97896
+ // PARSING & DISPATCH PIPELINE
97897
+ that._processChunk = function(chunk) {
97898
+ that._buffer += chunk;
97899
+ var delimiterPattern = that.delimiter === "\n" ? /\r?\n/ : (that.delimiter instanceof RegExp ? that.delimiter : new RegExp(that.delimiter));
97900
+ var lines = that._buffer.split(delimiterPattern);
97901
+ that._buffer = lines.pop(); // Retain incomplete tail segment
97902
+
97903
+ for (var i = 0; i < lines.length; i++) {
97904
+ var cleanLine = lines[i].trim();
97905
+ if (cleanLine.length === 0) continue;
97906
+
97907
+ that.raw = cleanLine;
97908
+ var parsed = cleanLine;
97909
+ var key = null;
97910
+ var val = null;
97911
+
97912
+ if (that.autoParse) {
97913
+ // 1. JSON Payload Parsing
97914
+ if ((cleanLine.charAt(0) === "{" && cleanLine.charAt(cleanLine.length - 1) === "}") ||
97915
+ (cleanLine.charAt(0) === "[" && cleanLine.charAt(cleanLine.length - 1) === "]")) {
97916
+ try {
97917
+ parsed = JSON.parse(cleanLine);
97918
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
97919
+ for (var k in parsed) {
97920
+ that.data[k] = parsed[k];
97921
+ }
97922
+ }
97923
+ } catch (e) {
97924
+ parsed = cleanLine;
97127
97925
  }
97128
- buffer += result.value;
97129
- var delimiterPattern = that.delimiter === "\n" ? /\r?\n/ : new RegExp(that.delimiter)
97130
- var lines = buffer.split(delimiterPattern)
97131
- buffer = lines.pop()
97132
- for (var i = 0; i < lines.length; i++) {
97133
- var cleanLine = lines[i].trim()
97134
- if (cleanLine.length > 0 && typeof callback === "function") (callback)(cleanLine);
97926
+ }
97927
+ // 2. Pure Numeric String Parsing
97928
+ else if (!isNaN(cleanLine)) {
97929
+ parsed = Number(cleanLine);
97930
+ }
97931
+ // 3. Key-Value Formatting (e.g. "pot:512", "x=10", "btn: 1")
97932
+ else {
97933
+ var match = cleanLine.match(/^([a-zA-Z0-9_.-]+)\s*[:=]\s*(.*)$/);
97934
+ if (match) {
97935
+ key = match[1];
97936
+ var rawVal = match[2].trim();
97937
+ val = (!isNaN(rawVal) && rawVal !== "") ? Number(rawVal) : rawVal;
97938
+ that.data[key] = val;
97939
+ parsed = val;
97135
97940
  }
97136
- that._readLoop(buffer);
97137
- })
97138
- .catch(function (error) {
97139
- // If it crashes due to a sudden cable pull, let the event listener handle the cleanup
97140
- that.isListening = false
97141
- });
97941
+ }
97942
+ }
97943
+
97944
+ that.value = parsed;
97945
+
97946
+ var eventObj = {
97947
+ type: "data",
97948
+ data: parsed,
97949
+ value: parsed,
97950
+ raw: cleanLine,
97951
+ key: key,
97952
+ val: val,
97953
+ target: that
97954
+ };
97955
+
97956
+ that.dispatchEvent(eventObj);
97957
+ if (typeof that.callback === "function") (that.callback)(parsed, cleanLine, eventObj);
97142
97958
  }
97143
- }
97144
- zim.extend(zim.Hardware, createjs.EventDispatcher, null, "cjsEventDispatcher", false);
97959
+ };
97960
+
97961
+ // VALUE QUERY HELPER (e.g. hardware.get("pot"))
97962
+ that.get = function(name) {
97963
+ return that.data[name];
97964
+ };
97965
+ };
97966
+
97967
+ zim.extend(zim.Hardware, createjs.EventDispatcher, null, "cjsEventDispatcher", false);
97145
97968
  //-83.097
97146
97969
 
97147
97970
 
@@ -97287,11 +98110,15 @@ DESCRIPTION
97287
98110
  The Frame has a "deviceorientation" event to capture tilt or device rotation (like a compass)
97288
98111
  and a "devicemotion" event to capture shaking the device with the accelerometer.
97289
98112
 
98113
+ SEE: the tilt() method (just after gesture()) for a one line version
98114
+
97290
98115
  SEE: Shake - for the "devicemotion" event for accelerometer motion
97291
98116
 
97292
98117
  SEE: the PermissionAsk() class which will handle asking for permissions on devices.
97293
98118
 
97294
98119
  EXAMPLE
98120
+ // SEE ALSO THE TILT METHOD FOR A ONE LINE VERSION
98121
+
97295
98122
  // TILT OR COMPASS - https://zimjs.com/zapp/Z_Q5UYS
97296
98123
  // for capturing tilt on device (rotation about an axis)
97297
98124
  // be on a mobile device or a device with sensors
@@ -99493,6 +100320,19 @@ function zimify(obj, a, b, c, d, list) {
99493
100320
  gestureRect:function(boundary, update) {
99494
100321
  return zim.gestureBoundary(this, boundary, update);
99495
100322
  },
100323
+
100324
+ tilt:function(boundary, damp, factor, mode, type, call) {
100325
+ if (isDUO(arguments)) {arguments[0].obj = this; return zim.tilt(arguments[0]);}
100326
+ else {return zim.tilt(this, boundary, damp, factor, mode, type, call);}
100327
+ },
100328
+ noTilt:function() {
100329
+ if (isDUO(arguments)) {arguments[0].obj = this; return zim.noTilt(arguments[0]);}
100330
+ else {return zim.noTilt(this);}
100331
+ },
100332
+ tiltBoundary:function(boundary, update) {
100333
+ return zim.tiltBoundary(this, boundary, update);
100334
+ },
100335
+
99496
100336
  addPhysics:function(dynamic, contract, shape, friction, linear, angular, density, restitution, maskBits, categoryBits, physics) {
99497
100337
  if (isDUO(arguments)) {arguments[0].obj = this; return zim.addPhysics(arguments[0]);}
99498
100338
  else {return zim.addPhysics(this, dynamic, contract, shape, friction, linear, angular, density, restitution, maskBits, categoryBits, physics);}
@@ -106252,7 +107092,7 @@ for (z_i = 0; z_i < globalFunctions.length; z_i++) {
106252
107092
  ["FILL", zim.FILL],
106253
107093
  ["FULL", zim.FULL],
106254
107094
  ["LEFT", zim.LEFT],
106255
- ["RIGHT", zim.RIGHT],s
107095
+ ["RIGHT", zim.RIGHT],
106256
107096
  ["CENTER", zim.CENTER],
106257
107097
  ["MIDDLE", zim.MIDDLE],
106258
107098
  ["JUSTIFY", zim.JUSTIFY],
@@ -106527,6 +107367,9 @@ export let transform = zim.transform;
106527
107367
  export let gesture = zim.gesture;
106528
107368
  export let noGesture = zim.noGesture;
106529
107369
  export let gestureBoundary = zim.gestureBoundary;
107370
+ export let tilt = zim.tilt;
107371
+ export let noTilt = zim.noTilt;
107372
+ export let tiltBoundary = zim.tiltBoundary;
106530
107373
  export let effect = zim.effect;
106531
107374
  export let updateEffects = zim.updateEffects;
106532
107375
  export let noEffect = zim.noEffect;