Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,29 @@ Should you experience any issues, [please try the latest development version](ht

![Overview of sciview's user interface](https://gblobscdn.gitbook.com/assets%2F-LqBCy3SBefXis0YnrcI%2F-MK5WLQvMLIvw2GF6Rn2%2F-MK5WMGzmSavDTwlGro2%2Fmain-cheatsheet.jpg?alt=media&token=70c82549-e939-4752-af12-1756492a5f01)

## API Features

### Custom Window Dimensions

SciView now supports setting custom window dimensions via API, which is essential for VR headsets that require specific resolutions:

```kotlin
// Create SciView with custom dimensions
val sciview = SciView.create(1920, 1080)

// Or resize an existing instance
sciview.setWindowSize(2880, 1700) // Example: Oculus Quest 2 resolution

// Query current dimensions
val (width, height) = sciview.getWindowSize()
```

This feature is particularly useful for:
- VR headset integration requiring exact resolutions
- Multi-monitor setups
- Creating screenshots or recordings at specific resolutions
- Kiosk or presentation modes

## Developers

[Kyle Harrington](https://kyleharrington.com), University of Idaho & [Ulrik Guenther](https://ulrik.is/writing), MPI-CBG
Expand Down
71 changes: 71 additions & 0 deletions src/main/kotlin/sc/iview/SciView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1866,6 +1866,57 @@ class SciView : SceneryBase, CalibratedRealInterval<CalibratedAxis> {
println(scijavaContext!!.serviceIndex)
}

/**
* Set the window dimensions of the sciview rendering window.
* This is essential for VR headsets that require specific resolutions.
*
* @param width The desired width of the window in pixels
* @param height The desired height of the window in pixels
* @return true if the window was successfully resized, false otherwise
*/
fun setWindowSize(width: Int, height: Int): Boolean {
if (width <= 0 || height <= 0) {
log.error("Window dimensions must be positive: width=$width, height=$height")
return false
}

try {
// Update internal dimensions
windowWidth = width
windowHeight = height

// Update the main window frame if it exists
if (mainWindow is SwingMainWindow) {
val swingWindow = mainWindow as SwingMainWindow
swingWindow.frame.setSize(width, height)

// Update the renderer dimensions
renderer?.let { r ->
r.reshape(width, height)
}

// Update camera aspect ratio
camera?.perspectiveCamera(50.0f, width, height, 0.1f, 1000.0f)
}

log.info("Window resized to ${width}x${height}")
return true
} catch (e: Exception) {
log.error("Failed to resize window: ${e.message}")
e.printStackTrace()
return false
}
}

/**
* Get the current window dimensions.
*
* @return a Pair containing the width and height of the window
*/
fun getWindowSize(): Pair<Int, Int> {
return Pair(windowWidth, windowHeight)
}

/**
* Return the color table corresponding to the [lutName]
* @param lutName a String represening an ImageJ style LUT name, like Fire.lut
Expand Down Expand Up @@ -1952,6 +2003,26 @@ class SciView : SceneryBase, CalibratedRealInterval<CalibratedAxis> {
val sciViewService = context.service(SciViewService::class.java)
return sciViewService.orCreateActiveSciView
}

/**
* Static launching method with custom window dimensions
*
* @param width The desired width of the window in pixels
* @param height The desired height of the window in pixels
* @return a newly created SciView with specified dimensions
*/
@JvmStatic
@Throws(Exception::class)
fun create(width: Int, height: Int): SciView {
xinitThreads()
val context = Context(ImageJService::class.java, SciJavaService::class.java, SCIFIOService::class.java)
val objectService = context.service(ObjectService::class.java)
objectService.addObject(Utils.SciviewStandalone())
val sciViewService = context.service(SciViewService::class.java)
val sciView = sciViewService.orCreateActiveSciView
sciView.setWindowSize(width, height)
return sciView
}

/**
* Static launching method
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/sc/iview/commands/MenuWeights.kt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ object MenuWeights {
const val DEMO_BASIC_IMAGEPLANE = 4.0
const val DEMO_BASIC_VOLUME = 6.0
const val DEMO_BASIC_POINTCLOUD = 7.0
const val DEMO_BASIC_CUSTOM_WINDOW = 8.0
// Demo/Animation
const val DEMO_ANIMATION_PARTICLE = 0.0
const val DEMO_ANIMATION_VOLUMETIMESERIES = 1.0
Expand Down
129 changes: 129 additions & 0 deletions src/main/kotlin/sc/iview/commands/demo/basic/CustomWindowSizeDemo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*-
* #%L
* Scenery-backed 3D visualization package for ImageJ.
* %%
* Copyright (C) 2016 - 2024 sciview developers.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package sc.iview.commands.demo.basic

import org.joml.Vector3f
import org.scijava.command.Command
import org.scijava.plugin.Menu
import org.scijava.plugin.Parameter
import org.scijava.plugin.Plugin
import org.scijava.util.ColorRGB
import sc.iview.SciView
import sc.iview.commands.MenuWeights

/**
* Demo to test custom window sizing API.
* Shows how to set custom window dimensions for VR or other specific display requirements.
*
* @author Kyle Harrington
*/
@Plugin(type = Command::class,
label = "Custom Window Size Demo",
menuRoot = "SciView",
menu = [Menu(label = "Demo", weight = MenuWeights.DEMO),
Menu(label = "Basic", weight = MenuWeights.DEMO_BASIC),
Menu(label = "Custom Window Size", weight = MenuWeights.DEMO_BASIC_CUSTOM_WINDOW)])
class CustomWindowSizeDemo : Command {
@Parameter
private lateinit var sciview: SciView

@Parameter(label = "Window Width", min = "100", max = "3840")
private var width: Int = 1920

@Parameter(label = "Window Height", min = "100", max = "2160")
private var height: Int = 1080

override fun run() {
// Get current window size
val (currentWidth, currentHeight) = sciview.getWindowSize()
println("Current window size: ${currentWidth}x${currentHeight}")

// Set new window size
println("Setting window size to ${width}x${height}...")
val success = sciview.setWindowSize(width, height)

if (success) {
println("Window successfully resized to ${width}x${height}")

// Add some demo content to visualize the new dimensions
sciview.addSphere(
position = Vector3f(0f, 0f, 0f),
radius = 1f,
color = ColorRGB(128, 255, 128)
) {
name = "Center Sphere"
}

// Add corner markers to show the viewport
val aspectRatio = width.toFloat() / height.toFloat()
val markerSize = 0.2f

// Top-left
sciview.addBox(
position = Vector3f(-aspectRatio * 2, 2f, -5f),
size = Vector3f(markerSize, markerSize, markerSize),
color = ColorRGB(255, 0, 0)
) {
name = "Top-Left Marker"
}

// Top-right
sciview.addBox(
position = Vector3f(aspectRatio * 2, 2f, -5f),
size = Vector3f(markerSize, markerSize, markerSize),
color = ColorRGB(0, 255, 0)
) {
name = "Top-Right Marker"
}

// Bottom-left
sciview.addBox(
position = Vector3f(-aspectRatio * 2, -2f, -5f),
size = Vector3f(markerSize, markerSize, markerSize),
color = ColorRGB(0, 0, 255)
) {
name = "Bottom-Left Marker"
}

// Bottom-right
sciview.addBox(
position = Vector3f(aspectRatio * 2, -2f, -5f),
size = Vector3f(markerSize, markerSize, markerSize),
color = ColorRGB(255, 255, 0)
) {
name = "Bottom-Right Marker"
}

// Center the camera
sciview.centerOnScene()
} else {
println("Failed to resize window")
}
}
}
Loading