diff --git a/androidApp/src/screenshotTest/kotlin/org/neotech/app/abysner/presentation/screens/ShareImageScreenshotTest.kt b/androidApp/src/screenshotTest/kotlin/org/neotech/app/abysner/presentation/screens/ShareImageScreenshotTest.kt index 028024d..60e2e14 100644 --- a/androidApp/src/screenshotTest/kotlin/org/neotech/app/abysner/presentation/screens/ShareImageScreenshotTest.kt +++ b/androidApp/src/screenshotTest/kotlin/org/neotech/app/abysner/presentation/screens/ShareImageScreenshotTest.kt @@ -15,7 +15,6 @@ package org.neotech.app.abysner.presentation.screens import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import com.android.tools.screenshot.PreviewTest -import org.neotech.app.abysner.presentation.preview.DEVICE_PHONE_MAX_HEIGHT @PreviewTest @Preview(device = DEVICE_SHARE_IMAGE) diff --git a/buildSrc/src/main/kotlin/org/neotech/gradle/GradleUtilities.kt b/buildSrc/src/main/kotlin/org/neotech/gradle/GradleUtilities.kt deleted file mode 100644 index e5010f0..0000000 --- a/buildSrc/src/main/kotlin/org/neotech/gradle/GradleUtilities.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Abysner - Dive planner - * Copyright (C) 2024-2026 Neotech - * - * Abysner is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License version 3, - * as published by the Free Software Foundation. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see https://www.gnu.org/licenses/. - */ - -package org.neotech.gradle - -import java.util.Locale - -fun String.capitalizeFirstCharacter(): String { - return if (isEmpty()) { - this - } else { - this[0].titlecase(Locale.getDefault()) + substring(1) - } -} diff --git a/buildSrc/src/main/kotlin/org/neotech/plugin/ScreenshotTestCoveragePlugin.kt b/buildSrc/src/main/kotlin/org/neotech/plugin/ScreenshotTestCoveragePlugin.kt index 6decb62..9238bf7 100644 --- a/buildSrc/src/main/kotlin/org/neotech/plugin/ScreenshotTestCoveragePlugin.kt +++ b/buildSrc/src/main/kotlin/org/neotech/plugin/ScreenshotTestCoveragePlugin.kt @@ -121,9 +121,9 @@ class ScreenshotTestCoveragePlugin : Plugin { val classFiles = listOf( "com/android/tools/screenshot/renderer/Renderer.class", - "com/android/tools/screenshot/renderer/Renderer\$copyObject\$objectIn\$1.class", + $$"com/android/tools/screenshot/renderer/Renderer$copyObject$objectIn$1.class", "com/android/tools/screenshot/renderer/RendererKt.class", - "com/android/tools/screenshot/renderer/RendererKt\$createResourceEnhancedClassLoader\$1.class", + $$"com/android/tools/screenshot/renderer/RendererKt$createResourceEnhancedClassLoader$1.class", ) for (classFile in classFiles) { diff --git a/composeApp/src/commonMain/kotlin/androidx/compose/material3/AlertDialogCustomContent.kt b/composeApp/src/commonMain/kotlin/androidx/compose/material3/AlertDialogCustomContent.kt index 303e875..bf9f37d 100644 --- a/composeApp/src/commonMain/kotlin/androidx/compose/material3/AlertDialogCustomContent.kt +++ b/composeApp/src/commonMain/kotlin/androidx/compose/material3/AlertDialogCustomContent.kt @@ -192,11 +192,7 @@ private fun AlertDialogFlowRowCopy( val crossAxisLayoutSize = max(crossAxisSpace, constraints.minHeight) - val layoutWidth = mainAxisLayoutSize - - val layoutHeight = crossAxisLayoutSize - - layout(layoutWidth, layoutHeight) { + layout(mainAxisLayoutSize, crossAxisLayoutSize) { sequences.fastForEachIndexed { i, placeables -> val childrenMainAxisSizes = IntArray(placeables.size) { j -> placeables[j].width + diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/App.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/App.kt index cef8dfe..d94e365 100755 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/App.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/App.kt @@ -13,11 +13,9 @@ package org.neotech.app.abysner import androidx.compose.runtime.Composable -import androidx.compose.ui.tooling.preview.Preview import org.neotech.app.abysner.di.AppComponent @Composable -@Preview fun App(appComponent: AppComponent) { appComponent.mainNavController() } diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/AnimationExtensions.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/AnimationExtensions.kt index b3875f2..6116f93 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/AnimationExtensions.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/AnimationExtensions.kt @@ -18,7 +18,7 @@ import androidx.compose.animation.core.DurationBasedAnimationSpec import androidx.compose.animation.core.TwoWayConverter import androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec -fun none(): AnimationSpec = AnimationSpecNone() +fun none(): AnimationSpec = AnimationSpecNone() class AnimationSpecNone: DurationBasedAnimationSpec { diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/AnnotatedStringExtensions.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/AnnotatedStringExtensions.kt index 701cf5e..07f1073 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/AnnotatedStringExtensions.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/AnnotatedStringExtensions.kt @@ -13,7 +13,6 @@ package org.neotech.app.abysner.presentation.component import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.AnnotatedString.Builder import androidx.compose.ui.text.ParagraphStyle import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.font.FontWeight @@ -21,7 +20,7 @@ import androidx.compose.ui.text.style.TextIndent import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.sp -internal fun Builder.appendBold( +internal fun AnnotatedString.Builder.appendBold( text: String, ) { withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { @@ -29,7 +28,7 @@ internal fun Builder.appendBold( } } -internal fun Builder.appendBoldLine( +internal fun AnnotatedString.Builder.appendBoldLine( text: String, ) { withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { @@ -46,9 +45,5 @@ internal inline fun AnnotatedString.Builder.appendBulletPoint(crossinli } internal fun CharSequence.toAnnotatedString(): AnnotatedString { - return if(this is AnnotatedString) { - return this - } else { - AnnotatedString(this.toString()) - } + return this as? AnnotatedString ?: AnnotatedString(this.toString()) } diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/CheckableListComponent.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/CheckableListComponent.kt deleted file mode 100644 index 20e42e8..0000000 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/CheckableListComponent.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Abysner - Dive planner - * Copyright (C) 2024 Neotech - * - * Abysner is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License version 3, - * as published by the Free Software Foundation. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see https://www.gnu.org/licenses/. - */ - -package org.neotech.app.abysner.presentation.component - -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Checkbox -import androidx.compose.material3.LocalMinimumInteractiveComponentSize -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp - -@Composable -fun CheckableListItemComponent( - modifier: Modifier = Modifier, - checked: Boolean = true, - enabled: Boolean = true, - onCheckedChanged: (isChecked: Boolean) -> Unit = {}, - content: @Composable RowScope.() -> Unit, -) { - Row( - modifier = modifier.padding(start = 16.dp), - verticalAlignment = Alignment.CenterVertically - ) { - CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) { - Checkbox( - checked = checked, - enabled = enabled, - onCheckedChange = onCheckedChanged - ) - } - content() - } -} diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/CylinderSizeField.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/CylinderSizeField.kt index 7dd888c..af2cb2d 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/CylinderSizeField.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/CylinderSizeField.kt @@ -58,8 +58,10 @@ import org.neotech.app.abysner.domain.core.model.Cylinder import org.neotech.app.abysner.domain.core.model.Gas import org.neotech.app.abysner.domain.core.model.UnitSystem import org.neotech.app.abysner.domain.core.physics.GasEquationOfStateModel -import org.neotech.app.abysner.domain.core.physics.LITERS_PER_CUBIC_FOOT -import org.neotech.app.abysner.domain.core.physics.PSI_PER_BAR +import org.neotech.app.abysner.domain.core.physics.asBarToPsi +import org.neotech.app.abysner.domain.core.physics.asCubicFeetToLiters +import org.neotech.app.abysner.domain.core.physics.asLitersToCubicFeet +import org.neotech.app.abysner.domain.core.physics.asPsiToBar import org.neotech.app.abysner.domain.utilities.DecimalFormat import org.neotech.app.abysner.presentation.component.list.LazyColumnWithScrollIndicators import org.neotech.app.abysner.presentation.component.textfield.OutlinedDecimalInputField @@ -81,7 +83,7 @@ fun CylinderSizeField( val displayText = when (unitSystem) { UnitSystem.METRIC -> DecimalFormat.format(1, cylinderSize.waterVolume) - UnitSystem.IMPERIAL -> (cylinderSize.ratedCapacity() / LITERS_PER_CUBIC_FOOT).roundToInt().toString() + UnitSystem.IMPERIAL -> cylinderSize.ratedCapacity().asLitersToCubicFeet().roundToInt().toString() } OutlinedTextField( @@ -143,7 +145,7 @@ private fun CylinderSizeDialog( mutableStateOf( when (unitSystem) { UnitSystem.METRIC -> currentCylinderSize.waterVolume - UnitSystem.IMPERIAL -> currentCylinderSize.ratedCapacity() / LITERS_PER_CUBIC_FOOT + UnitSystem.IMPERIAL -> currentCylinderSize.ratedCapacity().asLitersToCubicFeet() } ) } @@ -151,7 +153,7 @@ private fun CylinderSizeDialog( mutableStateOf( when (unitSystem) { UnitSystem.METRIC -> currentCylinderSize.workingPressure - UnitSystem.IMPERIAL -> currentCylinderSize.workingPressure * PSI_PER_BAR + UnitSystem.IMPERIAL -> currentCylinderSize.workingPressure.asBarToPsi() } ) } @@ -177,8 +179,8 @@ private fun CylinderSizeDialog( workingPressure = customPressure!!, ) UnitSystem.IMPERIAL -> { - val pressureBar = customPressure!! / PSI_PER_BAR - val targetCapacityLiters = customVolume!! * LITERS_PER_CUBIC_FOOT + val pressureBar = customPressure!!.asPsiToBar() + val targetCapacityLiters = customVolume!!.asCubicFeetToLiters() val gasVolumePerLiter = GasEquationOfStateModel.Default.getGasVolume(Gas.Air, 1.0, pressureBar) Cylinder.Size( waterVolume = targetCapacityLiters / gasVolumePerLiter, @@ -300,14 +302,14 @@ private fun PresetsTankSizeTab( label = when (unitSystem) { UnitSystem.METRIC -> null UnitSystem.IMPERIAL -> { - val pressurePsi = (preset.workingPressure * PSI_PER_BAR).roundToInt() + val pressurePsi = preset.workingPressure.asBarToPsi().roundToInt() "$pressurePsi ${unitSystem.pressureUnitLabel}" } }, value = when (unitSystem) { UnitSystem.METRIC -> "${DecimalFormat.format(1, preset.waterVolume)} ${unitSystem.volumeUnitLabel}" UnitSystem.IMPERIAL -> { - val capacityCuFt = (preset.ratedCapacity() / LITERS_PER_CUBIC_FOOT).roundToInt() + val capacityCuFt = preset.ratedCapacity().asLitersToCubicFeet().roundToInt() "$capacityCuFt ${unitSystem.volumeUnitLabel}" } }, diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/MultiChoiceSegmentedButtonRowComponent.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/MultiChoiceSegmentedButtonRowComponent.kt index 86477df..1cf295f 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/MultiChoiceSegmentedButtonRowComponent.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/MultiChoiceSegmentedButtonRowComponent.kt @@ -12,20 +12,18 @@ package org.neotech.app.abysner.presentation.component -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.material3.SegmentedButton import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList @Stable class MultiChoiceSegmentedButtonRowState(initialCheckedItemIndexes: ImmutableList) { diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/bottomsheet/BottomSheetButtonRow.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/bottomsheet/BottomSheetButtonRow.kt deleted file mode 100644 index f9d1b1c..0000000 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/bottomsheet/BottomSheetButtonRow.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Abysner - Dive planner - * Copyright (C) 2026 Neotech - * - * Abysner is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License version 3, - * as published by the Free Software Foundation. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see https://www.gnu.org/licenses/. - */ - -package org.neotech.app.abysner.presentation.component.bottomsheet - -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier - -@Composable -fun BottomSheetButtonRow( - modifier: Modifier = Modifier, - primaryLabel: String, - primaryEnabled: Boolean = true, - secondaryLabel: String? = null, - secondaryEnabled: Boolean = true, - onSecondary: () -> Unit = {}, - onPrimary: () -> Unit, -) { - Row(modifier = modifier) { - if (secondaryLabel != null) { - TextButton( - enabled = secondaryEnabled, - modifier = Modifier.weight(1f), - onClick = onSecondary, - ) { - Text(secondaryLabel) - } - } else { - Spacer(modifier = Modifier.weight(1f)) - } - Button( - enabled = primaryEnabled, - modifier = Modifier.weight(1f), - onClick = onPrimary, - ) { - Text(primaryLabel) - } - } -} diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/core/PaddingValuesExtensions.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/core/PaddingValuesExtensions.kt index d7a80b1..2ae3887 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/core/PaddingValuesExtensions.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/core/PaddingValuesExtensions.kt @@ -19,13 +19,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.LayoutDirection -@Composable -fun PaddingValues.withoutBottom(layoutDirection: LayoutDirection = LocalLayoutDirection.current): PaddingValues = PaddingValues( - start = calculateStartPadding(layoutDirection), - end = calculateEndPadding(layoutDirection), - top = calculateTopPadding() -) - fun PaddingValues.onlyBottom(): PaddingValues = PaddingValues( bottom = calculateBottomPadding() ) diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/DecimalNumberPreference.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/DecimalNumberPreference.kt index a6aec24..22ee540 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/DecimalNumberPreference.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/DecimalNumberPreference.kt @@ -12,11 +12,9 @@ package org.neotech.app.abysner.presentation.component.preferences -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.AlertDialogCustomContent -import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -27,7 +25,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/GradientFactorPreference.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/GradientFactorPreference.kt index bf30c1a..405ca4a 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/GradientFactorPreference.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/GradientFactorPreference.kt @@ -12,12 +12,10 @@ package org.neotech.app.abysner.presentation.component.preferences -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.material3.AlertDialogCustomContent -import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -27,7 +25,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.neotech.app.abysner.presentation.component.textfield.OutlinedNumberInputField diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/NumberPreference.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/NumberPreference.kt index 52cb1d1..45f85ed 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/NumberPreference.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/NumberPreference.kt @@ -12,11 +12,9 @@ package org.neotech.app.abysner.presentation.component.preferences -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.AlertDialogCustomContent -import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -28,10 +26,9 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.unit.dp import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import org.neotech.app.abysner.presentation.component.textfield.OutlinedNumberInputField import org.neotech.app.abysner.presentation.component.textfield.SuffixVisualTransformation import org.neotech.app.abysner.presentation.theme.AbysnerTheme diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/SingleChoicePreference.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/SingleChoicePreference.kt index 5ebb1a6..4604cb4 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/SingleChoicePreference.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/preferences/SingleChoicePreference.kt @@ -32,11 +32,10 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList -import androidx.compose.ui.tooling.preview.Preview import org.neotech.app.abysner.domain.core.model.Salinity import org.neotech.app.abysner.presentation.component.list.LazyColumnWithScrollIndicators import org.neotech.app.abysner.presentation.component.toAnnotatedString diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/textfield/ExpandableText.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/textfield/ExpandableText.kt index 2c2ac46..6922459 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/textfield/ExpandableText.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/component/textfield/ExpandableText.kt @@ -37,7 +37,6 @@ import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/DiveConfigurationScreen.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/DiveConfigurationScreen.kt index f0dd112..eeeb14b 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/DiveConfigurationScreen.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/DiveConfigurationScreen.kt @@ -44,7 +44,8 @@ import kotlinx.collections.immutable.toImmutableList import org.neotech.app.abysner.domain.core.model.Configuration import org.neotech.app.abysner.domain.core.model.Salinity import org.neotech.app.abysner.domain.core.model.UnitSystem -import org.neotech.app.abysner.domain.core.physics.LITERS_PER_CUBIC_FOOT +import org.neotech.app.abysner.domain.core.physics.asCubicFeetToLiters +import org.neotech.app.abysner.domain.core.physics.asLitersToCubicFeet import org.neotech.app.abysner.domain.core.physics.METERS_PER_FOOT import org.neotech.app.abysner.domain.diveplanning.PlanningRepository import org.neotech.app.abysner.domain.settings.SettingsRepository @@ -238,7 +239,7 @@ fun DiveConfigurationScreen( description = "The average amount of gas the diver is breathing per minute at 1 atmosphere during normal diving conditions. This is also known as SAC or RMV rate.", initialValue = when (unitSystem) { UnitSystem.METRIC -> configuration.sacRate - UnitSystem.IMPERIAL -> configuration.sacRate / LITERS_PER_CUBIC_FOOT + UnitSystem.IMPERIAL -> configuration.sacRate.asLitersToCubicFeet() }, minValue = if (unitSystem == UnitSystem.IMPERIAL) { 0.3 } else { 5.0 }, maxValue = if (unitSystem == UnitSystem.IMPERIAL) { 3.5 } else { 99.0 }, @@ -248,7 +249,7 @@ fun DiveConfigurationScreen( ) { sacRate -> val liters = when (unitSystem) { UnitSystem.METRIC -> sacRate - UnitSystem.IMPERIAL -> sacRate * LITERS_PER_CUBIC_FOOT + UnitSystem.IMPERIAL -> sacRate.asCubicFeetToLiters() } updateConfiguration { it.copy(sacRate = liters) } } @@ -258,7 +259,7 @@ fun DiveConfigurationScreen( description = "The average amount of gas a diver is breathing per minute at 1 atmosphere during an emergency scenario. This is also known as the panic SAC or RMV rate.", initialValue = when (unitSystem) { UnitSystem.METRIC -> configuration.sacRateOutOfAir - UnitSystem.IMPERIAL -> configuration.sacRateOutOfAir / LITERS_PER_CUBIC_FOOT + UnitSystem.IMPERIAL -> configuration.sacRateOutOfAir.asLitersToCubicFeet() }, minValue = if (unitSystem == UnitSystem.IMPERIAL) { 0.3 } else { 5.0 }, maxValue = if (unitSystem == UnitSystem.IMPERIAL) { 3.5 } else { 99.0 }, @@ -268,7 +269,7 @@ fun DiveConfigurationScreen( ) { sacRate -> val liters = when (unitSystem) { UnitSystem.METRIC -> sacRate - UnitSystem.IMPERIAL -> sacRate * LITERS_PER_CUBIC_FOOT + UnitSystem.IMPERIAL -> sacRate.asCubicFeetToLiters() } updateConfiguration { it.copy(sacRateOutOfAir = liters) } } diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/PlanScreenContent.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/PlanScreenContent.kt index 652a069..fffa839 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/PlanScreenContent.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/PlanScreenContent.kt @@ -18,7 +18,6 @@ import androidx.compose.animation.fadeOut import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.runtime.Composable @@ -26,7 +25,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import org.neotech.app.abysner.domain.core.model.Cylinder -import org.neotech.app.abysner.domain.core.model.DiveMode import org.neotech.app.abysner.presentation.screens.planner.cylinders.CylinderSelectionCardComponent import org.neotech.app.abysner.presentation.screens.planner.decoplan.DecoPlanCardComponent import org.neotech.app.abysner.presentation.screens.planner.gasplan.GasPlanCardComponent diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/cylinders/CylinderPickerBottomSheet.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/cylinders/CylinderPickerBottomSheet.kt index 855bef7..1b639b5 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/cylinders/CylinderPickerBottomSheet.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/cylinders/CylinderPickerBottomSheet.kt @@ -50,7 +50,8 @@ import org.neotech.app.abysner.domain.core.model.Cylinder import org.neotech.app.abysner.domain.core.model.Environment import org.neotech.app.abysner.domain.core.model.Gas import org.neotech.app.abysner.domain.core.model.UnitSystem -import org.neotech.app.abysner.domain.core.physics.PSI_PER_BAR +import org.neotech.app.abysner.domain.core.physics.asBarToPsi +import org.neotech.app.abysner.domain.core.physics.asPsiToBar import org.neotech.app.abysner.domain.diveplanning.model.PlannedCylinderModel import org.neotech.app.abysner.presentation.utilities.ModalTarget import org.neotech.app.abysner.presentation.component.GasPickerComponent @@ -194,7 +195,7 @@ private fun CylinderPickerBottomSheetContent( var startPressure: Double? by remember(initialValue) { mutableStateOf(when (unitSystem) { UnitSystem.METRIC -> initialValue.pressure - UnitSystem.IMPERIAL -> initialValue.pressure * PSI_PER_BAR + UnitSystem.IMPERIAL -> initialValue.pressure.asBarToPsi() }) } val isStartPressureValid = remember { mutableStateOf(true) } @@ -221,7 +222,7 @@ private fun CylinderPickerBottomSheetContent( onPrimary = { val pressureBar = when (unitSystem) { UnitSystem.METRIC -> startPressure!! - UnitSystem.IMPERIAL -> startPressure!! / PSI_PER_BAR + UnitSystem.IMPERIAL -> startPressure!!.asPsiToBar() } onAddOrUpdateCylinder( // Copy since we want to maintain the uniqueIdentifier @@ -263,7 +264,7 @@ private fun CylinderPickerBottomSheetContent( workingPressure = selectedSize.workingPressure startPressure = when (unitSystem) { UnitSystem.METRIC -> selectedSize.workingPressure - UnitSystem.IMPERIAL -> selectedSize.workingPressure * PSI_PER_BAR + UnitSystem.IMPERIAL -> selectedSize.workingPressure.asBarToPsi() } }, ) diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/cylinders/CylindersCard.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/cylinders/CylindersCard.kt index cdb9aad..348b783 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/cylinders/CylindersCard.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/cylinders/CylindersCard.kt @@ -55,7 +55,7 @@ import org.neotech.app.abysner.domain.core.model.Cylinder import org.neotech.app.abysner.domain.core.model.DiveMode import org.neotech.app.abysner.domain.core.model.Gas import org.neotech.app.abysner.domain.core.model.UnitSystem -import org.neotech.app.abysner.domain.core.physics.LITERS_PER_CUBIC_FOOT +import org.neotech.app.abysner.domain.core.physics.asLitersToCubicFeet import org.neotech.app.abysner.domain.diveplanning.model.CylinderRole import org.neotech.app.abysner.domain.diveplanning.model.PlannedCylinderModel import org.neotech.app.abysner.presentation.component.IconAndTextButton @@ -258,7 +258,7 @@ fun CylinderListItemComponent( val capacityDisplay = when (unitSystem) { UnitSystem.METRIC -> cylinder.waterVolume.formatVolume(unitSystem, decimals = 1) UnitSystem.IMPERIAL -> { - val ratedCapacityCuFt = cylinder.size.ratedCapacity() / LITERS_PER_CUBIC_FOOT + val ratedCapacityCuFt = cylinder.size.ratedCapacity().asLitersToCubicFeet() "${ratedCapacityCuFt.roundToInt()} ${unitSystem.volumeUnitLabel}" } } diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/gasplan/GasPlanCard.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/gasplan/GasPlanCard.kt index 245bc58..5810ae6 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/gasplan/GasPlanCard.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/gasplan/GasPlanCard.kt @@ -34,7 +34,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.neotech.app.abysner.domain.core.model.Gas import org.neotech.app.abysner.domain.core.model.UnitSystem -import org.neotech.app.abysner.domain.core.physics.LITERS_PER_CUBIC_FOOT +import org.neotech.app.abysner.domain.core.physics.asLitersToCubicFeet import org.neotech.app.abysner.domain.diveplanning.model.DivePlanSet import org.neotech.app.abysner.domain.gasplanning.model.CylinderGasRequirements import org.neotech.app.abysner.domain.utilities.DecimalFormat @@ -280,7 +280,7 @@ fun CylindersTable( modifier = Modifier.weight(0.3f), text = when (unitSystem) { UnitSystem.METRIC -> usage.cylinder.waterVolume.formatVolume(unitSystem, decimals = 1, unit = noUnitLabel) - UnitSystem.IMPERIAL -> (usage.cylinder.size.ratedCapacity() / LITERS_PER_CUBIC_FOOT).roundToInt().toString() + UnitSystem.IMPERIAL -> usage.cylinder.size.ratedCapacity().asLitersToCubicFeet().roundToInt().toString() }, ) diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/gasplan/GasUsageDetailsDialog.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/gasplan/GasUsageDetailsDialog.kt index b10827c..a962b30 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/gasplan/GasUsageDetailsDialog.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/screens/planner/gasplan/GasUsageDetailsDialog.kt @@ -45,7 +45,7 @@ import org.jetbrains.compose.resources.painterResource import org.neotech.app.abysner.domain.core.model.Cylinder import org.neotech.app.abysner.domain.core.model.Gas import org.neotech.app.abysner.domain.core.model.UnitSystem -import org.neotech.app.abysner.domain.core.physics.LITERS_PER_CUBIC_FOOT +import org.neotech.app.abysner.domain.core.physics.asLitersToCubicFeet import org.neotech.app.abysner.domain.gasplanning.model.CylinderGasRequirements import org.neotech.app.abysner.domain.gasplanning.model.GasPlan import org.neotech.app.abysner.presentation.component.AlertSeverity @@ -118,7 +118,7 @@ fun GasUsageDetailsDialog( ) Text( modifier = Modifier.weight(1f), - text = "${(cylinderGasRequirements.cylinder.size.ratedCapacity() / LITERS_PER_CUBIC_FOOT).roundToInt()} ${unitSystem.volumeUnitLabel}" + text = "${cylinderGasRequirements.cylinder.size.ratedCapacity().asLitersToCubicFeet().roundToInt()} ${unitSystem.volumeUnitLabel}" ) } } diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/theme/Color.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/theme/Color.kt index 3fe24f4..a8516d3 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/theme/Color.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/theme/Color.kt @@ -104,10 +104,12 @@ data class CustomColors( val onWarning: Color = onWarningLight, ) +@Suppress("UnusedReceiverParameter") val ColorScheme.warning: Color @Composable get() = LocalCustomColors.current.warning +@Suppress("UnusedReceiverParameter") val ColorScheme.onWarning: Color @Composable get() = LocalCustomColors.current.onWarning diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/theme/Type.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/theme/Type.kt index 7a2dbed..04893bd 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/theme/Type.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/theme/Type.kt @@ -94,6 +94,7 @@ data class CustomTypography( val bodyExtraLarge: TextStyle = baseline.bodyLarge.copy(fontSize = 24.sp), ) +@Suppress("UnusedReceiverParameter") val Typography.bodyExtraLarge: TextStyle @Composable get() = LocalCustomTypography.current.bodyExtraLarge diff --git a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/utilities/UnitFormatter.kt b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/utilities/UnitFormatter.kt index 50ab521..d1aa74f 100644 --- a/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/utilities/UnitFormatter.kt +++ b/composeApp/src/commonMain/kotlin/org/neotech/app/abysner/presentation/utilities/UnitFormatter.kt @@ -13,8 +13,8 @@ package org.neotech.app.abysner.presentation.utilities import org.neotech.app.abysner.domain.core.model.UnitSystem -import org.neotech.app.abysner.domain.core.physics.LITERS_PER_CUBIC_FOOT -import org.neotech.app.abysner.domain.core.physics.PSI_PER_BAR +import org.neotech.app.abysner.domain.core.physics.asBarToPsi +import org.neotech.app.abysner.domain.core.physics.asLitersToCubicFeet import org.neotech.app.abysner.domain.utilities.DecimalFormat import kotlin.math.roundToInt @@ -73,7 +73,7 @@ fun Double.formatDepth(unitSystem: UnitSystem, includeUnit: Boolean = true): Str fun Double.formatPressure(unitSystem: UnitSystem, includeUnit: Boolean = true): String { val value = when (unitSystem) { UnitSystem.METRIC -> roundToInt() - UnitSystem.IMPERIAL -> (this * PSI_PER_BAR).roundToInt() + UnitSystem.IMPERIAL -> this.asBarToPsi().roundToInt() } return if (includeUnit) { "$value ${unitSystem.pressureUnitLabel}" } else { value.toString() } } @@ -85,7 +85,7 @@ fun Double.formatPressure(unitSystem: UnitSystem, includeUnit: Boolean = true): fun Double.formatVolume(unitSystem: UnitSystem, decimals: Int = 0, unit: String = unitSystem.volumeUnitLabel): String { val value = when (unitSystem) { UnitSystem.METRIC -> DecimalFormat.format(decimals, this) - UnitSystem.IMPERIAL -> DecimalFormat.format(decimals, this / LITERS_PER_CUBIC_FOOT) + UnitSystem.IMPERIAL -> DecimalFormat.format(decimals, this.asLitersToCubicFeet()) } return if (unit.isNotEmpty()) { "$value $unit" } else { value } } diff --git a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/model/BreathingMode.kt b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/model/BreathingMode.kt index e33c6a5..18abd7c 100644 --- a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/model/BreathingMode.kt +++ b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/model/BreathingMode.kt @@ -44,7 +44,7 @@ sealed class BreathingMode { fun ccr(setpoint: Double) = ClosedCircuit(setpoint) - fun oc() = BreathingMode.OpenCircuit + fun oc() = OpenCircuit } } diff --git a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/model/DiveMode.kt b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/model/DiveMode.kt index 36b95c0..89915ba 100644 --- a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/model/DiveMode.kt +++ b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/model/DiveMode.kt @@ -14,11 +14,11 @@ package org.neotech.app.abysner.domain.core.model import org.neotech.app.abysner.domain.persistence.EnumPreference -enum class DiveMode(val humanReadableName: String) : EnumPreference { - OPEN_CIRCUIT("Open circuit") { +enum class DiveMode : EnumPreference { + OPEN_CIRCUIT { override val preferenceValue: String = "open-circuit" }, - CLOSED_CIRCUIT("CCR") { + CLOSED_CIRCUIT { override val preferenceValue: String = "closed-circuit" }; diff --git a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/model/Gas.kt b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/model/Gas.kt index 3bdc6c5..fc12eb7 100644 --- a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/model/Gas.kt +++ b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/model/Gas.kt @@ -12,11 +12,9 @@ package org.neotech.app.abysner.domain.core.model +import org.neotech.app.abysner.domain.core.model.Gas.Companion.MOD_TOLERANCE import org.neotech.app.abysner.domain.core.physics.Pressure -import org.neotech.app.abysner.domain.core.physics.ambientPressureToMeters -import org.neotech.app.abysner.domain.core.physics.metersToAmbientPressure import org.neotech.app.abysner.domain.utilities.DecimalFormat -import kotlin.math.floor import kotlin.math.round data class Gas(val oxygenFraction: Double, val heliumFraction: Double) { diff --git a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/physics/PolynomialRealGasModel.kt b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/physics/PolynomialRealGasModel.kt index a580be9..3d74069 100644 --- a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/physics/PolynomialRealGasModel.kt +++ b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/physics/PolynomialRealGasModel.kt @@ -1,6 +1,6 @@ /* * Abysner - Dive planner - * Copyright (C) 2024 Neotech + * Copyright (C) 2024-2026 Neotech * * Abysner is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3, @@ -53,10 +53,12 @@ class PolynomialRealGasModel( /** * Find the pressure when compressing the given volume of gas (at 1 atm) into a 1 liter * cylinder/space. + * + * The iterative approach can get stuck (infinite loop) at very high pressures where the + * polynomial Z-factor curve peaks and starts decreasing. For air this happens above ~930 bar, + * well beyond any diving related pressures. */ private fun approximatePressure(gas: Gas, volume: Double): Double { - // TODO some clamping needs to be done here, this can get stuck on high pressures. - // Note: volume serves a double purpose here, it is both the initial estimate (Boyle's Law: // `pressure = volume / 1.0` where 1.0 is the size of the cylinder). But it also still // serves as the actual volume of gas that is being compressed. @@ -76,43 +78,6 @@ class PolynomialRealGasModel( return adjustedEstimatedPressure } - /** - * Same as [approximatePressure] but with some extra mechanics to detect a stable oscillation. - */ - private fun approximatePressureWithOscillationDetection(gas: Gas, volume: Double): Double { - var estimatedPressure = volume - val zFactorAtOneAtm = compressibilityFactor(1.0, gas) - - var previousDifferencePositive: Double = Double.MAX_VALUE - var previousDifferenceNegative: Double = Double.MIN_VALUE - do { - val currentVolume = zFactorAtOneAtm * estimatedPressure - val zFactorAtEstimatedPressure = compressibilityFactor(estimatedPressure, gas) - - val estimatedVolume = zFactorAtEstimatedPressure * volume - - // Update the pressure estimate based on the target volume - estimatedPressure = volume * zFactorAtEstimatedPressure / zFactorAtOneAtm - - val difference = currentVolume - estimatedVolume - - if(difference > 0) { - if(abs(previousDifferencePositive - difference) < 0.001) { - // Delta has not changed enough since previous either we have stabilized precise enough, or a stabilized oscillation may be going on. - return estimatedPressure - } - previousDifferencePositive = difference - } else { - if(abs(previousDifferenceNegative - difference) < 0.001) { - // Delta has not changed enough since previous either we have stabilized precise enough, or a stabilized oscillation may be going on. - return estimatedPressure - } - previousDifferenceNegative = difference - } - } while(abs(difference) > VOLUME_PRECISION_DELTA) - return estimatedPressure - } - override fun getGasVolume(gas: Gas, cylinderSize: Double, pressure: Double): Double { return volumeCorrectionFactor(pressure, gas) * cylinderSize } diff --git a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/physics/Pressure.kt b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/physics/Pressure.kt index e1c0de8..6475ac6 100644 --- a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/physics/Pressure.kt +++ b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/physics/Pressure.kt @@ -191,8 +191,3 @@ const val METERS_PER_FOOT = 0.3048 * Conversion factor from bar to psi (pounds per square inch). */ const val PSI_PER_BAR = 14.503773773 - -/** - * Conversion factor from liters to cubic feet. - */ -const val LITERS_PER_CUBIC_FOOT = 28.316846592 diff --git a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/physics/Volume.kt b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/physics/Volume.kt index ec2928d..f0e3b34 100644 --- a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/physics/Volume.kt +++ b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/core/physics/Volume.kt @@ -1,6 +1,6 @@ /* * Abysner - Dive planner - * Copyright (C) 2024 Neotech + * Copyright (C) 2024-2026 Neotech * * Abysner is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3, @@ -12,6 +12,12 @@ package org.neotech.app.abysner.domain.core.physics -fun Double.asCubicFeetToLiters(): Double = this * 28.3168466 +fun Double.asCubicFeetToLiters(): Double = this * LITERS_PER_CUBIC_FOOT + +fun Double.asLitersToCubicFeet(): Double = this / LITERS_PER_CUBIC_FOOT + +/** + * Conversion factor from liters to cubic feet. + */ +const val LITERS_PER_CUBIC_FOOT = 28.316846592 -fun Double.asLitersToCubicFeet(): Double = this / 28.3168466 diff --git a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/decompression/model/DiveSegment.kt b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/decompression/model/DiveSegment.kt index e51cd49..4bdee13 100644 --- a/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/decompression/model/DiveSegment.kt +++ b/domain/src/commonMain/kotlin/org/neotech/app/abysner/domain/decompression/model/DiveSegment.kt @@ -156,17 +156,6 @@ data class DiveSegment( val isStop: Boolean get() = isDecompressionStop || isGasSwitch - fun depthAt(duration: Int): Double { - require(duration >= 0 && duration <= this.duration) - if (startDepth == endDepth) { - return startDepth - } else { - val depthDifference = - (endDepth - startDepth) * (duration.toDouble() / this.duration.toDouble()) - return startDepth + depthDifference - } - } - /** * Average depth of this section (essentially the depth mid-point). */ @@ -191,16 +180,6 @@ data class DiveSegment( } } -fun List.subList(fromTimeStamp: Int): List { - val indexOfFirstAffectedDiveSegment = indexOfFirst { it.start >= fromTimeStamp } - return if(indexOfFirstAffectedDiveSegment == -1) { - emptyList() - } else { - // TODO exact subList at the timestamp by averaging the found dive segment? - this.subList(indexOfFirstAffectedDiveSegment, this.size) - } -} - /** * Merges adjacent segments that are logically equivalent, reducing the list to a minimal set of * dive instructions. Operates in place and returns the same list. diff --git a/domain/src/commonTest/kotlin/org/neotech/app/abysner/domain/core/model/CylinderTest.kt b/domain/src/commonTest/kotlin/org/neotech/app/abysner/domain/core/model/CylinderTest.kt index 162e131..d2a3c1c 100644 --- a/domain/src/commonTest/kotlin/org/neotech/app/abysner/domain/core/model/CylinderTest.kt +++ b/domain/src/commonTest/kotlin/org/neotech/app/abysner/domain/core/model/CylinderTest.kt @@ -12,7 +12,7 @@ package org.neotech.app.abysner.domain.core.model -import org.neotech.app.abysner.domain.core.physics.PSI_PER_BAR +import org.neotech.app.abysner.domain.core.physics.asBarToPsi import kotlin.math.roundToInt import kotlin.test.Test import kotlin.test.assertEquals @@ -39,8 +39,8 @@ class CylinderTest { @Test fun workingPressure_roundTripsToExactPsiForImperialCylinders() { - assertEquals(3000, (Cylinder.AL80.workingPressure * PSI_PER_BAR).roundToInt()) - assertEquals(3300, (Cylinder.AL100.workingPressure * PSI_PER_BAR).roundToInt()) - assertEquals(3442, (Cylinder.HP100.workingPressure * PSI_PER_BAR).roundToInt()) + assertEquals(3000, Cylinder.AL80.workingPressure.asBarToPsi().roundToInt()) + assertEquals(3300, Cylinder.AL100.workingPressure.asBarToPsi().roundToInt()) + assertEquals(3442, Cylinder.HP100.workingPressure.asBarToPsi().roundToInt()) } } diff --git a/domain/src/commonTest/kotlin/org/neotech/app/abysner/domain/diveplanning/SetpointSwitchTest.kt b/domain/src/commonTest/kotlin/org/neotech/app/abysner/domain/diveplanning/SetpointSwitchTest.kt index c25d189..9ae7052 100644 --- a/domain/src/commonTest/kotlin/org/neotech/app/abysner/domain/diveplanning/SetpointSwitchTest.kt +++ b/domain/src/commonTest/kotlin/org/neotech/app/abysner/domain/diveplanning/SetpointSwitchTest.kt @@ -24,8 +24,6 @@ import org.neotech.app.abysner.domain.diveplanning.model.DiveProfileSection import org.neotech.app.abysner.domain.diveplanning.model.assign import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull class CcrSetpointSwitchTest {