// James Brink, 7/23/2020 let output; // for debugging // _mainCanvasClass was an attempt to see if one could have a default // canvas. Unfortunately, it does not work completely. The // backgroundClass, rectClass, textClass work as hoped. However // mouseClickClass and the mouse variables do not work as desired. let _mainCanvasClass; function backgroundClass(arg1, arg2, arg3, arg4) { _mainCanvasClass.background(arg1, arg2, arg3, arg4); } // end backgroundClass function rectClass(x, y, width, height) { _mainCanvasClass.rect(x, y, width, height); } // end rectClass function textClass(msg, x, y) { _mainCanvasClass.text(msg, x, y); } function mouseClickClass() { _mainCanvasClass.mouseClicked(); } // the renderer parameter allows one to use WEBGL. createCanvasClass = function(w, h, renderer) { if (w <= 0 || h <= 0) { // this is an error print("Invalid canvas dimensions: createMyCanvas(" + w+", " +h +")"); } else { if (canvasCounter == 0) { _mainCanvasClass = new CanvasClass(w, h, renderer); _mainCanvasClass._isMainCanvas = true; return _mainCanvasClass; } else { return new CanvasClass(w, h, renderer); } } } // end createCanvasClass /** * canvasCounter is used to for giving id's to MyCanvas and for determining * the first canvas created: _mainCanvasClass */ let canvasCounter = 0; /** * The MyCanvas class which contains an HTML canvas. */ class CanvasClass { /** * The constructor declares all the properties and methods for MyCanvas. * * Most of this work was done before I found out that _renderer could * be used. I would have done some things differently if I had know * that _renderer was usable. I w */ constructor(itsWidth, itsHeight, renderer) { // itsP is no longer used. this.pInst is created differently than // what was done originally. this.width = itsWidth; this.height = itsHeight; canvasCounter ++; this._pInst = null; // the following takes advantage of instance mode. const __s = (sketch) => { sketch.setup = () => { let cnv = sketch.createCanvas(itsWidth, itsHeight, renderer); cnv.loadPixels(); this.__cnv = cnv; } }; let __myp5 = new p5(__s); this._pInst = __myp5; this.canvas = __myp5.canvas; this.elt = this.canvas; this.renderer = __myp5._renderer; //listMembersTable(this._renderer); this.isOver = false; // a CanvasClass extra. true if mouse is over the // true if mouse is over the canvas this._pInst._textSize = 12; // ***************************** // Color // ***************************** this._pInst._colorMode = _colorMode; this._pInst._colorMaxes = _colorMaxes; /* This is equivalent to the following 3 lines this._colorMaxes[RGB] = [255, 255, 255, 255]; this._colorMaxes[HSB] = [360, 100, 100, 1]; this._colorMaxes[HSL] = [360, 100, 100, 1]; */ // The this.color and this.colorMode is handled is very // unsatisfactory except that it works. Hopefully there is a better // way. this.color = function(r, g, b, a) { // tested // One would think that one could the p5.color function or just pass the // parameters on to this._pInst.background but when a 1 parameter color // number, e.g. background(210), the number would get converted to a // string and then not be interpreted correctly. this.color makes // special case of the 1 argument number to avoid the problem let saveMode = _colorMode; let mode = this._pInst._colorMode; let saveMaxes = _colorMaxes[mode]; colorMode(mode, this._pInst._colorMaxes[mode][0], this._pInst._colorMaxes[mode][1], this._pInst._colorMaxes[mode][2], this._pInst._colorMaxes[mode][3]); let c; if (r == null) { return "No color numbers specified"; } else if (g == null && (typeof r == "object")) { return r; } else if (g == null && (typeof r == "string") && (r.length > 3 || r == "red" || r == "tan")) { let str = r.trim().toLowerCase(); c = color(str); return c; // Return if string is a named color. } else if (b == null) { c = color(r, g); } else if (a == null) { c = color(r, g, b); } else { c = color(r, g, b, a); } colorMode(saveMode, saveMaxes[0], saveMaxes[1], saveMaxes[2], saveMaxes[3]); return c; } // end color this.blue = function(color) { // tested return this._pInst.blue(color); } this.green = function(color) { // tested return this._pInst.green(color); } this.red = function(color) { // tested return this._pInst.red(color); } this.brightness = function(color){ // tested return this._pInst.brightness(color); } this.hue = function(color){ // tested return this._pInst.hue(color); } this.lerp = function(start, stop, amt){ // tested return this._pInst.lerp(start, stop, amt); } this.saturation = function(color){ // tested return this._pInst.saturation(color); } this.colorMode = function(mode, max1, max2, max3, maxA) { if ( mode === RGB || mode === HSB || mode === HSL) { // Set color mode. this._pInst._colorMode = mode; // Set color maxes. let maxes = this._pInst._colorMaxes[mode]; if (arguments.length === 2) { maxes[0] = max1; // Red maxes[1] = max1; // Green maxes[2] = max1; // Blue maxes[3] = max1; // Alpha } else if (arguments.length === 4) { maxes[0] = max1; // Red maxes[1] = max2; // Green maxes[2] = max3; // Blue } else if (arguments.length === 5) { maxes[0] = max1; // Red maxes[1] = max2; // Green maxes[2] = max3; // Blue maxes[3] = maxA; // Alpha } } } // end colorMode this.set = function(x, y, c) { // tested this._pInst.set(x, y, c); } this.get = function(x, y, w, h) { // Works with 2 parameters. // With 0 or 4 parameters it returns an array of pixels but it is unclear // how to use that array. if (h || y == null) { // 4 parameters or no parameters if (y == null) { x = 0; y = 0; w = this.width; h = this.height; } let img = createImage(w, h); img.loadPixels(); let d = this.pixelDensity(); for (let i = x; i < x + w; i++) { for (let j = y; j < y + h; j++) { let off = (j * this.width + i) * d * 4; img.set(i, j, [this._pInst.pixels[off], this._pInst.pixels[off + 1], this._pInst.pixels[off + 2], this._pInst.pixels[off + 3]]); } } img.updatePixels(); return img; } else if (y) { this.loadPixels(); let d = this.pixelDensity(); let off = (y * this.width + x) * d * 4; let components = [ this._pInst.pixels[off], this._pInst.pixels[off + 1], this._pInst.pixels[off + 2], this._pInst.pixels[off + 3] ]; return components; } } // end get this.updatePixels = function(x, y, w, h) { // tested this._pInst.updatePixels(x, y, w, h); } this.pixelDensity = function(val) { // tested return this._pInst.pixelDensity(val); } this.loadPixels = function() { // ???????? this._pInst.loadPixels(); this.pixels = this._pInst.pixels; } // ***************************** // General purpose // ***************************** this.background = function(arg1, arg2, arg3, arg4) { // tested // One would think that one could the p5.color function or just pass the // parameters on to this._pInst.background but when a 1 parameter color // number, e.g. background(210), the number would get converted to a // string and then not be interpreted correctly. this.color makes // special case of the 1 argument number to avoid the problem. let c = this.color(arg1, arg2, arg3, arg4); this._pInst.background(c); } this.parent = function(parent) { // nonstandard - tested // If parameter parent isn't null, it makes parameter // parent the parent of this.canvas. In any case, it // returns the parent of this.canvas. let prnt = parent; if (parent != null) { if (typeof(parent) === "string") { if (parent[0] === "#") { parent = parent.substring(1); } prnt = document.getElementById(parent); } else if (parent.elt) { prnt = parent.elt; } prnt.appendChild(this.canvas); } return this.canvas.parentNode; } // end parent method /* this.child = function(child) { // mostly works // If child is a CanvasClass, the error // Node.appendChild: Argument 1 does not implement interface Node. // results. It works for divs. It also works with a regular p5 // canvas. return this.__cnv.child(child); } */ this.child = function(child) { // adapted from p5.js // makes parameter child a child of this.canvas if // parameter child is not void. // This does not result in the error mentioned above! if (child === void 0) { return this.canvas.childNodes; } if (typeof child == "string") { if (child[0] === "#") { child=child.substring(1); } child=document.getElementById(child); } else if (child.elt) { child = child.elt; } this.canvas.appendChild(child) return this; } // end child method this.id = function(id) { // tested if (id != null) { this._pInst.canvas.id = id; } return this._pInst.canvas.id; } this.push = function() { // tested this._pInst.push(); } this.pop = function() { // tested this._pInst.pop(); } this.show = function(inLineOption) { // Provides an "inline" option because canvases are normally inline. if (inLineOption){ // any argument will cause the display: inline this.style("display: inline"); } else { this.style("display: block"); } } this.hide = function() { this.style("display: none"); } this.remove = function() { // tested this._pInst.remove(); } this.position = function(x, y, positionType) { // copied from p5.js changing only the above line if (arguments.length === 0) { return { x: this.elt.offsetLeft, y: this.elt.offsetTop }; } else { let positionType = 'absolute'; if ( arguments[2] === 'static' || arguments[2] === 'fixed' || arguments[2] === 'relative' || arguments[2] === 'sticky' || arguments[2] === 'initial' || arguments[2] === 'inherit' ) { positionType = arguments[2]; } this.elt.style.position = positionType; this.elt.style.left = arguments[0] + 'px'; this.elt.style.top = arguments[1] + 'px'; this.x = arguments[0]; this.y = arguments[1]; return this; } } // ebd position this.center = function(align) { // center does not center a p5 canvas correctly if the canvas has // not been assigned a parent. This is a problem with p5.js this.__cnv.center(align); } this.style = function(prop, val) { // tested // adapted from p5.js if (val instanceof color) { val = 'rgba(' + val.levels[0] + ',' + val.levels[1] + ',' + val.levels[2] + ',' + val.levels[3] / 255 + ')'; } if (typeof val === 'undefined') { // input provided as single line string if (prop.indexOf(':') === -1) { let styles = window.getComputedStyle(this.elt); let style = styles.getPropertyValue(prop); return style; } else { let attrs = prop.split(';'); for (let i = 0; i < attrs.length; i++) { let parts = attrs[i].split(':'); if (parts[0] && parts[1]) { this.elt.style[parts[0].trim()] = parts[1].trim(); } } } } else { // input provided as key,val pair this.elt.style[prop] = val; if ( prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top' ) { let numVal = val.replace(/\D+/g, ''); this[prop] = parseInt(numVal, 10); } } return this; }; this.html = function(html, append) { // tested // this.__cnv.html(html, append); // results in undefined // this._pInst.html(html, append); // not a function // adapted from p5.js if (arguments.length === 0) { return this.elt.innerHTML; } else if (arguments[1]) { this.elt.insertAdjacentHTML('beforeend', arguments[0]); return this; } else { this.elt.innerHTML = arguments[0]; return this; } }; // Note: These class functions don't work with a p5 canvas // unless the class is named and used with the function. // For example: cvs = createCanvas(100, 100); cvs.addClass("border"); this.addClass = function(c) { // tested this.__cnv.addClass(c); } this.removeClass = function(c) { // tested this.__cnv.removeClass(c); } this.toggleClass = function(c) { // tested this.__cnv.toggleClass(c); } // If c is specified, it replaces any existing classes this.class = function(c) { // tested return this.__cnv.class(c); } this.hasClass = function(c) { // tested return this.__cnv.hasClass(c); } this.clear = function() { this._pInst.clear(); } this.erase = function(strengthFill, strengthStroke) { // tested this._pInst.erase(strengthFill, strengthStroke); } this.noErase = function() { // tested this._pInst.noErase(); } this.smooth = function() { // tested this._pInst.smooth(); // but hard to tell if it helps } this.noSmooth = function() { // tested this._pInst.noSmooth(); // but hard to tell if it helps } this.resizeCanvas = function(w, h, noRedraw) { // tested this.width = w; this.height = h; this._pInst.resizeCanvas(w, h, noRedraw); } this.size = function() { return this.__cnv.size(); } this.blendMode = function(mode) { // tested this._pInst.blendMode(mode); } this.attribute = function(attr, value) { // tested return this.__cnv.attribute(attr, value); } this.removeAttribute = function(attr) { // tested this.__cnv.removeAttribute(attr); } this.saveCanvas = function(a, b, c) { // tested this._pInst.saveCanvas(a, b, c); } // ***************************** // Drawings // ***************************** this.rect = function(x, y, w, h, tl, tr, br, bl) { // tested this._pInst.rect(x, y, w, h, tl, tr, br, bl); } this.ellipse = function(x, y, w, h) { // tested this._pInst.ellipse(x, y, w, h); } this.line = function(x1, y1, x2, y2) { // tested this._pInst.line(x1, y1, x2, y2); } this.triangle = function(x1, y1, x2, y2, x3, y3) { //tested this._pInst.triangle(x1, y1, x2, y2, x3, y3); } this.quad = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) { //tested this._pInst.quad(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); } this.point = function(x, y, z) {//tested this._pInst.point(x, y, z); } this.arc = function(x, y, w, h, start, stop, mode, detail) { // tested this._pInst.arc(x, y, w, h, start, stop, mode, detail); } this.circle = function(x, y, d) { //tested this.ellipse(x, y, d, d); } this.square = function(x, y, s, tl, tr, br, bl) { // tested this.rect(x, y, s, s, tl, tr, br, bl); } this.fill = function(arg1, arg2, arg3, arg4) { // tested let c = this.color(arg1, arg2, arg3, arg4); this._pInst.fill(c); } this.noFill = function() { // tested this._pInst.noFill(); } this.stroke = function(arg1, arg2, arg3, arg4) { // tested let c = this.color(arg1, arg2, arg3, arg4); this._pInst.stroke(c); } this.noStroke = function() { // tested this._pInst.noStroke(); } this.strokeWeight = function(weight) { // tested this._pInst.strokeWeight(weight); } this.strokeCap = function(cap) { // texted this._pInst.strokeCap(cap); } this.strokeJoin = function(join) { // tested this._pInst.strokeJoin(join); } this.ellipseMode = function(mode) { //tested this._pInst.ellipseMode(mode); } this.rectMode = function(mode) { //tested this._pInst.rectMode(mode); } // ***************************** // Shapes, Bezier and Curves // ***************************** this.beginShape = function(kind) { // tested this._pInst.beginShape(kind); } this.endShape = function(mode) { // tested this._pInst.endShape(mode); } this.vertex = function(x, y, z, u, v) { // tested this._pInst.vertex(x, y, z, u, v); } this.beginContour = function() { // tested this._pInst.beginContour(); } this.endContour = function() { // tested this._pInst.endContour(); } this.curveVertex = function(x, y, z) { // tested this._pInst.curveVertex(x, y, z); } this.bezierVertex = function(x2, y2, z2, x3, y3, z3, x4, y4, z4){ // tested this._pInst.bezierVertex(x2, y2, z2, x3, y3, z3, x4, y4, z4); } this.quadraticVertex = function(cx, cy, cz, x3, y3, z3) { // tested this._pInst.quadraticVertex(cx, cy, cz, x3, y3, z3); } this.bezier = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) { // tested this._pInst.bezier(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); } this.bezierDetail = function(detail) { this._pInst.bezierDetail(detail); } this.bezierPoint = function(a, b, c, d, t) { // tested return this._pInst.bezierPoint(a, b, c, d, t); } this.bezierTangent = function(a, b, c, d, t) { // tested return this._pInst.bezierTangent(a, b, c, d, t); } this.curve = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) { // tested this._pInst.curve(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); } this.curveDetail = function(resolution) { this._pInst.curveDetail(resolution); } this.curveTightness = function(amount) { // tested this._pInst.curveTightness(amount); } this.curvePoint = function(a, b, c, d, t) { // tested return this._pInst.curvePoint(a, b, c, d, t); } this.curveTangent = function(a, b, c, d, t) { // tested return this._pInst.curveTangent(a, b, c, d, t); } // ***************************** // text // ***************************** this.text = function(str, x, y, w, h) { // tested if (h) { this._pInst.text(str, x, y, w, h); } else if (w) { this._pInst.text(str, x, y, w); } else { this._pInst.text(str, x, y); } } this.textFont = function(font, size) { // tested this._pInst.textFont(font, size); } this.textWidth = function(theText) { // tested return this._pInst.textWidth(theText); } this.textStyle = function(theStyle) { // tested this._pInst.textStyle(theStyle); } this.textSize = function(theSize) { // tested // Note: this.__cnv._textSize seems OK // but this._pInst._textSize doesn't seem to update this._pInst.textSize(theSize); } this.textAlign = function(horizAlign, vertAlign) { // tested this._pInst.textAlign(horizAlign, vertAlign); } this.textAscent = function() { // tested return this._pInst.textAscent(); } this.textDescent = function() { // tested return this._pInst.textDescent(); } this.textLeading = function(leading) { // tested this._pInst.textLeading(leading); } this.loadFont = function(path, callback, onError) { return this._pInst.loadFont(path, callback, onError); } /* problem this.p5 is undefined this.p5.Font = function(pInst) { this.pInst.p5.Font(pInst); } */ // ***************************** // Image // ***************************** this._imageMode = CORNER; this.blend = function(srcImage, sx, sy, sw, sh, dx, dy, dw, dh, blendMode) { this._pInst.blend(srcImage, sx, sy, sw, sh, dx, dy, dw, dh, blendMode); } this.image = function(img, dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight) { this._pInst.image(img, dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight); } // end image method this.loadImage = function(path, successCallback, failureCallback) { this._pInst.loadImage(path, successCallback, failureCallback); } this.imageMode = function(mode) { this._imageMode = mode; this._pInst.imageMode(mode); } /** * getImageFromWebPage * Gets and returns an image from a webpage that can be used by * image(....) * parameter: * id: the id of an image on web page. * Example usage: in the web page * *
GrandCanyon
* ... * In the p5js file * let img; * function setup() { * can = createCanvasClass(200, 200); * img = can.getImageFromWebPage("photo"); * ... * } * function draw() { * ... * can.image(img, ....) ; * ... * } * * Note: for debugging, you can change "display: none" to * "display:inline" or "display:block" temporarily to make * sure the image is loaded. */ this.getImageFromWebPage = function(id) { let img = document.getElementById(id); if (img == "[object HTMLImageElement]") { let aImage = createImg(img.src); return aImage; } else { alert(id + " is not an HTMLImageElement") return null; } } // getImageFromWebPage // ***************************** // Webgl // ***************************** // these work in setup() but have trouble in draw() if rotateZ, rotateX, // or rotateY are used. this.normalMaterial = function() { // tested this._pInst.normalMaterial(); } this.box = function(width, height, depth, detailX, detailY) {// tested this._pInst.box(width, height, depth, detailX, detailY); } this.sphere = function(radius, detailX, detailY) { // tested this._pInst.sphere(radius, detailX, detailY); } this.cone = function(radius, height, detailX, detailY, cap) { // tested this._pInst.cone(radius, height, detailX, detailY, cap); } this.cylinder = function(radius, height, detailX, detailY, bottomCap, topCap) // tested { this._pInst.cylinder(radius, height, detailX, detailY, bottomCap, topCap); } this.torus = function(radius, tubeRadius, detailX, detailY) { // tested this._pInst.torus(radius, tubeRadius, detailX, detailY); } this.plane = function(width, height, detailX, detailY) { // tested this._pInst.plane(width, height, detailX, detailY); } // just like fill and stroke, the colors have to set by // this.color. These methods have been tested only with 3 parameters // but this.color has been carefully tested. this.ambientLight = function(v1, v2, v3, alpha) { let c = this.color(v1, v2, v3, alpha); // print("ambientLight: " + c); this._pInst.ambientLight(c); } this.ambientMaterial = function(v1, v2, v3) { let c = this.color(v1, v2, v3); // alert(c); // print("ambientMaterial: " + c); this._pInst.ambientMaterial(c); } this.emissiveMaterial = function(v1, v2, v3, a){ let c = this.color(v1, v2, v3, a); this._pInst.emissiveMaterial(c); } /* * These functions will not work at this point. In some cases the problem * is with pointLight doesn't work correctly in vesion 1.0 * */ this.specularMaterial = function(v1, v2, v3, alpha) { if (arguments.length >= 3) { let c = this.color(v1, v2, v3, alpha); this._pInst.specularMaterial(c); } else { this._pInst.specularMaterial(v1, v2); } } // end specularMaterial // this is a little messy because pointLight has 4 distinct // parameter options. The code to sort them out was taken from // https://github.com/processing/p5.js/blob/1.0.0/src/webgl/light.js#L281 // // The reference for pointLight doesn't work. It appears the // problem is with the p5 code for pointLight so I can't tell // if this code work or not. This is a known problem with // version 1.0 this.pointLight = function(v1, v2, v3, x, y, z) { let c; if (v1 instanceof p5.Color) { c = v1; } else { c = this.color(v1, v2, v3); } let _x, _y, _z; const v = arguments[arguments.length - 1]; if (typeof v === 'number') { _x = arguments[arguments.length - 3]; _y = arguments[arguments.length - 2]; _z = arguments[arguments.length - 1]; } else { _x = v.x; _y = v.y; _z = v.z; } this._pInst.pointLight(c, _x, _y, _z); } // end pointLight this.directionalLight = function(v1, v2, v3, x, y, z) { let c; if (v1 instanceof p5.Color) { c = v1; } else { c = this.color(v1, v2, v3); } let _x, _y, _z; const v = arguments[arguments.length - 1]; if (typeof v === 'number') { _x = arguments[arguments.length - 3]; _y = arguments[arguments.length - 2]; _z = arguments[arguments.length - 1]; } else { _x = v.x; _y = v.y; _z = v.z; } this._pInst.directionalLight(c, _x, _y, _z); } // end directionalLight // Sorry that this is so long but there are 8 different ways for the // parameters to set and each was two optional parameters // Adapted from https://p5js.org/reference/#/p5/spotLight this.spotLight = function(v1, v2, v3, x, y, z, nx, ny, nz, angle, concentration) { var color, position, direction; var length = arguments.length; switch (length) { case 11: case 10: color = this.color(v1, v2, v3); position = createVector(x, y, z); direction = createVector(nx, ny, nz); break; case 9: if (v1 instanceof p5.Color) { color = v1; position = createVector(v2, v3, x); direction = createVector(y, z, nx); angle = ny; concentration = nz; } else if (x instanceof p5.Vector) { color = this.color(v1, v2, v3); position = x; direction = createVector(y, z, nx); angle = ny; concentration = nz; } else if (nx instanceof p5.Vector) { color = this.color(v1, v2, v3); position = createVector(x, y, z); direction = nx; angle = ny; concentration = nz; } else { color = this.color(v1, v2, v3); position = createVector(x, y, z); direction = createVector(nx, ny, nz); } break; case 8: if (v1 instanceof p5.Color) { color = v1; position = createVector(v2, v3, x); direction = createVector(y, z, nx); angle = ny; } else if (x instanceof p5.Vector) { color = this.color(v1, v2, v3); position = x; direction = createVector(y, z, nx); angle = ny; } else { color = this.color(v1, v2, v3); position = createVector(x, y, z); direction = nx; angle = ny; } break; case 7: if ( v1 instanceof p5.Color && v2 instanceof p5.Vector ) { color = v1; position = v2; direction = createVector(v3, x, y); angle = z; concentration = nx; } else if ( v1 instanceof p5.Color && y instanceof p5.Vector ) { color = v1; position = createVector(v2, v3, x); direction = y; angle = z; concentration = nx; } else if ( x instanceof p5.Vector && y instanceof p5.Vector ) { color = this.color(v1, v2, v3); position = x; direction = y; angle = z; concentration = nx; } else if (v1 instanceof p5.Color) { color = v1; position = createVector(v2, v3, x); direction = createVector(y, z, nx); } else if (x instanceof p5.Vector) { color = this.color(v1, v2, v3); position = x; direction = createVector(y, z, nx); } else { color = this.color(v1, v2, v3); position = createVector(x, y, z); direction = nx; } break; case 6: if ( x instanceof p5.Vector && y instanceof p5.Vector ) { color = this.color(v1, v2, v3); position = x; direction = y; angle = z; } else if ( v1 instanceof p5.Color && y instanceof p5.Vector ) { color = v1; position = createVector(v2, v3, x); direction = y; angle = z; } else if ( v1 instanceof p5.Color && v2 instanceof p5.Vector ) { color = v1; position = v2; direction = createVector(v3, x, y); angle = z; } break; case 5: if ( v1 instanceof p5.Color && v2 instanceof p5.Vector && v3 instanceof p5.Vector ) { color = v1; position = v2; direction = v3; angle = x; concentration = y; } else if ( x instanceof p5.Vector && y instanceof p5.Vector ) { color = this.color(v1, v2, v3); position = x; direction = y; } else if ( v1 instanceof p5.Color && y instanceof p5.Vector ) { color = v1; position = createVector(v2, v3, x); direction = y; } else if ( v1 instanceof p5.Color && v2 instanceof p5.Vector ) { color = v1; position = v2; direction = createVector(v3, x, y); } break; case 4: color = v1; position = v2; direction = v3; angle = x; break; case 3: color = v1; position = v2; direction = v3; break; default: console.warn( 'Sorry, input for spotlight() is not in prescribed format. Too '.concat( length < 3 ? 'few' : 'many', ' arguments were provided' ) ); return this; } this._pInst.spotLight(color, position, direction, angle, concentration); } // end spotLight this.specularColor = function(v1, v2, v3) { let c; if (arguments.length == 3) { c = this.color(v1, v2, v3); this._pInst.specularColor(c); } else { this._pInst.specularColor(v1); } } this.shininess = function(shine) { this._pInst.shininess(shine); } /* Doesn't work. Probably a problem with the mouse this.orbitControl = function(sensitivityX, sensitivityY, sensitivityZ) { this._pInst.orbitControl(sensitivityX, sensitivityY, sensitivityZ); } */ this.camera = function(x, y, z, centerX, centerY, centerZ, upX, upY, upZ) { this._pInst.camera(x, y, z, centerX, centerY, centerZ, upX, upY, upZ); } // ***************************** // Transform // ***************************** // translate works fine but it doesn't reset on every new frame. // that may be a problem with some of the other methods. this.translate = function(x, y, z) { // tested this._pInst.translate(x, y, z); } this.rotate = function(angle, axis) { // tested this._pInst.rotate(angle, axis); } this.scale = function(s, y, z) { // tested this._pInst.scale(s, y, z); } this.rotateX = function(angle) { // tested this._pInst.rotateX(angle); // It doesn't seem to make any difference // this.__cnv.rotateX(angle); // if _pInst or __cnv is used. } this.rotateY = function(angle) { // tested this._pInst.rotateY(angle); // this.__cnv.rotateY(angle); } this.rotateZ = function(angle) { // tested this._pInst.rotateZ(angle); // this.__cnv.rotateZ(angle); } this.shearX = function(angle) { // tested this._pInst.shearX(angle); } this.shearY = function(angle) { // tested this._pInst.shearY(angle); } this.applyMatrix = function(a, b, c, d, e, f) { // tested // resetMatrix() is not applied at the beginning of every // new frame. this._pInst.applyMatrix(a, b, c, d, e, f); } this.resetMatrix = function() { // tested this._pInst.resetMatrix(); } // ***************************** // Events // ***************************** this.mouseIsPressed = false; this.keyIsPressed = false; // Initialize mouse variables this.mouseX = this._pInst.mouseX; this.mouseY = this._pInst.mouseY; this.movedX = this._pInst.movedX; this.movedY = this._pInst.movedY; this.pmouseX = this._pInst.pmouseX; this.pmouseY = this._pInst.pmouseY; this.winMouseX = this._pInst.winMouseX; this.winMouseY = this._pInst.winMouseX; this.pwinMouseX = this._pInst.pwinMouseY; this.pwinMouseY = this._pInst.pwinMouseY; this.mousePressed = function(fxn) { // tested // if this type of function is called with no arguments a error // will occur in p5.js. So "false" is passed to avoid the problem. if (fxn) { this.__cnv.mousePressed(fxn); } else { this.__cnv.mousePressed(false); } } this.mouseReleased = function(fxn){ // tested this.__cnv.mouseReleased(fxn); if (fxn) { this.__cnv.mouseReleased(fxn); } else { this.__cnv.mouseReleased(false); } } this.mouseClicked = function(fxn) { // tested if (fxn) { this.__cnv.mouseClicked(fxn); } else { this.__cnv.mouseClicked(false); } } this.doubleClicked = function(fxn) { // tested if (fxn) { this.__cnv.doubleClicked(fxn); } else { this.__cnv.doubleClicked(false); } } this.mouseWheel = function(fxn) { // tested if (fxn) { this.__cnv.mouseWheel(fxn); } else { this.__cnv.mouseWheel(false); } } this.mouseOver = function(fxn) { // tested this.over = true; if (fxn) { this.__cnv.mouseOver(fxn); } else { this.__cnv.mouseOver(false); } } this.mouseOut = function(fxn) { // tested this.out = false if (fxn) { this.__cnv.mouseOut(fxn); } else { this.__cnv.mouseOut(false); } } this.mouseMoved = function(fxn) { // tested if (fxn) { this.__cnv.mouseMoved(fxn); } else { this.__cnv.mouseMoved(false); } } this.touchStarted = function(fxn) { if (fxn) { this.__cnv.touchStarted(fxn); } else { this.__cnv.touchStarted(false); } } this.touchMoved = function(fxn) { if (fxn) { this.__cnv.touchMoved(fxn); } else { this.__cnv.touchMoved(false); } } this.touchEnded = function(fxn) { if (fxn) { this.__cnv.touchEnded(fxn); } else { this.__cnv.touchEnded(false); } } // ***************************************** // touch functions might not perfect but seem to work // in simple testing! Note: touches[] // is updated to the location of the touch BEFORE the // user's touchStarted is called so they can be used as // the starting point of the touch. (Unfortunately, it appears // p5's touchStarted is called before its touches[] is // updated. // ***************************************** this.touches = []; // _updateTouchCoords and getTouchInfo adapted from p5.js this._updateTouchCoords = function(e) { if (this.elt !== null) { touches = []; for (let i = 0; i < e.touches.length; i++) { touches[i] = getTouchInfo( this.elt, this.width, this.height, e, i ); if (i == 0) { // p5.js uses touch info to update mouseX and mouseY so add this this.pmouseX = this.mouseX; this.pmouseY = this.mouseY; this.mouseX = touches[0].x; // use touch x,y for mouseX and Y. this.mouseY = touches[0].y; // The items in a touch element are this.movedX = this.mouseX - this.pmouseX; this.movedY = this.mouseY - this.pmouseY; this.pwinMouseX = this.winMouseX; // x, y (not rounded), winX, this.pwinMouseY = this.winMouseY; // winY, and id. this.winMouseX = touches[0].winX; this.winMouseY = touches[0].winY; } } this.touches = touches; } return false; }; function getTouchInfo(canvas, w, h, e) { let i = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; let rect = canvas.getBoundingClientRect(); let sx = canvas.scrollWidth / w || 1; let sy = canvas.scrollHeight / h || 1; let touch = e.touches[i] || e.changedTouches[i]; // print("getTouchInfo; " + i + " " + (touch.clientX - rect.left) / sx); return { x: (touch.clientX - rect.left) / sx, y: (touch.clientY - rect.top) / sy, winX: touch.clientX, winY: touch.clientY, id: touch.identifier }; } this.canvas.addEventListener("touchstart", e => { this._updateTouchCoords(e);}); this.canvas.addEventListener("touchmove", e => { this._updateTouchCoords(e);}); this.canvas.addEventListener("touchend", e => { this._updateTouchCoords(e);}); this.dragOver = function(fxn) { if (fxn) { this.__cnv.dragOver(fxn); } else { this.__cnv.dragOver(false); } } this.dragLeave = function(fxn) { if (fxn) { this.__cnv.dragLeave(fxn); } else { this.__cnv.dragLeave(false); } } this.drop = function(fxn, dropped) { this.__cnv.drop(fxn, dropped); } this._events = this._pInst._events; this.keyDown = function(fn) { // key (not this.key) is p5.js key. This only happens once per key // down but strange things may happen if more than one key pressed // at the same time. this.key may be "shift', "control" , .... It shows // cap letter if cap letters if shift is pressed first. // // Apparently this.canvas.addEventListener event doesn't happen for // keydown so we are using a global event and check on location of // mouse. this._events.keydown = fn; addEventListener("keydown", e => { if (this.isOver) { this.keyIsPressed = true; this._events.keydown(); } }); } // end keyDown this.keyUp = function(fn) { // this happens even if the mouse has not been or is no longer in the // CanvasClass. this._events.keyup = fn; addEventListener("keyup", e => { this.keyIsPressed = false; this._events.keyup(); }); } // end keyUp this.canvas.addEventListener("mousedown", e => { this.mouseIsPressed = true;}); this.canvas.addEventListener("mouseup", e => { this.mouseIsPressed = false;}); this.canvas.addEventListener("mouseover", e => { this._mouseOver();}); this.canvas.addEventListener("mouseout", e => { this._mouseOut();}); this.canvas.addEventListener("mousemove", e => { this._mouseMove(e);}); } // end constructor _mouseOver() { this.isOver = true; this.focused = focused && this.isOver; } // end _mouseOver _mouseOut() { this.isOver = false; this.focused = focused && this.isOver; } // end _mouseOut _mouseMove(e) { this.pmouseX = this.mouseX; this.pmouseY = this.mouseY; this.pwinMouseX = this.winMouseX; this.pwinMouseY = this.winMouseY; this.mouseX = e.offsetX; this.mouseY = e.offsetY; this.movedX = this.mouseX - this.pmouseX; this.movedY = this.mouseY - this.pmouseY; this.winMouseX = e.x; this.winMouseY = e.y; } // end _mouseMove } // end CanvasClass let globalMouse = null; let gMouseX = 0; let gMouseY = 0; addEventListener('mousemove', mouseMovedEvent); addEventListener('touchmove', touchmoveEvent); function mouseMovedEvent(event) { // listMembersTable(event); if (globalMouse) { gMouseX = event.pageX - globalMouse.canvas.offsetLeft; gMouseY = event.pageY - globalMouse.canvas.offsetTop; } } function touchmoveEvent(event) { if (globalMouse) { gMouseX = event.touches[0].pageX - globalMouse.canvas.offsetLeft; gMouseY = event.touches[0].pageY - globalMouse.canvas.offsetTop; return false; } }