From 1c26415fef658d6f80e1eb49bad8197389528c21 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Thu, 23 Mar 2023 23:20:54 +0900 Subject: [PATCH 01/22] port Renderer to es6 class, not done. --- src/core/p5.Renderer.js | 776 ++++++++++++++++++++-------------------- 1 file changed, 387 insertions(+), 389 deletions(-) diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index ecb1680326..9cf4ca7a9e 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -19,341 +19,398 @@ import * as constants from '../core/constants'; * @param {p5} [pInst] pointer to p5 instance * @param {Boolean} [isMainCanvas] whether we're using it as main canvas */ -p5.Renderer = function(elt, pInst, isMainCanvas) { - p5.Element.call(this, elt, pInst); - this.canvas = elt; - this._pixelsState = pInst; - if (isMainCanvas) { - this._isMainCanvas = true; - // for pixel method sharing with pimage - this._pInst._setProperty('_curElement', this); - this._pInst._setProperty('canvas', this.canvas); - this._pInst._setProperty('width', this.width); - this._pInst._setProperty('height', this.height); - } else { +p5.Renderer = class extends p5.Element { + constructor(elt, pInst, isMainCanvas){ + super(elt, pInst); + this.canvas = elt; + this._pixelsState = pInst; + if (isMainCanvas) { + this._isMainCanvas = true; + // for pixel method sharing with pimage + this._pInst._setProperty('_curElement', this); + this._pInst._setProperty('canvas', this.canvas); + this._pInst._setProperty('width', this.width); + this._pInst._setProperty('height', this.height); + } else { // hide if offscreen buffer by default - this.canvas.style.display = 'none'; - this._styles = []; // non-main elt styles stored in p5.Renderer - } - - this._textSize = 12; - this._textLeading = 15; - this._textFont = 'sans-serif'; - this._textStyle = constants.NORMAL; - this._textAscent = null; - this._textDescent = null; - this._textAlign = constants.LEFT; - this._textBaseline = constants.BASELINE; - this._textWrap = constants.WORD; - - this._rectMode = constants.CORNER; - this._ellipseMode = constants.CENTER; - this._curveTightness = 0; - this._imageMode = constants.CORNER; - - this._tint = null; - this._doStroke = true; - this._doFill = true; - this._strokeSet = false; - this._fillSet = false; - this._leadingSet = false; -}; - -p5.Renderer.prototype = Object.create(p5.Element.prototype); - -// the renderer should return a 'style' object that it wishes to -// store on the push stack. -p5.Renderer.prototype.push = function() { - return { - properties: { - _doStroke: this._doStroke, - _strokeSet: this._strokeSet, - _doFill: this._doFill, - _fillSet: this._fillSet, - _tint: this._tint, - _imageMode: this._imageMode, - _rectMode: this._rectMode, - _ellipseMode: this._ellipseMode, - _textFont: this._textFont, - _textLeading: this._textLeading, - _leadingSet: this._leadingSet, - _textSize: this._textSize, - _textAlign: this._textAlign, - _textBaseline: this._textBaseline, - _textStyle: this._textStyle, - _textWrap: this._textWrap + this.canvas.style.display = 'none'; + this._styles = []; // non-main elt styles stored in p5.Renderer } - }; -}; -// a pop() operation is in progress -// the renderer is passed the 'style' object that it returned -// from its push() method. -p5.Renderer.prototype.pop = function(style) { - if (style.properties) { + this._textSize = 12; + this._textLeading = 15; + this._textFont = 'sans-serif'; + this._textStyle = constants.NORMAL; + this._textAscent = null; + this._textDescent = null; + this._textAlign = constants.LEFT; + this._textBaseline = constants.BASELINE; + this._textWrap = constants.WORD; + + this._rectMode = constants.CORNER; + this._ellipseMode = constants.CENTER; + this._curveTightness = 0; + this._imageMode = constants.CORNER; + + this._tint = null; + this._doStroke = true; + this._doFill = true; + this._strokeSet = false; + this._fillSet = false; + this._leadingSet = false; + } + // the renderer should return a 'style' object that it wishes to + // store on the push stack. + push() { + return { + properties: { + _doStroke: this._doStroke, + _strokeSet: this._strokeSet, + _doFill: this._doFill, + _fillSet: this._fillSet, + _tint: this._tint, + _imageMode: this._imageMode, + _rectMode: this._rectMode, + _ellipseMode: this._ellipseMode, + _textFont: this._textFont, + _textLeading: this._textLeading, + _leadingSet: this._leadingSet, + _textSize: this._textSize, + _textAlign: this._textAlign, + _textBaseline: this._textBaseline, + _textStyle: this._textStyle, + _textWrap: this._textWrap + } + }; + } + + // a pop() operation is in progress + // the renderer is passed the 'style' object that it returned + // from its push() method. + pop(style) { + if (style.properties) { // copy the style properties back into the renderer - Object.assign(this, style.properties); + Object.assign(this, style.properties); + } } -}; -/** + /** * Resize our canvas element. */ -p5.Renderer.prototype.resize = function(w, h) { - this.width = w; - this.height = h; - this.elt.width = w * this._pInst._pixelDensity; - this.elt.height = h * this._pInst._pixelDensity; - this.elt.style.width = `${w}px`; - this.elt.style.height = `${h}px`; - if (this._isMainCanvas) { - this._pInst._setProperty('width', this.width); - this._pInst._setProperty('height', this.height); + resize(w, h) { + this.width = w; + this.height = h; + this.elt.width = w * this._pInst._pixelDensity; + this.elt.height = h * this._pInst._pixelDensity; + this.elt.style.width = `${w}px`; + this.elt.style.height = `${h}px`; + if (this._isMainCanvas) { + this._pInst._setProperty('width', this.width); + this._pInst._setProperty('height', this.height); + } } -}; -p5.Renderer.prototype.get = function(x, y, w, h) { - const pixelsState = this._pixelsState; - const pd = pixelsState._pixelDensity; - const canvas = this.canvas; + get(x, y, w, h) { + const pixelsState = this._pixelsState; + const pd = pixelsState._pixelDensity; + const canvas = this.canvas; - if (typeof x === 'undefined' && typeof y === 'undefined') { + if (typeof x === 'undefined' && typeof y === 'undefined') { // get() - x = y = 0; - w = pixelsState.width; - h = pixelsState.height; - } else { - x *= pd; - y *= pd; + x = y = 0; + w = pixelsState.width; + h = pixelsState.height; + } else { + x *= pd; + y *= pd; - if (typeof w === 'undefined' && typeof h === 'undefined') { + if (typeof w === 'undefined' && typeof h === 'undefined') { // get(x,y) - if (x < 0 || y < 0 || x >= canvas.width || y >= canvas.height) { - return [0, 0, 0, 0]; - } + if (x < 0 || y < 0 || x >= canvas.width || y >= canvas.height) { + return [0, 0, 0, 0]; + } - return this._getPixel(x, y); - } + return this._getPixel(x, y); + } // get(x,y,w,h) - } + } - const region = new p5.Image(w, h); - region.canvas - .getContext('2d') - .drawImage(canvas, x, y, w * pd, h * pd, 0, 0, w, h); + const region = new p5.Image(w, h); + region.canvas + .getContext('2d') + .drawImage(canvas, x, y, w * pd, h * pd, 0, 0, w, h); - return region; -}; - -p5.Renderer.prototype.textLeading = function(l) { - if (typeof l === 'number') { - this._setProperty('_leadingSet', true); - this._setProperty('_textLeading', l); - return this._pInst; + return region; } - return this._textLeading; -}; + textLeading(l) { + if (typeof l === 'number') { + this._setProperty('_leadingSet', true); + this._setProperty('_textLeading', l); + return this._pInst; + } -p5.Renderer.prototype.textSize = function(s) { - if (typeof s === 'number') { - this._setProperty('_textSize', s); - if (!this._leadingSet) { + return this._textLeading; + } + + textSize(s) { + if (typeof s === 'number') { + this._setProperty('_textSize', s); + if (!this._leadingSet) { // only use a default value if not previously set (#5181) - this._setProperty('_textLeading', s * constants._DEFAULT_LEADMULT); + this._setProperty('_textLeading', s * constants._DEFAULT_LEADMULT); + } + return this._applyTextProperties(); } - return this._applyTextProperties(); - } - return this._textSize; -}; + return this._textSize; + } -p5.Renderer.prototype.textStyle = function(s) { - if (s) { - if ( - s === constants.NORMAL || + textStyle(s) { + if (s) { + if ( + s === constants.NORMAL || s === constants.ITALIC || s === constants.BOLD || s === constants.BOLDITALIC - ) { - this._setProperty('_textStyle', s); - } - - return this._applyTextProperties(); - } + ) { + this._setProperty('_textStyle', s); + } - return this._textStyle; -}; + return this._applyTextProperties(); + } -p5.Renderer.prototype.textAscent = function() { - if (this._textAscent === null) { - this._updateTextMetrics(); + return this._textStyle; } - return this._textAscent; -}; -p5.Renderer.prototype.textDescent = function() { - if (this._textDescent === null) { - this._updateTextMetrics(); + textAscent() { + if (this._textAscent === null) { + this._updateTextMetrics(); + } + return this._textAscent; } - return this._textDescent; -}; -p5.Renderer.prototype.textAlign = function(h, v) { - if (typeof h !== 'undefined') { - this._setProperty('_textAlign', h); - - if (typeof v !== 'undefined') { - this._setProperty('_textBaseline', v); + textDescent() { + if (this._textDescent === null) { + this._updateTextMetrics(); } - - return this._applyTextProperties(); - } else { - return { - horizontal: this._textAlign, - vertical: this._textBaseline - }; + return this._textDescent; } -}; -p5.Renderer.prototype.textWrap = function(wrapStyle) { - this._setProperty('_textWrap', wrapStyle); - return this._textWrap; -}; + textAlign(h, v) { + if (typeof h !== 'undefined') { + this._setProperty('_textAlign', h); -p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { - const p = this._pInst; - const textWrapStyle = this._textWrap; - - let lines; - let line; - let testLine; - let testWidth; - let words; - let chars; - let shiftedY; - let finalMaxHeight = Number.MAX_VALUE; - // fix for #5785 (top of bounding box) - let finalMinHeight = y; - - if (!(this._doFill || this._doStroke)) { - return; - } + if (typeof v !== 'undefined') { + this._setProperty('_textBaseline', v); + } - if (typeof str === 'undefined') { - return; - } else if (typeof str !== 'string') { - str = str.toString(); + return this._applyTextProperties(); + } else { + return { + horizontal: this._textAlign, + vertical: this._textBaseline + }; + } } - // Replaces tabs with double-spaces and splits string on any line - // breaks present in the original string - str = str.replace(/(\t)/g, ' '); - lines = str.split('\n'); + textWrap(wrapStyle) { + this._setProperty('_textWrap', wrapStyle); + return this._textWrap; + } - if (typeof maxWidth !== 'undefined') { - if (this._rectMode === constants.CENTER) { - x -= maxWidth / 2; + text(str, x, y, maxWidth, maxHeight) { + const p = this._pInst; + const textWrapStyle = this._textWrap; + + let lines; + let line; + let testLine; + let testWidth; + let words; + let chars; + let shiftedY; + let finalMaxHeight = Number.MAX_VALUE; + // fix for #5785 (top of bounding box) + let finalMinHeight = y; + + if (!(this._doFill || this._doStroke)) { + return; } - switch (this._textAlign) { - case constants.CENTER: - x += maxWidth / 2; - break; - case constants.RIGHT: - x += maxWidth; - break; + if (typeof str === 'undefined') { + return; + } else if (typeof str !== 'string') { + str = str.toString(); } - if (typeof maxHeight !== 'undefined') { + // Replaces tabs with double-spaces and splits string on any line + // breaks present in the original string + str = str.replace(/(\t)/g, ' '); + lines = str.split('\n'); + + if (typeof maxWidth !== 'undefined') { if (this._rectMode === constants.CENTER) { - y -= maxHeight / 2; - finalMinHeight -= maxHeight / 2; + x -= maxWidth / 2; } - let originalY = y; - let ascent = p.textAscent(); - - switch (this._textBaseline) { - case constants.BOTTOM: - shiftedY = y + maxHeight; - y = Math.max(shiftedY, y); - // fix for #5785 (top of bounding box) - finalMinHeight += ascent; - break; + switch (this._textAlign) { case constants.CENTER: - shiftedY = y + maxHeight / 2; - y = Math.max(shiftedY, y); - // fix for #5785 (top of bounding box) - finalMinHeight += ascent / 2; + x += maxWidth / 2; + break; + case constants.RIGHT: + x += maxWidth; break; } - // remember the max-allowed y-position for any line (fix to #928) - finalMaxHeight = y + maxHeight - ascent; + if (typeof maxHeight !== 'undefined') { + if (this._rectMode === constants.CENTER) { + y -= maxHeight / 2; + finalMinHeight -= maxHeight / 2; + } - // fix for #5785 (bottom of bounding box) - if (this._textBaseline === constants.CENTER) { - finalMaxHeight = originalY + maxHeight - ascent / 2; - } - } else { + let originalY = y; + let ascent = p.textAscent(); + + switch (this._textBaseline) { + case constants.BOTTOM: + shiftedY = y + maxHeight; + y = Math.max(shiftedY, y); + // fix for #5785 (top of bounding box) + finalMinHeight += ascent; + break; + case constants.CENTER: + shiftedY = y + maxHeight / 2; + y = Math.max(shiftedY, y); + // fix for #5785 (top of bounding box) + finalMinHeight += ascent / 2; + break; + } + + // remember the max-allowed y-position for any line (fix to #928) + finalMaxHeight = y + maxHeight - ascent; + + // fix for #5785 (bottom of bounding box) + if (this._textBaseline === constants.CENTER) { + finalMaxHeight = originalY + maxHeight - ascent / 2; + } + } else { // no text-height specified, show warning for BOTTOM / CENTER - if (this._textBaseline === constants.BOTTOM || + if (this._textBaseline === constants.BOTTOM || this._textBaseline === constants.CENTER) { // use rectHeight as an approximation for text height - let rectHeight = p.textSize() * this._textLeading; - finalMinHeight = y - rectHeight / 2; - finalMaxHeight = y + rectHeight / 2; + let rectHeight = p.textSize() * this._textLeading; + finalMinHeight = y - rectHeight / 2; + finalMaxHeight = y + rectHeight / 2; + } } - } - // Render lines of text according to settings of textWrap - // Splits lines at spaces, for loop adds one word + space - // at a time and tests length with next word added - if (textWrapStyle === constants.WORD) { - let nlines = []; - for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { - line = ''; - words = lines[lineIndex].split(' '); - for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { - testLine = `${line + words[wordIndex]}` + ' '; - testWidth = this.textWidth(testLine); - if (testWidth > maxWidth && line.length > 0) { - nlines.push(line); - line = `${words[wordIndex]}` + ' '; - } else { - line = testLine; + // Render lines of text according to settings of textWrap + // Splits lines at spaces, for loop adds one word + space + // at a time and tests length with next word added + if (textWrapStyle === constants.WORD) { + let nlines = []; + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + line = ''; + words = lines[lineIndex].split(' '); + for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { + testLine = `${line + words[wordIndex]}` + ' '; + testWidth = this.textWidth(testLine); + if (testWidth > maxWidth && line.length > 0) { + nlines.push(line); + line = `${words[wordIndex]}` + ' '; + } else { + line = testLine; + } } + nlines.push(line); } - nlines.push(line); - } - let offset = 0; - if (this._textBaseline === constants.CENTER) { - offset = (nlines.length - 1) * p.textLeading() / 2; - } else if (this._textBaseline === constants.BOTTOM) { - offset = (nlines.length - 1) * p.textLeading(); - } + let offset = 0; + if (this._textBaseline === constants.CENTER) { + offset = (nlines.length - 1) * p.textLeading() / 2; + } else if (this._textBaseline === constants.BOTTOM) { + offset = (nlines.length - 1) * p.textLeading(); + } - for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { - line = ''; - words = lines[lineIndex].split(' '); - for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { - testLine = `${line + words[wordIndex]}` + ' '; - testWidth = this.textWidth(testLine); - if (testWidth > maxWidth && line.length > 0) { - this._renderText( - p, - line.trim(), - x, - y - offset, - finalMaxHeight, - finalMinHeight - ); - line = `${words[wordIndex]}` + ' '; - y += p.textLeading(); - } else { - line = testLine; + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + line = ''; + words = lines[lineIndex].split(' '); + for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { + testLine = `${line + words[wordIndex]}` + ' '; + testWidth = this.textWidth(testLine); + if (testWidth > maxWidth && line.length > 0) { + this._renderText( + p, + line.trim(), + x, + y - offset, + finalMaxHeight, + finalMinHeight + ); + line = `${words[wordIndex]}` + ' '; + y += p.textLeading(); + } else { + line = testLine; + } + } + this._renderText( + p, + line.trim(), + x, + y - offset, + finalMaxHeight, + finalMinHeight + ); + y += p.textLeading(); + } + } else { + let nlines = []; + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + line = ''; + chars = lines[lineIndex].split(''); + for (let charIndex = 0; charIndex < chars.length; charIndex++) { + testLine = `${line + chars[charIndex]}`; + testWidth = this.textWidth(testLine); + if (testWidth <= maxWidth) { + line += chars[charIndex]; + } else if (testWidth > maxWidth && line.length > 0) { + nlines.push(line); + line = `${chars[charIndex]}`; + } + } + } + + nlines.push(line); + let offset = 0; + if (this._textBaseline === constants.CENTER) { + offset = (nlines.length - 1) * p.textLeading() / 2; + } else if (this._textBaseline === constants.BOTTOM) { + offset = (nlines.length - 1) * p.textLeading(); + } + + // Splits lines at characters, for loop adds one char at a time + // and tests length with next char added + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + line = ''; + chars = lines[lineIndex].split(''); + for (let charIndex = 0; charIndex < chars.length; charIndex++) { + testLine = `${line + chars[charIndex]}`; + testWidth = this.textWidth(testLine); + if (testWidth <= maxWidth) { + line += chars[charIndex]; + } else if (testWidth > maxWidth && line.length > 0) { + this._renderText( + p, + line.trim(), + x, + y - offset, + finalMaxHeight, + finalMinHeight + ); + y += p.textLeading(); + line = `${chars[charIndex]}`; + } } } this._renderText( @@ -367,147 +424,88 @@ p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { y += p.textLeading(); } } else { - let nlines = []; - for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { - line = ''; - chars = lines[lineIndex].split(''); - for (let charIndex = 0; charIndex < chars.length; charIndex++) { - testLine = `${line + chars[charIndex]}`; - testWidth = this.textWidth(testLine); - if (testWidth <= maxWidth) { - line += chars[charIndex]; - } else if (testWidth > maxWidth && line.length > 0) { - nlines.push(line); - line = `${chars[charIndex]}`; - } - } - } - - nlines.push(line); + // Offset to account for vertically centering multiple lines of text - no + // need to adjust anything for vertical align top or baseline let offset = 0; if (this._textBaseline === constants.CENTER) { - offset = (nlines.length - 1) * p.textLeading() / 2; + offset = (lines.length - 1) * p.textLeading() / 2; } else if (this._textBaseline === constants.BOTTOM) { - offset = (nlines.length - 1) * p.textLeading(); + offset = (lines.length - 1) * p.textLeading(); } - // Splits lines at characters, for loop adds one char at a time - // and tests length with next char added - for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { - line = ''; - chars = lines[lineIndex].split(''); - for (let charIndex = 0; charIndex < chars.length; charIndex++) { - testLine = `${line + chars[charIndex]}`; - testWidth = this.textWidth(testLine); - if (testWidth <= maxWidth) { - line += chars[charIndex]; - } else if (testWidth > maxWidth && line.length > 0) { - this._renderText( - p, - line.trim(), - x, - y - offset, - finalMaxHeight, - finalMinHeight - ); - y += p.textLeading(); - line = `${chars[charIndex]}`; - } - } + // Renders lines of text at any line breaks present in the original string + for (let i = 0; i < lines.length; i++) { + this._renderText( + p, + lines[i], + x, + y - offset, + finalMaxHeight, + finalMinHeight - offset + ); + y += p.textLeading(); } - this._renderText( - p, - line.trim(), - x, - y - offset, - finalMaxHeight, - finalMinHeight - ); - y += p.textLeading(); - } - } else { - // Offset to account for vertically centering multiple lines of text - no - // need to adjust anything for vertical align top or baseline - let offset = 0; - if (this._textBaseline === constants.CENTER) { - offset = (lines.length - 1) * p.textLeading() / 2; - } else if (this._textBaseline === constants.BOTTOM) { - offset = (lines.length - 1) * p.textLeading(); } - // Renders lines of text at any line breaks present in the original string - for (let i = 0; i < lines.length; i++) { - this._renderText( - p, - lines[i], - x, - y - offset, - finalMaxHeight, - finalMinHeight - offset - ); - y += p.textLeading(); - } + return p; } - return p; -}; - -p5.Renderer.prototype._applyDefaults = function() { - return this; -}; + _applyDefaults() { + return this; + } -/** + /** * Helper function to check font type (system or otf) */ -p5.Renderer.prototype._isOpenType = function(f = this._textFont) { - return typeof f === 'object' && f.font && f.font.supported; -}; - -p5.Renderer.prototype._updateTextMetrics = function() { - if (this._isOpenType()) { - this._setProperty('_textAscent', this._textFont._textAscent()); - this._setProperty('_textDescent', this._textFont._textDescent()); - return this; + _isOpenType(f = this._textFont) { + return typeof f === 'object' && f.font && f.font.supported; } - // Adapted from http://stackoverflow.com/a/25355178 - const text = document.createElement('span'); - text.style.fontFamily = this._textFont; - text.style.fontSize = `${this._textSize}px`; - text.innerHTML = 'ABCjgq|'; + _updateTextMetrics() { + if (this._isOpenType()) { + this._setProperty('_textAscent', this._textFont._textAscent()); + this._setProperty('_textDescent', this._textFont._textDescent()); + return this; + } - const block = document.createElement('div'); - block.style.display = 'inline-block'; - block.style.width = '1px'; - block.style.height = '0px'; + // Adapted from http://stackoverflow.com/a/25355178 + const text = document.createElement('span'); + text.style.fontFamily = this._textFont; + text.style.fontSize = `${this._textSize}px`; + text.innerHTML = 'ABCjgq|'; - const container = document.createElement('div'); - container.appendChild(text); - container.appendChild(block); + const block = document.createElement('div'); + block.style.display = 'inline-block'; + block.style.width = '1px'; + block.style.height = '0px'; - container.style.height = '0px'; - container.style.overflow = 'hidden'; - document.body.appendChild(container); + const container = document.createElement('div'); + container.appendChild(text); + container.appendChild(block); - block.style.verticalAlign = 'baseline'; - let blockOffset = calculateOffset(block); - let textOffset = calculateOffset(text); - const ascent = blockOffset[1] - textOffset[1]; + container.style.height = '0px'; + container.style.overflow = 'hidden'; + document.body.appendChild(container); - block.style.verticalAlign = 'bottom'; - blockOffset = calculateOffset(block); - textOffset = calculateOffset(text); - const height = blockOffset[1] - textOffset[1]; - const descent = height - ascent; + block.style.verticalAlign = 'baseline'; + let blockOffset = calculateOffset(block); + let textOffset = calculateOffset(text); + const ascent = blockOffset[1] - textOffset[1]; - document.body.removeChild(container); + block.style.verticalAlign = 'bottom'; + blockOffset = calculateOffset(block); + textOffset = calculateOffset(text); + const height = blockOffset[1] - textOffset[1]; + const descent = height - ascent; - this._setProperty('_textAscent', ascent); - this._setProperty('_textDescent', descent); + document.body.removeChild(container); - return this; -}; + this._setProperty('_textAscent', ascent); + this._setProperty('_textDescent', descent); + return this; + } +}; /** * Helper fxn to measure ascent and descent. * Adapted from http://stackoverflow.com/a/25355178 From 384e0eb2461270161ebef637209597faffe2a5af Mon Sep 17 00:00:00 2001 From: asukaminato Date: Thu, 23 Mar 2023 23:52:39 +0900 Subject: [PATCH 02/22] Color && Renderer2D to class --- src/color/p5.Color.js | 28 +++++++++++++++------------- src/core/p5.Renderer2D.js | 14 +++++++------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/color/p5.Color.js b/src/color/p5.Color.js index 6e5c305a42..83ead6590f 100644 --- a/src/color/p5.Color.js +++ b/src/color/p5.Color.js @@ -37,24 +37,26 @@ import color_conversion from './color_conversion'; * for red, green, blue and alpha channel * or CSS color. */ -p5.Color = function(pInst, vals) { +p5.Color = class { + constructor(pInst, vals){ // Record color mode and maxes at time of construction. - this._storeModeAndMaxes(pInst._colorMode, pInst._colorMaxes); + this._storeModeAndMaxes(pInst._colorMode, pInst._colorMaxes); - // Calculate normalized RGBA values. - if ( - this.mode !== constants.RGB && + // Calculate normalized RGBA values. + if ( + this.mode !== constants.RGB && this.mode !== constants.HSL && this.mode !== constants.HSB - ) { - throw new Error(`${this.mode} is an invalid colorMode.`); - } else { - this._array = p5.Color._parseInputs.apply(this, vals); - } + ) { + throw new Error(`${this.mode} is an invalid colorMode.`); + } else { + this._array = p5.Color._parseInputs.apply(this, vals); + } - // Expose closest screen color. - this._calculateLevels(); - return this; + // Expose closest screen color. + this._calculateLevels(); + return this; + } }; /** diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index a6f0ed534c..28d6a01c23 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -11,15 +11,15 @@ import './p5.Renderer'; const styleEmpty = 'rgba(0,0,0,0)'; // const alphaThreshold = 0.00125; // minimum visible -p5.Renderer2D = function(elt, pInst, isMainCanvas) { - p5.Renderer.call(this, elt, pInst, isMainCanvas); - this.drawingContext = this.canvas.getContext('2d'); - this._pInst._setProperty('drawingContext', this.drawingContext); - return this; +p5.Renderer2D = class extends p5.Renderer { + constructor(elt, pInst, isMainCanvas) { + super(elt, pInst, isMainCanvas); + this.drawingContext = this.canvas.getContext('2d'); + this._pInst._setProperty('drawingContext', this.drawingContext); + return this; + } }; -p5.Renderer2D.prototype = Object.create(p5.Renderer.prototype); - p5.Renderer2D.prototype._applyDefaults = function() { this._cachedFillStyle = this._cachedStrokeStyle = undefined; this._cachedBlendMode = constants.BLEND; From a36da0cacae74bcc5199613ad6b3a5670af7c2fd Mon Sep 17 00:00:00 2001 From: asukaminato Date: Fri, 24 Mar 2023 01:13:08 +0900 Subject: [PATCH 03/22] RendererGL to es6 class --- src/webgl/p5.RendererGL.js | 319 ++++++++++++++++++------------------- 1 file changed, 159 insertions(+), 160 deletions(-) diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 726d65ea30..c676bb8667 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -86,192 +86,191 @@ const defaultShaders = { * @todo extend class to include public method for offscreen * rendering (FBO). */ -p5.RendererGL = function(elt, pInst, isMainCanvas, attr) { - p5.Renderer.call(this, elt, pInst, isMainCanvas); - this._setAttributeDefaults(pInst); - this._initContext(); - this.isP3D = true; //lets us know we're in 3d mode - - // 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; +p5.RendererGL = class extends p5.Renderer { + constructor(elt, pInst, isMainCanvas, attr){ + super(elt, pInst, isMainCanvas); + this._setAttributeDefaults(pInst); + this._initContext(); + this.isP3D = true; //lets us know we're in 3d mode + + // 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; - // lights - this._enableLighting = false; + // lights + this._enableLighting = false; - this.ambientLightColors = []; - this.specularColors = [1, 1, 1]; + this.ambientLightColors = []; + this.specularColors = [1, 1, 1]; - this.directionalLightDirections = []; - this.directionalLightDiffuseColors = []; - this.directionalLightSpecularColors = []; + this.directionalLightDirections = []; + this.directionalLightDiffuseColors = []; + this.directionalLightSpecularColors = []; - this.pointLightPositions = []; - this.pointLightDiffuseColors = []; - this.pointLightSpecularColors = []; + this.pointLightPositions = []; + this.pointLightDiffuseColors = []; + this.pointLightSpecularColors = []; - this.spotLightPositions = []; - this.spotLightDirections = []; - this.spotLightDiffuseColors = []; - this.spotLightSpecularColors = []; - this.spotLightAngle = []; - this.spotLightConc = []; + this.spotLightPositions = []; + this.spotLightDirections = []; + this.spotLightDiffuseColors = []; + this.spotLightSpecularColors = []; + this.spotLightAngle = []; + this.spotLightConc = []; - this.drawMode = constants.FILL; + 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.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._cachedBlendMode = undefined; - if (this.webglVersion === constants.WEBGL2) { - this.blendExt = this.GL; - } else { - this.blendExt = this.GL.getExtension('EXT_blend_minmax'); - } - this._isBlending = false; + this.curBlendMode = constants.BLEND; + 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._useSpecularMaterial = false; - this._useEmissiveMaterial = false; - this._useNormalMaterial = false; - this._useShininess = 1; + this._useSpecularMaterial = false; + this._useEmissiveMaterial = false; + this._useNormalMaterial = false; + this._useShininess = 1; - this._useLineColor = false; - this._useVertexColor = false; + this._useLineColor = false; + this._useVertexColor = false; - this.registerEnabled = []; + this.registerEnabled = []; - this._tint = [255, 255, 255, 255]; + this._tint = [255, 255, 255, 255]; - // lightFalloff variables - this.constantAttenuation = 1; - this.linearAttenuation = 0; - this.quadraticAttenuation = 0; + // lightFalloff variables + this.constantAttenuation = 1; + this.linearAttenuation = 0; + this.quadraticAttenuation = 0; - /** + /** * model view, projection, & normal * matrices */ - this.uMVMatrix = new p5.Matrix(); - this.uPMatrix = new p5.Matrix(); - this.uNMatrix = new p5.Matrix('mat3'); - - // Current vertex normal - this._currentNormal = new p5.Vector(0, 0, 1); - - // Camera - this._curCamera = new p5.Camera(this); - this._curCamera._computeCameraDefaultSettings(); - this._curCamera._setDefaultCamera(); - - 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 p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), - new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), - new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) - ], - fill: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), - new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), - new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), - new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), - //new BufferDef(3, 'vertexSpeculars', 'specularBuffer', 'aSpecularColor'), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) - ], - text: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition',this, this._vToNArray), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) - ] - } - }; - - // Immediate Mode - // Geometry and Material hashes stored here - this.immediateMode = { - geometry: new p5.Geometry(), - shapeMode: constants.TRIANGLE_FAN, - _bezierVertex: [], - _quadraticVertex: [], - _curveVertex: [], - buffers: { - fill: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), - new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), - new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), - new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) - ], - stroke: [ - new p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), - new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), - new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) - ], - point: this.GL.createBuffer() - } - }; - - this.pointSize = 5.0; //default point size - this.curStrokeWeight = 1; - this.curStrokeCap = constants.ROUND; - this.curStrokeJoin = constants.ROUND; + this.uMVMatrix = new p5.Matrix(); + this.uPMatrix = new p5.Matrix(); + this.uNMatrix = new p5.Matrix('mat3'); + + // Current vertex normal + this._currentNormal = new p5.Vector(0, 0, 1); + + // Camera + this._curCamera = new p5.Camera(this); + this._curCamera._computeCameraDefaultSettings(); + this._curCamera._setDefaultCamera(); + + 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 p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), + new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), + new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), + new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), + new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) + ], + fill: [ + new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), + new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), + new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), + new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), + //new BufferDef(3, 'vertexSpeculars', 'specularBuffer', 'aSpecularColor'), + new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) + ], + text: [ + new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition',this, this._vToNArray), + new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) + ] + } + }; + + // Immediate Mode + // Geometry and Material hashes stored here + this.immediateMode = { + geometry: new p5.Geometry(), + shapeMode: constants.TRIANGLE_FAN, + _bezierVertex: [], + _quadraticVertex: [], + _curveVertex: [], + buffers: { + fill: [ + new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), + new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), + new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), + new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), + new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) + ], + stroke: [ + new p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), + new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), + new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), + new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), + new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) + ], + point: this.GL.createBuffer() + } + }; - // array of textures created in this gl context via this.getTexture(src) - this.textures = []; + this.pointSize = 5.0; //default point size + this.curStrokeWeight = 1; + this.curStrokeCap = constants.ROUND; + this.curStrokeJoin = constants.ROUND; - this.textureMode = constants.IMAGE; - // default wrap settings - this.textureWrapX = constants.CLAMP; - this.textureWrapY = constants.CLAMP; - this._tex = null; - this._curveTightness = 6; + // array of textures created in this gl context via this.getTexture(src) + this.textures = []; - // 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 = []; + this.textureMode = constants.IMAGE; + // default wrap settings + this.textureWrapX = constants.CLAMP; + this.textureWrapY = constants.CLAMP; + this._tex = null; + this._curveTightness = 6; - // current curveDetail in the Bezier lookUpTable - this._lutBezierDetail = 0; - // current curveDetail in the Quadratic lookUpTable - this._lutQuadraticDetail = 0; + // 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 = []; - // Used to distinguish between user calls to vertex() and internal calls - this.isProcessingVertices = false; - this._tessy = this._initTessy(); + // current curveDetail in the Bezier lookUpTable + this._lutBezierDetail = 0; + // current curveDetail in the Quadratic lookUpTable + this._lutQuadraticDetail = 0; - this.fontInfos = {}; + // Used to distinguish between user calls to vertex() and internal calls + this.isProcessingVertices = false; + this._tessy = this._initTessy(); - this._curShader = undefined; + this.fontInfos = {}; - return this; -}; + this._curShader = undefined; -p5.RendererGL.prototype = Object.create(p5.Renderer.prototype); + return this; + }}; ////////////////////////////////////////////// // Setting From fc29d0c196cc338e58a9dff27ae3d05ace8bab63 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Tue, 28 Mar 2023 23:14:21 +0900 Subject: [PATCH 04/22] reduce modified lines --- src/core/p5.Renderer.js | 700 ++++++++++++++++++++-------------------- 1 file changed, 351 insertions(+), 349 deletions(-) diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index 9cf4ca7a9e..1d682ef721 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -59,358 +59,301 @@ p5.Renderer = class extends p5.Element { this._fillSet = false; this._leadingSet = false; } - // the renderer should return a 'style' object that it wishes to - // store on the push stack. - push() { - return { - properties: { - _doStroke: this._doStroke, - _strokeSet: this._strokeSet, - _doFill: this._doFill, - _fillSet: this._fillSet, - _tint: this._tint, - _imageMode: this._imageMode, - _rectMode: this._rectMode, - _ellipseMode: this._ellipseMode, - _textFont: this._textFont, - _textLeading: this._textLeading, - _leadingSet: this._leadingSet, - _textSize: this._textSize, - _textAlign: this._textAlign, - _textBaseline: this._textBaseline, - _textStyle: this._textStyle, - _textWrap: this._textWrap - } - }; - } +}; - // a pop() operation is in progress - // the renderer is passed the 'style' object that it returned - // from its push() method. - pop(style) { - if (style.properties) { - // copy the style properties back into the renderer - Object.assign(this, style.properties); +// the renderer should return a 'style' object that it wishes to +// store on the push stack. +p5.Renderer.prototype.push = function() { + return { + properties: { + _doStroke: this._doStroke, + _strokeSet: this._strokeSet, + _doFill: this._doFill, + _fillSet: this._fillSet, + _tint: this._tint, + _imageMode: this._imageMode, + _rectMode: this._rectMode, + _ellipseMode: this._ellipseMode, + _textFont: this._textFont, + _textLeading: this._textLeading, + _leadingSet: this._leadingSet, + _textSize: this._textSize, + _textAlign: this._textAlign, + _textBaseline: this._textBaseline, + _textStyle: this._textStyle, + _textWrap: this._textWrap } + }; +}; + +// a pop() operation is in progress +// the renderer is passed the 'style' object that it returned +// from its push() method. +p5.Renderer.prototype.pop = function(style) { + if (style.properties) { + // copy the style properties back into the renderer + Object.assign(this, style.properties); } +}; - /** +/** * Resize our canvas element. */ - resize(w, h) { - this.width = w; - this.height = h; - this.elt.width = w * this._pInst._pixelDensity; - this.elt.height = h * this._pInst._pixelDensity; - this.elt.style.width = `${w}px`; - this.elt.style.height = `${h}px`; - if (this._isMainCanvas) { - this._pInst._setProperty('width', this.width); - this._pInst._setProperty('height', this.height); - } +p5.Renderer.prototype.resize = function(w, h) { + this.width = w; + this.height = h; + this.elt.width = w * this._pInst._pixelDensity; + this.elt.height = h * this._pInst._pixelDensity; + this.elt.style.width = `${w}px`; + this.elt.style.height = `${h}px`; + if (this._isMainCanvas) { + this._pInst._setProperty('width', this.width); + this._pInst._setProperty('height', this.height); } +}; - get(x, y, w, h) { - const pixelsState = this._pixelsState; - const pd = pixelsState._pixelDensity; - const canvas = this.canvas; +p5.Renderer.prototype.get = function(x, y, w, h) { + const pixelsState = this._pixelsState; + const pd = pixelsState._pixelDensity; + const canvas = this.canvas; - if (typeof x === 'undefined' && typeof y === 'undefined') { + if (typeof x === 'undefined' && typeof y === 'undefined') { // get() - x = y = 0; - w = pixelsState.width; - h = pixelsState.height; - } else { - x *= pd; - y *= pd; + x = y = 0; + w = pixelsState.width; + h = pixelsState.height; + } else { + x *= pd; + y *= pd; - if (typeof w === 'undefined' && typeof h === 'undefined') { + if (typeof w === 'undefined' && typeof h === 'undefined') { // get(x,y) - if (x < 0 || y < 0 || x >= canvas.width || y >= canvas.height) { - return [0, 0, 0, 0]; - } - - return this._getPixel(x, y); + if (x < 0 || y < 0 || x >= canvas.width || y >= canvas.height) { + return [0, 0, 0, 0]; } - // get(x,y,w,h) - } - const region = new p5.Image(w, h); - region.canvas - .getContext('2d') - .drawImage(canvas, x, y, w * pd, h * pd, 0, 0, w, h); - - return region; + return this._getPixel(x, y); + } + // get(x,y,w,h) } - textLeading(l) { - if (typeof l === 'number') { - this._setProperty('_leadingSet', true); - this._setProperty('_textLeading', l); - return this._pInst; - } + const region = new p5.Image(w, h); + region.canvas + .getContext('2d') + .drawImage(canvas, x, y, w * pd, h * pd, 0, 0, w, h); + + return region; +}; - return this._textLeading; +p5.Renderer.prototype.textLeading = function(l) { + if (typeof l === 'number') { + this._setProperty('_leadingSet', true); + this._setProperty('_textLeading', l); + return this._pInst; } - textSize(s) { - if (typeof s === 'number') { - this._setProperty('_textSize', s); - if (!this._leadingSet) { + return this._textLeading; +}; + +p5.Renderer.prototype.textSize = function(s) { + if (typeof s === 'number') { + this._setProperty('_textSize', s); + if (!this._leadingSet) { // only use a default value if not previously set (#5181) - this._setProperty('_textLeading', s * constants._DEFAULT_LEADMULT); - } - return this._applyTextProperties(); + this._setProperty('_textLeading', s * constants._DEFAULT_LEADMULT); } - - return this._textSize; + return this._applyTextProperties(); } - textStyle(s) { - if (s) { - if ( - s === constants.NORMAL || + return this._textSize; +}; + +p5.Renderer.prototype.textStyle = function(s) { + if (s) { + if ( + s === constants.NORMAL || s === constants.ITALIC || s === constants.BOLD || s === constants.BOLDITALIC - ) { - this._setProperty('_textStyle', s); - } - - return this._applyTextProperties(); + ) { + this._setProperty('_textStyle', s); } - return this._textStyle; + return this._applyTextProperties(); } - textAscent() { - if (this._textAscent === null) { - this._updateTextMetrics(); - } - return this._textAscent; - } + return this._textStyle; +}; - textDescent() { - if (this._textDescent === null) { - this._updateTextMetrics(); - } - return this._textDescent; +p5.Renderer.prototype.textAscent = function() { + if (this._textAscent === null) { + this._updateTextMetrics(); } + return this._textAscent; +}; - textAlign(h, v) { - if (typeof h !== 'undefined') { - this._setProperty('_textAlign', h); +p5.Renderer.prototype.textDescent = function() { + if (this._textDescent === null) { + this._updateTextMetrics(); + } + return this._textDescent; +}; - if (typeof v !== 'undefined') { - this._setProperty('_textBaseline', v); - } +p5.Renderer.prototype.textAlign = function(h, v) { + if (typeof h !== 'undefined') { + this._setProperty('_textAlign', h); - return this._applyTextProperties(); - } else { - return { - horizontal: this._textAlign, - vertical: this._textBaseline - }; + if (typeof v !== 'undefined') { + this._setProperty('_textBaseline', v); } + + return this._applyTextProperties(); + } else { + return { + horizontal: this._textAlign, + vertical: this._textBaseline + }; } +}; - textWrap(wrapStyle) { - this._setProperty('_textWrap', wrapStyle); - return this._textWrap; +p5.Renderer.prototype.textWrap = function(wrapStyle) { + this._setProperty('_textWrap', wrapStyle); + return this._textWrap; +}; + +p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { + const p = this._pInst; + const textWrapStyle = this._textWrap; + + let lines; + let line; + let testLine; + let testWidth; + let words; + let chars; + let shiftedY; + let finalMaxHeight = Number.MAX_VALUE; + // fix for #5785 (top of bounding box) + let finalMinHeight = y; + + if (!(this._doFill || this._doStroke)) { + return; } - text(str, x, y, maxWidth, maxHeight) { - const p = this._pInst; - const textWrapStyle = this._textWrap; - - let lines; - let line; - let testLine; - let testWidth; - let words; - let chars; - let shiftedY; - let finalMaxHeight = Number.MAX_VALUE; - // fix for #5785 (top of bounding box) - let finalMinHeight = y; - - if (!(this._doFill || this._doStroke)) { - return; - } + if (typeof str === 'undefined') { + return; + } else if (typeof str !== 'string') { + str = str.toString(); + } + + // Replaces tabs with double-spaces and splits string on any line + // breaks present in the original string + str = str.replace(/(\t)/g, ' '); + lines = str.split('\n'); - if (typeof str === 'undefined') { - return; - } else if (typeof str !== 'string') { - str = str.toString(); + if (typeof maxWidth !== 'undefined') { + if (this._rectMode === constants.CENTER) { + x -= maxWidth / 2; } - // Replaces tabs with double-spaces and splits string on any line - // breaks present in the original string - str = str.replace(/(\t)/g, ' '); - lines = str.split('\n'); + switch (this._textAlign) { + case constants.CENTER: + x += maxWidth / 2; + break; + case constants.RIGHT: + x += maxWidth; + break; + } - if (typeof maxWidth !== 'undefined') { + if (typeof maxHeight !== 'undefined') { if (this._rectMode === constants.CENTER) { - x -= maxWidth / 2; + y -= maxHeight / 2; + finalMinHeight -= maxHeight / 2; } - switch (this._textAlign) { - case constants.CENTER: - x += maxWidth / 2; + let originalY = y; + let ascent = p.textAscent(); + + switch (this._textBaseline) { + case constants.BOTTOM: + shiftedY = y + maxHeight; + y = Math.max(shiftedY, y); + // fix for #5785 (top of bounding box) + finalMinHeight += ascent; break; - case constants.RIGHT: - x += maxWidth; + case constants.CENTER: + shiftedY = y + maxHeight / 2; + y = Math.max(shiftedY, y); + // fix for #5785 (top of bounding box) + finalMinHeight += ascent / 2; break; } - if (typeof maxHeight !== 'undefined') { - if (this._rectMode === constants.CENTER) { - y -= maxHeight / 2; - finalMinHeight -= maxHeight / 2; - } + // remember the max-allowed y-position for any line (fix to #928) + finalMaxHeight = y + maxHeight - ascent; - let originalY = y; - let ascent = p.textAscent(); - - switch (this._textBaseline) { - case constants.BOTTOM: - shiftedY = y + maxHeight; - y = Math.max(shiftedY, y); - // fix for #5785 (top of bounding box) - finalMinHeight += ascent; - break; - case constants.CENTER: - shiftedY = y + maxHeight / 2; - y = Math.max(shiftedY, y); - // fix for #5785 (top of bounding box) - finalMinHeight += ascent / 2; - break; - } - - // remember the max-allowed y-position for any line (fix to #928) - finalMaxHeight = y + maxHeight - ascent; - - // fix for #5785 (bottom of bounding box) - if (this._textBaseline === constants.CENTER) { - finalMaxHeight = originalY + maxHeight - ascent / 2; - } - } else { + // fix for #5785 (bottom of bounding box) + if (this._textBaseline === constants.CENTER) { + finalMaxHeight = originalY + maxHeight - ascent / 2; + } + } else { // no text-height specified, show warning for BOTTOM / CENTER - if (this._textBaseline === constants.BOTTOM || + if (this._textBaseline === constants.BOTTOM || this._textBaseline === constants.CENTER) { // use rectHeight as an approximation for text height - let rectHeight = p.textSize() * this._textLeading; - finalMinHeight = y - rectHeight / 2; - finalMaxHeight = y + rectHeight / 2; - } + let rectHeight = p.textSize() * this._textLeading; + finalMinHeight = y - rectHeight / 2; + finalMaxHeight = y + rectHeight / 2; } + } - // Render lines of text according to settings of textWrap - // Splits lines at spaces, for loop adds one word + space - // at a time and tests length with next word added - if (textWrapStyle === constants.WORD) { - let nlines = []; - for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { - line = ''; - words = lines[lineIndex].split(' '); - for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { - testLine = `${line + words[wordIndex]}` + ' '; - testWidth = this.textWidth(testLine); - if (testWidth > maxWidth && line.length > 0) { - nlines.push(line); - line = `${words[wordIndex]}` + ' '; - } else { - line = testLine; - } - } - nlines.push(line); - } - - let offset = 0; - if (this._textBaseline === constants.CENTER) { - offset = (nlines.length - 1) * p.textLeading() / 2; - } else if (this._textBaseline === constants.BOTTOM) { - offset = (nlines.length - 1) * p.textLeading(); - } - - for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { - line = ''; - words = lines[lineIndex].split(' '); - for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { - testLine = `${line + words[wordIndex]}` + ' '; - testWidth = this.textWidth(testLine); - if (testWidth > maxWidth && line.length > 0) { - this._renderText( - p, - line.trim(), - x, - y - offset, - finalMaxHeight, - finalMinHeight - ); - line = `${words[wordIndex]}` + ' '; - y += p.textLeading(); - } else { - line = testLine; - } + // Render lines of text according to settings of textWrap + // Splits lines at spaces, for loop adds one word + space + // at a time and tests length with next word added + if (textWrapStyle === constants.WORD) { + let nlines = []; + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + line = ''; + words = lines[lineIndex].split(' '); + for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { + testLine = `${line + words[wordIndex]}` + ' '; + testWidth = this.textWidth(testLine); + if (testWidth > maxWidth && line.length > 0) { + nlines.push(line); + line = `${words[wordIndex]}` + ' '; + } else { + line = testLine; } - this._renderText( - p, - line.trim(), - x, - y - offset, - finalMaxHeight, - finalMinHeight - ); - y += p.textLeading(); } - } else { - let nlines = []; - for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { - line = ''; - chars = lines[lineIndex].split(''); - for (let charIndex = 0; charIndex < chars.length; charIndex++) { - testLine = `${line + chars[charIndex]}`; - testWidth = this.textWidth(testLine); - if (testWidth <= maxWidth) { - line += chars[charIndex]; - } else if (testWidth > maxWidth && line.length > 0) { - nlines.push(line); - line = `${chars[charIndex]}`; - } - } - } - nlines.push(line); - let offset = 0; - if (this._textBaseline === constants.CENTER) { - offset = (nlines.length - 1) * p.textLeading() / 2; - } else if (this._textBaseline === constants.BOTTOM) { - offset = (nlines.length - 1) * p.textLeading(); - } + } + + let offset = 0; + if (this._textBaseline === constants.CENTER) { + offset = (nlines.length - 1) * p.textLeading() / 2; + } else if (this._textBaseline === constants.BOTTOM) { + offset = (nlines.length - 1) * p.textLeading(); + } - // Splits lines at characters, for loop adds one char at a time - // and tests length with next char added - for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { - line = ''; - chars = lines[lineIndex].split(''); - for (let charIndex = 0; charIndex < chars.length; charIndex++) { - testLine = `${line + chars[charIndex]}`; - testWidth = this.textWidth(testLine); - if (testWidth <= maxWidth) { - line += chars[charIndex]; - } else if (testWidth > maxWidth && line.length > 0) { - this._renderText( - p, - line.trim(), - x, - y - offset, - finalMaxHeight, - finalMinHeight - ); - y += p.textLeading(); - line = `${chars[charIndex]}`; - } + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + line = ''; + words = lines[lineIndex].split(' '); + for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { + testLine = `${line + words[wordIndex]}` + ' '; + testWidth = this.textWidth(testLine); + if (testWidth > maxWidth && line.length > 0) { + this._renderText( + p, + line.trim(), + x, + y - offset, + finalMaxHeight, + finalMinHeight + ); + line = `${words[wordIndex]}` + ' '; + y += p.textLeading(); + } else { + line = testLine; } } this._renderText( @@ -424,88 +367,147 @@ p5.Renderer = class extends p5.Element { y += p.textLeading(); } } else { - // Offset to account for vertically centering multiple lines of text - no - // need to adjust anything for vertical align top or baseline + let nlines = []; + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + line = ''; + chars = lines[lineIndex].split(''); + for (let charIndex = 0; charIndex < chars.length; charIndex++) { + testLine = `${line + chars[charIndex]}`; + testWidth = this.textWidth(testLine); + if (testWidth <= maxWidth) { + line += chars[charIndex]; + } else if (testWidth > maxWidth && line.length > 0) { + nlines.push(line); + line = `${chars[charIndex]}`; + } + } + } + + nlines.push(line); let offset = 0; if (this._textBaseline === constants.CENTER) { - offset = (lines.length - 1) * p.textLeading() / 2; + offset = (nlines.length - 1) * p.textLeading() / 2; } else if (this._textBaseline === constants.BOTTOM) { - offset = (lines.length - 1) * p.textLeading(); + offset = (nlines.length - 1) * p.textLeading(); } - // Renders lines of text at any line breaks present in the original string - for (let i = 0; i < lines.length; i++) { - this._renderText( - p, - lines[i], - x, - y - offset, - finalMaxHeight, - finalMinHeight - offset - ); - y += p.textLeading(); + // Splits lines at characters, for loop adds one char at a time + // and tests length with next char added + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + line = ''; + chars = lines[lineIndex].split(''); + for (let charIndex = 0; charIndex < chars.length; charIndex++) { + testLine = `${line + chars[charIndex]}`; + testWidth = this.textWidth(testLine); + if (testWidth <= maxWidth) { + line += chars[charIndex]; + } else if (testWidth > maxWidth && line.length > 0) { + this._renderText( + p, + line.trim(), + x, + y - offset, + finalMaxHeight, + finalMinHeight + ); + y += p.textLeading(); + line = `${chars[charIndex]}`; + } + } } + this._renderText( + p, + line.trim(), + x, + y - offset, + finalMaxHeight, + finalMinHeight + ); + y += p.textLeading(); + } + } else { + // Offset to account for vertically centering multiple lines of text - no + // need to adjust anything for vertical align top or baseline + let offset = 0; + if (this._textBaseline === constants.CENTER) { + offset = (lines.length - 1) * p.textLeading() / 2; + } else if (this._textBaseline === constants.BOTTOM) { + offset = (lines.length - 1) * p.textLeading(); } - return p; + // Renders lines of text at any line breaks present in the original string + for (let i = 0; i < lines.length; i++) { + this._renderText( + p, + lines[i], + x, + y - offset, + finalMaxHeight, + finalMinHeight - offset + ); + y += p.textLeading(); + } } - _applyDefaults() { - return this; - } + return p; +}; - /** +p5.Renderer.prototype._applyDefaults = function() { + return this; +}; + +/** * Helper function to check font type (system or otf) */ - _isOpenType(f = this._textFont) { - return typeof f === 'object' && f.font && f.font.supported; - } +p5.Renderer.prototype._isOpenType = function(f = this._textFont) { + return typeof f === 'object' && f.font && f.font.supported; +}; - _updateTextMetrics() { - if (this._isOpenType()) { - this._setProperty('_textAscent', this._textFont._textAscent()); - this._setProperty('_textDescent', this._textFont._textDescent()); - return this; - } +p5.Renderer.prototype._updateTextMetrics = function() { + if (this._isOpenType()) { + this._setProperty('_textAscent', this._textFont._textAscent()); + this._setProperty('_textDescent', this._textFont._textDescent()); + return this; + } - // Adapted from http://stackoverflow.com/a/25355178 - const text = document.createElement('span'); - text.style.fontFamily = this._textFont; - text.style.fontSize = `${this._textSize}px`; - text.innerHTML = 'ABCjgq|'; + // Adapted from http://stackoverflow.com/a/25355178 + const text = document.createElement('span'); + text.style.fontFamily = this._textFont; + text.style.fontSize = `${this._textSize}px`; + text.innerHTML = 'ABCjgq|'; - const block = document.createElement('div'); - block.style.display = 'inline-block'; - block.style.width = '1px'; - block.style.height = '0px'; + const block = document.createElement('div'); + block.style.display = 'inline-block'; + block.style.width = '1px'; + block.style.height = '0px'; - const container = document.createElement('div'); - container.appendChild(text); - container.appendChild(block); + const container = document.createElement('div'); + container.appendChild(text); + container.appendChild(block); - container.style.height = '0px'; - container.style.overflow = 'hidden'; - document.body.appendChild(container); + container.style.height = '0px'; + container.style.overflow = 'hidden'; + document.body.appendChild(container); - block.style.verticalAlign = 'baseline'; - let blockOffset = calculateOffset(block); - let textOffset = calculateOffset(text); - const ascent = blockOffset[1] - textOffset[1]; + block.style.verticalAlign = 'baseline'; + let blockOffset = calculateOffset(block); + let textOffset = calculateOffset(text); + const ascent = blockOffset[1] - textOffset[1]; - block.style.verticalAlign = 'bottom'; - blockOffset = calculateOffset(block); - textOffset = calculateOffset(text); - const height = blockOffset[1] - textOffset[1]; - const descent = height - ascent; + block.style.verticalAlign = 'bottom'; + blockOffset = calculateOffset(block); + textOffset = calculateOffset(text); + const height = blockOffset[1] - textOffset[1]; + const descent = height - ascent; - document.body.removeChild(container); + document.body.removeChild(container); - this._setProperty('_textAscent', ascent); - this._setProperty('_textDescent', descent); + this._setProperty('_textAscent', ascent); + this._setProperty('_textDescent', descent); - return this; - } + return this; }; + /** * Helper fxn to measure ascent and descent. * Adapted from http://stackoverflow.com/a/25355178 From 655eaa3ee322842bd89d5373eae498f88bfd498c Mon Sep 17 00:00:00 2001 From: asukaminato Date: Thu, 30 Mar 2023 23:09:38 +0900 Subject: [PATCH 05/22] add four names --- src/color/p5.Color.js | 2 +- src/core/p5.Renderer.js | 2 +- src/core/p5.Renderer2D.js | 2 +- src/webgl/p5.RendererGL.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/color/p5.Color.js b/src/color/p5.Color.js index 83ead6590f..bd4026865a 100644 --- a/src/color/p5.Color.js +++ b/src/color/p5.Color.js @@ -37,7 +37,7 @@ import color_conversion from './color_conversion'; * for red, green, blue and alpha channel * or CSS color. */ -p5.Color = class { +p5.Color = class Color { constructor(pInst, vals){ // Record color mode and maxes at time of construction. this._storeModeAndMaxes(pInst._colorMode, pInst._colorMaxes); diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index 1d682ef721..26113ce059 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -19,7 +19,7 @@ import * as constants from '../core/constants'; * @param {p5} [pInst] pointer to p5 instance * @param {Boolean} [isMainCanvas] whether we're using it as main canvas */ -p5.Renderer = class extends p5.Element { +p5.Renderer = class Renderer extends p5.Element { constructor(elt, pInst, isMainCanvas){ super(elt, pInst); this.canvas = elt; diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index 28d6a01c23..8af09c891c 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -11,7 +11,7 @@ import './p5.Renderer'; const styleEmpty = 'rgba(0,0,0,0)'; // const alphaThreshold = 0.00125; // minimum visible -p5.Renderer2D = class extends p5.Renderer { +p5.Renderer2D = class Renderer2D extends p5.Renderer { constructor(elt, pInst, isMainCanvas) { super(elt, pInst, isMainCanvas); this.drawingContext = this.canvas.getContext('2d'); diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index c676bb8667..e604f244ff 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -86,7 +86,7 @@ const defaultShaders = { * @todo extend class to include public method for offscreen * rendering (FBO). */ -p5.RendererGL = class extends p5.Renderer { +p5.RendererGL = class RendererGL extends p5.Renderer { constructor(elt, pInst, isMainCanvas, attr){ super(elt, pInst, isMainCanvas); this._setAttributeDefaults(pInst); From 0b76897b7f054688e2cdd01ea0ae654f29722de7 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Fri, 7 Apr 2023 05:18:18 +0900 Subject: [PATCH 06/22] port ImageInfos, FontInfo, Cubic to class --- src/webgl/text.js | 314 +++++++++++++++++++++++----------------------- 1 file changed, 158 insertions(+), 156 deletions(-) diff --git a/src/webgl/text.js b/src/webgl/text.js index 6d589474af..276b1a5d25 100644 --- a/src/webgl/text.js +++ b/src/webgl/text.js @@ -44,21 +44,22 @@ const cellImageHeight = 64; * * the ImageInfos class holds a list of ImageDatas of a given size. */ -function ImageInfos(width, height) { - this.width = width; - this.height = height; - this.infos = []; // the list of images - +class ImageInfos { + constructor(width, height) { + this.width = width; + this.height = height; + this.infos = []; // the list of images + } /** - * - * @method findImage - * @param {Integer} space - * @return {Object} contains the ImageData, and pixel index into that - * ImageData where the free space was allocated. - * - * finds free space of a given size in the ImageData list - */ - this.findImage = function(space) { + * + * @method findImage + * @param {Integer} space + * @return {Object} contains the ImageData, and pixel index into that + * ImageData where the free space was allocated. + * + * finds free space of a given size in the ImageData list + */ + findImage (space) { const imageSize = this.width * this.height; if (space > imageSize) throw new Error('font is too complex to render in 3D'); @@ -109,7 +110,7 @@ function ImageInfos(width, height) { imageInfo.index += space; // move to the start of the next image imageData._dirty = true; return { imageData, index }; - }; + } } /** @@ -141,30 +142,30 @@ const SQRT3 = Math.sqrt(3); * * contains cached images and glyph information for an opentype font */ -const FontInfo = function(font) { - this.font = font; - // the bezier curve coordinates - this.strokeImageInfos = new ImageInfos(strokeImageWidth, strokeImageHeight); - // lists of curve indices for each row/column slice - this.colDimImageInfos = new ImageInfos(gridImageWidth, gridImageHeight); - this.rowDimImageInfos = new ImageInfos(gridImageWidth, gridImageHeight); - // the offset & length of each row/col slice in the glyph - this.colCellImageInfos = new ImageInfos(cellImageWidth, cellImageHeight); - this.rowCellImageInfos = new ImageInfos(cellImageWidth, cellImageHeight); - - // the cached information for each glyph - this.glyphInfos = {}; - +class FontInfo { + constructor(font) { + this.font = font; + // the bezier curve coordinates + this.strokeImageInfos = new ImageInfos(strokeImageWidth, strokeImageHeight); + // lists of curve indices for each row/column slice + this.colDimImageInfos = new ImageInfos(gridImageWidth, gridImageHeight); + this.rowDimImageInfos = new ImageInfos(gridImageWidth, gridImageHeight); + // the offset & length of each row/col slice in the glyph + this.colCellImageInfos = new ImageInfos(cellImageWidth, cellImageHeight); + this.rowCellImageInfos = new ImageInfos(cellImageWidth, cellImageHeight); + + // the cached information for each glyph + this.glyphInfos = {}; + } /** - * @method getGlyphInfo - * @param {Glyph} glyph the x positions of points in the curve - * @returns {Object} the glyphInfo for that glyph - * - * calculates rendering info for a glyph, including the curve information, - * row & column stripes compiled into textures. - */ - - this.getGlyphInfo = function(glyph) { + * @method getGlyphInfo + * @param {Glyph} glyph the x positions of points in the curve + * @returns {Object} the glyphInfo for that glyph + * + * calculates rendering info for a glyph, including the curve information, + * row & column stripes compiled into textures. + */ + getGlyphInfo (glyph) { // check the cache let gi = this.glyphInfos[glyph.index]; if (gi) return gi; @@ -189,25 +190,25 @@ const FontInfo = function(font) { for (i = charGridHeight - 1; i >= 0; --i) rows.push([]); /** - * @function push - * @param {Number[]} xs the x positions of points in the curve - * @param {Number[]} ys the y positions of points in the curve - * @param {Object} v the curve information - * - * adds a curve to the rows & columns that it intersects with - */ + * @function push + * @param {Number[]} xs the x positions of points in the curve + * @param {Number[]} ys the y positions of points in the curve + * @param {Object} v the curve information + * + * adds a curve to the rows & columns that it intersects with + */ function push(xs, ys, v) { const index = strokes.length; // the index of this stroke strokes.push(v); // add this stroke to the list /** - * @function minMax - * @param {Number[]} rg the list of values to compare - * @param {Number} min the initial minimum value - * @param {Number} max the initial maximum value - * - * find the minimum & maximum value in a list of values - */ + * @function minMax + * @param {Number[]} rg the list of values to compare + * @param {Number} min the initial minimum value + * @param {Number} max the initial maximum value + * + * find the minimum & maximum value in a list of values + */ function minMax(rg, min, max) { for (let i = rg.length; i-- > 0; ) { const v = rg[i]; @@ -250,13 +251,13 @@ const FontInfo = function(font) { } /** - * @function clamp - * @param {Number} v the value to clamp - * @param {Number} min the minimum value - * @param {Number} max the maxmimum value - * - * clamps a value between a minimum & maximum value - */ + * @function clamp + * @param {Number} v the value to clamp + * @param {Number} min the minimum value + * @param {Number} max the maxmimum value + * + * clamps a value between a minimum & maximum value + */ function clamp(v, min, max) { if (v < min) return min; if (v > max) return max; @@ -264,39 +265,40 @@ const FontInfo = function(font) { } /** - * @function byte - * @param {Number} v the value to scale - * - * converts a floating-point number in the range 0-1 to a byte 0-255 - */ + * @function byte + * @param {Number} v the value to scale + * + * converts a floating-point number in the range 0-1 to a byte 0-255 + */ function byte(v) { return clamp(255 * v, 0, 255); } /** - * @private - * @class Cubic - * @param {Number} p0 the start point of the curve - * @param {Number} c0 the first control point - * @param {Number} c1 the second control point - * @param {Number} p1 the end point - * - * a cubic curve - */ - function Cubic(p0, c0, c1, p1) { - this.p0 = p0; - this.c0 = c0; - this.c1 = c1; - this.p1 = p1; - - /** - * @method toQuadratic - * @return {Object} the quadratic approximation + * @private + * @class Cubic + * @param {Number} p0 the start point of the curve + * @param {Number} c0 the first control point + * @param {Number} c1 the second control point + * @param {Number} p1 the end point * - * converts the cubic to a quadtratic approximation by - * picking an appropriate quadratic control point + * a cubic curve */ - this.toQuadratic = function() { + class Cubic { + constructor(p0, c0, c1, p1) { + this.p0 = p0; + this.c0 = c0; + this.c1 = c1; + this.p1 = p1; + } + /** + * @method toQuadratic + * @return {Object} the quadratic approximation + * + * converts the cubic to a quadtratic approximation by + * picking an appropriate quadratic control point + */ + toQuadratic () { return { x: this.p0.x, y: this.p0.y, @@ -305,34 +307,34 @@ const FontInfo = function(font) { cx: ((this.c0.x + this.c1.x) * 3 - (this.p0.x + this.p1.x)) / 4, cy: ((this.c0.y + this.c1.y) * 3 - (this.p0.y + this.p1.y)) / 4 }; - }; + } /** - * @method quadError - * @return {Number} the error - * - * calculates the magnitude of error of this curve's - * quadratic approximation. - */ - this.quadError = function() { + * @method quadError + * @return {Number} the error + * + * calculates the magnitude of error of this curve's + * quadratic approximation. + */ + quadError () { return ( p5.Vector.sub( p5.Vector.sub(this.p1, this.p0), p5.Vector.mult(p5.Vector.sub(this.c1, this.c0), 3) ).mag() / 2 ); - }; + } /** - * @method split - * @param {Number} t the value (0-1) at which to split - * @return {Cubic} the second part of the curve - * - * splits the cubic into two parts at a point 't' along the curve. - * this cubic keeps its start point and its end point becomes the - * point at 't'. the 'end half is returned. - */ - this.split = function(t) { + * @method split + * @param {Number} t the value (0-1) at which to split + * @return {Cubic} the second part of the curve + * + * splits the cubic into two parts at a point 't' along the curve. + * this cubic keeps its start point and its end point becomes the + * point at 't'. the 'end half is returned. + */ + split (t) { const m1 = p5.Vector.lerp(this.p0, this.c0, t); const m2 = p5.Vector.lerp(this.c0, this.c1, t); const mm1 = p5.Vector.lerp(m1, m2, t); @@ -343,17 +345,17 @@ const FontInfo = function(font) { const part1 = new Cubic(this.p0, m1, mm1, pt); this.p0 = pt; return part1; - }; + } /** - * @method splitInflections - * @return {Cubic[]} the non-inflecting pieces of this cubic - * - * returns an array containing 0, 1 or 2 cubics split resulting - * from splitting this cubic at its inflection points. - * this cubic is (potentially) altered and returned in the list. - */ - this.splitInflections = function() { + * @method splitInflections + * @return {Cubic[]} the non-inflecting pieces of this cubic + * + * returns an array containing 0, 1 or 2 cubics split resulting + * from splitting this cubic at its inflection points. + * this cubic is (potentially) altered and returned in the list. + */ + splitInflections () { const a = p5.Vector.sub(this.c0, this.p0); const b = p5.Vector.sub(p5.Vector.sub(this.c1, this.c0), a); const c = p5.Vector.sub( @@ -398,24 +400,24 @@ const FontInfo = function(font) { cubics.push(this); return cubics; - }; + } } /** - * @function cubicToQuadratics - * @param {Number} x0 - * @param {Number} y0 - * @param {Number} cx0 - * @param {Number} cy0 - * @param {Number} cx1 - * @param {Number} cy1 - * @param {Number} x1 - * @param {Number} y1 - * @returns {Cubic[]} an array of cubics whose quadratic approximations - * closely match the civen cubic. - * - * converts a cubic curve to a list of quadratics. - */ + * @function cubicToQuadratics + * @param {Number} x0 + * @param {Number} y0 + * @param {Number} cx0 + * @param {Number} cy0 + * @param {Number} cx1 + * @param {Number} cy1 + * @param {Number} x1 + * @param {Number} y1 + * @returns {Cubic[]} an array of cubics whose quadratic approximations + * closely match the civen cubic. + * + * converts a cubic curve to a list of quadratics. + */ function cubicToQuadratics(x0, y0, cx0, cy0, cx1, cy1, x1, y1) { // create the Cubic object and split it at its inflections const cubics = new Cubic( @@ -470,14 +472,14 @@ const FontInfo = function(font) { } /** - * @function pushLine - * @param {Number} x0 - * @param {Number} y0 - * @param {Number} x1 - * @param {Number} y1 - * - * add a straight line to the row/col grid of a glyph - */ + * @function pushLine + * @param {Number} x0 + * @param {Number} y0 + * @param {Number} x1 + * @param {Number} y1 + * + * add a straight line to the row/col grid of a glyph + */ function pushLine(x0, y0, x1, y1) { const mx = (x0 + x1) / 2; const my = (y0 + y1) / 2; @@ -485,15 +487,15 @@ const FontInfo = function(font) { } /** - * @function samePoint - * @param {Number} x0 - * @param {Number} y0 - * @param {Number} x1 - * @param {Number} y1 - * @return {Boolean} true if the two points are sufficiently close - * - * tests if two points are close enough to be considered the same - */ + * @function samePoint + * @param {Number} x0 + * @param {Number} y0 + * @param {Number} x1 + * @param {Number} y1 + * @return {Boolean} true if the two points are sufficiently close + * + * tests if two points are close enough to be considered the same + */ function samePoint(x0, y0, x1, y1) { return Math.abs(x1 - x0) < 0.00001 && Math.abs(y1 - y0) < 0.00001; } @@ -570,16 +572,16 @@ const FontInfo = function(font) { } /** - * @function layout - * @param {Number[][]} dim - * @param {ImageInfo[]} dimImageInfos - * @param {ImageInfo[]} cellImageInfos - * @return {Object} - * - * lays out the curves in a dimension (row or col) into two - * images, one for the indices of the curves themselves, and - * one containing the offset and length of those index spans. - */ + * @function layout + * @param {Number[][]} dim + * @param {ImageInfo[]} dimImageInfos + * @param {ImageInfo[]} cellImageInfos + * @return {Object} + * + * lays out the curves in a dimension (row or col) into two + * images, one for the indices of the curves themselves, and + * one containing the offset and length of those index spans. + */ function layout(dim, dimImageInfos, cellImageInfos) { const dimLength = dim.length; // the number of slices in this dimension const dimImageInfo = dimImageInfos.findImage(dimLength); @@ -634,8 +636,8 @@ const FontInfo = function(font) { }; gi.uGridOffset = [gi.colInfo.dimOffset, gi.rowInfo.dimOffset]; return gi; - }; -}; + } +} p5.RendererGL.prototype._renderText = function(p, line, x, y, maxY) { if (!this._textFont || typeof this._textFont === 'string') { From 45f23151a715a9bb981f3da11276a0cc1b2a7e1a Mon Sep 17 00:00:00 2001 From: asukaminato Date: Fri, 7 Apr 2023 05:33:48 +0900 Subject: [PATCH 07/22] port manual test to class --- .../chp10/example_10_1/catcher.js | 30 +- .../chp10/example_10_10/catcher.js | 57 ++-- .../chp10/example_10_10/drop.js | 76 +++-- .../chp10/example_10_10/timer.js | 42 +-- .../chp10/example_10_2/ball.js | 50 +-- .../chp10/example_10_3/ball.js | 96 +++--- .../chp10/example_10_5/timer.js | 42 +-- .../chp10/example_10_7/drop.js | 76 +++-- .../chp10/example_10_9/catcher.js | 30 +- .../chp10/example_10_9/drop.js | 78 +++-- .../chp10/example_10_9/timer.js | 42 +-- .../learningprocessing/chp8/example_8_1.js | 50 +-- .../learningprocessing/chp8/example_8_2.js | 43 ++- .../learningprocessing/chp8/example_8_3.js | 124 +++---- .../learningprocessing/chp9/example_9_10.js | 80 ++--- .../learningprocessing/chp9/example_9_11.js | 58 ++-- .../learningprocessing/chp9/example_9_12.js | 126 ++++---- .../learningprocessing/chp9/example_9_9.js | 44 +-- .../p5.Font/pathpoints/boids.js | 126 ++++---- .../NOC_2_5_fluidresistance/mover.js | 70 ++-- .../p5.Vector/NOC_6_09_Flocking/boid.js | 302 +++++++++--------- .../p5.Vector/NOC_6_09_Flocking/flock.js | 26 +- .../webgl/performance/points/sketch.js | 26 +- 23 files changed, 855 insertions(+), 839 deletions(-) diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_1/catcher.js b/test/manual-test-examples/learningprocessing/chp10/example_10_1/catcher.js index 8033cf0dc1..dd4bbe3fe1 100644 --- a/test/manual-test-examples/learningprocessing/chp10/example_10_1/catcher.js +++ b/test/manual-test-examples/learningprocessing/chp10/example_10_1/catcher.js @@ -1,18 +1,18 @@ // Catch class -function Catcher(tempR) { - this.r = tempR; // radius - this.x = 0; // location - this.y = 0; +class Catcher { + constructor(tempR) { + this.r = tempR; // radius + this.x = 0; // location + this.y = 0; + } + setLocation(tempX, tempY) { + this.x = tempX; + this.y = tempY; + } + display() { + stroke(0); + fill(175); + ellipse(this.x, this.y, this.r * 2, this.r * 2); + } } - -Catcher.prototype.setLocation = function(tempX, tempY) { - this.x = tempX; - this.y = tempY; -}; - -Catcher.prototype.display = function() { - stroke(0); - fill(175); - ellipse(this.x, this.y, this.r * 2, this.r * 2); -}; diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_10/catcher.js b/test/manual-test-examples/learningprocessing/chp10/example_10_10/catcher.js index 0250bd6abb..85b72ae3e1 100644 --- a/test/manual-test-examples/learningprocessing/chp10/example_10_10/catcher.js +++ b/test/manual-test-examples/learningprocessing/chp10/example_10_10/catcher.js @@ -6,34 +6,33 @@ // Example 10-10: The raindrop catching game -function Catcher(tempR) { - this.r = tempR; // radius - this.col = [50, 10, 10, 150]; // color - this.x = 0; // location - this.y = 0; -} - -Catcher.prototype.setLocation = function(tempX, tempY) { - this.x = tempX; - this.y = tempY; -}; - -Catcher.prototype.display = function() { - stroke(0); - fill(this.col); - ellipse(this.x, this.y, this.r * 2, this.r * 2); -}; - -// A function that returns true or false based on -// if the catcher intersects a raindrop -Catcher.prototype.intersect = function(d) { - // Calculate distance - var distance = dist(this.x, this.y, d.x, d.y); +class Catcher { + constructor(tempR) { + this.r = tempR; // radius + this.col = [50, 10, 10, 150]; // color + this.x = 0; // location + this.y = 0; + } + setLocation(tempX, tempY) { + this.x = tempX; + this.y = tempY; + } + display() { + stroke(0); + fill(this.col); + ellipse(this.x, this.y, this.r * 2, this.r * 2); + } + // A function that returns true or false based on + // if the catcher intersects a raindrop + intersect(d) { + // Calculate distance + var distance = dist(this.x, this.y, d.x, d.y); - // Compare distance to sum of radii - if (distance < this.r + d.r) { - return true; - } else { - return false; + // Compare distance to sum of radii + if (distance < this.r + d.r) { + return true; + } else { + return false; + } } -}; +} diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_10/drop.js b/test/manual-test-examples/learningprocessing/chp10/example_10_10/drop.js index b7848d487b..7be3571399 100644 --- a/test/manual-test-examples/learningprocessing/chp10/example_10_10/drop.js +++ b/test/manual-test-examples/learningprocessing/chp10/example_10_10/drop.js @@ -6,44 +6,42 @@ // Example 10-10: The raindrop catching game -function Drop() { - this.r = 8; // All raindrops are the same size - this.x = random(width); // Start with a random x location - this.y = -this.r * 4; // Start a little above the window - this.speed = random(1, 5); // Speed of raindrop - this.c = [50, 100, 150]; -} - -// Move the raindrop down -Drop.prototype.move = function() { - // Increment by speed - this.y += this.speed; -}; - -// Check if it hits the bottom -Drop.prototype.reachedBottom = function() { - // If we go a little beyond the bottom - if (this.y > height + this.r * 4) { - return true; - } else { - return false; +class Drop { + constructor() { + this.r = 8; // All raindrops are the same size + this.x = random(width); // Start with a random x location + this.y = -this.r * 4; // Start a little above the window + this.speed = random(1, 5); // Speed of raindrop + this.c = [50, 100, 150]; } -}; - -// Display the raindrop -Drop.prototype.display = function() { - // Display the drop - fill(this.c); - noStroke(); - for (var i = 2; i < this.r; i++) { - ellipse(this.x, this.y + i * 4, i * 2, i * 2); + // Move the raindrop down + move() { + // Increment by speed + this.y += this.speed; } -}; - -// If the drop is caught -Drop.prototype.caught = function() { - // Stop it from moving by setting speed equal to zero - this.speed = 0; - // Set the location to somewhere way off-screen - this.y = -1000; -}; + // Check if it hits the bottom + reachedBottom() { + // If we go a little beyond the bottom + if (this.y > height + this.r * 4) { + return true; + } else { + return false; + } + } + // Display the raindrop + display() { + // Display the drop + fill(this.c); + noStroke(); + for (var i = 2; i < this.r; i++) { + ellipse(this.x, this.y + i * 4, i * 2, i * 2); + } + } + // If the drop is caught + caught() { + // Stop it from moving by setting speed equal to zero + this.speed = 0; + // Set the location to somewhere way off-screen + this.y = -1000; + } +} diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_10/timer.js b/test/manual-test-examples/learningprocessing/chp10/example_10_10/timer.js index 4fb0d15d60..66a28358ce 100644 --- a/test/manual-test-examples/learningprocessing/chp10/example_10_10/timer.js +++ b/test/manual-test-examples/learningprocessing/chp10/example_10_10/timer.js @@ -6,25 +6,25 @@ // Example 10-5: Object-oriented timer -function Timer(tempTotalTime) { - this.savedTime = 0; // When Timer started - this.totalTime = tempTotalTime; // How long Timer should last -} - -// Starting the timer -Timer.prototype.start = function() { - // When the timer starts it stores the current time in milliseconds. - this.savedTime = millis(); -}; - -// The function isFinished() returns true if 5,000 ms have passed. -// The work of the timer is farmed out to this method. -Timer.prototype.isFinished = function() { - // Check how much time has passed - var passedTime = millis() - this.savedTime; - if (passedTime > this.totalTime) { - return true; - } else { - return false; +class Timer { + constructor(tempTotalTime) { + this.savedTime = 0; // When Timer started + this.totalTime = tempTotalTime; // How long Timer should last } -}; + // Starting the timer + start() { + // When the timer starts it stores the current time in milliseconds. + this.savedTime = millis(); + } + // The function isFinished() returns true if 5,000 ms have passed. + // The work of the timer is farmed out to this method. + isFinished() { + // Check how much time has passed + var passedTime = millis() - this.savedTime; + if (passedTime > this.totalTime) { + return true; + } else { + return false; + } + } +} diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_2/ball.js b/test/manual-test-examples/learningprocessing/chp10/example_10_2/ball.js index 6176226609..c98c93e79c 100644 --- a/test/manual-test-examples/learningprocessing/chp10/example_10_2/ball.js +++ b/test/manual-test-examples/learningprocessing/chp10/example_10_2/ball.js @@ -7,31 +7,31 @@ // Example 10-2: Bouncing ball class -function Ball(tempR) { - this.r = tempR; - this.x = random(width); - this.y = random(height); - this.xspeed = random(-5, 5); - this.yspeed = random(-5, 5); -} - -Ball.prototype.move = function() { - this.x += this.xspeed; // Increment x - this.y += this.yspeed; // Increment y +class Ball { + constructor(tempR) { + this.r = tempR; + this.x = random(width); + this.y = random(height); + this.xspeed = random(-5, 5); + this.yspeed = random(-5, 5); + } + move() { + this.x += this.xspeed; // Increment x + this.y += this.yspeed; // Increment y - // Check horizontal edges - if (this.x > width || this.x < 0) { - this.xspeed *= -1; + // Check horizontal edges + if (this.x > width || this.x < 0) { + this.xspeed *= -1; + } + //Check vertical edges + if (this.y > height || this.y < 0) { + this.yspeed *= -1; + } } - //Check vertical edges - if (this.y > height || this.y < 0) { - this.yspeed *= -1; + // Draw the ball + display() { + stroke(0); + fill(0, 50); + ellipse(this.x, this.y, this.r * 2, this.r * 2); } -}; - -// Draw the ball -Ball.prototype.display = function() { - stroke(0); - fill(0, 50); - ellipse(this.x, this.y, this.r * 2, this.r * 2); -}; +} diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_3/ball.js b/test/manual-test-examples/learningprocessing/chp10/example_10_3/ball.js index 75a0c3e781..cbc5f6a2fc 100644 --- a/test/manual-test-examples/learningprocessing/chp10/example_10_3/ball.js +++ b/test/manual-test-examples/learningprocessing/chp10/example_10_3/ball.js @@ -7,55 +7,53 @@ // Example 10-3: Bouncing ball with intersection -function Ball(tempR) { - this.r = tempR; // radius - this.x = random(width); - this.y = random(height); - this.xspeed = random(-5, 5); - this.yspeed = random(-5, 5); - this.c = [100, 50]; -} - -Ball.prototype.move = function() { - this.x += this.xspeed; // Increment x - this.y += this.yspeed; // Increment y - - // Check horizontal edges - if (this.x > width || this.x < 0) { - this.xspeed *= -1; +class Ball { + constructor(tempR) { + this.r = tempR; // radius + this.x = random(width); + this.y = random(height); + this.xspeed = random(-5, 5); + this.yspeed = random(-5, 5); + this.c = [100, 50]; } - - // Check vertical edges - if (this.y > height || this.y < 0) { - this.yspeed *= -1; + move() { + this.x += this.xspeed; // Increment x + this.y += this.yspeed; // Increment y + + // Check horizontal edges + if (this.x > width || this.x < 0) { + this.xspeed *= -1; + } + + // Check vertical edges + if (this.y > height || this.y < 0) { + this.yspeed *= -1; + } } -}; - -// Whenever the balls are touching, this highlight() function is called -// and the color is darkened. -Ball.prototype.highlight = function() { - this.c = [0, 150]; -}; - -// Draw the ball -Ball.prototype.display = function() { - stroke(0); - fill(this.c); - ellipse(this.x, this.y, this.r * 2, this.r * 2); - // After the ball is displayed, the color is reset back to a darker gray. - this.c = [100, 50]; -}; - -// A function that returns true or false based on whether two circles intersect -// If distance is less than the sum of radii the circles touch -Ball.prototype.intersect = function(b) { - // Objects can be passed into functions as arguments too! - var distance = dist(this.x, this.y, b.x, b.y); // Calculate distance - - // Compare distance to sum of radii - if (distance < this.r + b.r) { - return true; - } else { - return false; + // Whenever the balls are touching, this highlight() function is called + // and the color is darkened. + highlight() { + this.c = [0, 150]; + } + // Draw the ball + display() { + stroke(0); + fill(this.c); + ellipse(this.x, this.y, this.r * 2, this.r * 2); + // After the ball is displayed, the color is reset back to a darker gray. + this.c = [100, 50]; } -}; + // A function that returns true or false based on whether two circles intersect + // If distance is less than the sum of radii the circles touch + intersect(b) { + // Objects can be passed into functions as arguments too! + var distance = dist(this.x, this.y, b.x, b.y); // Calculate distance + + // Compare distance to sum of radii + if (distance < this.r + b.r) { + return true; + } else { + return false; + } + } +} diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_5/timer.js b/test/manual-test-examples/learningprocessing/chp10/example_10_5/timer.js index 0328a2ee77..1d3164a3bc 100644 --- a/test/manual-test-examples/learningprocessing/chp10/example_10_5/timer.js +++ b/test/manual-test-examples/learningprocessing/chp10/example_10_5/timer.js @@ -1,24 +1,24 @@ // Timer class -function Timer(tempTotalTime) { - this.savedTime = 0; // When Timer started - this.totalTime = tempTotalTime; // How long Timer should last -} - -// Starting the timer -Timer.prototype.start = function() { - // When the timer starts it stores the current time in milliseconds. - this.savedTime = millis(); -}; - -// The function isFinished() returns true if 5,000 ms have passed. -// The work of the timer is farmed out to this method. -Timer.prototype.isFinished = function() { - // Check how much time has passed - var passedTime = millis() - this.savedTime; - if (passedTime > this.totalTime) { - return true; - } else { - return false; +class Timer { + constructor(tempTotalTime) { + this.savedTime = 0; // When Timer started + this.totalTime = tempTotalTime; // How long Timer should last } -}; + // Starting the timer + start() { + // When the timer starts it stores the current time in milliseconds. + this.savedTime = millis(); + } + // The function isFinished() returns true if 5,000 ms have passed. + // The work of the timer is farmed out to this method. + isFinished() { + // Check how much time has passed + var passedTime = millis() - this.savedTime; + if (passedTime > this.totalTime) { + return true; + } else { + return false; + } + } +} diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_7/drop.js b/test/manual-test-examples/learningprocessing/chp10/example_10_7/drop.js index 95143a0ce4..960311f59e 100644 --- a/test/manual-test-examples/learningprocessing/chp10/example_10_7/drop.js +++ b/test/manual-test-examples/learningprocessing/chp10/example_10_7/drop.js @@ -8,44 +8,42 @@ // Drop class -function Drop() { - this.r = 8; // All raindrops are the same size - this.x = random(width); // Start with a random x location - this.y = -this.r * 4; // Start a little above the window - this.speed = random(1, 5); // Pick a random speed - this.c = [50, 100, 150]; // Color -} - -// Move the raindrop down -Drop.prototype.move = function() { - // Increment by speed - this.y += this.speed; -}; - -// Check if it hits the bottom -Drop.prototype.reachedBottom = function() { - // If we go a little beyond the bottom - if (this.y > height + this.r * 4) { - return true; - } else { - return false; +class Drop { + constructor() { + this.r = 8; // All raindrops are the same size + this.x = random(width); // Start with a random x location + this.y = -this.r * 4; // Start a little above the window + this.speed = random(1, 5); // Pick a random speed + this.c = [50, 100, 150]; // Color } -}; - -// Display the raindrop -Drop.prototype.display = function() { - // Display the drop - fill(this.c); - noStroke(); - for (var i = 2; i < this.r; i++) { - ellipse(this.x, this.y + i * 4, i * 2, i * 2); + // Move the raindrop down + move() { + // Increment by speed + this.y += this.speed; } -}; - -// If the drop is caught -Drop.prototype.caught = function() { - // Stop it from moving by setting speed equal to zero - this.speed = 0; - // Set the location to somewhere way off-screen - this.y = -1000; -}; + // Check if it hits the bottom + reachedBottom() { + // If we go a little beyond the bottom + if (this.y > height + this.r * 4) { + return true; + } else { + return false; + } + } + // Display the raindrop + display() { + // Display the drop + fill(this.c); + noStroke(); + for (var i = 2; i < this.r; i++) { + ellipse(this.x, this.y + i * 4, i * 2, i * 2); + } + } + // If the drop is caught + caught() { + // Stop it from moving by setting speed equal to zero + this.speed = 0; + // Set the location to somewhere way off-screen + this.y = -1000; + } +} diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_9/catcher.js b/test/manual-test-examples/learningprocessing/chp10/example_10_9/catcher.js index 7402c4dfc1..cfeeba0642 100644 --- a/test/manual-test-examples/learningprocessing/chp10/example_10_9/catcher.js +++ b/test/manual-test-examples/learningprocessing/chp10/example_10_9/catcher.js @@ -6,19 +6,19 @@ // Example 10-1: Catcher -function Catcher(tempR) { - this.r = tempR; // radius - this.x = 0; // location - this.y = 0; +class Catcher { + constructor(tempR) { + this.r = tempR; // radius + this.x = 0; // location + this.y = 0; + } + setLocation(tempX, tempY) { + this.x = tempX; + this.y = tempY; + } + display() { + stroke(0); + fill(175); + ellipse(this.x, this.y, this.r * 2, this.r * 2); + } } - -Catcher.prototype.setLocation = function(tempX, tempY) { - this.x = tempX; - this.y = tempY; -}; - -Catcher.prototype.display = function() { - stroke(0); - fill(175); - ellipse(this.x, this.y, this.r * 2, this.r * 2); -}; diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_9/drop.js b/test/manual-test-examples/learningprocessing/chp10/example_10_9/drop.js index aa3c427ef0..ea54d69f74 100644 --- a/test/manual-test-examples/learningprocessing/chp10/example_10_9/drop.js +++ b/test/manual-test-examples/learningprocessing/chp10/example_10_9/drop.js @@ -6,47 +6,45 @@ // Example 10-7: Drops one at a time -function Drop() { - this.r = 8; // Radius of raindrop (all raindrops are the same size) +class Drop { + constructor() { + this.r = 8; // Radius of raindrop (all raindrops are the same size) - // Variables for location of raindrop - this.x = random(width); - this.y = -this.r * 4; + // Variables for location of raindrop + this.x = random(width); + this.y = -this.r * 4; - this.speed = random(1, 5); // Speed of raindrop - this.c = [50, 100, 150]; -} - -// Move the raindrop down -Drop.prototype.move = function() { - // Increment by speed - this.y += this.speed; -}; - -// Check if it hits the bottom -Drop.prototype.reachedBottom = function() { - // If we go a little beyond the bottom - if (this.y > height + this.r * 4) { - return true; - } else { - return false; + this.speed = random(1, 5); // Speed of raindrop + this.c = [50, 100, 150]; } -}; - -// Display the raindrop -Drop.prototype.display = function() { - // Display the drop - fill(this.c); - noStroke(); - for (var i = 2; i < this.r; i++) { - ellipse(this.x, this.y + i * 4, i * 2, i * 2); + // Move the raindrop down + move() { + // Increment by speed + this.y += this.speed; } -}; - -// If the drop is caught -Drop.prototype.caught = function() { - // Stop it from moving by setting speed equal to zero - this.speed = 0; - // Set the location to somewhere way off-screen - this.y = -1000; -}; + // Check if it hits the bottom + reachedBottom() { + // If we go a little beyond the bottom + if (this.y > height + this.r * 4) { + return true; + } else { + return false; + } + } + // Display the raindrop + display() { + // Display the drop + fill(this.c); + noStroke(); + for (var i = 2; i < this.r; i++) { + ellipse(this.x, this.y + i * 4, i * 2, i * 2); + } + } + // If the drop is caught + caught() { + // Stop it from moving by setting speed equal to zero + this.speed = 0; + // Set the location to somewhere way off-screen + this.y = -1000; + } +} diff --git a/test/manual-test-examples/learningprocessing/chp10/example_10_9/timer.js b/test/manual-test-examples/learningprocessing/chp10/example_10_9/timer.js index 58e10ee54e..d4ded395db 100644 --- a/test/manual-test-examples/learningprocessing/chp10/example_10_9/timer.js +++ b/test/manual-test-examples/learningprocessing/chp10/example_10_9/timer.js @@ -6,25 +6,25 @@ // Example 10-5: Object-oriented timer -function Timer(tempTotalTime) { - this.savedTime = 0; // When Timer started - this.totalTime = tempTotalTime; // How long Timer should last -} - -// Starting the timer -Timer.prototype.start = function() { - // When the timer starts it stores the current time in milliseconds. - this.savedTime = millis(); -}; - -// The function isFinished() returns true if 5,000 ms have passed. -// The work of the timer is farmed out to this method. -Timer.prototype.isFinished = function() { - // Check how much time has passed - var passedTime = millis() - this.savedTime; - if (passedTime > this.totalTime) { - return true; - } else { - return false; +class Timer { + constructor(tempTotalTime) { + this.savedTime = 0; // When Timer started + this.totalTime = tempTotalTime; // How long Timer should last } -}; + // Starting the timer + start() { + // When the timer starts it stores the current time in milliseconds. + this.savedTime = millis(); + } + // The function isFinished() returns true if 5,000 ms have passed. + // The work of the timer is farmed out to this method. + isFinished() { + // Check how much time has passed + var passedTime = millis() - this.savedTime; + if (passedTime > this.totalTime) { + return true; + } else { + return false; + } + } +} diff --git a/test/manual-test-examples/learningprocessing/chp8/example_8_1.js b/test/manual-test-examples/learningprocessing/chp8/example_8_1.js index 643a6d8ea0..a173cddb72 100644 --- a/test/manual-test-examples/learningprocessing/chp8/example_8_1.js +++ b/test/manual-test-examples/learningprocessing/chp8/example_8_1.js @@ -6,6 +6,31 @@ // Example 8-1: A Car class and a Car object +// Define a class below the rest of the program. +class Car { + constructor() { + this.c = 175; + this.xpos = width / 2; + this.ypos = height / 2; + this.xspeed = 1; + } + display() { + // Function. + // The car is just a square + rectMode(CENTER); + stroke(0); + fill(this.c); + rect(this.xpos, this.ypos, 20, 10); + } + move() { + // Function. + this.xpos = this.xpos + this.xspeed; + if (this.xpos > width) { + this.xpos = 0; + } + } +} + var myCar; // Declare car object as a globle variable. function setup() { @@ -20,28 +45,3 @@ function draw() { myCar.move(); // Operate the car object in draw( ) by calling object methods using the dots syntax. myCar.display(); } - -// Define a class below the rest of the program. -function Car() { - this.c = 175; - this.xpos = width / 2; - this.ypos = height / 2; - this.xspeed = 1; -} - -Car.prototype.display = function() { - // Function. - // The car is just a square - rectMode(CENTER); - stroke(0); - fill(this.c); - rect(this.xpos, this.ypos, 20, 10); -}; - -Car.prototype.move = function() { - // Function. - this.xpos = this.xpos + this.xspeed; - if (this.xpos > width) { - this.xpos = 0; - } -}; diff --git a/test/manual-test-examples/learningprocessing/chp8/example_8_2.js b/test/manual-test-examples/learningprocessing/chp8/example_8_2.js index e94c4a4741..a29e80d21d 100644 --- a/test/manual-test-examples/learningprocessing/chp8/example_8_2.js +++ b/test/manual-test-examples/learningprocessing/chp8/example_8_2.js @@ -8,6 +8,27 @@ var myCar1; var myCar2; // Two objects! +class Car { + constructor(tempC, tempXpos, tempYpos, tempXspeed) { + // Even though there are multiple objects, we still only need one class. No matter how many cookies we make, only one cookie cutter is needed.Isn’t object-oriented programming swell? + this.c = tempC; + this.xpos = tempXpos; + this.ypos = tempYpos; + this.xspeed = tempXspeed; + } + display() { + stroke(0); + fill(this.c); + rectMode(CENTER); + rect(this.xpos, this.ypos, 20, 10); + } + move() { + this.xpos = this.xpos + this.xspeed; + if (this.xpos > width) { + this.xpos = 0; + } + } +} function setup() { createCanvas(200, 200); @@ -22,25 +43,3 @@ function draw() { myCar2.move(); myCar2.display(); } - -function Car(tempC, tempXpos, tempYpos, tempXspeed) { - // Even though there are multiple objects, we still only need one class. No matter how many cookies we make, only one cookie cutter is needed.Isn’t object-oriented programming swell? - this.c = tempC; - this.xpos = tempXpos; - this.ypos = tempYpos; - this.xspeed = tempXspeed; -} - -Car.prototype.display = function() { - stroke(0); - fill(this.c); - rectMode(CENTER); - rect(this.xpos, this.ypos, 20, 10); -}; - -Car.prototype.move = function() { - this.xpos = this.xpos + this.xspeed; - if (this.xpos > width) { - this.xpos = 0; - } -}; diff --git a/test/manual-test-examples/learningprocessing/chp8/example_8_3.js b/test/manual-test-examples/learningprocessing/chp8/example_8_3.js index db51f84cb0..efbd2942ab 100644 --- a/test/manual-test-examples/learningprocessing/chp8/example_8_3.js +++ b/test/manual-test-examples/learningprocessing/chp8/example_8_3.js @@ -5,6 +5,73 @@ // Ported by Lauren McCarthy // Example 8-3 +class Zoog { + constructor(tempX, tempY, tempW, tempH, tempEyeSize) { + // Zoog's variables + this.x = tempX; + this.y = tempY; + this.w = tempW; + this.h = tempH; + this.eyeSize = tempEyeSize; + } + // Move Zoog + jiggle(speed) { + // Change the location of Zoog randomly + this.x = this.x + random(-1, 1) * speed; + this.y = this.y + random(-1, 1) * speed; + // Constrain Zoog to window + this.x = constrain(this.x, 0, width); + this.y = constrain(this.y, 0, height); + } + // Display Zoog + display() { + // Set ellipses and rects to CENTER mode + ellipseMode(CENTER); + rectMode(CENTER); + // Draw Zoog's arms with a for loop + for (var i = this.y - this.h / 3; i < this.y + this.h / 2; i += 10) { + stroke(0); + line(this.x - this.w / 4, i, this.x + this.w / 4, i); + } + // Draw Zoog's body + stroke(0); + fill(175); + rect(this.x, this.y, this.w / 6, this.h); + // Draw Zoog's head + stroke(0); + fill(255); + ellipse(this.x, this.y - this.h, this.w, this.h); + // Draw Zoog's eyes + fill(0); + ellipse( + this.x - this.w / 3, + this.y - this.h, + this.eyeSize, + this.eyeSize * 2 + ); + ellipse( + this.x + this.w / 3, + this.y - this.h, + this.eyeSize, + this.eyeSize * 2 + ); + // Draw Zoog's legs + stroke(0); + line( + this.x - this.w / 12, + this.y + this.h / 2, + this.x - this.w / 4, + this.y + this.h / 2 + 10 + ); + line( + this.x + this.w / 12, + this.y + this.h / 2, + this.x + this.w / 4, + this.y + this.h / 2 + 10 + ); + } +} + var zoog; function setup() { @@ -20,60 +87,3 @@ function draw() { zoog.jiggle(factor); zoog.display(); } - -function Zoog(tempX, tempY, tempW, tempH, tempEyeSize) { - // Zoog's variables - this.x = tempX; - this.y = tempY; - this.w = tempW; - this.h = tempH; - this.eyeSize = tempEyeSize; -} - -// Move Zoog -Zoog.prototype.jiggle = function(speed) { - // Change the location of Zoog randomly - this.x = this.x + random(-1, 1) * speed; - this.y = this.y + random(-1, 1) * speed; - // Constrain Zoog to window - this.x = constrain(this.x, 0, width); - this.y = constrain(this.y, 0, height); -}; - -// Display Zoog -Zoog.prototype.display = function() { - // Set ellipses and rects to CENTER mode - ellipseMode(CENTER); - rectMode(CENTER); - // Draw Zoog's arms with a for loop - for (var i = this.y - this.h / 3; i < this.y + this.h / 2; i += 10) { - stroke(0); - line(this.x - this.w / 4, i, this.x + this.w / 4, i); - } - // Draw Zoog's body - stroke(0); - fill(175); - rect(this.x, this.y, this.w / 6, this.h); - // Draw Zoog's head - stroke(0); - fill(255); - ellipse(this.x, this.y - this.h, this.w, this.h); - // Draw Zoog's eyes - fill(0); - ellipse(this.x - this.w / 3, this.y - this.h, this.eyeSize, this.eyeSize * 2); - ellipse(this.x + this.w / 3, this.y - this.h, this.eyeSize, this.eyeSize * 2); - // Draw Zoog's legs - stroke(0); - line( - this.x - this.w / 12, - this.y + this.h / 2, - this.x - this.w / 4, - this.y + this.h / 2 + 10 - ); - line( - this.x + this.w / 12, - this.y + this.h / 2, - this.x + this.w / 4, - this.y + this.h / 2 + 10 - ); -}; diff --git a/test/manual-test-examples/learningprocessing/chp9/example_9_10.js b/test/manual-test-examples/learningprocessing/chp9/example_9_10.js index 2528538066..51ff5b1f09 100644 --- a/test/manual-test-examples/learningprocessing/chp9/example_9_10.js +++ b/test/manual-test-examples/learningprocessing/chp9/example_9_10.js @@ -7,6 +7,47 @@ // Example 9-10: Interactive stripes // An array of stripes + +class Stripe { + constructor() { + // All stripes start at 0 + this.x = 0; // horizontal location of stripe + + // All stripes have a random positive speed + this.speed = random(1); // speed of stripe + this.w = random(10, 30); // width of stripe + + // A boolean variable keeps track of the object's state. + this.mouse = false; // state of stripe (mouse is over or not?) + } + // Draw stripe + display() { + // Boolean variable determines Stripe color. + if (this.mouse) { + fill(255); + } else { + fill(255, 100); + } + + noStroke(); + rect(this.x, 0, this.w, height); + } + // Move stripe + move() { + this.x += this.speed; + if (this.x > width + 20) this.x = -20; + } + // Check to see if point (mx,my) is inside the Stripe. + rollover(mx, my) { + // Left edge is x, Right edge is x + w + if (mx > this.x && mx < this.x + this.w) { + this.mouse = true; + } else { + this.mouse = false; + } + } +} + var stripes = []; function setup() { @@ -28,42 +69,3 @@ function draw() { stripes[i].display(); } } - -function Stripe() { - // All stripes start at 0 - this.x = 0; // horizontal location of stripe - // All stripes have a random positive speed - this.speed = random(1); // speed of stripe - this.w = random(10, 30); // width of stripe - // A boolean variable keeps track of the object's state. - this.mouse = false; // state of stripe (mouse is over or not?) -} - -// Draw stripe -Stripe.prototype.display = function() { - // Boolean variable determines Stripe color. - if (this.mouse) { - fill(255); - } else { - fill(255, 100); - } - - noStroke(); - rect(this.x, 0, this.w, height); -}; - -// Move stripe -Stripe.prototype.move = function() { - this.x += this.speed; - if (this.x > width + 20) this.x = -20; -}; - -// Check to see if point (mx,my) is inside the Stripe. -Stripe.prototype.rollover = function(mx, my) { - // Left edge is x, Right edge is x + w - if (mx > this.x && mx < this.x + this.w) { - this.mouse = true; - } else { - this.mouse = false; - } -}; diff --git a/test/manual-test-examples/learningprocessing/chp9/example_9_11.js b/test/manual-test-examples/learningprocessing/chp9/example_9_11.js index a18ab24b95..ca11a64669 100644 --- a/test/manual-test-examples/learningprocessing/chp9/example_9_11.js +++ b/test/manual-test-examples/learningprocessing/chp9/example_9_11.js @@ -6,6 +6,34 @@ var balls = []; var gravity = 0.1; +class Ball { + constructor(tempX, tempY, tempW) { + this.x = tempX; + this.y = tempY; + this.w = tempW; + this.speed = 0; + } + gravity() { + // Add gravity to speed + this.speed = this.speed + gravity; + } + move() { + // Add speed to y location + this.y = this.y + this.speed; + // If square reaches the bottom + // Reverse speed + if (this.y > height) { + this.speed = this.speed * -0.95; + this.y = height; + } + } + display() { + // Display the circle + fill(175); + stroke(0); + ellipse(this.x, this.y, this.w, this.w); + } +} function setup() { createCanvas(200, 200); @@ -40,33 +68,3 @@ function mousePressed() { // In addition, the append() function requires that you explicitly state the type of data in the array again by putting the // array data type in parentheses: (Ball[]) This is known as casting. } - -function Ball(tempX, tempY, tempW) { - this.x = tempX; - this.y = tempY; - this.w = tempW; - this.speed = 0; -} - -Ball.prototype.gravity = function() { - // Add gravity to speed - this.speed = this.speed + gravity; -}; - -Ball.prototype.move = function() { - // Add speed to y location - this.y = this.y + this.speed; - // If square reaches the bottom - // Reverse speed - if (this.y > height) { - this.speed = this.speed * -0.95; - this.y = height; - } -}; - -Ball.prototype.display = function() { - // Display the circle - fill(175); - stroke(0); - ellipse(this.x, this.y, this.w, this.w); -}; diff --git a/test/manual-test-examples/learningprocessing/chp9/example_9_12.js b/test/manual-test-examples/learningprocessing/chp9/example_9_12.js index f310263d44..5535bdbdf2 100644 --- a/test/manual-test-examples/learningprocessing/chp9/example_9_12.js +++ b/test/manual-test-examples/learningprocessing/chp9/example_9_12.js @@ -8,6 +8,75 @@ // The only difference between this example and the previous chapter (Example 8-3) // is the use of an array for multiple Zoog objects. +class Zoog { + constructor(tempX, tempY, tempW, tempH, tempEyeSize) { + // Zoog's variables + this.x = tempX; + this.y = tempY; + this.w = tempW; + this.h = tempH; + this.eyeSize = tempEyeSize; + } + // Move Zoog + jiggle() { + // For simplicity we have also removed the “speed” argument from the jiggle() function. Try adding it back in as an exercise. + // Change the location + this.x = this.x + random(-1, 1); + this.y = this.y + random(-1, 1); + // Constrain Zoog to window + this.x = constrain(this.x, 0, width); + this.y = constrain(this.y, 0, height); + } + // Display Zoog + display() { + // Set ellipses and rects to CENTER mode + ellipseMode(CENTER); + rectMode(CENTER); + // Draw Zoog's arms with a for loop + for (var i = this.y - this.h / 3; i < this.y + this.h / 2; i += 10) { + stroke(0); + line(this.x - this.w / 4, i, this.x + this.w / 4, i); + } + // Draw Zoog's body + stroke(0); + fill(175); + rect(this.x, this.y, this.w / 6, this.h); + // Draw Zoog's head + stroke(0); + fill(255); + ellipse(this.x, this.y - this.h, this.w, this.h); + // Draw Zoog's eyes + fill(0); + ellipse( + this.x - this.w / 3, + this.y - this.h, + this.eyeSize, + this.eyeSize * 2 + ); + ellipse( + this.x + this.w / 3, + this.y - this.h, + this.eyeSize, + this.eyeSize * 2 + ); + // Draw Zoog's legs + stroke(0); + line( + this.x - this.w / 12, + this.y + this.h / 2, + this.x - this.w / 4, + this.y + this.h / 2 + 10 + ); + line( + this.x + this.w / 12, + this.y + this.h / 2, + this.x + this.w / 4, + this.y + this.h / 2 + 10 + ); + } +} + + var zoogies = []; function setup() { @@ -26,60 +95,3 @@ function draw() { } } -function Zoog(tempX, tempY, tempW, tempH, tempEyeSize) { - // Zoog's variables - this.x = tempX; - this.y = tempY; - this.w = tempW; - this.h = tempH; - this.eyeSize = tempEyeSize; -} - -// Move Zoog -Zoog.prototype.jiggle = function() { - // For simplicity we have also removed the “speed” argument from the jiggle() function. Try adding it back in as an exercise. - // Change the location - this.x = this.x + random(-1, 1); - this.y = this.y + random(-1, 1); - // Constrain Zoog to window - this.x = constrain(this.x, 0, width); - this.y = constrain(this.y, 0, height); -}; - -// Display Zoog -Zoog.prototype.display = function() { - // Set ellipses and rects to CENTER mode - ellipseMode(CENTER); - rectMode(CENTER); - // Draw Zoog's arms with a for loop - for (var i = this.y - this.h / 3; i < this.y + this.h / 2; i += 10) { - stroke(0); - line(this.x - this.w / 4, i, this.x + this.w / 4, i); - } - // Draw Zoog's body - stroke(0); - fill(175); - rect(this.x, this.y, this.w / 6, this.h); - // Draw Zoog's head - stroke(0); - fill(255); - ellipse(this.x, this.y - this.h, this.w, this.h); - // Draw Zoog's eyes - fill(0); - ellipse(this.x - this.w / 3, this.y - this.h, this.eyeSize, this.eyeSize * 2); - ellipse(this.x + this.w / 3, this.y - this.h, this.eyeSize, this.eyeSize * 2); - // Draw Zoog's legs - stroke(0); - line( - this.x - this.w / 12, - this.y + this.h / 2, - this.x - this.w / 4, - this.y + this.h / 2 + 10 - ); - line( - this.x + this.w / 12, - this.y + this.h / 2, - this.x + this.w / 4, - this.y + this.h / 2 + 10 - ); -}; diff --git a/test/manual-test-examples/learningprocessing/chp9/example_9_9.js b/test/manual-test-examples/learningprocessing/chp9/example_9_9.js index be39507dd4..d51317457b 100644 --- a/test/manual-test-examples/learningprocessing/chp9/example_9_9.js +++ b/test/manual-test-examples/learningprocessing/chp9/example_9_9.js @@ -6,6 +6,28 @@ // Example 9-9: An array of Car objects +// The Car class does not change whether we are making one car, 100 cars or 1,000 cars! +class Car { + constructor(c, xpos, ypos, xspeed) { + this.c = c; + this.xpos = xpos; + this.ypos = ypos; + this.xspeed = xspeed; + } + display() { + rectMode(CENTER); + stroke(0); + fill(this.c); + rect(this.xpos, this.ypos, 20, 10); + } + move() { + this.xpos = this.xpos + this.xspeed; + if (this.xpos > width) { + this.xpos = 0; + } + } +} + var cars = []; // An array for Car objects function setup() { @@ -25,25 +47,3 @@ function draw() { cars[i].display(); } } - -// The Car class does not change whether we are making one car, 100 cars or 1,000 cars! -function Car(c, xpos, ypos, xspeed) { - this.c = c; - this.xpos = xpos; - this.ypos = ypos; - this.xspeed = xspeed; -} - -Car.prototype.display = function() { - rectMode(CENTER); - stroke(0); - fill(this.c); - rect(this.xpos, this.ypos, 20, 10); -}; - -Car.prototype.move = function() { - this.xpos = this.xpos + this.xspeed; - if (this.xpos > width) { - this.xpos = 0; - } -}; diff --git a/test/manual-test-examples/p5.Font/pathpoints/boids.js b/test/manual-test-examples/p5.Font/pathpoints/boids.js index 9fa0eb3bfc..c868aa9c33 100644 --- a/test/manual-test-examples/p5.Font/pathpoints/boids.js +++ b/test/manual-test-examples/p5.Font/pathpoints/boids.js @@ -1,32 +1,34 @@ // adapted from Shiffman's The Nature of Code // http://natureofcode.com -function Boid(target) { - this.acceleration = createVector(0, 0); - this.velocity = createVector(random(-1, 1), random(-1, 1)); - this.position = createVector(width / 2, height / 2); - - this.r = 3.0; - this.maxspeed = 3; // Maximum speed - this.maxforce = 0.05; // Maximum steering force - - this.theta = - p5.Vector.fromAngle(radians(target.alpha)).heading() + radians(90); - this.target = createVector(target.x, target.y); - this.arrived = false; - this.hidden = true; - - this.place = function(x, y) { +class Boid { + constructor(target) { + this.acceleration = createVector(0, 0); + this.velocity = createVector(random(-1, 1), random(-1, 1)); + this.position = createVector(width / 2, height / 2); + + this.r = 3.0; + this.maxspeed = 3; // Maximum speed + this.maxforce = 0.05; // Maximum steering force + + this.theta = + p5.Vector.fromAngle(radians(target.alpha)).heading() + radians(90); + this.target = createVector(target.x, target.y); + this.arrived = false; + this.hidden = true; + } + place (x, y) { this.position = createVector(mouseX, mouseY); this.velocity = p5.Vector.sub( createVector(mouseX, mouseY), createVector(pmouseX, pmouseY) ); this.hidden = false; - }; + } - this.run = function(boids) { - if (this.hidden) return; + run (boids) { + if (this.hidden) + return; if (flock.assemble) { this.arrive(this.target); @@ -36,18 +38,19 @@ function Boid(target) { this.update(); this.borders(); this.render(); - }; + } - this.applyForce = function(force) { + applyForce (force) { // We could add mass here if we want A = F / M this.acceleration.add(force); - }; + } // We accumulate a new acceleration each time based on three rules - this.flock = function(boids) { + flock (boids) { var sep = this.separate(boids); // Separation var ali = this.align(boids); // Alignment var coh = this.cohesion(boids); // Cohesion + // Arbitrarily weight these forces sep.mult(1.5); ali.mult(1.0); @@ -56,15 +59,13 @@ function Boid(target) { this.applyForce(sep); this.applyForce(ali); this.applyForce(coh); - }; + } // Method to update location - this.update = function() { - if ( - flock.assemble && - !this.arrived && - this.target.dist(this.position) < 1 - ) { + update () { + if (flock.assemble && + !this.arrived && + this.target.dist(this.position) < 1) { this.arrived = true; this.velocity = p5.Vector.fromAngle(this.theta + radians(90)); } else { @@ -73,9 +74,9 @@ function Boid(target) { this.position.add(this.velocity); this.acceleration.mult(0); } - }; + } - this.seek = function(target) { + seek (target) { var desired = p5.Vector.sub(target, this.position); // Normalize desired and scale to maximum speed desired.normalize(); @@ -84,9 +85,9 @@ function Boid(target) { var steer = p5.Vector.sub(desired, this.velocity); steer.limit(this.maxforce); // Limit to maximum steering force return steer; - }; + } - this.render = function() { + render () { // Draw a triangle rotated in the direction of velocity var theta = this.velocity.heading() + radians(90); fill(255); @@ -100,19 +101,23 @@ function Boid(target) { vertex(this.r, this.r * 2); endShape(CLOSE); pop(); - }; + } // Wraparound - this.borders = function() { - if (this.position.x < -this.r) this.position.x = width + this.r; - if (this.position.y < -this.r) this.position.y = height + this.r; - if (this.position.x > width + this.r) this.position.x = -this.r; - if (this.position.y > height + this.r) this.position.y = -this.r; - }; + borders () { + if (this.position.x < -this.r) + this.position.x = width + this.r; + if (this.position.y < -this.r) + this.position.y = height + this.r; + if (this.position.x > width + this.r) + this.position.x = -this.r; + if (this.position.y > height + this.r) + this.position.y = -this.r; + } // Separation // Method checks for nearby boids and steers away - this.separate = function(boids) { + separate (boids) { var desiredseparation = 25.0; var steer = createVector(0, 0); var count = 0; @@ -143,11 +148,11 @@ function Boid(target) { steer.limit(this.maxforce); } return steer; - }; + } // Alignment // For every nearby boid in the system, calculate the average velocity - this.align = function(boids) { + align (boids) { var neighbordist = 50; var sum = createVector(0, 0); var count = 0; @@ -168,11 +173,11 @@ function Boid(target) { } else { return createVector(0, 0); } - }; + } // Cohesion // For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location - this.cohesion = function(boids) { + cohesion (boids) { var neighbordist = 50; var sum = createVector(0, 0); // Start with empty vector to accumulate all locations var num = 0; @@ -188,12 +193,11 @@ function Boid(target) { } else { return createVector(0, 0); } - }; + } - Boid.prototype.arrive = function(target) { + arrive (target) { // A vector pointing from the location to the target - var desired = p5.Vector.sub(target, this.position), - d = desired.mag(); + var desired = p5.Vector.sub(target, this.position), d = desired.mag(); // Scale with arbitrary damping within 100 pixels desired.setMag(d < 100 ? map(d, 0, 100, 0, this.maxspeed) : this.maxspeed); @@ -202,19 +206,21 @@ function Boid(target) { var steer = p5.Vector.sub(desired, this.velocity); steer.limit(this.maxforce); // Limit to maximum steering force this.applyForce(steer); - }; + } + } function mouseOnScreen() { return mouseX && mouseX <= width && mouseY && mouseY <= height; } -function Flock() { - this.count = 0; - this.boids = []; - this.assemble = false; - - this.arrived = function() { +class Flock { + constructor() { + this.count = 0; + this.boids = []; + this.assemble = false; + } + arrived() { var i; if (arguments.length) { for (i = 0; i < this.boids.length; i++) @@ -225,14 +231,14 @@ function Flock() { if (!this.boids[i].arrived) return false; return true; } - }; + } - this.run = function() { + run() { this.assemble = this.count === flock.boids.length; if (!this.assemble && mouseOnScreen()) this.boids[this.count++].place(mouseX, mouseY); for (var i = 0; i < this.boids.length; i++) this.boids[i].run(this.boids); - }; + } } diff --git a/test/manual-test-examples/p5.Vector/NOC_2_5_fluidresistance/mover.js b/test/manual-test-examples/p5.Vector/NOC_2_5_fluidresistance/mover.js index c201b3e3fd..abe950e69d 100644 --- a/test/manual-test-examples/p5.Vector/NOC_2_5_fluidresistance/mover.js +++ b/test/manual-test-examples/p5.Vector/NOC_2_5_fluidresistance/mover.js @@ -2,40 +2,38 @@ // Daniel Shiffman // http://natureofcode.com -function Mover(m, x, y) { - this.mass = m; - this.position = createVector(x, y); - this.velocity = createVector(0, 0); - this.acceleration = createVector(0, 0); -} - -// Newton's 2nd law: F = M * A -// or A = F / M -Mover.prototype.applyForce = function(force) { - var f = p5.Vector.div(force, this.mass); - this.acceleration.add(f); -}; - -Mover.prototype.update = function() { - // Velocity changes according to acceleration - this.velocity.add(this.acceleration); - // position changes by velocity - this.position.add(this.velocity); - // We must clear acceleration each frame - this.acceleration.mult(0); -}; - -Mover.prototype.display = function() { - stroke(0); - strokeWeight(2); - fill(255, 127); - ellipse(this.position.x, this.position.y, this.mass * 16, this.mass * 16); -}; - -// Bounce off bottom of window -Mover.prototype.checkEdges = function() { - if (this.position.y > height) { - this.velocity.y *= -0.9; // A little dampening when hitting the bottom - this.position.y = height; +class Mover { + constructor(m, x, y) { + this.mass = m; + this.position = createVector(x, y); + this.velocity = createVector(0, 0); + this.acceleration = createVector(0, 0); } -}; + // Newton's 2nd law: F = M * A + // or A = F / M + applyForce(force) { + var f = p5.Vector.div(force, this.mass); + this.acceleration.add(f); + } + update() { + // Velocity changes according to acceleration + this.velocity.add(this.acceleration); + // position changes by velocity + this.position.add(this.velocity); + // We must clear acceleration each frame + this.acceleration.mult(0); + } + display() { + stroke(0); + strokeWeight(2); + fill(255, 127); + ellipse(this.position.x, this.position.y, this.mass * 16, this.mass * 16); + } + // Bounce off bottom of window + checkEdges() { + if (this.position.y > height) { + this.velocity.y *= -0.9; // A little dampening when hitting the bottom + this.position.y = height; + } + } +} diff --git a/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/boid.js b/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/boid.js index ed1f796dbe..b1c411ed0d 100644 --- a/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/boid.js +++ b/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/boid.js @@ -5,167 +5,165 @@ // Boid class // Methods for Separation, Cohesion, Alignment added -function Boid(x, y) { - this.acceleration = createVector(0, 0); - this.velocity = createVector(random(-1, 1), random(-1, 1)); - this.position = createVector(x, y); - this.r = 3.0; - this.maxspeed = 3; // Maximum speed - this.maxforce = 0.05; // Maximum steering force -} - -Boid.prototype.run = function(boids) { - this.flock(boids); - this.update(); - this.borders(); - this.render(); -}; - -Boid.prototype.applyForce = function(force) { - // We could add mass here if we want A = F / M - this.acceleration.add(force); -}; - -// We accumulate a new acceleration each time based on three rules -Boid.prototype.flock = function(boids) { - var sep = this.separate(boids); // Separation - var ali = this.align(boids); // Alignment - var coh = this.cohesion(boids); // Cohesion - // Arbitrarily weight these forces - sep.mult(1.5); - ali.mult(1.0); - coh.mult(1.0); - // Add the force vectors to acceleration - this.applyForce(sep); - this.applyForce(ali); - this.applyForce(coh); -}; - -// Method to update location -Boid.prototype.update = function() { - // Update velocity - this.velocity.add(this.acceleration); - // Limit speed - this.velocity.limit(this.maxspeed); - this.position.add(this.velocity); - // Reset accelertion to 0 each cycle - this.acceleration.mult(0); -}; - -// A method that calculates and applies a steering force towards a target -// STEER = DESIRED MINUS VELOCITY -Boid.prototype.seek = function(target) { - var desired = p5.Vector.sub(target, this.position); // A vector pointing from the location to the target - // Normalize desired and scale to maximum speed - desired.normalize(); - desired.mult(this.maxspeed); - // Steering = Desired minus Velocity - var steer = p5.Vector.sub(desired, this.velocity); - steer.limit(this.maxforce); // Limit to maximum steering force - return steer; -}; - -Boid.prototype.render = function() { - // Draw a triangle rotated in the direction of velocity - var theta = this.velocity.heading() + radians(90); - fill(127); - stroke(200); - push(); - translate(this.position.x, this.position.y); - rotate(theta); - beginShape(); - vertex(0, -this.r * 2); - vertex(-this.r, this.r * 2); - vertex(this.r, this.r * 2); - endShape(CLOSE); - pop(); -}; - -// Wraparound -Boid.prototype.borders = function() { - if (this.position.x < -this.r) this.position.x = width + this.r; - if (this.position.y < -this.r) this.position.y = height + this.r; - if (this.position.x > width + this.r) location.x = -this.r; - if (this.position.y > height + this.r) location.y = -this.r; -}; +class Boid { + constructor(x, y) { + this.acceleration = createVector(0, 0); + this.velocity = createVector(random(-1, 1), random(-1, 1)); + this.position = createVector(x, y); + this.r = 3.0; + this.maxspeed = 3; // Maximum speed + this.maxforce = 0.05; // Maximum steering force + } + run(boids) { + this.flock(boids); + this.update(); + this.borders(); + this.render(); + } + applyForce(force) { + // We could add mass here if we want A = F / M + this.acceleration.add(force); + } + // We accumulate a new acceleration each time based on three rules + flock(boids) { + var sep = this.separate(boids); // Separation + var ali = this.align(boids); // Alignment + var coh = this.cohesion(boids); // Cohesion -// Separation -// Method checks for nearby boids and steers away -Boid.prototype.separate = function(boids) { - var desiredseparation = 25.0; - var steer = createVector(0, 0); - var count = 0; - // For every boid in the system, check if it's too close - for (var i = 0; i < boids.length; i++) { - var d = p5.Vector.dist(this.position, boids[i].position); - // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself) - if (d > 0 && d < desiredseparation) { - // Calculate vector pointing away from neighbor - var diff = p5.Vector.sub(this.position, boids[i].position); - diff.normalize(); - diff.div(d); // Weight by distance - steer.add(diff); - count++; // Keep track of how many - } + // Arbitrarily weight these forces + sep.mult(1.5); + ali.mult(1.0); + coh.mult(1.0); + // Add the force vectors to acceleration + this.applyForce(sep); + this.applyForce(ali); + this.applyForce(coh); } - // Average -- divide by how many - if (count > 0) { - steer.div(count); + // Method to update location + update() { + // Update velocity + this.velocity.add(this.acceleration); + // Limit speed + this.velocity.limit(this.maxspeed); + this.position.add(this.velocity); + // Reset accelertion to 0 each cycle + this.acceleration.mult(0); } + // A method that calculates and applies a steering force towards a target + // STEER = DESIRED MINUS VELOCITY + seek(target) { + var desired = p5.Vector.sub(target, this.position); // A vector pointing from the location to the target - // As long as the vector is greater than 0 - if (steer.mag() > 0) { - // Implement Reynolds: Steering = Desired - Velocity - steer.normalize(); - steer.mult(this.maxspeed); - steer.sub(this.velocity); - steer.limit(this.maxforce); + // Normalize desired and scale to maximum speed + desired.normalize(); + desired.mult(this.maxspeed); + // Steering = Desired minus Velocity + var steer = p5.Vector.sub(desired, this.velocity); + steer.limit(this.maxforce); // Limit to maximum steering force + return steer; + } + render() { + // Draw a triangle rotated in the direction of velocity + var theta = this.velocity.heading() + radians(90); + fill(127); + stroke(200); + push(); + translate(this.position.x, this.position.y); + rotate(theta); + beginShape(); + vertex(0, -this.r * 2); + vertex(-this.r, this.r * 2); + vertex(this.r, this.r * 2); + endShape(CLOSE); + pop(); + } + // Wraparound + borders() { + if (this.position.x < -this.r) + this.position.x = width + this.r; + if (this.position.y < -this.r) + this.position.y = height + this.r; + if (this.position.x > width + this.r) + location.x = -this.r; + if (this.position.y > height + this.r) + location.y = -this.r; } - return steer; -}; + // Separation + // Method checks for nearby boids and steers away + separate(boids) { + var desiredseparation = 25.0; + var steer = createVector(0, 0); + var count = 0; + // For every boid in the system, check if it's too close + for (var i = 0; i < boids.length; i++) { + var d = p5.Vector.dist(this.position, boids[i].position); + // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself) + if (d > 0 && d < desiredseparation) { + // Calculate vector pointing away from neighbor + var diff = p5.Vector.sub(this.position, boids[i].position); + diff.normalize(); + diff.div(d); // Weight by distance + steer.add(diff); + count++; // Keep track of how many + } + } + // Average -- divide by how many + if (count > 0) { + steer.div(count); + } -// Alignment -// For every nearby boid in the system, calculate the average velocity -Boid.prototype.align = function(boids) { - var neighbordist = 50; - var sum = createVector(0, 0); - var count = 0; - for (var i = 0; i < boids.length; i++) { - var d = p5.Vector.dist(this.position, boids[i].position); - if (d > 0 && d < neighbordist) { - sum.add(boids[i].velocity); - count++; + // As long as the vector is greater than 0 + if (steer.mag() > 0) { + // Implement Reynolds: Steering = Desired - Velocity + steer.normalize(); + steer.mult(this.maxspeed); + steer.sub(this.velocity); + steer.limit(this.maxforce); } - } - if (count > 0) { - sum.div(count); - sum.normalize(); - sum.mult(this.maxspeed); - var steer = p5.Vector.sub(sum, this.velocity); - steer.limit(this.maxforce); return steer; - } else { - return createVector(0, 0); } -}; - -// Cohesion -// For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location -Boid.prototype.cohesion = function(boids) { - var neighbordist = 50; - var sum = createVector(0, 0); // Start with empty vector to accumulate all locations - var count = 0; - for (var i = 0; i < boids.length; i++) { - var d = p5.Vector.dist(this.position, boids[i].position); - if (d > 0 && d < neighbordist) { - sum.add(boids[i].position); // Add location - count++; + // Alignment + // For every nearby boid in the system, calculate the average velocity + align(boids) { + var neighbordist = 50; + var sum = createVector(0, 0); + var count = 0; + for (var i = 0; i < boids.length; i++) { + var d = p5.Vector.dist(this.position, boids[i].position); + if (d > 0 && d < neighbordist) { + sum.add(boids[i].velocity); + count++; + } + } + if (count > 0) { + sum.div(count); + sum.normalize(); + sum.mult(this.maxspeed); + var steer = p5.Vector.sub(sum, this.velocity); + steer.limit(this.maxforce); + return steer; + } else { + return createVector(0, 0); } } - if (count > 0) { - sum.div(count); - return this.seek(sum); // Steer towards the location - } else { - return createVector(0, 0); + // Cohesion + // For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location + cohesion(boids) { + var neighbordist = 50; + var sum = createVector(0, 0); // Start with empty vector to accumulate all locations + var count = 0; + for (var i = 0; i < boids.length; i++) { + var d = p5.Vector.dist(this.position, boids[i].position); + if (d > 0 && d < neighbordist) { + sum.add(boids[i].position); // Add location + count++; + } + } + if (count > 0) { + sum.div(count); + return this.seek(sum); // Steer towards the location + } else { + return createVector(0, 0); + } } -}; +} diff --git a/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/flock.js b/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/flock.js index 3cd00323de..1708e485c6 100644 --- a/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/flock.js +++ b/test/manual-test-examples/p5.Vector/NOC_6_09_Flocking/flock.js @@ -5,17 +5,17 @@ // Flock object // Does very little, simply manages the array of all the boids -function Flock() { - // An array for all the boids - this.boids = []; // Initialize the array -} - -Flock.prototype.run = function() { - for (var i = 0; i < this.boids.length; i++) { - this.boids[i].run(this.boids); // Passing the entire list of boids to each boid individually +class Flock { + constructor() { + // An array for all the boids + this.boids = []; // Initialize the array } -}; - -Flock.prototype.addBoid = function(b) { - this.boids.push(b); -}; + run() { + for (var i = 0; i < this.boids.length; i++) { + this.boids[i].run(this.boids); // Passing the entire list of boids to each boid individually + } + } + addBoid(b) { + this.boids.push(b); + } +} diff --git a/test/manual-test-examples/webgl/performance/points/sketch.js b/test/manual-test-examples/webgl/performance/points/sketch.js index f7fb3fc4f5..aa4de2b297 100644 --- a/test/manual-test-examples/webgl/performance/points/sketch.js +++ b/test/manual-test-examples/webgl/performance/points/sketch.js @@ -1,20 +1,21 @@ -function Particle(x, y) { - this.pos = createVector(x, y); - this.vel = p5.Vector.random2D(); - this.vel.setMag(random(2, 5)); - this.acc = createVector(); - - this.update = function() { +class Particle { + constructor(x, y) { + this.pos = createVector(x, y); + this.vel = p5.Vector.random2D(); + this.vel.setMag(random(2, 5)); + this.acc = createVector(); + } + update () { this.pos.add(this.vel); this.vel.add(this.acc); this.acc.mult(0); - }; + } - this.show = function() { + show () { vertex(this.pos.x, this.pos.y); - }; + } - this.attracted = function(target) { + attracted (target) { var force = p5.Vector.sub(target, this.pos); var dsquared = force.magSq(); dsquared = constrain(dsquared, 25, 500); @@ -22,7 +23,8 @@ function Particle(x, y) { var strength = G / dsquared; force.setMag(strength); this.acc.add(force); - }; + } + } var attractor_1; From ac0b5317efeb975236b4fab451b6be9491189f19 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Tue, 9 May 2023 02:15:04 +0900 Subject: [PATCH 08/22] fix lint --- src/webgl/p5.RendererGL.js | 305 +++++++++++++++++++------------------ 1 file changed, 153 insertions(+), 152 deletions(-) diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 720547f541..ddb36b6505 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -11,11 +11,11 @@ import { join } from 'path'; const STROKE_CAP_ENUM = {}; const STROKE_JOIN_ENUM = {}; let lineDefs = ''; -const defineStrokeCapEnum = function(key, val) { +const defineStrokeCapEnum = function (key, val) { lineDefs += `#define STROKE_CAP_${key} ${val}\n`; STROKE_CAP_ENUM[constants[key]] = val; }; -const defineStrokeJoinEnum = function(key, val) { +const defineStrokeJoinEnum = function (key, val) { lineDefs += `#define STROKE_JOIN_${key} ${val}\n`; STROKE_JOIN_ENUM[constants[key]] = val; }; @@ -86,8 +86,8 @@ const defaultShaders = { * @todo extend class to include public method for offscreen * rendering (FBO). */ -p5.RendererGL = class RendererGL extends p5.Renderer { - constructor(elt, pInst, isMainCanvas, attr){ +p5.RendererGL = class RendererGL extends p5.Renderer { + constructor(elt, pInst, isMainCanvas, attr) { super(elt, pInst, isMainCanvas); this._setAttributeDefaults(pInst); this._initContext(); @@ -140,11 +140,11 @@ p5.RendererGL = class RendererGL extends p5.Renderer { this._isBlending = false; - this._hasSetAmbient = false; - this._useSpecularMaterial = false; - this._useEmissiveMaterial = false; - this._useNormalMaterial = false; - this._useShininess = 1; + this._hasSetAmbient = false; + this._useSpecularMaterial = false; + this._useEmissiveMaterial = false; + this._useNormalMaterial = false; + this._useShininess = 1; this._useLineColor = false; this._useVertexColor = false; @@ -163,85 +163,85 @@ p5.RendererGL = class RendererGL extends p5.Renderer { * matrices */ - this.uMVMatrix = new p5.Matrix(); - this.uPMatrix = new p5.Matrix(); - this.uNMatrix = new p5.Matrix('mat3'); - - // Current vertex normal - this._currentNormal = new p5.Vector(0, 0, 1); - - // Camera - this._curCamera = new p5.Camera(this); - this._curCamera._computeCameraDefaultSettings(); - this._curCamera._setDefaultCamera(); - - // Information about the previous frame's touch object - // for executing orbitControl() - this.prevTouches = []; - - 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 p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), - new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), - new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) - ], - fill: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), - new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), - new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), - new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), - //new BufferDef(3, 'vertexSpeculars', 'specularBuffer', 'aSpecularColor'), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) - ], - text: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition',this, this._vToNArray), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) - ] - } - }; - - // Immediate Mode - // Geometry and Material hashes stored here - this.immediateMode = { - geometry: new p5.Geometry(), - shapeMode: constants.TRIANGLE_FAN, - _bezierVertex: [], - _quadraticVertex: [], - _curveVertex: [], - buffers: { - fill: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), - new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), - new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), - new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) - ], - stroke: [ - new p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), - new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), - new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) - ], - point: this.GL.createBuffer() - } - }; + this.uMVMatrix = new p5.Matrix(); + this.uPMatrix = new p5.Matrix(); + this.uNMatrix = new p5.Matrix('mat3'); + + // Current vertex normal + this._currentNormal = new p5.Vector(0, 0, 1); + + // Camera + this._curCamera = new p5.Camera(this); + this._curCamera._computeCameraDefaultSettings(); + this._curCamera._setDefaultCamera(); + + // Information about the previous frame's touch object + // for executing orbitControl() + this.prevTouches = []; + + 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 p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), + new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), + new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), + new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), + new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) + ], + fill: [ + new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), + new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), + new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), + new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), + //new BufferDef(3, 'vertexSpeculars', 'specularBuffer', 'aSpecularColor'), + new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) + ], + text: [ + new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), + new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) + ] + } + }; + + // Immediate Mode + // Geometry and Material hashes stored here + this.immediateMode = { + geometry: new p5.Geometry(), + shapeMode: constants.TRIANGLE_FAN, + _bezierVertex: [], + _quadraticVertex: [], + _curveVertex: [], + buffers: { + fill: [ + new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), + new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), + new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), + new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), + new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) + ], + stroke: [ + new p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), + new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), + new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), + new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), + new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) + ], + point: this.GL.createBuffer() + } + }; this.pointSize = 5.0; //default point size this.curStrokeWeight = 1; @@ -280,13 +280,14 @@ p5.RendererGL = class RendererGL extends p5.Renderer { this._curShader = undefined; return this; - }}; + } +}; ////////////////////////////////////////////// // Setting ////////////////////////////////////////////// -p5.RendererGL.prototype._setAttributeDefaults = function(pInst) { +p5.RendererGL.prototype._setAttributeDefaults = function (pInst) { // See issue #3850, safer to enable AA in Safari const applyAA = navigator.userAgent.toLowerCase().includes('safari'); const defaults = { @@ -307,7 +308,7 @@ p5.RendererGL.prototype._setAttributeDefaults = function(pInst) { return; }; -p5.RendererGL.prototype._initContext = function() { +p5.RendererGL.prototype._initContext = function () { if (this._pInst._glAttributes.version !== 1) { // Unless WebGL1 is explicitly asked for, try to create a WebGL2 context this.drawingContext = @@ -344,7 +345,7 @@ p5.RendererGL.prototype._initContext = function() { //This is helper function to reset the context anytime the attributes //are changed with setAttributes() -p5.RendererGL.prototype._resetContext = function(options, callback) { +p5.RendererGL.prototype._resetContext = function (options, callback) { const w = this.width; const h = this.height; const defaultId = this.canvas.id; @@ -551,11 +552,11 @@ p5.RendererGL.prototype._resetContext = function(options, callback) { * @param {Object} obj object with key-value pairs */ -p5.prototype.setAttributes = function(key, value) { +p5.prototype.setAttributes = function (key, value) { if (typeof this._glAttributes === 'undefined') { console.log( 'You are trying to use setAttributes on a p5.Graphics object ' + - 'that does not use a WEBGL renderer.' + 'that does not use a WEBGL renderer.' ); return; } @@ -587,7 +588,7 @@ p5.prototype.setAttributes = function(key, value) { if (this._renderer.retainedMode.geometry.hasOwnProperty(x)) { p5._friendlyError( 'Sorry, Could not set the attributes, you need to call setAttributes() ' + - 'before calling the other drawing methods in setup()' + 'before calling the other drawing methods in setup()' ); return; } @@ -607,7 +608,7 @@ p5.prototype.setAttributes = function(key, value) { * @class p5.RendererGL */ -p5.RendererGL.prototype._update = function() { +p5.RendererGL.prototype._update = function () { // reset model view and apply initial camera transform // (containing only look at info; no projection). this.uMVMatrix.set( @@ -661,7 +662,7 @@ p5.RendererGL.prototype._update = function() { /** * [background description] */ -p5.RendererGL.prototype.background = function(...args) { +p5.RendererGL.prototype.background = function (...args) { const _col = this._pInst.color(...args); const _r = _col.levels[0] / 255; const _g = _col.levels[1] / 255; @@ -705,7 +706,7 @@ p5.RendererGL.prototype.background = function(...args) { * @alt * black canvas with purple cube spinning */ -p5.RendererGL.prototype.fill = function(v1, v2, v3, a) { +p5.RendererGL.prototype.fill = function (v1, v2, v3, a) { //see material.js for more info on color blending in webgl const color = p5.prototype.color.apply(this._pInst, arguments); this.curFillColor = color._array; @@ -744,27 +745,27 @@ p5.RendererGL.prototype.fill = function(v1, v2, v3, a) { * @alt * black canvas with purple cube with pink outline spinning */ -p5.RendererGL.prototype.stroke = function(r, g, b, a) { +p5.RendererGL.prototype.stroke = function (r, g, b, a) { const color = p5.prototype.color.apply(this._pInst, arguments); this.curStrokeColor = color._array; }; -p5.RendererGL.prototype.strokeCap = function(cap) { +p5.RendererGL.prototype.strokeCap = function (cap) { this.curStrokeCap = cap; }; -p5.RendererGL.prototype.strokeJoin = function(join) { +p5.RendererGL.prototype.strokeJoin = function (join) { this.curStrokeJoin = join; }; -p5.RendererGL.prototype.filter = function(filterType) { +p5.RendererGL.prototype.filter = function (filterType) { // filter can be achieved using custom shaders. // https://github.com/aferriss/p5jsShaderExamples // https://itp-xstory.github.io/p5js-shaders/#/ p5._friendlyError('filter() does not work in WEBGL mode'); }; -p5.RendererGL.prototype.blendMode = function(mode) { +p5.RendererGL.prototype.blendMode = function (mode) { if ( mode === constants.DARKEST || mode === constants.LIGHTEST || @@ -791,7 +792,7 @@ p5.RendererGL.prototype.blendMode = function(mode) { } }; -p5.RendererGL.prototype.erase = function(opacityFill, opacityStroke) { +p5.RendererGL.prototype.erase = function (opacityFill, opacityStroke) { if (!this._isErasing) { this._applyBlendMode(constants.REMOVE); this._isErasing = true; @@ -804,7 +805,7 @@ p5.RendererGL.prototype.erase = function(opacityFill, opacityStroke) { } }; -p5.RendererGL.prototype.noErase = function() { +p5.RendererGL.prototype.noErase = function () { if (this._isErasing) { this._isErasing = false; this.curFillColor = this._cachedFillStyle.slice(); @@ -852,7 +853,7 @@ p5.RendererGL.prototype.noErase = function() { * black canvas with two purple rotating spheres with pink * outlines the sphere on top has much heavier outlines, */ -p5.RendererGL.prototype.strokeWeight = function(w) { +p5.RendererGL.prototype.strokeWeight = function (w) { if (this.curStrokeWeight !== w) { this.pointSize = w; this.curStrokeWeight = w; @@ -860,7 +861,7 @@ p5.RendererGL.prototype.strokeWeight = function(w) { }; // x,y are canvas-relative (pre-scaled by _pixelDensity) -p5.RendererGL.prototype._getPixel = function(x, y) { +p5.RendererGL.prototype._getPixel = function (x, y) { const gl = this.GL; return readPixelWebGL( gl, @@ -882,7 +883,7 @@ p5.RendererGL.prototype._getPixel = function(x, y) { * @method loadPixels */ -p5.RendererGL.prototype.loadPixels = function() { +p5.RendererGL.prototype.loadPixels = function () { const pixelsState = this._pixelsState; //@todo_FES @@ -913,7 +914,7 @@ p5.RendererGL.prototype.loadPixels = function() { ); }; -p5.RendererGL.prototype.updatePixels = function() { +p5.RendererGL.prototype.updatePixels = function () { const fbo = this._getTempFramebuffer(); fbo.pixels = this._pixelsState.pixels; fbo.updatePixels(); @@ -933,7 +934,7 @@ p5.RendererGL.prototype.updatePixels = function() { * of the renderer's canvas. It will be created if it does not yet exist, and * reused if it does. */ -p5.RendererGL.prototype._getTempFramebuffer = function() { +p5.RendererGL.prototype._getTempFramebuffer = function () { if (!this._tempFramebuffer) { this._tempFramebuffer = this._pInst.createFramebuffer({ format: constants.UNSIGNED_BYTE, @@ -1061,7 +1062,7 @@ export function readPixelWebGL( // HASH | for geometry ////////////////////////////////////////////// -p5.RendererGL.prototype.geometryInHash = function(gId) { +p5.RendererGL.prototype.geometryInHash = function (gId) { return this.retainedMode.geometry[gId] !== undefined; }; @@ -1071,7 +1072,7 @@ p5.RendererGL.prototype.geometryInHash = function(gId) { * @param {Number} w [description] * @param {Number} h [description] */ -p5.RendererGL.prototype.resize = function(w, h) { +p5.RendererGL.prototype.resize = function (w, h) { p5.Renderer.prototype.resize.call(this, w, h); this.GL.viewport( 0, @@ -1110,7 +1111,7 @@ p5.RendererGL.prototype.resize = function(w, h) { * @param {Number} b normalized blue val. * @param {Number} a normalized alpha val. */ -p5.RendererGL.prototype.clear = function(...args) { +p5.RendererGL.prototype.clear = function (...args) { const _r = args[0] || 0; const _g = args[1] || 0; const _b = args[2] || 0; @@ -1121,7 +1122,7 @@ p5.RendererGL.prototype.clear = function(...args) { this.GL.clear(this.GL.COLOR_BUFFER_BIT | this.GL.DEPTH_BUFFER_BIT); }; -p5.RendererGL.prototype.applyMatrix = function(a, b, c, d, e, f) { +p5.RendererGL.prototype.applyMatrix = function (a, b, c, d, e, f) { if (arguments.length === 16) { p5.Matrix.prototype.apply.apply(this.uMVMatrix, arguments); } else { @@ -1143,7 +1144,7 @@ p5.RendererGL.prototype.applyMatrix = function(a, b, c, d, e, f) { * @chainable * @todo implement handle for components or vector as args */ -p5.RendererGL.prototype.translate = function(x, y, z) { +p5.RendererGL.prototype.translate = function (x, y, z) { if (x instanceof p5.Vector) { z = x.z; y = x.y; @@ -1161,12 +1162,12 @@ p5.RendererGL.prototype.translate = function(x, y, z) { * @param {Number} [z] z-axis scalar * @chainable */ -p5.RendererGL.prototype.scale = function(x, y, z) { +p5.RendererGL.prototype.scale = function (x, y, z) { this.uMVMatrix.scale(x, y, z); return this; }; -p5.RendererGL.prototype.rotate = function(rad, axis) { +p5.RendererGL.prototype.rotate = function (rad, axis) { if (typeof axis === 'undefined') { return this.rotateZ(rad); } @@ -1174,22 +1175,22 @@ p5.RendererGL.prototype.rotate = function(rad, axis) { return this; }; -p5.RendererGL.prototype.rotateX = function(rad) { +p5.RendererGL.prototype.rotateX = function (rad) { this.rotate(rad, 1, 0, 0); return this; }; -p5.RendererGL.prototype.rotateY = function(rad) { +p5.RendererGL.prototype.rotateY = function (rad) { this.rotate(rad, 0, 1, 0); return this; }; -p5.RendererGL.prototype.rotateZ = function(rad) { +p5.RendererGL.prototype.rotateZ = function (rad) { this.rotate(rad, 0, 0, 1); return this; }; -p5.RendererGL.prototype.push = function() { +p5.RendererGL.prototype.push = function () { // get the base renderer style const style = p5.Renderer.prototype.push.apply(this); @@ -1257,7 +1258,7 @@ p5.RendererGL.prototype.push = function() { return style; }; -p5.RendererGL.prototype.resetMatrix = function() { +p5.RendererGL.prototype.resetMatrix = function () { this.uMVMatrix.set( this._curCamera.cameraMatrix.mat4[0], this._curCamera.cameraMatrix.mat4[1], @@ -1289,7 +1290,7 @@ p5.RendererGL.prototype.resetMatrix = function() { * and the shader must be valid in that context. */ -p5.RendererGL.prototype._getImmediateStrokeShader = function() { +p5.RendererGL.prototype._getImmediateStrokeShader = function () { // select the stroke shader to use const stroke = this.userStrokeShader; if (!stroke || !stroke.isStrokeShader()) { @@ -1305,7 +1306,7 @@ p5.RendererGL.prototype._getRetainedStrokeShader = * selects which fill shader should be used based on renderer state, * for use with begin/endShape and immediate vertex mode. */ -p5.RendererGL.prototype._getImmediateFillShader = function() { +p5.RendererGL.prototype._getImmediateFillShader = function () { const fill = this.userFillShader; if (this._useNormalMaterial) { if (!fill || !fill.isNormalShader()) { @@ -1330,7 +1331,7 @@ p5.RendererGL.prototype._getImmediateFillShader = function() { * selects which fill shader should be used based on renderer state * for retained mode. */ -p5.RendererGL.prototype._getRetainedFillShader = function() { +p5.RendererGL.prototype._getRetainedFillShader = function () { if (this._useNormalMaterial) { return this._getNormalShader(); } @@ -1350,7 +1351,7 @@ p5.RendererGL.prototype._getRetainedFillShader = function() { return fill; }; -p5.RendererGL.prototype._getImmediatePointShader = function() { +p5.RendererGL.prototype._getImmediatePointShader = function () { // select the point shader to use const point = this.userPointShader; if (!point || !point.isPointShader()) { @@ -1362,7 +1363,7 @@ p5.RendererGL.prototype._getImmediatePointShader = function() { p5.RendererGL.prototype._getRetainedLineShader = p5.RendererGL.prototype._getImmediateLineShader; -p5.RendererGL.prototype._getLightShader = function() { +p5.RendererGL.prototype._getLightShader = function () { if (!this._defaultLightShader) { if (this._pInst._glAttributes.perPixelLighting) { this._defaultLightShader = new p5.Shader( @@ -1382,7 +1383,7 @@ p5.RendererGL.prototype._getLightShader = function() { return this._defaultLightShader; }; -p5.RendererGL.prototype._getImmediateModeShader = function() { +p5.RendererGL.prototype._getImmediateModeShader = function () { if (!this._defaultImmediateModeShader) { this._defaultImmediateModeShader = new p5.Shader( this, @@ -1394,7 +1395,7 @@ p5.RendererGL.prototype._getImmediateModeShader = function() { return this._defaultImmediateModeShader; }; -p5.RendererGL.prototype._getNormalShader = function() { +p5.RendererGL.prototype._getNormalShader = function () { if (!this._defaultNormalShader) { this._defaultNormalShader = new p5.Shader( this, @@ -1406,7 +1407,7 @@ p5.RendererGL.prototype._getNormalShader = function() { return this._defaultNormalShader; }; -p5.RendererGL.prototype._getColorShader = function() { +p5.RendererGL.prototype._getColorShader = function () { if (!this._defaultColorShader) { this._defaultColorShader = new p5.Shader( this, @@ -1418,7 +1419,7 @@ p5.RendererGL.prototype._getColorShader = function() { return this._defaultColorShader; }; -p5.RendererGL.prototype._getPointShader = function() { +p5.RendererGL.prototype._getPointShader = function () { if (!this._defaultPointShader) { this._defaultPointShader = new p5.Shader( this, @@ -1429,7 +1430,7 @@ p5.RendererGL.prototype._getPointShader = function() { return this._defaultPointShader; }; -p5.RendererGL.prototype._getLineShader = function() { +p5.RendererGL.prototype._getLineShader = function () { if (!this._defaultLineShader) { this._defaultLineShader = new p5.Shader( this, @@ -1441,7 +1442,7 @@ p5.RendererGL.prototype._getLineShader = function() { return this._defaultLineShader; }; -p5.RendererGL.prototype._getFontShader = function() { +p5.RendererGL.prototype._getFontShader = function () { if (!this._defaultFontShader) { if (this.webglVersion === constants.WEBGL) { this.GL.getExtension('OES_standard_derivatives'); @@ -1449,15 +1450,15 @@ p5.RendererGL.prototype._getFontShader = function() { this._defaultFontShader = new p5.Shader( this, this._webGL2CompatibilityPrefix('vert', 'mediump') + - defaultShaders.fontVert, + defaultShaders.fontVert, this._webGL2CompatibilityPrefix('frag', 'mediump') + - defaultShaders.fontFrag + defaultShaders.fontFrag ); } return this._defaultFontShader; }; -p5.RendererGL.prototype._webGL2CompatibilityPrefix = function( +p5.RendererGL.prototype._webGL2CompatibilityPrefix = function ( shaderType, floatPrecision ) { @@ -1476,7 +1477,7 @@ p5.RendererGL.prototype._webGL2CompatibilityPrefix = function( return code; }; -p5.RendererGL.prototype._getEmptyTexture = function() { +p5.RendererGL.prototype._getEmptyTexture = function () { if (!this._emptyTexture) { // a plain white texture RGBA, full alpha, single pixel. const im = new p5.Image(1, 1); @@ -1486,7 +1487,7 @@ p5.RendererGL.prototype._getEmptyTexture = function() { return this._emptyTexture; }; -p5.RendererGL.prototype.getTexture = function(input) { +p5.RendererGL.prototype.getTexture = function (input) { let src = input; if (src instanceof p5.Framebuffer) { src = src.color; @@ -1502,11 +1503,11 @@ p5.RendererGL.prototype.getTexture = function(input) { return tex; }; -p5.RendererGL.prototype.createFramebuffer = function(options) { +p5.RendererGL.prototype.createFramebuffer = function (options) { return new p5.Framebuffer(this, options); }; -p5.RendererGL.prototype._setStrokeUniforms = function(strokeShader) { +p5.RendererGL.prototype._setStrokeUniforms = function (strokeShader) { strokeShader.bindShader(); // set the uniform values @@ -1517,7 +1518,7 @@ p5.RendererGL.prototype._setStrokeUniforms = function(strokeShader) { strokeShader.setUniform('uStrokeJoin', STROKE_JOIN_ENUM[this.curStrokeJoin]); }; -p5.RendererGL.prototype._setFillUniforms = function(fillShader) { +p5.RendererGL.prototype._setFillUniforms = function (fillShader) { fillShader.bindShader(); // TODO: optimize @@ -1587,7 +1588,7 @@ p5.RendererGL.prototype._setFillUniforms = function(fillShader) { fillShader.bindTextures(); }; -p5.RendererGL.prototype._setPointUniforms = function(pointShader) { +p5.RendererGL.prototype._setPointUniforms = function (pointShader) { pointShader.bindShader(); // set the uniform values @@ -1604,7 +1605,7 @@ p5.RendererGL.prototype._setPointUniforms = function(pointShader) { * when passed more than two arguments it also updates or initializes * the data associated with the buffer */ -p5.RendererGL.prototype._bindBuffer = function( +p5.RendererGL.prototype._bindBuffer = function ( buffer, target, values, @@ -1622,7 +1623,7 @@ p5.RendererGL.prototype._bindBuffer = function( /////////////////////////////// //// UTILITY FUNCTIONS ////////////////////////////// -p5.RendererGL.prototype._arraysEqual = function(a, b) { +p5.RendererGL.prototype._arraysEqual = function (a, b) { const aLength = a.length; if (aLength !== b.length) return false; for (let i = 0; i < aLength; i++) { @@ -1631,7 +1632,7 @@ p5.RendererGL.prototype._arraysEqual = function(a, b) { return true; }; -p5.RendererGL.prototype._isTypedArray = function(arr) { +p5.RendererGL.prototype._isTypedArray = function (arr) { let res = false; res = arr instanceof Float32Array; res = arr instanceof Float64Array; @@ -1647,7 +1648,7 @@ p5.RendererGL.prototype._isTypedArray = function(arr) { * @return {Array} 1-dimensional array * [[1, 2, 3],[4, 5, 6]] -> [1, 2, 3, 4, 5, 6] */ -p5.RendererGL.prototype._flatten = function(arr) { +p5.RendererGL.prototype._flatten = function (arr) { //when empty, return empty if (arr.length === 0) { return []; @@ -1685,7 +1686,7 @@ p5.RendererGL.prototype._flatten = function(arr) { * [p5.Vector(1, 2, 3), p5.Vector(4, 5, 6)] -> * [1, 2, 3, 4, 5, 6] */ -p5.RendererGL.prototype._vToNArray = function(arr) { +p5.RendererGL.prototype._vToNArray = function (arr) { const ret = []; for (const item of arr) { @@ -1698,7 +1699,7 @@ p5.RendererGL.prototype._vToNArray = function(arr) { /** * ensures that p5 is using a 3d renderer. throws an error if not. */ -p5.prototype._assert3d = function(name) { +p5.prototype._assert3d = function (name) { if (!this._renderer.isP3D) throw new Error( `${name}() is only supported in WEBGL mode. If you'd like to use 3D graphics and WebGL, see https://p5js.org/examples/form-3d-primitives.html for more information.` @@ -1752,7 +1753,7 @@ p5.RendererGL.prototype._initTessy = function initTesselator() { return tessy; }; -p5.RendererGL.prototype._triangulate = function(contours) { +p5.RendererGL.prototype._triangulate = function (contours) { // libtess will take 3d verts and flatten to a plane for tesselation. // libtess is capable of calculating a plane to tesselate on, but // if all of the vertices have the same z values, we'll just @@ -1806,7 +1807,7 @@ p5.RendererGL.prototype._triangulate = function(contours) { }; // function to calculate BezierVertex Coefficients -p5.RendererGL.prototype._bezierCoefficients = function(t) { +p5.RendererGL.prototype._bezierCoefficients = function (t) { const t2 = t * t; const t3 = t2 * t; const mt = 1 - t; @@ -1816,7 +1817,7 @@ p5.RendererGL.prototype._bezierCoefficients = function(t) { }; // function to calculate QuadraticVertex Coefficients -p5.RendererGL.prototype._quadraticCoefficients = function(t) { +p5.RendererGL.prototype._quadraticCoefficients = function (t) { const t2 = t * t; const mt = 1 - t; const mt2 = mt * mt; @@ -1824,7 +1825,7 @@ p5.RendererGL.prototype._quadraticCoefficients = function(t) { }; // function to convert Bezier coordinates to Catmull Rom Splines -p5.RendererGL.prototype._bezierToCatmull = function(w) { +p5.RendererGL.prototype._bezierToCatmull = function (w) { const p1 = w[1]; const p2 = w[1] + (w[2] - w[0]) / this._curveTightness; const p3 = w[2] - (w[3] - w[1]) / this._curveTightness; From 51a2ff91abf396bf8bba4fef9d4250c785fecd02 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Sun, 21 May 2023 20:26:04 +0900 Subject: [PATCH 09/22] fix ci --- src/webgl/p5.RendererGL.js | 61 +++----------------------------------- 1 file changed, 4 insertions(+), 57 deletions(-) diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 74d05af3c5..0870d79d4e 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -162,63 +162,6 @@ p5.RendererGL = class RendererGL extends p5.Renderer { * model view, projection, & normal * matrices */ - this.uMVMatrix = new p5.Matrix(); - this.uPMatrix = new p5.Matrix(); - this.uNMatrix = new p5.Matrix('mat3'); - - // Current vertex normal - this._currentNormal = new p5.Vector(0, 0, 1); - - // Camera - this._curCamera = new p5.Camera(this); - this._curCamera._computeCameraDefaultSettings(); - this._curCamera._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 p5.Vector(0, 0); - this.moveVelocity = new p5.Vector(0, 0); - - 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 p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), - new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), - new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) - ], - fill: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), - new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), - new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), - new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), - //new BufferDef(3, 'vertexSpeculars', 'specularBuffer', 'aSpecularColor'), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) - ], - text: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition',this, this._vToNArray), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) - ] - } - }; - this.uMVMatrix = new p5.Matrix(); this.uPMatrix = new p5.Matrix(); this.uNMatrix = new p5.Matrix('mat3'); @@ -234,6 +177,10 @@ p5.RendererGL = class RendererGL extends p5.Renderer { // 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 p5.Vector(0, 0); + this.moveVelocity = new p5.Vector(0, 0); this._defaultLightShader = undefined; this._defaultImmediateModeShader = undefined; From 07fa7e68724aa8c4e1a7445da054178381eaeb7d Mon Sep 17 00:00:00 2001 From: asukaminato Date: Sun, 21 May 2023 20:47:04 +0900 Subject: [PATCH 10/22] revert some --- src/core/p5.Renderer.js | 108 +++++------ src/core/p5.Renderer2D.js | 116 ++++++------ src/webgl/p5.RendererGL.js | 360 ++++++++++++++++++------------------- 3 files changed, 292 insertions(+), 292 deletions(-) diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index 26113ce059..8372b1f62d 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -19,51 +19,51 @@ import * as constants from '../core/constants'; * @param {p5} [pInst] pointer to p5 instance * @param {Boolean} [isMainCanvas] whether we're using it as main canvas */ -p5.Renderer = class Renderer extends p5.Element { - constructor(elt, pInst, isMainCanvas){ - super(elt, pInst); - this.canvas = elt; - this._pixelsState = pInst; - if (isMainCanvas) { - this._isMainCanvas = true; - // for pixel method sharing with pimage - this._pInst._setProperty('_curElement', this); - this._pInst._setProperty('canvas', this.canvas); - this._pInst._setProperty('width', this.width); - this._pInst._setProperty('height', this.height); - } else { +p5.Renderer = function (elt, pInst, isMainCanvas) { + p5.Element.call(this, elt, pInst); + this.canvas = elt; + this._pixelsState = pInst; + if (isMainCanvas) { + this._isMainCanvas = true; + // for pixel method sharing with pimage + this._pInst._setProperty('_curElement', this); + this._pInst._setProperty('canvas', this.canvas); + this._pInst._setProperty('width', this.width); + this._pInst._setProperty('height', this.height); + } else { // hide if offscreen buffer by default - this.canvas.style.display = 'none'; - this._styles = []; // non-main elt styles stored in p5.Renderer - } - - this._textSize = 12; - this._textLeading = 15; - this._textFont = 'sans-serif'; - this._textStyle = constants.NORMAL; - this._textAscent = null; - this._textDescent = null; - this._textAlign = constants.LEFT; - this._textBaseline = constants.BASELINE; - this._textWrap = constants.WORD; - - this._rectMode = constants.CORNER; - this._ellipseMode = constants.CENTER; - this._curveTightness = 0; - this._imageMode = constants.CORNER; - - this._tint = null; - this._doStroke = true; - this._doFill = true; - this._strokeSet = false; - this._fillSet = false; - this._leadingSet = false; + this.canvas.style.display = 'none'; + this._styles = []; // non-main elt styles stored in p5.Renderer } -}; + + this._textSize = 12; + this._textLeading = 15; + this._textFont = 'sans-serif'; + this._textStyle = constants.NORMAL; + this._textAscent = null; + this._textDescent = null; + this._textAlign = constants.LEFT; + this._textBaseline = constants.BASELINE; + this._textWrap = constants.WORD; + + this._rectMode = constants.CORNER; + this._ellipseMode = constants.CENTER; + this._curveTightness = 0; + this._imageMode = constants.CORNER; + + this._tint = null; + this._doStroke = true; + this._doFill = true; + this._strokeSet = false; + this._fillSet = false; + this._leadingSet = false; +} + +p5.Renderer.prototype = Object.create(p5.Element.prototype); // the renderer should return a 'style' object that it wishes to // store on the push stack. -p5.Renderer.prototype.push = function() { +p5.Renderer.prototype.push = function () { return { properties: { _doStroke: this._doStroke, @@ -89,7 +89,7 @@ p5.Renderer.prototype.push = function() { // a pop() operation is in progress // the renderer is passed the 'style' object that it returned // from its push() method. -p5.Renderer.prototype.pop = function(style) { +p5.Renderer.prototype.pop = function (style) { if (style.properties) { // copy the style properties back into the renderer Object.assign(this, style.properties); @@ -99,7 +99,7 @@ p5.Renderer.prototype.pop = function(style) { /** * Resize our canvas element. */ -p5.Renderer.prototype.resize = function(w, h) { +p5.Renderer.prototype.resize = function (w, h) { this.width = w; this.height = h; this.elt.width = w * this._pInst._pixelDensity; @@ -112,7 +112,7 @@ p5.Renderer.prototype.resize = function(w, h) { } }; -p5.Renderer.prototype.get = function(x, y, w, h) { +p5.Renderer.prototype.get = function (x, y, w, h) { const pixelsState = this._pixelsState; const pd = pixelsState._pixelDensity; const canvas = this.canvas; @@ -145,7 +145,7 @@ p5.Renderer.prototype.get = function(x, y, w, h) { return region; }; -p5.Renderer.prototype.textLeading = function(l) { +p5.Renderer.prototype.textLeading = function (l) { if (typeof l === 'number') { this._setProperty('_leadingSet', true); this._setProperty('_textLeading', l); @@ -155,7 +155,7 @@ p5.Renderer.prototype.textLeading = function(l) { return this._textLeading; }; -p5.Renderer.prototype.textSize = function(s) { +p5.Renderer.prototype.textSize = function (s) { if (typeof s === 'number') { this._setProperty('_textSize', s); if (!this._leadingSet) { @@ -168,7 +168,7 @@ p5.Renderer.prototype.textSize = function(s) { return this._textSize; }; -p5.Renderer.prototype.textStyle = function(s) { +p5.Renderer.prototype.textStyle = function (s) { if (s) { if ( s === constants.NORMAL || @@ -185,21 +185,21 @@ p5.Renderer.prototype.textStyle = function(s) { return this._textStyle; }; -p5.Renderer.prototype.textAscent = function() { +p5.Renderer.prototype.textAscent = function () { if (this._textAscent === null) { this._updateTextMetrics(); } return this._textAscent; }; -p5.Renderer.prototype.textDescent = function() { +p5.Renderer.prototype.textDescent = function () { if (this._textDescent === null) { this._updateTextMetrics(); } return this._textDescent; }; -p5.Renderer.prototype.textAlign = function(h, v) { +p5.Renderer.prototype.textAlign = function (h, v) { if (typeof h !== 'undefined') { this._setProperty('_textAlign', h); @@ -216,12 +216,12 @@ p5.Renderer.prototype.textAlign = function(h, v) { } }; -p5.Renderer.prototype.textWrap = function(wrapStyle) { +p5.Renderer.prototype.textWrap = function (wrapStyle) { this._setProperty('_textWrap', wrapStyle); return this._textWrap; }; -p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { +p5.Renderer.prototype.text = function (str, x, y, maxWidth, maxHeight) { const p = this._pInst; const textWrapStyle = this._textWrap; @@ -452,18 +452,18 @@ p5.Renderer.prototype.text = function(str, x, y, maxWidth, maxHeight) { return p; }; -p5.Renderer.prototype._applyDefaults = function() { +p5.Renderer.prototype._applyDefaults = function () { return this; }; /** * Helper function to check font type (system or otf) */ -p5.Renderer.prototype._isOpenType = function(f = this._textFont) { +p5.Renderer.prototype._isOpenType = function (f = this._textFont) { return typeof f === 'object' && f.font && f.font.supported; }; -p5.Renderer.prototype._updateTextMetrics = function() { +p5.Renderer.prototype._updateTextMetrics = function () { if (this._isOpenType()) { this._setProperty('_textAscent', this._textFont._textAscent()); this._setProperty('_textDescent', this._textFont._textDescent()); diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index 8af09c891c..bc69814c74 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -11,16 +11,16 @@ import './p5.Renderer'; const styleEmpty = 'rgba(0,0,0,0)'; // const alphaThreshold = 0.00125; // minimum visible -p5.Renderer2D = class Renderer2D extends p5.Renderer { - constructor(elt, pInst, isMainCanvas) { - super(elt, pInst, isMainCanvas); - this.drawingContext = this.canvas.getContext('2d'); - this._pInst._setProperty('drawingContext', this.drawingContext); - return this; - } -}; +p5.Renderer2D = function (elt, pInst, isMainCanvas) { + p5.Renderer.call(this, elt, pInst, isMainCanvas); + this.drawingContext = this.canvas.getContext('2d'); + this._pInst._setProperty('drawingContext', this.drawingContext); + return this; +} + +p5.Renderer2D.prototype = Object.create(p5.Renderer.prototype); -p5.Renderer2D.prototype._applyDefaults = function() { +p5.Renderer2D.prototype._applyDefaults = function () { this._cachedFillStyle = this._cachedStrokeStyle = undefined; this._cachedBlendMode = constants.BLEND; this._setFill(constants._DEFAULT_FILL); @@ -29,7 +29,7 @@ p5.Renderer2D.prototype._applyDefaults = function() { this.drawingContext.font = 'normal 12px sans-serif'; }; -p5.Renderer2D.prototype.resize = function(w, h) { +p5.Renderer2D.prototype.resize = function (w, h) { p5.Renderer.prototype.resize.call(this, w, h); this.drawingContext.scale( this._pInst._pixelDensity, @@ -41,7 +41,7 @@ p5.Renderer2D.prototype.resize = function(w, h) { // COLOR | Setting ////////////////////////////////////////////// -p5.Renderer2D.prototype.background = function(...args) { +p5.Renderer2D.prototype.background = function (...args) { this.drawingContext.save(); this.resetMatrix(); @@ -82,14 +82,14 @@ p5.Renderer2D.prototype.background = function(...args) { this.drawingContext.restore(); }; -p5.Renderer2D.prototype.clear = function() { +p5.Renderer2D.prototype.clear = function () { this.drawingContext.save(); this.resetMatrix(); this.drawingContext.clearRect(0, 0, this.width, this.height); this.drawingContext.restore(); }; -p5.Renderer2D.prototype.fill = function(...args) { +p5.Renderer2D.prototype.fill = function (...args) { const color = this._pInst.color(...args); this._setFill(color.toString()); @@ -99,7 +99,7 @@ p5.Renderer2D.prototype.fill = function(...args) { } }; -p5.Renderer2D.prototype.stroke = function(...args) { +p5.Renderer2D.prototype.stroke = function (...args) { const color = this._pInst.color(...args); this._setStroke(color.toString()); @@ -109,7 +109,7 @@ p5.Renderer2D.prototype.stroke = function(...args) { } }; -p5.Renderer2D.prototype.erase = function(opacityFill, opacityStroke) { +p5.Renderer2D.prototype.erase = function (opacityFill, opacityStroke) { if (!this._isErasing) { // cache the fill style this._cachedFillStyle = this.drawingContext.fillStyle; @@ -130,7 +130,7 @@ p5.Renderer2D.prototype.erase = function(opacityFill, opacityStroke) { } }; -p5.Renderer2D.prototype.noErase = function() { +p5.Renderer2D.prototype.noErase = function () { if (this._isErasing) { this.drawingContext.fillStyle = this._cachedFillStyle; this.drawingContext.strokeStyle = this._cachedStrokeStyle; @@ -144,7 +144,7 @@ p5.Renderer2D.prototype.noErase = function() { // IMAGE | Loading & Displaying ////////////////////////////////////////////// -p5.Renderer2D.prototype.image = function( +p5.Renderer2D.prototype.image = function ( img, sx, sy, @@ -198,7 +198,7 @@ p5.Renderer2D.prototype.image = function( } }; -p5.Renderer2D.prototype._getTintedImageCanvas = function(img) { +p5.Renderer2D.prototype._getTintedImageCanvas = function (img) { if (!img.canvas) { return img; } @@ -268,7 +268,7 @@ p5.Renderer2D.prototype._getTintedImageCanvas = function(img) { // IMAGE | Pixels ////////////////////////////////////////////// -p5.Renderer2D.prototype.blendMode = function(mode) { +p5.Renderer2D.prototype.blendMode = function (mode) { if (mode === constants.SUBTRACT) { console.warn('blendMode(SUBTRACT) only works in WEBGL mode.'); } else if ( @@ -295,7 +295,7 @@ p5.Renderer2D.prototype.blendMode = function(mode) { } }; -p5.Renderer2D.prototype.blend = function(...args) { +p5.Renderer2D.prototype.blend = function (...args) { const currBlend = this.drawingContext.globalCompositeOperation; const blendMode = args[args.length - 1]; @@ -312,7 +312,7 @@ p5.Renderer2D.prototype.blend = function(...args) { // .get() is not overridden // x,y are canvas-relative (pre-scaled by _pixelDensity) -p5.Renderer2D.prototype._getPixel = function(x, y) { +p5.Renderer2D.prototype._getPixel = function (x, y) { let imageData, index; imageData = this.drawingContext.getImageData(x, y, 1, 1).data; index = 0; @@ -324,7 +324,7 @@ p5.Renderer2D.prototype._getPixel = function(x, y) { ]; }; -p5.Renderer2D.prototype.loadPixels = function() { +p5.Renderer2D.prototype.loadPixels = function () { const pixelsState = this._pixelsState; // if called by p5.Image const pd = pixelsState._pixelDensity; @@ -337,7 +337,7 @@ p5.Renderer2D.prototype.loadPixels = function() { pixelsState._setProperty('pixels', imageData.data); }; -p5.Renderer2D.prototype.set = function(x, y, imgOrCol) { +p5.Renderer2D.prototype.set = function (x, y, imgOrCol) { // round down to get integer numbers x = Math.floor(x); y = Math.floor(y); @@ -413,7 +413,7 @@ p5.Renderer2D.prototype.set = function(x, y, imgOrCol) { } }; -p5.Renderer2D.prototype.updatePixels = function(x, y, w, h) { +p5.Renderer2D.prototype.updatePixels = function (x, y, w, h) { const pixelsState = this._pixelsState; const pd = pixelsState._pixelDensity; if ( @@ -487,7 +487,7 @@ p5.Renderer2D.prototype._acuteArcToBezier = function _acuteArcToBezier( * * start <= stop < start + TWO_PI */ -p5.Renderer2D.prototype.arc = function(x, y, w, h, start, stop, mode) { +p5.Renderer2D.prototype.arc = function (x, y, w, h, start, stop, mode) { const ctx = this.drawingContext; const rx = w / 2.0; const ry = h / 2.0; @@ -514,8 +514,8 @@ p5.Renderer2D.prototype.arc = function(x, y, w, h, start, stop, mode) { } /* eslint-disable indent */ ctx.bezierCurveTo(x + curve.bx * rx, y + curve.by * ry, - x + curve.cx * rx, y + curve.cy * ry, - x + curve.dx * rx, y + curve.dy * ry); + x + curve.cx * rx, y + curve.cy * ry, + x + curve.dx * rx, y + curve.dy * ry); /* eslint-enable indent */ }); if (mode === constants.PIE || mode == null) { @@ -534,8 +534,8 @@ p5.Renderer2D.prototype.arc = function(x, y, w, h, start, stop, mode) { } /* eslint-disable indent */ ctx.bezierCurveTo(x + curve.bx * rx, y + curve.by * ry, - x + curve.cx * rx, y + curve.cy * ry, - x + curve.dx * rx, y + curve.dy * ry); + x + curve.cx * rx, y + curve.cy * ry, + x + curve.dx * rx, y + curve.dy * ry); /* eslint-enable indent */ }); if (mode === constants.PIE) { @@ -549,7 +549,7 @@ p5.Renderer2D.prototype.arc = function(x, y, w, h, start, stop, mode) { return this; }; -p5.Renderer2D.prototype.ellipse = function(args) { +p5.Renderer2D.prototype.ellipse = function (args) { const ctx = this.drawingContext; const doFill = this._doFill, doStroke = this._doStroke; @@ -592,7 +592,7 @@ p5.Renderer2D.prototype.ellipse = function(args) { } }; -p5.Renderer2D.prototype.line = function(x1, y1, x2, y2) { +p5.Renderer2D.prototype.line = function (x1, y1, x2, y2) { const ctx = this.drawingContext; if (!this._doStroke) { return this; @@ -606,7 +606,7 @@ p5.Renderer2D.prototype.line = function(x1, y1, x2, y2) { return this; }; -p5.Renderer2D.prototype.point = function(x, y) { +p5.Renderer2D.prototype.point = function (x, y) { const ctx = this.drawingContext; if (!this._doStroke) { return this; @@ -623,7 +623,7 @@ p5.Renderer2D.prototype.point = function(x, y) { this._setFill(f); }; -p5.Renderer2D.prototype.quad = function(x1, y1, x2, y2, x3, y3, x4, y4) { +p5.Renderer2D.prototype.quad = function (x1, y1, x2, y2, x3, y3, x4, y4) { const ctx = this.drawingContext; const doFill = this._doFill, doStroke = this._doStroke; @@ -651,7 +651,7 @@ p5.Renderer2D.prototype.quad = function(x1, y1, x2, y2, x3, y3, x4, y4) { return this; }; -p5.Renderer2D.prototype.rect = function(args) { +p5.Renderer2D.prototype.rect = function (args) { const x = args[0]; const y = args[1]; const w = args[2]; @@ -740,7 +740,7 @@ p5.Renderer2D.prototype.rect = function(args) { return this; }; -p5.Renderer2D.prototype.triangle = function(args) { +p5.Renderer2D.prototype.triangle = function (args) { const ctx = this.drawingContext; const doFill = this._doFill, doStroke = this._doStroke; @@ -772,7 +772,7 @@ p5.Renderer2D.prototype.triangle = function(args) { } }; -p5.Renderer2D.prototype.endShape = function( +p5.Renderer2D.prototype.endShape = function ( mode, vertices, isCurve, @@ -809,7 +809,7 @@ p5.Renderer2D.prototype.endShape = function( ]; b[2] = [ vertices[i + 1][0] + - (s * vertices[i][0] - s * vertices[i + 2][0]) / 6, + (s * vertices[i][0] - s * vertices[i + 2][0]) / 6, vertices[i + 1][1] + (s * vertices[i][1] - s * vertices[i + 2][1]) / 6 ]; b[3] = [vertices[i + 1][0], vertices[i + 1][1]]; @@ -1034,7 +1034,7 @@ p5.Renderer2D.prototype.endShape = function( // SHAPE | Attributes ////////////////////////////////////////////// -p5.Renderer2D.prototype.strokeCap = function(cap) { +p5.Renderer2D.prototype.strokeCap = function (cap) { if ( cap === constants.ROUND || cap === constants.SQUARE || @@ -1045,7 +1045,7 @@ p5.Renderer2D.prototype.strokeCap = function(cap) { return this; }; -p5.Renderer2D.prototype.strokeJoin = function(join) { +p5.Renderer2D.prototype.strokeJoin = function (join) { if ( join === constants.ROUND || join === constants.BEVEL || @@ -1056,7 +1056,7 @@ p5.Renderer2D.prototype.strokeJoin = function(join) { return this; }; -p5.Renderer2D.prototype.strokeWeight = function(w) { +p5.Renderer2D.prototype.strokeWeight = function (w) { if (typeof w === 'undefined' || w === 0) { // hack because lineWidth 0 doesn't work this.drawingContext.lineWidth = 0.0001; @@ -1066,28 +1066,28 @@ p5.Renderer2D.prototype.strokeWeight = function(w) { return this; }; -p5.Renderer2D.prototype._getFill = function() { +p5.Renderer2D.prototype._getFill = function () { if (!this._cachedFillStyle) { this._cachedFillStyle = this.drawingContext.fillStyle; } return this._cachedFillStyle; }; -p5.Renderer2D.prototype._setFill = function(fillStyle) { +p5.Renderer2D.prototype._setFill = function (fillStyle) { if (fillStyle !== this._cachedFillStyle) { this.drawingContext.fillStyle = fillStyle; this._cachedFillStyle = fillStyle; } }; -p5.Renderer2D.prototype._getStroke = function() { +p5.Renderer2D.prototype._getStroke = function () { if (!this._cachedStrokeStyle) { this._cachedStrokeStyle = this.drawingContext.strokeStyle; } return this._cachedStrokeStyle; }; -p5.Renderer2D.prototype._setStroke = function(strokeStyle) { +p5.Renderer2D.prototype._setStroke = function (strokeStyle) { if (strokeStyle !== this._cachedStrokeStyle) { this.drawingContext.strokeStyle = strokeStyle; this._cachedStrokeStyle = strokeStyle; @@ -1097,7 +1097,7 @@ p5.Renderer2D.prototype._setStroke = function(strokeStyle) { ////////////////////////////////////////////// // SHAPE | Curves ////////////////////////////////////////////// -p5.Renderer2D.prototype.bezier = function(x1, y1, x2, y2, x3, y3, x4, y4) { +p5.Renderer2D.prototype.bezier = function (x1, y1, x2, y2, x3, y3, x4, y4) { this._pInst.beginShape(); this._pInst.vertex(x1, y1); this._pInst.bezierVertex(x2, y2, x3, y3, x4, y4); @@ -1105,7 +1105,7 @@ p5.Renderer2D.prototype.bezier = function(x1, y1, x2, y2, x3, y3, x4, y4) { return this; }; -p5.Renderer2D.prototype.curve = function(x1, y1, x2, y2, x3, y3, x4, y4) { +p5.Renderer2D.prototype.curve = function (x1, y1, x2, y2, x3, y3, x4, y4) { this._pInst.beginShape(); this._pInst.curveVertex(x1, y1); this._pInst.curveVertex(x2, y2); @@ -1119,7 +1119,7 @@ p5.Renderer2D.prototype.curve = function(x1, y1, x2, y2, x3, y3, x4, y4) { // SHAPE | Vertex ////////////////////////////////////////////// -p5.Renderer2D.prototype._doFillStrokeClose = function(closeShape) { +p5.Renderer2D.prototype._doFillStrokeClose = function (closeShape) { if (closeShape) { this.drawingContext.closePath(); } @@ -1135,11 +1135,11 @@ p5.Renderer2D.prototype._doFillStrokeClose = function(closeShape) { // TRANSFORM ////////////////////////////////////////////// -p5.Renderer2D.prototype.applyMatrix = function(a, b, c, d, e, f) { +p5.Renderer2D.prototype.applyMatrix = function (a, b, c, d, e, f) { this.drawingContext.transform(a, b, c, d, e, f); }; -p5.Renderer2D.prototype.resetMatrix = function() { +p5.Renderer2D.prototype.resetMatrix = function () { this.drawingContext.setTransform(1, 0, 0, 1, 0, 0); this.drawingContext.scale( this._pInst._pixelDensity, @@ -1148,16 +1148,16 @@ p5.Renderer2D.prototype.resetMatrix = function() { return this; }; -p5.Renderer2D.prototype.rotate = function(rad) { +p5.Renderer2D.prototype.rotate = function (rad) { this.drawingContext.rotate(rad); }; -p5.Renderer2D.prototype.scale = function(x, y) { +p5.Renderer2D.prototype.scale = function (x, y) { this.drawingContext.scale(x, y); return this; }; -p5.Renderer2D.prototype.translate = function(x, y) { +p5.Renderer2D.prototype.translate = function (x, y) { // support passing a vector as the 1st parameter if (x instanceof p5.Vector) { y = x.y; @@ -1172,7 +1172,7 @@ p5.Renderer2D.prototype.translate = function(x, y) { // ////////////////////////////////////////////// -p5.Renderer2D.prototype.text = function(str, x, y, maxWidth, maxHeight) { +p5.Renderer2D.prototype.text = function (str, x, y, maxWidth, maxHeight) { let baselineHacked; // baselineHacked: (HACK) @@ -1195,7 +1195,7 @@ p5.Renderer2D.prototype.text = function(str, x, y, maxWidth, maxHeight) { return p; }; -p5.Renderer2D.prototype._renderText = function(p, line, x, y, maxY, minY) { +p5.Renderer2D.prototype._renderText = function (p, line, x, y, maxY, minY) { if (y < minY || y >= maxY) { return; // don't render lines beyond our minY/maxY bounds (see #5785) } @@ -1228,7 +1228,7 @@ p5.Renderer2D.prototype._renderText = function(p, line, x, y, maxY, minY) { return p; }; -p5.Renderer2D.prototype.textWidth = function(s) { +p5.Renderer2D.prototype.textWidth = function (s) { if (this._isOpenType()) { return this._textFont._textWidth(s, this._textSize); } @@ -1236,7 +1236,7 @@ p5.Renderer2D.prototype.textWidth = function(s) { return this.drawingContext.measureText(s).width; }; -p5.Renderer2D.prototype._applyTextProperties = function() { +p5.Renderer2D.prototype._applyTextProperties = function () { let font; const p = this._pInst; @@ -1272,7 +1272,7 @@ p5.Renderer2D.prototype._applyTextProperties = function() { // store on the push stack. // derived renderers should call the base class' push() method // to fetch the base style object. -p5.Renderer2D.prototype.push = function() { +p5.Renderer2D.prototype.push = function () { this.drawingContext.save(); // get the base renderer style @@ -1284,7 +1284,7 @@ p5.Renderer2D.prototype.push = function() { // from its push() method. // derived renderers should pass this object to their base // class' pop method -p5.Renderer2D.prototype.pop = function(style) { +p5.Renderer2D.prototype.pop = function (style) { this.drawingContext.restore(); // Re-cache the fill / stroke state this._cachedFillStyle = this.drawingContext.fillStyle; diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 0870d79d4e..74a0c5c953 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -86,205 +86,205 @@ const defaultShaders = { * @todo extend class to include public method for offscreen * rendering (FBO). */ -p5.RendererGL = class RendererGL extends p5.Renderer { - constructor(elt, pInst, isMainCanvas, attr) { - super(elt, pInst, isMainCanvas); - this._setAttributeDefaults(pInst); - this._initContext(); - this.isP3D = true; //lets us know we're in 3d mode - - // 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; +p5.RendererGL = function (elt, pInst, isMainCanvas, attr) { + p5.Renderer.call(this, elt, pInst, isMainCanvas); + this._setAttributeDefaults(pInst); + this._initContext(); + this.isP3D = true; //lets us know we're in 3d mode - // lights - this._enableLighting = false; + // 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); - this.ambientLightColors = []; - this.specularColors = [1, 1, 1]; + // erasing + this._isErasing = false; - this.directionalLightDirections = []; - this.directionalLightDiffuseColors = []; - this.directionalLightSpecularColors = []; + // lights + this._enableLighting = false; - this.pointLightPositions = []; - this.pointLightDiffuseColors = []; - this.pointLightSpecularColors = []; + this.ambientLightColors = []; + this.specularColors = [1, 1, 1]; - this.spotLightPositions = []; - this.spotLightDirections = []; - this.spotLightDiffuseColors = []; - this.spotLightSpecularColors = []; - this.spotLightAngle = []; - this.spotLightConc = []; + this.directionalLightDirections = []; + this.directionalLightDiffuseColors = []; + this.directionalLightSpecularColors = []; - this.drawMode = constants.FILL; + this.pointLightPositions = []; + this.pointLightDiffuseColors = []; + this.pointLightSpecularColors = []; - 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.spotLightPositions = []; + this.spotLightDirections = []; + this.spotLightDiffuseColors = []; + this.spotLightSpecularColors = []; + this.spotLightAngle = []; + this.spotLightConc = []; - this.curBlendMode = constants.BLEND; - this._cachedBlendMode = undefined; - if (this.webglVersion === constants.WEBGL2) { - this.blendExt = this.GL; - } else { - this.blendExt = this.GL.getExtension('EXT_blend_minmax'); + 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._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._useLineColor = false; + this._useVertexColor = false; + + this.registerEnabled = []; + + 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 p5.Matrix(); + this.uPMatrix = new p5.Matrix(); + this.uNMatrix = new p5.Matrix('mat3'); + + // Current vertex normal + this._currentNormal = new p5.Vector(0, 0, 1); + + // Camera + this._curCamera = new p5.Camera(this); + this._curCamera._computeCameraDefaultSettings(); + this._curCamera._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 p5.Vector(0, 0); + this.moveVelocity = new p5.Vector(0, 0); + + 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 p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), + new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), + new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), + new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), + new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) + ], + fill: [ + new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), + new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), + new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), + new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), + //new BufferDef(3, 'vertexSpeculars', 'specularBuffer', 'aSpecularColor'), + new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) + ], + text: [ + new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), + new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) + ] } - this._isBlending = false; - - - this._hasSetAmbient = false; - this._useSpecularMaterial = false; - this._useEmissiveMaterial = false; - this._useNormalMaterial = false; - this._useShininess = 1; - - this._useLineColor = false; - this._useVertexColor = false; - - this.registerEnabled = []; - - 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 p5.Matrix(); - this.uPMatrix = new p5.Matrix(); - this.uNMatrix = new p5.Matrix('mat3'); - - // Current vertex normal - this._currentNormal = new p5.Vector(0, 0, 1); - - // Camera - this._curCamera = new p5.Camera(this); - this._curCamera._computeCameraDefaultSettings(); - this._curCamera._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 p5.Vector(0, 0); - this.moveVelocity = new p5.Vector(0, 0); - - 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 p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), - new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), - new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) - ], - fill: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), - new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), - new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), - new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), - //new BufferDef(3, 'vertexSpeculars', 'specularBuffer', 'aSpecularColor'), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) - ], - text: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) - ] - } - }; - - // Immediate Mode - // Geometry and Material hashes stored here - this.immediateMode = { - geometry: new p5.Geometry(), - shapeMode: constants.TRIANGLE_FAN, - _bezierVertex: [], - _quadraticVertex: [], - _curveVertex: [], - buffers: { - fill: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), - new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), - new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), - new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) - ], - stroke: [ - new p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), - new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), - new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), - new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) - ], - point: this.GL.createBuffer() - } - }; + }; + + // Immediate Mode + // Geometry and Material hashes stored here + this.immediateMode = { + geometry: new p5.Geometry(), + shapeMode: constants.TRIANGLE_FAN, + _bezierVertex: [], + _quadraticVertex: [], + _curveVertex: [], + buffers: { + fill: [ + new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), + new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), + new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), + new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), + new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) + ], + stroke: [ + new p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this, this._flatten), + new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this, this._flatten), + new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this, this._flatten), + new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this, this._flatten), + new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) + ], + point: this.GL.createBuffer() + } + }; - this.pointSize = 5.0; //default point size - this.curStrokeWeight = 1; - this.curStrokeCap = constants.ROUND; - this.curStrokeJoin = constants.ROUND; + this.pointSize = 5.0; //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(); + // 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(); + // set of framebuffers in use + this.framebuffers = new Set(); - this.textureMode = constants.IMAGE; - // default wrap settings - this.textureWrapX = constants.CLAMP; - this.textureWrapY = constants.CLAMP; - this._tex = null; - this._curveTightness = 6; + 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 = []; + // 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; + // 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(); + // Used to distinguish between user calls to vertex() and internal calls + this.isProcessingVertices = false; + this._tessy = this._initTessy(); - this.fontInfos = {}; + this.fontInfos = {}; - this._curShader = undefined; + this._curShader = undefined; - return this; - } -}; + return this; +} + +p5.RendererGL.prototype = Object.create(p5.Renderer.prototype); ////////////////////////////////////////////// // Setting From 9704b389e5783344e1e92327a8ae946d91f9f002 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Sun, 21 May 2023 20:50:06 +0900 Subject: [PATCH 11/22] add Missing semicolon. --- src/core/p5.Renderer.js | 2 +- src/core/p5.Renderer2D.js | 2 +- src/webgl/p5.RendererGL.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index 8372b1f62d..af2d77d1ec 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -57,7 +57,7 @@ p5.Renderer = function (elt, pInst, isMainCanvas) { this._strokeSet = false; this._fillSet = false; this._leadingSet = false; -} +}; p5.Renderer.prototype = Object.create(p5.Element.prototype); diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index bc69814c74..1e0875f8da 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -16,7 +16,7 @@ p5.Renderer2D = function (elt, pInst, isMainCanvas) { this.drawingContext = this.canvas.getContext('2d'); this._pInst._setProperty('drawingContext', this.drawingContext); return this; -} +}; p5.Renderer2D.prototype = Object.create(p5.Renderer.prototype); diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 74a0c5c953..ff4b635dd3 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -282,7 +282,7 @@ p5.RendererGL = function (elt, pInst, isMainCanvas, attr) { this._curShader = undefined; return this; -} +}; p5.RendererGL.prototype = Object.create(p5.Renderer.prototype); From 4ce53afb2933f6b09f480c762bd163ccf812f79a Mon Sep 17 00:00:00 2001 From: AsukaMinato Date: Wed, 31 May 2023 01:16:19 +0900 Subject: [PATCH 12/22] ColorConversion to simpler obj. --- src/color/color_conversion.js | 435 +++++++++++++++++----------------- 1 file changed, 217 insertions(+), 218 deletions(-) diff --git a/src/color/color_conversion.js b/src/color/color_conversion.js index 53b089d139..b07d432114 100644 --- a/src/color/color_conversion.js +++ b/src/color/color_conversion.js @@ -14,130 +14,96 @@ */ import p5 from '../core/main'; -p5.ColorConversion = {}; +p5.ColorConversion = { + /** + * Convert an HSBA array to HSLA. + */ + _hsbaToHSLA(hsba) { + const hue = hsba[0]; + let sat = hsba[1]; + const val = hsba[2]; -/** - * Convert an HSBA array to HSLA. - */ -p5.ColorConversion._hsbaToHSLA = function(hsba) { - const hue = hsba[0]; - let sat = hsba[1]; - const val = hsba[2]; + // Calculate lightness. + const li = (2 - sat) * val / 2; - // Calculate lightness. - const li = (2 - sat) * val / 2; - - // Convert saturation. - if (li !== 0) { - if (li === 1) { - sat = 0; - } else if (li < 0.5) { - sat = sat / (2 - sat); - } else { - sat = sat * val / (2 - li * 2); + // Convert saturation. + if (li !== 0) { + if (li === 1) { + sat = 0; + } else if (li < 0.5) { + sat = sat / (2 - sat); + } else { + sat = sat * val / (2 - li * 2); + } } - } - // Hue and alpha stay the same. - return [hue, sat, li, hsba[3]]; -}; + // Hue and alpha stay the same. + return [hue, sat, li, hsba[3]]; + }, -/** - * Convert an HSBA array to RGBA. - */ -p5.ColorConversion._hsbaToRGBA = function(hsba) { - const hue = hsba[0] * 6; // We will split hue into 6 sectors. - const sat = hsba[1]; - const val = hsba[2]; + /** + * Convert an HSBA array to RGBA. + */ + _hsbaToRGBA(hsba) { + const hue = hsba[0] * 6; // We will split hue into 6 sectors. + const sat = hsba[1]; + const val = hsba[2]; - let RGBA = []; + let RGBA = []; - if (sat === 0) { - RGBA = [val, val, val, hsba[3]]; // Return early if grayscale. - } else { - const sector = Math.floor(hue); - const tint1 = val * (1 - sat); - const tint2 = val * (1 - sat * (hue - sector)); - const tint3 = val * (1 - sat * (1 + sector - hue)); - let red, green, blue; - if (sector === 1) { - // Yellow to green. - red = tint2; - green = val; - blue = tint1; - } else if (sector === 2) { - // Green to cyan. - red = tint1; - green = val; - blue = tint3; - } else if (sector === 3) { - // Cyan to blue. - red = tint1; - green = tint2; - blue = val; - } else if (sector === 4) { - // Blue to magenta. - red = tint3; - green = tint1; - blue = val; - } else if (sector === 5) { - // Magenta to red. - red = val; - green = tint1; - blue = tint2; + if (sat === 0) { + RGBA = [val, val, val, hsba[3]]; // Return early if grayscale. } else { - // Red to yellow (sector could be 0 or 6). - red = val; - green = tint3; - blue = tint1; + const sector = Math.floor(hue); + const tint1 = val * (1 - sat); + const tint2 = val * (1 - sat * (hue - sector)); + const tint3 = val * (1 - sat * (1 + sector - hue)); + let red, green, blue; + if (sector === 1) { + // Yellow to green. + red = tint2; + green = val; + blue = tint1; + } else if (sector === 2) { + // Green to cyan. + red = tint1; + green = val; + blue = tint3; + } else if (sector === 3) { + // Cyan to blue. + red = tint1; + green = tint2; + blue = val; + } else if (sector === 4) { + // Blue to magenta. + red = tint3; + green = tint1; + blue = val; + } else if (sector === 5) { + // Magenta to red. + red = val; + green = tint1; + blue = tint2; + } else { + // Red to yellow (sector could be 0 or 6). + red = val; + green = tint3; + blue = tint1; + } + RGBA = [red, green, blue, hsba[3]]; } - RGBA = [red, green, blue, hsba[3]]; - } - - return RGBA; -}; - -/** - * Convert an HSLA array to HSBA. - */ -p5.ColorConversion._hslaToHSBA = function(hsla) { - const hue = hsla[0]; - let sat = hsla[1]; - const li = hsla[2]; - // Calculate brightness. - let val; - if (li < 0.5) { - val = (1 + sat) * li; - } else { - val = li + sat - li * sat; - } + return RGBA; + }, - // Convert saturation. - sat = 2 * (val - li) / val; + /** + * Convert an HSLA array to HSBA. + */ + _hslaToHSBA(hsla) { + const hue = hsla[0]; + let sat = hsla[1]; + const li = hsla[2]; - // Hue and alpha stay the same. - return [hue, sat, val, hsla[3]]; -}; - -/** - * Convert an HSLA array to RGBA. - * - * We need to change basis from HSLA to something that can be more easily be - * projected onto RGBA. We will choose hue and brightness as our first two - * components, and pick a convenient third one ('zest') so that we don't need - * to calculate formal HSBA saturation. - */ -p5.ColorConversion._hslaToRGBA = function(hsla) { - const hue = hsla[0] * 6; // We will split hue into 6 sectors. - const sat = hsla[1]; - const li = hsla[2]; - - let RGBA = []; - - if (sat === 0) { - RGBA = [li, li, li, hsla[3]]; // Return early if grayscale. - } else { // Calculate brightness. let val; if (li < 0.5) { @@ -146,126 +112,159 @@ p5.ColorConversion._hslaToRGBA = function(hsla) { val = li + sat - li * sat; } - // Define zest. - const zest = 2 * li - val; + // Convert saturation. + sat = 2 * (val - li) / val; - // Implement projection (project onto green by default). - const hzvToRGB = (hue, zest, val) => { - if (hue < 0) { - // Hue must wrap to allow projection onto red and blue. - hue += 6; - } else if (hue >= 6) { - hue -= 6; - } - if (hue < 1) { - // Red to yellow (increasing green). - return zest + (val - zest) * hue; - } else if (hue < 3) { - // Yellow to cyan (greatest green). - return val; - } else if (hue < 4) { - // Cyan to blue (decreasing green). - return zest + (val - zest) * (4 - hue); + // Hue and alpha stay the same. + return [hue, sat, val, hsla[3]]; + }, + + /** + * Convert an HSLA array to RGBA. + * + * We need to change basis from HSLA to something that can be more easily be + * projected onto RGBA. We will choose hue and brightness as our first two + * components, and pick a convenient third one ('zest') so that we don't need + * to calculate formal HSBA saturation. + */ + _hslaToRGBA(hsla) { + const hue = hsla[0] * 6; // We will split hue into 6 sectors. + const sat = hsla[1]; + const li = hsla[2]; + + let RGBA = []; + + if (sat === 0) { + RGBA = [li, li, li, hsla[3]]; // Return early if grayscale. + } else { + // Calculate brightness. + let val; + if (li < 0.5) { + val = (1 + sat) * li; } else { - // Blue to red (least green). - return zest; + val = li + sat - li * sat; } - }; - // Perform projections, offsetting hue as necessary. - RGBA = [ - hzvToRGB(hue + 2, zest, val), - hzvToRGB(hue, zest, val), - hzvToRGB(hue - 2, zest, val), - hsla[3] - ]; - } + // Define zest. + const zest = 2 * li - val; - return RGBA; -}; + // Implement projection (project onto green by default). + const hzvToRGB = (hue, zest, val) => { + if (hue < 0) { + // Hue must wrap to allow projection onto red and blue. + hue += 6; + } else if (hue >= 6) { + hue -= 6; + } + if (hue < 1) { + // Red to yellow (increasing green). + return zest + (val - zest) * hue; + } else if (hue < 3) { + // Yellow to cyan (greatest green). + return val; + } else if (hue < 4) { + // Cyan to blue (decreasing green). + return zest + (val - zest) * (4 - hue); + } else { + // Blue to red (least green). + return zest; + } + }; -/** - * Convert an RGBA array to HSBA. - */ -p5.ColorConversion._rgbaToHSBA = function(rgba) { - const red = rgba[0]; - const green = rgba[1]; - const blue = rgba[2]; + // Perform projections, offsetting hue as necessary. + RGBA = [ + hzvToRGB(hue + 2, zest, val), + hzvToRGB(hue, zest, val), + hzvToRGB(hue - 2, zest, val), + hsla[3] + ]; + } - const val = Math.max(red, green, blue); - const chroma = val - Math.min(red, green, blue); + return RGBA; + }, - let hue, sat; - if (chroma === 0) { - // Return early if grayscale. - hue = 0; - sat = 0; - } else { - sat = chroma / val; - if (red === val) { - // Magenta to yellow. - hue = (green - blue) / chroma; - } else if (green === val) { - // Yellow to cyan. - hue = 2 + (blue - red) / chroma; - } else if (blue === val) { - // Cyan to magenta. - hue = 4 + (red - green) / chroma; - } - if (hue < 0) { - // Confine hue to the interval [0, 1). - hue += 6; - } else if (hue >= 6) { - hue -= 6; + /** + * Convert an RGBA array to HSBA. + */ + _rgbaToHSBA(rgba) { + const red = rgba[0]; + const green = rgba[1]; + const blue = rgba[2]; + + const val = Math.max(red, green, blue); + const chroma = val - Math.min(red, green, blue); + + let hue, sat; + if (chroma === 0) { + // Return early if grayscale. + hue = 0; + sat = 0; + } else { + sat = chroma / val; + if (red === val) { + // Magenta to yellow. + hue = (green - blue) / chroma; + } else if (green === val) { + // Yellow to cyan. + hue = 2 + (blue - red) / chroma; + } else if (blue === val) { + // Cyan to magenta. + hue = 4 + (red - green) / chroma; + } + if (hue < 0) { + // Confine hue to the interval [0, 1). + hue += 6; + } else if (hue >= 6) { + hue -= 6; + } } - } - return [hue / 6, sat, val, rgba[3]]; -}; + return [hue / 6, sat, val, rgba[3]]; + }, -/** - * Convert an RGBA array to HSLA. - */ -p5.ColorConversion._rgbaToHSLA = function(rgba) { - const red = rgba[0]; - const green = rgba[1]; - const blue = rgba[2]; + /** + * Convert an RGBA array to HSLA. + */ + _rgbaToHSLA(rgba) { + const red = rgba[0]; + const green = rgba[1]; + const blue = rgba[2]; - const val = Math.max(red, green, blue); - const min = Math.min(red, green, blue); - const li = val + min; // We will halve this later. - const chroma = val - min; + const val = Math.max(red, green, blue); + const min = Math.min(red, green, blue); + const li = val + min; // We will halve this later. + const chroma = val - min; - let hue, sat; - if (chroma === 0) { - // Return early if grayscale. - hue = 0; - sat = 0; - } else { - if (li < 1) { - sat = chroma / li; + let hue, sat; + if (chroma === 0) { + // Return early if grayscale. + hue = 0; + sat = 0; } else { - sat = chroma / (2 - li); - } - if (red === val) { - // Magenta to yellow. - hue = (green - blue) / chroma; - } else if (green === val) { - // Yellow to cyan. - hue = 2 + (blue - red) / chroma; - } else if (blue === val) { - // Cyan to magenta. - hue = 4 + (red - green) / chroma; - } - if (hue < 0) { - // Confine hue to the interval [0, 1). - hue += 6; - } else if (hue >= 6) { - hue -= 6; + if (li < 1) { + sat = chroma / li; + } else { + sat = chroma / (2 - li); + } + if (red === val) { + // Magenta to yellow. + hue = (green - blue) / chroma; + } else if (green === val) { + // Yellow to cyan. + hue = 2 + (blue - red) / chroma; + } else if (blue === val) { + // Cyan to magenta. + hue = 4 + (red - green) / chroma; + } + if (hue < 0) { + // Confine hue to the interval [0, 1). + hue += 6; + } else if (hue >= 6) { + hue -= 6; + } } - } - - return [hue / 6, sat, li / 2, rgba[3]]; -}; + return [hue / 6, sat, li / 2, rgba[3]]; + }, +} export default p5.ColorConversion; From 20a504ac7df6428514ef8af037d118c384ee6174 Mon Sep 17 00:00:00 2001 From: AsukaMinato Date: Wed, 31 May 2023 01:20:11 +0900 Subject: [PATCH 13/22] fix ci --- src/color/color_conversion.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/color/color_conversion.js b/src/color/color_conversion.js index b07d432114..850d24918b 100644 --- a/src/color/color_conversion.js +++ b/src/color/color_conversion.js @@ -265,6 +265,6 @@ p5.ColorConversion = { } return [hue / 6, sat, li / 2, rgba[3]]; - }, -} + } +}; export default p5.ColorConversion; From d505d9448df68b7a5ec14a160ca252010535634d Mon Sep 17 00:00:00 2001 From: asukaminato Date: Wed, 31 May 2023 01:35:00 +0900 Subject: [PATCH 14/22] Color to all es6 --- src/color/p5.Color.js | 1237 ++++++++++++++++++++--------------------- 1 file changed, 615 insertions(+), 622 deletions(-) diff --git a/src/color/p5.Color.js b/src/color/p5.Color.js index bd4026865a..4375afb0be 100644 --- a/src/color/p5.Color.js +++ b/src/color/p5.Color.js @@ -38,426 +38,654 @@ import color_conversion from './color_conversion'; * or CSS color. */ p5.Color = class Color { - constructor(pInst, vals){ - // Record color mode and maxes at time of construction. + constructor(pInst, vals) { + // Record color mode and maxes at time of construction. this._storeModeAndMaxes(pInst._colorMode, pInst._colorMaxes); // Calculate normalized RGBA values. - if ( - this.mode !== constants.RGB && - this.mode !== constants.HSL && - this.mode !== constants.HSB - ) { + if (![constants.RGB, constants.HSL, constants.HSB].includes(this.mode)) { throw new Error(`${this.mode} is an invalid colorMode.`); } else { - this._array = p5.Color._parseInputs.apply(this, vals); + this._array = Color._parseInputs(...vals); } // Expose closest screen color. this._calculateLevels(); - return this; } -}; -/** - * This method returns the color formatted as a string. This can be useful - * for debugging, or for using p5.js with other libraries. - * - * @method toString - * @param {String} [format] How the color string will be formatted. - * Leaving this empty formats the string as rgba(r, g, b, a). - * '#rgb' '#rgba' '#rrggbb' and '#rrggbbaa' format as hexadecimal color codes. - * 'rgb' 'hsb' and 'hsl' return the color formatted in the specified color mode. - * 'rgba' 'hsba' and 'hsla' are the same as above but with alpha channels. - * 'rgb%' 'hsb%' 'hsl%' 'rgba%' 'hsba%' and 'hsla%' format as percentages. - * @return {String} the formatted string - * - * @example - *
- * - * createCanvas(200, 100); - * let myColor; - * stroke(255); - * myColor = color(100, 100, 250); - * fill(myColor); - * rotate(HALF_PI); - * text(myColor.toString(), 0, -5); - * text(myColor.toString('#rrggbb'), 0, -30); - * text(myColor.toString('rgba%'), 0, -55); - * describe('A canvas with 3 text representation of their color.'); - * - *
- * - *
- * - * let myColor = color(100, 130, 250); - * text(myColor.toString('#rrggbb'), 25, 25); - * - *
- */ -p5.Color.prototype.toString = function(format) { - const a = this.levels; - const f = this._array; - const alpha = f[3]; // String representation uses normalized alpha - - switch (format) { - case '#rrggbb': - return '#'.concat( - a[0] < 16 ? '0'.concat(a[0].toString(16)) : a[0].toString(16), - a[1] < 16 ? '0'.concat(a[1].toString(16)) : a[1].toString(16), - a[2] < 16 ? '0'.concat(a[2].toString(16)) : a[2].toString(16) - ); - - case '#rrggbbaa': - return '#'.concat( - a[0] < 16 ? '0'.concat(a[0].toString(16)) : a[0].toString(16), - a[1] < 16 ? '0'.concat(a[1].toString(16)) : a[1].toString(16), - a[2] < 16 ? '0'.concat(a[2].toString(16)) : a[2].toString(16), - a[3] < 16 ? '0'.concat(a[3].toString(16)) : a[3].toString(16) - ); - - case '#rgb': - return '#'.concat( - Math.round(f[0] * 15).toString(16), - Math.round(f[1] * 15).toString(16), - Math.round(f[2] * 15).toString(16) - ); - - case '#rgba': - return '#'.concat( - Math.round(f[0] * 15).toString(16), - Math.round(f[1] * 15).toString(16), - Math.round(f[2] * 15).toString(16), - Math.round(f[3] * 15).toString(16) - ); - - case 'rgb': - return 'rgb('.concat(a[0], ', ', a[1], ', ', a[2], ')'); - - case 'rgb%': - return 'rgb('.concat( - (100 * f[0]).toPrecision(3), - '%, ', - (100 * f[1]).toPrecision(3), - '%, ', - (100 * f[2]).toPrecision(3), - '%)' - ); - - case 'rgba%': - return 'rgba('.concat( - (100 * f[0]).toPrecision(3), - '%, ', - (100 * f[1]).toPrecision(3), - '%, ', - (100 * f[2]).toPrecision(3), - '%, ', - (100 * f[3]).toPrecision(3), - '%)' - ); - - case 'hsb': - case 'hsv': - if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array); - return 'hsb('.concat( - this.hsba[0] * this.maxes[constants.HSB][0], - ', ', - this.hsba[1] * this.maxes[constants.HSB][1], - ', ', - this.hsba[2] * this.maxes[constants.HSB][2], - ')' - ); - - case 'hsb%': - case 'hsv%': - if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array); - return 'hsb('.concat( - (100 * this.hsba[0]).toPrecision(3), - '%, ', - (100 * this.hsba[1]).toPrecision(3), - '%, ', - (100 * this.hsba[2]).toPrecision(3), - '%)' - ); - - case 'hsba': - case 'hsva': - if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array); - return 'hsba('.concat( - this.hsba[0] * this.maxes[constants.HSB][0], - ', ', - this.hsba[1] * this.maxes[constants.HSB][1], - ', ', - this.hsba[2] * this.maxes[constants.HSB][2], - ', ', - alpha, - ')' - ); - - case 'hsba%': - case 'hsva%': - if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array); - return 'hsba('.concat( - (100 * this.hsba[0]).toPrecision(3), - '%, ', - (100 * this.hsba[1]).toPrecision(3), - '%, ', - (100 * this.hsba[2]).toPrecision(3), - '%, ', - (100 * alpha).toPrecision(3), - '%)' - ); - - case 'hsl': - if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array); - return 'hsl('.concat( - this.hsla[0] * this.maxes[constants.HSL][0], - ', ', - this.hsla[1] * this.maxes[constants.HSL][1], - ', ', - this.hsla[2] * this.maxes[constants.HSL][2], - ')' - ); - - case 'hsl%': - if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array); - return 'hsl('.concat( - (100 * this.hsla[0]).toPrecision(3), - '%, ', - (100 * this.hsla[1]).toPrecision(3), - '%, ', - (100 * this.hsla[2]).toPrecision(3), - '%)' - ); - - case 'hsla': - if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array); - return 'hsla('.concat( - this.hsla[0] * this.maxes[constants.HSL][0], - ', ', - this.hsla[1] * this.maxes[constants.HSL][1], - ', ', - this.hsla[2] * this.maxes[constants.HSL][2], - ', ', - alpha, - ')' - ); - - case 'hsla%': - if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array); - return 'hsl('.concat( - (100 * this.hsla[0]).toPrecision(3), - '%, ', - (100 * this.hsla[1]).toPrecision(3), - '%, ', - (100 * this.hsla[2]).toPrecision(3), - '%, ', - (100 * alpha).toPrecision(3), - '%)' - ); - - case 'rgba': - default: - return 'rgba('.concat(a[0], ',', a[1], ',', a[2], ',', alpha, ')'); - } -}; + /** + * This method returns the color formatted as a string. This can be useful + * for debugging, or for using p5.js with other libraries. + * + * @method toString + * @param {String} [format] How the color string will be formatted. + * Leaving this empty formats the string as rgba(r, g, b, a). + * '#rgb' '#rgba' '#rrggbb' and '#rrggbbaa' format as hexadecimal color codes. + * 'rgb' 'hsb' and 'hsl' return the color formatted in the specified color mode. + * 'rgba' 'hsba' and 'hsla' are the same as above but with alpha channels. + * 'rgb%' 'hsb%' 'hsl%' 'rgba%' 'hsba%' and 'hsla%' format as percentages. + * @return {String} the formatted string + * + * @example + *
+ * + * createCanvas(200, 100); + * let myColor; + * stroke(255); + * myColor = color(100, 100, 250); + * fill(myColor); + * rotate(HALF_PI); + * text(myColor.toString(), 0, -5); + * text(myColor.toString('#rrggbb'), 0, -30); + * text(myColor.toString('rgba%'), 0, -55); + * describe('A canvas with 3 text representation of their color.'); + * + *
+ * + *
+ * + * let myColor = color(100, 130, 250); + * text(myColor.toString('#rrggbb'), 25, 25); + * + *
+ */ + toString(format) { + const a = this.levels; + const f = this._array; + const alpha = f[3]; // String representation uses normalized alpha + + switch (format) { + case '#rrggbb': + return '#'.concat( + a[0] < 16 ? '0'.concat(a[0].toString(16)) : a[0].toString(16), + a[1] < 16 ? '0'.concat(a[1].toString(16)) : a[1].toString(16), + a[2] < 16 ? '0'.concat(a[2].toString(16)) : a[2].toString(16) + ); + + case '#rrggbbaa': + return '#'.concat( + a[0] < 16 ? '0'.concat(a[0].toString(16)) : a[0].toString(16), + a[1] < 16 ? '0'.concat(a[1].toString(16)) : a[1].toString(16), + a[2] < 16 ? '0'.concat(a[2].toString(16)) : a[2].toString(16), + a[3] < 16 ? '0'.concat(a[3].toString(16)) : a[3].toString(16) + ); + + case '#rgb': + return '#'.concat( + Math.round(f[0] * 15).toString(16), + Math.round(f[1] * 15).toString(16), + Math.round(f[2] * 15).toString(16) + ); + + case '#rgba': + return '#'.concat( + Math.round(f[0] * 15).toString(16), + Math.round(f[1] * 15).toString(16), + Math.round(f[2] * 15).toString(16), + Math.round(f[3] * 15).toString(16) + ); + + case 'rgb': + return 'rgb('.concat(a[0], ', ', a[1], ', ', a[2], ')'); + + case 'rgb%': + return 'rgb('.concat( + (100 * f[0]).toPrecision(3), + '%, ', + (100 * f[1]).toPrecision(3), + '%, ', + (100 * f[2]).toPrecision(3), + '%)' + ); + + case 'rgba%': + return 'rgba('.concat( + (100 * f[0]).toPrecision(3), + '%, ', + (100 * f[1]).toPrecision(3), + '%, ', + (100 * f[2]).toPrecision(3), + '%, ', + (100 * f[3]).toPrecision(3), + '%)' + ); + + case 'hsb': + case 'hsv': + if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array); + return 'hsb('.concat( + this.hsba[0] * this.maxes[constants.HSB][0], + ', ', + this.hsba[1] * this.maxes[constants.HSB][1], + ', ', + this.hsba[2] * this.maxes[constants.HSB][2], + ')' + ); + + case 'hsb%': + case 'hsv%': + if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array); + return 'hsb('.concat( + (100 * this.hsba[0]).toPrecision(3), + '%, ', + (100 * this.hsba[1]).toPrecision(3), + '%, ', + (100 * this.hsba[2]).toPrecision(3), + '%)' + ); + + case 'hsba': + case 'hsva': + if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array); + return 'hsba('.concat( + this.hsba[0] * this.maxes[constants.HSB][0], + ', ', + this.hsba[1] * this.maxes[constants.HSB][1], + ', ', + this.hsba[2] * this.maxes[constants.HSB][2], + ', ', + alpha, + ')' + ); + + case 'hsba%': + case 'hsva%': + if (!this.hsba) this.hsba = color_conversion._rgbaToHSBA(this._array); + return 'hsba('.concat( + (100 * this.hsba[0]).toPrecision(3), + '%, ', + (100 * this.hsba[1]).toPrecision(3), + '%, ', + (100 * this.hsba[2]).toPrecision(3), + '%, ', + (100 * alpha).toPrecision(3), + '%)' + ); + + case 'hsl': + if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array); + return 'hsl('.concat( + this.hsla[0] * this.maxes[constants.HSL][0], + ', ', + this.hsla[1] * this.maxes[constants.HSL][1], + ', ', + this.hsla[2] * this.maxes[constants.HSL][2], + ')' + ); + + case 'hsl%': + if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array); + return 'hsl('.concat( + (100 * this.hsla[0]).toPrecision(3), + '%, ', + (100 * this.hsla[1]).toPrecision(3), + '%, ', + (100 * this.hsla[2]).toPrecision(3), + '%)' + ); + + case 'hsla': + if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array); + return 'hsla('.concat( + this.hsla[0] * this.maxes[constants.HSL][0], + ', ', + this.hsla[1] * this.maxes[constants.HSL][1], + ', ', + this.hsla[2] * this.maxes[constants.HSL][2], + ', ', + alpha, + ')' + ); + + case 'hsla%': + if (!this.hsla) this.hsla = color_conversion._rgbaToHSLA(this._array); + return 'hsl('.concat( + (100 * this.hsla[0]).toPrecision(3), + '%, ', + (100 * this.hsla[1]).toPrecision(3), + '%, ', + (100 * this.hsla[2]).toPrecision(3), + '%, ', + (100 * alpha).toPrecision(3), + '%)' + ); + + case 'rgba': + default: + return 'rgba('.concat(a[0], ',', a[1], ',', a[2], ',', alpha, ')'); + } + }; + + /** + * The setRed method sets the red component of a color. + * The range depends on your color mode, in the default RGB mode it's between 0 and 255. + * @method setRed + * @param {Number} red the new red value + * @example + *
+ * + * let backgroundColor; + * + * function setup() { + * backgroundColor = color(100, 50, 150); + * } + * + * function draw() { + * backgroundColor.setRed(128 + 128 * sin(millis() / 1000)); + * background(backgroundColor); + * describe('canvas with gradually changing background color'); + * } + * + *
+ */ + setRed(new_red) { + this._array[0] = new_red / this.maxes[constants.RGB][0]; + this._calculateLevels(); + }; + + /** + * The setGreen method sets the green component of a color. + * The range depends on your color mode, in the default RGB mode it's between 0 and 255. + * @method setGreen + * @param {Number} green the new green value + * @example + *
+ * + * let backgroundColor = color(100, 50, 150); + * function draw() { + * backgroundColor.setGreen(128 + 128 * sin(millis() / 1000)); + * background(backgroundColor); + * describe('canvas with gradually changing background color'); + * } + * + *
+ * + **/ + setGreen(new_green) { + this._array[1] = new_green / this.maxes[constants.RGB][1]; + this._calculateLevels(); + }; + + /** + * The setBlue method sets the blue component of a color. + * The range depends on your color mode, in the default RGB mode it's between 0 and 255. + * @method setBlue + * @param {Number} blue the new blue value + * @example + *
+ * + * let backgroundColor = color(100, 50, 150); + * function draw() { + * backgroundColor.setBlue(128 + 128 * sin(millis() / 1000)); + * background(backgroundColor); + * describe('canvas with gradually changing background color'); + * } + * + *
+ * + **/ + setBlue(new_blue) { + this._array[2] = new_blue / this.maxes[constants.RGB][2]; + this._calculateLevels(); + }; + + /** + * The setAlpha method sets the transparency (alpha) value of a color. + * The range depends on your color mode, in the default RGB mode it's between 0 and 255. + * @method setAlpha + * @param {Number} alpha the new alpha value + * @example + *
+ * + * function draw() { + * clear(); + * background(200); + * squareColor = color(100, 50, 100); + * squareColor.setAlpha(128 + 128 * sin(millis() / 1000)); + * fill(squareColor); + * rect(13, 13, width - 26, height - 26); + * describe('a square with gradually changing opacity on a gray background'); + * } + * + *
+ **/ + setAlpha(new_alpha) { + this._array[3] = new_alpha / this.maxes[this.mode][3]; + this._calculateLevels(); + }; -/** - * The setRed method sets the red component of a color. - * The range depends on your color mode, in the default RGB mode it's between 0 and 255. - * @method setRed - * @param {Number} red the new red value - * @example - *
- * - * let backgroundColor; - * - * function setup() { - * backgroundColor = color(100, 50, 150); - * } - * - * function draw() { - * backgroundColor.setRed(128 + 128 * sin(millis() / 1000)); - * background(backgroundColor); - * describe('canvas with gradually changing background color'); - * } - * - *
- */ -p5.Color.prototype.setRed = function(new_red) { - this._array[0] = new_red / this.maxes[constants.RGB][0]; - this._calculateLevels(); -}; + // calculates and stores the closest screen levels + _calculateLevels() { + const array = this._array; + // (loop backwards for performance) + const levels = (this.levels = new Array(array.length)); + for (let i = array.length - 1; i >= 0; --i) { + levels[i] = Math.round(array[i] * 255); + } -/** - * The setGreen method sets the green component of a color. - * The range depends on your color mode, in the default RGB mode it's between 0 and 255. - * @method setGreen - * @param {Number} green the new green value - * @example - *
- * - * let backgroundColor = color(100, 50, 150); - * function draw() { - * backgroundColor.setGreen(128 + 128 * sin(millis() / 1000)); - * background(backgroundColor); - * describe('canvas with gradually changing background color'); - * } - * - *
- * - **/ -p5.Color.prototype.setGreen = function(new_green) { - this._array[1] = new_green / this.maxes[constants.RGB][1]; - this._calculateLevels(); -}; + // Clear cached HSL/HSB values + this.hsla = null; + this.hsba = null; + }; -/** - * The setBlue method sets the blue component of a color. - * The range depends on your color mode, in the default RGB mode it's between 0 and 255. - * @method setBlue - * @param {Number} blue the new blue value - * @example - *
- * - * let backgroundColor = color(100, 50, 150); - * function draw() { - * backgroundColor.setBlue(128 + 128 * sin(millis() / 1000)); - * background(backgroundColor); - * describe('canvas with gradually changing background color'); - * } - * - *
- * - **/ -p5.Color.prototype.setBlue = function(new_blue) { - this._array[2] = new_blue / this.maxes[constants.RGB][2]; - this._calculateLevels(); -}; + _getAlpha() { + return this._array[3] * this.maxes[this.mode][3]; + }; -/** - * The setAlpha method sets the transparency (alpha) value of a color. - * The range depends on your color mode, in the default RGB mode it's between 0 and 255. - * @method setAlpha - * @param {Number} alpha the new alpha value + // stores the color mode and maxes in this instance of Color + // for later use (by _parseInputs()) + _storeModeAndMaxes(new_mode, new_maxes) { + this.mode = new_mode; + this.maxes = new_maxes; + }; + + _getMode() { + return this.mode; + }; + + _getMaxes() { + return this.maxes; + }; + + _getBlue() { + return this._array[2] * this.maxes[constants.RGB][2]; + }; + + _getBrightness() { + if (!this.hsba) { + this.hsba = color_conversion._rgbaToHSBA(this._array); + } + return this.hsba[2] * this.maxes[constants.HSB][2]; + }; + + _getGreen() { + return this._array[1] * this.maxes[constants.RGB][1]; + }; + + /** + * Hue is the same in HSB and HSL, but the maximum value may be different. + * This function will return the HSB-normalized saturation when supplied with + * an HSB color object, but will default to the HSL-normalized saturation + * otherwise. + */ + _getHue() { + if (this.mode === constants.HSB) { + if (!this.hsba) { + this.hsba = color_conversion._rgbaToHSBA(this._array); + } + return this.hsba[0] * this.maxes[constants.HSB][0]; + } else { + if (!this.hsla) { + this.hsla = color_conversion._rgbaToHSLA(this._array); + } + return this.hsla[0] * this.maxes[constants.HSL][0]; + } + }; + + _getLightness() { + if (!this.hsla) { + this.hsla = color_conversion._rgbaToHSLA(this._array); + } + return this.hsla[2] * this.maxes[constants.HSL][2]; + }; + + _getRed() { + return this._array[0] * this.maxes[constants.RGB][0]; + }; + + /** + * Saturation is scaled differently in HSB and HSL. This function will return + * the HSB saturation when supplied with an HSB color object, but will default + * to the HSL saturation otherwise. + */ + _getSaturation() { + if (this.mode === constants.HSB) { + if (!this.hsba) { + this.hsba = color_conversion._rgbaToHSBA(this._array); + } + return this.hsba[1] * this.maxes[constants.HSB][1]; + } else { + if (!this.hsla) { + this.hsla = color_conversion._rgbaToHSLA(this._array); + } + return this.hsla[1] * this.maxes[constants.HSL][1]; + } + }; + /** + * For a number of different inputs, returns a color formatted as [r, g, b, a] + * arrays, with each component normalized between 0 and 1. + * + * @private + * @param {Array} [...args] An 'array-like' object that represents a list of + * arguments + * @return {Number[]} a color formatted as [r, g, b, a] + * Example: + * input ==> output + * g ==> [g, g, g, 255] + * g,a ==> [g, g, g, a] + * r, g, b ==> [r, g, b, 255] + * r, g, b, a ==> [r, g, b, a] + * [g] ==> [g, g, g, 255] + * [g, a] ==> [g, g, g, a] + * [r, g, b] ==> [r, g, b, 255] + * [r, g, b, a] ==> [r, g, b, a] * @example *
* - * function draw() { - * clear(); - * background(200); - * squareColor = color(100, 50, 100); - * squareColor.setAlpha(128 + 128 * sin(millis() / 1000)); - * fill(squareColor); - * rect(13, 13, width - 26, height - 26); - * describe('a square with gradually changing opacity on a gray background'); - * } + * // todo + * // + * // describe(''); * *
- **/ -p5.Color.prototype.setAlpha = function(new_alpha) { - this._array[3] = new_alpha / this.maxes[this.mode][3]; - this._calculateLevels(); -}; + */ + static _parseInputs(r, g, b, a) { + const numArgs = arguments.length; + const mode = this.mode; + const maxes = this.maxes[mode]; + let results = []; + let i; + + if (numArgs >= 3) { + // Argument is a list of component values. + + results[0] = r / maxes[0]; + results[1] = g / maxes[1]; + results[2] = b / maxes[2]; + + // Alpha may be undefined, so default it to 100%. + if (typeof a === 'number') { + results[3] = a / maxes[3]; + } else { + results[3] = 1; + } -// calculates and stores the closest screen levels -p5.Color.prototype._calculateLevels = function() { - const array = this._array; - // (loop backwards for performance) - const levels = (this.levels = new Array(array.length)); - for (let i = array.length - 1; i >= 0; --i) { - levels[i] = Math.round(array[i] * 255); - } + // Constrain components to the range [0,1]. + // (loop backwards for performance) + for (i = results.length - 1; i >= 0; --i) { + const result = results[i]; + if (result < 0) { + results[i] = 0; + } else if (result > 1) { + results[i] = 1; + } + } - // Clear cached HSL/HSB values - this.hsla = null; - this.hsba = null; -}; + // Convert to RGBA and return. + if (mode === constants.HSL) { + return color_conversion._hslaToRGBA(results); + } else if (mode === constants.HSB) { + return color_conversion._hsbaToRGBA(results); + } else { + return results; + } + } else if (numArgs === 1 && typeof r === 'string') { + const str = r.trim().toLowerCase(); -p5.Color.prototype._getAlpha = function() { - return this._array[3] * this.maxes[this.mode][3]; -}; + // Return if string is a named colour. + if (namedColors[str]) { + return Color._parseInputs(namedColors[str]); + } -// stores the color mode and maxes in this instance of Color -// for later use (by _parseInputs()) -p5.Color.prototype._storeModeAndMaxes = function(new_mode, new_maxes) { - this.mode = new_mode; - this.maxes = new_maxes; -}; + // Try RGBA pattern matching. + if (colorPatterns.HEX3.test(str)) { + // #rgb + results = colorPatterns.HEX3.exec(str) + .slice(1) + .map(color => parseInt(color + color, 16) / 255); + results[3] = 1; + return results; + } else if (colorPatterns.HEX6.test(str)) { + // #rrggbb + results = colorPatterns.HEX6.exec(str) + .slice(1) + .map(color => parseInt(color, 16) / 255); + results[3] = 1; + return results; + } else if (colorPatterns.HEX4.test(str)) { + // #rgba + results = colorPatterns.HEX4.exec(str) + .slice(1) + .map(color => parseInt(color + color, 16) / 255); + return results; + } else if (colorPatterns.HEX8.test(str)) { + // #rrggbbaa + results = colorPatterns.HEX8.exec(str) + .slice(1) + .map(color => parseInt(color, 16) / 255); + return results; + } else if (colorPatterns.RGB.test(str)) { + // rgb(R,G,B) + results = colorPatterns.RGB.exec(str) + .slice(1) + .map(color => color / 255); + results[3] = 1; + return results; + } else if (colorPatterns.RGB_PERCENT.test(str)) { + // rgb(R%,G%,B%) + results = colorPatterns.RGB_PERCENT.exec(str) + .slice(1) + .map(color => parseFloat(color) / 100); + results[3] = 1; + return results; + } else if (colorPatterns.RGBA.test(str)) { + // rgba(R,G,B,A) + results = colorPatterns.RGBA.exec(str) + .slice(1) + .map((color, idx) => { + if (idx === 3) { + return parseFloat(color); + } + return color / 255; + }); + return results; + } else if (colorPatterns.RGBA_PERCENT.test(str)) { + // rgba(R%,G%,B%,A%) + results = colorPatterns.RGBA_PERCENT.exec(str) + .slice(1) + .map((color, idx) => { + if (idx === 3) { + return parseFloat(color); + } + return parseFloat(color) / 100; + }); + return results; + } -p5.Color.prototype._getMode = function() { - return this.mode; -}; + // Try HSLA pattern matching. + if (colorPatterns.HSL.test(str)) { + // hsl(H,S,L) + results = colorPatterns.HSL.exec(str) + .slice(1) + .map((color, idx) => { + if (idx === 0) { + return parseInt(color, 10) / 360; + } + return parseInt(color, 10) / 100; + }); + results[3] = 1; + } else if (colorPatterns.HSLA.test(str)) { + // hsla(H,S,L,A) + results = colorPatterns.HSLA.exec(str) + .slice(1) + .map((color, idx) => { + if (idx === 0) { + return parseInt(color, 10) / 360; + } else if (idx === 3) { + return parseFloat(color); + } + return parseInt(color, 10) / 100; + }); + } + results = results.map(value => Math.max(Math.min(value, 1), 0)); + if (results.length) { + return color_conversion._hslaToRGBA(results); + } -p5.Color.prototype._getMaxes = function() { - return this.maxes; -}; + // Try HSBA pattern matching. + if (colorPatterns.HSB.test(str)) { + // hsb(H,S,B) + results = colorPatterns.HSB.exec(str) + .slice(1) + .map((color, idx) => { + if (idx === 0) { + return parseInt(color, 10) / 360; + } + return parseInt(color, 10) / 100; + }); + results[3] = 1; + } else if (colorPatterns.HSBA.test(str)) { + // hsba(H,S,B,A) + results = colorPatterns.HSBA.exec(str) + .slice(1) + .map((color, idx) => { + if (idx === 0) { + return parseInt(color, 10) / 360; + } else if (idx === 3) { + return parseFloat(color); + } + return parseInt(color, 10) / 100; + }); + } -p5.Color.prototype._getBlue = function() { - return this._array[2] * this.maxes[constants.RGB][2]; -}; + if (results.length) { + // (loop backwards for performance) + for (i = results.length - 1; i >= 0; --i) { + results[i] = Math.max(Math.min(results[i], 1), 0); + } -p5.Color.prototype._getBrightness = function() { - if (!this.hsba) { - this.hsba = color_conversion._rgbaToHSBA(this._array); - } - return this.hsba[2] * this.maxes[constants.HSB][2]; -}; + return color_conversion._hsbaToRGBA(results); + } -p5.Color.prototype._getGreen = function() { - return this._array[1] * this.maxes[constants.RGB][1]; -}; + // Input did not match any CSS color pattern: default to white. + results = [1, 1, 1, 1]; + } else if ((numArgs === 1 || numArgs === 2) && typeof r === 'number') { + // 'Grayscale' mode. + + /** + * For HSB and HSL, interpret the gray level as a brightness/lightness + * value (they are equivalent when chroma is zero). For RGB, normalize the + * gray level according to the blue maximum. + */ + results[0] = r / maxes[2]; + results[1] = r / maxes[2]; + results[2] = r / maxes[2]; + + // Alpha may be undefined, so default it to 100%. + if (typeof g === 'number') { + results[3] = g / maxes[3]; + } else { + results[3] = 1; + } -/** - * Hue is the same in HSB and HSL, but the maximum value may be different. - * This function will return the HSB-normalized saturation when supplied with - * an HSB color object, but will default to the HSL-normalized saturation - * otherwise. - */ -p5.Color.prototype._getHue = function() { - if (this.mode === constants.HSB) { - if (!this.hsba) { - this.hsba = color_conversion._rgbaToHSBA(this._array); - } - return this.hsba[0] * this.maxes[constants.HSB][0]; - } else { - if (!this.hsla) { - this.hsla = color_conversion._rgbaToHSLA(this._array); + // Constrain components to the range [0,1]. + results = results.map(value => Math.max(Math.min(value, 1), 0)); + } else { + throw new Error(`${arguments}is not a valid color representation.`); } - return this.hsla[0] * this.maxes[constants.HSL][0]; - } -}; -p5.Color.prototype._getLightness = function() { - if (!this.hsla) { - this.hsla = color_conversion._rgbaToHSLA(this._array); - } - return this.hsla[2] * this.maxes[constants.HSL][2]; -}; - -p5.Color.prototype._getRed = function() { - return this._array[0] * this.maxes[constants.RGB][0]; + return results; + }; }; - -/** - * Saturation is scaled differently in HSB and HSL. This function will return - * the HSB saturation when supplied with an HSB color object, but will default - * to the HSL saturation otherwise. - */ -p5.Color.prototype._getSaturation = function() { - if (this.mode === constants.HSB) { - if (!this.hsba) { - this.hsba = color_conversion._rgbaToHSBA(this._array); - } - return this.hsba[1] * this.maxes[constants.HSB][1]; - } else { - if (!this.hsla) { - this.hsla = color_conversion._rgbaToHSLA(this._array); - } - return this.hsla[1] * this.maxes[constants.HSL][1]; - } -}; - /** * CSS named colors. */ @@ -762,239 +990,4 @@ const colorPatterns = { ) }; -/** - * For a number of different inputs, returns a color formatted as [r, g, b, a] - * arrays, with each component normalized between 0 and 1. - * - * @private - * @param {Array} [...args] An 'array-like' object that represents a list of - * arguments - * @return {Number[]} a color formatted as [r, g, b, a] - * Example: - * input ==> output - * g ==> [g, g, g, 255] - * g,a ==> [g, g, g, a] - * r, g, b ==> [r, g, b, 255] - * r, g, b, a ==> [r, g, b, a] - * [g] ==> [g, g, g, 255] - * [g, a] ==> [g, g, g, a] - * [r, g, b] ==> [r, g, b, 255] - * [r, g, b, a] ==> [r, g, b, a] - * @example - *
- * - * // todo - * // - * // describe(''); - * - *
- */ -p5.Color._parseInputs = function(r, g, b, a) { - const numArgs = arguments.length; - const mode = this.mode; - const maxes = this.maxes[mode]; - let results = []; - let i; - - if (numArgs >= 3) { - // Argument is a list of component values. - - results[0] = r / maxes[0]; - results[1] = g / maxes[1]; - results[2] = b / maxes[2]; - - // Alpha may be undefined, so default it to 100%. - if (typeof a === 'number') { - results[3] = a / maxes[3]; - } else { - results[3] = 1; - } - - // Constrain components to the range [0,1]. - // (loop backwards for performance) - for (i = results.length - 1; i >= 0; --i) { - const result = results[i]; - if (result < 0) { - results[i] = 0; - } else if (result > 1) { - results[i] = 1; - } - } - - // Convert to RGBA and return. - if (mode === constants.HSL) { - return color_conversion._hslaToRGBA(results); - } else if (mode === constants.HSB) { - return color_conversion._hsbaToRGBA(results); - } else { - return results; - } - } else if (numArgs === 1 && typeof r === 'string') { - const str = r.trim().toLowerCase(); - - // Return if string is a named colour. - if (namedColors[str]) { - return p5.Color._parseInputs.call(this, namedColors[str]); - } - - // Try RGBA pattern matching. - if (colorPatterns.HEX3.test(str)) { - // #rgb - results = colorPatterns.HEX3.exec(str) - .slice(1) - .map(color => parseInt(color + color, 16) / 255); - results[3] = 1; - return results; - } else if (colorPatterns.HEX6.test(str)) { - // #rrggbb - results = colorPatterns.HEX6.exec(str) - .slice(1) - .map(color => parseInt(color, 16) / 255); - results[3] = 1; - return results; - } else if (colorPatterns.HEX4.test(str)) { - // #rgba - results = colorPatterns.HEX4.exec(str) - .slice(1) - .map(color => parseInt(color + color, 16) / 255); - return results; - } else if (colorPatterns.HEX8.test(str)) { - // #rrggbbaa - results = colorPatterns.HEX8.exec(str) - .slice(1) - .map(color => parseInt(color, 16) / 255); - return results; - } else if (colorPatterns.RGB.test(str)) { - // rgb(R,G,B) - results = colorPatterns.RGB.exec(str) - .slice(1) - .map(color => color / 255); - results[3] = 1; - return results; - } else if (colorPatterns.RGB_PERCENT.test(str)) { - // rgb(R%,G%,B%) - results = colorPatterns.RGB_PERCENT.exec(str) - .slice(1) - .map(color => parseFloat(color) / 100); - results[3] = 1; - return results; - } else if (colorPatterns.RGBA.test(str)) { - // rgba(R,G,B,A) - results = colorPatterns.RGBA.exec(str) - .slice(1) - .map((color, idx) => { - if (idx === 3) { - return parseFloat(color); - } - return color / 255; - }); - return results; - } else if (colorPatterns.RGBA_PERCENT.test(str)) { - // rgba(R%,G%,B%,A%) - results = colorPatterns.RGBA_PERCENT.exec(str) - .slice(1) - .map((color, idx) => { - if (idx === 3) { - return parseFloat(color); - } - return parseFloat(color) / 100; - }); - return results; - } - - // Try HSLA pattern matching. - if (colorPatterns.HSL.test(str)) { - // hsl(H,S,L) - results = colorPatterns.HSL.exec(str) - .slice(1) - .map((color, idx) => { - if (idx === 0) { - return parseInt(color, 10) / 360; - } - return parseInt(color, 10) / 100; - }); - results[3] = 1; - } else if (colorPatterns.HSLA.test(str)) { - // hsla(H,S,L,A) - results = colorPatterns.HSLA.exec(str) - .slice(1) - .map((color, idx) => { - if (idx === 0) { - return parseInt(color, 10) / 360; - } else if (idx === 3) { - return parseFloat(color); - } - return parseInt(color, 10) / 100; - }); - } - results = results.map(value => Math.max(Math.min(value, 1), 0)); - if (results.length) { - return color_conversion._hslaToRGBA(results); - } - - // Try HSBA pattern matching. - if (colorPatterns.HSB.test(str)) { - // hsb(H,S,B) - results = colorPatterns.HSB.exec(str) - .slice(1) - .map((color, idx) => { - if (idx === 0) { - return parseInt(color, 10) / 360; - } - return parseInt(color, 10) / 100; - }); - results[3] = 1; - } else if (colorPatterns.HSBA.test(str)) { - // hsba(H,S,B,A) - results = colorPatterns.HSBA.exec(str) - .slice(1) - .map((color, idx) => { - if (idx === 0) { - return parseInt(color, 10) / 360; - } else if (idx === 3) { - return parseFloat(color); - } - return parseInt(color, 10) / 100; - }); - } - - if (results.length) { - // (loop backwards for performance) - for (i = results.length - 1; i >= 0; --i) { - results[i] = Math.max(Math.min(results[i], 1), 0); - } - - return color_conversion._hsbaToRGBA(results); - } - - // Input did not match any CSS color pattern: default to white. - results = [1, 1, 1, 1]; - } else if ((numArgs === 1 || numArgs === 2) && typeof r === 'number') { - // 'Grayscale' mode. - - /** - * For HSB and HSL, interpret the gray level as a brightness/lightness - * value (they are equivalent when chroma is zero). For RGB, normalize the - * gray level according to the blue maximum. - */ - results[0] = r / maxes[2]; - results[1] = r / maxes[2]; - results[2] = r / maxes[2]; - - // Alpha may be undefined, so default it to 100%. - if (typeof g === 'number') { - results[3] = g / maxes[3]; - } else { - results[3] = 1; - } - - // Constrain components to the range [0,1]. - results = results.map(value => Math.max(Math.min(value, 1), 0)); - } else { - throw new Error(`${arguments}is not a valid color representation.`); - } - - return results; -}; - export default p5.Color; From cfc1f5831b58b555bbd6c19bc19254869f2f17db Mon Sep 17 00:00:00 2001 From: asukaminato Date: Wed, 31 May 2023 01:45:18 +0900 Subject: [PATCH 15/22] fix ci --- src/color/p5.Color.js | 1043 +++++++++++++++++++++-------------------- 1 file changed, 522 insertions(+), 521 deletions(-) diff --git a/src/color/p5.Color.js b/src/color/p5.Color.js index 4375afb0be..b6615ec7c0 100644 --- a/src/color/p5.Color.js +++ b/src/color/p5.Color.js @@ -11,6 +11,310 @@ import p5 from '../core/main'; import * as constants from '../core/constants'; import color_conversion from './color_conversion'; +/** + * CSS named colors. + */ +const namedColors = { + aliceblue: '#f0f8ff', + antiquewhite: '#faebd7', + aqua: '#00ffff', + aquamarine: '#7fffd4', + azure: '#f0ffff', + beige: '#f5f5dc', + bisque: '#ffe4c4', + black: '#000000', + blanchedalmond: '#ffebcd', + blue: '#0000ff', + blueviolet: '#8a2be2', + brown: '#a52a2a', + burlywood: '#deb887', + cadetblue: '#5f9ea0', + chartreuse: '#7fff00', + chocolate: '#d2691e', + coral: '#ff7f50', + cornflowerblue: '#6495ed', + cornsilk: '#fff8dc', + crimson: '#dc143c', + cyan: '#00ffff', + darkblue: '#00008b', + darkcyan: '#008b8b', + darkgoldenrod: '#b8860b', + darkgray: '#a9a9a9', + darkgreen: '#006400', + darkgrey: '#a9a9a9', + darkkhaki: '#bdb76b', + darkmagenta: '#8b008b', + darkolivegreen: '#556b2f', + darkorange: '#ff8c00', + darkorchid: '#9932cc', + darkred: '#8b0000', + darksalmon: '#e9967a', + darkseagreen: '#8fbc8f', + darkslateblue: '#483d8b', + darkslategray: '#2f4f4f', + darkslategrey: '#2f4f4f', + darkturquoise: '#00ced1', + darkviolet: '#9400d3', + deeppink: '#ff1493', + deepskyblue: '#00bfff', + dimgray: '#696969', + dimgrey: '#696969', + dodgerblue: '#1e90ff', + firebrick: '#b22222', + floralwhite: '#fffaf0', + forestgreen: '#228b22', + fuchsia: '#ff00ff', + gainsboro: '#dcdcdc', + ghostwhite: '#f8f8ff', + gold: '#ffd700', + goldenrod: '#daa520', + gray: '#808080', + green: '#008000', + greenyellow: '#adff2f', + grey: '#808080', + honeydew: '#f0fff0', + hotpink: '#ff69b4', + indianred: '#cd5c5c', + indigo: '#4b0082', + ivory: '#fffff0', + khaki: '#f0e68c', + lavender: '#e6e6fa', + lavenderblush: '#fff0f5', + lawngreen: '#7cfc00', + lemonchiffon: '#fffacd', + lightblue: '#add8e6', + lightcoral: '#f08080', + lightcyan: '#e0ffff', + lightgoldenrodyellow: '#fafad2', + lightgray: '#d3d3d3', + lightgreen: '#90ee90', + lightgrey: '#d3d3d3', + lightpink: '#ffb6c1', + lightsalmon: '#ffa07a', + lightseagreen: '#20b2aa', + lightskyblue: '#87cefa', + lightslategray: '#778899', + lightslategrey: '#778899', + lightsteelblue: '#b0c4de', + lightyellow: '#ffffe0', + lime: '#00ff00', + limegreen: '#32cd32', + linen: '#faf0e6', + magenta: '#ff00ff', + maroon: '#800000', + mediumaquamarine: '#66cdaa', + mediumblue: '#0000cd', + mediumorchid: '#ba55d3', + mediumpurple: '#9370db', + mediumseagreen: '#3cb371', + mediumslateblue: '#7b68ee', + mediumspringgreen: '#00fa9a', + mediumturquoise: '#48d1cc', + mediumvioletred: '#c71585', + midnightblue: '#191970', + mintcream: '#f5fffa', + mistyrose: '#ffe4e1', + moccasin: '#ffe4b5', + navajowhite: '#ffdead', + navy: '#000080', + oldlace: '#fdf5e6', + olive: '#808000', + olivedrab: '#6b8e23', + orange: '#ffa500', + orangered: '#ff4500', + orchid: '#da70d6', + palegoldenrod: '#eee8aa', + palegreen: '#98fb98', + paleturquoise: '#afeeee', + palevioletred: '#db7093', + papayawhip: '#ffefd5', + peachpuff: '#ffdab9', + peru: '#cd853f', + pink: '#ffc0cb', + plum: '#dda0dd', + powderblue: '#b0e0e6', + purple: '#800080', + rebeccapurple: '#663399', + red: '#ff0000', + rosybrown: '#bc8f8f', + royalblue: '#4169e1', + saddlebrown: '#8b4513', + salmon: '#fa8072', + sandybrown: '#f4a460', + seagreen: '#2e8b57', + seashell: '#fff5ee', + sienna: '#a0522d', + silver: '#c0c0c0', + skyblue: '#87ceeb', + slateblue: '#6a5acd', + slategray: '#708090', + slategrey: '#708090', + snow: '#fffafa', + springgreen: '#00ff7f', + steelblue: '#4682b4', + tan: '#d2b48c', + teal: '#008080', + thistle: '#d8bfd8', + tomato: '#ff6347', + turquoise: '#40e0d0', + violet: '#ee82ee', + wheat: '#f5deb3', + white: '#ffffff', + whitesmoke: '#f5f5f5', + yellow: '#ffff00', + yellowgreen: '#9acd32' +}; + +/** + * These regular expressions are used to build up the patterns for matching + * viable CSS color strings: fragmenting the regexes in this way increases the + * legibility and comprehensibility of the code. + * + * Note that RGB values of .9 are not parsed by IE, but are supported here for + * color string consistency. + */ +const WHITESPACE = /\s*/; // Match zero or more whitespace characters. +const INTEGER = /(\d{1,3})/; // Match integers: 79, 255, etc. +const DECIMAL = /((?:\d+(?:\.\d+)?)|(?:\.\d+))/; // Match 129.6, 79, .9, etc. +const PERCENT = new RegExp(`${DECIMAL.source}%`); // Match 12.9%, 79%, .9%, etc. + +/** + * Full color string patterns. The capture groups are necessary. + */ +const colorPatterns = { + // Match colors in format #XXX, e.g. #416. + HEX3: /^#([a-f0-9])([a-f0-9])([a-f0-9])$/i, + + // Match colors in format #XXXX, e.g. #5123. + HEX4: /^#([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])$/i, + + // Match colors in format #XXXXXX, e.g. #b4d455. + HEX6: /^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i, + + // Match colors in format #XXXXXXXX, e.g. #b4d45535. + HEX8: /^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i, + + // Match colors in format rgb(R, G, B), e.g. rgb(255, 0, 128). + RGB: new RegExp( + [ + '^rgb\\(', + INTEGER.source, + ',', + INTEGER.source, + ',', + INTEGER.source, + '\\)$' + ].join(WHITESPACE.source), + 'i' + ), + + // Match colors in format rgb(R%, G%, B%), e.g. rgb(100%, 0%, 28.9%). + RGB_PERCENT: new RegExp( + [ + '^rgb\\(', + PERCENT.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + '\\)$' + ].join(WHITESPACE.source), + 'i' + ), + + // Match colors in format rgb(R, G, B, A), e.g. rgb(255, 0, 128, 0.25). + RGBA: new RegExp( + [ + '^rgba\\(', + INTEGER.source, + ',', + INTEGER.source, + ',', + INTEGER.source, + ',', + DECIMAL.source, + '\\)$' + ].join(WHITESPACE.source), + 'i' + ), + + // Match colors in format rgb(R%, G%, B%, A), e.g. rgb(100%, 0%, 28.9%, 0.5). + RGBA_PERCENT: new RegExp( + [ + '^rgba\\(', + PERCENT.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + ',', + DECIMAL.source, + '\\)$' + ].join(WHITESPACE.source), + 'i' + ), + + // Match colors in format hsla(H, S%, L%), e.g. hsl(100, 40%, 28.9%). + HSL: new RegExp( + [ + '^hsl\\(', + INTEGER.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + '\\)$' + ].join(WHITESPACE.source), + 'i' + ), + + // Match colors in format hsla(H, S%, L%, A), e.g. hsla(100, 40%, 28.9%, 0.5). + HSLA: new RegExp( + [ + '^hsla\\(', + INTEGER.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + ',', + DECIMAL.source, + '\\)$' + ].join(WHITESPACE.source), + 'i' + ), + + // Match colors in format hsb(H, S%, B%), e.g. hsb(100, 40%, 28.9%). + HSB: new RegExp( + [ + '^hsb\\(', + INTEGER.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + '\\)$' + ].join(WHITESPACE.source), + 'i' + ), + + // Match colors in format hsba(H, S%, B%, A), e.g. hsba(100, 40%, 28.9%, 0.5). + HSBA: new RegExp( + [ + '^hsba\\(', + INTEGER.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + ',', + DECIMAL.source, + '\\)$' + ].join(WHITESPACE.source), + 'i' + ) +}; + /** * Each color stores the color mode and level maxes that were applied at the * time of its construction. These are used to interpret the input arguments @@ -254,7 +558,7 @@ p5.Color = class Color { default: return 'rgba('.concat(a[0], ',', a[1], ',', a[2], ',', alpha, ')'); } - }; + } /** * The setRed method sets the red component of a color. @@ -281,7 +585,7 @@ p5.Color = class Color { setRed(new_red) { this._array[0] = new_red / this.maxes[constants.RGB][0]; this._calculateLevels(); - }; + } /** * The setGreen method sets the green component of a color. @@ -304,7 +608,7 @@ p5.Color = class Color { setGreen(new_green) { this._array[1] = new_green / this.maxes[constants.RGB][1]; this._calculateLevels(); - }; + } /** * The setBlue method sets the blue component of a color. @@ -327,7 +631,7 @@ p5.Color = class Color { setBlue(new_blue) { this._array[2] = new_blue / this.maxes[constants.RGB][2]; this._calculateLevels(); - }; + } /** * The setAlpha method sets the transparency (alpha) value of a color. @@ -352,7 +656,7 @@ p5.Color = class Color { setAlpha(new_alpha) { this._array[3] = new_alpha / this.maxes[this.mode][3]; this._calculateLevels(); - }; + } // calculates and stores the closest screen levels _calculateLevels() { @@ -366,41 +670,41 @@ p5.Color = class Color { // Clear cached HSL/HSB values this.hsla = null; this.hsba = null; - }; + } _getAlpha() { return this._array[3] * this.maxes[this.mode][3]; - }; + } // stores the color mode and maxes in this instance of Color // for later use (by _parseInputs()) _storeModeAndMaxes(new_mode, new_maxes) { this.mode = new_mode; this.maxes = new_maxes; - }; + } _getMode() { return this.mode; - }; + } _getMaxes() { return this.maxes; - }; + } _getBlue() { return this._array[2] * this.maxes[constants.RGB][2]; - }; + } _getBrightness() { if (!this.hsba) { this.hsba = color_conversion._rgbaToHSBA(this._array); } return this.hsba[2] * this.maxes[constants.HSB][2]; - }; + } _getGreen() { return this._array[1] * this.maxes[constants.RGB][1]; - }; + } /** * Hue is the same in HSB and HSL, but the maximum value may be different. @@ -420,18 +724,18 @@ p5.Color = class Color { } return this.hsla[0] * this.maxes[constants.HSL][0]; } - }; + } _getLightness() { if (!this.hsla) { this.hsla = color_conversion._rgbaToHSLA(this._array); } return this.hsla[2] * this.maxes[constants.HSL][2]; - }; + } _getRed() { return this._array[0] * this.maxes[constants.RGB][0]; - }; + } /** * Saturation is scaled differently in HSB and HSL. This function will return @@ -450,7 +754,7 @@ p5.Color = class Color { } return this.hsla[1] * this.maxes[constants.HSL][1]; } - }; + } /** * For a number of different inputs, returns a color formatted as [r, g, b, a] * arrays, with each component normalized between 0 and 1. @@ -463,531 +767,228 @@ p5.Color = class Color { * input ==> output * g ==> [g, g, g, 255] * g,a ==> [g, g, g, a] - * r, g, b ==> [r, g, b, 255] - * r, g, b, a ==> [r, g, b, a] - * [g] ==> [g, g, g, 255] - * [g, a] ==> [g, g, g, a] - * [r, g, b] ==> [r, g, b, 255] - * [r, g, b, a] ==> [r, g, b, a] - * @example - *
- * - * // todo - * // - * // describe(''); - * - *
- */ - static _parseInputs(r, g, b, a) { - const numArgs = arguments.length; - const mode = this.mode; - const maxes = this.maxes[mode]; - let results = []; - let i; - - if (numArgs >= 3) { - // Argument is a list of component values. - - results[0] = r / maxes[0]; - results[1] = g / maxes[1]; - results[2] = b / maxes[2]; - - // Alpha may be undefined, so default it to 100%. - if (typeof a === 'number') { - results[3] = a / maxes[3]; - } else { - results[3] = 1; - } - - // Constrain components to the range [0,1]. - // (loop backwards for performance) - for (i = results.length - 1; i >= 0; --i) { - const result = results[i]; - if (result < 0) { - results[i] = 0; - } else if (result > 1) { - results[i] = 1; - } - } - - // Convert to RGBA and return. - if (mode === constants.HSL) { - return color_conversion._hslaToRGBA(results); - } else if (mode === constants.HSB) { - return color_conversion._hsbaToRGBA(results); - } else { - return results; - } - } else if (numArgs === 1 && typeof r === 'string') { - const str = r.trim().toLowerCase(); - - // Return if string is a named colour. - if (namedColors[str]) { - return Color._parseInputs(namedColors[str]); - } - - // Try RGBA pattern matching. - if (colorPatterns.HEX3.test(str)) { - // #rgb - results = colorPatterns.HEX3.exec(str) - .slice(1) - .map(color => parseInt(color + color, 16) / 255); - results[3] = 1; - return results; - } else if (colorPatterns.HEX6.test(str)) { - // #rrggbb - results = colorPatterns.HEX6.exec(str) - .slice(1) - .map(color => parseInt(color, 16) / 255); - results[3] = 1; - return results; - } else if (colorPatterns.HEX4.test(str)) { - // #rgba - results = colorPatterns.HEX4.exec(str) - .slice(1) - .map(color => parseInt(color + color, 16) / 255); - return results; - } else if (colorPatterns.HEX8.test(str)) { - // #rrggbbaa - results = colorPatterns.HEX8.exec(str) - .slice(1) - .map(color => parseInt(color, 16) / 255); - return results; - } else if (colorPatterns.RGB.test(str)) { - // rgb(R,G,B) - results = colorPatterns.RGB.exec(str) - .slice(1) - .map(color => color / 255); - results[3] = 1; - return results; - } else if (colorPatterns.RGB_PERCENT.test(str)) { - // rgb(R%,G%,B%) - results = colorPatterns.RGB_PERCENT.exec(str) - .slice(1) - .map(color => parseFloat(color) / 100); - results[3] = 1; - return results; - } else if (colorPatterns.RGBA.test(str)) { - // rgba(R,G,B,A) - results = colorPatterns.RGBA.exec(str) - .slice(1) - .map((color, idx) => { - if (idx === 3) { - return parseFloat(color); - } - return color / 255; - }); - return results; - } else if (colorPatterns.RGBA_PERCENT.test(str)) { - // rgba(R%,G%,B%,A%) - results = colorPatterns.RGBA_PERCENT.exec(str) - .slice(1) - .map((color, idx) => { - if (idx === 3) { - return parseFloat(color); - } - return parseFloat(color) / 100; - }); - return results; - } - - // Try HSLA pattern matching. - if (colorPatterns.HSL.test(str)) { - // hsl(H,S,L) - results = colorPatterns.HSL.exec(str) - .slice(1) - .map((color, idx) => { - if (idx === 0) { - return parseInt(color, 10) / 360; - } - return parseInt(color, 10) / 100; - }); - results[3] = 1; - } else if (colorPatterns.HSLA.test(str)) { - // hsla(H,S,L,A) - results = colorPatterns.HSLA.exec(str) - .slice(1) - .map((color, idx) => { - if (idx === 0) { - return parseInt(color, 10) / 360; - } else if (idx === 3) { - return parseFloat(color); - } - return parseInt(color, 10) / 100; - }); - } - results = results.map(value => Math.max(Math.min(value, 1), 0)); - if (results.length) { - return color_conversion._hslaToRGBA(results); - } - - // Try HSBA pattern matching. - if (colorPatterns.HSB.test(str)) { - // hsb(H,S,B) - results = colorPatterns.HSB.exec(str) - .slice(1) - .map((color, idx) => { - if (idx === 0) { - return parseInt(color, 10) / 360; - } - return parseInt(color, 10) / 100; - }); - results[3] = 1; - } else if (colorPatterns.HSBA.test(str)) { - // hsba(H,S,B,A) - results = colorPatterns.HSBA.exec(str) - .slice(1) - .map((color, idx) => { - if (idx === 0) { - return parseInt(color, 10) / 360; - } else if (idx === 3) { - return parseFloat(color); - } - return parseInt(color, 10) / 100; - }); - } - - if (results.length) { - // (loop backwards for performance) - for (i = results.length - 1; i >= 0; --i) { - results[i] = Math.max(Math.min(results[i], 1), 0); - } - - return color_conversion._hsbaToRGBA(results); - } + * r, g, b ==> [r, g, b, 255] + * r, g, b, a ==> [r, g, b, a] + * [g] ==> [g, g, g, 255] + * [g, a] ==> [g, g, g, a] + * [r, g, b] ==> [r, g, b, 255] + * [r, g, b, a] ==> [r, g, b, a] + * @example + *
+ * + * // todo + * // + * // describe(''); + * + *
+ */ + static _parseInputs(r, g, b, a) { + const numArgs = arguments.length; + const mode = this.mode; + const maxes = this.maxes[mode]; + let results = []; + let i; - // Input did not match any CSS color pattern: default to white. - results = [1, 1, 1, 1]; - } else if ((numArgs === 1 || numArgs === 2) && typeof r === 'number') { - // 'Grayscale' mode. + if (numArgs >= 3) { + // Argument is a list of component values. - /** - * For HSB and HSL, interpret the gray level as a brightness/lightness - * value (they are equivalent when chroma is zero). For RGB, normalize the - * gray level according to the blue maximum. - */ - results[0] = r / maxes[2]; - results[1] = r / maxes[2]; - results[2] = r / maxes[2]; + results[0] = r / maxes[0]; + results[1] = g / maxes[1]; + results[2] = b / maxes[2]; // Alpha may be undefined, so default it to 100%. - if (typeof g === 'number') { - results[3] = g / maxes[3]; + if (typeof a === 'number') { + results[3] = a / maxes[3]; } else { results[3] = 1; } // Constrain components to the range [0,1]. - results = results.map(value => Math.max(Math.min(value, 1), 0)); - } else { - throw new Error(`${arguments}is not a valid color representation.`); - } - - return results; - }; -}; -/** - * CSS named colors. - */ -const namedColors = { - aliceblue: '#f0f8ff', - antiquewhite: '#faebd7', - aqua: '#00ffff', - aquamarine: '#7fffd4', - azure: '#f0ffff', - beige: '#f5f5dc', - bisque: '#ffe4c4', - black: '#000000', - blanchedalmond: '#ffebcd', - blue: '#0000ff', - blueviolet: '#8a2be2', - brown: '#a52a2a', - burlywood: '#deb887', - cadetblue: '#5f9ea0', - chartreuse: '#7fff00', - chocolate: '#d2691e', - coral: '#ff7f50', - cornflowerblue: '#6495ed', - cornsilk: '#fff8dc', - crimson: '#dc143c', - cyan: '#00ffff', - darkblue: '#00008b', - darkcyan: '#008b8b', - darkgoldenrod: '#b8860b', - darkgray: '#a9a9a9', - darkgreen: '#006400', - darkgrey: '#a9a9a9', - darkkhaki: '#bdb76b', - darkmagenta: '#8b008b', - darkolivegreen: '#556b2f', - darkorange: '#ff8c00', - darkorchid: '#9932cc', - darkred: '#8b0000', - darksalmon: '#e9967a', - darkseagreen: '#8fbc8f', - darkslateblue: '#483d8b', - darkslategray: '#2f4f4f', - darkslategrey: '#2f4f4f', - darkturquoise: '#00ced1', - darkviolet: '#9400d3', - deeppink: '#ff1493', - deepskyblue: '#00bfff', - dimgray: '#696969', - dimgrey: '#696969', - dodgerblue: '#1e90ff', - firebrick: '#b22222', - floralwhite: '#fffaf0', - forestgreen: '#228b22', - fuchsia: '#ff00ff', - gainsboro: '#dcdcdc', - ghostwhite: '#f8f8ff', - gold: '#ffd700', - goldenrod: '#daa520', - gray: '#808080', - green: '#008000', - greenyellow: '#adff2f', - grey: '#808080', - honeydew: '#f0fff0', - hotpink: '#ff69b4', - indianred: '#cd5c5c', - indigo: '#4b0082', - ivory: '#fffff0', - khaki: '#f0e68c', - lavender: '#e6e6fa', - lavenderblush: '#fff0f5', - lawngreen: '#7cfc00', - lemonchiffon: '#fffacd', - lightblue: '#add8e6', - lightcoral: '#f08080', - lightcyan: '#e0ffff', - lightgoldenrodyellow: '#fafad2', - lightgray: '#d3d3d3', - lightgreen: '#90ee90', - lightgrey: '#d3d3d3', - lightpink: '#ffb6c1', - lightsalmon: '#ffa07a', - lightseagreen: '#20b2aa', - lightskyblue: '#87cefa', - lightslategray: '#778899', - lightslategrey: '#778899', - lightsteelblue: '#b0c4de', - lightyellow: '#ffffe0', - lime: '#00ff00', - limegreen: '#32cd32', - linen: '#faf0e6', - magenta: '#ff00ff', - maroon: '#800000', - mediumaquamarine: '#66cdaa', - mediumblue: '#0000cd', - mediumorchid: '#ba55d3', - mediumpurple: '#9370db', - mediumseagreen: '#3cb371', - mediumslateblue: '#7b68ee', - mediumspringgreen: '#00fa9a', - mediumturquoise: '#48d1cc', - mediumvioletred: '#c71585', - midnightblue: '#191970', - mintcream: '#f5fffa', - mistyrose: '#ffe4e1', - moccasin: '#ffe4b5', - navajowhite: '#ffdead', - navy: '#000080', - oldlace: '#fdf5e6', - olive: '#808000', - olivedrab: '#6b8e23', - orange: '#ffa500', - orangered: '#ff4500', - orchid: '#da70d6', - palegoldenrod: '#eee8aa', - palegreen: '#98fb98', - paleturquoise: '#afeeee', - palevioletred: '#db7093', - papayawhip: '#ffefd5', - peachpuff: '#ffdab9', - peru: '#cd853f', - pink: '#ffc0cb', - plum: '#dda0dd', - powderblue: '#b0e0e6', - purple: '#800080', - rebeccapurple: '#663399', - red: '#ff0000', - rosybrown: '#bc8f8f', - royalblue: '#4169e1', - saddlebrown: '#8b4513', - salmon: '#fa8072', - sandybrown: '#f4a460', - seagreen: '#2e8b57', - seashell: '#fff5ee', - sienna: '#a0522d', - silver: '#c0c0c0', - skyblue: '#87ceeb', - slateblue: '#6a5acd', - slategray: '#708090', - slategrey: '#708090', - snow: '#fffafa', - springgreen: '#00ff7f', - steelblue: '#4682b4', - tan: '#d2b48c', - teal: '#008080', - thistle: '#d8bfd8', - tomato: '#ff6347', - turquoise: '#40e0d0', - violet: '#ee82ee', - wheat: '#f5deb3', - white: '#ffffff', - whitesmoke: '#f5f5f5', - yellow: '#ffff00', - yellowgreen: '#9acd32' -}; - -/** - * These regular expressions are used to build up the patterns for matching - * viable CSS color strings: fragmenting the regexes in this way increases the - * legibility and comprehensibility of the code. - * - * Note that RGB values of .9 are not parsed by IE, but are supported here for - * color string consistency. - */ -const WHITESPACE = /\s*/; // Match zero or more whitespace characters. -const INTEGER = /(\d{1,3})/; // Match integers: 79, 255, etc. -const DECIMAL = /((?:\d+(?:\.\d+)?)|(?:\.\d+))/; // Match 129.6, 79, .9, etc. -const PERCENT = new RegExp(`${DECIMAL.source}%`); // Match 12.9%, 79%, .9%, etc. + // (loop backwards for performance) + for (i = results.length - 1; i >= 0; --i) { + const result = results[i]; + if (result < 0) { + results[i] = 0; + } else if (result > 1) { + results[i] = 1; + } + } -/** - * Full color string patterns. The capture groups are necessary. - */ -const colorPatterns = { - // Match colors in format #XXX, e.g. #416. - HEX3: /^#([a-f0-9])([a-f0-9])([a-f0-9])$/i, + // Convert to RGBA and return. + if (mode === constants.HSL) { + return color_conversion._hslaToRGBA(results); + } else if (mode === constants.HSB) { + return color_conversion._hsbaToRGBA(results); + } else { + return results; + } + } else if (numArgs === 1 && typeof r === 'string') { + const str = r.trim().toLowerCase(); - // Match colors in format #XXXX, e.g. #5123. - HEX4: /^#([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])$/i, + // Return if string is a named colour. + if (namedColors[str]) { + return Color._parseInputs(namedColors[str]); + } - // Match colors in format #XXXXXX, e.g. #b4d455. - HEX6: /^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i, + // Try RGBA pattern matching. + if (colorPatterns.HEX3.test(str)) { + // #rgb + results = colorPatterns.HEX3.exec(str) + .slice(1) + .map(color => parseInt(color + color, 16) / 255); + results[3] = 1; + return results; + } else if (colorPatterns.HEX6.test(str)) { + // #rrggbb + results = colorPatterns.HEX6.exec(str) + .slice(1) + .map(color => parseInt(color, 16) / 255); + results[3] = 1; + return results; + } else if (colorPatterns.HEX4.test(str)) { + // #rgba + results = colorPatterns.HEX4.exec(str) + .slice(1) + .map(color => parseInt(color + color, 16) / 255); + return results; + } else if (colorPatterns.HEX8.test(str)) { + // #rrggbbaa + results = colorPatterns.HEX8.exec(str) + .slice(1) + .map(color => parseInt(color, 16) / 255); + return results; + } else if (colorPatterns.RGB.test(str)) { + // rgb(R,G,B) + results = colorPatterns.RGB.exec(str) + .slice(1) + .map(color => color / 255); + results[3] = 1; + return results; + } else if (colorPatterns.RGB_PERCENT.test(str)) { + // rgb(R%,G%,B%) + results = colorPatterns.RGB_PERCENT.exec(str) + .slice(1) + .map(color => parseFloat(color) / 100); + results[3] = 1; + return results; + } else if (colorPatterns.RGBA.test(str)) { + // rgba(R,G,B,A) + results = colorPatterns.RGBA.exec(str) + .slice(1) + .map((color, idx) => { + if (idx === 3) { + return parseFloat(color); + } + return color / 255; + }); + return results; + } else if (colorPatterns.RGBA_PERCENT.test(str)) { + // rgba(R%,G%,B%,A%) + results = colorPatterns.RGBA_PERCENT.exec(str) + .slice(1) + .map((color, idx) => { + if (idx === 3) { + return parseFloat(color); + } + return parseFloat(color) / 100; + }); + return results; + } - // Match colors in format #XXXXXXXX, e.g. #b4d45535. - HEX8: /^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i, + // Try HSLA pattern matching. + if (colorPatterns.HSL.test(str)) { + // hsl(H,S,L) + results = colorPatterns.HSL.exec(str) + .slice(1) + .map((color, idx) => { + if (idx === 0) { + return parseInt(color, 10) / 360; + } + return parseInt(color, 10) / 100; + }); + results[3] = 1; + } else if (colorPatterns.HSLA.test(str)) { + // hsla(H,S,L,A) + results = colorPatterns.HSLA.exec(str) + .slice(1) + .map((color, idx) => { + if (idx === 0) { + return parseInt(color, 10) / 360; + } else if (idx === 3) { + return parseFloat(color); + } + return parseInt(color, 10) / 100; + }); + } + results = results.map(value => Math.max(Math.min(value, 1), 0)); + if (results.length) { + return color_conversion._hslaToRGBA(results); + } - // Match colors in format rgb(R, G, B), e.g. rgb(255, 0, 128). - RGB: new RegExp( - [ - '^rgb\\(', - INTEGER.source, - ',', - INTEGER.source, - ',', - INTEGER.source, - '\\)$' - ].join(WHITESPACE.source), - 'i' - ), + // Try HSBA pattern matching. + if (colorPatterns.HSB.test(str)) { + // hsb(H,S,B) + results = colorPatterns.HSB.exec(str) + .slice(1) + .map((color, idx) => { + if (idx === 0) { + return parseInt(color, 10) / 360; + } + return parseInt(color, 10) / 100; + }); + results[3] = 1; + } else if (colorPatterns.HSBA.test(str)) { + // hsba(H,S,B,A) + results = colorPatterns.HSBA.exec(str) + .slice(1) + .map((color, idx) => { + if (idx === 0) { + return parseInt(color, 10) / 360; + } else if (idx === 3) { + return parseFloat(color); + } + return parseInt(color, 10) / 100; + }); + } - // Match colors in format rgb(R%, G%, B%), e.g. rgb(100%, 0%, 28.9%). - RGB_PERCENT: new RegExp( - [ - '^rgb\\(', - PERCENT.source, - ',', - PERCENT.source, - ',', - PERCENT.source, - '\\)$' - ].join(WHITESPACE.source), - 'i' - ), + if (results.length) { + // (loop backwards for performance) + for (i = results.length - 1; i >= 0; --i) { + results[i] = Math.max(Math.min(results[i], 1), 0); + } - // Match colors in format rgb(R, G, B, A), e.g. rgb(255, 0, 128, 0.25). - RGBA: new RegExp( - [ - '^rgba\\(', - INTEGER.source, - ',', - INTEGER.source, - ',', - INTEGER.source, - ',', - DECIMAL.source, - '\\)$' - ].join(WHITESPACE.source), - 'i' - ), + return color_conversion._hsbaToRGBA(results); + } - // Match colors in format rgb(R%, G%, B%, A), e.g. rgb(100%, 0%, 28.9%, 0.5). - RGBA_PERCENT: new RegExp( - [ - '^rgba\\(', - PERCENT.source, - ',', - PERCENT.source, - ',', - PERCENT.source, - ',', - DECIMAL.source, - '\\)$' - ].join(WHITESPACE.source), - 'i' - ), + // Input did not match any CSS color pattern: default to white. + results = [1, 1, 1, 1]; + } else if ((numArgs === 1 || numArgs === 2) && typeof r === 'number') { + // 'Grayscale' mode. - // Match colors in format hsla(H, S%, L%), e.g. hsl(100, 40%, 28.9%). - HSL: new RegExp( - [ - '^hsl\\(', - INTEGER.source, - ',', - PERCENT.source, - ',', - PERCENT.source, - '\\)$' - ].join(WHITESPACE.source), - 'i' - ), + /** + * For HSB and HSL, interpret the gray level as a brightness/lightness + * value (they are equivalent when chroma is zero). For RGB, normalize the + * gray level according to the blue maximum. + */ + results[0] = r / maxes[2]; + results[1] = r / maxes[2]; + results[2] = r / maxes[2]; - // Match colors in format hsla(H, S%, L%, A), e.g. hsla(100, 40%, 28.9%, 0.5). - HSLA: new RegExp( - [ - '^hsla\\(', - INTEGER.source, - ',', - PERCENT.source, - ',', - PERCENT.source, - ',', - DECIMAL.source, - '\\)$' - ].join(WHITESPACE.source), - 'i' - ), + // Alpha may be undefined, so default it to 100%. + if (typeof g === 'number') { + results[3] = g / maxes[3]; + } else { + results[3] = 1; + } - // Match colors in format hsb(H, S%, B%), e.g. hsb(100, 40%, 28.9%). - HSB: new RegExp( - [ - '^hsb\\(', - INTEGER.source, - ',', - PERCENT.source, - ',', - PERCENT.source, - '\\)$' - ].join(WHITESPACE.source), - 'i' - ), + // Constrain components to the range [0,1]. + results = results.map(value => Math.max(Math.min(value, 1), 0)); + } else { + throw new Error(`${arguments}is not a valid color representation.`); + } - // Match colors in format hsba(H, S%, B%, A), e.g. hsba(100, 40%, 28.9%, 0.5). - HSBA: new RegExp( - [ - '^hsba\\(', - INTEGER.source, - ',', - PERCENT.source, - ',', - PERCENT.source, - ',', - DECIMAL.source, - '\\)$' - ].join(WHITESPACE.source), - 'i' - ) + return results; + } }; export default p5.Color; From cd1094a46172e1af7552e4ca42398c107c6688f8 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Wed, 31 May 2023 01:56:36 +0900 Subject: [PATCH 16/22] try fix ci --- src/color/p5.Color.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/color/p5.Color.js b/src/color/p5.Color.js index b6615ec7c0..717897707d 100644 --- a/src/color/p5.Color.js +++ b/src/color/p5.Color.js @@ -350,7 +350,7 @@ p5.Color = class Color { if (![constants.RGB, constants.HSL, constants.HSB].includes(this.mode)) { throw new Error(`${this.mode} is an invalid colorMode.`); } else { - this._array = Color._parseInputs(...vals); + this._array = Color._parseInputs.apply(this, vals); } // Expose closest screen color. @@ -827,7 +827,7 @@ p5.Color = class Color { // Return if string is a named colour. if (namedColors[str]) { - return Color._parseInputs(namedColors[str]); + return Color._parseInputs.call(this, namedColors[str]); } // Try RGBA pattern matching. From b170a2024afa1ae4f230fcff400d9a4a7c692e04 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Wed, 31 May 2023 13:21:18 +0900 Subject: [PATCH 17/22] MediaElement, File to class --- src/dom/dom.js | 461 +++++++++++++++++++++++++------------------------ 1 file changed, 232 insertions(+), 229 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 10c8f0cc59..7e5faadd70 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -54,7 +54,7 @@ import p5 from '../core/main'; * [a, b, c, d, e]; // unused * */ -p5.prototype.select = function(e, p) { +p5.prototype.select = function (e, p) { p5._validateParameters('select', arguments); const container = this._getContainer(p); const res = container.querySelector(e); @@ -110,7 +110,7 @@ p5.prototype.select = function(e, p) { * console.log(a); * */ -p5.prototype.selectAll = function(e, p) { +p5.prototype.selectAll = function (e, p) { p5._validateParameters('selectAll', arguments); const arr = []; const container = this._getContainer(p); @@ -127,7 +127,7 @@ p5.prototype.selectAll = function(e, p) { /** * Helper function for select and selectAll */ -p5.prototype._getContainer = function(p) { +p5.prototype._getContainer = function (p) { let container = document; if (typeof p === 'string') { container = document.querySelector(p) || document; @@ -142,11 +142,11 @@ p5.prototype._getContainer = function(p) { /** * Helper function for getElement and getElements. */ -p5.prototype._wrapElement = function(elt) { +p5.prototype._wrapElement = function (elt) { const children = Array.prototype.slice.call(elt.children); if (elt.tagName === 'INPUT' && elt.type === 'checkbox') { let converted = new p5.Element(elt, this); - converted.checked = function() { + converted.checked = function () { if (arguments.length === 0) { return this.elt.checked; } else if (arguments[0]) { @@ -163,7 +163,7 @@ p5.prototype._wrapElement = function(elt) { return this.createSelect(new p5.Element(elt, this)); } else if ( children.length > 0 && - children.every(function(c) { + children.every(function (c) { return c.tagName === 'INPUT' || c.tagName === 'LABEL'; }) ) { @@ -193,7 +193,7 @@ p5.prototype._wrapElement = function(elt) { * } * */ -p5.prototype.removeElements = function(e) { +p5.prototype.removeElements = function (e) { p5._validateParameters('removeElements', arguments); // el.remove splices from this._elements, so don't mix iteration with it const isNotCanvasElement = el => !(el.elt instanceof HTMLCanvasElement); @@ -263,7 +263,7 @@ p5.prototype.removeElements = function(e) { * @alt * dropdown: pear, kiwi, grape. When selected text "it's a" + selection shown. */ -p5.Element.prototype.changed = function(fxn) { +p5.Element.prototype.changed = function (fxn) { p5.Element._adjustListener('change', fxn, this); return this; }; @@ -301,7 +301,7 @@ p5.Element.prototype.changed = function(fxn) { * @alt * no display. */ -p5.Element.prototype.input = function(fxn) { +p5.Element.prototype.input = function (fxn) { p5.Element._adjustListener('input', fxn, this); return this; }; @@ -332,7 +332,7 @@ function addElement(elt, pInst, media) { * div.position(10, 0); * */ -p5.prototype.createDiv = function(html = '') { +p5.prototype.createDiv = function (html = '') { let elt = document.createElement('div'); elt.innerHTML = html; return addElement(elt, this); @@ -352,7 +352,7 @@ p5.prototype.createDiv = function(html = '') { * p.position(10, 0); * */ -p5.prototype.createP = function(html = '') { +p5.prototype.createP = function (html = '') { let elt = document.createElement('p'); elt.innerHTML = html; return addElement(elt, this); @@ -370,7 +370,7 @@ p5.prototype.createP = function(html = '') { * span.position(0, 0); * */ -p5.prototype.createSpan = function(html = '') { +p5.prototype.createSpan = function (html = '') { let elt = document.createElement('span'); elt.innerHTML = html; return addElement(elt, this); @@ -401,7 +401,7 @@ p5.prototype.createSpan = function(html = '') { * @param {Function} [successCallback] callback to be called once image data is loaded with the p5.Element as argument * @return {p5.Element} pointer to p5.Element holding created node */ -p5.prototype.createImg = function() { +p5.prototype.createImg = function () { p5._validateParameters('createImg', arguments); const elt = document.createElement('img'); const args = arguments; @@ -414,7 +414,7 @@ p5.prototype.createImg = function() { } elt.src = args[0]; self = addElement(elt, this); - elt.addEventListener('load', function() { + elt.addEventListener('load', function () { self.width = elt.offsetWidth || elt.width; self.height = elt.offsetHeight || elt.height; const last = args[args.length - 1]; @@ -438,7 +438,7 @@ p5.prototype.createImg = function() { * a.position(0, 0); * */ -p5.prototype.createA = function(href, html, target) { +p5.prototype.createA = function (href, html, target) { p5._validateParameters('createA', arguments); const elt = document.createElement('a'); elt.href = href; @@ -489,7 +489,7 @@ p5.prototype.createA = function(href, html, target) { * } * */ -p5.prototype.createSlider = function(min, max, value, step) { +p5.prototype.createSlider = function (min, max, value, step) { p5._validateParameters('createSlider', arguments); const elt = document.createElement('input'); elt.type = 'range'; @@ -530,7 +530,7 @@ p5.prototype.createSlider = function(min, max, value, step) { * } * */ -p5.prototype.createButton = function(label, value) { +p5.prototype.createButton = function (label, value) { p5._validateParameters('createButton', arguments); const elt = document.createElement('button'); elt.innerHTML = label; @@ -565,7 +565,7 @@ p5.prototype.createButton = function(label, value) { * } * */ -p5.prototype.createCheckbox = function() { +p5.prototype.createCheckbox = function () { p5._validateParameters('createCheckbox', arguments); // Create a container element @@ -585,7 +585,7 @@ p5.prototype.createCheckbox = function() { //checkbox must be wrapped in p5.Element before label so that label appears after const self = addElement(elt, this); - self.checked = function() { + self.checked = function () { const cb = self.elt.firstElementChild.getElementsByTagName('input')[0]; if (cb) { if (arguments.length === 0) { @@ -599,7 +599,7 @@ p5.prototype.createCheckbox = function() { return self; }; - this.value = function(val) { + this.value = function (val) { self.value = val; return this; }; @@ -697,7 +697,7 @@ p5.prototype.createCheckbox = function() { * @return {p5.Element} */ -p5.prototype.createSelect = function() { +p5.prototype.createSelect = function () { p5._validateParameters('createSelect', arguments); let self; let arg = arguments[0]; @@ -716,7 +716,7 @@ p5.prototype.createSelect = function() { self = addElement(elt, this); this.elt = elt; } - self.option = function(name, value) { + self.option = function (name, value) { let index; // if no name is passed, return @@ -749,7 +749,7 @@ p5.prototype.createSelect = function() { } }; - self.selected = function(value) { + self.selected = function (value) { // Update selected status of option if (value !== undefined) { for (let i = 0; i < this.elt.length; i += 1) { @@ -771,7 +771,7 @@ p5.prototype.createSelect = function() { } }; - self.disable = function(value) { + self.disable = function (value) { if (typeof value === 'string') { for (let i = 0; i < this.elt.length; i++) { if (this.elt[i].value.toString() === value) { @@ -785,7 +785,7 @@ p5.prototype.createSelect = function() { return this; }; - self.enable = function(value) { + self.enable = function (value) { if (typeof value === 'string') { for (let i = 0; i < this.elt.length; i++) { if (this.elt[i].value.toString() === value) { @@ -877,7 +877,7 @@ p5.prototype.createSelect = function() { * @method createRadio * @return {p5.Element} pointer to p5.Element holding created node */ -p5.prototype.createRadio = function() { +p5.prototype.createRadio = function () { // Creates a div, adds each option as an individual input inside it. // If already given with a containerEl, will search for all input[radio] // it, create a p5.Element out of it, add options to it and return the p5.Element. @@ -916,7 +916,7 @@ p5.prototype.createRadio = function() { const isLabelElement = el => el instanceof HTMLLabelElement; const isSpanElement = el => el instanceof HTMLSpanElement; - self._getOptionsArray = function() { + self._getOptionsArray = function () { return Array.from(this.elt.children) .filter( el => @@ -926,7 +926,7 @@ p5.prototype.createRadio = function() { .map(el => (isRadioInput(el) ? el : el.firstElementChild)); }; - self.option = function(value, label) { + self.option = function (value, label) { // return an option with this value, create if not exists. let optionEl; for (const option of self._getOptionsArray()) { @@ -972,7 +972,7 @@ p5.prototype.createRadio = function() { return optionEl; }; - self.remove = function(value) { + self.remove = function (value) { for (const optionEl of self._getOptionsArray()) { if (optionEl.value === value) { if (isLabelElement(optionEl.parentElement)) { @@ -987,7 +987,7 @@ p5.prototype.createRadio = function() { } }; - self.value = function() { + self.value = function () { let result = ''; for (const option of self._getOptionsArray()) { if (option.checked) { @@ -998,7 +998,7 @@ p5.prototype.createRadio = function() { return result; }; - self.selected = function(value) { + self.selected = function (value) { let result = null; if (value === undefined) { for (const option of self._getOptionsArray()) { @@ -1026,7 +1026,7 @@ p5.prototype.createRadio = function() { return result; }; - self.disable = function(shouldDisable = true) { + self.disable = function (shouldDisable = true) { for (const radioInput of self._getOptionsArray()) { radioInput.setAttribute('disabled', shouldDisable); } @@ -1088,7 +1088,7 @@ p5.prototype.createRadio = function() { * } * */ -p5.prototype.createColorPicker = function(value) { +p5.prototype.createColorPicker = function (value) { p5._validateParameters('createColorPicker', arguments); const elt = document.createElement('input'); let self; @@ -1110,7 +1110,7 @@ p5.prototype.createColorPicker = function(value) { } self = addElement(elt, this); // Method to return a p5.Color object for the given color. - self.color = function() { + self.color = function () { if (value) { if (value.mode) { p5.prototype._colorMode = value.mode; @@ -1154,7 +1154,7 @@ p5.prototype.createColorPicker = function(value) { * @param {String} [value] * @return {p5.Element} */ -p5.prototype.createInput = function(value = '', type = 'text') { +p5.prototype.createInput = function (value = '', type = 'text') { p5._validateParameters('createInput', arguments); let elt = document.createElement('input'); elt.setAttribute('value', value); @@ -1198,10 +1198,10 @@ p5.prototype.createInput = function(value = '', type = 'text') { * } * */ -p5.prototype.createFileInput = function(callback, multiple = false) { +p5.prototype.createFileInput = function (callback, multiple = false) { p5._validateParameters('createFileInput', arguments); - const handleFileSelect = function(event) { + const handleFileSelect = function (event) { for (const file of event.target.files) { p5.File._load(file, callback); } @@ -1310,7 +1310,7 @@ function createMedia(pInst, type, src, callback) { * } * */ -p5.prototype.createVideo = function(src, callback) { +p5.prototype.createVideo = function (src, callback) { p5._validateParameters('createVideo', arguments); return createMedia(this, 'video', src, callback); }; @@ -1349,7 +1349,7 @@ p5.prototype.createVideo = function(src, callback) { * } * */ -p5.prototype.createAudio = function(src, callback) { +p5.prototype.createAudio = function (src, callback) { p5._validateParameters('createAudio', arguments); return createMedia(this, 'audio', src, callback); }; @@ -1370,7 +1370,7 @@ if (navigator.mediaDevices === undefined) { // with getUserMedia as it would overwrite existing properties. // Here, we will just add the getUserMedia property if it's missing. if (navigator.mediaDevices.getUserMedia === undefined) { - navigator.mediaDevices.getUserMedia = function(constraints) { + navigator.mediaDevices.getUserMedia = function (constraints) { // First get ahold of the legacy getUserMedia, if present const getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia; @@ -1384,7 +1384,7 @@ if (navigator.mediaDevices.getUserMedia === undefined) { } // Otherwise, wrap the call to the old navigator.getUserMedia with a Promise - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { getUserMedia.call(navigator, constraints, resolve, reject); }); }; @@ -1472,7 +1472,7 @@ if (navigator.mediaDevices.getUserMedia === undefined) { * * */ -p5.prototype.createCapture = function() { +p5.prototype.createCapture = function () { p5._validateParameters('createCapture', arguments); // return if getUserMedia is not supported by browser @@ -1496,7 +1496,7 @@ p5.prototype.createCapture = function() { // required to work in iOS 11 & up: domElement.setAttribute('playsinline', ''); - navigator.mediaDevices.getUserMedia(constraints).then(function(stream) { + navigator.mediaDevices.getUserMedia(constraints).then(function (stream) { try { if ('srcObject' in domElement) { domElement.srcObject = stream; @@ -1511,7 +1511,7 @@ p5.prototype.createCapture = function() { const videoEl = addElement(domElement, this, true); videoEl.loadedmetadata = false; // set width and height onload metadata - domElement.addEventListener('loadedmetadata', function() { + domElement.addEventListener('loadedmetadata', function () { domElement.play(); if (domElement.width) { videoEl.width = domElement.width; @@ -1541,7 +1541,7 @@ p5.prototype.createCapture = function() { * h5.position(0, 0); * */ -p5.prototype.createElement = function(tag, content) { +p5.prototype.createElement = function (tag, content) { p5._validateParameters('createElement', arguments); const elt = document.createElement(tag); if (typeof content !== 'undefined') { @@ -1567,7 +1567,7 @@ p5.prototype.createElement = function(tag, content) { * div.addClass('myClass'); * */ -p5.Element.prototype.addClass = function(c) { +p5.Element.prototype.addClass = function (c) { if (this.elt.className) { if (!this.hasClass(c)) { this.elt.className = this.elt.className + ' ' + c; @@ -1603,7 +1603,7 @@ p5.Element.prototype.addClass = function(c) { * } * */ -p5.Element.prototype.removeClass = function(c) { +p5.Element.prototype.removeClass = function (c) { // Note: Removing a class that does not exist does NOT throw an error in classList.remove method this.elt.classList.remove(c); return this; @@ -1634,7 +1634,7 @@ p5.Element.prototype.removeClass = function(c) { * } * */ -p5.Element.prototype.hasClass = function(c) { +p5.Element.prototype.hasClass = function (c) { return this.elt.classList.contains(c); }; @@ -1659,7 +1659,7 @@ p5.Element.prototype.hasClass = function(c) { * } * */ -p5.Element.prototype.toggleClass = function(c) { +p5.Element.prototype.toggleClass = function (c) { // classList also has a toggle() method, but we cannot use that yet as support is unclear. // See https://github.com/processing/p5.js/issues/3631 // this.elt.classList.toggle(c); @@ -1705,7 +1705,7 @@ p5.Element.prototype.toggleClass = function(c) { * to add to the current element * @chainable */ -p5.Element.prototype.child = function(childNode) { +p5.Element.prototype.child = function (childNode) { if (typeof childNode === 'undefined') { return this.elt.childNodes; } @@ -1743,7 +1743,7 @@ p5.Element.prototype.child = function(childNode) { * } * */ -p5.Element.prototype.center = function(align) { +p5.Element.prototype.center = function (align) { const style = this.elt.style.display; const hidden = this.elt.style.display === 'none'; const parentHidden = this.parent().style.display === 'none'; @@ -1802,7 +1802,7 @@ p5.Element.prototype.center = function(align) { * @param {boolean} [append] whether to append HTML to existing * @chainable */ -p5.Element.prototype.html = function() { +p5.Element.prototype.html = function () { if (arguments.length === 0) { return this.elt.innerHTML; } else if (arguments[1]) { @@ -1854,7 +1854,7 @@ p5.Element.prototype.html = function() { * @param {String} [positionType] it can be static, fixed, relative, sticky, initial or inherit (optional) * @chainable */ -p5.Element.prototype.position = function() { +p5.Element.prototype.position = function () { if (arguments.length === 0) { return { x: this.elt.offsetLeft, y: this.elt.offsetTop }; } else { @@ -1879,7 +1879,7 @@ p5.Element.prototype.position = function() { }; /* Helper method called by p5.Element.style() */ -p5.Element.prototype._translate = function() { +p5.Element.prototype._translate = function () { this.elt.style.position = 'absolute'; // save out initial non-translate transform styling let transform = ''; @@ -1911,7 +1911,7 @@ p5.Element.prototype._translate = function() { }; /* Helper method called by p5.Element.style() */ -p5.Element.prototype._rotate = function() { +p5.Element.prototype._rotate = function () { // save out initial non-rotate transform styling let transform = ''; if (this.elt.style.transform) { @@ -1979,7 +1979,7 @@ p5.Element.prototype._rotate = function() { * @return {String} current value of property, if no value is given as second argument * @chainable */ -p5.Element.prototype.style = function(prop, val) { +p5.Element.prototype.style = function (prop, val) { const self = this; if (val instanceof p5.Color) { @@ -2050,7 +2050,7 @@ p5.Element.prototype.style = function(prop, val) { * @param {String} value value to assign to attribute * @chainable */ -p5.Element.prototype.attribute = function(attr, value) { +p5.Element.prototype.attribute = function (attr, value) { //handling for checkboxes and radios to ensure options get //attributes not divs if ( @@ -2104,7 +2104,7 @@ p5.Element.prototype.attribute = function(attr, value) { * } * */ -p5.Element.prototype.removeAttribute = function(attr) { +p5.Element.prototype.removeAttribute = function (attr) { if ( this.elt.firstChild != null && (this.elt.firstChild.type === 'checkbox' || @@ -2153,7 +2153,7 @@ p5.Element.prototype.removeAttribute = function(attr) { * @param {String|Number} value * @chainable */ -p5.Element.prototype.value = function() { +p5.Element.prototype.value = function () { if (arguments.length > 0) { this.elt.value = arguments[0]; return this; @@ -2177,7 +2177,7 @@ p5.Element.prototype.value = function() { * div.show(); // turns display to block * */ -p5.Element.prototype.show = function() { +p5.Element.prototype.show = function () { this.elt.style.display = 'block'; return this; }; @@ -2193,7 +2193,7 @@ p5.Element.prototype.show = function() { * div.hide(); * */ -p5.Element.prototype.hide = function() { +p5.Element.prototype.hide = function () { this.elt.style.display = 'none'; return this; }; @@ -2228,7 +2228,7 @@ p5.Element.prototype.hide = function() { * @param {Number|Constant} [h] height of the element, either AUTO, or a number * @chainable */ -p5.Element.prototype.size = function(w, h) { +p5.Element.prototype.size = function (w, h) { if (arguments.length === 0) { return { width: this.elt.offsetWidth, height: this.elt.offsetHeight }; } else { @@ -2288,7 +2288,7 @@ p5.Element.prototype.size = function(w, h) { * myDiv.remove(); * */ -p5.Element.prototype.remove = function() { +p5.Element.prototype.remove = function () { // stop all audios/videos and detach all devices like microphone/camera etc // used as input/output for audios/videos. if (this instanceof p5.MediaElement) { @@ -2376,13 +2376,13 @@ p5.Element.prototype.remove = function() { * @alt * Canvas turns into whatever image is dragged/dropped onto it. */ -p5.Element.prototype.drop = function(callback, fxn) { +p5.Element.prototype.drop = function (callback, fxn) { // Is the file stuff supported? if (window.File && window.FileReader && window.FileList && window.Blob) { if (!this._dragDisabled) { this._dragDisabled = true; - const preventDefault = function(evt) { + const preventDefault = function (evt) { evt.preventDefault(); }; @@ -2398,7 +2398,7 @@ p5.Element.prototype.drop = function(callback, fxn) { // Deal with the files p5.Element._attachListener( 'drop', - function(evt) { + function (evt) { evt.preventDefault(); // Call the second argument as a callback that receives the raw drop event if (typeof fxn === 'function') { @@ -2436,91 +2436,92 @@ p5.Element.prototype.drop = function(callback, fxn) { * @constructor * @param {String} elt DOM node that is wrapped */ -p5.MediaElement = function(elt, pInst) { - p5.Element.call(this, elt, pInst); - - const self = this; - this.elt.crossOrigin = 'anonymous'; - - this._prevTime = 0; - this._cueIDCounter = 0; - this._cues = []; - this._pixelsState = this; - this._pixelDensity = 1; - this._modified = false; - - // Media has an internal canvas that is used when drawing it to the main - // canvas. It will need to be updated each frame as the video itself plays. - // We don't want to update it every time we draw, however, in case the user - // has used load/updatePixels. To handle this, we record the frame drawn to - // the internal canvas so we only update it if the frame has changed. - this._frameOnCanvas = -1; - - /** - * Path to the media element source. - * - * @property src - * @return {String} src - * @example - *
- * let ele; - * - * function setup() { - * background(250); - * - * //p5.MediaElement objects are usually created - * //by calling the createAudio(), createVideo(), - * //and createCapture() functions. - * - * //In this example we create - * //a new p5.MediaElement via createAudio(). - * ele = createAudio('assets/beat.mp3'); - * - * //We'll set up our example so that - * //when you click on the text, - * //an alert box displays the MediaElement's - * //src field. - * textAlign(CENTER); - * text('Click Me!', width / 2, height / 2); - * } - * - * function mouseClicked() { - * //here we test if the mouse is over the - * //canvas element when it's clicked - * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { - * //Show our p5.MediaElement's src field - * alert(ele.src); - * } - * } - *
- */ - Object.defineProperty(self, 'src', { - get: function() { - const firstChildSrc = self.elt.children[0].src; - const srcVal = self.elt.src === window.location.href ? '' : self.elt.src; - const ret = - firstChildSrc === window.location.href ? srcVal : firstChildSrc; - return ret; - }, - set: function(newValue) { - for (let i = 0; i < self.elt.children.length; i++) { - self.elt.removeChild(self.elt.children[i]); +p5.MediaElement = class MediaElement extends p5.Element { + constructor(elt, pInst) { + super(elt, pInst); + + const self = this; + this.elt.crossOrigin = 'anonymous'; + + this._prevTime = 0; + this._cueIDCounter = 0; + this._cues = []; + this._pixelsState = this; + this._pixelDensity = 1; + this._modified = false; + + // Media has an internal canvas that is used when drawing it to the main + // canvas. It will need to be updated each frame as the video itself plays. + // We don't want to update it every time we draw, however, in case the user + // has used load/updatePixels. To handle this, we record the frame drawn to + // the internal canvas so we only update it if the frame has changed. + this._frameOnCanvas = -1; + + /** + * Path to the media element source. + * + * @property src + * @return {String} src + * @example + *
+ * let ele; + * + * function setup() { + * background(250); + * + * //p5.MediaElement objects are usually created + * //by calling the createAudio(), createVideo(), + * //and createCapture() functions. + * + * //In this example we create + * //a new p5.MediaElement via createAudio(). + * ele = createAudio('assets/beat.mp3'); + * + * //We'll set up our example so that + * //when you click on the text, + * //an alert box displays the MediaElement's + * //src field. + * textAlign(CENTER); + * text('Click Me!', width / 2, height / 2); + * } + * + * function mouseClicked() { + * //here we test if the mouse is over the + * //canvas element when it's clicked + * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { + * //Show our p5.MediaElement's src field + * alert(ele.src); + * } + * } + *
+ */ + Object.defineProperty(self, 'src', { + get() { + const firstChildSrc = self.elt.children[0].src; + const srcVal = self.elt.src === window.location.href ? '' : self.elt.src; + const ret = + firstChildSrc === window.location.href ? srcVal : firstChildSrc; + return ret; + }, + set(newValue) { + for (let i = 0; i < self.elt.children.length; i++) { + self.elt.removeChild(self.elt.children[i]); + } + const source = document.createElement('source'); + source.src = newValue; + elt.appendChild(source); + self.elt.src = newValue; + self.modified = true; } - const source = document.createElement('source'); - source.src = newValue; - elt.appendChild(source); - self.elt.src = newValue; - self.modified = true; - } - }); + }); - // private _onended callback, set by the method: onended(callback) - self._onended = function() {}; - self.elt.onended = function() { - self._onended(self); + // private _onended callback, set by the method: onended(callback) + self._onended = function () { }; + self.elt.onended = function () { + self._onended(self); + }; }; -}; -p5.MediaElement.prototype = Object.create(p5.Element.prototype); +} /** * Play an HTML5 media element. @@ -2560,7 +2561,7 @@ p5.MediaElement.prototype = Object.create(p5.Element.prototype); * } * */ -p5.MediaElement.prototype.play = function() { +p5.MediaElement.prototype.play = function () { if (this.elt.currentTime === this.elt.duration) { this.elt.currentTime = 0; } @@ -2648,7 +2649,7 @@ p5.MediaElement.prototype.play = function() { * } * */ -p5.MediaElement.prototype.stop = function() { +p5.MediaElement.prototype.stop = function () { this.elt.pause(); this.elt.currentTime = 0; return this; @@ -2711,7 +2712,7 @@ p5.MediaElement.prototype.stop = function() { * } * */ -p5.MediaElement.prototype.pause = function() { +p5.MediaElement.prototype.pause = function () { this.elt.pause(); return this; }; @@ -2767,7 +2768,7 @@ p5.MediaElement.prototype.pause = function() { * } * */ -p5.MediaElement.prototype.loop = function() { +p5.MediaElement.prototype.loop = function () { this.elt.setAttribute('loop', true); this.play(); return this; @@ -2819,7 +2820,7 @@ p5.MediaElement.prototype.loop = function() { * } * */ -p5.MediaElement.prototype.noLoop = function() { +p5.MediaElement.prototype.noLoop = function () { this.elt.removeAttribute('loop'); return this; }; @@ -2830,7 +2831,7 @@ p5.MediaElement.prototype.noLoop = function() { * @method setupAutoplayFailDetection * @private */ -p5.MediaElement.prototype._setupAutoplayFailDetection = function() { +p5.MediaElement.prototype._setupAutoplayFailDetection = function () { const timeout = setTimeout(() => { if (typeof IS_MINIFIED === 'undefined') { p5._friendlyAutoplayError(this.src); @@ -2889,7 +2890,7 @@ p5.MediaElement.prototype._setupAutoplayFailDetection = function() { * An example of a video element which waits for a trigger for playing. */ -p5.MediaElement.prototype.autoplay = function(val) { +p5.MediaElement.prototype.autoplay = function (val) { const oldVal = this.elt.getAttribute('autoplay'); this.elt.setAttribute('autoplay', val); // if we turned on autoplay @@ -2989,7 +2990,7 @@ p5.MediaElement.prototype.autoplay = function(val) { * @param {Number} val volume between 0.0 and 1.0 * @chainable */ -p5.MediaElement.prototype.volume = function(val) { +p5.MediaElement.prototype.volume = function (val) { if (typeof val === 'undefined') { return this.elt.volume; } else { @@ -3074,7 +3075,7 @@ p5.MediaElement.prototype.volume = function(val) { * @param {Number} speed speed multiplier for element playback * @chainable */ -p5.MediaElement.prototype.speed = function(val) { +p5.MediaElement.prototype.speed = function (val) { if (typeof val === 'undefined') { return this.presetPlaybackRate || this.elt.playbackRate; } else { @@ -3136,7 +3137,7 @@ p5.MediaElement.prototype.speed = function(val) { * @param {Number} time time to jump to (in seconds) * @chainable */ -p5.MediaElement.prototype.time = function(val) { +p5.MediaElement.prototype.time = function (val) { if (typeof val === 'undefined') { return this.elt.currentTime; } else { @@ -3173,11 +3174,11 @@ p5.MediaElement.prototype.time = function(val) { * } * */ -p5.MediaElement.prototype.duration = function() { +p5.MediaElement.prototype.duration = function () { return this.elt.duration; }; p5.MediaElement.prototype.pixels = []; -p5.MediaElement.prototype._ensureCanvas = function() { +p5.MediaElement.prototype._ensureCanvas = function () { if (!this.canvas) { this.canvas = document.createElement('canvas'); this.drawingContext = this.canvas.getContext('2d'); @@ -3207,11 +3208,11 @@ p5.MediaElement.prototype._ensureCanvas = function() { this._frameOnCanvas = this._pInst.frameCount; } }; -p5.MediaElement.prototype.loadPixels = function() { +p5.MediaElement.prototype.loadPixels = function () { this._ensureCanvas(); return p5.Renderer2D.prototype.loadPixels.apply(this, arguments); }; -p5.MediaElement.prototype.updatePixels = function(x, y, w, h) { +p5.MediaElement.prototype.updatePixels = function (x, y, w, h) { if (this.loadedmetadata) { // wait for metadata this._ensureCanvas(); @@ -3220,16 +3221,16 @@ p5.MediaElement.prototype.updatePixels = function(x, y, w, h) { this.setModified(true); return this; }; -p5.MediaElement.prototype.get = function() { +p5.MediaElement.prototype.get = function () { this._ensureCanvas(); return p5.Renderer2D.prototype.get.apply(this, arguments); }; -p5.MediaElement.prototype._getPixel = function() { +p5.MediaElement.prototype._getPixel = function () { this.loadPixels(); return p5.Renderer2D.prototype._getPixel.apply(this, arguments); }; -p5.MediaElement.prototype.set = function(x, y, imgOrCol) { +p5.MediaElement.prototype.set = function (x, y, imgOrCol) { if (this.loadedmetadata) { // wait for metadata this._ensureCanvas(); @@ -3237,11 +3238,11 @@ p5.MediaElement.prototype.set = function(x, y, imgOrCol) { this.setModified(true); } }; -p5.MediaElement.prototype.copy = function() { +p5.MediaElement.prototype.copy = function () { this._ensureCanvas(); p5.prototype.copy.apply(this, arguments); }; -p5.MediaElement.prototype.mask = function() { +p5.MediaElement.prototype.mask = function () { this.loadPixels(); this.setModified(true); p5.Image.prototype.mask.apply(this, arguments); @@ -3255,7 +3256,7 @@ p5.MediaElement.prototype.mask = function() { * @return {boolean} a boolean indicating whether or not the * image has been updated or modified since last texture upload. */ -p5.MediaElement.prototype.isModified = function() { +p5.MediaElement.prototype.isModified = function () { return this._modified; }; /** @@ -3269,7 +3270,7 @@ p5.MediaElement.prototype.isModified = function() { * modified. * @private */ -p5.MediaElement.prototype.setModified = function(value) { +p5.MediaElement.prototype.setModified = function (value) { this._modified = value; }; /** @@ -3298,7 +3299,7 @@ p5.MediaElement.prototype.setModified = function(value) { * } * */ -p5.MediaElement.prototype.onended = function(callback) { +p5.MediaElement.prototype.onended = function (callback) { this._onended = callback; return this; }; @@ -3317,7 +3318,7 @@ p5.MediaElement.prototype.onended = function(callback) { * @param {AudioNode|Object} audioNode AudioNode from the Web Audio API, * or an object from the p5.sound library */ -p5.MediaElement.prototype.connect = function(obj) { +p5.MediaElement.prototype.connect = function (obj) { let audioContext, mainOutput; // if p5.sound exists, same audio context @@ -3361,7 +3362,7 @@ p5.MediaElement.prototype.connect = function(obj) { * * @method disconnect */ -p5.MediaElement.prototype.disconnect = function() { +p5.MediaElement.prototype.disconnect = function () { if (this.audioSourceNode) { this.audioSourceNode.disconnect(); } else { @@ -3396,7 +3397,7 @@ p5.MediaElement.prototype.disconnect = function() { * } * */ -p5.MediaElement.prototype.showControls = function() { +p5.MediaElement.prototype.showControls = function () { // must set style for the element to show on the page this.elt.style['text-align'] = 'inherit'; this.elt.controls = true; @@ -3427,7 +3428,7 @@ p5.MediaElement.prototype.showControls = function() { * } * */ -p5.MediaElement.prototype.hideControls = function() { +p5.MediaElement.prototype.hideControls = function () { this.elt.controls = false; }; @@ -3435,7 +3436,7 @@ p5.MediaElement.prototype.hideControls = function() { // Cue inspired by JavaScript setTimeout, and the // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org -const Cue = function(callback, time, id, val) { +const Cue = function (callback, time, id, val) { this.callback = callback; this.time = time; this.id = id; @@ -3492,7 +3493,7 @@ const Cue = function(callback, time, id, val) { * } * */ -p5.MediaElement.prototype.addCue = function(time, callback, val) { +p5.MediaElement.prototype.addCue = function (time, callback, val) { const id = this._cueIDCounter++; const cue = new Cue(callback, time, id, val); @@ -3534,7 +3535,7 @@ p5.MediaElement.prototype.addCue = function(time, callback, val) { * } * */ -p5.MediaElement.prototype.removeCue = function(id) { +p5.MediaElement.prototype.removeCue = function (id) { for (let i = 0; i < this._cues.length; i++) { if (this._cues[i].id === id) { console.log(id); @@ -3581,14 +3582,14 @@ p5.MediaElement.prototype.removeCue = function(id) { * } * */ -p5.MediaElement.prototype.clearCues = function() { +p5.MediaElement.prototype.clearCues = function () { this._cues = []; this.elt.ontimeupdate = null; }; // private method that checks for cues to be fired if events // have been scheduled using addCue(callback, time). -p5.MediaElement.prototype._onTimeUpdate = function() { +p5.MediaElement.prototype._onTimeUpdate = function () { const playbackTime = this.time(); for (let i = 0; i < this._cues.length; i++) { @@ -3612,56 +3613,58 @@ p5.MediaElement.prototype._onTimeUpdate = function() { * @constructor * @param {File} file File that is wrapped */ -p5.File = function(file, pInst) { - /** - * Underlying File object. All normal File methods can be called on this. - * - * @property file - */ - this.file = file; - - this._pInst = pInst; - - // Splitting out the file type into two components - // This makes determining if image or text etc simpler - const typeList = file.type.split('/'); - /** - * File type (image, text, etc.) - * - * @property type - */ - this.type = typeList[0]; - /** - * File subtype (usually the file extension jpg, png, xml, etc.) - * - * @property subtype - */ - this.subtype = typeList[1]; - /** - * File name - * - * @property name - */ - this.name = file.name; - /** - * File size - * - * @property size - */ - this.size = file.size; - - /** - * URL string containing either image data, the text contents of the file or - * a parsed object if file is JSON and p5.XML if XML - * - * @property data - */ - this.data = undefined; -}; - -p5.File._createLoader = function(theFile, callback) { +p5.File = class File { + constructor(file, pInst) { + /** + * Underlying File object. All normal File methods can be called on this. + * + * @property file + */ + this.file = file; + + this._pInst = pInst; + + // Splitting out the file type into two components + // This makes determining if image or text etc simpler + const typeList = file.type.split('/'); + /** + * File type (image, text, etc.) + * + * @property type + */ + this.type = typeList[0]; + /** + * File subtype (usually the file extension jpg, png, xml, etc.) + * + * @property subtype + */ + this.subtype = typeList[1]; + /** + * File name + * + * @property name + */ + this.name = file.name; + /** + * File size + * + * @property size + */ + this.size = file.size; + + /** + * URL string containing either image data, the text contents of the file or + * a parsed object if file is JSON and p5.XML if XML + * + * @property data + */ + this.data = undefined; + }; +} + +p5.File._createLoader = function (theFile, callback) { const reader = new FileReader(); - reader.onload = function(e) { + reader.onload = function (e) { const p5file = new p5.File(theFile); if (p5file.file.type === 'application/json') { // Parse JSON and store the result in data @@ -3679,7 +3682,7 @@ p5.File._createLoader = function(theFile, callback) { return reader; }; -p5.File._load = function(f, callback) { +p5.File._load = function (f, callback) { // Text or data? // This should likely be improved if (/^text\//.test(f.type) || f.type === 'application/json') { From 48c65b7a0d7dfee70cc7b6f4a621f1574c8eb827 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Wed, 31 May 2023 13:23:48 +0900 Subject: [PATCH 18/22] fix eslint --- src/dom/dom.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 7e5faadd70..4287c65447 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -2520,8 +2520,8 @@ p5.MediaElement = class MediaElement extends p5.Element { self.elt.onended = function () { self._onended(self); }; - }; -} + } +}; /** * Play an HTML5 media element. @@ -3659,8 +3659,8 @@ p5.File = class File { * @property data */ this.data = undefined; - }; -} + } +}; p5.File._createLoader = function (theFile, callback) { const reader = new FileReader(); From 21847cc64e14c9db181bf084244a734cc9ffdd82 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Wed, 31 May 2023 13:41:20 +0900 Subject: [PATCH 19/22] TypedDict, StringDict, NumberDict to class --- src/data/p5.TypedDict.js | 1016 +++++++++++++++++++------------------- 1 file changed, 512 insertions(+), 504 deletions(-) diff --git a/src/data/p5.TypedDict.js b/src/data/p5.TypedDict.js index 6df8d1d01f..449887157b 100644 --- a/src/data/p5.TypedDict.js +++ b/src/data/p5.TypedDict.js @@ -40,7 +40,7 @@ import p5 from '../core/main'; * @return {p5.StringDict} */ -p5.prototype.createStringDict = function(key, value) { +p5.prototype.createStringDict = function (key, value) { p5._validateParameters('createStringDict', arguments); return new p5.StringDict(key, value); }; @@ -74,7 +74,7 @@ p5.prototype.createStringDict = function(key, value) { * @return {p5.NumberDict} */ -p5.prototype.createNumberDict = function(key, value) { +p5.prototype.createNumberDict = function (key, value) { p5._validateParameters('createNumberDict', arguments); return new p5.NumberDict(key, value); }; @@ -88,309 +88,312 @@ p5.prototype.createNumberDict = function(key, value) { * @constructor */ -p5.TypedDict = function(key, value) { - if (key instanceof Object) { - this.data = key; - } else { - this.data = {}; - this.data[key] = value; +p5.TypedDict = class TypedDict { + constructor(key, value) { + if (key instanceof Object) { + this.data = key; + } else { + this.data = {}; + this.data[key] = value; + } + return this; } - return this; -}; - -/** - * Returns the number of key-value pairs currently stored in the Dictionary. - * - * @method size - * @return {Integer} the number of key-value pairs in the Dictionary - * - * @example - *
- * - * function setup() { - * let myDictionary = createNumberDict(1, 10); - * myDictionary.create(2, 20); - * myDictionary.create(3, 30); - * print(myDictionary.size()); // logs 3 to the console - * } - *
- */ -p5.TypedDict.prototype.size = function() { - return Object.keys(this.data).length; -}; - -/** - * Returns true if the given key exists in the Dictionary, - * otherwise returns false. - * - * @method hasKey - * @param {Number|String} key that you want to look up - * @return {Boolean} whether that key exists in Dictionary - * - * @example - *
- * - * function setup() { - * let myDictionary = createStringDict('p5', 'js'); - * print(myDictionary.hasKey('p5')); // logs true to console - * } - *
- */ -p5.TypedDict.prototype.hasKey = function(key) { - return this.data.hasOwnProperty(key); -}; - -/** - * Returns the value stored at the given key. - * - * @method get - * @param {Number|String} the key you want to access - * @return {Number|String} the value stored at that key - * - * @example - *
- * - * function setup() { - * let myDictionary = createStringDict('p5', 'js'); - * let myValue = myDictionary.get('p5'); - * print(myValue === 'js'); // logs true to console - * } - *
- */ + /** + * Returns the number of key-value pairs currently stored in the Dictionary. + * + * @method size + * @return {Integer} the number of key-value pairs in the Dictionary + * + * @example + *
+ * + * function setup() { + * let myDictionary = createNumberDict(1, 10); + * myDictionary.create(2, 20); + * myDictionary.create(3, 30); + * print(myDictionary.size()); // logs 3 to the console + * } + *
+ */ + size() { + return Object.keys(this.data).length; + } -p5.TypedDict.prototype.get = function(key) { - if (this.data.hasOwnProperty(key)) { - return this.data[key]; - } else { - console.log(`${key} does not exist in this Dictionary`); + /** + * Returns true if the given key exists in the Dictionary, + * otherwise returns false. + * + * @method hasKey + * @param {Number|String} key that you want to look up + * @return {Boolean} whether that key exists in Dictionary + * + * @example + *
+ * + * function setup() { + * let myDictionary = createStringDict('p5', 'js'); + * print(myDictionary.hasKey('p5')); // logs true to console + * } + *
+ */ + + hasKey(key) { + return this.data.hasOwnProperty(key); } -}; -/** - * Updates the value associated with the given key in case it already exists - * in the Dictionary. Otherwise a new key-value pair is added. - * - * @method set - * @param {Number|String} key - * @param {Number|String} value - * - * @example - *
- * - * function setup() { - * let myDictionary = createStringDict('p5', 'js'); - * myDictionary.set('p5', 'JS'); - * myDictionary.print(); // logs "key: p5 - value: JS" to console - * } - *
- */ + /** + * Returns the value stored at the given key. + * + * @method get + * @param {Number|String} the key you want to access + * @return {Number|String} the value stored at that key + * + * @example + *
+ * + * function setup() { + * let myDictionary = createStringDict('p5', 'js'); + * let myValue = myDictionary.get('p5'); + * print(myValue === 'js'); // logs true to console + * } + *
+ */ + + get(key) { + if (this.data.hasOwnProperty(key)) { + return this.data[key]; + } else { + console.log(`${key} does not exist in this Dictionary`); + } + } -p5.TypedDict.prototype.set = function(key, value) { - if (this._validate(value)) { - this.data[key] = value; - } else { - console.log('Those values dont work for this dictionary type.'); + /** + * Updates the value associated with the given key in case it already exists + * in the Dictionary. Otherwise a new key-value pair is added. + * + * @method set + * @param {Number|String} key + * @param {Number|String} value + * + * @example + *
+ * + * function setup() { + * let myDictionary = createStringDict('p5', 'js'); + * myDictionary.set('p5', 'JS'); + * myDictionary.print(); // logs "key: p5 - value: JS" to console + * } + *
+ */ + + set(key, value) { + if (this._validate(value)) { + this.data[key] = value; + } else { + console.log('Those values dont work for this dictionary type.'); + } } -}; -/** - * private helper function to handle the user passing in objects - * during construction or calls to create() - */ + /** + * private helper function to handle the user passing in objects + * during construction or calls to create() + */ -p5.TypedDict.prototype._addObj = function(obj) { - for (const key in obj) { - this.set(key, obj[key]); + _addObj(obj) { + for (const key in obj) { + this.set(key, obj[key]); + } } -}; - -/** - * Creates a new key-value pair in the Dictionary. - * - * @method create - * @param {Number|String} key - * @param {Number|String} value - * - * @example - *
- * - * function setup() { - * let myDictionary = createStringDict('p5', 'js'); - * myDictionary.create('happy', 'coding'); - * myDictionary.print(); - * // above logs "key: p5 - value: js, key: happy - value: coding" to console - * } - *
- */ -/** - * @method create - * @param {Object} obj key/value pair - */ -p5.TypedDict.prototype.create = function(key, value) { - if (key instanceof Object && typeof value === 'undefined') { - this._addObj(key); - } else if (typeof key !== 'undefined') { - this.set(key, value); - } else { - console.log( - 'In order to create a new Dictionary entry you must pass ' + + /** + * Creates a new key-value pair in the Dictionary. + * + * @method create + * @param {Number|String} key + * @param {Number|String} value + * + * @example + *
+ * + * function setup() { + * let myDictionary = createStringDict('p5', 'js'); + * myDictionary.create('happy', 'coding'); + * myDictionary.print(); + * // above logs "key: p5 - value: js, key: happy - value: coding" to console + * } + *
+ */ + /** + * @method create + * @param {Object} obj key/value pair + */ + + create(key, value) { + if (key instanceof Object && typeof value === 'undefined') { + this._addObj(key); + } else if (typeof key !== 'undefined') { + this.set(key, value); + } else { + console.log( + 'In order to create a new Dictionary entry you must pass ' + 'an object or a key, value pair' - ); + ); + } } -}; - -/** - * Removes all previously stored key-value pairs from the Dictionary. - * - * @method clear - * @example - *
- * - * function setup() { - * let myDictionary = createStringDict('p5', 'js'); - * print(myDictionary.hasKey('p5')); // prints 'true' - * myDictionary.clear(); - * print(myDictionary.hasKey('p5')); // prints 'false' - * } - * - *
- */ - -p5.TypedDict.prototype.clear = function() { - this.data = {}; -}; - -/** - * Removes the key-value pair stored at the given key from the Dictionary. - * - * @method remove - * @param {Number|String} key for the pair to remove - * - * @example - *
- * - * function setup() { - * let myDictionary = createStringDict('p5', 'js'); - * myDictionary.create('happy', 'coding'); - * myDictionary.print(); - * // above logs "key: p5 - value: js, key: happy - value: coding" to console - * myDictionary.remove('p5'); - * myDictionary.print(); - * // above logs "key: happy value: coding" to console - * } - *
- */ -p5.TypedDict.prototype.remove = function(key) { - if (this.data.hasOwnProperty(key)) { - delete this.data[key]; - } else { - throw new Error(`${key} does not exist in this Dictionary`); + /** + * Removes all previously stored key-value pairs from the Dictionary. + * + * @method clear + * @example + *
+ * + * function setup() { + * let myDictionary = createStringDict('p5', 'js'); + * print(myDictionary.hasKey('p5')); // prints 'true' + * myDictionary.clear(); + * print(myDictionary.hasKey('p5')); // prints 'false' + * } + * + *
+ */ + + clear() { + this.data = {}; } -}; -/** - * Logs the set of items currently stored in the Dictionary to the console. - * - * @method print - * - * @example - *
- * - * function setup() { - * let myDictionary = createStringDict('p5', 'js'); - * myDictionary.create('happy', 'coding'); - * myDictionary.print(); - * // above logs "key: p5 - value: js, key: happy - value: coding" to console - * } - * - *
- */ + /** + * Removes the key-value pair stored at the given key from the Dictionary. + * + * @method remove + * @param {Number|String} key for the pair to remove + * + * @example + *
+ * + * function setup() { + * let myDictionary = createStringDict('p5', 'js'); + * myDictionary.create('happy', 'coding'); + * myDictionary.print(); + * // above logs "key: p5 - value: js, key: happy - value: coding" to console + * myDictionary.remove('p5'); + * myDictionary.print(); + * // above logs "key: happy value: coding" to console + * } + *
+ */ + + remove(key) { + if (this.data.hasOwnProperty(key)) { + delete this.data[key]; + } else { + throw new Error(`${key} does not exist in this Dictionary`); + } + } -p5.TypedDict.prototype.print = function() { - for (const item in this.data) { - console.log(`key:${item} value:${this.data[item]}`); + /** + * Logs the set of items currently stored in the Dictionary to the console. + * + * @method print + * + * @example + *
+ * + * function setup() { + * let myDictionary = createStringDict('p5', 'js'); + * myDictionary.create('happy', 'coding'); + * myDictionary.print(); + * // above logs "key: p5 - value: js, key: happy - value: coding" to console + * } + * + *
+ */ + + print() { + for (const item in this.data) { + console.log(`key:${item} value:${this.data[item]}`); + } } -}; -/** - * Converts the Dictionary into a CSV file for local download. - * - * @method saveTable - * @example - *
- * - * function setup() { - * createCanvas(100, 100); - * background(200); - * text('click here to save', 10, 10, 70, 80); - * } - * - * function mousePressed() { - * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) { - * createStringDict({ - * john: 1940, - * paul: 1942, - * george: 1943, - * ringo: 1940 - * }).saveTable('beatles'); - * } - * } - * - *
- */ + /** + * Converts the Dictionary into a CSV file for local download. + * + * @method saveTable + * @example + *
+ * + * function setup() { + * createCanvas(100, 100); + * background(200); + * text('click here to save', 10, 10, 70, 80); + * } + * + * function mousePressed() { + * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) { + * createStringDict({ + * john: 1940, + * paul: 1942, + * george: 1943, + * ringo: 1940 + * }).saveTable('beatles'); + * } + * } + * + *
+ */ + + saveTable(filename) { + let output = ''; -p5.TypedDict.prototype.saveTable = function(filename) { - let output = ''; + for (const key in this.data) { + output += `${key},${this.data[key]}\n`; + } - for (const key in this.data) { - output += `${key},${this.data[key]}\n`; + const blob = new Blob([output], { type: 'text/csv' }); + p5.prototype.downloadFile(blob, filename || 'mycsv', 'csv'); } - const blob = new Blob([output], { type: 'text/csv' }); - p5.prototype.downloadFile(blob, filename || 'mycsv', 'csv'); -}; - -/** - * Converts the Dictionary into a JSON file for local download. - * - * @method saveJSON - * @example - *
- * - * function setup() { - * createCanvas(100, 100); - * background(200); - * text('click here to save', 10, 10, 70, 80); - * } - * - * function mousePressed() { - * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) { - * createStringDict({ - * john: 1940, - * paul: 1942, - * george: 1943, - * ringo: 1940 - * }).saveJSON('beatles'); - * } - * } - * - *
- */ + /** + * Converts the Dictionary into a JSON file for local download. + * + * @method saveJSON + * @example + *
+ * + * function setup() { + * createCanvas(100, 100); + * background(200); + * text('click here to save', 10, 10, 70, 80); + * } + * + * function mousePressed() { + * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) { + * createStringDict({ + * john: 1940, + * paul: 1942, + * george: 1943, + * ringo: 1940 + * }).saveJSON('beatles'); + * } + * } + * + *
+ */ + + saveJSON(filename, opt) { + p5.prototype.saveJSON(this.data, filename, opt); + } -p5.TypedDict.prototype.saveJSON = function(filename, opt) { - p5.prototype.saveJSON(this.data, filename, opt); + /** + * private helper function to ensure that the user passed in valid + * values for the Dictionary type + */ + _validate(value) { + return true; + } }; -/** - * private helper function to ensure that the user passed in valid - * values for the Dictionary type - */ - -p5.TypedDict.prototype._validate = value => true; - /** * * A simple Dictionary class for Strings. @@ -399,13 +402,15 @@ p5.TypedDict.prototype._validate = value => true; * @extends p5.TypedDict */ -p5.StringDict = function(...args) { - p5.TypedDict.apply(this, args); -}; - -p5.StringDict.prototype = Object.create(p5.TypedDict.prototype); +p5.StringDict = class StringDict extends p5.TypedDict { + constructor(...args) { + super(...args); + } -p5.StringDict.prototype._validate = value => typeof value === 'string'; + _validate(value) { + return typeof value === 'string'; + } +}; /** * @@ -416,247 +421,250 @@ p5.StringDict.prototype._validate = value => typeof value === 'string'; * @extends p5.TypedDict */ -p5.NumberDict = function(...args) { - p5.TypedDict.apply(this, args); -}; - -p5.NumberDict.prototype = Object.create(p5.TypedDict.prototype); - -/** - * private helper function to ensure that the user passed in valid - * values for the Dictionary type - */ - -p5.NumberDict.prototype._validate = value => typeof value === 'number'; - -/** - * Add the given number to the value currently stored at the given key. - * The sum then replaces the value previously stored in the Dictionary. - * - * @method add - * @param {Number} Key for the value you wish to add to - * @param {Number} Number to add to the value - * @example - *
- * - * function setup() { - * let myDictionary = createNumberDict(2, 5); - * myDictionary.add(2, 2); - * print(myDictionary.get(2)); // logs 7 to console. - * } - *
- * - */ - -p5.NumberDict.prototype.add = function(key, amount) { - if (this.data.hasOwnProperty(key)) { - this.data[key] += amount; - } else { - console.log(`The key - ${key} does not exist in this dictionary.`); +p5.NumberDict = class NumberDict extends p5.TypedDict { + constructor(...args) { + super(...args); } -}; -/** - * Subtract the given number from the value currently stored at the given key. - * The difference then replaces the value previously stored in the Dictionary. - * - * @method sub - * @param {Number} Key for the value you wish to subtract from - * @param {Number} Number to subtract from the value - * @example - *
- * - * function setup() { - * let myDictionary = createNumberDict(2, 5); - * myDictionary.sub(2, 2); - * print(myDictionary.get(2)); // logs 3 to console. - * } - *
- * - */ -p5.NumberDict.prototype.sub = function(key, amount) { - this.add(key, -amount); -}; + /** + * private helper function to ensure that the user passed in valid + * values for the Dictionary type + */ -/** - * Multiply the given number with the value currently stored at the given key. - * The product then replaces the value previously stored in the Dictionary. - * - * @method mult - * @param {Number} Key for value you wish to multiply - * @param {Number} Amount to multiply the value by - * @example - *
- * - * function setup() { - * let myDictionary = createNumberDict(2, 4); - * myDictionary.mult(2, 2); - * print(myDictionary.get(2)); // logs 8 to console. - * } - *
- * - */ + _validate(value) { + return typeof value === 'number'; + } -p5.NumberDict.prototype.mult = function(key, amount) { - if (this.data.hasOwnProperty(key)) { - this.data[key] *= amount; - } else { - console.log(`The key - ${key} does not exist in this dictionary.`); + /** + * Add the given number to the value currently stored at the given key. + * The sum then replaces the value previously stored in the Dictionary. + * + * @method add + * @param {Number} Key for the value you wish to add to + * @param {Number} Number to add to the value + * @example + *
+ * + * function setup() { + * let myDictionary = createNumberDict(2, 5); + * myDictionary.add(2, 2); + * print(myDictionary.get(2)); // logs 7 to console. + * } + *
+ * + */ + + add(key, amount) { + if (this.data.hasOwnProperty(key)) { + this.data[key] += amount; + } else { + console.log(`The key - ${key} does not exist in this dictionary.`); + } } -}; -/** - * Divide the given number with the value currently stored at the given key. - * The quotient then replaces the value previously stored in the Dictionary. - * - * @method div - * @param {Number} Key for value you wish to divide - * @param {Number} Amount to divide the value by - * @example - *
- * - * function setup() { - * let myDictionary = createNumberDict(2, 8); - * myDictionary.div(2, 2); - * print(myDictionary.get(2)); // logs 4 to console. - * } - *
- * - */ + /** + * Subtract the given number from the value currently stored at the given key. + * The difference then replaces the value previously stored in the Dictionary. + * + * @method sub + * @param {Number} Key for the value you wish to subtract from + * @param {Number} Number to subtract from the value + * @example + *
+ * + * function setup() { + * let myDictionary = createNumberDict(2, 5); + * myDictionary.sub(2, 2); + * print(myDictionary.get(2)); // logs 3 to console. + * } + *
+ * + */ + + sub(key, amount) { + this.add(key, -amount); + } -p5.NumberDict.prototype.div = function(key, amount) { - if (this.data.hasOwnProperty(key)) { - this.data[key] /= amount; - } else { - console.log(`The key - ${key} does not exist in this dictionary.`); + /** + * Multiply the given number with the value currently stored at the given key. + * The product then replaces the value previously stored in the Dictionary. + * + * @method mult + * @param {Number} Key for value you wish to multiply + * @param {Number} Amount to multiply the value by + * @example + *
+ * + * function setup() { + * let myDictionary = createNumberDict(2, 4); + * myDictionary.mult(2, 2); + * print(myDictionary.get(2)); // logs 8 to console. + * } + *
+ * + */ + + mult(key, amount) { + if (this.data.hasOwnProperty(key)) { + this.data[key] *= amount; + } else { + console.log(`The key - ${key} does not exist in this dictionary.`); + } } -}; -/** - * private helper function for finding lowest or highest value - * the argument 'flip' is used to flip the comparison arrow - * from 'less than' to 'greater than' - */ + /** + * Divide the given number with the value currently stored at the given key. + * The quotient then replaces the value previously stored in the Dictionary. + * + * @method div + * @param {Number} Key for value you wish to divide + * @param {Number} Amount to divide the value by + * @example + *
+ * + * function setup() { + * let myDictionary = createNumberDict(2, 8); + * myDictionary.div(2, 2); + * print(myDictionary.get(2)); // logs 4 to console. + * } + *
+ * + */ + + div(key, amount) { + if (this.data.hasOwnProperty(key)) { + this.data[key] /= amount; + } else { + console.log(`The key - ${key} does not exist in this dictionary.`); + } + } -p5.NumberDict.prototype._valueTest = function(flip) { - if (Object.keys(this.data).length === 0) { - throw new Error( - 'Unable to search for a minimum or maximum value on an empty NumberDict' - ); - } else if (Object.keys(this.data).length === 1) { - return this.data[Object.keys(this.data)[0]]; - } else { - let result = this.data[Object.keys(this.data)[0]]; - for (const key in this.data) { - if (this.data[key] * flip < result * flip) { - result = this.data[key]; + /** + * private helper function for finding lowest or highest value + * the argument 'flip' is used to flip the comparison arrow + * from 'less than' to 'greater than' + */ + + _valueTest(flip) { + if (Object.keys(this.data).length === 0) { + throw new Error( + 'Unable to search for a minimum or maximum value on an empty NumberDict' + ); + } else if (Object.keys(this.data).length === 1) { + return this.data[Object.keys(this.data)[0]]; + } else { + let result = this.data[Object.keys(this.data)[0]]; + for (const key in this.data) { + if (this.data[key] * flip < result * flip) { + result = this.data[key]; + } } + return result; } - return result; } -}; - -/** - * Return the lowest number currently stored in the Dictionary. - * - * @method minValue - * @return {Number} - * @example - *
- * - * function setup() { - * let myDictionary = createNumberDict({ 2: -10, 4: 0.65, 1.2: 3 }); - * let lowestValue = myDictionary.minValue(); // value is -10 - * print(lowestValue); - * } - *
- */ - -p5.NumberDict.prototype.minValue = function() { - return this._valueTest(1); -}; - -/** - * Return the highest number currently stored in the Dictionary. - * - * @method maxValue - * @return {Number} - * @example - *
- * - * function setup() { - * let myDictionary = createNumberDict({ 2: -10, 4: 0.65, 1.2: 3 }); - * let highestValue = myDictionary.maxValue(); // value is 3 - * print(highestValue); - * } - *
- */ -p5.NumberDict.prototype.maxValue = function() { - return this._valueTest(-1); -}; + /** + * Return the lowest number currently stored in the Dictionary. + * + * @method minValue + * @return {Number} + * @example + *
+ * + * function setup() { + * let myDictionary = createNumberDict({ 2: -10, 4: 0.65, 1.2: 3 }); + * let lowestValue = myDictionary.minValue(); // value is -10 + * print(lowestValue); + * } + *
+ */ + + minValue() { + return this._valueTest(1); + } -/** - * private helper function for finding lowest or highest key - * the argument 'flip' is used to flip the comparison arrow - * from 'less than' to 'greater than' - */ + /** + * Return the highest number currently stored in the Dictionary. + * + * @method maxValue + * @return {Number} + * @example + *
+ * + * function setup() { + * let myDictionary = createNumberDict({ 2: -10, 4: 0.65, 1.2: 3 }); + * let highestValue = myDictionary.maxValue(); // value is 3 + * print(highestValue); + * } + *
+ */ + + maxValue() { + return this._valueTest(-1); + } -p5.NumberDict.prototype._keyTest = function(flip) { - if (Object.keys(this.data).length === 0) { - throw new Error('Unable to use minValue on an empty NumberDict'); - } else if (Object.keys(this.data).length === 1) { - return Object.keys(this.data)[0]; - } else { - let result = Object.keys(this.data)[0]; - for (let i = 1; i < Object.keys(this.data).length; i++) { - if (Object.keys(this.data)[i] * flip < result * flip) { - result = Object.keys(this.data)[i]; + /** + * private helper function for finding lowest or highest key + * the argument 'flip' is used to flip the comparison arrow + * from 'less than' to 'greater than' + */ + + _keyTest(flip) { + if (Object.keys(this.data).length === 0) { + throw new Error('Unable to use minValue on an empty NumberDict'); + } else if (Object.keys(this.data).length === 1) { + return Object.keys(this.data)[0]; + } else { + let result = Object.keys(this.data)[0]; + for (let i = 1; i < Object.keys(this.data).length; i++) { + if (Object.keys(this.data)[i] * flip < result * flip) { + result = Object.keys(this.data)[i]; + } } + return result; } - return result; } -}; - -/** - * Return the lowest key currently used in the Dictionary. - * - * @method minKey - * @return {Number} - * @example - *
- * - * function setup() { - * let myDictionary = createNumberDict({ 2: 4, 4: 6, 1.2: 3 }); - * let lowestKey = myDictionary.minKey(); // value is 1.2 - * print(lowestKey); - * } - *
- */ -p5.NumberDict.prototype.minKey = function() { - return this._keyTest(1); -}; - -/** - * Return the highest key currently used in the Dictionary. - * - * @method maxKey - * @return {Number} - * @example - *
- * - * function setup() { - * let myDictionary = createNumberDict({ 2: 4, 4: 6, 1.2: 3 }); - * let highestKey = myDictionary.maxKey(); // value is 4 - * print(highestKey); - * } - *
- */ + /** + * Return the lowest key currently used in the Dictionary. + * + * @method minKey + * @return {Number} + * @example + *
+ * + * function setup() { + * let myDictionary = createNumberDict({ 2: 4, 4: 6, 1.2: 3 }); + * let lowestKey = myDictionary.minKey(); // value is 1.2 + * print(lowestKey); + * } + *
+ */ + + minKey() { + return this._keyTest(1); + } -p5.NumberDict.prototype.maxKey = function() { - return this._keyTest(-1); + /** + * Return the highest key currently used in the Dictionary. + * + * @method maxKey + * @return {Number} + * @example + *
+ * + * function setup() { + * let myDictionary = createNumberDict({ 2: 4, 4: 6, 1.2: 3 }); + * let highestKey = myDictionary.maxKey(); // value is 4 + * print(highestKey); + * } + *
+ */ + + maxKey() { + return this._keyTest(-1); + } }; export default p5.TypedDict; From 699de84795f4a32ca71b362efd7005d7b15f15d7 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Wed, 31 May 2023 13:54:05 +0900 Subject: [PATCH 20/22] finish rewrite MediaElement --- src/dom/dom.js | 1741 ++++++++++++++++++++++++------------------------ 1 file changed, 872 insertions(+), 869 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 4287c65447..dcca5da76a 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -2446,6 +2446,7 @@ p5.MediaElement = class MediaElement extends p5.Element { this._prevTime = 0; this._cueIDCounter = 0; this._cues = []; + this.pixels = []; this._pixelsState = this; this._pixelDensity = 1; this._modified = false; @@ -2521,917 +2522,919 @@ p5.MediaElement = class MediaElement extends p5.Element { self._onended(self); }; } -}; -/** - * Play an HTML5 media element. - * - * @method play - * @chainable - * @example - *
- * let ele; - * - * function setup() { - * //p5.MediaElement objects are usually created - * //by calling the createAudio(), createVideo(), - * //and createCapture() functions. - * - * //In this example we create - * //a new p5.MediaElement via createAudio(). - * ele = createAudio('assets/beat.mp3'); - * - * background(250); - * textAlign(CENTER); - * text('Click to Play!', width / 2, height / 2); - * } - * - * function mouseClicked() { - * //here we test if the mouse is over the - * //canvas element when it's clicked - * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { - * //Here we call the play() function on - * //the p5.MediaElement we created above. - * //This will start the audio sample. - * ele.play(); - * - * background(200); - * text('You clicked Play!', width / 2, height / 2); - * } - * } - *
- */ -p5.MediaElement.prototype.play = function () { - if (this.elt.currentTime === this.elt.duration) { - this.elt.currentTime = 0; - } - let promise; - if (this.elt.readyState > 1) { - promise = this.elt.play(); - } else { - // in Chrome, playback cannot resume after being stopped and must reload - this.elt.load(); - promise = this.elt.play(); - } - if (promise && promise.catch) { - promise.catch(e => { - // if it's an autoplay failure error - if (e.name === 'NotAllowedError') { - if (typeof IS_MINIFIED === 'undefined') { - p5._friendlyAutoplayError(this.src); + + /** + * Play an HTML5 media element. + * + * @method play + * @chainable + * @example + *
+ * let ele; + * + * function setup() { + * //p5.MediaElement objects are usually created + * //by calling the createAudio(), createVideo(), + * //and createCapture() functions. + * + * //In this example we create + * //a new p5.MediaElement via createAudio(). + * ele = createAudio('assets/beat.mp3'); + * + * background(250); + * textAlign(CENTER); + * text('Click to Play!', width / 2, height / 2); + * } + * + * function mouseClicked() { + * //here we test if the mouse is over the + * //canvas element when it's clicked + * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { + * //Here we call the play() function on + * //the p5.MediaElement we created above. + * //This will start the audio sample. + * ele.play(); + * + * background(200); + * text('You clicked Play!', width / 2, height / 2); + * } + * } + *
+ */ + play() { + if (this.elt.currentTime === this.elt.duration) { + this.elt.currentTime = 0; + } + let promise; + if (this.elt.readyState > 1) { + promise = this.elt.play(); + } else { + // in Chrome, playback cannot resume after being stopped and must reload + this.elt.load(); + promise = this.elt.play(); + } + if (promise && promise.catch) { + promise.catch(e => { + // if it's an autoplay failure error + if (e.name === 'NotAllowedError') { + if (typeof IS_MINIFIED === 'undefined') { + p5._friendlyAutoplayError(this.src); + } else { + console.error(e); + } } else { - console.error(e); + // any other kind of error + console.error('Media play method encountered an unexpected error', e); } - } else { - // any other kind of error - console.error('Media play method encountered an unexpected error', e); - } - }); + }); + } + return this; } - return this; -}; -/** - * Stops an HTML5 media element (sets current time to zero). - * - * @method stop - * @chainable - * @example - *
- * //This example both starts - * //and stops a sound sample - * //when the user clicks the canvas - * - * //We will store the p5.MediaElement - * //object in here - * let ele; - * - * //while our audio is playing, - * //this will be set to true - * let sampleIsPlaying = false; - * - * function setup() { - * //Here we create a p5.MediaElement object - * //using the createAudio() function. - * ele = createAudio('assets/beat.mp3'); - * background(200); - * textAlign(CENTER); - * text('Click to play!', width / 2, height / 2); - * } - * - * function mouseClicked() { - * //here we test if the mouse is over the - * //canvas element when it's clicked - * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { - * background(200); - * - * if (sampleIsPlaying) { - * //if the sample is currently playing - * //calling the stop() function on - * //our p5.MediaElement will stop - * //it and reset its current - * //time to 0 (i.e. it will start - * //at the beginning the next time - * //you play it) - * ele.stop(); - * - * sampleIsPlaying = false; - * text('Click to play!', width / 2, height / 2); - * } else { - * //loop our sound element until we - * //call ele.stop() on it. - * ele.loop(); - * - * sampleIsPlaying = true; - * text('Click to stop!', width / 2, height / 2); - * } - * } - * } - *
- */ -p5.MediaElement.prototype.stop = function () { - this.elt.pause(); - this.elt.currentTime = 0; - return this; -}; + /** + * Stops an HTML5 media element (sets current time to zero). + * + * @method stop + * @chainable + * @example + *
+ * //This example both starts + * //and stops a sound sample + * //when the user clicks the canvas + * + * //We will store the p5.MediaElement + * //object in here + * let ele; + * + * //while our audio is playing, + * //this will be set to true + * let sampleIsPlaying = false; + * + * function setup() { + * //Here we create a p5.MediaElement object + * //using the createAudio() function. + * ele = createAudio('assets/beat.mp3'); + * background(200); + * textAlign(CENTER); + * text('Click to play!', width / 2, height / 2); + * } + * + * function mouseClicked() { + * //here we test if the mouse is over the + * //canvas element when it's clicked + * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { + * background(200); + * + * if (sampleIsPlaying) { + * //if the sample is currently playing + * //calling the stop() function on + * //our p5.MediaElement will stop + * //it and reset its current + * //time to 0 (i.e. it will start + * //at the beginning the next time + * //you play it) + * ele.stop(); + * + * sampleIsPlaying = false; + * text('Click to play!', width / 2, height / 2); + * } else { + * //loop our sound element until we + * //call ele.stop() on it. + * ele.loop(); + * + * sampleIsPlaying = true; + * text('Click to stop!', width / 2, height / 2); + * } + * } + * } + *
+ */ + stop() { + this.elt.pause(); + this.elt.currentTime = 0; + return this; + } -/** - * Pauses an HTML5 media element. - * - * @method pause - * @chainable - * @example - *
- * //This example both starts - * //and pauses a sound sample - * //when the user clicks the canvas - * - * //We will store the p5.MediaElement - * //object in here - * let ele; - * - * //while our audio is playing, - * //this will be set to true - * let sampleIsPlaying = false; - * - * function setup() { - * //Here we create a p5.MediaElement object - * //using the createAudio() function. - * ele = createAudio('assets/lucky_dragons.mp3'); - * background(200); - * textAlign(CENTER); - * text('Click to play!', width / 2, height / 2); - * } - * - * function mouseClicked() { - * //here we test if the mouse is over the - * //canvas element when it's clicked - * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { - * background(200); - * - * if (sampleIsPlaying) { - * //Calling pause() on our - * //p5.MediaElement will stop it - * //playing, but when we call the - * //loop() or play() functions - * //the sample will start from - * //where we paused it. - * ele.pause(); - * - * sampleIsPlaying = false; - * text('Click to resume!', width / 2, height / 2); - * } else { - * //loop our sound element until we - * //call ele.pause() on it. - * ele.loop(); - * - * sampleIsPlaying = true; - * text('Click to pause!', width / 2, height / 2); - * } - * } - * } - *
- */ -p5.MediaElement.prototype.pause = function () { - this.elt.pause(); - return this; -}; + /** + * Pauses an HTML5 media element. + * + * @method pause + * @chainable + * @example + *
+ * //This example both starts + * //and pauses a sound sample + * //when the user clicks the canvas + * + * //We will store the p5.MediaElement + * //object in here + * let ele; + * + * //while our audio is playing, + * //this will be set to true + * let sampleIsPlaying = false; + * + * function setup() { + * //Here we create a p5.MediaElement object + * //using the createAudio() function. + * ele = createAudio('assets/lucky_dragons.mp3'); + * background(200); + * textAlign(CENTER); + * text('Click to play!', width / 2, height / 2); + * } + * + * function mouseClicked() { + * //here we test if the mouse is over the + * //canvas element when it's clicked + * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { + * background(200); + * + * if (sampleIsPlaying) { + * //Calling pause() on our + * //p5.MediaElement will stop it + * //playing, but when we call the + * //loop() or play() functions + * //the sample will start from + * //where we paused it. + * ele.pause(); + * + * sampleIsPlaying = false; + * text('Click to resume!', width / 2, height / 2); + * } else { + * //loop our sound element until we + * //call ele.pause() on it. + * ele.loop(); + * + * sampleIsPlaying = true; + * text('Click to pause!', width / 2, height / 2); + * } + * } + * } + *
+ */ + pause() { + this.elt.pause(); + return this; + } -/** - * Set 'loop' to true for an HTML5 media element, and starts playing. - * - * @method loop - * @chainable - * @example - *
- * //Clicking the canvas will loop - * //the audio sample until the user - * //clicks again to stop it - * - * //We will store the p5.MediaElement - * //object in here - * let ele; - * - * //while our audio is playing, - * //this will be set to true - * let sampleIsLooping = false; - * - * function setup() { - * //Here we create a p5.MediaElement object - * //using the createAudio() function. - * ele = createAudio('assets/lucky_dragons.mp3'); - * background(200); - * textAlign(CENTER); - * text('Click to loop!', width / 2, height / 2); - * } - * - * function mouseClicked() { - * //here we test if the mouse is over the - * //canvas element when it's clicked - * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { - * background(200); - * - * if (!sampleIsLooping) { - * //loop our sound element until we - * //call ele.stop() on it. - * ele.loop(); - * - * sampleIsLooping = true; - * text('Click to stop!', width / 2, height / 2); - * } else { - * ele.stop(); - * - * sampleIsLooping = false; - * text('Click to loop!', width / 2, height / 2); - * } - * } - * } - *
- */ -p5.MediaElement.prototype.loop = function () { - this.elt.setAttribute('loop', true); - this.play(); - return this; -}; -/** - * Set 'loop' to false for an HTML5 media element. Element will stop - * when it reaches the end. - * - * @method noLoop - * @chainable - * @example - *
- * //This example both starts - * //and stops loop of sound sample - * //when the user clicks the canvas - * - * //We will store the p5.MediaElement - * //object in here - * let ele; - * //while our audio is playing, - * //this will be set to true - * let sampleIsPlaying = false; - * - * function setup() { - * //Here we create a p5.MediaElement object - * //using the createAudio() function. - * ele = createAudio('assets/beat.mp3'); - * background(200); - * textAlign(CENTER); - * text('Click to play!', width / 2, height / 2); - * } - * - * function mouseClicked() { - * //here we test if the mouse is over the - * //canvas element when it's clicked - * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { - * background(200); - * - * if (sampleIsPlaying) { - * ele.noLoop(); - * sampleIsPlaying = false; - * text('No more Loops!', width / 2, height / 2); - * } else { - * ele.loop(); - * sampleIsPlaying = true; - * text('Click to stop looping!', width / 2, height / 2); - * } - * } - * } - *
- */ -p5.MediaElement.prototype.noLoop = function () { - this.elt.removeAttribute('loop'); - return this; -}; + /** + * Set 'loop' to true for an HTML5 media element, and starts playing. + * + * @method loop + * @chainable + * @example + *
+ * //Clicking the canvas will loop + * //the audio sample until the user + * //clicks again to stop it + * + * //We will store the p5.MediaElement + * //object in here + * let ele; + * + * //while our audio is playing, + * //this will be set to true + * let sampleIsLooping = false; + * + * function setup() { + * //Here we create a p5.MediaElement object + * //using the createAudio() function. + * ele = createAudio('assets/lucky_dragons.mp3'); + * background(200); + * textAlign(CENTER); + * text('Click to loop!', width / 2, height / 2); + * } + * + * function mouseClicked() { + * //here we test if the mouse is over the + * //canvas element when it's clicked + * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { + * background(200); + * + * if (!sampleIsLooping) { + * //loop our sound element until we + * //call ele.stop() on it. + * ele.loop(); + * + * sampleIsLooping = true; + * text('Click to stop!', width / 2, height / 2); + * } else { + * ele.stop(); + * + * sampleIsLooping = false; + * text('Click to loop!', width / 2, height / 2); + * } + * } + * } + *
+ */ + loop() { + this.elt.setAttribute('loop', true); + this.play(); + return this; + } + /** + * Set 'loop' to false for an HTML5 media element. Element will stop + * when it reaches the end. + * + * @method noLoop + * @chainable + * @example + *
+ * //This example both starts + * //and stops loop of sound sample + * //when the user clicks the canvas + * + * //We will store the p5.MediaElement + * //object in here + * let ele; + * //while our audio is playing, + * //this will be set to true + * let sampleIsPlaying = false; + * + * function setup() { + * //Here we create a p5.MediaElement object + * //using the createAudio() function. + * ele = createAudio('assets/beat.mp3'); + * background(200); + * textAlign(CENTER); + * text('Click to play!', width / 2, height / 2); + * } + * + * function mouseClicked() { + * //here we test if the mouse is over the + * //canvas element when it's clicked + * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { + * background(200); + * + * if (sampleIsPlaying) { + * ele.noLoop(); + * sampleIsPlaying = false; + * text('No more Loops!', width / 2, height / 2); + * } else { + * ele.loop(); + * sampleIsPlaying = true; + * text('Click to stop looping!', width / 2, height / 2); + * } + * } + * } + *
+ */ + noLoop() { + this.elt.removeAttribute('loop'); + return this; + } -/** - * Sets up logic to check that autoplay succeeded. - * - * @method setupAutoplayFailDetection - * @private - */ -p5.MediaElement.prototype._setupAutoplayFailDetection = function () { - const timeout = setTimeout(() => { - if (typeof IS_MINIFIED === 'undefined') { - p5._friendlyAutoplayError(this.src); - } else { - console.error(e); + /** + * Sets up logic to check that autoplay succeeded. + * + * @method setupAutoplayFailDetection + * @private + */ + _setupAutoplayFailDetection() { + const timeout = setTimeout(() => { + if (typeof IS_MINIFIED === 'undefined') { + p5._friendlyAutoplayError(this.src); + } else { + console.error(e); + } + }, 500); + this.elt.addEventListener('play', () => clearTimeout(timeout), { + passive: true, + once: true + }); + } + + /** + * Set HTML5 media element to autoplay or not. If no argument is specified, by + * default it will autoplay. + * + * @method autoplay + * @param {Boolean} shouldAutoplay whether the element should autoplay + * @chainable + * @example + *
+ * let videoElement; + * function setup() { + * noCanvas(); + * videoElement = createVideo(['assets/small.mp4'], onVideoLoad); + * } + * function onVideoLoad() { + * // The media will play as soon as it is loaded. + * videoElement.autoplay(); + * videoElement.volume(0); + * videoElement.size(100, 100); + * } + *
+ * + *
+ * let videoElement; + * function setup() { + * noCanvas(); + * videoElement = createVideo(['assets/small.mp4'], onVideoLoad); + * } + * function onVideoLoad() { + * // The media will not play until some explicitly triggered. + * videoElement.autoplay(false); + * videoElement.volume(0); + * videoElement.size(100, 100); + * } + * + * function mouseClicked() { + * videoElement.play(); + * } + *
+ * + * @alt + * An example of a video element which autoplays after it is loaded. + * An example of a video element which waits for a trigger for playing. + */ + + autoplay(val) { + const oldVal = this.elt.getAttribute('autoplay'); + this.elt.setAttribute('autoplay', val); + // if we turned on autoplay + if (val && !oldVal) { + // bind method to this scope + const setupAutoplayFailDetection = + () => this._setupAutoplayFailDetection(); + // if media is ready to play, schedule check now + if (this.elt.readyState === 4) { + setupAutoplayFailDetection(); + } else { + // otherwise, schedule check whenever it is ready + this.elt.addEventListener('canplay', setupAutoplayFailDetection, { + passive: true, + once: true + }); + } } - }, 500); - this.elt.addEventListener('play', () => clearTimeout(timeout), { - passive: true, - once: true - }); -}; -/** - * Set HTML5 media element to autoplay or not. If no argument is specified, by - * default it will autoplay. - * - * @method autoplay - * @param {Boolean} shouldAutoplay whether the element should autoplay - * @chainable - * @example - *
- * let videoElement; - * function setup() { - * noCanvas(); - * videoElement = createVideo(['assets/small.mp4'], onVideoLoad); - * } - * function onVideoLoad() { - * // The media will play as soon as it is loaded. - * videoElement.autoplay(); - * videoElement.volume(0); - * videoElement.size(100, 100); - * } - *
- * - *
- * let videoElement; - * function setup() { - * noCanvas(); - * videoElement = createVideo(['assets/small.mp4'], onVideoLoad); - * } - * function onVideoLoad() { - * // The media will not play until some explicitly triggered. - * videoElement.autoplay(false); - * videoElement.volume(0); - * videoElement.size(100, 100); - * } - * - * function mouseClicked() { - * videoElement.play(); - * } - *
- * - * @alt - * An example of a video element which autoplays after it is loaded. - * An example of a video element which waits for a trigger for playing. - */ + return this; + } -p5.MediaElement.prototype.autoplay = function (val) { - const oldVal = this.elt.getAttribute('autoplay'); - this.elt.setAttribute('autoplay', val); - // if we turned on autoplay - if (val && !oldVal) { - // bind method to this scope - const setupAutoplayFailDetection = () => this._setupAutoplayFailDetection(); - // if media is ready to play, schedule check now - if (this.elt.readyState === 4) { - setupAutoplayFailDetection(); + /** + * Sets volume for this HTML5 media element. If no argument is given, + * returns the current volume. + * + * @method volume + * @return {Number} current volume + * + * @example + *
+ * let ele; + * function setup() { + * // p5.MediaElement objects are usually created + * // by calling the createAudio(), createVideo(), + * // and createCapture() functions. + * // In this example we create + * // a new p5.MediaElement via createAudio(). + * ele = createAudio('assets/lucky_dragons.mp3'); + * background(250); + * textAlign(CENTER); + * text('Click to Play!', width / 2, height / 2); + * } + * function mouseClicked() { + * // Here we call the volume() function + * // on the sound element to set its volume + * // Volume must be between 0.0 and 1.0 + * ele.volume(0.2); + * ele.play(); + * background(200); + * text('You clicked Play!', width / 2, height / 2); + * } + *
+ *
+ * let audio; + * let counter = 0; + * + * function loaded() { + * audio.play(); + * } + * + * function setup() { + * audio = createAudio('assets/lucky_dragons.mp3', loaded); + * textAlign(CENTER); + * } + * + * function draw() { + * if (counter === 0) { + * background(0, 255, 0); + * text('volume(0.9)', width / 2, height / 2); + * } else if (counter === 1) { + * background(255, 255, 0); + * text('volume(0.5)', width / 2, height / 2); + * } else if (counter === 2) { + * background(255, 0, 0); + * text('volume(0.1)', width / 2, height / 2); + * } + * } + * + * function mousePressed() { + * counter++; + * if (counter === 0) { + * audio.volume(0.9); + * } else if (counter === 1) { + * audio.volume(0.5); + * } else if (counter === 2) { + * audio.volume(0.1); + * } else { + * counter = 0; + * audio.volume(0.9); + * } + * } + * + *
+ */ + /** + * @method volume + * @param {Number} val volume between 0.0 and 1.0 + * @chainable + */ + volume(val) { + if (typeof val === 'undefined') { + return this.elt.volume; } else { - // otherwise, schedule check whenever it is ready - this.elt.addEventListener('canplay', setupAutoplayFailDetection, { - passive: true, - once: true - }); + this.elt.volume = val; } } - return this; -}; - -/** - * Sets volume for this HTML5 media element. If no argument is given, - * returns the current volume. - * - * @method volume - * @return {Number} current volume - * - * @example - *
- * let ele; - * function setup() { - * // p5.MediaElement objects are usually created - * // by calling the createAudio(), createVideo(), - * // and createCapture() functions. - * // In this example we create - * // a new p5.MediaElement via createAudio(). - * ele = createAudio('assets/lucky_dragons.mp3'); - * background(250); - * textAlign(CENTER); - * text('Click to Play!', width / 2, height / 2); - * } - * function mouseClicked() { - * // Here we call the volume() function - * // on the sound element to set its volume - * // Volume must be between 0.0 and 1.0 - * ele.volume(0.2); - * ele.play(); - * background(200); - * text('You clicked Play!', width / 2, height / 2); - * } - *
- *
- * let audio; - * let counter = 0; - * - * function loaded() { - * audio.play(); - * } - * - * function setup() { - * audio = createAudio('assets/lucky_dragons.mp3', loaded); - * textAlign(CENTER); - * } - * - * function draw() { - * if (counter === 0) { - * background(0, 255, 0); - * text('volume(0.9)', width / 2, height / 2); - * } else if (counter === 1) { - * background(255, 255, 0); - * text('volume(0.5)', width / 2, height / 2); - * } else if (counter === 2) { - * background(255, 0, 0); - * text('volume(0.1)', width / 2, height / 2); - * } - * } - * - * function mousePressed() { - * counter++; - * if (counter === 0) { - * audio.volume(0.9); - * } else if (counter === 1) { - * audio.volume(0.5); - * } else if (counter === 2) { - * audio.volume(0.1); - * } else { - * counter = 0; - * audio.volume(0.9); - * } - * } - * - *
- */ -/** - * @method volume - * @param {Number} val volume between 0.0 and 1.0 - * @chainable - */ -p5.MediaElement.prototype.volume = function (val) { - if (typeof val === 'undefined') { - return this.elt.volume; - } else { - this.elt.volume = val; + /** + * If no arguments are given, returns the current playback speed of the + * element. The speed parameter sets the speed where 2.0 will play the + * element twice as fast, 0.5 will play at half the speed, and -1 will play + * the element in normal speed in reverse.(Note that not all browsers support + * backward playback and even if they do, playback might not be smooth.) + * + * @method speed + * @return {Number} current playback speed of the element + * + * @example + *
+ * //Clicking the canvas will loop + * //the audio sample until the user + * //clicks again to stop it + * + * //We will store the p5.MediaElement + * //object in here + * let ele; + * let button; + * + * function setup() { + * createCanvas(710, 400); + * //Here we create a p5.MediaElement object + * //using the createAudio() function. + * ele = createAudio('assets/beat.mp3'); + * ele.loop(); + * background(200); + * + * button = createButton('2x speed'); + * button.position(100, 68); + * button.mousePressed(twice_speed); + * + * button = createButton('half speed'); + * button.position(200, 68); + * button.mousePressed(half_speed); + * + * button = createButton('reverse play'); + * button.position(300, 68); + * button.mousePressed(reverse_speed); + * + * button = createButton('STOP'); + * button.position(400, 68); + * button.mousePressed(stop_song); + * + * button = createButton('PLAY!'); + * button.position(500, 68); + * button.mousePressed(play_speed); + * } + * + * function twice_speed() { + * ele.speed(2); + * } + * + * function half_speed() { + * ele.speed(0.5); + * } + * + * function reverse_speed() { + * ele.speed(-1); + * } + * + * function stop_song() { + * ele.stop(); + * } + * + * function play_speed() { + * ele.play(); + * } + *
+ */ + + /** + * @method speed + * @param {Number} speed speed multiplier for element playback + * @chainable + */ + speed(val) { + if (typeof val === 'undefined') { + return this.presetPlaybackRate || this.elt.playbackRate; + } else { + if (this.loadedmetadata) { + this.elt.playbackRate = val; + } else { + this.presetPlaybackRate = val; + } + } } -}; - -/** - * If no arguments are given, returns the current playback speed of the - * element. The speed parameter sets the speed where 2.0 will play the - * element twice as fast, 0.5 will play at half the speed, and -1 will play - * the element in normal speed in reverse.(Note that not all browsers support - * backward playback and even if they do, playback might not be smooth.) - * - * @method speed - * @return {Number} current playback speed of the element - * - * @example - *
- * //Clicking the canvas will loop - * //the audio sample until the user - * //clicks again to stop it - * - * //We will store the p5.MediaElement - * //object in here - * let ele; - * let button; - * - * function setup() { - * createCanvas(710, 400); - * //Here we create a p5.MediaElement object - * //using the createAudio() function. - * ele = createAudio('assets/beat.mp3'); - * ele.loop(); - * background(200); - * - * button = createButton('2x speed'); - * button.position(100, 68); - * button.mousePressed(twice_speed); - * - * button = createButton('half speed'); - * button.position(200, 68); - * button.mousePressed(half_speed); - * - * button = createButton('reverse play'); - * button.position(300, 68); - * button.mousePressed(reverse_speed); - * - * button = createButton('STOP'); - * button.position(400, 68); - * button.mousePressed(stop_song); - * - * button = createButton('PLAY!'); - * button.position(500, 68); - * button.mousePressed(play_speed); - * } - * - * function twice_speed() { - * ele.speed(2); - * } - * - * function half_speed() { - * ele.speed(0.5); - * } - * - * function reverse_speed() { - * ele.speed(-1); - * } - * - * function stop_song() { - * ele.stop(); - * } - * - * function play_speed() { - * ele.play(); - * } - *
- */ -/** - * @method speed - * @param {Number} speed speed multiplier for element playback - * @chainable - */ -p5.MediaElement.prototype.speed = function (val) { - if (typeof val === 'undefined') { - return this.presetPlaybackRate || this.elt.playbackRate; - } else { - if (this.loadedmetadata) { - this.elt.playbackRate = val; + /** + * If no arguments are given, returns the current time of the element. + * If an argument is given the current time of the element is set to it. + * + * @method time + * @return {Number} current time (in seconds) + * + * @example + *
+ * let ele; + * let beginning = true; + * function setup() { + * //p5.MediaElement objects are usually created + * //by calling the createAudio(), createVideo(), + * //and createCapture() functions. + * + * //In this example we create + * //a new p5.MediaElement via createAudio(). + * ele = createAudio('assets/lucky_dragons.mp3'); + * background(250); + * textAlign(CENTER); + * text('start at beginning', width / 2, height / 2); + * } + * + * // this function fires with click anywhere + * function mousePressed() { + * if (beginning === true) { + * // here we start the sound at the beginning + * // time(0) is not necessary here + * // as this produces the same result as + * // play() + * ele.play().time(0); + * background(200); + * text('jump 2 sec in', width / 2, height / 2); + * beginning = false; + * } else { + * // here we jump 2 seconds into the sound + * ele.play().time(2); + * background(250); + * text('start at beginning', width / 2, height / 2); + * beginning = true; + * } + * } + *
+ */ + /** + * @method time + * @param {Number} time time to jump to (in seconds) + * @chainable + */ + time(val) { + if (typeof val === 'undefined') { + return this.elt.currentTime; } else { - this.presetPlaybackRate = val; + this.elt.currentTime = val; + return this; } } -}; -/** - * If no arguments are given, returns the current time of the element. - * If an argument is given the current time of the element is set to it. - * - * @method time - * @return {Number} current time (in seconds) - * - * @example - *
- * let ele; - * let beginning = true; - * function setup() { - * //p5.MediaElement objects are usually created - * //by calling the createAudio(), createVideo(), - * //and createCapture() functions. - * - * //In this example we create - * //a new p5.MediaElement via createAudio(). - * ele = createAudio('assets/lucky_dragons.mp3'); - * background(250); - * textAlign(CENTER); - * text('start at beginning', width / 2, height / 2); - * } - * - * // this function fires with click anywhere - * function mousePressed() { - * if (beginning === true) { - * // here we start the sound at the beginning - * // time(0) is not necessary here - * // as this produces the same result as - * // play() - * ele.play().time(0); - * background(200); - * text('jump 2 sec in', width / 2, height / 2); - * beginning = false; - * } else { - * // here we jump 2 seconds into the sound - * ele.play().time(2); - * background(250); - * text('start at beginning', width / 2, height / 2); - * beginning = true; - * } - * } - *
- */ -/** - * @method time - * @param {Number} time time to jump to (in seconds) - * @chainable - */ -p5.MediaElement.prototype.time = function (val) { - if (typeof val === 'undefined') { - return this.elt.currentTime; - } else { - this.elt.currentTime = val; - return this; - } -}; + /** + * Returns the duration of the HTML5 media element. + * + * @method duration + * @return {Number} duration + * + * @example + *
+ * let ele; + * function setup() { + * //p5.MediaElement objects are usually created + * //by calling the createAudio(), createVideo(), + * //and createCapture() functions. + * //In this example we create + * //a new p5.MediaElement via createAudio(). + * ele = createAudio('assets/doorbell.mp3'); + * background(250); + * textAlign(CENTER); + * text('Click to know the duration!', 10, 25, 70, 80); + * } + * function mouseClicked() { + * ele.play(); + * background(200); + * //ele.duration dislpays the duration + * text(ele.duration() + ' seconds', width / 2, height / 2); + * } + *
+ */ + duration() { + return this.elt.duration; + } + + _ensureCanvas() { + if (!this.canvas) { + this.canvas = document.createElement('canvas'); + this.drawingContext = this.canvas.getContext('2d'); + this.setModified(true); + } -/** - * Returns the duration of the HTML5 media element. - * - * @method duration - * @return {Number} duration - * - * @example - *
- * let ele; - * function setup() { - * //p5.MediaElement objects are usually created - * //by calling the createAudio(), createVideo(), - * //and createCapture() functions. - * //In this example we create - * //a new p5.MediaElement via createAudio(). - * ele = createAudio('assets/doorbell.mp3'); - * background(250); - * textAlign(CENTER); - * text('Click to know the duration!', 10, 25, 70, 80); - * } - * function mouseClicked() { - * ele.play(); - * background(200); - * //ele.duration dislpays the duration - * text(ele.duration() + ' seconds', width / 2, height / 2); - * } - *
- */ -p5.MediaElement.prototype.duration = function () { - return this.elt.duration; -}; -p5.MediaElement.prototype.pixels = []; -p5.MediaElement.prototype._ensureCanvas = function () { - if (!this.canvas) { - this.canvas = document.createElement('canvas'); - this.drawingContext = this.canvas.getContext('2d'); - this.setModified(true); - } + // Don't update the canvas again if we have already updated the canvas with + // the current frame + const needsRedraw = this._frameOnCanvas !== this._pInst.frameCount; + if (this.loadedmetadata && needsRedraw) { + // wait for metadata for w/h + if (this.canvas.width !== this.elt.width) { + this.canvas.width = this.elt.width; + this.canvas.height = this.elt.height; + this.width = this.canvas.width; + this.height = this.canvas.height; + } - // Don't update the canvas again if we have already updated the canvas with - // the current frame - const needsRedraw = this._frameOnCanvas !== this._pInst.frameCount; - if (this.loadedmetadata && needsRedraw) { - // wait for metadata for w/h - if (this.canvas.width !== this.elt.width) { - this.canvas.width = this.elt.width; - this.canvas.height = this.elt.height; - this.width = this.canvas.width; - this.height = this.canvas.height; + this.drawingContext.drawImage( + this.elt, + 0, + 0, + this.canvas.width, + this.canvas.height + ); + this.setModified(true); + this._frameOnCanvas = this._pInst.frameCount; + } + } + loadPixels() { + this._ensureCanvas(); + return p5.Renderer2D.prototype.loadPixels.apply(this, arguments); + } + updatePixels(x, y, w, h) { + if (this.loadedmetadata) { + // wait for metadata + this._ensureCanvas(); + p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h); } - - this.drawingContext.drawImage( - this.elt, - 0, - 0, - this.canvas.width, - this.canvas.height - ); this.setModified(true); - this._frameOnCanvas = this._pInst.frameCount; + return this; } -}; -p5.MediaElement.prototype.loadPixels = function () { - this._ensureCanvas(); - return p5.Renderer2D.prototype.loadPixels.apply(this, arguments); -}; -p5.MediaElement.prototype.updatePixels = function (x, y, w, h) { - if (this.loadedmetadata) { - // wait for metadata + get() { this._ensureCanvas(); - p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h); + return p5.Renderer2D.prototype.get.apply(this, arguments); + } + _getPixel() { + this.loadPixels(); + return p5.Renderer2D.prototype._getPixel.apply(this, arguments); } - this.setModified(true); - return this; -}; -p5.MediaElement.prototype.get = function () { - this._ensureCanvas(); - return p5.Renderer2D.prototype.get.apply(this, arguments); -}; -p5.MediaElement.prototype._getPixel = function () { - this.loadPixels(); - return p5.Renderer2D.prototype._getPixel.apply(this, arguments); -}; -p5.MediaElement.prototype.set = function (x, y, imgOrCol) { - if (this.loadedmetadata) { - // wait for metadata + set(x, y, imgOrCol) { + if (this.loadedmetadata) { + // wait for metadata + this._ensureCanvas(); + p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol); + this.setModified(true); + } + } + copy() { this._ensureCanvas(); - p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol); + p5.prototype.copy.apply(this, arguments); + } + mask() { + this.loadPixels(); this.setModified(true); + p5.Image.prototype.mask.apply(this, arguments); + } + /** + * helper method for web GL mode to figure out if the element + * has been modified and might need to be re-uploaded to texture + * memory between frames. + * @method isModified + * @private + * @return {boolean} a boolean indicating whether or not the + * image has been updated or modified since last texture upload. + */ + isModified() { + return this._modified; + } + /** + * helper method for web GL mode to indicate that an element has been + * changed or unchanged since last upload. gl texture upload will + * set this value to false after uploading the texture; or might set + * it to true if metadata has become available but there is no actual + * texture data available yet.. + * @method setModified + * @param {boolean} val sets whether or not the element has been + * modified. + * @private + */ + setModified(value) { + this._modified = value; + } + /** + * Schedule an event to be called when the audio or video + * element reaches the end. If the element is looping, + * this will not be called. The element is passed in + * as the argument to the onended callback. + * + * @method onended + * @param {Function} callback function to call when the + * soundfile has ended. The + * media element will be passed + * in as the argument to the + * callback. + * @chainable + * @example + *
+ * function setup() { + * let audioEl = createAudio('assets/beat.mp3'); + * audioEl.showControls(); + * audioEl.onended(sayDone); + * } + * + * function sayDone(elt) { + * alert('done playing ' + elt.src); + * } + *
+ */ + onended(callback) { + this._onended = callback; + return this; } -}; -p5.MediaElement.prototype.copy = function () { - this._ensureCanvas(); - p5.prototype.copy.apply(this, arguments); -}; -p5.MediaElement.prototype.mask = function () { - this.loadPixels(); - this.setModified(true); - p5.Image.prototype.mask.apply(this, arguments); -}; -/** - * helper method for web GL mode to figure out if the element - * has been modified and might need to be re-uploaded to texture - * memory between frames. - * @method isModified - * @private - * @return {boolean} a boolean indicating whether or not the - * image has been updated or modified since last texture upload. - */ -p5.MediaElement.prototype.isModified = function () { - return this._modified; -}; -/** - * helper method for web GL mode to indicate that an element has been - * changed or unchanged since last upload. gl texture upload will - * set this value to false after uploading the texture; or might set - * it to true if metadata has become available but there is no actual - * texture data available yet.. - * @method setModified - * @param {boolean} val sets whether or not the element has been - * modified. - * @private - */ -p5.MediaElement.prototype.setModified = function (value) { - this._modified = value; -}; -/** - * Schedule an event to be called when the audio or video - * element reaches the end. If the element is looping, - * this will not be called. The element is passed in - * as the argument to the onended callback. - * - * @method onended - * @param {Function} callback function to call when the - * soundfile has ended. The - * media element will be passed - * in as the argument to the - * callback. - * @chainable - * @example - *
- * function setup() { - * let audioEl = createAudio('assets/beat.mp3'); - * audioEl.showControls(); - * audioEl.onended(sayDone); - * } - * - * function sayDone(elt) { - * alert('done playing ' + elt.src); - * } - *
- */ -p5.MediaElement.prototype.onended = function (callback) { - this._onended = callback; - return this; -}; -/*** CONNECT TO WEB AUDIO API / p5.sound.js ***/ + /*** CONNECT TO WEB AUDIO API / p5.sound.js ***/ + + /** + * Send the audio output of this element to a specified audioNode or + * p5.sound object. If no element is provided, connects to p5's main + * output. That connection is established when this method is first called. + * All connections are removed by the .disconnect() method. + * + * This method is meant to be used with the p5.sound.js addon library. + * + * @method connect + * @param {AudioNode|Object} audioNode AudioNode from the Web Audio API, + * or an object from the p5.sound library + */ + connect(obj) { + let audioContext, mainOutput; + + // if p5.sound exists, same audio context + if (typeof p5.prototype.getAudioContext === 'function') { + audioContext = p5.prototype.getAudioContext(); + mainOutput = p5.soundOut.input; + } else { + try { + audioContext = obj.context; + mainOutput = audioContext.destination; + } catch (e) { + throw 'connect() is meant to be used with Web Audio API or p5.sound.js'; + } + } -/** - * Send the audio output of this element to a specified audioNode or - * p5.sound object. If no element is provided, connects to p5's main - * output. That connection is established when this method is first called. - * All connections are removed by the .disconnect() method. - * - * This method is meant to be used with the p5.sound.js addon library. - * - * @method connect - * @param {AudioNode|Object} audioNode AudioNode from the Web Audio API, - * or an object from the p5.sound library - */ -p5.MediaElement.prototype.connect = function (obj) { - let audioContext, mainOutput; + // create a Web Audio MediaElementAudioSourceNode if none already exists + if (!this.audioSourceNode) { + this.audioSourceNode = audioContext.createMediaElementSource(this.elt); - // if p5.sound exists, same audio context - if (typeof p5.prototype.getAudioContext === 'function') { - audioContext = p5.prototype.getAudioContext(); - mainOutput = p5.soundOut.input; - } else { - try { - audioContext = obj.context; - mainOutput = audioContext.destination; - } catch (e) { - throw 'connect() is meant to be used with Web Audio API or p5.sound.js'; + // connect to main output when this method is first called + this.audioSourceNode.connect(mainOutput); } - } - - // create a Web Audio MediaElementAudioSourceNode if none already exists - if (!this.audioSourceNode) { - this.audioSourceNode = audioContext.createMediaElementSource(this.elt); - // connect to main output when this method is first called - this.audioSourceNode.connect(mainOutput); + // connect to object if provided + if (obj) { + if (obj.input) { + this.audioSourceNode.connect(obj.input); + } else { + this.audioSourceNode.connect(obj); + } + } else { + // otherwise connect to main output of p5.sound / AudioContext + this.audioSourceNode.connect(mainOutput); + } } - // connect to object if provided - if (obj) { - if (obj.input) { - this.audioSourceNode.connect(obj.input); + /** + * Disconnect all Web Audio routing, including to main output. + * This is useful if you want to re-route the output through + * audio effects, for example. + * + * @method disconnect + */ + disconnect() { + if (this.audioSourceNode) { + this.audioSourceNode.disconnect(); } else { - this.audioSourceNode.connect(obj); + throw 'nothing to disconnect'; } - } else { - // otherwise connect to main output of p5.sound / AudioContext - this.audioSourceNode.connect(mainOutput); } -}; -/** - * Disconnect all Web Audio routing, including to main output. - * This is useful if you want to re-route the output through - * audio effects, for example. - * - * @method disconnect - */ -p5.MediaElement.prototype.disconnect = function () { - if (this.audioSourceNode) { - this.audioSourceNode.disconnect(); - } else { - throw 'nothing to disconnect'; + /*** SHOW / HIDE CONTROLS ***/ + + /** + * Show the default MediaElement controls, as determined by the web browser. + * + * @method showControls + * @example + *
+ * let ele; + * function setup() { + * //p5.MediaElement objects are usually created + * //by calling the createAudio(), createVideo(), + * //and createCapture() functions. + * //In this example we create + * //a new p5.MediaElement via createAudio() + * ele = createAudio('assets/lucky_dragons.mp3'); + * background(200); + * textAlign(CENTER); + * text('Click to Show Controls!', 10, 25, 70, 80); + * } + * function mousePressed() { + * ele.showControls(); + * background(200); + * text('Controls Shown', width / 2, height / 2); + * } + *
+ */ + showControls() { + // must set style for the element to show on the page + this.elt.style['text-align'] = 'inherit'; + this.elt.controls = true; + } + + /** + * Hide the default mediaElement controls. + * @method hideControls + * @example + *
+ * let ele; + * function setup() { + * //p5.MediaElement objects are usually created + * //by calling the createAudio(), createVideo(), + * //and createCapture() functions. + * //In this example we create + * //a new p5.MediaElement via createAudio() + * ele = createAudio('assets/lucky_dragons.mp3'); + * ele.showControls(); + * background(200); + * textAlign(CENTER); + * text('Click to hide Controls!', 10, 25, 70, 80); + * } + * function mousePressed() { + * ele.hideControls(); + * background(200); + * text('Controls hidden', width / 2, height / 2); + * } + *
+ */ + hideControls() { + this.elt.controls = false; } }; -/*** SHOW / HIDE CONTROLS ***/ - -/** - * Show the default MediaElement controls, as determined by the web browser. - * - * @method showControls - * @example - *
- * let ele; - * function setup() { - * //p5.MediaElement objects are usually created - * //by calling the createAudio(), createVideo(), - * //and createCapture() functions. - * //In this example we create - * //a new p5.MediaElement via createAudio() - * ele = createAudio('assets/lucky_dragons.mp3'); - * background(200); - * textAlign(CENTER); - * text('Click to Show Controls!', 10, 25, 70, 80); - * } - * function mousePressed() { - * ele.showControls(); - * background(200); - * text('Controls Shown', width / 2, height / 2); - * } - *
- */ -p5.MediaElement.prototype.showControls = function () { - // must set style for the element to show on the page - this.elt.style['text-align'] = 'inherit'; - this.elt.controls = true; -}; - -/** - * Hide the default mediaElement controls. - * @method hideControls - * @example - *
- * let ele; - * function setup() { - * //p5.MediaElement objects are usually created - * //by calling the createAudio(), createVideo(), - * //and createCapture() functions. - * //In this example we create - * //a new p5.MediaElement via createAudio() - * ele = createAudio('assets/lucky_dragons.mp3'); - * ele.showControls(); - * background(200); - * textAlign(CENTER); - * text('Click to hide Controls!', 10, 25, 70, 80); - * } - * function mousePressed() { - * ele.hideControls(); - * background(200); - * text('Controls hidden', width / 2, height / 2); - * } - *
- */ -p5.MediaElement.prototype.hideControls = function () { - this.elt.controls = false; -}; - /*** SCHEDULE EVENTS ***/ // Cue inspired by JavaScript setTimeout, and the @@ -3493,7 +3496,7 @@ const Cue = function (callback, time, id, val) { * } * */ -p5.MediaElement.prototype.addCue = function (time, callback, val) { +addCue = function (time, callback, val) { const id = this._cueIDCounter++; const cue = new Cue(callback, time, id, val); @@ -3535,7 +3538,7 @@ p5.MediaElement.prototype.addCue = function (time, callback, val) { * } * */ -p5.MediaElement.prototype.removeCue = function (id) { +removeCue = function (id) { for (let i = 0; i < this._cues.length; i++) { if (this._cues[i].id === id) { console.log(id); @@ -3582,14 +3585,14 @@ p5.MediaElement.prototype.removeCue = function (id) { * } * */ -p5.MediaElement.prototype.clearCues = function () { +clearCues = function () { this._cues = []; this.elt.ontimeupdate = null; }; // private method that checks for cues to be fired if events // have been scheduled using addCue(callback, time). -p5.MediaElement.prototype._onTimeUpdate = function () { +_onTimeUpdate = function () { const playbackTime = this.time(); for (let i = 0; i < this._cues.length; i++) { From f841c832fa6558f6b42027ebba4878b023a65661 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Wed, 31 May 2023 14:10:07 +0900 Subject: [PATCH 21/22] fix ci --- src/dom/dom.js | 164 +++++++++++++++++++++++++------------------------ 1 file changed, 84 insertions(+), 80 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index dcca5da76a..9d28420c9f 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -2422,6 +2422,20 @@ p5.Element.prototype.drop = function (callback, fxn) { return this; }; +/*** SCHEDULE EVENTS ***/ + +// Cue inspired by JavaScript setTimeout, and the +// Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org +// eslint-disable-next-line no-unused-vars +class Cue { + constructor(callback, time, id, val) { + this.callback = callback; + this.time = time; + this.id = id; + this.val = val; + } +} + // ============================================================================= // p5.MediaElement additions // ============================================================================= @@ -2898,7 +2912,7 @@ p5.MediaElement = class MediaElement extends p5.Element { if (val && !oldVal) { // bind method to this scope const setupAutoplayFailDetection = - () => this._setupAutoplayFailDetection(); + () => this._setupAutoplayFailDetection(); // if media is ready to play, schedule check now if (this.elt.readyState === 4) { setupAutoplayFailDetection(); @@ -3433,20 +3447,8 @@ p5.MediaElement = class MediaElement extends p5.Element { hideControls() { this.elt.controls = false; } -}; - -/*** SCHEDULE EVENTS ***/ - -// Cue inspired by JavaScript setTimeout, and the -// Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org -const Cue = function (callback, time, id, val) { - this.callback = callback; - this.time = time; - this.id = id; - this.val = val; -}; -/** + /** * Schedule events to trigger every time a MediaElement * (audio/video) reaches a playback cue point. * @@ -3496,20 +3498,20 @@ const Cue = function (callback, time, id, val) { * } * */ -addCue = function (time, callback, val) { - const id = this._cueIDCounter++; + addCue(time, callback, val) { + const id = this._cueIDCounter++; - const cue = new Cue(callback, time, id, val); - this._cues.push(cue); + const cue = new Cue(callback, time, id, val); + this._cues.push(cue); - if (!this.elt.ontimeupdate) { - this.elt.ontimeupdate = this._onTimeUpdate.bind(this); - } + if (!this.elt.ontimeupdate) { + this.elt.ontimeupdate = this._onTimeUpdate.bind(this); + } - return id; -}; + return id; + } -/** + /** * Remove a callback based on its ID. The ID is returned by the * addCue method. * @method removeCue @@ -3538,20 +3540,20 @@ addCue = function (time, callback, val) { * } * */ -removeCue = function (id) { - for (let i = 0; i < this._cues.length; i++) { - if (this._cues[i].id === id) { - console.log(id); - this._cues.splice(i, 1); + removeCue(id) { + for (let i = 0; i < this._cues.length; i++) { + if (this._cues[i].id === id) { + console.log(id); + this._cues.splice(i, 1); + } } - } - if (this._cues.length === 0) { - this.elt.ontimeupdate = null; + if (this._cues.length === 0) { + this.elt.ontimeupdate = null; + } } -}; -/** + /** * Remove all of the callbacks that had originally been scheduled * via the addCue method. * @method clearCues @@ -3585,27 +3587,28 @@ removeCue = function (id) { * } * */ -clearCues = function () { - this._cues = []; - this.elt.ontimeupdate = null; -}; + clearCues() { + this._cues = []; + this.elt.ontimeupdate = null; + } -// private method that checks for cues to be fired if events -// have been scheduled using addCue(callback, time). -_onTimeUpdate = function () { - const playbackTime = this.time(); + // private method that checks for cues to be fired if events + // have been scheduled using addCue(callback, time). + _onTimeUpdate() { + const playbackTime = this.time(); - for (let i = 0; i < this._cues.length; i++) { - const callbackTime = this._cues[i].time; - const val = this._cues[i].val; + for (let i = 0; i < this._cues.length; i++) { + const callbackTime = this._cues[i].time; + const val = this._cues[i].val; - if (this._prevTime < callbackTime && callbackTime <= playbackTime) { - // pass the scheduled callbackTime as parameter to the callback - this._cues[i].callback(val); + if (this._prevTime < callbackTime && callbackTime <= playbackTime) { + // pass the scheduled callbackTime as parameter to the callback + this._cues[i].callback(val); + } } - } - this._prevTime = playbackTime; + this._prevTime = playbackTime; + } }; /** @@ -3663,39 +3666,40 @@ p5.File = class File { */ this.data = undefined; } -}; -p5.File._createLoader = function (theFile, callback) { - const reader = new FileReader(); - reader.onload = function (e) { - const p5file = new p5.File(theFile); - if (p5file.file.type === 'application/json') { - // Parse JSON and store the result in data - p5file.data = JSON.parse(e.target.result); - } else if (p5file.file.type === 'text/xml') { - // Parse XML, wrap it in p5.XML and store the result in data - const parser = new DOMParser(); - const xml = parser.parseFromString(e.target.result, 'text/xml'); - p5file.data = new p5.XML(xml.documentElement); + + static _createLoader(theFile, callback) { + const reader = new FileReader(); + reader.onload = function (e) { + const p5file = new p5.File(theFile); + if (p5file.file.type === 'application/json') { + // Parse JSON and store the result in data + p5file.data = JSON.parse(e.target.result); + } else if (p5file.file.type === 'text/xml') { + // Parse XML, wrap it in p5.XML and store the result in data + const parser = new DOMParser(); + const xml = parser.parseFromString(e.target.result, 'text/xml'); + p5file.data = new p5.XML(xml.documentElement); + } else { + p5file.data = e.target.result; + } + callback(p5file); + }; + return reader; + } + + static _load(f, callback) { + // Text or data? + // This should likely be improved + if (/^text\//.test(f.type) || f.type === 'application/json') { + p5.File._createLoader(f, callback).readAsText(f); + } else if (!/^(video|audio)\//.test(f.type)) { + p5.File._createLoader(f, callback).readAsDataURL(f); } else { - p5file.data = e.target.result; + const file = new p5.File(f); + file.data = URL.createObjectURL(f); + callback(file); } - callback(p5file); - }; - return reader; -}; - -p5.File._load = function (f, callback) { - // Text or data? - // This should likely be improved - if (/^text\//.test(f.type) || f.type === 'application/json') { - p5.File._createLoader(f, callback).readAsText(f); - } else if (!/^(video|audio)\//.test(f.type)) { - p5.File._createLoader(f, callback).readAsDataURL(f); - } else { - const file = new p5.File(f); - file.data = URL.createObjectURL(f); - callback(file); } }; From 346a560bcc396985ed76851b446a11222c940401 Mon Sep 17 00:00:00 2001 From: asukaminato Date: Wed, 31 May 2023 14:25:52 +0900 Subject: [PATCH 22/22] Camera to class --- src/webgl/p5.Camera.js | 1170 ++++++++++++++++++++-------------------- 1 file changed, 586 insertions(+), 584 deletions(-) diff --git a/src/webgl/p5.Camera.js b/src/webgl/p5.Camera.js index bc3be9ae72..7a81de3172 100644 --- a/src/webgl/p5.Camera.js +++ b/src/webgl/p5.Camera.js @@ -109,7 +109,7 @@ import p5 from '../core/main'; * An interactive example of a red cube with 3 sliders for moving it across x, y, * z axis and 3 sliders for shifting its center. */ -p5.prototype.camera = function(...args) { +p5.prototype.camera = function (...args) { this._assert3d('camera'); p5._validateParameters('camera', args); this._renderer._curCamera.camera(...args); @@ -173,7 +173,7 @@ p5.prototype.camera = function(...args) { * @alt * two colored 3D boxes move back and forth, rotating as mouse is dragged. */ -p5.prototype.perspective = function(...args) { +p5.prototype.perspective = function (...args) { this._assert3d('perspective'); p5._validateParameters('perspective', args); this._renderer._curCamera.perspective(...args); @@ -236,7 +236,7 @@ p5.prototype.perspective = function(...args) { * @alt * two 3D boxes move back and forth along same plane, rotating as mouse is dragged. */ -p5.prototype.ortho = function(...args) { +p5.prototype.ortho = function (...args) { this._assert3d('ortho'); p5._validateParameters('ortho', args); this._renderer._curCamera.ortho(...args); @@ -303,7 +303,7 @@ p5.prototype.ortho = function(...args) { * @alt * two 3D boxes move back and forth along same plane, rotating as mouse is dragged. */ -p5.prototype.frustum = function(...args) { +p5.prototype.frustum = function (...args) { this._assert3d('frustum'); p5._validateParameters('frustum', args); this._renderer._curCamera.frustum(...args); @@ -368,7 +368,7 @@ p5.prototype.frustum = function(...args) { * @alt * An example that creates a camera and moves it around the box. */ -p5.prototype.createCamera = function() { +p5.prototype.createCamera = function () { this._assert3d('createCamera'); const _cam = new p5.Camera(this._renderer); @@ -464,16 +464,17 @@ p5.prototype.createCamera = function() { * @alt * camera view pans left and right across a series of rotating 3D boxes. */ -p5.Camera = function(renderer) { - this._renderer = renderer; +p5.Camera = class Camera { + constructor(renderer) { + this._renderer = renderer; - this.cameraType = 'default'; + this.cameraType = 'default'; - this.cameraMatrix = new p5.Matrix(); - this.projMatrix = new p5.Matrix(); - this.yScale = 1; -}; -/** + this.cameraMatrix = new p5.Matrix(); + this.projMatrix = new p5.Matrix(); + this.yScale = 1; + } + /** * camera position value on x axis * @property {Number} eyeX * @readonly @@ -502,7 +503,7 @@ p5.Camera = function(renderer) { * */ -/** + /** * camera position value on y axis * @property {Number} eyeY * @readonly @@ -530,7 +531,7 @@ p5.Camera = function(renderer) { * */ -/** + /** * camera position value on z axis * @property {Number} eyeZ * @readonly @@ -558,7 +559,7 @@ p5.Camera = function(renderer) { * */ -/** + /** * x coordinate representing center of the sketch * @property {Number} centerX * @readonly @@ -587,7 +588,7 @@ p5.Camera = function(renderer) { * */ -/** + /** * y coordinate representing center of the sketch * @property {Number} centerY * @readonly @@ -616,7 +617,7 @@ p5.Camera = function(renderer) { * */ -/** + /** * z coordinate representing center of the sketch * @property {Number} centerZ * @readonly @@ -645,7 +646,7 @@ p5.Camera = function(renderer) { * */ -/** + /** * x component of direction 'up' from camera * @property {Number} upX * @readonly @@ -669,7 +670,7 @@ p5.Camera = function(renderer) { * */ -/** + /** * y component of direction 'up' from camera * @property {Number} upY * @readonly @@ -693,7 +694,7 @@ p5.Camera = function(renderer) { * */ -/** + /** * z component of direction 'up' from camera * @property {Number} upZ * @readonly @@ -717,11 +718,11 @@ p5.Camera = function(renderer) { * */ -//////////////////////////////////////////////////////////////////////////////// -// Camera Projection Methods -//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + // Camera Projection Methods + //////////////////////////////////////////////////////////////////////////////// -/** + /** * Sets a perspective projection. * Accepts the same parameters as the global * perspective(). @@ -766,81 +767,81 @@ p5.Camera = function(renderer) { * @alt * two colored 3D boxes move back and forth, rotating as mouse is dragged. */ -p5.Camera.prototype.perspective = function(fovy, aspect, near, far) { - this.cameraType = arguments.length > 0 ? 'custom' : 'default'; - if (typeof fovy === 'undefined') { - fovy = this.defaultCameraFOV; - // this avoids issue where setting angleMode(DEGREES) before calling - // perspective leads to a smaller than expected FOV (because - // _computeCameraDefaultSettings computes in radians) - this.cameraFOV = fovy; - } else { - this.cameraFOV = this._renderer._pInst._toRadians(fovy); - } - if (typeof aspect === 'undefined') { - aspect = this.defaultAspectRatio; - } - if (typeof near === 'undefined') { - near = this.defaultCameraNear; - } - if (typeof far === 'undefined') { - far = this.defaultCameraFar; - } - - if (near <= 0.0001) { - near = 0.01; - console.log( - 'Avoid perspective near plane values close to or below 0. ' + + perspective(fovy, aspect, near, far) { + this.cameraType = arguments.length > 0 ? 'custom' : 'default'; + if (typeof fovy === 'undefined') { + fovy = this.defaultCameraFOV; + // this avoids issue where setting angleMode(DEGREES) before calling + // perspective leads to a smaller than expected FOV (because + // _computeCameraDefaultSettings computes in radians) + this.cameraFOV = fovy; + } else { + this.cameraFOV = this._renderer._pInst._toRadians(fovy); + } + if (typeof aspect === 'undefined') { + aspect = this.defaultAspectRatio; + } + if (typeof near === 'undefined') { + near = this.defaultCameraNear; + } + if (typeof far === 'undefined') { + far = this.defaultCameraFar; + } + + if (near <= 0.0001) { + near = 0.01; + console.log( + 'Avoid perspective near plane values close to or below 0. ' + 'Setting value to 0.01.' - ); - } + ); + } - if (far < near) { - console.log( - 'Perspective far plane value is less than near plane value. ' + + if (far < near) { + console.log( + 'Perspective far plane value is less than near plane value. ' + 'Nothing will be shown.' - ); + ); + } + + this.aspectRatio = aspect; + this.cameraNear = near; + this.cameraFar = far; + + this.projMatrix = p5.Matrix.identity(); + + const f = 1.0 / Math.tan(this.cameraFOV / 2); + const nf = 1.0 / (this.cameraNear - this.cameraFar); + + /* eslint-disable indent */ + this.projMatrix.set(f / aspect, 0, 0, 0, + 0, -f * this.yScale, 0, 0, + 0, 0, (far + near) * nf, -1, + 0, 0, (2 * far * near) * nf, 0); + /* eslint-enable indent */ + + if (this._isActive()) { + this._renderer.uPMatrix.set( + this.projMatrix.mat4[0], + this.projMatrix.mat4[1], + this.projMatrix.mat4[2], + this.projMatrix.mat4[3], + this.projMatrix.mat4[4], + this.projMatrix.mat4[5], + this.projMatrix.mat4[6], + this.projMatrix.mat4[7], + this.projMatrix.mat4[8], + this.projMatrix.mat4[9], + this.projMatrix.mat4[10], + this.projMatrix.mat4[11], + this.projMatrix.mat4[12], + this.projMatrix.mat4[13], + this.projMatrix.mat4[14], + this.projMatrix.mat4[15] + ); + } } - this.aspectRatio = aspect; - this.cameraNear = near; - this.cameraFar = far; - - this.projMatrix = p5.Matrix.identity(); - - const f = 1.0 / Math.tan(this.cameraFOV / 2); - const nf = 1.0 / (this.cameraNear - this.cameraFar); - - /* eslint-disable indent */ - this.projMatrix.set(f / aspect, 0, 0, 0, - 0, -f * this.yScale, 0, 0, - 0, 0, (far + near) * nf, -1, - 0, 0, (2 * far * near) * nf, 0); - /* eslint-enable indent */ - - if (this._isActive()) { - this._renderer.uPMatrix.set( - this.projMatrix.mat4[0], - this.projMatrix.mat4[1], - this.projMatrix.mat4[2], - this.projMatrix.mat4[3], - this.projMatrix.mat4[4], - this.projMatrix.mat4[5], - this.projMatrix.mat4[6], - this.projMatrix.mat4[7], - this.projMatrix.mat4[8], - this.projMatrix.mat4[9], - this.projMatrix.mat4[10], - this.projMatrix.mat4[11], - this.projMatrix.mat4[12], - this.projMatrix.mat4[13], - this.projMatrix.mat4[14], - this.projMatrix.mat4[15] - ); - } -}; - -/** + /** * Sets an orthographic projection. * Accepts the same parameters as the global * ortho(). @@ -883,64 +884,64 @@ p5.Camera.prototype.perspective = function(fovy, aspect, near, far) { * @alt * two 3D boxes move back and forth along same plane, rotating as mouse is dragged. */ -p5.Camera.prototype.ortho = function(left, right, bottom, top, near, far) { - if (left === undefined) left = -this._renderer.width / 2; - if (right === undefined) right = +this._renderer.width / 2; - if (bottom === undefined) bottom = -this._renderer.height / 2; - if (top === undefined) top = +this._renderer.height / 2; - if (near === undefined) near = 0; - if (far === undefined) - far = Math.max(this._renderer.width, this._renderer.height); - - this.cameraNear = near; - this.cameraFar = far; - - const w = right - left; - const h = top - bottom; - const d = far - near; - - const x = +2.0 / w; - const y = +2.0 / h * this.yScale; - const z = -2.0 / d; - - const tx = -(right + left) / w; - const ty = -(top + bottom) / h; - const tz = -(far + near) / d; - - this.projMatrix = p5.Matrix.identity(); - - /* eslint-disable indent */ - this.projMatrix.set( x, 0, 0, 0, - 0, -y, 0, 0, - 0, 0, z, 0, - tx, ty, tz, 1); - /* eslint-enable indent */ - - if (this._isActive()) { - this._renderer.uPMatrix.set( - this.projMatrix.mat4[0], - this.projMatrix.mat4[1], - this.projMatrix.mat4[2], - this.projMatrix.mat4[3], - this.projMatrix.mat4[4], - this.projMatrix.mat4[5], - this.projMatrix.mat4[6], - this.projMatrix.mat4[7], - this.projMatrix.mat4[8], - this.projMatrix.mat4[9], - this.projMatrix.mat4[10], - this.projMatrix.mat4[11], - this.projMatrix.mat4[12], - this.projMatrix.mat4[13], - this.projMatrix.mat4[14], - this.projMatrix.mat4[15] - ); + ortho(left, right, bottom, top, near, far) { + if (left === undefined) left = -this._renderer.width / 2; + if (right === undefined) right = +this._renderer.width / 2; + if (bottom === undefined) bottom = -this._renderer.height / 2; + if (top === undefined) top = +this._renderer.height / 2; + if (near === undefined) near = 0; + if (far === undefined) + far = Math.max(this._renderer.width, this._renderer.height); + + this.cameraNear = near; + this.cameraFar = far; + + const w = right - left; + const h = top - bottom; + const d = far - near; + + const x = +2.0 / w; + const y = +2.0 / h * this.yScale; + const z = -2.0 / d; + + const tx = -(right + left) / w; + const ty = -(top + bottom) / h; + const tz = -(far + near) / d; + + this.projMatrix = p5.Matrix.identity(); + + /* eslint-disable indent */ + this.projMatrix.set( x, 0, 0, 0, + 0, -y, 0, 0, + 0, 0, z, 0, + tx, ty, tz, 1); + /* eslint-enable indent */ + + if (this._isActive()) { + this._renderer.uPMatrix.set( + this.projMatrix.mat4[0], + this.projMatrix.mat4[1], + this.projMatrix.mat4[2], + this.projMatrix.mat4[3], + this.projMatrix.mat4[4], + this.projMatrix.mat4[5], + this.projMatrix.mat4[6], + this.projMatrix.mat4[7], + this.projMatrix.mat4[8], + this.projMatrix.mat4[9], + this.projMatrix.mat4[10], + this.projMatrix.mat4[11], + this.projMatrix.mat4[12], + this.projMatrix.mat4[13], + this.projMatrix.mat4[14], + this.projMatrix.mat4[15] + ); + } + + this.cameraType = 'custom'; } - this.cameraType = 'custom'; -}; - -/** + /** * Sets the camera's frustum. * Accepts the same parameters as the global * frustum(). @@ -982,112 +983,112 @@ p5.Camera.prototype.ortho = function(left, right, bottom, top, near, far) { * @alt * two 3D boxes move back and forth along same plane, rotating as mouse is dragged. */ -p5.Camera.prototype.frustum = function(left, right, bottom, top, near, far) { - if (left === undefined) left = -this._renderer.width * 0.05; - if (right === undefined) right = +this._renderer.width * 0.05; - if (bottom === undefined) bottom = +this._renderer.height * 0.05; - if (top === undefined) top = -this._renderer.height * 0.05; - if (near === undefined) near = this.defaultCameraNear; - if (far === undefined) far = this.defaultCameraFar; - - this.cameraNear = near; - this.cameraFar = far; - - const w = right - left; - const h = top - bottom; - const d = far - near; - - const x = +(2.0 * near) / w; - const y = +(2.0 * near) / h * this.yScale; - const z = -(2.0 * far * near) / d; - - const tx = (right + left) / w; - const ty = (top + bottom) / h; - const tz = -(far + near) / d; - - this.projMatrix = p5.Matrix.identity(); - - /* eslint-disable indent */ - this.projMatrix.set( x, 0, 0, 0, - 0, -y, 0, 0, - tx, ty, tz, -1, - 0, 0, z, 0); - /* eslint-enable indent */ - - if (this._isActive()) { - this._renderer.uPMatrix.set( - this.projMatrix.mat4[0], - this.projMatrix.mat4[1], - this.projMatrix.mat4[2], - this.projMatrix.mat4[3], - this.projMatrix.mat4[4], - this.projMatrix.mat4[5], - this.projMatrix.mat4[6], - this.projMatrix.mat4[7], - this.projMatrix.mat4[8], - this.projMatrix.mat4[9], - this.projMatrix.mat4[10], - this.projMatrix.mat4[11], - this.projMatrix.mat4[12], - this.projMatrix.mat4[13], - this.projMatrix.mat4[14], - this.projMatrix.mat4[15] - ); + frustum(left, right, bottom, top, near, far) { + if (left === undefined) left = -this._renderer.width * 0.05; + if (right === undefined) right = +this._renderer.width * 0.05; + if (bottom === undefined) bottom = +this._renderer.height * 0.05; + if (top === undefined) top = -this._renderer.height * 0.05; + if (near === undefined) near = this.defaultCameraNear; + if (far === undefined) far = this.defaultCameraFar; + + this.cameraNear = near; + this.cameraFar = far; + + const w = right - left; + const h = top - bottom; + const d = far - near; + + const x = +(2.0 * near) / w; + const y = +(2.0 * near) / h * this.yScale; + const z = -(2.0 * far * near) / d; + + const tx = (right + left) / w; + const ty = (top + bottom) / h; + const tz = -(far + near) / d; + + this.projMatrix = p5.Matrix.identity(); + + /* eslint-disable indent */ + this.projMatrix.set( x, 0, 0, 0, + 0, -y, 0, 0, + tx, ty, tz, -1, + 0, 0, z, 0); + /* eslint-enable indent */ + + if (this._isActive()) { + this._renderer.uPMatrix.set( + this.projMatrix.mat4[0], + this.projMatrix.mat4[1], + this.projMatrix.mat4[2], + this.projMatrix.mat4[3], + this.projMatrix.mat4[4], + this.projMatrix.mat4[5], + this.projMatrix.mat4[6], + this.projMatrix.mat4[7], + this.projMatrix.mat4[8], + this.projMatrix.mat4[9], + this.projMatrix.mat4[10], + this.projMatrix.mat4[11], + this.projMatrix.mat4[12], + this.projMatrix.mat4[13], + this.projMatrix.mat4[14], + this.projMatrix.mat4[15] + ); + } + + this.cameraType = 'custom'; } - this.cameraType = 'custom'; -}; - -//////////////////////////////////////////////////////////////////////////////// -// Camera Orientation Methods -//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + // Camera Orientation Methods + //////////////////////////////////////////////////////////////////////////////// -/** + /** * Rotate camera view about arbitrary axis defined by x,y,z * based on http://learnwebgl.brown37.net/07_cameras/camera_rotating_motion.html * @method _rotateView * @private */ -p5.Camera.prototype._rotateView = function(a, x, y, z) { - let centerX = this.centerX; - let centerY = this.centerY; - let centerZ = this.centerZ; - - // move center by eye position such that rotation happens around eye position - centerX -= this.eyeX; - centerY -= this.eyeY; - centerZ -= this.eyeZ; - - const rotation = p5.Matrix.identity(this._renderer._pInst); - rotation.rotate(this._renderer._pInst._toRadians(a), x, y, z); - - /* eslint-disable max-len */ - const rotatedCenter = [ - centerX * rotation.mat4[0] + centerY * rotation.mat4[4] + centerZ * rotation.mat4[8], - centerX * rotation.mat4[1] + centerY * rotation.mat4[5] + centerZ * rotation.mat4[9], - centerX * rotation.mat4[2] + centerY * rotation.mat4[6] + centerZ * rotation.mat4[10] - ]; - /* eslint-enable max-len */ - - // add eye position back into center - rotatedCenter[0] += this.eyeX; - rotatedCenter[1] += this.eyeY; - rotatedCenter[2] += this.eyeZ; - - this.camera( - this.eyeX, - this.eyeY, - this.eyeZ, - rotatedCenter[0], - rotatedCenter[1], - rotatedCenter[2], - this.upX, - this.upY, - this.upZ - ); -}; + _rotateView(a, x, y, z) { + let centerX = this.centerX; + let centerY = this.centerY; + let centerZ = this.centerZ; + + // move center by eye position such that rotation happens around eye position + centerX -= this.eyeX; + centerY -= this.eyeY; + centerZ -= this.eyeZ; + + const rotation = p5.Matrix.identity(this._renderer._pInst); + rotation.rotate(this._renderer._pInst._toRadians(a), x, y, z); + + /* eslint-disable max-len */ + const rotatedCenter = [ + centerX * rotation.mat4[0] + centerY * rotation.mat4[4] + centerZ * rotation.mat4[8], + centerX * rotation.mat4[1] + centerY * rotation.mat4[5] + centerZ * rotation.mat4[9], + centerX * rotation.mat4[2] + centerY * rotation.mat4[6] + centerZ * rotation.mat4[10] + ]; + /* eslint-enable max-len */ + + // add eye position back into center + rotatedCenter[0] += this.eyeX; + rotatedCenter[1] += this.eyeY; + rotatedCenter[2] += this.eyeZ; + + this.camera( + this.eyeX, + this.eyeY, + this.eyeZ, + rotatedCenter[0], + rotatedCenter[1], + rotatedCenter[2], + this.upX, + this.upY, + this.upZ + ); + } -/** + /** * Panning rotates the camera view to the left and right. * @method pan * @param {Number} angle amount to rotate camera in current @@ -1140,12 +1141,12 @@ p5.Camera.prototype._rotateView = function(a, x, y, z) { * @alt * camera view pans left and right across a series of rotating 3D boxes. */ -p5.Camera.prototype.pan = function(amount) { - const local = this._getLocalAxes(); - this._rotateView(amount, local.y[0], local.y[1], local.y[2]); -}; + pan(amount) { + const local = this._getLocalAxes(); + this._rotateView(amount, local.y[0], local.y[1], local.y[2]); + } -/** + /** * Tilting rotates the camera view up and down. * @method tilt * @param {Number} angle amount to rotate camera in current @@ -1198,12 +1199,12 @@ p5.Camera.prototype.pan = function(amount) { * @alt * camera view tilts up and down across a series of rotating 3D boxes. */ -p5.Camera.prototype.tilt = function(amount) { - const local = this._getLocalAxes(); - this._rotateView(amount, local.x[0], local.x[1], local.x[2]); -}; + tilt(amount) { + const local = this._getLocalAxes(); + this._rotateView(amount, local.x[0], local.x[1], local.x[2]); + } -/** + /** * Reorients the camera to look at a position in world space. * @method lookAt * @for p5.Camera @@ -1252,25 +1253,25 @@ p5.Camera.prototype.tilt = function(amount) { * camera view of rotating 3D cubes changes to look at a new random * point every second . */ -p5.Camera.prototype.lookAt = function(x, y, z) { - this.camera( - this.eyeX, - this.eyeY, - this.eyeZ, - x, - y, - z, - this.upX, - this.upY, - this.upZ - ); -}; + lookAt(x, y, z) { + this.camera( + this.eyeX, + this.eyeY, + this.eyeZ, + x, + y, + z, + this.upX, + this.upY, + this.upZ + ); + } -//////////////////////////////////////////////////////////////////////////////// -// Camera Position Methods -//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + // Camera Position Methods + //////////////////////////////////////////////////////////////////////////////// -/** + /** * Sets the camera's position and orientation. * Accepts the same parameters as the global * camera(). @@ -1354,87 +1355,87 @@ p5.Camera.prototype.lookAt = function(x, y, z) { * An interactive example of a red cube with 3 sliders for moving it across x, y, * z axis and 3 sliders for shifting its center. */ -p5.Camera.prototype.camera = function( - eyeX, - eyeY, - eyeZ, - centerX, - centerY, - centerZ, - upX, - upY, - upZ -) { - if (typeof eyeX === 'undefined') { - eyeX = this.defaultEyeX; - eyeY = this.defaultEyeY; - eyeZ = this.defaultEyeZ; - centerX = eyeX; - centerY = eyeY; - centerZ = 0; - upX = 0; - upY = 1; - upZ = 0; + camera( + eyeX, + eyeY, + eyeZ, + centerX, + centerY, + centerZ, + upX, + upY, + upZ + ) { + if (typeof eyeX === 'undefined') { + eyeX = this.defaultEyeX; + eyeY = this.defaultEyeY; + eyeZ = this.defaultEyeZ; + centerX = eyeX; + centerY = eyeY; + centerZ = 0; + upX = 0; + upY = 1; + upZ = 0; + } + + this.eyeX = eyeX; + this.eyeY = eyeY; + this.eyeZ = eyeZ; + + if (typeof centerX !== 'undefined') { + this.centerX = centerX; + this.centerY = centerY; + this.centerZ = centerZ; + } + + if (typeof upX !== 'undefined') { + this.upX = upX; + this.upY = upY; + this.upZ = upZ; + } + + const local = this._getLocalAxes(); + + // the camera affects the model view matrix, insofar as it + // inverse translates the world to the eye position of the camera + // and rotates it. + /* eslint-disable indent */ + this.cameraMatrix.set(local.x[0], local.y[0], local.z[0], 0, + local.x[1], local.y[1], local.z[1], 0, + local.x[2], local.y[2], local.z[2], 0, + 0, 0, 0, 1); + /* eslint-enable indent */ + + const tx = -eyeX; + const ty = -eyeY; + const tz = -eyeZ; + + this.cameraMatrix.translate([tx, ty, tz]); + + if (this._isActive()) { + this._renderer.uMVMatrix.set( + this.cameraMatrix.mat4[0], + this.cameraMatrix.mat4[1], + this.cameraMatrix.mat4[2], + this.cameraMatrix.mat4[3], + this.cameraMatrix.mat4[4], + this.cameraMatrix.mat4[5], + this.cameraMatrix.mat4[6], + this.cameraMatrix.mat4[7], + this.cameraMatrix.mat4[8], + this.cameraMatrix.mat4[9], + this.cameraMatrix.mat4[10], + this.cameraMatrix.mat4[11], + this.cameraMatrix.mat4[12], + this.cameraMatrix.mat4[13], + this.cameraMatrix.mat4[14], + this.cameraMatrix.mat4[15] + ); + } + return this; } - this.eyeX = eyeX; - this.eyeY = eyeY; - this.eyeZ = eyeZ; - - if (typeof centerX !== 'undefined') { - this.centerX = centerX; - this.centerY = centerY; - this.centerZ = centerZ; - } - - if (typeof upX !== 'undefined') { - this.upX = upX; - this.upY = upY; - this.upZ = upZ; - } - - const local = this._getLocalAxes(); - - // the camera affects the model view matrix, insofar as it - // inverse translates the world to the eye position of the camera - // and rotates it. - /* eslint-disable indent */ - this.cameraMatrix.set(local.x[0], local.y[0], local.z[0], 0, - local.x[1], local.y[1], local.z[1], 0, - local.x[2], local.y[2], local.z[2], 0, - 0, 0, 0, 1); - /* eslint-enable indent */ - - const tx = -eyeX; - const ty = -eyeY; - const tz = -eyeZ; - - this.cameraMatrix.translate([tx, ty, tz]); - - if (this._isActive()) { - this._renderer.uMVMatrix.set( - this.cameraMatrix.mat4[0], - this.cameraMatrix.mat4[1], - this.cameraMatrix.mat4[2], - this.cameraMatrix.mat4[3], - this.cameraMatrix.mat4[4], - this.cameraMatrix.mat4[5], - this.cameraMatrix.mat4[6], - this.cameraMatrix.mat4[7], - this.cameraMatrix.mat4[8], - this.cameraMatrix.mat4[9], - this.cameraMatrix.mat4[10], - this.cameraMatrix.mat4[11], - this.cameraMatrix.mat4[12], - this.cameraMatrix.mat4[13], - this.cameraMatrix.mat4[14], - this.cameraMatrix.mat4[15] - ); - } - return this; -}; - -/** + /** * Move camera along its local axes while maintaining current camera orientation. * @method move * @param {Number} x amount to move along camera's left-right axis @@ -1484,29 +1485,29 @@ p5.Camera.prototype.camera = function( * camera view moves along a series of 3D boxes, maintaining the same * orientation throughout the move */ -p5.Camera.prototype.move = function(x, y, z) { - const local = this._getLocalAxes(); - - // scale local axes by movement amounts - // based on http://learnwebgl.brown37.net/07_cameras/camera_linear_motion.html - const dx = [local.x[0] * x, local.x[1] * x, local.x[2] * x]; - const dy = [local.y[0] * y, local.y[1] * y, local.y[2] * y]; - const dz = [local.z[0] * z, local.z[1] * z, local.z[2] * z]; - - this.camera( - this.eyeX + dx[0] + dy[0] + dz[0], - this.eyeY + dx[1] + dy[1] + dz[1], - this.eyeZ + dx[2] + dy[2] + dz[2], - this.centerX + dx[0] + dy[0] + dz[0], - this.centerY + dx[1] + dy[1] + dz[1], - this.centerZ + dx[2] + dy[2] + dz[2], - this.upX, - this.upY, - this.upZ - ); -}; + move(x, y, z) { + const local = this._getLocalAxes(); + + // scale local axes by movement amounts + // based on http://learnwebgl.brown37.net/07_cameras/camera_linear_motion.html + const dx = [local.x[0] * x, local.x[1] * x, local.x[2] * x]; + const dy = [local.y[0] * y, local.y[1] * y, local.y[2] * y]; + const dz = [local.z[0] * z, local.z[1] * z, local.z[2] * z]; + + this.camera( + this.eyeX + dx[0] + dy[0] + dz[0], + this.eyeY + dx[1] + dy[1] + dz[1], + this.eyeZ + dx[2] + dy[2] + dz[2], + this.centerX + dx[0] + dy[0] + dz[0], + this.centerY + dx[1] + dy[1] + dz[1], + this.centerZ + dx[2] + dy[2] + dz[2], + this.upX, + this.upY, + this.upZ + ); + } -/** + /** * Set camera position in world-space while maintaining current camera * orientation. * @method setPosition @@ -1550,168 +1551,168 @@ p5.Camera.prototype.move = function(x, y, z) { * @alt * camera position changes as the user presses keys, altering view of a 3D box */ -p5.Camera.prototype.setPosition = function(x, y, z) { - const diffX = x - this.eyeX; - const diffY = y - this.eyeY; - const diffZ = z - this.eyeZ; - - this.camera( - x, - y, - z, - this.centerX + diffX, - this.centerY + diffY, - this.centerZ + diffZ, - this.upX, - this.upY, - this.upZ - ); -}; - -//////////////////////////////////////////////////////////////////////////////// -// Camera Helper Methods -//////////////////////////////////////////////////////////////////////////////// + setPosition(x, y, z) { + const diffX = x - this.eyeX; + const diffY = y - this.eyeY; + const diffZ = z - this.eyeZ; + + this.camera( + x, + y, + z, + this.centerX + diffX, + this.centerY + diffY, + this.centerZ + diffZ, + this.upX, + this.upY, + this.upZ + ); + } -// @TODO: combine this function with _setDefaultCamera to compute these values -// as-needed -p5.Camera.prototype._computeCameraDefaultSettings = function() { - this.defaultCameraFOV = 60 / 180 * Math.PI; - this.defaultAspectRatio = this._renderer.width / this._renderer.height; - this.defaultEyeX = 0; - this.defaultEyeY = 0; - this.defaultEyeZ = - this._renderer.height / 2.0 / Math.tan(this.defaultCameraFOV / 2.0); - this.defaultCenterX = 0; - this.defaultCenterY = 0; - this.defaultCenterZ = 0; - this.defaultCameraNear = this.defaultEyeZ * 0.1; - this.defaultCameraFar = this.defaultEyeZ * 10; -}; + //////////////////////////////////////////////////////////////////////////////// + // Camera Helper Methods + //////////////////////////////////////////////////////////////////////////////// + + // @TODO: combine this function with _setDefaultCamera to compute these values + // as-needed + _computeCameraDefaultSettings() { + this.defaultCameraFOV = 60 / 180 * Math.PI; + this.defaultAspectRatio = this._renderer.width / this._renderer.height; + this.defaultEyeX = 0; + this.defaultEyeY = 0; + this.defaultEyeZ = + this._renderer.height / 2.0 / Math.tan(this.defaultCameraFOV / 2.0); + this.defaultCenterX = 0; + this.defaultCenterY = 0; + this.defaultCenterZ = 0; + this.defaultCameraNear = this.defaultEyeZ * 0.1; + this.defaultCameraFar = this.defaultEyeZ * 10; + } -//detect if user didn't set the camera -//then call this function below -p5.Camera.prototype._setDefaultCamera = function() { - this.cameraFOV = this.defaultCameraFOV; - this.aspectRatio = this.defaultAspectRatio; - this.eyeX = this.defaultEyeX; - this.eyeY = this.defaultEyeY; - this.eyeZ = this.defaultEyeZ; - this.centerX = this.defaultCenterX; - this.centerY = this.defaultCenterY; - this.centerZ = this.defaultCenterZ; - this.upX = 0; - this.upY = 1; - this.upZ = 0; - this.cameraNear = this.defaultCameraNear; - this.cameraFar = this.defaultCameraFar; - - this.perspective(); - this.camera(); - - this.cameraType = 'default'; -}; + //detect if user didn't set the camera + //then call this function below + _setDefaultCamera() { + this.cameraFOV = this.defaultCameraFOV; + this.aspectRatio = this.defaultAspectRatio; + this.eyeX = this.defaultEyeX; + this.eyeY = this.defaultEyeY; + this.eyeZ = this.defaultEyeZ; + this.centerX = this.defaultCenterX; + this.centerY = this.defaultCenterY; + this.centerZ = this.defaultCenterZ; + this.upX = 0; + this.upY = 1; + this.upZ = 0; + this.cameraNear = this.defaultCameraNear; + this.cameraFar = this.defaultCameraFar; + + this.perspective(); + this.camera(); + + this.cameraType = 'default'; + } -p5.Camera.prototype._resize = function() { - // If we're using the default camera, update the aspect ratio - if (this.cameraType === 'default') { - this._computeCameraDefaultSettings(); - this._setDefaultCamera(); - } else { - this.perspective( - this.cameraFOV, - this._renderer.width / this._renderer.height - ); + _resize() { + // If we're using the default camera, update the aspect ratio + if (this.cameraType === 'default') { + this._computeCameraDefaultSettings(); + this._setDefaultCamera(); + } else { + this.perspective( + this.cameraFOV, + this._renderer.width / this._renderer.height + ); + } } -}; -/** + /** * Returns a copy of a camera. * @method copy * @private */ -p5.Camera.prototype.copy = function() { - const _cam = new p5.Camera(this._renderer); - _cam.cameraFOV = this.cameraFOV; - _cam.aspectRatio = this.aspectRatio; - _cam.eyeX = this.eyeX; - _cam.eyeY = this.eyeY; - _cam.eyeZ = this.eyeZ; - _cam.centerX = this.centerX; - _cam.centerY = this.centerY; - _cam.centerZ = this.centerZ; - _cam.upX = this.upX; - _cam.upY = this.upY; - _cam.upZ = this.upZ; - _cam.cameraNear = this.cameraNear; - _cam.cameraFar = this.cameraFar; - - _cam.cameraType = this.cameraType; - - _cam.cameraMatrix = this.cameraMatrix.copy(); - _cam.projMatrix = this.projMatrix.copy(); - - return _cam; -}; + copy() { + const _cam = new p5.Camera(this._renderer); + _cam.cameraFOV = this.cameraFOV; + _cam.aspectRatio = this.aspectRatio; + _cam.eyeX = this.eyeX; + _cam.eyeY = this.eyeY; + _cam.eyeZ = this.eyeZ; + _cam.centerX = this.centerX; + _cam.centerY = this.centerY; + _cam.centerZ = this.centerZ; + _cam.upX = this.upX; + _cam.upY = this.upY; + _cam.upZ = this.upZ; + _cam.cameraNear = this.cameraNear; + _cam.cameraFar = this.cameraFar; + + _cam.cameraType = this.cameraType; + + _cam.cameraMatrix = this.cameraMatrix.copy(); + _cam.projMatrix = this.projMatrix.copy(); + + return _cam; + } -/** + /** * Returns a camera's local axes: left-right, up-down, and forward-backward, * as defined by vectors in world-space. * @method _getLocalAxes * @private */ -p5.Camera.prototype._getLocalAxes = function() { - // calculate camera local Z vector - let z0 = this.eyeX - this.centerX; - let z1 = this.eyeY - this.centerY; - let z2 = this.eyeZ - this.centerZ; - - // normalize camera local Z vector - const eyeDist = Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); - if (eyeDist !== 0) { - z0 /= eyeDist; - z1 /= eyeDist; - z2 /= eyeDist; - } - - // calculate camera Y vector - let y0 = this.upX; - let y1 = this.upY; - let y2 = this.upZ; - - // compute camera local X vector as up vector (local Y) cross local Z - let x0 = y1 * z2 - y2 * z1; - let x1 = -y0 * z2 + y2 * z0; - let x2 = y0 * z1 - y1 * z0; - - // recompute y = z cross x - y0 = z1 * x2 - z2 * x1; - y1 = -z0 * x2 + z2 * x0; - y2 = z0 * x1 - z1 * x0; - - // cross product gives area of parallelogram, which is < 1.0 for - // non-perpendicular unit-length vectors; so normalize x, y here: - const xmag = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); - if (xmag !== 0) { - x0 /= xmag; - x1 /= xmag; - x2 /= xmag; + _getLocalAxes() { + // calculate camera local Z vector + let z0 = this.eyeX - this.centerX; + let z1 = this.eyeY - this.centerY; + let z2 = this.eyeZ - this.centerZ; + + // normalize camera local Z vector + const eyeDist = Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + if (eyeDist !== 0) { + z0 /= eyeDist; + z1 /= eyeDist; + z2 /= eyeDist; + } + + // calculate camera Y vector + let y0 = this.upX; + let y1 = this.upY; + let y2 = this.upZ; + + // compute camera local X vector as up vector (local Y) cross local Z + let x0 = y1 * z2 - y2 * z1; + let x1 = -y0 * z2 + y2 * z0; + let x2 = y0 * z1 - y1 * z0; + + // recompute y = z cross x + y0 = z1 * x2 - z2 * x1; + y1 = -z0 * x2 + z2 * x0; + y2 = z0 * x1 - z1 * x0; + + // cross product gives area of parallelogram, which is < 1.0 for + // non-perpendicular unit-length vectors; so normalize x, y here: + const xmag = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (xmag !== 0) { + x0 /= xmag; + x1 /= xmag; + x2 /= xmag; + } + + const ymag = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (ymag !== 0) { + y0 /= ymag; + y1 /= ymag; + y2 /= ymag; + } + + return { + x: [x0, x1, x2], + y: [y0, y1, y2], + z: [z0, z1, z2] + }; } - const ymag = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); - if (ymag !== 0) { - y0 /= ymag; - y1 /= ymag; - y2 /= ymag; - } - - return { - x: [x0, x1, x2], - y: [y0, y1, y2], - z: [z0, z1, z2] - }; -}; - -/** + /** * Orbits the camera about center point. For use with orbitControl(). * @method _orbit * @private @@ -1719,74 +1720,75 @@ p5.Camera.prototype._getLocalAxes = function() { * @param {Number} dPhi change in spherical coordinate phi * @param {Number} dRadius change in radius */ -p5.Camera.prototype._orbit = function(dTheta, dPhi, dRadius) { - // Calculate the vector and its magnitude from the center to the viewpoint - const diffX = this.eyeX - this.centerX; - const diffY = this.eyeY - this.centerY; - const diffZ = this.eyeZ - this.centerZ; - let camRadius = Math.hypot(diffX, diffY, diffZ); - // front vector. unit vector from center to eye. - const front = new p5.Vector(diffX, diffY, diffZ).normalize(); - // up vector. normalized camera's up vector. - const up = new p5.Vector(this.upX, this.upY, this.upZ).normalize(); // y-axis - // side vector. Right when viewed from the front - const side = new p5.Vector.cross(up, front).normalize(); // x-axis - // vertical vector. normalized vector of projection of front vector. - const vertical = new p5.Vector.cross(side, up); // z-axis - - // update camRadius - camRadius *= Math.pow(10, dRadius); - // prevent zooming through the center: - if (camRadius < this.cameraNear) { - camRadius = this.cameraNear; - } - if (camRadius > this.cameraFar) { - camRadius = this.cameraFar; - } - - // calculate updated camera angle - // Find the angle between the "up" and the "front", add dPhi to that. - // angleBetween() may return negative value. Since this specification is subject to change - // due to version updates, it cannot be adopted, so here we calculate using a method - // that directly obtains the absolute value. - const camPhi = - Math.acos(Math.max(-1, Math.min(1, p5.Vector.dot(front, up)))) + dPhi; - // Rotate by dTheta in the shortest direction from "vertical" to "side" - const camTheta = dTheta; - - // Invert camera's upX, upY, upZ if dPhi is below 0 or above PI - if(camPhi <= 0 || camPhi >= Math.PI){ - this.upX *= -1; - this.upY *= -1; - this.upZ *= -1; + _orbit(dTheta, dPhi, dRadius) { + // Calculate the vector and its magnitude from the center to the viewpoint + const diffX = this.eyeX - this.centerX; + const diffY = this.eyeY - this.centerY; + const diffZ = this.eyeZ - this.centerZ; + let camRadius = Math.hypot(diffX, diffY, diffZ); + // front vector. unit vector from center to eye. + const front = new p5.Vector(diffX, diffY, diffZ).normalize(); + // up vector. normalized camera's up vector. + const up = new p5.Vector(this.upX, this.upY, this.upZ).normalize(); // y-axis + // side vector. Right when viewed from the front + const side = new p5.Vector.cross(up, front).normalize(); // x-axis + // vertical vector. normalized vector of projection of front vector. + const vertical = new p5.Vector.cross(side, up); // z-axis + + // update camRadius + camRadius *= Math.pow(10, dRadius); + // prevent zooming through the center: + if (camRadius < this.cameraNear) { + camRadius = this.cameraNear; + } + if (camRadius > this.cameraFar) { + camRadius = this.cameraFar; + } + + // calculate updated camera angle + // Find the angle between the "up" and the "front", add dPhi to that. + // angleBetween() may return negative value. Since this specification is subject to change + // due to version updates, it cannot be adopted, so here we calculate using a method + // that directly obtains the absolute value. + const camPhi = + Math.acos(Math.max(-1, Math.min(1, p5.Vector.dot(front, up)))) + dPhi; + // Rotate by dTheta in the shortest direction from "vertical" to "side" + const camTheta = dTheta; + + // Invert camera's upX, upY, upZ if dPhi is below 0 or above PI + if (camPhi <= 0 || camPhi >= Math.PI) { + this.upX *= -1; + this.upY *= -1; + this.upZ *= -1; + } + + // update eye vector by calculate new front vector + up.mult(Math.cos(camPhi)); + vertical.mult(Math.cos(camTheta) * Math.sin(camPhi)); + side.mult(Math.sin(camTheta) * Math.sin(camPhi)); + + front.set(up).add(vertical).add(side); + + this.eyeX = camRadius * front.x + this.centerX; + this.eyeY = camRadius * front.y + this.centerY; + this.eyeZ = camRadius * front.z + this.centerZ; + + // update camera + this.camera( + this.eyeX, this.eyeY, this.eyeZ, + this.centerX, this.centerY, this.centerZ, + this.upX, this.upY, this.upZ + ); } - // update eye vector by calculate new front vector - up.mult(Math.cos(camPhi)); - vertical.mult(Math.cos(camTheta) * Math.sin(camPhi)); - side.mult(Math.sin(camTheta) * Math.sin(camPhi)); - - front.set(up).add(vertical).add(side); - - this.eyeX = camRadius * front.x + this.centerX; - this.eyeY = camRadius * front.y + this.centerY; - this.eyeZ = camRadius * front.z + this.centerZ; - - // update camera - this.camera( - this.eyeX, this.eyeY, this.eyeZ, - this.centerX, this.centerY, this.centerZ, - this.upX, this.upY, this.upZ - ); -}; - -/** + /** * Returns true if camera is currently attached to renderer. * @method _isActive * @private */ -p5.Camera.prototype._isActive = function() { - return this === this._renderer._curCamera; + _isActive() { + return this === this._renderer._curCamera; + } }; /** @@ -1864,7 +1866,7 @@ p5.Camera.prototype._isActive = function() { * Canvas switches between two camera views, each showing a series of spinning * 3D boxes. */ -p5.prototype.setCamera = function(cam) { +p5.prototype.setCamera = function (cam) { this._renderer._curCamera = cam; // set the projection matrix (which is not normally updated each frame)