diff --git a/src/color/color_conversion.js b/src/color/color_conversion.js
index 53b089d139..850d24918b 100644
--- a/src/color/color_conversion.js
+++ b/src/color/color_conversion.js
@@ -14,130 +14,96 @@
*/
import p5 from '../core/main';
-p5.ColorConversion = {};
+p5.ColorConversion = {
+ /**
+ * Convert an HSBA array to HSLA.
+ */
+ _hsbaToHSLA(hsba) {
+ const hue = hsba[0];
+ let sat = hsba[1];
+ const val = hsba[2];
-/**
- * Convert an HSBA array to HSLA.
- */
-p5.ColorConversion._hsbaToHSLA = function(hsba) {
- const hue = hsba[0];
- let sat = hsba[1];
- const val = hsba[2];
-
- // Calculate lightness.
- const li = (2 - sat) * val / 2;
+ // Calculate lightness.
+ const li = (2 - sat) * val / 2;
- // Convert saturation.
- if (li !== 0) {
- if (li === 1) {
- sat = 0;
- } else if (li < 0.5) {
- sat = sat / (2 - sat);
- } else {
- sat = sat * val / (2 - li * 2);
+ // Convert saturation.
+ if (li !== 0) {
+ if (li === 1) {
+ sat = 0;
+ } else if (li < 0.5) {
+ sat = sat / (2 - sat);
+ } else {
+ sat = sat * val / (2 - li * 2);
+ }
}
- }
- // Hue and alpha stay the same.
- return [hue, sat, li, hsba[3]];
-};
+ // Hue and alpha stay the same.
+ return [hue, sat, li, hsba[3]];
+ },
-/**
- * Convert an HSBA array to RGBA.
- */
-p5.ColorConversion._hsbaToRGBA = function(hsba) {
- const hue = hsba[0] * 6; // We will split hue into 6 sectors.
- const sat = hsba[1];
- const val = hsba[2];
+ /**
+ * Convert an HSBA array to RGBA.
+ */
+ _hsbaToRGBA(hsba) {
+ const hue = hsba[0] * 6; // We will split hue into 6 sectors.
+ const sat = hsba[1];
+ const val = hsba[2];
- let RGBA = [];
+ let RGBA = [];
- if (sat === 0) {
- RGBA = [val, val, val, hsba[3]]; // Return early if grayscale.
- } else {
- const sector = Math.floor(hue);
- const tint1 = val * (1 - sat);
- const tint2 = val * (1 - sat * (hue - sector));
- const tint3 = val * (1 - sat * (1 + sector - hue));
- let red, green, blue;
- if (sector === 1) {
- // Yellow to green.
- red = tint2;
- green = val;
- blue = tint1;
- } else if (sector === 2) {
- // Green to cyan.
- red = tint1;
- green = val;
- blue = tint3;
- } else if (sector === 3) {
- // Cyan to blue.
- red = tint1;
- green = tint2;
- blue = val;
- } else if (sector === 4) {
- // Blue to magenta.
- red = tint3;
- green = tint1;
- blue = val;
- } else if (sector === 5) {
- // Magenta to red.
- red = val;
- green = tint1;
- blue = tint2;
+ if (sat === 0) {
+ RGBA = [val, val, val, hsba[3]]; // Return early if grayscale.
} else {
- // Red to yellow (sector could be 0 or 6).
- red = val;
- green = tint3;
- blue = tint1;
+ const sector = Math.floor(hue);
+ const tint1 = val * (1 - sat);
+ const tint2 = val * (1 - sat * (hue - sector));
+ const tint3 = val * (1 - sat * (1 + sector - hue));
+ let red, green, blue;
+ if (sector === 1) {
+ // Yellow to green.
+ red = tint2;
+ green = val;
+ blue = tint1;
+ } else if (sector === 2) {
+ // Green to cyan.
+ red = tint1;
+ green = val;
+ blue = tint3;
+ } else if (sector === 3) {
+ // Cyan to blue.
+ red = tint1;
+ green = tint2;
+ blue = val;
+ } else if (sector === 4) {
+ // Blue to magenta.
+ red = tint3;
+ green = tint1;
+ blue = val;
+ } else if (sector === 5) {
+ // Magenta to red.
+ red = val;
+ green = tint1;
+ blue = tint2;
+ } else {
+ // Red to yellow (sector could be 0 or 6).
+ red = val;
+ green = tint3;
+ blue = tint1;
+ }
+ RGBA = [red, green, blue, hsba[3]];
}
- RGBA = [red, green, blue, hsba[3]];
- }
- return RGBA;
-};
+ return RGBA;
+ },
-/**
- * Convert an HSLA array to HSBA.
- */
-p5.ColorConversion._hslaToHSBA = function(hsla) {
- const hue = hsla[0];
- let sat = hsla[1];
- const li = hsla[2];
+ /**
+ * Convert an HSLA array to HSBA.
+ */
+ _hslaToHSBA(hsla) {
+ const hue = hsla[0];
+ let sat = hsla[1];
+ const li = hsla[2];
- // Calculate brightness.
- let val;
- if (li < 0.5) {
- val = (1 + sat) * li;
- } else {
- val = li + sat - li * sat;
- }
-
- // Convert saturation.
- sat = 2 * (val - li) / val;
-
- // Hue and alpha stay the same.
- return [hue, sat, val, hsla[3]];
-};
-
-/**
- * Convert an HSLA array to RGBA.
- *
- * We need to change basis from HSLA to something that can be more easily be
- * projected onto RGBA. We will choose hue and brightness as our first two
- * components, and pick a convenient third one ('zest') so that we don't need
- * to calculate formal HSBA saturation.
- */
-p5.ColorConversion._hslaToRGBA = function(hsla) {
- const hue = hsla[0] * 6; // We will split hue into 6 sectors.
- const sat = hsla[1];
- const li = hsla[2];
-
- let RGBA = [];
-
- if (sat === 0) {
- RGBA = [li, li, li, hsla[3]]; // Return early if grayscale.
- } else {
// Calculate brightness.
let val;
if (li < 0.5) {
@@ -146,126 +112,159 @@ p5.ColorConversion._hslaToRGBA = function(hsla) {
val = li + sat - li * sat;
}
- // Define zest.
- const zest = 2 * li - val;
+ // Convert saturation.
+ sat = 2 * (val - li) / val;
- // Implement projection (project onto green by default).
- const hzvToRGB = (hue, zest, val) => {
- if (hue < 0) {
- // Hue must wrap to allow projection onto red and blue.
- hue += 6;
- } else if (hue >= 6) {
- hue -= 6;
- }
- if (hue < 1) {
- // Red to yellow (increasing green).
- return zest + (val - zest) * hue;
- } else if (hue < 3) {
- // Yellow to cyan (greatest green).
- return val;
- } else if (hue < 4) {
- // Cyan to blue (decreasing green).
- return zest + (val - zest) * (4 - hue);
+ // Hue and alpha stay the same.
+ return [hue, sat, val, hsla[3]];
+ },
+
+ /**
+ * Convert an HSLA array to RGBA.
+ *
+ * We need to change basis from HSLA to something that can be more easily be
+ * projected onto RGBA. We will choose hue and brightness as our first two
+ * components, and pick a convenient third one ('zest') so that we don't need
+ * to calculate formal HSBA saturation.
+ */
+ _hslaToRGBA(hsla) {
+ const hue = hsla[0] * 6; // We will split hue into 6 sectors.
+ const sat = hsla[1];
+ const li = hsla[2];
+
+ let RGBA = [];
+
+ if (sat === 0) {
+ RGBA = [li, li, li, hsla[3]]; // Return early if grayscale.
+ } else {
+ // Calculate brightness.
+ let val;
+ if (li < 0.5) {
+ val = (1 + sat) * li;
} else {
- // Blue to red (least green).
- return zest;
+ val = li + sat - li * sat;
}
- };
- // Perform projections, offsetting hue as necessary.
- RGBA = [
- hzvToRGB(hue + 2, zest, val),
- hzvToRGB(hue, zest, val),
- hzvToRGB(hue - 2, zest, val),
- hsla[3]
- ];
- }
+ // Define zest.
+ const zest = 2 * li - val;
- return RGBA;
-};
+ // Implement projection (project onto green by default).
+ const hzvToRGB = (hue, zest, val) => {
+ if (hue < 0) {
+ // Hue must wrap to allow projection onto red and blue.
+ hue += 6;
+ } else if (hue >= 6) {
+ hue -= 6;
+ }
+ if (hue < 1) {
+ // Red to yellow (increasing green).
+ return zest + (val - zest) * hue;
+ } else if (hue < 3) {
+ // Yellow to cyan (greatest green).
+ return val;
+ } else if (hue < 4) {
+ // Cyan to blue (decreasing green).
+ return zest + (val - zest) * (4 - hue);
+ } else {
+ // Blue to red (least green).
+ return zest;
+ }
+ };
-/**
- * Convert an RGBA array to HSBA.
- */
-p5.ColorConversion._rgbaToHSBA = function(rgba) {
- const red = rgba[0];
- const green = rgba[1];
- const blue = rgba[2];
+ // Perform projections, offsetting hue as necessary.
+ RGBA = [
+ hzvToRGB(hue + 2, zest, val),
+ hzvToRGB(hue, zest, val),
+ hzvToRGB(hue - 2, zest, val),
+ hsla[3]
+ ];
+ }
- const val = Math.max(red, green, blue);
- const chroma = val - Math.min(red, green, blue);
+ return RGBA;
+ },
- let hue, sat;
- if (chroma === 0) {
- // Return early if grayscale.
- hue = 0;
- sat = 0;
- } else {
- sat = chroma / val;
- if (red === val) {
- // Magenta to yellow.
- hue = (green - blue) / chroma;
- } else if (green === val) {
- // Yellow to cyan.
- hue = 2 + (blue - red) / chroma;
- } else if (blue === val) {
- // Cyan to magenta.
- hue = 4 + (red - green) / chroma;
- }
- if (hue < 0) {
- // Confine hue to the interval [0, 1).
- hue += 6;
- } else if (hue >= 6) {
- hue -= 6;
+ /**
+ * Convert an RGBA array to HSBA.
+ */
+ _rgbaToHSBA(rgba) {
+ const red = rgba[0];
+ const green = rgba[1];
+ const blue = rgba[2];
+
+ const val = Math.max(red, green, blue);
+ const chroma = val - Math.min(red, green, blue);
+
+ let hue, sat;
+ if (chroma === 0) {
+ // Return early if grayscale.
+ hue = 0;
+ sat = 0;
+ } else {
+ sat = chroma / val;
+ if (red === val) {
+ // Magenta to yellow.
+ hue = (green - blue) / chroma;
+ } else if (green === val) {
+ // Yellow to cyan.
+ hue = 2 + (blue - red) / chroma;
+ } else if (blue === val) {
+ // Cyan to magenta.
+ hue = 4 + (red - green) / chroma;
+ }
+ if (hue < 0) {
+ // Confine hue to the interval [0, 1).
+ hue += 6;
+ } else if (hue >= 6) {
+ hue -= 6;
+ }
}
- }
- return [hue / 6, sat, val, rgba[3]];
-};
+ return [hue / 6, sat, val, rgba[3]];
+ },
-/**
- * Convert an RGBA array to HSLA.
- */
-p5.ColorConversion._rgbaToHSLA = function(rgba) {
- const red = rgba[0];
- const green = rgba[1];
- const blue = rgba[2];
+ /**
+ * Convert an RGBA array to HSLA.
+ */
+ _rgbaToHSLA(rgba) {
+ const red = rgba[0];
+ const green = rgba[1];
+ const blue = rgba[2];
- const val = Math.max(red, green, blue);
- const min = Math.min(red, green, blue);
- const li = val + min; // We will halve this later.
- const chroma = val - min;
+ const val = Math.max(red, green, blue);
+ const min = Math.min(red, green, blue);
+ const li = val + min; // We will halve this later.
+ const chroma = val - min;
- let hue, sat;
- if (chroma === 0) {
- // Return early if grayscale.
- hue = 0;
- sat = 0;
- } else {
- if (li < 1) {
- sat = chroma / li;
+ let hue, sat;
+ if (chroma === 0) {
+ // Return early if grayscale.
+ hue = 0;
+ sat = 0;
} else {
- sat = chroma / (2 - li);
- }
- if (red === val) {
- // Magenta to yellow.
- hue = (green - blue) / chroma;
- } else if (green === val) {
- // Yellow to cyan.
- hue = 2 + (blue - red) / chroma;
- } else if (blue === val) {
- // Cyan to magenta.
- hue = 4 + (red - green) / chroma;
- }
- if (hue < 0) {
- // Confine hue to the interval [0, 1).
- hue += 6;
- } else if (hue >= 6) {
- hue -= 6;
+ if (li < 1) {
+ sat = chroma / li;
+ } else {
+ sat = chroma / (2 - li);
+ }
+ if (red === val) {
+ // Magenta to yellow.
+ hue = (green - blue) / chroma;
+ } else if (green === val) {
+ // Yellow to cyan.
+ hue = 2 + (blue - red) / chroma;
+ } else if (blue === val) {
+ // Cyan to magenta.
+ hue = 4 + (red - green) / chroma;
+ }
+ if (hue < 0) {
+ // Confine hue to the interval [0, 1).
+ hue += 6;
+ } else if (hue >= 6) {
+ hue -= 6;
+ }
}
- }
- return [hue / 6, sat, li / 2, rgba[3]];
+ return [hue / 6, sat, li / 2, rgba[3]];
+ }
};
-
export default p5.ColorConversion;
diff --git a/src/color/p5.Color.js b/src/color/p5.Color.js
index 6e5c305a42..717897707d 100644
--- a/src/color/p5.Color.js
+++ b/src/color/p5.Color.js
@@ -11,451 +11,6 @@ import p5 from '../core/main';
import * as constants from '../core/constants';
import color_conversion from './color_conversion';
-/**
- * Each color stores the color mode and level maxes that were applied at the
- * time of its construction. These are used to interpret the input arguments
- * (at construction and later for that instance of color) and to format the
- * output e.g. when saturation() is requested.
- *
- * Internally, we store an array representing the ideal RGBA values in floating
- * point form, normalized from 0 to 1. From this we calculate the closest
- * screen color (RGBA levels from 0 to 255) and expose this to the renderer.
- *
- * We also cache normalized, floating-point components of the color in various
- * representations as they are calculated. This is done to prevent repeating a
- * conversion that has already been performed.
- *
- * color() is the recommended way to create an instance
- * of this class. However, one can also create a color instace from the constructor
- * using the parameters below.
- *
- * @class p5.Color
- * @constructor
- * @param {p5} [pInst] pointer to p5 instance.
- *
- * @param {Number[]|String} vals an array containing the color values
- * for red, green, blue and alpha channel
- * or CSS color.
- */
-p5.Color = function(pInst, vals) {
- // Record color mode and maxes at time of construction.
- this._storeModeAndMaxes(pInst._colorMode, pInst._colorMaxes);
-
- // Calculate normalized RGBA values.
- if (
- this.mode !== constants.RGB &&
- this.mode !== constants.HSL &&
- this.mode !== constants.HSB
- ) {
- throw new Error(`${this.mode} is an invalid colorMode.`);
- } else {
- this._array = p5.Color._parseInputs.apply(this, vals);
- }
-
- // Expose closest screen color.
- this._calculateLevels();
- return this;
-};
-
-/**
- * This method returns the color formatted as a string. This can be useful
- * for debugging, or for using p5.js with other libraries.
- *
- * @method toString
- * @param {String} [format] How the color string will be formatted.
- * Leaving this empty formats the string as rgba(r, g, b, a).
- * '#rgb' '#rgba' '#rrggbb' and '#rrggbbaa' format as hexadecimal color codes.
- * 'rgb' 'hsb' and 'hsl' return the color formatted in the specified color mode.
- * 'rgba' 'hsba' and 'hsla' are the same as above but with alpha channels.
- * 'rgb%' 'hsb%' 'hsl%' 'rgba%' 'hsba%' and 'hsla%' format as percentages.
- * @return {String} the formatted string
- *
- * @example
- *
- *
- * createCanvas(200, 100);
- * let myColor;
- * stroke(255);
- * myColor = color(100, 100, 250);
- * fill(myColor);
- * rotate(HALF_PI);
- * text(myColor.toString(), 0, -5);
- * text(myColor.toString('#rrggbb'), 0, -30);
- * text(myColor.toString('rgba%'), 0, -55);
- * describe('A canvas with 3 text representation of their color.');
- *
- *
- *
- *
- *
- * let myColor = color(100, 130, 250);
- * text(myColor.toString('#rrggbb'), 25, 25);
- *
- *
- */
-p5.Color.prototype.toString = function(format) {
- const a = this.levels;
- const f = this._array;
- const alpha = f[3]; // String representation uses normalized alpha
-
- switch (format) {
- case '#rrggbb':
- return '#'.concat(
- a[0] < 16 ? '0'.concat(a[0].toString(16)) : a[0].toString(16),
- a[1] < 16 ? '0'.concat(a[1].toString(16)) : a[1].toString(16),
- a[2] < 16 ? '0'.concat(a[2].toString(16)) : a[2].toString(16)
- );
-
- case '#rrggbbaa':
- return '#'.concat(
- a[0] < 16 ? '0'.concat(a[0].toString(16)) : a[0].toString(16),
- a[1] < 16 ? '0'.concat(a[1].toString(16)) : a[1].toString(16),
- a[2] < 16 ? '0'.concat(a[2].toString(16)) : a[2].toString(16),
- a[3] < 16 ? '0'.concat(a[3].toString(16)) : a[3].toString(16)
- );
-
- case '#rgb':
- return '#'.concat(
- Math.round(f[0] * 15).toString(16),
- Math.round(f[1] * 15).toString(16),
- Math.round(f[2] * 15).toString(16)
- );
-
- case '#rgba':
- return '#'.concat(
- Math.round(f[0] * 15).toString(16),
- Math.round(f[1] * 15).toString(16),
- Math.round(f[2] * 15).toString(16),
- Math.round(f[3] * 15).toString(16)
- );
-
- case 'rgb':
- return 'rgb('.concat(a[0], ', ', a[1], ', ', a[2], ')');
-
- case 'rgb%':
- return 'rgb('.concat(
- (100 * f[0]).toPrecision(3),
- '%, ',
- (100 * f[1]).toPrecision(3),
- '%, ',
- (100 * f[2]).toPrecision(3),
- '%)'
- );
-
- case 'rgba%':
- return 'rgba('.concat(
- (100 * f[0]).toPrecision(3),
- '%, ',
- (100 * f[1]).toPrecision(3),
- '%, ',
- (100 * f[2]).toPrecision(3),
- '%, ',
- (100 * f[3]).toPrecision(3),
- '%)'
- );
-
- case 'hsb':
- case 'hsv':
- if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array);
- return 'hsb('.concat(
- this.hsba[0] * this.maxes[constants.HSB][0],
- ', ',
- this.hsba[1] * this.maxes[constants.HSB][1],
- ', ',
- this.hsba[2] * this.maxes[constants.HSB][2],
- ')'
- );
-
- case 'hsb%':
- case 'hsv%':
- if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array);
- return 'hsb('.concat(
- (100 * this.hsba[0]).toPrecision(3),
- '%, ',
- (100 * this.hsba[1]).toPrecision(3),
- '%, ',
- (100 * this.hsba[2]).toPrecision(3),
- '%)'
- );
-
- case 'hsba':
- case 'hsva':
- if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array);
- return 'hsba('.concat(
- this.hsba[0] * this.maxes[constants.HSB][0],
- ', ',
- this.hsba[1] * this.maxes[constants.HSB][1],
- ', ',
- this.hsba[2] * this.maxes[constants.HSB][2],
- ', ',
- alpha,
- ')'
- );
-
- case 'hsba%':
- case 'hsva%':
- if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array);
- return 'hsba('.concat(
- (100 * this.hsba[0]).toPrecision(3),
- '%, ',
- (100 * this.hsba[1]).toPrecision(3),
- '%, ',
- (100 * this.hsba[2]).toPrecision(3),
- '%, ',
- (100 * alpha).toPrecision(3),
- '%)'
- );
-
- case 'hsl':
- if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array);
- return 'hsl('.concat(
- this.hsla[0] * this.maxes[constants.HSL][0],
- ', ',
- this.hsla[1] * this.maxes[constants.HSL][1],
- ', ',
- this.hsla[2] * this.maxes[constants.HSL][2],
- ')'
- );
-
- case 'hsl%':
- if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array);
- return 'hsl('.concat(
- (100 * this.hsla[0]).toPrecision(3),
- '%, ',
- (100 * this.hsla[1]).toPrecision(3),
- '%, ',
- (100 * this.hsla[2]).toPrecision(3),
- '%)'
- );
-
- case 'hsla':
- if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array);
- return 'hsla('.concat(
- this.hsla[0] * this.maxes[constants.HSL][0],
- ', ',
- this.hsla[1] * this.maxes[constants.HSL][1],
- ', ',
- this.hsla[2] * this.maxes[constants.HSL][2],
- ', ',
- alpha,
- ')'
- );
-
- case 'hsla%':
- if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array);
- return 'hsl('.concat(
- (100 * this.hsla[0]).toPrecision(3),
- '%, ',
- (100 * this.hsla[1]).toPrecision(3),
- '%, ',
- (100 * this.hsla[2]).toPrecision(3),
- '%, ',
- (100 * alpha).toPrecision(3),
- '%)'
- );
-
- case 'rgba':
- default:
- return 'rgba('.concat(a[0], ',', a[1], ',', a[2], ',', alpha, ')');
- }
-};
-
-/**
- * The setRed method sets the red component of a color.
- * The range depends on your color mode, in the default RGB mode it's between 0 and 255.
- * @method setRed
- * @param {Number} red the new red value
- * @example
- *
- *
- * let backgroundColor;
- *
- * function setup() {
- * backgroundColor = color(100, 50, 150);
- * }
- *
- * function draw() {
- * backgroundColor.setRed(128 + 128 * sin(millis() / 1000));
- * background(backgroundColor);
- * describe('canvas with gradually changing background color');
- * }
- *
- *
- */
-p5.Color.prototype.setRed = function(new_red) {
- this._array[0] = new_red / this.maxes[constants.RGB][0];
- this._calculateLevels();
-};
-
-/**
- * The setGreen method sets the green component of a color.
- * The range depends on your color mode, in the default RGB mode it's between 0 and 255.
- * @method setGreen
- * @param {Number} green the new green value
- * @example
- *
- *
- * let backgroundColor = color(100, 50, 150);
- * function draw() {
- * backgroundColor.setGreen(128 + 128 * sin(millis() / 1000));
- * background(backgroundColor);
- * describe('canvas with gradually changing background color');
- * }
- *
- *
- *
- **/
-p5.Color.prototype.setGreen = function(new_green) {
- this._array[1] = new_green / this.maxes[constants.RGB][1];
- this._calculateLevels();
-};
-
-/**
- * The setBlue method sets the blue component of a color.
- * The range depends on your color mode, in the default RGB mode it's between 0 and 255.
- * @method setBlue
- * @param {Number} blue the new blue value
- * @example
- *
- *
- * let backgroundColor = color(100, 50, 150);
- * function draw() {
- * backgroundColor.setBlue(128 + 128 * sin(millis() / 1000));
- * background(backgroundColor);
- * describe('canvas with gradually changing background color');
- * }
- *
- *
- *
- **/
-p5.Color.prototype.setBlue = function(new_blue) {
- this._array[2] = new_blue / this.maxes[constants.RGB][2];
- this._calculateLevels();
-};
-
-/**
- * The setAlpha method sets the transparency (alpha) value of a color.
- * The range depends on your color mode, in the default RGB mode it's between 0 and 255.
- * @method setAlpha
- * @param {Number} alpha the new alpha value
- * @example
- *
- *
- * function draw() {
- * clear();
- * background(200);
- * squareColor = color(100, 50, 100);
- * squareColor.setAlpha(128 + 128 * sin(millis() / 1000));
- * fill(squareColor);
- * rect(13, 13, width - 26, height - 26);
- * describe('a square with gradually changing opacity on a gray background');
- * }
- *
- *
- **/
-p5.Color.prototype.setAlpha = function(new_alpha) {
- this._array[3] = new_alpha / this.maxes[this.mode][3];
- this._calculateLevels();
-};
-
-// calculates and stores the closest screen levels
-p5.Color.prototype._calculateLevels = function() {
- const array = this._array;
- // (loop backwards for performance)
- const levels = (this.levels = new Array(array.length));
- for (let i = array.length - 1; i >= 0; --i) {
- levels[i] = Math.round(array[i] * 255);
- }
-
- // Clear cached HSL/HSB values
- this.hsla = null;
- this.hsba = null;
-};
-
-p5.Color.prototype._getAlpha = function() {
- return this._array[3] * this.maxes[this.mode][3];
-};
-
-// stores the color mode and maxes in this instance of Color
-// for later use (by _parseInputs())
-p5.Color.prototype._storeModeAndMaxes = function(new_mode, new_maxes) {
- this.mode = new_mode;
- this.maxes = new_maxes;
-};
-
-p5.Color.prototype._getMode = function() {
- return this.mode;
-};
-
-p5.Color.prototype._getMaxes = function() {
- return this.maxes;
-};
-
-p5.Color.prototype._getBlue = function() {
- return this._array[2] * this.maxes[constants.RGB][2];
-};
-
-p5.Color.prototype._getBrightness = function() {
- if (!this.hsba) {
- this.hsba = color_conversion._rgbaToHSBA(this._array);
- }
- return this.hsba[2] * this.maxes[constants.HSB][2];
-};
-
-p5.Color.prototype._getGreen = function() {
- return this._array[1] * this.maxes[constants.RGB][1];
-};
-
-/**
- * Hue is the same in HSB and HSL, but the maximum value may be different.
- * This function will return the HSB-normalized saturation when supplied with
- * an HSB color object, but will default to the HSL-normalized saturation
- * otherwise.
- */
-p5.Color.prototype._getHue = function() {
- if (this.mode === constants.HSB) {
- if (!this.hsba) {
- this.hsba = color_conversion._rgbaToHSBA(this._array);
- }
- return this.hsba[0] * this.maxes[constants.HSB][0];
- } else {
- if (!this.hsla) {
- this.hsla = color_conversion._rgbaToHSLA(this._array);
- }
- return this.hsla[0] * this.maxes[constants.HSL][0];
- }
-};
-
-p5.Color.prototype._getLightness = function() {
- if (!this.hsla) {
- this.hsla = color_conversion._rgbaToHSLA(this._array);
- }
- return this.hsla[2] * this.maxes[constants.HSL][2];
-};
-
-p5.Color.prototype._getRed = function() {
- return this._array[0] * this.maxes[constants.RGB][0];
-};
-
-/**
- * Saturation is scaled differently in HSB and HSL. This function will return
- * the HSB saturation when supplied with an HSB color object, but will default
- * to the HSL saturation otherwise.
- */
-p5.Color.prototype._getSaturation = function() {
- if (this.mode === constants.HSB) {
- if (!this.hsba) {
- this.hsba = color_conversion._rgbaToHSBA(this._array);
- }
- return this.hsba[1] * this.maxes[constants.HSB][1];
- } else {
- if (!this.hsla) {
- this.hsla = color_conversion._rgbaToHSLA(this._array);
- }
- return this.hsla[1] * this.maxes[constants.HSL][1];
- }
-};
-
/**
* CSS named colors.
*/
@@ -761,6 +316,446 @@ const colorPatterns = {
};
/**
+ * Each color stores the color mode and level maxes that were applied at the
+ * time of its construction. These are used to interpret the input arguments
+ * (at construction and later for that instance of color) and to format the
+ * output e.g. when saturation() is requested.
+ *
+ * Internally, we store an array representing the ideal RGBA values in floating
+ * point form, normalized from 0 to 1. From this we calculate the closest
+ * screen color (RGBA levels from 0 to 255) and expose this to the renderer.
+ *
+ * We also cache normalized, floating-point components of the color in various
+ * representations as they are calculated. This is done to prevent repeating a
+ * conversion that has already been performed.
+ *
+ * color() is the recommended way to create an instance
+ * of this class. However, one can also create a color instace from the constructor
+ * using the parameters below.
+ *
+ * @class p5.Color
+ * @constructor
+ * @param {p5} [pInst] pointer to p5 instance.
+ *
+ * @param {Number[]|String} vals an array containing the color values
+ * for red, green, blue and alpha channel
+ * or CSS color.
+ */
+p5.Color = class Color {
+ constructor(pInst, vals) {
+ // Record color mode and maxes at time of construction.
+ this._storeModeAndMaxes(pInst._colorMode, pInst._colorMaxes);
+
+ // Calculate normalized RGBA values.
+ if (![constants.RGB, constants.HSL, constants.HSB].includes(this.mode)) {
+ throw new Error(`${this.mode} is an invalid colorMode.`);
+ } else {
+ this._array = Color._parseInputs.apply(this, vals);
+ }
+
+ // Expose closest screen color.
+ this._calculateLevels();
+ }
+
+ /**
+ * This method returns the color formatted as a string. This can be useful
+ * for debugging, or for using p5.js with other libraries.
+ *
+ * @method toString
+ * @param {String} [format] How the color string will be formatted.
+ * Leaving this empty formats the string as rgba(r, g, b, a).
+ * '#rgb' '#rgba' '#rrggbb' and '#rrggbbaa' format as hexadecimal color codes.
+ * 'rgb' 'hsb' and 'hsl' return the color formatted in the specified color mode.
+ * 'rgba' 'hsba' and 'hsla' are the same as above but with alpha channels.
+ * 'rgb%' 'hsb%' 'hsl%' 'rgba%' 'hsba%' and 'hsla%' format as percentages.
+ * @return {String} the formatted string
+ *
+ * @example
+ *
+ *
+ * createCanvas(200, 100);
+ * let myColor;
+ * stroke(255);
+ * myColor = color(100, 100, 250);
+ * fill(myColor);
+ * rotate(HALF_PI);
+ * text(myColor.toString(), 0, -5);
+ * text(myColor.toString('#rrggbb'), 0, -30);
+ * text(myColor.toString('rgba%'), 0, -55);
+ * describe('A canvas with 3 text representation of their color.');
+ *
+ *
+ *
+ *
+ *
+ * let myColor = color(100, 130, 250);
+ * text(myColor.toString('#rrggbb'), 25, 25);
+ *
+ *
+ */
+ toString(format) {
+ const a = this.levels;
+ const f = this._array;
+ const alpha = f[3]; // String representation uses normalized alpha
+
+ switch (format) {
+ case '#rrggbb':
+ return '#'.concat(
+ a[0] < 16 ? '0'.concat(a[0].toString(16)) : a[0].toString(16),
+ a[1] < 16 ? '0'.concat(a[1].toString(16)) : a[1].toString(16),
+ a[2] < 16 ? '0'.concat(a[2].toString(16)) : a[2].toString(16)
+ );
+
+ case '#rrggbbaa':
+ return '#'.concat(
+ a[0] < 16 ? '0'.concat(a[0].toString(16)) : a[0].toString(16),
+ a[1] < 16 ? '0'.concat(a[1].toString(16)) : a[1].toString(16),
+ a[2] < 16 ? '0'.concat(a[2].toString(16)) : a[2].toString(16),
+ a[3] < 16 ? '0'.concat(a[3].toString(16)) : a[3].toString(16)
+ );
+
+ case '#rgb':
+ return '#'.concat(
+ Math.round(f[0] * 15).toString(16),
+ Math.round(f[1] * 15).toString(16),
+ Math.round(f[2] * 15).toString(16)
+ );
+
+ case '#rgba':
+ return '#'.concat(
+ Math.round(f[0] * 15).toString(16),
+ Math.round(f[1] * 15).toString(16),
+ Math.round(f[2] * 15).toString(16),
+ Math.round(f[3] * 15).toString(16)
+ );
+
+ case 'rgb':
+ return 'rgb('.concat(a[0], ', ', a[1], ', ', a[2], ')');
+
+ case 'rgb%':
+ return 'rgb('.concat(
+ (100 * f[0]).toPrecision(3),
+ '%, ',
+ (100 * f[1]).toPrecision(3),
+ '%, ',
+ (100 * f[2]).toPrecision(3),
+ '%)'
+ );
+
+ case 'rgba%':
+ return 'rgba('.concat(
+ (100 * f[0]).toPrecision(3),
+ '%, ',
+ (100 * f[1]).toPrecision(3),
+ '%, ',
+ (100 * f[2]).toPrecision(3),
+ '%, ',
+ (100 * f[3]).toPrecision(3),
+ '%)'
+ );
+
+ case 'hsb':
+ case 'hsv':
+ if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array);
+ return 'hsb('.concat(
+ this.hsba[0] * this.maxes[constants.HSB][0],
+ ', ',
+ this.hsba[1] * this.maxes[constants.HSB][1],
+ ', ',
+ this.hsba[2] * this.maxes[constants.HSB][2],
+ ')'
+ );
+
+ case 'hsb%':
+ case 'hsv%':
+ if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array);
+ return 'hsb('.concat(
+ (100 * this.hsba[0]).toPrecision(3),
+ '%, ',
+ (100 * this.hsba[1]).toPrecision(3),
+ '%, ',
+ (100 * this.hsba[2]).toPrecision(3),
+ '%)'
+ );
+
+ case 'hsba':
+ case 'hsva':
+ if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array);
+ return 'hsba('.concat(
+ this.hsba[0] * this.maxes[constants.HSB][0],
+ ', ',
+ this.hsba[1] * this.maxes[constants.HSB][1],
+ ', ',
+ this.hsba[2] * this.maxes[constants.HSB][2],
+ ', ',
+ alpha,
+ ')'
+ );
+
+ case 'hsba%':
+ case 'hsva%':
+ if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array);
+ return 'hsba('.concat(
+ (100 * this.hsba[0]).toPrecision(3),
+ '%, ',
+ (100 * this.hsba[1]).toPrecision(3),
+ '%, ',
+ (100 * this.hsba[2]).toPrecision(3),
+ '%, ',
+ (100 * alpha).toPrecision(3),
+ '%)'
+ );
+
+ case 'hsl':
+ if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array);
+ return 'hsl('.concat(
+ this.hsla[0] * this.maxes[constants.HSL][0],
+ ', ',
+ this.hsla[1] * this.maxes[constants.HSL][1],
+ ', ',
+ this.hsla[2] * this.maxes[constants.HSL][2],
+ ')'
+ );
+
+ case 'hsl%':
+ if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array);
+ return 'hsl('.concat(
+ (100 * this.hsla[0]).toPrecision(3),
+ '%, ',
+ (100 * this.hsla[1]).toPrecision(3),
+ '%, ',
+ (100 * this.hsla[2]).toPrecision(3),
+ '%)'
+ );
+
+ case 'hsla':
+ if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array);
+ return 'hsla('.concat(
+ this.hsla[0] * this.maxes[constants.HSL][0],
+ ', ',
+ this.hsla[1] * this.maxes[constants.HSL][1],
+ ', ',
+ this.hsla[2] * this.maxes[constants.HSL][2],
+ ', ',
+ alpha,
+ ')'
+ );
+
+ case 'hsla%':
+ if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array);
+ return 'hsl('.concat(
+ (100 * this.hsla[0]).toPrecision(3),
+ '%, ',
+ (100 * this.hsla[1]).toPrecision(3),
+ '%, ',
+ (100 * this.hsla[2]).toPrecision(3),
+ '%, ',
+ (100 * alpha).toPrecision(3),
+ '%)'
+ );
+
+ case 'rgba':
+ default:
+ return 'rgba('.concat(a[0], ',', a[1], ',', a[2], ',', alpha, ')');
+ }
+ }
+
+ /**
+ * The setRed method sets the red component of a color.
+ * The range depends on your color mode, in the default RGB mode it's between 0 and 255.
+ * @method setRed
+ * @param {Number} red the new red value
+ * @example
+ *
+ *
+ * let backgroundColor;
+ *
+ * function setup() {
+ * backgroundColor = color(100, 50, 150);
+ * }
+ *
+ * function draw() {
+ * backgroundColor.setRed(128 + 128 * sin(millis() / 1000));
+ * background(backgroundColor);
+ * describe('canvas with gradually changing background color');
+ * }
+ *
+ *
+ */
+ setRed(new_red) {
+ this._array[0] = new_red / this.maxes[constants.RGB][0];
+ this._calculateLevels();
+ }
+
+ /**
+ * The setGreen method sets the green component of a color.
+ * The range depends on your color mode, in the default RGB mode it's between 0 and 255.
+ * @method setGreen
+ * @param {Number} green the new green value
+ * @example
+ *
+ *
+ * let backgroundColor = color(100, 50, 150);
+ * function draw() {
+ * backgroundColor.setGreen(128 + 128 * sin(millis() / 1000));
+ * background(backgroundColor);
+ * describe('canvas with gradually changing background color');
+ * }
+ *
+ *
+ *
+ **/
+ setGreen(new_green) {
+ this._array[1] = new_green / this.maxes[constants.RGB][1];
+ this._calculateLevels();
+ }
+
+ /**
+ * The setBlue method sets the blue component of a color.
+ * The range depends on your color mode, in the default RGB mode it's between 0 and 255.
+ * @method setBlue
+ * @param {Number} blue the new blue value
+ * @example
+ *
+ *
+ * let backgroundColor = color(100, 50, 150);
+ * function draw() {
+ * backgroundColor.setBlue(128 + 128 * sin(millis() / 1000));
+ * background(backgroundColor);
+ * describe('canvas with gradually changing background color');
+ * }
+ *
+ *
+ *
+ **/
+ setBlue(new_blue) {
+ this._array[2] = new_blue / this.maxes[constants.RGB][2];
+ this._calculateLevels();
+ }
+
+ /**
+ * The setAlpha method sets the transparency (alpha) value of a color.
+ * The range depends on your color mode, in the default RGB mode it's between 0 and 255.
+ * @method setAlpha
+ * @param {Number} alpha the new alpha value
+ * @example
+ *
+ *
+ * function draw() {
+ * clear();
+ * background(200);
+ * squareColor = color(100, 50, 100);
+ * squareColor.setAlpha(128 + 128 * sin(millis() / 1000));
+ * fill(squareColor);
+ * rect(13, 13, width - 26, height - 26);
+ * describe('a square with gradually changing opacity on a gray background');
+ * }
+ *
+ *
+ **/
+ setAlpha(new_alpha) {
+ this._array[3] = new_alpha / this.maxes[this.mode][3];
+ this._calculateLevels();
+ }
+
+ // calculates and stores the closest screen levels
+ _calculateLevels() {
+ const array = this._array;
+ // (loop backwards for performance)
+ const levels = (this.levels = new Array(array.length));
+ for (let i = array.length - 1; i >= 0; --i) {
+ levels[i] = Math.round(array[i] * 255);
+ }
+
+ // Clear cached HSL/HSB values
+ this.hsla = null;
+ this.hsba = null;
+ }
+
+ _getAlpha() {
+ return this._array[3] * this.maxes[this.mode][3];
+ }
+
+ // stores the color mode and maxes in this instance of Color
+ // for later use (by _parseInputs())
+ _storeModeAndMaxes(new_mode, new_maxes) {
+ this.mode = new_mode;
+ this.maxes = new_maxes;
+ }
+
+ _getMode() {
+ return this.mode;
+ }
+
+ _getMaxes() {
+ return this.maxes;
+ }
+
+ _getBlue() {
+ return this._array[2] * this.maxes[constants.RGB][2];
+ }
+
+ _getBrightness() {
+ if (!this.hsba) {
+ this.hsba = color_conversion._rgbaToHSBA(this._array);
+ }
+ return this.hsba[2] * this.maxes[constants.HSB][2];
+ }
+
+ _getGreen() {
+ return this._array[1] * this.maxes[constants.RGB][1];
+ }
+
+ /**
+ * Hue is the same in HSB and HSL, but the maximum value may be different.
+ * This function will return the HSB-normalized saturation when supplied with
+ * an HSB color object, but will default to the HSL-normalized saturation
+ * otherwise.
+ */
+ _getHue() {
+ if (this.mode === constants.HSB) {
+ if (!this.hsba) {
+ this.hsba = color_conversion._rgbaToHSBA(this._array);
+ }
+ return this.hsba[0] * this.maxes[constants.HSB][0];
+ } else {
+ if (!this.hsla) {
+ this.hsla = color_conversion._rgbaToHSLA(this._array);
+ }
+ return this.hsla[0] * this.maxes[constants.HSL][0];
+ }
+ }
+
+ _getLightness() {
+ if (!this.hsla) {
+ this.hsla = color_conversion._rgbaToHSLA(this._array);
+ }
+ return this.hsla[2] * this.maxes[constants.HSL][2];
+ }
+
+ _getRed() {
+ return this._array[0] * this.maxes[constants.RGB][0];
+ }
+
+ /**
+ * Saturation is scaled differently in HSB and HSL. This function will return
+ * the HSB saturation when supplied with an HSB color object, but will default
+ * to the HSL saturation otherwise.
+ */
+ _getSaturation() {
+ if (this.mode === constants.HSB) {
+ if (!this.hsba) {
+ this.hsba = color_conversion._rgbaToHSBA(this._array);
+ }
+ return this.hsba[1] * this.maxes[constants.HSB][1];
+ } else {
+ if (!this.hsla) {
+ this.hsla = color_conversion._rgbaToHSLA(this._array);
+ }
+ return this.hsla[1] * this.maxes[constants.HSL][1];
+ }
+ }
+ /**
* For a number of different inputs, returns a color formatted as [r, g, b, a]
* arrays, with each component normalized between 0 and 1.
*
@@ -787,212 +782,213 @@ const colorPatterns = {
*
*
*/
-p5.Color._parseInputs = function(r, g, b, a) {
- const numArgs = arguments.length;
- const mode = this.mode;
- const maxes = this.maxes[mode];
- let results = [];
- let i;
-
- if (numArgs >= 3) {
- // Argument is a list of component values.
-
- results[0] = r / maxes[0];
- results[1] = g / maxes[1];
- results[2] = b / maxes[2];
-
- // Alpha may be undefined, so default it to 100%.
- if (typeof a === 'number') {
- results[3] = a / maxes[3];
- } else {
- results[3] = 1;
- }
+ static _parseInputs(r, g, b, a) {
+ const numArgs = arguments.length;
+ const mode = this.mode;
+ const maxes = this.maxes[mode];
+ let results = [];
+ let i;
+
+ if (numArgs >= 3) {
+ // Argument is a list of component values.
+
+ results[0] = r / maxes[0];
+ results[1] = g / maxes[1];
+ results[2] = b / maxes[2];
+
+ // Alpha may be undefined, so default it to 100%.
+ if (typeof a === 'number') {
+ results[3] = a / maxes[3];
+ } else {
+ results[3] = 1;
+ }
- // Constrain components to the range [0,1].
- // (loop backwards for performance)
- for (i = results.length - 1; i >= 0; --i) {
- const result = results[i];
- if (result < 0) {
- results[i] = 0;
- } else if (result > 1) {
- results[i] = 1;
+ // Constrain components to the range [0,1].
+ // (loop backwards for performance)
+ for (i = results.length - 1; i >= 0; --i) {
+ const result = results[i];
+ if (result < 0) {
+ results[i] = 0;
+ } else if (result > 1) {
+ results[i] = 1;
+ }
}
- }
- // Convert to RGBA and return.
- if (mode === constants.HSL) {
- return color_conversion._hslaToRGBA(results);
- } else if (mode === constants.HSB) {
- return color_conversion._hsbaToRGBA(results);
- } else {
- return results;
- }
- } else if (numArgs === 1 && typeof r === 'string') {
- const str = r.trim().toLowerCase();
+ // Convert to RGBA and return.
+ if (mode === constants.HSL) {
+ return color_conversion._hslaToRGBA(results);
+ } else if (mode === constants.HSB) {
+ return color_conversion._hsbaToRGBA(results);
+ } else {
+ return results;
+ }
+ } else if (numArgs === 1 && typeof r === 'string') {
+ const str = r.trim().toLowerCase();
- // Return if string is a named colour.
- if (namedColors[str]) {
- return p5.Color._parseInputs.call(this, namedColors[str]);
- }
+ // Return if string is a named colour.
+ if (namedColors[str]) {
+ return Color._parseInputs.call(this, namedColors[str]);
+ }
- // Try RGBA pattern matching.
- if (colorPatterns.HEX3.test(str)) {
- // #rgb
- results = colorPatterns.HEX3.exec(str)
- .slice(1)
- .map(color => parseInt(color + color, 16) / 255);
- results[3] = 1;
- return results;
- } else if (colorPatterns.HEX6.test(str)) {
- // #rrggbb
- results = colorPatterns.HEX6.exec(str)
- .slice(1)
- .map(color => parseInt(color, 16) / 255);
- results[3] = 1;
- return results;
- } else if (colorPatterns.HEX4.test(str)) {
- // #rgba
- results = colorPatterns.HEX4.exec(str)
- .slice(1)
- .map(color => parseInt(color + color, 16) / 255);
- return results;
- } else if (colorPatterns.HEX8.test(str)) {
- // #rrggbbaa
- results = colorPatterns.HEX8.exec(str)
- .slice(1)
- .map(color => parseInt(color, 16) / 255);
- return results;
- } else if (colorPatterns.RGB.test(str)) {
- // rgb(R,G,B)
- results = colorPatterns.RGB.exec(str)
- .slice(1)
- .map(color => color / 255);
- results[3] = 1;
- return results;
- } else if (colorPatterns.RGB_PERCENT.test(str)) {
- // rgb(R%,G%,B%)
- results = colorPatterns.RGB_PERCENT.exec(str)
- .slice(1)
- .map(color => parseFloat(color) / 100);
- results[3] = 1;
- return results;
- } else if (colorPatterns.RGBA.test(str)) {
- // rgba(R,G,B,A)
- results = colorPatterns.RGBA.exec(str)
- .slice(1)
- .map((color, idx) => {
- if (idx === 3) {
- return parseFloat(color);
- }
- return color / 255;
- });
- return results;
- } else if (colorPatterns.RGBA_PERCENT.test(str)) {
- // rgba(R%,G%,B%,A%)
- results = colorPatterns.RGBA_PERCENT.exec(str)
- .slice(1)
- .map((color, idx) => {
- if (idx === 3) {
- return parseFloat(color);
- }
- return parseFloat(color) / 100;
- });
- return results;
- }
+ // Try RGBA pattern matching.
+ if (colorPatterns.HEX3.test(str)) {
+ // #rgb
+ results = colorPatterns.HEX3.exec(str)
+ .slice(1)
+ .map(color => parseInt(color + color, 16) / 255);
+ results[3] = 1;
+ return results;
+ } else if (colorPatterns.HEX6.test(str)) {
+ // #rrggbb
+ results = colorPatterns.HEX6.exec(str)
+ .slice(1)
+ .map(color => parseInt(color, 16) / 255);
+ results[3] = 1;
+ return results;
+ } else if (colorPatterns.HEX4.test(str)) {
+ // #rgba
+ results = colorPatterns.HEX4.exec(str)
+ .slice(1)
+ .map(color => parseInt(color + color, 16) / 255);
+ return results;
+ } else if (colorPatterns.HEX8.test(str)) {
+ // #rrggbbaa
+ results = colorPatterns.HEX8.exec(str)
+ .slice(1)
+ .map(color => parseInt(color, 16) / 255);
+ return results;
+ } else if (colorPatterns.RGB.test(str)) {
+ // rgb(R,G,B)
+ results = colorPatterns.RGB.exec(str)
+ .slice(1)
+ .map(color => color / 255);
+ results[3] = 1;
+ return results;
+ } else if (colorPatterns.RGB_PERCENT.test(str)) {
+ // rgb(R%,G%,B%)
+ results = colorPatterns.RGB_PERCENT.exec(str)
+ .slice(1)
+ .map(color => parseFloat(color) / 100);
+ results[3] = 1;
+ return results;
+ } else if (colorPatterns.RGBA.test(str)) {
+ // rgba(R,G,B,A)
+ results = colorPatterns.RGBA.exec(str)
+ .slice(1)
+ .map((color, idx) => {
+ if (idx === 3) {
+ return parseFloat(color);
+ }
+ return color / 255;
+ });
+ return results;
+ } else if (colorPatterns.RGBA_PERCENT.test(str)) {
+ // rgba(R%,G%,B%,A%)
+ results = colorPatterns.RGBA_PERCENT.exec(str)
+ .slice(1)
+ .map((color, idx) => {
+ if (idx === 3) {
+ return parseFloat(color);
+ }
+ return parseFloat(color) / 100;
+ });
+ return results;
+ }
- // Try HSLA pattern matching.
- if (colorPatterns.HSL.test(str)) {
- // hsl(H,S,L)
- results = colorPatterns.HSL.exec(str)
- .slice(1)
- .map((color, idx) => {
- if (idx === 0) {
- return parseInt(color, 10) / 360;
- }
- return parseInt(color, 10) / 100;
- });
- results[3] = 1;
- } else if (colorPatterns.HSLA.test(str)) {
- // hsla(H,S,L,A)
- results = colorPatterns.HSLA.exec(str)
- .slice(1)
- .map((color, idx) => {
- if (idx === 0) {
- return parseInt(color, 10) / 360;
- } else if (idx === 3) {
- return parseFloat(color);
- }
- return parseInt(color, 10) / 100;
- });
- }
- results = results.map(value => Math.max(Math.min(value, 1), 0));
- if (results.length) {
- return color_conversion._hslaToRGBA(results);
- }
+ // Try HSLA pattern matching.
+ if (colorPatterns.HSL.test(str)) {
+ // hsl(H,S,L)
+ results = colorPatterns.HSL.exec(str)
+ .slice(1)
+ .map((color, idx) => {
+ if (idx === 0) {
+ return parseInt(color, 10) / 360;
+ }
+ return parseInt(color, 10) / 100;
+ });
+ results[3] = 1;
+ } else if (colorPatterns.HSLA.test(str)) {
+ // hsla(H,S,L,A)
+ results = colorPatterns.HSLA.exec(str)
+ .slice(1)
+ .map((color, idx) => {
+ if (idx === 0) {
+ return parseInt(color, 10) / 360;
+ } else if (idx === 3) {
+ return parseFloat(color);
+ }
+ return parseInt(color, 10) / 100;
+ });
+ }
+ results = results.map(value => Math.max(Math.min(value, 1), 0));
+ if (results.length) {
+ return color_conversion._hslaToRGBA(results);
+ }
- // Try HSBA pattern matching.
- if (colorPatterns.HSB.test(str)) {
- // hsb(H,S,B)
- results = colorPatterns.HSB.exec(str)
- .slice(1)
- .map((color, idx) => {
- if (idx === 0) {
- return parseInt(color, 10) / 360;
- }
- return parseInt(color, 10) / 100;
- });
- results[3] = 1;
- } else if (colorPatterns.HSBA.test(str)) {
- // hsba(H,S,B,A)
- results = colorPatterns.HSBA.exec(str)
- .slice(1)
- .map((color, idx) => {
- if (idx === 0) {
- return parseInt(color, 10) / 360;
- } else if (idx === 3) {
- return parseFloat(color);
- }
- return parseInt(color, 10) / 100;
- });
- }
+ // Try HSBA pattern matching.
+ if (colorPatterns.HSB.test(str)) {
+ // hsb(H,S,B)
+ results = colorPatterns.HSB.exec(str)
+ .slice(1)
+ .map((color, idx) => {
+ if (idx === 0) {
+ return parseInt(color, 10) / 360;
+ }
+ return parseInt(color, 10) / 100;
+ });
+ results[3] = 1;
+ } else if (colorPatterns.HSBA.test(str)) {
+ // hsba(H,S,B,A)
+ results = colorPatterns.HSBA.exec(str)
+ .slice(1)
+ .map((color, idx) => {
+ if (idx === 0) {
+ return parseInt(color, 10) / 360;
+ } else if (idx === 3) {
+ return parseFloat(color);
+ }
+ return parseInt(color, 10) / 100;
+ });
+ }
- if (results.length) {
- // (loop backwards for performance)
- for (i = results.length - 1; i >= 0; --i) {
- results[i] = Math.max(Math.min(results[i], 1), 0);
+ if (results.length) {
+ // (loop backwards for performance)
+ for (i = results.length - 1; i >= 0; --i) {
+ results[i] = Math.max(Math.min(results[i], 1), 0);
+ }
+
+ return color_conversion._hsbaToRGBA(results);
}
- return color_conversion._hsbaToRGBA(results);
- }
+ // Input did not match any CSS color pattern: default to white.
+ results = [1, 1, 1, 1];
+ } else if ((numArgs === 1 || numArgs === 2) && typeof r === 'number') {
+ // 'Grayscale' mode.
+
+ /**
+ * For HSB and HSL, interpret the gray level as a brightness/lightness
+ * value (they are equivalent when chroma is zero). For RGB, normalize the
+ * gray level according to the blue maximum.
+ */
+ results[0] = r / maxes[2];
+ results[1] = r / maxes[2];
+ results[2] = r / maxes[2];
+
+ // Alpha may be undefined, so default it to 100%.
+ if (typeof g === 'number') {
+ results[3] = g / maxes[3];
+ } else {
+ results[3] = 1;
+ }
- // Input did not match any CSS color pattern: default to white.
- results = [1, 1, 1, 1];
- } else if ((numArgs === 1 || numArgs === 2) && typeof r === 'number') {
- // 'Grayscale' mode.
-
- /**
- * For HSB and HSL, interpret the gray level as a brightness/lightness
- * value (they are equivalent when chroma is zero). For RGB, normalize the
- * gray level according to the blue maximum.
- */
- results[0] = r / maxes[2];
- results[1] = r / maxes[2];
- results[2] = r / maxes[2];
-
- // Alpha may be undefined, so default it to 100%.
- if (typeof g === 'number') {
- results[3] = g / maxes[3];
+ // Constrain components to the range [0,1].
+ results = results.map(value => Math.max(Math.min(value, 1), 0));
} else {
- results[3] = 1;
+ throw new Error(`${arguments}is not a valid color representation.`);
}
- // Constrain components to the range [0,1].
- results = results.map(value => Math.max(Math.min(value, 1), 0));
- } else {
- throw new Error(`${arguments}is not a valid color representation.`);
+ return results;
}
-
- return results;
};
export default p5.Color;
diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js
index ecb1680326..af2d77d1ec 100644
--- a/src/core/p5.Renderer.js
+++ b/src/core/p5.Renderer.js
@@ -19,7 +19,7 @@ import * as constants from '../core/constants';
* @param {p5} [pInst] pointer to p5 instance
* @param {Boolean} [isMainCanvas] whether we're using it as main canvas
*/
-p5.Renderer = function(elt, pInst, isMainCanvas) {
+p5.Renderer = function (elt, pInst, isMainCanvas) {
p5.Element.call(this, elt, pInst);
this.canvas = elt;
this._pixelsState = pInst;
@@ -63,7 +63,7 @@ p5.Renderer.prototype = Object.create(p5.Element.prototype);
// the renderer should return a 'style' object that it wishes to
// store on the push stack.
-p5.Renderer.prototype.push = function() {
+p5.Renderer.prototype.push = function () {
return {
properties: {
_doStroke: this._doStroke,
@@ -89,7 +89,7 @@ p5.Renderer.prototype.push = function() {
// a pop() operation is in progress
// the renderer is passed the 'style' object that it returned
// from its push() method.
-p5.Renderer.prototype.pop = function(style) {
+p5.Renderer.prototype.pop = function (style) {
if (style.properties) {
// copy the style properties back into the renderer
Object.assign(this, style.properties);
@@ -99,7 +99,7 @@ p5.Renderer.prototype.pop = function(style) {
/**
* Resize our canvas element.
*/
-p5.Renderer.prototype.resize = function(w, h) {
+p5.Renderer.prototype.resize = function (w, h) {
this.width = w;
this.height = h;
this.elt.width = w * this._pInst._pixelDensity;
@@ -112,7 +112,7 @@ p5.Renderer.prototype.resize = function(w, h) {
}
};
-p5.Renderer.prototype.get = function(x, y, w, h) {
+p5.Renderer.prototype.get = function (x, y, w, h) {
const pixelsState = this._pixelsState;
const pd = pixelsState._pixelDensity;
const canvas = this.canvas;
@@ -145,7 +145,7 @@ p5.Renderer.prototype.get = function(x, y, w, h) {
return region;
};
-p5.Renderer.prototype.textLeading = function(l) {
+p5.Renderer.prototype.textLeading = function (l) {
if (typeof l === 'number') {
this._setProperty('_leadingSet', true);
this._setProperty('_textLeading', l);
@@ -155,7 +155,7 @@ p5.Renderer.prototype.textLeading = function(l) {
return this._textLeading;
};
-p5.Renderer.prototype.textSize = function(s) {
+p5.Renderer.prototype.textSize = function (s) {
if (typeof s === 'number') {
this._setProperty('_textSize', s);
if (!this._leadingSet) {
@@ -168,7 +168,7 @@ p5.Renderer.prototype.textSize = function(s) {
return this._textSize;
};
-p5.Renderer.prototype.textStyle = function(s) {
+p5.Renderer.prototype.textStyle = function (s) {
if (s) {
if (
s === constants.NORMAL ||
@@ -185,21 +185,21 @@ p5.Renderer.prototype.textStyle = function(s) {
return this._textStyle;
};
-p5.Renderer.prototype.textAscent = function() {
+p5.Renderer.prototype.textAscent = function () {
if (this._textAscent === null) {
this._updateTextMetrics();
}
return this._textAscent;
};
-p5.Renderer.prototype.textDescent = function() {
+p5.Renderer.prototype.textDescent = function () {
if (this._textDescent === null) {
this._updateTextMetrics();
}
return this._textDescent;
};
-p5.Renderer.prototype.textAlign = function(h, v) {
+p5.Renderer.prototype.textAlign = function (h, v) {
if (typeof h !== 'undefined') {
this._setProperty('_textAlign', h);
@@ -216,12 +216,12 @@ p5.Renderer.prototype.textAlign = function(h, v) {
}
};
-p5.Renderer.prototype.textWrap = function(wrapStyle) {
+p5.Renderer.prototype.textWrap = function (wrapStyle) {
this._setProperty('_textWrap', wrapStyle);
return this._textWrap;
};
-p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) {
+p5.Renderer.prototype.text = function (str, x, y, maxWidth, maxHeight) {
const p = this._pInst;
const textWrapStyle = this._textWrap;
@@ -452,18 +452,18 @@ p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) {
return p;
};
-p5.Renderer.prototype._applyDefaults = function() {
+p5.Renderer.prototype._applyDefaults = function () {
return this;
};
/**
* Helper function to check font type (system or otf)
*/
-p5.Renderer.prototype._isOpenType = function(f = this._textFont) {
+p5.Renderer.prototype._isOpenType = function (f = this._textFont) {
return typeof f === 'object' && f.font && f.font.supported;
};
-p5.Renderer.prototype._updateTextMetrics = function() {
+p5.Renderer.prototype._updateTextMetrics = function () {
if (this._isOpenType()) {
this._setProperty('_textAscent', this._textFont._textAscent());
this._setProperty('_textDescent', this._textFont._textDescent());
diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js
index a6f0ed534c..1e0875f8da 100644
--- a/src/core/p5.Renderer2D.js
+++ b/src/core/p5.Renderer2D.js
@@ -11,7 +11,7 @@ import './p5.Renderer';
const styleEmpty = 'rgba(0,0,0,0)';
// const alphaThreshold = 0.00125; // minimum visible
-p5.Renderer2D = function(elt, pInst, isMainCanvas) {
+p5.Renderer2D = function (elt, pInst, isMainCanvas) {
p5.Renderer.call(this, elt, pInst, isMainCanvas);
this.drawingContext = this.canvas.getContext('2d');
this._pInst._setProperty('drawingContext', this.drawingContext);
@@ -20,7 +20,7 @@ p5.Renderer2D = function(elt, pInst, isMainCanvas) {
p5.Renderer2D.prototype = Object.create(p5.Renderer.prototype);
-p5.Renderer2D.prototype._applyDefaults = function() {
+p5.Renderer2D.prototype._applyDefaults = function () {
this._cachedFillStyle = this._cachedStrokeStyle = undefined;
this._cachedBlendMode = constants.BLEND;
this._setFill(constants._DEFAULT_FILL);
@@ -29,7 +29,7 @@ p5.Renderer2D.prototype._applyDefaults = function() {
this.drawingContext.font = 'normal 12px sans-serif';
};
-p5.Renderer2D.prototype.resize = function(w, h) {
+p5.Renderer2D.prototype.resize = function (w, h) {
p5.Renderer.prototype.resize.call(this, w, h);
this.drawingContext.scale(
this._pInst._pixelDensity,
@@ -41,7 +41,7 @@ p5.Renderer2D.prototype.resize = function(w, h) {
// COLOR | Setting
//////////////////////////////////////////////
-p5.Renderer2D.prototype.background = function(...args) {
+p5.Renderer2D.prototype.background = function (...args) {
this.drawingContext.save();
this.resetMatrix();
@@ -82,14 +82,14 @@ p5.Renderer2D.prototype.background = function(...args) {
this.drawingContext.restore();
};
-p5.Renderer2D.prototype.clear = function() {
+p5.Renderer2D.prototype.clear = function () {
this.drawingContext.save();
this.resetMatrix();
this.drawingContext.clearRect(0, 0, this.width, this.height);
this.drawingContext.restore();
};
-p5.Renderer2D.prototype.fill = function(...args) {
+p5.Renderer2D.prototype.fill = function (...args) {
const color = this._pInst.color(...args);
this._setFill(color.toString());
@@ -99,7 +99,7 @@ p5.Renderer2D.prototype.fill = function(...args) {
}
};
-p5.Renderer2D.prototype.stroke = function(...args) {
+p5.Renderer2D.prototype.stroke = function (...args) {
const color = this._pInst.color(...args);
this._setStroke(color.toString());
@@ -109,7 +109,7 @@ p5.Renderer2D.prototype.stroke = function(...args) {
}
};
-p5.Renderer2D.prototype.erase = function(opacityFill, opacityStroke) {
+p5.Renderer2D.prototype.erase = function (opacityFill, opacityStroke) {
if (!this._isErasing) {
// cache the fill style
this._cachedFillStyle = this.drawingContext.fillStyle;
@@ -130,7 +130,7 @@ p5.Renderer2D.prototype.erase = function(opacityFill, opacityStroke) {
}
};
-p5.Renderer2D.prototype.noErase = function() {
+p5.Renderer2D.prototype.noErase = function () {
if (this._isErasing) {
this.drawingContext.fillStyle = this._cachedFillStyle;
this.drawingContext.strokeStyle = this._cachedStrokeStyle;
@@ -144,7 +144,7 @@ p5.Renderer2D.prototype.noErase = function() {
// IMAGE | Loading & Displaying
//////////////////////////////////////////////
-p5.Renderer2D.prototype.image = function(
+p5.Renderer2D.prototype.image = function (
img,
sx,
sy,
@@ -198,7 +198,7 @@ p5.Renderer2D.prototype.image = function(
}
};
-p5.Renderer2D.prototype._getTintedImageCanvas = function(img) {
+p5.Renderer2D.prototype._getTintedImageCanvas = function (img) {
if (!img.canvas) {
return img;
}
@@ -268,7 +268,7 @@ p5.Renderer2D.prototype._getTintedImageCanvas = function(img) {
// IMAGE | Pixels
//////////////////////////////////////////////
-p5.Renderer2D.prototype.blendMode = function(mode) {
+p5.Renderer2D.prototype.blendMode = function (mode) {
if (mode === constants.SUBTRACT) {
console.warn('blendMode(SUBTRACT) only works in WEBGL mode.');
} else if (
@@ -295,7 +295,7 @@ p5.Renderer2D.prototype.blendMode = function(mode) {
}
};
-p5.Renderer2D.prototype.blend = function(...args) {
+p5.Renderer2D.prototype.blend = function (...args) {
const currBlend = this.drawingContext.globalCompositeOperation;
const blendMode = args[args.length - 1];
@@ -312,7 +312,7 @@ p5.Renderer2D.prototype.blend = function(...args) {
// .get() is not overridden
// x,y are canvas-relative (pre-scaled by _pixelDensity)
-p5.Renderer2D.prototype._getPixel = function(x, y) {
+p5.Renderer2D.prototype._getPixel = function (x, y) {
let imageData, index;
imageData = this.drawingContext.getImageData(x, y, 1, 1).data;
index = 0;
@@ -324,7 +324,7 @@ p5.Renderer2D.prototype._getPixel = function(x, y) {
];
};
-p5.Renderer2D.prototype.loadPixels = function() {
+p5.Renderer2D.prototype.loadPixels = function () {
const pixelsState = this._pixelsState; // if called by p5.Image
const pd = pixelsState._pixelDensity;
@@ -337,7 +337,7 @@ p5.Renderer2D.prototype.loadPixels = function() {
pixelsState._setProperty('pixels', imageData.data);
};
-p5.Renderer2D.prototype.set = function(x, y, imgOrCol) {
+p5.Renderer2D.prototype.set = function (x, y, imgOrCol) {
// round down to get integer numbers
x = Math.floor(x);
y = Math.floor(y);
@@ -413,7 +413,7 @@ p5.Renderer2D.prototype.set = function(x, y, imgOrCol) {
}
};
-p5.Renderer2D.prototype.updatePixels = function(x, y, w, h) {
+p5.Renderer2D.prototype.updatePixels = function (x, y, w, h) {
const pixelsState = this._pixelsState;
const pd = pixelsState._pixelDensity;
if (
@@ -487,7 +487,7 @@ p5.Renderer2D.prototype._acuteArcToBezier = function _acuteArcToBezier(
*
* start <= stop < start + TWO_PI
*/
-p5.Renderer2D.prototype.arc = function(x, y, w, h, start, stop, mode) {
+p5.Renderer2D.prototype.arc = function (x, y, w, h, start, stop, mode) {
const ctx = this.drawingContext;
const rx = w / 2.0;
const ry = h / 2.0;
@@ -514,8 +514,8 @@ p5.Renderer2D.prototype.arc = function(x, y, w, h, start, stop, mode) {
}
/* eslint-disable indent */
ctx.bezierCurveTo(x + curve.bx * rx, y + curve.by * ry,
- x + curve.cx * rx, y + curve.cy * ry,
- x + curve.dx * rx, y + curve.dy * ry);
+ x + curve.cx * rx, y + curve.cy * ry,
+ x + curve.dx * rx, y + curve.dy * ry);
/* eslint-enable indent */
});
if (mode === constants.PIE || mode == null) {
@@ -534,8 +534,8 @@ p5.Renderer2D.prototype.arc = function(x, y, w, h, start, stop, mode) {
}
/* eslint-disable indent */
ctx.bezierCurveTo(x + curve.bx * rx, y + curve.by * ry,
- x + curve.cx * rx, y + curve.cy * ry,
- x + curve.dx * rx, y + curve.dy * ry);
+ x + curve.cx * rx, y + curve.cy * ry,
+ x + curve.dx * rx, y + curve.dy * ry);
/* eslint-enable indent */
});
if (mode === constants.PIE) {
@@ -549,7 +549,7 @@ p5.Renderer2D.prototype.arc = function(x, y, w, h, start, stop, mode) {
return this;
};
-p5.Renderer2D.prototype.ellipse = function(args) {
+p5.Renderer2D.prototype.ellipse = function (args) {
const ctx = this.drawingContext;
const doFill = this._doFill,
doStroke = this._doStroke;
@@ -592,7 +592,7 @@ p5.Renderer2D.prototype.ellipse = function(args) {
}
};
-p5.Renderer2D.prototype.line = function(x1, y1, x2, y2) {
+p5.Renderer2D.prototype.line = function (x1, y1, x2, y2) {
const ctx = this.drawingContext;
if (!this._doStroke) {
return this;
@@ -606,7 +606,7 @@ p5.Renderer2D.prototype.line = function(x1, y1, x2, y2) {
return this;
};
-p5.Renderer2D.prototype.point = function(x, y) {
+p5.Renderer2D.prototype.point = function (x, y) {
const ctx = this.drawingContext;
if (!this._doStroke) {
return this;
@@ -623,7 +623,7 @@ p5.Renderer2D.prototype.point = function(x, y) {
this._setFill(f);
};
-p5.Renderer2D.prototype.quad = function(x1, y1, x2, y2, x3, y3, x4, y4) {
+p5.Renderer2D.prototype.quad = function (x1, y1, x2, y2, x3, y3, x4, y4) {
const ctx = this.drawingContext;
const doFill = this._doFill,
doStroke = this._doStroke;
@@ -651,7 +651,7 @@ p5.Renderer2D.prototype.quad = function(x1, y1, x2, y2, x3, y3, x4, y4) {
return this;
};
-p5.Renderer2D.prototype.rect = function(args) {
+p5.Renderer2D.prototype.rect = function (args) {
const x = args[0];
const y = args[1];
const w = args[2];
@@ -740,7 +740,7 @@ p5.Renderer2D.prototype.rect = function(args) {
return this;
};
-p5.Renderer2D.prototype.triangle = function(args) {
+p5.Renderer2D.prototype.triangle = function (args) {
const ctx = this.drawingContext;
const doFill = this._doFill,
doStroke = this._doStroke;
@@ -772,7 +772,7 @@ p5.Renderer2D.prototype.triangle = function(args) {
}
};
-p5.Renderer2D.prototype.endShape = function(
+p5.Renderer2D.prototype.endShape = function (
mode,
vertices,
isCurve,
@@ -809,7 +809,7 @@ p5.Renderer2D.prototype.endShape = function(
];
b[2] = [
vertices[i + 1][0] +
- (s * vertices[i][0] - s * vertices[i + 2][0]) / 6,
+ (s * vertices[i][0] - s * vertices[i + 2][0]) / 6,
vertices[i + 1][1] + (s * vertices[i][1] - s * vertices[i + 2][1]) / 6
];
b[3] = [vertices[i + 1][0], vertices[i + 1][1]];
@@ -1034,7 +1034,7 @@ p5.Renderer2D.prototype.endShape = function(
// SHAPE | Attributes
//////////////////////////////////////////////
-p5.Renderer2D.prototype.strokeCap = function(cap) {
+p5.Renderer2D.prototype.strokeCap = function (cap) {
if (
cap === constants.ROUND ||
cap === constants.SQUARE ||
@@ -1045,7 +1045,7 @@ p5.Renderer2D.prototype.strokeCap = function(cap) {
return this;
};
-p5.Renderer2D.prototype.strokeJoin = function(join) {
+p5.Renderer2D.prototype.strokeJoin = function (join) {
if (
join === constants.ROUND ||
join === constants.BEVEL ||
@@ -1056,7 +1056,7 @@ p5.Renderer2D.prototype.strokeJoin = function(join) {
return this;
};
-p5.Renderer2D.prototype.strokeWeight = function(w) {
+p5.Renderer2D.prototype.strokeWeight = function (w) {
if (typeof w === 'undefined' || w === 0) {
// hack because lineWidth 0 doesn't work
this.drawingContext.lineWidth = 0.0001;
@@ -1066,28 +1066,28 @@ p5.Renderer2D.prototype.strokeWeight = function(w) {
return this;
};
-p5.Renderer2D.prototype._getFill = function() {
+p5.Renderer2D.prototype._getFill = function () {
if (!this._cachedFillStyle) {
this._cachedFillStyle = this.drawingContext.fillStyle;
}
return this._cachedFillStyle;
};
-p5.Renderer2D.prototype._setFill = function(fillStyle) {
+p5.Renderer2D.prototype._setFill = function (fillStyle) {
if (fillStyle !== this._cachedFillStyle) {
this.drawingContext.fillStyle = fillStyle;
this._cachedFillStyle = fillStyle;
}
};
-p5.Renderer2D.prototype._getStroke = function() {
+p5.Renderer2D.prototype._getStroke = function () {
if (!this._cachedStrokeStyle) {
this._cachedStrokeStyle = this.drawingContext.strokeStyle;
}
return this._cachedStrokeStyle;
};
-p5.Renderer2D.prototype._setStroke = function(strokeStyle) {
+p5.Renderer2D.prototype._setStroke = function (strokeStyle) {
if (strokeStyle !== this._cachedStrokeStyle) {
this.drawingContext.strokeStyle = strokeStyle;
this._cachedStrokeStyle = strokeStyle;
@@ -1097,7 +1097,7 @@ p5.Renderer2D.prototype._setStroke = function(strokeStyle) {
//////////////////////////////////////////////
// SHAPE | Curves
//////////////////////////////////////////////
-p5.Renderer2D.prototype.bezier = function(x1, y1, x2, y2, x3, y3, x4, y4) {
+p5.Renderer2D.prototype.bezier = function (x1, y1, x2, y2, x3, y3, x4, y4) {
this._pInst.beginShape();
this._pInst.vertex(x1, y1);
this._pInst.bezierVertex(x2, y2, x3, y3, x4, y4);
@@ -1105,7 +1105,7 @@ p5.Renderer2D.prototype.bezier = function(x1, y1, x2, y2, x3, y3, x4, y4) {
return this;
};
-p5.Renderer2D.prototype.curve = function(x1, y1, x2, y2, x3, y3, x4, y4) {
+p5.Renderer2D.prototype.curve = function (x1, y1, x2, y2, x3, y3, x4, y4) {
this._pInst.beginShape();
this._pInst.curveVertex(x1, y1);
this._pInst.curveVertex(x2, y2);
@@ -1119,7 +1119,7 @@ p5.Renderer2D.prototype.curve = function(x1, y1, x2, y2, x3, y3, x4, y4) {
// SHAPE | Vertex
//////////////////////////////////////////////
-p5.Renderer2D.prototype._doFillStrokeClose = function(closeShape) {
+p5.Renderer2D.prototype._doFillStrokeClose = function (closeShape) {
if (closeShape) {
this.drawingContext.closePath();
}
@@ -1135,11 +1135,11 @@ p5.Renderer2D.prototype._doFillStrokeClose = function(closeShape) {
// TRANSFORM
//////////////////////////////////////////////
-p5.Renderer2D.prototype.applyMatrix = function(a, b, c, d, e, f) {
+p5.Renderer2D.prototype.applyMatrix = function (a, b, c, d, e, f) {
this.drawingContext.transform(a, b, c, d, e, f);
};
-p5.Renderer2D.prototype.resetMatrix = function() {
+p5.Renderer2D.prototype.resetMatrix = function () {
this.drawingContext.setTransform(1, 0, 0, 1, 0, 0);
this.drawingContext.scale(
this._pInst._pixelDensity,
@@ -1148,16 +1148,16 @@ p5.Renderer2D.prototype.resetMatrix = function() {
return this;
};
-p5.Renderer2D.prototype.rotate = function(rad) {
+p5.Renderer2D.prototype.rotate = function (rad) {
this.drawingContext.rotate(rad);
};
-p5.Renderer2D.prototype.scale = function(x, y) {
+p5.Renderer2D.prototype.scale = function (x, y) {
this.drawingContext.scale(x, y);
return this;
};
-p5.Renderer2D.prototype.translate = function(x, y) {
+p5.Renderer2D.prototype.translate = function (x, y) {
// support passing a vector as the 1st parameter
if (x instanceof p5.Vector) {
y = x.y;
@@ -1172,7 +1172,7 @@ p5.Renderer2D.prototype.translate = function(x, y) {
//
//////////////////////////////////////////////
-p5.Renderer2D.prototype.text = function(str, x, y, maxWidth, maxHeight) {
+p5.Renderer2D.prototype.text = function (str, x, y, maxWidth, maxHeight) {
let baselineHacked;
// baselineHacked: (HACK)
@@ -1195,7 +1195,7 @@ p5.Renderer2D.prototype.text = function(str, x, y, maxWidth, maxHeight) {
return p;
};
-p5.Renderer2D.prototype._renderText = function(p, line, x, y, maxY, minY) {
+p5.Renderer2D.prototype._renderText = function (p, line, x, y, maxY, minY) {
if (y < minY || y >= maxY) {
return; // don't render lines beyond our minY/maxY bounds (see #5785)
}
@@ -1228,7 +1228,7 @@ p5.Renderer2D.prototype._renderText = function(p, line, x, y, maxY, minY) {
return p;
};
-p5.Renderer2D.prototype.textWidth = function(s) {
+p5.Renderer2D.prototype.textWidth = function (s) {
if (this._isOpenType()) {
return this._textFont._textWidth(s, this._textSize);
}
@@ -1236,7 +1236,7 @@ p5.Renderer2D.prototype.textWidth = function(s) {
return this.drawingContext.measureText(s).width;
};
-p5.Renderer2D.prototype._applyTextProperties = function() {
+p5.Renderer2D.prototype._applyTextProperties = function () {
let font;
const p = this._pInst;
@@ -1272,7 +1272,7 @@ p5.Renderer2D.prototype._applyTextProperties = function() {
// store on the push stack.
// derived renderers should call the base class' push() method
// to fetch the base style object.
-p5.Renderer2D.prototype.push = function() {
+p5.Renderer2D.prototype.push = function () {
this.drawingContext.save();
// get the base renderer style
@@ -1284,7 +1284,7 @@ p5.Renderer2D.prototype.push = function() {
// from its push() method.
// derived renderers should pass this object to their base
// class' pop method
-p5.Renderer2D.prototype.pop = function(style) {
+p5.Renderer2D.prototype.pop = function (style) {
this.drawingContext.restore();
// Re-cache the fill / stroke state
this._cachedFillStyle = this.drawingContext.fillStyle;
diff --git a/src/data/p5.TypedDict.js b/src/data/p5.TypedDict.js
index 6df8d1d01f..449887157b 100644
--- a/src/data/p5.TypedDict.js
+++ b/src/data/p5.TypedDict.js
@@ -40,7 +40,7 @@ import p5 from '../core/main';
* @return {p5.StringDict}
*/
-p5.prototype.createStringDict = function(key, value) {
+p5.prototype.createStringDict = function (key, value) {
p5._validateParameters('createStringDict', arguments);
return new p5.StringDict(key, value);
};
@@ -74,7 +74,7 @@ p5.prototype.createStringDict = function(key, value) {
* @return {p5.NumberDict}
*/
-p5.prototype.createNumberDict = function(key, value) {
+p5.prototype.createNumberDict = function (key, value) {
p5._validateParameters('createNumberDict', arguments);
return new p5.NumberDict(key, value);
};
@@ -88,309 +88,312 @@ p5.prototype.createNumberDict = function(key, value) {
* @constructor
*/
-p5.TypedDict = function(key, value) {
- if (key instanceof Object) {
- this.data = key;
- } else {
- this.data = {};
- this.data[key] = value;
+p5.TypedDict = class TypedDict {
+ constructor(key, value) {
+ if (key instanceof Object) {
+ this.data = key;
+ } else {
+ this.data = {};
+ this.data[key] = value;
+ }
+ return this;
}
- return this;
-};
-
-/**
- * Returns the number of key-value pairs currently stored in the Dictionary.
- *
- * @method size
- * @return {Integer} the number of key-value pairs in the Dictionary
- *
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createNumberDict(1, 10);
- * myDictionary.create(2, 20);
- * myDictionary.create(3, 30);
- * print(myDictionary.size()); // logs 3 to the console
- * }
- *
- */
-p5.TypedDict.prototype.size = function() {
- return Object.keys(this.data).length;
-};
-
-/**
- * Returns true if the given key exists in the Dictionary,
- * otherwise returns false.
- *
- * @method hasKey
- * @param {Number|String} key that you want to look up
- * @return {Boolean} whether that key exists in Dictionary
- *
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createStringDict('p5', 'js');
- * print(myDictionary.hasKey('p5')); // logs true to console
- * }
- *
- */
-p5.TypedDict.prototype.hasKey = function(key) {
- return this.data.hasOwnProperty(key);
-};
-
-/**
- * Returns the value stored at the given key.
- *
- * @method get
- * @param {Number|String} the key you want to access
- * @return {Number|String} the value stored at that key
- *
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createStringDict('p5', 'js');
- * let myValue = myDictionary.get('p5');
- * print(myValue === 'js'); // logs true to console
- * }
- *
- */
+ /**
+ * Returns the number of key-value pairs currently stored in the Dictionary.
+ *
+ * @method size
+ * @return {Integer} the number of key-value pairs in the Dictionary
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict(1, 10);
+ * myDictionary.create(2, 20);
+ * myDictionary.create(3, 30);
+ * print(myDictionary.size()); // logs 3 to the console
+ * }
+ *
+ */
+ size() {
+ return Object.keys(this.data).length;
+ }
-p5.TypedDict.prototype.get = function(key) {
- if (this.data.hasOwnProperty(key)) {
- return this.data[key];
- } else {
- console.log(`${key} does not exist in this Dictionary`);
+ /**
+ * Returns true if the given key exists in the Dictionary,
+ * otherwise returns false.
+ *
+ * @method hasKey
+ * @param {Number|String} key that you want to look up
+ * @return {Boolean} whether that key exists in Dictionary
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createStringDict('p5', 'js');
+ * print(myDictionary.hasKey('p5')); // logs true to console
+ * }
+ *
+ */
+
+ hasKey(key) {
+ return this.data.hasOwnProperty(key);
}
-};
-/**
- * Updates the value associated with the given key in case it already exists
- * in the Dictionary. Otherwise a new key-value pair is added.
- *
- * @method set
- * @param {Number|String} key
- * @param {Number|String} value
- *
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createStringDict('p5', 'js');
- * myDictionary.set('p5', 'JS');
- * myDictionary.print(); // logs "key: p5 - value: JS" to console
- * }
- *
- */
+ /**
+ * Returns the value stored at the given key.
+ *
+ * @method get
+ * @param {Number|String} the key you want to access
+ * @return {Number|String} the value stored at that key
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createStringDict('p5', 'js');
+ * let myValue = myDictionary.get('p5');
+ * print(myValue === 'js'); // logs true to console
+ * }
+ *
+ */
+
+ get(key) {
+ if (this.data.hasOwnProperty(key)) {
+ return this.data[key];
+ } else {
+ console.log(`${key} does not exist in this Dictionary`);
+ }
+ }
-p5.TypedDict.prototype.set = function(key, value) {
- if (this._validate(value)) {
- this.data[key] = value;
- } else {
- console.log('Those values dont work for this dictionary type.');
+ /**
+ * Updates the value associated with the given key in case it already exists
+ * in the Dictionary. Otherwise a new key-value pair is added.
+ *
+ * @method set
+ * @param {Number|String} key
+ * @param {Number|String} value
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createStringDict('p5', 'js');
+ * myDictionary.set('p5', 'JS');
+ * myDictionary.print(); // logs "key: p5 - value: JS" to console
+ * }
+ *
+ */
+
+ set(key, value) {
+ if (this._validate(value)) {
+ this.data[key] = value;
+ } else {
+ console.log('Those values dont work for this dictionary type.');
+ }
}
-};
-/**
- * private helper function to handle the user passing in objects
- * during construction or calls to create()
- */
+ /**
+ * private helper function to handle the user passing in objects
+ * during construction or calls to create()
+ */
-p5.TypedDict.prototype._addObj = function(obj) {
- for (const key in obj) {
- this.set(key, obj[key]);
+ _addObj(obj) {
+ for (const key in obj) {
+ this.set(key, obj[key]);
+ }
}
-};
-
-/**
- * Creates a new key-value pair in the Dictionary.
- *
- * @method create
- * @param {Number|String} key
- * @param {Number|String} value
- *
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createStringDict('p5', 'js');
- * myDictionary.create('happy', 'coding');
- * myDictionary.print();
- * // above logs "key: p5 - value: js, key: happy - value: coding" to console
- * }
- *
- */
-/**
- * @method create
- * @param {Object} obj key/value pair
- */
-p5.TypedDict.prototype.create = function(key, value) {
- if (key instanceof Object && typeof value === 'undefined') {
- this._addObj(key);
- } else if (typeof key !== 'undefined') {
- this.set(key, value);
- } else {
- console.log(
- 'In order to create a new Dictionary entry you must pass ' +
+ /**
+ * Creates a new key-value pair in the Dictionary.
+ *
+ * @method create
+ * @param {Number|String} key
+ * @param {Number|String} value
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createStringDict('p5', 'js');
+ * myDictionary.create('happy', 'coding');
+ * myDictionary.print();
+ * // above logs "key: p5 - value: js, key: happy - value: coding" to console
+ * }
+ *
+ */
+ /**
+ * @method create
+ * @param {Object} obj key/value pair
+ */
+
+ create(key, value) {
+ if (key instanceof Object && typeof value === 'undefined') {
+ this._addObj(key);
+ } else if (typeof key !== 'undefined') {
+ this.set(key, value);
+ } else {
+ console.log(
+ 'In order to create a new Dictionary entry you must pass ' +
'an object or a key, value pair'
- );
+ );
+ }
}
-};
-
-/**
- * Removes all previously stored key-value pairs from the Dictionary.
- *
- * @method clear
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createStringDict('p5', 'js');
- * print(myDictionary.hasKey('p5')); // prints 'true'
- * myDictionary.clear();
- * print(myDictionary.hasKey('p5')); // prints 'false'
- * }
- *
- *
- */
-
-p5.TypedDict.prototype.clear = function() {
- this.data = {};
-};
-
-/**
- * Removes the key-value pair stored at the given key from the Dictionary.
- *
- * @method remove
- * @param {Number|String} key for the pair to remove
- *
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createStringDict('p5', 'js');
- * myDictionary.create('happy', 'coding');
- * myDictionary.print();
- * // above logs "key: p5 - value: js, key: happy - value: coding" to console
- * myDictionary.remove('p5');
- * myDictionary.print();
- * // above logs "key: happy value: coding" to console
- * }
- *
- */
-p5.TypedDict.prototype.remove = function(key) {
- if (this.data.hasOwnProperty(key)) {
- delete this.data[key];
- } else {
- throw new Error(`${key} does not exist in this Dictionary`);
+ /**
+ * Removes all previously stored key-value pairs from the Dictionary.
+ *
+ * @method clear
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createStringDict('p5', 'js');
+ * print(myDictionary.hasKey('p5')); // prints 'true'
+ * myDictionary.clear();
+ * print(myDictionary.hasKey('p5')); // prints 'false'
+ * }
+ *
+ *
+ */
+
+ clear() {
+ this.data = {};
}
-};
-/**
- * Logs the set of items currently stored in the Dictionary to the console.
- *
- * @method print
- *
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createStringDict('p5', 'js');
- * myDictionary.create('happy', 'coding');
- * myDictionary.print();
- * // above logs "key: p5 - value: js, key: happy - value: coding" to console
- * }
- *
- *
- */
+ /**
+ * Removes the key-value pair stored at the given key from the Dictionary.
+ *
+ * @method remove
+ * @param {Number|String} key for the pair to remove
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createStringDict('p5', 'js');
+ * myDictionary.create('happy', 'coding');
+ * myDictionary.print();
+ * // above logs "key: p5 - value: js, key: happy - value: coding" to console
+ * myDictionary.remove('p5');
+ * myDictionary.print();
+ * // above logs "key: happy value: coding" to console
+ * }
+ *
+ */
+
+ remove(key) {
+ if (this.data.hasOwnProperty(key)) {
+ delete this.data[key];
+ } else {
+ throw new Error(`${key} does not exist in this Dictionary`);
+ }
+ }
-p5.TypedDict.prototype.print = function() {
- for (const item in this.data) {
- console.log(`key:${item} value:${this.data[item]}`);
+ /**
+ * Logs the set of items currently stored in the Dictionary to the console.
+ *
+ * @method print
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createStringDict('p5', 'js');
+ * myDictionary.create('happy', 'coding');
+ * myDictionary.print();
+ * // above logs "key: p5 - value: js, key: happy - value: coding" to console
+ * }
+ *
+ *
+ */
+
+ print() {
+ for (const item in this.data) {
+ console.log(`key:${item} value:${this.data[item]}`);
+ }
}
-};
-/**
- * Converts the Dictionary into a CSV file for local download.
- *
- * @method saveTable
- * @example
- *
- *
- * function setup() {
- * createCanvas(100, 100);
- * background(200);
- * text('click here to save', 10, 10, 70, 80);
- * }
- *
- * function mousePressed() {
- * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
- * createStringDict({
- * john: 1940,
- * paul: 1942,
- * george: 1943,
- * ringo: 1940
- * }).saveTable('beatles');
- * }
- * }
- *
- *
- */
+ /**
+ * Converts the Dictionary into a CSV file for local download.
+ *
+ * @method saveTable
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * background(200);
+ * text('click here to save', 10, 10, 70, 80);
+ * }
+ *
+ * function mousePressed() {
+ * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
+ * createStringDict({
+ * john: 1940,
+ * paul: 1942,
+ * george: 1943,
+ * ringo: 1940
+ * }).saveTable('beatles');
+ * }
+ * }
+ *
+ *
+ */
+
+ saveTable(filename) {
+ let output = '';
-p5.TypedDict.prototype.saveTable = function(filename) {
- let output = '';
+ for (const key in this.data) {
+ output += `${key},${this.data[key]}\n`;
+ }
- for (const key in this.data) {
- output += `${key},${this.data[key]}\n`;
+ const blob = new Blob([output], { type: 'text/csv' });
+ p5.prototype.downloadFile(blob, filename || 'mycsv', 'csv');
}
- const blob = new Blob([output], { type: 'text/csv' });
- p5.prototype.downloadFile(blob, filename || 'mycsv', 'csv');
-};
-
-/**
- * Converts the Dictionary into a JSON file for local download.
- *
- * @method saveJSON
- * @example
- *
- *
- * function setup() {
- * createCanvas(100, 100);
- * background(200);
- * text('click here to save', 10, 10, 70, 80);
- * }
- *
- * function mousePressed() {
- * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
- * createStringDict({
- * john: 1940,
- * paul: 1942,
- * george: 1943,
- * ringo: 1940
- * }).saveJSON('beatles');
- * }
- * }
- *
- *
- */
+ /**
+ * Converts the Dictionary into a JSON file for local download.
+ *
+ * @method saveJSON
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * background(200);
+ * text('click here to save', 10, 10, 70, 80);
+ * }
+ *
+ * function mousePressed() {
+ * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
+ * createStringDict({
+ * john: 1940,
+ * paul: 1942,
+ * george: 1943,
+ * ringo: 1940
+ * }).saveJSON('beatles');
+ * }
+ * }
+ *
+ *
+ */
+
+ saveJSON(filename, opt) {
+ p5.prototype.saveJSON(this.data, filename, opt);
+ }
-p5.TypedDict.prototype.saveJSON = function(filename, opt) {
- p5.prototype.saveJSON(this.data, filename, opt);
+ /**
+ * private helper function to ensure that the user passed in valid
+ * values for the Dictionary type
+ */
+ _validate(value) {
+ return true;
+ }
};
-/**
- * private helper function to ensure that the user passed in valid
- * values for the Dictionary type
- */
-
-p5.TypedDict.prototype._validate = value => true;
-
/**
*
* A simple Dictionary class for Strings.
@@ -399,13 +402,15 @@ p5.TypedDict.prototype._validate = value => true;
* @extends p5.TypedDict
*/
-p5.StringDict = function(...args) {
- p5.TypedDict.apply(this, args);
-};
-
-p5.StringDict.prototype = Object.create(p5.TypedDict.prototype);
+p5.StringDict = class StringDict extends p5.TypedDict {
+ constructor(...args) {
+ super(...args);
+ }
-p5.StringDict.prototype._validate = value => typeof value === 'string';
+ _validate(value) {
+ return typeof value === 'string';
+ }
+};
/**
*
@@ -416,247 +421,250 @@ p5.StringDict.prototype._validate = value => typeof value === 'string';
* @extends p5.TypedDict
*/
-p5.NumberDict = function(...args) {
- p5.TypedDict.apply(this, args);
-};
-
-p5.NumberDict.prototype = Object.create(p5.TypedDict.prototype);
-
-/**
- * private helper function to ensure that the user passed in valid
- * values for the Dictionary type
- */
-
-p5.NumberDict.prototype._validate = value => typeof value === 'number';
-
-/**
- * Add the given number to the value currently stored at the given key.
- * The sum then replaces the value previously stored in the Dictionary.
- *
- * @method add
- * @param {Number} Key for the value you wish to add to
- * @param {Number} Number to add to the value
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createNumberDict(2, 5);
- * myDictionary.add(2, 2);
- * print(myDictionary.get(2)); // logs 7 to console.
- * }
- *
- *
- */
-
-p5.NumberDict.prototype.add = function(key, amount) {
- if (this.data.hasOwnProperty(key)) {
- this.data[key] += amount;
- } else {
- console.log(`The key - ${key} does not exist in this dictionary.`);
+p5.NumberDict = class NumberDict extends p5.TypedDict {
+ constructor(...args) {
+ super(...args);
}
-};
-/**
- * Subtract the given number from the value currently stored at the given key.
- * The difference then replaces the value previously stored in the Dictionary.
- *
- * @method sub
- * @param {Number} Key for the value you wish to subtract from
- * @param {Number} Number to subtract from the value
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createNumberDict(2, 5);
- * myDictionary.sub(2, 2);
- * print(myDictionary.get(2)); // logs 3 to console.
- * }
- *
- *
- */
-p5.NumberDict.prototype.sub = function(key, amount) {
- this.add(key, -amount);
-};
+ /**
+ * private helper function to ensure that the user passed in valid
+ * values for the Dictionary type
+ */
-/**
- * Multiply the given number with the value currently stored at the given key.
- * The product then replaces the value previously stored in the Dictionary.
- *
- * @method mult
- * @param {Number} Key for value you wish to multiply
- * @param {Number} Amount to multiply the value by
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createNumberDict(2, 4);
- * myDictionary.mult(2, 2);
- * print(myDictionary.get(2)); // logs 8 to console.
- * }
- *
- *
- */
+ _validate(value) {
+ return typeof value === 'number';
+ }
-p5.NumberDict.prototype.mult = function(key, amount) {
- if (this.data.hasOwnProperty(key)) {
- this.data[key] *= amount;
- } else {
- console.log(`The key - ${key} does not exist in this dictionary.`);
+ /**
+ * Add the given number to the value currently stored at the given key.
+ * The sum then replaces the value previously stored in the Dictionary.
+ *
+ * @method add
+ * @param {Number} Key for the value you wish to add to
+ * @param {Number} Number to add to the value
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict(2, 5);
+ * myDictionary.add(2, 2);
+ * print(myDictionary.get(2)); // logs 7 to console.
+ * }
+ *
+ *
+ */
+
+ add(key, amount) {
+ if (this.data.hasOwnProperty(key)) {
+ this.data[key] += amount;
+ } else {
+ console.log(`The key - ${key} does not exist in this dictionary.`);
+ }
}
-};
-/**
- * Divide the given number with the value currently stored at the given key.
- * The quotient then replaces the value previously stored in the Dictionary.
- *
- * @method div
- * @param {Number} Key for value you wish to divide
- * @param {Number} Amount to divide the value by
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createNumberDict(2, 8);
- * myDictionary.div(2, 2);
- * print(myDictionary.get(2)); // logs 4 to console.
- * }
- *
- *
- */
+ /**
+ * Subtract the given number from the value currently stored at the given key.
+ * The difference then replaces the value previously stored in the Dictionary.
+ *
+ * @method sub
+ * @param {Number} Key for the value you wish to subtract from
+ * @param {Number} Number to subtract from the value
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict(2, 5);
+ * myDictionary.sub(2, 2);
+ * print(myDictionary.get(2)); // logs 3 to console.
+ * }
+ *
+ *
+ */
+
+ sub(key, amount) {
+ this.add(key, -amount);
+ }
-p5.NumberDict.prototype.div = function(key, amount) {
- if (this.data.hasOwnProperty(key)) {
- this.data[key] /= amount;
- } else {
- console.log(`The key - ${key} does not exist in this dictionary.`);
+ /**
+ * Multiply the given number with the value currently stored at the given key.
+ * The product then replaces the value previously stored in the Dictionary.
+ *
+ * @method mult
+ * @param {Number} Key for value you wish to multiply
+ * @param {Number} Amount to multiply the value by
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict(2, 4);
+ * myDictionary.mult(2, 2);
+ * print(myDictionary.get(2)); // logs 8 to console.
+ * }
+ *
+ *
+ */
+
+ mult(key, amount) {
+ if (this.data.hasOwnProperty(key)) {
+ this.data[key] *= amount;
+ } else {
+ console.log(`The key - ${key} does not exist in this dictionary.`);
+ }
}
-};
-/**
- * private helper function for finding lowest or highest value
- * the argument 'flip' is used to flip the comparison arrow
- * from 'less than' to 'greater than'
- */
+ /**
+ * Divide the given number with the value currently stored at the given key.
+ * The quotient then replaces the value previously stored in the Dictionary.
+ *
+ * @method div
+ * @param {Number} Key for value you wish to divide
+ * @param {Number} Amount to divide the value by
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict(2, 8);
+ * myDictionary.div(2, 2);
+ * print(myDictionary.get(2)); // logs 4 to console.
+ * }
+ *
+ *
+ */
+
+ div(key, amount) {
+ if (this.data.hasOwnProperty(key)) {
+ this.data[key] /= amount;
+ } else {
+ console.log(`The key - ${key} does not exist in this dictionary.`);
+ }
+ }
-p5.NumberDict.prototype._valueTest = function(flip) {
- if (Object.keys(this.data).length === 0) {
- throw new Error(
- 'Unable to search for a minimum or maximum value on an empty NumberDict'
- );
- } else if (Object.keys(this.data).length === 1) {
- return this.data[Object.keys(this.data)[0]];
- } else {
- let result = this.data[Object.keys(this.data)[0]];
- for (const key in this.data) {
- if (this.data[key] * flip < result * flip) {
- result = this.data[key];
+ /**
+ * private helper function for finding lowest or highest value
+ * the argument 'flip' is used to flip the comparison arrow
+ * from 'less than' to 'greater than'
+ */
+
+ _valueTest(flip) {
+ if (Object.keys(this.data).length === 0) {
+ throw new Error(
+ 'Unable to search for a minimum or maximum value on an empty NumberDict'
+ );
+ } else if (Object.keys(this.data).length === 1) {
+ return this.data[Object.keys(this.data)[0]];
+ } else {
+ let result = this.data[Object.keys(this.data)[0]];
+ for (const key in this.data) {
+ if (this.data[key] * flip < result * flip) {
+ result = this.data[key];
+ }
}
+ return result;
}
- return result;
}
-};
-
-/**
- * Return the lowest number currently stored in the Dictionary.
- *
- * @method minValue
- * @return {Number}
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createNumberDict({ 2: -10, 4: 0.65, 1.2: 3 });
- * let lowestValue = myDictionary.minValue(); // value is -10
- * print(lowestValue);
- * }
- *
- */
-
-p5.NumberDict.prototype.minValue = function() {
- return this._valueTest(1);
-};
-
-/**
- * Return the highest number currently stored in the Dictionary.
- *
- * @method maxValue
- * @return {Number}
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createNumberDict({ 2: -10, 4: 0.65, 1.2: 3 });
- * let highestValue = myDictionary.maxValue(); // value is 3
- * print(highestValue);
- * }
- *
- */
-p5.NumberDict.prototype.maxValue = function() {
- return this._valueTest(-1);
-};
+ /**
+ * Return the lowest number currently stored in the Dictionary.
+ *
+ * @method minValue
+ * @return {Number}
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict({ 2: -10, 4: 0.65, 1.2: 3 });
+ * let lowestValue = myDictionary.minValue(); // value is -10
+ * print(lowestValue);
+ * }
+ *
+ */
+
+ minValue() {
+ return this._valueTest(1);
+ }
-/**
- * private helper function for finding lowest or highest key
- * the argument 'flip' is used to flip the comparison arrow
- * from 'less than' to 'greater than'
- */
+ /**
+ * Return the highest number currently stored in the Dictionary.
+ *
+ * @method maxValue
+ * @return {Number}
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict({ 2: -10, 4: 0.65, 1.2: 3 });
+ * let highestValue = myDictionary.maxValue(); // value is 3
+ * print(highestValue);
+ * }
+ *
+ */
+
+ maxValue() {
+ return this._valueTest(-1);
+ }
-p5.NumberDict.prototype._keyTest = function(flip) {
- if (Object.keys(this.data).length === 0) {
- throw new Error('Unable to use minValue on an empty NumberDict');
- } else if (Object.keys(this.data).length === 1) {
- return Object.keys(this.data)[0];
- } else {
- let result = Object.keys(this.data)[0];
- for (let i = 1; i < Object.keys(this.data).length; i++) {
- if (Object.keys(this.data)[i] * flip < result * flip) {
- result = Object.keys(this.data)[i];
+ /**
+ * private helper function for finding lowest or highest key
+ * the argument 'flip' is used to flip the comparison arrow
+ * from 'less than' to 'greater than'
+ */
+
+ _keyTest(flip) {
+ if (Object.keys(this.data).length === 0) {
+ throw new Error('Unable to use minValue on an empty NumberDict');
+ } else if (Object.keys(this.data).length === 1) {
+ return Object.keys(this.data)[0];
+ } else {
+ let result = Object.keys(this.data)[0];
+ for (let i = 1; i < Object.keys(this.data).length; i++) {
+ if (Object.keys(this.data)[i] * flip < result * flip) {
+ result = Object.keys(this.data)[i];
+ }
}
+ return result;
}
- return result;
}
-};
-
-/**
- * Return the lowest key currently used in the Dictionary.
- *
- * @method minKey
- * @return {Number}
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createNumberDict({ 2: 4, 4: 6, 1.2: 3 });
- * let lowestKey = myDictionary.minKey(); // value is 1.2
- * print(lowestKey);
- * }
- *
- */
-p5.NumberDict.prototype.minKey = function() {
- return this._keyTest(1);
-};
-
-/**
- * Return the highest key currently used in the Dictionary.
- *
- * @method maxKey
- * @return {Number}
- * @example
- *
- *
- * function setup() {
- * let myDictionary = createNumberDict({ 2: 4, 4: 6, 1.2: 3 });
- * let highestKey = myDictionary.maxKey(); // value is 4
- * print(highestKey);
- * }
- *
- */
+ /**
+ * Return the lowest key currently used in the Dictionary.
+ *
+ * @method minKey
+ * @return {Number}
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict({ 2: 4, 4: 6, 1.2: 3 });
+ * let lowestKey = myDictionary.minKey(); // value is 1.2
+ * print(lowestKey);
+ * }
+ *
+ */
+
+ minKey() {
+ return this._keyTest(1);
+ }
-p5.NumberDict.prototype.maxKey = function() {
- return this._keyTest(-1);
+ /**
+ * Return the highest key currently used in the Dictionary.
+ *
+ * @method maxKey
+ * @return {Number}
+ * @example
+ *
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict({ 2: 4, 4: 6, 1.2: 3 });
+ * let highestKey = myDictionary.maxKey(); // value is 4
+ * print(highestKey);
+ * }
+ *
+ */
+
+ maxKey() {
+ return this._keyTest(-1);
+ }
};
export default p5.TypedDict;
diff --git a/src/dom/dom.js b/src/dom/dom.js
index 10c8f0cc59..9d28420c9f 100644
--- a/src/dom/dom.js
+++ b/src/dom/dom.js
@@ -54,7 +54,7 @@ import p5 from '../core/main';
* [a, b, c, d, e]; // unused
*
*/
-p5.prototype.select = function(e, p) {
+p5.prototype.select = function (e, p) {
p5._validateParameters('select', arguments);
const container = this._getContainer(p);
const res = container.querySelector(e);
@@ -110,7 +110,7 @@ p5.prototype.select = function(e, p) {
* console.log(a);
*
*/
-p5.prototype.selectAll = function(e, p) {
+p5.prototype.selectAll = function (e, p) {
p5._validateParameters('selectAll', arguments);
const arr = [];
const container = this._getContainer(p);
@@ -127,7 +127,7 @@ p5.prototype.selectAll = function(e, p) {
/**
* Helper function for select and selectAll
*/
-p5.prototype._getContainer = function(p) {
+p5.prototype._getContainer = function (p) {
let container = document;
if (typeof p === 'string') {
container = document.querySelector(p) || document;
@@ -142,11 +142,11 @@ p5.prototype._getContainer = function(p) {
/**
* Helper function for getElement and getElements.
*/
-p5.prototype._wrapElement = function(elt) {
+p5.prototype._wrapElement = function (elt) {
const children = Array.prototype.slice.call(elt.children);
if (elt.tagName === 'INPUT' && elt.type === 'checkbox') {
let converted = new p5.Element(elt, this);
- converted.checked = function() {
+ converted.checked = function () {
if (arguments.length === 0) {
return this.elt.checked;
} else if (arguments[0]) {
@@ -163,7 +163,7 @@ p5.prototype._wrapElement = function(elt) {
return this.createSelect(new p5.Element(elt, this));
} else if (
children.length > 0 &&
- children.every(function(c) {
+ children.every(function (c) {
return c.tagName === 'INPUT' || c.tagName === 'LABEL';
})
) {
@@ -193,7 +193,7 @@ p5.prototype._wrapElement = function(elt) {
* }
*
*/
-p5.prototype.removeElements = function(e) {
+p5.prototype.removeElements = function (e) {
p5._validateParameters('removeElements', arguments);
// el.remove splices from this._elements, so don't mix iteration with it
const isNotCanvasElement = el => !(el.elt instanceof HTMLCanvasElement);
@@ -263,7 +263,7 @@ p5.prototype.removeElements = function(e) {
* @alt
* dropdown: pear, kiwi, grape. When selected text "it's a" + selection shown.
*/
-p5.Element.prototype.changed = function(fxn) {
+p5.Element.prototype.changed = function (fxn) {
p5.Element._adjustListener('change', fxn, this);
return this;
};
@@ -301,7 +301,7 @@ p5.Element.prototype.changed = function(fxn) {
* @alt
* no display.
*/
-p5.Element.prototype.input = function(fxn) {
+p5.Element.prototype.input = function (fxn) {
p5.Element._adjustListener('input', fxn, this);
return this;
};
@@ -332,7 +332,7 @@ function addElement(elt, pInst, media) {
* div.position(10, 0);
*
*/
-p5.prototype.createDiv = function(html = '') {
+p5.prototype.createDiv = function (html = '') {
let elt = document.createElement('div');
elt.innerHTML = html;
return addElement(elt, this);
@@ -352,7 +352,7 @@ p5.prototype.createDiv = function(html = '') {
* p.position(10, 0);
*
*/
-p5.prototype.createP = function(html = '') {
+p5.prototype.createP = function (html = '') {
let elt = document.createElement('p');
elt.innerHTML = html;
return addElement(elt, this);
@@ -370,7 +370,7 @@ p5.prototype.createP = function(html = '') {
* span.position(0, 0);
*
*/
-p5.prototype.createSpan = function(html = '') {
+p5.prototype.createSpan = function (html = '') {
let elt = document.createElement('span');
elt.innerHTML = html;
return addElement(elt, this);
@@ -401,7 +401,7 @@ p5.prototype.createSpan = function(html = '') {
* @param {Function} [successCallback] callback to be called once image data is loaded with the p5.Element as argument
* @return {p5.Element} pointer to p5.Element holding created node
*/
-p5.prototype.createImg = function() {
+p5.prototype.createImg = function () {
p5._validateParameters('createImg', arguments);
const elt = document.createElement('img');
const args = arguments;
@@ -414,7 +414,7 @@ p5.prototype.createImg = function() {
}
elt.src = args[0];
self = addElement(elt, this);
- elt.addEventListener('load', function() {
+ elt.addEventListener('load', function () {
self.width = elt.offsetWidth || elt.width;
self.height = elt.offsetHeight || elt.height;
const last = args[args.length - 1];
@@ -438,7 +438,7 @@ p5.prototype.createImg = function() {
* a.position(0, 0);
*
*/
-p5.prototype.createA = function(href, html, target) {
+p5.prototype.createA = function (href, html, target) {
p5._validateParameters('createA', arguments);
const elt = document.createElement('a');
elt.href = href;
@@ -489,7 +489,7 @@ p5.prototype.createA = function(href, html, target) {
* }
*
*/
-p5.prototype.createSlider = function(min, max, value, step) {
+p5.prototype.createSlider = function (min, max, value, step) {
p5._validateParameters('createSlider', arguments);
const elt = document.createElement('input');
elt.type = 'range';
@@ -530,7 +530,7 @@ p5.prototype.createSlider = function(min, max, value, step) {
* }
*
*/
-p5.prototype.createButton = function(label, value) {
+p5.prototype.createButton = function (label, value) {
p5._validateParameters('createButton', arguments);
const elt = document.createElement('button');
elt.innerHTML = label;
@@ -565,7 +565,7 @@ p5.prototype.createButton = function(label, value) {
* }
*
*/
-p5.prototype.createCheckbox = function() {
+p5.prototype.createCheckbox = function () {
p5._validateParameters('createCheckbox', arguments);
// Create a container element
@@ -585,7 +585,7 @@ p5.prototype.createCheckbox = function() {
//checkbox must be wrapped in p5.Element before label so that label appears after
const self = addElement(elt, this);
- self.checked = function() {
+ self.checked = function () {
const cb = self.elt.firstElementChild.getElementsByTagName('input')[0];
if (cb) {
if (arguments.length === 0) {
@@ -599,7 +599,7 @@ p5.prototype.createCheckbox = function() {
return self;
};
- this.value = function(val) {
+ this.value = function (val) {
self.value = val;
return this;
};
@@ -697,7 +697,7 @@ p5.prototype.createCheckbox = function() {
* @return {p5.Element}
*/
-p5.prototype.createSelect = function() {
+p5.prototype.createSelect = function () {
p5._validateParameters('createSelect', arguments);
let self;
let arg = arguments[0];
@@ -716,7 +716,7 @@ p5.prototype.createSelect = function() {
self = addElement(elt, this);
this.elt = elt;
}
- self.option = function(name, value) {
+ self.option = function (name, value) {
let index;
// if no name is passed, return
@@ -749,7 +749,7 @@ p5.prototype.createSelect = function() {
}
};
- self.selected = function(value) {
+ self.selected = function (value) {
// Update selected status of option
if (value !== undefined) {
for (let i = 0; i < this.elt.length; i += 1) {
@@ -771,7 +771,7 @@ p5.prototype.createSelect = function() {
}
};
- self.disable = function(value) {
+ self.disable = function (value) {
if (typeof value === 'string') {
for (let i = 0; i < this.elt.length; i++) {
if (this.elt[i].value.toString() === value) {
@@ -785,7 +785,7 @@ p5.prototype.createSelect = function() {
return this;
};
- self.enable = function(value) {
+ self.enable = function (value) {
if (typeof value === 'string') {
for (let i = 0; i < this.elt.length; i++) {
if (this.elt[i].value.toString() === value) {
@@ -877,7 +877,7 @@ p5.prototype.createSelect = function() {
* @method createRadio
* @return {p5.Element} pointer to p5.Element holding created node
*/
-p5.prototype.createRadio = function() {
+p5.prototype.createRadio = function () {
// Creates a div, adds each option as an individual input inside it.
// If already given with a containerEl, will search for all input[radio]
// it, create a p5.Element out of it, add options to it and return the p5.Element.
@@ -916,7 +916,7 @@ p5.prototype.createRadio = function() {
const isLabelElement = el => el instanceof HTMLLabelElement;
const isSpanElement = el => el instanceof HTMLSpanElement;
- self._getOptionsArray = function() {
+ self._getOptionsArray = function () {
return Array.from(this.elt.children)
.filter(
el =>
@@ -926,7 +926,7 @@ p5.prototype.createRadio = function() {
.map(el => (isRadioInput(el) ? el : el.firstElementChild));
};
- self.option = function(value, label) {
+ self.option = function (value, label) {
// return an option with this value, create if not exists.
let optionEl;
for (const option of self._getOptionsArray()) {
@@ -972,7 +972,7 @@ p5.prototype.createRadio = function() {
return optionEl;
};
- self.remove = function(value) {
+ self.remove = function (value) {
for (const optionEl of self._getOptionsArray()) {
if (optionEl.value === value) {
if (isLabelElement(optionEl.parentElement)) {
@@ -987,7 +987,7 @@ p5.prototype.createRadio = function() {
}
};
- self.value = function() {
+ self.value = function () {
let result = '';
for (const option of self._getOptionsArray()) {
if (option.checked) {
@@ -998,7 +998,7 @@ p5.prototype.createRadio = function() {
return result;
};
- self.selected = function(value) {
+ self.selected = function (value) {
let result = null;
if (value === undefined) {
for (const option of self._getOptionsArray()) {
@@ -1026,7 +1026,7 @@ p5.prototype.createRadio = function() {
return result;
};
- self.disable = function(shouldDisable = true) {
+ self.disable = function (shouldDisable = true) {
for (const radioInput of self._getOptionsArray()) {
radioInput.setAttribute('disabled', shouldDisable);
}
@@ -1088,7 +1088,7 @@ p5.prototype.createRadio = function() {
* }
*
*/
-p5.prototype.createColorPicker = function(value) {
+p5.prototype.createColorPicker = function (value) {
p5._validateParameters('createColorPicker', arguments);
const elt = document.createElement('input');
let self;
@@ -1110,7 +1110,7 @@ p5.prototype.createColorPicker = function(value) {
}
self = addElement(elt, this);
// Method to return a p5.Color object for the given color.
- self.color = function() {
+ self.color = function () {
if (value) {
if (value.mode) {
p5.prototype._colorMode = value.mode;
@@ -1154,7 +1154,7 @@ p5.prototype.createColorPicker = function(value) {
* @param {String} [value]
* @return {p5.Element}
*/
-p5.prototype.createInput = function(value = '', type = 'text') {
+p5.prototype.createInput = function (value = '', type = 'text') {
p5._validateParameters('createInput', arguments);
let elt = document.createElement('input');
elt.setAttribute('value', value);
@@ -1198,10 +1198,10 @@ p5.prototype.createInput = function(value = '', type = 'text') {
* }
*
*/
-p5.prototype.createFileInput = function(callback, multiple = false) {
+p5.prototype.createFileInput = function (callback, multiple = false) {
p5._validateParameters('createFileInput', arguments);
- const handleFileSelect = function(event) {
+ const handleFileSelect = function (event) {
for (const file of event.target.files) {
p5.File._load(file, callback);
}
@@ -1310,7 +1310,7 @@ function createMedia(pInst, type, src, callback) {
* }
*
*/
-p5.prototype.createVideo = function(src, callback) {
+p5.prototype.createVideo = function (src, callback) {
p5._validateParameters('createVideo', arguments);
return createMedia(this, 'video', src, callback);
};
@@ -1349,7 +1349,7 @@ p5.prototype.createVideo = function(src, callback) {
* }
*
*/
-p5.prototype.createAudio = function(src, callback) {
+p5.prototype.createAudio = function (src, callback) {
p5._validateParameters('createAudio', arguments);
return createMedia(this, 'audio', src, callback);
};
@@ -1370,7 +1370,7 @@ if (navigator.mediaDevices === undefined) {
// with getUserMedia as it would overwrite existing properties.
// Here, we will just add the getUserMedia property if it's missing.
if (navigator.mediaDevices.getUserMedia === undefined) {
- navigator.mediaDevices.getUserMedia = function(constraints) {
+ navigator.mediaDevices.getUserMedia = function (constraints) {
// First get ahold of the legacy getUserMedia, if present
const getUserMedia =
navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
@@ -1384,7 +1384,7 @@ if (navigator.mediaDevices.getUserMedia === undefined) {
}
// Otherwise, wrap the call to the old navigator.getUserMedia with a Promise
- return new Promise(function(resolve, reject) {
+ return new Promise(function (resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
};
@@ -1472,7 +1472,7 @@ if (navigator.mediaDevices.getUserMedia === undefined) {
*
*
*/
-p5.prototype.createCapture = function() {
+p5.prototype.createCapture = function () {
p5._validateParameters('createCapture', arguments);
// return if getUserMedia is not supported by browser
@@ -1496,7 +1496,7 @@ p5.prototype.createCapture = function() {
// required to work in iOS 11 & up:
domElement.setAttribute('playsinline', '');
- navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
+ navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
try {
if ('srcObject' in domElement) {
domElement.srcObject = stream;
@@ -1511,7 +1511,7 @@ p5.prototype.createCapture = function() {
const videoEl = addElement(domElement, this, true);
videoEl.loadedmetadata = false;
// set width and height onload metadata
- domElement.addEventListener('loadedmetadata', function() {
+ domElement.addEventListener('loadedmetadata', function () {
domElement.play();
if (domElement.width) {
videoEl.width = domElement.width;
@@ -1541,7 +1541,7 @@ p5.prototype.createCapture = function() {
* h5.position(0, 0);
*
*/
-p5.prototype.createElement = function(tag, content) {
+p5.prototype.createElement = function (tag, content) {
p5._validateParameters('createElement', arguments);
const elt = document.createElement(tag);
if (typeof content !== 'undefined') {
@@ -1567,7 +1567,7 @@ p5.prototype.createElement = function(tag, content) {
* div.addClass('myClass');
*
*/
-p5.Element.prototype.addClass = function(c) {
+p5.Element.prototype.addClass = function (c) {
if (this.elt.className) {
if (!this.hasClass(c)) {
this.elt.className = this.elt.className + ' ' + c;
@@ -1603,7 +1603,7 @@ p5.Element.prototype.addClass = function(c) {
* }
*
*/
-p5.Element.prototype.removeClass = function(c) {
+p5.Element.prototype.removeClass = function (c) {
// Note: Removing a class that does not exist does NOT throw an error in classList.remove method
this.elt.classList.remove(c);
return this;
@@ -1634,7 +1634,7 @@ p5.Element.prototype.removeClass = function(c) {
* }
*
*/
-p5.Element.prototype.hasClass = function(c) {
+p5.Element.prototype.hasClass = function (c) {
return this.elt.classList.contains(c);
};
@@ -1659,7 +1659,7 @@ p5.Element.prototype.hasClass = function(c) {
* }
*
*/
-p5.Element.prototype.toggleClass = function(c) {
+p5.Element.prototype.toggleClass = function (c) {
// classList also has a toggle() method, but we cannot use that yet as support is unclear.
// See https://github.com/processing/p5.js/issues/3631
// this.elt.classList.toggle(c);
@@ -1705,7 +1705,7 @@ p5.Element.prototype.toggleClass = function(c) {
* to add to the current element
* @chainable
*/
-p5.Element.prototype.child = function(childNode) {
+p5.Element.prototype.child = function (childNode) {
if (typeof childNode === 'undefined') {
return this.elt.childNodes;
}
@@ -1743,7 +1743,7 @@ p5.Element.prototype.child = function(childNode) {
* }
*
*/
-p5.Element.prototype.center = function(align) {
+p5.Element.prototype.center = function (align) {
const style = this.elt.style.display;
const hidden = this.elt.style.display === 'none';
const parentHidden = this.parent().style.display === 'none';
@@ -1802,7 +1802,7 @@ p5.Element.prototype.center = function(align) {
* @param {boolean} [append] whether to append HTML to existing
* @chainable
*/
-p5.Element.prototype.html = function() {
+p5.Element.prototype.html = function () {
if (arguments.length === 0) {
return this.elt.innerHTML;
} else if (arguments[1]) {
@@ -1854,7 +1854,7 @@ p5.Element.prototype.html = function() {
* @param {String} [positionType] it can be static, fixed, relative, sticky, initial or inherit (optional)
* @chainable
*/
-p5.Element.prototype.position = function() {
+p5.Element.prototype.position = function () {
if (arguments.length === 0) {
return { x: this.elt.offsetLeft, y: this.elt.offsetTop };
} else {
@@ -1879,7 +1879,7 @@ p5.Element.prototype.position = function() {
};
/* Helper method called by p5.Element.style() */
-p5.Element.prototype._translate = function() {
+p5.Element.prototype._translate = function () {
this.elt.style.position = 'absolute';
// save out initial non-translate transform styling
let transform = '';
@@ -1911,7 +1911,7 @@ p5.Element.prototype._translate = function() {
};
/* Helper method called by p5.Element.style() */
-p5.Element.prototype._rotate = function() {
+p5.Element.prototype._rotate = function () {
// save out initial non-rotate transform styling
let transform = '';
if (this.elt.style.transform) {
@@ -1979,7 +1979,7 @@ p5.Element.prototype._rotate = function() {
* @return {String} current value of property, if no value is given as second argument
* @chainable
*/
-p5.Element.prototype.style = function(prop, val) {
+p5.Element.prototype.style = function (prop, val) {
const self = this;
if (val instanceof p5.Color) {
@@ -2050,7 +2050,7 @@ p5.Element.prototype.style = function(prop, val) {
* @param {String} value value to assign to attribute
* @chainable
*/
-p5.Element.prototype.attribute = function(attr, value) {
+p5.Element.prototype.attribute = function (attr, value) {
//handling for checkboxes and radios to ensure options get
//attributes not divs
if (
@@ -2104,7 +2104,7 @@ p5.Element.prototype.attribute = function(attr, value) {
* }
*
*/
-p5.Element.prototype.removeAttribute = function(attr) {
+p5.Element.prototype.removeAttribute = function (attr) {
if (
this.elt.firstChild != null &&
(this.elt.firstChild.type === 'checkbox' ||
@@ -2153,7 +2153,7 @@ p5.Element.prototype.removeAttribute = function(attr) {
* @param {String|Number} value
* @chainable
*/
-p5.Element.prototype.value = function() {
+p5.Element.prototype.value = function () {
if (arguments.length > 0) {
this.elt.value = arguments[0];
return this;
@@ -2177,7 +2177,7 @@ p5.Element.prototype.value = function() {
* div.show(); // turns display to block
*
*/
-p5.Element.prototype.show = function() {
+p5.Element.prototype.show = function () {
this.elt.style.display = 'block';
return this;
};
@@ -2193,7 +2193,7 @@ p5.Element.prototype.show = function() {
* div.hide();
*
*/
-p5.Element.prototype.hide = function() {
+p5.Element.prototype.hide = function () {
this.elt.style.display = 'none';
return this;
};
@@ -2228,7 +2228,7 @@ p5.Element.prototype.hide = function() {
* @param {Number|Constant} [h] height of the element, either AUTO, or a number
* @chainable
*/
-p5.Element.prototype.size = function(w, h) {
+p5.Element.prototype.size = function (w, h) {
if (arguments.length === 0) {
return { width: this.elt.offsetWidth, height: this.elt.offsetHeight };
} else {
@@ -2288,7 +2288,7 @@ p5.Element.prototype.size = function(w, h) {
* myDiv.remove();
*
*/
-p5.Element.prototype.remove = function() {
+p5.Element.prototype.remove = function () {
// stop all audios/videos and detach all devices like microphone/camera etc
// used as input/output for audios/videos.
if (this instanceof p5.MediaElement) {
@@ -2376,13 +2376,13 @@ p5.Element.prototype.remove = function() {
* @alt
* Canvas turns into whatever image is dragged/dropped onto it.
*/
-p5.Element.prototype.drop = function(callback, fxn) {
+p5.Element.prototype.drop = function (callback, fxn) {
// Is the file stuff supported?
if (window.File && window.FileReader && window.FileList && window.Blob) {
if (!this._dragDisabled) {
this._dragDisabled = true;
- const preventDefault = function(evt) {
+ const preventDefault = function (evt) {
evt.preventDefault();
};
@@ -2398,7 +2398,7 @@ p5.Element.prototype.drop = function(callback, fxn) {
// Deal with the files
p5.Element._attachListener(
'drop',
- function(evt) {
+ function (evt) {
evt.preventDefault();
// Call the second argument as a callback that receives the raw drop event
if (typeof fxn === 'function') {
@@ -2422,6 +2422,20 @@ p5.Element.prototype.drop = function(callback, fxn) {
return this;
};
+/*** SCHEDULE EVENTS ***/
+
+// Cue inspired by JavaScript setTimeout, and the
+// Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
+// eslint-disable-next-line no-unused-vars
+class Cue {
+ constructor(callback, time, id, val) {
+ this.callback = callback;
+ this.time = time;
+ this.id = id;
+ this.val = val;
+ }
+}
+
// =============================================================================
// p5.MediaElement additions
// =============================================================================
@@ -2436,38 +2450,104 @@ p5.Element.prototype.drop = function(callback, fxn) {
* @constructor
* @param {String} elt DOM node that is wrapped
*/
-p5.MediaElement = function(elt, pInst) {
- p5.Element.call(this, elt, pInst);
+p5.MediaElement = class MediaElement extends p5.Element {
+ constructor(elt, pInst) {
+ super(elt, pInst);
+
+ const self = this;
+ this.elt.crossOrigin = 'anonymous';
+
+ this._prevTime = 0;
+ this._cueIDCounter = 0;
+ this._cues = [];
+ this.pixels = [];
+ this._pixelsState = this;
+ this._pixelDensity = 1;
+ this._modified = false;
+
+ // Media has an internal canvas that is used when drawing it to the main
+ // canvas. It will need to be updated each frame as the video itself plays.
+ // We don't want to update it every time we draw, however, in case the user
+ // has used load/updatePixels. To handle this, we record the frame drawn to
+ // the internal canvas so we only update it if the frame has changed.
+ this._frameOnCanvas = -1;
+
+ /**
+ * Path to the media element source.
+ *
+ * @property src
+ * @return {String} src
+ * @example
+ *
+ * let ele;
+ *
+ * function setup() {
+ * background(250);
+ *
+ * //p5.MediaElement objects are usually created
+ * //by calling the createAudio(), createVideo(),
+ * //and createCapture() functions.
+ *
+ * //In this example we create
+ * //a new p5.MediaElement via createAudio().
+ * ele = createAudio('assets/beat.mp3');
+ *
+ * //We'll set up our example so that
+ * //when you click on the text,
+ * //an alert box displays the MediaElement's
+ * //src field.
+ * textAlign(CENTER);
+ * text('Click Me!', width / 2, height / 2);
+ * }
+ *
+ * function mouseClicked() {
+ * //here we test if the mouse is over the
+ * //canvas element when it's clicked
+ * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
+ * //Show our p5.MediaElement's src field
+ * alert(ele.src);
+ * }
+ * }
+ *
+ */
+ Object.defineProperty(self, 'src', {
+ get() {
+ const firstChildSrc = self.elt.children[0].src;
+ const srcVal = self.elt.src === window.location.href ? '' : self.elt.src;
+ const ret =
+ firstChildSrc === window.location.href ? srcVal : firstChildSrc;
+ return ret;
+ },
+ set(newValue) {
+ for (let i = 0; i < self.elt.children.length; i++) {
+ self.elt.removeChild(self.elt.children[i]);
+ }
+ const source = document.createElement('source');
+ source.src = newValue;
+ elt.appendChild(source);
+ self.elt.src = newValue;
+ self.modified = true;
+ }
+ });
+
+ // private _onended callback, set by the method: onended(callback)
+ self._onended = function () { };
+ self.elt.onended = function () {
+ self._onended(self);
+ };
+ }
- const self = this;
- this.elt.crossOrigin = 'anonymous';
-
- this._prevTime = 0;
- this._cueIDCounter = 0;
- this._cues = [];
- this._pixelsState = this;
- this._pixelDensity = 1;
- this._modified = false;
-
- // Media has an internal canvas that is used when drawing it to the main
- // canvas. It will need to be updated each frame as the video itself plays.
- // We don't want to update it every time we draw, however, in case the user
- // has used load/updatePixels. To handle this, we record the frame drawn to
- // the internal canvas so we only update it if the frame has changed.
- this._frameOnCanvas = -1;
/**
- * Path to the media element source.
+ * Play an HTML5 media element.
*
- * @property src
- * @return {String} src
+ * @method play
+ * @chainable
* @example
*
* let ele;
*
* function setup() {
- * background(250);
- *
* //p5.MediaElement objects are usually created
* //by calling the createAudio(), createVideo(),
* //and createCapture() functions.
@@ -2476,973 +2556,899 @@ p5.MediaElement = function(elt, pInst) {
* //a new p5.MediaElement via createAudio().
* ele = createAudio('assets/beat.mp3');
*
- * //We'll set up our example so that
- * //when you click on the text,
- * //an alert box displays the MediaElement's
- * //src field.
+ * background(250);
* textAlign(CENTER);
- * text('Click Me!', width / 2, height / 2);
+ * text('Click to Play!', width / 2, height / 2);
* }
*
* function mouseClicked() {
* //here we test if the mouse is over the
* //canvas element when it's clicked
* if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
- * //Show our p5.MediaElement's src field
- * alert(ele.src);
+ * //Here we call the play() function on
+ * //the p5.MediaElement we created above.
+ * //This will start the audio sample.
+ * ele.play();
+ *
+ * background(200);
+ * text('You clicked Play!', width / 2, height / 2);
* }
* }
*
*/
- Object.defineProperty(self, 'src', {
- get: function() {
- const firstChildSrc = self.elt.children[0].src;
- const srcVal = self.elt.src === window.location.href ? '' : self.elt.src;
- const ret =
- firstChildSrc === window.location.href ? srcVal : firstChildSrc;
- return ret;
- },
- set: function(newValue) {
- for (let i = 0; i < self.elt.children.length; i++) {
- self.elt.removeChild(self.elt.children[i]);
- }
- const source = document.createElement('source');
- source.src = newValue;
- elt.appendChild(source);
- self.elt.src = newValue;
- self.modified = true;
+ play() {
+ if (this.elt.currentTime === this.elt.duration) {
+ this.elt.currentTime = 0;
}
- });
-
- // private _onended callback, set by the method: onended(callback)
- self._onended = function() {};
- self.elt.onended = function() {
- self._onended(self);
- };
-};
-p5.MediaElement.prototype = Object.create(p5.Element.prototype);
+ let promise;
+ if (this.elt.readyState > 1) {
+ promise = this.elt.play();
+ } else {
+ // in Chrome, playback cannot resume after being stopped and must reload
+ this.elt.load();
+ promise = this.elt.play();
+ }
+ if (promise && promise.catch) {
+ promise.catch(e => {
+ // if it's an autoplay failure error
+ if (e.name === 'NotAllowedError') {
+ if (typeof IS_MINIFIED === 'undefined') {
+ p5._friendlyAutoplayError(this.src);
+ } else {
+ console.error(e);
+ }
+ } else {
+ // any other kind of error
+ console.error('Media play method encountered an unexpected error', e);
+ }
+ });
+ }
+ return this;
+ }
-/**
- * Play an HTML5 media element.
- *
- * @method play
- * @chainable
- * @example
- *
- * let ele;
- *
- * function setup() {
- * //p5.MediaElement objects are usually created
- * //by calling the createAudio(), createVideo(),
- * //and createCapture() functions.
- *
- * //In this example we create
- * //a new p5.MediaElement via createAudio().
- * ele = createAudio('assets/beat.mp3');
- *
- * background(250);
- * textAlign(CENTER);
- * text('Click to Play!', width / 2, height / 2);
- * }
- *
- * function mouseClicked() {
- * //here we test if the mouse is over the
- * //canvas element when it's clicked
- * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
- * //Here we call the play() function on
- * //the p5.MediaElement we created above.
- * //This will start the audio sample.
- * ele.play();
- *
- * background(200);
- * text('You clicked Play!', width / 2, height / 2);
- * }
- * }
- *
- */
-p5.MediaElement.prototype.play = function() {
- if (this.elt.currentTime === this.elt.duration) {
+ /**
+ * Stops an HTML5 media element (sets current time to zero).
+ *
+ * @method stop
+ * @chainable
+ * @example
+ *
+ * //This example both starts
+ * //and stops a sound sample
+ * //when the user clicks the canvas
+ *
+ * //We will store the p5.MediaElement
+ * //object in here
+ * let ele;
+ *
+ * //while our audio is playing,
+ * //this will be set to true
+ * let sampleIsPlaying = false;
+ *
+ * function setup() {
+ * //Here we create a p5.MediaElement object
+ * //using the createAudio() function.
+ * ele = createAudio('assets/beat.mp3');
+ * background(200);
+ * textAlign(CENTER);
+ * text('Click to play!', width / 2, height / 2);
+ * }
+ *
+ * function mouseClicked() {
+ * //here we test if the mouse is over the
+ * //canvas element when it's clicked
+ * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
+ * background(200);
+ *
+ * if (sampleIsPlaying) {
+ * //if the sample is currently playing
+ * //calling the stop() function on
+ * //our p5.MediaElement will stop
+ * //it and reset its current
+ * //time to 0 (i.e. it will start
+ * //at the beginning the next time
+ * //you play it)
+ * ele.stop();
+ *
+ * sampleIsPlaying = false;
+ * text('Click to play!', width / 2, height / 2);
+ * } else {
+ * //loop our sound element until we
+ * //call ele.stop() on it.
+ * ele.loop();
+ *
+ * sampleIsPlaying = true;
+ * text('Click to stop!', width / 2, height / 2);
+ * }
+ * }
+ * }
+ *
+ */
+ stop() {
+ this.elt.pause();
this.elt.currentTime = 0;
+ return this;
}
- let promise;
- if (this.elt.readyState > 1) {
- promise = this.elt.play();
- } else {
- // in Chrome, playback cannot resume after being stopped and must reload
- this.elt.load();
- promise = this.elt.play();
- }
- if (promise && promise.catch) {
- promise.catch(e => {
- // if it's an autoplay failure error
- if (e.name === 'NotAllowedError') {
- if (typeof IS_MINIFIED === 'undefined') {
- p5._friendlyAutoplayError(this.src);
- } else {
- console.error(e);
- }
+
+ /**
+ * Pauses an HTML5 media element.
+ *
+ * @method pause
+ * @chainable
+ * @example
+ *
+ * //This example both starts
+ * //and pauses a sound sample
+ * //when the user clicks the canvas
+ *
+ * //We will store the p5.MediaElement
+ * //object in here
+ * let ele;
+ *
+ * //while our audio is playing,
+ * //this will be set to true
+ * let sampleIsPlaying = false;
+ *
+ * function setup() {
+ * //Here we create a p5.MediaElement object
+ * //using the createAudio() function.
+ * ele = createAudio('assets/lucky_dragons.mp3');
+ * background(200);
+ * textAlign(CENTER);
+ * text('Click to play!', width / 2, height / 2);
+ * }
+ *
+ * function mouseClicked() {
+ * //here we test if the mouse is over the
+ * //canvas element when it's clicked
+ * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
+ * background(200);
+ *
+ * if (sampleIsPlaying) {
+ * //Calling pause() on our
+ * //p5.MediaElement will stop it
+ * //playing, but when we call the
+ * //loop() or play() functions
+ * //the sample will start from
+ * //where we paused it.
+ * ele.pause();
+ *
+ * sampleIsPlaying = false;
+ * text('Click to resume!', width / 2, height / 2);
+ * } else {
+ * //loop our sound element until we
+ * //call ele.pause() on it.
+ * ele.loop();
+ *
+ * sampleIsPlaying = true;
+ * text('Click to pause!', width / 2, height / 2);
+ * }
+ * }
+ * }
+ *
+ */
+ pause() {
+ this.elt.pause();
+ return this;
+ }
+
+ /**
+ * Set 'loop' to true for an HTML5 media element, and starts playing.
+ *
+ * @method loop
+ * @chainable
+ * @example
+ *
+ * //Clicking the canvas will loop
+ * //the audio sample until the user
+ * //clicks again to stop it
+ *
+ * //We will store the p5.MediaElement
+ * //object in here
+ * let ele;
+ *
+ * //while our audio is playing,
+ * //this will be set to true
+ * let sampleIsLooping = false;
+ *
+ * function setup() {
+ * //Here we create a p5.MediaElement object
+ * //using the createAudio() function.
+ * ele = createAudio('assets/lucky_dragons.mp3');
+ * background(200);
+ * textAlign(CENTER);
+ * text('Click to loop!', width / 2, height / 2);
+ * }
+ *
+ * function mouseClicked() {
+ * //here we test if the mouse is over the
+ * //canvas element when it's clicked
+ * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
+ * background(200);
+ *
+ * if (!sampleIsLooping) {
+ * //loop our sound element until we
+ * //call ele.stop() on it.
+ * ele.loop();
+ *
+ * sampleIsLooping = true;
+ * text('Click to stop!', width / 2, height / 2);
+ * } else {
+ * ele.stop();
+ *
+ * sampleIsLooping = false;
+ * text('Click to loop!', width / 2, height / 2);
+ * }
+ * }
+ * }
+ *
+ */
+ loop() {
+ this.elt.setAttribute('loop', true);
+ this.play();
+ return this;
+ }
+ /**
+ * Set 'loop' to false for an HTML5 media element. Element will stop
+ * when it reaches the end.
+ *
+ * @method noLoop
+ * @chainable
+ * @example
+ *
+ * //This example both starts
+ * //and stops loop of sound sample
+ * //when the user clicks the canvas
+ *
+ * //We will store the p5.MediaElement
+ * //object in here
+ * let ele;
+ * //while our audio is playing,
+ * //this will be set to true
+ * let sampleIsPlaying = false;
+ *
+ * function setup() {
+ * //Here we create a p5.MediaElement object
+ * //using the createAudio() function.
+ * ele = createAudio('assets/beat.mp3');
+ * background(200);
+ * textAlign(CENTER);
+ * text('Click to play!', width / 2, height / 2);
+ * }
+ *
+ * function mouseClicked() {
+ * //here we test if the mouse is over the
+ * //canvas element when it's clicked
+ * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
+ * background(200);
+ *
+ * if (sampleIsPlaying) {
+ * ele.noLoop();
+ * sampleIsPlaying = false;
+ * text('No more Loops!', width / 2, height / 2);
+ * } else {
+ * ele.loop();
+ * sampleIsPlaying = true;
+ * text('Click to stop looping!', width / 2, height / 2);
+ * }
+ * }
+ * }
+ *
+ */
+ noLoop() {
+ this.elt.removeAttribute('loop');
+ return this;
+ }
+
+ /**
+ * Sets up logic to check that autoplay succeeded.
+ *
+ * @method setupAutoplayFailDetection
+ * @private
+ */
+ _setupAutoplayFailDetection() {
+ const timeout = setTimeout(() => {
+ if (typeof IS_MINIFIED === 'undefined') {
+ p5._friendlyAutoplayError(this.src);
} else {
- // any other kind of error
- console.error('Media play method encountered an unexpected error', e);
+ console.error(e);
}
+ }, 500);
+ this.elt.addEventListener('play', () => clearTimeout(timeout), {
+ passive: true,
+ once: true
});
}
- return this;
-};
-
-/**
- * Stops an HTML5 media element (sets current time to zero).
- *
- * @method stop
- * @chainable
- * @example
- *
- * //This example both starts
- * //and stops a sound sample
- * //when the user clicks the canvas
- *
- * //We will store the p5.MediaElement
- * //object in here
- * let ele;
- *
- * //while our audio is playing,
- * //this will be set to true
- * let sampleIsPlaying = false;
- *
- * function setup() {
- * //Here we create a p5.MediaElement object
- * //using the createAudio() function.
- * ele = createAudio('assets/beat.mp3');
- * background(200);
- * textAlign(CENTER);
- * text('Click to play!', width / 2, height / 2);
- * }
- *
- * function mouseClicked() {
- * //here we test if the mouse is over the
- * //canvas element when it's clicked
- * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
- * background(200);
- *
- * if (sampleIsPlaying) {
- * //if the sample is currently playing
- * //calling the stop() function on
- * //our p5.MediaElement will stop
- * //it and reset its current
- * //time to 0 (i.e. it will start
- * //at the beginning the next time
- * //you play it)
- * ele.stop();
- *
- * sampleIsPlaying = false;
- * text('Click to play!', width / 2, height / 2);
- * } else {
- * //loop our sound element until we
- * //call ele.stop() on it.
- * ele.loop();
- *
- * sampleIsPlaying = true;
- * text('Click to stop!', width / 2, height / 2);
- * }
- * }
- * }
- *
- */
-p5.MediaElement.prototype.stop = function() {
- this.elt.pause();
- this.elt.currentTime = 0;
- return this;
-};
-/**
- * Pauses an HTML5 media element.
- *
- * @method pause
- * @chainable
- * @example
- *
- * //This example both starts
- * //and pauses a sound sample
- * //when the user clicks the canvas
- *
- * //We will store the p5.MediaElement
- * //object in here
- * let ele;
- *
- * //while our audio is playing,
- * //this will be set to true
- * let sampleIsPlaying = false;
- *
- * function setup() {
- * //Here we create a p5.MediaElement object
- * //using the createAudio() function.
- * ele = createAudio('assets/lucky_dragons.mp3');
- * background(200);
- * textAlign(CENTER);
- * text('Click to play!', width / 2, height / 2);
- * }
- *
- * function mouseClicked() {
- * //here we test if the mouse is over the
- * //canvas element when it's clicked
- * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
- * background(200);
- *
- * if (sampleIsPlaying) {
- * //Calling pause() on our
- * //p5.MediaElement will stop it
- * //playing, but when we call the
- * //loop() or play() functions
- * //the sample will start from
- * //where we paused it.
- * ele.pause();
- *
- * sampleIsPlaying = false;
- * text('Click to resume!', width / 2, height / 2);
- * } else {
- * //loop our sound element until we
- * //call ele.pause() on it.
- * ele.loop();
- *
- * sampleIsPlaying = true;
- * text('Click to pause!', width / 2, height / 2);
- * }
- * }
- * }
- *
- */
-p5.MediaElement.prototype.pause = function() {
- this.elt.pause();
- return this;
-};
-
-/**
- * Set 'loop' to true for an HTML5 media element, and starts playing.
- *
- * @method loop
- * @chainable
- * @example
- *
- * //Clicking the canvas will loop
- * //the audio sample until the user
- * //clicks again to stop it
- *
- * //We will store the p5.MediaElement
- * //object in here
- * let ele;
- *
- * //while our audio is playing,
- * //this will be set to true
- * let sampleIsLooping = false;
- *
- * function setup() {
- * //Here we create a p5.MediaElement object
- * //using the createAudio() function.
- * ele = createAudio('assets/lucky_dragons.mp3');
- * background(200);
- * textAlign(CENTER);
- * text('Click to loop!', width / 2, height / 2);
- * }
- *
- * function mouseClicked() {
- * //here we test if the mouse is over the
- * //canvas element when it's clicked
- * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
- * background(200);
- *
- * if (!sampleIsLooping) {
- * //loop our sound element until we
- * //call ele.stop() on it.
- * ele.loop();
- *
- * sampleIsLooping = true;
- * text('Click to stop!', width / 2, height / 2);
- * } else {
- * ele.stop();
- *
- * sampleIsLooping = false;
- * text('Click to loop!', width / 2, height / 2);
- * }
- * }
- * }
- *
- */
-p5.MediaElement.prototype.loop = function() {
- this.elt.setAttribute('loop', true);
- this.play();
- return this;
-};
-/**
- * Set 'loop' to false for an HTML5 media element. Element will stop
- * when it reaches the end.
- *
- * @method noLoop
- * @chainable
- * @example
- *
- * //This example both starts
- * //and stops loop of sound sample
- * //when the user clicks the canvas
- *
- * //We will store the p5.MediaElement
- * //object in here
- * let ele;
- * //while our audio is playing,
- * //this will be set to true
- * let sampleIsPlaying = false;
- *
- * function setup() {
- * //Here we create a p5.MediaElement object
- * //using the createAudio() function.
- * ele = createAudio('assets/beat.mp3');
- * background(200);
- * textAlign(CENTER);
- * text('Click to play!', width / 2, height / 2);
- * }
- *
- * function mouseClicked() {
- * //here we test if the mouse is over the
- * //canvas element when it's clicked
- * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
- * background(200);
- *
- * if (sampleIsPlaying) {
- * ele.noLoop();
- * sampleIsPlaying = false;
- * text('No more Loops!', width / 2, height / 2);
- * } else {
- * ele.loop();
- * sampleIsPlaying = true;
- * text('Click to stop looping!', width / 2, height / 2);
- * }
- * }
- * }
- *
- */
-p5.MediaElement.prototype.noLoop = function() {
- this.elt.removeAttribute('loop');
- return this;
-};
+ /**
+ * Set HTML5 media element to autoplay or not. If no argument is specified, by
+ * default it will autoplay.
+ *
+ * @method autoplay
+ * @param {Boolean} shouldAutoplay whether the element should autoplay
+ * @chainable
+ * @example
+ *
+ * let videoElement;
+ * function setup() {
+ * noCanvas();
+ * videoElement = createVideo(['assets/small.mp4'], onVideoLoad);
+ * }
+ * function onVideoLoad() {
+ * // The media will play as soon as it is loaded.
+ * videoElement.autoplay();
+ * videoElement.volume(0);
+ * videoElement.size(100, 100);
+ * }
+ *
+ *
+ *
+ * let videoElement;
+ * function setup() {
+ * noCanvas();
+ * videoElement = createVideo(['assets/small.mp4'], onVideoLoad);
+ * }
+ * function onVideoLoad() {
+ * // The media will not play until some explicitly triggered.
+ * videoElement.autoplay(false);
+ * videoElement.volume(0);
+ * videoElement.size(100, 100);
+ * }
+ *
+ * function mouseClicked() {
+ * videoElement.play();
+ * }
+ *
+ *
+ * @alt
+ * An example of a video element which autoplays after it is loaded.
+ * An example of a video element which waits for a trigger for playing.
+ */
-/**
- * Sets up logic to check that autoplay succeeded.
- *
- * @method setupAutoplayFailDetection
- * @private
- */
-p5.MediaElement.prototype._setupAutoplayFailDetection = function() {
- const timeout = setTimeout(() => {
- if (typeof IS_MINIFIED === 'undefined') {
- p5._friendlyAutoplayError(this.src);
- } else {
- console.error(e);
+ autoplay(val) {
+ const oldVal = this.elt.getAttribute('autoplay');
+ this.elt.setAttribute('autoplay', val);
+ // if we turned on autoplay
+ if (val && !oldVal) {
+ // bind method to this scope
+ const setupAutoplayFailDetection =
+ () => this._setupAutoplayFailDetection();
+ // if media is ready to play, schedule check now
+ if (this.elt.readyState === 4) {
+ setupAutoplayFailDetection();
+ } else {
+ // otherwise, schedule check whenever it is ready
+ this.elt.addEventListener('canplay', setupAutoplayFailDetection, {
+ passive: true,
+ once: true
+ });
+ }
}
- }, 500);
- this.elt.addEventListener('play', () => clearTimeout(timeout), {
- passive: true,
- once: true
- });
-};
-/**
- * Set HTML5 media element to autoplay or not. If no argument is specified, by
- * default it will autoplay.
- *
- * @method autoplay
- * @param {Boolean} shouldAutoplay whether the element should autoplay
- * @chainable
- * @example
- *
- * let videoElement;
- * function setup() {
- * noCanvas();
- * videoElement = createVideo(['assets/small.mp4'], onVideoLoad);
- * }
- * function onVideoLoad() {
- * // The media will play as soon as it is loaded.
- * videoElement.autoplay();
- * videoElement.volume(0);
- * videoElement.size(100, 100);
- * }
- *
- *
- *
- * let videoElement;
- * function setup() {
- * noCanvas();
- * videoElement = createVideo(['assets/small.mp4'], onVideoLoad);
- * }
- * function onVideoLoad() {
- * // The media will not play until some explicitly triggered.
- * videoElement.autoplay(false);
- * videoElement.volume(0);
- * videoElement.size(100, 100);
- * }
- *
- * function mouseClicked() {
- * videoElement.play();
- * }
- *
- *
- * @alt
- * An example of a video element which autoplays after it is loaded.
- * An example of a video element which waits for a trigger for playing.
- */
+ return this;
+ }
-p5.MediaElement.prototype.autoplay = function(val) {
- const oldVal = this.elt.getAttribute('autoplay');
- this.elt.setAttribute('autoplay', val);
- // if we turned on autoplay
- if (val && !oldVal) {
- // bind method to this scope
- const setupAutoplayFailDetection = () => this._setupAutoplayFailDetection();
- // if media is ready to play, schedule check now
- if (this.elt.readyState === 4) {
- setupAutoplayFailDetection();
+ /**
+ * Sets volume for this HTML5 media element. If no argument is given,
+ * returns the current volume.
+ *
+ * @method volume
+ * @return {Number} current volume
+ *
+ * @example
+ *
+ * let ele;
+ * function setup() {
+ * // p5.MediaElement objects are usually created
+ * // by calling the createAudio(), createVideo(),
+ * // and createCapture() functions.
+ * // In this example we create
+ * // a new p5.MediaElement via createAudio().
+ * ele = createAudio('assets/lucky_dragons.mp3');
+ * background(250);
+ * textAlign(CENTER);
+ * text('Click to Play!', width / 2, height / 2);
+ * }
+ * function mouseClicked() {
+ * // Here we call the volume() function
+ * // on the sound element to set its volume
+ * // Volume must be between 0.0 and 1.0
+ * ele.volume(0.2);
+ * ele.play();
+ * background(200);
+ * text('You clicked Play!', width / 2, height / 2);
+ * }
+ *
+ *
+ * let audio;
+ * let counter = 0;
+ *
+ * function loaded() {
+ * audio.play();
+ * }
+ *
+ * function setup() {
+ * audio = createAudio('assets/lucky_dragons.mp3', loaded);
+ * textAlign(CENTER);
+ * }
+ *
+ * function draw() {
+ * if (counter === 0) {
+ * background(0, 255, 0);
+ * text('volume(0.9)', width / 2, height / 2);
+ * } else if (counter === 1) {
+ * background(255, 255, 0);
+ * text('volume(0.5)', width / 2, height / 2);
+ * } else if (counter === 2) {
+ * background(255, 0, 0);
+ * text('volume(0.1)', width / 2, height / 2);
+ * }
+ * }
+ *
+ * function mousePressed() {
+ * counter++;
+ * if (counter === 0) {
+ * audio.volume(0.9);
+ * } else if (counter === 1) {
+ * audio.volume(0.5);
+ * } else if (counter === 2) {
+ * audio.volume(0.1);
+ * } else {
+ * counter = 0;
+ * audio.volume(0.9);
+ * }
+ * }
+ *
+ *
+ */
+ /**
+ * @method volume
+ * @param {Number} val volume between 0.0 and 1.0
+ * @chainable
+ */
+ volume(val) {
+ if (typeof val === 'undefined') {
+ return this.elt.volume;
} else {
- // otherwise, schedule check whenever it is ready
- this.elt.addEventListener('canplay', setupAutoplayFailDetection, {
- passive: true,
- once: true
- });
+ this.elt.volume = val;
}
}
- return this;
-};
-
-/**
- * Sets volume for this HTML5 media element. If no argument is given,
- * returns the current volume.
- *
- * @method volume
- * @return {Number} current volume
- *
- * @example
- *
- * let ele;
- * function setup() {
- * // p5.MediaElement objects are usually created
- * // by calling the createAudio(), createVideo(),
- * // and createCapture() functions.
- * // In this example we create
- * // a new p5.MediaElement via createAudio().
- * ele = createAudio('assets/lucky_dragons.mp3');
- * background(250);
- * textAlign(CENTER);
- * text('Click to Play!', width / 2, height / 2);
- * }
- * function mouseClicked() {
- * // Here we call the volume() function
- * // on the sound element to set its volume
- * // Volume must be between 0.0 and 1.0
- * ele.volume(0.2);
- * ele.play();
- * background(200);
- * text('You clicked Play!', width / 2, height / 2);
- * }
- *
- *
- * let audio;
- * let counter = 0;
- *
- * function loaded() {
- * audio.play();
- * }
- *
- * function setup() {
- * audio = createAudio('assets/lucky_dragons.mp3', loaded);
- * textAlign(CENTER);
- * }
- *
- * function draw() {
- * if (counter === 0) {
- * background(0, 255, 0);
- * text('volume(0.9)', width / 2, height / 2);
- * } else if (counter === 1) {
- * background(255, 255, 0);
- * text('volume(0.5)', width / 2, height / 2);
- * } else if (counter === 2) {
- * background(255, 0, 0);
- * text('volume(0.1)', width / 2, height / 2);
- * }
- * }
- *
- * function mousePressed() {
- * counter++;
- * if (counter === 0) {
- * audio.volume(0.9);
- * } else if (counter === 1) {
- * audio.volume(0.5);
- * } else if (counter === 2) {
- * audio.volume(0.1);
- * } else {
- * counter = 0;
- * audio.volume(0.9);
- * }
- * }
- *
- *
- */
-/**
- * @method volume
- * @param {Number} val volume between 0.0 and 1.0
- * @chainable
- */
-p5.MediaElement.prototype.volume = function(val) {
- if (typeof val === 'undefined') {
- return this.elt.volume;
- } else {
- this.elt.volume = val;
- }
-};
-
-/**
- * If no arguments are given, returns the current playback speed of the
- * element. The speed parameter sets the speed where 2.0 will play the
- * element twice as fast, 0.5 will play at half the speed, and -1 will play
- * the element in normal speed in reverse.(Note that not all browsers support
- * backward playback and even if they do, playback might not be smooth.)
- *
- * @method speed
- * @return {Number} current playback speed of the element
- *
- * @example
- *
- * //Clicking the canvas will loop
- * //the audio sample until the user
- * //clicks again to stop it
- *
- * //We will store the p5.MediaElement
- * //object in here
- * let ele;
- * let button;
- *
- * function setup() {
- * createCanvas(710, 400);
- * //Here we create a p5.MediaElement object
- * //using the createAudio() function.
- * ele = createAudio('assets/beat.mp3');
- * ele.loop();
- * background(200);
- *
- * button = createButton('2x speed');
- * button.position(100, 68);
- * button.mousePressed(twice_speed);
- *
- * button = createButton('half speed');
- * button.position(200, 68);
- * button.mousePressed(half_speed);
- *
- * button = createButton('reverse play');
- * button.position(300, 68);
- * button.mousePressed(reverse_speed);
- *
- * button = createButton('STOP');
- * button.position(400, 68);
- * button.mousePressed(stop_song);
- *
- * button = createButton('PLAY!');
- * button.position(500, 68);
- * button.mousePressed(play_speed);
- * }
- *
- * function twice_speed() {
- * ele.speed(2);
- * }
- *
- * function half_speed() {
- * ele.speed(0.5);
- * }
- *
- * function reverse_speed() {
- * ele.speed(-1);
- * }
- *
- * function stop_song() {
- * ele.stop();
- * }
- *
- * function play_speed() {
- * ele.play();
- * }
- *
- */
+ /**
+ * If no arguments are given, returns the current playback speed of the
+ * element. The speed parameter sets the speed where 2.0 will play the
+ * element twice as fast, 0.5 will play at half the speed, and -1 will play
+ * the element in normal speed in reverse.(Note that not all browsers support
+ * backward playback and even if they do, playback might not be smooth.)
+ *
+ * @method speed
+ * @return {Number} current playback speed of the element
+ *
+ * @example
+ *
+ * //Clicking the canvas will loop
+ * //the audio sample until the user
+ * //clicks again to stop it
+ *
+ * //We will store the p5.MediaElement
+ * //object in here
+ * let ele;
+ * let button;
+ *
+ * function setup() {
+ * createCanvas(710, 400);
+ * //Here we create a p5.MediaElement object
+ * //using the createAudio() function.
+ * ele = createAudio('assets/beat.mp3');
+ * ele.loop();
+ * background(200);
+ *
+ * button = createButton('2x speed');
+ * button.position(100, 68);
+ * button.mousePressed(twice_speed);
+ *
+ * button = createButton('half speed');
+ * button.position(200, 68);
+ * button.mousePressed(half_speed);
+ *
+ * button = createButton('reverse play');
+ * button.position(300, 68);
+ * button.mousePressed(reverse_speed);
+ *
+ * button = createButton('STOP');
+ * button.position(400, 68);
+ * button.mousePressed(stop_song);
+ *
+ * button = createButton('PLAY!');
+ * button.position(500, 68);
+ * button.mousePressed(play_speed);
+ * }
+ *
+ * function twice_speed() {
+ * ele.speed(2);
+ * }
+ *
+ * function half_speed() {
+ * ele.speed(0.5);
+ * }
+ *
+ * function reverse_speed() {
+ * ele.speed(-1);
+ * }
+ *
+ * function stop_song() {
+ * ele.stop();
+ * }
+ *
+ * function play_speed() {
+ * ele.play();
+ * }
+ *
+ */
-/**
- * @method speed
- * @param {Number} speed speed multiplier for element playback
- * @chainable
- */
-p5.MediaElement.prototype.speed = function(val) {
- if (typeof val === 'undefined') {
- return this.presetPlaybackRate || this.elt.playbackRate;
- } else {
- if (this.loadedmetadata) {
- this.elt.playbackRate = val;
+ /**
+ * @method speed
+ * @param {Number} speed speed multiplier for element playback
+ * @chainable
+ */
+ speed(val) {
+ if (typeof val === 'undefined') {
+ return this.presetPlaybackRate || this.elt.playbackRate;
} else {
- this.presetPlaybackRate = val;
+ if (this.loadedmetadata) {
+ this.elt.playbackRate = val;
+ } else {
+ this.presetPlaybackRate = val;
+ }
}
}
-};
-/**
- * If no arguments are given, returns the current time of the element.
- * If an argument is given the current time of the element is set to it.
- *
- * @method time
- * @return {Number} current time (in seconds)
- *
- * @example
- *
- * let ele;
- * let beginning = true;
- * function setup() {
- * //p5.MediaElement objects are usually created
- * //by calling the createAudio(), createVideo(),
- * //and createCapture() functions.
- *
- * //In this example we create
- * //a new p5.MediaElement via createAudio().
- * ele = createAudio('assets/lucky_dragons.mp3');
- * background(250);
- * textAlign(CENTER);
- * text('start at beginning', width / 2, height / 2);
- * }
- *
- * // this function fires with click anywhere
- * function mousePressed() {
- * if (beginning === true) {
- * // here we start the sound at the beginning
- * // time(0) is not necessary here
- * // as this produces the same result as
- * // play()
- * ele.play().time(0);
- * background(200);
- * text('jump 2 sec in', width / 2, height / 2);
- * beginning = false;
- * } else {
- * // here we jump 2 seconds into the sound
- * ele.play().time(2);
- * background(250);
- * text('start at beginning', width / 2, height / 2);
- * beginning = true;
- * }
- * }
- *
- */
-/**
- * @method time
- * @param {Number} time time to jump to (in seconds)
- * @chainable
- */
-p5.MediaElement.prototype.time = function(val) {
- if (typeof val === 'undefined') {
- return this.elt.currentTime;
- } else {
- this.elt.currentTime = val;
- return this;
+ /**
+ * If no arguments are given, returns the current time of the element.
+ * If an argument is given the current time of the element is set to it.
+ *
+ * @method time
+ * @return {Number} current time (in seconds)
+ *
+ * @example
+ *
+ * let ele;
+ * let beginning = true;
+ * function setup() {
+ * //p5.MediaElement objects are usually created
+ * //by calling the createAudio(), createVideo(),
+ * //and createCapture() functions.
+ *
+ * //In this example we create
+ * //a new p5.MediaElement via createAudio().
+ * ele = createAudio('assets/lucky_dragons.mp3');
+ * background(250);
+ * textAlign(CENTER);
+ * text('start at beginning', width / 2, height / 2);
+ * }
+ *
+ * // this function fires with click anywhere
+ * function mousePressed() {
+ * if (beginning === true) {
+ * // here we start the sound at the beginning
+ * // time(0) is not necessary here
+ * // as this produces the same result as
+ * // play()
+ * ele.play().time(0);
+ * background(200);
+ * text('jump 2 sec in', width / 2, height / 2);
+ * beginning = false;
+ * } else {
+ * // here we jump 2 seconds into the sound
+ * ele.play().time(2);
+ * background(250);
+ * text('start at beginning', width / 2, height / 2);
+ * beginning = true;
+ * }
+ * }
+ *
+ */
+ /**
+ * @method time
+ * @param {Number} time time to jump to (in seconds)
+ * @chainable
+ */
+ time(val) {
+ if (typeof val === 'undefined') {
+ return this.elt.currentTime;
+ } else {
+ this.elt.currentTime = val;
+ return this;
+ }
}
-};
-/**
- * Returns the duration of the HTML5 media element.
- *
- * @method duration
- * @return {Number} duration
- *
- * @example
- *
- * let ele;
- * function setup() {
- * //p5.MediaElement objects are usually created
- * //by calling the createAudio(), createVideo(),
- * //and createCapture() functions.
- * //In this example we create
- * //a new p5.MediaElement via createAudio().
- * ele = createAudio('assets/doorbell.mp3');
- * background(250);
- * textAlign(CENTER);
- * text('Click to know the duration!', 10, 25, 70, 80);
- * }
- * function mouseClicked() {
- * ele.play();
- * background(200);
- * //ele.duration dislpays the duration
- * text(ele.duration() + ' seconds', width / 2, height / 2);
- * }
- *
- */
-p5.MediaElement.prototype.duration = function() {
- return this.elt.duration;
-};
-p5.MediaElement.prototype.pixels = [];
-p5.MediaElement.prototype._ensureCanvas = function() {
- if (!this.canvas) {
- this.canvas = document.createElement('canvas');
- this.drawingContext = this.canvas.getContext('2d');
- this.setModified(true);
+ /**
+ * Returns the duration of the HTML5 media element.
+ *
+ * @method duration
+ * @return {Number} duration
+ *
+ * @example
+ *
+ * let ele;
+ * function setup() {
+ * //p5.MediaElement objects are usually created
+ * //by calling the createAudio(), createVideo(),
+ * //and createCapture() functions.
+ * //In this example we create
+ * //a new p5.MediaElement via createAudio().
+ * ele = createAudio('assets/doorbell.mp3');
+ * background(250);
+ * textAlign(CENTER);
+ * text('Click to know the duration!', 10, 25, 70, 80);
+ * }
+ * function mouseClicked() {
+ * ele.play();
+ * background(200);
+ * //ele.duration dislpays the duration
+ * text(ele.duration() + ' seconds', width / 2, height / 2);
+ * }
+ *
+ */
+ duration() {
+ return this.elt.duration;
}
- // Don't update the canvas again if we have already updated the canvas with
- // the current frame
- const needsRedraw = this._frameOnCanvas !== this._pInst.frameCount;
- if (this.loadedmetadata && needsRedraw) {
- // wait for metadata for w/h
- if (this.canvas.width !== this.elt.width) {
- this.canvas.width = this.elt.width;
- this.canvas.height = this.elt.height;
- this.width = this.canvas.width;
- this.height = this.canvas.height;
+ _ensureCanvas() {
+ if (!this.canvas) {
+ this.canvas = document.createElement('canvas');
+ this.drawingContext = this.canvas.getContext('2d');
+ this.setModified(true);
}
- this.drawingContext.drawImage(
- this.elt,
- 0,
- 0,
- this.canvas.width,
- this.canvas.height
- );
+ // Don't update the canvas again if we have already updated the canvas with
+ // the current frame
+ const needsRedraw = this._frameOnCanvas !== this._pInst.frameCount;
+ if (this.loadedmetadata && needsRedraw) {
+ // wait for metadata for w/h
+ if (this.canvas.width !== this.elt.width) {
+ this.canvas.width = this.elt.width;
+ this.canvas.height = this.elt.height;
+ this.width = this.canvas.width;
+ this.height = this.canvas.height;
+ }
+
+ this.drawingContext.drawImage(
+ this.elt,
+ 0,
+ 0,
+ this.canvas.width,
+ this.canvas.height
+ );
+ this.setModified(true);
+ this._frameOnCanvas = this._pInst.frameCount;
+ }
+ }
+ loadPixels() {
+ this._ensureCanvas();
+ return p5.Renderer2D.prototype.loadPixels.apply(this, arguments);
+ }
+ updatePixels(x, y, w, h) {
+ if (this.loadedmetadata) {
+ // wait for metadata
+ this._ensureCanvas();
+ p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
+ }
this.setModified(true);
- this._frameOnCanvas = this._pInst.frameCount;
+ return this;
}
-};
-p5.MediaElement.prototype.loadPixels = function() {
- this._ensureCanvas();
- return p5.Renderer2D.prototype.loadPixels.apply(this, arguments);
-};
-p5.MediaElement.prototype.updatePixels = function(x, y, w, h) {
- if (this.loadedmetadata) {
- // wait for metadata
+ get() {
this._ensureCanvas();
- p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
+ return p5.Renderer2D.prototype.get.apply(this, arguments);
+ }
+ _getPixel() {
+ this.loadPixels();
+ return p5.Renderer2D.prototype._getPixel.apply(this, arguments);
}
- this.setModified(true);
- return this;
-};
-p5.MediaElement.prototype.get = function() {
- this._ensureCanvas();
- return p5.Renderer2D.prototype.get.apply(this, arguments);
-};
-p5.MediaElement.prototype._getPixel = function() {
- this.loadPixels();
- return p5.Renderer2D.prototype._getPixel.apply(this, arguments);
-};
-p5.MediaElement.prototype.set = function(x, y, imgOrCol) {
- if (this.loadedmetadata) {
- // wait for metadata
+ set(x, y, imgOrCol) {
+ if (this.loadedmetadata) {
+ // wait for metadata
+ this._ensureCanvas();
+ p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
+ this.setModified(true);
+ }
+ }
+ copy() {
this._ensureCanvas();
- p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
+ p5.prototype.copy.apply(this, arguments);
+ }
+ mask() {
+ this.loadPixels();
this.setModified(true);
+ p5.Image.prototype.mask.apply(this, arguments);
+ }
+ /**
+ * helper method for web GL mode to figure out if the element
+ * has been modified and might need to be re-uploaded to texture
+ * memory between frames.
+ * @method isModified
+ * @private
+ * @return {boolean} a boolean indicating whether or not the
+ * image has been updated or modified since last texture upload.
+ */
+ isModified() {
+ return this._modified;
+ }
+ /**
+ * helper method for web GL mode to indicate that an element has been
+ * changed or unchanged since last upload. gl texture upload will
+ * set this value to false after uploading the texture; or might set
+ * it to true if metadata has become available but there is no actual
+ * texture data available yet..
+ * @method setModified
+ * @param {boolean} val sets whether or not the element has been
+ * modified.
+ * @private
+ */
+ setModified(value) {
+ this._modified = value;
+ }
+ /**
+ * Schedule an event to be called when the audio or video
+ * element reaches the end. If the element is looping,
+ * this will not be called. The element is passed in
+ * as the argument to the onended callback.
+ *
+ * @method onended
+ * @param {Function} callback function to call when the
+ * soundfile has ended. The
+ * media element will be passed
+ * in as the argument to the
+ * callback.
+ * @chainable
+ * @example
+ *
+ * function setup() {
+ * let audioEl = createAudio('assets/beat.mp3');
+ * audioEl.showControls();
+ * audioEl.onended(sayDone);
+ * }
+ *
+ * function sayDone(elt) {
+ * alert('done playing ' + elt.src);
+ * }
+ *
+ */
+ onended(callback) {
+ this._onended = callback;
+ return this;
}
-};
-p5.MediaElement.prototype.copy = function() {
- this._ensureCanvas();
- p5.prototype.copy.apply(this, arguments);
-};
-p5.MediaElement.prototype.mask = function() {
- this.loadPixels();
- this.setModified(true);
- p5.Image.prototype.mask.apply(this, arguments);
-};
-/**
- * helper method for web GL mode to figure out if the element
- * has been modified and might need to be re-uploaded to texture
- * memory between frames.
- * @method isModified
- * @private
- * @return {boolean} a boolean indicating whether or not the
- * image has been updated or modified since last texture upload.
- */
-p5.MediaElement.prototype.isModified = function() {
- return this._modified;
-};
-/**
- * helper method for web GL mode to indicate that an element has been
- * changed or unchanged since last upload. gl texture upload will
- * set this value to false after uploading the texture; or might set
- * it to true if metadata has become available but there is no actual
- * texture data available yet..
- * @method setModified
- * @param {boolean} val sets whether or not the element has been
- * modified.
- * @private
- */
-p5.MediaElement.prototype.setModified = function(value) {
- this._modified = value;
-};
-/**
- * Schedule an event to be called when the audio or video
- * element reaches the end. If the element is looping,
- * this will not be called. The element is passed in
- * as the argument to the onended callback.
- *
- * @method onended
- * @param {Function} callback function to call when the
- * soundfile has ended. The
- * media element will be passed
- * in as the argument to the
- * callback.
- * @chainable
- * @example
- *
- * function setup() {
- * let audioEl = createAudio('assets/beat.mp3');
- * audioEl.showControls();
- * audioEl.onended(sayDone);
- * }
- *
- * function sayDone(elt) {
- * alert('done playing ' + elt.src);
- * }
- *
- */
-p5.MediaElement.prototype.onended = function(callback) {
- this._onended = callback;
- return this;
-};
-/*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
+ /*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
-/**
- * Send the audio output of this element to a specified audioNode or
- * p5.sound object. If no element is provided, connects to p5's main
- * output. That connection is established when this method is first called.
- * All connections are removed by the .disconnect() method.
- *
- * This method is meant to be used with the p5.sound.js addon library.
- *
- * @method connect
- * @param {AudioNode|Object} audioNode AudioNode from the Web Audio API,
- * or an object from the p5.sound library
- */
-p5.MediaElement.prototype.connect = function(obj) {
- let audioContext, mainOutput;
+ /**
+ * Send the audio output of this element to a specified audioNode or
+ * p5.sound object. If no element is provided, connects to p5's main
+ * output. That connection is established when this method is first called.
+ * All connections are removed by the .disconnect() method.
+ *
+ * This method is meant to be used with the p5.sound.js addon library.
+ *
+ * @method connect
+ * @param {AudioNode|Object} audioNode AudioNode from the Web Audio API,
+ * or an object from the p5.sound library
+ */
+ connect(obj) {
+ let audioContext, mainOutput;
- // if p5.sound exists, same audio context
- if (typeof p5.prototype.getAudioContext === 'function') {
- audioContext = p5.prototype.getAudioContext();
- mainOutput = p5.soundOut.input;
- } else {
- try {
- audioContext = obj.context;
- mainOutput = audioContext.destination;
- } catch (e) {
- throw 'connect() is meant to be used with Web Audio API or p5.sound.js';
+ // if p5.sound exists, same audio context
+ if (typeof p5.prototype.getAudioContext === 'function') {
+ audioContext = p5.prototype.getAudioContext();
+ mainOutput = p5.soundOut.input;
+ } else {
+ try {
+ audioContext = obj.context;
+ mainOutput = audioContext.destination;
+ } catch (e) {
+ throw 'connect() is meant to be used with Web Audio API or p5.sound.js';
+ }
}
- }
- // create a Web Audio MediaElementAudioSourceNode if none already exists
- if (!this.audioSourceNode) {
- this.audioSourceNode = audioContext.createMediaElementSource(this.elt);
+ // create a Web Audio MediaElementAudioSourceNode if none already exists
+ if (!this.audioSourceNode) {
+ this.audioSourceNode = audioContext.createMediaElementSource(this.elt);
- // connect to main output when this method is first called
- this.audioSourceNode.connect(mainOutput);
- }
+ // connect to main output when this method is first called
+ this.audioSourceNode.connect(mainOutput);
+ }
- // connect to object if provided
- if (obj) {
- if (obj.input) {
- this.audioSourceNode.connect(obj.input);
+ // connect to object if provided
+ if (obj) {
+ if (obj.input) {
+ this.audioSourceNode.connect(obj.input);
+ } else {
+ this.audioSourceNode.connect(obj);
+ }
} else {
- this.audioSourceNode.connect(obj);
+ // otherwise connect to main output of p5.sound / AudioContext
+ this.audioSourceNode.connect(mainOutput);
}
- } else {
- // otherwise connect to main output of p5.sound / AudioContext
- this.audioSourceNode.connect(mainOutput);
}
-};
-/**
- * Disconnect all Web Audio routing, including to main output.
- * This is useful if you want to re-route the output through
- * audio effects, for example.
- *
- * @method disconnect
- */
-p5.MediaElement.prototype.disconnect = function() {
- if (this.audioSourceNode) {
- this.audioSourceNode.disconnect();
- } else {
- throw 'nothing to disconnect';
+ /**
+ * Disconnect all Web Audio routing, including to main output.
+ * This is useful if you want to re-route the output through
+ * audio effects, for example.
+ *
+ * @method disconnect
+ */
+ disconnect() {
+ if (this.audioSourceNode) {
+ this.audioSourceNode.disconnect();
+ } else {
+ throw 'nothing to disconnect';
+ }
}
-};
-
-/*** SHOW / HIDE CONTROLS ***/
-
-/**
- * Show the default MediaElement controls, as determined by the web browser.
- *
- * @method showControls
- * @example
- *
- * let ele;
- * function setup() {
- * //p5.MediaElement objects are usually created
- * //by calling the createAudio(), createVideo(),
- * //and createCapture() functions.
- * //In this example we create
- * //a new p5.MediaElement via createAudio()
- * ele = createAudio('assets/lucky_dragons.mp3');
- * background(200);
- * textAlign(CENTER);
- * text('Click to Show Controls!', 10, 25, 70, 80);
- * }
- * function mousePressed() {
- * ele.showControls();
- * background(200);
- * text('Controls Shown', width / 2, height / 2);
- * }
- *
- */
-p5.MediaElement.prototype.showControls = function() {
- // must set style for the element to show on the page
- this.elt.style['text-align'] = 'inherit';
- this.elt.controls = true;
-};
-/**
- * Hide the default mediaElement controls.
- * @method hideControls
- * @example
- *
- * let ele;
- * function setup() {
- * //p5.MediaElement objects are usually created
- * //by calling the createAudio(), createVideo(),
- * //and createCapture() functions.
- * //In this example we create
- * //a new p5.MediaElement via createAudio()
- * ele = createAudio('assets/lucky_dragons.mp3');
- * ele.showControls();
- * background(200);
- * textAlign(CENTER);
- * text('Click to hide Controls!', 10, 25, 70, 80);
- * }
- * function mousePressed() {
- * ele.hideControls();
- * background(200);
- * text('Controls hidden', width / 2, height / 2);
- * }
- *
- */
-p5.MediaElement.prototype.hideControls = function() {
- this.elt.controls = false;
-};
+ /*** SHOW / HIDE CONTROLS ***/
-/*** SCHEDULE EVENTS ***/
+ /**
+ * Show the default MediaElement controls, as determined by the web browser.
+ *
+ * @method showControls
+ * @example
+ *
+ * let ele;
+ * function setup() {
+ * //p5.MediaElement objects are usually created
+ * //by calling the createAudio(), createVideo(),
+ * //and createCapture() functions.
+ * //In this example we create
+ * //a new p5.MediaElement via createAudio()
+ * ele = createAudio('assets/lucky_dragons.mp3');
+ * background(200);
+ * textAlign(CENTER);
+ * text('Click to Show Controls!', 10, 25, 70, 80);
+ * }
+ * function mousePressed() {
+ * ele.showControls();
+ * background(200);
+ * text('Controls Shown', width / 2, height / 2);
+ * }
+ *
+ */
+ showControls() {
+ // must set style for the element to show on the page
+ this.elt.style['text-align'] = 'inherit';
+ this.elt.controls = true;
+ }
-// Cue inspired by JavaScript setTimeout, and the
-// Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
-const Cue = function(callback, time, id, val) {
- this.callback = callback;
- this.time = time;
- this.id = id;
- this.val = val;
-};
+ /**
+ * Hide the default mediaElement controls.
+ * @method hideControls
+ * @example
+ *
+ * let ele;
+ * function setup() {
+ * //p5.MediaElement objects are usually created
+ * //by calling the createAudio(), createVideo(),
+ * //and createCapture() functions.
+ * //In this example we create
+ * //a new p5.MediaElement via createAudio()
+ * ele = createAudio('assets/lucky_dragons.mp3');
+ * ele.showControls();
+ * background(200);
+ * textAlign(CENTER);
+ * text('Click to hide Controls!', 10, 25, 70, 80);
+ * }
+ * function mousePressed() {
+ * ele.hideControls();
+ * background(200);
+ * text('Controls hidden', width / 2, height / 2);
+ * }
+ *
+ */
+ hideControls() {
+ this.elt.controls = false;
+ }
-/**
+ /**
* Schedule events to trigger every time a MediaElement
* (audio/video) reaches a playback cue point.
*
@@ -3492,20 +3498,20 @@ const Cue = function(callback, time, id, val) {
* }
*
*/
-p5.MediaElement.prototype.addCue = function(time, callback, val) {
- const id = this._cueIDCounter++;
+ addCue(time, callback, val) {
+ const id = this._cueIDCounter++;
- const cue = new Cue(callback, time, id, val);
- this._cues.push(cue);
+ const cue = new Cue(callback, time, id, val);
+ this._cues.push(cue);
- if (!this.elt.ontimeupdate) {
- this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
- }
+ if (!this.elt.ontimeupdate) {
+ this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
+ }
- return id;
-};
+ return id;
+ }
-/**
+ /**
* Remove a callback based on its ID. The ID is returned by the
* addCue method.
* @method removeCue
@@ -3534,20 +3540,20 @@ p5.MediaElement.prototype.addCue = function(time, callback, val) {
* }
*
*/
-p5.MediaElement.prototype.removeCue = function(id) {
- for (let i = 0; i < this._cues.length; i++) {
- if (this._cues[i].id === id) {
- console.log(id);
- this._cues.splice(i, 1);
+ removeCue(id) {
+ for (let i = 0; i < this._cues.length; i++) {
+ if (this._cues[i].id === id) {
+ console.log(id);
+ this._cues.splice(i, 1);
+ }
}
- }
- if (this._cues.length === 0) {
- this.elt.ontimeupdate = null;
+ if (this._cues.length === 0) {
+ this.elt.ontimeupdate = null;
+ }
}
-};
-/**
+ /**
* Remove all of the callbacks that had originally been scheduled
* via the addCue method.
* @method clearCues
@@ -3581,27 +3587,28 @@ p5.MediaElement.prototype.removeCue = function(id) {
* }
*
*/
-p5.MediaElement.prototype.clearCues = function() {
- this._cues = [];
- this.elt.ontimeupdate = null;
-};
+ clearCues() {
+ this._cues = [];
+ this.elt.ontimeupdate = null;
+ }
-// private method that checks for cues to be fired if events
-// have been scheduled using addCue(callback, time).
-p5.MediaElement.prototype._onTimeUpdate = function() {
- const playbackTime = this.time();
+ // private method that checks for cues to be fired if events
+ // have been scheduled using addCue(callback, time).
+ _onTimeUpdate() {
+ const playbackTime = this.time();
- for (let i = 0; i < this._cues.length; i++) {
- const callbackTime = this._cues[i].time;
- const val = this._cues[i].val;
+ for (let i = 0; i < this._cues.length; i++) {
+ const callbackTime = this._cues[i].time;
+ const val = this._cues[i].val;
- if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
- // pass the scheduled callbackTime as parameter to the callback
- this._cues[i].callback(val);
+ if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
+ // pass the scheduled callbackTime as parameter to the callback
+ this._cues[i].callback(val);
+ }
}
- }
- this._prevTime = playbackTime;
+ this._prevTime = playbackTime;
+ }
};
/**
@@ -3612,84 +3619,87 @@ p5.MediaElement.prototype._onTimeUpdate = function() {
* @constructor
* @param {File} file File that is wrapped
*/
-p5.File = function(file, pInst) {
- /**
- * Underlying File object. All normal File methods can be called on this.
- *
- * @property file
- */
- this.file = file;
-
- this._pInst = pInst;
+p5.File = class File {
+ constructor(file, pInst) {
+ /**
+ * Underlying File object. All normal File methods can be called on this.
+ *
+ * @property file
+ */
+ this.file = file;
+
+ this._pInst = pInst;
+
+ // Splitting out the file type into two components
+ // This makes determining if image or text etc simpler
+ const typeList = file.type.split('/');
+ /**
+ * File type (image, text, etc.)
+ *
+ * @property type
+ */
+ this.type = typeList[0];
+ /**
+ * File subtype (usually the file extension jpg, png, xml, etc.)
+ *
+ * @property subtype
+ */
+ this.subtype = typeList[1];
+ /**
+ * File name
+ *
+ * @property name
+ */
+ this.name = file.name;
+ /**
+ * File size
+ *
+ * @property size
+ */
+ this.size = file.size;
+
+ /**
+ * URL string containing either image data, the text contents of the file or
+ * a parsed object if file is JSON and p5.XML if XML
+ *
+ * @property data
+ */
+ this.data = undefined;
+ }
- // Splitting out the file type into two components
- // This makes determining if image or text etc simpler
- const typeList = file.type.split('/');
- /**
- * File type (image, text, etc.)
- *
- * @property type
- */
- this.type = typeList[0];
- /**
- * File subtype (usually the file extension jpg, png, xml, etc.)
- *
- * @property subtype
- */
- this.subtype = typeList[1];
- /**
- * File name
- *
- * @property name
- */
- this.name = file.name;
- /**
- * File size
- *
- * @property size
- */
- this.size = file.size;
- /**
- * URL string containing either image data, the text contents of the file or
- * a parsed object if file is JSON and p5.XML if XML
- *
- * @property data
- */
- this.data = undefined;
-};
+ static _createLoader(theFile, callback) {
+ const reader = new FileReader();
+ reader.onload = function (e) {
+ const p5file = new p5.File(theFile);
+ if (p5file.file.type === 'application/json') {
+ // Parse JSON and store the result in data
+ p5file.data = JSON.parse(e.target.result);
+ } else if (p5file.file.type === 'text/xml') {
+ // Parse XML, wrap it in p5.XML and store the result in data
+ const parser = new DOMParser();
+ const xml = parser.parseFromString(e.target.result, 'text/xml');
+ p5file.data = new p5.XML(xml.documentElement);
+ } else {
+ p5file.data = e.target.result;
+ }
+ callback(p5file);
+ };
+ return reader;
+ }
-p5.File._createLoader = function(theFile, callback) {
- const reader = new FileReader();
- reader.onload = function(e) {
- const p5file = new p5.File(theFile);
- if (p5file.file.type === 'application/json') {
- // Parse JSON and store the result in data
- p5file.data = JSON.parse(e.target.result);
- } else if (p5file.file.type === 'text/xml') {
- // Parse XML, wrap it in p5.XML and store the result in data
- const parser = new DOMParser();
- const xml = parser.parseFromString(e.target.result, 'text/xml');
- p5file.data = new p5.XML(xml.documentElement);
+ static _load(f, callback) {
+ // Text or data?
+ // This should likely be improved
+ if (/^text\//.test(f.type) || f.type === 'application/json') {
+ p5.File._createLoader(f, callback).readAsText(f);
+ } else if (!/^(video|audio)\//.test(f.type)) {
+ p5.File._createLoader(f, callback).readAsDataURL(f);
} else {
- p5file.data = e.target.result;
+ const file = new p5.File(f);
+ file.data = URL.createObjectURL(f);
+ callback(file);
}
- callback(p5file);
- };
- return reader;
-};
-
-p5.File._load = function(f, callback) {
- // Text or data?
- // This should likely be improved
- if (/^text\//.test(f.type) || f.type === 'application/json') {
- p5.File._createLoader(f, callback).readAsText(f);
- } else if (!/^(video|audio)\//.test(f.type)) {
- p5.File._createLoader(f, callback).readAsDataURL(f);
- } else {
- const file = new p5.File(f);
- file.data = URL.createObjectURL(f);
- callback(file);
}
};
diff --git a/src/webgl/p5.Camera.js b/src/webgl/p5.Camera.js
index bc3be9ae72..7a81de3172 100644
--- a/src/webgl/p5.Camera.js
+++ b/src/webgl/p5.Camera.js
@@ -109,7 +109,7 @@ import p5 from '../core/main';
* An interactive example of a red cube with 3 sliders for moving it across x, y,
* z axis and 3 sliders for shifting its center.
*/
-p5.prototype.camera = function(...args) {
+p5.prototype.camera = function (...args) {
this._assert3d('camera');
p5._validateParameters('camera', args);
this._renderer._curCamera.camera(...args);
@@ -173,7 +173,7 @@ p5.prototype.camera = function(...args) {
* @alt
* two colored 3D boxes move back and forth, rotating as mouse is dragged.
*/
-p5.prototype.perspective = function(...args) {
+p5.prototype.perspective = function (...args) {
this._assert3d('perspective');
p5._validateParameters('perspective', args);
this._renderer._curCamera.perspective(...args);
@@ -236,7 +236,7 @@ p5.prototype.perspective = function(...args) {
* @alt
* two 3D boxes move back and forth along same plane, rotating as mouse is dragged.
*/
-p5.prototype.ortho = function(...args) {
+p5.prototype.ortho = function (...args) {
this._assert3d('ortho');
p5._validateParameters('ortho', args);
this._renderer._curCamera.ortho(...args);
@@ -303,7 +303,7 @@ p5.prototype.ortho = function(...args) {
* @alt
* two 3D boxes move back and forth along same plane, rotating as mouse is dragged.
*/
-p5.prototype.frustum = function(...args) {
+p5.prototype.frustum = function (...args) {
this._assert3d('frustum');
p5._validateParameters('frustum', args);
this._renderer._curCamera.frustum(...args);
@@ -368,7 +368,7 @@ p5.prototype.frustum = function(...args) {
* @alt
* An example that creates a camera and moves it around the box.
*/
-p5.prototype.createCamera = function() {
+p5.prototype.createCamera = function () {
this._assert3d('createCamera');
const _cam = new p5.Camera(this._renderer);
@@ -464,16 +464,17 @@ p5.prototype.createCamera = function() {
* @alt
* camera view pans left and right across a series of rotating 3D boxes.
*/
-p5.Camera = function(renderer) {
- this._renderer = renderer;
+p5.Camera = class Camera {
+ constructor(renderer) {
+ this._renderer = renderer;
- this.cameraType = 'default';
+ this.cameraType = 'default';
- this.cameraMatrix = new p5.Matrix();
- this.projMatrix = new p5.Matrix();
- this.yScale = 1;
-};
-/**
+ this.cameraMatrix = new p5.Matrix();
+ this.projMatrix = new p5.Matrix();
+ this.yScale = 1;
+ }
+ /**
* camera position value on x axis
* @property {Number} eyeX
* @readonly
@@ -502,7 +503,7 @@ p5.Camera = function(renderer) {
*
*/
-/**
+ /**
* camera position value on y axis
* @property {Number} eyeY
* @readonly
@@ -530,7 +531,7 @@ p5.Camera = function(renderer) {
*
*/
-/**
+ /**
* camera position value on z axis
* @property {Number} eyeZ
* @readonly
@@ -558,7 +559,7 @@ p5.Camera = function(renderer) {
*
*/
-/**
+ /**
* x coordinate representing center of the sketch
* @property {Number} centerX
* @readonly
@@ -587,7 +588,7 @@ p5.Camera = function(renderer) {
*
*/
-/**
+ /**
* y coordinate representing center of the sketch
* @property {Number} centerY
* @readonly
@@ -616,7 +617,7 @@ p5.Camera = function(renderer) {
*
*/
-/**
+ /**
* z coordinate representing center of the sketch
* @property {Number} centerZ
* @readonly
@@ -645,7 +646,7 @@ p5.Camera = function(renderer) {
*
*/
-/**
+ /**
* x component of direction 'up' from camera
* @property {Number} upX
* @readonly
@@ -669,7 +670,7 @@ p5.Camera = function(renderer) {
*
*/
-/**
+ /**
* y component of direction 'up' from camera
* @property {Number} upY
* @readonly
@@ -693,7 +694,7 @@ p5.Camera = function(renderer) {
*
*/
-/**
+ /**
* z component of direction 'up' from camera
* @property {Number} upZ
* @readonly
@@ -717,11 +718,11 @@ p5.Camera = function(renderer) {
*
*/
-////////////////////////////////////////////////////////////////////////////////
-// Camera Projection Methods
-////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ // Camera Projection Methods
+ ////////////////////////////////////////////////////////////////////////////////
-/**
+ /**
* Sets a perspective projection.
* Accepts the same parameters as the global
* perspective().
@@ -766,81 +767,81 @@ p5.Camera = function(renderer) {
* @alt
* two colored 3D boxes move back and forth, rotating as mouse is dragged.
*/
-p5.Camera.prototype.perspective = function(fovy, aspect, near, far) {
- this.cameraType = arguments.length > 0 ? 'custom' : 'default';
- if (typeof fovy === 'undefined') {
- fovy = this.defaultCameraFOV;
- // this avoids issue where setting angleMode(DEGREES) before calling
- // perspective leads to a smaller than expected FOV (because
- // _computeCameraDefaultSettings computes in radians)
- this.cameraFOV = fovy;
- } else {
- this.cameraFOV = this._renderer._pInst._toRadians(fovy);
- }
- if (typeof aspect === 'undefined') {
- aspect = this.defaultAspectRatio;
- }
- if (typeof near === 'undefined') {
- near = this.defaultCameraNear;
- }
- if (typeof far === 'undefined') {
- far = this.defaultCameraFar;
- }
-
- if (near <= 0.0001) {
- near = 0.01;
- console.log(
- 'Avoid perspective near plane values close to or below 0. ' +
+ perspective(fovy, aspect, near, far) {
+ this.cameraType = arguments.length > 0 ? 'custom' : 'default';
+ if (typeof fovy === 'undefined') {
+ fovy = this.defaultCameraFOV;
+ // this avoids issue where setting angleMode(DEGREES) before calling
+ // perspective leads to a smaller than expected FOV (because
+ // _computeCameraDefaultSettings computes in radians)
+ this.cameraFOV = fovy;
+ } else {
+ this.cameraFOV = this._renderer._pInst._toRadians(fovy);
+ }
+ if (typeof aspect === 'undefined') {
+ aspect = this.defaultAspectRatio;
+ }
+ if (typeof near === 'undefined') {
+ near = this.defaultCameraNear;
+ }
+ if (typeof far === 'undefined') {
+ far = this.defaultCameraFar;
+ }
+
+ if (near <= 0.0001) {
+ near = 0.01;
+ console.log(
+ 'Avoid perspective near plane values close to or below 0. ' +
'Setting value to 0.01.'
- );
- }
+ );
+ }
- if (far < near) {
- console.log(
- 'Perspective far plane value is less than near plane value. ' +
+ if (far < near) {
+ console.log(
+ 'Perspective far plane value is less than near plane value. ' +
'Nothing will be shown.'
- );
+ );
+ }
+
+ this.aspectRatio = aspect;
+ this.cameraNear = near;
+ this.cameraFar = far;
+
+ this.projMatrix = p5.Matrix.identity();
+
+ const f = 1.0 / Math.tan(this.cameraFOV / 2);
+ const nf = 1.0 / (this.cameraNear - this.cameraFar);
+
+ /* eslint-disable indent */
+ this.projMatrix.set(f / aspect, 0, 0, 0,
+ 0, -f * this.yScale, 0, 0,
+ 0, 0, (far + near) * nf, -1,
+ 0, 0, (2 * far * near) * nf, 0);
+ /* eslint-enable indent */
+
+ if (this._isActive()) {
+ this._renderer.uPMatrix.set(
+ this.projMatrix.mat4[0],
+ this.projMatrix.mat4[1],
+ this.projMatrix.mat4[2],
+ this.projMatrix.mat4[3],
+ this.projMatrix.mat4[4],
+ this.projMatrix.mat4[5],
+ this.projMatrix.mat4[6],
+ this.projMatrix.mat4[7],
+ this.projMatrix.mat4[8],
+ this.projMatrix.mat4[9],
+ this.projMatrix.mat4[10],
+ this.projMatrix.mat4[11],
+ this.projMatrix.mat4[12],
+ this.projMatrix.mat4[13],
+ this.projMatrix.mat4[14],
+ this.projMatrix.mat4[15]
+ );
+ }
}
- this.aspectRatio = aspect;
- this.cameraNear = near;
- this.cameraFar = far;
-
- this.projMatrix = p5.Matrix.identity();
-
- const f = 1.0 / Math.tan(this.cameraFOV / 2);
- const nf = 1.0 / (this.cameraNear - this.cameraFar);
-
- /* eslint-disable indent */
- this.projMatrix.set(f / aspect, 0, 0, 0,
- 0, -f * this.yScale, 0, 0,
- 0, 0, (far + near) * nf, -1,
- 0, 0, (2 * far * near) * nf, 0);
- /* eslint-enable indent */
-
- if (this._isActive()) {
- this._renderer.uPMatrix.set(
- this.projMatrix.mat4[0],
- this.projMatrix.mat4[1],
- this.projMatrix.mat4[2],
- this.projMatrix.mat4[3],
- this.projMatrix.mat4[4],
- this.projMatrix.mat4[5],
- this.projMatrix.mat4[6],
- this.projMatrix.mat4[7],
- this.projMatrix.mat4[8],
- this.projMatrix.mat4[9],
- this.projMatrix.mat4[10],
- this.projMatrix.mat4[11],
- this.projMatrix.mat4[12],
- this.projMatrix.mat4[13],
- this.projMatrix.mat4[14],
- this.projMatrix.mat4[15]
- );
- }
-};
-
-/**
+ /**
* Sets an orthographic projection.
* Accepts the same parameters as the global
* ortho().
@@ -883,64 +884,64 @@ p5.Camera.prototype.perspective = function(fovy, aspect, near, far) {
* @alt
* two 3D boxes move back and forth along same plane, rotating as mouse is dragged.
*/
-p5.Camera.prototype.ortho = function(left, right, bottom, top, near, far) {
- if (left === undefined) left = -this._renderer.width / 2;
- if (right === undefined) right = +this._renderer.width / 2;
- if (bottom === undefined) bottom = -this._renderer.height / 2;
- if (top === undefined) top = +this._renderer.height / 2;
- if (near === undefined) near = 0;
- if (far === undefined)
- far = Math.max(this._renderer.width, this._renderer.height);
-
- this.cameraNear = near;
- this.cameraFar = far;
-
- const w = right - left;
- const h = top - bottom;
- const d = far - near;
-
- const x = +2.0 / w;
- const y = +2.0 / h * this.yScale;
- const z = -2.0 / d;
-
- const tx = -(right + left) / w;
- const ty = -(top + bottom) / h;
- const tz = -(far + near) / d;
-
- this.projMatrix = p5.Matrix.identity();
-
- /* eslint-disable indent */
- this.projMatrix.set( x, 0, 0, 0,
- 0, -y, 0, 0,
- 0, 0, z, 0,
- tx, ty, tz, 1);
- /* eslint-enable indent */
-
- if (this._isActive()) {
- this._renderer.uPMatrix.set(
- this.projMatrix.mat4[0],
- this.projMatrix.mat4[1],
- this.projMatrix.mat4[2],
- this.projMatrix.mat4[3],
- this.projMatrix.mat4[4],
- this.projMatrix.mat4[5],
- this.projMatrix.mat4[6],
- this.projMatrix.mat4[7],
- this.projMatrix.mat4[8],
- this.projMatrix.mat4[9],
- this.projMatrix.mat4[10],
- this.projMatrix.mat4[11],
- this.projMatrix.mat4[12],
- this.projMatrix.mat4[13],
- this.projMatrix.mat4[14],
- this.projMatrix.mat4[15]
- );
+ ortho(left, right, bottom, top, near, far) {
+ if (left === undefined) left = -this._renderer.width / 2;
+ if (right === undefined) right = +this._renderer.width / 2;
+ if (bottom === undefined) bottom = -this._renderer.height / 2;
+ if (top === undefined) top = +this._renderer.height / 2;
+ if (near === undefined) near = 0;
+ if (far === undefined)
+ far = Math.max(this._renderer.width, this._renderer.height);
+
+ this.cameraNear = near;
+ this.cameraFar = far;
+
+ const w = right - left;
+ const h = top - bottom;
+ const d = far - near;
+
+ const x = +2.0 / w;
+ const y = +2.0 / h * this.yScale;
+ const z = -2.0 / d;
+
+ const tx = -(right + left) / w;
+ const ty = -(top + bottom) / h;
+ const tz = -(far + near) / d;
+
+ this.projMatrix = p5.Matrix.identity();
+
+ /* eslint-disable indent */
+ this.projMatrix.set( x, 0, 0, 0,
+ 0, -y, 0, 0,
+ 0, 0, z, 0,
+ tx, ty, tz, 1);
+ /* eslint-enable indent */
+
+ if (this._isActive()) {
+ this._renderer.uPMatrix.set(
+ this.projMatrix.mat4[0],
+ this.projMatrix.mat4[1],
+ this.projMatrix.mat4[2],
+ this.projMatrix.mat4[3],
+ this.projMatrix.mat4[4],
+ this.projMatrix.mat4[5],
+ this.projMatrix.mat4[6],
+ this.projMatrix.mat4[7],
+ this.projMatrix.mat4[8],
+ this.projMatrix.mat4[9],
+ this.projMatrix.mat4[10],
+ this.projMatrix.mat4[11],
+ this.projMatrix.mat4[12],
+ this.projMatrix.mat4[13],
+ this.projMatrix.mat4[14],
+ this.projMatrix.mat4[15]
+ );
+ }
+
+ this.cameraType = 'custom';
}
- this.cameraType = 'custom';
-};
-
-/**
+ /**
* Sets the camera's frustum.
* Accepts the same parameters as the global
* frustum().
@@ -982,112 +983,112 @@ p5.Camera.prototype.ortho = function(left, right, bottom, top, near, far) {
* @alt
* two 3D boxes move back and forth along same plane, rotating as mouse is dragged.
*/
-p5.Camera.prototype.frustum = function(left, right, bottom, top, near, far) {
- if (left === undefined) left = -this._renderer.width * 0.05;
- if (right === undefined) right = +this._renderer.width * 0.05;
- if (bottom === undefined) bottom = +this._renderer.height * 0.05;
- if (top === undefined) top = -this._renderer.height * 0.05;
- if (near === undefined) near = this.defaultCameraNear;
- if (far === undefined) far = this.defaultCameraFar;
-
- this.cameraNear = near;
- this.cameraFar = far;
-
- const w = right - left;
- const h = top - bottom;
- const d = far - near;
-
- const x = +(2.0 * near) / w;
- const y = +(2.0 * near) / h * this.yScale;
- const z = -(2.0 * far * near) / d;
-
- const tx = (right + left) / w;
- const ty = (top + bottom) / h;
- const tz = -(far + near) / d;
-
- this.projMatrix = p5.Matrix.identity();
-
- /* eslint-disable indent */
- this.projMatrix.set( x, 0, 0, 0,
- 0, -y, 0, 0,
- tx, ty, tz, -1,
- 0, 0, z, 0);
- /* eslint-enable indent */
-
- if (this._isActive()) {
- this._renderer.uPMatrix.set(
- this.projMatrix.mat4[0],
- this.projMatrix.mat4[1],
- this.projMatrix.mat4[2],
- this.projMatrix.mat4[3],
- this.projMatrix.mat4[4],
- this.projMatrix.mat4[5],
- this.projMatrix.mat4[6],
- this.projMatrix.mat4[7],
- this.projMatrix.mat4[8],
- this.projMatrix.mat4[9],
- this.projMatrix.mat4[10],
- this.projMatrix.mat4[11],
- this.projMatrix.mat4[12],
- this.projMatrix.mat4[13],
- this.projMatrix.mat4[14],
- this.projMatrix.mat4[15]
- );
+ frustum(left, right, bottom, top, near, far) {
+ if (left === undefined) left = -this._renderer.width * 0.05;
+ if (right === undefined) right = +this._renderer.width * 0.05;
+ if (bottom === undefined) bottom = +this._renderer.height * 0.05;
+ if (top === undefined) top = -this._renderer.height * 0.05;
+ if (near === undefined) near = this.defaultCameraNear;
+ if (far === undefined) far = this.defaultCameraFar;
+
+ this.cameraNear = near;
+ this.cameraFar = far;
+
+ const w = right - left;
+ const h = top - bottom;
+ const d = far - near;
+
+ const x = +(2.0 * near) / w;
+ const y = +(2.0 * near) / h * this.yScale;
+ const z = -(2.0 * far * near) / d;
+
+ const tx = (right + left) / w;
+ const ty = (top + bottom) / h;
+ const tz = -(far + near) / d;
+
+ this.projMatrix = p5.Matrix.identity();
+
+ /* eslint-disable indent */
+ this.projMatrix.set( x, 0, 0, 0,
+ 0, -y, 0, 0,
+ tx, ty, tz, -1,
+ 0, 0, z, 0);
+ /* eslint-enable indent */
+
+ if (this._isActive()) {
+ this._renderer.uPMatrix.set(
+ this.projMatrix.mat4[0],
+ this.projMatrix.mat4[1],
+ this.projMatrix.mat4[2],
+ this.projMatrix.mat4[3],
+ this.projMatrix.mat4[4],
+ this.projMatrix.mat4[5],
+ this.projMatrix.mat4[6],
+ this.projMatrix.mat4[7],
+ this.projMatrix.mat4[8],
+ this.projMatrix.mat4[9],
+ this.projMatrix.mat4[10],
+ this.projMatrix.mat4[11],
+ this.projMatrix.mat4[12],
+ this.projMatrix.mat4[13],
+ this.projMatrix.mat4[14],
+ this.projMatrix.mat4[15]
+ );
+ }
+
+ this.cameraType = 'custom';
}
- this.cameraType = 'custom';
-};
-
-////////////////////////////////////////////////////////////////////////////////
-// Camera Orientation Methods
-////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ // Camera Orientation Methods
+ ////////////////////////////////////////////////////////////////////////////////
-/**
+ /**
* Rotate camera view about arbitrary axis defined by x,y,z
* based on http://learnwebgl.brown37.net/07_cameras/camera_rotating_motion.html
* @method _rotateView
* @private
*/
-p5.Camera.prototype._rotateView = function(a, x, y, z) {
- let centerX = this.centerX;
- let centerY = this.centerY;
- let centerZ = this.centerZ;
-
- // move center by eye position such that rotation happens around eye position
- centerX -= this.eyeX;
- centerY -= this.eyeY;
- centerZ -= this.eyeZ;
-
- const rotation = p5.Matrix.identity(this._renderer._pInst);
- rotation.rotate(this._renderer._pInst._toRadians(a), x, y, z);
-
- /* eslint-disable max-len */
- const rotatedCenter = [
- centerX * rotation.mat4[0] + centerY * rotation.mat4[4] + centerZ * rotation.mat4[8],
- centerX * rotation.mat4[1] + centerY * rotation.mat4[5] + centerZ * rotation.mat4[9],
- centerX * rotation.mat4[2] + centerY * rotation.mat4[6] + centerZ * rotation.mat4[10]
- ];
- /* eslint-enable max-len */
-
- // add eye position back into center
- rotatedCenter[0] += this.eyeX;
- rotatedCenter[1] += this.eyeY;
- rotatedCenter[2] += this.eyeZ;
-
- this.camera(
- this.eyeX,
- this.eyeY,
- this.eyeZ,
- rotatedCenter[0],
- rotatedCenter[1],
- rotatedCenter[2],
- this.upX,
- this.upY,
- this.upZ
- );
-};
+ _rotateView(a, x, y, z) {
+ let centerX = this.centerX;
+ let centerY = this.centerY;
+ let centerZ = this.centerZ;
+
+ // move center by eye position such that rotation happens around eye position
+ centerX -= this.eyeX;
+ centerY -= this.eyeY;
+ centerZ -= this.eyeZ;
+
+ const rotation = p5.Matrix.identity(this._renderer._pInst);
+ rotation.rotate(this._renderer._pInst._toRadians(a), x, y, z);
+
+ /* eslint-disable max-len */
+ const rotatedCenter = [
+ centerX * rotation.mat4[0] + centerY * rotation.mat4[4] + centerZ * rotation.mat4[8],
+ centerX * rotation.mat4[1] + centerY * rotation.mat4[5] + centerZ * rotation.mat4[9],
+ centerX * rotation.mat4[2] + centerY * rotation.mat4[6] + centerZ * rotation.mat4[10]
+ ];
+ /* eslint-enable max-len */
+
+ // add eye position back into center
+ rotatedCenter[0] += this.eyeX;
+ rotatedCenter[1] += this.eyeY;
+ rotatedCenter[2] += this.eyeZ;
+
+ this.camera(
+ this.eyeX,
+ this.eyeY,
+ this.eyeZ,
+ rotatedCenter[0],
+ rotatedCenter[1],
+ rotatedCenter[2],
+ this.upX,
+ this.upY,
+ this.upZ
+ );
+ }
-/**
+ /**
* Panning rotates the camera view to the left and right.
* @method pan
* @param {Number} angle amount to rotate camera in current
@@ -1140,12 +1141,12 @@ p5.Camera.prototype._rotateView = function(a, x, y, z) {
* @alt
* camera view pans left and right across a series of rotating 3D boxes.
*/
-p5.Camera.prototype.pan = function(amount) {
- const local = this._getLocalAxes();
- this._rotateView(amount, local.y[0], local.y[1], local.y[2]);
-};
+ pan(amount) {
+ const local = this._getLocalAxes();
+ this._rotateView(amount, local.y[0], local.y[1], local.y[2]);
+ }
-/**
+ /**
* Tilting rotates the camera view up and down.
* @method tilt
* @param {Number} angle amount to rotate camera in current
@@ -1198,12 +1199,12 @@ p5.Camera.prototype.pan = function(amount) {
* @alt
* camera view tilts up and down across a series of rotating 3D boxes.
*/
-p5.Camera.prototype.tilt = function(amount) {
- const local = this._getLocalAxes();
- this._rotateView(amount, local.x[0], local.x[1], local.x[2]);
-};
+ tilt(amount) {
+ const local = this._getLocalAxes();
+ this._rotateView(amount, local.x[0], local.x[1], local.x[2]);
+ }
-/**
+ /**
* Reorients the camera to look at a position in world space.
* @method lookAt
* @for p5.Camera
@@ -1252,25 +1253,25 @@ p5.Camera.prototype.tilt = function(amount) {
* camera view of rotating 3D cubes changes to look at a new random
* point every second .
*/
-p5.Camera.prototype.lookAt = function(x, y, z) {
- this.camera(
- this.eyeX,
- this.eyeY,
- this.eyeZ,
- x,
- y,
- z,
- this.upX,
- this.upY,
- this.upZ
- );
-};
+ lookAt(x, y, z) {
+ this.camera(
+ this.eyeX,
+ this.eyeY,
+ this.eyeZ,
+ x,
+ y,
+ z,
+ this.upX,
+ this.upY,
+ this.upZ
+ );
+ }
-////////////////////////////////////////////////////////////////////////////////
-// Camera Position Methods
-////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ // Camera Position Methods
+ ////////////////////////////////////////////////////////////////////////////////
-/**
+ /**
* Sets the camera's position and orientation.
* Accepts the same parameters as the global
* camera().
@@ -1354,87 +1355,87 @@ p5.Camera.prototype.lookAt = function(x, y, z) {
* An interactive example of a red cube with 3 sliders for moving it across x, y,
* z axis and 3 sliders for shifting its center.
*/
-p5.Camera.prototype.camera = function(
- eyeX,
- eyeY,
- eyeZ,
- centerX,
- centerY,
- centerZ,
- upX,
- upY,
- upZ
-) {
- if (typeof eyeX === 'undefined') {
- eyeX = this.defaultEyeX;
- eyeY = this.defaultEyeY;
- eyeZ = this.defaultEyeZ;
- centerX = eyeX;
- centerY = eyeY;
- centerZ = 0;
- upX = 0;
- upY = 1;
- upZ = 0;
+ camera(
+ eyeX,
+ eyeY,
+ eyeZ,
+ centerX,
+ centerY,
+ centerZ,
+ upX,
+ upY,
+ upZ
+ ) {
+ if (typeof eyeX === 'undefined') {
+ eyeX = this.defaultEyeX;
+ eyeY = this.defaultEyeY;
+ eyeZ = this.defaultEyeZ;
+ centerX = eyeX;
+ centerY = eyeY;
+ centerZ = 0;
+ upX = 0;
+ upY = 1;
+ upZ = 0;
+ }
+
+ this.eyeX = eyeX;
+ this.eyeY = eyeY;
+ this.eyeZ = eyeZ;
+
+ if (typeof centerX !== 'undefined') {
+ this.centerX = centerX;
+ this.centerY = centerY;
+ this.centerZ = centerZ;
+ }
+
+ if (typeof upX !== 'undefined') {
+ this.upX = upX;
+ this.upY = upY;
+ this.upZ = upZ;
+ }
+
+ const local = this._getLocalAxes();
+
+ // the camera affects the model view matrix, insofar as it
+ // inverse translates the world to the eye position of the camera
+ // and rotates it.
+ /* eslint-disable indent */
+ this.cameraMatrix.set(local.x[0], local.y[0], local.z[0], 0,
+ local.x[1], local.y[1], local.z[1], 0,
+ local.x[2], local.y[2], local.z[2], 0,
+ 0, 0, 0, 1);
+ /* eslint-enable indent */
+
+ const tx = -eyeX;
+ const ty = -eyeY;
+ const tz = -eyeZ;
+
+ this.cameraMatrix.translate([tx, ty, tz]);
+
+ if (this._isActive()) {
+ this._renderer.uMVMatrix.set(
+ this.cameraMatrix.mat4[0],
+ this.cameraMatrix.mat4[1],
+ this.cameraMatrix.mat4[2],
+ this.cameraMatrix.mat4[3],
+ this.cameraMatrix.mat4[4],
+ this.cameraMatrix.mat4[5],
+ this.cameraMatrix.mat4[6],
+ this.cameraMatrix.mat4[7],
+ this.cameraMatrix.mat4[8],
+ this.cameraMatrix.mat4[9],
+ this.cameraMatrix.mat4[10],
+ this.cameraMatrix.mat4[11],
+ this.cameraMatrix.mat4[12],
+ this.cameraMatrix.mat4[13],
+ this.cameraMatrix.mat4[14],
+ this.cameraMatrix.mat4[15]
+ );
+ }
+ return this;
}
- this.eyeX = eyeX;
- this.eyeY = eyeY;
- this.eyeZ = eyeZ;
-
- if (typeof centerX !== 'undefined') {
- this.centerX = centerX;
- this.centerY = centerY;
- this.centerZ = centerZ;
- }
-
- if (typeof upX !== 'undefined') {
- this.upX = upX;
- this.upY = upY;
- this.upZ = upZ;
- }
-
- const local = this._getLocalAxes();
-
- // the camera affects the model view matrix, insofar as it
- // inverse translates the world to the eye position of the camera
- // and rotates it.
- /* eslint-disable indent */
- this.cameraMatrix.set(local.x[0], local.y[0], local.z[0], 0,
- local.x[1], local.y[1], local.z[1], 0,
- local.x[2], local.y[2], local.z[2], 0,
- 0, 0, 0, 1);
- /* eslint-enable indent */
-
- const tx = -eyeX;
- const ty = -eyeY;
- const tz = -eyeZ;
-
- this.cameraMatrix.translate([tx, ty, tz]);
-
- if (this._isActive()) {
- this._renderer.uMVMatrix.set(
- this.cameraMatrix.mat4[0],
- this.cameraMatrix.mat4[1],
- this.cameraMatrix.mat4[2],
- this.cameraMatrix.mat4[3],
- this.cameraMatrix.mat4[4],
- this.cameraMatrix.mat4[5],
- this.cameraMatrix.mat4[6],
- this.cameraMatrix.mat4[7],
- this.cameraMatrix.mat4[8],
- this.cameraMatrix.mat4[9],
- this.cameraMatrix.mat4[10],
- this.cameraMatrix.mat4[11],
- this.cameraMatrix.mat4[12],
- this.cameraMatrix.mat4[13],
- this.cameraMatrix.mat4[14],
- this.cameraMatrix.mat4[15]
- );
- }
- return this;
-};
-
-/**
+ /**
* Move camera along its local axes while maintaining current camera orientation.
* @method move
* @param {Number} x amount to move along camera's left-right axis
@@ -1484,29 +1485,29 @@ p5.Camera.prototype.camera = function(
* camera view moves along a series of 3D boxes, maintaining the same
* orientation throughout the move
*/
-p5.Camera.prototype.move = function(x, y, z) {
- const local = this._getLocalAxes();
-
- // scale local axes by movement amounts
- // based on http://learnwebgl.brown37.net/07_cameras/camera_linear_motion.html
- const dx = [local.x[0] * x, local.x[1] * x, local.x[2] * x];
- const dy = [local.y[0] * y, local.y[1] * y, local.y[2] * y];
- const dz = [local.z[0] * z, local.z[1] * z, local.z[2] * z];
-
- this.camera(
- this.eyeX + dx[0] + dy[0] + dz[0],
- this.eyeY + dx[1] + dy[1] + dz[1],
- this.eyeZ + dx[2] + dy[2] + dz[2],
- this.centerX + dx[0] + dy[0] + dz[0],
- this.centerY + dx[1] + dy[1] + dz[1],
- this.centerZ + dx[2] + dy[2] + dz[2],
- this.upX,
- this.upY,
- this.upZ
- );
-};
+ move(x, y, z) {
+ const local = this._getLocalAxes();
+
+ // scale local axes by movement amounts
+ // based on http://learnwebgl.brown37.net/07_cameras/camera_linear_motion.html
+ const dx = [local.x[0] * x, local.x[1] * x, local.x[2] * x];
+ const dy = [local.y[0] * y, local.y[1] * y, local.y[2] * y];
+ const dz = [local.z[0] * z, local.z[1] * z, local.z[2] * z];
+
+ this.camera(
+ this.eyeX + dx[0] + dy[0] + dz[0],
+ this.eyeY + dx[1] + dy[1] + dz[1],
+ this.eyeZ + dx[2] + dy[2] + dz[2],
+ this.centerX + dx[0] + dy[0] + dz[0],
+ this.centerY + dx[1] + dy[1] + dz[1],
+ this.centerZ + dx[2] + dy[2] + dz[2],
+ this.upX,
+ this.upY,
+ this.upZ
+ );
+ }
-/**
+ /**
* Set camera position in world-space while maintaining current camera
* orientation.
* @method setPosition
@@ -1550,168 +1551,168 @@ p5.Camera.prototype.move = function(x, y, z) {
* @alt
* camera position changes as the user presses keys, altering view of a 3D box
*/
-p5.Camera.prototype.setPosition = function(x, y, z) {
- const diffX = x - this.eyeX;
- const diffY = y - this.eyeY;
- const diffZ = z - this.eyeZ;
-
- this.camera(
- x,
- y,
- z,
- this.centerX + diffX,
- this.centerY + diffY,
- this.centerZ + diffZ,
- this.upX,
- this.upY,
- this.upZ
- );
-};
-
-////////////////////////////////////////////////////////////////////////////////
-// Camera Helper Methods
-////////////////////////////////////////////////////////////////////////////////
+ setPosition(x, y, z) {
+ const diffX = x - this.eyeX;
+ const diffY = y - this.eyeY;
+ const diffZ = z - this.eyeZ;
+
+ this.camera(
+ x,
+ y,
+ z,
+ this.centerX + diffX,
+ this.centerY + diffY,
+ this.centerZ + diffZ,
+ this.upX,
+ this.upY,
+ this.upZ
+ );
+ }
-// @TODO: combine this function with _setDefaultCamera to compute these values
-// as-needed
-p5.Camera.prototype._computeCameraDefaultSettings = function() {
- this.defaultCameraFOV = 60 / 180 * Math.PI;
- this.defaultAspectRatio = this._renderer.width / this._renderer.height;
- this.defaultEyeX = 0;
- this.defaultEyeY = 0;
- this.defaultEyeZ =
- this._renderer.height / 2.0 / Math.tan(this.defaultCameraFOV / 2.0);
- this.defaultCenterX = 0;
- this.defaultCenterY = 0;
- this.defaultCenterZ = 0;
- this.defaultCameraNear = this.defaultEyeZ * 0.1;
- this.defaultCameraFar = this.defaultEyeZ * 10;
-};
+ ////////////////////////////////////////////////////////////////////////////////
+ // Camera Helper Methods
+ ////////////////////////////////////////////////////////////////////////////////
+
+ // @TODO: combine this function with _setDefaultCamera to compute these values
+ // as-needed
+ _computeCameraDefaultSettings() {
+ this.defaultCameraFOV = 60 / 180 * Math.PI;
+ this.defaultAspectRatio = this._renderer.width / this._renderer.height;
+ this.defaultEyeX = 0;
+ this.defaultEyeY = 0;
+ this.defaultEyeZ =
+ this._renderer.height / 2.0 / Math.tan(this.defaultCameraFOV / 2.0);
+ this.defaultCenterX = 0;
+ this.defaultCenterY = 0;
+ this.defaultCenterZ = 0;
+ this.defaultCameraNear = this.defaultEyeZ * 0.1;
+ this.defaultCameraFar = this.defaultEyeZ * 10;
+ }
-//detect if user didn't set the camera
-//then call this function below
-p5.Camera.prototype._setDefaultCamera = function() {
- this.cameraFOV = this.defaultCameraFOV;
- this.aspectRatio = this.defaultAspectRatio;
- this.eyeX = this.defaultEyeX;
- this.eyeY = this.defaultEyeY;
- this.eyeZ = this.defaultEyeZ;
- this.centerX = this.defaultCenterX;
- this.centerY = this.defaultCenterY;
- this.centerZ = this.defaultCenterZ;
- this.upX = 0;
- this.upY = 1;
- this.upZ = 0;
- this.cameraNear = this.defaultCameraNear;
- this.cameraFar = this.defaultCameraFar;
-
- this.perspective();
- this.camera();
-
- this.cameraType = 'default';
-};
+ //detect if user didn't set the camera
+ //then call this function below
+ _setDefaultCamera() {
+ this.cameraFOV = this.defaultCameraFOV;
+ this.aspectRatio = this.defaultAspectRatio;
+ this.eyeX = this.defaultEyeX;
+ this.eyeY = this.defaultEyeY;
+ this.eyeZ = this.defaultEyeZ;
+ this.centerX = this.defaultCenterX;
+ this.centerY = this.defaultCenterY;
+ this.centerZ = this.defaultCenterZ;
+ this.upX = 0;
+ this.upY = 1;
+ this.upZ = 0;
+ this.cameraNear = this.defaultCameraNear;
+ this.cameraFar = this.defaultCameraFar;
+
+ this.perspective();
+ this.camera();
+
+ this.cameraType = 'default';
+ }
-p5.Camera.prototype._resize = function() {
- // If we're using the default camera, update the aspect ratio
- if (this.cameraType === 'default') {
- this._computeCameraDefaultSettings();
- this._setDefaultCamera();
- } else {
- this.perspective(
- this.cameraFOV,
- this._renderer.width / this._renderer.height
- );
+ _resize() {
+ // If we're using the default camera, update the aspect ratio
+ if (this.cameraType === 'default') {
+ this._computeCameraDefaultSettings();
+ this._setDefaultCamera();
+ } else {
+ this.perspective(
+ this.cameraFOV,
+ this._renderer.width / this._renderer.height
+ );
+ }
}
-};
-/**
+ /**
* Returns a copy of a camera.
* @method copy
* @private
*/
-p5.Camera.prototype.copy = function() {
- const _cam = new p5.Camera(this._renderer);
- _cam.cameraFOV = this.cameraFOV;
- _cam.aspectRatio = this.aspectRatio;
- _cam.eyeX = this.eyeX;
- _cam.eyeY = this.eyeY;
- _cam.eyeZ = this.eyeZ;
- _cam.centerX = this.centerX;
- _cam.centerY = this.centerY;
- _cam.centerZ = this.centerZ;
- _cam.upX = this.upX;
- _cam.upY = this.upY;
- _cam.upZ = this.upZ;
- _cam.cameraNear = this.cameraNear;
- _cam.cameraFar = this.cameraFar;
-
- _cam.cameraType = this.cameraType;
-
- _cam.cameraMatrix = this.cameraMatrix.copy();
- _cam.projMatrix = this.projMatrix.copy();
-
- return _cam;
-};
+ copy() {
+ const _cam = new p5.Camera(this._renderer);
+ _cam.cameraFOV = this.cameraFOV;
+ _cam.aspectRatio = this.aspectRatio;
+ _cam.eyeX = this.eyeX;
+ _cam.eyeY = this.eyeY;
+ _cam.eyeZ = this.eyeZ;
+ _cam.centerX = this.centerX;
+ _cam.centerY = this.centerY;
+ _cam.centerZ = this.centerZ;
+ _cam.upX = this.upX;
+ _cam.upY = this.upY;
+ _cam.upZ = this.upZ;
+ _cam.cameraNear = this.cameraNear;
+ _cam.cameraFar = this.cameraFar;
+
+ _cam.cameraType = this.cameraType;
+
+ _cam.cameraMatrix = this.cameraMatrix.copy();
+ _cam.projMatrix = this.projMatrix.copy();
+
+ return _cam;
+ }
-/**
+ /**
* Returns a camera's local axes: left-right, up-down, and forward-backward,
* as defined by vectors in world-space.
* @method _getLocalAxes
* @private
*/
-p5.Camera.prototype._getLocalAxes = function() {
- // calculate camera local Z vector
- let z0 = this.eyeX - this.centerX;
- let z1 = this.eyeY - this.centerY;
- let z2 = this.eyeZ - this.centerZ;
-
- // normalize camera local Z vector
- const eyeDist = Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
- if (eyeDist !== 0) {
- z0 /= eyeDist;
- z1 /= eyeDist;
- z2 /= eyeDist;
- }
-
- // calculate camera Y vector
- let y0 = this.upX;
- let y1 = this.upY;
- let y2 = this.upZ;
-
- // compute camera local X vector as up vector (local Y) cross local Z
- let x0 = y1 * z2 - y2 * z1;
- let x1 = -y0 * z2 + y2 * z0;
- let x2 = y0 * z1 - y1 * z0;
-
- // recompute y = z cross x
- y0 = z1 * x2 - z2 * x1;
- y1 = -z0 * x2 + z2 * x0;
- y2 = z0 * x1 - z1 * x0;
-
- // cross product gives area of parallelogram, which is < 1.0 for
- // non-perpendicular unit-length vectors; so normalize x, y here:
- const xmag = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
- if (xmag !== 0) {
- x0 /= xmag;
- x1 /= xmag;
- x2 /= xmag;
+ _getLocalAxes() {
+ // calculate camera local Z vector
+ let z0 = this.eyeX - this.centerX;
+ let z1 = this.eyeY - this.centerY;
+ let z2 = this.eyeZ - this.centerZ;
+
+ // normalize camera local Z vector
+ const eyeDist = Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
+ if (eyeDist !== 0) {
+ z0 /= eyeDist;
+ z1 /= eyeDist;
+ z2 /= eyeDist;
+ }
+
+ // calculate camera Y vector
+ let y0 = this.upX;
+ let y1 = this.upY;
+ let y2 = this.upZ;
+
+ // compute camera local X vector as up vector (local Y) cross local Z
+ let x0 = y1 * z2 - y2 * z1;
+ let x1 = -y0 * z2 + y2 * z0;
+ let x2 = y0 * z1 - y1 * z0;
+
+ // recompute y = z cross x
+ y0 = z1 * x2 - z2 * x1;
+ y1 = -z0 * x2 + z2 * x0;
+ y2 = z0 * x1 - z1 * x0;
+
+ // cross product gives area of parallelogram, which is < 1.0 for
+ // non-perpendicular unit-length vectors; so normalize x, y here:
+ const xmag = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
+ if (xmag !== 0) {
+ x0 /= xmag;
+ x1 /= xmag;
+ x2 /= xmag;
+ }
+
+ const ymag = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
+ if (ymag !== 0) {
+ y0 /= ymag;
+ y1 /= ymag;
+ y2 /= ymag;
+ }
+
+ return {
+ x: [x0, x1, x2],
+ y: [y0, y1, y2],
+ z: [z0, z1, z2]
+ };
}
- const ymag = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
- if (ymag !== 0) {
- y0 /= ymag;
- y1 /= ymag;
- y2 /= ymag;
- }
-
- return {
- x: [x0, x1, x2],
- y: [y0, y1, y2],
- z: [z0, z1, z2]
- };
-};
-
-/**
+ /**
* Orbits the camera about center point. For use with orbitControl().
* @method _orbit
* @private
@@ -1719,74 +1720,75 @@ p5.Camera.prototype._getLocalAxes = function() {
* @param {Number} dPhi change in spherical coordinate phi
* @param {Number} dRadius change in radius
*/
-p5.Camera.prototype._orbit = function(dTheta, dPhi, dRadius) {
- // Calculate the vector and its magnitude from the center to the viewpoint
- const diffX = this.eyeX - this.centerX;
- const diffY = this.eyeY - this.centerY;
- const diffZ = this.eyeZ - this.centerZ;
- let camRadius = Math.hypot(diffX, diffY, diffZ);
- // front vector. unit vector from center to eye.
- const front = new p5.Vector(diffX, diffY, diffZ).normalize();
- // up vector. normalized camera's up vector.
- const up = new p5.Vector(this.upX, this.upY, this.upZ).normalize(); // y-axis
- // side vector. Right when viewed from the front
- const side = new p5.Vector.cross(up, front).normalize(); // x-axis
- // vertical vector. normalized vector of projection of front vector.
- const vertical = new p5.Vector.cross(side, up); // z-axis
-
- // update camRadius
- camRadius *= Math.pow(10, dRadius);
- // prevent zooming through the center:
- if (camRadius < this.cameraNear) {
- camRadius = this.cameraNear;
- }
- if (camRadius > this.cameraFar) {
- camRadius = this.cameraFar;
- }
-
- // calculate updated camera angle
- // Find the angle between the "up" and the "front", add dPhi to that.
- // angleBetween() may return negative value. Since this specification is subject to change
- // due to version updates, it cannot be adopted, so here we calculate using a method
- // that directly obtains the absolute value.
- const camPhi =
- Math.acos(Math.max(-1, Math.min(1, p5.Vector.dot(front, up)))) + dPhi;
- // Rotate by dTheta in the shortest direction from "vertical" to "side"
- const camTheta = dTheta;
-
- // Invert camera's upX, upY, upZ if dPhi is below 0 or above PI
- if(camPhi <= 0 || camPhi >= Math.PI){
- this.upX *= -1;
- this.upY *= -1;
- this.upZ *= -1;
+ _orbit(dTheta, dPhi, dRadius) {
+ // Calculate the vector and its magnitude from the center to the viewpoint
+ const diffX = this.eyeX - this.centerX;
+ const diffY = this.eyeY - this.centerY;
+ const diffZ = this.eyeZ - this.centerZ;
+ let camRadius = Math.hypot(diffX, diffY, diffZ);
+ // front vector. unit vector from center to eye.
+ const front = new p5.Vector(diffX, diffY, diffZ).normalize();
+ // up vector. normalized camera's up vector.
+ const up = new p5.Vector(this.upX, this.upY, this.upZ).normalize(); // y-axis
+ // side vector. Right when viewed from the front
+ const side = new p5.Vector.cross(up, front).normalize(); // x-axis
+ // vertical vector. normalized vector of projection of front vector.
+ const vertical = new p5.Vector.cross(side, up); // z-axis
+
+ // update camRadius
+ camRadius *= Math.pow(10, dRadius);
+ // prevent zooming through the center:
+ if (camRadius < this.cameraNear) {
+ camRadius = this.cameraNear;
+ }
+ if (camRadius > this.cameraFar) {
+ camRadius = this.cameraFar;
+ }
+
+ // calculate updated camera angle
+ // Find the angle between the "up" and the "front", add dPhi to that.
+ // angleBetween() may return negative value. Since this specification is subject to change
+ // due to version updates, it cannot be adopted, so here we calculate using a method
+ // that directly obtains the absolute value.
+ const camPhi =
+ Math.acos(Math.max(-1, Math.min(1, p5.Vector.dot(front, up)))) + dPhi;
+ // Rotate by dTheta in the shortest direction from "vertical" to "side"
+ const camTheta = dTheta;
+
+ // Invert camera's upX, upY, upZ if dPhi is below 0 or above PI
+ if (camPhi <= 0 || camPhi >= Math.PI) {
+ this.upX *= -1;
+ this.upY *= -1;
+ this.upZ *= -1;
+ }
+
+ // update eye vector by calculate new front vector
+ up.mult(Math.cos(camPhi));
+ vertical.mult(Math.cos(camTheta) * Math.sin(camPhi));
+ side.mult(Math.sin(camTheta) * Math.sin(camPhi));
+
+ front.set(up).add(vertical).add(side);
+
+ this.eyeX = camRadius * front.x + this.centerX;
+ this.eyeY = camRadius * front.y + this.centerY;
+ this.eyeZ = camRadius * front.z + this.centerZ;
+
+ // update camera
+ this.camera(
+ this.eyeX, this.eyeY, this.eyeZ,
+ this.centerX, this.centerY, this.centerZ,
+ this.upX, this.upY, this.upZ
+ );
}
- // update eye vector by calculate new front vector
- up.mult(Math.cos(camPhi));
- vertical.mult(Math.cos(camTheta) * Math.sin(camPhi));
- side.mult(Math.sin(camTheta) * Math.sin(camPhi));
-
- front.set(up).add(vertical).add(side);
-
- this.eyeX = camRadius * front.x + this.centerX;
- this.eyeY = camRadius * front.y + this.centerY;
- this.eyeZ = camRadius * front.z + this.centerZ;
-
- // update camera
- this.camera(
- this.eyeX, this.eyeY, this.eyeZ,
- this.centerX, this.centerY, this.centerZ,
- this.upX, this.upY, this.upZ
- );
-};
-
-/**
+ /**
* Returns true if camera is currently attached to renderer.
* @method _isActive
* @private
*/
-p5.Camera.prototype._isActive = function() {
- return this === this._renderer._curCamera;
+ _isActive() {
+ return this === this._renderer._curCamera;
+ }
};
/**
@@ -1864,7 +1866,7 @@ p5.Camera.prototype._isActive = function() {
* Canvas switches between two camera views, each showing a series of spinning
* 3D boxes.
*/
-p5.prototype.setCamera = function(cam) {
+p5.prototype.setCamera = function (cam) {
this._renderer._curCamera = cam;
// set the projection matrix (which is not normally updated each frame)
diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js
index fd6e0e2d2b..f5f28e60f7 100644
--- a/src/webgl/p5.RendererGL.js
+++ b/src/webgl/p5.RendererGL.js
@@ -11,11 +11,11 @@ import { join } from 'path';
const STROKE_CAP_ENUM = {};
const STROKE_JOIN_ENUM = {};
let lineDefs = '';
-const defineStrokeCapEnum = function(key, val) {
+const defineStrokeCapEnum = function (key, val) {
lineDefs += `#define STROKE_CAP_${key} ${val}\n`;
STROKE_CAP_ENUM[constants[key]] = val;
};
-const defineStrokeJoinEnum = function(key, val) {
+const defineStrokeJoinEnum = function (key, val) {
lineDefs += `#define STROKE_JOIN_${key} ${val}\n`;
STROKE_JOIN_ENUM[constants[key]] = val;
};
@@ -86,7 +86,7 @@ const defaultShaders = {
* @todo extend class to include public method for offscreen
* rendering (FBO).
*/
-p5.RendererGL = function(elt, pInst, isMainCanvas, attr) {
+p5.RendererGL = function (elt, pInst, isMainCanvas, attr) {
p5.Renderer.call(this, elt, pInst, isMainCanvas);
this._setAttributeDefaults(pInst);
this._initContext();
@@ -138,6 +138,7 @@ p5.RendererGL = function(elt, pInst, isMainCanvas, attr) {
}
this._isBlending = false;
+
this._hasSetAmbient = false;
this._useSpecularMaterial = false;
this._useEmissiveMaterial = false;
@@ -157,9 +158,9 @@ p5.RendererGL = function(elt, pInst, isMainCanvas, attr) {
this.quadraticAttenuation = 0;
/**
- * model view, projection, & normal
- * matrices
- */
+ * model view, projection, & normal
+ * matrices
+ */
this.uMVMatrix = new p5.Matrix();
this.uPMatrix = new p5.Matrix();
this.uNMatrix = new p5.Matrix('mat3');
@@ -214,7 +215,7 @@ p5.RendererGL = function(elt, pInst, isMainCanvas, attr) {
new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten)
],
text: [
- new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition',this, this._vToNArray),
+ new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray),
new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten)
]
}
@@ -292,7 +293,7 @@ p5.RendererGL.prototype = Object.create(p5.Renderer.prototype);
// Setting
//////////////////////////////////////////////
-p5.RendererGL.prototype._setAttributeDefaults = function(pInst) {
+p5.RendererGL.prototype._setAttributeDefaults = function (pInst) {
// See issue #3850, safer to enable AA in Safari
const applyAA = navigator.userAgent.toLowerCase().includes('safari');
const defaults = {
@@ -313,7 +314,7 @@ p5.RendererGL.prototype._setAttributeDefaults = function(pInst) {
return;
};
-p5.RendererGL.prototype._initContext = function() {
+p5.RendererGL.prototype._initContext = function () {
if (this._pInst._glAttributes.version !== 1) {
// Unless WebGL1 is explicitly asked for, try to create a WebGL2 context
this.drawingContext =
@@ -350,7 +351,7 @@ p5.RendererGL.prototype._initContext = function() {
//This is helper function to reset the context anytime the attributes
//are changed with setAttributes()
-p5.RendererGL.prototype._resetContext = function(options, callback) {
+p5.RendererGL.prototype._resetContext = function (options, callback) {
const w = this.width;
const h = this.height;
const defaultId = this.canvas.id;
@@ -557,11 +558,11 @@ p5.RendererGL.prototype._resetContext = function(options, callback) {
* @param {Object} obj object with key-value pairs
*/
-p5.prototype.setAttributes = function(key, value) {
+p5.prototype.setAttributes = function (key, value) {
if (typeof this._glAttributes === 'undefined') {
console.log(
'You are trying to use setAttributes on a p5.Graphics object ' +
- 'that does not use a WEBGL renderer.'
+ 'that does not use a WEBGL renderer.'
);
return;
}
@@ -593,7 +594,7 @@ p5.prototype.setAttributes = function(key, value) {
if (this._renderer.retainedMode.geometry.hasOwnProperty(x)) {
p5._friendlyError(
'Sorry, Could not set the attributes, you need to call setAttributes() ' +
- 'before calling the other drawing methods in setup()'
+ 'before calling the other drawing methods in setup()'
);
return;
}
@@ -613,7 +614,7 @@ p5.prototype.setAttributes = function(key, value) {
* @class p5.RendererGL
*/
-p5.RendererGL.prototype._update = function() {
+p5.RendererGL.prototype._update = function () {
// reset model view and apply initial camera transform
// (containing only look at info; no projection).
this.uMVMatrix.set(
@@ -667,7 +668,7 @@ p5.RendererGL.prototype._update = function() {
/**
* [background description]
*/
-p5.RendererGL.prototype.background = function(...args) {
+p5.RendererGL.prototype.background = function (...args) {
const _col = this._pInst.color(...args);
const _r = _col.levels[0] / 255;
const _g = _col.levels[1] / 255;
@@ -711,7 +712,7 @@ p5.RendererGL.prototype.background = function(...args) {
* @alt
* black canvas with purple cube spinning
*/
-p5.RendererGL.prototype.fill = function(v1, v2, v3, a) {
+p5.RendererGL.prototype.fill = function (v1, v2, v3, a) {
//see material.js for more info on color blending in webgl
const color = p5.prototype.color.apply(this._pInst, arguments);
this.curFillColor = color._array;
@@ -750,27 +751,27 @@ p5.RendererGL.prototype.fill = function(v1, v2, v3, a) {
* @alt
* black canvas with purple cube with pink outline spinning
*/
-p5.RendererGL.prototype.stroke = function(r, g, b, a) {
+p5.RendererGL.prototype.stroke = function (r, g, b, a) {
const color = p5.prototype.color.apply(this._pInst, arguments);
this.curStrokeColor = color._array;
};
-p5.RendererGL.prototype.strokeCap = function(cap) {
+p5.RendererGL.prototype.strokeCap = function (cap) {
this.curStrokeCap = cap;
};
-p5.RendererGL.prototype.strokeJoin = function(join) {
+p5.RendererGL.prototype.strokeJoin = function (join) {
this.curStrokeJoin = join;
};
-p5.RendererGL.prototype.filter = function(filterType) {
+p5.RendererGL.prototype.filter = function (filterType) {
// filter can be achieved using custom shaders.
// https://github.com/aferriss/p5jsShaderExamples
// https://itp-xstory.github.io/p5js-shaders/#/
p5._friendlyError('filter() does not work in WEBGL mode');
};
-p5.RendererGL.prototype.blendMode = function(mode) {
+p5.RendererGL.prototype.blendMode = function (mode) {
if (
mode === constants.DARKEST ||
mode === constants.LIGHTEST ||
@@ -797,7 +798,7 @@ p5.RendererGL.prototype.blendMode = function(mode) {
}
};
-p5.RendererGL.prototype.erase = function(opacityFill, opacityStroke) {
+p5.RendererGL.prototype.erase = function (opacityFill, opacityStroke) {
if (!this._isErasing) {
this._applyBlendMode(constants.REMOVE);
this._isErasing = true;
@@ -810,7 +811,7 @@ p5.RendererGL.prototype.erase = function(opacityFill, opacityStroke) {
}
};
-p5.RendererGL.prototype.noErase = function() {
+p5.RendererGL.prototype.noErase = function () {
if (this._isErasing) {
this._isErasing = false;
this.curFillColor = this._cachedFillStyle.slice();
@@ -858,7 +859,7 @@ p5.RendererGL.prototype.noErase = function() {
* black canvas with two purple rotating spheres with pink
* outlines the sphere on top has much heavier outlines,
*/
-p5.RendererGL.prototype.strokeWeight = function(w) {
+p5.RendererGL.prototype.strokeWeight = function (w) {
if (this.curStrokeWeight !== w) {
this.pointSize = w;
this.curStrokeWeight = w;
@@ -866,7 +867,7 @@ p5.RendererGL.prototype.strokeWeight = function(w) {
};
// x,y are canvas-relative (pre-scaled by _pixelDensity)
-p5.RendererGL.prototype._getPixel = function(x, y) {
+p5.RendererGL.prototype._getPixel = function (x, y) {
const gl = this.GL;
return readPixelWebGL(
gl,
@@ -888,7 +889,7 @@ p5.RendererGL.prototype._getPixel = function(x, y) {
* @method loadPixels
*/
-p5.RendererGL.prototype.loadPixels = function() {
+p5.RendererGL.prototype.loadPixels = function () {
const pixelsState = this._pixelsState;
//@todo_FES
@@ -919,7 +920,7 @@ p5.RendererGL.prototype.loadPixels = function() {
);
};
-p5.RendererGL.prototype.updatePixels = function() {
+p5.RendererGL.prototype.updatePixels = function () {
const fbo = this._getTempFramebuffer();
fbo.pixels = this._pixelsState.pixels;
fbo.updatePixels();
@@ -939,7 +940,7 @@ p5.RendererGL.prototype.updatePixels = function() {
* of the renderer's canvas. It will be created if it does not yet exist, and
* reused if it does.
*/
-p5.RendererGL.prototype._getTempFramebuffer = function() {
+p5.RendererGL.prototype._getTempFramebuffer = function () {
if (!this._tempFramebuffer) {
this._tempFramebuffer = this._pInst.createFramebuffer({
format: constants.UNSIGNED_BYTE,
@@ -1067,7 +1068,7 @@ export function readPixelWebGL(
// HASH | for geometry
//////////////////////////////////////////////
-p5.RendererGL.prototype.geometryInHash = function(gId) {
+p5.RendererGL.prototype.geometryInHash = function (gId) {
return this.retainedMode.geometry[gId] !== undefined;
};
@@ -1077,7 +1078,7 @@ p5.RendererGL.prototype.geometryInHash = function(gId) {
* @param {Number} w [description]
* @param {Number} h [description]
*/
-p5.RendererGL.prototype.resize = function(w, h) {
+p5.RendererGL.prototype.resize = function (w, h) {
p5.Renderer.prototype.resize.call(this, w, h);
this.GL.viewport(
0,
@@ -1116,7 +1117,7 @@ p5.RendererGL.prototype.resize = function(w, h) {
* @param {Number} b normalized blue val.
* @param {Number} a normalized alpha val.
*/
-p5.RendererGL.prototype.clear = function(...args) {
+p5.RendererGL.prototype.clear = function (...args) {
const _r = args[0] || 0;
const _g = args[1] || 0;
const _b = args[2] || 0;
@@ -1127,7 +1128,7 @@ p5.RendererGL.prototype.clear = function(...args) {
this.GL.clear(this.GL.COLOR_BUFFER_BIT | this.GL.DEPTH_BUFFER_BIT);
};
-p5.RendererGL.prototype.applyMatrix = function(a, b, c, d, e, f) {
+p5.RendererGL.prototype.applyMatrix = function (a, b, c, d, e, f) {
if (arguments.length === 16) {
p5.Matrix.prototype.apply.apply(this.uMVMatrix, arguments);
} else {
@@ -1149,7 +1150,7 @@ p5.RendererGL.prototype.applyMatrix = function(a, b, c, d, e, f) {
* @chainable
* @todo implement handle for components or vector as args
*/
-p5.RendererGL.prototype.translate = function(x, y, z) {
+p5.RendererGL.prototype.translate = function (x, y, z) {
if (x instanceof p5.Vector) {
z = x.z;
y = x.y;
@@ -1167,12 +1168,12 @@ p5.RendererGL.prototype.translate = function(x, y, z) {
* @param {Number} [z] z-axis scalar
* @chainable
*/
-p5.RendererGL.prototype.scale = function(x, y, z) {
+p5.RendererGL.prototype.scale = function (x, y, z) {
this.uMVMatrix.scale(x, y, z);
return this;
};
-p5.RendererGL.prototype.rotate = function(rad, axis) {
+p5.RendererGL.prototype.rotate = function (rad, axis) {
if (typeof axis === 'undefined') {
return this.rotateZ(rad);
}
@@ -1180,22 +1181,22 @@ p5.RendererGL.prototype.rotate = function(rad, axis) {
return this;
};
-p5.RendererGL.prototype.rotateX = function(rad) {
+p5.RendererGL.prototype.rotateX = function (rad) {
this.rotate(rad, 1, 0, 0);
return this;
};
-p5.RendererGL.prototype.rotateY = function(rad) {
+p5.RendererGL.prototype.rotateY = function (rad) {
this.rotate(rad, 0, 1, 0);
return this;
};
-p5.RendererGL.prototype.rotateZ = function(rad) {
+p5.RendererGL.prototype.rotateZ = function (rad) {
this.rotate(rad, 0, 0, 1);
return this;
};
-p5.RendererGL.prototype.push = function() {
+p5.RendererGL.prototype.push = function () {
// get the base renderer style
const style = p5.Renderer.prototype.push.apply(this);
@@ -1263,7 +1264,7 @@ p5.RendererGL.prototype.push = function() {
return style;
};
-p5.RendererGL.prototype.resetMatrix = function() {
+p5.RendererGL.prototype.resetMatrix = function () {
this.uMVMatrix.set(
this._curCamera.cameraMatrix.mat4[0],
this._curCamera.cameraMatrix.mat4[1],
@@ -1295,7 +1296,7 @@ p5.RendererGL.prototype.resetMatrix = function() {
* and the shader must be valid in that context.
*/
-p5.RendererGL.prototype._getImmediateStrokeShader = function() {
+p5.RendererGL.prototype._getImmediateStrokeShader = function () {
// select the stroke shader to use
const stroke = this.userStrokeShader;
if (!stroke || !stroke.isStrokeShader()) {
@@ -1311,7 +1312,7 @@ p5.RendererGL.prototype._getRetainedStrokeShader =
* selects which fill shader should be used based on renderer state,
* for use with begin/endShape and immediate vertex mode.
*/
-p5.RendererGL.prototype._getImmediateFillShader = function() {
+p5.RendererGL.prototype._getImmediateFillShader = function () {
const fill = this.userFillShader;
if (this._useNormalMaterial) {
if (!fill || !fill.isNormalShader()) {
@@ -1336,7 +1337,7 @@ p5.RendererGL.prototype._getImmediateFillShader = function() {
* selects which fill shader should be used based on renderer state
* for retained mode.
*/
-p5.RendererGL.prototype._getRetainedFillShader = function() {
+p5.RendererGL.prototype._getRetainedFillShader = function () {
if (this._useNormalMaterial) {
return this._getNormalShader();
}
@@ -1356,7 +1357,7 @@ p5.RendererGL.prototype._getRetainedFillShader = function() {
return fill;
};
-p5.RendererGL.prototype._getImmediatePointShader = function() {
+p5.RendererGL.prototype._getImmediatePointShader = function () {
// select the point shader to use
const point = this.userPointShader;
if (!point || !point.isPointShader()) {
@@ -1368,7 +1369,7 @@ p5.RendererGL.prototype._getImmediatePointShader = function() {
p5.RendererGL.prototype._getRetainedLineShader =
p5.RendererGL.prototype._getImmediateLineShader;
-p5.RendererGL.prototype._getLightShader = function() {
+p5.RendererGL.prototype._getLightShader = function () {
if (!this._defaultLightShader) {
if (this._pInst._glAttributes.perPixelLighting) {
this._defaultLightShader = new p5.Shader(
@@ -1388,7 +1389,7 @@ p5.RendererGL.prototype._getLightShader = function() {
return this._defaultLightShader;
};
-p5.RendererGL.prototype._getImmediateModeShader = function() {
+p5.RendererGL.prototype._getImmediateModeShader = function () {
if (!this._defaultImmediateModeShader) {
this._defaultImmediateModeShader = new p5.Shader(
this,
@@ -1400,7 +1401,7 @@ p5.RendererGL.prototype._getImmediateModeShader = function() {
return this._defaultImmediateModeShader;
};
-p5.RendererGL.prototype._getNormalShader = function() {
+p5.RendererGL.prototype._getNormalShader = function () {
if (!this._defaultNormalShader) {
this._defaultNormalShader = new p5.Shader(
this,
@@ -1412,7 +1413,7 @@ p5.RendererGL.prototype._getNormalShader = function() {
return this._defaultNormalShader;
};
-p5.RendererGL.prototype._getColorShader = function() {
+p5.RendererGL.prototype._getColorShader = function () {
if (!this._defaultColorShader) {
this._defaultColorShader = new p5.Shader(
this,
@@ -1424,7 +1425,7 @@ p5.RendererGL.prototype._getColorShader = function() {
return this._defaultColorShader;
};
-p5.RendererGL.prototype._getPointShader = function() {
+p5.RendererGL.prototype._getPointShader = function () {
if (!this._defaultPointShader) {
this._defaultPointShader = new p5.Shader(
this,
@@ -1435,7 +1436,7 @@ p5.RendererGL.prototype._getPointShader = function() {
return this._defaultPointShader;
};
-p5.RendererGL.prototype._getLineShader = function() {
+p5.RendererGL.prototype._getLineShader = function () {
if (!this._defaultLineShader) {
this._defaultLineShader = new p5.Shader(
this,
@@ -1447,7 +1448,7 @@ p5.RendererGL.prototype._getLineShader = function() {
return this._defaultLineShader;
};
-p5.RendererGL.prototype._getFontShader = function() {
+p5.RendererGL.prototype._getFontShader = function () {
if (!this._defaultFontShader) {
if (this.webglVersion === constants.WEBGL) {
this.GL.getExtension('OES_standard_derivatives');
@@ -1455,15 +1456,15 @@ p5.RendererGL.prototype._getFontShader = function() {
this._defaultFontShader = new p5.Shader(
this,
this._webGL2CompatibilityPrefix('vert', 'mediump') +
- defaultShaders.fontVert,
+ defaultShaders.fontVert,
this._webGL2CompatibilityPrefix('frag', 'mediump') +
- defaultShaders.fontFrag
+ defaultShaders.fontFrag
);
}
return this._defaultFontShader;
};
-p5.RendererGL.prototype._webGL2CompatibilityPrefix = function(
+p5.RendererGL.prototype._webGL2CompatibilityPrefix = function (
shaderType,
floatPrecision
) {
@@ -1482,7 +1483,7 @@ p5.RendererGL.prototype._webGL2CompatibilityPrefix = function(
return code;
};
-p5.RendererGL.prototype._getEmptyTexture = function() {
+p5.RendererGL.prototype._getEmptyTexture = function () {
if (!this._emptyTexture) {
// a plain white texture RGBA, full alpha, single pixel.
const im = new p5.Image(1, 1);
@@ -1492,7 +1493,7 @@ p5.RendererGL.prototype._getEmptyTexture = function() {
return this._emptyTexture;
};
-p5.RendererGL.prototype.getTexture = function(input) {
+p5.RendererGL.prototype.getTexture = function (input) {
let src = input;
if (src instanceof p5.Framebuffer) {
src = src.color;
@@ -1508,11 +1509,11 @@ p5.RendererGL.prototype.getTexture = function(input) {
return tex;
};
-p5.RendererGL.prototype.createFramebuffer = function(options) {
+p5.RendererGL.prototype.createFramebuffer = function (options) {
return new p5.Framebuffer(this, options);
};
-p5.RendererGL.prototype._setStrokeUniforms = function(strokeShader) {
+p5.RendererGL.prototype._setStrokeUniforms = function (strokeShader) {
strokeShader.bindShader();
// set the uniform values
@@ -1523,7 +1524,7 @@ p5.RendererGL.prototype._setStrokeUniforms = function(strokeShader) {
strokeShader.setUniform('uStrokeJoin', STROKE_JOIN_ENUM[this.curStrokeJoin]);
};
-p5.RendererGL.prototype._setFillUniforms = function(fillShader) {
+p5.RendererGL.prototype._setFillUniforms = function (fillShader) {
fillShader.bindShader();
// TODO: optimize
@@ -1593,7 +1594,7 @@ p5.RendererGL.prototype._setFillUniforms = function(fillShader) {
fillShader.bindTextures();
};
-p5.RendererGL.prototype._setPointUniforms = function(pointShader) {
+p5.RendererGL.prototype._setPointUniforms = function (pointShader) {
pointShader.bindShader();
// set the uniform values
@@ -1610,7 +1611,7 @@ p5.RendererGL.prototype._setPointUniforms = function(pointShader) {
* when passed more than two arguments it also updates or initializes
* the data associated with the buffer
*/
-p5.RendererGL.prototype._bindBuffer = function(
+p5.RendererGL.prototype._bindBuffer = function (
buffer,
target,
values,
@@ -1628,7 +1629,7 @@ p5.RendererGL.prototype._bindBuffer = function(
///////////////////////////////
//// UTILITY FUNCTIONS
//////////////////////////////
-p5.RendererGL.prototype._arraysEqual = function(a, b) {
+p5.RendererGL.prototype._arraysEqual = function (a, b) {
const aLength = a.length;
if (aLength !== b.length) return false;
for (let i = 0; i < aLength; i++) {
@@ -1637,7 +1638,7 @@ p5.RendererGL.prototype._arraysEqual = function(a, b) {
return true;
};
-p5.RendererGL.prototype._isTypedArray = function(arr) {
+p5.RendererGL.prototype._isTypedArray = function (arr) {
let res = false;
res = arr instanceof Float32Array;
res = arr instanceof Float64Array;
@@ -1653,7 +1654,7 @@ p5.RendererGL.prototype._isTypedArray = function(arr) {
* @return {Array} 1-dimensional array
* [[1, 2, 3],[4, 5, 6]] -> [1, 2, 3, 4, 5, 6]
*/
-p5.RendererGL.prototype._flatten = function(arr) {
+p5.RendererGL.prototype._flatten = function (arr) {
//when empty, return empty
if (arr.length === 0) {
return [];
@@ -1691,7 +1692,7 @@ p5.RendererGL.prototype._flatten = function(arr) {
* [p5.Vector(1, 2, 3), p5.Vector(4, 5, 6)] ->
* [1, 2, 3, 4, 5, 6]
*/
-p5.RendererGL.prototype._vToNArray = function(arr) {
+p5.RendererGL.prototype._vToNArray = function (arr) {
const ret = [];
for (const item of arr) {
@@ -1704,7 +1705,7 @@ p5.RendererGL.prototype._vToNArray = function(arr) {
/**
* ensures that p5 is using a 3d renderer. throws an error if not.
*/
-p5.prototype._assert3d = function(name) {
+p5.prototype._assert3d = function (name) {
if (!this._renderer.isP3D)
throw new Error(
`${name}() is only supported in WEBGL mode. If you'd like to use 3D graphics and WebGL, see https://p5js.org/examples/form-3d-primitives.html for more information.`
@@ -1758,7 +1759,7 @@ p5.RendererGL.prototype._initTessy = function initTesselator() {
return tessy;
};
-p5.RendererGL.prototype._triangulate = function(contours) {
+p5.RendererGL.prototype._triangulate = function (contours) {
// libtess will take 3d verts and flatten to a plane for tesselation.
// libtess is capable of calculating a plane to tesselate on, but
// if all of the vertices have the same z values, we'll just
@@ -1812,7 +1813,7 @@ p5.RendererGL.prototype._triangulate = function(contours) {
};
// function to calculate BezierVertex Coefficients
-p5.RendererGL.prototype._bezierCoefficients = function(t) {
+p5.RendererGL.prototype._bezierCoefficients = function (t) {
const t2 = t * t;
const t3 = t2 * t;
const mt = 1 - t;
@@ -1822,7 +1823,7 @@ p5.RendererGL.prototype._bezierCoefficients = function(t) {
};
// function to calculate QuadraticVertex Coefficients
-p5.RendererGL.prototype._quadraticCoefficients = function(t) {
+p5.RendererGL.prototype._quadraticCoefficients = function (t) {
const t2 = t * t;
const mt = 1 - t;
const mt2 = mt * mt;
@@ -1830,7 +1831,7 @@ p5.RendererGL.prototype._quadraticCoefficients = function(t) {
};
// function to convert Bezier coordinates to Catmull Rom Splines
-p5.RendererGL.prototype._bezierToCatmull = function(w) {
+p5.RendererGL.prototype._bezierToCatmull = function (w) {
const p1 = w[1];
const p2 = w[1] + (w[2] - w[0]) / this._curveTightness;
const p3 = w[2] - (w[3] - w[1]) / this._curveTightness;
diff --git a/src/webgl/text.js b/src/webgl/text.js
index 6d589474af..276b1a5d25 100644
--- a/src/webgl/text.js
+++ b/src/webgl/text.js
@@ -44,21 +44,22 @@ const cellImageHeight = 64;
*
* the ImageInfos class holds a list of ImageDatas of a given size.
*/
-function ImageInfos(width, height) {
- this.width = width;
- this.height = height;
- this.infos = []; // the list of images
-
+class ImageInfos {
+ constructor(width, height) {
+ this.width = width;
+ this.height = height;
+ this.infos = []; // the list of images
+ }
/**
- *
- * @method findImage
- * @param {Integer} space
- * @return {Object} contains the ImageData, and pixel index into that
- * ImageData where the free space was allocated.
- *
- * finds free space of a given size in the ImageData list
- */
- this.findImage = function(space) {
+ *
+ * @method findImage
+ * @param {Integer} space
+ * @return {Object} contains the ImageData, and pixel index into that
+ * ImageData where the free space was allocated.
+ *
+ * finds free space of a given size in the ImageData list
+ */
+ findImage (space) {
const imageSize = this.width * this.height;
if (space > imageSize)
throw new Error('font is too complex to render in 3D');
@@ -109,7 +110,7 @@ function ImageInfos(width, height) {
imageInfo.index += space; // move to the start of the next image
imageData._dirty = true;
return { imageData, index };
- };
+ }
}
/**
@@ -141,30 +142,30 @@ const SQRT3 = Math.sqrt(3);
*
* contains cached images and glyph information for an opentype font
*/
-const FontInfo = function(font) {
- this.font = font;
- // the bezier curve coordinates
- this.strokeImageInfos = new ImageInfos(strokeImageWidth, strokeImageHeight);
- // lists of curve indices for each row/column slice
- this.colDimImageInfos = new ImageInfos(gridImageWidth, gridImageHeight);
- this.rowDimImageInfos = new ImageInfos(gridImageWidth, gridImageHeight);
- // the offset & length of each row/col slice in the glyph
- this.colCellImageInfos = new ImageInfos(cellImageWidth, cellImageHeight);
- this.rowCellImageInfos = new ImageInfos(cellImageWidth, cellImageHeight);
-
- // the cached information for each glyph
- this.glyphInfos = {};
-
+class FontInfo {
+ constructor(font) {
+ this.font = font;
+ // the bezier curve coordinates
+ this.strokeImageInfos = new ImageInfos(strokeImageWidth, strokeImageHeight);
+ // lists of curve indices for each row/column slice
+ this.colDimImageInfos = new ImageInfos(gridImageWidth, gridImageHeight);
+ this.rowDimImageInfos = new ImageInfos(gridImageWidth, gridImageHeight);
+ // the offset & length of each row/col slice in the glyph
+ this.colCellImageInfos = new ImageInfos(cellImageWidth, cellImageHeight);
+ this.rowCellImageInfos = new ImageInfos(cellImageWidth, cellImageHeight);
+
+ // the cached information for each glyph
+ this.glyphInfos = {};
+ }
/**
- * @method getGlyphInfo
- * @param {Glyph} glyph the x positions of points in the curve
- * @returns {Object} the glyphInfo for that glyph
- *
- * calculates rendering info for a glyph, including the curve information,
- * row & column stripes compiled into textures.
- */
-
- this.getGlyphInfo = function(glyph) {
+ * @method getGlyphInfo
+ * @param {Glyph} glyph the x positions of points in the curve
+ * @returns {Object} the glyphInfo for that glyph
+ *
+ * calculates rendering info for a glyph, including the curve information,
+ * row & column stripes compiled into textures.
+ */
+ getGlyphInfo (glyph) {
// check the cache
let gi = this.glyphInfos[glyph.index];
if (gi) return gi;
@@ -189,25 +190,25 @@ const FontInfo = function(font) {
for (i = charGridHeight - 1; i >= 0; --i) rows.push([]);
/**
- * @function push
- * @param {Number[]} xs the x positions of points in the curve
- * @param {Number[]} ys the y positions of points in the curve
- * @param {Object} v the curve information
- *
- * adds a curve to the rows & columns that it intersects with
- */
+ * @function push
+ * @param {Number[]} xs the x positions of points in the curve
+ * @param {Number[]} ys the y positions of points in the curve
+ * @param {Object} v the curve information
+ *
+ * adds a curve to the rows & columns that it intersects with
+ */
function push(xs, ys, v) {
const index = strokes.length; // the index of this stroke
strokes.push(v); // add this stroke to the list
/**
- * @function minMax
- * @param {Number[]} rg the list of values to compare
- * @param {Number} min the initial minimum value
- * @param {Number} max the initial maximum value
- *
- * find the minimum & maximum value in a list of values
- */
+ * @function minMax
+ * @param {Number[]} rg the list of values to compare
+ * @param {Number} min the initial minimum value
+ * @param {Number} max the initial maximum value
+ *
+ * find the minimum & maximum value in a list of values
+ */
function minMax(rg, min, max) {
for (let i = rg.length; i-- > 0; ) {
const v = rg[i];
@@ -250,13 +251,13 @@ const FontInfo = function(font) {
}
/**
- * @function clamp
- * @param {Number} v the value to clamp
- * @param {Number} min the minimum value
- * @param {Number} max the maxmimum value
- *
- * clamps a value between a minimum & maximum value
- */
+ * @function clamp
+ * @param {Number} v the value to clamp
+ * @param {Number} min the minimum value
+ * @param {Number} max the maxmimum value
+ *
+ * clamps a value between a minimum & maximum value
+ */
function clamp(v, min, max) {
if (v < min) return min;
if (v > max) return max;
@@ -264,39 +265,40 @@ const FontInfo = function(font) {
}
/**
- * @function byte
- * @param {Number} v the value to scale
- *
- * converts a floating-point number in the range 0-1 to a byte 0-255
- */
+ * @function byte
+ * @param {Number} v the value to scale
+ *
+ * converts a floating-point number in the range 0-1 to a byte 0-255
+ */
function byte(v) {
return clamp(255 * v, 0, 255);
}
/**
- * @private
- * @class Cubic
- * @param {Number} p0 the start point of the curve
- * @param {Number} c0 the first control point
- * @param {Number} c1 the second control point
- * @param {Number} p1 the end point
- *
- * a cubic curve
- */
- function Cubic(p0, c0, c1, p1) {
- this.p0 = p0;
- this.c0 = c0;
- this.c1 = c1;
- this.p1 = p1;
-
- /**
- * @method toQuadratic
- * @return {Object} the quadratic approximation
+ * @private
+ * @class Cubic
+ * @param {Number} p0 the start point of the curve
+ * @param {Number} c0 the first control point
+ * @param {Number} c1 the second control point
+ * @param {Number} p1 the end point
*
- * converts the cubic to a quadtratic approximation by
- * picking an appropriate quadratic control point
+ * a cubic curve
*/
- this.toQuadratic = function() {
+ class Cubic {
+ constructor(p0, c0, c1, p1) {
+ this.p0 = p0;
+ this.c0 = c0;
+ this.c1 = c1;
+ this.p1 = p1;
+ }
+ /**
+ * @method toQuadratic
+ * @return {Object} the quadratic approximation
+ *
+ * converts the cubic to a quadtratic approximation by
+ * picking an appropriate quadratic control point
+ */
+ toQuadratic () {
return {
x: this.p0.x,
y: this.p0.y,
@@ -305,34 +307,34 @@ const FontInfo = function(font) {
cx: ((this.c0.x + this.c1.x) * 3 - (this.p0.x + this.p1.x)) / 4,
cy: ((this.c0.y + this.c1.y) * 3 - (this.p0.y + this.p1.y)) / 4
};
- };
+ }
/**
- * @method quadError
- * @return {Number} the error
- *
- * calculates the magnitude of error of this curve's
- * quadratic approximation.
- */
- this.quadError = function() {
+ * @method quadError
+ * @return {Number} the error
+ *
+ * calculates the magnitude of error of this curve's
+ * quadratic approximation.
+ */
+ quadError () {
return (
p5.Vector.sub(
p5.Vector.sub(this.p1, this.p0),
p5.Vector.mult(p5.Vector.sub(this.c1, this.c0), 3)
).mag() / 2
);
- };
+ }
/**
- * @method split
- * @param {Number} t the value (0-1) at which to split
- * @return {Cubic} the second part of the curve
- *
- * splits the cubic into two parts at a point 't' along the curve.
- * this cubic keeps its start point and its end point becomes the
- * point at 't'. the 'end half is returned.
- */
- this.split = function(t) {
+ * @method split
+ * @param {Number} t the value (0-1) at which to split
+ * @return {Cubic} the second part of the curve
+ *
+ * splits the cubic into two parts at a point 't' along the curve.
+ * this cubic keeps its start point and its end point becomes the
+ * point at 't'. the 'end half is returned.
+ */
+ split (t) {
const m1 = p5.Vector.lerp(this.p0, this.c0, t);
const m2 = p5.Vector.lerp(this.c0, this.c1, t);
const mm1 = p5.Vector.lerp(m1, m2, t);
@@ -343,17 +345,17 @@ const FontInfo = function(font) {
const part1 = new Cubic(this.p0, m1, mm1, pt);
this.p0 = pt;
return part1;
- };
+ }
/**
- * @method splitInflections
- * @return {Cubic[]} the non-inflecting pieces of this cubic
- *
- * returns an array containing 0, 1 or 2 cubics split resulting
- * from splitting this cubic at its inflection points.
- * this cubic is (potentially) altered and returned in the list.
- */
- this.splitInflections = function() {
+ * @method splitInflections
+ * @return {Cubic[]} the non-inflecting pieces of this cubic
+ *
+ * returns an array containing 0, 1 or 2 cubics split resulting
+ * from splitting this cubic at its inflection points.
+ * this cubic is (potentially) altered and returned in the list.
+ */
+ splitInflections () {
const a = p5.Vector.sub(this.c0, this.p0);
const b = p5.Vector.sub(p5.Vector.sub(this.c1, this.c0), a);
const c = p5.Vector.sub(
@@ -398,24 +400,24 @@ const FontInfo = function(font) {
cubics.push(this);
return cubics;
- };
+ }
}
/**
- * @function cubicToQuadratics
- * @param {Number} x0
- * @param {Number} y0
- * @param {Number} cx0
- * @param {Number} cy0
- * @param {Number} cx1
- * @param {Number} cy1
- * @param {Number} x1
- * @param {Number} y1
- * @returns {Cubic[]} an array of cubics whose quadratic approximations
- * closely match the civen cubic.
- *
- * converts a cubic curve to a list of quadratics.
- */
+ * @function cubicToQuadratics
+ * @param {Number} x0
+ * @param {Number} y0
+ * @param {Number} cx0
+ * @param {Number} cy0
+ * @param {Number} cx1
+ * @param {Number} cy1
+ * @param {Number} x1
+ * @param {Number} y1
+ * @returns {Cubic[]} an array of cubics whose quadratic approximations
+ * closely match the civen cubic.
+ *
+ * converts a cubic curve to a list of quadratics.
+ */
function cubicToQuadratics(x0, y0, cx0, cy0, cx1, cy1, x1, y1) {
// create the Cubic object and split it at its inflections
const cubics = new Cubic(
@@ -470,14 +472,14 @@ const FontInfo = function(font) {
}
/**
- * @function pushLine
- * @param {Number} x0
- * @param {Number} y0
- * @param {Number} x1
- * @param {Number} y1
- *
- * add a straight line to the row/col grid of a glyph
- */
+ * @function pushLine
+ * @param {Number} x0
+ * @param {Number} y0
+ * @param {Number} x1
+ * @param {Number} y1
+ *
+ * add a straight line to the row/col grid of a glyph
+ */
function pushLine(x0, y0, x1, y1) {
const mx = (x0 + x1) / 2;
const my = (y0 + y1) / 2;
@@ -485,15 +487,15 @@ const FontInfo = function(font) {
}
/**
- * @function samePoint
- * @param {Number} x0
- * @param {Number} y0
- * @param {Number} x1
- * @param {Number} y1
- * @return {Boolean} true if the two points are sufficiently close
- *
- * tests if two points are close enough to be considered the same
- */
+ * @function samePoint
+ * @param {Number} x0
+ * @param {Number} y0
+ * @param {Number} x1
+ * @param {Number} y1
+ * @return {Boolean} true if the two points are sufficiently close
+ *
+ * tests if two points are close enough to be considered the same
+ */
function samePoint(x0, y0, x1, y1) {
return Math.abs(x1 - x0) < 0.00001 && Math.abs(y1 - y0) < 0.00001;
}
@@ -570,16 +572,16 @@ const FontInfo = function(font) {
}
/**
- * @function layout
- * @param {Number[][]} dim
- * @param {ImageInfo[]} dimImageInfos
- * @param {ImageInfo[]} cellImageInfos
- * @return {Object}
- *
- * lays out the curves in a dimension (row or col) into two
- * images, one for the indices of the curves themselves, and
- * one containing the offset and length of those index spans.
- */
+ * @function layout
+ * @param {Number[][]} dim
+ * @param {ImageInfo[]} dimImageInfos
+ * @param {ImageInfo[]} cellImageInfos
+ * @return {Object}
+ *
+ * lays out the curves in a dimension (row or col) into two
+ * images, one for the indices of the curves themselves, and
+ * one containing the offset and length of those index spans.
+ */
function layout(dim, dimImageInfos, cellImageInfos) {
const dimLength = dim.length; // the number of slices in this dimension
const dimImageInfo = dimImageInfos.findImage(dimLength);
@@ -634,8 +636,8 @@ const FontInfo = function(font) {
};
gi.uGridOffset = [gi.colInfo.dimOffset, gi.rowInfo.dimOffset];
return gi;
- };
-};
+ }
+}
p5.RendererGL.prototype._renderText = function(p, line, x, y, maxY) {
if (!this._textFont || typeof this._textFont === 'string') {
diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_1/catcher.js b/test/manual-test-examples/learningprocessing/chp10/example_10_1/catcher.js
index 8033cf0dc1..dd4bbe3fe1 100644
--- a/test/manual-test-examples/learningprocessing/chp10/example_10_1/catcher.js
+++ b/test/manual-test-examples/learningprocessing/chp10/example_10_1/catcher.js
@@ -1,18 +1,18 @@
// Catch class
-function Catcher(tempR) {
- this.r = tempR; // radius
- this.x = 0; // location
- this.y = 0;
+class Catcher {
+ constructor(tempR) {
+ this.r = tempR; // radius
+ this.x = 0; // location
+ this.y = 0;
+ }
+ setLocation(tempX, tempY) {
+ this.x = tempX;
+ this.y = tempY;
+ }
+ display() {
+ stroke(0);
+ fill(175);
+ ellipse(this.x, this.y, this.r * 2, this.r * 2);
+ }
}
-
-Catcher.prototype.setLocation = function(tempX, tempY) {
- this.x = tempX;
- this.y = tempY;
-};
-
-Catcher.prototype.display = function() {
- stroke(0);
- fill(175);
- ellipse(this.x, this.y, this.r * 2, this.r * 2);
-};
diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_10/catcher.js b/test/manual-test-examples/learningprocessing/chp10/example_10_10/catcher.js
index 0250bd6abb..85b72ae3e1 100644
--- a/test/manual-test-examples/learningprocessing/chp10/example_10_10/catcher.js
+++ b/test/manual-test-examples/learningprocessing/chp10/example_10_10/catcher.js
@@ -6,34 +6,33 @@
// Example 10-10: The raindrop catching game
-function Catcher(tempR) {
- this.r = tempR; // radius
- this.col = [50, 10, 10, 150]; // color
- this.x = 0; // location
- this.y = 0;
-}
-
-Catcher.prototype.setLocation = function(tempX, tempY) {
- this.x = tempX;
- this.y = tempY;
-};
-
-Catcher.prototype.display = function() {
- stroke(0);
- fill(this.col);
- ellipse(this.x, this.y, this.r * 2, this.r * 2);
-};
-
-// A function that returns true or false based on
-// if the catcher intersects a raindrop
-Catcher.prototype.intersect = function(d) {
- // Calculate distance
- var distance = dist(this.x, this.y, d.x, d.y);
+class Catcher {
+ constructor(tempR) {
+ this.r = tempR; // radius
+ this.col = [50, 10, 10, 150]; // color
+ this.x = 0; // location
+ this.y = 0;
+ }
+ setLocation(tempX, tempY) {
+ this.x = tempX;
+ this.y = tempY;
+ }
+ display() {
+ stroke(0);
+ fill(this.col);
+ ellipse(this.x, this.y, this.r * 2, this.r * 2);
+ }
+ // A function that returns true or false based on
+ // if the catcher intersects a raindrop
+ intersect(d) {
+ // Calculate distance
+ var distance = dist(this.x, this.y, d.x, d.y);
- // Compare distance to sum of radii
- if (distance < this.r + d.r) {
- return true;
- } else {
- return false;
+ // Compare distance to sum of radii
+ if (distance < this.r + d.r) {
+ return true;
+ } else {
+ return false;
+ }
}
-};
+}
diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_10/drop.js b/test/manual-test-examples/learningprocessing/chp10/example_10_10/drop.js
index b7848d487b..7be3571399 100644
--- a/test/manual-test-examples/learningprocessing/chp10/example_10_10/drop.js
+++ b/test/manual-test-examples/learningprocessing/chp10/example_10_10/drop.js
@@ -6,44 +6,42 @@
// Example 10-10: The raindrop catching game
-function Drop() {
- this.r = 8; // All raindrops are the same size
- this.x = random(width); // Start with a random x location
- this.y = -this.r * 4; // Start a little above the window
- this.speed = random(1, 5); // Speed of raindrop
- this.c = [50, 100, 150];
-}
-
-// Move the raindrop down
-Drop.prototype.move = function() {
- // Increment by speed
- this.y += this.speed;
-};
-
-// Check if it hits the bottom
-Drop.prototype.reachedBottom = function() {
- // If we go a little beyond the bottom
- if (this.y > height + this.r * 4) {
- return true;
- } else {
- return false;
+class Drop {
+ constructor() {
+ this.r = 8; // All raindrops are the same size
+ this.x = random(width); // Start with a random x location
+ this.y = -this.r * 4; // Start a little above the window
+ this.speed = random(1, 5); // Speed of raindrop
+ this.c = [50, 100, 150];
}
-};
-
-// Display the raindrop
-Drop.prototype.display = function() {
- // Display the drop
- fill(this.c);
- noStroke();
- for (var i = 2; i < this.r; i++) {
- ellipse(this.x, this.y + i * 4, i * 2, i * 2);
+ // Move the raindrop down
+ move() {
+ // Increment by speed
+ this.y += this.speed;
}
-};
-
-// If the drop is caught
-Drop.prototype.caught = function() {
- // Stop it from moving by setting speed equal to zero
- this.speed = 0;
- // Set the location to somewhere way off-screen
- this.y = -1000;
-};
+ // Check if it hits the bottom
+ reachedBottom() {
+ // If we go a little beyond the bottom
+ if (this.y > height + this.r * 4) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // Display the raindrop
+ display() {
+ // Display the drop
+ fill(this.c);
+ noStroke();
+ for (var i = 2; i < this.r; i++) {
+ ellipse(this.x, this.y + i * 4, i * 2, i * 2);
+ }
+ }
+ // If the drop is caught
+ caught() {
+ // Stop it from moving by setting speed equal to zero
+ this.speed = 0;
+ // Set the location to somewhere way off-screen
+ this.y = -1000;
+ }
+}
diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_10/timer.js b/test/manual-test-examples/learningprocessing/chp10/example_10_10/timer.js
index 4fb0d15d60..66a28358ce 100644
--- a/test/manual-test-examples/learningprocessing/chp10/example_10_10/timer.js
+++ b/test/manual-test-examples/learningprocessing/chp10/example_10_10/timer.js
@@ -6,25 +6,25 @@
// Example 10-5: Object-oriented timer
-function Timer(tempTotalTime) {
- this.savedTime = 0; // When Timer started
- this.totalTime = tempTotalTime; // How long Timer should last
-}
-
-// Starting the timer
-Timer.prototype.start = function() {
- // When the timer starts it stores the current time in milliseconds.
- this.savedTime = millis();
-};
-
-// The function isFinished() returns true if 5,000 ms have passed.
-// The work of the timer is farmed out to this method.
-Timer.prototype.isFinished = function() {
- // Check how much time has passed
- var passedTime = millis() - this.savedTime;
- if (passedTime > this.totalTime) {
- return true;
- } else {
- return false;
+class Timer {
+ constructor(tempTotalTime) {
+ this.savedTime = 0; // When Timer started
+ this.totalTime = tempTotalTime; // How long Timer should last
}
-};
+ // Starting the timer
+ start() {
+ // When the timer starts it stores the current time in milliseconds.
+ this.savedTime = millis();
+ }
+ // The function isFinished() returns true if 5,000 ms have passed.
+ // The work of the timer is farmed out to this method.
+ isFinished() {
+ // Check how much time has passed
+ var passedTime = millis() - this.savedTime;
+ if (passedTime > this.totalTime) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_2/ball.js b/test/manual-test-examples/learningprocessing/chp10/example_10_2/ball.js
index 6176226609..c98c93e79c 100644
--- a/test/manual-test-examples/learningprocessing/chp10/example_10_2/ball.js
+++ b/test/manual-test-examples/learningprocessing/chp10/example_10_2/ball.js
@@ -7,31 +7,31 @@
// Example 10-2: Bouncing ball class
-function Ball(tempR) {
- this.r = tempR;
- this.x = random(width);
- this.y = random(height);
- this.xspeed = random(-5, 5);
- this.yspeed = random(-5, 5);
-}
-
-Ball.prototype.move = function() {
- this.x += this.xspeed; // Increment x
- this.y += this.yspeed; // Increment y
+class Ball {
+ constructor(tempR) {
+ this.r = tempR;
+ this.x = random(width);
+ this.y = random(height);
+ this.xspeed = random(-5, 5);
+ this.yspeed = random(-5, 5);
+ }
+ move() {
+ this.x += this.xspeed; // Increment x
+ this.y += this.yspeed; // Increment y
- // Check horizontal edges
- if (this.x > width || this.x < 0) {
- this.xspeed *= -1;
+ // Check horizontal edges
+ if (this.x > width || this.x < 0) {
+ this.xspeed *= -1;
+ }
+ //Check vertical edges
+ if (this.y > height || this.y < 0) {
+ this.yspeed *= -1;
+ }
}
- //Check vertical edges
- if (this.y > height || this.y < 0) {
- this.yspeed *= -1;
+ // Draw the ball
+ display() {
+ stroke(0);
+ fill(0, 50);
+ ellipse(this.x, this.y, this.r * 2, this.r * 2);
}
-};
-
-// Draw the ball
-Ball.prototype.display = function() {
- stroke(0);
- fill(0, 50);
- ellipse(this.x, this.y, this.r * 2, this.r * 2);
-};
+}
diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_3/ball.js b/test/manual-test-examples/learningprocessing/chp10/example_10_3/ball.js
index 75a0c3e781..cbc5f6a2fc 100644
--- a/test/manual-test-examples/learningprocessing/chp10/example_10_3/ball.js
+++ b/test/manual-test-examples/learningprocessing/chp10/example_10_3/ball.js
@@ -7,55 +7,53 @@
// Example 10-3: Bouncing ball with intersection
-function Ball(tempR) {
- this.r = tempR; // radius
- this.x = random(width);
- this.y = random(height);
- this.xspeed = random(-5, 5);
- this.yspeed = random(-5, 5);
- this.c = [100, 50];
-}
-
-Ball.prototype.move = function() {
- this.x += this.xspeed; // Increment x
- this.y += this.yspeed; // Increment y
-
- // Check horizontal edges
- if (this.x > width || this.x < 0) {
- this.xspeed *= -1;
+class Ball {
+ constructor(tempR) {
+ this.r = tempR; // radius
+ this.x = random(width);
+ this.y = random(height);
+ this.xspeed = random(-5, 5);
+ this.yspeed = random(-5, 5);
+ this.c = [100, 50];
}
-
- // Check vertical edges
- if (this.y > height || this.y < 0) {
- this.yspeed *= -1;
+ move() {
+ this.x += this.xspeed; // Increment x
+ this.y += this.yspeed; // Increment y
+
+ // Check horizontal edges
+ if (this.x > width || this.x < 0) {
+ this.xspeed *= -1;
+ }
+
+ // Check vertical edges
+ if (this.y > height || this.y < 0) {
+ this.yspeed *= -1;
+ }
}
-};
-
-// Whenever the balls are touching, this highlight() function is called
-// and the color is darkened.
-Ball.prototype.highlight = function() {
- this.c = [0, 150];
-};
-
-// Draw the ball
-Ball.prototype.display = function() {
- stroke(0);
- fill(this.c);
- ellipse(this.x, this.y, this.r * 2, this.r * 2);
- // After the ball is displayed, the color is reset back to a darker gray.
- this.c = [100, 50];
-};
-
-// A function that returns true or false based on whether two circles intersect
-// If distance is less than the sum of radii the circles touch
-Ball.prototype.intersect = function(b) {
- // Objects can be passed into functions as arguments too!
- var distance = dist(this.x, this.y, b.x, b.y); // Calculate distance
-
- // Compare distance to sum of radii
- if (distance < this.r + b.r) {
- return true;
- } else {
- return false;
+ // Whenever the balls are touching, this highlight() function is called
+ // and the color is darkened.
+ highlight() {
+ this.c = [0, 150];
+ }
+ // Draw the ball
+ display() {
+ stroke(0);
+ fill(this.c);
+ ellipse(this.x, this.y, this.r * 2, this.r * 2);
+ // After the ball is displayed, the color is reset back to a darker gray.
+ this.c = [100, 50];
}
-};
+ // A function that returns true or false based on whether two circles intersect
+ // If distance is less than the sum of radii the circles touch
+ intersect(b) {
+ // Objects can be passed into functions as arguments too!
+ var distance = dist(this.x, this.y, b.x, b.y); // Calculate distance
+
+ // Compare distance to sum of radii
+ if (distance < this.r + b.r) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_5/timer.js b/test/manual-test-examples/learningprocessing/chp10/example_10_5/timer.js
index 0328a2ee77..1d3164a3bc 100644
--- a/test/manual-test-examples/learningprocessing/chp10/example_10_5/timer.js
+++ b/test/manual-test-examples/learningprocessing/chp10/example_10_5/timer.js
@@ -1,24 +1,24 @@
// Timer class
-function Timer(tempTotalTime) {
- this.savedTime = 0; // When Timer started
- this.totalTime = tempTotalTime; // How long Timer should last
-}
-
-// Starting the timer
-Timer.prototype.start = function() {
- // When the timer starts it stores the current time in milliseconds.
- this.savedTime = millis();
-};
-
-// The function isFinished() returns true if 5,000 ms have passed.
-// The work of the timer is farmed out to this method.
-Timer.prototype.isFinished = function() {
- // Check how much time has passed
- var passedTime = millis() - this.savedTime;
- if (passedTime > this.totalTime) {
- return true;
- } else {
- return false;
+class Timer {
+ constructor(tempTotalTime) {
+ this.savedTime = 0; // When Timer started
+ this.totalTime = tempTotalTime; // How long Timer should last
}
-};
+ // Starting the timer
+ start() {
+ // When the timer starts it stores the current time in milliseconds.
+ this.savedTime = millis();
+ }
+ // The function isFinished() returns true if 5,000 ms have passed.
+ // The work of the timer is farmed out to this method.
+ isFinished() {
+ // Check how much time has passed
+ var passedTime = millis() - this.savedTime;
+ if (passedTime > this.totalTime) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_7/drop.js b/test/manual-test-examples/learningprocessing/chp10/example_10_7/drop.js
index 95143a0ce4..960311f59e 100644
--- a/test/manual-test-examples/learningprocessing/chp10/example_10_7/drop.js
+++ b/test/manual-test-examples/learningprocessing/chp10/example_10_7/drop.js
@@ -8,44 +8,42 @@
// Drop class
-function Drop() {
- this.r = 8; // All raindrops are the same size
- this.x = random(width); // Start with a random x location
- this.y = -this.r * 4; // Start a little above the window
- this.speed = random(1, 5); // Pick a random speed
- this.c = [50, 100, 150]; // Color
-}
-
-// Move the raindrop down
-Drop.prototype.move = function() {
- // Increment by speed
- this.y += this.speed;
-};
-
-// Check if it hits the bottom
-Drop.prototype.reachedBottom = function() {
- // If we go a little beyond the bottom
- if (this.y > height + this.r * 4) {
- return true;
- } else {
- return false;
+class Drop {
+ constructor() {
+ this.r = 8; // All raindrops are the same size
+ this.x = random(width); // Start with a random x location
+ this.y = -this.r * 4; // Start a little above the window
+ this.speed = random(1, 5); // Pick a random speed
+ this.c = [50, 100, 150]; // Color
}
-};
-
-// Display the raindrop
-Drop.prototype.display = function() {
- // Display the drop
- fill(this.c);
- noStroke();
- for (var i = 2; i < this.r; i++) {
- ellipse(this.x, this.y + i * 4, i * 2, i * 2);
+ // Move the raindrop down
+ move() {
+ // Increment by speed
+ this.y += this.speed;
}
-};
-
-// If the drop is caught
-Drop.prototype.caught = function() {
- // Stop it from moving by setting speed equal to zero
- this.speed = 0;
- // Set the location to somewhere way off-screen
- this.y = -1000;
-};
+ // Check if it hits the bottom
+ reachedBottom() {
+ // If we go a little beyond the bottom
+ if (this.y > height + this.r * 4) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // Display the raindrop
+ display() {
+ // Display the drop
+ fill(this.c);
+ noStroke();
+ for (var i = 2; i < this.r; i++) {
+ ellipse(this.x, this.y + i * 4, i * 2, i * 2);
+ }
+ }
+ // If the drop is caught
+ caught() {
+ // Stop it from moving by setting speed equal to zero
+ this.speed = 0;
+ // Set the location to somewhere way off-screen
+ this.y = -1000;
+ }
+}
diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_9/catcher.js b/test/manual-test-examples/learningprocessing/chp10/example_10_9/catcher.js
index 7402c4dfc1..cfeeba0642 100644
--- a/test/manual-test-examples/learningprocessing/chp10/example_10_9/catcher.js
+++ b/test/manual-test-examples/learningprocessing/chp10/example_10_9/catcher.js
@@ -6,19 +6,19 @@
// Example 10-1: Catcher
-function Catcher(tempR) {
- this.r = tempR; // radius
- this.x = 0; // location
- this.y = 0;
+class Catcher {
+ constructor(tempR) {
+ this.r = tempR; // radius
+ this.x = 0; // location
+ this.y = 0;
+ }
+ setLocation(tempX, tempY) {
+ this.x = tempX;
+ this.y = tempY;
+ }
+ display() {
+ stroke(0);
+ fill(175);
+ ellipse(this.x, this.y, this.r * 2, this.r * 2);
+ }
}
-
-Catcher.prototype.setLocation = function(tempX, tempY) {
- this.x = tempX;
- this.y = tempY;
-};
-
-Catcher.prototype.display = function() {
- stroke(0);
- fill(175);
- ellipse(this.x, this.y, this.r * 2, this.r * 2);
-};
diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_9/drop.js b/test/manual-test-examples/learningprocessing/chp10/example_10_9/drop.js
index aa3c427ef0..ea54d69f74 100644
--- a/test/manual-test-examples/learningprocessing/chp10/example_10_9/drop.js
+++ b/test/manual-test-examples/learningprocessing/chp10/example_10_9/drop.js
@@ -6,47 +6,45 @@
// Example 10-7: Drops one at a time
-function Drop() {
- this.r = 8; // Radius of raindrop (all raindrops are the same size)
+class Drop {
+ constructor() {
+ this.r = 8; // Radius of raindrop (all raindrops are the same size)
- // Variables for location of raindrop
- this.x = random(width);
- this.y = -this.r * 4;
+ // Variables for location of raindrop
+ this.x = random(width);
+ this.y = -this.r * 4;
- this.speed = random(1, 5); // Speed of raindrop
- this.c = [50, 100, 150];
-}
-
-// Move the raindrop down
-Drop.prototype.move = function() {
- // Increment by speed
- this.y += this.speed;
-};
-
-// Check if it hits the bottom
-Drop.prototype.reachedBottom = function() {
- // If we go a little beyond the bottom
- if (this.y > height + this.r * 4) {
- return true;
- } else {
- return false;
+ this.speed = random(1, 5); // Speed of raindrop
+ this.c = [50, 100, 150];
}
-};
-
-// Display the raindrop
-Drop.prototype.display = function() {
- // Display the drop
- fill(this.c);
- noStroke();
- for (var i = 2; i < this.r; i++) {
- ellipse(this.x, this.y + i * 4, i * 2, i * 2);
+ // Move the raindrop down
+ move() {
+ // Increment by speed
+ this.y += this.speed;
}
-};
-
-// If the drop is caught
-Drop.prototype.caught = function() {
- // Stop it from moving by setting speed equal to zero
- this.speed = 0;
- // Set the location to somewhere way off-screen
- this.y = -1000;
-};
+ // Check if it hits the bottom
+ reachedBottom() {
+ // If we go a little beyond the bottom
+ if (this.y > height + this.r * 4) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // Display the raindrop
+ display() {
+ // Display the drop
+ fill(this.c);
+ noStroke();
+ for (var i = 2; i < this.r; i++) {
+ ellipse(this.x, this.y + i * 4, i * 2, i * 2);
+ }
+ }
+ // If the drop is caught
+ caught() {
+ // Stop it from moving by setting speed equal to zero
+ this.speed = 0;
+ // Set the location to somewhere way off-screen
+ this.y = -1000;
+ }
+}
diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_9/timer.js b/test/manual-test-examples/learningprocessing/chp10/example_10_9/timer.js
index 58e10ee54e..d4ded395db 100644
--- a/test/manual-test-examples/learningprocessing/chp10/example_10_9/timer.js
+++ b/test/manual-test-examples/learningprocessing/chp10/example_10_9/timer.js
@@ -6,25 +6,25 @@
// Example 10-5: Object-oriented timer
-function Timer(tempTotalTime) {
- this.savedTime = 0; // When Timer started
- this.totalTime = tempTotalTime; // How long Timer should last
-}
-
-// Starting the timer
-Timer.prototype.start = function() {
- // When the timer starts it stores the current time in milliseconds.
- this.savedTime = millis();
-};
-
-// The function isFinished() returns true if 5,000 ms have passed.
-// The work of the timer is farmed out to this method.
-Timer.prototype.isFinished = function() {
- // Check how much time has passed
- var passedTime = millis() - this.savedTime;
- if (passedTime > this.totalTime) {
- return true;
- } else {
- return false;
+class Timer {
+ constructor(tempTotalTime) {
+ this.savedTime = 0; // When Timer started
+ this.totalTime = tempTotalTime; // How long Timer should last
}
-};
+ // Starting the timer
+ start() {
+ // When the timer starts it stores the current time in milliseconds.
+ this.savedTime = millis();
+ }
+ // The function isFinished() returns true if 5,000 ms have passed.
+ // The work of the timer is farmed out to this method.
+ isFinished() {
+ // Check how much time has passed
+ var passedTime = millis() - this.savedTime;
+ if (passedTime > this.totalTime) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/test/manual-test-examples/learningprocessing/chp8/example_8_1.js b/test/manual-test-examples/learningprocessing/chp8/example_8_1.js
index 643a6d8ea0..a173cddb72 100644
--- a/test/manual-test-examples/learningprocessing/chp8/example_8_1.js
+++ b/test/manual-test-examples/learningprocessing/chp8/example_8_1.js
@@ -6,6 +6,31 @@
// Example 8-1: A Car class and a Car object
+// Define a class below the rest of the program.
+class Car {
+ constructor() {
+ this.c = 175;
+ this.xpos = width / 2;
+ this.ypos = height / 2;
+ this.xspeed = 1;
+ }
+ display() {
+ // Function.
+ // The car is just a square
+ rectMode(CENTER);
+ stroke(0);
+ fill(this.c);
+ rect(this.xpos, this.ypos, 20, 10);
+ }
+ move() {
+ // Function.
+ this.xpos = this.xpos + this.xspeed;
+ if (this.xpos > width) {
+ this.xpos = 0;
+ }
+ }
+}
+
var myCar; // Declare car object as a globle variable.
function setup() {
@@ -20,28 +45,3 @@ function draw() {
myCar.move(); // Operate the car object in draw( ) by calling object methods using the dots syntax.
myCar.display();
}
-
-// Define a class below the rest of the program.
-function Car() {
- this.c = 175;
- this.xpos = width / 2;
- this.ypos = height / 2;
- this.xspeed = 1;
-}
-
-Car.prototype.display = function() {
- // Function.
- // The car is just a square
- rectMode(CENTER);
- stroke(0);
- fill(this.c);
- rect(this.xpos, this.ypos, 20, 10);
-};
-
-Car.prototype.move = function() {
- // Function.
- this.xpos = this.xpos + this.xspeed;
- if (this.xpos > width) {
- this.xpos = 0;
- }
-};
diff --git a/test/manual-test-examples/learningprocessing/chp8/example_8_2.js b/test/manual-test-examples/learningprocessing/chp8/example_8_2.js
index e94c4a4741..a29e80d21d 100644
--- a/test/manual-test-examples/learningprocessing/chp8/example_8_2.js
+++ b/test/manual-test-examples/learningprocessing/chp8/example_8_2.js
@@ -8,6 +8,27 @@
var myCar1;
var myCar2; // Two objects!
+class Car {
+ constructor(tempC, tempXpos, tempYpos, tempXspeed) {
+ // Even though there are multiple objects, we still only need one class. No matter how many cookies we make, only one cookie cutter is needed.Isn’t object-oriented programming swell?
+ this.c = tempC;
+ this.xpos = tempXpos;
+ this.ypos = tempYpos;
+ this.xspeed = tempXspeed;
+ }
+ display() {
+ stroke(0);
+ fill(this.c);
+ rectMode(CENTER);
+ rect(this.xpos, this.ypos, 20, 10);
+ }
+ move() {
+ this.xpos = this.xpos + this.xspeed;
+ if (this.xpos > width) {
+ this.xpos = 0;
+ }
+ }
+}
function setup() {
createCanvas(200, 200);
@@ -22,25 +43,3 @@ function draw() {
myCar2.move();
myCar2.display();
}
-
-function Car(tempC, tempXpos, tempYpos, tempXspeed) {
- // Even though there are multiple objects, we still only need one class. No matter how many cookies we make, only one cookie cutter is needed.Isn’t object-oriented programming swell?
- this.c = tempC;
- this.xpos = tempXpos;
- this.ypos = tempYpos;
- this.xspeed = tempXspeed;
-}
-
-Car.prototype.display = function() {
- stroke(0);
- fill(this.c);
- rectMode(CENTER);
- rect(this.xpos, this.ypos, 20, 10);
-};
-
-Car.prototype.move = function() {
- this.xpos = this.xpos + this.xspeed;
- if (this.xpos > width) {
- this.xpos = 0;
- }
-};
diff --git a/test/manual-test-examples/learningprocessing/chp8/example_8_3.js b/test/manual-test-examples/learningprocessing/chp8/example_8_3.js
index db51f84cb0..efbd2942ab 100644
--- a/test/manual-test-examples/learningprocessing/chp8/example_8_3.js
+++ b/test/manual-test-examples/learningprocessing/chp8/example_8_3.js
@@ -5,6 +5,73 @@
// Ported by Lauren McCarthy
// Example 8-3
+class Zoog {
+ constructor(tempX, tempY, tempW, tempH, tempEyeSize) {
+ // Zoog's variables
+ this.x = tempX;
+ this.y = tempY;
+ this.w = tempW;
+ this.h = tempH;
+ this.eyeSize = tempEyeSize;
+ }
+ // Move Zoog
+ jiggle(speed) {
+ // Change the location of Zoog randomly
+ this.x = this.x + random(-1, 1) * speed;
+ this.y = this.y + random(-1, 1) * speed;
+ // Constrain Zoog to window
+ this.x = constrain(this.x, 0, width);
+ this.y = constrain(this.y, 0, height);
+ }
+ // Display Zoog
+ display() {
+ // Set ellipses and rects to CENTER mode
+ ellipseMode(CENTER);
+ rectMode(CENTER);
+ // Draw Zoog's arms with a for loop
+ for (var i = this.y - this.h / 3; i < this.y + this.h / 2; i += 10) {
+ stroke(0);
+ line(this.x - this.w / 4, i, this.x + this.w / 4, i);
+ }
+ // Draw Zoog's body
+ stroke(0);
+ fill(175);
+ rect(this.x, this.y, this.w / 6, this.h);
+ // Draw Zoog's head
+ stroke(0);
+ fill(255);
+ ellipse(this.x, this.y - this.h, this.w, this.h);
+ // Draw Zoog's eyes
+ fill(0);
+ ellipse(
+ this.x - this.w / 3,
+ this.y - this.h,
+ this.eyeSize,
+ this.eyeSize * 2
+ );
+ ellipse(
+ this.x + this.w / 3,
+ this.y - this.h,
+ this.eyeSize,
+ this.eyeSize * 2
+ );
+ // Draw Zoog's legs
+ stroke(0);
+ line(
+ this.x - this.w / 12,
+ this.y + this.h / 2,
+ this.x - this.w / 4,
+ this.y + this.h / 2 + 10
+ );
+ line(
+ this.x + this.w / 12,
+ this.y + this.h / 2,
+ this.x + this.w / 4,
+ this.y + this.h / 2 + 10
+ );
+ }
+}
+
var zoog;
function setup() {
@@ -20,60 +87,3 @@ function draw() {
zoog.jiggle(factor);
zoog.display();
}
-
-function Zoog(tempX, tempY, tempW, tempH, tempEyeSize) {
- // Zoog's variables
- this.x = tempX;
- this.y = tempY;
- this.w = tempW;
- this.h = tempH;
- this.eyeSize = tempEyeSize;
-}
-
-// Move Zoog
-Zoog.prototype.jiggle = function(speed) {
- // Change the location of Zoog randomly
- this.x = this.x + random(-1, 1) * speed;
- this.y = this.y + random(-1, 1) * speed;
- // Constrain Zoog to window
- this.x = constrain(this.x, 0, width);
- this.y = constrain(this.y, 0, height);
-};
-
-// Display Zoog
-Zoog.prototype.display = function() {
- // Set ellipses and rects to CENTER mode
- ellipseMode(CENTER);
- rectMode(CENTER);
- // Draw Zoog's arms with a for loop
- for (var i = this.y - this.h / 3; i < this.y + this.h / 2; i += 10) {
- stroke(0);
- line(this.x - this.w / 4, i, this.x + this.w / 4, i);
- }
- // Draw Zoog's body
- stroke(0);
- fill(175);
- rect(this.x, this.y, this.w / 6, this.h);
- // Draw Zoog's head
- stroke(0);
- fill(255);
- ellipse(this.x, this.y - this.h, this.w, this.h);
- // Draw Zoog's eyes
- fill(0);
- ellipse(this.x - this.w / 3, this.y - this.h, this.eyeSize, this.eyeSize * 2);
- ellipse(this.x + this.w / 3, this.y - this.h, this.eyeSize, this.eyeSize * 2);
- // Draw Zoog's legs
- stroke(0);
- line(
- this.x - this.w / 12,
- this.y + this.h / 2,
- this.x - this.w / 4,
- this.y + this.h / 2 + 10
- );
- line(
- this.x + this.w / 12,
- this.y + this.h / 2,
- this.x + this.w / 4,
- this.y + this.h / 2 + 10
- );
-};
diff --git a/test/manual-test-examples/learningprocessing/chp9/example_9_10.js b/test/manual-test-examples/learningprocessing/chp9/example_9_10.js
index 2528538066..51ff5b1f09 100644
--- a/test/manual-test-examples/learningprocessing/chp9/example_9_10.js
+++ b/test/manual-test-examples/learningprocessing/chp9/example_9_10.js
@@ -7,6 +7,47 @@
// Example 9-10: Interactive stripes
// An array of stripes
+
+class Stripe {
+ constructor() {
+ // All stripes start at 0
+ this.x = 0; // horizontal location of stripe
+
+ // All stripes have a random positive speed
+ this.speed = random(1); // speed of stripe
+ this.w = random(10, 30); // width of stripe
+
+ // A boolean variable keeps track of the object's state.
+ this.mouse = false; // state of stripe (mouse is over or not?)
+ }
+ // Draw stripe
+ display() {
+ // Boolean variable determines Stripe color.
+ if (this.mouse) {
+ fill(255);
+ } else {
+ fill(255, 100);
+ }
+
+ noStroke();
+ rect(this.x, 0, this.w, height);
+ }
+ // Move stripe
+ move() {
+ this.x += this.speed;
+ if (this.x > width + 20) this.x = -20;
+ }
+ // Check to see if point (mx,my) is inside the Stripe.
+ rollover(mx, my) {
+ // Left edge is x, Right edge is x + w
+ if (mx > this.x && mx < this.x + this.w) {
+ this.mouse = true;
+ } else {
+ this.mouse = false;
+ }
+ }
+}
+
var stripes = [];
function setup() {
@@ -28,42 +69,3 @@ function draw() {
stripes[i].display();
}
}
-
-function Stripe() {
- // All stripes start at 0
- this.x = 0; // horizontal location of stripe
- // All stripes have a random positive speed
- this.speed = random(1); // speed of stripe
- this.w = random(10, 30); // width of stripe
- // A boolean variable keeps track of the object's state.
- this.mouse = false; // state of stripe (mouse is over or not?)
-}
-
-// Draw stripe
-Stripe.prototype.display = function() {
- // Boolean variable determines Stripe color.
- if (this.mouse) {
- fill(255);
- } else {
- fill(255, 100);
- }
-
- noStroke();
- rect(this.x, 0, this.w, height);
-};
-
-// Move stripe
-Stripe.prototype.move = function() {
- this.x += this.speed;
- if (this.x > width + 20) this.x = -20;
-};
-
-// Check to see if point (mx,my) is inside the Stripe.
-Stripe.prototype.rollover = function(mx, my) {
- // Left edge is x, Right edge is x + w
- if (mx > this.x && mx < this.x + this.w) {
- this.mouse = true;
- } else {
- this.mouse = false;
- }
-};
diff --git a/test/manual-test-examples/learningprocessing/chp9/example_9_11.js b/test/manual-test-examples/learningprocessing/chp9/example_9_11.js
index a18ab24b95..ca11a64669 100644
--- a/test/manual-test-examples/learningprocessing/chp9/example_9_11.js
+++ b/test/manual-test-examples/learningprocessing/chp9/example_9_11.js
@@ -6,6 +6,34 @@
var balls = [];
var gravity = 0.1;
+class Ball {
+ constructor(tempX, tempY, tempW) {
+ this.x = tempX;
+ this.y = tempY;
+ this.w = tempW;
+ this.speed = 0;
+ }
+ gravity() {
+ // Add gravity to speed
+ this.speed = this.speed + gravity;
+ }
+ move() {
+ // Add speed to y location
+ this.y = this.y + this.speed;
+ // If square reaches the bottom
+ // Reverse speed
+ if (this.y > height) {
+ this.speed = this.speed * -0.95;
+ this.y = height;
+ }
+ }
+ display() {
+ // Display the circle
+ fill(175);
+ stroke(0);
+ ellipse(this.x, this.y, this.w, this.w);
+ }
+}
function setup() {
createCanvas(200, 200);
@@ -40,33 +68,3 @@ function mousePressed() {
// In addition, the append() function requires that you explicitly state the type of data in the array again by putting the
// array data type in parentheses: (Ball[]) This is known as casting.
}
-
-function Ball(tempX, tempY, tempW) {
- this.x = tempX;
- this.y = tempY;
- this.w = tempW;
- this.speed = 0;
-}
-
-Ball.prototype.gravity = function() {
- // Add gravity to speed
- this.speed = this.speed + gravity;
-};
-
-Ball.prototype.move = function() {
- // Add speed to y location
- this.y = this.y + this.speed;
- // If square reaches the bottom
- // Reverse speed
- if (this.y > height) {
- this.speed = this.speed * -0.95;
- this.y = height;
- }
-};
-
-Ball.prototype.display = function() {
- // Display the circle
- fill(175);
- stroke(0);
- ellipse(this.x, this.y, this.w, this.w);
-};
diff --git a/test/manual-test-examples/learningprocessing/chp9/example_9_12.js b/test/manual-test-examples/learningprocessing/chp9/example_9_12.js
index f310263d44..5535bdbdf2 100644
--- a/test/manual-test-examples/learningprocessing/chp9/example_9_12.js
+++ b/test/manual-test-examples/learningprocessing/chp9/example_9_12.js
@@ -8,6 +8,75 @@
// The only difference between this example and the previous chapter (Example 8-3)
// is the use of an array for multiple Zoog objects.
+class Zoog {
+ constructor(tempX, tempY, tempW, tempH, tempEyeSize) {
+ // Zoog's variables
+ this.x = tempX;
+ this.y = tempY;
+ this.w = tempW;
+ this.h = tempH;
+ this.eyeSize = tempEyeSize;
+ }
+ // Move Zoog
+ jiggle() {
+ // For simplicity we have also removed the “speed” argument from the jiggle() function. Try adding it back in as an exercise.
+ // Change the location
+ this.x = this.x + random(-1, 1);
+ this.y = this.y + random(-1, 1);
+ // Constrain Zoog to window
+ this.x = constrain(this.x, 0, width);
+ this.y = constrain(this.y, 0, height);
+ }
+ // Display Zoog
+ display() {
+ // Set ellipses and rects to CENTER mode
+ ellipseMode(CENTER);
+ rectMode(CENTER);
+ // Draw Zoog's arms with a for loop
+ for (var i = this.y - this.h / 3; i < this.y + this.h / 2; i += 10) {
+ stroke(0);
+ line(this.x - this.w / 4, i, this.x + this.w / 4, i);
+ }
+ // Draw Zoog's body
+ stroke(0);
+ fill(175);
+ rect(this.x, this.y, this.w / 6, this.h);
+ // Draw Zoog's head
+ stroke(0);
+ fill(255);
+ ellipse(this.x, this.y - this.h, this.w, this.h);
+ // Draw Zoog's eyes
+ fill(0);
+ ellipse(
+ this.x - this.w / 3,
+ this.y - this.h,
+ this.eyeSize,
+ this.eyeSize * 2
+ );
+ ellipse(
+ this.x + this.w / 3,
+ this.y - this.h,
+ this.eyeSize,
+ this.eyeSize * 2
+ );
+ // Draw Zoog's legs
+ stroke(0);
+ line(
+ this.x - this.w / 12,
+ this.y + this.h / 2,
+ this.x - this.w / 4,
+ this.y + this.h / 2 + 10
+ );
+ line(
+ this.x + this.w / 12,
+ this.y + this.h / 2,
+ this.x + this.w / 4,
+ this.y + this.h / 2 + 10
+ );
+ }
+}
+
+
var zoogies = [];
function setup() {
@@ -26,60 +95,3 @@ function draw() {
}
}
-function Zoog(tempX, tempY, tempW, tempH, tempEyeSize) {
- // Zoog's variables
- this.x = tempX;
- this.y = tempY;
- this.w = tempW;
- this.h = tempH;
- this.eyeSize = tempEyeSize;
-}
-
-// Move Zoog
-Zoog.prototype.jiggle = function() {
- // For simplicity we have also removed the “speed” argument from the jiggle() function. Try adding it back in as an exercise.
- // Change the location
- this.x = this.x + random(-1, 1);
- this.y = this.y + random(-1, 1);
- // Constrain Zoog to window
- this.x = constrain(this.x, 0, width);
- this.y = constrain(this.y, 0, height);
-};
-
-// Display Zoog
-Zoog.prototype.display = function() {
- // Set ellipses and rects to CENTER mode
- ellipseMode(CENTER);
- rectMode(CENTER);
- // Draw Zoog's arms with a for loop
- for (var i = this.y - this.h / 3; i < this.y + this.h / 2; i += 10) {
- stroke(0);
- line(this.x - this.w / 4, i, this.x + this.w / 4, i);
- }
- // Draw Zoog's body
- stroke(0);
- fill(175);
- rect(this.x, this.y, this.w / 6, this.h);
- // Draw Zoog's head
- stroke(0);
- fill(255);
- ellipse(this.x, this.y - this.h, this.w, this.h);
- // Draw Zoog's eyes
- fill(0);
- ellipse(this.x - this.w / 3, this.y - this.h, this.eyeSize, this.eyeSize * 2);
- ellipse(this.x + this.w / 3, this.y - this.h, this.eyeSize, this.eyeSize * 2);
- // Draw Zoog's legs
- stroke(0);
- line(
- this.x - this.w / 12,
- this.y + this.h / 2,
- this.x - this.w / 4,
- this.y + this.h / 2 + 10
- );
- line(
- this.x + this.w / 12,
- this.y + this.h / 2,
- this.x + this.w / 4,
- this.y + this.h / 2 + 10
- );
-};
diff --git a/test/manual-test-examples/learningprocessing/chp9/example_9_9.js b/test/manual-test-examples/learningprocessing/chp9/example_9_9.js
index be39507dd4..d51317457b 100644
--- a/test/manual-test-examples/learningprocessing/chp9/example_9_9.js
+++ b/test/manual-test-examples/learningprocessing/chp9/example_9_9.js
@@ -6,6 +6,28 @@
// Example 9-9: An array of Car objects
+// The Car class does not change whether we are making one car, 100 cars or 1,000 cars!
+class Car {
+ constructor(c, xpos, ypos, xspeed) {
+ this.c = c;
+ this.xpos = xpos;
+ this.ypos = ypos;
+ this.xspeed = xspeed;
+ }
+ display() {
+ rectMode(CENTER);
+ stroke(0);
+ fill(this.c);
+ rect(this.xpos, this.ypos, 20, 10);
+ }
+ move() {
+ this.xpos = this.xpos + this.xspeed;
+ if (this.xpos > width) {
+ this.xpos = 0;
+ }
+ }
+}
+
var cars = []; // An array for Car objects
function setup() {
@@ -25,25 +47,3 @@ function draw() {
cars[i].display();
}
}
-
-// The Car class does not change whether we are making one car, 100 cars or 1,000 cars!
-function Car(c, xpos, ypos, xspeed) {
- this.c = c;
- this.xpos = xpos;
- this.ypos = ypos;
- this.xspeed = xspeed;
-}
-
-Car.prototype.display = function() {
- rectMode(CENTER);
- stroke(0);
- fill(this.c);
- rect(this.xpos, this.ypos, 20, 10);
-};
-
-Car.prototype.move = function() {
- this.xpos = this.xpos + this.xspeed;
- if (this.xpos > width) {
- this.xpos = 0;
- }
-};
diff --git a/test/manual-test-examples/p5.Font/pathpoints/boids.js b/test/manual-test-examples/p5.Font/pathpoints/boids.js
index 9fa0eb3bfc..c868aa9c33 100644
--- a/test/manual-test-examples/p5.Font/pathpoints/boids.js
+++ b/test/manual-test-examples/p5.Font/pathpoints/boids.js
@@ -1,32 +1,34 @@
// adapted from Shiffman's The Nature of Code
// http://natureofcode.com
-function Boid(target) {
- this.acceleration = createVector(0, 0);
- this.velocity = createVector(random(-1, 1), random(-1, 1));
- this.position = createVector(width / 2, height / 2);
-
- this.r = 3.0;
- this.maxspeed = 3; // Maximum speed
- this.maxforce = 0.05; // Maximum steering force
-
- this.theta =
- p5.Vector.fromAngle(radians(target.alpha)).heading() + radians(90);
- this.target = createVector(target.x, target.y);
- this.arrived = false;
- this.hidden = true;
-
- this.place = function(x, y) {
+class Boid {
+ constructor(target) {
+ this.acceleration = createVector(0, 0);
+ this.velocity = createVector(random(-1, 1), random(-1, 1));
+ this.position = createVector(width / 2, height / 2);
+
+ this.r = 3.0;
+ this.maxspeed = 3; // Maximum speed
+ this.maxforce = 0.05; // Maximum steering force
+
+ this.theta =
+ p5.Vector.fromAngle(radians(target.alpha)).heading() + radians(90);
+ this.target = createVector(target.x, target.y);
+ this.arrived = false;
+ this.hidden = true;
+ }
+ place (x, y) {
this.position = createVector(mouseX, mouseY);
this.velocity = p5.Vector.sub(
createVector(mouseX, mouseY),
createVector(pmouseX, pmouseY)
);
this.hidden = false;
- };
+ }
- this.run = function(boids) {
- if (this.hidden) return;
+ run (boids) {
+ if (this.hidden)
+ return;
if (flock.assemble) {
this.arrive(this.target);
@@ -36,18 +38,19 @@ function Boid(target) {
this.update();
this.borders();
this.render();
- };
+ }
- this.applyForce = function(force) {
+ applyForce (force) {
// We could add mass here if we want A = F / M
this.acceleration.add(force);
- };
+ }
// We accumulate a new acceleration each time based on three rules
- this.flock = function(boids) {
+ flock (boids) {
var sep = this.separate(boids); // Separation
var ali = this.align(boids); // Alignment
var coh = this.cohesion(boids); // Cohesion
+
// Arbitrarily weight these forces
sep.mult(1.5);
ali.mult(1.0);
@@ -56,15 +59,13 @@ function Boid(target) {
this.applyForce(sep);
this.applyForce(ali);
this.applyForce(coh);
- };
+ }
// Method to update location
- this.update = function() {
- if (
- flock.assemble &&
- !this.arrived &&
- this.target.dist(this.position) < 1
- ) {
+ update () {
+ if (flock.assemble &&
+ !this.arrived &&
+ this.target.dist(this.position) < 1) {
this.arrived = true;
this.velocity = p5.Vector.fromAngle(this.theta + radians(90));
} else {
@@ -73,9 +74,9 @@ function Boid(target) {
this.position.add(this.velocity);
this.acceleration.mult(0);
}
- };
+ }
- this.seek = function(target) {
+ seek (target) {
var desired = p5.Vector.sub(target, this.position);
// Normalize desired and scale to maximum speed
desired.normalize();
@@ -84,9 +85,9 @@ function Boid(target) {
var steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxforce); // Limit to maximum steering force
return steer;
- };
+ }
- this.render = function() {
+ render () {
// Draw a triangle rotated in the direction of velocity
var theta = this.velocity.heading() + radians(90);
fill(255);
@@ -100,19 +101,23 @@ function Boid(target) {
vertex(this.r, this.r * 2);
endShape(CLOSE);
pop();
- };
+ }
// Wraparound
- this.borders = function() {
- if (this.position.x < -this.r) this.position.x = width + this.r;
- if (this.position.y < -this.r) this.position.y = height + this.r;
- if (this.position.x > width + this.r) this.position.x = -this.r;
- if (this.position.y > height + this.r) this.position.y = -this.r;
- };
+ borders () {
+ if (this.position.x < -this.r)
+ this.position.x = width + this.r;
+ if (this.position.y < -this.r)
+ this.position.y = height + this.r;
+ if (this.position.x > width + this.r)
+ this.position.x = -this.r;
+ if (this.position.y > height + this.r)
+ this.position.y = -this.r;
+ }
// Separation
// Method checks for nearby boids and steers away
- this.separate = function(boids) {
+ separate (boids) {
var desiredseparation = 25.0;
var steer = createVector(0, 0);
var count = 0;
@@ -143,11 +148,11 @@ function Boid(target) {
steer.limit(this.maxforce);
}
return steer;
- };
+ }
// Alignment
// For every nearby boid in the system, calculate the average velocity
- this.align = function(boids) {
+ align (boids) {
var neighbordist = 50;
var sum = createVector(0, 0);
var count = 0;
@@ -168,11 +173,11 @@ function Boid(target) {
} else {
return createVector(0, 0);
}
- };
+ }
// Cohesion
// For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
- this.cohesion = function(boids) {
+ cohesion (boids) {
var neighbordist = 50;
var sum = createVector(0, 0); // Start with empty vector to accumulate all locations
var num = 0;
@@ -188,12 +193,11 @@ function Boid(target) {
} else {
return createVector(0, 0);
}
- };
+ }
- Boid.prototype.arrive = function(target) {
+ arrive (target) {
// A vector pointing from the location to the target
- var desired = p5.Vector.sub(target, this.position),
- d = desired.mag();
+ var desired = p5.Vector.sub(target, this.position), d = desired.mag();
// Scale with arbitrary damping within 100 pixels
desired.setMag(d < 100 ? map(d, 0, 100, 0, this.maxspeed) : this.maxspeed);
@@ -202,19 +206,21 @@ function Boid(target) {
var steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxforce); // Limit to maximum steering force
this.applyForce(steer);
- };
+ }
+
}
function mouseOnScreen() {
return mouseX && mouseX <= width && mouseY && mouseY <= height;
}
-function Flock() {
- this.count = 0;
- this.boids = [];
- this.assemble = false;
-
- this.arrived = function() {
+class Flock {
+ constructor() {
+ this.count = 0;
+ this.boids = [];
+ this.assemble = false;
+ }
+ arrived() {
var i;
if (arguments.length) {
for (i = 0; i < this.boids.length; i++)
@@ -225,14 +231,14 @@ function Flock() {
if (!this.boids[i].arrived) return false;
return true;
}
- };
+ }
- this.run = function() {
+ run() {
this.assemble = this.count === flock.boids.length;
if (!this.assemble && mouseOnScreen())
this.boids[this.count++].place(mouseX, mouseY);
for (var i = 0; i < this.boids.length; i++) this.boids[i].run(this.boids);
- };
+ }
}
diff --git a/test/manual-test-examples/p5.Vector/NOC_2_5_fluidresistance/mover.js b/test/manual-test-examples/p5.Vector/NOC_2_5_fluidresistance/mover.js
index c201b3e3fd..abe950e69d 100644
--- a/test/manual-test-examples/p5.Vector/NOC_2_5_fluidresistance/mover.js
+++ b/test/manual-test-examples/p5.Vector/NOC_2_5_fluidresistance/mover.js
@@ -2,40 +2,38 @@
// Daniel Shiffman
// http://natureofcode.com
-function Mover(m, x, y) {
- this.mass = m;
- this.position = createVector(x, y);
- this.velocity = createVector(0, 0);
- this.acceleration = createVector(0, 0);
-}
-
-// Newton's 2nd law: F = M * A
-// or A = F / M
-Mover.prototype.applyForce = function(force) {
- var f = p5.Vector.div(force, this.mass);
- this.acceleration.add(f);
-};
-
-Mover.prototype.update = function() {
- // Velocity changes according to acceleration
- this.velocity.add(this.acceleration);
- // position changes by velocity
- this.position.add(this.velocity);
- // We must clear acceleration each frame
- this.acceleration.mult(0);
-};
-
-Mover.prototype.display = function() {
- stroke(0);
- strokeWeight(2);
- fill(255, 127);
- ellipse(this.position.x, this.position.y, this.mass * 16, this.mass * 16);
-};
-
-// Bounce off bottom of window
-Mover.prototype.checkEdges = function() {
- if (this.position.y > height) {
- this.velocity.y *= -0.9; // A little dampening when hitting the bottom
- this.position.y = height;
+class Mover {
+ constructor(m, x, y) {
+ this.mass = m;
+ this.position = createVector(x, y);
+ this.velocity = createVector(0, 0);
+ this.acceleration = createVector(0, 0);
}
-};
+ // Newton's 2nd law: F = M * A
+ // or A = F / M
+ applyForce(force) {
+ var f = p5.Vector.div(force, this.mass);
+ this.acceleration.add(f);
+ }
+ update() {
+ // Velocity changes according to acceleration
+ this.velocity.add(this.acceleration);
+ // position changes by velocity
+ this.position.add(this.velocity);
+ // We must clear acceleration each frame
+ this.acceleration.mult(0);
+ }
+ display() {
+ stroke(0);
+ strokeWeight(2);
+ fill(255, 127);
+ ellipse(this.position.x, this.position.y, this.mass * 16, this.mass * 16);
+ }
+ // Bounce off bottom of window
+ checkEdges() {
+ if (this.position.y > height) {
+ this.velocity.y *= -0.9; // A little dampening when hitting the bottom
+ this.position.y = height;
+ }
+ }
+}
diff --git a/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/boid.js b/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/boid.js
index ed1f796dbe..b1c411ed0d 100644
--- a/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/boid.js
+++ b/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/boid.js
@@ -5,167 +5,165 @@
// Boid class
// Methods for Separation, Cohesion, Alignment added
-function Boid(x, y) {
- this.acceleration = createVector(0, 0);
- this.velocity = createVector(random(-1, 1), random(-1, 1));
- this.position = createVector(x, y);
- this.r = 3.0;
- this.maxspeed = 3; // Maximum speed
- this.maxforce = 0.05; // Maximum steering force
-}
-
-Boid.prototype.run = function(boids) {
- this.flock(boids);
- this.update();
- this.borders();
- this.render();
-};
-
-Boid.prototype.applyForce = function(force) {
- // We could add mass here if we want A = F / M
- this.acceleration.add(force);
-};
-
-// We accumulate a new acceleration each time based on three rules
-Boid.prototype.flock = function(boids) {
- var sep = this.separate(boids); // Separation
- var ali = this.align(boids); // Alignment
- var coh = this.cohesion(boids); // Cohesion
- // Arbitrarily weight these forces
- sep.mult(1.5);
- ali.mult(1.0);
- coh.mult(1.0);
- // Add the force vectors to acceleration
- this.applyForce(sep);
- this.applyForce(ali);
- this.applyForce(coh);
-};
-
-// Method to update location
-Boid.prototype.update = function() {
- // Update velocity
- this.velocity.add(this.acceleration);
- // Limit speed
- this.velocity.limit(this.maxspeed);
- this.position.add(this.velocity);
- // Reset accelertion to 0 each cycle
- this.acceleration.mult(0);
-};
-
-// A method that calculates and applies a steering force towards a target
-// STEER = DESIRED MINUS VELOCITY
-Boid.prototype.seek = function(target) {
- var desired = p5.Vector.sub(target, this.position); // A vector pointing from the location to the target
- // Normalize desired and scale to maximum speed
- desired.normalize();
- desired.mult(this.maxspeed);
- // Steering = Desired minus Velocity
- var steer = p5.Vector.sub(desired, this.velocity);
- steer.limit(this.maxforce); // Limit to maximum steering force
- return steer;
-};
-
-Boid.prototype.render = function() {
- // Draw a triangle rotated in the direction of velocity
- var theta = this.velocity.heading() + radians(90);
- fill(127);
- stroke(200);
- push();
- translate(this.position.x, this.position.y);
- rotate(theta);
- beginShape();
- vertex(0, -this.r * 2);
- vertex(-this.r, this.r * 2);
- vertex(this.r, this.r * 2);
- endShape(CLOSE);
- pop();
-};
-
-// Wraparound
-Boid.prototype.borders = function() {
- if (this.position.x < -this.r) this.position.x = width + this.r;
- if (this.position.y < -this.r) this.position.y = height + this.r;
- if (this.position.x > width + this.r) location.x = -this.r;
- if (this.position.y > height + this.r) location.y = -this.r;
-};
+class Boid {
+ constructor(x, y) {
+ this.acceleration = createVector(0, 0);
+ this.velocity = createVector(random(-1, 1), random(-1, 1));
+ this.position = createVector(x, y);
+ this.r = 3.0;
+ this.maxspeed = 3; // Maximum speed
+ this.maxforce = 0.05; // Maximum steering force
+ }
+ run(boids) {
+ this.flock(boids);
+ this.update();
+ this.borders();
+ this.render();
+ }
+ applyForce(force) {
+ // We could add mass here if we want A = F / M
+ this.acceleration.add(force);
+ }
+ // We accumulate a new acceleration each time based on three rules
+ flock(boids) {
+ var sep = this.separate(boids); // Separation
+ var ali = this.align(boids); // Alignment
+ var coh = this.cohesion(boids); // Cohesion
-// Separation
-// Method checks for nearby boids and steers away
-Boid.prototype.separate = function(boids) {
- var desiredseparation = 25.0;
- var steer = createVector(0, 0);
- var count = 0;
- // For every boid in the system, check if it's too close
- for (var i = 0; i < boids.length; i++) {
- var d = p5.Vector.dist(this.position, boids[i].position);
- // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
- if (d > 0 && d < desiredseparation) {
- // Calculate vector pointing away from neighbor
- var diff = p5.Vector.sub(this.position, boids[i].position);
- diff.normalize();
- diff.div(d); // Weight by distance
- steer.add(diff);
- count++; // Keep track of how many
- }
+ // Arbitrarily weight these forces
+ sep.mult(1.5);
+ ali.mult(1.0);
+ coh.mult(1.0);
+ // Add the force vectors to acceleration
+ this.applyForce(sep);
+ this.applyForce(ali);
+ this.applyForce(coh);
}
- // Average -- divide by how many
- if (count > 0) {
- steer.div(count);
+ // Method to update location
+ update() {
+ // Update velocity
+ this.velocity.add(this.acceleration);
+ // Limit speed
+ this.velocity.limit(this.maxspeed);
+ this.position.add(this.velocity);
+ // Reset accelertion to 0 each cycle
+ this.acceleration.mult(0);
}
+ // A method that calculates and applies a steering force towards a target
+ // STEER = DESIRED MINUS VELOCITY
+ seek(target) {
+ var desired = p5.Vector.sub(target, this.position); // A vector pointing from the location to the target
- // As long as the vector is greater than 0
- if (steer.mag() > 0) {
- // Implement Reynolds: Steering = Desired - Velocity
- steer.normalize();
- steer.mult(this.maxspeed);
- steer.sub(this.velocity);
- steer.limit(this.maxforce);
+ // Normalize desired and scale to maximum speed
+ desired.normalize();
+ desired.mult(this.maxspeed);
+ // Steering = Desired minus Velocity
+ var steer = p5.Vector.sub(desired, this.velocity);
+ steer.limit(this.maxforce); // Limit to maximum steering force
+ return steer;
+ }
+ render() {
+ // Draw a triangle rotated in the direction of velocity
+ var theta = this.velocity.heading() + radians(90);
+ fill(127);
+ stroke(200);
+ push();
+ translate(this.position.x, this.position.y);
+ rotate(theta);
+ beginShape();
+ vertex(0, -this.r * 2);
+ vertex(-this.r, this.r * 2);
+ vertex(this.r, this.r * 2);
+ endShape(CLOSE);
+ pop();
+ }
+ // Wraparound
+ borders() {
+ if (this.position.x < -this.r)
+ this.position.x = width + this.r;
+ if (this.position.y < -this.r)
+ this.position.y = height + this.r;
+ if (this.position.x > width + this.r)
+ location.x = -this.r;
+ if (this.position.y > height + this.r)
+ location.y = -this.r;
}
- return steer;
-};
+ // Separation
+ // Method checks for nearby boids and steers away
+ separate(boids) {
+ var desiredseparation = 25.0;
+ var steer = createVector(0, 0);
+ var count = 0;
+ // For every boid in the system, check if it's too close
+ for (var i = 0; i < boids.length; i++) {
+ var d = p5.Vector.dist(this.position, boids[i].position);
+ // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
+ if (d > 0 && d < desiredseparation) {
+ // Calculate vector pointing away from neighbor
+ var diff = p5.Vector.sub(this.position, boids[i].position);
+ diff.normalize();
+ diff.div(d); // Weight by distance
+ steer.add(diff);
+ count++; // Keep track of how many
+ }
+ }
+ // Average -- divide by how many
+ if (count > 0) {
+ steer.div(count);
+ }
-// Alignment
-// For every nearby boid in the system, calculate the average velocity
-Boid.prototype.align = function(boids) {
- var neighbordist = 50;
- var sum = createVector(0, 0);
- var count = 0;
- for (var i = 0; i < boids.length; i++) {
- var d = p5.Vector.dist(this.position, boids[i].position);
- if (d > 0 && d < neighbordist) {
- sum.add(boids[i].velocity);
- count++;
+ // As long as the vector is greater than 0
+ if (steer.mag() > 0) {
+ // Implement Reynolds: Steering = Desired - Velocity
+ steer.normalize();
+ steer.mult(this.maxspeed);
+ steer.sub(this.velocity);
+ steer.limit(this.maxforce);
}
- }
- if (count > 0) {
- sum.div(count);
- sum.normalize();
- sum.mult(this.maxspeed);
- var steer = p5.Vector.sub(sum, this.velocity);
- steer.limit(this.maxforce);
return steer;
- } else {
- return createVector(0, 0);
}
-};
-
-// Cohesion
-// For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
-Boid.prototype.cohesion = function(boids) {
- var neighbordist = 50;
- var sum = createVector(0, 0); // Start with empty vector to accumulate all locations
- var count = 0;
- for (var i = 0; i < boids.length; i++) {
- var d = p5.Vector.dist(this.position, boids[i].position);
- if (d > 0 && d < neighbordist) {
- sum.add(boids[i].position); // Add location
- count++;
+ // Alignment
+ // For every nearby boid in the system, calculate the average velocity
+ align(boids) {
+ var neighbordist = 50;
+ var sum = createVector(0, 0);
+ var count = 0;
+ for (var i = 0; i < boids.length; i++) {
+ var d = p5.Vector.dist(this.position, boids[i].position);
+ if (d > 0 && d < neighbordist) {
+ sum.add(boids[i].velocity);
+ count++;
+ }
+ }
+ if (count > 0) {
+ sum.div(count);
+ sum.normalize();
+ sum.mult(this.maxspeed);
+ var steer = p5.Vector.sub(sum, this.velocity);
+ steer.limit(this.maxforce);
+ return steer;
+ } else {
+ return createVector(0, 0);
}
}
- if (count > 0) {
- sum.div(count);
- return this.seek(sum); // Steer towards the location
- } else {
- return createVector(0, 0);
+ // Cohesion
+ // For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
+ cohesion(boids) {
+ var neighbordist = 50;
+ var sum = createVector(0, 0); // Start with empty vector to accumulate all locations
+ var count = 0;
+ for (var i = 0; i < boids.length; i++) {
+ var d = p5.Vector.dist(this.position, boids[i].position);
+ if (d > 0 && d < neighbordist) {
+ sum.add(boids[i].position); // Add location
+ count++;
+ }
+ }
+ if (count > 0) {
+ sum.div(count);
+ return this.seek(sum); // Steer towards the location
+ } else {
+ return createVector(0, 0);
+ }
}
-};
+}
diff --git a/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/flock.js b/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/flock.js
index 3cd00323de..1708e485c6 100644
--- a/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/flock.js
+++ b/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/flock.js
@@ -5,17 +5,17 @@
// Flock object
// Does very little, simply manages the array of all the boids
-function Flock() {
- // An array for all the boids
- this.boids = []; // Initialize the array
-}
-
-Flock.prototype.run = function() {
- for (var i = 0; i < this.boids.length; i++) {
- this.boids[i].run(this.boids); // Passing the entire list of boids to each boid individually
+class Flock {
+ constructor() {
+ // An array for all the boids
+ this.boids = []; // Initialize the array
}
-};
-
-Flock.prototype.addBoid = function(b) {
- this.boids.push(b);
-};
+ run() {
+ for (var i = 0; i < this.boids.length; i++) {
+ this.boids[i].run(this.boids); // Passing the entire list of boids to each boid individually
+ }
+ }
+ addBoid(b) {
+ this.boids.push(b);
+ }
+}
diff --git a/test/manual-test-examples/webgl/performance/points/sketch.js b/test/manual-test-examples/webgl/performance/points/sketch.js
index f7fb3fc4f5..aa4de2b297 100644
--- a/test/manual-test-examples/webgl/performance/points/sketch.js
+++ b/test/manual-test-examples/webgl/performance/points/sketch.js
@@ -1,20 +1,21 @@
-function Particle(x, y) {
- this.pos = createVector(x, y);
- this.vel = p5.Vector.random2D();
- this.vel.setMag(random(2, 5));
- this.acc = createVector();
-
- this.update = function() {
+class Particle {
+ constructor(x, y) {
+ this.pos = createVector(x, y);
+ this.vel = p5.Vector.random2D();
+ this.vel.setMag(random(2, 5));
+ this.acc = createVector();
+ }
+ update () {
this.pos.add(this.vel);
this.vel.add(this.acc);
this.acc.mult(0);
- };
+ }
- this.show = function() {
+ show () {
vertex(this.pos.x, this.pos.y);
- };
+ }
- this.attracted = function(target) {
+ attracted (target) {
var force = p5.Vector.sub(target, this.pos);
var dsquared = force.magSq();
dsquared = constrain(dsquared, 25, 500);
@@ -22,7 +23,8 @@ function Particle(x, y) {
var strength = G / dsquared;
force.setMag(strength);
this.acc.add(force);
- };
+ }
+
}
var attractor_1;