I have the following data class representing a color from red/green/blue values :
data class HexColor(
val red: Byte,
val blue: Byte,
val green: Byte
)
I'm trying to create an enumeration with defined colors (RED, YELLOW, PURPLE, ...) and colors from other red/green/blue possibilities (using HexColor) :
enum class Color {
RED,
YELLOW,
PURPLE,
// ...
HEX_COLOR: HexColor // how to represent this ?
}
The previous code does not compile, but just shows the general idea I want to implement.
How can I define my enum to represent a Color as defined values without parameters (constants like YELLOW for example) and HexColor (a data class with parameters). Is it possible ?
The idea is to use like this, or something similar :
val red = Color.RED
val blue = Color.HEX_COLOR(0, 255, 0)
It's rather not possible with an enum class (all enum instances need to be defined at the compile time), but you can try something like this:
class Color private constructor(val r: Float, val g: Float, val b: Float) {
companion object {
val RED = Color(1.0f, 0.0f, 0.0f)
val GREEN = Color(0.0f,1.0f, 0.0f)
val BLUE = Color(0.0f, 0.0f, 1.0f)
fun HEX_COLOR(r: Float, g: Float, b: Float) = Color(r, g, b)
}
}
Related
I use Code A to draw a shape with a path, I hope to fill the shape in a color, and draw border with different color and width. I get the Image A as I expected.
I find Code A to launch drawPath operation two times, maybe it's not good way, can I use drawPath to both fill shape and draw border only one time?
Code A
fun setProcess(drawScope: DrawScope, dataList: List<Double>){
drawScope.drawIntoCanvas{
val step = xAxisLength / maxPointCount
val shadowPath = Path()
shadowPath.moveTo(0f.toX, 0f.toY)
for (i in dataList.indices) {
...
}
shadowPath.close()
it.drawPath(shadowPath, paintTablePath)
it.drawPath(shadowPath, paintTableBorder)
}
}
val paintTablePath = Paint().also {
it.isAntiAlias = true
it.style = PaintingStyle.Fill
it.strokeWidth = 1f
it.color = Color(0xffdfecfe)
}
val paintTableBorder = Paint().also {
it.isAntiAlias = true
it.style = PaintingStyle.Stroke
it.strokeWidth = 3f
it.color = Color.Red
}
Image A
What you ask is neither available in Compose nor Android Canvas. As you can check here but with Jetpack Compose Canvas.
You don't need to use the ones with Paint unless you need some properties of Paint or functions like drawText
DrawScope already has functions such as
fun drawPath(
path: Path,
color: Color,
/*#FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
And instead of passing DrawScope as parameter it would help creating extension functions of DrawScope as default Compose source code often does.
inline fun DrawScope.rotate(
degrees: Float,
pivot: Offset = center,
block: DrawScope.() -> Unit
) = withTransform({ rotate(degrees, pivot) }, block)
inline fun DrawScope.clipRect(
left: Float = 0.0f,
top: Float = 0.0f,
right: Float = size.width,
bottom: Float = size.height,
clipOp: ClipOp = ClipOp.Intersect,
block: DrawScope.() -> Unit
) = withTransform({ clipRect(left, top, right, bottom, clipOp) }, block)
I want to print out labels of spices in SpiceContainer but I get
Here is my code. How do I solve this?
package Spice
fun main(args: Array<String> ) {
jack()
}
class Spice(){
val name:String=("Curry")
val spiciness:String=("mild")
}
data class SpiceContainer(var spice: Spice){
val label=spice.name
}
fun jack(){
val spiceCabinet = listOf(SpiceContainer(Curry("Yellow Curry", "mild")),
SpiceContainer(Curry("Red Curry", "medium")),
SpiceContainer(Curry("Green Curry", "spicy")))
for(element in spiceCabinet) println(element.label)
}
You are trying to create an instance of a class Curry you just don't have. Instead, you have a class Spice with a name and a spiciness. If you want an instance of, say Red Curry, you would have to instantiate a Spice and give it the desired name: Spice("Red Curry", "kinda hot").
A good practice for that spiciness (at least a better one than just a String) would be an enum class Spiciness...
Here's a simple example:
// a spice consisting of a name and some degree of spiciness
data class Spice(val name: String, val spiciness: Spiciness)
// the spice container that gets the label of the spice contained
data class SpiceContainer(val spice: Spice) {
val label: String = spice.name
}
// enumerate possible degrees of spiciness
enum class Spiciness {
NONE_AT_ALL,
MILD,
MODERATE,
HOT,
DANGEROUSLY_HOT,
LIFE_THREATENING,
UNKNOWN_TRY_YOURSELF
}
fun jack() {
// create a list of spices and add some arbitrary ones
val spiceCabinet = listOf(SpiceContainer(Spice("Yellow Curry", Spiciness.MODERATE)),
SpiceContainer(Spice("Red Curry", Spiciness.HOT)),
SpiceContainer(Spice("Green Curry", Spiciness.UNKNOWN)),
SpiceContainer(Spice("Pepper", Spiciness.MILD)))
// print each item's label
spiceCabinet.forEach{ it -> println(it.label) }
}
and run the fun jack() like this
fun main() {
jack()
}
in order to get an output of
Yellow Curry
Red Curry
Green Curry
Pepper
I don't really get why you need or want the SpiceContainer, but that's beyond the scope of this answer (and question, most likely).
currently i have an uni project which involves on building a simpler version of the game arkanoid (ball that reflects on racket destroying blocks) on canvas using kotlin. We use intelij. Im trying to create a function that based on a mutable list of bricks, builds a brick on canvas and adds it to the list, and when the ball hits it , it disapears and gets removed from that list.
So far i have this
enum class TypeOfBrick(val color: Int, val points: Int){
Sb(SILVER,0),
Wb(WHITE,1),
Ob(ORANGE,2),
Cb(CYAN,3),
Gb(GREEN,4),
Rb(RED,6),
Bb(BLUE,7),
Mb(MAGENTA,8),
Yb(YELLOW,9),
}
data class Brick(val x: Int, val y: Int, val height: Int = Brick_Height, val width: Int = Brick_Width, val typeOfBrick: TypeOfBrick)
val brickList = mutableListOf<TypeOfBrick>()
fun createBrick(x: Int = Random.nextInt(32, Width-Brick_Width), y: Int = Random.nextInt(3*Brick_Height, 9*Brick_Height), typeOfBrick: TypeOfBrick): Brick{
return Brick(x, y, Brick_Height, Brick_Width, typeOfBrick)
}
fun Canvas.drawBrick(brick: Brick){
this.drawRect(brick.x, brick.y, Brick_Width, Brick_Height, brick.typeOfBrick.color)
}
fun addBricksToList(canvas: Canvas, brick: Brick) {
/*
* HERE: I know inside here i can use the for loop,
* but i just cant seem to do it.
*/
}
What I would like to have is two different integer types which are semantically distinguishable.
E.g. in this code a 'Meter' type and a 'Pixel' int type
typealias Meter = Int
typealias Pixel = Int
fun Meter.toPixel() = this * 100
fun Pixel.toMeter() = this / 100
fun calcSquareMeters(width: Meter, height: Meter) = width * height
fun calcSquarePixels(width: Pixel, height: Pixel) = width * height
fun main(args: Array<String>) {
val pixelWidth: Pixel = 50
val pixelHeight: Pixel = 50
val meterWidth: Meter = 50
val meterHeight: Meter = 50
calcSquareMeters(pixelWidth, pixelHeight) // (a) this should not work
pixelWidth.toPixel() // (b) this should not work
}
The problem with this solution is
(a) that I can call calcSquareMeters with my 'Pixel' type which I don't want to be possible and
(b) that I can call the toPixel() extension function which I only want to have for my 'Meter' type on my 'Pixel' type which I don't want to be possible.
I guess this is the intended behaviour of typealias, so I guess to achieve my goal I have to use something different than typealias...
So how can I achieve this?
In addition to the existing answer: If you have a lot of common functionality between the two types and don't want to duplicate it, you can work with an interface:
interface MetricType<T> {
val value: Int
fun new(value: Int): T
}
data class Meter(override val value: Int) : MetricType<Meter> {
override fun new(value: Int) = Meter(value)
}
data class Pixel(override val value: Int) : MetricType<Pixel> {
override fun new(value: Int) = Pixel(value)
}
Like this, you can easily define operations on the base interface, such as addition, subtraction and scaling:
operator fun <T : MetricType<T>> T.plus(rhs: T) = new(this.value + rhs.value)
operator fun <T : MetricType<T>> T.minus(rhs: T) = new(this.value + rhs.value)
operator fun <T : MetricType<T>> T.times(rhs: Int) = new(this.value * rhs)
The combination of interface and generics ensures type safety, so you do not accidentally mix types:
fun test() {
val m = Meter(3)
val p = Pixel(7)
val mm = m + m // OK
val pp = p + p // OK
val mp = m + p // does not compile
}
Keep in mind that this solution comes at a runtime cost due to the virtual functions (compared to rewriting the operators for each type separately). This in addition to the overhead of object creation.
Indeed, typealiases don't guarantee this sort of type safety. You'll have to create wrapper classes around an Int value instead to achieve this - it's a good idea to make these data classes so that equality comparisons work on them:
data class Meter(val value: Int)
data class Pixel(val value: Int)
Creation of instances of these classes can be solved with extension properties:
val Int.px
get() = Pixel(this)
val pixelWidth: Pixel = 50.px
The only problematic thing is that you can no longer directly perform arithmetic operations on Pixel and Meter instances, for example, the conversion functions would now look like this:
fun Meter.toPixel() = this.value * 100
Or the square calculations like this:
fun calcSquareMeters(width: Meter, height: Meter) = width.value * height.value
If you really need direct operator use, you can still define those, but it will be quite tedious:
class Meter(val value: Int) {
operator fun times(that: Meter) = this.value * that.value
}
fun calcSquareMeters(width: Meter, height: Meter) = width * height
There is a proposal (not yet guaranteed to be accepted) to add inline classes for this purpose. I.e.
#InlineOnly inline class Meter(val value: Int)
will really be an Int at runtime.
See https://github.com/zarechenskiy/KEEP/blob/28f7fdbe9ca22db5cfc0faeb8c2647949c9fd61b/proposals/inline-classes.md and https://github.com/Kotlin/KEEP/issues/104.
From kotlin doc:
Type aliases do not introduce new types. They are equivalent to the corresponding underlying types. When you add typealias Predicate and use Predicate in your code, the Kotlin compiler always expand it to (Int) -> Boolean. Thus you can pass a variable of your type whenever a general function type is required and vice versa
This means that there isn't possible check over your typealias, and you are rally declaring your extensions functions as:
fun Int.toPixel() = this * 100
fun Int.toMeter() = this / 100
fun calcSquareMeters(width: Int, height: Int) = width * height
fun calcSquarePixels(width: Int, height: Int) = width * height
I fear the only way to achieve that you want is implementing an extra class for each type.
I would also go with the solution from TheOperator. But I would like to add the inline keyword to the operator functions. By doing so you could avoid a virtual function call when ever you use this operators.
inline operator fun <T : MetricType<T>> T.plus(rhs: T) = new(this.value + rhs.value)
inline operator fun <T : MetricType<T>> T.minus(rhs: T) = new(this.value + rhs.value)
inline operator fun <T : MetricType<T>> T.times(rhs: Int) = new(this.value * rhs)
I'm trying to reduce duplication across some vector types by defining the operators once, but I'm not sure it's possible. This seemed like the most promising approach:
open class VecN<Derived: VecN<Derived>>(val buffer: FloatArray) {
operator fun minus(other: Derived) = Derived(buffer.zip(other.buffer, { a, b -> a - b }).toFloatArray())
operator fun plus(other: Derived) = Derived(buffer.zip(other.buffer, { a, b -> a + b }).toFloatArray())
... many more operators...
}
class Vec2(x: Float, y: Float) : VecN<Vec2>(floatArrayOf(x, y))
class Vec3(x: Float, y: Float, z: Float) : VecN<Vec3>(floatArrayOf(x, y, z))
class Vec4(x: Float, y: Float, z: Float, w: Float) : VecN<Vec4>(floatArrayOf(x, y, z, w))
This gives me "Type parameter Derived cannot be called as function" where I try to construct my Derived return value.
Is it possible to achieve this in Kotlin?
You cannot do that in a straightforward way because, in Kotlin, you can only call a constructor of a concrete type, there's no way to call a constructor of a type parameter. Moreover, Kotlin does not allow passing an array into a function/constructor that expects fixed number of separate values.
However, you can try to achieve that without too much boilerplate using an abstract function, like this:
abstract class VecN<Derived: VecN<Derived>>(val buffer: FloatArray) {
protected abstract fun createNew(buffer: FloatArray): Derived
operator fun minus(other: Derived) =
createNew(buffer.zip(other.buffer, Float::minus).toFloatArray())
// ...
}
Then you have to override this function in each of the derived classes:
class Vec2(x: Float, y: Float) : VecN<Vec2>(floatArrayOf(x, y)) {
override protected fun createNew(buffer: FloatArray) = Vec2(buffer[0], buffer[1])
}
(demo of this code)