diff --git a/orx-camera/src/commonMain/kotlin/Camera2D.kt b/orx-camera/src/commonMain/kotlin/Camera2D.kt index bbf910d8..0d572ec6 100644 --- a/orx-camera/src/commonMain/kotlin/Camera2D.kt +++ b/orx-camera/src/commonMain/kotlin/Camera2D.kt @@ -6,6 +6,7 @@ import org.openrndr.MouseEvents import org.openrndr.Program import org.openrndr.draw.Drawer import org.openrndr.draw.RenderTarget +import org.openrndr.draw.isolated import org.openrndr.events.Event import org.openrndr.math.Matrix44 import org.openrndr.math.Vector2 @@ -23,6 +24,8 @@ import org.openrndr.math.transforms.buildTransform class Camera2D : Extension, ChangeEvents { override var enabled = true + private lateinit var program: Program + private var controlInitialized = false var view = Matrix44.IDENTITY var rotationCenter = Vector2.ZERO @@ -38,6 +41,30 @@ class Camera2D : Extension, ChangeEvents { override val hasChanged: Boolean get() = dirty + + /** + * Executes the provided drawing function in an isolated scope, preserving the current + * drawing state and then restoring it after the function is executed. The `ortho` projection + * and custom view transformation are applied during the isolated drawing session. + * + * @param function the drawing function to be applied within the isolated scope of the `Drawer`. + */ + fun isolated(function: Drawer.() -> Unit) { + program.drawer.isolated { + program.drawer.ortho(RenderTarget.active) + + program.drawer.view = this@Camera2D.view + program.drawer.function() + } + } + + /** + * Configures the mouse interaction events for controlling the camera view and handling + * transformations such as translation, rotation, and scaling via mouse inputs. + * + * @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 @@ -76,10 +103,14 @@ class Camera2D : Extension, ChangeEvents { dirty = true } } + controlInitialized = true } override fun setup(program: Program) { - setupMouseEvents(program.mouse) + this.program = program + if (!controlInitialized) { + setupMouseEvents(program.mouse) + } } override fun beforeDraw(drawer: Drawer, program: Program) { @@ -94,4 +125,13 @@ class Camera2D : Extension, ChangeEvents { } } - +/** + * Creates a new instance of the Camera2D extension suitable for manual application. + * + * @return a configured Camera2D instance ready to be used with the calling Program. + */ +fun Program.Camera2DManual(): Camera2D { + val camera = Camera2D() + camera.setup(this) + return camera +} diff --git a/orx-camera/src/jvmDemo/kotlin/DemoCamera2DManual01.kt b/orx-camera/src/jvmDemo/kotlin/DemoCamera2DManual01.kt new file mode 100644 index 00000000..3da9bd80 --- /dev/null +++ b/orx-camera/src/jvmDemo/kotlin/DemoCamera2DManual01.kt @@ -0,0 +1,46 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.isolatedWithTarget +import org.openrndr.draw.loadFont +import org.openrndr.draw.renderTarget +import org.openrndr.extra.camera.Camera2D +import org.openrndr.extra.camera.Camera2DManual +import org.openrndr.math.Vector2 +import org.openrndr.math.transforms.transform +import org.openrndr.shape.Rectangle +import kotlin.math.sin + +/** + * Demonstrate the use of `Camera2DManual` for manual camera control. + * + * The application is configured with a 720x720 window size. Within the program, a custom camera (`Camera2DManual`) + * is initialized and used to create isolated drawing scopes. The `isolated` method is utilized to overlay different + * drawing operations while maintaining individual camera states, ensuring proper transformations for specific elements. + * + * A pink circle is drawn at the center of the canvas with varying radii using isolated and non-isolated camera states. + * The outermost and innermost circles are affected by the `Camera2DManual` isolated scope, while the middle circle + * is outside of the camera's isolated scope, creating a layered visual effect. + */ +fun main() = application { + configure { + width = 720 + height = 720 + } + + program { + val camera = Camera2DManual() + extend { + camera.isolated { + drawer.fill = ColorRGBa.PINK + drawer.circle(drawer.bounds.center, 300.0) + } + + drawer.circle(drawer.bounds.center, 200.0) + + camera.isolated { + drawer.fill = ColorRGBa.PINK + drawer.circle(drawer.bounds.center, 100.0) + } + } + } +}