Skip to content

Commit

Permalink
Merge pull request #103 from SciProgCentre/dev
Browse files Browse the repository at this point in the history
0.7.0-dev-1
  • Loading branch information
SPC-code authored Feb 4, 2024
2 parents 1ebabc9 + 95be28d commit 53ed384
Show file tree
Hide file tree
Showing 37 changed files with 385 additions and 187 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased

### Added
- Compose demo

### Changed
- Migrated to DF 0.8

### Deprecated

### Removed
- Grid view

### Fixed

Expand Down
6 changes: 3 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ plugins {
id("space.kscience.gradle.project")
}

val dataforgeVersion by extra("0.7.1")
val plotlyVersion by extra("2.24.1")
val dataforgeVersion by extra("0.8.0")
val plotlyVersion by extra("2.29.0")

allprojects {
group = "space.kscience"
version = "0.6.1"
version = "0.7.0-dev-1"
}

apiValidation {
Expand Down
42 changes: 42 additions & 0 deletions examples/compose-demo/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin("multiplatform")
id("org.jetbrains.compose") version "1.5.12"
}

repositories {
mavenCentral()
maven("https://repo.kotlin.link")
maven("https://jogamp.org/deployment/maven")
}

kotlin{
jvm()
jvmToolchain(17)
sourceSets{
jvmMain{
dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)
implementation(compose.desktop.currentOs)
implementation("io.github.kevinnzou:compose-webview-multiplatform:1.8.6")
implementation(projects.plotlyktServer)
implementation(spclibs.logback.classic)
}
}
}
}

tasks.withType<KotlinCompile> {
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}

