diff --git a/src/main/kotlin/apps/ch13_groups.kt b/src/main/kotlin/apps/ch13_groups.kt index 15c8447..4fdb6ab 100644 --- a/src/main/kotlin/apps/ch13_groups.kt +++ b/src/main/kotlin/apps/ch13_groups.kt @@ -58,7 +58,7 @@ fun main() { Cylinder(0, 0.5, true, t) } - Group(gt, m, listOf(concentricCylinder1, concentricCylinder2, concentricCylinder3, concentricCylinder4)) + Group(gt, m, children=listOf(concentricCylinder1, concentricCylinder2, concentricCylinder3, concentricCylinder4)) } val decorativeCylinder1 = run { diff --git a/src/main/kotlin/shapes/Cone.kt b/src/main/kotlin/shapes/Cone.kt index c00a7e8..a2481d2 100644 --- a/src/main/kotlin/shapes/Cone.kt +++ b/src/main/kotlin/shapes/Cone.kt @@ -13,7 +13,7 @@ class Cone(minimum: Number = Double.NEGATIVE_INFINITY, maximum: Number = Double.POSITIVE_INFINITY, val closed: Boolean = false, transformation: Matrix = Matrix.I, - material: Material = Material(), + material: Material? = null, castsShadow: Boolean = true, parent: Shape? = null): Shape(transformation, material, castsShadow, parent) { @@ -21,8 +21,9 @@ class Cone(minimum: Number = Double.NEGATIVE_INFINITY, val minimum = minimum.toDouble() val maximum = maximum.toDouble() + // Note due to Kotlin semantics, we have to use objMaterial here. override fun withParent(parent: Shape?): Shape = - Cone(minimum, maximum, closed, transformation, material, castsShadow, parent) + Cone(minimum, maximum, closed, transformation, objMaterial, castsShadow, parent) override fun withMaterial(material: Material): Shape = Cone(minimum, maximum, closed, transformation, material, castsShadow, parent) diff --git a/src/main/kotlin/shapes/Cube.kt b/src/main/kotlin/shapes/Cube.kt index e863d3b..9fffca8 100644 --- a/src/main/kotlin/shapes/Cube.kt +++ b/src/main/kotlin/shapes/Cube.kt @@ -8,12 +8,14 @@ import math.Intersection import kotlin.math.absoluteValue class Cube(transformation: Matrix = Matrix.I, - material: Material = Material(), + material: Material? = null, castsShadow: Boolean = true, parent: Shape? = null): Shape(transformation, material, castsShadow, parent) { + + // Note due to Kotlin semantics, we have to use objMaterial here. override fun withParent(parent: Shape?): Shape = - Cube(transformation, material, castsShadow, parent) + Cube(transformation, objMaterial, castsShadow, parent) override fun withMaterial(material: Material): Shape = Cube(transformation, material, castsShadow, parent) diff --git a/src/main/kotlin/shapes/Cylinder.kt b/src/main/kotlin/shapes/Cylinder.kt index 4bc9701..4632cd1 100644 --- a/src/main/kotlin/shapes/Cylinder.kt +++ b/src/main/kotlin/shapes/Cylinder.kt @@ -11,7 +11,7 @@ class Cylinder(minimum: Number = Double.NEGATIVE_INFINITY, maximum: Number = Double.POSITIVE_INFINITY, val closed: Boolean = false, transformation: Matrix = Matrix.I, - material: Material = Material(), + material: Material? = null, castsShadow: Boolean = true, parent: Shape? = null): Shape(transformation, material, castsShadow, parent) { @@ -19,8 +19,9 @@ class Cylinder(minimum: Number = Double.NEGATIVE_INFINITY, val minimum = minimum.toDouble() val maximum = maximum.toDouble() + // Note due to Kotlin semantics, we have to use objMaterial here. override fun withParent(parent: Shape?): Shape = - Cylinder(minimum, maximum, closed, transformation, material, castsShadow, parent) + Cylinder(minimum, maximum, closed, transformation, objMaterial, castsShadow, parent) override fun withMaterial(material: Material): Shape = Cylinder(minimum, maximum, closed, transformation, material, castsShadow, parent) diff --git a/src/main/kotlin/shapes/Group.kt b/src/main/kotlin/shapes/Group.kt index 58d7d3d..264ba6e 100644 --- a/src/main/kotlin/shapes/Group.kt +++ b/src/main/kotlin/shapes/Group.kt @@ -7,17 +7,16 @@ import math.* import math.BoundingBox.Companion.MaxPoint import math.BoundingBox.Companion.MinPoint import math.Intersection -import kotlin.math.PI class Group(transformation: Matrix = Matrix.I, - material: Material = Material(), + material: Material? = null, children: List = emptyList(), castsShadow: Boolean = true, parent: Shape? = null): Shape(transformation, material, castsShadow, parent) { // Make copies of all the children to backreference this as their parent. - val children = children.map { it.withParent(this) } + val children = run { children.map { it.withParent(this) } } val size = children.size val isEmpty = children.isEmpty() val isNotEmpty = children.isNotEmpty() @@ -28,8 +27,9 @@ class Group(transformation: Matrix = Matrix.I, operator fun contains(s: Shape): Boolean = s in children + // Note due to Kotlin semantics, we have to use objMaterial here. override fun withParent(parent: Shape?): Shape = - Group(transformation, material, children, castsShadow, parent) + Group(transformation, objMaterial, children, castsShadow, parent) fun withTransformation(transformation: Matrix): Shape { if (!transformation.isTransformation()) @@ -38,7 +38,7 @@ class Group(transformation: Matrix = Matrix.I, return Group(transformation, material, children, castsShadow, parent) } - fun forEach(f: (Shape) -> Unit): Unit { + fun forEach(f: (Shape) -> Unit) { children.forEach(f) } diff --git a/src/main/kotlin/shapes/Plane.kt b/src/main/kotlin/shapes/Plane.kt index 7ea0764..8e34c2c 100644 --- a/src/main/kotlin/shapes/Plane.kt +++ b/src/main/kotlin/shapes/Plane.kt @@ -7,12 +7,14 @@ import math.* import kotlin.math.absoluteValue class Plane(transformation: Matrix = Matrix.I, - material: Material = Material(), + material: Material? = null, castsShadow: Boolean = true, parent: Shape? = null): Shape(transformation, material, castsShadow, parent) { + + // Note due to Kotlin semantics, we have to use objMaterial here. override fun withParent(parent: Shape?): Shape = - Plane(transformation, material, castsShadow, parent) + Plane(transformation, objMaterial, castsShadow, parent) override fun withMaterial(material: Material): Shape = Plane(transformation, material, castsShadow, parent) diff --git a/src/main/kotlin/shapes/Shape.kt b/src/main/kotlin/shapes/Shape.kt index 84b22eb..cfb5d9a 100644 --- a/src/main/kotlin/shapes/Shape.kt +++ b/src/main/kotlin/shapes/Shape.kt @@ -9,7 +9,7 @@ import java.util.UUID import kotlin.math.PI abstract class Shape(val transformation: Matrix, - val material: Material, + material: Material? = null, val castsShadow: Boolean, val parent: Shape?, private val id: UUID = UUID.randomUUID()) { @@ -19,6 +19,14 @@ abstract class Shape(val transformation: Matrix, "\tShape: ${javaClass.name}\nTransformation:\n${transformation.show()}") } + // We need to store the parameter passed in for material here in order to propagate it correctly. + // We will access it below: if an object does not have a material, it will try to see if it has a parent + // with a material. + protected val objMaterial = material + + val material: Material + get() = objMaterial ?: (parent?.objMaterial ?: DefaultMaterial) + // This method should only be invoked by Groups containing the object. internal abstract fun withParent(parent: Shape? = null): Shape @@ -71,4 +79,8 @@ abstract class Shape(val transformation: Matrix, internal val parentBounds: BoundingBox by lazy { bounds.transform(transformation) } + + companion object { + private val DefaultMaterial = Material() + } } diff --git a/src/main/kotlin/shapes/Sphere.kt b/src/main/kotlin/shapes/Sphere.kt index 51d3778..8724dc7 100644 --- a/src/main/kotlin/shapes/Sphere.kt +++ b/src/main/kotlin/shapes/Sphere.kt @@ -8,12 +8,14 @@ import math.Intersection import kotlin.math.sqrt class Sphere(transformation: Matrix = Matrix.I, - material: Material = Material(), + material: Material? = null, castsShadow: Boolean = true, parent: Shape? = null): Shape(transformation, material, castsShadow, parent) { + + // Note due to Kotlin semantics, we have to use objMaterial here. override fun withParent(parent: Shape?): Shape = - Sphere(transformation, material, castsShadow, parent) + Sphere(transformation, objMaterial, castsShadow, parent) override fun withMaterial(material: Material): Shape = Sphere(transformation, material, castsShadow, parent)