Skip to content

Commit

Permalink
Merge pull request #499 from scenerygraphics/lut-fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
kephale authored Jun 15, 2023
2 parents b113a18 + 735d366 commit 498daee
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 37 deletions.
53 changes: 52 additions & 1 deletion src/main/kotlin/sc/iview/SciView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ import sc.iview.ui.TaskManager
import tpietzsch.example2.VolumeViewerOptions
import java.awt.event.WindowListener
import java.io.IOException
import java.net.URL
import java.nio.ByteBuffer
import java.nio.FloatBuffer
import java.time.LocalDate
Expand All @@ -122,6 +123,9 @@ import java.util.function.Consumer
import java.util.function.Function
import java.util.function.Predicate
import java.util.stream.Collectors
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.collections.LinkedHashMap
import javax.swing.JOptionPane
import kotlin.math.cos
import kotlin.math.sin
Expand All @@ -134,6 +138,7 @@ import kotlin.math.sin
// we suppress unused warnings here because @Parameter-annotated fields
// get updated automatically by SciJava.
class SciView : SceneryBase, CalibratedRealInterval<CalibratedAxis> {

val sceneryPanel = arrayOf<SceneryPanel?>(null)

/*
Expand Down Expand Up @@ -216,6 +221,11 @@ class SciView : SceneryBase, CalibratedRealInterval<CalibratedAxis> {
* Set the SciJava Display
*/ var display: Display<*>? = null

/**
* List of available LUTs for caching
*/
private var availableLUTs = LinkedHashMap<String, URL>()

/**
* Return the current SceneryJPanel. This is necessary for custom context menus
* @return panel the current SceneryJPanel
Expand Down Expand Up @@ -899,6 +909,7 @@ class SciView : SceneryBase, CalibratedRealInterval<CalibratedAxis> {
n.let {
objectService.addObject(n)
eventService.publish(NodeAddedEvent(n))
(mainWindow as SwingMainWindow).nodePropertyEditor.updateProperties(n, rebuild = true)
}
return n
}
Expand Down Expand Up @@ -1418,7 +1429,8 @@ fun deleteNode(node: Node?, activePublish: Boolean = true) {
numTimepoints: Int,
name: String = "Volume",
vararg voxelDimensions: Float,
block: Volume.() -> Unit = {}): Volume {
block: Volume.() -> Unit = {},
colormapName: String = "Fire.lut"): Volume {
var timepoints = numTimepoints
var cacheControl: CacheControl? = null

Expand Down Expand Up @@ -1462,6 +1474,10 @@ fun deleteNode(node: Node?, activePublish: Boolean = true) {
val bg = BoundingGrid()
bg.node = v

// Set default colormap
v.metadata["sciview.colormapName"] = colormapName
v.colormap = Colormap.fromColorTable(getLUT(colormapName))

imageToVolumeMap[image] = v
return addNode(v, block = block)
}
Expand Down Expand Up @@ -1745,6 +1761,41 @@ fun deleteNode(node: Node?, activePublish: Boolean = true) {
println(scijavaContext!!.serviceIndex)
}

/**
* Return the color table corresponding to the [lutName]
* @param lutName a String represening an ImageJ style LUT name, like Fire.lut
* @return a [ColorTable] corresponding to the LUT or null if LUT not available
*/
fun getLUT(lutName: String = "Fire.lut"): ColorTable {
try {
refreshLUTs()
var lutResult = availableLUTs[lutName]
return lutService.loadLUT(lutResult)
} catch (e: IOException) {
log.error("LUT $lutName not available")
e.printStackTrace()
throw e
}
}

/**
* Refresh the cache of available LUTs fetched from LutService
*/
private fun refreshLUTs() {
if(availableLUTs.isEmpty()) {
availableLUTs.putAll(lutService.findLUTs().entries.sortedBy { it.key }.map { it.key to it.value })
}
}

/**
* Return a list of available LUTs/colormaps
* @return a list of LUT names
*/
fun getAvailableLUTs(): List<String> {
refreshLUTs()
return availableLUTs.map {entry -> entry.key}
}

companion object {
//bounds for the controls
const val FPSSPEED_MINBOUND_SLOW = 0.01f
Expand Down
36 changes: 17 additions & 19 deletions src/main/kotlin/sc/iview/commands/edit/Properties.kt
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,6 @@ class Properties : InteractiveCommand() {
@Parameter
private lateinit var log: LogService

private var availableLUTs = LinkedHashMap<String, URL>()

/**
* Nothing happens here, as cancelling the dialog is not possible.
*/
Expand All @@ -242,13 +240,6 @@ class Properties : InteractiveCommand() {
}

protected fun initValues() {
if (colormap == null) {
try {
lutService.loadLUT(availableLUTs["Red.lut"])
} catch (ioe: IOException) {
System.err.println("IOException while loading Red.lut")
}
}
rebuildSceneObjectChoiceList()
refreshSceneNodeInDialog()
updateCommandFields()
Expand Down Expand Up @@ -345,10 +336,6 @@ class Properties : InteractiveCommand() {
/** Updates command fields to match current scene node properties. */
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
fun updateCommandFields() {
if(availableLUTs.isEmpty()) {
availableLUTs.putAll(lutService.findLUTs().entries.sortedBy { it.key }.map { it.key to it.value })
}

val node = currentSceneNode ?: return

fieldsUpdating = true
Expand Down Expand Up @@ -397,13 +384,24 @@ class Properties : InteractiveCommand() {
slicingMode = slicingModeChoices[Volume.SlicingMode.values().indexOf(node .slicingMode)].toString()

val lutNameItem = info.getMutableInput("colormapName", String::class.java)
lutNameItem.choices = availableLUTs.keys.toMutableList()
val cachedColormapName = node.metadata["sciview.colormap-name"] as? String
lutNameItem.choices = sciView.getAvailableLUTs()
val cachedColormapName = node.metadata["sciview.colormapName"] as? String

if(cachedColormapName != null && availableLUTs[cachedColormapName] != null) {
if(cachedColormapName != null && sciView.getLUT(cachedColormapName) != null) {
colormapName = cachedColormapName
}

try {
val cm = sciView.getLUT(colormapName)
// Ensure the node matches
node.colormap = fromColorTable(cm)
node.metadata["sciview.colormapName"] = colormapName

colormap = cm
} catch (ioe: IOException) {
log.error("Could not load LUT $colormapName")
}

min = node.converterSetups[0].displayRangeMin.toInt()
max = node.converterSetups[0].displayRangeMax.toInt()

Expand Down Expand Up @@ -538,9 +536,9 @@ class Properties : InteractiveCommand() {
node.renderingMethod = Volume.RenderingMethod.values()[mode]
}
try {
val cm = lutService.loadLUT(availableLUTs[colormapName])
node.colormap = fromColorTable(cm)
node.metadata["sciview.colormap-name"] = colormapName
val cm = sciView.getLUT(colormapName)
node.colormap = fromColorTable(cm!!)
node.metadata["sciview.colormapName"] = colormapName
log.info("Setting new colormap to $colormapName / $cm")

colormap = cm
Expand Down
36 changes: 24 additions & 12 deletions src/main/kotlin/sc/iview/commands/view/SetLUT.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,22 @@
package sc.iview.commands.view

import graphics.scenery.Node
import net.imagej.lut.LUTService
import net.imglib2.display.AbstractArrayColorTable
import net.imglib2.display.ColorTable
import org.scijava.command.Command
import org.scijava.command.CommandService
import org.scijava.command.DynamicCommand
import org.scijava.plugin.Menu
import org.scijava.plugin.Parameter
import org.scijava.plugin.Plugin
import org.scijava.prefs.PrefService
import sc.iview.SciView
import sc.iview.commands.MenuWeights.VIEW
import sc.iview.commands.MenuWeights.VIEW_SET_LUT
import sc.iview.commands.demo.basic.VolumeRenderDemo
import java.io.IOException
import java.net.URL
import java.util.*


/**
* Command to set the currently used Look Up Table (LUT). This is a colormap for the volume.
*
Expand All @@ -55,38 +56,49 @@ class SetLUT : DynamicCommand() {
@Parameter
private lateinit var sciView: SciView

@Parameter
private lateinit var lutService: LUTService

@Parameter(label = "Node")
private lateinit var node: Node

@Parameter(label = "Selected LUT", choices = [], callback = "lutNameChanged")
@Parameter(label = "Selected LUT", choices = [], callback = "lutNameChanged", initializer = "initLutName")
private lateinit var lutName: String

@Parameter(label = "LUT Selection")
private lateinit var colorTable: ColorTable

@Parameter
private lateinit var prefs: PrefService

protected fun initLutName() {
lutName = "Fire.lut"
}

protected fun lutNameChanged() {
val lutNameItem = info.getMutableInput("lutName", String::class.java)
try {
colorTable = lutService.loadLUT(lutService.findLUTs()[lutNameItem.toString()])
colorTable = sciView.getLUT(lutName)!!
} catch (e: IOException) {
e.printStackTrace()
} catch (e: Exception) {
e.printStackTrace()
}
}

override fun initialize() {
val lutNameItem = info.getMutableInput("lutName", String::class.java)
lutNameItem.choices = sciView.getAvailableLUTs()

var persistLutName = prefs.get(SetLUT::class.java, "lutName");

try {
colorTable = lutService.loadLUT(lutService.findLUTs()["Red.lut"])
colorTable = sciView.getLUT(persistLutName)!!
} catch (e: IOException) {
e.printStackTrace()
}
val lutNameItem = info.getMutableInput("lutName", String::class.java)
lutNameItem.choices = ArrayList(lutService.findLUTs().keys)
}

override fun run() {
node.metadata["sciview.colormapName"] = lutName
sciView.setColormap(node, colorTable)
// Trigger an update to the UI for the colormap
sciView.publishNode(node)
}
}
9 changes: 4 additions & 5 deletions src/main/kotlin/sc/iview/ui/SwingNodePropertyEditor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ class SwingNodePropertyEditor(private val sciView: SciView) : UIComponent<JPanel
private val updateLock = ReentrantLock()

/** Generates a properties panel for the given node. */
fun updateProperties(sceneNode: Node?) {
fun updateProperties(sceneNode: Node?, rebuild: Boolean = false) {
if (sceneNode == null) {
try {
if (updateLock.tryLock() || updateLock.tryLock(200, TimeUnit.MILLISECONDS)) {
Expand All @@ -295,7 +295,7 @@ class SwingNodePropertyEditor(private val sciView: SciView) : UIComponent<JPanel
}
try {
if (updateLock.tryLock() || updateLock.tryLock(200, TimeUnit.MILLISECONDS)) {
if (currentNode === sceneNode && currentProperties != null) {
if (!rebuild && currentNode === sceneNode && currentProperties != null) {
currentProperties!!.updateCommandFields()
inputPanel.refresh()
updateLock.unlock()
Expand Down Expand Up @@ -347,6 +347,7 @@ class SwingNodePropertyEditor(private val sciView: SciView) : UIComponent<JPanel
textArea.text = "<html><pre>$stackTrace</pre>"
updatePropertiesPanel(textArea)
}

updateLock.unlock()
}
} catch (e: InterruptedException) {
Expand Down Expand Up @@ -418,9 +419,7 @@ class SwingNodePropertyEditor(private val sciView: SciView) : UIComponent<JPanel
if (newPath != null) {
tree.selectionPath = newPath
tree.scrollPathToVisible(newPath)
if (node !== sciView.activeNode) {
updateProperties(node)
}
updateProperties(node)
}
}

Expand Down

0 comments on commit 498daee

Please sign in to comment.