Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MonolithicJS to js-package #55

Merged
merged 3 commits into from
Nov 7, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Move MonolithicJS to js-package
  • Loading branch information
IKupriyanov-HORIS committed Nov 6, 2019
commit 5c4b2b809631157c979e392bf22bfa5c0b4bb12d
10 changes: 10 additions & 0 deletions js-package/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ kotlin {
implementation project(':base')
implementation project(':mapper-core')
implementation project(':vis-svg')
implementation project(':vis-canvas')
implementation project(':vis-svg-mapper')
implementation project(':plot-base-portable')
implementation project(':plot-base')
Expand All @@ -46,6 +47,15 @@ kotlin {
jsMain {
dependencies {
implementation kotlin('stdlib-js')
implementation "io.github.microutils:kotlin-logging-js:$kotlinLogging_version"

implementation project(':mapper-core')
implementation project(':vis-svg-mapper')
}
}
jsTest {
dependencies {
implementation kotlin('test-js')
}
}
}
Expand Down
230 changes: 230 additions & 0 deletions js-package/src/jsMain/kotlin/MonolithicJs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/*
* Copyright (c) 2019. JetBrains s.r.o.
* Use of this source code is governed by the MIT license that can be found in the LICENSE file.
*/

/* root package */

import jetbrains.datalore.base.event.MouseEventSpec
import jetbrains.datalore.base.event.dom.DomEventUtil
import jetbrains.datalore.base.geometry.DoubleVector
import jetbrains.datalore.base.geometry.Vector
import jetbrains.datalore.base.js.dom.DomEventType
import jetbrains.datalore.base.jsObject.dynamicObjectToMap
import jetbrains.datalore.base.observable.property.ValueProperty
import jetbrains.datalore.plot.builder.Plot
import jetbrains.datalore.plot.builder.PlotContainer
import jetbrains.datalore.plot.builder.assemble.PlotAssembler
import jetbrains.datalore.plot.config.*
import jetbrains.datalore.plot.livemap.LiveMapUtil
import jetbrains.datalore.plot.server.config.PlotConfigServerSide
import jetbrains.datalore.vis.canvas.dom.DomCanvasControl
import jetbrains.datalore.vis.svg.SvgNodeContainer
import jetbrains.datalore.vis.svgMapper.dom.SvgRootDocumentMapper
import mu.KotlinLogging
import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLParagraphElement
import org.w3c.dom.Node
import org.w3c.dom.events.Event
import org.w3c.dom.events.MouseEvent
import org.w3c.dom.svg.SVGSVGElement


private val LOG = KotlinLogging.logger {}

private val DEF_PLOT_SIZE = DoubleVector(500.0, 400.0)
private val DEF_LIVE_MAP_SIZE = DoubleVector(800.0, 600.0)

/**
* The entry point to call in JS
* `raw specs` are plot specs not processed by datalore plot backend
*/
@Suppress("unused")
@JsName("buildPlotFromRawSpecs")
fun buildPlotFromRawSpecs(plotSpecJs: dynamic, width: Double, height: Double, parentElement: HTMLElement) {
try {
val plotSpec = dynamicObjectToMap(plotSpecJs)
PlotConfig.assertPlotSpecOrErrorMessage(plotSpec)
val processedSpec = if (PlotConfig.isFailure(plotSpec)) {
plotSpec
} else {
PlotConfigServerSide.processTransform(plotSpec)
}
buildPlotFromProcessedSpecsIntern(processedSpec, width, height, parentElement)
} catch (e: RuntimeException) {
handleException(e, parentElement)
}
}

/**
* The entry point to call in JS
* `processed specs` are plot specs processed by datalore plot backend
*/
@Suppress("unused")
@JsName("buildPlotFromProcessedSpecs")
fun buildPlotFromProcessedSpecs(plotSpecJs: dynamic, width: Double, height: Double, parentElement: HTMLElement) {
try {
val plotSpec = dynamicObjectToMap(plotSpecJs)
buildPlotFromProcessedSpecsIntern(plotSpec, width, height, parentElement)
} catch (e: RuntimeException) {
handleException(e, parentElement)
}
}

private fun buildPlotFromProcessedSpecsIntern(
plotSpec: MutableMap<String, Any>,
width: Double,
height: Double,
parentElement: HTMLElement
) {
// testing errors
// throw RuntimeException()
// throw RuntimeException("My sudden crush")
// throw IllegalArgumentException("User configuration error")
// throw IllegalStateException("User configuration error")
// throw IllegalStateException() // Huh?

PlotConfig.assertPlotSpecOrErrorMessage(plotSpec)
if (PlotConfig.isFailure(plotSpec)) {
val errorMessage = PlotConfig.getErrorMessage(plotSpec)
showError(errorMessage, parentElement)
return
}

val assembler = createPlotAssembler(plotSpec) { messages ->
messages.forEach {
showInfo(it, parentElement)
}
}

// Figure out plot size
val plotSize = if (width > 0 && height > 0) {
DoubleVector(width, height)
} else {
val maxWidth = if (width > 0) width else parentElement.offsetWidth.toDouble()
val defaultSize = defaultPlotSize(plotSpec, assembler)
if (defaultSize.x > maxWidth) {
val scaler = maxWidth / defaultSize.x
DoubleVector(maxWidth, defaultSize.y * scaler)
} else {
defaultSize
}
}

LiveMapOptionsParser.parseFromPlotOptions(OptionsAccessor(plotSpec))
?.let {
LiveMapUtil.injectLiveMapProvider(
assembler.layersByTile,
it
)
}

val plot = assembler.createPlot()
val svg = buildPlotSvg(plot, plotSize, parentElement)
parentElement.appendChild(svg)
}

private fun createPlotAssembler(
plotSpec: MutableMap<String, Any>,
computationMessagesHandler: ((List<String>) -> Unit)?
): PlotAssembler {
@Suppress("NAME_SHADOWING")
var plotSpec = plotSpec
plotSpec = PlotConfigClientSide.processTransform(plotSpec)
if (computationMessagesHandler != null) {
val computationMessages = PlotConfigUtil.findComputationMessages(plotSpec)
if (computationMessages.isNotEmpty()) {
computationMessagesHandler(computationMessages)
}
}

return PlotConfigClientSideUtil.createPlotAssembler(plotSpec)
}

private fun defaultPlotSize(plotSpec: Map<String, Any>, assembler: PlotAssembler): DoubleVector {
var plotSize = PlotConfigClientSideUtil.getPlotSizeOrNull(plotSpec)
if (plotSize == null) {
plotSize = DEF_PLOT_SIZE
val facets = assembler.facets
if (facets.isDefined) {
val xLevels = facets.xLevels!!
val yLevels = facets.yLevels!!
val columns = if (xLevels.isEmpty()) 1 else xLevels.size
val rows = if (yLevels.isEmpty()) 1 else yLevels.size
val panelWidth = DEF_PLOT_SIZE.x * (0.5 + 0.5 / columns)
val panelHeight = DEF_PLOT_SIZE.y * (0.5 + 0.5 / rows)
plotSize = DoubleVector(panelWidth * columns, panelHeight * rows)
} else if (assembler.containsLiveMap) {
plotSize = DEF_LIVE_MAP_SIZE
}
}

return plotSize
}

private fun buildPlotSvg(
plot: Plot,
plotSize: DoubleVector,
eventTarget: Node
): SVGSVGElement {

val plotContainer = PlotContainer(plot, ValueProperty(plotSize))

eventTarget.addEventListener(DomEventType.MOUSE_MOVE.name, { e: Event ->
plotContainer.mouseEventPeer.dispatch(
MouseEventSpec.MOUSE_MOVED,
DomEventUtil.translateInTargetCoord(e as MouseEvent)
)
})

eventTarget.addEventListener(DomEventType.MOUSE_LEAVE.name, { e: Event ->
plotContainer.mouseEventPeer.dispatch(
MouseEventSpec.MOUSE_LEFT,
DomEventUtil.translateInTargetCoord(e as MouseEvent)
)
})

plotContainer.ensureContentBuilt()

plotContainer.liveMapFigures.forEach { liveMapFigure ->
val canvasControl =
DomCanvasControl(liveMapFigure.dimension().get().toVector())
liveMapFigure.mapToCanvas(canvasControl)
eventTarget.appendChild(canvasControl.rootElement)
}

val svgRoot = plotContainer.svg
val mapper = SvgRootDocumentMapper(svgRoot)
SvgNodeContainer(svgRoot)
mapper.attachRoot()
return mapper.target
}

private fun DoubleVector.toVector(): Vector {
return Vector(x.toInt(), y.toInt())
}

private fun handleException(e: RuntimeException, parentElement: HTMLElement) {
val failureInfo = FailureHandler.failureInfo(e)
showError(failureInfo.message, parentElement)
if (failureInfo.isInternalError) {
LOG.error(e) {}
}
}

private fun showError(message: String, parentElement: HTMLElement) {
showText(message, "color:darkred;", parentElement)
}

private fun showInfo(message: String, parentElement: HTMLElement) {
showText(message, "color:darkblue;", parentElement)
}

private fun showText(message: String, style: String, parentElement: HTMLElement) {
val paragraphElement = parentElement.ownerDocument!!.createElement("p") as HTMLParagraphElement
if (style.isNotBlank()) {
paragraphElement.setAttribute("style", style)
}
paragraphElement.textContent = message
parentElement.appendChild(paragraphElement)
}
25 changes: 0 additions & 25 deletions plot-app/src/commonMain/kotlin/jetbrains.datalore.plot/Utils.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@

package jetbrains.datalore.plot

import jetbrains.datalore.plot.config.LiveMapOptionsParser
import jetbrains.datalore.plot.config.OptionsAccessor
import jetbrains.datalore.plot.config.PlotConfigClientSide
import jetbrains.datalore.plot.config.PlotConfigClientSideUtil
import jetbrains.datalore.plot.livemap.LiveMapUtil
import kotlin.test.Test

// {data={
Expand Down Expand Up @@ -37,7 +40,13 @@ class PlotBuilderTest {
val plotSpec = PlotConfigClientSide.processTransform(initPlotSpec)
val assembler = PlotConfigClientSideUtil.createPlotAssembler(plotSpec)

injectLiveMap(plotSpec, assembler)
LiveMapOptionsParser.parseFromPlotOptions(OptionsAccessor(plotSpec))
?.let {
LiveMapUtil.injectLiveMapProvider(
assembler.layersByTile,
it
)
}

return assembler.createPlot()
}
Expand Down
10 changes: 8 additions & 2 deletions plot-app/src/jsMain/kotlin/MonolithicJs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import jetbrains.datalore.plot.builder.Plot
import jetbrains.datalore.plot.builder.PlotContainer
import jetbrains.datalore.plot.builder.assemble.PlotAssembler
import jetbrains.datalore.plot.config.*
import jetbrains.datalore.plot.injectLiveMap
import jetbrains.datalore.plot.livemap.LiveMapUtil
import jetbrains.datalore.plot.server.config.PlotConfigServerSide
import jetbrains.datalore.vis.canvas.dom.DomCanvasControl
import jetbrains.datalore.vis.svg.SvgNodeContainer
Expand Down Expand Up @@ -111,7 +111,13 @@ private fun buildPlotFromProcessedSpecsIntern(
}
}

injectLiveMap(plotSpec, assembler)
LiveMapOptionsParser.parseFromPlotOptions(OptionsAccessor(plotSpec))
?.let {
LiveMapUtil.injectLiveMapProvider(
assembler.layersByTile,
it
)
}

val plot = assembler.createPlot()
val svg = buildPlotSvg(plot, plotSize, parentElement)
Expand Down