From c48aa83cedf53c6f42177ba779175329dc811a5b Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Tue, 19 Mar 2024 17:54:03 +0100 Subject: [PATCH] [orx-shapes, orx-mesh-generator] Move frames code from orx-mesh-generator to orx-shapes --- orx-mesh-generators/build.gradle.kts | 1 + .../src/commonMain/kotlin/Extrusion.kt | 4 +- .../src/commonMain/kotlin/ExtrusionMorphed.kt | 1 + .../src/commonMain/kotlin/ExtrusionScaled.kt | 1 + .../src/commonMain/kotlin/frames}/Frames.kt | 42 +++++++----- .../kotlin/frames/Path3DExtensions.kt | 20 ++++++ .../kotlin/rectify/RectifiedPath.kt | 8 +++ .../adjust/DemoAdjustContourContinue01.kt | 4 +- .../kotlin/arrangement/DemoArrangement04.kt | 44 +++++++++++++ .../src/jvmDemo/kotlin/frames/DemoFrames01.kt | 65 +++++++++++++++++++ 10 files changed, 167 insertions(+), 23 deletions(-) rename {orx-mesh-generators/src/commonMain/kotlin => orx-shapes/src/commonMain/kotlin/frames}/Frames.kt (57%) create mode 100644 orx-shapes/src/commonMain/kotlin/frames/Path3DExtensions.kt create mode 100644 orx-shapes/src/jvmDemo/kotlin/arrangement/DemoArrangement04.kt create mode 100644 orx-shapes/src/jvmDemo/kotlin/frames/DemoFrames01.kt diff --git a/orx-mesh-generators/build.gradle.kts b/orx-mesh-generators/build.gradle.kts index 2b03a28c..452baa3f 100644 --- a/orx-mesh-generators/build.gradle.kts +++ b/orx-mesh-generators/build.gradle.kts @@ -9,6 +9,7 @@ kotlin { dependencies { api(libs.openrndr.application) api(libs.openrndr.math) + implementation(project(":orx-shapes")) } } diff --git a/orx-mesh-generators/src/commonMain/kotlin/Extrusion.kt b/orx-mesh-generators/src/commonMain/kotlin/Extrusion.kt index 45bace82..db81336c 100644 --- a/orx-mesh-generators/src/commonMain/kotlin/Extrusion.kt +++ b/orx-mesh-generators/src/commonMain/kotlin/Extrusion.kt @@ -1,14 +1,12 @@ package org.openrndr.extra.meshgenerators +import org.openrndr.extra.shapes.frames.frames import org.openrndr.math.Matrix44 import org.openrndr.math.Vector2 import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 -import org.openrndr.math.transforms.normalMatrix import org.openrndr.shape.Path3D import org.openrndr.shape.Shape import org.openrndr.shape.ShapeContour -import org.openrndr.shape.Triangle /** * Writes quads to [writer] creating a surface that connects two diff --git a/orx-mesh-generators/src/commonMain/kotlin/ExtrusionMorphed.kt b/orx-mesh-generators/src/commonMain/kotlin/ExtrusionMorphed.kt index 236881c1..6ca44c46 100644 --- a/orx-mesh-generators/src/commonMain/kotlin/ExtrusionMorphed.kt +++ b/orx-mesh-generators/src/commonMain/kotlin/ExtrusionMorphed.kt @@ -1,5 +1,6 @@ package org.openrndr.extra.meshgenerators +import org.openrndr.extra.shapes.frames.frames import org.openrndr.math.Matrix44 import org.openrndr.math.Vector3 import org.openrndr.shape.Path3D diff --git a/orx-mesh-generators/src/commonMain/kotlin/ExtrusionScaled.kt b/orx-mesh-generators/src/commonMain/kotlin/ExtrusionScaled.kt index 9b6dffc3..f971281b 100644 --- a/orx-mesh-generators/src/commonMain/kotlin/ExtrusionScaled.kt +++ b/orx-mesh-generators/src/commonMain/kotlin/ExtrusionScaled.kt @@ -1,5 +1,6 @@ package org.openrndr.extra.meshgenerators +import org.openrndr.extra.shapes.frames.frames import org.openrndr.math.Matrix44 import org.openrndr.math.Vector3 import org.openrndr.shape.Path3D diff --git a/orx-mesh-generators/src/commonMain/kotlin/Frames.kt b/orx-shapes/src/commonMain/kotlin/frames/Frames.kt similarity index 57% rename from orx-mesh-generators/src/commonMain/kotlin/Frames.kt rename to orx-shapes/src/commonMain/kotlin/frames/Frames.kt index 7e8bf821..f3d88c55 100644 --- a/orx-mesh-generators/src/commonMain/kotlin/Frames.kt +++ b/orx-shapes/src/commonMain/kotlin/frames/Frames.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.meshgenerators +package org.openrndr.extra.shapes.frames import org.openrndr.math.Matrix44 import org.openrndr.math.Vector3 @@ -9,45 +9,51 @@ import org.openrndr.math.transforms.buildTransform * Calculate frames (pose matrices) using parallel transport * @param up0 initial up vector, should not be collinear with `this[1] - this[0]` */ + fun List.frames(up0: Vector3): List { + return frames(this, up0 = up0) +} + +fun frames(positions: List, directions: List = emptyList(), up0: Vector3): List { + + require(up0.squaredLength > 0.0) { + "up0 ($up0) has 0 or NaN length" + } + val result = mutableListOf() - if (this.isEmpty()) { + if (positions.isEmpty()) { return emptyList() } - if (this.size == 1) { + if (positions.size == 1) { return listOf(Matrix44.IDENTITY) } var up = up0.normalized run { - val current = this[0] - val next = this[1] - val forward = (next - current).normalized + val current = positions[0] + val next = positions[1] + val forward = (directions.getOrNull(0) ?: (next - current)).normalized val right = (forward cross up).normalized up = ((right cross forward)).normalized result.add(Matrix44.fromColumnVectors(right.xyz0, up.xyz0, forward.xyz0, current.xyz1)) } - require(up.length > 0.0) { "initial `up.length` is zero in .frames()" } - - for (i in 1 until size - 1) { - val prev = this[i - 1] - val current = this[i] - val next = this[i + 1] + for (i in 1 until positions.size - 1) { + val prev = positions[i - 1] + val current = positions[i] + val next = positions[i + 1] val f1 = (next - current).normalized val f0 = (current - prev).normalized - val forward = (f0 + f1).normalized - require(forward.length > 0.0) { "`forward.length` is zero in .frames()" } + val forward = (directions.getOrNull(i) ?: (f0 + f1)).normalized + require(forward.length > 0.0) { "`forward.length` is zero or NaN in .frames()" } val right = (forward cross up).normalized up = ((right cross forward)).normalized - require(up.length > 0.0) { "`up.length` is zero in .frames()" } - require(right.length > 0.0) { "`right.length` is zero in .frames()" } - - //val m = Matrix44.fromColumnVectors(right.xyz0, up.xyz0, forward.xyz0, current.xyz1) + require(up.length > 0.0) { "`up.length` is zero or NaN in .frames()" } + require(right.length > 0.0) { "`right.length` is zero or NaN in .frames()" } val m = buildTransform { translate(current) diff --git a/orx-shapes/src/commonMain/kotlin/frames/Path3DExtensions.kt b/orx-shapes/src/commonMain/kotlin/frames/Path3DExtensions.kt new file mode 100644 index 00000000..cf60b6b7 --- /dev/null +++ b/orx-shapes/src/commonMain/kotlin/frames/Path3DExtensions.kt @@ -0,0 +1,20 @@ +package org.openrndr.extra.shapes.frames + +import org.openrndr.extra.shapes.rectify.RectifiedPath3D +import org.openrndr.math.Matrix44 +import org.openrndr.math.Vector3 +import org.openrndr.shape.Path3D + +fun Path3D.frames(ascendingTs: List, up0: Vector3, analyticalDirections: Boolean) : List { + val positions = ascendingTs.map { this.position(it) } + val directions = if (analyticalDirections) ascendingTs.map { this.direction(it) } else emptyList() + + return frames(positions, directions, up0) +} + +fun RectifiedPath3D.frames(ascendingTs: List, up0: Vector3, analyticalDirections: Boolean = true) : List { + val positions = ascendingTs.map { this.position(it) } + val directions = if (analyticalDirections) ascendingTs.map { this.direction(it) } else emptyList() + + return frames(positions, directions, up0) +} \ No newline at end of file diff --git a/orx-shapes/src/commonMain/kotlin/rectify/RectifiedPath.kt b/orx-shapes/src/commonMain/kotlin/rectify/RectifiedPath.kt index 9bdbf897..8a2cd47d 100644 --- a/orx-shapes/src/commonMain/kotlin/rectify/RectifiedPath.kt +++ b/orx-shapes/src/commonMain/kotlin/rectify/RectifiedPath.kt @@ -92,6 +92,14 @@ abstract class RectifiedPath>( } } + fun direction(t: Double): T { + return if (path.empty) { + path.infinity + } else { + path.direction(rectify(safe(t))) + } + } + abstract fun sub(t0: Double, t1: Double): Path /** diff --git a/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContourContinue01.kt b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContourContinue01.kt index 7e66def7..2dbfff60 100644 --- a/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContourContinue01.kt +++ b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContourContinue01.kt @@ -9,7 +9,7 @@ import org.openrndr.extra.shapes.tunni.tunniPoint import org.openrndr.extra.shapes.tunni.withTunniLine import org.openrndr.launch import org.openrndr.math.Vector2 -import org.openrndr.shape.Segment +import org.openrndr.shape.Segment2D import kotlin.math.cos fun main() { @@ -21,7 +21,7 @@ fun main() { program { var res = drawer.bounds.offsetEdges(-200.0).contour - var selectedSegments = emptyList() + var selectedSegments = emptyList() var selectedPoints = emptyList() val contourSeq = adjustContourSequence(res) { diff --git a/orx-shapes/src/jvmDemo/kotlin/arrangement/DemoArrangement04.kt b/orx-shapes/src/jvmDemo/kotlin/arrangement/DemoArrangement04.kt new file mode 100644 index 00000000..0a44be4b --- /dev/null +++ b/orx-shapes/src/jvmDemo/kotlin/arrangement/DemoArrangement04.kt @@ -0,0 +1,44 @@ +package arrangement + +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.color.rgb +import org.openrndr.extra.color.spaces.OKHSV +import org.openrndr.extra.color.tools.saturate +import org.openrndr.extra.noise.uniform +import org.openrndr.extra.shapes.arrangement.Arrangement +import org.openrndr.math.Vector2 +import org.openrndr.shape.Circle +import kotlin.math.sqrt +import kotlin.random.Random + +fun main() { + application { + program { + val circles = listOf( + Circle(drawer.bounds.center -Vector2(50.0, 0.0), 50.0), + Circle(drawer.bounds.center + Vector2(50.0, 0.0), 50.0), + Circle(drawer.bounds.center + Vector2(0.0, 50.0), 50.0), + Circle(drawer.bounds.center - Vector2(0.0, 50.0), 50.0), + Circle(drawer.bounds.center -Vector2(50.0, 0.0), sqrt(50.0*50.0+50.0*50.0)-49.9), + Circle(drawer.bounds.center +Vector2(50.0, 0.0), sqrt(50.0*50.0+50.0*50.0)-49.9), + Circle(drawer.bounds.center -Vector2(0.0, 50.0), sqrt(50.0*50.0+50.0*50.0)-49.9), + Circle(drawer.bounds.center +Vector2(0.0, 50.0), sqrt(50.0*50.0+50.0*50.0)-49.9), + ).shuffled() + + val arr = Arrangement(circles) + + extend { + val r = Random(100) + drawer.stroke = ColorRGBa.WHITE + for (f in arr.boundedFaces) { + drawer.fill = + + rgb(Double.uniform(0.0, 1.0, r), Double.uniform(0.0, 1.0, r), Double.uniform(0.0, 1.0, r)).saturate(0.25) + drawer.contour(f.contour) + } + } + + } + } +} \ No newline at end of file diff --git a/orx-shapes/src/jvmDemo/kotlin/frames/DemoFrames01.kt b/orx-shapes/src/jvmDemo/kotlin/frames/DemoFrames01.kt new file mode 100644 index 00000000..6f832f43 --- /dev/null +++ b/orx-shapes/src/jvmDemo/kotlin/frames/DemoFrames01.kt @@ -0,0 +1,65 @@ +package frames + +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.DrawPrimitive +import org.openrndr.draw.isolated +import org.openrndr.draw.shadeStyle +import org.openrndr.extra.camera.Orbital +import org.openrndr.extra.meshgenerators.cylinderMesh +import org.openrndr.extra.noise.uniformRing +import org.openrndr.extra.shapes.frames.frames +import org.openrndr.extra.shapes.rectify.rectified +import org.openrndr.math.Vector3 +import org.openrndr.shape.path3D +import kotlin.random.Random + +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + val random = Random(0) + val cylinder = cylinderMesh(radius = 0.5, length = 0.1) + val p = path3D { + moveTo(0.0, 0.0, 0.0) + curveTo( + Vector3.uniformRing(0.1, 1.0, random = random)*10.0, + Vector3.uniformRing(0.1, 1.0, random = random)*10.0, + Vector3.uniformRing(0.1, 1.0, random = random)*10.0 + ) + for (i in 0 until 10) { + continueTo( + Vector3.uniformRing(0.1, 1.0, random = random)*10.0, + Vector3.uniformRing(0.1, 1.0, random = random)*10.0 + ) + } + } + val pr = p.rectified(0.01, 100.0) + + + val frames = pr.frames((0 until 100).map { it / 100.0}, Vector3.UNIT_Y) + extend(Orbital()) + extend { + drawer.shadeStyle = shadeStyle { + fragmentTransform = """ + x_fill.rgb = vec3(abs(v_viewNormal.z)*0.9+ 0.1); + """.trimIndent() + } + + drawer.stroke = ColorRGBa.PINK + drawer.path(p) + + for (frame in frames) { + drawer.isolated { + drawer.model = frame + drawer.vertexBuffer(cylinder, DrawPrimitive.TRIANGLES) + } + } + + } + } + } +} \ No newline at end of file