-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[orx-shapes] Add Hilbert and Morton point ordering in 2d and 3d
- Loading branch information
Showing
9 changed files
with
548 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package org.openrndr.extra.shapes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package org.openrndr.extra.shapes.ordering | ||
|
||
@OptIn(ExperimentalUnsignedTypes::class) | ||
fun hilbert2dDecode16Bit(hilbert: UInt): UIntArray { | ||
val morton = hilbertToMorton2d(hilbert, 10) | ||
return morton2dDecode16Bit(morton) | ||
} | ||
|
||
@OptIn(ExperimentalUnsignedTypes::class) | ||
fun hilbert2dEncode16Bit(index1: UInt, index2: UInt): UInt { | ||
val morton = morton2dEncode16Bit(index1, index2) | ||
return mortonToHilbert2d(morton, 16) | ||
} | ||
|
||
|
||
@OptIn(ExperimentalUnsignedTypes::class) | ||
fun hilbert2dDecode5Bit(hilbert: UInt): UIntArray { | ||
val morton = hilbertToMorton2d(hilbert, 5) | ||
return morton2dDecode5Bit(morton) | ||
} | ||
|
||
@OptIn(ExperimentalUnsignedTypes::class) | ||
fun hilbert2dEncode5Bit(index1: UInt, index2: UInt): UInt { | ||
val morton = morton2dEncode5Bit(index1, index2) | ||
return mortonToHilbert3d(morton, 5) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package org.openrndr.extra.shapes.ordering | ||
|
||
@OptIn(ExperimentalUnsignedTypes::class) | ||
fun hilbert3dDecode10Bit(hilbert: UInt): UIntArray { | ||
val morton = hilbertToMorton3d(hilbert, 10) | ||
return morton3dDecode10Bit(morton) | ||
} | ||
|
||
@OptIn(ExperimentalUnsignedTypes::class) | ||
fun hilbert3dEncode10Bit(index1: UInt, index2: UInt, index3: UInt): UInt { | ||
val morton = morton3dEncode10Bit(index1, index2, index3) | ||
return mortonToHilbert3d(morton, 10) | ||
} | ||
|
||
|
||
@OptIn(ExperimentalUnsignedTypes::class) | ||
fun hilbert3dDecode5Bit(hilbert: UInt): UIntArray { | ||
val morton = hilbertToMorton3d(hilbert, 5) | ||
return morton3dDecode5Bit(morton) | ||
} | ||
|
||
@OptIn(ExperimentalUnsignedTypes::class) | ||
fun hilbert3dEncode5Bit(index1: UInt, index2: UInt, index3: UInt): UInt { | ||
val morton = morton3dEncode5Bit(index1, index2, index3) | ||
return mortonToHilbert3d(morton, 5) | ||
} |
75 changes: 75 additions & 0 deletions
75
orx-shapes/src/commonMain/kotlin/ordering/ListVector2Extensions.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package org.openrndr.extra.shapes.ordering | ||
|
||
import org.openrndr.math.Vector2 | ||
import org.openrndr.math.map | ||
import org.openrndr.shape.Rectangle | ||
import org.openrndr.shape.bounds | ||
import kotlin.math.max | ||
import kotlin.math.pow | ||
|
||
enum class Axis2DPermutation { | ||
XY, | ||
YX, | ||
} | ||
|
||
fun List<Vector2>.mortonOrder( | ||
scale: Double = 1.0, | ||
permutation: Axis2DPermutation = Axis2DPermutation.XY, | ||
bits: Int = 16, | ||
): List<Vector2> { | ||
val bounds = this.bounds | ||
val md = max(bounds.width, bounds.height) * scale | ||
val rbounds = Rectangle(bounds.corner.x, bounds.corner.y, md, md) | ||
val extend = 2.0.pow(bits.toDouble()) - 1.0 | ||
val inputPoints = map { | ||
it.map( | ||
rbounds.position(0.0, 0.0), | ||
rbounds.position(1.0, 1.0), | ||
Vector2(0.0, 0.0), | ||
Vector2(extend, extend) | ||
) | ||
} | ||
val mortonCodes = when (bits) { | ||
5 -> when (permutation) { | ||
Axis2DPermutation.XY -> inputPoints.map { morton2dEncode5Bit(it.x.toUInt(), it.y.toUInt()) } | ||
Axis2DPermutation.YX -> inputPoints.map { morton2dEncode5Bit(it.y.toUInt(), it.x.toUInt()) } | ||
} | ||
16 -> when (permutation) { | ||
Axis2DPermutation.XY -> inputPoints.map { morton2dEncode16Bit(it.x.toUInt(), it.y.toUInt()) } | ||
Axis2DPermutation.YX -> inputPoints.map { morton2dEncode16Bit(it.y.toUInt(), it.x.toUInt()) } | ||
} | ||
else -> error("Only 5 and 16 bit modes are supported.") | ||
} | ||
return (this zip mortonCodes).sortedBy { it.second }.map { it.first } | ||
} | ||
|
||
fun List<Vector2>.hilbertOrder( | ||
scale: Double = 1.0, | ||
permutation: Axis2DPermutation = Axis2DPermutation.XY, | ||
bits: Int = 16, | ||
): List<Vector2> { | ||
val bounds = this.bounds | ||
val md = max(bounds.width, bounds.height) * scale | ||
val rbounds = Rectangle(bounds.corner.x, bounds.corner.y, md, md) | ||
val extend = 2.0.pow(bits.toDouble()) - 1.0 | ||
val inputPoints = map { | ||
it.map( | ||
rbounds.position(0.0, 0.0), | ||
rbounds.position(1.0, 1.0), | ||
Vector2(0.0, 0.0), | ||
Vector2(extend, extend) | ||
) | ||
} | ||
val hilbertCodes = when (bits) { | ||
5 -> when (permutation) { | ||
Axis2DPermutation.XY -> inputPoints.map { hilbert2dEncode5Bit(it.x.toUInt(), it.y.toUInt()) } | ||
Axis2DPermutation.YX -> inputPoints.map { hilbert2dEncode5Bit(it.y.toUInt(), it.x.toUInt()) } | ||
} | ||
16 -> when (permutation) { | ||
Axis2DPermutation.XY -> inputPoints.map { hilbert2dEncode16Bit(it.x.toUInt(), it.y.toUInt()) } | ||
Axis2DPermutation.YX -> inputPoints.map { hilbert2dEncode16Bit(it.y.toUInt(), it.x.toUInt()) } | ||
} | ||
else -> error("Only 5 and 16 bit modes are supported.") | ||
} | ||
return (this zip hilbertCodes).sortedBy { it.second }.map { it.first } | ||
} |
92 changes: 92 additions & 0 deletions
92
orx-shapes/src/commonMain/kotlin/ordering/ListVector3Extensions.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package org.openrndr.extra.shapes.ordering | ||
|
||
import org.openrndr.math.Vector3 | ||
import org.openrndr.math.map | ||
import org.openrndr.shape.Box | ||
import org.openrndr.shape.bounds | ||
import kotlin.math.max | ||
|
||
enum class Axis3DPermutation { | ||
XYZ, | ||
XZY, | ||
YXZ, | ||
YZX, | ||
ZXY, | ||
ZYX | ||
} | ||
|
||
fun List<Vector3>.mortonOrder( | ||
scale: Double = 1.0, | ||
permutation: Axis3DPermutation = Axis3DPermutation.XYZ, | ||
bits: Int = 10, | ||
): List<Vector3> { | ||
val bounds = this.bounds | ||
val md = max(max(bounds.width, bounds.height), bounds.depth) * scale | ||
val rbounds = Box(bounds.corner.x, bounds.corner.y, bounds.corner.z, md, md, md) | ||
val inputPoints = map { | ||
it.map( | ||
rbounds.position(0.0, 0.0, 0.0), | ||
rbounds.position(1.0, 1.0, 1.0), | ||
Vector3(0.0, 0.0, 0.0), | ||
Vector3(1023.0, 1023.0, 1023.0) | ||
) | ||
} | ||
val mortonCodes = when (bits) { | ||
5 -> when (permutation) { | ||
Axis3DPermutation.XYZ -> inputPoints.map { morton3dEncode5Bit(it.x.toUInt(), it.y.toUInt(), it.z.toUInt()) } | ||
Axis3DPermutation.XZY -> inputPoints.map { morton3dEncode5Bit(it.x.toUInt(), it.z.toUInt(), it.y.toUInt()) } | ||
Axis3DPermutation.YXZ -> inputPoints.map { morton3dEncode5Bit(it.y.toUInt(), it.x.toUInt(), it.z.toUInt()) } | ||
Axis3DPermutation.YZX -> inputPoints.map { morton3dEncode5Bit(it.y.toUInt(), it.z.toUInt(), it.x.toUInt()) } | ||
Axis3DPermutation.ZXY -> inputPoints.map { morton3dEncode5Bit(it.z.toUInt(), it.x.toUInt(), it.y.toUInt()) } | ||
Axis3DPermutation.ZYX -> inputPoints.map { morton3dEncode5Bit(it.z.toUInt(), it.y.toUInt(), it.x.toUInt()) } | ||
} | ||
10 -> when (permutation) { | ||
Axis3DPermutation.XYZ -> inputPoints.map { morton3dEncode10Bit(it.x.toUInt(), it.y.toUInt(), it.z.toUInt()) } | ||
Axis3DPermutation.XZY -> inputPoints.map { morton3dEncode10Bit(it.x.toUInt(), it.z.toUInt(), it.y.toUInt()) } | ||
Axis3DPermutation.YXZ -> inputPoints.map { morton3dEncode10Bit(it.y.toUInt(), it.x.toUInt(), it.z.toUInt()) } | ||
Axis3DPermutation.YZX -> inputPoints.map { morton3dEncode10Bit(it.y.toUInt(), it.z.toUInt(), it.x.toUInt()) } | ||
Axis3DPermutation.ZXY -> inputPoints.map { morton3dEncode10Bit(it.z.toUInt(), it.x.toUInt(), it.y.toUInt()) } | ||
Axis3DPermutation.ZYX -> inputPoints.map { morton3dEncode10Bit(it.z.toUInt(), it.y.toUInt(), it.x.toUInt()) } | ||
} | ||
else -> error("Only 5 and 10 bit modes are supported.") | ||
} | ||
return (this zip mortonCodes).sortedBy { it.second }.map { it.first } | ||
} | ||
|
||
fun List<Vector3>.hilbertOrder( | ||
scale: Double = 1.0, | ||
permutation: Axis3DPermutation = Axis3DPermutation.XYZ, | ||
bits: Int | ||
): List<Vector3> { | ||
val bounds = this.bounds | ||
val md = max(max(bounds.width, bounds.height), bounds.depth) * scale | ||
val rbounds = Box(bounds.corner.x, bounds.corner.y, bounds.corner.z, md, md, md) | ||
val inputPoints = map { | ||
it.map( | ||
rbounds.position(0.0, 0.0, 0.0), | ||
rbounds.position(1.0, 1.0, 1.0), | ||
Vector3(0.0, 0.0, 0.0), | ||
Vector3(1023.0, 1023.0, 1023.0) | ||
) | ||
} | ||
val hilbertCodes = when(bits) { | ||
5 -> when (permutation) { | ||
Axis3DPermutation.XYZ -> inputPoints.map { hilbert3dEncode5Bit(it.x.toUInt(), it.y.toUInt(), it.z.toUInt()) } | ||
Axis3DPermutation.XZY -> inputPoints.map { hilbert3dEncode5Bit(it.x.toUInt(), it.z.toUInt(), it.y.toUInt()) } | ||
Axis3DPermutation.YXZ -> inputPoints.map { hilbert3dEncode5Bit(it.y.toUInt(), it.x.toUInt(), it.z.toUInt()) } | ||
Axis3DPermutation.YZX -> inputPoints.map { hilbert3dEncode5Bit(it.y.toUInt(), it.z.toUInt(), it.x.toUInt()) } | ||
Axis3DPermutation.ZXY -> inputPoints.map { hilbert3dEncode5Bit(it.z.toUInt(), it.x.toUInt(), it.y.toUInt()) } | ||
Axis3DPermutation.ZYX -> inputPoints.map { hilbert3dEncode5Bit(it.z.toUInt(), it.y.toUInt(), it.x.toUInt()) } | ||
} | ||
10 -> when (permutation) { | ||
Axis3DPermutation.XYZ -> inputPoints.map { hilbert3dEncode10Bit(it.x.toUInt(), it.y.toUInt(), it.z.toUInt()) } | ||
Axis3DPermutation.XZY -> inputPoints.map { hilbert3dEncode10Bit(it.x.toUInt(), it.z.toUInt(), it.y.toUInt()) } | ||
Axis3DPermutation.YXZ -> inputPoints.map { hilbert3dEncode10Bit(it.y.toUInt(), it.x.toUInt(), it.z.toUInt()) } | ||
Axis3DPermutation.YZX -> inputPoints.map { hilbert3dEncode10Bit(it.y.toUInt(), it.z.toUInt(), it.x.toUInt()) } | ||
Axis3DPermutation.ZXY -> inputPoints.map { hilbert3dEncode10Bit(it.z.toUInt(), it.x.toUInt(), it.y.toUInt()) } | ||
Axis3DPermutation.ZYX -> inputPoints.map { hilbert3dEncode10Bit(it.z.toUInt(), it.y.toUInt(), it.x.toUInt()) } | ||
} | ||
else -> error("Only 5 and 10 bit modes are supported.") | ||
} | ||
return (this zip hilbertCodes).sortedBy { it.second }.map { it.first } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
@file:OptIn(ExperimentalUnsignedTypes::class) | ||
|
||
package org.openrndr.extra.shapes.ordering | ||
|
||
fun morton2dEncode5Bit(index1: UInt, index2: UInt): UInt { | ||
// pack 2 5-bit indices into a 10-bit Morton code | ||
var index1: UInt = index1 | ||
var index2: UInt = index2 | ||
index1 = index1 and 0x0000001fu | ||
index2 = index2 and 0x0000001fu | ||
index1 *= 0x01041041u | ||
index2 *= 0x01041041u | ||
index1 = index1 and 0x10204081u | ||
index2 = index2 and 0x10204081u | ||
index1 *= 0x00108421u | ||
index2 *= 0x00108421u | ||
index1 = index1 and 0x15500000u | ||
index2 = index2 and 0x15500000u | ||
return ((index1 shr 20) or (index2 shr 19)) | ||
} | ||
|
||
fun morton2dDecode5Bit(morton: UInt): UIntArray { // unpack 2 5-bit indices from a 10-bit Morton code | ||
var value1 = morton; | ||
var value2 = (value1 shr 1) | ||
value1 = value1 and 0x00000155u | ||
value2 = value2 and 0x00000155u | ||
value1 = value1 or (value1 shr 1) | ||
value2 = value2 or (value2 shr 1) | ||
value1 = value1 and 0x00000133u | ||
value2 = value2 and 0x00000133u | ||
value1 = value1 or (value1 shr 2) | ||
value2 = value2 or (value2 shr 2) | ||
value1 = value1 and 0x0000010fu | ||
value2 = value2 and 0x0000010fu | ||
value1 = value1 or (value1 shr 4) | ||
value2 = value2 or (value2 shr 4) | ||
value1 = value1 and 0x0000001fu | ||
value2 = value2 and 0x0000001fu | ||
return uintArrayOf(value1, value2) | ||
} | ||
|
||
fun morton2dEncode16Bit(index1: UInt, index2: UInt): UInt { // pack 2 16-bit indices into a 32-bit Morton code | ||
var index1: UInt = index1 | ||
var index2: UInt = index2 | ||
index1 = index1 and 0x0000ffffu | ||
index2 = index2 and 0x0000ffffu | ||
index1 = index1 or (index1 shl 8) | ||
index2 = index2 or (index2 shl 8) | ||
index1 = index1 and 0x00ff00ffu | ||
index2 = index2 and 0x00ff00ffu | ||
index1 = index1 or (index1 shl 4) | ||
index2 = index2 or (index2 shl 4) | ||
index1 = index1 and 0x0f0f0f0fu | ||
index2 = index2 and 0x0f0f0f0fu | ||
index1 = index1 or (index1 shl 2) | ||
index2 = index2 or (index2 shl 2) | ||
index1 = index1 and 0x33333333u | ||
index2 = index2 and 0x33333333u | ||
index1 = index1 or (index1 shl 1) | ||
index2 = index2 or (index2 shl 1) | ||
index1 = index1 and 0x55555555u | ||
index2 = index2 and 0x55555555u | ||
return (index1 or (index2 shl 1)) | ||
} | ||
|
||
fun morton2dDecode16Bit(morton: UInt): UIntArray { // unpack 2 16-bit indices from a 32-bit Morton code | ||
var value1 = morton; | ||
var value2 = (value1 shr 1); | ||
value1 = value1 and 0x55555555u | ||
value2 = value2 and 0x55555555u | ||
value1 = value1 or (value1 shr 1) | ||
value2 = value2 or (value2 shr 1) | ||
value1 = value1 and 0x33333333u | ||
value2 = value2 and 0x33333333u | ||
value1 = value1 or (value1 shr 2) | ||
value2 = value2 or (value2 shr 2) | ||
value1 = value1 and 0x0f0f0f0fu | ||
value2 = value2 and 0x0f0f0f0fu | ||
value1 = value1 or (value1 shr 4) | ||
value2 = value2 or (value2 shr 4) | ||
value1 = value1 and 0x00ff00ffu | ||
value2 = value2 and 0x00ff00ffu | ||
value1 = value1 or (value1 shr 8) | ||
value2 = value2 or (value2 shr 8) | ||
value1 = value1 and 0x0000ffffu | ||
value2 = value2 and 0x0000ffffu | ||
return uintArrayOf(value1, value2) | ||
} |
Oops, something went wrong.