[orx-camera] Introduce customizable camera defaults and control functions
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package org.openrndr.extra.camera
|
||||
|
||||
import org.openrndr.Extension
|
||||
import org.openrndr.KeyEvents
|
||||
import org.openrndr.MouseButton
|
||||
import org.openrndr.MouseEvents
|
||||
import org.openrndr.Program
|
||||
@@ -26,8 +27,35 @@ class Camera2D : Extension, ChangeEvents {
|
||||
|
||||
private lateinit var program: Program
|
||||
private var controlInitialized = false
|
||||
|
||||
/**
|
||||
* Represents the 4x4 transformation matrix of the camera view in a 2D drawing environment.
|
||||
* This matrix is used to apply custom transformations such as translation, rotation,
|
||||
* or scaling to the viewport. By default, it is set to the identity matrix.
|
||||
*
|
||||
* When modified, the `dirty` flag is automatically set to `true` to indicate
|
||||
* that the view matrix has been updated and subsequent transformations might
|
||||
* need recalculation or application.
|
||||
*/
|
||||
var view = Matrix44.IDENTITY
|
||||
set(value) {
|
||||
field = value
|
||||
dirty = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the center of rotation for the camera in 2D space.
|
||||
*
|
||||
* Changes to this property will mark the camera's state as dirty, necessitating
|
||||
* a re-calculation of the view transformation.
|
||||
*
|
||||
* Default value is [Vector2.ZERO].
|
||||
*/
|
||||
var rotationCenter = Vector2.ZERO
|
||||
set(value) {
|
||||
field = value
|
||||
dirty = true
|
||||
}
|
||||
|
||||
override val changed = Event<Unit>()
|
||||
|
||||
@@ -61,13 +89,90 @@ class Camera2D : Extension, ChangeEvents {
|
||||
|
||||
/**
|
||||
* Reinitialize the camera to its default state, where no transformations
|
||||
* (such as rotation, translation, or scaling) are applied. It also sets the `dirty` flag to `true`,
|
||||
* indicating that the camera's state has been modified and needs to be updated or rendered accordingly.
|
||||
* (such as rotation, translation, or scaling) are applied.
|
||||
*/
|
||||
fun defaults() {
|
||||
var defaults = {
|
||||
view = Matrix44.IDENTITY
|
||||
rotationCenter = Vector2.ZERO
|
||||
dirty = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a panning transformation to the camera view. The method modifies the current view
|
||||
* by translating it based on the provided displacement vector, effectively shifting the
|
||||
* camera's view in the scene.
|
||||
*
|
||||
* @param displacement the vector by which the camera view is translated.
|
||||
*/
|
||||
fun pan(displacement: Vector2) {
|
||||
view = buildTransform {
|
||||
translate(displacement)
|
||||
} * view
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the camera view by a specified angle around its rotation center.
|
||||
*
|
||||
* @param angle the angle in degrees by which the view is rotated.
|
||||
*/
|
||||
fun rotate(angle: Double) {
|
||||
view = buildTransform {
|
||||
translate(rotationCenter)
|
||||
rotate(angle)
|
||||
translate(-rotationCenter)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a zoom transformation to the camera view. The transformation is centered
|
||||
* around the specified point while adjusting the zoom level by the given factor.
|
||||
*
|
||||
* @param center The point in space around which the zoom transformation is centered.
|
||||
* @param factor The zoom factor, where values greater than 1.0 zoom in and values less than 1.0 zoom out.
|
||||
*/
|
||||
fun zoom(center: Vector2, factor: Double) {
|
||||
view = buildTransform {
|
||||
translate(center)
|
||||
scale(factor)
|
||||
translate(-center)
|
||||
} * view
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up and applies mouse and keyboard controls for interacting with the camera.
|
||||
* This variable provides event-driven logic to handle user input for panning, rotation, and zooming.
|
||||
*
|
||||
* - Mouse button interactions are used to configure the center of rotation and reset the view.
|
||||
* - Mouse drag events control panning and rotation with the left and right mouse buttons respectively.
|
||||
* - Mouse scrolling adjusts the zoom level based on the scroll direction and position.
|
||||
*
|
||||
* @param mouse an instance of `MouseEvents` providing data for mouse interactions,
|
||||
* such as button presses, movement, and scrolling.
|
||||
* @param keyboard an instance of `KeyEvents` providing the framework for handling keyboard inputs,
|
||||
* though currently unused in this implementation.
|
||||
*/
|
||||
var controls = { mouse: MouseEvents, keyboard: KeyEvents ->
|
||||
mouse.buttonDown.listen {
|
||||
rotationCenter = it.position
|
||||
if (it.button == MouseButton.CENTER) {
|
||||
defaults()
|
||||
}
|
||||
}
|
||||
mouse.dragged.listen {
|
||||
if (!it.propagationCancelled) {
|
||||
when (it.button) {
|
||||
MouseButton.LEFT -> pan(it.dragDisplacement)
|
||||
MouseButton.RIGHT -> rotate(it.dragDisplacement.x + it.dragDisplacement.y)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
mouse.scrolled.listen {
|
||||
if (!it.propagationCancelled) {
|
||||
val scaleFactor = 1.0 - it.rotation.y * 0.03
|
||||
zoom(it.position, scaleFactor)
|
||||
}
|
||||
}
|
||||
Unit
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,42 +182,9 @@ class Camera2D : Extension, ChangeEvents {
|
||||
* @param mouse the MouseEvents instance that provides mouse interaction data, including
|
||||
* button presses, dragging, and scrolling events.
|
||||
*/
|
||||
fun setupMouseEvents(mouse: MouseEvents) {
|
||||
mouse.buttonDown.listen {
|
||||
rotationCenter = it.position
|
||||
|
||||
if (it.button == MouseButton.CENTER) {
|
||||
defaults()
|
||||
}
|
||||
}
|
||||
mouse.dragged.listen {
|
||||
if (!it.propagationCancelled) {
|
||||
when (it.button) {
|
||||
MouseButton.LEFT -> view = buildTransform {
|
||||
translate(it.dragDisplacement)
|
||||
} * view
|
||||
|
||||
MouseButton.RIGHT -> view = buildTransform {
|
||||
translate(rotationCenter)
|
||||
rotate(it.dragDisplacement.x + it.dragDisplacement.y)
|
||||
translate(-rotationCenter)
|
||||
} * view
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
dirty = true
|
||||
}
|
||||
}
|
||||
mouse.scrolled.listen {
|
||||
if (!it.propagationCancelled) {
|
||||
val scaleFactor = 1.0 - it.rotation.y * 0.03
|
||||
view = buildTransform {
|
||||
translate(it.position)
|
||||
scale(scaleFactor)
|
||||
translate(-it.position)
|
||||
} * view
|
||||
dirty = true
|
||||
}
|
||||
fun setupControls(mouse: MouseEvents, keyboard: KeyEvents) {
|
||||
if (!controlInitialized) {
|
||||
controls(mouse, keyboard)
|
||||
}
|
||||
controlInitialized = true
|
||||
}
|
||||
@@ -120,8 +192,9 @@ class Camera2D : Extension, ChangeEvents {
|
||||
override fun setup(program: Program) {
|
||||
this.program = program
|
||||
if (!controlInitialized) {
|
||||
setupMouseEvents(program.mouse)
|
||||
setupControls(program.mouse, program.keyboard)
|
||||
}
|
||||
defaults()
|
||||
}
|
||||
|
||||
override fun beforeDraw(drawer: Drawer, program: Program) {
|
||||
@@ -137,12 +210,19 @@ class Camera2D : Extension, ChangeEvents {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the Camera2D extension suitable for manual application.
|
||||
* Creates and sets up a custom-configured Camera2D instance within a Program.
|
||||
*
|
||||
* @return a configured Camera2D instance ready to be used with the calling Program.
|
||||
* This function initializes a new Camera2D, applies the provided configuration block,
|
||||
* and sets it up with the current Program context for interactive 2D transformations
|
||||
* such as panning, rotating, and zooming.
|
||||
*
|
||||
* @param configure an optional configuration block where you can set up the Camera2D
|
||||
* instance (e.g., setting view or rotation center). The default is an empty block.
|
||||
* @return the configured Camera2D instance.
|
||||
*/
|
||||
fun Program.Camera2DManual(): Camera2D {
|
||||
fun Program.Camera2DManual(configure: Camera2D.() -> Unit = { }): Camera2D {
|
||||
val camera = Camera2D()
|
||||
camera.configure()
|
||||
camera.setup(this)
|
||||
return camera
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user