chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2026 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.image.toolbox.library)
|
||||
alias(libs.plugins.image.toolbox.compose)
|
||||
}
|
||||
|
||||
android.namespace = "com.t8rin.snowfall"
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ ImageToolbox is an image editor for android
|
||||
~ Copyright (c) 2026 T8RIN (Malik Mukhametzyanov)
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
~
|
||||
~ You should have received a copy of the Apache License
|
||||
~ along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
-->
|
||||
|
||||
<manifest />
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2026 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package com.t8rin.snowfall
|
||||
|
||||
internal object Constants {
|
||||
internal const val angleRange = 0.1f
|
||||
internal const val angleDivisor = 10000.0f
|
||||
internal const val angleSeed = 100.0f
|
||||
internal const val baseFrameDurationMillis = 16
|
||||
internal const val snowflakeDensity = 0.05
|
||||
internal const val defaultAlpha = 0.65f
|
||||
|
||||
internal val incrementRange = 0.2f..0.8f
|
||||
internal val sizeRange = 20f..40f
|
||||
internal val angleSeedRange = -angleSeed..angleSeed
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2026 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package com.t8rin.snowfall
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import com.t8rin.snowfall.Constants.angleRange
|
||||
import com.t8rin.snowfall.Constants.angleSeed
|
||||
import com.t8rin.snowfall.Constants.incrementRange
|
||||
import com.t8rin.snowfall.Constants.sizeRange
|
||||
import com.t8rin.snowfall.types.AnimType
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
internal data class SnowAnimState(
|
||||
var tickNanos: Long,
|
||||
val snowflakes: List<Snowflake>,
|
||||
val painters: List<Painter>,
|
||||
val animType: AnimType,
|
||||
val colors: List<Color>,
|
||||
val density: Double,
|
||||
val alpha: Float,
|
||||
) {
|
||||
constructor(
|
||||
tick: Long,
|
||||
canvasSize: IntSize,
|
||||
painters: List<Painter>,
|
||||
animType: AnimType,
|
||||
colors: List<Color>,
|
||||
density: Double,
|
||||
alpha: Float
|
||||
) : this(
|
||||
tickNanos = tick,
|
||||
snowflakes = createSnowFlakes(
|
||||
flakesProvider = painters,
|
||||
canvasSize = canvasSize,
|
||||
animType = animType,
|
||||
colors = colors,
|
||||
density = density,
|
||||
alpha = alpha
|
||||
),
|
||||
painters = painters,
|
||||
animType = animType,
|
||||
colors = colors,
|
||||
density = density,
|
||||
alpha = alpha
|
||||
)
|
||||
|
||||
fun draw(contentDrawScope: ContentDrawScope) {
|
||||
snowflakes.forEach {
|
||||
it.draw(contentDrawScope)
|
||||
}
|
||||
}
|
||||
|
||||
fun resize(newSize: IntSize) = copy(
|
||||
snowflakes = createSnowFlakes(
|
||||
flakesProvider = painters,
|
||||
canvasSize = newSize,
|
||||
animType = animType,
|
||||
colors = colors,
|
||||
density = density,
|
||||
alpha = alpha
|
||||
)
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
fun createSnowFlakes(
|
||||
flakesProvider: List<Painter>,
|
||||
canvasSize: IntSize,
|
||||
animType: AnimType,
|
||||
colors: List<Color>,
|
||||
density: Double,
|
||||
alpha: Float,
|
||||
): List<Snowflake> =
|
||||
when (animType) {
|
||||
AnimType.Falling -> createFallingSnowflakes(
|
||||
canvasSize = canvasSize,
|
||||
painters = flakesProvider,
|
||||
colors = colors,
|
||||
snowflakeDensity = density,
|
||||
alpha = alpha
|
||||
)
|
||||
|
||||
AnimType.Melting -> createMeltingSnowflakes(
|
||||
canvasSize = canvasSize,
|
||||
painters = flakesProvider,
|
||||
colors = colors,
|
||||
snowflakeDensity = density,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createMeltingSnowflakes(
|
||||
canvasSize: IntSize,
|
||||
painters: List<Painter>,
|
||||
colors: List<Color>,
|
||||
snowflakeDensity: Double,
|
||||
): List<MeltingSnowflake> {
|
||||
if (canvasSize.height == 0 || canvasSize.width == 0 || snowflakeDensity == 0.0) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val canvasArea = canvasSize.width * canvasSize.height
|
||||
val normalizedDensity = snowflakeDensity.coerceIn(0.0..1.0) / 2000.0
|
||||
val count = (canvasArea * normalizedDensity).roundToInt()
|
||||
val snowflakesCount = count.coerceIn(painters.size.coerceAtMost(count), count)
|
||||
|
||||
return List(snowflakesCount) {
|
||||
MeltingSnowflake(
|
||||
incrementFactor = incrementRange.random(),
|
||||
canvasSize = canvasSize,
|
||||
maxAlpha = (0.1f..0.7f).random(),
|
||||
painter = painters[it % painters.size],
|
||||
initialPosition = canvasSize.randomPosition(),
|
||||
color = colors.random(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFallingSnowflakes(
|
||||
canvasSize: IntSize,
|
||||
painters: List<Painter>,
|
||||
colors: List<Color>,
|
||||
snowflakeDensity: Double,
|
||||
alpha: Float,
|
||||
): List<FallingSnowflake> {
|
||||
val canvasArea = canvasSize.width * canvasSize.height
|
||||
val normalizedDensity = snowflakeDensity.coerceIn(0.0..1.0) / 1000.0
|
||||
val snowflakesCount = (canvasArea * normalizedDensity).roundToInt()
|
||||
|
||||
return List(snowflakesCount) {
|
||||
FallingSnowflake(
|
||||
incrementFactor = incrementRange.random(),
|
||||
size = sizeRange.random(),
|
||||
canvasSize = canvasSize,
|
||||
initialPosition = canvasSize.randomPosition(),
|
||||
angle = angleSeed.random() / angleSeed * angleRange + (PI / 2.0) - (angleRange / 2.0),
|
||||
painter = painters[it % painters.size],
|
||||
color = colors.random(),
|
||||
alpha = alpha,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2026 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package com.t8rin.snowfall
|
||||
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.withFrameNanos
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import com.t8rin.snowfall.Constants.defaultAlpha
|
||||
import com.t8rin.snowfall.Constants.snowflakeDensity
|
||||
import com.t8rin.snowfall.types.AnimType
|
||||
import com.t8rin.snowfall.types.FlakeType
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlin.time.Duration.Companion.nanoseconds
|
||||
|
||||
/**
|
||||
* Creates flakes falling animation base on `FlakeType` param.
|
||||
* @param type type of flake.
|
||||
* @param colors list of Colors for Flakes
|
||||
* @param density density of Flakes must be between 0.0 (no Flakes) or 1.0 (a lot of Flakes). Default is 0.05
|
||||
* @param alpha transparency of the falling flakes
|
||||
*
|
||||
*/
|
||||
fun Modifier.snowfall(
|
||||
type: FlakeType,
|
||||
colors: List<Color>,
|
||||
density: Double = snowflakeDensity,
|
||||
alpha: Float = defaultAlpha,
|
||||
): Modifier =
|
||||
letItSnow(
|
||||
flakeType = type,
|
||||
animType = AnimType.Falling, colors = colors,
|
||||
density = density,
|
||||
alpha = alpha
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates flakes falling animation base on `FlakeType` param.
|
||||
* @param type type of flake.
|
||||
* @param color single Color for Flakes
|
||||
* @param density density of Flakes must be between 0.0 (no Flakes) or 1.0 (a lot of Flakes). Default is 0.05
|
||||
* @param alpha transparency of the falling flakes
|
||||
*
|
||||
*/
|
||||
fun Modifier.snowfall(
|
||||
type: FlakeType,
|
||||
color: Color = Color.Unspecified,
|
||||
density: Double = snowflakeDensity,
|
||||
alpha: Float = defaultAlpha,
|
||||
): Modifier =
|
||||
letItSnow(
|
||||
flakeType = type,
|
||||
animType = AnimType.Falling, colors = listOf(color),
|
||||
density = density,
|
||||
alpha = alpha
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates flakes melting animation base on `FlakeType` param.
|
||||
* @param type - type of flake.
|
||||
* @param colors - list of Colors for Flakes
|
||||
* @param density - density of Flakes must be between 0.0 (no Flakes) or 1.0 (a lot of Flakes). Default is 0.05
|
||||
*
|
||||
*/
|
||||
fun Modifier.snowmelt(
|
||||
type: FlakeType,
|
||||
colors: List<Color>,
|
||||
density: Double = snowflakeDensity,
|
||||
): Modifier =
|
||||
letItSnow(type, AnimType.Melting, colors = colors, density = density)
|
||||
|
||||
/**
|
||||
* Creates flakes melting animation base on `FlakeType` param.
|
||||
* @param type - type of flake.
|
||||
* @param color - single Color for Flakes
|
||||
* @param density - density of Flakes must be between 0.0 (no Flakes) or 1.0 (a lot of Flakes). Default is 0.05
|
||||
*
|
||||
*/
|
||||
fun Modifier.snowmelt(
|
||||
type: FlakeType,
|
||||
color: Color = Color.Unspecified,
|
||||
density: Double = snowflakeDensity,
|
||||
): Modifier =
|
||||
letItSnow(type, AnimType.Melting, colors = listOf(color), density = density)
|
||||
|
||||
|
||||
private fun Modifier.letItSnow(
|
||||
flakeType: FlakeType,
|
||||
animType: AnimType,
|
||||
colors: List<Color>,
|
||||
density: Double,
|
||||
alpha: Float = 1f,
|
||||
) = composed {
|
||||
val flakes = when (flakeType) {
|
||||
is FlakeType.Custom -> flakeType.data
|
||||
}
|
||||
var snowAnimState by remember {
|
||||
mutableStateOf(
|
||||
SnowAnimState(
|
||||
tick = -1,
|
||||
canvasSize = IntSize(0, 0),
|
||||
painters = flakes,
|
||||
animType = animType,
|
||||
colors = colors,
|
||||
density = density,
|
||||
alpha = alpha,
|
||||
)
|
||||
)
|
||||
}
|
||||
val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
while (isActive) {
|
||||
if (lifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
|
||||
withFrameNanos { frameTimeNanos ->
|
||||
val elapsedMillis =
|
||||
(frameTimeNanos - snowAnimState.tickNanos).nanoseconds.inWholeMilliseconds
|
||||
val isFirstRun = snowAnimState.tickNanos < 0
|
||||
snowAnimState.tickNanos = frameTimeNanos
|
||||
if (isFirstRun) return@withFrameNanos 0
|
||||
snowAnimState.snowflakes.forEach {
|
||||
it.update(elapsedMillis)
|
||||
}
|
||||
return@withFrameNanos frameTimeNanos
|
||||
}
|
||||
} else {
|
||||
withFrameNanos { frameTimeNanos ->
|
||||
snowAnimState.tickNanos = frameTimeNanos
|
||||
}
|
||||
}
|
||||
delay(8L)
|
||||
}
|
||||
}
|
||||
onSizeChanged { newSize ->
|
||||
snowAnimState = snowAnimState.resize(newSize)
|
||||
}
|
||||
.clipToBounds()
|
||||
.drawWithContent {
|
||||
drawContent()
|
||||
snowAnimState.draw(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2026 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package com.t8rin.snowfall
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableDoubleStateOf
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import com.t8rin.snowfall.Constants.angleDivisor
|
||||
import com.t8rin.snowfall.Constants.angleSeedRange
|
||||
import com.t8rin.snowfall.Constants.baseFrameDurationMillis
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
internal interface Snowflake {
|
||||
fun update(elapsedMillis: Long)
|
||||
fun draw(contentDrawScope: ContentDrawScope)
|
||||
}
|
||||
|
||||
internal class MeltingSnowflake(
|
||||
private val initialPosition: Offset,
|
||||
private val incrementFactor: Float,
|
||||
private val canvasSize: IntSize,
|
||||
private val maxAlpha: Float,
|
||||
private val painter: Painter,
|
||||
private val color: Color,
|
||||
) : Snowflake {
|
||||
|
||||
init {
|
||||
require(maxAlpha in 0.1..1.0)
|
||||
}
|
||||
|
||||
private var position by mutableStateOf(initialPosition)
|
||||
private var alpha by mutableFloatStateOf(0.001f)
|
||||
private var isIncreasing by mutableStateOf(true)
|
||||
|
||||
override fun update(elapsedMillis: Long) {
|
||||
val increment = incrementFactor * (elapsedMillis / baseFrameDurationMillis) / 100f
|
||||
alpha = (if (isIncreasing) {
|
||||
alpha + increment
|
||||
} else {
|
||||
alpha - increment
|
||||
}).coerceIn(0f, maxAlpha)
|
||||
|
||||
if (alpha == maxAlpha) {
|
||||
isIncreasing = false
|
||||
}
|
||||
|
||||
if (alpha == 0f) {
|
||||
isIncreasing = true
|
||||
alpha = 0.001f
|
||||
position = canvasSize.randomPosition()
|
||||
}
|
||||
}
|
||||
|
||||
override fun draw(contentDrawScope: ContentDrawScope) {
|
||||
with(contentDrawScope) {
|
||||
translate(
|
||||
left = position.x,
|
||||
top = position.y
|
||||
) {
|
||||
with(painter) {
|
||||
draw(
|
||||
size = intrinsicSize,
|
||||
alpha = alpha,
|
||||
colorFilter = if (color == Color.Unspecified) null else ColorFilter.tint(
|
||||
color
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class FallingSnowflake(
|
||||
private val incrementFactor: Float,
|
||||
private val size: Float,
|
||||
private val canvasSize: IntSize,
|
||||
initialPosition: Offset,
|
||||
angle: Double,
|
||||
private val painter: Painter,
|
||||
private val color: Color,
|
||||
private val alpha: Float,
|
||||
) : Snowflake {
|
||||
private val baseSpeedPxAt60Fps = 5
|
||||
private var position by mutableStateOf(initialPosition)
|
||||
private var angle by mutableDoubleStateOf(angle)
|
||||
|
||||
override fun update(elapsedMillis: Long) {
|
||||
val increment =
|
||||
incrementFactor * (elapsedMillis / baseFrameDurationMillis) * baseSpeedPxAt60Fps
|
||||
val xDelta = (increment * cos(angle)).toFloat()
|
||||
val yDelta = (increment * sin(angle)).toFloat()
|
||||
position = Offset(position.x + xDelta, position.y + yDelta)
|
||||
angle += angleSeedRange.random() / angleDivisor
|
||||
|
||||
if (position.y > canvasSize.height + size) {
|
||||
position =
|
||||
Offset(canvasSize.width.random().toFloat(), -size - painter.intrinsicSize.height)
|
||||
}
|
||||
}
|
||||
|
||||
override fun draw(contentDrawScope: ContentDrawScope) {
|
||||
with(contentDrawScope) {
|
||||
translate(
|
||||
position.x,
|
||||
position.y
|
||||
) {
|
||||
with(painter) {
|
||||
draw(
|
||||
size = intrinsicSize,
|
||||
alpha = alpha,
|
||||
colorFilter = if (color == Color.Unspecified) null else ColorFilter.tint(
|
||||
color
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2026 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package com.t8rin.snowfall
|
||||
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import java.util.concurrent.ThreadLocalRandom
|
||||
|
||||
|
||||
internal fun ClosedRange<Float>.random() =
|
||||
ThreadLocalRandom.current().nextFloat() * (endInclusive - start) + start
|
||||
|
||||
internal fun Float.random() =
|
||||
ThreadLocalRandom.current().nextFloat() * this
|
||||
|
||||
internal fun Int.random() =
|
||||
ThreadLocalRandom.current().nextInt(this)
|
||||
|
||||
internal fun IntSize.randomPosition() =
|
||||
Offset(width.random().toFloat(), height.random().toFloat())
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2026 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package com.t8rin.snowfall.types
|
||||
|
||||
internal enum class AnimType {
|
||||
Falling,
|
||||
Melting
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2026 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package com.t8rin.snowfall.types
|
||||
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
|
||||
/**
|
||||
* Type of flake used for animation.
|
||||
*/
|
||||
sealed interface FlakeType {
|
||||
class Custom(val data: List<Painter>) : FlakeType
|
||||
}
|
||||
Reference in New Issue
Block a user