From f53a549e3324df1b56cd9a2492bf395b4e24100f Mon Sep 17 00:00:00 2001 From: Michael Ennen Date: Tue, 9 Jul 2024 01:20:38 -0700 Subject: [PATCH] Cleanup (use const instead of let more), extract some helper methods, etc. --- delaunay.js | 54 ++++++------ index.html | 2 +- main.js | 235 ++++++++++++++++++++++++++-------------------------- math.js | 47 ++++++++++- 4 files changed, 192 insertions(+), 146 deletions(-) diff --git a/delaunay.js b/delaunay.js index 10fa7a1..c2eab59 100644 --- a/delaunay.js +++ b/delaunay.js @@ -48,10 +48,8 @@ class Triangle { const G = 2.0 * (A * (this.v2.y - this.v1.y) - B * (this.v2.x - this.v1.x)) - let dx, dy - - // Collinear points, get extremes and use midpoint as center - if (Math.round(Math.abs(G)) == 0) { + if (G == 0) { + // Collinear points (no circle through them exists) so: get extremes and use midpoint as center. const minx = Math.min(this.v0.x, this.v1.x, this.v2.x) const miny = Math.min(this.v0.y, this.v1.y, this.v2.y) const maxx = Math.max(this.v0.x, this.v1.x, this.v2.x) @@ -59,18 +57,19 @@ class Triangle { this.center = new Vertex((minx + maxx) / 2, (miny + maxy) / 2, 0) - dx = this.center.x - minx - dy = this.center.y - miny + const dx = this.center.x - minx + const dy = this.center.y - miny + this.radius = Math.sqrt(dx * dx + dy * dy); } else { const cx = (D * E - B * F) / G const cy = (A * F - C * E) / G this.center = new Vertex(cx, cy, 0) - dx = this.center.x - this.v0.x - dy = this.center.y - this.v0.y + const dx = this.center.x - this.v0.x + const dy = this.center.y - this.v0.y + this.radius = Math.sqrt(dx * dx + dy * dy); } - this.radius = Math.sqrt(dx * dx + dy * dy) } inCircumcircle(v) { @@ -85,24 +84,27 @@ class Triangle { } function getSuperTriangle(vertices) { - let minx = Infinity - let miny = Infinity - - let maxx = -Infinity - let maxy = -Infinity - vertices.forEach(vertex => { - minx = Math.min(minx, vertex.x) - miny = Math.min(minx, vertex.y) - maxx = Math.max(maxx, vertex.x) - maxy = Math.max(maxx, vertex.y) - }) + // Initialize with first vertex. + let minX = vertices[0].x + let minY = vertices[0].y + let maxX = vertices[0].x + let maxY = vertices[0].y + + // Loop through remaining vertices to find min/max. + for (let i = 1; i < vertices.length; i++) { + const vertex = vertices[i] + minX = Math.min(minX, vertex.x) + minY = Math.min(minY, vertex.y) + maxX = Math.max(maxX, vertex.x) + maxY = Math.max(maxY, vertex.y) + } - const dx = (maxx - minx) * 10 - const dy = (maxy - miny) * 10 + const dx = (maxX - minX) * 10 + const dy = (maxY - minY) * 10 - const v0 = new Vertex(minx - dx, miny - dy * 3, 0) - const v1 = new Vertex(minx - dx, maxy + dy, 0) - const v2 = new Vertex(maxx + dx * 3, maxy + dy, 0) + const v0 = new Vertex(minX - dx, minY - dy * 3, 0) + const v1 = new Vertex(minX - dx, maxY + dy, 0) + const v2 = new Vertex(maxX + dx * 3, maxY + dy, 0) return new Triangle(v0, v1, v2) } @@ -154,7 +156,7 @@ function uniqueEdges(edges) { function bowyerWatson(vertices) { // Create bounding 'super' triangle. - let st = getSuperTriangle(vertices) + const st = getSuperTriangle(vertices) // Initialize triangles while adding bounding triangle. let triangles = [st] diff --git a/index.html b/index.html index 8e5787e..f236a42 100644 --- a/index.html +++ b/index.html @@ -175,7 +175,7 @@ - +
diff --git a/main.js b/main.js index fd2f81b..232d1e3 100644 --- a/main.js +++ b/main.js @@ -308,6 +308,7 @@ function initialize3DTableGraphic(moodyReport) { ctx.fillText("Unable to initialize WebGL!", canvas.width / 2, canvas.height / 2) return } + gl.viewport(0, 0, gl.canvas.width, gl.canvas.height) document.querySelector("#zMultiplier").addEventListener("input", event => { zMultiplier = event.target.value @@ -459,6 +460,15 @@ function initialize3DTableGraphic(moodyReport) { buffers = getBuffers(gl, moodyReport, zMultiplier) function render(now) { + now = updateFps(now) + + boundingBoxCache[zMultiplier] = getBoundingBox(moodyReport) + drawTableSurface(moodyReport, gl, programInfo, buffers, texture) + requestAnimationFrame(render) + } + requestAnimationFrame(render) + + function updateFps(now) { now *= 0.001 const deltaTime = now - then then = now @@ -471,26 +481,22 @@ function initialize3DTableGraphic(moodyReport) { frameCursor %= maxFrames const averageFPS = totalFPS / numFrames avgElem.textContent = averageFPS.toFixed(1) - - boundingBoxCache[zMultiplier] = getBoundingBox(moodyReport) - drawTableSurface(moodyReport, gl, programInfo, buffers, texture) - requestAnimationFrame(render) + return now } - requestAnimationFrame(render) -} -let then = 0 -const frameTimes = [] -let frameCursor = 0 -let numFrames = 0 -const maxFrames = 20 -let totalFPS = 0 -const fpsElem = document.querySelector("#fps") -const avgElem = document.querySelector("#avg") + let then = 0 + const frameTimes = [] + let frameCursor = 0 + let numFrames = 0 + const maxFrames = 20 + let totalFPS = 0 + const fpsElem = document.querySelector("#fps") + const avgElem = document.querySelector("#avg") +} // Creates a 3D surface of the linear plate heights (calculated as Column #8 of the line tables). function drawTableSurface(moodyReport, gl, programInfo, buffers, texture) { - let tableModelMatrix = Mat4.create() + const tableModelMatrix = Mat4.create() Mat4.multiply(tableModelMatrix, tableModelMatrix, tableScaleMatrix) Mat4.multiply(tableModelMatrix, tableModelMatrix, tableRotationMatrix) Mat4.multiply(tableModelMatrix, tableModelMatrix, tableTranslateMatrix) @@ -527,8 +533,8 @@ function drawTableSurface(moodyReport, gl, programInfo, buffers, texture) { Mat4.invert(normalMatrix, Mat4.multiply(normalMatrix, viewMatrix, tableModelMatrix)) Mat4.transpose(normalMatrix, normalMatrix) - let lightPos = [document.getElementById("lightPosX").value, document.getElementById("lightPosY").value, document.getElementById("lightPosZ").value] - let lightPower = document.getElementById("lightPower").value + const lightPos = [document.getElementById("lightPosX").value, document.getElementById("lightPosY").value, document.getElementById("lightPosZ").value] + const lightPower = document.getElementById("lightPower").value gl.uniformMatrix4fv(programInfo.uniformLocations.projectionMatrix, false, projectionMatrix) gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, tableModelMatrix) @@ -586,103 +592,103 @@ function drawTableSurface(moodyReport, gl, programInfo, buffers, texture) { } const vsSource = `#version 300 es - in vec4 vertexPosition; - in vec3 vertexNormal; - in vec4 vertexColor; - in vec2 vertexTextureCoord; - in float vertexType; - uniform mat4 modelMatrix; - uniform mat4 viewMatrix; - uniform mat4 projectionMatrix; - uniform mat4 normalMatrix; - out lowp vec4 color; - out highp vec2 textureCoord; - out highp vec3 normalInterp; - out highp vec3 vertPos; - out highp float vVertexType; - - void main() { - gl_Position = projectionMatrix * viewMatrix * modelMatrix * vertexPosition; - color = vertexColor; - textureCoord = vertexTextureCoord; - vVertexType = vertexType; - - normalInterp = vec3(normalMatrix * vec4(vertexNormal, 0.0)); - vec4 vertPos4 = viewMatrix * modelMatrix * vertexPosition; - vertPos = vec3(vertPos4) / vertPos4.w; - } +in vec4 vertexPosition; +in vec3 vertexNormal; +in vec4 vertexColor; +in vec2 vertexTextureCoord; +in float vertexType; +uniform mat4 modelMatrix; +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; +uniform mat4 normalMatrix; +out lowp vec4 color; +out highp vec2 textureCoord; +out highp vec3 normalInterp; +out highp vec3 vertPos; +out highp float vVertexType; + +void main() { + gl_Position = projectionMatrix * viewMatrix * modelMatrix * vertexPosition; + color = vertexColor; + textureCoord = vertexTextureCoord; + vVertexType = vertexType; + + normalInterp = vec3(normalMatrix * vec4(vertexNormal, 0.0)); + vec4 vertPos4 = viewMatrix * modelMatrix * vertexPosition; + vertPos = vec3(vertPos4) / vertPos4.w; +} ` const fsSource = `#version 300 es - precision mediump float; - in lowp vec4 color; - in vec3 normalInterp; - in vec3 vertPos; - in highp vec2 textureCoord; - in highp float vVertexType; - const highp vec3 lightColor = vec3(1.0, 1.0, 1.0); - const highp vec3 ambientColor = vec3(0.4, 0.4, 0.4); - const highp vec3 diffuseColor = vec3(0.2, 0.2, 0.2); - const highp vec3 specColor = vec3(1.0, 1.0, 1.0); - const highp float shininess = 8.0; - const highp float screenGamma = 2.2; // Assume the monitor is calibrated to the sRGB color space - uniform vec3 lightPos; - uniform float lightPower; - uniform sampler2D sampler; - uniform bool showLines; - uniform bool showHeatmap; - uniform bool lightingOn; - out vec4 outputColor; - - void main() { - if (vVertexType == 0.0) { - // This vertex belongs to one of the Union jack Moody lines. - if (showLines) { - outputColor = color; - } else { - discard; - } +precision mediump float; +in lowp vec4 color; +in vec3 normalInterp; +in vec3 vertPos; +in highp vec2 textureCoord; +in highp float vVertexType; +const highp vec3 lightColor = vec3(1.0, 1.0, 1.0); +const highp vec3 ambientColor = vec3(0.4, 0.4, 0.4); +const highp vec3 diffuseColor = vec3(0.2, 0.2, 0.2); +const highp vec3 specColor = vec3(1.0, 1.0, 1.0); +const highp float shininess = 8.0; +const highp float screenGamma = 2.2; // Assume the monitor is calibrated to the sRGB color space +uniform vec3 lightPos; +uniform float lightPower; +uniform sampler2D sampler; +uniform bool showLines; +uniform bool showHeatmap; +uniform bool lightingOn; +out vec4 outputColor; + +void main() { + if (vVertexType == 0.0) { + // This vertex belongs to one of the Union jack Moody lines. + if (showLines) { + outputColor = color; + } else { + discard; + } + } else { + // This vertex belongs to the table mesh. + vec3 normal = normalize(normalInterp); + vec3 lightDir = lightPos - vertPos; + float distance = length(lightDir); + + distance = distance * distance; + lightDir = normalize(lightDir); + + float lambertian = max(dot(lightDir, normal), 0.0); + float specular = 0.0; + + if (lambertian > 0.0) { + vec3 viewDir = normalize(-vertPos); + vec3 halfDir = normalize(lightDir + viewDir); + float specAngle = max(dot(halfDir, normal), 0.0); + specular = pow(specAngle, shininess); + } + + vec3 colorLinear = ambientColor + + diffuseColor * lambertian * lightColor * lightPower / distance + + specColor * specular * lightColor * lightPower / distance; + // apply gamma correction (assume ambientColor, diffuseColor and specColor + // have been linearized, i.e. have no gamma correction in them) + vec3 colorGammaCorrected = pow(colorLinear, vec3(1.0 / screenGamma)); + if (!showHeatmap) { + if (lightingOn) { + outputColor = texture(sampler, textureCoord) * vec4(colorGammaCorrected, 1.0); } else { - // This vertex belongs to the table mesh. - vec3 normal = normalize(normalInterp); - vec3 lightDir = lightPos - vertPos; - float distance = length(lightDir); - - distance = distance * distance; - lightDir = normalize(lightDir); - - float lambertian = max(dot(lightDir, normal), 0.0); - float specular = 0.0; - - if (lambertian > 0.0) { - vec3 viewDir = normalize(-vertPos); - vec3 halfDir = normalize(lightDir + viewDir); - float specAngle = max(dot(halfDir, normal), 0.0); - specular = pow(specAngle, shininess); - } - - vec3 colorLinear = ambientColor + - diffuseColor * lambertian * lightColor * lightPower / distance + - specColor * specular * lightColor * lightPower / distance; - // apply gamma correction (assume ambientColor, diffuseColor and specColor - // have been linearized, i.e. have no gamma correction in them) - vec3 colorGammaCorrected = pow(colorLinear, vec3(1.0 / screenGamma)); - if (!showHeatmap) { - if (lightingOn) { - outputColor = texture(sampler, textureCoord) * vec4(colorGammaCorrected, 1.0); - } else { - outputColor = texture(sampler, textureCoord); - } - } else { - if (lightingOn) { - // No heatmap - show the table with a granite texture. - outputColor = vec4(color.rgb * colorGammaCorrected, 1.0); - } else { - outputColor = color; - } - } + outputColor = texture(sampler, textureCoord); + } + } else { + if (lightingOn) { + // No heatmap - show the table with a granite texture. + outputColor = vec4(color.rgb * colorGammaCorrected, 1.0); + } else { + outputColor = color; } } + } +} ` function getBuffers(gl, moodyReport, zMultiplier) { @@ -707,16 +713,13 @@ function getBuffers(gl, moodyReport, zMultiplier) { function getNonColorBuffers(gl, moodyReport, zMultiplier) { const vertices = moodyReport.vertices(zMultiplier).map(vertex => new Vertex(vertex[0], vertex[1], vertex[2])).flat(1) - const triangulation = bowyerWatson(vertices) - const triangulatedVertices = triangulation.map(triangle => [ triangle.v0.x, triangle.v0.y, triangle.v0.z, triangle.v1.x, triangle.v1.y, triangle.v1.z, triangle.v2.x, triangle.v2.y, triangle.v2.z]).flat(1) const axisSize = 20 - // FIXME: We want the line to always be on top of the surface but it can dip underneath it at extreme points. Adding 0.1 to z-coordinate is not good enough. // We need to calculate the slope from point x -> y and use it to find amount to add so line is always on top. const positions = new Float32Array( @@ -739,11 +742,11 @@ function getNonColorBuffers(gl, moodyReport, zMultiplier) { triangle.surfaceNormal().y * triangle.surfaceNormal().y + triangle.surfaceNormal().z * triangle.surfaceNormal().z) return [ - triangle.surfaceNormal().x / length, triangle.surfaceNormal().y / length, triangle.surfaceNormal().z / length, - triangle.surfaceNormal().x / length, triangle.surfaceNormal().y / length, triangle.surfaceNormal().z / length, - triangle.surfaceNormal().x / length, triangle.surfaceNormal().y / length, triangle.surfaceNormal().z / length] - }).flat(1) - const normals = new Float32Array(lineNormals.concat(triangleNormals).concat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + triangle.surfaceNormal().x / length, triangle.surfaceNormal().y / length, triangle.surfaceNormal().z / length, + triangle.surfaceNormal().x / length, triangle.surfaceNormal().y / length, triangle.surfaceNormal().z / length, + triangle.surfaceNormal().x / length, triangle.surfaceNormal().y / length, triangle.surfaceNormal().z / length + ]}).flat(1) + const normals = new Float32Array(lineNormals.concat(triangleNormals).concat(new Array(18).fill(0))) const normalBuffer = gl.createBuffer() gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer) gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW) diff --git a/math.js b/math.js index 044b5ef..ad8d6ec 100644 --- a/math.js +++ b/math.js @@ -2,6 +2,47 @@ // https://github.com/toji/gl-matrix/tree/glmatrix-next // Copyright 2022 Brandon Jones, Colin MacKenzie IV +class Vector2 extends Float32Array { + constructor(...values) { + switch(values.length) { + case 2: { + const v = values[0] + if (typeof v === 'number') { + super([v, values[1]]) + } else { + super(v, values[1], 2) + } + break; + } + case 1: { + const v = values[0] + if (typeof v === 'number') { + super([v, v]) + } else { + super(v, 0, 2) + } + break + } + default: + super(2) + break + } + } + + get x() { return this[0]; } + get y() { return this[1]; } + + static transformMat3(out, a, m) { + const x = a[0] + const y = a[1] + // const d = x * m[0 * 3 + 2] + y * m[1 * 3 + 2] + m[2 * 3 + 2] + const d = 1 + out[0] = m[0] * x + m[3] * y + m[6] / d + out[1] = m[1] * x + m[4] * y + m[7] / d + return out + } +} + class Vector3 extends Float32Array { constructor(...values) { @@ -118,9 +159,9 @@ class Vector3 extends Float32Array { } static magnitude(a) { - let x = a[0] - let y = a[1] - let z = a[2] + const x = a[0] + const y = a[1] + const z = a[2] return Math.sqrt(x * x + y * y + z * z) }