实现等高线的绘制
This commit is contained in:
@@ -30,6 +30,7 @@ kotlin {
|
||||
implementation(project(":orx-marching-squares"))
|
||||
implementation(project(":orx-text-writer"))
|
||||
implementation(project(":orx-obj-loader"))
|
||||
implementation(project(":orx-palette"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
desktop/src/jvmDemo/kotlin/DemoColorBrewer2.kt
Normal file
35
desktop/src/jvmDemo/kotlin/DemoColorBrewer2.kt
Normal file
@@ -0,0 +1,35 @@
|
||||
import org.openrndr.application
|
||||
import org.openrndr.shape.Rectangle
|
||||
|
||||
/**
|
||||
* Demonstrates how to use a ColorBrewer2 palette.
|
||||
* Finds the first available palette with 5 colors,
|
||||
* then draws concentric circles filled with those colors.
|
||||
*/
|
||||
fun main() = application {
|
||||
configure {
|
||||
width = 720
|
||||
height = 720
|
||||
}
|
||||
program {
|
||||
val palette = colorBrewer2Palettes(
|
||||
numberOfColors = 6,
|
||||
paletteType = ColorBrewer2Type.Any
|
||||
).first().colors
|
||||
val cellSize = 50.0
|
||||
extend {
|
||||
palette.forEachIndexed { i, color ->
|
||||
drawer.fill = color
|
||||
drawer.rectangle(
|
||||
Rectangle(
|
||||
x = 0.0,
|
||||
y = cellSize * i,
|
||||
width = cellSize,
|
||||
height = cellSize
|
||||
)
|
||||
)
|
||||
// drawer.circle(drawer.bounds.center, 300.0 - i * 40.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import org.openrndr.draw.loadFont
|
||||
import org.openrndr.extra.camera.Camera2D
|
||||
import org.openrndr.extra.marchingsquares.findContours
|
||||
import org.openrndr.extra.noise.gradientPerturbFractal
|
||||
import org.openrndr.extra.noise.simplex
|
||||
import org.openrndr.extra.textwriter.writer
|
||||
import org.openrndr.extra.triangulation.DelaunayTriangulation
|
||||
import org.openrndr.math.Vector2
|
||||
@@ -18,7 +17,6 @@ import org.openrndr.math.Vector3
|
||||
import org.openrndr.shape.Segment2D
|
||||
import org.openrndr.shape.Segment3D
|
||||
import org.openrndr.shape.ShapeContour
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
@@ -30,7 +28,7 @@ import kotlin.random.Random
|
||||
fun main() = application {
|
||||
configure {
|
||||
width = 720
|
||||
height = 720
|
||||
height = 480
|
||||
title = "Delaunator"
|
||||
}
|
||||
program {
|
||||
@@ -62,6 +60,10 @@ fun main() = application {
|
||||
extend(Camera2D())
|
||||
println("draw")
|
||||
var targetHeight: Double = zs.average()
|
||||
val step = zs.max() - zs.min() / 6
|
||||
var heightList = (0..5).map { index ->
|
||||
zs.min() + step * index
|
||||
}
|
||||
var logEnabled = true
|
||||
var useInterpolation = false
|
||||
var sampleLinear = false
|
||||
@@ -171,12 +173,41 @@ fun main() = application {
|
||||
cellSize = 4.0,
|
||||
useInterpolation = useInterpolation
|
||||
)
|
||||
val associateWith: List<List<ShapeContour>> = heightList.map { height ->
|
||||
findContours(
|
||||
f = {
|
||||
val triangle = triangles.firstOrNull { triangle ->
|
||||
isPointInTriangle(it, listOf(triangle.x1, triangle.x2, triangle.x3))
|
||||
}
|
||||
triangle ?: return@findContours 0.0
|
||||
val interpolate = interpolateHeight(
|
||||
point = it,
|
||||
triangle = listOf(
|
||||
triangle.x1,
|
||||
triangle.x2,
|
||||
triangle.x3,
|
||||
).map {
|
||||
Vector3(it.x, it.y, associate[it]!!)
|
||||
}
|
||||
)
|
||||
interpolate.z - height
|
||||
},
|
||||
area = drawer.bounds,
|
||||
cellSize = 4.0,
|
||||
useInterpolation = useInterpolation
|
||||
)
|
||||
}
|
||||
if (logEnabled) println("useInterpolation = $useInterpolation")
|
||||
drawer.stroke = null
|
||||
contours.forEach {
|
||||
if (true) contours.forEach {
|
||||
drawer.fill = ColorRGBa.GREEN.opacify(0.1)
|
||||
drawer.contour(if (sampleLinear) it.sampleLinear() else it)
|
||||
|
||||
}
|
||||
if (false) associateWith.forEachIndexed { index, contours ->
|
||||
contours.forEach {
|
||||
drawer.fill = colorBrewer2[index].colors.first().opacify(0.1)
|
||||
drawer.contour(it)
|
||||
}
|
||||
}
|
||||
|
||||
drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 24.0)
|
||||
@@ -210,6 +241,24 @@ fun isPointInTriangle(point: Vector2, triangle: List<Vector2>): Boolean {
|
||||
alpha <= 1 && beta <= 1 && gamma <= 1
|
||||
}
|
||||
|
||||
fun isPointInTriangle3D(point: Vector2, triangle: List<Vector3>): Boolean {
|
||||
require(triangle.size == 3) { "三角形必须有3个顶点" }
|
||||
|
||||
val (v1, v2, v3) = triangle
|
||||
|
||||
// 计算重心坐标
|
||||
val denominator = (v2.y - v3.y) * (v1.x - v3.x) + (v3.x - v2.x) * (v1.y - v3.y)
|
||||
if (denominator == 0.0) return false // 退化三角形
|
||||
|
||||
val alpha = ((v2.y - v3.y) * (point.x - v3.x) + (v3.x - v2.x) * (point.y - v3.y)) / denominator
|
||||
val beta = ((v3.y - v1.y) * (point.x - v3.x) + (v1.x - v3.x) * (point.y - v3.y)) / denominator
|
||||
val gamma = 1.0 - alpha - beta
|
||||
|
||||
// 点在三角形内当且仅当所有重心坐标都在[0,1]范围内
|
||||
return alpha >= 0 && beta >= 0 && gamma >= 0 &&
|
||||
alpha <= 1 && beta <= 1 && gamma <= 1
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用重心坐标计算点在三角形上的高度
|
||||
* @param point 二维点 (x, y)
|
||||
|
||||
@@ -9,15 +9,14 @@ import org.openrndr.draw.loadFont
|
||||
import org.openrndr.draw.shadeStyle
|
||||
import org.openrndr.extra.camera.Orbital
|
||||
import org.openrndr.extra.marchingsquares.findContours
|
||||
import org.openrndr.extra.noise.gradientPerturbFractal
|
||||
import org.openrndr.extra.objloader.loadOBJasVertexBuffer
|
||||
import org.openrndr.extra.textwriter.writer
|
||||
import org.openrndr.extra.triangulation.DelaunayTriangulation
|
||||
import org.openrndr.extra.triangulation.DelaunayTriangulation3D
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.Vector3
|
||||
import org.openrndr.shape.Path3D
|
||||
import org.openrndr.shape.Segment3D
|
||||
import org.openrndr.shape.ShapeContour
|
||||
|
||||
/**
|
||||
* @author tabidachinokaze
|
||||
@@ -48,15 +47,16 @@ fun main() = application {
|
||||
height = height,
|
||||
volcanoCount = 3
|
||||
)*/
|
||||
val points3D = coordinateGenerate(width, height).map {
|
||||
it.copy(x = it.x - width / 2, y = it.y - height / 2)
|
||||
}
|
||||
val points3D = coordinateGenerate(width, height).map {
|
||||
it.copy(x = it.x - width / 2, y = it.y - height / 2, z = it.z * 10)
|
||||
}
|
||||
val zs = points3D.map { it.z }
|
||||
println("zs = ${zs}")
|
||||
val associate: MutableMap<Vector2, Double> = points3D.associate {
|
||||
Vector2(it.x, it.y) to it.z
|
||||
}.toMutableMap()
|
||||
val delaunay = DelaunayTriangulation(associate.map { it.key })
|
||||
val delaunay3D = DelaunayTriangulation3D(points3D.map { Vector3(it.x, it.y, it.z) })
|
||||
|
||||
//println(points3D.niceStr())
|
||||
//extend(Camera2D())
|
||||
@@ -84,7 +84,7 @@ fun main() = application {
|
||||
val vb = loadOBJasVertexBuffer("orx-obj-loader/test-data/non-planar.obj")
|
||||
|
||||
extend {
|
||||
val triangles = delaunay.triangles()
|
||||
val triangles = delaunay3D.triangles()
|
||||
val segments = mutableListOf<Segment3D>()
|
||||
drawer.clear(ColorRGBa.BLACK)
|
||||
val indexDiff = (frameCount / 1000) % triangles.size
|
||||
@@ -95,10 +95,12 @@ fun main() = application {
|
||||
}
|
||||
|
||||
drawer.vertexBuffer(vb, DrawPrimitive.TRIANGLES)
|
||||
|
||||
// 绘制等高线段区域
|
||||
for ((i, triangle) in triangles.withIndex()) {
|
||||
val segment2DS = triangle.contour.segments.filter {
|
||||
val startZ = associate[it.start]!!
|
||||
val endZ = associate[it.end]!!
|
||||
val segment2DS = triangle.segments.filter {
|
||||
val startZ = it.start.z
|
||||
val endZ = it.end.z
|
||||
if (startZ < endZ) {
|
||||
targetHeight in startZ..endZ
|
||||
} else {
|
||||
@@ -108,8 +110,8 @@ fun main() = application {
|
||||
|
||||
if (segment2DS.size == 2) {
|
||||
val vector2s = segment2DS.map {
|
||||
val startZ = associate[it.start]!!
|
||||
val endZ = associate[it.end]!!
|
||||
val startZ = it.start.z
|
||||
val endZ = it.end.z
|
||||
val start = Vector3(it.start.x, it.start.y, startZ)
|
||||
val end = Vector3(it.end.x, it.end.y, endZ)
|
||||
if (startZ < endZ) {
|
||||
@@ -130,14 +132,8 @@ fun main() = application {
|
||||
}
|
||||
drawer.strokeWeight = 20.0
|
||||
drawer.stroke = ColorRGBa.PINK
|
||||
val segment3DS = triangle.contour.segments.map {
|
||||
val startZ = associate[it.start]!!
|
||||
val endZ = associate[it.end]!!
|
||||
Segment3D(it.start.vector3(z = startZ), it.end.vector3(z = endZ))
|
||||
}
|
||||
|
||||
//drawer.contour(triangle.contour)
|
||||
drawer.path(Path3D.fromSegments(segment3DS, closed = true))
|
||||
drawer.path(triangle.path)
|
||||
}
|
||||
|
||||
val sorted = connectAllSegments(segments)
|
||||
@@ -161,6 +157,7 @@ fun main() = application {
|
||||
drawer.fill = ColorRGBa.YELLOW
|
||||
// if (false) drawer.contour(ShapeContour.fromSegments(it, closed = true))
|
||||
}
|
||||
// 结束绘制等高线
|
||||
/*for (y in 0 until (area.height / cellSize).toInt()) {
|
||||
for (x in 0 until (area.width / cellSize).toInt()) {
|
||||
values[IntVector2(x, y)] = f(Vector2(x * cellSize + area.x, y * cellSize + area.y))
|
||||
@@ -169,7 +166,7 @@ fun main() = application {
|
||||
val contours = findContours(
|
||||
f = {
|
||||
val triangle = triangles.firstOrNull { triangle ->
|
||||
isPointInTriangle(it, listOf(triangle.x1, triangle.x2, triangle.x3))
|
||||
isPointInTriangle3D(it, listOf(triangle.x1, triangle.x2, triangle.x3))
|
||||
}
|
||||
triangle ?: return@findContours 0.0
|
||||
val interpolate = interpolateHeight(
|
||||
@@ -178,9 +175,7 @@ fun main() = application {
|
||||
triangle.x1,
|
||||
triangle.x2,
|
||||
triangle.x3,
|
||||
).map {
|
||||
Vector3(it.x, it.y, associate[it]!!)
|
||||
}
|
||||
)
|
||||
)
|
||||
interpolate.z - targetHeight
|
||||
},
|
||||
@@ -191,6 +186,7 @@ fun main() = application {
|
||||
if (logEnabled) println("useInterpolation = $useInterpolation")
|
||||
drawer.stroke = null
|
||||
contours.map {
|
||||
if (false) drawer.contour(it)
|
||||
it.segments.map {
|
||||
Segment3D(
|
||||
it.start.vector3(),
|
||||
|
||||
Reference in New Issue
Block a user