From ff59babd1a141757fde0c49c3de729972bebc383 Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Sat, 18 Jan 2025 22:58:19 +0100 Subject: [PATCH] [orx-mesh-noise] Add generated and verified documentation --- .../src/commonMain/kotlin/MeshNoise.kt | 90 +++++++++++++++++-- .../src/jvmDemo/kotlin/DemoMeshNoise01.kt | 15 +++- 2 files changed, 97 insertions(+), 8 deletions(-) diff --git a/orx-mesh-noise/src/commonMain/kotlin/MeshNoise.kt b/orx-mesh-noise/src/commonMain/kotlin/MeshNoise.kt index cb35bd8f4..af5961053 100644 --- a/orx-mesh-noise/src/commonMain/kotlin/MeshNoise.kt +++ b/orx-mesh-noise/src/commonMain/kotlin/MeshNoise.kt @@ -23,6 +23,22 @@ fun uniformBarycentric(random: Random = Random.Default): Vector3 { return Vector3(b0, b1, 1.0 - b0 - b1) } +/** + * Generate a non-uniformly distributed barycentric coordinate + * @param random a random number generator + */ +fun nonUniformBarycentric(weight0: Double, weight1: Double, weight2: Double, random: Random = Random.Default): Vector3 { + val b = uniformBarycentric() + var b0 = b.x / weight0 + var b1 = b.y / weight1 + var b2 = b.z / weight2 + val totalWeight = b0 + b1 + b2 + b0 /= totalWeight + b1 /= totalWeight + b2 /= totalWeight + return Vector3(b0, b1, b2) +} + /** * Generate a uniformly distributed barycentric coordinate * @param random a random number generator @@ -32,7 +48,6 @@ fun hashBarycentric(seed: Int, x: Int): Vector3 { val v = fhash1D(seed, u.toRawBits().toInt() - x) - val su0 = sqrt(u) val b0 = 1.0 - su0 val b1 = v * su0 @@ -46,28 +61,64 @@ fun hashBarycentric(seed: Int, x: Int): Vector3 { * @param random a random number generator */ fun IIndexedPolygon.uniform(vertexData: IVertexData, random: Random = Random.Default): Vector3 { - require(positions.size == 3) { "polygon must be a triangle"} + require(positions.size == 3) { "polygon must be a triangle" } val x = vertexData.positions.slice(positions) val b = uniformBarycentric(random) return x[0] * b.x + x[1] * b.y + x[2] * b.z } +/** + * Computes a point within a triangle defined by the current indexed polygon. The point is determined + * through non-uniform barycentric coordinates, which are influenced by the specified weights. + * + * @param vertexData the vertex data containing positions and other attributes + * @param weight0 the weight associated with the first vertex of the triangle + * @param weight1 the weight associated with the second vertex of the triangle + * @param weight2 the weight associated with the third vertex of the triangle + * @param random an optional random number generator used for generating the barycentric coordinates + * @return a 3D vector representing a point within the triangle specified by the barycentric coordinates + */ +fun IIndexedPolygon.nonUniform( + vertexData: IVertexData, + weight0: Double, + weight1: Double, + weight2: Double, + random: Random = Random.Default +): Vector3 { + require(positions.size == 3) { "polygon must be a triangle" } + + val x = vertexData.positions.slice(positions) + val b = nonUniformBarycentric(weight0, weight1, weight2, random) + return x[0] * b.x + x[1] * b.y + x[2] * b.z +} + /** * Generate a uniformly distributed point that lies inside this [IIndexedPolygon] * @param vertexData vertex data used to resolve positions * @param random a random number generator */ -fun IIndexedPolygon.hash(vertexData: IVertexData, seed:Int, x: Int): Vector3 { - require(positions.size == 3) { "polygon must be a triangle"} +fun IIndexedPolygon.hash(vertexData: IVertexData, seed: Int, x: Int): Vector3 { + require(positions.size == 3) { "polygon must be a triangle" } val s = vertexData.positions.slice(positions) val b = hashBarycentric(seed, x) return s[0] * b.x + s[1] * b.y + s[2] * b.z } +/** + * Calculates the area of the triangular polygon. + * + * The method assumes that the polygon is a triangle and computes its area + * using the cross product formula. The computed area is a positive value as it + * represents the absolute area of the triangle. + * + * @param vertexData the vertex data containing positional information of the polygon vertices + * @return the area of the triangle as a Double + * @throws IllegalArgumentException if the polygon is not a triangle (i.e., does not have exactly 3 vertices) + */ internal fun IIndexedPolygon.area(vertexData: IVertexData): Double { - require(positions.size == 3) { "polygon must be a triangle"} + require(positions.size == 3) { "polygon must be a triangle" } val x = vertexData.positions.slice(positions) val u = x[1] - x[0] val v = x[2] - x[0] @@ -75,7 +126,32 @@ internal fun IIndexedPolygon.area(vertexData: IVertexData): Double { } /** - * Generate points on the surface described by the mesh data + * Computes the weighted area of a triangular polygon by scaling its area with the average of the given weights. + * + * @param vertexData the vertex data containing position information of the polygon vertices + * @param weight0 the weight associated with the first vertex of the polygon + * @param weight1 the weight associated with the second vertex of the polygon + * @param weight2 the weight associated with the third vertex of the polygon + * @return the weighted area of the triangular polygon + */ +internal fun IIndexedPolygon.weightedArea( + vertexData: IVertexData, + weight0: Double, + weight1: Double, + weight2: Double +): Double { + return area(vertexData) * (weight0 + weight1 + weight2) / 3.0 +} + +/** + * Generates a list of uniformly distributed points on the surface of the given mesh. + * + * The method uses triangulation and computes areas of triangular polygons to ensure + * uniform distribution of points across the surface. + * + * @param count the number of points to generate + * @param random a random number generator instance, defaulting to [Random.Default] + * @return a list of [Vector3] points uniformly distributed across the mesh surface */ fun IMeshData.uniform(count: Int, random: Random = Random.Default): List { val triangulated = triangulate() @@ -100,7 +176,7 @@ fun IMeshData.uniform(count: Int, random: Random = Random.Default): List { +fun IMeshData.hash(count: Int, seed: Int, x: Int): List { val triangulated = triangulate() val result = mutableListOf() val totalArea = triangulated.polygons.sumOf { it.area(vertexData) } diff --git a/orx-mesh-noise/src/jvmDemo/kotlin/DemoMeshNoise01.kt b/orx-mesh-noise/src/jvmDemo/kotlin/DemoMeshNoise01.kt index 5aab85220..37bc0fa2b 100644 --- a/orx-mesh-noise/src/jvmDemo/kotlin/DemoMeshNoise01.kt +++ b/orx-mesh-noise/src/jvmDemo/kotlin/DemoMeshNoise01.kt @@ -10,8 +10,21 @@ import org.openrndr.math.Vector3 import java.io.File import kotlin.random.Random + /** - * Demonstrate uniform point on mesh generation + * This demo creates a 3D visualization program using the OPENRNDR framework. + * It demonstrates loading an OBJ model, generating uniform points on the surface + * of the mesh, and rendering these points as small spheres using a custom shader. + * + * The following key processes are performed: + * - Loading mesh data from an OBJ file. + * - Generating a list of uniformly distributed points on the mesh surface. + * - Rendering the generated points with small spheres. + * - Using an "Orbital" extension for interactive camera control. + * - Applying a shader effect to visualize surface normals. + * + * The application runs with a window size of 720x720 pixels and positions the camera + * in front of the scene using the "Orbital" extension. */ fun main() { application {