Add 3D Bézier patch (#190)
This commit is contained in:
committed by
GitHub
parent
941d5020db
commit
a6d08467fa
274
orx-shapes/src/commonMain/kotlin/BezierPatch3D.kt
Normal file
274
orx-shapes/src/commonMain/kotlin/BezierPatch3D.kt
Normal file
@@ -0,0 +1,274 @@
|
||||
package org.openrndr.extra.shapes
|
||||
|
||||
import org.openrndr.color.AlgebraicColor
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.color.ConvertibleToColorRGBa
|
||||
import org.openrndr.math.Matrix44
|
||||
import org.openrndr.math.Vector3
|
||||
import org.openrndr.shape.Path3D
|
||||
import org.openrndr.shape.Segment3D
|
||||
import kotlin.random.Random
|
||||
|
||||
open class BezierPatch3DBase<C>(
|
||||
val points: List<List<Vector3>>,
|
||||
val colors: List<List<C>> = emptyList()
|
||||
)
|
||||
where C : AlgebraicColor<C>, C : ConvertibleToColorRGBa {
|
||||
init {
|
||||
require(points.size == 4 && points.all { it.size == 4 })
|
||||
require(colors.isEmpty() || colors.size == 4 && colors.all { it.size == 4 })
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a transposed version of the bezier path by transposing the [points] matrix
|
||||
*/
|
||||
val transposed
|
||||
get() = BezierPatch3DBase(
|
||||
listOf(
|
||||
listOf(points[0][0], points[1][0], points[2][0], points[3][0]),
|
||||
listOf(points[0][1], points[1][1], points[2][1], points[3][1]),
|
||||
listOf(points[0][2], points[1][2], points[2][2], points[3][2]),
|
||||
listOf(points[0][3], points[1][3], points[2][3], points[3][3]),
|
||||
),
|
||||
if (colors.isEmpty()) emptyList() else {
|
||||
listOf(
|
||||
listOf(colors[0][0], colors[1][0], colors[2][0], colors[3][0]),
|
||||
listOf(colors[0][1], colors[1][1], colors[2][1], colors[3][1]),
|
||||
listOf(colors[0][2], colors[1][2], colors[2][2], colors[3][2]),
|
||||
listOf(colors[0][3], colors[1][3], colors[2][3], colors[3][3]),
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
fun transform(transform: Matrix44) = BezierPatch3DBase(points.map { r ->
|
||||
r.map { (transform * it.xyz1).div }
|
||||
}, colors)
|
||||
|
||||
private fun coeffs2(t: Double): DoubleArray {
|
||||
val it = 1.0 - t
|
||||
val it2 = it * it
|
||||
val t2 = t * t
|
||||
return doubleArrayOf(it2, 2 * it * t, t2)
|
||||
}
|
||||
|
||||
private fun coeffs3(t: Double): DoubleArray {
|
||||
val it = 1.0 - t
|
||||
val it2 = it * it
|
||||
val it3 = it2 * it
|
||||
val t2 = t * t
|
||||
val t3 = t2 * t
|
||||
return doubleArrayOf(it3, 3 * it2 * t, 3 * it * t2, t3)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a point on the patch by using its u,v parameterization
|
||||
* @param u a value between 0 and 1
|
||||
* @param v a value between 0 and 1
|
||||
*/
|
||||
fun position(u: Double, v: Double): Vector3 {
|
||||
val csu = coeffs3(u)
|
||||
val csv = coeffs3(v)
|
||||
var result = Vector3.ZERO
|
||||
for (j in 0 until 4) {
|
||||
for (i in 0 until 4) {
|
||||
result += points[j][i] * csu[i] * csv[j]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a gradient vector on the patch by using its u,v parameterization
|
||||
* @param u a value between 0 and 1
|
||||
* @param v a value between 0 and 1
|
||||
*/
|
||||
fun gradient(u: Double, v: Double): Vector3 {
|
||||
val f0 = List(4) { MutableList(3) { Vector3.ZERO } }
|
||||
for (j in 0 until 4) {
|
||||
for (i in 0 until 3) {
|
||||
f0[j][i] = points[j][i + 1] - points[j][i]
|
||||
}
|
||||
}
|
||||
|
||||
val f1 = List(3) { MutableList(3) { Vector3.ZERO } }
|
||||
for (j in 0 until 3) {
|
||||
for (i in 0 until 3) {
|
||||
f1[j][i] = f0[j + 1][i] - f0[j][i]
|
||||
}
|
||||
}
|
||||
|
||||
val csu = coeffs2(u)
|
||||
val csv = coeffs2(v)
|
||||
var result = Vector3.ZERO
|
||||
for (j in 0 until 3) {
|
||||
for (i in 0 until 3) {
|
||||
result += f1[j][i] * csu[i] * csv[j]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random point on the path
|
||||
* @return a point that is uniformly distributed in uv space
|
||||
*/
|
||||
fun randomPoint(random: Random = Random.Default) = position(random.nextDouble(), random.nextDouble())
|
||||
|
||||
fun horizontal(v: Double): Path3D {
|
||||
val cs = coeffs3(v)
|
||||
val cps = Array(4) { Vector3.ZERO }
|
||||
for (j in 0 until 4) {
|
||||
for (i in 0 until 4) {
|
||||
cps[j] += points[i][j] * cs[i]
|
||||
}
|
||||
}
|
||||
return Path3D(listOf(Segment3D(cps[0], cps[1], cps[2], cps[3])), false)
|
||||
}
|
||||
|
||||
fun vertical(u: Double): Path3D {
|
||||
val cs = coeffs3(u)
|
||||
val cps = Array(4) { Vector3.ZERO }
|
||||
for (j in 0 until 4) {
|
||||
for (i in 0 until 4) {
|
||||
cps[j] += points[j][i] * cs[i]
|
||||
}
|
||||
}
|
||||
return Path3D(listOf(Segment3D(cps[0], cps[1], cps[2], cps[3])), false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a sub-patch based on uv parameterization
|
||||
*/
|
||||
fun sub(u0: Double, v0: Double, u1: Double, v1: Double): BezierPatch3DBase<C> {
|
||||
val c0 = Segment3D(points[0][0], points[0][1], points[0][2], points[0][3]).sub(u0, u1)
|
||||
val c1 = Segment3D(points[1][0], points[1][1], points[1][2], points[1][3]).sub(u0, u1)
|
||||
val c2 = Segment3D(points[2][0], points[2][1], points[2][2], points[2][3]).sub(u0, u1)
|
||||
val c3 = Segment3D(points[3][0], points[3][1], points[3][2], points[3][3]).sub(u0, u1)
|
||||
|
||||
val sub0 = bezierPatch(c0, c1, c2, c3)
|
||||
val d0 = Segment3D(sub0.points[0][0], sub0.points[1][0], sub0.points[2][0], sub0.points[3][0]).sub(v0, v1)
|
||||
val d1 = Segment3D(sub0.points[0][1], sub0.points[1][1], sub0.points[2][1], sub0.points[3][1]).sub(v0, v1)
|
||||
val d2 = Segment3D(sub0.points[0][2], sub0.points[1][2], sub0.points[2][2], sub0.points[3][2]).sub(v0, v1)
|
||||
val d3 = Segment3D(sub0.points[0][3], sub0.points[1][3], sub0.points[2][3], sub0.points[3][3]).sub(v0, v1)
|
||||
|
||||
return fromSegments<C>(d0, d1, d2, d3).transposed
|
||||
}
|
||||
|
||||
val path: Path3D = Path3D(
|
||||
listOf(
|
||||
Segment3D(points[0][0], points[0][1], points[0][2], points[0][3]),
|
||||
Segment3D(points[0][3], points[1][3], points[2][3], points[3][3]),
|
||||
Segment3D(points[3][3], points[3][2], points[3][1], points[3][0]),
|
||||
Segment3D(points[3][0], points[2][0], points[1][0], points[0][0]),
|
||||
), true
|
||||
)
|
||||
|
||||
operator fun times(scale: Double) =
|
||||
BezierPatch3DBase(
|
||||
points.map { j -> j.map { i -> i * scale } },
|
||||
if (colors.isEmpty()) colors else colors.map { j -> j.map { i -> i * scale } }
|
||||
)
|
||||
|
||||
operator fun div(scale: Double) =
|
||||
BezierPatch3DBase(points.map { j -> j.map { i -> i / scale } },
|
||||
if (colors.isEmpty()) colors else colors.map { j -> j.map { i -> i / scale } }
|
||||
)
|
||||
operator fun plus(right: BezierPatch3DBase<C>) =
|
||||
BezierPatch3DBase(List(4) { j -> List(4) { i -> points[j][i] + right.points[j][i] } },
|
||||
if (colors.isEmpty() && right.colors.isEmpty()) { colors }
|
||||
else if (colors.isEmpty() && right.colors.isNotEmpty()) { right.colors }
|
||||
else if (colors.isNotEmpty() && right.colors.isEmpty()) { colors }
|
||||
else { List(4) { j -> List(4) { i -> colors[j][i] + right.colors[j][i] } } }
|
||||
)
|
||||
|
||||
operator fun minus(right: BezierPatch3DBase<C>) =
|
||||
BezierPatch3DBase(List(4) { j -> List(4) { i -> points[j][i] - right.points[j][i] } },
|
||||
if (colors.isEmpty() && right.colors.isEmpty()) { colors }
|
||||
else if (colors.isEmpty() && right.colors.isNotEmpty()) { right.colors }
|
||||
else if (colors.isNotEmpty() && right.colors.isEmpty()) { colors }
|
||||
else { List(4) { j -> List(4) { i -> colors[j][i] - right.colors[j][i] } } }
|
||||
)
|
||||
|
||||
fun <K> withColors(colors: List<List<K>>): BezierPatch3DBase<K>
|
||||
where K : AlgebraicColor<K>, K : ConvertibleToColorRGBa {
|
||||
return BezierPatch3DBase(points, colors)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <C> fromSegments(c0: Segment3D, c1: Segment3D, c2: Segment3D, c3: Segment3D): BezierPatch3DBase<C>
|
||||
where C : AlgebraicColor<C>, C : ConvertibleToColorRGBa {
|
||||
val c0c = c0.cubic
|
||||
val c1c = c1.cubic
|
||||
val c2c = c2.cubic
|
||||
val c3c = c3.cubic
|
||||
|
||||
val c0l = listOf(c0c.start, c0c.control[0], c0c.control[1], c0c.end)
|
||||
val c1l = listOf(c1c.start, c1c.control[0], c1c.control[1], c1c.end)
|
||||
val c2l = listOf(c2c.start, c2c.control[0], c2c.control[1], c2c.end)
|
||||
val c3l = listOf(c3c.start, c3c.control[0], c3c.control[1], c3c.end)
|
||||
|
||||
return BezierPatch3DBase(listOf(c0l, c1l, c2l, c3l))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BezierPatch3D(points: List<List<Vector3>>, colors: List<List<ColorRGBa>> = emptyList()) :
|
||||
BezierPatch3DBase<ColorRGBa>(points, colors)
|
||||
|
||||
/**
|
||||
* Create a cubic bezier patch from 4 segments. The control points of the segments are used in row-wise fashion
|
||||
*/
|
||||
fun bezierPatch(c0: Segment3D, c1: Segment3D, c2: Segment3D, c3: Segment3D): BezierPatch3D {
|
||||
val c0c = c0.cubic
|
||||
val c1c = c1.cubic
|
||||
val c2c = c2.cubic
|
||||
val c3c = c3.cubic
|
||||
|
||||
val c0l = listOf(c0c.start, c0c.control[0], c0c.control[1], c0c.end)
|
||||
val c1l = listOf(c1c.start, c1c.control[0], c1c.control[1], c1c.end)
|
||||
val c2l = listOf(c2c.start, c2c.control[0], c2c.control[1], c2c.end)
|
||||
val c3l = listOf(c3c.start, c3c.control[0], c3c.control[1], c3c.end)
|
||||
|
||||
return BezierPatch3D(listOf(c0l, c1l, c2l, c3l))
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bezier patch from a closed shape contour (with 4 segments).
|
||||
* @param alpha control for linearity, default is `1.0/3.0`
|
||||
*/
|
||||
fun bezierPatch(path: Path3D, alpha: Double = 1.0 / 3.0): BezierPatch3D {
|
||||
require(path.segments.size == 4) {
|
||||
"""contour needs exactly 4 segments (has ${path.segments.size})"""
|
||||
}
|
||||
val c0 = path.segments[0].cubic
|
||||
val c1 = path.segments[1].cubic
|
||||
val c2 = path.segments[2].cubic
|
||||
val c3 = path.segments[3].cubic
|
||||
|
||||
val fa = 1.0 - alpha
|
||||
val fb = alpha
|
||||
|
||||
val x00 = (c0.control[0] * fa + c2.control[1] * fb + c3.control[1] * fa + c1.control[0] * fb) / 2.0
|
||||
val x01 = (c0.control[1] * fa + c2.control[0] * fb + c3.control[1] * fb + c1.control[0] * fa) / 2.0
|
||||
val x10 = (c0.control[0] * fb + c2.control[1] * fa + c3.control[0] * fa + c1.control[1] * fb) / 2.0
|
||||
val x11 = (c0.control[1] * fb + c2.control[0] * fa + c3.control[0] * fb + c1.control[1] * fa) / 2.0
|
||||
val cps = listOf(
|
||||
listOf(c0.start, c0.control[0], c0.control[1], c0.end),
|
||||
listOf(c3.control[1], x00, x01, c1.control[0]),
|
||||
listOf(c3.control[0], x10, x11, c1.control[1]),
|
||||
listOf(c2.end, c2.control[1], c2.control[0], c2.start),
|
||||
)
|
||||
return BezierPatch3D(cps)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bezier patch from 4 corners
|
||||
* @param corners a list of corners from which to create the patch
|
||||
* @param alpha control for linearity, default is `1.0/3.0`
|
||||
*/
|
||||
fun bezierPatch(corners: List<Vector3>, alpha: Double = 1.0 / 3.0): BezierPatch3D {
|
||||
require(corners.size == 4) {
|
||||
"""need exactly 4 corners (got ${corners.size}"""
|
||||
}
|
||||
return bezierPatch(Path3D.fromPoints(corners, true), alpha)
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package org.openrndr.extra.shapes.drawers
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.*
|
||||
import org.openrndr.extra.shapes.BezierPatchBase
|
||||
import org.openrndr.extra.shapes.BezierPatch3DBase
|
||||
import org.openrndr.internal.Driver
|
||||
import org.openrndr.math.Vector2
|
||||
|
||||
@@ -258,6 +259,97 @@ class BezierPatchDrawer {
|
||||
)
|
||||
shader.end()
|
||||
}
|
||||
|
||||
@JvmName("drawBezierPatch3D")
|
||||
fun drawBezierPatches(
|
||||
context: DrawContext,
|
||||
drawStyle: DrawStyle,
|
||||
bezierPatches: List<BezierPatch3DBase<ColorRGBa>>,
|
||||
subdivisions: Int = 32
|
||||
) {
|
||||
ensureVertexCount(bezierPatches.size * 16)
|
||||
val shader = shadeStyleManager.shader(
|
||||
drawStyle.shadeStyle,
|
||||
listOf(vertices.vertexFormat),
|
||||
emptyList()
|
||||
)
|
||||
vertices.put {
|
||||
for (bezierPatch in bezierPatches) {
|
||||
for (j in 0 until 4) {
|
||||
for (i in 0 until 4) {
|
||||
write(bezierPatch.points[j][i])
|
||||
if (bezierPatch.colors.isEmpty()) {
|
||||
write(ColorRGBa.WHITE)
|
||||
} else {
|
||||
write(bezierPatch.colors[j][i])
|
||||
}
|
||||
write(Vector2(i / 3.0, j / 3.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
shader.begin()
|
||||
shader.uniform("u_subdivisions", subdivisions)
|
||||
context.applyToShader(shader)
|
||||
drawStyle.applyToShader(shader)
|
||||
Driver.instance.setState(drawStyle)
|
||||
Driver.instance.drawVertexBuffer(
|
||||
shader,
|
||||
listOf(vertices),
|
||||
DrawPrimitive.PATCHES,
|
||||
0,
|
||||
16 * bezierPatches.size,
|
||||
16
|
||||
)
|
||||
shader.end()
|
||||
}
|
||||
|
||||
@JvmName("drawBezierPatch3DOKLab")
|
||||
fun drawBezierPatches(
|
||||
context: DrawContext,
|
||||
drawStyle: DrawStyle,
|
||||
bezierPatches: List<BezierPatch3DBase<ColorOKLABa>>,
|
||||
subdivisions: Int = 32
|
||||
) {
|
||||
ensureVertexCount(bezierPatches.size * 16)
|
||||
val shader = shadeStyleManagerOKLab.shader(
|
||||
drawStyle.shadeStyle,
|
||||
listOf(vertices.vertexFormat),
|
||||
emptyList()
|
||||
)
|
||||
|
||||
vertices.put {
|
||||
for(bezierPatch in bezierPatches) {
|
||||
for (j in 0 until 4) {
|
||||
for (i in 0 until 4) {
|
||||
write(bezierPatch.points[j][i])
|
||||
if (bezierPatch.colors.isEmpty()) {
|
||||
write(ColorRGBa.WHITE)
|
||||
} else {
|
||||
write(bezierPatch.colors[j][i].let {
|
||||
Vector4(it.l, it.a, it.b, it.alpha)
|
||||
})
|
||||
}
|
||||
write(Vector2(i / 3.0, j / 3.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
shader.begin()
|
||||
shader.uniform("u_subdivisions", subdivisions)
|
||||
context.applyToShader(shader)
|
||||
drawStyle.applyToShader(shader)
|
||||
Driver.instance.setState(drawStyle)
|
||||
Driver.instance.drawVertexBuffer(
|
||||
shader,
|
||||
listOf(vertices),
|
||||
DrawPrimitive.PATCHES,
|
||||
0,
|
||||
16 * bezierPatches.size,
|
||||
16
|
||||
)
|
||||
shader.end()
|
||||
}
|
||||
}
|
||||
|
||||
private val Drawer.bezierPatchDrawer: BezierPatchDrawer by lazy { BezierPatchDrawer() }
|
||||
@@ -281,3 +373,23 @@ fun Drawer.bezierPatch(bezierPatch: BezierPatchBase<ColorOKLABa>, subdivisions:
|
||||
fun Drawer.bezierPatches(bezierPatch: List<BezierPatchBase<ColorOKLABa>>, subdivisions: Int = 32) {
|
||||
bezierPatchDrawer.drawBezierPatches(context, drawStyle, bezierPatch, subdivisions)
|
||||
}
|
||||
|
||||
@JvmName("bezierPatch3DRGBa")
|
||||
fun Drawer.bezierPatch(bezierPatch: BezierPatch3DBase<ColorRGBa>, subdivisions: Int = 32) {
|
||||
bezierPatchDrawer.drawBezierPatches(context, drawStyle, listOf(bezierPatch), subdivisions)
|
||||
}
|
||||
|
||||
@JvmName("bezierPatches3DRGBa")
|
||||
fun Drawer.bezierPatches(bezierPatch: List<BezierPatch3DBase<ColorRGBa>>, subdivisions: Int = 32) {
|
||||
bezierPatchDrawer.drawBezierPatches(context, drawStyle, bezierPatch, subdivisions)
|
||||
}
|
||||
|
||||
@JvmName("bezierPatch3DOKLAB")
|
||||
fun Drawer.bezierPatch(bezierPatch: BezierPatch3DBase<ColorOKLABa>, subdivisions: Int = 32) {
|
||||
bezierPatchDrawer.drawBezierPatches(context, drawStyle, listOf(bezierPatch), subdivisions)
|
||||
}
|
||||
|
||||
@JvmName("bezierPatches3DOKLAB")
|
||||
fun Drawer.bezierPatches(bezierPatch: List<BezierPatch3DBase<ColorOKLABa>>, subdivisions: Int = 32) {
|
||||
bezierPatchDrawer.drawBezierPatches(context, drawStyle, bezierPatch, subdivisions)
|
||||
}
|
||||
|
||||
82
orx-shapes/src/demo/kotlin/DemoBezierPatch05.kt
Normal file
82
orx-shapes/src/demo/kotlin/DemoBezierPatch05.kt
Normal file
@@ -0,0 +1,82 @@
|
||||
import org.openrndr.WindowMultisample
|
||||
import org.openrndr.application
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.BufferMultisample
|
||||
import org.openrndr.extensions.SingleScreenshot
|
||||
import org.openrndr.extra.shapes.bezierPatch
|
||||
import org.openrndr.extra.shapes.drawers.bezierPatch
|
||||
import org.openrndr.extras.camera.Orbital
|
||||
import org.openrndr.math.Vector3
|
||||
import org.openrndr.shape.Segment3D
|
||||
|
||||
/**
|
||||
* Shows how to
|
||||
* - create a [bezierPatch] out of 4 [Segment3D]
|
||||
* - create a sub-patch out of a [bezierPatch]
|
||||
* - create horizontal and vertical [Path3D]s out of [bezierPatch]es
|
||||
* - add colors to a [bezierPatch]
|
||||
* - draw a [bezierPatch] surface
|
||||
*
|
||||
* The created contours are horizontal and vertical in "bezier-patch space" but
|
||||
* are rendered deformed following the shape of the bezier patch.
|
||||
*/
|
||||
suspend fun main() {
|
||||
application {
|
||||
configure {
|
||||
width = 800
|
||||
height = 800
|
||||
multisample = WindowMultisample.SampleCount(8)
|
||||
}
|
||||
program {
|
||||
if (System.getProperty("takeScreenshot") == "true") {
|
||||
extend(SingleScreenshot()) {
|
||||
this.outputFile = System.getProperty("screenshotPath")
|
||||
multisample = BufferMultisample.SampleCount(8)
|
||||
}
|
||||
}
|
||||
|
||||
val c0 = Segment3D(Vector3(-5.0, 0.0, -9.0), Vector3(5.0, 0.0, -9.0))
|
||||
val c1 = Segment3D(Vector3(-5.0, -5.0, -3.0), Vector3(5.0, -5.0, -3.0))
|
||||
val c2 = Segment3D(Vector3(-5.0, 5.0, 3.0), Vector3(5.0, 5.0, 3.0))
|
||||
val c3 = Segment3D(Vector3(-5.0, 0.0, 9.0), Vector3(5.0, 0.0, 9.0))
|
||||
|
||||
val col = listOf(ColorRGBa.PINK, ColorRGBa.RED, ColorRGBa.BLUE, ColorRGBa.PINK)
|
||||
val cols = listOf(col, col, col, col)
|
||||
val bp = bezierPatch(c0, c1, c2, c3).withColors(cols)
|
||||
val bpSub = bp.sub(0.1, 0.1, 0.6, 0.6)
|
||||
|
||||
val cam = Orbital()
|
||||
extend(cam){
|
||||
eye = Vector3(x=9.9, y=12.8, z=6.9)
|
||||
lookAt = Vector3(x=1.6, y=-1.9, z=1.2)
|
||||
}
|
||||
|
||||
extend {
|
||||
drawer.clear(ColorRGBa.PINK)
|
||||
|
||||
drawer.translate(-5.0, 0.0, 0.0)
|
||||
// Show the segments that form the bezier patch
|
||||
drawer.stroke = ColorRGBa.YELLOW
|
||||
drawer.strokeWeight = 50.0
|
||||
drawer.segments(listOf(c0, c1, c2, c3))
|
||||
|
||||
// Show the grid
|
||||
drawer.strokeWeight = 1.0
|
||||
val n = 10
|
||||
for (i in 0..n) {
|
||||
drawer.stroke = ColorRGBa.BLACK
|
||||
drawer.lineStrip(bp.horizontal(i / n.toDouble()).adaptivePositions(0.01))
|
||||
drawer.lineStrip(bp.vertical(i / n.toDouble()).adaptivePositions(0.01))
|
||||
|
||||
drawer.stroke = ColorRGBa.RED
|
||||
drawer.lineStrip(bpSub.horizontal(i / n.toDouble()).adaptivePositions(0.01))
|
||||
drawer.lineStrip(bpSub.vertical(i / n.toDouble()).adaptivePositions(0.01))
|
||||
}
|
||||
|
||||
// Draw the colored Bezier surface
|
||||
drawer.translate(10.0, 0.0, 0.0)
|
||||
drawer.bezierPatch(bp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user