I;m working through the ray tracer challenge book,...
# announcements
p
I;m working through the ray tracer challenge book, which is a test based tutorial on how to code a ray tracer. At the start of the book they define a class known as a Tuple, which models a position or a vector. A simplified version is this class Tuple(val x: Double, val y: Double, val z: Double, val w: Double) There's a lot of overloaded constructors and operator overloads but the basic structure is pretty simple, 4 doubles, x,y,z, and w. In the second chapter they introduce the concept of a Color, which is in essence also a Tuple, with red,green and blue instead of x,y and z. Is there any way I can somehow base Color on Tuple, with RGB mapping to xyz ?I guess I could delegate to a Tuple rather than somehow extend it, but that that sounds a bit clunky.
g
I think that own class with own set of properties is just better
Don’t see why you need such specialised (not-generic) Tuple class. Why not just use
Vector
Copy code
data class Vector(val x: Double, val y: Double, val z: Double, val w: Double)
Copy code
data class Color(val alpha: Double, val red: Double, val green: Double, val blue: Double)
p
Because for example, I have functions like this: fun dot(a: Tuple, b: Tuple) = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w and this: operator fun times(scale: Double): Any { return Tuple(x * scale, y * scale, z * scale, w * scale) } That I don't want to copy paste
g
This is type safe and idiomatic, don’t see any reason to use common parent class (especially for such different classes). If you want represent color as vector for some calculations, I would better write explicit converter that also allows add some custom converting rules or validation Or just some builder function that creates vector from color
👍 2
So what function times should do for color? Isnt’ it easy to get invalid color values
I agree, you cannot abstract such behaviours like above, but I would rather copy-paste functions for each type, but do not create data models hierarchy, this usually not a good idea Also, you can provide only functions that make sense for this model
p
The builder to convert Color to Vector for certain operations sounds good, I'll try that. Thanks !
g
I just want to make it clear what I mean about adapter and about builder: Adapter:
Copy code
fun Color.toVector() = Vector(alpha, red, green, blue)
Builder function:
Copy code
vectorFromColor(alpha: Double, red: Double, green: Double, blue: Double) = Vector(alpha, red, green, blue)
builder doesn’t look as very useful, but just provides little bit better API, harder to make mistake if you use named arguments:
Copy code
vectorFromColor(alpha = 0, red = 0.5, green = 0.2, blue = 0.1)
Also cool thing about having own class, that you actually can optimise it for particular use case. For example use color to use just one Int field under the hood but keep old API (of course it depends on case, because this would save memory, but you need to extract color channel value each time when someone requested it)
One more thing, you always can introduce some interface something like:
Copy code
interface FourComponents<A, B, C, D> {
    operator fun component1(): A
    operator fun component2(): B
    operator fun component3(): C
    operator fun component4(): D
}
it allows you to use destructuring syntax:
Copy code
val (a, r, g, b) = someColor
And abstract such classes for cases like times But you have to implement it each class, but at least it’s interface implementation, not a class that have much more problems in terms of inheritance. So I would recommend to do this only if you have some real use cases for this
👍 1
d
Yeah, you could make color even an inline class. What's the advantage of declaring the interface
FourComponents
? You now have to write
override fun
instead of
operator fun
and any primitives have to get boxed.
g
Yes, it's true about boxing and that you still have to implement it. Sometimes you just need some abstraction to manipulate similar data models and this is just one of ways to implement it without subclasses. But as I mentioned, this rarely make sense
d
I see