From e41c5d616d70b7e4d8eb54f65e82404dc66c554f Mon Sep 17 00:00:00 2001 From: Jerboa-app Date: Sat, 17 Aug 2024 08:42:25 +0100 Subject: [PATCH] Add particles sizes slider Refactor sliders Adds play controller to vertical menu --- .../app/jerboa/spp/composable/menuPrompt.kt | 19 +++ .../java/app/jerboa/spp/composable/screen.kt | 5 +- .../java/app/jerboa/spp/composable/toyMenu.kt | 156 ++++++------------ app/src/main/java/app/jerboa/spp/data/fade.kt | 2 +- .../app/jerboa/spp/data/particleShaders.kt | 6 +- .../app/jerboa/spp/ui/view/SPPRenderer.kt | 4 +- .../java/app/jerboa/spp/ui/view/SPPView.kt | 1 + ...inMenuViewModel.kt => toyMenuViewModel.kt} | 17 +- 8 files changed, 93 insertions(+), 117 deletions(-) rename app/src/main/java/app/jerboa/spp/viewmodel/{mainMenuViewModel.kt => toyMenuViewModel.kt} (91%) diff --git a/app/src/main/java/app/jerboa/spp/composable/menuPrompt.kt b/app/src/main/java/app/jerboa/spp/composable/menuPrompt.kt index 93c0930..ddec3ee 100644 --- a/app/src/main/java/app/jerboa/spp/composable/menuPrompt.kt +++ b/app/src/main/java/app/jerboa/spp/composable/menuPrompt.kt @@ -11,6 +11,7 @@ import androidx.compose.animation.fadeOut import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.material.IconButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState @@ -21,12 +22,14 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import app.jerboa.spp.viewmodel.AboutViewModel import app.jerboa.spp.viewmodel.MenuPromptViewModel +import app.jerboa.spp.viewmodel.SPPViewModel @OptIn(ExperimentalAnimationApi::class) @Composable fun menuPrompt( menuPromptViewModel: MenuPromptViewModel, aboutViewModel: AboutViewModel, + sppViewModel: SPPViewModel, images: Map, menuItemHeight: Double ){ @@ -34,6 +37,7 @@ fun menuPrompt( val displayingMenu: Boolean by menuPromptViewModel.displayingMenu.observeAsState(initial = false) val displayingSound: Boolean by menuPromptViewModel.displayingSound.observeAsState(initial = false) val paused: Boolean by menuPromptViewModel.paused.observeAsState(initial = false) + val playSuccess: Boolean by sppViewModel.playSuccess.observeAsState(initial = false) val fadePromptAlpha: Float by animateFloatAsState( targetValue = if (!displayingMenu) 0.33f else 1.0f, @@ -139,6 +143,21 @@ fun menuPrompt( } ) ) + IconButton(onClick = { sppViewModel.onRequestPlayServices() }) { + Image( + modifier = Modifier + .size(menuItemHeight.dp) + .alpha( + if (playSuccess) { + 0.66f + } else { + 1f + } + ), + painter = painterResource(id = images["play-controller"]!!), + contentDescription = "play" + ) + } } } } diff --git a/app/src/main/java/app/jerboa/spp/composable/screen.kt b/app/src/main/java/app/jerboa/spp/composable/screen.kt index d8b6b6f..16ecb24 100644 --- a/app/src/main/java/app/jerboa/spp/composable/screen.kt +++ b/app/src/main/java/app/jerboa/spp/composable/screen.kt @@ -59,6 +59,7 @@ fun screen( val spin: Float by toyMenuViewModel.spinStrength.observeAsState(1500f) val mass: Float by toyMenuViewModel.mass.observeAsState(0.1f) val fade: Float by toyMenuViewModel.fade.observeAsState(initial = 1.0f) + val scale: Float by toyMenuViewModel.scale.observeAsState(initial = 3f) val showToys: Boolean by toyMenuViewModel.showToys.observeAsState(initial = false) val promptPGS: Boolean by sppViewModel.promptInstallPGS.observeAsState(initial = false) @@ -81,8 +82,6 @@ fun screen( bottomBar = { toyMenu( toyMenuViewModel, - sppViewModel, - aboutViewModel, displayingToyMenu, width75Percent, height10Percent, @@ -152,6 +151,7 @@ fun screen( view.setOrbit(orbit) view.setSpin(spin) view.setFade(fade) + view.setScale(scale) view.showToys(showToys) if (clear) { view.clearToys(); menuPromptViewModel.onClear(false); } } @@ -169,6 +169,7 @@ fun screen( menuPrompt( menuPromptViewModel, aboutViewModel, + sppViewModel, images, menuItemHeight ) diff --git a/app/src/main/java/app/jerboa/spp/composable/toyMenu.kt b/app/src/main/java/app/jerboa/spp/composable/toyMenu.kt index 14f75bd..09bdaf3 100644 --- a/app/src/main/java/app/jerboa/spp/composable/toyMenu.kt +++ b/app/src/main/java/app/jerboa/spp/composable/toyMenu.kt @@ -1,5 +1,6 @@ package app.jerboa.spp.composable +import android.util.Log import androidx.compose.animation.* import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -31,11 +32,13 @@ import app.jerboa.spp.viewmodel.MAX_LOG_ORBIT import app.jerboa.spp.viewmodel.MAX_LOG_SPEED import app.jerboa.spp.viewmodel.MAX_LOG_SPIN import app.jerboa.spp.viewmodel.MAX_PARTICLES +import app.jerboa.spp.viewmodel.MAX_SCALE import app.jerboa.spp.viewmodel.MIN_LOG_AR import app.jerboa.spp.viewmodel.MIN_LOG_FADE import app.jerboa.spp.viewmodel.MIN_LOG_MASS import app.jerboa.spp.viewmodel.MIN_LOG_ORBIT import app.jerboa.spp.viewmodel.MIN_LOG_SPIN +import app.jerboa.spp.viewmodel.MIN_SCALE import app.jerboa.spp.viewmodel.ToyMenuViewModel import app.jerboa.spp.viewmodel.PARAM import app.jerboa.spp.viewmodel.PARTICLES_SLIDER_DEFAULT @@ -69,32 +72,31 @@ fun label( } @Composable -fun logSlider( +fun slider( v: Float, min: Float, max: Float, name: String, onChange: (Float) -> Unit, - width75Percent: Double + width75Percent: Double, + label: (Float) -> String = {v: Float -> "${round(10.0f.pow(v) * 100.0f) / 100.0f}"}, + postValue: (Float) -> Float = {v: Float -> 10.0f.pow(v)}, + steps: Int = 100 ) { var sliderValue by remember { - mutableFloatStateOf(log10(v)) + mutableFloatStateOf(v) } - label(text = "$name " + "${round(10.0f.pow(sliderValue) * 100.0f) / 100.0f}") + label(text = "$name " + label(sliderValue)) Slider( value = sliderValue, onValueChange = { sliderValue = it }, onValueChangeFinished = { - onChange( - 10.0f.pow( - sliderValue - ) - ) + onChange(postValue(sliderValue)) }, valueRange = min..max, - steps = 100, + steps = steps, modifier = Modifier .width(width75Percent.dp * 0.75f) .background(color = Color(1, 1, 1, 1)) @@ -104,8 +106,6 @@ fun logSlider( @Composable fun toyMenu( toyMenuViewModel: ToyMenuViewModel, - sppViewModel: SPPViewModel, - aboutViewModel: AboutViewModel, displayingMenu: Boolean, width75Percent: Double, height10Percent: Double, @@ -113,7 +113,6 @@ fun toyMenu( images: Map ) { - val playSuccess: Boolean by sppViewModel.playSuccess.observeAsState(initial = false) val particleNumber: Float by toyMenuViewModel.particleNumber.observeAsState(initial = PARTICLES_SLIDER_DEFAULT) val speed: Float by toyMenuViewModel.speed.observeAsState(initial = 1.0f) val attraction: Float by toyMenuViewModel.attractorStrength.observeAsState(50000f) @@ -122,16 +121,9 @@ fun toyMenu( val spin: Float by toyMenuViewModel.spinStrength.observeAsState(1500f) val mass: Float by toyMenuViewModel.mass.observeAsState(0.1f) val fade: Float by toyMenuViewModel.fade.observeAsState(initial = 1.0f) + val scale: Float by toyMenuViewModel.scale.observeAsState(initial = 3.0f) val showToys: Boolean by toyMenuViewModel.showToys.observeAsState(initial = false) - var particleSliderValue by remember { - mutableFloatStateOf(log10(particleNumber)) - } - - var fadeSliderValue by remember { - mutableFloatStateOf(log10(fade)) - } - var showToysValue by remember { mutableStateOf(showToys) } @@ -156,47 +148,6 @@ fun toyMenu( modifier = Modifier.fillMaxWidth(), ) { colourMapMenu(images, menuItemHeight) { toyMenuViewModel.onSelectColourMap(it) } - - Box( - modifier = Modifier - .fillMaxWidth() - .height((menuItemHeight).dp) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - IconButton(onClick = { sppViewModel.onRequestPlayServices() }) { - Image( - modifier = Modifier - .fillMaxHeight() - .size(menuItemHeight.dp) - .alpha( - if (playSuccess) { - 0.66f - } else { - 1f - } - ), - painter = painterResource(id = images["play-controller"]!!), - contentDescription = "play" - ) - } - IconButton(onClick = { aboutViewModel.onDisplayingAboutChanged(true) }) { - Image( - modifier = Modifier - .fillMaxHeight() - .size(menuItemHeight.dp) - .padding(2.dp), - painter = painterResource(id = images["about"]!!), - contentDescription = "Image" - ) - } - } - } Box( Modifier .width(width75Percent.dp) @@ -280,31 +231,31 @@ fun toyMenu( .verticalScroll(scroll) .verticalScrollBar(scroll, false) ) { - label(text = "Particles " + ceil(particleNumber * MAX_PARTICLES).toInt()) - Slider( - value = particleSliderValue, - onValueChange = { particleSliderValue = it }, - onValueChangeFinished = { - toyMenuViewModel.onParameterChanged( - Pair( - 10.0f.pow( - particleSliderValue - ), - PARAM.PARTICLES - ) - ) - }, - valueRange = -3.0f..0.0f, - steps = 100, - modifier = Modifier - .width(width75Percent.dp * 0.75f) - .background(color = Color(1, 1, 1, 1)) + slider( + log10(particleNumber), + -6.0f, + 0.0f, + "Particles", + {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.PARTICLES))}, + width75Percent, + label = {v: Float -> "${ceil(10.0f.pow(v) * MAX_PARTICLES).toInt()}"} + ) + Spacer(modifier = Modifier.size(8.dp)) + slider( + scale, + MIN_SCALE, + MAX_SCALE, + "Size", + {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.SCALE))}, + width75Percent, + label = {v: Float -> "$v"}, + postValue = {v: Float -> v} ) Spacer(modifier = Modifier.size(8.dp)) - logSlider(speed, -3.0f, MAX_LOG_SPEED, "Speed", {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.SPEED))}, width75Percent) + slider(log10(speed), -3.0f, MAX_LOG_SPEED, "Speed", {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.SPEED))}, width75Percent) Spacer(modifier = Modifier.size(8.dp)) - logSlider( - attraction, + slider( + log10(attraction), MIN_LOG_AR, MAX_LOG_AR, "Attraction", @@ -312,8 +263,8 @@ fun toyMenu( width75Percent ) Spacer(modifier = Modifier.size(8.dp)) - logSlider( - repulsion, + slider( + log10(repulsion), MIN_LOG_AR, MAX_LOG_AR, "Repulsion", @@ -321,29 +272,22 @@ fun toyMenu( width75Percent ) Spacer(modifier = Modifier.size(8.dp)) - logSlider(orbit, MIN_LOG_ORBIT, MAX_LOG_ORBIT, "Orbit", {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.ORBIT))}, width75Percent) + slider(log10(orbit), MIN_LOG_ORBIT, MAX_LOG_ORBIT, "Orbit", {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.ORBIT))}, width75Percent) Spacer(modifier = Modifier.size(8.dp)) - logSlider(spin, MIN_LOG_SPIN, MAX_LOG_SPIN, "Spin", {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.SPIN))}, width75Percent) + slider(log10(spin), MIN_LOG_SPIN, MAX_LOG_SPIN, "Spin", {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.SPIN))}, width75Percent) Spacer(modifier = Modifier.size(8.dp)) - logSlider(mass, MIN_LOG_MASS, MAX_LOG_MASS, "Mass", {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.MASS))}, width75Percent) + slider(log10(mass), MIN_LOG_MASS, MAX_LOG_MASS, "Mass", {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.MASS))}, width75Percent) Spacer(modifier = Modifier.size(8.dp)) - label(text = "Tracing " + "${round(fadeSliderValue * 100.0f) / 100.0f}") - Slider( - value = fadeSliderValue, - onValueChange = { fadeSliderValue = it }, - onValueChangeFinished = { - toyMenuViewModel.onParameterChanged( - Pair( - 10.0f.pow((1.0f - fadeSliderValue) * (MAX_LOG_FADE - MIN_LOG_FADE) + MIN_LOG_FADE), - PARAM.FADE - ) - ) - }, - valueRange = 0.0f..1.0f, - steps = 100, - modifier = Modifier - .width(width75Percent.dp * 0.75f) - .background(color = Color(1, 1, 1, 1)) + Log.d("fade", "$fade") + slider( + fade, + 10.0f.pow(MIN_LOG_FADE), + 10.0f.pow(MAX_LOG_FADE), + "Tracing", + {toyMenuViewModel.onParameterChanged(Pair(it, PARAM.FADE))}, + width75Percent, + postValue = {v: Float -> v}, + label = {v: Float -> "${(v-10.0f.pow(MIN_LOG_FADE))/(10.0f.pow(MAX_LOG_FADE)-10.0f.pow(MIN_LOG_FADE))}"} ) Spacer(modifier = Modifier.size(8.dp)) label(text = "Show Toys") diff --git a/app/src/main/java/app/jerboa/spp/data/fade.kt b/app/src/main/java/app/jerboa/spp/data/fade.kt index 3b93096..3cd0f21 100644 --- a/app/src/main/java/app/jerboa/spp/data/fade.kt +++ b/app/src/main/java/app/jerboa/spp/data/fade.kt @@ -11,6 +11,6 @@ data class FadeShaderData( "uniform float fadeRate;\n"+ "out vec4 colour;\n"+ "void main(void){\n"+ - " colour = vec4(0.0,0.0,0.0,fadeRate);\n"+ + " colour = vec4(0.0,0.0,0.0,1.0-fadeRate);\n"+ "}") : ShaderData(vertexShader,fragmentShader) \ No newline at end of file diff --git a/app/src/main/java/app/jerboa/spp/data/particleShaders.kt b/app/src/main/java/app/jerboa/spp/data/particleShaders.kt index c2f5cdf..fa41720 100644 --- a/app/src/main/java/app/jerboa/spp/data/particleShaders.kt +++ b/app/src/main/java/app/jerboa/spp/data/particleShaders.kt @@ -91,9 +91,9 @@ data class ParticleDrawShaderData( "out vec4 colour;\n"+ "void main(void){\n"+ "vec2 circCoord = 2.0 * gl_PointCoord - 1.0;"+ - //"float dd = length(circCoord);\n"+ - //"float alpha = 1.0-smoothstep(0.9,1.1,dd);\n"+ - "colour = vec4(o_colour.rgb,transitionAlpha);\n"+ + "float dd = length(circCoord);\n"+ + "float alpha = 1.0-smoothstep(0.9,1.1,dd);\n"+ + "colour = vec4(o_colour.rgb,alpha*transitionAlpha);\n"+ "if (colour.a == 0.0){discard;}}") : ShaderData(vertexShader,fragmentShader) diff --git a/app/src/main/java/app/jerboa/spp/ui/view/SPPRenderer.kt b/app/src/main/java/app/jerboa/spp/ui/view/SPPRenderer.kt index a877377..375752d 100644 --- a/app/src/main/java/app/jerboa/spp/ui/view/SPPRenderer.kt +++ b/app/src/main/java/app/jerboa/spp/ui/view/SPPRenderer.kt @@ -81,7 +81,7 @@ class SPPRenderer( private var generatedParticles = 0 private var generating: Boolean = false // particle scale, gl_PointSize - private val scale = 3f + private var scale = 3f private val projection: FloatArray = FloatArray(16){0f} private val invProjection: FloatArray = FloatArray(16){0f} @@ -302,6 +302,8 @@ class SPPRenderer( fun setOrbit(v: Float) { orbitStrength = v } fun setShowToys(v: Boolean){ showToys = v } + fun setScale(s: Float) { scale = s; recompileDrawShader = true } + fun clearToys() { attractors.clear() repellors.clear() diff --git a/app/src/main/java/app/jerboa/spp/ui/view/SPPView.kt b/app/src/main/java/app/jerboa/spp/ui/view/SPPView.kt index d25c96b..b1056e8 100644 --- a/app/src/main/java/app/jerboa/spp/ui/view/SPPView.kt +++ b/app/src/main/java/app/jerboa/spp/ui/view/SPPView.kt @@ -65,6 +65,7 @@ class SPPView ( fun setOrbit(v: Float) { renderer.setOrbit(v) } fun showToys(v: Boolean){ renderer.setShowToys(v) } fun clearToys() { renderer.clearToys() } + fun setScale(s: Float) { renderer.setScale(s) } override fun onResume() { super.onResume() diff --git a/app/src/main/java/app/jerboa/spp/viewmodel/mainMenuViewModel.kt b/app/src/main/java/app/jerboa/spp/viewmodel/toyMenuViewModel.kt similarity index 91% rename from app/src/main/java/app/jerboa/spp/viewmodel/mainMenuViewModel.kt rename to app/src/main/java/app/jerboa/spp/viewmodel/toyMenuViewModel.kt index 7248c54..6991eaa 100644 --- a/app/src/main/java/app/jerboa/spp/viewmodel/mainMenuViewModel.kt +++ b/app/src/main/java/app/jerboa/spp/viewmodel/toyMenuViewModel.kt @@ -3,12 +3,13 @@ package app.jerboa.spp.viewmodel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import java.util.Calendar +import kotlin.math.pow const val MAX_PARTICLES = 500000f const val PARTICLES_SLIDER_DEFAULT = 100000f/ MAX_PARTICLES const val MAX_LOG_SPEED = 0.30103f -const val MAX_LOG_FADE = 0.0f -const val MIN_LOG_FADE = -2.0f +const val MAX_LOG_FADE = -0.0013648f +const val MIN_LOG_FADE = -0.30103f const val MIN_LOG_MASS = -3f const val MAX_LOG_MASS = 0.5f const val MIN_LOG_AR = 1f @@ -17,6 +18,8 @@ const val MAX_LOG_ORBIT = 0.30103f const val MIN_LOG_ORBIT = -1f const val MAX_LOG_SPIN = 4.0f const val MIN_LOG_SPIN = 2f +const val MIN_SCALE = 2f +const val MAX_SCALE = 10f enum class COLOUR_MAP { R1, @@ -31,7 +34,7 @@ enum class COLOUR_MAP { enum class TOY {ATTRACTOR,REPELLOR,SPINNER,FREEZER,ORBITER, NOTHING} -enum class PARAM {MASS, SPEED, ATTRACTION, REPULSION, ORBIT, SPIN, PARTICLES, FADE} +enum class PARAM {MASS, SPEED, ATTRACTION, REPULSION, ORBIT, SPIN, PARTICLES, FADE, SCALE} class ToyMenuViewModel: ViewModel() { @@ -80,7 +83,7 @@ class ToyMenuViewModel: ViewModel() { private val _speed = MutableLiveData(1.0f) val speed: MutableLiveData = _speed - private val _fade = MutableLiveData(1.0f) + private val _fade = MutableLiveData(10.0f.pow(MIN_LOG_FADE)) val fade: MutableLiveData = _fade private val _attractorStrength = MutableLiveData(50000.0f) @@ -98,6 +101,9 @@ class ToyMenuViewModel: ViewModel() { private val _spin = MutableLiveData(1500f) val spinStrength: MutableLiveData = _spin + private val _scale = MutableLiveData(3f) + val scale: MutableLiveData = _scale + fun onParameterChanged(v: Pair){ when (v.second) { PARAM.PARTICLES -> { @@ -124,6 +130,9 @@ class ToyMenuViewModel: ViewModel() { PARAM.SPIN -> { _spin.value = v.first } + PARAM.SCALE -> { + _scale.value = v.first + } } }