From 60febd482bf07b9a5772a0394cdea24ec7bfebca Mon Sep 17 00:00:00 2001 From: Simonpso <163044785+Simonpso@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:10:36 -0400 Subject: [PATCH 1/5] Update README.md --- estudiantes/23-Simonpso/clase-15/README.md | 89 ++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/estudiantes/23-Simonpso/clase-15/README.md b/estudiantes/23-Simonpso/clase-15/README.md index c4e5f6c6..7b0e1e7c 100644 --- a/estudiantes/23-Simonpso/clase-15/README.md +++ b/estudiantes/23-Simonpso/clase-15/README.md @@ -1 +1,90 @@ # clase-15 + +--- + +24.06.2024 + +--- + +El siguiente codigo utiliza la biblioteca p5.createloop.js encontrada en https://www.npmjs.com/package/p5.createloop?activeTab=readme el cual genera un bucle animado que produce un gift + +En la pagina indica que es necesario un html para poder usarlo, asi que lo copie en el index de p5 editor. + +```javascript + + +``` + +--- + +Para empezar definimos las constantes dentro del programa + +Semillas dentro del ruido: +```javascript +// La siguiente constante define semillas aleatorias para el ruido +const seedX = Math.random() +const seedY = Math.random() +``` + +Radio de los círculos: +```javascript +// Define los radios de los círculos generados por el ruido +const radX = 1 +const radY = 4 +``` + +Variable de las amplitudes de la animacion: +```javascript +// Variable para amplitudes +let ampX +let ampY +``` + +Configuracion del gif: +```javascript +// Esta constante define la configuracion del gif +const gif = { + startLoop: 4, + endLoop: 2, + fileName: "noiseLoop4x.gif" +``` + +--- +Seguimos estableciendo distintos parametros dentro del codigo como: + +- El tamaño del lienzo. +- La configuracion del modo de color. +- La tasa de frames. +- El color del fondo. +- El contorno. +- Generar un bucle. +- Calcular las amplitudes de la animación. +```javascript +function setup() { + // Creacion del lienzo midendo este 600 x 600 + createCanvas(600, 600) + + // La configuracion del modo del color + colorMode(HSB, 1, 1, 1) + + // Establecer la tasa de frames a 200 + frameRate(200) + + // Configurar el color del fondo a negro + background(0) + fill(100) + + // No hacer un contorno a las figuras + noStroke() + + // Crear el bucle animado como e identificarlo como gif + createLoop(9, { gif }) + + // Calcula las amplitudes de la animación a lo largo del lienzo + ampX = width / 1 + ampY = height / 2 +} +``` + +--- + From 7ffd3158277b78fbd62ede922ddc9905db850cc3 Mon Sep 17 00:00:00 2001 From: Simonpso <163044785+Simonpso@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:16:21 -0400 Subject: [PATCH 2/5] Update README.md --- estudiantes/23-Simonpso/clase-15/README.md | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/estudiantes/23-Simonpso/clase-15/README.md b/estudiantes/23-Simonpso/clase-15/README.md index 7b0e1e7c..c9b73543 100644 --- a/estudiantes/23-Simonpso/clase-15/README.md +++ b/estudiantes/23-Simonpso/clase-15/README.md @@ -59,6 +59,7 @@ Seguimos estableciendo distintos parametros dentro del codigo como: - El contorno. - Generar un bucle. - Calcular las amplitudes de la animación. + ```javascript function setup() { // Creacion del lienzo midendo este 600 x 600 @@ -88,3 +89,31 @@ function setup() { --- +Ahora seguimos con la function draw donde establecemos otros parametros dentro del codigo como: + +- El color de relleno y que este cambia dependiendo al progreso del bucle. +- Traasladar el punto de origen dentro del lienzo. +- Crear coordenadas x e y +- Dibujar y editar las medidas del circulo. + +```javascript +function draw() { + // background(0) + + // El color del relleno basado en el progreso del bucle + fill(animLoop.progress, 6, 8) + const density = 0.05 + + // Traslada el punto de origen al centro del lienzo + translate(width / 3, height / 2) + + // Creacion de coordenadas x e y usando ruido + const x = animLoop.noise({ radius: radX, seed: seedX }) * ampX + const y = animLoop.noise({ radius: radY, seed: seedY }) * ampY + + // Dibujar un circulo acorde a las coordenadas establecidas + ellipse(x, y, 50, 30) +} +``` + +Link del codigo: https://editor.p5js.org/Simonpso/sketches/KGohbtrTe From 75f3d300ad0a6c303d29cf8d57cd421f54df2e16 Mon Sep 17 00:00:00 2001 From: Simonpso <163044785+Simonpso@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:53:24 -0400 Subject: [PATCH 3/5] Update README.md --- estudiantes/23-Simonpso/clase-15/README.md | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/estudiantes/23-Simonpso/clase-15/README.md b/estudiantes/23-Simonpso/clase-15/README.md index c9b73543..db04bcd3 100644 --- a/estudiantes/23-Simonpso/clase-15/README.md +++ b/estudiantes/23-Simonpso/clase-15/README.md @@ -8,7 +8,7 @@ El siguiente codigo utiliza la biblioteca p5.createloop.js encontrada en https://www.npmjs.com/package/p5.createloop?activeTab=readme el cual genera un bucle animado que produce un gift -En la pagina indica que es necesario un html para poder usarlo, asi que lo copie en el index de p5 editor. +En la página indica que es necesario un html para poder usarlo, así que lo copie en el index de p5 editor. ```javascript @@ -33,16 +33,16 @@ const radX = 1 const radY = 4 ``` -Variable de las amplitudes de la animacion: +Variable de las amplitudes de la animación: ```javascript // Variable para amplitudes let ampX let ampY ``` -Configuracion del gif: +Configuración del gif: ```javascript -// Esta constante define la configuracion del gif +// Esta constante define la configuración del gif const gif = { startLoop: 4, endLoop: 2, @@ -50,7 +50,7 @@ const gif = { ``` --- -Seguimos estableciendo distintos parametros dentro del codigo como: +Seguimos estableciendo distintos parámetros dentro del código como: - El tamaño del lienzo. - La configuracion del modo de color. @@ -62,10 +62,10 @@ Seguimos estableciendo distintos parametros dentro del codigo como: ```javascript function setup() { - // Creacion del lienzo midendo este 600 x 600 + // Creación del lienzo midiendo este 600 x 600 createCanvas(600, 600) - // La configuracion del modo del color + // La configuración del modo del color colorMode(HSB, 1, 1, 1) // Establecer la tasa de frames a 200 @@ -89,12 +89,12 @@ function setup() { --- -Ahora seguimos con la function draw donde establecemos otros parametros dentro del codigo como: +Ahora seguimos con la function draw donde establecemos otros parámetros dentro del codigo como: - El color de relleno y que este cambia dependiendo al progreso del bucle. -- Traasladar el punto de origen dentro del lienzo. +- Trasladar el punto de origen dentro del lienzo. - Crear coordenadas x e y -- Dibujar y editar las medidas del circulo. +- Dibujar y editar las medidas del círculo. ```javascript function draw() { @@ -107,11 +107,11 @@ function draw() { // Traslada el punto de origen al centro del lienzo translate(width / 3, height / 2) - // Creacion de coordenadas x e y usando ruido + // Creación de coordenadas x e y usando ruido const x = animLoop.noise({ radius: radX, seed: seedX }) * ampX const y = animLoop.noise({ radius: radY, seed: seedY }) * ampY - // Dibujar un circulo acorde a las coordenadas establecidas + // Dibujar un círculo acorde a las coordenadas establecidas ellipse(x, y, 50, 30) } ``` From aef6046d288de7c1eaf68a18a18d7de6ef08d264 Mon Sep 17 00:00:00 2001 From: Simonpso <163044785+Simonpso@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:59:51 -0400 Subject: [PATCH 4/5] Update README.md From af1ee73ecdabca3f4b336a429fafa0f3ba3eb44a Mon Sep 17 00:00:00 2001 From: Simonpso <163044785+Simonpso@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:01:08 -0400 Subject: [PATCH 5/5] Add files via upload --- estudiantes/23-Simonpso/clase-15/index.html | 15 + .../23-Simonpso/clase-15/p5.createloop.js | 178 + estudiantes/23-Simonpso/clase-15/p5.js | 144657 +++++++++++++++ .../23-Simonpso/clase-15/p5.sound.min.js | 3 + estudiantes/23-Simonpso/clase-15/sketch.js | 63 + estudiantes/23-Simonpso/clase-15/style.css | 7 + 6 files changed, 144923 insertions(+) create mode 100644 estudiantes/23-Simonpso/clase-15/index.html create mode 100644 estudiantes/23-Simonpso/clase-15/p5.createloop.js create mode 100644 estudiantes/23-Simonpso/clase-15/p5.js create mode 100644 estudiantes/23-Simonpso/clase-15/p5.sound.min.js create mode 100644 estudiantes/23-Simonpso/clase-15/sketch.js create mode 100644 estudiantes/23-Simonpso/clase-15/style.css diff --git a/estudiantes/23-Simonpso/clase-15/index.html b/estudiantes/23-Simonpso/clase-15/index.html new file mode 100644 index 00000000..12569d64 --- /dev/null +++ b/estudiantes/23-Simonpso/clase-15/index.html @@ -0,0 +1,15 @@ + + +
+ + + + + + + +description of the canvas.
\n', + 'type': 'String' + }, + { + 'name': 'display', + 'description': 'either LABEL or FALLBACK.
\n', + 'type': 'Constant', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Environment' + }, + 'describeElement': { + 'name': 'describeElement', + 'params': [ + { + 'name': 'name', + 'description': 'name of the element.
\n', + 'type': 'String' + }, + { + 'name': 'text', + 'description': 'description of the element.
\n', + 'type': 'String' + }, + { + 'name': 'display', + 'description': 'either LABEL or FALLBACK.
\n', + 'type': 'Constant', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Environment' + }, + 'textOutput': { + 'name': 'textOutput', + 'params': [ + { + 'name': 'display', + 'description': 'either FALLBACK or LABEL.
\n', + 'type': 'Constant', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Environment' + }, + 'gridOutput': { + 'name': 'gridOutput', + 'params': [ + { + 'name': 'display', + 'description': 'either FALLBACK or LABEL.
\n', + 'type': 'Constant', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Environment' + }, + 'alpha': { + 'name': 'alpha', + 'params': [ + { + 'name': 'color', + 'description': 'p5.Color object, array of\n color components, or CSS color string.
\n', + 'type': 'p5.Color|Number[]|String' + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'blue': { + 'name': 'blue', + 'params': [ + { + 'name': 'color', + 'description': 'p5.Color object, array of\n color components, or CSS color string.
\n', + 'type': 'p5.Color|Number[]|String' + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'brightness': { + 'name': 'brightness', + 'params': [ + { + 'name': 'color', + 'description': 'p5.Color object, array of\n color components, or CSS color string.
\n', + 'type': 'p5.Color|Number[]|String' + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'color': { + 'name': 'color', + 'class': 'p5', + 'module': 'Color', + 'overloads': [ + { + 'params': [ + { + 'name': 'gray', + 'description': 'number specifying value between white and black.
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': 'alpha value relative to current color range\n (default is 0-255).
\n', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'v1', + 'description': 'red or hue value relative to\n the current color range.
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green or saturation value\n relative to the current color range.
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue or brightness value\n relative to the current color range.
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': '', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'value', + 'description': 'a color string.
\n', + 'type': 'String' + } + ] + }, + { + 'params': [ + { + 'name': 'values', + 'description': 'an array containing the red, green, blue,\n and alpha components of the color.
\n', + 'type': 'Number[]' + } + ] + }, + { + 'params': [ + { + 'name': 'color', + 'description': '', + 'type': 'p5.Color' + } + ] + } + ] + }, + 'green': { + 'name': 'green', + 'params': [ + { + 'name': 'color', + 'description': 'p5.Color object, array of\n color components, or CSS color string.
\n', + 'type': 'p5.Color|Number[]|String' + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'hue': { + 'name': 'hue', + 'params': [ + { + 'name': 'color', + 'description': 'p5.Color object, array of\n color components, or CSS color string.
\n', + 'type': 'p5.Color|Number[]|String' + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'lerpColor': { + 'name': 'lerpColor', + 'params': [ + { + 'name': 'c1', + 'description': 'interpolate from this color.
\n', + 'type': 'p5.Color' + }, + { + 'name': 'c2', + 'description': 'interpolate to this color.
\n', + 'type': 'p5.Color' + }, + { + 'name': 'amt', + 'description': 'number between 0 and 1.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'lightness': { + 'name': 'lightness', + 'params': [ + { + 'name': 'color', + 'description': 'p5.Color object, array of\n color components, or CSS color string.
\n', + 'type': 'p5.Color|Number[]|String' + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'red': { + 'name': 'red', + 'params': [ + { + 'name': 'color', + 'description': 'p5.Color object, array of\n color components, or CSS color string.
\n', + 'type': 'p5.Color|Number[]|String' + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'saturation': { + 'name': 'saturation', + 'params': [ + { + 'name': 'color', + 'description': 'p5.Color object, array of\n color components, or CSS color string.
\n', + 'type': 'p5.Color|Number[]|String' + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'beginClip': { + 'name': 'beginClip', + 'params': [ + { + 'name': 'options', + 'description': 'an object containing clip settings.
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'endClip': { + 'name': 'endClip', + 'class': 'p5', + 'module': 'Color' + }, + 'clip': { + 'name': 'clip', + 'params': [ + { + 'name': 'callback', + 'description': 'a function that draws the mask shape.
\n', + 'type': 'Function' + }, + { + 'name': 'options', + 'description': 'an object containing clip settings.
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'background': { + 'name': 'background', + 'class': 'p5', + 'module': 'Color', + 'overloads': [ + { + 'params': [ + { + 'name': 'color', + 'description': 'any value created by the color() function
\n', + 'type': 'p5.Color' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'colorstring', + 'description': 'color string, possible formats include: integer\n rgb() or rgba(), percentage rgb() or rgba(),\n 3-digit hex, 6-digit hex.
\n', + 'type': 'String' + }, + { + 'name': 'a', + 'description': 'opacity of the background relative to current\n color range (default is 0-255).
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'gray', + 'description': 'specifies a value between white and black.
\n', + 'type': 'Number' + }, + { + 'name': 'a', + 'description': '', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v1', + 'description': 'red value if color mode is RGB, or hue value if color mode is HSB.
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green value if color mode is RGB, or saturation value if color mode is HSB.
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue value if color mode is RGB, or brightness value if color mode is HSB.
\n', + 'type': 'Number' + }, + { + 'name': 'a', + 'description': '', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'values', + 'description': 'an array containing the red, green, blue\n and alpha components of the color.
\n', + 'type': 'Number[]' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'image', + 'description': 'image created with loadImage()\n or createImage(),\n to set as background.\n (must be same size as the sketch window).
\n', + 'type': 'p5.Image' + }, + { + 'name': 'a', + 'description': '', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + } + ] + }, + 'clear': { + 'name': 'clear', + 'params': [ + { + 'name': 'r', + 'description': 'normalized red value.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'g', + 'description': 'normalized green value.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'b', + 'description': 'normalized blue value.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'a', + 'description': 'normalized alpha value.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'colorMode': { + 'name': 'colorMode', + 'class': 'p5', + 'module': 'Color', + 'overloads': [ + { + 'params': [ + { + 'name': 'mode', + 'description': 'either RGB, HSB or HSL, corresponding to\n Red/Green/Blue and Hue/Saturation/Brightness\n (or Lightness).
\n', + 'type': 'Constant' + }, + { + 'name': 'max', + 'description': 'range for all values.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'mode', + 'description': '', + 'type': 'Constant' + }, + { + 'name': 'max1', + 'description': 'range for the red or hue depending on the\n current color mode.
\n', + 'type': 'Number' + }, + { + 'name': 'max2', + 'description': 'range for the green or saturation depending\n on the current color mode.
\n', + 'type': 'Number' + }, + { + 'name': 'max3', + 'description': 'range for the blue or brightness/lightness\n depending on the current color mode.
\n', + 'type': 'Number' + }, + { + 'name': 'maxA', + 'description': 'range for the alpha.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + } + ] + }, + 'fill': { + 'name': 'fill', + 'class': 'p5', + 'module': 'Color', + 'overloads': [ + { + 'params': [ + { + 'name': 'v1', + 'description': 'red value if color mode is RGB or hue value if color mode is HSB.
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green value if color mode is RGB or saturation value if color mode is HSB.
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue value if color mode is RGB or brightness value if color mode is HSB.
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': '', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'value', + 'description': 'a color string.
\n', + 'type': 'String' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'gray', + 'description': 'a grayscale value.
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': '', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'values', + 'description': 'an array containing the red, green, blue &\n and alpha components of the color.
\n', + 'type': 'Number[]' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'color', + 'description': 'the fill color.
\n', + 'type': 'p5.Color' + } + ], + 'chainable': 1 + } + ] + }, + 'noFill': { + 'name': 'noFill', + 'class': 'p5', + 'module': 'Color' + }, + 'noStroke': { + 'name': 'noStroke', + 'class': 'p5', + 'module': 'Color' + }, + 'stroke': { + 'name': 'stroke', + 'class': 'p5', + 'module': 'Color', + 'overloads': [ + { + 'params': [ + { + 'name': 'v1', + 'description': 'red value if color mode is RGB or hue value if color mode is HSB.
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green value if color mode is RGB or saturation value if color mode is HSB.
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue value if color mode is RGB or brightness value if color mode is HSB.
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': '', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'value', + 'description': 'a color string.
\n', + 'type': 'String' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'gray', + 'description': 'a grayscale value.
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': '', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'values', + 'description': 'an array containing the red, green, blue,\n and alpha components of the color.
\n', + 'type': 'Number[]' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'color', + 'description': 'the stroke color.
\n', + 'type': 'p5.Color' + } + ], + 'chainable': 1 + } + ] + }, + 'erase': { + 'name': 'erase', + 'params': [ + { + 'name': 'strengthFill', + 'description': 'a number (0-255) for the strength of erasing under a shape\'s interior.\n Defaults to 255, which is full strength.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'strengthStroke', + 'description': 'a number (0-255) for the strength of erasing under a shape\'s edge.\n Defaults to 255, which is full strength.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Color' + }, + 'noErase': { + 'name': 'noErase', + 'class': 'p5', + 'module': 'Color' + }, + 'arc': { + 'name': 'arc', + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the arc\'s ellipse.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the arc\'s ellipse.
\n', + 'type': 'Number' + }, + { + 'name': 'w', + 'description': 'width of the arc\'s ellipse by default.
\n', + 'type': 'Number' + }, + { + 'name': 'h', + 'description': 'height of the arc\'s ellipse by default.
\n', + 'type': 'Number' + }, + { + 'name': 'start', + 'description': 'angle to start the arc, specified in radians.
\n', + 'type': 'Number' + }, + { + 'name': 'stop', + 'description': 'angle to stop the arc, specified in radians.
\n', + 'type': 'Number' + }, + { + 'name': 'mode', + 'description': 'optional parameter to determine the way of drawing\n the arc. either CHORD, PIE, or OPEN.
\n', + 'type': 'Constant', + 'optional': true + }, + { + 'name': 'detail', + 'description': 'optional parameter for WebGL mode only. This is to\n specify the number of vertices that makes up the\n perimeter of the arc. Default value is 25. Won\'t\n draw a stroke for a detail of more than 50.
\n', + 'type': 'Integer', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'ellipse': { + 'name': 'ellipse', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the center of the ellipse.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the center of the ellipse.
\n', + 'type': 'Number' + }, + { + 'name': 'w', + 'description': 'width of the ellipse.
\n', + 'type': 'Number' + }, + { + 'name': 'h', + 'description': 'height of the ellipse.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'w', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'h', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'detail', + 'description': 'optional parameter for WebGL mode only. This is to\n specify the number of vertices that makes up the\n perimeter of the ellipse. Default value is 25. Won\'t\n draw a stroke for a detail of more than 50.
\n', + 'type': 'Integer', + 'optional': true + } + ] + } + ] + }, + 'circle': { + 'name': 'circle', + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the center of the circle.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the center of the circle.
\n', + 'type': 'Number' + }, + { + 'name': 'd', + 'description': 'diameter of the circle.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'line': { + 'name': 'line', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'x1', + 'description': 'the x-coordinate of the first point.
\n', + 'type': 'Number' + }, + { + 'name': 'y1', + 'description': 'the y-coordinate of the first point.
\n', + 'type': 'Number' + }, + { + 'name': 'x2', + 'description': 'the x-coordinate of the second point.
\n', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': 'the y-coordinate of the second point.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z1', + 'description': 'the z-coordinate of the first point.
\n', + 'type': 'Number' + }, + { + 'name': 'x2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z2', + 'description': 'the z-coordinate of the second point.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + } + ] + }, + 'point': { + 'name': 'point', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'the x-coordinate.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'the y-coordinate.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'the z-coordinate (for WebGL mode).
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'coordinateVector', + 'description': 'the coordinate vector.
\n', + 'type': 'p5.Vector' + } + ], + 'chainable': 1 + } + ] + }, + 'quad': { + 'name': 'quad', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'x1', + 'description': 'the x-coordinate of the first point.
\n', + 'type': 'Number' + }, + { + 'name': 'y1', + 'description': 'the y-coordinate of the first point.
\n', + 'type': 'Number' + }, + { + 'name': 'x2', + 'description': 'the x-coordinate of the second point.
\n', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': 'the y-coordinate of the second point.
\n', + 'type': 'Number' + }, + { + 'name': 'x3', + 'description': 'the x-coordinate of the third point.
\n', + 'type': 'Number' + }, + { + 'name': 'y3', + 'description': 'the y-coordinate of the third point.
\n', + 'type': 'Number' + }, + { + 'name': 'x4', + 'description': 'the x-coordinate of the fourth point.
\n', + 'type': 'Number' + }, + { + 'name': 'y4', + 'description': 'the y-coordinate of the fourth point.
\n', + 'type': 'Number' + }, + { + 'name': 'detailX', + 'description': 'number of segments in the x-direction.
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'detailY', + 'description': 'number of segments in the y-direction.
\n', + 'type': 'Integer', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z1', + 'description': 'the z-coordinate of the first point.
\n', + 'type': 'Number' + }, + { + 'name': 'x2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z2', + 'description': 'the z-coordinate of the second point.
\n', + 'type': 'Number' + }, + { + 'name': 'x3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z3', + 'description': 'the z-coordinate of the third point.
\n', + 'type': 'Number' + }, + { + 'name': 'x4', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y4', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z4', + 'description': 'the z-coordinate of the fourth point.
\n', + 'type': 'Number' + }, + { + 'name': 'detailX', + 'description': '', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'detailY', + 'description': '', + 'type': 'Integer', + 'optional': true + } + ], + 'chainable': 1 + } + ] + }, + 'rect': { + 'name': 'rect', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the rectangle.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the rectangle.
\n', + 'type': 'Number' + }, + { + 'name': 'w', + 'description': 'width of the rectangle.
\n', + 'type': 'Number' + }, + { + 'name': 'h', + 'description': 'height of the rectangle.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'tl', + 'description': 'optional radius of top-left corner.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'tr', + 'description': 'optional radius of top-right corner.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'br', + 'description': 'optional radius of bottom-right corner.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'bl', + 'description': 'optional radius of bottom-left corner.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'w', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'h', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'detailX', + 'description': 'number of segments in the x-direction (for WebGL mode).
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'detailY', + 'description': 'number of segments in the y-direction (for WebGL mode).
\n', + 'type': 'Integer', + 'optional': true + } + ], + 'chainable': 1 + } + ] + }, + 'square': { + 'name': 'square', + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the square.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the square.
\n', + 'type': 'Number' + }, + { + 'name': 's', + 'description': 'side size of the square.
\n', + 'type': 'Number' + }, + { + 'name': 'tl', + 'description': 'optional radius of top-left corner.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'tr', + 'description': 'optional radius of top-right corner.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'br', + 'description': 'optional radius of bottom-right corner.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'bl', + 'description': 'optional radius of bottom-left corner.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'triangle': { + 'name': 'triangle', + 'params': [ + { + 'name': 'x1', + 'description': 'x-coordinate of the first point.
\n', + 'type': 'Number' + }, + { + 'name': 'y1', + 'description': 'y-coordinate of the first point.
\n', + 'type': 'Number' + }, + { + 'name': 'x2', + 'description': 'x-coordinate of the second point.
\n', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': 'y-coordinate of the second point.
\n', + 'type': 'Number' + }, + { + 'name': 'x3', + 'description': 'x-coordinate of the third point.
\n', + 'type': 'Number' + }, + { + 'name': 'y3', + 'description': 'y-coordinate of the third point.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'ellipseMode': { + 'name': 'ellipseMode', + 'params': [ + { + 'name': 'mode', + 'description': 'either CENTER, RADIUS, CORNER, or CORNERS
\n', + 'type': 'Constant' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'noSmooth': { + 'name': 'noSmooth', + 'class': 'p5', + 'module': 'Shape' + }, + 'rectMode': { + 'name': 'rectMode', + 'params': [ + { + 'name': 'mode', + 'description': 'either CORNER, CORNERS, CENTER, or RADIUS
\n', + 'type': 'Constant' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'smooth': { + 'name': 'smooth', + 'class': 'p5', + 'module': 'Shape' + }, + 'strokeCap': { + 'name': 'strokeCap', + 'params': [ + { + 'name': 'cap', + 'description': 'either ROUND, SQUARE, or PROJECT
\n', + 'type': 'Constant' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'strokeJoin': { + 'name': 'strokeJoin', + 'params': [ + { + 'name': 'join', + 'description': 'either MITER, BEVEL, or ROUND
\n', + 'type': 'Constant' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'strokeWeight': { + 'name': 'strokeWeight', + 'params': [ + { + 'name': 'weight', + 'description': 'the weight of the stroke (in pixels).
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'bezier': { + 'name': 'bezier', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'x1', + 'description': 'x-coordinate of the first anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'y1', + 'description': 'y-coordinate of the first anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'x2', + 'description': 'x-coordinate of the first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': 'y-coordinate of the first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'x3', + 'description': 'x-coordinate of the second control point.
\n', + 'type': 'Number' + }, + { + 'name': 'y3', + 'description': 'y-coordinate of the second control point.
\n', + 'type': 'Number' + }, + { + 'name': 'x4', + 'description': 'x-coordinate of the second anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'y4', + 'description': 'y-coordinate of the second anchor point.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z1', + 'description': 'z-coordinate of the first anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'x2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z2', + 'description': 'z-coordinate of the first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'x3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z3', + 'description': 'z-coordinate of the second control point.
\n', + 'type': 'Number' + }, + { + 'name': 'x4', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y4', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z4', + 'description': 'z-coordinate of the second anchor point.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + } + ] + }, + 'bezierDetail': { + 'name': 'bezierDetail', + 'params': [ + { + 'name': 'detail', + 'description': 'number of segments to use. Defaults to 20.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'bezierPoint': { + 'name': 'bezierPoint', + 'params': [ + { + 'name': 'a', + 'description': 'coordinate of first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'b', + 'description': 'coordinate of first anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'c', + 'description': 'coordinate of second anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'd', + 'description': 'coordinate of second control point.
\n', + 'type': 'Number' + }, + { + 'name': 't', + 'description': 'amount to interpolate between 0 and 1.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'bezierTangent': { + 'name': 'bezierTangent', + 'params': [ + { + 'name': 'a', + 'description': 'coordinate of first anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'b', + 'description': 'coordinate of first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'c', + 'description': 'coordinate of second control point.
\n', + 'type': 'Number' + }, + { + 'name': 'd', + 'description': 'coordinate of second anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 't', + 'description': 'amount to interpolate between 0 and 1.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'curve': { + 'name': 'curve', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'x1', + 'description': 'x-coordinate of the first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'y1', + 'description': 'y-coordinate of the first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'x2', + 'description': 'x-coordinate of the first anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': 'y-coordinate of the first anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'x3', + 'description': 'x-coordinate of the second anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'y3', + 'description': 'y-coordinate of the second anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'x4', + 'description': 'x-coordinate of the second control point.
\n', + 'type': 'Number' + }, + { + 'name': 'y4', + 'description': 'y-coordinate of the second control point.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z1', + 'description': 'z-coordinate of the first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'x2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z2', + 'description': 'z-coordinate of the first anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'x3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z3', + 'description': 'z-coordinate of the second anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'x4', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y4', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z4', + 'description': 'z-coordinate of the second control point.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + } + ] + }, + 'curveDetail': { + 'name': 'curveDetail', + 'params': [ + { + 'name': 'resolution', + 'description': 'number of segments to use. Defaults to 20.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'curveTightness': { + 'name': 'curveTightness', + 'params': [ + { + 'name': 'amount', + 'description': 'amount of tightness.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'curvePoint': { + 'name': 'curvePoint', + 'params': [ + { + 'name': 'a', + 'description': 'coordinate of first anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'b', + 'description': 'coordinate of first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'c', + 'description': 'coordinate of second control point.
\n', + 'type': 'Number' + }, + { + 'name': 'd', + 'description': 'coordinate of second anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 't', + 'description': 'amount to interpolate between 0 and 1.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'curveTangent': { + 'name': 'curveTangent', + 'params': [ + { + 'name': 'a', + 'description': 'coordinate of first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'b', + 'description': 'coordinate of first anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'c', + 'description': 'coordinate of second anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'd', + 'description': 'coordinate of second control point.
\n', + 'type': 'Number' + }, + { + 'name': 't', + 'description': 'amount to interpolate between 0 and 1.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'beginContour': { + 'name': 'beginContour', + 'class': 'p5', + 'module': 'Shape' + }, + 'beginShape': { + 'name': 'beginShape', + 'params': [ + { + 'name': 'kind', + 'description': 'either POINTS, LINES, TRIANGLES, TRIANGLE_FAN\n TRIANGLE_STRIP, QUADS, QUAD_STRIP or TESS.
\n', + 'type': 'Constant', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'bezierVertex': { + 'name': 'bezierVertex', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'x2', + 'description': 'x-coordinate of the first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': 'y-coordinate of the first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'x3', + 'description': 'x-coordinate of the second control point.
\n', + 'type': 'Number' + }, + { + 'name': 'y3', + 'description': 'y-coordinate of the second control point.
\n', + 'type': 'Number' + }, + { + 'name': 'x4', + 'description': 'x-coordinate of the anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'y4', + 'description': 'y-coordinate of the anchor point.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z2', + 'description': 'z-coordinate of the first control point.
\n', + 'type': 'Number' + }, + { + 'name': 'x3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z3', + 'description': 'z-coordinate of the second control point.
\n', + 'type': 'Number' + }, + { + 'name': 'x4', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y4', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z4', + 'description': 'z-coordinate of the anchor point.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + } + ] + }, + 'curveVertex': { + 'name': 'curveVertex', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the vertex
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the vertex
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'z-coordinate of the vertex.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + } + ] + }, + 'endContour': { + 'name': 'endContour', + 'class': 'p5', + 'module': 'Shape' + }, + 'endShape': { + 'name': 'endShape', + 'params': [ + { + 'name': 'mode', + 'description': 'use CLOSE to close the shape
\n', + 'type': 'Constant', + 'optional': true + }, + { + 'name': 'count', + 'description': 'number of times you want to draw/instance the shape (for WebGL mode).
\n', + 'type': 'Integer', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'quadraticVertex': { + 'name': 'quadraticVertex', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'cx', + 'description': 'x-coordinate of the control point.
\n', + 'type': 'Number' + }, + { + 'name': 'cy', + 'description': 'y-coordinate of the control point.
\n', + 'type': 'Number' + }, + { + 'name': 'x3', + 'description': 'x-coordinate of the anchor point.
\n', + 'type': 'Number' + }, + { + 'name': 'y3', + 'description': 'y-coordinate of the anchor point.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'cx', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'cy', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'cz', + 'description': 'z-coordinate of the control point.
\n', + 'type': 'Number' + }, + { + 'name': 'x3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z3', + 'description': 'z-coordinate of the anchor point.
\n', + 'type': 'Number' + } + ] + } + ] + }, + 'vertex': { + 'name': 'vertex', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the vertex.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the vertex.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'z-coordinate of the vertex. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'u', + 'description': 'u-coordinate of the vertex\'s texture. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'v', + 'description': 'v-coordinate of the vertex\'s texture. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + } + ] + }, + 'normal': { + 'name': 'normal', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'vector', + 'description': 'vertex normal as a p5.Vector object.
\n', + 'type': 'p5.Vector' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x', + 'description': 'x-component of the vertex normal.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-component of the vertex normal.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'z-component of the vertex normal.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + } + ] + }, + 'VERSION': { + 'name': 'VERSION', + 'class': 'p5', + 'module': 'Constants' + }, + 'P2D': { + 'name': 'P2D', + 'class': 'p5', + 'module': 'Constants' + }, + 'WEBGL': { + 'name': 'WEBGL', + 'class': 'p5', + 'module': 'Constants' + }, + 'WEBGL2': { + 'name': 'WEBGL2', + 'class': 'p5', + 'module': 'Constants' + }, + 'ARROW': { + 'name': 'ARROW', + 'class': 'p5', + 'module': 'Constants' + }, + 'CROSS': { + 'name': 'CROSS', + 'class': 'p5', + 'module': 'Constants' + }, + 'HAND': { + 'name': 'HAND', + 'class': 'p5', + 'module': 'Constants' + }, + 'MOVE': { + 'name': 'MOVE', + 'class': 'p5', + 'module': 'Constants' + }, + 'TEXT': { + 'name': 'TEXT', + 'class': 'p5', + 'module': 'Constants' + }, + 'WAIT': { + 'name': 'WAIT', + 'class': 'p5', + 'module': 'Constants' + }, + 'HALF_PI': { + 'name': 'HALF_PI', + 'class': 'p5', + 'module': 'Constants' + }, + 'PI': { + 'name': 'PI', + 'class': 'p5', + 'module': 'Constants' + }, + 'QUARTER_PI': { + 'name': 'QUARTER_PI', + 'class': 'p5', + 'module': 'Constants' + }, + 'TAU': { + 'name': 'TAU', + 'class': 'p5', + 'module': 'Constants' + }, + 'TWO_PI': { + 'name': 'TWO_PI', + 'class': 'p5', + 'module': 'Constants' + }, + 'DEGREES': { + 'name': 'DEGREES', + 'class': 'p5', + 'module': 'Constants' + }, + 'RADIANS': { + 'name': 'RADIANS', + 'class': 'p5', + 'module': 'Constants' + }, + 'CORNER': { + 'name': 'CORNER', + 'class': 'p5', + 'module': 'Constants' + }, + 'CORNERS': { + 'name': 'CORNERS', + 'class': 'p5', + 'module': 'Constants' + }, + 'RADIUS': { + 'name': 'RADIUS', + 'class': 'p5', + 'module': 'Constants' + }, + 'RIGHT': { + 'name': 'RIGHT', + 'class': 'p5', + 'module': 'Constants' + }, + 'LEFT': { + 'name': 'LEFT', + 'class': 'p5', + 'module': 'Constants' + }, + 'CENTER': { + 'name': 'CENTER', + 'class': 'p5', + 'module': 'Constants' + }, + 'TOP': { + 'name': 'TOP', + 'class': 'p5', + 'module': 'Constants' + }, + 'BOTTOM': { + 'name': 'BOTTOM', + 'class': 'p5', + 'module': 'Constants' + }, + 'BASELINE': { + 'name': 'BASELINE', + 'class': 'p5', + 'module': 'Constants' + }, + 'POINTS': { + 'name': 'POINTS', + 'class': 'p5', + 'module': 'Constants' + }, + 'LINES': { + 'name': 'LINES', + 'class': 'p5', + 'module': 'Constants' + }, + 'LINE_STRIP': { + 'name': 'LINE_STRIP', + 'class': 'p5', + 'module': 'Constants' + }, + 'LINE_LOOP': { + 'name': 'LINE_LOOP', + 'class': 'p5', + 'module': 'Constants' + }, + 'TRIANGLES': { + 'name': 'TRIANGLES', + 'class': 'p5', + 'module': 'Constants' + }, + 'TRIANGLE_FAN': { + 'name': 'TRIANGLE_FAN', + 'class': 'p5', + 'module': 'Constants' + }, + 'TRIANGLE_STRIP': { + 'name': 'TRIANGLE_STRIP', + 'class': 'p5', + 'module': 'Constants' + }, + 'QUADS': { + 'name': 'QUADS', + 'class': 'p5', + 'module': 'Constants' + }, + 'QUAD_STRIP': { + 'name': 'QUAD_STRIP', + 'class': 'p5', + 'module': 'Constants' + }, + 'TESS': { + 'name': 'TESS', + 'class': 'p5', + 'module': 'Constants' + }, + 'CLOSE': { + 'name': 'CLOSE', + 'class': 'p5', + 'module': 'Constants' + }, + 'OPEN': { + 'name': 'OPEN', + 'class': 'p5', + 'module': 'Constants' + }, + 'CHORD': { + 'name': 'CHORD', + 'class': 'p5', + 'module': 'Constants' + }, + 'PIE': { + 'name': 'PIE', + 'class': 'p5', + 'module': 'Constants' + }, + 'PROJECT': { + 'name': 'PROJECT', + 'class': 'p5', + 'module': 'Constants' + }, + 'SQUARE': { + 'name': 'SQUARE', + 'class': 'p5', + 'module': 'Constants' + }, + 'ROUND': { + 'name': 'ROUND', + 'class': 'p5', + 'module': 'Constants' + }, + 'BEVEL': { + 'name': 'BEVEL', + 'class': 'p5', + 'module': 'Constants' + }, + 'MITER': { + 'name': 'MITER', + 'class': 'p5', + 'module': 'Constants' + }, + 'RGB': { + 'name': 'RGB', + 'class': 'p5', + 'module': 'Constants' + }, + 'HSB': { + 'name': 'HSB', + 'class': 'p5', + 'module': 'Constants' + }, + 'HSL': { + 'name': 'HSL', + 'class': 'p5', + 'module': 'Constants' + }, + 'AUTO': { + 'name': 'AUTO', + 'class': 'p5', + 'module': 'Constants' + }, + 'ALT': { + 'name': 'ALT', + 'class': 'p5', + 'module': 'Constants' + }, + 'BACKSPACE': { + 'name': 'BACKSPACE', + 'class': 'p5', + 'module': 'Constants' + }, + 'CONTROL': { + 'name': 'CONTROL', + 'class': 'p5', + 'module': 'Constants' + }, + 'DELETE': { + 'name': 'DELETE', + 'class': 'p5', + 'module': 'Constants' + }, + 'DOWN_ARROW': { + 'name': 'DOWN_ARROW', + 'class': 'p5', + 'module': 'Constants' + }, + 'ENTER': { + 'name': 'ENTER', + 'class': 'p5', + 'module': 'Constants' + }, + 'ESCAPE': { + 'name': 'ESCAPE', + 'class': 'p5', + 'module': 'Constants' + }, + 'LEFT_ARROW': { + 'name': 'LEFT_ARROW', + 'class': 'p5', + 'module': 'Constants' + }, + 'OPTION': { + 'name': 'OPTION', + 'class': 'p5', + 'module': 'Constants' + }, + 'RETURN': { + 'name': 'RETURN', + 'class': 'p5', + 'module': 'Constants' + }, + 'RIGHT_ARROW': { + 'name': 'RIGHT_ARROW', + 'class': 'p5', + 'module': 'Constants' + }, + 'SHIFT': { + 'name': 'SHIFT', + 'class': 'p5', + 'module': 'Constants' + }, + 'TAB': { + 'name': 'TAB', + 'class': 'p5', + 'module': 'Constants' + }, + 'UP_ARROW': { + 'name': 'UP_ARROW', + 'class': 'p5', + 'module': 'Constants' + }, + 'BLEND': { + 'name': 'BLEND', + 'class': 'p5', + 'module': 'Constants' + }, + 'REMOVE': { + 'name': 'REMOVE', + 'class': 'p5', + 'module': 'Constants' + }, + 'ADD': { + 'name': 'ADD', + 'class': 'p5', + 'module': 'Constants' + }, + 'DARKEST': { + 'name': 'DARKEST', + 'class': 'p5', + 'module': 'Constants' + }, + 'LIGHTEST': { + 'name': 'LIGHTEST', + 'class': 'p5', + 'module': 'Constants' + }, + 'DIFFERENCE': { + 'name': 'DIFFERENCE', + 'class': 'p5', + 'module': 'Constants' + }, + 'SUBTRACT': { + 'name': 'SUBTRACT', + 'class': 'p5', + 'module': 'Constants' + }, + 'EXCLUSION': { + 'name': 'EXCLUSION', + 'class': 'p5', + 'module': 'Constants' + }, + 'MULTIPLY': { + 'name': 'MULTIPLY', + 'class': 'p5', + 'module': 'Constants' + }, + 'SCREEN': { + 'name': 'SCREEN', + 'class': 'p5', + 'module': 'Constants' + }, + 'REPLACE': { + 'name': 'REPLACE', + 'class': 'p5', + 'module': 'Constants' + }, + 'OVERLAY': { + 'name': 'OVERLAY', + 'class': 'p5', + 'module': 'Constants' + }, + 'HARD_LIGHT': { + 'name': 'HARD_LIGHT', + 'class': 'p5', + 'module': 'Constants' + }, + 'SOFT_LIGHT': { + 'name': 'SOFT_LIGHT', + 'class': 'p5', + 'module': 'Constants' + }, + 'DODGE': { + 'name': 'DODGE', + 'class': 'p5', + 'module': 'Constants' + }, + 'BURN': { + 'name': 'BURN', + 'class': 'p5', + 'module': 'Constants' + }, + 'THRESHOLD': { + 'name': 'THRESHOLD', + 'class': 'p5', + 'module': 'Constants' + }, + 'GRAY': { + 'name': 'GRAY', + 'class': 'p5', + 'module': 'Constants' + }, + 'OPAQUE': { + 'name': 'OPAQUE', + 'class': 'p5', + 'module': 'Constants' + }, + 'INVERT': { + 'name': 'INVERT', + 'class': 'p5', + 'module': 'Constants' + }, + 'POSTERIZE': { + 'name': 'POSTERIZE', + 'class': 'p5', + 'module': 'Constants' + }, + 'DILATE': { + 'name': 'DILATE', + 'class': 'p5', + 'module': 'Constants' + }, + 'ERODE': { + 'name': 'ERODE', + 'class': 'p5', + 'module': 'Constants' + }, + 'BLUR': { + 'name': 'BLUR', + 'class': 'p5', + 'module': 'Constants' + }, + 'NORMAL': { + 'name': 'NORMAL', + 'class': 'p5', + 'module': 'Constants' + }, + 'ITALIC': { + 'name': 'ITALIC', + 'class': 'p5', + 'module': 'Constants' + }, + 'BOLD': { + 'name': 'BOLD', + 'class': 'p5', + 'module': 'Constants' + }, + 'BOLDITALIC': { + 'name': 'BOLDITALIC', + 'class': 'p5', + 'module': 'Constants' + }, + 'CHAR': { + 'name': 'CHAR', + 'class': 'p5', + 'module': 'Constants' + }, + 'WORD': { + 'name': 'WORD', + 'class': 'p5', + 'module': 'Constants' + }, + 'LINEAR': { + 'name': 'LINEAR', + 'class': 'p5', + 'module': 'Constants' + }, + 'QUADRATIC': { + 'name': 'QUADRATIC', + 'class': 'p5', + 'module': 'Constants' + }, + 'BEZIER': { + 'name': 'BEZIER', + 'class': 'p5', + 'module': 'Constants' + }, + 'CURVE': { + 'name': 'CURVE', + 'class': 'p5', + 'module': 'Constants' + }, + 'STROKE': { + 'name': 'STROKE', + 'class': 'p5', + 'module': 'Constants' + }, + 'FILL': { + 'name': 'FILL', + 'class': 'p5', + 'module': 'Constants' + }, + 'TEXTURE': { + 'name': 'TEXTURE', + 'class': 'p5', + 'module': 'Constants' + }, + 'IMMEDIATE': { + 'name': 'IMMEDIATE', + 'class': 'p5', + 'module': 'Constants' + }, + 'IMAGE': { + 'name': 'IMAGE', + 'class': 'p5', + 'module': 'Constants' + }, + 'NEAREST': { + 'name': 'NEAREST', + 'class': 'p5', + 'module': 'Constants' + }, + 'REPEAT': { + 'name': 'REPEAT', + 'class': 'p5', + 'module': 'Constants' + }, + 'CLAMP': { + 'name': 'CLAMP', + 'class': 'p5', + 'module': 'Constants' + }, + 'MIRROR': { + 'name': 'MIRROR', + 'class': 'p5', + 'module': 'Constants' + }, + 'FLAT': { + 'name': 'FLAT', + 'class': 'p5', + 'module': 'Constants' + }, + 'SMOOTH': { + 'name': 'SMOOTH', + 'class': 'p5', + 'module': 'Constants' + }, + 'LANDSCAPE': { + 'name': 'LANDSCAPE', + 'class': 'p5', + 'module': 'Constants' + }, + 'PORTRAIT': { + 'name': 'PORTRAIT', + 'class': 'p5', + 'module': 'Constants' + }, + 'GRID': { + 'name': 'GRID', + 'class': 'p5', + 'module': 'Constants' + }, + 'AXES': { + 'name': 'AXES', + 'class': 'p5', + 'module': 'Constants' + }, + 'LABEL': { + 'name': 'LABEL', + 'class': 'p5', + 'module': 'Constants' + }, + 'FALLBACK': { + 'name': 'FALLBACK', + 'class': 'p5', + 'module': 'Constants' + }, + 'CONTAIN': { + 'name': 'CONTAIN', + 'class': 'p5', + 'module': 'Constants' + }, + 'COVER': { + 'name': 'COVER', + 'class': 'p5', + 'module': 'Constants' + }, + 'UNSIGNED_BYTE': { + 'name': 'UNSIGNED_BYTE', + 'class': 'p5', + 'module': 'Constants' + }, + 'UNSIGNED_INT': { + 'name': 'UNSIGNED_INT', + 'class': 'p5', + 'module': 'Constants' + }, + 'FLOAT': { + 'name': 'FLOAT', + 'class': 'p5', + 'module': 'Constants' + }, + 'HALF_FLOAT': { + 'name': 'HALF_FLOAT', + 'class': 'p5', + 'module': 'Constants' + }, + 'RGBA': { + 'name': 'RGBA', + 'class': 'p5', + 'module': 'Constants' + }, + 'print': { + 'name': 'print', + 'params': [ + { + 'name': 'contents', + 'description': 'content to print to the console.
\n', + 'type': 'Any' + } + ], + 'class': 'p5', + 'module': 'Environment' + }, + 'frameCount': { + 'name': 'frameCount', + 'class': 'p5', + 'module': 'Environment' + }, + 'deltaTime': { + 'name': 'deltaTime', + 'class': 'p5', + 'module': 'Environment' + }, + 'focused': { + 'name': 'focused', + 'class': 'p5', + 'module': 'Environment' + }, + 'cursor': { + 'name': 'cursor', + 'params': [ + { + 'name': 'type', + 'description': 'Built-in: either ARROW, CROSS, HAND, MOVE, TEXT, or WAIT.\n Native CSS properties: \'grab\', \'progress\', and so on.\n Path to cursor image.
\n', + 'type': 'String|Constant' + }, + { + 'name': 'x', + 'description': 'horizontal active spot of the cursor.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'y', + 'description': 'vertical active spot of the cursor.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Environment' + }, + 'frameRate': { + 'name': 'frameRate', + 'class': 'p5', + 'module': 'Environment', + 'overloads': [ + { + 'params': [ + { + 'name': 'fps', + 'description': 'number of frames to draw per second.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + ] + } + ] + }, + 'getTargetFrameRate': { + 'name': 'getTargetFrameRate', + 'class': 'p5', + 'module': 'Environment' + }, + 'noCursor': { + 'name': 'noCursor', + 'class': 'p5', + 'module': 'Environment' + }, + 'webglVersion': { + 'name': 'webglVersion', + 'class': 'p5', + 'module': 'Environment' + }, + 'displayWidth': { + 'name': 'displayWidth', + 'class': 'p5', + 'module': 'Environment' + }, + 'displayHeight': { + 'name': 'displayHeight', + 'class': 'p5', + 'module': 'Environment' + }, + 'windowWidth': { + 'name': 'windowWidth', + 'class': 'p5', + 'module': 'Environment' + }, + 'windowHeight': { + 'name': 'windowHeight', + 'class': 'p5', + 'module': 'Environment' + }, + 'windowResized': { + 'name': 'windowResized', + 'params': [ + { + 'name': 'event', + 'description': 'optional resize Event.
\n', + 'type': 'UIEvent', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Environment' + }, + 'width': { + 'name': 'width', + 'class': 'p5', + 'module': 'Environment' + }, + 'height': { + 'name': 'height', + 'class': 'p5', + 'module': 'Environment' + }, + 'fullscreen': { + 'name': 'fullscreen', + 'params': [ + { + 'name': 'val', + 'description': 'whether the sketch should be in fullscreen mode.
\n', + 'type': 'Boolean', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Environment' + }, + 'pixelDensity': { + 'name': 'pixelDensity', + 'class': 'p5', + 'module': 'Environment', + 'overloads': [ + { + 'params': [ + { + 'name': 'val', + 'description': 'desired pixel density.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + ] + } + ] + }, + 'displayDensity': { + 'name': 'displayDensity', + 'class': 'p5', + 'module': 'Environment' + }, + 'getURL': { + 'name': 'getURL', + 'class': 'p5', + 'module': 'Environment' + }, + 'getURLPath': { + 'name': 'getURLPath', + 'class': 'p5', + 'module': 'Environment' + }, + 'getURLParams': { + 'name': 'getURLParams', + 'class': 'p5', + 'module': 'Environment' + }, + 'preload': { + 'name': 'preload', + 'class': 'p5', + 'module': 'Structure' + }, + 'setup': { + 'name': 'setup', + 'class': 'p5', + 'module': 'Structure' + }, + 'draw': { + 'name': 'draw', + 'class': 'p5', + 'module': 'Structure' + }, + 'remove': { + 'name': 'remove', + 'class': 'p5', + 'module': 'Structure' + }, + 'disableFriendlyErrors': { + 'name': 'disableFriendlyErrors', + 'class': 'p5', + 'module': 'Structure' + }, + 'let': { + 'name': 'let', + 'class': 'p5', + 'module': 'Foundation' + }, + 'if': { + 'name': 'if', + 'class': 'p5', + 'module': 'Foundation' + }, + 'function': { + 'name': 'function', + 'class': 'p5', + 'module': 'Foundation' + }, + 'Boolean': { + 'name': 'Boolean', + 'class': 'p5', + 'module': 'Foundation' + }, + 'String': { + 'name': 'String', + 'class': 'p5', + 'module': 'Foundation' + }, + 'Number': { + 'name': 'Number', + 'class': 'p5', + 'module': 'Foundation' + }, + 'Object': { + 'name': 'Object', + 'class': 'p5', + 'module': 'Foundation' + }, + 'Array': { + 'name': 'Array', + 'class': 'p5', + 'module': 'Foundation' + }, + 'class': { + 'name': 'class', + 'class': 'p5', + 'module': 'Foundation' + }, + 'for': { + 'name': 'for', + 'class': 'p5', + 'module': 'Foundation' + }, + 'while': { + 'name': 'while', + 'class': 'p5', + 'module': 'Foundation' + }, + 'console': { + 'name': 'console', + 'class': 'p5', + 'module': 'Foundation' + }, + 'createCanvas': { + 'name': 'createCanvas', + 'class': 'p5', + 'module': 'Rendering', + 'overloads': [ + { + 'params': [ + { + 'name': 'width', + 'description': 'width of the canvas. Defaults to 100.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'height', + 'description': 'width of the canvas. Defaults to 100.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'renderer', + 'description': 'either P2D or WEBGL. Defaults to P2D
.
existing canvas element that should be used for the sketch.
\n', + 'type': 'HTMLCanvasElement', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'width', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'height', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'canvas', + 'description': '', + 'type': 'HTMLCanvasElement', + 'optional': true + } + ] + } + ] + }, + 'resizeCanvas': { + 'name': 'resizeCanvas', + 'params': [ + { + 'name': 'width', + 'description': 'width of the canvas.
\n', + 'type': 'Number' + }, + { + 'name': 'height', + 'description': 'height of the canvas.
\n', + 'type': 'Number' + }, + { + 'name': 'noRedraw', + 'description': 'whether to delay calling\n redraw(). Defaults\n to false
.
width of the graphics buffer.
\n', + 'type': 'Number' + }, + { + 'name': 'height', + 'description': 'height of the graphics buffer.
\n', + 'type': 'Number' + }, + { + 'name': 'renderer', + 'description': 'either P2D or WEBGL. Defaults to P2D.
\n', + 'type': 'Constant', + 'optional': true + }, + { + 'name': 'canvas', + 'description': 'existing canvas element that should be\n used for the graphics buffer..
\n', + 'type': 'HTMLCanvasElement', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'width', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'height', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'canvas', + 'description': '', + 'type': 'HTMLCanvasElement', + 'optional': true + } + ] + } + ] + }, + 'createFramebuffer': { + 'name': 'createFramebuffer', + 'params': [ + { + 'name': 'options', + 'description': 'configuration options.
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Rendering' + }, + 'clearDepth': { + 'name': 'clearDepth', + 'params': [ + { + 'name': 'depth', + 'description': 'amount of the depth buffer to clear between 0\n (none) and 1 (far clipping plane). Defaults to 1.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Rendering' + }, + 'blendMode': { + 'name': 'blendMode', + 'params': [ + { + 'name': 'mode', + 'description': 'blend mode to set.\n either BLEND, DARKEST, LIGHTEST, DIFFERENCE, MULTIPLY,\n EXCLUSION, SCREEN, REPLACE, OVERLAY, HARD_LIGHT,\n SOFT_LIGHT, DODGE, BURN, ADD, REMOVE or SUBTRACT
\n', + 'type': 'Constant' + } + ], + 'class': 'p5', + 'module': 'Rendering' + }, + 'drawingContext': { + 'name': 'drawingContext', + 'class': 'p5', + 'module': 'Rendering' + }, + 'noLoop': { + 'name': 'noLoop', + 'class': 'p5', + 'module': 'Structure' + }, + 'loop': { + 'name': 'loop', + 'class': 'p5', + 'module': 'Structure' + }, + 'isLooping': { + 'name': 'isLooping', + 'class': 'p5', + 'module': 'Structure' + }, + 'push': { + 'name': 'push', + 'class': 'p5', + 'module': 'Structure' + }, + 'pop': { + 'name': 'pop', + 'class': 'p5', + 'module': 'Structure' + }, + 'redraw': { + 'name': 'redraw', + 'params': [ + { + 'name': 'n', + 'description': 'number of times to run draw(). Defaults to 1.
\n', + 'type': 'Integer', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Structure' + }, + 'p5': { + 'name': 'p5', + 'params': [ + { + 'name': 'sketch', + 'description': 'function containing the sketch.
\n', + 'type': 'Object' + }, + { + 'name': 'node', + 'description': 'ID or reference to the HTML element that will contain the sketch.
\n', + 'type': 'String|HTMLElement' + } + ], + 'class': 'p5', + 'module': 'Structure' + }, + 'applyMatrix': { + 'name': 'applyMatrix', + 'class': 'p5', + 'module': 'Transform', + 'overloads': [ + { + 'params': [ + { + 'name': 'arr', + 'description': 'an array containing the elements of the transformation matrix. Its length should be either 6 (2D) or 16 (3D).
\n', + 'type': 'Array' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'a', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'b', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'c', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'd', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'e', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'f', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'a', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'b', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'c', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'd', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'e', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'f', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'g', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'h', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'i', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'j', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'k', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'l', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'm', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'n', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'o', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + }, + { + 'name': 'p', + 'description': 'an element of the transformation matrix.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + } + ] + }, + 'resetMatrix': { + 'name': 'resetMatrix', + 'class': 'p5', + 'module': 'Transform' + }, + 'rotate': { + 'name': 'rotate', + 'params': [ + { + 'name': 'angle', + 'description': 'angle of rotation in the current angleMode().
\n', + 'type': 'Number' + }, + { + 'name': 'axis', + 'description': 'axis to rotate about in 3D.
\n', + 'type': 'p5.Vector|Number[]', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Transform' + }, + 'rotateX': { + 'name': 'rotateX', + 'params': [ + { + 'name': 'angle', + 'description': 'angle of rotation in the current angleMode().
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Transform' + }, + 'rotateY': { + 'name': 'rotateY', + 'params': [ + { + 'name': 'angle', + 'description': 'angle of rotation in the current angleMode().
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Transform' + }, + 'rotateZ': { + 'name': 'rotateZ', + 'params': [ + { + 'name': 'angle', + 'description': 'angle of rotation in the current angleMode().
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Transform' + }, + 'scale': { + 'name': 'scale', + 'class': 'p5', + 'module': 'Transform', + 'overloads': [ + { + 'params': [ + { + 'name': 's', + 'description': 'amount to scale along the positive x-axis.
\n', + 'type': 'Number|p5.Vector|Number[]' + }, + { + 'name': 'y', + 'description': 'amount to scale along the positive y-axis. Defaults to s
.
amount to scale along the positive z-axis. Defaults to y
.
vector whose components should be used to scale.
\n', + 'type': 'p5.Vector|Number[]' + } + ], + 'chainable': 1 + } + ] + }, + 'shearX': { + 'name': 'shearX', + 'params': [ + { + 'name': 'angle', + 'description': 'angle to shear by in the current angleMode().
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Transform' + }, + 'shearY': { + 'name': 'shearY', + 'params': [ + { + 'name': 'angle', + 'description': 'angle to shear by in the current angleMode().
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Transform' + }, + 'translate': { + 'name': 'translate', + 'class': 'p5', + 'module': 'Transform', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'amount to translate along the positive x-axis.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'amount to translate along the positive y-axis.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'amount to translate along the positive z-axis.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'vector', + 'description': 'vector by which to translate.
\n', + 'type': 'p5.Vector' + } + ], + 'chainable': 1 + } + ] + }, + 'storeItem': { + 'name': 'storeItem', + 'params': [ + { + 'name': 'key', + 'description': 'name of the value.
\n', + 'type': 'String' + }, + { + 'name': 'value', + 'description': 'value to be stored.
\n', + 'type': 'String|Number|Boolean|Object|Array' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'getItem': { + 'name': 'getItem', + 'params': [ + { + 'name': 'key', + 'description': 'name of the value.
\n', + 'type': 'String' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'clearStorage': { + 'name': 'clearStorage', + 'class': 'p5', + 'module': 'Data' + }, + 'removeItem': { + 'name': 'removeItem', + 'params': [ + { + 'name': 'key', + 'description': 'name of the value to remove.
\n', + 'type': 'String' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'createStringDict': { + 'name': 'createStringDict', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'key', + 'description': '', + 'type': 'String' + }, + { + 'name': 'value', + 'description': '', + 'type': 'String' + } + ] + }, + { + 'params': [ + { + 'name': 'object', + 'description': 'object
\n', + 'type': 'Object' + } + ] + } + ] + }, + 'createNumberDict': { + 'name': 'createNumberDict', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'key', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'value', + 'description': '', + 'type': 'Number' + } + ] + }, + { + 'params': [ + { + 'name': 'object', + 'description': 'object
\n', + 'type': 'Object' + } + ] + } + ] + }, + 'select': { + 'name': 'select', + 'params': [ + { + 'name': 'selectors', + 'description': 'CSS selector string of element to search for.
\n', + 'type': 'String' + }, + { + 'name': 'container', + 'description': 'CSS selector string, p5.Element, or\n HTMLElement to search within.
\n', + 'type': 'String|p5.Element|HTMLElement', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'DOM' + }, + 'selectAll': { + 'name': 'selectAll', + 'params': [ + { + 'name': 'selectors', + 'description': 'CSS selector string of element to search for.
\n', + 'type': 'String' + }, + { + 'name': 'container', + 'description': 'CSS selector string, p5.Element, or\n HTMLElement to search within.
\n', + 'type': 'String|p5.Element|HTMLElement', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'DOM' + }, + 'removeElements': { + 'name': 'removeElements', + 'class': 'p5', + 'module': 'DOM' + }, + 'changed': { + 'name': 'changed', + 'params': [ + { + 'name': 'fxn', + 'description': 'function to call when the element changes.\n false
disables the function.
function to call when input is detected within\n the element.\n false
disables the function.
inner HTML for the new <div></div>
element.
inner HTML for the new <p></p>
element.
inner HTML for the new <span></span>
element.
relative path or URL for the image.
\n', + 'type': 'String' + }, + { + 'name': 'alt', + 'description': 'alternate text for the image.
\n', + 'type': 'String' + } + ] + }, + { + 'params': [ + { + 'name': 'src', + 'description': '', + 'type': 'String' + }, + { + 'name': 'alt', + 'description': '', + 'type': 'String' + }, + { + 'name': 'crossOrigin', + 'description': 'crossOrigin property to use when fetching the image.
\n', + 'type': 'String', + 'optional': true + }, + { + 'name': 'successCallback', + 'description': 'function to call once the image loads. The new image will be passed\n to the function as a p5.Element object.
\n', + 'type': 'Function', + 'optional': true + } + ] + } + ] + }, + 'createA': { + 'name': 'createA', + 'params': [ + { + 'name': 'href', + 'description': 'URL of linked page.
\n', + 'type': 'String' + }, + { + 'name': 'html', + 'description': 'inner HTML of link element to display.
\n', + 'type': 'String' + }, + { + 'name': 'target', + 'description': 'target where the new link should open,\n either \'_blank\'
, \'_self\'
, \'_parent\'
, or \'_top\'
.
minimum value of the slider.
\n', + 'type': 'Number' + }, + { + 'name': 'max', + 'description': 'maximum value of the slider.
\n', + 'type': 'Number' + }, + { + 'name': 'value', + 'description': 'default value of the slider.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'step', + 'description': 'size for each step in the slider\'s range.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'DOM' + }, + 'createButton': { + 'name': 'createButton', + 'params': [ + { + 'name': 'label', + 'description': 'label displayed on the button.
\n', + 'type': 'String' + }, + { + 'name': 'value', + 'description': 'value of the button.
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'DOM' + }, + 'createCheckbox': { + 'name': 'createCheckbox', + 'params': [ + { + 'name': 'label', + 'description': 'label displayed after the checkbox.
\n', + 'type': 'String', + 'optional': true + }, + { + 'name': 'value', + 'description': 'value of the checkbox. Checked is true
and unchecked is false
.
support multiple selections.
\n', + 'type': 'Boolean', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'existing', + 'description': 'select element to wrap, either as a p5.Element or\n a HTMLSelectElement.
\n', + 'type': 'Object' + } + ] + } + ] + }, + 'createRadio': { + 'name': 'createRadio', + 'class': 'p5', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + { + 'name': 'containerElement', + 'description': 'container HTML Element, either a <div></div>
\nor <span></span>
.
name parameter assigned to each option\'s <input></input>
element.
default color as a CSS color string.
\n', + 'type': 'String|p5.Color', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'DOM' + }, + 'createInput': { + 'name': 'createInput', + 'class': 'p5', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + { + 'name': 'value', + 'description': 'default value of the input box. Defaults to an empty string \'\'
.
type of input. Defaults to \'text\'
.
function to call once the file loads.
\n', + 'type': 'Function' + }, + { + 'name': 'multiple', + 'description': 'allow multiple files to be selected.
\n', + 'type': 'Boolean', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'DOM' + }, + 'createVideo': { + 'name': 'createVideo', + 'params': [ + { + 'name': 'src', + 'description': 'path to a video file, or an array of paths for\n supporting different browsers.
\n', + 'type': 'String|String[]' + }, + { + 'name': 'callback', + 'description': 'function to call once the video is ready to play.
\n', + 'type': 'Function', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'DOM' + }, + 'createAudio': { + 'name': 'createAudio', + 'params': [ + { + 'name': 'src', + 'description': 'path to an audio file, or an array of paths\n for supporting different browsers.
\n', + 'type': 'String|String[]', + 'optional': true + }, + { + 'name': 'callback', + 'description': 'function to call once the audio is ready to play.
\n', + 'type': 'Function', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'DOM' + }, + 'createCapture': { + 'name': 'createCapture', + 'params': [ + { + 'name': 'type', + 'description': 'type of capture, either AUDIO or VIDEO,\n or a constraints object. Both video and audio\n audio streams are captured by default.
\n', + 'type': 'String|Constant|Object', + 'optional': true + }, + { + 'name': 'flipped', + 'description': 'flip the capturing video and mirror the output with {flipped:true}
. By\n default it is false.
function to call once the stream\n has loaded.
\n', + 'type': 'Function', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'DOM' + }, + 'createElement': { + 'name': 'createElement', + 'params': [ + { + 'name': 'tag', + 'description': 'tag for the new element.
\n', + 'type': 'String' + }, + { + 'name': 'content', + 'description': 'HTML content to insert into the element.
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'DOM' + }, + 'deviceOrientation': { + 'name': 'deviceOrientation', + 'class': 'p5', + 'module': 'Events' + }, + 'accelerationX': { + 'name': 'accelerationX', + 'class': 'p5', + 'module': 'Events' + }, + 'accelerationY': { + 'name': 'accelerationY', + 'class': 'p5', + 'module': 'Events' + }, + 'accelerationZ': { + 'name': 'accelerationZ', + 'class': 'p5', + 'module': 'Events' + }, + 'pAccelerationX': { + 'name': 'pAccelerationX', + 'class': 'p5', + 'module': 'Events' + }, + 'pAccelerationY': { + 'name': 'pAccelerationY', + 'class': 'p5', + 'module': 'Events' + }, + 'pAccelerationZ': { + 'name': 'pAccelerationZ', + 'class': 'p5', + 'module': 'Events' + }, + 'rotationX': { + 'name': 'rotationX', + 'class': 'p5', + 'module': 'Events' + }, + 'rotationY': { + 'name': 'rotationY', + 'class': 'p5', + 'module': 'Events' + }, + 'rotationZ': { + 'name': 'rotationZ', + 'class': 'p5', + 'module': 'Events' + }, + 'pRotationX': { + 'name': 'pRotationX', + 'class': 'p5', + 'module': 'Events' + }, + 'pRotationY': { + 'name': 'pRotationY', + 'class': 'p5', + 'module': 'Events' + }, + 'pRotationZ': { + 'name': 'pRotationZ', + 'class': 'p5', + 'module': 'Events' + }, + 'turnAxis': { + 'name': 'turnAxis', + 'class': 'p5', + 'module': 'Events' + }, + 'setMoveThreshold': { + 'name': 'setMoveThreshold', + 'params': [ + { + 'name': 'value', + 'description': 'The threshold value
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Events' + }, + 'setShakeThreshold': { + 'name': 'setShakeThreshold', + 'params': [ + { + 'name': 'value', + 'description': 'The threshold value
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Events' + }, + 'deviceMoved': { + 'name': 'deviceMoved', + 'class': 'p5', + 'module': 'Events' + }, + 'deviceTurned': { + 'name': 'deviceTurned', + 'class': 'p5', + 'module': 'Events' + }, + 'deviceShaken': { + 'name': 'deviceShaken', + 'class': 'p5', + 'module': 'Events' + }, + 'keyIsPressed': { + 'name': 'keyIsPressed', + 'class': 'p5', + 'module': 'Events' + }, + 'key': { + 'name': 'key', + 'class': 'p5', + 'module': 'Events' + }, + 'keyCode': { + 'name': 'keyCode', + 'class': 'p5', + 'module': 'Events' + }, + 'keyPressed': { + 'name': 'keyPressed', + 'params': [ + { + 'name': 'event', + 'description': 'optional KeyboardEvent
callback argument.
optional KeyboardEvent
callback argument.
optional KeyboardEvent
callback argument.
key to check.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Events' + }, + 'movedX': { + 'name': 'movedX', + 'class': 'p5', + 'module': 'Events' + }, + 'movedY': { + 'name': 'movedY', + 'class': 'p5', + 'module': 'Events' + }, + 'mouseX': { + 'name': 'mouseX', + 'class': 'p5', + 'module': 'Events' + }, + 'mouseY': { + 'name': 'mouseY', + 'class': 'p5', + 'module': 'Events' + }, + 'pmouseX': { + 'name': 'pmouseX', + 'class': 'p5', + 'module': 'Events' + }, + 'pmouseY': { + 'name': 'pmouseY', + 'class': 'p5', + 'module': 'Events' + }, + 'winMouseX': { + 'name': 'winMouseX', + 'class': 'p5', + 'module': 'Events' + }, + 'winMouseY': { + 'name': 'winMouseY', + 'class': 'p5', + 'module': 'Events' + }, + 'pwinMouseX': { + 'name': 'pwinMouseX', + 'class': 'p5', + 'module': 'Events' + }, + 'pwinMouseY': { + 'name': 'pwinMouseY', + 'class': 'p5', + 'module': 'Events' + }, + 'mouseButton': { + 'name': 'mouseButton', + 'class': 'p5', + 'module': 'Events' + }, + 'mouseIsPressed': { + 'name': 'mouseIsPressed', + 'class': 'p5', + 'module': 'Events' + }, + 'mouseMoved': { + 'name': 'mouseMoved', + 'params': [ + { + 'name': 'event', + 'description': 'optional MouseEvent
argument.
optional MouseEvent
argument.
optional MouseEvent
argument.
optional MouseEvent
argument.
optional MouseEvent
argument.
optional MouseEvent
argument.
optional WheelEvent
argument.
optional TouchEvent
argument.
optional TouchEvent argument.
\n', + 'type': 'TouchEvent', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Events' + }, + 'touchEnded': { + 'name': 'touchEnded', + 'params': [ + { + 'name': 'event', + 'description': 'optional TouchEvent
argument.
width in pixels.
\n', + 'type': 'Integer' + }, + { + 'name': 'height', + 'description': 'height in pixels.
\n', + 'type': 'Integer' + } + ], + 'class': 'p5', + 'module': 'Image' + }, + 'saveCanvas': { + 'name': 'saveCanvas', + 'class': 'p5', + 'module': 'Image', + 'overloads': [ + { + 'params': [ + { + 'name': 'selectedCanvas', + 'description': 'reference to a\n specific HTML5 canvas element.
\n', + 'type': 'p5.Framebuffer|p5.Element|HTMLCanvasElement' + }, + { + 'name': 'filename', + 'description': 'file name. Defaults to \'untitled\'.
\n', + 'type': 'String', + 'optional': true + }, + { + 'name': 'extension', + 'description': 'file extension, either \'jpg\' or \'png\'. Defaults to \'png\'.
\n', + 'type': 'String', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'filename', + 'description': '', + 'type': 'String', + 'optional': true + }, + { + 'name': 'extension', + 'description': '', + 'type': 'String', + 'optional': true + } + ] + } + ] + }, + 'saveFrames': { + 'name': 'saveFrames', + 'params': [ + { + 'name': 'filename', + 'description': 'prefix of file name.
\n', + 'type': 'String' + }, + { + 'name': 'extension', + 'description': 'file extension, either \'jpg\' or \'png\'.
\n', + 'type': 'String' + }, + { + 'name': 'duration', + 'description': 'duration in seconds to record. This parameter will be constrained to be less or equal to 15.
\n', + 'type': 'Number' + }, + { + 'name': 'framerate', + 'description': 'number of frames to save per second. This parameter will be constrained to be less or equal to 22.
\n', + 'type': 'Number' + }, + { + 'name': 'callback', + 'description': 'callback function that will be executed\n to handle the image data. This function\n should accept an array as argument. The\n array will contain the specified number of\n frames of objects. Each object has three\n properties: imageData
, filename
, and extension
.
path of the image to be loaded or base64 encoded image.
\n', + 'type': 'String' + }, + { + 'name': 'successCallback', + 'description': 'function called with\n p5.Image once it\n loads.
\n', + 'type': 'function(p5.Image)', + 'optional': true + }, + { + 'name': 'failureCallback', + 'description': 'function called with event\n error if the image fails to load.
\n', + 'type': 'Function(Event)', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Image' + }, + 'saveGif': { + 'name': 'saveGif', + 'params': [ + { + 'name': 'filename', + 'description': 'file name of gif.
\n', + 'type': 'String' + }, + { + 'name': 'duration', + 'description': 'duration in seconds to capture from the sketch.
\n', + 'type': 'Number' + }, + { + 'name': 'options', + 'description': 'an object that can contain five more properties:\n delay
, a Number specifying how much time to wait before recording;\n units
, a String that can be either \'seconds\' or \'frames\'. By default it\'s \'seconds’;\n silent
, a Boolean that defines presence of progress notifications. By default it’s false
;\n notificationDuration
, a Number that defines how long in seconds the final notification\n will live. By default it\'s 0
, meaning the notification will never be removed;\n notificationID
, a String that specifies the id of the notification\'s DOM element. By default it’s \'progressBar’
.
image to display.
\n', + 'type': 'p5.Image|p5.Element|p5.Texture|p5.Framebuffer|p5.FramebufferTexture' + }, + { + 'name': 'x', + 'description': 'x-coordinate of the top-left corner of the image.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the top-left corner of the image.
\n', + 'type': 'Number' + }, + { + 'name': 'width', + 'description': 'width to draw the image.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'height', + 'description': 'height to draw the image.
\n', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'img', + 'description': '', + 'type': 'p5.Image|p5.Element|p5.Texture|p5.Framebuffer|p5.FramebufferTexture' + }, + { + 'name': 'dx', + 'description': 'the x-coordinate of the destination\n rectangle in which to draw the source image
\n', + 'type': 'Number' + }, + { + 'name': 'dy', + 'description': 'the y-coordinate of the destination\n rectangle in which to draw the source image
\n', + 'type': 'Number' + }, + { + 'name': 'dWidth', + 'description': 'the width of the destination rectangle
\n', + 'type': 'Number' + }, + { + 'name': 'dHeight', + 'description': 'the height of the destination rectangle
\n', + 'type': 'Number' + }, + { + 'name': 'sx', + 'description': 'the x-coordinate of the subsection of the source\nimage to draw into the destination rectangle
\n', + 'type': 'Number' + }, + { + 'name': 'sy', + 'description': 'the y-coordinate of the subsection of the source\nimage to draw into the destination rectangle
\n', + 'type': 'Number' + }, + { + 'name': 'sWidth', + 'description': 'the width of the subsection of the\n source image to draw into the destination\n rectangle
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'sHeight', + 'description': 'the height of the subsection of the\n source image to draw into the destination rectangle
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'fit', + 'description': 'either CONTAIN or COVER
\n', + 'type': 'Constant', + 'optional': true + }, + { + 'name': 'xAlign', + 'description': 'either LEFT, RIGHT or CENTER default is CENTER
\n', + 'type': 'Constant', + 'optional': true + }, + { + 'name': 'yAlign', + 'description': 'either TOP, BOTTOM or CENTER default is CENTER
\n', + 'type': 'Constant', + 'optional': true + } + ] + } + ] + }, + 'tint': { + 'name': 'tint', + 'class': 'p5', + 'module': 'Image', + 'overloads': [ + { + 'params': [ + { + 'name': 'v1', + 'description': 'red or hue value.
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green or saturation value.
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue or brightness.
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': '', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'value', + 'description': 'CSS color string.
\n', + 'type': 'String' + } + ] + }, + { + 'params': [ + { + 'name': 'gray', + 'description': 'grayscale value.
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': '', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'values', + 'description': 'array containing the red, green, blue &\n alpha components of the color.
\n', + 'type': 'Number[]' + } + ] + }, + { + 'params': [ + { + 'name': 'color', + 'description': 'the tint color
\n', + 'type': 'p5.Color' + } + ] + } + ] + }, + 'noTint': { + 'name': 'noTint', + 'class': 'p5', + 'module': 'Image' + }, + 'imageMode': { + 'name': 'imageMode', + 'params': [ + { + 'name': 'mode', + 'description': 'either CORNER, CORNERS, or CENTER.
\n', + 'type': 'Constant' + } + ], + 'class': 'p5', + 'module': 'Image' + }, + 'pixels': { + 'name': 'pixels', + 'class': 'p5', + 'module': 'Image' + }, + 'blend': { + 'name': 'blend', + 'class': 'p5', + 'module': 'Image', + 'overloads': [ + { + 'params': [ + { + 'name': 'srcImage', + 'description': 'source image.
\n', + 'type': 'p5.Image' + }, + { + 'name': 'sx', + 'description': 'x-coordinate of the source\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'sy', + 'description': 'y-coordinate of the source\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'sw', + 'description': 'source image width.
\n', + 'type': 'Integer' + }, + { + 'name': 'sh', + 'description': 'source image height.
\n', + 'type': 'Integer' + }, + { + 'name': 'dx', + 'description': 'x-coordinate of the destination\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'dy', + 'description': 'y-coordinate of the destination\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'dw', + 'description': 'destination image width.
\n', + 'type': 'Integer' + }, + { + 'name': 'dh', + 'description': 'destination image height.
\n', + 'type': 'Integer' + }, + { + 'name': 'blendMode', + 'description': 'the blend mode. either\n BLEND, DARKEST, LIGHTEST, DIFFERENCE,\n MULTIPLY, EXCLUSION, SCREEN, REPLACE, OVERLAY, HARD_LIGHT,\n SOFT_LIGHT, DODGE, BURN, ADD or NORMAL.
\n', + 'type': 'Constant' + } + ] + }, + { + 'params': [ + { + 'name': 'sx', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'sy', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'sw', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'sh', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dx', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dy', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dw', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dh', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'blendMode', + 'description': '', + 'type': 'Constant' + } + ] + } + ] + }, + 'copy': { + 'name': 'copy', + 'class': 'p5', + 'module': 'Image', + 'overloads': [ + { + 'params': [ + { + 'name': 'srcImage', + 'description': 'source image.
\n', + 'type': 'p5.Image|p5.Element' + }, + { + 'name': 'sx', + 'description': 'x-coordinate of the source\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'sy', + 'description': 'y-coordinate of the source\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'sw', + 'description': 'source image width.
\n', + 'type': 'Integer' + }, + { + 'name': 'sh', + 'description': 'source image height.
\n', + 'type': 'Integer' + }, + { + 'name': 'dx', + 'description': 'x-coordinate of the destination\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'dy', + 'description': 'y-coordinate of the destination\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'dw', + 'description': 'destination image width.
\n', + 'type': 'Integer' + }, + { + 'name': 'dh', + 'description': 'destination image height.
\n', + 'type': 'Integer' + } + ] + }, + { + 'params': [ + { + 'name': 'sx', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'sy', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'sw', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'sh', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dx', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dy', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dw', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dh', + 'description': '', + 'type': 'Integer' + } + ] + } + ] + }, + 'filter': { + 'name': 'filter', + 'class': 'p5', + 'module': 'Image', + 'overloads': [ + { + 'params': [ + { + 'name': 'filterType', + 'description': 'either THRESHOLD, GRAY, OPAQUE, INVERT,\n POSTERIZE, BLUR, ERODE, DILATE or BLUR.
\n', + 'type': 'Constant' + }, + { + 'name': 'filterParam', + 'description': 'parameter unique to each filter.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'useWebGL', + 'description': 'flag to control whether to use fast\n WebGL filters (GPU) or original image\n filters (CPU); defaults to true
.
shader that\'s been loaded, with the\n frag shader using a tex0
uniform.
x-coordinate of the pixel.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the pixel.
\n', + 'type': 'Number' + }, + { + 'name': 'w', + 'description': 'width of the subsection to be returned.
\n', + 'type': 'Number' + }, + { + 'name': 'h', + 'description': 'height of the subsection to be returned.
\n', + 'type': 'Number' + } + ] + }, + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + } + ] + } + ] + }, + 'loadPixels': { + 'name': 'loadPixels', + 'class': 'p5', + 'module': 'Image' + }, + 'set': { + 'name': 'set', + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the pixel.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the pixel.
\n', + 'type': 'Number' + }, + { + 'name': 'c', + 'description': 'grayscale value | pixel array |\n p5.Color object | p5.Image to copy.
\n', + 'type': 'Number|Number[]|Object' + } + ], + 'class': 'p5', + 'module': 'Image' + }, + 'updatePixels': { + 'name': 'updatePixels', + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the upper-left corner of region\n to update.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'y', + 'description': 'y-coordinate of the upper-left corner of region\n to update.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'w', + 'description': 'width of region to update.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'h', + 'description': 'height of region to update.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Image' + }, + 'loadJSON': { + 'name': 'loadJSON', + 'params': [ + { + 'name': 'path', + 'description': 'path of the JSON file to be loaded.
\n', + 'type': 'String' + }, + { + 'name': 'successCallback', + 'description': 'function to call once the data is loaded. Will be passed the object.
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': 'function to call if the data fails to load. Will be passed an Error
event object.
path of the text file to be loaded.
\n', + 'type': 'String' + }, + { + 'name': 'successCallback', + 'description': 'function to call once the data is\n loaded. Will be passed the array.
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': 'function to call if the data fails to\n load. Will be passed an Error
event\n object.
name of the file or URL to load
\n', + 'type': 'String' + }, + { + 'name': 'extension', + 'description': 'parse the table by comma-separated values "csv", semicolon-separated\n values "ssv", or tab-separated values "tsv"
\n', + 'type': 'String', + 'optional': true + }, + { + 'name': 'header', + 'description': '"header" to indicate table has header row
\n', + 'type': 'String', + 'optional': true + }, + { + 'name': 'callback', + 'description': 'function to be executed after\n loadTable() completes. On success, the\n Table object is passed in as the\n first argument.
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': 'function to be executed if\n there is an error, response is passed\n in as first argument
\n', + 'type': 'Function', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'IO' + }, + 'loadXML': { + 'name': 'loadXML', + 'params': [ + { + 'name': 'path', + 'description': 'path of the XML file to be loaded.
\n', + 'type': 'String' + }, + { + 'name': 'successCallback', + 'description': 'function to call once the data is\n loaded. Will be passed the\n p5.XML object.
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': 'function to call if the data fails to\n load. Will be passed an Error
event\n object.
name of the file or URL to load
\n', + 'type': 'String' + }, + { + 'name': 'callback', + 'description': 'function to be executed after loadBytes()\n completes
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': 'function to be executed if there\n is an error
\n', + 'type': 'Function', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'IO' + }, + 'httpGet': { + 'name': 'httpGet', + 'class': 'p5', + 'module': 'IO', + 'overloads': [ + { + 'params': [ + { + 'name': 'path', + 'description': 'name of the file or url to load
\n', + 'type': 'String' + }, + { + 'name': 'datatype', + 'description': '"json", "jsonp", "binary", "arrayBuffer",\n "xml", or "text"
\n', + 'type': 'String', + 'optional': true + }, + { + 'name': 'data', + 'description': 'param data passed sent with request
\n', + 'type': 'Object|Boolean', + 'optional': true + }, + { + 'name': 'callback', + 'description': 'function to be executed after\n httpGet() completes, data is passed in\n as first argument
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': 'function to be executed if\n there is an error, response is passed\n in as first argument
\n', + 'type': 'Function', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'path', + 'description': '', + 'type': 'String' + }, + { + 'name': 'data', + 'description': '', + 'type': 'Object|Boolean' + }, + { + 'name': 'callback', + 'description': '', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': '', + 'type': 'Function', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'path', + 'description': '', + 'type': 'String' + }, + { + 'name': 'callback', + 'description': '', + 'type': 'Function' + }, + { + 'name': 'errorCallback', + 'description': '', + 'type': 'Function', + 'optional': true + } + ] + } + ] + }, + 'httpPost': { + 'name': 'httpPost', + 'class': 'p5', + 'module': 'IO', + 'overloads': [ + { + 'params': [ + { + 'name': 'path', + 'description': 'name of the file or url to load
\n', + 'type': 'String' + }, + { + 'name': 'datatype', + 'description': '"json", "jsonp", "xml", or "text".\n If omitted, httpPost() will guess.
\n', + 'type': 'String', + 'optional': true + }, + { + 'name': 'data', + 'description': 'param data passed sent with request
\n', + 'type': 'Object|Boolean', + 'optional': true + }, + { + 'name': 'callback', + 'description': 'function to be executed after\n httpPost() completes, data is passed in\n as first argument
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': 'function to be executed if\n there is an error, response is passed\n in as first argument
\n', + 'type': 'Function', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'path', + 'description': '', + 'type': 'String' + }, + { + 'name': 'data', + 'description': '', + 'type': 'Object|Boolean' + }, + { + 'name': 'callback', + 'description': '', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': '', + 'type': 'Function', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'path', + 'description': '', + 'type': 'String' + }, + { + 'name': 'callback', + 'description': '', + 'type': 'Function' + }, + { + 'name': 'errorCallback', + 'description': '', + 'type': 'Function', + 'optional': true + } + ] + } + ] + }, + 'httpDo': { + 'name': 'httpDo', + 'class': 'p5', + 'module': 'IO', + 'overloads': [ + { + 'params': [ + { + 'name': 'path', + 'description': 'name of the file or url to load
\n', + 'type': 'String' + }, + { + 'name': 'method', + 'description': 'either "GET", "POST", or "PUT",\n defaults to "GET"
\n', + 'type': 'String', + 'optional': true + }, + { + 'name': 'datatype', + 'description': '"json", "jsonp", "xml", or "text"
\n', + 'type': 'String', + 'optional': true + }, + { + 'name': 'data', + 'description': 'param data passed sent with request
\n', + 'type': 'Object', + 'optional': true + }, + { + 'name': 'callback', + 'description': 'function to be executed after\n httpGet() completes, data is passed in\n as first argument
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': 'function to be executed if\n there is an error, response is passed\n in as first argument
\n', + 'type': 'Function', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'path', + 'description': '', + 'type': 'String' + }, + { + 'name': 'options', + 'description': 'Request object options as documented in the\n "fetch" API\nreference
\n', + 'type': 'Object' + }, + { + 'name': 'callback', + 'description': '', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': '', + 'type': 'Function', + 'optional': true + } + ] + } + ] + }, + 'createWriter': { + 'name': 'createWriter', + 'params': [ + { + 'name': 'name', + 'description': 'name of the file to create.
\n', + 'type': 'String' + }, + { + 'name': 'extension', + 'description': 'format to use for the file.
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'IO' + }, + 'save': { + 'name': 'save', + 'params': [ + { + 'name': 'objectOrFilename', + 'description': 'If filename is provided, will\n save canvas as an image with\n either png or jpg extension\n depending on the filename.\n If object is provided, will\n save depending on the object\n and filename (see examples\n above).
\n', + 'type': 'Object|String', + 'optional': true + }, + { + 'name': 'filename', + 'description': 'If an object is provided as the first\n parameter, then the second parameter\n indicates the filename,\n and should include an appropriate\n file extension (see examples above).
\n', + 'type': 'String', + 'optional': true + }, + { + 'name': 'options', + 'description': 'Additional options depend on\n filetype. For example, when saving JSON,\n true
indicates that the\n output will be optimized for filesize,\n rather than readability.
data to save.
\n', + 'type': 'Array|Object' + }, + { + 'name': 'filename', + 'description': 'name of the file to be saved.
\n', + 'type': 'String' + }, + { + 'name': 'optimize', + 'description': 'whether to trim unneeded whitespace. Defaults\n to true
.
data to save.
\n', + 'type': 'String[]' + }, + { + 'name': 'filename', + 'description': 'name of file to be saved.
\n', + 'type': 'String' + }, + { + 'name': 'extension', + 'description': 'format to use for the file.
\n', + 'type': 'String', + 'optional': true + }, + { + 'name': 'isCRLF', + 'description': 'whether to add \\r\\n
to the end of each\n string. Defaults to false
.
the Table object to save to a file
\n', + 'type': 'p5.Table' + }, + { + 'name': 'filename', + 'description': 'the filename to which the Table should be saved
\n', + 'type': 'String' + }, + { + 'name': 'options', + 'description': 'can be one of "tsv", "csv", or "html"
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'IO' + }, + 'abs': { + 'name': 'abs', + 'params': [ + { + 'name': 'n', + 'description': 'number to compute.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'ceil': { + 'name': 'ceil', + 'params': [ + { + 'name': 'n', + 'description': 'number to round up.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'constrain': { + 'name': 'constrain', + 'params': [ + { + 'name': 'n', + 'description': 'number to constrain.
\n', + 'type': 'Number' + }, + { + 'name': 'low', + 'description': 'minimum limit.
\n', + 'type': 'Number' + }, + { + 'name': 'high', + 'description': 'maximum limit.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'dist': { + 'name': 'dist', + 'class': 'p5', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'x1', + 'description': 'x-coordinate of the first point.
\n', + 'type': 'Number' + }, + { + 'name': 'y1', + 'description': 'y-coordinate of the first point.
\n', + 'type': 'Number' + }, + { + 'name': 'x2', + 'description': 'x-coordinate of the second point.
\n', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': 'y-coordinate of the second point.
\n', + 'type': 'Number' + } + ] + }, + { + 'params': [ + { + 'name': 'x1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z1', + 'description': 'z-coordinate of the first point.
\n', + 'type': 'Number' + }, + { + 'name': 'x2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z2', + 'description': 'z-coordinate of the second point.
\n', + 'type': 'Number' + } + ] + } + ] + }, + 'exp': { + 'name': 'exp', + 'params': [ + { + 'name': 'n', + 'description': 'exponent to raise.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'floor': { + 'name': 'floor', + 'params': [ + { + 'name': 'n', + 'description': 'number to round down.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'lerp': { + 'name': 'lerp', + 'params': [ + { + 'name': 'start', + 'description': 'first value.
\n', + 'type': 'Number' + }, + { + 'name': 'stop', + 'description': 'second value.
\n', + 'type': 'Number' + }, + { + 'name': 'amt', + 'description': 'number.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'log': { + 'name': 'log', + 'params': [ + { + 'name': 'n', + 'description': 'number greater than 0.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'mag': { + 'name': 'mag', + 'params': [ + { + 'name': 'x', + 'description': 'first component.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'second component.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'map': { + 'name': 'map', + 'params': [ + { + 'name': 'value', + 'description': 'the value to be remapped.
\n', + 'type': 'Number' + }, + { + 'name': 'start1', + 'description': 'lower bound of the value\'s current range.
\n', + 'type': 'Number' + }, + { + 'name': 'stop1', + 'description': 'upper bound of the value\'s current range.
\n', + 'type': 'Number' + }, + { + 'name': 'start2', + 'description': 'lower bound of the value\'s target range.
\n', + 'type': 'Number' + }, + { + 'name': 'stop2', + 'description': 'upper bound of the value\'s target range.
\n', + 'type': 'Number' + }, + { + 'name': 'withinBounds', + 'description': 'constrain the value to the newly mapped range.
\n', + 'type': 'Boolean', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'max': { + 'name': 'max', + 'class': 'p5', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'n0', + 'description': 'first number to compare.
\n', + 'type': 'Number' + }, + { + 'name': 'n1', + 'description': 'second number to compare.
\n', + 'type': 'Number' + } + ] + }, + { + 'params': [ + { + 'name': 'nums', + 'description': 'numbers to compare.
\n', + 'type': 'Number[]' + } + ] + } + ] + }, + 'min': { + 'name': 'min', + 'class': 'p5', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'n0', + 'description': 'first number to compare.
\n', + 'type': 'Number' + }, + { + 'name': 'n1', + 'description': 'second number to compare.
\n', + 'type': 'Number' + } + ] + }, + { + 'params': [ + { + 'name': 'nums', + 'description': 'numbers to compare.
\n', + 'type': 'Number[]' + } + ] + } + ] + }, + 'norm': { + 'name': 'norm', + 'params': [ + { + 'name': 'value', + 'description': 'incoming value to be normalized.
\n', + 'type': 'Number' + }, + { + 'name': 'start', + 'description': 'lower bound of the value\'s current range.
\n', + 'type': 'Number' + }, + { + 'name': 'stop', + 'description': 'upper bound of the value\'s current range.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'pow': { + 'name': 'pow', + 'params': [ + { + 'name': 'n', + 'description': 'base of the exponential expression.
\n', + 'type': 'Number' + }, + { + 'name': 'e', + 'description': 'power by which to raise the base.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'round': { + 'name': 'round', + 'params': [ + { + 'name': 'n', + 'description': 'number to round.
\n', + 'type': 'Number' + }, + { + 'name': 'decimals', + 'description': 'number of decimal places to round to, default is 0.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'sq': { + 'name': 'sq', + 'params': [ + { + 'name': 'n', + 'description': 'number to square.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'sqrt': { + 'name': 'sqrt', + 'params': [ + { + 'name': 'n', + 'description': 'non-negative number to square root.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'fract': { + 'name': 'fract', + 'params': [ + { + 'name': 'n', + 'description': 'number whose fractional part will be found.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'createVector': { + 'name': 'createVector', + 'params': [ + { + 'name': 'x', + 'description': 'x component of the vector.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'y', + 'description': 'y component of the vector.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'z', + 'description': 'z component of the vector.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'noise': { + 'name': 'noise', + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate in noise space.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate in noise space.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'z', + 'description': 'z-coordinate in noise space.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'noiseDetail': { + 'name': 'noiseDetail', + 'params': [ + { + 'name': 'lod', + 'description': 'number of octaves to be used by the noise.
\n', + 'type': 'Number' + }, + { + 'name': 'falloff', + 'description': 'falloff factor for each octave.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'noiseSeed': { + 'name': 'noiseSeed', + 'params': [ + { + 'name': 'seed', + 'description': 'seed value.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'randomSeed': { + 'name': 'randomSeed', + 'params': [ + { + 'name': 'seed', + 'description': 'seed value.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'random': { + 'name': 'random', + 'class': 'p5', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'min', + 'description': 'lower bound (inclusive).
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'max', + 'description': 'upper bound (exclusive).
\n', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'choices', + 'description': 'array to choose from.
\n', + 'type': 'Array' + } + ] + } + ] + }, + 'randomGaussian': { + 'name': 'randomGaussian', + 'params': [ + { + 'name': 'mean', + 'description': 'mean.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'sd', + 'description': 'standard deviation.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'acos': { + 'name': 'acos', + 'params': [ + { + 'name': 'value', + 'description': 'value whose arc cosine is to be returned.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'asin': { + 'name': 'asin', + 'params': [ + { + 'name': 'value', + 'description': 'value whose arc sine is to be returned.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'atan': { + 'name': 'atan', + 'params': [ + { + 'name': 'value', + 'description': 'value whose arc tangent is to be returned.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'atan2': { + 'name': 'atan2', + 'params': [ + { + 'name': 'y', + 'description': 'y-coordinate of the point.
\n', + 'type': 'Number' + }, + { + 'name': 'x', + 'description': 'x-coordinate of the point.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'cos': { + 'name': 'cos', + 'params': [ + { + 'name': 'angle', + 'description': 'the angle.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'sin': { + 'name': 'sin', + 'params': [ + { + 'name': 'angle', + 'description': 'the angle.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'tan': { + 'name': 'tan', + 'params': [ + { + 'name': 'angle', + 'description': 'the angle.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'degrees': { + 'name': 'degrees', + 'params': [ + { + 'name': 'radians', + 'description': 'radians value to convert to degrees.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'radians': { + 'name': 'radians', + 'params': [ + { + 'name': 'degrees', + 'description': 'degree value to convert to radians.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'Math' + }, + 'angleMode': { + 'name': 'angleMode', + 'class': 'p5', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'mode', + 'description': 'either RADIANS or DEGREES.
\n', + 'type': 'Constant' + } + ] + }, + { + 'params': [ + ] + } + ] + }, + 'textAlign': { + 'name': 'textAlign', + 'class': 'p5', + 'module': 'Typography', + 'overloads': [ + { + 'params': [ + { + 'name': 'horizAlign', + 'description': 'horizontal alignment, either LEFT,\n CENTER, or RIGHT.
\n', + 'type': 'Constant' + }, + { + 'name': 'vertAlign', + 'description': 'vertical alignment, either TOP,\n BOTTOM, CENTER, or BASELINE.
\n', + 'type': 'Constant', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + ] + } + ] + }, + 'textLeading': { + 'name': 'textLeading', + 'class': 'p5', + 'module': 'Typography', + 'overloads': [ + { + 'params': [ + { + 'name': 'leading', + 'description': 'spacing between lines of text in units of pixels.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + ] + } + ] + }, + 'textSize': { + 'name': 'textSize', + 'class': 'p5', + 'module': 'Typography', + 'overloads': [ + { + 'params': [ + { + 'name': 'size', + 'description': 'size of the letters in units of pixels.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + ] + } + ] + }, + 'textStyle': { + 'name': 'textStyle', + 'class': 'p5', + 'module': 'Typography', + 'overloads': [ + { + 'params': [ + { + 'name': 'style', + 'description': 'styling for text, either NORMAL,\n ITALIC, BOLD or BOLDITALIC.
\n', + 'type': 'Constant' + } + ], + 'chainable': 1 + }, + { + 'params': [ + ] + } + ] + }, + 'textWidth': { + 'name': 'textWidth', + 'params': [ + { + 'name': 'str', + 'description': 'string of text to measure.
\n', + 'type': 'String' + } + ], + 'class': 'p5', + 'module': 'Typography' + }, + 'textAscent': { + 'name': 'textAscent', + 'class': 'p5', + 'module': 'Typography' + }, + 'textDescent': { + 'name': 'textDescent', + 'class': 'p5', + 'module': 'Typography' + }, + 'textWrap': { + 'name': 'textWrap', + 'params': [ + { + 'name': 'style', + 'description': 'text wrapping style, either WORD or CHAR.
\n', + 'type': 'Constant' + } + ], + 'class': 'p5', + 'module': 'Typography' + }, + 'loadFont': { + 'name': 'loadFont', + 'params': [ + { + 'name': 'path', + 'description': 'path of the font to be loaded.
\n', + 'type': 'String' + }, + { + 'name': 'successCallback', + 'description': 'function called with the\n p5.Font object after it\n loads.
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'failureCallback', + 'description': 'function called with the error\n Event\n object if the font fails to load.
\n', + 'type': 'Function', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Typography' + }, + 'text': { + 'name': 'text', + 'params': [ + { + 'name': 'str', + 'description': 'text to be displayed.
\n', + 'type': 'String|Object|Array|Number|Boolean' + }, + { + 'name': 'x', + 'description': 'x-coordinate of the text box.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the text box.
\n', + 'type': 'Number' + }, + { + 'name': 'maxWidth', + 'description': 'maximum width of the text box. See\n rectMode() for\n other options.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'maxHeight', + 'description': 'maximum height of the text box. See\n rectMode() for\n other options.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Typography' + }, + 'textFont': { + 'name': 'textFont', + 'class': 'p5', + 'module': 'Typography', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'font', + 'description': 'font as a p5.Font object or a string.
\n', + 'type': 'Object|String' + }, + { + 'name': 'size', + 'description': 'font size in pixels.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + } + ] + }, + 'append': { + 'name': 'append', + 'params': [ + { + 'name': 'array', + 'description': 'Array to append
\n', + 'type': 'Array' + }, + { + 'name': 'value', + 'description': 'to be added to the Array
\n', + 'type': 'Any' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'arrayCopy': { + 'name': 'arrayCopy', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'src', + 'description': 'the source Array
\n', + 'type': 'Array' + }, + { + 'name': 'srcPosition', + 'description': 'starting position in the source Array
\n', + 'type': 'Integer' + }, + { + 'name': 'dst', + 'description': 'the destination Array
\n', + 'type': 'Array' + }, + { + 'name': 'dstPosition', + 'description': 'starting position in the destination Array
\n', + 'type': 'Integer' + }, + { + 'name': 'length', + 'description': 'number of Array elements to be copied
\n', + 'type': 'Integer' + } + ] + }, + { + 'params': [ + { + 'name': 'src', + 'description': '', + 'type': 'Array' + }, + { + 'name': 'dst', + 'description': '', + 'type': 'Array' + }, + { + 'name': 'length', + 'description': '', + 'type': 'Integer', + 'optional': true + } + ] + } + ] + }, + 'concat': { + 'name': 'concat', + 'params': [ + { + 'name': 'a', + 'description': 'first Array to concatenate
\n', + 'type': 'Array' + }, + { + 'name': 'b', + 'description': 'second Array to concatenate
\n', + 'type': 'Array' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'reverse': { + 'name': 'reverse', + 'params': [ + { + 'name': 'list', + 'description': 'Array to reverse
\n', + 'type': 'Array' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'shorten': { + 'name': 'shorten', + 'params': [ + { + 'name': 'list', + 'description': 'Array to shorten
\n', + 'type': 'Array' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'shuffle': { + 'name': 'shuffle', + 'params': [ + { + 'name': 'array', + 'description': 'array to shuffle.
\n', + 'type': 'Array' + }, + { + 'name': 'bool', + 'description': 'if true
, shuffle the original array in place. Defaults to false
.
Array to sort
\n', + 'type': 'Array' + }, + { + 'name': 'count', + 'description': 'number of elements to sort, starting from 0
\n', + 'type': 'Integer', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'splice': { + 'name': 'splice', + 'params': [ + { + 'name': 'list', + 'description': 'Array to splice into
\n', + 'type': 'Array' + }, + { + 'name': 'value', + 'description': 'value to be spliced in
\n', + 'type': 'Any' + }, + { + 'name': 'position', + 'description': 'in the array from which to insert data
\n', + 'type': 'Integer' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'subset': { + 'name': 'subset', + 'params': [ + { + 'name': 'list', + 'description': 'Array to extract from
\n', + 'type': 'Array' + }, + { + 'name': 'start', + 'description': 'position to begin
\n', + 'type': 'Integer' + }, + { + 'name': 'count', + 'description': 'number of values to extract
\n', + 'type': 'Integer', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'float': { + 'name': 'float', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'str', + 'description': 'string to convert.
\n', + 'type': 'String' + } + ] + }, + { + 'params': [ + { + 'name': 'ns', + 'description': 'array of strings to convert.
\n', + 'type': 'String[]' + } + ] + } + ] + }, + 'int': { + 'name': 'int', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'n', + 'description': 'value to convert.
\n', + 'type': 'String|Boolean|Number' + } + ] + }, + { + 'params': [ + { + 'name': 'ns', + 'description': 'values to convert.
\n', + 'type': 'Array' + } + ] + } + ] + }, + 'str': { + 'name': 'str', + 'params': [ + { + 'name': 'n', + 'description': 'value to convert.
\n', + 'type': 'String|Boolean|Number' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'boolean': { + 'name': 'boolean', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'n', + 'description': 'value to convert.
\n', + 'type': 'String|Boolean|Number' + } + ] + }, + { + 'params': [ + { + 'name': 'ns', + 'description': 'values to convert.
\n', + 'type': 'Array' + } + ] + } + ] + }, + 'byte': { + 'name': 'byte', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'n', + 'description': 'value to convert.
\n', + 'type': 'String|Boolean|Number' + } + ] + }, + { + 'params': [ + { + 'name': 'ns', + 'description': 'values to convert.
\n', + 'type': 'Array' + } + ] + } + ] + }, + 'char': { + 'name': 'char', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'n', + 'description': 'value to convert.
\n', + 'type': 'String|Number' + } + ] + }, + { + 'params': [ + { + 'name': 'ns', + 'description': 'values to convert.
\n', + 'type': 'Array' + } + ] + } + ] + }, + 'unchar': { + 'name': 'unchar', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'n', + 'description': 'value to convert.
\n', + 'type': 'String' + } + ] + }, + { + 'params': [ + { + 'name': 'ns', + 'description': 'values to convert.
\n', + 'type': 'String[]' + } + ] + } + ] + }, + 'hex': { + 'name': 'hex', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'n', + 'description': 'value to convert.
\n', + 'type': 'Number' + }, + { + 'name': 'digits', + 'description': 'number of digits to include.
\n', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'ns', + 'description': 'values to convert.
\n', + 'type': 'Number[]' + }, + { + 'name': 'digits', + 'description': '', + 'type': 'Number', + 'optional': true + } + ] + } + ] + }, + 'unhex': { + 'name': 'unhex', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'n', + 'description': 'value to convert.
\n', + 'type': 'String' + } + ] + }, + { + 'params': [ + { + 'name': 'ns', + 'description': 'values to convert.
\n', + 'type': 'String[]' + } + ] + } + ] + }, + 'join': { + 'name': 'join', + 'params': [ + { + 'name': 'list', + 'description': 'array of strings to combine.
\n', + 'type': 'Array' + }, + { + 'name': 'separator', + 'description': 'character(s) to place between strings when they\'re combined.
\n', + 'type': 'String' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'match': { + 'name': 'match', + 'params': [ + { + 'name': 'str', + 'description': 'string to search.
\n', + 'type': 'String' + }, + { + 'name': 'regexp', + 'description': 'regular expression to match.
\n', + 'type': 'String' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'matchAll': { + 'name': 'matchAll', + 'params': [ + { + 'name': 'str', + 'description': 'string to search.
\n', + 'type': 'String' + }, + { + 'name': 'regexp', + 'description': 'regular expression to match.
\n', + 'type': 'String' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'nf': { + 'name': 'nf', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'num', + 'description': 'number to format.
\n', + 'type': 'Number|String' + }, + { + 'name': 'left', + 'description': 'number of digits to include to the left of\n the decimal point.
\n', + 'type': 'Integer|String', + 'optional': true + }, + { + 'name': 'right', + 'description': 'number of digits to include to the right\n of the decimal point.
\n', + 'type': 'Integer|String', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'nums', + 'description': 'numbers to format.
\n', + 'type': 'Number[]' + }, + { + 'name': 'left', + 'description': '', + 'type': 'Integer|String', + 'optional': true + }, + { + 'name': 'right', + 'description': '', + 'type': 'Integer|String', + 'optional': true + } + ] + } + ] + }, + 'nfc': { + 'name': 'nfc', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'num', + 'description': 'number to format.
\n', + 'type': 'Number|String' + }, + { + 'name': 'right', + 'description': 'number of digits to include to the right\n of the decimal point.
\n', + 'type': 'Integer|String', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'nums', + 'description': 'numbers to format.
\n', + 'type': 'Number[]' + }, + { + 'name': 'right', + 'description': '', + 'type': 'Integer|String', + 'optional': true + } + ] + } + ] + }, + 'nfp': { + 'name': 'nfp', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'num', + 'description': 'number to format.
\n', + 'type': 'Number' + }, + { + 'name': 'left', + 'description': 'number of digits to include to the left of the\n decimal point.
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'right', + 'description': 'number of digits to include to the right of the\n decimal point.
\n', + 'type': 'Integer', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'nums', + 'description': 'numbers to format.
\n', + 'type': 'Number[]' + }, + { + 'name': 'left', + 'description': '', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'right', + 'description': '', + 'type': 'Integer', + 'optional': true + } + ] + } + ] + }, + 'nfs': { + 'name': 'nfs', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'num', + 'description': 'number to format.
\n', + 'type': 'Number' + }, + { + 'name': 'left', + 'description': 'number of digits to include to the left of the\n decimal point.
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'right', + 'description': 'number of digits to include to the right of the\n decimal point.
\n', + 'type': 'Integer', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'nums', + 'description': 'numbers to format.
\n', + 'type': 'Array' + }, + { + 'name': 'left', + 'description': '', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'right', + 'description': '', + 'type': 'Integer', + 'optional': true + } + ] + } + ] + }, + 'split': { + 'name': 'split', + 'params': [ + { + 'name': 'value', + 'description': 'the String to be split
\n', + 'type': 'String' + }, + { + 'name': 'delim', + 'description': 'the String used to separate the data
\n', + 'type': 'String' + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'splitTokens': { + 'name': 'splitTokens', + 'params': [ + { + 'name': 'value', + 'description': 'string to split.
\n', + 'type': 'String' + }, + { + 'name': 'delim', + 'description': 'character(s) to use for splitting the string.
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Data' + }, + 'trim': { + 'name': 'trim', + 'class': 'p5', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'str', + 'description': 'string to trim.
\n', + 'type': 'String' + } + ] + }, + { + 'params': [ + { + 'name': 'strs', + 'description': 'strings to trim.
\n', + 'type': 'String[]' + } + ] + } + ] + }, + 'day': { + 'name': 'day', + 'class': 'p5', + 'module': 'IO' + }, + 'hour': { + 'name': 'hour', + 'class': 'p5', + 'module': 'IO' + }, + 'minute': { + 'name': 'minute', + 'class': 'p5', + 'module': 'IO' + }, + 'millis': { + 'name': 'millis', + 'class': 'p5', + 'module': 'IO' + }, + 'month': { + 'name': 'month', + 'class': 'p5', + 'module': 'IO' + }, + 'second': { + 'name': 'second', + 'class': 'p5', + 'module': 'IO' + }, + 'year': { + 'name': 'year', + 'class': 'p5', + 'module': 'IO' + }, + 'beginGeometry': { + 'name': 'beginGeometry', + 'class': 'p5', + 'module': 'Shape' + }, + 'endGeometry': { + 'name': 'endGeometry', + 'class': 'p5', + 'module': 'Shape' + }, + 'buildGeometry': { + 'name': 'buildGeometry', + 'params': [ + { + 'name': 'callback', + 'description': 'function that draws the shape.
\n', + 'type': 'Function' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'freeGeometry': { + 'name': 'freeGeometry', + 'params': [ + { + 'name': 'geometry', + 'description': '3D shape whose resources should be freed.
\n', + 'type': 'p5.Geometry' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'plane': { + 'name': 'plane', + 'params': [ + { + 'name': 'width', + 'description': 'width of the plane.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'height', + 'description': 'height of the plane.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'detailX', + 'description': 'number of triangle subdivisions along the x-axis.
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'detailY', + 'description': 'number of triangle subdivisions along the y-axis.
\n', + 'type': 'Integer', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'box': { + 'name': 'box', + 'params': [ + { + 'name': 'width', + 'description': 'width of the box.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'height', + 'description': 'height of the box.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'depth', + 'description': 'depth of the box.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'detailX', + 'description': 'number of triangle subdivisions along the x-axis.
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'detailY', + 'description': 'number of triangle subdivisions along the y-axis.
\n', + 'type': 'Integer', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'sphere': { + 'name': 'sphere', + 'params': [ + { + 'name': 'radius', + 'description': 'radius of the sphere. Defaults to 50.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'detailX', + 'description': 'number of triangle subdivisions along the x-axis. Defaults to 24.
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'detailY', + 'description': 'number of triangle subdivisions along the y-axis. Defaults to 16.
\n', + 'type': 'Integer', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'cylinder': { + 'name': 'cylinder', + 'params': [ + { + 'name': 'radius', + 'description': 'radius of the cylinder. Defaults to 50.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'height', + 'description': 'height of the cylinder. Defaults to the value of radius
.
number of edges along the top and bottom. Defaults to 24.
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'detailY', + 'description': 'number of triangle subdivisions along the y-axis. Defaults to 1.
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'bottomCap', + 'description': 'whether to draw the cylinder\'s bottom. Defaults to true
.
whether to draw the cylinder\'s top. Defaults to true
.
radius of the cone\'s base. Defaults to 50.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'height', + 'description': 'height of the cone. Defaults to the value of radius
.
number of edges used to draw the base. Defaults to 24.
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'detailY', + 'description': 'number of triangle subdivisions along the y-axis. Defaults to 1.
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'cap', + 'description': 'whether to draw the cone\'s base. Defaults to true
.
radius of the ellipsoid along the x-axis. Defaults to 50.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'radiusY', + 'description': 'radius of the ellipsoid along the y-axis. Defaults to radiusX
.
radius of the ellipsoid along the z-axis. Defaults to radiusY
.
number of triangle subdivisions along the x-axis. Defaults to 24.
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'detailY', + 'description': 'number of triangle subdivisions along the y-axis. Defaults to 16.
\n', + 'type': 'Integer', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'torus': { + 'name': 'torus', + 'params': [ + { + 'name': 'radius', + 'description': 'radius of the torus. Defaults to 50.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'tubeRadius', + 'description': 'radius of the tube. Defaults to 10.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'detailX', + 'description': 'number of edges that form the hole. Defaults to 24.
\n', + 'type': 'Integer', + 'optional': true + }, + { + 'name': 'detailY', + 'description': 'number of triangle subdivisions along the y-axis. Defaults to 16.
\n', + 'type': 'Integer', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'orbitControl': { + 'name': 'orbitControl', + 'params': [ + { + 'name': 'sensitivityX', + 'description': 'sensitivity to movement along the x-axis. Defaults to 1.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'sensitivityY', + 'description': 'sensitivity to movement along the y-axis. Defaults to 1.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'sensitivityZ', + 'description': 'sensitivity to movement along the z-axis. Defaults to 1.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'options', + 'description': 'object with two optional properties, disableTouchActions
\n and freeRotation
. Both are Boolean
s. disableTouchActions
\n defaults to true
and freeRotation
defaults to false
.
either GRID or AXES
\n', + 'type': 'Constant' + } + ] + }, + { + 'params': [ + { + 'name': 'mode', + 'description': '', + 'type': 'Constant' + }, + { + 'name': 'gridSize', + 'description': 'side length of the grid.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'gridDivisions', + 'description': 'number of divisions in the grid.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'xOff', + 'description': 'offset from origin along the x-axis.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'yOff', + 'description': 'offset from origin along the y-axis.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'zOff', + 'description': 'offset from origin along the z-axis.
\n', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'mode', + 'description': '', + 'type': 'Constant' + }, + { + 'name': 'axesSize', + 'description': 'length of axes icon markers.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'xOff', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'yOff', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'zOff', + 'description': '', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'gridSize', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'gridDivisions', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'gridXOff', + 'description': 'grid offset from the origin along the x-axis.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'gridYOff', + 'description': 'grid offset from the origin along the y-axis.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'gridZOff', + 'description': 'grid offset from the origin along the z-axis.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'axesSize', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'axesXOff', + 'description': 'axes icon offset from the origin along the x-axis.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'axesYOff', + 'description': 'axes icon offset from the origin along the y-axis.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'axesZOff', + 'description': 'axes icon offset from the origin along the z-axis.
\n', + 'type': 'Number', + 'optional': true + } + ] + } + ] + }, + 'noDebugMode': { + 'name': 'noDebugMode', + 'class': 'p5', + 'module': '3D' + }, + 'ambientLight': { + 'name': 'ambientLight', + 'class': 'p5', + 'module': '3D', + 'overloads': [ + { + 'params': [ + { + 'name': 'v1', + 'description': 'red or hue value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green or saturation value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue, brightness, or lightness value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': 'alpha (transparency) value in the current\n colorMode().
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'gray', + 'description': 'grayscale value between 0 and 255.
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': '', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'value', + 'description': 'color as a CSS string.
\n', + 'type': 'String' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'values', + 'description': 'color as an array of RGBA, HSBA, or HSLA\n values.
\n', + 'type': 'Number[]' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'color', + 'description': 'color as a p5.Color object.
\n', + 'type': 'p5.Color' + } + ], + 'chainable': 1 + } + ] + }, + 'specularColor': { + 'name': 'specularColor', + 'class': 'p5', + 'module': '3D', + 'overloads': [ + { + 'params': [ + { + 'name': 'v1', + 'description': 'red or hue value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green or saturation value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue, brightness, or lightness value in the current\n colorMode().
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'gray', + 'description': 'grayscale value between 0 and 255.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'value', + 'description': 'color as a CSS string.
\n', + 'type': 'String' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'values', + 'description': 'color as an array of RGBA, HSBA, or HSLA\n values.
\n', + 'type': 'Number[]' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'color', + 'description': 'color as a p5.Color object.
\n', + 'type': 'p5.Color' + } + ], + 'chainable': 1 + } + ] + }, + 'directionalLight': { + 'name': 'directionalLight', + 'class': 'p5', + 'module': '3D', + 'overloads': [ + { + 'params': [ + { + 'name': 'v1', + 'description': 'red or hue value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green or saturation value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue, brightness, or lightness value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'x', + 'description': 'x-component of the light\'s direction between -1 and 1.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-component of the light\'s direction between -1 and 1.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'z-component of the light\'s direction between -1 and 1.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'direction', + 'description': 'direction of the light as a\n p5.Vector object.
\n', + 'type': 'p5.Vector' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'color', + 'description': 'color as a p5.Color object,\n an array of color values, or as a CSS string.
\n', + 'type': 'p5.Color|Number[]|String' + }, + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': '', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'color', + 'description': '', + 'type': 'p5.Color|Number[]|String' + }, + { + 'name': 'direction', + 'description': '', + 'type': 'p5.Vector' + } + ], + 'chainable': 1 + } + ] + }, + 'pointLight': { + 'name': 'pointLight', + 'class': 'p5', + 'module': '3D', + 'overloads': [ + { + 'params': [ + { + 'name': 'v1', + 'description': 'red or hue value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green or saturation value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue, brightness, or lightness value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'x', + 'description': 'x-coordinate of the light.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the light.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'z-coordinate of the light.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'position', + 'description': 'position of the light as a\n p5.Vector object.
\n', + 'type': 'p5.Vector' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'color', + 'description': 'color as a p5.Color object,\n an array of color values, or a CSS string.
\n', + 'type': 'p5.Color|Number[]|String' + }, + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': '', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'color', + 'description': '', + 'type': 'p5.Color|Number[]|String' + }, + { + 'name': 'position', + 'description': '', + 'type': 'p5.Vector' + } + ], + 'chainable': 1 + } + ] + }, + 'imageLight': { + 'name': 'imageLight', + 'params': [ + { + 'name': 'img', + 'description': 'image to use as the light source.
\n', + 'type': 'p5.image' + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'panorama': { + 'name': 'panorama', + 'params': [ + { + 'name': 'img', + 'description': '360˚ image to use as the background.
\n', + 'type': 'p5.Image' + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'lights': { + 'name': 'lights', + 'class': 'p5', + 'module': '3D' + }, + 'lightFalloff': { + 'name': 'lightFalloff', + 'params': [ + { + 'name': 'constant', + 'description': 'constant value for calculating falloff.
\n', + 'type': 'Number' + }, + { + 'name': 'linear', + 'description': 'linear value for calculating falloff.
\n', + 'type': 'Number' + }, + { + 'name': 'quadratic', + 'description': 'quadratic value for calculating falloff.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'spotLight': { + 'name': 'spotLight', + 'class': 'p5', + 'module': '3D', + 'overloads': [ + { + 'params': [ + { + 'name': 'v1', + 'description': 'red or hue value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green or saturation value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue, brightness, or lightness value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'x', + 'description': 'x-coordinate of the light.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the light.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'z-coordinate of the light.
\n', + 'type': 'Number' + }, + { + 'name': 'rx', + 'description': 'x-component of light direction between -1 and 1.
\n', + 'type': 'Number' + }, + { + 'name': 'ry', + 'description': 'y-component of light direction between -1 and 1.
\n', + 'type': 'Number' + }, + { + 'name': 'rz', + 'description': 'z-component of light direction between -1 and 1.
\n', + 'type': 'Number' + }, + { + 'name': 'angle', + 'description': 'angle of the light cone. Defaults to PI / 3
.
concentration of the light. Defaults to 100.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'color', + 'description': 'color as a p5.Color object,\n an array of color values, or a CSS string.
\n', + 'type': 'p5.Color|Number[]|String' + }, + { + 'name': 'position', + 'description': 'position of the light as a p5.Vector object.
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'direction', + 'description': 'direction of light as a p5.Vector object.
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'angle', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'concentration', + 'description': '', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'v1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'position', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'direction', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'angle', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'concentration', + 'description': '', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'color', + 'description': '', + 'type': 'p5.Color|Number[]|String' + }, + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'direction', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'angle', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'concentration', + 'description': '', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'color', + 'description': '', + 'type': 'p5.Color|Number[]|String' + }, + { + 'name': 'position', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'rx', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'ry', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'rz', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'angle', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'concentration', + 'description': '', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'v1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'direction', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'angle', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'concentration', + 'description': '', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'v1', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'position', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'rx', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'ry', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'rz', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'angle', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'concentration', + 'description': '', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'color', + 'description': '', + 'type': 'p5.Color|Number[]|String' + }, + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'rx', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'ry', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'rz', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'angle', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'concentration', + 'description': '', + 'type': 'Number', + 'optional': true + } + ] + } + ] + }, + 'noLights': { + 'name': 'noLights', + 'class': 'p5', + 'module': '3D' + }, + 'loadModel': { + 'name': 'loadModel', + 'class': 'p5', + 'module': 'Shape', + 'overloads': [ + { + 'params': [ + { + 'name': 'path', + 'description': 'path of the model to be loaded.
\n', + 'type': 'String' + }, + { + 'name': 'normalize', + 'description': 'if true
, scale the model to fit the canvas.
function to call once the model is loaded. Will be passed\n the p5.Geometry object.
\n', + 'type': 'function(p5.Geometry)', + 'optional': true + }, + { + 'name': 'failureCallback', + 'description': 'function to call if the model fails to load. Will be passed an Error
event object.
model’s file extension. Either \'.obj\'
or \'.stl\'
.
loading options.
\n', + 'type': 'Object', + 'optional': true, + 'props': [ + { + 'name': 'successCallback', + 'description': '', + 'type': 'function(p5.Geometry)', + 'optional': true + }, + { + 'name': 'failureCallback', + 'description': '', + 'type': 'Function(Event)', + 'optional': true + }, + { + 'name': 'fileType', + 'description': '', + 'type': 'String', + 'optional': true + }, + { + 'name': 'normalize', + 'description': '', + 'type': 'Boolean', + 'optional': true + }, + { + 'name': 'flipU', + 'description': '', + 'type': 'Boolean', + 'optional': true + }, + { + 'name': 'flipV', + 'description': '', + 'type': 'Boolean', + 'optional': true + } + ] + } + ] + } + ] + }, + 'model': { + 'name': 'model', + 'params': [ + { + 'name': 'model', + 'description': '3D shape to be drawn.
\n', + 'type': 'p5.Geometry' + } + ], + 'class': 'p5', + 'module': 'Shape' + }, + 'loadShader': { + 'name': 'loadShader', + 'params': [ + { + 'name': 'vertFilename', + 'description': 'path of the vertex shader to be loaded.
\n', + 'type': 'String' + }, + { + 'name': 'fragFilename', + 'description': 'path of the fragment shader to be loaded.
\n', + 'type': 'String' + }, + { + 'name': 'successCallback', + 'description': 'function to call once the shader is loaded. Can be passed the\n p5.Shader object.
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'failureCallback', + 'description': 'function to call if the shader fails to load. Can be passed an\n Error
event object.
source code for the vertex shader.
\n', + 'type': 'String' + }, + { + 'name': 'fragSrc', + 'description': 'source code for the fragment shader.
\n', + 'type': 'String' + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'createFilterShader': { + 'name': 'createFilterShader', + 'params': [ + { + 'name': 'fragSrc', + 'description': 'source code for the fragment shader.
\n', + 'type': 'String' + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'shader': { + 'name': 'shader', + 'params': [ + { + 'name': 's', + 'description': 'p5.Shader object\n to apply.
\n', + 'type': 'p5.Shader' + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'resetShader': { + 'name': 'resetShader', + 'class': 'p5', + 'module': '3D' + }, + 'texture': { + 'name': 'texture', + 'params': [ + { + 'name': 'tex', + 'description': 'media to use as the texture.
\n', + 'type': 'p5.Image|p5.MediaElement|p5.Graphics|p5.Texture|p5.Framebuffer|p5.FramebufferTexture' + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'textureMode': { + 'name': 'textureMode', + 'params': [ + { + 'name': 'mode', + 'description': 'either IMAGE or NORMAL.
\n', + 'type': 'Constant' + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'textureWrap': { + 'name': 'textureWrap', + 'params': [ + { + 'name': 'wrapX', + 'description': 'either CLAMP, REPEAT, or MIRROR
\n', + 'type': 'Constant' + }, + { + 'name': 'wrapY', + 'description': 'either CLAMP, REPEAT, or MIRROR
\n', + 'type': 'Constant', + 'optional': true + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'normalMaterial': { + 'name': 'normalMaterial', + 'class': 'p5', + 'module': '3D' + }, + 'ambientMaterial': { + 'name': 'ambientMaterial', + 'class': 'p5', + 'module': '3D', + 'overloads': [ + { + 'params': [ + { + 'name': 'v1', + 'description': 'red or hue value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green or saturation value in the\n current colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue, brightness, or lightness value in the\n current colorMode().
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'gray', + 'description': 'grayscale value between 0 (black) and 255 (white).
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'color', + 'description': 'color as a p5.Color object,\n an array of color values, or a CSS string.
\n', + 'type': 'p5.Color|Number[]|String' + } + ], + 'chainable': 1 + } + ] + }, + 'emissiveMaterial': { + 'name': 'emissiveMaterial', + 'class': 'p5', + 'module': '3D', + 'overloads': [ + { + 'params': [ + { + 'name': 'v1', + 'description': 'red or hue value in the current\n colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green or saturation value in the\n current colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue, brightness, or lightness value in the\n current colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': 'alpha value in the current\n colorMode().
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'gray', + 'description': 'grayscale value between 0 (black) and 255 (white).
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'color', + 'description': 'color as a p5.Color object,\n an array of color values, or a CSS string.
\n', + 'type': 'p5.Color|Number[]|String' + } + ], + 'chainable': 1 + } + ] + }, + 'specularMaterial': { + 'name': 'specularMaterial', + 'class': 'p5', + 'module': '3D', + 'overloads': [ + { + 'params': [ + { + 'name': 'gray', + 'description': 'grayscale value between 0 (black) and 255 (white).
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': 'alpha value in the current current\n colorMode().
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v1', + 'description': 'red or hue value in\n the current colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'green or saturation value\n in the current colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'v3', + 'description': 'blue, brightness, or lightness value\n in the current colorMode().
\n', + 'type': 'Number' + }, + { + 'name': 'alpha', + 'description': '', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'color', + 'description': 'color as a p5.Color object,\n an array of color values, or a CSS string.
\n', + 'type': 'p5.Color|Number[]|String' + } + ], + 'chainable': 1 + } + ] + }, + 'shininess': { + 'name': 'shininess', + 'params': [ + { + 'name': 'shine', + 'description': 'amount of shine.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'metalness': { + 'name': 'metalness', + 'params': [ + { + 'name': 'metallic', + 'description': 'amount of metalness.
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'camera': { + 'name': 'camera', + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the camera. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'y', + 'description': 'y-coordinate of the camera. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'z', + 'description': 'z-coordinate of the camera. Defaults to 800.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'centerX', + 'description': 'x-coordinate of the point the camera faces. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'centerY', + 'description': 'y-coordinate of the point the camera faces. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'centerZ', + 'description': 'z-coordinate of the point the camera faces. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'upX', + 'description': 'x-component of the camera’s "up" vector. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'upY', + 'description': 'y-component of the camera’s "up" vector. Defaults to 1.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'upZ', + 'description': 'z-component of the camera’s "up" vector. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'perspective': { + 'name': 'perspective', + 'params': [ + { + 'name': 'fovy', + 'description': 'camera frustum vertical field of view. Defaults to\n 2 * atan(height / 2 / 800)
.
camera frustum aspect ratio. Defaults to\n width / height
.
distance from the camera to the near clipping plane.\n Defaults to 0.1 * 800
.
distance from the camera to the far clipping plane.\n Defaults to 10 * 800
.
whether to enable line perspective.
\n', + 'type': 'Boolean' + } + ] + }, + { + 'params': [ + ] + } + ] + }, + 'ortho': { + 'name': 'ortho', + 'params': [ + { + 'name': 'left', + 'description': 'x-coordinate of the frustum’s left plane. Defaults to -width / 2
.
x-coordinate of the frustum’s right plane. Defaults to width / 2
.
y-coordinate of the frustum’s bottom plane. Defaults to height / 2
.
y-coordinate of the frustum’s top plane. Defaults to -height / 2
.
z-coordinate of the frustum’s near plane. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'far', + 'description': 'z-coordinate of the frustum’s far plane. Defaults to max(width, height) + 800
.
x-coordinate of the frustum’s left plane. Defaults to -width / 20
.
x-coordinate of the frustum’s right plane. Defaults to width / 20
.
y-coordinate of the frustum’s bottom plane. Defaults to height / 20
.
y-coordinate of the frustum’s top plane. Defaults to -height / 20
.
z-coordinate of the frustum’s near plane. Defaults to 0.1 * 800
.
z-coordinate of the frustum’s far plane. Defaults to 10 * 800
.
camera that should be made active.
\n', + 'type': 'p5.Camera' + } + ], + 'class': 'p5', + 'module': '3D' + }, + 'setAttributes': { + 'name': 'setAttributes', + 'class': 'p5', + 'module': 'Rendering', + 'overloads': [ + { + 'params': [ + { + 'name': 'key', + 'description': 'Name of attribute
\n', + 'type': 'String' + }, + { + 'name': 'value', + 'description': 'New value of named attribute
\n', + 'type': 'Boolean' + } + ] + }, + { + 'params': [ + { + 'name': 'obj', + 'description': 'object with key-value pairs
\n', + 'type': 'Object' + } + ] + } + ] + }, + 'getAudioContext': { + 'name': 'getAudioContext', + 'class': 'p5', + 'module': 'p5.sound' + }, + 'userStartAudio': { + 'params': [ + { + 'name': 'elements', + 'description': 'This argument can be an Element,\n Selector String, NodeList, p5.Element,\n jQuery Element, or an Array of any of those.
\n', + 'type': 'Element|Array', + 'optional': true + }, + { + 'name': 'callback', + 'description': 'Callback to invoke when the AudioContext\n has started
\n', + 'type': 'Function', + 'optional': true + } + ], + 'name': 'userStartAudio', + 'class': 'p5', + 'module': 'p5.sound' + }, + 'getOutputVolume': { + 'name': 'getOutputVolume', + 'class': 'p5', + 'module': 'p5.sound' + }, + 'outputVolume': { + 'name': 'outputVolume', + 'params': [ + { + 'name': 'volume', + 'description': 'Volume (amplitude) between 0.0\n and 1.0 or modulating signal/oscillator
\n', + 'type': 'Number|Object' + }, + { + 'name': 'rampTime', + 'description': 'Fade for t seconds
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'timeFromNow', + 'description': 'Schedule this event to happen at\n t seconds in the future
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'p5.sound' + }, + 'soundOut': { + 'name': 'soundOut', + 'class': 'p5', + 'module': 'p5.sound' + }, + 'sampleRate': { + 'name': 'sampleRate', + 'class': 'p5', + 'module': 'p5.sound' + }, + 'freqToMidi': { + 'name': 'freqToMidi', + 'params': [ + { + 'name': 'frequency', + 'description': 'A freqeuncy, for example, the "A"\n above Middle C is 440Hz
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'p5.sound' + }, + 'midiToFreq': { + 'name': 'midiToFreq', + 'params': [ + { + 'name': 'midiNote', + 'description': 'The number of a MIDI note
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'p5.sound' + }, + 'soundFormats': { + 'name': 'soundFormats', + 'params': [ + { + 'name': 'formats', + 'description': 'i.e. \'mp3\', \'wav\', \'ogg\'
\n', + 'type': 'String', + 'optional': true, + 'multiple': true + } + ], + 'class': 'p5', + 'module': 'p5.sound' + }, + 'saveSound': { + 'name': 'saveSound', + 'params': [ + { + 'name': 'soundFile', + 'description': 'p5.SoundFile that you wish to save
\n', + 'type': 'p5.SoundFile' + }, + { + 'name': 'fileName', + 'description': 'name of the resulting .wav file.
\n', + 'type': 'String' + } + ], + 'class': 'p5', + 'module': 'p5.sound' + }, + 'loadSound': { + 'name': 'loadSound', + 'params': [ + { + 'name': 'path', + 'description': 'Path to the sound file, or an array with\n paths to soundfiles in multiple formats\n i.e. [\'sound.ogg\', \'sound.mp3\'].\n Alternately, accepts an object: either\n from the HTML5 File API, or a p5.File.
\n', + 'type': 'String|Array' + }, + { + 'name': 'successCallback', + 'description': 'Name of a function to call once file loads
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': 'Name of a function to call if there is\n an error loading the file.
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'whileLoading', + 'description': 'Name of a function to call while file is loading.\n This function will receive the percentage loaded\n so far, from 0.0 to 1.0.
\n', + 'type': 'Function', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'p5.sound' + }, + 'createConvolver': { + 'name': 'createConvolver', + 'params': [ + { + 'name': 'path', + 'description': 'path to a sound file
\n', + 'type': 'String' + }, + { + 'name': 'callback', + 'description': 'function to call if loading is successful.\n The object will be passed in as the argument\n to the callback function.
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': 'function to call if loading is not successful.\n A custom error will be passed in as the argument\n to the callback function.
\n', + 'type': 'Function', + 'optional': true + } + ], + 'class': 'p5', + 'module': 'p5.sound' + }, + 'setBPM': { + 'name': 'setBPM', + 'params': [ + { + 'name': 'BPM', + 'description': 'Beats Per Minute
\n', + 'type': 'Number' + }, + { + 'name': 'rampTime', + 'description': 'Seconds from now
\n', + 'type': 'Number' + } + ], + 'class': 'p5', + 'module': 'p5.sound' + } + }, + 'p5.Color': { + 'toString': { + 'name': 'toString', + 'params': [ + { + 'name': 'format', + 'description': 'how the color string will be formatted.\nLeaving this empty formats the string as rgba(r, g, b, a).\n\'#rgb\' \'#rgba\' \'#rrggbb\' and \'#rrggbbaa\' format as hexadecimal color codes.\n\'rgb\' \'hsb\' and \'hsl\' return the color formatted in the specified color mode.\n\'rgba\' \'hsba\' and \'hsla\' are the same as above but with alpha channels.\n\'rgb%\' \'hsb%\' \'hsl%\' \'rgba%\' \'hsba%\' and \'hsla%\' format as percentages.
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5.Color', + 'module': 'Color' + }, + 'setRed': { + 'name': 'setRed', + 'params': [ + { + 'name': 'red', + 'description': 'the new red value.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Color', + 'module': 'Color' + }, + 'setGreen': { + 'name': 'setGreen', + 'params': [ + { + 'name': 'green', + 'description': 'the new green value.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Color', + 'module': 'Color' + }, + 'setBlue': { + 'name': 'setBlue', + 'params': [ + { + 'name': 'blue', + 'description': 'the new blue value.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Color', + 'module': 'Color' + }, + 'setAlpha': { + 'name': 'setAlpha', + 'params': [ + { + 'name': 'alpha', + 'description': 'the new alpha value.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Color', + 'module': 'Color' + } + }, + 'p5.Element': { + 'elt': { + 'name': 'elt', + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'width': { + 'name': 'width', + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'height': { + 'name': 'height', + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'parent': { + 'name': 'parent', + 'class': 'p5.Element', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + { + 'name': 'parent', + 'description': 'ID, p5.Element,\n or HTMLElement of desired parent element.
\n', + 'type': 'String|p5.Element|Object' + } + ], + 'chainable': 1 + }, + { + 'params': [ + ] + } + ] + }, + 'id': { + 'name': 'id', + 'class': 'p5.Element', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + { + 'name': 'id', + 'description': 'ID of the element.
\n', + 'type': 'String' + } + ], + 'chainable': 1 + }, + { + 'params': [ + ] + } + ] + }, + 'class': { + 'name': 'class', + 'class': 'p5.Element', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + { + 'name': 'class', + 'description': 'class to add.
\n', + 'type': 'String' + } + ], + 'chainable': 1 + }, + { + 'params': [ + ] + } + ] + }, + 'mousePressed': { + 'name': 'mousePressed', + 'params': [ + { + 'name': 'fxn', + 'description': 'function to call when the mouse is\n pressed over the element.\n false
disables the function.
function to call when the mouse is\n double clicked over the element.\n false
disables the function.
function to call when the mouse wheel is\n scrolled over the element.\n false
disables the function.
function to call when the mouse is\n pressed over the element.\n false
disables the function.
function to call when the mouse is\n pressed and released over the element.\n false
disables the function.
function to call when the mouse\n moves over the element.\n false
disables the function.
function to call when the mouse\n moves onto the element.\n false
disables the function.
function to call when the mouse\n moves off the element.\n false
disables the function.
function to call when the touch\n starts.\n false
disables the function.
function to call when the touch\n moves over the element.\n false
disables the function.
function to call when the touch\n ends.\n false
disables the function.
function to call when the file is\n dragged over the element.\n false
disables the function.
function to call when the file is\n dragged off the element.\n false
disables the function.
name of class to add.
\n', + 'type': 'String' + } + ], + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'removeClass': { + 'name': 'removeClass', + 'params': [ + { + 'name': 'class', + 'description': 'name of class to remove.
\n', + 'type': 'String' + } + ], + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'hasClass': { + 'name': 'hasClass', + 'params': [ + { + 'name': 'c', + 'description': 'name of class to check.
\n', + 'type': 'String' + } + ], + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'toggleClass': { + 'name': 'toggleClass', + 'params': [ + { + 'name': 'c', + 'description': 'class name to toggle.
\n', + 'type': 'String' + } + ], + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'child': { + 'name': 'child', + 'class': 'p5.Element', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'child', + 'description': 'the ID, DOM node, or p5.Element\n to add to the current element
\n', + 'type': 'String|p5.Element', + 'optional': true + } + ], + 'chainable': 1 + } + ] + }, + 'center': { + 'name': 'center', + 'params': [ + { + 'name': 'align', + 'description': 'passing \'vertical\', \'horizontal\' aligns element accordingly
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'html': { + 'name': 'html', + 'class': 'p5.Element', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'html', + 'description': 'the HTML to be placed inside the element
\n', + 'type': 'String', + 'optional': true + }, + { + 'name': 'append', + 'description': 'whether to append HTML to existing
\n', + 'type': 'Boolean', + 'optional': true + } + ], + 'chainable': 1 + } + ] + }, + 'position': { + 'name': 'position', + 'class': 'p5.Element', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'x', + 'description': 'x-position relative to top-left of window (optional)
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'y', + 'description': 'y-position relative to top-left of window (optional)
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'positionType', + 'description': 'it can be static, fixed, relative, sticky, initial or inherit (optional)
\n', + 'type': 'String', + 'optional': true + } + ], + 'chainable': 1 + } + ] + }, + 'style': { + 'name': 'style', + 'class': 'p5.Element', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + { + 'name': 'property', + 'description': 'style property to set.
\n', + 'type': 'String' + } + ] + }, + { + 'params': [ + { + 'name': 'property', + 'description': '', + 'type': 'String' + }, + { + 'name': 'value', + 'description': 'value to assign to the property.
\n', + 'type': 'String|p5.Color' + } + ], + 'chainable': 1 + } + ] + }, + 'attribute': { + 'name': 'attribute', + 'class': 'p5.Element', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'attr', + 'description': 'attribute to set.
\n', + 'type': 'String' + }, + { + 'name': 'value', + 'description': 'value to assign to the attribute.
\n', + 'type': 'String' + } + ], + 'chainable': 1 + } + ] + }, + 'removeAttribute': { + 'name': 'removeAttribute', + 'params': [ + { + 'name': 'attr', + 'description': 'attribute to remove.
\n', + 'type': 'String' + } + ], + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'value': { + 'name': 'value', + 'class': 'p5.Element', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'value', + 'description': '', + 'type': 'String|Number' + } + ], + 'chainable': 1 + } + ] + }, + 'show': { + 'name': 'show', + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'hide': { + 'name': 'hide', + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'size': { + 'name': 'size', + 'class': 'p5.Element', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'w', + 'description': 'width of the element, either AUTO, or a number.
\n', + 'type': 'Number|Constant', + 'optional': true + }, + { + 'name': 'h', + 'description': 'height of the element, either AUTO, or a number.
\n', + 'type': 'Number|Constant', + 'optional': true + } + ], + 'chainable': 1 + } + ] + }, + 'remove': { + 'name': 'remove', + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'drop': { + 'name': 'drop', + 'params': [ + { + 'name': 'callback', + 'description': 'called when a file loads. Called once for each file dropped.
\n', + 'type': 'Function' + }, + { + 'name': 'fxn', + 'description': 'called once when any files are dropped.
\n', + 'type': 'Function', + 'optional': true + } + ], + 'class': 'p5.Element', + 'module': 'DOM' + }, + 'draggable': { + 'name': 'draggable', + 'params': [ + { + 'name': 'elmnt', + 'description': 'another p5.Element.
\n', + 'type': 'p5.Element', + 'optional': true + } + ], + 'class': 'p5.Element', + 'module': 'DOM' + } + }, + 'p5.Graphics': { + 'reset': { + 'name': 'reset', + 'class': 'p5.Graphics', + 'module': 'Rendering' + }, + 'remove': { + 'name': 'remove', + 'class': 'p5.Graphics', + 'module': 'Rendering' + }, + 'createFramebuffer': { + 'name': 'createFramebuffer', + 'params': [ + { + 'name': 'options', + 'description': 'configuration options.
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5.Graphics', + 'module': 'Rendering' + } + }, + 'p5.TypedDict': { + 'size': { + 'name': 'size', + 'class': 'p5.TypedDict', + 'module': 'Data' + }, + 'hasKey': { + 'name': 'hasKey', + 'params': [ + { + 'name': 'key', + 'description': 'that you want to look up
\n', + 'type': 'Number|String' + } + ], + 'class': 'p5.TypedDict', + 'module': 'Data' + }, + 'get': { + 'name': 'get', + 'params': [ + { + 'name': 'the', + 'description': 'key you want to access
\n', + 'type': 'Number|String' + } + ], + 'class': 'p5.TypedDict', + 'module': 'Data' + }, + 'set': { + 'name': 'set', + 'params': [ + { + 'name': 'key', + 'description': '', + 'type': 'Number|String' + }, + { + 'name': 'value', + 'description': '', + 'type': 'Number|String' + } + ], + 'class': 'p5.TypedDict', + 'module': 'Data' + }, + 'create': { + 'name': 'create', + 'class': 'p5.TypedDict', + 'module': 'Data', + 'overloads': [ + { + 'params': [ + { + 'name': 'key', + 'description': '', + 'type': 'Number|String' + }, + { + 'name': 'value', + 'description': '', + 'type': 'Number|String' + } + ] + }, + { + 'params': [ + { + 'name': 'obj', + 'description': 'key/value pair
\n', + 'type': 'Object' + } + ] + } + ] + }, + 'clear': { + 'name': 'clear', + 'class': 'p5.TypedDict', + 'module': 'Data' + }, + 'remove': { + 'name': 'remove', + 'params': [ + { + 'name': 'key', + 'description': 'for the pair to remove
\n', + 'type': 'Number|String' + } + ], + 'class': 'p5.TypedDict', + 'module': 'Data' + }, + 'print': { + 'name': 'print', + 'class': 'p5.TypedDict', + 'module': 'Data' + }, + 'saveTable': { + 'name': 'saveTable', + 'class': 'p5.TypedDict', + 'module': 'Data' + }, + 'saveJSON': { + 'name': 'saveJSON', + 'class': 'p5.TypedDict', + 'module': 'Data' + } + }, + 'p5.NumberDict': { + 'add': { + 'name': 'add', + 'params': [ + { + 'name': 'Key', + 'description': 'for the value you wish to add to
\n', + 'type': 'Number' + }, + { + 'name': 'Number', + 'description': 'to add to the value
\n', + 'type': 'Number' + } + ], + 'class': 'p5.NumberDict', + 'module': 'Data' + }, + 'sub': { + 'name': 'sub', + 'params': [ + { + 'name': 'Key', + 'description': 'for the value you wish to subtract from
\n', + 'type': 'Number' + }, + { + 'name': 'Number', + 'description': 'to subtract from the value
\n', + 'type': 'Number' + } + ], + 'class': 'p5.NumberDict', + 'module': 'Data' + }, + 'mult': { + 'name': 'mult', + 'params': [ + { + 'name': 'Key', + 'description': 'for value you wish to multiply
\n', + 'type': 'Number' + }, + { + 'name': 'Amount', + 'description': 'to multiply the value by
\n', + 'type': 'Number' + } + ], + 'class': 'p5.NumberDict', + 'module': 'Data' + }, + 'div': { + 'name': 'div', + 'params': [ + { + 'name': 'Key', + 'description': 'for value you wish to divide
\n', + 'type': 'Number' + }, + { + 'name': 'Amount', + 'description': 'to divide the value by
\n', + 'type': 'Number' + } + ], + 'class': 'p5.NumberDict', + 'module': 'Data' + }, + 'minValue': { + 'name': 'minValue', + 'class': 'p5.NumberDict', + 'module': 'Data' + }, + 'maxValue': { + 'name': 'maxValue', + 'class': 'p5.NumberDict', + 'module': 'Data' + }, + 'minKey': { + 'name': 'minKey', + 'class': 'p5.NumberDict', + 'module': 'Data' + }, + 'maxKey': { + 'name': 'maxKey', + 'class': 'p5.NumberDict', + 'module': 'Data' + } + }, + 'p5.MediaElement': { + 'src': { + 'name': 'src', + 'class': 'p5.MediaElement', + 'module': 'DOM' + }, + 'play': { + 'name': 'play', + 'class': 'p5.MediaElement', + 'module': 'DOM' + }, + 'stop': { + 'name': 'stop', + 'class': 'p5.MediaElement', + 'module': 'DOM' + }, + 'pause': { + 'name': 'pause', + 'class': 'p5.MediaElement', + 'module': 'DOM' + }, + 'loop': { + 'name': 'loop', + 'class': 'p5.MediaElement', + 'module': 'DOM' + }, + 'noLoop': { + 'name': 'noLoop', + 'class': 'p5.MediaElement', + 'module': 'DOM' + }, + 'autoplay': { + 'name': 'autoplay', + 'params': [ + { + 'name': 'shouldAutoplay', + 'description': 'whether the element should autoplay.
\n', + 'type': 'Boolean', + 'optional': true + } + ], + 'class': 'p5.MediaElement', + 'module': 'DOM' + }, + 'volume': { + 'name': 'volume', + 'class': 'p5.MediaElement', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'val', + 'description': 'volume between 0.0 and 1.0.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + } + ] + }, + 'speed': { + 'name': 'speed', + 'class': 'p5.MediaElement', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'speed', + 'description': 'speed multiplier for playback.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + } + ] + }, + 'time': { + 'name': 'time', + 'class': 'p5.MediaElement', + 'module': 'DOM', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'time', + 'description': 'time to jump to (in seconds).
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + } + ] + }, + 'duration': { + 'name': 'duration', + 'class': 'p5.MediaElement', + 'module': 'DOM' + }, + 'onended': { + 'name': 'onended', + 'params': [ + { + 'name': 'callback', + 'description': 'function to call when playback ends.\n The p5.MediaElement
is passed as\n the argument.
AudioNode from the Web Audio API,\nor an object from the p5.sound library
\n', + 'type': 'AudioNode|Object' + } + ], + 'class': 'p5.MediaElement', + 'module': 'DOM' + }, + 'disconnect': { + 'name': 'disconnect', + 'class': 'p5.MediaElement', + 'module': 'DOM' + }, + 'showControls': { + 'name': 'showControls', + 'class': 'p5.MediaElement', + 'module': 'DOM' + }, + 'hideControls': { + 'name': 'hideControls', + 'class': 'p5.MediaElement', + 'module': 'DOM' + }, + 'addCue': { + 'name': 'addCue', + 'params': [ + { + 'name': 'time', + 'description': 'cue time to run the callback function.
\n', + 'type': 'Number' + }, + { + 'name': 'callback', + 'description': 'function to call at the cue time.
\n', + 'type': 'Function' + }, + { + 'name': 'value', + 'description': 'object to pass as the argument to\n callback
.
ID of the cue, created by media.addCue()
.
A scaling factor for the number of pixels per\nside
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Image', + 'module': 'Image' + }, + 'loadPixels': { + 'name': 'loadPixels', + 'class': 'p5.Image', + 'module': 'Image' + }, + 'updatePixels': { + 'name': 'updatePixels', + 'class': 'p5.Image', + 'module': 'Image', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the upper-left corner\n of the subsection to update.
\n', + 'type': 'Integer' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the upper-left corner\n of the subsection to update.
\n', + 'type': 'Integer' + }, + { + 'name': 'w', + 'description': 'width of the subsection to update.
\n', + 'type': 'Integer' + }, + { + 'name': 'h', + 'description': 'height of the subsection to update.
\n', + 'type': 'Integer' + } + ] + }, + { + 'params': [ + ] + } + ] + }, + 'get': { + 'name': 'get', + 'class': 'p5.Image', + 'module': 'Image', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the pixel.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the pixel.
\n', + 'type': 'Number' + }, + { + 'name': 'w', + 'description': 'width of the subsection to be returned.
\n', + 'type': 'Number' + }, + { + 'name': 'h', + 'description': 'height of the subsection to be returned.
\n', + 'type': 'Number' + } + ] + }, + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + } + ] + } + ] + }, + 'set': { + 'name': 'set', + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the pixel.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the pixel.
\n', + 'type': 'Number' + }, + { + 'name': 'a', + 'description': 'grayscale value | pixel array |\n p5.Color object |\n p5.Image to copy.
\n', + 'type': 'Number|Number[]|Object' + } + ], + 'class': 'p5.Image', + 'module': 'Image' + }, + 'resize': { + 'name': 'resize', + 'params': [ + { + 'name': 'width', + 'description': 'resized image width.
\n', + 'type': 'Number' + }, + { + 'name': 'height', + 'description': 'resized image height.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Image', + 'module': 'Image' + }, + 'copy': { + 'name': 'copy', + 'class': 'p5.Image', + 'module': 'Image', + 'overloads': [ + { + 'params': [ + { + 'name': 'srcImage', + 'description': 'source image.
\n', + 'type': 'p5.Image|p5.Element' + }, + { + 'name': 'sx', + 'description': 'x-coordinate of the source\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'sy', + 'description': 'y-coordinate of the source\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'sw', + 'description': 'source image width.
\n', + 'type': 'Integer' + }, + { + 'name': 'sh', + 'description': 'source image height.
\n', + 'type': 'Integer' + }, + { + 'name': 'dx', + 'description': 'x-coordinate of the destination\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'dy', + 'description': 'y-coordinate of the destination\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'dw', + 'description': 'destination image width.
\n', + 'type': 'Integer' + }, + { + 'name': 'dh', + 'description': 'destination image height.
\n', + 'type': 'Integer' + } + ] + }, + { + 'params': [ + { + 'name': 'sx', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'sy', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'sw', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'sh', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dx', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dy', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dw', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dh', + 'description': '', + 'type': 'Integer' + } + ] + } + ] + }, + 'mask': { + 'name': 'mask', + 'params': [ + { + 'name': 'srcImage', + 'description': 'source image.
\n', + 'type': 'p5.Image' + } + ], + 'class': 'p5.Image', + 'module': 'Image' + }, + 'filter': { + 'name': 'filter', + 'params': [ + { + 'name': 'filterType', + 'description': 'either THRESHOLD, GRAY, OPAQUE, INVERT,\n POSTERIZE, ERODE, DILATE or BLUR.
\n', + 'type': 'Constant' + }, + { + 'name': 'filterParam', + 'description': 'parameter unique to each filter.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Image', + 'module': 'Image' + }, + 'blend': { + 'name': 'blend', + 'class': 'p5.Image', + 'module': 'Image', + 'overloads': [ + { + 'params': [ + { + 'name': 'srcImage', + 'description': 'source image
\n', + 'type': 'p5.Image' + }, + { + 'name': 'sx', + 'description': 'x-coordinate of the source\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'sy', + 'description': 'y-coordinate of the source\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'sw', + 'description': 'source image width.
\n', + 'type': 'Integer' + }, + { + 'name': 'sh', + 'description': 'source image height.
\n', + 'type': 'Integer' + }, + { + 'name': 'dx', + 'description': 'x-coordinate of the destination\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'dy', + 'description': 'y-coordinate of the destination\'s upper-left corner.
\n', + 'type': 'Integer' + }, + { + 'name': 'dw', + 'description': 'destination image width.
\n', + 'type': 'Integer' + }, + { + 'name': 'dh', + 'description': 'destination image height.
\n', + 'type': 'Integer' + }, + { + 'name': 'blendMode', + 'description': 'the blend mode. either\n BLEND, DARKEST, LIGHTEST, DIFFERENCE,\n MULTIPLY, EXCLUSION, SCREEN, REPLACE, OVERLAY, HARD_LIGHT,\n SOFT_LIGHT, DODGE, BURN, ADD or NORMAL.
\nAvailable blend modes are: normal | multiply | screen | overlay |\n darken | lighten | color-dodge | color-burn | hard-light |\n soft-light | difference | exclusion | hue | saturation |\n color | luminosity
\nhttp://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/
\n', + 'type': 'Constant' + } + ] + }, + { + 'params': [ + { + 'name': 'sx', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'sy', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'sw', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'sh', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dx', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dy', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dw', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'dh', + 'description': '', + 'type': 'Integer' + }, + { + 'name': 'blendMode', + 'description': '', + 'type': 'Constant' + } + ] + } + ] + }, + 'save': { + 'name': 'save', + 'params': [ + { + 'name': 'filename', + 'description': 'filename. Defaults to \'untitled\'.
\n', + 'type': 'String' + }, + { + 'name': 'extension', + 'description': 'file extension, either \'png\' or \'jpg\'.\n Defaults to \'png\'.
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5.Image', + 'module': 'Image' + }, + 'reset': { + 'name': 'reset', + 'class': 'p5.Image', + 'module': 'Image' + }, + 'getCurrentFrame': { + 'name': 'getCurrentFrame', + 'class': 'p5.Image', + 'module': 'Image' + }, + 'setFrame': { + 'name': 'setFrame', + 'params': [ + { + 'name': 'index', + 'description': 'index of the frame to display.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Image', + 'module': 'Image' + }, + 'numFrames': { + 'name': 'numFrames', + 'class': 'p5.Image', + 'module': 'Image' + }, + 'play': { + 'name': 'play', + 'class': 'p5.Image', + 'module': 'Image' + }, + 'pause': { + 'name': 'pause', + 'class': 'p5.Image', + 'module': 'Image' + }, + 'delay': { + 'name': 'delay', + 'params': [ + { + 'name': 'd', + 'description': 'delay in milliseconds between switching frames.
\n', + 'type': 'Number' + }, + { + 'name': 'index', + 'description': 'index of the frame that will have its delay modified.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Image', + 'module': 'Image' + } + }, + 'p5.PrintWriter': { + 'write': { + 'name': 'write', + 'params': [ + { + 'name': 'data', + 'description': 'data to be written as a string, number,\n or array of strings and numbers.
\n', + 'type': 'String|Number|Array' + } + ], + 'class': 'p5.PrintWriter', + 'module': 'IO' + }, + 'print': { + 'name': 'print', + 'params': [ + { + 'name': 'data', + 'description': 'data to be written as a string, number,\n or array of strings and numbers.
\n', + 'type': 'String|Number|Array' + } + ], + 'class': 'p5.PrintWriter', + 'module': 'IO' + }, + 'clear': { + 'name': 'clear', + 'class': 'p5.PrintWriter', + 'module': 'IO' + }, + 'close': { + 'name': 'close', + 'class': 'p5.PrintWriter', + 'module': 'IO' + } + }, + 'p5.Table': { + 'columns': { + 'name': 'columns', + 'class': 'p5.Table', + 'module': 'IO' + }, + 'rows': { + 'name': 'rows', + 'class': 'p5.Table', + 'module': 'IO' + }, + 'addRow': { + 'name': 'addRow', + 'params': [ + { + 'name': 'row', + 'description': 'row to be added to the table
\n', + 'type': 'p5.TableRow', + 'optional': true + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'removeRow': { + 'name': 'removeRow', + 'params': [ + { + 'name': 'id', + 'description': 'ID number of the row to remove
\n', + 'type': 'Integer' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'getRow': { + 'name': 'getRow', + 'params': [ + { + 'name': 'rowID', + 'description': 'ID number of the row to get
\n', + 'type': 'Integer' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'getRows': { + 'name': 'getRows', + 'class': 'p5.Table', + 'module': 'IO' + }, + 'findRow': { + 'name': 'findRow', + 'params': [ + { + 'name': 'value', + 'description': 'The value to match
\n', + 'type': 'String' + }, + { + 'name': 'column', + 'description': 'ID number or title of the\n column to search
\n', + 'type': 'Integer|String' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'findRows': { + 'name': 'findRows', + 'params': [ + { + 'name': 'value', + 'description': 'The value to match
\n', + 'type': 'String' + }, + { + 'name': 'column', + 'description': 'ID number or title of the\n column to search
\n', + 'type': 'Integer|String' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'matchRow': { + 'name': 'matchRow', + 'params': [ + { + 'name': 'regexp', + 'description': 'The regular expression to match
\n', + 'type': 'String|RegExp' + }, + { + 'name': 'column', + 'description': 'The column ID (number) or\n title (string)
\n', + 'type': 'String|Integer' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'matchRows': { + 'name': 'matchRows', + 'params': [ + { + 'name': 'regexp', + 'description': 'The regular expression to match
\n', + 'type': 'String' + }, + { + 'name': 'column', + 'description': 'The column ID (number) or\n title (string)
\n', + 'type': 'String|Integer', + 'optional': true + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'getColumn': { + 'name': 'getColumn', + 'params': [ + { + 'name': 'column', + 'description': 'String or Number of the column to return
\n', + 'type': 'String|Number' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'clearRows': { + 'name': 'clearRows', + 'class': 'p5.Table', + 'module': 'IO' + }, + 'addColumn': { + 'name': 'addColumn', + 'params': [ + { + 'name': 'title', + 'description': 'title of the given column
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'getColumnCount': { + 'name': 'getColumnCount', + 'class': 'p5.Table', + 'module': 'IO' + }, + 'getRowCount': { + 'name': 'getRowCount', + 'class': 'p5.Table', + 'module': 'IO' + }, + 'removeTokens': { + 'name': 'removeTokens', + 'params': [ + { + 'name': 'chars', + 'description': 'String listing characters to be removed
\n', + 'type': 'String' + }, + { + 'name': 'column', + 'description': 'Column ID (number)\n or name (string)
\n', + 'type': 'String|Integer', + 'optional': true + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'trim': { + 'name': 'trim', + 'params': [ + { + 'name': 'column', + 'description': 'Column ID (number)\n or name (string)
\n', + 'type': 'String|Integer', + 'optional': true + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'removeColumn': { + 'name': 'removeColumn', + 'params': [ + { + 'name': 'column', + 'description': 'columnName (string) or ID (number)
\n', + 'type': 'String|Integer' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'set': { + 'name': 'set', + 'params': [ + { + 'name': 'row', + 'description': 'row ID
\n', + 'type': 'Integer' + }, + { + 'name': 'column', + 'description': 'column ID (Number)\n or title (String)
\n', + 'type': 'String|Integer' + }, + { + 'name': 'value', + 'description': 'value to assign
\n', + 'type': 'String|Number' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'setNum': { + 'name': 'setNum', + 'params': [ + { + 'name': 'row', + 'description': 'row ID
\n', + 'type': 'Integer' + }, + { + 'name': 'column', + 'description': 'column ID (Number)\n or title (String)
\n', + 'type': 'String|Integer' + }, + { + 'name': 'value', + 'description': 'value to assign
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'setString': { + 'name': 'setString', + 'params': [ + { + 'name': 'row', + 'description': 'row ID
\n', + 'type': 'Integer' + }, + { + 'name': 'column', + 'description': 'column ID (Number)\n or title (String)
\n', + 'type': 'String|Integer' + }, + { + 'name': 'value', + 'description': 'value to assign
\n', + 'type': 'String' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'get': { + 'name': 'get', + 'params': [ + { + 'name': 'row', + 'description': 'row ID
\n', + 'type': 'Integer' + }, + { + 'name': 'column', + 'description': 'columnName (string) or\n ID (number)
\n', + 'type': 'String|Integer' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'getNum': { + 'name': 'getNum', + 'params': [ + { + 'name': 'row', + 'description': 'row ID
\n', + 'type': 'Integer' + }, + { + 'name': 'column', + 'description': 'columnName (string) or\n ID (number)
\n', + 'type': 'String|Integer' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'getString': { + 'name': 'getString', + 'params': [ + { + 'name': 'row', + 'description': 'row ID
\n', + 'type': 'Integer' + }, + { + 'name': 'column', + 'description': 'columnName (string) or\n ID (number)
\n', + 'type': 'String|Integer' + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'getObject': { + 'name': 'getObject', + 'params': [ + { + 'name': 'headerColumn', + 'description': 'Name of the column which should be used to\n title each row object (optional)
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5.Table', + 'module': 'IO' + }, + 'getArray': { + 'name': 'getArray', + 'class': 'p5.Table', + 'module': 'IO' + } + }, + 'p5.TableRow': { + 'set': { + 'name': 'set', + 'params': [ + { + 'name': 'column', + 'description': 'Column ID (Number)\n or Title (String)
\n', + 'type': 'String|Integer' + }, + { + 'name': 'value', + 'description': 'The value to be stored
\n', + 'type': 'String|Number' + } + ], + 'class': 'p5.TableRow', + 'module': 'IO' + }, + 'setNum': { + 'name': 'setNum', + 'params': [ + { + 'name': 'column', + 'description': 'Column ID (Number)\n or Title (String)
\n', + 'type': 'String|Integer' + }, + { + 'name': 'value', + 'description': 'The value to be stored\n as a Float
\n', + 'type': 'Number|String' + } + ], + 'class': 'p5.TableRow', + 'module': 'IO' + }, + 'setString': { + 'name': 'setString', + 'params': [ + { + 'name': 'column', + 'description': 'Column ID (Number)\n or Title (String)
\n', + 'type': 'String|Integer' + }, + { + 'name': 'value', + 'description': 'The value to be stored\n as a String
\n', + 'type': 'String|Number|Boolean|Object' + } + ], + 'class': 'p5.TableRow', + 'module': 'IO' + }, + 'get': { + 'name': 'get', + 'params': [ + { + 'name': 'column', + 'description': 'columnName (string) or\n ID (number)
\n', + 'type': 'String|Integer' + } + ], + 'class': 'p5.TableRow', + 'module': 'IO' + }, + 'getNum': { + 'name': 'getNum', + 'params': [ + { + 'name': 'column', + 'description': 'columnName (string) or\n ID (number)
\n', + 'type': 'String|Integer' + } + ], + 'class': 'p5.TableRow', + 'module': 'IO' + }, + 'getString': { + 'name': 'getString', + 'params': [ + { + 'name': 'column', + 'description': 'columnName (string) or\n ID (number)
\n', + 'type': 'String|Integer' + } + ], + 'class': 'p5.TableRow', + 'module': 'IO' + } + }, + 'p5.XML': { + 'getParent': { + 'name': 'getParent', + 'class': 'p5.XML', + 'module': 'IO' + }, + 'getName': { + 'name': 'getName', + 'class': 'p5.XML', + 'module': 'IO' + }, + 'setName': { + 'name': 'setName', + 'params': [ + { + 'name': 'name', + 'description': 'new tag name of the element.
\n', + 'type': 'String' + } + ], + 'class': 'p5.XML', + 'module': 'IO' + }, + 'hasChildren': { + 'name': 'hasChildren', + 'class': 'p5.XML', + 'module': 'IO' + }, + 'listChildren': { + 'name': 'listChildren', + 'class': 'p5.XML', + 'module': 'IO' + }, + 'getChildren': { + 'name': 'getChildren', + 'params': [ + { + 'name': 'name', + 'description': 'name of the elements to return.
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5.XML', + 'module': 'IO' + }, + 'getChild': { + 'name': 'getChild', + 'params': [ + { + 'name': 'name', + 'description': 'element name or index.
\n', + 'type': 'String|Integer' + } + ], + 'class': 'p5.XML', + 'module': 'IO' + }, + 'addChild': { + 'name': 'addChild', + 'params': [ + { + 'name': 'child', + 'description': 'child element to add.
\n', + 'type': 'p5.XML' + } + ], + 'class': 'p5.XML', + 'module': 'IO' + }, + 'removeChild': { + 'name': 'removeChild', + 'params': [ + { + 'name': 'name', + 'description': 'name or index of the child element to remove.
\n', + 'type': 'String|Integer' + } + ], + 'class': 'p5.XML', + 'module': 'IO' + }, + 'getAttributeCount': { + 'name': 'getAttributeCount', + 'class': 'p5.XML', + 'module': 'IO' + }, + 'listAttributes': { + 'name': 'listAttributes', + 'class': 'p5.XML', + 'module': 'IO' + }, + 'hasAttribute': { + 'name': 'hasAttribute', + 'params': [ + { + 'name': 'name', + 'description': 'name of the attribute to be checked.
\n', + 'type': 'String' + } + ], + 'class': 'p5.XML', + 'module': 'IO' + }, + 'getNum': { + 'name': 'getNum', + 'params': [ + { + 'name': 'name', + 'description': 'name of the attribute to be checked.
\n', + 'type': 'String' + }, + { + 'name': 'defaultValue', + 'description': 'value to return if the attribute doesn\'t exist.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.XML', + 'module': 'IO' + }, + 'getString': { + 'name': 'getString', + 'params': [ + { + 'name': 'name', + 'description': 'name of the attribute to be checked.
\n', + 'type': 'String' + }, + { + 'name': 'defaultValue', + 'description': 'value to return if the attribute doesn\'t exist.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.XML', + 'module': 'IO' + }, + 'setAttribute': { + 'name': 'setAttribute', + 'params': [ + { + 'name': 'name', + 'description': 'name of the attribute to be set.
\n', + 'type': 'String' + }, + { + 'name': 'value', + 'description': 'attribute\'s new value.
\n', + 'type': 'Number|String|Boolean' + } + ], + 'class': 'p5.XML', + 'module': 'IO' + }, + 'getContent': { + 'name': 'getContent', + 'params': [ + { + 'name': 'defaultValue', + 'description': 'value to return if the element has no\n content.
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5.XML', + 'module': 'IO' + }, + 'setContent': { + 'name': 'setContent', + 'params': [ + { + 'name': 'content', + 'description': 'new content for the element.
\n', + 'type': 'String' + } + ], + 'class': 'p5.XML', + 'module': 'IO' + }, + 'serialize': { + 'name': 'serialize', + 'class': 'p5.XML', + 'module': 'IO' + } + }, + 'p5.Vector': { + 'x': { + 'name': 'x', + 'class': 'p5.Vector', + 'module': 'Math' + }, + 'y': { + 'name': 'y', + 'class': 'p5.Vector', + 'module': 'Math' + }, + 'z': { + 'name': 'z', + 'class': 'p5.Vector', + 'module': 'Math' + }, + 'toString': { + 'name': 'toString', + 'class': 'p5.Vector', + 'module': 'Math' + }, + 'set': { + 'name': 'set', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x component of the vector.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'y', + 'description': 'y component of the vector.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'z', + 'description': 'z component of the vector.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'value', + 'description': 'vector to set.
\n', + 'type': 'p5.Vector|Number[]' + } + ], + 'chainable': 1 + } + ] + }, + 'copy': { + 'name': 'copy', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'v', + 'description': 'the p5.Vector to create a copy of
\n', + 'type': 'p5.Vector' + } + ], + 'static': 1 + } + ] + }, + 'add': { + 'name': 'add', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x component of the vector to be added.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y component of the vector to be added.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'z', + 'description': 'z component of the vector to be added.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'value', + 'description': 'The vector to add
\n', + 'type': 'p5.Vector|Number[]' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v1', + 'description': 'A p5.Vector to add
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'v2', + 'description': 'A p5.Vector to add
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'target', + 'description': 'vector to receive the result.
\n', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + } + ] + }, + 'rem': { + 'name': 'rem', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x component of divisor vector.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y component of divisor vector.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'z component of divisor vector.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'value', + 'description': 'divisor vector.
\n', + 'type': 'p5.Vector | Number[]' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v1', + 'description': 'The dividend p5.Vector
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'v2', + 'description': 'The divisor p5.Vector
\n', + 'type': 'p5.Vector' + } + ], + 'static': 1 + }, + { + 'params': [ + { + 'name': 'v1', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'v2', + 'description': '', + 'type': 'p5.Vector' + } + ], + 'static': 1 + } + ] + }, + 'sub': { + 'name': 'sub', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x component of the vector to subtract.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y component of the vector to subtract.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'z', + 'description': 'z component of the vector to subtract.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'value', + 'description': 'the vector to subtract
\n', + 'type': 'p5.Vector|Number[]' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v1', + 'description': 'A p5.Vector to subtract from
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'v2', + 'description': 'A p5.Vector to subtract
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'target', + 'description': 'vector to receive the result.
\n', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + } + ] + }, + 'mult': { + 'name': 'mult', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'n', + 'description': 'The number to multiply with the vector
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x', + 'description': 'number to multiply with the x component of the vector.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'number to multiply with the y component of the vector.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'number to multiply with the z component of the vector.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'arr', + 'description': 'array to multiply with the components of the vector.
\n', + 'type': 'Number[]' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v', + 'description': 'vector to multiply with the components of the original vector.
\n', + 'type': 'p5.Vector' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': '', + 'type': 'Number', + 'optional': true + } + ], + 'static': 1 + }, + { + 'params': [ + { + 'name': 'v', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'n', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'target', + 'description': 'vector to receive the result.
\n', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + }, + { + 'params': [ + { + 'name': 'v0', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'v1', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'target', + 'description': '', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + }, + { + 'params': [ + { + 'name': 'v0', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'arr', + 'description': '', + 'type': 'Number[]' + }, + { + 'name': 'target', + 'description': '', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + } + ] + }, + 'div': { + 'name': 'div', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'n', + 'description': 'The number to divide the vector by
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x', + 'description': 'number to divide with the x component of the vector.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'number to divide with the y component of the vector.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'number to divide with the z component of the vector.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'arr', + 'description': 'array to divide the components of the vector by.
\n', + 'type': 'Number[]' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v', + 'description': 'vector to divide the components of the original vector by.
\n', + 'type': 'p5.Vector' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': '', + 'type': 'Number', + 'optional': true + } + ], + 'static': 1 + }, + { + 'params': [ + { + 'name': 'v', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'n', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'target', + 'description': 'The vector to receive the result
\n', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + }, + { + 'params': [ + { + 'name': 'v0', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'v1', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'target', + 'description': '', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + }, + { + 'params': [ + { + 'name': 'v0', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'arr', + 'description': '', + 'type': 'Number[]' + }, + { + 'name': 'target', + 'description': '', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + } + ] + }, + 'mag': { + 'name': 'mag', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'vecT', + 'description': 'The vector to return the magnitude of
\n', + 'type': 'p5.Vector' + } + ], + 'static': 1 + } + ] + }, + 'magSq': { + 'name': 'magSq', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'vecT', + 'description': 'the vector to return the squared magnitude of
\n', + 'type': 'p5.Vector' + } + ], + 'static': 1 + } + ] + }, + 'dot': { + 'name': 'dot', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x component of the vector.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y component of the vector.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'z', + 'description': 'z component of the vector.
\n', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'v', + 'description': 'p5.Vector to be dotted.
\n', + 'type': 'p5.Vector' + } + ] + }, + { + 'params': [ + { + 'name': 'v1', + 'description': 'first p5.Vector.
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'v2', + 'description': 'second p5.Vector.
\n', + 'type': 'p5.Vector' + } + ], + 'static': 1 + } + ] + }, + 'cross': { + 'name': 'cross', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'v', + 'description': 'p5.Vector to be crossed.
\n', + 'type': 'p5.Vector' + } + ] + }, + { + 'params': [ + { + 'name': 'v1', + 'description': 'first p5.Vector.
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'v2', + 'description': 'second p5.Vector.
\n', + 'type': 'p5.Vector' + } + ], + 'static': 1 + } + ] + }, + 'dist': { + 'name': 'dist', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'v', + 'description': 'x, y, and z coordinates of a p5.Vector.
\n', + 'type': 'p5.Vector' + } + ] + }, + { + 'params': [ + { + 'name': 'v1', + 'description': 'The first p5.Vector
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'v2', + 'description': 'The second p5.Vector
\n', + 'type': 'p5.Vector' + } + ], + 'static': 1 + } + ] + }, + 'normalize': { + 'name': 'normalize', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'v', + 'description': 'The vector to normalize
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'target', + 'description': 'The vector to receive the result
\n', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + } + ] + }, + 'limit': { + 'name': 'limit', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'max', + 'description': 'maximum magnitude for the vector.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v', + 'description': 'the vector to limit
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'max', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'target', + 'description': 'the vector to receive the result (Optional)
\n', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + } + ] + }, + 'setMag': { + 'name': 'setMag', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'len', + 'description': 'new length for this vector.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v', + 'description': 'the vector to set the magnitude of
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'len', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'target', + 'description': 'the vector to receive the result (Optional)
\n', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + } + ] + }, + 'heading': { + 'name': 'heading', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'v', + 'description': 'the vector to find the angle of
\n', + 'type': 'p5.Vector' + } + ], + 'static': 1 + } + ] + }, + 'setHeading': { + 'name': 'setHeading', + 'params': [ + { + 'name': 'angle', + 'description': 'angle of rotation.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Vector', + 'module': 'Math' + }, + 'rotate': { + 'name': 'rotate', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'angle', + 'description': 'angle of rotation.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'angle', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'target', + 'description': 'The vector to receive the result
\n', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + } + ] + }, + 'angleBetween': { + 'name': 'angleBetween', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'value', + 'description': 'x, y, and z components of a p5.Vector.
\n', + 'type': 'p5.Vector' + } + ] + }, + { + 'params': [ + { + 'name': 'v1', + 'description': 'the first vector.
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'v2', + 'description': 'the second vector.
\n', + 'type': 'p5.Vector' + } + ], + 'static': 1 + } + ] + }, + 'lerp': { + 'name': 'lerp', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x component.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y component.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'z component.
\n', + 'type': 'Number' + }, + { + 'name': 'amt', + 'description': 'amount of interpolation between 0.0 (old vector)\n and 1.0 (new vector). 0.5 is halfway between.
\n', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v', + 'description': 'p5.Vector to lerp toward.
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'amt', + 'description': '', + 'type': 'Number' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'v1', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'v2', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'amt', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'target', + 'description': 'The vector to receive the result
\n', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + } + ] + }, + 'slerp': { + 'name': 'slerp', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'v', + 'description': 'p5.Vector to slerp toward.
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'amt', + 'description': 'amount of interpolation between 0.0 (old vector)\n and 1.0 (new vector). 0.5 is halfway between.
\n', + 'type': 'Number' + } + ] + }, + { + 'params': [ + { + 'name': 'v1', + 'description': 'old vector.
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'v2', + 'description': 'new vector.
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'amt', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'target', + 'description': 'vector to receive the result.
\n', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + } + ] + }, + 'reflect': { + 'name': 'reflect', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'surfaceNormal', + 'description': 'p5.Vector\n to reflect about.
\n', + 'type': 'p5.Vector' + } + ], + 'chainable': 1 + }, + { + 'params': [ + { + 'name': 'incidentVector', + 'description': 'vector to be reflected.
\n', + 'type': 'p5.Vector' + }, + { + 'name': 'surfaceNormal', + 'description': '', + 'type': 'p5.Vector' + }, + { + 'name': 'target', + 'description': 'vector to receive the result.
\n', + 'type': 'p5.Vector', + 'optional': true + } + ], + 'static': 1 + } + ] + }, + 'array': { + 'name': 'array', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'v', + 'description': 'the vector to convert to an array
\n', + 'type': 'p5.Vector' + } + ], + 'static': 1 + } + ] + }, + 'equals': { + 'name': 'equals', + 'class': 'p5.Vector', + 'module': 'Math', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x component of the vector.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'y', + 'description': 'y component of the vector.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'z', + 'description': 'z component of the vector.
\n', + 'type': 'Number', + 'optional': true + } + ] + }, + { + 'params': [ + { + 'name': 'value', + 'description': 'vector to compare.
\n', + 'type': 'p5.Vector|Array' + } + ] + }, + { + 'params': [ + { + 'name': 'v1', + 'description': 'the first vector to compare
\n', + 'type': 'p5.Vector|Array' + }, + { + 'name': 'v2', + 'description': 'the second vector to compare
\n', + 'type': 'p5.Vector|Array' + } + ], + 'static': 1 + } + ] + }, + 'fromAngle': { + 'name': 'fromAngle', + 'params': [ + { + 'name': 'angle', + 'description': 'desired angle, in radians. Unaffected by angleMode().
\n', + 'type': 'Number' + }, + { + 'name': 'length', + 'description': 'length of the new vector (defaults to 1).
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Vector', + 'module': 'Math' + }, + 'fromAngles': { + 'name': 'fromAngles', + 'params': [ + { + 'name': 'theta', + 'description': 'polar angle in radians (zero is up).
\n', + 'type': 'Number' + }, + { + 'name': 'phi', + 'description': 'azimuthal angle in radians\n (zero is out of the screen).
\n', + 'type': 'Number' + }, + { + 'name': 'length', + 'description': 'length of the new vector (defaults to 1).
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Vector', + 'module': 'Math' + }, + 'random2D': { + 'name': 'random2D', + 'class': 'p5.Vector', + 'module': 'Math' + }, + 'random3D': { + 'name': 'random3D', + 'class': 'p5.Vector', + 'module': 'Math' + } + }, + 'p5.Font': { + 'font': { + 'name': 'font', + 'class': 'p5.Font', + 'module': 'Typography' + }, + 'textBounds': { + 'name': 'textBounds', + 'params': [ + { + 'name': 'str', + 'description': 'string of text.
\n', + 'type': 'String' + }, + { + 'name': 'x', + 'description': 'x-coordinate of the text.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the text.
\n', + 'type': 'Number' + }, + { + 'name': 'fontSize', + 'description': 'font size. Defaults to the current\n textSize().
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Font', + 'module': 'Typography' + }, + 'textToPoints': { + 'name': 'textToPoints', + 'params': [ + { + 'name': 'str', + 'description': 'string of text.
\n', + 'type': 'String' + }, + { + 'name': 'x', + 'description': 'x-coordinate of the text.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the text.
\n', + 'type': 'Number' + }, + { + 'name': 'fontSize', + 'description': 'font size. Defaults to the current\n textSize().
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'options', + 'description': 'object with sampleFactor and simplifyThreshold\n properties.
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5.Font', + 'module': 'Typography' + } + }, + 'p5.Camera': { + 'eyeX': { + 'name': 'eyeX', + 'class': 'p5.Camera', + 'module': '3D' + }, + 'eyeY': { + 'name': 'eyeY', + 'class': 'p5.Camera', + 'module': '3D' + }, + 'eyeZ': { + 'name': 'eyeZ', + 'class': 'p5.Camera', + 'module': '3D' + }, + 'centerX': { + 'name': 'centerX', + 'class': 'p5.Camera', + 'module': '3D' + }, + 'centerY': { + 'name': 'centerY', + 'class': 'p5.Camera', + 'module': '3D' + }, + 'centerZ': { + 'name': 'centerZ', + 'class': 'p5.Camera', + 'module': '3D' + }, + 'upX': { + 'name': 'upX', + 'class': 'p5.Camera', + 'module': '3D' + }, + 'upY': { + 'name': 'upY', + 'class': 'p5.Camera', + 'module': '3D' + }, + 'upZ': { + 'name': 'upZ', + 'class': 'p5.Camera', + 'module': '3D' + }, + 'perspective': { + 'name': 'perspective', + 'params': [ + { + 'name': 'fovy', + 'description': 'camera frustum vertical field of view. Defaults to\n 2 * atan(height / 2 / 800)
.
camera frustum aspect ratio. Defaults to\n width / height
.
distance from the camera to the near clipping plane.\n Defaults to 0.1 * 800
.
distance from the camera to the far clipping plane.\n Defaults to 10 * 800
.
x-coordinate of the frustum’s left plane. Defaults to -width / 2
.
x-coordinate of the frustum’s right plane. Defaults to width / 2
.
y-coordinate of the frustum’s bottom plane. Defaults to height / 2
.
y-coordinate of the frustum’s top plane. Defaults to -height / 2
.
z-coordinate of the frustum’s near plane. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'far', + 'description': 'z-coordinate of the frustum’s far plane. Defaults to max(width, height) + 800
.
x-coordinate of the frustum’s left plane. Defaults to -width / 20
.
x-coordinate of the frustum’s right plane. Defaults to width / 20
.
y-coordinate of the frustum’s bottom plane. Defaults to height / 20
.
y-coordinate of the frustum’s top plane. Defaults to -height / 20
.
z-coordinate of the frustum’s near plane. Defaults to 0.1 * 800
.
z-coordinate of the frustum’s far plane. Defaults to 10 * 800
.
amount to rotate in the current\n angleMode().
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Camera', + 'module': '3D' + }, + 'tilt': { + 'name': 'tilt', + 'params': [ + { + 'name': 'angle', + 'description': 'amount to rotate in the current\n angleMode().
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Camera', + 'module': '3D' + }, + 'lookAt': { + 'name': 'lookAt', + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the position where the camera should look in "world" space.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the position where the camera should look in "world" space.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'z-coordinate of the position where the camera should look in "world" space.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Camera', + 'module': '3D' + }, + 'camera': { + 'name': 'camera', + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the camera. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'y', + 'description': 'y-coordinate of the camera. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'z', + 'description': 'z-coordinate of the camera. Defaults to 800.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'centerX', + 'description': 'x-coordinate of the point the camera faces. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'centerY', + 'description': 'y-coordinate of the point the camera faces. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'centerZ', + 'description': 'z-coordinate of the point the camera faces. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'upX', + 'description': 'x-component of the camera’s "up" vector. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'upY', + 'description': 'x-component of the camera’s "up" vector. Defaults to 1.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'upZ', + 'description': 'z-component of the camera’s "up" vector. Defaults to 0.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Camera', + 'module': '3D' + }, + 'move': { + 'name': 'move', + 'params': [ + { + 'name': 'x', + 'description': 'distance to move along the camera’s "local" x-axis.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'distance to move along the camera’s "local" y-axis.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'distance to move along the camera’s "local" z-axis.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Camera', + 'module': '3D' + }, + 'setPosition': { + 'name': 'setPosition', + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate in "world" space.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate in "world" space.
\n', + 'type': 'Number' + }, + { + 'name': 'z', + 'description': 'z-coordinate in "world" space.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Camera', + 'module': '3D' + }, + 'set': { + 'name': 'set', + 'params': [ + { + 'name': 'cam', + 'description': 'camera to copy.
\n', + 'type': 'p5.Camera' + } + ], + 'class': 'p5.Camera', + 'module': '3D' + }, + 'slerp': { + 'name': 'slerp', + 'params': [ + { + 'name': 'cam0', + 'description': 'first camera.
\n', + 'type': 'p5.Camera' + }, + { + 'name': 'cam1', + 'description': 'second camera.
\n', + 'type': 'p5.Camera' + }, + { + 'name': 'amt', + 'description': 'amount of interpolation between 0.0 (cam0
) and 1.0 (cam1
).
width of the framebuffer.
\n', + 'type': 'Number' + }, + { + 'name': 'height', + 'description': 'height of the framebuffer.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Framebuffer', + 'module': 'Rendering' + }, + 'pixelDensity': { + 'name': 'pixelDensity', + 'params': [ + { + 'name': 'density', + 'description': 'pixel density to set.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Framebuffer', + 'module': 'Rendering' + }, + 'autoSized': { + 'name': 'autoSized', + 'params': [ + { + 'name': 'autoSized', + 'description': 'whether to automatically resize the framebuffer to match the canvas.
\n', + 'type': 'Boolean', + 'optional': true + } + ], + 'class': 'p5.Framebuffer', + 'module': 'Rendering' + }, + 'createCamera': { + 'name': 'createCamera', + 'class': 'p5.Framebuffer', + 'module': 'Rendering' + }, + 'remove': { + 'name': 'remove', + 'class': 'p5.Framebuffer', + 'module': 'Rendering' + }, + 'begin': { + 'name': 'begin', + 'class': 'p5.Framebuffer', + 'module': 'Rendering' + }, + 'end': { + 'name': 'end', + 'class': 'p5.Framebuffer', + 'module': 'Rendering' + }, + 'draw': { + 'name': 'draw', + 'params': [ + { + 'name': 'callback', + 'description': 'function that draws to the framebuffer.
\n', + 'type': 'Function' + } + ], + 'class': 'p5.Framebuffer', + 'module': 'Rendering' + }, + 'loadPixels': { + 'name': 'loadPixels', + 'class': 'p5.Framebuffer', + 'module': 'Rendering' + }, + 'get': { + 'name': 'get', + 'class': 'p5.Framebuffer', + 'module': 'Rendering', + 'overloads': [ + { + 'params': [ + { + 'name': 'x', + 'description': 'x-coordinate of the pixel. Defaults to 0.
\n', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': 'y-coordinate of the pixel. Defaults to 0.
\n', + 'type': 'Number' + }, + { + 'name': 'w', + 'description': 'width of the subsection to be returned.
\n', + 'type': 'Number' + }, + { + 'name': 'h', + 'description': 'height of the subsection to be returned.
\n', + 'type': 'Number' + } + ] + }, + { + 'params': [ + ] + }, + { + 'params': [ + { + 'name': 'x', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'y', + 'description': '', + 'type': 'Number' + } + ] + } + ] + }, + 'updatePixels': { + 'name': 'updatePixels', + 'class': 'p5.Framebuffer', + 'module': 'Rendering' + }, + 'color': { + 'name': 'color', + 'class': 'p5.Framebuffer', + 'module': 'Rendering' + }, + 'depth': { + 'name': 'depth', + 'class': 'p5.Framebuffer', + 'module': 'Rendering' + } + }, + 'p5.Geometry': { + 'vertices': { + 'name': 'vertices', + 'class': 'p5.Geometry', + 'module': 'Shape' + }, + 'vertexNormals': { + 'name': 'vertexNormals', + 'class': 'p5.Geometry', + 'module': 'Shape' + }, + 'faces': { + 'name': 'faces', + 'class': 'p5.Geometry', + 'module': 'Shape' + }, + 'uvs': { + 'name': 'uvs', + 'class': 'p5.Geometry', + 'module': 'Shape' + }, + 'calculateBoundingBox': { + 'name': 'calculateBoundingBox', + 'class': 'p5.Geometry', + 'module': 'Shape' + }, + 'clearColors': { + 'name': 'clearColors', + 'class': 'p5.Geometry', + 'module': 'Shape' + }, + 'flipU': { + 'name': 'flipU', + 'class': 'p5.Geometry', + 'module': 'Shape' + }, + 'flipV': { + 'name': 'flipV', + 'class': 'p5.Geometry', + 'module': 'Shape' + }, + 'computeFaces': { + 'name': 'computeFaces', + 'class': 'p5.Geometry', + 'module': 'Shape' + }, + 'computeNormals': { + 'name': 'computeNormals', + 'params': [ + { + 'name': 'shadingType', + 'description': 'shading type. either FLAT or SMOOTH. Defaults to FLAT
.
shading options.
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5.Geometry', + 'module': 'Shape' + }, + 'normalize': { + 'name': 'normalize', + 'class': 'p5.Geometry', + 'module': 'Shape' + } + }, + 'p5.Shader': { + 'copyToContext': { + 'name': 'copyToContext', + 'params': [ + { + 'name': 'context', + 'description': 'WebGL context for the copied shader.
\n', + 'type': 'p5|p5.Graphics' + } + ], + 'class': 'p5.Shader', + 'module': '3D' + }, + 'setUniform': { + 'name': 'setUniform', + 'params': [ + { + 'name': 'uniformName', + 'description': 'name of the uniform. Must match the name\n used in the vertex and fragment shaders.
\n', + 'type': 'String' + }, + { + 'name': 'data', + 'description': 'value to assign to the uniform. Must match the uniform’s data type.
\n', + 'type': 'Boolean|Number|Number[]|p5.Image|p5.Graphics|p5.MediaElement|p5.Texture' + } + ], + 'class': 'p5.Shader', + 'module': '3D' + } + }, + 'p5.SoundFile': { + 'isLoaded': { + 'name': 'isLoaded', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'play': { + 'name': 'play', + 'params': [ + { + 'name': 'startTime', + 'description': '(optional) schedule playback to start (in seconds from now).
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'rate', + 'description': '(optional) playback rate
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'amp', + 'description': '(optional) amplitude (volume)\n of playback
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'cueStart', + 'description': '(optional) cue start time in seconds
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'duration', + 'description': '(optional) duration of playback in seconds
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'playMode': { + 'name': 'playMode', + 'params': [ + { + 'name': 'str', + 'description': '\'restart\' or \'sustain\' or \'untilDone\'
\n', + 'type': 'String' + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'pause': { + 'name': 'pause', + 'params': [ + { + 'name': 'startTime', + 'description': '(optional) schedule event to occur\n seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'loop': { + 'name': 'loop', + 'params': [ + { + 'name': 'startTime', + 'description': '(optional) schedule event to occur\n seconds from now
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'rate', + 'description': '(optional) playback rate
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'amp', + 'description': '(optional) playback volume
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'cueLoopStart', + 'description': '(optional) startTime in seconds
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'duration', + 'description': '(optional) loop duration in seconds
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'setLoop': { + 'name': 'setLoop', + 'params': [ + { + 'name': 'Boolean', + 'description': 'set looping to true or false
\n', + 'type': 'Boolean' + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'isLooping': { + 'name': 'isLooping', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'isPlaying': { + 'name': 'isPlaying', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'isPaused': { + 'name': 'isPaused', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'stop': { + 'name': 'stop', + 'params': [ + { + 'name': 'startTime', + 'description': '(optional) schedule event to occur\n in seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'pan': { + 'name': 'pan', + 'params': [ + { + 'name': 'panValue', + 'description': 'Set the stereo panner
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'timeFromNow', + 'description': 'schedule this event to happen\n seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'getPan': { + 'name': 'getPan', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'rate': { + 'name': 'rate', + 'params': [ + { + 'name': 'playbackRate', + 'description': 'Set the playback rate. 1.0 is normal,\n .5 is half-speed, 2.0 is twice as fast.\n Values less than zero play backwards.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'setVolume': { + 'name': 'setVolume', + 'params': [ + { + 'name': 'volume', + 'description': 'Volume (amplitude) between 0.0\n and 1.0 or modulating signal/oscillator
\n', + 'type': 'Number|Object' + }, + { + 'name': 'rampTime', + 'description': 'Fade for t seconds
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'timeFromNow', + 'description': 'Schedule this event to happen at\n t seconds in the future
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'duration': { + 'name': 'duration', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'currentTime': { + 'name': 'currentTime', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'jump': { + 'name': 'jump', + 'params': [ + { + 'name': 'cueTime', + 'description': 'cueTime of the soundFile in seconds.
\n', + 'type': 'Number' + }, + { + 'name': 'duration', + 'description': 'duration in seconds.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'channels': { + 'name': 'channels', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'sampleRate': { + 'name': 'sampleRate', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'frames': { + 'name': 'frames', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'getPeaks': { + 'name': 'getPeaks', + 'params': [ + { + 'name': 'length', + 'description': 'length is the size of the returned array.\n Larger length results in more precision.\n Defaults to 5*width of the browser window.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'reverseBuffer': { + 'name': 'reverseBuffer', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'onended': { + 'name': 'onended', + 'params': [ + { + 'name': 'callback', + 'description': 'function to call when the\n soundfile has ended.
\n', + 'type': 'Function' + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'connect': { + 'name': 'connect', + 'params': [ + { + 'name': 'object', + 'description': 'Audio object that accepts an input
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'disconnect': { + 'name': 'disconnect', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'setPath': { + 'name': 'setPath', + 'params': [ + { + 'name': 'path', + 'description': 'path to audio file
\n', + 'type': 'String' + }, + { + 'name': 'callback', + 'description': 'Callback
\n', + 'type': 'Function' + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'setBuffer': { + 'name': 'setBuffer', + 'params': [ + { + 'name': 'buf', + 'description': 'Array of Float32 Array(s). 2 Float32 Arrays\n will create a stereo source. 1 will create\n a mono source.
\n', + 'type': 'Array' + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'addCue': { + 'name': 'addCue', + 'params': [ + { + 'name': 'time', + 'description': 'Time in seconds, relative to this media\n element\'s playback. For example, to trigger\n an event every time playback reaches two\n seconds, pass in the number 2. This will be\n passed as the first parameter to\n the callback function.
\n', + 'type': 'Number' + }, + { + 'name': 'callback', + 'description': 'Name of a function that will be\n called at the given time. The callback will\n receive time and (optionally) param as its\n two parameters.
\n', + 'type': 'Function' + }, + { + 'name': 'value', + 'description': 'An object to be passed as the\n second parameter to the\n callback function.
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'removeCue': { + 'name': 'removeCue', + 'params': [ + { + 'name': 'id', + 'description': 'ID of the cue, as returned by addCue
\n', + 'type': 'Number' + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'clearCues': { + 'name': 'clearCues', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'save': { + 'name': 'save', + 'params': [ + { + 'name': 'fileName', + 'description': 'name of the resulting .wav file.
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + }, + 'getBlob': { + 'name': 'getBlob', + 'class': 'p5.SoundFile', + 'module': 'p5.sound' + } + }, + 'p5.Amplitude': { + 'setInput': { + 'name': 'setInput', + 'params': [ + { + 'name': 'snd', + 'description': 'set the sound source\n (optional, defaults to\n main output)
\n', + 'type': 'SoundObject|undefined', + 'optional': true + }, + { + 'name': 'smoothing', + 'description': 'a range between 0.0 and 1.0\n to smooth amplitude readings
\n', + 'type': 'Number|undefined', + 'optional': true + } + ], + 'class': 'p5.Amplitude', + 'module': 'p5.sound' + }, + 'getLevel': { + 'name': 'getLevel', + 'params': [ + { + 'name': 'channel', + 'description': 'Optionally return only channel 0 (left) or 1 (right)
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Amplitude', + 'module': 'p5.sound' + }, + 'toggleNormalize': { + 'name': 'toggleNormalize', + 'params': [ + { + 'name': 'boolean', + 'description': 'set normalize to true (1) or false (0)
\n', + 'type': 'Boolean', + 'optional': true + } + ], + 'class': 'p5.Amplitude', + 'module': 'p5.sound' + }, + 'smooth': { + 'name': 'smooth', + 'params': [ + { + 'name': 'set', + 'description': 'smoothing from 0.0 <= 1
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Amplitude', + 'module': 'p5.sound' + } + }, + 'p5.FFT': { + 'setInput': { + 'name': 'setInput', + 'params': [ + { + 'name': 'source', + 'description': 'p5.sound object (or web audio API source node)
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5.FFT', + 'module': 'p5.sound' + }, + 'waveform': { + 'name': 'waveform', + 'params': [ + { + 'name': 'bins', + 'description': 'Must be a power of two between\n 16 and 1024. Defaults to 1024.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'precision', + 'description': 'If any value is provided, will return results\n in a Float32 Array which is more precise\n than a regular array.
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5.FFT', + 'module': 'p5.sound' + }, + 'analyze': { + 'name': 'analyze', + 'params': [ + { + 'name': 'bins', + 'description': 'Must be a power of two between\n 16 and 1024. Defaults to 1024.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'scale', + 'description': 'If "dB," returns decibel\n float measurements between\n -140 and 0 (max).\n Otherwise returns integers from 0-255.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.FFT', + 'module': 'p5.sound' + }, + 'getEnergy': { + 'name': 'getEnergy', + 'params': [ + { + 'name': 'frequency1', + 'description': 'Will return a value representing\n energy at this frequency. Alternately,\n the strings "bass", "lowMid" "mid",\n "highMid", and "treble" will return\n predefined frequency ranges.
\n', + 'type': 'Number|String' + }, + { + 'name': 'frequency2', + 'description': 'If a second frequency is given,\n will return average amount of\n energy that exists between the\n two frequencies.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.FFT', + 'module': 'p5.sound' + }, + 'getCentroid': { + 'name': 'getCentroid', + 'class': 'p5.FFT', + 'module': 'p5.sound' + }, + 'smooth': { + 'name': 'smooth', + 'params': [ + { + 'name': 'smoothing', + 'description': '0.0 < smoothing < 1.0.\n Defaults to 0.8.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.FFT', + 'module': 'p5.sound' + }, + 'linAverages': { + 'name': 'linAverages', + 'params': [ + { + 'name': 'N', + 'description': 'Number of returned frequency groups
\n', + 'type': 'Number' + } + ], + 'class': 'p5.FFT', + 'module': 'p5.sound' + }, + 'logAverages': { + 'name': 'logAverages', + 'params': [ + { + 'name': 'octaveBands', + 'description': 'Array of Octave Bands objects for grouping
\n', + 'type': 'Array' + } + ], + 'class': 'p5.FFT', + 'module': 'p5.sound' + }, + 'getOctaveBands': { + 'name': 'getOctaveBands', + 'params': [ + { + 'name': 'N', + 'description': 'Specifies the 1/N type of generated octave bands
\n', + 'type': 'Number' + }, + { + 'name': 'fCtr0', + 'description': 'Minimum central frequency for the lowest band
\n', + 'type': 'Number' + } + ], + 'class': 'p5.FFT', + 'module': 'p5.sound' + } + }, + 'p5.Oscillator': { + 'start': { + 'name': 'start', + 'params': [ + { + 'name': 'time', + 'description': 'startTime in seconds from now.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'frequency', + 'description': 'frequency in Hz.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'stop': { + 'name': 'stop', + 'params': [ + { + 'name': 'secondsFromNow', + 'description': 'Time, in seconds from now.
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'amp': { + 'name': 'amp', + 'params': [ + { + 'name': 'vol', + 'description': 'between 0 and 1.0\n or a modulating signal/oscillator
\n', + 'type': 'Number|Object' + }, + { + 'name': 'rampTime', + 'description': 'create a fade that lasts rampTime
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'timeFromNow', + 'description': 'schedule this event to happen\n seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'getAmp': { + 'name': 'getAmp', + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'freq': { + 'name': 'freq', + 'params': [ + { + 'name': 'Frequency', + 'description': 'Frequency in Hz\n or modulating signal/oscillator
\n', + 'type': 'Number|Object' + }, + { + 'name': 'rampTime', + 'description': 'Ramp time (in seconds)
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'timeFromNow', + 'description': 'Schedule this event to happen\n at x seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'getFreq': { + 'name': 'getFreq', + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'setType': { + 'name': 'setType', + 'params': [ + { + 'name': 'type', + 'description': '\'sine\', \'triangle\', \'sawtooth\' or \'square\'.
\n', + 'type': 'String' + } + ], + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'getType': { + 'name': 'getType', + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'connect': { + 'name': 'connect', + 'params': [ + { + 'name': 'unit', + 'description': 'A p5.sound or Web Audio object
\n', + 'type': 'Object' + } + ], + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'disconnect': { + 'name': 'disconnect', + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'pan': { + 'name': 'pan', + 'params': [ + { + 'name': 'panning', + 'description': 'Number between -1 and 1
\n', + 'type': 'Number' + }, + { + 'name': 'timeFromNow', + 'description': 'schedule this event to happen\n seconds from now
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'getPan': { + 'name': 'getPan', + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'phase': { + 'name': 'phase', + 'params': [ + { + 'name': 'phase', + 'description': 'float between 0.0 and 1.0
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'add': { + 'name': 'add', + 'params': [ + { + 'name': 'number', + 'description': 'Constant number to add
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'mult': { + 'name': 'mult', + 'params': [ + { + 'name': 'number', + 'description': 'Constant number to multiply
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + }, + 'scale': { + 'name': 'scale', + 'params': [ + { + 'name': 'inMin', + 'description': 'input range minumum
\n', + 'type': 'Number' + }, + { + 'name': 'inMax', + 'description': 'input range maximum
\n', + 'type': 'Number' + }, + { + 'name': 'outMin', + 'description': 'input range minumum
\n', + 'type': 'Number' + }, + { + 'name': 'outMax', + 'description': 'input range maximum
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Oscillator', + 'module': 'p5.sound' + } + }, + 'p5.Envelope': { + 'attackTime': { + 'name': 'attackTime', + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'attackLevel': { + 'name': 'attackLevel', + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'decayTime': { + 'name': 'decayTime', + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'decayLevel': { + 'name': 'decayLevel', + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'releaseTime': { + 'name': 'releaseTime', + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'releaseLevel': { + 'name': 'releaseLevel', + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'set': { + 'name': 'set', + 'params': [ + { + 'name': 'attackTime', + 'description': 'Time (in seconds) before level\n reaches attackLevel
\n', + 'type': 'Number' + }, + { + 'name': 'attackLevel', + 'description': 'Typically an amplitude between\n 0.0 and 1.0
\n', + 'type': 'Number' + }, + { + 'name': 'decayTime', + 'description': 'Time
\n', + 'type': 'Number' + }, + { + 'name': 'decayLevel', + 'description': 'Amplitude (In a standard ADSR envelope,\n decayLevel = sustainLevel)
\n', + 'type': 'Number' + }, + { + 'name': 'releaseTime', + 'description': 'Release Time (in seconds)
\n', + 'type': 'Number' + }, + { + 'name': 'releaseLevel', + 'description': 'Amplitude
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'setADSR': { + 'name': 'setADSR', + 'params': [ + { + 'name': 'attackTime', + 'description': 'Time (in seconds before envelope\n reaches Attack Level
\n', + 'type': 'Number' + }, + { + 'name': 'decayTime', + 'description': 'Time (in seconds) before envelope\n reaches Decay/Sustain Level
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'susRatio', + 'description': 'Ratio between attackLevel and releaseLevel, on a scale from 0 to 1,\n where 1.0 = attackLevel, 0.0 = releaseLevel.\n The susRatio determines the decayLevel and the level at which the\n sustain portion of the envelope will sustain.\n For example, if attackLevel is 0.4, releaseLevel is 0,\n and susAmt is 0.5, the decayLevel would be 0.2. If attackLevel is\n increased to 1.0 (using setRange
),\n then decayLevel would increase proportionally, to become 0.5.
Time in seconds from now (defaults to 0)
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'setRange': { + 'name': 'setRange', + 'params': [ + { + 'name': 'aLevel', + 'description': 'attack level (defaults to 1)
\n', + 'type': 'Number' + }, + { + 'name': 'rLevel', + 'description': 'release level (defaults to 0)
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'setInput': { + 'name': 'setInput', + 'params': [ + { + 'name': 'inputs', + 'description': 'A p5.sound object or\n Web Audio Param.
\n', + 'type': 'Object', + 'optional': true, + 'multiple': true + } + ], + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'setExp': { + 'name': 'setExp', + 'params': [ + { + 'name': 'isExp', + 'description': 'true is exponential, false is linear
\n', + 'type': 'Boolean' + } + ], + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'play': { + 'name': 'play', + 'params': [ + { + 'name': 'unit', + 'description': 'A p5.sound object or\n Web Audio Param.
\n', + 'type': 'Object' + }, + { + 'name': 'startTime', + 'description': 'time from now (in seconds) at which to play
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'sustainTime', + 'description': 'time to sustain before releasing the envelope
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'triggerAttack': { + 'name': 'triggerAttack', + 'params': [ + { + 'name': 'unit', + 'description': 'p5.sound Object or Web Audio Param
\n', + 'type': 'Object' + }, + { + 'name': 'secondsFromNow', + 'description': 'time from now (in seconds)
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'triggerRelease': { + 'name': 'triggerRelease', + 'params': [ + { + 'name': 'unit', + 'description': 'p5.sound Object or Web Audio Param
\n', + 'type': 'Object' + }, + { + 'name': 'secondsFromNow', + 'description': 'time to trigger the release
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'ramp': { + 'name': 'ramp', + 'params': [ + { + 'name': 'unit', + 'description': 'p5.sound Object or Web Audio Param
\n', + 'type': 'Object' + }, + { + 'name': 'secondsFromNow', + 'description': 'When to trigger the ramp
\n', + 'type': 'Number' + }, + { + 'name': 'v', + 'description': 'Target value
\n', + 'type': 'Number' + }, + { + 'name': 'v2', + 'description': 'Second target value
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'add': { + 'name': 'add', + 'params': [ + { + 'name': 'number', + 'description': 'Constant number to add
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'mult': { + 'name': 'mult', + 'params': [ + { + 'name': 'number', + 'description': 'Constant number to multiply
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Envelope', + 'module': 'p5.sound' + }, + 'scale': { + 'name': 'scale', + 'params': [ + { + 'name': 'inMin', + 'description': 'input range minumum
\n', + 'type': 'Number' + }, + { + 'name': 'inMax', + 'description': 'input range maximum
\n', + 'type': 'Number' + }, + { + 'name': 'outMin', + 'description': 'input range minumum
\n', + 'type': 'Number' + }, + { + 'name': 'outMax', + 'description': 'input range maximum
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Envelope', + 'module': 'p5.sound' + } + }, + 'p5.Noise': { + 'setType': { + 'name': 'setType', + 'params': [ + { + 'name': 'type', + 'description': '\'white\', \'pink\' or \'brown\'
\n', + 'type': 'String', + 'optional': true + } + ], + 'class': 'p5.Noise', + 'module': 'p5.sound' + } + }, + 'p5.Pulse': { + 'width': { + 'name': 'width', + 'params': [ + { + 'name': 'width', + 'description': 'Width between the pulses (0 to 1.0,\n defaults to 0)
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Pulse', + 'module': 'p5.sound' + } + }, + 'p5.AudioIn': { + 'input': { + 'name': 'input', + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'output': { + 'name': 'output', + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'stream': { + 'name': 'stream', + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'mediaStream': { + 'name': 'mediaStream', + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'currentSource': { + 'name': 'currentSource', + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'enabled': { + 'name': 'enabled', + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'amplitude': { + 'name': 'amplitude', + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'start': { + 'name': 'start', + 'params': [ + { + 'name': 'successCallback', + 'description': 'Name of a function to call on\n success.
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': 'Name of a function to call if\n there was an error. For example,\n some browsers do not support\n getUserMedia.
\n', + 'type': 'Function', + 'optional': true + } + ], + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'stop': { + 'name': 'stop', + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'connect': { + 'name': 'connect', + 'params': [ + { + 'name': 'unit', + 'description': 'An object that accepts audio input,\n such as an FFT
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'disconnect': { + 'name': 'disconnect', + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'getLevel': { + 'name': 'getLevel', + 'params': [ + { + 'name': 'smoothing', + 'description': 'Smoothing is 0.0 by default.\n Smooths values based on previous values.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'amp': { + 'name': 'amp', + 'params': [ + { + 'name': 'vol', + 'description': 'between 0 and 1.0
\n', + 'type': 'Number' + }, + { + 'name': 'time', + 'description': 'ramp time (optional)
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'getSources': { + 'name': 'getSources', + 'params': [ + { + 'name': 'successCallback', + 'description': 'This callback function handles the sources when they\n have been enumerated. The callback function\n receives the deviceList array as its only argument
\n', + 'type': 'Function', + 'optional': true + }, + { + 'name': 'errorCallback', + 'description': 'This optional callback receives the error\n message as its argument.
\n', + 'type': 'Function', + 'optional': true + } + ], + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + }, + 'setSource': { + 'name': 'setSource', + 'params': [ + { + 'name': 'num', + 'description': 'position of input source in the array
\n', + 'type': 'Number' + } + ], + 'class': 'p5.AudioIn', + 'module': 'p5.sound' + } + }, + 'p5.Effect': { + 'amp': { + 'name': 'amp', + 'params': [ + { + 'name': 'vol', + 'description': 'amplitude between 0 and 1.0
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'rampTime', + 'description': 'create a fade that lasts until rampTime
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'tFromNow', + 'description': 'schedule this event to happen in tFromNow seconds
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Effect', + 'module': 'p5.sound' + }, + 'chain': { + 'name': 'chain', + 'params': [ + { + 'name': 'arguments', + 'description': 'Chain together multiple sound objects
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5.Effect', + 'module': 'p5.sound' + }, + 'drywet': { + 'name': 'drywet', + 'params': [ + { + 'name': 'fade', + 'description': 'The desired drywet value (0 - 1.0)
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Effect', + 'module': 'p5.sound' + }, + 'connect': { + 'name': 'connect', + 'params': [ + { + 'name': 'unit', + 'description': '', + 'type': 'Object' + } + ], + 'class': 'p5.Effect', + 'module': 'p5.sound' + }, + 'disconnect': { + 'name': 'disconnect', + 'class': 'p5.Effect', + 'module': 'p5.sound' + } + }, + 'p5.Filter': { + 'biquadFilter': { + 'name': 'biquadFilter', + 'class': 'p5.Filter', + 'module': 'p5.sound' + }, + 'process': { + 'name': 'process', + 'params': [ + { + 'name': 'Signal', + 'description': 'An object that outputs audio
\n', + 'type': 'Object' + }, + { + 'name': 'freq', + 'description': 'Frequency in Hz, from 10 to 22050
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'res', + 'description': 'Resonance/Width of the filter frequency\n from 0.001 to 1000
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Filter', + 'module': 'p5.sound' + }, + 'set': { + 'name': 'set', + 'params': [ + { + 'name': 'freq', + 'description': 'Frequency in Hz, from 10 to 22050
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'res', + 'description': 'Resonance (Q) from 0.001 to 1000
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'timeFromNow', + 'description': 'schedule this event to happen\n seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Filter', + 'module': 'p5.sound' + }, + 'freq': { + 'name': 'freq', + 'params': [ + { + 'name': 'freq', + 'description': 'Filter Frequency
\n', + 'type': 'Number' + }, + { + 'name': 'timeFromNow', + 'description': 'schedule this event to happen\n seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Filter', + 'module': 'p5.sound' + }, + 'res': { + 'name': 'res', + 'params': [ + { + 'name': 'res', + 'description': 'Resonance/Width of filter freq\n from 0.001 to 1000
\n', + 'type': 'Number' + }, + { + 'name': 'timeFromNow', + 'description': 'schedule this event to happen\n seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Filter', + 'module': 'p5.sound' + }, + 'gain': { + 'name': 'gain', + 'params': [ + { + 'name': 'gain', + 'description': '', + 'type': 'Number' + } + ], + 'class': 'p5.Filter', + 'module': 'p5.sound' + }, + 'toggle': { + 'name': 'toggle', + 'class': 'p5.Filter', + 'module': 'p5.sound' + }, + 'setType': { + 'name': 'setType', + 'params': [ + { + 'name': 't', + 'description': '', + 'type': 'String' + } + ], + 'class': 'p5.Filter', + 'module': 'p5.sound' + } + }, + 'p5.EQ': { + 'bands': { + 'name': 'bands', + 'class': 'p5.EQ', + 'module': 'p5.sound' + }, + 'process': { + 'name': 'process', + 'params': [ + { + 'name': 'src', + 'description': 'Audio source
\n', + 'type': 'Object' + } + ], + 'class': 'p5.EQ', + 'module': 'p5.sound' + } + }, + 'p5.Panner3D': { + 'panner': { + 'name': 'panner', + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + }, + 'process': { + 'name': 'process', + 'params': [ + { + 'name': 'src', + 'description': 'Input source
\n', + 'type': 'Object' + } + ], + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + }, + 'set': { + 'name': 'set', + 'params': [ + { + 'name': 'xVal', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'yVal', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'zVal', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'time', + 'description': '', + 'type': 'Number' + } + ], + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + }, + 'positionX': { + 'name': 'positionX', + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + }, + 'positionY': { + 'name': 'positionY', + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + }, + 'positionZ': { + 'name': 'positionZ', + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + }, + 'orient': { + 'name': 'orient', + 'params': [ + { + 'name': 'xVal', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'yVal', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'zVal', + 'description': '', + 'type': 'Number' + }, + { + 'name': 'time', + 'description': '', + 'type': 'Number' + } + ], + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + }, + 'orientX': { + 'name': 'orientX', + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + }, + 'orientY': { + 'name': 'orientY', + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + }, + 'orientZ': { + 'name': 'orientZ', + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + }, + 'setFalloff': { + 'name': 'setFalloff', + 'params': [ + { + 'name': 'maxDistance', + 'description': '', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'rolloffFactor', + 'description': '', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + }, + 'maxDist': { + 'name': 'maxDist', + 'params': [ + { + 'name': 'maxDistance', + 'description': '', + 'type': 'Number' + } + ], + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + }, + 'rollof': { + 'name': 'rollof', + 'params': [ + { + 'name': 'rolloffFactor', + 'description': '', + 'type': 'Number' + } + ], + 'class': 'p5.Panner3D', + 'module': 'p5.sound' + } + }, + 'p5.Delay': { + 'leftDelay': { + 'name': 'leftDelay', + 'class': 'p5.Delay', + 'module': 'p5.sound' + }, + 'rightDelay': { + 'name': 'rightDelay', + 'class': 'p5.Delay', + 'module': 'p5.sound' + }, + 'process': { + 'name': 'process', + 'params': [ + { + 'name': 'Signal', + 'description': 'An object that outputs audio
\n', + 'type': 'Object' + }, + { + 'name': 'delayTime', + 'description': 'Time (in seconds) of the delay/echo.\n Some browsers limit delayTime to\n 1 second.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'feedback', + 'description': 'sends the delay back through itself\n in a loop that decreases in volume\n each time.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'lowPass', + 'description': 'Cutoff frequency. Only frequencies\n below the lowPass will be part of the\n delay.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Delay', + 'module': 'p5.sound' + }, + 'delayTime': { + 'name': 'delayTime', + 'params': [ + { + 'name': 'delayTime', + 'description': 'Time (in seconds) of the delay
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Delay', + 'module': 'p5.sound' + }, + 'feedback': { + 'name': 'feedback', + 'params': [ + { + 'name': 'feedback', + 'description': '0.0 to 1.0, or an object such as an\n Oscillator that can be used to\n modulate this param
\n', + 'type': 'Number|Object' + } + ], + 'class': 'p5.Delay', + 'module': 'p5.sound' + }, + 'filter': { + 'name': 'filter', + 'params': [ + { + 'name': 'cutoffFreq', + 'description': 'A lowpass filter will cut off any\n frequencies higher than the filter frequency.
\n', + 'type': 'Number|Object' + }, + { + 'name': 'res', + 'description': 'Resonance of the filter frequency\n cutoff, or an object (i.e. a p5.Oscillator)\n that can be used to modulate this parameter.\n High numbers (i.e. 15) will produce a resonance,\n low numbers (i.e. .2) will produce a slope.
\n', + 'type': 'Number|Object' + } + ], + 'class': 'p5.Delay', + 'module': 'p5.sound' + }, + 'setType': { + 'name': 'setType', + 'params': [ + { + 'name': 'type', + 'description': '\'pingPong\' (1) or \'default\' (0)
\n', + 'type': 'String|Number' + } + ], + 'class': 'p5.Delay', + 'module': 'p5.sound' + }, + 'amp': { + 'name': 'amp', + 'params': [ + { + 'name': 'volume', + 'description': 'amplitude between 0 and 1.0
\n', + 'type': 'Number' + }, + { + 'name': 'rampTime', + 'description': 'create a fade that lasts rampTime
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'timeFromNow', + 'description': 'schedule this event to happen\n seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Delay', + 'module': 'p5.sound' + }, + 'connect': { + 'name': 'connect', + 'params': [ + { + 'name': 'unit', + 'description': '', + 'type': 'Object' + } + ], + 'class': 'p5.Delay', + 'module': 'p5.sound' + }, + 'disconnect': { + 'name': 'disconnect', + 'class': 'p5.Delay', + 'module': 'p5.sound' + } + }, + 'p5.Reverb': { + 'process': { + 'name': 'process', + 'params': [ + { + 'name': 'src', + 'description': 'p5.sound / Web Audio object with a sound\n output.
\n', + 'type': 'Object' + }, + { + 'name': 'seconds', + 'description': 'Duration of the reverb, in seconds.\n Min: 0, Max: 10. Defaults to 3.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'decayRate', + 'description': 'Percentage of decay with each echo.\n Min: 0, Max: 100. Defaults to 2.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'reverse', + 'description': 'Play the reverb backwards or forwards.
\n', + 'type': 'Boolean', + 'optional': true + } + ], + 'class': 'p5.Reverb', + 'module': 'p5.sound' + }, + 'set': { + 'name': 'set', + 'params': [ + { + 'name': 'seconds', + 'description': 'Duration of the reverb, in seconds.\n Min: 0, Max: 10. Defaults to 3.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'decayRate', + 'description': 'Percentage of decay with each echo.\n Min: 0, Max: 100. Defaults to 2.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'reverse', + 'description': 'Play the reverb backwards or forwards.
\n', + 'type': 'Boolean', + 'optional': true + } + ], + 'class': 'p5.Reverb', + 'module': 'p5.sound' + }, + 'amp': { + 'name': 'amp', + 'params': [ + { + 'name': 'volume', + 'description': 'amplitude between 0 and 1.0
\n', + 'type': 'Number' + }, + { + 'name': 'rampTime', + 'description': 'create a fade that lasts rampTime
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'timeFromNow', + 'description': 'schedule this event to happen\n seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Reverb', + 'module': 'p5.sound' + }, + 'connect': { + 'name': 'connect', + 'params': [ + { + 'name': 'unit', + 'description': '', + 'type': 'Object' + } + ], + 'class': 'p5.Reverb', + 'module': 'p5.sound' + }, + 'disconnect': { + 'name': 'disconnect', + 'class': 'p5.Reverb', + 'module': 'p5.sound' + } + }, + 'p5.Convolver': { + 'convolverNode': { + 'name': 'convolverNode', + 'class': 'p5.Convolver', + 'module': 'p5.sound' + }, + 'impulses': { + 'name': 'impulses', + 'class': 'p5.Convolver', + 'module': 'p5.sound' + }, + 'process': { + 'name': 'process', + 'params': [ + { + 'name': 'src', + 'description': 'p5.sound / Web Audio object with a sound\n output.
\n', + 'type': 'Object' + } + ], + 'class': 'p5.Convolver', + 'module': 'p5.sound' + }, + 'addImpulse': { + 'name': 'addImpulse', + 'params': [ + { + 'name': 'path', + 'description': 'path to a sound file
\n', + 'type': 'String' + }, + { + 'name': 'callback', + 'description': 'function (optional)
\n', + 'type': 'Function' + }, + { + 'name': 'errorCallback', + 'description': 'function (optional)
\n', + 'type': 'Function' + } + ], + 'class': 'p5.Convolver', + 'module': 'p5.sound' + }, + 'resetImpulse': { + 'name': 'resetImpulse', + 'params': [ + { + 'name': 'path', + 'description': 'path to a sound file
\n', + 'type': 'String' + }, + { + 'name': 'callback', + 'description': 'function (optional)
\n', + 'type': 'Function' + }, + { + 'name': 'errorCallback', + 'description': 'function (optional)
\n', + 'type': 'Function' + } + ], + 'class': 'p5.Convolver', + 'module': 'p5.sound' + }, + 'toggleImpulse': { + 'name': 'toggleImpulse', + 'params': [ + { + 'name': 'id', + 'description': 'Identify the impulse by its original filename\n (String), or by its position in the\n .impulses
Array (Number).
Beats Per Minute
\n', + 'type': 'Number' + }, + { + 'name': 'rampTime', + 'description': 'Seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Part', + 'module': 'p5.sound' + }, + 'getBPM': { + 'name': 'getBPM', + 'class': 'p5.Part', + 'module': 'p5.sound' + }, + 'start': { + 'name': 'start', + 'params': [ + { + 'name': 'time', + 'description': 'seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Part', + 'module': 'p5.sound' + }, + 'loop': { + 'name': 'loop', + 'params': [ + { + 'name': 'time', + 'description': 'seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Part', + 'module': 'p5.sound' + }, + 'noLoop': { + 'name': 'noLoop', + 'class': 'p5.Part', + 'module': 'p5.sound' + }, + 'stop': { + 'name': 'stop', + 'params': [ + { + 'name': 'time', + 'description': 'seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Part', + 'module': 'p5.sound' + }, + 'pause': { + 'name': 'pause', + 'params': [ + { + 'name': 'time', + 'description': 'seconds from now
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Part', + 'module': 'p5.sound' + }, + 'addPhrase': { + 'name': 'addPhrase', + 'params': [ + { + 'name': 'phrase', + 'description': 'reference to a p5.Phrase
\n', + 'type': 'p5.Phrase' + } + ], + 'class': 'p5.Part', + 'module': 'p5.sound' + }, + 'removePhrase': { + 'name': 'removePhrase', + 'params': [ + { + 'name': 'phraseName', + 'description': '', + 'type': 'String' + } + ], + 'class': 'p5.Part', + 'module': 'p5.sound' + }, + 'getPhrase': { + 'name': 'getPhrase', + 'params': [ + { + 'name': 'phraseName', + 'description': '', + 'type': 'String' + } + ], + 'class': 'p5.Part', + 'module': 'p5.sound' + }, + 'replaceSequence': { + 'name': 'replaceSequence', + 'params': [ + { + 'name': 'phraseName', + 'description': '', + 'type': 'String' + }, + { + 'name': 'sequence', + 'description': 'Array of values to pass into the callback\n at each step of the phrase.
\n', + 'type': 'Array' + } + ], + 'class': 'p5.Part', + 'module': 'p5.sound' + }, + 'onStep': { + 'name': 'onStep', + 'params': [ + { + 'name': 'callback', + 'description': 'The name of the callback\n you want to fire\n on every beat/tatum.
\n', + 'type': 'Function' + } + ], + 'class': 'p5.Part', + 'module': 'p5.sound' + } + }, + 'p5.Score': { + 'start': { + 'name': 'start', + 'class': 'p5.Score', + 'module': 'p5.sound' + }, + 'stop': { + 'name': 'stop', + 'class': 'p5.Score', + 'module': 'p5.sound' + }, + 'pause': { + 'name': 'pause', + 'class': 'p5.Score', + 'module': 'p5.sound' + }, + 'loop': { + 'name': 'loop', + 'class': 'p5.Score', + 'module': 'p5.sound' + }, + 'noLoop': { + 'name': 'noLoop', + 'class': 'p5.Score', + 'module': 'p5.sound' + }, + 'setBPM': { + 'name': 'setBPM', + 'params': [ + { + 'name': 'BPM', + 'description': 'Beats Per Minute
\n', + 'type': 'Number' + }, + { + 'name': 'rampTime', + 'description': 'Seconds from now
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Score', + 'module': 'p5.sound' + } + }, + 'p5.SoundLoop': { + 'bpm': { + 'name': 'bpm', + 'class': 'p5.SoundLoop', + 'module': 'p5.sound' + }, + 'timeSignature': { + 'name': 'timeSignature', + 'class': 'p5.SoundLoop', + 'module': 'p5.sound' + }, + 'interval': { + 'name': 'interval', + 'class': 'p5.SoundLoop', + 'module': 'p5.sound' + }, + 'iterations': { + 'name': 'iterations', + 'class': 'p5.SoundLoop', + 'module': 'p5.sound' + }, + 'musicalTimeMode': { + 'name': 'musicalTimeMode', + 'class': 'p5.SoundLoop', + 'module': 'p5.sound' + }, + 'maxIterations': { + 'name': 'maxIterations', + 'class': 'p5.SoundLoop', + 'module': 'p5.sound' + }, + 'start': { + 'name': 'start', + 'params': [ + { + 'name': 'timeFromNow', + 'description': 'schedule a starting time
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.SoundLoop', + 'module': 'p5.sound' + }, + 'stop': { + 'name': 'stop', + 'params': [ + { + 'name': 'timeFromNow', + 'description': 'schedule a stopping time
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.SoundLoop', + 'module': 'p5.sound' + }, + 'pause': { + 'name': 'pause', + 'params': [ + { + 'name': 'timeFromNow', + 'description': 'schedule a pausing time
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.SoundLoop', + 'module': 'p5.sound' + }, + 'syncedStart': { + 'name': 'syncedStart', + 'params': [ + { + 'name': 'otherLoop', + 'description': 'a p5.SoundLoop to sync with
\n', + 'type': 'Object' + }, + { + 'name': 'timeFromNow', + 'description': 'Start the loops in sync after timeFromNow seconds
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.SoundLoop', + 'module': 'p5.sound' + } + }, + 'p5.Compressor': { + 'compressor': { + 'name': 'compressor', + 'class': 'p5.Compressor', + 'module': 'p5.sound' + }, + 'process': { + 'name': 'process', + 'params': [ + { + 'name': 'src', + 'description': 'Sound source to be connected
\n', + 'type': 'Object' + }, + { + 'name': 'attack', + 'description': 'The amount of time (in seconds) to reduce the gain by 10dB,\n default = .003, range 0 - 1
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'knee', + 'description': 'A decibel value representing the range above the\n threshold where the curve smoothly transitions to the "ratio" portion.\n default = 30, range 0 - 40
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'ratio', + 'description': 'The amount of dB change in input for a 1 dB change in output\n default = 12, range 1 - 20
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'threshold', + 'description': 'The decibel value above which the compression will start taking effect\n default = -24, range -100 - 0
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'release', + 'description': 'The amount of time (in seconds) to increase the gain by 10dB\n default = .25, range 0 - 1
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Compressor', + 'module': 'p5.sound' + }, + 'set': { + 'name': 'set', + 'params': [ + { + 'name': 'attack', + 'description': 'The amount of time (in seconds) to reduce the gain by 10dB,\n default = .003, range 0 - 1
\n', + 'type': 'Number' + }, + { + 'name': 'knee', + 'description': 'A decibel value representing the range above the\n threshold where the curve smoothly transitions to the "ratio" portion.\n default = 30, range 0 - 40
\n', + 'type': 'Number' + }, + { + 'name': 'ratio', + 'description': 'The amount of dB change in input for a 1 dB change in output\n default = 12, range 1 - 20
\n', + 'type': 'Number' + }, + { + 'name': 'threshold', + 'description': 'The decibel value above which the compression will start taking effect\n default = -24, range -100 - 0
\n', + 'type': 'Number' + }, + { + 'name': 'release', + 'description': 'The amount of time (in seconds) to increase the gain by 10dB\n default = .25, range 0 - 1
\n', + 'type': 'Number' + } + ], + 'class': 'p5.Compressor', + 'module': 'p5.sound' + }, + 'attack': { + 'name': 'attack', + 'params': [ + { + 'name': 'attack', + 'description': 'Attack is the amount of time (in seconds) to reduce the gain by 10dB,\n default = .003, range 0 - 1
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'time', + 'description': 'Assign time value to schedule the change in value
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Compressor', + 'module': 'p5.sound' + }, + 'knee': { + 'name': 'knee', + 'params': [ + { + 'name': 'knee', + 'description': 'A decibel value representing the range above the\n threshold where the curve smoothly transitions to the "ratio" portion.\n default = 30, range 0 - 40
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'time', + 'description': 'Assign time value to schedule the change in value
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Compressor', + 'module': 'p5.sound' + }, + 'ratio': { + 'name': 'ratio', + 'params': [ + { + 'name': 'ratio', + 'description': 'The amount of dB change in input for a 1 dB change in output\n default = 12, range 1 - 20
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'time', + 'description': 'Assign time value to schedule the change in value
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Compressor', + 'module': 'p5.sound' + }, + 'threshold': { + 'name': 'threshold', + 'params': [ + { + 'name': 'threshold', + 'description': 'The decibel value above which the compression will start taking effect\n default = -24, range -100 - 0
\n', + 'type': 'Number' + }, + { + 'name': 'time', + 'description': 'Assign time value to schedule the change in value
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Compressor', + 'module': 'p5.sound' + }, + 'release': { + 'name': 'release', + 'params': [ + { + 'name': 'release', + 'description': 'The amount of time (in seconds) to increase the gain by 10dB\n default = .25, range 0 - 1
\n', + 'type': 'Number' + }, + { + 'name': 'time', + 'description': 'Assign time value to schedule the change in value
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Compressor', + 'module': 'p5.sound' + }, + 'reduction': { + 'name': 'reduction', + 'class': 'p5.Compressor', + 'module': 'p5.sound' + } + }, + 'p5.PeakDetect': { + 'isDetected': { + 'name': 'isDetected', + 'class': 'p5.PeakDetect', + 'module': 'p5.sound' + }, + 'update': { + 'name': 'update', + 'params': [ + { + 'name': 'fftObject', + 'description': 'A p5.FFT object
\n', + 'type': 'p5.FFT' + } + ], + 'class': 'p5.PeakDetect', + 'module': 'p5.sound' + }, + 'onPeak': { + 'name': 'onPeak', + 'params': [ + { + 'name': 'callback', + 'description': 'Name of a function that will\n be called when a peak is\n detected.
\n', + 'type': 'Function' + }, + { + 'name': 'val', + 'description': 'Optional value to pass\n into the function when\n a peak is detected.
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5.PeakDetect', + 'module': 'p5.sound' + } + }, + 'p5.SoundRecorder': { + 'setInput': { + 'name': 'setInput', + 'params': [ + { + 'name': 'unit', + 'description': 'p5.sound object or a web audio unit\n that outputs sound
\n', + 'type': 'Object', + 'optional': true + } + ], + 'class': 'p5.SoundRecorder', + 'module': 'p5.sound' + }, + 'record': { + 'name': 'record', + 'params': [ + { + 'name': 'soundFile', + 'description': 'p5.SoundFile
\n', + 'type': 'p5.SoundFile' + }, + { + 'name': 'duration', + 'description': 'Time (in seconds)
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'callback', + 'description': 'The name of a function that will be\n called once the recording completes
\n', + 'type': 'Function', + 'optional': true + } + ], + 'class': 'p5.SoundRecorder', + 'module': 'p5.sound' + }, + 'stop': { + 'name': 'stop', + 'class': 'p5.SoundRecorder', + 'module': 'p5.sound' + } + }, + 'p5.Distortion': { + 'WaveShaperNode': { + 'name': 'WaveShaperNode', + 'class': 'p5.Distortion', + 'module': 'p5.sound' + }, + 'process': { + 'name': 'process', + 'params': [ + { + 'name': 'amount', + 'description': 'Unbounded distortion amount.\n Normal values range from 0-1.
\n', + 'type': 'Number', + 'optional': true, + 'optdefault': '0.25' + }, + { + 'name': 'oversample', + 'description': '\'none\', \'2x\', or \'4x\'.
\n', + 'type': 'String', + 'optional': true, + 'optdefault': '\'none\'' + } + ], + 'class': 'p5.Distortion', + 'module': 'p5.sound' + }, + 'set': { + 'name': 'set', + 'params': [ + { + 'name': 'amount', + 'description': 'Unbounded distortion amount.\n Normal values range from 0-1.
\n', + 'type': 'Number', + 'optional': true, + 'optdefault': '0.25' + }, + { + 'name': 'oversample', + 'description': '\'none\', \'2x\', or \'4x\'.
\n', + 'type': 'String', + 'optional': true, + 'optdefault': '\'none\'' + } + ], + 'class': 'p5.Distortion', + 'module': 'p5.sound' + }, + 'getAmount': { + 'name': 'getAmount', + 'class': 'p5.Distortion', + 'module': 'p5.sound' + }, + 'getOversample': { + 'name': 'getOversample', + 'class': 'p5.Distortion', + 'module': 'p5.sound' + } + }, + 'p5.Gain': { + 'setInput': { + 'name': 'setInput', + 'params': [ + { + 'name': 'src', + 'description': 'p5.sound / Web Audio object with a sound\n output.
\n', + 'type': 'Object' + } + ], + 'class': 'p5.Gain', + 'module': 'p5.sound' + }, + 'connect': { + 'name': 'connect', + 'params': [ + { + 'name': 'unit', + 'description': '', + 'type': 'Object' + } + ], + 'class': 'p5.Gain', + 'module': 'p5.sound' + }, + 'disconnect': { + 'name': 'disconnect', + 'class': 'p5.Gain', + 'module': 'p5.sound' + }, + 'amp': { + 'name': 'amp', + 'params': [ + { + 'name': 'volume', + 'description': 'amplitude between 0 and 1.0
\n', + 'type': 'Number' + }, + { + 'name': 'rampTime', + 'description': 'create a fade that lasts rampTime
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'timeFromNow', + 'description': 'schedule this event to happen\n seconds from now
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.Gain', + 'module': 'p5.sound' + } + }, + 'p5.AudioVoice': { + 'connect': { + 'name': 'connect', + 'params': [ + { + 'name': 'unit', + 'description': '', + 'type': 'Object' + } + ], + 'class': 'p5.AudioVoice', + 'module': 'p5.sound' + }, + 'disconnect': { + 'name': 'disconnect', + 'class': 'p5.AudioVoice', + 'module': 'p5.sound' + } + }, + 'p5.MonoSynth': { + 'attack': { + 'name': 'attack', + 'class': 'p5.MonoSynth', + 'module': 'p5.sound' + }, + 'decay': { + 'name': 'decay', + 'class': 'p5.MonoSynth', + 'module': 'p5.sound' + }, + 'sustain': { + 'name': 'sustain', + 'class': 'p5.MonoSynth', + 'module': 'p5.sound' + }, + 'release': { + 'name': 'release', + 'class': 'p5.MonoSynth', + 'module': 'p5.sound' + }, + 'play': { + 'name': 'play', + 'params': [ + { + 'name': 'note', + 'description': 'the note you want to play, specified as a\n frequency in Hertz (Number) or as a midi\n value in Note/Octave format ("C4", "Eb3"...etc")\n See \n Tone. Defaults to 440 hz.
\n', + 'type': 'String | Number' + }, + { + 'name': 'velocity', + 'description': 'velocity of the note to play (ranging from 0 to 1)
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'secondsFromNow', + 'description': 'time from now (in seconds) at which to play
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'sustainTime', + 'description': 'time to sustain before releasing the envelope. Defaults to 0.15 seconds.
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.MonoSynth', + 'module': 'p5.sound' + }, + 'triggerAttack': { + 'params': [ + { + 'name': 'note', + 'description': 'the note you want to play, specified as a\n frequency in Hertz (Number) or as a midi\n value in Note/Octave format ("C4", "Eb3"...etc")\n See \n Tone. Defaults to 440 hz
\n', + 'type': 'String | Number' + }, + { + 'name': 'velocity', + 'description': 'velocity of the note to play (ranging from 0 to 1)
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'secondsFromNow', + 'description': 'time from now (in seconds) at which to play
\n', + 'type': 'Number', + 'optional': true + } + ], + 'name': 'triggerAttack', + 'class': 'p5.MonoSynth', + 'module': 'p5.sound' + }, + 'triggerRelease': { + 'params': [ + { + 'name': 'secondsFromNow', + 'description': 'time to trigger the release
\n', + 'type': 'Number' + } + ], + 'name': 'triggerRelease', + 'class': 'p5.MonoSynth', + 'module': 'p5.sound' + }, + 'setADSR': { + 'name': 'setADSR', + 'params': [ + { + 'name': 'attackTime', + 'description': 'Time (in seconds before envelope\n reaches Attack Level
\n', + 'type': 'Number' + }, + { + 'name': 'decayTime', + 'description': 'Time (in seconds) before envelope\n reaches Decay/Sustain Level
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'susRatio', + 'description': 'Ratio between attackLevel and releaseLevel, on a scale from 0 to 1,\n where 1.0 = attackLevel, 0.0 = releaseLevel.\n The susRatio determines the decayLevel and the level at which the\n sustain portion of the envelope will sustain.\n For example, if attackLevel is 0.4, releaseLevel is 0,\n and susAmt is 0.5, the decayLevel would be 0.2. If attackLevel is\n increased to 1.0 (using setRange
),\n then decayLevel would increase proportionally, to become 0.5.
Time in seconds from now (defaults to 0)
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.MonoSynth', + 'module': 'p5.sound' + }, + 'amp': { + 'name': 'amp', + 'params': [ + { + 'name': 'vol', + 'description': 'desired volume
\n', + 'type': 'Number' + }, + { + 'name': 'rampTime', + 'description': 'Time to reach new volume
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.MonoSynth', + 'module': 'p5.sound' + }, + 'connect': { + 'name': 'connect', + 'params': [ + { + 'name': 'unit', + 'description': 'A p5.sound or Web Audio object
\n', + 'type': 'Object' + } + ], + 'class': 'p5.MonoSynth', + 'module': 'p5.sound' + }, + 'disconnect': { + 'name': 'disconnect', + 'class': 'p5.MonoSynth', + 'module': 'p5.sound' + }, + 'dispose': { + 'name': 'dispose', + 'class': 'p5.MonoSynth', + 'module': 'p5.sound' + } + }, + 'p5.PolySynth': { + 'notes': { + 'name': 'notes', + 'class': 'p5.PolySynth', + 'module': 'p5.sound' + }, + 'polyvalue': { + 'name': 'polyvalue', + 'class': 'p5.PolySynth', + 'module': 'p5.sound' + }, + 'AudioVoice': { + 'name': 'AudioVoice', + 'class': 'p5.PolySynth', + 'module': 'p5.sound' + }, + 'play': { + 'name': 'play', + 'params': [ + { + 'name': 'note', + 'description': 'midi note to play (ranging from 0 to 127 - 60 being a middle C)
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'velocity', + 'description': 'velocity of the note to play (ranging from 0 to 1)
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'secondsFromNow', + 'description': 'time from now (in seconds) at which to play
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'sustainTime', + 'description': 'time to sustain before releasing the envelope
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.PolySynth', + 'module': 'p5.sound' + }, + 'noteADSR': { + 'name': 'noteADSR', + 'params': [ + { + 'name': 'note', + 'description': 'Midi note on which ADSR should be set.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'attackTime', + 'description': 'Time (in seconds before envelope\n reaches Attack Level
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'decayTime', + 'description': 'Time (in seconds) before envelope\n reaches Decay/Sustain Level
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'susRatio', + 'description': 'Ratio between attackLevel and releaseLevel, on a scale from 0 to 1,\n where 1.0 = attackLevel, 0.0 = releaseLevel.\n The susRatio determines the decayLevel and the level at which the\n sustain portion of the envelope will sustain.\n For example, if attackLevel is 0.4, releaseLevel is 0,\n and susAmt is 0.5, the decayLevel would be 0.2. If attackLevel is\n increased to 1.0 (using setRange
),\n then decayLevel would increase proportionally, to become 0.5.
Time in seconds from now (defaults to 0)
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.PolySynth', + 'module': 'p5.sound' + }, + 'setADSR': { + 'name': 'setADSR', + 'params': [ + { + 'name': 'attackTime', + 'description': 'Time (in seconds before envelope\n reaches Attack Level
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'decayTime', + 'description': 'Time (in seconds) before envelope\n reaches Decay/Sustain Level
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'susRatio', + 'description': 'Ratio between attackLevel and releaseLevel, on a scale from 0 to 1,\n where 1.0 = attackLevel, 0.0 = releaseLevel.\n The susRatio determines the decayLevel and the level at which the\n sustain portion of the envelope will sustain.\n For example, if attackLevel is 0.4, releaseLevel is 0,\n and susAmt is 0.5, the decayLevel would be 0.2. If attackLevel is\n increased to 1.0 (using setRange
),\n then decayLevel would increase proportionally, to become 0.5.
Time in seconds from now (defaults to 0)
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.PolySynth', + 'module': 'p5.sound' + }, + 'noteAttack': { + 'name': 'noteAttack', + 'params': [ + { + 'name': 'note', + 'description': 'midi note on which attack should be triggered.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'velocity', + 'description': 'velocity of the note to play (ranging from 0 to 1)/
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'secondsFromNow', + 'description': 'time from now (in seconds)
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.PolySynth', + 'module': 'p5.sound' + }, + 'noteRelease': { + 'name': 'noteRelease', + 'params': [ + { + 'name': 'note', + 'description': 'midi note on which attack should be triggered.\n If no value is provided, all notes will be released.
\n', + 'type': 'Number', + 'optional': true + }, + { + 'name': 'secondsFromNow', + 'description': 'time to trigger the release
\n', + 'type': 'Number', + 'optional': true + } + ], + 'class': 'p5.PolySynth', + 'module': 'p5.sound' + }, + 'connect': { + 'name': 'connect', + 'params': [ + { + 'name': 'unit', + 'description': 'A p5.sound or Web Audio object
\n', + 'type': 'Object' + } + ], + 'class': 'p5.PolySynth', + 'module': 'p5.sound' + }, + 'disconnect': { + 'name': 'disconnect', + 'class': 'p5.PolySynth', + 'module': 'p5.sound' + }, + 'dispose': { + 'name': 'dispose', + 'class': 'p5.PolySynth', + 'module': 'p5.sound' + } + } + } + }, + { + } + ], + 2: [ + function (_dereq_, module, exports) { + function _arrayWithHoles(arr) { + if (Array.isArray(arr)) return arr; + } + module.exports = _arrayWithHoles; + }, + { + } + ], + 3: [ + function (_dereq_, module, exports) { + function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { + arr2[i] = arr[i]; + } + return arr2; + } + } + module.exports = _arrayWithoutHoles; + }, + { + } + ], + 4: [ + function (_dereq_, module, exports) { + function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called'); + } + return self; + } + module.exports = _assertThisInitialized; + }, + { + } + ], + 5: [ + function (_dereq_, module, exports) { + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError('Cannot call a class as a function'); + } + } + module.exports = _classCallCheck; + }, + { + } + ], + 6: [ + function (_dereq_, module, exports) { + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ('value' in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + module.exports = _createClass; + }, + { + } + ], + 7: [ + function (_dereq_, module, exports) { + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; + } + module.exports = _defineProperty; + }, + { + } + ], + 8: [ + function (_dereq_, module, exports) { + function _getPrototypeOf(o) { + module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { + return o.__proto__ || Object.getPrototypeOf(o); + }; + return _getPrototypeOf(o); + } + module.exports = _getPrototypeOf; + }, + { + } + ], + 9: [ + function (_dereq_, module, exports) { + var setPrototypeOf = _dereq_('./setPrototypeOf'); + function _inherits(subClass, superClass) { + if (typeof superClass !== 'function' && superClass !== null) { + throw new TypeError('Super expression must either be null or a function'); + } + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + writable: true, + configurable: true + } + }); + if (superClass) setPrototypeOf(subClass, superClass); + } + module.exports = _inherits; + }, + { + './setPrototypeOf': 16 + } + ], + 10: [ + function (_dereq_, module, exports) { + function _iterableToArray(iter) { + if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === '[object Arguments]') return Array.from(iter); + } + module.exports = _iterableToArray; + }, + { + } + ], + 11: [ + function (_dereq_, module, exports) { + function _iterableToArrayLimit(arr, i) { + var _arr = [ + ]; + var _n = true; + var _d = false; + var _e = undefined; + try { + for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { + _arr.push(_s.value); + if (i && _arr.length === i) break; + } + } catch (err) { + _d = true; + _e = err; + } finally { + try { + if (!_n && _i['return'] != null) _i['return'](); + } finally { + if (_d) throw _e; + } + } + return _arr; + } + module.exports = _iterableToArrayLimit; + }, + { + } + ], + 12: [ + function (_dereq_, module, exports) { + function _nonIterableRest() { + throw new TypeError('Invalid attempt to destructure non-iterable instance'); + } + module.exports = _nonIterableRest; + }, + { + } + ], + 13: [ + function (_dereq_, module, exports) { + function _nonIterableSpread() { + throw new TypeError('Invalid attempt to spread non-iterable instance'); + } + module.exports = _nonIterableSpread; + }, + { + } + ], + 14: [ + function (_dereq_, module, exports) { + var defineProperty = _dereq_('./defineProperty'); + function _objectSpread(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : { + }; + var ownKeys = Object.keys(source); + if (typeof Object.getOwnPropertySymbols === 'function') { + ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { + return Object.getOwnPropertyDescriptor(source, sym).enumerable; + })); + } + ownKeys.forEach(function (key) { + defineProperty(target, key, source[key]); + }); + } + return target; + } + module.exports = _objectSpread; + }, + { + './defineProperty': 7 + } + ], + 15: [ + function (_dereq_, module, exports) { + var _typeof = _dereq_('../helpers/typeof'); + var assertThisInitialized = _dereq_('./assertThisInitialized'); + function _possibleConstructorReturn(self, call) { + if (call && (_typeof(call) === 'object' || typeof call === 'function')) { + return call; + } + return assertThisInitialized(self); + } + module.exports = _possibleConstructorReturn; + }, + { + '../helpers/typeof': 19, + './assertThisInitialized': 4 + } + ], + 16: [ + function (_dereq_, module, exports) { + function _setPrototypeOf(o, p) { + module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }; + return _setPrototypeOf(o, p); + } + module.exports = _setPrototypeOf; + }, + { + } + ], + 17: [ + function (_dereq_, module, exports) { + var arrayWithHoles = _dereq_('./arrayWithHoles'); + var iterableToArrayLimit = _dereq_('./iterableToArrayLimit'); + var nonIterableRest = _dereq_('./nonIterableRest'); + function _slicedToArray(arr, i) { + return arrayWithHoles(arr) || iterableToArrayLimit(arr, i) || nonIterableRest(); + } + module.exports = _slicedToArray; + }, + { + './arrayWithHoles': 2, + './iterableToArrayLimit': 11, + './nonIterableRest': 12 + } + ], + 18: [ + function (_dereq_, module, exports) { + var arrayWithoutHoles = _dereq_('./arrayWithoutHoles'); + var iterableToArray = _dereq_('./iterableToArray'); + var nonIterableSpread = _dereq_('./nonIterableSpread'); + function _toConsumableArray(arr) { + return arrayWithoutHoles(arr) || iterableToArray(arr) || nonIterableSpread(); + } + module.exports = _toConsumableArray; + }, + { + './arrayWithoutHoles': 3, + './iterableToArray': 10, + './nonIterableSpread': 13 + } + ], + 19: [ + function (_dereq_, module, exports) { + function _typeof2(obj) { + if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') { + _typeof2 = function _typeof2(obj) { + return typeof obj; + }; + } else { + _typeof2 = function _typeof2(obj) { + return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj; + }; + } + return _typeof2(obj); + } + function _typeof(obj) { + if (typeof Symbol === 'function' && _typeof2(Symbol.iterator) === 'symbol') { + module.exports = _typeof = function _typeof(obj) { + return _typeof2(obj); + }; + } else { + module.exports = _typeof = function _typeof(obj) { + return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : _typeof2(obj); + }; + } + return _typeof(obj); + } + module.exports = _typeof; + }, + { + } + ], + 20: [ + function (_dereq_, module, exports) { + 'use strict'; + exports.byteLength = byteLength; + exports.toByteArray = toByteArray; + exports.fromByteArray = fromByteArray; + var lookup = [ + ]; + var revLookup = [ + ]; + var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array; + var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + for (var i = 0, len = code.length; i < len; ++i) { + lookup[i] = code[i]; + revLookup[code.charCodeAt(i)] = i; + } // Support decoding URL-safe base64 strings, as Node.js does. + // See: https://en.wikipedia.org/wiki/Base64#URL_applications + + revLookup['-'.charCodeAt(0)] = 62; + revLookup['_'.charCodeAt(0)] = 63; + function getLens(b64) { + var len = b64.length; + if (len % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4'); + } // Trim off extra bytes after placeholder bytes are found + // See: https://github.com/beatgammit/base64-js/issues/42 + + var validLen = b64.indexOf('='); + if (validLen === - 1) validLen = len; + var placeHoldersLen = validLen === len ? 0 : 4 - validLen % 4; + return [validLen, + placeHoldersLen]; + } // base64 is 4/3 + up to two characters of the original data + + function byteLength(b64) { + var lens = getLens(b64); + var validLen = lens[0]; + var placeHoldersLen = lens[1]; + return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen; + } + function _byteLength(b64, validLen, placeHoldersLen) { + return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen; + } + function toByteArray(b64) { + var tmp; + var lens = getLens(b64); + var validLen = lens[0]; + var placeHoldersLen = lens[1]; + var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)); + var curByte = 0; + // if there are placeholders, only get up to the last complete 4 chars + var len = placeHoldersLen > 0 ? validLen - 4 : validLen; + var i; + for (i = 0; i < len; i += 4) { + tmp = revLookup[b64.charCodeAt(i)] << 18 | revLookup[b64.charCodeAt(i + 1)] << 12 | revLookup[b64.charCodeAt(i + 2)] << 6 | revLookup[b64.charCodeAt(i + 3)]; + arr[curByte++] = tmp >> 16 & 255; + arr[curByte++] = tmp >> 8 & 255; + arr[curByte++] = tmp & 255; + } + if (placeHoldersLen === 2) { + tmp = revLookup[b64.charCodeAt(i)] << 2 | revLookup[b64.charCodeAt(i + 1)] >> 4; + arr[curByte++] = tmp & 255; + } + if (placeHoldersLen === 1) { + tmp = revLookup[b64.charCodeAt(i)] << 10 | revLookup[b64.charCodeAt(i + 1)] << 4 | revLookup[b64.charCodeAt(i + 2)] >> 2; + arr[curByte++] = tmp >> 8 & 255; + arr[curByte++] = tmp & 255; + } + return arr; + } + function tripletToBase64(num) { + return lookup[num >> 18 & 63] + lookup[num >> 12 & 63] + lookup[num >> 6 & 63] + lookup[num & 63]; + } + function encodeChunk(uint8, start, end) { + var tmp; + var output = [ + ]; + for (var i = start; i < end; i += 3) { + tmp = (uint8[i] << 16 & 16711680) + (uint8[i + 1] << 8 & 65280) + (uint8[i + 2] & 255); + output.push(tripletToBase64(tmp)); + } + return output.join(''); + } + function fromByteArray(uint8) { + var tmp; + var len = uint8.length; + var extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes + var parts = [ + ]; + var maxChunkLength = 16383; // must be multiple of 3 + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk(uint8, i, i + maxChunkLength > len2 ? len2 : i + maxChunkLength)); + } // pad the end with zeros, but make sure to not forget the extra bytes + + if (extraBytes === 1) { + tmp = uint8[len - 1]; + parts.push(lookup[tmp >> 2] + lookup[tmp << 4 & 63] + '=='); + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + uint8[len - 1]; + parts.push(lookup[tmp >> 10] + lookup[tmp >> 4 & 63] + lookup[tmp << 2 & 63] + '='); + } + return parts.join(''); + } + }, + { + } + ], + 21: [ + function (_dereq_, module, exports) { + }, + { + } + ], + 22: [ + function (_dereq_, module, exports) { + (function (Buffer) { + /*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh
+ * function setup() {
+ * background('pink');
+ *
+ * // Draw a heart.
+ * fill('red');
+ * noStroke();
+ * circle(67, 67, 20);
+ * circle(83, 67, 20);
+ * triangle(91, 73, 75, 95, 59, 73);
+ *
+ * // Add a general description of the canvas.
+ * describe('A pink square with a red heart in the bottom-right corner.');
+ * }
+ *
+ *
+ * function setup() {
+ * background('pink');
+ *
+ * // Draw a heart.
+ * fill('red');
+ * noStroke();
+ * circle(67, 67, 20);
+ * circle(83, 67, 20);
+ * triangle(91, 73, 75, 95, 59, 73);
+ *
+ * // Add a general description of the canvas
+ * // and display it for debugging.
+ * describe('A pink square with a red heart in the bottom-right corner.', LABEL);
+ * }
+ *
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // The expression
+ * // frameCount % 100
+ * // causes x to increase from 0
+ * // to 99, then restart from 0.
+ * let x = frameCount % 100;
+ *
+ * // Draw the circle.
+ * fill(0, 255, 0);
+ * circle(x, 50, 40);
+ *
+ * // Add a general description of the canvas.
+ * describe(`A green circle at (${x}, 50) moves from left to right on a gray square.`);
+ * }
+ *
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // The expression
+ * // frameCount % 100
+ * // causes x to increase from 0
+ * // to 99, then restart from 0.
+ * let x = frameCount % 100;
+ *
+ * // Draw the circle.
+ * fill(0, 255, 0);
+ * circle(x, 50, 40);
+ *
+ * // Add a general description of the canvas
+ * // and display it for debugging.
+ * describe(`A green circle at (${x}, 50) moves from left to right on a gray square.`, LABEL);
+ * }
+ *
+ *
+ * function setup() {
+ * background('pink');
+ *
+ * // Describe the first element
+ * // and draw it.
+ * describeElement('Circle', 'A yellow circle in the top-left corner.');
+ * noStroke();
+ * fill('yellow');
+ * circle(25, 25, 40);
+ *
+ * // Describe the second element
+ * // and draw it.
+ * describeElement('Heart', 'A red heart in the bottom-right corner.');
+ * fill('red');
+ * circle(66.6, 66.6, 20);
+ * circle(83.2, 66.6, 20);
+ * triangle(91.2, 72.6, 75, 95, 58.6, 72.6);
+ *
+ * // Add a general description of the canvas.
+ * describe('A red heart and yellow circle over a pink background.');
+ * }
+ *
+ *
+ * function setup() {
+ * background('pink');
+ *
+ * // Describe the first element
+ * // and draw it. Display the
+ * // description for debugging.
+ * describeElement('Circle', 'A yellow circle in the top-left corner.', LABEL);
+ * noStroke();
+ * fill('yellow');
+ * circle(25, 25, 40);
+ *
+ * // Describe the second element
+ * // and draw it. Display the
+ * // description for debugging.
+ * describeElement('Heart', 'A red heart in the bottom-right corner.', LABEL);
+ * fill('red');
+ * circle(66.6, 66.6, 20);
+ * circle(83.2, 66.6, 20);
+ * triangle(91.2, 72.6, 75, 95, 58.6, 72.6);
+ *
+ * // Add a general description of the canvas.
+ * describe('A red heart and yellow circle over a pink background.');
+ * }
+ *
+ * for fallback description + this.dummyDOM.querySelector('#'.concat(cnvId)).innerHTML = html; + } else { + //create description container +
for fallback description before outputs + this.dummyDOM.querySelector('#'.concat(cnvId, 'accessibleOutput')).insertAdjacentHTML('beforebegin', html); + } + } else { + //if describeElement() has already created the container and added a table of elements + //create fallback description
before the table + this.dummyDOM.querySelector('#' + cnvId + fallbackTableId).insertAdjacentHTML('beforebegin', '
')); + } //if the container for the description exists + + this.descriptions.fallback = this.dummyDOM.querySelector('#'.concat(cnvId).concat(fallbackDescId)); + this.descriptions.fallback.innerHTML = text; + return; + } else if (type === 'label') { + //if there is no label container + if (!this.dummyDOM.querySelector('#'.concat(cnvId + labelContainer))) { + var _html = 'for label description + this.dummyDOM.querySelector('#' + cnvId).insertAdjacentHTML('afterend', _html); + } else { + //create label container +
for label description before outputs + this.dummyDOM.querySelector('#'.concat(cnvId, 'accessibleOutputLabel')).insertAdjacentHTML('beforebegin', _html); + } + } else if (this.dummyDOM.querySelector('#'.concat(cnvId + labelTableId))) { + //if describeElement() has already created the container and added a table of elements + //create label description
before the table + this.dummyDOM.querySelector('#'.concat(cnvId + labelTableId)).insertAdjacentHTML('beforebegin', '
')); + } + this.descriptions.label = this.dummyDOM.querySelector('#' + cnvId + labelDescId); + this.descriptions.label.innerHTML = text; + return; + } + }; + /* + * Helper functions for describeElement(). + */ + //check that name is not LABEL or FALLBACK and ensure text ends with colon + function _elementName(name) { + if (name === 'label' || name === 'fallback') { + throw new Error('element name should not be LABEL or FALLBACK'); + } //check if last character of string n is '.', ';', or ',' + + if (name.endsWith('.') || name.endsWith(';') || name.endsWith(',')) { + //replace last character with ':' + name = name.replace(/.$/, ':'); + } else if (!name.endsWith(':')) { + //if string n does not end with ':' + //add ':'' at the end of string + name = name + ':'; + } + return name; + } //creates HTML structure for element descriptions + + _main.default.prototype._describeElementHTML = function (type, name, text) { + var cnvId = this.canvas.id; + if (type === 'fallback') { + //if there is no description container + if (!this.dummyDOM.querySelector('#'.concat(cnvId + descContainer))) { + //if there are no accessible outputs (see textOutput() and gridOutput()) + var html = '
+ * function setup() {
+ * // Add the text description.
+ * textOutput();
+ *
+ * // Draw a couple of shapes.
+ * background(200);
+ * fill(255, 0, 0);
+ * circle(20, 20, 20);
+ * fill(0, 0, 255);
+ * square(50, 50, 50);
+ *
+ * // Add a general description of the canvas.
+ * describe('A red circle and a blue square on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Add the text description and
+ * // display it for debugging.
+ * textOutput(LABEL);
+ *
+ * // Draw a couple of shapes.
+ * background(200);
+ * fill(255, 0, 0);
+ * circle(20, 20, 20);
+ * fill(0, 0, 255);
+ * square(50, 50, 50);
+ *
+ * // Add a general description of the canvas.
+ * describe('A red circle and a blue square on a gray background.');
+ * }
+ *
+ *
+ * function draw() {
+ * // Add the text description.
+ * textOutput();
+ *
+ * // Draw a moving circle.
+ * background(200);
+ * let x = frameCount * 0.1;
+ * fill(255, 0, 0);
+ * circle(x, 20, 20);
+ * fill(0, 0, 255);
+ * square(50, 50, 50);
+ *
+ * // Add a general description of the canvas.
+ * describe('A red circle moves from left to right above a blue square.');
+ * }
+ *
+ *
+ * function draw() {
+ * // Add the text description and
+ * // display it for debugging.
+ * textOutput(LABEL);
+ *
+ * // Draw a moving circle.
+ * background(200);
+ * let x = frameCount * 0.1;
+ * fill(255, 0, 0);
+ * circle(x, 20, 20);
+ * fill(0, 0, 255);
+ * square(50, 50, 50);
+ *
+ * // Add a general description of the canvas.
+ * describe('A red circle moves from left to right above a blue square.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Add the grid description.
+ * gridOutput();
+ *
+ * // Draw a couple of shapes.
+ * background(200);
+ * fill(255, 0, 0);
+ * circle(20, 20, 20);
+ * fill(0, 0, 255);
+ * square(50, 50, 50);
+ *
+ * // Add a general description of the canvas.
+ * describe('A red circle and a blue square on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Add the grid description and
+ * // display it for debugging.
+ * gridOutput(LABEL);
+ *
+ * // Draw a couple of shapes.
+ * background(200);
+ * fill(255, 0, 0);
+ * circle(20, 20, 20);
+ * fill(0, 0, 255);
+ * square(50, 50, 50);
+ *
+ * // Add a general description of the canvas.
+ * describe('A red circle and a blue square on a gray background.');
+ * }
+ *
+ *
+ * function draw() {
+ * // Add the grid description.
+ * gridOutput();
+ *
+ * // Draw a moving circle.
+ * background(200);
+ * let x = frameCount * 0.1;
+ * fill(255, 0, 0);
+ * circle(x, 20, 20);
+ * fill(0, 0, 255);
+ * square(50, 50, 50);
+ *
+ * // Add a general description of the canvas.
+ * describe('A red circle moves from left to right above a blue square.');
+ * }
+ *
+ *
+ * function draw() {
+ * // Add the grid description and
+ * // display it for debugging.
+ * gridOutput(LABEL);
+ *
+ * // Draw a moving circle.
+ * background(200);
+ * let x = frameCount * 0.1;
+ * fill(255, 0, 0);
+ * circle(x, 20, 20);
+ * fill(0, 0, 255);
+ * square(50, 50, 50);
+ *
+ * // Add a general description of the canvas.
+ * describe('A red circle moves from left to right above a blue square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object.
+ * let c = color(0, 126, 255, 102);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'alphaValue' to 102.
+ * let alphaValue = alpha(c);
+ *
+ * // Draw the right rectangle.
+ * fill(alphaValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light blue and the right one is charcoal gray.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a color array.
+ * let c = [0, 126, 255, 102];
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'alphaValue' to 102.
+ * let alphaValue = alpha(c);
+ *
+ * // Draw the left rectangle.
+ * fill(alphaValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light blue and the right one is charcoal gray.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a CSS color string.
+ * let c = 'rgba(0, 126, 255, 0.4)';
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'alphaValue' to 102.
+ * let alphaValue = alpha(c);
+ *
+ * // Draw the right rectangle.
+ * fill(alphaValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light blue and the right one is charcoal gray.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object using RGB values.
+ * let c = color(175, 100, 220);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'blueValue' to 220.
+ * let blueValue = blue(c);
+ *
+ * // Draw the right rectangle.
+ * fill(0, 0, blueValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light purple and the right one is royal blue.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a color array.
+ * let c = [175, 100, 220];
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'blueValue' to 220.
+ * let blueValue = blue(c);
+ *
+ * // Draw the right rectangle.
+ * fill(0, 0, blueValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light purple and the right one is royal blue.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a CSS color string.
+ * let c = 'rgb(175, 100, 220)';
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'blueValue' to 220.
+ * let blueValue = blue(c);
+ *
+ * // Draw the right rectangle.
+ * fill(0, 0, blueValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light purple and the right one is royal blue.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use RGB color with values in the range 0-100.
+ * colorMode(RGB, 100);
+ *
+ * // Create a p5.Color object using RGB values.
+ * let c = color(69, 39, 86);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'blueValue' to 86.
+ * let blueValue = blue(c);
+ *
+ * // Draw the right rectangle.
+ * fill(0, 0, blueValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light purple and the right one is royal blue.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use HSB color.
+ * colorMode(HSB);
+ *
+ * // Create a p5.Color object.
+ * let c = color(0, 50, 100);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'brightValue' to 100.
+ * let brightValue = brightness(c);
+ *
+ * // Draw the right rectangle.
+ * fill(brightValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is white.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use HSB color.
+ * colorMode(HSB);
+ *
+ * // Create a color array.
+ * let c = [0, 50, 100];
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'brightValue' to 100.
+ * let brightValue = brightness(c);
+ *
+ * // Draw the right rectangle.
+ * fill(brightValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is white.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use HSB color.
+ * colorMode(HSB);
+ *
+ * // Create a CSS color string.
+ * let c = 'rgb(255, 128, 128)';
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'brightValue' to 100.
+ * let brightValue = brightness(c);
+ *
+ * // Draw the right rectangle.
+ * fill(brightValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is white.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use HSB color with values in the range 0-255.
+ * colorMode(HSB, 255);
+ *
+ * // Create a p5.Color object.
+ * let c = color(0, 127, 255);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'brightValue' to 255.
+ * let brightValue = brightness(c);
+ *
+ * // Draw the right rectangle.
+ * fill(brightValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is white.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object using RGB values.
+ * let c = color(255, 204, 0);
+ *
+ * // Draw the square.
+ * fill(c);
+ * noStroke();
+ * square(30, 20, 55);
+ *
+ * describe('A yellow square on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object using RGB values.
+ * let c1 = color(255, 204, 0);
+ *
+ * // Draw the left circle.
+ * fill(c1);
+ * noStroke();
+ * circle(25, 25, 80);
+ *
+ * // Create a p5.Color object using a grayscale value.
+ * let c2 = color(65);
+ *
+ * // Draw the right circle.
+ * fill(c2);
+ * circle(75, 75, 80);
+ *
+ * describe(
+ * 'Two circles on a gray canvas. The circle in the top-left corner is yellow and the one at the bottom-right is gray.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object using a named color.
+ * let c = color('magenta');
+ *
+ * // Draw the square.
+ * fill(c);
+ * noStroke();
+ * square(20, 20, 60);
+ *
+ * describe('A magenta square on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object using a hex color code.
+ * let c1 = color('#0f0');
+ *
+ * // Draw the left rectangle.
+ * fill(c1);
+ * noStroke();
+ * rect(0, 10, 45, 80);
+ *
+ * // Create a p5.Color object using a hex color code.
+ * let c2 = color('#00ff00');
+ *
+ * // Draw the right rectangle.
+ * fill(c2);
+ * rect(55, 10, 45, 80);
+ *
+ * describe('Two bright green rectangles on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object using a RGB color string.
+ * let c1 = color('rgb(0, 0, 255)');
+ *
+ * // Draw the top-left square.
+ * fill(c1);
+ * square(10, 10, 35);
+ *
+ * // Create a p5.Color object using a RGB color string.
+ * let c2 = color('rgb(0%, 0%, 100%)');
+ *
+ * // Draw the top-right square.
+ * fill(c2);
+ * square(55, 10, 35);
+ *
+ * // Create a p5.Color object using a RGBA color string.
+ * let c3 = color('rgba(0, 0, 255, 1)');
+ *
+ * // Draw the bottom-left square.
+ * fill(c3);
+ * square(10, 55, 35);
+ *
+ * // Create a p5.Color object using a RGBA color string.
+ * let c4 = color('rgba(0%, 0%, 100%, 1)');
+ *
+ * // Draw the bottom-right square.
+ * fill(c4);
+ * square(55, 55, 35);
+ *
+ * describe('Four blue squares in the corners of a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object using a HSL color string.
+ * let c1 = color('hsl(160, 100%, 50%)');
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c1);
+ * rect(0, 10, 45, 80);
+ *
+ * // Create a p5.Color object using a HSLA color string.
+ * let c2 = color('hsla(160, 100%, 50%, 0.5)');
+ *
+ * // Draw the right rectangle.
+ * fill(c2);
+ * rect(55, 10, 45, 80);
+ *
+ * describe('Two sea green rectangles. A darker rectangle on the left and a brighter one on the right.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object using a HSB color string.
+ * let c1 = color('hsb(160, 100%, 50%)');
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c1);
+ * rect(0, 10, 45, 80);
+ *
+ * // Create a p5.Color object using a HSBA color string.
+ * let c2 = color('hsba(160, 100%, 50%, 0.5)');
+ *
+ * // Draw the right rectangle.
+ * fill(c2);
+ * rect(55, 10, 45, 80);
+ *
+ * describe('Two green rectangles. A darker rectangle on the left and a brighter one on the right.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object using RGB values.
+ * let c1 = color(50, 55, 100);
+ *
+ * // Draw the left rectangle.
+ * fill(c1);
+ * rect(0, 10, 45, 80);
+ *
+ * // Switch the color mode to HSB.
+ * colorMode(HSB, 100);
+ *
+ * // Create a p5.Color object using HSB values.
+ * let c2 = color(50, 55, 100);
+ *
+ * // Draw the right rectangle.
+ * fill(c2);
+ * rect(55, 10, 45, 80);
+ *
+ * describe('Two blue rectangles. A darker rectangle on the left and a brighter one on the right.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object.
+ * let c = color(175, 100, 220);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'greenValue' to 100.
+ * let greenValue = green(c);
+ *
+ * // Draw the right rectangle.
+ * fill(0, greenValue, 0);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light purple and the right one is dark green.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a color array.
+ * let c = [175, 100, 220];
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'greenValue' to 100.
+ * let greenValue = green(c);
+ *
+ * // Draw the right rectangle.
+ * fill(0, greenValue, 0);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light purple and the right one is dark green.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a CSS color string.
+ * let c = 'rgb(175, 100, 220)';
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'greenValue' to 100.
+ * let greenValue = green(c);
+ *
+ * // Draw the right rectangle.
+ * fill(0, greenValue, 0);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light purple and the right one is dark green.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use RGB color with values in the range 0-100.
+ * colorMode(RGB, 100);
+ *
+ * // Create a p5.Color object using RGB values.
+ * let c = color(69, 39, 86);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'greenValue' to 39.
+ * let greenValue = green(c);
+ *
+ * // Draw the right rectangle.
+ * fill(0, greenValue, 0);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light purple and the right one is dark green.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use HSL color.
+ * colorMode(HSL);
+ *
+ * // Create a p5.Color object.
+ * let c = color(0, 50, 100);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ *
+ * // Set 'hueValue' to 0.
+ * let hueValue = hue(c);
+ *
+ * // Draw the right rectangle.
+ * fill(hueValue);
+ * rect(50, 20, 35, 60);
+ *
+ * describe(
+ * 'Two rectangles. The rectangle on the left is salmon pink and the one on the right is black.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use HSL color.
+ * colorMode(HSL);
+ *
+ * // Create a color array.
+ * let c = [0, 50, 100];
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ *
+ * // Set 'hueValue' to 0.
+ * let hueValue = hue(c);
+ *
+ * // Draw the right rectangle.
+ * fill(hueValue);
+ * rect(50, 20, 35, 60);
+ *
+ * describe(
+ * 'Two rectangles. The rectangle on the left is salmon pink and the one on the right is black.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use HSL color.
+ * colorMode(HSL);
+ *
+ * // Create a CSS color string.
+ * let c = 'rgb(255, 128, 128)';
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ *
+ * // Set 'hueValue' to 0.
+ * let hueValue = hue(c);
+ *
+ * // Draw the right rectangle.
+ * fill(hueValue);
+ * rect(50, 20, 35, 60);
+ *
+ * describe(
+ * 'Two rectangles. The rectangle on the left is salmon pink and the one on the right is black.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create p5.Color objects to interpolate between.
+ * let from = color(218, 165, 32);
+ * let to = color(72, 61, 139);
+ *
+ * // Create intermediate colors.
+ * let interA = lerpColor(from, to, 0.33);
+ * let interB = lerpColor(from, to, 0.66);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(from);
+ * rect(10, 20, 20, 60);
+ *
+ * // Draw the left-center rectangle.
+ * fill(interA);
+ * rect(30, 20, 20, 60);
+ *
+ * // Draw the right-center rectangle.
+ * fill(interB);
+ * rect(50, 20, 20, 60);
+ *
+ * // Draw the right rectangle.
+ * fill(to);
+ * rect(70, 20, 20, 60);
+ *
+ * describe(
+ * 'Four rectangles. From left to right, the rectangles are tan, brown, brownish purple, and purple.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Use HSL color.
+ * colorMode(HSL);
+ *
+ * // Create a p5.Color object using HSL values.
+ * let c = color(0, 100, 75);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'lightValue' to 75.
+ * let lightValue = lightness(c);
+ *
+ * // Draw the right rectangle.
+ * fill(lightValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is gray.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Use HSL color.
+ * colorMode(HSL);
+ *
+ * // Create a color array.
+ * let c = [0, 100, 75];
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'lightValue' to 75.
+ * let lightValue = lightness(c);
+ *
+ * // Draw the right rectangle.
+ * fill(lightValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is gray.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Use HSL color.
+ * colorMode(HSL);
+ *
+ * // Create a CSS color string.
+ * let c = 'rgb(255, 128, 128)';
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'lightValue' to 75.
+ * let lightValue = lightness(c);
+ *
+ * // Draw the right rectangle.
+ * fill(lightValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is gray.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Use HSL color with values in the range 0-255.
+ * colorMode(HSL, 255);
+ *
+ * // Create a p5.Color object using HSL values.
+ * let c = color(0, 255, 191.5);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'lightValue' to 191.5.
+ * let lightValue = lightness(c);
+ *
+ * // Draw the right rectangle.
+ * fill(lightValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is gray.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object.
+ * let c = color(175, 100, 220);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'redValue' to 175.
+ * let redValue = red(c);
+ *
+ * // Draw the right rectangle.
+ * fill(redValue, 0, 0);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light purple and the right one is red.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a color array.
+ * let c = [175, 100, 220];
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'redValue' to 175.
+ * let redValue = red(c);
+ *
+ * // Draw the right rectangle.
+ * fill(redValue, 0, 0);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light purple and the right one is red.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a CSS color string.
+ * let c = 'rgb(175, 100, 220)';
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'redValue' to 175.
+ * let redValue = red(c);
+ *
+ * // Draw the right rectangle.
+ * fill(redValue, 0, 0);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light purple and the right one is red.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use RGB color with values in the range 0-100.
+ * colorMode(RGB, 100);
+ *
+ * // Create a p5.Color object.
+ * let c = color(69, 39, 86);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'redValue' to 69.
+ * let redValue = red(c);
+ *
+ * // Draw the right rectangle.
+ * fill(redValue, 0, 0);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is light purple and the right one is red.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Use HSB color.
+ * colorMode(HSB);
+ *
+ * // Create a p5.Color object.
+ * let c = color(0, 50, 100);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'satValue' to 50.
+ * let satValue = saturation(c);
+ *
+ * // Draw the right rectangle.
+ * fill(satValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is dark gray.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Use HSB color.
+ * colorMode(HSB);
+ *
+ * // Create a color array.
+ * let c = [0, 50, 100];
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'satValue' to 100.
+ * let satValue = saturation(c);
+ *
+ * // Draw the right rectangle.
+ * fill(satValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is gray.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Use HSB color.
+ * colorMode(HSB);
+ *
+ * // Create a CSS color string.
+ * let c = 'rgb(255, 128, 128)';
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'satValue' to 100.
+ * let satValue = saturation(c);
+ *
+ * // Draw the right rectangle.
+ * fill(satValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is gray.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Use HSL color.
+ * colorMode(HSL);
+ *
+ * // Create a p5.Color object.
+ * let c = color(0, 100, 75);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'satValue' to 100.
+ * let satValue = saturation(c);
+ *
+ * // Draw the right rectangle.
+ * fill(satValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is white.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Use HSL color with values in the range 0-255.
+ * colorMode(HSL, 255);
+ *
+ * // Create a p5.Color object.
+ * let c = color(0, 255, 191.5);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ *
+ * // Set 'satValue' to 255.
+ * let satValue = saturation(c);
+ *
+ * // Draw the right rectangle.
+ * fill(satValue);
+ * rect(50, 15, 35, 70);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is white.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object.
+ * let myColor = color('darkorchid');
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the text.
+ * text(myColor.toString('#rrggbb'), 50, 50);
+ *
+ * describe('The text "#9932cc" written in purple on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object.
+ * let c = color(255, 128, 128);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ *
+ * // Change the red value.
+ * c.setRed(64);
+ *
+ * // Draw the right rectangle.
+ * fill(c);
+ * rect(50, 20, 35, 60);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is teal.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object.
+ * let c = color(255, 128, 128);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ *
+ * // Change the green value.
+ * c.setGreen(255);
+ *
+ * // Draw the right rectangle.
+ * fill(c);
+ * rect(50, 20, 35, 60);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is yellow.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object.
+ * let c = color(255, 128, 128);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ *
+ * // Change the blue value.
+ * c.setBlue(255);
+ *
+ * // Draw the right rectangle.
+ * fill(c);
+ * rect(50, 20, 35, 60);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is pale fuchsia.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object.
+ * let c = color(255, 128, 128);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ *
+ * // Change the alpha value.
+ * c.setAlpha(128);
+ *
+ * // Draw the right rectangle.
+ * fill(c);
+ * rect(50, 20, 35, 60);
+ *
+ * describe('Two rectangles. The left one is salmon pink and the right one is faded pink.');
+ * }
+ *
+ *
+ * // todo
+ * //
+ * // describe('');
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a mask.
+ * beginClip();
+ * triangle(15, 37, 30, 13, 43, 37);
+ * circle(45, 45, 7);
+ * endClip();
+ *
+ * // Draw a backing shape.
+ * square(5, 5, 45);
+ *
+ * describe('A white triangle and circle on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an inverted mask.
+ * beginClip({ invert: true });
+ * triangle(15, 37, 30, 13, 43, 37);
+ * circle(45, 45, 7);
+ * endClip();
+ *
+ * // Draw a backing shape.
+ * square(5, 5, 45);
+ *
+ * describe('A white square at the top-left corner of a gray square. The white square has a triangle and a circle cut out of it.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * noStroke();
+ *
+ * // Draw a masked shape.
+ * push();
+ * // Create a mask.
+ * beginClip();
+ * triangle(15, 37, 30, 13, 43, 37);
+ * circle(45, 45, 7);
+ * endClip();
+ *
+ * // Draw a backing shape.
+ * square(5, 5, 45);
+ * pop();
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Draw an inverted masked shape.
+ * push();
+ * // Create an inverted mask.
+ * beginClip({ invert: true });
+ * triangle(15, 37, 30, 13, 43, 37);
+ * circle(45, 45, 7);
+ * endClip();
+ *
+ * // Draw a backing shape.
+ * square(5, 5, 45);
+ * pop();
+ *
+ * describe('In the top left, a white triangle and circle. In the bottom right, a white square with a triangle and circle cut out of it.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A silhouette of a rotating torus colored fuchsia.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Create a mask.
+ * beginClip();
+ * push();
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * scale(0.5);
+ * torus(30, 15);
+ * pop();
+ * endClip();
+ *
+ * // Draw a backing shape.
+ * noStroke();
+ * fill('fuchsia');
+ * plane(100);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A silhouette of a rotating torus colored with a gradient from cyan to purple.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Create a mask.
+ * beginClip();
+ * push();
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * scale(0.5);
+ * torus(30, 15);
+ * pop();
+ * endClip();
+ *
+ * // Draw a backing shape.
+ * noStroke();
+ * beginShape(QUAD_STRIP);
+ * fill(0, 255, 255);
+ * vertex(-width / 2, -height / 2);
+ * vertex(width / 2, -height / 2);
+ * fill(100, 0, 100);
+ * vertex(-width / 2, height / 2);
+ * vertex(width / 2, height / 2);
+ * endShape();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a mask.
+ * beginClip();
+ * triangle(15, 37, 30, 13, 43, 37);
+ * circle(45, 45, 7);
+ * endClip();
+ *
+ * // Draw a backing shape.
+ * square(5, 5, 45);
+ *
+ * describe('A white triangle and circle on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a mask.
+ * clip(mask);
+ *
+ * // Draw a backing shape.
+ * square(5, 5, 45);
+ *
+ * describe('A white triangle and circle on a gray background.');
+ * }
+ *
+ * // Declare a function that defines the mask.
+ * function mask() {
+ * triangle(15, 37, 30, 13, 43, 37);
+ * circle(45, 45, 7);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an inverted mask.
+ * clip(mask, { invert: true });
+ *
+ * // Draw a backing shape.
+ * square(5, 5, 45);
+ *
+ * describe('A white square at the top-left corner of a gray square. The white square has a triangle and a circle cut out of it.');
+ * }
+ *
+ * // Declare a function that defines the mask.
+ * function mask() {
+ * triangle(15, 37, 30, 13, 43, 37);
+ * circle(45, 45, 7);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * noStroke();
+ *
+ * // Draw a masked shape.
+ * push();
+ * // Create a mask.
+ * clip(mask);
+ *
+ * // Draw a backing shape.
+ * square(5, 5, 45);
+ * pop();
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Draw an inverted masked shape.
+ * push();
+ * // Create an inverted mask.
+ * clip(mask, { invert: true });
+ *
+ * // Draw a backing shape.
+ * square(5, 5, 45);
+ * pop();
+ *
+ * describe('In the top left, a white triangle and circle. In the bottom right, a white square with a triangle and circle cut out of it.');
+ * }
+ *
+ * // Declare a function that defines the mask.
+ * function mask() {
+ * triangle(15, 37, 30, 13, 43, 37);
+ * circle(45, 45, 7);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A silhouette of a rotating torus colored fuchsia.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Create a mask.
+ * clip(mask);
+ *
+ * // Draw a backing shape.
+ * noStroke();
+ * fill('fuchsia');
+ * plane(100);
+ * }
+ *
+ * // Declare a function that defines the mask.
+ * function mask() {
+ * push();
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * scale(0.5);
+ * torus(30, 15);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A silhouette of a rotating torus colored with a gradient from cyan to purple.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Create a mask.
+ * clip(mask);
+ *
+ * // Draw a backing shape.
+ * noStroke();
+ * beginShape(QUAD_STRIP);
+ * fill(0, 255, 255);
+ * vertex(-width / 2, -height / 2);
+ * vertex(width / 2, -height / 2);
+ * fill(100, 0, 100);
+ * vertex(-width / 2, height / 2);
+ * vertex(width / 2, height / 2);
+ * endShape();
+ * }
+ *
+ * // Declare a function that defines the mask.
+ * function mask() {
+ * push();
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * scale(0.5);
+ * torus(30, 15);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // A grayscale value.
+ * background(51);
+ *
+ * describe('A canvas with a dark charcoal gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // A grayscale value and an alpha value.
+ * background(51, 0.4);
+ * describe('A canvas with a transparent gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // R, G & B values.
+ * background(255, 204, 0);
+ *
+ * describe('A canvas with a yellow background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use HSB color.
+ * colorMode(HSB);
+ *
+ * // H, S & B values.
+ * background(255, 204, 100);
+ *
+ * describe('A canvas with a royal blue background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // A CSS named color.
+ * background('red');
+ *
+ * describe('A canvas with a red background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Three-digit hex RGB notation.
+ * background('#fae');
+ *
+ * describe('A canvas with a pink background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Six-digit hex RGB notation.
+ * background('#222222');
+ *
+ * describe('A canvas with a black background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Integer RGB notation.
+ * background('rgb(0, 255, 0)');
+ *
+ * describe('A canvas with a bright green background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Integer RGBA notation.
+ * background('rgba(0, 255, 0, 0.25)');
+ *
+ * describe('A canvas with a transparent green background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Percentage RGB notation.
+ * background('rgb(100%, 0%, 10%)');
+ *
+ * describe('A canvas with a red background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Percentage RGBA notation.
+ * background('rgba(100%, 0%, 100%, 0.5)');
+ *
+ * describe('A canvas with a transparent purple background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // A p5.Color object.
+ * let c = color(0, 0, 255);
+ * background(c);
+ *
+ * describe('A canvas with a blue background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('A gray square. White circles are drawn as the user moves the mouse. The circles disappear when the user presses the mouse.');
+ * }
+ *
+ * function draw() {
+ * circle(mouseX, mouseY, 20);
+ * }
+ *
+ * function mousePressed() {
+ * clear();
+ * background(200);
+ * }
+ *
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * background(200);
+ *
+ * pg = createGraphics(60, 60);
+ * pg.background(200);
+ * pg.noStroke();
+ * pg.circle(pg.width / 2, pg.height / 2, 15);
+ * image(pg, 20, 20);
+ *
+ * describe('A white circle drawn on a gray square. The square gets smaller when the mouse is pressed.');
+ * }
+ *
+ * function mousePressed() {
+ * clear();
+ * image(pg, 20, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Fill with pure red.
+ * fill(255, 0, 0);
+ *
+ * circle(50, 50, 25);
+ *
+ * describe('A gray square with a red circle at its center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use RGB color with values in the range 0-100.
+ * colorMode(RGB, 100);
+ *
+ * // Fill with pure red.
+ * fill(100, 0, 0);
+ *
+ * circle(50, 50, 25);
+ *
+ * describe('A gray square with a red circle at its center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use HSB color.
+ * colorMode(HSB);
+ *
+ * // Fill with pure red.
+ * fill(0, 100, 100);
+ *
+ * circle(50, 50, 25);
+ *
+ * describe('A gray square with a red circle at its center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use HSL color.
+ * colorMode(HSL);
+ *
+ * // Fill with pure red.
+ * fill(0, 100, 50);
+ *
+ * circle(50, 50, 25);
+ *
+ * describe('A gray square with a red circle at its center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use RGB color with values in the range 0-100.
+ * colorMode(RGB, 100);
+ *
+ * for (let x = 0; x < 100; x += 1) {
+ * for (let y = 0; y < 100; y += 1) {
+ * stroke(x, y, 0);
+ * point(x, y);
+ * }
+ * }
+ *
+ * describe(
+ * 'A diagonal green to red gradient from bottom-left to top-right with shading transitioning to black at top-left corner.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use HSB color with values in the range 0-100.
+ * colorMode(HSB, 100);
+ *
+ * for (let x = 0; x < 100; x += 1) {
+ * for (let y = 0; y < 100; y += 1) {
+ * stroke(x, y, 100);
+ * point(x, y);
+ * }
+ * }
+ *
+ * describe('A rainbow gradient from left-to-right. Brightness transitions to white at the top.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.Color object.
+ * let myColor = color(180, 175, 230);
+ * background(myColor);
+ *
+ * // Use RGB color with values in the range 0-1.
+ * colorMode(RGB, 1);
+ *
+ * // Get the red, green, and blue color components.
+ * let redValue = red(myColor);
+ * let greenValue = green(myColor);
+ * let blueValue = blue(myColor);
+ *
+ * // Round the color components for display.
+ * redValue = round(redValue, 2);
+ * greenValue = round(greenValue, 2);
+ * blueValue = round(blueValue, 2);
+ *
+ * // Display the color components.
+ * text(`Red: ${redValue}`, 10, 10, 80, 80);
+ * text(`Green: ${greenValue}`, 10, 40, 80, 80);
+ * text(`Blue: ${blueValue}`, 10, 70, 80, 80);
+ *
+ * describe('A purple canvas with the red, green, and blue decimal values of the color written on it.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(255);
+ *
+ * // Use RGB color with alpha values in the range 0-1.
+ * colorMode(RGB, 255, 255, 255, 1);
+ *
+ * noFill();
+ * strokeWeight(4);
+ * stroke(255, 0, 10, 0.3);
+ * circle(40, 40, 50);
+ * circle(50, 60, 50);
+ *
+ * describe('Two overlapping translucent pink circle outlines.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // A grayscale value.
+ * fill(51);
+ * square(20, 20, 60);
+ *
+ * describe('A dark charcoal gray square with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // R, G & B values.
+ * fill(255, 204, 0);
+ * square(20, 20, 60);
+ *
+ * describe('A yellow square with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(100);
+ *
+ * // Use HSB color.
+ * colorMode(HSB);
+ *
+ * // H, S & B values.
+ * fill(255, 204, 100);
+ * square(20, 20, 60);
+ *
+ * describe('A royal blue square with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // A CSS named color.
+ * fill('red');
+ * square(20, 20, 60);
+ *
+ * describe('A red square with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Three-digit hex RGB notation.
+ * fill('#fae');
+ * square(20, 20, 60);
+ *
+ * describe('A pink square with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Six-digit hex RGB notation.
+ * fill('#A251FA');
+ * square(20, 20, 60);
+ *
+ * describe('A purple square with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Integer RGB notation.
+ * fill('rgb(0, 255, 0)');
+ * square(20, 20, 60);
+ *
+ * describe('A bright green square with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Integer RGBA notation.
+ * fill('rgba(0, 255, 0, 0.25)');
+ * square(20, 20, 60);
+ *
+ * describe('A soft green rectange with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Percentage RGB notation.
+ * fill('rgb(100%, 0%, 10%)');
+ * square(20, 20, 60);
+ *
+ * describe('A red square with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Percentage RGBA notation.
+ * fill('rgba(100%, 0%, 100%, 0.5)');
+ * square(20, 20, 60);
+ *
+ * describe('A dark fuchsia square with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // A p5.Color object.
+ * let c = color(0, 0, 255);
+ * fill(c);
+ * square(20, 20, 60);
+ *
+ * describe('A blue square with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw the top square.
+ * square(32, 10, 35);
+ *
+ * // Draw the bottom square.
+ * noFill();
+ * square(32, 55, 35);
+ *
+ * describe('A white square on above an empty square. Both squares have black outlines.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A purple cube wireframe spinning on a black canvas.');
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Style the box.
+ * noFill();
+ * stroke(100, 100, 240);
+ *
+ * // Rotate the coordinates.
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ *
+ * // Draw the box.
+ * box(45);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * noStroke();
+ * square(20, 20, 60);
+ *
+ * describe('A white square with no outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A pink cube with no edge outlines spinning on a black canvas.');
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Style the box.
+ * noStroke();
+ * fill(240, 150, 150);
+ *
+ * // Rotate the coordinates.
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ *
+ * // Draw the box.
+ * box(45);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // A grayscale value.
+ * strokeWeight(4);
+ * stroke(51);
+ * square(20, 20, 60);
+ *
+ * describe('A white square with a dark charcoal gray outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // R, G & B values.
+ * stroke(255, 204, 0);
+ * strokeWeight(4);
+ * square(20, 20, 60);
+ *
+ * describe('A white square with a yellow outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use HSB color.
+ * colorMode(HSB);
+ *
+ * // H, S & B values.
+ * strokeWeight(4);
+ * stroke(255, 204, 100);
+ * square(20, 20, 60);
+ *
+ * describe('A white square with a royal blue outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // A CSS named color.
+ * stroke('red');
+ * strokeWeight(4);
+ * square(20, 20, 60);
+ *
+ * describe('A white square with a red outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Three-digit hex RGB notation.
+ * stroke('#fae');
+ * strokeWeight(4);
+ * square(20, 20, 60);
+ *
+ * describe('A white square with a pink outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Six-digit hex RGB notation.
+ * stroke('#222222');
+ * strokeWeight(4);
+ * square(20, 20, 60);
+ *
+ * describe('A white square with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Integer RGB notation.
+ * stroke('rgb(0, 255, 0)');
+ * strokeWeight(4);
+ * square(20, 20, 60);
+ *
+ * describe('A whiite square with a bright green outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Integer RGBA notation.
+ * stroke('rgba(0, 255, 0, 0.25)');
+ * strokeWeight(4);
+ * square(20, 20, 60);
+ *
+ * describe('A white square with a soft green outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Percentage RGB notation.
+ * stroke('rgb(100%, 0%, 10%)');
+ * strokeWeight(4);
+ * square(20, 20, 60);
+ *
+ * describe('A white square with a red outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Percentage RGBA notation.
+ * stroke('rgba(100%, 0%, 100%, 0.5)');
+ * strokeWeight(4);
+ * square(20, 20, 60);
+ *
+ * describe('A white square with a dark fuchsia outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // A p5.Color object.
+ * stroke(color(0, 0, 255));
+ * strokeWeight(4);
+ * square(20, 20, 60);
+ *
+ * describe('A white square with a blue outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(100, 100, 250);
+ *
+ * // Draw a pink square.
+ * fill(250, 100, 100);
+ * square(20, 20, 60);
+ *
+ * // Erase a circular area.
+ * erase();
+ * circle(25, 30, 30);
+ * noErase();
+ *
+ * describe('A purple canvas with a pink square in the middle. A circle is erased from the top-left, leaving a hole.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(100, 100, 250);
+ *
+ * // Draw a pink square.
+ * fill(250, 100, 100);
+ * square(20, 20, 60);
+ *
+ * // Erase a circular area.
+ * strokeWeight(5);
+ * erase(150, 255);
+ * circle(25, 30, 30);
+ * noErase();
+ *
+ * describe('A purple canvas with a pink square in the middle. A circle at the top-left partially erases its interior and a fully erases its outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(235, 145, 15);
+ *
+ * // Draw the left rectangle.
+ * noStroke();
+ * fill(30, 45, 220);
+ * rect(30, 10, 10, 80);
+ *
+ * // Erase a circle.
+ * erase();
+ * circle(50, 50, 60);
+ * noErase();
+ *
+ * // Draw the right rectangle.
+ * rect(70, 10, 10, 80);
+ *
+ * describe('An orange canvas with two tall blue rectangles. A circular hole in the center erases the rectangle on the left but not the one on the right.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw an arc from 0 to HALF_PI.
+ * arc(50, 50, 80, 80, 0, HALF_PI);
+ *
+ * describe('The bottom-right quarter of a circle drawn in white on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Draw a line.
+ * line(0, 0, 40, 0);
+ *
+ * // Rotate a quarter turn.
+ * rotate(HALF_PI);
+ *
+ * // Draw the same line, rotated.
+ * line(0, 0, 40, 0);
+ *
+ * describe('Two black lines on a gray background. One line extends from the center to the right. The other line extends from the center to the bottom.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A red circle and a blue circle oscillate from left to right on a gray background. The red circle appears to chase the blue circle.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Calculate the x-coordinates.
+ * let x1 = 40 * sin(frameCount * 0.05);
+ * let x2 = 40 * sin(frameCount * 0.05 + HALF_PI);
+ *
+ * // Style the oscillators.
+ * noStroke();
+ *
+ * // Draw the red oscillator.
+ * fill(255, 0, 0);
+ * circle(x1, 0, 20);
+ *
+ * // Draw the blue oscillator.
+ * fill(0, 0, 255);
+ * circle(x2, 0, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw an arc from 0 to PI.
+ * arc(50, 50, 80, 80, 0, PI);
+ *
+ * describe('The bottom half of a circle drawn in white on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Draw a line.
+ * line(0, 0, 40, 0);
+ *
+ * // Rotate a half turn.
+ * rotate(PI);
+ *
+ * // Draw the same line, rotated.
+ * line(0, 0, 40, 0);
+ *
+ * describe('A horizontal black line on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A red circle and a blue circle oscillate from left to right on a gray background. The circles drift apart, then meet in the middle, over and over again.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Calculate the x-coordinates.
+ * let x1 = 40 * sin(frameCount * 0.05);
+ * let x2 = 40 * sin(frameCount * 0.05 + PI);
+ *
+ * // Style the oscillators.
+ * noStroke();
+ *
+ * // Draw the red oscillator.
+ * fill(255, 0, 0);
+ * circle(x1, 0, 20);
+ *
+ * // Draw the blue oscillator.
+ * fill(0, 0, 255);
+ * circle(x2, 0, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw an arc from 0 to QUARTER_PI.
+ * arc(50, 50, 80, 80, 0, QUARTER_PI);
+ *
+ * describe('A one-eighth slice of a circle drawn in white on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Draw a line.
+ * line(0, 0, 40, 0);
+ *
+ * // Rotate an eighth turn.
+ * rotate(QUARTER_PI);
+ *
+ * // Draw the same line, rotated.
+ * line(0, 0, 40, 0);
+ *
+ * describe('Two black lines that form a "V" opening towards the bottom-right corner of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A red circle and a blue circle oscillate from left to right on a gray background. The red circle appears to chase the blue circle.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Calculate the x-coordinates.
+ * let x1 = 40 * sin(frameCount * 0.05);
+ * let x2 = 40 * sin(frameCount * 0.05 + QUARTER_PI);
+ *
+ * // Style the oscillators.
+ * noStroke();
+ *
+ * // Draw the red oscillator.
+ * fill(255, 0, 0);
+ * circle(x1, 0, 20);
+ *
+ * // Draw the blue oscillator.
+ * fill(0, 0, 255);
+ * circle(x2, 0, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw an arc from 0 to TAU.
+ * arc(50, 50, 80, 80, 0, TAU);
+ *
+ * describe('A white circle drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Draw a line.
+ * line(0, 0, 40, 0);
+ *
+ * // Rotate a full turn.
+ * rotate(TAU);
+ *
+ * // Style the second line.
+ * strokeWeight(5);
+ *
+ * // Draw the same line, shorter and rotated.
+ * line(0, 0, 20, 0);
+ *
+ * describe(
+ * 'Two horizontal black lines on a gray background. A thick line extends from the center toward the right. A thin line extends from the end of the thick line.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A red circle with a blue center oscillates from left to right on a gray background.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Calculate the x-coordinates.
+ * let x1 = 40 * sin(frameCount * 0.05);
+ * let x2 = 40 * sin(frameCount * 0.05 + TAU);
+ *
+ * // Style the oscillators.
+ * noStroke();
+ *
+ * // Draw the red oscillator.
+ * fill(255, 0, 0);
+ * circle(x1, 0, 20);
+ *
+ * // Draw the blue oscillator, smaller.
+ * fill(0, 0, 255);
+ * circle(x2, 0, 10);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw an arc from 0 to TWO_PI.
+ * arc(50, 50, 80, 80, 0, TWO_PI);
+ *
+ * describe('A white circle drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Draw a line.
+ * line(0, 0, 40, 0);
+ *
+ * // Rotate a full turn.
+ * rotate(TWO_PI);
+ *
+ * // Style the second line.
+ * strokeWeight(5);
+ *
+ * // Draw the same line, shorter and rotated.
+ * line(0, 0, 20, 0);
+ *
+ * describe(
+ * 'Two horizontal black lines on a gray background. A thick line extends from the center toward the right. A thin line extends from the end of the thick line.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A red circle with a blue center oscillates from left to right on a gray background.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Calculate the x-coordinates.
+ * let x1 = 40 * sin(frameCount * 0.05);
+ * let x2 = 40 * sin(frameCount * 0.05 + TWO_PI);
+ *
+ * // Style the oscillators.
+ * noStroke();
+ *
+ * // Draw the red oscillator.
+ * fill(255, 0, 0);
+ * circle(x1, 0, 20);
+ *
+ * // Draw the blue oscillator, smaller.
+ * fill(0, 0, 255);
+ * circle(x2, 0, 10);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw a red arc from 0 to HALF_PI radians.
+ * fill(255, 0, 0);
+ * arc(50, 50, 80, 80, 0, HALF_PI);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Draw a blue arc from 90˚ to 180˚.
+ * fill(0, 0, 255);
+ * arc(50, 50, 80, 80, 90, 180);
+ *
+ * describe('The bottom half of a circle drawn on a gray background. The bottom-right quarter is red. The bottom-left quarter is blue.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Draw a red arc from 0˚ to 90˚.
+ * fill(255, 0, 0);
+ * arc(50, 50, 80, 80, 0, 90);
+ *
+ * // Use radians.
+ * angleMode(RADIANS);
+ *
+ * // Draw a blue arc from HALF_PI to PI.
+ * fill(0, 0, 255);
+ * arc(50, 50, 80, 80, HALF_PI, PI);
+ *
+ * describe('The bottom half of a circle drawn on a gray background. The bottom-right quarter is red. The bottom-left quarter is blue.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Prints "hello, world" to the console.
+ * print('hello, world');
+ * }
+ *
+ *
+ * function setup() {
+ * let name = 'ada';
+ * // Prints "hello, ada" to the console.
+ * print(`hello, ${name}`);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Display the value of
+ * // frameCount.
+ * textSize(30);
+ * textAlign(CENTER, CENTER);
+ * text(frameCount, 50, 50);
+ *
+ * describe('The number 0 written in black in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Set the frameRate to 30.
+ * frameRate(30);
+ *
+ * textSize(30);
+ * textAlign(CENTER, CENTER);
+ *
+ * describe('A number written in black in the middle of a gray square. Its value increases rapidly.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Display the value of
+ * // frameCount.
+ * text(frameCount, 50, 50);
+ * }
+ *
+ *
+ * let x = 0;
+ * let speed = 0.05;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Set the frameRate to 30.
+ * frameRate(30);
+ *
+ * describe('A white circle moves from left to right on a gray background. It reappears on the left side when it reaches the right side.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Use deltaTime to calculate
+ * // a change in position.
+ * let deltaX = speed * deltaTime;
+ *
+ * // Update the x variable.
+ * x += deltaX;
+ *
+ * // Reset x to 0 if it's
+ * // greater than 100.
+ * if (x > 100) {
+ * x = 0;
+ * }
+ *
+ * // Use x to set the circle's
+ * // position.
+ * circle(x, 50, 20);
+ * }
+ *
+ *
+ * // Open this example in two separate browser
+ * // windows placed side-by-side to demonstrate.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A square changes color from green to red when the browser window is out of focus.');
+ * }
+ *
+ * function draw() {
+ * // Change the background color
+ * // when the browser window
+ * // goes in/out of focus.
+ * if (focused === true) {
+ * background(0, 255, 0);
+ * } else {
+ * background(255, 0, 0);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A gray square. The cursor appears as crosshairs.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Set the cursor to crosshairs: +
+ * cursor(CROSS);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A gray square divided into quadrants. The cursor image changes when the mouse moves to each quadrant.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Divide the canvas into quadrants.
+ * line(50, 0, 50, 100);
+ * line(0, 50, 100, 50);
+ *
+ * // Change cursor based on mouse position.
+ * if (mouseX < 50 && mouseY < 50) {
+ * cursor(CROSS);
+ * } else if (mouseX > 50 && mouseY < 50) {
+ * cursor('progress');
+ * } else if (mouseX > 50 && mouseY > 50) {
+ * cursor('https://avatars0.githubusercontent.com/u/1617169?s=16');
+ * } else {
+ * cursor('grab');
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('An image of three purple curves follows the mouse. The image shifts when the mouse is pressed.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Change the cursor's active spot
+ * // when the mouse is pressed.
+ * if (mouseIsPressed === true) {
+ * cursor('https://avatars0.githubusercontent.com/u/1617169?s=16', 8, 8);
+ * } else {
+ * cursor('https://avatars0.githubusercontent.com/u/1617169?s=16');
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white circle on a gray background. The circle moves from left to right in a loop. It slows down when the mouse is pressed.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Set the x variable based
+ * // on the current frameCount.
+ * let x = frameCount % 100;
+ *
+ * // If the mouse is pressed,
+ * // decrease the frame rate.
+ * if (mouseIsPressed === true) {
+ * frameRate(10);
+ * } else {
+ * frameRate(60);
+ * }
+ *
+ * // Use x to set the circle's
+ * // position.
+ * circle(x, 50, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A number written in black on a gray background. The number decreases when the mouse is pressed.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // If the mouse is pressed, do lots
+ * // of math to slow down drawing.
+ * if (mouseIsPressed === true) {
+ * for (let i = 0; i < 1000000; i += 1) {
+ * random();
+ * }
+ * }
+ *
+ * // Get the current frame rate
+ * // and display it.
+ * let fps = frameRate();
+ * text(fps, 50, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('The number 20 written in black on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Set the frame rate to 20.
+ * frameRate(20);
+ *
+ * // Get the target frame rate and
+ * // display it.
+ * let fps = getTargetFrameRate();
+ * text(fps, 43, 54);
+ * }
+ *
+ *
+ * function setup() {
+ * // Hide the cursor.
+ * noCursor();
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * circle(mouseX, mouseY, 10);
+ *
+ * describe('A white circle on a gray background. The circle follows the mouse as it moves. The cursor is hidden.');
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ *
+ * // Display the current WebGL version.
+ * text(webglVersion, 42, 54);
+ *
+ * describe('The text "p2d" written in black on a gray background.');
+ * }
+ *
+ *
+ * let font;
+ *
+ * function preload() {
+ * // Load a font to use.
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * // Create a canvas using WEBGL mode.
+ * createCanvas(100, 50, WEBGL);
+ * background(200);
+ *
+ * // Display the current WebGL version.
+ * fill(0);
+ * textFont(font);
+ * text(webglVersion, -15, 5);
+ *
+ * describe('The text "webgl2" written in black on a gray background.');
+ * }
+ *
+ *
+ * let font;
+ *
+ * function preload() {
+ * // Load a font to use.
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * // Create a canvas using WEBGL mode.
+ * createCanvas(100, 50, WEBGL);
+ *
+ * // Set WebGL to version 1.
+ * setAttributes({ version: 1 });
+ *
+ * background(200);
+ *
+ * // Display the current WebGL version.
+ * fill(0);
+ * textFont(font);
+ * text(webglVersion, -14, 5);
+ *
+ * describe('The text "webgl" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Set the canvas' width and height
+ * // using the display's dimensions.
+ * createCanvas(displayWidth, displayHeight);
+ *
+ * background(200);
+ *
+ * describe('A gray canvas that is the same size as the display.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Set the canvas' width and height
+ * // using the display's dimensions.
+ * createCanvas(displayWidth, displayHeight);
+ *
+ * background(200);
+ *
+ * describe('A gray canvas that is the same size as the display.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Set the canvas' width and height
+ * // using the browser's dimensions.
+ * createCanvas(windowWidth, windowHeight);
+ *
+ * background(200);
+ *
+ * describe('A gray canvas that takes up the entire browser window.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Set the canvas' width and height
+ * // using the browser's dimensions.
+ * createCanvas(windowWidth, windowHeight);
+ *
+ * background(200);
+ *
+ * describe('A gray canvas that takes up the entire browser window.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(windowWidth, windowHeight);
+ *
+ * describe('A gray canvas with a white circle at its center. The canvas takes up the entire browser window. It changes size to match the browser window.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a circle at the center.
+ * circle(width / 2, height / 2, 50);
+ * }
+ *
+ * // Resize the canvas when the
+ * // browser's size changes.
+ * function windowResized() {
+ * resizeCanvas(windowWidth, windowHeight);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(windowWidth, windowHeight);
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * describe('A gray canvas that takes up the entire browser window. It changes size to match the browser window.');
+ * }
+ *
+ * function windowResized(event) {
+ * // Resize the canvas when the
+ * // browser's size changes.
+ * resizeCanvas(windowWidth, windowHeight);
+ *
+ * // Print the resize event to the console for debugging.
+ * print(event);
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ *
+ * // Display the canvas' width.
+ * text(width, 42, 54);
+ *
+ * describe('The number 100 written in black on a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(50, 100);
+ *
+ * background(200);
+ *
+ * // Display the canvas' width.
+ * text(width, 21, 54);
+ *
+ * describe('The number 50 written in black on a gray rectangle.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Display the canvas' width.
+ * text(width, 42, 54);
+ *
+ * describe('The number 100 written in black on a gray square. When the mouse is pressed, the square becomes a rectangle and the number becomes 50.');
+ * }
+ *
+ * // If the mouse is pressed, reisze
+ * // the canvas and display its new
+ * // width.
+ * function mousePressed() {
+ * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
+ * resizeCanvas(50, 100);
+ * background(200);
+ * text(width, 21, 54);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ *
+ * // Display the canvas' height.
+ * text(height, 42, 54);
+ *
+ * describe('The number 100 written in black on a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 50);
+ *
+ * background(200);
+ *
+ * // Display the canvas' height.
+ * text(height, 42, 27);
+ *
+ * describe('The number 50 written in black on a gray rectangle.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Display the canvas' height.
+ * text(height, 42, 54);
+ *
+ * describe('The number 100 written in black on a gray square. When the mouse is pressed, the square becomes a rectangle and the number becomes 50.');
+ * }
+ *
+ * // If the mouse is pressed, reisze
+ * // the canvas and display its new
+ * // height.
+ * function mousePressed() {
+ * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
+ * resizeCanvas(100, 50);
+ * background(200);
+ * text(height, 42, 27);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ *
+ * describe('A gray canvas that switches between default and full-screen display when clicked.');
+ * }
+ *
+ * // If the mouse is pressed,
+ * // toggle full-screen mode.
+ * function mousePressed() {
+ * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
+ * let fs = fullscreen();
+ * fullscreen(!fs);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * // Set the pixel density to 1.
+ * pixelDensity(1);
+ *
+ * // Create a canvas and draw
+ * // a circle.
+ * createCanvas(100, 100);
+ * background(200);
+ * circle(50, 50, 70);
+ *
+ * describe('A fuzzy white circle on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Set the pixel density to 3.
+ * pixelDensity(3);
+ *
+ * // Create a canvas, paint the
+ * // background, and draw a
+ * // circle.
+ * createCanvas(100, 100);
+ * background(200);
+ * circle(50, 50, 70);
+ *
+ * describe('A sharp white circle on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Set the pixel density to 1.
+ * pixelDensity(1);
+ *
+ * // Create a canvas and draw
+ * // a circle.
+ * createCanvas(100, 100);
+ * background(200);
+ * circle(50, 50, 70);
+ *
+ * describe('A fuzzy white circle drawn on a gray background. The circle becomes sharper when the mouse is pressed.');
+ * }
+ *
+ * function mousePressed() {
+ * // Get the current display density.
+ * let d = displayDensity();
+ *
+ * // Use the display density to set
+ * // the sketch's pixel density.
+ * pixelDensity(d);
+ *
+ * // Paint the background and
+ * // draw a circle.
+ * background(200);
+ * circle(50, 50, 70);
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ *
+ * // Get the sketch's URL
+ * // and display it.
+ * let url = getURL();
+ * textWrap(CHAR);
+ * text(url, 0, 40, 100);
+ *
+ * describe('The URL "https://p5js.org/reference/#/p5/getURL" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ *
+ * // Get the sketch's URL path
+ * // and display the first
+ * // part.
+ * let path = getURLPath();
+ * text(path[0], 25, 54);
+ *
+ * describe('The word "reference" written in black on a gray background.');
+ * }
+ *
+ *
+ * // Imagine this sketch is hosted at the following URL:
+ * // https://p5js.org?year=2014&month=May&day=15
+ *
+ * function setup() {
+ * background(200);
+ *
+ * // Get the sketch's URL
+ * // parameters and display
+ * // them.
+ * let params = getURLParams();
+ * text(params.day, 10, 20);
+ * text(params.month, 10, 40);
+ * text(params.year, 10, 60);
+ *
+ * describe('The text "15", "May", and "2014" written in black on separate lines.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load an image and create a p5.Image object.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Draw the image.
+ * image(img, 0, 0);
+ *
+ * describe('A red brick wall.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw the circle.
+ * circle(50, 50, 40);
+ *
+ * describe('A white circle on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Paint the background once.
+ * background(200);
+ *
+ * describe(
+ * 'A white circle on a gray background. The circle follows the mouse as the user moves, leaving a trail.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Draw circles repeatedly.
+ * circle(mouseX, mouseY, 40);
+ * }
+ *
+ *
+ * let img;
+ *
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Draw the image.
+ * image(img, 0, 0);
+ *
+ * describe(
+ * 'A white circle on a brick wall. The circle follows the mouse as the user moves, leaving a trail.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Style the circle.
+ * noStroke();
+ *
+ * // Draw the circle.
+ * circle(mouseX, mouseY, 10);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Paint the background once.
+ * background(200);
+ *
+ * describe(
+ * 'A white circle on a gray background. The circle follows the mouse as the user moves, leaving a trail.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Draw circles repeatedly.
+ * circle(mouseX, mouseY, 40);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A white circle on a gray background. The circle follows the mouse as the user moves.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Paint the background repeatedly.
+ * background(200);
+ *
+ * // Draw circles repeatedly.
+ * circle(mouseX, mouseY, 40);
+ * }
+ *
+ *
+ * // Double-click the canvas to change the circle's color.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A white circle on a gray background. The circle follows the mouse as the user moves. The circle changes color to pink when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Paint the background repeatedly.
+ * background(200);
+ *
+ * // Draw circles repeatedly.
+ * circle(mouseX, mouseY, 40);
+ * }
+ *
+ * // Change the fill color when the user double-clicks.
+ * function doubleClicked() {
+ * fill('deeppink');
+ * }
+ *
+ *
+ * // Double-click to remove the canvas.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A white circle on a gray background. The circle follows the mouse as the user moves. The sketch disappears when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Paint the background repeatedly.
+ * background(200);
+ *
+ * // Draw circles repeatedly.
+ * circle(mouseX, mouseY, 40);
+ * }
+ *
+ * // Remove the sketch when the user double-clicks.
+ * function doubleClicked() {
+ * remove();
+ * }
+ *
+ *
+ * // Disable the FES.
+ * p5.disableFriendlyErrors = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // The circle() function requires three arguments. The
+ * // next line would normally display a friendly error that
+ * // points this out. Instead, nothing happens and it fails
+ * // silently.
+ * circle(50, 50);
+ *
+ * describe('A gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a button element and
+ * // place it beneath the canvas.
+ * let btn = createButton('change');
+ * btn.position(0, 100);
+ *
+ * // Call randomColor() when
+ * // the button is pressed.
+ * btn.mousePressed(randomColor);
+ *
+ * describe('A gray square with a button that says "change" beneath it. The square changes color when the user presses the button.');
+ * }
+ *
+ * // Paint the background either
+ * // red, yellow, blue, or green.
+ * function randomColor() {
+ * let c = random(['red', 'yellow', 'blue', 'green']);
+ * background(c);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the border style for the
+ * // canvas.
+ * cnv.elt.style.border = '5px dashed deeppink';
+ *
+ * describe('A gray square with a pink border drawn with dashed lines.');
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ *
+ * // Create a div element.
+ * let div = createDiv();
+ *
+ * // Place the div in the top-left corner.
+ * div.position(10, 20);
+ *
+ * // Set its width and height.
+ * div.size(80, 60);
+ *
+ * // Set its background color to white
+ * div.style('background-color', 'white');
+ *
+ * // Align any text to the center.
+ * div.style('text-align', 'center');
+ *
+ * // Set its ID to "container".
+ * div.id('container');
+ *
+ * // Create a paragraph element.
+ * let p = createP('p5*js');
+ *
+ * // Make the div its parent
+ * // using its ID "container".
+ * p.parent('container');
+ *
+ * describe('The text "p5*js" written in black at the center of a white rectangle. The rectangle is inside a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ *
+ * // Create rectangular div element.
+ * let div = createDiv();
+ *
+ * // Place the div in the top-left corner.
+ * div.position(10, 20);
+ *
+ * // Set its width and height.
+ * div.size(80, 60);
+ *
+ * // Set its background color and align
+ * // any text to the center.
+ * div.style('background-color', 'white');
+ * div.style('text-align', 'center');
+ *
+ * // Create a paragraph element.
+ * let p = createP('p5*js');
+ *
+ * // Make the div its parent.
+ * p.parent(div);
+ *
+ * describe('The text "p5*js" written in black at the center of a white rectangle. The rectangle is inside a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ *
+ * // Create rectangular div element.
+ * let div = createDiv();
+ *
+ * // Place the div in the top-left corner.
+ * div.position(10, 20);
+ *
+ * // Set its width and height.
+ * div.size(80, 60);
+ *
+ * // Set its background color and align
+ * // any text to the center.
+ * div.style('background-color', 'white');
+ * div.style('text-align', 'center');
+ *
+ * // Create a paragraph element.
+ * let p = createP('p5*js');
+ *
+ * // Make the div its parent
+ * // using the underlying
+ * // HTMLElement.
+ * p.parent(div.elt);
+ *
+ * describe('The text "p5*js" written in black at the center of a white rectangle. The rectangle is inside a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the canvas' ID
+ * // to "mycanvas".
+ * cnv.id('mycanvas');
+ *
+ * // Get the canvas' ID.
+ * let id = cnv.id();
+ * text(id, 24, 54);
+ *
+ * describe('The text "mycanvas" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Add the class "small" to the
+ * // canvas element.
+ * cnv.class('small');
+ *
+ * // Get the canvas element's class
+ * // and display it.
+ * let c = cnv.class();
+ * text(c, 35, 54);
+ *
+ * describe('The word "small" written in black on a gray canvas.');
+ *
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call randomColor() when the canvas
+ * // is pressed.
+ * cnv.mousePressed(randomColor);
+ *
+ * describe('A gray square changes color when the mouse is pressed.');
+ * }
+ *
+ * // Paint the background either
+ * // red, yellow, blue, or green.
+ * function randomColor() {
+ * let c = random(['red', 'yellow', 'blue', 'green']);
+ * background(c);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call randomColor() when the
+ * // canvas is double-clicked.
+ * cnv.doubleClicked(randomColor);
+ *
+ * describe('A gray square changes color when the user double-clicks the canvas.');
+ * }
+ *
+ * // Paint the background either
+ * // red, yellow, blue, or green.
+ * function randomColor() {
+ * let c = random(['red', 'yellow', 'blue', 'green']);
+ * background(c);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call randomColor() when the
+ * // mouse wheel moves.
+ * cnv.mouseWheel(randomColor);
+ *
+ * describe('A gray square changes color when the user scrolls the mouse wheel over the canvas.');
+ * }
+ *
+ * // Paint the background either
+ * // red, yellow, blue, or green.
+ * function randomColor() {
+ * let c = random(['red', 'yellow', 'blue', 'green']);
+ * background(c);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call changeBackground() when the
+ * // mouse wheel moves.
+ * cnv.mouseWheel(changeBackground);
+ *
+ * describe('A gray square. When the mouse wheel scrolls over the square, it changes color and displays shapes.');
+ * }
+ *
+ * function changeBackground(event) {
+ * // Change the background color
+ * // based on deltaY.
+ * if (event.deltaY > 0) {
+ * background('deeppink');
+ * } else if (event.deltaY < 0) {
+ * background('cornflowerblue');
+ * } else {
+ * background(200);
+ * }
+ *
+ * // Draw a shape based on deltaX.
+ * if (event.deltaX > 0) {
+ * circle(50, 50, 20);
+ * } else if (event.deltaX < 0) {
+ * square(40, 40, 20);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call randomColor() when a
+ * // mouse press ends.
+ * cnv.mouseReleased(randomColor);
+ *
+ * describe('A gray square changes color when the user releases a mouse press.');
+ * }
+ *
+ * // Paint the background either
+ * // red, yellow, blue, or green.
+ * function randomColor() {
+ * let c = random(['red', 'yellow', 'blue', 'green']);
+ * background(c);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call randomColor() when a
+ * // mouse press ends.
+ * cnv.mouseClicked(randomColor);
+ *
+ * describe('A gray square changes color when the user releases a mouse press.');
+ * }
+ *
+ * // Paint the background either
+ * // red, yellow, blue, or green.
+ * function randomColor() {
+ * let c = random(['red', 'yellow', 'blue', 'green']);
+ * background(c);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call randomColor() when the
+ * // mouse moves.
+ * cnv.mouseMoved(randomColor);
+ *
+ * describe('A gray square changes color when the mouse moves over the canvas.');
+ * }
+ *
+ * // Paint the background either
+ * // red, yellow, blue, or green.
+ * function randomColor() {
+ * let c = random(['red', 'yellow', 'blue', 'green']);
+ * background(c);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call randomColor() when the
+ * // mouse moves onto the canvas.
+ * cnv.mouseOver(randomColor);
+ *
+ * describe('A gray square changes color when the mouse moves onto the canvas.');
+ * }
+ *
+ * // Paint the background either
+ * // red, yellow, blue, or green.
+ * function randomColor() {
+ * let c = random(['red', 'yellow', 'blue', 'green']);
+ * background(c);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call randomColor() when the
+ * // mouse moves off the canvas.
+ * cnv.mouseOut(randomColor);
+ *
+ * describe('A gray square changes color when the mouse moves off the canvas.');
+ * }
+ *
+ * // Paint the background either
+ * // red, yellow, blue, or green.
+ * function randomColor() {
+ * let c = random(['red', 'yellow', 'blue', 'green']);
+ * background(c);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call randomColor() when the
+ * // user touches the canvas.
+ * cnv.touchStarted(randomColor);
+ *
+ * describe('A gray square changes color when the user touches the canvas.');
+ * }
+ *
+ * // Paint the background either
+ * // red, yellow, blue, or green.
+ * function randomColor() {
+ * let c = random(['red', 'yellow', 'blue', 'green']);
+ * background(c);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call randomColor() when the
+ * // user touches the canvas
+ * // and moves.
+ * cnv.touchMoved(randomColor);
+ *
+ * describe('A gray square changes color when the user touches the canvas and moves.');
+ * }
+ *
+ * // Paint the background either
+ * // red, yellow, blue, or green.
+ * function randomColor() {
+ * let c = random(['red', 'yellow', 'blue', 'green']);
+ * background(c);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call randomColor() when the
+ * // user touches the canvas,
+ * // then lifts their finger.
+ * cnv.touchEnded(randomColor);
+ *
+ * describe('A gray square changes color when the user touches the canvas, then lifts their finger.');
+ * }
+ *
+ * // Paint the background either
+ * // red, yellow, blue, or green.
+ * function randomColor() {
+ * let c = random(['red', 'yellow', 'blue', 'green']);
+ * background(c);
+ * }
+ *
+ *
+ * // Drag a file over the canvas to test.
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call helloFile() when a
+ * // file is dragged over
+ * // the canvas.
+ * cnv.dragOver(helloFile);
+ *
+ * describe('A gray square. The text "hello, file" appears when a file is dragged over the square.');
+ * }
+ *
+ * function helloFile() {
+ * text('hello, file', 50, 50);
+ * }
+ *
+ *
+ * // Drag a file over, then off
+ * // the canvas to test.
+ *
+ * function setup() {
+ * // Create a canvas element and
+ * // assign it to cnv.
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call byeFile() when a
+ * // file is dragged over,
+ * // then off the canvas.
+ * cnv.dragLeave(byeFile);
+ *
+ * describe('A gray square. The text "bye, file" appears when a file is dragged over, then off the square.');
+ * }
+ *
+ * function byeFile() {
+ * text('bye, file', 50, 50);
+ * }
+ *
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.Graphics object.
+ * pg = createGraphics(50, 50);
+ *
+ * // Draw to the p5.Graphics object.
+ * pg.background(100);
+ * pg.circle(25, 25, 20);
+ *
+ * describe('A dark gray square with a white circle at its center drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Display the p5.Graphics object.
+ * image(pg, 25, 25);
+ * }
+ *
+ *
+ * // Click the canvas to display the graphics buffer.
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.Graphics object.
+ * pg = createGraphics(50, 50);
+ *
+ * describe('A square appears on a gray background when the user presses the mouse. The square cycles between white and black.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the background color.
+ * let bg = frameCount % 255;
+ *
+ * // Draw to the p5.Graphics object.
+ * pg.background(bg);
+ *
+ * // Display the p5.Graphics object while
+ * // the user presses the mouse.
+ * if (mouseIsPressed === true) {
+ * image(pg, 25, 25);
+ * }
+ * }
+ *
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.Graphics object.
+ * pg = createGraphics(60, 60);
+ *
+ * describe('A white circle moves downward slowly within a dark square. The circle resets at the top of the dark square when the user presses the mouse.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the p5.Graphics object's coordinate system.
+ * // The translation accumulates; the white circle moves.
+ * pg.translate(0, 0.1);
+ *
+ * // Draw to the p5.Graphics object.
+ * pg.background(100);
+ * pg.circle(30, 0, 10);
+ *
+ * // Display the p5.Graphics object.
+ * image(pg, 20, 20);
+ *
+ * // Translate the main canvas' coordinate system.
+ * // The translation doesn't accumulate; the dark
+ * // square is always in the same place.
+ * translate(0, 0.1);
+ *
+ * // Reset the p5.Graphics object when the
+ * // user presses the mouse.
+ * if (mouseIsPressed === true) {
+ * pg.reset();
+ * }
+ * }
+ *
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.Graphics object.
+ * pg = createGraphics(60, 60);
+ *
+ * describe('A white circle at the center of a dark gray square. The image is drawn on a light gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the p5.Graphics object's coordinate system.
+ * pg.translate(30, 30);
+ *
+ * // Draw to the p5.Graphics object.
+ * pg.background(100);
+ * pg.circle(0, 0, 10);
+ *
+ * // Display the p5.Graphics object.
+ * image(pg, 20, 20);
+ *
+ * // Reset the p5.Graphics object automatically.
+ * pg.reset();
+ * }
+ *
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.Graphics object using WebGL mode.
+ * pg = createGraphics(100, 100, WEBGL);
+ *
+ * describe("A sphere lit from above with a red light. The sphere's surface becomes glossy while the user clicks and holds the mouse.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Add a red point light from the top-right.
+ * pg.pointLight(255, 0, 0, 50, -100, 50);
+ *
+ * // Style the sphere.
+ * // It should appear glossy when the
+ * // lighting values are reset.
+ * pg.noStroke();
+ * pg.specularMaterial(255);
+ * pg.shininess(100);
+ *
+ * // Draw the sphere.
+ * pg.sphere(30);
+ *
+ * // Display the p5.Graphics object.
+ * image(pg, -50, -50);
+ *
+ * // Reset the p5.Graphics object when
+ * // the user presses the mouse.
+ * if (mouseIsPressed === true) {
+ * pg.reset();
+ * }
+ * }
+ *
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.Graphics object using WebGL mode.
+ * pg = createGraphics(100, 100, WEBGL);
+ *
+ * describe('A sphere with a glossy surface is lit from the top-right by a red light.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Add a red point light from the top-right.
+ * pg.pointLight(255, 0, 0, 50, -100, 50);
+ *
+ * // Style the sphere.
+ * pg.noStroke();
+ * pg.specularMaterial(255);
+ * pg.shininess(100);
+ *
+ * // Draw the sphere.
+ * pg.sphere(30);
+ *
+ * // Display the p5.Graphics object.
+ * image(pg, 0, 0);
+ *
+ * // Reset the p5.Graphics object automatically.
+ * pg.reset();
+ * }
+ *
+ *
+ * // Double-click to remove the p5.Graphics object.
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.Graphics object.
+ * pg = createGraphics(60, 60);
+ *
+ * // Draw to the p5.Graphics object.
+ * pg.background(100);
+ * pg.circle(30, 30, 20);
+ *
+ * describe('A white circle at the center of a dark gray square disappears when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Display the p5.Graphics object if
+ * // it's available.
+ * if (pg) {
+ * image(pg, 20, 20);
+ * }
+ * }
+ *
+ * // Remove the p5.Graphics object when the
+ * // the user double-clicks.
+ * function doubleClicked() {
+ * // Remove the p5.Graphics object from the web page.
+ * pg.remove();
+ *
+ * // Delete the p5.Graphics object from CPU memory.
+ * pg = undefined;
+ * }
+ *
+ *
+ * // Click and hold a mouse button to change shapes.
+ *
+ * let pg;
+ * let torusLayer;
+ * let boxLayer;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.Graphics object using WebGL mode.
+ * pg = createGraphics(100, 100, WEBGL);
+ *
+ * // Create the p5.Framebuffer objects.
+ * torusLayer = pg.createFramebuffer();
+ * boxLayer = pg.createFramebuffer();
+ *
+ * describe('A grid of white toruses rotating against a dark gray background. The shapes become boxes while the user holds a mouse button.');
+ * }
+ *
+ * function draw() {
+ * // Update and draw the layers offscreen.
+ * drawTorus();
+ * drawBox();
+ *
+ * // Choose the layer to display.
+ * let layer;
+ * if (mouseIsPressed === true) {
+ * layer = boxLayer;
+ * } else {
+ * layer = torusLayer;
+ * }
+ *
+ * // Draw to the p5.Graphics object.
+ * pg.background(50);
+ *
+ * // Iterate from left to right.
+ * for (let x = -50; x < 50; x += 25) {
+ * // Iterate from top to bottom.
+ * for (let y = -50; y < 50; y += 25) {
+ * // Draw the layer to the p5.Graphics object
+ * pg.image(layer, x, y, 25, 25);
+ * }
+ * }
+ *
+ * // Display the p5.Graphics object.
+ * image(pg, 0, 0);
+ * }
+ *
+ * // Update and draw the torus layer offscreen.
+ * function drawTorus() {
+ * // Start drawing to the torus p5.Framebuffer.
+ * torusLayer.begin();
+ *
+ * // Clear the drawing surface.
+ * pg.clear();
+ *
+ * // Turn on the lights.
+ * pg.lights();
+ *
+ * // Rotate the coordinate system.
+ * pg.rotateX(frameCount * 0.01);
+ * pg.rotateY(frameCount * 0.01);
+ *
+ * // Style the torus.
+ * pg.noStroke();
+ *
+ * // Draw the torus.
+ * pg.torus(20);
+ *
+ * // Start drawing to the torus p5.Framebuffer.
+ * torusLayer.end();
+ * }
+ *
+ * // Update and draw the box layer offscreen.
+ * function drawBox() {
+ * // Start drawing to the box p5.Framebuffer.
+ * boxLayer.begin();
+ *
+ * // Clear the drawing surface.
+ * pg.clear();
+ *
+ * // Turn on the lights.
+ * pg.lights();
+ *
+ * // Rotate the coordinate system.
+ * pg.rotateX(frameCount * 0.01);
+ * pg.rotateY(frameCount * 0.01);
+ *
+ * // Style the box.
+ * pg.noStroke();
+ *
+ * // Draw the box.
+ * pg.box(30);
+ *
+ * // Start drawing to the box p5.Framebuffer.
+ * boxLayer.end();
+ * }
+ *
+ *
+ * // Click and hold a mouse button to change shapes.
+ *
+ * let pg;
+ * let torusLayer;
+ * let boxLayer;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create an options object.
+ * let options = { width: 25, height: 25 };
+ *
+ * // Create a p5.Graphics object using WebGL mode.
+ * pg = createGraphics(100, 100, WEBGL);
+ *
+ * // Create the p5.Framebuffer objects.
+ * // Use options for configuration.
+ * torusLayer = pg.createFramebuffer(options);
+ * boxLayer = pg.createFramebuffer(options);
+ *
+ * describe('A grid of white toruses rotating against a dark gray background. The shapes become boxes while the user holds a mouse button.');
+ * }
+ *
+ * function draw() {
+ * // Update and draw the layers offscreen.
+ * drawTorus();
+ * drawBox();
+ *
+ * // Choose the layer to display.
+ * let layer;
+ * if (mouseIsPressed === true) {
+ * layer = boxLayer;
+ * } else {
+ * layer = torusLayer;
+ * }
+ *
+ * // Draw to the p5.Graphics object.
+ * pg.background(50);
+ *
+ * // Iterate from left to right.
+ * for (let x = -50; x < 50; x += 25) {
+ * // Iterate from top to bottom.
+ * for (let y = -50; y < 50; y += 25) {
+ * // Draw the layer to the p5.Graphics object
+ * pg.image(layer, x, y);
+ * }
+ * }
+ *
+ * // Display the p5.Graphics object.
+ * image(pg, 0, 0);
+ * }
+ *
+ * // Update and draw the torus layer offscreen.
+ * function drawTorus() {
+ * // Start drawing to the torus p5.Framebuffer.
+ * torusLayer.begin();
+ *
+ * // Clear the drawing surface.
+ * pg.clear();
+ *
+ * // Turn on the lights.
+ * pg.lights();
+ *
+ * // Rotate the coordinate system.
+ * pg.rotateX(frameCount * 0.01);
+ * pg.rotateY(frameCount * 0.01);
+ *
+ * // Style the torus.
+ * pg.noStroke();
+ *
+ * // Draw the torus.
+ * pg.torus(5, 2.5);
+ *
+ * // Start drawing to the torus p5.Framebuffer.
+ * torusLayer.end();
+ * }
+ *
+ * // Update and draw the box layer offscreen.
+ * function drawBox() {
+ * // Start drawing to the box p5.Framebuffer.
+ * boxLayer.begin();
+ *
+ * // Clear the drawing surface.
+ * pg.clear();
+ *
+ * // Turn on the lights.
+ * pg.lights();
+ *
+ * // Rotate the coordinate system.
+ * pg.rotateX(frameCount * 0.01);
+ * pg.rotateY(frameCount * 0.01);
+ *
+ * // Style the box.
+ * pg.noStroke();
+ *
+ * // Draw the box.
+ * pg.box(7.5);
+ *
+ * // Start drawing to the box p5.Framebuffer.
+ * boxLayer.end();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw a diagonal line.
+ * line(0, 0, width, height);
+ *
+ * describe('A diagonal line drawn from top-left to bottom-right on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 50);
+ *
+ * background(200);
+ *
+ * // Draw a diagonal line.
+ * line(0, 0, width, height);
+ *
+ * describe('A diagonal line drawn from top-left to bottom-right on a gray background.');
+ * }
+ *
+ *
+ * // Use WebGL mode.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Draw a diagonal line.
+ * line(-width / 2, -height / 2, width / 2, height / 2);
+ *
+ * describe('A diagonal line drawn from top-left to bottom-right on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Render object.
+ * let cnv = createCanvas(50, 50);
+ *
+ * // Position the canvas.
+ * cnv.position(10, 20);
+ *
+ * background(200);
+ *
+ * // Draw a diagonal line.
+ * line(0, 0, width, height);
+ *
+ * describe('A diagonal line drawn from top-left to bottom-right on a gray background.');
+ * }
+ *
+ *
+ * // Double-click to resize the canvas.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A white circle drawn on a gray background. The canvas shrinks by half the first time the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a circle at the center of the canvas.
+ * circle(width / 2, height / 2, 20);
+ * }
+ *
+ * // Resize the canvas when the user double-clicks.
+ * function doubleClicked() {
+ * resizeCanvas(50, 50);
+ * }
+ *
+ *
+ * // Resize the web browser to change the canvas size.
+ *
+ * function setup() {
+ * createCanvas(windowWidth, windowHeight);
+ *
+ * describe('A white circle drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a circle at the center of the canvas.
+ * circle(width / 2, height / 2, 20);
+ * }
+ *
+ * // Always resize the canvas to fill the browser window.
+ * function windowResized() {
+ * resizeCanvas(windowWidth, windowHeight);
+ * }
+ *
+ *
+ * function setup() {
+ * noCanvas();
+ * }
+ *
+ *
+ * // Double-click to draw the contents of the graphics buffer.
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create the p5.Graphics object.
+ * pg = createGraphics(50, 50);
+ *
+ * // Draw to the graphics buffer.
+ * pg.background(100);
+ * pg.circle(pg.width / 2, pg.height / 2, 20);
+ *
+ * describe('A gray square. A smaller, darker square with a white circle at its center appears when the user double-clicks.');
+ * }
+ *
+ * // Display the graphics buffer when the user double-clicks.
+ * function doubleClicked() {
+ * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
+ * image(pg, 25, 25);
+ * }
+ * }
+ *
+ *
+ * // Double-click to draw the contents of the graphics buffer.
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create the p5.Graphics object in WebGL mode.
+ * pg = createGraphics(50, 50, WEBGL);
+ *
+ * // Draw to the graphics buffer.
+ * pg.background(100);
+ * pg.lights();
+ * pg.noStroke();
+ * pg.rotateX(QUARTER_PI);
+ * pg.rotateY(QUARTER_PI);
+ * pg.torus(15, 5);
+ *
+ * describe('A gray square. A smaller, darker square with a white torus at its center appears when the user double-clicks.');
+ * }
+ *
+ * // Display the graphics buffer when the user double-clicks.
+ * function doubleClicked() {
+ * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
+ * image(pg, 25, 25);
+ * }
+ * }
+ *
+ *
+ * let myBuffer;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Framebuffer object.
+ * myBuffer = createFramebuffer();
+ *
+ * describe('A grid of white toruses rotating against a dark gray background.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Start drawing to the p5.Framebuffer object.
+ * myBuffer.begin();
+ *
+ * // Clear the drawing surface.
+ * clear();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Rotate the coordinate system.
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ *
+ * // Style the torus.
+ * noStroke();
+ *
+ * // Draw the torus.
+ * torus(20);
+ *
+ * // Stop drawing to the p5.Framebuffer object.
+ * myBuffer.end();
+ *
+ * // Iterate from left to right.
+ * for (let x = -50; x < 50; x += 25) {
+ * // Iterate from top to bottom.
+ * for (let y = -50; y < 50; y += 25) {
+ * // Draw the p5.Framebuffer object to the canvas.
+ * image(myBuffer, x, y, 25, 25);
+ * }
+ * }
+ * }
+ *
+ *
+ * let myBuffer;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create an options object.
+ * let options = { width: 25, height: 25 };
+ *
+ * // Create a p5.Framebuffer object.
+ * // Use options for configuration.
+ * myBuffer = createFramebuffer(options);
+ *
+ * describe('A grid of white toruses rotating against a dark gray background.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Start drawing to the p5.Framebuffer object.
+ * myBuffer.begin();
+ *
+ * // Clear the drawing surface.
+ * clear();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Rotate the coordinate system.
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ *
+ * // Style the torus.
+ * noStroke();
+ *
+ * // Draw the torus.
+ * torus(5, 2.5);
+ *
+ * // Stop drawing to the p5.Framebuffer object.
+ * myBuffer.end();
+ *
+ * // Iterate from left to right.
+ * for (let x = -50; x < 50; x += 25) {
+ * // Iterate from top to bottom.
+ * for (let y = -50; y < 50; y += 25) {
+ * // Draw the p5.Framebuffer object to the canvas.
+ * image(myBuffer, x, y);
+ * }
+ * }
+ * }
+ *
+ *
+ * let previous;
+ * let current;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the p5.Framebuffer objects.
+ * prev = createFramebuffer({ format: FLOAT });
+ * current = createFramebuffer({ format: FLOAT });
+ *
+ * describe(
+ * 'A multicolor box drifts from side to side on a white background. It leaves a trail that fades over time.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Set the previous p5.Framebuffer to the
+ * // current one so it can be used as a texture.
+ * previous = current;
+ *
+ * // Start drawing to the current p5.Framebuffer.
+ * current.begin();
+ *
+ * // Paint the background.
+ * background(255);
+ *
+ * // Draw the previous p5.Framebuffer.
+ * // Clear the depth buffer so the previous
+ * // frame doesn't block the current one.
+ * push();
+ * tint(255, 250);
+ * image(previous, -50, -50);
+ * clearDepth();
+ * pop();
+ *
+ * // Draw the box on top of the previous frame.
+ * push();
+ * let x = 25 * sin(frameCount * 0.01);
+ * let y = 25 * sin(frameCount * 0.02);
+ * translate(x, y, 0);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * normalMaterial();
+ * box(12);
+ * pop();
+ *
+ * // Stop drawing to the current p5.Framebuffer.
+ * current.end();
+ *
+ * // Display the current p5.Framebuffer.
+ * image(current, -50, -50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use the default blend mode.
+ * blendMode(BLEND);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A blue line and a red line form an X on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(ADD);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A faint blue line and a faint red line form an X on a gray background. The area where they overlap is faint magenta.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(DARKEST);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A blue line and a red line form an X on a gray background. The area where they overlap is black.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(LIGHTEST);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A faint blue line and a faint red line form an X on a gray background. The area where they overlap is faint magenta.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(EXCLUSION);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A yellow line and a cyan line form an X on a gray background. The area where they overlap is green.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(MULTIPLY);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A blue line and a red line form an X on a gray background. The area where they overlap is black.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(SCREEN);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A faint blue line and a faint red line form an X on a gray background. The area where they overlap is faint magenta.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(REPLACE);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A diagonal red line.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(REMOVE);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('The silhouette of an X is missing from a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(DIFFERENCE);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A yellow line and a cyan line form an X on a gray background. The area where they overlap is green.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(OVERLAY);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A faint blue line and a faint red line form an X on a gray background. The area where they overlap is bright magenta.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(HARD_LIGHT);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A blue line and a red line form an X on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(SOFT_LIGHT);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A faint blue line and a faint red line form an X on a gray background. The area where they overlap is violet.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(DODGE);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A faint blue line and a faint red line form an X on a gray background. The area where they overlap is faint violet.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(BURN);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A blue line and a red line form an X on a gray background. The area where they overlap is black.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the blend mode.
+ * blendMode(SUBTRACT);
+ *
+ * // Style the lines.
+ * strokeWeight(30);
+ *
+ * // Draw the blue line.
+ * stroke('blue');
+ * line(25, 25, 75, 75);
+ *
+ * // Draw the red line.
+ * stroke('red');
+ * line(75, 25, 25, 75);
+ *
+ * describe('A yellow line and a turquoise line form an X on a gray background. The area where they overlap is green.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the circle using shadows.
+ * drawingContext.shadowOffsetX = 5;
+ * drawingContext.shadowOffsetY = -5;
+ * drawingContext.shadowBlur = 10;
+ * drawingContext.shadowColor = 'black';
+ *
+ * // Draw the circle.
+ * circle(50, 50, 40);
+ *
+ * describe("A white circle on a gray background. The circle's edges are shadowy.");
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background('skyblue');
+ *
+ * // Style the circle using a color gradient.
+ * let myGradient = drawingContext.createRadialGradient(50, 50, 3, 50, 50, 40);
+ * myGradient.addColorStop(0, 'yellow');
+ * myGradient.addColorStop(0.6, 'orangered');
+ * myGradient.addColorStop(1, 'yellow');
+ * drawingContext.fillStyle = myGradient;
+ * drawingContext.strokeStyle = 'rgba(0, 0, 0, 0)';
+ *
+ * // Draw the circle.
+ * circle(50, 50, 40);
+ *
+ * describe('A fiery sun drawn on a light blue background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * arc(50, 50, 80, 80, 0, PI + HALF_PI);
+ *
+ * describe('A white circle on a gray canvas. The top-right quarter of the circle is missing.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * arc(50, 50, 80, 40, 0, PI + HALF_PI);
+ *
+ * describe('A white ellipse on a gray canvas. The top-right quarter of the ellipse is missing.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Bottom-right.
+ * arc(50, 55, 50, 50, 0, HALF_PI);
+ *
+ * noFill();
+ *
+ * // Bottom-left.
+ * arc(50, 55, 60, 60, HALF_PI, PI);
+ *
+ * // Top-left.
+ * arc(50, 55, 70, 70, PI, PI + QUARTER_PI);
+ *
+ * // Top-right.
+ * arc(50, 55, 80, 80, PI + QUARTER_PI, TWO_PI);
+ *
+ * describe(
+ * 'A shattered outline of an circle with a quarter of a white circle at the bottom-right.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Default fill mode.
+ * arc(50, 50, 80, 80, 0, PI + QUARTER_PI);
+ *
+ * describe('A white circle with the top-right third missing. The bottom is outlined in black.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // OPEN fill mode.
+ * arc(50, 50, 80, 80, 0, PI + QUARTER_PI, OPEN);
+ *
+ * describe(
+ * 'A white circle missing a section from the top-right. The bottom is outlined in black.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // CHORD fill mode.
+ * arc(50, 50, 80, 80, 0, PI + QUARTER_PI, CHORD);
+ *
+ * describe('A white circle with a black outline missing a section from the top-right.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // PIE fill mode.
+ * arc(50, 50, 80, 80, 0, PI + QUARTER_PI, PIE);
+ *
+ * describe('A white circle with a black outline. The top-right third is missing.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // PIE fill mode.
+ * arc(0, 0, 80, 80, 0, PI + QUARTER_PI, PIE);
+ *
+ * describe('A white circle with a black outline. The top-right third is missing.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // PIE fill mode with 5 vertices.
+ * arc(0, 0, 80, 80, 0, PI + QUARTER_PI, PIE, 5);
+ *
+ * describe('A white circle with a black outline. The top-right third is missing.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A yellow circle on a black background. The circle opens and closes its mouth.');
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Style the arc.
+ * noStroke();
+ * fill(255, 255, 0);
+ *
+ * // Update start and stop angles.
+ * let biteSize = PI / 16;
+ * let startAngle = biteSize * sin(frameCount * 0.1) + biteSize;
+ * let endAngle = TWO_PI - startAngle;
+ *
+ * // Draw the arc.
+ * arc(50, 50, 80, 80, startAngle, endAngle, PIE);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * ellipse(50, 50, 80, 80);
+ *
+ * describe('A white circle on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * ellipse(50, 50, 80);
+ *
+ * describe('A white circle on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * ellipse(50, 50, 80, 40);
+ *
+ * describe('A white ellipse on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * ellipse(0, 0, 80, 40);
+ *
+ * describe('A white ellipse on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Use 6 vertices.
+ * ellipse(0, 0, 80, 40, 6);
+ *
+ * describe('A white hexagon on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * circle(50, 50, 25);
+ *
+ * describe('A white circle with black outline in the middle of a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * circle(0, 0, 25);
+ *
+ * describe('A white circle with black outline in the middle of a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * line(30, 20, 85, 75);
+ *
+ * describe(
+ * 'A black line on a gray canvas running from top-center to bottom-right.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the line.
+ * stroke('magenta');
+ * strokeWeight(5);
+ *
+ * line(30, 20, 85, 75);
+ *
+ * describe(
+ * 'A thick, magenta line on a gray canvas running from top-center to bottom-right.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Top.
+ * line(30, 20, 85, 20);
+ *
+ * // Right.
+ * stroke(126);
+ * line(85, 20, 85, 75);
+ *
+ * // Bottom.
+ * stroke(255);
+ * line(85, 75, 30, 75);
+ *
+ * describe(
+ * 'Three lines drawn in grayscale on a gray canvas. They form the top, right, and bottom sides of a square.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * line(-20, -30, 35, 25);
+ *
+ * describe(
+ * 'A black line on a gray canvas running from top-center to bottom-right.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A black line connecting two spheres. The scene spins slowly.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Draw a line.
+ * line(0, 0, 0, 30, 20, -10);
+ *
+ * // Draw the center sphere.
+ * sphere(10);
+ *
+ * // Translate to the second point.
+ * translate(30, 20, -10);
+ *
+ * // Draw the bottom-right sphere.
+ * sphere(10);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Top-left.
+ * point(30, 20);
+ *
+ * // Top-right.
+ * point(85, 20);
+ *
+ * // Bottom-right.
+ * point(85, 75);
+ *
+ * // Bottom-left.
+ * point(30, 75);
+ *
+ * describe(
+ * 'Four small, black points drawn on a gray canvas. The points form the corners of a square.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Top-left.
+ * point(30, 20);
+ *
+ * // Top-right.
+ * point(70, 20);
+ *
+ * // Style the next points.
+ * stroke('purple');
+ * strokeWeight(10);
+ *
+ * // Bottom-right.
+ * point(70, 80);
+ *
+ * // Bottom-left.
+ * point(30, 80);
+ *
+ * describe(
+ * 'Four points drawn on a gray canvas. Two are black and two are purple. The points form the corners of a square.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Top-left.
+ * let a = createVector(30, 20);
+ * point(a);
+ *
+ * // Top-right.
+ * let b = createVector(70, 20);
+ * point(b);
+ *
+ * // Bottom-right.
+ * let c = createVector(70, 80);
+ * point(c);
+ *
+ * // Bottom-left.
+ * let d = createVector(30, 80);
+ * point(d);
+ *
+ * describe(
+ * 'Four small, black points drawn on a gray canvas. The points form the corners of a square.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('Two purple points drawn on a gray canvas.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the points.
+ * stroke('purple');
+ * strokeWeight(10);
+ *
+ * // Top-left.
+ * point(-20, -30);
+ *
+ * // Bottom-right.
+ * point(20, 30);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('Two purple points drawn on a gray canvas. The scene spins slowly.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Style the points.
+ * stroke('purple');
+ * strokeWeight(10);
+ *
+ * // Top-left.
+ * point(-20, -30, 0);
+ *
+ * // Bottom-right.
+ * point(20, 30, -50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * quad(20, 20, 80, 20, 80, 80, 20, 80);
+ *
+ * describe('A white square with a black outline drawn on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * quad(20, 30, 80, 30, 80, 70, 20, 70);
+ *
+ * describe('A white rectangle with a black outline drawn on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * quad(50, 62, 86, 50, 50, 38, 14, 50);
+ *
+ * describe('A white rhombus with a black outline drawn on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * quad(20, 50, 80, 30, 80, 70, 20, 70);
+ *
+ * describe('A white trapezoid with a black outline drawn on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * quad(-30, -30, 30, -30, 30, 30, -30, 30);
+ *
+ * describe('A white square with a black outline drawn on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A wavy white surface spins around on gray canvas.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Draw the quad.
+ * quad(-30, -30, 0, 30, -30, 0, 30, 30, 20, -30, 30, -20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * rect(30, 20, 55, 55);
+ *
+ * describe('A white square with a black outline on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * rect(30, 20, 55, 40);
+ *
+ * describe('A white rectangle with a black outline on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Give all corners a radius of 20.
+ * rect(30, 20, 55, 50, 20);
+ *
+ * describe('A white rectangle with a black outline and round edges on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Give each corner a unique radius.
+ * rect(30, 20, 55, 50, 20, 15, 10, 5);
+ *
+ * describe('A white rectangle with a black outline and round edges of different radii.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * rect(-20, -30, 55, 55);
+ *
+ * describe('A white square with a black outline on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white square spins around on gray canvas.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Draw the rectangle.
+ * rect(-20, -30, 55, 55);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * square(30, 20, 55);
+ *
+ * describe('A white square with a black outline in on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Give all corners a radius of 20.
+ * square(30, 20, 55, 20);
+ *
+ * describe(
+ * 'A white square with a black outline and round edges on a gray canvas.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Give each corner a unique radius.
+ * square(30, 20, 55, 20, 15, 10, 5);
+ *
+ * describe('A white square with a black outline and round edges of different radii.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * square(-20, -30, 55);
+ *
+ * describe('A white square with a black outline in on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white square spins around on gray canvas.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Draw the square.
+ * square(-20, -30, 55);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * triangle(30, 75, 58, 20, 86, 75);
+ *
+ * describe('A white triangle with a black outline on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * triangle(-20, 25, 8, -30, 36, 25);
+ *
+ * describe('A white triangle with a black outline on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white triangle spins around on a gray canvas.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Draw the triangle.
+ * triangle(-20, 25, 8, -30, 36, 25);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // White ellipse.
+ * ellipseMode(RADIUS);
+ * fill(255);
+ * ellipse(50, 50, 30, 30);
+ *
+ * // Gray ellipse.
+ * ellipseMode(CENTER);
+ * fill(100);
+ * ellipse(50, 50, 30, 30);
+ *
+ * describe('A white circle with a gray circle at its center. Both circles have black outlines.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // White ellipse.
+ * ellipseMode(CORNER);
+ * fill(255);
+ * ellipse(25, 25, 50, 50);
+ *
+ * // Gray ellipse.
+ * ellipseMode(CORNERS);
+ * fill(100);
+ * ellipse(25, 25, 50, 50);
+ *
+ * describe('A white circle with a gray circle at its top-left corner. Both circles have black outlines.');
+ * }
+ *
+ *
+ * let heart;
+ *
+ * // Load a pixelated heart image from an image data string.
+ * function preload() {
+ * heart = loadImage('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAEZJREFUGFd9jcsNACAIQ9tB2MeR3YdBMBBq8CIXPi2vBICIiOwkOedatllqWO6Y8yOWoyuNf1GZwgmf+RRG2YXr+xVFmA8HZ9Mx/KGPMtcAAAAASUVORK5CYII=');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Antialiased hearts.
+ * image(heart, 10, 10);
+ * image(heart, 20, 10, 16, 16);
+ * image(heart, 40, 10, 32, 32);
+ *
+ * // Aliased hearts.
+ * noSmooth();
+ * image(heart, 10, 60);
+ * image(heart, 20, 60, 16, 16);
+ * image(heart, 40, 60, 32, 32);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * circle(0, 0, 80);
+ *
+ * describe('A white circle on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Disable smoothing.
+ * noSmooth();
+ *
+ * background(200);
+ *
+ * circle(0, 0, 80);
+ *
+ * describe('A pixelated white circle on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * rectMode(CORNER);
+ * fill(255);
+ * rect(25, 25, 50, 50);
+ *
+ * rectMode(CORNERS);
+ * fill(100);
+ * rect(25, 25, 50, 50);
+ *
+ * describe('A small gray square drawn at the top-left corner of a white square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * rectMode(RADIUS);
+ * fill(255);
+ * rect(50, 50, 30, 30);
+ *
+ * rectMode(CENTER);
+ * fill(100);
+ * rect(50, 50, 30, 30);
+ *
+ * describe('A small gray square drawn at the center of a white square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * rectMode(CORNER);
+ * fill(255);
+ * square(25, 25, 50);
+ *
+ * describe('A white square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * rectMode(RADIUS);
+ * fill(255);
+ * square(50, 50, 30);
+ *
+ * rectMode(CENTER);
+ * fill(100);
+ * square(50, 50, 30);
+ *
+ * describe('A small gray square drawn at the center of a white square.');
+ * }
+ *
+ *
+ * let heart;
+ *
+ * // Load a pixelated heart image from an image data string.
+ * function preload() {
+ * heart = loadImage('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAEZJREFUGFd9jcsNACAIQ9tB2MeR3YdBMBBq8CIXPi2vBICIiOwkOedatllqWO6Y8yOWoyuNf1GZwgmf+RRG2YXr+xVFmA8HZ9Mx/KGPMtcAAAAASUVORK5CYII=');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Antialiased hearts.
+ * image(heart, 10, 10);
+ * image(heart, 20, 10, 16, 16);
+ * image(heart, 40, 10, 32, 32);
+ *
+ * // Aliased hearts.
+ * noSmooth();
+ * image(heart, 10, 60);
+ * image(heart, 20, 60, 16, 16);
+ * image(heart, 40, 60, 32, 32);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * circle(0, 0, 80);
+ *
+ * describe('A white circle on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Disable smoothing.
+ * noSmooth();
+ *
+ * background(200);
+ *
+ * circle(0, 0, 80);
+ *
+ * describe('A pixelated white circle on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * strokeWeight(12);
+ *
+ * // Top.
+ * strokeCap(ROUND);
+ * line(20, 30, 80, 30);
+ *
+ * // Middle.
+ * strokeCap(SQUARE);
+ * line(20, 50, 80, 50);
+ *
+ * // Bottom.
+ * strokeCap(PROJECT);
+ * line(20, 70, 80, 70);
+ *
+ * describe(
+ * 'Three horizontal lines. The top line has rounded ends, the middle line has squared ends, and the bottom line has longer, squared ends.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the line.
+ * noFill();
+ * strokeWeight(10);
+ * strokeJoin(MITER);
+ *
+ * // Draw the line.
+ * beginShape();
+ * vertex(35, 20);
+ * vertex(65, 50);
+ * vertex(35, 80);
+ * endShape();
+ *
+ * describe('A right-facing arrowhead shape with a pointed tip in center of canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the line.
+ * noFill();
+ * strokeWeight(10);
+ * strokeJoin(BEVEL);
+ *
+ * // Draw the line.
+ * beginShape();
+ * vertex(35, 20);
+ * vertex(65, 50);
+ * vertex(35, 80);
+ * endShape();
+ *
+ * describe('A right-facing arrowhead shape with a flat tip in center of canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the line.
+ * noFill();
+ * strokeWeight(10);
+ * strokeJoin(ROUND);
+ *
+ * // Draw the line.
+ * beginShape();
+ * vertex(35, 20);
+ * vertex(65, 50);
+ * vertex(35, 80);
+ * endShape();
+ *
+ * describe('A right-facing arrowhead shape with a rounded tip in center of canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Top.
+ * line(20, 20, 80, 20);
+ *
+ * // Middle.
+ * strokeWeight(4);
+ * line(20, 40, 80, 40);
+ *
+ * // Bottom.
+ * strokeWeight(10);
+ * line(20, 70, 80, 70);
+ *
+ * describe('Three horizontal black lines. The top line is thin, the middle is medium, and the bottom is thick.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Top.
+ * line(20, 20, 80, 20);
+ *
+ * // Scale by a factor of 5.
+ * scale(5);
+ *
+ * // Bottom. Coordinates are adjusted for scaling.
+ * line(4, 8, 16, 8);
+ *
+ * describe('Two horizontal black lines. The top line is thin and the bottom is five times thicker than the top.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw the anchor points in black.
+ * stroke(0);
+ * strokeWeight(5);
+ * point(85, 20);
+ * point(15, 80);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(10, 10);
+ * point(90, 90);
+ *
+ * // Draw a black bezier curve.
+ * noFill();
+ * stroke(0);
+ * strokeWeight(1);
+ * bezier(85, 20, 10, 10, 90, 90, 15, 80);
+ *
+ * // Draw red lines from the anchor points to the control points.
+ * stroke(255, 0, 0);
+ * line(85, 20, 10, 10);
+ * line(15, 80, 90, 90);
+ *
+ * describe(
+ * 'A gray square with three curves. A black s-curve has two straight, red lines that extend from its ends. The endpoints of all the curves are marked with dots.'
+ * );
+ * }
+ *
+ *
+ * // Click the mouse near the red dot in the top-left corner
+ * // and drag to change the curve's shape.
+ *
+ * let x2 = 10;
+ * let y2 = 10;
+ * let isChanging = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with three curves. A black s-curve has two straight, red lines that extend from its ends. The endpoints of all the curves are marked with dots.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the anchor points in black.
+ * stroke(0);
+ * strokeWeight(5);
+ * point(85, 20);
+ * point(15, 80);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(x2, y2);
+ * point(90, 90);
+ *
+ * // Draw a black bezier curve.
+ * noFill();
+ * stroke(0);
+ * strokeWeight(1);
+ * bezier(85, 20, x2, y2, 90, 90, 15, 80);
+ *
+ * // Draw red lines from the anchor points to the control points.
+ * stroke(255, 0, 0);
+ * line(85, 20, x2, y2);
+ * line(15, 80, 90, 90);
+ * }
+ *
+ * // Start changing the first control point if the user clicks near it.
+ * function mousePressed() {
+ * if (dist(mouseX, mouseY, x2, y2) < 20) {
+ * isChanging = true;
+ * }
+ * }
+ *
+ * // Stop changing the first control point when the user releases the mouse.
+ * function mouseReleased() {
+ * isChanging = false;
+ * }
+ *
+ * // Update the first control point while the user drags the mouse.
+ * function mouseDragged() {
+ * if (isChanging === true) {
+ * x2 = mouseX;
+ * y2 = mouseY;
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background('skyblue');
+ *
+ * // Draw the red balloon.
+ * fill('red');
+ * bezier(50, 60, 5, 15, 95, 15, 50, 60);
+ *
+ * // Draw the balloon string.
+ * line(50, 60, 50, 80);
+ *
+ * describe('A red balloon in a blue sky.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A red balloon in a blue sky. The balloon rotates slowly, revealing that it is flat.');
+ * }
+ *
+ * function draw() {
+ * background('skyblue');
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Draw the red balloon.
+ * fill('red');
+ * bezier(0, 0, 0, -45, -45, 0, 45, -45, 0, 0, 0, 0);
+ *
+ * // Draw the balloon string.
+ * line(0, 0, 0, 0, 20, 0);
+ * }
+ *
+ *
+ * // Draw the original curve.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw the anchor points in black.
+ * stroke(0);
+ * strokeWeight(5);
+ * point(85, 20);
+ * point(15, 80);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(10, 10);
+ * point(90, 90);
+ *
+ * // Draw a black bezier curve.
+ * noFill();
+ * stroke(0);
+ * strokeWeight(1);
+ * bezier(85, 20, 10, 10, 90, 90, 15, 80);
+ *
+ * // Draw red lines from the anchor points to the control points.
+ * stroke(255, 0, 0);
+ * line(85, 20, 10, 10);
+ * line(15, 80, 90, 90);
+ *
+ * describe(
+ * 'A gray square with three curves. A black s-curve has two straight, red lines that extend from its ends. The endpoints of all the curves are marked with dots.'
+ * );
+ * }
+ *
+ *
+ * // Draw the curve with less detail.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Set the curveDetail() to 5.
+ * bezierDetail(5);
+ *
+ * // Draw the anchor points in black.
+ * stroke(0);
+ * strokeWeight(5);
+ * point(35, -30, 0);
+ * point(-35, 30, 0);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(-40, -40, 0);
+ * point(40, 40, 0);
+ *
+ * // Draw a black bezier curve.
+ * noFill();
+ * stroke(0);
+ * strokeWeight(1);
+ * bezier(35, -30, 0, -40, -40, 0, 40, 40, 0, -35, 30, 0);
+ *
+ * // Draw red lines from the anchor points to the control points.
+ * stroke(255, 0, 0);
+ * line(35, -30, -40, -40);
+ * line(-35, 30, 40, 40);
+ *
+ * describe(
+ * 'A gray square with three curves. A black s-curve is drawn with jagged segments. Two straight, red lines that extend from its ends. The endpoints of all the curves are marked with dots.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the coordinates for the curve's anchor and control points.
+ * let x1 = 85;
+ * let x2 = 10;
+ * let x3 = 90;
+ * let x4 = 15;
+ * let y1 = 20;
+ * let y2 = 10;
+ * let y3 = 90;
+ * let y4 = 80;
+ *
+ * // Style the curve.
+ * noFill();
+ *
+ * // Draw the curve.
+ * bezier(x1, y1, x2, y2, x3, y3, x4, y4);
+ *
+ * // Draw circles along the curve's path.
+ * fill(255);
+ *
+ * // Top-right.
+ * let x = bezierPoint(x1, x2, x3, x4, 0);
+ * let y = bezierPoint(y1, y2, y3, y4, 0);
+ * circle(x, y, 5);
+ *
+ * // Center.
+ * x = bezierPoint(x1, x2, x3, x4, 0.5);
+ * y = bezierPoint(y1, y2, y3, y4, 0.5);
+ * circle(x, y, 5);
+ *
+ * // Bottom-left.
+ * x = bezierPoint(x1, x2, x3, x4, 1);
+ * y = bezierPoint(y1, y2, y3, y4, 1);
+ * circle(x, y, 5);
+ *
+ * describe('A black s-curve on a gray square. The endpoints and center of the curve are marked with white circles.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A black s-curve on a gray square. A white circle moves back and forth along the curve.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Set the coordinates for the curve's anchor and control points.
+ * let x1 = 85;
+ * let x2 = 10;
+ * let x3 = 90;
+ * let x4 = 15;
+ * let y1 = 20;
+ * let y2 = 10;
+ * let y3 = 90;
+ * let y4 = 80;
+ *
+ * // Draw the curve.
+ * noFill();
+ * bezier(x1, y1, x2, y2, x3, y3, x4, y4);
+ *
+ * // Calculate the circle's coordinates.
+ * let t = 0.5 * sin(frameCount * 0.01) + 0.5;
+ * let x = bezierPoint(x1, x2, x3, x4, t);
+ * let y = bezierPoint(y1, y2, y3, y4, t);
+ *
+ * // Draw the circle.
+ * fill(255);
+ * circle(x, y, 5);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the coordinates for the curve's anchor and control points.
+ * let x1 = 85;
+ * let x2 = 10;
+ * let x3 = 90;
+ * let x4 = 15;
+ * let y1 = 20;
+ * let y2 = 10;
+ * let y3 = 90;
+ * let y4 = 80;
+ *
+ * // Style the curve.
+ * noFill();
+ *
+ * // Draw the curve.
+ * bezier(x1, y1, x2, y2, x3, y3, x4, y4);
+ *
+ * // Draw tangents along the curve's path.
+ * fill(255);
+ *
+ * // Top-right circle.
+ * stroke(0);
+ * let x = bezierPoint(x1, x2, x3, x4, 0);
+ * let y = bezierPoint(y1, y2, y3, y4, 0);
+ * circle(x, y, 5);
+ *
+ * // Top-right tangent line.
+ * // Scale the tangent point to draw a shorter line.
+ * stroke(255, 0, 0);
+ * let tx = 0.1 * bezierTangent(x1, x2, x3, x4, 0);
+ * let ty = 0.1 * bezierTangent(y1, y2, y3, y4, 0);
+ * line(x + tx, y + ty, x - tx, y - ty);
+ *
+ * // Center circle.
+ * stroke(0);
+ * x = bezierPoint(x1, x2, x3, x4, 0.5);
+ * y = bezierPoint(y1, y2, y3, y4, 0.5);
+ * circle(x, y, 5);
+ *
+ * // Center tangent line.
+ * // Scale the tangent point to draw a shorter line.
+ * stroke(255, 0, 0);
+ * tx = 0.1 * bezierTangent(x1, x2, x3, x4, 0.5);
+ * ty = 0.1 * bezierTangent(y1, y2, y3, y4, 0.5);
+ * line(x + tx, y + ty, x - tx, y - ty);
+ *
+ * // Bottom-left circle.
+ * stroke(0);
+ * x = bezierPoint(x1, x2, x3, x4, 1);
+ * y = bezierPoint(y1, y2, y3, y4, 1);
+ * circle(x, y, 5);
+ *
+ * // Bottom-left tangent.
+ * // Scale the tangent point to draw a shorter line.
+ * stroke(255, 0, 0);
+ * tx = 0.1 * bezierTangent(x1, x2, x3, x4, 1);
+ * ty = 0.1 * bezierTangent(y1, y2, y3, y4, 1);
+ * line(x + tx, y + ty, x - tx, y - ty);
+ *
+ * describe(
+ * 'A black s-curve on a gray square. The endpoints and center of the curve are marked with white circles. Red tangent lines extend from the white circles.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw a black spline curve.
+ * noFill();
+ * strokeWeight(1);
+ * stroke(0);
+ * curve(5, 26, 73, 24, 73, 61, 15, 65);
+ *
+ * // Draw red spline curves from the anchor points to the control points.
+ * stroke(255, 0, 0);
+ * curve(5, 26, 5, 26, 73, 24, 73, 61);
+ * curve(73, 24, 73, 61, 15, 65, 15, 65);
+ *
+ * // Draw the anchor points in black.
+ * strokeWeight(5);
+ * stroke(0);
+ * point(73, 24);
+ * point(73, 61);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(5, 26);
+ * point(15, 65);
+ *
+ * describe(
+ * 'A gray square with a curve drawn in three segments. The curve is a sideways U shape with red segments on top and bottom, and a black segment on the right. The endpoints of all the segments are marked with dots.'
+ * );
+ * }
+ *
+ *
+ * let x1 = 5;
+ * let y1 = 26;
+ * let isChanging = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a curve drawn in three segments. The curve is a sideways U shape with red segments on top and bottom, and a black segment on the right. The endpoints of all the segments are marked with dots.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a black spline curve.
+ * noFill();
+ * strokeWeight(1);
+ * stroke(0);
+ * curve(x1, y1, 73, 24, 73, 61, 15, 65);
+ *
+ * // Draw red spline curves from the anchor points to the control points.
+ * stroke(255, 0, 0);
+ * curve(x1, y1, x1, y1, 73, 24, 73, 61);
+ * curve(73, 24, 73, 61, 15, 65, 15, 65);
+ *
+ * // Draw the anchor points in black.
+ * strokeWeight(5);
+ * stroke(0);
+ * point(73, 24);
+ * point(73, 61);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(x1, y1);
+ * point(15, 65);
+ * }
+ *
+ * // Start changing the first control point if the user clicks near it.
+ * function mousePressed() {
+ * if (dist(mouseX, mouseY, x1, y1) < 20) {
+ * isChanging = true;
+ * }
+ * }
+ *
+ * // Stop changing the first control point when the user releases the mouse.
+ * function mouseReleased() {
+ * isChanging = false;
+ * }
+ *
+ * // Update the first control point while the user drags the mouse.
+ * function mouseDragged() {
+ * if (isChanging === true) {
+ * x1 = mouseX;
+ * y1 = mouseY;
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background('skyblue');
+ *
+ * // Draw the red balloon.
+ * fill('red');
+ * curve(-150, 275, 50, 60, 50, 60, 250, 275);
+ *
+ * // Draw the balloon string.
+ * line(50, 60, 50, 80);
+ *
+ * describe('A red balloon in a blue sky.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A red balloon in a blue sky.');
+ * }
+ *
+ * function draw() {
+ * background('skyblue');
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Draw the red balloon.
+ * fill('red');
+ * curve(-200, 225, 0, 0, 10, 0, 0, 10, 0, 200, 225, 0);
+ *
+ * // Draw the balloon string.
+ * line(0, 10, 0, 0, 30, 0);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw a black spline curve.
+ * noFill();
+ * strokeWeight(1);
+ * stroke(0);
+ * curve(5, 26, 73, 24, 73, 61, 15, 65);
+ *
+ * // Draw red spline curves from the anchor points to the control points.
+ * stroke(255, 0, 0);
+ * curve(5, 26, 5, 26, 73, 24, 73, 61);
+ * curve(73, 24, 73, 61, 15, 65, 15, 65);
+ *
+ * // Draw the anchor points in black.
+ * strokeWeight(5);
+ * stroke(0);
+ * point(73, 24);
+ * point(73, 61);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(5, 26);
+ * point(15, 65);
+ *
+ * describe(
+ * 'A gray square with a curve drawn in three segments. The curve is a sideways U shape with red segments on top and bottom, and a black segment on the right. The endpoints of all the segments are marked with dots.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Set the curveDetail() to 3.
+ * curveDetail(3);
+ *
+ * // Draw a black spline curve.
+ * noFill();
+ * strokeWeight(1);
+ * stroke(0);
+ * curve(-45, -24, 0, 23, -26, 0, 23, 11, 0, -35, 15, 0);
+ *
+ * // Draw red spline curves from the anchor points to the control points.
+ * stroke(255, 0, 0);
+ * curve(-45, -24, 0, -45, -24, 0, 23, -26, 0, 23, 11, 0);
+ * curve(23, -26, 0, 23, 11, 0, -35, 15, 0, -35, 15, 0);
+ *
+ * // Draw the anchor points in black.
+ * strokeWeight(5);
+ * stroke(0);
+ * point(23, -26);
+ * point(23, 11);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(-45, -24);
+ * point(-35, 15);
+ *
+ * describe(
+ * 'A gray square with a jagged curve drawn in three segments. The curve is a sideways U shape with red segments on top and bottom, and a black segment on the right. The endpoints of all the segments are marked with dots.'
+ * );
+ * }
+ *
+ *
+ * // Move the mouse left and right to see the curve change.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A black curve forms a sideways U shape. The curve deforms as the user moves the mouse from left to right');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Set the curve's tightness using the mouse.
+ * let t = map(mouseX, 0, 100, -5, 5, true);
+ * curveTightness(t);
+ *
+ * // Draw the curve.
+ * noFill();
+ * beginShape();
+ * curveVertex(10, 26);
+ * curveVertex(10, 26);
+ * curveVertex(83, 24);
+ * curveVertex(83, 61);
+ * curveVertex(25, 65);
+ * curveVertex(25, 65);
+ * endShape();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the coordinates for the curve's anchor and control points.
+ * let x1 = 5;
+ * let y1 = 26;
+ * let x2 = 73;
+ * let y2 = 24;
+ * let x3 = 73;
+ * let y3 = 61;
+ * let x4 = 15;
+ * let y4 = 65;
+ *
+ * // Draw the curve.
+ * noFill();
+ * curve(x1, y1, x2, y2, x3, y3, x4, y4);
+ *
+ * // Draw circles along the curve's path.
+ * fill(255);
+ *
+ * // Top.
+ * let x = curvePoint(x1, x2, x3, x4, 0);
+ * let y = curvePoint(y1, y2, y3, y4, 0);
+ * circle(x, y, 5);
+ *
+ * // Center.
+ * x = curvePoint(x1, x2, x3, x4, 0.5);
+ * y = curvePoint(y1, y2, y3, y4, 0.5);
+ * circle(x, y, 5);
+ *
+ * // Bottom.
+ * x = curvePoint(x1, x2, x3, x4, 1);
+ * y = curvePoint(y1, y2, y3, y4, 1);
+ * circle(x, y, 5);
+ *
+ * describe('A black curve on a gray square. The endpoints and center of the curve are marked with white circles.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A black curve on a gray square. A white circle moves back and forth along the curve.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Set the coordinates for the curve's anchor and control points.
+ * let x1 = 5;
+ * let y1 = 26;
+ * let x2 = 73;
+ * let y2 = 24;
+ * let x3 = 73;
+ * let y3 = 61;
+ * let x4 = 15;
+ * let y4 = 65;
+ *
+ * // Draw the curve.
+ * noFill();
+ * curve(x1, y1, x2, y2, x3, y3, x4, y4);
+ *
+ * // Calculate the circle's coordinates.
+ * let t = 0.5 * sin(frameCount * 0.01) + 0.5;
+ * let x = curvePoint(x1, x2, x3, x4, t);
+ * let y = curvePoint(y1, y2, y3, y4, t);
+ *
+ * // Draw the circle.
+ * fill(255);
+ * circle(x, y, 5);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the coordinates for the curve's anchor and control points.
+ * let x1 = 5;
+ * let y1 = 26;
+ * let x2 = 73;
+ * let y2 = 24;
+ * let x3 = 73;
+ * let y3 = 61;
+ * let x4 = 15;
+ * let y4 = 65;
+ *
+ * // Draw the curve.
+ * noFill();
+ * curve(x1, y1, x2, y2, x3, y3, x4, y4);
+ *
+ * // Draw tangents along the curve's path.
+ * fill(255);
+ *
+ * // Top circle.
+ * stroke(0);
+ * let x = curvePoint(x1, x2, x3, x4, 0);
+ * let y = curvePoint(y1, y2, y3, y4, 0);
+ * circle(x, y, 5);
+ *
+ * // Top tangent line.
+ * // Scale the tangent point to draw a shorter line.
+ * stroke(255, 0, 0);
+ * let tx = 0.2 * curveTangent(x1, x2, x3, x4, 0);
+ * let ty = 0.2 * curveTangent(y1, y2, y3, y4, 0);
+ * line(x + tx, y + ty, x - tx, y - ty);
+ *
+ * // Center circle.
+ * stroke(0);
+ * x = curvePoint(x1, x2, x3, x4, 0.5);
+ * y = curvePoint(y1, y2, y3, y4, 0.5);
+ * circle(x, y, 5);
+ *
+ * // Center tangent line.
+ * // Scale the tangent point to draw a shorter line.
+ * stroke(255, 0, 0);
+ * tx = 0.2 * curveTangent(x1, x2, x3, x4, 0.5);
+ * ty = 0.2 * curveTangent(y1, y2, y3, y4, 0.5);
+ * line(x + tx, y + ty, x - tx, y - ty);
+ *
+ * // Bottom circle.
+ * stroke(0);
+ * x = curvePoint(x1, x2, x3, x4, 1);
+ * y = curvePoint(y1, y2, y3, y4, 1);
+ * circle(x, y, 5);
+ *
+ * // Bottom tangent line.
+ * // Scale the tangent point to draw a shorter line.
+ * stroke(255, 0, 0);
+ * tx = 0.2 * curveTangent(x1, x2, x3, x4, 1);
+ * ty = 0.2 * curveTangent(y1, y2, y3, y4, 1);
+ * line(x + tx, y + ty, x - tx, y - ty);
+ *
+ * describe(
+ * 'A black curve on a gray square. A white circle moves back and forth along the curve.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Exterior vertices, clockwise winding.
+ * vertex(10, 10);
+ * vertex(90, 10);
+ * vertex(90, 90);
+ * vertex(10, 90);
+ *
+ * // Interior vertices, counter-clockwise winding.
+ * beginContour();
+ * vertex(30, 30);
+ * vertex(30, 70);
+ * vertex(70, 70);
+ * vertex(70, 30);
+ * endContour();
+ *
+ * // Stop drawing the shape.
+ * endShape(CLOSE);
+ *
+ * describe('A white square with a square hole in its center drawn on a gray background.');
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white square with a square hole in its center drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Exterior vertices, clockwise winding.
+ * vertex(-40, -40);
+ * vertex(40, -40);
+ * vertex(40, 40);
+ * vertex(-40, 40);
+ *
+ * // Interior vertices, counter-clockwise winding.
+ * beginContour();
+ * vertex(-20, -20);
+ * vertex(-20, 20);
+ * vertex(20, 20);
+ * vertex(20, -20);
+ * endContour();
+ *
+ * // Stop drawing the shape.
+ * endShape(CLOSE);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add vertices.
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ *
+ * // Stop drawing the shape.
+ * endShape(CLOSE);
+ *
+ * describe('A white square on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * // Only draw the vertices (points).
+ * beginShape(POINTS);
+ *
+ * // Add vertices.
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('Four black dots that form a square are drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * // Only draw lines between alternating pairs of vertices.
+ * beginShape(LINES);
+ *
+ * // Add vertices.
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('Two horizontal black lines on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the shape.
+ * noFill();
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add vertices.
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('Three black lines form a sideways U shape on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the shape.
+ * noFill();
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add vertices.
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ *
+ * // Stop drawing the shape.
+ * // Connect the first and last vertices.
+ * endShape(CLOSE);
+ *
+ * describe('A black outline of a square drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * // Draw a series of triangles.
+ * beginShape(TRIANGLES);
+ *
+ * // Left triangle.
+ * vertex(30, 75);
+ * vertex(40, 20);
+ * vertex(50, 75);
+ *
+ * // Right triangle.
+ * vertex(60, 20);
+ * vertex(70, 75);
+ * vertex(80, 20);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('Two white triangles drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * // Draw a series of triangles.
+ * beginShape(TRIANGLE_STRIP);
+ *
+ * // Add vertices.
+ * vertex(30, 75);
+ * vertex(40, 20);
+ * vertex(50, 75);
+ * vertex(60, 20);
+ * vertex(70, 75);
+ * vertex(80, 20);
+ * vertex(90, 75);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('Five white triangles that are interleaved drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * // Draw a series of triangles that share their first vertex.
+ * beginShape(TRIANGLE_FAN);
+ *
+ * // Add vertices.
+ * vertex(57.5, 50);
+ * vertex(57.5, 15);
+ * vertex(92, 50);
+ * vertex(57.5, 85);
+ * vertex(22, 50);
+ * vertex(57.5, 15);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('Four white triangles form a square are drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * // Draw a series of quadrilaterals.
+ * beginShape(QUADS);
+ *
+ * // Left rectangle.
+ * vertex(30, 20);
+ * vertex(30, 75);
+ * vertex(50, 75);
+ * vertex(50, 20);
+ *
+ * // Right rectangle.
+ * vertex(65, 20);
+ * vertex(65, 75);
+ * vertex(85, 75);
+ * vertex(85, 20);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('Two white rectangles drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * // Draw a series of quadrilaterals.
+ * beginShape(QUAD_STRIP);
+ *
+ * // Add vertices.
+ * vertex(30, 20);
+ * vertex(30, 75);
+ * vertex(50, 20);
+ * vertex(50, 75);
+ * vertex(65, 20);
+ * vertex(65, 75);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('Three white rectangles that share edges are drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * // Draw a series of quadrilaterals.
+ * beginShape(TESS);
+ *
+ * // Add the vertices.
+ * vertex(-30, -30, 0);
+ * vertex(30, -30, 0);
+ * vertex(30, -10, 0);
+ * vertex(-10, -10, 0);
+ * vertex(-10, 10, 0);
+ * vertex(30, 10, 0);
+ * vertex(30, 30, 0);
+ * vertex(-30, 30, 0);
+ *
+ * // Stop drawing the shape.
+ * // Connect the first and last vertices.
+ * endShape(CLOSE);
+ *
+ * describe('A blocky C shape drawn in white on a gray background.');
+ * }
+ *
+ *
+ * // Click and drag with the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A blocky C shape drawn in red, blue, and green on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Start drawing the shape.
+ * // Draw a series of quadrilaterals.
+ * beginShape(TESS);
+ *
+ * // Add the vertices.
+ * fill('red');
+ * stroke('red');
+ * vertex(-30, -30, 0);
+ * vertex(30, -30, 0);
+ * vertex(30, -10, 0);
+ * fill('green');
+ * stroke('green');
+ * vertex(-10, -10, 0);
+ * vertex(-10, 10, 0);
+ * vertex(30, 10, 0);
+ * fill('blue');
+ * stroke('blue');
+ * vertex(30, 30, 0);
+ * vertex(-30, 30, 0);
+ *
+ * // Stop drawing the shape.
+ * // Connect the first and last vertices.
+ * endShape(CLOSE);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the shape.
+ * noFill();
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add the first anchor point.
+ * vertex(30, 20);
+ *
+ * // Add the Bézier vertex.
+ * bezierVertex(80, 0, 80, 75, 30, 75);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('A black C curve on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw the anchor points in black.
+ * stroke(0);
+ * strokeWeight(5);
+ * point(30, 20);
+ * point(30, 75);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(80, 0);
+ * point(80, 75);
+ *
+ * // Style the shape.
+ * noFill();
+ * stroke(0);
+ * strokeWeight(1);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add the first anchor point.
+ * vertex(30, 20);
+ *
+ * // Add the Bézier vertex.
+ * bezierVertex(80, 0, 80, 75, 30, 75);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * // Draw red lines from the anchor points to the control points.
+ * stroke(255, 0, 0);
+ * line(30, 20, 80, 0);
+ * line(30, 75, 80, 75);
+ *
+ * describe(
+ * 'A gray square with three curves. A black curve has two straight, red lines that extend from its ends. The endpoints of all the curves are marked with dots.'
+ * );
+ * }
+ *
+ *
+ * // Click the mouse near the red dot in the top-right corner
+ * // and drag to change the curve's shape.
+ *
+ * let x2 = 80;
+ * let y2 = 0;
+ * let isChanging = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with three curves. A black curve has two straight, red lines that extend from its ends. The endpoints of all the curves are marked with dots.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the anchor points in black.
+ * stroke(0);
+ * strokeWeight(5);
+ * point(30, 20);
+ * point(30, 75);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(x2, y2);
+ * point(80, 75);
+ *
+ * // Style the shape.
+ * noFill();
+ * stroke(0);
+ * strokeWeight(1);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add the first anchor point.
+ * vertex(30, 20);
+ *
+ * // Add the Bézier vertex.
+ * bezierVertex(x2, y2, 80, 75, 30, 75);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * // Draw red lines from the anchor points to the control points.
+ * stroke(255, 0, 0);
+ * line(30, 20, x2, y2);
+ * line(30, 75, 80, 75);
+ * }
+ *
+ * // Start changing the first control point if the user clicks near it.
+ * function mousePressed() {
+ * if (dist(mouseX, mouseY, x2, y2) < 20) {
+ * isChanging = true;
+ * }
+ * }
+ *
+ * // Stop changing the first control point when the user releases the mouse.
+ * function mouseReleased() {
+ * isChanging = false;
+ * }
+ *
+ * // Update the first control point while the user drags the mouse.
+ * function mouseDragged() {
+ * if (isChanging === true) {
+ * x2 = mouseX;
+ * y2 = mouseY;
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add the first anchor point.
+ * vertex(30, 20);
+ *
+ * // Add the Bézier vertices.
+ * bezierVertex(80, 0, 80, 75, 30, 75);
+ * bezierVertex(50, 80, 60, 25, 30, 20);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('A crescent moon shape drawn in white on a gray background.');
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A crescent moon shape drawn in white on a blue background. When the user drags the mouse, the scene rotates and a second moon is revealed.');
+ * }
+ *
+ * function draw() {
+ * background('midnightblue');
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the moons.
+ * noStroke();
+ * fill('lemonchiffon');
+ *
+ * // Draw the first moon.
+ * beginShape();
+ * vertex(-20, -30, 0);
+ * bezierVertex(30, -50, 0, 30, 25, 0, -20, 25, 0);
+ * bezierVertex(0, 30, 0, 10, -25, 0, -20, -30, 0);
+ * endShape();
+ *
+ * // Draw the second moon.
+ * beginShape();
+ * vertex(-20, -30, -20);
+ * bezierVertex(30, -50, -20, 30, 25, -20, -20, 25, -20);
+ * bezierVertex(0, 30, -20, 10, -25, -20, -20, -30, -20);
+ * endShape();
+ * }
+ *
+ *
+ * beginShape();
+ *
+ * // Add the first control point.
+ * curveVertex(84, 91);
+ *
+ * // Add the anchor points to draw between.
+ * curveVertex(68, 19);
+ * curveVertex(21, 17);
+ *
+ * // Add the second control point.
+ * curveVertex(32, 91);
+ *
+ * endShape();
+ *
+ *
+ * The code snippet above would only draw the curve between the anchor points,
+ * similar to the curve() function. The segments
+ * between the control and anchor points can be drawn by calling
+ * `curveVertex()` with the coordinates of the control points:
+ *
+ *
+ * beginShape();
+ *
+ * // Add the first control point and draw a segment to it.
+ * curveVertex(84, 91);
+ * curveVertex(84, 91);
+ *
+ * // Add the anchor points to draw between.
+ * curveVertex(68, 19);
+ * curveVertex(21, 17);
+ *
+ * // Add the second control point.
+ * curveVertex(32, 91);
+ *
+ * // Uncomment the next line to draw the segment to the second control point.
+ * // curveVertex(32, 91);
+ *
+ * endShape();
+ *
+ *
+ * The first two parameters, `x` and `y`, set the vertex’s location. For
+ * example, calling `curveVertex(10, 10)` adds a point to the curve at
+ * `(10, 10)`.
+ *
+ * Spline curves can also be drawn in 3D using WebGL mode. The 3D version of
+ * `curveVertex()` has three arguments because each point has x-, y-, and
+ * z-coordinates. By default, the vertex’s z-coordinate is set to 0.
+ *
+ * Note: `curveVertex()` won’t work when an argument is passed to
+ * beginShape().
+ *
+ * @method curveVertex
+ * @param {Number} x x-coordinate of the vertex
+ * @param {Number} y y-coordinate of the vertex
+ * @chainable
+ *
+ * @example
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the shape.
+ * noFill();
+ * strokeWeight(1);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add the first control point.
+ * curveVertex(32, 91);
+ *
+ * // Add the anchor points.
+ * curveVertex(21, 17);
+ * curveVertex(68, 19);
+ *
+ * // Add the second control point.
+ * curveVertex(84, 91);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * // Style the anchor and control points.
+ * strokeWeight(5);
+ *
+ * // Draw the anchor points in black.
+ * stroke(0);
+ * point(21, 17);
+ * point(68, 19);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(32, 91);
+ * point(84, 91);
+ *
+ * describe(
+ * 'A black curve drawn on a gray background. The curve has black dots at its ends. Two red dots appear near the bottom of the canvas.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the shape.
+ * noFill();
+ * strokeWeight(1);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add the first control point and draw a segment to it.
+ * curveVertex(32, 91);
+ * curveVertex(32, 91);
+ *
+ * // Add the anchor points.
+ * curveVertex(21, 17);
+ * curveVertex(68, 19);
+ *
+ * // Add the second control point.
+ * curveVertex(84, 91);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * // Style the anchor and control points.
+ * strokeWeight(5);
+ *
+ * // Draw the anchor points in black.
+ * stroke(0);
+ * point(21, 17);
+ * point(68, 19);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(32, 91);
+ * point(84, 91);
+ *
+ * describe(
+ * 'A black curve drawn on a gray background. The curve passes through one red dot and two black dots. Another red dot appears near the bottom of the canvas.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the shape.
+ * noFill();
+ * strokeWeight(1);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add the first control point and draw a segment to it.
+ * curveVertex(32, 91);
+ * curveVertex(32, 91);
+ *
+ * // Add the anchor points.
+ * curveVertex(21, 17);
+ * curveVertex(68, 19);
+ *
+ * // Add the second control point and draw a segment to it.
+ * curveVertex(84, 91);
+ * curveVertex(84, 91);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * // Style the anchor and control points.
+ * strokeWeight(5);
+ *
+ * // Draw the anchor points in black.
+ * stroke(0);
+ * point(21, 17);
+ * point(68, 19);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(32, 91);
+ * point(84, 91);
+ *
+ * describe(
+ * 'A black U curve drawn upside down on a gray background. The curve passes from one red dot through two black dots and ends at another red dot.'
+ * );
+ * }
+ *
+ *
+ * // Click the mouse near the red dot in the bottom-left corner
+ * // and drag to change the curve's shape.
+ *
+ * let x1 = 32;
+ * let y1 = 91;
+ * let isChanging = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A black U curve drawn upside down on a gray background. The curve passes from one red dot through two black dots and ends at another red dot.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the shape.
+ * noFill();
+ * stroke(0);
+ * strokeWeight(1);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add the first control point and draw a segment to it.
+ * curveVertex(x1, y1);
+ * curveVertex(x1, y1);
+ *
+ * // Add the anchor points.
+ * curveVertex(21, 17);
+ * curveVertex(68, 19);
+ *
+ * // Add the second control point and draw a segment to it.
+ * curveVertex(84, 91);
+ * curveVertex(84, 91);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * // Style the anchor and control points.
+ * strokeWeight(5);
+ *
+ * // Draw the anchor points in black.
+ * stroke(0);
+ * point(21, 17);
+ * point(68, 19);
+ *
+ * // Draw the control points in red.
+ * stroke(255, 0, 0);
+ * point(x1, y1);
+ * point(84, 91);
+ * }
+ *
+ * // Start changing the first control point if the user clicks near it.
+ * function mousePressed() {
+ * if (dist(mouseX, mouseY, x1, y1) < 20) {
+ * isChanging = true;
+ * }
+ * }
+ *
+ * // Stop changing the first control point when the user releases the mouse.
+ * function mouseReleased() {
+ * isChanging = false;
+ * }
+ *
+ * // Update the first control point while the user drags the mouse.
+ * function mouseDragged() {
+ * if (isChanging === true) {
+ * x1 = mouseX;
+ * y1 = mouseY;
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add the first control point and draw a segment to it.
+ * curveVertex(32, 91);
+ * curveVertex(32, 91);
+ *
+ * // Add the anchor points.
+ * curveVertex(21, 17);
+ * curveVertex(68, 19);
+ *
+ * // Add the second control point.
+ * curveVertex(84, 91);
+ * curveVertex(84, 91);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('A ghost shape drawn in white on a gray background.');
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A ghost shape drawn in white on a blue background. When the user drags the mouse, the scene rotates to reveal the outline of a second ghost.');
+ * }
+ *
+ * function draw() {
+ * background('midnightblue');
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the first ghost.
+ * noStroke();
+ * fill('ghostwhite');
+ *
+ * beginShape();
+ * curveVertex(-28, 41, 0);
+ * curveVertex(-28, 41, 0);
+ * curveVertex(-29, -33, 0);
+ * curveVertex(18, -31, 0);
+ * curveVertex(34, 41, 0);
+ * curveVertex(34, 41, 0);
+ * endShape();
+ *
+ * // Draw the second ghost.
+ * noFill();
+ * stroke('ghostwhite');
+ *
+ * beginShape();
+ * curveVertex(-28, 41, -20);
+ * curveVertex(-28, 41, -20);
+ * curveVertex(-29, -33, -20);
+ * curveVertex(18, -31, -20);
+ * curveVertex(34, 41, -20);
+ * curveVertex(34, 41, -20);
+ * endShape();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Exterior vertices, clockwise winding.
+ * vertex(10, 10);
+ * vertex(90, 10);
+ * vertex(90, 90);
+ * vertex(10, 90);
+ *
+ * // Interior vertices, counter-clockwise winding.
+ * beginContour();
+ * vertex(30, 30);
+ * vertex(30, 70);
+ * vertex(70, 70);
+ * vertex(70, 30);
+ * endContour();
+ *
+ * // Stop drawing the shape.
+ * endShape(CLOSE);
+ *
+ * describe('A white square with a square hole in its center drawn on a gray background.');
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white square with a square hole in its center drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Exterior vertices, clockwise winding.
+ * vertex(-40, -40);
+ * vertex(40, -40);
+ * vertex(40, 40);
+ * vertex(-40, 40);
+ *
+ * // Interior vertices, counter-clockwise winding.
+ * beginContour();
+ * vertex(-20, -20);
+ * vertex(-20, 20);
+ * vertex(20, 20);
+ * vertex(20, -20);
+ * endContour();
+ *
+ * // Stop drawing the shape.
+ * endShape(CLOSE);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the shapes.
+ * noFill();
+ *
+ * // Left triangle.
+ * beginShape();
+ * vertex(20, 20);
+ * vertex(45, 20);
+ * vertex(45, 80);
+ * endShape(CLOSE);
+ *
+ * // Right triangle.
+ * beginShape();
+ * vertex(50, 20);
+ * vertex(75, 20);
+ * vertex(75, 80);
+ * endShape();
+ *
+ * describe(
+ * 'Two sets of black lines drawn on a gray background. The three lines on the left form a right triangle. The two lines on the right form a right angle.'
+ * );
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `#version 300 es
+ *
+ * precision mediump float;
+ *
+ * in vec3 aPosition;
+ * flat out int instanceID;
+ *
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * void main() {
+ *
+ * // Copy the instance ID to the fragment shader.
+ * instanceID = gl_InstanceID;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ *
+ * // gl_InstanceID represents a numeric value for each instance.
+ * // Using gl_InstanceID allows us to move each instance separately.
+ * // Here we move each instance horizontally by ID * 23.
+ * float xOffset = float(gl_InstanceID) * 23.0;
+ *
+ * // Apply the offset to the final position.
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * (positionVec4 -
+ * vec4(xOffset, 0.0, 0.0, 0.0));
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `#version 300 es
+ *
+ * precision mediump float;
+ *
+ * out vec4 outColor;
+ * flat in int instanceID;
+ * uniform float numInstances;
+ *
+ * void main() {
+ * vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
+ * vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
+ *
+ * // Normalize the instance ID.
+ * float normId = float(instanceID) / numInstances;
+ *
+ * // Mix between two colors using the normalized instance ID.
+ * outColor = mix(red, blue, normId);
+ * }
+ * `;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * let myShader = createShader(vertSrc, fragSrc);
+ *
+ * background(220);
+ *
+ * // Compile and apply the p5.Shader.
+ * shader(myShader);
+ *
+ * // Set the numInstances uniform.
+ * myShader.setUniform('numInstances', 4);
+ *
+ * // Translate the origin to help align the drawing.
+ * translate(25, -10);
+ *
+ * // Style the shapes.
+ * noStroke();
+ *
+ * // Draw the shapes.
+ * beginShape();
+ * vertex(0, 0);
+ * vertex(0, 20);
+ * vertex(20, 20);
+ * vertex(20, 0);
+ * vertex(0, 0);
+ * endShape(CLOSE, 4);
+ *
+ * describe('A row of four squares. Their colors transition from purple on the left to red on the right');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the curve.
+ * noFill();
+ *
+ * // Draw the curve.
+ * beginShape();
+ * vertex(20, 20);
+ * quadraticVertex(80, 20, 50, 50);
+ * endShape();
+ *
+ * describe('A black curve drawn on a gray square. The curve starts at the top-left corner and ends at the center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw the curve.
+ * noFill();
+ * beginShape();
+ * vertex(20, 20);
+ * quadraticVertex(80, 20, 50, 50);
+ * endShape();
+ *
+ * // Draw red lines from the anchor points to the control point.
+ * stroke(255, 0, 0);
+ * line(20, 20, 80, 20);
+ * line(50, 50, 80, 20);
+ *
+ * // Draw the anchor points in black.
+ * strokeWeight(5);
+ * stroke(0);
+ * point(20, 20);
+ * point(50, 50);
+ *
+ * // Draw the control point in red.
+ * stroke(255, 0, 0);
+ * point(80, 20);
+ *
+ * describe('A black curve that starts at the top-left corner and ends at the center. Its anchor and control points are marked with dots. Red lines connect both anchor points to the control point.');
+ * }
+ *
+ *
+ * // Click the mouse near the red dot in the top-right corner
+ * // and drag to change the curve's shape.
+ *
+ * let x2 = 80;
+ * let y2 = 20;
+ * let isChanging = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A black curve that starts at the top-left corner and ends at the center. Its anchor and control points are marked with dots. Red lines connect both anchor points to the control point.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the curve.
+ * noFill();
+ * strokeWeight(1);
+ * stroke(0);
+ *
+ * // Draw the curve.
+ * beginShape();
+ * vertex(20, 20);
+ * quadraticVertex(x2, y2, 50, 50);
+ * endShape();
+ *
+ * // Draw red lines from the anchor points to the control point.
+ * stroke(255, 0, 0);
+ * line(20, 20, x2, y2);
+ * line(50, 50, x2, y2);
+ *
+ * // Draw the anchor points in black.
+ * strokeWeight(5);
+ * stroke(0);
+ * point(20, 20);
+ * point(50, 50);
+ *
+ * // Draw the control point in red.
+ * stroke(255, 0, 0);
+ * point(x2, y2);
+ * }
+ *
+ * // Start changing the first control point if the user clicks near it.
+ * function mousePressed() {
+ * if (dist(mouseX, mouseY, x2, y2) < 20) {
+ * isChanging = true;
+ * }
+ * }
+ *
+ * // Stop changing the first control point when the user releases the mouse.
+ * function mouseReleased() {
+ * isChanging = false;
+ * }
+ *
+ * // Update the first control point while the user drags the mouse.
+ * function mouseDragged() {
+ * if (isChanging === true) {
+ * x2 = mouseX;
+ * y2 = mouseY;
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add the curved segments.
+ * vertex(20, 20);
+ * quadraticVertex(80, 20, 50, 50);
+ * quadraticVertex(20, 80, 80, 80);
+ *
+ * // Add the straight segments.
+ * vertex(80, 10);
+ * vertex(20, 10);
+ * vertex(20, 20);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('A white puzzle piece drawn on a gray background.');
+ * }
+ *
+ *
+ * // Click the and drag the mouse to view the scene from a different angle.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white puzzle piece on a dark gray background. When the user clicks and drags the scene, the outline of a second puzzle piece is revealed.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the first puzzle piece.
+ * noStroke();
+ * fill(255);
+ *
+ * // Draw the first puzzle piece.
+ * beginShape();
+ * vertex(-30, -30, 0);
+ * quadraticVertex(30, -30, 0, 0, 0, 0);
+ * quadraticVertex(-30, 30, 0, 30, 30, 0);
+ * vertex(30, -40, 0);
+ * vertex(-30, -40, 0);
+ * vertex(-30, -30, 0);
+ * endShape();
+ *
+ * // Style the second puzzle piece.
+ * stroke(255);
+ * noFill();
+ *
+ * // Draw the second puzzle piece.
+ * beginShape();
+ * vertex(-30, -30, -20);
+ * quadraticVertex(30, -30, -20, 0, 0, -20);
+ * quadraticVertex(-30, 30, -20, 30, 30, -20);
+ * vertex(30, -40, -20);
+ * vertex(-30, -40, -20);
+ * vertex(-30, -30, -20);
+ * endShape();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the shape.
+ * strokeWeight(3);
+ *
+ * // Start drawing the shape.
+ * // Only draw the vertices.
+ * beginShape(POINTS);
+ *
+ * // Add the vertices.
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ *
+ * describe('Four black dots that form a square are drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add vertices.
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ *
+ * // Stop drawing the shape.
+ * endShape(CLOSE);
+ *
+ * describe('A white square on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add vertices.
+ * vertex(-20, -30, 0);
+ * vertex(35, -30, 0);
+ * vertex(35, 25, 0);
+ * vertex(-20, 25, 0);
+ *
+ * // Stop drawing the shape.
+ * endShape(CLOSE);
+ *
+ * describe('A white square on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white square spins around slowly on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Start drawing the shape.
+ * beginShape();
+ *
+ * // Add vertices.
+ * vertex(-20, -30, 0);
+ * vertex(35, -30, 0);
+ * vertex(35, 25, 0);
+ * vertex(-20, 25, 0);
+ *
+ * // Stop drawing the shape.
+ * endShape(CLOSE);
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load an image to apply as a texture.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A photograph of a ceiling rotates slowly against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Style the shape.
+ * noStroke();
+ *
+ * // Apply the texture.
+ * texture(img);
+ * textureMode(NORMAL);
+ *
+ * // Start drawing the shape
+ * beginShape();
+ *
+ * // Add vertices.
+ * vertex(-20, -30, 0, 0, 0);
+ * vertex(35, -30, 0, 1, 0);
+ * vertex(35, 25, 0, 1, 1);
+ * vertex(-20, 25, 0, 0, 1);
+ *
+ * // Stop drawing the shape.
+ * endShape();
+ * }
+ *
+ *
+ * beginShape();
+ *
+ * // Set the vertex normal.
+ * normal(-0.4, -0.4, 0.8);
+ *
+ * // Add a vertex.
+ * vertex(-30, -30, 0);
+ *
+ * // Set the vertex normal.
+ * normal(0, 0, 1);
+ *
+ * // Add vertices.
+ * vertex(30, -30, 0);
+ * vertex(30, 30, 0);
+ *
+ * // Set the vertex normal.
+ * normal(0.4, -0.4, 0.8);
+ *
+ * // Add a vertex.
+ * vertex(-30, 30, 0);
+ *
+ * endShape();
+ *
+ *
+ * @method normal
+ * @param {p5.Vector} vector vertex normal as a p5.Vector object.
+ * @chainable
+ *
+ * @example
+ *
+ * // Click the and drag the mouse to view the scene from a different angle.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'A colorful square on a black background. The square changes color and rotates when the user drags the mouse. Parts of its surface reflect light in different directions.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the shape.
+ * normalMaterial();
+ * noStroke();
+ *
+ * // Draw the shape.
+ * beginShape();
+ * vertex(-30, -30, 0);
+ * vertex(30, -30, 0);
+ * vertex(30, 30, 0);
+ * vertex(-30, 30, 0);
+ * endShape();
+ * }
+ *
+ *
+ * // Click the and drag the mouse to view the scene from a different angle.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'A colorful square on a black background. The square changes color and rotates when the user drags the mouse. Parts of its surface reflect light in different directions.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the shape.
+ * normalMaterial();
+ * noStroke();
+ *
+ * // Draw the shape.
+ * // Use normal() to set vertex normals.
+ * beginShape();
+ * normal(-0.4, -0.4, 0.8);
+ * vertex(-30, -30, 0);
+ *
+ * normal(0, 0, 1);
+ * vertex(30, -30, 0);
+ * vertex(30, 30, 0);
+ *
+ * normal(0.4, -0.4, 0.8);
+ * vertex(-30, 30, 0);
+ * endShape();
+ * }
+ *
+ *
+ * // Click the and drag the mouse to view the scene from a different angle.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'A colorful square on a black background. The square changes color and rotates when the user drags the mouse. Parts of its surface reflect light in different directions.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the shape.
+ * normalMaterial();
+ * noStroke();
+ *
+ * // Create p5.Vector objects.
+ * let n1 = createVector(-0.4, -0.4, 0.8);
+ * let n2 = createVector(0, 0, 1);
+ * let n3 = createVector(0.4, -0.4, 0.8);
+ *
+ * // Draw the shape.
+ * // Use normal() to set vertex normals.
+ * beginShape();
+ * normal(n1);
+ * vertex(-30, -30, 0);
+ *
+ * normal(n2);
+ * vertex(30, -30, 0);
+ * vertex(30, 30, 0);
+ *
+ * normal(n3);
+ * vertex(-30, 30, 0);
+ * endShape();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Turn off the draw loop.
+ * noLoop();
+ *
+ * describe('A white half-circle on the left edge of a gray square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the circle's x-coordinate.
+ * let x = frameCount;
+ *
+ * // Draw the circle.
+ * // Normally, the circle would move from left to right.
+ * circle(x, 50, 20);
+ * }
+ *
+ *
+ * // Double-click to stop the draw loop.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Slow the frame rate.
+ * frameRate(5);
+ *
+ * describe('A white circle moves randomly on a gray background. It stops moving when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the circle's coordinates.
+ * let x = random(0, 100);
+ * let y = random(0, 100);
+ *
+ * // Draw the circle.
+ * // Normally, the circle would move from left to right.
+ * circle(x, y, 20);
+ * }
+ *
+ * // Stop the draw loop when the user double-clicks.
+ * function doubleClicked() {
+ * noLoop();
+ * }
+ *
+ *
+ * let startButton;
+ * let stopButton;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create the button elements and place them
+ * // beneath the canvas.
+ * startButton = createButton('▶');
+ * startButton.position(0, 100);
+ * startButton.size(50, 20);
+ * stopButton = createButton('◾');
+ * stopButton.position(50, 100);
+ * stopButton.size(50, 20);
+ *
+ * // Set functions to call when the buttons are pressed.
+ * startButton.mousePressed(loop);
+ * stopButton.mousePressed(noLoop);
+ *
+ * // Slow the frame rate.
+ * frameRate(5);
+ *
+ * describe(
+ * 'A white circle moves randomly on a gray background. Play and stop buttons are shown beneath the canvas. The circle stops or starts moving when the user presses a button.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the circle's coordinates.
+ * let x = random(0, 100);
+ * let y = random(0, 100);
+ *
+ * // Draw the circle.
+ * // Normally, the circle would move from left to right.
+ * circle(x, y, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Turn off the draw loop.
+ * noLoop();
+ *
+ * describe(
+ * 'A white half-circle on the left edge of a gray square. The circle starts moving to the right when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the circle's x-coordinate.
+ * let x = frameCount;
+ *
+ * // Draw the circle.
+ * circle(x, 50, 20);
+ * }
+ *
+ * // Resume the draw loop when the user double-clicks.
+ * function doubleClicked() {
+ * loop();
+ * }
+ *
+ *
+ * let startButton;
+ * let stopButton;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create the button elements and place them
+ * // beneath the canvas.
+ * startButton = createButton('▶');
+ * startButton.position(0, 100);
+ * startButton.size(50, 20);
+ * stopButton = createButton('◾');
+ * stopButton.position(50, 100);
+ * stopButton.size(50, 20);
+ *
+ * // Set functions to call when the buttons are pressed.
+ * startButton.mousePressed(loop);
+ * stopButton.mousePressed(noLoop);
+ *
+ * // Slow the frame rate.
+ * frameRate(5);
+ *
+ * describe(
+ * 'A white circle moves randomly on a gray background. Play and stop buttons are shown beneath the canvas. The circle stops or starts moving when the user presses a button.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the circle's coordinates.
+ * let x = random(0, 100);
+ * let y = random(0, 100);
+ *
+ * // Draw the circle.
+ * // Normally, the circle would move from left to right.
+ * circle(x, y, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white circle drawn against a gray background. When the user double-clicks, the circle stops or resumes following the mouse.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the circle at the mouse's position.
+ * circle(mouseX, mouseY, 20);
+ * }
+ *
+ * // Toggle the draw loop when the user double-clicks.
+ * function doubleClicked() {
+ * if (isLooping() === true) {
+ * noLoop();
+ * } else {
+ * loop();
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw the left circle.
+ * circle(25, 50, 20);
+ *
+ * // Begin the drawing group.
+ * push();
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Style the circle.
+ * strokeWeight(5);
+ * stroke('royalblue');
+ * fill('orange');
+ *
+ * // Draw the circle.
+ * circle(0, 0, 20);
+ *
+ * // End the drawing group.
+ * pop();
+ *
+ * // Draw the right circle.
+ * circle(75, 50, 20);
+ *
+ * describe(
+ * 'Three circles drawn in a row on a gray background. The left and right circles are white with thin, black borders. The middle circle is orange with a thick, blue border.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Slow the frame rate.
+ * frameRate(24);
+ *
+ * describe('A mosquito buzzes in front of a green frog. The frog follows the mouse as the user moves.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Begin the drawing group.
+ * push();
+ *
+ * // Translate the origin to the mouse's position.
+ * translate(mouseX, mouseY);
+ *
+ * // Style the face.
+ * noStroke();
+ * fill('green');
+ *
+ * // Draw a face.
+ * circle(0, 0, 60);
+ *
+ * // Style the eyes.
+ * fill('white');
+ *
+ * // Draw the left eye.
+ * push();
+ * translate(-20, -20);
+ * ellipse(0, 0, 30, 20);
+ * fill('black');
+ * circle(0, 0, 8);
+ * pop();
+ *
+ * // Draw the right eye.
+ * push();
+ * translate(20, -20);
+ * ellipse(0, 0, 30, 20);
+ * fill('black');
+ * circle(0, 0, 8);
+ * pop();
+ *
+ * // End the drawing group.
+ * pop();
+ *
+ * // Draw a bug.
+ * let x = random(0, 100);
+ * let y = random(0, 100);
+ * text('🦟', x, y);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'Two spheres drawn on a gray background. The sphere on the left is red and lit from the front. The sphere on the right is a blue wireframe.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the red sphere.
+ * push();
+ * translate(-25, 0, 0);
+ * noStroke();
+ * directionalLight(255, 0, 0, 0, 0, -1);
+ * sphere(20);
+ * pop();
+ *
+ * // Draw the blue sphere.
+ * push();
+ * translate(25, 0, 0);
+ * strokeWeight(0.3);
+ * stroke(0, 0, 255);
+ * noFill();
+ * sphere(20);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw the left circle.
+ * circle(25, 50, 20);
+ *
+ * // Begin the drawing group.
+ * push();
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Style the circle.
+ * strokeWeight(5);
+ * stroke('royalblue');
+ * fill('orange');
+ *
+ * // Draw the circle.
+ * circle(0, 0, 20);
+ *
+ * // End the drawing group.
+ * pop();
+ *
+ * // Draw the right circle.
+ * circle(75, 50, 20);
+ *
+ * describe(
+ * 'Three circles drawn in a row on a gray background. The left and right circles are white with thin, black borders. The middle circle is orange with a thick, blue border.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Slow the frame rate.
+ * frameRate(24);
+ *
+ * describe('A mosquito buzzes in front of a green frog. The frog follows the mouse as the user moves.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Begin the drawing group.
+ * push();
+ *
+ * // Translate the origin to the mouse's position.
+ * translate(mouseX, mouseY);
+ *
+ * // Style the face.
+ * noStroke();
+ * fill('green');
+ *
+ * // Draw a face.
+ * circle(0, 0, 60);
+ *
+ * // Style the eyes.
+ * fill('white');
+ *
+ * // Draw the left eye.
+ * push();
+ * translate(-20, -20);
+ * ellipse(0, 0, 30, 20);
+ * fill('black');
+ * circle(0, 0, 8);
+ * pop();
+ *
+ * // Draw the right eye.
+ * push();
+ * translate(20, -20);
+ * ellipse(0, 0, 30, 20);
+ * fill('black');
+ * circle(0, 0, 8);
+ * pop();
+ *
+ * // End the drawing group.
+ * pop();
+ *
+ * // Draw a bug.
+ * let x = random(0, 100);
+ * let y = random(0, 100);
+ * text('🦟', x, y);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'Two spheres drawn on a gray background. The sphere on the left is red and lit from the front. The sphere on the right is a blue wireframe.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the red sphere.
+ * push();
+ * translate(-25, 0, 0);
+ * noStroke();
+ * directionalLight(255, 0, 0, 0, 0, -1);
+ * sphere(20);
+ * pop();
+ *
+ * // Draw the blue sphere.
+ * push();
+ * translate(25, 0, 0);
+ * strokeWeight(0.3);
+ * stroke(0, 0, 255);
+ * noFill();
+ * sphere(20);
+ * pop();
+ * }
+ *
+ *
+ * // Double-click the canvas to move the circle.
+ *
+ * let x = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Turn off the draw loop.
+ * noLoop();
+ *
+ * describe(
+ * 'A white half-circle on the left edge of a gray square. The circle moves a little to the right when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the circle.
+ * circle(x, 50, 20);
+ *
+ * // Increment x.
+ * x += 5;
+ * }
+ *
+ * // Run the draw loop when the user double-clicks.
+ * function doubleClicked() {
+ * redraw();
+ * }
+ *
+ *
+ * // Double-click the canvas to move the circle.
+ *
+ * let x = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Turn off the draw loop.
+ * noLoop();
+ *
+ * describe(
+ * 'A white half-circle on the left edge of a gray square. The circle hops to the right when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the circle.
+ * circle(x, 50, 20);
+ *
+ * // Increment x.
+ * x += 5;
+ * }
+ *
+ * // Run the draw loop three times when the user double-clicks.
+ * function doubleClicked() {
+ * redraw(3);
+ * }
+ *
+ *
+ * // Declare the function containing the sketch.
+ * function sketch(p) {
+ *
+ * // Declare the setup() method.
+ * p.setup = function () {
+ * p.createCanvas(100, 100);
+ *
+ * p.describe('A white circle drawn on a gray background.');
+ * };
+ *
+ * // Declare the draw() method.
+ * p.draw = function () {
+ * p.background(200);
+ *
+ * // Draw the circle.
+ * p.circle(50, 50, 20);
+ * };
+ * }
+ *
+ * // Initialize the sketch.
+ * new p5(sketch);
+ *
+ *
+ * // Declare the function containing the sketch.
+ * function sketch(p) {
+ * // Create the sketch's variables within its scope.
+ * let x = 50;
+ * let y = 50;
+ *
+ * // Declare the setup() method.
+ * p.setup = function () {
+ * p.createCanvas(100, 100);
+ *
+ * p.describe('A white circle moves randomly on a gray background.');
+ * };
+ *
+ * // Declare the draw() method.
+ * p.draw = function () {
+ * p.background(200);
+ *
+ * // Update x and y.
+ * x += p.random(-1, 1);
+ * y += p.random(-1, 1);
+ *
+ * // Draw the circle.
+ * p.circle(x, y, 20);
+ * };
+ * }
+ *
+ * // Initialize the sketch.
+ * new p5(sketch);
+ *
+ *
+ * // Declare the function containing the sketch.
+ * function sketch(p) {
+ *
+ * // Declare the setup() method.
+ * p.setup = function () {
+ * p.createCanvas(100, 100);
+ *
+ * p.describe('A white circle drawn on a gray background.');
+ * };
+ *
+ * // Declare the draw() method.
+ * p.draw = function () {
+ * p.background(200);
+ *
+ * // Draw the circle.
+ * p.circle(50, 50, 20);
+ * };
+ * }
+ *
+ * // Select the web page's body element.
+ * let body = document.querySelector('body');
+ *
+ * // Initialize the sketch and attach it to the web page's body.
+ * new p5(sketch, body);
+ *
+ *
+ * // Declare the function containing the sketch.
+ * function sketch(p) {
+ *
+ * // Declare the setup() method.
+ * p.setup = function () {
+ * p.createCanvas(100, 100);
+ *
+ * p.describe(
+ * 'A white circle drawn on a gray background. The circle follows the mouse as the user moves.'
+ * );
+ * };
+ *
+ * // Declare the draw() method.
+ * p.draw = function () {
+ * p.background(200);
+ *
+ * // Draw the circle.
+ * p.circle(p.mouseX, p.mouseY, 20);
+ * };
+ * }
+ *
+ * // Initialize the sketch.
+ * new p5(sketch);
+ *
+ *
+ * // Declare the function containing the sketch.
+ * function sketch(p) {
+ *
+ * // Declare the setup() method.
+ * p.setup = function () {
+ * p.createCanvas(100, 100);
+ *
+ * p.describe(
+ * 'A white circle drawn on a gray background. The circle follows the mouse as the user moves. The circle becomes black when the user double-clicks.'
+ * );
+ * };
+ *
+ * // Declare the draw() method.
+ * p.draw = function () {
+ * p.background(200);
+ *
+ * // Draw the circle.
+ * p.circle(p.mouseX, p.mouseY, 20);
+ * };
+ *
+ * // Declare the doubleClicked() method.
+ * p.doubleClicked = function () {
+ * // Change the fill color when the user double-clicks.
+ * p.fill(0);
+ * };
+ * }
+ *
+ * // Initialize the sketch.
+ * new p5(sketch);
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white circle on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * applyMatrix(1, 0, 0, 1, 50, 50);
+ *
+ * // Draw the circle at coordinates (0, 0).
+ * circle(0, 0, 40);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white circle on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * let m = [1, 0, 0, 1, 50, 50];
+ * applyMatrix(m);
+ *
+ * // Draw the circle at coordinates (0, 0).
+ * circle(0, 0, 40);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe("A white rectangle on a gray background. The rectangle's long axis runs from top-left to bottom-right.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate the coordinate system 1/8 turn.
+ * let angle = QUARTER_PI;
+ * let ca = cos(angle);
+ * let sa = sin(angle);
+ * applyMatrix(ca, sa, -sa, ca, 0, 0);
+ *
+ * // Draw a rectangle at coordinates (50, 0).
+ * rect(50, 0, 40, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'Two white squares on a gray background. The larger square appears at the top-center. The smaller square appears at the top-left.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a square at (30, 20).
+ * square(30, 20, 40);
+ *
+ * // Scale the coordinate system by a factor of 0.5.
+ * applyMatrix(0.5, 0, 0, 0.5, 0, 0);
+ *
+ * // Draw a square at (30, 20).
+ * // It appears at (15, 10) after scaling.
+ * square(30, 20, 40);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white quadrilateral on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the shear factor.
+ * let angle = QUARTER_PI;
+ * let shearFactor = 1 / tan(HALF_PI - angle);
+ *
+ * // Shear the coordinate system along the x-axis.
+ * applyMatrix(1, 0, shearFactor, 1, 0, 0);
+ *
+ * // Draw the square.
+ * square(0, 0, 50);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube rotates slowly against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system a little more each frame.
+ * let angle = frameCount * 0.01;
+ * let ca = cos(angle);
+ * let sa = sin(angle);
+ * applyMatrix(ca, 0, sa, 0, 0, 1, 0, 0, -sa, 0, ca, 0, 0, 0, 0, 1);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'Two circles drawn on a gray background. A blue circle is at the top-left and a red circle is at the bottom-right.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Draw a blue circle at the coordinates (25, 25).
+ * fill('blue');
+ * circle(25, 25, 20);
+ *
+ * // Clear all transformations.
+ * // The origin is now at the top-left corner.
+ * resetMatrix();
+ *
+ * // Draw a red circle at the coordinates (25, 25).
+ * fill('red');
+ * circle(25, 25, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * "A white rectangle on a gray background. The rectangle's long axis runs from top-left to bottom-right."
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate the coordinate system 1/8 turn.
+ * rotate(QUARTER_PI);
+ *
+ * // Draw a rectangle at coordinates (50, 0).
+ * rect(50, 0, 40, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * "A white rectangle on a gray background. The rectangle's long axis runs from top-left to bottom-right."
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate the coordinate system 1/16 turn.
+ * rotate(QUARTER_PI / 2);
+ *
+ * // Rotate the coordinate system another 1/16 turn.
+ * rotate(QUARTER_PI / 2);
+ *
+ * // Draw a rectangle at coordinates (50, 0).
+ * rect(50, 0, 40, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * describe(
+ * "A white rectangle on a gray background. The rectangle's long axis runs from top-left to bottom-right."
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate the coordinate system 1/8 turn.
+ * rotate(45);
+ *
+ * // Draw a rectangle at coordinates (50, 0).
+ * rect(50, 0, 40, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A white rectangle on a gray background. The rectangle rotates slowly about the top-left corner. It disappears and reappears periodically.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate the coordinate system a little more each frame.
+ * let angle = frameCount * 0.01;
+ * rotate(angle);
+ *
+ * // Draw a rectangle at coordinates (50, 0).
+ * rect(50, 0, 40, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe("A cube on a gray background. The cube's front face points to the top-right.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate the coordinate system 1/8 turn about
+ * // the axis [1, 1, 0].
+ * let axis = createVector(1, 1, 0);
+ * rotate(QUARTER_PI, axis);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe("A cube on a gray background. The cube's front face points to the top-right.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate the coordinate system 1/8 turn about
+ * // the axis [1, 1, 0].
+ * let axis = [1, 1, 0];
+ * rotate(QUARTER_PI, axis);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system 1/8 turn.
+ * rotateX(QUARTER_PI);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system 1/16 turn.
+ * rotateX(QUARTER_PI / 2);
+ *
+ * // Rotate the coordinate system 1/16 turn.
+ * rotateX(QUARTER_PI / 2);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * describe('A white cube on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system 1/8 turn.
+ * rotateX(45);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube rotates slowly against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system a little more each frame.
+ * let angle = frameCount * 0.01;
+ * rotateX(angle);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system 1/8 turn.
+ * rotateY(QUARTER_PI);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system 1/16 turn.
+ * rotateY(QUARTER_PI / 2);
+ *
+ * // Rotate the coordinate system 1/16 turn.
+ * rotateY(QUARTER_PI / 2);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * describe('A white cube on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system 1/8 turn.
+ * rotateY(45);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube rotates slowly against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system a little more each frame.
+ * let angle = frameCount * 0.01;
+ * rotateY(angle);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system 1/8 turn.
+ * rotateZ(QUARTER_PI);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system 1/16 turn.
+ * rotateZ(QUARTER_PI / 2);
+ *
+ * // Rotate the coordinate system 1/16 turn.
+ * rotateZ(QUARTER_PI / 2);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * describe('A white cube on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system 1/8 turn.
+ * rotateZ(45);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube rotates slowly against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rotate the coordinate system a little more each frame.
+ * let angle = frameCount * 0.01;
+ * rotateZ(angle);
+ *
+ * // Draw a box.
+ * box();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'Two white squares on a gray background. The larger square appears at the top-center. The smaller square appears at the top-left.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a square at (30, 20).
+ * square(30, 20, 40);
+ *
+ * // Scale the coordinate system by a factor of 0.5.
+ * scale(0.5);
+ *
+ * // Draw a square at (30, 20).
+ * // It appears at (15, 10) after scaling.
+ * square(30, 20, 40);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A rectangle and a square drawn in white on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a square at (30, 20).
+ * square(30, 20, 40);
+ *
+ * // Scale the coordinate system by factors of
+ * // 0.5 along the x-axis and
+ * // 1.3 along the y-axis.
+ * scale(0.5, 1.3);
+ *
+ * // Draw a square at (30, 20).
+ * // It appears as a rectangle at (15, 26) after scaling.
+ * square(30, 20, 40);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A rectangle and a square drawn in white on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a square at (30, 20).
+ * square(30, 20, 40);
+ *
+ * // Create a p5.Vector object.
+ * let v = createVector(0.5, 1.3);
+ *
+ * // Scale the coordinate system by factors of
+ * // 0.5 along the x-axis and
+ * // 1.3 along the y-axis.
+ * scale(v);
+ *
+ * // Draw a square at (30, 20).
+ * // It appears as a rectangle at (15, 26) after scaling.
+ * square(30, 20, 40);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'A red box and a blue box drawn on a gray background. The red box appears embedded in the blue box.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the spheres.
+ * noStroke();
+ *
+ * // Draw the red sphere.
+ * fill('red');
+ * box();
+ *
+ * // Scale the coordinate system by factors of
+ * // 0.5 along the x-axis and
+ * // 1.3 along the y-axis and
+ * // 2 along the z-axis.
+ * scale(0.5, 1.3, 2);
+ *
+ * // Draw the blue sphere.
+ * fill('blue');
+ * box();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white quadrilateral on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Shear the coordinate system along the x-axis.
+ * shearX(QUARTER_PI);
+ *
+ * // Draw the square.
+ * square(0, 0, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * describe('A white quadrilateral on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Shear the coordinate system along the x-axis.
+ * shearX(45);
+ *
+ * // Draw the square.
+ * square(0, 0, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white quadrilateral on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Shear the coordinate system along the x-axis.
+ * shearY(QUARTER_PI);
+ *
+ * // Draw the square.
+ * square(0, 0, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * describe('A white quadrilateral on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Shear the coordinate system along the x-axis.
+ * shearY(45);
+ *
+ * // Draw the square.
+ * square(0, 0, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white circle on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Draw a circle at coordinates (0, 0).
+ * circle(0, 0, 40);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'Two circles drawn on a gray background. The blue circle on the right overlaps the red circle at the center.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Draw the red circle.
+ * fill('red');
+ * circle(0, 0, 40);
+ *
+ * // Translate the origin to the right.
+ * translate(25, 0);
+ *
+ * // Draw the blue circle.
+ * fill('blue');
+ * circle(0, 0, 40);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white circle moves slowly from left to right on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the x-coordinate.
+ * let x = frameCount * 0.2;
+ *
+ * // Translate the origin.
+ * translate(x, 50);
+ *
+ * // Draw a circle at coordinates (0, 0).
+ * circle(0, 0, 40);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white circle on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Create a p5.Vector object.
+ * let v = createVector(50, 50);
+ *
+ * // Translate the origin by the vector.
+ * translate(v);
+ *
+ * // Draw a circle at coordinates (0, 0).
+ * circle(0, 0, 40);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'Two spheres sitting side-by-side on gray background. The sphere at the center is red. The sphere on the right is blue.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the spheres.
+ * noStroke();
+ *
+ * // Draw the red sphere.
+ * fill('red');
+ * sphere(10);
+ *
+ * // Translate the origin to the right.
+ * translate(30, 0, 0);
+ *
+ * // Draw the blue sphere.
+ * fill('blue');
+ * sphere(10);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Store the player's name.
+ * storeItem('name', 'Feist');
+ *
+ * // Store the player's score.
+ * storeItem('score', 1234);
+ *
+ * describe('The text "Feist: 1234" written in black on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(14);
+ *
+ * // Retrieve the name.
+ * let name = getItem('name');
+ *
+ * // Retrieve the score.
+ * let score = getItem('score');
+ *
+ * // Display the score.
+ * text(`${name}: ${score}`, 50, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create an object.
+ * let p = { x: 50, y: 50 };
+ *
+ * // Store the object.
+ * storeItem('position', p);
+ *
+ * describe('A white circle on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Retrieve the object.
+ * let p = getItem('position');
+ *
+ * // Draw the circle.
+ * circle(p.x, p.y, 30);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Store the player's name.
+ * storeItem('name', 'Feist');
+ *
+ * // Store the player's score.
+ * storeItem('score', 1234);
+ *
+ * describe('The text "Feist: 1234" written in black on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(14);
+ *
+ * // Retrieve the name.
+ * let name = getItem('name');
+ *
+ * // Retrieve the score.
+ * let score = getItem('score');
+ *
+ * // Display the score.
+ * text(`${name}: ${score}`, 50, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create an object.
+ * let p = { x: 50, y: 50 };
+ *
+ * // Store the object.
+ * storeItem('position', p);
+ *
+ * describe('A white circle on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Retrieve the object.
+ * let p = getItem('position');
+ *
+ * // Draw the circle.
+ * circle(p.x, p.y, 30);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.Color object.
+ * let c = color('deeppink');
+ *
+ * // Store the object.
+ * storeItem('color', c);
+ *
+ * describe('A pink circle on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Retrieve the object.
+ * let c = getItem('color');
+ *
+ * // Style the circle.
+ * fill(c);
+ *
+ * // Draw the circle.
+ * circle(50, 50, 30);
+ * }
+ *
+ *
+ * // Double-click to clear localStorage.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Store the player's name.
+ * storeItem('name', 'Feist');
+ *
+ * // Store the player's score.
+ * storeItem('score', 1234);
+ *
+ * describe(
+ * 'The text "Feist: 1234" written in black on a gray background. The text "null: null" appears when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(14);
+ *
+ * // Retrieve the name.
+ * let name = getItem('name');
+ *
+ * // Retrieve the score.
+ * let score = getItem('score');
+ *
+ * // Display the score.
+ * text(`${name}: ${score}`, 50, 50);
+ * }
+ *
+ * // Clear localStorage when the user double-clicks.
+ * function doubleClicked() {
+ * clearStorage();
+ * }
+ *
+ *
+ * // Double-click to remove an item from localStorage.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Store the player's name.
+ * storeItem('name', 'Feist');
+ *
+ * // Store the player's score.
+ * storeItem('score', 1234);
+ *
+ * describe(
+ * 'The text "Feist: 1234" written in black on a gray background. The text "Feist: null" appears when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(14);
+ *
+ * // Retrieve the name.
+ * let name = getItem('name');
+ *
+ * // Retrieve the score.
+ * let score = getItem('score');
+ *
+ * // Display the score.
+ * text(`${name}: ${score}`, 50, 50);
+ * }
+ *
+ * // Remove the word from localStorage when the user double-clicks.
+ * function doubleClicked() {
+ * removeItem('score');
+ * }
+ *
+ *
+ * function setup() {
+ * let myDictionary = createStringDict('p5', 'js');
+ * print(myDictionary.hasKey('p5')); // logs true to console
+ *
+ * let anotherDictionary = createStringDict({ happy: 'coding' });
+ * print(anotherDictionary.hasKey('happy')); // logs true to console
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict(100, 42);
+ * print(myDictionary.hasKey(100)); // logs true to console
+ *
+ * let anotherDictionary = createNumberDict({ 200: 84 });
+ * print(anotherDictionary.hasKey(200)); // logs true to console
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict(1, 10);
+ * myDictionary.create(2, 20);
+ * myDictionary.create(3, 30);
+ * print(myDictionary.size()); // logs 3 to the console
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createStringDict('p5', 'js');
+ * print(myDictionary.hasKey('p5')); // logs true to console
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createStringDict('p5', 'js');
+ * let myValue = myDictionary.get('p5');
+ * print(myValue === 'js'); // logs true to console
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createStringDict('p5', 'js');
+ * myDictionary.set('p5', 'JS');
+ * myDictionary.print(); // logs "key: p5 - value: JS" to console
+ * }
+ *
+ * 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
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createStringDict('p5', 'js');
+ * print(myDictionary.hasKey('p5')); // prints 'true'
+ * myDictionary.clear();
+ * print(myDictionary.hasKey('p5')); // prints 'false'
+ * }
+ *
+ *
+ * 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
+ * }
+ *
+ * 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
+ * }
+ *
+ *
+ * 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');
+ * }
+ * }
+ *
+ *
+ * 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');
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict(2, 5);
+ * myDictionary.add(2, 2);
+ * print(myDictionary.get(2)); // logs 7 to console.
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict(2, 5);
+ * myDictionary.sub(2, 2);
+ * print(myDictionary.get(2)); // logs 3 to console.
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict(2, 4);
+ * myDictionary.mult(2, 2);
+ * print(myDictionary.get(2)); // logs 8 to console.
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict(2, 8);
+ * myDictionary.div(2, 2);
+ * print(myDictionary.get(2)); // logs 4 to console.
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict({ 2: -10, 4: 0.65, 1.2: 3 });
+ * let lowestValue = myDictionary.minValue(); // value is -10
+ * print(lowestValue);
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict({ 2: -10, 4: 0.65, 1.2: 3 });
+ * let highestValue = myDictionary.maxValue(); // value is 3
+ * print(highestValue);
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict({ 2: 4, 4: 6, 1.2: 3 });
+ * let lowestKey = myDictionary.minKey(); // value is 1.2
+ * print(lowestKey);
+ * }
+ *
+ * function setup() {
+ * let myDictionary = createNumberDict({ 2: 4, 4: 6, 1.2: 3 });
+ * let highestKey = myDictionary.maxKey(); // value is 4
+ * print(highestKey);
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * background(200);
+ *
+ * // Select the canvas by its tag.
+ * let cnv = select('canvas');
+ * cnv.style('border', '5px deeppink dashed');
+ *
+ * describe('A gray square with a dashed pink border.');
+ * }
+ *
+ *
+ * function setup() {
+ * let cnv = createCanvas(100, 100);
+ *
+ * // Add a class attribute to the canvas.
+ * cnv.class('pinkborder');
+ *
+ * background(200);
+ *
+ * // Select the canvas by its class.
+ * cnv = select('.pinkborder');
+ *
+ * // Style its border.
+ * cnv.style('border', '5px deeppink dashed');
+ *
+ * describe('A gray square with a dashed pink border.');
+ * }
+ *
+ *
+ * function setup() {
+ * let cnv = createCanvas(100, 100);
+ *
+ * // Set the canvas' ID.
+ * cnv.id('mycanvas');
+ *
+ * background(200);
+ *
+ * // Select the canvas by its ID.
+ * cnv = select('#mycanvas');
+ *
+ * // Style its border.
+ * cnv.style('border', '5px deeppink dashed');
+ *
+ * describe('A gray square with a dashed pink border.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create three buttons.
+ * createButton('1');
+ * createButton('2');
+ * createButton('3');
+ *
+ * // Select the buttons by their tag.
+ * let buttons = selectAll('button');
+ *
+ * // Position the buttons.
+ * for (let i = 0; i < 3; i += 1) {
+ * buttons[i].position(0, i * 30);
+ * }
+ *
+ * describe('Three buttons stacked vertically. The buttons are labeled, "1", "2", and "3".');
+ * }
+ *
+ *
+ * function setup() {
+ * // Create three buttons and position them.
+ * let b1 = createButton('1');
+ * b1.position(0, 0);
+ * let b2 = createButton('2');
+ * b2.position(0, 30);
+ * let b3 = createButton('3');
+ * b3.position(0, 60);
+ *
+ * // Add a class attribute to each button.
+ * b1.class('btn');
+ * b2.class('btn btn-pink');
+ * b3.class('btn');
+ *
+ * // Select the buttons by their class.
+ * let buttons = selectAll('.btn');
+ * let pinkButtons = selectAll('.btn-pink');
+ *
+ * // Style the selected buttons.
+ * buttons.forEach(setFont);
+ * pinkButtons.forEach(setColor);
+ *
+ * describe('Three buttons stacked vertically. The buttons are labeled, "1", "2", and "3". Buttons "1" and "3" are gray. Button "2" is pink.');
+ * }
+ *
+ * // Set a button's font to Comic Sans MS.
+ * function setFont(btn) {
+ * btn.style('font-family', 'Comic Sans MS');
+ * }
+ *
+ * // Set a button's background and font color.
+ * function setColor(btn) {
+ * btn.style('background', 'deeppink');
+ * btn.style('color', 'white');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a paragraph element and place
+ * // it in the middle of the canvas.
+ * let p = createP('p5*js');
+ * p.position(25, 25);
+ *
+ * describe('A gray square with the text "p5*js" written in its center. The text disappears when the mouse is pressed.');
+ * }
+ *
+ * // Remove all elements when the mouse is pressed.
+ * function mousePressed() {
+ * removeElements();
+ * }
+ *
+ *
+ * let slider;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a paragraph element and place
+ * // it at the top of the canvas.
+ * let p = createP('p5*js');
+ * p.position(25, 25);
+ *
+ * // Create a slider element and place it
+ * // beneath the canvas.
+ * slider = createSlider(0, 255, 200);
+ * slider.position(0, 100);
+ *
+ * describe('A gray square with the text "p5*js" written in its center and a range slider beneath it. The square changes color when the slider is moved. The text and slider disappear when the square is double-clicked.');
+ * }
+ *
+ * function draw() {
+ * // Use the slider value to change the background color.
+ * let g = slider.value();
+ * background(g);
+ * }
+ *
+ * // Remove all elements when the mouse is double-clicked.
+ * function doubleClicked() {
+ * removeElements();
+ * }
+ *
+ *
+ * let dropdown;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a dropdown menu and add a few color options.
+ * dropdown = createSelect();
+ * dropdown.position(0, 0);
+ * dropdown.option('red');
+ * dropdown.option('green');
+ * dropdown.option('blue');
+ *
+ * // Call paintBackground() when the color option changes.
+ * dropdown.changed(paintBackground);
+ *
+ * describe('A gray square with a dropdown menu at the top. The square changes color when an option is selected.');
+ * }
+ *
+ * // Paint the background with the selected color.
+ * function paintBackground() {
+ * let c = dropdown.value();
+ * background(c);
+ * }
+ *
+ *
+ * let checkbox;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a checkbox and place it beneath the canvas.
+ * checkbox = createCheckbox(' circle');
+ * checkbox.position(0, 100);
+ *
+ * // Call repaint() when the checkbox changes.
+ * checkbox.changed(repaint);
+ *
+ * describe('A gray square with a checkbox underneath it that says "circle". A white circle appears when the box is checked and disappears otherwise.');
+ * }
+ *
+ * // Paint the background gray and determine whether to draw a circle.
+ * function repaint() {
+ * background(200);
+ * if (checkbox.checked() === true) {
+ * circle(50, 50, 30);
+ * }
+ * }
+ *
+ *
+ * let slider;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a slider and place it beneath the canvas.
+ * slider = createSlider(0, 255, 200);
+ * slider.position(0, 100);
+ *
+ * // Call repaint() when the slider changes.
+ * slider.input(repaint);
+ *
+ * describe('A gray square with a range slider underneath it. The background changes shades of gray when the slider is moved.');
+ * }
+ *
+ * // Paint the background using slider's value.
+ * function repaint() {
+ * let g = slider.value();
+ * background(g);
+ * }
+ *
+ *
+ * let input;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an input and place it beneath the canvas.
+ * input = createInput('');
+ * input.position(0, 100);
+ *
+ * // Call repaint() when input is detected.
+ * input.input(repaint);
+ *
+ * describe('A gray square with a text input bar beneath it. Any text written in the input appears in the middle of the square.');
+ * }
+ *
+ * // Paint the background gray and display the input's value.
+ * function repaint() {
+ * background(200);
+ * let msg = input.value();
+ * text(msg, 5, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a div element and set its position.
+ * let div = createDiv('p5*js');
+ * div.position(25, 35);
+ *
+ * describe('A gray square with the text "p5*js" written in its center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an h3 element within the div.
+ * let div = createDiv('p5*js
');
+ * div.position(20, 5);
+ *
+ * describe('A gray square with the text "p5*js" written in its center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a paragraph element and set its position.
+ * let p = createP('Tell me a story.');
+ * p.position(5, 0);
+ *
+ * describe('A gray square displaying the text "Tell me a story." written in black.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a span element and set its position.
+ * let span = createSpan('p5*js');
+ * span.position(25, 35);
+ *
+ * describe('A gray square with the text "p5*js" written in its center.');
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ *
+ * // Create a div element as a container.
+ * let div = createDiv();
+ *
+ * // Place the div at the center.
+ * div.position(25, 35);
+ *
+ * // Create a span element.
+ * let s1 = createSpan('p5');
+ *
+ * // Create a second span element.
+ * let s2 = createSpan('*');
+ *
+ * // Set the second span's font color.
+ * s2.style('color', 'deeppink');
+ *
+ * // Create a third span element.
+ * let s3 = createSpan('js');
+ *
+ * // Add all the spans to the container div.
+ * s1.parent(div);
+ * s2.parent(div);
+ * s3.parent(div);
+ *
+ * describe('A gray square with the text "p5*js" written in black at its center. The asterisk is pink.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * let img = createImg(
+ * 'https://p5js.org/assets/img/asterisk-01.png',
+ * 'The p5.js magenta asterisk.'
+ * );
+ * img.position(0, -10);
+ *
+ * describe('A gray square with a magenta asterisk in its center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an anchor element that links to p5js.org.
+ * let a = createA('http://p5js.org/', 'p5*js');
+ * a.position(25, 35);
+ *
+ * describe('The text "p5*js" written at the center of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ *
+ * // Create an anchor tag that links to p5js.org.
+ * // Open the link in a new tab.
+ * let a = createA('http://p5js.org/', 'p5*js', '_blank');
+ * a.position(25, 35);
+ *
+ * describe('The text "p5*js" written at the center of a gray square.');
+ * }
+ *
+ *
+ * let slider;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a slider and place it at the top of the canvas.
+ * slider = createSlider(0, 255);
+ * slider.position(10, 10);
+ * slider.size(80);
+ *
+ * describe('A dark gray square with a range slider at the top. The square changes color when the slider is moved.');
+ * }
+ *
+ * function draw() {
+ * // Use the slider as a grayscale value.
+ * let g = slider.value();
+ * background(g);
+ * }
+ *
+ *
+ * let slider;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a slider and place it at the top of the canvas.
+ * // Set its default value to 0.
+ * slider = createSlider(0, 255, 0);
+ * slider.position(10, 10);
+ * slider.size(80);
+ *
+ * describe('A black square with a range slider at the top. The square changes color when the slider is moved.');
+ * }
+ *
+ * function draw() {
+ * // Use the slider as a grayscale value.
+ * let g = slider.value();
+ * background(g);
+ * }
+ *
+ *
+ * let slider;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a slider and place it at the top of the canvas.
+ * // Set its default value to 0.
+ * // Set its step size to 50.
+ * slider = createSlider(0, 255, 0, 50);
+ * slider.position(10, 10);
+ * slider.size(80);
+ *
+ * describe('A black square with a range slider at the top. The square changes color when the slider is moved.');
+ * }
+ *
+ * function draw() {
+ * // Use the slider as a grayscale value.
+ * let g = slider.value();
+ * background(g);
+ * }
+ *
+ *
+ * let slider;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a slider and place it at the top of the canvas.
+ * // Set its default value to 0.
+ * // Set its step size to 0 so that it moves smoothly.
+ * slider = createSlider(0, 255, 0, 0);
+ * slider.position(10, 10);
+ * slider.size(80);
+ *
+ * describe('A black square with a range slider at the top. The square changes color when the slider is moved.');
+ * }
+ *
+ * function draw() {
+ * // Use the slider as a grayscale value.
+ * let g = slider.value();
+ * background(g);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a button and place it beneath the canvas.
+ * let button = createButton('click me');
+ * button.position(0, 100);
+ *
+ * // Call repaint() when the button is pressed.
+ * button.mousePressed(repaint);
+ *
+ * describe('A gray square with a button that says "click me" beneath it. The square changes color when the button is clicked.');
+ * }
+ *
+ * // Change the background color.
+ * function repaint() {
+ * let g = random(255);
+ * background(g);
+ * }
+ *
+ *
+ * let button;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a button and set its value to 0.
+ * // Place the button beneath the canvas.
+ * button = createButton('click me', 'red');
+ * button.position(0, 100);
+ *
+ * // Call randomColor() when the button is pressed.
+ * button.mousePressed(randomColor);
+ *
+ * describe('A red square with a button that says "click me" beneath it. The square changes color when the button is clicked.');
+ * }
+ *
+ * function draw() {
+ * // Use the button's value to set the background color.
+ * let c = button.value();
+ * background(c);
+ * }
+ *
+ * // Set the button's value to a random color.
+ * function randomColor() {
+ * let c = random(['red', 'green', 'blue', 'yellow']);
+ * button.value(c);
+ * }
+ *
+ *
+ * let checkbox;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a checkbox and place it beneath the canvas.
+ * checkbox = createCheckbox();
+ * checkbox.position(0, 100);
+ *
+ * describe('A black square with a checkbox beneath it. The square turns white when the box is checked.');
+ * }
+ *
+ * function draw() {
+ * // Use the checkbox to set the background color.
+ * if (checkbox.checked()) {
+ * background(255);
+ * } else {
+ * background(0);
+ * }
+ * }
+ *
+ *
+ * let checkbox;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a checkbox and place it beneath the canvas.
+ * // Label the checkbox "white".
+ * checkbox = createCheckbox(' white');
+ * checkbox.position(0, 100);
+ *
+ * describe('A black square with a checkbox labeled "white" beneath it. The square turns white when the box is checked.');
+ * }
+ *
+ * function draw() {
+ * // Use the checkbox to set the background color.
+ * if (checkbox.checked()) {
+ * background(255);
+ * } else {
+ * background(0);
+ * }
+ * }
+ *
+ *
+ * let checkbox;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a checkbox and place it beneath the canvas.
+ * // Label the checkbox "white" and set its value to true.
+ * checkbox = createCheckbox(' white', true);
+ * checkbox.position(0, 100);
+ *
+ * describe('A white square with a checkbox labeled "white" beneath it. The square turns black when the box is unchecked.');
+ * }
+ *
+ * function draw() {
+ * // Use the checkbox to set the background color.
+ * if (checkbox.checked()) {
+ * background(255);
+ * } else {
+ * background(0);
+ * }
+ * }
+ *
+ *
+ * let mySelect;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a dropdown and place it beneath the canvas.
+ * mySelect = createSelect();
+ * mySelect.position(0, 100);
+ *
+ * // Add color options.
+ * mySelect.option('red');
+ * mySelect.option('green');
+ * mySelect.option('blue');
+ * mySelect.option('yellow');
+ *
+ * // Set the selected option to "red".
+ * mySelect.selected('red');
+ *
+ * describe('A red square with a dropdown menu beneath it. The square changes color when a new color is selected.');
+ * }
+ *
+ * function draw() {
+ * // Use the selected value to paint the background.
+ * let c = mySelect.selected();
+ * background(c);
+ * }
+ *
+ *
+ * let mySelect;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a dropdown and place it beneath the canvas.
+ * mySelect = createSelect();
+ * mySelect.position(0, 100);
+ *
+ * // Add color options.
+ * mySelect.option('red');
+ * mySelect.option('green');
+ * mySelect.option('blue');
+ * mySelect.option('yellow');
+ *
+ * // Set the selected option to "red".
+ * mySelect.selected('red');
+ *
+ * // Disable the "yellow" option.
+ * mySelect.disable('yellow');
+ *
+ * describe('A red square with a dropdown menu beneath it. The square changes color when a new color is selected.');
+ * }
+ *
+ * function draw() {
+ * // Use the selected value to paint the background.
+ * let c = mySelect.selected();
+ * background(c);
+ * }
+ *
+ *
+ * let mySelect;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a dropdown and place it beneath the canvas.
+ * mySelect = createSelect();
+ * mySelect.position(0, 100);
+ *
+ * // Add color options with names and values.
+ * mySelect.option('one', 'red');
+ * mySelect.option('two', 'green');
+ * mySelect.option('three', 'blue');
+ * mySelect.option('four', 'yellow');
+ *
+ * // Set the selected option to "one".
+ * mySelect.selected('one');
+ *
+ * describe('A red square with a dropdown menu beneath it. The square changes color when a new color is selected.');
+ * }
+ *
+ * function draw() {
+ * // Use the selected value to paint the background.
+ * let c = mySelect.selected();
+ * background(c);
+ * }
+ *
+ *
+ * // Hold CTRL to select multiple options on Windows and Linux.
+ * // Hold CMD to select multiple options on macOS.
+ * let mySelect;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a dropdown and allow multiple selections.
+ * // Place it beneath the canvas.
+ * mySelect = createSelect(true);
+ * mySelect.position(0, 100);
+ *
+ * // Add color options.
+ * mySelect.option('red');
+ * mySelect.option('green');
+ * mySelect.option('blue');
+ * mySelect.option('yellow');
+ *
+ * describe('A gray square with a dropdown menu beneath it. Colorful circles appear when their color is selected.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Use the selected value(s) to draw circles.
+ * let colors = mySelect.selected();
+ * for (let i = 0; i < colors.length; i += 1) {
+ * // Calculate the x-coordinate.
+ * let x = 10 + i * 20;
+ *
+ * // Access the color.
+ * let c = colors[i];
+ *
+ * // Draw the circle.
+ * fill(c);
+ * circle(x, 50, 20);
+ * }
+ * }
+ *
+ *
+ * let myRadio;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a radio button element and place it
+ * // in the top-left corner.
+ * myRadio = createRadio();
+ * myRadio.position(0, 0);
+ * myRadio.size(60);
+ *
+ * // Add a few color options.
+ * myRadio.option('red');
+ * myRadio.option('yellow');
+ * myRadio.option('blue');
+ *
+ * // Choose a default option.
+ * myRadio.selected('yellow');
+ *
+ * describe('A yellow square with three color options listed, "red", "yellow", and "blue". The square changes color when the user selects a new option.');
+ * }
+ *
+ * function draw() {
+ * // Set the background color using the radio button.
+ * let g = myRadio.value();
+ * background(g);
+ * }
+ *
+ *
+ * let myRadio;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a radio button element and place it
+ * // in the top-left corner.
+ * myRadio = createRadio();
+ * myRadio.position(0, 0);
+ * myRadio.size(50);
+ *
+ * // Add a few color options.
+ * // Color values are labeled with
+ * // emotions they evoke.
+ * myRadio.option('red', 'love');
+ * myRadio.option('yellow', 'joy');
+ * myRadio.option('blue', 'trust');
+ *
+ * // Choose a default option.
+ * myRadio.selected('yellow');
+ *
+ * describe('A yellow square with three options listed, "love", "joy", and "trust". The square changes color when the user selects a new option.');
+ * }
+ *
+ * function draw() {
+ * // Set the background color using the radio button.
+ * let c = myRadio.value();
+ * background(c);
+ * }
+ *
+ *
+ * let myRadio;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a radio button element and place it
+ * // in the top-left corner.
+ * myRadio = createRadio();
+ * myRadio.position(0, 0);
+ * myRadio.size(50);
+ *
+ * // Add a few color options.
+ * myRadio.option('red');
+ * myRadio.option('yellow');
+ * myRadio.option('blue');
+ *
+ * // Choose a default option.
+ * myRadio.selected('yellow');
+ *
+ * // Create a button and place it beneath the canvas.
+ * let btn = createButton('disable');
+ * btn.position(0, 100);
+ *
+ * // Call disableRadio() when btn is pressed.
+ * btn.mousePressed(disableRadio);
+ *
+ * describe('A yellow square with three options listed, "red", "yellow", and "blue". The square changes color when the user selects a new option. A "disable" button beneath the canvas disables the color options when pressed.');
+ * }
+ *
+ * function draw() {
+ * // Set the background color using the radio button.
+ * let c = myRadio.value();
+ * background(c);
+ * }
+ *
+ * // Disable myRadio.
+ * function disableRadio() {
+ * myRadio.disable(true);
+ * }
+ *
+ *
+ * let myPicker;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a color picker and set its position.
+ * myPicker = createColorPicker('deeppink');
+ * myPicker.position(0, 100);
+ *
+ * describe('A pink square with a color picker beneath it. The square changes color when the user picks a new color.');
+ * }
+ *
+ * function draw() {
+ * // Use the color picker to paint the background.
+ * let c = myPicker.color();
+ * background(c);
+ * }
+ *
+ *
+ * let myPicker;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a color picker and set its position.
+ * myPicker = createColorPicker('deeppink');
+ * myPicker.position(0, 100);
+ *
+ * describe('A number with the format "#rrggbb" is displayed on a pink canvas. The background color and number change when the user picks a new color.');
+ * }
+ *
+ * function draw() {
+ * // Use the color picker to paint the background.
+ * let c = myPicker.value();
+ * background(c);
+ *
+ * // Display the current color as a hex string.
+ * text(c, 25, 55);
+ * }
+ *
+ *
+ * let myInput;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create an input element and place it
+ * // beneath the canvas.
+ * myInput = createInput();
+ * myInput.position(0, 100);
+ *
+ * describe('A gray square with a text box beneath it. The text in the square changes when the user types something new in the input bar.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Use the input to display a message.
+ * let msg = myInput.value();
+ * text(msg, 25, 55);
+ * }
+ *
+ *
+ * let myInput;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create an input element and place it
+ * // beneath the canvas. Set its default
+ * // text to "hello!".
+ * myInput = createInput('hello!');
+ * myInput.position(0, 100);
+ *
+ * describe('The text "hello!" written at the center of a gray square. A text box beneath the square also says "hello!". The text in the square changes when the user types something new in the input bar.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Use the input to display a message.
+ * let msg = myInput.value();
+ * text(msg, 25, 55);
+ * }
+ *
+ *
+ * // Use the file input to select an image to
+ * // load and display.
+ * let input;
+ * let img;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a file input and place it beneath
+ * // the canvas.
+ * input = createFileInput(handleImage);
+ * input.position(0, 100);
+ *
+ * describe('A gray square with a file input beneath it. If the user selects an image file to load, it is displayed on the square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the image if loaded.
+ * if (img) {
+ * image(img, 0, 0, width, height);
+ * }
+ * }
+ *
+ * // Create an image if the file is an image.
+ * function handleImage(file) {
+ * if (file.type === 'image') {
+ * img = createImg(file.data, '');
+ * img.hide();
+ * } else {
+ * img = null;
+ * }
+ * }
+ *
+ *
+ * // Use the file input to select multiple images
+ * // to load and display.
+ * let input;
+ * let images = [];
+ *
+ * function setup() {
+ * // Create a file input and place it beneath
+ * // the canvas. Allow it to load multiple files.
+ * input = createFileInput(handleImage, true);
+ * input.position(0, 100);
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the images if loaded. Each image
+ * // is drawn 20 pixels lower than the
+ * // previous image.
+ * for (let i = 0; i < images.length; i += 1) {
+ * // Calculate the y-coordinate.
+ * let y = i * 20;
+ *
+ * // Draw the image.
+ * image(img, 0, y, 100, 100);
+ * }
+ *
+ * describe('A gray square with a file input beneath it. If the user selects multiple image files to load, they are displayed on the square.');
+ * }
+ *
+ * // Create an image if the file is an image,
+ * // then add it to the images array.
+ * function handleImage(file) {
+ * if (file.type === 'image') {
+ * let img = createImg(file.data, '');
+ * img.hide();
+ * images.push(img);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * noCanvas();
+ *
+ * // Load a video and add it to the page.
+ * // Note: this may not work in some browsers.
+ * let video = createVideo('assets/small.mp4');
+ *
+ * // Show the default video controls.
+ * video.showControls();
+ *
+ * describe('A video of a toy robot with playback controls beneath it.');
+ * }
+ *
+ *
+ * function setup() {
+ * noCanvas();
+ *
+ * // Load a video and add it to the page.
+ * // Provide an array options for different file formats.
+ * let video = createVideo(
+ * ['assets/small.mp4', 'assets/small.ogv', 'assets/small.webm']
+ * );
+ *
+ * // Show the default video controls.
+ * video.showControls();
+ *
+ * describe('A video of a toy robot with playback controls beneath it.');
+ * }
+ *
+ *
+ * let video;
+ *
+ * function setup() {
+ * noCanvas();
+ *
+ * // Load a video and add it to the page.
+ * // Provide an array options for different file formats.
+ * // Call mute() once the video loads.
+ * video = createVideo(
+ * ['assets/small.mp4', 'assets/small.ogv', 'assets/small.webm'],
+ * muteVideo
+ * );
+ *
+ * // Show the default video controls.
+ * video.showControls();
+ *
+ * describe('A video of a toy robot with playback controls beneath it.');
+ * }
+ *
+ * // Mute the video once it loads.
+ * function muteVideo() {
+ * video.volume(0);
+ * }
+ *
+ *
+ * function setup() {
+ * noCanvas();
+ *
+ * // Load the audio.
+ * let beat = createAudio('assets/beat.mp3');
+ *
+ * // Show the default audio controls.
+ * beat.showControls();
+ *
+ * describe('An audio beat plays when the user double-clicks the square.');
+ * }
+ *
+ *
+ * function setup() {
+ * noCanvas();
+ *
+ * // Create the video capture.
+ * createCapture(VIDEO);
+ *
+ * describe('A video stream from the webcam.');
+ * }
+ *
+ *
+ * let capture;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create the video capture and hide the element.
+ * capture = createCapture(VIDEO);
+ * capture.hide();
+ *
+ * describe('A video stream from the webcam with inverted colors.');
+ * }
+ *
+ * function draw() {
+ * // Draw the video capture within the canvas.
+ * image(capture, 0, 0, width, width * capture.height / capture.width);
+ *
+ * // Invert the colors in the stream.
+ * filter(INVERT);
+ * }
+ *
+ *
+ * let capture;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create the video capture with mirrored output.
+ * capture = createCapture(VIDEO,{ flipped:true });
+ * capture.size(100,100);
+ *
+ * describe('A video stream from the webcam with flipped or mirrored output.');
+ * }
+ *
+ *
+ *
+ * function setup() {
+ * createCanvas(480, 120);
+ *
+ * // Create a constraints object.
+ * let constraints = {
+ * video: {
+ * mandatory: {
+ * minWidth: 1280,
+ * minHeight: 720
+ * },
+ * optional: [{ maxFrameRate: 10 }]
+ * },
+ * audio: false
+ * };
+ *
+ * // Create the video capture.
+ * createCapture(constraints);
+ *
+ * describe('A video stream from the webcam.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an h5 element with nothing in it.
+ * createElement('h5');
+ *
+ * describe('A gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an h5 element with the content "p5*js".
+ * let h5 = createElement('h5', 'p5*js');
+ *
+ * // Set the element's style and position.
+ * h5.style('color', 'deeppink');
+ * h5.position(30, 15);
+ *
+ * describe('The text "p5*js" written in pink in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a div element.
+ * let div = createDiv('div');
+ *
+ * // Add a class to the div.
+ * div.addClass('myClass');
+ *
+ * describe('A gray square.');
+ * }
+ *
+ *
+ * // In this example, a class is set when the div is created
+ * // and removed when mouse is pressed. This could link up
+ * // with a CSS style rule to toggle style properties.
+ *
+ * let div;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a div element.
+ * div = createDiv('div');
+ *
+ * // Add a class to the div.
+ * div.addClass('myClass');
+ *
+ * describe('A gray square.');
+ * }
+ *
+ * // Remove 'myClass' from the div when the user presses the mouse.
+ * function mousePressed() {
+ * div.removeClass('myClass');
+ * }
+ *
+ *
+ * let div;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a div element.
+ * div = createDiv('div');
+ *
+ * // Add the class 'show' to the div.
+ * div.addClass('show');
+ *
+ * describe('A gray square.');
+ * }
+ *
+ * // Toggle the class 'show' when the mouse is pressed.
+ * function mousePressed() {
+ * if (div.hasClass('show')) {
+ * div.addClass('show');
+ * } else {
+ * div.removeClass('show');
+ * }
+ * }
+ *
+ *
+ * let div;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a div element.
+ * div = createDiv('div');
+ *
+ * // Add the 'show' class to the div.
+ * div.addClass('show');
+ *
+ * describe('A gray square.');
+ * }
+ *
+ * // Toggle the 'show' class when the mouse is pressed.
+ * function mousePressed() {
+ * div.toggleClass('show');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create the div elements.
+ * let div0 = createDiv('Parent');
+ * let div1 = createDiv('Child');
+ *
+ * // Make div1 the child of div0
+ * // using the p5.Element.
+ * div0.child(div1);
+ *
+ * describe('A gray square with the words "Parent" and "Child" written beneath it.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create the div elements.
+ * let div0 = createDiv('Parent');
+ * let div1 = createDiv('Child');
+ *
+ * // Give div1 an ID.
+ * div1.id('apples');
+ *
+ * // Make div1 the child of div0
+ * // using its ID.
+ * div0.child('apples');
+ *
+ * describe('A gray square with the words "Parent" and "Child" written beneath it.');
+ * }
+ *
+ *
+ * // This example assumes there is a div already on the page
+ * // with id "myChildDiv".
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create the div elements.
+ * let div0 = createDiv('Parent');
+ *
+ * // Select the child element by its ID.
+ * let elt = document.getElementById('myChildDiv');
+ *
+ * // Make div1 the child of div0
+ * // using its HTMLElement object.
+ * div0.child(elt);
+ *
+ * describe('A gray square with the words "Parent" and "Child" written beneath it.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create the div element and style it.
+ * let div = createDiv('');
+ * div.size(10, 10);
+ * div.style('background-color', 'orange');
+ *
+ * // Center the div relative to the page's body.
+ * div.center();
+ *
+ * describe('A gray square and an orange rectangle. The rectangle is at the center of the page.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create the div element and set its size.
+ * let div = createDiv('');
+ * div.size(100, 100);
+ *
+ * // Set the inner HTML to "hi".
+ * div.html('hi');
+ *
+ * describe('A gray square with the word "hi" written beneath it.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create the div element and set its size.
+ * let div = createDiv('Hello ');
+ * div.size(100, 100);
+ *
+ * // Append "World" to the div's HTML.
+ * div.html('World', true);
+ *
+ * describe('A gray square with the text "Hello World" written beneath it.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create the div element.
+ * let div = createDiv('Hello');
+ *
+ * // Prints "Hello" to the console.
+ * print(div.html());
+ *
+ * describe('A gray square with the word "Hello!" written beneath it.');
+ * }
+ *
+ *
+ * function setup() {
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Positions the canvas 50px to the right and 100px
+ * // below the top-left corner of the window.
+ * cnv.position(50, 100);
+ *
+ * describe('A gray square that is 50 pixels to the right and 100 pixels down from the top-left corner of the web page.');
+ * }
+ *
+ *
+ * function setup() {
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Positions the canvas at the top-left corner
+ * // of the window with a 'fixed' position type.
+ * cnv.position(0, 0, 'fixed');
+ *
+ * describe('A gray square in the top-left corner of the web page.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a paragraph element and set its font color to "deeppink".
+ * let p = createP('p5*js');
+ * p.position(25, 20);
+ * p.style('color', 'deeppink');
+ *
+ * describe('The text p5*js written in pink on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object.
+ * let c = color('deeppink');
+ *
+ * // Create a paragraph element and set its font color using a p5.Color object.
+ * let p = createP('p5*js');
+ * p.position(25, 20);
+ * p.style('color', c);
+ *
+ * describe('The text p5*js written in pink on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a paragraph element and set its font color to "deeppink"
+ * // using property:value syntax.
+ * let p = createP('p5*js');
+ * p.position(25, 20);
+ * p.style('color:deeppink');
+ *
+ * describe('The text p5*js written in pink on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an empty paragraph element and set its font color to "deeppink".
+ * let p = createP();
+ * p.position(5, 5);
+ * p.style('color', 'deeppink');
+ *
+ * // Get the element's color as an RGB color string.
+ * let c = p.style('color');
+ *
+ * // Set the element's inner HTML using the RGB color string.
+ * p.html(c);
+ *
+ * describe('The text "rgb(255, 20, 147)" written in pink on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a container div element and place it at the top-left corner.
+ * let container = createDiv();
+ * container.position(0, 0);
+ *
+ * // Create a paragraph element and place it within the container.
+ * // Set its horizontal alignment to "left".
+ * let p1 = createP('hi');
+ * p1.parent(container);
+ * p1.attribute('align', 'left');
+ *
+ * // Create a paragraph element and place it within the container.
+ * // Set its horizontal alignment to "center".
+ * let p2 = createP('hi');
+ * p2.parent(container);
+ * p2.attribute('align', 'center');
+ *
+ * // Create a paragraph element and place it within the container.
+ * // Set its horizontal alignment to "right".
+ * let p3 = createP('hi');
+ * p3.parent(container);
+ * p3.attribute('align', 'right');
+ *
+ * describe('A gray square with the text "hi" written on three separate lines, each placed further to the right.');
+ * }
+ *
+ *
+ * let p;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a paragraph element and place it in the center of the canvas.
+ * // Set its "align" attribute to "center".
+ * p = createP('hi');
+ * p.position(0, 20);
+ * p.attribute('align', 'center');
+ *
+ * describe('The text "hi" written in black at the center of a gray square. The text moves to the left edge when double-clicked.');
+ * }
+ *
+ * // Remove the 'align' attribute when the user double-clicks the paragraph.
+ * function doubleClicked() {
+ * p.removeAttribute('align');
+ * }
+ *
+ *
+ * let input;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a text input and place it beneath the canvas.
+ * // Set its default value to "hello".
+ * input = createInput('hello');
+ * input.position(0, 100);
+ *
+ * describe('The text from an input box is displayed on a gray square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Use the input's value to display a message.
+ * let msg = input.value();
+ * text(msg, 0, 55);
+ * }
+ *
+ *
+ * let input;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a text input and place it beneath the canvas.
+ * // Set its default value to "hello".
+ * input = createInput('hello');
+ * input.position(0, 100);
+ *
+ * describe('The text from an input box is displayed on a gray square. The text resets to "hello" when the user double-clicks the square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Use the input's value to display a message.
+ * let msg = input.value();
+ * text(msg, 0, 55);
+ * }
+ *
+ * // Reset the input's value.
+ * function doubleClicked() {
+ * input.value('hello');
+ * }
+ *
+ *
+ * let p;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a paragraph element and hide it.
+ * p = createP('p5*js');
+ * p.position(10, 10);
+ * p.hide();
+ *
+ * describe('A gray square. The text "p5*js" appears when the user double-clicks the square.');
+ * }
+ *
+ * // Show the paragraph when the user double-clicks.
+ * function doubleClicked() {
+ * p.show();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a pink div element and place it at the top-left corner.
+ * let div = createDiv();
+ * div.position(10, 10);
+ * div.style('background-color', 'deeppink');
+ *
+ * // Set the div's width to 80 pixels and height to 20 pixels.
+ * div.size(80, 20);
+ *
+ * describe('A gray square with a pink rectangle near its top.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a pink div element and place it at the top-left corner.
+ * let div = createDiv();
+ * div.position(10, 10);
+ * div.style('background-color', 'deeppink');
+ *
+ * // Set the div's width to 80 pixels and height to 40 pixels.
+ * div.size(80, 40);
+ *
+ * // Get the div's size as an object.
+ * let s = div.size();
+ *
+ * // Display the div's dimensions.
+ * div.html(`${s.width} x ${s.height}`);
+ *
+ * describe('A gray square with a pink rectangle near its top. The text "80 x 40" is written within the rectangle.');
+ * }
+ *
+ *
+ * let img1;
+ * let img2;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Load an image of an astronaut on the moon
+ * // and place it at the top-left of the canvas.
+ * img1 = createImg(
+ * 'assets/moonwalk.jpg',
+ * 'An astronaut walking on the moon',
+ * ''
+ * );
+ * img1.position(0, 0);
+ *
+ * // Load an image of an astronaut on the moon
+ * // and place it at the top-left of the canvas.
+ * // Resize the image once it's loaded.
+ * img2 = createImg(
+ * 'assets/moonwalk.jpg',
+ * 'An astronaut walking on the moon',
+ * '',
+ * resizeImage
+ * );
+ * img2.position(0, 0);
+ *
+ * describe('A gray square two copies of a space image at the top-left. The copy in front is smaller.');
+ * }
+ *
+ * // Resize img2 and keep its aspect ratio.
+ * function resizeImage() {
+ * img2.size(50, AUTO);
+ * }
+ *
+ *
+ * let p;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a paragraph element.
+ * p = createP('p5*js');
+ * p.position(10, 10);
+ *
+ * describe('The text "p5*js" written at the center of a gray square. ');
+ * }
+ *
+ * // Remove the paragraph when the user double-clicks.
+ * function doubleClicked() {
+ * p.remove();
+ * }
+ *
+ *
+ * // Drop an image on the canvas to view
+ * // this example.
+ * let img;
+ *
+ * function setup() {
+ * let c = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call handleFile() when a file that's dropped on the canvas has loaded.
+ * c.drop(handleFile);
+ *
+ * describe('A gray square. When the user drops an image on the square, it is displayed.');
+ * }
+ *
+ * // Remove the existing image and display the new one.
+ * function handleFile(file) {
+ * // Remove the current image, if any.
+ * if (img) {
+ * img.remove();
+ * }
+ *
+ * // Create an element with the
+ * // dropped file.
+ * img = createImg(file.data, '');
+ * img.hide();
+ *
+ * // Draw the image.
+ * image(img, 0, 0, width, height);
+ * }
+ *
+ *
+ * // Drop an image on the canvas to view
+ * // this example.
+ * let img;
+ * let msg;
+ *
+ * function setup() {
+ * let c = createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Call functions when the user drops a file on the canvas
+ * // and when the file loads.
+ * c.drop(handleFile, handleDrop);
+ *
+ * describe('A gray square. When the user drops an image on the square, it is displayed. The id attribute of canvas element is also displayed.');
+ * }
+ *
+ * // Display the image when it loads.
+ * function handleFile(file) {
+ * // Remove the current image, if any.
+ * if (img) {
+ * img.remove();
+ * }
+ *
+ * // Create an img element with the dropped file.
+ * img = createImg(file.data, '');
+ * img.hide();
+ *
+ * // Draw the image.
+ * image(img, 0, 0, width, height);
+ * }
+ *
+ * // Display the file's name when it loads.
+ * function handleDrop(event) {
+ * // Remove current paragraph, if any.
+ * if (msg) {
+ * msg.remove();
+ * }
+ *
+ * // Use event to get the drop target's id.
+ * let id = event.target.id;
+ *
+ * // Write the canvas' id beneath it.
+ * msg = createP(id);
+ * msg.position(0, 100);
+ *
+ * // Set the font color randomly for each drop.
+ * let c = random(['red', 'green', 'blue']);
+ * msg.style('color', c);
+ * msg.style('font-size', '12px');
+ * }
+ *
+ *
+ * let stickyNote;
+ * let textInput;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a div element and style it.
+ * stickyNote = createDiv('Note');
+ * stickyNote.position(5, 5);
+ * stickyNote.size(80, 20);
+ * stickyNote.style('font-size', '16px');
+ * stickyNote.style('font-family', 'Comic Sans MS');
+ * stickyNote.style('background', 'orchid');
+ * stickyNote.style('padding', '5px');
+ *
+ * // Make the note draggable.
+ * stickyNote.draggable();
+ *
+ * // Create a panel div and style it.
+ * let panel = createDiv('');
+ * panel.position(5, 40);
+ * panel.size(80, 50);
+ * panel.style('background', 'orchid');
+ * panel.style('font-size', '16px');
+ * panel.style('padding', '5px');
+ * panel.style('text-align', 'center');
+ *
+ * // Make the panel draggable.
+ * panel.draggable();
+ *
+ * // Create a text input and style it.
+ * textInput = createInput('Note');
+ * textInput.size(70);
+ *
+ * // Add the input to the panel.
+ * textInput.parent(panel);
+ *
+ * // Call handleInput() when text is input.
+ * textInput.input(handleInput);
+ *
+ * describe(
+ * 'A gray square with two purple rectangles that move when dragged. The top rectangle displays the text that is typed into the bottom rectangle.'
+ * );
+ * }
+ *
+ * // Update stickyNote's HTML when text is input.
+ * function handleInput() {
+ * stickyNote.html(textInput.value());
+ * }
+ *
+ *
+ * let capture;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.MediaElement using createCapture().
+ * capture = createCapture(VIDEO);
+ * capture.hide();
+ *
+ * describe('A webcam feed with inverted colors.');
+ * }
+ *
+ * function draw() {
+ * // Display the video stream and invert the colors.
+ * image(capture, 0, 0, width, width * capture.height / capture.width);
+ * filter(INVERT);
+ * }
+ *
+ *
+ * let beat;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * beat = createAudio('assets/beat.mp3');
+ *
+ * describe('The text "https://p5js.org/reference/assets/beat.mp3" written in black on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * textWrap(CHAR);
+ * text(beat.src, 10, 10, 80, 80);
+ * }
+ *
+ *
+ * let beat;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display a message.
+ * text('Click to play', 50, 50);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * beat = createAudio('assets/beat.mp3');
+ *
+ * describe('The text "Click to play" written in black on a gray background. A beat plays when the user clicks the square.');
+ * }
+ *
+ * // Play the beat when the user presses the mouse.
+ * function mousePressed() {
+ * beat.play();
+ * }
+ *
+ *
+ * let beat;
+ * let isStopped = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * beat = createAudio('assets/beat.mp3');
+ *
+ * describe('The text "Click to start" written in black on a gray background. The beat starts or stops when the user presses the mouse.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display different instructions based on playback.
+ * if (isStopped === true) {
+ * text('Click to start', 50, 50);
+ * } else {
+ * text('Click to stop', 50, 50);
+ * }
+ * }
+ *
+ * // Adjust playback when the user presses the mouse.
+ * function mousePressed() {
+ * if (isStopped === true) {
+ * // If the beat is stopped, play it.
+ * beat.play();
+ * isStopped = false;
+ * } else {
+ * // If the beat is playing, stop it.
+ * beat.stop();
+ * isStopped = true;
+ * }
+ * }
+ *
+ *
+ * let beat;
+ * let isPaused = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * beat = createAudio('assets/beat.mp3');
+ *
+ * describe('The text "Click to play" written in black on a gray background. The beat plays or pauses when the user clicks the square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display different instructions based on playback.
+ * if (isPaused === true) {
+ * text('Click to play', 50, 50);
+ * } else {
+ * text('Click to pause', 50, 50);
+ * }
+ * }
+ *
+ * // Adjust playback when the user presses the mouse.
+ * function mousePressed() {
+ * if (isPaused === true) {
+ * // If the beat is paused,
+ * // play it.
+ * beat.play();
+ * isPaused = false;
+ * } else {
+ * // If the beat is playing,
+ * // pause it.
+ * beat.pause();
+ * isPaused = true;
+ * }
+ * }
+ *
+ *
+ * let beat;
+ * let isLooping = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * beat = createAudio('assets/beat.mp3');
+ *
+ * describe('The text "Click to loop" written in black on a gray background. A beat plays repeatedly in a loop when the user clicks. The beat stops when the user clicks again.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display different instructions based on playback.
+ * if (isLooping === true) {
+ * text('Click to stop', 50, 50);
+ * } else {
+ * text('Click to loop', 50, 50);
+ * }
+ * }
+ *
+ * // Adjust playback when the user presses the mouse.
+ * function mousePressed() {
+ * if (isLooping === true) {
+ * // If the beat is looping, stop it.
+ * beat.stop();
+ * isLooping = false;
+ * } else {
+ * // If the beat is stopped, loop it.
+ * beat.loop();
+ * isLooping = true;
+ * }
+ * }
+ *
+ *
+ * let beat;
+ * let isPlaying = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * beat = createAudio('assets/beat.mp3');
+ *
+ * describe('The text "Click to play" written in black on a gray background. A beat plays when the user clicks. The beat stops when the user clicks again.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display different instructions based on playback.
+ * if (isPlaying === true) {
+ * text('Click to stop', 50, 50);
+ * } else {
+ * text('Click to play', 50, 50);
+ * }
+ * }
+ *
+ * // Adjust playback when the user presses the mouse.
+ * function mousePressed() {
+ * if (isPlaying === true) {
+ * // If the beat is playing, stop it.
+ * beat.stop();
+ * isPlaying = false;
+ * } else {
+ * // If the beat is stopped, play it.
+ * beat.play();
+ * isPlaying = true;
+ * }
+ * }
+ *
+ *
+ * let video;
+ *
+ * function setup() {
+ * noCanvas();
+ *
+ * // Call handleVideo() once the video loads.
+ * video = createVideo('assets/fingers.mov', handleVideo);
+ *
+ * describe('A video of fingers walking on a treadmill.');
+ * }
+ *
+ * // Set the video's size and play it.
+ * function handleVideo() {
+ * video.size(100, 100);
+ * video.autoplay();
+ * }
+ *
+ *
+ * function setup() {
+ * noCanvas();
+ *
+ * // Load a video, but don't play it automatically.
+ * let video = createVideo('assets/fingers.mov', handleVideo);
+ *
+ * // Play the video when the user clicks on it.
+ * video.mousePressed(handlePress);
+ *
+ * describe('An image of fingers on a treadmill. They start walking when the user double-clicks on them.');
+ * }
+ *
+ *
+ * let dragon;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * dragon = createAudio('assets/lucky_dragons.mp3');
+ *
+ * // Show the default media controls.
+ * dragon.showControls();
+ *
+ * describe('The text "Volume: V" on a gray square with media controls beneath it. The number "V" oscillates between 0 and 1 as the music plays.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Produce a number between 0 and 1.
+ * let n = 0.5 * sin(frameCount * 0.01) + 0.5;
+ *
+ * // Use n to set the volume.
+ * dragon.volume(n);
+ *
+ * // Get the current volume and display it.
+ * let v = dragon.volume();
+ *
+ * // Round v to 1 decimal place for display.
+ * v = round(v, 1);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the volume.
+ * text(`Volume: ${v}`, 50, 50);
+ * }
+ *
+ *
+ * let dragon;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * dragon = createAudio('assets/lucky_dragons.mp3');
+ *
+ * // Show the default media controls.
+ * dragon.showControls();
+ *
+ * describe('The text "Speed: S" on a gray square with media controls beneath it. The number "S" oscillates between 0 and 1 as the music plays.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Produce a number between 0 and 2.
+ * let n = sin(frameCount * 0.01) + 1;
+ *
+ * // Use n to set the playback speed.
+ * dragon.speed(n);
+ *
+ * // Get the current speed and display it.
+ * let s = dragon.speed();
+ *
+ * // Round s to 1 decimal place for display.
+ * s = round(s, 1);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the speed.
+ * text(`Speed: ${s}`, 50, 50);
+ * }
+ *
+ */
+ /**
+ * @method speed
+ * @param {Number} speed speed multiplier for playback.
+ * @chainable
+ */
+
+ },
+ {
+ key: 'speed',
+ value: function speed(val) {
+ if (typeof val === 'undefined') {
+ return this.presetPlaybackRate || this.elt.playbackRate;
+ } else {
+ if (this.loadedmetadata) {
+ this.elt.playbackRate = val;
+ } else {
+ this.presetPlaybackRate = val;
+ }
+ }
+ } /**
+ * Sets the media element's playback time.
+ *
+ * The parameter, `time`, is optional. It's a number that specifies the
+ * time, in seconds, to jump to when playback begins.
+ *
+ * Calling `media.time()` without an argument returns the number of seconds
+ * the audio/video has played.
+ *
+ * Note: Time resets to 0 when looping media restarts.
+ *
+ * @method time
+ * @return {Number} current time (in seconds).
+ *
+ * @example
+ *
+ * let dragon;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * dragon = createAudio('assets/lucky_dragons.mp3');
+ *
+ * // Show the default media controls.
+ * dragon.showControls();
+ *
+ * describe('The text "S seconds" on a gray square with media controls beneath it. The number "S" increases as the song plays.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Get the current playback time.
+ * let s = dragon.time();
+ *
+ * // Round s to 1 decimal place for display.
+ * s = round(s, 1);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the playback time.
+ * text(`${s} seconds`, 50, 50);
+ * }
+ *
+ *
+ * let dragon;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * dragon = createAudio('assets/lucky_dragons.mp3');
+ *
+ * // Show the default media controls.
+ * dragon.showControls();
+ *
+ * // Jump to 2 seconds to start.
+ * dragon.time(2);
+ *
+ * describe('The text "S seconds" on a gray square with media controls beneath it. The number "S" increases as the song plays.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Get the current playback time.
+ * let s = dragon.time();
+ *
+ * // Round s to 1 decimal place for display.
+ * s = round(s, 1);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the playback time.
+ * text(`${s} seconds`, 50, 50);
+ * }
+ *
+ *
+ * let dragon;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * dragon = createAudio('assets/lucky_dragons.mp3');
+ *
+ * // Show the default media controls.
+ * dragon.showControls();
+ *
+ * describe('The text "S seconds left" on a gray square with media controls beneath it. The number "S" decreases as the song plays.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the time remaining.
+ * let s = dragon.duration() - dragon.time();
+ *
+ * // Round s to 1 decimal place for display.
+ * s = round(s, 1);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the time remaining.
+ * text(`${s} seconds left`, 50, 50);
+ * }
+ *
+ *
+ * let beat;
+ * let isPlaying = false;
+ * let isDone = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * beat = createAudio('assets/beat.mp3');
+ *
+ * // Call handleEnd() when the beat finishes.
+ * beat.onended(handleEnd);
+ *
+ * describe('The text "Click to play" written in black on a gray square. A beat plays when the user clicks. The text "Done!" appears when the beat finishes playing.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display different messages based on playback.
+ * if (isDone === true) {
+ * text('Done!', 50, 50);
+ * } else if (isPlaying === false) {
+ * text('Click to play', 50, 50);
+ * } else {
+ * text('Playing...', 50, 50);
+ * }
+ * }
+ *
+ * // Play the beat when the user presses the mouse.
+ * function mousePressed() {
+ * if (isPlaying === false) {
+ * isPlaying = true;
+ * beat.play();
+ * }
+ * }
+ *
+ * // Set isDone when playback ends.
+ * function handleEnd() {
+ * isDone = false;
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background('cornflowerblue');
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(50);
+ *
+ * // Display a dragon.
+ * text('🐉', 50, 50);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * let dragon = createAudio('assets/lucky_dragons.mp3');
+ *
+ * // Show the default media controls.
+ * dragon.showControls();
+ *
+ * describe('A dragon emoji, 🐉, drawn in the center of a blue square. A song plays in the background. Audio controls are displayed beneath the canvas.');
+ * }
+ *
+ *
+ * let dragon;
+ * let isHidden = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * dragon = createAudio('assets/lucky_dragons.mp3');
+ *
+ * // Show the default media controls.
+ * dragon.showControls();
+ *
+ * describe('The text "Double-click to hide controls" written in the middle of a gray square. A song plays in the background. Audio controls are displayed beneath the canvas. The controls appear/disappear when the user double-clicks the square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ *
+ * // Display a different message when controls are hidden or shown.
+ * if (isHidden === true) {
+ * text('Double-click to show controls', 10, 20, 80, 80);
+ * } else {
+ * text('Double-click to hide controls', 10, 20, 80, 80);
+ * }
+ * }
+ *
+ * // Show/hide controls based on a double-click.
+ * function doubleClicked() {
+ * if (isHidden === true) {
+ * dragon.showControls();
+ * isHidden = false;
+ * } else {
+ * dragon.hideControls();
+ * isHidden = true;
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * let beat = createAudio('assets/beat.mp3');
+ *
+ * // Play the beat in a loop.
+ * beat.loop();
+ *
+ * // Schedule a few events.
+ * beat.addCue(0, changeBackground, 'red');
+ * beat.addCue(2, changeBackground, 'deeppink');
+ * beat.addCue(4, changeBackground, 'orchid');
+ * beat.addCue(6, changeBackground, 'lavender');
+ *
+ * describe('A red square with a beat playing in the background. Its color changes every 2 seconds while the audio plays.');
+ * }
+ *
+ * // Change the background color.
+ * function changeBackground(c) {
+ * background(c);
+ * }
+ *
+ *
+ * let lavenderID;
+ * let isRemoved = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * let beat = createAudio('assets/beat.mp3');
+ *
+ * // Play the beat in a loop.
+ * beat.loop();
+ *
+ * // Schedule a few events.
+ * beat.addCue(0, changeBackground, 'red');
+ * beat.addCue(2, changeBackground, 'deeppink');
+ * beat.addCue(4, changeBackground, 'orchid');
+ *
+ * // Record the ID of the "lavender" callback.
+ * lavenderID = beat.addCue(6, changeBackground, 'lavender');
+ *
+ * describe('The text "Double-click to remove lavender." written on a red square. The color changes every 2 seconds while the audio plays. The lavender option is removed when the user double-clicks the square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Display different instructions based on the available callbacks.
+ * if (isRemoved === false) {
+ * text('Double-click to remove lavender.', 10, 10, 80, 80);
+ * } else {
+ * text('No more lavender.', 10, 10, 80, 80);
+ * }
+ * }
+ *
+ * // Change the background color.
+ * function changeBackground(c) {
+ * background(c);
+ * }
+ *
+ * // Remove the lavender color-change cue when the user double-clicks.
+ * function doubleClicked() {
+ * if (isRemoved === false) {
+ * beat.removeCue(lavenderID);
+ * isRemoved = true;
+ * }
+ * }
+ *
+ *
+ * let isChanging = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.MediaElement using createAudio().
+ * let beat = createAudio('assets/beat.mp3');
+ *
+ * // Play the beat in a loop.
+ * beat.loop();
+ *
+ * // Schedule a few events.
+ * beat.addCue(0, changeBackground, 'red');
+ * beat.addCue(2, changeBackground, 'deeppink');
+ * beat.addCue(4, changeBackground, 'orchid');
+ * beat.addCue(6, changeBackground, 'lavender');
+ *
+ * describe('The text "Double-click to stop changing." written on a square. The color changes every 2 seconds while the audio plays. The color stops changing when the user double-clicks the square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Display different instructions based on the available callbacks.
+ * if (isChanging === true) {
+ * text('Double-click to stop changing.', 10, 10, 80, 80);
+ * } else {
+ * text('No more changes.', 10, 10, 80, 80);
+ * }
+ * }
+ *
+ * // Change the background color.
+ * function changeBackground(c) {
+ * background(c);
+ * }
+ *
+ * // Remove cued functions and stop changing colors when the user
+ * // double-clicks.
+ * function doubleClicked() {
+ * if (isChanging === true) {
+ * beat.clearCues();
+ * isChanging = false;
+ * }
+ * }
+ *
+ *
+ * // Use the file input to load a
+ * // file and display its info.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a file input and place it beneath the canvas.
+ * // Call displayInfo() when the file loads.
+ * let input = createFileInput(displayInfo);
+ * input.position(0, 100);
+ *
+ * describe('A gray square with a file input beneath it. If the user loads a file, its info is written in black.');
+ * }
+ *
+ * // Display the p5.File's info once it loads.
+ * function displayInfo(file) {
+ * background(200);
+ *
+ * // Display the p5.File's name.
+ * text(file.name, 10, 10, 80, 40);
+ *
+ * // Display the p5.File's type and subtype.
+ * text(`${file.type}/${file.subtype}`, 10, 70);
+ *
+ * // Display the p5.File's size in bytes.
+ * text(file.size, 10, 90);
+ * }
+ *
+ *
+ * // Use the file input to select an image to
+ * // load and display.
+ * let img;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a file input and place it beneath the canvas.
+ * // Call handleImage() when the file image loads.
+ * let input = createFileInput(handleImage);
+ * input.position(0, 100);
+ *
+ * describe('A gray square with a file input beneath it. If the user selects an image file to load, it is displayed on the square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the image if it's ready.
+ * if (img) {
+ * image(img, 0, 0, width, height);
+ * }
+ * }
+ *
+ * // Use the p5.File's data once it loads.
+ * function handleImage(file) {
+ * // Check the p5.File's type.
+ * if (file.type === 'image') {
+ * // Create an image using using the p5.File's data.
+ * img = createImg(file.data, '');
+ *
+ * // Hide the image element so it doesn't appear twice.
+ * img.hide();
+ * } else {
+ * img = null;
+ * }
+ * }
+ *
+ *
+ * // Use the file input to load a
+ * // file and display its info.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a file input and place it beneath the canvas.
+ * // Call displayInfo() when the file loads.
+ * let input = createFileInput(displayInfo);
+ * input.position(0, 100);
+ *
+ * describe('A gray square with a file input beneath it. If the user loads a file, its info is written in black.');
+ * }
+ *
+ * // Use the p5.File once it loads.
+ * function displayInfo(file) {
+ * background(200);
+ *
+ * // Display the p5.File's name.
+ * text(file.name, 10, 10, 80, 40);
+ *
+ * // Display the p5.File's type and subtype.
+ * text(`${file.type}/${file.subtype}`, 10, 70);
+ *
+ * // Display the p5.File's size in bytes.
+ * text(file.size, 10, 90);
+ * }
+ *
+ *
+ * // Use the file input to load a file and display its info.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a file input and place it beneath the canvas.
+ * // Call displayType() when the file loads.
+ * let input = createFileInput(displayType);
+ * input.position(0, 100);
+ *
+ * describe('A gray square with a file input beneath it. If the user loads a file, its type is written in black.');
+ * }
+ *
+ * // Display the p5.File's type once it loads.
+ * function displayType(file) {
+ * background(200);
+ *
+ * // Display the p5.File's type.
+ * text(`This is file's type is: ${file.type}`, 10, 10, 80, 80);
+ * }
+ *
+ *
+ * // Use the file input to load a
+ * // file and display its info.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a file input and place it beneath the canvas.
+ * // Call displaySubtype() when the file loads.
+ * let input = createFileInput(displaySubtype);
+ * input.position(0, 100);
+ *
+ * describe('A gray square with a file input beneath it. If the user loads a file, its subtype is written in black.');
+ * }
+ *
+ * // Display the p5.File's type once it loads.
+ * function displaySubtype(file) {
+ * background(200);
+ *
+ * // Display the p5.File's subtype.
+ * text(`This is file's subtype is: ${file.subtype}`, 10, 10, 80, 80);
+ * }
+ *
+ *
+ * // Use the file input to load a
+ * // file and display its info.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a file input and place it beneath the canvas.
+ * // Call displayName() when the file loads.
+ * let input = createFileInput(displayName);
+ * input.position(0, 100);
+ *
+ * describe('A gray square with a file input beneath it. If the user loads a file, its name is written in black.');
+ * }
+ *
+ * // Display the p5.File's name once it loads.
+ * function displayName(file) {
+ * background(200);
+ *
+ * // Display the p5.File's name.
+ * text(`This is file's name is: ${file.name}`, 10, 10, 80, 80);
+ * }
+ *
+ *
+ * // Use the file input to load a file and display its info.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a file input and place it beneath the canvas.
+ * // Call displaySize() when the file loads.
+ * let input = createFileInput(displaySize);
+ * input.position(0, 100);
+ *
+ * describe('A gray square with a file input beneath it. If the user loads a file, its size in bytes is written in black.');
+ * }
+ *
+ * // Display the p5.File's size in bytes once it loads.
+ * function displaySize(file) {
+ * background(200);
+ *
+ * // Display the p5.File's size.
+ * text(`This is file has ${file.size} bytes.`, 10, 10, 80, 80);
+ * }
+ *
+ *
+ * // Use the file input to load a file and display its info.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a file input and place it beneath the canvas.
+ * // Call displayData() when the file loads.
+ * let input = createFileInput(displayData);
+ * input.position(0, 100);
+ *
+ * describe('A gray square with a file input beneath it. If the user loads a file, its data is written in black.');
+ * }
+ *
+ * // Display the p5.File's data once it loads.
+ * function displayData(file) {
+ * background(200);
+ *
+ * // Display the p5.File's data, which looks like a random string of characters.
+ * text(file.data, 10, 10, 80, 80);
+ * }
+ *
+ *
+ * // Move a touchscreen device to register
+ * // acceleration changes.
+ * function draw() {
+ * background(220, 50);
+ * fill('magenta');
+ * ellipse(width / 2, height / 2, accelerationX);
+ * describe('Magnitude of device acceleration is displayed as ellipse size.');
+ * }
+ *
+ *
+ * // Move a touchscreen device to register
+ * // acceleration changes.
+ * function draw() {
+ * background(220, 50);
+ * fill('magenta');
+ * ellipse(width / 2, height / 2, accelerationY);
+ * describe('Magnitude of device acceleration is displayed as ellipse size');
+ * }
+ *
+ *
+ * // Move a touchscreen device to register
+ * // acceleration changes.
+ * function draw() {
+ * background(220, 50);
+ * fill('magenta');
+ * ellipse(width / 2, height / 2, accelerationZ);
+ * describe('Magnitude of device acceleration is displayed as ellipse size');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * //rotateZ(radians(rotationZ));
+ * rotateX(radians(rotationX));
+ * //rotateY(radians(rotationY));
+ * box(200, 200, 200);
+ * describe(`red horizontal line right, green vertical line bottom.
+ * black background.`);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * //rotateZ(radians(rotationZ));
+ * //rotateX(radians(rotationX));
+ * rotateY(radians(rotationY));
+ * box(200, 200, 200);
+ * describe(`red horizontal line right, green vertical line bottom.
+ * black background.`);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * rotateZ(radians(rotationZ));
+ * //rotateX(radians(rotationX));
+ * //rotateY(radians(rotationY));
+ * box(200, 200, 200);
+ * describe(`red horizontal line right, green vertical line bottom.
+ * black background.`);
+ * }
+ *
+ *
+ * // A simple if statement looking at whether
+ * // rotationX - pRotationX < 0 is true or not will be
+ * // sufficient for determining the rotate direction
+ * // in most cases.
+ *
+ * // Some extra logic is needed to account for cases where
+ * // the angles wrap around.
+ * let rotateDirection = 'clockwise';
+ *
+ * // Simple range conversion to make things simpler.
+ * // This is not absolutely necessary but the logic
+ * // will be different in that case.
+ *
+ * let rX = rotationX + 180;
+ * let pRX = pRotationX + 180;
+ *
+ * if ((rX - pRX > 0 && rX - pRX < 270) || rX - pRX < -270) {
+ * rotateDirection = 'clockwise';
+ * } else if (rX - pRX < 0 || rX - pRX > 270) {
+ * rotateDirection = 'counter-clockwise';
+ * }
+ *
+ * print(rotateDirection);
+ * describe('no image to display.');
+ *
+ *
+ * // A simple if statement looking at whether
+ * // rotationY - pRotationY < 0 is true or not will be
+ * // sufficient for determining the rotate direction
+ * // in most cases.
+ *
+ * // Some extra logic is needed to account for cases where
+ * // the angles wrap around.
+ * let rotateDirection = 'clockwise';
+ *
+ * // Simple range conversion to make things simpler.
+ * // This is not absolutely necessary but the logic
+ * // will be different in that case.
+ *
+ * let rY = rotationY + 180;
+ * let pRY = pRotationY + 180;
+ *
+ * if ((rY - pRY > 0 && rY - pRY < 270) || rY - pRY < -270) {
+ * rotateDirection = 'clockwise';
+ * } else if (rY - pRY < 0 || rY - pRY > 270) {
+ * rotateDirection = 'counter-clockwise';
+ * }
+ * print(rotateDirection);
+ * describe('no image to display.');
+ *
+ *
+ * // A simple if statement looking at whether
+ * // rotationZ - pRotationZ < 0 is true or not will be
+ * // sufficient for determining the rotate direction
+ * // in most cases.
+ *
+ * // Some extra logic is needed to account for cases where
+ * // the angles wrap around.
+ * let rotateDirection = 'clockwise';
+ *
+ * if (
+ * (rotationZ - pRotationZ > 0 && rotationZ - pRotationZ < 270) ||
+ * rotationZ - pRotationZ < -270
+ * ) {
+ * rotateDirection = 'clockwise';
+ * } else if (rotationZ - pRotationZ < 0 || rotationZ - pRotationZ > 270) {
+ * rotateDirection = 'counter-clockwise';
+ * }
+ * print(rotateDirection);
+ * describe('no image to display.');
+ *
+ *
+ * // Run this example on a mobile device
+ * // Rotate the device by 90 degrees in the
+ * // X-axis to change the value.
+ *
+ * let value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * describe(`50-by-50 black rect in center of canvas.
+ * turns white on mobile when device turns`);
+ * describe(`50-by-50 black rect in center of canvas.
+ * turns white on mobile when x-axis turns`);
+ * }
+ * function deviceTurned() {
+ * if (turnAxis === 'X') {
+ * if (value === 0) {
+ * value = 255;
+ * } else if (value === 255) {
+ * value = 0;
+ * }
+ * }
+ * }
+ *
+ *
+ * // Run this example on a mobile device
+ * // You will need to move the device incrementally further
+ * // the closer the square's color gets to white in order to change the value.
+ *
+ * let value = 0;
+ * let threshold = 0.5;
+ * function setup() {
+ * setMoveThreshold(threshold);
+ * }
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * describe(`50-by-50 black rect in center of canvas.
+ * turns white on mobile when device moves`);
+ * }
+ * function deviceMoved() {
+ * value = value + 5;
+ * threshold = threshold + 0.1;
+ * if (value > 255) {
+ * value = 0;
+ * threshold = 30;
+ * }
+ * setMoveThreshold(threshold);
+ * }
+ *
+ *
+ * // Run this example on a mobile device
+ * // You will need to shake the device more firmly
+ * // the closer the box's fill gets to white in order to change the value.
+ *
+ * let value = 0;
+ * let threshold = 30;
+ * function setup() {
+ * setShakeThreshold(threshold);
+ * }
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * describe(`50-by-50 black rect in center of canvas.
+ * turns white on mobile when device is being shaked`);
+ * }
+ * function deviceMoved() {
+ * value = value + 5;
+ * threshold = threshold + 5;
+ * if (value > 255) {
+ * value = 0;
+ * threshold = 30;
+ * }
+ * setShakeThreshold(threshold);
+ * }
+ *
+ *
+ * // Run this example on a mobile device
+ * // Move the device around
+ * // to change the value.
+ *
+ * let value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * describe(`50-by-50 black rect in center of canvas.
+ * turns white on mobile when device moves`);
+ * }
+ * function deviceMoved() {
+ * value = value + 5;
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ * // Run this example on a mobile device
+ * // Rotate the device by 90 degrees
+ * // to change the value.
+ *
+ * let value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * describe(`50-by-50 black rect in center of canvas.
+ * turns white on mobile when device turns`);
+ * }
+ * function deviceTurned() {
+ * if (value === 0) {
+ * value = 255;
+ * } else if (value === 255) {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ * // Run this example on a mobile device
+ * // Rotate the device by 90 degrees in the
+ * // X-axis to change the value.
+ *
+ * let value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * describe(`50-by-50 black rect in center of canvas.
+ * turns white on mobile when x-axis turns`);
+ * }
+ * function deviceTurned() {
+ * if (turnAxis === 'X') {
+ * if (value === 0) {
+ * value = 255;
+ * } else if (value === 255) {
+ * value = 0;
+ * }
+ * }
+ * }
+ *
+ *
+ * // Run this example on a mobile device
+ * // Shake the device to change the value.
+ *
+ * let value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * describe(`50-by-50 black rect in center of canvas.
+ * turns white on mobile when device shakes`);
+ * }
+ * function deviceShaken() {
+ * value = value + 5;
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a white square at its center. The white square turns black when the user presses a key.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * if (keyIsPressed === true) {
+ * fill(0);
+ * } else {
+ * fill(255);
+ * }
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a white square at its center. The white square turns black when the user presses a key.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * if (keyIsPressed) {
+ * fill(0);
+ * } else {
+ * fill(255);
+ * }
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with the word "false" at its center. The word switches to "true" when the user presses a key.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the value of keyIsPressed.
+ * text(keyIsPressed, 50, 50);
+ * }
+ *
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square. The last key pressed is displayed at the center.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the last key pressed.
+ * text(key, 50, 50);
+ * }
+ *
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * let x = 50;
+ * let y = 50;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe(
+ * 'A gray square with a black circle at its center. The circle moves when the user presses the keys "w", "a", "s", or "d". It leaves a trail as it moves.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Update x and y if a key is pressed.
+ * if (keyIsPressed === true) {
+ * if (key === 'w') {
+ * y -= 1;
+ * } else if (key === 's') {
+ * y += 1;
+ * } else if (key === 'a') {
+ * x -= 1;
+ * } else if (key === 'd') {
+ * x += 1;
+ * }
+ * }
+ *
+ * // Style the circle.
+ * fill(0);
+ *
+ * // Draw the circle at (x, y).
+ * circle(x, y, 5);
+ * }
+ *
+ *
+ * if (keyCode === 13) {
+ * // Code to run if the enter key was pressed.
+ * }
+ *
+ *
+ * The same code can be written more clearly using the system variable `ENTER`
+ * which has a value of 13:
+ *
+ *
+ * if (keyCode === ENTER) {
+ * // Code to run if the enter key was pressed.
+ * }
+ *
+ *
+ * The system variables `BACKSPACE`, `DELETE`, `ENTER`, `RETURN`, `TAB`,
+ * `ESCAPE`, `SHIFT`, `CONTROL`, `OPTION`, `ALT`, `UP_ARROW`, `DOWN_ARROW`,
+ * `LEFT_ARROW`, and `RIGHT_ARROW` are all helpful shorthands the key codes of
+ * special keys. Key codes can be found on websites such as
+ * keycode.info.
+ *
+ * @property {Integer} keyCode
+ * @readOnly
+ *
+ * @example
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square. The last key pressed and its code are displayed at the center.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the last key pressed and its code.
+ * text(`${key} : ${keyCode}`, 50, 50);
+ * }
+ *
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * let x = 50;
+ * let y = 50;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe(
+ * 'A gray square with a black circle at its center. The circle moves when the user presses an arrow key. It leaves a trail as it moves.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Update x and y if an arrow key is pressed.
+ * if (keyIsPressed === true) {
+ * if (keyCode === UP_ARROW) {
+ * y -= 1;
+ * } else if (keyCode === DOWN_ARROW) {
+ * y += 1;
+ * } else if (keyCode === LEFT_ARROW) {
+ * x -= 1;
+ * } else if (keyCode === RIGHT_ARROW) {
+ * x += 1;
+ * }
+ * }
+ *
+ * // Style the circle.
+ * fill(0);
+ *
+ * // Draw the circle at (x, y).
+ * circle(x, y, 5);
+ * }
+ *
+ *
+ * function keyPressed() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The key and keyCode
+ * variables will be updated with the most recently typed value when
+ * `keyPressed()` is called by p5.js:
+ *
+ *
+ * function keyPressed() {
+ * if (key === 'c') {
+ * // Code to run.
+ * }
+ *
+ * if (keyCode === ENTER) {
+ * // Code to run.
+ * }
+ * }
+ *
+ *
+ * The parameter, `event`, is optional. `keyPressed()` is always passed a
+ * KeyboardEvent
+ * object with properties that describe the key press event:
+ *
+ *
+ * function keyPressed(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * Browsers may have default behaviors attached to various key events. For
+ * example, some browsers may jump to the bottom of a web page when the
+ * `SPACE` key is pressed. To prevent any default behavior for this event, add
+ * `return false;` to the end of the function.
+ *
+ * @method keyPressed
+ * @param {KeyboardEvent} [event] optional `KeyboardEvent` callback argument.
+ *
+ * @example
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square changes color when the user presses a key.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * // Toggle the background color when the user presses a key.
+ * function keyPressed() {
+ * if (value === 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a white square at its center. The inner square turns black when the user presses the "b" key. It turns white when the user presses the "a" key.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * // Reassign value when the user presses the 'a' or 'b' key.
+ * function keyPressed() {
+ * if (key === 'a') {
+ * value = 255;
+ * } else if (key === 'b') {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square turns white when the user presses the left arrow key. It turns black when the user presses the right arrow key.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * // Toggle the background color when the user presses an arrow key.
+ * function keyPressed() {
+ * if (keyCode === LEFT_ARROW) {
+ * value = 255;
+ * } else if (keyCode === RIGHT_ARROW) {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * function keyReleased() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The key and keyCode
+ * variables will be updated with the most recently released value when
+ * `keyReleased()` is called by p5.js:
+ *
+ *
+ * function keyReleased() {
+ * if (key === 'c') {
+ * // Code to run.
+ * }
+ *
+ * if (keyCode === ENTER) {
+ * // Code to run.
+ * }
+ * }
+ *
+ *
+ * The parameter, `event`, is optional. `keyReleased()` is always passed a
+ * KeyboardEvent
+ * object with properties that describe the key press event:
+ *
+ *
+ * function keyReleased(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * Browsers may have default behaviors attached to various key events. To
+ * prevent any default behavior for this event, add `return false;` to the end
+ * of the function.
+ *
+ * @method keyReleased
+ * @param {KeyboardEvent} [event] optional `KeyboardEvent` callback argument.
+ *
+ * @example
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square changes color when the user releases a key.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * // Toggle value when the user releases a key.
+ * function keyReleased() {
+ * if (value === 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square becomes white when the user releases the "w" key.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * // Set value to 255 the user releases the 'w' key.
+ * function keyReleased() {
+ * if (key === 'w') {
+ * value = 255;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square turns white when the user presses and releases the left arrow key. It turns black when the user presses and releases the right arrow key.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * // Toggle the background color when the user releases an arrow key.
+ * function keyReleased() {
+ * if (keyCode === LEFT_ARROW) {
+ * value = 255;
+ * } else if (keyCode === RIGHT_ARROW) {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * function keyTyped() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The key and keyCode
+ * variables will be updated with the most recently released value when
+ * `keyTyped()` is called by p5.js:
+ *
+ *
+ * function keyTyped() {
+ * // Check for the "c" character using key.
+ * if (key === 'c') {
+ * // Code to run.
+ * }
+ *
+ * // Check for "c" using keyCode.
+ * if (keyCode === 67) {
+ * // Code to run.
+ * }
+ * }
+ *
+ *
+ * The parameter, `event`, is optional. `keyTyped()` is always passed a
+ * KeyboardEvent
+ * object with properties that describe the key press event:
+ *
+ *
+ * function keyReleased(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * Note: Use the keyPressed() function and
+ * keyCode system variable to respond to modifier
+ * keys such as `ALT`.
+ *
+ * Browsers may have default behaviors attached to various key events. To
+ * prevent any default behavior for this event, add `return false;` to the end
+ * of the function.
+ *
+ * @method keyTyped
+ * @param {KeyboardEvent} [event] optional `KeyboardEvent` callback argument.
+ *
+ * @example
+ *
+ * // Click on the canvas to begin detecting key presses.
+ * // Note: Pressing special keys such as SPACE have no effect.
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a white square at its center. The inner square changes color when the user presses a key.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * // Toggle the square's color when the user types a printable key.
+ * function keyTyped() {
+ * if (value === 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a white square at its center. The inner square turns black when the user types the "b" key. It turns white when the user types the "a" key.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * // Reassign value when the user types the 'a' or 'b' key.
+ * function keyTyped() {
+ * if (key === 'a') {
+ * value = 255;
+ * } else if (key === 'b') {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * if (keyIsDown(LEFT_ARROW) && keyIsDown(UP_ARROW)) {
+ * // Move diagonally.
+ * }
+ *
+ *
+ * `keyIsDown()` can check for key presses using
+ * keyCode values, as in `keyIsDown(37)` or
+ * `keyIsDown(LEFT_ARROW)`. Key codes can be found on websites such as
+ * keycode.info.
+ *
+ * @method keyIsDown
+ * @param {Number} code key to check.
+ * @return {Boolean} whether the key is down or not.
+ *
+ * @example
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * let x = 50;
+ * let y = 50;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe(
+ * 'A gray square with a black circle at its center. The circle moves when the user presses an arrow key. It leaves a trail as it moves.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Update x and y if an arrow key is pressed.
+ * if (keyIsDown(LEFT_ARROW) === true) {
+ * x -= 1;
+ * }
+ *
+ * if (keyIsDown(RIGHT_ARROW) === true) {
+ * x += 1;
+ * }
+ *
+ * if (keyIsDown(UP_ARROW) === true) {
+ * y -= 1;
+ * }
+ *
+ * if (keyIsDown(DOWN_ARROW) === true) {
+ * y += 1;
+ * }
+ *
+ * // Style the circle.
+ * fill(0);
+ *
+ * // Draw the circle.
+ * circle(x, y, 5);
+ * }
+ *
+ *
+ * // Click on the canvas to begin detecting key presses.
+ *
+ * let x = 50;
+ * let y = 50;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe(
+ * 'A gray square with a black circle at its center. The circle moves when the user presses an arrow key. It leaves a trail as it moves.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Update x and y if an arrow key is pressed.
+ * if (keyIsDown(37) === true) {
+ * x -= 1;
+ * }
+ *
+ * if (keyIsDown(39) === true) {
+ * x += 1;
+ * }
+ *
+ * if (keyIsDown(38) === true) {
+ * y -= 1;
+ * }
+ *
+ * if (keyIsDown(40) === true) {
+ * y += 1;
+ * }
+ *
+ * // Style the circle.
+ * fill(0);
+ *
+ * // Draw the circle.
+ * circle(x, y, 5);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square. The text ">>" appears when the user moves the mouse to the right. The text "<<" appears when the user moves the mouse to the left.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display >> when movedX is positive and
+ * // << when it's negative.
+ * if (movedX > 0) {
+ * text('>>', 50, 50);
+ * } else if (movedX < 0) {
+ * text('<<', 50, 50);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square. The text "▲" appears when the user moves the mouse upward. The text "▼" appears when the user moves the mouse downward.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display ▼ when movedY is positive and
+ * // ▲ when it's negative.
+ * if (movedY > 0) {
+ * text('▼', 50, 50);
+ * } else if (movedY < 0) {
+ * text('▲', 50, 50);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe("A vertical black line moves left and right following the mouse's x-position.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a vertical line that follows the mouse's x-coordinate.
+ * line(mouseX, 0, mouseX, 100);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe("A gray square. The mouse's x- and y-coordinates are displayed as the user moves the mouse.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the mouse's coordinates.
+ * text(`x: ${mouseX} y: ${mouseY}`, 50, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe("A vertical black line moves left and right following the mouse's x-position.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Adjust coordinates for WebGL mode.
+ * // The origin (0, 0) is at the center of the canvas.
+ * let mx = mouseX - 50;
+ *
+ * // Draw the line.
+ * line(mx, -50, mx, 50);
+ * }
+ *
+ *
+ * let font;
+ *
+ * // Load a font for WebGL mode.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * "A gray square. The mouse's x- and y-coordinates are displayed as the user moves the mouse."
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Display the mouse's coordinates.
+ * text(`x: ${mouseX} y: ${mouseY}`, 0, 0);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe("A horizontal black line moves up and down following the mouse's y-position.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a horizontal line that follows the mouse's y-coordinate.
+ * line(0, mouseY, 0, mouseY);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe("A gray square. The mouse's x- and y-coordinates are displayed as the user moves the mouse.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the mouse's coordinates.
+ * text(`x: ${mouseX} y: ${mouseY}`, 50, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe("A horizontal black line moves up and down following the mouse's y-position.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Adjust coordinates for WebGL mode.
+ * // The origin (0, 0) is at the center of the canvas.
+ * let my = mouseY - 50;
+ *
+ * // Draw the line.
+ * line(-50, my, 50, my);
+ * }
+ *
+ *
+ * let font;
+ *
+ * // Load a font for WebGL mode.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * "A gray square. The mouse's x- and y-coordinates are displayed as the user moves the mouse."
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Display the mouse's coordinates.
+ * text(`x: ${mouseX} y: ${mouseY}`, 0, 0);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Slow the frame rate.
+ * frameRate(10);
+ *
+ * describe('A line follows the mouse as it moves. The line grows longer with faster movements.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * line(pmouseX, pmouseY, mouseX, mouseY);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A line follows the mouse as it moves. The line grows longer with faster movements.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Adjust coordinates for WebGL mode.
+ * // The origin (0, 0) is at the center of the canvas.
+ * let pmx = pmouseX - 50;
+ * let pmy = pmouseY - 50;
+ * let mx = mouseX - 50;
+ * let my = mouseY - 50;
+ *
+ * // Draw the line.
+ * line(pmx, pmy, mx, my);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Slow the frame rate.
+ * frameRate(10);
+ *
+ * describe('A line follows the mouse as it moves. The line grows longer with faster movements.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * line(pmouseX, pmouseY, mouseX, mouseY);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A line follows the mouse as it moves. The line grows longer with faster movements.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Adjust coordinates for WebGL mode.
+ * // The origin (0, 0) is at the center of the canvas.
+ * let pmx = pmouseX - 50;
+ * let pmy = pmouseY - 50;
+ * let mx = mouseX - 50;
+ * let my = mouseY - 50;
+ *
+ * // Draw the line.
+ * line(pmx, pmy, mx, my);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe("A gray square. The mouse's x- and y-coordinates are displayed as the user moves the mouse.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the mouse's coordinates within the browser window.
+ * text(`x: ${winMouseX} y: ${winMouseY}`, 50, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe("A gray square. The mouse's x- and y-coordinates are displayed as the user moves the mouse.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the mouse's coordinates within the browser window.
+ * text(`x: ${winMouseX} y: ${winMouseY}`, 50, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Slow the frame rate.
+ * frameRate(10);
+ *
+ * describe('A gray square. A white circle at its center grows larger when the mouse moves horizontally.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the circle's diameter.
+ * let d = winMouseX - pwinMouseX;
+ *
+ * // Draw the circle.
+ * circle(50, 50, d);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create the canvas and set its position.
+ * let cnv = createCanvas(100, 100);
+ * cnv.position(20, 20);
+ *
+ * describe('A gray square with a number at its center. The number changes as the user moves the mouse vertically.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display pwinMouseX.
+ * text(pwinMouseX, 50, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Slow the frame rate.
+ * frameRate(10);
+ *
+ * describe('A gray square. A white circle at its center grows larger when the mouse moves vertically.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the circle's diameter.
+ * let d = winMouseY - pwinMouseY;
+ *
+ * // Draw the circle.
+ * circle(50, 50, d);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create the canvas and set its position.
+ * let cnv = createCanvas(100, 100);
+ * cnv.position(20, 20);
+ *
+ * describe('A gray square with a number at its center. The number changes as the user moves the mouse vertically.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display pwinMouseY.
+ * text(pwinMouseY, 50, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with black text at its center. The text changes from 0 to either "left" or "right" when the user clicks a mouse button.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the mouse button.
+ * text(mouseButton, 50, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * "A gray square. Different shapes appear at its center depending on the mouse button that's clicked."
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * if (mouseIsPressed === true) {
+ * if (mouseButton === LEFT) {
+ * circle(50, 50, 50);
+ * }
+ * if (mouseButton === RIGHT) {
+ * square(25, 25, 50);
+ * }
+ * if (mouseButton === CENTER) {
+ * triangle(23, 75, 50, 20, 78, 75);
+ * }
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with the word "false" at its center. The word changes to "true" when the user presses a mouse button.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the mouseIsPressed variable.
+ * text(mouseIsPressed, 25, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a white square at its center. The inner square turns black when the user presses the mouse.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * if (mouseIsPressed === true) {
+ * fill(0);
+ * } else {
+ * fill(255);
+ * }
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ *
+ * function mouseMoved() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The mouse system variables, such as mouseX and
+ * mouseY, will be updated with their most recent
+ * value when `mouseMoved()` is called by p5.js:
+ *
+ *
+ * function mouseMoved() {
+ * if (mouseX < 50) {
+ * // Code to run if the mouse is on the left.
+ * }
+ *
+ * if (mouseY > 50) {
+ * // Code to run if the mouse is near the bottom.
+ * }
+ * }
+ *
+ *
+ * The parameter, `event`, is optional. `mouseMoved()` is always passed a
+ * MouseEvent
+ * object with properties that describe the mouse move event:
+ *
+ *
+ * function mouseMoved(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * Browsers may have default behaviors attached to various mouse events. For
+ * example, some browsers highlight text when the user moves the mouse while
+ * pressing a mouse button. To prevent any default behavior for this event,
+ * add `return false;` to the end of the function.
+ *
+ * @method mouseMoved
+ * @param {MouseEvent} [event] optional `MouseEvent` argument.
+ *
+ * @example
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square becomes lighter as the mouse moves.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * function mouseMoved() {
+ * // Update the grayscale value.
+ * value += 5;
+ *
+ * // Reset the grayscale value.
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * function mouseDragged() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The mouse system variables, such as mouseX and
+ * mouseY, will be updated with their most recent
+ * value when `mouseDragged()` is called by p5.js:
+ *
+ *
+ * function mouseDragged() {
+ * if (mouseX < 50) {
+ * // Code to run if the mouse is on the left.
+ * }
+ *
+ * if (mouseY > 50) {
+ * // Code to run if the mouse is near the bottom.
+ * }
+ * }
+ *
+ *
+ * The parameter, `event`, is optional. `mouseDragged()` is always passed a
+ * MouseEvent
+ * object with properties that describe the mouse drag event:
+ *
+ *
+ * function mouseDragged(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * On touchscreen devices, `mouseDragged()` will run when a user moves a touch
+ * point if touchMoved() isn’t declared. If
+ * touchMoved() is declared, then
+ * touchMoved() will run when a user moves a
+ * touch point and `mouseDragged()` won’t.
+ *
+ * Browsers may have default behaviors attached to various mouse events. For
+ * example, some browsers highlight text when the user moves the mouse while
+ * pressing a mouse button. To prevent any default behavior for this event,
+ * add `return false;` to the end of the function.
+ *
+ * @method mouseDragged
+ * @param {MouseEvent} [event] optional `MouseEvent` argument.
+ *
+ * @example
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square becomes lighter as the user drags the mouse.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * function mouseDragged() {
+ * // Update the grayscale value.
+ * value += 5;
+ *
+ * // Reset the grayscale value.
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * function mousePressed() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The mouse system variables, such as mouseX and
+ * mouseY, will be updated with their most recent
+ * value when `mousePressed()` is called by p5.js:
+ *
+ *
+ * function mousePressed() {
+ * if (mouseX < 50) {
+ * // Code to run if the mouse is on the left.
+ * }
+ *
+ * if (mouseY > 50) {
+ * // Code to run if the mouse is near the bottom.
+ * }
+ * }
+ *
+ *
+ * The parameter, `event`, is optional. `mousePressed()` is always passed a
+ * MouseEvent
+ * object with properties that describe the mouse press event:
+ *
+ *
+ * function mousePressed(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * On touchscreen devices, `mousePressed()` will run when a user’s touch
+ * begins if touchStarted() isn’t declared. If
+ * touchStarted() is declared, then
+ * touchStarted() will run when a user’s touch
+ * begins and `mousePressed()` won’t.
+ *
+ * Browsers may have default behaviors attached to various mouse events. For
+ * example, some browsers highlight text when the user moves the mouse while
+ * pressing a mouse button. To prevent any default behavior for this event,
+ * add `return false;` to the end of the function.
+ *
+ * Note: `mousePressed()`, mouseReleased(),
+ * and mouseClicked() are all related.
+ * `mousePressed()` runs as soon as the user clicks the mouse.
+ * mouseReleased() runs as soon as the user
+ * releases the mouse click. mouseClicked()
+ * runs immediately after mouseReleased().
+ *
+ * @method mousePressed
+ * @param {MouseEvent} [event] optional `MouseEvent` argument.
+ *
+ * @example
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square becomes lighter when the user presses a mouse button.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * function mousePressed() {
+ * // Update the grayscale value.
+ * value += 5;
+ *
+ * // Reset the grayscale value.
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Style the circle.
+ * fill('orange');
+ * stroke('royalblue');
+ * strokeWeight(10);
+ *
+ * describe(
+ * 'An orange circle with a thick, blue border drawn on a gray background. When the user presses and holds the mouse, the border becomes thin and pink. When the user releases the mouse, the border becomes thicker and changes color to blue.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(220);
+ *
+ * // Draw the circle.
+ * circle(50, 50, 20);
+ * }
+ *
+ * // Set the stroke color and weight as soon as the user clicks.
+ * function mousePressed() {
+ * stroke('deeppink');
+ * strokeWeight(3);
+ * }
+ *
+ * // Set the stroke and fill colors as soon as the user releases
+ * // the mouse.
+ * function mouseReleased() {
+ * stroke('royalblue');
+ *
+ * // This is never visible because fill() is called
+ * // in mouseClicked() which runs immediately after
+ * // mouseReleased();
+ * fill('limegreen');
+ * }
+ *
+ * // Set the fill color and stroke weight after
+ * // mousePressed() and mouseReleased() are called.
+ * function mouseClicked() {
+ * fill('orange');
+ * strokeWeight(10);
+ * }
+ *
+ *
+ * function mouseReleased() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The mouse system variables, such as mouseX and
+ * mouseY, will be updated with their most recent
+ * value when `mouseReleased()` is called by p5.js:
+ *
+ *
+ * function mouseReleased() {
+ * if (mouseX < 50) {
+ * // Code to run if the mouse is on the left.
+ * }
+ *
+ * if (mouseY > 50) {
+ * // Code to run if the mouse is near the bottom.
+ * }
+ * }
+ *
+ *
+ * The parameter, `event`, is optional. `mouseReleased()` is always passed a
+ * MouseEvent
+ * object with properties that describe the mouse release event:
+ *
+ *
+ * function mouseReleased(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * On touchscreen devices, `mouseReleased()` will run when a user’s touch
+ * ends if touchEnded() isn’t declared. If
+ * touchEnded() is declared, then
+ * touchEnded() will run when a user’s touch
+ * ends and `mouseReleased()` won’t.
+ *
+ * Browsers may have default behaviors attached to various mouse events. For
+ * example, some browsers highlight text when the user moves the mouse while
+ * pressing a mouse button. To prevent any default behavior for this event,
+ * add `return false;` to the end of the function.
+ *
+ * Note: mousePressed(), `mouseReleased()`,
+ * and mouseClicked() are all related.
+ * mousePressed() runs as soon as the user
+ * clicks the mouse. `mouseReleased()` runs as soon as the user releases the
+ * mouse click. mouseClicked() runs
+ * immediately after `mouseReleased()`.
+ *
+ * @method mouseReleased
+ * @param {MouseEvent} [event] optional `MouseEvent` argument.
+ *
+ * @example
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square becomes lighter when the user presses and releases a mouse button.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * function mouseReleased() {
+ * // Update the grayscale value.
+ * value += 5;
+ *
+ * // Reset the grayscale value.
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Style the circle.
+ * fill('orange');
+ * stroke('royalblue');
+ * strokeWeight(10);
+ *
+ * describe(
+ * 'An orange circle with a thick, blue border drawn on a gray background. When the user presses and holds the mouse, the border becomes thin and pink. When the user releases the mouse, the border becomes thicker and changes color to blue.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(220);
+ *
+ * // Draw the circle.
+ * circle(50, 50, 20);
+ * }
+ *
+ * // Set the stroke color and weight as soon as the user clicks.
+ * function mousePressed() {
+ * stroke('deeppink');
+ * strokeWeight(3);
+ * }
+ *
+ * // Set the stroke and fill colors as soon as the user releases
+ * // the mouse.
+ * function mouseReleased() {
+ * stroke('royalblue');
+ *
+ * // This is never visible because fill() is called
+ * // in mouseClicked() which runs immediately after
+ * // mouseReleased();
+ * fill('limegreen');
+ * }
+ *
+ * // Set the fill color and stroke weight after
+ * // mousePressed() and mouseReleased() are called.
+ * function mouseClicked() {
+ * fill('orange');
+ * strokeWeight(10);
+ * }
+ *
+ *
+ * function mouseClicked() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The mouse system variables, such as mouseX and
+ * mouseY, will be updated with their most recent
+ * value when `mouseClicked()` is called by p5.js:
+ *
+ *
+ * function mouseClicked() {
+ * if (mouseX < 50) {
+ * // Code to run if the mouse is on the left.
+ * }
+ *
+ * if (mouseY > 50) {
+ * // Code to run if the mouse is near the bottom.
+ * }
+ * }
+ *
+ *
+ * The parameter, `event`, is optional. `mouseClicked()` is always passed a
+ * MouseEvent
+ * object with properties that describe the mouse click event:
+ *
+ *
+ * function mouseClicked(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * On touchscreen devices, `mouseClicked()` will run when a user’s touch
+ * ends if touchEnded() isn’t declared. If
+ * touchEnded() is declared, then
+ * touchEnded() will run when a user’s touch
+ * ends and `mouseClicked()` won’t.
+ *
+ * Browsers may have default behaviors attached to various mouse events. For
+ * example, some browsers highlight text when the user moves the mouse while
+ * pressing a mouse button. To prevent any default behavior for this event,
+ * add `return false;` to the end of the function.
+ *
+ * Note: mousePressed(),
+ * mouseReleased(),
+ * and `mouseClicked()` are all related.
+ * mousePressed() runs as soon as the user
+ * clicks the mouse. mouseReleased() runs as
+ * soon as the user releases the mouse click. `mouseClicked()` runs
+ * immediately after mouseReleased().
+ *
+ * @method mouseClicked
+ * @param {MouseEvent} [event] optional `MouseEvent` argument.
+ *
+ * @example
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square changes color when the user presses and releases a mouse button.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * // Toggle the square's color when the user clicks.
+ * function mouseClicked() {
+ * if (value === 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Style the circle.
+ * fill('orange');
+ * stroke('royalblue');
+ * strokeWeight(10);
+ *
+ * describe(
+ * 'An orange circle with a thick, blue border drawn on a gray background. When the user presses and holds the mouse, the border becomes thin and pink. When the user releases the mouse, the border becomes thicker and changes color to blue.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(220);
+ *
+ * // Draw the circle.
+ * circle(50, 50, 20);
+ * }
+ *
+ * // Set the stroke color and weight as soon as the user clicks.
+ * function mousePressed() {
+ * stroke('deeppink');
+ * strokeWeight(3);
+ * }
+ *
+ * // Set the stroke and fill colors as soon as the user releases
+ * // the mouse.
+ * function mouseReleased() {
+ * stroke('royalblue');
+ *
+ * // This is never visible because fill() is called
+ * // in mouseClicked() which runs immediately after
+ * // mouseReleased();
+ * fill('limegreen');
+ * }
+ *
+ * // Set the fill color and stroke weight after
+ * // mousePressed() and mouseReleased() are called.
+ * function mouseClicked() {
+ * fill('orange');
+ * strokeWeight(10);
+ * }
+ *
+ *
+ * function doubleClicked() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The mouse system variables, such as mouseX and
+ * mouseY, will be updated with their most recent
+ * value when `doubleClicked()` is called by p5.js:
+ *
+ *
+ * function doubleClicked() {
+ * if (mouseX < 50) {
+ * // Code to run if the mouse is on the left.
+ * }
+ *
+ * if (mouseY > 50) {
+ * // Code to run if the mouse is near the bottom.
+ * }
+ * }
+ *
+ *
+ * The parameter, `event`, is optional. `doubleClicked()` is always passed a
+ * MouseEvent
+ * object with properties that describe the double-click event:
+ *
+ *
+ * function doubleClicked(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * On touchscreen devices, code placed in `doubleClicked()` will run after two
+ * touches that occur within a short time.
+ *
+ * Browsers may have default behaviors attached to various mouse events. For
+ * example, some browsers highlight text when the user moves the mouse while
+ * pressing a mouse button. To prevent any default behavior for this event,
+ * add `return false;` to the end of the function.
+ *
+ * @method doubleClicked
+ * @param {MouseEvent} [event] optional `MouseEvent` argument.
+ *
+ * @example
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square changes color when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * // Toggle the square's color when the user double-clicks.
+ * function doubleClicked() {
+ * if (value === 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black circle at its center. When the user double-clicks on the circle, it changes color to white.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the circle.
+ * fill(value);
+ *
+ * // Draw the circle.
+ * circle(50, 50, 80);
+ * }
+ *
+ * // Reassign value to 255 when the user double-clicks on the circle.
+ * function doubleClicked() {
+ * if (dist(50, 50, mouseX, mouseY) < 40) {
+ * value = 255;
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * function mouseWheel() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The mouse system variables, such as mouseX and
+ * mouseY, will be updated with their most recent
+ * value when `mouseWheel()` is called by p5.js:
+ *
+ *
+ * function mouseWheel() {
+ * if (mouseX < 50) {
+ * // Code to run if the mouse is on the left.
+ * }
+ *
+ * if (mouseY > 50) {
+ * // Code to run if the mouse is near the bottom.
+ * }
+ * }
+ *
+ *
+ * The parameter, `event`, is optional. `mouseWheel()` is always passed a
+ * MouseEvent
+ * object with properties that describe the mouse scroll event:
+ *
+ *
+ * function mouseWheel(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * The `event` object has many properties including `delta`, a `Number`
+ * containing the distance that the user scrolled. For example, `event.delta`
+ * might have the value 5 when the user scrolls up. `event.delta` is positive
+ * if the user scrolls up and negative if they scroll down. The signs are
+ * opposite on macOS with "natural" scrolling enabled.
+ *
+ * Browsers may have default behaviors attached to various mouse events. For
+ * example, some browsers highlight text when the user moves the mouse while
+ * pressing a mouse button. To prevent any default behavior for this event,
+ * add `return false;` to the end of the function.
+ *
+ * Note: On Safari, `mouseWheel()` may only work as expected if
+ * `return false;` is added at the end of the function.
+ *
+ * @method mouseWheel
+ * @param {WheelEvent} [event] optional `WheelEvent` argument.
+ *
+ * @example
+ *
+ * let circleSize = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square. A white circle at its center grows up when the user scrolls the mouse wheel.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the circle
+ * circle(circleSize, 50, 50);
+ * }
+ *
+ * // Increment circleSize when the user scrolls the mouse wheel.
+ * function mouseWheel() {
+ * circleSize += 1;
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * let direction = '';
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square. An arrow at its center points up when the user scrolls up. The arrow points down when the user scrolls down.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Draw an arrow that points where
+ * // the mouse last scrolled.
+ * text(direction, 50, 50);
+ * }
+ *
+ * // Change direction when the user scrolls the mouse wheel.
+ * function mouseWheel(event) {
+ * if (event.delta > 0) {
+ * direction = '▲';
+ * } else {
+ * direction = '▼';
+ * }
+ * // Uncomment to prevent any default behavior.
+ * // return false;
+ * }
+ *
+ *
+ * let score = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with the text "Score: X" at its center. The score increases when the user moves the mouse upward. It decreases when the user moves the mouse downward.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Update the score.
+ * score -= movedY;
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the score.
+ * text(`Score: ${score}`, 50, 50);
+ * }
+ *
+ * // Lock the pointer when the user double-clicks.
+ * function doubleClicked() {
+ * requestPointerLock();
+ * }
+ *
+ *
+ * let isLocked = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a word at its center. The word changes between "Unlocked" and "Locked" when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Tell the user whether the pointer is locked.
+ * if (isLocked === true) {
+ * text('Locked', 50, 50);
+ * } else {
+ * text('Unlocked', 50, 50);
+ * }
+ * }
+ *
+ * // Toggle the pointer lock when the user double-clicks.
+ * function doubleClicked() {
+ * if (isLocked === true) {
+ * exitPointerLock();
+ * isLocked = false;
+ * } else {
+ * requestPointerLock();
+ * isLocked = true;
+ * }
+ * }
+ *
+ *
+ * // Iterate over the touches array.
+ * for (let touch of touches) {
+ * // x-coordinate relative to the top-left
+ * // corner of the canvas.
+ * console.log(touch.x);
+ *
+ * // y-coordinate relative to the top-left
+ * // corner of the canvas.
+ * console.log(touch.y);
+ *
+ * // x-coordinate relative to the top-left
+ * // corner of the browser.
+ * console.log(touch.winX);
+ *
+ * // y-coordinate relative to the top-left
+ * // corner of the browser.
+ * console.log(touch.winY);
+ *
+ * // ID number
+ * console.log(touch.id);
+ * }
+ *
+ *
+ * @property {Object[]} touches
+ * @readOnly
+ *
+ * @example
+ *
+ * // On a touchscreen device, touch the canvas using one or more fingers
+ * // at the same time.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square. White circles appear where the user touches the square.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a circle at each touch point.
+ * for (let touch of touches) {
+ * circle(touch.x, touch.y, 40);
+ * }
+ * }
+ *
+ *
+ * // On a touchscreen device, touch the canvas using one or more fingers
+ * // at the same time.
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square. Labels appear where the user touches the square, displaying the coordinates.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a label above each touch point.
+ * for (let touch of touches) {
+ * text(`${touch.x}, ${touch.y}`, touch.x, touch.y - 40);
+ * }
+ * }
+ *
+ *
+ * function touchStarted() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The touches array will be updated with the most
+ * recent touch points when `touchStarted()` is called by p5.js:
+ *
+ *
+ * function touchStarted() {
+ * // Paint over the background.
+ * background(200);
+ *
+ * // Mark each touch point once with a circle.
+ * for (let touch of touches) {
+ * circle(touch.x, touch.y, 40);
+ * }
+ * }
+ *
+ *
+ * The parameter, event, is optional. `touchStarted()` will be passed a
+ * TouchEvent
+ * object with properties that describe the touch event:
+ *
+ *
+ * function touchStarted(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * On touchscreen devices, mousePressed() will
+ * run when a user’s touch starts if `touchStarted()` isn’t declared. If
+ * `touchStarted()` is declared, then `touchStarted()` will run when a user’s
+ * touch starts and mousePressed() won’t.
+ *
+ * Note: `touchStarted()`, touchEnded(), and
+ * touchMoved() are all related.
+ * `touchStarted()` runs as soon as the user touches a touchscreen device.
+ * touchEnded() runs as soon as the user ends a
+ * touch. touchMoved() runs repeatedly as the
+ * user moves any touch points.
+ *
+ * @method touchStarted
+ * @param {TouchEvent} [event] optional `TouchEvent` argument.
+ *
+ * @example
+ *
+ * // On a touchscreen device, touch the canvas using one or more fingers
+ * // at the same time.
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square switches color between black and white each time the user touches the screen.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * // Toggle colors with each touch.
+ * function touchStarted() {
+ * if (value === 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ * // On a touchscreen device, touch the canvas using one or more fingers
+ * // at the same time.
+ *
+ * let bgColor = 50;
+ * let fillColor = 255;
+ * let borderWidth = 0.5;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with the number 0 at the top-center. The number tracks the number of places the user is touching the screen. Circles appear at each touch point and change style in response to events.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(bgColor);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * fill(0);
+ * noStroke();
+ *
+ * // Display the number of touch points.
+ * text(touches.length, 50, 20);
+ *
+ * // Style the touch points.
+ * fill(fillColor);
+ * stroke(0);
+ * strokeWeight(borderWidth);
+ *
+ * // Display the touch points as circles.
+ * for (let touch of touches) {
+ * circle(touch.x, touch.y, 40);
+ * }
+ * }
+ *
+ * // Set the background color to a random grayscale value.
+ * function touchStarted() {
+ * bgColor = random(80, 255);
+ * }
+ *
+ * // Set the fill color to a random grayscale value.
+ * function touchEnded() {
+ * fillColor = random(0, 255);
+ * }
+ *
+ * // Set the stroke weight.
+ * function touchMoved() {
+ * // Increment the border width.
+ * borderWidth += 0.1;
+ *
+ * // Reset the border width once it's too thick.
+ * if (borderWidth > 20) {
+ * borderWidth = 0.5;
+ * }
+ * }
+ *
+ *
+ * function touchMoved() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The touches array will be updated with the most
+ * recent touch points when `touchMoved()` is called by p5.js:
+ *
+ *
+ * function touchMoved() {
+ * // Paint over the background.
+ * background(200);
+ *
+ * // Mark each touch point while the user moves.
+ * for (let touch of touches) {
+ * circle(touch.x, touch.y, 40);
+ * }
+ * }
+ *
+ *
+ * The parameter, event, is optional. `touchMoved()` will be passed a
+ * TouchEvent
+ * object with properties that describe the touch event:
+ *
+ *
+ * function touchMoved(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * On touchscreen devices, mouseDragged() will
+ * run when the user’s touch points move if `touchMoved()` isn’t declared. If
+ * `touchMoved()` is declared, then `touchMoved()` will run when a user’s
+ * touch points move and mouseDragged() won’t.
+ *
+ * Note: touchStarted(),
+ * touchEnded(), and
+ * `touchMoved()` are all related.
+ * touchStarted() runs as soon as the user
+ * touches a touchscreen device. touchEnded()
+ * runs as soon as the user ends a touch. `touchMoved()` runs repeatedly as
+ * the user moves any touch points.
+ *
+ * @method touchMoved
+ * @param {TouchEvent} [event] optional TouchEvent argument.
+ *
+ * @example
+ *
+ * // On a touchscreen device, touch the canvas using one or more fingers
+ * // at the same time.
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square becomes lighter when the user touches the screen and moves.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * function touchMoved() {
+ * // Update the grayscale value.
+ * value += 5;
+ *
+ * // Reset the grayscale value.
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ * // On a touchscreen device, touch the canvas using one or more fingers
+ * // at the same time.
+ *
+ * let bgColor = 50;
+ * let fillColor = 255;
+ * let borderWidth = 0.5;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with the number 0 at the top-center. The number tracks the number of places the user is touching the screen. Circles appear at each touch point and change style in response to events.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(bgColor);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * fill(0);
+ * noStroke();
+ *
+ * // Display the number of touch points.
+ * text(touches.length, 50, 20);
+ *
+ * // Style the touch points.
+ * fill(fillColor);
+ * stroke(0);
+ * strokeWeight(borderWidth);
+ *
+ * // Display the touch points as circles.
+ * for (let touch of touches) {
+ * circle(touch.x, touch.y, 40);
+ * }
+ * }
+ *
+ * // Set the background color to a random grayscale value.
+ * function touchStarted() {
+ * bgColor = random(80, 255);
+ * }
+ *
+ * // Set the fill color to a random grayscale value.
+ * function touchEnded() {
+ * fillColor = random(0, 255);
+ * }
+ *
+ * // Set the stroke weight.
+ * function touchMoved() {
+ * // Increment the border width.
+ * borderWidth += 0.1;
+ *
+ * // Reset the border width once it's too thick.
+ * if (borderWidth > 20) {
+ * borderWidth = 0.5;
+ * }
+ * }
+ *
+ *
+ * function touchEnded() {
+ * // Code to run.
+ * }
+ *
+ *
+ * The touches array will be updated with the most
+ * recent touch points when `touchEnded()` is called by p5.js:
+ *
+ *
+ * function touchEnded() {
+ * // Paint over the background.
+ * background(200);
+ *
+ * // Mark each remaining touch point when the user stops
+ * // a touch.
+ * for (let touch of touches) {
+ * circle(touch.x, touch.y, 40);
+ * }
+ * }
+ *
+ *
+ * The parameter, event, is optional. `touchEnded()` will be passed a
+ * TouchEvent
+ * object with properties that describe the touch event:
+ *
+ *
+ * function touchEnded(event) {
+ * // Code to run that uses the event.
+ * console.log(event);
+ * }
+ *
+ *
+ * On touchscreen devices, mouseReleased() will
+ * run when the user’s touch ends if `touchEnded()` isn’t declared. If
+ * `touchEnded()` is declared, then `touchEnded()` will run when a user’s
+ * touch ends and mouseReleased() won’t.
+ *
+ * Note: touchStarted(),
+ * `touchEnded()`, and touchMoved() are all
+ * related. touchStarted() runs as soon as the
+ * user touches a touchscreen device. `touchEnded()` runs as soon as the user
+ * ends a touch. touchMoved() runs repeatedly as
+ * the user moves any touch points.
+ *
+ * @method touchEnded
+ * @param {TouchEvent} [event] optional `TouchEvent` argument.
+ *
+ * @example
+ *
+ * // On a touchscreen device, touch the canvas using one or more fingers
+ * // at the same time.
+ *
+ * let value = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with a black square at its center. The inner square switches color between black and white each time the user stops touching the screen.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the square.
+ * fill(value);
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ * }
+ *
+ * // Toggle colors when a touch ends.
+ * function touchEnded() {
+ * if (value === 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ * // On a touchscreen device, touch the canvas using one or more fingers
+ * // at the same time.
+ *
+ * let bgColor = 50;
+ * let fillColor = 255;
+ * let borderWidth = 0.5;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe(
+ * 'A gray square with the number 0 at the top-center. The number tracks the number of places the user is touching the screen. Circles appear at each touch point and change style in response to events.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(bgColor);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * fill(0);
+ * noStroke();
+ *
+ * // Display the number of touch points.
+ * text(touches.length, 50, 20);
+ *
+ * // Style the touch points.
+ * fill(fillColor);
+ * stroke(0);
+ * strokeWeight(borderWidth);
+ *
+ * // Display the touch points as circles.
+ * for (let touch of touches) {
+ * circle(touch.x, touch.y, 40);
+ * }
+ * }
+ *
+ * // Set the background color to a random grayscale value.
+ * function touchStarted() {
+ * bgColor = random(80, 255);
+ * }
+ *
+ * // Set the fill color to a random grayscale value.
+ * function touchEnded() {
+ * fillColor = random(0, 255);
+ * }
+ *
+ * // Set the stroke weight.
+ * function touchMoved() {
+ * // Increment the border width.
+ * borderWidth += 0.1;
+ *
+ * // Reset the border width once it's too thick.
+ * if (borderWidth > 20) {
+ * borderWidth = 0.5;
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(66, 66);
+ *
+ * // Load the image's pixels into memory.
+ * img.loadPixels();
+ *
+ * // Set all the image's pixels to black.
+ * for (let x = 0; x < img.width; x += 1) {
+ * for (let y = 0; y < img.height; y += 1) {
+ * img.set(x, y, 0);
+ * }
+ * }
+ *
+ * // Update the image's pixel values.
+ * img.updatePixels();
+ *
+ * // Draw the image.
+ * image(img, 17, 17);
+ *
+ * describe('A black square drawn in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(66, 66);
+ *
+ * // Load the image's pixels into memory.
+ * img.loadPixels();
+ *
+ * // Create a color gradient.
+ * for (let x = 0; x < img.width; x += 1) {
+ * for (let y = 0; y < img.height; y += 1) {
+ * // Calculate the transparency.
+ * let a = map(x, 0, img.width, 0, 255);
+ *
+ * // Create a p5.Color object.
+ * let c = color(0, a);
+ *
+ * // Set the pixel's color.
+ * img.set(x, y, c);
+ * }
+ * }
+ *
+ * // Update the image's pixels.
+ * img.updatePixels();
+ *
+ * // Display the image.
+ * image(img, 17, 17);
+ *
+ * describe('A square with a horizontal color gradient that transitions from gray to black.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(66, 66);
+ *
+ * // Load the pixels into memory.
+ * img.loadPixels();
+ * // Get the current pixel density.
+ * let d = pixelDensity();
+ *
+ * // Calculate the pixel that is halfway through the image's pixel array.
+ * let halfImage = 4 * (d * img.width) * (d * img.height / 2);
+ *
+ * // Set half of the image's pixels to black.
+ * for (let i = 0; i < halfImage; i += 4) {
+ * // Red.
+ * img.pixels[i] = 0;
+ * // Green.
+ * img.pixels[i + 1] = 0;
+ * // Blue.
+ * img.pixels[i + 2] = 0;
+ * // Alpha.
+ * img.pixels[i + 3] = 255;
+ * }
+ *
+ * // Update the image's pixels.
+ * img.updatePixels();
+ *
+ * // Display the image.
+ * image(img, 17, 17);
+ *
+ * describe('A black square drawn in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * background(255);
+ *
+ * // Save the canvas to 'untitled.png'.
+ * saveCanvas();
+ *
+ * describe('A white square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(255);
+ *
+ * // Save the canvas to 'myCanvas.jpg'.
+ * saveCanvas('myCanvas.jpg');
+ *
+ * describe('A white square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(255);
+ *
+ * // Save the canvas to 'myCanvas.jpg'.
+ * saveCanvas('myCanvas', 'jpg');
+ *
+ * describe('A white square.');
+ * }
+ *
+ *
+ * function setup() {
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(255);
+ *
+ * // Save the canvas to 'untitled.png'.
+ * saveCanvas(cnv);
+ *
+ * describe('A white square.');
+ * }
+ *
+ *
+ * function setup() {
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(255);
+ *
+ * // Save the canvas to 'myCanvas.jpg'.
+ * saveCanvas(cnv, 'myCanvas.jpg');
+ *
+ * describe('A white square.');
+ * }
+ *
+ *
+ * function setup() {
+ * let cnv = createCanvas(100, 100);
+ *
+ * background(255);
+ *
+ * // Save the canvas to 'myCanvas.jpg'.
+ * saveCanvas(cnv, 'myCanvas', 'jpg');
+ *
+ * describe('A white square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A square repeatedly changes color from blue to pink.');
+ * }
+ *
+ * function draw() {
+ * let r = frameCount % 255;
+ * let g = 50;
+ * let b = 100;
+ * background(r, g, b);
+ * }
+ *
+ * // Save the frames when the user presses the 's' key.
+ * function keyPressed() {
+ * if (key === 's') {
+ * saveFrames('frame', 'png', 1, 5);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A square repeatedly changes color from blue to pink.');
+ * }
+ *
+ * function draw() {
+ * let r = frameCount % 255;
+ * let g = 50;
+ * let b = 100;
+ * background(r, g, b);
+ * }
+ *
+ * // Print 5 frames when the user presses the mouse.
+ * function mousePressed() {
+ * saveFrames('frame', 'png', 1, 5, printFrames);
+ * }
+ *
+ * // Prints an array of objects containing raw image data, filenames, and extensions.
+ * function printFrames(frames) {
+ * for (let frame of frames) {
+ * print(frame);
+ * }
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image and create a p5.Image object.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Draw the image.
+ * image(img, 0, 0);
+ *
+ * describe('Image of the underside of a white umbrella and a gridded ceiling.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Call handleImage() once the image loads.
+ * loadImage('assets/laDefense.jpg', handleImage);
+ *
+ * describe('Image of the underside of a white umbrella and a gridded ceiling.');
+ * }
+ *
+ * // Display the image.
+ * function handleImage(img) {
+ * image(img, 0, 0);
+ * }
+ *
+ *
+ * function setup() {
+ * // Call handleImage() once the image loads or
+ * // call handleError() if an error occurs.
+ * loadImage('assets/laDefense.jpg', handleImage, handleError);
+ * }
+ *
+ * // Display the image.
+ * function handleImage(img) {
+ * image(img, 0, 0);
+ *
+ * describe('Image of the underside of a white umbrella and a gridded ceiling.');
+ * }
+ *
+ * // Log the error.
+ * function handleError(event) {
+ * console.error('Oops!', event);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A circle drawn in the middle of a gray square. The circle changes color from black to white, then repeats.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the circle.
+ * let c = frameCount % 255;
+ * fill(c);
+ *
+ * // Display the circle.
+ * circle(50, 50, 25);
+ * }
+ *
+ * // Save a 5-second gif when the user presses the 's' key.
+ * function keyPressed() {
+ * if (key === 's') {
+ * saveGif('mySketch', 5);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A circle drawn in the middle of a gray square. The circle changes color from black to white, then repeats.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the circle.
+ * let c = frameCount % 255;
+ * fill(c);
+ *
+ * // Display the circle.
+ * circle(50, 50, 25);
+ * }
+ *
+ * // Save a 5-second gif when the user presses the 's' key.
+ * // Wait 1 second after the key press before recording.
+ * function keyPressed() {
+ * if (key === 's') {
+ * saveGif('mySketch', 5, { delay: 1 });
+ * }
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Draw the image.
+ * image(img, 0, 0);
+ *
+ * describe('An image of the underside of a white umbrella with a gridded ceiling above.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Draw the image.
+ * image(img, 10, 10);
+ *
+ * describe('An image of the underside of a white umbrella with a gridded ceiling above. The image has dark gray borders on its left and top.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Draw the image 50x50.
+ * image(img, 0, 0, 50, 50);
+ *
+ * describe('An image of the underside of a white umbrella with a gridded ceiling above. The image is drawn in the top left corner of a dark gray square.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Draw the center of the image.
+ * image(img, 25, 25, 50, 50, 25, 25, 50, 50);
+ *
+ * describe('An image of a gridded ceiling drawn in the center of a dark gray square.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/moonwalk.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Draw the image and scale it to fit within the canvas.
+ * image(img, 0, 0, width, height, 0, 0, img.width, img.height, CONTAIN);
+ *
+ * describe('An image of an astronaut on the moon. The top and bottom borders of the image are dark gray.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * // Image is 50 x 50 pixels.
+ * img = loadImage('assets/laDefense50.png');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Draw the image and scale it to cover the canvas.
+ * image(img, 0, 0, width, height, 0, 0, img.width, img.height, COVER);
+ *
+ * describe('A pixelated image of the underside of a white umbrella with a gridded ceiling above.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Left image.
+ * image(img, 0, 0);
+ *
+ * // Right image.
+ * // Tint with a CSS color string.
+ * tint('red');
+ * image(img, 50, 0);
+ *
+ * describe('Two images of an umbrella and a ceiling side-by-side. The image on the right has a red tint.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Left image.
+ * image(img, 0, 0);
+ *
+ * // Right image.
+ * // Tint with RGB values.
+ * tint(255, 0, 0);
+ * image(img, 50, 0);
+ *
+ * describe('Two images of an umbrella and a ceiling side-by-side. The image on the right has a red tint.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Left.
+ * image(img, 0, 0);
+ *
+ * // Right.
+ * // Tint with RGBA values.
+ * tint(255, 0, 0, 100);
+ * image(img, 50, 0);
+ *
+ * describe('Two images of an umbrella and a ceiling side-by-side. The image on the right has a transparent red tint.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Left.
+ * image(img, 0, 0);
+ *
+ * // Right.
+ * // Tint with grayscale and alpha values.
+ * tint(255, 180);
+ * image(img, 50, 0);
+ *
+ * describe('Two images of an umbrella and a ceiling side-by-side. The image on the right is transparent.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Left.
+ * // Tint with a CSS color string.
+ * tint('red');
+ * image(img, 0, 0);
+ *
+ * // Right.
+ * // Remove the tint.
+ * noTint();
+ * image(img, 50, 0);
+ *
+ * describe('Two images of an umbrella and a ceiling side-by-side. The image on the left has a red tint.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use CORNER mode.
+ * imageMode(CORNER);
+ *
+ * // Display the image.
+ * image(img, 10, 10, 50, 50);
+ *
+ * describe('A square image of a brick wall is drawn at the top left of a gray square.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use CORNERS mode.
+ * imageMode(CORNERS);
+ *
+ * // Display the image.
+ * image(img, 10, 10, 90, 40);
+ *
+ * describe('An image of a brick wall is drawn on a gray square. The image is squeezed into a small rectangular area.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use CENTER mode.
+ * imageMode(CENTER);
+ *
+ * // Display the image.
+ * image(img, 50, 50, 80, 80);
+ *
+ * describe('A square image of a brick wall is drawn on a gray square.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('An image of a brick wall.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Apply the GRAY filter.
+ * img.filter(GRAY);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('A grayscale image of a brick wall.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(66, 66);
+ *
+ * // Load the image's pixels.
+ * img.loadPixels();
+ *
+ * // Set the pixels to black.
+ * for (let x = 0; x < img.width; x += 1) {
+ * for (let y = 0; y < img.height; y += 1) {
+ * img.set(x, y, 0);
+ * }
+ * }
+ *
+ * // Update the image.
+ * img.updatePixels();
+ *
+ * // Display the image.
+ * image(img, 17, 17);
+ *
+ * describe('A black square drawn in the middle of a gray square.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Calculate the center coordinates.
+ * let x = img.width / 2;
+ * let y = img.height / 2;
+ *
+ * // Draw a circle at the image's center.
+ * circle(x, y, 20);
+ *
+ * describe('An image of a mountain landscape with a white circle drawn in the middle.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Calculate the center coordinates.
+ * let x = img.width / 2;
+ * let y = img.height / 2;
+ *
+ * // Draw a circle at the image's center.
+ * circle(x, y, 20);
+ *
+ * describe('An image of a mountain landscape with a white circle drawn in the middle.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(66, 66);
+ *
+ * // Load the image's pixels.
+ * img.loadPixels();
+ *
+ * for (let i = 0; i < img.pixels.length; i += 4) {
+ * // Red.
+ * img.pixels[i] = 0;
+ * // Green.
+ * img.pixels[i + 1] = 0;
+ * // Blue.
+ * img.pixels[i + 2] = 0;
+ * // Alpha.
+ * img.pixels[i + 3] = 255;
+ * }
+ *
+ * // Update the image.
+ * img.updatePixels();
+ *
+ * // Display the image.
+ * image(img, 17, 17);
+ *
+ * describe('A black square drawn in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(66, 66);
+ *
+ * // Load the image's pixels.
+ * img.loadPixels();
+ *
+ * // Set the pixels to red.
+ * for (let i = 0; i < img.pixels.length; i += 4) {
+ * // Red.
+ * img.pixels[i] = 255;
+ * // Green.
+ * img.pixels[i + 1] = 0;
+ * // Blue.
+ * img.pixels[i + 2] = 0;
+ * // Alpha.
+ * img.pixels[i + 3] = 255;
+ * }
+ *
+ * // Update the image.
+ * img.updatePixels();
+ *
+ * // Display the image.
+ * image(img, 17, 17);
+ *
+ * describe('A red square drawn in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(66, 66);
+ *
+ * // Load the image's pixels.
+ * img.loadPixels();
+ *
+ * // Set the pixels to black.
+ * for (let x = 0; x < img.width; x += 1) {
+ * for (let y = 0; y < img.height; y += 1) {
+ * img.set(x, y, 0);
+ * }
+ * }
+ *
+ * // Update the image.
+ * img.updatePixels();
+ *
+ * // Display the image.
+ * image(img, 17, 17);
+ *
+ * describe('A black square drawn in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(66, 66);
+ *
+ * // Load the image's pixels.
+ * img.loadPixels();
+ *
+ * for (let i = 0; i < img.pixels.length; i += 4) {
+ * // Red.
+ * img.pixels[i] = 0;
+ * // Green.
+ * img.pixels[i + 1] = 0;
+ * // Blue.
+ * img.pixels[i + 2] = 0;
+ * // Alpha.
+ * img.pixels[i + 3] = 255;
+ * }
+ *
+ * // Update the image.
+ * img.updatePixels();
+ *
+ * // Display the image.
+ * image(img, 17, 17);
+ *
+ * describe('A black square drawn in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(66, 66);
+ *
+ * // Load the image's pixels.
+ * img.loadPixels();
+ *
+ * // Set the pixels to black.
+ * for (let x = 0; x < img.width; x += 1) {
+ * for (let y = 0; y < img.height; y += 1) {
+ * img.set(x, y, 0);
+ * }
+ * }
+ *
+ * // Update the image.
+ * img.updatePixels();
+ *
+ * // Display the image.
+ * image(img, 17, 17);
+ *
+ * describe('A black square drawn in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(66, 66);
+ *
+ * // Load the image's pixels.
+ * img.loadPixels();
+ *
+ * // Set the pixels to black.
+ * for (let i = 0; i < img.pixels.length; i += 4) {
+ * // Red.
+ * img.pixels[i] = 0;
+ * // Green.
+ * img.pixels[i + 1] = 0;
+ * // Blue.
+ * img.pixels[i + 2] = 0;
+ * // Alpha.
+ * img.pixels[i + 3] = 255;
+ * }
+ *
+ * // Update the image.
+ * img.updatePixels();
+ *
+ * // Display the image.
+ * image(img, 17, 17);
+ *
+ * describe('A black square drawn in the middle of a gray square.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Copy the image.
+ * let img2 = get();
+ *
+ * // Display the copied image on the right.
+ * image(img2, 50, 0);
+ *
+ * describe('Two identical mountain landscapes shown side-by-side.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Get a pixel's color.
+ * let c = img.get(50, 90);
+ *
+ * // Style the square using the pixel's color.
+ * fill(c);
+ * noStroke();
+ *
+ * // Draw the square.
+ * square(25, 25, 50);
+ *
+ * describe('A mountain landscape with an olive green square in its center.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Copy half of the image.
+ * let img2 = img.get(0, 0, img.width / 2, img.height / 2);
+ *
+ * // Display half of the image.
+ * image(img2, 50, 50);
+ *
+ * describe('A mountain landscape drawn on top of another mountain landscape.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(100, 100);
+ *
+ * // Set four pixels to black.
+ * img.set(30, 20, 0);
+ * img.set(85, 20, 0);
+ * img.set(85, 75, 0);
+ * img.set(30, 75, 0);
+ *
+ * // Update the image.
+ * img.updatePixels();
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('Four black dots arranged in a square drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(100, 100);
+ *
+ * // Create a p5.Color object.
+ * let black = color(0);
+ *
+ * // Set four pixels to black.
+ * img.set(30, 20, black);
+ * img.set(85, 20, black);
+ * img.set(85, 75, black);
+ * img.set(30, 75, black);
+ *
+ * // Update the image.
+ * img.updatePixels();
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('Four black dots arranged in a square drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Image object.
+ * let img = createImage(66, 66);
+ *
+ * // Draw a color gradient.
+ * for (let x = 0; x < img.width; x += 1) {
+ * for (let y = 0; y < img.height; y += 1) {
+ * let c = map(x, 0, img.width, 0, 255);
+ * img.set(x, y, c);
+ * }
+ * }
+ *
+ * // Update the image.
+ * img.updatePixels();
+ *
+ * // Display the image.
+ * image(img, 17, 17);
+ *
+ * describe('A square with a horiztonal color gradient from black to white drawn on a gray background.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.Image object.
+ * let img2 = createImage(100, 100);
+ *
+ * // Set the blank image's pixels using the landscape.
+ * img2.set(0, 0, img);
+ *
+ * // Display the second image.
+ * image(img2, 0, 0);
+ *
+ * describe('An image of a mountain landscape.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Resize the image.
+ * img.resize(50, 100);
+ *
+ * // Display the resized image.
+ * image(img, 0, 0);
+ *
+ * describe('Two images of a mountain landscape. One copy of the image is squeezed horizontally.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Resize the image, keeping the aspect ratio.
+ * img.resize(0, 30);
+ *
+ * // Display the resized image.
+ * image(img, 0, 0);
+ *
+ * describe('Two images of a mountain landscape. The small copy of the image covers the top-left corner of the larger image.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Resize the image, keeping the aspect ratio.
+ * img.resize(60, 0);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('Two images of a mountain landscape. The small copy of the image covers the top-left corner of the larger image.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Copy one region of the image to another.
+ * img.copy(7, 22, 10, 10, 35, 25, 50, 50);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Outline the copied region.
+ * stroke(255);
+ * noFill();
+ * square(7, 22, 10);
+ *
+ * describe('An image of a mountain landscape. A square region is outlined in white. A larger square contains a pixelated view of the outlined region.');
+ * }
+ *
+ *
+ * let mountains;
+ * let bricks;
+ *
+ * // Load the images.
+ * function preload() {
+ * mountains = loadImage('assets/rockies.jpg');
+ * bricks = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Calculate the center of the bricks image.
+ * let x = bricks.width / 2;
+ * let y = bricks.height / 2;
+ *
+ * // Copy the bricks to the mountains image.
+ * mountains.copy(bricks, 0, 0, x, y, 0, 0, x, y);
+ *
+ * // Display the mountains image.
+ * image(mountains, 0, 0);
+ *
+ * describe('An image of a brick wall drawn at the top-left of an image of a mountain landscape.');
+ * }
+ *
+ *
+ * let photo;
+ * let maskImage;
+ *
+ * // Load the images.
+ * function preload() {
+ * photo = loadImage('assets/rockies.jpg');
+ * maskImage = loadImage('assets/mask2.png');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Apply the mask.
+ * photo.mask(maskImage);
+ *
+ * // Display the image.
+ * image(photo, 0, 0);
+ *
+ * describe('An image of a mountain landscape. The right side of the image has a faded patch of white.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Apply the INVERT filter.
+ * img.filter(INVERT);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('A blue brick wall.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Apply the GRAY filter.
+ * img.filter(GRAY);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('A brick wall drawn in grayscale.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Apply the THRESHOLD filter.
+ * img.filter(THRESHOLD);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('A brick wall drawn in black and white.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Apply the OPAQUE filter.
+ * img.filter(OPAQUE);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('A red brick wall.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Apply the POSTERIZE filter.
+ * img.filter(POSTERIZE, 3);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('An image of a red brick wall drawn with a limited color palette.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Apply the BLUR filter.
+ * img.filter(BLUR, 3);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('A blurry image of a red brick wall.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Apply the DILATE filter.
+ * img.filter(DILATE);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('A red brick wall with bright lines between each brick.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Apply the ERODE filter.
+ * img.filter(ERODE);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('A red brick wall with faint lines between each brick.');
+ * }
+ *
+ *
+ * let mountains;
+ * let bricks;
+ *
+ * // Load the images.
+ * function preload() {
+ * mountains = loadImage('assets/rockies.jpg');
+ * bricks = loadImage('assets/bricks_third.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Blend the bricks image into the mountains.
+ * mountains.blend(bricks, 0, 0, 33, 100, 67, 0, 33, 100, ADD);
+ *
+ * // Display the mountains image.
+ * image(mountains, 0, 0);
+ *
+ * // Display the bricks image.
+ * image(bricks, 0, 0);
+ *
+ * describe('A wall of bricks in front of a mountain landscape. The same wall of bricks appears faded on the right of the image.');
+ * }
+ *
+ *
+ * let mountains;
+ * let bricks;
+ *
+ * // Load the images.
+ * function preload() {
+ * mountains = loadImage('assets/rockies.jpg');
+ * bricks = loadImage('assets/bricks_third.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Blend the bricks image into the mountains.
+ * mountains.blend(bricks, 0, 0, 33, 100, 67, 0, 33, 100, DARKEST);
+ *
+ * // Display the mountains image.
+ * image(mountains, 0, 0);
+ *
+ * // Display the bricks image.
+ * image(bricks, 0, 0);
+ *
+ * describe('A wall of bricks in front of a mountain landscape. The same wall of bricks appears transparent on the right of the image.');
+ * }
+ *
+ *
+ * let mountains;
+ * let bricks;
+ *
+ * // Load the images.
+ * function preload() {
+ * mountains = loadImage('assets/rockies.jpg');
+ * bricks = loadImage('assets/bricks_third.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Blend the bricks image into the mountains.
+ * mountains.blend(bricks, 0, 0, 33, 100, 67, 0, 33, 100, LIGHTEST);
+ *
+ * // Display the mountains image.
+ * image(mountains, 0, 0);
+ *
+ * // Display the bricks image.
+ * image(bricks, 0, 0);
+ *
+ * describe('A wall of bricks in front of a mountain landscape. The same wall of bricks appears washed out on the right of the image.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * describe('An image of a mountain landscape. The image is downloaded when the user presses the "s", "j", or "p" key.');
+ * }
+ *
+ * // Save the image with different options when the user presses a key.
+ * function keyPressed() {
+ * if (key === 's') {
+ * img.save();
+ * } else if (key === 'j') {
+ * img.save('rockies.jpg');
+ * } else if (key === 'p') {
+ * img.save('rockies', 'png');
+ * }
+ * }
+ *
+ *
+ * let gif;
+ *
+ * // Load the image.
+ * function preload() {
+ * gif = loadImage('assets/arnott-wallace-wink-loop-once.gif');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A cartoon face winks once and then freezes. Clicking resets the face and makes it wink again.');
+ * }
+ *
+ * function draw() {
+ * background(255);
+ *
+ * // Display the image.
+ * image(gif, 0, 0);
+ * }
+ *
+ * // Reset the GIF when the user presses the mouse.
+ * function mousePressed() {
+ * gif.reset();
+ * }
+ *
+ *
+ * let gif;
+ *
+ * // Load the image.
+ * function preload() {
+ * gif = loadImage('assets/arnott-wallace-eye-loop-forever.gif');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A cartoon eye repeatedly looks around, then outwards. A number displayed in the bottom-left corner increases from 0 to 124, then repeats.');
+ * }
+ *
+ * function draw() {
+ * // Get the index of the current GIF frame.
+ * let index = gif.getCurrentFrame();
+ *
+ * // Display the image.
+ * image(gif, 0, 0);
+ *
+ * // Display the current frame.
+ * text(index, 10, 90);
+ * }
+ *
+ *
+ * let gif;
+ * let frameSlider;
+ *
+ * // Load the image.
+ * function preload() {
+ * gif = loadImage('assets/arnott-wallace-eye-loop-forever.gif');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Get the index of the last frame.
+ * let maxFrame = gif.numFrames() - 1;
+ *
+ * // Create a slider to control which frame is drawn.
+ * frameSlider = createSlider(0, maxFrame);
+ * frameSlider.position(10, 80);
+ * frameSlider.size(80);
+ *
+ * describe('A cartoon eye looks around when a slider is moved.');
+ * }
+ *
+ * function draw() {
+ * // Get the slider's value.
+ * let index = frameSlider.value();
+ *
+ * // Set the GIF's frame.
+ * gif.setFrame(index);
+ *
+ * // Display the image.
+ * image(gif, 0, 0);
+ * }
+ *
+ *
+ * let gif;
+ *
+ * // Load the image.
+ * function preload() {
+ * gif = loadImage('assets/arnott-wallace-eye-loop-forever.gif');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A cartoon eye looks around. The text "n / 125" is shown at the bottom of the canvas.');
+ * }
+ *
+ * function draw() {
+ * // Display the image.
+ * image(gif, 0, 0);
+ *
+ * // Display the current state of playback.
+ * let total = gif.numFrames();
+ * let index = gif.getCurrentFrame();
+ * text(`${index} / ${total}`, 30, 90);
+ * }
+ *
+ *
+ * let gif;
+ *
+ * // Load the image.
+ * function preload() {
+ * gif = loadImage('assets/nancy-liang-wind-loop-forever.gif');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A drawing of a child with hair blowing in the wind. The animation freezes when clicked and resumes when released.');
+ * }
+ *
+ * function draw() {
+ * background(255);
+ * image(gif, 0, 0);
+ * }
+ *
+ * // Pause the GIF when the user presses the mouse.
+ * function mousePressed() {
+ * gif.pause();
+ * }
+ *
+ * // Play the GIF when the user releases the mouse.
+ * function mouseReleased() {
+ * gif.play();
+ * }
+ *
+ *
+ * let gif;
+ *
+ * // Load the image.
+ * function preload() {
+ * gif = loadImage('assets/nancy-liang-wind-loop-forever.gif');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A drawing of a child with hair blowing in the wind. The animation freezes when clicked and resumes when released.');
+ * }
+ *
+ * function draw() {
+ * background(255);
+ *
+ * // Display the image.
+ * image(gif, 0, 0);
+ * }
+ *
+ * // Pause the GIF when the user presses the mouse.
+ * function mousePressed() {
+ * gif.pause();
+ * }
+ *
+ * // Play the GIF when the user presses the mouse.
+ * function mouseReleased() {
+ * gif.play();
+ * }
+ *
+ *
+ * let gifFast;
+ * let gifSlow;
+ *
+ * // Load the images.
+ * function preload() {
+ * gifFast = loadImage('assets/arnott-wallace-eye-loop-forever.gif');
+ * gifSlow = loadImage('assets/arnott-wallace-eye-loop-forever.gif');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Resize the images.
+ * gifFast.resize(50, 50);
+ * gifSlow.resize(50, 50);
+ *
+ * // Set the delay lengths.
+ * gifFast.delay(10);
+ * gifSlow.delay(100);
+ *
+ * describe('Two animated eyes looking around. The eye on the left moves faster than the eye on the right.');
+ * }
+ *
+ * function draw() {
+ * // Display the images.
+ * image(gifFast, 0, 0);
+ * image(gifSlow, 50, 0);
+ * }
+ *
+ *
+ * let gif;
+ *
+ * // Load the image.
+ * function preload() {
+ * gif = loadImage('assets/arnott-wallace-eye-loop-forever.gif');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Set the delay of frame 67.
+ * gif.delay(3000, 67);
+ *
+ * describe('An animated eye looking around. It pauses for three seconds while it looks down.');
+ * }
+ *
+ * function draw() {
+ * // Display the image.
+ * image(gif, 0, 0);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Load the pixels array.
+ * loadPixels();
+ *
+ * // Set the dot's coordinates.
+ * let x = 50;
+ * let y = 50;
+ *
+ * // Get the pixel density.
+ * let d = pixelDensity();
+ *
+ * // Set the pixel(s) at the center of the canvas black.
+ * for (let i = 0; i < d; i += 1) {
+ * for (let j = 0; j < d; j += 1) {
+ * let index = 4 * ((y * d + j) * width * d + (x * d + i));
+ * // Red.
+ * pixels[index] = 0;
+ * // Green.
+ * pixels[index + 1] = 0;
+ * // Blue.
+ * pixels[index + 2] = 0;
+ * // Alpha.
+ * pixels[index + 3] = 255;
+ * }
+ * }
+ *
+ * // Update the canvas.
+ * updatePixels();
+ *
+ * describe('A black dot in the middle of a gray rectangle.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Load the pixels array.
+ * loadPixels();
+ *
+ * // Get the pixel density.
+ * let d = pixelDensity();
+ *
+ * // Calculate the halfway index in the pixels array.
+ * let halfImage = 4 * (d * width) * (d * height / 2);
+ *
+ * // Make the top half of the canvas red.
+ * for (let i = 0; i < halfImage; i += 4) {
+ * // Red.
+ * pixels[i] = 255;
+ * // Green.
+ * pixels[i + 1] = 0;
+ * // Blue.
+ * pixels[i + 2] = 0;
+ * // Alpha.
+ * pixels[i + 3] = 255;
+ * }
+ *
+ * // Update the canvas.
+ * updatePixels();
+ *
+ * describe('A red rectangle drawn above a gray rectangle.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create a p5.Color object.
+ * let pink = color(255, 102, 204);
+ *
+ * // Load the pixels array.
+ * loadPixels();
+ *
+ * // Get the pixel density.
+ * let d = pixelDensity();
+ *
+ * // Calculate the halfway index in the pixels array.
+ * let halfImage = 4 * (d * width) * (d * height / 2);
+ *
+ * // Make the top half of the canvas red.
+ * for (let i = 0; i < halfImage; i += 4) {
+ * pixels[i] = red(pink);
+ * pixels[i + 1] = green(pink);
+ * pixels[i + 2] = blue(pink);
+ * pixels[i + 3] = alpha(pink);
+ * }
+ *
+ * // Update the canvas.
+ * updatePixels();
+ *
+ * describe('A pink rectangle drawn above a gray rectangle.');
+ * }
+ *
+ *
+ * let img0;
+ * let img1;
+ *
+ * // Load the images.
+ * function preload() {
+ * img0 = loadImage('assets/rockies.jpg');
+ * img1 = loadImage('assets/bricks_third.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use the mountains as the background.
+ * background(img0);
+ *
+ * // Display the bricks.
+ * image(img1, 0, 0);
+ *
+ * // Display the bricks faded into the landscape.
+ * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, LIGHTEST);
+ *
+ * describe('A wall of bricks in front of a mountain landscape. The same wall of bricks appears faded on the right of the image.');
+ * }
+ *
+ *
+ * let img0;
+ * let img1;
+ *
+ * // Load the images.
+ * function preload() {
+ * img0 = loadImage('assets/rockies.jpg');
+ * img1 = loadImage('assets/bricks_third.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use the mountains as the background.
+ * background(img0);
+ *
+ * // Display the bricks.
+ * image(img1, 0, 0);
+ *
+ * // Display the bricks partially transparent.
+ * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, DARKEST);
+ *
+ * describe('A wall of bricks in front of a mountain landscape. The same wall of bricks appears transparent on the right of the image.');
+ * }
+ *
+ *
+ * let img0;
+ * let img1;
+ *
+ * // Load the images.
+ * function preload() {
+ * img0 = loadImage('assets/rockies.jpg');
+ * img1 = loadImage('assets/bricks_third.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use the mountains as the background.
+ * background(img0);
+ *
+ * // Display the bricks.
+ * image(img1, 0, 0);
+ *
+ * // Display the bricks washed out into the landscape.
+ * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, ADD);
+ *
+ * describe('A wall of bricks in front of a mountain landscape. The same wall of bricks appears washed out on the right of the image.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use the mountains as the background.
+ * background(img);
+ *
+ * // Copy a region of pixels to another spot.
+ * copy(img, 7, 22, 10, 10, 35, 25, 50, 50);
+ *
+ * // Outline the copied region.
+ * stroke(255);
+ * noFill();
+ * square(7, 22, 10);
+ *
+ * describe('An image of a mountain landscape. A square region is outlined in white. A larger square contains a pixelated view of the outlined region.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Apply the INVERT filter.
+ * filter(INVERT);
+ *
+ * describe('A blue brick wall.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Apply the GRAY filter.
+ * filter(GRAY);
+ *
+ * describe('A brick wall drawn in grayscale.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Apply the THRESHOLD filter.
+ * filter(THRESHOLD);
+ *
+ * describe('A brick wall drawn in black and white.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Apply the OPAQUE filter.
+ * filter(OPAQUE);
+ *
+ * describe('A red brick wall.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Apply the POSTERIZE filter.
+ * filter(POSTERIZE, 3);
+ *
+ * describe('An image of a red brick wall drawn with limited color palette.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Apply the BLUR filter.
+ * filter(BLUR, 3);
+ *
+ * describe('A blurry image of a red brick wall.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Apply the DILATE filter.
+ * filter(DILATE);
+ *
+ * describe('A red brick wall with bright lines between each brick.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Apply the ERODE filter.
+ * filter(ERODE);
+ *
+ * describe('A red brick wall with faint lines between each brick.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Apply the BLUR filter.
+ * // Don't use WebGL.
+ * filter(BLUR, 3, false);
+ *
+ * describe('A blurry image of a red brick wall.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Get the entire canvas.
+ * let c = get();
+ *
+ * // Display half the canvas.
+ * image(c, 50, 0);
+ *
+ * describe('Two identical mountain landscapes shown side-by-side.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Get the color of a pixel.
+ * let c = get(50, 90);
+ *
+ * // Style the square with the pixel's color.
+ * fill(c);
+ * noStroke();
+ *
+ * // Display the square.
+ * square(25, 25, 50);
+ *
+ * describe('A mountain landscape with an olive green square in its center.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0);
+ *
+ * // Get a region of the image.
+ * let c = get(0, 0, 50, 50);
+ *
+ * // Display the region.
+ * image(c, 50, 50);
+ *
+ * describe('A mountain landscape drawn on top of another mountain landscape.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0, 100, 100);
+ *
+ * // Get the pixel density.
+ * let d = pixelDensity();
+ *
+ * // Calculate the halfway index in the pixels array.
+ * let halfImage = 4 * (d * width) * (d * height / 2);
+ *
+ * // Load the pixels array.
+ * loadPixels();
+ *
+ * // Copy the top half of the canvas to the bottom.
+ * for (let i = 0; i < halfImage; i += 1) {
+ * pixels[i + halfImage] = pixels[i];
+ * }
+ *
+ * // Update the canvas.
+ * updatePixels();
+ *
+ * describe('Two identical images of mountain landscapes, one on top of the other.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set four pixels to black.
+ * set(30, 20, 0);
+ * set(85, 20, 0);
+ * set(85, 75, 0);
+ * set(30, 75, 0);
+ *
+ * // Update the canvas.
+ * updatePixels();
+ *
+ * describe('Four black dots arranged in a square drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object.
+ * let black = color(0);
+ *
+ * // Set four pixels to black.
+ * set(30, 20, black);
+ * set(85, 20, black);
+ * set(85, 75, black);
+ * set(30, 75, black);
+ *
+ * // Update the canvas.
+ * updatePixels();
+ *
+ * describe('Four black dots arranged in a square drawn on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(255);
+ *
+ * // Draw a horizontal color gradient.
+ * for (let x = 0; x < 100; x += 1) {
+ * for (let y = 0; y < 100; y += 1) {
+ * // Calculate the grayscale value.
+ * let c = map(x, 0, 100, 0, 255);
+ *
+ * // Set the pixel using the grayscale value.
+ * set(x, y, c);
+ * }
+ * }
+ *
+ * // Update the canvas.
+ * updatePixels();
+ *
+ * describe('A horiztonal color gradient from black to white.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use the image to set all pixels.
+ * set(0, 0, img);
+ *
+ * // Update the canvas.
+ * updatePixels();
+ *
+ * describe('An image of a mountain landscape.');
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load the image.
+ * function preload() {
+ * img = loadImage('assets/rockies.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Display the image.
+ * image(img, 0, 0, 100, 100);
+ *
+ * // Get the pixel density.
+ * let d = pixelDensity();
+ *
+ * // Calculate the halfway index in the pixels array.
+ * let halfImage = 4 * (d * width) * (d * height / 2);
+ *
+ * // Load the pixels array.
+ * loadPixels();
+ *
+ * // Copy the top half of the canvas to the bottom.
+ * for (let i = 0; i < halfImage; i += 1) {
+ * pixels[i + halfImage] = pixels[i];
+ * }
+ *
+ * // Update the canvas.
+ * updatePixels();
+ *
+ * describe('Two identical images of mountain landscapes, one on top of the other.');
+ * }
+ *
+ *
+ * let myData;
+ *
+ * // Load the JSON and create an object.
+ * function preload() {
+ * myData = loadJSON('assets/data.json');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the circle.
+ * fill(myData.color);
+ * noStroke();
+ *
+ * // Draw the circle.
+ * circle(myData.x, myData.y, myData.d);
+ *
+ * describe('A pink circle on a gray background.');
+ * }
+ *
+ *
+ * let myData;
+ *
+ * // Load the JSON and create an object.
+ * function preload() {
+ * myData = loadJSON('assets/data.json');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Color object and make it transparent.
+ * let c = color(myData.color);
+ * c.setAlpha(80);
+ *
+ * // Style the circles.
+ * fill(c);
+ * noStroke();
+ *
+ * // Iterate over the myData.bubbles array.
+ * for (let b of myData.bubbles) {
+ * // Draw a circle for each bubble.
+ * circle(b.x, b.y, b.d);
+ * }
+ *
+ * describe('Several pink bubbles floating in a blue sky.');
+ * }
+ *
+ *
+ * let myData;
+ *
+ * // Load the GeoJSON and create an object.
+ * function preload() {
+ * myData = loadJSON('https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get data about the most recent earthquake.
+ * let quake = myData.features[0].properties;
+ *
+ * // Draw a circle based on the earthquake's magnitude.
+ * circle(50, 50, quake.mag * 10);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(11);
+ *
+ * // Display the earthquake's location.
+ * text(quake.place, 5, 80, 100);
+ *
+ * describe(`A white circle on a gray background. The text "${quake.place}" is written beneath the circle.`);
+ * }
+ *
+ *
+ * let bigQuake;
+ *
+ * // Load the GeoJSON and preprocess it.
+ * function preload() {
+ * loadJSON(
+ * 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson',
+ * handleData
+ * );
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw a circle based on the earthquake's magnitude.
+ * circle(50, 50, bigQuake.mag * 10);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(11);
+ *
+ * // Display the earthquake's location.
+ * text(bigQuake.place, 5, 80, 100);
+ *
+ * describe(`A white circle on a gray background. The text "${bigQuake.place}" is written beneath the circle.`);
+ * }
+ *
+ * // Find the biggest recent earthquake.
+ * function handleData(data) {
+ * let maxMag = 0;
+ * // Iterate over the earthquakes array.
+ * for (let quake of data.features) {
+ * // Reassign bigQuake if a larger
+ * // magnitude quake is found.
+ * if (quake.properties.mag > maxMag) {
+ * bigQuake = quake.properties;
+ * }
+ * }
+ * }
+ *
+ *
+ * let bigQuake;
+ *
+ * // Load the GeoJSON and preprocess it.
+ * function preload() {
+ * loadJSON(
+ * 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson',
+ * handleData,
+ * handleError
+ * );
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw a circle based on the earthquake's magnitude.
+ * circle(50, 50, bigQuake.mag * 10);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(11);
+ *
+ * // Display the earthquake's location.
+ * text(bigQuake.place, 5, 80, 100);
+ *
+ * describe(`A white circle on a gray background. The text "${bigQuake.place}" is written beneath the circle.`);
+ * }
+ *
+ * // Find the biggest recent earthquake.
+ * function handleData(data) {
+ * let maxMag = 0;
+ * // Iterate over the earthquakes array.
+ * for (let quake of data.features) {
+ * // Reassign bigQuake if a larger
+ * // magnitude quake is found.
+ * if (quake.properties.mag > maxMag) {
+ * bigQuake = quake.properties;
+ * }
+ * }
+ * }
+ *
+ * // Log any errors to the console.
+ * function handleError(error) {
+ * console.log('Oops!', error);
+ * }
+ *
+ *
+ * let myData;
+ *
+ * // Load the text and create an array.
+ * function preload() {
+ * myData = loadStrings('assets/test.txt');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Select a random line from the text.
+ * let phrase = random(myData);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display the text.
+ * text(phrase, 10, 50, 90);
+ *
+ * describe(`The text "${phrase}" written in black on a gray background.`);
+ * }
+ *
+ *
+ * let lastLine;
+ *
+ * // Load the text and preprocess it.
+ * function preload() {
+ * loadStrings('assets/test.txt', handleData);
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display the text.
+ * text(lastLine, 10, 50, 90);
+ *
+ * describe('The text "I talk like an orange" written in black on a gray background.');
+ * }
+ *
+ * // Select the last line from the text.
+ * function handleData(data) {
+ * lastLine = data[data.length - 1];
+ * }
+ *
+ *
+ * let lastLine;
+ *
+ * // Load the text and preprocess it.
+ * function preload() {
+ * loadStrings('assets/test.txt', handleData, handleError);
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display the text.
+ * text(lastLine, 10, 50, 90);
+ *
+ * describe('The text "I talk like an orange" written in black on a gray background.');
+ * }
+ *
+ * // Select the last line from the text.
+ * function handleData(data) {
+ * lastLine = data[data.length - 1];
+ * }
+ *
+ * // Log any errors to the console.
+ * function handleError(error) {
+ * console.error('Oops!', error);
+ * }
+ *
+ *
+ * // Given the following CSV file called "mammals.csv"
+ * // located in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * //the file can be remote
+ * //table = loadTable("http://p5js.org/reference/assets/mammals.csv",
+ * // "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //count the columns
+ * print(table.getRowCount() + ' total rows in table');
+ * print(table.getColumnCount() + ' total columns in table');
+ *
+ * print(table.getColumn('name'));
+ * //["Goat", "Leopard", "Zebra"]
+ *
+ * //cycle through the table
+ * for (let r = 0; r < table.getRowCount(); r++)
+ * for (let c = 0; c < table.getColumnCount(); c++) {
+ * print(table.getString(r, c));
+ * }
+ * describe(`randomly generated text from a file,
+ * for example "i smell like butter"`);
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get an array with all mammal tags.
+ * let mammals = myXML.getChildren('mammal');
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Iterate over the mammals array.
+ * for (let i = 0; i < mammals.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 25;
+ *
+ * // Get the mammal's common name.
+ * let name = mammals[i].getContent();
+ *
+ * // Display the mammal's name.
+ * text(name, 20, y);
+ * }
+ *
+ * describe(
+ * 'The words "Goat", "Leopard", and "Zebra" written on three separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * let lastMammal;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * loadXML('assets/animals.xml', handleData);
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(16);
+ *
+ * // Display the content of the last mammal element.
+ * text(lastMammal, 50, 50);
+ *
+ * describe('The word "Zebra" written in black on a gray background.');
+ * }
+ *
+ * // Get the content of the last mammal element.
+ * function handleData(data) {
+ * // Get an array with all mammal elements.
+ * let mammals = data.getChildren('mammal');
+ *
+ * // Get the content of the last mammal.
+ * lastMammal = mammals[mammals.length - 1].getContent();
+ * }
+ *
+ *
+ * let lastMammal;
+ *
+ * // Load the XML and preprocess it.
+ * function preload() {
+ * loadXML('assets/animals.xml', handleData, handleError);
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(16);
+ *
+ * // Display the content of the last mammal element.
+ * text(lastMammal, 50, 50);
+ *
+ * describe('The word "Zebra" written in black on a gray background.');
+ * }
+ *
+ * // Get the content of the last mammal element.
+ * function handleData(data) {
+ * // Get an array with all mammal elements.
+ * let mammals = data.getChildren('mammal');
+ *
+ * // Get the content of the last mammal.
+ * lastMammal = mammals[mammals.length - 1].getContent();
+ * }
+ *
+ * // Log any errors to the console.
+ * function handleError(error) {
+ * console.error('Oops!', error);
+ * }
+ *
+ *
+ * let data;
+ *
+ * function preload() {
+ * data = loadBytes('assets/mammals.xml');
+ * }
+ *
+ * function setup() {
+ * for (let i = 0; i < 5; i++) {
+ * console.log(data.bytes[i].toString(16));
+ * }
+ * describe('no image displayed');
+ * }
+ *
httpDo(path, 'GET')
. The 'binary' datatype will return
+ * a Blob object, and the 'arrayBuffer' datatype will return an ArrayBuffer
+ * which can be used to initialize typed arrays (such as Uint8Array).
+ *
+ * @method httpGet
+ * @param {String} path name of the file or url to load
+ * @param {String} [datatype] "json", "jsonp", "binary", "arrayBuffer",
+ * "xml", or "text"
+ * @param {Object|Boolean} [data] param data passed sent with request
+ * @param {function} [callback] function to be executed after
+ * httpGet() completes, data is passed in
+ * as first argument
+ * @param {function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ * @return {Promise} A promise that resolves with the data when the operation
+ * completes successfully or rejects with the error after
+ * one occurs.
+ * @example
+ *
+ * // Examples use USGS Earthquake API:
+ * // https://earthquake.usgs.gov/fdsnws/event/1/#methods
+ * let earthquakes;
+ * function preload() {
+ * // Get the most recent earthquake in the database
+ * let url =
+ 'https://earthquake.usgs.gov/fdsnws/event/1/query?' +
+ * 'format=geojson&limit=1&orderby=time';
+ * httpGet(url, 'jsonp', false, function(response) {
+ * // when the HTTP request completes, populate the variable that holds the
+ * // earthquake data used in the visualization.
+ * earthquakes = response;
+ * });
+ * }
+ *
+ * function draw() {
+ * if (!earthquakes) {
+ * // Wait until the earthquake data has loaded before drawing.
+ * return;
+ * }
+ * background(200);
+ * // Get the magnitude and name of the earthquake out of the loaded JSON
+ * let earthquakeMag = earthquakes.features[0].properties.mag;
+ * let earthquakeName = earthquakes.features[0].properties.place;
+ * ellipse(width / 2, height / 2, earthquakeMag * 10, earthquakeMag * 10);
+ * textAlign(CENTER);
+ * text(earthquakeName, 0, height - 30, width, 30);
+ * noLoop();
+ * }
+ *
httpDo(path, 'POST')
.
+ *
+ * @method httpPost
+ * @param {String} path name of the file or url to load
+ * @param {String} [datatype] "json", "jsonp", "xml", or "text".
+ * If omitted, httpPost() will guess.
+ * @param {Object|Boolean} [data] param data passed sent with request
+ * @param {function} [callback] function to be executed after
+ * httpPost() completes, data is passed in
+ * as first argument
+ * @param {function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ * @return {Promise} A promise that resolves with the data when the operation
+ * completes successfully or rejects with the error after
+ * one occurs.
+ *
+ * @example
+ *
+ * // Examples use jsonplaceholder.typicode.com for a Mock Data API
+ *
+ * let url = 'https://jsonplaceholder.typicode.com/posts';
+ * let postData = { userId: 1, title: 'p5 Clicked!', body: 'p5.js is very cool.' };
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * background(200);
+ * }
+ *
+ * function mousePressed() {
+ * httpPost(url, 'json', postData, function(result) {
+ * strokeWeight(2);
+ * text(result.body, mouseX, mouseY);
+ * });
+ * }
+ *
+ *
+ * let url = 'ttps://invalidURL'; // A bad URL that will cause errors
+ * let postData = { title: 'p5 Clicked!', body: 'p5.js is very cool.' };
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * background(200);
+ * }
+ *
+ * function mousePressed() {
+ * httpPost(
+ * url,
+ * 'json',
+ * postData,
+ * function(result) {
+ * // ... won't be called
+ * },
+ * function(error) {
+ * strokeWeight(2);
+ * text(error.toString(), mouseX, mouseY);
+ * }
+ * );
+ * }
+ *
+ * // Examples use USGS Earthquake API:
+ * // https://earthquake.usgs.gov/fdsnws/event/1/#methods
+ *
+ * // displays an animation of all USGS earthquakes
+ * let earthquakes;
+ * let eqFeatureIndex = 0;
+ *
+ * function preload() {
+ * let url = 'https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson';
+ * httpDo(
+ * url,
+ * {
+ * method: 'GET',
+ * // Other Request options, like special headers for apis
+ * headers: { authorization: 'Bearer secretKey' }
+ * },
+ * function(res) {
+ * earthquakes = res;
+ * }
+ * );
+ * }
+ *
+ * function draw() {
+ * // wait until the data is loaded
+ * if (!earthquakes || !earthquakes.features[eqFeatureIndex]) {
+ * return;
+ * }
+ * clear();
+ *
+ * let feature = earthquakes.features[eqFeatureIndex];
+ * let mag = feature.properties.mag;
+ * let rad = mag / 11 * ((width + height) / 2);
+ * fill(255, 0, 0, 100);
+ * ellipse(width / 2 + random(-2, 2), height / 2 + random(-2, 2), rad, rad);
+ *
+ * if (eqFeatureIndex >= earthquakes.features.length) {
+ * eqFeatureIndex = 0;
+ * } else {
+ * eqFeatureIndex += 1;
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
+ * // Create a p5.PrintWriter object.
+ * let myWriter = createWriter('xo.txt');
+ *
+ * // Add some lines to the print stream.
+ * myWriter.print('XOO');
+ * myWriter.print('OXO');
+ * myWriter.print('OOX');
+ *
+ * // Save the file and close the print stream.
+ * myWriter.close();
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
+ * // Create a p5.PrintWriter object.
+ * // Use the file format .csv.
+ * let myWriter = createWriter('mauna_loa_co2', 'csv');
+ *
+ * // Add some lines to the print stream.
+ * myWriter.print('date,ppm_co2');
+ * myWriter.print('1960-01-01,316.43');
+ * myWriter.print('1970-01-01,325.06');
+ * myWriter.print('1980-01-01,337.9');
+ * myWriter.print('1990-01-01,353.86');
+ * myWriter.print('2000-01-01,369.45');
+ * myWriter.print('2020-01-01,413.61');
+ *
+ * // Save the file and close the print stream.
+ * myWriter.close();
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * // Create a p5.PrintWriter object.
+ * let myWriter = createWriter('xo.txt');
+ *
+ * // Add some lines to the print stream.
+ * myWriter.print('XOO');
+ * myWriter.print('OXO');
+ * myWriter.print('OOX');
+ *
+ * // Save the file and close the print stream.
+ * myWriter.close();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * // Create a p5.PrintWriter object.
+ * let myWriter = createWriter('numbers.txt');
+ *
+ * // Add some data to the print stream.
+ * myWriter.write('1,2,3,');
+ * myWriter.write(['4', '5', '6']);
+ *
+ * // Save the file and close the print stream.
+ * myWriter.close();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * // Create a p5.PrintWriter object.
+ * let myWriter = createWriter('numbers.txt');
+ *
+ * // Add some data to the print stream.
+ * myWriter.print('1,2,3,');
+ * myWriter.print(['4', '5', '6']);
+ *
+ * // Save the file and close the print stream.
+ * myWriter.close();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * // Create a p5.PrintWriter object.
+ * let myWriter = createWriter('numbers.txt');
+ *
+ * // Add some data to the print stream.
+ * myWriter.print('Hello p5*js!');
+ *
+ * // Clear the print stream.
+ * myWriter.clear();
+ *
+ * // Save the file and close the print stream.
+ * myWriter.close();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * // Create a p5.PrintWriter object.
+ * let myWriter = createWriter('cat.txt');
+ *
+ * // Add some data to the print stream.
+ * // ASCII art courtesy Wikipedia:
+ * // https://en.wikipedia.org/wiki/ASCII_art
+ * myWriter.print(' (\\_/) ');
+ * myWriter.print("(='.'=)");
+ * myWriter.print('(")_(")');
+ *
+ * // Save the file and close the print stream.
+ * myWriter.close();
+ * }
+ *
+ * true
indicates that the
+ * output will be optimized for filesize,
+ * rather than readability.
+ *
+ * @example
+ *
+ * // Saves the canvas as an image
+ * cnv = createCanvas(300, 300);
+ * save(cnv, 'myCanvas.jpg');
+ *
+ * // Saves the canvas as an image by default
+ * save('myCanvas.jpg');
+ * describe('An example for saving a canvas as an image.');
+ *
+ * // Saves p5.Image as an image
+ * img = createImage(10, 10);
+ * save(img, 'myImage.png');
+ * describe('An example for saving a p5.Image element as an image.');
+ *
+ * // Saves p5.Renderer object as an image
+ * obj = createGraphics(100, 100);
+ * save(obj, 'myObject.png');
+ * describe('An example for saving a p5.Renderer element.');
+ *
+ * let myTable = new p5.Table();
+ * // Saves table as html file
+ * save(myTable, 'myTable.html');
+ *
+ * // Comma Separated Values
+ * save(myTable, 'myTable.csv');
+ *
+ * // Tab Separated Values
+ * save(myTable, 'myTable.tsv');
+ *
+ * describe(`An example showing how to save a table in formats of
+ * HTML, CSV and TSV.`);
+ *
+ * let myJSON = { a: 1, b: true };
+ *
+ * // Saves pretty JSON
+ * save(myJSON, 'my.json');
+ *
+ * // Optimizes JSON filesize
+ * save(myJSON, 'my.json', true);
+ *
+ * describe('An example for saving JSON to a txt file with some extra arguments.');
+ *
+ * // Saves array of strings to text file with line breaks after each item
+ * let arrayOfStrings = ['a', 'b'];
+ * save(arrayOfStrings, 'my.txt');
+ * describe(`An example for saving an array of strings to text file
+ * with line breaks.`);
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
+ * // Create an array.
+ * let data = [1, 2, 3];
+ *
+ * // Save the JSON file.
+ * saveJSON(data, 'numbers.json');
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
+ * // Create an object.
+ * let data = { x: mouseX, y: mouseY };
+ *
+ * // Save the JSON file.
+ * saveJSON(data, 'state.json');
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
+ * // Create an object.
+ * let data = { x: mouseX, y: mouseY };
+ *
+ * // Save the JSON file and reduce its size.
+ * saveJSON(data, 'state.json', true);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
+ * // Create an array.
+ * let data = ['0', '01', '011'];
+ *
+ * // Save the text file.
+ * saveStrings(data, 'data.txt');
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
+ * // Create an array.
+ * // ASCII art courtesy Wikipedia:
+ * // https://en.wikipedia.org/wiki/ASCII_art
+ * let data = [' (\\_/) ', "(='.'=)", '(")_(")'];
+ *
+ * // Save the text file.
+ * saveStrings(data, 'cat', 'txt');
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
+ * // Create an array.
+ * // +--+
+ * // / /|
+ * // +--+ +
+ * // | |/
+ * // +--+
+ * let data = [' +--+', ' / /|', '+--+ +', '| |/', '+--+'];
+ *
+ * // Save the text file.
+ * // Use CRLF for line endings.
+ * saveStrings(data, 'box', 'txt', true);
+ * }
+ * }
+ *
+ *
+ * let table;
+ *
+ * function setup() {
+ * table = new p5.Table();
+ *
+ * table.addColumn('id');
+ * table.addColumn('species');
+ * table.addColumn('name');
+ *
+ * let newRow = table.addRow();
+ * newRow.setNum('id', table.getRowCount() - 1);
+ * newRow.setString('species', 'Panthera leo');
+ * newRow.setString('name', 'Lion');
+ *
+ * // To save, un-comment next line then click 'run'
+ * // saveTable(table, 'new.csv');
+ *
+ * describe('no image displayed');
+ * }
+ *
+ * // Saves the following to a file called 'new.csv':
+ * // id,species,name
+ * // 0,Panthera leo,Lion
+ *
'.concat(e)); + pWriter.print(' | '); + } + pWriter.print('
'.concat(htmlEntry)); + pWriter.print(' | '); + } + pWriter.print('
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * //print the column names
+ * for (let c = 0; c < table.getColumnCount(); c++) {
+ * print('column ' + c + ' is named ' + table.columns[c]);
+ * }
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * //add a row
+ * let newRow = table.addRow();
+ * newRow.setString('id', table.getRowCount() - 1);
+ * newRow.setString('species', 'Canis Lupus');
+ * newRow.setString('name', 'Wolf');
+ *
+ * //print the results
+ * for (let r = 0; r < table.getRowCount(); r++)
+ * for (let c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ *
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * //remove the first row
+ * table.removeRow(0);
+ *
+ * //print the results
+ * for (let r = 0; r < table.getRowCount(); r++)
+ * for (let c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ *
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * let row = table.getRow(1);
+ * //print it column by column
+ * //note: a row is an object, not an array
+ * for (let c = 0; c < table.getColumnCount(); c++) {
+ * print(row.getString(c));
+ * }
+ *
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * let rows = table.getRows();
+ *
+ * //warning: rows is an array of objects
+ * for (let r = 0; r < rows.length; r++) {
+ * rows[r].set('name', 'Unicorn');
+ * }
+ *
+ * //print the results
+ * for (let r = 0; r < table.getRowCount(); r++)
+ * for (let c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ *
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * //find the animal named zebra
+ * let row = table.findRow('Zebra', 'name');
+ * //find the corresponding species
+ * print(row.getString('species'));
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * //add another goat
+ * let newRow = table.addRow();
+ * newRow.setString('id', table.getRowCount() - 1);
+ * newRow.setString('species', 'Scape Goat');
+ * newRow.setString('name', 'Goat');
+ *
+ * //find the rows containing animals named Goat
+ * let rows = table.findRows('Goat', 'name');
+ * print(rows.length + ' Goats found');
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * //Search using specified regex on a given column, return TableRow object
+ * let mammal = table.matchRow(new RegExp('ant'), 1);
+ * print(mammal.getString(1));
+ * //Output "Panthera pardus"
+ * }
+ *
+ *
+ * let table;
+ *
+ * function setup() {
+ * table = new p5.Table();
+ *
+ * table.addColumn('name');
+ * table.addColumn('type');
+ *
+ * let newRow = table.addRow();
+ * newRow.setString('name', 'Lion');
+ * newRow.setString('type', 'Mammal');
+ *
+ * newRow = table.addRow();
+ * newRow.setString('name', 'Snake');
+ * newRow.setString('type', 'Reptile');
+ *
+ * newRow = table.addRow();
+ * newRow.setString('name', 'Mosquito');
+ * newRow.setString('type', 'Insect');
+ *
+ * newRow = table.addRow();
+ * newRow.setString('name', 'Lizard');
+ * newRow.setString('type', 'Reptile');
+ *
+ * let rows = table.matchRows('R.*', 'type');
+ * for (let i = 0; i < rows.length; i++) {
+ * print(rows[i].getString('name') + ': ' + rows[i].getString('type'));
+ * }
+ * }
+ * // Sketch prints:
+ * // Snake: Reptile
+ * // Lizard: Reptile
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * //getColumn returns an array that can be printed directly
+ * print(table.getColumn('species'));
+ * //outputs ["Capra hircus", "Panthera pardus", "Equus zebra"]
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * table.clearRows();
+ * print(table.getRowCount() + ' total rows in table');
+ * print(table.getColumnCount() + ' total columns in table');
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * table.addColumn('carnivore');
+ * table.set(0, 'carnivore', 'no');
+ * table.set(1, 'carnivore', 'yes');
+ * table.set(2, 'carnivore', 'no');
+ *
+ * //print the results
+ * for (let r = 0; r < table.getRowCount(); r++)
+ * for (let c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ *
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // given the cvs file "blobs.csv" in /assets directory
+ * // ID, Name, Flavor, Shape, Color
+ * // Blob1, Blobby, Sweet, Blob, Pink
+ * // Blob2, Saddy, Savory, Blob, Blue
+ *
+ * let table;
+ *
+ * function preload() {
+ * table = loadTable('assets/blobs.csv');
+ * }
+ *
+ * function setup() {
+ * createCanvas(200, 100);
+ * textAlign(CENTER);
+ * background(255);
+ * }
+ *
+ * function draw() {
+ * let numOfColumn = table.getColumnCount();
+ * text('There are ' + numOfColumn + ' columns in the table.', 100, 50);
+ * }
+ *
+ *
+ * // given the cvs file "blobs.csv" in /assets directory
+ * //
+ * // ID, Name, Flavor, Shape, Color
+ * // Blob1, Blobby, Sweet, Blob, Pink
+ * // Blob2, Saddy, Savory, Blob, Blue
+ *
+ * let table;
+ *
+ * function preload() {
+ * table = loadTable('assets/blobs.csv');
+ * }
+ *
+ * function setup() {
+ * createCanvas(200, 100);
+ * textAlign(CENTER);
+ * background(255);
+ * }
+ *
+ * function draw() {
+ * text('There are ' + table.getRowCount() + ' rows in the table.', 100, 50);
+ * }
+ *
+ *
+ * function setup() {
+ * let table = new p5.Table();
+ *
+ * table.addColumn('name');
+ * table.addColumn('type');
+ *
+ * let newRow = table.addRow();
+ * newRow.setString('name', ' $Lion ,');
+ * newRow.setString('type', ',,,Mammal');
+ *
+ * newRow = table.addRow();
+ * newRow.setString('name', '$Snake ');
+ * newRow.setString('type', ',,,Reptile');
+ *
+ * table.removeTokens(',$ ');
+ * print(table.getArray());
+ * }
+ *
+ * // prints:
+ * // 0 "Lion" "Mamal"
+ * // 1 "Snake" "Reptile"
+ *
+ * function setup() {
+ * let table = new p5.Table();
+ *
+ * table.addColumn('name');
+ * table.addColumn('type');
+ *
+ * let newRow = table.addRow();
+ * newRow.setString('name', ' Lion ,');
+ * newRow.setString('type', ' Mammal ');
+ *
+ * newRow = table.addRow();
+ * newRow.setString('name', ' Snake ');
+ * newRow.setString('type', ' Reptile ');
+ *
+ * table.trim();
+ * print(table.getArray());
+ * }
+ *
+ * // prints:
+ * // 0 "Lion" "Mamal"
+ * // 1 "Snake" "Reptile"
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * table.removeColumn('id');
+ * print(table.getColumnCount());
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * table.set(0, 'species', 'Canis Lupus');
+ * table.set(0, 'name', 'Wolf');
+ *
+ * //print the results
+ * for (let r = 0; r < table.getRowCount(); r++)
+ * for (let c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ *
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * table.setNum(1, 'id', 1);
+ *
+ * print(table.getColumn(0));
+ * //["0", 1, "2"]
+ *
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv" in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * //add a row
+ * let newRow = table.addRow();
+ * newRow.setString('id', table.getRowCount() - 1);
+ * newRow.setString('species', 'Canis Lupus');
+ * newRow.setString('name', 'Wolf');
+ *
+ * print(table.getArray());
+ *
+ * describe('no image displayed');
+ * }
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * print(table.get(0, 1));
+ * //Capra hircus
+ * print(table.get(0, 'species'));
+ * //Capra hircus
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * print(table.getNum(1, 0) + 100);
+ * //id 1 + 100 = 101
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * // table is comma separated value "CSV"
+ * // and has specifiying header for column labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * print(table.getString(0, 0)); // 0
+ * print(table.getString(0, 1)); // Capra hircus
+ * print(table.getString(0, 2)); // Goat
+ * print(table.getString(1, 0)); // 1
+ * print(table.getString(1, 1)); // Panthera pardus
+ * print(table.getString(1, 2)); // Leopard
+ * print(table.getString(2, 0)); // 2
+ * print(table.getString(2, 1)); // Equus zebra
+ * print(table.getString(2, 2)); // Zebra
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * let tableObject = table.getObject();
+ *
+ * print(tableObject);
+ * //outputs an object
+ *
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leoperd
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * // table is comma separated value "CSV"
+ * // and has specifiying header for column labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * let tableArray = table.getArray();
+ * for (let i = 0; i < tableArray.length; i++) {
+ * print(tableArray[i]);
+ * }
+ * describe('no image displayed');
+ * }
+ *
+ *
+ * // Given the CSV file "mammals.csv" in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * let rows = table.getRows();
+ * for (let r = 0; r < rows.length; r++) {
+ * rows[r].set('name', 'Unicorn');
+ * }
+ *
+ * //print the results
+ * print(table.getArray());
+ *
+ * describe('no image displayed');
+ * }
+ *
+ * // Given the CSV file "mammals.csv" in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * let rows = table.getRows();
+ * for (let r = 0; r < rows.length; r++) {
+ * rows[r].setNum('id', r + 10);
+ * }
+ *
+ * print(table.getArray());
+ *
+ * describe('no image displayed');
+ * }
+ *
+ * // Given the CSV file "mammals.csv" in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * let rows = table.getRows();
+ * for (let r = 0; r < rows.length; r++) {
+ * let name = rows[r].getString('name');
+ * rows[r].setString('name', 'A ' + name + ' named George');
+ * }
+ *
+ * print(table.getArray());
+ *
+ * describe('no image displayed');
+ * }
+ *
+ * // Given the CSV file "mammals.csv" in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * let names = [];
+ * let rows = table.getRows();
+ * for (let r = 0; r < rows.length; r++) {
+ * names.push(rows[r].get('name'));
+ * }
+ *
+ * print(names);
+ *
+ * describe('no image displayed');
+ * }
+ *
+ * // Given the CSV file "mammals.csv" in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * let rows = table.getRows();
+ * let minId = Infinity;
+ * let maxId = -Infinity;
+ * for (let r = 0; r < rows.length; r++) {
+ * let id = rows[r].getNum('id');
+ * minId = min(minId, id);
+ * maxId = min(maxId, id);
+ * }
+ * print('minimum id = ' + minId + ', maximum id = ' + maxId);
+ * describe('no image displayed');
+ * }
+ *
+ * // Given the CSV file "mammals.csv" in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * let table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable('assets/mammals.csv', 'csv', 'header');
+ * }
+ *
+ * function setup() {
+ * let rows = table.getRows();
+ * let longest = '';
+ * for (let r = 0; r < rows.length; r++) {
+ * let species = rows[r].getString('species');
+ * if (longest.length < species.length) {
+ * longest = species;
+ * }
+ * }
+ *
+ * print('longest: ' + longest);
+ *
+ * describe('no image displayed');
+ * }
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get an array with all mammal tags.
+ * let mammals = myXML.getChildren('mammal');
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Iterate over the mammals array.
+ * for (let i = 0; i < mammals.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 25;
+ *
+ * // Get the mammal's common name.
+ * let name = mammals[i].getContent();
+ *
+ * // Display the mammal's name.
+ * text(name, 20, y);
+ * }
+ *
+ * describe(
+ * 'The words "Goat", "Leopard", and "Zebra" written on three separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get an array with all mammal elements.
+ * let mammals = myXML.getChildren('mammal');
+ *
+ * // Get the first mammal element.
+ * let firstMammal = mammals[0];
+ *
+ * // Get the parent element.
+ * let parent = firstMammal.getParent();
+ *
+ * // Get the parent element's name.
+ * let name = parent.getName();
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display the parent element's name.
+ * text(name, 50, 50);
+ *
+ * describe('The word "animals" written in black on a gray background.');
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get an array with all mammal elements.
+ * let mammals = myXML.getChildren('mammal');
+ *
+ * // Get the first mammal element.
+ * let firstMammal = mammals[0];
+ *
+ * // Get the mammal element's name.
+ * let name = firstMammal.getName();
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display the element's name.
+ * text(name, 50, 50);
+ *
+ * describe('The word "mammal" written in black on a gray background.');
+ * }
+ *
+
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the element's original name.
+ * let oldName = myXML.getName();
+ *
+ * // Set the element's name.
+ * myXML.setName('monsters');
+ *
+ * // Get the element's new name.
+ * let newName = myXML.getName();
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display the element's names.
+ * text(oldName, 50, 33);
+ * text(newName, 50, 67);
+ *
+ * describe(
+ * 'The words "animals" and "monsters" written on separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Check whether the element has child elements.
+ * let isParent = myXML.hasChildren();
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Style the text.
+ * if (isParent === true) {
+ * text('Parent', 50, 50);
+ * } else {
+ * text('Not Parent', 50, 50);
+ * }
+ *
+ * describe('The word "Parent" written in black on a gray background.');
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the names of the element's children as an array.
+ * let children = myXML.listChildren();
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Iterate over the array.
+ * for (let i = 0; i < children.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 25;
+ *
+ * // Display the child element's name.
+ * text(children[i], 10, y);
+ * }
+ *
+ * describe(
+ * 'The words "mammal", "mammal", "mammal", and "reptile" written on separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get an array of the child elements.
+ * let children = myXML.getChildren();
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Iterate over the array.
+ * for (let i = 0; i < children.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 20;
+ *
+ * // Get the child element's content.
+ * let content = children[i].getContent();
+ *
+ * // Display the child element's content.
+ * text(content, 10, y);
+ * }
+ *
+ * describe(
+ * 'The words "Goat", "Leopard", "Zebra", and "Turtle" written on separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get an array of the child elements
+ * // that are mammals.
+ * let children = myXML.getChildren('mammal');
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Iterate over the array.
+ * for (let i = 0; i < children.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 20;
+ *
+ * // Get the child element's content.
+ * let content = children[i].getContent();
+ *
+ * // Display the child element's content.
+ * text(content, 10, y);
+ * }
+ *
+ * describe(
+ * 'The words "Goat", "Leopard", and "Zebra" written on separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the first child element that is a mammal.
+ * let goat = myXML.getChild('mammal');
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Get the child element's content.
+ * let content = goat.getContent();
+ *
+ * // Display the child element's content.
+ * text(content, 50, 50);
+ *
+ * describe('The word "Goat" written in black on a gray background.');
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the child element at index 1.
+ * let leopard = myXML.getChild(1);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Get the child element's content.
+ * let content = leopard.getContent();
+ *
+ * // Display the child element's content.
+ * text(content, 50, 50);
+ *
+ * describe('The word "Leopard" written in black on a gray background.');
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a new p5.XML object.
+ * let newAnimal = new p5.XML();
+ *
+ * // Set its properties.
+ * newAnimal.setName('hydrozoa');
+ * newAnimal.setAttribute('id', 4);
+ * newAnimal.setAttribute('species', 'Physalia physalis');
+ * newAnimal.setContent('Bluebottle');
+ *
+ * // Add the child element.
+ * myXML.addChild(newAnimal);
+ *
+ * // Get the first child element that is a hydrozoa.
+ * let blueBottle = myXML.getChild('hydrozoa');
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Get the child element's content.
+ * let content = blueBottle.getContent();
+ *
+ * // Display the child element's content.
+ * text(content, 50, 50);
+ *
+ * describe('The word "Bluebottle" written in black on a gray background.');
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Remove the first mammal element.
+ * myXML.removeChild('mammal');
+ *
+ * // Get an array of child elements.
+ * let children = myXML.getChildren();
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Iterate over the array.
+ * for (let i = 0; i < children.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 25;
+ *
+ * // Get the child element's content.
+ * let content = children[i].getContent();
+ *
+ * // Display the child element's content.
+ * text(content, 10, y);
+ * }
+ *
+ * describe(
+ * 'The words "Leopard", "Zebra", and "Turtle" written on separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Remove the element at index 2.
+ * myXML.removeChild(2);
+ *
+ * // Get an array of child elements.
+ * let children = myXML.getChildren();
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Iterate over the array.
+ * for (let i = 0; i < children.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 25;
+ *
+ * // Get the child element's content.
+ * let content = children[i].getContent();
+ *
+ * // Display the child element's content.
+ * text(content, 10, y);
+ * }
+ *
+ * describe(
+ * 'The words "Goat", "Leopard", and "Turtle" written on separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the first child element.
+ * let first = myXML.getChild(0);
+ *
+ * // Get the number of attributes.
+ * let numAttributes = first.getAttributeCount();
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display the number of attributes.
+ * text(numAttributes, 50, 50);
+ *
+ * describe('The number "2" written in black on a gray background.');
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the first child element.
+ * let first = myXML.getChild(0);
+ *
+ * // Get the number of attributes.
+ * let attributes = first.listAttributes();
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display the element's attributes.
+ * text(attributes, 50, 50);
+ *
+ * describe('The text "id,species" written in black on a gray background.');
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the first mammal child element.
+ * let mammal = myXML.getChild('mammal');
+ *
+ * // Check whether the element has an
+ * // species attribute.
+ * let hasSpecies = mammal.hasAttribute('species');
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display whether the element has a species attribute.
+ * if (hasSpecies === true) {
+ * text('Species', 50, 50);
+ * } else {
+ * text('No species', 50, 50);
+ * }
+ *
+ * describe('The text "Species" written in black on a gray background.');
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the first reptile child element.
+ * let reptile = myXML.getChild('reptile');
+ *
+ * // Get the reptile's content.
+ * let content = reptile.getContent();
+ *
+ * // Get the reptile's ID.
+ * let id = reptile.getNum('id');
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display the ID attribute.
+ * text(`${content} is ${id + 1}th`, 5, 50, 90);
+ *
+ * describe(`The text "${content} is ${id + 1}th" written in black on a gray background.`);
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the first reptile child element.
+ * let reptile = myXML.getChild('reptile');
+ *
+ * // Get the reptile's content.
+ * let content = reptile.getContent();
+ *
+ * // Get the reptile's size.
+ * let weight = reptile.getNum('weight', 135);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display the ID attribute.
+ * text(`${content} is ${weight}kg`, 5, 50, 90);
+ *
+ * describe(
+ * `The text "${content} is ${weight}kg" written in black on a gray background.`
+ * );
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the first reptile child element.
+ * let reptile = myXML.getChild('reptile');
+ *
+ * // Get the reptile's content.
+ * let content = reptile.getContent();
+ *
+ * // Get the reptile's species.
+ * let species = reptile.getString('species');
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display the species attribute.
+ * text(`${content}: ${species}`, 5, 50, 90);
+ *
+ * describe(`The text "${content}: ${species}" written in black on a gray background.`);
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the first reptile child element.
+ * let reptile = myXML.getChild('reptile');
+ *
+ * // Get the reptile's content.
+ * let content = reptile.getContent();
+ *
+ * // Get the reptile's color.
+ * let attribute = reptile.getString('color', 'green');
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ * fill(attribute);
+ *
+ * // Display the element's content.
+ * text(content, 50, 50);
+ *
+ * describe(`The text "${content}" written in green on a gray background.`);
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the first reptile child element.
+ * let reptile = myXML.getChild('reptile');
+ *
+ * // Set the reptile's color.
+ * reptile.setAttribute('color', 'green');
+ *
+ * // Get the reptile's content.
+ * let content = reptile.getContent();
+ *
+ * // Get the reptile's color.
+ * let attribute = reptile.getString('color');
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display the element's content.
+ * text(`${content} is ${attribute}`, 5, 50, 90);
+ *
+ * describe(
+ * `The text "${content} is ${attribute}" written in green on a gray background.`
+ * );
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the first reptile child element.
+ * let reptile = myXML.getChild('reptile');
+ *
+ * // Get the reptile's content.
+ * let content = reptile.getContent();
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display the element's content.
+ * text(content, 5, 50, 90);
+ *
+ * describe(`The text "${content}" written in green on a gray background.`);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.XML object.
+ * let blankSpace = new p5.XML();
+ *
+ * // Get the element's content and use a default value.
+ * let content = blankSpace.getContent('Your name');
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display the element's content.
+ * text(content, 5, 50, 90);
+ *
+ * describe(`The text "${content}" written in green on a gray background.`);
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the first reptile child element.
+ * let reptile = myXML.getChild('reptile');
+ *
+ * // Get the reptile's original content.
+ * let oldContent = reptile.getContent();
+ *
+ * // Set the reptile's content.
+ * reptile.setContent('Loggerhead');
+ *
+ * // Get the reptile's new content.
+ * let newContent = reptile.getContent();
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(14);
+ *
+ * // Display the element's old and new content.
+ * text(`${oldContent}: ${newContent}`, 5, 50, 90);
+ *
+ * describe(
+ * `The text "${oldContent}: ${newContent}" written in green on a gray background.`
+ * );
+ * }
+ *
+ *
+ * let myXML;
+ *
+ * // Load the XML and create a p5.XML object.
+ * function preload() {
+ * myXML = loadXML('assets/animals.xml');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Display instructions.
+ * text('Double-click to save', 5, 50, 90);
+ *
+ * describe('The text "Double-click to save" written in black on a gray background.');
+ * }
+ *
+ * // Save the file when the user double-clicks.
+ * function doubleClicked() {
+ * // Create a p5.PrintWriter object.
+ * // Use the file format .xml.
+ * let myWriter = createWriter('animals', 'xml');
+ *
+ * // Serialize the XML data to a string.
+ * let data = myXML.serialize();
+ *
+ * // Write the data to the print stream.
+ * myWriter.write(data);
+ *
+ * // Save the file and close the print stream.
+ * myWriter.close();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A gray square with a vertical black line that divides it in half. A white rectangle gets taller when the user moves the mouse away from the line.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Divide the canvas.
+ * line(50, 0, 50, 100);
+ *
+ * // Calculate the mouse's distance from the middle.
+ * let h = abs(mouseX - 50);
+ *
+ * // Draw a rectangle based on the mouse's distance
+ * // from the middle.
+ * rect(0, 100 - h, 100, h);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use RGB color with values from 0 to 1.
+ * colorMode(RGB, 1);
+ *
+ * noStroke();
+ *
+ * // Draw the left rectangle.
+ * let r = 0.3;
+ * fill(r, 0, 0);
+ * rect(0, 0, 50, 100);
+ *
+ * // Round r up to 1.
+ * r = ceil(r);
+ *
+ * // Draw the right rectangle.
+ * fill(r, 0, 0);
+ * rect(50, 0, 50, 100);
+ *
+ * describe('Two rectangles. The one on the left is dark red and the one on the right is bright red.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A black dot drawn on a gray square follows the mouse from left to right. Its movement is constrained to the middle third of the square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * let x = constrain(mouseX, 33, 67);
+ * let y = 50;
+ *
+ * strokeWeight(5);
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Two vertical lines. Two circles move horizontally with the mouse. One circle stops at the vertical lines.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Set boundaries and draw them.
+ * let leftWall = 25;
+ * let rightWall = 75;
+ * line(leftWall, 0, leftWall, 100);
+ * line(rightWall, 0, rightWall, 100);
+ *
+ * // Draw a circle that follows the mouse freely.
+ * fill(255);
+ * circle(mouseX, 33, 9);
+ *
+ * // Draw a circle that's constrained.
+ * let xc = constrain(mouseX, leftWall, rightWall);
+ * fill(0);
+ * circle(xc, 67, 9);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the coordinates.
+ * let x1 = 10;
+ * let y1 = 50;
+ * let x2 = 90;
+ * let y2 = 50;
+ *
+ * // Draw the points and a line connecting them.
+ * line(x1, y1, x2, y2);
+ * strokeWeight(5);
+ * point(x1, y1);
+ * point(x2, y2);
+ *
+ * // Calculate the distance.
+ * let d = dist(x1, y1, x2, y2);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the distance.
+ * text(d, 43, 40);
+ *
+ * describe('Two dots connected by a horizontal line. The number 80 is written above the center of the line.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Top-left.
+ * let d = exp(1);
+ * circle(10, 10, d);
+ *
+ * // Left-center.
+ * d = exp(2);
+ * circle(20, 20, d);
+ *
+ * // Right-center.
+ * d = exp(3);
+ * circle(40, 40, d);
+ *
+ * // Bottom-right.
+ * d = exp(4);
+ * circle(80, 80, d);
+ *
+ * describe('A series of circles that grow exponentially from top left to bottom right.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('A series of black dots that grow exponentially from left to right.');
+ * }
+ *
+ * function draw() {
+ * // Invert the y-axis.
+ * scale(1, -1);
+ * translate(0, -100);
+ *
+ * // Calculate the coordinates.
+ * let x = frameCount;
+ * let y = 0.005 * exp(x * 0.1);
+ *
+ * // Draw a point.
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use RGB color with values from 0 to 1.
+ * colorMode(RGB, 1);
+ *
+ * noStroke();
+ *
+ * // Draw the left rectangle.
+ * let r = 0.8;
+ * fill(r, 0, 0);
+ * rect(0, 0, 50, 100);
+ *
+ * // Round r down to 0.
+ * r = floor(r);
+ *
+ * // Draw the right rectangle.
+ * fill(r, 0, 0);
+ * rect(50, 0, 50, 100);
+ *
+ * describe('Two rectangles. The one on the left is bright red and the one on the right is black.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Declare variables for coordinates.
+ * let a = 20;
+ * let b = 80;
+ * let c = lerp(a, b, 0.2);
+ * let d = lerp(a, b, 0.5);
+ * let e = lerp(a, b, 0.8);
+ *
+ * strokeWeight(5);
+ *
+ * // Draw the original points in black.
+ * stroke(0);
+ * point(a, 50);
+ * point(b, 50);
+ *
+ * // Draw the lerped points in gray.
+ * stroke(100);
+ * point(c, 50);
+ * point(d, 50);
+ * point(e, 50);
+ *
+ * describe('Five points in a horizontal line. The outer points are black and the inner points are gray.');
+ * }
+ *
+ *
+ * let x = 50;
+ * let y = 50;
+ * let targetX = 50;
+ * let targetY = 50;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('A white circle at the center of a gray canvas. The circle moves to where the user clicks, then moves smoothly back to the center.');
+ * }
+ *
+ * function draw() {
+ * background(220);
+ *
+ * // Move x and y toward the target.
+ * x = lerp(x, targetX, 0.05);
+ * y = lerp(y, targetY, 0.05);
+ *
+ * // Draw the circle.
+ * circle(x, y, 20);
+ * }
+ *
+ * // Set x and y when the user clicks the mouse.
+ * function mouseClicked() {
+ * x = mouseX;
+ * y = mouseY;
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Top-left.
+ * let d = log(50);
+ * circle(33, 33, d);
+ *
+ * // Bottom-right.
+ * d = log(500000000);
+ * circle(67, 67, d);
+ *
+ * describe('Two white circles. The circle at the top-left is small. The circle at the bottom-right is about five times larger.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('A series of black dots that get higher slowly from left to right.');
+ * }
+ *
+ * function draw() {
+ * // Invert the y-axis.
+ * scale(1, -1);
+ * translate(0, -100);
+ *
+ * // Calculate coordinates.
+ * let x = frameCount;
+ * let y = 15 * log(x);
+ *
+ * // Draw a point.
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the vector's components.
+ * let x = 30;
+ * let y = 40;
+ *
+ * // Calculate the magnitude.
+ * let m = mag(x, y);
+ *
+ * // Style the text.
+ * textSize(16);
+ *
+ * // Display the vector and its magnitude.
+ * line(0, 0, x, y);
+ * text(m, x, y);
+ *
+ * describe('A diagonal line is drawn from the top left of the canvas. The number 50 is written at the end of the line.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Two horizontal lines. The top line grows horizontally as the mouse moves to the right. The bottom line also grows horizontally but is scaled to stay on the left half of the canvas.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the top line.
+ * line(0, 25, mouseX, 25);
+ *
+ * // Remap mouseX from [0, 100] to [0, 50].
+ * let x = map(mouseX, 0, 100, 0, 50);
+ *
+ * // Draw the bottom line.
+ * line(0, 75, x, 75);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A circle changes color from black to white as the mouse moves from left to right.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Remap mouseX from [0, 100] to [0, 255]
+ * let c = map(mouseX, 0, 100, 0, 255);
+ *
+ * // Style the circle.
+ * fill(c);
+ *
+ * // Draw the circle.
+ * circle(50, 50, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Calculate the maximum of 10, 5, and 20.
+ * let m = max(10, 5, 20);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the max.
+ * text(m, 50, 50);
+ *
+ * describe('The number 20 written in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of numbers.
+ * let numbers = [10, 5, 20];
+ *
+ * // Calculate the maximum of the array.
+ * let m = max(numbers);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the max.
+ * text(m, 50, 50);
+ *
+ * describe('The number 20 written in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Calculate the minimum of 10, 5, and 20.
+ * let m = min(10, 5, 20);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the min.
+ * text(m, 50, 50);
+ *
+ * describe('The number 5 written in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of numbers.
+ * let numbers = [10, 5, 20];
+ *
+ * // Calculate the minimum of the array.
+ * let m = min(numbers);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the min.
+ * text(m, 50, 50);
+ *
+ * describe('The number 5 written in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use RGB color with values from 0 to 1.
+ * colorMode(RGB, 1);
+ *
+ * describe('A square changes color from black to red as the mouse moves from left to right.');
+ * }
+ *
+ * function draw() {
+ * // Calculate the redValue.
+ * let redValue = norm(mouseX, 0, 100);
+ *
+ * // Paint the background.
+ * background(redValue, 0, 0);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the base of the exponent.
+ * let base = 3;
+ *
+ * // Top-left.
+ * let d = pow(base, 1);
+ * circle(10, 10, d);
+ *
+ * // Left-center.
+ * d = pow(base, 2);
+ * circle(20, 20, d);
+ *
+ * // Right-center.
+ * d = pow(base, 3);
+ * circle(40, 40, d);
+ *
+ * // Bottom-right.
+ * d = pow(base, 4);
+ * circle(80, 80, d);
+ *
+ * describe('A series of circles that grow exponentially from top left to bottom right.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Round a number.
+ * let x = round(4.2);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the rounded number.
+ * text(x, 50, 50);
+ *
+ * describe('The number 4 written in middle of the canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Round a number to 2 decimal places.
+ * let x = round(12.782383, 2);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the rounded number.
+ * text(x, 50, 50);
+ *
+ * describe('The number 12.78 written in middle of canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Top-left.
+ * let d = sq(3);
+ * circle(33, 33, d);
+ *
+ * // Bottom-right.
+ * d = sq(6);
+ * circle(67, 67, d);
+ *
+ * describe('Two white circles. The circle at the top-left is small. The circle at the bottom-right is four times larger.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('A series of black dots that get higher quickly from left to right.');
+ * }
+ *
+ * function draw() {
+ * // Invert the y-axis.
+ * scale(1, -1);
+ * translate(0, -100);
+ *
+ * // Calculate the coordinates.
+ * let x = frameCount;
+ * let y = 0.01 * sq(x);
+ *
+ * // Draw the point.
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Top-left.
+ * let d = sqrt(16);
+ * circle(33, 33, d);
+ *
+ * // Bottom-right.
+ * d = sqrt(1600);
+ * circle(67, 67, d);
+ *
+ * describe('Two white circles. The circle at the top-left is small. The circle at the bottom-right is ten times larger.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('A series of black dots that get higher slowly from left to right.');
+ * }
+ *
+ * function draw() {
+ * // Invert the y-axis.
+ * scale(1, -1);
+ * translate(0, -100);
+ *
+ * // Calculate the coordinates.
+ * let x = frameCount;
+ * let y = 5 * sqrt(x);
+ *
+ * // Draw the point.
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Original number.
+ * let n = 56.78;
+ * text(n, 50, 33);
+ *
+ * // Fractional part.
+ * let f = fract(n);
+ * text(f, 50, 67);
+ *
+ * describe('The number 56.78 written above the number 0.78.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create p5.Vector objects.
+ * let p1 = createVector(25, 25);
+ * let p2 = createVector(50, 50);
+ * let p3 = createVector(75, 75);
+ *
+ * // Draw the dots.
+ * strokeWeight(5);
+ * point(p1);
+ * point(p2);
+ * point(p3);
+ *
+ * describe('Three black dots form a diagonal line from top left to bottom right.');
+ * }
+ *
+ *
+ * let pos;
+ * let vel;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create p5.Vector objects.
+ * pos = createVector(50, 100);
+ * vel = createVector(0, -1);
+ *
+ * describe('A black dot moves from bottom to top on a gray square. The dot reappears at the bottom when it reaches the top.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Add velocity to position.
+ * pos.add(vel);
+ *
+ * // If the dot reaches the top of the canvas,
+ * // restart from the bottom.
+ * if (pos.y < 0) {
+ * pos.y = 100;
+ * }
+ *
+ * // Draw the dot.
+ * strokeWeight(5);
+ * point(pos);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A black dot moves randomly on a gray square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the coordinates.
+ * let x = 100 * noise(0.005 * frameCount);
+ * let y = 100 * noise(0.005 * frameCount + 10000);
+ *
+ * // Draw the point.
+ * strokeWeight(5);
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A black dot moves randomly on a gray square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Set the noise level and scale.
+ * let noiseLevel = 100;
+ * let noiseScale = 0.005;
+ *
+ * // Scale the input coordinate.
+ * let nt = noiseScale * frameCount;
+ *
+ * // Compute the noise values.
+ * let x = noiseLevel * noise(nt);
+ * let y = noiseLevel * noise(nt + 10000);
+ *
+ * // Draw the point.
+ * strokeWeight(5);
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A hilly terrain drawn in gray against a black sky.');
+ * }
+ *
+ * function draw() {
+ * // Set the noise level and scale.
+ * let noiseLevel = 100;
+ * let noiseScale = 0.02;
+ *
+ * // Scale the input coordinate.
+ * let x = frameCount;
+ * let nx = noiseScale * x;
+ *
+ * // Compute the noise value.
+ * let y = noiseLevel * noise(nx);
+ *
+ * // Draw the line.
+ * line(x, 0, x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A calm sea drawn in gray against a black sky.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Set the noise level and scale.
+ * let noiseLevel = 100;
+ * let noiseScale = 0.002;
+ *
+ * // Iterate from left to right.
+ * for (let x = 0; x < 100; x += 1) {
+ * // Scale the input coordinates.
+ * let nx = noiseScale * x;
+ * let nt = noiseScale * frameCount;
+ *
+ * // Compute the noise value.
+ * let y = noiseLevel * noise(nx, nt);
+ *
+ * // Draw the line.
+ * line(x, 0, x, y);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the noise level and scale.
+ * let noiseLevel = 255;
+ * let noiseScale = 0.01;
+ *
+ * // Iterate from top to bottom.
+ * for (let y = 0; y < 100; y += 1) {
+ * // Iterate from left to right.
+ * for (let x = 0; x < 100; x += 1) {
+ * // Scale the input coordinates.
+ * let nx = noiseScale * x;
+ * let ny = noiseScale * y;
+ *
+ * // Compute the noise value.
+ * let c = noiseLevel * noise(nx, ny);
+ *
+ * // Draw the point.
+ * stroke(c);
+ * point(x, y);
+ * }
+ * }
+ *
+ * describe('A gray cloudy pattern.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A gray cloudy pattern that changes.');
+ * }
+ *
+ * function draw() {
+ * // Set the noise level and scale.
+ * let noiseLevel = 255;
+ * let noiseScale = 0.009;
+ *
+ * // Iterate from top to bottom.
+ * for (let y = 0; y < 100; y += 1) {
+ * // Iterate from left to right.
+ * for (let x = 0; x < width; x += 1) {
+ * // Scale the input coordinates.
+ * let nx = noiseScale * x;
+ * let ny = noiseScale * y;
+ * let nt = noiseScale * frameCount;
+ *
+ * // Compute the noise value.
+ * let c = noiseLevel * noise(nx, ny, nt);
+ *
+ * // Draw the point.
+ * stroke(c);
+ * point(x, y);
+ * }
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Set the noise level and scale.
+ * let noiseLevel = 255;
+ * let noiseScale = 0.02;
+ *
+ * // Iterate from top to bottom.
+ * for (let y = 0; y < 100; y += 1) {
+ * // Iterate from left to right.
+ * for (let x = 0; x < 50; x += 1) {
+ * // Scale the input coordinates.
+ * let nx = noiseScale * x;
+ * let ny = noiseScale * y;
+ *
+ * // Compute the noise value with six octaves
+ * // and a low falloff factor.
+ * noiseDetail(6, 0.25);
+ * let c = noiseLevel * noise(nx, ny);
+ *
+ * // Draw the left side.
+ * stroke(c);
+ * point(x, y);
+ *
+ * // Compute the noise value with four octaves
+ * // and a high falloff factor.
+ * noiseDetail(4, 0.5);
+ * c = noiseLevel * noise(nx, ny);
+ *
+ * // Draw the right side.
+ * stroke(c);
+ * point(x + 50, y);
+ * }
+ * }
+ *
+ * describe('Two gray cloudy patterns. The pattern on the right is cloudier than the pattern on the left.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Set the noise seed for consistent results.
+ * noiseSeed(99);
+ *
+ * describe('A black rectangle that grows randomly, first to the right and then to the left.');
+ * }
+ *
+ * function draw() {
+ * // Set the noise level and scale.
+ * let noiseLevel = 100;
+ * let noiseScale = 0.005;
+ *
+ * // Scale the input coordinate.
+ * let nt = noiseScale * frameCount;
+ *
+ * // Compute the noise value.
+ * let x = noiseLevel * noise(nt);
+ *
+ * // Draw the line.
+ * line(x, 0, x, height);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create p5.Vector objects.
+ * let p1 = createVector(25, 25);
+ * let p2 = createVector(75, 75);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Draw the first point using a p5.Vector.
+ * point(p1);
+ *
+ * // Draw the second point using a p5.Vector's components.
+ * point(p2.x, p2.y);
+ *
+ * describe('Two black dots on a gray square, one at the top left and the other at the bottom right.');
+ * }
+ *
+ *
+ * let pos;
+ * let vel;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create p5.Vector objects.
+ * pos = createVector(50, 100);
+ * vel = createVector(0, -1);
+ *
+ * describe('A black dot moves from bottom to top on a gray square. The dot reappears at the bottom when it reaches the top.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Add velocity to position.
+ * pos.add(vel);
+ *
+ * // If the dot reaches the top of the canvas,
+ * // restart from the bottom.
+ * if (pos.y < 0) {
+ * pos.y = 100;
+ * }
+ *
+ * // Draw the dot.
+ * strokeWeight(5);
+ * point(pos);
+ * }
+ *
+ *
+ * function setup() {
+ * let v = createVector(20, 30);
+ *
+ * // Prints 'p5.Vector Object : [20, 30, 0]'.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Top left.
+ * let pos = createVector(25, 25);
+ * point(pos);
+ *
+ * // Top right.
+ * // set() with numbers.
+ * pos.set(75, 25);
+ * point(pos);
+ *
+ * // Bottom right.
+ * // set() with a p5.Vector.
+ * let p2 = createVector(75, 75);
+ * pos.set(p2);
+ * point(pos);
+ *
+ * // Bottom left.
+ * // set() with an array.
+ * let arr = [25, 75];
+ * pos.set(arr);
+ * point(pos);
+ *
+ * describe('Four black dots arranged in a square on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100 ,100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Vector object.
+ * let pos = createVector(50, 50);
+ *
+ * // Make a copy.
+ * let pc = pos.copy();
+ *
+ * // Draw the point.
+ * strokeWeight(5);
+ * point(pc);
+ *
+ * describe('A black point drawn in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Top left.
+ * let pos = createVector(25, 25);
+ * point(pos);
+ *
+ * // Top right.
+ * // Add numbers.
+ * pos.add(50, 0);
+ * point(pos);
+ *
+ * // Bottom right.
+ * // Add a p5.Vector.
+ * let p2 = createVector(0, 50);
+ * pos.add(p2);
+ * point(pos);
+ *
+ * // Bottom left.
+ * // Add an array.
+ * let arr = [-50, 0];
+ * pos.add(arr);
+ * point(pos);
+ *
+ * describe('Four black dots arranged in a square on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Top left.
+ * let p1 = createVector(25, 25);
+ *
+ * // Center.
+ * let p2 = createVector(50, 50);
+ *
+ * // Bottom right.
+ * // Add p1 and p2.
+ * let p3 = p5.Vector.add(p1, p2);
+ *
+ * // Draw the points.
+ * strokeWeight(5);
+ * point(p1);
+ * point(p2);
+ * point(p3);
+ *
+ * describe('Three black dots in a diagonal line from top left to bottom right.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Three arrows drawn on a gray square. A red arrow extends from the top left corner to the center. A blue arrow extends from the tip of the red arrow. A purple arrow extends from the origin to the tip of the blue arrow.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * let origin = createVector(0, 0);
+ *
+ * // Draw the red arrow.
+ * let v1 = createVector(50, 50);
+ * drawArrow(origin, v1, 'red');
+ *
+ * // Draw the blue arrow.
+ * let v2 = createVector(-30, 20);
+ * drawArrow(v1, v2, 'blue');
+ *
+ * // Purple arrow.
+ * let v3 = p5.Vector.add(v1, v2);
+ * drawArrow(origin, v3, 'purple');
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = createVector(3, 4, 5);
+ *
+ * // Divide numbers.
+ * v.rem(2);
+ *
+ * // Prints 'p5.Vector Object : [1, 0, 1]'.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = createVector(3, 4, 5);
+ *
+ * // Divide numbers.
+ * v.rem(2, 3);
+ *
+ * // Prints 'p5.Vector Object : [1, 1, 5]'.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = createVector(3, 4, 5);
+ *
+ * // Divide numbers.
+ * v.rem(2, 3, 4);
+ *
+ * // Prints 'p5.Vector Object : [1, 1, 1]'.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create p5.Vector objects.
+ * let v1 = createVector(3, 4, 5);
+ * let v2 = createVector(2, 3, 4);
+ *
+ * // Divide a p5.Vector.
+ * v1.rem(v2);
+ *
+ * // Prints 'p5.Vector Object : [1, 1, 1]'.
+ * print(v1.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = createVector(3, 4, 5);
+ *
+ * // Divide an array.
+ * let arr = [2, 3, 4];
+ * v.rem(arr);
+ *
+ * // Prints 'p5.Vector Object : [1, 1, 1]'.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create p5.Vector objects.
+ * let v1 = createVector(3, 4, 5);
+ * let v2 = createVector(2, 3, 4);
+ *
+ * // Divide without modifying the original vectors.
+ * let v3 = p5.Vector.rem(v1, v2);
+ *
+ * // Prints 'p5.Vector Object : [1, 1, 1]'.
+ * print(v3.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Bottom right.
+ * let pos = createVector(75, 75);
+ * point(pos);
+ *
+ * // Top right.
+ * // Subtract numbers.
+ * pos.sub(0, 50);
+ * point(pos);
+ *
+ * // Top left.
+ * // Subtract a p5.Vector.
+ * let p2 = createVector(50, 0);
+ * pos.sub(p2);
+ * point(pos);
+ *
+ * // Bottom left.
+ * // Subtract an array.
+ * let arr = [0, -50];
+ * pos.sub(arr);
+ * point(pos);
+ *
+ * describe('Four black dots arranged in a square on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create p5.Vector objects.
+ * let p1 = createVector(75, 75);
+ * let p2 = createVector(50, 50);
+ *
+ * // Subtract with modifying the original vectors.
+ * let p3 = p5.Vector.sub(p1, p2);
+ *
+ * // Draw the points.
+ * strokeWeight(5);
+ * point(p1);
+ * point(p2);
+ * point(p3);
+ *
+ * describe('Three black dots in a diagonal line from top left to bottom right.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Three arrows drawn on a gray square. A red and a blue arrow extend from the top left. A purple arrow extends from the tip of the red arrow to the tip of the blue arrow.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * let origin = createVector(0, 0);
+ *
+ * // Draw the red arrow.
+ * let v1 = createVector(50, 50);
+ * drawArrow(origin, v1, 'red');
+ *
+ * // Draw the blue arrow.
+ * let v2 = createVector(20, 70);
+ * drawArrow(origin, v2, 'blue');
+ *
+ * // Purple arrow.
+ * let v3 = p5.Vector.sub(v2, v1);
+ * drawArrow(v1, v3, 'purple');
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Top-left.
+ * let p = createVector(25, 25);
+ * point(p);
+ *
+ * // Center.
+ * // Multiply all components by 2.
+ * p.mult(2);
+ * point(p);
+ *
+ * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the center.');
+ * }
+ *
+ *
+ * function setup() {
+ * strokeWeight(5);
+ *
+ * // Top-left.
+ * let p = createVector(25, 25);
+ * point(p);
+ *
+ * // Bottom-right.
+ * // Multiply p.x * 2 and p.y * 3
+ * p.mult(2, 3);
+ * point(p);
+ *
+ * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Top-left.
+ * let p = createVector(25, 25);
+ * point(p);
+ *
+ * // Bottom-right.
+ * // Multiply p.x * 2 and p.y * 3
+ * let arr = [2, 3];
+ * p.mult(arr);
+ * point(p);
+ *
+ * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Top-left.
+ * let p = createVector(25, 25);
+ * point(p);
+ *
+ * // Bottom-right.
+ * // Multiply p.x * p2.x and p.y * p2.y
+ * let p2 = createVector(2, 3);
+ * p.mult(p2);
+ * point(p);
+ *
+ * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Top-left.
+ * let p = createVector(25, 25);
+ * point(p);
+ *
+ * // Bottom-right.
+ * // Create a new p5.Vector with
+ * // p3.x = p.x * p2.x
+ * // p3.y = p.y * p2.y
+ * let p2 = createVector(2, 3);
+ * let p3 = p5.Vector.mult(p, p2);
+ * point(p3);
+ *
+ * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Two arrows extending from the top left corner. The blue arrow is twice the length of the red arrow.');
+ * }
+ * function draw() {
+ * background(200);
+ *
+ * let origin = createVector(0, 0);
+ *
+ * // Draw the red arrow.
+ * let v1 = createVector(25, 25);
+ * drawArrow(origin, v1, 'red');
+ *
+ * // Draw the blue arrow.
+ * let v2 = p5.Vector.mult(v1, 2);
+ * drawArrow(origin, v2, 'blue');
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Center.
+ * let p = createVector(50, 50);
+ * point(p);
+ *
+ * // Top-left.
+ * // Divide p.x / 2 and p.y / 2
+ * p.div(2);
+ * point(p);
+ *
+ * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Bottom-right.
+ * let p = createVector(50, 75);
+ * point(p);
+ *
+ * // Top-left.
+ * // Divide p.x / 2 and p.y / 3
+ * p.div(2, 3);
+ * point(p);
+ *
+ * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Bottom-right.
+ * let p = createVector(50, 75);
+ * point(p);
+ *
+ * // Top-left.
+ * // Divide p.x / 2 and p.y / 3
+ * let arr = [2, 3];
+ * p.div(arr);
+ * point(p);
+ *
+ * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Bottom-right.
+ * let p = createVector(50, 75);
+ * point(p);
+ *
+ * // Top-left.
+ * // Divide p.x / 2 and p.y / 3
+ * let p2 = createVector(2, 3);
+ * p.div(p2);
+ * point(p);
+ *
+ * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the points.
+ * strokeWeight(5);
+ *
+ * // Bottom-right.
+ * let p = createVector(50, 75);
+ * point(p);
+ *
+ * // Top-left.
+ * // Create a new p5.Vector with
+ * // p3.x = p.x / p2.x
+ * // p3.y = p.y / p2.y
+ * let p2 = createVector(2, 3);
+ * let p3 = p5.Vector.div(p, p2);
+ * point(p3);
+ *
+ * describe('Two black dots drawn on a gray square. One dot is in the top left corner and the other is in the bottom center.');
+ * }
+ *
+ *
+ * function draw() {
+ * background(200);
+ *
+ * let origin = createVector(0, 0);
+ *
+ * // Draw the red arrow.
+ * let v1 = createVector(50, 50);
+ * drawArrow(origin, v1, 'red');
+ *
+ * // Draw the blue arrow.
+ * let v2 = p5.Vector.div(v1, 2);
+ * drawArrow(origin, v2, 'blue');
+ *
+ * describe('Two arrows extending from the top left corner. The blue arrow is half the length of the red arrow.');
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Vector object.
+ * let p = createVector(30, 40);
+ *
+ * // Draw a line from the origin.
+ * line(0, 0, p.x, p.y);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the vector's magnitude.
+ * let m = p.mag();
+ * text(m, p.x, p.y);
+ *
+ * describe('A diagonal black line extends from the top left corner of a gray square. The number 50 is written at the end of the line.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Vector object.
+ * let p = createVector(30, 40);
+ *
+ * // Draw a line from the origin.
+ * line(0, 0, p.x, p.y);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ *
+ * // Display the vector's magnitude squared.
+ * let m = p.magSq();
+ * text(m, p.x, p.y);
+ *
+ * describe('A diagonal black line extends from the top left corner of a gray square. The number 2500 is written at the end of the line.');
+ * }
+ *
+ *
+ * function setup() {
+ * // Create p5.Vector objects.
+ * let v1 = createVector(3, 4);
+ * let v2 = createVector(3, 0);
+ *
+ * // Calculate the dot product.
+ * let dp = v1.dot(v2);
+ *
+ * // Prints "9" to the console.
+ * print(dp);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create p5.Vector objects.
+ * let v1 = createVector(1, 0);
+ * let v2 = createVector(0, 1);
+ *
+ * // Calculate the dot product.
+ * let dp = p5.Vector.dot(v1, v2);
+ *
+ * // Prints "0" to the console.
+ * print(dp);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Two arrows drawn on a gray square. A black arrow points to the right and a red arrow follows the mouse. The text "v1 • v2 = something" changes as the mouse moves.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Center.
+ * let v0 = createVector(50, 50);
+ *
+ * // Draw the black arrow.
+ * let v1 = createVector(30, 0);
+ * drawArrow(v0, v1, 'black');
+ *
+ * // Draw the red arrow.
+ * let v2 = createVector(mouseX - 50, mouseY - 50);
+ * drawArrow(v0, v2, 'red');
+ *
+ * // Display the dot product.
+ * let dp = v2.dot(v1);
+ * text(`v2 • v1 = ${dp}`, 10, 20);
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create p5.Vector objects.
+ * let v1 = createVector(1, 0);
+ * let v2 = createVector(3, 4);
+ *
+ * // Calculate the cross product.
+ * let cp = v1.cross(v2);
+ *
+ * // Prints "p5.Vector Object : [0, 0, 4]" to the console.
+ * print(cp.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create p5.Vector objects.
+ * let v1 = createVector(1, 0);
+ * let v2 = createVector(3, 4);
+ *
+ * // Calculate the cross product.
+ * let cp = p5.Vector.cross(v1, v2);
+ *
+ * // Prints "p5.Vector Object : [0, 0, 4]" to the console.
+ * print(cp.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create p5.Vector objects.
+ * let v1 = createVector(1, 0);
+ * let v2 = createVector(0, 1);
+ *
+ * // Calculate the distance between them.
+ * let d = v1.dist(v2);
+ *
+ * // Prints "1.414..." to the console.
+ * print(d);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create p5.Vector objects.
+ * let v1 = createVector(1, 0);
+ * let v2 = createVector(0, 1);
+ *
+ * // Calculate the distance between them.
+ * let d = p5.Vector.dist(v1, v2);
+ *
+ * // Prints "1.414..." to the console.
+ * print(d);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Three arrows drawn on a gray square. A red and a blue arrow extend from the top left. A purple arrow extends from the tip of the red arrow to the tip of the blue arrow. The number 36 is written in black near the purple arrow.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * let origin = createVector(0, 0);
+ *
+ * // Draw the red arrow.
+ * let v1 = createVector(50, 50);
+ * drawArrow(origin, v1, 'red');
+ *
+ * // Draw the blue arrow.
+ * let v2 = createVector(20, 70);
+ * drawArrow(origin, v2, 'blue');
+ *
+ * // Purple arrow.
+ * let v3 = p5.Vector.sub(v2, v1);
+ * drawArrow(v1, v3, 'purple');
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ *
+ * // Display the magnitude.
+ * let m = floor(v3.mag());
+ * text(m, 50, 75);
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Vector.
+ * let v = createVector(10, 20, 2);
+ *
+ * // Normalize.
+ * v.normalize();
+ *
+ * // Prints "p5.Vector Object : [0.445..., 0.890..., 0.089...]" to the console.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a p5.Vector.
+ * let v0 = createVector(10, 20, 2);
+ *
+ * // Create a normalized copy.
+ * let v1 = p5.Vector.normalize(v0);
+ *
+ * // Prints "p5.Vector Object : [10, 20, 2]" to the console.
+ * print(v0.toString());
+ * // Prints "p5.Vector Object : [0.445..., 0.890..., 0.089...]" to the console.
+ * print(v1.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe("A red and blue arrow extend from the center of a circle. Both arrows follow the mouse, but the blue arrow's length is fixed to the circle's radius.");
+ * }
+ *
+ * function draw() {
+ * background(240);
+ *
+ * // Vector to the center.
+ * let v0 = createVector(50, 50);
+ *
+ * // Vector from the center to the mouse.
+ * let v1 = createVector(mouseX - 50, mouseY - 50);
+ *
+ * // Circle's radius.
+ * let r = 25;
+ *
+ * // Draw the red arrow.
+ * drawArrow(v0, v1, 'red');
+ *
+ * // Draw the blue arrow.
+ * v1.normalize();
+ * drawArrow(v0, v1.mult(r), 'blue');
+ *
+ * // Draw the circle.
+ * noFill();
+ * circle(50, 50, r * 2);
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = createVector(10, 20, 2);
+ *
+ * // Limit its magnitude.
+ * v.limit(5);
+ *
+ * // Prints "p5.Vector Object : [2.227..., 4.454..., 0.445...]" to the console.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v0 = createVector(10, 20, 2);
+ *
+ * // Create a copy an limit its magintude.
+ * let v1 = p5.Vector.limit(v0, 5);
+ *
+ * // Prints "p5.Vector Object : [2.227..., 4.454..., 0.445...]" to the console.
+ * print(v1.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe("A red and blue arrow extend from the center of a circle. Both arrows follow the mouse, but the blue arrow never crosses the circle's edge.");
+ * }
+ * function draw() {
+ * background(240);
+ *
+ * // Vector to the center.
+ * let v0 = createVector(50, 50);
+ *
+ * // Vector from the center to the mouse.
+ * let v1 = createVector(mouseX - 50, mouseY - 50);
+ *
+ * // Circle's radius.
+ * let r = 25;
+ *
+ * // Draw the red arrow.
+ * drawArrow(v0, v1, 'red');
+ *
+ * // Draw the blue arrow.
+ * drawArrow(v0, v1.limit(r), 'blue');
+ *
+ * // Draw the circle.
+ * noFill();
+ * circle(50, 50, r * 2);
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = createVector(3, 4, 0);
+ *
+ * // Prints "5" to the console.
+ * print(v.mag());
+ *
+ * // Set its magnitude to 10.
+ * v.setMag(10);
+ *
+ * // Prints "p5.Vector Object : [6, 8, 0]" to the console.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v0 = createVector(3, 4, 0);
+ *
+ * // Create a copy with a magnitude of 10.
+ * let v1 = p5.Vector.setMag(v0, 10);
+ *
+ * // Prints "5" to the console.
+ * print(v0.mag());
+ *
+ * // Prints "p5.Vector Object : [6, 8, 0]" to the console.
+ * print(v1.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Two arrows extend from the top left corner of a square toward its center. The red arrow reaches the center and the blue arrow only extends part of the way.');
+ * }
+ *
+ * function draw() {
+ * background(240);
+ *
+ * let origin = createVector(0, 0);
+ * let v = createVector(50, 50);
+ *
+ * // Draw the red arrow.
+ * drawArrow(origin, v, 'red');
+ *
+ * // Set v's magnitude to 30.
+ * v.setMag(30);
+ *
+ * // Draw the blue arrow.
+ * drawArrow(origin, v, 'blue');
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = createVector(1, 1);
+ *
+ * // Prints "0.785..." to the console.
+ * print(v.heading());
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Prints "45" to the console.
+ * print(v.heading());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = createVector(1, 1);
+ *
+ * // Prints "0.785..." to the console.
+ * print(p5.Vector.heading(v));
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Prints "45" to the console.
+ * print(p5.Vector.heading(v));
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A black arrow extends from the top left of a square to its center. The text "Radians: 0.79" and "Degrees: 45" is written near the tip of the arrow.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * let origin = createVector(0, 0);
+ * let v = createVector(50, 50);
+ *
+ * // Draw the black arrow.
+ * drawArrow(origin, v, 'black');
+ *
+ * // Use radians.
+ * angleMode(RADIANS);
+ *
+ * // Display the heading in radians.
+ * let h = round(v.heading(), 2);
+ * text(`Radians: ${h}`, 20, 70);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Display the heading in degrees.
+ * h = v.heading();
+ * text(`Degrees: ${h}`, 20, 85);
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = createVector(0, 1);
+ *
+ * // Prints "1.570..." to the console.
+ * print(v.heading());
+ *
+ * // Point to the left.
+ * v.setHeading(PI);
+ *
+ * // Prints "3.141..." to the console.
+ * print(v.heading());
+ * }
+ *
+ *
+ * function setup() {
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Create a p5.Vector object.
+ * let v = createVector(0, 1);
+ *
+ * // Prints "90" to the console.
+ * print(v.heading());
+ *
+ * // Point to the left.
+ * v.setHeading(180);
+ *
+ * // Prints "180" to the console.
+ * print(v.heading());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Two arrows extend from the center of a gray square. The red arrow points to the right and the blue arrow points down.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Create p5.Vector objects.
+ * let v0 = createVector(50, 50);
+ * let v1 = createVector(30, 0);
+ *
+ * // Draw the red arrow.
+ * drawArrow(v0, v1, 'red');
+ *
+ * // Point down.
+ * v1.setHeading(HALF_PI);
+ *
+ * // Draw the blue arrow.
+ * drawArrow(v0, v1, 'blue');
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = createVector(1, 0);
+ *
+ * // Prints "p5.Vector Object : [1, 0, 0]" to the console.
+ * print(v.toString());
+ *
+ * // Rotate a quarter turn.
+ * v.rotate(HALF_PI);
+ *
+ * // Prints "p5.Vector Object : [0, 1, 0]" to the console.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Create a p5.Vector object.
+ * let v = createVector(1, 0);
+ *
+ * // Prints "p5.Vector Object : [1, 0, 0]" to the console.
+ * print(v.toString());
+ *
+ * // Rotate a quarter turn.
+ * v.rotate(90);
+ *
+ * // Prints "p5.Vector Object : [0, 1, 0]" to the console.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v0 = createVector(1, 0);
+ *
+ * // Create a rotated copy.
+ * let v1 = p5.Vector.rotate(v0, HALF_PI);
+ *
+ * // Prints "p5.Vector Object : [1, 0, 0]" to the console.
+ * print(v0.toString());
+ * // Prints "p5.Vector Object : [0, 1, 0]" to the console.
+ * print(v1.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Create a p5.Vector object.
+ * let v0 = createVector(1, 0);
+ *
+ * // Create a rotated copy.
+ * let v1 = p5.Vector.rotate(v0, 90);
+ *
+ * // Prints "p5.Vector Object : [1, 0, 0]" to the console.
+ * print(v0.toString());
+ *
+ * // Prints "p5.Vector Object : [0, 1, 0]" to the console.
+ * print(v1.toString());
+ * }
+ *
+ *
+ * let v0;
+ * let v1;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Create p5.Vector objects.
+ * v0 = createVector(50, 50);
+ * v1 = createVector(30, 0);
+ *
+ * describe('A black arrow extends from the center of a gray square. The arrow rotates clockwise.');
+ * }
+ *
+ * function draw() {
+ * background(240);
+ *
+ * // Rotate v1.
+ * v1.rotate(0.01);
+ *
+ * // Draw the black arrow.
+ * drawArrow(v0, v1, 'black');
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create p5.Vector objects.
+ * let v0 = createVector(1, 0);
+ * let v1 = createVector(0, 1);
+ *
+ * // Prints "1.570..." to the console.
+ * print(v0.angleBetween(v1));
+ *
+ * // Prints "-1.570..." to the console.
+ * print(v1.angleBetween(v0));
+ * }
+ *
+ *
+ * function setup() {
+ * // Use degrees.
+ * angleMode(DEGREES);
+ * // Create p5.Vector objects.
+ * let v0 = createVector(1, 0);
+ * let v1 = createVector(0, 1);
+ *
+ * // Prints "90" to the console.
+ * print(v0.angleBetween(v1));
+ *
+ * // Prints "-90" to the console.
+ * print(v1.angleBetween(v0));
+ * }
+ *
+ *
+ * function setup() {
+ * // Create p5.Vector objects.
+ * let v0 = createVector(1, 0);
+ * let v1 = createVector(0, 1);
+ *
+ * // Prints "1.570..." to the console.
+ * print(p5.Vector.angleBetween(v0, v1));
+ *
+ * // Prints "-1.570..." to the console.
+ * print(p5.Vector.angleBetween(v1, v0));
+ * }
+ *
+ *
+ * function setup() {
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Create p5.Vector objects.
+ * let v0 = createVector(1, 0);
+ * let v1 = createVector(0, 1);
+ *
+ * // Prints "90" to the console.
+ * print(p5.Vector.angleBetween(v0, v1));
+ *
+ * // Prints "-90" to the console.
+ * print(p5.Vector.angleBetween(v1, v0));
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Two arrows extend from the center of a gray square. A red arrow points to the right and a blue arrow points down. The text "Radians: 1.57" and "Degrees: 90" is written above the arrows.');
+ * }
+ * function draw() {
+ * background(200);
+ *
+ * // Create p5.Vector objects.
+ * let v0 = createVector(50, 50);
+ * let v1 = createVector(30, 0);
+ * let v2 = createVector(0, 30);
+ *
+ * // Draw the red arrow.
+ * drawArrow(v0, v1, 'red');
+ *
+ * // Draw the blue arrow.
+ * drawArrow(v0, v2, 'blue');
+ *
+ * // Use radians.
+ * angleMode(RADIANS);
+ *
+ * // Display the angle in radians.
+ * let angle = round(v1.angleBetween(v2), 2);
+ * text(`Radians: ${angle}`, 20, 20);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Display the angle in degrees.
+ * angle = round(v1.angleBetween(v2), 2);
+ * text(`Degrees: ${angle}`, 20, 35);
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v0 = createVector(1, 1, 1);
+ * let v1 = createVector(3, 3, 3);
+ *
+ * // Interpolate.
+ * v0.lerp(v1, 0.5);
+ *
+ * // Prints "p5.Vector Object : [2, 2, 2]" to the console.
+ * print(v0.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = createVector(1, 1, 1);
+ *
+ * // Interpolate.
+ * v.lerp(3, 3, 3, 0.5);
+ *
+ * // Prints "p5.Vector Object : [2, 2, 2]" to the console.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create p5.Vector objects.
+ * let v0 = createVector(1, 1, 1);
+ * let v1 = createVector(3, 3, 3);
+ *
+ * // Interpolate.
+ * let v2 = p5.Vector.lerp(v0, v1, 0.5);
+ *
+ * // Prints "p5.Vector Object : [2, 2, 2]" to the console.
+ * print(v2.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Three arrows extend from the center of a gray square. A red arrow points to the right, a blue arrow points down, and a purple arrow points to the bottom right.');
+ * }
+ * function draw() {
+ * background(200);
+ *
+ * // Create p5.Vector objects.
+ * let v0 = createVector(50, 50);
+ * let v1 = createVector(30, 0);
+ * let v2 = createVector(0, 30);
+ *
+ * // Interpolate.
+ * let v3 = p5.Vector.lerp(v1, v2, 0.5);
+ *
+ * // Draw the red arrow.
+ * drawArrow(v0, v1, 'red');
+ *
+ * // Draw the blue arrow.
+ * drawArrow(v0, v2, 'blue');
+ *
+ * // Draw the purple arrow.
+ * drawArrow(v0, v3, 'purple');
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v0 = createVector(3, 0);
+ *
+ * // Prints "3" to the console.
+ * print(v0.mag());
+ *
+ * // Prints "0" to the console.
+ * print(v0.heading());
+ *
+ * // Create a p5.Vector object.
+ * let v1 = createVector(0, 1);
+ *
+ * // Prints "1" to the console.
+ * print(v1.mag());
+ *
+ * // Prints "1.570..." to the console.
+ * print(v1.heading());
+ *
+ * // Interpolate halfway between v0 and v1.
+ * v0.slerp(v1, 0.5);
+ *
+ * // Prints "2" to the console.
+ * print(v0.mag());
+ *
+ * // Prints "0.785..." to the console.
+ * print(v0.heading());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v0 = createVector(3, 0);
+ *
+ * // Prints "3" to the console.
+ * print(v0.mag());
+ *
+ * // Prints "0" to the console.
+ * print(v0.heading());
+ *
+ * // Create a p5.Vector object.
+ * let v1 = createVector(0, 1);
+ *
+ * // Prints "1" to the console.
+ * print(v1.mag());
+ *
+ * // Prints "1.570..." to the console.
+ * print(v1.heading());
+ *
+ * // Create a p5.Vector that's halfway between v0 and v1.
+ * let v3 = p5.Vector.slerp(v0, v1, 0.5);
+ *
+ * // Prints "2" to the console.
+ * print(v3.mag());
+ *
+ * // Prints "0.785..." to the console.
+ * print(v3.heading());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Three arrows extend from the center of a gray square. A red arrow points to the right, a blue arrow points to the left, and a purple arrow points down.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Create p5.Vector objects.
+ * let v0 = createVector(50, 50);
+ * let v1 = createVector(20, 0);
+ * let v2 = createVector(-40, 0);
+ *
+ * // Create a p5.Vector that's halfway between v1 and v2.
+ * let v3 = p5.Vector.slerp(v1, v2, 0.5);
+ *
+ * // Draw the red arrow.
+ * drawArrow(v0, v1, 'red');
+ *
+ * // Draw the blue arrow.
+ * drawArrow(v0, v2, 'blue');
+ *
+ * // Draw the purple arrow.
+ * drawArrow(v0, v3, 'purple');
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a normal vector.
+ * let n = createVector(0, 1);
+ * // Create a vector to reflect.
+ * let v = createVector(4, 6);
+ *
+ * // Reflect v about n.
+ * v.reflect(n);
+ *
+ * // Prints "p5.Vector Object : [4, -6, 0]" to the console.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a normal vector.
+ * let n = createVector(0, 1);
+ *
+ * // Create a vector to reflect.
+ * let v0 = createVector(4, 6);
+ *
+ * // Create a reflected vector.
+ * let v1 = p5.Vector.reflect(v0, n);
+ *
+ * // Prints "p5.Vector Object : [4, -6, 0]" to the console.
+ * print(v1.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('Three arrows extend from the center of a gray square with a vertical line down its middle. A black arrow points to the right, a blue arrow points to the bottom left, and a red arrow points to the bottom right.');
+ * }
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a vertical line.
+ * line(50, 0, 50, 100);
+ *
+ * // Create a normal vector.
+ * let n = createVector(1, 0);
+ *
+ * // Center.
+ * let v0 = createVector(50, 50);
+ *
+ * // Create a vector to reflect.
+ * let v1 = createVector(30, 40);
+ *
+ * // Create a reflected vector.
+ * let v2 = p5.Vector.reflect(v1, n);
+ *
+ * // Scale the normal vector for drawing.
+ * n.setMag(30);
+ *
+ * // Draw the black arrow.
+ * drawArrow(v0, n, 'black');
+ *
+ * // Draw the red arrow.
+ * drawArrow(v0, v1, 'red');
+ *
+ * // Draw the blue arrow.
+ * drawArrow(v0, v2, 'blue');
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = createVector(20, 30);
+ *
+ * // Prints "[20, 30, 0]" to the console.
+ * print(v.array());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create p5.Vector objects.
+ * let v0 = createVector(10, 20, 30);
+ * let v1 = createVector(10, 20, 30);
+ * let v2 = createVector(0, 0, 0);
+ *
+ * // Prints "true" to the console.
+ * print(v0.equals(v1));
+ *
+ * // Prints "false" to the console.
+ * print(v0.equals(v2));
+ * }
+ *
+ *
+ * function setup() {
+ * // Create p5.Vector objects.
+ * let v0 = createVector(5, 10, 20);
+ * let v1 = createVector(5, 10, 20);
+ * let v2 = createVector(13, 10, 19);
+ *
+ * // Prints "true" to the console.
+ * print(v0.equals(v1.x, v1.y, v1.z));
+ *
+ * // Prints "false" to the console.
+ * print(v0.equals(v2.x, v2.y, v2.z));
+ * }
+ *
+ *
+ * function setup() {
+ * // Create p5.Vector objects.
+ * let v0 = createVector(10, 20, 30);
+ * let v1 = createVector(10, 20, 30);
+ * let v2 = createVector(0, 0, 0);
+ *
+ * // Prints "true" to the console.
+ * print(p5.Vector.equals(v0, v1));
+ *
+ * // Prints "false" to the console.
+ * print(p5.Vector.equals(v0, v2));
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = p5.Vector.fromAngle(0);
+ *
+ * // Prints "p5.Vector Object : [1, 0, 0]" to the console.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = p5.Vector.fromAngle(0, 30);
+ *
+ * // Prints "p5.Vector Object : [30, 0, 0]" to the console.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A black arrow extends from the center of a gray square. It points to the right.');
+ * }
+ * function draw() {
+ * background(200);
+ *
+ * // Create a p5.Vector to the center.
+ * let v0 = createVector(50, 50);
+ *
+ * // Create a p5.Vector with an angle 0 and magnitude 30.
+ * let v1 = p5.Vector.fromAngle(0, 30);
+ *
+ * // Draw the black arrow.
+ * drawArrow(v0, v1, 'black');
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = p5.Vector.fromAngles(0, 0);
+ *
+ * // Prints "p5.Vector Object : [0, -1, 0]" to the console.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A light shines on a pink sphere as it orbits.');
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Calculate the ISO angles.
+ * let theta = frameCount * 0.05;
+ * let phi = 0;
+ *
+ * // Create a p5.Vector object.
+ * let v = p5.Vector.fromAngles(theta, phi, 100);
+ *
+ * // Create a point light using the p5.Vector.
+ * let c = color('deeppink');
+ * pointLight(c, v);
+ *
+ * // Style the sphere.
+ * fill(255);
+ * noStroke();
+ *
+ * // Draw the sphere.
+ * sphere(35);
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = p5.Vector.random2D();
+ *
+ * // Prints "p5.Vector Object : [x, y, 0]" to the console
+ * // where x and y are small random numbers.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Slow the frame rate.
+ * frameRate(1);
+ *
+ * describe('A black arrow in extends from the center of a gray square. It changes direction once per second.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Create a p5.Vector to the center.
+ * let v0 = createVector(50, 50);
+ *
+ * // Create a random p5.Vector.
+ * let v1 = p5.Vector.random2D();
+ *
+ * // Scale v1 for drawing.
+ * v1.mult(30);
+ *
+ * // Draw the black arrow.
+ * drawArrow(v0, v1, 'black');
+ * }
+ *
+ * // Draws an arrow between two vectors.
+ * function drawArrow(base, vec, myColor) {
+ * push();
+ * stroke(myColor);
+ * strokeWeight(3);
+ * fill(myColor);
+ * translate(base.x, base.y);
+ * line(0, 0, vec.x, vec.y);
+ * rotate(vec.heading());
+ * let arrowSize = 7;
+ * translate(vec.mag() - arrowSize, 0);
+ * triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
+ * pop();
+ * }
+ *
+ *
+ * function setup() {
+ * // Create a p5.Vector object.
+ * let v = p5.Vector.random3D();
+ *
+ * // Prints "p5.Vector Object : [x, y, z]" to the console
+ * // where x, y, and z are small random numbers.
+ * print(v.toString());
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get random coordinates.
+ * let x = random(0, 100);
+ * let y = random(0, 100);
+ *
+ * // Draw the white circle.
+ * circle(x, y, 10);
+ *
+ * // Set a random seed for consistency.
+ * randomSeed(99);
+ *
+ * // Get random coordinates.
+ * x = random(0, 100);
+ * y = random(0, 100);
+ *
+ * // Draw the black circle.
+ * fill(0);
+ * circle(x, y, 10);
+ *
+ * describe('A white circle appears at a random position. A black circle appears at (27.4, 25.8).');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get random coordinates between 0 and 100.
+ * let x = random(0, 100);
+ * let y = random(0, 100);
+ *
+ * // Draw a point.
+ * strokeWeight(5);
+ * point(x, y);
+ *
+ * describe('A black dot appears in a random position on a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get random coordinates between 0 and 100.
+ * let x = random(100);
+ * let y = random(100);
+ *
+ * // Draw the point.
+ * strokeWeight(5);
+ * point(x, y);
+ *
+ * describe('A black dot appears in a random position on a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of emoji strings.
+ * let animals = ['🦁', '🐯', '🐻'];
+ *
+ * // Choose a random element from the array.
+ * let choice = random(animals);
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(20);
+ *
+ * // Display the emoji.
+ * text(choice, 50, 50);
+ *
+ * describe('An animal face is displayed at random. Either a lion, tiger, or bear.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Slow the frame rate.
+ * frameRate(5);
+ *
+ * describe('A black dot moves around randomly on a gray square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Get random coordinates between 0 and 100.
+ * let x = random(100);
+ * let y = random(100);
+ *
+ * // Draw the point.
+ * strokeWeight(5);
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Slow the frame rate.
+ * frameRate(5);
+ *
+ * describe('A black dot moves around randomly in the middle of a gray square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Get random coordinates between 45 and 55.
+ * let x = random(45, 55);
+ * let y = random(45, 55);
+ *
+ * // Draw the point.
+ * strokeWeight(5);
+ * point(x, y);
+ * }
+ *
+ *
+ * let x = 50;
+ * let y = 50;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('A black dot moves around randomly leaving a trail.');
+ * }
+ *
+ * function draw() {
+ * // Update x and y randomly.
+ * x += random(-1, 1);
+ * y += random(-1, 1);
+ *
+ * // Draw the point.
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('Three horizontal black lines are filled in randomly. The top line spans entire canvas. The middle line is very short. The bottom line spans two-thirds of the canvas.');
+ * }
+ *
+ * function draw() {
+ * // Style the circles.
+ * noStroke();
+ * fill(0, 10);
+ *
+ * // Uniform distribution between 0 and 100.
+ * let x = random(100);
+ * let y = 25;
+ * circle(x, y, 5);
+ *
+ * // Gaussian distribution with a mean of 50 and sd of 1.
+ * x = randomGaussian(50);
+ * y = 50;
+ * circle(x, y, 5);
+ *
+ * // Gaussian distribution with a mean of 50 and sd of 10.
+ * x = randomGaussian(50, 10);
+ * y = 75;
+ * circle(x, y, 5);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Calculate cos() and acos() values.
+ * let a = PI;
+ * let c = cos(a);
+ * let ac = acos(c);
+ *
+ * // Display the values.
+ * text(`${round(a, 3)}`, 35, 25);
+ * text(`${round(c, 3)}`, 35, 50);
+ * text(`${round(ac, 3)}`, 35, 75);
+ *
+ * describe('The numbers 3.142, -1, and 3.142 written on separate rows.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Calculate cos() and acos() values.
+ * let a = PI + QUARTER_PI;
+ * let c = cos(a);
+ * let ac = acos(c);
+ *
+ * // Display the values.
+ * text(`${round(a, 3)}`, 35, 25);
+ * text(`${round(c, 3)}`, 35, 50);
+ * text(`${round(ac, 3)}`, 35, 75);
+ *
+ * describe('The numbers 3.927, -0.707, and 2.356 written on separate rows.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Calculate sin() and asin() values.
+ * let a = PI / 3;
+ * let s = sin(a);
+ * let as = asin(s);
+ *
+ * // Display the values.
+ * text(`${round(a, 3)}`, 35, 25);
+ * text(`${round(s, 3)}`, 35, 50);
+ * text(`${round(as, 3)}`, 35, 75);
+ *
+ * describe('The numbers 1.047, 0.866, and 1.047 written on separate rows.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Calculate sin() and asin() values.
+ * let a = PI + PI / 3;
+ * let s = sin(a);
+ * let as = asin(s);
+ *
+ * // Display the values.
+ * text(`${round(a, 3)}`, 35, 25);
+ * text(`${round(s, 3)}`, 35, 50);
+ * text(`${round(as, 3)}`, 35, 75);
+ *
+ * describe('The numbers 4.189, -0.866, and -1.047 written on separate rows.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Calculate tan() and atan() values.
+ * let a = PI / 3;
+ * let t = tan(a);
+ * let at = atan(t);
+ *
+ * // Display the values.
+ * text(`${round(a, 3)}`, 35, 25);
+ * text(`${round(t, 3)}`, 35, 50);
+ * text(`${round(at, 3)}`, 35, 75);
+ *
+ * describe('The numbers 1.047, 1.732, and 1.047 written on separate rows.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Calculate tan() and atan() values.
+ * let a = PI + PI / 3;
+ * let t = tan(a);
+ * let at = atan(t);
+ *
+ * // Display the values.
+ * text(`${round(a, 3)}`, 35, 25);
+ * text(`${round(t, 3)}`, 35, 50);
+ * text(`${round(at, 3)}`, 35, 75);
+ *
+ * describe('The numbers 4.189, 1.732, and 1.047 written on separate rows.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A rectangle at the top-left of the canvas rotates with mouse movements.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the angle between the mouse
+ * // and the origin.
+ * let a = atan2(mouseY, mouseX);
+ *
+ * // Rotate.
+ * rotate(a);
+ *
+ * // Draw the shape.
+ * rect(0, 0, 60, 10);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A rectangle at the center of the canvas rotates with mouse movements.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin to the center.
+ * translate(50, 50);
+ *
+ * // Get the mouse's coordinates relative to the origin.
+ * let x = mouseX - 50;
+ * let y = mouseY - 50;
+ *
+ * // Calculate the angle between the mouse and the origin.
+ * let a = atan2(y, x);
+ *
+ * // Rotate.
+ * rotate(a);
+ *
+ * // Draw the shape.
+ * rect(-30, -5, 60, 10);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white ball on a string oscillates left and right.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the coordinates.
+ * let x = 30 * cos(frameCount * 0.05) + 50;
+ * let y = 50;
+ *
+ * // Draw the oscillator.
+ * line(50, y, x, y);
+ * circle(x, y, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('A series of black dots form a wave pattern.');
+ * }
+ *
+ * function draw() {
+ * // Calculate the coordinates.
+ * let x = frameCount;
+ * let y = 30 * cos(x * 0.1) + 50;
+ *
+ * // Draw the point.
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('A series of black dots form an infinity symbol.');
+ * }
+ *
+ * function draw() {
+ * // Calculate the coordinates.
+ * let x = 30 * cos(frameCount * 0.1) + 50;
+ * let y = 10 * sin(frameCount * 0.2) + 50;
+ *
+ * // Draw the point.
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white ball on a string oscillates up and down.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the coordinates.
+ * let x = 50;
+ * let y = 30 * sin(frameCount * 0.05) + 50;
+ *
+ * // Draw the oscillator.
+ * line(50, y, x, y);
+ * circle(x, y, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('A series of black dots form a wave pattern.');
+ * }
+ *
+ * function draw() {
+ * // Calculate the coordinates.
+ * let x = frameCount;
+ * let y = 30 * sin(x * 0.1) + 50;
+ *
+ * // Draw the point.
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('A series of black dots form an infinity symbol.');
+ * }
+ *
+ * function draw() {
+ * // Calculate the coordinates.
+ * let x = 30 * cos(frameCount * 0.1) + 50;
+ * let y = 10 * sin(frameCount * 0.2) + 50;
+ *
+ * // Draw the point.
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * describe('A series of identical curves drawn with black dots. Each curve starts from the top of the canvas, continues down at a slight angle, flattens out at the middle of the canvas, then continues to the bottom.');
+ * }
+ *
+ * function draw() {
+ * // Calculate the coordinates.
+ * let x = frameCount;
+ * let y = 5 * tan(x * 0.1) + 50;
+ *
+ * // Draw the point.
+ * point(x, y);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Calculate the angle conversion.
+ * let rad = QUARTER_PI;
+ * let deg = degrees(rad);
+ *
+ * // Display the conversion.
+ * text(`${round(rad, 2)} rad = ${deg}˚`, 10, 50);
+ *
+ * describe('The text "0.79 rad = 45˚".');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Caclulate the angle conversion.
+ * let deg = 45;
+ * let rad = radians(deg);
+ *
+ * // Display the angle conversion.
+ * text(`${deg}˚ = ${round(rad, 3)} rad`, 10, 50);
+ *
+ * describe('The text "45˚ = 0.785 rad".');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Rotate 1/8 turn.
+ * rotate(QUARTER_PI);
+ *
+ * // Draw a line.
+ * line(0, 0, 80, 0);
+ *
+ * describe('A diagonal line radiating from the top-left corner of a square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Rotate 1/8 turn.
+ * rotate(45);
+ *
+ * // Draw a line.
+ * line(0, 0, 80, 0);
+ *
+ * describe('A diagonal line radiating from the top-left corner of a square.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Calculate the angle to rotate.
+ * let angle = TWO_PI / 7;
+ *
+ * // Move the origin to the center.
+ * translate(50, 50);
+ *
+ * // Style the flower.
+ * noStroke();
+ * fill(255, 50);
+ *
+ * // Draw the flower.
+ * for (let i = 0; i < 7; i += 1) {
+ * ellipse(0, 0, 80, 20);
+ * rotate(angle);
+ * }
+ *
+ * describe('A translucent white flower on a dark background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(50);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Calculate the angle to rotate.
+ * let angle = 360 / 7;
+ *
+ * // Move the origin to the center.
+ * translate(50, 50);
+ *
+ * // Style the flower.
+ * noStroke();
+ * fill(255, 50);
+ *
+ * // Draw the flower.
+ * for (let i = 0; i < 7; i += 1) {
+ * ellipse(0, 0, 80, 20);
+ * rotate(angle);
+ * }
+ *
+ * describe('A translucent white flower on a dark background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white ball on a string oscillates left and right.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the coordinates.
+ * let x = 30 * cos(frameCount * 0.05) + 50;
+ * let y = 50;
+ *
+ * // Draw the oscillator.
+ * line(50, y, x, y);
+ * circle(x, y, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * describe('A white ball on a string oscillates left and right.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the coordinates.
+ * let x = 30 * cos(frameCount * 2.86) + 50;
+ * let y = 50;
+ *
+ * // Draw the oscillator.
+ * line(50, y, x, y);
+ * circle(x, y, 20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw the upper line.
+ * rotate(PI / 6);
+ * line(0, 0, 80, 0);
+ *
+ * // Use degrees.
+ * angleMode(DEGREES);
+ *
+ * // Draw the lower line.
+ * rotate(30);
+ * line(0, 0, 80, 0);
+ *
+ * describe('Two diagonal lines radiating from the top-left corner of a square. The lines are oriented 30 degrees from the edges of the square and 30 degrees apart from each other.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Draw a vertical line.
+ * strokeWeight(0.5);
+ * line(50, 0, 50, 100);
+ *
+ * // Top line.
+ * textSize(16);
+ * textAlign(RIGHT);
+ * text('ABCD', 50, 30);
+ *
+ * // Middle line.
+ * textAlign(CENTER);
+ * text('EFGH', 50, 50);
+ *
+ * // Bottom line.
+ * textAlign(LEFT);
+ * text('IJKL', 50, 70);
+ *
+ * describe('The letters ABCD displayed at top-left, EFGH at center, and IJKL at bottom-right. A vertical line divides the canvas in half.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * strokeWeight(0.5);
+ *
+ * // First line.
+ * line(0, 12, width, 12);
+ * textAlign(CENTER, TOP);
+ * text('TOP', 50, 12);
+ *
+ * // Second line.
+ * line(0, 37, width, 37);
+ * textAlign(CENTER, CENTER);
+ * text('CENTER', 50, 37);
+ *
+ * // Third line.
+ * line(0, 62, width, 62);
+ * textAlign(CENTER, BASELINE);
+ * text('BASELINE', 50, 62);
+ *
+ * // Fourth line.
+ * line(0, 97, width, 97);
+ * textAlign(CENTER, BOTTOM);
+ * text('BOTTOM', 50, 97);
+ *
+ * describe('The words "TOP", "CENTER", "BASELINE", and "BOTTOM" each drawn relative to a horizontal line. Their positions demonstrate different vertical alignments.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // "\n" starts a new line of text.
+ * let lines = 'one\ntwo';
+ *
+ * // Left.
+ * text(lines, 10, 25);
+ *
+ * // Right.
+ * textLeading(30);
+ * text(lines, 70, 25);
+ *
+ * describe('The words "one" and "two" written on separate lines twice. The words on the left have less vertical spacing than the words on the right.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Top.
+ * textSize(12);
+ * text('Font Size 12', 10, 30);
+ *
+ * // Middle.
+ * textSize(14);
+ * text('Font Size 14', 10, 60);
+ *
+ * // Bottom.
+ * textSize(16);
+ * text('Font Size 16', 10, 90);
+ *
+ * describe('The text "Font Size 12" drawn small, "Font Size 14" drawn medium, and "Font Size 16" drawn large.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textSize(12);
+ * textAlign(CENTER);
+ *
+ * // First row.
+ * textStyle(NORMAL);
+ * text('Normal', 50, 15);
+ *
+ * // Second row.
+ * textStyle(ITALIC);
+ * text('Italic', 50, 40);
+ *
+ * // Third row.
+ * textStyle(BOLD);
+ * text('Bold', 50, 65);
+ *
+ * // Fourth row.
+ * textStyle(BOLDITALIC);
+ * text('Bold Italic', 50, 90);
+ *
+ * describe('The words "Normal" displayed normally, "Italic" in italic, "Bold" in bold, and "Bold Italic" in bold italics.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textSize(28);
+ * strokeWeight(0.5);
+ *
+ * // Calculate the text width.
+ * let s = 'yoyo';
+ * let w = textWidth(s);
+ *
+ * // Display the text.
+ * text(s, 22, 55);
+ *
+ * // Underline the text.
+ * line(22, 55, 22 + w, 55);
+ *
+ * describe('The word "yoyo" underlined.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textSize(28);
+ * strokeWeight(0.5);
+ *
+ * // Calculate the text width.
+ * // "\n" starts a new line.
+ * let s = 'yo\nyo';
+ * let w = textWidth(s);
+ *
+ * // Display the text.
+ * text(s, 22, 55);
+ *
+ * // Underline the text.
+ * line(22, 55, 22 + w, 55);
+ *
+ * describe('The word "yo" written twice, one copy beneath the other. The words are divided by a horizontal line.');
+ * }
+ *
+ *
+ * let font;
+ *
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textFont(font);
+ *
+ * // Different for each font.
+ * let fontScale = 0.8;
+ *
+ * let baseY = 75;
+ * strokeWeight(0.5);
+ *
+ * // Draw small text.
+ * textSize(24);
+ * text('dp', 0, baseY);
+ *
+ * // Draw baseline and ascent.
+ * let a = textAscent() * fontScale;
+ * line(0, baseY, 23, baseY);
+ * line(23, baseY - a, 23, baseY);
+ *
+ * // Draw large text.
+ * textSize(48);
+ * text('dp', 45, baseY);
+ *
+ * // Draw baseline and ascent.
+ * a = textAscent() * fontScale;
+ * line(45, baseY, 91, baseY);
+ * line(91, baseY - a, 91, baseY);
+ *
+ * describe('The letters "dp" written twice in different sizes. Each version has a horizontal baseline. A vertical line extends upward from each baseline to the top of the "d".');
+ * }
+ *
+ *
+ * let font;
+ *
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the font.
+ * textFont(font);
+ *
+ * // Different for each font.
+ * let fontScale = 0.9;
+ *
+ * let baseY = 75;
+ * strokeWeight(0.5);
+ *
+ * // Draw small text.
+ * textSize(24);
+ * text('dp', 0, baseY);
+ *
+ * // Draw baseline and descent.
+ * let d = textDescent() * fontScale;
+ * line(0, baseY, 23, baseY);
+ * line(23, baseY, 23, baseY + d);
+ *
+ * // Draw large text.
+ * textSize(48);
+ * text('dp', 45, baseY);
+ *
+ * // Draw baseline and descent.
+ * d = textDescent() * fontScale;
+ * line(45, baseY, 91, baseY);
+ * line(91, baseY, 91, baseY + d);
+ *
+ * describe('The letters "dp" written twice in different sizes. Each version has a horizontal baseline. A vertical line extends downward from each baseline to the bottom of the "p".');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textSize(20);
+ * textWrap(WORD);
+ *
+ * // Display the text.
+ * text('Have a wonderful day', 0, 10, 100);
+ *
+ * describe('The text "Have a wonderful day" written across three lines.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textSize(20);
+ * textWrap(CHAR);
+ *
+ * // Display the text.
+ * text('Have a wonderful day', 0, 10, 100);
+ *
+ * describe('The text "Have a wonderful day" written across two lines.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textSize(20);
+ * textWrap(CHAR);
+ *
+ * // Display the text.
+ * text('祝你有美好的一天', 0, 10, 100);
+ *
+ * describe('The text "祝你有美好的一天" written across two lines.');
+ * }
+ *
+ *
+ * let font;
+ *
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * fill('deeppink');
+ * textFont(font);
+ * textSize(36);
+ * text('p5*js', 10, 50);
+ *
+ * describe('The text "p5*js" written in pink on a white background.');
+ * }
+ *
+ *
+ * function setup() {
+ * loadFont('assets/inconsolata.otf', font => {
+ * fill('deeppink');
+ * textFont(font);
+ * textSize(36);
+ * text('p5*js', 10, 50);
+ *
+ * describe('The text "p5*js" written in pink on a white background.');
+ * });
+ * }
+ *
+ *
+ * function setup() {
+ * loadFont('assets/inconsolata.otf', success, failure);
+ * }
+ *
+ * function success(font) {
+ * fill('deeppink');
+ * textFont(font);
+ * textSize(36);
+ * text('p5*js', 10, 50);
+ *
+ * describe('The text "p5*js" written in pink on a white background.');
+ * }
+ *
+ * function failure(event) {
+ * console.error('Oops!', event);
+ * }
+ *
+ *
+ * function preload() {
+ * loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * let p = createP('p5*js');
+ * p.style('color', 'deeppink');
+ * p.style('font-family', 'Inconsolata');
+ * p.style('font-size', '36px');
+ * p.position(10, 50);
+ *
+ * describe('The text "p5*js" written in pink on a white background.');
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ * text('hi', 50, 50);
+ *
+ * describe('The text "hi" written in black in the middle of a gray square.');
+ * }
+ *
+ *
+ * function setup() {
+ * background('skyblue');
+ * textSize(100);
+ * text('🌈', 0, 100);
+ *
+ * describe('A rainbow in a blue sky.');
+ * }
+ *
+ *
+ * function setup() {
+ * textSize(32);
+ * fill(255);
+ * stroke(0);
+ * strokeWeight(4);
+ * text('hi', 50, 50);
+ *
+ * describe('The text "hi" written in white with a black outline.');
+ * }
+ *
+ *
+ * function setup() {
+ * background('black');
+ * textSize(22);
+ * fill('yellow');
+ * text('rainbows', 6, 20);
+ * fill('cornflowerblue');
+ * text('rainbows', 6, 45);
+ * fill('tomato');
+ * text('rainbows', 6, 70);
+ * fill('limegreen');
+ * text('rainbows', 6, 95);
+ *
+ * describe('The text "rainbows" written on several lines, each in a different color.');
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ * let s = 'The quick brown fox jumps over the lazy dog.';
+ * text(s, 10, 10, 70, 80);
+ *
+ * describe('The sample text "The quick brown fox..." written in black across several lines.');
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ * rectMode(CENTER);
+ * let s = 'The quick brown fox jumps over the lazy dog.';
+ * text(s, 50, 50, 70, 80);
+ *
+ * describe('The sample text "The quick brown fox..." written in black across several lines.');
+ * }
+ *
+ *
+ * let font;
+ *
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ * textFont(font);
+ * textSize(32);
+ * textAlign(CENTER, CENTER);
+ * }
+ *
+ * function draw() {
+ * background(0);
+ * rotateY(frameCount / 30);
+ * text('p5*js', 0, 0);
+ *
+ * describe('The text "p5*js" written in white and spinning in 3D.');
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ * textFont('Courier New');
+ * textSize(24);
+ * text('hi', 35, 55);
+ *
+ * describe('The text "hi" written in a black, monospace font on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * background('black');
+ * fill('palegreen');
+ * textFont('Courier New', 10);
+ * text('You turn to the left and see a door. Do you enter?', 5, 5, 90, 90);
+ * text('>', 5, 70);
+ *
+ * describe('A text prompt from a game is written in a green, monospace font on a black background.');
+ * }
+ *
+ *
+ * function setup() {
+ * background(200);
+ * textFont('Verdana');
+ * let currentFont = textFont();
+ * text(currentFont, 25, 50);
+ *
+ * describe('The text "Verdana" written in a black, sans-serif font on a gray background.');
+ * }
+ *
+ *
+ * let fontRegular;
+ * let fontItalic;
+ * let fontBold;
+ *
+ * function preload() {
+ * fontRegular = loadFont('assets/Regular.otf');
+ * fontItalic = loadFont('assets/Italic.ttf');
+ * fontBold = loadFont('assets/Bold.ttf');
+ * }
+ *
+ * function setup() {
+ * background(200);
+ * textFont(fontRegular);
+ * text('I am Normal', 10, 30);
+ * textFont(fontItalic);
+ * text('I am Italic', 10, 50);
+ * textFont(fontBold);
+ * text('I am Bold', 10, 70);
+ *
+ * describe('The statements "I am Normal", "I am Italic", and "I am Bold" written in black on separate lines. The statements have normal, italic, and bold fonts, respectively.');
+ * }
+ *
+ *
+ * let font;
+ *
+ * function preload() {
+ * // Creates a p5.Font object.
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * // Style the text.
+ * fill('deeppink');
+ * textFont(font);
+ * textSize(36);
+ *
+ * // Display the text.
+ * text('p5*js', 10, 50);
+ *
+ * describe('The text "p5*js" written in pink on a gray background.');
+ * }
+ *
+ *
+ * let font;
+ *
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Display the bounding box.
+ * let bbox = font.textBounds('p5*js', 35, 53);
+ * rect(bbox.x, bbox.y, bbox.w, bbox.h);
+ *
+ * // Style the text.
+ * textFont(font);
+ *
+ * // Display the text.
+ * text('p5*js', 35, 53);
+ *
+ * describe('The text "p5*js" written in black inside a white rectangle.');
+ * }
+ *
+ *
+ * let font;
+ *
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textFont(font);
+ * textSize(15);
+ * textAlign(CENTER, CENTER);
+ *
+ * // Display the bounding box.
+ * let bbox = font.textBounds('p5*js', 50, 50);
+ * rect(bbox.x, bbox.y, bbox.w, bbox.h);
+ *
+ * // Display the text.
+ * text('p5*js', 50, 50);
+ *
+ * describe('The text "p5*js" written in black inside a white rectangle.');
+ * }
+ *
+ *
+ * let font;
+ *
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Display the bounding box.
+ * let bbox = font.textBounds('p5*js', 31, 53, 15);
+ * rect(bbox.x, bbox.y, bbox.w, bbox.h);
+ *
+ * // Style the text.
+ * textFont(font);
+ * textSize(15);
+ *
+ * // Display the text.
+ * text('p5*js', 31, 53);
+ *
+ * describe('The text "p5*js" written in black inside a white rectangle.');
+ * }
+ *
+ *
+ * let font;
+ *
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the point array.
+ * let points = font.textToPoints('p5*js', 6, 60, 35, { sampleFactor: 0.5 });
+ *
+ * // Draw a dot at each point.
+ * for (let p of points) {
+ * point(p.x, p.y);
+ * }
+ *
+ * describe('A set of black dots outlining the text "p5*js" on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * let myArray = ['Mango', 'Apple', 'Papaya'];
+ * print(myArray); // ['Mango', 'Apple', 'Papaya']
+ *
+ * append(myArray, 'Peach');
+ * print(myArray); // ['Mango', 'Apple', 'Papaya', 'Peach']
+ * }
+ *
+ * let src = ['A', 'B', 'C'];
+ * let dst = [1, 2, 3];
+ * let srcPosition = 1;
+ * let dstPosition = 0;
+ * let length = 2;
+ *
+ * print(src); // ['A', 'B', 'C']
+ * print(dst); // [ 1 , 2 , 3 ]
+ *
+ * arrayCopy(src, srcPosition, dst, dstPosition, length);
+ * print(dst); // ['B', 'C', 3]
+ *
+ * function setup() {
+ * let arr1 = ['A', 'B', 'C'];
+ * let arr2 = [1, 2, 3];
+ *
+ * print(arr1); // ['A','B','C']
+ * print(arr2); // [1,2,3]
+ *
+ * let arr3 = concat(arr1, arr2);
+ *
+ * print(arr1); // ['A','B','C']
+ * print(arr2); // [1, 2, 3]
+ * print(arr3); // ['A','B','C', 1, 2, 3]
+ * }
+ *
+ * function setup() {
+ * let myArray = ['A', 'B', 'C'];
+ * print(myArray); // ['A','B','C']
+ *
+ * reverse(myArray);
+ * print(myArray); // ['C','B','A']
+ * }
+ *
+ * function setup() {
+ * let myArray = ['A', 'B', 'C'];
+ * print(myArray); // ['A', 'B', 'C']
+ * let newArray = shorten(myArray);
+ * print(myArray); // ['A','B','C']
+ * print(newArray); // ['A','B']
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of colors.
+ * let colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
+ *
+ * // Create a shuffled copy of the array.
+ * let shuffledColors = shuffle(colors);
+ *
+ * // Draw a row of circles using the original array.
+ * for (let i = 0; i < colors.length; i += 1) {
+ * // Calculate the x-coordinate.
+ * let x = (i + 1) * 12.5;
+ *
+ * // Style the circle.
+ * let c = colors[i];
+ * fill(c);
+ *
+ * // Draw the circle.
+ * circle(x, 33, 10);
+ * }
+ *
+ * // Draw a row of circles using the original array.
+ * for (let i = 0; i < shuffledColors.length; i += 1) {
+ * // Calculate the x-coordinate.
+ * let x = (i + 1) * 12.5;
+ *
+ * // Style the circle.
+ * let c = shuffledColors[i];
+ * fill(c);
+ *
+ * // Draw the circle.
+ * circle(x, 67, 10);
+ * }
+ *
+ * describe(
+ * 'Two rows of circles on a gray background. The top row follows the color sequence ROYGBIV. The bottom row has all the same colors but they are shuffled.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of colors.
+ * let colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
+ *
+ * // Shuffle the array.
+ * shuffle(colors, true);
+ *
+ * // Draw a row of circles using the original array.
+ * for (let i = 0; i < colors.length; i += 1) {
+ * // Calculate the x-coordinate.
+ * let x = (i + 1) * 12.5;
+ *
+ * // Style the circle.
+ * let c = colors[i];
+ * fill(c);
+ *
+ * // Draw the circle.
+ * circle(x, 50, 10);
+ * }
+ *
+ * describe(
+ * 'A row of colorful circles on a gray background. Their sequence changes each time the sketch runs.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * let words = ['banana', 'apple', 'pear', 'lime'];
+ * print(words); // ['banana', 'apple', 'pear', 'lime']
+ * let count = 4; // length of array
+ *
+ * words = sort(words, count);
+ * print(words); // ['apple', 'banana', 'lime', 'pear']
+ * }
+ *
+ * function setup() {
+ * let numbers = [2, 6, 1, 5, 14, 9, 8, 12];
+ * print(numbers); // [2, 6, 1, 5, 14, 9, 8, 12]
+ * let count = 5; // Less than the length of the array
+ *
+ * numbers = sort(numbers, count);
+ * print(numbers); // [1,2,5,6,14,9,8,12]
+ * }
+ *
+ * function setup() {
+ * let myArray = [0, 1, 2, 3, 4];
+ * let insArray = ['A', 'B', 'C'];
+ * print(myArray); // [0, 1, 2, 3, 4]
+ * print(insArray); // ['A','B','C']
+ *
+ * splice(myArray, insArray, 3);
+ * print(myArray); // [0,1,2,'A','B','C',3,4]
+ * }
+ *
+ * function setup() {
+ * let myArray = [1, 2, 3, 4, 5];
+ * print(myArray); // [1, 2, 3, 4, 5]
+ *
+ * let sub1 = subset(myArray, 0, 3);
+ * let sub2 = subset(myArray, 2, 2);
+ * print(sub1); // [1,2,3]
+ * print(sub2); // [3,4]
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let original = '12.3';
+ *
+ * // Convert the string to a number.
+ * let converted = float(original);
+ *
+ * // Double the converted value.
+ * let twice = converted * 2;
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(12);
+ *
+ * // Display the original and converted values.
+ * text(`${original} × 2 = ${twice}`, 50, 50);
+ *
+ * describe('The text "12.3 × 2 = 24.6" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of strings.
+ * let original = ['60', '30', '15'];
+ *
+ * // Convert the strings to numbers.
+ * let diameters = float(original);
+ *
+ * for (let d of diameters) {
+ * // Draw a circle.
+ * circle(50, 50, d);
+ * }
+ *
+ * describe('Three white, concentric circles on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a Boolean variable.
+ * let original = false;
+ *
+ * // Convert the Boolean to an integer.
+ * let converted = int(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the original and converted values.
+ * text(`${original} : ${converted}`, 50, 50);
+ *
+ * describe('The text "false : 0" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let original = '12.34';
+ *
+ * // Convert the string to an integer.
+ * let converted = int(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(14);
+ *
+ * // Display the original and converted values.
+ * text(`${original} ≈ ${converted}`, 50, 50);
+ *
+ * describe('The text "12.34 ≈ 12" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a decimal number variable.
+ * let original = 12.34;
+ *
+ * // Convert the decimal number to an integer.
+ * let converted = int(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(14);
+ *
+ * // Display the original and converted values.
+ * text(`${original} ≈ ${converted}`, 50, 50);
+ *
+ * describe('The text "12.34 ≈ 12" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of strings.
+ * let original = ['60', '30', '15'];
+ *
+ * // Convert the strings to integers.
+ * let diameters = int(original);
+ *
+ * for (let d of diameters) {
+ * // Draw a circle.
+ * circle(50, 50, d);
+ * }
+ *
+ * describe('Three white, concentric circles on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a Boolean variable.
+ * let original = false;
+ *
+ * // Convert the Boolean to a string.
+ * let converted = str(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the original and converted values.
+ * text(`${original} : ${converted}`, 50, 50);
+ *
+ * describe('The text "false : false" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a number variable.
+ * let original = 123;
+ *
+ * // Convert the number to a string.
+ * let converted = str(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the original and converted values.
+ * text(`${original} = ${converted}`, 50, 50);
+ *
+ * describe('The text "123 = 123" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of numbers.
+ * let original = [12, 34, 56];
+ *
+ * // Convert the numbers to strings.
+ * let strings = str(original);
+ *
+ * // Create an empty string variable.
+ * let final = '';
+ *
+ * // Concatenate all the strings.
+ * for (let s of strings) {
+ * final += s;
+ * }
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the concatenated string.
+ * text(final, 50, 50);
+ *
+ * describe('The text "123456" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a number variable.
+ * let original = 0;
+ *
+ * // Convert the number to a Boolean value.
+ * let converted = boolean(original);
+ *
+ * // Style the circle based on the converted value.
+ * if (converted === true) {
+ * fill('blue');
+ * } else {
+ * fill('red');
+ * }
+ *
+ * // Draw the circle.
+ * circle(50, 50, 40);
+ *
+ * describe('A red circle on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let original = 'true';
+ *
+ * // Convert the string to a Boolean value.
+ * let converted = boolean(original);
+ *
+ * // Style the circle based on the converted value.
+ * if (converted === true) {
+ * fill('blue');
+ * } else {
+ * fill('red');
+ * }
+ *
+ * // Draw the circle.
+ * circle(50, 50, 40);
+ *
+ * describe('A blue circle on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of values.
+ * let original = [0, 'hi', 123, 'true'];
+ *
+ * // Convert the array to a Boolean values.
+ * let converted = boolean(original);
+ *
+ * // Iterate over the array of converted Boolean values.
+ * for (let i = 0; i < converted.length; i += 1) {
+ *
+ * // Style the circle based on the converted value.
+ * if (converted[i] === true) {
+ * fill('blue');
+ * } else {
+ * fill('red');
+ * }
+ *
+ * // Calculate the x-coordinate.
+ * let x = (i + 1) * 20;
+ *
+ * // Draw the circle.
+ * circle(x, 50, 15);
+ * }
+ *
+ * describe(
+ * 'A row of circles on a gray background. The two circles on the left are red and the two on the right are blue.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a Boolean variable.
+ * let original = true;
+ *
+ * // Convert the Boolean to its byte value.
+ * let converted = byte(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the original and converted values.
+ * text(`${original} : ${converted}`, 50, 50);
+ *
+ * describe('The text "true : 1" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let original = '256';
+ *
+ * // Convert the string to its byte value.
+ * let converted = byte(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the original and converted values.
+ * text(`${original} : ${converted}`, 50, 50);
+ *
+ * describe('The text "256 : 0" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a number variable.
+ * let original = 256;
+ *
+ * // Convert the number to its byte value.
+ * let converted = byte(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the original and converted values.
+ * text(`${original} : ${converted}`, 50, 50);
+ *
+ * describe('The text "256 : 0" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of values.
+ * let original = [false, '64', 383];
+ *
+ * // Convert the array elements to their byte values.
+ * let converted = byte(original);
+ *
+ * // Iterate over the converted array elements.
+ * for (let i = 0; i < converted.length; i += 1) {
+ *
+ * // Style the circle.
+ * fill(converted[i]);
+ *
+ * // Calculate the x-coordinate.
+ * let x = (i + 1) * 25;
+ *
+ * // Draw the circle.
+ * circle(x, 50, 20);
+ * }
+ *
+ * describe(
+ * 'Three gray circles on a gray background. The circles get lighter from left to right.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a number variable.
+ * let original = 65;
+ *
+ * // Convert the number to a char.
+ * let converted = char(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the original and converted values.
+ * text(`${original} : ${converted}`, 50, 50);
+ *
+ * describe('The text "65 : A" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let original = '65';
+ *
+ * // Convert the string to a char.
+ * let converted = char(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the original and converted values.
+ * text(`${original} : ${converted}`, 50, 50);
+ *
+ * describe('The text "65 : A" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of numbers.
+ * let original = ['65', 66, '67'];
+ *
+ * // Convert the string to a char.
+ * let converted = char(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Iterate over elements of the converted array.
+ * for (let i = 0; i < converted.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 25;
+ *
+ * // Display the original and converted values.
+ * text(`${original[i]} : ${converted[i]}`, 50, y);
+ * }
+ *
+ * describe(
+ * 'The text "65 : A", "66 : B", and "67 : C" written on three separate lines. The text is in black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let original = 'A';
+ *
+ * // Convert the string to a number.
+ * let converted = unchar(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the original and converted values.
+ * text(`${original} : ${converted}`, 50, 50);
+ *
+ * describe('The text "A : 65" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of characters.
+ * let original = ['A', 'B', 'C'];
+ *
+ * // Convert the string to a number.
+ * let converted = unchar(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Iterate over elements of the converted array.
+ * for (let i = 0; i < converted.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 25;
+ *
+ * // Display the original and converted values.
+ * text(`${original[i]} : ${converted[i]}`, 50, y);
+ * }
+ *
+ * describe(
+ * 'The text "A : 65", "B : 66", and "C :67" written on three separate lines. The text is in black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a number variable.
+ * let original = 20;
+ *
+ * // Convert the number to a hex string.
+ * let converted = hex(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(14);
+ *
+ * // Display the original and converted values.
+ * text(`${original} = ${converted}`, 50, 50);
+ *
+ * describe('The text "20 = 00000014" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a number variable.
+ * let original = 20;
+ *
+ * // Convert the number to a hex string.
+ * // Only display two hex digits.
+ * let converted = hex(original, 2);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the original and converted values.
+ * text(`${original} = ${converted}`, 50, 50);
+ *
+ * describe('The text "20 = 14" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of numbers.
+ * let original = [1, 10, 100];
+ *
+ * // Convert the numbers to hex strings.
+ * // Only use two hex digits.
+ * let converted = hex(original, 2);
+ *
+ * // Style the text.
+ * textAlign(RIGHT, CENTER);
+ * textSize(16);
+ *
+ * // Iterate over the converted values.
+ * for (let i = 0; i < converted.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 25;
+ *
+ * // Display the original and converted values.
+ * text(`${ original[i]} = ${converted[i]}`, 75, y);
+ * }
+ *
+ * describe(
+ * 'The text "1 = 01", "10 = 0A", and "100 = 64" written on three separate lines. The text is in black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a a hex string variable
+ * let original = 'FF';
+ *
+ * // Convert the hex string to a number.
+ * let converted = unhex(original);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the original and converted values.
+ * text(`${original} = ${converted}`, 50, 50);
+ *
+ * describe('The text "FF = 255" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of numbers.
+ * let original = ['00', '80', 'FF'];
+ *
+ * // Convert the numbers to hex strings.
+ * // Only use two hex digits.
+ * let converted = unhex(original, 2);
+ *
+ * // Style the text.
+ * textAlign(RIGHT, CENTER);
+ * textSize(16);
+ *
+ * // Iterate over the converted values.
+ * for (let i = 0; i < converted.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 25;
+ *
+ * // Display the original and converted values.
+ * text(`${ original[i]} = ${converted[i]}`, 80, y);
+ * }
+ *
+ * describe(
+ * 'The text "00 = 0", "80 = 128", and "FF = 255" written on three separate lines. The text is in black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of strings.
+ * let myWords = ['one', 'two', 'three'];
+ *
+ * // Create a combined string
+ * let combined = join(myWords, ' : ');
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ *
+ * // Display the combined string.
+ * text(combined, 50, 50);
+ *
+ * describe('The text "one : two : three" written in black on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let string = 'Hello, p5*js!';
+ *
+ * // Match the characters that are lowercase
+ * // letters followed by digits.
+ * let matches = match(string, '[a-z][0-9]');
+ *
+ * // Print the matches array to the console:
+ * // ['p5']
+ * print(matches);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the matches.
+ * text(matches, 50, 50);
+ *
+ * describe('The text "p5" written in black on a gray canvas.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let string = 'p5*js is easier than abc123';
+ *
+ * // Match the character sequences that are
+ * // lowercase letters followed by digits.
+ * let matches = matchAll(string, '[a-z][0-9]');
+ *
+ * // Print the matches array to the console:
+ * // [['p5'], ['c1']]
+ * print(matches);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Iterate over the matches array.
+ * for (let i = 0; i < matches.length; i += 1) {
+ *
+ * // Calculate the y-coordainate.
+ * let y = (i + 1) * 33;
+ *
+ * // Display the match.
+ * text(matches[i], 50, y);
+ * }
+ *
+ * describe(
+ * 'The text "p5" and "c1" written on separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textSize(16);
+ *
+ * // Create a number variable.
+ * let number = 123.45;
+ *
+ * // Display the number as a string.
+ * let formatted = nf(number);
+ * text(formatted, 20, 25);
+ *
+ * // Display the number with four digits
+ * // to the left of the decimal.
+ * let left = nf(number, 4);
+ * text(left, 20, 50);
+ *
+ * // Display the number with four digits
+ * // to the left of the decimal and one
+ * // to the right.
+ * let right = nf(number, 4, 1);
+ * text(right, 20, 75);
+ *
+ * describe(
+ * 'The numbers "123.45", "0123.45", and "0123.5" written on three separate lines. The text is in black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textSize(16);
+ *
+ * // Create a number variable.
+ * let number = 12345;
+ *
+ * // Display the number as a string.
+ * let commas = nfc(number);
+ * text(commas, 15, 33);
+ *
+ * // Display the number with four digits
+ * // to the left of the decimal.
+ * let decimals = nfc(number, 2);
+ * text(decimals, 15, 67);
+ *
+ * describe(
+ * 'The numbers "12,345" and "12,345.00" written on separate lines. The text is in black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of numbers.
+ * let numbers = [12345, 6789];
+ *
+ * // Convert the numbers to formatted strings.
+ * let formatted = nfc(numbers);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(14);
+ *
+ * // Iterate over the array.
+ * for (let i = 0; i < formatted.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 33;
+ *
+ * // Display the original and formatted numbers.
+ * text(`${numbers[i]} : ${formatted[i]}`, 50, y);
+ * }
+ *
+ * describe(
+ * 'The text "12345 : 12,345" and "6789 : 6,789" written on two separate lines. The text is in black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create number variables.
+ * let positive = 123;
+ * let negative = -123;
+ *
+ * // Convert the positive number to a formatted string.
+ * let p = nfp(positive);
+ *
+ * // Convert the negative number to a formatted string
+ * // with four digits to the left of the decimal
+ * // and two digits to the right of the decimal.
+ * let n = nfp(negative, 4, 2);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(14);
+ *
+ * // Display the original and formatted numbers.
+ * text(`${positive} : ${p}`, 50, 33);
+ * text(`${negative} : ${n}`, 50, 67);
+ *
+ * describe(
+ * 'The text "123 : +123" and "-123 : -123.00" written on separate lines. The text is in black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create number variables.
+ * let numbers = [123, -4.56];
+ *
+ * // Convert the numbers to formatted strings
+ * // with four digits to the left of the decimal
+ * // and one digit to the right of the decimal.
+ * let formatted = nfp(numbers, 4, 1);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(14);
+ *
+ * // Iterate over the array.
+ * for (let i = 0; i < formatted.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 33;
+ *
+ * // Display the original and formatted numbers.
+ * text(`${numbers[i]} : ${formatted[i]}`, 50, y);
+ * }
+ *
+ * describe(
+ * 'The text "123 : +0123.0" and "-4.56 : 00-4.6" written on separate lines. The text is in black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create number variables.
+ * let positive = 123;
+ * let negative = -123;
+ *
+ * // Convert the positive number to a formatted string.
+ * let formatted = nfs(positive);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(16);
+ *
+ * // Display the negative number and the formatted positive number.
+ * text(negative, 50, 33);
+ * text(formatted, 50, 67);
+ *
+ * describe(
+ * 'The numbers -123 and 123 written on separate lines. The numbers align vertically. The text is in black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a number variable.
+ * let number = 123.45;
+ *
+ * // Convert the positive number to a formatted string.
+ * // Use four digits to the left of the decimal and
+ * // one digit to the right.
+ * let formatted = nfs(number, 4, 1);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(16);
+ *
+ * // Display a negative version of the number and
+ * // the formatted positive version.
+ * text('-0123.5', 50, 33);
+ * text(formatted, 50, 67);
+ *
+ * describe(
+ * 'The numbers "-0123.5" and "0123.5" written on separate lines. The numbers align vertically. The text is in black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let string = 'rock...paper...scissors';
+ *
+ * // Split the string at each ...
+ * let words = split(string, '...');
+ *
+ * // Print the array to the console:
+ * // ["rock", "paper", "scissors"]
+ * print(words);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(16);
+ *
+ * // Iterate over the words array.
+ * for (let i = 0; i < words.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 25;
+ *
+ * // Display the word.
+ * text(words[i], 50, y);
+ * }
+ *
+ * describe(
+ * 'The words "rock", "paper", and "scissors" written on separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let string = 'rock paper scissors shoot';
+ *
+ * // Split the string at each space.
+ * let words = splitTokens(string);
+ *
+ * // Print the array to the console.
+ * print(words);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Iterate over the words array.
+ * for (let i = 0; i < words.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 20;
+ *
+ * // Display the word.
+ * text(words[i], 50, y);
+ * }
+ *
+ * describe(
+ * 'The words "rock", "paper", "scissors", and "shoot" written on separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let string = 'rock...paper...scissors...shoot';
+ *
+ * // Split the string at each ...
+ * let words = splitTokens(string, '...');
+ *
+ * // Print the array to the console.
+ * print(words);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Iterate over the words array.
+ * for (let i = 0; i < words.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 20;
+ *
+ * // Display the word.
+ * text(words[i], 50, y);
+ * }
+ *
+ * describe(
+ * 'The words "rock", "paper", "scissors", and "shoot" written on separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let string = 'rock;paper,scissors...shoot';
+ *
+ * // Split the string at each semicolon, comma, or ...
+ * let words = splitTokens(string, [';', ',', '...']);
+ *
+ * // Print the array to the console.
+ * print(words);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(12);
+ *
+ * // Iterate over the words array.
+ * for (let i = 0; i < words.length; i += 1) {
+ *
+ * // Calculate the y-coordinate.
+ * let y = (i + 1) * 20;
+ *
+ * // Display the word.
+ * text(words[i], 50, y);
+ * }
+ *
+ * describe(
+ * 'The words "rock", "paper", "scissors", and "shoot" written on separate lines. The text is black on a gray background.'
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create a string variable.
+ * let string = ' p5*js ';
+ *
+ * // Trim the whitespace.
+ * let trimmed = trim(string);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textSize(16);
+ *
+ * // Display the text.
+ * text(`Hello, ${trimmed}!`, 50, 50);
+ *
+ * describe('The text "Hello, p5*js!" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Create an array of strings.
+ * let strings = [' wide ', '\n open ', '\n spaces '];
+ *
+ * // Trim the whitespace.
+ * let trimmed = trim(strings);
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont('Courier New');
+ * textSize(10);
+ *
+ * // Display the text.
+ * text(`${trimmed[0]} ${trimmed[1]} ${trimmed[2]}`, 50, 50);
+ *
+ * describe('The text "wide open spaces" written in black on a gray background.');
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the current day.
+ * let d = day();
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textSize(12);
+ * textFont('Courier New');
+ *
+ * // Display the day.
+ * text(`Current day: ${d}`, 20, 50, 60);
+ *
+ * describe(`The text 'Current day: ${d}' written in black on a gray background.`);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the current hour.
+ * let h = hour();
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textSize(12);
+ * textFont('Courier New');
+ *
+ * // Display the hour.
+ * text(`Current hour: ${h}`, 20, 50, 60);
+ *
+ * describe(`The text 'Current hour: ${h}' written in black on a gray background.`);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the current minute.
+ * let m = minute();
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textSize(12);
+ * textFont('Courier New');
+ *
+ * // Display the minute.
+ * text(`Current minute: ${m}`, 10, 50, 80);
+ *
+ * describe(`The text 'Current minute: ${m}' written in black on a gray background.`);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the number of milliseconds the sketch has run.
+ * let ms = millis();
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textSize(10);
+ * textFont('Courier New');
+ *
+ * // Display how long it took setup() to be called.
+ * text(`Startup time: ${round(ms, 2)} ms`, 5, 50, 90);
+ *
+ * describe(
+ * `The text 'Startup time: ${round(ms, 2)} ms' written in black on a gray background.`
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('The text "Running time: S sec" written in black on a gray background. The number S increases as the sketch runs.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Get the number of seconds the sketch has run.
+ * let s = millis() / 1000;
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textSize(10);
+ * textFont('Courier New');
+ *
+ * // Display how long the sketch has run.
+ * text(`Running time: ${nf(s, 1, 1)} sec`, 5, 50, 90);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * describe('A white circle oscillates left and right on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Get the number of seconds the sketch has run.
+ * let s = millis() / 1000;
+ *
+ * // Calculate an x-coordinate.
+ * let x = 30 * sin(s) + 50;
+ *
+ * // Draw the circle.
+ * circle(x, 50, 30);
+ * }
+ *
+ *
+ * // Load the GeoJSON.
+ * function preload() {
+ * loadJSON('https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the number of milliseconds the sketch has run.
+ * let ms = millis();
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textFont('Courier New');
+ * textSize(11);
+ *
+ * // Display how long it took to load the data.
+ * text(`It took ${round(ms, 2)} ms to load the data`, 5, 50, 100);
+ *
+ * describe(
+ * `The text "It took ${round(ms, 2)} ms to load the data" written in black on a gray background.`
+ * );
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the current month.
+ * let m = month();
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textSize(12);
+ * textFont('Courier New');
+ *
+ * // Display the month.
+ * text(`Current month: ${m}`, 10, 50, 80);
+ *
+ * describe(`The text 'Current month: ${m}' written in black on a gray background.`);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the current second.
+ * let s = second();
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textSize(12);
+ * textFont('Courier New');
+ *
+ * // Display the second.
+ * text(`Current second: ${s}`, 10, 50, 80);
+ *
+ * describe(`The text 'Current second: ${s}' written in black on a gray background.`);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ *
+ * background(200);
+ *
+ * // Get the current year.
+ * let y = year();
+ *
+ * // Style the text.
+ * textAlign(LEFT, CENTER);
+ * textSize(12);
+ * textFont('Courier New');
+ *
+ * // Display the year.
+ * text(`Current year: ${y}`, 10, 50, 80);
+ *
+ * describe(`The text 'Current year: ${y}' written in black on a gray background.`);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Start building the p5.Geometry object.
+ * beginGeometry();
+ *
+ * // Add a cone.
+ * cone();
+ *
+ * // Stop building the p5.Geometry object.
+ * shape = endGeometry();
+ *
+ * describe('A white cone drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the p5.Geometry object.
+ * noStroke();
+ *
+ * // Draw the p5.Geometry object.
+ * model(shape);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the p5.Geometry object.
+ * createArrow();
+ *
+ * describe('A white arrow drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the p5.Geometry object.
+ * noStroke();
+ *
+ * // Draw the p5.Geometry object.
+ * model(shape);
+ * }
+ *
+ * function createArrow() {
+ * // Start building the p5.Geometry object.
+ * beginGeometry();
+ *
+ * // Add shapes.
+ * push();
+ * rotateX(PI);
+ * cone(10);
+ * translate(0, -10, 0);
+ * cylinder(3, 20);
+ * pop();
+ *
+ * // Stop building the p5.Geometry object.
+ * shape = endGeometry();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let blueArrow;
+ * let redArrow;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the arrows.
+ * redArrow = createArrow('red');
+ * blueArrow = createArrow('blue');
+ *
+ * describe('A red arrow and a blue arrow drawn on a gray background. The blue arrow rotates slowly.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the arrows.
+ * noStroke();
+ *
+ * // Draw the red arrow.
+ * model(redArrow);
+ *
+ * // Translate and rotate the coordinate system.
+ * translate(30, 0, 0);
+ * rotateZ(frameCount * 0.01);
+ *
+ * // Draw the blue arrow.
+ * model(blueArrow);
+ * }
+ *
+ * function createArrow(fillColor) {
+ * // Start building the p5.Geometry object.
+ * beginGeometry();
+ *
+ * fill(fillColor);
+ *
+ * // Add shapes to the p5.Geometry object.
+ * push();
+ * rotateX(PI);
+ * cone(10);
+ * translate(0, -10, 0);
+ * cylinder(3, 20);
+ * pop();
+ *
+ * // Stop building the p5.Geometry object.
+ * let shape = endGeometry();
+ *
+ * return shape;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let button;
+ * let particles;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a button to reset the particle system.
+ * button = createButton('Reset');
+ *
+ * // Call resetModel() when the user presses the button.
+ * button.mousePressed(resetModel);
+ *
+ * // Add the original set of particles.
+ * resetModel();
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the particles.
+ * noStroke();
+ *
+ * // Draw the particles.
+ * model(particles);
+ * }
+ *
+ * function resetModel() {
+ * // If the p5.Geometry object has already been created,
+ * // free those resources.
+ * if (particles) {
+ * freeGeometry(particles);
+ * }
+ *
+ * // Create a new p5.Geometry object with random spheres.
+ * particles = createParticles();
+ * }
+ *
+ * function createParticles() {
+ * // Start building the p5.Geometry object.
+ * beginGeometry();
+ *
+ * // Add shapes.
+ * for (let i = 0; i < 60; i += 1) {
+ * // Calculate random coordinates.
+ * let x = randomGaussian(0, 20);
+ * let y = randomGaussian(0, 20);
+ * let z = randomGaussian(0, 20);
+ *
+ * push();
+ * // Translate to the particle's coordinates.
+ * translate(x, y, z);
+ * // Draw the particle.
+ * sphere(5);
+ * pop();
+ * }
+ *
+ * // Stop building the p5.Geometry object.
+ * let shape = endGeometry();
+ *
+ * return shape;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Start building the p5.Geometry object.
+ * beginGeometry();
+ *
+ * // Add a cone.
+ * cone();
+ *
+ * // Stop building the p5.Geometry object.
+ * shape = endGeometry();
+ *
+ * describe('A white cone drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the p5.Geometry object.
+ * noStroke();
+ *
+ * // Draw the p5.Geometry object.
+ * model(shape);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the p5.Geometry object.
+ * createArrow();
+ *
+ * describe('A white arrow drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the p5.Geometry object.
+ * noStroke();
+ *
+ * // Draw the p5.Geometry object.
+ * model(shape);
+ * }
+ *
+ * function createArrow() {
+ * // Start building the p5.Geometry object.
+ * beginGeometry();
+ *
+ * // Add shapes.
+ * push();
+ * rotateX(PI);
+ * cone(10);
+ * translate(0, -10, 0);
+ * cylinder(3, 20);
+ * pop();
+ *
+ * // Stop building the p5.Geometry object.
+ * shape = endGeometry();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let blueArrow;
+ * let redArrow;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the arrows.
+ * redArrow = createArrow('red');
+ * blueArrow = createArrow('blue');
+ *
+ * describe('A red arrow and a blue arrow drawn on a gray background. The blue arrow rotates slowly.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the arrows.
+ * noStroke();
+ *
+ * // Draw the red arrow.
+ * model(redArrow);
+ *
+ * // Translate and rotate the coordinate system.
+ * translate(30, 0, 0);
+ * rotateZ(frameCount * 0.01);
+ *
+ * // Draw the blue arrow.
+ * model(blueArrow);
+ * }
+ *
+ * function createArrow(fillColor) {
+ * // Start building the p5.Geometry object.
+ * beginGeometry();
+ *
+ * fill(fillColor);
+ *
+ * // Add shapes to the p5.Geometry object.
+ * push();
+ * rotateX(PI);
+ * cone(10);
+ * translate(0, -10, 0);
+ * cylinder(3, 20);
+ * pop();
+ *
+ * // Stop building the p5.Geometry object.
+ * let shape = endGeometry();
+ *
+ * return shape;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let button;
+ * let particles;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a button to reset the particle system.
+ * button = createButton('Reset');
+ *
+ * // Call resetModel() when the user presses the button.
+ * button.mousePressed(resetModel);
+ *
+ * // Add the original set of particles.
+ * resetModel();
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the particles.
+ * noStroke();
+ *
+ * // Draw the particles.
+ * model(particles);
+ * }
+ *
+ * function resetModel() {
+ * // If the p5.Geometry object has already been created,
+ * // free those resources.
+ * if (particles) {
+ * freeGeometry(particles);
+ * }
+ *
+ * // Create a new p5.Geometry object with random spheres.
+ * particles = createParticles();
+ * }
+ *
+ * function createParticles() {
+ * // Start building the p5.Geometry object.
+ * beginGeometry();
+ *
+ * // Add shapes.
+ * for (let i = 0; i < 60; i += 1) {
+ * // Calculate random coordinates.
+ * let x = randomGaussian(0, 20);
+ * let y = randomGaussian(0, 20);
+ * let z = randomGaussian(0, 20);
+ *
+ * push();
+ * // Translate to the particle's coordinates.
+ * translate(x, y, z);
+ * // Draw the particle.
+ * sphere(5);
+ * pop();
+ * }
+ *
+ * // Stop building the p5.Geometry object.
+ * let shape = endGeometry();
+ *
+ * return shape;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the p5.Geometry object.
+ * shape = buildGeometry(createShape);
+ *
+ * describe('A white cone drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the p5.Geometry object.
+ * noStroke();
+ *
+ * // Draw the p5.Geometry object.
+ * model(shape);
+ * }
+ *
+ * // Create p5.Geometry object from a single cone.
+ * function createShape() {
+ * cone();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the arrow.
+ * shape = buildGeometry(createArrow);
+ *
+ * describe('A white arrow drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the arrow.
+ * noStroke();
+ *
+ * // Draw the arrow.
+ * model(shape);
+ * }
+ *
+ * function createArrow() {
+ * // Add shapes to the p5.Geometry object.
+ * push();
+ * rotateX(PI);
+ * cone(10);
+ * translate(0, -10, 0);
+ * cylinder(3, 20);
+ * pop();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the p5.Geometry object.
+ * shape = buildGeometry(createArrow);
+ *
+ * describe('Two white arrows drawn on a gray background. The arrow on the right rotates slowly.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the arrows.
+ * noStroke();
+ *
+ * // Draw the p5.Geometry object.
+ * model(shape);
+ *
+ * // Translate and rotate the coordinate system.
+ * translate(30, 0, 0);
+ * rotateZ(frameCount * 0.01);
+ *
+ * // Draw the p5.Geometry object again.
+ * model(shape);
+ * }
+ *
+ * function createArrow() {
+ * // Add shapes to the p5.Geometry object.
+ * push();
+ * rotateX(PI);
+ * cone(10);
+ * translate(0, -10, 0);
+ * cylinder(3, 20);
+ * pop();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let button;
+ * let particles;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a button to reset the particle system.
+ * button = createButton('Reset');
+ *
+ * // Call resetModel() when the user presses the button.
+ * button.mousePressed(resetModel);
+ *
+ * // Add the original set of particles.
+ * resetModel();
+ *
+ * describe('A set of white spheres on a gray background. The spheres are positioned randomly. Their positions reset when the user presses the Reset button.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the particles.
+ * noStroke();
+ *
+ * // Draw the particles.
+ * model(particles);
+ * }
+ *
+ * function resetModel() {
+ * // If the p5.Geometry object has already been created,
+ * // free those resources.
+ * if (particles) {
+ * freeGeometry(particles);
+ * }
+ *
+ * // Create a new p5.Geometry object with random spheres.
+ * particles = buildGeometry(createParticles);
+ * }
+ *
+ * function createParticles() {
+ * for (let i = 0; i < 60; i += 1) {
+ * // Calculate random coordinates.
+ * let x = randomGaussian(0, 20);
+ * let y = randomGaussian(0, 20);
+ * let z = randomGaussian(0, 20);
+ *
+ * push();
+ * // Translate to the particle's coordinates.
+ * translate(x, y, z);
+ * // Draw the particle.
+ * sphere(5);
+ * pop();
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Create a p5.Geometry object.
+ * beginGeometry();
+ * cone();
+ * let shape = endGeometry();
+ *
+ * // Draw the shape.
+ * model(shape);
+ *
+ * // Free the shape's resources.
+ * freeGeometry(shape);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let button;
+ * let particles;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a button to reset the particle system.
+ * button = createButton('Reset');
+ *
+ * // Call resetModel() when the user presses the button.
+ * button.mousePressed(resetModel);
+ *
+ * // Add the original set of particles.
+ * resetModel();
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the particles.
+ * noStroke();
+ *
+ * // Draw the particles.
+ * model(particles);
+ * }
+ *
+ * function resetModel() {
+ * // If the p5.Geometry object has already been created,
+ * // free those resources.
+ * if (particles) {
+ * freeGeometry(particles);
+ * }
+ *
+ * // Create a new p5.Geometry object with random spheres.
+ * particles = buildGeometry(createParticles);
+ * }
+ *
+ * function createParticles() {
+ * for (let i = 0; i < 60; i += 1) {
+ * // Calculate random coordinates.
+ * let x = randomGaussian(0, 20);
+ * let y = randomGaussian(0, 20);
+ * let z = randomGaussian(0, 20);
+ *
+ * push();
+ * // Translate to the particle's coordinates.
+ * translate(x, y, z);
+ * // Draw the particle.
+ * sphere(5);
+ * pop();
+ * }
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white plane on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the plane.
+ * plane();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white plane on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the plane.
+ * // Set its width and height to 30.
+ * plane(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white plane on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the plane.
+ * // Set its width to 30 and height to 50.
+ * plane(30, 50);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white box on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white box on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the box.
+ * // Set its width and height to 30.
+ * box(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white box on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the box.
+ * // Set its width to 30 and height to 50.
+ * box(30, 50);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white box on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the box.
+ * // Set its width to 30, height to 50, and depth to 10.
+ * box(30, 50, 10);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white sphere on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the sphere.
+ * sphere();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white sphere on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the sphere.
+ * // Set its radius to 30.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white sphere on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the sphere.
+ * // Set its radius to 30.
+ * // Set its detailX to 6.
+ * sphere(30, 6);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white sphere on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the sphere.
+ * // Set its radius to 30.
+ * // Set its detailX to 24.
+ * // Set its detailY to 4.
+ * sphere(30, 24, 4);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cylinder on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cylinder.
+ * cylinder();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cylinder on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cylinder.
+ * // Set its radius and height to 30.
+ * cylinder(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cylinder on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cylinder.
+ * // Set its radius to 30 and height to 50.
+ * cylinder(30, 50);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white box on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cylinder.
+ * // Set its radius to 30 and height to 50.
+ * // Set its detailX to 5.
+ * cylinder(30, 50, 5);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cylinder on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cylinder.
+ * // Set its radius to 30 and height to 50.
+ * // Set its detailX to 24 and detailY to 2.
+ * cylinder(30, 50, 24, 2);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cylinder on a gray background. Its top is missing.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cylinder.
+ * // Set its radius to 30 and height to 50.
+ * // Set its detailX to 24 and detailY to 1.
+ * // Don't draw its bottom.
+ * cylinder(30, 50, 24, 1, false);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cylinder on a gray background. Its top and bottom are missing.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cylinder.
+ * // Set its radius to 30 and height to 50.
+ * // Set its detailX to 24 and detailY to 1.
+ * // Don't draw its bottom or top.
+ * cylinder(30, 50, 24, 1, false, false);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cone on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cone.
+ * cone();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cone on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cone.
+ * // Set its radius and height to 30.
+ * cone(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cone on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cone.
+ * // Set its radius to 30 and height to 50.
+ * cone(30, 50);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cone on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cone.
+ * // Set its radius to 30 and height to 50.
+ * // Set its detailX to 5.
+ * cone(30, 50, 5);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white pyramid on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cone.
+ * // Set its radius to 30 and height to 50.
+ * // Set its detailX to 5.
+ * cone(30, 50, 5);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cone on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cone.
+ * // Set its radius to 30 and height to 50.
+ * // Set its detailX to 24 and detailY to 2.
+ * cone(30, 50, 24, 2);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cone on a gray background. Its base is missing.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the cone.
+ * // Set its radius to 30 and height to 50.
+ * // Set its detailX to 24 and detailY to 1.
+ * // Don't draw its base.
+ * cone(30, 50, 24, 1, false);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white sphere on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the ellipsoid.
+ * // Set its radiusX to 30.
+ * ellipsoid(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white ellipsoid on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the ellipsoid.
+ * // Set its radiusX to 30.
+ * // Set its radiusY to 40.
+ * ellipsoid(30, 40);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white ellipsoid on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the ellipsoid.
+ * // Set its radiusX to 30.
+ * // Set its radiusY to 40.
+ * // Set its radiusZ to 50.
+ * ellipsoid(30, 40, 50);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white ellipsoid on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the ellipsoid.
+ * // Set its radiusX to 30.
+ * // Set its radiusY to 40.
+ * // Set its radiusZ to 50.
+ * // Set its detailX to 4.
+ * ellipsoid(30, 40, 50, 4);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white ellipsoid on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the ellipsoid.
+ * // Set its radiusX to 30.
+ * // Set its radiusY to 40.
+ * // Set its radiusZ to 50.
+ * // Set its detailX to 4.
+ * // Set its detailY to 3.
+ * ellipsoid(30, 40, 50, 4, 3);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white torus on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the torus.
+ * torus();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white torus on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the torus.
+ * // Set its radius to 30.
+ * torus(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white torus on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the torus.
+ * // Set its radius to 30 and tubeRadius to 15.
+ * torus(30, 15);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white torus on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the torus.
+ * // Set its radius to 30 and tubeRadius to 15.
+ * // Set its detailX to 5.
+ * torus(30, 15, 5);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white torus on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the torus.
+ * // Set its radius to 30 and tubeRadius to 15.
+ * // Set its detailX to 5.
+ * // Set its detailY to 3.
+ * torus(30, 15, 5, 3);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw() {
+ * background(50);
+ * stroke(255);
+ * strokeWeight(4);
+ * point(25, 0);
+ * strokeWeight(3);
+ * point(-25, 0);
+ * strokeWeight(2);
+ * point(0, 25);
+ * strokeWeight(1);
+ * point(0, -25);
+ * }
+ *
+ *
+ * //draw a line
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * // Use fill instead of stroke to change the color of shape.
+ * fill(255, 0, 0);
+ * line(10, 10, 0, 60, 60, 20);
+ * }
+ *
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Rest of sketch.
+ * }
+ *
+ *
+ * Left-clicking and dragging or swipe motion will rotate the camera position
+ * about the center of the sketch. Right-clicking and dragging or multi-swipe
+ * will pan the camera position without rotation. Using the mouse wheel
+ * (scrolling) or pinch in/out will move the camera further or closer from the
+ * center of the sketch.
+ *
+ * The first three parameters, `sensitivityX`, `sensitivityY`, and
+ * `sensitivityZ`, are optional. They’re numbers that set the sketch’s
+ * sensitivity to movement along each axis. For example, calling
+ * `orbitControl(1, 2, -1)` keeps movement along the x-axis at its default
+ * value, makes the sketch twice as sensitive to movement along the y-axis,
+ * and reverses motion along the z-axis. By default, all sensitivity values
+ * are 1.
+ *
+ * The fourth parameter, `options`, is also optional. It’s an object that
+ * changes the behavior of orbiting. For example, calling
+ * `orbitControl(1, 1, 1, options)` keeps the default sensitivity values while
+ * changing the behaviors set with `options`. The object can have the
+ * following properties:
+ *
+ *
+ * let options = {
+ * // Setting this to false makes mobile interactions smoother by
+ * // preventing accidental interactions with the page while orbiting.
+ * // By default, it's true.
+ * disableTouchActions: true,
+ *
+ * // Setting this to true makes the camera always rotate in the
+ * // direction the mouse/touch is moving.
+ * // By default, it's false.
+ * freeRotation: false
+ * };
+ *
+ * orbitControl(1, 1, 1, options);
+ *
+ *
+ * @method orbitControl
+ * @for p5
+ * @param {Number} [sensitivityX] sensitivity to movement along the x-axis. Defaults to 1.
+ * @param {Number} [sensitivityY] sensitivity to movement along the y-axis. Defaults to 1.
+ * @param {Number} [sensitivityZ] sensitivity to movement along the z-axis. Defaults to 1.
+ * @param {Object} [options] object with two optional properties, `disableTouchActions`
+ * and `freeRotation`. Both are `Boolean`s. `disableTouchActions`
+ * defaults to `true` and `freeRotation` defaults to `false`.
+ * @chainable
+ *
+ * @example
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A multicolor box on a gray background. The camera angle changes when the user interacts using a mouse, trackpad, or touchscreen.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the box.
+ * normalMaterial();
+ *
+ * // Draw the box.
+ * box(30, 50);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A multicolor box on a gray background. The camera angle changes when the user interacts using a mouse, trackpad, or touchscreen.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * // Make the interactions 3X sensitive.
+ * orbitControl(3, 3, 3);
+ *
+ * // Style the box.
+ * normalMaterial();
+ *
+ * // Draw the box.
+ * box(30, 50);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A multicolor box on a gray background. The camera angle changes when the user interacts using a mouse, trackpad, or touchscreen.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Create an options object.
+ * let options = {
+ * disableTouchActions: false,
+ * freeRotation: true
+ * };
+ *
+ * // Enable orbiting with the mouse.
+ * // Prevent accidental touch actions on touchscreen devices
+ * // and enable free rotation.
+ * orbitControl(1, 1, 1, options);
+ *
+ * // Style the box.
+ * normalMaterial();
+ *
+ * // Draw the box.
+ * box(30, 50);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Enable debug mode.
+ * debugMode();
+ *
+ * describe('A multicolor box on a gray background. A grid and axes icon are displayed near the box.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the box.
+ * normalMaterial();
+ *
+ * // Draw the box.
+ * box(20, 40);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Enable debug mode.
+ * // Only display the axes icon.
+ * debugMode(AXES);
+ *
+ * describe('A multicolor box on a gray background. A grid and axes icon are displayed near the box.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the box.
+ * normalMaterial();
+ *
+ * // Draw the box.
+ * box(20, 40);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Enable debug mode.
+ * // Only display the grid and customize it:
+ * // - size: 50
+ * // - divisions: 10
+ * // - offsets: 0, 20, 0
+ * debugMode(GRID, 50, 10, 0, 20, 0);
+ *
+ * describe('A multicolor box on a gray background. A grid is displayed below the box.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the box.
+ * normalMaterial();
+ *
+ * // Draw the box.
+ * box(20, 40);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Enable debug mode.
+ * // Display the grid and axes icon and customize them:
+ * // Grid
+ * // ----
+ * // - size: 50
+ * // - divisions: 10
+ * // - offsets: 0, 20, 0
+ * // Axes
+ * // ----
+ * // - size: 50
+ * // - offsets: 0, 0, 0
+ * debugMode(50, 10, 0, 20, 0, 50, 0, 0, 0);
+ *
+ * describe('A multicolor box on a gray background. A grid is displayed below the box. An axes icon is displayed at the center of the box.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the box.
+ * normalMaterial();
+ *
+ * // Draw the box.
+ * box(20, 40);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Enable debug mode.
+ * debugMode();
+ *
+ * describe('A multicolor box on a gray background. A grid and axes icon are displayed near the box. They disappear when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the box.
+ * normalMaterial();
+ *
+ * // Draw the box. box(20, 40);
+ * }
+ *
+ * // Disable debug mode when the user double-clicks.
+ * function doubleClicked() {
+ * noDebugMode();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ * // Double-click the canvas to turn on the light.
+ *
+ * let isLit = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A sphere drawn against a gray background. The sphere appears to change color when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Control the light.
+ * if (isLit === true) {
+ * // Use a grayscale value of 80.
+ * ambientLight(80);
+ * }
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ * // Turn on the ambient light when the user double-clicks.
+ * function doubleClicked() {
+ * isLit = true;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A faded magenta sphere drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * // Use a p5.Color object.
+ * let c = color('orchid');
+ * ambientLight(c);
+ *
+ * // Draw the sphere.
+ * sphere();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A faded magenta sphere drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * // Use a CSS color string.
+ * ambientLight('#DA70D6');
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A faded magenta sphere drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * // Use RGB values
+ * ambientLight(218, 112, 214);
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white sphere drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // No specular color.
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ * // Double-click the canvas to add a point light.
+ *
+ * let isLit = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A sphere drawn on a gray background. A spotlight starts shining when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the sphere.
+ * noStroke();
+ * specularColor(100);
+ * specularMaterial(255, 255, 255);
+ *
+ * // Control the light.
+ * if (isLit === true) {
+ * // Add a white point light from the top-right.
+ * pointLight(255, 255, 255, 30, -20, 40);
+ * }
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ * // Turn on the point light when the user double-clicks.
+ * function doubleClicked() {
+ * isLit = true;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A black sphere drawn on a gray background. An area on the surface of the sphere is highlighted in blue.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Add a specular highlight.
+ * // Use a p5.Color object.
+ * let c = color('dodgerblue');
+ * specularColor(c);
+ *
+ * // Add a white point light from the top-right.
+ * pointLight(255, 255, 255, 30, -20, 40);
+ *
+ * // Style the sphere.
+ * noStroke();
+ *
+ * // Add a white specular material.
+ * specularMaterial(255, 255, 255);
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A black sphere drawn on a gray background. An area on the surface of the sphere is highlighted in blue.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Add a specular highlight.
+ * // Use a CSS color string.
+ * specularColor('#1E90FF');
+ *
+ * // Add a white point light from the top-right.
+ * pointLight(255, 255, 255, 30, -20, 40);
+ *
+ * // Style the sphere.
+ * noStroke();
+ *
+ * // Add a white specular material.
+ * specularMaterial(255, 255, 255);
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A black sphere drawn on a gray background. An area on the surface of the sphere is highlighted in blue.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Add a specular highlight.
+ * // Use RGB values.
+ * specularColor(30, 144, 255);
+ *
+ * // Add a white point light from the top-right.
+ * pointLight(255, 255, 255, 30, -20, 40);
+ *
+ * // Style the sphere.
+ * noStroke();
+ *
+ * // Add a white specular material.
+ * specularMaterial(255, 255, 255);
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ * // Double-click to turn on the directional light.
+ *
+ * let isLit = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A sphere drawn on a gray background. A red light starts shining from above when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Control the light.
+ * if (isLit === true) {
+ * // Add a red directional light from above.
+ * // Use RGB values and XYZ directions.
+ * directionalLight(255, 0, 0, 0, 1, 0);
+ * }
+ *
+ * // Style the sphere.
+ * noStroke();
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A sphere drawn on a gray background. The top of the sphere appears bright red. The color gets darker toward the bottom.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Add a red directional light from above.
+ * // Use a p5.Color object and XYZ directions.
+ * let c = color(255, 0, 0);
+ * directionalLight(c, 0, 1, 0);
+ *
+ * // Style the sphere.
+ * noStroke();
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A sphere drawn on a gray background. The top of the sphere appears bright red. The color gets darker toward the bottom.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Add a red directional light from above.
+ * // Use a p5.Color object and a p5.Vector object.
+ * let c = color(255, 0, 0);
+ * let lightDir = createVector(0, 1, 0);
+ * directionalLight(c, lightDir);
+ *
+ * // Style the sphere.
+ * noStroke();
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ * // Double-click to turn on the point light.
+ *
+ * let isLit = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A sphere drawn on a gray background. A red light starts shining from above when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Control the light.
+ * if (isLit === true) {
+ * // Add a red point light from above.
+ * // Use RGB values and XYZ coordinates.
+ * pointLight(255, 0, 0, 0, -150, 0);
+ * }
+ *
+ * // Style the sphere.
+ * noStroke();
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ * // Turn on the point light when the user double-clicks.
+ * function doubleClicked() {
+ * isLit = true;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A sphere drawn on a gray background. The top of the sphere appears bright red. The color gets darker toward the bottom.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Add a red point light from above.
+ * // Use a p5.Color object and XYZ directions.
+ * let c = color(255, 0, 0);
+ * pointLight(c, 0, -150, 0);
+ *
+ * // Style the sphere.
+ * noStroke();
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A sphere drawn on a gray background. The top of the sphere appears bright red. The color gets darker toward the bottom.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Add a red point light from above.
+ * // Use a p5.Color object and a p5.Vector object.
+ * let c = color(255, 0, 0);
+ * let lightPos = createVector(0, -150, 0);
+ * pointLight(c, lightPos);
+ *
+ * // Style the sphere.
+ * noStroke();
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('Four spheres arranged in a square and drawn on a gray background. The spheres appear bright red toward the center of the square. The color gets darker toward the corners of the square.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Add a red point light that points to the center of the scene.
+ * // Use a p5.Color object and a p5.Vector object.
+ * let c = color(255, 0, 0);
+ * let lightPos = createVector(0, 0, 65);
+ * pointLight(c, lightPos);
+ *
+ * // Style the spheres.
+ * noStroke();
+ *
+ * // Draw a sphere up and to the left.
+ * push();
+ * translate(-25, -25, 25);
+ * sphere(10);
+ * pop();
+ *
+ * // Draw a box up and to the right.
+ * push();
+ * translate(25, -25, 25);
+ * sphere(10);
+ * pop();
+ *
+ * // Draw a sphere down and to the left.
+ * push();
+ * translate(-25, 25, 25);
+ * sphere(10);
+ * pop();
+ *
+ * // Draw a box down and to the right.
+ * push();
+ * translate(25, 25, 25);
+ * sphere(10);
+ * pop();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let img;
+ *
+ * // Load an image and create a p5.Image object.
+ * function preload() {
+ * img = loadImage('assets/outdoor_spheremap.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A sphere floating above a landscape. The surface of the sphere reflects the landscape.');
+ * }
+ *
+ * function draw() {
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the image as a panorama (360˚ background).
+ * panorama(img);
+ *
+ * // Add a soft ambient light.
+ * ambientLight(50);
+ *
+ * // Add light from the image.
+ * imageLight(img);
+ *
+ * // Style the sphere.
+ * specularMaterial(20);
+ * shininess(100);
+ * noStroke();
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let img;
+ *
+ * // Load an image and create a p5.Image object.
+ * function preload() {
+ * img = loadImage('assets/outdoor_spheremap.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100 ,100 ,WEBGL);
+ *
+ * describe('A sphere floating above a landscape. The surface of the sphere reflects the landscape. The full landscape is viewable in 3D as the user drags the mouse.');
+ * }
+ *
+ * function draw() {
+ * // Add the panorama.
+ * panorama(img);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Use the image as a light source.
+ * imageLight(img);
+ *
+ * // Style the sphere.
+ * noStroke();
+ * specularMaterial(50);
+ * shininess(200);
+ * metalness(100);
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ * // Double-click to turn on the lights.
+ *
+ * let isLit = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white box drawn against a gray background. The quality of the light changes when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Control the lights.
+ * if (isLit === true) {
+ * lights();
+ * }
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ * // Turn on the lights when the user double-clicks.
+ * function doubleClicked() {
+ * isLit = true;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white box drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * ambientLight(128, 128, 128);
+ * directionalLight(128, 128, 128, 0, 0, -1);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ * // Double-click to change the falloff rate.
+ *
+ * let useFalloff = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A sphere drawn against a gray background. The intensity of the light changes when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Set the light falloff.
+ * if (useFalloff === true) {
+ * lightFalloff(2, 0, 0);
+ * }
+ *
+ * // Add a white point light from the front.
+ * pointLight(255, 255, 255, 0, 0, 100);
+ *
+ * // Style the sphere.
+ * noStroke();
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ * // Change the falloff value when the user double-clicks.
+ * function doubleClicked() {
+ * useFalloff = true;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ * // Double-click to adjust the spotlight.
+ *
+ * let isLit = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white sphere drawn on a gray background. A red spotlight starts shining when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Control the spotlight.
+ * if (isLit === true) {
+ * // Add a red spot light that shines into the screen.
+ * // Set its angle to PI / 32 radians.
+ * spotLight(255, 0, 0, 0, 0, 100, 0, 0, -1, PI / 32);
+ * }
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ * // Turn on the spotlight when the user double-clicks.
+ * function doubleClicked() {
+ * isLit = true;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ * // Double-click to adjust the spotlight.
+ *
+ * let isLit = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white sphere drawn on a gray background. A red spotlight starts shining when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Control the spotlight.
+ * if (isLit === true) {
+ * // Add a red spot light that shines into the screen.
+ * // Set its angle to PI / 3 radians (default).
+ * // Set its concentration to 1000.
+ * let c = color(255, 0, 0);
+ * let position = createVector(0, 0, 100);
+ * let direction = createVector(0, 0, -1);
+ * spotLight(c, position, direction, PI / 3, 1000);
+ * }
+ *
+ * // Draw the sphere.
+ * sphere(30);
+ * }
+ *
+ * // Turn on the spotlight when the user double-clicks.
+ * function doubleClicked() {
+ * isLit = true;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('Two spheres drawn against a gray background. The top sphere is white and the bottom sphere is red.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the spheres.
+ * noStroke();
+ *
+ * // Draw the top sphere.
+ * push();
+ * translate(0, -25, 0);
+ * sphere(20);
+ * pop();
+ *
+ * // Turn off the lights.
+ * noLights();
+ *
+ * // Add a red directional light that points into the screen.
+ * directionalLight(255, 0, 0, 0, 0, -1);
+ *
+ * // Draw the bottom sphere.
+ * push();
+ * translate(0, 25, 0);
+ * sphere(20);
+ * pop();
+ * }
+ *
+ *
+ * let options = {
+ * // Enables standardized size scaling during loading if set to true.
+ * normalize: true,
+ *
+ * // Function to call once the model loads.
+ * successCallback: handleModel,
+ *
+ * // Function to call if an error occurs while loading.
+ * failureCallback: handleError,
+ *
+ * // Model's file extension.
+ * fileType: '.stl',
+ *
+ * // Flips the U texture coordinates of the model.
+ * flipU: false,
+ *
+ * // Flips the V texture coordinates of the model.
+ * flipV: false
+ * };
+ *
+ * // Pass the options object to loadModel().
+ * loadModel('assets/model.obj', options);
+ *
+ *
+ * Models can take time to load. Calling `loadModel()` in
+ * preload() ensures models load before they're
+ * used in setup() or draw().
+ *
+ * Note: There’s no support for colored STL files. STL files with color will
+ * be rendered without color.
+ *
+ * @method loadModel
+ * @param {String} path path of the model to be loaded.
+ * @param {Boolean} normalize if `true`, scale the model to fit the canvas.
+ * @param {function(p5.Geometry)} [successCallback] function to call once the model is loaded. Will be passed
+ * the p5.Geometry object.
+ * @param {function(Event)} [failureCallback] function to call if the model fails to load. Will be passed an `Error` event object.
+ * @param {String} [fileType] model’s file extension. Either `'.obj'` or `'.stl'`.
+ * @return {p5.Geometry} the p5.Geometry object
+ *
+ * @example
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * // Load the file and create a p5.Geometry object.
+ * function preload() {
+ * shape = loadModel('assets/teapot.obj');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white teapot drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the shape.
+ * model(shape);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * // Load the file and create a p5.Geometry object.
+ * // Normalize the geometry's size to fit the canvas.
+ * function preload() {
+ * shape = loadModel('assets/teapot.obj', true);
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white teapot drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the shape.
+ * model(shape);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * // Load the file and create a p5.Geometry object.
+ * function preload() {
+ * loadModel('assets/teapot.obj', true, handleModel);
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white teapot drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the shape.
+ * model(shape);
+ * }
+ *
+ * // Set the shape variable and log the geometry's
+ * // ID to the console.
+ * function handleModel(data) {
+ * shape = data;
+ * console.log(shape.gid);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * // Load the file and create a p5.Geometry object.
+ * function preload() {
+ * loadModel('assets/wrong.obj', true, handleModel, handleError);
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white teapot drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the shape.
+ * model(shape);
+ * }
+ *
+ * // Set the shape variable and print the geometry's
+ * // ID to the console.
+ * function handleModel(data) {
+ * shape = data;
+ * console.log(shape.gid);
+ * }
+ *
+ * // Print an error message if the file doesn't load.
+ * function handleError(error) {
+ * console.error('Oops!', error);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * // Load the file and create a p5.Geometry object.
+ * function preload() {
+ * loadModel('assets/teapot.obj', true, handleModel, handleError, '.obj');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white teapot drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the shape.
+ * model(shape);
+ * }
+ *
+ * // Set the shape variable and print the geometry's
+ * // ID to the console.
+ * function handleModel(data) {
+ * shape = data;
+ * console.log(shape.gid);
+ * }
+ *
+ * // Print an error message if the file doesn't load.
+ * function handleError(error) {
+ * console.error('Oops!', error);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ * let options = {
+ * normalize: true,
+ * successCallback: handleModel,
+ * failureCallback: handleError,
+ * fileType: '.obj'
+ * };
+ *
+ * // Load the file and create a p5.Geometry object.
+ * function preload() {
+ * loadModel('assets/teapot.obj', options);
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white teapot drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the shape.
+ * model(shape);
+ * }
+ *
+ * // Set the shape variable and print the geometry's
+ * // ID to the console.
+ * function handleModel(data) {
+ * shape = data;
+ * console.log(shape.gid);
+ * }
+ *
+ * // Print an error message if the file doesn't load.
+ * function handleError(error) {
+ * console.error('Oops!', error);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the p5.Geometry object.
+ * shape = buildGeometry(createShape);
+ *
+ * describe('A white cone drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the p5.Geometry object.
+ * model(shape);
+ * }
+ *
+ * // Create p5.Geometry object from a single cone.
+ * function createShape() {
+ * cone();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the p5.Geometry object.
+ * shape = buildGeometry(createArrow);
+ *
+ * describe('Two white arrows drawn on a gray background. The arrow on the right rotates slowly.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the arrows.
+ * noStroke();
+ *
+ * // Draw the p5.Geometry object.
+ * model(shape);
+ *
+ * // Translate and rotate the coordinate system.
+ * translate(30, 0, 0);
+ * rotateZ(frameCount * 0.01);
+ *
+ * // Draw the p5.Geometry object again.
+ * model(shape);
+ * }
+ *
+ * function createArrow() {
+ * // Add shapes to the p5.Geometry object.
+ * push();
+ * rotateX(PI);
+ * cone(10);
+ * translate(0, -10, 0);
+ * cylinder(3, 20);
+ * pop();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let shape;
+ *
+ * // Load the file and create a p5.Geometry object.
+ * function preload() {
+ * shape = loadModel('assets/octahedron.obj');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white octahedron drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the shape.
+ * model(shape);
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * let mandelbrot;
+ *
+ * // Load the shader and create a p5.Shader object.
+ * function preload() {
+ * mandelbrot = loadShader('assets/shader.vert', 'assets/shader.frag');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Compile and apply the p5.Shader object.
+ * shader(mandelbrot);
+ *
+ * // Set the shader uniform p to an array.
+ * mandelbrot.setUniform('p', [-0.74364388703, 0.13182590421]);
+ *
+ * // Set the shader uniform r to the value 1.5.
+ * mandelbrot.setUniform('r', 1.5);
+ *
+ * // Add a quad as a display surface for the shader.
+ * quad(-1, -1, 1, -1, 1, 1, -1, 1);
+ *
+ * describe('A black fractal image on a magenta background.');
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * let mandelbrot;
+ *
+ * // Load the shader and create a p5.Shader object.
+ * function preload() {
+ * mandelbrot = loadShader('assets/shader.vert', 'assets/shader.frag');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Use the p5.Shader object.
+ * shader(mandelbrot);
+ *
+ * // Set the shader uniform p to an array.
+ * mandelbrot.setUniform('p', [-0.74364388703, 0.13182590421]);
+ *
+ * describe('A fractal image zooms in and out of focus.');
+ * }
+ *
+ * function draw() {
+ * // Set the shader uniform r to a value that oscillates between 0 and 2.
+ * mandelbrot.setUniform('r', sin(frameCount * 0.01) + 1);
+ *
+ * // Add a quad as a display surface for the shader.
+ * quad(-1, -1, 1, -1, 1, 1, -1, 1);
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision highp float;
+ *
+ * void main() {
+ * // Set each pixel's RGBA value to yellow.
+ * gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
+ * }
+ * `;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * let shaderProgram = createShader(vertSrc, fragSrc);
+ *
+ * // Compile and apply the p5.Shader object.
+ * shader(shaderProgram);
+ *
+ * // Style the drawing surface.
+ * noStroke();
+ *
+ * // Add a plane as a drawing surface.
+ * plane(100, 100);
+ *
+ * describe('A yellow square.');
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision highp float;
+ * uniform vec2 p;
+ * uniform float r;
+ * const int numIterations = 500;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vec2 c = p + gl_FragCoord.xy * r;
+ * vec2 z = c;
+ * float n = 0.0;
+ *
+ * for (int i = numIterations; i > 0; i--) {
+ * if (z.x * z.x + z.y * z.y > 4.0) {
+ * n = float(i) / float(numIterations);
+ * break;
+ * }
+ * z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c;
+ * }
+ *
+ * gl_FragColor = vec4(
+ * 0.5 - cos(n * 17.0) / 2.0,
+ * 0.5 - cos(n * 13.0) / 2.0,
+ * 0.5 - cos(n * 23.0) / 2.0,
+ * 1.0
+ * );
+ * }
+ * `;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * let mandelbrot = createShader(vertSrc, fragSrc);
+ *
+ * // Compile and apply the p5.Shader object.
+ * shader(mandelbrot);
+ *
+ * // Set the shader uniform p to an array.
+ * // p is the center point of the Mandelbrot image.
+ * mandelbrot.setUniform('p', [-0.74364388703, 0.13182590421]);
+ *
+ * // Set the shader uniform r to 0.005.
+ * // r is the size of the image in Mandelbrot-space.
+ * mandelbrot.setUniform('r', 0.005);
+ *
+ * // Style the drawing surface.
+ * noStroke();
+ *
+ * // Add a plane as a drawing surface.
+ * plane(100, 100);
+ *
+ * describe('A black fractal image on a magenta background.');
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision highp float;
+ * uniform vec2 p;
+ * uniform float r;
+ * const int numIterations = 500;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vec2 c = p + gl_FragCoord.xy * r;
+ * vec2 z = c;
+ * float n = 0.0;
+ *
+ * for (int i = numIterations; i > 0; i--) {
+ * if (z.x * z.x + z.y * z.y > 4.0) {
+ * n = float(i) / float(numIterations);
+ * break;
+ * }
+ *
+ * z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c;
+ * }
+ *
+ * gl_FragColor = vec4(
+ * 0.5 - cos(n * 17.0) / 2.0,
+ * 0.5 - cos(n * 13.0) / 2.0,
+ * 0.5 - cos(n * 23.0) / 2.0,
+ * 1.0
+ * );
+ * }
+ * `;
+ *
+ * let mandelbrot;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * mandelbrot = createShader(vertSrc, fragSrc);
+ *
+ * // Apply the p5.Shader object.
+ * shader(mandelbrot);
+ *
+ * // Set the shader uniform p to an array.
+ * // p is the center point of the Mandelbrot image.
+ * mandelbrot.setUniform('p', [-0.74364388703, 0.13182590421]);
+ *
+ * describe('A fractal image zooms in and out of focus.');
+ * }
+ *
+ * function draw() {
+ * // Set the shader uniform r to a value that oscillates
+ * // between 0 and 0.005.
+ * // r is the size of the image in Mandelbrot-space.
+ * let radius = 0.005 * (sin(frameCount * 0.01) + 1);
+ * mandelbrot.setUniform('r', radius);
+ *
+ * // Style the drawing surface.
+ * noStroke();
+ *
+ * // Add a plane as a drawing surface.
+ * plane(100, 100);
+ * }
+ *
+ *
+ * function setup() {
+ * let fragSrc = `precision highp float;
+ * void main() {
+ * gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
+ * }`;
+ *
+ * createCanvas(100, 100, WEBGL);
+ * let s = createFilterShader(fragSrc);
+ * filter(s);
+ * describe('a yellow canvas');
+ * }
+ *
+ *
+ * let img, s;
+ * function preload() {
+ * img = loadImage('assets/bricks.jpg');
+ * }
+ * function setup() {
+ * let fragSrc = `precision highp float;
+ *
+ * // x,y coordinates, given from the vertex shader
+ * varying vec2 vTexCoord;
+ *
+ * // the canvas contents, given from filter()
+ * uniform sampler2D tex0;
+ * // other useful information from the canvas
+ * uniform vec2 texelSize;
+ * uniform vec2 canvasSize;
+ * // a custom variable from this sketch
+ * uniform float darkness;
+ *
+ * void main() {
+ * // get the color at current pixel
+ * vec4 color = texture2D(tex0, vTexCoord);
+ * // set the output color
+ * color.b = 1.0;
+ * color *= darkness;
+ * gl_FragColor = vec4(color.rgb, 1.0);
+ * }`;
+ *
+ * createCanvas(100, 100, WEBGL);
+ * s = createFilterShader(fragSrc);
+ * }
+ * function draw() {
+ * image(img, -50, -50);
+ * s.setUniform('darkness', 0.5);
+ * filter(s);
+ * describe('a image of bricks tinted dark blue');
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision highp float;
+ *
+ * void main() {
+ * // Set each pixel's RGBA value to yellow.
+ * gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
+ * }
+ * `;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * let shaderProgram = createShader(vertSrc, fragSrc);
+ *
+ * // Apply the p5.Shader object.
+ * shader(shaderProgram);
+ *
+ * // Style the drawing surface.
+ * noStroke();
+ *
+ * // Add a plane as a drawing surface.
+ * plane(100, 100);
+ *
+ * describe('A yellow square.');
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * let mandelbrot;
+ *
+ * // Load the shader and create a p5.Shader object.
+ * function preload() {
+ * mandelbrot = loadShader('assets/shader.vert', 'assets/shader.frag');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Use the p5.Shader object.
+ * shader(mandelbrot);
+ *
+ * // Set the shader uniform p to an array.
+ * mandelbrot.setUniform('p', [-0.74364388703, 0.13182590421]);
+ *
+ * describe('A fractal image zooms in and out of focus.');
+ * }
+ *
+ * function draw() {
+ * // Set the shader uniform r to a value that oscillates between 0 and 2.
+ * mandelbrot.setUniform('r', sin(frameCount * 0.01) + 1);
+ *
+ * // Add a quad as a display surface for the shader.
+ * quad(-1, -1, 1, -1, 1, 1, -1, 1);
+ * }
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * let redGreen;
+ * let orangeBlue;
+ * let showRedGreen = false;
+ *
+ * // Load the shader and create two separate p5.Shader objects.
+ * function preload() {
+ * redGreen = loadShader('assets/shader.vert', 'assets/shader-gradient.frag');
+ * orangeBlue = loadShader('assets/shader.vert', 'assets/shader-gradient.frag');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Initialize the redGreen shader.
+ * shader(redGreen);
+ *
+ * // Set the redGreen shader's center and background color.
+ * redGreen.setUniform('colorCenter', [1.0, 0.0, 0.0]);
+ * redGreen.setUniform('colorBackground', [0.0, 1.0, 0.0]);
+ *
+ * // Initialize the orangeBlue shader.
+ * shader(orangeBlue);
+ *
+ * // Set the orangeBlue shader's center and background color.
+ * orangeBlue.setUniform('colorCenter', [1.0, 0.5, 0.0]);
+ * orangeBlue.setUniform('colorBackground', [0.226, 0.0, 0.615]);
+ *
+ * describe(
+ * 'The scene toggles between two circular gradients when the user double-clicks. An orange and blue gradient vertically, and red and green gradient moves horizontally.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Update the offset values for each shader.
+ * // Move orangeBlue vertically.
+ * // Move redGreen horizontally.
+ * orangeBlue.setUniform('offset', [0, sin(frameCount * 0.01) + 1]);
+ * redGreen.setUniform('offset', [sin(frameCount * 0.01), 1]);
+ *
+ * if (showRedGreen === true) {
+ * shader(redGreen);
+ * } else {
+ * shader(orangeBlue);
+ * }
+ *
+ * // Style the drawing surface.
+ * noStroke();
+ *
+ * // Add a quad as a drawing surface.
+ * quad(-1, -1, 1, -1, 1, 1, -1, 1);
+ * }
+ *
+ * // Toggle between shaders when the user double-clicks.
+ * function doubleClicked() {
+ * showRedGreen = !showRedGreen;
+ * }
+ *
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * uniform mat4 uProjectionMatrix;
+ * uniform mat4 uModelViewMatrix;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 position = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * position;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision mediump float;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vec2 uv = vTexCoord;
+ * vec3 color = vec3(uv.x, uv.y, min(uv.x + uv.y, 1.0));
+ * gl_FragColor = vec4(color, 1.0);
+ * }
+ * `;
+ *
+ * let myShader;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * myShader = createShader(vertSrc, fragSrc);
+ *
+ * describe(
+ * 'Two rotating cubes on a gray background. The left one has a blue-purple gradient on each face. The right one is red.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw a box using the p5.Shader.
+ * // shader() sets the active shader to myShader.
+ * shader(myShader);
+ * push();
+ * translate(-25, 0, 0);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * box(width / 4);
+ * pop();
+ *
+ * // Draw a box using the default fill shader.
+ * // resetShader() restores the default fill shader.
+ * resetShader();
+ * fill(255, 0, 0);
+ * push();
+ * translate(25, 0, 0);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * box(width / 4);
+ * pop();
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load an image and create a p5.Image object.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A spinning cube with an image of a ceiling on each face.');
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Rotate around the x-, y-, and z-axes.
+ * rotateZ(frameCount * 0.01);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Draw the box.
+ * box(50);
+ * }
+ *
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Graphics object.
+ * pg = createGraphics(100, 100);
+ *
+ * // Draw a circle to the p5.Graphics object.
+ * pg.background(200);
+ * pg.circle(50, 50, 30);
+ *
+ * describe('A spinning cube with circle at the center of each face.');
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Rotate around the x-, y-, and z-axes.
+ * rotateZ(frameCount * 0.01);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ *
+ * // Apply the p5.Graphics object as a texture.
+ * texture(pg);
+ *
+ * // Draw the box.
+ * box(50);
+ * }
+ *
+ *
+ * let vid;
+ *
+ * // Load a video and create a p5.MediaElement object.
+ * function preload() {
+ * vid = createVideo('assets/fingers.mov');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Hide the video.
+ * vid.hide();
+ *
+ * // Set the video to loop.
+ * vid.loop();
+ *
+ * describe('A rectangle with video as texture');
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Apply the video as a texture.
+ * texture(vid);
+ *
+ * // Draw the rectangle.
+ * rect(-40, -40, 80, 80);
+ * }
+ *
+ *
+ * let vid;
+ *
+ * // Load a video and create a p5.MediaElement object.
+ * function preload() {
+ * vid = createVideo('assets/fingers.mov');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Hide the video.
+ * vid.hide();
+ *
+ * // Set the video to loop.
+ * vid.loop();
+ *
+ * describe('A rectangle with video as texture');
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Set the texture mode.
+ * textureMode(NORMAL);
+ *
+ * // Apply the video as a texture.
+ * texture(vid);
+ *
+ * // Draw a custom shape using uv coordinates.
+ * beginShape();
+ * vertex(-40, -40, 0, 0);
+ * vertex(40, -40, 1, 0);
+ * vertex(40, 40, 1, 1);
+ * vertex(-40, 40, 0, 1);
+ * endShape();
+ * }
+ *
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Draw the rectangle.
+ * rect(0, 0, 30, 50);
+ *
+ *
+ * If the image in the code snippet above has dimensions of 300 x 500 pixels,
+ * the same result could be achieved as follows:
+ *
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Draw the rectangle.
+ * beginShape();
+ *
+ * // Top-left.
+ * // u: 0, v: 0
+ * vertex(0, 0, 0, 0, 0);
+ *
+ * // Top-right.
+ * // u: 300, v: 0
+ * vertex(30, 0, 0, 300, 0);
+ *
+ * // Bottom-right.
+ * // u: 300, v: 500
+ * vertex(30, 50, 0, 300, 500);
+ *
+ * // Bottom-left.
+ * // u: 0, v: 500
+ * vertex(0, 50, 0, 0, 500);
+ *
+ * endShape();
+ *
+ *
+ * `textureMode()` changes the coordinate system for uv coordinates.
+ *
+ * The parameter, `mode`, accepts two possible constants. If `NORMAL` is
+ * passed, as in `textureMode(NORMAL)`, then the texture’s uv coordinates can
+ * be provided in the range 0 to 1 instead of the image’s dimensions. This can
+ * be helpful for using the same code for multiple images of different sizes.
+ * For example, the code snippet above could be rewritten as follows:
+ *
+ *
+ * // Set the texture mode to use normalized coordinates.
+ * textureMode(NORMAL);
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Draw the rectangle.
+ * beginShape();
+ *
+ * // Top-left.
+ * // u: 0, v: 0
+ * vertex(0, 0, 0, 0, 0);
+ *
+ * // Top-right.
+ * // u: 1, v: 0
+ * vertex(30, 0, 0, 1, 0);
+ *
+ * // Bottom-right.
+ * // u: 1, v: 1
+ * vertex(30, 50, 0, 1, 1);
+ *
+ * // Bottom-left.
+ * // u: 0, v: 1
+ * vertex(0, 50, 0, 0, 1);
+ *
+ * endShape();
+ *
+ *
+ * By default, `mode` is `IMAGE`, which scales uv coordinates to the
+ * dimensions of the image. Calling `textureMode(IMAGE)` applies the default.
+ *
+ * Note: `textureMode()` can only be used in WebGL mode.
+ *
+ * @method textureMode
+ * @param {Constant} mode either IMAGE or NORMAL.
+ *
+ * @example
+ *
+ * let img;
+ *
+ * // Load an image and create a p5.Image object.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('An image of a ceiling against a black background.');
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Draw the custom shape.
+ * // Use the image's width and height as uv coordinates.
+ * beginShape();
+ * vertex(-30, -30, 0, 0);
+ * vertex(30, -30, img.width, 0);
+ * vertex(30, 30, img.width, img.height);
+ * vertex(-30, 30, 0, img.height);
+ * endShape();
+ * }
+ *
+ *
+ * let img;
+ *
+ * // Load an image and create a p5.Image object.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('An image of a ceiling against a black background.');
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Set the texture mode.
+ * textureMode(NORMAL);
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Draw the custom shape.
+ * // Use normalized uv coordinates.
+ * beginShape();
+ * vertex(-30, -30, 0, 0);
+ * vertex(30, -30, 1, 0);
+ * vertex(30, 30, 1, 1);
+ * vertex(-30, 30, 0, 1);
+ * endShape();
+ * }
+ *
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Draw the rectangle.
+ * rect(0, 0, 30, 50);
+ *
+ *
+ * If the image in the code snippet above has dimensions of 300 x 500 pixels,
+ * the same result could be achieved as follows:
+ *
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Draw the rectangle.
+ * beginShape();
+ *
+ * // Top-left.
+ * // u: 0, v: 0
+ * vertex(0, 0, 0, 0, 0);
+ *
+ * // Top-right.
+ * // u: 300, v: 0
+ * vertex(30, 0, 0, 300, 0);
+ *
+ * // Bottom-right.
+ * // u: 300, v: 500
+ * vertex(30, 50, 0, 300, 500);
+ *
+ * // Bottom-left.
+ * // u: 0, v: 500
+ * vertex(0, 50, 0, 0, 500);
+ *
+ * endShape();
+ *
+ *
+ * `textureWrap()` controls how textures behave when their uv's go beyond the
+ * texture. Doing so can produce interesting visual effects such as tiling.
+ * For example, the custom shape above could have u-coordinates are greater
+ * than the image’s width:
+ *
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Draw the rectangle.
+ * beginShape();
+ * vertex(0, 0, 0, 0, 0);
+ *
+ * // Top-right.
+ * // u: 600
+ * vertex(30, 0, 0, 600, 0);
+ *
+ * // Bottom-right.
+ * // u: 600
+ * vertex(30, 50, 0, 600, 500);
+ *
+ * vertex(0, 50, 0, 0, 500);
+ * endShape();
+ *
+ *
+ * The u-coordinates of 600 are greater than the texture image’s width of 300.
+ * This creates interesting possibilities.
+ *
+ * The first parameter, `wrapX`, accepts three possible constants. If `CLAMP`
+ * is passed, as in `textureWrap(CLAMP)`, the pixels at the edge of the
+ * texture will extend to the shape’s edges. If `REPEAT` is passed, as in
+ * `textureWrap(REPEAT)`, the texture will tile repeatedly until reaching the
+ * shape’s edges. If `MIRROR` is passed, as in `textureWrap(MIRROR)`, the
+ * texture will tile repeatedly until reaching the shape’s edges, flipping
+ * its orientation between tiles. By default, textures `CLAMP`.
+ *
+ * The second parameter, `wrapY`, is optional. It accepts the same three
+ * constants, `CLAMP`, `REPEAT`, and `MIRROR`. If one of these constants is
+ * passed, as in `textureWRAP(MIRROR, REPEAT)`, then the texture will `MIRROR`
+ * horizontally and `REPEAT` vertically. By default, `wrapY` will be set to
+ * the same value as `wrapX`.
+ *
+ * Note: `textureWrap()` can only be used in WebGL mode.
+ *
+ * @method textureWrap
+ * @param {Constant} wrapX either CLAMP, REPEAT, or MIRROR
+ * @param {Constant} [wrapY] either CLAMP, REPEAT, or MIRROR
+ *
+ * @example
+ *
+ * let img;
+ *
+ * function preload() {
+ * img = loadImage('assets/rockies128.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'An image of a landscape occupies the top-left corner of a square. Its edge colors smear to cover the other thre quarters of the square.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Set the texture mode.
+ * textureMode(NORMAL);
+ *
+ * // Set the texture wrapping.
+ * // Note: CLAMP is the default mode.
+ * textureWrap(CLAMP);
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Style the shape.
+ * noStroke();
+ *
+ * // Draw the shape.
+ * // Use uv coordinates > 1.
+ * beginShape();
+ * vertex(-30, -30, 0, 0, 0);
+ * vertex(30, -30, 0, 2, 0);
+ * vertex(30, 30, 0, 2, 2);
+ * vertex(-30, 30, 0, 0, 2);
+ * endShape();
+ * }
+ *
+ *
+ * let img;
+ *
+ * function preload() {
+ * img = loadImage('assets/rockies128.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('Four identical images of a landscape arranged in a grid.');
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Set the texture mode.
+ * textureMode(NORMAL);
+ *
+ * // Set the texture wrapping.
+ * textureWrap(REPEAT);
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Style the shape.
+ * noStroke();
+ *
+ * // Draw the shape.
+ * // Use uv coordinates > 1.
+ * beginShape();
+ * vertex(-30, -30, 0, 0, 0);
+ * vertex(30, -30, 0, 2, 0);
+ * vertex(30, 30, 0, 2, 2);
+ * vertex(-30, 30, 0, 0, 2);
+ * endShape();
+ * }
+ *
+ *
+ * let img;
+ *
+ * function preload() {
+ * img = loadImage('assets/rockies128.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'Four identical images of a landscape arranged in a grid. The images are reflected horizontally and vertically, creating a kaleidoscope effect.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Set the texture mode.
+ * textureMode(NORMAL);
+ *
+ * // Set the texture wrapping.
+ * textureWrap(MIRROR);
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Style the shape.
+ * noStroke();
+ *
+ * // Draw the shape.
+ * // Use uv coordinates > 1.
+ * beginShape();
+ * vertex(-30, -30, 0, 0, 0);
+ * vertex(30, -30, 0, 2, 0);
+ * vertex(30, 30, 0, 2, 2);
+ * vertex(-30, 30, 0, 0, 2);
+ * endShape();
+ * }
+ *
+ *
+ * let img;
+ *
+ * function preload() {
+ * img = loadImage('assets/rockies128.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'Four identical images of a landscape arranged in a grid. The top row and bottom row are reflections of each other.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(0);
+ *
+ * // Set the texture mode.
+ * textureMode(NORMAL);
+ *
+ * // Set the texture wrapping.
+ * textureWrap(REPEAT, MIRROR);
+ *
+ * // Apply the image as a texture.
+ * texture(img);
+ *
+ * // Style the shape.
+ * noStroke();
+ *
+ * // Draw the shape.
+ * // Use uv coordinates > 1.
+ * beginShape();
+ * vertex(-30, -30, 0, 0, 0);
+ * vertex(30, -30, 0, 2, 0);
+ * vertex(30, 30, 0, 2, 2);
+ * vertex(-30, 30, 0, 0, 2);
+ * endShape();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A multicolor torus drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Style the torus.
+ * normalMaterial();
+ *
+ * // Draw the torus.
+ * torus(30);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A magenta cube drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on a magenta ambient light.
+ * ambientLight(255, 0, 255);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A purple cube drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on a magenta ambient light.
+ * ambientLight(255, 0, 255);
+ *
+ * // Add a dark gray ambient material.
+ * ambientMaterial(150);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A red cube drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on a magenta ambient light.
+ * ambientLight(255, 0, 255);
+ *
+ * // Add a yellow ambient material using RGB values.
+ * ambientMaterial(255, 255, 0);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A red cube drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on a magenta ambient light.
+ * ambientLight(255, 0, 255);
+ *
+ * // Add a yellow ambient material using a p5.Color object.
+ * let c = color(255, 255, 0);
+ * ambientMaterial(c);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A red cube drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on a magenta ambient light.
+ * ambientLight(255, 0, 255);
+ *
+ * // Add a yellow ambient material using a color string.
+ * ambientMaterial('yellow');
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A yellow cube drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on a white ambient light.
+ * ambientLight(255, 255, 255);
+ *
+ * // Add a yellow ambient material using a color string.
+ * ambientMaterial('yellow');
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A red cube drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on a white ambient light.
+ * ambientLight(255, 255, 255);
+ *
+ * // Add a red emissive material using RGB values.
+ * emissiveMaterial(255, 0, 0);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ * // Double-click the canvas to apply a specular material.
+ *
+ * let isGlossy = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A red torus drawn on a gray background. It becomes glossy when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on a white point light at the top-right.
+ * pointLight(255, 255, 255, 30, -40, 30);
+ *
+ * // Add a glossy coat if the user has double-clicked.
+ * if (isGlossy === true) {
+ * specularMaterial(255);
+ * shininess(50);
+ * }
+ *
+ * // Style the torus.
+ * noStroke();
+ * fill(255, 0, 0);
+ *
+ * // Draw the torus.
+ * torus(30);
+ * }
+ *
+ * // Make the torus glossy when the user double-clicks.
+ * function doubleClicked() {
+ * isGlossy = true;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ * // Double-click the canvas to apply a specular material.
+ *
+ * let isGlossy = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'A red torus drawn on a gray background. It becomes glossy and reflects green light when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on a white point light at the top-right.
+ * pointLight(255, 255, 255, 30, -40, 30);
+ *
+ * // Add a glossy green coat if the user has double-clicked.
+ * if (isGlossy === true) {
+ * specularMaterial(0, 255, 0);
+ * shininess(50);
+ * }
+ *
+ * // Style the torus.
+ * noStroke();
+ * fill(255, 0, 0);
+ *
+ * // Draw the torus.
+ * torus(30);
+ * }
+ *
+ * // Make the torus glossy when the user double-clicks.
+ * function doubleClicked() {
+ * isGlossy = true;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ * // Double-click the canvas to apply a specular material.
+ *
+ * let isGlossy = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'A red torus drawn on a gray background. It becomes glossy and reflects green light when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on a white point light at the top-right.
+ * pointLight(255, 255, 255, 30, -40, 30);
+ *
+ * // Add a glossy green coat if the user has double-clicked.
+ * if (isGlossy === true) {
+ * // Create a p5.Color object.
+ * let c = color('green');
+ * specularMaterial(c);
+ * shininess(50);
+ * }
+ *
+ * // Style the torus.
+ * noStroke();
+ * fill(255, 0, 0);
+ *
+ * // Draw the torus.
+ * torus(30);
+ * }
+ *
+ * // Make the torus glossy when the user double-clicks.
+ * function doubleClicked() {
+ * isGlossy = true;
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ * // Double-click the canvas to apply a specular material.
+ *
+ * let isGlossy = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'A red torus drawn on a gray background. It becomes glossy and reflects green light when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on a white point light at the top-right.
+ * pointLight(255, 255, 255, 30, -40, 30);
+ *
+ * // Add a glossy green coat if the user has double-clicked.
+ * if (isGlossy === true) {
+ * specularMaterial('#00FF00');
+ * shininess(50);
+ * }
+ *
+ * // Style the torus.
+ * noStroke();
+ * fill(255, 0, 0);
+ *
+ * // Draw the torus.
+ * torus(30);
+ * }
+ *
+ * // Make the torus glossy when the user double-clicks.
+ * function doubleClicked() {
+ * isGlossy = true;
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'Two red spheres drawn on a gray background. White light reflects from their surfaces as the mouse moves. The right sphere is shinier than the left sphere.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Turn on a red ambient light.
+ * ambientLight(255, 0, 0);
+ *
+ * // Get the mouse's coordinates.
+ * let mx = mouseX - 50;
+ * let my = mouseY - 50;
+ *
+ * // Turn on a white point light that follows the mouse.
+ * pointLight(255, 255, 255, mx, my, 50);
+ *
+ * // Style the sphere.
+ * noStroke();
+ *
+ * // Add a specular material with a grayscale value.
+ * specularMaterial(255);
+ *
+ * // Draw the left sphere with low shininess.
+ * translate(-25, 0, 0);
+ * shininess(10);
+ * sphere(20);
+ *
+ * // Draw the right sphere with high shininess.
+ * translate(50, 0, 0);
+ * shininess(100);
+ * sphere(20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'Two blue spheres drawn on a gray background. White light reflects from their surfaces as the mouse moves. The right sphere is more metallic than the left sphere.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Turn on an ambient light.
+ * ambientLight(200);
+ *
+ * // Get the mouse's coordinates.
+ * let mx = mouseX - 50;
+ * let my = mouseY - 50;
+ *
+ * // Turn on a white point light that follows the mouse.
+ * pointLight(255, 255, 255, mx, my, 50);
+ *
+ * // Style the spheres.
+ * noStroke();
+ * fill(30, 30, 255);
+ * specularMaterial(255);
+ * shininess(20);
+ *
+ * // Draw the left sphere with low metalness.
+ * translate(-25, 0, 0);
+ * metalness(1);
+ * sphere(20);
+ *
+ * // Draw the right sphere with high metalness.
+ * translate(50, 0, 0);
+ * metalness(50);
+ * sphere(20);
+ * }
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let img;
+ *
+ * function preload() {
+ * img = loadImage('assets/outdoor_spheremap.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100 ,100 ,WEBGL);
+ *
+ * describe(
+ * 'Two spheres floating above a landscape. The surface of the spheres reflect the landscape. The right sphere is more reflective than the left sphere.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Add the panorama.
+ * panorama(img);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Use the image as a light source.
+ * imageLight(img);
+ *
+ * // Style the spheres.
+ * noStroke();
+ * specularMaterial(50);
+ * shininess(200);
+ *
+ * // Draw the left sphere with low metalness.
+ * translate(-25, 0, 0);
+ * metalness(1);
+ * sphere(20);
+ *
+ * // Draw the right sphere with high metalness.
+ * translate(50, 0, 0);
+ * metalness(50);
+ * sphere(20);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Move the camera to the top-right.
+ * camera(200, -400, 800);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube apperas to sway left and right on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the camera's x-coordinate.
+ * let x = 400 * cos(frameCount * 0.01);
+ *
+ * // Orbit the camera around the box.
+ * camera(x, -400, 800);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Adjust the range sliders to change the camera's position.
+ *
+ * let xSlider;
+ * let ySlider;
+ * let zSlider;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create slider objects to set the camera's coordinates.
+ * xSlider = createSlider(-400, 400, 400);
+ * xSlider.position(0, 100);
+ * xSlider.size(100);
+ * ySlider = createSlider(-400, 400, -200);
+ * ySlider.position(0, 120);
+ * ySlider.size(100);
+ * zSlider = createSlider(0, 1600, 800);
+ * zSlider.position(0, 140);
+ * zSlider.size(100);
+ *
+ * describe(
+ * 'A white cube drawn against a gray background. Three range sliders appear beneath the image. The camera position changes when the user moves the sliders.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Get the camera's coordinates from the sliders.
+ * let x = xSlider.value();
+ * let y = ySlider.value();
+ * let z = zSlider.value();
+ *
+ * // Move the camera.
+ * camera(x, y, z);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Double-click to squeeze the box.
+ *
+ * let isSqueezed = false;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white rectangular prism on a gray background. The box appears to become thinner when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Place the camera at the top-right.
+ * camera(400, -400, 800);
+ *
+ * if (isSqueezed === true) {
+ * // Set fovy to 0.2.
+ * // Set aspect to 1.5.
+ * perspective(0.2, 1.5);
+ * }
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ * // Change the camera's perspective when the user double-clicks.
+ * function doubleClicked() {
+ * isSqueezed = true;
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white rectangular prism on a gray background. The prism moves away from the camera until it disappears.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Place the camera at the top-right.
+ * camera(400, -400, 800);
+ *
+ * // Set fovy to 0.2.
+ * // Set aspect to 1.5.
+ * // Set near to 600.
+ * // Set far to 1200.
+ * perspective(0.2, 1.5, 600, 1200);
+ *
+ * // Move the origin away from the camera.
+ * let x = -frameCount;
+ * let y = frameCount;
+ * let z = -2 * frameCount;
+ * translate(x, y, z);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Double-click the canvas to toggle the line perspective.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'A white cube with black edges on a gray background. Its edges toggle between thick and thin when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 600);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * translate(0, 0, -40);
+ * box(10);
+ * }
+ * }
+ *
+ * // Toggle the line perspective when the user double-clicks.
+ * function doubleClicked() {
+ * let isEnabled = linePerspective();
+ * linePerspective(!isEnabled);
+ * }
+ *
+ *
+ * // Double-click the canvas to toggle the line perspective.
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe(
+ * 'A row of cubes with black edges on a gray background. Their edges toggle between thick and thin when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Use an orthographic projection.
+ * ortho();
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 600);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * translate(0, 0, -40);
+ * box(10);
+ * }
+ * }
+ *
+ * // Toggle the line perspective when the user double-clicks.
+ * function doubleClicked() {
+ * let isEnabled = linePerspective();
+ * linePerspective(!isEnabled);
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A row of tiny, white cubes on a gray background. All the cubes appear the same size.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Apply an orthographic projection.
+ * ortho();
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 600);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * translate(0, 0, -40);
+ * box(10);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A white cube on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Apply an orthographic projection.
+ * // Center the frustum.
+ * // Set its width and height to 20.
+ * // Place its near plane 300 pixels from the camera.
+ * // Place its far plane 350 pixels from the camera.
+ * ortho(-10, 10, -10, 10, 300, 350);
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 600);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * translate(0, 0, -40);
+ * box(10);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * describe('A row of white cubes on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Apply the default frustum projection.
+ * frustum();
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 600);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * translate(0, 0, -40);
+ * box(10);
+ * }
+ * }
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ * describe('A white cube on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Adjust the frustum.
+ * // Center it.
+ * // Set its width and height to 20 pixels.
+ * // Place its near plane 300 pixels from the camera.
+ * // Place its far plane 350 pixels from the camera.
+ * frustum(-10, 10, -10, 10, 300, 350);
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 600);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * translate(0, 0, -40);
+ * box(10);
+ * }
+ * }
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let cam1;
+ * let cam2;
+ * let usingCam1 = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = createCamera();
+ *
+ * // Create the second camera.
+ * // Place it at the top-left.
+ * // Point it at the origin.
+ * cam2 = createCamera();
+ * cam2.setPosition(400, -400, 800);
+ * cam2.lookAt(0, 0, 0);
+ *
+ * // Set the current camera to cam1.
+ * setCamera(cam1);
+ *
+ * describe('A white cube on a gray background. The camera toggles between frontal and aerial views when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (usingCam1 === true) {
+ * setCamera(cam2);
+ * usingCam1 = false;
+ * } else {
+ * setCamera(cam1);
+ * usingCam1 = true;
+ * }
+ * }
+ *
+ *
+ * let cam;
+ * let delta = 0.001;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at the origin.
+ * cam.lookAt(0, 0, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The cube goes in and out of view as the camera pans left and right.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Turn the camera left and right, called "panning".
+ * cam.pan(delta);
+ *
+ * // Switch directions every 120 frames.
+ * if (frameCount % 120 === 0) {
+ * delta *= -1;
+ * }
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let cam1;
+ * let cam2;
+ * let isDefaultCamera = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = createCamera();
+ *
+ * // Create the second camera.
+ * // Place it at the top-left.
+ * // Point it at the origin.
+ * cam2 = createCamera();
+ * cam2.setPosition(400, -400, 800);
+ * cam2.lookAt(0, 0, 0);
+ *
+ * // Set the current camera to cam1.
+ * setCamera(cam1);
+ *
+ * describe(
+ * 'A white cube on a gray background. The camera toggles between frontal and aerial views when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (isDefaultCamera === true) {
+ * setCamera(cam2);
+ * isDefaultCamera = false;
+ * } else {
+ * setCamera(cam1);
+ * isDefaultCamera = true;
+ * }
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at the origin.
+ * cam.lookAt(0, 0, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The text "eyeX: 0" is written in black beneath it.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Display the value of eyeX, rounded to the nearest integer.
+ * text(`eyeX: ${round(cam.eyeX)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at the origin.
+ * cam.lookAt(0, 0, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The cube appears to move left and right as the camera moves. The text "eyeX: X" is written in black beneath the cube. X oscillates between -25 and 25.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Calculate the new x-coordinate.
+ * let x = 25 * sin(frameCount * 0.01);
+ *
+ * // Set the camera's position.
+ * cam.setPosition(x, -400, 800);
+ *
+ * // Display the value of eyeX, rounded to the nearest integer.
+ * text(`eyeX: ${round(cam.eyeX)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at the origin.
+ * cam.lookAt(0, 0, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The text "eyeY: -400" is written in black beneath it.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Display the value of eyeY, rounded to the nearest integer.
+ * text(`eyeX: ${round(cam.eyeY)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at the origin.
+ * cam.lookAt(0, 0, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The cube appears to move up and down as the camera moves. The text "eyeY: Y" is written in black beneath the cube. Y oscillates between -374 and -425.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Calculate the new y-coordinate.
+ * let y = 25 * sin(frameCount * 0.01) - 400;
+ *
+ * // Set the camera's position.
+ * cam.setPosition(0, y, 800);
+ *
+ * // Display the value of eyeY, rounded to the nearest integer.
+ * text(`eyeY: ${round(cam.eyeY)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at the origin.
+ * cam.lookAt(0, 0, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The text "eyeZ: 800" is written in black beneath it.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Display the value of eyeZ, rounded to the nearest integer.
+ * text(`eyeZ: ${round(cam.eyeZ)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at the origin.
+ * cam.lookAt(0, 0, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The cube appears to move forward and back as the camera moves. The text "eyeZ: Z" is written in black beneath the cube. Z oscillates between 700 and 900.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Calculate the new z-coordinate.
+ * let z = 100 * sin(frameCount * 0.01) + 800;
+ *
+ * // Set the camera's position.
+ * cam.setPosition(0, -400, z);
+ *
+ * // Display the value of eyeZ, rounded to the nearest integer.
+ * text(`eyeZ: ${round(cam.eyeZ)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at (10, 20, -30).
+ * cam.lookAt(10, 20, -30);
+ *
+ * describe(
+ * 'A white cube on a gray background. The text "centerX: 10" is written in black beneath it.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Display the value of centerX, rounded to the nearest integer.
+ * text(`centerX: ${round(cam.centerX)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-right.
+ * cam.setPosition(100, -400, 800);
+ *
+ * // Point the camera at (10, 20, -30).
+ * cam.lookAt(10, 20, -30);
+ *
+ * describe(
+ * 'A white cube on a gray background. The cube appears to move left and right as the camera shifts its focus. The text "centerX: X" is written in black beneath the cube. X oscillates between -15 and 35.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Calculate the new x-coordinate.
+ * let x = 25 * sin(frameCount * 0.01) + 10;
+ *
+ * // Point the camera.
+ * cam.lookAt(x, 20, -30);
+ *
+ * // Display the value of centerX, rounded to the nearest integer.
+ * text(`centerX: ${round(cam.centerX)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at (10, 20, -30).
+ * cam.lookAt(10, 20, -30);
+ *
+ * describe(
+ * 'A white cube on a gray background. The text "centerY: 20" is written in black beneath it.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Display the value of centerY, rounded to the nearest integer.
+ * text(`centerY: ${round(cam.centerY)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-right.
+ * cam.setPosition(100, -400, 800);
+ *
+ * // Point the camera at (10, 20, -30).
+ * cam.lookAt(10, 20, -30);
+ *
+ * describe(
+ * 'A white cube on a gray background. The cube appears to move up and down as the camera shifts its focus. The text "centerY: Y" is written in black beneath the cube. Y oscillates between -5 and 45.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Calculate the new y-coordinate.
+ * let y = 25 * sin(frameCount * 0.01) + 20;
+ *
+ * // Point the camera.
+ * cam.lookAt(10, y, -30);
+ *
+ * // Display the value of centerY, rounded to the nearest integer.
+ * text(`centerY: ${round(cam.centerY)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at (10, 20, -30).
+ * cam.lookAt(10, 20, -30);
+ *
+ * describe(
+ * 'A white cube on a gray background. The text "centerZ: -30" is written in black beneath it.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Display the value of centerZ, rounded to the nearest integer.
+ * text(`centerZ: ${round(cam.centerZ)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-right.
+ * cam.setPosition(100, -400, 800);
+ *
+ * // Point the camera at (10, 20, -30).
+ * cam.lookAt(10, 20, -30);
+ *
+ * describe(
+ * 'A white cube on a gray background. The cube appears to move forward and back as the camera shifts its focus. The text "centerZ: Z" is written in black beneath the cube. Z oscillates between -55 and -25.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Calculate the new z-coordinate.
+ * let z = 25 * sin(frameCount * 0.01) - 30;
+ *
+ * // Point the camera.
+ * cam.lookAt(10, 20, z);
+ *
+ * // Display the value of centerZ, rounded to the nearest integer.
+ * text(`centerZ: ${round(cam.centerZ)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-right: (100, -400, 800)
+ * // Point it at the origin: (0, 0, 0)
+ * // Set its "up" vector: (0, 1, 0).
+ * cam.camera(100, -400, 800, 0, 0, 0, 0, 1, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The text "upX: 0" is written in black beneath it.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Display the value of upX, rounded to the nearest tenth.
+ * text(`upX: ${round(cam.upX, 1)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-right: (100, -400, 800)
+ * // Point it at the origin: (0, 0, 0)
+ * // Set its "up" vector: (0, 1, 0).
+ * cam.camera(100, -400, 800, 0, 0, 0, 0, 1, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The cube appears to rock back and forth. The text "upX: X" is written in black beneath it. X oscillates between -1 and 1.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Calculate the x-component.
+ * let x = sin(frameCount * 0.01);
+ *
+ * // Update the camera's "up" vector.
+ * cam.camera(100, -400, 800, 0, 0, 0, x, 1, 0);
+ *
+ * // Display the value of upX, rounded to the nearest tenth.
+ * text(`upX: ${round(cam.upX, 1)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-right: (100, -400, 800)
+ * // Point it at the origin: (0, 0, 0)
+ * // Set its "up" vector: (0, 1, 0).
+ * cam.camera(100, -400, 800, 0, 0, 0, 0, 1, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The text "upY: 1" is written in black beneath it.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Display the value of upY, rounded to the nearest tenth.
+ * text(`upY: ${round(cam.upY, 1)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-right: (100, -400, 800)
+ * // Point it at the origin: (0, 0, 0)
+ * // Set its "up" vector: (0, 1, 0).
+ * cam.camera(100, -400, 800, 0, 0, 0, 0, 1, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The cube flips upside-down periodically. The text "upY: Y" is written in black beneath it. Y oscillates between -1 and 1.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Calculate the y-component.
+ * let y = sin(frameCount * 0.01);
+ *
+ * // Update the camera's "up" vector.
+ * cam.camera(100, -400, 800, 0, 0, 0, 0, y, 0);
+ *
+ * // Display the value of upY, rounded to the nearest tenth.
+ * text(`upY: ${round(cam.upY, 1)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-right: (100, -400, 800)
+ * // Point it at the origin: (0, 0, 0)
+ * // Set its "up" vector: (0, 1, 0).
+ * cam.camera(100, -400, 800, 0, 0, 0, 0, 1, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The text "upZ: 0" is written in black beneath it.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Display the value of upZ, rounded to the nearest tenth.
+ * text(`upZ: ${round(cam.upZ, 1)}`, 0, 55);
+ * }
+ *
+ *
+ * let cam;
+ * let font;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * font = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-right: (100, -400, 800)
+ * // Point it at the origin: (0, 0, 0)
+ * // Set its "up" vector: (0, 1, 0).
+ * cam.camera(100, -400, 800, 0, 0, 0, 0, 1, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The cube appears to rock back and forth. The text "upZ: Z" is written in black beneath it. Z oscillates between -1 and 1.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the box.
+ * fill(255);
+ *
+ * // Draw the box.
+ * box();
+ *
+ * // Style the text.
+ * textAlign(CENTER);
+ * textSize(16);
+ * textFont(font);
+ * fill(0);
+ *
+ * // Calculate the z-component.
+ * let z = sin(frameCount * 0.01);
+ *
+ * // Update the camera's "up" vector.
+ * cam.camera(100, -400, 800, 0, 0, 0, 0, 1, z);
+ *
+ * // Display the value of upZ, rounded to the nearest tenth.
+ * text(`upZ: ${round(cam.upZ, 1)}`, 0, 55);
+ * }
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let cam1;
+ * let cam2;
+ * let isDefaultCamera = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = createCamera();
+ *
+ * // Create the second camera.
+ * cam2 = createCamera();
+ *
+ * // Place it at the top-right.
+ * cam2.camera(400, -400, 800);
+ *
+ * // Set its fovy to 0.2.
+ * // Set its aspect to 1.5.
+ * // Set its near to 600.
+ * // Set its far to 1200.
+ * cam2.perspective(0.2, 1.5, 600, 1200);
+ *
+ * // Set the current camera to cam1.
+ * setCamera(cam1);
+ *
+ * describe('A white cube on a gray background. The camera toggles between a frontal view and a skewed aerial view when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (isDefaultCamera === true) {
+ * setCamera(cam2);
+ * isDefaultCamera = false;
+ * } else {
+ * setCamera(cam1);
+ * isDefaultCamera = true;
+ * }
+ * }
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let cam1;
+ * let cam2;
+ * let isDefaultCamera = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = createCamera();
+ *
+ * // Create the second camera.
+ * cam2 = createCamera();
+ *
+ * // Place it at the top-right.
+ * cam2.camera(400, -400, 800);
+ *
+ * // Set its fovy to 0.2.
+ * // Set its aspect to 1.5.
+ * // Set its near to 600.
+ * // Set its far to 1200.
+ * cam2.perspective(0.2, 1.5, 600, 1200);
+ *
+ * // Set the current camera to cam1.
+ * setCamera(cam1);
+ *
+ * describe('A white cube moves left and right on a gray background. The camera toggles between a frontal and a skewed aerial view when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin left and right.
+ * let x = 100 * sin(frameCount * 0.01);
+ * translate(x, 0, 0);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (isDefaultCamera === true) {
+ * setCamera(cam2);
+ * isDefaultCamera = false;
+ * } else {
+ * setCamera(cam1);
+ * isDefaultCamera = true;
+ * }
+ * }
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let cam1;
+ * let cam2;
+ * let isDefaultCamera = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = createCamera();
+ *
+ * // Create the second camera.
+ * cam2 = createCamera();
+ *
+ * // Apply an orthographic projection.
+ * cam2.ortho();
+ *
+ * // Set the current camera to cam1.
+ * setCamera(cam1);
+ *
+ * describe('A row of white cubes against a gray background. The camera toggles between a perspective and an orthographic projection when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 500);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * translate(0, 0, -40);
+ * box(10);
+ * }
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (isDefaultCamera === true) {
+ * setCamera(cam2);
+ * isDefaultCamera = false;
+ * } else {
+ * setCamera(cam1);
+ * isDefaultCamera = true;
+ * }
+ * }
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let cam1;
+ * let cam2;
+ * let isDefaultCamera = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = createCamera();
+ *
+ * // Create the second camera.
+ * cam2 = createCamera();
+ *
+ * // Apply an orthographic projection.
+ * cam2.ortho();
+ *
+ * // Set the current camera to cam1.
+ * setCamera(cam1);
+ *
+ * describe('A row of white cubes slither like a snake against a gray background. The camera toggles between a perspective and an orthographic projection when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 500);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * push();
+ * // Calculate the box's coordinates.
+ * let x = 10 * sin(frameCount * 0.02 + i * 0.6);
+ * let z = -40 * i;
+ * // Translate the origin.
+ * translate(x, 0, z);
+ * // Draw the box.
+ * box(10);
+ * pop();
+ * }
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (isDefaultCamera === true) {
+ * setCamera(cam2);
+ * isDefaultCamera = false;
+ * } else {
+ * setCamera(cam1);
+ * isDefaultCamera = true;
+ * }
+ * }
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let cam1;
+ * let cam2;
+ * let isDefaultCamera = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = createCamera();
+ *
+ * // Create the second camera.
+ * cam2 = createCamera();
+ *
+ * // Adjust the frustum.
+ * // Center it.
+ * // Set its width and height to 20 pixels.
+ * // Place its near plane 300 pixels from the camera.
+ * // Place its far plane 350 pixels from the camera.
+ * cam2.frustum(-10, 10, -10, 10, 300, 350);
+ *
+ * // Set the current camera to cam1.
+ * setCamera(cam1);
+ *
+ * describe(
+ * 'A row of white cubes against a gray background. The camera zooms in on one cube when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 600);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * translate(0, 0, -40);
+ * box(10);
+ * }
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (isDefaultCamera === true) {
+ * setCamera(cam2);
+ * isDefaultCamera = false;
+ * } else {
+ * setCamera(cam1);
+ * isDefaultCamera = true;
+ * }
+ * }
+ *
+ *
+ * let cam;
+ * let delta = 0.001;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at the origin.
+ * cam.lookAt(0, 0, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The cube goes in and out of view as the camera pans left and right.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Pan with the camera.
+ * cam.pan(delta);
+ *
+ * // Switch directions every 120 frames.
+ * if (frameCount % 120 === 0) {
+ * delta *= -1;
+ * }
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * let cam;
+ * let delta = 0.001;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at the origin.
+ * cam.lookAt(0, 0, 0);
+ *
+ * describe(
+ * 'A white cube on a gray background. The cube goes in and out of view as the camera tilts up and down.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Pan with the camera.
+ * cam.tilt(delta);
+ *
+ * // Switch directions every 120 frames.
+ * if (frameCount % 120 === 0) {
+ * delta *= -1;
+ * }
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Double-click to look at a different cube.
+ *
+ * let cam;
+ * let isLookingLeft = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Camera object.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-center.
+ * cam.setPosition(0, -400, 800);
+ *
+ * // Point the camera at the origin.
+ * cam.lookAt(-30, 0, 0);
+ *
+ * describe(
+ * 'A red cube and a blue cube on a gray background. The camera switches focus between the cubes when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the box on the left.
+ * push();
+ * // Translate the origin to the left.
+ * translate(-30, 0, 0);
+ * // Style the box.
+ * fill(255, 0, 0);
+ * // Draw the box.
+ * box(20);
+ * pop();
+ *
+ * // Draw the box on the right.
+ * push();
+ * // Translate the origin to the right.
+ * translate(30, 0, 0);
+ * // Style the box.
+ * fill(0, 0, 255);
+ * // Draw the box.
+ * box(20);
+ * pop();
+ * }
+ *
+ * // Change the camera's focus when the user double-clicks.
+ * function doubleClicked() {
+ * if (isLookingLeft === true) {
+ * cam.lookAt(30, 0, 0);
+ * isLookingLeft = false;
+ * } else {
+ * cam.lookAt(-30, 0, 0);
+ * isLookingLeft = true;
+ * }
+ * }
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let cam1;
+ * let cam2;
+ * let isDefaultCamera = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = createCamera();
+ *
+ * // Create the second camera.
+ * cam2 = createCamera();
+ *
+ * // Place it at the top-right: (1200, -600, 100)
+ * // Point it at the row of boxes: (-10, -10, 400)
+ * // Set its "up" vector to the default: (0, 1, 0)
+ * cam2.camera(1200, -600, 100, -10, -10, 400, 0, 1, 0);
+ *
+ * // Set the current camera to cam1.
+ * setCamera(cam1);
+ *
+ * describe(
+ * 'A row of white cubes against a gray background. The camera toggles between a frontal and an aerial view when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 500);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * translate(0, 0, -30);
+ * box(10);
+ * }
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (isDefaultCamera === true) {
+ * setCamera(cam2);
+ * isDefaultCamera = false;
+ * } else {
+ * setCamera(cam1);
+ * isDefaultCamera = true;
+ * }
+ * }
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let cam1;
+ * let cam2;
+ * let isDefaultCamera = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = createCamera();
+ *
+ * // Create the second camera.
+ * cam2 = createCamera();
+ *
+ * // Place it at the right: (1200, 0, 100)
+ * // Point it at the row of boxes: (-10, -10, 400)
+ * // Set its "up" vector to the default: (0, 1, 0)
+ * cam2.camera(1200, 0, 100, -10, -10, 400, 0, 1, 0);
+ *
+ * // Set the current camera to cam1.
+ * setCamera(cam1);
+ *
+ * describe(
+ * 'A row of white cubes against a gray background. The camera toggles between a static frontal view and an orbiting view when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Update cam2's position.
+ * let x = 1200 * cos(frameCount * 0.01);
+ * let y = -600 * sin(frameCount * 0.01);
+ * cam2.camera(x, y, 100, -10, -10, 400, 0, 1, 0);
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 500);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * translate(0, 0, -30);
+ * box(10);
+ * }
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (isDefaultCamera === true) {
+ * setCamera(cam2);
+ * isDefaultCamera = false;
+ * } else {
+ * setCamera(cam1);
+ * isDefaultCamera = true;
+ * }
+ * }
+ *
+ *
+ * // Click the canvas to begin detecting key presses.
+ *
+ * let cam;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam = createCamera();
+ *
+ * // Place the camera at the top-right.
+ * cam.setPosition(400, -400, 800);
+ *
+ * // Point it at the origin.
+ * cam.lookAt(0, 0, 0);
+ *
+ * describe(
+ * 'A white cube drawn against a gray background. The cube appears to move when the user presses certain keys.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Move the camera along its "local" axes
+ * // when the user presses certain keys.
+ * if (keyIsPressed === true) {
+ *
+ * // Move horizontally.
+ * if (keyCode === LEFT_ARROW) {
+ * cam.move(-1, 0, 0);
+ * }
+ * if (keyCode === RIGHT_ARROW) {
+ * cam.move(1, 0, 0);
+ * }
+ *
+ * // Move vertically.
+ * if (keyCode === UP_ARROW) {
+ * cam.move(0, -1, 0);
+ * }
+ * if (keyCode === DOWN_ARROW) {
+ * cam.move(0, 1, 0);
+ * }
+ *
+ * // Move in/out of the screen.
+ * if (key === 'i') {
+ * cam.move(0, 0, -1);
+ * }
+ * if (key === 'o') {
+ * cam.move(0, 0, 1);
+ * }
+ * }
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let cam1;
+ * let cam2;
+ * let isDefaultCamera = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = createCamera();
+ *
+ * // Create the second camera.
+ * cam2 = createCamera();
+ *
+ * // Place it closer to the origin.
+ * cam2.setPosition(0, 0, 600);
+ *
+ * // Set the current camera to cam1.
+ * setCamera(cam1);
+ *
+ * describe(
+ * 'A row of white cubes against a gray background. The camera toggles the amount of zoom when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 500);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * translate(0, 0, -30);
+ * box(10);
+ * }
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (isDefaultCamera === true) {
+ * setCamera(cam2);
+ * isDefaultCamera = false;
+ * } else {
+ * setCamera(cam1);
+ * isDefaultCamera = true;
+ * }
+ * }
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let cam1;
+ * let cam2;
+ * let isDefaultCamera = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = createCamera();
+ *
+ * // Create the second camera.
+ * cam2 = createCamera();
+ *
+ * // Place it closer to the origin.
+ * cam2.setPosition(0, 0, 600);
+ *
+ * // Set the current camera to cam1.
+ * setCamera(cam1);
+ *
+ * describe(
+ * 'A row of white cubes against a gray background. The camera toggles between a static view and a view that zooms in and out when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Update cam2's z-coordinate.
+ * let z = 100 * sin(frameCount * 0.01) + 700;
+ * cam2.setPosition(0, 0, z);
+ *
+ * // Translate the origin toward the camera.
+ * translate(-10, 10, 500);
+ *
+ * // Rotate the coordinate system.
+ * rotateY(-0.1);
+ * rotateX(-0.1);
+ *
+ * // Draw the row of boxes.
+ * for (let i = 0; i < 6; i += 1) {
+ * translate(0, 0, -30);
+ * box(10);
+ * }
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (isDefaultCamera === true) {
+ * setCamera(cam2);
+ * isDefaultCamera = false;
+ * } else {
+ * setCamera(cam1);
+ * isDefaultCamera = true;
+ * }
+ * }
+ *
+ *
+ * // Double-click to "reset" the camera zoom.
+ *
+ * let cam1;
+ * let cam2;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * cam1 = createCamera();
+ *
+ * // Place the camera at the top-right.
+ * cam1.setPosition(400, -400, 800);
+ *
+ * // Point it at the origin.
+ * cam1.lookAt(0, 0, 0);
+ *
+ * // Create the second camera.
+ * cam2 = createCamera();
+ *
+ * // Copy cam1's configuration.
+ * cam2.set(cam1);
+ *
+ * describe(
+ * 'A white cube drawn against a gray background. The camera slowly moves forward. The camera resets when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Update cam2's position.
+ * cam2.move(0, 0, -1);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ * // "Reset" the camera when the user double-clicks.
+ * function doubleClicked() {
+ * cam2.set(cam1);
+ * }
+ */
+
+ },
+ {
+ key: 'set',
+ value: function set(cam) {
+ var keyNamesOfThePropToCopy = [
+ 'eyeX',
+ 'eyeY',
+ 'eyeZ',
+ 'centerX',
+ 'centerY',
+ 'centerZ',
+ 'upX',
+ 'upY',
+ 'upZ',
+ 'cameraFOV',
+ 'aspectRatio',
+ 'cameraNear',
+ 'cameraFar',
+ 'cameraType',
+ 'yScale'
+ ];
+ for (var _i = 0, _keyNamesOfThePropToC = keyNamesOfThePropToCopy; _i < _keyNamesOfThePropToC.length; _i++) {
+ var keyName = _keyNamesOfThePropToC[_i];
+ this[keyName] = cam[keyName];
+ }
+ this.cameraMatrix = cam.cameraMatrix.copy();
+ this.projMatrix = cam.projMatrix.copy();
+ // If the target camera is active, update uMVMatrix and uPMatrix.
+ if (this._isActive()) {
+ this._renderer.uMVMatrix.mat4 = this.cameraMatrix.mat4.slice();
+ this._renderer.uPMatrix.mat4 = this.projMatrix.mat4.slice();
+ }
+ } /**
+ * Sets the camera’s position and orientation to values that are in-between
+ * those of two other cameras.
+ *
+ * `myCamera.slerp()` uses spherical linear interpolation to calculate a
+ * position and orientation that’s in-between two other cameras. Doing so is
+ * helpful for transitioning smoothly between two perspectives.
+ *
+ * The first two parameters, `cam0` and `cam1`, are the `p5.Camera` objects
+ * that should be used to set the current camera.
+ *
+ * The third parameter, `amt`, is the amount to interpolate between `cam0` and
+ * `cam1`. 0.0 keeps the camera’s position and orientation equal to `cam0`’s,
+ * 0.5 sets them halfway between `cam0`’s and `cam1`’s , and 1.0 sets the
+ * position and orientation equal to `cam1`’s.
+ *
+ * For example, calling `myCamera.slerp(cam0, cam1, 0.1)` sets cam’s position
+ * and orientation very close to `cam0`’s. Calling
+ * `myCamera.slerp(cam0, cam1, 0.9)` sets cam’s position and orientation very
+ * close to `cam1`’s.
+ *
+ * Note: All of the cameras must use the same projection.
+ *
+ * @method slerp
+ * @param {p5.Camera} cam0 first camera.
+ * @param {p5.Camera} cam1 second camera.
+ * @param {Number} amt amount of interpolation between 0.0 (`cam0`) and 1.0 (`cam1`).
+ *
+ * @example
+ *
+ *
+ * let cam;
+ * let cam0;
+ * let cam1;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the main camera.
+ * // Keep its default settings.
+ * cam = createCamera();
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam0 = createCamera();
+ *
+ * // Create the second camera.
+ * cam1 = createCamera();
+ *
+ * // Place it at the top-right.
+ * cam1.setPosition(400, -400, 800);
+ *
+ * // Point it at the origin.
+ * cam1.lookAt(0, 0, 0);
+ *
+ * // Set the current camera to cam.
+ * setCamera(cam);
+ *
+ * describe('A white cube drawn against a gray background. The camera slowly oscillates between a frontal view and an aerial view.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Calculate the amount to interpolate between cam0 and cam1.
+ * let amt = 0.5 * sin(frameCount * 0.01) + 0.5;
+ *
+ * // Update the main camera's position and orientation.
+ * cam.slerp(cam0, cam1, amt);
+ *
+ * box();
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'slerp',
+ value: function slerp(cam0, cam1, amt) {
+ // If t is 0 or 1, do not interpolate and set the argument camera.
+ if (amt === 0) {
+ this.set(cam0);
+ return;
+ } else if (amt === 1) {
+ this.set(cam1);
+ return;
+ } // For this cameras is ortho, assume that cam0 and cam1 are also ortho
+ // and interpolate the elements of the projection matrix.
+ // Use logarithmic interpolation for interpolation.
+
+ if (this.projMatrix.mat4[15] !== 0) {
+ this.projMatrix.mat4[0] = cam0.projMatrix.mat4[0] * Math.pow(cam1.projMatrix.mat4[0] / cam0.projMatrix.mat4[0], amt);
+ this.projMatrix.mat4[5] = cam0.projMatrix.mat4[5] * Math.pow(cam1.projMatrix.mat4[5] / cam0.projMatrix.mat4[5], amt);
+ // If the camera is active, make uPMatrix reflect changes in projMatrix.
+ if (this._isActive()) {
+ this._renderer.uPMatrix.mat4 = this.projMatrix.mat4.slice();
+ }
+ } // prepare eye vector and center vector of argument cameras.
+
+ var eye0 = new _main.default.Vector(cam0.eyeX, cam0.eyeY, cam0.eyeZ);
+ var eye1 = new _main.default.Vector(cam1.eyeX, cam1.eyeY, cam1.eyeZ);
+ var center0 = new _main.default.Vector(cam0.centerX, cam0.centerY, cam0.centerZ);
+ var center1 = new _main.default.Vector(cam1.centerX, cam1.centerY, cam1.centerZ);
+ // Calculate the distance between eye and center for each camera.
+ // Logarithmically interpolate these with amt.
+ var dist0 = _main.default.Vector.dist(eye0, center0);
+ var dist1 = _main.default.Vector.dist(eye1, center1);
+ var lerpedDist = dist0 * Math.pow(dist1 / dist0, amt);
+ // Next, calculate the ratio to interpolate the eye and center by a constant
+ // ratio for each camera. This ratio is the same for both. Also, with this ratio
+ // of points, the distance is the minimum distance of the two points of
+ // the same ratio.
+ // With this method, if the viewpoint is fixed, linear interpolation is performed
+ // at the viewpoint, and if the center is fixed, linear interpolation is performed
+ // at the center, resulting in reasonable interpolation. If both move, the point
+ // halfway between them is taken.
+ var eyeDiff = _main.default.Vector.sub(eye0, eye1);
+ var diffDiff = eye0.copy().sub(eye1).sub(center0).add(center1);
+ // Suppose there are two line segments. Consider the distance between the points
+ // above them as if they were taken in the same ratio. This calculation figures out
+ // a ratio that minimizes this.
+ // Each line segment is, a line segment connecting the viewpoint and the center
+ // for each camera.
+ var divider = diffDiff.magSq();
+ var ratio = 1; // default.
+ if (divider > 0.000001) {
+ ratio = _main.default.Vector.dot(eyeDiff, diffDiff) / divider;
+ ratio = Math.max(0, Math.min(ratio, 1));
+ } // Take the appropriate proportions and work out the points
+ // that are between the new viewpoint and the new center position.
+
+ var lerpedMedium = _main.default.Vector.lerp(_main.default.Vector.lerp(eye0, center0, ratio), _main.default.Vector.lerp(eye1, center1, ratio), amt);
+ // Prepare each of rotation matrix from their camera matrix
+ var rotMat0 = cam0.cameraMatrix.createSubMatrix3x3();
+ var rotMat1 = cam1.cameraMatrix.createSubMatrix3x3();
+ // get front and up vector from local-coordinate-system.
+ var front0 = rotMat0.row(2);
+ var front1 = rotMat1.row(2);
+ var up0 = rotMat0.row(1);
+ var up1 = rotMat1.row(1);
+ // prepare new vectors.
+ var newFront = new _main.default.Vector();
+ var newUp = new _main.default.Vector();
+ var newEye = new _main.default.Vector();
+ var newCenter = new _main.default.Vector();
+ // Create the inverse matrix of mat0 by transposing mat0,
+ // and multiply it to mat1 from the right.
+ // This matrix represents the difference between the two.
+ // 'deltaRot' means 'difference of rotation matrices'.
+ var deltaRot = rotMat1.mult3x3(rotMat0.copy().transpose3x3());
+ // Calculate the trace and from it the cos value of the angle.
+ // An orthogonal matrix is just an orthonormal basis. If this is not the identity
+ // matrix, it is a centered orthonormal basis plus some angle of rotation about
+ // some axis. That's the angle. Letting this be theta, trace becomes 1+2cos(theta).
+ // reference: https://en.wikipedia.org/wiki/Rotation_matrix#Determining_the_angle
+ var diag = deltaRot.diagonal();
+ var cosTheta = 0.5 * (diag[0] + diag[1] + diag[2] - 1);
+ // If the angle is close to 0, the two matrices are very close,
+ // so in that case we execute linearly interpolate.
+ if (1 - cosTheta < 1e-7) {
+ // Obtain the front vector and up vector by linear interpolation
+ // and normalize them.
+ // calculate newEye, newCenter with newFront vector.
+ newFront.set(_main.default.Vector.lerp(front0, front1, amt)).normalize();
+ newEye.set(newFront).mult(ratio * lerpedDist).add(lerpedMedium);
+ newCenter.set(newFront).mult((ratio - 1) * lerpedDist).add(lerpedMedium);
+ newUp.set(_main.default.Vector.lerp(up0, up1, amt)).normalize();
+ // set the camera
+ this.camera(newEye.x, newEye.y, newEye.z, newCenter.x, newCenter.y, newCenter.z, newUp.x, newUp.y, newUp.z);
+ return;
+ } // Calculates the axis vector and the angle of the difference orthogonal matrix.
+ // The axis vector is what I explained earlier in the comments.
+ // similar calculation is here:
+ // https://github.com/mrdoob/three.js/blob/883249620049d1632e8791732808fefd1a98c871/src/math/Quaternion.js#L294
+
+ var a,
+ b,
+ c,
+ sinTheta;
+ var invOneMinusCosTheta = 1 / (1 - cosTheta);
+ var maxDiag = Math.max(diag[0], diag[1], diag[2]);
+ var offDiagSum13 = deltaRot.mat3[1] + deltaRot.mat3[3];
+ var offDiagSum26 = deltaRot.mat3[2] + deltaRot.mat3[6];
+ var offDiagSum57 = deltaRot.mat3[5] + deltaRot.mat3[7];
+ if (maxDiag === diag[0]) {
+ a = Math.sqrt((diag[0] - cosTheta) * invOneMinusCosTheta); // not zero.
+ invOneMinusCosTheta /= a;
+ b = 0.5 * offDiagSum13 * invOneMinusCosTheta;
+ c = 0.5 * offDiagSum26 * invOneMinusCosTheta;
+ sinTheta = 0.5 * (deltaRot.mat3[7] - deltaRot.mat3[5]) / a;
+ } else if (maxDiag === diag[1]) {
+ b = Math.sqrt((diag[1] - cosTheta) * invOneMinusCosTheta); // not zero.
+ invOneMinusCosTheta /= b;
+ c = 0.5 * offDiagSum57 * invOneMinusCosTheta;
+ a = 0.5 * offDiagSum13 * invOneMinusCosTheta;
+ sinTheta = 0.5 * (deltaRot.mat3[2] - deltaRot.mat3[6]) / b;
+ } else {
+ c = Math.sqrt((diag[2] - cosTheta) * invOneMinusCosTheta); // not zero.
+ invOneMinusCosTheta /= c;
+ a = 0.5 * offDiagSum26 * invOneMinusCosTheta;
+ b = 0.5 * offDiagSum57 * invOneMinusCosTheta;
+ sinTheta = 0.5 * (deltaRot.mat3[3] - deltaRot.mat3[1]) / c;
+ } // Constructs a new matrix after interpolating the angles.
+ // Multiplying mat0 by the first matrix yields mat1, but by creating a state
+ // in the middle of that matrix, you can obtain a matrix that is
+ // an intermediate state between mat0 and mat1.
+
+ var angle = amt * Math.atan2(sinTheta, cosTheta);
+ var cosAngle = Math.cos(angle);
+ var sinAngle = Math.sin(angle);
+ var oneMinusCosAngle = 1 - cosAngle;
+ var ab = a * b;
+ var bc = b * c;
+ var ca = c * a;
+ var lerpedRotMat = new _main.default.Matrix('mat3', [
+ cosAngle + oneMinusCosAngle * a * a,
+ oneMinusCosAngle * ab + sinAngle * c,
+ oneMinusCosAngle * ca - sinAngle * b,
+ oneMinusCosAngle * ab - sinAngle * c,
+ cosAngle + oneMinusCosAngle * b * b,
+ oneMinusCosAngle * bc + sinAngle * a,
+ oneMinusCosAngle * ca + sinAngle * b,
+ oneMinusCosAngle * bc - sinAngle * a,
+ cosAngle + oneMinusCosAngle * c * c
+ ]);
+ // Multiply this to mat0 from left to get the interpolated front vector.
+ // calculate newEye, newCenter with newFront vector.
+ lerpedRotMat.multiplyVec3(front0, newFront);
+ newEye.set(newFront).mult(ratio * lerpedDist).add(lerpedMedium);
+ newCenter.set(newFront).mult((ratio - 1) * lerpedDist).add(lerpedMedium);
+ lerpedRotMat.multiplyVec3(up0, newUp);
+ // We also get the up vector in the same way and set the camera.
+ // The eye position and center position are calculated based on the front vector.
+ this.camera(newEye.x, newEye.y, newEye.z, newCenter.x, newCenter.y, newCenter.z, newUp.x, newUp.y, newUp.z);
+ } ////////////////////////////////////////////////////////////////////////////////
+ // Camera Helper Methods
+ ////////////////////////////////////////////////////////////////////////////////
+ // @TODO: combine this function with _setDefaultCamera to compute these values
+ // as-needed
+
+ },
+ {
+ key: '_computeCameraDefaultSettings',
+ value: function _computeCameraDefaultSettings() {
+ this.defaultAspectRatio = this._renderer.width / this._renderer.height;
+ this.defaultEyeX = 0;
+ this.defaultEyeY = 0;
+ this.defaultEyeZ = 800;
+ this.defaultCameraFOV = 2 * Math.atan(this._renderer.height / 2 / this.defaultEyeZ);
+ 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
+
+ },
+ {
+ key: '_setDefaultCamera',
+ value: function _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';
+ }
+ },
+ {
+ key: '_resize',
+ value: function _resize() {
+ // If we're using the default camera, update the aspect ratio
+ if (this.cameraType === 'default') {
+ this._computeCameraDefaultSettings();
+ this.cameraFOV = this.defaultCameraFOV;
+ this.aspectRatio = this.defaultAspectRatio;
+ this.perspective();
+ }
+ } /**
+ * Returns a copy of a camera.
+ * @method copy
+ * @private
+ */
+
+ },
+ {
+ key: 'copy',
+ value: function copy() {
+ var _cam = new _main.default.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();
+ _cam.yScale = this.yScale;
+ 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
+ */
+
+ },
+ {
+ key: '_getLocalAxes',
+ value: function _getLocalAxes() {
+ // calculate camera local Z vector
+ var z0 = this.eyeX - this.centerX;
+ var z1 = this.eyeY - this.centerY;
+ var z2 = this.eyeZ - this.centerZ;
+ // normalize camera local Z vector
+ var eyeDist = Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
+ if (eyeDist !== 0) {
+ z0 /= eyeDist;
+ z1 /= eyeDist;
+ z2 /= eyeDist;
+ } // calculate camera Y vector
+
+ var y0 = this.upX;
+ var y1 = this.upY;
+ var y2 = this.upZ;
+ // compute camera local X vector as up vector (local Y) cross local Z
+ var x0 = y1 * z2 - y2 * z1;
+ var x1 = - y0 * z2 + y2 * z0;
+ var 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:
+ var xmag = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
+ if (xmag !== 0) {
+ x0 /= xmag;
+ x1 /= xmag;
+ x2 /= xmag;
+ }
+ var 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
+ * @param {Number} dTheta change in spherical coordinate theta
+ * @param {Number} dPhi change in spherical coordinate phi
+ * @param {Number} dRadius change in radius
+ */
+
+ },
+ {
+ key: '_orbit',
+ value: function _orbit(dTheta, dPhi, dRadius) {
+ // Calculate the vector and its magnitude from the center to the viewpoint
+ var diffX = this.eyeX - this.centerX;
+ var diffY = this.eyeY - this.centerY;
+ var diffZ = this.eyeZ - this.centerZ;
+ var camRadius = Math.hypot(diffX, diffY, diffZ);
+ // front vector. unit vector from center to eye.
+ var front = new _main.default.Vector(diffX, diffY, diffZ).normalize();
+ // up vector. normalized camera's up vector.
+ var up = new _main.default.Vector(this.upX, this.upY, this.upZ).normalize(); // y-axis
+ // side vector. Right when viewed from the front
+ var side = _main.default.Vector.cross(up, front).normalize(); // x-axis
+ // vertical vector. normalized vector of projection of front vector.
+ var vertical = _main.default.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.
+
+ var camPhi = Math.acos(Math.max( - 1, Math.min(1, _main.default.Vector.dot(front, up)))) + dPhi;
+ // Rotate by dTheta in the shortest direction from "vertical" to "side"
+ var 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);
+ } /**
+ * Orbits the camera about center point. For use with orbitControl().
+ * Unlike _orbit(), the direction of rotation always matches the direction of pointer movement.
+ * @method _orbitFree
+ * @private
+ * @param {Number} dx the x component of the rotation vector.
+ * @param {Number} dy the y component of the rotation vector.
+ * @param {Number} dRadius change in radius
+ */
+
+ },
+ {
+ key: '_orbitFree',
+ value: function _orbitFree(dx, dy, dRadius) {
+ // Calculate the vector and its magnitude from the center to the viewpoint
+ var diffX = this.eyeX - this.centerX;
+ var diffY = this.eyeY - this.centerY;
+ var diffZ = this.eyeZ - this.centerZ;
+ var camRadius = Math.hypot(diffX, diffY, diffZ);
+ // front vector. unit vector from center to eye.
+ var front = new _main.default.Vector(diffX, diffY, diffZ).normalize();
+ // up vector. camera's up vector.
+ var up = new _main.default.Vector(this.upX, this.upY, this.upZ);
+ // side vector. Right when viewed from the front. (like x-axis)
+ var side = _main.default.Vector.cross(up, front).normalize();
+ // down vector. Bottom when viewed from the front. (like y-axis)
+ var down = _main.default.Vector.cross(front, side);
+ // side vector and down vector are no longer used as-is.
+ // Create a vector representing the direction of rotation
+ // in the form cos(direction)*side + sin(direction)*down.
+ // Make the current side vector into this.
+ var directionAngle = Math.atan2(dy, dx);
+ down.mult(Math.sin(directionAngle));
+ side.mult(Math.cos(directionAngle)).add(down);
+ // The amount of rotation is the size of the vector (dx, dy).
+ var rotAngle = Math.sqrt(dx * dx + dy * dy);
+ // The vector that is orthogonal to both the front vector and
+ // the rotation direction vector is the rotation axis vector.
+ var axis = _main.default.Vector.cross(front, side);
+ // 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;
+ } // If the axis vector is likened to the z-axis, the front vector is
+ // the x-axis and the side vector is the y-axis. Rotate the up and front
+ // vectors respectively by thinking of them as rotations around the z-axis.
+ // Calculate the components by taking the dot product and
+ // calculate a rotation based on that.
+
+ var c = Math.cos(rotAngle);
+ var s = Math.sin(rotAngle);
+ var dotFront = up.dot(front);
+ var dotSide = up.dot(side);
+ var ux = dotFront * c + dotSide * s;
+ var uy = - dotFront * s + dotSide * c;
+ var uz = up.dot(axis);
+ up.x = ux * front.x + uy * side.x + uz * axis.x;
+ up.y = ux * front.y + uy * side.y + uz * axis.y;
+ up.z = ux * front.z + uy * side.z + uz * axis.z;
+ // We won't be using the side vector and the front vector anymore,
+ // so let's make the front vector into the vector from the center to the new eye.
+ side.mult( - s);
+ front.mult(c).add(side).mult(camRadius);
+ // it's complete. let's update camera.
+ this.camera(front.x + this.centerX, front.y + this.centerY, front.z + this.centerZ, this.centerX, this.centerY, this.centerZ, up.x, up.y, up.z);
+ } /**
+ * Returns true if camera is currently attached to renderer.
+ * @method _isActive
+ * @private
+ */
+
+ },
+ {
+ key: '_isActive',
+ value: function _isActive() {
+ return this === this._renderer._curCamera;
+ }
+ }
+ ]);
+ return Camera;
+ }();
+ /**
+ * Sets the current (active) camera of a 3D sketch.
+ *
+ * `setCamera()` allows for switching between multiple cameras created with
+ * createCamera().
+ *
+ * Note: `setCamera()` can only be used in WebGL mode.
+ *
+ * @method setCamera
+ * @param {p5.Camera} cam camera that should be made active.
+ * @for p5
+ *
+ * @example
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let cam1;
+ * let cam2;
+ * let usingCam1 = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = createCamera();
+ *
+ * // Create the second camera.
+ * // Place it at the top-left.
+ * // Point it at the origin.
+ * cam2 = createCamera();
+ * cam2.setPosition(400, -400, 800);
+ * cam2.lookAt(0, 0, 0);
+ *
+ * // Set the current camera to cam1.
+ * setCamera(cam1);
+ *
+ * describe('A white cube on a gray background. The camera toggles between frontal and aerial views when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the box.
+ * box();
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (usingCam1 === true) {
+ * setCamera(cam2);
+ * usingCam1 = false;
+ * } else {
+ * setCamera(cam1);
+ * usingCam1 = true;
+ * }
+ * }
+ *
+ *
+ */
+ _main.default.prototype.setCamera = function (cam) {
+ this._renderer._curCamera = cam;
+ // set the projection matrix (which is not normally updated each frame)
+ this._renderer.uPMatrix.set(cam.projMatrix);
+ };
+ var _default = _main.default.Camera;
+ exports.default = _default;
+ },
+ {
+ '../core/main': 303,
+ 'core-js/modules/es.array.slice': 186,
+ 'core-js/modules/es.math.hypot': 193,
+ 'core-js/modules/es.string.sub': 223
+ }
+ ],
+ 353: [
+ function (_dereq_, module, exports) {
+ 'use strict';
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.array.slice');
+ _dereq_('core-js/modules/es.math.log2');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.typed-array.float32-array');
+ _dereq_('core-js/modules/es.typed-array.copy-within');
+ _dereq_('core-js/modules/es.typed-array.every');
+ _dereq_('core-js/modules/es.typed-array.fill');
+ _dereq_('core-js/modules/es.typed-array.filter');
+ _dereq_('core-js/modules/es.typed-array.find');
+ _dereq_('core-js/modules/es.typed-array.find-index');
+ _dereq_('core-js/modules/es.typed-array.for-each');
+ _dereq_('core-js/modules/es.typed-array.includes');
+ _dereq_('core-js/modules/es.typed-array.index-of');
+ _dereq_('core-js/modules/es.typed-array.iterator');
+ _dereq_('core-js/modules/es.typed-array.join');
+ _dereq_('core-js/modules/es.typed-array.last-index-of');
+ _dereq_('core-js/modules/es.typed-array.map');
+ _dereq_('core-js/modules/es.typed-array.reduce');
+ _dereq_('core-js/modules/es.typed-array.reduce-right');
+ _dereq_('core-js/modules/es.typed-array.reverse');
+ _dereq_('core-js/modules/es.typed-array.set');
+ _dereq_('core-js/modules/es.typed-array.slice');
+ _dereq_('core-js/modules/es.typed-array.some');
+ _dereq_('core-js/modules/es.typed-array.sort');
+ _dereq_('core-js/modules/es.typed-array.subarray');
+ _dereq_('core-js/modules/es.typed-array.to-locale-string');
+ _dereq_('core-js/modules/es.typed-array.to-string');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.array.slice');
+ _dereq_('core-js/modules/es.math.log2');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.typed-array.float32-array');
+ _dereq_('core-js/modules/es.typed-array.copy-within');
+ _dereq_('core-js/modules/es.typed-array.every');
+ _dereq_('core-js/modules/es.typed-array.fill');
+ _dereq_('core-js/modules/es.typed-array.filter');
+ _dereq_('core-js/modules/es.typed-array.find');
+ _dereq_('core-js/modules/es.typed-array.find-index');
+ _dereq_('core-js/modules/es.typed-array.for-each');
+ _dereq_('core-js/modules/es.typed-array.includes');
+ _dereq_('core-js/modules/es.typed-array.index-of');
+ _dereq_('core-js/modules/es.typed-array.iterator');
+ _dereq_('core-js/modules/es.typed-array.join');
+ _dereq_('core-js/modules/es.typed-array.last-index-of');
+ _dereq_('core-js/modules/es.typed-array.map');
+ _dereq_('core-js/modules/es.typed-array.reduce');
+ _dereq_('core-js/modules/es.typed-array.reduce-right');
+ _dereq_('core-js/modules/es.typed-array.reverse');
+ _dereq_('core-js/modules/es.typed-array.set');
+ _dereq_('core-js/modules/es.typed-array.slice');
+ _dereq_('core-js/modules/es.typed-array.some');
+ _dereq_('core-js/modules/es.typed-array.sort');
+ _dereq_('core-js/modules/es.typed-array.subarray');
+ _dereq_('core-js/modules/es.typed-array.to-locale-string');
+ _dereq_('core-js/modules/es.typed-array.to-string');
+ Object.defineProperty(exports, '__esModule', {
+ value: true
+ });
+ exports.default = void 0;
+ var _main = _interopRequireDefault(_dereq_('../core/main'));
+ function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : {
+ default:
+ obj
+ };
+ }
+ function _classCallCheck(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError('Cannot call a class as a function');
+ }
+ }
+ function _defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ('value' in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+ function _createClass(Constructor, protoProps, staticProps) {
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) _defineProperties(Constructor, staticProps);
+ return Constructor;
+ } /**
+ * An internal class to store data that will be sent to a p5.RenderBuffer.
+ * Those need to eventually go into a Float32Array, so this class provides a
+ * variable-length array container backed by a Float32Array so that it can be
+ * sent to the GPU without allocating a new array each frame.
+ *
+ * Like a C++ vector, its fixed-length Float32Array backing its contents will
+ * double in size when it goes over its capacity.
+ *
+ * @example
+ *
+ *
+ * // Initialize storage with a capacity of 4
+ * const storage = new DataArray(4);
+ * console.log(storage.data.length); // 4
+ * console.log(storage.length); // 0
+ * console.log(storage.dataArray()); // Empty Float32Array
+ *
+ * storage.push(1, 2, 3, 4, 5, 6);
+ * console.log(storage.data.length); // 8
+ * console.log(storage.length); // 6
+ * console.log(storage.dataArray()); // Float32Array{1, 2, 3, 4, 5, 6}
+ *
+ *
+ */
+
+ _main.default.DataArray = /*#__PURE__*/ function () {
+ function DataArray() {
+ var initialLength = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 128;
+ _classCallCheck(this, DataArray);
+ this.length = 0;
+ this.data = new Float32Array(initialLength);
+ this.initialLength = initialLength;
+ } /**
+ * Returns a Float32Array window sized to the exact length of the data
+ */
+
+ _createClass(DataArray, [
+ {
+ key: 'dataArray',
+ value: function dataArray() {
+ return this.subArray(0, this.length);
+ } /**
+ * A "soft" clear, which keeps the underlying storage size the same, but
+ * empties the contents of its dataArray()
+ */
+
+ },
+ {
+ key: 'clear',
+ value: function clear() {
+ this.length = 0;
+ } /**
+ * Can be used to scale a DataArray back down to fit its contents.
+ */
+
+ },
+ {
+ key: 'rescale',
+ value: function rescale() {
+ if (this.length < this.data.length / 2) {
+ // Find the power of 2 size that fits the data
+ var targetLength = 1 << Math.ceil(Math.log2(this.length));
+ var newData = new Float32Array(targetLength);
+ newData.set(this.data.subarray(0, this.length), 0);
+ this.data = newData;
+ }
+ } /**
+ * A full reset, which allocates a new underlying Float32Array at its initial
+ * length
+ */
+
+ },
+ {
+ key: 'reset',
+ value: function reset() {
+ this.clear();
+ this.data = new Float32Array(this.initialLength);
+ } /**
+ * Adds values to the DataArray, expanding its internal storage to
+ * accommodate the new items.
+ */
+
+ },
+ {
+ key: 'push',
+ value: function push() {
+ for (var _len = arguments.length, values = new Array(_len), _key = 0; _key < _len; _key++) {
+ values[_key] = arguments[_key];
+ }
+ this.ensureLength(this.length + values.length);
+ this.data.set(values, this.length);
+ this.length += values.length;
+ } /**
+ * Returns a copy of the data from the index `from`, inclusive, to the index
+ * `to`, exclusive
+ */
+
+ },
+ {
+ key: 'slice',
+ value: function slice(from, to) {
+ return this.data.slice(from, Math.min(to, this.length));
+ } /**
+ * Returns a mutable Float32Array window from the index `from`, inclusive, to
+ * the index `to`, exclusive
+ */
+
+ },
+ {
+ key: 'subArray',
+ value: function subArray(from, to) {
+ return this.data.subarray(from, Math.min(to, this.length));
+ } /**
+ * Expand capacity of the internal storage until it can fit a target size
+ */
+
+ },
+ {
+ key: 'ensureLength',
+ value: function ensureLength(target) {
+ while (this.data.length < target) {
+ var newData = new Float32Array(this.data.length * 2);
+ newData.set(this.data, 0);
+ this.data = newData;
+ }
+ }
+ }
+ ]);
+ return DataArray;
+ }();
+ var _default = _main.default.DataArray;
+ exports.default = _default;
+ },
+ {
+ '../core/main': 303,
+ 'core-js/modules/es.array.iterator': 182,
+ 'core-js/modules/es.array.slice': 186,
+ 'core-js/modules/es.math.log2': 194,
+ 'core-js/modules/es.object.to-string': 205,
+ 'core-js/modules/es.typed-array.copy-within': 228,
+ 'core-js/modules/es.typed-array.every': 229,
+ 'core-js/modules/es.typed-array.fill': 230,
+ 'core-js/modules/es.typed-array.filter': 231,
+ 'core-js/modules/es.typed-array.find': 233,
+ 'core-js/modules/es.typed-array.find-index': 232,
+ 'core-js/modules/es.typed-array.float32-array': 234,
+ 'core-js/modules/es.typed-array.for-each': 236,
+ 'core-js/modules/es.typed-array.includes': 237,
+ 'core-js/modules/es.typed-array.index-of': 238,
+ 'core-js/modules/es.typed-array.iterator': 241,
+ 'core-js/modules/es.typed-array.join': 242,
+ 'core-js/modules/es.typed-array.last-index-of': 243,
+ 'core-js/modules/es.typed-array.map': 244,
+ 'core-js/modules/es.typed-array.reduce': 246,
+ 'core-js/modules/es.typed-array.reduce-right': 245,
+ 'core-js/modules/es.typed-array.reverse': 247,
+ 'core-js/modules/es.typed-array.set': 248,
+ 'core-js/modules/es.typed-array.slice': 249,
+ 'core-js/modules/es.typed-array.some': 250,
+ 'core-js/modules/es.typed-array.sort': 251,
+ 'core-js/modules/es.typed-array.subarray': 252,
+ 'core-js/modules/es.typed-array.to-locale-string': 253,
+ 'core-js/modules/es.typed-array.to-string': 254
+ }
+ ],
+ 354: [
+ function (_dereq_, module, exports) {
+ 'use strict';
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.fill');
+ _dereq_('core-js/modules/es.array.includes');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.object.get-own-property-descriptor');
+ _dereq_('core-js/modules/es.object.get-prototype-of');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.reflect.construct');
+ _dereq_('core-js/modules/es.reflect.get');
+ _dereq_('core-js/modules/es.regexp.to-string');
+ _dereq_('core-js/modules/es.string.includes');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/es.typed-array.float32-array');
+ _dereq_('core-js/modules/es.typed-array.uint8-array');
+ _dereq_('core-js/modules/es.typed-array.uint8-clamped-array');
+ _dereq_('core-js/modules/es.typed-array.copy-within');
+ _dereq_('core-js/modules/es.typed-array.every');
+ _dereq_('core-js/modules/es.typed-array.fill');
+ _dereq_('core-js/modules/es.typed-array.filter');
+ _dereq_('core-js/modules/es.typed-array.find');
+ _dereq_('core-js/modules/es.typed-array.find-index');
+ _dereq_('core-js/modules/es.typed-array.for-each');
+ _dereq_('core-js/modules/es.typed-array.includes');
+ _dereq_('core-js/modules/es.typed-array.index-of');
+ _dereq_('core-js/modules/es.typed-array.iterator');
+ _dereq_('core-js/modules/es.typed-array.join');
+ _dereq_('core-js/modules/es.typed-array.last-index-of');
+ _dereq_('core-js/modules/es.typed-array.map');
+ _dereq_('core-js/modules/es.typed-array.reduce');
+ _dereq_('core-js/modules/es.typed-array.reduce-right');
+ _dereq_('core-js/modules/es.typed-array.reverse');
+ _dereq_('core-js/modules/es.typed-array.set');
+ _dereq_('core-js/modules/es.typed-array.slice');
+ _dereq_('core-js/modules/es.typed-array.some');
+ _dereq_('core-js/modules/es.typed-array.sort');
+ _dereq_('core-js/modules/es.typed-array.subarray');
+ _dereq_('core-js/modules/es.typed-array.to-locale-string');
+ _dereq_('core-js/modules/es.typed-array.to-string');
+ _dereq_('core-js/modules/es.weak-map');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ function _typeof2(obj) {
+ if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') {
+ _typeof2 = function _typeof2(obj) {
+ return typeof obj;
+ };
+ } else {
+ _typeof2 = function _typeof2(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
+ };
+ }
+ return _typeof2(obj);
+ }
+ function _typeof(obj) {
+ if (typeof Symbol === 'function' && _typeof2(Symbol.iterator) === 'symbol') {
+ _typeof = function _typeof(obj) {
+ return _typeof2(obj);
+ };
+ } else {
+ _typeof = function _typeof(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : _typeof2(obj);
+ };
+ }
+ return _typeof(obj);
+ }
+ _dereq_('core-js/modules/es.array.fill');
+ _dereq_('core-js/modules/es.array.includes');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.object.get-prototype-of');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.string.includes');
+ _dereq_('core-js/modules/es.typed-array.float32-array');
+ _dereq_('core-js/modules/es.typed-array.uint8-array');
+ _dereq_('core-js/modules/es.typed-array.uint8-clamped-array');
+ _dereq_('core-js/modules/es.typed-array.copy-within');
+ _dereq_('core-js/modules/es.typed-array.every');
+ _dereq_('core-js/modules/es.typed-array.fill');
+ _dereq_('core-js/modules/es.typed-array.filter');
+ _dereq_('core-js/modules/es.typed-array.find');
+ _dereq_('core-js/modules/es.typed-array.find-index');
+ _dereq_('core-js/modules/es.typed-array.for-each');
+ _dereq_('core-js/modules/es.typed-array.includes');
+ _dereq_('core-js/modules/es.typed-array.index-of');
+ _dereq_('core-js/modules/es.typed-array.iterator');
+ _dereq_('core-js/modules/es.typed-array.join');
+ _dereq_('core-js/modules/es.typed-array.last-index-of');
+ _dereq_('core-js/modules/es.typed-array.map');
+ _dereq_('core-js/modules/es.typed-array.reduce');
+ _dereq_('core-js/modules/es.typed-array.reduce-right');
+ _dereq_('core-js/modules/es.typed-array.reverse');
+ _dereq_('core-js/modules/es.typed-array.set');
+ _dereq_('core-js/modules/es.typed-array.slice');
+ _dereq_('core-js/modules/es.typed-array.some');
+ _dereq_('core-js/modules/es.typed-array.sort');
+ _dereq_('core-js/modules/es.typed-array.subarray');
+ _dereq_('core-js/modules/es.typed-array.to-locale-string');
+ _dereq_('core-js/modules/es.typed-array.to-string');
+ Object.defineProperty(exports, '__esModule', {
+ value: true
+ });
+ exports.default = void 0;
+ var _main = _interopRequireDefault(_dereq_('../core/main'));
+ var constants = _interopRequireWildcard(_dereq_('../core/constants'));
+ var _p = _dereq_('./p5.Texture');
+ var _p2 = _dereq_('./p5.RendererGL');
+ function _getRequireWildcardCache() {
+ if (typeof WeakMap !== 'function') return null;
+ var cache = new WeakMap();
+ _getRequireWildcardCache = function _getRequireWildcardCache() {
+ return cache;
+ };
+ return cache;
+ }
+ function _interopRequireWildcard(obj) {
+ if (obj && obj.__esModule) {
+ return obj;
+ }
+ if (obj === null || _typeof(obj) !== 'object' && typeof obj !== 'function') {
+ return {
+ default:
+ obj
+ };
+ }
+ var cache = _getRequireWildcardCache();
+ if (cache && cache.has(obj)) {
+ return cache.get(obj);
+ }
+ var newObj = {
+ };
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
+ if (desc && (desc.get || desc.set)) {
+ Object.defineProperty(newObj, key, desc);
+ } else {
+ newObj[key] = obj[key];
+ }
+ }
+ }
+ newObj.default = obj;
+ if (cache) {
+ cache.set(obj, newObj);
+ }
+ return newObj;
+ }
+ function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : {
+ default:
+ obj
+ };
+ }
+ function _slicedToArray(arr, i) {
+ return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
+ }
+ function _nonIterableRest() {
+ throw new TypeError('Invalid attempt to destructure non-iterable instance');
+ }
+ function _iterableToArrayLimit(arr, i) {
+ if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === '[object Arguments]')) {
+ return;
+ }
+ var _arr = [
+ ];
+ var _n = true;
+ var _d = false;
+ var _e = undefined;
+ try {
+ for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
+ _arr.push(_s.value);
+ if (i && _arr.length === i) break;
+ }
+ } catch (err) {
+ _d = true;
+ _e = err;
+ } finally {
+ try {
+ if (!_n && _i['return'] != null) _i['return']();
+ } finally {
+ if (_d) throw _e;
+ }
+ }
+ return _arr;
+ }
+ function _arrayWithHoles(arr) {
+ if (Array.isArray(arr)) return arr;
+ }
+ function _defineProperty(obj, key, value) {
+ if (key in obj) {
+ Object.defineProperty(obj, key, {
+ value: value,
+ enumerable: true,
+ configurable: true,
+ writable: true
+ });
+ } else {
+ obj[key] = value;
+ }
+ return obj;
+ }
+ function _classCallCheck(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError('Cannot call a class as a function');
+ }
+ }
+ function _defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ('value' in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+ function _createClass(Constructor, protoProps, staticProps) {
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) _defineProperties(Constructor, staticProps);
+ return Constructor;
+ }
+ function _get(target, property, receiver) {
+ if (typeof Reflect !== 'undefined' && Reflect.get) {
+ _get = Reflect.get;
+ } else {
+ _get = function _get(target, property, receiver) {
+ var base = _superPropBase(target, property);
+ if (!base) return;
+ var desc = Object.getOwnPropertyDescriptor(base, property);
+ if (desc.get) {
+ return desc.get.call(receiver);
+ }
+ return desc.value;
+ };
+ }
+ return _get(target, property, receiver || target);
+ }
+ function _superPropBase(object, property) {
+ while (!Object.prototype.hasOwnProperty.call(object, property)) {
+ object = _getPrototypeOf(object);
+ if (object === null) break;
+ }
+ return object;
+ }
+ function _inherits(subClass, superClass) {
+ if (typeof superClass !== 'function' && superClass !== null) {
+ throw new TypeError('Super expression must either be null or a function');
+ }
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
+ constructor: {
+ value: subClass,
+ writable: true,
+ configurable: true
+ }
+ });
+ if (superClass) _setPrototypeOf(subClass, superClass);
+ }
+ function _setPrototypeOf(o, p) {
+ _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
+ o.__proto__ = p;
+ return o;
+ };
+ return _setPrototypeOf(o, p);
+ }
+ function _createSuper(Derived) {
+ function isNativeReflectConstruct() {
+ if (typeof Reflect === 'undefined' || !Reflect.construct) return false;
+ if (Reflect.construct.sham) return false;
+ if (typeof Proxy === 'function') return true;
+ try {
+ Date.prototype.toString.call(Reflect.construct(Date, [
+ ], function () {
+ }));
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+ return function () {
+ var Super = _getPrototypeOf(Derived),
+ result;
+ if (isNativeReflectConstruct()) {
+ var NewTarget = _getPrototypeOf(this).constructor;
+ result = Reflect.construct(Super, arguments, NewTarget);
+ } else {
+ result = Super.apply(this, arguments);
+ }
+ return _possibleConstructorReturn(this, result);
+ };
+ }
+ function _possibleConstructorReturn(self, call) {
+ if (call && (_typeof(call) === 'object' || typeof call === 'function')) {
+ return call;
+ }
+ return _assertThisInitialized(self);
+ }
+ function _assertThisInitialized(self) {
+ if (self === void 0) {
+ throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');
+ }
+ return self;
+ }
+ function _getPrototypeOf(o) {
+ _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
+ return o.__proto__ || Object.getPrototypeOf(o);
+ };
+ return _getPrototypeOf(o);
+ } /**
+ * @module Rendering
+ * @requires constants
+ */
+
+ var FramebufferCamera = /*#__PURE__*/ function (_p5$Camera) {
+ _inherits(FramebufferCamera, _p5$Camera);
+ var _super = _createSuper(FramebufferCamera);
+ /**
+ * A p5.Camera attached to a
+ * p5.Framebuffer.
+ *
+ * @class p5.FramebufferCamera
+ * @constructor
+ * @param {p5.Framebuffer} framebuffer The framebuffer this camera is
+ * attached to
+ * @private
+ */
+ function FramebufferCamera(framebuffer) {
+ var _this;
+ _classCallCheck(this, FramebufferCamera);
+ _this = _super.call(this, framebuffer.target._renderer);
+ _this.fbo = framebuffer;
+ // WebGL textures are upside-down compared to textures that come from
+ // images and graphics. Framebuffer cameras need to invert their y
+ // axes when being rendered to so that the texture comes out rightway up
+ // when read in shaders or image().
+ _this.yScale = - 1;
+ return _this;
+ }
+ _createClass(FramebufferCamera, [
+ {
+ key: '_computeCameraDefaultSettings',
+ value: function _computeCameraDefaultSettings() {
+ _get(_getPrototypeOf(FramebufferCamera.prototype), '_computeCameraDefaultSettings', this).call(this);
+ this.defaultAspectRatio = this.fbo.width / this.fbo.height;
+ this.defaultCameraFOV = 2 * Math.atan(this.fbo.height / 2 / this.defaultEyeZ);
+ }
+ }
+ ]);
+ return FramebufferCamera;
+ }(_main.default.Camera);
+ _main.default.FramebufferCamera = FramebufferCamera;
+ var FramebufferTexture = /*#__PURE__*/ function () {
+ /**
+ * A p5.Texture corresponding to a property of a
+ * p5.Framebuffer.
+ *
+ * @class p5.FramebufferTexture
+ * @param {p5.Framebuffer} framebuffer The framebuffer represented by this
+ * texture
+ * @param {String} property The property of the framebuffer represented by
+ * this texture, either `color` or `depth`
+ * @private
+ */
+ function FramebufferTexture(framebuffer, property) {
+ _classCallCheck(this, FramebufferTexture);
+ this.framebuffer = framebuffer;
+ this.property = property;
+ }
+ _createClass(FramebufferTexture, [
+ {
+ key: 'rawTexture',
+ value: function rawTexture() {
+ return this.framebuffer[this.property];
+ }
+ },
+ {
+ key: 'width',
+ get: function get() {
+ return this.framebuffer.width * this.framebuffer.density;
+ }
+ },
+ {
+ key: 'height',
+ get: function get() {
+ return this.framebuffer.height * this.framebuffer.density;
+ }
+ }
+ ]);
+ return FramebufferTexture;
+ }();
+ _main.default.FramebufferTexture = FramebufferTexture;
+ var Framebuffer = /*#__PURE__*/ function () {
+ /**
+ * A class to describe a high-performance drawing surface for textures.
+ *
+ * Each `p5.Framebuffer` object provides a dedicated drawing surface called
+ * a *framebuffer*. They're similar to
+ * p5.Graphics objects but can run much faster.
+ * Performance is improved because the framebuffer shares the same WebGL
+ * context as the canvas used to create it.
+ *
+ * `p5.Framebuffer` objects have all the drawing features of the main
+ * canvas. Drawing instructions meant for the framebuffer must be placed
+ * between calls to
+ * myBuffer.begin() and
+ * myBuffer.end(). The resulting image
+ * can be applied as a texture by passing the `p5.Framebuffer` object to the
+ * texture() function, as in `texture(myBuffer)`.
+ * It can also be displayed on the main canvas by passing it to the
+ * image() function, as in `image(myBuffer, 0, 0)`.
+ *
+ * Note: createFramebuffer() is the
+ * recommended way to create an instance of this class.
+ *
+ * @class p5.Framebuffer
+ * @constructor
+ * @param {p5.Graphics|p5} target sketch instance or
+ * p5.Graphics
+ * object.
+ * @param {Object} [settings] configuration options.
+ */
+ function Framebuffer(target) {
+ var _this2 = this;
+ var settings = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
+ };
+ _classCallCheck(this, Framebuffer);
+ this.target = target;
+ this.target._renderer.framebuffers.add(this);
+ this._isClipApplied = false;
+ /**
+ * An array containing the color of each pixel in the framebuffer.
+ *
+ * myBuffer.loadPixels() must be
+ * called before accessing the `myBuffer.pixels` array.
+ * myBuffer.updatePixels()
+ * must be called after any changes are made.
+ *
+ * Note: Updating pixels via this property is slower than drawing to the
+ * framebuffer directly. Consider using a
+ * p5.Shader object instead of looping over
+ * `myBuffer.pixels`.
+ *
+ * @property {Number[]} pixels
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Create a p5.Framebuffer object.
+ * let myBuffer = createFramebuffer();
+ *
+ * // Load the pixels array.
+ * myBuffer.loadPixels();
+ *
+ * // Get the number of pixels in the
+ * // top half of the framebuffer.
+ * let numPixels = myBuffer.pixels.length / 2;
+ *
+ * // Set the framebuffer's top half to pink.
+ * for (let i = 0; i < numPixels; i += 4) {
+ * myBuffer.pixels[i] = 255;
+ * myBuffer.pixels[i + 1] = 102;
+ * myBuffer.pixels[i + 2] = 204;
+ * myBuffer.pixels[i + 3] = 255;
+ * }
+ *
+ * // Update the pixels array.
+ * myBuffer.updatePixels();
+ *
+ * // Draw the p5.Framebuffer object to the canvas.
+ * image(myBuffer, -50, -50);
+ *
+ * describe('A pink rectangle above a gray rectangle.');
+ * }
+ *
+ *
+ */
+ this.pixels = [
+ ];
+ this.format = settings.format || constants.UNSIGNED_BYTE;
+ this.channels = settings.channels || (target._renderer._pInst._glAttributes.alpha ? constants.RGBA : constants.RGB);
+ this.useDepth = settings.depth === undefined ? true : settings.depth;
+ this.depthFormat = settings.depthFormat || constants.FLOAT;
+ this.textureFiltering = settings.textureFiltering || constants.LINEAR;
+ if (settings.antialias === undefined) {
+ this.antialiasSamples = target._renderer._pInst._glAttributes.antialias ? 2 : 0;
+ } else if (typeof settings.antialias === 'number') {
+ this.antialiasSamples = settings.antialias;
+ } else {
+ this.antialiasSamples = settings.antialias ? 2 : 0;
+ }
+ this.antialias = this.antialiasSamples > 0;
+ if (this.antialias && target.webglVersion !== constants.WEBGL2) {
+ console.warn('Antialiasing is unsupported in a WebGL 1 context');
+ this.antialias = false;
+ }
+ this.density = settings.density || target.pixelDensity();
+ var gl = target._renderer.GL;
+ this.gl = gl;
+ if (settings.width && settings.height) {
+ var dimensions = target._renderer._adjustDimensions(settings.width, settings.height);
+ this.width = dimensions.adjustedWidth;
+ this.height = dimensions.adjustedHeight;
+ this._autoSized = false;
+ } else {
+ if (settings.width === undefined !== (settings.height === undefined)) {
+ console.warn('Please supply both width and height for a framebuffer to give it a ' + 'size. Only one was given, so the framebuffer will match the size ' + 'of its canvas.');
+ }
+ this.width = target.width;
+ this.height = target.height;
+ this._autoSized = true;
+ }
+ this._checkIfFormatsAvailable();
+ if (settings.stencil && !this.useDepth) {
+ console.warn('A stencil buffer can only be used if also using depth. Since the framebuffer has no depth buffer, the stencil buffer will be ignored.');
+ }
+ this.useStencil = this.useDepth && (settings.stencil === undefined ? true : settings.stencil);
+ this.framebuffer = gl.createFramebuffer();
+ if (!this.framebuffer) {
+ throw new Error('Unable to create a framebuffer');
+ }
+ if (this.antialias) {
+ this.aaFramebuffer = gl.createFramebuffer();
+ if (!this.aaFramebuffer) {
+ throw new Error('Unable to create a framebuffer for antialiasing');
+ }
+ }
+ this._recreateTextures();
+ var prevCam = this.target._renderer._curCamera;
+ this.defaultCamera = this.createCamera();
+ this.filterCamera = this.createCamera();
+ this.target._renderer._curCamera = prevCam;
+ this.draw(function () {
+ return _this2.target.clear();
+ });
+ } /**
+ * Resizes the framebuffer to a given width and height.
+ *
+ * The parameters, `width` and `height`, set the dimensions of the
+ * framebuffer. For example, calling `myBuffer.resize(300, 500)` resizes
+ * the framebuffer to 300×500 pixels, then sets `myBuffer.width` to 300
+ * and `myBuffer.height` 500.
+ *
+ * @method resize
+ * @param {Number} width width of the framebuffer.
+ * @param {Number} height height of the framebuffer.
+ *
+ * @example
+ *
+ *
+ * let myBuffer;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Framebuffer object.
+ * myBuffer = createFramebuffer();
+ *
+ * describe('A multicolor sphere on a white surface. The image grows larger or smaller when the user moves the mouse, revealing a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw to the p5.Framebuffer object.
+ * myBuffer.begin();
+ * background(255);
+ * normalMaterial();
+ * sphere(20);
+ * myBuffer.end();
+ *
+ * // Display the p5.Framebuffer object.
+ * image(myBuffer, -50, -50);
+ * }
+ *
+ * // Resize the p5.Framebuffer object when the
+ * // user moves the mouse.
+ * function mouseMoved() {
+ * myBuffer.resize(mouseX, mouseY);
+ * }
+ *
+ *
+ */
+
+ _createClass(Framebuffer, [
+ {
+ key: 'resize',
+ value: function resize(width, height) {
+ this._autoSized = false;
+ var dimensions = this.target._renderer._adjustDimensions(width, height);
+ width = dimensions.adjustedWidth;
+ height = dimensions.adjustedHeight;
+ this.width = width;
+ this.height = height;
+ this._handleResize();
+ } /**
+ * Sets the framebuffer's pixel density or returns its current density.
+ *
+ * Computer displays are grids of little lights called pixels. A display's
+ * pixel density describes how many pixels it packs into an area. Displays
+ * with smaller pixels have a higher pixel density and create sharper
+ * images.
+ *
+ * The parameter, `density`, is optional. If a number is passed, as in
+ * `myBuffer.pixelDensity(1)`, it sets the framebuffer's pixel density. By
+ * default, the framebuffer's pixel density will match that of the canvas
+ * where it was created. All canvases default to match the display's pixel
+ * density.
+ *
+ * Calling `myBuffer.pixelDensity()` without an argument returns its current
+ * pixel density.
+ *
+ * @method pixelDensity
+ * @param {Number} [density] pixel density to set.
+ * @returns {Number} current pixel density.
+ *
+ * @example
+ *
+ *
+ * let myBuffer;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Framebuffer object.
+ * myBuffer = createFramebuffer();
+ *
+ * describe("A white circle on a gray canvas. The circle's edge become fuzzy while the user presses and holds the mouse.");
+ * }
+ *
+ * function draw() {
+ * // Draw to the p5.Framebuffer object.
+ * myBuffer.begin();
+ * background(200);
+ * circle(0, 0, 40);
+ * myBuffer.end();
+ *
+ * // Display the p5.Framebuffer object.
+ * image(myBuffer, -50, -50);
+ * }
+ *
+ * // Decrease the pixel density when the user
+ * // presses the mouse.
+ * function mousePressed() {
+ * myBuffer.pixelDensity(1);
+ * }
+ *
+ * // Increase the pixel density when the user
+ * // releases the mouse.
+ * function mouseReleased() {
+ * myBuffer.pixelDensity(2);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * let myBuffer;
+ * let myFont;
+ *
+ * // Load a font and create a p5.Font object.
+ * function preload() {
+ * myFont = loadFont('assets/inconsolata.otf');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Create a p5.Framebuffer object.
+ * myBuffer = createFramebuffer();
+ *
+ * // Get the p5.Framebuffer object's pixel density.
+ * let d = myBuffer.pixelDensity();
+ *
+ * // Style the text.
+ * textAlign(CENTER, CENTER);
+ * textFont(myFont);
+ * textSize(16);
+ * fill(0);
+ *
+ * // Display the pixel density.
+ * text(`Density: ${d}`, 0, 0);
+ *
+ * describe(`The text "Density: ${d}" written in black on a gray background.`);
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'pixelDensity',
+ value: function pixelDensity(density) {
+ if (density) {
+ this._autoSized = false;
+ this.density = density;
+ this._handleResize();
+ } else {
+ return this.density;
+ }
+ } /**
+ * Toggles the framebuffer's autosizing mode or returns the current mode.
+ *
+ * By default, the framebuffer automatically resizes to match the canvas
+ * that created it. Calling `myBuffer.autoSized(false)` disables this
+ * behavior and calling `myBuffer.autoSized(true)` re-enables it.
+ *
+ * Calling `myBuffer.autoSized()` without an argument returns `true` if
+ * the framebuffer automatically resizes and `false` if not.
+ *
+ * @method autoSized
+ * @param {Boolean} [autoSized] whether to automatically resize the framebuffer to match the canvas.
+ * @returns {Boolean} current autosize setting.
+ *
+ * @example
+ *
+ *
+ * // Double-click to toggle the autosizing mode.
+ *
+ * let myBuffer;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Framebuffer object.
+ * myBuffer = createFramebuffer();
+ *
+ * describe('A multicolor sphere on a gray background. The image resizes when the user moves the mouse.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Draw to the p5.Framebuffer object.
+ * myBuffer.begin();
+ * background(200);
+ * normalMaterial();
+ * sphere(width / 4);
+ * myBuffer.end();
+ *
+ * // Display the p5.Framebuffer object.
+ * image(myBuffer, -width / 2, -height / 2);
+ * }
+ *
+ * // Resize the canvas when the user moves the mouse.
+ * function mouseMoved() {
+ * let w = constrain(mouseX, 0, 100);
+ * let h = constrain(mouseY, 0, 100);
+ * resizeCanvas(w, h);
+ * }
+ *
+ * // Toggle autoSizing when the user double-clicks.
+ * // Note: opened an issue to fix(?) this.
+ * function doubleClicked() {
+ * let isAuto = myBuffer.autoSized();
+ * myBuffer.autoSized(!isAuto);
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'autoSized',
+ value: function autoSized(_autoSized) {
+ if (_autoSized === undefined) {
+ return this._autoSized;
+ } else {
+ this._autoSized = _autoSized;
+ this._handleResize();
+ }
+ } /**
+ * Checks the capabilities of the current WebGL environment to see if the
+ * settings supplied by the user are capable of being fulfilled. If they
+ * are not, warnings will be logged and the settings will be changed to
+ * something close that can be fulfilled.
+ *
+ * @private
+ */
+
+ },
+ {
+ key: '_checkIfFormatsAvailable',
+ value: function _checkIfFormatsAvailable() {
+ var gl = this.gl;
+ if (this.useDepth && this.target.webglVersion === constants.WEBGL && !gl.getExtension('WEBGL_depth_texture')) {
+ console.warn('Unable to create depth textures in this environment. Falling back ' + 'to a framebuffer without depth.');
+ this.useDepth = false;
+ }
+ if (this.useDepth && this.target.webglVersion === constants.WEBGL && this.depthFormat === constants.FLOAT) {
+ console.warn('FLOAT depth format is unavailable in WebGL 1. ' + 'Defaulting to UNSIGNED_INT.');
+ this.depthFormat = constants.UNSIGNED_INT;
+ }
+ if (![constants.UNSIGNED_BYTE,
+ constants.FLOAT,
+ constants.HALF_FLOAT].includes(this.format)) {
+ console.warn('Unknown Framebuffer format. ' + 'Please use UNSIGNED_BYTE, FLOAT, or HALF_FLOAT. ' + 'Defaulting to UNSIGNED_BYTE.');
+ this.format = constants.UNSIGNED_BYTE;
+ }
+ if (this.useDepth && ![constants.UNSIGNED_INT,
+ constants.FLOAT].includes(this.depthFormat)) {
+ console.warn('Unknown Framebuffer depth format. ' + 'Please use UNSIGNED_INT or FLOAT. Defaulting to FLOAT.');
+ this.depthFormat = constants.FLOAT;
+ }
+ var support = (0, _p.checkWebGLCapabilities) (this.target._renderer);
+ if (!support.float && this.format === constants.FLOAT) {
+ console.warn('This environment does not support FLOAT textures. ' + 'Falling back to UNSIGNED_BYTE.');
+ this.format = constants.UNSIGNED_BYTE;
+ }
+ if (this.useDepth && !support.float && this.depthFormat === constants.FLOAT) {
+ console.warn('This environment does not support FLOAT depth textures. ' + 'Falling back to UNSIGNED_INT.');
+ this.depthFormat = constants.UNSIGNED_INT;
+ }
+ if (!support.halfFloat && this.format === constants.HALF_FLOAT) {
+ console.warn('This environment does not support HALF_FLOAT textures. ' + 'Falling back to UNSIGNED_BYTE.');
+ this.format = constants.UNSIGNED_BYTE;
+ }
+ if (this.channels === constants.RGB && [
+ constants.FLOAT,
+ constants.HALF_FLOAT
+ ].includes(this.format)) {
+ console.warn('FLOAT and HALF_FLOAT formats do not work cross-platform with only ' + 'RGB channels. Falling back to RGBA.');
+ this.channels = constants.RGBA;
+ }
+ } /**
+ * Creates new textures and renderbuffers given the current size of the
+ * framebuffer.
+ *
+ * @private
+ */
+
+ },
+ {
+ key: '_recreateTextures',
+ value: function _recreateTextures() {
+ var gl = this.gl;
+ this._updateSize();
+ var prevBoundTexture = gl.getParameter(gl.TEXTURE_BINDING_2D);
+ var prevBoundFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
+ var colorTexture = gl.createTexture();
+ if (!colorTexture) {
+ throw new Error('Unable to create color texture');
+ }
+ gl.bindTexture(gl.TEXTURE_2D, colorTexture);
+ var colorFormat = this._glColorFormat();
+ gl.texImage2D(gl.TEXTURE_2D, 0, colorFormat.internalFormat, this.width * this.density, this.height * this.density, 0, colorFormat.format, colorFormat.type, null);
+ this.colorTexture = colorTexture;
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTexture, 0);
+ if (this.useDepth) {
+ // Create the depth texture
+ var depthTexture = gl.createTexture();
+ if (!depthTexture) {
+ throw new Error('Unable to create depth texture');
+ }
+ var depthFormat = this._glDepthFormat();
+ gl.bindTexture(gl.TEXTURE_2D, depthTexture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, depthFormat.internalFormat, this.width * this.density, this.height * this.density, 0, depthFormat.format, depthFormat.type, null);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, this.useStencil ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTexture, 0);
+ this.depthTexture = depthTexture;
+ } // Create separate framebuffer for antialiasing
+
+ if (this.antialias) {
+ this.colorRenderbuffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, this.colorRenderbuffer);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, Math.max(0, Math.min(this.antialiasSamples, gl.getParameter(gl.MAX_SAMPLES))), colorFormat.internalFormat, this.width * this.density, this.height * this.density);
+ if (this.useDepth) {
+ var _depthFormat = this._glDepthFormat();
+ this.depthRenderbuffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, this.depthRenderbuffer);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, Math.max(0, Math.min(this.antialiasSamples, gl.getParameter(gl.MAX_SAMPLES))), _depthFormat.internalFormat, this.width * this.density, this.height * this.density);
+ }
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.aaFramebuffer);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, this.colorRenderbuffer);
+ if (this.useDepth) {
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, this.useStencil ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthRenderbuffer);
+ }
+ }
+ if (this.useDepth) {
+ this.depth = new FramebufferTexture(this, 'depthTexture');
+ var depthFilter = gl.NEAREST;
+ this.depthP5Texture = new _main.default.Texture(this.target._renderer, this.depth, {
+ minFilter: depthFilter,
+ magFilter: depthFilter
+ });
+ this.target._renderer.textures.set(this.depth, this.depthP5Texture);
+ }
+ this.color = new FramebufferTexture(this, 'colorTexture');
+ var filter = this.textureFiltering === constants.LINEAR ? gl.LINEAR : gl.NEAREST;
+ this.colorP5Texture = new _main.default.Texture(this.target._renderer, this.color, {
+ minFilter: filter,
+ magFilter: filter
+ });
+ this.target._renderer.textures.set(this.color, this.colorP5Texture);
+ gl.bindTexture(gl.TEXTURE_2D, prevBoundTexture);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, prevBoundFramebuffer);
+ } /**
+ * To create a WebGL texture, one needs to supply three pieces of information:
+ * the type (the data type each channel will be stored as, e.g. int or float),
+ * the format (the color channels that will each be stored in the previously
+ * specified type, e.g. rgb or rgba), and the internal format (the specifics
+ * of how data for each channel, in the aforementioned type, will be packed
+ * together, such as how many bits to use, e.g. RGBA32F or RGB565.)
+ *
+ * The format and channels asked for by the user hint at what these values
+ * need to be, and the WebGL version affects what options are avaiable.
+ * This method returns the values for these three properties, given the
+ * framebuffer's settings.
+ *
+ * @private
+ */
+
+ },
+ {
+ key: '_glColorFormat',
+ value: function _glColorFormat() {
+ var type,
+ format,
+ internalFormat;
+ var gl = this.gl;
+ if (this.format === constants.FLOAT) {
+ type = gl.FLOAT;
+ } else if (this.format === constants.HALF_FLOAT) {
+ type = this.target.webglVersion === constants.WEBGL2 ? gl.HALF_FLOAT : gl.getExtension('OES_texture_half_float').HALF_FLOAT_OES;
+ } else {
+ type = gl.UNSIGNED_BYTE;
+ }
+ if (this.channels === constants.RGBA) {
+ format = gl.RGBA;
+ } else {
+ format = gl.RGB;
+ }
+ if (this.target.webglVersion === constants.WEBGL2) {
+ var _gl$UNSIGNED_BYTE,
+ _table;
+ // https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html
+ var table = (_table = {
+ }, _defineProperty(_table, gl.FLOAT, _defineProperty({
+ }, gl.RGBA, gl.RGBA32F)), _defineProperty(_table, gl.HALF_FLOAT, _defineProperty({
+ }, gl.RGBA, gl.RGBA16F)), _defineProperty(_table, gl.UNSIGNED_BYTE, (_gl$UNSIGNED_BYTE = {
+ }, _defineProperty(_gl$UNSIGNED_BYTE, gl.RGBA, gl.RGBA8), _defineProperty(_gl$UNSIGNED_BYTE, gl.RGB, gl.RGB8), _gl$UNSIGNED_BYTE)), _table);
+ internalFormat = table[type][format];
+ } else if (this.format === constants.HALF_FLOAT) {
+ internalFormat = gl.RGBA;
+ } else {
+ internalFormat = format;
+ }
+ return {
+ internalFormat: internalFormat,
+ format: format,
+ type: type
+ };
+ } /**
+ * To create a WebGL texture, one needs to supply three pieces of information:
+ * the type (the data type each channel will be stored as, e.g. int or float),
+ * the format (the color channels that will each be stored in the previously
+ * specified type, e.g. rgb or rgba), and the internal format (the specifics
+ * of how data for each channel, in the aforementioned type, will be packed
+ * together, such as how many bits to use, e.g. RGBA32F or RGB565.)
+ *
+ * This method takes into account the settings asked for by the user and
+ * returns values for these three properties that can be used for the
+ * texture storing depth information.
+ *
+ * @private
+ */
+
+ },
+ {
+ key: '_glDepthFormat',
+ value: function _glDepthFormat() {
+ var type,
+ format,
+ internalFormat;
+ var gl = this.gl;
+ if (this.useStencil) {
+ if (this.depthFormat === constants.FLOAT) {
+ type = gl.FLOAT_32_UNSIGNED_INT_24_8_REV;
+ } else if (this.target.webglVersion === constants.WEBGL2) {
+ type = gl.UNSIGNED_INT_24_8;
+ } else {
+ type = gl.getExtension('WEBGL_depth_texture').UNSIGNED_INT_24_8_WEBGL;
+ }
+ } else {
+ if (this.depthFormat === constants.FLOAT) {
+ type = gl.FLOAT;
+ } else {
+ type = gl.UNSIGNED_INT;
+ }
+ }
+ if (this.useStencil) {
+ format = gl.DEPTH_STENCIL;
+ } else {
+ format = gl.DEPTH_COMPONENT;
+ }
+ if (this.useStencil) {
+ if (this.depthFormat === constants.FLOAT) {
+ internalFormat = gl.DEPTH32F_STENCIL8;
+ } else if (this.target.webglVersion === constants.WEBGL2) {
+ internalFormat = gl.DEPTH24_STENCIL8;
+ } else {
+ internalFormat = gl.DEPTH_STENCIL;
+ }
+ } else if (this.target.webglVersion === constants.WEBGL2) {
+ if (this.depthFormat === constants.FLOAT) {
+ internalFormat = gl.DEPTH_COMPONENT32F;
+ } else {
+ internalFormat = gl.DEPTH_COMPONENT24;
+ }
+ } else {
+ internalFormat = gl.DEPTH_COMPONENT;
+ }
+ return {
+ internalFormat: internalFormat,
+ format: format,
+ type: type
+ };
+ } /**
+ * A method that will be called when recreating textures. If the framebuffer
+ * is auto-sized, it will update its width, height, and density properties.
+ *
+ * @private
+ */
+
+ },
+ {
+ key: '_updateSize',
+ value: function _updateSize() {
+ if (this._autoSized) {
+ this.width = this.target.width;
+ this.height = this.target.height;
+ this.density = this.target.pixelDensity();
+ }
+ } /**
+ * Called when the canvas that the framebuffer is attached to resizes. If the
+ * framebuffer is auto-sized, it will update its textures to match the new
+ * size.
+ *
+ * @private
+ */
+
+ },
+ {
+ key: '_canvasSizeChanged',
+ value: function _canvasSizeChanged() {
+ if (this._autoSized) {
+ this._handleResize();
+ }
+ } /**
+ * Called when the size of the framebuffer has changed (either by being
+ * manually updated or from auto-size updates when its canvas changes size.)
+ * Old textures and renderbuffers will be deleted, and then recreated with the
+ * new size.
+ *
+ * @private
+ */
+
+ },
+ {
+ key: '_handleResize',
+ value: function _handleResize() {
+ var oldColor = this.color;
+ var oldDepth = this.depth;
+ var oldColorRenderbuffer = this.colorRenderbuffer;
+ var oldDepthRenderbuffer = this.depthRenderbuffer;
+ this._deleteTexture(oldColor);
+ if (oldDepth) this._deleteTexture(oldDepth);
+ var gl = this.gl;
+ if (oldColorRenderbuffer) gl.deleteRenderbuffer(oldColorRenderbuffer);
+ if (oldDepthRenderbuffer) gl.deleteRenderbuffer(oldDepthRenderbuffer);
+ this._recreateTextures();
+ this.defaultCamera._resize();
+ } /**
+ * Creates a new
+ * p5.Camera object to use with the framebuffer.
+ *
+ * The new camera is initialized with a default position `(0, 0, 800)` and a
+ * default perspective projection. Its properties can be controlled with
+ * p5.Camera methods such as `myCamera.lookAt(0, 0, 0)`.
+ *
+ * Framebuffer cameras should be created between calls to
+ * myBuffer.begin() and
+ * myBuffer.end() like so:
+ *
+ * ```js
+ * let myCamera;
+ *
+ * myBuffer.begin();
+ *
+ * // Create the camera for the framebuffer.
+ * myCamera = myBuffer.createCamera();
+ *
+ * myBuffer.end();
+ * ```
+ *
+ * Calling setCamera() updates the
+ * framebuffer's projection using the camera.
+ * resetMatrix() must also be called for the
+ * view to change properly:
+ *
+ * ```js
+ * myBuffer.begin();
+ *
+ * // Set the camera for the framebuffer.
+ * setCamera(myCamera);
+ *
+ * // Reset all transformations.
+ * resetMatrix();
+ *
+ * // Draw stuff...
+ *
+ * myBuffer.end();
+ * ```
+ *
+ * @method createCamera
+ * @returns {p5.Camera} new camera.
+ *
+ * @example
+ *
+ *
+ * // Double-click to toggle between cameras.
+ *
+ * let myBuffer;
+ * let cam1;
+ * let cam2;
+ * let usingCam1 = true;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Framebuffer object.
+ * myBuffer = createFramebuffer();
+ *
+ * // Create the cameras between begin() and end().
+ * myBuffer.begin();
+ *
+ * // Create the first camera.
+ * // Keep its default settings.
+ * cam1 = myBuffer.createCamera();
+ *
+ * // Create the second camera.
+ * // Place it at the top-left.
+ * // Point it at the origin.
+ * cam2 = myBuffer.createCamera();
+ * cam2.setPosition(400, -400, 800);
+ * cam2.lookAt(0, 0, 0);
+ *
+ * myBuffer.end();
+ *
+ * describe(
+ * 'A white cube on a gray background. The camera toggles between frontal and aerial views when the user double-clicks.'
+ * );
+ * }
+ *
+ * function draw() {
+ * // Draw to the p5.Framebuffer object.
+ * myBuffer.begin();
+ * background(200);
+ *
+ * // Set the camera.
+ * if (usingCam1 === true) {
+ * setCamera(cam1);
+ * } else {
+ * setCamera(cam2);
+ * }
+ *
+ * // Reset all transformations.
+ * resetMatrix();
+ *
+ * // Draw the box.
+ * box();
+ *
+ * myBuffer.end();
+ *
+ * // Display the p5.Framebuffer object.
+ * image(myBuffer, -50, -50);
+ * }
+ *
+ * // Toggle the current camera when the user double-clicks.
+ * function doubleClicked() {
+ * if (usingCam1 === true) {
+ * usingCam1 = false;
+ * } else {
+ * usingCam1 = true;
+ * }
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'createCamera',
+ value: function createCamera() {
+ var cam = new FramebufferCamera(this);
+ cam._computeCameraDefaultSettings();
+ cam._setDefaultCamera();
+ this.target._renderer._curCamera = cam;
+ return cam;
+ } /**
+ * Given a raw texture wrapper, delete its stored texture from WebGL memory,
+ * and remove it from p5's list of active textures.
+ *
+ * @param {p5.FramebufferTexture} texture
+ * @private
+ */
+
+ },
+ {
+ key: '_deleteTexture',
+ value: function _deleteTexture(texture) {
+ var gl = this.gl;
+ gl.deleteTexture(texture.rawTexture());
+ this.target._renderer.textures.delete(texture);
+ } /**
+ * Deletes the framebuffer from GPU memory.
+ *
+ * Calling `myBuffer.remove()` frees the GPU memory used by the framebuffer.
+ * The framebuffer also uses a bit of memory on the CPU which can be freed
+ * like so:
+ *
+ * ```js
+ * // Delete the framebuffer from GPU memory.
+ * myBuffer.remove();
+ *
+ * // Delete the framebuffer from CPU memory.
+ * myBuffer = undefined;
+ * ```
+ *
+ * Note: All variables that reference the framebuffer must be assigned
+ * the value `undefined` to delete the framebuffer from CPU memory. If any
+ * variable still refers to the framebuffer, then it won't be garbage
+ * collected.
+ *
+ * @method remove
+ *
+ * @example
+ *
+ *
+ * // Double-click to remove the p5.Framebuffer object.
+ *
+ * let myBuffer;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create an options object.
+ * let options = { width: 60, height: 60 };
+ *
+ * // Create a p5.Framebuffer object and
+ * // configure it using options.
+ * myBuffer = createFramebuffer(options);
+ *
+ * describe('A white circle at the center of a dark gray square disappears when the user double-clicks.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Display the p5.Framebuffer object if
+ * // it's available.
+ * if (myBuffer) {
+ * // Draw to the p5.Framebuffer object.
+ * myBuffer.begin();
+ * background(100);
+ * circle(0, 0, 20);
+ * myBuffer.end();
+ *
+ * image(myBuffer, -30, -30);
+ * }
+ * }
+ *
+ * // Remove the p5.Framebuffer object when the
+ * // the user double-clicks.
+ * function doubleClicked() {
+ * // Delete the framebuffer from GPU memory.
+ * myBuffer.remove();
+ *
+ * // Delete the framebuffer from CPU memory.
+ * myBuffer = undefined;
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'remove',
+ value: function remove() {
+ var gl = this.gl;
+ this._deleteTexture(this.color);
+ if (this.depth) this._deleteTexture(this.depth);
+ gl.deleteFramebuffer(this.framebuffer);
+ if (this.aaFramebuffer) {
+ gl.deleteFramebuffer(this.aaFramebuffer);
+ }
+ if (this.depthRenderbuffer) {
+ gl.deleteRenderbuffer(this.depthRenderbuffer);
+ }
+ if (this.colorRenderbuffer) {
+ gl.deleteRenderbuffer(this.colorRenderbuffer);
+ }
+ this.target._renderer.framebuffers.delete(this);
+ } /**
+ * Begins drawing shapes to the framebuffer.
+ *
+ * `myBuffer.begin()` and myBuffer.end()
+ * allow shapes to be drawn to the framebuffer. `myBuffer.begin()` begins
+ * drawing to the framebuffer and
+ * myBuffer.end() stops drawing to the
+ * framebuffer. Changes won't be visible until the framebuffer is displayed
+ * as an image or texture.
+ *
+ * @method begin
+ *
+ * @example
+ *
+ *
+ * let myBuffer;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Framebuffer object.
+ * myBuffer = createFramebuffer();
+ *
+ * describe('An empty gray canvas. The canvas gets darker and a rotating, multicolor torus appears while the user presses and holds the mouse.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Start drawing to the p5.Framebuffer object.
+ * myBuffer.begin();
+ *
+ * background(50);
+ * rotateY(frameCount * 0.01);
+ * normalMaterial();
+ * torus(30);
+ *
+ * // Stop drawing to the p5.Framebuffer object.
+ * myBuffer.end();
+ *
+ * // Display the p5.Framebuffer object while
+ * // the user presses the mouse.
+ * if (mouseIsPressed === true) {
+ * image(myBuffer, -50, -50);
+ * }
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'begin',
+ value: function begin() {
+ this.prevFramebuffer = this.target._renderer.activeFramebuffer();
+ if (this.prevFramebuffer) {
+ this.prevFramebuffer._beforeEnd();
+ }
+ this.target._renderer.activeFramebuffers.push(this);
+ this._beforeBegin();
+ this.target.push();
+ // Apply the framebuffer's camera. This does almost what
+ // RendererGL.reset() does, but this does not try to clear any buffers;
+ // it only sets the camera.
+ this.target.setCamera(this.defaultCamera);
+ this.target._renderer.uMVMatrix.set(this.target._renderer._curCamera.cameraMatrix);
+ this.target._renderer._applyStencilTestIfClipping();
+ } /**
+ * When making a p5.Framebuffer active so that it may be drawn to, this method
+ * returns the underlying WebGL framebuffer that needs to be active to
+ * support this. Antialiased framebuffers first write to a multisampled
+ * renderbuffer, while other framebuffers can write directly to their main
+ * framebuffers.
+ *
+ * @method _framebufferToBind
+ * @private
+ */
+
+ },
+ {
+ key: '_framebufferToBind',
+ value: function _framebufferToBind() {
+ if (this.antialias) {
+ // If antialiasing, draw to an antialiased renderbuffer rather
+ // than directly to the texture. In end() we will copy from the
+ // renderbuffer to the texture.
+ return this.aaFramebuffer;
+ } else {
+ return this.framebuffer;
+ }
+ } /**
+ * Ensures that the framebuffer is ready to be drawn to
+ *
+ * @method _beforeBegin
+ * @private
+ */
+
+ },
+ {
+ key: '_beforeBegin',
+ value: function _beforeBegin() {
+ var gl = this.gl;
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this._framebufferToBind());
+ this.target._renderer.viewport(this.width * this.density, this.height * this.density);
+ } /**
+ * Ensures that the framebuffer is ready to be read by other framebuffers.
+ *
+ * @method _beforeEnd
+ * @private
+ */
+
+ },
+ {
+ key: '_beforeEnd',
+ value: function _beforeEnd() {
+ if (this.antialias) {
+ var gl = this.gl;
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.aaFramebuffer);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.framebuffer);
+ var partsToCopy = [
+ [gl.COLOR_BUFFER_BIT,
+ this.colorP5Texture.glMagFilter]
+ ];
+ if (this.useDepth) {
+ partsToCopy.push([gl.DEPTH_BUFFER_BIT,
+ this.depthP5Texture.glMagFilter]);
+ }
+ for (var _i = 0, _partsToCopy = partsToCopy; _i < _partsToCopy.length; _i++) {
+ var _partsToCopy$_i = _slicedToArray(_partsToCopy[_i], 2),
+ flag = _partsToCopy$_i[0],
+ filter = _partsToCopy$_i[1];
+ gl.blitFramebuffer(0, 0, this.width * this.density, this.height * this.density, 0, 0, this.width * this.density, this.height * this.density, flag, filter);
+ }
+ }
+ } /**
+ * Stops drawing shapes to the framebuffer.
+ *
+ * myBuffer.begin() and `myBuffer.end()`
+ * allow shapes to be drawn to the framebuffer.
+ * myBuffer.begin() begins drawing to
+ * the framebuffer and `myBuffer.end()` stops drawing to the framebuffer.
+ * Changes won't be visible until the framebuffer is displayed as an image
+ * or texture.
+ *
+ * @method end
+ *
+ * @example
+ *
+ *
+ * let myBuffer;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Framebuffer object.
+ * myBuffer = createFramebuffer();
+ *
+ * describe('An empty gray canvas. The canvas gets darker and a rotating, multicolor torus appears while the user presses and holds the mouse.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Start drawing to the p5.Framebuffer object.
+ * myBuffer.begin();
+ *
+ * background(50);
+ * rotateY(frameCount * 0.01);
+ * normalMaterial();
+ * torus(30);
+ *
+ * // Stop drawing to the p5.Framebuffer object.
+ * myBuffer.end();
+ *
+ * // Display the p5.Framebuffer object while
+ * // the user presses the mouse.
+ * if (mouseIsPressed === true) {
+ * image(myBuffer, -50, -50);
+ * }
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'end',
+ value: function end() {
+ var gl = this.gl;
+ this.target.pop();
+ var fbo = this.target._renderer.activeFramebuffers.pop();
+ if (fbo !== this) {
+ throw new Error('It looks like you\'ve called end() while another Framebuffer is active.');
+ }
+ this._beforeEnd();
+ if (this.prevFramebuffer) {
+ this.prevFramebuffer._beforeBegin();
+ } else {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ this.target._renderer.viewport(this.target._renderer._origViewport.width, this.target._renderer._origViewport.height);
+ }
+ this.target._renderer._applyStencilTestIfClipping();
+ } /**
+ * Draws to the framebuffer by calling a function that contains drawing
+ * instructions.
+ *
+ * The parameter, `callback`, is a function with the drawing instructions
+ * for the framebuffer. For example, calling `myBuffer.draw(myFunction)`
+ * will call a function named `myFunction()` to draw to the framebuffer.
+ * Doing so has the same effect as the following:
+ *
+ * ```js
+ * myBuffer.begin();
+ * myFunction();
+ * myBuffer.end();
+ * ```
+ *
+ * @method draw
+ * @param {Function} callback function that draws to the framebuffer.
+ *
+ * @example
+ *
+ *
+ * // Click the canvas to display the framebuffer.
+ *
+ * let myBuffer;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Framebuffer object.
+ * myBuffer = createFramebuffer();
+ *
+ * describe('An empty gray canvas. The canvas gets darker and a rotating, multicolor torus appears while the user presses and holds the mouse.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw to the p5.Framebuffer object.
+ * myBuffer.draw(bagel);
+ *
+ * // Display the p5.Framebuffer object while
+ * // the user presses the mouse.
+ * if (mouseIsPressed === true) {
+ * image(myBuffer, -50, -50);
+ * }
+ * }
+ *
+ * // Draw a rotating, multicolor torus.
+ * function bagel() {
+ * background(50);
+ * rotateY(frameCount * 0.01);
+ * normalMaterial();
+ * torus(30);
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'draw',
+ value: function draw(callback) {
+ this.begin();
+ callback();
+ this.end();
+ } /**
+ * Loads the current value of each pixel in the framebuffer into its
+ * pixels array.
+ *
+ * `myBuffer.loadPixels()` must be called before reading from or writing to
+ * myBuffer.pixels.
+ *
+ * @method loadPixels
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Create a p5.Framebuffer object.
+ * let myBuffer = createFramebuffer();
+ *
+ * // Load the pixels array.
+ * myBuffer.loadPixels();
+ *
+ * // Get the number of pixels in the
+ * // top half of the framebuffer.
+ * let numPixels = myBuffer.pixels.length / 2;
+ *
+ * // Set the framebuffer's top half to pink.
+ * for (let i = 0; i < numPixels; i += 4) {
+ * myBuffer.pixels[i] = 255;
+ * myBuffer.pixels[i + 1] = 102;
+ * myBuffer.pixels[i + 2] = 204;
+ * myBuffer.pixels[i + 3] = 255;
+ * }
+ *
+ * // Update the pixels array.
+ * myBuffer.updatePixels();
+ *
+ * // Draw the p5.Framebuffer object to the canvas.
+ * image(myBuffer, -50, -50);
+ *
+ * describe('A pink rectangle above a gray rectangle.');
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'loadPixels',
+ value: function loadPixels() {
+ var gl = this.gl;
+ var prevFramebuffer = this.target._renderer.activeFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
+ var colorFormat = this._glColorFormat();
+ this.pixels = (0, _p2.readPixelsWebGL) (this.pixels, gl, this.framebuffer, 0, 0, this.width * this.density, this.height * this.density, colorFormat.format, colorFormat.type);
+ if (prevFramebuffer) {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, prevFramebuffer._framebufferToBind());
+ } else {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ }
+ } /**
+ * Gets a pixel or a region of pixels from the framebuffer.
+ *
+ * `myBuffer.get()` is easy to use but it's not as fast as
+ * myBuffer.pixels. Use
+ * myBuffer.pixels to read many pixel
+ * values.
+ *
+ * The version of `myBuffer.get()` with no parameters returns the entire
+ * framebuffer as a a p5.Image object.
+ *
+ * The version of `myBuffer.get()` with two parameters interprets them as
+ * coordinates. It returns an array with the `[R, G, B, A]` values of the
+ * pixel at the given point.
+ *
+ * The version of `myBuffer.get()` with four parameters interprets them as
+ * coordinates and dimensions. It returns a subsection of the framebuffer as
+ * a p5.Image object. The first two parameters are
+ * the coordinates for the upper-left corner of the subsection. The last two
+ * parameters are the width and height of the subsection.
+ *
+ * @method get
+ * @param {Number} x x-coordinate of the pixel. Defaults to 0.
+ * @param {Number} y y-coordinate of the pixel. Defaults to 0.
+ * @param {Number} w width of the subsection to be returned.
+ * @param {Number} h height of the subsection to be returned.
+ * @return {p5.Image} subsection as a p5.Image object.
+ */
+ /**
+ * @method get
+ * @return {p5.Image} entire framebuffer as a p5.Image object.
+ */
+ /**
+ * @method get
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Number[]} color of the pixel at `(x, y)` as an array of color values `[R, G, B, A]`.
+ */
+
+ },
+ {
+ key: 'get',
+ value: function get(x, y, w, h) {
+ _main.default._validateParameters('p5.Framebuffer.get', arguments);
+ var colorFormat = this._glColorFormat();
+ if (x === undefined && y === undefined) {
+ x = 0;
+ y = 0;
+ w = this.width;
+ h = this.height;
+ } else if (w === undefined && h === undefined) {
+ if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
+ console.warn('The x and y values passed to p5.Framebuffer.get are outside of its range and will be clamped.');
+ x = this.target.constrain(x, 0, this.width - 1);
+ y = this.target.constrain(y, 0, this.height - 1);
+ }
+ return (0, _p2.readPixelWebGL) (this.gl, this.framebuffer, x * this.density, y * this.density, colorFormat.format, colorFormat.type);
+ }
+ x = this.target.constrain(x, 0, this.width - 1);
+ y = this.target.constrain(y, 0, this.height - 1);
+ w = this.target.constrain(w, 1, this.width - x);
+ h = this.target.constrain(h, 1, this.height - y);
+ var rawData = (0, _p2.readPixelsWebGL) (undefined, this.gl, this.framebuffer, x * this.density, y * this.density, w * this.density, h * this.density, colorFormat.format, colorFormat.type);
+ // Framebuffer data might be either a Uint8Array or Float32Array
+ // depending on its format, and it may or may not have an alpha channel.
+ // To turn it into an image, we have to normalize the data into a
+ // Uint8ClampedArray with alpha.
+ var fullData = new Uint8ClampedArray(w * h * this.density * this.density * 4);
+ // Default channels that aren't in the framebuffer (e.g. alpha, if the
+ // framebuffer is in RGB mode instead of RGBA) to 255
+ fullData.fill(255);
+ var channels = colorFormat.type === this.gl.RGB ? 3 : 4;
+ for (var _y = 0; _y < h * this.density; _y++) {
+ for (var _x = 0; _x < w * this.density; _x++) {
+ for (var channel = 0; channel < 4; channel++) {
+ var idx = (_y * w * this.density + _x) * 4 + channel;
+ if (channel < channels) {
+ // Find the index of this pixel in `rawData`, which might have a
+ // different number of channels
+ var rawDataIdx = channels === 4 ? idx : (_y * w * this.density + _x) * channels + channel;
+ fullData[idx] = rawData[rawDataIdx];
+ }
+ }
+ }
+ } // Create an image from the data
+
+ var region = new _main.default.Image(w * this.density, h * this.density);
+ region.imageData = region.canvas.getContext('2d').createImageData(region.width, region.height);
+ region.imageData.data.set(fullData);
+ region.pixels = region.imageData.data;
+ region.updatePixels();
+ if (this.density !== 1) {
+ // TODO: support get() at a pixel density > 1
+ region.resize(w, h);
+ }
+ return region;
+ } /**
+ * Updates the framebuffer with the RGBA values in the
+ * pixels array.
+ *
+ * `myBuffer.updatePixels()` only needs to be called after changing values
+ * in the myBuffer.pixels array. Such
+ * changes can be made directly after calling
+ * myBuffer.loadPixels().
+ *
+ * @method updatePixels
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Create a p5.Framebuffer object.
+ * let myBuffer = createFramebuffer();
+ *
+ * // Load the pixels array.
+ * myBuffer.loadPixels();
+ *
+ * // Get the number of pixels in the
+ * // top half of the framebuffer.
+ * let numPixels = myBuffer.pixels.length / 2;
+ *
+ * // Set the framebuffer's top half to pink.
+ * for (let i = 0; i < numPixels; i += 4) {
+ * myBuffer.pixels[i] = 255;
+ * myBuffer.pixels[i + 1] = 102;
+ * myBuffer.pixels[i + 2] = 204;
+ * myBuffer.pixels[i + 3] = 255;
+ * }
+ *
+ * // Update the pixels array.
+ * myBuffer.updatePixels();
+ *
+ * // Draw the p5.Framebuffer object to the canvas.
+ * image(myBuffer, -50, -50);
+ *
+ * describe('A pink rectangle above a gray rectangle.');
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'updatePixels',
+ value: function updatePixels() {
+ var gl = this.gl;
+ this.colorP5Texture.bindTexture();
+ var colorFormat = this._glColorFormat();
+ var channels = colorFormat.format === gl.RGBA ? 4 : 3;
+ var len = this.width * this.height * this.density * this.density * channels;
+ var TypedArrayClass = colorFormat.type === gl.UNSIGNED_BYTE ? Uint8Array : Float32Array;
+ if (!(this.pixels instanceof TypedArrayClass) || this.pixels.length !== len) {
+ throw new Error('The pixels array has not been set correctly. Please call loadPixels() before updatePixels().');
+ }
+ gl.texImage2D(gl.TEXTURE_2D, 0, colorFormat.internalFormat, this.width * this.density, this.height * this.density, 0, colorFormat.format, colorFormat.type, this.pixels);
+ this.colorP5Texture.unbindTexture();
+ var prevFramebuffer = this.target._renderer.activeFramebuffer();
+ if (this.antialias) {
+ // We need to make sure the antialiased framebuffer also has the updated
+ // pixels so that if more is drawn to it, it goes on top of the updated
+ // pixels instead of replacing them.
+ // We can't blit the framebuffer to the multisampled antialias
+ // framebuffer to leave both in the same state, so instead we have
+ // to use image() to put the framebuffer texture onto the antialiased
+ // framebuffer.
+ this.begin();
+ this.target.push();
+ this.target.imageMode(this.target.CENTER);
+ this.target.resetMatrix();
+ this.target.noStroke();
+ this.target.clear();
+ this.target.image(this, 0, 0);
+ this.target.pop();
+ if (this.useDepth) {
+ gl.clearDepth(1);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ }
+ this.end();
+ } else {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
+ if (this.useDepth) {
+ gl.clearDepth(1);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ }
+ if (prevFramebuffer) {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, prevFramebuffer._framebufferToBind());
+ } else {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ }
+ }
+ }
+ }
+ ]);
+ return Framebuffer;
+ }();
+ /**
+ * An object that stores the framebuffer's color data.
+ *
+ * Each framebuffer uses a
+ * WebGLTexture
+ * object internally to store its color data. The `myBuffer.color` property
+ * makes it possible to pass this data directly to other functions. For
+ * example, calling `texture(myBuffer.color)` or
+ * `myShader.setUniform('colorTexture', myBuffer.color)` may be helpful for
+ * advanced use cases.
+ *
+ * Note: By default, a framebuffer's y-coordinates are flipped compared to
+ * images and videos. It's easy to flip a framebuffer's y-coordinates as
+ * needed when applying it as a texture. For example, calling
+ * `plane(myBuffer.width, -myBuffer.height)` will flip the framebuffer.
+ *
+ * @property {p5.FramebufferTexture} color
+ * @for p5.Framebuffer
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Create a p5.Framebuffer object.
+ * let myBuffer = createFramebuffer();
+ *
+ * // Start drawing to the p5.Framebuffer object.
+ * myBuffer.begin();
+ *
+ * triangle(-25, 25, 0, -25, 25, 25);
+ *
+ * // Stop drawing to the p5.Framebuffer object.
+ * myBuffer.end();
+ *
+ * // Use the p5.Framebuffer object's WebGLTexture.
+ * texture(myBuffer.color);
+ *
+ * // Style the plane.
+ * noStroke();
+ *
+ * // Draw the plane.
+ * plane(myBuffer.width, myBuffer.height);
+ *
+ * describe('A white triangle on a gray background.');
+ * }
+ *
+ *
+ */
+ /**
+ * An object that stores the framebuffer's dpeth data.
+ *
+ * Each framebuffer uses a
+ * WebGLTexture
+ * object internally to store its depth data. The `myBuffer.depth` property
+ * makes it possible to pass this data directly to other functions. For
+ * example, calling `texture(myBuffer.depth)` or
+ * `myShader.setUniform('depthTexture', myBuffer.depth)` may be helpful for
+ * advanced use cases.
+ *
+ * Note: By default, a framebuffer's y-coordinates are flipped compared to
+ * images and videos. It's easy to flip a framebuffer's y-coordinates as
+ * needed when applying it as a texture. For example, calling
+ * `plane(myBuffer.width, -myBuffer.height)` will flip the framebuffer.
+ *
+ * @property {p5.FramebufferTexture} depth
+ * @for p5.Framebuffer
+ *
+ * @example
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vec4 viewModelPosition = uModelViewMatrix * vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * viewModelPosition;
+ * vTexCoord = aTexCoord;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision highp float;
+ * varying vec2 vTexCoord;
+ * uniform sampler2D depth;
+ *
+ * void main() {
+ * // Get the pixel's depth value.
+ * float depthVal = texture2D(depth, vTexCoord).r;
+ *
+ * // Set the pixel's color based on its depth.
+ * gl_FragColor = mix(
+ * vec4(0., 0., 0., 1.),
+ * vec4(1., 0., 1., 1.),
+ * depthVal);
+ * }
+ * `;
+ *
+ * let myBuffer;
+ * let myShader;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Framebuffer object.
+ * myBuffer = createFramebuffer();
+ *
+ * // Create a p5.Shader object.
+ * myShader = createShader(vertSrc, fragSrc);
+ *
+ * // Compile and apply the shader.
+ * shader(myShader);
+ *
+ * describe('The shadow of a box rotates slowly against a magenta background.');
+ * }
+ *
+ * function draw() {
+ * // Draw to the p5.Framebuffer object.
+ * myBuffer.begin();
+ * background(255);
+ * rotateX(frameCount * 0.01);
+ * box(20, 20, 80);
+ * myBuffer.end();
+ *
+ * // Set the shader's depth uniform using
+ * // the framebuffer's depth texture.
+ * myShader.setUniform('depth', myBuffer.depth);
+ *
+ * // Style the plane.
+ * noStroke();
+ *
+ * // Draw the plane.
+ * plane(myBuffer.width, myBuffer.height);
+ * }
+ *
+ *
+ */
+ _main.default.Framebuffer = Framebuffer;
+ var _default = Framebuffer;
+ exports.default = _default;
+ },
+ {
+ '../core/constants': 291,
+ '../core/main': 303,
+ './p5.RendererGL': 360,
+ './p5.Texture': 362,
+ 'core-js/modules/es.array.fill': 173,
+ 'core-js/modules/es.array.includes': 180,
+ 'core-js/modules/es.array.iterator': 182,
+ 'core-js/modules/es.object.get-own-property-descriptor': 201,
+ 'core-js/modules/es.object.get-prototype-of': 203,
+ 'core-js/modules/es.object.to-string': 205,
+ 'core-js/modules/es.reflect.construct': 207,
+ 'core-js/modules/es.reflect.get': 208,
+ 'core-js/modules/es.regexp.to-string': 211,
+ 'core-js/modules/es.string.includes': 214,
+ 'core-js/modules/es.string.iterator': 215,
+ 'core-js/modules/es.symbol': 227,
+ 'core-js/modules/es.symbol.description': 225,
+ 'core-js/modules/es.symbol.iterator': 226,
+ 'core-js/modules/es.typed-array.copy-within': 228,
+ 'core-js/modules/es.typed-array.every': 229,
+ 'core-js/modules/es.typed-array.fill': 230,
+ 'core-js/modules/es.typed-array.filter': 231,
+ 'core-js/modules/es.typed-array.find': 233,
+ 'core-js/modules/es.typed-array.find-index': 232,
+ 'core-js/modules/es.typed-array.float32-array': 234,
+ 'core-js/modules/es.typed-array.for-each': 236,
+ 'core-js/modules/es.typed-array.includes': 237,
+ 'core-js/modules/es.typed-array.index-of': 238,
+ 'core-js/modules/es.typed-array.iterator': 241,
+ 'core-js/modules/es.typed-array.join': 242,
+ 'core-js/modules/es.typed-array.last-index-of': 243,
+ 'core-js/modules/es.typed-array.map': 244,
+ 'core-js/modules/es.typed-array.reduce': 246,
+ 'core-js/modules/es.typed-array.reduce-right': 245,
+ 'core-js/modules/es.typed-array.reverse': 247,
+ 'core-js/modules/es.typed-array.set': 248,
+ 'core-js/modules/es.typed-array.slice': 249,
+ 'core-js/modules/es.typed-array.some': 250,
+ 'core-js/modules/es.typed-array.sort': 251,
+ 'core-js/modules/es.typed-array.subarray': 252,
+ 'core-js/modules/es.typed-array.to-locale-string': 253,
+ 'core-js/modules/es.typed-array.to-string': 254,
+ 'core-js/modules/es.typed-array.uint8-array': 257,
+ 'core-js/modules/es.typed-array.uint8-clamped-array': 258,
+ 'core-js/modules/es.weak-map': 259,
+ 'core-js/modules/web.dom-collections.iterator': 261
+ }
+ ],
+ 355: [
+ function (_dereq_, module, exports) {
+ 'use strict';
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.concat');
+ _dereq_('core-js/modules/es.array.flat');
+ _dereq_('core-js/modules/es.array.for-each');
+ _dereq_('core-js/modules/es.array.from');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.array.map');
+ _dereq_('core-js/modules/es.array.slice');
+ _dereq_('core-js/modules/es.array.unscopables.flat');
+ _dereq_('core-js/modules/es.map');
+ _dereq_('core-js/modules/es.number.constructor');
+ _dereq_('core-js/modules/es.object.get-own-property-descriptor');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.regexp.to-string');
+ _dereq_('core-js/modules/es.set');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/es.string.sub');
+ _dereq_('core-js/modules/es.weak-map');
+ _dereq_('core-js/modules/web.dom-collections.for-each');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ function _typeof2(obj) {
+ if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') {
+ _typeof2 = function _typeof2(obj) {
+ return typeof obj;
+ };
+ } else {
+ _typeof2 = function _typeof2(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
+ };
+ }
+ return _typeof2(obj);
+ }
+ function _typeof(obj) {
+ if (typeof Symbol === 'function' && _typeof2(Symbol.iterator) === 'symbol') {
+ _typeof = function _typeof(obj) {
+ return _typeof2(obj);
+ };
+ } else {
+ _typeof = function _typeof(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : _typeof2(obj);
+ };
+ }
+ return _typeof(obj);
+ }
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.concat');
+ _dereq_('core-js/modules/es.array.flat');
+ _dereq_('core-js/modules/es.array.for-each');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.array.map');
+ _dereq_('core-js/modules/es.array.slice');
+ _dereq_('core-js/modules/es.array.unscopables.flat');
+ _dereq_('core-js/modules/es.map');
+ _dereq_('core-js/modules/es.number.constructor');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.set');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/es.string.sub');
+ _dereq_('core-js/modules/web.dom-collections.for-each');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ Object.defineProperty(exports, '__esModule', {
+ value: true
+ });
+ exports.default = void 0;
+ var _main = _interopRequireDefault(_dereq_('../core/main'));
+ var constants = _interopRequireWildcard(_dereq_('../core/constants'));
+ function _getRequireWildcardCache() {
+ if (typeof WeakMap !== 'function') return null;
+ var cache = new WeakMap();
+ _getRequireWildcardCache = function _getRequireWildcardCache() {
+ return cache;
+ };
+ return cache;
+ }
+ function _interopRequireWildcard(obj) {
+ if (obj && obj.__esModule) {
+ return obj;
+ }
+ if (obj === null || _typeof(obj) !== 'object' && typeof obj !== 'function') {
+ return {
+ default:
+ obj
+ };
+ }
+ var cache = _getRequireWildcardCache();
+ if (cache && cache.has(obj)) {
+ return cache.get(obj);
+ }
+ var newObj = {
+ };
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
+ if (desc && (desc.get || desc.set)) {
+ Object.defineProperty(newObj, key, desc);
+ } else {
+ newObj[key] = obj[key];
+ }
+ }
+ }
+ newObj.default = obj;
+ if (cache) {
+ cache.set(obj, newObj);
+ }
+ return newObj;
+ }
+ function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : {
+ default:
+ obj
+ };
+ }
+ function _toConsumableArray(arr) {
+ return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
+ }
+ function _nonIterableSpread() {
+ throw new TypeError('Invalid attempt to spread non-iterable instance');
+ }
+ function _iterableToArray(iter) {
+ if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === '[object Arguments]') return Array.from(iter);
+ }
+ function _arrayWithoutHoles(arr) {
+ if (Array.isArray(arr)) {
+ for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
+ arr2[i] = arr[i];
+ }
+ return arr2;
+ }
+ }
+ function _classCallCheck(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError('Cannot call a class as a function');
+ }
+ }
+ function _defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ('value' in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+ function _createClass(Constructor, protoProps, staticProps) {
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) _defineProperties(Constructor, staticProps);
+ return Constructor;
+ } /**
+ * @module Shape
+ * @submodule 3D Primitives
+ * @for p5
+ * @requires core
+ * @requires p5.Geometry
+ */
+ //some of the functions are adjusted from Three.js(http://threejs.org)
+ /**
+ * A class to describe a 3D shape.
+ *
+ * Each `p5.Geometry` object represents a 3D shape as a set of connected
+ * points called *vertices*. All 3D shapes are made by connecting vertices to
+ * form triangles that are stitched together. Each triangular patch on the
+ * geometry's surface is called a *face*. The geometry stores information
+ * about its vertices and faces for use with effects such as lighting and
+ * texture mapping.
+ *
+ * The first parameter, `detailX`, is optional. If a number is passed, as in
+ * `new p5.Geometry(24)`, it sets the number of triangle subdivisions to use
+ * along the geometry's x-axis. By default, `detailX` is 1.
+ *
+ * The second parameter, `detailY`, is also optional. If a number is passed,
+ * as in `new p5.Geometry(24, 16)`, it sets the number of triangle
+ * subdivisions to use along the geometry's y-axis. By default, `detailX` is
+ * 1.
+ *
+ * The third parameter, `callback`, is also optional. If a function is passed,
+ * as in `new p5.Geometry(24, 16, createShape)`, it will be called once to add
+ * vertices to the new 3D shape.
+ *
+ * @class p5.Geometry
+ * @constructor
+ * @param {Integer} [detailX] number of vertices along the x-axis.
+ * @param {Integer} [detailY] number of vertices along the y-axis.
+ * @param {function} [callback] function to call once the geometry is created.
+ *
+ * @example
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object.
+ * myGeometry = new p5.Geometry();
+ *
+ * // Create p5.Vector objects to position the vertices.
+ * let v0 = createVector(-40, 0, 0);
+ * let v1 = createVector(0, -40, 0);
+ * let v2 = createVector(40, 0, 0);
+ *
+ * // Add the vertices to the p5.Geometry object's vertices array.
+ * myGeometry.vertices.push(v0, v1, v2);
+ *
+ * describe('A white triangle drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the p5.Geometry object.
+ * model(myGeometry);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object using a callback function.
+ * myGeometry = new p5.Geometry(1, 1, createShape);
+ *
+ * describe('A white triangle drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the p5.Geometry object.
+ * model(myGeometry);
+ * }
+ *
+ * function createShape() {
+ * // Create p5.Vector objects to position the vertices.
+ * let v0 = createVector(-40, 0, 0);
+ * let v1 = createVector(0, -40, 0);
+ * let v2 = createVector(40, 0, 0);
+ *
+ * // "this" refers to the p5.Geometry object being created.
+ *
+ * // Add the vertices to the p5.Geometry object's vertices array.
+ * this.vertices.push(v0, v1, v2);
+ *
+ * // Add an array to list which vertices belong to the face.
+ * // Vertices are listed in clockwise "winding" order from
+ * // left to top to right.
+ * this.faces.push([0, 1, 2]);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object using a callback function.
+ * myGeometry = new p5.Geometry(1, 1, createShape);
+ *
+ * describe('A white triangle drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the p5.Geometry object.
+ * model(myGeometry);
+ * }
+ *
+ * function createShape() {
+ * // Create p5.Vector objects to position the vertices.
+ * let v0 = createVector(-40, 0, 0);
+ * let v1 = createVector(0, -40, 0);
+ * let v2 = createVector(40, 0, 0);
+ *
+ * // "this" refers to the p5.Geometry object being created.
+ *
+ * // Add the vertices to the p5.Geometry object's vertices array.
+ * this.vertices.push(v0, v1, v2);
+ *
+ * // Add an array to list which vertices belong to the face.
+ * // Vertices are listed in clockwise "winding" order from
+ * // left to top to right.
+ * this.faces.push([0, 1, 2]);
+ *
+ * // Compute the surface normals to help with lighting.
+ * this.computeNormals();
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * // Adapted from Paul Wheeler's wonderful p5.Geometry tutorial.
+ * // https://www.paulwheeler.us/articles/custom-3d-geometry-in-p5js/
+ * // CC-BY-SA 4.0
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create the p5.Geometry object.
+ * // Set detailX to 48 and detailY to 2.
+ * // >>> try changing them.
+ * myGeometry = new p5.Geometry(48, 2, createShape);
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the p5.Geometry object.
+ * strokeWeight(0.2);
+ *
+ * // Draw the p5.Geometry object.
+ * model(myGeometry);
+ * }
+ *
+ * function createShape() {
+ * // "this" refers to the p5.Geometry object being created.
+ *
+ * // Define the Möbius strip with a few parameters.
+ * let spread = 0.1;
+ * let radius = 30;
+ * let stripWidth = 15;
+ * let xInterval = 4 * PI / this.detailX;
+ * let yOffset = -stripWidth / 2;
+ * let yInterval = stripWidth / this.detailY;
+ *
+ * for (let j = 0; j <= this.detailY; j += 1) {
+ * // Calculate the "vertical" point along the strip.
+ * let v = yOffset + yInterval * j;
+ *
+ * for (let i = 0; i <= this.detailX; i += 1) {
+ * // Calculate the angle of rotation around the strip.
+ * let u = i * xInterval;
+ *
+ * // Calculate the coordinates of the vertex.
+ * let x = (radius + v * cos(u / 2)) * cos(u) - sin(u / 2) * 2 * spread;
+ * let y = (radius + v * cos(u / 2)) * sin(u);
+ * if (u < TWO_PI) {
+ * y += sin(u) * spread;
+ * } else {
+ * y -= sin(u) * spread;
+ * }
+ * let z = v * sin(u / 2) + sin(u / 4) * 4 * spread;
+ *
+ * // Create a p5.Vector object to position the vertex.
+ * let vert = createVector(x, y, z);
+ *
+ * // Add the vertex to the p5.Geometry object's vertices array.
+ * this.vertices.push(vert);
+ * }
+ * }
+ *
+ * // Compute the faces array.
+ * this.computeFaces();
+ *
+ * // Compute the surface normals to help with lighting.
+ * this.computeNormals();
+ * }
+ *
+ *
+ */
+
+ _main.default.Geometry = /*#__PURE__*/ function () {
+ function Geometry(detailX, detailY, callback) {
+ _classCallCheck(this, Geometry);
+ /**
+ * An array with the geometry's vertices.
+ *
+ * The geometry's vertices are stored as
+ * p5.Vector objects in the `myGeometry.vertices`
+ * array. The geometry's first vertex is the
+ * p5.Vector object at `myGeometry.vertices[0]`,
+ * its second vertex is `myGeometry.vertices[1]`, its third vertex is
+ * `myGeometry.vertices[2]`, and so on.
+ *
+ * @property vertices
+ * @name vertices
+ *
+ * @example
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object.
+ * myGeometry = new p5.Geometry();
+ *
+ * // Create p5.Vector objects to position the vertices.
+ * let v0 = createVector(-40, 0, 0);
+ * let v1 = createVector(0, -40, 0);
+ * let v2 = createVector(40, 0, 0);
+ *
+ * // Add the vertices to the p5.Geometry object's vertices array.
+ * myGeometry.vertices.push(v0, v1, v2);
+ *
+ * describe('A white triangle drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Draw the p5.Geometry object.
+ * model(myGeometry);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object.
+ * beginGeometry();
+ * torus(30, 15, 10, 8);
+ * myGeometry = endGeometry();
+ *
+ * describe('A white torus rotates slowly against a dark gray background. Red spheres mark its vertices.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Rotate the coordinate system.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Style the p5.Geometry object.
+ * fill(255);
+ * stroke(0);
+ *
+ * // Display the p5.Geometry object.
+ * model(myGeometry);
+ *
+ * // Style the vertices.
+ * fill(255, 0, 0);
+ * noStroke();
+ *
+ * // Iterate over the vertices array.
+ * for (let v of myGeometry.vertices) {
+ * // Draw a sphere to mark the vertex.
+ * push();
+ * translate(v);
+ * sphere(2.5);
+ * pop();
+ * }
+ * }
+ *
+ *
+ */
+ this.vertices = [
+ ];
+ this.boundingBoxCache = null;
+ //an array containing every vertex for stroke drawing
+ this.lineVertices = new _main.default.DataArray();
+ // The tangents going into or out of a vertex on a line. Along a straight
+ // line segment, both should be equal. At an endpoint, one or the other
+ // will not exist and will be all 0. In joins between line segments, they
+ // may be different, as they will be the tangents on either side of the join.
+ this.lineTangentsIn = new _main.default.DataArray();
+ this.lineTangentsOut = new _main.default.DataArray();
+ // When drawing lines with thickness, entries in this buffer represent which
+ // side of the centerline the vertex will be placed. The sign of the number
+ // will represent the side of the centerline, and the absolute value will be
+ // used as an enum to determine which part of the cap or join each vertex
+ // represents. See the doc comments for _addCap and _addJoin for diagrams.
+ this.lineSides = new _main.default.DataArray();
+ /**
+ * An array with the vectors that are normal to the geometry's vertices.
+ *
+ * A face's orientation is defined by its *normal vector* which points out
+ * of the face and is normal (perpendicular) to the surface. Calling
+ * `myGeometry.computeNormals()` first calculates each face's normal
+ * vector. Then it calculates the normal vector for each vertex by
+ * averaging the normal vectors of the faces surrounding the vertex. The
+ * vertex normals are stored as p5.Vector
+ * objects in the `myGeometry.vertexNormals` array.
+ *
+ * @property vertexNormals
+ * @name vertexNormals
+ *
+ * @example
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object.
+ * beginGeometry();
+ * torus(30, 15, 10, 8);
+ * myGeometry = endGeometry();
+ *
+ * // Compute the vertex normals.
+ * myGeometry.computeNormals();
+ *
+ * describe(
+ * 'A white torus rotates against a dark gray background. Red lines extend outward from its vertices.'
+ * );
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Rotate the coordinate system.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Style the p5.Geometry object.
+ * stroke(0);
+ *
+ * // Display the p5.Geometry object.
+ * model(myGeometry);
+ *
+ * // Style the normal vectors.
+ * stroke(255, 0, 0);
+ *
+ * // Iterate over the vertices and vertexNormals arrays.
+ * for (let i = 0; i < myGeometry.vertices.length; i += 1) {
+ *
+ * // Get the vertex p5.Vector object.
+ * let v = myGeometry.vertices[i];
+ *
+ * // Get the vertex normal p5.Vector object.
+ * let n = myGeometry.vertexNormals[i];
+ *
+ * // Calculate a point along the vertex normal.
+ * let p = p5.Vector.mult(n, 8);
+ *
+ * // Draw the vertex normal as a red line.
+ * push();
+ * translate(v);
+ * line(0, 0, 0, p.x, p.y, p.z);
+ * pop();
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object.
+ * myGeometry = new p5.Geometry();
+ *
+ * // Create p5.Vector objects to position the vertices.
+ * let v0 = createVector(-40, 0, 0);
+ * let v1 = createVector(0, -40, 0);
+ * let v2 = createVector(0, 40, 0);
+ * let v3 = createVector(40, 0, 0);
+ *
+ * // Add the vertices to the p5.Geometry object's vertices array.
+ * myGeometry.vertices.push(v0, v1, v2, v3);
+ *
+ * // Compute the faces array.
+ * myGeometry.computeFaces();
+ *
+ * // Compute the surface normals.
+ * myGeometry.computeNormals();
+ *
+ * describe('A red square drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Add a white point light.
+ * pointLight(255, 255, 255, 0, 0, 10);
+ *
+ * // Style the p5.Geometry object.
+ * noStroke();
+ * fill(255, 0, 0);
+ *
+ * // Display the p5.Geometry object.
+ * model(myGeometry);
+ * }
+ *
+ *
+ */
+ this.vertexNormals = [
+ ];
+ /**
+ * An array that lists which of the geometry's vertices form each of its
+ * faces.
+ *
+ * All 3D shapes are made by connecting sets of points called *vertices*. A
+ * geometry's surface is formed by connecting vertices to form triangles
+ * that are stitched together. Each triangular patch on the geometry's
+ * surface is called a *face*.
+ *
+ * The geometry's vertices are stored as
+ * p5.Vector objects in the
+ * myGeometry.vertices array. The
+ * geometry's first vertex is the p5.Vector
+ * object at `myGeometry.vertices[0]`, its second vertex is
+ * `myGeometry.vertices[1]`, its third vertex is `myGeometry.vertices[2]`,
+ * and so on.
+ *
+ * For example, a geometry made from a rectangle has two faces because a
+ * rectangle is made by joining two triangles. `myGeometry.faces` for a
+ * rectangle would be the two-dimensional array `[[0, 1, 2], [2, 1, 3]]`.
+ * The first face, `myGeometry.faces[0]`, is the array `[0, 1, 2]` because
+ * it's formed by connecting `myGeometry.vertices[0]`,
+ * `myGeometry.vertices[1]`,and `myGeometry.vertices[2]`. The second face,
+ * `myGeometry.faces[1]`, is the array `[2, 1, 3]` because it's formed by
+ * connecting `myGeometry.vertices[2]`, `myGeometry.vertices[1]`,and
+ * `myGeometry.vertices[3]`.
+ *
+ * @property faces
+ * @name faces
+ *
+ * @example
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object.
+ * beginGeometry();
+ * sphere();
+ * myGeometry = endGeometry();
+ *
+ * describe("A sphere drawn on a gray background. The sphere's surface is a grayscale patchwork of triangles.");
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the p5.Geometry object.
+ * noStroke();
+ *
+ * // Set a random seed.
+ * randomSeed(1234);
+ *
+ * // Iterate over the faces array.
+ * for (let face of myGeometry.faces) {
+ *
+ * // Style the face.
+ * let g = random(0, 255);
+ * fill(g);
+ *
+ * // Draw the face.
+ * beginShape();
+ * // Iterate over the vertices that form the face.
+ * for (let f of face) {
+ * // Get the vertex's p5.Vector object.
+ * let v = myGeometry.vertices[f];
+ * vertex(v.x, v.y, v.z);
+ * }
+ * endShape();
+ *
+ * }
+ * }
+ *
+ *
+ */
+ this.faces = [
+ ];
+ /**
+ * An array that lists the texture coordinates for each of the geometry's
+ * vertices.
+ *
+ * In order for texture() to work, the geometry
+ * needs a way to map the points on its surface to the pixels in a
+ * rectangular image that's used as a texture. The geometry's vertex at
+ * coordinates `(x, y, z)` maps to the texture image's pixel at coordinates
+ * `(u, v)`.
+ *
+ * The `myGeometry.uvs` array stores the `(u, v)` coordinates for each
+ * vertex in the order it was added to the geometry. For example, the
+ * first vertex, `myGeometry.vertices[0]`, has its `(u, v)` coordinates
+ * stored at `myGeometry.uvs[0]` and `myGeometry.uvs[1]`.
+ *
+ * @property uvs
+ * @name uvs
+ *
+ * @example
+ *
+ *
+ * let img;
+ *
+ * // Load the image and create a p5.Image object.
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Create p5.Geometry objects.
+ * let geom1 = buildGeometry(createShape);
+ * let geom2 = buildGeometry(createShape);
+ *
+ * // Left (original).
+ * push();
+ * translate(-25, 0, 0);
+ * texture(img);
+ * noStroke();
+ * model(geom1);
+ * pop();
+ *
+ * // Set geom2's texture coordinates.
+ * geom2.uvs = [0.25, 0.25, 0.75, 0.25, 0.25, 0.75, 0.75, 0.75];
+ *
+ * // Right (zoomed in).
+ * push();
+ * translate(25, 0, 0);
+ * texture(img);
+ * noStroke();
+ * model(geom2);
+ * pop();
+ *
+ * describe(
+ * 'Two photos of a ceiling on a gray background. The photo on the right zooms in to the center of the photo.'
+ * );
+ * }
+ *
+ * function createShape() {
+ * plane(40);
+ * }
+ *
+ *
+ */
+ this.uvs = [
+ ];
+ // a 2D array containing edge connectivity pattern for create line vertices
+ //based on faces for most objects;
+ this.edges = [
+ ];
+ this.vertexColors = [
+ ];
+ // One color per vertex representing the stroke color at that vertex
+ this.vertexStrokeColors = [
+ ];
+ // One color per line vertex, generated automatically based on
+ // vertexStrokeColors in _edgesToVertices()
+ this.lineVertexColors = new _main.default.DataArray();
+ this.detailX = detailX !== undefined ? detailX : 1;
+ this.detailY = detailY !== undefined ? detailY : 1;
+ this.dirtyFlags = {
+ };
+ this._hasFillTransparency = undefined;
+ this._hasStrokeTransparency = undefined;
+ if (callback instanceof Function) {
+ callback.call(this);
+ }
+ } /**
+ * Calculates the position and size of the smallest box that contains the geometry.
+ *
+ * A bounding box is the smallest rectangular prism that contains the entire
+ * geometry. It's defined by the box's minimum and maximum coordinates along
+ * each axis, as well as the size (length) and offset (center).
+ *
+ * Calling `myGeometry.calculateBoundingBox()` returns an object with four
+ * properties that describe the bounding box:
+ *
+ * ```js
+ * // Get myGeometry's bounding box.
+ * let bbox = myGeometry.calculateBoundingBox();
+ *
+ * // Print the bounding box to the console.
+ * console.log(bbox);
+ *
+ * // {
+ * // // The minimum coordinate along each axis.
+ * // min: { x: -1, y: -2, z: -3 },
+ * //
+ * // // The maximum coordinate along each axis.
+ * // max: { x: 1, y: 2, z: 3},
+ * //
+ * // // The size (length) along each axis.
+ * // size: { x: 2, y: 4, z: 6},
+ * //
+ * // // The offset (center) along each axis.
+ * // offset: { x: 0, y: 0, z: 0}
+ * // }
+ * ```
+ *
+ * @method calculateBoundingBox
+ * @returns {Object} bounding box of the geometry.
+ *
+ * @example
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let particles;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a new p5.Geometry object with random spheres.
+ * particles = buildGeometry(createParticles);
+ *
+ * describe('Ten white spheres placed randomly against a gray background. A box encloses the spheres.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the particles.
+ * noStroke();
+ * fill(255);
+ *
+ * // Draw the particles.
+ * model(particles);
+ *
+ * // Calculate the bounding box.
+ * let bbox = particles.calculateBoundingBox();
+ *
+ * // Translate to the bounding box's center.
+ * translate(bbox.offset.x, bbox.offset.y, bbox.offset.z);
+ *
+ * // Style the bounding box.
+ * stroke(255);
+ * noFill();
+ *
+ * // Draw the bounding box.
+ * box(bbox.size.x, bbox.size.y, bbox.size.z);
+ * }
+ *
+ * function createParticles() {
+ * for (let i = 0; i < 10; i += 1) {
+ * // Calculate random coordinates.
+ * let x = randomGaussian(0, 15);
+ * let y = randomGaussian(0, 15);
+ * let z = randomGaussian(0, 15);
+ *
+ * push();
+ * // Translate to the particle's coordinates.
+ * translate(x, y, z);
+ * // Draw the particle.
+ * sphere(3);
+ * pop();
+ * }
+ * }
+ *
+ *
+ */
+
+ _createClass(Geometry, [
+ {
+ key: 'calculateBoundingBox',
+ value: function calculateBoundingBox() {
+ if (this.boundingBoxCache) {
+ return this.boundingBoxCache; // Return cached result if available
+ }
+ var minVertex = new _main.default.Vector(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+ var maxVertex = new _main.default.Vector(Number.MIN_VALUE, Number.MIN_VALUE, Number.MIN_VALUE);
+ for (var i = 0; i < this.vertices.length; i++) {
+ var vertex = this.vertices[i];
+ minVertex.x = Math.min(minVertex.x, vertex.x);
+ minVertex.y = Math.min(minVertex.y, vertex.y);
+ minVertex.z = Math.min(minVertex.z, vertex.z);
+ maxVertex.x = Math.max(maxVertex.x, vertex.x);
+ maxVertex.y = Math.max(maxVertex.y, vertex.y);
+ maxVertex.z = Math.max(maxVertex.z, vertex.z);
+ } // Calculate size and offset properties
+
+ var size = new _main.default.Vector(maxVertex.x - minVertex.x, maxVertex.y - minVertex.y, maxVertex.z - minVertex.z);
+ var offset = new _main.default.Vector((minVertex.x + maxVertex.x) / 2, (minVertex.y + maxVertex.y) / 2, (minVertex.z + maxVertex.z) / 2);
+ // Cache the result for future access
+ this.boundingBoxCache = {
+ min: minVertex,
+ max: maxVertex,
+ size: size,
+ offset: offset
+ };
+ return this.boundingBoxCache;
+ }
+ },
+ {
+ key: 'reset',
+ value: function reset() {
+ this._hasFillTransparency = undefined;
+ this._hasStrokeTransparency = undefined;
+ this.lineVertices.clear();
+ this.lineTangentsIn.clear();
+ this.lineTangentsOut.clear();
+ this.lineSides.clear();
+ this.vertices.length = 0;
+ this.edges.length = 0;
+ this.vertexColors.length = 0;
+ this.vertexStrokeColors.length = 0;
+ this.lineVertexColors.clear();
+ this.vertexNormals.length = 0;
+ this.uvs.length = 0;
+ this.dirtyFlags = {
+ };
+ }
+ },
+ {
+ key: 'hasFillTransparency',
+ value: function hasFillTransparency() {
+ if (this._hasFillTransparency === undefined) {
+ this._hasFillTransparency = false;
+ for (var i = 0; i < this.vertexColors.length; i += 4) {
+ if (this.vertexColors[i + 3] < 1) {
+ this._hasFillTransparency = true;
+ break;
+ }
+ }
+ }
+ return this._hasFillTransparency;
+ }
+ },
+ {
+ key: 'hasStrokeTransparency',
+ value: function hasStrokeTransparency() {
+ if (this._hasStrokeTransparency === undefined) {
+ this._hasStrokeTransparency = false;
+ for (var i = 0; i < this.lineVertexColors.length; i += 4) {
+ if (this.lineVertexColors[i + 3] < 1) {
+ this._hasStrokeTransparency = true;
+ break;
+ }
+ }
+ }
+ return this._hasStrokeTransparency;
+ } /**
+ * Removes the geometry’s internal colors.
+ *
+ * `p5.Geometry` objects can be created with "internal colors" assigned to
+ * vertices or the entire shape. When a geometry has internal colors,
+ * fill() has no effect. Calling
+ * `myGeometry.clearColors()` allows the
+ * fill() function to apply color to the geometry.
+ *
+ * @method clearColors
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Create a p5.Geometry object.
+ * // Set its internal color to red.
+ * beginGeometry();
+ * fill(255, 0, 0);
+ * plane(20);
+ * let myGeometry = endGeometry();
+ *
+ * // Style the shape.
+ * noStroke();
+ *
+ * // Draw the p5.Geometry object (center).
+ * model(myGeometry);
+ *
+ * // Translate the origin to the bottom-right.
+ * translate(25, 25, 0);
+ *
+ * // Try to fill the geometry with green.
+ * fill(0, 255, 0);
+ *
+ * // Draw the geometry again (bottom-right).
+ * model(myGeometry);
+ *
+ * // Clear the geometry's colors.
+ * myGeometry.clearColors();
+ *
+ * // Fill the geometry with blue.
+ * fill(0, 0, 255);
+ *
+ * // Translate the origin up.
+ * translate(0, -50, 0);
+ *
+ * // Draw the geometry again (top-right).
+ * model(myGeometry);
+ *
+ * describe(
+ * 'Three squares drawn against a gray background. Red squares are at the center and the bottom-right. A blue square is at the top-right.'
+ * );
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'clearColors',
+ value: function clearColors() {
+ this.vertexColors = [
+ ];
+ return this;
+ } /**
+ * Flips the geometry’s texture u-coordinates.
+ *
+ * In order for texture() to work, the geometry
+ * needs a way to map the points on its surface to the pixels in a rectangular
+ * image that's used as a texture. The geometry's vertex at coordinates
+ * `(x, y, z)` maps to the texture image's pixel at coordinates `(u, v)`.
+ *
+ * The myGeometry.uvs array stores the
+ * `(u, v)` coordinates for each vertex in the order it was added to the
+ * geometry. Calling `myGeometry.flipU()` flips a geometry's u-coordinates
+ * so that the texture appears mirrored horizontally.
+ *
+ * For example, a plane's four vertices are added clockwise starting from the
+ * top-left corner. Here's how calling `myGeometry.flipU()` would change a
+ * plane's texture coordinates:
+ *
+ * ```js
+ * // Print the original texture coordinates.
+ * // Output: [0, 0, 1, 0, 0, 1, 1, 1]
+ * console.log(myGeometry.uvs);
+ *
+ * // Flip the u-coordinates.
+ * myGeometry.flipU();
+ *
+ * // Print the flipped texture coordinates.
+ * // Output: [1, 0, 0, 0, 1, 1, 0, 1]
+ * console.log(myGeometry.uvs);
+ *
+ * // Notice the swaps:
+ * // Top vertices: [0, 0, 1, 0] --> [1, 0, 0, 0]
+ * // Bottom vertices: [0, 1, 1, 1] --> [1, 1, 0, 1]
+ * ```
+ *
+ * @method flipU
+ * @for p5.Geometry
+ *
+ * @example
+ *
+ *
+ * let img;
+ *
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Create p5.Geometry objects.
+ * let geom1 = buildGeometry(createShape);
+ * let geom2 = buildGeometry(createShape);
+ *
+ * // Flip geom2's U texture coordinates.
+ * geom2.flipU();
+ *
+ * // Left (original).
+ * push();
+ * translate(-25, 0, 0);
+ * texture(img);
+ * noStroke();
+ * model(geom1);
+ * pop();
+ *
+ * // Right (flipped).
+ * push();
+ * translate(25, 0, 0);
+ * texture(img);
+ * noStroke();
+ * model(geom2);
+ * pop();
+ *
+ * describe(
+ * 'Two photos of a ceiling on a gray background. The photos are mirror images of each other.'
+ * );
+ * }
+ *
+ * function createShape() {
+ * plane(40);
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'flipU',
+ value: function flipU() {
+ this.uvs = this.uvs.flat().map(function (val, index) {
+ if (index % 2 === 0) {
+ return 1 - val;
+ } else {
+ return val;
+ }
+ });
+ } /**
+ * Flips the geometry’s texture v-coordinates.
+ *
+ * In order for texture() to work, the geometry
+ * needs a way to map the points on its surface to the pixels in a rectangular
+ * image that's used as a texture. The geometry's vertex at coordinates
+ * `(x, y, z)` maps to the texture image's pixel at coordinates `(u, v)`.
+ *
+ * The myGeometry.uvs array stores the
+ * `(u, v)` coordinates for each vertex in the order it was added to the
+ * geometry. Calling `myGeometry.flipV()` flips a geometry's v-coordinates
+ * so that the texture appears mirrored vertically.
+ *
+ * For example, a plane's four vertices are added clockwise starting from the
+ * top-left corner. Here's how calling `myGeometry.flipV()` would change a
+ * plane's texture coordinates:
+ *
+ * ```js
+ * // Print the original texture coordinates.
+ * // Output: [0, 0, 1, 0, 0, 1, 1, 1]
+ * console.log(myGeometry.uvs);
+ *
+ * // Flip the v-coordinates.
+ * myGeometry.flipV();
+ *
+ * // Print the flipped texture coordinates.
+ * // Output: [0, 1, 1, 1, 0, 0, 1, 0]
+ * console.log(myGeometry.uvs);
+ *
+ * // Notice the swaps:
+ * // Left vertices: [0, 0] <--> [1, 0]
+ * // Right vertices: [1, 0] <--> [1, 1]
+ * ```
+ *
+ * @method flipV
+ * @for p5.Geometry
+ *
+ * @example
+ *
+ *
+ * let img;
+ *
+ * function preload() {
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Create p5.Geometry objects.
+ * let geom1 = buildGeometry(createShape);
+ * let geom2 = buildGeometry(createShape);
+ *
+ * // Flip geom2's V texture coordinates.
+ * geom2.flipV();
+ *
+ * // Left (original).
+ * push();
+ * translate(-25, 0, 0);
+ * texture(img);
+ * noStroke();
+ * model(geom1);
+ * pop();
+ *
+ * // Right (flipped).
+ * push();
+ * translate(25, 0, 0);
+ * texture(img);
+ * noStroke();
+ * model(geom2);
+ * pop();
+ *
+ * describe(
+ * 'Two photos of a ceiling on a gray background. The photos are mirror images of each other.'
+ * );
+ * }
+ *
+ * function createShape() {
+ * plane(40);
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'flipV',
+ value: function flipV() {
+ this.uvs = this.uvs.flat().map(function (val, index) {
+ if (index % 2 === 0) {
+ return val;
+ } else {
+ return 1 - val;
+ }
+ });
+ } /**
+ * Computes the geometry's faces using its vertices.
+ *
+ * All 3D shapes are made by connecting sets of points called *vertices*. A
+ * geometry's surface is formed by connecting vertices to form triangles that
+ * are stitched together. Each triangular patch on the geometry's surface is
+ * called a *face*. `myGeometry.computeFaces()` performs the math needed to
+ * define each face based on the distances between vertices.
+ *
+ * The geometry's vertices are stored as p5.Vector
+ * objects in the myGeometry.vertices
+ * array. The geometry's first vertex is the
+ * p5.Vector object at `myGeometry.vertices[0]`,
+ * its second vertex is `myGeometry.vertices[1]`, its third vertex is
+ * `myGeometry.vertices[2]`, and so on.
+ *
+ * Calling `myGeometry.computeFaces()` fills the
+ * myGeometry.faces array with three-element
+ * arrays that list the vertices that form each face. For example, a geometry
+ * made from a rectangle has two faces because a rectangle is made by joining
+ * two triangles. myGeometry.faces for a
+ * rectangle would be the two-dimensional array
+ * `[[0, 1, 2], [2, 1, 3]]`. The first face, `myGeometry.faces[0]`, is the
+ * array `[0, 1, 2]` because it's formed by connecting
+ * `myGeometry.vertices[0]`, `myGeometry.vertices[1]`,and
+ * `myGeometry.vertices[2]`. The second face, `myGeometry.faces[1]`, is the
+ * array `[2, 1, 3]` because it's formed by connecting
+ * `myGeometry.vertices[2]`, `myGeometry.vertices[1]`, and
+ * `myGeometry.vertices[3]`.
+ *
+ * Note: `myGeometry.computeFaces()` only works when geometries have four or more vertices.
+ *
+ * @method computeFaces
+ * @chainable
+ *
+ * @example
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object.
+ * myGeometry = new p5.Geometry();
+ *
+ * // Create p5.Vector objects to position the vertices.
+ * let v0 = createVector(-40, 0, 0);
+ * let v1 = createVector(0, -40, 0);
+ * let v2 = createVector(0, 40, 0);
+ * let v3 = createVector(40, 0, 0);
+ *
+ * // Add the vertices to myGeometry's vertices array.
+ * myGeometry.vertices.push(v0, v1, v2, v3);
+ *
+ * // Compute myGeometry's faces array.
+ * myGeometry.computeFaces();
+ *
+ * describe('A red square drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the shape.
+ * noStroke();
+ * fill(255, 0, 0);
+ *
+ * // Draw the p5.Geometry object.
+ * model(myGeometry);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object using a callback function.
+ * myGeometry = new p5.Geometry(1, 1, createShape);
+ *
+ * describe('A red square drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Style the shape.
+ * noStroke();
+ * fill(255, 0, 0);
+ *
+ * // Draw the p5.Geometry object.
+ * model(myGeometry);
+ * }
+ *
+ * function createShape() {
+ * // Create p5.Vector objects to position the vertices.
+ * let v0 = createVector(-40, 0, 0);
+ * let v1 = createVector(0, -40, 0);
+ * let v2 = createVector(0, 40, 0);
+ * let v3 = createVector(40, 0, 0);
+ *
+ * // Add the vertices to the p5.Geometry object's vertices array.
+ * this.vertices.push(v0, v1, v2, v3);
+ *
+ * // Compute the faces array.
+ * this.computeFaces();
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'computeFaces',
+ value: function computeFaces() {
+ this.faces.length = 0;
+ var sliceCount = this.detailX + 1;
+ var a,
+ b,
+ c,
+ d;
+ for (var i = 0; i < this.detailY; i++) {
+ for (var j = 0; j < this.detailX; j++) {
+ a = i * sliceCount + j; // + offset;
+ b = i * sliceCount + j + 1; // + offset;
+ c = (i + 1) * sliceCount + j + 1; // + offset;
+ d = (i + 1) * sliceCount + j; // + offset;
+ this.faces.push([a,
+ b,
+ d]);
+ this.faces.push([d,
+ b,
+ c]);
+ }
+ }
+ return this;
+ }
+ },
+ {
+ key: '_getFaceNormal',
+ value: function _getFaceNormal(faceId) {
+ //This assumes that vA->vB->vC is a counter-clockwise ordering
+ var face = this.faces[faceId];
+ var vA = this.vertices[face[0]];
+ var vB = this.vertices[face[1]];
+ var vC = this.vertices[face[2]];
+ var ab = _main.default.Vector.sub(vB, vA);
+ var ac = _main.default.Vector.sub(vC, vA);
+ var n = _main.default.Vector.cross(ab, ac);
+ var ln = _main.default.Vector.mag(n);
+ var sinAlpha = ln / (_main.default.Vector.mag(ab) * _main.default.Vector.mag(ac));
+ if (sinAlpha === 0 || isNaN(sinAlpha)) {
+ console.warn('p5.Geometry.prototype._getFaceNormal:', 'face has colinear sides or a repeated vertex');
+ return n;
+ }
+ if (sinAlpha > 1) sinAlpha = 1; // handle float rounding error
+ return n.mult(Math.asin(sinAlpha) / ln);
+ } /**
+ * Calculates the normal vector for each vertex on the geometry.
+ *
+ * All 3D shapes are made by connecting sets of points called *vertices*. A
+ * geometry's surface is formed by connecting vertices to create triangles
+ * that are stitched together. Each triangular patch on the geometry's
+ * surface is called a *face*. `myGeometry.computeNormals()` performs the
+ * math needed to orient each face. Orientation is important for lighting
+ * and other effects.
+ *
+ * A face's orientation is defined by its *normal vector* which points out
+ * of the face and is normal (perpendicular) to the surface. Calling
+ * `myGeometry.computeNormals()` first calculates each face's normal vector.
+ * Then it calculates the normal vector for each vertex by averaging the
+ * normal vectors of the faces surrounding the vertex. The vertex normals
+ * are stored as p5.Vector objects in the
+ * myGeometry.vertexNormals array.
+ *
+ * The first parameter, `shadingType`, is optional. Passing the constant
+ * `FLAT`, as in `myGeometry.computeNormals(FLAT)`, provides neighboring
+ * faces with their own copies of the vertices they share. Surfaces appear
+ * tiled with flat shading. Passing the constant `SMOOTH`, as in
+ * `myGeometry.computeNormals(SMOOTH)`, makes neighboring faces reuse their
+ * shared vertices. Surfaces appear smoother with smooth shading. By
+ * default, `shadingType` is `FLAT`.
+ *
+ * The second parameter, `options`, is also optional. If an object with a
+ * `roundToPrecision` property is passed, as in
+ * `myGeometry.computeNormals(SMOOTH, { roundToPrecision: 5 })`, it sets the
+ * number of decimal places to use for calculations. By default,
+ * `roundToPrecision` uses 3 decimal places.
+ *
+ * @method computeNormals
+ * @param {String} [shadingType] shading type. either FLAT or SMOOTH. Defaults to `FLAT`.
+ * @param {Object} [options] shading options.
+ * @chainable
+ *
+ * @example
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object.
+ * beginGeometry();
+ * torus();
+ * myGeometry = endGeometry();
+ *
+ * // Compute the vertex normals.
+ * myGeometry.computeNormals();
+ *
+ * describe(
+ * "A white torus drawn on a dark gray background. Red lines extend outward from the torus' vertices."
+ * );
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Rotate the coordinate system.
+ * rotateX(1);
+ *
+ * // Style the helix.
+ * stroke(0);
+ *
+ * // Display the helix.
+ * model(myGeometry);
+ *
+ * // Style the normal vectors.
+ * stroke(255, 0, 0);
+ *
+ * // Iterate over the vertices and vertexNormals arrays.
+ * for (let i = 0; i < myGeometry.vertices.length; i += 1) {
+ *
+ * // Get the vertex p5.Vector object.
+ * let v = myGeometry.vertices[i];
+ *
+ * // Get the vertex normal p5.Vector object.
+ * let n = myGeometry.vertexNormals[i];
+ *
+ * // Calculate a point along the vertex normal.
+ * let p = p5.Vector.mult(n, 5);
+ *
+ * // Draw the vertex normal as a red line.
+ * push();
+ * translate(v);
+ * line(0, 0, 0, p.x, p.y, p.z);
+ * pop();
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object using a callback function.
+ * myGeometry = new p5.Geometry();
+ *
+ * // Create p5.Vector objects to position the vertices.
+ * let v0 = createVector(-40, 0, 0);
+ * let v1 = createVector(0, -40, 0);
+ * let v2 = createVector(0, 40, 0);
+ * let v3 = createVector(40, 0, 0);
+ *
+ * // Add the vertices to the p5.Geometry object's vertices array.
+ * myGeometry.vertices.push(v0, v1, v2, v3);
+ *
+ * // Compute the faces array.
+ * myGeometry.computeFaces();
+ *
+ * // Compute the surface normals.
+ * myGeometry.computeNormals();
+ *
+ * describe('A red square drawn on a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Add a white point light.
+ * pointLight(255, 255, 255, 0, 0, 10);
+ *
+ * // Style the p5.Geometry object.
+ * noStroke();
+ * fill(255, 0, 0);
+ *
+ * // Draw the p5.Geometry object.
+ * model(myGeometry);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object.
+ * myGeometry = buildGeometry(createShape);
+ *
+ * // Compute normals using default (FLAT) shading.
+ * myGeometry.computeNormals(FLAT);
+ *
+ * describe('A white, helical structure drawn on a dark gray background. Its faces appear faceted.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Rotate the coordinate system.
+ * rotateX(1);
+ *
+ * // Style the helix.
+ * noStroke();
+ *
+ * // Display the helix.
+ * model(myGeometry);
+ * }
+ *
+ * function createShape() {
+ * // Create a helical shape.
+ * beginShape();
+ * for (let i = 0; i < TWO_PI * 3; i += 0.5) {
+ * let x = 30 * cos(i);
+ * let y = 30 * sin(i);
+ * let z = map(i, 0, TWO_PI * 3, -40, 40);
+ * vertex(x, y, z);
+ * }
+ * endShape();
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object.
+ * myGeometry = buildGeometry(createShape);
+ *
+ * // Compute normals using smooth shading.
+ * myGeometry.computeNormals(SMOOTH);
+ *
+ * describe('A white, helical structure drawn on a dark gray background.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Rotate the coordinate system.
+ * rotateX(1);
+ *
+ * // Style the helix.
+ * noStroke();
+ *
+ * // Display the helix.
+ * model(myGeometry);
+ * }
+ *
+ * function createShape() {
+ * // Create a helical shape.
+ * beginShape();
+ * for (let i = 0; i < TWO_PI * 3; i += 0.5) {
+ * let x = 30 * cos(i);
+ * let y = 30 * sin(i);
+ * let z = map(i, 0, TWO_PI * 3, -40, 40);
+ * vertex(x, y, z);
+ * }
+ * endShape();
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Click and drag the mouse to view the scene from different angles.
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Geometry object.
+ * myGeometry = buildGeometry(createShape);
+ *
+ * // Create an options object.
+ * let options = { roundToPrecision: 5 };
+ *
+ * // Compute normals using smooth shading.
+ * myGeometry.computeNormals(SMOOTH, options);
+ *
+ * describe('A white, helical structure drawn on a dark gray background.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Enable orbiting with the mouse.
+ * orbitControl();
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Rotate the coordinate system.
+ * rotateX(1);
+ *
+ * // Style the helix.
+ * noStroke();
+ *
+ * // Display the helix.
+ * model(myGeometry);
+ * }
+ *
+ * function createShape() {
+ * // Create a helical shape.
+ * beginShape();
+ * for (let i = 0; i < TWO_PI * 3; i += 0.5) {
+ * let x = 30 * cos(i);
+ * let y = 30 * sin(i);
+ * let z = map(i, 0, TWO_PI * 3, -40, 40);
+ * vertex(x, y, z);
+ * }
+ * endShape();
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'computeNormals',
+ value: function computeNormals() {
+ var _this = this;
+ var shadingType = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : constants.FLAT;
+ var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
+ },
+ _ref$roundToPrecision = _ref.roundToPrecision,
+ roundToPrecision = _ref$roundToPrecision === void 0 ? 3 : _ref$roundToPrecision;
+ var vertexNormals = this.vertexNormals;
+ var vertices = this.vertices;
+ var faces = this.faces;
+ var iv;
+ if (shadingType === constants.SMOOTH) {
+ var vertexIndices = {
+ };
+ var uniqueVertices = [
+ ];
+ var power = Math.pow(10, roundToPrecision);
+ var rounded = function rounded(val) {
+ return Math.round(val * power) / power;
+ };
+ var getKey = function getKey(vert) {
+ return ''.concat(rounded(vert.x), ',').concat(rounded(vert.y), ',').concat(rounded(vert.z));
+ };
+ // loop through each vertex and add uniqueVertices
+ for (var i = 0; i < vertices.length; i++) {
+ var vertex = vertices[i];
+ var key = getKey(vertex);
+ if (vertexIndices[key] === undefined) {
+ vertexIndices[key] = uniqueVertices.length;
+ uniqueVertices.push(vertex);
+ }
+ } // update face indices to use the deduplicated vertex indices
+
+ faces.forEach(function (face) {
+ for (var fv = 0; fv < 3; ++fv) {
+ var originalVertexIndex = face[fv];
+ var originalVertex = vertices[originalVertexIndex];
+ var _key = getKey(originalVertex);
+ face[fv] = vertexIndices[_key];
+ }
+ });
+ // update edge indices to use the deduplicated vertex indices
+ this.edges.forEach(function (edge) {
+ for (var ev = 0; ev < 2; ++ev) {
+ var originalVertexIndex = edge[ev];
+ var originalVertex = vertices[originalVertexIndex];
+ var _key2 = getKey(originalVertex);
+ edge[ev] = vertexIndices[_key2];
+ }
+ });
+ // update the deduplicated vertices
+ this.vertices = vertices = uniqueVertices;
+ } // initialize the vertexNormals array with empty vectors
+
+ vertexNormals.length = 0;
+ for (iv = 0; iv < vertices.length; ++iv) {
+ vertexNormals.push(new _main.default.Vector());
+ } // loop through all the faces adding its normal to the normal
+ // of each of its vertices
+
+ faces.forEach(function (face, f) {
+ var faceNormal = _this._getFaceNormal(f);
+ // all three vertices get the normal added
+ for (var fv = 0; fv < 3; ++fv) {
+ var vertexIndex = face[fv];
+ vertexNormals[vertexIndex].add(faceNormal);
+ }
+ });
+ // normalize the normals
+ for (iv = 0; iv < vertices.length; ++iv) {
+ vertexNormals[iv].normalize();
+ }
+ return this;
+ } /**
+ * Averages the vertex normals. Used in curved
+ * surfaces
+ * @private
+ * @chainable
+ */
+
+ },
+ {
+ key: 'averageNormals',
+ value: function averageNormals() {
+ for (var i = 0; i <= this.detailY; i++) {
+ var offset = this.detailX + 1;
+ var temp = _main.default.Vector.add(this.vertexNormals[i * offset], this.vertexNormals[i * offset + this.detailX]);
+ temp = _main.default.Vector.div(temp, 2);
+ this.vertexNormals[i * offset] = temp;
+ this.vertexNormals[i * offset + this.detailX] = temp;
+ }
+ return this;
+ } /**
+ * Averages pole normals. Used in spherical primitives
+ * @private
+ * @chainable
+ */
+
+ },
+ {
+ key: 'averagePoleNormals',
+ value: function averagePoleNormals() {
+ //average the north pole
+ var sum = new _main.default.Vector(0, 0, 0);
+ for (var i = 0; i < this.detailX; i++) {
+ sum.add(this.vertexNormals[i]);
+ }
+ sum = _main.default.Vector.div(sum, this.detailX);
+ for (var _i = 0; _i < this.detailX; _i++) {
+ this.vertexNormals[_i] = sum;
+ } //average the south pole
+
+ sum = new _main.default.Vector(0, 0, 0);
+ for (var _i2 = this.vertices.length - 1; _i2 > this.vertices.length - 1 - this.detailX; _i2--) {
+ sum.add(this.vertexNormals[_i2]);
+ }
+ sum = _main.default.Vector.div(sum, this.detailX);
+ for (var _i3 = this.vertices.length - 1; _i3 > this.vertices.length - 1 - this.detailX; _i3--) {
+ this.vertexNormals[_i3] = sum;
+ }
+ return this;
+ } /**
+ * Create a 2D array for establishing stroke connections
+ * @private
+ * @chainable
+ */
+
+ },
+ {
+ key: '_makeTriangleEdges',
+ value: function _makeTriangleEdges() {
+ this.edges.length = 0;
+ for (var j = 0; j < this.faces.length; j++) {
+ this.edges.push([this.faces[j][0],
+ this.faces[j][1]]);
+ this.edges.push([this.faces[j][1],
+ this.faces[j][2]]);
+ this.edges.push([this.faces[j][2],
+ this.faces[j][0]]);
+ }
+ return this;
+ } /**
+ * Converts each line segment into the vertices and vertex attributes needed
+ * to turn the line into a polygon on screen. This will include:
+ * - Two triangles line segment to create a rectangle
+ * - Two triangles per endpoint to create a stroke cap rectangle. A fragment
+ * shader is responsible for displaying the appropriate cap style within
+ * that rectangle.
+ * - Four triangles per join between adjacent line segments, creating a quad on
+ * either side of the join, perpendicular to the lines. A vertex shader will
+ * discard the quad in the "elbow" of the join, and a fragment shader will
+ * display the appropriate join style within the remaining quad.
+ *
+ * @private
+ * @chainable
+ */
+
+ },
+ {
+ key: '_edgesToVertices',
+ value: function _edgesToVertices() {
+ this.lineVertices.clear();
+ this.lineTangentsIn.clear();
+ this.lineTangentsOut.clear();
+ this.lineSides.clear();
+ var potentialCaps = new Map();
+ var connected = new Set();
+ var lastValidDir;
+ for (var i = 0; i < this.edges.length; i++) {
+ var prevEdge = this.edges[i - 1];
+ var currEdge = this.edges[i];
+ var begin = this.vertices[currEdge[0]];
+ var end = this.vertices[currEdge[1]];
+ var fromColor = this.vertexStrokeColors.length > 0 ? this.vertexStrokeColors.slice(currEdge[0] * 4, (currEdge[0] + 1) * 4) : [
+ 0,
+ 0,
+ 0,
+ 0
+ ];
+ var toColor = this.vertexStrokeColors.length > 0 ? this.vertexStrokeColors.slice(currEdge[1] * 4, (currEdge[1] + 1) * 4) : [
+ 0,
+ 0,
+ 0,
+ 0
+ ];
+ var dir = end.copy().sub(begin).normalize();
+ var dirOK = dir.magSq() > 0;
+ if (dirOK) {
+ this._addSegment(begin, end, fromColor, toColor, dir);
+ }
+ if (i > 0 && prevEdge[1] === currEdge[0]) {
+ if (!connected.has(currEdge[0])) {
+ connected.add(currEdge[0]);
+ potentialCaps.delete(currEdge[0]);
+ // Add a join if this segment shares a vertex with the previous. Skip
+ // actually adding join vertices if either the previous segment or this
+ // one has a length of 0.
+ //
+ // Don't add a join if the tangents point in the same direction, which
+ // would mean the edges line up exactly, and there is no need for a join.
+ if (lastValidDir && dirOK && dir.dot(lastValidDir) < 1 - 1e-8) {
+ this._addJoin(begin, lastValidDir, dir, fromColor);
+ }
+ }
+ } else {
+ // Start a new line
+ if (dirOK && !connected.has(currEdge[0])) {
+ var existingCap = potentialCaps.get(currEdge[0]);
+ if (existingCap) {
+ this._addJoin(begin, existingCap.dir, dir, fromColor);
+ potentialCaps.delete(currEdge[0]);
+ connected.add(currEdge[0]);
+ } else {
+ potentialCaps.set(currEdge[0], {
+ point: begin,
+ dir: dir.copy().mult( - 1),
+ color: fromColor
+ });
+ }
+ }
+ if (lastValidDir && !connected.has(prevEdge[1])) {
+ var _existingCap = potentialCaps.get(prevEdge[1]);
+ if (_existingCap) {
+ this._addJoin(this.vertices[prevEdge[1]], lastValidDir, _existingCap.dir.copy().mult( - 1), fromColor);
+ potentialCaps.delete(prevEdge[1]);
+ connected.add(prevEdge[1]);
+ } else {
+ // Close off the last segment with a cap
+ potentialCaps.set(prevEdge[1], {
+ point: this.vertices[prevEdge[1]],
+ dir: lastValidDir,
+ color: fromColor
+ });
+ }
+ lastValidDir = undefined;
+ }
+ }
+ if (i === this.edges.length - 1 && !connected.has(currEdge[1])) {
+ var _existingCap2 = potentialCaps.get(currEdge[1]);
+ if (_existingCap2) {
+ this._addJoin(end, dir, _existingCap2.dir.copy().mult( - 1), toColor);
+ potentialCaps.delete(currEdge[1]);
+ connected.add(currEdge[1]);
+ } else {
+ potentialCaps.set(currEdge[1], {
+ point: end,
+ dir: dir,
+ color: toColor
+ });
+ }
+ }
+ if (dirOK) {
+ lastValidDir = dir;
+ }
+ }
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+ try {
+ for (var _iterator = potentialCaps.values() [Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var _step$value = _step.value,
+ point = _step$value.point,
+ _dir = _step$value.dir,
+ color = _step$value.color;
+ this._addCap(point, _dir, color);
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ return this;
+ } /**
+ * Adds the vertices and vertex attributes for two triangles making a rectangle
+ * for a straight line segment. A vertex shader is responsible for picking
+ * proper coordinates on the screen given the centerline positions, the tangent,
+ * and the side of the centerline each vertex belongs to. Sides follow the
+ * following scheme:
+ *
+ * -1 -1
+ * o-------------o
+ * | |
+ * o-------------o
+ * 1 1
+ *
+ * @private
+ * @chainable
+ */
+
+ },
+ {
+ key: '_addSegment',
+ value: function _addSegment(begin, end, fromColor, toColor, dir) {
+ var _this$lineVertices,
+ _this$lineVertexColor;
+ var a = begin.array();
+ var b = end.array();
+ var dirArr = dir.array();
+ this.lineSides.push(1, 1, - 1, 1, - 1, - 1);
+ for (var _i4 = 0, _arr = [
+ this.lineTangentsIn,
+ this.lineTangentsOut
+ ]; _i4 < _arr.length; _i4++) {
+ var tangents = _arr[_i4];
+ for (var i = 0; i < 6; i++) {
+ tangents.push.apply(tangents, _toConsumableArray(dirArr));
+ }
+ }(_this$lineVertices = this.lineVertices).push.apply(_this$lineVertices, _toConsumableArray(a).concat(_toConsumableArray(b), _toConsumableArray(a), _toConsumableArray(b), _toConsumableArray(b), _toConsumableArray(a)));
+ (_this$lineVertexColor = this.lineVertexColors).push.apply(_this$lineVertexColor, _toConsumableArray(fromColor).concat(_toConsumableArray(toColor), _toConsumableArray(fromColor), _toConsumableArray(toColor), _toConsumableArray(toColor), _toConsumableArray(fromColor)));
+ return this;
+ } /**
+ * Adds the vertices and vertex attributes for two triangles representing the
+ * stroke cap of a line. A fragment shader is responsible for displaying the
+ * appropriate cap style within the rectangle they make.
+ *
+ * The lineSides buffer will include the following values for the points on
+ * the cap rectangle:
+ *
+ * -1 -2
+ * -----------o---o
+ * | |
+ * -----------o---o
+ * 1 2
+ * @private
+ * @chainable
+ */
+
+ },
+ {
+ key: '_addCap',
+ value: function _addCap(point, tangent, color) {
+ var ptArray = point.array();
+ var tanInArray = tangent.array();
+ var tanOutArray = [
+ 0,
+ 0,
+ 0
+ ];
+ for (var i = 0; i < 6; i++) {
+ var _this$lineVertices2,
+ _this$lineTangentsIn,
+ _this$lineTangentsOut,
+ _this$lineVertexColor2;
+ (_this$lineVertices2 = this.lineVertices).push.apply(_this$lineVertices2, _toConsumableArray(ptArray));
+ (_this$lineTangentsIn = this.lineTangentsIn).push.apply(_this$lineTangentsIn, _toConsumableArray(tanInArray));
+ (_this$lineTangentsOut = this.lineTangentsOut).push.apply(_this$lineTangentsOut, tanOutArray);
+ (_this$lineVertexColor2 = this.lineVertexColors).push.apply(_this$lineVertexColor2, _toConsumableArray(color));
+ }
+ this.lineSides.push( - 1, 2, - 2, 1, 2, - 1);
+ return this;
+ } /**
+ * Adds the vertices and vertex attributes for four triangles representing a
+ * join between two adjacent line segments. This creates a quad on either side
+ * of the shared vertex of the two line segments, with each quad perpendicular
+ * to the lines. A vertex shader will discard all but the quad in the "elbow" of
+ * the join, and a fragment shader will display the appropriate join style
+ * within the remaining quad.
+ *
+ * The lineSides buffer will include the following values for the points on
+ * the join rectangles:
+ *
+ * -1 -2
+ * -------------o----o
+ * | |
+ * 1 o----o----o -3
+ * | | 0 |
+ * --------o----o |
+ * 2| 3 |
+ * | |
+ * | |
+ * @private
+ * @chainable
+ */
+
+ },
+ {
+ key: '_addJoin',
+ value: function _addJoin(point, fromTangent, toTangent, color) {
+ var ptArray = point.array();
+ var tanInArray = fromTangent.array();
+ var tanOutArray = toTangent.array();
+ for (var i = 0; i < 12; i++) {
+ var _this$lineVertices3,
+ _this$lineTangentsIn2,
+ _this$lineTangentsOut2,
+ _this$lineVertexColor3;
+ (_this$lineVertices3 = this.lineVertices).push.apply(_this$lineVertices3, _toConsumableArray(ptArray));
+ (_this$lineTangentsIn2 = this.lineTangentsIn).push.apply(_this$lineTangentsIn2, _toConsumableArray(tanInArray));
+ (_this$lineTangentsOut2 = this.lineTangentsOut).push.apply(_this$lineTangentsOut2, _toConsumableArray(tanOutArray));
+ (_this$lineVertexColor3 = this.lineVertexColors).push.apply(_this$lineVertexColor3, _toConsumableArray(color));
+ }
+ this.lineSides.push( - 1, - 3, - 2, - 1, 0, - 3);
+ this.lineSides.push(3, 1, 2, 3, 0, 1);
+ return this;
+ } /**
+ * Transforms the geometry's vertices to fit snugly within a 100×100×100 box
+ * centered at the origin.
+ *
+ * Calling `myGeometry.normalize()` translates the geometry's vertices so that
+ * they're centered at the origin `(0, 0, 0)`. Then it scales the vertices so
+ * that they fill a 100×100×100 box. As a result, small geometries will grow
+ * and large geometries will shrink.
+ *
+ * Note: `myGeometry.normalize()` only works when called in the
+ * setup() function.
+ *
+ * @method normalize
+ * @chainable
+ *
+ * @example
+ *
+ *
+ * let myGeometry;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a very small torus.
+ * beginGeometry();
+ * torus(1, 0.25);
+ * myGeometry = endGeometry();
+ *
+ * // Normalize the torus so its vertices fill
+ * // the range [-100, 100].
+ * myGeometry.normalize();
+ *
+ * describe('A white torus rotates slowly against a dark gray background.');
+ * }
+ *
+ * function draw() {
+ * background(50);
+ *
+ * // Turn on the lights.
+ * lights();
+ *
+ * // Rotate around the y-axis.
+ * rotateY(frameCount * 0.01);
+ *
+ * // Style the torus.
+ * noStroke();
+ *
+ * // Draw the torus.
+ * model(myGeometry);
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'normalize',
+ value: function normalize() {
+ if (this.vertices.length > 0) {
+ // Find the corners of our bounding box
+ var maxPosition = this.vertices[0].copy();
+ var minPosition = this.vertices[0].copy();
+ for (var i = 0; i < this.vertices.length; i++) {
+ maxPosition.x = Math.max(maxPosition.x, this.vertices[i].x);
+ minPosition.x = Math.min(minPosition.x, this.vertices[i].x);
+ maxPosition.y = Math.max(maxPosition.y, this.vertices[i].y);
+ minPosition.y = Math.min(minPosition.y, this.vertices[i].y);
+ maxPosition.z = Math.max(maxPosition.z, this.vertices[i].z);
+ minPosition.z = Math.min(minPosition.z, this.vertices[i].z);
+ }
+ var center = _main.default.Vector.lerp(maxPosition, minPosition, 0.5);
+ var dist = _main.default.Vector.sub(maxPosition, minPosition);
+ var longestDist = Math.max(Math.max(dist.x, dist.y), dist.z);
+ var scale = 200 / longestDist;
+ for (var _i5 = 0; _i5 < this.vertices.length; _i5++) {
+ this.vertices[_i5].sub(center);
+ this.vertices[_i5].mult(scale);
+ }
+ }
+ return this;
+ }
+ }
+ ]);
+ return Geometry;
+ }();
+ var _default = _main.default.Geometry;
+ exports.default = _default;
+ },
+ {
+ '../core/constants': 291,
+ '../core/main': 303,
+ 'core-js/modules/es.array.concat': 170,
+ 'core-js/modules/es.array.flat': 177,
+ 'core-js/modules/es.array.for-each': 178,
+ 'core-js/modules/es.array.from': 179,
+ 'core-js/modules/es.array.iterator': 182,
+ 'core-js/modules/es.array.map': 185,
+ 'core-js/modules/es.array.slice': 186,
+ 'core-js/modules/es.array.unscopables.flat': 190,
+ 'core-js/modules/es.map': 192,
+ 'core-js/modules/es.number.constructor': 196,
+ 'core-js/modules/es.object.get-own-property-descriptor': 201,
+ 'core-js/modules/es.object.to-string': 205,
+ 'core-js/modules/es.regexp.to-string': 211,
+ 'core-js/modules/es.set': 212,
+ 'core-js/modules/es.string.iterator': 215,
+ 'core-js/modules/es.string.sub': 223,
+ 'core-js/modules/es.symbol': 227,
+ 'core-js/modules/es.symbol.description': 225,
+ 'core-js/modules/es.symbol.iterator': 226,
+ 'core-js/modules/es.weak-map': 259,
+ 'core-js/modules/web.dom-collections.for-each': 260,
+ 'core-js/modules/web.dom-collections.iterator': 261
+ }
+ ],
+ 356: [
+ function (_dereq_, module, exports) {
+ 'use strict';
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.typed-array.float32-array');
+ _dereq_('core-js/modules/es.typed-array.copy-within');
+ _dereq_('core-js/modules/es.typed-array.every');
+ _dereq_('core-js/modules/es.typed-array.fill');
+ _dereq_('core-js/modules/es.typed-array.filter');
+ _dereq_('core-js/modules/es.typed-array.find');
+ _dereq_('core-js/modules/es.typed-array.find-index');
+ _dereq_('core-js/modules/es.typed-array.for-each');
+ _dereq_('core-js/modules/es.typed-array.includes');
+ _dereq_('core-js/modules/es.typed-array.index-of');
+ _dereq_('core-js/modules/es.typed-array.iterator');
+ _dereq_('core-js/modules/es.typed-array.join');
+ _dereq_('core-js/modules/es.typed-array.last-index-of');
+ _dereq_('core-js/modules/es.typed-array.map');
+ _dereq_('core-js/modules/es.typed-array.reduce');
+ _dereq_('core-js/modules/es.typed-array.reduce-right');
+ _dereq_('core-js/modules/es.typed-array.reverse');
+ _dereq_('core-js/modules/es.typed-array.set');
+ _dereq_('core-js/modules/es.typed-array.slice');
+ _dereq_('core-js/modules/es.typed-array.some');
+ _dereq_('core-js/modules/es.typed-array.sort');
+ _dereq_('core-js/modules/es.typed-array.subarray');
+ _dereq_('core-js/modules/es.typed-array.to-locale-string');
+ _dereq_('core-js/modules/es.typed-array.to-string');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.typed-array.float32-array');
+ _dereq_('core-js/modules/es.typed-array.copy-within');
+ _dereq_('core-js/modules/es.typed-array.every');
+ _dereq_('core-js/modules/es.typed-array.fill');
+ _dereq_('core-js/modules/es.typed-array.filter');
+ _dereq_('core-js/modules/es.typed-array.find');
+ _dereq_('core-js/modules/es.typed-array.find-index');
+ _dereq_('core-js/modules/es.typed-array.for-each');
+ _dereq_('core-js/modules/es.typed-array.includes');
+ _dereq_('core-js/modules/es.typed-array.index-of');
+ _dereq_('core-js/modules/es.typed-array.iterator');
+ _dereq_('core-js/modules/es.typed-array.join');
+ _dereq_('core-js/modules/es.typed-array.last-index-of');
+ _dereq_('core-js/modules/es.typed-array.map');
+ _dereq_('core-js/modules/es.typed-array.reduce');
+ _dereq_('core-js/modules/es.typed-array.reduce-right');
+ _dereq_('core-js/modules/es.typed-array.reverse');
+ _dereq_('core-js/modules/es.typed-array.set');
+ _dereq_('core-js/modules/es.typed-array.slice');
+ _dereq_('core-js/modules/es.typed-array.some');
+ _dereq_('core-js/modules/es.typed-array.sort');
+ _dereq_('core-js/modules/es.typed-array.subarray');
+ _dereq_('core-js/modules/es.typed-array.to-locale-string');
+ _dereq_('core-js/modules/es.typed-array.to-string');
+ Object.defineProperty(exports, '__esModule', {
+ value: true
+ });
+ exports.default = void 0;
+ var _main = _interopRequireDefault(_dereq_('../core/main'));
+ function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : {
+ default:
+ obj
+ };
+ }
+ function _classCallCheck(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError('Cannot call a class as a function');
+ }
+ }
+ function _defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ('value' in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+ function _createClass(Constructor, protoProps, staticProps) {
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) _defineProperties(Constructor, staticProps);
+ return Constructor;
+ } /**
+ * @requires constants
+ * @todo see methods below needing further implementation.
+ * future consideration: implement SIMD optimizations
+ * when browser compatibility becomes available
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/
+ * Reference/Global_Objects/SIMD
+ */
+
+ var GLMAT_ARRAY_TYPE = Array;
+ var isMatrixArray = function isMatrixArray(x) {
+ return Array.isArray(x);
+ };
+ if (typeof Float32Array !== 'undefined') {
+ GLMAT_ARRAY_TYPE = Float32Array;
+ isMatrixArray = function isMatrixArray(x) {
+ return Array.isArray(x) || x instanceof Float32Array;
+ };
+ } /**
+ * A class to describe a 4×4 matrix
+ * for model and view matrix manipulation in the p5js webgl renderer.
+ * @class p5.Matrix
+ * @private
+ * @constructor
+ * @param {Array} [mat4] column-major array literal of our 4×4 matrix
+ */
+
+ _main.default.Matrix = /*#__PURE__*/ function () {
+ function _class() {
+ var _ref;
+ _classCallCheck(this, _class);
+ // This is default behavior when object
+ // instantiated using createMatrix()
+ // @todo implement createMatrix() in core/math.js
+ if (arguments.length && (_ref = arguments.length - 1, _ref < 0 || arguments.length <= _ref ? undefined : arguments[_ref]) instanceof _main.default) {
+ var _ref2;
+ this.p5 = (_ref2 = arguments.length - 1, _ref2 < 0 || arguments.length <= _ref2 ? undefined : arguments[_ref2]);
+ }
+ if ((arguments.length <= 0 ? undefined : arguments[0]) === 'mat3') {
+ this.mat3 = Array.isArray(arguments.length <= 1 ? undefined : arguments[1]) ? arguments.length <= 1 ? undefined : arguments[1] : new GLMAT_ARRAY_TYPE([1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1]);
+ } else {
+ this.mat4 = Array.isArray(arguments.length <= 0 ? undefined : arguments[0]) ? arguments.length <= 0 ? undefined : arguments[0] : new GLMAT_ARRAY_TYPE([1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1]);
+ }
+ return this;
+ } /**
+ * Replace the entire contents of a 4x4 matrix.
+ * If providing an array or a p5.Matrix, the values will be copied without
+ * referencing the source object.
+ * Can also provide 16 numbers as individual arguments.
+ *
+ * @method set
+ * @param {p5.Matrix|Float32Array|Number[]} [inMatrix] the input p5.Matrix or
+ * an Array of length 16
+ * @chainable
+ */
+ /**
+ * @method set
+ * @param {Number[]} elements 16 numbers passed by value to avoid
+ * array copying.
+ * @chainable
+ */
+
+ _createClass(_class, [
+ {
+ key: 'set',
+ value: function set(inMatrix) {
+ var refArray = arguments;
+ if (inMatrix instanceof _main.default.Matrix) {
+ refArray = inMatrix.mat4;
+ } else if (isMatrixArray(inMatrix)) {
+ refArray = inMatrix;
+ }
+ if (refArray.length !== 16) {
+ _main.default._friendlyError('Expected 16 values but received '.concat(refArray.length, '.'), 'p5.Matrix.set');
+ return this;
+ }
+ for (var i = 0; i < 16; i++) {
+ this.mat4[i] = refArray[i];
+ }
+ return this;
+ } /**
+ * Gets a copy of the vector, returns a p5.Matrix object.
+ *
+ * @method get
+ * @return {p5.Matrix} the copy of the p5.Matrix object
+ */
+
+ },
+ {
+ key: 'get',
+ value: function get() {
+ return new _main.default.Matrix(this.mat4, this.p5);
+ } /**
+ * return a copy of this matrix.
+ * If this matrix is 4x4, a 4x4 matrix with exactly the same entries will be
+ * generated. The same is true if this matrix is 3x3.
+ *
+ * @method copy
+ * @return {p5.Matrix} the result matrix
+ */
+
+ },
+ {
+ key: 'copy',
+ value: function copy() {
+ if (this.mat3 !== undefined) {
+ var copied3x3 = new _main.default.Matrix('mat3', this.p5);
+ copied3x3.mat3[0] = this.mat3[0];
+ copied3x3.mat3[1] = this.mat3[1];
+ copied3x3.mat3[2] = this.mat3[2];
+ copied3x3.mat3[3] = this.mat3[3];
+ copied3x3.mat3[4] = this.mat3[4];
+ copied3x3.mat3[5] = this.mat3[5];
+ copied3x3.mat3[6] = this.mat3[6];
+ copied3x3.mat3[7] = this.mat3[7];
+ copied3x3.mat3[8] = this.mat3[8];
+ return copied3x3;
+ }
+ var copied = new _main.default.Matrix(this.p5);
+ copied.mat4[0] = this.mat4[0];
+ copied.mat4[1] = this.mat4[1];
+ copied.mat4[2] = this.mat4[2];
+ copied.mat4[3] = this.mat4[3];
+ copied.mat4[4] = this.mat4[4];
+ copied.mat4[5] = this.mat4[5];
+ copied.mat4[6] = this.mat4[6];
+ copied.mat4[7] = this.mat4[7];
+ copied.mat4[8] = this.mat4[8];
+ copied.mat4[9] = this.mat4[9];
+ copied.mat4[10] = this.mat4[10];
+ copied.mat4[11] = this.mat4[11];
+ copied.mat4[12] = this.mat4[12];
+ copied.mat4[13] = this.mat4[13];
+ copied.mat4[14] = this.mat4[14];
+ copied.mat4[15] = this.mat4[15];
+ return copied;
+ } /**
+ * return an identity matrix
+ * @method identity
+ * @return {p5.Matrix} the result matrix
+ */
+
+ },
+ {
+ key: 'transpose',
+ /**
+ * transpose according to a given matrix
+ * @method transpose
+ * @param {p5.Matrix|Float32Array|Number[]} a the matrix to be
+ * based on to transpose
+ * @chainable
+ */
+ value: function transpose(a) {
+ var a01,
+ a02,
+ a03,
+ a12,
+ a13,
+ a23;
+ if (a instanceof _main.default.Matrix) {
+ a01 = a.mat4[1];
+ a02 = a.mat4[2];
+ a03 = a.mat4[3];
+ a12 = a.mat4[6];
+ a13 = a.mat4[7];
+ a23 = a.mat4[11];
+ this.mat4[0] = a.mat4[0];
+ this.mat4[1] = a.mat4[4];
+ this.mat4[2] = a.mat4[8];
+ this.mat4[3] = a.mat4[12];
+ this.mat4[4] = a01;
+ this.mat4[5] = a.mat4[5];
+ this.mat4[6] = a.mat4[9];
+ this.mat4[7] = a.mat4[13];
+ this.mat4[8] = a02;
+ this.mat4[9] = a12;
+ this.mat4[10] = a.mat4[10];
+ this.mat4[11] = a.mat4[14];
+ this.mat4[12] = a03;
+ this.mat4[13] = a13;
+ this.mat4[14] = a23;
+ this.mat4[15] = a.mat4[15];
+ } else if (isMatrixArray(a)) {
+ a01 = a[1];
+ a02 = a[2];
+ a03 = a[3];
+ a12 = a[6];
+ a13 = a[7];
+ a23 = a[11];
+ this.mat4[0] = a[0];
+ this.mat4[1] = a[4];
+ this.mat4[2] = a[8];
+ this.mat4[3] = a[12];
+ this.mat4[4] = a01;
+ this.mat4[5] = a[5];
+ this.mat4[6] = a[9];
+ this.mat4[7] = a[13];
+ this.mat4[8] = a02;
+ this.mat4[9] = a12;
+ this.mat4[10] = a[10];
+ this.mat4[11] = a[14];
+ this.mat4[12] = a03;
+ this.mat4[13] = a13;
+ this.mat4[14] = a23;
+ this.mat4[15] = a[15];
+ }
+ return this;
+ } /**
+ * invert matrix according to a give matrix
+ * @method invert
+ * @param {p5.Matrix|Float32Array|Number[]} a the matrix to be
+ * based on to invert
+ * @chainable
+ */
+
+ },
+ {
+ key: 'invert',
+ value: function invert(a) {
+ var a00,
+ a01,
+ a02,
+ a03,
+ a10,
+ a11,
+ a12,
+ a13;
+ var a20,
+ a21,
+ a22,
+ a23,
+ a30,
+ a31,
+ a32,
+ a33;
+ if (a instanceof _main.default.Matrix) {
+ a00 = a.mat4[0];
+ a01 = a.mat4[1];
+ a02 = a.mat4[2];
+ a03 = a.mat4[3];
+ a10 = a.mat4[4];
+ a11 = a.mat4[5];
+ a12 = a.mat4[6];
+ a13 = a.mat4[7];
+ a20 = a.mat4[8];
+ a21 = a.mat4[9];
+ a22 = a.mat4[10];
+ a23 = a.mat4[11];
+ a30 = a.mat4[12];
+ a31 = a.mat4[13];
+ a32 = a.mat4[14];
+ a33 = a.mat4[15];
+ } else if (isMatrixArray(a)) {
+ a00 = a[0];
+ a01 = a[1];
+ a02 = a[2];
+ a03 = a[3];
+ a10 = a[4];
+ a11 = a[5];
+ a12 = a[6];
+ a13 = a[7];
+ a20 = a[8];
+ a21 = a[9];
+ a22 = a[10];
+ a23 = a[11];
+ a30 = a[12];
+ a31 = a[13];
+ a32 = a[14];
+ a33 = a[15];
+ }
+ var b00 = a00 * a11 - a01 * a10;
+ var b01 = a00 * a12 - a02 * a10;
+ var b02 = a00 * a13 - a03 * a10;
+ var b03 = a01 * a12 - a02 * a11;
+ var b04 = a01 * a13 - a03 * a11;
+ var b05 = a02 * a13 - a03 * a12;
+ var b06 = a20 * a31 - a21 * a30;
+ var b07 = a20 * a32 - a22 * a30;
+ var b08 = a20 * a33 - a23 * a30;
+ var b09 = a21 * a32 - a22 * a31;
+ var b10 = a21 * a33 - a23 * a31;
+ var b11 = a22 * a33 - a23 * a32;
+ // Calculate the determinant
+ var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
+ if (!det) {
+ return null;
+ }
+ det = 1 / det;
+ this.mat4[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
+ this.mat4[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
+ this.mat4[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
+ this.mat4[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
+ this.mat4[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
+ this.mat4[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
+ this.mat4[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
+ this.mat4[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
+ this.mat4[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
+ this.mat4[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
+ this.mat4[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
+ this.mat4[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
+ this.mat4[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
+ this.mat4[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
+ this.mat4[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
+ this.mat4[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
+ return this;
+ } /**
+ * Inverts a 3×3 matrix
+ * @method invert3x3
+ * @chainable
+ */
+
+ },
+ {
+ key: 'invert3x3',
+ value: function invert3x3() {
+ var a00 = this.mat3[0];
+ var a01 = this.mat3[1];
+ var a02 = this.mat3[2];
+ var a10 = this.mat3[3];
+ var a11 = this.mat3[4];
+ var a12 = this.mat3[5];
+ var a20 = this.mat3[6];
+ var a21 = this.mat3[7];
+ var a22 = this.mat3[8];
+ var b01 = a22 * a11 - a12 * a21;
+ var b11 = - a22 * a10 + a12 * a20;
+ var b21 = a21 * a10 - a11 * a20;
+ // Calculate the determinant
+ var det = a00 * b01 + a01 * b11 + a02 * b21;
+ if (!det) {
+ return null;
+ }
+ det = 1 / det;
+ this.mat3[0] = b01 * det;
+ this.mat3[1] = ( - a22 * a01 + a02 * a21) * det;
+ this.mat3[2] = (a12 * a01 - a02 * a11) * det;
+ this.mat3[3] = b11 * det;
+ this.mat3[4] = (a22 * a00 - a02 * a20) * det;
+ this.mat3[5] = ( - a12 * a00 + a02 * a10) * det;
+ this.mat3[6] = b21 * det;
+ this.mat3[7] = ( - a21 * a00 + a01 * a20) * det;
+ this.mat3[8] = (a11 * a00 - a01 * a10) * det;
+ return this;
+ } /**
+ * This function is only for 3x3 matrices.
+ * transposes a 3×3 p5.Matrix by a mat3
+ * If there is an array of arguments, the matrix obtained by transposing
+ * the 3x3 matrix generated based on that array is set.
+ * If no arguments, it transposes itself and returns it.
+ *
+ * @method transpose3x3
+ * @param {Number[]} mat3 1-dimensional array
+ * @chainable
+ */
+
+ },
+ {
+ key: 'transpose3x3',
+ value: function transpose3x3(mat3) {
+ if (mat3 === undefined) {
+ mat3 = this.mat3;
+ }
+ var a01 = mat3[1];
+ var a02 = mat3[2];
+ var a12 = mat3[5];
+ this.mat3[0] = mat3[0];
+ this.mat3[1] = mat3[3];
+ this.mat3[2] = mat3[6];
+ this.mat3[3] = a01;
+ this.mat3[4] = mat3[4];
+ this.mat3[5] = mat3[7];
+ this.mat3[6] = a02;
+ this.mat3[7] = a12;
+ this.mat3[8] = mat3[8];
+ return this;
+ } /**
+ * converts a 4×4 matrix to its 3×3 inverse transform
+ * commonly used in MVMatrix to NMatrix conversions.
+ * @method invertTranspose
+ * @param {p5.Matrix} mat4 the matrix to be based on to invert
+ * @chainable
+ * @todo finish implementation
+ */
+
+ },
+ {
+ key: 'inverseTranspose',
+ value: function inverseTranspose(_ref3) {
+ var mat4 = _ref3.mat4;
+ if (this.mat3 === undefined) {
+ _main.default._friendlyError('sorry, this function only works with mat3');
+ } else {
+ //convert mat4 -> mat3
+ this.mat3[0] = mat4[0];
+ this.mat3[1] = mat4[1];
+ this.mat3[2] = mat4[2];
+ this.mat3[3] = mat4[4];
+ this.mat3[4] = mat4[5];
+ this.mat3[5] = mat4[6];
+ this.mat3[6] = mat4[8];
+ this.mat3[7] = mat4[9];
+ this.mat3[8] = mat4[10];
+ }
+ var inverse = this.invert3x3();
+ // check inverse succeeded
+ if (inverse) {
+ inverse.transpose3x3(this.mat3);
+ } else {
+ // in case of singularity, just zero the matrix
+ for (var i = 0; i < 9; i++) {
+ this.mat3[i] = 0;
+ }
+ }
+ return this;
+ } /**
+ * inspired by Toji's mat4 determinant
+ * @method determinant
+ * @return {Number} Determinant of our 4×4 matrix
+ */
+
+ },
+ {
+ key: 'determinant',
+ value: function determinant() {
+ var d00 = this.mat4[0] * this.mat4[5] - this.mat4[1] * this.mat4[4],
+ d01 = this.mat4[0] * this.mat4[6] - this.mat4[2] * this.mat4[4],
+ d02 = this.mat4[0] * this.mat4[7] - this.mat4[3] * this.mat4[4],
+ d03 = this.mat4[1] * this.mat4[6] - this.mat4[2] * this.mat4[5],
+ d04 = this.mat4[1] * this.mat4[7] - this.mat4[3] * this.mat4[5],
+ d05 = this.mat4[2] * this.mat4[7] - this.mat4[3] * this.mat4[6],
+ d06 = this.mat4[8] * this.mat4[13] - this.mat4[9] * this.mat4[12],
+ d07 = this.mat4[8] * this.mat4[14] - this.mat4[10] * this.mat4[12],
+ d08 = this.mat4[8] * this.mat4[15] - this.mat4[11] * this.mat4[12],
+ d09 = this.mat4[9] * this.mat4[14] - this.mat4[10] * this.mat4[13],
+ d10 = this.mat4[9] * this.mat4[15] - this.mat4[11] * this.mat4[13],
+ d11 = this.mat4[10] * this.mat4[15] - this.mat4[11] * this.mat4[14];
+ // Calculate the determinant
+ return d00 * d11 - d01 * d10 + d02 * d09 + d03 * d08 - d04 * d07 + d05 * d06;
+ } /**
+ * multiply two mat4s
+ * @method mult
+ * @param {p5.Matrix|Float32Array|Number[]} multMatrix The matrix
+ * we want to multiply by
+ * @chainable
+ */
+
+ },
+ {
+ key: 'mult',
+ value: function mult(multMatrix) {
+ var _src;
+ if (multMatrix === this || multMatrix === this.mat4) {
+ _src = this.copy().mat4; // only need to allocate in this rare case
+ } else if (multMatrix instanceof _main.default.Matrix) {
+ _src = multMatrix.mat4;
+ } else if (isMatrixArray(multMatrix)) {
+ _src = multMatrix;
+ } else if (arguments.length === 16) {
+ _src = arguments;
+ } else {
+ return; // nothing to do.
+ } // each row is used for the multiplier
+
+ var b0 = this.mat4[0],
+ b1 = this.mat4[1],
+ b2 = this.mat4[2],
+ b3 = this.mat4[3];
+ this.mat4[0] = b0 * _src[0] + b1 * _src[4] + b2 * _src[8] + b3 * _src[12];
+ this.mat4[1] = b0 * _src[1] + b1 * _src[5] + b2 * _src[9] + b3 * _src[13];
+ this.mat4[2] = b0 * _src[2] + b1 * _src[6] + b2 * _src[10] + b3 * _src[14];
+ this.mat4[3] = b0 * _src[3] + b1 * _src[7] + b2 * _src[11] + b3 * _src[15];
+ b0 = this.mat4[4];
+ b1 = this.mat4[5];
+ b2 = this.mat4[6];
+ b3 = this.mat4[7];
+ this.mat4[4] = b0 * _src[0] + b1 * _src[4] + b2 * _src[8] + b3 * _src[12];
+ this.mat4[5] = b0 * _src[1] + b1 * _src[5] + b2 * _src[9] + b3 * _src[13];
+ this.mat4[6] = b0 * _src[2] + b1 * _src[6] + b2 * _src[10] + b3 * _src[14];
+ this.mat4[7] = b0 * _src[3] + b1 * _src[7] + b2 * _src[11] + b3 * _src[15];
+ b0 = this.mat4[8];
+ b1 = this.mat4[9];
+ b2 = this.mat4[10];
+ b3 = this.mat4[11];
+ this.mat4[8] = b0 * _src[0] + b1 * _src[4] + b2 * _src[8] + b3 * _src[12];
+ this.mat4[9] = b0 * _src[1] + b1 * _src[5] + b2 * _src[9] + b3 * _src[13];
+ this.mat4[10] = b0 * _src[2] + b1 * _src[6] + b2 * _src[10] + b3 * _src[14];
+ this.mat4[11] = b0 * _src[3] + b1 * _src[7] + b2 * _src[11] + b3 * _src[15];
+ b0 = this.mat4[12];
+ b1 = this.mat4[13];
+ b2 = this.mat4[14];
+ b3 = this.mat4[15];
+ this.mat4[12] = b0 * _src[0] + b1 * _src[4] + b2 * _src[8] + b3 * _src[12];
+ this.mat4[13] = b0 * _src[1] + b1 * _src[5] + b2 * _src[9] + b3 * _src[13];
+ this.mat4[14] = b0 * _src[2] + b1 * _src[6] + b2 * _src[10] + b3 * _src[14];
+ this.mat4[15] = b0 * _src[3] + b1 * _src[7] + b2 * _src[11] + b3 * _src[15];
+ return this;
+ }
+ },
+ {
+ key: 'apply',
+ value: function apply(multMatrix) {
+ var _src;
+ if (multMatrix === this || multMatrix === this.mat4) {
+ _src = this.copy().mat4; // only need to allocate in this rare case
+ } else if (multMatrix instanceof _main.default.Matrix) {
+ _src = multMatrix.mat4;
+ } else if (isMatrixArray(multMatrix)) {
+ _src = multMatrix;
+ } else if (arguments.length === 16) {
+ _src = arguments;
+ } else {
+ return; // nothing to do.
+ }
+ var mat4 = this.mat4;
+ // each row is used for the multiplier
+ var m0 = mat4[0];
+ var m4 = mat4[4];
+ var m8 = mat4[8];
+ var m12 = mat4[12];
+ mat4[0] = _src[0] * m0 + _src[1] * m4 + _src[2] * m8 + _src[3] * m12;
+ mat4[4] = _src[4] * m0 + _src[5] * m4 + _src[6] * m8 + _src[7] * m12;
+ mat4[8] = _src[8] * m0 + _src[9] * m4 + _src[10] * m8 + _src[11] * m12;
+ mat4[12] = _src[12] * m0 + _src[13] * m4 + _src[14] * m8 + _src[15] * m12;
+ var m1 = mat4[1];
+ var m5 = mat4[5];
+ var m9 = mat4[9];
+ var m13 = mat4[13];
+ mat4[1] = _src[0] * m1 + _src[1] * m5 + _src[2] * m9 + _src[3] * m13;
+ mat4[5] = _src[4] * m1 + _src[5] * m5 + _src[6] * m9 + _src[7] * m13;
+ mat4[9] = _src[8] * m1 + _src[9] * m5 + _src[10] * m9 + _src[11] * m13;
+ mat4[13] = _src[12] * m1 + _src[13] * m5 + _src[14] * m9 + _src[15] * m13;
+ var m2 = mat4[2];
+ var m6 = mat4[6];
+ var m10 = mat4[10];
+ var m14 = mat4[14];
+ mat4[2] = _src[0] * m2 + _src[1] * m6 + _src[2] * m10 + _src[3] * m14;
+ mat4[6] = _src[4] * m2 + _src[5] * m6 + _src[6] * m10 + _src[7] * m14;
+ mat4[10] = _src[8] * m2 + _src[9] * m6 + _src[10] * m10 + _src[11] * m14;
+ mat4[14] = _src[12] * m2 + _src[13] * m6 + _src[14] * m10 + _src[15] * m14;
+ var m3 = mat4[3];
+ var m7 = mat4[7];
+ var m11 = mat4[11];
+ var m15 = mat4[15];
+ mat4[3] = _src[0] * m3 + _src[1] * m7 + _src[2] * m11 + _src[3] * m15;
+ mat4[7] = _src[4] * m3 + _src[5] * m7 + _src[6] * m11 + _src[7] * m15;
+ mat4[11] = _src[8] * m3 + _src[9] * m7 + _src[10] * m11 + _src[11] * m15;
+ mat4[15] = _src[12] * m3 + _src[13] * m7 + _src[14] * m11 + _src[15] * m15;
+ return this;
+ } /**
+ * scales a p5.Matrix by scalars or a vector
+ * @method scale
+ * @param {p5.Vector|Float32Array|Number[]} s vector to scale by
+ * @chainable
+ */
+
+ },
+ {
+ key: 'scale',
+ value: function scale(x, y, z) {
+ if (x instanceof _main.default.Vector) {
+ // x is a vector, extract the components from it.
+ y = x.y;
+ z = x.z;
+ x = x.x; // must be last
+ } else if (x instanceof Array) {
+ // x is an array, extract the components from it.
+ y = x[1];
+ z = x[2];
+ x = x[0]; // must be last
+ }
+ this.mat4[0] *= x;
+ this.mat4[1] *= x;
+ this.mat4[2] *= x;
+ this.mat4[3] *= x;
+ this.mat4[4] *= y;
+ this.mat4[5] *= y;
+ this.mat4[6] *= y;
+ this.mat4[7] *= y;
+ this.mat4[8] *= z;
+ this.mat4[9] *= z;
+ this.mat4[10] *= z;
+ this.mat4[11] *= z;
+ return this;
+ } /**
+ * rotate our Matrix around an axis by the given angle.
+ * @method rotate
+ * @param {Number} a The angle of rotation in radians
+ * @param {p5.Vector|Number[]} axis the axis(es) to rotate around
+ * @chainable
+ * inspired by Toji's gl-matrix lib, mat4 rotation
+ */
+
+ },
+ {
+ key: 'rotate',
+ value: function rotate(a, x, y, z) {
+ if (x instanceof _main.default.Vector) {
+ // x is a vector, extract the components from it.
+ y = x.y;
+ z = x.z;
+ x = x.x; //must be last
+ } else if (x instanceof Array) {
+ // x is an array, extract the components from it.
+ y = x[1];
+ z = x[2];
+ x = x[0]; //must be last
+ }
+ var len = Math.sqrt(x * x + y * y + z * z);
+ x *= 1 / len;
+ y *= 1 / len;
+ z *= 1 / len;
+ var a00 = this.mat4[0];
+ var a01 = this.mat4[1];
+ var a02 = this.mat4[2];
+ var a03 = this.mat4[3];
+ var a10 = this.mat4[4];
+ var a11 = this.mat4[5];
+ var a12 = this.mat4[6];
+ var a13 = this.mat4[7];
+ var a20 = this.mat4[8];
+ var a21 = this.mat4[9];
+ var a22 = this.mat4[10];
+ var a23 = this.mat4[11];
+ //sin,cos, and tan of respective angle
+ var sA = Math.sin(a);
+ var cA = Math.cos(a);
+ var tA = 1 - cA;
+ // Construct the elements of the rotation matrix
+ var b00 = x * x * tA + cA;
+ var b01 = y * x * tA + z * sA;
+ var b02 = z * x * tA - y * sA;
+ var b10 = x * y * tA - z * sA;
+ var b11 = y * y * tA + cA;
+ var b12 = z * y * tA + x * sA;
+ var b20 = x * z * tA + y * sA;
+ var b21 = y * z * tA - x * sA;
+ var b22 = z * z * tA + cA;
+ // rotation-specific matrix multiplication
+ this.mat4[0] = a00 * b00 + a10 * b01 + a20 * b02;
+ this.mat4[1] = a01 * b00 + a11 * b01 + a21 * b02;
+ this.mat4[2] = a02 * b00 + a12 * b01 + a22 * b02;
+ this.mat4[3] = a03 * b00 + a13 * b01 + a23 * b02;
+ this.mat4[4] = a00 * b10 + a10 * b11 + a20 * b12;
+ this.mat4[5] = a01 * b10 + a11 * b11 + a21 * b12;
+ this.mat4[6] = a02 * b10 + a12 * b11 + a22 * b12;
+ this.mat4[7] = a03 * b10 + a13 * b11 + a23 * b12;
+ this.mat4[8] = a00 * b20 + a10 * b21 + a20 * b22;
+ this.mat4[9] = a01 * b20 + a11 * b21 + a21 * b22;
+ this.mat4[10] = a02 * b20 + a12 * b21 + a22 * b22;
+ this.mat4[11] = a03 * b20 + a13 * b21 + a23 * b22;
+ return this;
+ } /**
+ * @todo finish implementing this method!
+ * translates
+ * @method translate
+ * @param {Number[]} v vector to translate by
+ * @chainable
+ */
+
+ },
+ {
+ key: 'translate',
+ value: function translate(v) {
+ var x = v[0],
+ y = v[1],
+ z = v[2] || 0;
+ this.mat4[12] += this.mat4[0] * x + this.mat4[4] * y + this.mat4[8] * z;
+ this.mat4[13] += this.mat4[1] * x + this.mat4[5] * y + this.mat4[9] * z;
+ this.mat4[14] += this.mat4[2] * x + this.mat4[6] * y + this.mat4[10] * z;
+ this.mat4[15] += this.mat4[3] * x + this.mat4[7] * y + this.mat4[11] * z;
+ }
+ },
+ {
+ key: 'rotateX',
+ value: function rotateX(a) {
+ this.rotate(a, 1, 0, 0);
+ }
+ },
+ {
+ key: 'rotateY',
+ value: function rotateY(a) {
+ this.rotate(a, 0, 1, 0);
+ }
+ },
+ {
+ key: 'rotateZ',
+ value: function rotateZ(a) {
+ this.rotate(a, 0, 0, 1);
+ } /**
+ * sets the perspective matrix
+ * @method perspective
+ * @param {Number} fovy [description]
+ * @param {Number} aspect [description]
+ * @param {Number} near near clipping plane
+ * @param {Number} far far clipping plane
+ * @chainable
+ */
+
+ },
+ {
+ key: 'perspective',
+ value: function perspective(fovy, aspect, near, far) {
+ var f = 1 / Math.tan(fovy / 2),
+ nf = 1 / (near - far);
+ this.mat4[0] = f / aspect;
+ this.mat4[1] = 0;
+ this.mat4[2] = 0;
+ this.mat4[3] = 0;
+ this.mat4[4] = 0;
+ this.mat4[5] = f;
+ this.mat4[6] = 0;
+ this.mat4[7] = 0;
+ this.mat4[8] = 0;
+ this.mat4[9] = 0;
+ this.mat4[10] = (far + near) * nf;
+ this.mat4[11] = - 1;
+ this.mat4[12] = 0;
+ this.mat4[13] = 0;
+ this.mat4[14] = 2 * far * near * nf;
+ this.mat4[15] = 0;
+ return this;
+ } /**
+ * sets the ortho matrix
+ * @method ortho
+ * @param {Number} left [description]
+ * @param {Number} right [description]
+ * @param {Number} bottom [description]
+ * @param {Number} top [description]
+ * @param {Number} near near clipping plane
+ * @param {Number} far far clipping plane
+ * @chainable
+ */
+
+ },
+ {
+ key: 'ortho',
+ value: function ortho(left, right, bottom, top, near, far) {
+ var lr = 1 / (left - right),
+ bt = 1 / (bottom - top),
+ nf = 1 / (near - far);
+ this.mat4[0] = - 2 * lr;
+ this.mat4[1] = 0;
+ this.mat4[2] = 0;
+ this.mat4[3] = 0;
+ this.mat4[4] = 0;
+ this.mat4[5] = - 2 * bt;
+ this.mat4[6] = 0;
+ this.mat4[7] = 0;
+ this.mat4[8] = 0;
+ this.mat4[9] = 0;
+ this.mat4[10] = 2 * nf;
+ this.mat4[11] = 0;
+ this.mat4[12] = (left + right) * lr;
+ this.mat4[13] = (top + bottom) * bt;
+ this.mat4[14] = (far + near) * nf;
+ this.mat4[15] = 1;
+ return this;
+ } /**
+ * apply a matrix to a vector with x,y,z,w components
+ * get the results in the form of an array
+ * @method multiplyVec4
+ * @param {Number}
+ * @return {Number[]}
+ */
+
+ },
+ {
+ key: 'multiplyVec4',
+ value: function multiplyVec4(x, y, z, w) {
+ var result = new Array(4);
+ var m = this.mat4;
+ result[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
+ result[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
+ result[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
+ result[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
+ return result;
+ } /**
+ * Applies a matrix to a vector.
+ * The fourth component is set to 1.
+ * Returns a vector consisting of the first
+ * through third components of the result.
+ *
+ * @method multiplyPoint
+ * @param {p5.Vector}
+ * @return {p5.Vector}
+ */
+
+ },
+ {
+ key: 'multiplyPoint',
+ value: function multiplyPoint(_ref4) {
+ var x = _ref4.x,
+ y = _ref4.y,
+ z = _ref4.z;
+ var array = this.multiplyVec4(x, y, z, 1);
+ return new _main.default.Vector(array[0], array[1], array[2]);
+ } /**
+ * Applies a matrix to a vector.
+ * The fourth component is set to 1.
+ * Returns the result of dividing the 1st to 3rd components
+ * of the result by the 4th component as a vector.
+ *
+ * @method multiplyAndNormalizePoint
+ * @param {p5.Vector}
+ * @return {p5.Vector}
+ */
+
+ },
+ {
+ key: 'multiplyAndNormalizePoint',
+ value: function multiplyAndNormalizePoint(_ref5) {
+ var x = _ref5.x,
+ y = _ref5.y,
+ z = _ref5.z;
+ var array = this.multiplyVec4(x, y, z, 1);
+ array[0] /= array[3];
+ array[1] /= array[3];
+ array[2] /= array[3];
+ return new _main.default.Vector(array[0], array[1], array[2]);
+ } /**
+ * Applies a matrix to a vector.
+ * The fourth component is set to 0.
+ * Returns a vector consisting of the first
+ * through third components of the result.
+ *
+ * @method multiplyDirection
+ * @param {p5.Vector}
+ * @return {p5.Vector}
+ */
+
+ },
+ {
+ key: 'multiplyDirection',
+ value: function multiplyDirection(_ref6) {
+ var x = _ref6.x,
+ y = _ref6.y,
+ z = _ref6.z;
+ var array = this.multiplyVec4(x, y, z, 0);
+ return new _main.default.Vector(array[0], array[1], array[2]);
+ } /**
+ * This function is only for 3x3 matrices.
+ * multiply two mat3s. It is an operation to multiply the 3x3 matrix of
+ * the argument from the right. Arguments can be a 3x3 p5.Matrix,
+ * a Float32Array of length 9, or a javascript array of length 9.
+ * In addition, it can also be done by enumerating 9 numbers.
+ *
+ * @method mult3x3
+ * @param {p5.Matrix|Float32Array|Number[]} multMatrix The matrix
+ * we want to multiply by
+ * @chainable
+ */
+
+ },
+ {
+ key: 'mult3x3',
+ value: function mult3x3(multMatrix) {
+ var _src;
+ if (multMatrix === this || multMatrix === this.mat3) {
+ _src = this.copy().mat3; // only need to allocate in this rare case
+ } else if (multMatrix instanceof _main.default.Matrix) {
+ _src = multMatrix.mat3;
+ } else if (isMatrixArray(multMatrix)) {
+ _src = multMatrix;
+ } else if (arguments.length === 9) {
+ _src = arguments;
+ } else {
+ return; // nothing to do.
+ } // each row is used for the multiplier
+
+ var b0 = this.mat3[0];
+ var b1 = this.mat3[1];
+ var b2 = this.mat3[2];
+ this.mat3[0] = b0 * _src[0] + b1 * _src[3] + b2 * _src[6];
+ this.mat3[1] = b0 * _src[1] + b1 * _src[4] + b2 * _src[7];
+ this.mat3[2] = b0 * _src[2] + b1 * _src[5] + b2 * _src[8];
+ b0 = this.mat3[3];
+ b1 = this.mat3[4];
+ b2 = this.mat3[5];
+ this.mat3[3] = b0 * _src[0] + b1 * _src[3] + b2 * _src[6];
+ this.mat3[4] = b0 * _src[1] + b1 * _src[4] + b2 * _src[7];
+ this.mat3[5] = b0 * _src[2] + b1 * _src[5] + b2 * _src[8];
+ b0 = this.mat3[6];
+ b1 = this.mat3[7];
+ b2 = this.mat3[8];
+ this.mat3[6] = b0 * _src[0] + b1 * _src[3] + b2 * _src[6];
+ this.mat3[7] = b0 * _src[1] + b1 * _src[4] + b2 * _src[7];
+ this.mat3[8] = b0 * _src[2] + b1 * _src[5] + b2 * _src[8];
+ return this;
+ } /**
+ * This function is only for 3x3 matrices.
+ * A function that returns a column vector of a 3x3 matrix.
+ *
+ * @method column
+ * @param {Number} columnIndex matrix column number
+ * @return {p5.Vector}
+ */
+
+ },
+ {
+ key: 'column',
+ value: function column(columnIndex) {
+ return new _main.default.Vector(this.mat3[3 * columnIndex], this.mat3[3 * columnIndex + 1], this.mat3[3 * columnIndex + 2]);
+ } /**
+ * This function is only for 3x3 matrices.
+ * A function that returns a row vector of a 3x3 matrix.
+ *
+ * @method row
+ * @param {Number} rowIndex matrix row number
+ * @return {p5.Vector}
+ */
+
+ },
+ {
+ key: 'row',
+ value: function row(rowIndex) {
+ return new _main.default.Vector(this.mat3[rowIndex], this.mat3[rowIndex + 3], this.mat3[rowIndex + 6]);
+ } /**
+ * Returns the diagonal elements of the matrix in the form of an array.
+ * A 3x3 matrix will return an array of length 3.
+ * A 4x4 matrix will return an array of length 4.
+ *
+ * @method diagonal
+ * @return {Number[]} An array obtained by arranging the diagonal elements
+ * of the matrix in ascending order of index
+ */
+
+ },
+ {
+ key: 'diagonal',
+ value: function diagonal() {
+ if (this.mat3 !== undefined) {
+ return [this.mat3[0],
+ this.mat3[4],
+ this.mat3[8]];
+ }
+ return [this.mat4[0],
+ this.mat4[5],
+ this.mat4[10],
+ this.mat4[15]];
+ } /**
+ * This function is only for 3x3 matrices.
+ * Takes a vector and returns the vector resulting from multiplying to
+ * that vector by this matrix from left.
+ *
+ * @method multiplyVec3
+ * @param {p5.Vector} multVector the vector to which this matrix applies
+ * @param {p5.Vector} [target] The vector to receive the result
+ * @return {p5.Vector}
+ */
+
+ },
+ {
+ key: 'multiplyVec3',
+ value: function multiplyVec3(multVector, target) {
+ if (target === undefined) {
+ target = multVector.copy();
+ }
+ target.x = this.row(0).dot(multVector);
+ target.y = this.row(1).dot(multVector);
+ target.z = this.row(2).dot(multVector);
+ return target;
+ } /**
+ * This function is only for 4x4 matrices.
+ * Creates a 3x3 matrix whose entries are the top left 3x3 part and returns it.
+ *
+ * @method createSubMatrix3x3
+ * @return {p5.Matrix}
+ */
+
+ },
+ {
+ key: 'createSubMatrix3x3',
+ value: function createSubMatrix3x3() {
+ var result = new _main.default.Matrix('mat3');
+ result.mat3[0] = this.mat4[0];
+ result.mat3[1] = this.mat4[1];
+ result.mat3[2] = this.mat4[2];
+ result.mat3[3] = this.mat4[4];
+ result.mat3[4] = this.mat4[5];
+ result.mat3[5] = this.mat4[6];
+ result.mat3[6] = this.mat4[8];
+ result.mat3[7] = this.mat4[9];
+ result.mat3[8] = this.mat4[10];
+ return result;
+ } /**
+ * PRIVATE
+ */
+ // matrix methods adapted from:
+ // https://developer.mozilla.org/en-US/docs/Web/WebGL/
+ // gluPerspective
+ //
+ // function _makePerspective(fovy, aspect, znear, zfar){
+ // const ymax = znear * Math.tan(fovy * Math.PI / 360.0);
+ // const ymin = -ymax;
+ // const xmin = ymin * aspect;
+ // const xmax = ymax * aspect;
+ // return _makeFrustum(xmin, xmax, ymin, ymax, znear, zfar);
+ // }
+ ////
+ //// glFrustum
+ ////
+ //function _makeFrustum(left, right, bottom, top, znear, zfar){
+ // const X = 2*znear/(right-left);
+ // const Y = 2*znear/(top-bottom);
+ // const A = (right+left)/(right-left);
+ // const B = (top+bottom)/(top-bottom);
+ // const C = -(zfar+znear)/(zfar-znear);
+ // const D = -2*zfar*znear/(zfar-znear);
+ // const frustrumMatrix =[
+ // X, 0, A, 0,
+ // 0, Y, B, 0,
+ // 0, 0, C, D,
+ // 0, 0, -1, 0
+ //];
+ //return frustrumMatrix;
+ // }
+ // function _setMVPMatrices(){
+ ////an identity matrix
+ ////@TODO use the p5.Matrix class to abstract away our MV matrices and
+ ///other math
+ //const _mvMatrix =
+ //[
+ // 1.0,0.0,0.0,0.0,
+ // 0.0,1.0,0.0,0.0,
+ // 0.0,0.0,1.0,0.0,
+ // 0.0,0.0,0.0,1.0
+ //];
+
+ }
+ ], [
+ {
+ key: 'identity',
+ value: function identity(pInst) {
+ return new _main.default.Matrix(pInst);
+ }
+ }
+ ]);
+ return _class;
+ }();
+ var _default = _main.default.Matrix;
+ exports.default = _default;
+ },
+ {
+ '../core/main': 303,
+ 'core-js/modules/es.array.iterator': 182,
+ 'core-js/modules/es.object.to-string': 205,
+ 'core-js/modules/es.typed-array.copy-within': 228,
+ 'core-js/modules/es.typed-array.every': 229,
+ 'core-js/modules/es.typed-array.fill': 230,
+ 'core-js/modules/es.typed-array.filter': 231,
+ 'core-js/modules/es.typed-array.find': 233,
+ 'core-js/modules/es.typed-array.find-index': 232,
+ 'core-js/modules/es.typed-array.float32-array': 234,
+ 'core-js/modules/es.typed-array.for-each': 236,
+ 'core-js/modules/es.typed-array.includes': 237,
+ 'core-js/modules/es.typed-array.index-of': 238,
+ 'core-js/modules/es.typed-array.iterator': 241,
+ 'core-js/modules/es.typed-array.join': 242,
+ 'core-js/modules/es.typed-array.last-index-of': 243,
+ 'core-js/modules/es.typed-array.map': 244,
+ 'core-js/modules/es.typed-array.reduce': 246,
+ 'core-js/modules/es.typed-array.reduce-right': 245,
+ 'core-js/modules/es.typed-array.reverse': 247,
+ 'core-js/modules/es.typed-array.set': 248,
+ 'core-js/modules/es.typed-array.slice': 249,
+ 'core-js/modules/es.typed-array.some': 250,
+ 'core-js/modules/es.typed-array.sort': 251,
+ 'core-js/modules/es.typed-array.subarray': 252,
+ 'core-js/modules/es.typed-array.to-locale-string': 253,
+ 'core-js/modules/es.typed-array.to-string': 254
+ }
+ ],
+ 357: [
+ function (_dereq_, module, exports) {
+ 'use strict';
+ _dereq_('core-js/modules/es.array.map');
+ _dereq_('core-js/modules/es.array.map');
+ Object.defineProperty(exports, '__esModule', {
+ value: true
+ });
+ exports.default = void 0;
+ var _main = _interopRequireDefault(_dereq_('../core/main'));
+ function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : {
+ default:
+ obj
+ };
+ }
+ function _classCallCheck(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError('Cannot call a class as a function');
+ }
+ }
+ function _defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ('value' in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+ function _createClass(Constructor, protoProps, staticProps) {
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) _defineProperties(Constructor, staticProps);
+ return Constructor;
+ }
+ _main.default.RenderBuffer = /*#__PURE__*/ function () {
+ function _class(size, src, dst, attr, renderer, map) {
+ _classCallCheck(this, _class);
+ this.size = size; // the number of FLOATs in each vertex
+ this.src = src; // the name of the model's source array
+ this.dst = dst; // the name of the geometry's buffer
+ this.attr = attr; // the name of the vertex attribute
+ this._renderer = renderer;
+ this.map = map; // optional, a transformation function to apply to src
+ } /**
+ * Enables and binds the buffers used by shader when the appropriate data exists in geometry.
+ * Must always be done prior to drawing geometry in WebGL.
+ * @param {p5.Geometry} geometry Geometry that is going to be drawn
+ * @param {p5.Shader} shader Active shader
+ * @private
+ */
+
+ _createClass(_class, [
+ {
+ key: '_prepareBuffer',
+ value: function _prepareBuffer(geometry, shader) {
+ var attributes = shader.attributes;
+ var gl = this._renderer.GL;
+ var model;
+ if (geometry.model) {
+ model = geometry.model;
+ } else {
+ model = geometry;
+ } // loop through each of the buffer definitions
+
+ var attr = attributes[this.attr];
+ if (!attr) {
+ return;
+ } // check if the model has the appropriate source array
+
+ var buffer = geometry[this.dst];
+ var src = model[this.src];
+ if (src.length > 0) {
+ // check if we need to create the GL buffer
+ var createBuffer = !buffer;
+ if (createBuffer) {
+ // create and remember the buffer
+ geometry[this.dst] = buffer = gl.createBuffer();
+ } // bind the buffer
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ // check if we need to fill the buffer with data
+ if (createBuffer || model.dirtyFlags[this.src] !== false) {
+ var map = this.map;
+ // get the values from the model, possibly transformed
+ var values = map ? map(src) : src;
+ // fill the buffer with the values
+ this._renderer._bindBuffer(buffer, gl.ARRAY_BUFFER, values);
+ // mark the model's source array as clean
+ model.dirtyFlags[this.src] = false;
+ } // enable the attribute
+
+ shader.enableAttrib(attr, this.size);
+ } else {
+ var loc = attr.location;
+ if (loc === - 1 || !this._renderer.registerEnabled.has(loc)) {
+ return;
+ } // Disable register corresponding to unused attribute
+
+ gl.disableVertexAttribArray(loc);
+ // Record register availability
+ this._renderer.registerEnabled.delete(loc);
+ }
+ }
+ }
+ ]);
+ return _class;
+ }();
+ var _default = _main.default.RenderBuffer;
+ exports.default = _default;
+ },
+ {
+ '../core/main': 303,
+ 'core-js/modules/es.array.map': 185
+ }
+ ],
+ 358: [
+ function (_dereq_, module, exports) {
+ 'use strict';
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.concat');
+ _dereq_('core-js/modules/es.array.fill');
+ _dereq_('core-js/modules/es.array.find-index');
+ _dereq_('core-js/modules/es.array.from');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.array.map');
+ _dereq_('core-js/modules/es.array.slice');
+ _dereq_('core-js/modules/es.map');
+ _dereq_('core-js/modules/es.object.get-own-property-descriptor');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.regexp.to-string');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/es.weak-map');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ function _typeof2(obj) {
+ if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') {
+ _typeof2 = function _typeof2(obj) {
+ return typeof obj;
+ };
+ } else {
+ _typeof2 = function _typeof2(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
+ };
+ }
+ return _typeof2(obj);
+ }
+ function _typeof(obj) {
+ if (typeof Symbol === 'function' && _typeof2(Symbol.iterator) === 'symbol') {
+ _typeof = function _typeof(obj) {
+ return _typeof2(obj);
+ };
+ } else {
+ _typeof = function _typeof(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : _typeof2(obj);
+ };
+ }
+ return _typeof(obj);
+ }
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.concat');
+ _dereq_('core-js/modules/es.array.fill');
+ _dereq_('core-js/modules/es.array.find-index');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.array.map');
+ _dereq_('core-js/modules/es.array.slice');
+ _dereq_('core-js/modules/es.map');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ Object.defineProperty(exports, '__esModule', {
+ value: true
+ });
+ exports.default = void 0;
+ var _main = _interopRequireDefault(_dereq_('../core/main'));
+ var constants = _interopRequireWildcard(_dereq_('../core/constants'));
+ _dereq_('./p5.RenderBuffer');
+ function _getRequireWildcardCache() {
+ if (typeof WeakMap !== 'function') return null;
+ var cache = new WeakMap();
+ _getRequireWildcardCache = function _getRequireWildcardCache() {
+ return cache;
+ };
+ return cache;
+ }
+ function _interopRequireWildcard(obj) {
+ if (obj && obj.__esModule) {
+ return obj;
+ }
+ if (obj === null || _typeof(obj) !== 'object' && typeof obj !== 'function') {
+ return {
+ default:
+ obj
+ };
+ }
+ var cache = _getRequireWildcardCache();
+ if (cache && cache.has(obj)) {
+ return cache.get(obj);
+ }
+ var newObj = {
+ };
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
+ if (desc && (desc.get || desc.set)) {
+ Object.defineProperty(newObj, key, desc);
+ } else {
+ newObj[key] = obj[key];
+ }
+ }
+ }
+ newObj.default = obj;
+ if (cache) {
+ cache.set(obj, newObj);
+ }
+ return newObj;
+ }
+ function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : {
+ default:
+ obj
+ };
+ }
+ function _toConsumableArray(arr) {
+ return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
+ }
+ function _nonIterableSpread() {
+ throw new TypeError('Invalid attempt to spread non-iterable instance');
+ }
+ function _iterableToArray(iter) {
+ if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === '[object Arguments]') return Array.from(iter);
+ }
+ function _arrayWithoutHoles(arr) {
+ if (Array.isArray(arr)) {
+ for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
+ arr2[i] = arr[i];
+ }
+ return arr2;
+ }
+ } /**
+ * Welcome to RendererGL Immediate Mode.
+ * Immediate mode is used for drawing custom shapes
+ * from a set of vertices. Immediate Mode is activated
+ * when you call beginShape() & de-activated when you call endShape().
+ * Immediate mode is a style of programming borrowed
+ * from OpenGL's (now-deprecated) immediate mode.
+ * It differs from p5.js' default, Retained Mode, which caches
+ * geometries and buffers on the CPU to reduce the number of webgl
+ * draw calls. Retained mode is more efficient & performative,
+ * however, Immediate Mode is useful for sketching quick
+ * geometric ideas.
+ */
+ /**
+ * Begin shape drawing. This is a helpful way of generating
+ * custom shapes quickly. However in WEBGL mode, application
+ * performance will likely drop as a result of too many calls to
+ * beginShape() / endShape(). As a high performance alternative,
+ * please use p5.js geometry primitives.
+ * @private
+ * @method beginShape
+ * @param {Number} mode webgl primitives mode. beginShape supports the
+ * following modes:
+ * POINTS,LINES,LINE_STRIP,LINE_LOOP,TRIANGLES,
+ * TRIANGLE_STRIP, TRIANGLE_FAN, QUADS, QUAD_STRIP,
+ * and TESS(WEBGL only)
+ * @chainable
+ */
+
+ _main.default.RendererGL.prototype.beginShape = function (mode) {
+ this.immediateMode.shapeMode = mode !== undefined ? mode : constants.TESS;
+ this.immediateMode.geometry.reset();
+ this.immediateMode.contourIndices = [
+ ];
+ return this;
+ };
+ var immediateBufferStrides = {
+ vertices: 1,
+ vertexNormals: 1,
+ vertexColors: 4,
+ vertexStrokeColors: 4,
+ uvs: 2
+ };
+ _main.default.RendererGL.prototype.beginContour = function () {
+ if (this.immediateMode.shapeMode !== constants.TESS) {
+ throw new Error('WebGL mode can only use contours with beginShape(TESS).');
+ }
+ this.immediateMode.contourIndices.push(this.immediateMode.geometry.vertices.length);
+ };
+ /**
+ * adds a vertex to be drawn in a custom Shape.
+ * @private
+ * @method vertex
+ * @param {Number} x x-coordinate of vertex
+ * @param {Number} y y-coordinate of vertex
+ * @param {Number} z z-coordinate of vertex
+ * @chainable
+ * @TODO implement handling of p5.Vector args
+ */
+ _main.default.RendererGL.prototype.vertex = function (x, y) {
+ // WebGL 1 doesn't support QUADS or QUAD_STRIP, so we duplicate data to turn
+ // QUADS into TRIANGLES and QUAD_STRIP into TRIANGLE_STRIP. (There is no extra
+ // work to convert QUAD_STRIP here, since the only difference is in how edges
+ // are rendered.)
+ if (this.immediateMode.shapeMode === constants.QUADS) {
+ // A finished quad turned into triangles should leave 6 vertices in the
+ // buffer:
+ // 0--3 0 3--5
+ // | | --> | \ \ |
+ // 1--2 1--2 4
+ // When vertex index 3 is being added, add the necessary duplicates.
+ if (this.immediateMode.geometry.vertices.length % 6 === 3) {
+ for (var key in immediateBufferStrides) {
+ var stride = immediateBufferStrides[key];
+ var buffer = this.immediateMode.geometry[key];
+ buffer.push.apply(buffer, _toConsumableArray(buffer.slice(buffer.length - 3 * stride, buffer.length - 2 * stride)).concat(_toConsumableArray(buffer.slice(buffer.length - stride, buffer.length))));
+ }
+ }
+ }
+ var z,
+ u,
+ v;
+ // default to (x, y) mode: all other arguments assumed to be 0.
+ z = u = v = 0;
+ if (arguments.length === 3) {
+ // (x, y, z) mode: (u, v) assumed to be 0.
+ z = arguments[2];
+ } else if (arguments.length === 4) {
+ // (x, y, u, v) mode: z assumed to be 0.
+ u = arguments[2];
+ v = arguments[3];
+ } else if (arguments.length === 5) {
+ // (x, y, z, u, v) mode
+ z = arguments[2];
+ u = arguments[3];
+ v = arguments[4];
+ }
+ var vert = new _main.default.Vector(x, y, z);
+ this.immediateMode.geometry.vertices.push(vert);
+ this.immediateMode.geometry.vertexNormals.push(this._currentNormal);
+ var vertexColor = this.curFillColor || [
+ 0.5,
+ 0.5,
+ 0.5,
+ 1
+ ];
+ this.immediateMode.geometry.vertexColors.push(vertexColor[0], vertexColor[1], vertexColor[2], vertexColor[3]);
+ var lineVertexColor = this.curStrokeColor || [
+ 0.5,
+ 0.5,
+ 0.5,
+ 1
+ ];
+ this.immediateMode.geometry.vertexStrokeColors.push(lineVertexColor[0], lineVertexColor[1], lineVertexColor[2], lineVertexColor[3]);
+ if (this.textureMode === constants.IMAGE && !this.isProcessingVertices) {
+ if (this._tex !== null) {
+ if (this._tex.width > 0 && this._tex.height > 0) {
+ u /= this._tex.width;
+ v /= this._tex.height;
+ }
+ } else if (this.userFillShader !== undefined || this.userStrokeShader !== undefined || this.userPointShader !== undefined) {
+ // Do nothing if user-defined shaders are present
+ } else if (this._tex === null && arguments.length >= 4) {
+ // Only throw this warning if custom uv's have been provided
+ console.warn('You must first call texture() before using' + ' vertex() with image based u and v coordinates');
+ }
+ }
+ this.immediateMode.geometry.uvs.push(u, v);
+ this.immediateMode._bezierVertex[0] = x;
+ this.immediateMode._bezierVertex[1] = y;
+ this.immediateMode._bezierVertex[2] = z;
+ this.immediateMode._quadraticVertex[0] = x;
+ this.immediateMode._quadraticVertex[1] = y;
+ this.immediateMode._quadraticVertex[2] = z;
+ return this;
+ };
+ /**
+ * Sets the normal to use for subsequent vertices.
+ * @private
+ * @method normal
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} z
+ * @chainable
+ *
+ * @method normal
+ * @param {Vector} v
+ * @chainable
+ */
+ _main.default.RendererGL.prototype.normal = function (xorv, y, z) {
+ if (xorv instanceof _main.default.Vector) {
+ this._currentNormal = xorv;
+ } else {
+ this._currentNormal = new _main.default.Vector(xorv, y, z);
+ }
+ return this;
+ };
+ /**
+ * End shape drawing and render vertices to screen.
+ * @chainable
+ */
+ _main.default.RendererGL.prototype.endShape = function (mode, isCurve, isBezier, isQuadratic, isContour, shapeKind) {
+ var count = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 1;
+ if (this.immediateMode.shapeMode === constants.POINTS) {
+ this._drawPoints(this.immediateMode.geometry.vertices, this.immediateMode.buffers.point);
+ return this;
+ } // When we are drawing a shape then the shape mode is TESS,
+ // but in case of triangle we can skip the breaking into small triangle
+ // this can optimize performance by skipping the step of breaking it into triangles
+
+ if (this.immediateMode.geometry.vertices.length === 3 && this.immediateMode.shapeMode === constants.TESS) {
+ this.immediateMode.shapeMode === constants.TRIANGLES;
+ }
+ this.isProcessingVertices = true;
+ this._processVertices.apply(this, arguments);
+ this.isProcessingVertices = false;
+ // LINE_STRIP and LINES are not used for rendering, instead
+ // they only indicate a way to modify vertices during the _processVertices() step
+ var is_line = false;
+ if (this.immediateMode.shapeMode === constants.LINE_STRIP || this.immediateMode.shapeMode === constants.LINES) {
+ this.immediateMode.shapeMode = constants.TRIANGLE_FAN;
+ is_line = true;
+ } // WebGL doesn't support the QUADS and QUAD_STRIP modes, so we
+ // need to convert them to a supported format. In `vertex()`, we reformat
+ // the input data into the formats specified below.
+
+ if (this.immediateMode.shapeMode === constants.QUADS) {
+ this.immediateMode.shapeMode = constants.TRIANGLES;
+ } else if (this.immediateMode.shapeMode === constants.QUAD_STRIP) {
+ this.immediateMode.shapeMode = constants.TRIANGLE_STRIP;
+ }
+ if (this._doFill && !is_line) {
+ if (!this.geometryBuilder && this.immediateMode.geometry.vertices.length >= 3) {
+ this._drawImmediateFill(count);
+ }
+ }
+ if (this._doStroke) {
+ if (!this.geometryBuilder && this.immediateMode.geometry.lineVertices.length >= 1) {
+ this._drawImmediateStroke();
+ }
+ }
+ if (this.geometryBuilder) {
+ this.geometryBuilder.addImmediate();
+ }
+ this.isBezier = false;
+ this.isQuadratic = false;
+ this.isCurve = false;
+ this.immediateMode._bezierVertex.length = 0;
+ this.immediateMode._quadraticVertex.length = 0;
+ this.immediateMode._curveVertex.length = 0;
+ return this;
+ };
+ /**
+ * Called from endShape(). This function calculates the stroke vertices for custom shapes and
+ * tesselates shapes when applicable.
+ * @private
+ * @param {Number} mode webgl primitives mode. beginShape supports the
+ * following modes:
+ * POINTS,LINES,LINE_STRIP,LINE_LOOP,TRIANGLES,
+ * TRIANGLE_STRIP, TRIANGLE_FAN and TESS(WEBGL only)
+ */
+ _main.default.RendererGL.prototype._processVertices = function (mode) {
+ if (this.immediateMode.geometry.vertices.length === 0) return;
+ var calculateStroke = this._doStroke;
+ var shouldClose = mode === constants.CLOSE;
+ if (calculateStroke) {
+ this.immediateMode.geometry.edges = this._calculateEdges(this.immediateMode.shapeMode, this.immediateMode.geometry.vertices, shouldClose);
+ if (!this.geometryBuilder) {
+ this.immediateMode.geometry._edgesToVertices();
+ }
+ } // For hollow shapes, user must set mode to TESS
+
+ var convexShape = this.immediateMode.shapeMode === constants.TESS;
+ // If the shape has a contour, we have to re-triangulate to cut out the
+ // contour region
+ var hasContour = this.immediateMode.contourIndices.length > 0;
+ // We tesselate when drawing curves or convex shapes
+ var shouldTess = this._doFill && (this.isBezier || this.isQuadratic || this.isCurve || convexShape || hasContour) && this.immediateMode.shapeMode !== constants.LINES;
+ if (shouldTess) {
+ this._tesselateShape();
+ }
+ };
+ /**
+ * Called from _processVertices(). This function calculates the stroke vertices for custom shapes and
+ * tesselates shapes when applicable.
+ * @private
+ * @returns {Array[Number]} indices for custom shape vertices indicating edges.
+ */
+ _main.default.RendererGL.prototype._calculateEdges = function (shapeMode, verts, shouldClose) {
+ var res = [
+ ];
+ var i = 0;
+ var contourIndices = this.immediateMode.contourIndices.slice();
+ var contourStart = 0;
+ switch (shapeMode) {
+ case constants.TRIANGLE_STRIP:
+ for (i = 0; i < verts.length - 2; i++) {
+ res.push([i,
+ i + 1]);
+ res.push([i,
+ i + 2]);
+ }
+ res.push([i,
+ i + 1]);
+ break;
+ case constants.TRIANGLE_FAN:
+ for (i = 1; i < verts.length - 1; i++) {
+ res.push([0,
+ i]);
+ res.push([i,
+ i + 1]);
+ }
+ res.push([0,
+ verts.length - 1]);
+ break;
+ case constants.TRIANGLES:
+ for (i = 0; i < verts.length - 2; i = i + 3) {
+ res.push([i,
+ i + 1]);
+ res.push([i + 1,
+ i + 2]);
+ res.push([i + 2,
+ i]);
+ }
+ break;
+ case constants.LINES:
+ for (i = 0; i < verts.length - 1; i = i + 2) {
+ res.push([i,
+ i + 1]);
+ }
+ break;
+ case constants.QUADS:
+ // Quads have been broken up into two triangles by `vertex()`:
+ // 0 3--5
+ // | \ \ |
+ // 1--2 4
+ for (i = 0; i < verts.length - 5; i += 6) {
+ res.push([i,
+ i + 1]);
+ res.push([i + 1,
+ i + 2]);
+ res.push([i + 3,
+ i + 5]);
+ res.push([i + 4,
+ i + 5]);
+ }
+ break;
+ case constants.QUAD_STRIP:
+ // 0---2---4
+ // | | |
+ // 1---3---5
+ for (i = 0; i < verts.length - 2; i += 2) {
+ res.push([i,
+ i + 1]);
+ res.push([i,
+ i + 2]);
+ res.push([i + 1,
+ i + 3]);
+ }
+ res.push([i,
+ i + 1]);
+ break;
+ default:
+ // TODO: handle contours in other modes too
+ for (i = 0; i < verts.length; i++) {
+ // Handle breaks between contours
+ if (i + 1 < verts.length && i + 1 !== contourIndices[0]) {
+ res.push([i,
+ i + 1]);
+ } else {
+ if (shouldClose || contourStart) {
+ res.push([i,
+ contourStart]);
+ }
+ if (contourIndices.length > 0) {
+ contourStart = contourIndices.shift();
+ }
+ }
+ }
+ break;
+ }
+ if (shapeMode !== constants.TESS && shouldClose) {
+ res.push([verts.length - 1,
+ 0]);
+ }
+ return res;
+ };
+ /**
+ * Called from _processVertices() when applicable. This function tesselates immediateMode.geometry.
+ * @private
+ */
+ _main.default.RendererGL.prototype._tesselateShape = function () {
+ var _this = this;
+ // TODO: handle non-TESS shape modes that have contours
+ this.immediateMode.shapeMode = constants.TRIANGLES;
+ var contours = [
+ []
+ ];
+ for (var i = 0; i < this.immediateMode.geometry.vertices.length; i++) {
+ if (this.immediateMode.contourIndices.length > 0 && this.immediateMode.contourIndices[0] === i) {
+ this.immediateMode.contourIndices.shift();
+ contours.push([]);
+ }
+ contours[contours.length - 1].push(this.immediateMode.geometry.vertices[i].x, this.immediateMode.geometry.vertices[i].y, this.immediateMode.geometry.vertices[i].z, this.immediateMode.geometry.uvs[i * 2], this.immediateMode.geometry.uvs[i * 2 + 1], this.immediateMode.geometry.vertexColors[i * 4], this.immediateMode.geometry.vertexColors[i * 4 + 1], this.immediateMode.geometry.vertexColors[i * 4 + 2], this.immediateMode.geometry.vertexColors[i * 4 + 3], this.immediateMode.geometry.vertexNormals[i].x, this.immediateMode.geometry.vertexNormals[i].y, this.immediateMode.geometry.vertexNormals[i].z);
+ }
+ var polyTriangles = this._triangulate(contours);
+ var originalVertices = this.immediateMode.geometry.vertices;
+ this.immediateMode.geometry.vertices = [
+ ];
+ this.immediateMode.geometry.vertexNormals = [
+ ];
+ this.immediateMode.geometry.uvs = [
+ ];
+ var colors = [
+ ];
+ for (var j = 0, polyTriLength = polyTriangles.length; j < polyTriLength; j = j + _main.default.RendererGL.prototype.tessyVertexSize) {
+ colors.push.apply(colors, _toConsumableArray(polyTriangles.slice(j + 5, j + 9)));
+ this.normal.apply(this, _toConsumableArray(polyTriangles.slice(j + 9, j + 12)));
+ this.vertex.apply(this, _toConsumableArray(polyTriangles.slice(j, j + 5)));
+ }
+ if (this.geometryBuilder) {
+ // Tesselating the face causes the indices of edge vertices to stop being
+ // correct. When rendering, this is not a problem, since _edgesToVertices
+ // will have been called before this, and edge vertex indices are no longer
+ // needed. However, the geometry builder still needs this information, so
+ // when one is active, we need to update the indices.
+ //
+ // We record index mappings in a Map so that once we have found a
+ // corresponding vertex, we don't need to loop to find it again.
+ var newIndex = new Map();
+ this.immediateMode.geometry.edges = this.immediateMode.geometry.edges.map(function (edge) {
+ return edge.map(function (origIdx) {
+ if (!newIndex.has(origIdx)) {
+ var orig = originalVertices[origIdx];
+ var newVertIndex = _this.immediateMode.geometry.vertices.findIndex(function (v) {
+ return orig.x === v.x && orig.y === v.y && orig.z === v.z;
+ });
+ if (newVertIndex === - 1) {
+ // The tesselation process didn't output a vertex with the exact
+ // coordinate as before, potentially due to numerical issues. This
+ // doesn't happen often, but in this case, pick the closest point
+ var closestDist = Infinity;
+ var closestIndex = 0;
+ for (var _i = 0; _i < _this.immediateMode.geometry.vertices.length; _i++) {
+ var vert = _this.immediateMode.geometry.vertices[_i];
+ var dX = orig.x - vert.x;
+ var dY = orig.y - vert.y;
+ var dZ = orig.z - vert.z;
+ var dist = dX * dX + dY * dY + dZ * dZ;
+ if (dist < closestDist) {
+ closestDist = dist;
+ closestIndex = _i;
+ }
+ }
+ newVertIndex = closestIndex;
+ }
+ newIndex.set(origIdx, newVertIndex);
+ }
+ return newIndex.get(origIdx);
+ });
+ });
+ }
+ this.immediateMode.geometry.vertexColors = colors;
+ };
+ /**
+ * Called from endShape(). Responsible for calculating normals, setting shader uniforms,
+ * enabling all appropriate buffers, applying color blend, and drawing the fill geometry.
+ * @private
+ */
+ _main.default.RendererGL.prototype._drawImmediateFill = function () {
+ var count = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
+ var gl = this.GL;
+ this._useVertexColor = this.immediateMode.geometry.vertexColors.length > 0;
+ var shader;
+ shader = this._getImmediateFillShader();
+ this._setFillUniforms(shader);
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+ try {
+ for (var _iterator = this.immediateMode.buffers.fill[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var buff = _step.value;
+ buff._prepareBuffer(this.immediateMode.geometry, shader);
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ shader.disableRemainingAttributes();
+ this._applyColorBlend(this.curFillColor, this.immediateMode.geometry.hasFillTransparency());
+ if (count === 1) {
+ gl.drawArrays(this.immediateMode.shapeMode, 0, this.immediateMode.geometry.vertices.length);
+ } else {
+ try {
+ gl.drawArraysInstanced(this.immediateMode.shapeMode, 0, this.immediateMode.geometry.vertices.length, count);
+ } catch (e) {
+ console.log('🌸 p5.js says: Instancing is only supported in WebGL2 mode');
+ }
+ }
+ shader.unbindShader();
+ };
+ /**
+ * Called from endShape(). Responsible for calculating normals, setting shader uniforms,
+ * enabling all appropriate buffers, applying color blend, and drawing the stroke geometry.
+ * @private
+ */
+ _main.default.RendererGL.prototype._drawImmediateStroke = function () {
+ var gl = this.GL;
+ this._useLineColor = this.immediateMode.geometry.vertexStrokeColors.length > 0;
+ var shader = this._getImmediateStrokeShader();
+ this._setStrokeUniforms(shader);
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
+ try {
+ for (var _iterator2 = this.immediateMode.buffers.stroke[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var buff = _step2.value;
+ buff._prepareBuffer(this.immediateMode.geometry, shader);
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
+ }
+ shader.disableRemainingAttributes();
+ this._applyColorBlend(this.curStrokeColor, this.immediateMode.geometry.hasFillTransparency());
+ gl.drawArrays(gl.TRIANGLES, 0, this.immediateMode.geometry.lineVertices.length / 3);
+ shader.unbindShader();
+ };
+ var _default = _main.default.RendererGL;
+ exports.default = _default;
+ },
+ {
+ '../core/constants': 291,
+ '../core/main': 303,
+ './p5.RenderBuffer': 357,
+ 'core-js/modules/es.array.concat': 170,
+ 'core-js/modules/es.array.fill': 173,
+ 'core-js/modules/es.array.find-index': 175,
+ 'core-js/modules/es.array.from': 179,
+ 'core-js/modules/es.array.iterator': 182,
+ 'core-js/modules/es.array.map': 185,
+ 'core-js/modules/es.array.slice': 186,
+ 'core-js/modules/es.map': 192,
+ 'core-js/modules/es.object.get-own-property-descriptor': 201,
+ 'core-js/modules/es.object.to-string': 205,
+ 'core-js/modules/es.regexp.to-string': 211,
+ 'core-js/modules/es.string.iterator': 215,
+ 'core-js/modules/es.symbol': 227,
+ 'core-js/modules/es.symbol.description': 225,
+ 'core-js/modules/es.symbol.iterator': 226,
+ 'core-js/modules/es.weak-map': 259,
+ 'core-js/modules/web.dom-collections.iterator': 261
+ }
+ ],
+ 359: [
+ function (_dereq_, module, exports) {
+ 'use strict';
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.fill');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.array.some');
+ _dereq_('core-js/modules/es.object.get-own-property-descriptor');
+ _dereq_('core-js/modules/es.object.keys');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/es.typed-array.float32-array');
+ _dereq_('core-js/modules/es.typed-array.uint16-array');
+ _dereq_('core-js/modules/es.typed-array.uint32-array');
+ _dereq_('core-js/modules/es.typed-array.copy-within');
+ _dereq_('core-js/modules/es.typed-array.every');
+ _dereq_('core-js/modules/es.typed-array.fill');
+ _dereq_('core-js/modules/es.typed-array.filter');
+ _dereq_('core-js/modules/es.typed-array.find');
+ _dereq_('core-js/modules/es.typed-array.find-index');
+ _dereq_('core-js/modules/es.typed-array.for-each');
+ _dereq_('core-js/modules/es.typed-array.includes');
+ _dereq_('core-js/modules/es.typed-array.index-of');
+ _dereq_('core-js/modules/es.typed-array.iterator');
+ _dereq_('core-js/modules/es.typed-array.join');
+ _dereq_('core-js/modules/es.typed-array.last-index-of');
+ _dereq_('core-js/modules/es.typed-array.map');
+ _dereq_('core-js/modules/es.typed-array.reduce');
+ _dereq_('core-js/modules/es.typed-array.reduce-right');
+ _dereq_('core-js/modules/es.typed-array.reverse');
+ _dereq_('core-js/modules/es.typed-array.set');
+ _dereq_('core-js/modules/es.typed-array.slice');
+ _dereq_('core-js/modules/es.typed-array.some');
+ _dereq_('core-js/modules/es.typed-array.sort');
+ _dereq_('core-js/modules/es.typed-array.subarray');
+ _dereq_('core-js/modules/es.typed-array.to-locale-string');
+ _dereq_('core-js/modules/es.typed-array.to-string');
+ _dereq_('core-js/modules/es.weak-map');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ function _typeof2(obj) {
+ if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') {
+ _typeof2 = function _typeof2(obj) {
+ return typeof obj;
+ };
+ } else {
+ _typeof2 = function _typeof2(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
+ };
+ }
+ return _typeof2(obj);
+ }
+ function _typeof(obj) {
+ if (typeof Symbol === 'function' && _typeof2(Symbol.iterator) === 'symbol') {
+ _typeof = function _typeof(obj) {
+ return _typeof2(obj);
+ };
+ } else {
+ _typeof = function _typeof(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : _typeof2(obj);
+ };
+ }
+ return _typeof(obj);
+ }
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.fill');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.array.some');
+ _dereq_('core-js/modules/es.object.keys');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/es.typed-array.float32-array');
+ _dereq_('core-js/modules/es.typed-array.uint16-array');
+ _dereq_('core-js/modules/es.typed-array.uint32-array');
+ _dereq_('core-js/modules/es.typed-array.copy-within');
+ _dereq_('core-js/modules/es.typed-array.every');
+ _dereq_('core-js/modules/es.typed-array.fill');
+ _dereq_('core-js/modules/es.typed-array.filter');
+ _dereq_('core-js/modules/es.typed-array.find');
+ _dereq_('core-js/modules/es.typed-array.find-index');
+ _dereq_('core-js/modules/es.typed-array.for-each');
+ _dereq_('core-js/modules/es.typed-array.includes');
+ _dereq_('core-js/modules/es.typed-array.index-of');
+ _dereq_('core-js/modules/es.typed-array.iterator');
+ _dereq_('core-js/modules/es.typed-array.join');
+ _dereq_('core-js/modules/es.typed-array.last-index-of');
+ _dereq_('core-js/modules/es.typed-array.map');
+ _dereq_('core-js/modules/es.typed-array.reduce');
+ _dereq_('core-js/modules/es.typed-array.reduce-right');
+ _dereq_('core-js/modules/es.typed-array.reverse');
+ _dereq_('core-js/modules/es.typed-array.set');
+ _dereq_('core-js/modules/es.typed-array.slice');
+ _dereq_('core-js/modules/es.typed-array.some');
+ _dereq_('core-js/modules/es.typed-array.sort');
+ _dereq_('core-js/modules/es.typed-array.subarray');
+ _dereq_('core-js/modules/es.typed-array.to-locale-string');
+ _dereq_('core-js/modules/es.typed-array.to-string');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ Object.defineProperty(exports, '__esModule', {
+ value: true
+ });
+ exports.default = void 0;
+ var _main = _interopRequireDefault(_dereq_('../core/main'));
+ _dereq_('./p5.RendererGL');
+ _dereq_('./p5.RenderBuffer');
+ var constants = _interopRequireWildcard(_dereq_('../core/constants'));
+ function _getRequireWildcardCache() {
+ if (typeof WeakMap !== 'function') return null;
+ var cache = new WeakMap();
+ _getRequireWildcardCache = function _getRequireWildcardCache() {
+ return cache;
+ };
+ return cache;
+ }
+ function _interopRequireWildcard(obj) {
+ if (obj && obj.__esModule) {
+ return obj;
+ }
+ if (obj === null || _typeof(obj) !== 'object' && typeof obj !== 'function') {
+ return {
+ default:
+ obj
+ };
+ }
+ var cache = _getRequireWildcardCache();
+ if (cache && cache.has(obj)) {
+ return cache.get(obj);
+ }
+ var newObj = {
+ };
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
+ if (desc && (desc.get || desc.set)) {
+ Object.defineProperty(newObj, key, desc);
+ } else {
+ newObj[key] = obj[key];
+ }
+ }
+ }
+ newObj.default = obj;
+ if (cache) {
+ cache.set(obj, newObj);
+ }
+ return newObj;
+ }
+ function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : {
+ default:
+ obj
+ };
+ } //Retained Mode. The default mode for rendering 3D primitives
+ //in WEBGL.
+ /**
+ * @param {p5.Geometry} geometry The model whose resources will be freed
+ */
+
+ _main.default.RendererGL.prototype.freeGeometry = function (geometry) {
+ if (!geometry.gid) {
+ console.warn('The model you passed to freeGeometry does not have an id!');
+ return;
+ }
+ this._freeBuffers(geometry.gid);
+ };
+ /**
+ * _initBufferDefaults
+ * @private
+ * @description initializes buffer defaults. runs each time a new geometry is
+ * registered
+ * @param {String} gId key of the geometry object
+ * @returns {Object} a new buffer object
+ */
+ _main.default.RendererGL.prototype._initBufferDefaults = function (gId) {
+ this._freeBuffers(gId);
+ //@TODO remove this limit on hashes in retainedMode.geometry
+ if (Object.keys(this.retainedMode.geometry).length > 1000) {
+ var key = Object.keys(this.retainedMode.geometry) [0];
+ this._freeBuffers(key);
+ } //create a new entry in our retainedMode.geometry
+
+ return this.retainedMode.geometry[gId] = {
+ };
+ };
+ _main.default.RendererGL.prototype._freeBuffers = function (gId) {
+ var buffers = this.retainedMode.geometry[gId];
+ if (!buffers) {
+ return;
+ }
+ delete this.retainedMode.geometry[gId];
+ var gl = this.GL;
+ if (buffers.indexBuffer) {
+ gl.deleteBuffer(buffers.indexBuffer);
+ }
+ function freeBuffers(defs) {
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+ try {
+ for (var _iterator = defs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var def = _step.value;
+ if (buffers[def.dst]) {
+ gl.deleteBuffer(buffers[def.dst]);
+ buffers[def.dst] = null;
+ }
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ } // free all the buffers
+
+ freeBuffers(this.retainedMode.buffers.stroke);
+ freeBuffers(this.retainedMode.buffers.fill);
+ };
+ /**
+ * creates a buffers object that holds the WebGL render buffers
+ * for a geometry.
+ * @private
+ * @param {String} gId key of the geometry object
+ * @param {p5.Geometry} model contains geometry data
+ */
+ _main.default.RendererGL.prototype.createBuffers = function (gId, model) {
+ var gl = this.GL;
+ //initialize the gl buffers for our geom groups
+ var buffers = this._initBufferDefaults(gId);
+ buffers.model = model;
+ var indexBuffer = buffers.indexBuffer;
+ if (model.faces.length) {
+ // allocate space for faces
+ if (!indexBuffer) indexBuffer = buffers.indexBuffer = gl.createBuffer();
+ var vals = _main.default.RendererGL.prototype._flatten(model.faces);
+ // If any face references a vertex with an index greater than the maximum
+ // un-singed 16 bit integer, then we need to use a Uint32Array instead of a
+ // Uint16Array
+ var hasVertexIndicesOverMaxUInt16 = vals.some(function (v) {
+ return v > 65535;
+ });
+ var type = hasVertexIndicesOverMaxUInt16 ? Uint32Array : Uint16Array;
+ this._bindBuffer(indexBuffer, gl.ELEMENT_ARRAY_BUFFER, vals, type);
+ // If we're using a Uint32Array for our indexBuffer we will need to pass a
+ // different enum value to WebGL draw triangles. This happens in
+ // the _drawElements function.
+ buffers.indexBufferType = hasVertexIndicesOverMaxUInt16 ? gl.UNSIGNED_INT : gl.UNSIGNED_SHORT;
+ // the vertex count is based on the number of faces
+ buffers.vertexCount = model.faces.length * 3;
+ } else {
+ // the index buffer is unused, remove it
+ if (indexBuffer) {
+ gl.deleteBuffer(indexBuffer);
+ buffers.indexBuffer = null;
+ } // the vertex count comes directly from the model
+
+ buffers.vertexCount = model.vertices ? model.vertices.length : 0;
+ }
+ buffers.lineVertexCount = model.lineVertices ? model.lineVertices.length / 3 : 0;
+ return buffers;
+ };
+ /**
+ * Draws buffers given a geometry key ID
+ * @private
+ * @param {String} gId ID in our geom hash
+ * @chainable
+ */
+ _main.default.RendererGL.prototype.drawBuffers = function (gId) {
+ var gl = this.GL;
+ var geometry = this.retainedMode.geometry[gId];
+ if (!this.geometryBuilder && this._doFill && this.retainedMode.geometry[gId].vertexCount > 0) {
+ this._useVertexColor = geometry.model.vertexColors.length > 0;
+ var fillShader = this._getRetainedFillShader();
+ this._setFillUniforms(fillShader);
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
+ try {
+ for (var _iterator2 = this.retainedMode.buffers.fill[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var buff = _step2.value;
+ buff._prepareBuffer(geometry, fillShader);
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
+ }
+ fillShader.disableRemainingAttributes();
+ if (geometry.indexBuffer) {
+ //vertex index buffer
+ this._bindBuffer(geometry.indexBuffer, gl.ELEMENT_ARRAY_BUFFER);
+ }
+ this._applyColorBlend(this.curFillColor, geometry.model.hasFillTransparency());
+ this._drawElements(gl.TRIANGLES, gId);
+ fillShader.unbindShader();
+ }
+ if (!this.geometryBuilder && this._doStroke && geometry.lineVertexCount > 0) {
+ this._useLineColor = geometry.model.vertexStrokeColors.length > 0;
+ var strokeShader = this._getRetainedStrokeShader();
+ this._setStrokeUniforms(strokeShader);
+ var _iteratorNormalCompletion3 = true;
+ var _didIteratorError3 = false;
+ var _iteratorError3 = undefined;
+ try {
+ for (var _iterator3 = this.retainedMode.buffers.stroke[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
+ var _buff = _step3.value;
+ _buff._prepareBuffer(geometry, strokeShader);
+ }
+ } catch (err) {
+ _didIteratorError3 = true;
+ _iteratorError3 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
+ _iterator3.return();
+ }
+ } finally {
+ if (_didIteratorError3) {
+ throw _iteratorError3;
+ }
+ }
+ }
+ strokeShader.disableRemainingAttributes();
+ this._applyColorBlend(this.curStrokeColor, geometry.model.hasStrokeTransparency());
+ this._drawArrays(gl.TRIANGLES, gId);
+ strokeShader.unbindShader();
+ }
+ if (this.geometryBuilder) {
+ this.geometryBuilder.addRetained(geometry);
+ }
+ return this;
+ };
+ /**
+ * Calls drawBuffers() with a scaled model/view matrix.
+ *
+ * This is used by various 3d primitive methods (in primitives.js, eg. plane,
+ * box, torus, etc...) to allow caching of un-scaled geometries. Those
+ * geometries are generally created with unit-length dimensions, cached as
+ * such, and then scaled appropriately in this method prior to rendering.
+ *
+ * @private
+ * @method drawBuffersScaled
+ * @param {String} gId ID in our geom hash
+ * @param {Number} scaleX the amount to scale in the X direction
+ * @param {Number} scaleY the amount to scale in the Y direction
+ * @param {Number} scaleZ the amount to scale in the Z direction
+ */
+ _main.default.RendererGL.prototype.drawBuffersScaled = function (gId, scaleX, scaleY, scaleZ) {
+ var uMVMatrix = this.uMVMatrix.copy();
+ try {
+ this.uMVMatrix.scale(scaleX, scaleY, scaleZ);
+ this.drawBuffers(gId);
+ } finally {
+ this.uMVMatrix = uMVMatrix;
+ }
+ };
+ _main.default.RendererGL.prototype._drawArrays = function (drawMode, gId) {
+ this.GL.drawArrays(drawMode, 0, this.retainedMode.geometry[gId].lineVertexCount);
+ return this;
+ };
+ _main.default.RendererGL.prototype._drawElements = function (drawMode, gId) {
+ var buffers = this.retainedMode.geometry[gId];
+ var gl = this.GL;
+ // render the fill
+ if (buffers.indexBuffer) {
+ // If this model is using a Uint32Array we need to ensure the
+ // OES_element_index_uint WebGL extension is enabled.
+ if (this._pInst.webglVersion !== constants.WEBGL2 && buffers.indexBufferType === gl.UNSIGNED_INT) {
+ if (!gl.getExtension('OES_element_index_uint')) {
+ throw new Error('Unable to render a 3d model with > 65535 triangles. Your web browser does not support the WebGL Extension OES_element_index_uint.');
+ }
+ } // we're drawing faces
+
+ gl.drawElements(gl.TRIANGLES, buffers.vertexCount, buffers.indexBufferType, 0);
+ } else {
+ // drawing vertices
+ gl.drawArrays(drawMode || gl.TRIANGLES, 0, buffers.vertexCount);
+ }
+ };
+ _main.default.RendererGL.prototype._drawPoints = function (vertices, vertexBuffer) {
+ var gl = this.GL;
+ var pointShader = this._getImmediatePointShader();
+ this._setPointUniforms(pointShader);
+ this._bindBuffer(vertexBuffer, gl.ARRAY_BUFFER, this._vToNArray(vertices), Float32Array, gl.STATIC_DRAW);
+ pointShader.enableAttrib(pointShader.attributes.aPosition, 3);
+ this._applyColorBlend(this.curStrokeColor);
+ gl.drawArrays(gl.Points, 0, vertices.length);
+ pointShader.unbindShader();
+ };
+ var _default = _main.default.RendererGL;
+ exports.default = _default;
+ },
+ {
+ '../core/constants': 291,
+ '../core/main': 303,
+ './p5.RenderBuffer': 357,
+ './p5.RendererGL': 360,
+ 'core-js/modules/es.array.fill': 173,
+ 'core-js/modules/es.array.iterator': 182,
+ 'core-js/modules/es.array.some': 187,
+ 'core-js/modules/es.object.get-own-property-descriptor': 201,
+ 'core-js/modules/es.object.keys': 204,
+ 'core-js/modules/es.object.to-string': 205,
+ 'core-js/modules/es.string.iterator': 215,
+ 'core-js/modules/es.symbol': 227,
+ 'core-js/modules/es.symbol.description': 225,
+ 'core-js/modules/es.symbol.iterator': 226,
+ 'core-js/modules/es.typed-array.copy-within': 228,
+ 'core-js/modules/es.typed-array.every': 229,
+ 'core-js/modules/es.typed-array.fill': 230,
+ 'core-js/modules/es.typed-array.filter': 231,
+ 'core-js/modules/es.typed-array.find': 233,
+ 'core-js/modules/es.typed-array.find-index': 232,
+ 'core-js/modules/es.typed-array.float32-array': 234,
+ 'core-js/modules/es.typed-array.for-each': 236,
+ 'core-js/modules/es.typed-array.includes': 237,
+ 'core-js/modules/es.typed-array.index-of': 238,
+ 'core-js/modules/es.typed-array.iterator': 241,
+ 'core-js/modules/es.typed-array.join': 242,
+ 'core-js/modules/es.typed-array.last-index-of': 243,
+ 'core-js/modules/es.typed-array.map': 244,
+ 'core-js/modules/es.typed-array.reduce': 246,
+ 'core-js/modules/es.typed-array.reduce-right': 245,
+ 'core-js/modules/es.typed-array.reverse': 247,
+ 'core-js/modules/es.typed-array.set': 248,
+ 'core-js/modules/es.typed-array.slice': 249,
+ 'core-js/modules/es.typed-array.some': 250,
+ 'core-js/modules/es.typed-array.sort': 251,
+ 'core-js/modules/es.typed-array.subarray': 252,
+ 'core-js/modules/es.typed-array.to-locale-string': 253,
+ 'core-js/modules/es.typed-array.to-string': 254,
+ 'core-js/modules/es.typed-array.uint16-array': 255,
+ 'core-js/modules/es.typed-array.uint32-array': 256,
+ 'core-js/modules/es.weak-map': 259,
+ 'core-js/modules/web.dom-collections.iterator': 261
+ }
+ ],
+ 360: [
+ function (_dereq_, module, exports) {
+ 'use strict';
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.concat');
+ _dereq_('core-js/modules/es.array.copy-within');
+ _dereq_('core-js/modules/es.array.every');
+ _dereq_('core-js/modules/es.array.fill');
+ _dereq_('core-js/modules/es.array.flat');
+ _dereq_('core-js/modules/es.array.flat-map');
+ _dereq_('core-js/modules/es.array.from');
+ _dereq_('core-js/modules/es.array.includes');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.array.map');
+ _dereq_('core-js/modules/es.array.slice');
+ _dereq_('core-js/modules/es.array.some');
+ _dereq_('core-js/modules/es.array.unscopables.flat');
+ _dereq_('core-js/modules/es.array.unscopables.flat-map');
+ _dereq_('core-js/modules/es.map');
+ _dereq_('core-js/modules/es.object.assign');
+ _dereq_('core-js/modules/es.object.get-own-property-descriptor');
+ _dereq_('core-js/modules/es.object.get-prototype-of');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.reflect.construct');
+ _dereq_('core-js/modules/es.reflect.get');
+ _dereq_('core-js/modules/es.regexp.to-string');
+ _dereq_('core-js/modules/es.set');
+ _dereq_('core-js/modules/es.string.includes');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/es.typed-array.float32-array');
+ _dereq_('core-js/modules/es.typed-array.float64-array');
+ _dereq_('core-js/modules/es.typed-array.int16-array');
+ _dereq_('core-js/modules/es.typed-array.uint8-array');
+ _dereq_('core-js/modules/es.typed-array.uint16-array');
+ _dereq_('core-js/modules/es.typed-array.uint32-array');
+ _dereq_('core-js/modules/es.typed-array.copy-within');
+ _dereq_('core-js/modules/es.typed-array.every');
+ _dereq_('core-js/modules/es.typed-array.fill');
+ _dereq_('core-js/modules/es.typed-array.filter');
+ _dereq_('core-js/modules/es.typed-array.find');
+ _dereq_('core-js/modules/es.typed-array.find-index');
+ _dereq_('core-js/modules/es.typed-array.for-each');
+ _dereq_('core-js/modules/es.typed-array.includes');
+ _dereq_('core-js/modules/es.typed-array.index-of');
+ _dereq_('core-js/modules/es.typed-array.iterator');
+ _dereq_('core-js/modules/es.typed-array.join');
+ _dereq_('core-js/modules/es.typed-array.last-index-of');
+ _dereq_('core-js/modules/es.typed-array.map');
+ _dereq_('core-js/modules/es.typed-array.reduce');
+ _dereq_('core-js/modules/es.typed-array.reduce-right');
+ _dereq_('core-js/modules/es.typed-array.reverse');
+ _dereq_('core-js/modules/es.typed-array.set');
+ _dereq_('core-js/modules/es.typed-array.slice');
+ _dereq_('core-js/modules/es.typed-array.some');
+ _dereq_('core-js/modules/es.typed-array.sort');
+ _dereq_('core-js/modules/es.typed-array.subarray');
+ _dereq_('core-js/modules/es.typed-array.to-locale-string');
+ _dereq_('core-js/modules/es.typed-array.to-string');
+ _dereq_('core-js/modules/es.weak-map');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ function _typeof2(obj) {
+ if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') {
+ _typeof2 = function _typeof2(obj) {
+ return typeof obj;
+ };
+ } else {
+ _typeof2 = function _typeof2(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
+ };
+ }
+ return _typeof2(obj);
+ }
+ function _typeof(obj) {
+ if (typeof Symbol === 'function' && _typeof2(Symbol.iterator) === 'symbol') {
+ _typeof = function _typeof(obj) {
+ return _typeof2(obj);
+ };
+ } else {
+ _typeof = function _typeof(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : _typeof2(obj);
+ };
+ }
+ return _typeof(obj);
+ }
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.concat');
+ _dereq_('core-js/modules/es.array.copy-within');
+ _dereq_('core-js/modules/es.array.every');
+ _dereq_('core-js/modules/es.array.fill');
+ _dereq_('core-js/modules/es.array.flat');
+ _dereq_('core-js/modules/es.array.flat-map');
+ _dereq_('core-js/modules/es.array.from');
+ _dereq_('core-js/modules/es.array.includes');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.array.map');
+ _dereq_('core-js/modules/es.array.slice');
+ _dereq_('core-js/modules/es.array.some');
+ _dereq_('core-js/modules/es.array.unscopables.flat');
+ _dereq_('core-js/modules/es.array.unscopables.flat-map');
+ _dereq_('core-js/modules/es.map');
+ _dereq_('core-js/modules/es.object.assign');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.set');
+ _dereq_('core-js/modules/es.string.includes');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/es.typed-array.float32-array');
+ _dereq_('core-js/modules/es.typed-array.float64-array');
+ _dereq_('core-js/modules/es.typed-array.int16-array');
+ _dereq_('core-js/modules/es.typed-array.uint8-array');
+ _dereq_('core-js/modules/es.typed-array.uint16-array');
+ _dereq_('core-js/modules/es.typed-array.uint32-array');
+ _dereq_('core-js/modules/es.typed-array.copy-within');
+ _dereq_('core-js/modules/es.typed-array.every');
+ _dereq_('core-js/modules/es.typed-array.fill');
+ _dereq_('core-js/modules/es.typed-array.filter');
+ _dereq_('core-js/modules/es.typed-array.find');
+ _dereq_('core-js/modules/es.typed-array.find-index');
+ _dereq_('core-js/modules/es.typed-array.for-each');
+ _dereq_('core-js/modules/es.typed-array.includes');
+ _dereq_('core-js/modules/es.typed-array.index-of');
+ _dereq_('core-js/modules/es.typed-array.iterator');
+ _dereq_('core-js/modules/es.typed-array.join');
+ _dereq_('core-js/modules/es.typed-array.last-index-of');
+ _dereq_('core-js/modules/es.typed-array.map');
+ _dereq_('core-js/modules/es.typed-array.reduce');
+ _dereq_('core-js/modules/es.typed-array.reduce-right');
+ _dereq_('core-js/modules/es.typed-array.reverse');
+ _dereq_('core-js/modules/es.typed-array.set');
+ _dereq_('core-js/modules/es.typed-array.slice');
+ _dereq_('core-js/modules/es.typed-array.some');
+ _dereq_('core-js/modules/es.typed-array.sort');
+ _dereq_('core-js/modules/es.typed-array.subarray');
+ _dereq_('core-js/modules/es.typed-array.to-locale-string');
+ _dereq_('core-js/modules/es.typed-array.to-string');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ Object.defineProperty(exports, '__esModule', {
+ value: true
+ });
+ exports.readPixelsWebGL = readPixelsWebGL;
+ exports.readPixelWebGL = readPixelWebGL;
+ exports.default = void 0;
+ var _main = _interopRequireDefault(_dereq_('../core/main'));
+ var constants = _interopRequireWildcard(_dereq_('../core/constants'));
+ var _GeometryBuilder = _interopRequireDefault(_dereq_('./GeometryBuilder'));
+ var _libtess = _interopRequireDefault(_dereq_('libtess'));
+ _dereq_('./p5.Shader');
+ _dereq_('./p5.Camera');
+ _dereq_('../core/p5.Renderer');
+ _dereq_('./p5.Matrix');
+ _dereq_('./p5.Framebuffer');
+ var _path = _dereq_('path');
+ var _p6 = _dereq_('./p5.Texture');
+ var _filterShaderFrags;
+ function _getRequireWildcardCache() {
+ if (typeof WeakMap !== 'function') return null;
+ var cache = new WeakMap();
+ _getRequireWildcardCache = function _getRequireWildcardCache() {
+ return cache;
+ };
+ return cache;
+ }
+ function _interopRequireWildcard(obj) {
+ if (obj && obj.__esModule) {
+ return obj;
+ }
+ if (obj === null || _typeof(obj) !== 'object' && typeof obj !== 'function') {
+ return {
+ default:
+ obj
+ };
+ }
+ var cache = _getRequireWildcardCache();
+ if (cache && cache.has(obj)) {
+ return cache.get(obj);
+ }
+ var newObj = {
+ };
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
+ if (desc && (desc.get || desc.set)) {
+ Object.defineProperty(newObj, key, desc);
+ } else {
+ newObj[key] = obj[key];
+ }
+ }
+ }
+ newObj.default = obj;
+ if (cache) {
+ cache.set(obj, newObj);
+ }
+ return newObj;
+ }
+ function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : {
+ default:
+ obj
+ };
+ }
+ function _toConsumableArray(arr) {
+ return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
+ }
+ function _nonIterableSpread() {
+ throw new TypeError('Invalid attempt to spread non-iterable instance');
+ }
+ function _iterableToArray(iter) {
+ if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === '[object Arguments]') return Array.from(iter);
+ }
+ function _arrayWithoutHoles(arr) {
+ if (Array.isArray(arr)) {
+ for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
+ arr2[i] = arr[i];
+ }
+ return arr2;
+ }
+ }
+ function _classCallCheck(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError('Cannot call a class as a function');
+ }
+ }
+ function _defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ('value' in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+ function _createClass(Constructor, protoProps, staticProps) {
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) _defineProperties(Constructor, staticProps);
+ return Constructor;
+ }
+ function _get(target, property, receiver) {
+ if (typeof Reflect !== 'undefined' && Reflect.get) {
+ _get = Reflect.get;
+ } else {
+ _get = function _get(target, property, receiver) {
+ var base = _superPropBase(target, property);
+ if (!base) return;
+ var desc = Object.getOwnPropertyDescriptor(base, property);
+ if (desc.get) {
+ return desc.get.call(receiver);
+ }
+ return desc.value;
+ };
+ }
+ return _get(target, property, receiver || target);
+ }
+ function _superPropBase(object, property) {
+ while (!Object.prototype.hasOwnProperty.call(object, property)) {
+ object = _getPrototypeOf(object);
+ if (object === null) break;
+ }
+ return object;
+ }
+ function _inherits(subClass, superClass) {
+ if (typeof superClass !== 'function' && superClass !== null) {
+ throw new TypeError('Super expression must either be null or a function');
+ }
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
+ constructor: {
+ value: subClass,
+ writable: true,
+ configurable: true
+ }
+ });
+ if (superClass) _setPrototypeOf(subClass, superClass);
+ }
+ function _setPrototypeOf(o, p) {
+ _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
+ o.__proto__ = p;
+ return o;
+ };
+ return _setPrototypeOf(o, p);
+ }
+ function _createSuper(Derived) {
+ function isNativeReflectConstruct() {
+ if (typeof Reflect === 'undefined' || !Reflect.construct) return false;
+ if (Reflect.construct.sham) return false;
+ if (typeof Proxy === 'function') return true;
+ try {
+ Date.prototype.toString.call(Reflect.construct(Date, [
+ ], function () {
+ }));
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+ return function () {
+ var Super = _getPrototypeOf(Derived),
+ result;
+ if (isNativeReflectConstruct()) {
+ var NewTarget = _getPrototypeOf(this).constructor;
+ result = Reflect.construct(Super, arguments, NewTarget);
+ } else {
+ result = Super.apply(this, arguments);
+ }
+ return _possibleConstructorReturn(this, result);
+ };
+ }
+ function _possibleConstructorReturn(self, call) {
+ if (call && (_typeof(call) === 'object' || typeof call === 'function')) {
+ return call;
+ }
+ return _assertThisInitialized(self);
+ }
+ function _assertThisInitialized(self) {
+ if (self === void 0) {
+ throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');
+ }
+ return self;
+ }
+ function _getPrototypeOf(o) {
+ _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
+ return o.__proto__ || Object.getPrototypeOf(o);
+ };
+ return _getPrototypeOf(o);
+ }
+ function _defineProperty(obj, key, value) {
+ if (key in obj) {
+ Object.defineProperty(obj, key, {
+ value: value,
+ enumerable: true,
+ configurable: true,
+ writable: true
+ });
+ } else {
+ obj[key] = value;
+ }
+ return obj;
+ }
+ var STROKE_CAP_ENUM = {
+ };
+ var STROKE_JOIN_ENUM = {
+ };
+ var lineDefs = '';
+ var defineStrokeCapEnum = function defineStrokeCapEnum(key, val) {
+ lineDefs += '#define STROKE_CAP_'.concat(key, ' ').concat(val, '\n');
+ STROKE_CAP_ENUM[constants[key]] = val;
+ };
+ var defineStrokeJoinEnum = function defineStrokeJoinEnum(key, val) {
+ lineDefs += '#define STROKE_JOIN_'.concat(key, ' ').concat(val, '\n');
+ STROKE_JOIN_ENUM[constants[key]] = val;
+ };
+ // Define constants in line shaders for each type of cap/join, and also record
+ // the values in JS objects
+ defineStrokeCapEnum('ROUND', 0);
+ defineStrokeCapEnum('PROJECT', 1);
+ defineStrokeCapEnum('SQUARE', 2);
+ defineStrokeJoinEnum('ROUND', 0);
+ defineStrokeJoinEnum('MITER', 1);
+ defineStrokeJoinEnum('BEVEL', 2);
+ var lightingShader = '#define PI 3.141592\n\nprecision highp float;\nprecision highp int;\n\nuniform mat4 uViewMatrix;\n\nuniform bool uUseLighting;\n\nuniform int uAmbientLightCount;\nuniform vec3 uAmbientColor[5];\nuniform mat3 uCameraRotation;\nuniform int uDirectionalLightCount;\nuniform vec3 uLightingDirection[5];\nuniform vec3 uDirectionalDiffuseColors[5];\nuniform vec3 uDirectionalSpecularColors[5];\n\nuniform int uPointLightCount;\nuniform vec3 uPointLightLocation[5];\nuniform vec3 uPointLightDiffuseColors[5];\t\nuniform vec3 uPointLightSpecularColors[5];\n\nuniform int uSpotLightCount;\nuniform float uSpotLightAngle[5];\nuniform float uSpotLightConc[5];\nuniform vec3 uSpotLightDiffuseColors[5];\nuniform vec3 uSpotLightSpecularColors[5];\nuniform vec3 uSpotLightLocation[5];\nuniform vec3 uSpotLightDirection[5];\n\nuniform bool uSpecular;\nuniform float uShininess;\nuniform float metallic;\n\nuniform float uConstantAttenuation;\nuniform float uLinearAttenuation;\nuniform float uQuadraticAttenuation;\n\n// setting from _setImageLightUniforms()\n// boolean to initiate the calculateImageDiffuse and calculateImageSpecular\nuniform bool uUseImageLight;\n// texture for use in calculateImageDiffuse\nuniform sampler2D environmentMapDiffused;\n// texture for use in calculateImageSpecular\nuniform sampler2D environmentMapSpecular;\n// roughness for use in calculateImageSpecular\nuniform float levelOfDetail;\n\nconst float specularFactor = 2.0;\nconst float diffuseFactor = 0.73;\n\nstruct LightResult {\n float specular;\n float diffuse;\n};\n\nfloat _phongSpecular(\n vec3 lightDirection,\n vec3 viewDirection,\n vec3 surfaceNormal,\n float shininess) {\n\n vec3 R = reflect(lightDirection, surfaceNormal);\n return pow(max(0.0, dot(R, viewDirection)), shininess);\n}\n\nfloat _lambertDiffuse(vec3 lightDirection, vec3 surfaceNormal) {\n return max(0.0, dot(-lightDirection, surfaceNormal));\n}\n\nLightResult _light(vec3 viewDirection, vec3 normal, vec3 lightVector) {\n\n vec3 lightDir = normalize(lightVector);\n\n //compute our diffuse & specular terms\n LightResult lr;\n float specularIntensity = mix(1.0, 0.4, metallic);\n float diffuseIntensity = mix(1.0, 0.1, metallic);\n if (uSpecular)\n lr.specular = (_phongSpecular(lightDir, viewDirection, normal, uShininess)) * specularIntensity;\n lr.diffuse = _lambertDiffuse(lightDir, normal) * diffuseIntensity;\n return lr;\n}\n\n// converts the range of "value" from [min1 to max1] to [min2 to max2]\nfloat map(float value, float min1, float max1, float min2, float max2) {\n return min2 + (value - min1) * (max2 - min2) / (max1 - min1);\n}\n\nvec2 mapTextureToNormal( vec3 v ){\n // x = r sin(phi) cos(theta) \n // y = r cos(phi) \n // z = r sin(phi) sin(theta)\n float phi = acos( v.y );\n // if phi is 0, then there are no x, z components\n float theta = 0.0;\n // else \n theta = acos(v.x / sin(phi));\n float sinTheta = v.z / sin(phi);\n if (sinTheta < 0.0) {\n // Turn it into -theta, but in the 0-2PI range\n theta = 2.0 * PI - theta;\n }\n theta = theta / (2.0 * 3.14159);\n phi = phi / 3.14159 ;\n \n vec2 angles = vec2( fract(theta + 0.25), 1.0 - phi );\n return angles;\n}\n\n\nvec3 calculateImageDiffuse( vec3 vNormal, vec3 vViewPosition ){\n // make 2 seperate builds \n vec3 worldCameraPosition = vec3(0.0, 0.0, 0.0); // hardcoded world camera position\n vec3 worldNormal = normalize(vNormal * uCameraRotation);\n vec2 newTexCoor = mapTextureToNormal( worldNormal );\n vec4 texture = TEXTURE( environmentMapDiffused, newTexCoor );\n // this is to make the darker sections more dark\n // png and jpg usually flatten the brightness so it is to reverse that\n return mix(smoothstep(vec3(0.0), vec3(1.0), texture.xyz), vec3(0.0), metallic);\n}\n\nvec3 calculateImageSpecular( vec3 vNormal, vec3 vViewPosition ){\n vec3 worldCameraPosition = vec3(0.0, 0.0, 0.0);\n vec3 worldNormal = normalize(vNormal);\n vec3 lightDirection = normalize( vViewPosition - worldCameraPosition );\n vec3 R = reflect(lightDirection, worldNormal) * uCameraRotation;\n vec2 newTexCoor = mapTextureToNormal( R );\n#ifdef WEBGL2\n vec4 outColor = textureLod(environmentMapSpecular, newTexCoor, levelOfDetail);\n#else\n vec4 outColor = TEXTURE(environmentMapSpecular, newTexCoor);\n#endif\n // this is to make the darker sections more dark\n // png and jpg usually flatten the brightness so it is to reverse that\n return mix(\n pow(outColor.xyz, vec3(10)),\n pow(outColor.xyz, vec3(1.2)),\n metallic \n );\n}\n\nvoid totalLight(\n vec3 modelPosition,\n vec3 normal,\n out vec3 totalDiffuse,\n out vec3 totalSpecular\n) {\n\n totalSpecular = vec3(0.0);\n\n if (!uUseLighting) {\n totalDiffuse = vec3(1.0);\n return;\n }\n\n totalDiffuse = vec3(0.0);\n\n vec3 viewDirection = normalize(-modelPosition);\n\n for (int j = 0; j < 5; j++) {\n if (j < uDirectionalLightCount) {\n vec3 lightVector = (uViewMatrix * vec4(uLightingDirection[j], 0.0)).xyz;\n vec3 lightColor = uDirectionalDiffuseColors[j];\n vec3 specularColor = uDirectionalSpecularColors[j];\n LightResult result = _light(viewDirection, normal, lightVector);\n totalDiffuse += result.diffuse * lightColor;\n totalSpecular += result.specular * lightColor * specularColor;\n }\n\n if (j < uPointLightCount) {\n vec3 lightPosition = (uViewMatrix * vec4(uPointLightLocation[j], 1.0)).xyz;\n vec3 lightVector = modelPosition - lightPosition;\n //calculate attenuation\n float lightDistance = length(lightVector);\n float lightFalloff = 1.0 / (uConstantAttenuation + lightDistance * uLinearAttenuation + (lightDistance * lightDistance) * uQuadraticAttenuation);\n vec3 lightColor = lightFalloff * uPointLightDiffuseColors[j];\n vec3 specularColor = lightFalloff * uPointLightSpecularColors[j];\n\n LightResult result = _light(viewDirection, normal, lightVector);\n totalDiffuse += result.diffuse * lightColor;\n totalSpecular += result.specular * lightColor * specularColor;\n }\n\n if(j < uSpotLightCount) {\n vec3 lightPosition = (uViewMatrix * vec4(uSpotLightLocation[j], 1.0)).xyz;\n vec3 lightVector = modelPosition - lightPosition;\n \n float lightDistance = length(lightVector);\n float lightFalloff = 1.0 / (uConstantAttenuation + lightDistance * uLinearAttenuation + (lightDistance * lightDistance) * uQuadraticAttenuation);\n\n vec3 lightDirection = (uViewMatrix * vec4(uSpotLightDirection[j], 0.0)).xyz;\n float spotDot = dot(normalize(lightVector), normalize(lightDirection));\n float spotFalloff;\n if(spotDot < uSpotLightAngle[j]) {\n spotFalloff = 0.0;\n }\n else {\n spotFalloff = pow(spotDot, uSpotLightConc[j]);\n }\n lightFalloff *= spotFalloff;\n\n vec3 lightColor = uSpotLightDiffuseColors[j];\n vec3 specularColor = uSpotLightSpecularColors[j];\n \n LightResult result = _light(viewDirection, normal, lightVector);\n \n totalDiffuse += result.diffuse * lightColor * lightFalloff;\n totalSpecular += result.specular * lightColor * specularColor * lightFalloff;\n }\n }\n\n if( uUseImageLight ){\n totalDiffuse += calculateImageDiffuse(normal, modelPosition);\n totalSpecular += calculateImageSpecular(normal, modelPosition);\n }\n\n totalDiffuse *= diffuseFactor;\n totalSpecular *= specularFactor;\n}\n';
+ var webgl2CompatibilityShader = '#ifdef WEBGL2\n\n#define IN in\n#define OUT out\n\n#ifdef FRAGMENT_SHADER\nout vec4 outColor;\n#define OUT_COLOR outColor\n#endif\n#define TEXTURE texture\n\n#else\n\n#ifdef FRAGMENT_SHADER\n#define IN varying\n#else\n#define IN attribute\n#endif\n#define OUT varying\n#define TEXTURE texture2D\n\n#ifdef FRAGMENT_SHADER\n#define OUT_COLOR gl_FragColor\n#endif\n\n#endif\n';
+ var defaultShaders = {
+ sphereMappingFrag: '#define PI 3.141592\n\nprecision highp float;\n \nuniform sampler2D uSampler;\nuniform mat3 uNewNormalMatrix;\nuniform float uFovY;\nuniform float uAspect;\n\nvarying vec2 vTexCoord;\n \nvoid main() {\n float uFovX = uFovY * uAspect; \n vec4 newTexColor = texture2D(uSampler, vTexCoord);\n float angleY = mix(uFovY/2.0, -uFovY/2.0, vTexCoord.y);\n float angleX = mix(uFovX/2.0, -uFovX/2.0, vTexCoord.x);\n vec3 rotatedNormal = vec3( angleX, angleY, 1.0 );\n rotatedNormal = uNewNormalMatrix * normalize(rotatedNormal);\n vec2 suv;\n suv.y = 0.5 + 0.5 * (-rotatedNormal.y);\n suv.x = atan(rotatedNormal.z, rotatedNormal.x) / (2.0 * PI) + 0.5;\n newTexColor = texture2D(uSampler, suv.xy);\n gl_FragColor = newTexColor;\n}\n',
+ immediateVert: 'IN vec3 aPosition;\nIN vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform float uResolution;\nuniform float uPointSize;\n\nOUT vec4 vColor;\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition, 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n gl_PointSize = uPointSize;\n}\n',
+ vertexColorVert: 'IN vec3 aPosition;\nIN vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\n\nOUT vec4 vColor;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition, 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n}\n',
+ vertexColorFrag: 'IN vec4 vColor;\nvoid main(void) {\n OUT_COLOR = vec4(vColor.rgb, 1.) * vColor.a;\n}\n',
+ normalVert: 'IN vec3 aPosition;\nIN vec3 aNormal;\nIN vec2 aTexCoord;\nIN vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\n\nuniform vec4 uMaterialColor;\nuniform bool uUseVertexColor;\n\nOUT vec3 vVertexNormal;\nOUT highp vec2 vVertTexCoord;\nOUT vec4 vColor;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition, 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vVertexNormal = normalize(vec3( uNormalMatrix * aNormal ));\n vVertTexCoord = aTexCoord;\n vColor = (uUseVertexColor ? aVertexColor : uMaterialColor);\n}\n',
+ normalFrag: 'IN vec3 vVertexNormal;\nvoid main(void) {\n OUT_COLOR = vec4(vVertexNormal, 1.0);\n}\n',
+ basicFrag: 'IN vec4 vColor;\nvoid main(void) {\n OUT_COLOR = vec4(vColor.rgb, 1.) * vColor.a;\n}\n',
+ lightVert: lightingShader + '// include lighting.glgl\n\nIN vec3 aPosition;\nIN vec3 aNormal;\nIN vec2 aTexCoord;\nIN vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\n\nuniform bool uUseVertexColor;\nuniform vec4 uMaterialColor;\n\nOUT highp vec2 vVertTexCoord;\nOUT vec3 vDiffuseColor;\nOUT vec3 vSpecularColor;\nOUT vec4 vColor;\n\nvoid main(void) {\n\n vec4 viewModelPosition = uModelViewMatrix * vec4(aPosition, 1.0);\n gl_Position = uProjectionMatrix * viewModelPosition;\n\n vec3 vertexNormal = normalize(uNormalMatrix * aNormal);\n vVertTexCoord = aTexCoord;\n\n totalLight(viewModelPosition.xyz, vertexNormal, vDiffuseColor, vSpecularColor);\n\n for (int i = 0; i < 8; i++) {\n if (i < uAmbientLightCount) {\n vDiffuseColor += uAmbientColor[i];\n }\n }\n \n vColor = (uUseVertexColor ? aVertexColor : uMaterialColor);\n}\n',
+ lightTextureFrag: 'uniform vec4 uTint;\nuniform sampler2D uSampler;\nuniform bool isTexture;\nuniform bool uEmissive;\n\nIN highp vec2 vVertTexCoord;\nIN vec3 vDiffuseColor;\nIN vec3 vSpecularColor;\nIN vec4 vColor;\n\nvoid main(void) {\n if(uEmissive && !isTexture) {\n OUT_COLOR = vColor;\n }\n else {\n vec4 baseColor = isTexture\n // Textures come in with premultiplied alpha. To apply tint and still have\n // premultiplied alpha output, we need to multiply the RGB channels by the\n // tint RGB, and all channels by the tint alpha.\n ? TEXTURE(uSampler, vVertTexCoord) * vec4(uTint.rgb/255., 1.) * (uTint.a/255.)\n // Colors come in with unmultiplied alpha, so we need to multiply the RGB\n // channels by alpha to convert it to premultiplied alpha.\n : vec4(vColor.rgb * vColor.a, vColor.a);\n OUT_COLOR = vec4(baseColor.rgb * vDiffuseColor + vSpecularColor, baseColor.a);\n }\n}\n',
+ phongVert: 'precision highp int;\n\nIN vec3 aPosition;\nIN vec3 aNormal;\nIN vec2 aTexCoord;\nIN vec4 aVertexColor;\n\nuniform vec3 uAmbientColor[5];\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\nuniform int uAmbientLightCount;\n\nuniform bool uUseVertexColor;\nuniform vec4 uMaterialColor;\n\nOUT vec3 vNormal;\nOUT vec2 vTexCoord;\nOUT vec3 vViewPosition;\nOUT vec3 vAmbientColor;\nOUT vec4 vColor;\n\nvoid main(void) {\n\n vec4 viewModelPosition = uModelViewMatrix * vec4(aPosition, 1.0);\n\n // Pass varyings to fragment shader\n vViewPosition = viewModelPosition.xyz;\n gl_Position = uProjectionMatrix * viewModelPosition; \n\n vNormal = uNormalMatrix * aNormal;\n vTexCoord = aTexCoord;\n\n // TODO: this should be a uniform\n vAmbientColor = vec3(0.0);\n for (int i = 0; i < 5; i++) {\n if (i < uAmbientLightCount) {\n vAmbientColor += uAmbientColor[i];\n }\n }\n \n vColor = (uUseVertexColor ? aVertexColor : uMaterialColor);\n}\n',
+ phongFrag: lightingShader + '// include lighting.glsl\nprecision highp int;\n\nuniform bool uHasSetAmbient;\nuniform vec4 uSpecularMatColor;\nuniform vec4 uAmbientMatColor;\nuniform vec4 uEmissiveMatColor;\n\nuniform vec4 uTint;\nuniform sampler2D uSampler;\nuniform bool isTexture;\n\nIN vec3 vNormal;\nIN vec2 vTexCoord;\nIN vec3 vViewPosition;\nIN vec3 vAmbientColor;\nIN vec4 vColor;\n\nvoid main(void) {\n\n vec3 diffuse;\n vec3 specular;\n totalLight(vViewPosition, normalize(vNormal), diffuse, specular);\n\n // Calculating final color as result of all lights (plus emissive term).\n\n vec4 baseColor = isTexture\n // Textures come in with premultiplied alpha. To apply tint and still have\n // premultiplied alpha output, we need to multiply the RGB channels by the\n // tint RGB, and all channels by the tint alpha.\n ? TEXTURE(uSampler, vTexCoord) * vec4(uTint.rgb/255., 1.) * (uTint.a/255.)\n // Colors come in with unmultiplied alpha, so we need to multiply the RGB\n // channels by alpha to convert it to premultiplied alpha.\n : vec4(vColor.rgb * vColor.a, vColor.a);\n OUT_COLOR = vec4(diffuse * baseColor.rgb + \n vAmbientColor * (\n uHasSetAmbient ? uAmbientMatColor.rgb : baseColor.rgb\n ) + \n specular * uSpecularMatColor.rgb + \n uEmissiveMatColor.rgb, baseColor.a);\n}\n',
+ fontVert: 'IN vec3 aPosition;\nIN vec2 aTexCoord;\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\n\nuniform vec4 uGlyphRect;\nuniform float uGlyphOffset;\n\nOUT vec2 vTexCoord;\nOUT float w;\n\nvoid main() {\n vec4 positionVec4 = vec4(aPosition, 1.0);\n\n // scale by the size of the glyph\'s rectangle\n positionVec4.xy *= uGlyphRect.zw - uGlyphRect.xy;\n\n // Expand glyph bounding boxes by 1px on each side to give a bit of room\n // for antialiasing\n vec3 newOrigin = (uModelViewMatrix * vec4(0., 0., 0., 1.)).xyz;\n vec3 newDX = (uModelViewMatrix * vec4(1., 0., 0., 1.)).xyz;\n vec3 newDY = (uModelViewMatrix * vec4(0., 1., 0., 1.)).xyz;\n vec2 pixelScale = vec2(\n 1. / length(newOrigin - newDX),\n 1. / length(newOrigin - newDY)\n );\n vec2 offset = pixelScale * normalize(aTexCoord - vec2(0.5, 0.5)) * vec2(1., -1.);\n vec2 textureOffset = offset * (1. / vec2(\n uGlyphRect.z - uGlyphRect.x,\n uGlyphRect.w - uGlyphRect.y\n ));\n\n // move to the corner of the glyph\n positionVec4.xy += uGlyphRect.xy;\n\n // move to the letter\'s line offset\n positionVec4.x += uGlyphOffset;\n\n positionVec4.xy += offset;\n \n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vTexCoord = aTexCoord + textureOffset;\n w = gl_Position.w;\n}\n',
+ fontFrag: '#ifndef WEBGL2\n#extension GL_OES_standard_derivatives : enable\n#endif\n\n#if 0\n // simulate integer math using floats\n\t#define int float\n\t#define ivec2 vec2\n\t#define INT(x) float(x)\n\n\tint ifloor(float v) { return floor(v); }\n\tivec2 ifloor(vec2 v) { return floor(v); }\n\n#else\n // use native integer math\n\tprecision highp int;\n\t#define INT(x) x\n\n\tint ifloor(float v) { return int(v); }\n\tint ifloor(int v) { return v; }\n\tivec2 ifloor(vec2 v) { return ivec2(v); }\n\n#endif\n\nuniform sampler2D uSamplerStrokes;\nuniform sampler2D uSamplerRowStrokes;\nuniform sampler2D uSamplerRows;\nuniform sampler2D uSamplerColStrokes;\nuniform sampler2D uSamplerCols;\n\nuniform ivec2 uStrokeImageSize;\nuniform ivec2 uCellsImageSize;\nuniform ivec2 uGridImageSize;\n\nuniform ivec2 uGridOffset;\nuniform ivec2 uGridSize;\nuniform vec4 uMaterialColor;\n\nIN vec2 vTexCoord;\n\n// some helper functions\nint ROUND(float v) { return ifloor(v + 0.5); }\nivec2 ROUND(vec2 v) { return ifloor(v + 0.5); }\nfloat saturate(float v) { return clamp(v, 0.0, 1.0); }\nvec2 saturate(vec2 v) { return clamp(v, 0.0, 1.0); }\n\nint mul(float v1, int v2) {\n return ifloor(v1 * float(v2));\n}\n\nivec2 mul(vec2 v1, ivec2 v2) {\n return ifloor(v1 * vec2(v2) + 0.5);\n}\n\n// unpack a 16-bit integer from a float vec2\nint getInt16(vec2 v) {\n ivec2 iv = ROUND(v * 255.0);\n return iv.x * INT(128) + iv.y;\n}\n\nvec2 pixelScale;\nvec2 coverage = vec2(0.0);\nvec2 weight = vec2(0.5);\nconst float minDistance = 1.0/8192.0;\nconst float hardness = 1.05; // amount of antialias\n\n// the maximum number of curves in a glyph\nconst int N = INT(250);\n\n// retrieves an indexed pixel from a sampler\nvec4 getTexel(sampler2D sampler, int pos, ivec2 size) {\n int width = size.x;\n int y = ifloor(pos / width);\n int x = pos - y * width; // pos % width\n\n return TEXTURE(sampler, (vec2(x, y) + 0.5) / vec2(size));\n}\n\nvoid calulateCrossings(vec2 p0, vec2 p1, vec2 p2, out vec2 C1, out vec2 C2) {\n\n // get the coefficients of the quadratic in t\n vec2 a = p0 - p1 * 2.0 + p2;\n vec2 b = p0 - p1;\n vec2 c = p0 - vTexCoord;\n\n // found out which values of \'t\' it crosses the axes\n vec2 surd = sqrt(max(vec2(0.0), b * b - a * c));\n vec2 t1 = ((b - surd) / a).yx;\n vec2 t2 = ((b + surd) / a).yx;\n\n // approximate straight lines to avoid rounding errors\n if (abs(a.y) < 0.001)\n t1.x = t2.x = c.y / (2.0 * b.y);\n\n if (abs(a.x) < 0.001)\n t1.y = t2.y = c.x / (2.0 * b.x);\n\n // plug into quadratic formula to find the corrdinates of the crossings\n C1 = ((a * t1 - b * 2.0) * t1 + c) * pixelScale;\n C2 = ((a * t2 - b * 2.0) * t2 + c) * pixelScale;\n}\n\nvoid coverageX(vec2 p0, vec2 p1, vec2 p2) {\n\n vec2 C1, C2;\n calulateCrossings(p0, p1, p2, C1, C2);\n\n // determine on which side of the x-axis the points lie\n bool y0 = p0.y > vTexCoord.y;\n bool y1 = p1.y > vTexCoord.y;\n bool y2 = p2.y > vTexCoord.y;\n\n // could web be under the curve (after t1)?\n if (y1 ? !y2 : y0) {\n // add the coverage for t1\n coverage.x += saturate(C1.x + 0.5);\n // calculate the anti-aliasing for t1\n weight.x = min(weight.x, abs(C1.x));\n }\n\n // are we outside the curve (after t2)?\n if (y1 ? !y0 : y2) {\n // subtract the coverage for t2\n coverage.x -= saturate(C2.x + 0.5);\n // calculate the anti-aliasing for t2\n weight.x = min(weight.x, abs(C2.x));\n }\n}\n\n// this is essentially the same as coverageX, but with the axes swapped\nvoid coverageY(vec2 p0, vec2 p1, vec2 p2) {\n\n vec2 C1, C2;\n calulateCrossings(p0, p1, p2, C1, C2);\n\n bool x0 = p0.x > vTexCoord.x;\n bool x1 = p1.x > vTexCoord.x;\n bool x2 = p2.x > vTexCoord.x;\n\n if (x1 ? !x2 : x0) {\n coverage.y -= saturate(C1.y + 0.5);\n weight.y = min(weight.y, abs(C1.y));\n }\n\n if (x1 ? !x0 : x2) {\n coverage.y += saturate(C2.y + 0.5);\n weight.y = min(weight.y, abs(C2.y));\n }\n}\n\nvoid main() {\n\n // calculate the pixel scale based on screen-coordinates\n pixelScale = hardness / fwidth(vTexCoord);\n\n // which grid cell is this pixel in?\n ivec2 gridCoord = ifloor(vTexCoord * vec2(uGridSize));\n\n // intersect curves in this row\n {\n // the index into the row info bitmap\n int rowIndex = gridCoord.y + uGridOffset.y;\n // fetch the info texel\n vec4 rowInfo = getTexel(uSamplerRows, rowIndex, uGridImageSize);\n // unpack the rowInfo\n int rowStrokeIndex = getInt16(rowInfo.xy);\n int rowStrokeCount = getInt16(rowInfo.zw);\n\n for (int iRowStroke = INT(0); iRowStroke < N; iRowStroke++) {\n if (iRowStroke >= rowStrokeCount)\n break;\n\n // each stroke is made up of 3 points: the start and control point\n // and the start of the next curve.\n // fetch the indices of this pair of strokes:\n vec4 strokeIndices = getTexel(uSamplerRowStrokes, rowStrokeIndex++, uCellsImageSize);\n\n // unpack the stroke index\n int strokePos = getInt16(strokeIndices.xy);\n\n // fetch the two strokes\n vec4 stroke0 = getTexel(uSamplerStrokes, strokePos + INT(0), uStrokeImageSize);\n vec4 stroke1 = getTexel(uSamplerStrokes, strokePos + INT(1), uStrokeImageSize);\n\n // calculate the coverage\n coverageX(stroke0.xy, stroke0.zw, stroke1.xy);\n }\n }\n\n // intersect curves in this column\n {\n int colIndex = gridCoord.x + uGridOffset.x;\n vec4 colInfo = getTexel(uSamplerCols, colIndex, uGridImageSize);\n int colStrokeIndex = getInt16(colInfo.xy);\n int colStrokeCount = getInt16(colInfo.zw);\n \n for (int iColStroke = INT(0); iColStroke < N; iColStroke++) {\n if (iColStroke >= colStrokeCount)\n break;\n\n vec4 strokeIndices = getTexel(uSamplerColStrokes, colStrokeIndex++, uCellsImageSize);\n\n int strokePos = getInt16(strokeIndices.xy);\n vec4 stroke0 = getTexel(uSamplerStrokes, strokePos + INT(0), uStrokeImageSize);\n vec4 stroke1 = getTexel(uSamplerStrokes, strokePos + INT(1), uStrokeImageSize);\n coverageY(stroke0.xy, stroke0.zw, stroke1.xy);\n }\n }\n\n weight = saturate(1.0 - weight * 2.0);\n float distance = max(weight.x + weight.y, minDistance); // manhattan approx.\n float antialias = abs(dot(coverage, weight) / distance);\n float cover = min(abs(coverage.x), abs(coverage.y));\n OUT_COLOR = vec4(uMaterialColor.rgb, 1.) * uMaterialColor.a;\n OUT_COLOR *= saturate(max(antialias, cover));\n}\n',
+ lineVert: lineDefs + '/*\n Part of the Processing project - http://processing.org\n Copyright (c) 2012-15 The Processing Foundation\n Copyright (c) 2004-12 Ben Fry and Casey Reas\n Copyright (c) 2001-04 Massachusetts Institute of Technology\n This library is free software; you can redistribute it and/or\n modify it under the terms of the GNU Lesser General Public\n License as published by the Free Software Foundation, version 2.1.\n This library is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n Lesser General Public License for more details.\n You should have received a copy of the GNU Lesser General\n Public License along with this library; if not, write to the\n Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n Boston, MA 02111-1307 USA\n*/\n\n#define PROCESSING_LINE_SHADER\n\nprecision mediump int;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform float uStrokeWeight;\n\nuniform bool uUseLineColor;\nuniform vec4 uMaterialColor;\n\nuniform vec4 uViewport;\nuniform int uPerspective;\nuniform int uStrokeJoin;\n\nIN vec4 aPosition;\nIN vec3 aTangentIn;\nIN vec3 aTangentOut;\nIN float aSide;\nIN vec4 aVertexColor;\n\nOUT vec4 vColor;\nOUT vec2 vTangent;\nOUT vec2 vCenter;\nOUT vec2 vPosition;\nOUT float vMaxDist;\nOUT float vCap;\nOUT float vJoin;\n\nvec2 lineIntersection(vec2 aPoint, vec2 aDir, vec2 bPoint, vec2 bDir) {\n // Rotate and translate so a starts at the origin and goes out to the right\n bPoint -= aPoint;\n vec2 rotatedBFrom = vec2(\n bPoint.x*aDir.x + bPoint.y*aDir.y,\n bPoint.y*aDir.x - bPoint.x*aDir.y\n );\n vec2 bTo = bPoint + bDir;\n vec2 rotatedBTo = vec2(\n bTo.x*aDir.x + bTo.y*aDir.y,\n bTo.y*aDir.x - bTo.x*aDir.y\n );\n float intersectionDistance =\n rotatedBTo.x + (rotatedBFrom.x - rotatedBTo.x) * rotatedBTo.y /\n (rotatedBTo.y - rotatedBFrom.y);\n return aPoint + aDir * intersectionDistance;\n}\n\nvoid main() {\n // Caps have one of either the in or out tangent set to 0\n vCap = (aTangentIn == vec3(0.)) != (aTangentOut == (vec3(0.)))\n ? 1. : 0.;\n\n // Joins have two unique, defined tangents\n vJoin = (\n aTangentIn != vec3(0.) &&\n aTangentOut != vec3(0.) &&\n aTangentIn != aTangentOut\n ) ? 1. : 0.;\n\n vec4 posp = uModelViewMatrix * aPosition;\n vec4 posqIn = uModelViewMatrix * (aPosition + vec4(aTangentIn, 0));\n vec4 posqOut = uModelViewMatrix * (aPosition + vec4(aTangentOut, 0));\n\n float facingCamera = pow(\n // The word space tangent\'s z value is 0 if it\'s facing the camera\n abs(normalize(posqIn-posp).z),\n\n // Using pow() here to ramp `facingCamera` up from 0 to 1 really quickly\n // so most lines get scaled and don\'t get clipped\n 0.25\n );\n\n // using a scale <1 moves the lines towards the camera\n // in order to prevent popping effects due to half of\n // the line disappearing behind the geometry faces.\n float scale = mix(1., 0.995, facingCamera);\n\n // Moving vertices slightly toward the camera\n // to avoid depth-fighting with the fill triangles.\n // Discussed here:\n // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=252848 \n posp.xyz = posp.xyz * scale;\n posqIn.xyz = posqIn.xyz * scale;\n posqOut.xyz = posqOut.xyz * scale;\n\n vec4 p = uProjectionMatrix * posp;\n vec4 qIn = uProjectionMatrix * posqIn;\n vec4 qOut = uProjectionMatrix * posqOut;\n vCenter = p.xy;\n\n // formula to convert from clip space (range -1..1) to screen space (range 0..[width or height])\n // screen_p = (p.xy/p.w + <1,1>) * 0.5 * uViewport.zw\n\n // prevent division by W by transforming the tangent formula (div by 0 causes\n // the line to disappear, see https://github.com/processing/processing/issues/5183)\n // t = screen_q - screen_p\n //\n // tangent is normalized and we don\'t care which aDirection it points to (+-)\n // t = +- normalize( screen_q - screen_p )\n // t = +- normalize( (q.xy/q.w+<1,1>)*0.5*uViewport.zw - (p.xy/p.w+<1,1>)*0.5*uViewport.zw )\n //\n // extract common factor, <1,1> - <1,1> cancels out\n // t = +- normalize( (q.xy/q.w - p.xy/p.w) * 0.5 * uViewport.zw )\n //\n // convert to common divisor\n // t = +- normalize( ((q.xy*p.w - p.xy*q.w) / (p.w*q.w)) * 0.5 * uViewport.zw )\n //\n // remove the common scalar divisor/factor, not needed due to normalize and +-\n // (keep uViewport - can\'t remove because it has different components for x and y\n // and corrects for aspect ratio, see https://github.com/processing/processing/issues/5181)\n // t = +- normalize( (q.xy*p.w - p.xy*q.w) * uViewport.zw )\n\n vec2 tangentIn = normalize((qIn.xy*p.w - p.xy*qIn.w) * uViewport.zw);\n vec2 tangentOut = normalize((qOut.xy*p.w - p.xy*qOut.w) * uViewport.zw);\n\n vec2 curPerspScale;\n if(uPerspective == 1) {\n // Perspective ---\n // convert from world to clip by multiplying with projection scaling factor\n // to get the right thickness (see https://github.com/processing/processing/issues/5182)\n\n // The y value of the projection matrix may be flipped if rendering to a Framebuffer.\n // Multiplying again by its sign here negates the flip to get just the scale.\n curPerspScale = (uProjectionMatrix * vec4(1, sign(uProjectionMatrix[1][1]), 0, 0)).xy;\n } else {\n // No Perspective ---\n // multiply by W (to cancel out division by W later in the pipeline) and\n // convert from screen to clip (derived from clip to screen above)\n curPerspScale = p.w / (0.5 * uViewport.zw);\n }\n\n vec2 offset;\n if (vJoin == 1.) {\n vTangent = normalize(tangentIn + tangentOut);\n vec2 normalIn = vec2(-tangentIn.y, tangentIn.x);\n vec2 normalOut = vec2(-tangentOut.y, tangentOut.x);\n float side = sign(aSide);\n float sideEnum = abs(aSide);\n\n // We generate vertices for joins on either side of the centerline, but\n // the "elbow" side is the only one needing a join. By not setting the\n // offset for the other side, all its vertices will end up in the same\n // spot and not render, effectively discarding it.\n if (sign(dot(tangentOut, vec2(-tangentIn.y, tangentIn.x))) != side) {\n // Side enums:\n // 1: the side going into the join\n // 2: the middle of the join\n // 3: the side going out of the join\n if (sideEnum == 2.) {\n // Calculate the position + tangent on either side of the join, and\n // find where the lines intersect to find the elbow of the join\n vec2 c = (posp.xy/posp.w + vec2(1.,1.)) * 0.5 * uViewport.zw;\n vec2 intersection = lineIntersection(\n c + (side * normalIn * uStrokeWeight / 2.),\n tangentIn,\n c + (side * normalOut * uStrokeWeight / 2.),\n tangentOut\n );\n offset = (intersection - c);\n\n // When lines are thick and the angle of the join approaches 180, the\n // elbow might be really far from the center. We\'ll apply a limit to\n // the magnitude to avoid lines going across the whole screen when this\n // happens.\n float mag = length(offset);\n float maxMag = 3. * uStrokeWeight;\n if (mag > maxMag) {\n offset *= maxMag / mag;\n }\n } else if (sideEnum == 1.) {\n offset = side * normalIn * uStrokeWeight / 2.;\n } else if (sideEnum == 3.) {\n offset = side * normalOut * uStrokeWeight / 2.;\n }\n }\n if (uStrokeJoin == STROKE_JOIN_BEVEL) {\n vec2 avgNormal = vec2(-vTangent.y, vTangent.x);\n vMaxDist = abs(dot(avgNormal, normalIn * uStrokeWeight / 2.));\n } else {\n vMaxDist = uStrokeWeight / 2.;\n }\n } else {\n vec2 tangent = aTangentIn == vec3(0.) ? tangentOut : tangentIn;\n vTangent = tangent;\n vec2 normal = vec2(-tangent.y, tangent.x);\n\n float normalOffset = sign(aSide);\n // Caps will have side values of -2 or 2 on the edge of the cap that\n // extends out from the line\n float tangentOffset = abs(aSide) - 1.;\n offset = (normal * normalOffset + tangent * tangentOffset) *\n uStrokeWeight * 0.5;\n vMaxDist = uStrokeWeight / 2.;\n }\n vPosition = vCenter + offset;\n\n gl_Position.xy = p.xy + offset.xy * curPerspScale;\n gl_Position.zw = p.zw;\n \n vColor = (uUseLineColor ? aVertexColor : uMaterialColor);\n}\n',
+ lineFrag: lineDefs + 'precision mediump int;\n\nuniform vec4 uMaterialColor;\nuniform int uStrokeCap;\nuniform int uStrokeJoin;\nuniform float uStrokeWeight;\n\nIN vec4 vColor;\nIN vec2 vTangent;\nIN vec2 vCenter;\nIN vec2 vPosition;\nIN float vMaxDist;\nIN float vCap;\nIN float vJoin;\n\nfloat distSquared(vec2 a, vec2 b) {\n vec2 aToB = b - a;\n return dot(aToB, aToB);\n}\n\nvoid main() {\n if (vCap > 0.) {\n if (\n uStrokeCap == STROKE_CAP_ROUND &&\n distSquared(vPosition, vCenter) > uStrokeWeight * uStrokeWeight * 0.25\n ) {\n discard;\n } else if (\n uStrokeCap == STROKE_CAP_SQUARE &&\n dot(vPosition - vCenter, vTangent) > 0.\n ) {\n discard;\n }\n // Use full area for PROJECT\n } else if (vJoin > 0.) {\n if (\n uStrokeJoin == STROKE_JOIN_ROUND &&\n distSquared(vPosition, vCenter) > uStrokeWeight * uStrokeWeight * 0.25\n ) {\n discard;\n } else if (uStrokeJoin == STROKE_JOIN_BEVEL) {\n vec2 normal = vec2(-vTangent.y, vTangent.x);\n if (abs(dot(vPosition - vCenter, normal)) > vMaxDist) {\n discard;\n }\n }\n // Use full area for MITER\n }\n OUT_COLOR = vec4(vColor.rgb, 1.) * vColor.a;\n}\n',
+ pointVert: 'IN vec3 aPosition;\nuniform float uPointSize;\nOUT float vStrokeWeight;\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nvoid main() {\n\tvec4 positionVec4 = vec4(aPosition, 1.0);\n\tgl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n\tgl_PointSize = uPointSize;\n\tvStrokeWeight = uPointSize;\n}\n',
+ pointFrag: 'precision mediump int;\nuniform vec4 uMaterialColor;\nIN float vStrokeWeight;\n\nvoid main(){\n float mask = 0.0;\n\n // make a circular mask using the gl_PointCoord (goes from 0 - 1 on a point)\n // might be able to get a nicer edge on big strokeweights with smoothstep but slightly less performant\n\n mask = step(0.98, length(gl_PointCoord * 2.0 - 1.0));\n\n // if strokeWeight is 1 or less lets just draw a square\n // this prevents weird artifacting from carving circles when our points are really small\n // if strokeWeight is larger than 1, we just use it as is\n\n mask = mix(0.0, mask, clamp(floor(vStrokeWeight - 0.5),0.0,1.0));\n\n // throw away the borders of the mask\n // otherwise we get weird alpha blending issues\n\n if(mask > 0.98){\n discard;\n }\n\n OUT_COLOR = vec4(uMaterialColor.rgb, 1.) * uMaterialColor.a;\n}\n',
+ imageLightVert: 'precision highp float;\nattribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nvarying vec3 localPos;\nvarying vec3 vWorldNormal;\nvarying vec3 vWorldPosition;\nvarying vec2 vTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\n\nvoid main() {\n // Multiply the position by the matrix.\n vec4 viewModelPosition = uModelViewMatrix * vec4(aPosition, 1.0);\n gl_Position = uProjectionMatrix * viewModelPosition; \n \n // orient the normals and pass to the fragment shader\n vWorldNormal = uNormalMatrix * aNormal;\n \n // send the view position to the fragment shader\n vWorldPosition = (uModelViewMatrix * vec4(aPosition, 1.0)).xyz;\n \n localPos = vWorldPosition;\n vTexCoord = aTexCoord;\n}\n\n\n/*\nin the vertex shader we\'ll compute the world position and world oriented normal of the vertices and pass those to the fragment shader as varyings.\n*/\n',
+ imageLightDiffusedFrag: 'precision highp float;\nvarying vec3 localPos;\n\n// the HDR cubemap converted (can be from an equirectangular environment map.)\nuniform sampler2D environmentMap;\nvarying vec2 vTexCoord;\n\nconst float PI = 3.14159265359;\n\nvec2 nTOE( vec3 v ){\n // x = r sin(phi) cos(theta) \n // y = r cos(phi) \n // z = r sin(phi) sin(theta)\n float phi = acos( v.y );\n // if phi is 0, then there are no x, z components\n float theta = 0.0;\n // else \n theta = acos(v.x / sin(phi));\n float sinTheta = v.z / sin(phi);\n if (sinTheta < 0.0) {\n // Turn it into -theta, but in the 0-2PI range\n theta = 2.0 * PI - theta;\n }\n theta = theta / (2.0 * 3.14159);\n phi = phi / 3.14159 ;\n \n vec2 angles = vec2( phi, theta );\n return angles;\n}\n\nfloat random(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * .1031);\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nvoid main()\n{ \t \n\t// the sample direction equals the hemisphere\'s orientation\n float phi = vTexCoord.x * 2.0 * PI;\n float theta = vTexCoord.y * PI;\n float x = sin(theta) * cos(phi);\n float y = sin(theta) * sin(phi);\n float z = cos(theta);\n vec3 normal = vec3( x, y, z);\n\n\t// Discretely sampling the hemisphere given the integral\'s\n // spherical coordinates translates to the following fragment code:\n\tvec3 irradiance = vec3(0.0); \n\tvec3 up\t= vec3(0.0, 1.0, 0.0);\n\tvec3 right = normalize(cross(up, normal));\n\tup = normalize(cross(normal, right));\n\n\t// We specify a fixed sampleDelta delta value to traverse\n // the hemisphere; decreasing or increasing the sample delta\n // will increase or decrease the accuracy respectively.\n\tconst float sampleDelta = 0.100;\n\tfloat nrSamples = 0.0;\n float randomOffset = random(gl_FragCoord.xy) * sampleDelta;\n\tfor(float rawPhi = 0.0; rawPhi < 2.0 * PI; rawPhi += sampleDelta)\n\t{\n float phi = rawPhi + randomOffset;\n for(float rawTheta = 0.0; rawTheta < ( 0.5 ) * PI; rawTheta += sampleDelta)\n {\n float theta = rawTheta + randomOffset;\n // spherical to cartesian (in tangent space) // tangent space to world // add each sample result to irradiance\n float x = sin(theta) * cos(phi);\n float y = sin(theta) * sin(phi);\n float z = cos(theta);\n vec3 tangentSample = vec3( x, y, z);\n \n vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * normal;\n irradiance += (texture2D(environmentMap, nTOE(sampleVec)).xyz) * cos(theta) * sin(theta);\n nrSamples++;\n }\n\t}\n\t// divide by the total number of samples taken, giving us the average sampled irradiance.\n\tirradiance = PI * irradiance * (1.0 / float(nrSamples )) ;\n \n \n\tgl_FragColor = vec4(irradiance, 1.0);\n}',
+ imageLightSpecularFrag: 'precision highp float;\r\nvarying vec3 localPos;\r\nvarying vec2 vTexCoord;\r\n\r\n// our texture\r\nuniform sampler2D environmentMap;\r\nuniform float roughness;\r\n\r\nconst float PI = 3.14159265359;\r\n\r\nfloat VanDerCorput(int bits);\r\nvec2 HammersleyNoBitOps(int i, int N);\r\nvec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness);\r\n\r\n\r\nvec2 nTOE( vec3 v ){\r\n // x = r sin(phi) cos(theta) \r\n // y = r cos(phi) \r\n // z = r sin(phi) sin(theta)\r\n float phi = acos( v.y );\r\n // if phi is 0, then there are no x, z components\r\n float theta = 0.0;\r\n // else \r\n theta = acos(v.x / sin(phi));\r\n float sinTheta = v.z / sin(phi);\r\n if (sinTheta < 0.0) {\r\n // Turn it into -theta, but in the 0-2PI range\r\n theta = 2.0 * PI - theta;\r\n }\r\n theta = theta / (2.0 * 3.14159);\r\n phi = phi / 3.14159 ;\r\n \r\n vec2 angles = vec2( phi, theta );\r\n return angles;\r\n}\r\n\r\n\r\nvoid main(){\r\n const int SAMPLE_COUNT = 400; // 4096\r\n int lowRoughnessLimit = int(pow(2.0,(roughness+0.1)*20.0));\r\n float totalWeight = 0.0;\r\n vec3 prefilteredColor = vec3(0.0);\r\n float phi = vTexCoord.x * 2.0 * PI;\r\n float theta = vTexCoord.y * PI;\r\n float x = sin(theta) * cos(phi);\r\n float y = sin(theta) * sin(phi);\r\n float z = cos(theta);\r\n vec3 N = vec3(x,y,z);\r\n vec3 V = N;\r\n for (int i = 0; i < SAMPLE_COUNT; ++i)\r\n {\r\n // break at smaller sample numbers for low roughness levels\r\n if(i == lowRoughnessLimit)\r\n {\r\n break;\r\n }\r\n vec2 Xi = HammersleyNoBitOps(i, SAMPLE_COUNT);\r\n vec3 H = ImportanceSampleGGX(Xi, N, roughness);\r\n vec3 L = normalize(2.0 * dot(V, H) * H - V);\r\n\r\n float NdotL = max(dot(N, L), 0.0);\r\n if (NdotL > 0.0)\r\n {\r\n prefilteredColor += texture2D(environmentMap, nTOE(L)).xyz * NdotL;\r\n totalWeight += NdotL;\r\n }\r\n }\r\n prefilteredColor = prefilteredColor / totalWeight;\r\n\r\n gl_FragColor = vec4(prefilteredColor, 1.0);\r\n}\r\n\r\nvec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness){\r\n float a = roughness * roughness;\r\n\r\n float phi = 2.0 * PI * Xi.x;\r\n float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y));\r\n float sinTheta = sqrt(1.0 - cosTheta * cosTheta);\r\n // from spherical coordinates to cartesian coordinates\r\n vec3 H;\r\n H.x = cos(phi) * sinTheta;\r\n H.y = sin(phi) * sinTheta;\r\n H.z = cosTheta;\r\n\r\n // from tangent-space vector to world-space sample vector\r\n vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);\r\n vec3 tangent = normalize(cross(up, N));\r\n vec3 bitangent = cross(N, tangent);\r\n\r\n vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;\r\n return normalize(sampleVec);\r\n}\r\n\r\n\r\nfloat VanDerCorput(int n, int base)\r\n{\r\n#ifdef WEBGL2\r\n\r\n uint bits = uint(n);\r\n bits = (bits << 16u) | (bits >> 16u);\r\n bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);\r\n bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);\r\n bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);\r\n bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);\r\n return float(bits) * 2.3283064365386963e-10; // / 0x100000000\r\n\r\n#else\r\n\r\n float invBase = 1.0 / float(base);\r\n float denom = 1.0;\r\n float result = 0.0;\r\n\r\n\r\n for (int i = 0; i < 32; ++i)\r\n {\r\n if (n > 0)\r\n {\r\n denom = mod(float(n), 2.0);\r\n result += denom * invBase;\r\n invBase = invBase / 2.0;\r\n n = int(float(n) / 2.0);\r\n }\r\n }\r\n\r\n\r\n return result;\r\n\r\n#endif\r\n}\r\n\r\nvec2 HammersleyNoBitOps(int i, int N)\r\n{\r\n return vec2(float(i) / float(N), VanDerCorput(i, 2));\r\n}\r\n'
+ };
+ var sphereMapping = defaultShaders.sphereMappingFrag;
+ for (var key in defaultShaders) {
+ defaultShaders[key] = webgl2CompatibilityShader + defaultShaders[key];
+ }
+ var filterShaderFrags = (_filterShaderFrags = {
+ }, _defineProperty(_filterShaderFrags, constants.GRAY, 'precision highp float;\n\nvarying vec2 vTexCoord;\n\nuniform sampler2D tex0;\n\nfloat luma(vec3 color) {\n // weighted grayscale with luminance values\n return dot(color, vec3(0.2126, 0.7152, 0.0722));\n}\n\nvoid main() {\n vec4 tex = texture2D(tex0, vTexCoord);\n float gray = luma(tex.rgb);\n gl_FragColor = vec4(gray, gray, gray, tex.a);\n}\n'), _defineProperty(_filterShaderFrags, constants.ERODE, '// Reduces the bright areas in an image\n\nprecision highp float;\n\nvarying vec2 vTexCoord;\n\nuniform sampler2D tex0;\nuniform vec2 texelSize;\n\nfloat luma(vec3 color) {\n // weighted grayscale with luminance values\n // weights 77, 151, 28 taken from src/image/filters.js\n return dot(color, vec3(0.300781, 0.589844, 0.109375));\n}\n\nvoid main() {\n vec4 color = texture2D(tex0, vTexCoord);\n float lum = luma(color.rgb);\n\n // set current color as the darkest neighbor color\n\n vec4 neighbors[4];\n neighbors[0] = texture2D(tex0, vTexCoord + vec2( texelSize.x, 0.0));\n neighbors[1] = texture2D(tex0, vTexCoord + vec2(-texelSize.x, 0.0));\n neighbors[2] = texture2D(tex0, vTexCoord + vec2(0.0, texelSize.y));\n neighbors[3] = texture2D(tex0, vTexCoord + vec2(0.0, -texelSize.y));\n\n for (int i = 0; i < 4; i++) {\n vec4 neighborColor = neighbors[i];\n float neighborLum = luma(neighborColor.rgb);\n\n if (neighborLum < lum) {\n color = neighborColor;\n lum = neighborLum;\n }\n }\n\n gl_FragColor = color;\n}\n'), _defineProperty(_filterShaderFrags, constants.DILATE, '// Increase the bright areas in an image\n\nprecision highp float;\n\nvarying vec2 vTexCoord;\n\nuniform sampler2D tex0;\nuniform vec2 texelSize;\n\nfloat luma(vec3 color) {\n // weighted grayscale with luminance values\n // weights 77, 151, 28 taken from src/image/filters.js\n return dot(color, vec3(0.300781, 0.589844, 0.109375));\n}\n\nvoid main() {\n vec4 color = texture2D(tex0, vTexCoord);\n float lum = luma(color.rgb);\n\n // set current color as the brightest neighbor color\n\n vec4 neighbors[4];\n neighbors[0] = texture2D(tex0, vTexCoord + vec2( texelSize.x, 0.0));\n neighbors[1] = texture2D(tex0, vTexCoord + vec2(-texelSize.x, 0.0));\n neighbors[2] = texture2D(tex0, vTexCoord + vec2(0.0, texelSize.y));\n neighbors[3] = texture2D(tex0, vTexCoord + vec2(0.0, -texelSize.y));\n\n for (int i = 0; i < 4; i++) {\n vec4 neighborColor = neighbors[i];\n float neighborLum = luma(neighborColor.rgb);\n\n if (neighborLum > lum) {\n color = neighborColor;\n lum = neighborLum;\n }\n }\n\n gl_FragColor = color;\n}\n'), _defineProperty(_filterShaderFrags, constants.BLUR, 'precision highp float;\n\n// Two-pass blur filter, unweighted kernel.\n// See also a similar blur at Adam Ferriss\' repo of shader examples:\n// https://github.com/aferriss/p5jsShaderExamples/blob/gh-pages/4_image-effects/4-9_single-pass-blur/effect.frag\n\n\nuniform sampler2D tex0;\nvarying vec2 vTexCoord;\nuniform vec2 direction;\nuniform vec2 canvasSize;\nuniform float radius;\n\nfloat random(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * .1031);\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\n// This isn\'t a real Gaussian weight, it\'s a quadratic weight. It\'s what the\n// CPU mode\'s blur uses though, so we also use it here to match.\nfloat quadWeight(float x, float e) {\n return pow(e-abs(x), 2.);\n}\n\nvoid main(){\n vec2 uv = vTexCoord;\n\n // A reasonable maximum number of samples\n const float maxSamples = 64.0;\n\n float numSamples = floor(7. * radius);\n if (fract(numSamples / 2.) == 0.) {\n numSamples++;\n }\n vec4 avg = vec4(0.0);\n float total = 0.0;\n\n // Calculate the spacing to avoid skewing if numSamples > maxSamples\n float spacing = 1.0;\n if (numSamples > maxSamples) {\n spacing = numSamples / maxSamples;\n numSamples = maxSamples;\n }\n\n float randomOffset = (spacing - 1.0) * mix(-0.5, 0.5, random(gl_FragCoord.xy));\n for (float i = 0.0; i < maxSamples; i++) {\n if (i >= numSamples) break;\n\n float sample = i * spacing - (numSamples - 1.0) * 0.5 * spacing + randomOffset;\n vec2 sampleCoord = uv + vec2(sample, sample) / canvasSize * direction;\n float weight = quadWeight(sample, (numSamples - 1.0) * 0.5 * spacing);\n\n avg += weight * texture2D(tex0, sampleCoord);\n total += weight;\n }\n\n avg /= total;\n gl_FragColor = avg;\n}\n'), _defineProperty(_filterShaderFrags, constants.POSTERIZE, '// Limit color space for a stylized cartoon / poster effect\n\nprecision highp float;\n\nvarying vec2 vTexCoord;\n\nuniform sampler2D tex0;\nuniform float filterParameter;\n\nvec3 quantize(vec3 color, float n) {\n // restrict values to N options/bins\n // and floor each channel to nearest value\n //\n // eg. when N = 5, values = 0.0, 0.25, 0.50, 0.75, 1.0\n // then quantize (0.1, 0.7, 0.9) -> (0.0, 0.5, 1.0)\n\n color = color * n;\n color = floor(color);\n color = color / (n - 1.0);\n return color;\n}\n\nvoid main() {\n vec4 color = texture2D(tex0, vTexCoord);\n\n vec3 restrictedColor = quantize(color.rgb / color.a, filterParameter);\n\n gl_FragColor = vec4(restrictedColor.rgb * color.a, color.a);\n}\n'), _defineProperty(_filterShaderFrags, constants.OPAQUE, '// Set alpha channel to entirely opaque\n\nprecision highp float;\n\nvarying vec2 vTexCoord;\n\nuniform sampler2D tex0;\n\nvoid main() {\n vec4 color = texture2D(tex0, vTexCoord);\n gl_FragColor = vec4(color.rgb / color.a, 1.0);\n}\n'), _defineProperty(_filterShaderFrags, constants.INVERT, '// Set each pixel to inverse value\n// Note that original INVERT does not change the opacity, so this follows suit\n\nprecision highp float;\n\nvarying vec2 vTexCoord;\n\nuniform sampler2D tex0;\n\nvoid main() {\nvec4 color = texture2D(tex0, vTexCoord);\nvec3 origColor = color.rgb / color.a;\nvec3 invertedColor = vec3(1.0) - origColor;\ngl_FragColor = vec4(invertedColor * color.a, color.a);\n}\n'), _defineProperty(_filterShaderFrags, constants.THRESHOLD, '// Convert pixels to either white or black, \n// depending on if their luma is above or below filterParameter\n\nprecision highp float;\n\nvarying vec2 vTexCoord;\n\nuniform sampler2D tex0;\nuniform float filterParameter;\n\nfloat luma(vec3 color) {\n // weighted grayscale with luminance values\n return dot(color, vec3(0.2126, 0.7152, 0.0722));\n}\n\nvoid main() {\n vec4 color = texture2D(tex0, vTexCoord);\n float gray = luma(color.rgb / color.a);\n // floor() used to match src/image/filters.js\n float threshold = floor(filterParameter * 255.0) / 255.0;\n float blackOrWhite = step(threshold, gray);\n gl_FragColor = vec4(vec3(blackOrWhite) * color.a, color.a);\n}\n'), _filterShaderFrags);
+ var filterShaderVert = 'uniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\n\nattribute vec3 aPosition;\n// texcoords only come from p5 to vertex shader\n// so pass texcoords on to the fragment shader in a varying variable\nattribute vec2 aTexCoord;\nvarying vec2 vTexCoord;\n\nvoid main() {\n // transferring texcoords for the frag shader\n vTexCoord = aTexCoord;\n\n // copy position with a fourth coordinate for projection (1.0 is normal)\n vec4 positionVec4 = vec4(aPosition, 1.0);\n\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n}\n';
+ /**
+ * @module Rendering
+ * @submodule Rendering
+ * @for p5
+ */
+ /**
+ * Set attributes for the WebGL Drawing context.
+ * This is a way of adjusting how the WebGL
+ * renderer works to fine-tune the display and performance.
+ *
+ * Note that this will reinitialize the drawing context
+ * if called after the WebGL canvas is made.
+ *
+ * If an object is passed as the parameter, all attributes
+ * not declared in the object will be set to defaults.
+ *
+ * The available attributes are:
+ *
+ * alpha - indicates if the canvas contains an alpha buffer
+ * default is true
+ *
+ * depth - indicates whether the drawing buffer has a depth buffer
+ * of at least 16 bits - default is true
+ *
+ * stencil - indicates whether the drawing buffer has a stencil buffer
+ * of at least 8 bits
+ *
+ * antialias - indicates whether or not to perform anti-aliasing
+ * default is false (true in Safari)
+ *
+ * premultipliedAlpha - indicates that the page compositor will assume
+ * the drawing buffer contains colors with pre-multiplied alpha
+ * default is true
+ *
+ * preserveDrawingBuffer - if true the buffers will not be cleared and
+ * and will preserve their values until cleared or overwritten by author
+ * (note that p5 clears automatically on draw loop)
+ * default is true
+ *
+ * perPixelLighting - if true, per-pixel lighting will be used in the
+ * lighting shader otherwise per-vertex lighting is used.
+ * default is true.
+ *
+ * version - either 1 or 2, to specify which WebGL version to ask for. By
+ * default, WebGL 2 will be requested. If WebGL2 is not available, it will
+ * fall back to WebGL 1. You can check what version is used with by looking at
+ * the global `webglVersion` property.
+ *
+ * @method setAttributes
+ * @for p5
+ * @param {String} key Name of attribute
+ * @param {Boolean} value New value of named attribute
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw() {
+ * background(255);
+ * push();
+ * rotateZ(frameCount * 0.02);
+ * rotateX(frameCount * 0.02);
+ * rotateY(frameCount * 0.02);
+ * fill(0, 0, 0);
+ * box(50);
+ * pop();
+ * }
+ *
+ *
+ *
+ * Now with the antialias attribute set to true.
+ *
+ *
+ *
+ * function setup() {
+ * setAttributes('antialias', true);
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw() {
+ * background(255);
+ * push();
+ * rotateZ(frameCount * 0.02);
+ * rotateX(frameCount * 0.02);
+ * rotateY(frameCount * 0.02);
+ * fill(0, 0, 0);
+ * box(50);
+ * pop();
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // press the mouse button to disable perPixelLighting
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ * noStroke();
+ * fill(255);
+ * }
+ *
+ * let lights = [
+ * { c: '#f00', t: 1.12, p: 1.91, r: 0.2 },
+ * { c: '#0f0', t: 1.21, p: 1.31, r: 0.2 },
+ * { c: '#00f', t: 1.37, p: 1.57, r: 0.2 },
+ * { c: '#ff0', t: 1.12, p: 1.91, r: 0.7 },
+ * { c: '#0ff', t: 1.21, p: 1.31, r: 0.7 },
+ * { c: '#f0f', t: 1.37, p: 1.57, r: 0.7 }
+ * ];
+ *
+ * function draw() {
+ * let t = millis() / 1000 + 1000;
+ * background(0);
+ * directionalLight(color('#222'), 1, 1, 1);
+ *
+ * for (let i = 0; i < lights.length; i++) {
+ * let light = lights[i];
+ * pointLight(
+ * color(light.c),
+ * p5.Vector.fromAngles(t * light.t, t * light.p, width * light.r)
+ * );
+ * }
+ *
+ * specularMaterial(255);
+ * sphere(width * 0.1);
+ *
+ * rotateX(t * 0.77);
+ * rotateY(t * 0.83);
+ * rotateZ(t * 0.91);
+ * torus(width * 0.3, width * 0.07, 24, 10);
+ * }
+ *
+ * function mousePressed() {
+ * setAttributes('perPixelLighting', false);
+ * noStroke();
+ * fill(255);
+ * }
+ * function mouseReleased() {
+ * setAttributes('perPixelLighting', true);
+ * noStroke();
+ * fill(255);
+ * }
+ *
+ *
+ *
+ * @alt a rotating cube with smoother edges
+ */
+ /**
+ * @method setAttributes
+ * @for p5
+ * @param {Object} obj object with key-value pairs
+ */
+ _main.default.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.');
+ return;
+ }
+ var unchanged = true;
+ if (typeof value !== 'undefined') {
+ //first time modifying the attributes
+ if (this._glAttributes === null) {
+ this._glAttributes = {
+ };
+ }
+ if (this._glAttributes[key] !== value) {
+ //changing value of previously altered attribute
+ this._glAttributes[key] = value;
+ unchanged = false;
+ } //setting all attributes with some change
+
+ } else if (key instanceof Object) {
+ if (this._glAttributes !== key) {
+ this._glAttributes = key;
+ unchanged = false;
+ }
+ } //@todo_FES
+
+ if (!this._renderer.isP3D || unchanged) {
+ return;
+ }
+ if (!this._setupDone) {
+ for (var x in this._renderer.retainedMode.geometry) {
+ if (this._renderer.retainedMode.geometry.hasOwnProperty(x)) {
+ _main.default._friendlyError('Sorry, Could not set the attributes, you need to call setAttributes() ' + 'before calling the other drawing methods in setup()');
+ return;
+ }
+ }
+ }
+ this.push();
+ this._renderer._resetContext();
+ this.pop();
+ if (this._renderer._curCamera) {
+ this._renderer._curCamera._renderer = this._renderer;
+ }
+ };
+ /**
+ * @private
+ * @param {Uint8Array|Float32Array|undefined} pixels An existing pixels array to reuse if the size is the same
+ * @param {WebGLRenderingContext} gl The WebGL context
+ * @param {WebGLFramebuffer|null} framebuffer The Framebuffer to read
+ * @param {Number} x The x coordiante to read, premultiplied by pixel density
+ * @param {Number} y The y coordiante to read, premultiplied by pixel density
+ * @param {Number} width The width in pixels to be read (factoring in pixel density)
+ * @param {Number} height The height in pixels to be read (factoring in pixel density)
+ * @param {GLEnum} format Either RGB or RGBA depending on how many channels to read
+ * @param {GLEnum} type The datatype of each channel, e.g. UNSIGNED_BYTE or FLOAT
+ * @param {Number|undefined} flipY If provided, the total height with which to flip the y axis about
+ * @returns {Uint8Array|Float32Array} pixels A pixels array with the current state of the
+ * WebGL context read into it
+ */
+ function readPixelsWebGL(pixels, gl, framebuffer, x, y, width, height, format, type, flipY) {
+ // Record the currently bound framebuffer so we can go back to it after, and
+ // bind the framebuffer we want to read from
+ var prevFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+ var channels = format === gl.RGBA ? 4 : 3;
+ // Make a pixels buffer if it doesn't already exist
+ var len = width * height * channels;
+ var TypedArrayClass = type === gl.UNSIGNED_BYTE ? Uint8Array : Float32Array;
+ if (!(pixels instanceof TypedArrayClass) || pixels.length !== len) {
+ pixels = new TypedArrayClass(len);
+ }
+ gl.readPixels(x, flipY ? flipY - y - height : y, width, height, format, type, pixels);
+ // Re-bind whatever was previously bound
+ gl.bindFramebuffer(gl.FRAMEBUFFER, prevFramebuffer);
+ if (flipY) {
+ // WebGL pixels are inverted compared to 2D pixels, so we have to flip
+ // the resulting rows. Adapted from https://stackoverflow.com/a/41973289
+ var halfHeight = Math.floor(height / 2);
+ var tmpRow = new TypedArrayClass(width * channels);
+ for (var _y = 0; _y < halfHeight; _y++) {
+ var topOffset = _y * width * 4;
+ var bottomOffset = (height - _y - 1) * width * 4;
+ tmpRow.set(pixels.subarray(topOffset, topOffset + width * 4));
+ pixels.copyWithin(topOffset, bottomOffset, bottomOffset + width * 4);
+ pixels.set(tmpRow, bottomOffset);
+ }
+ }
+ return pixels;
+ } /**
+ * @private
+ * @param {WebGLRenderingContext} gl The WebGL context
+ * @param {WebGLFramebuffer|null} framebuffer The Framebuffer to read
+ * @param {Number} x The x coordinate to read, premultiplied by pixel density
+ * @param {Number} y The y coordinate to read, premultiplied by pixel density
+ * @param {GLEnum} format Either RGB or RGBA depending on how many channels to read
+ * @param {GLEnum} type The datatype of each channel, e.g. UNSIGNED_BYTE or FLOAT
+ * @param {Number|undefined} flipY If provided, the total height with which to flip the y axis about
+ * @returns {Number[]} pixels The channel data for the pixel at that location
+ */
+
+ function readPixelWebGL(gl, framebuffer, x, y, format, type, flipY) {
+ // Record the currently bound framebuffer so we can go back to it after, and
+ // bind the framebuffer we want to read from
+ var prevFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+ var channels = format === gl.RGBA ? 4 : 3;
+ var TypedArrayClass = type === gl.UNSIGNED_BYTE ? Uint8Array : Float32Array;
+ var pixels = new TypedArrayClass(channels);
+ gl.readPixels(x, flipY ? flipY - y - 1 : y, 1, 1, format, type, pixels);
+ // Re-bind whatever was previously bound
+ gl.bindFramebuffer(gl.FRAMEBUFFER, prevFramebuffer);
+ return Array.from(pixels);
+ } /**
+ * 3D graphics class
+ * @private
+ * @class p5.RendererGL
+ * @constructor
+ * @extends p5.Renderer
+ * @todo extend class to include public method for offscreen
+ * rendering (FBO).
+ */
+
+ _main.default.RendererGL = /*#__PURE__*/ function (_p5$Renderer) {
+ _inherits(RendererGL, _p5$Renderer);
+ var _super = _createSuper(RendererGL);
+ function RendererGL(elt, pInst, isMainCanvas, attr) {
+ var _this;
+ _classCallCheck(this, RendererGL);
+ _this = _super.call(this, elt, pInst, isMainCanvas);
+ _this._setAttributeDefaults(pInst);
+ _this._initContext();
+ _this.isP3D = true; //lets us know we're in 3d mode
+ // When constructing a new p5.Geometry, this will represent the builder
+ _this.geometryBuilder = undefined;
+ // This redundant property is useful in reminding you that you are
+ // interacting with WebGLRenderingContext, still worth considering future removal
+ _this.GL = _this.drawingContext;
+ _this._pInst._setProperty('drawingContext', _this.drawingContext);
+ // erasing
+ _this._isErasing = false;
+ // clipping
+ _this._clipDepths = [
+ ];
+ _this._isClipApplied = false;
+ _this._stencilTestOn = false;
+ // lights
+ _this._enableLighting = false;
+ _this.ambientLightColors = [
+ ];
+ _this.mixedAmbientLight = [
+ ];
+ _this.mixedSpecularColor = [
+ ];
+ _this.specularColors = [
+ 1,
+ 1,
+ 1
+ ];
+ _this.directionalLightDirections = [
+ ];
+ _this.directionalLightDiffuseColors = [
+ ];
+ _this.directionalLightSpecularColors = [
+ ];
+ _this.pointLightPositions = [
+ ];
+ _this.pointLightDiffuseColors = [
+ ];
+ _this.pointLightSpecularColors = [
+ ];
+ _this.spotLightPositions = [
+ ];
+ _this.spotLightDirections = [
+ ];
+ _this.spotLightDiffuseColors = [
+ ];
+ _this.spotLightSpecularColors = [
+ ];
+ _this.spotLightAngle = [
+ ];
+ _this.spotLightConc = [
+ ];
+ // This property contains the input image if imageLight function
+ // is called.
+ // activeImageLight is checked by _setFillUniforms
+ // for sending uniforms to the fillshader
+ _this.activeImageLight = null;
+ // If activeImageLight property is Null, diffusedTextures,
+ // specularTextures are Empty.
+ // Else, it maps a p5.Image used by imageLight() to a p5.framebuffer.
+ // p5.framebuffer for this are calculated in getDiffusedTexture function
+ _this.diffusedTextures = new Map();
+ // p5.framebuffer for this are calculated in getSpecularTexture function
+ _this.specularTextures = new Map();
+ _this.drawMode = constants.FILL;
+ _this.curFillColor = _this._cachedFillStyle = [
+ 1,
+ 1,
+ 1,
+ 1
+ ];
+ _this.curAmbientColor = _this._cachedFillStyle = [
+ 1,
+ 1,
+ 1,
+ 1
+ ];
+ _this.curSpecularColor = _this._cachedFillStyle = [
+ 0,
+ 0,
+ 0,
+ 0
+ ];
+ _this.curEmissiveColor = _this._cachedFillStyle = [
+ 0,
+ 0,
+ 0,
+ 0
+ ];
+ _this.curStrokeColor = _this._cachedStrokeStyle = [
+ 0,
+ 0,
+ 0,
+ 1
+ ];
+ _this.curBlendMode = constants.BLEND;
+ _this.preEraseBlend = undefined;
+ _this._cachedBlendMode = undefined;
+ if (_this.webglVersion === constants.WEBGL2) {
+ _this.blendExt = _this.GL;
+ } else {
+ _this.blendExt = _this.GL.getExtension('EXT_blend_minmax');
+ }
+ _this._isBlending = false;
+ _this._hasSetAmbient = false;
+ _this._useSpecularMaterial = false;
+ _this._useEmissiveMaterial = false;
+ _this._useNormalMaterial = false;
+ _this._useShininess = 1;
+ _this._useMetalness = 0;
+ _this._useLineColor = false;
+ _this._useVertexColor = false;
+ _this.registerEnabled = new Set();
+ _this._tint = [
+ 255,
+ 255,
+ 255,
+ 255
+ ];
+ // lightFalloff variables
+ _this.constantAttenuation = 1;
+ _this.linearAttenuation = 0;
+ _this.quadraticAttenuation = 0;
+ /**
+ * model view, projection, & normal
+ * matrices
+ */
+ _this.uMVMatrix = new _main.default.Matrix();
+ _this.uPMatrix = new _main.default.Matrix();
+ _this.uNMatrix = new _main.default.Matrix('mat3');
+ _this.curMatrix = new _main.default.Matrix('mat3');
+ // Current vertex normal
+ _this._currentNormal = new _main.default.Vector(0, 0, 1);
+ // Camera
+ _this._curCamera = new _main.default.Camera(_assertThisInitialized(_this));
+ _this._curCamera._computeCameraDefaultSettings();
+ _this._curCamera._setDefaultCamera();
+ // FilterCamera
+ _this.filterCamera = new _main.default.Camera(_assertThisInitialized(_this));
+ _this.filterCamera._computeCameraDefaultSettings();
+ _this.filterCamera._setDefaultCamera();
+ // Information about the previous frame's touch object
+ // for executing orbitControl()
+ _this.prevTouches = [
+ ];
+ // Velocity variable for use with orbitControl()
+ _this.zoomVelocity = 0;
+ _this.rotateVelocity = new _main.default.Vector(0, 0);
+ _this.moveVelocity = new _main.default.Vector(0, 0);
+ // Flags for recording the state of zooming, rotation and moving
+ _this.executeZoom = false;
+ _this.executeRotateAndMove = false;
+ _this.specularShader = undefined;
+ _this.sphereMapping = undefined;
+ _this.diffusedShader = undefined;
+ _this._defaultLightShader = undefined;
+ _this._defaultImmediateModeShader = undefined;
+ _this._defaultNormalShader = undefined;
+ _this._defaultColorShader = undefined;
+ _this._defaultPointShader = undefined;
+ _this.userFillShader = undefined;
+ _this.userStrokeShader = undefined;
+ _this.userPointShader = undefined;
+ // Default drawing is done in Retained Mode
+ // Geometry and Material hashes stored here
+ _this.retainedMode = {
+ geometry: {
+ },
+ buffers: {
+ stroke: [
+ new _main.default.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', _assertThisInitialized(_this)),
+ new _main.default.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', _assertThisInitialized(_this)),
+ new _main.default.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', _assertThisInitialized(_this)),
+ new _main.default.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', _assertThisInitialized(_this)),
+ new _main.default.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', _assertThisInitialized(_this))
+ ],
+ fill: [
+ new _main.default.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', _assertThisInitialized(_this), _this._vToNArray),
+ new _main.default.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', _assertThisInitialized(_this), _this._vToNArray),
+ new _main.default.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', _assertThisInitialized(_this)),
+ new _main.default.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', _assertThisInitialized(_this)),
+ //new BufferDef(3, 'vertexSpeculars', 'specularBuffer', 'aSpecularColor'),
+ new _main.default.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', _assertThisInitialized(_this), _this._flatten)
+ ],
+ text: [
+ new _main.default.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', _assertThisInitialized(_this), _this._vToNArray),
+ new _main.default.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', _assertThisInitialized(_this), _this._flatten)
+ ]
+ }
+ };
+ // Immediate Mode
+ // Geometry and Material hashes stored here
+ _this.immediateMode = {
+ geometry: new _main.default.Geometry(),
+ shapeMode: constants.TRIANGLE_FAN,
+ contourIndices: [
+ ],
+ _bezierVertex: [
+ ],
+ _quadraticVertex: [
+ ],
+ _curveVertex: [
+ ],
+ buffers: {
+ fill: [
+ new _main.default.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', _assertThisInitialized(_this), _this._vToNArray),
+ new _main.default.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', _assertThisInitialized(_this), _this._vToNArray),
+ new _main.default.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', _assertThisInitialized(_this)),
+ new _main.default.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', _assertThisInitialized(_this)),
+ new _main.default.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', _assertThisInitialized(_this), _this._flatten)
+ ],
+ stroke: [
+ new _main.default.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', _assertThisInitialized(_this)),
+ new _main.default.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', _assertThisInitialized(_this)),
+ new _main.default.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', _assertThisInitialized(_this)),
+ new _main.default.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', _assertThisInitialized(_this)),
+ new _main.default.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', _assertThisInitialized(_this))
+ ],
+ point: _this.GL.createBuffer()
+ }
+ };
+ _this.pointSize = 5; //default point size
+ _this.curStrokeWeight = 1;
+ _this.curStrokeCap = constants.ROUND;
+ _this.curStrokeJoin = constants.ROUND;
+ // map of texture sources to textures created in this gl context via this.getTexture(src)
+ _this.textures = new Map();
+ // set of framebuffers in use
+ _this.framebuffers = new Set();
+ // stack of active framebuffers
+ _this.activeFramebuffers = [
+ ];
+ // for post processing step
+ _this.filterShader = undefined;
+ _this.filterLayer = undefined;
+ _this.filterLayerTemp = undefined;
+ _this.defaultFilterShaders = {
+ };
+ _this.textureMode = constants.IMAGE;
+ // default wrap settings
+ _this.textureWrapX = constants.CLAMP;
+ _this.textureWrapY = constants.CLAMP;
+ _this._tex = null;
+ _this._curveTightness = 6;
+ // lookUpTable for coefficients needed to be calculated for bezierVertex, same are used for curveVertex
+ _this._lookUpTableBezier = [
+ ];
+ // lookUpTable for coefficients needed to be calculated for quadraticVertex
+ _this._lookUpTableQuadratic = [
+ ];
+ // current curveDetail in the Bezier lookUpTable
+ _this._lutBezierDetail = 0;
+ // current curveDetail in the Quadratic lookUpTable
+ _this._lutQuadraticDetail = 0;
+ // Used to distinguish between user calls to vertex() and internal calls
+ _this.isProcessingVertices = false;
+ _this._tessy = _this._initTessy();
+ _this.fontInfos = {
+ };
+ _this._curShader = undefined;
+ return _this;
+ } /**
+ * Starts creating a new p5.Geometry. Subsequent shapes drawn will be added
+ * to the geometry and then returned when
+ * endGeometry() is called. One can also use
+ * buildGeometry() to pass a function that
+ * draws shapes.
+ *
+ * If you need to draw complex shapes every frame which don't change over time,
+ * combining them upfront with `beginGeometry()` and `endGeometry()` and then
+ * drawing that will run faster than repeatedly drawing the individual pieces.
+ *
+ * @method beginGeometry
+ */
+
+ _createClass(RendererGL, [
+ {
+ key: 'beginGeometry',
+ value: function beginGeometry() {
+ if (this.geometryBuilder) {
+ throw new Error('It looks like `beginGeometry()` is being called while another p5.Geometry is already being build.');
+ }
+ this.geometryBuilder = new _GeometryBuilder.default(this);
+ } /**
+ * Finishes creating a new p5.Geometry that was
+ * started using beginGeometry(). One can also
+ * use buildGeometry() to pass a function that
+ * draws shapes.
+ *
+ * @method endGeometry
+ * @returns {p5.Geometry} The model that was built.
+ */
+
+ },
+ {
+ key: 'endGeometry',
+ value: function endGeometry() {
+ if (!this.geometryBuilder) {
+ throw new Error('Make sure you call beginGeometry() before endGeometry()!');
+ }
+ var geometry = this.geometryBuilder.finish();
+ this.geometryBuilder = undefined;
+ return geometry;
+ } /**
+ * Creates a new p5.Geometry that contains all
+ * the shapes drawn in a provided callback function. The returned combined shape
+ * can then be drawn all at once using model().
+ *
+ * If you need to draw complex shapes every frame which don't change over time,
+ * combining them with `buildGeometry()` once and then drawing that will run
+ * faster than repeatedly drawing the individual pieces.
+ *
+ * One can also draw shapes directly between
+ * beginGeometry() and
+ * endGeometry() instead of using a callback
+ * function.
+ *
+ * @method buildGeometry
+ * @param {Function} callback A function that draws shapes.
+ * @returns {p5.Geometry} The model that was built from the callback function.
+ */
+
+ },
+ {
+ key: 'buildGeometry',
+ value: function buildGeometry(callback) {
+ this.beginGeometry();
+ callback();
+ return this.endGeometry();
+ } //////////////////////////////////////////////
+ // Setting
+ //////////////////////////////////////////////
+
+ },
+ {
+ key: '_setAttributeDefaults',
+ value: function _setAttributeDefaults(pInst) {
+ // See issue #3850, safer to enable AA in Safari
+ var applyAA = navigator.userAgent.toLowerCase().includes('safari');
+ var defaults = {
+ alpha: true,
+ depth: true,
+ stencil: true,
+ antialias: applyAA,
+ premultipliedAlpha: true,
+ preserveDrawingBuffer: true,
+ perPixelLighting: true,
+ version: 2
+ };
+ if (pInst._glAttributes === null) {
+ pInst._glAttributes = defaults;
+ } else {
+ pInst._glAttributes = Object.assign(defaults, pInst._glAttributes);
+ }
+ return;
+ }
+ },
+ {
+ key: '_initContext',
+ value: function _initContext() {
+ if (this._pInst._glAttributes.version !== 1) {
+ // Unless WebGL1 is explicitly asked for, try to create a WebGL2 context
+ this.drawingContext = this.canvas.getContext('webgl2', this._pInst._glAttributes);
+ }
+ this.webglVersion = this.drawingContext ? constants.WEBGL2 : constants.WEBGL;
+ // If this is the main canvas, make sure the global `webglVersion` is set
+ this._pInst._setProperty('webglVersion', this.webglVersion);
+ if (!this.drawingContext) {
+ // If we were unable to create a WebGL2 context (either because it was
+ // disabled via `setAttributes({ version: 1 })` or because the device
+ // doesn't support it), fall back to a WebGL1 context
+ this.drawingContext = this.canvas.getContext('webgl', this._pInst._glAttributes) || this.canvas.getContext('experimental-webgl', this._pInst._glAttributes);
+ }
+ if (this.drawingContext === null) {
+ throw new Error('Error creating webgl context');
+ } else {
+ var gl = this.drawingContext;
+ gl.enable(gl.DEPTH_TEST);
+ gl.depthFunc(gl.LEQUAL);
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
+ // Make sure all images are loaded into the canvas premultiplied so that
+ // they match the way we render colors. This will make framebuffer textures
+ // be encoded the same way as textures from everything else.
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
+ this._viewport = this.drawingContext.getParameter(this.drawingContext.VIEWPORT);
+ }
+ }
+ },
+ {
+ key: '_getParam',
+ value: function _getParam() {
+ var gl = this.drawingContext;
+ return gl.getParameter(gl.MAX_TEXTURE_SIZE);
+ }
+ },
+ {
+ key: '_adjustDimensions',
+ value: function _adjustDimensions(width, height) {
+ if (!this._maxTextureSize) {
+ this._maxTextureSize = this._getParam();
+ }
+ var maxTextureSize = this._maxTextureSize;
+ var maxAllowedPixelDimensions = _main.default.prototype._maxAllowedPixelDimensions;
+ maxAllowedPixelDimensions = Math.floor(maxTextureSize / this.pixelDensity());
+ var adjustedWidth = Math.min(width, maxAllowedPixelDimensions);
+ var adjustedHeight = Math.min(height, maxAllowedPixelDimensions);
+ if (adjustedWidth !== width || adjustedHeight !== height) {
+ console.warn('Warning: The requested width/height exceeds hardware limits. ' + 'Adjusting dimensions to width: '.concat(adjustedWidth, ', height: ').concat(adjustedHeight, '.'));
+ }
+ return {
+ adjustedWidth: adjustedWidth,
+ adjustedHeight: adjustedHeight
+ };
+ } //This is helper function to reset the context anytime the attributes
+ //are changed with setAttributes()
+
+ },
+ {
+ key: '_resetContext',
+ value: function _resetContext(options, callback) {
+ var w = this.width;
+ var h = this.height;
+ var defaultId = this.canvas.id;
+ var isPGraphics = this._pInst instanceof _main.default.Graphics;
+ if (isPGraphics) {
+ var pg = this._pInst;
+ pg.canvas.parentNode.removeChild(pg.canvas);
+ pg.canvas = document.createElement('canvas');
+ var node = pg._pInst._userNode || document.body;
+ node.appendChild(pg.canvas);
+ _main.default.Element.call(pg, pg.canvas, pg._pInst);
+ pg.width = w;
+ pg.height = h;
+ } else {
+ var c = this.canvas;
+ if (c) {
+ c.parentNode.removeChild(c);
+ }
+ c = document.createElement('canvas');
+ c.id = defaultId;
+ if (this._pInst._userNode) {
+ this._pInst._userNode.appendChild(c);
+ } else {
+ document.body.appendChild(c);
+ }
+ this._pInst.canvas = c;
+ this.canvas = c;
+ }
+ var renderer = new _main.default.RendererGL(this._pInst.canvas, this._pInst, !isPGraphics);
+ this._pInst._setProperty('_renderer', renderer);
+ renderer.resize(w, h);
+ renderer._applyDefaults();
+ if (!isPGraphics) {
+ this._pInst._elements.push(renderer);
+ }
+ if (typeof callback === 'function') {
+ //setTimeout with 0 forces the task to the back of the queue, this ensures that
+ //we finish switching out the renderer
+ setTimeout(function () {
+ callback.apply(window._renderer, options);
+ }, 0);
+ }
+ } /**
+ * @class p5.RendererGL
+ */
+
+ },
+ {
+ key: '_update',
+ value: function _update() {
+ // reset model view and apply initial camera transform
+ // (containing only look at info; no projection).
+ this.uMVMatrix.set(this._curCamera.cameraMatrix);
+ // reset light data for new frame.
+ this.ambientLightColors.length = 0;
+ this.specularColors = [
+ 1,
+ 1,
+ 1
+ ];
+ this.directionalLightDirections.length = 0;
+ this.directionalLightDiffuseColors.length = 0;
+ this.directionalLightSpecularColors.length = 0;
+ this.pointLightPositions.length = 0;
+ this.pointLightDiffuseColors.length = 0;
+ this.pointLightSpecularColors.length = 0;
+ this.spotLightPositions.length = 0;
+ this.spotLightDirections.length = 0;
+ this.spotLightDiffuseColors.length = 0;
+ this.spotLightSpecularColors.length = 0;
+ this.spotLightAngle.length = 0;
+ this.spotLightConc.length = 0;
+ this._enableLighting = false;
+ //reset tint value for new frame
+ this._tint = [
+ 255,
+ 255,
+ 255,
+ 255
+ ];
+ //Clear depth every frame
+ this.GL.clearStencil(0);
+ this.GL.clear(this.GL.DEPTH_BUFFER_BIT | this.GL.STENCIL_BUFFER_BIT);
+ this.GL.disable(this.GL.STENCIL_TEST);
+ } /**
+ * [background description]
+ */
+
+ },
+ {
+ key: 'background',
+ value: function background() {
+ var _this$_pInst;
+ var _col = (_this$_pInst = this._pInst).color.apply(_this$_pInst, arguments);
+ var _r = _col.levels[0] / 255;
+ var _g = _col.levels[1] / 255;
+ var _b = _col.levels[2] / 255;
+ var _a = _col.levels[3] / 255;
+ this.clear(_r, _g, _b, _a);
+ } //////////////////////////////////////////////
+ // COLOR
+ //////////////////////////////////////////////
+ /**
+ * Basic fill material for geometry with a given color
+ * @method fill
+ * @class p5.RendererGL
+ * @param {Number|Number[]|String|p5.Color} v1 gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param {Number} [v2] green or saturation value
+ * @param {Number} [v3] blue or brightness value
+ * @param {Number} [a] opacity
+ * @chainable
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(200, 200, WEBGL);
+ * }
+ *
+ * function draw() {
+ * background(0);
+ * noStroke();
+ * fill(100, 100, 240);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * box(75, 75, 75);
+ * }
+ *
+ *
+ *
+ * @alt
+ * black canvas with purple cube spinning
+ */
+
+ },
+ {
+ key: 'fill',
+ value: function fill(v1, v2, v3, a) {
+ //see material.js for more info on color blending in webgl
+ var color = _main.default.prototype.color.apply(this._pInst, arguments);
+ this.curFillColor = color._array;
+ this.drawMode = constants.FILL;
+ this._useNormalMaterial = false;
+ this._tex = null;
+ } /**
+ * Basic stroke material for geometry with a given color
+ * @method stroke
+ * @param {Number|Number[]|String|p5.Color} v1 gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param {Number} [v2] green or saturation value
+ * @param {Number} [v3] blue or brightness value
+ * @param {Number} [a] opacity
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(200, 200, WEBGL);
+ * }
+ *
+ * function draw() {
+ * background(0);
+ * stroke(240, 150, 150);
+ * fill(100, 100, 240);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * box(75, 75, 75);
+ * }
+ *
+ *
+ *
+ * @alt
+ * black canvas with purple cube with pink outline spinning
+ */
+
+ },
+ {
+ key: 'stroke',
+ value: function stroke(r, g, b, a) {
+ var color = _main.default.prototype.color.apply(this._pInst, arguments);
+ this.curStrokeColor = color._array;
+ }
+ },
+ {
+ key: 'strokeCap',
+ value: function strokeCap(cap) {
+ this.curStrokeCap = cap;
+ }
+ },
+ {
+ key: 'strokeJoin',
+ value: function strokeJoin(join) {
+ this.curStrokeJoin = join;
+ }
+ },
+ {
+ key: 'getFilterLayer',
+ value: function getFilterLayer() {
+ if (!this.filterLayer) {
+ this.filterLayer = this._pInst.createFramebuffer();
+ }
+ return this.filterLayer;
+ }
+ },
+ {
+ key: 'getFilterLayerTemp',
+ value: function getFilterLayerTemp() {
+ if (!this.filterLayerTemp) {
+ this.filterLayerTemp = this._pInst.createFramebuffer();
+ }
+ return this.filterLayerTemp;
+ }
+ },
+ {
+ key: 'matchSize',
+ value: function matchSize(fboToMatch, target) {
+ if (fboToMatch.width !== target.width || fboToMatch.height !== target.height) {
+ fboToMatch.resize(target.width, target.height);
+ }
+ if (fboToMatch.pixelDensity() !== target.pixelDensity()) {
+ fboToMatch.pixelDensity(target.pixelDensity());
+ }
+ }
+ },
+ {
+ key: 'filter',
+ value: function filter() {
+ var _this2 = this;
+ var fbo = this.getFilterLayer();
+ // use internal shader for filter constants BLUR, INVERT, etc
+ var filterParameter = undefined;
+ var operation = undefined;
+ if (typeof (arguments.length <= 0 ? undefined : arguments[0]) === 'string') {
+ var _defaults;
+ operation = arguments.length <= 0 ? undefined : arguments[0];
+ var defaults = (_defaults = {
+ }, _defineProperty(_defaults, constants.BLUR, 3), _defineProperty(_defaults, constants.POSTERIZE, 4), _defineProperty(_defaults, constants.THRESHOLD, 0.5), _defaults);
+ var useDefaultParam = operation in defaults && (arguments.length <= 1 ? undefined : arguments[1]) === undefined;
+ filterParameter = useDefaultParam ? defaults[operation] : arguments.length <= 1 ? undefined : arguments[1];
+ // Create and store shader for constants once on initial filter call.
+ // Need to store multiple in case user calls different filters,
+ // eg. filter(BLUR) then filter(GRAY)
+ if (!(operation in this.defaultFilterShaders)) {
+ this.defaultFilterShaders[operation] = new _main.default.Shader(fbo._renderer, filterShaderVert, filterShaderFrags[operation]);
+ }
+ this.filterShader = this.defaultFilterShaders[operation];
+ } // use custom user-supplied shader
+ else {
+ this.filterShader = arguments.length <= 0 ? undefined : arguments[0];
+ } // Setting the target to the framebuffer when applying a filter to a framebuffer.
+
+ var target = this.activeFramebuffer() || this;
+ // Resize the framebuffer 'fbo' and adjust its pixel density if it doesn't match the target.
+ this.matchSize(fbo, target);
+ fbo.draw(function () {
+ return _this2._pInst.clear();
+ }); // prevent undesirable feedback effects accumulating secretly.
+ var texelSize = [
+ 1 / (target.width * target.pixelDensity()),
+ 1 / (target.height * target.pixelDensity())
+ ];
+ // apply blur shader with multiple passes.
+ if (operation === constants.BLUR) {
+ // Treating 'tmp' as a framebuffer.
+ var tmp = this.getFilterLayerTemp();
+ // Resize the framebuffer 'tmp' and adjust its pixel density if it doesn't match the target.
+ this.matchSize(tmp, target);
+ // setup
+ this._pInst.push();
+ this._pInst.noStroke();
+ this._pInst.blendMode(constants.BLEND);
+ // draw main to temp buffer
+ this._pInst.shader(this.filterShader);
+ this.filterShader.setUniform('texelSize', texelSize);
+ this.filterShader.setUniform('canvasSize', [
+ target.width,
+ target.height
+ ]);
+ this.filterShader.setUniform('radius', Math.max(1, filterParameter));
+ // Horiz pass: draw `target` to `tmp`
+ tmp.draw(function () {
+ _this2.filterShader.setUniform('direction', [
+ 1,
+ 0
+ ]);
+ _this2.filterShader.setUniform('tex0', target);
+ _this2._pInst.clear();
+ _this2._pInst.shader(_this2.filterShader);
+ _this2._pInst.noLights();
+ _this2._pInst.plane(target.width, target.height);
+ });
+ // Vert pass: draw `tmp` to `fbo`
+ fbo.draw(function () {
+ _this2.filterShader.setUniform('direction', [
+ 0,
+ 1
+ ]);
+ _this2.filterShader.setUniform('tex0', tmp);
+ _this2._pInst.clear();
+ _this2._pInst.shader(_this2.filterShader);
+ _this2._pInst.noLights();
+ _this2._pInst.plane(target.width, target.height);
+ });
+ this._pInst.pop();
+ } // every other non-blur shader uses single pass
+ else {
+ fbo.draw(function () {
+ _this2._pInst.noStroke();
+ _this2._pInst.blendMode(constants.BLEND);
+ _this2._pInst.shader(_this2.filterShader);
+ _this2.filterShader.setUniform('tex0', target);
+ _this2.filterShader.setUniform('texelSize', texelSize);
+ _this2.filterShader.setUniform('canvasSize', [
+ target.width,
+ target.height
+ ]);
+ // filterParameter uniform only used for POSTERIZE, and THRESHOLD
+ // but shouldn't hurt to always set
+ _this2.filterShader.setUniform('filterParameter', filterParameter);
+ _this2._pInst.noLights();
+ _this2._pInst.plane(target.width, target.height);
+ });
+ } // draw fbo contents onto main renderer.
+
+ this._pInst.push();
+ this._pInst.noStroke();
+ this.clear();
+ this._pInst.push();
+ this._pInst.imageMode(constants.CORNER);
+ this._pInst.blendMode(constants.BLEND);
+ target.filterCamera._resize();
+ this._pInst.setCamera(target.filterCamera);
+ this._pInst.resetMatrix();
+ this._pInst.image(fbo, - target.width / 2, - target.height / 2, target.width, target.height);
+ this._pInst.clearDepth();
+ this._pInst.pop();
+ this._pInst.pop();
+ } // Pass this off to the host instance so that we can treat a renderer and a
+ // framebuffer the same in filter()
+
+ },
+ {
+ key: 'pixelDensity',
+ value: function pixelDensity(newDensity) {
+ if (newDensity) {
+ return this._pInst.pixelDensity(newDensity);
+ }
+ return this._pInst.pixelDensity();
+ }
+ },
+ {
+ key: 'blendMode',
+ value: function blendMode(mode) {
+ if (mode === constants.DARKEST || mode === constants.LIGHTEST || mode === constants.ADD || mode === constants.BLEND || mode === constants.SUBTRACT || mode === constants.SCREEN || mode === constants.EXCLUSION || mode === constants.REPLACE || mode === constants.MULTIPLY || mode === constants.REMOVE) this.curBlendMode = mode;
+ else if (mode === constants.BURN || mode === constants.OVERLAY || mode === constants.HARD_LIGHT || mode === constants.SOFT_LIGHT || mode === constants.DODGE) {
+ console.warn('BURN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, and DODGE only work for blendMode in 2D mode.');
+ }
+ }
+ },
+ {
+ key: 'erase',
+ value: function erase(opacityFill, opacityStroke) {
+ if (!this._isErasing) {
+ this.preEraseBlend = this.curBlendMode;
+ this._isErasing = true;
+ this.blendMode(constants.REMOVE);
+ this._cachedFillStyle = this.curFillColor.slice();
+ this.curFillColor = [
+ 1,
+ 1,
+ 1,
+ opacityFill / 255
+ ];
+ this._cachedStrokeStyle = this.curStrokeColor.slice();
+ this.curStrokeColor = [
+ 1,
+ 1,
+ 1,
+ opacityStroke / 255
+ ];
+ }
+ }
+ },
+ {
+ key: 'noErase',
+ value: function noErase() {
+ if (this._isErasing) {
+ // Restore colors
+ this.curFillColor = this._cachedFillStyle.slice();
+ this.curStrokeColor = this._cachedStrokeStyle.slice();
+ // Restore blend mode
+ this.curBlendMode = this.preEraseBlend;
+ this.blendMode(this.preEraseBlend);
+ // Ensure that _applyBlendMode() sets preEraseBlend back to the original blend mode
+ this._isErasing = false;
+ this._applyBlendMode();
+ }
+ }
+ },
+ {
+ key: 'drawTarget',
+ value: function drawTarget() {
+ return this.activeFramebuffers[this.activeFramebuffers.length - 1] || this;
+ }
+ },
+ {
+ key: 'beginClip',
+ value: function beginClip() {
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
+ };
+ _get(_getPrototypeOf(RendererGL.prototype), 'beginClip', this).call(this, options);
+ this.drawTarget()._isClipApplied = true;
+ var gl = this.GL;
+ gl.clearStencil(0);
+ gl.clear(gl.STENCIL_BUFFER_BIT);
+ gl.enable(gl.STENCIL_TEST);
+ this._stencilTestOn = true;
+ gl.stencilFunc(gl.ALWAYS, // the test
+ 1, // reference value
+ 255 // mask
+ );
+ gl.stencilOp(gl.KEEP, // what to do if the stencil test fails
+ gl.KEEP, // what to do if the depth test fails
+ gl.REPLACE // what to do if both tests pass
+ );
+ gl.disable(gl.DEPTH_TEST);
+ this._pInst.push();
+ this._pInst.resetShader();
+ if (this._doFill) this._pInst.fill(0, 0);
+ if (this._doStroke) this._pInst.stroke(0, 0);
+ }
+ },
+ {
+ key: 'endClip',
+ value: function endClip() {
+ this._pInst.pop();
+ var gl = this.GL;
+ gl.stencilOp(gl.KEEP, // what to do if the stencil test fails
+ gl.KEEP, // what to do if the depth test fails
+ gl.KEEP // what to do if both tests pass
+ );
+ gl.stencilFunc(this._clipInvert ? gl.EQUAL : gl.NOTEQUAL, // the test
+ 0, // reference value
+ 255 // mask
+ );
+ gl.enable(gl.DEPTH_TEST);
+ // Mark the depth at which the clip has been applied so that we can clear it
+ // when we pop past this depth
+ this._clipDepths.push(this._pushPopDepth);
+ _get(_getPrototypeOf(RendererGL.prototype), 'endClip', this).call(this);
+ }
+ },
+ {
+ key: '_clearClip',
+ value: function _clearClip() {
+ this.GL.clearStencil(1);
+ this.GL.clear(this.GL.STENCIL_BUFFER_BIT);
+ if (this._clipDepths.length > 0) {
+ this._clipDepths.pop();
+ }
+ this.drawTarget()._isClipApplied = false;
+ } /**
+ * Change weight of stroke
+ * @method strokeWeight
+ * @param {Number} stroke weight to be used for drawing
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(200, 400, WEBGL);
+ * setAttributes('antialias', true);
+ * }
+ *
+ * function draw() {
+ * background(0);
+ * noStroke();
+ * translate(0, -100, 0);
+ * stroke(240, 150, 150);
+ * fill(100, 100, 240);
+ * push();
+ * strokeWeight(8);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * sphere(75);
+ * pop();
+ * push();
+ * translate(0, 200, 0);
+ * strokeWeight(1);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * sphere(75);
+ * pop();
+ * }
+ *
+ *
+ *
+ * @alt
+ * black canvas with two purple rotating spheres with pink
+ * outlines the sphere on top has much heavier outlines,
+ */
+
+ },
+ {
+ key: 'strokeWeight',
+ value: function strokeWeight(w) {
+ if (this.curStrokeWeight !== w) {
+ this.pointSize = w;
+ this.curStrokeWeight = w;
+ }
+ } // x,y are canvas-relative (pre-scaled by _pixelDensity)
+
+ },
+ {
+ key: '_getPixel',
+ value: function _getPixel(x, y) {
+ var gl = this.GL;
+ return readPixelWebGL(gl, null, x, y, gl.RGBA, gl.UNSIGNED_BYTE, this._pInst.height * this._pInst.pixelDensity());
+ } /**
+ * Loads the pixels data for this canvas into the pixels[] attribute.
+ * Note that updatePixels() and set() do not work.
+ * Any pixel manipulation must be done directly to the pixels[] array.
+ *
+ * @private
+ * @method loadPixels
+ */
+
+ },
+ {
+ key: 'loadPixels',
+ value: function loadPixels() {
+ var pixelsState = this._pixelsState;
+ //@todo_FES
+ if (this._pInst._glAttributes.preserveDrawingBuffer !== true) {
+ console.log('loadPixels only works in WebGL when preserveDrawingBuffer ' + 'is true.');
+ return;
+ }
+ var pd = this._pInst._pixelDensity;
+ var gl = this.GL;
+ pixelsState._setProperty('pixels', readPixelsWebGL(pixelsState.pixels, gl, null, 0, 0, this.width * pd, this.height * pd, gl.RGBA, gl.UNSIGNED_BYTE, this.height * pd));
+ }
+ },
+ {
+ key: 'updatePixels',
+ value: function updatePixels() {
+ var fbo = this._getTempFramebuffer();
+ fbo.pixels = this._pixelsState.pixels;
+ fbo.updatePixels();
+ this._pInst.push();
+ this._pInst.resetMatrix();
+ this._pInst.clear();
+ this._pInst.imageMode(constants.CENTER);
+ this._pInst.image(fbo, 0, 0);
+ this._pInst.pop();
+ this.GL.clearDepth(1);
+ this.GL.clear(this.GL.DEPTH_BUFFER_BIT);
+ } /**
+ * @private
+ * @returns {p5.Framebuffer} A p5.Framebuffer set to match the size and settings
+ * of the renderer's canvas. It will be created if it does not yet exist, and
+ * reused if it does.
+ */
+
+ },
+ {
+ key: '_getTempFramebuffer',
+ value: function _getTempFramebuffer() {
+ if (!this._tempFramebuffer) {
+ this._tempFramebuffer = this._pInst.createFramebuffer({
+ format: constants.UNSIGNED_BYTE,
+ useDepth: this._pInst._glAttributes.depth,
+ depthFormat: constants.UNSIGNED_INT,
+ antialias: this._pInst._glAttributes.antialias
+ });
+ }
+ return this._tempFramebuffer;
+ } //////////////////////////////////////////////
+ // HASH | for geometry
+ //////////////////////////////////////////////
+
+ },
+ {
+ key: 'geometryInHash',
+ value: function geometryInHash(gId) {
+ return this.retainedMode.geometry[gId] !== undefined;
+ }
+ },
+ {
+ key: 'viewport',
+ value: function viewport(w, h) {
+ this._viewport = [
+ 0,
+ 0,
+ w,
+ h
+ ];
+ this.GL.viewport(0, 0, w, h);
+ } /**
+ * [resize description]
+ * @private
+ * @param {Number} w [description]
+ * @param {Number} h [description]
+ */
+
+ },
+ {
+ key: 'resize',
+ value: function resize(w, h) {
+ _main.default.Renderer.prototype.resize.call(this, w, h);
+ this._origViewport = {
+ width: this.GL.drawingBufferWidth,
+ height: this.GL.drawingBufferHeight
+ };
+ this.viewport(this._origViewport.width, this._origViewport.height);
+ this._curCamera._resize();
+ //resize pixels buffer
+ var pixelsState = this._pixelsState;
+ if (typeof pixelsState.pixels !== 'undefined') {
+ pixelsState._setProperty('pixels', new Uint8Array(this.GL.drawingBufferWidth * this.GL.drawingBufferHeight * 4));
+ }
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+ try {
+ for (var _iterator = this.framebuffers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var framebuffer = _step.value;
+ // Notify framebuffers of the resize so that any auto-sized framebuffers
+ // can also update their size
+ framebuffer._canvasSizeChanged();
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ } /**
+ * clears color and depth buffers
+ * with r,g,b,a
+ * @private
+ * @param {Number} r normalized red val.
+ * @param {Number} g normalized green val.
+ * @param {Number} b normalized blue val.
+ * @param {Number} a normalized alpha val.
+ */
+
+ },
+ {
+ key: 'clear',
+ value: function clear() {
+ var _r = (arguments.length <= 0 ? undefined : arguments[0]) || 0;
+ var _g = (arguments.length <= 1 ? undefined : arguments[1]) || 0;
+ var _b = (arguments.length <= 2 ? undefined : arguments[2]) || 0;
+ var _a = (arguments.length <= 3 ? undefined : arguments[3]) || 0;
+ var activeFramebuffer = this.activeFramebuffer();
+ if (activeFramebuffer && activeFramebuffer.format === constants.UNSIGNED_BYTE && !activeFramebuffer.antialias && _a === 0) {
+ // Drivers on Intel Macs check for 0,0,0,0 exactly when drawing to a
+ // framebuffer and ignore the command if it's the only drawing command to
+ // the framebuffer. To work around it, we can set the alpha to a value so
+ // low that it still rounds down to 0, but that circumvents the buggy
+ // check in the driver.
+ _a = 1e-10;
+ }
+ this.GL.clearColor(_r * _a, _g * _a, _b * _a, _a);
+ this.GL.clearDepth(1);
+ this.GL.clear(this.GL.COLOR_BUFFER_BIT | this.GL.DEPTH_BUFFER_BIT);
+ } /**
+ * Resets all depth information so that nothing previously drawn will
+ * occlude anything subsequently drawn.
+ */
+
+ },
+ {
+ key: 'clearDepth',
+ value: function clearDepth() {
+ var depth = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
+ this.GL.clearDepth(depth);
+ this.GL.clear(this.GL.DEPTH_BUFFER_BIT);
+ }
+ },
+ {
+ key: 'applyMatrix',
+ value: function applyMatrix(a, b, c, d, e, f) {
+ if (arguments.length === 16) {
+ _main.default.Matrix.prototype.apply.apply(this.uMVMatrix, arguments);
+ } else {
+ this.uMVMatrix.apply([a,
+ b,
+ 0,
+ 0,
+ c,
+ d,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ e,
+ f,
+ 0,
+ 1]);
+ }
+ } /**
+ * [translate description]
+ * @private
+ * @param {Number} x [description]
+ * @param {Number} y [description]
+ * @param {Number} z [description]
+ * @chainable
+ * @todo implement handle for components or vector as args
+ */
+
+ },
+ {
+ key: 'translate',
+ value: function translate(x, y, z) {
+ if (x instanceof _main.default.Vector) {
+ z = x.z;
+ y = x.y;
+ x = x.x;
+ }
+ this.uMVMatrix.translate([x,
+ y,
+ z]);
+ return this;
+ } /**
+ * Scales the Model View Matrix by a vector
+ * @private
+ * @param {Number | p5.Vector | Array} x [description]
+ * @param {Number} [y] y-axis scalar
+ * @param {Number} [z] z-axis scalar
+ * @chainable
+ */
+
+ },
+ {
+ key: 'scale',
+ value: function scale(x, y, z) {
+ this.uMVMatrix.scale(x, y, z);
+ return this;
+ }
+ },
+ {
+ key: 'rotate',
+ value: function rotate(rad, axis) {
+ if (typeof axis === 'undefined') {
+ return this.rotateZ(rad);
+ }
+ _main.default.Matrix.prototype.rotate.apply(this.uMVMatrix, arguments);
+ return this;
+ }
+ },
+ {
+ key: 'rotateX',
+ value: function rotateX(rad) {
+ this.rotate(rad, 1, 0, 0);
+ return this;
+ }
+ },
+ {
+ key: 'rotateY',
+ value: function rotateY(rad) {
+ this.rotate(rad, 0, 1, 0);
+ return this;
+ }
+ },
+ {
+ key: 'rotateZ',
+ value: function rotateZ(rad) {
+ this.rotate(rad, 0, 0, 1);
+ return this;
+ }
+ },
+ {
+ key: 'push',
+ value: function push() {
+ // get the base renderer style
+ var style = _main.default.Renderer.prototype.push.apply(this);
+ // add webgl-specific style properties
+ var properties = style.properties;
+ properties.uMVMatrix = this.uMVMatrix.copy();
+ properties.uPMatrix = this.uPMatrix.copy();
+ properties._curCamera = this._curCamera;
+ // make a copy of the current camera for the push state
+ // this preserves any references stored using 'createCamera'
+ this._curCamera = this._curCamera.copy();
+ properties.ambientLightColors = this.ambientLightColors.slice();
+ properties.specularColors = this.specularColors.slice();
+ properties.directionalLightDirections = this.directionalLightDirections.slice();
+ properties.directionalLightDiffuseColors = this.directionalLightDiffuseColors.slice();
+ properties.directionalLightSpecularColors = this.directionalLightSpecularColors.slice();
+ properties.pointLightPositions = this.pointLightPositions.slice();
+ properties.pointLightDiffuseColors = this.pointLightDiffuseColors.slice();
+ properties.pointLightSpecularColors = this.pointLightSpecularColors.slice();
+ properties.spotLightPositions = this.spotLightPositions.slice();
+ properties.spotLightDirections = this.spotLightDirections.slice();
+ properties.spotLightDiffuseColors = this.spotLightDiffuseColors.slice();
+ properties.spotLightSpecularColors = this.spotLightSpecularColors.slice();
+ properties.spotLightAngle = this.spotLightAngle.slice();
+ properties.spotLightConc = this.spotLightConc.slice();
+ properties.userFillShader = this.userFillShader;
+ properties.userStrokeShader = this.userStrokeShader;
+ properties.userPointShader = this.userPointShader;
+ properties.pointSize = this.pointSize;
+ properties.curStrokeWeight = this.curStrokeWeight;
+ properties.curStrokeColor = this.curStrokeColor;
+ properties.curFillColor = this.curFillColor;
+ properties.curAmbientColor = this.curAmbientColor;
+ properties.curSpecularColor = this.curSpecularColor;
+ properties.curEmissiveColor = this.curEmissiveColor;
+ properties._hasSetAmbient = this._hasSetAmbient;
+ properties._useSpecularMaterial = this._useSpecularMaterial;
+ properties._useEmissiveMaterial = this._useEmissiveMaterial;
+ properties._useShininess = this._useShininess;
+ properties._useMetalness = this._useMetalness;
+ properties.constantAttenuation = this.constantAttenuation;
+ properties.linearAttenuation = this.linearAttenuation;
+ properties.quadraticAttenuation = this.quadraticAttenuation;
+ properties._enableLighting = this._enableLighting;
+ properties._useNormalMaterial = this._useNormalMaterial;
+ properties._tex = this._tex;
+ properties.drawMode = this.drawMode;
+ properties._currentNormal = this._currentNormal;
+ properties.curBlendMode = this.curBlendMode;
+ // So that the activeImageLight gets reset in push/pop
+ properties.activeImageLight = this.activeImageLight;
+ return style;
+ }
+ },
+ {
+ key: 'pop',
+ value: function pop() {
+ var _get2;
+ if (this._clipDepths.length > 0 && this._pushPopDepth === this._clipDepths[this._clipDepths.length - 1]) {
+ this._clearClip();
+ }
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }(_get2 = _get(_getPrototypeOf(RendererGL.prototype), 'pop', this)).call.apply(_get2, [
+ this
+ ].concat(args));
+ this._applyStencilTestIfClipping();
+ }
+ },
+ {
+ key: '_applyStencilTestIfClipping',
+ value: function _applyStencilTestIfClipping() {
+ var drawTarget = this.drawTarget();
+ if (drawTarget._isClipApplied !== this._stencilTestOn) {
+ if (drawTarget._isClipApplied) {
+ this.GL.enable(this.GL.STENCIL_TEST);
+ this._stencilTestOn = true;
+ } else {
+ this.GL.disable(this.GL.STENCIL_TEST);
+ this._stencilTestOn = false;
+ }
+ }
+ }
+ },
+ {
+ key: 'resetMatrix',
+ value: function resetMatrix() {
+ this.uMVMatrix.set(this._curCamera.cameraMatrix);
+ return this;
+ } //////////////////////////////////////////////
+ // SHADER
+ //////////////////////////////////////////////
+ /*
+ * shaders are created and cached on a per-renderer basis,
+ * on the grounds that each renderer will have its own gl context
+ * and the shader must be valid in that context.
+ */
+
+ },
+ {
+ key: '_getImmediateStrokeShader',
+ value: function _getImmediateStrokeShader() {
+ // select the stroke shader to use
+ var stroke = this.userStrokeShader;
+ if (!stroke || !stroke.isStrokeShader()) {
+ return this._getLineShader();
+ }
+ return stroke;
+ }
+ },
+ {
+ key: '_getRetainedStrokeShader',
+ value: function _getRetainedStrokeShader() {
+ return this._getImmediateStrokeShader();
+ }
+ },
+ {
+ key: '_getSphereMapping',
+ value: function _getSphereMapping(img) {
+ if (!this.sphereMapping) {
+ this.sphereMapping = this._pInst.createFilterShader(sphereMapping);
+ }
+ this.uNMatrix.inverseTranspose(this.uMVMatrix);
+ this.uNMatrix.invert3x3(this.uNMatrix);
+ this.sphereMapping.setUniform('uFovY', this._curCamera.cameraFOV);
+ this.sphereMapping.setUniform('uAspect', this._curCamera.aspectRatio);
+ this.sphereMapping.setUniform('uNewNormalMatrix', this.uNMatrix.mat3);
+ this.sphereMapping.setUniform('uSampler', img);
+ return this.sphereMapping;
+ } /*
+ * selects which fill shader should be used based on renderer state,
+ * for use with begin/endShape and immediate vertex mode.
+ */
+
+ },
+ {
+ key: '_getImmediateFillShader',
+ value: function _getImmediateFillShader() {
+ var fill = this.userFillShader;
+ if (this._useNormalMaterial) {
+ if (!fill || !fill.isNormalShader()) {
+ return this._getNormalShader();
+ }
+ }
+ if (this._enableLighting) {
+ if (!fill || !fill.isLightShader()) {
+ return this._getLightShader();
+ }
+ } else if (this._tex) {
+ if (!fill || !fill.isTextureShader()) {
+ return this._getLightShader();
+ }
+ } else if (!fill /*|| !fill.isColorShader()*/ ) {
+ return this._getImmediateModeShader();
+ }
+ return fill;
+ } /*
+ * selects which fill shader should be used based on renderer state
+ * for retained mode.
+ */
+
+ },
+ {
+ key: '_getRetainedFillShader',
+ value: function _getRetainedFillShader() {
+ if (this._useNormalMaterial) {
+ return this._getNormalShader();
+ }
+ var fill = this.userFillShader;
+ if (this._enableLighting) {
+ if (!fill || !fill.isLightShader()) {
+ return this._getLightShader();
+ }
+ } else if (this._tex) {
+ if (!fill || !fill.isTextureShader()) {
+ return this._getLightShader();
+ }
+ } else if (!fill /* || !fill.isColorShader()*/ ) {
+ return this._getColorShader();
+ }
+ return fill;
+ }
+ },
+ {
+ key: '_getImmediatePointShader',
+ value: function _getImmediatePointShader() {
+ // select the point shader to use
+ var point = this.userPointShader;
+ if (!point || !point.isPointShader()) {
+ return this._getPointShader();
+ }
+ return point;
+ }
+ },
+ {
+ key: '_getRetainedLineShader',
+ value: function _getRetainedLineShader() {
+ return this._getImmediateLineShader();
+ }
+ },
+ {
+ key: '_getLightShader',
+ value: function _getLightShader() {
+ if (!this._defaultLightShader) {
+ if (this._pInst._glAttributes.perPixelLighting) {
+ this._defaultLightShader = new _main.default.Shader(this, this._webGL2CompatibilityPrefix('vert', 'highp') + defaultShaders.phongVert, this._webGL2CompatibilityPrefix('frag', 'highp') + defaultShaders.phongFrag);
+ } else {
+ this._defaultLightShader = new _main.default.Shader(this, this._webGL2CompatibilityPrefix('vert', 'highp') + defaultShaders.lightVert, this._webGL2CompatibilityPrefix('frag', 'highp') + defaultShaders.lightTextureFrag);
+ }
+ }
+ return this._defaultLightShader;
+ }
+ },
+ {
+ key: '_getImmediateModeShader',
+ value: function _getImmediateModeShader() {
+ if (!this._defaultImmediateModeShader) {
+ this._defaultImmediateModeShader = new _main.default.Shader(this, this._webGL2CompatibilityPrefix('vert', 'mediump') + defaultShaders.immediateVert, this._webGL2CompatibilityPrefix('frag', 'mediump') + defaultShaders.vertexColorFrag);
+ }
+ return this._defaultImmediateModeShader;
+ }
+ },
+ {
+ key: '_getNormalShader',
+ value: function _getNormalShader() {
+ if (!this._defaultNormalShader) {
+ this._defaultNormalShader = new _main.default.Shader(this, this._webGL2CompatibilityPrefix('vert', 'mediump') + defaultShaders.normalVert, this._webGL2CompatibilityPrefix('frag', 'mediump') + defaultShaders.normalFrag);
+ }
+ return this._defaultNormalShader;
+ }
+ },
+ {
+ key: '_getColorShader',
+ value: function _getColorShader() {
+ if (!this._defaultColorShader) {
+ this._defaultColorShader = new _main.default.Shader(this, this._webGL2CompatibilityPrefix('vert', 'mediump') + defaultShaders.normalVert, this._webGL2CompatibilityPrefix('frag', 'mediump') + defaultShaders.basicFrag);
+ }
+ return this._defaultColorShader;
+ }
+ },
+ {
+ key: '_getPointShader',
+ value: function _getPointShader() {
+ if (!this._defaultPointShader) {
+ this._defaultPointShader = new _main.default.Shader(this, this._webGL2CompatibilityPrefix('vert', 'mediump') + defaultShaders.pointVert, this._webGL2CompatibilityPrefix('frag', 'mediump') + defaultShaders.pointFrag);
+ }
+ return this._defaultPointShader;
+ }
+ },
+ {
+ key: '_getLineShader',
+ value: function _getLineShader() {
+ if (!this._defaultLineShader) {
+ this._defaultLineShader = new _main.default.Shader(this, this._webGL2CompatibilityPrefix('vert', 'mediump') + defaultShaders.lineVert, this._webGL2CompatibilityPrefix('frag', 'mediump') + defaultShaders.lineFrag);
+ }
+ return this._defaultLineShader;
+ }
+ },
+ {
+ key: '_getFontShader',
+ value: function _getFontShader() {
+ if (!this._defaultFontShader) {
+ if (this.webglVersion === constants.WEBGL) {
+ this.GL.getExtension('OES_standard_derivatives');
+ }
+ this._defaultFontShader = new _main.default.Shader(this, this._webGL2CompatibilityPrefix('vert', 'mediump') + defaultShaders.fontVert, this._webGL2CompatibilityPrefix('frag', 'mediump') + defaultShaders.fontFrag);
+ }
+ return this._defaultFontShader;
+ }
+ },
+ {
+ key: '_webGL2CompatibilityPrefix',
+ value: function _webGL2CompatibilityPrefix(shaderType, floatPrecision) {
+ var code = '';
+ if (this.webglVersion === constants.WEBGL2) {
+ code += '#version 300 es\n#define WEBGL2\n';
+ }
+ if (shaderType === 'vert') {
+ code += '#define VERTEX_SHADER\n';
+ } else if (shaderType === 'frag') {
+ code += '#define FRAGMENT_SHADER\n';
+ }
+ if (floatPrecision) {
+ code += 'precision '.concat(floatPrecision, ' float;\n');
+ }
+ return code;
+ }
+ },
+ {
+ key: '_getEmptyTexture',
+ value: function _getEmptyTexture() {
+ if (!this._emptyTexture) {
+ // a plain white texture RGBA, full alpha, single pixel.
+ var im = new _main.default.Image(1, 1);
+ im.set(0, 0, 255);
+ this._emptyTexture = new _main.default.Texture(this, im);
+ }
+ return this._emptyTexture;
+ }
+ },
+ {
+ key: 'getTexture',
+ value: function getTexture(input) {
+ var src = input;
+ if (src instanceof _main.default.Framebuffer) {
+ src = src.color;
+ }
+ var texture = this.textures.get(src);
+ if (texture) {
+ return texture;
+ }
+ var tex = new _main.default.Texture(this, src);
+ this.textures.set(src, tex);
+ return tex;
+ } /*
+ * used in imageLight,
+ * To create a blurry image from the input non blurry img, if it doesn't already exist
+ * Add it to the diffusedTexture map,
+ * Returns the blurry image
+ * maps a p5.Image used by imageLight() to a p5.Framebuffer
+ */
+
+ },
+ {
+ key: 'getDiffusedTexture',
+ value: function getDiffusedTexture(input) {
+ var _this3 = this;
+ // if one already exists for a given input image
+ if (this.diffusedTextures.get(input) != null) {
+ return this.diffusedTextures.get(input);
+ } // if not, only then create one
+
+ var newFramebuffer;
+ // hardcoded to 200px, because it's going to be blurry and smooth
+ var smallWidth = 200;
+ var width = smallWidth;
+ var height = Math.floor(smallWidth * (input.height / input.width));
+ newFramebuffer = this._pInst.createFramebuffer({
+ width: width,
+ height: height,
+ density: 1
+ });
+ // create framebuffer is like making a new sketch, all functions on main
+ // sketch it would be available on framebuffer
+ if (!this.diffusedShader) {
+ this.diffusedShader = this._pInst.createShader(defaultShaders.imageLightVert, defaultShaders.imageLightDiffusedFrag);
+ }
+ newFramebuffer.draw(function () {
+ _this3._pInst.shader(_this3.diffusedShader);
+ _this3.diffusedShader.setUniform('environmentMap', input);
+ _this3._pInst.noStroke();
+ _this3._pInst.rectMode(constants.CENTER);
+ _this3._pInst.noLights();
+ _this3._pInst.rect(0, 0, width, height);
+ });
+ this.diffusedTextures.set(input, newFramebuffer);
+ return newFramebuffer;
+ } /*
+ * used in imageLight,
+ * To create a texture from the input non blurry image, if it doesn't already exist
+ * Creating 8 different levels of textures according to different
+ * sizes and atoring them in `levels` array
+ * Creating a new Mipmap texture with that `levels` array
+ * Storing the texture for input image in map called `specularTextures`
+ * maps the input p5.Image to a p5.MipmapTexture
+ */
+
+ },
+ {
+ key: 'getSpecularTexture',
+ value: function getSpecularTexture(input) {
+ var _this4 = this;
+ // check if already exits (there are tex of diff resolution so which one to check)
+ // currently doing the whole array
+ if (this.specularTextures.get(input) != null) {
+ return this.specularTextures.get(input);
+ } // Hardcoded size
+
+ var size = 512;
+ var tex;
+ var levels = [
+ ];
+ var framebuffer = this._pInst.createFramebuffer({
+ width: size,
+ height: size,
+ density: 1
+ });
+ var count = Math.log(size) / Math.log(2);
+ if (!this.specularShader) {
+ this.specularShader = this._pInst.createShader(defaultShaders.imageLightVert, defaultShaders.imageLightSpecularFrag);
+ } // currently only 8 levels
+ // This loop calculates 8 framebuffers of varying size of canvas
+ // and corresponding different roughness levels.
+ // Roughness increases with the decrease in canvas size,
+ // because rougher surfaces have less detailed/more blurry reflections.
+
+ var _loop = function _loop(w) {
+ framebuffer.resize(w, w);
+ var currCount = Math.log(w) / Math.log(2);
+ var roughness = 1 - currCount / count;
+ framebuffer.draw(function () {
+ _this4._pInst.shader(_this4.specularShader);
+ _this4._pInst.clear();
+ _this4.specularShader.setUniform('environmentMap', input);
+ _this4.specularShader.setUniform('roughness', roughness);
+ _this4._pInst.noStroke();
+ _this4._pInst.noLights();
+ _this4._pInst.plane(w, w);
+ });
+ levels.push(framebuffer.get().drawingContext.getImageData(0, 0, w, w));
+ };
+ for (var w = size; w >= 1; w /= 2) {
+ _loop(w);
+ } // Free the Framebuffer
+
+ framebuffer.remove();
+ tex = new _p6.MipmapTexture(this, levels, {
+ });
+ this.specularTextures.set(input, tex);
+ return tex;
+ } /**
+ * @method activeFramebuffer
+ * @private
+ * @returns {p5.Framebuffer|null} The currently active framebuffer, or null if
+ * the main canvas is the current draw target.
+ */
+
+ },
+ {
+ key: 'activeFramebuffer',
+ value: function activeFramebuffer() {
+ return this.activeFramebuffers[this.activeFramebuffers.length - 1] || null;
+ }
+ },
+ {
+ key: 'createFramebuffer',
+ value: function createFramebuffer(options) {
+ return new _main.default.Framebuffer(this, options);
+ }
+ },
+ {
+ key: '_setStrokeUniforms',
+ value: function _setStrokeUniforms(strokeShader) {
+ strokeShader.bindShader();
+ // set the uniform values
+ strokeShader.setUniform('uUseLineColor', this._useLineColor);
+ strokeShader.setUniform('uMaterialColor', this.curStrokeColor);
+ strokeShader.setUniform('uStrokeWeight', this.curStrokeWeight);
+ strokeShader.setUniform('uStrokeCap', STROKE_CAP_ENUM[this.curStrokeCap]);
+ strokeShader.setUniform('uStrokeJoin', STROKE_JOIN_ENUM[this.curStrokeJoin]);
+ }
+ },
+ {
+ key: '_setFillUniforms',
+ value: function _setFillUniforms(fillShader) {
+ var _this5 = this;
+ fillShader.bindShader();
+ this.mixedSpecularColor = _toConsumableArray(this.curSpecularColor);
+ if (this._useMetalness > 0) {
+ this.mixedSpecularColor = this.mixedSpecularColor.map(function (mixedSpecularColor, index) {
+ return _this5.curFillColor[index] * _this5._useMetalness + mixedSpecularColor * (1 - _this5._useMetalness);
+ });
+ } // TODO: optimize
+
+ fillShader.setUniform('uUseVertexColor', this._useVertexColor);
+ fillShader.setUniform('uMaterialColor', this.curFillColor);
+ fillShader.setUniform('isTexture', !!this._tex);
+ if (this._tex) {
+ fillShader.setUniform('uSampler', this._tex);
+ }
+ fillShader.setUniform('uTint', this._tint);
+ fillShader.setUniform('uHasSetAmbient', this._hasSetAmbient);
+ fillShader.setUniform('uAmbientMatColor', this.curAmbientColor);
+ fillShader.setUniform('uSpecularMatColor', this.mixedSpecularColor);
+ fillShader.setUniform('uEmissiveMatColor', this.curEmissiveColor);
+ fillShader.setUniform('uSpecular', this._useSpecularMaterial);
+ fillShader.setUniform('uEmissive', this._useEmissiveMaterial);
+ fillShader.setUniform('uShininess', this._useShininess);
+ fillShader.setUniform('metallic', this._useMetalness);
+ this._setImageLightUniforms(fillShader);
+ fillShader.setUniform('uUseLighting', this._enableLighting);
+ var pointLightCount = this.pointLightDiffuseColors.length / 3;
+ fillShader.setUniform('uPointLightCount', pointLightCount);
+ fillShader.setUniform('uPointLightLocation', this.pointLightPositions);
+ fillShader.setUniform('uPointLightDiffuseColors', this.pointLightDiffuseColors);
+ fillShader.setUniform('uPointLightSpecularColors', this.pointLightSpecularColors);
+ var directionalLightCount = this.directionalLightDiffuseColors.length / 3;
+ fillShader.setUniform('uDirectionalLightCount', directionalLightCount);
+ fillShader.setUniform('uLightingDirection', this.directionalLightDirections);
+ fillShader.setUniform('uDirectionalDiffuseColors', this.directionalLightDiffuseColors);
+ fillShader.setUniform('uDirectionalSpecularColors', this.directionalLightSpecularColors);
+ // TODO: sum these here...
+ var ambientLightCount = this.ambientLightColors.length / 3;
+ this.mixedAmbientLight = _toConsumableArray(this.ambientLightColors);
+ if (this._useMetalness > 0) {
+ this.mixedAmbientLight = this.mixedAmbientLight.map(function (ambientColors) {
+ var mixing = ambientColors - _this5._useMetalness;
+ return Math.max(0, mixing);
+ });
+ }
+ fillShader.setUniform('uAmbientLightCount', ambientLightCount);
+ fillShader.setUniform('uAmbientColor', this.mixedAmbientLight);
+ var spotLightCount = this.spotLightDiffuseColors.length / 3;
+ fillShader.setUniform('uSpotLightCount', spotLightCount);
+ fillShader.setUniform('uSpotLightAngle', this.spotLightAngle);
+ fillShader.setUniform('uSpotLightConc', this.spotLightConc);
+ fillShader.setUniform('uSpotLightDiffuseColors', this.spotLightDiffuseColors);
+ fillShader.setUniform('uSpotLightSpecularColors', this.spotLightSpecularColors);
+ fillShader.setUniform('uSpotLightLocation', this.spotLightPositions);
+ fillShader.setUniform('uSpotLightDirection', this.spotLightDirections);
+ fillShader.setUniform('uConstantAttenuation', this.constantAttenuation);
+ fillShader.setUniform('uLinearAttenuation', this.linearAttenuation);
+ fillShader.setUniform('uQuadraticAttenuation', this.quadraticAttenuation);
+ fillShader.bindTextures();
+ } // getting called from _setFillUniforms
+
+ },
+ {
+ key: '_setImageLightUniforms',
+ value: function _setImageLightUniforms(shader) {
+ //set uniform values
+ shader.setUniform('uUseImageLight', this.activeImageLight != null);
+ // true
+ if (this.activeImageLight) {
+ // this.activeImageLight has image as a key
+ // look up the texture from the diffusedTexture map
+ var diffusedLight = this.getDiffusedTexture(this.activeImageLight);
+ shader.setUniform('environmentMapDiffused', diffusedLight);
+ var specularLight = this.getSpecularTexture(this.activeImageLight);
+ // In p5js the range of shininess is >= 1,
+ // Therefore roughness range will be ([0,1]*8)*20 or [0, 160]
+ // The factor of 8 is because currently the getSpecularTexture
+ // only calculated 8 different levels of roughness
+ // The factor of 20 is just to spread up this range so that,
+ // [1, max] of shininess is converted to [0,160] of roughness
+ var roughness = 20 / this._useShininess;
+ shader.setUniform('levelOfDetail', roughness * 8);
+ shader.setUniform('environmentMapSpecular', specularLight);
+ }
+ }
+ },
+ {
+ key: '_setPointUniforms',
+ value: function _setPointUniforms(pointShader) {
+ pointShader.bindShader();
+ // set the uniform values
+ pointShader.setUniform('uMaterialColor', this.curStrokeColor);
+ // @todo is there an instance where this isn't stroke weight?
+ // should be they be same var?
+ pointShader.setUniform('uPointSize', this.pointSize * this._pInst._pixelDensity);
+ } /* Binds a buffer to the drawing context
+ * when passed more than two arguments it also updates or initializes
+ * the data associated with the buffer
+ */
+
+ },
+ {
+ key: '_bindBuffer',
+ value: function _bindBuffer(buffer, target, values, type, usage) {
+ if (!target) target = this.GL.ARRAY_BUFFER;
+ this.GL.bindBuffer(target, buffer);
+ if (values !== undefined) {
+ var data = values;
+ if (values instanceof _main.default.DataArray) {
+ data = values.dataArray();
+ } else if (!(data instanceof (type || Float32Array))) {
+ data = new (type || Float32Array) (data);
+ }
+ this.GL.bufferData(target, data, usage || this.GL.STATIC_DRAW);
+ }
+ } ///////////////////////////////
+ //// UTILITY FUNCTIONS
+ //////////////////////////////
+
+ },
+ {
+ key: '_arraysEqual',
+ value: function _arraysEqual(a, b) {
+ var aLength = a.length;
+ if (aLength !== b.length) return false;
+ return a.every(function (ai, i) {
+ return ai === b[i];
+ });
+ }
+ },
+ {
+ key: '_isTypedArray',
+ value: function _isTypedArray(arr) {
+ return [Float32Array,
+ Float64Array,
+ Int16Array,
+ Uint16Array,
+ Uint32Array].some(function (x) {
+ return arr instanceof x;
+ });
+ } /**
+ * turn a two dimensional array into one dimensional array
+ * @private
+ * @param {Array} arr 2-dimensional array
+ * @return {Array} 1-dimensional array
+ * [[1, 2, 3],[4, 5, 6]] -> [1, 2, 3, 4, 5, 6]
+ */
+
+ },
+ {
+ key: '_flatten',
+ value: function _flatten(arr) {
+ return arr.flat();
+ } /**
+ * turn a p5.Vector Array into a one dimensional number array
+ * @private
+ * @param {p5.Vector[]} arr an array of p5.Vector
+ * @return {Number[]} a one dimensional array of numbers
+ * [p5.Vector(1, 2, 3), p5.Vector(4, 5, 6)] ->
+ * [1, 2, 3, 4, 5, 6]
+ */
+
+ },
+ {
+ key: '_vToNArray',
+ value: function _vToNArray(arr) {
+ return arr.flatMap(function (item) {
+ return [item.x,
+ item.y,
+ item.z];
+ });
+ } // function to calculate BezierVertex Coefficients
+
+ },
+ {
+ key: '_bezierCoefficients',
+ value: function _bezierCoefficients(t) {
+ var t2 = t * t;
+ var t3 = t2 * t;
+ var mt = 1 - t;
+ var mt2 = mt * mt;
+ var mt3 = mt2 * mt;
+ return [mt3,
+ 3 * mt2 * t,
+ 3 * mt * t2,
+ t3];
+ } // function to calculate QuadraticVertex Coefficients
+
+ },
+ {
+ key: '_quadraticCoefficients',
+ value: function _quadraticCoefficients(t) {
+ var t2 = t * t;
+ var mt = 1 - t;
+ var mt2 = mt * mt;
+ return [mt2,
+ 2 * mt * t,
+ t2];
+ } // function to convert Bezier coordinates to Catmull Rom Splines
+
+ },
+ {
+ key: '_bezierToCatmull',
+ value: function _bezierToCatmull(w) {
+ var p1 = w[1];
+ var p2 = w[1] + (w[2] - w[0]) / this._curveTightness;
+ var p3 = w[2] - (w[3] - w[1]) / this._curveTightness;
+ var p4 = w[2];
+ var p = [
+ p1,
+ p2,
+ p3,
+ p4
+ ];
+ return p;
+ }
+ },
+ {
+ key: '_initTessy',
+ value: function _initTessy() {
+ // function called for each vertex of tesselator output
+ function vertexCallback(data, polyVertArray) {
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
+ try {
+ for (var _iterator2 = data[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var element = _step2.value;
+ polyVertArray.push(element);
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
+ }
+ }
+ function begincallback(type) {
+ if (type !== _libtess.default.primitiveType.GL_TRIANGLES) {
+ console.log('expected TRIANGLES but got type: '.concat(type));
+ }
+ }
+ function errorcallback(errno) {
+ console.log('error callback');
+ console.log('error number: '.concat(errno));
+ } // callback for when segments intersect and must be split
+
+ function combinecallback(coords, data, weight) {
+ var result = new Array(_main.default.RendererGL.prototype.tessyVertexSize).fill(0);
+ for (var i = 0; i < weight.length; i++) {
+ for (var j = 0; j < result.length; j++) {
+ if (weight[i] === 0 || !data[i]) continue;
+ result[j] += data[i][j] * weight[i];
+ }
+ }
+ return result;
+ }
+ function edgeCallback(flag) {
+ // don't really care about the flag, but need no-strip/no-fan behavior
+ }
+ var tessy = new _libtess.default.GluTesselator();
+ tessy.gluTessCallback(_libtess.default.gluEnum.GLU_TESS_VERTEX_DATA, vertexCallback);
+ tessy.gluTessCallback(_libtess.default.gluEnum.GLU_TESS_BEGIN, begincallback);
+ tessy.gluTessCallback(_libtess.default.gluEnum.GLU_TESS_ERROR, errorcallback);
+ tessy.gluTessCallback(_libtess.default.gluEnum.GLU_TESS_COMBINE, combinecallback);
+ tessy.gluTessCallback(_libtess.default.gluEnum.GLU_TESS_EDGE_FLAG, edgeCallback);
+ tessy.gluTessProperty(_libtess.default.gluEnum.GLU_TESS_WINDING_RULE, _libtess.default.windingRule.GLU_TESS_WINDING_NONZERO);
+ return tessy;
+ }
+ },
+ {
+ key: '_triangulate',
+ value: function _triangulate(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
+ // assume the face is facing the camera, letting us skip any performance
+ // issues or bugs in libtess's automatic calculation.
+ var z = contours[0] ? contours[0][2] : undefined;
+ var allSameZ = true;
+ var _iteratorNormalCompletion3 = true;
+ var _didIteratorError3 = false;
+ var _iteratorError3 = undefined;
+ try {
+ for (var _iterator3 = contours[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
+ var contour = _step3.value;
+ for (var j = 0; j < contour.length; j += _main.default.RendererGL.prototype.tessyVertexSize) {
+ if (contour[j + 2] !== z) {
+ allSameZ = false;
+ break;
+ }
+ }
+ }
+ } catch (err) {
+ _didIteratorError3 = true;
+ _iteratorError3 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
+ _iterator3.return();
+ }
+ } finally {
+ if (_didIteratorError3) {
+ throw _iteratorError3;
+ }
+ }
+ }
+ if (allSameZ) {
+ this._tessy.gluTessNormal(0, 0, 1);
+ } else {
+ // Let libtess pick a plane for us
+ this._tessy.gluTessNormal(0, 0, 0);
+ }
+ var triangleVerts = [
+ ];
+ this._tessy.gluTessBeginPolygon(triangleVerts);
+ var _iteratorNormalCompletion4 = true;
+ var _didIteratorError4 = false;
+ var _iteratorError4 = undefined;
+ try {
+ for (var _iterator4 = contours[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
+ var _contour = _step4.value;
+ this._tessy.gluTessBeginContour();
+ for (var _j = 0; _j < _contour.length; _j += _main.default.RendererGL.prototype.tessyVertexSize) {
+ var coords = _contour.slice(_j, _j + _main.default.RendererGL.prototype.tessyVertexSize);
+ this._tessy.gluTessVertex(coords, coords);
+ }
+ this._tessy.gluTessEndContour();
+ } // finish polygon
+
+ } catch (err) {
+ _didIteratorError4 = true;
+ _iteratorError4 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
+ _iterator4.return();
+ }
+ } finally {
+ if (_didIteratorError4) {
+ throw _iteratorError4;
+ }
+ }
+ }
+ this._tessy.gluTessEndPolygon();
+ return triangleVerts;
+ }
+ }
+ ]);
+ return RendererGL;
+ }(_main.default.Renderer);
+ /**
+ * ensures that p5 is using a 3d renderer. throws an error if not.
+ */
+ _main.default.prototype._assert3d = function (name) {
+ if (!this._renderer.isP3D) throw new Error(''.concat(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.'));
+ };
+ // function to initialize GLU Tesselator
+ _main.default.RendererGL.prototype.tessyVertexSize = 12;
+ var _default = _main.default.RendererGL;
+ exports.default = _default;
+ },
+ {
+ '../core/constants': 291,
+ '../core/main': 303,
+ '../core/p5.Renderer': 306,
+ './GeometryBuilder': 347,
+ './p5.Camera': 352,
+ './p5.Framebuffer': 354,
+ './p5.Matrix': 356,
+ './p5.Shader': 361,
+ './p5.Texture': 362,
+ 'core-js/modules/es.array.concat': 170,
+ 'core-js/modules/es.array.copy-within': 171,
+ 'core-js/modules/es.array.every': 172,
+ 'core-js/modules/es.array.fill': 173,
+ 'core-js/modules/es.array.flat': 177,
+ 'core-js/modules/es.array.flat-map': 176,
+ 'core-js/modules/es.array.from': 179,
+ 'core-js/modules/es.array.includes': 180,
+ 'core-js/modules/es.array.iterator': 182,
+ 'core-js/modules/es.array.map': 185,
+ 'core-js/modules/es.array.slice': 186,
+ 'core-js/modules/es.array.some': 187,
+ 'core-js/modules/es.array.unscopables.flat': 190,
+ 'core-js/modules/es.array.unscopables.flat-map': 189,
+ 'core-js/modules/es.map': 192,
+ 'core-js/modules/es.object.assign': 199,
+ 'core-js/modules/es.object.get-own-property-descriptor': 201,
+ 'core-js/modules/es.object.get-prototype-of': 203,
+ 'core-js/modules/es.object.to-string': 205,
+ 'core-js/modules/es.reflect.construct': 207,
+ 'core-js/modules/es.reflect.get': 208,
+ 'core-js/modules/es.regexp.to-string': 211,
+ 'core-js/modules/es.set': 212,
+ 'core-js/modules/es.string.includes': 214,
+ 'core-js/modules/es.string.iterator': 215,
+ 'core-js/modules/es.symbol': 227,
+ 'core-js/modules/es.symbol.description': 225,
+ 'core-js/modules/es.symbol.iterator': 226,
+ 'core-js/modules/es.typed-array.copy-within': 228,
+ 'core-js/modules/es.typed-array.every': 229,
+ 'core-js/modules/es.typed-array.fill': 230,
+ 'core-js/modules/es.typed-array.filter': 231,
+ 'core-js/modules/es.typed-array.find': 233,
+ 'core-js/modules/es.typed-array.find-index': 232,
+ 'core-js/modules/es.typed-array.float32-array': 234,
+ 'core-js/modules/es.typed-array.float64-array': 235,
+ 'core-js/modules/es.typed-array.for-each': 236,
+ 'core-js/modules/es.typed-array.includes': 237,
+ 'core-js/modules/es.typed-array.index-of': 238,
+ 'core-js/modules/es.typed-array.int16-array': 239,
+ 'core-js/modules/es.typed-array.iterator': 241,
+ 'core-js/modules/es.typed-array.join': 242,
+ 'core-js/modules/es.typed-array.last-index-of': 243,
+ 'core-js/modules/es.typed-array.map': 244,
+ 'core-js/modules/es.typed-array.reduce': 246,
+ 'core-js/modules/es.typed-array.reduce-right': 245,
+ 'core-js/modules/es.typed-array.reverse': 247,
+ 'core-js/modules/es.typed-array.set': 248,
+ 'core-js/modules/es.typed-array.slice': 249,
+ 'core-js/modules/es.typed-array.some': 250,
+ 'core-js/modules/es.typed-array.sort': 251,
+ 'core-js/modules/es.typed-array.subarray': 252,
+ 'core-js/modules/es.typed-array.to-locale-string': 253,
+ 'core-js/modules/es.typed-array.to-string': 254,
+ 'core-js/modules/es.typed-array.uint16-array': 255,
+ 'core-js/modules/es.typed-array.uint32-array': 256,
+ 'core-js/modules/es.typed-array.uint8-array': 257,
+ 'core-js/modules/es.weak-map': 259,
+ 'core-js/modules/web.dom-collections.iterator': 261,
+ 'libtess': 274,
+ 'path': 277
+ }
+ ],
+ 361: [
+ function (_dereq_, module, exports) {
+ 'use strict';
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.index-of');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.array.slice');
+ _dereq_('core-js/modules/es.array.some');
+ _dereq_('core-js/modules/es.function.name');
+ _dereq_('core-js/modules/es.object.keys');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.index-of');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.array.slice');
+ _dereq_('core-js/modules/es.array.some');
+ _dereq_('core-js/modules/es.function.name');
+ _dereq_('core-js/modules/es.object.keys');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ Object.defineProperty(exports, '__esModule', {
+ value: true
+ });
+ exports.default = void 0;
+ var _main = _interopRequireDefault(_dereq_('../core/main'));
+ function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : {
+ default:
+ obj
+ };
+ }
+ function _classCallCheck(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError('Cannot call a class as a function');
+ }
+ }
+ function _defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ('value' in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+ function _createClass(Constructor, protoProps, staticProps) {
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) _defineProperties(Constructor, staticProps);
+ return Constructor;
+ } /**
+ * This module defines the p5.Shader class
+ * @module 3D
+ * @submodule Material
+ * @for p5
+ * @requires core
+ */
+ /**
+ * A class to describe a shader program.
+ *
+ * Each `p5.Shader` object contains a shader program that runs on the graphics
+ * processing unit (GPU). Shaders can process many pixels or vertices at the
+ * same time, making them fast for many graphics tasks. They’re written in a
+ * language called
+ * GLSL
+ * and run along with the rest of the code in a sketch.
+ *
+ * A shader program consists of two files, a vertex shader and a fragment
+ * shader. The vertex shader affects where 3D geometry is drawn on the screen
+ * and the fragment shader affects color. Once the `p5.Shader` object is
+ * created, it can be used with the shader()
+ * function, as in `shader(myShader)`.
+ *
+ * Note: createShader(),
+ * createFilterShader(), and
+ * loadShader() are the recommended ways to
+ * create an instance of this class.
+ *
+ * @class p5.Shader
+ * @constructor
+ * @param {p5.RendererGL} renderer WebGL context for this shader.
+ * @param {String} vertSrc source code for the vertex shader program.
+ * @param {String} fragSrc source code for the fragment shader program.
+ *
+ * @example
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision highp float;
+ *
+ * void main() {
+ * // Set each pixel's RGBA value to yellow.
+ * gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
+ * }
+ * `;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * let myShader = createShader(vertSrc, fragSrc);
+ *
+ * // Apply the p5.Shader object.
+ * shader(myShader);
+ *
+ * // Style the drawing surface.
+ * noStroke();
+ *
+ * // Add a plane as a drawing surface.
+ * plane(100, 100);
+ *
+ * describe('A yellow square.');
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * let mandelbrot;
+ *
+ * // Load the shader and create a p5.Shader object.
+ * function preload() {
+ * mandelbrot = loadShader('assets/shader.vert', 'assets/shader.frag');
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Use the p5.Shader object.
+ * shader(mandelbrot);
+ *
+ * // Set the shader uniform p to an array.
+ * mandelbrot.setUniform('p', [-0.74364388703, 0.13182590421]);
+ *
+ * describe('A fractal image zooms in and out of focus.');
+ * }
+ *
+ * function draw() {
+ * // Set the shader uniform r to a value that oscillates between 0 and 2.
+ * mandelbrot.setUniform('r', sin(frameCount * 0.01) + 1);
+ *
+ * // Add a quad as a display surface for the shader.
+ * quad(-1, -1, 1, -1, 1, 1, -1, 1);
+ * }
+ *
+ *
+ */
+
+ _main.default.Shader = /*#__PURE__*/ function () {
+ function _class(renderer, vertSrc, fragSrc) {
+ _classCallCheck(this, _class);
+ // TODO: adapt this to not take ids, but rather,
+ // to take the source for a vertex and fragment shader
+ // to enable custom shaders at some later date
+ this._renderer = renderer;
+ this._vertSrc = vertSrc;
+ this._fragSrc = fragSrc;
+ this._vertShader = - 1;
+ this._fragShader = - 1;
+ this._glProgram = 0;
+ this._loadedAttributes = false;
+ this.attributes = {
+ };
+ this._loadedUniforms = false;
+ this.uniforms = {
+ };
+ this._bound = false;
+ this.samplers = [
+ ];
+ } /**
+ * Creates, compiles, and links the shader based on its
+ * sources for the vertex and fragment shaders (provided
+ * to the constructor). Populates known attributes and
+ * uniforms from the shader.
+ * @method init
+ * @chainable
+ * @private
+ */
+
+ _createClass(_class, [
+ {
+ key: 'init',
+ value: function init() {
+ if (this._glProgram === 0 /* or context is stale? */ ) {
+ var gl = this._renderer.GL;
+ // @todo: once custom shading is allowed,
+ // friendly error messages should be used here to share
+ // compiler and linker errors.
+ //set up the shader by
+ // 1. creating and getting a gl id for the shader program,
+ // 2. compliling its vertex & fragment sources,
+ // 3. linking the vertex and fragment shaders
+ this._vertShader = gl.createShader(gl.VERTEX_SHADER);
+ //load in our default vertex shader
+ gl.shaderSource(this._vertShader, this._vertSrc);
+ gl.compileShader(this._vertShader);
+ // if our vertex shader failed compilation?
+ if (!gl.getShaderParameter(this._vertShader, gl.COMPILE_STATUS)) {
+ _main.default._friendlyError('Yikes! An error occurred compiling the vertex shader:'.concat(gl.getShaderInfoLog(this._vertShader)));
+ return null;
+ }
+ this._fragShader = gl.createShader(gl.FRAGMENT_SHADER);
+ //load in our material frag shader
+ gl.shaderSource(this._fragShader, this._fragSrc);
+ gl.compileShader(this._fragShader);
+ // if our frag shader failed compilation?
+ if (!gl.getShaderParameter(this._fragShader, gl.COMPILE_STATUS)) {
+ _main.default._friendlyError('Darn! An error occurred compiling the fragment shader:'.concat(gl.getShaderInfoLog(this._fragShader)));
+ return null;
+ }
+ this._glProgram = gl.createProgram();
+ gl.attachShader(this._glProgram, this._vertShader);
+ gl.attachShader(this._glProgram, this._fragShader);
+ gl.linkProgram(this._glProgram);
+ if (!gl.getProgramParameter(this._glProgram, gl.LINK_STATUS)) {
+ _main.default._friendlyError('Snap! Error linking shader program: '.concat(gl.getProgramInfoLog(this._glProgram)));
+ }
+ this._loadAttributes();
+ this._loadUniforms();
+ }
+ return this;
+ } /**
+ * Copies the shader from one drawing context to another.
+ *
+ * Each `p5.Shader` object must be compiled by calling
+ * shader() before it can run. Compilation happens
+ * in a drawing context which is usually the main canvas or an instance of
+ * p5.Graphics. A shader can only be used in the
+ * context where it was compiled. The `copyToContext()` method compiles the
+ * shader again and copies it to another drawing context where it can be
+ * reused.
+ *
+ * The parameter, `context`, is the drawing context where the shader will be
+ * used. The shader can be copied to an instance of
+ * p5.Graphics, as in
+ * `myShader.copyToContext(pg)`. The shader can also be copied from a
+ * p5.Graphics object to the main canvas using
+ * the `window` variable, as in `myShader.copyToContext(window)`.
+ *
+ * Note: A p5.Shader object created with
+ * createShader(),
+ * createFilterShader(), or
+ * loadShader()
+ * can be used directly with a p5.Framebuffer
+ * object created with
+ * createFramebuffer(). Both objects
+ * have the same context as the main canvas.
+ *
+ * @method copyToContext
+ * @param {p5|p5.Graphics} context WebGL context for the copied shader.
+ * @returns {p5.Shader} new shader compiled for the target context.
+ *
+ * @example
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision mediump float;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vec2 uv = vTexCoord;
+ * vec3 color = vec3(uv.x, uv.y, min(uv.x + uv.y, 1.0));
+ * gl_FragColor = vec4(color, 1.0);\
+ * }
+ * `;
+ *
+ * let pg;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * background(200);
+ *
+ * // Create a p5.Shader object.
+ * let original = createShader(vertSrc, fragSrc);
+ *
+ * // Compile the p5.Shader object.
+ * shader(original);
+ *
+ * // Create a p5.Graphics object.
+ * pg = createGraphics(50, 50, WEBGL);
+ *
+ * // Copy the original shader to the p5.Graphics object.
+ * let copied = original.copyToContext(pg);
+ *
+ * // Apply the copied shader to the p5.Graphics object.
+ * pg.shader(copied);
+ *
+ * // Style the display surface.
+ * pg.noStroke();
+ *
+ * // Add a display surface for the shader.
+ * pg.plane(50, 50);
+ *
+ * describe('A square with purple-blue gradient on its surface drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Draw the p5.Graphics object to the main canvas.
+ * image(pg, -25, -25);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision mediump float;
+ *
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vec2 uv = vTexCoord;
+ * vec3 color = vec3(uv.x, uv.y, min(uv.x + uv.y, 1.0));
+ * gl_FragColor = vec4(color, 1.0);
+ * }
+ * `;
+ *
+ * let copied;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Graphics object.
+ * let pg = createGraphics(25, 25, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * let original = pg.createShader(vertSrc, fragSrc);
+ *
+ * // Compile the p5.Shader object.
+ * pg.shader(original);
+ *
+ * // Copy the original shader to the main canvas.
+ * copied = original.copyToContext(window);
+ *
+ * // Apply the copied shader to the main canvas.
+ * shader(copied);
+ *
+ * describe('A rotating cube with a purple-blue gradient on its surface drawn against a gray background.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Rotate around the x-, y-, and z-axes.
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * rotateZ(frameCount * 0.01);
+ *
+ * // Draw the box.
+ * box(50);
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'copyToContext',
+ value: function copyToContext(context) {
+ var shader = new _main.default.Shader(context._renderer, this._vertSrc, this._fragSrc);
+ shader.ensureCompiledOnContext(context);
+ return shader;
+ } /**
+ * @private
+ */
+
+ },
+ {
+ key: 'ensureCompiledOnContext',
+ value: function ensureCompiledOnContext(context) {
+ if (this._glProgram !== 0 && this._renderer !== context._renderer) {
+ throw new Error('The shader being run is attached to a different context. Do you need to copy it to this context first with .copyToContext()?');
+ } else if (this._glProgram === 0) {
+ this._renderer = context._renderer;
+ this.init();
+ }
+ } /**
+ * Queries the active attributes for this shader and loads
+ * their names and locations into the attributes array.
+ * @method _loadAttributes
+ * @private
+ */
+
+ },
+ {
+ key: '_loadAttributes',
+ value: function _loadAttributes() {
+ if (this._loadedAttributes) {
+ return;
+ }
+ this.attributes = {
+ };
+ var gl = this._renderer.GL;
+ var numAttributes = gl.getProgramParameter(this._glProgram, gl.ACTIVE_ATTRIBUTES);
+ for (var i = 0; i < numAttributes; ++i) {
+ var attributeInfo = gl.getActiveAttrib(this._glProgram, i);
+ var name = attributeInfo.name;
+ var location = gl.getAttribLocation(this._glProgram, name);
+ var attribute = {
+ };
+ attribute.name = name;
+ attribute.location = location;
+ attribute.index = i;
+ attribute.type = attributeInfo.type;
+ attribute.size = attributeInfo.size;
+ this.attributes[name] = attribute;
+ }
+ this._loadedAttributes = true;
+ } /**
+ * Queries the active uniforms for this shader and loads
+ * their names and locations into the uniforms array.
+ * @method _loadUniforms
+ * @private
+ */
+
+ },
+ {
+ key: '_loadUniforms',
+ value: function _loadUniforms() {
+ if (this._loadedUniforms) {
+ return;
+ }
+ var gl = this._renderer.GL;
+ // Inspect shader and cache uniform info
+ var numUniforms = gl.getProgramParameter(this._glProgram, gl.ACTIVE_UNIFORMS);
+ var samplerIndex = 0;
+ for (var i = 0; i < numUniforms; ++i) {
+ var uniformInfo = gl.getActiveUniform(this._glProgram, i);
+ var uniform = {
+ };
+ uniform.location = gl.getUniformLocation(this._glProgram, uniformInfo.name);
+ uniform.size = uniformInfo.size;
+ var uniformName = uniformInfo.name;
+ //uniforms that are arrays have their name returned as
+ //someUniform[0] which is a bit silly so we trim it
+ //off here. The size property tells us that its an array
+ //so we dont lose any information by doing this
+ if (uniformInfo.size > 1) {
+ uniformName = uniformName.substring(0, uniformName.indexOf('[0]'));
+ }
+ uniform.name = uniformName;
+ uniform.type = uniformInfo.type;
+ uniform._cachedData = undefined;
+ if (uniform.type === gl.SAMPLER_2D) {
+ uniform.samplerIndex = samplerIndex;
+ samplerIndex++;
+ this.samplers.push(uniform);
+ }
+ uniform.isArray = uniformInfo.size > 1 || uniform.type === gl.FLOAT_MAT3 || uniform.type === gl.FLOAT_MAT4 || uniform.type === gl.FLOAT_VEC2 || uniform.type === gl.FLOAT_VEC3 || uniform.type === gl.FLOAT_VEC4 || uniform.type === gl.INT_VEC2 || uniform.type === gl.INT_VEC4 || uniform.type === gl.INT_VEC3;
+ this.uniforms[uniformName] = uniform;
+ }
+ this._loadedUniforms = true;
+ }
+ },
+ {
+ key: 'compile',
+ value: function compile() {
+ // TODO
+ } /**
+ * initializes (if needed) and binds the shader program.
+ * @method bindShader
+ * @private
+ */
+
+ },
+ {
+ key: 'bindShader',
+ value: function bindShader() {
+ this.init();
+ if (!this._bound) {
+ this.useProgram();
+ this._bound = true;
+ this._setMatrixUniforms();
+ this.setUniform('uViewport', this._renderer._viewport);
+ }
+ } /**
+ * @method unbindShader
+ * @chainable
+ * @private
+ */
+
+ },
+ {
+ key: 'unbindShader',
+ value: function unbindShader() {
+ if (this._bound) {
+ this.unbindTextures();
+ //this._renderer.GL.useProgram(0); ??
+ this._bound = false;
+ }
+ return this;
+ }
+ },
+ {
+ key: 'bindTextures',
+ value: function bindTextures() {
+ var gl = this._renderer.GL;
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+ try {
+ for (var _iterator = this.samplers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var uniform = _step.value;
+ var tex = uniform.texture;
+ if (tex === undefined) {
+ // user hasn't yet supplied a texture for this slot.
+ // (or there may not be one--maybe just lighting),
+ // so we supply a default texture instead.
+ tex = this._renderer._getEmptyTexture();
+ }
+ gl.activeTexture(gl.TEXTURE0 + uniform.samplerIndex);
+ tex.bindTexture();
+ tex.update();
+ gl.uniform1i(uniform.location, uniform.samplerIndex);
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ }
+ },
+ {
+ key: 'updateTextures',
+ value: function updateTextures() {
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
+ try {
+ for (var _iterator2 = this.samplers[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var uniform = _step2.value;
+ var tex = uniform.texture;
+ if (tex) {
+ tex.update();
+ }
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
+ }
+ }
+ },
+ {
+ key: 'unbindTextures',
+ value: function unbindTextures() {
+ var _iteratorNormalCompletion3 = true;
+ var _didIteratorError3 = false;
+ var _iteratorError3 = undefined;
+ try {
+ for (var _iterator3 = this.samplers[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
+ var uniform = _step3.value;
+ this.setUniform(uniform.name, this._renderer._getEmptyTexture());
+ }
+ } catch (err) {
+ _didIteratorError3 = true;
+ _iteratorError3 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
+ _iterator3.return();
+ }
+ } finally {
+ if (_didIteratorError3) {
+ throw _iteratorError3;
+ }
+ }
+ }
+ }
+ },
+ {
+ key: '_setMatrixUniforms',
+ value: function _setMatrixUniforms() {
+ var viewMatrix = this._renderer._curCamera.cameraMatrix;
+ var projectionMatrix = this._renderer.uPMatrix;
+ var modelViewMatrix = this._renderer.uMVMatrix;
+ var modelViewProjectionMatrix = modelViewMatrix.copy();
+ modelViewProjectionMatrix.mult(projectionMatrix);
+ if (this.isStrokeShader()) {
+ this.setUniform('uPerspective', this._renderer._curCamera.useLinePerspective ? 1 : 0);
+ }
+ this.setUniform('uViewMatrix', viewMatrix.mat4);
+ this.setUniform('uProjectionMatrix', projectionMatrix.mat4);
+ this.setUniform('uModelViewMatrix', modelViewMatrix.mat4);
+ this.setUniform('uModelViewProjectionMatrix', modelViewProjectionMatrix.mat4);
+ if (this.uniforms.uNormalMatrix) {
+ this._renderer.uNMatrix.inverseTranspose(this._renderer.uMVMatrix);
+ this.setUniform('uNormalMatrix', this._renderer.uNMatrix.mat3);
+ }
+ if (this.uniforms.uCameraRotation) {
+ this._renderer.curMatrix.inverseTranspose(this._renderer._curCamera.cameraMatrix);
+ this.setUniform('uCameraRotation', this._renderer.curMatrix.mat3);
+ }
+ } /**
+ * @method useProgram
+ * @chainable
+ * @private
+ */
+
+ },
+ {
+ key: 'useProgram',
+ value: function useProgram() {
+ var gl = this._renderer.GL;
+ if (this._renderer._curShader !== this) {
+ gl.useProgram(this._glProgram);
+ this._renderer._curShader = this;
+ }
+ return this;
+ } /**
+ * Sets the shader’s uniform (global) variables.
+ *
+ * Shader programs run on the computer’s graphics processing unit (GPU).
+ * They live in part of the computer’s memory that’s completely separate
+ * from the sketch that runs them. Uniforms are global variables within a
+ * shader program. They provide a way to pass values from a sketch running
+ * on the CPU to a shader program running on the GPU.
+ *
+ * The first parameter, `uniformName`, is a string with the uniform’s name.
+ * For the shader above, `uniformName` would be `'r'`.
+ *
+ * The second parameter, `data`, is the value that should be used to set the
+ * uniform. For example, calling `myShader.setUniform('r', 0.5)` would set
+ * the `r` uniform in the shader above to `0.5`. data should match the
+ * uniform’s type. Numbers, strings, booleans, arrays, and many types of
+ * images can all be passed to a shader with `setUniform()`.
+ *
+ * @method setUniform
+ * @chainable
+ * @param {String} uniformName name of the uniform. Must match the name
+ * used in the vertex and fragment shaders.
+ * @param {Boolean|Number|Number[]|p5.Image|p5.Graphics|p5.MediaElement|p5.Texture}
+ * data value to assign to the uniform. Must match the uniform’s data type.
+ *
+ * @example
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision mediump float;
+ *
+ * uniform float r;
+ *
+ * void main() {
+ * gl_FragColor = vec4(r, 1.0, 1.0, 1.0);
+ * }
+ * `;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * let myShader = createShader(vertSrc, fragSrc);
+ *
+ * // Apply the p5.Shader object.
+ * shader(myShader);
+ *
+ * // Set the r uniform to 0.5.
+ * myShader.setUniform('r', 0.5);
+ *
+ * // Style the drawing surface.
+ * noStroke();
+ *
+ * // Add a plane as a drawing surface for the shader.
+ * plane(100, 100);
+ *
+ * describe('A cyan square.');
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision mediump float;
+ *
+ * uniform float r;
+ *
+ * void main() {
+ * gl_FragColor = vec4(r, 1.0, 1.0, 1.0);
+ * }
+ * `;
+ *
+ * let myShader;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * myShader = createShader(vertSrc, fragSrc);
+ *
+ * // Compile and apply the p5.Shader object.
+ * shader(myShader);
+ *
+ * describe('A square oscillates color between cyan and white.');
+ * }
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Style the drawing surface.
+ * noStroke();
+ *
+ * // Update the r uniform.
+ * let nextR = 0.5 * (sin(frameCount * 0.01) + 1);
+ * myShader.setUniform('r', nextR);
+ *
+ * // Add a plane as a drawing surface.
+ * plane(100, 100);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * // Note: A "uniform" is a global variable within a shader program.
+ *
+ * // Create a string with the vertex shader program.
+ * // The vertex shader is called for each vertex.
+ * let vertSrc = `
+ * precision highp float;
+ * uniform mat4 uModelViewMatrix;
+ * uniform mat4 uProjectionMatrix;
+ *
+ * attribute vec3 aPosition;
+ * attribute vec2 aTexCoord;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vTexCoord = aTexCoord;
+ * vec4 positionVec4 = vec4(aPosition, 1.0);
+ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
+ * }
+ * `;
+ *
+ * // Create a string with the fragment shader program.
+ * // The fragment shader is called for each pixel.
+ * let fragSrc = `
+ * precision highp float;
+ * uniform vec2 p;
+ * uniform float r;
+ * const int numIterations = 500;
+ * varying vec2 vTexCoord;
+ *
+ * void main() {
+ * vec2 c = p + gl_FragCoord.xy * r;
+ * vec2 z = c;
+ * float n = 0.0;
+ *
+ * for (int i = numIterations; i > 0; i--) {
+ * if (z.x * z.x + z.y * z.y > 4.0) {
+ * n = float(i) / float(numIterations);
+ * break;
+ * }
+ *
+ * z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c;
+ * }
+ *
+ * gl_FragColor = vec4(
+ * 0.5 - cos(n * 17.0) / 2.0,
+ * 0.5 - cos(n * 13.0) / 2.0,
+ * 0.5 - cos(n * 23.0) / 2.0,
+ * 1.0
+ * );
+ * }
+ * `;
+ *
+ * let mandelbrot;
+ *
+ * function setup() {
+ * createCanvas(100, 100, WEBGL);
+ *
+ * // Create a p5.Shader object.
+ * mandelbrot = createShader(vertSrc, fragSrc);
+ *
+ * // Compile and apply the p5.Shader object.
+ * shader(mandelbrot);
+ *
+ * // Set the shader uniform p to an array.
+ * // p is the center point of the Mandelbrot image.
+ * mandelbrot.setUniform('p', [-0.74364388703, 0.13182590421]);
+ *
+ * describe('A fractal image zooms in and out of focus.');
+ * }
+ *
+ * function draw() {
+ * // Set the shader uniform r to a value that oscillates
+ * // between 0 and 0.005.
+ * // r is the size of the image in Mandelbrot-space.
+ * let radius = 0.005 * (sin(frameCount * 0.01) + 1);
+ * mandelbrot.setUniform('r', radius);
+ *
+ * // Style the drawing surface.
+ * noStroke();
+ *
+ * // Add a plane as a drawing surface.
+ * plane(100, 100);
+ * }
+ *
+ *
+ */
+
+ },
+ {
+ key: 'setUniform',
+ value: function setUniform(uniformName, data) {
+ var uniform = this.uniforms[uniformName];
+ if (!uniform) {
+ return;
+ }
+ var gl = this._renderer.GL;
+ if (uniform.isArray) {
+ if (uniform._cachedData && this._renderer._arraysEqual(uniform._cachedData, data)) {
+ return;
+ } else {
+ uniform._cachedData = data.slice(0);
+ }
+ } else if (uniform._cachedData && uniform._cachedData === data) {
+ return;
+ } else {
+ if (Array.isArray(data)) {
+ uniform._cachedData = data.slice(0);
+ } else {
+ uniform._cachedData = data;
+ }
+ }
+ var location = uniform.location;
+ this.useProgram();
+ switch (uniform.type) {
+ case gl.BOOL:
+ if (data === true) {
+ gl.uniform1i(location, 1);
+ } else {
+ gl.uniform1i(location, 0);
+ }
+ break;
+ case gl.INT:
+ if (uniform.size > 1) {
+ data.length && gl.uniform1iv(location, data);
+ } else {
+ gl.uniform1i(location, data);
+ }
+ break;
+ case gl.FLOAT:
+ if (uniform.size > 1) {
+ data.length && gl.uniform1fv(location, data);
+ } else {
+ gl.uniform1f(location, data);
+ }
+ break;
+ case gl.FLOAT_MAT3:
+ gl.uniformMatrix3fv(location, false, data);
+ break;
+ case gl.FLOAT_MAT4:
+ gl.uniformMatrix4fv(location, false, data);
+ break;
+ case gl.FLOAT_VEC2:
+ if (uniform.size > 1) {
+ data.length && gl.uniform2fv(location, data);
+ } else {
+ gl.uniform2f(location, data[0], data[1]);
+ }
+ break;
+ case gl.FLOAT_VEC3:
+ if (uniform.size > 1) {
+ data.length && gl.uniform3fv(location, data);
+ } else {
+ gl.uniform3f(location, data[0], data[1], data[2]);
+ }
+ break;
+ case gl.FLOAT_VEC4:
+ if (uniform.size > 1) {
+ data.length && gl.uniform4fv(location, data);
+ } else {
+ gl.uniform4f(location, data[0], data[1], data[2], data[3]);
+ }
+ break;
+ case gl.INT_VEC2:
+ if (uniform.size > 1) {
+ data.length && gl.uniform2iv(location, data);
+ } else {
+ gl.uniform2i(location, data[0], data[1]);
+ }
+ break;
+ case gl.INT_VEC3:
+ if (uniform.size > 1) {
+ data.length && gl.uniform3iv(location, data);
+ } else {
+ gl.uniform3i(location, data[0], data[1], data[2]);
+ }
+ break;
+ case gl.INT_VEC4:
+ if (uniform.size > 1) {
+ data.length && gl.uniform4iv(location, data);
+ } else {
+ gl.uniform4i(location, data[0], data[1], data[2], data[3]);
+ }
+ break;
+ case gl.SAMPLER_2D:
+ gl.activeTexture(gl.TEXTURE0 + uniform.samplerIndex);
+ uniform.texture = data instanceof _main.default.Texture ? data : this._renderer.getTexture(data);
+ gl.uniform1i(location, uniform.samplerIndex);
+ if (uniform.texture.src.gifProperties) {
+ uniform.texture.src._animateGif(this._renderer._pInst);
+ }
+ break;
+ //@todo complete all types
+ }
+ return this;
+ } /* NONE OF THIS IS FAST OR EFFICIENT BUT BEAR WITH ME
+ *
+ * these shader "type" query methods are used by various
+ * facilities of the renderer to determine if changing
+ * the shader type for the required action (for example,
+ * do we need to load the default lighting shader if the
+ * current shader cannot handle lighting?)
+ *
+ **/
+
+ },
+ {
+ key: 'isLightShader',
+ value: function isLightShader() {
+ return [this.attributes.aNormal,
+ this.uniforms.uUseLighting,
+ this.uniforms.uAmbientLightCount,
+ this.uniforms.uDirectionalLightCount,
+ this.uniforms.uPointLightCount,
+ this.uniforms.uAmbientColor,
+ this.uniforms.uDirectionalDiffuseColors,
+ this.uniforms.uDirectionalSpecularColors,
+ this.uniforms.uPointLightLocation,
+ this.uniforms.uPointLightDiffuseColors,
+ this.uniforms.uPointLightSpecularColors,
+ this.uniforms.uLightingDirection,
+ this.uniforms.uSpecular].some(function (x) {
+ return x !== undefined;
+ });
+ }
+ },
+ {
+ key: 'isNormalShader',
+ value: function isNormalShader() {
+ return this.attributes.aNormal !== undefined;
+ }
+ },
+ {
+ key: 'isTextureShader',
+ value: function isTextureShader() {
+ return this.samplers.length > 0;
+ }
+ },
+ {
+ key: 'isColorShader',
+ value: function isColorShader() {
+ return this.attributes.aVertexColor !== undefined || this.uniforms.uMaterialColor !== undefined;
+ }
+ },
+ {
+ key: 'isTexLightShader',
+ value: function isTexLightShader() {
+ return this.isLightShader() && this.isTextureShader();
+ }
+ },
+ {
+ key: 'isStrokeShader',
+ value: function isStrokeShader() {
+ return this.uniforms.uStrokeWeight !== undefined;
+ } /**
+ * @method enableAttrib
+ * @chainable
+ * @private
+ */
+
+ },
+ {
+ key: 'enableAttrib',
+ value: function enableAttrib(attr, size, type, normalized, stride, offset) {
+ if (attr) {
+ if (typeof IS_MINIFIED === 'undefined' && this.attributes[attr.name] !== attr) {
+ console.warn('The attribute "'.concat(attr.name, '"passed to enableAttrib does not belong to this shader.'));
+ }
+ var loc = attr.location;
+ if (loc !== - 1) {
+ var gl = this._renderer.GL;
+ // Enable register even if it is disabled
+ if (!this._renderer.registerEnabled.has(loc)) {
+ gl.enableVertexAttribArray(loc);
+ // Record register availability
+ this._renderer.registerEnabled.add(loc);
+ }
+ this._renderer.GL.vertexAttribPointer(loc, size, type || gl.FLOAT, normalized || false, stride || 0, offset || 0);
+ }
+ }
+ return this;
+ } /**
+ * Once all buffers have been bound, this checks to see if there are any
+ * remaining active attributes, likely left over from previous renders,
+ * and disables them so that they don't affect rendering.
+ * @method disableRemainingAttributes
+ * @private
+ */
+
+ },
+ {
+ key: 'disableRemainingAttributes',
+ value: function disableRemainingAttributes() {
+ var _this = this;
+ var _iteratorNormalCompletion4 = true;
+ var _didIteratorError4 = false;
+ var _iteratorError4 = undefined;
+ try {
+ var _loop = function _loop() {
+ var location = _step4.value;
+ if (!Object.keys(_this.attributes).some(function (key) {
+ return _this.attributes[key].location === location;
+ })) {
+ _this._renderer.GL.disableVertexAttribArray(location);
+ _this._renderer.registerEnabled.delete(location);
+ }
+ };
+ for (var _iterator4 = this._renderer.registerEnabled.values() [Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
+ _loop();
+ }
+ } catch (err) {
+ _didIteratorError4 = true;
+ _iteratorError4 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
+ _iterator4.return();
+ }
+ } finally {
+ if (_didIteratorError4) {
+ throw _iteratorError4;
+ }
+ }
+ }
+ }
+ }
+ ]);
+ return _class;
+ }();
+ var _default = _main.default.Shader;
+ exports.default = _default;
+ },
+ {
+ '../core/main': 303,
+ 'core-js/modules/es.array.index-of': 181,
+ 'core-js/modules/es.array.iterator': 182,
+ 'core-js/modules/es.array.slice': 186,
+ 'core-js/modules/es.array.some': 187,
+ 'core-js/modules/es.function.name': 191,
+ 'core-js/modules/es.object.keys': 204,
+ 'core-js/modules/es.object.to-string': 205,
+ 'core-js/modules/es.string.iterator': 215,
+ 'core-js/modules/es.symbol': 227,
+ 'core-js/modules/es.symbol.description': 225,
+ 'core-js/modules/es.symbol.iterator': 226,
+ 'core-js/modules/web.dom-collections.iterator': 261
+ }
+ ],
+ 362: [
+ function (_dereq_, module, exports) {
+ 'use strict';
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.object.get-own-property-descriptor');
+ _dereq_('core-js/modules/es.object.get-prototype-of');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.reflect.construct');
+ _dereq_('core-js/modules/es.regexp.to-string');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/es.typed-array.uint8-array');
+ _dereq_('core-js/modules/es.typed-array.copy-within');
+ _dereq_('core-js/modules/es.typed-array.every');
+ _dereq_('core-js/modules/es.typed-array.fill');
+ _dereq_('core-js/modules/es.typed-array.filter');
+ _dereq_('core-js/modules/es.typed-array.find');
+ _dereq_('core-js/modules/es.typed-array.find-index');
+ _dereq_('core-js/modules/es.typed-array.for-each');
+ _dereq_('core-js/modules/es.typed-array.includes');
+ _dereq_('core-js/modules/es.typed-array.index-of');
+ _dereq_('core-js/modules/es.typed-array.iterator');
+ _dereq_('core-js/modules/es.typed-array.join');
+ _dereq_('core-js/modules/es.typed-array.last-index-of');
+ _dereq_('core-js/modules/es.typed-array.map');
+ _dereq_('core-js/modules/es.typed-array.reduce');
+ _dereq_('core-js/modules/es.typed-array.reduce-right');
+ _dereq_('core-js/modules/es.typed-array.reverse');
+ _dereq_('core-js/modules/es.typed-array.set');
+ _dereq_('core-js/modules/es.typed-array.slice');
+ _dereq_('core-js/modules/es.typed-array.some');
+ _dereq_('core-js/modules/es.typed-array.sort');
+ _dereq_('core-js/modules/es.typed-array.subarray');
+ _dereq_('core-js/modules/es.typed-array.to-locale-string');
+ _dereq_('core-js/modules/es.typed-array.to-string');
+ _dereq_('core-js/modules/es.weak-map');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ function _typeof2(obj) {
+ if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') {
+ _typeof2 = function _typeof2(obj) {
+ return typeof obj;
+ };
+ } else {
+ _typeof2 = function _typeof2(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
+ };
+ }
+ return _typeof2(obj);
+ }
+ function _typeof(obj) {
+ if (typeof Symbol === 'function' && _typeof2(Symbol.iterator) === 'symbol') {
+ _typeof = function _typeof(obj) {
+ return _typeof2(obj);
+ };
+ } else {
+ _typeof = function _typeof(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : _typeof2(obj);
+ };
+ }
+ return _typeof(obj);
+ }
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.typed-array.uint8-array');
+ _dereq_('core-js/modules/es.typed-array.copy-within');
+ _dereq_('core-js/modules/es.typed-array.every');
+ _dereq_('core-js/modules/es.typed-array.fill');
+ _dereq_('core-js/modules/es.typed-array.filter');
+ _dereq_('core-js/modules/es.typed-array.find');
+ _dereq_('core-js/modules/es.typed-array.find-index');
+ _dereq_('core-js/modules/es.typed-array.for-each');
+ _dereq_('core-js/modules/es.typed-array.includes');
+ _dereq_('core-js/modules/es.typed-array.index-of');
+ _dereq_('core-js/modules/es.typed-array.iterator');
+ _dereq_('core-js/modules/es.typed-array.join');
+ _dereq_('core-js/modules/es.typed-array.last-index-of');
+ _dereq_('core-js/modules/es.typed-array.map');
+ _dereq_('core-js/modules/es.typed-array.reduce');
+ _dereq_('core-js/modules/es.typed-array.reduce-right');
+ _dereq_('core-js/modules/es.typed-array.reverse');
+ _dereq_('core-js/modules/es.typed-array.set');
+ _dereq_('core-js/modules/es.typed-array.slice');
+ _dereq_('core-js/modules/es.typed-array.some');
+ _dereq_('core-js/modules/es.typed-array.sort');
+ _dereq_('core-js/modules/es.typed-array.subarray');
+ _dereq_('core-js/modules/es.typed-array.to-locale-string');
+ _dereq_('core-js/modules/es.typed-array.to-string');
+ Object.defineProperty(exports, '__esModule', {
+ value: true
+ });
+ exports.checkWebGLCapabilities = checkWebGLCapabilities;
+ exports.default = exports.MipmapTexture = void 0;
+ var _main = _interopRequireDefault(_dereq_('../core/main'));
+ var constants = _interopRequireWildcard(_dereq_('../core/constants'));
+ function _getRequireWildcardCache() {
+ if (typeof WeakMap !== 'function') return null;
+ var cache = new WeakMap();
+ _getRequireWildcardCache = function _getRequireWildcardCache() {
+ return cache;
+ };
+ return cache;
+ }
+ function _interopRequireWildcard(obj) {
+ if (obj && obj.__esModule) {
+ return obj;
+ }
+ if (obj === null || _typeof(obj) !== 'object' && typeof obj !== 'function') {
+ return {
+ default:
+ obj
+ };
+ }
+ var cache = _getRequireWildcardCache();
+ if (cache && cache.has(obj)) {
+ return cache.get(obj);
+ }
+ var newObj = {
+ };
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
+ if (desc && (desc.get || desc.set)) {
+ Object.defineProperty(newObj, key, desc);
+ } else {
+ newObj[key] = obj[key];
+ }
+ }
+ }
+ newObj.default = obj;
+ if (cache) {
+ cache.set(obj, newObj);
+ }
+ return newObj;
+ }
+ function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : {
+ default:
+ obj
+ };
+ }
+ function _inherits(subClass, superClass) {
+ if (typeof superClass !== 'function' && superClass !== null) {
+ throw new TypeError('Super expression must either be null or a function');
+ }
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
+ constructor: {
+ value: subClass,
+ writable: true,
+ configurable: true
+ }
+ });
+ if (superClass) _setPrototypeOf(subClass, superClass);
+ }
+ function _setPrototypeOf(o, p) {
+ _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
+ o.__proto__ = p;
+ return o;
+ };
+ return _setPrototypeOf(o, p);
+ }
+ function _createSuper(Derived) {
+ function isNativeReflectConstruct() {
+ if (typeof Reflect === 'undefined' || !Reflect.construct) return false;
+ if (Reflect.construct.sham) return false;
+ if (typeof Proxy === 'function') return true;
+ try {
+ Date.prototype.toString.call(Reflect.construct(Date, [
+ ], function () {
+ }));
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+ return function () {
+ var Super = _getPrototypeOf(Derived),
+ result;
+ if (isNativeReflectConstruct()) {
+ var NewTarget = _getPrototypeOf(this).constructor;
+ result = Reflect.construct(Super, arguments, NewTarget);
+ } else {
+ result = Super.apply(this, arguments);
+ }
+ return _possibleConstructorReturn(this, result);
+ };
+ }
+ function _possibleConstructorReturn(self, call) {
+ if (call && (_typeof(call) === 'object' || typeof call === 'function')) {
+ return call;
+ }
+ return _assertThisInitialized(self);
+ }
+ function _assertThisInitialized(self) {
+ if (self === void 0) {
+ throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');
+ }
+ return self;
+ }
+ function _getPrototypeOf(o) {
+ _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
+ return o.__proto__ || Object.getPrototypeOf(o);
+ };
+ return _getPrototypeOf(o);
+ }
+ function _classCallCheck(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError('Cannot call a class as a function');
+ }
+ }
+ function _defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ('value' in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+ function _createClass(Constructor, protoProps, staticProps) {
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) _defineProperties(Constructor, staticProps);
+ return Constructor;
+ } /**
+ * This module defines the p5.Texture class
+ * @module 3D
+ * @submodule Material
+ * @for p5
+ * @requires core
+ */
+ /**
+ * Texture class for WEBGL Mode
+ * @private
+ * @class p5.Texture
+ * @param {p5.RendererGL} renderer an instance of p5.RendererGL that
+ * will provide the GL context for this new p5.Texture
+ * @param {p5.Image|p5.Graphics|p5.Element|p5.MediaElement|ImageData|p5.Framebuffer|p5.FramebufferTexture|ImageData} [obj] the
+ * object containing the image data to store in the texture.
+ * @param {Object} [settings] optional A javascript object containing texture
+ * settings.
+ * @param {Number} [settings.format] optional The internal color component
+ * format for the texture. Possible values for format include gl.RGBA,
+ * gl.RGB, gl.ALPHA, gl.LUMINANCE, gl.LUMINANCE_ALPHA. Defaults to gl.RBGA
+ * @param {Number} [settings.minFilter] optional The texture minification
+ * filter setting. Possible values are gl.NEAREST or gl.LINEAR. Defaults
+ * to gl.LINEAR. Note, Mipmaps are not implemented in p5.
+ * @param {Number} [settings.magFilter] optional The texture magnification
+ * filter setting. Possible values are gl.NEAREST or gl.LINEAR. Defaults
+ * to gl.LINEAR. Note, Mipmaps are not implemented in p5.
+ * @param {Number} [settings.wrapS] optional The texture wrap settings for
+ * the s coordinate, or x axis. Possible values are gl.CLAMP_TO_EDGE,
+ * gl.REPEAT, and gl.MIRRORED_REPEAT. The mirror settings are only available
+ * when using a power of two sized texture. Defaults to gl.CLAMP_TO_EDGE
+ * @param {Number} [settings.wrapT] optional The texture wrap settings for
+ * the t coordinate, or y axis. Possible values are gl.CLAMP_TO_EDGE,
+ * gl.REPEAT, and gl.MIRRORED_REPEAT. The mirror settings are only available
+ * when using a power of two sized texture. Defaults to gl.CLAMP_TO_EDGE
+ * @param {Number} [settings.dataType] optional The data type of the texel
+ * data. Possible values are gl.UNSIGNED_BYTE or gl.FLOAT. There are more
+ * formats that are not implemented in p5. Defaults to gl.UNSIGNED_BYTE.
+ */
+
+ _main.default.Texture = /*#__PURE__*/ function () {
+ function Texture(renderer, obj, settings) {
+ _classCallCheck(this, Texture);
+ this._renderer = renderer;
+ var gl = this._renderer.GL;
+ settings = settings || {
+ };
+ this.src = obj;
+ this.glTex = undefined;
+ this.glTarget = gl.TEXTURE_2D;
+ this.glFormat = settings.format || gl.RGBA;
+ this.mipmaps = false;
+ this.glMinFilter = settings.minFilter || gl.LINEAR;
+ this.glMagFilter = settings.magFilter || gl.LINEAR;
+ this.glWrapS = settings.wrapS || gl.CLAMP_TO_EDGE;
+ this.glWrapT = settings.wrapT || gl.CLAMP_TO_EDGE;
+ this.glDataType = settings.dataType || gl.UNSIGNED_BYTE;
+ var support = checkWebGLCapabilities(renderer);
+ if (this.glFormat === gl.HALF_FLOAT && !support.halfFloat) {
+ console.log('This device does not support dataType HALF_FLOAT. Falling back to FLOAT.');
+ this.glDataType = gl.FLOAT;
+ }
+ if (this.glFormat === gl.HALF_FLOAT && (this.glMinFilter === gl.LINEAR || this.glMagFilter === gl.LINEAR) && !support.halfFloatLinear) {
+ console.log('This device does not support linear filtering for dataType FLOAT. Falling back to NEAREST.');
+ if (this.glMinFilter === gl.LINEAR) this.glMinFilter = gl.NEAREST;
+ if (this.glMagFilter === gl.LINEAR) this.glMagFilter = gl.NEAREST;
+ }
+ if (this.glFormat === gl.FLOAT && !support.float) {
+ console.log('This device does not support dataType FLOAT. Falling back to UNSIGNED_BYTE.');
+ this.glDataType = gl.UNSIGNED_BYTE;
+ }
+ if (this.glFormat === gl.FLOAT && (this.glMinFilter === gl.LINEAR || this.glMagFilter === gl.LINEAR) && !support.floatLinear) {
+ console.log('This device does not support linear filtering for dataType FLOAT. Falling back to NEAREST.');
+ if (this.glMinFilter === gl.LINEAR) this.glMinFilter = gl.NEAREST;
+ if (this.glMagFilter === gl.LINEAR) this.glMagFilter = gl.NEAREST;
+ } // used to determine if this texture might need constant updating
+ // because it is a video or gif.
+
+ this.isSrcMediaElement = typeof _main.default.MediaElement !== 'undefined' && obj instanceof _main.default.MediaElement;
+ this._videoPrevUpdateTime = 0;
+ this.isSrcHTMLElement = typeof _main.default.Element !== 'undefined' && obj instanceof _main.default.Element && !(obj instanceof _main.default.Graphics) && !(obj instanceof _main.default.Renderer);
+ this.isSrcP5Image = obj instanceof _main.default.Image;
+ this.isSrcP5Graphics = obj instanceof _main.default.Graphics;
+ this.isSrcP5Renderer = obj instanceof _main.default.Renderer;
+ this.isImageData = typeof ImageData !== 'undefined' && obj instanceof ImageData;
+ this.isFramebufferTexture = obj instanceof _main.default.FramebufferTexture;
+ var textureData = this._getTextureDataFromSource();
+ this.width = textureData.width;
+ this.height = textureData.height;
+ this.init(textureData);
+ return this;
+ }
+ _createClass(Texture, [
+ {
+ key: '_getTextureDataFromSource',
+ value: function _getTextureDataFromSource() {
+ var textureData;
+ if (this.isFramebufferTexture) {
+ textureData = this.src.rawTexture();
+ } else if (this.isSrcP5Image) {
+ // param is a p5.Image
+ textureData = this.src.canvas;
+ } else if (this.isSrcMediaElement || this.isSrcP5Graphics || this.isSrcP5Renderer || this.isSrcHTMLElement) {
+ // if param is a video HTML element
+ textureData = this.src.elt;
+ } else if (this.isImageData) {
+ textureData = this.src;
+ }
+ return textureData;
+ } /**
+ * Initializes common texture parameters, creates a gl texture,
+ * tries to upload the texture for the first time if data is
+ * already available.
+ * @private
+ * @method init
+ */
+
+ },
+ {
+ key: 'init',
+ value: function init(data) {
+ var gl = this._renderer.GL;
+ if (!this.isFramebufferTexture) {
+ this.glTex = gl.createTexture();
+ }
+ this.glWrapS = this._renderer.textureWrapX;
+ this.glWrapT = this._renderer.textureWrapY;
+ this.setWrapMode(this.glWrapS, this.glWrapT);
+ this.bindTexture();
+ //gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.glMagFilter);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.glMinFilter);
+ if (this.isFramebufferTexture) {
+ // Do nothing, the framebuffer manages its own content
+ } else if (this.width === 0 || this.height === 0 || this.isSrcMediaElement && !this.src.loadedmetadata) {
+ // assign a 1×1 empty texture initially, because data is not yet ready,
+ // so that no errors occur in gl console!
+ var tmpdata = new Uint8Array([1,
+ 1,
+ 1,
+ 1]);
+ gl.texImage2D(this.glTarget, 0, gl.RGBA, 1, 1, 0, this.glFormat, this.glDataType, tmpdata);
+ } else {
+ // data is ready: just push the texture!
+ gl.texImage2D(this.glTarget, 0, this.glFormat, this.glFormat, this.glDataType, data);
+ }
+ } /**
+ * Checks if the source data for this texture has changed (if it's
+ * easy to do so) and reuploads the texture if necessary. If it's not
+ * possible or to expensive to do a calculation to determine wheter or
+ * not the data has occurred, this method simply re-uploads the texture.
+ * @method update
+ */
+
+ },
+ {
+ key: 'update',
+ value: function update() {
+ var data = this.src;
+ if (data.width === 0 || data.height === 0) {
+ return false; // nothing to do!
+ } // FramebufferTexture instances wrap raw WebGL textures already, which
+ // don't need any extra updating, as they already live on the GPU
+
+ if (this.isFramebufferTexture) {
+ return false;
+ }
+ var textureData = this._getTextureDataFromSource();
+ var updated = false;
+ var gl = this._renderer.GL;
+ // pull texture from data, make sure width & height are appropriate
+ if (textureData.width !== this.width || textureData.height !== this.height) {
+ updated = true;
+ // make sure that if the width and height of this.src have changed
+ // for some reason, we update our metadata and upload the texture again
+ this.width = textureData.width || data.width;
+ this.height = textureData.height || data.height;
+ if (this.isSrcP5Image) {
+ data.setModified(false);
+ } else if (this.isSrcMediaElement || this.isSrcHTMLElement) {
+ // on the first frame the metadata comes in, the size will be changed
+ // from 0 to actual size, but pixels may not be available.
+ // flag for update in a future frame.
+ // if we don't do this, a paused video, for example, may not
+ // send the first frame to texture memory.
+ data.setModified(true);
+ }
+ } else if (this.isSrcP5Image) {
+ // for an image, we only update if the modified field has been set,
+ // for example, by a call to p5.Image.set
+ if (data.isModified()) {
+ updated = true;
+ data.setModified(false);
+ }
+ } else if (this.isSrcMediaElement) {
+ // for a media element (video), we'll check if the current time in
+ // the video frame matches the last time. if it doesn't match, the
+ // video has advanced or otherwise been taken to a new frame,
+ // and we need to upload it.
+ if (data.isModified()) {
+ // p5.MediaElement may have also had set/updatePixels, etc. called
+ // on it and should be updated, or may have been set for the first
+ // time!
+ updated = true;
+ data.setModified(false);
+ } else if (data.loadedmetadata) {
+ // if the meta data has been loaded, we can ask the video
+ // what it's current position (in time) is.
+ if (this._videoPrevUpdateTime !== data.time()) {
+ // update the texture in gpu mem only if the current
+ // video timestamp does not match the timestamp of the last
+ // time we uploaded this texture (and update the time we
+ // last uploaded, too)
+ this._videoPrevUpdateTime = data.time();
+ updated = true;
+ }
+ }
+ } else if (this.isImageData) {
+ if (data._dirty) {
+ data._dirty = false;
+ updated = true;
+ }
+ } else {
+ /* data instanceof p5.Graphics, probably */
+ // there is not enough information to tell if the texture can be
+ // conditionally updated; so to be safe, we just go ahead and upload it.
+ updated = true;
+ }
+ if (updated) {
+ this.bindTexture();
+ gl.texImage2D(this.glTarget, 0, this.glFormat, this.glFormat, this.glDataType, textureData);
+ }
+ return updated;
+ } /**
+ * Binds the texture to the appropriate GL target.
+ * @method bindTexture
+ */
+
+ },
+ {
+ key: 'bindTexture',
+ value: function bindTexture() {
+ // bind texture using gl context + glTarget and
+ // generated gl texture object
+ var gl = this._renderer.GL;
+ gl.bindTexture(this.glTarget, this.getTexture());
+ return this;
+ } /**
+ * Unbinds the texture from the appropriate GL target.
+ * @method unbindTexture
+ */
+
+ },
+ {
+ key: 'unbindTexture',
+ value: function unbindTexture() {
+ // unbind per above, disable texturing on glTarget
+ var gl = this._renderer.GL;
+ gl.bindTexture(this.glTarget, null);
+ }
+ },
+ {
+ key: 'getTexture',
+ value: function getTexture() {
+ if (this.isFramebufferTexture) {
+ return this.src.rawTexture();
+ } else {
+ return this.glTex;
+ }
+ } /**
+ * Sets how a texture is be interpolated when upscaled or downscaled.
+ * Nearest filtering uses nearest neighbor scaling when interpolating
+ * Linear filtering uses WebGL's linear scaling when interpolating
+ * @method setInterpolation
+ * @param {String} downScale Specifies the texture filtering when
+ * textures are shrunk. Options are LINEAR or NEAREST
+ * @param {String} upScale Specifies the texture filtering when
+ * textures are magnified. Options are LINEAR or NEAREST
+ * @todo implement mipmapping filters
+ */
+
+ },
+ {
+ key: 'setInterpolation',
+ value: function setInterpolation(downScale, upScale) {
+ var gl = this._renderer.GL;
+ this.glMinFilter = this.glFilter(downScale);
+ this.glMagFilter = this.glFilter(upScale);
+ this.bindTexture();
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.glMinFilter);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.glMagFilter);
+ this.unbindTexture();
+ }
+ },
+ {
+ key: 'glFilter',
+ value: function glFilter(filter) {
+ var gl = this._renderer.GL;
+ if (filter === constants.NEAREST) {
+ return gl.NEAREST;
+ } else {
+ return gl.LINEAR;
+ }
+ } /**
+ * Sets the texture wrapping mode. This controls how textures behave
+ * when their uv's go outside of the 0 - 1 range. There are three options:
+ * CLAMP, REPEAT, and MIRROR. REPEAT & MIRROR are only available if the texture
+ * is a power of two size (128, 256, 512, 1024, etc.).
+ * @method setWrapMode
+ * @param {String} wrapX Controls the horizontal texture wrapping behavior
+ * @param {String} wrapY Controls the vertical texture wrapping behavior
+ */
+
+ },
+ {
+ key: 'setWrapMode',
+ value: function setWrapMode(wrapX, wrapY) {
+ var gl = this._renderer.GL;
+ // for webgl 1 we need to check if the texture is power of two
+ // if it isn't we will set the wrap mode to CLAMP
+ // webgl2 will support npot REPEAT and MIRROR but we don't check for it yet
+ var isPowerOfTwo = function isPowerOfTwo(x) {
+ return (x & x - 1) === 0;
+ };
+ var textureData = this._getTextureDataFromSource();
+ var wrapWidth;
+ var wrapHeight;
+ if (textureData.naturalWidth && textureData.naturalHeight) {
+ wrapWidth = textureData.naturalWidth;
+ wrapHeight = textureData.naturalHeight;
+ } else {
+ wrapWidth = this.width;
+ wrapHeight = this.height;
+ }
+ var widthPowerOfTwo = isPowerOfTwo(wrapWidth);
+ var heightPowerOfTwo = isPowerOfTwo(wrapHeight);
+ if (wrapX === constants.REPEAT) {
+ if (this._renderer.webglVersion === constants.WEBGL2 || widthPowerOfTwo && heightPowerOfTwo) {
+ this.glWrapS = gl.REPEAT;
+ } else {
+ console.warn('You tried to set the wrap mode to REPEAT but the texture size is not a power of two. Setting to CLAMP instead');
+ this.glWrapS = gl.CLAMP_TO_EDGE;
+ }
+ } else if (wrapX === constants.MIRROR) {
+ if (this._renderer.webglVersion === constants.WEBGL2 || widthPowerOfTwo && heightPowerOfTwo) {
+ this.glWrapS = gl.MIRRORED_REPEAT;
+ } else {
+ console.warn('You tried to set the wrap mode to MIRROR but the texture size is not a power of two. Setting to CLAMP instead');
+ this.glWrapS = gl.CLAMP_TO_EDGE;
+ }
+ } else {
+ // falling back to default if didn't get a proper mode
+ this.glWrapS = gl.CLAMP_TO_EDGE;
+ }
+ if (wrapY === constants.REPEAT) {
+ if (this._renderer.webglVersion === constants.WEBGL2 || widthPowerOfTwo && heightPowerOfTwo) {
+ this.glWrapT = gl.REPEAT;
+ } else {
+ console.warn('You tried to set the wrap mode to REPEAT but the texture size is not a power of two. Setting to CLAMP instead');
+ this.glWrapT = gl.CLAMP_TO_EDGE;
+ }
+ } else if (wrapY === constants.MIRROR) {
+ if (this._renderer.webglVersion === constants.WEBGL2 || widthPowerOfTwo && heightPowerOfTwo) {
+ this.glWrapT = gl.MIRRORED_REPEAT;
+ } else {
+ console.warn('You tried to set the wrap mode to MIRROR but the texture size is not a power of two. Setting to CLAMP instead');
+ this.glWrapT = gl.CLAMP_TO_EDGE;
+ }
+ } else {
+ // falling back to default if didn't get a proper mode
+ this.glWrapT = gl.CLAMP_TO_EDGE;
+ }
+ this.bindTexture();
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, this.glWrapS);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, this.glWrapT);
+ this.unbindTexture();
+ }
+ }
+ ]);
+ return Texture;
+ }();
+ var MipmapTexture = /*#__PURE__*/ function (_p5$Texture) {
+ _inherits(MipmapTexture, _p5$Texture);
+ var _super = _createSuper(MipmapTexture);
+ function MipmapTexture(renderer, levels, settings) {
+ var _this;
+ _classCallCheck(this, MipmapTexture);
+ _this = _super.call(this, renderer, levels, settings);
+ var gl = _this._renderer.GL;
+ if (_this.glMinFilter === gl.LINEAR) {
+ _this.glMinFilter = gl.LINEAR_MIPMAP_LINEAR;
+ }
+ return _this;
+ }
+ _createClass(MipmapTexture, [
+ {
+ key: 'glFilter',
+ value: function glFilter(_filter) {
+ var gl = this._renderer.GL;
+ // TODO: support others
+ return gl.LINEAR_MIPMAP_LINEAR;
+ }
+ },
+ {
+ key: '_getTextureDataFromSource',
+ value: function _getTextureDataFromSource() {
+ return this.src;
+ }
+ },
+ {
+ key: 'init',
+ value: function init(levels) {
+ var gl = this._renderer.GL;
+ this.glTex = gl.createTexture();
+ this.bindTexture();
+ for (var level = 0; level < levels.length; level++) {
+ gl.texImage2D(this.glTarget, level, this.glFormat, this.glFormat, this.glDataType, levels[level]);
+ }
+ this.glMinFilter = gl.LINEAR_MIPMAP_LINEAR;
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.glMagFilter);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.glMinFilter);
+ this.unbindTexture();
+ }
+ },
+ {
+ key: 'update',
+ value: function update() {
+ }
+ }
+ ]);
+ return MipmapTexture;
+ }(_main.default.Texture);
+ exports.MipmapTexture = MipmapTexture;
+ function checkWebGLCapabilities(_ref) {
+ var GL = _ref.GL,
+ webglVersion = _ref.webglVersion;
+ var gl = GL;
+ var supportsFloat = webglVersion === constants.WEBGL2 ? gl.getExtension('EXT_color_buffer_float') && gl.getExtension('EXT_float_blend') : gl.getExtension('OES_texture_float');
+ var supportsFloatLinear = supportsFloat && gl.getExtension('OES_texture_float_linear');
+ var supportsHalfFloat = webglVersion === constants.WEBGL2 ? gl.getExtension('EXT_color_buffer_float') : gl.getExtension('OES_texture_half_float');
+ var supportsHalfFloatLinear = supportsHalfFloat && gl.getExtension('OES_texture_half_float_linear');
+ return {
+ float: supportsFloat,
+ floatLinear: supportsFloatLinear,
+ halfFloat: supportsHalfFloat,
+ halfFloatLinear: supportsHalfFloatLinear
+ };
+ }
+ var _default = _main.default.Texture;
+ exports.default = _default;
+ },
+ {
+ '../core/constants': 291,
+ '../core/main': 303,
+ 'core-js/modules/es.array.iterator': 182,
+ 'core-js/modules/es.object.get-own-property-descriptor': 201,
+ 'core-js/modules/es.object.get-prototype-of': 203,
+ 'core-js/modules/es.object.to-string': 205,
+ 'core-js/modules/es.reflect.construct': 207,
+ 'core-js/modules/es.regexp.to-string': 211,
+ 'core-js/modules/es.string.iterator': 215,
+ 'core-js/modules/es.symbol': 227,
+ 'core-js/modules/es.symbol.description': 225,
+ 'core-js/modules/es.symbol.iterator': 226,
+ 'core-js/modules/es.typed-array.copy-within': 228,
+ 'core-js/modules/es.typed-array.every': 229,
+ 'core-js/modules/es.typed-array.fill': 230,
+ 'core-js/modules/es.typed-array.filter': 231,
+ 'core-js/modules/es.typed-array.find': 233,
+ 'core-js/modules/es.typed-array.find-index': 232,
+ 'core-js/modules/es.typed-array.for-each': 236,
+ 'core-js/modules/es.typed-array.includes': 237,
+ 'core-js/modules/es.typed-array.index-of': 238,
+ 'core-js/modules/es.typed-array.iterator': 241,
+ 'core-js/modules/es.typed-array.join': 242,
+ 'core-js/modules/es.typed-array.last-index-of': 243,
+ 'core-js/modules/es.typed-array.map': 244,
+ 'core-js/modules/es.typed-array.reduce': 246,
+ 'core-js/modules/es.typed-array.reduce-right': 245,
+ 'core-js/modules/es.typed-array.reverse': 247,
+ 'core-js/modules/es.typed-array.set': 248,
+ 'core-js/modules/es.typed-array.slice': 249,
+ 'core-js/modules/es.typed-array.some': 250,
+ 'core-js/modules/es.typed-array.sort': 251,
+ 'core-js/modules/es.typed-array.subarray': 252,
+ 'core-js/modules/es.typed-array.to-locale-string': 253,
+ 'core-js/modules/es.typed-array.to-string': 254,
+ 'core-js/modules/es.typed-array.uint8-array': 257,
+ 'core-js/modules/es.weak-map': 259,
+ 'core-js/modules/web.dom-collections.iterator': 261
+ }
+ ],
+ 363: [
+ function (_dereq_, module, exports) {
+ 'use strict';
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.object.get-own-property-descriptor');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.regexp.exec');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/es.string.split');
+ _dereq_('core-js/modules/es.string.sub');
+ _dereq_('core-js/modules/es.weak-map');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ function _typeof2(obj) {
+ if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') {
+ _typeof2 = function _typeof2(obj) {
+ return typeof obj;
+ };
+ } else {
+ _typeof2 = function _typeof2(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
+ };
+ }
+ return _typeof2(obj);
+ }
+ function _typeof(obj) {
+ if (typeof Symbol === 'function' && _typeof2(Symbol.iterator) === 'symbol') {
+ _typeof = function _typeof(obj) {
+ return _typeof2(obj);
+ };
+ } else {
+ _typeof = function _typeof(obj) {
+ return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : _typeof2(obj);
+ };
+ }
+ return _typeof(obj);
+ }
+ _dereq_('core-js/modules/es.symbol');
+ _dereq_('core-js/modules/es.symbol.description');
+ _dereq_('core-js/modules/es.symbol.iterator');
+ _dereq_('core-js/modules/es.array.iterator');
+ _dereq_('core-js/modules/es.object.to-string');
+ _dereq_('core-js/modules/es.regexp.exec');
+ _dereq_('core-js/modules/es.string.iterator');
+ _dereq_('core-js/modules/es.string.split');
+ _dereq_('core-js/modules/es.string.sub');
+ _dereq_('core-js/modules/web.dom-collections.iterator');
+ var _main = _interopRequireDefault(_dereq_('../core/main'));
+ var constants = _interopRequireWildcard(_dereq_('../core/constants'));
+ _dereq_('./p5.Shader');
+ _dereq_('./p5.RendererGL.Retained');
+ function _getRequireWildcardCache() {
+ if (typeof WeakMap !== 'function') return null;
+ var cache = new WeakMap();
+ _getRequireWildcardCache = function _getRequireWildcardCache() {
+ return cache;
+ };
+ return cache;
+ }
+ function _interopRequireWildcard(obj) {
+ if (obj && obj.__esModule) {
+ return obj;
+ }
+ if (obj === null || _typeof(obj) !== 'object' && typeof obj !== 'function') {
+ return {
+ default:
+ obj
+ };
+ }
+ var cache = _getRequireWildcardCache();
+ if (cache && cache.has(obj)) {
+ return cache.get(obj);
+ }
+ var newObj = {
+ };
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
+ if (desc && (desc.get || desc.set)) {
+ Object.defineProperty(newObj, key, desc);
+ } else {
+ newObj[key] = obj[key];
+ }
+ }
+ }
+ newObj.default = obj;
+ if (cache) {
+ cache.set(obj, newObj);
+ }
+ return newObj;
+ }
+ function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : {
+ default:
+ obj
+ };
+ }
+ function _classCallCheck(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError('Cannot call a class as a function');
+ }
+ }
+ function _defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ('value' in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+ function _createClass(Constructor, protoProps, staticProps) {
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) _defineProperties(Constructor, staticProps);
+ return Constructor;
+ } // Text/Typography
+ // @TODO:
+
+ _main.default.RendererGL.prototype._applyTextProperties = function () {
+ //@TODO finish implementation
+ //console.error('text commands not yet implemented in webgl');
+ };
+ _main.default.RendererGL.prototype.textWidth = function (s) {
+ if (this._isOpenType()) {
+ return this._textFont._textWidth(s, this._textSize);
+ }
+ return 0; // TODO: error
+ };
+ // rendering constants
+ // the number of rows/columns dividing each glyph
+ var charGridWidth = 9;
+ var charGridHeight = charGridWidth;
+ // size of the image holding the bezier stroke info
+ var strokeImageWidth = 64;
+ var strokeImageHeight = 64;
+ // size of the image holding the stroke indices for each row/col
+ var gridImageWidth = 64;
+ var gridImageHeight = 64;
+ // size of the image holding the offset/length of each row/col stripe
+ var cellImageWidth = 64;
+ var cellImageHeight = 64;
+ /**
+ * @private
+ * @class ImageInfos
+ * @param {Integer} width
+ * @param {Integer} height
+ *
+ * the ImageInfos class holds a list of ImageDatas of a given size.
+ */
+ var ImageInfos = /*#__PURE__*/ function () {
+ function ImageInfos(width, height) {
+ _classCallCheck(this, ImageInfos);
+ 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
+ */
+
+ _createClass(ImageInfos, [
+ {
+ key: 'findImage',
+ value: function findImage(space) {
+ var imageSize = this.width * this.height;
+ if (space > imageSize) throw new Error('font is too complex to render in 3D');
+ // search through the list of images, looking for one with
+ // anough unused space.
+ var imageInfo,
+ imageData;
+ for (var ii = this.infos.length - 1; ii >= 0; --ii) {
+ var imageInfoTest = this.infos[ii];
+ if (imageInfoTest.index + space < imageSize) {
+ // found one
+ imageInfo = imageInfoTest;
+ imageData = imageInfoTest.imageData;
+ break;
+ }
+ }
+ if (!imageInfo) {
+ try {
+ // create a new image
+ imageData = new ImageData(this.width, this.height);
+ } catch (err) {
+ // for browsers that don't support ImageData constructors (ie IE11)
+ // create an ImageData using the old method
+ var canvas = document.getElementsByTagName('canvas') [0];
+ var created = !canvas;
+ if (!canvas) {
+ // create a temporary canvas
+ canvas = document.createElement('canvas');
+ canvas.style.display = 'none';
+ document.body.appendChild(canvas);
+ }
+ var ctx = canvas.getContext('2d');
+ if (ctx) {
+ imageData = ctx.createImageData(this.width, this.height);
+ }
+ if (created) {
+ // distroy the temporary canvas, if necessary
+ document.body.removeChild(canvas);
+ }
+ } // construct & dd the new image info
+
+ imageInfo = {
+ index: 0,
+ imageData: imageData
+ };
+ this.infos.push(imageInfo);
+ }
+ var index = imageInfo.index;
+ imageInfo.index += space; // move to the start of the next image
+ imageData._dirty = true;
+ return {
+ imageData: imageData,
+ index: index
+ };
+ }
+ }
+ ]);
+ return ImageInfos;
+ }();
+ /**
+ * @function setPixel
+ * @param {Object} imageInfo
+ * @param {Number} r
+ * @param {Number} g
+ * @param {Number} b
+ * @param {Number} a
+ *
+ * writes the next pixel into an indexed ImageData
+ */
+ function setPixel(imageInfo, r, g, b, a) {
+ var imageData = imageInfo.imageData;
+ var pixels = imageData.data;
+ var index = imageInfo.index++ * 4;
+ pixels[index++] = r;
+ pixels[index++] = g;
+ pixels[index++] = b;
+ pixels[index++] = a;
+ }
+ var SQRT3 = Math.sqrt(3);
+ /**
+ * @private
+ * @class FontInfo
+ * @param {Object} font an opentype.js font object
+ *
+ * contains cached images and glyph information for an opentype font
+ */
+ var FontInfo = /*#__PURE__*/ function () {
+ function FontInfo(font) {
+ _classCallCheck(this, FontInfo);
+ 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.
+ */
+
+ _createClass(FontInfo, [
+ {
+ key: 'getGlyphInfo',
+ value: function getGlyphInfo(glyph) {
+ // check the cache
+ var gi = this.glyphInfos[glyph.index];
+ if (gi) return gi;
+ // get the bounding box of the glyph from opentype.js
+ var bb = glyph.getBoundingBox();
+ var xMin = bb.x1;
+ var yMin = bb.y1;
+ var gWidth = bb.x2 - xMin;
+ var gHeight = bb.y2 - yMin;
+ var cmds = glyph.path.commands;
+ // don't bother rendering invisible glyphs
+ if (gWidth === 0 || gHeight === 0 || !cmds.length) {
+ return this.glyphInfos[glyph.index] = {
+ };
+ }
+ var i;
+ var strokes = [
+ ]; // the strokes in this glyph
+ var rows = [
+ ]; // the indices of strokes in each row
+ var cols = [
+ ]; // the indices of strokes in each column
+ for (i = charGridWidth - 1; i >= 0; --i) {
+ cols.push([]);
+ }
+ 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(xs, ys, v) {
+ var 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(rg, min, max) {
+ for (var _i = rg.length; _i-- > 0; ) {
+ var _v = rg[_i];
+ if (min > _v) min = _v;
+ if (max < _v) max = _v;
+ }
+ return {
+ min: min,
+ max: max
+ };
+ } // Expand the bounding box of the glyph by the number of cells below
+ // before rounding. Curves only partially through a cell won't be added
+ // to adjacent cells, but ones that are close will be. This helps fix
+ // small visual glitches that occur when curves are close to grid cell
+ // boundaries.
+
+ var cellOffset = 0.5;
+ // loop through the rows & columns that the curve intersects
+ // adding the curve to those slices
+ var mmX = minMax(xs, 1, 0);
+ var ixMin = Math.max(Math.floor(mmX.min * charGridWidth - cellOffset), 0);
+ var ixMax = Math.min(Math.ceil(mmX.max * charGridWidth + cellOffset), charGridWidth);
+ for (var iCol = ixMin; iCol < ixMax; ++iCol) {
+ cols[iCol].push(index);
+ }
+ var mmY = minMax(ys, 1, 0);
+ var iyMin = Math.max(Math.floor(mmY.min * charGridHeight - cellOffset), 0);
+ var iyMax = Math.min(Math.ceil(mmY.max * charGridHeight + cellOffset), charGridHeight);
+ for (var iRow = iyMin; iRow < iyMax; ++iRow) {
+ rows[iRow].push(index);
+ }
+ } /**
+ * @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;
+ return v;
+ } /**
+ * @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
+ */
+
+ var Cubic = /*#__PURE__*/ function () {
+ function Cubic(p0, c0, c1, p1) {
+ _classCallCheck(this, Cubic);
+ 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
+ */
+
+ _createClass(Cubic, [
+ {
+ key: 'toQuadratic',
+ value: function toQuadratic() {
+ return {
+ x: this.p0.x,
+ y: this.p0.y,
+ x1: this.p1.x,
+ y1: this.p1.y,
+ 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.
+ */
+
+ },
+ {
+ key: 'quadError',
+ value: function quadError() {
+ return _main.default.Vector.sub(_main.default.Vector.sub(this.p1, this.p0), _main.default.Vector.mult(_main.default.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.
+ */
+
+ },
+ {
+ key: 'split',
+ value: function split(t) {
+ var m1 = _main.default.Vector.lerp(this.p0, this.c0, t);
+ var m2 = _main.default.Vector.lerp(this.c0, this.c1, t);
+ var mm1 = _main.default.Vector.lerp(m1, m2, t);
+ this.c1 = _main.default.Vector.lerp(this.c1, this.p1, t);
+ this.c0 = _main.default.Vector.lerp(m2, this.c1, t);
+ var pt = _main.default.Vector.lerp(mm1, this.c0, t);
+ var 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.
+ */
+
+ },
+ {
+ key: 'splitInflections',
+ value: function splitInflections() {
+ var a = _main.default.Vector.sub(this.c0, this.p0);
+ var b = _main.default.Vector.sub(_main.default.Vector.sub(this.c1, this.c0), a);
+ var c = _main.default.Vector.sub(_main.default.Vector.sub(_main.default.Vector.sub(this.p1, this.c1), a), _main.default.Vector.mult(b, 2));
+ var cubics = [
+ ];
+ // find the derivative coefficients
+ var A = b.x * c.y - b.y * c.x;
+ if (A !== 0) {
+ var B = a.x * c.y - a.y * c.x;
+ var C = a.x * b.y - a.y * b.x;
+ var disc = B * B - 4 * A * C;
+ if (disc >= 0) {
+ if (A < 0) {
+ A = - A;
+ B = - B;
+ C = - C;
+ }
+ var Q = Math.sqrt(disc);
+ var t0 = ( - B - Q) / (2 * A); // the first inflection point
+ var t1 = ( - B + Q) / (2 * A); // the second inflection point
+ // test if the first inflection point lies on the curve
+ if (t0 > 0 && t0 < 1) {
+ // split at the first inflection point
+ cubics.push(this.split(t0));
+ // scale t2 into the second part
+ t1 = 1 - (1 - t1) / (1 - t0);
+ } // test if the second inflection point lies on the curve
+
+ if (t1 > 0 && t1 < 1) {
+ // split at the second inflection point
+ cubics.push(this.split(t1));
+ }
+ }
+ }
+ cubics.push(this);
+ return cubics;
+ }
+ }
+ ]);
+ return Cubic;
+ }();
+ /**
+ * @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
+ var cubics = new Cubic(new _main.default.Vector(x0, y0), new _main.default.Vector(cx0, cy0), new _main.default.Vector(cx1, cy1), new _main.default.Vector(x1, y1)).splitInflections();
+ var qs = [
+ ]; // the final list of quadratics
+ var precision = 30 / SQRT3;
+ // for each of the non-inflected pieces of the original cubic
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+ try {
+ for (var _iterator = cubics[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var cubic = _step.value;
+ // the cubic is iteratively split in 3 pieces:
+ // the first piece is accumulated in 'qs', the result.
+ // the last piece is accumulated in 'tail', temporarily.
+ // the middle piece is repeatedly split again, while necessary.
+ var tail = [
+ ];
+ var t3 = void 0;
+ for (; ; ) {
+ // calculate this cubic's precision
+ t3 = precision / cubic.quadError();
+ if (t3 >= 0.5 * 0.5 * 0.5) {
+ break; // not too bad, we're done
+ } // find a split point based on the error
+
+ var t = Math.pow(t3, 1 / 3);
+ // split the cubic in 3
+ var start = cubic.split(t);
+ var middle = cubic.split(1 - t / (1 - t));
+ qs.push(start); // the first part
+ tail.push(cubic); // the last part
+ cubic = middle; // iterate on the middle piece
+ }
+ if (t3 < 1) {
+ // a little excess error, split the middle in two
+ qs.push(cubic.split(0.5));
+ } // add the middle piece to the result
+
+ qs.push(cubic);
+ // finally add the tail, reversed, onto the result
+ Array.prototype.push.apply(qs, tail.reverse());
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ return qs;
+ } /**
+ * @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) {
+ var mx = (x0 + x1) / 2;
+ var my = (y0 + y1) / 2;
+ push([x0,
+ x1], [
+ y0,
+ y1
+ ], {
+ x: x0,
+ y: y0,
+ cx: mx,
+ cy: my
+ });
+ } /**
+ * @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;
+ }
+ var x0,
+ y0,
+ xs,
+ ys;
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
+ try {
+ for (var _iterator2 = cmds[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var cmd = _step2.value;
+ // scale the coordinates to the range 0-1
+ var x1 = (cmd.x - xMin) / gWidth;
+ var y1 = (cmd.y - yMin) / gHeight;
+ // don't bother if this point is the same as the last
+ if (samePoint(x0, y0, x1, y1)) continue;
+ switch (cmd.type) {
+ case 'M':
+ {
+ // move
+ xs = x1;
+ ys = y1;
+ break;
+ }
+ case 'L':
+ {
+ // line
+ pushLine(x0, y0, x1, y1);
+ break;
+ }
+ case 'Q':
+ {
+ // quadratic
+ var cx = (cmd.x1 - xMin) / gWidth;
+ var cy = (cmd.y1 - yMin) / gHeight;
+ push([x0,
+ x1,
+ cx], [
+ y0,
+ y1,
+ cy
+ ], {
+ x: x0,
+ y: y0,
+ cx: cx,
+ cy: cy
+ });
+ break;
+ }
+ case 'Z':
+ {
+ // end
+ if (!samePoint(x0, y0, xs, ys)) {
+ // add an extra line closing the loop, if necessary
+ pushLine(x0, y0, xs, ys);
+ strokes.push({
+ x: xs,
+ y: ys
+ });
+ } else {
+ strokes.push({
+ x: x0,
+ y: y0
+ });
+ }
+ break;
+ }
+ case 'C':
+ {
+ // cubic
+ var cx1 = (cmd.x1 - xMin) / gWidth;
+ var cy1 = (cmd.y1 - yMin) / gHeight;
+ var cx2 = (cmd.x2 - xMin) / gWidth;
+ var cy2 = (cmd.y2 - yMin) / gHeight;
+ var qs = cubicToQuadratics(x0, y0, cx1, cy1, cx2, cy2, x1, y1);
+ for (var iq = 0; iq < qs.length; iq++) {
+ var q = qs[iq].toQuadratic();
+ push([q.x,
+ q.x1,
+ q.cx], [
+ q.y,
+ q.y1,
+ q.cy
+ ], q);
+ }
+ break;
+ }
+ default:
+ throw new Error('unknown command type: '.concat(cmd.type));
+ }
+ x0 = x1;
+ y0 = y1;
+ } // allocate space for the strokes
+
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
+ }
+ var strokeCount = strokes.length;
+ var strokeImageInfo = this.strokeImageInfos.findImage(strokeCount);
+ var strokeOffset = strokeImageInfo.index;
+ // fill the stroke image
+ for (var il = 0; il < strokeCount; ++il) {
+ var s = strokes[il];
+ setPixel(strokeImageInfo, byte(s.x), byte(s.y), byte(s.cx), byte(s.cy));
+ } /**
+ * @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) {
+ var dimLength = dim.length; // the number of slices in this dimension
+ var dimImageInfo = dimImageInfos.findImage(dimLength);
+ var dimOffset = dimImageInfo.index;
+ // calculate the total number of stroke indices in this dimension
+ var totalStrokes = 0;
+ for (var id = 0; id < dimLength; ++id) {
+ totalStrokes += dim[id].length;
+ } // allocate space for the stroke indices
+
+ var cellImageInfo = cellImageInfos.findImage(totalStrokes);
+ // for each slice in the glyph
+ for (var _i2 = 0; _i2 < dimLength; ++_i2) {
+ var strokeIndices = dim[_i2];
+ var _strokeCount = strokeIndices.length;
+ var cellLineIndex = cellImageInfo.index;
+ // write the offset and count into the glyph slice image
+ setPixel(dimImageInfo, cellLineIndex >> 7, cellLineIndex & 127, _strokeCount >> 7, _strokeCount & 127);
+ // for each stroke index in that slice
+ for (var iil = 0; iil < _strokeCount; ++iil) {
+ // write the stroke index into the slice's image
+ var strokeIndex = strokeIndices[iil] + strokeOffset;
+ setPixel(cellImageInfo, strokeIndex >> 7, strokeIndex & 127, 0, 0);
+ }
+ }
+ return {
+ cellImageInfo: cellImageInfo,
+ dimOffset: dimOffset,
+ dimImageInfo: dimImageInfo
+ };
+ } // initialize the info for this glyph
+
+ gi = this.glyphInfos[glyph.index] = {
+ glyph: glyph,
+ uGlyphRect: [
+ bb.x1,
+ - bb.y1,
+ bb.x2,
+ - bb.y2
+ ],
+ strokeImageInfo: strokeImageInfo,
+ strokes: strokes,
+ colInfo: layout(cols, this.colDimImageInfos, this.colCellImageInfos),
+ rowInfo: layout(rows, this.rowDimImageInfos, this.rowCellImageInfos)
+ };
+ gi.uGridOffset = [
+ gi.colInfo.dimOffset,
+ gi.rowInfo.dimOffset
+ ];
+ return gi;
+ }
+ }
+ ]);
+ return FontInfo;
+ }();
+ _main.default.RendererGL.prototype._renderText = function (p, line, x, y, maxY) {
+ if (!this._textFont || typeof this._textFont === 'string') {
+ console.log('WEBGL: you must load and set a font before drawing text. See `loadFont` and `textFont` for more details.');
+ return;
+ }
+ if (y >= maxY || !this._doFill) {
+ return; // don't render lines beyond our maxY position
+ }
+ if (!this._isOpenType()) {
+ console.log('WEBGL: only Opentype (.otf) and Truetype (.ttf) fonts are supported');
+ return p;
+ }
+ p.push(); // fix to #803
+ // remember this state, so it can be restored later
+ var doStroke = this._doStroke;
+ var drawMode = this.drawMode;
+ this._doStroke = false;
+ this.drawMode = constants.TEXTURE;
+ // get the cached FontInfo object
+ var font = this._textFont.font;
+ var fontInfo = this._textFont._fontInfo;
+ if (!fontInfo) {
+ fontInfo = this._textFont._fontInfo = new FontInfo(font);
+ } // calculate the alignment and move/scale the view accordingly
+
+ var pos = this._textFont._handleAlignment(this, line, x, y);
+ var fontSize = this._textSize;
+ var scale = fontSize / font.unitsPerEm;
+ this.translate(pos.x, pos.y, 0);
+ this.scale(scale, scale, 1);
+ // initialize the font shader
+ var gl = this.GL;
+ var initializeShader = !this._defaultFontShader;
+ var sh = this._getFontShader();
+ sh.init();
+ sh.bindShader(); // first time around, bind the shader fully
+ if (initializeShader) {
+ // these are constants, really. just initialize them one-time.
+ sh.setUniform('uGridImageSize', [
+ gridImageWidth,
+ gridImageHeight
+ ]);
+ sh.setUniform('uCellsImageSize', [
+ cellImageWidth,
+ cellImageHeight
+ ]);
+ sh.setUniform('uStrokeImageSize', [
+ strokeImageWidth,
+ strokeImageHeight
+ ]);
+ sh.setUniform('uGridSize', [
+ charGridWidth,
+ charGridHeight
+ ]);
+ }
+ this._applyColorBlend(this.curFillColor);
+ var g = this.retainedMode.geometry['glyph'];
+ if (!g) {
+ // create the geometry for rendering a quad
+ var geom = this._textGeom = new _main.default.Geometry(1, 1, function () {
+ for (var i = 0; i <= 1; i++) {
+ for (var j = 0; j <= 1; j++) {
+ this.vertices.push(new _main.default.Vector(j, i, 0));
+ this.uvs.push(j, i);
+ }
+ }
+ });
+ geom.computeFaces().computeNormals();
+ g = this.createBuffers('glyph', geom);
+ } // bind the shader buffers
+
+ var _iteratorNormalCompletion3 = true;
+ var _didIteratorError3 = false;
+ var _iteratorError3 = undefined;
+ try {
+ for (var _iterator3 = this.retainedMode.buffers.text[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
+ var buff = _step3.value;
+ buff._prepareBuffer(g, sh);
+ }
+ } catch (err) {
+ _didIteratorError3 = true;
+ _iteratorError3 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
+ _iterator3.return();
+ }
+ } finally {
+ if (_didIteratorError3) {
+ throw _iteratorError3;
+ }
+ }
+ }
+ this._bindBuffer(g.indexBuffer, gl.ELEMENT_ARRAY_BUFFER);
+ // this will have to do for now...
+ sh.setUniform('uMaterialColor', this.curFillColor);
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
+ try {
+ var dx = 0; // the x position in the line
+ var glyphPrev = null; // the previous glyph, used for kerning
+ // fetch the glyphs in the line of text
+ var glyphs = font.stringToGlyphs(line);
+ var _iteratorNormalCompletion4 = true;
+ var _didIteratorError4 = false;
+ var _iteratorError4 = undefined;
+ try {
+ for (var _iterator4 = glyphs[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
+ var glyph = _step4.value;
+ // kern
+ if (glyphPrev) dx += font.getKerningValue(glyphPrev, glyph);
+ var gi = fontInfo.getGlyphInfo(glyph);
+ if (gi.uGlyphRect) {
+ var rowInfo = gi.rowInfo;
+ var colInfo = gi.colInfo;
+ sh.setUniform('uSamplerStrokes', gi.strokeImageInfo.imageData);
+ sh.setUniform('uSamplerRowStrokes', rowInfo.cellImageInfo.imageData);
+ sh.setUniform('uSamplerRows', rowInfo.dimImageInfo.imageData);
+ sh.setUniform('uSamplerColStrokes', colInfo.cellImageInfo.imageData);
+ sh.setUniform('uSamplerCols', colInfo.dimImageInfo.imageData);
+ sh.setUniform('uGridOffset', gi.uGridOffset);
+ sh.setUniform('uGlyphRect', gi.uGlyphRect);
+ sh.setUniform('uGlyphOffset', dx);
+ sh.bindTextures(); // afterwards, only textures need updating
+ // draw it
+ gl.drawElements(gl.TRIANGLES, 6, this.GL.UNSIGNED_SHORT, 0);
+ }
+ dx += glyph.advanceWidth;
+ glyphPrev = glyph;
+ }
+ } catch (err) {
+ _didIteratorError4 = true;
+ _iteratorError4 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
+ _iterator4.return();
+ }
+ } finally {
+ if (_didIteratorError4) {
+ throw _iteratorError4;
+ }
+ }
+ }
+ } finally {
+ // clean up
+ sh.unbindShader();
+ this._doStroke = doStroke;
+ this.drawMode = drawMode;
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
+ p.pop();
+ }
+ return p;
+ };
+ },
+ {
+ '../core/constants': 291,
+ '../core/main': 303,
+ './p5.RendererGL.Retained': 359,
+ './p5.Shader': 361,
+ 'core-js/modules/es.array.iterator': 182,
+ 'core-js/modules/es.object.get-own-property-descriptor': 201,
+ 'core-js/modules/es.object.to-string': 205,
+ 'core-js/modules/es.regexp.exec': 210,
+ 'core-js/modules/es.string.iterator': 215,
+ 'core-js/modules/es.string.split': 221,
+ 'core-js/modules/es.string.sub': 223,
+ 'core-js/modules/es.symbol': 227,
+ 'core-js/modules/es.symbol.description': 225,
+ 'core-js/modules/es.symbol.iterator': 226,
+ 'core-js/modules/es.weak-map': 259,
+ 'core-js/modules/web.dom-collections.iterator': 261
+ }
+ ],
+ 364: [
+ function (_dereq_, module, exports) {
+ module.exports = {
+ 'fes': {
+ 'autoplay': 'The media that tried to play (with \'{{src}}\') wasn\'t allowed to by this browser, most likely due to the browser\'s autoplay policy.\n\n+ More info: {{url}}',
+ 'checkUserDefinedFns': 'It seems that you may have accidentally written {{name}} instead of {{actualName}}. Please correct it if it\'s not intentional.',
+ 'fileLoadError': {
+ 'bytes': 'It looks like there was a problem loading your file. {{suggestion}}',
+ 'font': 'It looks like there was a problem loading your font. {{suggestion}}',
+ 'gif': 'There was some trouble loading your GIF. Make sure that your GIF is using 87a or 89a encoding.',
+ 'image': 'It looks like there was a problem loading your image. {{suggestion}}',
+ 'json': 'It looks like there was a problem loading your JSON file. {{suggestion}}',
+ 'large': 'If your large file isn\'t fetched successfully, we recommend splitting the file into smaller segments and fetching those.',
+ 'strings': 'It looks like there was a problem loading your text file. {{suggestion}}',
+ 'suggestion': 'Try checking if the file path ({{filePath}}) is correct, hosting the file online, or running a local server.\n\n+ More info: {{url}}',
+ 'table': 'It looks like there was a problem loading your table file. {{suggestion}}',
+ 'xml': 'It looks like there was a problem loading your XML file. {{suggestion}}'
+ },
+ 'friendlyParamError': {
+ 'type_EMPTY_VAR': '{{location}} {{func}}() was expecting {{formatType}} for the {{position}} parameter, received an empty variable instead. If not intentional, this is often a problem with scope.\n\n+ More info: {{url}}',
+ 'type_TOO_FEW_ARGUMENTS': '{{location}} {{func}}() was expecting at least {{minParams}} arguments, but received only {{argCount}}.',
+ 'type_TOO_MANY_ARGUMENTS': '{{location}} {{func}}() was expecting no more than {{maxParams}} arguments, but received {{argCount}}.',
+ 'type_WRONG_TYPE': '{{location}} {{func}}() was expecting {{formatType}} for the {{position}} parameter, received {{argType}} instead.'
+ },
+ 'globalErrors': {
+ 'reference': {
+ 'cannotAccess': '\n{{location}} "{{symbol}}" is used before declaration. Make sure you have declared the variable before using it.\n\n+ More info: {{url}}',
+ 'notDefined': '\n{{location}} "{{symbol}}" is not defined in the current scope. If you have defined it in your code, you should check its scope, spelling, and letter-casing (JavaScript is case-sensitive).\n\n+ More info: {{url}}'
+ },
+ 'stackSubseq': '└[{{location}}] \n\t Called from line {{line}} in {{func}}()\n',
+ 'stackTop': '┌[{{location}}] \n\t Error at line {{line}} in {{func}}()\n',
+ 'syntax': {
+ 'badReturnOrYield': '\nSyntax Error - return lies outside of a function. Make sure you’re not missing any brackets, so that return lies inside a function.\n\n+ More info: {{url}}',
+ 'invalidToken': '\nSyntax Error - Found a symbol that JavaScript doesn\'t recognize or didn\'t expect at it\'s place.\n\n+ More info: {{url}}',
+ 'missingInitializer': '\nSyntax Error - A const variable is declared but not initialized. In JavaScript, an initializer for a const is required. A value must be specified in the same statement in which the variable is declared. Check the line number in the error and assign the const variable a value.\n\n+ More info: {{url}}',
+ 'redeclaredVariable': '\nSyntax Error - "{{symbol}}" is being redeclared. JavaScript doesn\'t allow declaring a variable more than once. Check the line number in error for redeclaration of the variable.\n\n+ More info: {{url}}',
+ 'unexpectedToken': '\nSyntax Error - Symbol present at a place that wasn\'t expected.\nUsually this is due to a typo. Check the line number in the error for anything missing/extra.\n\n+ More info: {{url}}'
+ },
+ 'type': {
+ 'constAssign': '\n{{location}} A const variable is being re-assigned. In javascript, re-assigning a value to a constant is not allowed. If you want to re-assign new values to a variable, make sure it is declared as var or let.\n\n+ More info: {{url}}',
+ 'notfunc': '\n{{location}} "{{symbol}}" could not be called as a function.\nCheck the spelling, letter-casing (JavaScript is case-sensitive) and its type.\n\n+ More info: {{url}}',
+ 'notfuncObj': '\n{{location}} "{{symbol}}" could not be called as a function.\nVerify whether "{{obj}}" has "{{symbol}}" in it and check the spelling, letter-casing (JavaScript is case-sensitive) and its type.\n\n+ More info: {{url}}',
+ 'readFromNull': '\n{{location}} The property of null can\'t be read. In javascript the value null indicates that an object has no value.\n\n+ More info: {{url}}',
+ 'readFromUndefined': '\n{{location}} Cannot read property of undefined. Check the line number in error and make sure the variable which is being operated is not undefined.\n\n + More info: {{url}}'
+ }
+ },
+ 'libraryError': '{{location}} An error with message "{{error}}" occurred inside the p5js library when {{func}} was called. If not stated otherwise, it might be an issue with the arguments passed to {{func}}.',
+ 'location': '[{{file}}, line {{line}}]',
+ 'misspelling': '{{location}} It seems that you may have accidentally written "{{name}}" instead of "{{actualName}}". Please correct it to {{actualName}} if you wish to use the {{type}} from p5.js.',
+ 'misspelling_plural': '{{location}} It seems that you may have accidentally written "{{name}}".\nYou may have meant one of the following: \n{{suggestions}}',
+ 'misusedTopLevel': 'Did you just try to use p5.js\'s {{symbolName}} {{symbolType}}? If so, you may want to move it into your sketch\'s setup() function.\n\n+ More info: {{url}}',
+ 'positions': {
+ 'p_1': 'first',
+ 'p_10': 'tenth',
+ 'p_11': 'eleventh',
+ 'p_12': 'twelfth',
+ 'p_2': 'second',
+ 'p_3': 'third',
+ 'p_4': 'fourth',
+ 'p_5': 'fifth',
+ 'p_6': 'sixth',
+ 'p_7': 'seventh',
+ 'p_8': 'eighth',
+ 'p_9': 'ninth'
+ },
+ 'pre': '\n🌸 p5.js says: {{message}}',
+ 'sketchReaderErrors': {
+ 'reservedConst': 'you have used a p5.js reserved variable "{{symbol}}" make sure you change the variable name to something else.\n\n+ More info: {{url}}',
+ 'reservedFunc': 'you have used a p5.js reserved function "{{symbol}}" make sure you change the function name to something else.\n\n+ More info: {{url}}'
+ },
+ 'welcome': 'Welcome! This is your friendly debugger. To turn me off, switch to using p5.min.js.',
+ 'wrongPreload': '{{location}} An error with message "{{error}}" occurred inside the p5js library when "{{func}}" was called. If not stated otherwise, it might be due to "{{func}}" being called from preload. Nothing besides load calls (loadImage, loadJSON, loadFont, loadStrings, etc.) should be inside the preload function.'
+ }
+ }
+ },
+ {
+ }
+ ],
+ 365: [
+ function (_dereq_, module, exports) {
+ 'use strict';
+ Object.defineProperty(exports, '__esModule', {
+ value: true
+ });
+ exports.languages = exports.default = void 0;
+ var _translation = _interopRequireDefault(_dereq_('./en/translation'));
+ function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : {
+ default:
+ obj
+ };
+ } // Only one language is imported above. This is intentional as other languages
+ // will be hosted online and then downloaded whenever needed
+ /**
+ * Here, we define a default/fallback language which we can use without internet.
+ * You won't have to change this when adding a new language.
+ *
+ * `translation` is the namespace we are using for our initial set of strings
+ */
+
+ var _default = {
+ en: {
+ translation: _translation.default
+ }
+ };
+ /**
+ * This is a list of languages that we have added so far.
+ * If you have just added a new language (yay!), add its key to the list below
+ * (`en` is english, `es` es español). Also add its export to
+ * dev.js, which is another file in this folder.
+ */
+ exports.default = _default;
+ var languages = [
+ 'en',
+ 'es',
+ 'ko',
+ 'zh',
+ 'hi',
+ 'ja'
+ ];
+ exports.languages = languages;
+ },
+ {
+ './en/translation': 364
+ }
+ ]
+ }, {
+ }, [
+ 286
+ ]) (286)
+});
diff --git a/estudiantes/23-Simonpso/clase-15/p5.sound.min.js b/estudiantes/23-Simonpso/clase-15/p5.sound.min.js
new file mode 100644
index 00000000..44f25231
--- /dev/null
+++ b/estudiantes/23-Simonpso/clase-15/p5.sound.min.js
@@ -0,0 +1,3 @@
+/** [p5.sound] Version: 1.0.1 - 2021-05-25 */
+ !function(n){var i={};function r(t){if(i[t])return i[t].exports;var e=i[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}r.m=n,r.c=i,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)r.d(n,i,function(t){return e[t]}.bind(null,i));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=40)}([function(t,e,n){var i;void 0===(i=function(){"use strict";function l(t,e){this.isUndef(t)||1===t?this.input=this.context.createGain():1t)this.cancelScheduledValues(t),this.linearRampToValueAtTime(e,t);else{var i=this._searchAfter(t);i&&(this.cancelScheduledValues(t),i.type===u.TimelineSignal.Type.Linear?this.linearRampToValueAtTime(e,t):i.type===u.TimelineSignal.Type.Exponential&&this.exponentialRampToValueAtTime(e,t)),this.setValueAtTime(e,t)}return this},u.TimelineSignal.prototype.linearRampToValueBetween=function(t,e,n){return this.setRampPoint(e),this.linearRampToValueAtTime(t,n),this},u.TimelineSignal.prototype.exponentialRampToValueBetween=function(t,e,n){return this.setRampPoint(e),this.exponentialRampToValueAtTime(t,n),this},u.TimelineSignal.prototype._searchBefore=function(t){return this._events.get(t)},u.TimelineSignal.prototype._searchAfter=function(t){return this._events.getAfter(t)},u.TimelineSignal.prototype.getValueAtTime=function(t){t=this.toSeconds(t);var e=this._searchAfter(t),n=this._searchBefore(t),i=this._initial;if(null===n)i=this._initial;else if(n.type===u.TimelineSignal.Type.Target){var r,o=this._events.getBefore(n.time);r=null===o?this._initial:o.value,i=this._exponentialApproach(n.time,r,n.value,n.constant,t)}else i=n.type===u.TimelineSignal.Type.Curve?this._curveInterpolate(n.time,n.value,n.duration,t):null===e?n.value:e.type===u.TimelineSignal.Type.Linear?this._linearInterpolate(n.time,n.value,e.time,e.value,t):e.type===u.TimelineSignal.Type.Exponential?this._exponentialInterpolate(n.time,n.value,e.time,e.value,t):n.value;return i},u.TimelineSignal.prototype.connect=u.SignalBase.prototype.connect,u.TimelineSignal.prototype._exponentialApproach=function(t,e,n,i,r){return n+(e-n)*Math.exp(-(r-t)/i)},u.TimelineSignal.prototype._linearInterpolate=function(t,e,n,i,r){return e+(r-t)/(n-t)*(i-e)},u.TimelineSignal.prototype._exponentialInterpolate=function(t,e,n,i,r){return(e=Math.max(this._minOutput,e))*Math.pow(i/e,(r-t)/(n-t))},u.TimelineSignal.prototype._curveInterpolate=function(t,e,n,i){var r=e.length;if(t+n<=i)return e[r-1];if(i<=t)return e[0];var o=(i-t)/n,s=Math.floor((r-1)*o),a=Math.ceil((r-1)*o),u=e[s],c=e[a];return a===s?u:this._linearInterpolate(s,u,a,c,o*(r-1))},u.TimelineSignal.prototype.dispose=function(){u.Signal.prototype.dispose.call(this),u.Param.prototype.dispose.call(this),this._events.dispose(),this._events=null},u.TimelineSignal}.apply(e,i))||(t.exports=r)},function(t,e,n){var i,r;i=[n(0),n(4),n(1),n(2)],void 0===(r=function(n){"use strict";return n.Scale=function(t,e){this._outputMin=this.defaultArg(t,0),this._outputMax=this.defaultArg(e,1),this._scale=this.input=new n.Multiply(1),this._add=this.output=new n.Add(0),this._scale.connect(this._add),this._setRange()},n.extend(n.Scale,n.SignalBase),Object.defineProperty(n.Scale.prototype,"min",{get:function(){return this._outputMin},set:function(t){this._outputMin=t,this._setRange()}}),Object.defineProperty(n.Scale.prototype,"max",{get:function(){return this._outputMax},set:function(t){this._outputMax=t,this._setRange()}}),n.Scale.prototype._setRange=function(){this._add.value=this._outputMin,this._scale.value=this._outputMax-this._outputMin},n.Scale.prototype.dispose=function(){return n.prototype.dispose.call(this),this._add.dispose(),this._add=null,this._scale.dispose(),this._scale=null,this},n.Scale}.apply(e,i))||(t.exports=r)},function(t,e,n){var i,r;i=[n(0),n(16),n(30),n(31),n(12)],void 0===(r=function(e){return e.Type={Default:"number",Time:"time",Frequency:"frequency",TransportTime:"transportTime",Ticks:"ticks",NormalRange:"normalRange",AudioRange:"audioRange",Decibels:"db",Interval:"interval",BPM:"bpm",Positive:"positive",Cents:"cents",Degrees:"degrees",MIDI:"midi",BarsBeatsSixteenths:"barsBeatsSixteenths",Samples:"samples",Hertz:"hertz",Note:"note",Milliseconds:"milliseconds",Seconds:"seconds",Notation:"notation"},e.prototype.toSeconds=function(t){return this.isNumber(t)?t:this.isUndef(t)?this.now():this.isString(t)?new e.Time(t).toSeconds():t instanceof e.TimeBase?t.toSeconds():void 0},e.prototype.toFrequency=function(t){return this.isNumber(t)?t:this.isString(t)||this.isUndef(t)?new e.Frequency(t).valueOf():t instanceof e.TimeBase?t.toFrequency():void 0},e.prototype.toTicks=function(t){return this.isNumber(t)||this.isString(t)?new e.TransportTime(t).toTicks():this.isUndef(t)?e.Transport.ticks:t instanceof e.TimeBase?t.toTicks():void 0},e}.apply(e,i))||(t.exports=r)},function(t,e,n){var i,r;i=[n(0),n(18),n(9)],void 0===(r=function(n){"use strict";return window.GainNode&&!AudioContext.prototype.createGain&&(AudioContext.prototype.createGain=AudioContext.prototype.createGainNode),n.Gain=function(){var t=this.optionsObject(arguments,["gain","units"],n.Gain.defaults);this.input=this.output=this._gainNode=this.context.createGain(),this.gain=new n.Param({param:this._gainNode.gain,units:t.units,value:t.gain,convert:t.convert}),this._readOnly("gain")},n.extend(n.Gain),n.Gain.defaults={gain:1,convert:!0},n.Gain.prototype.dispose=function(){n.Param.prototype.dispose.call(this),this._gainNode.disconnect(),this._gainNode=null,this._writable("gain"),this.gain.dispose(),this.gain=null},n.prototype.createInsOuts=function(t,e){1===t?this.input=new n.Gain:1this._nextTick&&this._state;){var e=this._state.getValueAtTime(this._nextTick);if(e!==this._lastState){this._lastState=e;var n=this._state.get(this._nextTick);e===r.State.Started?(this._nextTick=n.time,this.isUndef(n.offset)||(this.ticks=n.offset),this.emit("start",n.time,this.ticks)):e===r.State.Stopped?(this.ticks=0,this.emit("stop",n.time)):e===r.State.Paused&&this.emit("pause",n.time)}var i=this._nextTick;this.frequency&&(this._nextTick+=1/this.frequency.getValueAtTime(this._nextTick),e===r.State.Started&&(this.callback(i),this.ticks++))}},r.Clock.prototype.getStateAtTime=function(t){return t=this.toSeconds(t),this._state.getValueAtTime(t)},r.Clock.prototype.dispose=function(){r.Emitter.prototype.dispose.call(this),this.context.off("tick",this._boundLoop),this._writable("frequency"),this.frequency.dispose(),this.frequency=null,this._boundLoop=null,this._nextTick=1/0,this.callback=null,this._state.dispose(),this._state=null},r.Clock}.apply(e,i))||(t.exports=r)},function(t,e,n){var i,r;i=[n(0),n(14)],void 0===(r=function(i){function t(t,e,n){if(t.input)Array.isArray(t.input)?(i.prototype.isUndef(n)&&(n=0),this.connect(t.input[n])):this.connect(t.input,e,n);else try{t instanceof AudioNode?r.call(this,t,e,n):r.call(this,t,e)}catch(e){throw new Error("error connecting to node: "+t+"\n"+e)}}var r,o;return!window.hasOwnProperty("AudioContext")&&window.hasOwnProperty("webkitAudioContext")&&(window.AudioContext=window.webkitAudioContext),i.Context=function(t){for(var e in i.Emitter.call(this),t=t||new window.AudioContext,this._context=t,this._context)this._defineProperty(this._context,e);this._latencyHint="interactive",this._lookAhead=.1,this._updateInterval=this._lookAhead/3,this._computedUpdateInterval=0,this._worker=this._createWorker(),this._constants={}},i.extend(i.Context,i.Emitter),i.Emitter.mixin(i.Context),i.Context.prototype._defineProperty=function(e,n){this.isUndef(this[n])&&Object.defineProperty(this,n,{get:function(){return"function"==typeof e[n]?e[n].bind(e):e[n]},set:function(t){e[n]=t}})},i.Context.prototype.now=function(){return this._context.currentTime},i.Context.prototype._createWorker=function(){window.URL=window.URL||window.webkitURL;var t=new Blob(["var timeoutTime = "+(1e3*this._updateInterval).toFixed(1)+";self.onmessage = function(msg){\ttimeoutTime = parseInt(msg.data);};function tick(){\tsetTimeout(tick, timeoutTime);\tself.postMessage('tick');}tick();"]),e=URL.createObjectURL(t),n=new Worker(e);return n.addEventListener("message",function(){this.emit("tick")}.bind(this)),n.addEventListener("message",function(){var t=this.now();if(this.isNumber(this._lastUpdate)){var e=t-this._lastUpdate;this._computedUpdateInterval=Math.max(e,.97*this._computedUpdateInterval)}this._lastUpdate=t}.bind(this)),n},i.Context.prototype.getConstant=function(t){if(this._constants[t])return this._constants[t];for(var e=this._context.createBuffer(1,128,this._context.sampleRate),n=e.getChannelData(0),i=0;ithis.memory){var n=this.length-this.memory;this._timeline.splice(0,n)}return this},e.Timeline.prototype.remove=function(t){if(this._iterating)this._toRemove.push(t);else{var e=this._timeline.indexOf(t);-1!==e&&this._timeline.splice(e,1)}return this},e.Timeline.prototype.get=function(t){var e=this._search(t);return-1!==e?this._timeline[e]:null},e.Timeline.prototype.peek=function(){return this._timeline[0]},e.Timeline.prototype.shift=function(){return this._timeline.shift()},e.Timeline.prototype.getAfter=function(t){var e=this._search(t);return e+1=t&&(this._timeline=[]);return this},e.Timeline.prototype.cancelBefore=function(t){if(this._timeline.length){var e=this._search(t);0<=e&&(this._timeline=this._timeline.slice(e+1))}return this},e.Timeline.prototype._search=function(t){var e=0,n=this._timeline.length,i=n;if(0t)return r;o.time>t?i=r:o.time=t;)n--;return this._iterate(e,n+1),this},e.Timeline.prototype.forEachAtTime=function(e,n){var t=this._search(e);return-1!==t&&this._iterate(function(t){t.time===e&&n(t)},0,t),this},e.Timeline.prototype.dispose=function(){e.prototype.dispose.call(this),this._timeline=null,this._toRemove=null},e.Timeline}.apply(e,i))||(t.exports=r)},function(t,e,n){var i,r;i=[n(0),n(1),n(2)],void 0===(r=function(t){"use strict";return t.Negate=function(){this._multiply=this.input=this.output=new t.Multiply(-1)},t.extend(t.Negate,t.SignalBase),t.Negate.prototype.dispose=function(){return t.prototype.dispose.call(this),this._multiply.dispose(),this._multiply=null,this},t.Negate}.apply(e,i))||(t.exports=r)},function(t,e,n){var i,r;i=[n(0),n(2),n(1),n(6)],void 0===(r=function(t){"use strict";return t.GreaterThanZero=function(){this._thresh=this.output=new t.WaveShaper(function(t){return t<=0?0:1},127),this._scale=this.input=new t.Multiply(1e4),this._scale.connect(this._thresh)},t.extend(t.GreaterThanZero,t.SignalBase),t.GreaterThanZero.prototype.dispose=function(){return t.prototype.dispose.call(this),this._scale.dispose(),this._scale=null,this._thresh.dispose(),this._thresh=null,this},t.GreaterThanZero}.apply(e,i))||(t.exports=r)},function(t,e,n){var i,r,o;r=[],void 0===(o="function"==typeof(i=function(){var s=function(t,e){this._dragged=!1,this._element=t,this._bindedMove=this._moved.bind(this),this._bindedEnd=this._ended.bind(this,e),t.addEventListener("touchstart",this._bindedEnd),t.addEventListener("touchmove",this._bindedMove),t.addEventListener("touchend",this._bindedEnd),t.addEventListener("mouseup",this._bindedEnd)};function o(t){return"running"===t.state}return s.prototype._moved=function(t){this._dragged=!0},s.prototype._ended=function(t){this._dragged||function(t){var e=t.createBuffer(1,1,t.sampleRate),n=t.createBufferSource();n.buffer=e,n.connect(t.destination),n.start(0),t.resume&&t.resume()}(t),this._dragged=!1},s.prototype.dispose=function(){this._element.removeEventListener("touchstart",this._bindedEnd),this._element.removeEventListener("touchmove",this._bindedMove),this._element.removeEventListener("touchend",this._bindedEnd),this._element.removeEventListener("mouseup",this._bindedEnd),this._bindedMove=null,this._bindedEnd=null,this._element=null},function(e,t,n){var i=new Promise(function(t){!function(e,n){o(e)?n():function t(){o(e)?n():(requestAnimationFrame(t),e.resume&&e.resume())}()}(e,t)}),r=[];return function t(e,n,i){if(Array.isArray(e)||NodeList&&e instanceof NodeList)for(var r=0;r= this._length) {\n this._writeIndex = 0;\n } // For excessive frames, the buffer will be overwritten.\n\n\n this._framesAvailable += sourceLength;\n\n if (this._framesAvailable > this._length) {\n this._framesAvailable = this._length;\n }\n }\n /**\n * Pull data out of buffer and fill a given sequence of Float32Arrays.\n *\n * @param {array} arraySequence An array of Float32Arrays.\n */\n\n }, {\n key: "pull",\n value: function pull(arraySequence) {\n // The channel count of arraySequence and the length of each channel must\n // match with this buffer obejct.\n // If the FIFO is completely empty, do nothing.\n if (this._framesAvailable === 0) {\n return;\n }\n\n var destinationLength = arraySequence[0].length; // Transfer data from the internal buffer to the |arraySequence| storage.\n\n for (var i = 0; i < destinationLength; ++i) {\n var readIndex = (this._readIndex + i) % this._length;\n\n for (var channel = 0; channel < this._channelCount; ++channel) {\n arraySequence[channel][i] = this._channelData[channel][readIndex];\n }\n }\n\n this._readIndex += destinationLength;\n\n if (this._readIndex >= this._length) {\n this._readIndex = 0;\n }\n\n this._framesAvailable -= destinationLength;\n\n if (this._framesAvailable < 0) {\n this._framesAvailable = 0;\n }\n }\n }, {\n key: "framesAvailable",\n get: function get() {\n return this._framesAvailable;\n }\n }]);\n\n return RingBuffer;\n }()\n}["default"];\n\nvar RecorderProcessor =\n/*#__PURE__*/\nfunction (_AudioWorkletProcesso) {\n _inherits(RecorderProcessor, _AudioWorkletProcesso);\n\n function RecorderProcessor(options) {\n var _this;\n\n _classCallCheck(this, RecorderProcessor);\n\n _this = _possibleConstructorReturn(this, _getPrototypeOf(RecorderProcessor).call(this));\n var processorOptions = options.processorOptions || {};\n _this.numOutputChannels = options.outputChannelCount || 2;\n _this.numInputChannels = processorOptions.numInputChannels || 2;\n _this.bufferSize = processorOptions.bufferSize || 1024;\n _this.recording = false;\n\n _this.clear();\n\n _this.port.onmessage = function (event) {\n var data = event.data;\n\n if (data.name === \'start\') {\n _this.record(data.duration);\n } else if (data.name === \'stop\') {\n _this.stop();\n }\n };\n\n return _this;\n }\n\n _createClass(RecorderProcessor, [{\n key: "process",\n value: function process(inputs) {\n if (!this.recording) {\n return true;\n } else if (this.sampleLimit && this.recordedSamples >= this.sampleLimit) {\n this.stop();\n return true;\n }\n\n var input = inputs[0];\n this.inputRingBuffer.push(input);\n\n if (this.inputRingBuffer.framesAvailable >= this.bufferSize) {\n this.inputRingBuffer.pull(this.inputRingBufferArraySequence);\n\n for (var channel = 0; channel < this.numOutputChannels; ++channel) {\n var inputChannelCopy = this.inputRingBufferArraySequence[channel].slice();\n\n if (channel === 0) {\n this.leftBuffers.push(inputChannelCopy);\n\n if (this.numInputChannels === 1) {\n this.rightBuffers.push(inputChannelCopy);\n }\n } else if (channel === 1 && this.numInputChannels > 1) {\n this.rightBuffers.push(inputChannelCopy);\n }\n }\n\n this.recordedSamples += this.bufferSize;\n }\n\n return true;\n }\n }, {\n key: "record",\n value: function record(duration) {\n if (duration) {\n this.sampleLimit = Math.round(duration * sampleRate);\n }\n\n this.recording = true;\n }\n }, {\n key: "stop",\n value: function stop() {\n this.recording = false;\n var buffers = this.getBuffers();\n var leftBuffer = buffers[0].buffer;\n var rightBuffer = buffers[1].buffer;\n this.port.postMessage({\n name: \'buffers\',\n leftBuffer: leftBuffer,\n rightBuffer: rightBuffer\n }, [leftBuffer, rightBuffer]);\n this.clear();\n }\n }, {\n key: "getBuffers",\n value: function getBuffers() {\n var buffers = [];\n buffers.push(this.mergeBuffers(this.leftBuffers));\n buffers.push(this.mergeBuffers(this.rightBuffers));\n return buffers;\n }\n }, {\n key: "mergeBuffers",\n value: function mergeBuffers(channelBuffer) {\n var result = new Float32Array(this.recordedSamples);\n var offset = 0;\n var lng = channelBuffer.length;\n\n for (var i = 0; i < lng; i++) {\n var buffer = channelBuffer[i];\n result.set(buffer, offset);\n offset += buffer.length;\n }\n\n return result;\n }\n }, {\n key: "clear",\n value: function clear() {\n var _this2 = this;\n\n this.leftBuffers = [];\n this.rightBuffers = [];\n this.inputRingBuffer = new RingBuffer(this.bufferSize, this.numInputChannels);\n this.inputRingBufferArraySequence = new Array(this.numInputChannels).fill(null).map(function () {\n return new Float32Array(_this2.bufferSize);\n });\n this.recordedSamples = 0;\n this.sampleLimit = null;\n }\n }]);\n\n return RecorderProcessor;\n}(_wrapNativeSuper(AudioWorkletProcessor));\n\nregisterProcessor(processorNames.recorderProcessor, RecorderProcessor);'},function(t,e,n){"use strict";n.r(e),e.default='function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn\'t been initialised - super() hasn\'t been called"); } return self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); }\n\nfunction isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _construct(Parent, args, Class) { if (isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }\n\nfunction _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n// import dependencies via preval.require so that they\'re available as values at compile time\nvar processorNames = {\n "recorderProcessor": "recorder-processor",\n "soundFileProcessor": "sound-file-processor",\n "amplitudeProcessor": "amplitude-processor"\n};\nvar RingBuffer = {\n "default":\n /*#__PURE__*/\n function () {\n /**\n * @constructor\n * @param {number} length Buffer length in frames.\n * @param {number} channelCount Buffer channel count.\n */\n function RingBuffer(length, channelCount) {\n _classCallCheck(this, RingBuffer);\n\n this._readIndex = 0;\n this._writeIndex = 0;\n this._framesAvailable = 0;\n this._channelCount = channelCount;\n this._length = length;\n this._channelData = [];\n\n for (var i = 0; i < this._channelCount; ++i) {\n this._channelData[i] = new Float32Array(length);\n }\n }\n /**\n * Getter for Available frames in buffer.\n *\n * @return {number} Available frames in buffer.\n */\n\n\n _createClass(RingBuffer, [{\n key: "push",\n\n /**\n * Push a sequence of Float32Arrays to buffer.\n *\n * @param {array} arraySequence A sequence of Float32Arrays.\n */\n value: function push(arraySequence) {\n // The channel count of arraySequence and the length of each channel must\n // match with this buffer obejct.\n // Transfer data from the |arraySequence| storage to the internal buffer.\n var sourceLength = arraySequence[0] ? arraySequence[0].length : 0;\n\n for (var i = 0; i < sourceLength; ++i) {\n var writeIndex = (this._writeIndex + i) % this._length;\n\n for (var channel = 0; channel < this._channelCount; ++channel) {\n this._channelData[channel][writeIndex] = arraySequence[channel][i];\n }\n }\n\n this._writeIndex += sourceLength;\n\n if (this._writeIndex >= this._length) {\n this._writeIndex = 0;\n } // For excessive frames, the buffer will be overwritten.\n\n\n this._framesAvailable += sourceLength;\n\n if (this._framesAvailable > this._length) {\n this._framesAvailable = this._length;\n }\n }\n /**\n * Pull data out of buffer and fill a given sequence of Float32Arrays.\n *\n * @param {array} arraySequence An array of Float32Arrays.\n */\n\n }, {\n key: "pull",\n value: function pull(arraySequence) {\n // The channel count of arraySequence and the length of each channel must\n // match with this buffer obejct.\n // If the FIFO is completely empty, do nothing.\n if (this._framesAvailable === 0) {\n return;\n }\n\n var destinationLength = arraySequence[0].length; // Transfer data from the internal buffer to the |arraySequence| storage.\n\n for (var i = 0; i < destinationLength; ++i) {\n var readIndex = (this._readIndex + i) % this._length;\n\n for (var channel = 0; channel < this._channelCount; ++channel) {\n arraySequence[channel][i] = this._channelData[channel][readIndex];\n }\n }\n\n this._readIndex += destinationLength;\n\n if (this._readIndex >= this._length) {\n this._readIndex = 0;\n }\n\n this._framesAvailable -= destinationLength;\n\n if (this._framesAvailable < 0) {\n this._framesAvailable = 0;\n }\n }\n }, {\n key: "framesAvailable",\n get: function get() {\n return this._framesAvailable;\n }\n }]);\n\n return RingBuffer;\n }()\n}["default"];\n\nvar SoundFileProcessor =\n/*#__PURE__*/\nfunction (_AudioWorkletProcesso) {\n _inherits(SoundFileProcessor, _AudioWorkletProcesso);\n\n function SoundFileProcessor(options) {\n var _this;\n\n _classCallCheck(this, SoundFileProcessor);\n\n _this = _possibleConstructorReturn(this, _getPrototypeOf(SoundFileProcessor).call(this));\n var processorOptions = options.processorOptions || {};\n _this.bufferSize = processorOptions.bufferSize || 256;\n _this.inputRingBuffer = new RingBuffer(_this.bufferSize, 1);\n _this.inputRingBufferArraySequence = [new Float32Array(_this.bufferSize)];\n return _this;\n }\n\n _createClass(SoundFileProcessor, [{\n key: "process",\n value: function process(inputs) {\n var input = inputs[0]; // we only care about the first input channel, because that contains the position data\n\n this.inputRingBuffer.push([input[0]]);\n\n if (this.inputRingBuffer.framesAvailable >= this.bufferSize) {\n this.inputRingBuffer.pull(this.inputRingBufferArraySequence);\n var inputChannel = this.inputRingBufferArraySequence[0];\n var position = inputChannel[inputChannel.length - 1] || 0;\n this.port.postMessage({\n name: \'position\',\n position: position\n });\n }\n\n return true;\n }\n }]);\n\n return SoundFileProcessor;\n}(_wrapNativeSuper(AudioWorkletProcessor));\n\nregisterProcessor(processorNames.soundFileProcessor, SoundFileProcessor);'},function(t,e,n){"use strict";n.r(e),e.default='function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn\'t been initialised - super() hasn\'t been called"); } return self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); }\n\nfunction isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _construct(Parent, args, Class) { if (isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }\n\nfunction _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n// import dependencies via preval.require so that they\'re available as values at compile time\nvar processorNames = {\n "recorderProcessor": "recorder-processor",\n "soundFileProcessor": "sound-file-processor",\n "amplitudeProcessor": "amplitude-processor"\n};\nvar RingBuffer = {\n "default":\n /*#__PURE__*/\n function () {\n /**\n * @constructor\n * @param {number} length Buffer length in frames.\n * @param {number} channelCount Buffer channel count.\n */\n function RingBuffer(length, channelCount) {\n _classCallCheck(this, RingBuffer);\n\n this._readIndex = 0;\n this._writeIndex = 0;\n this._framesAvailable = 0;\n this._channelCount = channelCount;\n this._length = length;\n this._channelData = [];\n\n for (var i = 0; i < this._channelCount; ++i) {\n this._channelData[i] = new Float32Array(length);\n }\n }\n /**\n * Getter for Available frames in buffer.\n *\n * @return {number} Available frames in buffer.\n */\n\n\n _createClass(RingBuffer, [{\n key: "push",\n\n /**\n * Push a sequence of Float32Arrays to buffer.\n *\n * @param {array} arraySequence A sequence of Float32Arrays.\n */\n value: function push(arraySequence) {\n // The channel count of arraySequence and the length of each channel must\n // match with this buffer obejct.\n // Transfer data from the |arraySequence| storage to the internal buffer.\n var sourceLength = arraySequence[0] ? arraySequence[0].length : 0;\n\n for (var i = 0; i < sourceLength; ++i) {\n var writeIndex = (this._writeIndex + i) % this._length;\n\n for (var channel = 0; channel < this._channelCount; ++channel) {\n this._channelData[channel][writeIndex] = arraySequence[channel][i];\n }\n }\n\n this._writeIndex += sourceLength;\n\n if (this._writeIndex >= this._length) {\n this._writeIndex = 0;\n } // For excessive frames, the buffer will be overwritten.\n\n\n this._framesAvailable += sourceLength;\n\n if (this._framesAvailable > this._length) {\n this._framesAvailable = this._length;\n }\n }\n /**\n * Pull data out of buffer and fill a given sequence of Float32Arrays.\n *\n * @param {array} arraySequence An array of Float32Arrays.\n */\n\n }, {\n key: "pull",\n value: function pull(arraySequence) {\n // The channel count of arraySequence and the length of each channel must\n // match with this buffer obejct.\n // If the FIFO is completely empty, do nothing.\n if (this._framesAvailable === 0) {\n return;\n }\n\n var destinationLength = arraySequence[0].length; // Transfer data from the internal buffer to the |arraySequence| storage.\n\n for (var i = 0; i < destinationLength; ++i) {\n var readIndex = (this._readIndex + i) % this._length;\n\n for (var channel = 0; channel < this._channelCount; ++channel) {\n arraySequence[channel][i] = this._channelData[channel][readIndex];\n }\n }\n\n this._readIndex += destinationLength;\n\n if (this._readIndex >= this._length) {\n this._readIndex = 0;\n }\n\n this._framesAvailable -= destinationLength;\n\n if (this._framesAvailable < 0) {\n this._framesAvailable = 0;\n }\n }\n }, {\n key: "framesAvailable",\n get: function get() {\n return this._framesAvailable;\n }\n }]);\n\n return RingBuffer;\n }()\n}["default"];\n\nvar AmplitudeProcessor =\n/*#__PURE__*/\nfunction (_AudioWorkletProcesso) {\n _inherits(AmplitudeProcessor, _AudioWorkletProcesso);\n\n function AmplitudeProcessor(options) {\n var _this;\n\n _classCallCheck(this, AmplitudeProcessor);\n\n _this = _possibleConstructorReturn(this, _getPrototypeOf(AmplitudeProcessor).call(this));\n var processorOptions = options.processorOptions || {};\n _this.numOutputChannels = options.outputChannelCount || 1;\n _this.numInputChannels = processorOptions.numInputChannels || 2;\n _this.normalize = processorOptions.normalize || false;\n _this.smoothing = processorOptions.smoothing || 0;\n _this.bufferSize = processorOptions.bufferSize || 2048;\n _this.inputRingBuffer = new RingBuffer(_this.bufferSize, _this.numInputChannels);\n _this.outputRingBuffer = new RingBuffer(_this.bufferSize, _this.numOutputChannels);\n _this.inputRingBufferArraySequence = new Array(_this.numInputChannels).fill(null).map(function () {\n return new Float32Array(_this.bufferSize);\n });\n _this.stereoVol = [0, 0];\n _this.stereoVolNorm = [0, 0];\n _this.volMax = 0.001;\n\n _this.port.onmessage = function (event) {\n var data = event.data;\n\n if (data.name === \'toggleNormalize\') {\n _this.normalize = data.normalize;\n } else if (data.name === \'smoothing\') {\n _this.smoothing = Math.max(0, Math.min(1, data.smoothing));\n }\n };\n\n return _this;\n } // TO DO make this stereo / dependent on # of audio channels\n\n\n _createClass(AmplitudeProcessor, [{\n key: "process",\n value: function process(inputs, outputs) {\n var input = inputs[0];\n var output = outputs[0];\n var smoothing = this.smoothing;\n this.inputRingBuffer.push(input);\n\n if (this.inputRingBuffer.framesAvailable >= this.bufferSize) {\n this.inputRingBuffer.pull(this.inputRingBufferArraySequence);\n\n for (var channel = 0; channel < this.numInputChannels; ++channel) {\n var inputBuffer = this.inputRingBufferArraySequence[channel];\n var bufLength = inputBuffer.length;\n var sum = 0;\n\n for (var i = 0; i < bufLength; i++) {\n var x = inputBuffer[i];\n\n if (this.normalize) {\n sum += Math.max(Math.min(x / this.volMax, 1), -1) * Math.max(Math.min(x / this.volMax, 1), -1);\n } else {\n sum += x * x;\n }\n } // ... then take the square root of the sum.\n\n\n var rms = Math.sqrt(sum / bufLength);\n this.stereoVol[channel] = Math.max(rms, this.stereoVol[channel] * smoothing);\n this.volMax = Math.max(this.stereoVol[channel], this.volMax);\n } // calculate stero normalized volume and add volume from all channels together\n\n\n var volSum = 0;\n\n for (var index = 0; index < this.stereoVol.length; index++) {\n this.stereoVolNorm[index] = Math.max(Math.min(this.stereoVol[index] / this.volMax, 1), 0);\n volSum += this.stereoVol[index];\n } // volume is average of channels\n\n\n var volume = volSum / this.stereoVol.length; // normalized value\n\n var volNorm = Math.max(Math.min(volume / this.volMax, 1), 0);\n this.port.postMessage({\n name: \'amplitude\',\n volume: volume,\n volNorm: volNorm,\n stereoVol: this.stereoVol,\n stereoVolNorm: this.stereoVolNorm\n }); // pass input through to output\n\n this.outputRingBuffer.push(this.inputRingBufferArraySequence);\n } // pull 128 frames out of the ring buffer\n // if the ring buffer does not have enough frames, the output will be silent\n\n\n this.outputRingBuffer.pull(output);\n return true;\n }\n }]);\n\n return AmplitudeProcessor;\n}(_wrapNativeSuper(AudioWorkletProcessor));\n\nregisterProcessor(processorNames.amplitudeProcessor, AmplitudeProcessor);'},function(t,e,n){var i,r;i=[n(0),n(17)],void 0===(r=function(r){r.Frequency=function(t,e){if(!(this instanceof r.Frequency))return new r.Frequency(t,e);r.TimeBase.call(this,t,e)},r.extend(r.Frequency,r.TimeBase),r.Frequency.prototype._primaryExpressions=Object.create(r.TimeBase.prototype._primaryExpressions),r.Frequency.prototype._primaryExpressions.midi={regexp:/^(\d+(?:\.\d+)?midi)/,method:function(t){return this.midiToFrequency(t)}},r.Frequency.prototype._primaryExpressions.note={regexp:/^([a-g]{1}(?:b|#|x|bb)?)(-?[0-9]+)/i,method:function(t,e){var n=i[t.toLowerCase()]+12*(parseInt(e)+1);return this.midiToFrequency(n)}},r.Frequency.prototype._primaryExpressions.tr={regexp:/^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?):?(\d+(?:\.\d+)?)?/,method:function(t,e,n){var i=1;return t&&"0"!==t&&(i*=this._beatsToUnits(this._timeSignature()*parseFloat(t))),e&&"0"!==e&&(i*=this._beatsToUnits(parseFloat(e))),n&&"0"!==n&&(i*=this._beatsToUnits(parseFloat(n)/4)),i}},r.Frequency.prototype.transpose=function(t){return this._expr=function(t,e){return t()*this.intervalToFrequencyRatio(e)}.bind(this,this._expr,t),this},r.Frequency.prototype.harmonize=function(t){return this._expr=function(t,e){for(var n=t(),i=[],r=0;rthis.buffer.duration)throw"jump time out of range";if(e>this.buffer.duration-t)throw"end time out of range";var n=t||0,i=e||void 0;this.isPlaying()&&(this.stop(0),this.play(0,this.playbackRate,this.output.gain.value,n,i))}},{key:"channels",value:function(){return this.buffer.numberOfChannels}},{key:"sampleRate",value:function(){return this.buffer.sampleRate}},{key:"frames",value:function(){return this.buffer.length}},{key:"getPeaks",value:function(t){if(!this.buffer)throw"Cannot load peaks yet, buffer is not loaded";if(t=t||5*window.width,this.buffer){for(var e=this.buffer,n=e.length/t,i=~~(n/10)||1,r=e.numberOfChannels,o=new Float32Array(Math.round(t)),s=0;so[u])&&(o[u]=h)}return o}}},{key:"reverseBuffer",value:function(){if(!this.buffer)throw"SoundFile is not done loading";var t=this._lastPos/R.sampleRate,e=this.getVolume();this.setVolume(0,.001);for(var n=this.buffer.numberOfChannels,i=0;it[o].hi&&o++,r[o]=void 0!==r[o]?(r[o]+n[s])/2:n[s]}return r}},{key:"getOctaveBands",value:function(t,e){var n=t||3,i=e||15.625,r=[],o={lo:i/Math.pow(2,1/(2*n)),ctr:i,hi:i*Math.pow(2,1/(2*n))};r.push(o);for(var s=p.audiocontext.sampleRate/2;o.hi=this._maxDelay)throw new Error("Delay Time exceeds maximum delay time of "+this._maxDelay+" second.");t.connect(this.input),this.leftDelay.delayTime.setValueAtTime(o,this.ac.currentTime),this.rightDelay.delayTime.setValueAtTime(o,this.ac.currentTime),this._leftGain.gain.value=r,this._rightGain.gain.value=r,i&&(this._leftFilter.freq(i),this._rightFilter.freq(i))}},{key:"delayTime",value:function(t){"number"!=typeof t?(t.connect(this.leftDelay.delayTime),t.connect(this.rightDelay.delayTime)):(this.leftDelay.delayTime.cancelScheduledValues(this.ac.currentTime),this.rightDelay.delayTime.cancelScheduledValues(this.ac.currentTime),this.leftDelay.delayTime.linearRampToValueAtTime(t,this.ac.currentTime),this.rightDelay.delayTime.linearRampToValueAtTime(t,this.ac.currentTime))}},{key:"feedback",value:function(t){if(t&&"number"!=typeof t)t.connect(this._leftGain.gain),t.connect(this._rightGain.gain);else{if(1<=t)throw new Error("Feedback value will force a positive feedback loop.");"number"==typeof t&&(this._leftGain.gain.value=t,this._rightGain.gain.value=t)}return this._leftGain.gain.value}},{key:"filter",value:function(t,e){this._leftFilter.set(t,e),this._rightFilter.set(t,e)}},{key:"setType",value:function(t){switch(1===t&&(t="pingPong"),this._split.disconnect(),this._leftFilter.disconnect(),this._rightFilter.disconnect(),this._split.connect(this.leftDelay,0),this._split.connect(this.rightDelay,1),t){case"pingPong":this._rightFilter.setType(this._leftFilter.biquad.type),this._leftFilter.output.connect(this._merge,0,0),this._rightFilter.output.connect(this._merge,0,1),this._leftFilter.output.connect(this.rightDelay),this._rightFilter.output.connect(this.leftDelay);break;default:this._leftFilter.output.connect(this._merge,0,0),this._rightFilter.output.connect(this._merge,0,1),this._leftFilter.output.connect(this.leftDelay),this._rightFilter.output.connect(this.rightDelay)}}},{key:"dispose",value:function(){de(ye(e.prototype),"dispose",this).call(this),this._split.disconnect(),this._leftFilter.dispose(),this._rightFilter.dispose(),this._merge.disconnect(),this._leftGain.disconnect(),this._rightGain.disconnect(),this.leftDelay.disconnect(),this.rightDelay.disconnect(),this._split=void 0,this._leftFilter=void 0,this._rightFilter=void 0,this._merge=void 0,this._leftGain=void 0,this._rightGain=void 0,this.leftDelay=void 0,this.rightDelay=void 0}}]),e}();function _e(t){return(_e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function ge(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function be(t,e){for(var n=0;nthis.length&&(this.length=i.sequence.length)}},{key:"removePhrase",value:function(t){for(var e in this.phrases)this.phrases[e].name===t&&this.phrases.splice(e,1)}},{key:"getPhrase",value:function(t){for(var e in this.phrases)if(this.phrases[e].name===t)return this.phrases[e]}},{key:"replaceSequence",value:function(t,e){for(var n in this.phrases)this.phrases[n].name===t&&(this.phrases[n].sequence=e)}},{key:"incrementStep",value:function(t){this.partStep=t.parts.length?(t.scoreStep=0,t.onended()):(t.scoreStep=0,t.parts[t.currentPart-1].stop(),t.parts[t.currentPart].start())}function Ue(t,e){for(var n=0;nthis.cutoff&&e>this.threshold&&0this.treshold){this.isDetected=!0,this.callback?this.callback(this.energy):e&&e(this.energy);var n=this;setTimeout(function(){n.isDetected=!1},this.sensitivity)}this.penergy=this.energy}}]),r}();function xn(t,e){for(var n=0;n