Viewer update

This commit is contained in:
Alexander Nozik 2018-04-21 16:47:48 +03:00
parent 1a9309fd21
commit 6cd618e5a3
7 changed files with 348 additions and 226 deletions

View File

@ -15,6 +15,7 @@
*/ */
package inr.numass.data.storage package inr.numass.data.storage
import hep.dataforge.context.Context
import hep.dataforge.exceptions.StorageException import hep.dataforge.exceptions.StorageException
import hep.dataforge.io.ColumnedDataReader import hep.dataforge.io.ColumnedDataReader
import hep.dataforge.io.envelopes.Envelope import hep.dataforge.io.envelopes.Envelope
@ -23,6 +24,7 @@ import hep.dataforge.meta.MetaBuilder
import hep.dataforge.providers.Provider import hep.dataforge.providers.Provider
import hep.dataforge.storage.api.ObjectLoader import hep.dataforge.storage.api.ObjectLoader
import hep.dataforge.storage.api.Storage import hep.dataforge.storage.api.Storage
import hep.dataforge.storage.commons.DummyStorage
import hep.dataforge.storage.filestorage.FileStorage import hep.dataforge.storage.filestorage.FileStorage
import hep.dataforge.storage.loaders.AbstractLoader import hep.dataforge.storage.loaders.AbstractLoader
import hep.dataforge.tables.Table import hep.dataforge.tables.Table
@ -30,7 +32,6 @@ import inr.numass.data.api.NumassPoint
import inr.numass.data.api.NumassSet import inr.numass.data.api.NumassSet
import inr.numass.data.legacy.NumassFileEnvelope import inr.numass.data.legacy.NumassFileEnvelope
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.IOException import java.io.IOException
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
@ -170,6 +171,10 @@ class NumassDataLoader(
return NumassDataLoader(storage, name, annotation, items) return NumassDataLoader(storage, name, annotation, items)
} }
fun fromDir(context: Context, directory: Path, name: String = FileStorage.entryName(directory)): NumassDataLoader {
return fromDir(DummyStorage(context), directory, name)
}
/** /**
* "start_time": "2016-04-20T04:08:50", * "start_time": "2016-04-20T04:08:50",
* *

View File

@ -7,16 +7,13 @@ import hep.dataforge.fx.ui
import hep.dataforge.goals.Goal import hep.dataforge.goals.Goal
import hep.dataforge.kodex.configure import hep.dataforge.kodex.configure
import hep.dataforge.kodex.toList import hep.dataforge.kodex.toList
import hep.dataforge.meta.Meta
import hep.dataforge.plots.PlotFrame import hep.dataforge.plots.PlotFrame
import hep.dataforge.plots.PlotGroup import hep.dataforge.plots.PlotGroup
import hep.dataforge.plots.Plottable import hep.dataforge.plots.Plottable
import hep.dataforge.plots.data.DataPlot import hep.dataforge.plots.data.DataPlot
import hep.dataforge.plots.jfreechart.JFreeChartFrame import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.tables.Adapters import hep.dataforge.tables.Adapters
import hep.dataforge.tables.Table
import inr.numass.data.analyzers.NumassAnalyzer import inr.numass.data.analyzers.NumassAnalyzer
import inr.numass.data.analyzers.SimpleAnalyzer
import inr.numass.data.analyzers.withBinning import inr.numass.data.analyzers.withBinning
import inr.numass.data.api.MetaBlock import inr.numass.data.api.MetaBlock
import inr.numass.data.api.NumassBlock import inr.numass.data.api.NumassBlock
@ -32,12 +29,8 @@ import javafx.scene.control.CheckBox
import javafx.scene.control.ChoiceBox import javafx.scene.control.ChoiceBox
import javafx.scene.image.ImageView import javafx.scene.image.ImageView
import tornadofx.* import tornadofx.*
import java.util.concurrent.ConcurrentHashMap
class AmplitudeView( class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = ImageView(dfIcon)) {
private val analyzer: NumassAnalyzer = SimpleAnalyzer(),
private val cache: MutableMap<NumassBlock, Table> = ConcurrentHashMap()
) : View(title = "Numass amplitude spectrum plot", icon = ImageView(dfIcon)) {
private val frame: PlotFrame = JFreeChartFrame().configure { private val frame: PlotFrame = JFreeChartFrame().configure {
"title" to "Detector response plot" "title" to "Detector response plot"
@ -116,13 +109,6 @@ class AmplitudeView(
center = container.root center = container.root
} }
/**
* Calculate or get spectrum from the immutable
*/
private fun getSpectrum(point: NumassBlock): Table {
return cache.computeIfAbsent(point) { analyzer.getAmplitudeSpectrum(point, Meta.empty()) }
}
/** /**
* Put or replace current plot with name `key` * Put or replace current plot with name `key`
*/ */
@ -138,7 +124,7 @@ class AmplitudeView(
* Distinct map of channel number to corresponding grouping block * Distinct map of channel number to corresponding grouping block
*/ */
private fun NumassPoint.getChannels(): Map<Int, NumassBlock> { private fun NumassPoint.getChannels(): Map<Int, NumassBlock> {
return blocks.toList().groupBy { it.channel ?: 0 }.mapValues { entry -> return blocks.toList().groupBy { it.channel }.mapValues { entry ->
if (entry.value.size == 1) { if (entry.value.size == 1) {
entry.value.first() entry.value.first()
} else { } else {
@ -164,7 +150,7 @@ class AmplitudeView(
DataPlot.plot( DataPlot.plot(
key, key,
adapter, adapter,
getSpectrum(point).withBinning(binning) PointCache[point].withBinning(binning)
) )
} else { } else {
val group = PlotGroup.typed<DataPlot>(key) val group = PlotGroup.typed<DataPlot>(key)
@ -172,7 +158,7 @@ class AmplitudeView(
val plot = DataPlot.plot( val plot = DataPlot.plot(
key.toString(), key.toString(),
adapter, adapter,
getSpectrum(block).withBinning(binning) PointCache[point].withBinning(binning)
) )
group.add(plot) group.add(plot)
} }

View File

@ -0,0 +1,200 @@
package inr.numass.viewer
import hep.dataforge.context.Context
import hep.dataforge.context.Global
import hep.dataforge.fx.*
import hep.dataforge.fx.fragments.LogFragment
import hep.dataforge.storage.commons.StorageManager
import inr.numass.NumassProperties
import inr.numass.data.api.NumassPoint
import inr.numass.data.legacy.NumassFileEnvelope
import inr.numass.data.storage.NumassDataLoader
import inr.numass.data.storage.NumassStorageFactory
import javafx.beans.property.SimpleObjectProperty
import javafx.geometry.Insets
import javafx.scene.control.Alert
import javafx.scene.layout.Priority
import javafx.scene.text.Font
import javafx.stage.DirectoryChooser
import javafx.stage.FileChooser
import kotlinx.coroutines.experimental.async
import org.controlsfx.control.StatusBar
import tornadofx.*
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
class MainView(val context: Context = Global.getContext("viewer")) : View(title = "Numass viewer", icon = dfIconView) {
private val statusBar = StatusBar();
private val logFragment = LogFragment().apply {
addLogHandler(context.logger)
}
private val pathProperty = SimpleObjectProperty<Path>()
private var path: Path by pathProperty
val contentViewProperty = SimpleObjectProperty<UIComponent>()
var contentView: UIComponent? by contentViewProperty
override val root = borderpane {
prefHeight = 600.0
prefWidth = 800.0
top {
toolbar {
prefHeight = 40.0
button("Load directory") {
action {
val chooser = DirectoryChooser()
chooser.title = "Select directory to view"
val homeDir = NumassProperties.getNumassProperty("numass.viewer.lastPath")
try {
if (homeDir == null) {
chooser.initialDirectory = File(".").absoluteFile
} else {
chooser.initialDirectory = File(homeDir)
}
} catch (ex: Exception) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", null)
}
val rootDir = chooser.showDialog(primaryStage.scene.window)
if (rootDir != null) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", rootDir.absolutePath)
async {
runLater {
path = rootDir.toPath()
}
load(rootDir.toPath())
}
}
}
}
button("Load file") {
action {
val chooser = FileChooser()
chooser.title = "Select file to view"
val homeDir = NumassProperties.getNumassProperty("numass.viewer.lastPath")
try {
if (homeDir == null) {
chooser.initialDirectory = File(".").absoluteFile
} else {
chooser.initialDirectory = File(homeDir)
}
} catch (ex: Exception) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", null)
}
val file = chooser.showOpenDialog(primaryStage.scene.window)
if (file != null) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", file.parentFile.absolutePath)
async {
runLater {
path = file.toPath()
}
load(file.toPath())
}
}
}
}
label(pathProperty.asString()) {
padding = Insets(0.0, 0.0, 0.0, 10.0);
font = Font.font("System Bold", 13.0);
}
pane {
hgrow = Priority.ALWAYS
}
togglebutton("Console") {
isSelected = false
logFragment.bindWindow(this@togglebutton)
}
}
}
bottom = statusBar
}
init {
contentViewProperty.onChange {
root.center = it?.root
}
}
private suspend fun load(path: Path) {
runLater {
contentView = null
}
if (Files.isDirectory(path)) {
if (Files.exists(path.resolve(NumassDataLoader.META_FRAGMENT_NAME))) {
//build set view
runGoal("viewer.load.set[$path]") {
title = "Load set ($path)"
message = "Building numass set..."
NumassDataLoader.fromDir(context, path)
} ui {
contentView = SpectrumView().apply {
add(it.name, it)
}
} except {
alert(
type = Alert.AlertType.ERROR,
header = "Error during set loading",
content = it.toString()
).show()
}
} else {
//build storage
runGoal("viewer.load.storage[$path]") {
title = "Load storage ($path)"
message = "Building numass storage tree..."
StorageManager.buildStorage(
context,
NumassStorageFactory.buildStorageMeta(path.toUri(), true, false)
)
} ui {
contentView = StorageView(it)
} except {
alert(
type = Alert.AlertType.ERROR,
header = "Error during storage loading",
content = it.toString()
).show()
}
}
} else {
//Reading individual file
val envelope = try {
NumassFileEnvelope.open(path,true)
} catch (ex: Exception){
runLater {
alert(
type = Alert.AlertType.ERROR,
header = "Can't load DF envelope from file $path",
content = ex.toString()
).show()
}
null
}
envelope?.let {
if(it.meta.hasMeta("external_meta")){
//try to read as point
val point = NumassPoint.read(it)
runLater {
contentView = AmplitudeView().apply {
add(path.toString(), point)
}
}
} else {
alert(
type = Alert.AlertType.ERROR,
header = "Unknown envelope content: $path"
).show()
}
}
}
}
}

View File

@ -0,0 +1,35 @@
package inr.numass.viewer
import hep.dataforge.fx.meta.MetaViewer
import inr.numass.data.analyzers.NumassAnalyzer
import inr.numass.data.api.NumassPoint
import tornadofx.*
import tornadofx.controlsfx.borders
class PointInfoView(val point: NumassPoint) : MetaViewer(point.meta) {
private val count: Int by lazy {
PointCache[point].sumBy { it.getValue(NumassAnalyzer.COUNT_KEY).int }
}
override val root = super.root.apply {
top {
gridpane {
borders {
lineBorder().build()
}
row {
hbox {
label("Total number of events: ")
label("$count")
}
}
row {
hbox {
label("Total count rate: ")
label(String.format("%.2f", count.toDouble() / point.length.toMillis() * 1000))
}
}
}
}
}
}

View File

@ -5,16 +5,11 @@ import hep.dataforge.fx.plots.PlotContainer
import hep.dataforge.fx.runGoal import hep.dataforge.fx.runGoal
import hep.dataforge.fx.ui import hep.dataforge.fx.ui
import hep.dataforge.kodex.configure import hep.dataforge.kodex.configure
import hep.dataforge.meta.Meta
import hep.dataforge.plots.PlotFrame import hep.dataforge.plots.PlotFrame
import hep.dataforge.plots.data.DataPlot import hep.dataforge.plots.data.DataPlot
import hep.dataforge.plots.jfreechart.JFreeChartFrame import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.tables.Adapters import hep.dataforge.tables.Adapters
import hep.dataforge.tables.Table
import inr.numass.data.analyzers.NumassAnalyzer
import inr.numass.data.analyzers.SimpleAnalyzer
import inr.numass.data.analyzers.countInWindow import inr.numass.data.analyzers.countInWindow
import inr.numass.data.api.NumassPoint
import inr.numass.data.api.NumassSet import inr.numass.data.api.NumassSet
import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleIntegerProperty
import javafx.collections.FXCollections import javafx.collections.FXCollections
@ -26,7 +21,6 @@ import javafx.scene.image.ImageView
import javafx.util.converter.NumberStringConverter import javafx.util.converter.NumberStringConverter
import org.controlsfx.control.RangeSlider import org.controlsfx.control.RangeSlider
import tornadofx.* import tornadofx.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import java.util.stream.Collectors import java.util.stream.Collectors
@ -35,10 +29,7 @@ import java.util.stream.Collectors
* @param analyzer * @param analyzer
* @param cache - optional global point immutable * @param cache - optional global point immutable
*/ */
class SpectrumView( class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIcon)) {
val analyzer: NumassAnalyzer = SimpleAnalyzer(),
val cache: MutableMap<NumassPoint, Table> = ConcurrentHashMap()
) : View(title = "Numass spectrum plot", icon = ImageView(dfIcon)) {
private val frame: PlotFrame = JFreeChartFrame().configure { private val frame: PlotFrame = JFreeChartFrame().configure {
"xAxis.title" to "U" "xAxis.title" to "U"
@ -119,11 +110,6 @@ class SpectrumView(
} }
} }
private fun getSpectrum(point: NumassPoint): Table {
return cache.computeIfAbsent(point) { analyzer.getAmplitudeSpectrum(point, Meta.empty()) }
}
private fun updateView() { private fun updateView() {
runLater { container.progress = 0.0 } runLater { container.progress = 0.0 }
val progress = AtomicInteger(0) val progress = AtomicInteger(0)
@ -134,7 +120,7 @@ class SpectrumView(
runGoal("spectrumData[$name]") { runGoal("spectrumData[$name]") {
set.points.map { point -> set.points.map { point ->
val count = getSpectrum(point).countInWindow(loChannel.toShort(), upChannel.toShort()); val count = PointCache[point].countInWindow(loChannel.toShort(), upChannel.toShort());
val seconds = point.length.toMillis() / 1000.0; val seconds = point.length.toMillis() / 1000.0;
runLater { runLater {
container.progress = progress.incrementAndGet().toDouble() / totalProgress container.progress = progress.incrementAndGet().toDouble() / totalProgress

View File

@ -1,55 +1,25 @@
package inr.numass.viewer package inr.numass.viewer
import hep.dataforge.context.Context import hep.dataforge.fx.dfIconView
import hep.dataforge.context.Global
import hep.dataforge.fx.*
import hep.dataforge.fx.fragments.LogFragment
import hep.dataforge.fx.meta.MetaViewer import hep.dataforge.fx.meta.MetaViewer
import hep.dataforge.fx.runGoal
import hep.dataforge.meta.Metoid import hep.dataforge.meta.Metoid
import hep.dataforge.storage.api.Loader import hep.dataforge.storage.api.Loader
import hep.dataforge.storage.api.Storage import hep.dataforge.storage.api.Storage
import hep.dataforge.storage.api.TableLoader import hep.dataforge.storage.api.TableLoader
import hep.dataforge.storage.commons.StorageManager
import hep.dataforge.tables.Table
import inr.numass.NumassProperties
import inr.numass.data.api.NumassPoint import inr.numass.data.api.NumassPoint
import inr.numass.data.api.NumassSet import inr.numass.data.api.NumassSet
import inr.numass.data.storage.NumassDataLoader import inr.numass.data.storage.NumassDataLoader
import inr.numass.data.storage.NumassStorageFactory
import javafx.beans.property.SimpleBooleanProperty import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import javafx.geometry.Insets
import javafx.scene.control.Alert
import javafx.scene.control.ContextMenu import javafx.scene.control.ContextMenu
import javafx.scene.control.TreeItem import javafx.scene.control.TreeItem
import javafx.scene.image.ImageView
import javafx.scene.layout.Priority
import javafx.scene.text.Font
import javafx.stage.DirectoryChooser
import org.controlsfx.control.StatusBar
import tornadofx.* import tornadofx.*
import java.io.File
import java.net.URI
import java.util.concurrent.ConcurrentHashMap
import kotlin.streams.toList import kotlin.streams.toList
class StorageView(private val context: Context = Global) : View(title = "Numass storage", icon = ImageView(dfIcon)) { class StorageView(val storage: Storage) : View(title = "Numass storage", icon = dfIconView) {
private val ampView: AmplitudeView by inject();
val storageProperty = SimpleObjectProperty<Storage?>() private val spectrumView: SpectrumView by inject();
var storage by storageProperty
private val storageNameProperty = SimpleStringProperty("")
private var storageName by storageNameProperty
private val statusBar = StatusBar();
private val cache: MutableMap<NumassPoint, Table> = ConcurrentHashMap()
private val ampView: AmplitudeView by inject(params = mapOf("cache" to cache));
private val spectrumView: SpectrumView by inject(params = mapOf("cache" to cache));
private val hvView: HVView by inject(); private val hvView: HVView by inject();
private val scView: SlowControlView by inject(); private val scView: SlowControlView by inject();
@ -57,6 +27,14 @@ class StorageView(private val context: Context = Global) : View(title = "Numass
val checkedProperty = SimpleBooleanProperty(false) val checkedProperty = SimpleBooleanProperty(false)
var checked by checkedProperty var checked by checkedProperty
val infoView: UIComponent? by lazy {
when (content) {
is NumassPoint -> PointInfoView(content)
is Metoid -> MetaViewer(content.meta, title = "Meta view: $id")
else -> null
}
}
init { init {
checkedProperty.onChange { selected -> checkedProperty.onChange { selected ->
when (content) { when (content) {
@ -90,7 +68,10 @@ class StorageView(private val context: Context = Global) : View(title = "Numass
val children: List<Container>? by lazy { val children: List<Container>? by lazy {
when (content) { when (content) {
is Storage -> (content.shelves().sorted() + content.loaders().sorted()).map { buildContainer(it, this) } is Storage -> (content.shelves().sorted() + content.loaders().sorted()).map { buildContainer(it, this) }
is NumassSet -> content.points.map { buildContainer(it, this) }.toList().sortedBy { it.id } is NumassSet -> content.points
.sorted(compareBy { it.index })
.map { buildContainer(it, this) }
.toList()
else -> null else -> null
} }
} }
@ -100,80 +81,14 @@ class StorageView(private val context: Context = Global) : View(title = "Numass
} }
override val root = borderpane {
top {
toolbar {
prefHeight = 40.0
button("Load") {
action {
val chooser = DirectoryChooser()
chooser.title = "Select numass storage root"
val storageRoot = NumassProperties.getNumassProperty("numass.storage.root")
try {
if (storageRoot == null) {
chooser.initialDirectory = File(".").absoluteFile
} else {
chooser.initialDirectory = File(storageRoot)
}
} catch (ex: Exception) {
NumassProperties.setNumassProperty("numass.storage.root", null)
}
val rootDir = chooser.showDialog(primaryStage.scene.window) override val root = splitpane {
if (rootDir != null) {
NumassProperties.setNumassProperty("numass.storage.root", rootDir.absolutePath)
loadDirectory(rootDir.toURI())
}
}
}
label(storageNameProperty) {
padding = Insets(0.0, 0.0, 0.0, 10.0);
font = Font.font("System Bold", 13.0);
}
pane {
hgrow = Priority.ALWAYS
}
togglebutton("Console") {
isSelected = false
LogFragment().apply {
addLogHandler(context.logger)
bindWindow(this@togglebutton)
}
}
}
}
center {
splitpane {
treeview<Container> { treeview<Container> {
//isShowRoot = false //isShowRoot = false
storageProperty.onChange { root = TreeItem(Container(storage.name, storage))
if (it != null) {
root = TreeItem(Container(it.name, it))
root.isExpanded = true root.isExpanded = true
runGoal("populateTree") { runGoal("viewer.storage.populateTree") {
runLater { statusBar.progress = -1.0 } populate { parent -> parent.value.children }
populate { parent ->
val value = parent.value.content
when (value) {
is Storage -> (value.shelves().sorted() + value.loaders().sorted()).map { buildContainer(it, parent.value) }
is NumassSet -> value.points.map { buildContainer(it, parent.value) }.toList().sortedBy { it.id }
else -> null
}
}
runLater { statusBar.progress = 0.0 }
}
/*
lazyPopulate( leafCheck = { it.value.hasChildren }) {
runLater { statusBar.progress = -1.0 }
it.value.children.also {
runLater { statusBar.progress = 0.0 }
}
}
*/
}
} }
cellFormat { value -> cellFormat { value ->
when (value.content) { when (value.content) {
@ -204,19 +119,19 @@ class StorageView(private val context: Context = Global) : View(title = "Numass
graphic = null graphic = null
} }
} }
contextMenu = ContextMenu() contextMenu = ContextMenu().apply {
contextMenu.item("Clear all") { item("Clear all") {
action { action {
this@cellFormat.treeItem.uncheckAll() this@cellFormat.treeItem.uncheckAll()
} }
} }
if (value.content is Metoid) { value.infoView?.let {
contextMenu.item("Meta") { item("Info") {
action { action {
openInternalWindow(MetaViewer(value.content.meta), escapeClosesWindow = true) it.openModal(escapeClosesWindow = true)
}
} }
} }
} }
} }
} }
@ -245,13 +160,8 @@ class StorageView(private val context: Context = Global) : View(title = "Numass
} }
setDividerPosition(0, 0.3); setDividerPosition(0, 0.3);
} }
}
bottom = statusBar;
}
private fun TreeItem<Container>.uncheckAll() { private fun TreeItem<Container>.uncheckAll() {
this.value.checked = false this.value.checked = false
this.children.forEach { it.uncheckAll() } this.children.forEach { it.uncheckAll() }
@ -280,21 +190,4 @@ class StorageView(private val context: Context = Global) : View(title = "Numass
else -> throw IllegalArgumentException("Unknown content type: ${content::class.java}"); else -> throw IllegalArgumentException("Unknown content type: ${content::class.java}");
} }
private fun loadDirectory(path: URI) {
statusBar.text = "Loading storage: $path"
runGoal("loadDirectory[$path]") {
title = "Load storage ($path)"
message = "Building numass storage tree..."
StorageManager.buildStorage(context, NumassStorageFactory.buildStorageMeta(path, true, false))
} ui {
storage = it
storageName = "Storage: $path"
statusBar.text = "OK"
} except {
alert(type = Alert.AlertType.ERROR, header = "Error during storage loading", content = it.toString()).show()
it.printStackTrace()
}
}
} }

View File

@ -4,6 +4,11 @@ import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger import ch.qos.logback.classic.Logger
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.fx.dfIcon import hep.dataforge.fx.dfIcon
import hep.dataforge.meta.Meta
import hep.dataforge.tables.Table
import hep.dataforge.utils.Misc
import inr.numass.data.analyzers.SimpleAnalyzer
import inr.numass.data.api.NumassPoint
import javafx.stage.Stage import javafx.stage.Stage
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import tornadofx.* import tornadofx.*
@ -11,7 +16,7 @@ import tornadofx.*
/** /**
* Created by darksnake on 14-Apr-17. * Created by darksnake on 14-Apr-17.
*/ */
class Viewer : App(StorageView::class) { class Viewer : App(MainView::class) {
init{ init{
(LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as Logger).level = Level.INFO (LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as Logger).level = Level.INFO
} }
@ -26,3 +31,15 @@ class Viewer : App(StorageView::class) {
Global.terminate(); Global.terminate();
} }
} }
/**
* Global point cache
*/
object PointCache{
private val analyzer = SimpleAnalyzer()
private val cache: MutableMap<NumassPoint, Table> = Misc.getLRUCache(1000)
operator fun get(point: NumassPoint): Table {
return cache.computeIfAbsent(point) { analyzer.getAmplitudeSpectrum(point, Meta.empty()) }
}
}