From 26e9a4970e61182f85a770c98392d0587eef241f Mon Sep 17 00:00:00 2001 From: Ulrik Guenther Date: Wed, 12 Jun 2024 17:21:03 +0200 Subject: [PATCH] Properties: Improve documentation --- .../commands/edit/AtmosphereProperties.kt | 11 +++++-- .../sc/iview/commands/edit/BasicProperties.kt | 15 --------- .../commands/edit/BoundingGridProperties.kt | 6 ++++ .../iview/commands/edit/CameraProperties.kt | 1 + .../edit/InspectorInteractiveCommand.kt | 32 +++++++++++++++++-- .../sc/iview/commands/edit/LightProperties.kt | 6 ++-- .../sc/iview/commands/edit/LineProperties.kt | 1 + .../commands/edit/SlicingPlaneProperties.kt | 1 + ...ingInspectorInteractiveCommandExtension.kt | 11 +++++++ .../commands/edit/SwingVolumeProperties.kt | 6 ++++ .../commands/edit/TextBoardProperties.kt | 1 + .../iview/commands/edit/VolumeProperties.kt | 3 ++ .../sc/iview/ui/SwingNodePropertyEditor.kt | 2 +- 13 files changed, 74 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/sc/iview/commands/edit/AtmosphereProperties.kt b/src/main/kotlin/sc/iview/commands/edit/AtmosphereProperties.kt index 4a1f374c..0b7076ba 100644 --- a/src/main/kotlin/sc/iview/commands/edit/AtmosphereProperties.kt +++ b/src/main/kotlin/sc/iview/commands/edit/AtmosphereProperties.kt @@ -7,25 +7,32 @@ import org.scijava.plugin.Parameter import org.scijava.plugin.Plugin import org.scijava.widget.NumberWidget +/** + * Inspector panel for adjusting the properties of an [Atmosphere] [graphics.scenery.Node]. + */ @Plugin(type = Command::class, initializer = "initValues", visible = false) class AtmosphereProperties : InspectorInteractiveCommand() { - + /** Field for [Atmosphere.latitude]. */ @Parameter(label = "Latitude", style = NumberWidget.SPINNER_STYLE+"group:Atmosphere"+",format:0.0", min = "-90", max = "90", stepSize = "1", callback = "updateNodeProperties") private var atmosphereLatitude = 50f + /** Field negating [Atmosphere.isSunAnimated]. */ @Parameter(label = "Enable keybindings and manual control", style = "group:Atmosphere", callback = "updateNodeProperties", description = "Use key bindings for controlling the sun.\nCtrl + Arrow Keys = large increments.\nCtrl + Shift + Arrow keys = small increments.") private var isSunManual = false + /** Field for [Atmosphere.azimuth]. */ @Parameter(label = "Sun Azimuth", style = "group:Atmosphere" + ",format:0.0", callback = "updateNodeProperties", description = "Azimuth value of the sun in degrees", min = "0", max = "360", stepSize = "1") private var sunAzimuth = 180f + /** Field for [Atmosphere.elevation]. */ @Parameter(label = "Sun Elevation", style = "group:Atmosphere" + ",format:0.0", callback = "updateNodeProperties", description = "Elevation value of the sun in degrees", min = "-90", max = "90", stepSize = "1") private var sunElevation = 45f + /** Field for [Atmosphere.emissionStrength]. */ @Parameter(label = "Emission Strength", style = NumberWidget.SPINNER_STYLE+"group:Atmosphere"+",format:0.00", min = "0", max="10", stepSize = "0.1", callback = "updateNodeProperties") private var atmosphereStrength = 1f - + /** Updates this command fields with the node's current properties. */ override fun updateCommandFields() { val node = currentSceneNode as? Atmosphere ?: return diff --git a/src/main/kotlin/sc/iview/commands/edit/BasicProperties.kt b/src/main/kotlin/sc/iview/commands/edit/BasicProperties.kt index b6c959fa..8e718920 100644 --- a/src/main/kotlin/sc/iview/commands/edit/BasicProperties.kt +++ b/src/main/kotlin/sc/iview/commands/edit/BasicProperties.kt @@ -47,21 +47,6 @@ import java.util.* /** * A command for interactively editing a node's properties. * - * * TODO: If the list of sceneNode changes while this dialog is open, it may - * not be notified and thus, may cause strange behaviours. Furthermore, - * refreshing the list of choises does not work. :( - * * Todo: Change the order of the property items. Scene node must be on top, - * as the user selects here which object to manipulate. - * * Todo: As soon as object selection in Scenery itself works, the node - * pulldown may be removed entirely. - * - * To add new properties you need to do a few things: - * 1. Create a @Parameter for the variable and ensure the style has an appropriate group - * Note: I believe the group relates to the class name, but I'm confused about where that happens. - * 2. Add code to get the value from the node to updateCommandFields - * 3. Add code to set the value of the node to updateNodeProperties - * - * * @author Robert Haase, Scientific Computing Facility, MPI-CBG Dresden * @author Curtis Rueden * @author Kyle Harrington diff --git a/src/main/kotlin/sc/iview/commands/edit/BoundingGridProperties.kt b/src/main/kotlin/sc/iview/commands/edit/BoundingGridProperties.kt index 65f570ed..c316b4fa 100644 --- a/src/main/kotlin/sc/iview/commands/edit/BoundingGridProperties.kt +++ b/src/main/kotlin/sc/iview/commands/edit/BoundingGridProperties.kt @@ -8,14 +8,20 @@ import org.scijava.plugin.Parameter import org.scijava.plugin.Plugin import org.scijava.util.ColorRGB +/** + * Inspector panel for inspecting a [BoundingGrid]'s properties. + */ @Plugin(type = Command::class, initializer = "initValues", visible = false) class BoundingGridProperties : InspectorInteractiveCommand() { + /** Parameter for the [BoundingGrid.gridColor] */ @Parameter(label = "Grid Color", callback = "updateNodeProperties", style = "group:Grid") private var gridColor: ColorRGB? = null + /** Parameter for the [BoundingGrid.ticksOnly], determining whether to show a box or only ticks. */ @Parameter(label = "Ticks only", callback = "updateNodeProperties", style = "group:Grid") private var ticksOnly = false + /** Updates this command fields with the node's current properties. */ override fun updateCommandFields() { val node = currentSceneNode as? BoundingGrid ?: return diff --git a/src/main/kotlin/sc/iview/commands/edit/CameraProperties.kt b/src/main/kotlin/sc/iview/commands/edit/CameraProperties.kt index f9e57daf..41f57acb 100644 --- a/src/main/kotlin/sc/iview/commands/edit/CameraProperties.kt +++ b/src/main/kotlin/sc/iview/commands/edit/CameraProperties.kt @@ -11,6 +11,7 @@ class CameraProperties : InspectorInteractiveCommand() { @Parameter(label = "Active", required = false, callback = "updateNodeProperties", style = "group:Camera") private var active = false + /** Updates this command fields with the node's current properties. */ override fun updateCommandFields() { val node = currentSceneNode as? Camera ?: return diff --git a/src/main/kotlin/sc/iview/commands/edit/InspectorInteractiveCommand.kt b/src/main/kotlin/sc/iview/commands/edit/InspectorInteractiveCommand.kt index 8acd4d5f..3e3fb00b 100644 --- a/src/main/kotlin/sc/iview/commands/edit/InspectorInteractiveCommand.kt +++ b/src/main/kotlin/sc/iview/commands/edit/InspectorInteractiveCommand.kt @@ -13,6 +13,9 @@ import sc.iview.SciView import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.locks.ReentrantLock +/** + * Custom inspector panels, e.g. for particular [Node] types. + */ abstract class InspectorInteractiveCommand : InteractiveCommand() { @Parameter protected lateinit var sciView: SciView @@ -23,17 +26,24 @@ abstract class InspectorInteractiveCommand : InteractiveCommand() { @Parameter protected lateinit var log: LogService + /** Lock to indicated whether the command's fields or the node's properties are currently being updated. */ var fieldsUpdating = ReentrantLock() protected set + /** The current node the inspector is focused on. */ var currentSceneNode: Node? = null protected set + /** Updates this command fields with the node's current properties. */ abstract fun updateCommandFields() + + /** Updates current scene node properties to match command fields. */ protected abstract fun updateNodeProperties() + /** With this hash map, inspector panels can declare that they include a special version for a particular UI type. */ var hasExtensions = hashMapOf>() protected set + /** Updates the currently-active node for the inspector. */ fun setSceneNode(node: Node?) { Colormap.Companion.lutService = sciView.scijavaContext?.getService(LUTService::class.java) @@ -41,12 +51,16 @@ abstract class InspectorInteractiveCommand : InteractiveCommand() { updateCommandFields() } + /** + * Initializes the command's fields from a node's properties. + */ protected fun initValues() { -// rebuildSceneObjectChoiceList() -// refreshSceneNodeInDialog() updateCommandFields() } + /** + * Remove an input given by [name] of a certain [type]. + */ protected fun maybeRemoveInput(name: String, type: Class) { try { val item = info.getMutableInput(name, type) ?: return @@ -56,17 +70,26 @@ abstract class InspectorInteractiveCommand : InteractiveCommand() { } } + /** + * Adds a new [input] to a given [module]. + */ fun addInput(input: ModuleItem<*>, module: Module) { super.addInput(input) inputModuleMaps[input] = module } + /** + * Checks for custom module items based on [moduleInfo]. + */ fun getCustomModuleForModuleItem(moduleInfo: ModuleItem<*>): Module? { val custom = inputModuleMaps[moduleInfo] log.debug("Custom module found: $custom") return custom } + /** + * Companion object with helper constants. + */ companion object { const val PI_NEG = "-3.142" const val PI_POS = "3.142" @@ -74,6 +97,11 @@ abstract class InspectorInteractiveCommand : InteractiveCommand() { private val inputModuleMaps = ConcurrentHashMap, Module>() } + /** + * Data class for storing [condition]s for usage of a particular [commandClass]. This + * class is used to determine which [InspectorInteractiveCommand]s will be displayed + * for a particular [Node] type. + */ data class UsageCondition( val condition: (Node) -> Boolean, val commandClass: Class diff --git a/src/main/kotlin/sc/iview/commands/edit/LightProperties.kt b/src/main/kotlin/sc/iview/commands/edit/LightProperties.kt index 64f603e5..70282a3f 100644 --- a/src/main/kotlin/sc/iview/commands/edit/LightProperties.kt +++ b/src/main/kotlin/sc/iview/commands/edit/LightProperties.kt @@ -7,13 +7,15 @@ import org.scijava.plugin.Parameter import org.scijava.plugin.Plugin import org.scijava.widget.NumberWidget +/** + * Inspector panel for [PointLight]s. + */ @Plugin(type = Command::class, initializer = "initValues", visible = false) class LightProperties : InspectorInteractiveCommand() { - /* Light properties */ - @Parameter(label = "Intensity", style = NumberWidget.SPINNER_STYLE+ ",group:Lighting", stepSize = "0.1", callback = "updateNodeProperties") private var intensity = 0f + /** Updates this command fields with the node's current properties. */ override fun updateCommandFields() { val node = currentSceneNode as? PointLight ?: return diff --git a/src/main/kotlin/sc/iview/commands/edit/LineProperties.kt b/src/main/kotlin/sc/iview/commands/edit/LineProperties.kt index 98129b76..5e16566e 100644 --- a/src/main/kotlin/sc/iview/commands/edit/LineProperties.kt +++ b/src/main/kotlin/sc/iview/commands/edit/LineProperties.kt @@ -13,6 +13,7 @@ class LineProperties : InspectorInteractiveCommand() { @Parameter(label = "Edge width", callback = "updateNodeProperties", style = "group:Line") private var edgeWidth = 0 + /** Updates this command fields with the node's current properties. */ override fun updateCommandFields() { val node = currentSceneNode as? Line ?: return diff --git a/src/main/kotlin/sc/iview/commands/edit/SlicingPlaneProperties.kt b/src/main/kotlin/sc/iview/commands/edit/SlicingPlaneProperties.kt index 0cccd832..1ed19f13 100644 --- a/src/main/kotlin/sc/iview/commands/edit/SlicingPlaneProperties.kt +++ b/src/main/kotlin/sc/iview/commands/edit/SlicingPlaneProperties.kt @@ -21,6 +21,7 @@ class SlicingPlaneProperties : InspectorInteractiveCommand() { @Parameter(label = "Sliced volumes", callback = "updateNodeProperties", style = "group:Targets") private var slicedVolumes: VolumeSelectorWidget.VolumeSelection = VolumeSelectorWidget.VolumeSelection() + /** Updates this command fields with the node's current properties. */ override fun updateCommandFields() { val node = currentSceneNode as? SlicingPlane ?: return diff --git a/src/main/kotlin/sc/iview/commands/edit/SwingInspectorInteractiveCommandExtension.kt b/src/main/kotlin/sc/iview/commands/edit/SwingInspectorInteractiveCommandExtension.kt index 3680a7ff..7343cb7d 100644 --- a/src/main/kotlin/sc/iview/commands/edit/SwingInspectorInteractiveCommandExtension.kt +++ b/src/main/kotlin/sc/iview/commands/edit/SwingInspectorInteractiveCommandExtension.kt @@ -3,6 +3,17 @@ package sc.iview.commands.edit import graphics.scenery.Node import org.scijava.ui.swing.widget.SwingInputPanel +/** + * Interface for Swing-based extensions for [InspectorInteractiveCommand]. + */ interface SwingInspectorInteractiveCommandExtension { + + /** + * The create() function is called when the Swing panel is contstructed, with [inputPanel] being + * the panel under construction, to which Swing objects can be attached. [sceneNode] is the + * currently-selected [Node] for which the inspector is being constructed, and [uiDebug] is + * a debug flag which can be used to determine whether additional debug output or visualisation + * should be provided. + */ fun create(inputPanel: SwingInputPanel, sceneNode: Node, uiDebug: Boolean) } \ No newline at end of file diff --git a/src/main/kotlin/sc/iview/commands/edit/SwingVolumeProperties.kt b/src/main/kotlin/sc/iview/commands/edit/SwingVolumeProperties.kt index a4740c1b..35fff4eb 100644 --- a/src/main/kotlin/sc/iview/commands/edit/SwingVolumeProperties.kt +++ b/src/main/kotlin/sc/iview/commands/edit/SwingVolumeProperties.kt @@ -11,9 +11,15 @@ import sc.iview.ui.SwingNodePropertyEditor.Companion.maybeActivateDebug import java.awt.Dimension import javax.swing.JPanel +/** + * Inspector extension that provides a [TransferFunctionEditor] for [Volume] nodes. + */ class SwingVolumeProperties : SwingInspectorInteractiveCommandExtension { val logger by lazyLogger() + /** + * Creates a [TransferFunctionEditor] panel as child of the [inputPanel]'s Volume group. + */ override fun create(inputPanel: SwingInputPanel, sceneNode: Node, uiDebug: Boolean) { if(sceneNode !is Volume) { logger.error("Wrong node type for SwingVolumeProperties") diff --git a/src/main/kotlin/sc/iview/commands/edit/TextBoardProperties.kt b/src/main/kotlin/sc/iview/commands/edit/TextBoardProperties.kt index d99c8e95..95af242b 100644 --- a/src/main/kotlin/sc/iview/commands/edit/TextBoardProperties.kt +++ b/src/main/kotlin/sc/iview/commands/edit/TextBoardProperties.kt @@ -23,6 +23,7 @@ class TextBoardProperties : InspectorInteractiveCommand() { @Parameter(label = "Transparent Background", callback = "updateNodeProperties", style = "group:Text") private var transparentBackground = false + /** Updates this command fields with the node's current properties. */ override fun updateCommandFields() { val node = currentSceneNode as? TextBoard ?: return diff --git a/src/main/kotlin/sc/iview/commands/edit/VolumeProperties.kt b/src/main/kotlin/sc/iview/commands/edit/VolumeProperties.kt index a791c260..8cdb4d0d 100644 --- a/src/main/kotlin/sc/iview/commands/edit/VolumeProperties.kt +++ b/src/main/kotlin/sc/iview/commands/edit/VolumeProperties.kt @@ -54,9 +54,11 @@ class VolumeProperties : InspectorInteractiveCommand() { private var timeSeriesPlayer: Thread? = null init { + // For Swing, we have a special extension containing the TransferFunctionEditor. hasExtensions["Swing"] = SwingVolumeProperties::class.java } + /** Plays a volume time series, if the volume has more than one timepoint. */ fun playTimeSeries() { if (currentSceneNode !is Volume) { return @@ -94,6 +96,7 @@ class VolumeProperties : InspectorInteractiveCommand() { } + /** Updates this command fields with the node's current properties. */ @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") override fun updateCommandFields() { val node = currentSceneNode as? Volume ?: return diff --git a/src/main/kotlin/sc/iview/ui/SwingNodePropertyEditor.kt b/src/main/kotlin/sc/iview/ui/SwingNodePropertyEditor.kt index c65b45fa..380daf53 100644 --- a/src/main/kotlin/sc/iview/ui/SwingNodePropertyEditor.kt +++ b/src/main/kotlin/sc/iview/ui/SwingNodePropertyEditor.kt @@ -310,7 +310,7 @@ class SwingNodePropertyEditor(private val sciView: SciView, val nodeSpecificProp // we reverse here, so BasicProperties appears first val modules = classes.reversed().map { clz -> - val info = commandService.getCommand(clz) ?: throw RuntimeException("Unknown command: ${clz.simpleName}") + val info = commandService.getCommand(clz) ?: throw IllegalStateException("Unknown command: ${clz.simpleName}") val module = moduleService.createModule(info) resolveInjectedInputs(module) module.setInput("sciView", sciView)