compose {
desktop {
application {
mainClass = "space.kscience.plotly.compose.AppKt"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package space.kscience.plotly.compose

import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import com.multiplatform.webview.web.LoadingState
import com.multiplatform.webview.web.WebView
import com.multiplatform.webview.web.rememberWebViewNavigator
import com.multiplatform.webview.web.rememberWebViewStateWithHTMLData
import dev.datlag.kcef.KCEF
import kotlinx.coroutines.flow.MutableStateFlow

private val allowedPages = listOf(
"Static",
"Dynamic"
)

@Composable
fun App() {
val scaleFlow = remember { MutableStateFlow(1f) }
val scale by scaleFlow.collectAsState()
val scope = rememberCoroutineScope()
val server = remember {
scope.servePlots(scaleFlow)
}

val state = rememberWebViewStateWithHTMLData(staticPlot())

val navigator = rememberWebViewNavigator()

val loadingState = state.loadingState
if (loadingState is LoadingState.Loading) {
LinearProgressIndicator(
progress = loadingState.progress,
modifier = Modifier.fillMaxWidth()
)
}

Row(Modifier.fillMaxSize()) {
Column(Modifier.width(300.dp)) {
Button({ navigator.loadHtml(staticPlot()) }, modifier = Modifier.fillMaxWidth()) {
Text("Static")
}
Button({ navigator.loadUrl("http://localhost:7778/Dynamic") }, modifier = Modifier.fillMaxWidth()) {
Text("Dynamic")
}

Slider(
scale,
{ scaleFlow.value = it },
valueRange = 0.1f..10f,
modifier = Modifier.fillMaxWidth()
)
}
Column(Modifier.fillMaxSize()) {

WebView(
state = state,
navigator = navigator,
modifier = Modifier.fillMaxSize()
)
}
}
}

fun main() = application {
KCEF.initBlocking(
builder = {
progress {
onDownloading {
println("Download progress: $it%")
}
}
release(true)
}
)
Window(onCloseRequest = ::exitApplication) {
MaterialTheme {
App()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package space.kscience.plotly.compose

import io.ktor.server.engine.ApplicationEngine
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import space.kscience.plotly.*
import space.kscience.plotly.models.Scatter
import space.kscience.plotly.models.invoke
import space.kscience.plotly.server.pushUpdates
import space.kscience.plotly.server.serve
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin

fun staticPlot(): String = Plotly.page {
val x = (0..100).map { it.toDouble() / 100.0 }.toDoubleArray()
val y1 = x.map { sin(2.0 * PI * it) }.toDoubleArray()
val y2 = x.map { cos(2.0 * PI * it) }.toDoubleArray()
val trace1 = Scatter(x, y1) {
name = "sin"
}
val trace2 = Scatter(x, y2) {
name = "cos"
}
plot(config = PlotlyConfig { responsive = true }) {//static plot
traces(trace1, trace2)
layout {
title = "First graph, row: 1, size: 8/12"
xaxis.title = "x axis name"
yaxis { title = "y axis name" }
}
}
}.render()

fun CoroutineScope.servePlots(scale: StateFlow<Number>): ApplicationEngine = Plotly.serve(this, port = 7778) {
page("Static") { container ->
val x = (0..100).map { it.toDouble() / 100.0 }.toDoubleArray()
val y1 = x.map { sin(2.0 * PI * it) }.toDoubleArray()
val y2 = x.map { cos(2.0 * PI * it) }.toDoubleArray()
val trace1 = Scatter(x, y1) {
name = "sin"
}
val trace2 = Scatter(x, y2) {
name = "cos"
}
plot(renderer = container) {//static plot
traces(trace1, trace2)
layout {
title = "First graph, row: 1, size: 8/12"
xaxis.title = "x axis name"
yaxis { title = "y axis name" }
}
}
}

page("Dynamic") { container ->
val x = (0..100).map { it.toDouble() / 100.0 }
val y = x.map { sin(2.0 * PI * it) }

val trace = Scatter(x, y) { name = "sin" }

val plot = plot("dynamic", config = PlotlyConfig { responsive = true }, renderer = container) {
traces(trace)
layout {
title = "Dynamic plot"
xaxis.title = "x axis name"
yaxis.title = "y axis name"
}
}

launch {
var time: Long = 0
while (isActive) {
delay(10)
time += 10
val frequency = scale.value.toDouble()
val dynamicY = x.map { sin(2.0 * PI * frequency * (it + time.toDouble() / 1000.0)) }
//trace.y.numbers = dynamicY
plot.data[0].y.numbers = dynamicY
plot.layout {
xaxis.title = "x axis name (t = $time)"
}
}
}
}
pushUpdates(100)
}


Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import kotlinx.serialization.json.Json
import org.w3c.dom.HTMLElement
import org.w3c.dom.events.Event
import space.kscience.dataforge.meta.MetaSerializer
import space.kscience.dataforge.meta.toMutableMeta
import space.kscience.plotly.*
import space.kscience.plotly.events.PlotlyEventListenerType
import space.kscience.plotly.models.ScatterMode
Expand Down Expand Up @@ -133,7 +132,7 @@ fun main(): Unit = withCanvas {
}
val serialized = plot.toJsonString()
console.log(serialized)
val deserialized = Plot(Json.decodeFromString(MetaSerializer, serialized).toMutableMeta())
val deserialized = Plot(Json.decodeFromString(MetaSerializer, serialized))
plotDiv(plot = deserialized).on(PlotlyEventListenerType.CLICK){
console.info(it.toString())
}
Expand Down
14 changes: 3 additions & 11 deletions examples/native-demo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,12 @@ repositories {
}

kotlin {
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}

nativeTarget.apply {
binaries {
linuxX64{
binaries{
executable()
}
}

sourceSets{
commonMain {
dependencies {
Expand Down
73 changes: 71 additions & 2 deletions examples/src/main/kotlin/gridPageLayout.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import kotlinx.html.div
import space.kscience.plotly.*
import space.kscience.plotly.models.Trace
import space.kscience.plotly.models.invoke
Expand All @@ -6,8 +7,76 @@ import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin

@Suppress("DEPRECATION")
@UnstablePlotlyAPI

private class PlotGrid {
public data class PlotCell(val id: String, val plot: Plot, val row: Int, val col: Int, val width: Int = 1)

private val cells = HashMap<String, PlotCell>()

/**
* @return Columns in ascending order, grouped by rows in ascending order.
* */
public val grid: List<List<PlotCell>>
get() = cells.values.groupBy { it.row }.toSortedMap().values.map {
it.sortedBy { cell -> cell.col }
}.toList()

public operator fun get(id: String): PlotCell? = cells[id]

private var currentRow = 0
private var currentCol = 0

public fun plot(
plot: Plot,
id: String = plot.toString(),
width: Int = 6,
row: Int? = null,
col: Int? = null,
): Plot {
val actualRow = if (row != null) {
row
} else {
currentCol = 0
currentRow++
}

val actualColumn = col ?: currentCol++

cells[id] = PlotCell(id, plot, actualRow, actualColumn, width)

return plot
}

public fun plot(
row: Int? = null,
col: Int? = null,
id: String? = null,
width: Int = 6,
block: Plot.() -> Unit,
): Plot {
val plot = Plotly.plot(block)
return plot(plot, id ?: plot.toString(), width, row, col)
}
}


private fun Plotly.grid(block: PlotGrid.() -> Unit): PlotlyPage {
val grid = PlotGrid().apply(block)
return page(cdnBootstrap, cdnPlotlyHeader) { container ->
div("col") {
grid.grid.forEach { row ->
div("row") {
row.forEach { cell ->
div("col-${cell.width}") {
plot(cell.plot, cell.id, renderer = container)
}
}
}
}
}
}
}

fun main() {

val x = (0..100).map { it.toDouble() / 100.0 }
Expand Down
1 change: 1 addition & 0 deletions plotlykt-core/api/plotlykt-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ public final class space/kscience/plotly/PlotGridKt {
}

public final class space/kscience/plotly/PlotKt {
public static final fun Plot (Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/plotly/Plot;
public static final fun layout (Lspace/kscience/plotly/Plot;Lkotlin/jvm/functions/Function1;)V
public static final fun toJsonString (Lspace/kscience/plotly/Plot;)Ljava/lang/String;
public static final fun trace (Lspace/kscience/plotly/Plot;Lkotlin/jvm/functions/Function1;)Lspace/kscience/plotly/models/Trace;
Expand Down
Loading

0 comments on commit 53ed384

Please sign in to comment.