q5 2.10.2 → 2.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q5",
3
- "version": "2.10.2",
3
+ "version": "2.10.5",
4
4
  "description": "A sequel to p5.js that's optimized for interactive art",
5
5
  "author": "quinton-ashley",
6
6
  "contributors": [
@@ -11,7 +11,7 @@
11
11
  "homepage": "https://q5js.org/home",
12
12
  "main": "q5-server.js",
13
13
  "scripts": {
14
- "bundle": "cat src/q5-core.js src/q5-canvas.js src/q5-2d-canvas.js src/q5-2d-drawing.js src/q5-2d-image.js src/q5-2d-text.js src/q5-ai.js src/q5-color.js src/q5-display.js src/q5-input.js src/q5-math.js src/q5-sound.js src/q5-util.js src/q5-vector.js src/q5-webgpu-canvas.js src/q5-webgpu-drawing.js src/q5-webgpu-image.js src/q5-webgpu-text.js > q5.js",
14
+ "bundle": "cat src/q5-core.js src/q5-canvas.js src/q5-2d-canvas.js src/q5-2d-drawing.js src/q5-2d-image.js src/q5-2d-soft-filters.js src/q5-2d-text.js src/q5-ai.js src/q5-color.js src/q5-display.js src/q5-input.js src/q5-math.js src/q5-sound.js src/q5-util.js src/q5-vector.js src/q5-webgpu-canvas.js src/q5-webgpu-drawing.js src/q5-webgpu-image.js src/q5-webgpu-text.js > q5.js",
15
15
  "min": "terser q5.js --compress ecma=2024 --mangle > q5.min.js",
16
16
  "dist": "bun bundle && bun min",
17
17
  "dist-p5play": "bun dist && cp q5.js ../../web/p5play-web/v3/q5.js && cp q5.min.js ../../web/p5play-web/v3/q5.min.js",
package/q5.d.ts CHANGED
@@ -1254,8 +1254,21 @@ function setup() {
1254
1254
 
1255
1255
  /** 🌆
1256
1256
  * Applies a filter to the image.
1257
- * @param {string} type - type of filter
1257
+ *
1258
+ * See the documentation for q5's filter constants below for more info.
1259
+ *
1260
+ * If a CSS filter string is provided, it will be applied to the image.
1261
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/filter
1262
+ * @param {string} type - type of filter or a CSS filter string
1258
1263
  * @param {number} [value] - optional parameter, depending on filter type
1264
+ * @example
1265
+ createCanvas(200, 200);
1266
+ let logo = loadImage('/q5js_logo.webp');
1267
+
1268
+ function setup() {
1269
+ logo.filter(INVERT);
1270
+ image(logo, 0, 0, 200, 200);
1271
+ }
1259
1272
  */
1260
1273
  function filter(type: string, value?: number): void;
1261
1274
 
@@ -1480,25 +1493,132 @@ function setup() {
1480
1493
 
1481
1494
  // 🎨 color
1482
1495
 
1496
+ /** 🎨
1497
+ * Creates a new `Color` object. This function can parse different
1498
+ * color representations depending on the current color mode.
1499
+ *
1500
+ * RGB colors have components `r`/`red`, `g`/`green`, `b`/`blue`,
1501
+ * and `a`/`alpha`.
1502
+ *
1503
+ * In rgb mode when only one numeric input is provided, it'll
1504
+ * be interpreted as a grayscale value. If only two numeric inputs
1505
+ * are provided, they will be used as grayscale and alpha values.
1506
+ *
1507
+ * `fill`, `stroke`, and `background` functions can accept the same
1508
+ * wide range of inputs as this function.
1509
+ * @param {string | number | Color | number[]} c0 - first color component, a CSS color string, a `Color` object (to make copy), or an array of components
1510
+ * @param {number} [c1] - second color component
1511
+ * @param {number} [c2] - third color component
1512
+ * @param {number} [c3] - fourth color component (alpha)
1513
+ * @returns {Color} a new `Color` object
1514
+ * @example
1515
+ createCanvas(200, 200);
1516
+
1517
+ let c = color(128);
1518
+
1519
+ function draw() {
1520
+ background(c);
1521
+ c.g = (c.g + 1) % 255;
1522
+ }
1523
+ */
1524
+ function color(c0: string | number | Color | number[], c1?: number, c2?: number, c3?: number): Color;
1525
+
1483
1526
  /** 🎨
1484
1527
  * Sets the color mode for the sketch. Changes the type of color object created by color functions.
1528
+ *
1529
+ * In q2d, the default color mode is RGB in legacy integer format.
1485
1530
  *
1486
- * In WebGPU, the default color mode is 'rgb' in float format.
1531
+ * In WebGPU, the default color mode is RGB in float format.
1532
+ *
1533
+ * See the documentation for q5's color constants below for more info.
1487
1534
  * @param {'rgb' | 'srgb' | 'oklch'} mode - color mode
1488
1535
  * @param {1 | 255} format - color format (1 for float, 255 for integer)
1536
+ * @example
1537
+ createCanvas(200, 200);
1538
+
1539
+ colorMode(RGB, 1);
1540
+ fill(1, 0, 0);
1541
+ rect(0, 0, 66, 200);
1542
+ fill(0, 1, 0);
1543
+ rect(66, 0, 67, 200);
1544
+ fill(0, 0, 1);
1545
+ rect(133, 0, 67, 200);
1546
+ * @example
1547
+ createCanvas(200, 200);
1548
+
1549
+ colorMode(OKLCH);
1550
+
1551
+ fill(0.25, 0.15, 0);
1552
+ rect(0, 0, 100, 200);
1553
+ fill(0.75, 0.15, 0)
1554
+ rect(100, 0, 100, 200);
1489
1555
  */
1490
1556
  function colorMode(mode: 'rgb' | 'srgb' | 'oklch', format: 1 | 255): void;
1491
1557
 
1492
1558
  /** 🎨
1493
- * Creates a new `Color` object. It can parse different
1494
- * color representations depending on the current `colorMode`.
1495
- * @param {string | number | Color | number[]} c0 - first color component, or a string representing the color, or a `Color` object, or an array of components
1496
- * @param {number} [c1] - second color component
1497
- * @param {number} [c2] - third color component
1498
- * @param {number} [c3] - fourth color component (alpha)
1499
- * @returns {Color} a new `Color` object
1559
+ * RGB colors have components `r`/`red`, `g`/`green`, `b`/`blue`,
1560
+ * and `a`/`alpha`.
1561
+ *
1562
+ * RGB is the default color mode.
1563
+ *
1564
+ * By default when a canvas is using the `display-p3` color space,
1565
+ * rgb colors are mapped to the full P3 gamut, even when they use the
1566
+ * legacy integer format.
1567
+ * @example
1568
+ createCanvas(200, 200);
1569
+
1570
+ function setup() {
1571
+ background(255, 0, 0);
1572
+ }
1500
1573
  */
1501
- function color(c0: string | number | Color | number[], c1?: number, c2?: number, c3?: number): Color;
1574
+ const RGB: 'rgb';
1575
+
1576
+ /** 🎨
1577
+ * This color mode limits the gamut of rgb colors to sRGB.
1578
+ *
1579
+ * If your display is HDR capable, take a look at the following
1580
+ * example, note that full red appears less saturated, as it would
1581
+ * on an SDR display.
1582
+ * @example
1583
+ createCanvas(200, 200);
1584
+
1585
+ colorMode(SRGB, 255);
1586
+
1587
+ function setup() {
1588
+ background(255, 0, 0);
1589
+ }
1590
+ */
1591
+ const SRGB: 'srgb';
1592
+
1593
+ /** 🎨
1594
+ * OKLCH colors have components `l`/`lightness`, `c`/`chroma`,
1595
+ * `h`/`hue`, and `a`/`alpha`.
1596
+ *
1597
+ * You may be familiar with the outdated HSL/HSV color formats,
1598
+ * which were created in the 1970s to be more intuitive for humans
1599
+ * to work with than RGB. But due to technical limitations of that
1600
+ * time, they are not perceptually uniform, meaning the same
1601
+ * brightness values may appear lighter or darker depending on the hue.
1602
+ *
1603
+ * The OKLCH format is similar to HSL/HSV but it is perceptually
1604
+ * uniform and supports a wider gamut (range of colors) than srgb.
1605
+ * Every display-p3 color can be represented in OKLCH.
1606
+ *
1607
+ * `lightness`: 0 to 1
1608
+ * `chroma`: 0 to 0.4
1609
+ * `hue`: 0 to 360
1610
+ * `alpha`: 0 to 1
1611
+ *
1612
+ * Note how seamless the hue transitions are in the following example.
1613
+ * @example
1614
+ createCanvas(200, 200);
1615
+ colorMode(OKLCH);
1616
+
1617
+ function draw() {
1618
+ background(0.7, 0.16, frameCount % 360);
1619
+ }
1620
+ */
1621
+ const OKLCH: 'oklch';
1502
1622
 
1503
1623
  // 🖲️ input
1504
1624
 
package/q5.js CHANGED
@@ -143,15 +143,16 @@ function Q5(scope, parent, renderer) {
143
143
  $.getTargetFrameRate = () => $._targetFrameRate || 60;
144
144
  $.getFPS = () => $._fps;
145
145
 
146
+ // shims for compatibility with p5.js libraries
146
147
  $.Element = function (a) {
147
148
  this.elt = a;
148
149
  };
149
150
  $._elements = [];
151
+ $.describe = () => {};
150
152
 
151
153
  $.TWO_PI = $.TAU = Math.PI * 2;
152
154
 
153
155
  $.log = $.print = console.log;
154
- $.describe = () => {};
155
156
 
156
157
  for (let m in Q5.modules) {
157
158
  Q5.modules[m]($, q);
@@ -612,6 +613,7 @@ Q5.modules.canvas = ($, q) => {
612
613
  $._styleNames = [
613
614
  '_fill',
614
615
  '_stroke',
616
+ '_strokeWeight',
615
617
  '_doStroke',
616
618
  '_doFill',
617
619
  '_strokeSet',
@@ -1269,6 +1271,9 @@ Q5.renderers.q2d.image = ($, q) => {
1269
1271
 
1270
1272
  Q5.Image ??= Q5Image;
1271
1273
 
1274
+ $._tint = null;
1275
+ let imgData = null;
1276
+
1272
1277
  $.createImage = (w, h, opt) => {
1273
1278
  opt ??= {};
1274
1279
  opt.alpha ??= true;
@@ -1363,7 +1368,7 @@ Q5.renderers.q2d.image = ($, q) => {
1363
1368
  } else sh *= pd;
1364
1369
 
1365
1370
  if ($._tint) {
1366
- if (img._tint != $._tint || img._retint) {
1371
+ if (img._retint || img._tint != $._tint) {
1367
1372
  img._tintImg ??= $.createImage(img.w, img.h, { pixelDensity: pd });
1368
1373
 
1369
1374
  if (img._tintImg.width != img.width || img._tintImg.height != img.height) {
@@ -1393,30 +1398,43 @@ Q5.renderers.q2d.image = ($, q) => {
1393
1398
  $.ctx.drawImage(drawable, sx * pd, sy * pd, sw, sh, dx, dy, dw, dh);
1394
1399
  };
1395
1400
 
1396
- $._tint = null;
1397
- let imgData = null;
1398
-
1399
- $._softFilter = () => {
1400
- throw new Error('Load q5-2d-soft-filters.js to use software filters.');
1401
- };
1402
-
1403
- $.filter = (type, x) => {
1404
- if (!$.ctx.filter) return $._softFilter(type, x);
1405
-
1406
- if (typeof type == 'string') f = type;
1407
- else if (type == Q5.GRAY) f = `saturate(0%)`;
1408
- else if (type == Q5.INVERT) f = `invert(100%)`;
1409
- else if (type == Q5.BLUR) {
1410
- let r = Math.ceil(x * $._pixelDensity) || 1;
1411
- f = `blur(${r}px)`;
1412
- } else if (type == Q5.THRESHOLD) {
1413
- x ??= 0.5;
1414
- let b = Math.floor((0.5 / Math.max(x, 0.00001)) * 100);
1415
- f = `saturate(0%) brightness(${b}%) contrast(1000000%)`;
1416
- } else return $._softFilter(type, x);
1401
+ $.filter = (type, value) => {
1402
+ if (!$.ctx.filter) return $._softFilter(type, value);
1403
+ let f = '';
1404
+ if (typeof type === 'string') {
1405
+ f = type;
1406
+ } else if (type === Q5.GRAY) {
1407
+ f = `saturate(0%)`;
1408
+ } else if (type === Q5.INVERT) {
1409
+ f = `invert(100%)`;
1410
+ } else if (type === Q5.BLUR) {
1411
+ const radius = Math.ceil(value * $._pixelDensity) || 1;
1412
+ f = `blur(${radius}px)`;
1413
+ } else if (type === Q5.THRESHOLD) {
1414
+ value ??= 0.5;
1415
+ const brightness = Math.floor((0.5 / Math.max(value, 0.00001)) * 100);
1416
+ f = `saturate(0%) brightness(${brightness}%) contrast(1000000%)`;
1417
+ } else if (type === Q5.SEPIA) {
1418
+ f = `sepia(${value ?? 1})`;
1419
+ } else if (type === Q5.BRIGHTNESS) {
1420
+ f = `brightness(${value ?? 1})`;
1421
+ } else if (type === Q5.SATURATION) {
1422
+ f = `saturate(${value ?? 1})`;
1423
+ } else if (type === Q5.CONTRAST) {
1424
+ f = `contrast(${value ?? 1})`;
1425
+ } else if (type === Q5.HUE_ROTATE) {
1426
+ const unit = $._angleMode === 0 ? 'rad' : 'deg';
1427
+ f = `hue-rotate(${value}${unit})`;
1428
+ } else {
1429
+ $._softFilter(type, value);
1430
+ return;
1431
+ }
1417
1432
 
1418
1433
  $.ctx.filter = f;
1419
- $.ctx.drawImage($.canvas, 0, 0, $.canvas.w, $.canvas.h);
1434
+ if ($.ctx.filter == 'none') {
1435
+ throw new Error(`Invalid filter format: ${type}`);
1436
+ }
1437
+ $.ctx.drawImage($.canvas, 0, 0, $.canvas.width, $.canvas.height);
1420
1438
  $.ctx.filter = 'none';
1421
1439
  $._retint = true;
1422
1440
  };
@@ -1569,6 +1587,156 @@ Q5.POSTERIZE = 5;
1569
1587
  Q5.DILATE = 6;
1570
1588
  Q5.ERODE = 7;
1571
1589
  Q5.BLUR = 8;
1590
+ Q5.SEPIA = 9;
1591
+ Q5.BRIGHTNESS = 10;
1592
+ Q5.SATURATION = 11;
1593
+ Q5.CONTRAST = 12;
1594
+ Q5.HUE_ROTATE = 13;
1595
+ /* software implementation of image filters */
1596
+ Q5.renderers.q2d.soft_filters = ($) => {
1597
+ let u = null; // uint8 temporary buffer
1598
+
1599
+ function ensureBuf() {
1600
+ let l = $.canvas.width * $.canvas.height * 4;
1601
+ if (!u || u.length != l) u = new Uint8ClampedArray(l);
1602
+ }
1603
+
1604
+ function initSoftFilters() {
1605
+ $._filters = [];
1606
+ $._filters[Q5.THRESHOLD] = (d, thresh) => {
1607
+ if (thresh === undefined) thresh = 127.5;
1608
+ else thresh *= 255;
1609
+ for (let i = 0; i < d.length; i += 4) {
1610
+ const gray = 0.2126 * d[i] + 0.7152 * d[i + 1] + 0.0722 * d[i + 2];
1611
+ d[i] = d[i + 1] = d[i + 2] = gray >= thresh ? 255 : 0;
1612
+ }
1613
+ };
1614
+ $._filters[Q5.GRAY] = (d) => {
1615
+ for (let i = 0; i < d.length; i += 4) {
1616
+ const gray = 0.2126 * d[i] + 0.7152 * d[i + 1] + 0.0722 * d[i + 2];
1617
+ d[i] = d[i + 1] = d[i + 2] = gray;
1618
+ }
1619
+ };
1620
+ $._filters[Q5.OPAQUE] = (d) => {
1621
+ for (let i = 0; i < d.length; i += 4) {
1622
+ d[i + 3] = 255;
1623
+ }
1624
+ };
1625
+ $._filters[Q5.INVERT] = (d) => {
1626
+ for (let i = 0; i < d.length; i += 4) {
1627
+ d[i] = 255 - d[i];
1628
+ d[i + 1] = 255 - d[i + 1];
1629
+ d[i + 2] = 255 - d[i + 2];
1630
+ }
1631
+ };
1632
+ $._filters[Q5.POSTERIZE] = (d, lvl = 4) => {
1633
+ let lvl1 = lvl - 1;
1634
+ for (let i = 0; i < d.length; i += 4) {
1635
+ d[i] = (((d[i] * lvl) >> 8) * 255) / lvl1;
1636
+ d[i + 1] = (((d[i + 1] * lvl) >> 8) * 255) / lvl1;
1637
+ d[i + 2] = (((d[i + 2] * lvl) >> 8) * 255) / lvl1;
1638
+ }
1639
+ };
1640
+ $._filters[Q5.DILATE] = (d, func) => {
1641
+ func ??= Math.max;
1642
+ ensureBuf();
1643
+ u.set(d);
1644
+ let [w, h] = [$.canvas.width, $.canvas.height];
1645
+ for (let i = 0; i < h; i++) {
1646
+ for (let j = 0; j < w; j++) {
1647
+ let l = 4 * Math.max(j - 1, 0);
1648
+ let r = 4 * Math.min(j + 1, w - 1);
1649
+ let t = 4 * Math.max(i - 1, 0) * w;
1650
+ let b = 4 * Math.min(i + 1, h - 1) * w;
1651
+ let oi = 4 * i * w;
1652
+ let oj = 4 * j;
1653
+ for (let k = 0; k < 4; k++) {
1654
+ let kt = k + t;
1655
+ let kb = k + b;
1656
+ let ko = k + oi;
1657
+ d[oi + oj + k] = func(u[kt + oj], u[ko + l], u[ko + oj], u[ko + r], u[kb + oj]);
1658
+ }
1659
+ }
1660
+ }
1661
+ };
1662
+ $._filters[Q5.ERODE] = (d) => {
1663
+ $._filters[Q5.DILATE](d, Math.min);
1664
+ };
1665
+ $._filters[Q5.BLUR] = (d, r) => {
1666
+ r = r || 1;
1667
+ r = Math.floor(r * $._pixelDensity);
1668
+ ensureBuf();
1669
+ u.set(d);
1670
+
1671
+ let ksize = r * 2 + 1;
1672
+
1673
+ function gauss(ksize) {
1674
+ let im = new Float32Array(ksize);
1675
+ let sigma = 0.3 * r + 0.8;
1676
+ let ss2 = sigma * sigma * 2;
1677
+ for (let i = 0; i < ksize; i++) {
1678
+ let x = i - ksize / 2;
1679
+ let z = Math.exp(-(x * x) / ss2) / (2.5066282746 * sigma);
1680
+ im[i] = z;
1681
+ }
1682
+ return im;
1683
+ }
1684
+
1685
+ let kern = gauss(ksize);
1686
+ let [w, h] = [$.canvas.width, $.canvas.height];
1687
+ for (let i = 0; i < h; i++) {
1688
+ for (let j = 0; j < w; j++) {
1689
+ let s0 = 0,
1690
+ s1 = 0,
1691
+ s2 = 0,
1692
+ s3 = 0;
1693
+ for (let k = 0; k < ksize; k++) {
1694
+ let jk = Math.min(Math.max(j - r + k, 0), w - 1);
1695
+ let idx = 4 * (i * w + jk);
1696
+ s0 += u[idx] * kern[k];
1697
+ s1 += u[idx + 1] * kern[k];
1698
+ s2 += u[idx + 2] * kern[k];
1699
+ s3 += u[idx + 3] * kern[k];
1700
+ }
1701
+ let idx = 4 * (i * w + j);
1702
+ d[idx] = s0;
1703
+ d[idx + 1] = s1;
1704
+ d[idx + 2] = s2;
1705
+ d[idx + 3] = s3;
1706
+ }
1707
+ }
1708
+ u.set(d);
1709
+ for (let i = 0; i < h; i++) {
1710
+ for (let j = 0; j < w; j++) {
1711
+ let s0 = 0,
1712
+ s1 = 0,
1713
+ s2 = 0,
1714
+ s3 = 0;
1715
+ for (let k = 0; k < ksize; k++) {
1716
+ let ik = Math.min(Math.max(i - r + k, 0), h - 1);
1717
+ let idx = 4 * (ik * w + j);
1718
+ s0 += u[idx] * kern[k];
1719
+ s1 += u[idx + 1] * kern[k];
1720
+ s2 += u[idx + 2] * kern[k];
1721
+ s3 += u[idx + 3] * kern[k];
1722
+ }
1723
+ let idx = 4 * (i * w + j);
1724
+ d[idx] = s0;
1725
+ d[idx + 1] = s1;
1726
+ d[idx + 2] = s2;
1727
+ d[idx + 3] = s3;
1728
+ }
1729
+ }
1730
+ };
1731
+ }
1732
+
1733
+ $._softFilter = (typ, x) => {
1734
+ if (!$._filters) initSoftFilters();
1735
+ let imgData = $.ctx._getImageData(0, 0, $.canvas.width, $.canvas.height);
1736
+ $._filters[typ](imgData.data, x);
1737
+ $.ctx.putImageData(imgData, 0, 0);
1738
+ };
1739
+ };
1572
1740
  Q5.renderers.q2d.text = ($, q) => {
1573
1741
  $._textAlign = 'left';
1574
1742
  $._textBaseline = 'alphabetic';
@@ -1898,6 +2066,7 @@ Q5.modules.ai = ($) => {
1898
2066
  };
1899
2067
  Q5.modules.color = ($, q) => {
1900
2068
  $.RGB = $.RGBA = $._colorMode = 'rgb';
2069
+ $.SRGB = 'srgb';
1901
2070
  $.OKLCH = 'oklch';
1902
2071
 
1903
2072
  $.colorMode = (mode, format) => {
@@ -1908,7 +2077,6 @@ Q5.modules.color = ($, q) => {
1908
2077
  if (mode == 'oklch') {
1909
2078
  q.Color = Q5.ColorOKLCH;
1910
2079
  } else {
1911
- let srgb = $.canvas.colorSpace == 'srgb';
1912
2080
  if ($._colorFormat == 255) {
1913
2081
  q.Color = srgb ? Q5.ColorRGBA_8 : Q5.ColorRGBA_P3_8;
1914
2082
  } else {
@@ -1992,10 +2160,12 @@ Q5.modules.color = ($, q) => {
1992
2160
  return new C(c0, c1, c2, c3);
1993
2161
  };
1994
2162
 
2163
+ // deprecated
1995
2164
  $.red = (c) => c.r;
1996
2165
  $.green = (c) => c.g;
1997
2166
  $.blue = (c) => c.b;
1998
2167
  $.alpha = (c) => c.a;
2168
+
1999
2169
  $.lightness = (c) => {
2000
2170
  if (c.l) return c.l;
2001
2171
  return ((0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b) * 100) / 255;
@@ -2053,9 +2223,43 @@ Q5.ColorOKLCH = class extends Q5.Color {
2053
2223
  this.h = h;
2054
2224
  this.a = a ?? 1;
2055
2225
  }
2226
+ get levels() {
2227
+ return [this.l, this.c, this.h, this.a];
2228
+ }
2229
+ equals(c) {
2230
+ return c && this.l == c.l && this.c == c.c && this.h == c.h && this.a == c.a;
2231
+ }
2232
+ isSameColor(c) {
2233
+ return c && this.l == c.l && this.c == c.c && this.h == c.h;
2234
+ }
2056
2235
  toString() {
2057
2236
  return `oklch(${this.l} ${this.c} ${this.h} / ${this.a})`;
2058
2237
  }
2238
+
2239
+ get lightness() {
2240
+ return this.l;
2241
+ }
2242
+ set lightness(v) {
2243
+ this.l = v;
2244
+ }
2245
+ get chroma() {
2246
+ return this.c;
2247
+ }
2248
+ set chroma(v) {
2249
+ this.c = v;
2250
+ }
2251
+ get hue() {
2252
+ return this.h;
2253
+ }
2254
+ set hue(v) {
2255
+ this.h = v;
2256
+ }
2257
+ get alpha() {
2258
+ return this.a;
2259
+ }
2260
+ set alpha(v) {
2261
+ this.a = v;
2262
+ }
2059
2263
  };
2060
2264
 
2061
2265
  Q5.ColorRGBA = class extends Q5.Color {
@@ -2069,9 +2273,39 @@ Q5.ColorRGBA = class extends Q5.Color {
2069
2273
  get levels() {
2070
2274
  return [this.r, this.g, this.b, this.a];
2071
2275
  }
2276
+ equals(c) {
2277
+ return c && this.r == c.r && this.g == c.g && this.b == c.b && this.a == c.a;
2278
+ }
2279
+ isSameColor(c) {
2280
+ return c && this.r == c.r && this.g == c.g && this.b == c.b;
2281
+ }
2072
2282
  toString() {
2073
2283
  return `color(srgb ${this.r} ${this.g} ${this.b} / ${this.a})`;
2074
2284
  }
2285
+ get red() {
2286
+ return this.r;
2287
+ }
2288
+ set red(v) {
2289
+ this.r = v;
2290
+ }
2291
+ get green() {
2292
+ return this.g;
2293
+ }
2294
+ set green(v) {
2295
+ this.g = v;
2296
+ }
2297
+ get blue() {
2298
+ return this.b;
2299
+ }
2300
+ set blue(v) {
2301
+ this.b = v;
2302
+ }
2303
+ get alpha() {
2304
+ return this.a;
2305
+ }
2306
+ set alpha(v) {
2307
+ this.a = v;
2308
+ }
2075
2309
  };
2076
2310
 
2077
2311
  Q5.ColorRGBA_P3 = class extends Q5.ColorRGBA {
@@ -2085,6 +2319,7 @@ Q5.ColorRGBA_8 = class extends Q5.ColorRGBA {
2085
2319
  constructor(r, g, b, a) {
2086
2320
  super(r, g, b, a ?? 255);
2087
2321
  }
2322
+ // deprecated set functions for backwards compatibility
2088
2323
  setRed(v) {
2089
2324
  this.r = v;
2090
2325
  }
@@ -2097,9 +2332,6 @@ Q5.ColorRGBA_8 = class extends Q5.ColorRGBA {
2097
2332
  setAlpha(v) {
2098
2333
  this.a = v;
2099
2334
  }
2100
- get levels() {
2101
- return [this.r, this.g, this.b, this.a];
2102
- }
2103
2335
  toString() {
2104
2336
  return `rgb(${this.r} ${this.g} ${this.b} / ${this.a / 255})`;
2105
2337
  }