diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index 251ee18adb..bf31695121 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -1217,56 +1217,126 @@ p5.RendererGL.prototype.arc = function(args) { }; p5.RendererGL.prototype.rect = function(args) { - const perPixelLighting = this._pInst._glAttributes.perPixelLighting; const x = args[0]; const y = args[1]; const width = args[2]; const height = args[3]; - const detailX = args[4] || (perPixelLighting ? 1 : 24); - const detailY = args[5] || (perPixelLighting ? 1 : 16); - const gId = `rect|${detailX}|${detailY}`; - if (!this.geometryInHash(gId)) { - const _rect = function() { - for (let i = 0; i <= this.detailY; i++) { - const v = i / this.detailY; - for (let j = 0; j <= this.detailX; j++) { - const u = j / this.detailX; - const p = new p5.Vector(u, v, 0); - this.vertices.push(p); - this.uvs.push(u, v); + + if (typeof args[4] === 'undefined') { + // Use the retained mode for drawing rectangle, + // if args for rounding rectangle is not provided by user. + const perPixelLighting = this._pInst._glAttributes.perPixelLighting; + const detailX = args[4] || (perPixelLighting ? 1 : 24); + const detailY = args[5] || (perPixelLighting ? 1 : 16); + const gId = `rect|${detailX}|${detailY}`; + if (!this.geometryInHash(gId)) { + const _rect = function() { + for (let i = 0; i <= this.detailY; i++) { + const v = i / this.detailY; + for (let j = 0; j <= this.detailX; j++) { + const u = j / this.detailX; + const p = new p5.Vector(u, v, 0); + this.vertices.push(p); + this.uvs.push(u, v); + } } - } - // using stroke indices to avoid stroke over face(s) of rectangle - if (detailX > 0 && detailY > 0) { - this.strokeIndices = [ - [0, detailX], - [detailX, (detailX + 1) * (detailY + 1) - 1], - [(detailX + 1) * (detailY + 1) - 1, (detailX + 1) * detailY], - [(detailX + 1) * detailY, 0] - ]; - } - }; - const rectGeom = new p5.Geometry(detailX, detailY, _rect); - rectGeom - .computeFaces() - .computeNormals() - ._makeTriangleEdges() - ._edgesToVertices(); - this.createBuffers(gId, rectGeom); - } + // using stroke indices to avoid stroke over face(s) of rectangle + if (detailX > 0 && detailY > 0) { + this.strokeIndices = [ + [0, detailX], + [detailX, (detailX + 1) * (detailY + 1) - 1], + [(detailX + 1) * (detailY + 1) - 1, (detailX + 1) * detailY], + [(detailX + 1) * detailY, 0] + ]; + } + }; + const rectGeom = new p5.Geometry(detailX, detailY, _rect); + rectGeom + .computeFaces() + .computeNormals() + ._makeTriangleEdges() + ._edgesToVertices(); + this.createBuffers(gId, rectGeom); + } - // only a single rectangle (of a given detail) is cached: a square with - // opposite corners at (0,0) & (1,1). - // - // before rendering, this square is scaled & moved to the required location. - const uMVMatrix = this.uMVMatrix.copy(); - try { - this.uMVMatrix.translate([x, y, 0]); - this.uMVMatrix.scale(width, height, 1); + // only a single rectangle (of a given detail) is cached: a square with + // opposite corners at (0,0) & (1,1). + // + // before rendering, this square is scaled & moved to the required location. + const uMVMatrix = this.uMVMatrix.copy(); + try { + this.uMVMatrix.translate([x, y, 0]); + this.uMVMatrix.scale(width, height, 1); + + this.drawBuffers(gId); + } finally { + this.uMVMatrix = uMVMatrix; + } + } else { + // Use Immediate mode to round the rectangle corner, + // if args for rounding corners is provided by user + let tl = args[4]; + let tr = typeof args[5] === 'undefined' ? tl : args[5]; + let br = typeof args[6] === 'undefined' ? tr : args[6]; + let bl = typeof args[7] === 'undefined' ? br : args[7]; + + let a = x; + let b = y; + let c = width; + let d = height; + + c += a; + d += b; + + if (a > c) { + const temp = a; + a = c; + c = temp; + } - this.drawBuffers(gId); - } finally { - this.uMVMatrix = uMVMatrix; + if (b > d) { + const temp = b; + b = d; + d = temp; + } + + const maxRounding = Math.min((c - a) / 2, (d - b) / 2); + if (tl > maxRounding) tl = maxRounding; + if (tr > maxRounding) tr = maxRounding; + if (br > maxRounding) br = maxRounding; + if (bl > maxRounding) bl = maxRounding; + + let x1 = a; + let y1 = b; + let x2 = c; + let y2 = d; + + this.beginShape(); + if (tr !== 0) { + this.vertex(x2 - tr, y1); + this.quadraticVertex(x2, y1, x2, y1 + tr); + } else { + this.vertex(x2, y1); + } + if (br !== 0) { + this.vertex(x2, y2 - br); + this.quadraticVertex(x2, y2, x2 - br, y2); + } else { + this.vertex(x2, y2); + } + if (bl !== 0) { + this.vertex(x1 + bl, y2); + this.quadraticVertex(x1, y2, x1, y2 - bl); + } else { + this.vertex(x1, y2); + } + if (tl !== 0) { + this.vertex(x1, y1 + tl); + this.quadraticVertex(x1, y1, x1 + tl, y1); + } else { + this.vertex(x1, y1); + } + this.endShape(constants.CLOSE); } return this; };