[orx-shapes, orx-mesh-generator] Move frames code from orx-mesh-generator to orx-shapes

This commit is contained in:
Edwin Jakobs
2024-03-19 17:54:03 +01:00
parent 76f96d2278
commit c48aa83ced
10 changed files with 167 additions and 23 deletions

View File

@@ -0,0 +1,66 @@
package org.openrndr.extra.shapes.frames
import org.openrndr.math.Matrix44
import org.openrndr.math.Vector3
import org.openrndr.math.Vector4
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<Vector3>.frames(up0: Vector3): List<Matrix44> {
return frames(this, up0 = up0)
}
fun frames(positions: List<Vector3>, directions: List<Vector3> = emptyList(), up0: Vector3): List<Matrix44> {
require(up0.squaredLength > 0.0) {
"up0 ($up0) has 0 or NaN length"
}
val result = mutableListOf<Matrix44>()
if (positions.isEmpty()) {
return emptyList()
}
if (positions.size == 1) {
return listOf(Matrix44.IDENTITY)
}
var up = up0.normalized
run {
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))
}
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 = (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 or NaN in .frames()" }
require(right.length > 0.0) { "`right.length` is zero or NaN in .frames()" }
val m = buildTransform {
translate(current)
multiply(Matrix44.fromColumnVectors(right.xyz0, up.xyz0, forward.xyz0, Vector4.UNIT_W))
}
result.add(m)
}
return result
}

View File

@@ -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<Double>, up0: Vector3, analyticalDirections: Boolean) : List<Matrix44> {
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<Double>, up0: Vector3, analyticalDirections: Boolean = true) : List<Matrix44> {
val positions = ascendingTs.map { this.position(it) }
val directions = if (analyticalDirections) ascendingTs.map { this.direction(it) } else emptyList()
return frames(positions, directions, up0)
}

View File

@@ -92,6 +92,14 @@ abstract class RectifiedPath<T : EuclideanVector<T>>(
}
}
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<T>
/**

View File

@@ -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<Segment>()
var selectedSegments = emptyList<Segment2D>()
var selectedPoints = emptyList<Vector2>()
val contourSeq = adjustContourSequence(res) {

View File

@@ -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<OKHSV>(0.25)
drawer.contour(f.contour)
}
}
}
}
}

View File

@@ -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)
}
}
}
}
}
}