diff --git a/.gitignore b/.gitignore index 95af1b44..fe3b002d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,11 @@ -.idea/ -.gradle/ -out/ -target/ -build/ -*.iml/ -gradle.properties -/hs_err_pid*.log -/gui-parameters/ -/ShaderError.glsl -/.kotlin -/.lwjgl +*.iml +.gradle /local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties +/.kotlin \ No newline at end of file diff --git a/CNAME b/CNAME deleted file mode 100644 index b3c682ac..00000000 --- a/CNAME +++ /dev/null @@ -1 +0,0 @@ -orx.openrndr.org \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index ce4b8fff..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,162 +0,0 @@ -# Contributing to ORX - -Thank you for your interest in contributing to ORX :-) - -This repository contains the OPENRNDR extras: a growing library of assorted data structures, algorithms and utilities to complement OPENRNDR. -Other repositories you can contribute to are the [core OPENRNDR](https://github.com/openrndr/openrndr/), -the [guide](https://github.com/openrndr/openrndr-guide/) and the [template](https://github.com/openrndr/openrndr-template/). - -Please read the [general information about contributing to OPENRNDR](https://github.com/openrndr/openrndr/blob/master/CONTRIBUTING.md). -This document focuses on specific details about the ORX repository. - -## How to build ORX - -[See the main readme](https://github.com/openrndr/orx/tree/master?tab=readme-ov-file#publish-and-use-local-builds-of-the-library-in-your-applications). - -## Overview - -There are two types of ORX extras: -- JVM only. Subfolders of `/orx-jvm/`. These run only on Desktop (not in web browsers). -- Multiplatform. Other `/orx-.../` folders. These run both on Desktop and web browsers. - -Each orx folder contains a `README.md`, a `build.gradle.kts` file and a `src` folder. -Please explore several orx directories to get a feel for how they look like. - -Gradle tasks are used to update the list of ORX'es in the root README.md, -and to update the list of demos in each ORX'es README.md. - -## Folder structure (JVM) - -``` -orx-magic/ -├── README.md -├── build.gradle.kts -└── src/ - ├── main/ - │ └── kotlin/ - │ └── Magic.kt - └── demo/ - └── kotlin/ - ├── DemoFoo01.kt - └── DemoBar01.kt -``` - -## Folder structure (multiplatform) - -``` -orx-magic/ -├── README.md -├── build.gradle.kts -└── src/ - ├── commonMain/kotlin/ - │ └── Magic.kt - ├── commonTest/kotlin/ - ├── jsMain/kotlin/ - ├── jsTest/kotlin/ - ├── jvmDemo/kotlin/ - │ ├── DemoFoo01.kt - │ └── DemoBar01.kt - ├── jvmMain/kotlin/ - └── jvmTest/kotlin/ -``` -Note that inside `src` only `commonMain` is required. - - -## ORX README.md - -Assuming you are creating an orx called `magic`, the readme should be formatted as follows: - -``` -# orx-magic - -One or more lines including a short description to display on the root README.md. -One or more lines including a short description to display on the root README.md. -One or more lines including a short description to display on the root README.md. - -Main content describing the usage of orx-magic goes here -... - - -``` - -1. Start with a markdown header with the name of the orx followed by an empty line. -2. One or more lines with a brief description to show on the root `README.md`, followed by an empty line. - (The `buildMainReadme` Gradle task will extract this description and update the root `README.md`). -3. A detailed description (a guide) of how to use the orx, possibly with code examples in code fences like - ```` - ```kotlin - //code example - ``` - ```` -4. If the orx includes demos (more below), running the `CollectScreenShots` Gradle task will append `` - to the readme followed by a list of automatically generated screenshots of the demos and links to their source code. - This is specially useful for orx'es that produce graphical output, but less so for orx'es that interface - with hardware (like `orx-midi`). - - -## ORX build.gradle.kts - -ORX `build.gradle.kts` files declare their dependencies and most follow the same structure. -Please explore various build files and find the simplest one that matches your use case. -Note that the JVM ones are somewhat simpler than the multiplatform ones. - -The `plugins` section includes either ``org.openrndr.extra.convention.`kotlin-multiplatform` `` or -``org.openrndr.extra.convention.`kotlin-jvm` `` depending on the orx type. - -### JVM - -The JVM build files declare separate dependencies for the orx itself (`implementation`) and for usage demos -(`demoImplementation`). -See an [example](https://github.com/openrndr/orx/blob/master/orx-jvm/orx-dnk3/build.gradle.kts). - -### Multiplatform - -The multiplatform build files may have blocks like `commonMain`, `commonTest`, `jvmTest`, `jvmDemo`, etc. to specify the dependencies for each case. See an [example](https://github.com/openrndr/orx/blob/master/orx-color/build.gradle.kts). - - -## I want to contribute to the documentation - -There are various places where you can contribute without writing code. It will be greatly -appreciated by others trying to learn about OPENRNDR. - -### Guide - -The [guide](https://guide.openrndr.org/) is the first contact with OPENRNDR for most users. -[Learn how to work on the guide](https://github.com/openrndr/openrndr-guide/blob/dev/contributing.md). - -### ORX API page - -The [ORX API page](https://orx.openrndr.org/) needs some love too. The content is automatically -extracted from comments written in ORX's source code. It goes like this: - -1. Fork the [ORX repo](https://github.com/openrndr/orx/), then clone your fork (so you - have a copy on your computer) and get familiar with OPENRNDR and ORX. -2. Find an undocumented section at https://orx.openrndr.org you want to explain. -3. Find the corresponding Kotlin file in your cloned repo and add missing comments. Read about - the [suggested style](https://developers.google.com/style). -4. Generate the API website locally to verify your changes look correct by running the following - command: `./gradlew dokkaGenerate -Dorg.gradle.jvmargs=-Xmx1536M`. This will create the - html documentation under `build/dokka/html/`. -5. Open the `build/dokka/html/index.html` in your web browser. If something looks off - tweak your comments. Note: the sidebar will be empty unless viewed through a web server. - You can launch one by running `python3 -m http.server --bind 127.0.0.1` in the html folder. -7. To continue improving the API go back to step 3, otherwise send a Pull Requests from your fork. - - -## Demos - -ORX'es often include a `jvmDemo` folder. This folder should contain small programs demonstrating -how the ORX can be used. When the build system runs the -[`CollectScreenShots`](buildSrc/src/main/kotlin/CollectScreenShots.kt) task, -the `SingleScreenshot()` extension will be injected into each program found inside the `jvmDemo` -folder, then executed. A PNG screenshot is saved and pushed into the [`media`](https://github.com/openrndr/orx/tree/media) brach. Finally, links to those PNG images are inserted into the README.md file of each ORX, -together with a link to the source code that produced the screenshot. - -This serves two purposes: it can be useful for the user to see images of what the ORX can produce, -while it can also be usefu to detect breaking changes (in case the demo fails to run, or produces a -blank image). - -## Gradle tasks - -* `CollectScreenShots` -* `buildMainReadme` diff --git a/LICENSE b/LICENSE deleted file mode 100644 index ab2fa95f..00000000 --- a/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -BSD 2-Clause License - -Copyright (c) 2019, OPENRNDR -All rights reserved. - -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 HOLDER 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. diff --git a/README.md b/README.md deleted file mode 100644 index 845a2dc9..00000000 --- a/README.md +++ /dev/null @@ -1,120 +0,0 @@ -# ORX (OPENRNDR EXTRA) - -[![ORX maven badge](https://img.shields.io/maven-central/v/org.openrndr.extra/orx-noise-jvm?style=flat&color=%23FFC0CB -)](https://mvnrepository.com/artifact/org.openrndr.extra) - -A growing library of assorted data structures, algorithms and utilities to -complement [OPENRNDR](https://github.com/openrndr/openrndr). -Multiplatform, unless they deal with hardware or depend on binary libraries. Those are JVM-only. - -Find an auto-generated API documentation page at https://orx.openrndr.org/. - -## Usage - -To make use of these extensions clone the [OPENRNDR template](https://github.com/openrndr/openrndr-template), uncomment the ones you need in its [build.gradle.kts](https://github.com/openrndr/openrndr-template/blob/master/build.gradle.kts) file, and reload Gradle. Cloning this repo is optional but useful to run the demos in each ORX folder, to study the source code, and to contribute to existing or new ORX'es. - - - -## Multiplatform - -| name                                     | description | -| --- | --- | -| [`orx-camera`](orx-camera/) | 2D and 3D cameras controllable via mouse and keyboard. | -| [`orx-color`](orx-color/) | Color spaces, palettes, histograms, named colors. | -| [`orx-composition`](orx-composition/) | Shape composition library | -| [`orx-compositor`](orx-compositor/) | Toolkit to make composite (layered) images using blend modes and filters. | -| [`orx-delegate-magic`](orx-delegate-magic/) | Collection of magical property delegators. For tracking variable change or interpolate towards the value of a variable. | -| [`orx-easing`](orx-easing/) | Easing functions for smooth animation or non-linear interpolation. | -| [`orx-envelopes`](orx-envelopes/) | ADSR (Attack, Decay, Sustain, Release) envelopes and tools. | -| [`orx-expression-evaluator`](orx-expression-evaluator/) | Tools to evaluate strings containing mathematical expressions. | -| [`orx-expression-evaluator-typed`](orx-expression-evaluator-typed/) | Tools to evaluate strings containing typed mathematical expressions. | -| [`orx-fcurve`](orx-fcurve/) | FCurves are 1 dimensional function curves constructed from 2D bezier functions. They are often used to control a property over time. `x` values don't have any units, but they often represent a duration in seconds. | -| [`orx-fft`](orx-fft/) | Simple forward and inverse FFT routine | -| [`orx-fx`](orx-fx/) | Ready-to-use GPU-based visual effects or filters. Most include [orx-parameters](https://github.com/openrndr/orx/tree/master/orx-parameters) annotations so they can be easily controlled via orx-gui. | -| [`orx-gradient-descent`](orx-gradient-descent/) | Finds equation inputs that output a minimum value: easy to use gradient descent based minimizer. | -| [`orx-hash-grid`](orx-hash-grid/) | 2D space partitioning for fast point queries. | -| [`orx-image-fit`](orx-image-fit/) | Draws an image ensuring it fits or covers the specified `Rectangle`. | -| [`orx-integral-image`](orx-integral-image/) | CPU and GPU-based implementation for integral images (summed area tables) | -| [`orx-interval-tree`](orx-interval-tree/) | For querying a data set containing time segments (start time and end time) when we need all entries containing a specific time value. Useful when creating a timeline. | -| [`orx-jumpflood`](orx-jumpflood/) | Calculates distance or direction fields from an image. GPU accelerated, 2D. Results are provided as an image. | -| [`orx-kdtree`](orx-kdtree/) | Fast search of points closest to the queried point in a data set. 2D, 3D and 4D. | -| [`orx-marching-squares`](orx-marching-squares/) | Tools for extracting contours from functions | -| [`orx-math`](orx-math/) | Mathematical utilities, including complex numbers, linear ranges, simplex ranges, matrices and radial basis functions (RBF). | -| [`orx-mesh-generators`](orx-mesh-generators/) | 3D-mesh generating functions and DSL. | -| [`orx-mesh-noise`](orx-mesh-noise/) | Generate random samples on the surface of a mesh ## Demos ### DemoMeshNoise01 | -| [`orx-no-clear`](orx-no-clear/) | Provides the classical "draw-without-clearing-the-screen" functionality. | -| [`orx-noise`](orx-noise/) | Randomness for every type of person: Perlin, uniform, value, simplex, fractal and many other types of noise. | -| [`orx-obj-loader`](orx-obj-loader/) | Simple loader and saver for Wavefront .obj 3D mesh files. | -| [`orx-palette`](orx-palette/) | Collections of color palettes and tools for interacting with them. | -| [`orx-parameters`](orx-parameters/) | Provides annotations and tools for turning Kotlin properties into introspectable parameters. Used by [`orx-gui`](../orx-jvm/orx-gui/README.md) to automatically generate user interfaces. | -| [`orx-property-watchers`](orx-property-watchers/) | Tools for setting up property watcher based pipelines | -| [`orx-quadtree`](orx-quadtree/) | A [Quadtree](https://en.wikipedia.org/wiki/Quadtree) is a spatial partioning tree structure meant to provide fast spatial queries such as nearest points within a range. | -| [`orx-shade-styles`](orx-shade-styles/) | Shader based fills and strokes, including various types of gradient fills. | -| [`orx-shader-phrases`](orx-shader-phrases/) | A library that provides a `#pragma import` statement for shaders. | -| [`orx-shapes`](orx-shapes/) | Collection of 2D shape generators and modifiers. | -| [`orx-svg`](orx-svg/) | SVG reader and writer library. | -| [`orx-temporal-blur`](orx-temporal-blur/) | Post-processing temporal-blur video effect. CPU intense, therefore not intended for use with the `ScreenRecorder` extension or other real-time uses. | -| [`orx-text-on-contour`](orx-text-on-contour/) | Writing texts on contours. | -| [`orx-text-writer`](orx-text-writer/) | Writing texts with layouts | -| [`orx-time-operators`](orx-time-operators/) | A collection of time-sensitive functions aimed at controlling raw data over-time, such as Envelope and LFO. | -| [`orx-timer`](orx-timer/) | Simple timer functionality providing `repeat`, to run code with a given interval and `timeOut`, to run code once after a given delay. | -| [`orx-triangulation`](orx-triangulation/) | **Delaunay** triangulation and **Voronoi** diagrams. | -| [`orx-turtle`](orx-turtle/) | Bezier (`ShapeContour`) backed [turtle graphics](https://en.wikipedia.org/wiki/Turtle_graphics). | -| [`orx-view-box`](orx-view-box/) | To create independent views inside one program window. | - -## JVM only - -| name                                     | description | -| --- | --- | -| [`orx-axidraw`](orx-jvm/orx-axidraw/) | GUI for configuring and plotting with an Axidraw pen-plotter. | -| [`orx-boofcv`](orx-jvm/orx-boofcv/) | Helper functions to ease working with the BoofCV computer vision library and its data types. | -| [`orx-chataigne`](orx-jvm/orx-chataigne/) | Expose variables to [Chataigne](http://benjamin.kuperberg.fr/chataigne/en) and any other applications that can interface with it. The current implementation makes use of the OSC protocol and supports `Double` and `ColorRGBa`. | -| [`orx-depth-camera-calibrator`](orx-jvm/orx-depth-camera-calibrator/) | Class to help callibrate depth and transformation matrices when using one or more depth cameras. | -| [`orx-dnk3`](orx-jvm/orx-dnk3/) | A scene graph based 3d renderer with support for Gltf based assets | -| [`orx-file-watcher`](orx-jvm/orx-file-watcher/) | Monitor files on disk and auto-reload them if they change. | -| [`orx-git-archiver`](orx-jvm/orx-git-archiver/) | An extension that hooks into `Program.requestAssets` to commit changed code to Git and provide filenames based on the commit hash. | -| [`orx-git-archiver-gradle`](orx-jvm/orx-git-archiver-gradle/) | A Gradle plugin that turns a git history and `screenshots` directory into a markdown file. | -| [`orx-gui`](orx-jvm/orx-gui/) | Automatic UI (sliders, buttons, etc.) generated from annotated classes and properties. Uses `orx-panel` and `orx-parameters`. | -| [`orx-keyframer`](orx-jvm/orx-keyframer/) | Create animated timelines by specifying properties and times in keyframes, then play it back at any speed (even backwards) automatically interpolating properties. Save, load, use mathematical expressions and callbacks. Powerful and highly reusable. | -| [`orx-kinect-v1`](orx-jvm/orx-kinect-v1/) | Support for the Kinect V1 RGB and depth cameras. | -| [`orx-midi`](orx-jvm/orx-midi/) | MIDI support for keyboards and controllers. Send and receive note and control change events. Bind inputs to variables. | -| [`orx-minim`](orx-jvm/orx-minim/) | Simplifies working with the Minim sound library. Provides sound synthesis and analysis. | -| [`orx-olive`](orx-jvm/orx-olive/) | Provides live coding functionality: updates a running OPENRNDR program when you save your changes. | -| [`orx-osc`](orx-jvm/orx-osc/) | Open Sound Control makes it possible to send and receive messages from other OSC enabled programs in the same or a different computer. Used to create multi-application or multi-device software. | -| [`orx-panel`](orx-jvm/orx-panel/) | The OPENRNDR UI toolkit. Provides buttons, sliders, text, a color picker and much more. HTML/CSS-like. | -| [`orx-poisson-fill`](orx-jvm/orx-poisson-fill/) | Post processing effect that fills transparent parts of the image interpolating the edge pixel colors. GPU-based. | -| [`orx-processing`](orx-jvm/orx-processing/) | orx-processing is a module designed to facilitate seamless type conversions between Processing's types and OPENRNDR's types. It provides utilities and methods that allow developers to integrate the two graphics frameworks effectively by bridging the gap between their respective data structures. | -| [`orx-rabbit-control`](orx-jvm/orx-rabbit-control/) | Creates a web-based remote UI to control your OPENRNDR program from a mobile device or a different computer. Alternative to `orx-gui`. | -| [`orx-syphon`](orx-jvm/orx-syphon/) | Send frames to- and from OPENRNDR to other applications in real time using _Syphon_ for Mac. | -| [`orx-video-profiles`](orx-jvm/orx-video-profiles/) | GIF, H265, PNG, Prores, TIFF and Webp `VideoWriterProfile`s for `ScreenRecorder` and `VideoWriter`. | - - -# Developer notes - -## Publish and use local builds of the library in your applications - -First, build and publish [OPENRNDR](https://github.com/openrndr/openrndr) to the local maven repository: - -Run (or import in IntelliJ IDEA and edit the run configuration). -```sh -# In openrndr repository -./gradlew publishToMavenLocal snapshot -``` - -This command will build and publish a snapshot of the next version of the library to your local maven repository. -The exact version will be shown in the console output during the build process. - -Now you can run the same command again but for this repository. - -```sh -# In orx repository -./gradlew publishToMavenLocal snapshot -``` - -It will automatically use the locally published snapshot of OPENRNDR for building ORX and will publish ORX to your local -maven repository with the same logic as before. - -Once that's done, you can use the local builds of OPENRNDR and ORX in -your [openrndr-template](https://github.com/openrndr/openrndr-template) by specifying the version that was published. - -Take a look at the [wiki](https://github.com/openrndr/openrndr/wiki/Building-OPENRNDR-and-ORX) for a more detailed walk-through. \ No newline at end of file diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 0dd296d4..4111140d 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -6,13 +6,13 @@ plugins { } android { - namespace = "com.icegps.orx" + namespace = "com.icegps.geotools" compileSdk { version = release(36) } defaultConfig { - applicationId = "com.icegps.orx" + applicationId = "com.icegps.geotools" minSdk = 28 targetSdk = 36 versionCode = 1 diff --git a/android/src/androidTest/java/com/icegps/orx/ExampleInstrumentedTest.kt b/android/src/androidTest/java/com/icegps/geotools/ExampleInstrumentedTest.kt similarity index 85% rename from android/src/androidTest/java/com/icegps/orx/ExampleInstrumentedTest.kt rename to android/src/androidTest/java/com/icegps/geotools/ExampleInstrumentedTest.kt index 2973c094..b9233505 100644 --- a/android/src/androidTest/java/com/icegps/orx/ExampleInstrumentedTest.kt +++ b/android/src/androidTest/java/com/icegps/geotools/ExampleInstrumentedTest.kt @@ -1,4 +1,4 @@ -package com.icegps.orx +package com.icegps.geotools import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -19,6 +19,6 @@ class ExampleInstrumentedTest { fun useAppContext() { // Context of the app under test. val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.icegps.orx", appContext.packageName) + assertEquals("com.icegps.geotools", appContext.packageName) } } \ No newline at end of file diff --git a/android/src/main/java/com/icegps/orx/ContoursManager.kt b/android/src/main/java/com/icegps/geotools/ContoursManager.kt similarity index 97% rename from android/src/main/java/com/icegps/orx/ContoursManager.kt rename to android/src/main/java/com/icegps/geotools/ContoursManager.kt index ba860cbc..541c54b2 100644 --- a/android/src/main/java/com/icegps/orx/ContoursManager.kt +++ b/android/src/main/java/com/icegps/geotools/ContoursManager.kt @@ -1,4 +1,4 @@ -package com.icegps.orx +package com.icegps.geotools import ColorBrewer2Type import android.content.Context @@ -7,13 +7,13 @@ import colorBrewer2Palettes import com.icegps.math.geometry.Rectangle import com.icegps.math.geometry.Vector2D import com.icegps.math.geometry.Vector3D -import com.icegps.orx.catmullrom.CatmullRomChain2 -import com.icegps.orx.ktx.area -import com.icegps.orx.ktx.toColorInt -import com.icegps.orx.ktx.toMapboxPoint -import com.icegps.orx.ktx.toast -import com.icegps.orx.marchingsquares.ShapeContour -import com.icegps.orx.marchingsquares.findContours +import com.icegps.geotools.catmullrom.CatmullRomChain2 +import com.icegps.geotools.ktx.area +import com.icegps.geotools.ktx.toColorInt +import com.icegps.geotools.ktx.toMapboxPoint +import com.icegps.geotools.ktx.toast +import com.icegps.geotools.marchingsquares.ShapeContour +import com.icegps.geotools.marchingsquares.findContours import com.icegps.shared.ktx.TAG import com.icegps.triangulation.DelaunayTriangulation import com.icegps.triangulation.Triangle diff --git a/android/src/main/java/com/icegps/orx/ControllableArrow.kt b/android/src/main/java/com/icegps/geotools/ControllableArrow.kt similarity index 98% rename from android/src/main/java/com/icegps/orx/ControllableArrow.kt rename to android/src/main/java/com/icegps/geotools/ControllableArrow.kt index 952ac154..dabb9cae 100644 --- a/android/src/main/java/com/icegps/orx/ControllableArrow.kt +++ b/android/src/main/java/com/icegps/geotools/ControllableArrow.kt @@ -1,8 +1,8 @@ -package com.icegps.orx +package com.icegps.geotools import com.icegps.math.geometry.Angle import com.icegps.math.geometry.Vector2D -import com.icegps.orx.ktx.toMapboxPoint +import com.icegps.geotools.ktx.toMapboxPoint import com.mapbox.geojson.Feature import com.mapbox.geojson.FeatureCollection import com.mapbox.geojson.LineString diff --git a/android/src/main/java/com/icegps/orx/CoordinateGenerator.kt b/android/src/main/java/com/icegps/geotools/CoordinateGenerator.kt similarity index 97% rename from android/src/main/java/com/icegps/orx/CoordinateGenerator.kt rename to android/src/main/java/com/icegps/geotools/CoordinateGenerator.kt index e4cae564..8fc533ad 100644 --- a/android/src/main/java/com/icegps/orx/CoordinateGenerator.kt +++ b/android/src/main/java/com/icegps/geotools/CoordinateGenerator.kt @@ -1,4 +1,4 @@ -package com.icegps.orx +package com.icegps.geotools import com.icegps.math.geometry.Angle import com.icegps.math.geometry.Vector3D diff --git a/android/src/main/java/com/icegps/orx/DisplaySlopeResult.kt b/android/src/main/java/com/icegps/geotools/DisplaySlopeResult.kt similarity index 98% rename from android/src/main/java/com/icegps/orx/DisplaySlopeResult.kt rename to android/src/main/java/com/icegps/geotools/DisplaySlopeResult.kt index 3f21e13d..3237be5a 100644 --- a/android/src/main/java/com/icegps/orx/DisplaySlopeResult.kt +++ b/android/src/main/java/com/icegps/geotools/DisplaySlopeResult.kt @@ -1,8 +1,8 @@ -package com.icegps.orx +package com.icegps.geotools import android.util.Log import com.icegps.math.geometry.Vector2D -import com.icegps.orx.ktx.toMapboxPoint +import com.icegps.geotools.ktx.toMapboxPoint import com.mapbox.geojson.Feature import com.mapbox.geojson.FeatureCollection import com.mapbox.geojson.Polygon diff --git a/android/src/main/java/com/icegps/orx/EarthworkManager.kt b/android/src/main/java/com/icegps/geotools/EarthworkManager.kt similarity index 99% rename from android/src/main/java/com/icegps/orx/EarthworkManager.kt rename to android/src/main/java/com/icegps/geotools/EarthworkManager.kt index 7dc5cd00..0ed2eb67 100644 --- a/android/src/main/java/com/icegps/orx/EarthworkManager.kt +++ b/android/src/main/java/com/icegps/geotools/EarthworkManager.kt @@ -1,4 +1,4 @@ -package com.icegps.orx +package com.icegps.geotools import android.graphics.PointF import android.util.Log diff --git a/android/src/main/java/com/icegps/orx/GridDisplay.kt b/android/src/main/java/com/icegps/geotools/GridDisplay.kt similarity index 98% rename from android/src/main/java/com/icegps/orx/GridDisplay.kt rename to android/src/main/java/com/icegps/geotools/GridDisplay.kt index 1e1257d1..43b120a7 100644 --- a/android/src/main/java/com/icegps/orx/GridDisplay.kt +++ b/android/src/main/java/com/icegps/geotools/GridDisplay.kt @@ -1,4 +1,4 @@ -package com.icegps.orx +package com.icegps.geotools import com.icegps.common.helper.GeoHelper import com.icegps.math.geometry.Vector2D diff --git a/android/src/main/java/com/icegps/orx/GridModel.kt b/android/src/main/java/com/icegps/geotools/GridModel.kt similarity index 99% rename from android/src/main/java/com/icegps/orx/GridModel.kt rename to android/src/main/java/com/icegps/geotools/GridModel.kt index eae4e661..300ad356 100644 --- a/android/src/main/java/com/icegps/orx/GridModel.kt +++ b/android/src/main/java/com/icegps/geotools/GridModel.kt @@ -1,4 +1,4 @@ -package com.icegps.orx +package com.icegps.geotools import com.icegps.math.geometry.Vector2D import com.icegps.math.geometry.Vector3D diff --git a/android/src/main/java/com/icegps/orx/MainActivity.kt b/android/src/main/java/com/icegps/geotools/MainActivity.kt similarity index 98% rename from android/src/main/java/com/icegps/orx/MainActivity.kt rename to android/src/main/java/com/icegps/geotools/MainActivity.kt index 91ee4fa1..144a0233 100644 --- a/android/src/main/java/com/icegps/orx/MainActivity.kt +++ b/android/src/main/java/com/icegps/geotools/MainActivity.kt @@ -1,4 +1,4 @@ -package com.icegps.orx +package com.icegps.geotools import android.os.Bundle import androidx.activity.enableEdgeToEdge @@ -10,8 +10,8 @@ import androidx.lifecycle.lifecycleScope import com.google.android.material.slider.RangeSlider import com.google.android.material.slider.Slider import com.icegps.common.helper.GeoHelper +import com.icegps.geotools.databinding.ActivityMainBinding import com.icegps.math.geometry.degrees -import com.icegps.orx.databinding.ActivityMainBinding import com.icegps.shared.model.GeoPoint import com.mapbox.geojson.Point import com.mapbox.maps.CameraOptions diff --git a/android/src/main/java/com/icegps/orx/MainViewModel.kt b/android/src/main/java/com/icegps/geotools/MainViewModel.kt similarity index 96% rename from android/src/main/java/com/icegps/orx/MainViewModel.kt rename to android/src/main/java/com/icegps/geotools/MainViewModel.kt index ccfba229..c33ff667 100644 --- a/android/src/main/java/com/icegps/orx/MainViewModel.kt +++ b/android/src/main/java/com/icegps/geotools/MainViewModel.kt @@ -1,4 +1,4 @@ -package com.icegps.orx +package com.icegps.geotools import android.app.Application import android.util.Log @@ -6,7 +6,7 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.icegps.common.helper.GeoHelper import com.icegps.math.geometry.Vector3D -import com.icegps.orx.ktx.toast +import com.icegps.geotools.ktx.toast import com.icegps.shared.SharedHttpClient import com.icegps.shared.SharedJson import com.icegps.shared.api.OpenElevation diff --git a/android/src/main/java/com/icegps/orx/PolygonTest.kt b/android/src/main/java/com/icegps/geotools/PolygonTest.kt similarity index 98% rename from android/src/main/java/com/icegps/orx/PolygonTest.kt rename to android/src/main/java/com/icegps/geotools/PolygonTest.kt index 6b1622a0..223f0635 100644 --- a/android/src/main/java/com/icegps/orx/PolygonTest.kt +++ b/android/src/main/java/com/icegps/geotools/PolygonTest.kt @@ -1,9 +1,9 @@ -package com.icegps.orx +package com.icegps.geotools import android.graphics.Color import com.icegps.common.helper.GeoHelper import com.icegps.math.geometry.Vector3D -import com.icegps.orx.ktx.toMapboxPoint +import com.icegps.geotools.ktx.toMapboxPoint import com.mapbox.geojson.Feature import com.mapbox.geojson.FeatureCollection import com.mapbox.geojson.LineString diff --git a/android/src/main/java/com/icegps/orx/PolylineManager.kt b/android/src/main/java/com/icegps/geotools/PolylineManager.kt similarity index 97% rename from android/src/main/java/com/icegps/orx/PolylineManager.kt rename to android/src/main/java/com/icegps/geotools/PolylineManager.kt index ec1a2a0f..6ae531e4 100644 --- a/android/src/main/java/com/icegps/orx/PolylineManager.kt +++ b/android/src/main/java/com/icegps/geotools/PolylineManager.kt @@ -1,9 +1,9 @@ -package com.icegps.orx +package com.icegps.geotools import android.graphics.Color import com.icegps.math.geometry.Line3D import com.icegps.math.geometry.Vector3D -import com.icegps.orx.ktx.toMapboxPoint +import com.icegps.geotools.ktx.toMapboxPoint import com.mapbox.geojson.Feature import com.mapbox.geojson.FeatureCollection import com.mapbox.geojson.LineString diff --git a/android/src/main/java/com/icegps/orx/RayCastingAlgorithm.kt b/android/src/main/java/com/icegps/geotools/RayCastingAlgorithm.kt similarity index 97% rename from android/src/main/java/com/icegps/orx/RayCastingAlgorithm.kt rename to android/src/main/java/com/icegps/geotools/RayCastingAlgorithm.kt index 2fff7006..bb81f3cb 100644 --- a/android/src/main/java/com/icegps/orx/RayCastingAlgorithm.kt +++ b/android/src/main/java/com/icegps/geotools/RayCastingAlgorithm.kt @@ -1,4 +1,4 @@ -package com.icegps.orx +package com.icegps.geotools import com.icegps.math.geometry.Vector2D import com.icegps.math.geometry.Vector3D diff --git a/android/src/main/java/com/icegps/orx/SimplePalette.kt b/android/src/main/java/com/icegps/geotools/SimplePalette.kt similarity index 99% rename from android/src/main/java/com/icegps/orx/SimplePalette.kt rename to android/src/main/java/com/icegps/geotools/SimplePalette.kt index 2b1f55c0..9e41de09 100644 --- a/android/src/main/java/com/icegps/orx/SimplePalette.kt +++ b/android/src/main/java/com/icegps/geotools/SimplePalette.kt @@ -1,4 +1,4 @@ -package com.icegps.orx +package com.icegps.geotools import android.util.Log diff --git a/android/src/main/java/com/icegps/orx/catmullrom/CatmullRom.kt b/android/src/main/java/com/icegps/geotools/catmullrom/CatmullRom.kt similarity index 96% rename from android/src/main/java/com/icegps/orx/catmullrom/CatmullRom.kt rename to android/src/main/java/com/icegps/geotools/catmullrom/CatmullRom.kt index 3a093f77..90ac451f 100644 --- a/android/src/main/java/com/icegps/orx/catmullrom/CatmullRom.kt +++ b/android/src/main/java/com/icegps/geotools/catmullrom/CatmullRom.kt @@ -1,8 +1,8 @@ -package com.icegps.orx.catmullrom +package com.icegps.geotools.catmullrom import com.icegps.math.geometry.Vector2D -import com.icegps.orx.marchingsquares.Segment2D -import com.icegps.orx.marchingsquares.ShapeContour +import com.icegps.geotools.marchingsquares.Segment2D +import com.icegps.geotools.marchingsquares.ShapeContour import kotlin.math.min import kotlin.math.pow diff --git a/android/src/main/java/com/icegps/orx/color/ColorRGBa.kt b/android/src/main/java/com/icegps/geotools/color/ColorRGBa.kt similarity index 99% rename from android/src/main/java/com/icegps/orx/color/ColorRGBa.kt rename to android/src/main/java/com/icegps/geotools/color/ColorRGBa.kt index cd53a619..1f667d36 100644 --- a/android/src/main/java/com/icegps/orx/color/ColorRGBa.kt +++ b/android/src/main/java/com/icegps/geotools/color/ColorRGBa.kt @@ -1,4 +1,4 @@ -package com.icegps.orx.color +package com.icegps.geotools.color import com.icegps.math.geometry.Vector3D import com.icegps.math.geometry.Vector4D diff --git a/android/src/main/java/com/icegps/orx/colorbrewer2/ColorBrewer2.kt b/android/src/main/java/com/icegps/geotools/colorbrewer2/ColorBrewer2.kt similarity index 99% rename from android/src/main/java/com/icegps/orx/colorbrewer2/ColorBrewer2.kt rename to android/src/main/java/com/icegps/geotools/colorbrewer2/ColorBrewer2.kt index 72881db9..2c28d0b3 100644 --- a/android/src/main/java/com/icegps/orx/colorbrewer2/ColorBrewer2.kt +++ b/android/src/main/java/com/icegps/geotools/colorbrewer2/ColorBrewer2.kt @@ -1,5 +1,5 @@ -import com.icegps.orx.color.ColorRGBa -import com.icegps.orx.color.rgb +import com.icegps.geotools.color.ColorRGBa +import com.icegps.geotools.color.rgb /** * # ColorBrewer2 diff --git a/android/src/main/java/com/icegps/orx/ktx/ColorRGBa.kt b/android/src/main/java/com/icegps/geotools/ktx/ColorRGBa.kt similarity index 88% rename from android/src/main/java/com/icegps/orx/ktx/ColorRGBa.kt rename to android/src/main/java/com/icegps/geotools/ktx/ColorRGBa.kt index 05f8c638..a806bd01 100644 --- a/android/src/main/java/com/icegps/orx/ktx/ColorRGBa.kt +++ b/android/src/main/java/com/icegps/geotools/ktx/ColorRGBa.kt @@ -1,6 +1,6 @@ -package com.icegps.orx.ktx +package com.icegps.geotools.ktx -import com.icegps.orx.color.ColorRGBa +import com.icegps.geotools.color.ColorRGBa /** * @author tabidachinokaze diff --git a/android/src/main/java/com/icegps/orx/ktx/Context.kt b/android/src/main/java/com/icegps/geotools/ktx/Context.kt similarity index 88% rename from android/src/main/java/com/icegps/orx/ktx/Context.kt rename to android/src/main/java/com/icegps/geotools/ktx/Context.kt index 8dc5c8d3..14a919d0 100644 --- a/android/src/main/java/com/icegps/orx/ktx/Context.kt +++ b/android/src/main/java/com/icegps/geotools/ktx/Context.kt @@ -1,4 +1,4 @@ -package com.icegps.orx.ktx +package com.icegps.geotools.ktx import android.content.Context import android.widget.Toast diff --git a/android/src/main/java/com/icegps/orx/ktx/Vector2D.kt b/android/src/main/java/com/icegps/geotools/ktx/Vector2D.kt similarity index 96% rename from android/src/main/java/com/icegps/orx/ktx/Vector2D.kt rename to android/src/main/java/com/icegps/geotools/ktx/Vector2D.kt index 327f98e4..d9299900 100644 --- a/android/src/main/java/com/icegps/orx/ktx/Vector2D.kt +++ b/android/src/main/java/com/icegps/geotools/ktx/Vector2D.kt @@ -1,4 +1,4 @@ -package com.icegps.orx.ktx +package com.icegps.geotools.ktx import com.icegps.common.helper.GeoHelper import com.icegps.math.geometry.Vector2D diff --git a/android/src/main/java/com/icegps/orx/ktx/Vector3D.kt b/android/src/main/java/com/icegps/geotools/ktx/Vector3D.kt similarity index 96% rename from android/src/main/java/com/icegps/orx/ktx/Vector3D.kt rename to android/src/main/java/com/icegps/geotools/ktx/Vector3D.kt index 9dd4b0f4..235f5956 100644 --- a/android/src/main/java/com/icegps/orx/ktx/Vector3D.kt +++ b/android/src/main/java/com/icegps/geotools/ktx/Vector3D.kt @@ -1,4 +1,4 @@ -package com.icegps.orx.ktx +package com.icegps.geotools.ktx import com.icegps.common.helper.GeoHelper import com.icegps.math.geometry.Rectangle diff --git a/android/src/main/java/com/icegps/orx/marchingsquares/MarchingSquares.kt b/android/src/main/java/com/icegps/geotools/marchingsquares/MarchingSquares.kt similarity index 99% rename from android/src/main/java/com/icegps/orx/marchingsquares/MarchingSquares.kt rename to android/src/main/java/com/icegps/geotools/marchingsquares/MarchingSquares.kt index ea1e79e3..a1340035 100644 --- a/android/src/main/java/com/icegps/orx/marchingsquares/MarchingSquares.kt +++ b/android/src/main/java/com/icegps/geotools/marchingsquares/MarchingSquares.kt @@ -1,9 +1,9 @@ -package com.icegps.orx.marchingsquares +package com.icegps.geotools.marchingsquares import com.icegps.math.geometry.Rectangle import com.icegps.math.geometry.Vector2D import com.icegps.math.geometry.Vector2I -import com.icegps.orx.ktx.mix +import com.icegps.geotools.ktx.mix import kotlin.math.max import kotlin.math.min diff --git a/android/src/test/java/com/icegps/orx/ExampleUnitTest.kt b/android/src/test/java/com/icegps/geotools/ExampleUnitTest.kt similarity index 91% rename from android/src/test/java/com/icegps/orx/ExampleUnitTest.kt rename to android/src/test/java/com/icegps/geotools/ExampleUnitTest.kt index e7a921d3..f7addbd8 100644 --- a/android/src/test/java/com/icegps/orx/ExampleUnitTest.kt +++ b/android/src/test/java/com/icegps/geotools/ExampleUnitTest.kt @@ -1,4 +1,4 @@ -package com.icegps.orx +package com.icegps.geotools import org.junit.Test diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts deleted file mode 100644 index 8156a89b..00000000 --- a/build-logic/build.gradle.kts +++ /dev/null @@ -1,7 +0,0 @@ -plugins { - `kotlin-dsl` -} - -repositories { - gradlePluginPortal() -} \ No newline at end of file diff --git a/build-logic/orx-convention/build.gradle.kts b/build-logic/orx-convention/build.gradle.kts deleted file mode 100644 index 127fc548..00000000 --- a/build-logic/orx-convention/build.gradle.kts +++ /dev/null @@ -1,24 +0,0 @@ -plugins { - `kotlin-dsl` -} - -val preload: SourceSet by project.sourceSets.creating - -repositories { - mavenCentral() - mavenLocal() -} -val libs = extensions.getByType().named("libs") -dependencies { - implementation(project(":orx-variant-plugin")) - implementation(libs.findLibrary("kotlin-gradle-plugin").get()) - implementation(libs.findLibrary("dokka-gradle-plugin").get()) - "preloadImplementation"(openrndr.application.core) - "preloadImplementation"(openrndr.orextensions) -} -kotlin { - compilerOptions { - freeCompilerArgs.add("-Xskip-metadata-version-check") - } -} -tasks.getByName("compileKotlin").dependsOn("compilePreloadKotlin") diff --git a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/CollectScreenShots.kt b/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/CollectScreenShots.kt deleted file mode 100644 index d23059fd..00000000 --- a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/CollectScreenShots.kt +++ /dev/null @@ -1,182 +0,0 @@ -import org.gradle.api.DefaultTask -import org.gradle.api.Project -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.FileCollection -import org.gradle.api.file.FileType -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.* -import org.gradle.kotlin.dsl.register -import org.gradle.process.ExecOperations -import org.gradle.work.InputChanges -import java.io.File -import java.net.URLClassLoader -import javax.inject.Inject - -private class CustomClassLoader(parent: ClassLoader) : ClassLoader(parent) { - fun findClass(file: File): Class<*> = defineClass(null, file.readBytes(), 0, file.readBytes().size) -} - -abstract class CollectScreenshotsTask @Inject constructor() : DefaultTask() { - @get:InputDirectory - @get:PathSensitive(PathSensitivity.NAME_ONLY) - @get:SkipWhenEmpty - abstract val inputDir: DirectoryProperty - - @get:InputFiles - abstract val runtimeDependencies: Property - - @get:OutputDirectory - abstract val outputDir: DirectoryProperty - - @get:Input - @get:Optional - abstract val ignore: ListProperty - - @get:Inject - abstract val execOperations: ExecOperations - - @TaskAction - fun execute(inputChanges: InputChanges) { - val preloadClass = File(project.rootProject.projectDir, "build-logic/orx-convention/build/classes/kotlin/preload") - require(preloadClass.exists()) { - "preload class not found: '${preloadClass.absolutePath}'" - } - - // Execute demos and produce PNG files - inputChanges.getFileChanges(inputDir).forEach { change -> - if (change.fileType == FileType.DIRECTORY) return@forEach - if (change.file.extension == "class") { - var klassName = change.file.nameWithoutExtension - if (klassName.dropLast(2) in ignore.get()) { - return@forEach - } - try { - val cp = (runtimeDependencies.get().map { it.toURI().toURL() } + inputDir.get().asFile.toURI() - .toURL()).toTypedArray() - val ucl = URLClassLoader(cp) - val ccl = CustomClassLoader(ucl) - val tempClass = ccl.findClass(change.file) - klassName = tempClass.name - val klass = ucl.loadClass(klassName) - klass.getMethod("main") - } catch (e: NoSuchMethodException) { - return@forEach - } - - println("Collecting screenshot for $klassName") - val imageName = klassName.replace(".", "-") - val pngFile = "${outputDir.get().asFile}/$imageName.png" - - fun launchDemoProgram() { - execOperations.javaexec { - this.classpath += project.files(inputDir.get().asFile, preloadClass) - this.classpath += runtimeDependencies.get() - this.mainClass.set(klassName) - this.workingDir(project.rootProject.projectDir) - this.jvmArgs( - "-DtakeScreenshot=true", - "-DscreenshotPath=$pngFile", - "-Dorg.openrndr.exceptions=JVM", - "-Dorg.openrndr.gl3.debug=true", - "-Dorg.openrndr.gl3.delete_angle_on_exit=false" - ) - } - } - - // A. Create an empty image for quick tests - //File(pngFile).createNewFile() - - // B. Create an actual image by running a demo program - runCatching { - launchDemoProgram() - }.onFailure { - println("Retrying $klassName after error: ${it.message}") - Thread.sleep(5000) - launchDemoProgram() - } - } - } - - // List produced PNG images. - // Only executed if there are changes in the inputDir. - val demoImageBaseNames = outputDir.get().asFile.listFiles { file: File -> - file.extension == "png" - }!!.sortedBy { it.absolutePath.lowercase() }.map { it.nameWithoutExtension } - - // Update readme.md using the found PNG images - val readme = File(project.projectDir, "README.md") - if (readme.exists()) { - var readmeLines = readme.readLines().toMutableList() - val screenshotsLine = readmeLines.indexOfFirst { it == "" } - if (screenshotsLine != -1) { - readmeLines = readmeLines.subList(0, screenshotsLine) - } - readmeLines.add("") - readmeLines.add("## Demos") - - val isKotlinMultiplatform = project.plugins.hasPlugin("org.jetbrains.kotlin.multiplatform") - val demoModuleName = if (isKotlinMultiplatform) "jvmDemo" else "demo" - - for (demoImageBaseName in demoImageBaseNames) { - val projectPath = project.projectDir.relativeTo(project.rootDir) - - // val url = "" // for local testing - val url = "https://raw.githubusercontent.com/openrndr/orx/media/$projectPath/" - - val imagePath = demoImageBaseName.dropLast(2).replace("-", "/") - val ktFilePath = "src/$demoModuleName/kotlin/$imagePath.kt" - val ktFile = File("$projectPath/$ktFilePath") - - val description = if (ktFile.isFile) { - val codeLines = ktFile.readLines() - val main = codeLines.indexOfFirst { it.startsWith("fun main") } - val head = codeLines.take(main) - val start = head.indexOfLast { it.startsWith("/*") } - val end = head.indexOfLast { it.endsWith("*/") } - - if ((start < end) && (end < main)) { - codeLines.subList(start + 1, end).joinToString("\n") { line -> - val trimmed = line.trimStart(' ', '*') - if(trimmed.startsWith("@see")) "" else trimmed - } - } else { - println("/** comment */ missing in $projectPath/$ktFilePath") - "" - } - } else "" - - readmeLines.add( - """ - |### $imagePath - | - |$description - | - |![$demoImageBaseName](${url}images/$demoImageBaseName.png) - | - |[source code]($ktFilePath) - | - """.trimMargin() - ) - } - readme.delete() - readme.writeText(readmeLines.joinToString("\n")) - } - } -} - -object ScreenshotsHelper { - fun collectScreenshots( - project: Project, - sourceSet: SourceSet, - config: CollectScreenshotsTask.() -> Unit - ): CollectScreenshotsTask { - val task = project.tasks.register("collectScreenshots").get() - task.outputDir.set(project.file(project.projectDir.toString() + "/images")) - task.inputDir.set(File(project.layout.buildDirectory.get().asFile, "classes/kotlin/${sourceSet.name}")) - task.runtimeDependencies.set(sourceSet.runtimeClasspath) - task.config() - task.dependsOn(sourceSet.output) - return task - } -} diff --git a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/EmbedShaders.kt b/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/EmbedShaders.kt deleted file mode 100644 index cba188c2..00000000 --- a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/EmbedShaders.kt +++ /dev/null @@ -1,67 +0,0 @@ -import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.FileType -import org.gradle.api.provider.Property -import org.gradle.api.tasks.* -import org.gradle.work.ChangeType -import org.gradle.work.Incremental -import org.gradle.work.InputChanges -import org.gradle.workers.WorkerExecutor -import javax.inject.Inject - -abstract class EmbedShadersTask : DefaultTask() { - @get:Incremental - @get:PathSensitive(PathSensitivity.NAME_ONLY) - @get:InputDirectory - abstract val inputDir: DirectoryProperty - - @get:OutputDirectory - abstract val outputDir: DirectoryProperty - - @get:Input - abstract val defaultPackage: Property - - @get:Input - abstract val defaultVisibility: Property - - @get:Input - abstract val namePrefix: Property - - @Inject - abstract fun getWorkerExecutor(): WorkerExecutor - - init { - defaultVisibility.set("") - namePrefix.set("") - } - - @TaskAction - fun execute(inputChanges: InputChanges) { - - inputChanges.getFileChanges(inputDir).forEach { change -> - if (change.fileType == FileType.DIRECTORY) return@forEach - val name = "${namePrefix.get()}${change.file.nameWithoutExtension.replace("-", "_")}" - val targetFile = outputDir.file(change.normalizedPath.replace(".", "_") + ".kt").get().asFile - if (change.changeType == ChangeType.REMOVED) { - targetFile.delete() - } else { - val contents = change.file.readText() - val lines = contents.split("\n") - var packageStatement = "package ${defaultPackage.get()}\n" - val visibilityStatement = - if (defaultVisibility.get().isNotBlank()) "${defaultVisibility.get()} " else "" - - val r = Regex("#pragma package ([a-z.]+)") - for (line in lines) { - val m = r.find(line.trim()) - if (m != null) { - packageStatement = "package ${m.groupValues[1]}\n" - } - } - val text = - "${packageStatement}${visibilityStatement}const val $name = ${"\"\"\""}${contents}${"\"\"\""}" - targetFile.writeText(text) - } - } - } -} diff --git a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/Utils.kt b/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/Utils.kt deleted file mode 100644 index eca448a1..00000000 --- a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/Utils.kt +++ /dev/null @@ -1,21 +0,0 @@ -package org.openrndr.extra.convention - -import org.gradle.api.Project -import org.gradle.kotlin.dsl.named -import org.gradle.nativeplatform.MachineArchitecture -import org.gradle.nativeplatform.OperatingSystemFamily -import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform - -val currentOperatingSystemName: String = DefaultNativePlatform.getCurrentOperatingSystem().toFamilyName() -val currentArchitectureName: String = DefaultNativePlatform.getCurrentArchitecture().name - -fun Project.addHostMachineAttributesToRuntimeConfigurations() { - configurations.matching { - it.name.endsWith("runtimeClasspath", ignoreCase = true) - }.configureEach { - attributes { - attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, objects.named(currentOperatingSystemName)) - attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, objects.named(currentArchitectureName)) - } - } -} \ No newline at end of file diff --git a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/component-metadata-rule.gradle.kts b/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/component-metadata-rule.gradle.kts deleted file mode 100644 index 18905da5..00000000 --- a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/component-metadata-rule.gradle.kts +++ /dev/null @@ -1,4 +0,0 @@ -package org.openrndr.extra.convention - -addHostMachineAttributesToRuntimeConfigurations() - diff --git a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/dokka.gradle.kts b/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/dokka.gradle.kts deleted file mode 100644 index b0692cc4..00000000 --- a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/dokka.gradle.kts +++ /dev/null @@ -1,38 +0,0 @@ -package org.openrndr.extra.convention - -plugins { - id("org.jetbrains.dokka") -} - -repositories { - mavenCentral() -} - -dokka { - pluginsConfiguration.html { - customStyleSheets.from(rootProject.file("dokka/styles/extra.css")) - customAssets.from(rootProject.file("dokka/images/logo-icon.svg")) - } - dokkaSourceSets.configureEach { - skipDeprecated.set(false) - - val sourcesDirectory = try { - file("src/$name/kotlin", PathValidation.EXISTS) - } catch (_: InvalidUserDataException) { - return@configureEach - } - - // Specifies the location of the project source code on the Web. - // If provided, Dokka generates "source" links for each declaration. - sourceLink { - // Unix based directory relative path to the root of the project (where you execute gradle respectively). - localDirectory = sourcesDirectory - - // URL showing where the source code can be accessed through the web browser - remoteUrl("https://github.com/openrndr/orx/blob/master/${moduleName.get()}/src/$name/kotlin") - - // Suffix which is used to append the line number to the URL. Use #L for GitHub - remoteLineSuffix.set("#L") - } - } -} diff --git a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/kotlin-jvm.gradle.kts b/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/kotlin-jvm.gradle.kts deleted file mode 100644 index 7fab222f..00000000 --- a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/kotlin-jvm.gradle.kts +++ /dev/null @@ -1,153 +0,0 @@ -package org.openrndr.extra.convention - -import ScreenshotsHelper.collectScreenshots -import org.gradle.api.tasks.testing.logging.TestExceptionFormat -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.dsl.KotlinVersion - -val sharedLibs = extensions.getByType(VersionCatalogsExtension::class.java).named("sharedLibs") -val openrndr = extensions.getByType(VersionCatalogsExtension::class.java).named("openrndr") -val libs = extensions.getByType(VersionCatalogsExtension::class.java).named("libs") - -val shouldPublish = project.name !in setOf("openrndr-demos", "orx-git-archiver-gradle") - -plugins { - java - kotlin("jvm") - `maven-publish` apply false - id("org.openrndr.extra.convention.component-metadata-rule") - id("org.openrndr.extra.convention.dokka") - signing -} -if (shouldPublish) { - apply(plugin = "maven-publish") -} - -repositories { - mavenCentral() - mavenLocal() -} - -group = "org.openrndr.extra" - -val main: SourceSet by project.sourceSets.getting - -@Suppress("UNUSED_VARIABLE") -val demo: SourceSet by project.sourceSets.creating { - val skipDemos = setOf( - "openrndr-demos", - "orx-axidraw", - "orx-midi", - "orx-minim", - "orx-realsense2", - "orx-runway", - "orx-syphon", - "orx-video-profiles", - "orx-crash-handler" - ) - if (project.name !in skipDemos) { - collectScreenshots(project, this@creating) { } - } -} - -dependencies { - implementation(sharedLibs.findLibrary("kotlin-stdlib").get()) - implementation(sharedLibs.findLibrary("kotlin-logging").get()) - testImplementation(sharedLibs.findLibrary("kotlin-test").get()) - testRuntimeOnly(sharedLibs.findLibrary("slf4j-simple").get()) - "demoImplementation"(main.output.classesDirs + main.runtimeClasspath) - "demoImplementation"(openrndr.findLibrary("application-core").get()) - "demoImplementation"(openrndr.findLibrary("orextensions").get()) - - "demoRuntimeOnly"(openrndr.findLibrary("application-glfw").get()) - - "demoRuntimeOnly"(sharedLibs.findLibrary("slf4j-simple").get()) -} - -tasks { - @Suppress("UNUSED_VARIABLE") - val test by getting(Test::class) { - if (DefaultNativePlatform.getCurrentOperatingSystem().isMacOsX) { - allJvmArgs = allJvmArgs + "-XstartOnFirstThread" - } - useJUnitPlatform() - testLogging.exceptionFormat = TestExceptionFormat.FULL - } - - @Suppress("UNUSED_VARIABLE") - val javadoc by getting(Javadoc::class) { - options { - this as StandardJavadocDocletOptions - addBooleanOption("Xdoclint:none", true) - } - } - withType { - compilerOptions { - jvmTarget.set(JvmTarget.valueOf("JVM_${libs.findVersion("jvmTarget").get().displayName.replace(".", "_")}")) - freeCompilerArgs.add("-Xexpect-actual-classes") - freeCompilerArgs.add("-Xjdk-release=${libs.findVersion("jvmTarget").get().displayName}") - apiVersion.set(KotlinVersion.valueOf("KOTLIN_${libs.findVersion("kotlinApi").get().displayName.replace(".", "_")}")) - languageVersion.set(KotlinVersion.valueOf("KOTLIN_${libs.findVersion("kotlinLanguage").get().displayName.replace(".", "_")}")) - } - } -} - -java { - withJavadocJar() - withSourcesJar() - targetCompatibility = JavaVersion.valueOf("VERSION_${libs.findVersion("jvmTarget").get().displayName}") - sourceCompatibility = JavaVersion.valueOf("VERSION_${libs.findVersion("jvmTarget").get().displayName}") -} - -val isReleaseVersion = !(version.toString()).endsWith("SNAPSHOT") - -if (shouldPublish) { - publishing { - publications { - create("maven") { - from(components["java"]) - groupId = "org.openrndr.extra" - artifactId = project.name - description = project.name - versionMapping { - allVariants { - fromResolutionResult() - } - } - pom { - name.set(project.name) - description.set(project.name) - url.set("https://openrndr.org") - developers { - developer { - id.set("edwinjakobs") - name.set("Edwin Jakobs") - email.set("edwin@openrndr.org") - } - } - - licenses { - license { - name.set("BSD-2-Clause") - url.set("https://github.com/openrndr/orx/blob/master/LICENSE") - distribution.set("repo") - } - } - - scm { - connection.set("scm:git:git@github.com:openrndr/orx.git") - developerConnection.set("scm:git:ssh://github.com/openrndr/orx.git") - url.set("https://github.com/openrndr/orx") - } - } - } - } - } - - signing { - setRequired({ isReleaseVersion && gradle.taskGraph.hasTask("publish") }) - sign(publishing.publications) - } -} diff --git a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/kotlin-multiplatform.gradle.kts b/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/kotlin-multiplatform.gradle.kts deleted file mode 100644 index 60dd134b..00000000 --- a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/kotlin-multiplatform.gradle.kts +++ /dev/null @@ -1,198 +0,0 @@ -package org.openrndr.extra.convention - -import CollectScreenshotsTask - -import org.gradle.api.tasks.testing.logging.TestExceptionFormat -import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform -import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.dsl.KotlinVersion -import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask -import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile - -val libs = extensions.getByType(VersionCatalogsExtension::class.java).named("libs") -val sharedLibs = extensions.getByType(VersionCatalogsExtension::class.java).named("sharedLibs") -val openrndr = extensions.getByType(VersionCatalogsExtension::class.java).named("openrndr") - -val shouldPublish = project.name !in setOf("openrndr-demos") - -plugins { - kotlin("multiplatform") - `maven-publish` apply false - id("org.openrndr.extra.convention.component-metadata-rule") - id("org.openrndr.extra.convention.dokka") - signing -} -if (shouldPublish) { - apply(plugin = "maven-publish") -} - -repositories { - mavenCentral() - mavenLocal() -} - -group = "org.openrndr.extra" - -tasks.withType> { - compilerOptions { - apiVersion.set(KotlinVersion.valueOf("KOTLIN_${libs.findVersion("kotlinApi").get().displayName.replace(".", "_")}")) - languageVersion.set(KotlinVersion.valueOf("KOTLIN_${libs.findVersion("kotlinLanguage").get().displayName.replace(".", "_")}")) - freeCompilerArgs.add("-Xexpect-actual-classes") - } -} - -tasks.withType().configureEach { - compilerOptions { - jvmTarget.set(JvmTarget.fromTarget(libs.findVersion("jvmTarget").get().displayName)) - freeCompilerArgs.add("-Xjdk-release=${libs.findVersion("jvmTarget").get().displayName}") - } -} - -kotlin { - jvm { - compilations { - val main by getting - - val demo by creating { - associateWith(main) - tasks.register("collectScreenshots") { - // since Kotlin 2.1.20 output.classesDirs no longer contains a single file - inputDir.set(output.classesDirs.filter { it.path.contains("classes/kotlin") }.singleFile) - runtimeDependencies.set(runtimeDependencyFiles) - outputDir.set(project.file(project.projectDir.toString() + "/images")) - dependsOn(compileTaskProvider) - } - dependencies { - runtimeOnly(openrndr.findLibrary("application-glfw").get()) - - } - } - } - testRuns["test"].executionTask { - useJUnitPlatform() - testLogging.exceptionFormat = TestExceptionFormat.FULL - } - - @OptIn(ExperimentalKotlinGradlePluginApi::class) - mainRun { - classpath(kotlin.jvm().compilations.getByName("demo").output.allOutputs) - classpath(kotlin.jvm().compilations.getByName("demo").configurations.runtimeDependencyConfiguration!!) - } - } - - js(IR) { - browser() - nodejs() - } - - sourceSets { - val commonMain by getting { - dependencies { - implementation(libs.findLibrary("kotlin-stdlib").get()) - implementation(sharedLibs.findLibrary("kotlin-logging").get()) - } - } - - val commonTest by getting { - dependencies { - implementation(libs.findLibrary("kotlin-test").get()) - } - } - - val jvmTest by getting { - dependencies { - runtimeOnly(sharedLibs.findBundle("jupiter").get()) - runtimeOnly(sharedLibs.findLibrary("slf4j.simple").get()) - } - } - - val jvmDemo by getting { - dependencies { - implementation(openrndr.findLibrary("application-core").get()) - implementation(openrndr.findLibrary("orextensions").get()) - runtimeOnly(openrndr.findLibrary("application-glfw").get()) - runtimeOnly(sharedLibs.findLibrary("slf4j-simple").get()) - } - } - } -} - -val isReleaseVersion = !(version.toString()).endsWith("SNAPSHOT") - -if (shouldPublish) { - publishing { - publications { - val fjdj = tasks.register("fakeJavaDocJar", Jar::class) { - archiveClassifier.set("javadoc") - } - named("js") { - this as MavenPublication - versionMapping { - allVariants { - fromResolutionOf("jsMainResolvableDependenciesMetadata") - } - } - } - named("jvm") { - this as MavenPublication - this.artifact(fjdj) - versionMapping { - allVariants { - fromResolutionOf("jvmMainResolvableDependenciesMetadata") - } - } - } - named("kotlinMultiplatform") { - this as MavenPublication - versionMapping { - allVariants { - fromResolutionOf("commonMainResolvableDependenciesMetadata") - } - } - } - all { - this as MavenPublication - pom { - name.set(project.name) - description.set(project.name) - url.set("https://openrndr.org") - developers { - developer { - id.set("edwinjakobs") - name.set("Edwin Jakobs") - email.set("edwin@openrndr.org") - } - } - - licenses { - license { - name.set("BSD-2-Clause") - url.set("https://github.com/openrndr/orx/blob/master/LICENSE") - distribution.set("repo") - } - } - - scm { - connection.set("scm:git:git@github.com:openrndr/orx.git") - developerConnection.set("scm:git:ssh://github.com/openrndr/orx.git") - url.set("https://github.com/openrndr/orx") - } - } - } - } - } - - signing { - setRequired({ isReleaseVersion && gradle.taskGraph.hasTask("publish") }) - sign(publishing.publications) - } -} - -tasks.withType().matching { it.name == "jvmRun" }.configureEach { - workingDir = rootDir - val os: OperatingSystem? = DefaultNativePlatform.getCurrentOperatingSystem() - if (os?.name == "Mac OS X") { - setJvmArgs(listOf("-XstartOnFirstThread")) - } -} diff --git a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/variant.gradle.kts b/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/variant.gradle.kts deleted file mode 100644 index 5630bc03..00000000 --- a/build-logic/orx-convention/src/main/kotlin/org/openrndr/extra/convention/variant.gradle.kts +++ /dev/null @@ -1,5 +0,0 @@ -package org.openrndr.extra.convention - -plugins { - id("orx-variant") -} \ No newline at end of file diff --git a/build-logic/orx-convention/src/preload/kotlin/ApplicationPreload.kt b/build-logic/orx-convention/src/preload/kotlin/ApplicationPreload.kt deleted file mode 100644 index 36d52918..00000000 --- a/build-logic/orx-convention/src/preload/kotlin/ApplicationPreload.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.openrndr - -import org.openrndr.extensions.SingleScreenshot - -/** - * This [Preload] class is used by the [CollectScreenshots] task to inject the [SingleScreenshot] extension - */ -class Preload : ApplicationPreload() { - override fun onProgramSetup(program: Program) { - program.extend(SingleScreenshot()) { - this.outputFile = System.getProperty("screenshotPath") - } - } -} \ No newline at end of file diff --git a/build-logic/orx-variant-plugin/build.gradle.kts b/build-logic/orx-variant-plugin/build.gradle.kts deleted file mode 100644 index afb6774b..00000000 --- a/build-logic/orx-variant-plugin/build.gradle.kts +++ /dev/null @@ -1,17 +0,0 @@ -plugins { - `kotlin-dsl` -} - -repositories { - mavenCentral() -} - - -gradlePlugin { - plugins { - create("orxVariants") { - id = "orx-variant" - implementationClass = "org.openrndr.extra.variant.plugin.VariantPlugin" - } - } -} \ No newline at end of file diff --git a/build-logic/orx-variant-plugin/src/main/kotlin/VariantPlugin.kt b/build-logic/orx-variant-plugin/src/main/kotlin/VariantPlugin.kt deleted file mode 100644 index 67441a88..00000000 --- a/build-logic/orx-variant-plugin/src/main/kotlin/VariantPlugin.kt +++ /dev/null @@ -1,169 +0,0 @@ -package org.openrndr.extra.variant.plugin - -import org.gradle.api.Action -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.Dependency -import org.gradle.api.attributes.Attribute -import org.gradle.api.attributes.Bundling -import org.gradle.api.attributes.Category -import org.gradle.api.attributes.LibraryElements -import org.gradle.api.attributes.Usage -import org.gradle.api.attributes.java.TargetJvmVersion -import org.gradle.api.component.AdhocComponentWithVariants -import org.gradle.api.model.ObjectFactory -import org.gradle.api.plugins.jvm.JvmComponentDependencies -import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.SourceSetContainer -import org.gradle.api.tasks.TaskContainer -import org.gradle.jvm.tasks.Jar -import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.named -import org.gradle.language.jvm.tasks.ProcessResources -import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform -import javax.inject.Inject - -fun arch(arch: String = System.getProperty("os.arch")): String { - return when (arch) { - "x86-64", "x86_64", "amd64" -> "x86-64" - "arm64", "aarch64" -> "aarch64" - else -> error("unsupported arch $arch") - } -} - -abstract class VariantContainer @Inject constructor( - @Inject val tasks: TaskContainer, - val apiElements: Configuration, - - val runtimeElements: Configuration, - val sourceSet: SourceSet -) { - - @Nested - abstract fun getDependencies(): JvmComponentDependencies - - fun Dependency.withClassifier(classifier: String): String { - return "$group:$name:$version:$classifier" - } - - /** - * Setup dependencies for this variant. - */ - fun dependencies(action: Action) { - action.execute(getDependencies()) - } - - /** - * Specify that this variant comes with a resource bundle. - */ - fun jar(action: Action) { - sourceSet.resources.srcDirs.add(sourceSet.java.srcDirs.first().parentFile.resolve("resources")) - sourceSet.resources.includes.add("**/*.*") - tasks.named(sourceSet.jarTaskName).configure { - include("**/*.*") - dependsOn(tasks.named(sourceSet.processResourcesTaskName)) - manifest { - //this.attributes() - } - this.from(sourceSet.resources.srcDirs) - } - runtimeElements.outgoing.artifact(tasks.named(sourceSet.jarTaskName)) - action.execute(Unit) - } -} - -abstract class VariantExtension( - @Inject val objectFactory: ObjectFactory, - @Inject val project: Project -) { - - fun platform(os: String, arch: String, f: VariantContainer.() -> Unit) { - val sourceSets = project.extensions.getByType(SourceSetContainer::class.java) - - val sourceSetArch = arch.replace("-", "_") - val nameMain = "${os}${sourceSetArch.capitalize()}Main" - val platformMain = sourceSets.create(nameMain) - val tasks = project.tasks - tasks.register(platformMain.jarTaskName, Jar::class.java) { - archiveClassifier.set("$os-$arch") - } - - val configurations = project.configurations - val objects = project.objects - - val main = sourceSets.getByName("main") - val mainApi = configurations.getByName(main.apiElementsConfigurationName) - val mainRuntimeOnly = configurations.getByName(main.runtimeElementsConfigurationName) - - mainApi.attributes { - val osAttribute = Attribute.of("org.gradle.native.operatingSystem", String::class.java) - attribute(osAttribute, "do_not_use_me") - } - - val platformMainRuntimeElements = configurations.create(platformMain.runtimeElementsConfigurationName) { - extendsFrom(mainRuntimeOnly, mainApi) - isCanBeResolved = false - isCanBeConsumed = true - val osAttribute = Attribute.of("org.gradle.native.operatingSystem", String::class.java) - val archAttribute = Attribute.of("org.gradle.native.architecture", String::class.java) - val typeAttribute = Attribute.of("org.jetbrains.kotlin.platform.type", String::class.java) - val environmentAttribute = Attribute.of("org.gradle.jvm.environment", String::class.java) - - attributes { - attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) - attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) - attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR)) - attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) - attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) - - attribute(osAttribute, os) - attribute(archAttribute, arch) - attribute(typeAttribute, "jvm") - attribute(environmentAttribute, "standard-jvm") - } - outgoing.artifact(tasks.named(main.jarTaskName)) - outgoing.artifact(tasks.named(platformMain.jarTaskName)) - } - - val javaComponent = project.components.getByName("java") as AdhocComponentWithVariants - javaComponent.addVariantsFromConfiguration(platformMainRuntimeElements) { - platformMain.runtimeClasspath.files.add(platformMain.resources.srcDirs.first()) - } - - val variantContainer = objectFactory.newInstance( - VariantContainer::class.java, - platformMainRuntimeElements, - platformMainRuntimeElements, - platformMain - ) - variantContainer.f() - - platformMainRuntimeElements.dependencies.addAll(variantContainer.getDependencies().runtimeOnly.dependencies.get()) - - /* - Setup dependencies for current platform. This will make in-module tests and demos work. - */ - val currentOperatingSystemName: String = DefaultNativePlatform.getCurrentOperatingSystem().toFamilyName() - val currentArchitectureName: String = arch() - - if (currentOperatingSystemName == os && currentArchitectureName == arch) { - project.dependencies { - add("testRuntimeOnly", platformMain.output) - add("demoRuntimeOnly", platformMain.output) - for (i in platformMainRuntimeElements.dependencies) { - add("testRuntimeOnly", i) - add("demoRuntimeOnly", i) - } - } - } - } -} - -class VariantPlugin : Plugin { - override fun apply(target: Project) { - val project = target - project.extensions.create("variants", VariantExtension::class.java) - } -} \ No newline at end of file diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts deleted file mode 100644 index f4ca4cd8..00000000 --- a/build-logic/settings.gradle.kts +++ /dev/null @@ -1,29 +0,0 @@ -include("orx-convention", "orx-variant-plugin") - -dependencyResolutionManagement { - repositories { - mavenCentral() - mavenLocal { - content { - includeGroup("org.openrndr") - } - } - } - - versionCatalogs { - create("libs") { - from(files("../gradle/libs.versions.toml")) - } - - // We use a regex to get the openrndr version from the primary catalog as there is no public Gradle API to parse catalogs. - val regEx = Regex("^openrndr[ ]*=[ ]*(?:\\{[ ]*require[ ]*=[ ]*)?\"(.*)\"[ ]*(?:\\})?", RegexOption.MULTILINE) - val openrndrVersion = regEx.find(File(rootDir,"../gradle/libs.versions.toml").readText())?.groupValues?.get(1) ?: error("can't find openrndr version") - create("sharedLibs") { - from("org.openrndr:openrndr-dependency-catalog:$openrndrVersion") - } - create("openrndr") { - from("org.openrndr:openrndr-module-catalog:$openrndrVersion") - } - } -} - diff --git a/build-tools/xvfb-java.sh b/build-tools/xvfb-java.sh deleted file mode 100755 index 2501403f..00000000 --- a/build-tools/xvfb-java.sh +++ /dev/null @@ -1,2 +0,0 @@ -#/bin/bash -xvfb-run -e /dev/stdout java "$@" diff --git a/build.gradle b/build.gradle index c2a58d8c..7b1ef2cc 100644 --- a/build.gradle +++ b/build.gradle @@ -1,144 +1,6 @@ plugins { - alias(libs.plugins.nebula.release) - alias(libs.plugins.nmcp) - id("org.openrndr.extra.convention.dokka") alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.android.library) apply false } - -repositories { - mavenCentral() -} - -tasks.register('buildMainReadme') { - doFirst { - def subProjects = project.subprojects - //.findAll { !it.name.contains("kinect-common") && !it.name.contains - // ("kinect-v1-") } - - // Load README.md and find [begin, end] section to replace - def mainReadme = file("README.md") - def lines = mainReadme.readLines() - - def begin = lines.findIndexOf { it == "" } - def end = lines.findIndexOf { it == "" } - if (begin == -1 || end == -1) { - println("Comments for orx list generation not found in README.md!") - return - } - - def header = lines.subList(0, begin + 1) - def footer = lines.subList(end, lines.size()) - - def newReadme = [] - for (line in header) { - newReadme.add(line) - } - - // Search for the description at the top of the readme. - // Skip the hash character from the headline, then start - // on the next line and continue until the next empty line. - // Don't fall into Windows line breaks. - def descriptionRx = ~/(?s)#.*?\n(.+?)\n\r?\n/ - // Note: the readme needs an empty line after the description - - def orxMultiplatform = [] - def orxJVMOnly = [] - - // Build orx list - for (sub in subProjects) { - def orxReadmeFile = sub.file("README.md") - if (orxReadmeFile.exists()) { - def orxReadmeText = orxReadmeFile.getText() - orxReadmeText.find(descriptionRx) { - description -> - def trimmedDescription = description[1].trim() //.strip() supports unicode, java11 only - .replace("\n", " ").replace("\r", "") - def path = sub.path.substring(1).replace(":", "/") - if (path.startsWith("orx-jvm")) { - orxJVMOnly.add("| [`${sub.name}`]($path/) " + - "| $trimmedDescription |") - } else { - orxMultiplatform.add("| [`${sub.name}`]($path/) " + - "| $trimmedDescription |") - } - } - - } else { - println("${sub.name}/README.md not found!") - } - } - - newReadme.add("\n## Multiplatform\n") - newReadme.add("| name" + " " * 36 + " | description |") - newReadme.add("| --- | --- |") - newReadme.addAll(orxMultiplatform) - - newReadme.add("\n## JVM only\n") - newReadme.add("| name" + " " * 36 + " | description |") - newReadme.add("| --- | --- |") - newReadme.addAll(orxJVMOnly) - - for (line in footer) { - newReadme.add(line) - } - - // Write result - if (mainReadme.exists()) { - mainReadme.delete() - } - mainReadme.write(newReadme.join("\n")) - } -} -group = "org.openrndr.extra" -nmcpAggregation { - centralPortal { - username.set(findProperty("ossrhUsername") ?: System.getenv("OSSRH_USERNAME")) - password.set(findProperty("ossrhPassword") ?: System.getenv("OSSRH_PASSWORD")) - - // publish manually from the portal - publishingType = "USER_MANAGED" - } - - // Publish all projects that apply the 'maven-publish' plugin - publishAllProjectsProbablyBreakingProjectIsolation() -} -//nexusPublishing { -// repositories { -// sonatype { -// username.set(findProperty("ossrhUsername") ?: System.getenv("OSSRH_USERNAME")) -// password.set(findProperty("ossrhPassword") ?: System.getenv("OSSRH_PASSWORD")) -// nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) -// snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) -// } -// } -//} - -subprojects { - // Equivalent Kotlin is: tasks.register("dependenciesAll") { ... - tasks.register("dependenciesAll", DependencyReportTask) { - group = HelpTasksPlugin.HELP_GROUP - description = "Displays all dependencies, including subprojects." - } -} - -dependencies { - subprojects.findAll { - it.name.startsWith("orx-") && !it.name.contains("-catalog") - }.each { subproject -> - dokka(project(subproject.path)) - } -} -class SleepTask extends DefaultTask { - @TaskAction - void action() { - sleep(60 * 5 * 1000) - } -} -tasks.register("sleep", SleepTask) -gradle.buildFinished { - println("\n") - println("orx = \"${version}\"") -} \ No newline at end of file diff --git a/demo-data/.gitignore b/demo-data/.gitignore deleted file mode 100644 index d8406b36..00000000 --- a/demo-data/.gitignore +++ /dev/null @@ -1 +0,0 @@ -**exported* diff --git a/demo-data/cubemaps/garage_iem.dds b/demo-data/cubemaps/garage_iem.dds deleted file mode 100644 index b2347974..00000000 Binary files a/demo-data/cubemaps/garage_iem.dds and /dev/null differ diff --git a/demo-data/fonts/IBMPlexMono-Regular.ttf b/demo-data/fonts/IBMPlexMono-Regular.ttf deleted file mode 100644 index f99c8e98..00000000 Binary files a/demo-data/fonts/IBMPlexMono-Regular.ttf and /dev/null differ diff --git a/demo-data/gltf-models/box-animated/BoxAnimated.glb b/demo-data/gltf-models/box-animated/BoxAnimated.glb deleted file mode 100644 index 69481ec3..00000000 Binary files a/demo-data/gltf-models/box-animated/BoxAnimated.glb and /dev/null differ diff --git a/demo-data/gltf-models/box/Box.glb b/demo-data/gltf-models/box/Box.glb deleted file mode 100644 index 95ec886b..00000000 Binary files a/demo-data/gltf-models/box/Box.glb and /dev/null differ diff --git a/demo-data/gltf-models/camera/Scene.glb b/demo-data/gltf-models/camera/Scene.glb deleted file mode 100644 index 4d66523a..00000000 Binary files a/demo-data/gltf-models/camera/Scene.glb and /dev/null differ diff --git a/demo-data/gltf-models/directional-light/Scene.glb b/demo-data/gltf-models/directional-light/Scene.glb deleted file mode 100644 index 5145de22..00000000 Binary files a/demo-data/gltf-models/directional-light/Scene.glb and /dev/null differ diff --git a/demo-data/gltf-models/duck/Duck.gltf b/demo-data/gltf-models/duck/Duck.gltf deleted file mode 100644 index b80c842c..00000000 --- a/demo-data/gltf-models/duck/Duck.gltf +++ /dev/null @@ -1,219 +0,0 @@ -{ - "asset": { - "generator": "COLLADA2GLTF", - "version": "2.0" - }, - "scene": 0, - "scenes": [ - { - "nodes": [ - 0 - ] - } - ], - "nodes": [ - { - "children": [ - 2, - 1 - ], - "matrix": [ - 0.009999999776482582, - 0.0, - 0.0, - 0.0, - 0.0, - 0.009999999776482582, - 0.0, - 0.0, - 0.0, - 0.0, - 0.009999999776482582, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0 - ] - }, - { - "matrix": [ - -0.7289686799049377, - 0.0, - -0.6845470666885376, - 0.0, - -0.4252049028873444, - 0.7836934328079224, - 0.4527972936630249, - 0.0, - 0.5364750623703003, - 0.6211478114128113, - -0.571287989616394, - 0.0, - 400.1130065917969, - 463.2640075683594, - -431.0780334472656, - 1.0 - ], - "camera": 0 - }, - { - "mesh": 0 - } - ], - "cameras": [ - { - "perspective": { - "aspectRatio": 1.5, - "yfov": 0.6605925559997559, - "zfar": 10000.0, - "znear": 1.0 - }, - "type": "perspective" - } - ], - "meshes": [ - { - "primitives": [ - { - "attributes": { - "NORMAL": 1, - "POSITION": 2, - "TEXCOORD_0": 3 - }, - "indices": 0, - "mode": 4, - "material": 0 - } - ], - "name": "LOD3spShape" - } - ], - "accessors": [ - { - "bufferView": 0, - "byteOffset": 0, - "componentType": 5123, - "count": 12636, - "max": [ - 2398 - ], - "min": [ - 0 - ], - "type": "SCALAR" - }, - { - "bufferView": 1, - "byteOffset": 0, - "componentType": 5126, - "count": 2399, - "max": [ - 0.9995989799499512, - 0.999580979347229, - 0.9984359741210938 - ], - "min": [ - -0.9990839958190918, - -1.0, - -0.9998319745063782 - ], - "type": "VEC3" - }, - { - "bufferView": 1, - "byteOffset": 28788, - "componentType": 5126, - "count": 2399, - "max": [ - 96.17990112304688, - 163.97000122070313, - 53.92519760131836 - ], - "min": [ - -69.29850006103516, - 9.929369926452637, - -61.32819747924805 - ], - "type": "VEC3" - }, - { - "bufferView": 2, - "byteOffset": 0, - "componentType": 5126, - "count": 2399, - "max": [ - 0.9833459854125976, - 0.9800369739532472 - ], - "min": [ - 0.026409000158309938, - 0.01996302604675293 - ], - "type": "VEC2" - } - ], - "materials": [ - { - "pbrMetallicRoughness": { - "baseColorTexture": { - "index": 0 - }, - "metallicFactor": 0.0 - }, - "emissiveFactor": [ - 0.0, - 0.0, - 0.0 - ], - "name": "blinn3-fx" - } - ], - "textures": [ - { - "sampler": 0, - "source": 0 - } - ], - "images": [ - { - "uri": "DuckCM.png" - } - ], - "samplers": [ - { - "magFilter": 9729, - "minFilter": 9986, - "wrapS": 10497, - "wrapT": 10497 - } - ], - "bufferViews": [ - { - "buffer": 0, - "byteOffset": 76768, - "byteLength": 25272, - "target": 34963 - }, - { - "buffer": 0, - "byteOffset": 0, - "byteLength": 57576, - "byteStride": 12, - "target": 34962 - }, - { - "buffer": 0, - "byteOffset": 57576, - "byteLength": 19192, - "byteStride": 8, - "target": 34962 - } - ], - "buffers": [ - { - "byteLength": 102040, - "uri": "Duck0.bin" - } - ] -} diff --git a/demo-data/gltf-models/duck/Duck0.bin b/demo-data/gltf-models/duck/Duck0.bin deleted file mode 100644 index 5f01f88a..00000000 Binary files a/demo-data/gltf-models/duck/Duck0.bin and /dev/null differ diff --git a/demo-data/gltf-models/duck/DuckCM.png b/demo-data/gltf-models/duck/DuckCM.png deleted file mode 100644 index 9fa2dd4c..00000000 Binary files a/demo-data/gltf-models/duck/DuckCM.png and /dev/null differ diff --git a/demo-data/gltf-models/fox/Fox.glb b/demo-data/gltf-models/fox/Fox.glb deleted file mode 100644 index a541b6f4..00000000 Binary files a/demo-data/gltf-models/fox/Fox.glb and /dev/null differ diff --git a/demo-data/gltf-models/irradiance-probes/model.glb b/demo-data/gltf-models/irradiance-probes/model.glb deleted file mode 100644 index 241e61c9..00000000 Binary files a/demo-data/gltf-models/irradiance-probes/model.glb and /dev/null differ diff --git a/demo-data/gltf-models/point-light/Scene.glb b/demo-data/gltf-models/point-light/Scene.glb deleted file mode 100644 index 3bd7ef1e..00000000 Binary files a/demo-data/gltf-models/point-light/Scene.glb and /dev/null differ diff --git a/demo-data/gltf-models/spot-light/Scene.glb b/demo-data/gltf-models/spot-light/Scene.glb deleted file mode 100644 index 2b15b6b9..00000000 Binary files a/demo-data/gltf-models/spot-light/Scene.glb and /dev/null differ diff --git a/demo-data/gltf-models/suzanne/Suzanne.bin b/demo-data/gltf-models/suzanne/Suzanne.bin deleted file mode 100644 index 60f54db7..00000000 Binary files a/demo-data/gltf-models/suzanne/Suzanne.bin and /dev/null differ diff --git a/demo-data/gltf-models/suzanne/Suzanne.gltf b/demo-data/gltf-models/suzanne/Suzanne.gltf deleted file mode 100644 index 56607849..00000000 --- a/demo-data/gltf-models/suzanne/Suzanne.gltf +++ /dev/null @@ -1,193 +0,0 @@ -{ - "accessors" : [ - { - "bufferView" : 0, - "byteOffset" : 0, - "componentType" : 5123, - "count" : 11808, - "max" : [ - 11807 - ], - "min" : [ - 0 - ], - "type" : "SCALAR" - }, - { - "bufferView" : 1, - "byteOffset" : 0, - "componentType" : 5126, - "count" : 11808, - "max" : [ - 1.336914, - 0.950195, - 0.825684 - ], - "min" : [ - -1.336914, - -0.974609, - -0.800781 - ], - "type" : "VEC3" - }, - { - "bufferView" : 2, - "byteOffset" : 0, - "componentType" : 5126, - "count" : 11808, - "max" : [ - 0.996339, - 0.999958, - 0.999929 - ], - "min" : [ - -0.996339, - -0.985940, - -0.999994 - ], - "type" : "VEC3" - }, - { - "bufferView" : 3, - "byteOffset" : 0, - "componentType" : 5126, - "count" : 11808, - "max" : [ - 0.998570, - 0.999996, - 0.999487, - 1.000000 - ], - "min" : [ - -0.999233, - -0.999453, - -0.999812, - 1.000000 - ], - "type" : "VEC4" - }, - { - "bufferView" : 4, - "byteOffset" : 0, - "componentType" : 5126, - "count" : 11808, - "max" : [ - 0.999884, - 0.884359 - ], - "min" : [ - 0.000116, - 0.000116 - ], - "type" : "VEC2" - } - ], - "asset" : { - "generator" : "VKTS glTF 2.0 exporter", - "version" : "2.0" - }, - "bufferViews" : [ - { - "buffer" : 0, - "byteLength" : 23616, - "byteOffset" : 0, - "target" : 34963 - }, - { - "buffer" : 0, - "byteLength" : 141696, - "byteOffset" : 23616, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 141696, - "byteOffset" : 165312, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 188928, - "byteOffset" : 307008, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 94464, - "byteOffset" : 495936, - "target" : 34962 - } - ], - "buffers" : [ - { - "byteLength" : 590400, - "uri" : "Suzanne.bin" - } - ], - "images" : [ - { - "uri" : "Suzanne_BaseColor.png" - }, - { - "uri" : "Suzanne_MetallicRoughness.png" - } - ], - "materials" : [ - { - "name" : "Suzanne", - "pbrMetallicRoughness" : { - "baseColorTexture" : { - "index" : 0 - }, - "metallicRoughnessTexture" : { - "index" : 1 - } - } - } - ], - "meshes" : [ - { - "name" : "Suzanne", - "primitives" : [ - { - "attributes" : { - "NORMAL" : 2, - "POSITION" : 1, - "TANGENT" : 3, - "TEXCOORD_0" : 4 - }, - "indices" : 0, - "material" : 0, - "mode" : 4 - } - ] - } - ], - "nodes" : [ - { - "mesh" : 0, - "name" : "Suzanne" - } - ], - "samplers" : [ - {} - ], - "scene" : 0, - "scenes" : [ - { - "nodes" : [ - 0 - ] - } - ], - "textures" : [ - { - "sampler" : 0, - "source" : 0 - }, - { - "sampler" : 0, - "source" : 1 - } - ] -} diff --git a/demo-data/gltf-models/suzanne/Suzanne_BaseColor.png b/demo-data/gltf-models/suzanne/Suzanne_BaseColor.png deleted file mode 100644 index 35469abf..00000000 Binary files a/demo-data/gltf-models/suzanne/Suzanne_BaseColor.png and /dev/null differ diff --git a/demo-data/gltf-models/suzanne/Suzanne_MetallicRoughness.png b/demo-data/gltf-models/suzanne/Suzanne_MetallicRoughness.png deleted file mode 100644 index e4ff1fde..00000000 Binary files a/demo-data/gltf-models/suzanne/Suzanne_MetallicRoughness.png and /dev/null differ diff --git a/demo-data/images/16-bit.png b/demo-data/images/16-bit.png deleted file mode 100644 index d93e236a..00000000 Binary files a/demo-data/images/16-bit.png and /dev/null differ diff --git a/demo-data/images/image-001.dds b/demo-data/images/image-001.dds deleted file mode 100644 index 90363b81..00000000 Binary files a/demo-data/images/image-001.dds and /dev/null differ diff --git a/demo-data/images/image-001.png b/demo-data/images/image-001.png deleted file mode 100644 index 06cd7a4b..00000000 Binary files a/demo-data/images/image-001.png and /dev/null differ diff --git a/demo-data/images/life-cover.jpg b/demo-data/images/life-cover.jpg deleted file mode 100644 index 4f27dfb1..00000000 Binary files a/demo-data/images/life-cover.jpg and /dev/null differ diff --git a/demo-data/images/peopleCity01.jpg b/demo-data/images/peopleCity01.jpg deleted file mode 100644 index d6bc4d2d..00000000 Binary files a/demo-data/images/peopleCity01.jpg and /dev/null differ diff --git a/demo-data/images/vw-beetle.jpg b/demo-data/images/vw-beetle.jpg deleted file mode 100644 index 849631ea..00000000 Binary files a/demo-data/images/vw-beetle.jpg and /dev/null differ diff --git a/demo-data/obj-models/suzanne/Suzanne.mtl b/demo-data/obj-models/suzanne/Suzanne.mtl deleted file mode 100644 index 6c4f6eab..00000000 --- a/demo-data/obj-models/suzanne/Suzanne.mtl +++ /dev/null @@ -1,2 +0,0 @@ -# Blender 4.1.1 MTL File: 'None' -# www.blender.org diff --git a/demo-data/obj-models/suzanne/Suzanne.obj b/demo-data/obj-models/suzanne/Suzanne.obj deleted file mode 100644 index 28f3e6dc..00000000 --- a/demo-data/obj-models/suzanne/Suzanne.obj +++ /dev/null @@ -1,2066 +0,0 @@ -# Blender 4.1.1 -# www.blender.org -mtllib Suzanne.mtl -o Suzanne -v 0.437500 0.164062 0.765625 -v -0.437500 0.164062 0.765625 -v 0.500000 0.093750 0.687500 -v -0.500000 0.093750 0.687500 -v 0.546875 0.054688 0.578125 -v -0.546875 0.054688 0.578125 -v 0.351562 -0.023438 0.617188 -v -0.351562 -0.023438 0.617188 -v 0.351562 0.031250 0.718750 -v -0.351562 0.031250 0.718750 -v 0.351562 0.132812 0.781250 -v -0.351562 0.132812 0.781250 -v 0.273438 0.164062 0.796875 -v -0.273438 0.164062 0.796875 -v 0.203125 0.093750 0.742188 -v -0.203125 0.093750 0.742188 -v 0.156250 0.054688 0.648438 -v -0.156250 0.054688 0.648438 -v 0.078125 0.242188 0.656250 -v -0.078125 0.242188 0.656250 -v 0.140625 0.242188 0.742188 -v -0.140625 0.242188 0.742188 -v 0.242188 0.242188 0.796875 -v -0.242188 0.242188 0.796875 -v 0.273438 0.328125 0.796875 -v -0.273438 0.328125 0.796875 -v 0.203125 0.390625 0.742188 -v -0.203125 0.390625 0.742188 -v 0.156250 0.437500 0.648438 -v -0.156250 0.437500 0.648438 -v 0.351562 0.515625 0.617188 -v -0.351562 0.515625 0.617188 -v 0.351562 0.453125 0.718750 -v -0.351562 0.453125 0.718750 -v 0.351562 0.359375 0.781250 -v -0.351562 0.359375 0.781250 -v 0.437500 0.328125 0.765625 -v -0.437500 0.328125 0.765625 -v 0.500000 0.390625 0.687500 -v -0.500000 0.390625 0.687500 -v 0.546875 0.437500 0.578125 -v -0.546875 0.437500 0.578125 -v 0.625000 0.242188 0.562500 -v -0.625000 0.242188 0.562500 -v 0.562500 0.242188 0.671875 -v -0.562500 0.242188 0.671875 -v 0.468750 0.242188 0.757812 -v -0.468750 0.242188 0.757812 -v 0.476562 0.242188 0.773438 -v -0.476562 0.242188 0.773438 -v 0.445312 0.335938 0.781250 -v -0.445312 0.335938 0.781250 -v 0.351562 0.375000 0.804688 -v -0.351562 0.375000 0.804688 -v 0.265625 0.335938 0.820312 -v -0.265625 0.335938 0.820312 -v 0.226562 0.242188 0.820312 -v -0.226562 0.242188 0.820312 -v 0.265625 0.156250 0.820312 -v -0.265625 0.156250 0.820312 -v 0.351562 0.242188 0.828125 -v -0.351562 0.242188 0.828125 -v 0.351562 0.117188 0.804688 -v -0.351562 0.117188 0.804688 -v 0.445312 0.156250 0.781250 -v -0.445312 0.156250 0.781250 -v 0.000000 0.429688 0.742188 -v 0.000000 0.351562 0.820312 -v 0.000000 -0.679688 0.734375 -v 0.000000 -0.320312 0.781250 -v 0.000000 -0.187500 0.796875 -v 0.000000 -0.773438 0.718750 -v 0.000000 0.406250 0.601562 -v 0.000000 0.570312 0.570312 -v 0.000000 0.898438 -0.546875 -v 0.000000 0.562500 -0.851562 -v 0.000000 0.070312 -0.828125 -v 0.000000 -0.382812 -0.351562 -v 0.203125 -0.187500 0.562500 -v -0.203125 -0.187500 0.562500 -v 0.312500 -0.437500 0.570312 -v -0.312500 -0.437500 0.570312 -v 0.351562 -0.695312 0.570312 -v -0.351562 -0.695312 0.570312 -v 0.367188 -0.890625 0.531250 -v -0.367188 -0.890625 0.531250 -v 0.328125 -0.945312 0.523438 -v -0.328125 -0.945312 0.523438 -v 0.179688 -0.968750 0.554688 -v -0.179688 -0.968750 0.554688 -v 0.000000 -0.984375 0.578125 -v 0.437500 -0.140625 0.531250 -v -0.437500 -0.140625 0.531250 -v 0.632812 -0.039062 0.539062 -v -0.632812 -0.039062 0.539062 -v 0.828125 0.148438 0.445312 -v -0.828125 0.148438 0.445312 -v 0.859375 0.429688 0.593750 -v -0.859375 0.429688 0.593750 -v 0.710938 0.484375 0.625000 -v -0.710938 0.484375 0.625000 -v 0.492188 0.601562 0.687500 -v -0.492188 0.601562 0.687500 -v 0.320312 0.757812 0.734375 -v -0.320312 0.757812 0.734375 -v 0.156250 0.718750 0.757812 -v -0.156250 0.718750 0.757812 -v 0.062500 0.492188 0.750000 -v -0.062500 0.492188 0.750000 -v 0.164062 0.414062 0.773438 -v -0.164062 0.414062 0.773438 -v 0.125000 0.304688 0.765625 -v -0.125000 0.304688 0.765625 -v 0.203125 0.093750 0.742188 -v -0.203125 0.093750 0.742188 -v 0.375000 0.015625 0.703125 -v -0.375000 0.015625 0.703125 -v 0.492188 0.062500 0.671875 -v -0.492188 0.062500 0.671875 -v 0.625000 0.187500 0.648438 -v -0.625000 0.187500 0.648438 -v 0.640625 0.296875 0.648438 -v -0.640625 0.296875 0.648438 -v 0.601562 0.375000 0.664062 -v -0.601562 0.375000 0.664062 -v 0.429688 0.437500 0.718750 -v -0.429688 0.437500 0.718750 -v 0.250000 0.468750 0.757812 -v -0.250000 0.468750 0.757812 -v 0.000000 -0.765625 0.734375 -v 0.109375 -0.718750 0.734375 -v -0.109375 -0.718750 0.734375 -v 0.117188 -0.835938 0.710938 -v -0.117188 -0.835938 0.710938 -v 0.062500 -0.882812 0.695312 -v -0.062500 -0.882812 0.695312 -v 0.000000 -0.890625 0.687500 -v 0.000000 -0.195312 0.750000 -v 0.000000 -0.140625 0.742188 -v 0.101562 -0.148438 0.742188 -v -0.101562 -0.148438 0.742188 -v 0.125000 -0.226562 0.750000 -v -0.125000 -0.226562 0.750000 -v 0.085938 -0.289062 0.742188 -v -0.085938 -0.289062 0.742188 -v 0.398438 -0.046875 0.671875 -v -0.398438 -0.046875 0.671875 -v 0.617188 0.054688 0.625000 -v -0.617188 0.054688 0.625000 -v 0.726562 0.203125 0.601562 -v -0.726562 0.203125 0.601562 -v 0.742188 0.375000 0.656250 -v -0.742188 0.375000 0.656250 -v 0.687500 0.414062 0.726562 -v -0.687500 0.414062 0.726562 -v 0.437500 0.546875 0.796875 -v -0.437500 0.546875 0.796875 -v 0.312500 0.640625 0.835938 -v -0.312500 0.640625 0.835938 -v 0.203125 0.617188 0.851562 -v -0.203125 0.617188 0.851562 -v 0.101562 0.429688 0.843750 -v -0.101562 0.429688 0.843750 -v 0.125000 -0.101562 0.812500 -v -0.125000 -0.101562 0.812500 -v 0.210938 -0.445312 0.710938 -v -0.210938 -0.445312 0.710938 -v 0.250000 -0.703125 0.687500 -v -0.250000 -0.703125 0.687500 -v 0.265625 -0.820312 0.664062 -v -0.265625 -0.820312 0.664062 -v 0.234375 -0.914062 0.632812 -v -0.234375 -0.914062 0.632812 -v 0.164062 -0.929688 0.632812 -v -0.164062 -0.929688 0.632812 -v 0.000000 -0.945312 0.640625 -v 0.000000 0.046875 0.726562 -v 0.000000 0.210938 0.765625 -v 0.328125 0.476562 0.742188 -v -0.328125 0.476562 0.742188 -v 0.164062 0.140625 0.750000 -v -0.164062 0.140625 0.750000 -v 0.132812 0.210938 0.757812 -v -0.132812 0.210938 0.757812 -v 0.117188 -0.687500 0.734375 -v -0.117188 -0.687500 0.734375 -v 0.078125 -0.445312 0.750000 -v -0.078125 -0.445312 0.750000 -v 0.000000 -0.445312 0.750000 -v 0.000000 -0.328125 0.742188 -v 0.093750 -0.273438 0.781250 -v -0.093750 -0.273438 0.781250 -v 0.132812 -0.226562 0.796875 -v -0.132812 -0.226562 0.796875 -v 0.109375 -0.132812 0.781250 -v -0.109375 -0.132812 0.781250 -v 0.039062 -0.125000 0.781250 -v -0.039062 -0.125000 0.781250 -v 0.000000 -0.203125 0.828125 -v 0.046875 -0.148438 0.812500 -v -0.046875 -0.148438 0.812500 -v 0.093750 -0.156250 0.812500 -v -0.093750 -0.156250 0.812500 -v 0.109375 -0.226562 0.828125 -v -0.109375 -0.226562 0.828125 -v 0.078125 -0.250000 0.804688 -v -0.078125 -0.250000 0.804688 -v 0.000000 -0.289062 0.804688 -v 0.257812 -0.312500 0.554688 -v -0.257812 -0.312500 0.554688 -v 0.164062 -0.242188 0.710938 -v -0.164062 -0.242188 0.710938 -v 0.179688 -0.312500 0.710938 -v -0.179688 -0.312500 0.710938 -v 0.234375 -0.250000 0.554688 -v -0.234375 -0.250000 0.554688 -v 0.000000 -0.875000 0.687500 -v 0.046875 -0.867188 0.687500 -v -0.046875 -0.867188 0.687500 -v 0.093750 -0.820312 0.710938 -v -0.093750 -0.820312 0.710938 -v 0.093750 -0.742188 0.726562 -v -0.093750 -0.742188 0.726562 -v 0.000000 -0.781250 0.656250 -v 0.093750 -0.750000 0.664062 -v -0.093750 -0.750000 0.664062 -v 0.093750 -0.812500 0.640625 -v -0.093750 -0.812500 0.640625 -v 0.046875 -0.851562 0.632812 -v -0.046875 -0.851562 0.632812 -v 0.000000 -0.859375 0.632812 -v 0.171875 0.218750 0.781250 -v -0.171875 0.218750 0.781250 -v 0.187500 0.156250 0.773438 -v -0.187500 0.156250 0.773438 -v 0.335938 0.429688 0.757812 -v -0.335938 0.429688 0.757812 -v 0.273438 0.421875 0.773438 -v -0.273438 0.421875 0.773438 -v 0.421875 0.398438 0.773438 -v -0.421875 0.398438 0.773438 -v 0.562500 0.351562 0.695312 -v -0.562500 0.351562 0.695312 -v 0.585938 0.289062 0.687500 -v -0.585938 0.289062 0.687500 -v 0.578125 0.195312 0.679688 -v -0.578125 0.195312 0.679688 -v 0.476562 0.101562 0.718750 -v -0.476562 0.101562 0.718750 -v 0.375000 0.062500 0.742188 -v -0.375000 0.062500 0.742188 -v 0.226562 0.109375 0.781250 -v -0.226562 0.109375 0.781250 -v 0.179688 0.296875 0.781250 -v -0.179688 0.296875 0.781250 -v 0.210938 0.375000 0.781250 -v -0.210938 0.375000 0.781250 -v 0.234375 0.359375 0.757812 -v -0.234375 0.359375 0.757812 -v 0.195312 0.296875 0.757812 -v -0.195312 0.296875 0.757812 -v 0.242188 0.125000 0.757812 -v -0.242188 0.125000 0.757812 -v 0.375000 0.085938 0.726562 -v -0.375000 0.085938 0.726562 -v 0.460938 0.117188 0.703125 -v -0.460938 0.117188 0.703125 -v 0.546875 0.210938 0.671875 -v -0.546875 0.210938 0.671875 -v 0.554688 0.281250 0.671875 -v -0.554688 0.281250 0.671875 -v 0.531250 0.335938 0.679688 -v -0.531250 0.335938 0.679688 -v 0.414062 0.390625 0.750000 -v -0.414062 0.390625 0.750000 -v 0.281250 0.398438 0.765625 -v -0.281250 0.398438 0.765625 -v 0.335938 0.406250 0.750000 -v -0.335938 0.406250 0.750000 -v 0.203125 0.171875 0.750000 -v -0.203125 0.171875 0.750000 -v 0.195312 0.226562 0.750000 -v -0.195312 0.226562 0.750000 -v 0.109375 0.460938 0.609375 -v -0.109375 0.460938 0.609375 -v 0.195312 0.664062 0.617188 -v -0.195312 0.664062 0.617188 -v 0.335938 0.687500 0.593750 -v -0.335938 0.687500 0.593750 -v 0.484375 0.554688 0.554688 -v -0.484375 0.554688 0.554688 -v 0.679688 0.453125 0.492188 -v -0.679688 0.453125 0.492188 -v 0.796875 0.406250 0.460938 -v -0.796875 0.406250 0.460938 -v 0.773438 0.164062 0.375000 -v -0.773438 0.164062 0.375000 -v 0.601562 0.000000 0.414062 -v -0.601562 0.000000 0.414062 -v 0.437500 -0.093750 0.468750 -v -0.437500 -0.093750 0.468750 -v 0.000000 0.898438 0.289062 -v 0.000000 0.984375 -0.078125 -v 0.000000 -0.195312 -0.671875 -v 0.000000 -0.460938 0.187500 -v 0.000000 -0.976562 0.460938 -v 0.000000 -0.804688 0.343750 -v 0.000000 -0.570312 0.320312 -v 0.000000 -0.484375 0.281250 -v 0.851562 0.234375 0.054688 -v -0.851562 0.234375 0.054688 -v 0.859375 0.320312 -0.046875 -v -0.859375 0.320312 -0.046875 -v 0.773438 0.265625 -0.437500 -v -0.773438 0.265625 -0.437500 -v 0.460938 0.437500 -0.703125 -v -0.460938 0.437500 -0.703125 -v 0.734375 -0.046875 0.070312 -v -0.734375 -0.046875 0.070312 -v 0.593750 -0.125000 -0.164062 -v -0.593750 -0.125000 -0.164062 -v 0.640625 -0.007812 -0.429688 -v -0.640625 -0.007812 -0.429688 -v 0.335938 0.054688 -0.664062 -v -0.335938 0.054688 -0.664062 -v 0.234375 -0.351562 0.406250 -v -0.234375 -0.351562 0.406250 -v 0.179688 -0.414062 0.257812 -v -0.179688 -0.414062 0.257812 -v 0.289062 -0.710938 0.382812 -v -0.289062 -0.710938 0.382812 -v 0.250000 -0.500000 0.390625 -v -0.250000 -0.500000 0.390625 -v 0.328125 -0.914062 0.398438 -v -0.328125 -0.914062 0.398438 -v 0.140625 -0.757812 0.367188 -v -0.140625 -0.757812 0.367188 -v 0.125000 -0.539062 0.359375 -v -0.125000 -0.539062 0.359375 -v 0.164062 -0.945312 0.437500 -v -0.164062 -0.945312 0.437500 -v 0.218750 -0.281250 0.429688 -v -0.218750 -0.281250 0.429688 -v 0.210938 -0.226562 0.468750 -v -0.210938 -0.226562 0.468750 -v 0.203125 -0.171875 0.500000 -v -0.203125 -0.171875 0.500000 -v 0.210938 -0.390625 0.164062 -v -0.210938 -0.390625 0.164062 -v 0.296875 -0.312500 -0.265625 -v -0.296875 -0.312500 -0.265625 -v 0.343750 -0.148438 -0.539062 -v -0.343750 -0.148438 -0.539062 -v 0.453125 0.867188 -0.382812 -v -0.453125 0.867188 -0.382812 -v 0.453125 0.929688 -0.070312 -v -0.453125 0.929688 -0.070312 -v 0.453125 0.851562 0.234375 -v -0.453125 0.851562 0.234375 -v 0.460938 0.523438 0.429688 -v -0.460938 0.523438 0.429688 -v 0.726562 0.406250 0.335938 -v -0.726562 0.406250 0.335938 -v 0.632812 0.453125 0.281250 -v -0.632812 0.453125 0.281250 -v 0.640625 0.703125 0.054688 -v -0.640625 0.703125 0.054688 -v 0.796875 0.562500 0.125000 -v -0.796875 0.562500 0.125000 -v 0.796875 0.617188 -0.117188 -v -0.796875 0.617188 -0.117188 -v 0.640625 0.750000 -0.195312 -v -0.640625 0.750000 -0.195312 -v 0.640625 0.679688 -0.445312 -v -0.640625 0.679688 -0.445312 -v 0.796875 0.539062 -0.359375 -v -0.796875 0.539062 -0.359375 -v 0.617188 0.328125 -0.585938 -v -0.617188 0.328125 -0.585938 -v 0.484375 0.023438 -0.546875 -v -0.484375 0.023438 -0.546875 -v 0.820312 0.328125 -0.203125 -v -0.820312 0.328125 -0.203125 -v 0.406250 -0.171875 0.148438 -v -0.406250 -0.171875 0.148438 -v 0.429688 -0.195312 -0.210938 -v -0.429688 -0.195312 -0.210938 -v 0.890625 0.406250 -0.234375 -v -0.890625 0.406250 -0.234375 -v 0.773438 -0.140625 -0.125000 -v -0.773438 -0.140625 -0.125000 -v 1.039062 -0.101562 -0.328125 -v -1.039062 -0.101562 -0.328125 -v 1.281250 0.054688 -0.429688 -v -1.281250 0.054688 -0.429688 -v 1.351562 0.320312 -0.421875 -v -1.351562 0.320312 -0.421875 -v 1.234375 0.507812 -0.421875 -v -1.234375 0.507812 -0.421875 -v 1.023438 0.476562 -0.312500 -v -1.023438 0.476562 -0.312500 -v 1.015625 0.414062 -0.289062 -v -1.015625 0.414062 -0.289062 -v 1.187500 0.437500 -0.390625 -v -1.187500 0.437500 -0.390625 -v 1.265625 0.289062 -0.406250 -v -1.265625 0.289062 -0.406250 -v 1.210938 0.078125 -0.406250 -v -1.210938 0.078125 -0.406250 -v 1.031250 -0.039062 -0.304688 -v -1.031250 -0.039062 -0.304688 -v 0.828125 -0.070312 -0.132812 -v -0.828125 -0.070312 -0.132812 -v 0.921875 0.359375 -0.218750 -v -0.921875 0.359375 -0.218750 -v 0.945312 0.304688 -0.289062 -v -0.945312 0.304688 -0.289062 -v 0.882812 -0.023438 -0.210938 -v -0.882812 -0.023438 -0.210938 -v 1.039062 0.000000 -0.367188 -v -1.039062 0.000000 -0.367188 -v 1.187500 0.093750 -0.445312 -v -1.187500 0.093750 -0.445312 -v 1.234375 0.250000 -0.445312 -v -1.234375 0.250000 -0.445312 -v 1.171875 0.359375 -0.437500 -v -1.171875 0.359375 -0.437500 -v 1.023438 0.343750 -0.359375 -v -1.023438 0.343750 -0.359375 -v 0.843750 0.289062 -0.210938 -v -0.843750 0.289062 -0.210938 -v 0.835938 0.171875 -0.273438 -v -0.835938 0.171875 -0.273438 -v 0.757812 0.093750 -0.273438 -v -0.757812 0.093750 -0.273438 -v 0.820312 0.085938 -0.273438 -v -0.820312 0.085938 -0.273438 -v 0.843750 0.015625 -0.273438 -v -0.843750 0.015625 -0.273438 -v 0.812500 -0.015625 -0.273438 -v -0.812500 -0.015625 -0.273438 -v 0.726562 0.000000 -0.070312 -v -0.726562 0.000000 -0.070312 -v 0.718750 -0.023438 -0.171875 -v -0.718750 -0.023438 -0.171875 -v 0.718750 0.039062 -0.187500 -v -0.718750 0.039062 -0.187500 -v 0.796875 0.203125 -0.210938 -v -0.796875 0.203125 -0.210938 -v 0.890625 0.242188 -0.265625 -v -0.890625 0.242188 -0.265625 -v 0.890625 0.234375 -0.320312 -v -0.890625 0.234375 -0.320312 -v 0.812500 -0.015625 -0.320312 -v -0.812500 -0.015625 -0.320312 -v 0.851562 0.015625 -0.320312 -v -0.851562 0.015625 -0.320312 -v 0.828125 0.078125 -0.320312 -v -0.828125 0.078125 -0.320312 -v 0.765625 0.093750 -0.320312 -v -0.765625 0.093750 -0.320312 -v 0.843750 0.171875 -0.320312 -v -0.843750 0.171875 -0.320312 -v 1.039062 0.328125 -0.414062 -v -1.039062 0.328125 -0.414062 -v 1.187500 0.343750 -0.484375 -v -1.187500 0.343750 -0.484375 -v 1.257812 0.242188 -0.492188 -v -1.257812 0.242188 -0.492188 -v 1.210938 0.085938 -0.484375 -v -1.210938 0.085938 -0.484375 -v 1.046875 0.000000 -0.421875 -v -1.046875 0.000000 -0.421875 -v 0.882812 -0.015625 -0.265625 -v -0.882812 -0.015625 -0.265625 -v 0.953125 0.289062 -0.343750 -v -0.953125 0.289062 -0.343750 -v 0.890625 0.109375 -0.328125 -v -0.890625 0.109375 -0.328125 -v 0.937500 0.062500 -0.335938 -v -0.937500 0.062500 -0.335938 -v 1.000000 0.125000 -0.367188 -v -1.000000 0.125000 -0.367188 -v 0.960938 0.171875 -0.351562 -v -0.960938 0.171875 -0.351562 -v 1.015625 0.234375 -0.375000 -v -1.015625 0.234375 -0.375000 -v 1.054688 0.187500 -0.382812 -v -1.054688 0.187500 -0.382812 -v 1.109375 0.210938 -0.390625 -v -1.109375 0.210938 -0.390625 -v 1.085938 0.273438 -0.390625 -v -1.085938 0.273438 -0.390625 -v 1.023438 0.437500 -0.484375 -v -1.023438 0.437500 -0.484375 -v 1.250000 0.468750 -0.546875 -v -1.250000 0.468750 -0.546875 -v 1.367188 0.296875 -0.500000 -v -1.367188 0.296875 -0.500000 -v 1.312500 0.054688 -0.531250 -v -1.312500 0.054688 -0.531250 -v 1.039062 -0.085938 -0.492188 -v -1.039062 -0.085938 -0.492188 -v 0.789062 -0.125000 -0.328125 -v -0.789062 -0.125000 -0.328125 -v 0.859375 0.382812 -0.382812 -v -0.859375 0.382812 -0.382812 -vn 0.6650 -0.2008 0.7194 -vn -0.6650 -0.2008 0.7194 -vn 0.8294 -0.3036 0.4689 -vn -0.8294 -0.3036 0.4689 -vn 0.4155 -0.7933 0.4449 -vn -0.4155 -0.7933 0.4449 -vn 0.3600 -0.5089 0.7820 -vn -0.3600 -0.5089 0.7820 -vn -0.0787 -0.5394 0.8384 -vn 0.0787 -0.5394 0.8384 -vn -0.2696 -0.8413 0.4685 -vn 0.2696 -0.8413 0.4685 -vn -0.7707 -0.3352 0.5420 -vn 0.7707 -0.3352 0.5420 -vn -0.4689 -0.1940 0.8617 -vn 0.4689 -0.1940 0.8617 -vn -0.4767 0.1907 0.8581 -vn 0.4767 0.1907 0.8581 -vn -0.7672 0.3264 0.5521 -vn 0.7672 0.3264 0.5521 -vn -0.2519 0.8173 0.5182 -vn 0.2519 0.8173 0.5182 -vn -0.0949 0.5696 0.8164 -vn 0.0949 0.5696 0.8164 -vn 0.3667 0.5370 0.7597 -vn -0.3667 0.5370 0.7597 -vn 0.4141 0.7672 0.4898 -vn -0.4141 0.7672 0.4898 -vn 0.8277 0.2952 0.4771 -vn -0.8277 0.2952 0.4771 -vn 0.6713 0.1971 0.7145 -vn -0.6713 0.1971 0.7145 -vn 0.8111 0.3244 -0.4867 -vn -0.8111 0.3244 -0.4867 -vn 0.2052 0.8206 -0.5334 -vn -0.2052 0.8206 -0.5334 -vn -0.4223 0.7806 -0.4607 -vn 0.4223 0.7806 -0.4607 -vn -0.8241 0.3225 -0.4658 -vn 0.8241 0.3225 -0.4658 -vn -0.8137 -0.3487 -0.4650 -vn 0.8137 -0.3487 -0.4650 -vn -0.4223 -0.7806 -0.4607 -vn 0.4223 -0.7806 -0.4607 -vn 0.2052 -0.8206 -0.5334 -vn -0.2052 -0.8206 -0.5334 -vn 0.7995 -0.3510 -0.4875 -vn -0.7995 -0.3510 -0.4875 -vn 0.4000 -0.0623 0.9144 -vn -0.4000 -0.0623 0.9144 -vn 0.3069 -0.1754 0.9354 -vn -0.3069 -0.1754 0.9354 -vn 0.0945 -0.1835 0.9785 -vn -0.0945 -0.1835 0.9785 -vn -0.0624 -0.0283 0.9977 -vn 0.0624 -0.0283 0.9977 -vn -0.0624 0.0260 0.9977 -vn 0.0624 0.0260 0.9977 -vn 0.0996 0.1729 0.9799 -vn -0.0996 0.1729 0.9799 -vn 0.3036 0.1656 0.9383 -vn -0.3036 0.1656 0.9383 -vn 0.4002 0.0572 0.9147 -vn -0.4002 0.0572 0.9147 -vn 0.1231 -0.8616 0.4924 -vn -0.1231 -0.8616 0.4924 -vn 0.2190 -0.8647 0.4520 -vn -0.2190 -0.8647 0.4520 -vn 0.5902 -0.4550 0.6668 -vn -0.5902 -0.4550 0.6668 -vn 0.7689 -0.0506 0.6374 -vn -0.7689 -0.0506 0.6374 -vn 0.7796 0.0900 0.6197 -vn -0.7796 0.0900 0.6197 -vn 0.3241 -0.8188 0.4739 -vn -0.3241 -0.8188 0.4739 -vn 0.3857 -0.6629 0.6417 -vn -0.3857 -0.6629 0.6417 -vn 0.6895 -0.4193 0.5906 -vn -0.6895 -0.4193 0.5906 -vn 0.6588 -0.3634 0.6588 -vn -0.6588 -0.3634 0.6588 -vn 0.5465 0.3707 0.7509 -vn -0.5465 0.3707 0.7509 -vn 0.5064 0.6464 0.5706 -vn -0.5064 0.6464 0.5706 -vn 0.6092 0.5167 0.6015 -vn -0.6092 0.5167 0.6015 -vn -0.0441 0.6610 0.7491 -vn 0.0441 0.6610 0.7491 -vn -0.7246 0.3187 0.6110 -vn 0.7246 0.3187 0.6110 -vn -0.5880 0.5554 0.5880 -vn 0.5880 0.5554 0.5880 -vn 0.5361 -0.3909 0.7482 -vn -0.5361 -0.3909 0.7482 -vn 0.2207 -0.4690 0.8552 -vn -0.2207 -0.4690 0.8552 -vn -0.0794 -0.5321 0.8429 -vn 0.0794 -0.5321 0.8429 -vn -0.0825 -0.6575 0.7490 -vn 0.0825 -0.6575 0.7490 -vn 0.0457 -0.5667 0.8226 -vn -0.0457 -0.5667 0.8226 -vn 0.2784 -0.2130 0.9365 -vn -0.2784 -0.2130 0.9365 -vn 0.3813 -0.1824 0.9063 -vn -0.3813 -0.1824 0.9063 -vn 0.3357 -0.2878 0.8969 -vn -0.3357 -0.2878 0.8969 -vn 0.3762 0.0603 0.9246 -vn -0.3762 0.0603 0.9246 -vn -0.1352 0.2680 0.9539 -vn 0.1352 0.2680 0.9539 -vn 0.3961 -0.4321 0.8102 -vn -0.3961 -0.4321 0.8102 -vn 0.1856 -0.2474 0.9510 -vn -0.1856 -0.2474 0.9510 -vn 0.0099 -0.1948 0.9808 -vn -0.0099 -0.1948 0.9808 -vn 0.0721 -0.6966 0.7138 -vn -0.0721 -0.6966 0.7138 -vn 0.1863 -0.5723 0.7986 -vn -0.1863 -0.5723 0.7986 -vn 0.3157 -0.2708 0.9094 -vn -0.3157 -0.2708 0.9094 -vn 0.3063 -0.0265 0.9516 -vn -0.3063 -0.0265 0.9516 -vn 0.3266 -0.1306 0.9361 -vn -0.3266 -0.1306 0.9361 -vn -0.0137 0.0574 0.9983 -vn 0.0137 0.0574 0.9983 -vn -0.0026 -0.0656 0.9978 -vn 0.0026 -0.0656 0.9978 -vn -0.0000 -0.0000 1.0000 -vn 0.8174 -0.5744 -0.0442 -vn -0.8174 -0.5744 -0.0442 -vn 0.9494 0.2297 -0.2144 -vn -0.9494 0.2297 -0.2144 -vn 0.0825 0.9073 -0.4124 -vn -0.0825 0.9073 -0.4124 -vn -0.8836 0.3555 0.3047 -vn 0.8836 0.3555 0.3047 -vn 0.4207 -0.8797 0.2218 -vn -0.4207 -0.8797 0.2218 -vn 0.2873 -0.5747 0.7663 -vn -0.2873 -0.5747 0.7663 -vn -0.6542 0.6019 0.4580 -vn 0.6542 0.6019 0.4580 -vn 0.1052 0.7892 0.6051 -vn -0.1052 0.7892 0.6051 -vn 0.7582 0.2916 0.5832 -vn -0.7582 0.2916 0.5832 -vn 0.3889 -0.7130 0.5834 -vn -0.3889 -0.7130 0.5834 -vn 0.0463 0.2314 0.9718 -vn -0.0463 0.2314 0.9718 -vn 0.0335 -0.4018 0.9151 -vn -0.0335 -0.4018 0.9151 -vn -0.4452 -0.1610 0.8809 -vn 0.4452 -0.1610 0.8809 -vn -0.2182 -0.4364 0.8729 -vn 0.2182 -0.4364 0.8729 -vn 0.4341 -0.1290 0.8916 -vn -0.4341 -0.1290 0.8916 -vn 0.3008 0.0501 0.9524 -vn -0.3008 0.0501 0.9524 -vn 0.8123 0.3010 0.4996 -vn -0.8123 0.3010 0.4996 -vn 0.8753 0.2574 0.4093 -vn -0.8753 0.2574 0.4093 -vn 0.9385 0.1601 0.3060 -vn -0.9385 0.1601 0.3060 -vn 0.2237 -0.6539 0.7227 -vn -0.2237 -0.6539 0.7227 -vn -0.1536 -0.1997 0.9677 -vn 0.1536 -0.1997 0.9677 -vn -0.2733 -0.1025 0.9565 -vn 0.2733 -0.1025 0.9565 -vn -0.0976 0.1952 0.9759 -vn 0.0976 0.1952 0.9759 -vn -0.1582 0.9494 0.2713 -vn 0.1582 0.9494 0.2713 -vn -0.6934 0.7082 0.1328 -vn 0.6934 0.7082 0.1328 -vn -1.0000 -0.0000 -0.0000 -vn 1.0000 -0.0000 -0.0000 -vn 0.3051 -0.9450 0.1181 -vn -0.3051 -0.9450 0.1181 -vn 0.0298 -0.2981 0.9541 -vn -0.0298 -0.2981 0.9541 -vn 0.1353 -0.3479 0.9277 -vn -0.1353 -0.3479 0.9277 -vn -0.5085 -0.2755 0.8158 -vn 0.5085 -0.2755 0.8158 -vn -0.3843 -0.0419 0.9223 -vn 0.3843 -0.0419 0.9223 -vn -0.2083 0.0374 0.9774 -vn 0.2083 0.0374 0.9774 -vn -0.5721 -0.4767 0.6674 -vn 0.5721 -0.4767 0.6674 -vn -0.1369 -0.7531 0.6435 -vn 0.1369 -0.7531 0.6435 -vn 0.4088 -0.6071 0.6814 -vn -0.4088 -0.6071 0.6814 -vn 0.5740 -0.4130 0.7070 -vn -0.5740 -0.4130 0.7070 -vn 0.5665 -0.0968 0.8183 -vn -0.5665 -0.0968 0.8183 -vn 0.5703 0.1180 0.8129 -vn -0.5703 0.1180 0.8129 -vn 0.4823 0.5621 0.6719 -vn -0.4823 0.5621 0.6719 -vn 0.2604 0.6114 0.7473 -vn -0.2604 0.6114 0.7473 -vn 0.1640 0.3607 0.9182 -vn -0.1640 0.3607 0.9182 -vn -0.0178 0.2495 0.9682 -vn 0.0178 0.2495 0.9682 -vn 0.3273 -0.4166 0.8481 -vn -0.3273 -0.4166 0.8481 -vn 0.2811 -0.2610 0.9235 -vn -0.2811 -0.2610 0.9235 -vn -0.2542 -0.6514 0.7149 -vn 0.2542 -0.6514 0.7149 -vn -0.0260 -0.8455 0.5333 -vn 0.0260 -0.8455 0.5333 -vn -0.3518 -0.2606 0.8991 -vn 0.3518 -0.2606 0.8991 -vn -0.3523 -0.0110 0.9358 -vn 0.3523 -0.0110 0.9358 -vn -0.1317 0.4608 0.8777 -vn 0.1317 0.4608 0.8777 -vn -0.0342 0.6159 0.7870 -vn 0.0342 0.6159 0.7870 -vn 0.3603 0.5836 0.7277 -vn -0.3603 0.5836 0.7277 -vn 0.4988 0.5300 0.6858 -vn -0.4988 0.5300 0.6858 -vn 0.6667 -0.3333 0.6667 -vn -0.6667 -0.3333 0.6667 -vn 0.8165 -0.0731 0.5727 -vn -0.8165 -0.0731 0.5727 -vn 0.7840 0.1161 0.6098 -vn -0.7840 0.1161 0.6098 -vn -0.5306 0.8111 -0.2461 -vn 0.5306 0.8111 -0.2461 -vn -0.8511 0.3695 -0.3730 -vn 0.8511 0.3695 -0.3730 -vn -0.2446 0.8675 -0.4331 -vn 0.2446 0.8675 -0.4331 -vn 0.5924 0.7465 -0.3030 -vn -0.5924 0.7465 -0.3030 -vn 0.3685 0.8758 -0.3118 -vn -0.3685 0.8758 -0.3118 -vn 0.2821 0.9151 -0.2880 -vn -0.2821 0.9151 -0.2880 -vn 0.8561 0.1340 -0.4991 -vn -0.8561 0.1340 -0.4991 -vn 0.5342 -0.7233 -0.4376 -vn -0.5342 -0.7233 -0.4376 -vn 0.3849 -0.8131 -0.4368 -vn -0.3849 -0.8131 -0.4368 -vn 0.2335 -0.5806 -0.7800 -vn -0.2335 -0.5806 -0.7800 -vn 0.2449 -0.0583 -0.9678 -vn -0.2449 -0.0583 -0.9678 -vn 0.1163 -0.4535 -0.8837 -vn -0.1163 -0.4535 -0.8837 -vn 0.1152 -0.9836 -0.1388 -vn -0.1152 -0.9836 -0.1388 -vn 0.1184 -0.9669 -0.2260 -vn -0.1184 -0.9669 -0.2260 -vn 0.9597 -0.0085 -0.2808 -vn -0.9597 -0.0085 -0.2808 -vn 0.9319 0.1629 -0.3242 -vn -0.9319 0.1629 -0.3242 -vn 0.1626 0.0207 -0.9865 -vn -0.1626 0.0207 -0.9865 -vn -0.0188 -0.2177 -0.9758 -vn 0.0188 -0.2177 -0.9758 -vn 0.7538 -0.2926 -0.5884 -vn -0.7538 -0.2926 -0.5884 -vn 0.9196 0.1379 -0.3678 -vn -0.9196 0.1379 -0.3678 -vn 0.9297 0.3127 -0.1944 -vn -0.9297 0.3127 -0.1944 -vn 0.9120 0.3376 -0.2329 -vn -0.9120 0.3376 -0.2329 -vn 0.9407 0.3338 -0.0607 -vn -0.9407 0.3338 -0.0607 -vn 0.1761 -0.8805 -0.4402 -vn -0.1761 -0.8805 -0.4402 -vn 0.3708 -0.4733 -0.7991 -vn -0.3708 -0.4733 -0.7991 -vn 0.3107 -0.8284 -0.4660 -vn -0.3107 -0.8284 -0.4660 -vn 0.2793 -0.9515 -0.1287 -vn -0.2793 -0.9515 -0.1287 -vn 0.3139 -0.9321 -0.1807 -vn -0.3139 -0.9321 -0.1807 -vn 0.9762 -0.2083 -0.0609 -vn -0.9762 -0.2083 -0.0609 -vn 0.8267 -0.5066 0.2447 -vn -0.8267 -0.5066 0.2447 -vn 0.3449 -0.1158 -0.9315 -vn -0.3449 -0.1158 -0.9315 -vn 0.1203 0.9644 0.2355 -vn -0.1203 0.9644 0.2355 -vn 0.1275 0.9744 -0.1851 -vn -0.1275 0.9744 -0.1851 -vn 0.3492 0.5947 -0.7241 -vn -0.3492 0.5947 -0.7241 -vn 0.4153 0.8981 -0.1449 -vn -0.4153 0.8981 -0.1449 -vn 0.1845 0.7036 0.6863 -vn -0.1845 0.7036 0.6863 -vn 0.6056 0.7794 0.1608 -vn -0.6056 0.7794 0.1608 -vn 0.7033 0.6806 -0.2053 -vn -0.7033 0.6806 -0.2053 -vn 0.6679 0.2007 -0.7166 -vn -0.6679 0.2007 -0.7166 -vn 0.4948 0.4342 -0.7528 -vn -0.4948 0.4342 -0.7528 -vn 0.6423 0.7459 -0.1761 -vn -0.6423 0.7459 -0.1761 -vn 0.7182 0.6788 0.1530 -vn -0.7182 0.6788 0.1530 -vn 0.7388 0.3972 0.5444 -vn -0.7388 0.3972 0.5444 -vn 0.3428 0.9261 -0.1579 -vn -0.3428 0.9261 -0.1579 -vn 0.2270 0.5740 0.7867 -vn -0.2270 0.5740 0.7867 -vn -0.1722 0.1046 -0.9795 -vn 0.1722 0.1046 -0.9795 -vn 0.0425 0.9150 0.4013 -vn -0.0425 0.9150 0.4013 -vn -0.1616 0.1847 0.9694 -vn 0.1616 0.1847 0.9694 -vn 0.9791 0.1973 0.0483 -vn -0.9791 0.1973 0.0483 -vn 0.9470 0.0918 0.3079 -vn -0.9470 0.0918 0.3079 -vn 0.9794 0.1905 -0.0661 -vn -0.9794 0.1905 -0.0661 -vn 0.9938 0.0312 -0.1070 -vn -0.9938 0.0312 -0.1070 -vn 0.7116 -0.7008 0.0501 -vn -0.7116 -0.7008 0.0501 -vn 0.3722 -0.9243 0.0847 -vn -0.3722 -0.9243 0.0847 -vn 0.4465 -0.8644 0.2310 -vn -0.4465 -0.8644 0.2310 -vn 0.6066 -0.7578 0.2405 -vn -0.6066 -0.7578 0.2405 -vn 0.7325 -0.6368 0.2407 -vn -0.7325 -0.6368 0.2407 -vn 0.2637 -0.4499 0.8533 -vn -0.2637 -0.4499 0.8533 -vn 0.5568 -0.3181 -0.7673 -vn -0.5568 -0.3181 -0.7673 -vn 0.5004 -0.2807 -0.8190 -vn -0.5004 -0.2807 -0.8190 -vn 0.3190 -0.8494 -0.4205 -vn -0.3190 -0.8494 -0.4205 -vn 0.7198 -0.6356 -0.2793 -vn -0.7198 -0.6356 -0.2793 -vn 0.4972 -0.4408 -0.7473 -vn -0.4972 -0.4408 -0.7473 -vn 0.3506 0.3807 0.8557 -vn -0.3506 0.3807 0.8557 -vn 0.4566 0.1715 0.8730 -vn -0.4566 0.1715 0.8730 -vn 0.2583 0.1055 0.9603 -vn -0.2583 0.1055 0.9603 -vn 0.2455 -0.0802 0.9661 -vn -0.2455 -0.0802 0.9661 -vn 0.4643 -0.0599 0.8837 -vn -0.4643 -0.0599 0.8837 -vn 0.6225 -0.3045 0.7210 -vn -0.6225 -0.3045 0.7210 -vn 0.4500 0.6590 0.6027 -vn -0.4500 0.6590 0.6027 -vn -0.2667 0.8309 0.4884 -vn 0.2667 0.8309 0.4884 -vn -0.8284 0.2291 0.5111 -vn 0.8284 0.2291 0.5111 -vn -0.5251 -0.3566 0.7727 -vn 0.5251 -0.3566 0.7727 -vn 0.4546 -0.5665 0.6873 -vn -0.4546 -0.5665 0.6873 -vn 0.6996 -0.4497 0.5552 -vn -0.6996 -0.4497 0.5552 -vn 0.7220 -0.6827 -0.1126 -vn -0.7220 -0.6827 -0.1126 -vn -0.1919 0.2860 0.9388 -vn 0.1919 0.2860 0.9388 -vn 0.9048 -0.3734 -0.2047 -vn -0.9048 -0.3734 -0.2047 -vn 0.1034 0.1551 0.9825 -vn -0.1034 0.1551 0.9825 -vn 0.0841 0.9318 0.3530 -vn -0.0841 0.9318 0.3530 -vn 0.6446 -0.0883 0.7594 -vn -0.6446 -0.0883 0.7594 -vn 0.4309 0.4740 0.7678 -vn -0.4309 0.4740 0.7678 -vn 0.8032 -0.4847 0.3462 -vn -0.8032 -0.4847 0.3462 -vn 0.5811 -0.4128 0.7014 -vn -0.5811 -0.4128 0.7014 -vn 0.5910 -0.4305 0.6822 -vn -0.5910 -0.4305 0.6822 -vn 0.9818 -0.1804 -0.0591 -vn -0.9818 -0.1804 -0.0591 -vn 0.9105 -0.3965 -0.1175 -vn -0.9105 -0.3965 -0.1175 -vn 0.9972 -0.0181 -0.0725 -vn -0.9972 -0.0181 -0.0725 -vn 0.7313 -0.6543 0.1925 -vn -0.7313 -0.6543 0.1925 -vn 0.7867 -0.6079 0.1073 -vn -0.7867 -0.6079 0.1073 -vn 0.7022 -0.7022 0.1170 -vn -0.7022 -0.7022 0.1170 -vn 0.1840 0.9816 -0.0511 -vn -0.1840 0.9816 -0.0511 -vn 0.9352 0.3301 0.1284 -vn -0.9352 0.3301 0.1284 -vn 0.6633 -0.7463 0.0553 -vn -0.6633 -0.7463 0.0553 -vn -0.0085 0.9970 0.0767 -vn 0.0085 0.9970 0.0767 -vn 0.6237 -0.7061 0.3354 -vn -0.6237 -0.7061 0.3354 -vn 0.2733 -0.8925 0.3587 -vn -0.2733 -0.8925 0.3587 -vn -0.8328 -0.5080 -0.2200 -vn 0.8328 -0.5080 -0.2200 -vn -0.8339 0.2377 -0.4981 -vn 0.8339 0.2377 -0.4981 -vn -0.5655 0.7847 -0.2539 -vn 0.5655 0.7847 -0.2539 -vn -0.0560 0.9962 0.0672 -vn 0.0560 0.9962 0.0672 -vn 0.1445 0.0222 0.9893 -vn -0.1445 0.0222 0.9893 -vn 0.3275 0.0645 0.9427 -vn -0.3275 0.0645 0.9427 -vn 0.3127 0.0232 0.9496 -vn -0.3127 0.0232 0.9496 -vn 0.1710 0.0274 0.9849 -vn -0.1710 0.0274 0.9849 -vn 0.3487 0.2849 0.8929 -vn -0.3487 0.2849 0.8929 -vn 0.4006 -0.0343 0.9156 -vn -0.4006 -0.0343 0.9156 -vn 0.2572 -0.0603 0.9645 -vn -0.2572 -0.0603 0.9645 -vn 0.0637 -0.0106 0.9979 -vn -0.0637 -0.0106 0.9979 -vn -0.3637 0.7039 0.6101 -vn 0.3637 0.7039 0.6101 -vn 0.6299 0.0355 0.7759 -vn -0.6299 0.0355 0.7759 -vn 0.4472 -0.2002 0.8717 -vn -0.4472 -0.2002 0.8717 -vn 0.5072 -0.2141 0.8348 -vn -0.5072 -0.2141 0.8348 -vn 0.5258 0.2619 0.8093 -vn -0.5258 0.2619 0.8093 -vn 0.2980 0.5802 0.7580 -vn -0.2980 0.5802 0.7580 -vn 0.0930 -0.9924 -0.0805 -vn -0.0930 -0.9924 -0.0805 -vn 0.5006 -0.8657 0.0080 -vn -0.5006 -0.8657 0.0080 -vn 0.9285 -0.2497 0.2748 -vn -0.9285 -0.2497 0.2748 -vn 0.8393 0.5424 -0.0378 -vn -0.8393 0.5424 -0.0378 -vn -0.2355 0.9367 -0.2589 -vn 0.2355 0.9367 -0.2589 -vn -0.4499 0.8838 -0.1285 -vn 0.4499 0.8838 -0.1285 -vn -0.5384 -0.0098 -0.8427 -vn 0.5384 -0.0098 -0.8427 -vn -0.1910 -0.0241 -0.9813 -vn 0.1910 -0.0241 -0.9813 -vn 0.4046 0.0266 -0.9141 -vn -0.4046 0.0266 -0.9141 -vn -0.7819 0.6231 0.0197 -vn 0.7819 0.6231 0.0197 -vn 0.5428 -0.2063 -0.8142 -vn -0.5428 -0.2063 -0.8142 -vn -0.2474 -0.9231 -0.2945 -vn 0.2474 -0.9231 -0.2945 -vt 0.898516 0.615168 -vt 0.889924 0.593688 -vt 0.917217 0.563713 -vt 0.938774 0.614911 -vt 0.816475 0.815667 -vt 0.842653 0.844479 -vt 0.834061 0.865960 -vt 0.795398 0.865726 -vt 0.951453 0.532635 -vt 0.985674 0.614766 -vt 0.781112 0.783437 -vt 0.746891 0.865568 -vt 0.866019 0.542156 -vt 0.865901 0.498414 -vt 0.866664 0.749216 -vt 0.866533 0.794590 -vt 0.866296 0.585096 -vt 0.866281 0.835888 -vt 0.844815 0.593688 -vt 0.814821 0.563713 -vt 0.916591 0.815667 -vt 0.887762 0.844479 -vt 0.780348 0.532635 -vt 0.952217 0.783437 -vt 0.793264 0.614911 -vt 0.746127 0.614766 -vt 0.986437 0.865568 -vt 0.937668 0.865726 -vt 0.836223 0.615168 -vt 0.896353 0.865960 -vt 0.844815 0.638796 -vt 0.814821 0.666108 -vt 0.916591 0.915784 -vt 0.887762 0.889588 -vt 0.780348 0.700318 -vt 0.952217 0.951120 -vt 0.866019 0.687665 -vt 0.865901 0.734539 -vt 0.866664 0.985341 -vt 0.866533 0.936861 -vt 0.866296 0.647388 -vt 0.866281 0.898180 -vt 0.889924 0.638796 -vt 0.917217 0.666108 -vt 0.816475 0.915784 -vt 0.842653 0.889588 -vt 0.951453 0.700318 -vt 0.781112 0.951120 -vt 0.884573 0.633599 -vt 0.890680 0.615278 -vt 0.847994 0.884400 -vt 0.841887 0.866078 -vt 0.866252 0.641233 -vt 0.866315 0.892033 -vt 0.849457 0.633599 -vt 0.883109 0.884400 -vt 0.841823 0.615278 -vt 0.890743 0.866078 -vt 0.849457 0.598483 -vt 0.883109 0.849284 -vt 0.866252 0.590849 -vt 0.866315 0.841650 -vt 0.884573 0.598483 -vt 0.847994 0.849284 -vt 0.866252 0.615278 -vt 0.866314 0.866078 -vt 0.519175 0.060448 -vt 0.515511 0.068449 -vt 0.499236 0.064527 -vt 0.499226 0.054411 -vt 0.482972 0.068490 -vt 0.479289 0.060503 -vt 0.528933 0.064984 -vt 0.521101 0.070625 -vt 0.477388 0.070678 -vt 0.469543 0.065062 -vt 0.531361 0.067741 -vt 0.524816 0.079366 -vt 0.473699 0.079422 -vt 0.467122 0.067822 -vt 0.543230 0.082916 -vt 0.527778 0.091040 -vt 0.470772 0.091098 -vt 0.455305 0.083023 -vt 0.564295 0.115333 -vt 0.536201 0.127706 -vt 0.462464 0.127779 -vt 0.434353 0.115495 -vt 0.585741 0.171832 -vt 0.625570 0.207851 -vt 0.599470 0.238064 -vt 0.534775 0.215506 -vt 0.399530 0.238288 -vt 0.373395 0.208159 -vt 0.413084 0.172042 -vt 0.464115 0.215568 -vt 0.649992 0.239957 -vt 0.636068 0.260938 -vt 0.363038 0.261221 -vt 0.349092 0.240294 -vt 0.688725 0.268431 -vt 0.655084 0.284130 -vt 0.344104 0.284413 -vt 0.310499 0.268805 -vt 0.683826 0.317538 -vt 0.658753 0.312921 -vt 0.340493 0.313152 -vt 0.315483 0.317795 -vt 0.670653 0.334491 -vt 0.648344 0.322238 -vt 0.350893 0.322437 -vt 0.328643 0.334694 -vt 0.636036 0.370629 -vt 0.612842 0.353000 -vt 0.386364 0.353108 -vt 0.363225 0.370732 -vt 0.601484 0.395542 -vt 0.593375 0.371207 -vt 0.405822 0.371277 -vt 0.397739 0.395588 -vt 0.580245 0.398434 -vt 0.576532 0.373285 -vt 0.422649 0.373341 -vt 0.418956 0.398468 -vt 0.531177 0.387347 -vt 0.538956 0.354297 -vt 0.460183 0.354339 -vt 0.467984 0.387365 -vt 0.499576 0.382860 -vt 0.499559 0.340417 -vt 0.558263 0.334249 -vt 0.578201 0.335550 -vt 0.420943 0.335646 -vt 0.440865 0.334326 -vt 0.591245 0.332772 -vt 0.407910 0.332885 -vt 0.605328 0.325379 -vt 0.393837 0.325516 -vt 0.627435 0.311447 -vt 0.371749 0.311636 -vt 0.634730 0.300287 -vt 0.364449 0.300507 -vt 0.630627 0.283646 -vt 0.368516 0.283889 -vt 0.610403 0.264181 -vt 0.388669 0.264411 -vt 0.591398 0.254700 -vt 0.407628 0.254897 -vt 0.558690 0.267603 -vt 0.440331 0.267717 -vt 0.549915 0.277739 -vt 0.499486 0.252445 -vt 0.449122 0.277832 -vt 0.543096 0.314455 -vt 0.455999 0.314523 -vt 0.499529 0.295691 -vt 0.542701 0.293295 -vt 0.456363 0.293371 -vt 0.505317 0.075368 -vt 0.499246 0.073497 -vt 0.493181 0.075382 -vt 0.510294 0.080611 -vt 0.488218 0.080633 -vt 0.510719 0.093440 -vt 0.487828 0.093457 -vt 0.512928 0.133399 -vt 0.512434 0.097171 -vt 0.486126 0.097191 -vt 0.485743 0.133422 -vt 0.518981 0.164589 -vt 0.499367 0.157864 -vt 0.499336 0.134352 -vt 0.479774 0.164615 -vt 0.499284 0.100063 -vt 0.499269 0.091346 -vt 0.529057 0.180195 -vt 0.520697 0.181536 -vt 0.516438 0.172335 -vt 0.482334 0.172353 -vt 0.478099 0.181560 -vt 0.469742 0.180239 -vt 0.522516 0.198783 -vt 0.517786 0.194706 -vt 0.481043 0.194728 -vt 0.476327 0.198817 -vt 0.499425 0.203867 -vt 0.507044 0.197287 -vt 0.491789 0.197295 -vt 0.499416 0.197278 -vt 0.499409 0.192167 -vt 0.499375 0.165170 -vt 0.512319 0.176937 -vt 0.499383 0.171718 -vt 0.486462 0.176949 -vt 0.499401 0.186182 -vt 0.507638 0.192399 -vt 0.491182 0.192407 -vt 0.514048 0.190694 -vt 0.484768 0.190709 -vt 0.515702 0.181858 -vt 0.483092 0.181873 -vt 0.542999 0.172734 -vt 0.455791 0.172814 -vt 0.540067 0.154824 -vt 0.458673 0.154899 -vt 0.577388 0.138981 -vt 0.421339 0.139176 -vt 0.582019 0.153952 -vt 0.416755 0.154155 -vt 0.499267 0.089977 -vt 0.508449 0.090711 -vt 0.490089 0.090724 -vt 0.508228 0.082219 -vt 0.490287 0.082236 -vt 0.503697 0.077074 -vt 0.494804 0.077083 -vt 0.499248 0.075473 -vt 0.501954 0.080835 -vt 0.499254 0.080320 -vt 0.496555 0.080840 -vt 0.504281 0.083193 -vt 0.494235 0.083202 -vt 0.505348 0.086781 -vt 0.493178 0.086791 -vt 0.499261 0.085637 -vt 0.561415 0.284224 -vt 0.558262 0.294780 -vt 0.437639 0.284338 -vt 0.440810 0.294882 -vt 0.562274 0.307050 -vt 0.436820 0.307151 -vt 0.569573 0.318594 -vt 0.429542 0.318697 -vt 0.568290 0.278113 -vt 0.430757 0.278245 -vt 0.588970 0.271589 -vt 0.410086 0.271772 -vt 0.601274 0.276977 -vt 0.397810 0.277180 -vt 0.616235 0.286959 -vt 0.382890 0.287175 -vt 0.617221 0.299000 -vt 0.381926 0.299197 -vt 0.615028 0.307259 -vt 0.384129 0.307438 -vt 0.597564 0.313582 -vt 0.401576 0.313725 -vt 0.589965 0.319671 -vt 0.409172 0.319797 -vt 0.581259 0.320877 -vt 0.417870 0.320991 -vt 0.577724 0.309274 -vt 0.583117 0.312767 -vt 0.421385 0.309394 -vt 0.416002 0.312892 -vt 0.588782 0.312203 -vt 0.410344 0.312336 -vt 0.595478 0.309103 -vt 0.403653 0.309249 -vt 0.605147 0.302344 -vt 0.393986 0.302517 -vt 0.606862 0.297335 -vt 0.392265 0.297520 -vt 0.606100 0.291021 -vt 0.393015 0.291216 -vt 0.597364 0.282892 -vt 0.401725 0.283083 -vt 0.588939 0.279989 -vt 0.410134 0.280167 -vt 0.575416 0.284480 -vt 0.423648 0.284624 -vt 0.571900 0.289783 -vt 0.427169 0.289915 -vt 0.572399 0.303223 -vt 0.426693 0.303342 -vt 0.571526 0.295591 -vt 0.427553 0.295716 -vt 0.528820 0.428638 -vt 0.499582 0.428449 -vt 0.470353 0.428636 -vt 0.583835 0.428591 -vt 0.415384 0.428598 -vt 0.621564 0.417863 -vt 0.377699 0.417889 -vt 0.665541 0.388861 -vt 0.333786 0.388954 -vt 0.700852 0.344252 -vt 0.298519 0.344467 -vt 0.711534 0.315224 -vt 0.287837 0.315527 -vt 0.703543 0.259804 -vt 0.295695 0.260225 -vt 0.672036 0.224472 -vt 0.327053 0.224880 -vt 0.639461 0.198488 -vt 0.359503 0.198841 -vt 0.601923 0.023020 -vt 0.623531 0.028944 -vt 0.634365 0.081724 -vt 0.597102 0.056491 -vt 0.364247 0.082198 -vt 0.374872 0.029461 -vt 0.396447 0.023452 -vt 0.401383 0.056841 -vt 0.545071 0.009261 -vt 0.554761 0.040719 -vt 0.453219 0.009442 -vt 0.443646 0.040906 -vt 0.499203 0.033223 -vt 0.525595 0.049432 -vt 0.472841 0.049514 -vt 0.535690 0.060692 -vt 0.462778 0.060794 -vt 0.556987 0.064968 -vt 0.441501 0.065137 -vt 0.586699 0.084908 -vt 0.411873 0.085177 -vt 0.605919 0.113619 -vt 0.392761 0.113940 -vt 0.611410 0.133352 -vt 0.604255 0.151220 -vt 0.394533 0.151503 -vt 0.387337 0.133675 -vt 0.599523 0.162726 -vt 0.399292 0.162985 -vt 0.921561 0.062683 -vt 0.858848 0.116790 -vt 0.822344 0.100435 -vt 0.860246 0.025793 -vt 0.176713 0.101559 -vt 0.140353 0.117994 -vt 0.077626 0.064228 -vt 0.138658 0.027230 -vt 0.764738 0.086041 -vt 0.773234 0.009669 -vt 0.234137 0.087001 -vt 0.225396 0.010837 -vt 0.660621 0.086994 -vt 0.645431 0.028106 -vt 0.338048 0.087561 -vt 0.352990 0.028716 -vt 0.742764 0.197916 -vt 0.765725 0.232421 -vt 0.256408 0.198572 -vt 0.233590 0.233065 -vt 0.987287 0.152728 -vt 0.888944 0.176456 -vt 0.110522 0.177602 -vt 0.012367 0.154243 -vt 0.900013 0.420388 -vt 0.844154 0.346818 -vt 0.881002 0.308389 -vt 0.959159 0.356531 -vt 0.118831 0.309153 -vt 0.155683 0.347370 -vt 0.100193 0.420886 -vt 0.041037 0.357385 -vt 0.903058 0.260405 -vt 0.999860 0.252255 -vt 0.096698 0.261367 -vt 0.000140 0.253531 -vt 0.724230 0.339961 -vt 0.744163 0.304222 -vt 0.765650 0.319075 -vt 0.255275 0.304629 -vt 0.275237 0.340212 -vt 0.233887 0.319494 -vt 0.792936 0.279898 -vt 0.826311 0.297169 -vt 0.206573 0.280495 -vt 0.173337 0.297810 -vt 0.823141 0.253863 -vt 0.854975 0.267153 -vt 0.176380 0.254612 -vt 0.144669 0.267956 -vt 0.835330 0.224364 -vt 0.867888 0.232592 -vt 0.164140 0.225225 -vt 0.131691 0.233525 -vt 0.812814 0.189821 -vt 0.850667 0.180124 -vt 0.148712 0.181145 -vt 0.186501 0.190705 -vt 0.771964 0.373733 -vt 0.227737 0.374010 -vt 0.744181 0.389903 -vt 0.255481 0.390057 -vt 0.824891 0.470849 -vt 0.175235 0.470987 -vt 0.792153 0.464650 -vt 0.742036 0.461609 -vt 0.726584 0.432597 -vt 0.273145 0.432574 -vt 0.257819 0.461543 -vt 0.207859 0.464714 -vt 0.795638 0.489991 -vt 0.204362 0.489991 -vt 0.785486 0.227075 -vt 0.213863 0.227788 -vt 0.802275 0.202593 -vt 0.197050 0.203415 -vt 0.768354 0.127888 -vt 0.690398 0.146106 -vt 0.230666 0.128771 -vt 0.308516 0.146686 -vt 0.779285 0.168550 -vt 0.219887 0.169386 -vt 0.803020 0.170278 -vt 0.830115 0.138457 -vt 0.169083 0.139518 -vt 0.196211 0.171181 -vt 0.560056 0.957116 -vt 0.449940 0.959188 -vt 0.462753 0.891851 -vt 0.530867 0.884581 -vt 0.272722 0.891853 -vt 0.285528 0.959188 -vt 0.175421 0.957118 -vt 0.204610 0.884582 -vt 0.590562 0.846978 -vt 0.701323 0.897070 -vt 0.144915 0.846977 -vt 0.034155 0.897069 -vt 0.590787 0.784411 -vt 0.669298 0.757013 -vt 0.144687 0.784410 -vt 0.066170 0.757009 -vt 0.560262 0.744213 -vt 0.603293 0.719157 -vt 0.175212 0.744211 -vt 0.132179 0.719155 -vt 0.520423 0.712528 -vt 0.554154 0.682132 -vt 0.215052 0.712528 -vt 0.181319 0.682131 -vt 0.468433 0.686792 -vt 0.493965 0.641024 -vt 0.267042 0.686791 -vt 0.241508 0.641024 -vt 0.498843 0.736977 -vt 0.464308 0.719593 -vt 0.271168 0.719592 -vt 0.236633 0.736977 -vt 0.526545 0.760754 -vt 0.208931 0.760754 -vt 0.543027 0.789442 -vt 0.192449 0.789442 -vt 0.539269 0.821849 -vt 0.196208 0.821849 -vt 0.510099 0.842380 -vt 0.225378 0.842380 -vt 0.470621 0.845320 -vt 0.264856 0.845322 -vt 0.785370 0.180300 -vt 0.769420 0.190471 -vt 0.213850 0.181127 -vt 0.229792 0.191225 -vt 0.393578 0.587505 -vt 0.395036 0.674023 -vt 0.341892 0.587509 -vt 0.340437 0.674021 -vt 0.385726 0.910555 -vt 0.399584 0.878316 -vt 0.335895 0.878320 -vt 0.349744 0.910558 -vt 0.429058 0.713731 -vt 0.306418 0.713731 -vt 0.415153 0.728622 -vt 0.378240 0.710772 -vt 0.357237 0.710776 -vt 0.320324 0.728623 -vt 0.408407 0.754607 -vt 0.418068 0.746526 -vt 0.327070 0.754610 -vt 0.317409 0.746528 -vt 0.413147 0.796172 -vt 0.377006 0.815186 -vt 0.358471 0.815189 -vt 0.322330 0.796174 -vt 0.433705 0.830975 -vt 0.301772 0.830977 -vt 0.797000 0.194826 -vt 0.801893 0.198928 -vt 0.197420 0.199758 -vt 0.202290 0.195652 -vt 0.788848 0.183842 -vt 0.210391 0.184671 -vt 0.451471 0.804090 -vt 0.475926 0.816075 -vt 0.284007 0.804092 -vt 0.259551 0.816076 -vt 0.433470 0.783920 -vt 0.302007 0.783922 -vt 0.424174 0.764959 -vt 0.311304 0.764961 -vt 0.430644 0.756350 -vt 0.304833 0.756352 -vt 0.435247 0.743457 -vt 0.300230 0.743459 -vt 0.441735 0.732686 -vt 0.293742 0.732687 -vt 0.460899 0.740871 -vt 0.274578 0.740871 -vt 0.499639 0.817768 -vt 0.235838 0.817769 -vt 0.513389 0.808597 -vt 0.222088 0.808598 -vt 0.515507 0.790754 -vt 0.219970 0.790754 -vt 0.504048 0.772385 -vt 0.231428 0.772385 -vt 0.484096 0.755033 -vt 0.251380 0.755033 -vt 0.453457 0.756171 -vt 0.445088 0.770458 -vt 0.282020 0.756172 -vt 0.290389 0.770460 -vt 0.472595 0.769899 -vt 0.462782 0.785392 -vt 0.262882 0.769899 -vt 0.272695 0.785393 -vt 0.490045 0.782999 -vt 0.481973 0.797121 -vt 0.245432 0.782999 -vt 0.253504 0.797122 -vt 0.501565 0.792591 -vt 0.499142 0.804019 -vt 0.233912 0.792591 -vt 0.236336 0.804020 -vt 0.559696 0.607669 -vt 0.598936 0.655817 -vt 0.175778 0.607667 -vt 0.136537 0.655816 -vt 0.629457 0.693271 -vt 0.106015 0.693269 -vt 0.653588 0.712098 -vt 0.081883 0.712096 -vt 0.712556 0.698473 -vt 0.662010 0.686010 -vt 0.022915 0.698478 -vt 0.073462 0.686010 -vt 0.715231 0.624373 -vt 0.652439 0.640199 -vt 0.020244 0.624367 -vt 0.083034 0.640197 -vt 0.706720 0.572934 -vt 0.633578 0.594596 -vt 0.028756 0.572934 -vt 0.101895 0.594596 -vt 0.615458 0.557583 -vt 0.703133 0.538140 -vt 0.032336 0.538145 -vt 0.120011 0.557584 -vt 0.512305 0.539237 -vt 0.223171 0.539233 -s 0 -f 47/1/1 1/2/1 3/3/1 45/4/1 -f 4/5/2 2/6/2 48/7/2 46/8/2 -f 45/4/3 3/3/3 5/9/3 43/10/3 -f 6/11/4 4/5/4 46/8/4 44/12/4 -f 3/3/5 9/13/5 7/14/5 5/9/5 -f 8/15/6 10/16/6 4/5/6 6/11/6 -f 1/2/7 11/17/7 9/13/7 3/3/7 -f 10/16/8 12/18/8 2/6/8 4/5/8 -f 11/17/9 13/19/9 15/20/9 9/13/9 -f 16/21/10 14/22/10 12/18/10 10/16/10 -f 9/13/11 15/20/11 17/23/11 7/14/11 -f 18/24/12 16/21/12 10/16/12 8/15/12 -f 15/20/13 21/25/13 19/26/13 17/23/13 -f 20/27/14 22/28/14 16/21/14 18/24/14 -f 13/19/15 23/29/15 21/25/15 15/20/15 -f 22/28/16 24/30/16 14/22/16 16/21/16 -f 23/29/17 25/31/17 27/32/17 21/25/17 -f 28/33/18 26/34/18 24/30/18 22/28/18 -f 21/25/19 27/32/19 29/35/19 19/26/19 -f 30/36/20 28/33/20 22/28/20 20/27/20 -f 27/32/21 33/37/21 31/38/21 29/35/21 -f 32/39/22 34/40/22 28/33/22 30/36/22 -f 25/31/23 35/41/23 33/37/23 27/32/23 -f 34/40/24 36/42/24 26/34/24 28/33/24 -f 35/41/25 37/43/25 39/44/25 33/37/25 -f 40/45/26 38/46/26 36/42/26 34/40/26 -f 33/37/27 39/44/27 41/47/27 31/38/27 -f 42/48/28 40/45/28 34/40/28 32/39/28 -f 39/44/29 45/4/29 43/10/29 41/47/29 -f 44/12/30 46/8/30 40/45/30 42/48/30 -f 37/43/31 47/1/31 45/4/31 39/44/31 -f 46/8/32 48/7/32 38/46/32 40/45/32 -f 47/1/33 37/43/33 51/49/33 49/50/33 -f 52/51/34 38/46/34 48/7/34 50/52/34 -f 37/43/35 35/41/35 53/53/35 51/49/35 -f 54/54/36 36/42/36 38/46/36 52/51/36 -f 35/41/37 25/31/37 55/55/37 53/53/37 -f 56/56/38 26/34/38 36/42/38 54/54/38 -f 25/31/39 23/29/39 57/57/39 55/55/39 -f 58/58/40 24/30/40 26/34/40 56/56/40 -f 23/29/41 13/19/41 59/59/41 57/57/41 -f 60/60/42 14/22/42 24/30/42 58/58/42 -f 13/19/43 11/17/43 63/61/43 59/59/43 -f 64/62/44 12/18/44 14/22/44 60/60/44 -f 11/17/45 1/2/45 65/63/45 63/61/45 -f 66/64/46 2/6/46 12/18/46 64/62/46 -f 1/2/47 47/1/47 49/50/47 65/63/47 -f 50/52/48 48/7/48 2/6/48 66/64/48 -f 61/65/49 65/63/49 49/50/49 -f 50/52/50 66/64/50 62/66/50 -f 63/61/51 65/63/51 61/65/51 -f 62/66/52 66/64/52 64/62/52 -f 61/65/53 59/59/53 63/61/53 -f 64/62/54 60/60/54 62/66/54 -f 61/65/55 57/57/55 59/59/55 -f 60/60/56 58/58/56 62/66/56 -f 61/65/57 55/55/57 57/57/57 -f 58/58/58 56/56/58 62/66/58 -f 61/65/59 53/53/59 55/55/59 -f 56/56/60 54/54/60 62/66/60 -f 61/65/61 51/49/61 53/53/61 -f 54/54/62 52/51/62 62/66/62 -f 61/65/63 49/50/63 51/49/63 -f 52/51/64 50/52/64 62/66/64 -f 89/67/65 174/68/65 176/69/65 91/70/65 -f 176/69/66 175/71/66 90/72/66 91/70/66 -f 87/73/67 172/74/67 174/68/67 89/67/67 -f 175/71/68 173/75/68 88/76/68 90/72/68 -f 85/77/69 170/78/69 172/74/69 87/73/69 -f 173/75/70 171/79/70 86/80/70 88/76/70 -f 83/81/71 168/82/71 170/78/71 85/77/71 -f 171/79/72 169/83/72 84/84/72 86/80/72 -f 81/85/73 166/86/73 168/82/73 83/81/73 -f 169/83/74 167/87/74 82/88/74 84/84/74 -f 79/89/75 92/90/75 146/91/75 164/92/75 -f 147/93/76 93/94/76 80/95/76 165/96/76 -f 92/90/77 94/97/77 148/98/77 146/91/77 -f 149/99/78 95/100/78 93/94/78 147/93/78 -f 94/97/79 96/101/79 150/102/79 148/98/79 -f 151/103/80 97/104/80 95/100/80 149/99/80 -f 96/101/81 98/105/81 152/106/81 150/102/81 -f 153/107/82 99/108/82 97/104/82 151/103/82 -f 98/105/83 100/109/83 154/110/83 152/106/83 -f 155/111/84 101/112/84 99/108/84 153/107/84 -f 100/109/85 102/113/85 156/114/85 154/110/85 -f 157/115/86 103/116/86 101/112/86 155/111/86 -f 102/113/87 104/117/87 158/118/87 156/114/87 -f 159/119/88 105/120/88 103/116/88 157/115/88 -f 104/117/89 106/121/89 160/122/89 158/118/89 -f 161/123/90 107/124/90 105/120/90 159/119/90 -f 106/121/91 108/125/91 162/126/91 160/122/91 -f 163/127/92 109/128/92 107/124/92 161/123/92 -f 108/125/93 67/129/93 68/130/93 162/126/93 -f 68/130/94 67/129/94 109/128/94 163/127/94 -f 110/131/95 128/132/95 160/122/95 162/126/95 -f 161/123/96 129/133/96 111/134/96 163/127/96 -f 128/132/97 179/135/97 158/118/97 160/122/97 -f 159/119/98 180/136/98 129/133/98 161/123/98 -f 126/137/99 156/114/99 158/118/99 179/135/99 -f 159/119/100 157/115/100 127/138/100 180/136/100 -f 124/139/101 154/110/101 156/114/101 126/137/101 -f 157/115/102 155/111/102 125/140/102 127/138/102 -f 122/141/103 152/106/103 154/110/103 124/139/103 -f 155/111/104 153/107/104 123/142/104 125/140/104 -f 120/143/105 150/102/105 152/106/105 122/141/105 -f 153/107/106 151/103/106 121/144/106 123/142/106 -f 118/145/107 148/98/107 150/102/107 120/143/107 -f 151/103/108 149/99/108 119/146/108 121/144/108 -f 116/147/109 146/91/109 148/98/109 118/145/109 -f 149/99/110 147/93/110 117/148/110 119/146/110 -f 114/149/111 164/92/111 146/91/111 116/147/111 -f 147/93/112 165/96/112 115/150/112 117/148/112 -f 114/149/113 181/151/113 177/152/113 164/92/113 -f 177/152/114 182/153/114 115/150/114 165/96/114 -f 110/131/115 162/126/115 68/130/115 112/154/115 -f 68/130/116 163/127/116 111/134/116 113/155/116 -f 112/154/117 68/130/117 178/156/117 183/157/117 -f 178/156/118 68/130/118 113/155/118 184/158/118 -f 177/152/119 181/151/119 183/157/119 178/156/119 -f 184/158/120 182/153/120 177/152/120 178/156/120 -f 135/159/121 137/160/121 176/69/121 174/68/121 -f 176/69/122 137/160/122 136/161/122 175/71/122 -f 133/162/123 135/159/123 174/68/123 172/74/123 -f 175/71/124 136/161/124 134/163/124 173/75/124 -f 131/164/125 133/162/125 172/74/125 170/78/125 -f 173/75/126 134/163/126 132/165/126 171/79/126 -f 166/86/127 187/166/127 185/167/127 168/82/127 -f 186/168/128 188/169/128 167/87/128 169/83/128 -f 131/164/129 170/78/129 168/82/129 185/167/129 -f 169/83/130 171/79/130 132/165/130 186/168/130 -f 144/170/131 190/171/131 189/172/131 187/166/131 -f 189/172/132 190/171/132 145/173/132 188/169/132 -f 185/167/133 187/166/133 189/172/133 69/174/133 -f 189/172/134 188/169/134 186/168/134 69/174/134 -f 130/175/135 131/164/135 185/167/135 69/174/135 -f 186/168/135 132/165/135 130/175/135 69/174/135 -f 142/176/136 193/177/136 191/178/136 144/170/136 -f 192/179/137 194/180/137 143/181/137 145/173/137 -f 140/182/138 195/183/138 193/177/138 142/176/138 -f 194/180/139 196/184/139 141/185/139 143/181/139 -f 139/186/140 197/187/140 195/183/140 140/182/140 -f 196/184/141 198/188/141 139/186/141 141/185/141 -f 138/189/142 71/190/142 197/187/142 139/186/142 -f 198/188/143 71/190/143 138/189/143 139/186/143 -f 190/171/144 144/170/144 191/178/144 70/191/144 -f 192/179/145 145/173/145 190/171/145 70/191/145 -f 70/191/146 191/178/146 206/192/146 208/193/146 -f 207/194/147 192/179/147 70/191/147 208/193/147 -f 71/190/148 199/195/148 200/196/148 197/187/148 -f 201/197/149 199/195/149 71/190/149 198/188/149 -f 197/187/150 200/196/150 202/198/150 195/183/150 -f 203/199/151 201/197/151 198/188/151 196/184/151 -f 195/183/152 202/198/152 204/200/152 193/177/152 -f 205/201/153 203/199/153 196/184/153 194/180/153 -f 193/177/154 204/200/154 206/192/154 191/178/154 -f 207/194/155 205/201/155 194/180/155 192/179/155 -f 199/195/156 204/200/156 202/198/156 200/196/156 -f 203/199/157 205/201/157 199/195/157 201/197/157 -f 199/195/158 208/193/158 206/192/158 204/200/158 -f 207/194/159 208/193/159 199/195/159 205/201/159 -f 139/186/160 140/182/160 164/92/160 177/152/160 -f 165/96/161 141/185/161 139/186/161 177/152/161 -f 140/182/162 142/176/162 211/202/162 164/92/162 -f 212/203/163 143/181/163 141/185/163 165/96/163 -f 142/176/164 144/170/164 213/204/164 211/202/164 -f 214/205/165 145/173/165 143/181/165 212/203/165 -f 144/170/166 187/166/166 166/86/166 213/204/166 -f 167/87/167 188/169/167 145/173/167 214/205/167 -f 81/85/168 209/206/168 213/204/168 166/86/168 -f 214/205/169 210/207/169 82/88/169 167/87/169 -f 209/206/170 215/208/170 211/202/170 213/204/170 -f 212/203/171 216/209/171 210/207/171 214/205/171 -f 79/89/172 164/92/172 211/202/172 215/208/172 -f 212/203/173 165/96/173 80/95/173 216/209/173 -f 131/164/174 130/175/174 72/210/174 222/211/174 -f 72/210/175 130/175/175 132/165/175 223/212/175 -f 133/162/176 131/164/176 222/211/176 220/213/176 -f 223/212/177 132/165/177 134/163/177 221/214/177 -f 135/159/178 133/162/178 220/213/178 218/215/178 -f 221/214/179 134/163/179 136/161/179 219/216/179 -f 137/160/180 135/159/180 218/215/180 217/217/180 -f 219/216/181 136/161/181 137/160/181 217/217/181 -f 217/217/182 218/215/182 229/218/182 231/219/182 -f 230/220/183 219/216/183 217/217/183 231/219/183 -f 218/215/184 220/213/184 227/221/184 229/218/184 -f 228/222/185 221/214/185 219/216/185 230/220/185 -f 220/213/186 222/211/186 225/223/186 227/221/186 -f 226/224/187 223/212/187 221/214/187 228/222/187 -f 222/211/188 72/210/188 224/225/188 225/223/188 -f 224/225/189 72/210/189 223/212/189 226/224/189 -f 224/225/190 231/219/190 229/218/190 225/223/190 -f 230/220/191 231/219/191 224/225/191 226/224/191 -f 225/223/192 229/218/192 227/221/192 -f 228/222/193 230/220/193 226/224/193 -f 183/157/194 181/151/194 234/226/194 232/227/194 -f 235/228/195 182/153/195 184/158/195 233/229/195 -f 112/154/196 183/157/196 232/227/196 254/230/196 -f 233/229/197 184/158/197 113/155/197 255/231/197 -f 110/131/198 112/154/198 254/230/198 256/232/198 -f 255/231/199 113/155/199 111/134/199 257/233/199 -f 181/151/200 114/149/200 252/234/200 234/226/200 -f 253/235/201 115/150/201 182/153/201 235/228/201 -f 114/149/202 116/147/202 250/236/202 252/234/202 -f 251/237/203 117/148/203 115/150/203 253/235/203 -f 116/147/204 118/145/204 248/238/204 250/236/204 -f 249/239/205 119/146/205 117/148/205 251/237/205 -f 118/145/206 120/143/206 246/240/206 248/238/206 -f 247/241/207 121/144/207 119/146/207 249/239/207 -f 120/143/208 122/141/208 244/242/208 246/240/208 -f 245/243/209 123/142/209 121/144/209 247/241/209 -f 122/141/210 124/139/210 242/244/210 244/242/210 -f 243/245/211 125/140/211 123/142/211 245/243/211 -f 124/139/212 126/137/212 240/246/212 242/244/212 -f 241/247/213 127/138/213 125/140/213 243/245/213 -f 126/137/214 179/135/214 236/248/214 240/246/214 -f 237/249/215 180/136/215 127/138/215 241/247/215 -f 179/135/216 128/132/216 238/250/216 236/248/216 -f 239/251/217 129/133/217 180/136/217 237/249/217 -f 128/132/218 110/131/218 256/232/218 238/250/218 -f 257/233/219 111/134/219 129/133/219 239/251/219 -f 238/250/220 256/232/220 258/252/220 276/253/220 -f 259/254/221 257/233/221 239/251/221 277/255/221 -f 236/248/222 238/250/222 276/253/222 278/256/222 -f 277/255/223 239/251/223 237/249/223 279/257/223 -f 240/246/224 236/248/224 278/256/224 274/258/224 -f 279/257/225 237/249/225 241/247/225 275/259/225 -f 242/244/226 240/246/226 274/258/226 272/260/226 -f 275/259/227 241/247/227 243/245/227 273/261/227 -f 244/242/228 242/244/228 272/260/228 270/262/228 -f 273/261/229 243/245/229 245/243/229 271/263/229 -f 246/240/230 244/242/230 270/262/230 268/264/230 -f 271/263/231 245/243/231 247/241/231 269/265/231 -f 248/238/232 246/240/232 268/264/232 266/266/232 -f 269/265/233 247/241/233 249/239/233 267/267/233 -f 250/236/234 248/238/234 266/266/234 264/268/234 -f 267/267/235 249/239/235 251/237/235 265/269/235 -f 252/234/236 250/236/236 264/268/236 262/270/236 -f 265/269/237 251/237/237 253/235/237 263/271/237 -f 234/226/238 252/234/238 262/270/238 280/272/238 -f 263/271/239 253/235/239 235/228/239 281/273/239 -f 256/232/240 254/230/240 260/274/240 258/252/240 -f 261/275/241 255/231/241 257/233/241 259/254/241 -f 254/230/242 232/227/242 282/276/242 260/274/242 -f 283/277/243 233/229/243 255/231/243 261/275/243 -f 232/227/244 234/226/244 280/272/244 282/276/244 -f 281/273/245 235/228/245 233/229/245 283/277/245 -f 67/129/246 108/125/246 284/278/246 73/279/246 -f 285/280/247 109/128/247 67/129/247 73/279/247 -f 108/125/248 106/121/248 286/281/248 284/278/248 -f 287/282/249 107/124/249 109/128/249 285/280/249 -f 106/121/250 104/117/250 288/283/250 286/281/250 -f 289/284/251 105/120/251 107/124/251 287/282/251 -f 104/117/252 102/113/252 290/285/252 288/283/252 -f 291/286/253 103/116/253 105/120/253 289/284/253 -f 102/113/254 100/109/254 292/287/254 290/285/254 -f 293/288/255 101/112/255 103/116/255 291/286/255 -f 100/109/256 98/105/256 294/289/256 292/287/256 -f 295/290/257 99/108/257 101/112/257 293/288/257 -f 98/105/258 96/101/258 296/291/258 294/289/258 -f 297/292/259 97/104/259 99/108/259 295/290/259 -f 96/101/260 94/97/260 298/293/260 296/291/260 -f 299/294/261 95/100/261 97/104/261 297/292/261 -f 94/97/262 92/90/262 300/295/262 298/293/262 -f 301/296/263 93/94/263 95/100/263 299/294/263 -f 308/297/264 309/298/264 328/299/264 338/300/264 -f 329/301/265 309/302/265 308/303/265 339/304/265 -f 307/305/266 308/297/266 338/300/266 336/306/266 -f 339/304/267 308/303/267 307/307/267 337/308/267 -f 306/309/268 307/305/268 336/306/268 340/310/268 -f 337/308/269 307/307/269 306/309/269 341/311/269 -f 89/67/270 91/70/270 306/309/270 340/310/270 -f 306/309/271 91/70/271 90/72/271 341/311/271 -f 87/73/272 89/67/272 340/310/272 334/312/272 -f 341/311/273 90/72/273 88/76/273 335/313/273 -f 85/77/274 87/73/274 334/312/274 330/314/274 -f 335/313/275 88/76/275 86/80/275 331/315/275 -f 83/81/276 85/77/276 330/314/276 332/316/276 -f 331/315/277 86/80/277 84/84/277 333/317/277 -f 330/314/278 336/306/278 338/300/278 332/316/278 -f 339/304/279 337/308/279 331/315/279 333/317/279 -f 330/314/280 334/312/280 340/310/280 336/306/280 -f 341/311/281 335/313/281 331/315/281 337/308/281 -f 326/318/282 332/316/282 338/300/282 328/299/282 -f 339/304/283 333/317/283 327/319/283 329/301/283 -f 81/85/284 83/81/284 332/316/284 326/318/284 -f 333/317/285 84/84/285 82/88/285 327/319/285 -f 209/206/286 342/320/286 344/321/286 215/208/286 -f 345/322/287 343/323/287 210/207/287 216/209/287 -f 81/85/288 326/318/288 342/320/288 209/206/288 -f 343/323/289 327/319/289 82/88/289 210/207/289 -f 79/89/290 215/208/290 344/321/290 346/324/290 -f 345/322/291 216/209/291 80/95/291 347/325/291 -f 79/89/292 346/324/292 300/295/292 92/90/292 -f 301/296/293 347/325/293 80/95/293 93/94/293 -f 77/326/294 324/327/294 352/328/294 304/329/294 -f 353/330/295 325/331/295 77/332/295 304/333/295 -f 304/329/296 352/328/296 350/334/296 78/335/296 -f 351/336/297 353/330/297 304/333/297 78/337/297 -f 78/335/298 350/334/298 348/338/298 305/339/298 -f 349/340/299 351/336/299 78/337/299 305/341/299 -f 305/339/300 348/338/300 328/299/300 309/298/300 -f 329/301/301 349/340/301 305/341/301 309/302/301 -f 326/318/302 328/299/302 348/338/302 342/320/302 -f 349/340/303 329/301/303 327/319/303 343/323/303 -f 296/291/304 298/293/304 318/342/304 310/343/304 -f 319/344/305 299/294/305 297/292/305 311/345/305 -f 76/346/306 316/347/306 324/327/306 77/326/306 -f 325/331/307 317/348/307 76/349/307 77/332/307 -f 302/350/308 358/351/308 356/352/308 303/353/308 -f 357/354/309 359/355/309 302/356/309 303/357/309 -f 303/353/310 356/352/310 354/358/310 75/359/310 -f 355/360/311 357/354/311 303/357/311 75/361/311 -f 75/359/312 354/358/312 316/347/312 76/346/312 -f 317/348/313 355/360/313 75/361/313 76/349/313 -f 292/362/314 294/289/314 362/363/314 364/364/314 -f 363/365/315 295/290/315 293/366/315 365/367/315 -f 364/364/316 362/363/316 368/368/316 366/369/316 -f 369/370/317 363/365/317 365/367/317 367/371/317 -f 366/369/318 368/368/318 370/372/318 372/373/318 -f 371/374/319 369/370/319 367/371/319 373/375/319 -f 372/373/320 370/372/320 376/376/320 374/377/320 -f 377/378/321 371/374/321 373/375/321 375/379/321 -f 314/380/322 378/381/322 374/377/322 376/376/322 -f 375/379/323 379/382/323 315/383/323 377/378/323 -f 316/347/324 354/358/324 374/377/324 378/381/324 -f 375/379/325 355/360/325 317/348/325 379/382/325 -f 354/358/326 356/352/326 372/373/326 374/377/326 -f 373/375/327 357/354/327 355/360/327 375/379/327 -f 356/352/328 358/351/328 366/369/328 372/373/328 -f 367/371/329 359/355/329 357/354/329 373/375/329 -f 358/351/330 360/384/330 364/364/330 366/369/330 -f 365/367/331 361/385/331 359/355/331 367/371/331 -f 290/386/332 292/362/332 364/364/332 360/384/332 -f 365/367/333 293/366/333 291/387/333 361/385/333 -f 74/388/334 360/384/334 358/351/334 302/350/334 -f 359/355/335 361/385/335 74/389/335 302/356/335 -f 284/390/336 286/391/336 288/392/336 290/386/336 -f 289/393/337 287/394/337 285/395/337 291/387/337 -f 284/390/338 290/386/338 360/384/338 74/388/338 -f 361/385/339 291/387/339 285/395/339 74/389/339 -f 73/396/340 284/390/340 74/388/340 -f 74/389/341 285/395/341 73/397/341 -f 294/289/342 296/291/342 310/343/342 362/363/342 -f 311/345/343 297/292/343 295/290/343 363/365/343 -f 310/343/344 312/398/344 368/368/344 362/363/344 -f 369/370/345 313/399/345 311/345/345 363/365/345 -f 312/398/346 382/400/346 370/372/346 368/368/346 -f 371/374/347 383/401/347 313/399/347 369/370/347 -f 314/380/348 376/376/348 370/372/348 382/400/348 -f 371/374/349 377/378/349 315/383/349 383/401/349 -f 348/338/350 350/334/350 386/402/350 384/403/350 -f 387/404/351 351/336/351 349/340/351 385/405/351 -f 318/342/352 384/403/352 386/402/352 320/406/352 -f 387/404/353 385/405/353 319/344/353 321/407/353 -f 298/293/354 300/295/354 384/403/354 318/342/354 -f 385/405/355 301/296/355 299/294/355 319/344/355 -f 300/295/356 344/321/356 342/320/356 384/403/356 -f 343/323/357 345/322/357 301/296/357 385/405/357 -f 342/320/358 348/338/358 384/403/358 -f 385/405/359 349/340/359 343/323/359 -f 300/295/360 346/324/360 344/321/360 -f 345/322/361 347/325/361 301/296/361 -f 314/380/362 322/408/362 380/409/362 378/381/362 -f 381/410/363 323/411/363 315/383/363 379/382/363 -f 316/347/364 378/381/364 380/409/364 324/327/364 -f 381/410/365 379/382/365 317/348/365 325/331/365 -f 320/406/366 386/402/366 380/409/366 322/408/366 -f 381/410/367 387/404/367 321/407/367 323/411/367 -f 350/334/368 352/328/368 380/409/368 386/402/368 -f 381/410/369 353/330/369 351/336/369 387/404/369 -f 324/327/370 380/409/370 352/328/370 -f 353/330/371 381/410/371 325/331/371 -f 400/412/372 388/413/372 414/414/372 402/415/372 -f 415/416/373 389/417/373 401/418/373 403/419/373 -f 400/412/374 402/415/374 404/420/374 398/421/374 -f 405/422/375 403/419/375 401/418/375 399/423/375 -f 398/421/376 404/420/376 406/424/376 396/425/376 -f 407/426/377 405/422/377 399/423/377 397/427/377 -f 396/425/378 406/424/378 408/428/378 394/429/378 -f 409/430/379 407/426/379 397/427/379 395/431/379 -f 394/429/380 408/428/380 410/432/380 392/433/380 -f 411/434/381 409/430/381 395/431/381 393/435/381 -f 392/433/382 410/432/382 412/436/382 390/437/382 -f 413/438/383 411/434/383 393/435/383 391/439/383 -f 410/432/384 420/440/384 418/441/384 412/436/384 -f 419/442/385 421/443/385 411/434/385 413/438/385 -f 408/428/386 422/444/386 420/440/386 410/432/386 -f 421/443/387 423/445/387 409/430/387 411/434/387 -f 406/424/388 424/446/388 422/444/388 408/428/388 -f 423/445/389 425/447/389 407/426/389 409/430/389 -f 404/420/390 426/448/390 424/446/390 406/424/390 -f 425/447/391 427/449/391 405/422/391 407/426/391 -f 402/415/392 428/450/392 426/448/392 404/420/392 -f 427/449/393 429/451/393 403/419/393 405/422/393 -f 402/415/394 414/414/394 416/452/394 428/450/394 -f 417/453/395 415/416/395 403/419/395 429/451/395 -f 318/342/396 320/406/396 444/454/396 442/455/396 -f 445/456/397 321/407/397 319/344/397 443/457/397 -f 320/458/398 390/437/398 412/436/398 444/459/398 -f 413/438/399 391/439/399 321/460/399 445/461/399 -f 310/343/400 318/342/400 442/455/400 312/398/400 -f 443/457/401 319/344/401 311/345/401 313/399/401 -f 382/462/402 430/463/402 414/414/402 388/413/402 -f 415/416/403 431/464/403 383/465/403 389/417/403 -f 412/436/404 418/441/404 440/466/404 444/459/404 -f 441/467/405 419/442/405 413/438/405 445/461/405 -f 438/468/406 446/469/406 444/459/406 440/466/406 -f 445/461/407 447/470/407 439/471/407 441/467/407 -f 434/472/408 446/469/408 438/468/408 436/473/408 -f 439/471/409 447/470/409 435/474/409 437/475/409 -f 432/476/410 448/477/410 446/469/410 434/472/410 -f 447/470/411 449/478/411 433/479/411 435/474/411 -f 430/463/412 448/477/412 432/476/412 450/480/412 -f 433/479/413 449/478/413 431/464/413 451/481/413 -f 414/414/414 430/463/414 450/480/414 416/452/414 -f 451/481/415 431/464/415 415/416/415 417/453/415 -f 312/398/416 448/482/416 430/483/416 382/400/416 -f 431/484/417 449/485/417 313/399/417 383/401/417 -f 312/398/418 442/455/418 446/486/418 448/482/418 -f 447/487/419 443/457/419 313/399/419 449/485/419 -f 442/455/420 444/454/420 446/486/420 -f 447/487/421 445/456/421 443/457/421 -f 416/452/422 450/480/422 452/488/422 476/489/422 -f 453/490/423 451/481/423 417/453/423 477/491/423 -f 450/480/424 432/476/424 462/492/424 452/488/424 -f 463/493/425 433/479/425 451/481/425 453/490/425 -f 432/476/426 434/472/426 460/494/426 462/492/426 -f 461/495/427 435/474/427 433/479/427 463/493/427 -f 434/472/428 436/473/428 458/496/428 460/494/428 -f 459/497/429 437/475/429 435/474/429 461/495/429 -f 436/473/430 438/468/430 456/498/430 458/496/430 -f 457/499/431 439/471/431 437/475/431 459/497/431 -f 438/468/432 440/466/432 454/500/432 456/498/432 -f 455/501/433 441/467/433 439/471/433 457/499/433 -f 440/466/434 418/441/434 474/502/434 454/500/434 -f 475/503/435 419/442/435 441/467/435 455/501/435 -f 428/450/436 416/452/436 476/489/436 464/504/436 -f 477/491/437 417/453/437 429/451/437 465/505/437 -f 426/448/438 428/450/438 464/504/438 466/506/438 -f 465/505/439 429/451/439 427/449/439 467/507/439 -f 424/446/440 426/448/440 466/506/440 468/508/440 -f 467/507/441 427/449/441 425/447/441 469/509/441 -f 422/444/442 424/446/442 468/508/442 470/510/442 -f 469/509/443 425/447/443 423/445/443 471/511/443 -f 420/440/444 422/444/444 470/510/444 472/512/444 -f 471/511/445 423/445/445 421/443/445 473/513/445 -f 418/441/446 420/440/446 472/512/446 474/502/446 -f 473/513/447 421/443/447 419/442/447 475/503/447 -f 458/496/448 456/498/448 480/514/448 478/515/448 -f 481/516/449 457/499/449 459/497/449 479/517/449 -f 478/515/450 480/514/450 482/518/450 484/519/450 -f 483/520/451 481/516/451 479/517/451 485/521/451 -f 484/519/452 482/518/452 488/522/452 486/523/452 -f 489/524/453 483/520/453 485/521/453 487/525/453 -f 486/523/454 488/522/454 490/526/454 492/527/454 -f 491/528/455 489/524/455 487/525/455 493/529/455 -f 464/504/456 476/489/456 486/523/456 492/527/456 -f 487/525/457 477/491/457 465/505/457 493/529/457 -f 452/488/458 484/519/458 486/523/458 476/489/458 -f 487/525/459 485/521/459 453/490/459 477/491/459 -f 452/488/460 462/492/460 478/515/460 484/519/460 -f 479/517/461 463/493/461 453/490/461 485/521/461 -f 458/496/462 478/515/462 462/492/462 460/494/462 -f 463/493/463 479/517/463 459/497/463 461/495/463 -f 454/500/464 474/502/464 480/514/464 456/498/464 -f 481/516/465 475/503/465 455/501/465 457/499/465 -f 472/512/466 482/518/466 480/514/466 474/502/466 -f 481/516/467 483/520/467 473/513/467 475/503/467 -f 470/510/468 488/522/468 482/518/468 472/512/468 -f 483/520/469 489/524/469 471/511/469 473/513/469 -f 468/508/470 490/526/470 488/522/470 470/510/470 -f 489/524/471 491/528/471 469/509/471 471/511/471 -f 466/506/472 492/527/472 490/526/472 468/508/472 -f 491/528/473 493/529/473 467/507/473 469/509/473 -f 464/504/474 492/527/474 466/506/474 -f 467/507/475 493/529/475 465/505/475 -f 392/433/476 390/437/476 504/530/476 502/531/476 -f 505/532/477 391/439/477 393/435/477 503/533/477 -f 394/429/478 392/433/478 502/531/478 500/534/478 -f 503/533/479 393/435/479 395/431/479 501/535/479 -f 396/425/480 394/429/480 500/534/480 498/536/480 -f 501/535/481 395/431/481 397/427/481 499/537/481 -f 398/538/482 396/425/482 498/536/482 496/539/482 -f 499/537/483 397/427/483 399/540/483 497/541/483 -f 400/542/484 398/538/484 496/539/484 494/543/484 -f 497/541/485 399/540/485 401/544/485 495/545/485 -f 388/546/486 400/542/486 494/543/486 506/547/486 -f 495/545/487 401/544/487 389/548/487 507/549/487 -f 494/543/488 502/531/488 504/530/488 506/547/488 -f 505/532/489 503/533/489 495/545/489 507/549/489 -f 494/543/490 496/539/490 500/534/490 502/531/490 -f 501/535/491 497/541/491 495/545/491 503/533/491 -f 496/539/492 498/536/492 500/534/492 -f 501/535/493 499/537/493 497/541/493 -f 314/550/494 382/551/494 388/546/494 506/547/494 -f 389/548/495 383/552/495 315/553/495 507/549/495 -f 314/550/496 506/547/496 504/530/496 322/554/496 -f 505/532/497 507/549/497 315/553/497 323/555/497 -f 320/458/498 322/554/498 504/530/498 390/437/498 -f 505/532/499 323/555/499 321/460/499 391/439/499 diff --git a/demo-data/sounds/26777__junggle__btn402.mp3 b/demo-data/sounds/26777__junggle__btn402.mp3 deleted file mode 100644 index f18599a7..00000000 Binary files a/demo-data/sounds/26777__junggle__btn402.mp3 and /dev/null differ diff --git a/demo-data/sounds/26777__junggle__btn402.txt b/demo-data/sounds/26777__junggle__btn402.txt deleted file mode 100644 index 8f0505fc..00000000 --- a/demo-data/sounds/26777__junggle__btn402.txt +++ /dev/null @@ -1,8 +0,0 @@ -Sound author -http://jazzy.junggle.net/ - -License -https://creativecommons.org/licenses/by/4.0/ - -Downloaded from -https://freesound.org/s/26777/ diff --git a/demo-data/words/adjectives.txt b/demo-data/words/adjectives.txt deleted file mode 100644 index 056f4583..00000000 --- a/demo-data/words/adjectives.txt +++ /dev/null @@ -1,14 +0,0 @@ -smiling -dumb -happy -red -green -blue -pink -white -black -purple -crying -running -jumping -eating \ No newline at end of file diff --git a/demo-data/words/nouns.txt b/demo-data/words/nouns.txt deleted file mode 100644 index a4c07a3b..00000000 --- a/demo-data/words/nouns.txt +++ /dev/null @@ -1,14 +0,0 @@ -man -woman -child -boy -girl -house -flower -chair -table -car -ground -garden -airplane -school \ No newline at end of file diff --git a/demo-data/words/prepositions.txt b/demo-data/words/prepositions.txt deleted file mode 100644 index 8b76fa2c..00000000 --- a/demo-data/words/prepositions.txt +++ /dev/null @@ -1,14 +0,0 @@ -in -under -at -on -near -amongst -in front of -behind -below -next to -on top of -opposite -with -close to \ No newline at end of file diff --git a/demo-data/words/words.txt b/demo-data/words/words.txt deleted file mode 100644 index 9150d10b..00000000 --- a/demo-data/words/words.txt +++ /dev/null @@ -1,14 +0,0 @@ -car -fish -flower -eagle -garbage -computer -beautiful -ugly -random -man -woman -child -missing -magic diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts deleted file mode 100644 index 0994b6c5..00000000 --- a/desktop/build.gradle.kts +++ /dev/null @@ -1,37 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") -} - -kotlin { - sourceSets { - val commonMain by getting { - dependencies { - api(openrndr.math) - api(openrndr.shape) - implementation(project(":orx-noise")) - } - } - val commonTest by getting { - dependencies { - implementation(project(":orx-shapes")) - implementation(openrndr.shape) - } - } - - val jvmDemo by getting { - dependencies { - implementation(project(":orx-triangulation")) - implementation(project(":orx-shapes")) - implementation(project(":orx-noise")) - implementation(openrndr.shape) - implementation(project(":math")) - implementation(project(":orx-camera")) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2") - implementation(project(":orx-marching-squares")) - implementation(project(":orx-text-writer")) - implementation(project(":orx-obj-loader")) - implementation(project(":orx-palette")) - } - } - } -} \ No newline at end of file diff --git a/desktop/src/jvmDemo/kotlin/DemoColorBrewer2.kt b/desktop/src/jvmDemo/kotlin/DemoColorBrewer2.kt deleted file mode 100644 index ac900866..00000000 --- a/desktop/src/jvmDemo/kotlin/DemoColorBrewer2.kt +++ /dev/null @@ -1,35 +0,0 @@ -import org.openrndr.application -import org.openrndr.shape.Rectangle - -/** - * Demonstrates how to use a ColorBrewer2 palette. - * Finds the first available palette with 5 colors, - * then draws concentric circles filled with those colors. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val palette = colorBrewer2Palettes( - numberOfColors = 6, - paletteType = ColorBrewer2Type.Any - ).first().colors - val cellSize = 50.0 - extend { - palette.forEachIndexed { i, color -> - drawer.fill = color - drawer.rectangle( - Rectangle( - x = 0.0, - y = cellSize * i, - width = cellSize, - height = cellSize - ) - ) - // drawer.circle(drawer.bounds.center, 300.0 - i * 40.0) - } - } - } -} diff --git a/desktop/src/jvmDemo/kotlin/DemoDelaunay03.kt b/desktop/src/jvmDemo/kotlin/DemoDelaunay03.kt deleted file mode 100644 index 2c51da8b..00000000 --- a/desktop/src/jvmDemo/kotlin/DemoDelaunay03.kt +++ /dev/null @@ -1,654 +0,0 @@ -import com.icegps.math.geometry.Angle -import com.icegps.math.geometry.Vector3D -import com.icegps.math.geometry.degrees -import org.openrndr.KEY_ARROW_DOWN -import org.openrndr.KEY_ARROW_UP -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.TextSettingMode -import org.openrndr.draw.loadFont -import org.openrndr.extra.camera.Camera2D -import org.openrndr.extra.marchingsquares.findContours -import org.openrndr.extra.noise.gradientPerturbFractal -import org.openrndr.extra.textwriter.writer -import org.openrndr.extra.triangulation.DelaunayTriangulation -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.shape.Segment2D -import org.openrndr.shape.Segment3D -import org.openrndr.shape.ShapeContour -import kotlin.math.cos -import kotlin.math.sin -import kotlin.random.Random - -/** - * @author tabidachinokaze - * @date 2025/11/22 - */ -fun main() = application { - configure { - width = 720 - height = 480 - title = "Delaunator" - } - program { - val points3D = (0 until height).step(36).map { y -> - (0 until width).step(36).map { x -> - gradientPerturbFractal( - 300, - frequency = 0.8, - position = Vector3(x.toDouble(), y.toDouble(), seconds) - ) - } - }.flatten().map { - it.copy(z = it.z * 100) - } - /*val points3D = HeightmapVolcanoGenerator.generateVolcanoClusterHeightmap( - width = width, - height = height, - volcanoCount = 3 - )*/ - // val points3D = coordinateGenerate(width, height) - val zs = points3D.map { it.z } - println("zs = ${zs}") - val associate: MutableMap = points3D.associate { - Vector2(it.x, it.y) to it.z - }.toMutableMap() - val delaunay = DelaunayTriangulation(associate.map { it.key }) - - //println(points3D.niceStr()) - extend(Camera2D()) - println("draw") - var targetHeight: Double = zs.average() - val step = zs.max() - zs.min() / 6 - var heightList = (0..5).map { index -> - zs.min() + step * index - } - var logEnabled = true - var useInterpolation = false - var sampleLinear = false - keyboard.keyDown.listen { - logEnabled = true - println(it) - when (it.key) { - KEY_ARROW_UP -> targetHeight++ - KEY_ARROW_DOWN -> targetHeight-- - 73 -> useInterpolation = !useInterpolation - 83 -> sampleLinear = !sampleLinear - } - } - extend { - val triangles = delaunay.triangles() - val segments = mutableListOf() - drawer.clear(ColorRGBa.BLACK) - val indexDiff = (frameCount / 1000) % triangles.size - for ((i, triangle) in triangles.withIndex()) { - val segment2DS = triangle.contour.segments.filter { - val startZ = associate[it.start]!! - val endZ = associate[it.end]!! - if (startZ < endZ) { - targetHeight in startZ..endZ - } else { - targetHeight in endZ..startZ - } - } - - if (segment2DS.size == 2) { - val vector2s = segment2DS.map { - val startZ = associate[it.start]!! - val endZ = associate[it.end]!! - val start = Vector3(it.start.x, it.start.y, startZ) - val end = Vector3(it.end.x, it.end.y, endZ) - if (startZ < endZ) { - start to end - } else { - end to start - } - }.map { (start, end) -> - val segment3D = Segment3D(start, end) - val vector3 = - segment3D.position(calculatePositionRatio(targetHeight, start.z, end.z)) - vector3 - }.map { - associate[it.xy] = it.z - it.xy - } - val element = Segment2D(vector2s[0], vector2s[1]) - segments.add(element) - } - drawer.fill = if (indexDiff == i) { - ColorRGBa.CYAN - } else { - ColorRGBa.PINK.shade(1.0 - i / (triangles.size * 1.2)) - } - drawer.stroke = ColorRGBa.PINK.shade(i / (triangles.size * 1.0) + 0.1) - drawer.contour(triangle.contour) - } - - val sorted = connectAllSegments(segments) - - drawer.stroke = ColorRGBa.WHITE - drawer.strokeWeight = 2.0 - if (logEnabled) { - segments.forEach { - println("${it.start} -> ${it.end}") - } - println("=====") - } - - sorted.forEach { - it.forEach { - if (logEnabled) println("${it.start} -> ${it.end}") - drawer.lineSegment(it.start, it.end) - drawer.fill = ColorRGBa.WHITE - } - if (logEnabled) println("=") - drawer.fill = ColorRGBa.YELLOW - if (false) drawer.contour(ShapeContour.fromSegments(it, closed = true)) - } - /*for (y in 0 until (area.height / cellSize).toInt()) { - for (x in 0 until (area.width / cellSize).toInt()) { - values[IntVector2(x, y)] = f(Vector2(x * cellSize + area.x, y * cellSize + area.y)) - } - }*/ - val contours = findContours( - f = { - val triangle = triangles.firstOrNull { triangle -> - isPointInTriangle(it, listOf(triangle.x1, triangle.x2, triangle.x3)) - } - triangle ?: return@findContours 0.0 - val interpolate = interpolateHeight( - point = it, - triangle = listOf( - triangle.x1, - triangle.x2, - triangle.x3, - ).map { - Vector3(it.x, it.y, associate[it]!!) - } - ) - interpolate.z - targetHeight - }, - area = drawer.bounds, - cellSize = 4.0, - useInterpolation = useInterpolation - ) - val associateWith: List> = heightList.map { height -> - findContours( - f = { - val triangle = triangles.firstOrNull { triangle -> - isPointInTriangle(it, listOf(triangle.x1, triangle.x2, triangle.x3)) - } - triangle ?: return@findContours 0.0 - val interpolate = interpolateHeight( - point = it, - triangle = listOf( - triangle.x1, - triangle.x2, - triangle.x3, - ).map { - Vector3(it.x, it.y, associate[it]!!) - } - ) - interpolate.z - height - }, - area = drawer.bounds, - cellSize = 4.0, - useInterpolation = useInterpolation - ) - } - if (logEnabled) println("useInterpolation = $useInterpolation") - drawer.stroke = null - if (true) contours.forEach { - drawer.fill = ColorRGBa.GREEN.opacify(0.1) - drawer.contour(if (sampleLinear) it.sampleLinear() else it) - } - if (false) associateWith.forEachIndexed { index, contours -> - contours.forEach { - drawer.fill = colorBrewer2[index].colors.first().opacify(0.1) - drawer.contour(it) - } - } - - drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 24.0) - writer { - drawer.drawStyle.textSetting = TextSettingMode.SUBPIXEL - text(targetHeight.toString()) - } - logEnabled = false - } - } -} - -/** - * 射线法判断点是否在单个三角形内 - */ -fun isPointInTriangle(point: Vector2, triangle: List): Boolean { - require(triangle.size == 3) { "三角形必须有3个顶点" } - - val (v1, v2, v3) = triangle - - // 计算重心坐标 - val denominator = (v2.y - v3.y) * (v1.x - v3.x) + (v3.x - v2.x) * (v1.y - v3.y) - if (denominator == 0.0) return false // 退化三角形 - - val alpha = ((v2.y - v3.y) * (point.x - v3.x) + (v3.x - v2.x) * (point.y - v3.y)) / denominator - val beta = ((v3.y - v1.y) * (point.x - v3.x) + (v1.x - v3.x) * (point.y - v3.y)) / denominator - val gamma = 1.0 - alpha - beta - - // 点在三角形内当且仅当所有重心坐标都在[0,1]范围内 - return alpha >= 0 && beta >= 0 && gamma >= 0 && - alpha <= 1 && beta <= 1 && gamma <= 1 -} - -fun isPointInTriangle3D(point: Vector2, triangle: List): Boolean { - require(triangle.size == 3) { "三角形必须有3个顶点" } - - val (v1, v2, v3) = triangle - - // 计算重心坐标 - val denominator = (v2.y - v3.y) * (v1.x - v3.x) + (v3.x - v2.x) * (v1.y - v3.y) - if (denominator == 0.0) return false // 退化三角形 - - val alpha = ((v2.y - v3.y) * (point.x - v3.x) + (v3.x - v2.x) * (point.y - v3.y)) / denominator - val beta = ((v3.y - v1.y) * (point.x - v3.x) + (v1.x - v3.x) * (point.y - v3.y)) / denominator - val gamma = 1.0 - alpha - beta - - // 点在三角形内当且仅当所有重心坐标都在[0,1]范围内 - return alpha >= 0 && beta >= 0 && gamma >= 0 && - alpha <= 1 && beta <= 1 && gamma <= 1 -} - -/** - * 使用重心坐标计算点在三角形上的高度 - * @param point 二维点 (x, y) - * @param triangle 三角形的三个顶点 - * @return 三维点 (x, y, z) - */ -fun interpolateHeight(point: Vector2, triangle: List): Vector3 { - require(triangle.size == 3) { "三角形必须有3个顶点" } - - val (v1, v2, v3) = triangle - - // 计算重心坐标 - val (alpha, beta, gamma) = calculateBarycentricCoordinates(point, v1, v2, v3) - - // 使用重心坐标插值z值 - val z = alpha * v1.z + beta * v2.z + gamma * v3.z - - return Vector3(point.x, point.y, z) -} - -/** - * 计算点在三角形中的重心坐标 - */ -fun calculateBarycentricCoordinates( - point: Vector2, - v1: Vector3, - v2: Vector3, - v3: Vector3 -): Triple { - val denom = (v2.y - v3.y) * (v1.x - v3.x) + (v3.x - v2.x) * (v1.y - v3.y) - - val alpha = ((v2.y - v3.y) * (point.x - v3.x) + (v3.x - v2.x) * (point.y - v3.y)) / denom - val beta = ((v3.y - v1.y) * (point.x - v3.x) + (v1.x - v3.x) * (point.y - v3.y)) / denom - val gamma = 1.0 - alpha - beta - - return Triple(alpha, beta, gamma) -} - -fun connectAllSegments(segments: List): List> { - val remaining = segments.toMutableList() - val allPaths = mutableListOf>() - - while (remaining.isNotEmpty()) { - val path = mutableListOf() - - // 开始新路径 - path.add(remaining.removeAt(0)) - - var changed: Boolean - do { - changed = false - - // 向前扩展 - val lastEnd = path.last().end - val forwardSegment = remaining.find { it.start == lastEnd || it.end == lastEnd } - if (forwardSegment != null) { - val connectedSegment = if (forwardSegment.start == lastEnd) { - forwardSegment // 正向 - } else { - Segment2D(forwardSegment.end, forwardSegment.start) // 反向 - } - path.add(connectedSegment) - remaining.remove(forwardSegment) - changed = true - } - - // 向后扩展 - val firstStart = path.first().start - val backwardSegment = remaining.find { it.end == firstStart || it.start == firstStart } - if (backwardSegment != null) { - val connectedSegment = if (backwardSegment.end == firstStart) { - backwardSegment // 正向 - } else { - Segment2D(backwardSegment.end, backwardSegment.start) // 反向 - } - path.add(0, connectedSegment) - remaining.remove(backwardSegment) - changed = true - } - - } while (changed && remaining.isNotEmpty()) - - allPaths.add(path) - } - - return allPaths -} - -fun connectSegmentsEfficient(segments: List): List { - if (segments.isEmpty()) return emptyList() - - val remaining = segments.toMutableList() - val connected = mutableListOf() - - // 构建端点查找表 - val startMap = mutableMapOf>() - val endMap = mutableMapOf>() - - segments.forEach { segment -> - startMap.getOrPut(segment.start) { mutableListOf() }.add(segment) - endMap.getOrPut(segment.end) { mutableListOf() }.add(segment) - } - - // 从第一个线段开始 - var currentSegment = remaining.removeAt(0) - connected.add(currentSegment) - - // 更新查找表 - startMap[currentSegment.start]?.remove(currentSegment) - endMap[currentSegment.end]?.remove(currentSegment) - - // 向前连接 - while (true) { - val nextFromStart = startMap[currentSegment.end]?.firstOrNull() - val nextFromEnd = endMap[currentSegment.end]?.firstOrNull() - - when { - nextFromStart != null -> { - // 正向连接 - connected.add(nextFromStart) - remaining.remove(nextFromStart) - startMap[nextFromStart.start]?.remove(nextFromStart) - endMap[nextFromStart.end]?.remove(nextFromStart) - currentSegment = nextFromStart - } - - nextFromEnd != null -> { - // 反向连接 - val reversed = Segment2D(nextFromEnd.end, nextFromEnd.start) - connected.add(reversed) - remaining.remove(nextFromEnd) - startMap[nextFromEnd.start]?.remove(nextFromEnd) - endMap[nextFromEnd.end]?.remove(nextFromEnd) - currentSegment = reversed - } - - else -> break - } - } - - // 向后连接 - currentSegment = connected.first() - while (true) { - val prevFromEnd = endMap[currentSegment.start]?.firstOrNull() - val prevFromStart = startMap[currentSegment.start]?.firstOrNull() - - when { - prevFromEnd != null -> { - // 正向连接到开头 - connected.add(0, prevFromEnd) - remaining.remove(prevFromEnd) - startMap[prevFromEnd.start]?.remove(prevFromEnd) - endMap[prevFromEnd.end]?.remove(prevFromEnd) - currentSegment = prevFromEnd - } - - prevFromStart != null -> { - // 反向连接到开头 - val reversed = Segment2D(prevFromStart.end, prevFromStart.start) - connected.add(0, reversed) - remaining.remove(prevFromStart) - startMap[prevFromStart.start]?.remove(prevFromStart) - endMap[prevFromStart.end]?.remove(prevFromStart) - currentSegment = reversed - } - - else -> break - } - } - - return connected -} - -fun connectSegments(segments: List): List { - if (segments.isEmpty()) return emptyList() - - val remaining = segments.toMutableList() - val connected = mutableListOf() - - // 从第一个线段开始,保持原方向 - connected.add(remaining.removeAt(0)) - - while (remaining.isNotEmpty()) { - val lastEnd = connected.last().end - var found = false - - // 查找可以连接的线段 - for (i in remaining.indices) { - val segment = remaining[i] - - // 检查四种可能的连接方式 - when { - // 正向连接:当前终点 == 线段起点 - segment.start == lastEnd -> { - connected.add(segment) - remaining.removeAt(i) - found = true - break - } - // 反向连接:当前终点 == 线段终点,需要反转线段 - segment.end == lastEnd -> { - connected.add(Segment2D(segment.end, segment.start)) // 反转 - remaining.removeAt(i) - found = true - break - } - // 正向连接另一端:当前起点 == 线段终点,需要插入到前面 - segment.end == connected.first().start -> { - connected.add(0, Segment2D(segment.end, segment.start)) // 反转后插入开头 - remaining.removeAt(i) - found = true - break - } - // 反向连接另一端:当前起点 == 线段起点,需要反转并插入到前面 - segment.start == connected.first().start -> { - connected.add(0, segment) // 直接插入开头(已经是正确方向) - remaining.removeAt(i) - found = true - break - } - } - } - - if (!found) break // 无法找到连接线段 - } - - return connected -} - -fun calculatePositionRatio(value: Double, rangeStart: Double, rangeEnd: Double): Double { - if (rangeStart == rangeEnd) return 0.0 // 避免除零 - - val ratio = (value - rangeStart) / (rangeEnd - rangeStart) - return ratio.coerceIn(0.0, 1.0) -} - -fun sortLinesEfficient(lines: List): List { - if (lines.isEmpty()) return emptyList() - - // 创建起点到线段的映射 - val startMap = lines.associateBy { it.start } - val sorted = mutableListOf() - - // 找到起点(没有其他线段的终点指向它的起点) - var currentLine = lines.firstOrNull { line -> - lines.none { it.end == line.start } - } ?: lines.first() - - sorted.add(currentLine) - - while (true) { - val nextLine = startMap[currentLine.end] - if (nextLine == null || nextLine == lines.first()) break - sorted.add(nextLine) - currentLine = nextLine - } - - return sorted -} - -fun sortLines(lines: List): List { - if (lines.isEmpty()) return emptyList() - - val remaining = lines.toMutableList() - val sorted = mutableListOf() - - // 从第一个线段开始 - sorted.add(remaining.removeAt(0)) - - while (remaining.isNotEmpty()) { - val lastEnd = sorted.last().end - var found = false - - // 查找下一个线段 - for (i in remaining.indices) { - if (remaining[i].start == lastEnd) { - sorted.add(remaining.removeAt(i)) - found = true - break - } - } - - if (!found) break // 无法找到下一个线段 - } - - return sorted -} - -fun findLineLoops(lines: List): List> { - val remaining = lines.toMutableList() - val loops = mutableListOf>() - - while (remaining.isNotEmpty()) { - val loop = findSingleLoop(remaining) - if (loop.isNotEmpty()) { - loops.add(loop) - // 移除已使用的线段 - loop.forEach { line -> - remaining.remove(line) - } - } else { - // 无法形成环的线段 - break - } - } - - return loops -} - -fun findSingleLoop(remaining: MutableList): List { - if (remaining.isEmpty()) return emptyList() - - val loop = mutableListOf() - loop.add(remaining.removeAt(0)) - - // 向前查找连接 - while (remaining.isNotEmpty()) { - val lastEnd = loop.last().end - val nextIndex = remaining.indexOfFirst { it.start == lastEnd } - - if (nextIndex == -1) { - // 尝试向后查找连接 - val firstStart = loop.first().start - val prevIndex = remaining.indexOfFirst { it.end == firstStart } - - if (prevIndex != -1) { - loop.add(0, remaining.removeAt(prevIndex)) - } else { - break // 无法继续连接 - } - } else { - loop.add(remaining.removeAt(nextIndex)) - } - - // 检查是否形成闭环 - if (loop.last().end == loop.first().start) { - return loop - } - } - - // 如果没有形成闭环,返回空列表(或者可以根据需求返回部分环) - remaining.addAll(loop) // 将线段放回剩余列表 - return emptyList() -} - -fun Vector3D.rotateAroundZ(angle: Angle): Vector3D { - val cosAngle = cos(angle.radians) - val sinAngle = sin(angle.radians) - - return Vector3D( - x = x * cosAngle - y * sinAngle, - y = x * sinAngle + y * cosAngle, - z = z - ) -} - -fun coordinateGenerate(width: Int, height: Int): List { - val minX = 0.0 - val maxX = width.toDouble() - val minY = 0.0 - val maxY = height.toDouble() - val minZ = -20.0 - val maxZ = 20.0 - val x: () -> Double = { Random.nextDouble(minX, maxX) } - val y: () -> Double = { Random.nextDouble(minY, maxY) } - val z: () -> Double = { Random.nextDouble(minZ, maxZ) } - val dPoints = (0..60).map { - Vector3D(x(), y(), z()) - } - return dPoints -} - -fun coordinateGenerate1(): List { - val center = Vector3D(0.0, 0.0, 0.0) - val direction = Vector3D(0.0, 1.0, -1.0) - return (0..360).step(36).map> { degrees: Int -> - val newDirection = direction.rotateAroundZ(angle = degrees.degrees) - (0..5).map { - center + newDirection * it * 100 - } - }.flatten() -} - - -fun Vector3D.niceStr(): String { - return "[$x, $y, $z]".format(this) -} - -fun List.niceStr(): String { - return joinToString(", ", "[", "]") { - it.niceStr() - } -} diff --git a/desktop/src/jvmDemo/kotlin/DemoDelaunay3D.kt b/desktop/src/jvmDemo/kotlin/DemoDelaunay3D.kt deleted file mode 100644 index f472da6f..00000000 --- a/desktop/src/jvmDemo/kotlin/DemoDelaunay3D.kt +++ /dev/null @@ -1,267 +0,0 @@ -import org.openrndr.KEY_ARROW_DOWN -import org.openrndr.KEY_ARROW_UP -import org.openrndr.WindowMultisample -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.TextSettingMode -import org.openrndr.draw.loadFont -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.marchingsquares.findContours -import org.openrndr.extra.objloader.loadOBJasVertexBuffer -import org.openrndr.extra.textwriter.writer -import org.openrndr.extra.triangulation.DelaunayTriangulation -import org.openrndr.extra.triangulation.DelaunayTriangulation3D -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.shape.Path3D -import org.openrndr.shape.Segment3D - -/** - * @author tabidachinokaze - * @date 2025/11/22 - */ -fun main() = application { - configure { - width = 720 - height = 720 - title = "Delaunator" - multisample = WindowMultisample.SampleCount(8) - - } - program { - /*val points3D = (0 until height).step(36).map { y -> - (0 until width).step(36).map { x -> - gradientPerturbFractal( - 300, - frequency = 0.8, - position = Vector3(x.toDouble(), y.toDouble(), seconds) - ) - } - }.flatten().map { - it.copy(x = it.x - width / 2, y = it.y - height / 2, z = it.z * 100) - }*/ - /*val points3D = HeightmapVolcanoGenerator.generateVolcanoClusterHeightmap( - width = width, - height = height, - volcanoCount = 3 - )*/ - val points3D = coordinateGenerate(width, height).map { - it.copy(x = it.x - width / 2, y = it.y - height / 2, z = it.z * 10) - } - val zs = points3D.map { it.z } - println("zs = ${zs}") - val associate: MutableMap = points3D.associate { - Vector2(it.x, it.y) to it.z - }.toMutableMap() - val delaunay = DelaunayTriangulation(associate.map { it.key }) - val delaunay3D = DelaunayTriangulation3D(points3D.map { Vector3(it.x, it.y, it.z) }) - - //println(points3D.niceStr()) - //extend(Camera2D()) - val cam = Orbital() - extend(cam) { - eye = Vector3(x = 100.0, y = 100.0, z = 0.0) - lookAt = Vector3(x = 1.6, y = -1.9, z = 1.2) - } - - println("draw") - var targetHeight: Double = zs.average() - var logEnabled = true - var useInterpolation = false - var sampleLinear = false - keyboard.keyDown.listen { - logEnabled = true - println(it) - when (it.key) { - KEY_ARROW_UP -> targetHeight++ - KEY_ARROW_DOWN -> targetHeight-- - 73 -> useInterpolation = !useInterpolation - 83 -> sampleLinear = !sampleLinear - } - } - val vb = loadOBJasVertexBuffer("orx-obj-loader/test-data/non-planar.obj") - - extend { - val triangles = delaunay3D.triangles() - val segments = mutableListOf() - drawer.clear(ColorRGBa.BLACK) - val indexDiff = (frameCount / 1000) % triangles.size - drawer.shadeStyle = shadeStyle { - fragmentTransform = """ - x_fill.rgb = normalize(v_viewNormal) * 0.5 + vec3(0.5); - """.trimIndent() - } - - drawer.vertexBuffer(vb, DrawPrimitive.TRIANGLES) - - // 绘制等高线段区域 - for ((i, triangle) in triangles.withIndex()) { - val segment2DS = triangle.segments.filter { - val startZ = it.start.z - val endZ = it.end.z - if (startZ < endZ) { - targetHeight in startZ..endZ - } else { - targetHeight in endZ..startZ - } - } - - if (segment2DS.size == 2) { - val vector2s = segment2DS.map { - val startZ = it.start.z - val endZ = it.end.z - val start = Vector3(it.start.x, it.start.y, startZ) - val end = Vector3(it.end.x, it.end.y, endZ) - if (startZ < endZ) { - start to end - } else { - end to start - } - }.map { (start, end) -> - val segment3D = Segment3D(start, end) - val vector3 = - segment3D.position(calculatePositionRatio(targetHeight, start.z, end.z)) - vector3 - }.onEach { - associate[it.xy] = it.z - } - val element = Segment3D(vector2s[0], vector2s[1]) - segments.add(element) - } - drawer.strokeWeight = 20.0 - drawer.stroke = ColorRGBa.PINK - //drawer.contour(triangle.contour) - drawer.path(triangle.path) - } - - val sorted = connectAllSegments(segments) - - drawer.stroke = ColorRGBa.WHITE - drawer.strokeWeight = 2.0 - if (logEnabled) { - segments.forEach { - println("${it.start} -> ${it.end}") - } - println("=====") - } - - sorted.forEach { - it.forEach { - if (logEnabled) println("${it.start} -> ${it.end}") - drawer.lineSegment(it.start, it.end) - drawer.fill = ColorRGBa.WHITE - } - if (logEnabled) println("=") - drawer.fill = ColorRGBa.YELLOW - // if (false) drawer.contour(ShapeContour.fromSegments(it, closed = true)) - } - // 结束绘制等高线 - /*for (y in 0 until (area.height / cellSize).toInt()) { - for (x in 0 until (area.width / cellSize).toInt()) { - values[IntVector2(x, y)] = f(Vector2(x * cellSize + area.x, y * cellSize + area.y)) - } - }*/ - val contours = findContours( - f = { - val triangle = triangles.firstOrNull { triangle -> - isPointInTriangle3D(it, listOf(triangle.x1, triangle.x2, triangle.x3)) - } - triangle ?: return@findContours 0.0 - val interpolate = interpolateHeight( - point = it, - triangle = listOf( - triangle.x1, - triangle.x2, - triangle.x3, - ) - ) - interpolate.z - targetHeight - }, - area = drawer.bounds.movedTo(Vector2(-width / 2.0, -height / 2.0)), - cellSize = 4.0, - useInterpolation = useInterpolation - ) - if (logEnabled) println("useInterpolation = $useInterpolation") - drawer.stroke = null - contours.map { - if (false) drawer.contour(it) - it.segments.map { - Segment3D( - it.start.vector3(), - it.end.vector3() - ) - } - }.forEach { - drawer.fill = ColorRGBa.GREEN.opacify(0.1) - drawer.path(Path3D.fromSegments(it, closed = true)) - } - - if (false) writer { - drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 24.0) - drawer.drawStyle.textSetting = TextSettingMode.SUBPIXEL - text(targetHeight.toString()) - } - logEnabled = false - } - } -} - -data class Triangle3D( - val x1: Vector3, - val x2: Vector3, - val x3: Vector3, -) { - fun toList(): List = listOf(x1, x2, x3) -} - -fun connectAllSegments(segments: List): List> { - val remaining = segments.toMutableList() - val allPaths = mutableListOf>() - - while (remaining.isNotEmpty()) { - val path = mutableListOf() - - // 开始新路径 - path.add(remaining.removeAt(0)) - - var changed: Boolean - do { - changed = false - - // 向前扩展 - val lastEnd = path.last().end - val forwardSegment = remaining.find { it.start == lastEnd || it.end == lastEnd } - if (forwardSegment != null) { - val connectedSegment = if (forwardSegment.start == lastEnd) { - forwardSegment // 正向 - } else { - Segment3D(forwardSegment.end, forwardSegment.start) // 反向 - } - path.add(connectedSegment) - remaining.remove(forwardSegment) - changed = true - } - - // 向后扩展 - val firstStart = path.first().start - val backwardSegment = remaining.find { it.end == firstStart || it.start == firstStart } - if (backwardSegment != null) { - val connectedSegment = if (backwardSegment.end == firstStart) { - backwardSegment // 正向 - } else { - Segment3D(backwardSegment.end, backwardSegment.start) // 反向 - } - path.add(0, connectedSegment) - remaining.remove(backwardSegment) - changed = true - } - - } while (changed && remaining.isNotEmpty()) - - allPaths.add(path) - } - - return allPaths -} diff --git a/desktop/src/jvmDemo/kotlin/FindContours.kt b/desktop/src/jvmDemo/kotlin/FindContours.kt deleted file mode 100644 index 34df6b20..00000000 --- a/desktop/src/jvmDemo/kotlin/FindContours.kt +++ /dev/null @@ -1,94 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.camera.Camera2D -import org.openrndr.extra.marchingsquares.findContours -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 - -/** - * A simple demonstration of using the `findContours` method provided by `orx-marching-squares`. - * - * `findContours` lets one generate contours by providing a mathematical function to be - * sampled within the provided area and with the given cell size. Contours are generated - * between the areas in which the function returns positive and negative values. - * - * In this example, the `f` function returns the distance of a point to the center of the window minus 200.0. - * Therefore, sampled locations which are less than 200 pixels away from the center return - * negative values and all others return positive values, effectively generating a circle of radius 200.0. - * - * Try increasing the cell size to see how the precision of the circle reduces. - * - * The circular contour created in this program has over 90 segments. The number of segments depends on the cell - * size, and the resulting radius. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - extend(Camera2D()) - var showLog = true - val target = Vector2(0.0, 0.0) - val points3D = (0..10).map { x -> - (0..10).map { y -> - Vector3(x.toDouble(), y.toDouble(), x * y * 1.0) - } - } - - extend { - drawer.clear(ColorRGBa.BLACK) - drawer.stroke = ColorRGBa.PINK - fun f3(v: Vector2): Double { - val distance = drawer.bounds.center.distanceTo(v) - return when (distance) { - in 0.0..<100.0 -> -3.0 - in 100.0..<200.0 -> 1.0 - in 200.0..300.0 -> -1.0 - else -> distance - } - } - - fun f(v: Vector2): Double { - val distanceTo = v.distanceTo(target) - return (distanceTo - 100.0).also { - if (showLog) println( - buildString { - appendLine("${v} distanceTo ${target} = ${distanceTo}") - appendLine("distanceTo - 100.0 = ${distanceTo - 100.0}") - } - ) - } - } - - val points = mutableListOf() - - fun f1(v: Vector2): Double { - val result = if (v.x == v.y * 2 || v.x * 2 == v.y) { - points.add(v) - -1.0 - } else 0.0 - return result.also { - if (showLog) { - println("$v -> $result") - } - } - } - - val contours = findContours(::f3, drawer.bounds, 4.0) - drawer.fill = null - drawer.contours(contours) - - if (showLog) { - println( - buildString { - for ((index, contour) in contours.withIndex()) { - appendLine("index = ${index}, $contour") - } - } - ) - } - showLog = false - } - } -} diff --git a/desktop/src/jvmDemo/kotlin/HeightmapVolcanoGenerator.kt b/desktop/src/jvmDemo/kotlin/HeightmapVolcanoGenerator.kt deleted file mode 100644 index eb6a923a..00000000 --- a/desktop/src/jvmDemo/kotlin/HeightmapVolcanoGenerator.kt +++ /dev/null @@ -1,373 +0,0 @@ -import com.icegps.math.geometry.Vector3D -import kotlin.math.PI -import kotlin.math.abs -import kotlin.math.cos -import kotlin.math.exp -import kotlin.math.floor -import kotlin.math.max -import kotlin.math.sin -import kotlin.math.sqrt - -/** - * @author tabidachinokaze - * @date 2025/11/22 - */ -object HeightmapVolcanoGenerator { - - // 基础火山高度图 - fun generateVolcanoHeightmap( - width: Int = 100, - height: Int = 100, - centerX: Double = 50.0, - centerY: Double = 50.0, - maxHeight: Double = 60.0, - craterRadius: Double = 8.0, - volcanoRadius: Double = 30.0 - ): List { - val points = mutableListOf() - - for (x in 0 until width) { - for (y in 0 until height) { - // 计算到火山中心的距离 - val dx = x - centerX - val dy = y - centerY - val distance = sqrt(dx * dx + dy * dy) - - // 计算基础火山高度 - var z = calculateVolcanoHeight(distance, craterRadius, volcanoRadius, maxHeight) - - // 添加噪声细节 - val noise = perlinNoise(x * 0.1, y * 0.1, 0.1) * 3.0 - z = max(0.0, z + noise) - - points.add(Vector3D(x.toDouble(), y.toDouble(), z)) - } - } - - return points - } - - // 复合火山群高度图 - fun generateVolcanoClusterHeightmap( - width: Int = 150, - height: Int = 150, - volcanoCount: Int = 3 - ): List { - val points = mutableListOf() - val volcanoes = generateRandomVolcanoPositions(volcanoCount, width, height) - - for (x in (0 until width).step(25)) { - for (y in (0 until height).step(25)) { - var totalZ = 0.0 - - // 叠加所有火山的影响 - for (volcano in volcanoes) { - val dx = x - volcano.x - val dy = y - volcano.y - val distance = sqrt(dx * dx + dy * dy) - - if (distance <= volcano.radius) { - val volcanoHeight = calculateVolcanoHeight( - distance, - volcano.craterRadius, - volcano.radius, - volcano.maxHeight - ) - totalZ += volcanoHeight - } - } - - // 基础地形 - val baseNoise = perlinNoise(x * 0.02, y * 0.02, 0.05) * 5.0 - val detailNoise = perlinNoise(x * 0.1, y * 0.1, 0.2) * 2.0 - - points.add(Vector3D(x.toDouble(), y.toDouble(), totalZ + baseNoise + detailNoise)) - } - } - - return points - } - - // 带熔岩流的火山高度图 - fun generateVolcanoWithLavaHeightmap( - width: Int = 100, - height: Int = 100 - ): List { - val points = mutableListOf() - val centerX = width / 2.0 - val centerY = height / 2.0 - - // 生成熔岩流路径 - val lavaFlows = generateLavaFlowPaths(centerX, centerY, 3) - - for (x in 0 until width) { - for (y in 0 until height) { - val dx = x - centerX - val dy = y - centerY - val distance = sqrt(dx * dx + dy * dy) - - // 基础火山高度 - var z = calculateVolcanoHeight(distance, 10.0, 35.0, 70.0) - - // 添加熔岩流 - z += calculateLavaFlowEffect(x.toDouble(), y.toDouble(), lavaFlows) - - // 侵蚀效果 - z += calculateErosionEffect(x.toDouble(), y.toDouble(), distance, z) - - points.add(Vector3D(x.toDouble(), y.toDouble(), max(0.0, z))) - } - } - - return points - } - - // 破火山口高度图 - fun generateCalderaHeightmap( - width: Int = 100, - height: Int = 100 - ): List { - val points = mutableListOf() - val centerX = width / 2.0 - val centerY = height / 2.0 - - for (x in 0 until width) { - for (y in 0 until height) { - val dx = x - centerX - val dy = y - centerY - val distance = sqrt(dx * dx + dy * dy) - - var z = calculateCalderaHeight(distance, 15.0, 45.0, 50.0) - - // 内部平坦区域细节 - if (distance < 20) { - z += perlinNoise(x * 0.2, y * 0.2, 0.3) * 1.5 - } - - points.add(Vector3D(x.toDouble(), y.toDouble(), max(0.0, z))) - } - } - - return points - } - - // 线性火山链高度图 - fun generateVolcanoChainHeightmap( - width: Int = 200, - height: Int = 100 - ): List { - val points = mutableListOf() - - // 在一条线上生成多个火山 - val chainCenters = listOf( - Vector3D(30.0, 50.0, 0.0), - Vector3D(70.0, 50.0, 0.0), - Vector3D(110.0, 50.0, 0.0), - Vector3D(150.0, 50.0, 0.0), - Vector3D(170.0, 50.0, 0.0) - ) - - for (x in 0 until width) { - for (y in 0 until height) { - var totalZ = 0.0 - - for (center in chainCenters) { - val dx = x - center.x - val dy = y - center.y - val distance = sqrt(dx * dx + dy * dy) - - if (distance <= 25.0) { - val volcanoZ = calculateVolcanoHeight(distance, 6.0, 25.0, 40.0) - totalZ += volcanoZ - } - } - - // 添加基底地形,模拟山脉链 - val baseRidge = calculateMountainRidge(x.toDouble(), y.toDouble(), width, height) - totalZ += baseRidge - - points.add(Vector3D(x.toDouble(), y.toDouble(), totalZ)) - } - } - - return points - } - - // 辅助函数 - private data class VolcanoInfo( - val x: Double, - val y: Double, - val radius: Double, - val craterRadius: Double, - val maxHeight: Double - ) - - private data class LavaFlowInfo( - val startX: Double, - val startY: Double, - val angle: Double, // 弧度 - val length: Double, - val width: Double, - val intensity: Double - ) - - private fun calculateVolcanoHeight( - distance: Double, - craterRadius: Double, - volcanoRadius: Double, - maxHeight: Double - ): Double { - return when { - distance <= craterRadius -> { - // 火山口 - 中心凹陷 - val craterDepth = maxHeight * 0.4 - craterDepth * (1.0 - distance / craterRadius) - } - - distance <= volcanoRadius -> { - // 火山锥 - val slopeDistance = distance - craterRadius - val maxSlopeDistance = volcanoRadius - craterRadius - val normalized = slopeDistance / maxSlopeDistance - maxHeight * (1.0 - normalized * normalized) - } - - else -> 0.0 - } - } - - private fun calculateCalderaHeight( - distance: Double, - innerRadius: Double, - outerRadius: Double, - rimHeight: Double - ): Double { - return when { - distance <= innerRadius -> { - // 平坦的破火山口底部 - rimHeight * 0.2 - } - - distance <= outerRadius -> { - // 陡峭的边缘 - val rimDistance = distance - innerRadius - val rimWidth = outerRadius - innerRadius - val normalized = rimDistance / rimWidth - rimHeight * (1.0 - (1.0 - normalized) * (1.0 - normalized)) - } - - else -> { - // 外部平缓斜坡 - val externalDistance = distance - outerRadius - rimHeight * exp(-externalDistance * 0.08) - } - } - } - - private fun calculateLavaFlowEffect(x: Double, y: Double, lavaFlows: List): Double { - var effect = 0.0 - - for (flow in lavaFlows) { - val dx = x - flow.startX - val dy = y - flow.startY - - // 计算到熔岩流中心线的距离 - val flowDirX = cos(flow.angle) - val flowDirY = sin(flow.angle) - - val projection = dx * flowDirX + dy * flowDirY - - if (projection in 0.0..flow.length) { - val perpendicularX = dx - projection * flowDirX - val perpendicularY = dy - projection * flowDirY - val perpendicularDist = sqrt(perpendicularX * perpendicularX + perpendicularY * perpendicularY) - - if (perpendicularDist <= flow.width) { - val widthFactor = 1.0 - (perpendicularDist / flow.width) - val lengthFactor = 1.0 - (projection / flow.length) - effect += flow.intensity * widthFactor * lengthFactor - } - } - } - - return effect - } - - private fun calculateErosionEffect(x: Double, y: Double, distance: Double, height: Double): Double { - // 基于坡度的侵蚀 - val slopeNoise = perlinNoise(x * 0.15, y * 0.15, 0.1) * 2.0 - // 基于距离的侵蚀 - val distanceErosion = if (distance > 25) perlinNoise(x * 0.08, y * 0.08, 0.05) * 1.5 else 0.0 - return slopeNoise + distanceErosion - } - - private fun calculateMountainRidge(x: Double, y: Double, width: Int, height: Int): Double { - // 创建山脉基底 - val ridgeCenter = height / 2.0 - val distanceToRidge = abs(y - ridgeCenter) - val ridgeWidth = height * 0.3 - - if (distanceToRidge <= ridgeWidth) { - val ridgeFactor = 1.0 - (distanceToRidge / ridgeWidth) - return ridgeFactor * 15.0 * perlinNoise(x * 0.01, y * 0.01, 0.02) - } - return 0.0 - } - - private fun generateRandomVolcanoPositions(count: Int, width: Int, height: Int): List { - return List(count) { - VolcanoInfo( - x = (width * 0.2 + random() * width * 0.6), - y = (height * 0.2 + random() * height * 0.6), - radius = 20.0 + random() * 20.0, - craterRadius = 5.0 + random() * 7.0, - maxHeight = 25.0 + random() * 35.0 - ) - } - } - - private fun generateLavaFlowPaths(centerX: Double, centerY: Double, count: Int): List { - return List(count) { - LavaFlowInfo( - startX = centerX, - startY = centerY, - angle = random() * 2 * PI, - length = 20.0 + random() * 15.0, - width = 2.0 + random() * 3.0, - intensity = 5.0 + random() * 8.0 - ) - } - } - - private fun perlinNoise(x: Double, y: Double, frequency: Double): Double { - // 简化的柏林噪声实现 - val x0 = floor(x * frequency) - val y0 = floor(y * frequency) - val x1 = x0 + 1 - val y1 = y0 + 1 - - fun grad(ix: Int, iy: Int): Double { - val random = sin(ix * 12.9898 + iy * 78.233) * 43758.5453 - return (random % 1.0) * 2 - 1 - } - - fun interpolate(a: Double, b: Double, w: Double): Double { - return a + (b - a) * (w * w * (3 - 2 * w)) - } - - val g00 = grad(x0.toInt(), y0.toInt()) - val g10 = grad(x1.toInt(), y0.toInt()) - val g01 = grad(x0.toInt(), y1.toInt()) - val g11 = grad(x1.toInt(), y1.toInt()) - - val tx = x * frequency - x0 - val ty = y * frequency - y0 - - val n0 = interpolate(g00, g10, tx) - val n1 = interpolate(g01, g11, tx) - - return interpolate(n0, n1, ty) - } - - private fun random(): Double = Math.random() -} \ No newline at end of file diff --git a/dokka/images/logo-icon.svg b/dokka/images/logo-icon.svg deleted file mode 100644 index 57694fb7..00000000 --- a/dokka/images/logo-icon.svg +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/dokka/styles/extra.css b/dokka/styles/extra.css deleted file mode 100644 index ea6338ee..00000000 --- a/dokka/styles/extra.css +++ /dev/null @@ -1,4 +0,0 @@ - -.library-name--link { text-transform: uppercase; } -.library-version { top: 0px; margin-left: 10px; } - diff --git a/gradle.properties b/gradle.properties index c066fcca..20e2a015 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,32 +1,22 @@ -# suppress inspection "UnusedProperty" for whole file - -kotlin.code.style=official - -# For optimal compilation performance -org.gradle.jvmargs=-Xmx2G -XX:+UseParallelGC -kotlin.incremental.multiplatform=true -# https://kotlinlang.org/docs/gradle.html#check-for-jvm-target-compatibility-of-related-compile-tasks -kotlin.jvm.target.validation.mode=error -org.gradle.parallel=true -org.gradle.caching=true - -#org.gradle.configuration-cache=true -#org.gradle.configuration-cache.parallel=true -#org.gradle.configuration-cache.problems=warn - -# Whether to automatically bundle the Kotlin standard library (true by default) -# https://kotlinlang.org/docs/gradle.html#dependency-on-the-standard-library -kotlin.stdlib.default.dependency=true -kotlin.mpp.import.legacyTestSourceSetDetection=true - -# Enable Dokka 2.0.0 -org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled -org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true - +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true # AndroidX package structure to make it clearer which packages are bundled with the # Android operating system, and which are packaged with your app's APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 68eff4f4..55395292 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,30 +1,6 @@ [versions] -kotlinApi = "2.2" -kotlinLanguage = "2.2" kotlin = "2.2.21" -jvmTarget = "17" -openrndr = { require = "[0.5,0.6.0)" } -kotest = "5.9.1" -dokka = "2.1.0" -nebulaRelease = "18.0.7" -boofcv = "1.2.4" -libfreenect = "0.5.7-1.5.9" -librealsense = "2.53.1-1.5.9" -gson = "2.13.2" -antlr = "4.13.2" -antlrKotlin = "1.0.8" -minim = "2.2.2" -netty = "4.2.7.Final" -rabbitcontrol = "0.3.39" -zxing = "3.5.4" ktor = "3.3.2" -jgit = "7.3.0.202506031305-r" -javaosc = "0.9" -jsoup = "1.21.2" -mockk = "1.14.2" -processing = "4.4.10" -nmcp = "1.2.0" -okhttp = "5.2.1" agp = "8.13.1" junit = "4.13.2" coreKtx = "1.17.0" @@ -39,36 +15,6 @@ kotlinx-serialization = "1.9.0" mapbox = "11.16.6" [libraries] -okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" } -kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } -kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin" } -kotlin-scriptingJvm = { group = "org.jetbrains.kotlin", name = "kotlin-scripting-jvm", version.ref = "kotlin" } -kotlin-scriptingJvmHost = { group = "org.jetbrains.kotlin", name = "kotlin-scripting-jvm-host", version.ref = "kotlin" } -kotlin-scriptingJSR223 = { group = "org.jetbrains.kotlin", name = "kotlin-scripting-jsr223", version.ref = "kotlin" } -kotlin-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } -kotlin-serialization-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" } -dokka-gradle-plugin = { group = "org.jetbrains.dokka", name = "dokka-gradle-plugin", version.ref = "dokka" } - -mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk"} - -processing-core = { group = "org.processing", name = "core", version.ref = "processing"} - -boofcv = { group = "org.boofcv", name = "boofcv-core", version.ref = "boofcv" } -libfreenect = { group = "org.bytedeco", name = "libfreenect", version.ref = "libfreenect" } -librealsense = { group = "org.bytedeco", name = "librealsense2", version.ref = "librealsense" } -minim = { group = "net.compartmental.code", name = "minim", version.ref = "minim" } -netty-all = { group = "io.netty", name = "netty-all", version.ref = "netty" } -rabbitcontrol-rcp = { group = "cc.rabbitcontrol", name = "rcp", version.ref = "rabbitcontrol" } -zxing-core = { group = "com.google.zxing", name = "core", version.ref = "zxing" } -ktor-server-core = { group = "io.ktor", name = "ktor-server-core", version.ref = "ktor" } -ktor-server-netty = { group = "io.ktor", name = "ktor-server-netty", version.ref = "ktor" } -jgit = { group = "org.eclipse.jgit", name = "org.eclipse.jgit", version.ref = "jgit" } -javaosc-core = { group = "com.illposed.osc", name = "javaosc-core", version.ref = "javaosc" } -gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } -antlr-core = { group = "org.antlr", name = "antlr4", version.ref = "antlr" } -antlr-runtime = { group = "org.antlr", name = "antlr4-runtime", version.ref = "antlr" } -antlr-kotlin-runtime = { group = "com.strumenta", name = "antlr-kotlin-runtime", version.ref = "antlrKotlin" } -jsoup = { group = "org.jsoup", name = "jsoup", version.ref = "jsoup" } junit = { group = "junit", name = "junit", version.ref = "junit" } core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } @@ -90,11 +36,7 @@ mapbox-maps = { module = "com.mapbox.maps:android-ndk27", version.ref = "mapbox" [plugins] kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } -nebula-release = { id = "nebula.release", version.ref = "nebulaRelease" } -kotest-multiplatform = { id = "io.kotest.multiplatform", version.ref = "kotest" } -antlr-kotlin = { id = "com.strumenta.antlr-kotlin", version.ref = "antlrKotlin" } -nmcp = { id = "com.gradleup.nmcp.aggregation", version.ref = "nmcp" } android-application = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-library = { id = "com.android.library", version.ref = "agp" } \ No newline at end of file +android-library = { id = "com.android.library", version.ref = "agp" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55b..f8e1ee31 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradlew b/gradlew index 23d15a93..adff685a 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index 5eed7ee8..e509b2dd 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock deleted file mode 100644 index 3f37bfe8..00000000 --- a/kotlin-js-store/yarn.lock +++ /dev/null @@ -1,2089 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== - -"@discoveryjs/json-ext@^0.6.1": - version "0.6.3" - resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz#f13c7c205915eb91ae54c557f5e92bddd8be0e83" - integrity sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ== - -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - dependencies: - string-width "^5.1.2" - string-width-cjs "npm:string-width@^4.2.0" - strip-ansi "^7.0.1" - strip-ansi-cjs "npm:strip-ansi@^6.0.1" - wrap-ansi "^8.1.0" - wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - -"@jridgewell/gen-mapping@^0.3.0": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/resolve-uri@^3.0.3": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - -"@jridgewell/resolve-uri@^3.1.0": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/source-map@^0.3.3": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" - integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== - dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/sourcemap-codec@^1.4.14": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== - -"@jridgewell/trace-mapping@^0.3.25": - version "0.3.30" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz#4a76c4daeee5df09f5d3940e087442fb36ce2b99" - integrity sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.15" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" - integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== - -"@socket.io/component-emitter@~3.1.0": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" - integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== - -"@types/cookie@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" - integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== - -"@types/cors@^2.8.12": - version "2.8.12" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" - integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== - -"@types/eslint-scope@^3.7.7": - version "3.7.7" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" - integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - -"@types/eslint@*": - version "9.6.1" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" - integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*", "@types/estree@^1.0.8": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" - integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== - -"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.9": - version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" - integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== - -"@types/node@*", "@types/node@>=10.0.0": - version "18.7.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.13.tgz#23e6c5168333480d454243378b69e861ab5c011a" - integrity sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw== - -"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" - integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== - dependencies: - "@webassemblyjs/helper-numbers" "1.13.2" - "@webassemblyjs/helper-wasm-bytecode" "1.13.2" - -"@webassemblyjs/floating-point-hex-parser@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" - integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== - -"@webassemblyjs/helper-api-error@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" - integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== - -"@webassemblyjs/helper-buffer@1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" - integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== - -"@webassemblyjs/helper-numbers@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" - integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== - dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.13.2" - "@webassemblyjs/helper-api-error" "1.13.2" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/helper-wasm-bytecode@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" - integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== - -"@webassemblyjs/helper-wasm-section@1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" - integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== - dependencies: - "@webassemblyjs/ast" "1.14.1" - "@webassemblyjs/helper-buffer" "1.14.1" - "@webassemblyjs/helper-wasm-bytecode" "1.13.2" - "@webassemblyjs/wasm-gen" "1.14.1" - -"@webassemblyjs/ieee754@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" - integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" - integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.13.2": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" - integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== - -"@webassemblyjs/wasm-edit@^1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" - integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== - dependencies: - "@webassemblyjs/ast" "1.14.1" - "@webassemblyjs/helper-buffer" "1.14.1" - "@webassemblyjs/helper-wasm-bytecode" "1.13.2" - "@webassemblyjs/helper-wasm-section" "1.14.1" - "@webassemblyjs/wasm-gen" "1.14.1" - "@webassemblyjs/wasm-opt" "1.14.1" - "@webassemblyjs/wasm-parser" "1.14.1" - "@webassemblyjs/wast-printer" "1.14.1" - -"@webassemblyjs/wasm-gen@1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" - integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== - dependencies: - "@webassemblyjs/ast" "1.14.1" - "@webassemblyjs/helper-wasm-bytecode" "1.13.2" - "@webassemblyjs/ieee754" "1.13.2" - "@webassemblyjs/leb128" "1.13.2" - "@webassemblyjs/utf8" "1.13.2" - -"@webassemblyjs/wasm-opt@1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" - integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== - dependencies: - "@webassemblyjs/ast" "1.14.1" - "@webassemblyjs/helper-buffer" "1.14.1" - "@webassemblyjs/wasm-gen" "1.14.1" - "@webassemblyjs/wasm-parser" "1.14.1" - -"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" - integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== - dependencies: - "@webassemblyjs/ast" "1.14.1" - "@webassemblyjs/helper-api-error" "1.13.2" - "@webassemblyjs/helper-wasm-bytecode" "1.13.2" - "@webassemblyjs/ieee754" "1.13.2" - "@webassemblyjs/leb128" "1.13.2" - "@webassemblyjs/utf8" "1.13.2" - -"@webassemblyjs/wast-printer@1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" - integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== - dependencies: - "@webassemblyjs/ast" "1.14.1" - "@xtuc/long" "4.2.2" - -"@webpack-cli/configtest@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-3.0.1.tgz#76ac285b9658fa642ce238c276264589aa2b6b57" - integrity sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA== - -"@webpack-cli/info@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-3.0.1.tgz#3cff37fabb7d4ecaab6a8a4757d3826cf5888c63" - integrity sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ== - -"@webpack-cli/serve@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-3.0.1.tgz#bd8b1f824d57e30faa19eb78e4c0951056f72f00" - integrity sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg== - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - -accepts@~1.3.4: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -acorn-import-phases@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7" - integrity sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ== - -acorn@^8.14.0, acorn@^8.15.0: - version "8.15.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" - integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== - -ajv-formats@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" - integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== - dependencies: - ajv "^8.0.0" - -ajv-keywords@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" - integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== - dependencies: - fast-deep-equal "^3.1.3" - -ajv@^8.0.0, ajv@^8.9.0: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" - integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== - dependencies: - fast-deep-equal "^3.1.3" - fast-uri "^3.0.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-regex@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" - integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - -anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64id@2.0.0, base64id@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" - integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -body-parser@^1.19.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" - integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.10.3" - raw-body "2.5.1" - type-is "~1.6.18" - unpipe "1.0.0" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -browser-stdout@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -browserslist@^4.24.0: - version "4.25.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.2.tgz#90c1507143742d743544ae6e92bca3348adff667" - integrity sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA== - dependencies: - caniuse-lite "^1.0.30001733" - electron-to-chromium "^1.5.199" - node-releases "^2.0.19" - update-browserslist-db "^1.1.3" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - -call-bind@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -camelcase@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -caniuse-lite@^1.0.30001733: - version "1.0.30001735" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz#ba658fd3fd24a4106fd68d5ce472a2c251494dbe" - integrity sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w== - -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chokidar@^3.5.1: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -chokidar@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" - integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== - dependencies: - readdirp "^4.0.1" - -chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -colorette@^2.0.14: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== - -commander@^12.1.0: - version "12.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" - integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== - -commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -connect@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" - integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== - dependencies: - debug "2.6.9" - finalhandler "1.1.2" - parseurl "~1.3.3" - utils-merge "1.0.1" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -cookie@~0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== - -cors@~2.8.5: - version "2.8.5" - resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" - integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== - dependencies: - object-assign "^4" - vary "^1" - -cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -cross-spawn@^7.0.6: - version "7.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" - integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -custom-event@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" - integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== - -date-format@^4.0.13: - version "4.0.13" - resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.13.tgz#87c3aab3a4f6f37582c5f5f63692d2956fa67890" - integrity sha512-bnYCwf8Emc3pTD8pXnre+wfnjGtfi5ncMDKy7+cWZXbmRAsdWkOQHrfC1yz/KiwP5thDp2kCHWYWKBX4HP1hoQ== - -debug@2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -debug@^4.3.5: - version "4.3.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" - integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== - dependencies: - ms "2.1.2" - -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - -depd@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -di@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" - integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== - -diff@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-7.0.0.tgz#3fb34d387cd76d803f6eebea67b921dab0182a9a" - integrity sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw== - -dom-serialize@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" - integrity sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ== - dependencies: - custom-event "~1.0.0" - ent "~2.2.0" - extend "^3.0.0" - void-elements "^2.0.0" - -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - -electron-to-chromium@^1.5.199: - version "1.5.203" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.203.tgz#ef7fc2f7e1b816fa4535c861d1ec1348204142b6" - integrity sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -engine.io-parser@~5.2.1: - version "5.2.2" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.2.tgz#37b48e2d23116919a3453738c5720455e64e1c49" - integrity sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw== - -engine.io@~6.5.2: - version "6.5.4" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.5.4.tgz#6822debf324e781add2254e912f8568508850cdc" - integrity sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg== - dependencies: - "@types/cookie" "^0.4.1" - "@types/cors" "^2.8.12" - "@types/node" ">=10.0.0" - accepts "~1.3.4" - base64id "2.0.0" - cookie "~0.4.1" - cors "~2.8.5" - debug "~4.3.1" - engine.io-parser "~5.2.1" - ws "~8.11.0" - -enhanced-resolve@^5.17.2: - version "5.18.3" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz#9b5f4c5c076b8787c78fe540392ce76a88855b44" - integrity sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - -ent@~2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" - integrity sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA== - -envinfo@^7.14.0: - version "7.14.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae" - integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg== - -es-module-lexer@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.0.tgz#6be9c9e0b4543a60cd166ff6f8b4e9dae0b0c16f" - integrity sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA== - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escalade@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" - integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-scope@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -events@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -extend@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-uri@^3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" - integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== - -fastest-levenshtein@^1.0.12: - version "1.0.16" - resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" - integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -flatted@^3.2.6: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== - -follow-redirects@^1.0.0: - version "1.15.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" - integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== - -foreground-child@^3.1.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" - integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== - dependencies: - cross-spawn "^7.0.6" - signal-exit "^4.0.1" - -format-util@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" - integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.0.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" - integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-to-regexp@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" - integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== - -glob@^10.4.5: - version "10.4.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" - integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== - dependencies: - foreground-child "^3.1.0" - jackspeak "^3.1.2" - minimatch "^9.0.4" - minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^1.11.1" - -glob@^7.1.3, glob@^7.1.7: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== - -graceful-fs@^4.2.10, graceful-fs@^4.2.11: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -he@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - -http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@^0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -interpret@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" - integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-core-module@^2.11.0: - version "2.12.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" - integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== - dependencies: - has "^1.0.3" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -isbinaryfile@^4.0.8: - version "4.0.10" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" - integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - -jackspeak@^3.1.2: - version "3.4.3" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" - integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - -jest-worker@^27.4.5: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -json-parse-even-better-errors@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== - optionalDependencies: - graceful-fs "^4.1.6" - -karma-chrome-launcher@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz#eb9c95024f2d6dfbb3748d3415ac9b381906b9a9" - integrity sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q== - dependencies: - which "^1.2.1" - -karma-mocha@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d" - integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ== - dependencies: - minimist "^1.2.3" - -karma-sourcemap-loader@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz#b01d73f8f688f533bcc8f5d273d43458e13b5488" - integrity sha512-xCRL3/pmhAYF3I6qOrcn0uhbQevitc2DERMPH82FMnG+4WReoGcGFZb1pURf2a5apyrOHRdvD+O6K7NljqKHyA== - dependencies: - graceful-fs "^4.2.10" - -karma-webpack@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.1.tgz#4eafd31bbe684a747a6e8f3e4ad373e53979ced4" - integrity sha512-oo38O+P3W2mSPCSUrQdySSPv1LvPpXP+f+bBimNomS5sW+1V4SuhCuW8TfJzV+rDv921w2fDSDw0xJbPe6U+kQ== - dependencies: - glob "^7.1.3" - minimatch "^9.0.3" - webpack-merge "^4.1.5" - -karma@6.4.4: - version "6.4.4" - resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.4.tgz#dfa5a426cf5a8b53b43cd54ef0d0d09742351492" - integrity sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w== - dependencies: - "@colors/colors" "1.5.0" - body-parser "^1.19.0" - braces "^3.0.2" - chokidar "^3.5.1" - connect "^3.7.0" - di "^0.0.1" - dom-serialize "^2.2.1" - glob "^7.1.7" - graceful-fs "^4.2.6" - http-proxy "^1.18.1" - isbinaryfile "^4.0.8" - lodash "^4.17.21" - log4js "^6.4.1" - mime "^2.5.2" - minimatch "^3.0.4" - mkdirp "^0.5.5" - qjobs "^1.2.0" - range-parser "^1.2.1" - rimraf "^3.0.2" - socket.io "^4.7.2" - source-map "^0.6.1" - tmp "^0.2.1" - ua-parser-js "^0.7.30" - yargs "^16.1.1" - -kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -kotlin-web-helpers@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz#6cd4b0f0dc3baea163929c8638155b8d19c55a74" - integrity sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg== - dependencies: - format-util "^1.0.5" - -loader-runner@^4.2.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" - integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash@^4.17.15, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -log4js@^6.4.1: - version "6.6.1" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.6.1.tgz#48f23de8a87d2f5ffd3d913f24ca9ce77895272f" - integrity sha512-J8VYFH2UQq/xucdNu71io4Fo+purYYudyErgBbswWKO0MC6QVOERRomt5su/z6d3RJSmLyTGmXl3Q/XjKCf+/A== - dependencies: - date-format "^4.0.13" - debug "^4.3.4" - flatted "^3.2.6" - rfdc "^1.3.0" - streamroller "^3.1.2" - -lru-cache@^10.2.0: - version "10.4.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" - integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime@^2.5.2: - version "2.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - -minimatch@^3.0.4, minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^9.0.3: - version "9.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" - integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^9.0.4, minimatch@^9.0.5: - version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== - dependencies: - brace-expansion "^2.0.1" - -minimist@^1.2.3, minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== - -mkdirp@^0.5.5: - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - -mocha@11.7.1: - version "11.7.1" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.1.tgz#91948fecd624fb4bd154ed260b7e1ad3910d7c7a" - integrity sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A== - dependencies: - browser-stdout "^1.3.1" - chokidar "^4.0.1" - debug "^4.3.5" - diff "^7.0.0" - escape-string-regexp "^4.0.0" - find-up "^5.0.0" - glob "^10.4.5" - he "^1.2.0" - js-yaml "^4.1.0" - log-symbols "^4.1.0" - minimatch "^9.0.5" - ms "^2.1.3" - picocolors "^1.1.1" - serialize-javascript "^6.0.2" - strip-json-comments "^3.1.1" - supports-color "^8.1.1" - workerpool "^9.2.0" - yargs "^17.7.2" - yargs-parser "^21.1.1" - yargs-unparser "^2.0.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -node-releases@^2.0.19: - version "2.0.19" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" - integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -object-assign@^4: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-inspect@^1.9.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== - -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== - dependencies: - ee-first "1.1.1" - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -package-json-from-dist@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" - integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== - -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-scurry@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" - integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== - dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - -picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -qjobs@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" - integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== - -qs@6.10.3: - version "6.10.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" - integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== - dependencies: - side-channel "^1.0.4" - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -range-parser@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - -readdirp@^4.0.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" - integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -rechoir@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" - integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== - dependencies: - resolve "^1.20.0" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve@^1.20.0: - version "1.22.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" - integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== - dependencies: - is-core-module "^2.11.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -rfdc@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" - integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== - -rimraf@^3.0.0, rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -safe-buffer@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -schema-utils@^4.3.0, schema-utils@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.2.tgz#0c10878bf4a73fd2b1dfd14b9462b26788c806ae" - integrity sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ== - dependencies: - "@types/json-schema" "^7.0.9" - ajv "^8.9.0" - ajv-formats "^2.1.1" - ajv-keywords "^5.1.0" - -serialize-javascript@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" - integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== - dependencies: - randombytes "^2.1.0" - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - -socket.io-adapter@~2.5.2: - version "2.5.4" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz#4fdb1358667f6d68f25343353bd99bd11ee41006" - integrity sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg== - dependencies: - debug "~4.3.4" - ws "~8.11.0" - -socket.io-parser@~4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" - integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== - dependencies: - "@socket.io/component-emitter" "~3.1.0" - debug "~4.3.1" - -socket.io@^4.7.2: - version "4.7.5" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.5.tgz#56eb2d976aef9d1445f373a62d781a41c7add8f8" - integrity sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA== - dependencies: - accepts "~1.3.4" - base64id "~2.0.0" - cors "~2.8.5" - debug "~4.3.2" - engine.io "~6.5.2" - socket.io-adapter "~2.5.2" - socket.io-parser "~4.2.4" - -source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== - -source-map-loader@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-5.0.0.tgz#f593a916e1cc54471cfc8851b905c8a845fc7e38" - integrity sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA== - dependencies: - iconv-lite "^0.6.3" - source-map-js "^1.0.2" - -source-map-support@0.5.21, source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0, source-map@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - -streamroller@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.2.tgz#abd444560768b340f696307cf84d3f46e86c0e63" - integrity sha512-wZswqzbgGGsXYIrBYhOE0yP+nQ6XRk7xDcYwuQAGTYXdyAUmvgVFE0YU1g5pvQT0m7GBaQfYcSnlHbapuK0H0A== - dependencies: - date-format "^4.0.13" - debug "^4.3.4" - fs-extra "^8.1.0" - -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.0.0, supports-color@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -tapable@^2.1.1, tapable@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" - integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== - -terser-webpack-plugin@^5.3.11: - version "5.3.14" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz#9031d48e57ab27567f02ace85c7d690db66c3e06" - integrity sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw== - dependencies: - "@jridgewell/trace-mapping" "^0.3.25" - jest-worker "^27.4.5" - schema-utils "^4.3.0" - serialize-javascript "^6.0.2" - terser "^5.31.1" - -terser@^5.31.1: - version "5.43.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.43.1.tgz#88387f4f9794ff1a29e7ad61fb2932e25b4fdb6d" - integrity sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg== - dependencies: - "@jridgewell/source-map" "^0.3.3" - acorn "^8.14.0" - commander "^2.20.0" - source-map-support "~0.5.20" - -tmp@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -ua-parser-js@^0.7.30: - version "0.7.31" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" - integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== - -update-browserslist-db@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" - integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.1" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - -vary@^1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -void-elements@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" - integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== - -watchpack@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.1.tgz#29308f2cac150fa8e4c92f90e0ec954a9fed7fff" - integrity sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg== - dependencies: - glob-to-regexp "^0.4.1" - graceful-fs "^4.1.2" - -webpack-cli@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-6.0.1.tgz#a1ce25da5ba077151afd73adfa12e208e5089207" - integrity sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw== - dependencies: - "@discoveryjs/json-ext" "^0.6.1" - "@webpack-cli/configtest" "^3.0.1" - "@webpack-cli/info" "^3.0.1" - "@webpack-cli/serve" "^3.0.1" - colorette "^2.0.14" - commander "^12.1.0" - cross-spawn "^7.0.3" - envinfo "^7.14.0" - fastest-levenshtein "^1.0.12" - import-local "^3.0.2" - interpret "^3.1.1" - rechoir "^0.8.0" - webpack-merge "^6.0.1" - -webpack-merge@^4.1.5: - version "4.2.2" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" - integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== - dependencies: - lodash "^4.17.15" - -webpack-merge@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-6.0.1.tgz#50c776868e080574725abc5869bd6e4ef0a16c6a" - integrity sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg== - dependencies: - clone-deep "^4.0.1" - flat "^5.0.2" - wildcard "^2.0.1" - -webpack-sources@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.3.3.tgz#d4bf7f9909675d7a070ff14d0ef2a4f3c982c723" - integrity sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg== - -webpack@5.100.2: - version "5.100.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.100.2.tgz#e2341facf9f7de1d702147c91bcb65b693adf9e8" - integrity sha512-QaNKAvGCDRh3wW1dsDjeMdDXwZm2vqq3zn6Pvq4rHOEOGSaUMgOOjG2Y9ZbIGzpfkJk9ZYTHpDqgDfeBDcnLaw== - dependencies: - "@types/eslint-scope" "^3.7.7" - "@types/estree" "^1.0.8" - "@types/json-schema" "^7.0.15" - "@webassemblyjs/ast" "^1.14.1" - "@webassemblyjs/wasm-edit" "^1.14.1" - "@webassemblyjs/wasm-parser" "^1.14.1" - acorn "^8.15.0" - acorn-import-phases "^1.0.3" - browserslist "^4.24.0" - chrome-trace-event "^1.0.2" - enhanced-resolve "^5.17.2" - es-module-lexer "^1.2.1" - eslint-scope "5.1.1" - events "^3.2.0" - glob-to-regexp "^0.4.1" - graceful-fs "^4.2.11" - json-parse-even-better-errors "^2.3.1" - loader-runner "^4.2.0" - mime-types "^2.1.27" - neo-async "^2.6.2" - schema-utils "^4.3.2" - tapable "^2.1.1" - terser-webpack-plugin "^5.3.11" - watchpack "^2.4.1" - webpack-sources "^3.3.3" - -which@^1.2.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wildcard@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" - integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== - -workerpool@^9.2.0: - version "9.3.3" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.3.3.tgz#e75281fe62e851afb21cdeef8fa85f6a62ec3583" - integrity sha512-slxCaKbYjEdFT/o2rH9xS1hf4uRDch1w7Uo+apxhZ+sf/1d9e0ZVkn42kPNGP2dgjIx6YFvSevj0zHvbWe2jdw== - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -ws@~8.11.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" - integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs-unparser@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@^16.1.1: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yargs@^17.7.2: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/openrndr-demos/build.gradle.kts b/openrndr-demos/build.gradle.kts deleted file mode 100644 index 69f90cb9..00000000 --- a/openrndr-demos/build.gradle.kts +++ /dev/null @@ -1,18 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - demoImplementation(project(":orx-mesh-generators")) - demoImplementation(project(":orx-parameters")) - demoImplementation(project(":orx-noise")) - demoImplementation(project(":orx-jvm:orx-gui")) - demoImplementation(project(":orx-shader-phrases")) - demoImplementation(project(":orx-camera")) - demoImplementation(project(":orx-color")) - demoImplementation(project(":orx-composition")) - demoImplementation(project(":orx-shapes")) - demoImplementation(project(":orx-svg")) - demoImplementation(sharedLibs.slf4j.simple) - demoImplementation(openrndr.ffmpeg) -} diff --git a/openrndr-demos/src/demo/kotlin/Demo16BitPng.kt b/openrndr-demos/src/demo/kotlin/Demo16BitPng.kt deleted file mode 100644 index 77f99c5c..00000000 --- a/openrndr-demos/src/demo/kotlin/Demo16BitPng.kt +++ /dev/null @@ -1,13 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.loadImage - -fun main() = application { - program { - val image16 = loadImage("demo-data/images/16-bit.png") - val image8 = loadImage("demo-data/images/image-001.png") - extend { - drawer.image(image16) - drawer.image(image8, 320.0, 0.0) - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoAnimation01.kt b/openrndr-demos/src/demo/kotlin/DemoAnimation01.kt deleted file mode 100644 index 1b9da509..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoAnimation01.kt +++ /dev/null @@ -1,29 +0,0 @@ -import org.openrndr.animatable.Animatable -import org.openrndr.animatable.easing.Easing -import org.openrndr.application -import org.openrndr.math.Vector2 - -fun main() = application { - program { - class A: Animatable() { - var x = 0.0 - var y = Vector2(200.0, 200.0) - } - - val a = A() - a.apply { - ::y.animate(Vector2.ZERO, 10000, Easing.CubicInOut) - ::x.animate(100.0, 5000).completed.listen { - println("hello world") - ::x.animate(1.0, 5000).completed.listen { - println("we meet again") - } - } - } - - extend { - a.updateAnimation() - drawer.circle(a.y, 10.0) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoAsyncImages01.kt b/openrndr-demos/src/demo/kotlin/DemoAsyncImages01.kt deleted file mode 100644 index 052ba1b3..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoAsyncImages01.kt +++ /dev/null @@ -1,13 +0,0 @@ -import org.openrndr.application -import org.openrndr.internal.colorBufferLoader - -fun main() = application { - program { - extend { - val proxy = colorBufferLoader.loadFromUrl("https://avatars3.githubusercontent.com/u/31103334?s=200&v=4") - proxy.colorBuffer?.let { - drawer.image(it) - } - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoBillboardCircles01.kt b/openrndr-demos/src/demo/kotlin/DemoBillboardCircles01.kt deleted file mode 100644 index 636eebb4..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoBillboardCircles01.kt +++ /dev/null @@ -1,65 +0,0 @@ -// Demonstration of circles that always face the camera - -import org.openrndr.WindowMultisample -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.noise.uniformRing -import org.openrndr.extra.camera.Orbital -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 - - -fun main() = application { - configure { - multisample = WindowMultisample.SampleCount(8) - } - program { - extend(Orbital()) - - val circlePositions = vertexBuffer(vertexFormat { - attribute("position", VertexElementType.VECTOR3_FLOAT32) - attribute("scale", VertexElementType.FLOAT32) - }, 1000) - - circlePositions.put { - for (i in 0 until circlePositions.vertexCount) { - write(Vector3.uniformRing(2.0, 3.0)) - write(Math.random().toFloat()*0.1f) - } - } - - extend { - drawer.perspective(90.0, width*1.0/height*1.0, 0.1, 100.0) - - drawer.fill = ColorRGBa.PINK - drawer.stroke = null - drawer.drawStyle.alphaToCoverage = true - - drawer.depthWrite = true - drawer.depthTestPass = DepthTestPass.LESS_OR_EQUAL - - drawer.shadeStyle = shadeStyle { - vertexTransform = """ - vec3 viewOffset = (x_viewMatrix * x_modelMatrix * vec4(i_position, 1.0)).xyz; - vec2 i = vec2(0.0, 1.0); - x_viewMatrix = mat4(i.yxxx, i.xyxx, i.xxyx, i.xxxy); - x_modelMatrix = mat4(i.yxxx, i.xyxx, i.xxyx, i.xxxy); - x_position = viewOffset + vec3(a_position.xy * i_scale, 0.0); - vi_radius = vec2(i_scale); - """.trimIndent() - - // The circle bounds can be used to calculate a color or to sample a texture - fragmentTransform = """ - float r = length(c_boundsPosition.xy - 0.5) * 2.0; - x_fill.rg = c_boundsPosition.xy; - x_fill.a = 1.0 - step(1.0, r); - """.trimIndent() - - attributes(circlePositions) - } - - drawer.circles((0 until circlePositions.vertexCount).map { Vector2.ZERO }, 0.0) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoCatmullRom01.kt b/openrndr-demos/src/demo/kotlin/DemoCatmullRom01.kt deleted file mode 100644 index 46b06be0..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoCatmullRom01.kt +++ /dev/null @@ -1,32 +0,0 @@ -// Converting Catmull-Rom curves to Bezier - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.shapes.splines.CatmullRomChain2 -import org.openrndr.extra.shapes.splines.toContour -import org.openrndr.math.Polar -import org.openrndr.shape.ShapeContour - -fun main() = application { - program { - val points = List(6) { - Polar(it * 70.0, 100.0).cartesian + drawer.bounds.center - } - val cmr = CatmullRomChain2(points, 1.0, loop = true) - val contour = ShapeContour.fromPoints(cmr.positions(200), true) - - extend { - drawer.run { - clear(ColorRGBa.WHITE) - fill = null - stroke = ColorRGBa.BLACK - contour(contour) - circles(points, 5.0) - - stroke = ColorRGBa.RED - contour(cmr.toContour()) - fill = ColorRGBa.BLACK - } - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoCircleBatch01.kt b/openrndr-demos/src/demo/kotlin/DemoCircleBatch01.kt deleted file mode 100644 index e15fe5e7..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoCircleBatch01.kt +++ /dev/null @@ -1,18 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa - -/* -This demo just verifies that drawing a single circle still works with revamped circle drawing code - */ -fun main() = application { - program { - extend { - drawer.clear(ColorRGBa.GRAY) - drawer.fill = ColorRGBa.PINK - drawer.stroke = ColorRGBa.WHITE - drawer.strokeWeight = 2.0 - drawer.circle(100.0, 100.0, 50.0) - drawer.rectangle(100.0, 100.0, 50.0, 50.0) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoCircleBatch02.kt b/openrndr-demos/src/demo/kotlin/DemoCircleBatch02.kt deleted file mode 100644 index 16771dc2..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoCircleBatch02.kt +++ /dev/null @@ -1,43 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.circleBatch -import org.openrndr.draw.shadeStyle - -/* -This program demonstrates creating "pre-baked" batches of circles. -Batches can have varying fill, stroke and strokeWeight settings. - -Batches are (currently) static but stored in GPU memory but can be -animated using a vertex shader. Batches are fast to draw. -*/ - -fun main() = application { - program { - val batch = drawer.circleBatch { - for (i in 0 until 2000) { - fill = ColorRGBa.PINK.shade(Math.random()) - strokeWeight = Math.random() * 5 - circle(width * 0.5, height * 0.5, 20 * Math.random() + 5) - } - } - - extend { - drawer.clear(ColorRGBa.GRAY) - - // The following optional shadeStyle animates the batch - // by using polar coordinates: - // sets angle and radius based on time and shape ID. - drawer.shadeStyle = shadeStyle { - vertexTransform = """ - float a = float(c_instance) + p_time * 0.1; - float r = 200.0 + 100.0 * sin(a * 0.998); - x_position.x += r * sin(a); - x_position.y += r * cos(a); - """.trimIndent() - parameter("time", seconds) - } - - drawer.circles(batch) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoCircleBatch03.kt b/openrndr-demos/src/demo/kotlin/DemoCircleBatch03.kt deleted file mode 100644 index 93c4b775..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoCircleBatch03.kt +++ /dev/null @@ -1,30 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.color.hsv -import org.openrndr.extra.noise.simplex -import kotlin.math.abs - -/* -This program demonstrates dynamic circle batches - */ - -fun main() = application { - program { - extend { - drawer.clear(ColorRGBa.GRAY) - drawer.circles { - this.fill = ColorRGBa.PINK - for (i in 0 until 10000) { - val hue = simplex(i * 403, i * 149.0 + 0.2 * seconds) * 180.0 + 180.0 - fill = hsv(hue, 0.5, 0.3).toRGBa() - stroke = hsv(hue + 180.0, 0.5, 1.0).toRGBa() - val x = simplex(i * 337, i * 43.0 + 0.1 * seconds) * width / 2.0 + width / 2.0 - val y = simplex(i * 439, i * 39.0 - 0.1 * seconds) * height / 2.0 + height / 2.0 - val radius = simplex(i * 139, i * 51.0 + seconds * 0.43) * 20.0 + 20.0 - strokeWeight = abs(simplex(i * 139, i * 51.0 + seconds * 0.43) * radius * 0.5) - circle(x, y, radius) - } - } - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoClipping.kt b/openrndr-demos/src/demo/kotlin/DemoClipping.kt deleted file mode 100644 index c224aa3b..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoClipping.kt +++ /dev/null @@ -1,43 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.composition.ClipMode -import org.openrndr.extra.composition.composition -import org.openrndr.extra.composition.drawComposition -import org.openrndr.math.Vector2 -import org.openrndr.shape.Circle -import org.openrndr.shape.LineSegment -import org.openrndr.shape.Shape - -fun main() = application { - program { - val outline = Shape( - listOf( - Circle(drawer.bounds.center, 70.0).contour.reversed, - Circle(drawer.bounds.center, 100.0).contour, - ) - ) - - val radius = outline.bounds.dimensions.length / 2 - val off = outline.bounds.center - val num = radius.toInt() - - val svg = drawComposition { - lineSegments(List(num) { segNum -> - val yNorm = (segNum / (num - 1.0)) - val x = ((segNum % 2) * 2.0 - 1.0) * radius - val y = (yNorm * 2.0 - 1.0) * radius - val start = Vector2(-x, y) + off - val end = Vector2(x, y) + off - LineSegment(start, end) - }) - clipMode = ClipMode.INTERSECT - shape(outline) - } - extend { - drawer.clear(ColorRGBa.PINK) - drawer.fill = null - drawer.shape(outline) - drawer.composition(svg) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoColorBufferCopy01.kt b/openrndr-demos/src/demo/kotlin/DemoColorBufferCopy01.kt deleted file mode 100644 index ca972264..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoColorBufferCopy01.kt +++ /dev/null @@ -1,14 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage - -fun main() = application { - program { - val cb0 = loadImage("demo-data/images/image-001.png") - val cb1 = cb0.createEquivalent() - extend { - cb0.copyTo(cb1) - drawer.image(cb1) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoComputeStyle01.kt b/openrndr-demos/src/demo/kotlin/DemoComputeStyle01.kt deleted file mode 100644 index 263346a1..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoComputeStyle01.kt +++ /dev/null @@ -1,97 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.* -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 - -/** - * SSBO -> https://www.khronos.org/opengl/wiki/Shader_Storage_Buffer_Object - * - * This program demonstrates - * - how to create an SSBO - * - how to populate an SSBO with data - * - how to pass an SSBO to a compute shader - * - how to download the SSBO data to the CPU before and after executing the compute shader - * - * It prints the before/after data to observe how it was modified by the compute shader. - * - * Writing a useful compute shader that processes data faster than in the CPU is NOT a goal - * of this program, since such a simple calculation would be faster and easier if done completely in the CPU. - * - * Notice how the execute() call has a width, height and depth of 1 - * (basically doing just one computation). - * - * A useful compute shader would do a large number of parallel computations. - * This will be presented in a different demo. - * - * Output: byteBuffer -> text - */ - -fun main() = application { - program { - // Define SSBO format - val fmt = shaderStorageFormat { - primitive("time", BufferPrimitiveType.FLOAT32) - primitive("vertex", BufferPrimitiveType.VECTOR2_FLOAT32, 3) - struct("Particle", "particles", 5) { - primitive("pos", BufferPrimitiveType.VECTOR3_FLOAT32) - primitive("age", BufferPrimitiveType.FLOAT32) - } - } - println("Study the padding in the format:\n$fmt\n") - - // Create SSBO - val ssbo = shaderStorageBuffer(fmt) - - // Populate SSBO - ssbo.put { - write(3.0.toFloat()) // time - repeat(3) { - write(Vector2(1.1, 1.2)) // vertex - } - repeat(5) { - write(Vector3(2.1, 2.2, 2.3)) // pos - write(1.0.toFloat()) // age - } - } - - // Create Compute Shader - val cs = computeStyle { - computeTransform = """ - b_myData.time = 3.3; - b_myData.vertex[0] = vec2(7.01); - b_myData.vertex[1] = vec2(7.02); - b_myData.vertex[2] = vec2(7.03); - b_myData.particles[0].pos = vec3(112.0); - b_myData.particles[0].age = 111.0; - """.trimIndent() - } - cs.buffer("myData", ssbo) - - // Download SSBO data to CPU - val byteBufferBeforeExecute = ssbo.createByteBuffer() - byteBufferBeforeExecute.rewind() - ssbo.read(byteBufferBeforeExecute) - - // Execute compute shader - cs.execute(1, 1, 1) - - // Download SSBO data to CPU - val byteBufferAfterExecute = ssbo.createByteBuffer() - byteBufferAfterExecute.rewind() - ssbo.read(byteBufferAfterExecute) - - // Debugging - - // Notice the (maybe unexpected) 0.0 padding values printed on the console. - // Depending on the variable size in bytes, padding may be added by the system - // to align them in memory. This will depend on the sizes of the involved variables, - // and their order. For instance, a vec3 and a float do not require padding, but - // a float followed by a vec3 pads the float with 3 values, and the vec3 with one. - // Run compute02.kt and study the output to observe a more inefficient layout. - byteBufferBeforeExecute.rewind() - byteBufferAfterExecute.rewind() - repeat(ssbo.format.size / 4) { - println("$it: ${byteBufferBeforeExecute.float} -> ${byteBufferAfterExecute.float}") - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoComputeStyle02.kt b/openrndr-demos/src/demo/kotlin/DemoComputeStyle02.kt deleted file mode 100644 index b57cd04e..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoComputeStyle02.kt +++ /dev/null @@ -1,83 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.* -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 - -/** - * A program identical to compute01.kt, except that the order of variables - * `age` and `pos` have been swapped, resulting in a less ideal memory layout. - * In this case the SSBO requires 192 bytes instead of 112, and padding is - * inserted after the variables `time`, `age` and `pos`. - * - * Output: byteBuffer -> text - */ - -fun main() = application { - program { - // Define SSBO format - val fmt = shaderStorageFormat { - primitive("time", BufferPrimitiveType.FLOAT32) - primitive("vertex", BufferPrimitiveType.VECTOR2_FLOAT32, 3) - struct("Particle", "particles", 5) { - primitive("age", BufferPrimitiveType.FLOAT32) - primitive("pos", BufferPrimitiveType.VECTOR3_FLOAT32) - } - } - println("Study the padding in the format:\n$fmt\n") - - // Create SSBO - val ssbo = shaderStorageBuffer(fmt) - - // Populate SSBO - ssbo.put { - write(3.0.toFloat()) // time - repeat(3) { - write(Vector2(1.1, 1.2)) // vertex - } - repeat(5) { - write(1.0.toFloat()) // age - write(Vector3(2.1, 2.2, 2.3))// pos - } - } - - // Create Compute Shader - val cs = computeStyle { - computeTransform = """ - b_myData.time = 3.3; - b_myData.vertex[0] = vec2(7.01); - b_myData.vertex[1] = vec2(7.02); - b_myData.vertex[2] = vec2(7.03); - b_myData.particles[0].pos = vec3(112.0); - b_myData.particles[0].age = 111.0; - """.trimIndent() - } - cs.buffer("myData", ssbo) - - // Download SSBO data to CPU - val byteBufferBeforeExecute = ssbo.createByteBuffer() - byteBufferBeforeExecute.rewind() - ssbo.read(byteBufferBeforeExecute) - - // Execute compute shader - cs.execute(1, 1, 1) - - // Download SSBO data to CPU - val byteBufferAfterExecute = ssbo.createByteBuffer() - byteBufferAfterExecute.rewind() - ssbo.read(byteBufferAfterExecute) - - // Debugging - - // Notice the (maybe unexpected) 0.0 padding values printed on the console. - // Depending on the variable size in bytes, padding may be added by the system - // to align them in memory. This will depend on the sizes of the involved variables, - // and their order. For instance, a vec3 and a float do not require padding, but - // a float followed by a vec3 pads the float with 3 values, and the vec3 with one. - // Run compute02.kt and study the output to observe a more inefficient layout. - byteBufferBeforeExecute.rewind() - byteBufferAfterExecute.rewind() - repeat(ssbo.format.size / 4) { - println("$it: ${byteBufferBeforeExecute.float} -> ${byteBufferAfterExecute.float}") - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoComputeStyle03.kt b/openrndr-demos/src/demo/kotlin/DemoComputeStyle03.kt deleted file mode 100644 index b9f8f2ac..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoComputeStyle03.kt +++ /dev/null @@ -1,88 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.* -import org.openrndr.math.IntVector3 - -/** - * This program demonstrates - * - how to use a compute shader and an SSBO to do many computations in parallel - * - how to use a compute shader to initialize an SSBO - * - how to use a different shader to update the SSBO - * - * Note the `workGroupSize` property. The GPU splits tasks - * into chunks and computes those in parallel. The ideal workGroupSize depends on - * the GPU being used. Too small of a size may be inefficient. - * - * In some cases a compute shader works with 2D images or 3D data structures, but in this - * program we are processing the elements of a 1D array. That's why we only - * increase the x value to 32, leaving y and z equal to 1. - * - * Note: this program only does the computation, but does not visualize the results - * in any way. We will do that in another program. - * - * Output: none - */ - -fun main() = application { - program { - val particleCount = 4800 - // Define SSBO format - val fmt = shaderStorageFormat { - struct("Particle", "particle", particleCount) { - primitive("pos", BufferPrimitiveType.VECTOR2_FLOAT32) - primitive("velocity", BufferPrimitiveType.VECTOR2_FLOAT32) - } - } - println("Study the padding in the format:\n$fmt\n") - - // Create SSBO - val particlesSSBO = shaderStorageBuffer(fmt) - - // Create Compute Shaders - val initCS = computeStyle { - computeTransform = """ - uint id = gl_GlobalInvocationID.x; - b_particles.particle[id].pos = vec2(320.0, 240.0); - b_particles.particle[id].velocity = vec2(cos(id), sin(id)); - """.trimIndent() - workGroupSize = IntVector3(32, 1, 1) - } - val updateCS = computeStyle { - computeTransform = """ - // The id of the element being currently processed - uint id = gl_GlobalInvocationID.x; - - // Add velocity to position - b_particles.particle[id].pos += b_particles.particle[id].velocity; - - // Deal with the particle trying to escape the window - if(b_particles.particle[id].pos.x < 0.0) { - b_particles.particle[id].pos.x = 0.0; - b_particles.particle[id].velocity.x = abs(b_particles.particle[id].velocity.x); - } - if(b_particles.particle[id].pos.y < 0.0) { - b_particles.particle[id].pos.y = 0.0; - b_particles.particle[id].velocity.y = abs(b_particles.particle[id].velocity.y); - } - if(b_particles.particle[id].pos.x > p_windowSize.x) { - b_particles.particle[id].pos.x = p_windowSize.x; - b_particles.particle[id].velocity.x = -abs(b_particles.particle[id].velocity.x); - } - if(b_particles.particle[id].pos.y > p_windowSize.y) { - b_particles.particle[id].pos.y = p_windowSize.y; - b_particles.particle[id].velocity.y = -abs(b_particles.particle[id].velocity.y); - } - """.trimIndent() - workGroupSize = IntVector3(32, 1, 1) - } - - // Execute initCS - initCS.buffer("particles", particlesSSBO) - initCS.execute(particleCount / initCS.workGroupSize.x) - - extend { - updateCS.buffer("particles", particlesSSBO) - updateCS.parameter("windowSize", drawer.bounds.dimensions) - updateCS.execute(particleCount / updateCS.workGroupSize.x) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoComputeStyle04.kt b/openrndr-demos/src/demo/kotlin/DemoComputeStyle04.kt deleted file mode 100644 index e4579554..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoComputeStyle04.kt +++ /dev/null @@ -1,107 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.math.IntVector3 - -/** - * This program draws moving points to demonstrate - * how to write the resulting calculations of a compute shader into a vertex buffer. - * - * There are various ways to output the calculations and make them visible to the user: - * - Write into a vertex buffer, which can be rendered as points, lines or triangles by OPENRNDR - * (this program). - * - Update a colorBuffer (the pixels of a texture). - * - * Output: vertexBuffer -> POINTS - */ - -fun main() = application { - program { - val particleCount = 4800 - // Define SSBO format - val fmt = shaderStorageFormat { - struct("Particle", "particle", particleCount) { - primitive("pos", BufferPrimitiveType.VECTOR2_FLOAT32) - primitive("velocity", BufferPrimitiveType.VECTOR2_FLOAT32) - } - } - println("Study the padding in the format:\n$fmt\n") - - // Create SSBO - val particleSSBO = shaderStorageBuffer(fmt) - - // Create a vertex buffer. - // Padding is required if position has less than 4 dimensions. - // val vb = vertexBuffer(vertexFormat { - // position(3) - // paddingFloat(1) - // }, particleCount) - - // With BufferAlignment.STD430 padding is taken care of - val vb = vertexBuffer(vertexFormat(BufferAlignment.STD430) { - position(3) - }, particleCount) - - // Create Compute Shaders - val initCS = computeStyle { - computeTransform = """ - uint id = gl_GlobalInvocationID.x; - b_particles.particle[id].pos = vec2(320.0, 240.0); - b_particles.particle[id].velocity = vec2(cos(id), sin(id)); - """.trimIndent() - workGroupSize = IntVector3(32, 1, 1) - } - val updateCS = computeStyle { - // We can create GLSL functions in the computePreamble. - // Thanks to an `inout` variable, we can shorten the code. - // (Compare this with compute03.kt) - computePreamble = """ - void updateParticle(inout Particle p) { - // Add velocity to position - p.pos += p.velocity; - - // Deal with the particle trying to escape the window - if(p.pos.x < 0.0) { - p.pos.x = 0.0; - p.velocity.x = abs(p.velocity.x); - } - if(p.pos.y < 0.0) { - p.pos.y = 0.0; - p.velocity.y = abs(p.velocity.y); - } - if(p.pos.x > p_windowSize.x) { - p.pos.x = p_windowSize.x; - p.velocity.x = -abs(p.velocity.x); - } - if(p.pos.y > p_windowSize.y) { - p.pos.y = p_windowSize.y; - p.velocity.y = -abs(p.velocity.y); - } - } - """.trimIndent() - computeTransform = """ - // The id of the element being currently processed - uint id = gl_GlobalInvocationID.x; - updateParticle(b_particles.particle[id]); - - // Update the vertexBuffer with data from the shaderStorageBuffer - b_vb.vertex[id].position.xy = b_particles.particle[id].pos; - """.trimIndent() - workGroupSize = IntVector3(32, 1, 1) - } - - // Execute initCS - initCS.buffer("particles", particleSSBO) - initCS.execute(particleCount / initCS.workGroupSize.x) - - extend { - updateCS.buffer("vb", vb.shaderStorageBufferView()) - updateCS.buffer("particles", particleSSBO) - updateCS.parameter("windowSize", drawer.bounds.dimensions) - updateCS.execute(particleCount /updateCS.workGroupSize.x) - - drawer.fill = ColorRGBa.WHITE - drawer.vertexBuffer(vb, DrawPrimitive.POINTS) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoComputeStyle05.kt b/openrndr-demos/src/demo/kotlin/DemoComputeStyle05.kt deleted file mode 100644 index 985ce2a3..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoComputeStyle05.kt +++ /dev/null @@ -1,97 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.math.IntVector3 - -/** - * This program demonstrates how to change the rendering type from POINTS to LINE_LOOP. - * In everything else, it's identical to compute04.kt - * - * Output: vertexBuffer -> LINE_LOOP - */ - -fun main() = application { - program { - val particleCount = 4800 - // Define SSBO format - val fmt = shaderStorageFormat { - struct("Particle", "particle", particleCount) { - primitive("pos", BufferPrimitiveType.VECTOR2_FLOAT32) - primitive("velocity", BufferPrimitiveType.VECTOR2_FLOAT32) - } - } - println("Study the padding in the format:\n$fmt\n") - - // Create SSBO - val particleSSBO = shaderStorageBuffer(fmt) - - // Create a vertex buffer. - // Padding is required if position has less than 4 dimensions. - // val vb = vertexBuffer(vertexFormat { - // position(3) - // paddingFloat(1) - // }, particleCount) - - // With BufferAlignment.STD430 padding is taken care of - val vb = vertexBuffer(vertexFormat(BufferAlignment.STD430) { - position(3) - }, particleCount) - - // Create Compute Shaders - val initCS = computeStyle { - computeTransform = """ - uint id = gl_GlobalInvocationID.x; - b_particles.particle[id].pos = vec2(320.0, 240.0); - b_particles.particle[id].velocity = vec2(cos(id), sin(id)); - """.trimIndent() - workGroupSize = IntVector3(32, 1, 1) - } - val updateCS = computeStyle { - computePreamble = """ - void updateParticle(inout Particle p) { - // Add velocity to position - p.pos += p.velocity; - - // Deal with the particle trying to escape the window - if(p.pos.x < 0.0) { - p.pos.x = 0.0; - p.velocity.x = abs(p.velocity.x); - } - if(p.pos.y < 0.0) { - p.pos.y = 0.0; - p.velocity.y = abs(p.velocity.y); - } - if(p.pos.x > p_windowSize.x) { - p.pos.x = p_windowSize.x; - p.velocity.x = -abs(p.velocity.x); - } - if(p.pos.y > p_windowSize.y) { - p.pos.y = p_windowSize.y; - p.velocity.y = -abs(p.velocity.y); - } - } - """.trimIndent() - computeTransform = """ - // The id of the element being currently processed - uint id = gl_GlobalInvocationID.x; - updateParticle(b_particles.particle[id]); - b_vb.vertex[id].position.xy = b_particles.particle[id].pos; - """.trimIndent() - workGroupSize = IntVector3(32, 1, 1) - } - - // Execute initCS - initCS.buffer("particles", particleSSBO) - initCS.execute(particleCount / initCS.workGroupSize.x) - - extend { - updateCS.buffer("vb", vb.shaderStorageBufferView()) - updateCS.buffer("particles", particleSSBO) - updateCS.parameter("windowSize", drawer.bounds.dimensions) - updateCS.execute(particleCount / updateCS.workGroupSize.x) - - drawer.fill = ColorRGBa.WHITE - drawer.vertexBuffer(vb, DrawPrimitive.LINE_LOOP) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoComputeStyle06.kt b/openrndr-demos/src/demo/kotlin/DemoComputeStyle06.kt deleted file mode 100644 index d9421352..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoComputeStyle06.kt +++ /dev/null @@ -1,93 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.math.IntVector3 - -/** - * This program is almost identical to compute04.kt, but instead of writing into - * a vertex buffer, it draws the particles into a 2D image (a ColorBuffer). - * - * Output: 2D Image - */ - -fun main() = application { - program { - val particleCount = 4800 - // Define SSBO format - val fmt = shaderStorageFormat { - struct("Particle", "particle", particleCount) { - primitive("pos", BufferPrimitiveType.VECTOR2_FLOAT32) - primitive("velocity", BufferPrimitiveType.VECTOR2_FLOAT32) - } - } - println("Study the padding in the format:\n$fmt\n") - - // Create SSBO - val particleSSBO = shaderStorageBuffer(fmt) - - // Create a color buffer to write into. - val cb = colorBuffer(width, height, type = ColorType.FLOAT32) - - // Create Compute Shaders - val initCS = computeStyle { - computeTransform = """ - uint id = gl_GlobalInvocationID.x; - b_particles.particle[id].pos = vec2(320.0, 240.0); - b_particles.particle[id].velocity = vec2(cos(id), sin(id)); - """.trimIndent() - workGroupSize = IntVector3(32, 1, 1) - } - val updateCS = computeStyle { - computePreamble = """ - void updateParticle(inout Particle p) { - // Add velocity to position - p.pos += p.velocity; - - // Deal with the particle trying to escape the window - if(p.pos.x < 0.0) { - p.pos.x = 0.0; - p.velocity.x = abs(p.velocity.x); - } - if(p.pos.y < 0.0) { - p.pos.y = 0.0; - p.velocity.y = abs(p.velocity.y); - } - if(p.pos.x > p_windowSize.x) { - p.pos.x = p_windowSize.x; - p.velocity.x = -abs(p.velocity.x); - } - if(p.pos.y > p_windowSize.y) { - p.pos.y = p_windowSize.y; - p.velocity.y = -abs(p.velocity.y); - } - } - """.trimIndent() - computeTransform = """ - // The id of the element being currently processed - uint id = gl_GlobalInvocationID.x; - updateParticle(b_particles.particle[id]); - - // write into the image - imageStore(p_img, ivec2(b_particles.particle[id].pos.xy), vec4(1.0)); - """.trimIndent() - workGroupSize = IntVector3(32, 1, 1) - } - // Execute initCS - initCS.buffer("particles", particleSSBO) - initCS.execute(particleCount, initCS.workGroupSize.x) - - extend { - // Clear the image, otherwise all pixels become eventually white - cb.fill(ColorRGBa.TRANSPARENT) - - // Pass image to the compute shader. - // We can choose between READ, READ_WRITE or WRITE. - updateCS.image("img", cb.imageBinding(0, ImageAccess.WRITE)) - updateCS.buffer("particles", particleSSBO) - updateCS.parameter("windowSize", drawer.bounds.dimensions) - updateCS.execute(particleCount / updateCS.workGroupSize.x) - - drawer.image(cb) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoComputeStyle07.kt b/openrndr-demos/src/demo/kotlin/DemoComputeStyle07.kt deleted file mode 100644 index edd92235..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoComputeStyle07.kt +++ /dev/null @@ -1,120 +0,0 @@ -package compute - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.math.IntVector3 - -/** - * This program demonstrates - * - how to modify the particles, adding color, age and ageVelocity. - * - make the particles wrap around the edges instead of bouncing on them. - * - * Output: 2D Image - */ - -fun main() = application { - program { - val particleCount = 4800 - // Define SSBO format - val fmt = shaderStorageFormat { - struct("Particle", "particle", particleCount) { - primitive("pos", BufferPrimitiveType.VECTOR2_FLOAT32) - primitive("velocity", BufferPrimitiveType.VECTOR2_FLOAT32) - primitive("color", BufferPrimitiveType.VECTOR3_FLOAT32) - primitive("age", BufferPrimitiveType.FLOAT32) - primitive("ageVelocity", BufferPrimitiveType.FLOAT32) - } - } - println("Study the padding in the format:\n$fmt\n") - - // Create SSBO - val particleSSBO = shaderStorageBuffer(fmt) - - // Create a color buffer to write into. - val cb = colorBuffer(width, height, type = ColorType.FLOAT32) - - // Create Compute Shaders - val initCS = computeStyle { - computePreamble = """ - // From lygia.xyz - vec3 hue2rgb(const in float hue) { - float R = abs(hue * 6.0 - 3.0) - 1.0; - float G = 2.0 - abs(hue * 6.0 - 2.0); - float B = 2.0 - abs(hue * 6.0 - 4.0); - return clamp(vec3(R,G,B), 0.0, 1.0); - } - void initParticle(uint id, inout Particle p) { - float k = 100.0 / $particleCount; - p.velocity = vec2(cos(id * k), sin(id * k)); - p.pos = vec2(320.0, 240.0) + p.velocity * id * 0.0003; - p.color = hue2rgb(fract(id * k / 4.0)); - p.age = id * k / 5.0; - p.ageVelocity = sin(id * k * 1.0) * 0.1 + 0.2; - } - """.trimIndent() - computeTransform = """ - uint id = gl_GlobalInvocationID.x; - initParticle(id, b_particles.particle[id]); - """.trimIndent() - workGroupSize = IntVector3(32, 1, 1) - } - val updateCS = computeStyle { - computePreamble = """ - void updateParticle(inout Particle p) { - // Add velocity to position - p.pos += p.velocity; - - // Update age - p.age += p.ageVelocity; - - // Deal with the particle trying to escape the window - if(p.pos.x < 0.0) { - p.pos.x += p_windowSize.x; - } - if(p.pos.y < 0.0) { - p.pos.y += p_windowSize.y; - } - if(p.pos.x > p_windowSize.x) { - p.pos.x = 0.0; - } - if(p.pos.y > p_windowSize.y) { - p.pos.y = 0.0; - } - } - """.trimIndent() - computeTransform = """ - // The id of the element being currently processed - uint id = gl_GlobalInvocationID.x; - updateParticle(b_particles.particle[id]); - - float alpha = sin(b_particles.particle[id].age); - alpha = sin(alpha + sin(alpha)) * 0.5 + 0.5; - - // draw particle in the image - imageStore(p_img, - ivec2(b_particles.particle[id].pos), - vec4((b_particles.particle[id].color * alpha), alpha) - ); - """.trimIndent() - } - - // Execute initCS - initCS.buffer("particles", particleSSBO) - initCS.execute(particleCount / initCS.workGroupSize.x) - - extend { - // Clear the image, otherwise all pixels become eventually white - cb.fill(ColorRGBa.TRANSPARENT) - - // Pass image to the compute shader. - // We can choose between READ, READ_WRITE or WRITE. - updateCS.image("img", cb.imageBinding(0, ImageAccess.WRITE)) - updateCS.buffer("particles", particleSSBO) - updateCS.parameter("windowSize", drawer.bounds.dimensions) - updateCS.execute(particleCount / updateCS.workGroupSize.x) - - drawer.image(cb) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoComputeStyle08.kt b/openrndr-demos/src/demo/kotlin/DemoComputeStyle08.kt deleted file mode 100644 index 251e14a4..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoComputeStyle08.kt +++ /dev/null @@ -1,128 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* - -/** - * This program demonstrates - * - How to animate 2D particles but render them as 2D triangles. - * - How to deal with two buffers of different size: the particles buffer - * and the vertex buffer, which has three times more elements. - * The update compute shader calculates 3 vertices for each particle. - * - How to make the init compute shader initialize both the particles (position and velocity) - * and the colors of the vertices. - * - How to create a minimal ShadeStyle to set per-vertex colors, - * and render the colors interpolated across each triangle. - * - * Output: vertexBuffer -> TRIANGLES - */ - -fun main() = application { - program { - val particleCount = 3200 - - // Define SSBO format - val ssboFmt = shaderStorageFormat { - struct("Particle", "particle", particleCount) { - primitive("pos", BufferPrimitiveType.VECTOR2_FLOAT32) - primitive("velocity", BufferPrimitiveType.VECTOR2_FLOAT32) - } - } - println("Study the padding in the format:\n$ssboFmt\n") - - // Create SSBO - val particleSSBO = shaderStorageBuffer(ssboFmt) - - // The padding is required to match the expected layout. - // Even if we are in a 2D world we need to use a 3D position - // because that's what OPENRNDR expects in its shaders. - val vertFormat = vertexFormat(BufferAlignment.STD430) { - position(3) - color(4) - } - println("Study the padding in the vertex buffer format:\n$vertFormat\n") - - // Create vertex buffer. - // Note how me multiply the particleCount by 3 (three vertices per particle). - val vb = vertexBuffer(vertFormat, particleCount * 3) - - // Create Compute Shaders - val initCS = computeStyle { - computeTransform = """ - uint id = gl_GlobalInvocationID.x; - - b_particles.particle[id].pos = vec2(320.0, 240.0); - b_particles.particle[id].velocity = vec2(cos(id), sin(id)); - - // Generate colors based on id - vec4 col = vec4( - sin(id + 0.000) * 0.5 + 0.5, - sin(id + 2.094) * 0.5 + 0.5, - sin(id + 4.188) * 0.5 + 0.5, 1.0); - - // Swap R, G and B and darken the two rear vertices of each triangle. - // This creates a gradient in each triangle. - b_vb.vertex[id * 3 + 0].color = col.rgba; - b_vb.vertex[id * 3 + 1].color = col.grba * 0.5; - b_vb.vertex[id * 3 + 2].color = col.rbga * 0.25; - """.trimIndent() - } - val updateCS = computeStyle { - computePreamble = """ - const float margin = 16.0; - void updateParticle(inout Particle p) { - // Add velocity to position - p.pos += p.velocity; - - // Deal with the particle trying to escape the window - if(p.pos.x < -margin) { - p.pos.x += p_windowSize.x + 2.0 * margin; - } - if(p.pos.y < -margin) { - p.pos.y += p_windowSize.y + 2.0 * margin; - } - if(p.pos.x > p_windowSize.x + margin) { - p.pos.x = -margin; - } - if(p.pos.y > p_windowSize.y + margin) { - p.pos.y = -margin; - } - } - """.trimIndent() - computeTransform = """ - // The id of the element being currently processed - uint id = gl_GlobalInvocationID.x; - updateParticle(b_particles.particle[id]); - - // Calculate the vertices of a directed triangle - // pointing towards `velocity`. Hint: - // vel (x,y) has two normals (-y, x) and (y, -x). - vec2 pos = b_particles.particle[id].pos; - vec2 vel = b_particles.particle[id].velocity * margin; - vec2 n0 = vec2(-vel.y, vel.x) * 0.5; - b_vb.vertex[id * 3 + 0].position = vec3(pos + vel, 0.0); - b_vb.vertex[id * 3 + 1].position = vec3(pos + n0, 0.0); - b_vb.vertex[id * 3 + 2].position = vec3(pos - n0, 0.0); - """.trimIndent() - } - - // Execute initCS - initCS.buffer("vb", vb.shaderStorageBufferView()) - initCS.buffer("particles", particleSSBO) - initCS.execute(particleCount / initCS.workGroupSize.x) - - extend { - updateCS.buffer("vb", vb.shaderStorageBufferView()) - updateCS.buffer("particles", particleSSBO) - updateCS.parameter("windowSize", drawer.bounds.dimensions) - updateCS.execute(particleCount / updateCS.workGroupSize.x) - - drawer.fill = ColorRGBa.WHITE - drawer.shadeStyle = shadeStyle { - // The color of every triangle's pixel is interpolated using - // its three vertex colors - fragmentTransform = "x_fill = va_color;" - } - drawer.vertexBuffer(vb, DrawPrimitive.TRIANGLES) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoComputeStyle09.kt b/openrndr-demos/src/demo/kotlin/DemoComputeStyle09.kt deleted file mode 100644 index 680b39b3..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoComputeStyle09.kt +++ /dev/null @@ -1,80 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.computeStyle -import org.openrndr.draw.execute -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.meshgenerators.dodecahedronMesh -import org.openrndr.math.IntVector3 -import org.openrndr.math.Vector3 - -/** - * This program demonstrates - * - How to use a compute shader to deform a 3D shape (a vertex buffer). - * We create a dodecahedron vertex buffer and displace its vertices - * along their normals, exploding the object into 12 pieces with - * 3 triangles each (3 triangles to form a pentagon). - * - * Use the mouse for panning, rotating and zooming. - * - * Output: vertexBuffer -> TRIANGLES - */ - -fun main() = application { - program { - val vb = dodecahedronMesh(2.0) - - //vb.saveOBJ("/tmp/dodecahedron.obj") // study with Blender3D - - println(vb.vertexFormat.toString().replace("), ", "),\n ").replace("[", "[\n ").replace("]", "\n]")) - println("Vertex count: ${vb.vertexCount}") - - // Create Compute Shaders - val updateCS = computeStyle { - computeTransform = """ - // The id of the element being currently processed - uint id = gl_GlobalInvocationID.x; - - b_vb.vertex[id].position += b_vb.vertex[id].normal * 0.01; - """.trimIndent() - workGroupSize = IntVector3(64, 1, 1) - } - - // Debugging: print the mesh data - val shadow = vb.shadow - shadow.download() - val reader = shadow.reader() - reader.rewind() - repeat(vb.vertexCount) { - println(it) - // Notice how we read Vector4's instead of Vector3 or Vector2 - // because the data has been padded to align 16-byte boundaries. - val pos = reader.readVector4() - val nrm = reader.readVector4() - val uv = reader.readVector4() - println(" pos: ${pos.xyz}") - println(" nrm: ${nrm.xyz}") - println(" uv: ${uv.xy}") - } - - val cam = Orbital() - cam.eye = Vector3.UNIT_Z * 5.0 - - extend(cam) - extend { - updateCS.buffer("vb", vb.shaderStorageBufferView()) - // We use a width of 2 because we have 108 vertices and the - // workgroup size is 64. 2 x 64 = 128, which is greater than 108 and - // therefore processes all the vertices. - updateCS.execute(2) - - drawer.clear(ColorRGBa.GRAY) - drawer.fill = ColorRGBa.WHITE - drawer.shadeStyle = shadeStyle { - fragmentTransform = "x_fill.rgb = va_normal.xyz * 0.5 + 0.5;" - } - drawer.vertexBuffer(vb, DrawPrimitive.TRIANGLES) - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoComputeStyle10.kt b/openrndr-demos/src/demo/kotlin/DemoComputeStyle10.kt deleted file mode 100644 index 1ed52971..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoComputeStyle10.kt +++ /dev/null @@ -1,88 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.computeStyle -import org.openrndr.draw.execute -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.color.presets.WHEAT -import org.openrndr.extra.meshgenerators.dodecahedronMesh -import org.openrndr.math.IntVector3 -import org.openrndr.math.Vector3 - -/** - * This program is a variation of compute09.kt. It draws `vb` - * multiple times, each with a unique translation and rotation. - * - * For each item drawn, the instance number is sent to the shade style - * as a float uniform (named `p_i`) to shade them with unique hues. - * The interpolated normal varying is used to set the color, and - * this color rotated using `p_i` as the rotation angle. - * - * Use the mouse for panning, rotating and zooming. - * - * Output: vertexBuffer -> TRIANGLES - */ - -fun main() = application { - program { - val vb = dodecahedronMesh(2.0) - - // Create Compute Shaders - val updateCS = computeStyle { - computeTransform = """ - // The id of the element being currently processed - uint id = gl_GlobalInvocationID.x; - - b_vb.vertex[id].position += b_vb.vertex[id].normal * 0.01; - """.trimIndent() - workGroupSize = IntVector3(64, 1, 1) - } - - val cam = Orbital() - cam.eye = Vector3.UNIT_Z * 5.0 - - val style = shadeStyle { - // From https://github.com/dmnsgn/glsl-rotate - fragmentPreamble = """ - mat4 rotation3d(vec3 axis, float angle) { - axis = normalize(axis); - float s = sin(angle); - float c = cos(angle); - float oc = 1.0 - c; - - return mat4( - oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, - oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, - oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, - 0.0, 0.0, 0.0, 1.0 - ); - } - - vec3 rotate(vec3 v, vec3 axis, float angle) { - return (rotation3d(axis, angle) * vec4(v, 1.0)).xyz; - } - """.trimIndent() - fragmentTransform = """ - x_fill.rgb = rotate(va_normal.xyz * 0.5 + 0.5, - normalize(vec3(1.0)), p_i); - """.trimIndent() - } - - extend(cam) - extend { - updateCS.buffer("vb", vb.shaderStorageBufferView()) - updateCS.execute(2) - - drawer.clear(ColorRGBa.WHEAT.shade(0.2)) - drawer.fill = ColorRGBa.WHITE - drawer.shadeStyle = style - repeat(10) { - style.parameter("i", it * 0.3) - drawer.translate(1.0, 0.0, 0.0) - drawer.rotate(Vector3.UNIT_Z, 5.0) - drawer.vertexBuffer(vb, DrawPrimitive.TRIANGLES) - } - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoComputeStyle20.kt b/openrndr-demos/src/demo/kotlin/DemoComputeStyle20.kt deleted file mode 100644 index 9839bf6c..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoComputeStyle20.kt +++ /dev/null @@ -1,61 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.math.IntVector3 -import kotlin.math.sin - -/** - * Use a compute shader to read from a colorBuffer - * and write into a different colorBuffer. - * - * The input colorBuffer is updated on every animation frame - * with a scaling circle. - * - * Then the compute shader is executed to update every pixel - * in the output colorBuffer by reading a displaced pixel - * from the input colorBuffer. - * - * Output: 2D Image - */ -fun main() { - application { - program { - val input = renderTarget(width, height) { - colorBuffer(type = ColorType.FLOAT32) - } - val output = input.colorBuffer(0).createEquivalent() - - val cs = computeStyle { - computeTransform = """ - ivec2 id = ivec2(gl_GlobalInvocationID.xy); - ivec2 src = ivec2(id + sin(id) * p_m); - vec4 c = imageLoad(p_inputImage, src); - imageStore(p_outputImage, id, c); - """.trimIndent() - workGroupSize = IntVector3(16, 16, 1) - } - - cs.image("inputImage", input.colorBuffer(0).imageBinding(0, ImageAccess.READ)) - cs.image("outputImage", output.imageBinding(0, ImageAccess.WRITE)) - - extend { - // Update input - drawer.isolatedWithTarget(input) { - clear(ColorRGBa.TRANSPARENT) - circle(bounds.center, 100.0 + 80 * sin(seconds)) - } - - // Apply the compute shader to update output - cs.parameter("m", sin(seconds * 0.8) * 13.0 + 15.0) - cs.execute( - output.width / cs.workGroupSize.x, - output.height / cs.workGroupSize.y, - 1 - ) - - // Draw result - drawer.image(output) - } - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoContour01.kt b/openrndr-demos/src/demo/kotlin/DemoContour01.kt deleted file mode 100644 index 45693f2a..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoContour01.kt +++ /dev/null @@ -1,19 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.shape.Circle - -/** - * a simple demo that tests heavy stroke weights on tiny geometry - * - * This was made to assist in resolving https://github.com/openrndr/openrndr/issues/164 - */ -fun main() = application { - program { - val c = Circle(200.0, 200.0, 10.0).contour - extend { - drawer.strokeWeight = mouse.position.y - drawer.stroke = ColorRGBa.PINK - drawer.contour(c) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoContour02.kt b/openrndr-demos/src/demo/kotlin/DemoContour02.kt deleted file mode 100644 index 7ca4113b..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoContour02.kt +++ /dev/null @@ -1,41 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.LineJoin -import org.openrndr.math.Vector2 -import org.openrndr.shape.ShapeContour -import org.openrndr.shape.contour - -/** - * a simple demo that tests line joins - * - * This was made to assist in resolving https://github.com/openrndr/openrndr/issues/162 - */ - -fun arc(start: Vector2, end: Vector2, radius: Double): ShapeContour { - return contour { - moveTo(start) - arcTo(radius, radius, 0.0, false, false, end) - } -} - -fun main() = application { - configure { - width = 800 - height = 800 - } - program { - val center = Vector2(width / 2.0, height / 2.0) - val extra = Vector2(75.0, 75.0) - - extend { - drawer.clear(ColorRGBa.PINK) - - drawer.lineJoin = LineJoin.BEVEL - - drawer.strokeWeight = 40.0 - drawer.contour(arc(center - extra, center - extra - extra, 75.0)) - drawer.contour(arc(center, center + extra, 75.0 / 2.0)) - drawer.contour(arc(center + extra + extra, center + extra, 75.0 / 2.0)) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoContour03.kt b/openrndr-demos/src/demo/kotlin/DemoContour03.kt deleted file mode 100644 index c4f1292c..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoContour03.kt +++ /dev/null @@ -1,42 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.isolated -import org.openrndr.shape.Circle -import org.openrndr.shape.Rectangle - -fun main() = application { - configure { - width = 720 - height = 720 - } - - program { - val cs = Rectangle(0.0, 0.0, 200.0, 200.0).contour - val cc = Circle(100.0, 0.0, 100.0).contour - - extend { - drawer.fill = ColorRGBa.GRAY - drawer.stroke = ColorRGBa.PINK - drawer.isolated { - drawer.contour(cs) - drawer.translate(300.0, 0.0) - - // this should create a contour similar to the input contour - drawer.contour(cs.sampleEquidistant(4)) - drawer.contour(cs.sampleEquidistant(3)) - } - - drawer.isolated { - drawer.translate(.0, 400.0) - drawer.contour(cc) - drawer.translate(300.0, 0.0) - - drawer.contour(cc) - // this should draw a hexagon - drawer.contour(cc.sampleEquidistant(6)) - // this should draw a triangle - drawer.contour(cc.sampleEquidistant(3)) - } - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoContourIntersections01.kt b/openrndr-demos/src/demo/kotlin/DemoContourIntersections01.kt deleted file mode 100644 index ac98561f..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoContourIntersections01.kt +++ /dev/null @@ -1,37 +0,0 @@ -//import org.openrndr.application -//import org.openrndr.color.ColorRGBa -//import org.openrndr.shape.Ellipse -//import org.openrndr.shape.OrientedEllipse -//import org.openrndr.shape.intersections -// -//fun main() { -// application { -// program { -// val c1 = Ellipse(width / 2.0, height / 2.0, 200.0, 100.0).contour -// extend { -// drawer.clear(ColorRGBa.PINK) -// drawer.fill = null -// val c2 = OrientedEllipse(mouse.position, 100.0, 200.0, seconds*45.0).contour -// drawer.contour(c1) -// //drawer.contour(c2) -// val ints = intersections(c1, c2) -// -// if (ints.isEmpty()) { -// drawer.contour(c2) -// } else { -// (ints + ints.take(1)).map { it.b.contourT }.zipWithNext().forEach { -// val end = if (it.second <= it.first) it.second + 1.0 else it.second -// val sub = c2.sub(it.first, end) -// val l = sub.length -// val ta = sub.tForLength(15.0) -// val tb = sub.tForLength(l - 15.0) -// drawer.contour(sub.sub(ta, tb)) -// } -// } -// for (i in ints) { -// drawer.circle(i.position, 10.0) -// } -// } -// } -// } -//} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoContourIntersections02.kt b/openrndr-demos/src/demo/kotlin/DemoContourIntersections02.kt deleted file mode 100644 index ce9bcacc..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoContourIntersections02.kt +++ /dev/null @@ -1,44 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.math.Vector2 -import org.openrndr.shape.Circle -import org.openrndr.shape.Rectangle -import org.openrndr.shape.intersections - -fun main() = application { - program { - extend { - val circle = Circle(mouse.position, 200.0).contour - - drawer.fill = null - for (y in 50 until height step 100) { - for (x in 50 until width step 100) { - - for (ring in 0 until 10) { - val r = Rectangle.fromCenter( - Vector2(x * 1.0, y * 1.0), - 90.0 - ring * 8.0, - 90.0 - ring * 8.0 - ).contour - - val ints = intersections(circle, r) - if (ints.isEmpty()) { - drawer.stroke = ColorRGBa.GREEN - drawer.contour(r) - } else { - drawer.stroke = ColorRGBa.WHITE - ints.map { it.b.contourT }.let { it + it.take(1) }.zipWithNext().forEach { - val end = if (it.second <= it.first) it.second + 1.0 else it.second - val sub = r.sub(it.first, end) - val length = sub.length - val ta = sub.tForLength(2.0) - val tb = sub.tForLength(length - 2.0) - drawer.contour(sub.sub(ta, tb)) - } - } - } - } - } - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoContourIntersections03.kt b/openrndr-demos/src/demo/kotlin/DemoContourIntersections03.kt deleted file mode 100644 index 3ed66230..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoContourIntersections03.kt +++ /dev/null @@ -1,35 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.math.Vector2 -import org.openrndr.shape.ShapeContour -import org.openrndr.shape.intersections -import kotlin.math.PI -import kotlin.math.cos -import kotlin.math.sin - -fun main() = application { - program { - val points = 200 - extend { - val contour = ShapeContour.fromPoints( - List(points) { - val a = PI * 2 * it / points - val x = (200 + 50 * cos(a * 2)) * sin(a * 3 + sin(a)) - val y = 150 * cos(a * 2 + seconds * 0.2) - Vector2(x, y) - }, closed = true - ) - val ints = intersections(contour, contour) - drawer.run { - clear(ColorRGBa.WHITE) - translate(width * 0.5, height * 0.5) - fill = null - stroke = ColorRGBa.BLACK - contour(contour) - fill = ColorRGBa.PINK.opacify(0.3) - - circles(ints.map { it.position }, 10.0) - } - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoContourNearest01.kt b/openrndr-demos/src/demo/kotlin/DemoContourNearest01.kt deleted file mode 100644 index a18c3cbd..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoContourNearest01.kt +++ /dev/null @@ -1,54 +0,0 @@ -//import org.openrndr.application -//import org.openrndr.color.ColorRGBa -//import org.openrndr.extra.noise.simplex -//import org.openrndr.shape.OrientedEllipse -// -//fun main() { -// application { -// program { -// extend { -// drawer.clear(ColorRGBa.BLACK) -// drawer.fill = null -// -// val offset = seconds * 0.1 -// val contours = listOf( -// OrientedEllipse( -// simplex(320, offset)*width/2.0 + width/2.0, -// simplex(3120, offset)*height/2.0 + height/2.0, -// simplex(3420, offset)*50.0 + 80.0, -// simplex(7521, offset)*50.0+ 80.0, -// simplex(3212, offset)*180.0+180.0 -// ).contour, -// OrientedEllipse( -// simplex(5320, offset)*width/2.0 + width/2.0, -// simplex(73120, offset)*height/2.0 + height/2.0, -// simplex(23420, offset)*50.0 + 80.0, -// simplex(47521, offset)*50.0+ 80.0, -// simplex(33212, offset)*180.0+180.0 -// ).contour -// ) -// drawer.fill = null -// drawer.stroke = ColorRGBa.PINK -// for (contour in contours) { -// drawer.contour(contour) -// } -// -// for (j in contours.indices) { -// for (i in 0 until j) { -// val eqj = contours[j].equidistantPositions(50) -// val eqi = contours[i].equidistantPositions(50) -// -// for (p in eqj) { -// val q = contours[i].nearest(p).position -// drawer.lineSegment(p, q) -// } -// for (p in eqi) { -// val q = contours[j].nearest(p).position -// drawer.lineSegment(p, q) -// } -// } -// } -// } -// } -// } -//} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoCubemap01.kt b/openrndr-demos/src/demo/kotlin/DemoCubemap01.kt deleted file mode 100644 index dddef7b7..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoCubemap01.kt +++ /dev/null @@ -1,25 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.Session -import org.openrndr.draw.loadCubemap -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.meshgenerators.boxMesh - -fun main() = application { - program { - val cubemap = loadCubemap("demo-data/cubemaps/garage_iem.dds", null, session = Session.active) - val cube = boxMesh() - - extend(Orbital()) - extend { - drawer.shadeStyle = shadeStyle { - fragmentTransform = """ - x_fill = texture(p_cubemap, va_position); - """ - parameter("cubemap", cubemap) - } - drawer.vertexBuffer(cube, DrawPrimitive.TRIANGLES) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoCubemap02.kt b/openrndr-demos/src/demo/kotlin/DemoCubemap02.kt deleted file mode 100644 index 6850ceb4..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoCubemap02.kt +++ /dev/null @@ -1,31 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.* -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.meshgenerators.boxMesh - -fun main() = application { - program { - val cubemap1 = loadCubemap("demo-data/cubemaps/garage_iem.dds", null, session = Session.active) - val cube = boxMesh() - val cubemap2 = cubemap( - cubemap1.width, - format = cubemap1.format, - type = cubemap1.type, - levels = 2, - session = Session.active - ) - cubemap1.copyTo(cubemap2, 0, 0) - cubemap2.generateMipmaps() - - extend(Orbital()) - extend { - drawer.shadeStyle = shadeStyle { - fragmentTransform = """ - x_fill = texture(p_cubemap, va_position); - """ - parameter("cubemap", cubemap2) - } - drawer.vertexBuffer(cube, DrawPrimitive.TRIANGLES) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoCubemap03.kt b/openrndr-demos/src/demo/kotlin/DemoCubemap03.kt deleted file mode 100644 index 040dd755..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoCubemap03.kt +++ /dev/null @@ -1,39 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.* -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.meshgenerators.boxMesh - -fun main() = application { - program { - val cubemap1 = loadCubemap("demo-data/cubemaps/garage_iem.dds", null, session = Session.active) - val cube = boxMesh() - val cubemap2 = cubemap( - cubemap1.width, - format = cubemap1.format, - type = cubemap1.type, - levels = 2, - session = Session.active - ) - cubemap1.copyTo(cubemap2, 0, 0) - cubemap2.generateMipmaps() - - val cma = arrayCubemap(cubemap1.width, 10) - for (i in 0 until 1) { - cubemap1.copyTo(cma, 8) - } - - cma.generateMipmaps() - - extend(Orbital()) - extend { - drawer.shadeStyle = shadeStyle { - fragmentTransform = """ - x_fill = texture(p_cma, vec4(va_position, 8.0)); - """ - parameter("cubemap", cubemap2) - parameter("cma", cma) - } - drawer.vertexBuffer(cube, DrawPrimitive.TRIANGLES) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoDDSImage01.kt b/openrndr-demos/src/demo/kotlin/DemoDDSImage01.kt deleted file mode 100644 index aa1e0bc4..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoDDSImage01.kt +++ /dev/null @@ -1,13 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.loadImage - -fun main() = application { - program { - val image = loadImage("demo-data/images/image-001.dds") - println(image.format) - println(image.type) - extend { - drawer.image(image) - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoGeometryShader01.kt b/openrndr-demos/src/demo/kotlin/DemoGeometryShader01.kt deleted file mode 100644 index 085bbf1a..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoGeometryShader01.kt +++ /dev/null @@ -1,30 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.Shader -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.meshgenerators.boxMesh -import org.openrndr.resourceText - -fun main() = application { - program { - val vb = boxMesh() - val shader = Shader.createFromCode( - vsCode = resourceText("/shaders/gs-01.vert"), - gsCode = resourceText("/shaders/gs-01.geom"), - fsCode = resourceText("/shaders/gs-01.frag"), - name = "x" - ) - extend(Orbital()) - extend { - drawer.clear(ColorRGBa.PINK) - shader.begin() - shader.uniform("offset", mouse.position.xy0) - shader.uniform("view", drawer.view) - shader.uniform("proj", drawer.projection) - shader.uniform("model", drawer.model) - driver.drawVertexBuffer(shader, listOf(vb), DrawPrimitive.TRIANGLES, 0, vb.vertexCount) - shader.end() - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoImageLoadStore01.kt b/openrndr-demos/src/demo/kotlin/DemoImageLoadStore01.kt deleted file mode 100644 index 71b2ea50..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoImageLoadStore01.kt +++ /dev/null @@ -1,48 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.meshgenerators.planeMesh -import org.openrndr.internal.Driver -import org.openrndr.math.Vector3 - -fun main() = application { - program { - val shader = Shader.createFromCode( - vsCode = - """${Driver.instance.shaderConfiguration(ShaderType.VERTEX)} -in vec3 a_position; -in vec2 a_texCoord0; -in vec3 a_normal; -uniform mat4 projMatrix; -uniform mat4 viewMatrix; - -void main() { - gl_Position = projMatrix * vec4(a_position, 1.0); -} - """, - fsCode = """${Driver.instance.shaderConfiguration(ShaderType.FRAGMENT)} -out vec4 o_color; -layout(rgba8) uniform writeonly image2D bla; -void main() { - imageStore(bla, ivec2(30,30), vec4(1.0, 0.0, 0.0, 1.0)); - o_color = vec4(1.0); -} - """, name = "ils" - ) - val cb = colorBuffer(128, 128, type = ColorType.UINT8) - val mesh = planeMesh(Vector3.ZERO, Vector3.UNIT_X, Vector3.UNIT_Y, -Vector3.UNIT_Z, 100.0, 100.0) - - extend { - drawer.clear(ColorRGBa.PINK) - shader.begin() - shader.image("bla", 0, cb.imageBinding(0, ImageAccess.READ_WRITE)) - shader.uniform("viewMatrix", drawer.view) - shader.uniform("projMatrix", drawer.projection) - - Driver.instance.drawVertexBuffer(shader, listOf(mesh), DrawPrimitive.TRIANGLES, 0, mesh.vertexCount) - shader.end() - drawer.clear(ColorRGBa.BLACK) - drawer.image(cb) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoImageLoadStore02.kt b/openrndr-demos/src/demo/kotlin/DemoImageLoadStore02.kt deleted file mode 100644 index f7c798cf..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoImageLoadStore02.kt +++ /dev/null @@ -1,24 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* - -fun main() = application { - program { - val cb = colorBuffer(128, 128, type = ColorType.UINT8) - cb.fill(ColorRGBa.BLACK) - val ss = shadeStyle { - fragmentTransform = """ - imageStore(p_image, ivec2(30, 30), vec4(1.0, 0.0, 0.0, 1.0)); - """.trimIndent() - - image("image", cb.imageBinding(0, ImageAccess.WRITE)) - } - extend { - drawer.shadeStyle = ss - drawer.clear(ColorRGBa.PINK) - drawer.rectangle(0.0, 0.0, 100.0, 100.0) - drawer.shadeStyle = null - drawer.image(cb, 0.0, 200.0) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoImageLoadStore03.kt b/openrndr-demos/src/demo/kotlin/DemoImageLoadStore03.kt deleted file mode 100644 index d87f95ed..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoImageLoadStore03.kt +++ /dev/null @@ -1,29 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.rgb -import org.openrndr.draw.* - -fun main() = application { - program { - val cb = colorBuffer(128, 128, type = ColorType.UINT8) - val at = arrayTexture(128, 128, 32, type = ColorType.UINT8) - val vt = volumeTexture(32, 32, 32, type = ColorType.UINT8) - extend { - val ss = shadeStyle { - fragmentTransform = """ - imageStore(p_image, ivec2(30.0, 30.0), vec4(1.0, 0.0, 0.0, 1.0)); - imageStore(p_vt, ivec3(2, 2, 2), vec4(1.0, 0.0, 0.0, 1.0)); - imageStore(p_at, ivec3(2, 2, 2), vec4(1.0, 0.0, 0.0, 1.0)); - """.trimIndent() - - image("at", at.imageBinding(0, ImageAccess.READ_WRITE)) - image("image", cb.imageBinding(0, ImageAccess.READ_WRITE)) - image("vt", vt.imageBinding(0, ImageAccess.READ_WRITE)) - } - drawer.shadeStyle = ss - drawer.clear(rgb(0.1)) - drawer.fill = rgb(0.2) - drawer.rectangle(0.0, 0.0, 100.0, 100.0) - drawer.image(cb, 0.0, 200.0) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoLineCapJoin.kt b/openrndr-demos/src/demo/kotlin/DemoLineCapJoin.kt deleted file mode 100644 index b3d89527..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoLineCapJoin.kt +++ /dev/null @@ -1,52 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.LineCap -import org.openrndr.draw.LineJoin -import org.openrndr.draw.isolated -import org.openrndr.draw.loadFont -import org.openrndr.math.IntVector2 -import org.openrndr.math.Vector2 -import org.openrndr.shape.Triangle - -/** - * Test all combinations of line cap and line join by drawing - * a 3x3 grid of triangles and lines. - */ - -fun main() = application { - configure { - width = 720 - height = 720 - } - - program { - val font = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 20.0) - val pointA = Vector2(0.0, 50.0) - val pointB = Vector2(50.0, -20.0) - val pointC = Vector2(-50.0, 0.0) - val triangle = Triangle(pointA, pointB, pointC).contour - - extend { - drawer.apply { - fill = ColorRGBa.GRAY - stroke = ColorRGBa.PINK - strokeWeight = 8.0 - fontMap = font - LineCap.entries.forEachIndexed { x, cap -> - lineCap = cap - LineJoin.entries.forEachIndexed { y, join -> - lineJoin = join - val pos = IntVector2(x - 1, y - 1).vector2 * 180.0 - isolated { - translate(bounds.position(0.46, 0.46) + pos) - text("cap: ${cap.name}", -30.5, 80.5) - text("join: ${join.name}", -30.5, 100.5) - contour(triangle) - lineSegment(pointA - pointC, pointB - pointC) - } - } - } - } - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoLineDash01.kt b/openrndr-demos/src/demo/kotlin/DemoLineDash01.kt deleted file mode 100644 index 6bfd56c0..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoLineDash01.kt +++ /dev/null @@ -1,33 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.shadeStyle -import org.openrndr.math.Polar -import org.openrndr.shape.contour - -fun main() = application { - program { - val style = shadeStyle { - //fragmentTransform = "x_stroke.a *= step(0.5, fract(c_contourPosition / p_dashLen));" - fragmentTransform = - "x_stroke.a *= smoothstep(0.0, 1.0, mod(c_contourPosition, p_dashLen)) * smoothstep(p_dashLen, p_dashLen-1.0, mod(c_contourPosition, p_dashLen));" - parameter("dashLen", 20.0) - } - extend { - drawer.run { - clear(ColorRGBa.WHITE) - stroke = ColorRGBa.BLACK.opacify(0.5) - val c = contour { - moveTo(100.0, 100.0) - continueTo(100.0, 300.0) - continueTo(bounds.center + Polar(seconds * 30, 100.0).cartesian) - continueTo(500.0, 100.0) - continueTo(600.0, 100.0) - } - shadeStyle = style - contour(c) - - drawer.lineSegment(0.0, 0.0, width * 1.0, height * 1.0) - } - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoMouseCursor01.kt b/openrndr-demos/src/demo/kotlin/DemoMouseCursor01.kt deleted file mode 100644 index 55973fe4..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoMouseCursor01.kt +++ /dev/null @@ -1,21 +0,0 @@ -//import org.openrndr.CursorType -//import org.openrndr.application -// -//fun main() { -// application { -// program { -// keyboard.character.listen { -// if (it.character == 'c') { -// mouse.cursorVisible = !mouse.cursorVisible -// } -// } -// extend { -// if (mouse.position.x < width/2.0) { -// mouse.cursorType = CursorType.ARROW_CURSOR -// } else { -// mouse.cursorType = CursorType.HAND_CURSOR -// } -// } -// } -// } -//} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoPointBatch01.kt b/openrndr-demos/src/demo/kotlin/DemoPointBatch01.kt deleted file mode 100644 index 3ad0e9a6..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoPointBatch01.kt +++ /dev/null @@ -1,22 +0,0 @@ -// Drawing points using a stored batch - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.pointBatch - -fun main() = application { - program { - val storedBatch = drawer.pointBatch { - for (y in 10 until height step 20) { - for (x in 10 until width step 20) { - fill = ColorRGBa.PINK.shade(Math.random()) - point(x * 1.0, y * 1.0) - } - } - } - extend { - drawer.clear(ColorRGBa.GRAY) - drawer.points(storedBatch) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoScreenCapture01.kt b/openrndr-demos/src/demo/kotlin/DemoScreenCapture01.kt deleted file mode 100644 index 7cc6ba8e..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoScreenCapture01.kt +++ /dev/null @@ -1,42 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DepthTestPass -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.isolated -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.meshgenerators.boxMesh -import org.openrndr.ffmpeg.VideoPlayerFFMPEG -import org.openrndr.math.Vector3 - -fun main() = application { - program { - val cube = boxMesh() - val screen = VideoPlayerFFMPEG.fromScreen( - frameRate = 15.0, - imageWidth = 300, - imageHeight = 300 - ) - - screen.play() - extend { - screen.draw(drawer, true) // update the screen grabber - drawer.isolated { - clear(ColorRGBa.WHITE) - perspective(60.0, width * 1.0 / height, 0.01, 1000.0) - depthWrite = true - depthTestPass = DepthTestPass.LESS_OR_EQUAL - shadeStyle = shadeStyle { - fragmentTransform = "x_fill = texture(p_tex, vec2(1.0-va_texCoord0.x, va_texCoord0.y));" - screen.colorBuffer?.run { - parameter("tex", this) - } - } - rotate(Vector3.UNIT_Z, 90.0) - translate(0.0, 0.0, -120.0) - rotate(Vector3.UNIT_X, seconds * 10) - scale(90.0) - vertexBuffer(cube, DrawPrimitive.TRIANGLES) - } - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoShaderStorageBuffer01.kt b/openrndr-demos/src/demo/kotlin/DemoShaderStorageBuffer01.kt deleted file mode 100644 index 085037d9..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoShaderStorageBuffer01.kt +++ /dev/null @@ -1,53 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.BufferPrimitiveType -import org.openrndr.draw.shadeStyle -import org.openrndr.draw.shaderStorageBuffer -import org.openrndr.draw.shaderStorageFormat -import java.nio.ByteBuffer -import java.nio.ByteOrder - -// A demo of shaderStorageBuffer doing no useful work - -fun main() = application { - program { - - // Construct a SSB - val ssb = shaderStorageBuffer(shaderStorageFormat { - primitive("foo", BufferPrimitiveType.FLOAT32, 1000) - }) - - // A ShadeStyle that reads from and writes into an SSB - val ss = shadeStyle { - buffer("someBuffer", ssb) - fragmentTransform = """ - float a = b_someBuffer.foo[0]; - b_someBuffer.foo[1] += 2.0; - """.trimIndent() - } - - // A ByteBuffer in RAM to download the GPU data into - val bb = ByteBuffer.allocateDirect(ssb.format.size) - bb.order(ByteOrder.nativeOrder()) - - extend { - // Clear the SSB - ssb.clear() - - drawer.shadeStyle = ss - drawer.circle(100.0, 100.0, 200.0) - - // Download the SSB into RAM - bb.rewind() - ssb.read(bb) - - bb.rewind() - val f0 = bb.float - val f1 = bb.float - println(f1) - // The shade style runs for every pix el in the circle. - // The order in which the pixels are processed is not known - // Therefore the value of `f1` can vary from frame to frame, - // because we don't know how many times `+= 2.0` was executed. - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DemoTessShader01.kt b/openrndr-demos/src/demo/kotlin/DemoTessShader01.kt deleted file mode 100644 index 8d07b9cc..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoTessShader01.kt +++ /dev/null @@ -1,50 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.Shader -import org.openrndr.draw.vertexBuffer -import org.openrndr.draw.vertexFormat -import org.openrndr.math.Vector3 -import org.openrndr.resourceText - -fun main() = application { - program { - val vb = vertexBuffer(vertexFormat { - position(3) - }, 12) - val shader = Shader.Companion.createFromCode( - vsCode = resourceText("/shaders/ts-01.vert"), - tcsCode = resourceText("/shaders/ts-01.tesc"), - tesCode = resourceText("/shaders/ts-01.tese"), - fsCode = resourceText("/shaders/ts-01.frag"), - name = "x" - ) - - vb.put { - write(Vector3(0.0, 0.0, 0.0)) - write(Vector3(100.0, 0.0, 0.0)) - write(Vector3(140.0, 200.0, 0.0)) - write(Vector3(200.0, 300.0, 0.0)) - write(Vector3(0.0, 0.0, 0.0)) - write(Vector3(100.0, 0.0, 0.0)) - write(Vector3(140.0, 200.0, 0.0)) - write(Vector3(200.0, 400.0, 0.0)) - write(Vector3(0.0, 0.0, 0.0)) - write(Vector3(100.0, 0.0, 0.0)) - write(Vector3(140.0, 200.0, 0.0)) - write(Vector3(200.0, 500.0, 0.0)) - } - - extend { - drawer.clear(ColorRGBa.PINK) - shader.begin() - shader.uniform("offset", mouse.position.xy0) - shader.uniform("view", drawer.view) - shader.uniform("proj", drawer.projection) - shader.uniform("model", drawer.model) - driver.drawVertexBuffer(shader, listOf(vb), DrawPrimitive.PATCHES, 0, vb.vertexCount) - - shader.end() - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoTessShader02.kt b/openrndr-demos/src/demo/kotlin/DemoTessShader02.kt deleted file mode 100644 index b7af7a45..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoTessShader02.kt +++ /dev/null @@ -1,49 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.Shader -import org.openrndr.draw.vertexBuffer -import org.openrndr.draw.vertexFormat -import org.openrndr.resourceText -import org.openrndr.shape.Ellipse - -fun main() = application { - program { - val ellipse = Ellipse(width / 2.0, height / 2.0, 100.0, 300.0).contour - - val vb = vertexBuffer(vertexFormat { - position(3) - }, ellipse.segments.size * 4) - - val shader = Shader.createFromCode( - vsCode = resourceText("/shaders/ts-02.vert"), - tcsCode = resourceText("/shaders/ts-02.tesc"), - tesCode = resourceText("/shaders/ts-02.tese"), - gsCode = resourceText("/shaders/ts-02.geom"), - fsCode = resourceText("/shaders/ts-02.frag"), - name = "x" - ) - - vb.put { - for (segment in ellipse.segments) { - val cubic = segment.cubic - write(cubic.start.xy0) - write(cubic.control[0].xy0) - write(cubic.control[1].xy0) - write(cubic.end.xy0) - } - } - - extend { - drawer.clear(ColorRGBa.PINK) - shader.begin() - shader.uniform("offset", mouse.position.xy0) - shader.uniform("view", drawer.view) - shader.uniform("proj", drawer.projection) - shader.uniform("model", drawer.model) - shader.uniform("resolution", ((mouse.position.x / width) * 63 + 1).toInt()) - driver.drawVertexBuffer(shader, listOf(vb), DrawPrimitive.PATCHES, 0, vb.vertexCount) - shader.end() - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoTessShader03.kt b/openrndr-demos/src/demo/kotlin/DemoTessShader03.kt deleted file mode 100644 index 035efa7e..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoTessShader03.kt +++ /dev/null @@ -1,55 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.Shader -import org.openrndr.draw.vertexBuffer -import org.openrndr.draw.vertexFormat -import org.openrndr.resourceText -import org.openrndr.shape.Ellipse - -fun main() = application { - program { - - val ellipse = Ellipse(width / 2.0, height / 2.0, 100.0, 200.0).contour - - val vb = vertexBuffer(vertexFormat { - position(3) - }, ellipse.segments.size * 4) - - val shader = Shader.createFromCode( - vsCode = resourceText("/shaders/ts-03.vert"), - tcsCode = resourceText("/shaders/ts-03.tesc"), - tesCode = resourceText("/shaders/ts-03.tese"), - gsCode = resourceText("/shaders/ts-03.geom"), - fsCode = resourceText("/shaders/ts-03.frag"), - name = "x" - ) - - vb.put { - for (segment in ellipse.segments) { - val cubic = segment.cubic - write(cubic.start.xy0) - write(cubic.control[0].xy0) - write(cubic.control[1].xy0) - write(cubic.end.xy0) - } - } - - extend { - drawer.clear(ColorRGBa.PINK) - drawer.translate(width / 2.0, height / 2.0, 0.0) - drawer.rotate(seconds * 45.0) - drawer.translate(-width / 2.0, -height / 2.0, 0.0) - - shader.begin() - shader.uniform("offset", mouse.position.xy0) - shader.uniform("view", drawer.view) - shader.uniform("proj", drawer.projection) - shader.uniform("model", drawer.model) - shader.uniform("resolution", ((mouse.position.x / width) * 63 + 1).toInt()) - shader.uniform("weight", ((mouse.position.y / height) * 128 + 1)) - driver.drawVertexBuffer(shader, listOf(vb), DrawPrimitive.PATCHES, 0, vb.vertexCount) - shader.end() - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoTessShader04.kt b/openrndr-demos/src/demo/kotlin/DemoTessShader04.kt deleted file mode 100644 index aefd0ce3..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoTessShader04.kt +++ /dev/null @@ -1,67 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.meshgenerators.sphereMesh -import org.openrndr.extra.noise.uniformRing -import org.openrndr.extra.shaderphrases.preprocessedFromUrls -import org.openrndr.math.Vector3 -import org.openrndr.resourceUrl -import org.openrndr.shape.path3D -import kotlin.math.cos - -fun main() = application { - program { - extend(Orbital()) - - val path = path3D { - moveTo(Vector3.ZERO) - for (i in 0 until 100) { - continueTo(anchor + Vector3.uniformRing(0.0, 10.0), anchor + Vector3.uniformRing(0.0, 10.0)) - } - } - val vb = vertexBuffer(vertexFormat { - position(3) - }, path.segments.size * 4) - - val shader = Shader.preprocessedFromUrls( - vsUrl = resourceUrl("/shaders/ts-04.vert"), - tcsUrl = resourceUrl("/shaders/ts-04.tesc"), - tesUrl = resourceUrl("/shaders/ts-04.tese"), - gsUrl = resourceUrl("/shaders/ts-04.geom"), - fsUrl = resourceUrl("/shaders/ts-04.frag") - ) - - val mesh = sphereMesh() - extend { - val vc = vb.put { - for (segment in path.sub(0.0, cos(seconds * 0.1) * 0.5 + 0.5).segments) { - val cubic = segment.cubic - write(cubic.start) - write(cubic.control[0]) - write(cubic.control[1]) - write(cubic.end) - } - } - drawer.clear(ColorRGBa.PINK) - drawer.depthTestPass = DepthTestPass.LESS_OR_EQUAL - drawer.depthWrite = true - drawer.vertexBuffer(mesh, DrawPrimitive.TRIANGLES) - - shader.begin() - shader.uniform("offset", mouse.position.xy0) - shader.uniform("view", drawer.view) - shader.uniform("proj", drawer.projection) - shader.uniform("model", drawer.model) - shader.uniform("resolution", 32) - shader.uniform("weight", 3.0 + cos(seconds)) - shader.uniform("time", seconds * 0.0) - drawer.depthWrite = false - - driver.setState(drawer.drawStyle) - driver.drawVertexBuffer(shader, listOf(vb), DrawPrimitive.PATCHES, 0, vc) - shader.end() - drawer.fill = ColorRGBa.WHITE - } - } -} diff --git a/openrndr-demos/src/demo/kotlin/DemoVolumeTexture01.kt b/openrndr-demos/src/demo/kotlin/DemoVolumeTexture01.kt deleted file mode 100644 index 80acb087..00000000 --- a/openrndr-demos/src/demo/kotlin/DemoVolumeTexture01.kt +++ /dev/null @@ -1,22 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* - -fun main() = application { - program { - val volumeTexture = VolumeTexture.create(128, 128, 32, type = ColorType.UINT8) - val rt = renderTarget(128, 128) { - volumeTexture(volumeTexture, 0) - } - - val cb = colorBuffer(128, 128) - extend { - drawer.isolatedWithTarget(rt) { - drawer.ortho(rt) - drawer.clear(ColorRGBa.PINK) - } - volumeTexture.copyTo(cb, 0) - drawer.image(cb) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DrawerNegativeDimensions.kt b/openrndr-demos/src/demo/kotlin/DrawerNegativeDimensions.kt deleted file mode 100644 index 97cec56c..00000000 --- a/openrndr-demos/src/demo/kotlin/DrawerNegativeDimensions.kt +++ /dev/null @@ -1,42 +0,0 @@ -// Test negative widths & heights for drawing aligned rectangles. -// Also draw a circle with negative radius. -// All shapes should appear with a white 4-pixel border - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.shape.Rectangle - -fun main() = application { - program { - val margin = 5.0 - val squareSize = 100.0 - - extend { - drawer.clear(ColorRGBa.GRAY) - drawer.fill = ColorRGBa.PINK - drawer.stroke = ColorRGBa.WHITE - drawer.strokeWeight = 4.0 - - // Top Left - drawer.rectangle(margin, margin, squareSize, squareSize) - // Top Right - drawer.rectangle(width - margin, margin, -squareSize, squareSize) - // Bottom Right - drawer.rectangle(width - margin, height - margin, -squareSize, -squareSize) - // Bottom Left - drawer.rectangle(margin, height - margin, squareSize, -squareSize) - - // Circle with negative radius - drawer.circle(drawer.bounds.center, -squareSize * 2) - - // Rectangles with the bottom right corner centered in the window - drawer.rectangles(List(10) { - Rectangle( - drawer.bounds.center, - -squareSize * 2 + it * 10, - -squareSize * 2 + it * 10 - ) - }) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DrawerRectangleBatch01.kt b/openrndr-demos/src/demo/kotlin/DrawerRectangleBatch01.kt deleted file mode 100644 index b9f69e09..00000000 --- a/openrndr-demos/src/demo/kotlin/DrawerRectangleBatch01.kt +++ /dev/null @@ -1,16 +0,0 @@ -// A single rectangle - -import org.openrndr.application -import org.openrndr.color.ColorRGBa - -fun main() = application { - program { - extend { - drawer.clear(ColorRGBa.GRAY) - drawer.fill = ColorRGBa.PINK - drawer.stroke = ColorRGBa.WHITE - drawer.strokeWeight = 2.0 - drawer.rectangle(100.0, 100.0, 50.0, 50.0) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DrawerRectangleBatch02.kt b/openrndr-demos/src/demo/kotlin/DrawerRectangleBatch02.kt deleted file mode 100644 index 6bea140a..00000000 --- a/openrndr-demos/src/demo/kotlin/DrawerRectangleBatch02.kt +++ /dev/null @@ -1,29 +0,0 @@ -// Stored rectangle batches -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.rectangleBatch - -fun main() = application { - program { - val batch = drawer.rectangleBatch { - fill = ColorRGBa.PINK - stroke = ColorRGBa.WHITE - for (i in 0 until 1000) { - strokeWeight = Math.random() * 5.0 + 1.0 - val rwidth = Math.random() * 40.0 + 4.0 - val rheight = Math.random() * 40.0 + 4.0 - val x = Math.random() * width - val y = Math.random() * height - rectangle(x, y, rwidth, rheight, Math.random() * 360.0) - } - } - - extend { - drawer.clear(ColorRGBa.GRAY) - drawer.fill = ColorRGBa.PINK - drawer.stroke = ColorRGBa.WHITE - drawer.strokeWeight = 2.0 - drawer.rectangles(batch) - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/kotlin/DrawerRectangleBatch03.kt b/openrndr-demos/src/demo/kotlin/DrawerRectangleBatch03.kt deleted file mode 100644 index 11819dc6..00000000 --- a/openrndr-demos/src/demo/kotlin/DrawerRectangleBatch03.kt +++ /dev/null @@ -1,22 +0,0 @@ -// Dynamic rectangle batches - -import org.openrndr.application -import org.openrndr.color.ColorRGBa - -fun main() = application { - program { - extend { - drawer.clear(ColorRGBa.GRAY) - drawer.fill = ColorRGBa.PINK - drawer.stroke = ColorRGBa.WHITE - drawer.strokeWeight = 2.0 - drawer.rectangles { - for (y in 0 until height / 20) { - for (x in 0 until width / 20) { - rectangle(x * 20.0, y * 20.0, 10.0, 15.0, (x + y) * 10.0 + seconds * 90.0) - } - } - } - } - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/gs-01.frag b/openrndr-demos/src/demo/resources/shaders/gs-01.frag deleted file mode 100644 index 2a60498a..00000000 --- a/openrndr-demos/src/demo/resources/shaders/gs-01.frag +++ /dev/null @@ -1,8 +0,0 @@ -#version 410 core - -out vec4 o_color; - - -void main() { - o_color = vec4(1.0, 0.0, 0.0, 1.0); -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/gs-01.geom b/openrndr-demos/src/demo/resources/shaders/gs-01.geom deleted file mode 100644 index 422ee2e2..00000000 --- a/openrndr-demos/src/demo/resources/shaders/gs-01.geom +++ /dev/null @@ -1,26 +0,0 @@ -#version 410 core - -layout (triangles) in; -layout (triangle_strip, max_vertices = 3) out; - -in InVertex { - vec3 va_position; - vec3 va_normal; - vec4 v_addedProperty; -} vertices[]; - -out vec3 va_position; -out vec3 va_normal; -out vec4 v_addedProperty; - - -uniform vec3 offset; - -void main() { - int i; - for(i = 0;i < gl_in.length();i++) { - gl_Position = gl_in[i].gl_Position; - EmitVertex(); - } - EndPrimitive(); -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/gs-01.vert b/openrndr-demos/src/demo/resources/shaders/gs-01.vert deleted file mode 100644 index 6a81c839..00000000 --- a/openrndr-demos/src/demo/resources/shaders/gs-01.vert +++ /dev/null @@ -1,22 +0,0 @@ -#version 410 core - -in vec3 a_position; -in vec3 a_normal; -in vec2 a_texCoord0; - -out InVertex { - vec3 va_position; - vec3 va_normal; - vec4 v_addedProperty; -} vertexOut; - -uniform mat4 view; -uniform mat4 proj; -uniform mat4 model; - -void main() { - vertexOut.v_addedProperty = vec4(1.0, 0.0, 0.0, 1.0); - vertexOut.va_position = a_position; - vertexOut.va_normal = a_normal; - gl_Position = proj * view * model * vec4(a_position, 1.0); -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-01.frag b/openrndr-demos/src/demo/resources/shaders/ts-01.frag deleted file mode 100644 index 660dc5e4..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-01.frag +++ /dev/null @@ -1,7 +0,0 @@ -#version 410 core - -out vec4 o_color; - -void main() { - o_color = vec4(1.0, 0.0, 0.0, 1.0); -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-01.tesc b/openrndr-demos/src/demo/resources/shaders/ts-01.tesc deleted file mode 100644 index e9ca834d..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-01.tesc +++ /dev/null @@ -1,16 +0,0 @@ -#version 410 core - -layout(vertices = 4) out; // 4 points per patch - - - -in vec3 va_position[]; -out vec3 cva_position[]; - -void main() { - cva_position[gl_InvocationID] = va_position[gl_InvocationID]; - if(gl_InvocationID == 0) { // levels only need to be set once per patch - gl_TessLevelOuter[0] = 1; // we're only tessellating one line - gl_TessLevelOuter[1] = 4; // tessellate the line into 100 segments - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-01.tese b/openrndr-demos/src/demo/resources/shaders/ts-01.tese deleted file mode 100644 index bb107c62..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-01.tese +++ /dev/null @@ -1,34 +0,0 @@ - -#version 410 core - -vec3 bezier2(vec3 a, vec3 b, float t) { - return mix(a, b, t); -} -vec3 bezier3(vec3 a, vec3 b, vec3 c, float t) { - return mix(bezier2(a, b, t), bezier2(b, c, t), t); -} -vec3 bezier4(vec3 a, vec3 b, vec3 c, vec3 d, float t) { - return mix(bezier3(a, b, c, t), bezier3(b, c, d, t), t); -} - -layout(isolines) in; -in vec3 cva_position[]; - -uniform mat4 proj; -uniform mat4 view; -uniform mat4 model; - -void main() { - float t = gl_TessCoord.x; - vec3 ePos = bezier4( - cva_position[0], - cva_position[1], - cva_position[2], - cva_position[3], - - - t); - - - gl_Position = proj * view * model * vec4(ePos, 1); -} diff --git a/openrndr-demos/src/demo/resources/shaders/ts-01.vert b/openrndr-demos/src/demo/resources/shaders/ts-01.vert deleted file mode 100644 index a5f37377..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-01.vert +++ /dev/null @@ -1,15 +0,0 @@ -#version 410 core - -in vec3 a_position; - - -out vec3 va_position; - -uniform mat4 view; -uniform mat4 proj; -uniform mat4 model; - -void main() { - va_position = a_position; - gl_Position = proj * view * model * vec4(a_position, 1.0); -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-02.frag b/openrndr-demos/src/demo/resources/shaders/ts-02.frag deleted file mode 100644 index 660dc5e4..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-02.frag +++ /dev/null @@ -1,7 +0,0 @@ -#version 410 core - -out vec4 o_color; - -void main() { - o_color = vec4(1.0, 0.0, 0.0, 1.0); -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-02.geom b/openrndr-demos/src/demo/resources/shaders/ts-02.geom deleted file mode 100644 index 7c706234..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-02.geom +++ /dev/null @@ -1,21 +0,0 @@ -#version 410 core - -layout (lines) in; -layout (line_strip, max_vertices = 2) out; - - -out vec3 va_position; -out vec3 va_normal; -out vec4 v_addedProperty; - - -uniform vec3 offset; - -void main() { - int i; - for(i = 0;i < gl_in.length();i++) { - gl_Position = gl_in[i].gl_Position; - EmitVertex(); - } - EndPrimitive(); -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-02.tesc b/openrndr-demos/src/demo/resources/shaders/ts-02.tesc deleted file mode 100644 index 0dd13870..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-02.tesc +++ /dev/null @@ -1,16 +0,0 @@ -#version 410 core - -layout(vertices = 4) out; // 4 points per patch - -uniform int resolution; - -in vec3 va_position[]; -out vec3 cva_position[]; - -void main() { - cva_position[gl_InvocationID] = va_position[gl_InvocationID]; - if(gl_InvocationID == 0) { // levels only need to be set once per patch - gl_TessLevelOuter[0] = 1; // we're only tessellating one line - gl_TessLevelOuter[1] = resolution; // tessellate the line into 100 segments - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-02.tese b/openrndr-demos/src/demo/resources/shaders/ts-02.tese deleted file mode 100644 index 04a7e26f..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-02.tese +++ /dev/null @@ -1,32 +0,0 @@ - -#version 410 core - -vec3 bezier2(vec3 a, vec3 b, float t) { - return mix(a, b, t); -} -vec3 bezier3(vec3 a, vec3 b, vec3 c, float t) { - return mix(bezier2(a, b, t), bezier2(b, c, t), t); -} -vec3 bezier4(vec3 a, vec3 b, vec3 c, vec3 d, float t) { - return mix(bezier3(a, b, c, t), bezier3(b, c, d, t), t); -} - -layout(isolines) in; -in vec3 cva_position[]; - -uniform mat4 proj; -uniform mat4 view; -uniform mat4 model; - -void main() { - float t = gl_TessCoord.x; - vec3 ePos = bezier4( - cva_position[0], - cva_position[1], - cva_position[2], - cva_position[3], - t); - - - gl_Position = proj * view * model * vec4(ePos, 1); -} diff --git a/openrndr-demos/src/demo/resources/shaders/ts-02.vert b/openrndr-demos/src/demo/resources/shaders/ts-02.vert deleted file mode 100644 index a5f37377..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-02.vert +++ /dev/null @@ -1,15 +0,0 @@ -#version 410 core - -in vec3 a_position; - - -out vec3 va_position; - -uniform mat4 view; -uniform mat4 proj; -uniform mat4 model; - -void main() { - va_position = a_position; - gl_Position = proj * view * model * vec4(a_position, 1.0); -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-03.frag b/openrndr-demos/src/demo/resources/shaders/ts-03.frag deleted file mode 100644 index 5604a5ff..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-03.frag +++ /dev/null @@ -1,11 +0,0 @@ -#version 410 core - -out vec4 o_color; - -//in vec3 va_position; -//in vec3 va_normal; -//in vec4 v_addedProperty; - -void main() { - o_color = vec4(1.0, 0.0, 0.0, 1.0); -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-03.geom b/openrndr-demos/src/demo/resources/shaders/ts-03.geom deleted file mode 100644 index 3e485133..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-03.geom +++ /dev/null @@ -1,50 +0,0 @@ -#version 410 core - -layout (lines) in; -layout (triangle_strip, max_vertices = 4) out; - -in InVertex { - vec3 va_position; - vec3 va_normal; - vec4 v_addedProperty; -} vertices[]; - -out vec3 va_position; -out vec3 va_normal; -out vec4 v_addedProperty; - - -uniform vec3 offset; -in vec3 derivative[]; -in vec3 position[]; - -uniform mat4 proj; -uniform mat4 view; -uniform mat4 model; -uniform float weight; - -void main() { - mat4 pvm = proj * view * model; - - vec2 direction0 = normalize(derivative[0].xy); - vec4 perp0 = vec4(direction0.y, -direction0.x, 0.0, 0.0); - - vec2 direction1 = normalize(derivative[1].xy); - vec4 perp1 = vec4(direction1.y, -direction1.x, 0.0, 0.0); - - // output a triangle strip encoded quad - - gl_Position = pvm * vec4( vec4(position[0],1.0) + perp0 * weight); - EmitVertex(); - - gl_Position = pvm * vec4( vec4(position[0],1.0) - perp0 * weight); - EmitVertex(); - - gl_Position = pvm * vec4( vec4(position[1],1.0) + perp1 * weight); - EmitVertex(); - - gl_Position = pvm * vec4( vec4(position[1],1.0) - perp1 * weight); - EmitVertex(); - - EndPrimitive(); -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-03.tesc b/openrndr-demos/src/demo/resources/shaders/ts-03.tesc deleted file mode 100644 index 698180ce..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-03.tesc +++ /dev/null @@ -1,17 +0,0 @@ -#version 410 core - -layout(vertices = 4) out; // 4 points per patch - -uniform int resolution; - - -in vec3 va_position[]; -out vec3 cva_position[]; - -void main() { - cva_position[gl_InvocationID] = va_position[gl_InvocationID]; - if(gl_InvocationID == 0) { // levels only need to be set once per patch - gl_TessLevelOuter[0] = 1; // we're only tessellating one line - gl_TessLevelOuter[1] = resolution; // tessellate the line into 100 segments - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-03.tese b/openrndr-demos/src/demo/resources/shaders/ts-03.tese deleted file mode 100644 index 600844ff..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-03.tese +++ /dev/null @@ -1,48 +0,0 @@ -#version 410 core - -vec3 bezier2(vec3 a, vec3 b, float t) { - return mix(a, b, t); -} -vec3 bezier3(vec3 a, vec3 b, vec3 c, float t) { - return mix(bezier2(a, b, t), bezier2(b, c, t), t); -} -vec3 bezier4(vec3 a, vec3 b, vec3 c, vec3 d, float t) { - return mix(bezier3(a, b, c, t), bezier3(b, c, d, t), t); -} - -struct Vertex { - vec3 va_position; - vec3 va_normal; - vec4 v_addedProperty; -}; - -layout(isolines) in; -in vec3 cva_position[]; - -out vec3 derivative; -out vec3 position; - -uniform int resolution; - -uniform mat4 proj; -uniform mat4 view; -uniform mat4 model; - -void main() { - float t = gl_TessCoord.x; - vec3 ePos = bezier4( - cva_position[0], - cva_position[1], - cva_position[2], - cva_position[3], - t); - - // calculate derivative using Hodograph - derivative = bezier3(cva_position[1] - cva_position[0], cva_position[2]-cva_position[1], cva_position[3]-cva_position[2], t); - - // output model space positions - position = ePos; - - float r = resolution + 1.0; - //gl_Position = proj * view * model * vec4(ePos, 1); -} diff --git a/openrndr-demos/src/demo/resources/shaders/ts-03.vert b/openrndr-demos/src/demo/resources/shaders/ts-03.vert deleted file mode 100644 index 44f14186..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-03.vert +++ /dev/null @@ -1,16 +0,0 @@ -#version 410 core - -in vec3 a_position; - - -out vec3 va_position; - -uniform mat4 view; -uniform mat4 proj; -uniform mat4 model; - -void main() { - va_position = a_position; - gl_Position = proj * view * model * vec4(a_position, 1.0); - -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-04.frag b/openrndr-demos/src/demo/resources/shaders/ts-04.frag deleted file mode 100644 index 5604a5ff..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-04.frag +++ /dev/null @@ -1,11 +0,0 @@ -#version 410 core - -out vec4 o_color; - -//in vec3 va_position; -//in vec3 va_normal; -//in vec4 v_addedProperty; - -void main() { - o_color = vec4(1.0, 0.0, 0.0, 1.0); -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-04.geom b/openrndr-demos/src/demo/resources/shaders/ts-04.geom deleted file mode 100644 index 820f504f..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-04.geom +++ /dev/null @@ -1,77 +0,0 @@ -#version 410 core - -layout (lines) in; -layout (triangle_strip, max_vertices = 4) out; -// -//in InVertex { -// vec3 va_position; -// vec3 va_normal; -// vec4 v_addedProperty; -//} vertices[]; - -out vec3 va_position; -out vec3 va_normal; -out vec4 v_addedProperty; - -uniform vec3 offset; -in vec3 derivative[]; -in vec3 position[]; - -uniform mat4 proj; -uniform mat4 view; -uniform mat4 model; -uniform float weight; - - - - -void main() { - - mat4 pvm = proj * view * model; - - // vec2 direction0 = normalize(derivative[0].xy); - // vec4 perp0 = vec4(direction0.y, -direction0.x, 0.0, 0.0); - // - // vec2 direction1 = normalize(derivative[1].xy); - // vec4 perp1 = vec4(direction1.y, -direction1.x, 0.0, 0.0); - - vec4 p00 = (pvm * vec4(position[0], 1.0)); - //p00 /= p00.w; - vec4 p01 = (pvm * vec4(position[0] + derivative[0], 1.0)); - //p01 /= p01.w; - vec4 p10 = (pvm * vec4(position[1], 1.0)); - //p10 /= p10.w; - vec4 p11 = (pvm * vec4(position[1] + derivative[1], 1.0)); - //p11 /= p11.w; - - vec2 direction0 = normalize(p01.xy - p00.xy); - vec2 direction1 = normalize(p11.xy - p10.xy); - vec4 perp0 = vec4(direction0.y, -direction0.x, 0.0, 0.0); - vec4 perp1 = vec4(direction1.y, -direction1.x, 0.0, 0.0); - -// v_addedProperty = vertices[0].v_addedProperty; -// va_normal = vertices[0].va_normal; -// va_position = vertices[0].va_position; - gl_Position = pvm * vec4(vec4(position[0], 1.0)) + perp0 * weight * 0.01; - EmitVertex(); - -// v_addedProperty = vertices[0].v_addedProperty; -// va_normal = vertices[0].va_normal; -// va_position = vertices[0].va_position; - gl_Position = pvm * vec4(vec4(position[0], 1.0)) - perp0 * weight * 0.01; - EmitVertex(); - -// v_addedProperty = vertices[1].v_addedProperty; -// va_normal = vertices[1].va_normal; -// va_position = vertices[1].va_position; - gl_Position = pvm * vec4(vec4(position[1], 1.0)) + perp1 * weight * 0.01; - EmitVertex(); - -// v_addedProperty = vertices[1].v_addedProperty; -// va_normal = vertices[1].va_normal; -// va_position = vertices[1].va_position; - gl_Position = pvm * vec4(vec4(position[1], 1.0)) - perp1 * weight * 0.01; - EmitVertex(); - - EndPrimitive(); -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-04.tesc b/openrndr-demos/src/demo/resources/shaders/ts-04.tesc deleted file mode 100644 index 698180ce..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-04.tesc +++ /dev/null @@ -1,17 +0,0 @@ -#version 410 core - -layout(vertices = 4) out; // 4 points per patch - -uniform int resolution; - - -in vec3 va_position[]; -out vec3 cva_position[]; - -void main() { - cva_position[gl_InvocationID] = va_position[gl_InvocationID]; - if(gl_InvocationID == 0) { // levels only need to be set once per patch - gl_TessLevelOuter[0] = 1; // we're only tessellating one line - gl_TessLevelOuter[1] = resolution; // tessellate the line into 100 segments - } -} \ No newline at end of file diff --git a/openrndr-demos/src/demo/resources/shaders/ts-04.tese b/openrndr-demos/src/demo/resources/shaders/ts-04.tese deleted file mode 100644 index 600844ff..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-04.tese +++ /dev/null @@ -1,48 +0,0 @@ -#version 410 core - -vec3 bezier2(vec3 a, vec3 b, float t) { - return mix(a, b, t); -} -vec3 bezier3(vec3 a, vec3 b, vec3 c, float t) { - return mix(bezier2(a, b, t), bezier2(b, c, t), t); -} -vec3 bezier4(vec3 a, vec3 b, vec3 c, vec3 d, float t) { - return mix(bezier3(a, b, c, t), bezier3(b, c, d, t), t); -} - -struct Vertex { - vec3 va_position; - vec3 va_normal; - vec4 v_addedProperty; -}; - -layout(isolines) in; -in vec3 cva_position[]; - -out vec3 derivative; -out vec3 position; - -uniform int resolution; - -uniform mat4 proj; -uniform mat4 view; -uniform mat4 model; - -void main() { - float t = gl_TessCoord.x; - vec3 ePos = bezier4( - cva_position[0], - cva_position[1], - cva_position[2], - cva_position[3], - t); - - // calculate derivative using Hodograph - derivative = bezier3(cva_position[1] - cva_position[0], cva_position[2]-cva_position[1], cva_position[3]-cva_position[2], t); - - // output model space positions - position = ePos; - - float r = resolution + 1.0; - //gl_Position = proj * view * model * vec4(ePos, 1); -} diff --git a/openrndr-demos/src/demo/resources/shaders/ts-04.vert b/openrndr-demos/src/demo/resources/shaders/ts-04.vert deleted file mode 100644 index 79a8af65..00000000 --- a/openrndr-demos/src/demo/resources/shaders/ts-04.vert +++ /dev/null @@ -1,16 +0,0 @@ -#version 410 core - -#pragma import org.openrndr.extra.noise.phrases.SimplexKt.phraseSimplex3; - -in vec3 a_position; -out vec3 va_position; - -uniform mat4 view; -uniform mat4 proj; -uniform mat4 model; - -uniform float time; - -void main() { - va_position = a_position; //4.0* vec3(simplex31(a_position + vec3(time, time, -time) ), 4.0*simplex31(a_position.zxy + vec3(-time, time, time)), 4.0*simplex31(a_position.yzx + vec3(time, -time, time))) ; -} \ No newline at end of file diff --git a/orx-camera/README.md b/orx-camera/README.md deleted file mode 100644 index 677cc39d..00000000 --- a/orx-camera/README.md +++ /dev/null @@ -1,160 +0,0 @@ -# orx-camera - -2D and 3D cameras controllable via mouse and keyboard. - -## Usage - -```kotlin -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.extra.camera.AxisHelper -import org.openrndr.extra.camera.GridHelper -import org.openrndr.extra.camera.OrbitalCamera -import org.openrndr.extra.camera.OrbitalControls -import org.openrndr.extra.meshgenerators.boxMesh -import org.openrndr.extra.meshgenerators.sphereMesh -import org.openrndr.math.Vector3 - -fun main() = application { - program { - val camera = OrbitalCamera( - Vector3.UNIT_Z * 90.0, Vector3.ZERO, 90.0, 0.1, 5000.0 - ) - val controls = OrbitalControls(camera, keySpeed = 10.0) - - val sphere = sphereMesh(radius = 25.0) - val cube = boxMesh(20.0, 20.0, 5.0, 5, 5, 2) - - extend(camera) - extend(AxisHelper()) // shows XYZ axes as RGB lines - extend(GridHelper(100)) // debug ground plane - extend(controls) // adds both mouse and keyboard bindings - extend { - drawer.vertexBuffer(sphere, DrawPrimitive.LINE_LOOP) - drawer.vertexBuffer(cube, DrawPrimitive.LINE_LOOP) - drawer.stroke = ColorRGBa.WHITE - drawer.fill = null - repeat(10) { - drawer.translate(0.0, 0.0, 10.0) - // 2D primitives are not optimized for 3D and can - // occlude each other - drawer.circle(0.0, 0.0, 50.0) - } - } - } -} -``` - -### Keybindings - -* `w` - move forwards (+z) -* `s` - move backwards (-z) -* `Left` or `a` - strafe left (-x) -* `Right` or `d` - strafe right (+x) -* `Up` or `e` - move up (+y) -* `Down` or `q` - move up (-y) -* `Page Up` - zoom in -* `Page Down` - zoom out - -## Demos -### DemoCamera2D01 - -#### Camera2D demo - -click and drag the mouse for panning, use the mouse wheel for zooming - -![DemoCamera2D01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-camera/images/DemoCamera2D01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCamera2D01.kt) - -### DemoCamera2D02 - -#### Camera2D demo with static elements - -An approach for having certain elements not affected by the camera. -See DemoCamera2DManual01.kt for a new and simpler approach - -![DemoCamera2D02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-camera/images/DemoCamera2D02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCamera2D02.kt) - -### DemoCamera2DManual01 - -Demonstrates how to use `Camera2DManual` to have -some elements affected by an interactive 2D camera combined with -other elements not affected by it. - -In this example both PINK circles can be dragged, scaled and rotated -while the white circle in the middle is static. - -![DemoCamera2DManual01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-camera/images/DemoCamera2DManual01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCamera2DManual01.kt) - -### DemoCamera2DManual02 - -Demonstrate the use of `Camera2DManual` to independently translate, scale and rotate one contour -in a collection. - -When the mouse is clicked, the active contour is transformed using the camera view matrix, -then the camera is reset to its default state and whatever shape is under the mouse becomes -the new active contour. - -As the mouse is dragged or its wheel scrolled, the camera is updated, affecting -how the active contour is rendered. - -![DemoCamera2DManual02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-camera/images/DemoCamera2DManual02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCamera2DManual02.kt) - -### DemoOrbital01 - -Demonstrate the use of `Orbital`, an interactive 3D camera -that can be controlled with a mouse and a keyboard. - -![DemoOrbital01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-camera/images/DemoOrbital01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoOrbital01.kt) - -### DemoOrbitalCamera01 - -Demonstrate the use of `OrbitalCamera`, `OrbitalControls`, `AxisHelper` and `GridHelper`. - -Press the `t` key to toggle camera interaction, or `r` to reset the camera to its defaults. - -![DemoOrbitalCamera01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-camera/images/DemoOrbitalCamera01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoOrbitalCamera01.kt) - -### DemoOrbitalManual01 - -Demonstrate the use of an orbital camera to render a sphere and a cube in 3D space as wireframe meshes, positioned -and rendered independently using the camera's isolated drawing state. A stationary pink circle is also drawn in the -center of the scene. - -Functionality: -- Initializes a sphere mesh and a cube mesh with predefined dimensions. -- Spawns an orbital camera, initially positioned away from the origin, to allow for focused rendering. -- Renders 3D wireframe shapes (sphere and cube) using the camera's isolated perspective. -- Draws a static 2D pink circle overlay at the window center. - -![DemoOrbitalManual01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-camera/images/DemoOrbitalManual01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoOrbitalManual01.kt) - -### DemoParametricOrbital01 - -Demonstrates the use of a `ParametricOrbital` camera. -This 3D camera can't be directly interacted with a mouse or a keyboard, -but only via a GUI (or via code). - -The GUI state is saved when closing the program and loaded -when running it again. - -The GUI also allows randomizing, loading and saving -its state to a file via the top buttons it displays. - -![DemoParametricOrbital01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-camera/images/DemoParametricOrbital01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoParametricOrbital01.kt) diff --git a/orx-camera/build.gradle.kts b/orx-camera/build.gradle.kts deleted file mode 100644 index 5578f0f9..00000000 --- a/orx-camera/build.gradle.kts +++ /dev/null @@ -1,35 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") -} - -kotlin { - jvm { - testRuns["test"].executionTask { - useJUnitPlatform { - } - } - } - - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - implementation(project(":orx-parameters")) - implementation(project(":orx-shader-phrases")) - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - } - } - - @Suppress("UNUSED_VARIABLE") - val jvmDemo by getting { - dependencies { - implementation(project(":orx-camera")) - implementation(project(":orx-mesh-generators")) - implementation(project(":orx-jvm:orx-gui")) - } - } - } -} diff --git a/orx-camera/src/commonMain/kotlin/AxisHelper.kt b/orx-camera/src/commonMain/kotlin/AxisHelper.kt deleted file mode 100644 index 982ab992..00000000 --- a/orx-camera/src/commonMain/kotlin/AxisHelper.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.openrndr.extra.camera - -import org.openrndr.Extension -import org.openrndr.Program -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.math.Matrix33 -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector3 - - -@Suppress("unused") -class AxisHelper(var size: Int = 80) : Extension { - override var enabled: Boolean = true - - val fbo = renderTarget(size, size) { - colorBuffer() - depthBuffer() - } - val side = size.toDouble() - val axisLength = size / 2.0 - val planeLength = axisLength + 10.0 - - override fun afterDraw(drawer: Drawer, program: Program) { - draw(drawer) - } - - fun draw(drawer: Drawer) { - val viewMatrix = drawer.view - val x = drawer.width - (size + 5.0) - val y = 5.0 - - drawer.isolatedWithTarget(fbo) { - drawer.defaults() - drawer.ortho(fbo) - - drawer.clear(ColorRGBa.TRANSPARENT) - drawer.stroke = null - drawer.fill = ColorRGBa.PINK.opacify(0.7) - drawer.circle(axisLength, axisLength, axisLength) - - drawer.ortho(-planeLength, planeLength, -planeLength, planeLength, -planeLength, planeLength) - drawer.view = getRotation(viewMatrix) - drawer.strokeWeight = 0.6 - - drawer.fill = ColorRGBa.RED - drawer.stroke = ColorRGBa.RED - drawer.lineSegment(Vector3.ZERO, Vector3.UNIT_X * axisLength) - - drawer.fill = ColorRGBa.GREEN - drawer.stroke = ColorRGBa.GREEN - drawer.lineSegment(Vector3.ZERO, Vector3.UNIT_Y * axisLength) - - drawer.fill = ColorRGBa.BLUE - drawer.stroke = ColorRGBa.BLUE - drawer.lineSegment(Vector3.ZERO, Vector3.UNIT_Z * axisLength) - } - - drawer.isolated { - drawer.defaults() - - drawer.image(fbo.colorBuffer(0), x, y, side, side) - } - } -} - -internal fun getRotation(mat: Matrix44): Matrix44 { - val mat3 = mat.matrix33 // without translation - val c0 = mat3[0].length - val c1 = mat3[1].length - val c2 = mat3[2].length - - return Matrix33( - mat3.c0r0 / c0, mat3.c1r0 / c1, mat3.c2r0 / c2, - mat3.c0r1 / c0, mat3.c1r1 / c1, mat3.c2r1 / c2, - mat3.c0r2 / c0, mat3.c1r2 / c1, mat3.c2r2 / c2 - ).matrix44 -} \ No newline at end of file diff --git a/orx-camera/src/commonMain/kotlin/Camera2D.kt b/orx-camera/src/commonMain/kotlin/Camera2D.kt deleted file mode 100644 index 1c868fc1..00000000 --- a/orx-camera/src/commonMain/kotlin/Camera2D.kt +++ /dev/null @@ -1,234 +0,0 @@ -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 -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 -import org.openrndr.math.transforms.buildTransform -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.contract - -/** - * The [Camera2D] extension enables panning, rotating, and zooming the view - * with the mouse: - * - left click and drag to **pan** - * - right click and drag to **rotate** - * - use the mouse wheel to **zoom** in and out - * - * Usage: `extend(Camera2D())` - */ -class Camera2D : Extension, ChangeEvents { - override var enabled = true - - 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() - - private var dirty = true - set(value) { - if (value && !field) { - changed.trigger(Unit) - program.window.requestDraw() - } - field = value - } - 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`. - */ - @OptIn(ExperimentalContracts::class) - fun isolated(function: Drawer.() -> Unit) { - contract { - callsInPlace(function, kotlin.contracts.InvocationKind.EXACTLY_ONCE) - } - program.drawer.isolated { - program.drawer.ortho(RenderTarget.active) - - program.drawer.view = this@Camera2D.view - program.drawer.function() - } - } - - /** - * Reinitialize the camera to its default state, where no transformations - * (such as rotation, translation, or scaling) are applied. - */ - var defaults = { - view = Matrix44.IDENTITY - rotationCenter = Vector2.ZERO - } - - /** - * 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) - } * view - } - - /** - * 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, factor, 1.0) - 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 - } - - /** - * 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 setupControls(mouse: MouseEvents, keyboard: KeyEvents) { - if (!controlInitialized) { - controls(mouse, keyboard) - } - controlInitialized = true - } - - override fun setup(program: Program) { - this.program = program - if (!controlInitialized) { - setupControls(program.mouse, program.keyboard) - } - defaults() - } - - override fun beforeDraw(drawer: Drawer, program: Program) { - drawer.pushTransforms() - drawer.ortho(RenderTarget.active) - drawer.view = view - } - - override fun afterDraw(drawer: Drawer, program: Program) { - dirty = false - drawer.popTransforms() - } -} - -/** - * Creates and sets up a custom-configured Camera2D instance within a 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(configure: Camera2D.() -> Unit = { }): Camera2D { - val camera = Camera2D() - camera.configure() - camera.setup(this) - return camera -} diff --git a/orx-camera/src/commonMain/kotlin/ChangeEvents.kt b/orx-camera/src/commonMain/kotlin/ChangeEvents.kt deleted file mode 100644 index 2f5962bf..00000000 --- a/orx-camera/src/commonMain/kotlin/ChangeEvents.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.openrndr.extra.camera - -import org.openrndr.events.Event - -interface ChangeEvents { - val changed : Event - val hasChanged: Boolean -} \ No newline at end of file diff --git a/orx-camera/src/commonMain/kotlin/GridHelper.kt b/orx-camera/src/commonMain/kotlin/GridHelper.kt deleted file mode 100644 index 754aacd5..00000000 --- a/orx-camera/src/commonMain/kotlin/GridHelper.kt +++ /dev/null @@ -1,56 +0,0 @@ -package org.openrndr.extra.camera - -import org.openrndr.Extension -import org.openrndr.Program -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.math.Vector3 - -@Suppress("unused") -class GridHelper(val size: Int = 10, val divisions: Int = 10) : Extension { - override var enabled: Boolean = true - private val step = size / divisions.toDouble() - - private val grid = vertexBuffer( - vertexFormat { - position(3) - } - , 4 * (size * divisions + 1)).apply { - put { - val halfSize = size / 2.0 - var k = -halfSize - - for(i in 0 until divisions + 1) { - write(Vector3(-halfSize, 0.0, k)) - write(Vector3(halfSize, 0.0, k)) - - write(Vector3(k, 0.0, -halfSize)) - write(Vector3(k, 0.0, halfSize)) - - k += step - } - } - } - - override fun beforeDraw(drawer: Drawer, program: Program) { - draw(drawer) - } - - fun draw(drawer: Drawer) { - drawer.isolated { - drawer.fill = ColorRGBa.WHITE - drawer.stroke = ColorRGBa.WHITE - drawer.vertexBuffer(grid, DrawPrimitive.LINES) - - // Axis cross - drawer.stroke = ColorRGBa.RED - drawer.lineSegment(Vector3.ZERO, Vector3(step, 0.0, 0.0)) - - drawer.stroke = ColorRGBa.GREEN - drawer.lineSegment(Vector3.ZERO, Vector3(0.0, step, 0.0)) - - drawer.stroke = ColorRGBa.BLUE - drawer.lineSegment(Vector3.ZERO, Vector3(0.0, 0.0, step)) - } - } -} \ No newline at end of file diff --git a/orx-camera/src/commonMain/kotlin/Orbital.kt b/orx-camera/src/commonMain/kotlin/Orbital.kt deleted file mode 100644 index 36b97f97..00000000 --- a/orx-camera/src/commonMain/kotlin/Orbital.kt +++ /dev/null @@ -1,67 +0,0 @@ -package org.openrndr.extra.camera - -import org.openrndr.Extension -import org.openrndr.Program -import org.openrndr.draw.Drawer -import org.openrndr.events.Event -import org.openrndr.math.Vector3 -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract - -/** - * Extension that provides orbital camera view and controls. - */ -class Orbital : Extension, ChangeEvents { - override var enabled: Boolean = true - - override val changed = Event() - override val hasChanged: Boolean - get() { - return camera.hasChanged - } - - - var eye = Vector3.UNIT_Z * 10.0 - var lookAt = Vector3.ZERO - var near = 0.1 - var far = 1000.0 - var fov = 90.0 - var userInteraction = true - var keySpeed = 1.0 - var projectionType = ProjectionType.PERSPECTIVE - - /** - * Damping factor for camera motion, set to 0 for no damping - */ - var dampingFactor = 0.05 - - val camera by lazy { - OrbitalCamera(eye, lookAt, fov, near, far, projectionType).apply { - dampingFactor = this@Orbital.dampingFactor - this.changed.listen(this@Orbital.changed) - } - } - val controls by lazy { OrbitalControls(camera, userInteraction, keySpeed) } - - override fun setup(program: Program) { - camera.setup(program) - controls.setup(program) - } - - override fun beforeDraw(drawer: Drawer, program: Program) { - camera.beforeDraw(drawer, program) - } - - override fun afterDraw(drawer: Drawer, program: Program) { - camera.afterDraw(drawer, program) - } - - @OptIn(ExperimentalContracts::class) - fun isolated(drawFunction: Drawer.() -> Unit) { - contract { - callsInPlace(drawFunction, InvocationKind.EXACTLY_ONCE) - } - camera.isolated(camera.program.drawer, drawFunction) - } -} \ No newline at end of file diff --git a/orx-camera/src/commonMain/kotlin/OrbitalCamera.kt b/orx-camera/src/commonMain/kotlin/OrbitalCamera.kt deleted file mode 100644 index 93e698a1..00000000 --- a/orx-camera/src/commonMain/kotlin/OrbitalCamera.kt +++ /dev/null @@ -1,459 +0,0 @@ -package org.openrndr.extra.camera - -import org.openrndr.Extension -import org.openrndr.Program -import org.openrndr.draw.DepthTestPass -import org.openrndr.draw.Drawer -import org.openrndr.events.Event -import org.openrndr.math.Matrix44 -import org.openrndr.math.Spherical -import org.openrndr.math.Vector3 -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract -import kotlin.math.abs -import kotlin.math.max -import kotlin.math.pow -import org.openrndr.math.transforms.lookAt as lookAt_ - -enum class ProjectionType { - PERSPECTIVE, - ORTHOGONAL -} - - -class OrbitalCamera( - eye: Vector3 = Vector3.ZERO, - lookAt: Vector3 = Vector3.UNIT_Z, - var fov: Double = 90.0, - var near: Double = 0.1, - var far: Double = 1000.0, - var projectionType: ProjectionType = ProjectionType.PERSPECTIVE -) : Extension, ChangeEvents { - - internal lateinit var program: Program - override val changed = Event() - - override val hasChanged: Boolean - get() = dirty - - - // current position in spherical coordinates - var spherical = Spherical.fromVector(eye) - private set - var lookAt = lookAt - private set - - var depthTest = true - - var magnitude = 100.0 - var magnitudeEnd = magnitude - - private var sphericalEnd = Spherical.fromVector(eye) - private var lookAtEnd = lookAt - private var dirty: Boolean = true - set(value) { - if (value && !field) { - changed.trigger(Unit) - } - field = value - } - private var lastSeconds: Double = -1.0 - - var fovEnd = fov - - var dampingFactor = 0.05 - var zoomSpeed = 1.0 - - var orthoNear = -1000.0 - var orthoFar = 1000.0 - - // Defaults - val eyeDefault = eye.copy() - val lookAtDefault = lookAt.copy() - val fovDefault = fov - val magnitudeDefault = magnitude - - /** - * Reinitialize the camera to its initial state. - * - * @param instant whether the rotation is applied immediately; if false, it interpolates over time (default is false) - */ - fun defaults(instant: Boolean = false) { - panTo(lookAtDefault, instant) - rotateTo(eyeDefault, instant) - zoomTo(fovDefault, instant) - scaleTo(magnitudeDefault, instant) - } - - /** - * Sets the view for the orbital camera by updating the look-at position, spherical coordinates, - * and field of view (FOV). This method initializes both the target and current states of these properties. - * - * @param lookAt the target position the camera should look at, represented as a 3D vector - * @param spherical the spherical coordinates defining the camera's orientation - * @param fov the field of view (in degrees) for the camera - */ - fun setView(lookAt: Vector3, spherical: Spherical, fov: Double) { - this.lookAt = lookAt - this.lookAtEnd = lookAt - this.spherical = spherical - this.sphericalEnd = spherical - this.fov = fov - this.fovEnd = fov - } - - - /** - * Rotates the orbital camera by the specified angles in the horizontal and vertical directions. - * The rotation can be applied instantly or smoothly interpolated over time. - * - * @param degreesX the rotation angle in degrees around the horizontal axis (theta) - * @param degreesY the rotation angle in degrees around the vertical axis (phi) - * @param instant whether the rotation is applied immediately; if false, it interpolates over time (default is false) - */ - fun rotate(degreesX: Double, degreesY: Double, instant: Boolean = false) { - sphericalEnd += Spherical(degreesX, degreesY, 0.0) - sphericalEnd = sphericalEnd.makeSafe() - if (instant) { - spherical = sphericalEnd - } - dirty = true - } - - /** - * Rotates the camera to the specified spherical angles. The rotation can occur instantly or - * smoothly over time based on the `instant` parameter. - * - * @param degreesX the target horizontal rotation angle (theta) in degrees - * @param degreesY the target vertical rotation angle (phi) in degrees - * @param instant whether the rotation should be applied immediately (default is `false`) - */ - fun rotateTo(degreesX: Double, degreesY: Double, instant: Boolean = false) { - sphericalEnd = sphericalEnd.copy(theta = degreesX, phi = degreesY) - sphericalEnd = sphericalEnd.makeSafe() - - if (instant) { - spherical = sphericalEnd - } - dirty = true - } - - /** - * Rotates the orbital camera to the specified position defined by the `eye` vector. - * The rotation can either occur instantly or smoothly interpolated over time, - * depending on the `instant` parameter. - * - * @param eye the target position to rotate the camera to, represented as a 3D vector - * @param instant whether the rotation should be applied immediately (default is `false`) - */ - fun rotateTo(eye: Vector3, instant: Boolean = false) { - sphericalEnd = Spherical.fromVector(eye) - sphericalEnd = sphericalEnd.makeSafe() - if (instant) { - spherical = sphericalEnd - } - dirty = true - } - - /** - * Zooms the camera in by decreasing the distance to the target. The zoom is based on - * an exponential scale factor determined by the `zoomSpeed` field. If the `instant` - * parameter is set to `true`, the zoom effect is applied immediately; otherwise, it - * will interpolate the change over time. - * - * @param instant whether the zoom-in effect should occur instantly (default is `false`) - */ - fun dollyIn(instant: Boolean = false) { - val zoomScale = pow(0.95, zoomSpeed) - dolly(sphericalEnd.radius * zoomScale - sphericalEnd.radius, instant) - } - - /** - * Zooms the camera out by increasing the distance to the target. The zoom operation - * is based on an exponential scale factor determined by the `zoomSpeed` field. - * - * @param instant whether the zoom-out effect should occur instantly (default is `false`) - */ - fun dollyOut(instant: Boolean = false) { - val zoomScale = pow(0.95, zoomSpeed) - dolly(sphericalEnd.radius / zoomScale - sphericalEnd.radius, instant) - } - - /** - * Adjusts the camera's distance from the target by the specified amount. - * The change in distance is applied immediately if `instant` is set to `true`, - * otherwise it will be interpolated over time with smoothing. - * - * @param distance the amount to adjust the camera's distance by - * @param instant whether the adjustment should be applied immediately (default is `false`) - */ - fun dolly(distance: Double, instant: Boolean = false) { - sphericalEnd += Spherical(0.0, 0.0, distance) - if (instant) { - spherical = sphericalEnd - } - dirty = true - } - - fun pan(x: Double, y: Double, z: Double, instant: Boolean = false) { - val view = viewMatrix() - val xColumn = Vector3(view.c0r0, view.c1r0, view.c2r0) * x - val yColumn = Vector3(view.c0r1, view.c1r1, view.c2r1) * y - val zColumn = Vector3(view.c0r2, view.c1r2, view.c2r2) * z - lookAtEnd += xColumn + yColumn + zColumn - if (instant) { - lookAt = lookAtEnd - } - dirty = true - } - - - /** - * Smoothly pans the camera to a specified target position. If the `instant` parameter is set - * to `true`, the panning occurs immediately; otherwise, it will be interpolated over time. - * - * @param target the target position to pan the camera to, represented as a 3D vector - * @param instant whether the panning should occur instantly (default is `false`) - */ - fun panTo(target: Vector3, instant: Boolean = false) { - lookAtEnd = target - if (instant) { - lookAt = lookAtEnd - } - dirty = true - } - - /** - * Adjusts the camera's distance (radius) to the specified value. If the `instant` parameter - * is set to true, the distance change is applied immediately; otherwise, it will be interpolated - * over time during updates. - * - * @param distance the target distance (radius) that the camera should move to - * @param instant whether the distance adjustment should occur instantly (default is `false`) - */ - fun dollyTo(distance: Double, instant: Boolean = false) { - sphericalEnd = sphericalEnd.copy(radius = distance) - if (instant) { - spherical = sphericalEnd - } - dirty = true - } - - /** - * Adjusts the magnitude of the orbital camera by the specified scale factor. - * If the `instant` parameter is set to true, the adjustment is applied immediately; - * otherwise, it will be interpolated over time during updates. - * - * @param scale the amount by which to adjust the camera's magnitude - * @param instant whether the scale adjustment should be applied instantly (default is `false`) - */ - fun scale(scale: Double, instant: Boolean = false) { - magnitudeEnd += scale - if (instant) { - magnitude = magnitudeEnd - } - dirty = true - } - - /** - * Adjusts the camera's scaling factor to the specified value. The scaling can either - * be applied instantly or interpolated over time during updates. - * - * @param scale the target scaling factor for the camera - * @param instant whether the scaling should be applied instantly (default is `false`) - */ - - fun scaleTo(scale: Double, instant: Boolean = false) { - magnitudeEnd = scale - if (instant) { - magnitude = magnitudeEnd - } - dirty = true - } - - /** - * Adjusts the camera's field of view (FOV) by the specified number of degrees. The transition can either - * happen instantly or be interpolated over time during updates. - * - * @param degrees the number of degrees to adjust the field of view by - * @param instant whether the adjustment should occur instantly (default is `false`) - */ - fun zoom(degrees: Double, instant: Boolean = false) { - fovEnd += degrees - if (instant) { - fov = fovEnd - } - dirty = true - } - - /** - * Adjusts the camera's field of view (FOV) to the specified number of degrees. If the `instant` - * parameter is set to `true`, the FOV immediately transitions to the specified value; otherwise, - * it will be interpolated over time during updates. - * - * @param degrees the target field of view (in degrees) for the camera - * @param instant whether the transition to the target FOV should occur instantly (default is `false`) - */ - fun zoomTo(degrees: Double, instant: Boolean = false) { - fovEnd = degrees - if (instant) { - fov = fovEnd - } - dirty = true - } - - /** - * Updates the orbital camera state by iteratively applying updates to the camera's parameters - * based on a fixed time step. The method ensures smooth interpolation of the camera properties - * (e.g., position, orientation) over a specified time delta. - * - * @param timeDelta the time elapsed for which the camera state should be updated, in seconds - */ - fun update(timeDelta: Double) { - if (!dirty) return - dirty = false - - val stepSize = 1.0/60.0 - val steps = max(timeDelta/stepSize, 1.0).toInt() - for (step in 0 until steps) { - updateStep(stepSize) - } - } - - /** - * Updates the camera position, orientation, and view properties such as spherical coordinates, - * look-at point, field of view, and magnitude based on damping factors and time delta. - * - * @param timeDelta the time step used to update the interpolation of camera parameters - */ - fun updateStep(timeDelta: Double) { - - val dampingFactor = if (dampingFactor > 0.0) { - dampingFactor * timeDelta / 0.0060 - } else 1.0 - val sphericalDelta = sphericalEnd - spherical - val lookAtDelta = lookAtEnd - lookAt - val fovDelta = fovEnd - fov - val magnitudeDelta = magnitudeEnd - magnitude - if ( - abs(sphericalDelta.radius) > EPSILON || - abs(sphericalDelta.theta) > EPSILON || - abs(sphericalDelta.phi) > EPSILON || - abs(lookAtDelta.x) > EPSILON || - abs(lookAtDelta.y) > EPSILON || - abs(lookAtDelta.z) > EPSILON || - abs(fovDelta) > EPSILON - ) { - fov += (fovDelta * dampingFactor) - spherical += (sphericalDelta * dampingFactor) - spherical = spherical.makeSafe() - lookAt += (lookAtDelta * dampingFactor) - magnitude += (magnitudeDelta * dampingFactor) - dirty = true - } else { - magnitude = magnitudeEnd - spherical = sphericalEnd.copy() - lookAt = lookAtEnd.copy() - fov = fovEnd - } - spherical = spherical.makeSafe() - } - - /** - * Computes and returns the view matrix for the orbital camera. The view matrix is - * calculated using the current spherical coordinates, look-at position, and the up vector (Vector3.UNIT_Y). - * - * @return a 4x4 matrix representing the current view transformation of the camera - */ - fun viewMatrix(): Matrix44 { - return lookAt_(Vector3.fromSpherical(spherical) + lookAt, lookAt, Vector3.UNIT_Y) - } - - companion object { - private const val EPSILON = 0.000001 - } - - // EXTENSION - override var enabled: Boolean = true - - override fun setup(program: Program) { - this.program = program - } - - override fun beforeDraw(drawer: Drawer, program: Program) { - drawer.pushTransforms() - applyTo(drawer) - } - - override fun afterDraw(drawer: Drawer, program: Program) { - drawer.popTransforms() - } - - /** - * Enables the perspective camera. Use this faster method instead of .isolated() - * if you don't need to revert back to the orthographic projection. - */ - fun OrbitalCamera.applyTo(drawer: Drawer) { - - if (lastSeconds == -1.0) lastSeconds = program.seconds - - val delta = program.seconds - lastSeconds - lastSeconds = program.seconds - - update(delta) - - if (projectionType == ProjectionType.PERSPECTIVE) { - drawer.perspective(fov, drawer.width.toDouble() / drawer.height, near, far) - } else { - val ar = drawer.width * 1.0 / drawer.height - drawer.ortho(-ar * magnitude, ar * magnitude, -1.0 * magnitude, 1.0 * magnitude, orthoNear, orthoFar) - } - drawer.view = viewMatrix() - - if (depthTest) { - drawer.drawStyle.depthWrite = true - drawer.drawStyle.depthTestPass = DepthTestPass.LESS_OR_EQUAL - } - } - -} - -/** - * Temporarily enables this camera, calls function to draw using - * that camera, then disables it by popping the last matrix changes. - * It makes it easy to combine perspective and orthographic projections - * in the same program. - * @param function the function that is called in the isolation - */ -@OptIn(ExperimentalContracts::class) -fun OrbitalCamera.isolated(drawer: Drawer, function: Drawer.() -> Unit) { - contract { - callsInPlace(function, InvocationKind.EXACTLY_ONCE) - } - drawer.pushTransforms() - drawer.pushStyle() - try { - applyTo(drawer) - function(drawer) - } finally { - drawer.popStyle() - drawer.popTransforms() - } -} - - -private fun pow(a: Double, x: Double): Double = a.pow(x) - - -/** - * Creates an instance of the Orbital extension, sets it up with the calling Program, - * and returns the configured instance. - * - * @return a configured Orbital instance ready for use with the calling Program. - */ -fun Program.OrbitalManual(): Orbital { - val orbital = Orbital() - orbital.setup(this) - return orbital -} \ No newline at end of file diff --git a/orx-camera/src/commonMain/kotlin/OrbitalControls.kt b/orx-camera/src/commonMain/kotlin/OrbitalControls.kt deleted file mode 100644 index 51d77755..00000000 --- a/orx-camera/src/commonMain/kotlin/OrbitalControls.kt +++ /dev/null @@ -1,151 +0,0 @@ -package org.openrndr.extra.camera - -import org.openrndr.* -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.asRadians -import kotlin.math.abs -import kotlin.math.tan - -class OrbitalControls( - val orbitalCamera: OrbitalCamera, - var userInteraction: Boolean = true, - val keySpeed: Double = 1.0 -) : Extension { - enum class STATE { - NONE, - ROTATE, - PAN, - } - - private var state = STATE.NONE - var fov = orbitalCamera.fov - - private lateinit var program: Program - private lateinit var lastMousePosition: Vector2 - - private fun mouseScrolled(event: MouseEvent) { - if (userInteraction && !event.propagationCancelled) { - - if (orbitalCamera.projectionType == ProjectionType.PERSPECTIVE) { - if (abs(event.rotation.x) > 0.1) return - when { - event.rotation.y > 0 -> orbitalCamera.dollyIn() - event.rotation.y < 0 -> orbitalCamera.dollyOut() - } - } else { - if (abs(event.rotation.x) > 0.1) return - when { - event.rotation.y > 0 -> orbitalCamera.scale(1.0) - event.rotation.y < 0 -> orbitalCamera.scale(-1.0) - } - } - } - } - - private fun mouseMoved(event: MouseEvent) { - - if (userInteraction && !event.propagationCancelled) { - if (state == STATE.NONE) return - val delta = lastMousePosition - event.position - lastMousePosition = event.position - - if (state == STATE.PAN) { - - val offset = Vector3.fromSpherical(orbitalCamera.spherical) - orbitalCamera.lookAt - - // half of the fov is center to top of screen - val targetDistance = offset.length * tan(fov.asRadians / 2) - val panX = (2 * delta.x * targetDistance / program.width) - val panY = (2 * delta.y * targetDistance / program.height) - - orbitalCamera.pan(panX, -panY, 0.0) - - } else { - val rotX = 360.0 * delta.x / program.width - val rotY = 360.0 * delta.y / program.height - orbitalCamera.rotate(rotX, rotY) - } - } - } - - private fun mouseButtonDown(event: MouseEvent) { - if (userInteraction && !event.propagationCancelled) { - val previousState = state - - when (event.button) { - MouseButton.LEFT -> { - state = STATE.ROTATE - } - MouseButton.RIGHT -> { - state = STATE.PAN - } - MouseButton.CENTER -> { - } - MouseButton.NONE -> { - } - } - - if (previousState == STATE.NONE) { - lastMousePosition = event.position - } - } - } - - fun keyPressed(keyEvent: KeyEvent) { - if (userInteraction && !keyEvent.propagationCancelled) { - if (keyEvent.key == KEY_ARROW_RIGHT) { - orbitalCamera.pan(keySpeed, 0.0, 0.0) - } - if (keyEvent.key == KEY_ARROW_LEFT) { - orbitalCamera.pan(-keySpeed, 0.0, 0.0) - } - if (keyEvent.key == KEY_ARROW_UP) { - orbitalCamera.pan(0.0, keySpeed, 0.0) - } - if (keyEvent.key == KEY_ARROW_DOWN) { - orbitalCamera.pan(0.0, -keySpeed, 0.0) - } - - if (keyEvent.name == "q") { - orbitalCamera.pan(0.0, -keySpeed, 0.0) - } - if (keyEvent.name == "e") { - orbitalCamera.pan(0.0, keySpeed, 0.0) - } - if (keyEvent.name == "w") { - orbitalCamera.pan(0.0, 0.0, -keySpeed) - } - if (keyEvent.name == "s") { - orbitalCamera.pan(0.0, 0.0, keySpeed) - } - if (keyEvent.name == "a") { - orbitalCamera.pan(-keySpeed, 0.0, 0.0) - } - if (keyEvent.name == "d") { - orbitalCamera.pan(keySpeed, 0.0, 0.0) - } - - if (keyEvent.key == KEY_PAGE_UP) { - orbitalCamera.zoom(keySpeed) - } - if (keyEvent.key == KEY_PAGE_DOWN) { - orbitalCamera.zoom(-keySpeed) - } - } - } - - // EXTENSION - override var enabled: Boolean = true - - override fun setup(program: Program) { - this.program = program - - program.mouse.moved.listen { mouseMoved(it) } - program.mouse.buttonDown.listen { mouseButtonDown(it) } - program.mouse.buttonUp.listen { state = STATE.NONE } - program.mouse.scrolled.listen { mouseScrolled(it) } - program.keyboard.keyDown.listen { keyPressed(it) } - program.keyboard.keyRepeat.listen { keyPressed(it) } - } -} diff --git a/orx-camera/src/commonMain/kotlin/ParametricOrbital.kt b/orx-camera/src/commonMain/kotlin/ParametricOrbital.kt deleted file mode 100644 index 92f78797..00000000 --- a/orx-camera/src/commonMain/kotlin/ParametricOrbital.kt +++ /dev/null @@ -1,106 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.camera - -import org.openrndr.Extension -import org.openrndr.Program -import org.openrndr.draw.Drawer -import org.openrndr.events.Event -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.Vector3Parameter -import org.openrndr.math.Spherical -import org.openrndr.math.Vector3 - -/** - * Extension that provides orbital camera through annotated parameters - */ -@Description("Orbital camera") -class ParametricOrbital : Extension, ChangeEvents { - override var enabled: Boolean = true - - override val changed = Event() - override val hasChanged: Boolean - get() = dirty - private var dirty = true - set(value) { - if (value && !field) { - changed.trigger(Unit) - } - field = value - } - @DoubleParameter("fov", 1.0, 90.0, order = 0) - var fov = 45.0 - set(value) { - if (field != value) { - dirty = true - } - field = value - camera.zoomTo(fov) - } - - val camera by lazy { - OrbitalCamera(Spherical(theta, phi, radius).cartesian, center, fov, 0.1, 1000.0, projectionType).apply { - dampingFactor = 0.0 - } - } - - @DoubleParameter("phi", 0.0, 180.0, order = 2) - var phi = 0.0 - set(value) { - if (field != value) { - dirty = true - } - field = value - camera.rotateTo(theta, phi.coerceAtLeast(1E-3)) - } - - @DoubleParameter("theta", -180.0, 180.0, order = 1) - var theta = 0.0 - set(value) { - if (field != value) { - dirty = true - } - field = value - camera.rotateTo(theta, phi.coerceAtLeast(1E-3)) - } - - - @DoubleParameter("orbit radius", 0.1, 100.0, order = 3) - var radius = 10.0 - set(value) { - if (field != value) { - dirty = true - } - field = value - camera.dollyTo(radius) - } - - - @Vector3Parameter("center", order = 4) - var center = Vector3.ZERO - set(value) { - if (field != value) { - dirty = true - } - field = value - camera.panTo(value) - } - - - var projectionType = ProjectionType.PERSPECTIVE - - - override fun setup(program: Program) { - camera.setup(program) - } - - override fun beforeDraw(drawer: Drawer, program: Program) { - camera.beforeDraw(drawer, program) - } - - override fun afterDraw(drawer: Drawer, program: Program) { - dirty = false - camera.afterDraw(drawer, program) - } -} \ No newline at end of file diff --git a/orx-camera/src/jvmDemo/kotlin/DemoCamera2D01.kt b/orx-camera/src/jvmDemo/kotlin/DemoCamera2D01.kt deleted file mode 100644 index 84ee2b56..00000000 --- a/orx-camera/src/jvmDemo/kotlin/DemoCamera2D01.kt +++ /dev/null @@ -1,25 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.loadFont -import org.openrndr.extra.camera.Camera2D - -/** - * #### Camera2D demo - * - * click and drag the mouse for panning, use the mouse wheel for zooming - */ -fun main() = application { - program { - val font = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 72.0) - - extend(Camera2D()) - extend { - drawer.circle(drawer.bounds.center, 300.0) - - drawer.fontMap = font - drawer.fill = ColorRGBa.PINK - drawer.text("click and drag mouse", 50.0, 400.0) - drawer.text("use mouse wheel", 50.0, 500.0) - } - } -} \ No newline at end of file diff --git a/orx-camera/src/jvmDemo/kotlin/DemoCamera2D02.kt b/orx-camera/src/jvmDemo/kotlin/DemoCamera2D02.kt deleted file mode 100644 index 223d0c60..00000000 --- a/orx-camera/src/jvmDemo/kotlin/DemoCamera2D02.kt +++ /dev/null @@ -1,64 +0,0 @@ -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.math.Vector2 -import org.openrndr.math.transforms.transform -import org.openrndr.shape.Rectangle -import kotlin.math.sin - -/** - * #### Camera2D demo with static elements - * - * An approach for having certain elements not affected by the camera. - * See DemoCamera2DManual01.kt for a new and simpler approach - */ -fun main() = application { - program { - // Create a renderTarget where to draw things. It will be controlled by the camera. - val rt = renderTarget(width, height) { - colorBuffer() - depthBuffer() - } - // Create a camera and apply an initial transformation - // so the origin is no longer in the top-left corner. - val cam = Camera2D() - cam.view *= transform { - translate(width * 0.5, height * 1.0) - rotate(45.0) - scale(2.0) - } - - // Add mouse listeners to the camera - cam.setup(this) - - val font = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 50.0) - - extend { - // Draw onto the renderTarget - drawer.isolatedWithTarget(rt) { - // Calling ortho required if the size differs from the window size - ortho(rt) - // Apply the current camera transformation - view = cam.view - // Clear render target - clear(ColorRGBa.TRANSPARENT) - // Draw the things affected by the camera (here a rectangle at the origin) - rectangle(Rectangle.fromCenter(Vector2.ZERO, 200.0, 100.0 + sin(seconds) * 20.0)) - } - - drawer.clear(ColorRGBa.PINK) - - // Draw the renderTarget with the camera applied to it - drawer.image(rt.colorBuffer(0)) - - // Draw things not affected by the camera - drawer.fontMap = font - drawer.fill = ColorRGBa.PINK.shade(0.5) - drawer.text("click and drag mouse", 50.0, 400.0) - drawer.text("use mouse wheel", 50.0, 450.0) - } - } -} diff --git a/orx-camera/src/jvmDemo/kotlin/DemoCamera2DManual01.kt b/orx-camera/src/jvmDemo/kotlin/DemoCamera2DManual01.kt deleted file mode 100644 index 9081c6b9..00000000 --- a/orx-camera/src/jvmDemo/kotlin/DemoCamera2DManual01.kt +++ /dev/null @@ -1,35 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.camera.Camera2DManual - -/** - * Demonstrates how to use `Camera2DManual` to have - * some elements affected by an interactive 2D camera combined with - * other elements not affected by it. - * - * In this example both PINK circles can be dragged, scaled and rotated - * while the white circle in the middle is static. - */ -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) - } - } - } -} diff --git a/orx-camera/src/jvmDemo/kotlin/DemoCamera2DManual02.kt b/orx-camera/src/jvmDemo/kotlin/DemoCamera2DManual02.kt deleted file mode 100644 index ba2b464b..00000000 --- a/orx-camera/src/jvmDemo/kotlin/DemoCamera2DManual02.kt +++ /dev/null @@ -1,70 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.camera.Camera2DManual -import org.openrndr.extra.noise.shapes.uniform -import org.openrndr.extra.noise.uniform -import org.openrndr.math.Vector2 -import org.openrndr.math.transforms.transform -import org.openrndr.shape.Rectangle -import org.openrndr.shape.contains - -/** - * Demonstrate the use of `Camera2DManual` to independently translate, scale and rotate one contour - * in a collection. - * - * When the mouse is clicked, the active contour is transformed using the camera view matrix, - * then the camera is reset to its default state and whatever shape is under the mouse becomes - * the new active contour. - * - * As the mouse is dragged or its wheel scrolled, the camera is updated, affecting - * how the active contour is rendered. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - - program { - val camera = Camera2DManual() - // Create a mutable list of rectangular contours with random transformations - // applied to them. This is the initial state for the contours. - val contours = MutableList(16) { - Rectangle.fromCenter(Vector2.ZERO, 85.0).contour.transform( - transform { - translate(drawer.bounds.uniform()) - scale(Double.uniform(0.5, 2.0)) - rotate(Double.uniform(0.0, 360.0)) - } - ) - } - - var activeContour = -1 - extend { - // Draw all contours. The active contour is drawn in pink and - // affected by the camera's transformations. - contours.forEachIndexed { i, c -> - if (i == activeContour) { - camera.isolated { - drawer.fill = ColorRGBa.PINK - drawer.contour(c) - } - } else { - drawer.fill = ColorRGBa.GRAY - drawer.contour(c) - } - } - } - mouse.buttonDown.listen { - // Apply the camera view matrix to the active contour - if (activeContour >= 0) contours[activeContour] = contours[activeContour].transform(camera.view) - - // Reset the camera to its default state - camera.defaults() - camera.rotationCenter = it.position - - // Make the contour under the mouse the active contour - activeContour = contours.indexOfLast { mouse.position in it } - } - } -} diff --git a/orx-camera/src/jvmDemo/kotlin/DemoOrbital01.kt b/orx-camera/src/jvmDemo/kotlin/DemoOrbital01.kt deleted file mode 100644 index f4c86dd6..00000000 --- a/orx-camera/src/jvmDemo/kotlin/DemoOrbital01.kt +++ /dev/null @@ -1,39 +0,0 @@ -import org.openrndr.WindowMultisample -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.meshgenerators.boxMesh -import org.openrndr.extra.meshgenerators.sphereMesh - -/** - * Demonstrate the use of `Orbital`, an interactive 3D camera - * that can be controlled with a mouse and a keyboard. - */ -fun main() = application { - configure { - width = 720 - height = 720 - multisample = WindowMultisample.SampleCount(8) - } - program { - val sphere = sphereMesh(radius = 25.0) - val cube = boxMesh(20.0, 20.0, 5.0, 5, 5, 2) - - extend(Orbital()) - - extend { - drawer.vertexBuffer(sphere, DrawPrimitive.LINE_LOOP) - drawer.vertexBuffer(cube, DrawPrimitive.LINE_LOOP) - drawer.fill = null - drawer.stroke = ColorRGBa.GREEN - - repeat(10) { - drawer.translate(0.0, 0.0, 10.0) - // Note: 2D primitives are not optimized for 3D and can - // occlude each other - drawer.circle(0.0, 0.0, 50.0) - } - } - } -} \ No newline at end of file diff --git a/orx-camera/src/jvmDemo/kotlin/DemoOrbitalCamera01.kt b/orx-camera/src/jvmDemo/kotlin/DemoOrbitalCamera01.kt deleted file mode 100644 index d2a82b29..00000000 --- a/orx-camera/src/jvmDemo/kotlin/DemoOrbitalCamera01.kt +++ /dev/null @@ -1,69 +0,0 @@ -import org.openrndr.WindowMultisample -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.extra.camera.AxisHelper -import org.openrndr.extra.camera.GridHelper -import org.openrndr.extra.camera.OrbitalCamera -import org.openrndr.extra.camera.OrbitalControls -import org.openrndr.extra.meshgenerators.boxMesh -import org.openrndr.extra.meshgenerators.sphereMesh -import org.openrndr.math.Vector3 - -/** - * Demonstrate the use of `OrbitalCamera`, `OrbitalControls`, `AxisHelper` and `GridHelper`. - * - * Press the `t` key to toggle camera interaction, or `r` to reset the camera to its defaults. - */ -fun main() = application { - configure { - width = 720 - height = 720 - multisample = WindowMultisample.SampleCount(8) - } - program { - val camera = OrbitalCamera( - Vector3.UNIT_Z * 90.0, Vector3.ZERO, 90.0, 0.1, 5000.0 - ) - val controls = OrbitalControls(camera, keySpeed = 10.0) - controls.userInteraction = false - - val sphere = sphereMesh(radius = 25.0) - val cube = boxMesh(20.0, 20.0, 5.0, 5, 5, 2) - - extend(camera) - - // shows XYZ axes as RGB lines - extend(AxisHelper()) - - // debug ground plane - extend(GridHelper(100)) - - // adds mouse and keyboard bindings - extend(controls) - - extend { - drawer.vertexBuffer(sphere, DrawPrimitive.LINE_LOOP) - drawer.vertexBuffer(cube, DrawPrimitive.LINE_LOOP) - - drawer.stroke = if(controls.userInteraction) ColorRGBa.GREEN else ColorRGBa.WHITE - drawer.fill = null - - repeat(10) { - drawer.translate(0.0, 0.0, 10.0) - // Note: 2D primitives are not optimized for 3D and can - // occlude each other - drawer.circle(0.0, 0.0, 50.0) - } - } - keyboard.keyDown.listen { - if (it.name == "r") { - camera.defaults() - } - if (it.name == "t") { - // mouse and keyboard input can be toggled on and off - controls.userInteraction = !controls.userInteraction - } - } - } -} \ No newline at end of file diff --git a/orx-camera/src/jvmDemo/kotlin/DemoOrbitalManual01.kt b/orx-camera/src/jvmDemo/kotlin/DemoOrbitalManual01.kt deleted file mode 100644 index a4444d86..00000000 --- a/orx-camera/src/jvmDemo/kotlin/DemoOrbitalManual01.kt +++ /dev/null @@ -1,49 +0,0 @@ -import org.openrndr.WindowMultisample -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.extra.camera.OrbitalManual -import org.openrndr.extra.meshgenerators.boxMesh -import org.openrndr.extra.meshgenerators.sphereMesh -import org.openrndr.math.Vector3 - -/** - * Demonstrate the use of an orbital camera to render a sphere and a cube in 3D space as wireframe meshes, positioned - * and rendered independently using the camera's isolated drawing state. A stationary pink circle is also drawn in the - * center of the scene. - * - * Functionality: - * - Initializes a sphere mesh and a cube mesh with predefined dimensions. - * - Spawns an orbital camera, initially positioned away from the origin, to allow for focused rendering. - * - Renders 3D wireframe shapes (sphere and cube) using the camera's isolated perspective. - * - Draws a static 2D pink circle overlay at the window center. - */ -fun main() = application { - configure { - width = 720 - height = 720 - multisample = WindowMultisample.SampleCount(8) - } - - program { - val sphere = sphereMesh(radius = 25.0) - val cube = boxMesh(20.0, 20.0, 5.0, 5, 5, 2) - - val camera = OrbitalManual() - camera.camera.rotateTo(Vector3(0.0, 0.0, 30.0), instant = true) - extend { - camera.isolated { - drawer.fill = ColorRGBa.WHITE - drawer.vertexBuffer(sphere, DrawPrimitive.LINE_LOOP) - } - - drawer.fill = ColorRGBa.PINK - drawer.circle(drawer.bounds.center, 250.0) - - camera.isolated { - drawer.fill = ColorRGBa.WHITE - drawer.vertexBuffer(cube, DrawPrimitive.LINE_LOOP) - } - } - } -} \ No newline at end of file diff --git a/orx-camera/src/jvmDemo/kotlin/DemoParametricOrbital01.kt b/orx-camera/src/jvmDemo/kotlin/DemoParametricOrbital01.kt deleted file mode 100644 index 590a87a2..00000000 --- a/orx-camera/src/jvmDemo/kotlin/DemoParametricOrbital01.kt +++ /dev/null @@ -1,46 +0,0 @@ -import org.openrndr.WindowMultisample -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.camera.* -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.gui.addTo -import org.openrndr.extra.meshgenerators.boxMesh - -/** - * Demonstrates the use of a `ParametricOrbital` camera. - * This 3D camera can't be directly interacted with a mouse or a keyboard, - * but only via a GUI (or via code). - * - * The GUI state is saved when closing the program and loaded - * when running it again. - * - * The GUI also allows randomizing, loading and saving - * its state to a file via the top buttons it displays. - */ -fun main() = application { - configure { - multisample = WindowMultisample.SampleCount(8) - } - - program { - val gui = GUI() - val po = ParametricOrbital() - po.addTo(gui) - extend(gui) - extend(po) - - val bm = boxMesh() - extend { - drawer.clear(ColorRGBa.PINK) - drawer.shadeStyle = shadeStyle { - fragmentTransform = """ - vec3 n = normalize(v_viewNormal) * 0.5 + 0.5; - x_fill = vec4(n, 1.0); - """.trimIndent() - } - drawer.vertexBuffer(bm, DrawPrimitive.TRIANGLES) - } - } -} diff --git a/orx-color/README.md b/orx-color/README.md deleted file mode 100644 index fb279e1d..00000000 --- a/orx-color/README.md +++ /dev/null @@ -1,421 +0,0 @@ -# orx-color - -Color spaces, palettes, histograms, named colors. - -## Color presets - -orx-color adds an extensive list of preset colors to `ColorRGBa`. Check [sources](src/commonMain/kotlin/presets/Colors.kt) for a listing of the preset colors. - -## Color histograms - -orx-color comes with tools to calculate color histograms for images. - -```kotlin -val histogram = calculateHistogramRGB(image) -val colors = histogram.sortedColors() -``` - -## Color sequences - -Easy ways of creating blends between colors. - -Using the `rangeTo` operator: -```kotlin -for (c in ColorRGBa.PINK..ColorRGBa.BLUE.toHSVa() blend 10) { - drawer.fill = c - drawer.rectangle(0.0, 0.0, 40.0, 40.0) - drawer.translate(0.0, 40.0) -} -``` - -Or blends for multiple color stops using `colorSequence`. Blending takes place in the colorspace of the input arguments. -```kotlin -val cs = colorSequence(0.0 to ColorRGBa.PINK, - 0.5 to ColorRGBa.BLUE, - 1.0 to ColorRGBa.PINK.toHSLUVa()) // <-- note this one is in hsluv - -for (c in cs blend (width / 40)) { - drawer.fill = c - drawer.stroke = null - drawer.rectangle(0.0, 0.0, 40.0, height.toDouble()) - drawer.translate(40.0, 0.0) -} -``` - - - -## HSLUVa and HPLUVa colorspaces - -Two color spaces are added: `ColorHSLUVa` and `ColorHPLUVa`, they are an implementation of the colorspaces presented at [hsluv.org](http://www.hsluv.org) - -## Demos -### colormap/DemoSpectralZucconiColormap - -This program demonstrates the `spectralZucconi6()` function, which -takes a normalized value and returns a `ColorRGBa` using the -accurate spectral colormap developed by Alan Zucconi. - -It draws a varying number of vertical bands (between 16 and 48) -filled with various hues. - -![colormap-DemoSpectralZucconiColormapKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colormap-DemoSpectralZucconiColormapKt.png) - -[source code](src/jvmDemo/kotlin/colormap/DemoSpectralZucconiColormap.kt) - -### colormap/DemoSpectralZucconiColormapPhrase - -This program demonstrates how to use the shader-based version of -the `spectral_zucconi6()` function, which -takes a normalized value and returns an `rgb` color using the -accurate spectral colormap developed by Alan Zucconi. - -It shades a full-window rectangle using its normalized `x` coordinate -in a `ShadeStyle` to choose pixel colors. - -![colormap-DemoSpectralZucconiColormapPhraseKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colormap-DemoSpectralZucconiColormapPhraseKt.png) - -[source code](src/jvmDemo/kotlin/colormap/DemoSpectralZucconiColormapPhrase.kt) - -### colormap/DemoSpectralZucconiColormapPlot - -This demo uses the shader based `spectral_zucconi6()` function to fill the background, -then visualizes the red, green and blue components of the colors used in the background -as red, green and blue line strips. - -The Vector2 points for the line strips are calculated only once when the program starts. - -![colormap-DemoSpectralZucconiColormapPlotKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colormap-DemoSpectralZucconiColormapPlotKt.png) - -[source code](src/jvmDemo/kotlin/colormap/DemoSpectralZucconiColormapPlot.kt) - -### colormap/DemoTurboColormap - -This program demonstrates the `turboColormap()` function, which -takes a normalized value and returns a `ColorRGBa` using the -Turbo colormap developed by Google. - -It draws a varying number of vertical bands (between 16 and 48) -filled with various hues. - -![colormap-DemoTurboColormapKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colormap-DemoTurboColormapKt.png) - -[source code](src/jvmDemo/kotlin/colormap/DemoTurboColormap.kt) - -### colormap/DemoTurboColormapPhrase - -This program demonstrates how to use the shader-based version of -the `turbo_colormap()` function, which -takes a normalized value and returns an `rgb` color using the -Turbo colormap developed by Google. - -It shades a full-window rectangle using its normalized `x` coordinate -in a `ShadeStyle` to choose pixel colors. - -![colormap-DemoTurboColormapPhraseKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colormap-DemoTurboColormapPhraseKt.png) - -[source code](src/jvmDemo/kotlin/colormap/DemoTurboColormapPhrase.kt) - -### colormap/DemoTurboColormapPlot - -This demo uses the shader based `turbo_colormap()` function to fill the background, -then visualizes the red, green and blue components of the colors used in the background -as red, green and blue line strips. - -The Vector2 points for the line strips are calculated only once when the program starts. - -![colormap-DemoTurboColormapPlotKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colormap-DemoTurboColormapPlotKt.png) - -[source code](src/jvmDemo/kotlin/colormap/DemoTurboColormapPlot.kt) - -### colormatrix/DemoColorMatrix01 - -This demo modifies the displayed image in each grid cell -using color matrix transformations to demonstrate color channel inversions based on -the grid cell's index. The image is adjusted to fit within each grid cell while maintaining -alignment. - -Functionality: -- Loads an image from the specified file path. -- Splits the drawing area into an evenly spaced 4x2 grid. -- Applies different color matrix inversions (red, green, blue) based on the position index. -- Fits the image into each grid cell while providing horizontal alignment adjustments. - -![colormatrix-DemoColorMatrix01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colormatrix-DemoColorMatrix01Kt.png) - -[source code](src/jvmDemo/kotlin/colormatrix/DemoColorMatrix01.kt) - -### colormatrix/DemoColorMatrix02 - -This demo modifies the displayed image in each grid cell -using color matrix transformations to demonstrate color channel inversions based on -the grid cell's index. The image is adjusted to fit within each grid cell while maintaining -alignment. - -Functionality: -- Loads an image from the specified file path. -- Splits the drawing area into an evenly spaced 4x2 grid. -- Applies different color matrix inversions (red, green, blue) based on the position index. -- Fits the image into each grid cell while providing horizontal alignment adjustments. - -![colormatrix-DemoColorMatrix02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colormatrix-DemoColorMatrix02Kt.png) - -[source code](src/jvmDemo/kotlin/colormatrix/DemoColorMatrix02.kt) - -### colormatrix/DemoColorMatrix03 - -Entry point for an application demonstrating the use of color matrix transformations on an image. - -The program initializes a graphical application with a resolution of 720x720 pixels -and processes an image to display it in a series of grid cells, applying a hue shift -transformation based on the index of each cell. - -Key features: -- Loads an image from a specified file path. -- Configures the drawing area to consist of a horizontal grid with 16 cells. -- Applies a color tint transformation utilizing the red channel, shifting its hue progressively -per cell index to create a colorful gradient effect. -- Adjusts the positions of the images within each grid cell for aesthetic alignment. - -![colormatrix-DemoColorMatrix03Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colormatrix-DemoColorMatrix03Kt.png) - -[source code](src/jvmDemo/kotlin/colormatrix/DemoColorMatrix03.kt) - -### colormatrix/DemoColorMatrix04 - -Entry point of a graphical application that demonstrates the use of color matrix -transformations on an image displayed within a grid layout. - -Overview: -- Initializes a window with a resolution of 720x720 pixels. -- Loads an image from the specified file path. -- Splits the drawing canvas into a 7x1 grid of cells. -- In each grid cell, applies custom grayscale transformations to the image using -a color matrix. The grayscale transformation coefficients for red, green, and blue -channels are computed based on the index of the grid cell. -- Displays the adjusted image in each grid cell with horizontal alignment modifications -to position the images dynamically based on their index within the grid. - -![colormatrix-DemoColorMatrix04Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colormatrix-DemoColorMatrix04Kt.png) - -[source code](src/jvmDemo/kotlin/colormatrix/DemoColorMatrix04.kt) - -### colorRange/DemoColorRange01 - -Comparison of color lists generated by interpolating from -`PINK` to `BLUE` in six different color spaces. - -![colorRange-DemoColorRange01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colorRange-DemoColorRange01Kt.png) - -[source code](src/jvmDemo/kotlin/colorRange/DemoColorRange01.kt) - -### colorRange/DemoColorRange02 - -Demonstrates how to create a `ColorSequence` containing three colors, one of them in the HSLUV color space. - -Each color in the sequence is assigned a normalized position: in this program, one at the start (0.0), -one in the middle (0.5) and one at the end (1.0). - -The `ColorSpace.blend()` method is used to get a list with 18 interpolated `ColorRGBa` colors, -then those colors are drawn as vertical rectangles covering the whole window. - -![colorRange-DemoColorRange02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colorRange-DemoColorRange02Kt.png) - -[source code](src/jvmDemo/kotlin/colorRange/DemoColorRange02.kt) - -### colorRange/DemoColorRange03 - -This program creates color interpolations from `ColorRGBa.BLUE` to -`ColorRGBa.PINK` in 25 steps in multiple color spaces. - -The window height is adjusted based on the number of interpolations to show. - -The resulting gradients differ in saturation and brightness and apparently include more -`BLUE` or more `PINK` depending on the chosen color space. - -![colorRange-DemoColorRange03Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colorRange-DemoColorRange03Kt.png) - -[source code](src/jvmDemo/kotlin/colorRange/DemoColorRange03.kt) - -### colorRange/DemoColorRange04 - -A visualization of color interpolations inside a 3D RGB cube with an interactive 3D `Orbital` camera. - -The hues of the source and target colors are animated over time. - -The color interpolations are shown simultaneously in nine different color spaces, revealing how in -each case they share common starting and ending points in 3D, but have unique paths going from -start to end. - -By rotating the cube 90 degrees towards the left and slightly zooming out, one can appreciate how -one of the points moves along the edges of the cube, while the other moves on the edges of a -smaller, invisible cube. - - -![colorRange-DemoColorRange04Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/colorRange-DemoColorRange04Kt.png) - -[source code](src/jvmDemo/kotlin/colorRange/DemoColorRange04.kt) - -### DemoColorPalette01 - -Demonstrates the creation of color palettes using various available methods - -![DemoColorPalette01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoColorPalette01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoColorPalette01.kt) - -### DemoColorPalette02 - -By default, generated palettes contain colors of varying hue -but similar brightness and saturation. -Here we alter the brightness of each color using .shade() for -an increased dynamic range. - -![DemoColorPalette02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoColorPalette02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoColorPalette02.kt) - -### DemoColorPlane01 - -Visualizes a plane of ColorOKLCH colors as small 3D spheres -inside a 3D box. The plane represents all available hues and chromas. -The luminosity used to create the colors is modulated over time -with a slow sine wave. -Instanced rendering is used to render 90 x 100 colored spheres, -each with a unique position based on the RGB components of the color. - -Since the OKLCH color space is larger than the RGB space, some -spheres would be outside the 3D box, but they are -actually clipped to the walls. - -![DemoColorPlane01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoColorPlane01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoColorPlane01.kt) - -### DemoColorPlane02 - - - -![DemoColorPlane02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoColorPlane02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoColorPlane02.kt) - -### DemoColorSequence01 - -A demo that demonstrates 3D objects with custom shading and color gradients. - -The application setup involves: -- Configuring the application window dimensions. -- Creating a color gradient using `ColorSequence` and converting it to a `ColorBuffer` for shading purposes. -- Defining a 3D sphere mesh with specified resolution. - -The rendering process includes: -- Setting up an orbital camera extension to provide an interactive 3D view. -- Applying a custom fragment shader with a palette-based shading style. -- Rendering a grid of 3D spheres, each transformed and rotated to create a dynamic pattern. - -![DemoColorSequence01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoColorSequence01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoColorSequence01.kt) - -### DemoDeltaE - - - -![DemoDeltaEKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoDeltaEKt.png) - -[source code](src/jvmDemo/kotlin/DemoDeltaE.kt) - -### DemoFettePalette01 - - - -![DemoFettePalette01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoFettePalette01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoFettePalette01.kt) - -### DemoFettePalette02 - - - -![DemoFettePalette02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoFettePalette02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoFettePalette02.kt) - -### DemoHSLUV01 - - - -![DemoHSLUV01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoHSLUV01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoHSLUV01.kt) - -### DemoHSLUV02 - - - -![DemoHSLUV02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoHSLUV02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoHSLUV02.kt) - -### DemoHueTools01 - - - -![DemoHueTools01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoHueTools01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoHueTools01.kt) - -### DemoMixSpectral01 - - - -![DemoMixSpectral01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoMixSpectral01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoMixSpectral01.kt) - -### DemoOKHSV01 - - - -![DemoOKHSV01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoOKHSV01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoOKHSV01.kt) - -### DemoXSLUV01 - - - -![DemoXSLUV01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/DemoXSLUV01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoXSLUV01.kt) - -### histogram/DemoHistogram01 - -Demonstrates how to generate a palette with the top 32 colors -of a loaded image, sorted by luminosity. The colors are displayed -as rectangles overlayed on top of the image. - -![histogram-DemoHistogram01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/histogram-DemoHistogram01Kt.png) - -[source code](src/jvmDemo/kotlin/histogram/DemoHistogram01.kt) - -### histogram/DemoHistogram02 - -Show the color histogram of an image using non-uniform weighting, -prioritizing bright colors. - -![histogram-DemoHistogram02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/histogram-DemoHistogram02Kt.png) - -[source code](src/jvmDemo/kotlin/histogram/DemoHistogram02.kt) - -### histogram/DemoHistogram03 - -Create a simple grid-like composition based on colors sampled from image. -The cells are 32 by 32 pixels in size and are filled with a random sample -taken from the color histogram of the image. - -Note: due to its random nature the resulting animation contains flickering colors. - -![histogram-DemoHistogram03Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-color/images/histogram-DemoHistogram03Kt.png) - -[source code](src/jvmDemo/kotlin/histogram/DemoHistogram03.kt) diff --git a/orx-color/build.gradle.kts b/orx-color/build.gradle.kts deleted file mode 100644 index a198d217..00000000 --- a/orx-color/build.gradle.kts +++ /dev/null @@ -1,50 +0,0 @@ -@Suppress("DSL_SCOPE_VIOLATION") -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") - alias(libs.plugins.kotlin.serialization) - alias(libs.plugins.kotest.multiplatform) -} - -kotlin { - sourceSets { - val commonMain by getting { - dependencies { - implementation(project(":orx-parameters")) - implementation(project(":orx-shader-phrases")) - implementation(sharedLibs.kotlin.serialization.core) - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - } - } - - val commonTest by getting { - dependencies { - implementation(sharedLibs.kotlin.serialization.json) - implementation(sharedLibs.kotest.assertions) - implementation(sharedLibs.kotest.framework.engine) - } - } - - val jvmTest by getting { - dependencies { - implementation(sharedLibs.kotlin.serialization.json) - implementation(sharedLibs.kotest.assertions) - implementation(sharedLibs.kotest.framework.engine) - } - } - - val jvmDemo by getting { - dependencies { - implementation(project(":orx-camera")) - implementation(project(":orx-mesh-generators")) - implementation(project(":orx-color")) - implementation(project(":orx-jvm:orx-gui")) - implementation(project(":orx-shade-styles")) - implementation(project(":orx-image-fit")) - implementation(project(":orx-shapes")) - } - } - } -} \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/Color.kt b/orx-color/src/commonMain/kotlin/Color.kt deleted file mode 100644 index 91eaf885..00000000 --- a/orx-color/src/commonMain/kotlin/Color.kt +++ /dev/null @@ -1,5 +0,0 @@ -// keeping this file here will stop IntelliJ from showing warning in nested relative packages -/** - * orx-color - */ -package org.openrndr.extra.color diff --git a/orx-color/src/commonMain/kotlin/colormaps/ColormapPhrases.kt b/orx-color/src/commonMain/kotlin/colormaps/ColormapPhrases.kt deleted file mode 100644 index 92603d45..00000000 --- a/orx-color/src/commonMain/kotlin/colormaps/ColormapPhrases.kt +++ /dev/null @@ -1,91 +0,0 @@ -package org.openrndr.extra.color.colormaps - -import org.openrndr.extra.shaderphrases.ShaderPhrase -import org.openrndr.extra.shaderphrases.ShaderPhraseBook - -/** - * Colormaps represent a class of functions taking a value in the range `0.0..1.0` and returning particular RGB color - * values. Colormaps can be used in data visualization for representing additional information/dimension of the data - * e.g: - * - depth - * - elevation - * - heat - * - * Note: the [ShaderPhrase] GLSL functions gathered in this [ShaderPhraseBook] also have respective Kotlin - * implementations. - * - * @see org.openrndr.extra.color.colormaps.turboColormap - * @see org.openrndr.extra.color.colormaps.spectralZucconi6 - */ -object ColormapPhraseBook : ShaderPhraseBook("colormap") { - - // Copyright 2019 Google LLC. - // SPDX-License-Identifier: Apache-2.0 - /** - * Polynomial approximation in GLSL for the Turbo colormap. - * - * See [Turbo, An Improved Rainbow Colormap for Visualization](https://research.google/blog/turbo-an-improved-rainbow-colormap-for-visualization/), - * [the source of this code](https://gist.github.com/mikhailov-work/0d177465a8151eb6ede1768d51d476c7), - * - * @author Anton Mikhailov (mikhailov@google.com) - Colormap Design - * @author Ruofei Du (ruofei@google.com) - GLSL Approximation - * @see org.openrndr.extra.color.colormaps.turboColormap - */ - val turboColormap = ShaderPhrase(""" - |vec3 turbo_colormap(in float x) { - | const vec4 kRedVec4 = vec4(0.13572138, 4.61539260, -42.66032258, 132.13108234); - | const vec4 kGreenVec4 = vec4(0.09140261, 2.19418839, 4.84296658, -14.18503333); - | const vec4 kBlueVec4 = vec4(0.10667330, 12.64194608, -60.58204836, 110.36276771); - | const vec2 kRedVec2 = vec2(-152.94239396, 59.28637943); - | const vec2 kGreenVec2 = vec2(4.27729857, 2.82956604); - | const vec2 kBlueVec2 = vec2(-89.90310912, 27.34824973); - | - | x = clamp(x, 0.0, 1.0); - | vec4 v4 = vec4( 1.0, x, x * x, x * x * x); - | vec2 v2 = v4.zw * v4.z; - | return vec3( - | dot(v4, kRedVec4) + dot(v2, kRedVec2), - | dot(v4, kGreenVec4) + dot(v2, kGreenVec2), - | dot(v4, kBlueVec4) + dot(v2, kBlueVec2) - | ); - |}""".trimMargin()) - - /** - * Accurate spectral colormap developed by Alan Zucconi. - * - * See [Improving the Rainbow](https://www.alanzucconi.com/2017/07/15/improving-the-rainbow/) article, - * [the source of this code](https://www.shadertoy.com/view/ls2Bz1) - * - * @author Alan Zucconi - * @see org.openrndr.extra.color.colormaps.spectralZucconi6 - */ - val spectralZucconi6 = ShaderPhrase(""" - |#pragma import colormap.bump3y - | - |vec3 spectral_zucconi6(in float x) { - | - | const vec3 c1 = vec3(3.54585104, 2.93225262, 2.41593945); - | const vec3 x1 = vec3(0.69549072, 0.49228336, 0.27699880); - | const vec3 y1 = vec3(0.02312639, 0.15225084, 0.52607955); - | - | const vec3 c2 = vec3(3.90307140, 3.21182957, 3.96587128); - | const vec3 x2 = vec3(0.11748627, 0.86755042, 0.66077860); - | const vec3 y2 = vec3(0.84897130, 0.88445281, 0.73949448); - | - | return - | bump3y(c1 * (x - x1), y1) + - | bump3y(c2 * (x - x2), y2) ; - |}""".trimMargin()) - - /** - * A function used internally by [spectralZucconi6]. - * - * @author Alan Zucconi - */ - val bump3y = ShaderPhrase(""" - |vec3 bump3y(in vec3 x, in vec3 yoffset) { - | vec3 y = vec3(1.0) - x * x; - | return clamp(y - yoffset, vec3(0.0), vec3(1.0)); - |}""".trimMargin()) - -} diff --git a/orx-color/src/commonMain/kotlin/colormaps/SpectralZucconiColormap.kt b/orx-color/src/commonMain/kotlin/colormaps/SpectralZucconiColormap.kt deleted file mode 100644 index 38698264..00000000 --- a/orx-color/src/commonMain/kotlin/colormaps/SpectralZucconiColormap.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.openrndr.extra.color.colormaps - -import org.openrndr.color.ColorRGBa -import org.openrndr.math.Vector3 -import org.openrndr.math.saturate - -/** - * Accurate spectral colormap developed by Alan Zucconi. - * - * @see spectralZucconi6Vector - * @see ColormapPhraseBook.spectralZucconi6 - */ -fun spectralZucconi6( - x: Double -): ColorRGBa = ColorRGBa.fromVector( - spectralZucconi6Vector(x) -) - -/** - * Accurate spectral colormap developed by Alan Zucconi. - * - * @see ColormapPhraseBook.spectralZucconi6 - */ -fun spectralZucconi6Vector(x: Double): Vector3 { - val v = Vector3(x) - return bump3y(c1 * (v - x1), y1) + bump3y(c2 * (v - x2), y2) -} - -private fun bump3y( - x: Vector3, - yOffset: Vector3 -) = (Vector3.ONE - x * x - yOffset).saturate() - -private val c1 = Vector3(3.54585104, 2.93225262, 2.41593945) -private val x1 = Vector3(0.69549072, 0.49228336, 0.27699880) -private val y1 = Vector3(0.02312639, 0.15225084, 0.52607955) - -private val c2 = Vector3(3.90307140, 3.21182957, 3.96587128) -private val x2 = Vector3(0.11748627, 0.86755042, 0.66077860) -private val y2 = Vector3(0.84897130, 0.88445281, 0.73949448) diff --git a/orx-color/src/commonMain/kotlin/colormaps/TurboColormap.kt b/orx-color/src/commonMain/kotlin/colormaps/TurboColormap.kt deleted file mode 100644 index af423c79..00000000 --- a/orx-color/src/commonMain/kotlin/colormaps/TurboColormap.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.openrndr.extra.color.colormaps - -import org.openrndr.color.ColorRGBa -import org.openrndr.math.* - -/** - * Polynomial approximation in GLSL for the Turbo colormap. - * - * @see turboColormapVector - * @see ColormapPhraseBook.turboColormap - */ -fun turboColormap( - x: Double -): ColorRGBa = ColorRGBa.fromVector( - turboColormapVector(x) -) - -/** - * Polynomial approximation in GLSL for the Turbo colormap. - * - * @see ColormapPhraseBook.turboColormap - */ -fun turboColormapVector(x: Double): Vector3 { - val v = saturate(x) - val v4 = Vector4( 1.0, v, v * v, v * v * v) - val v2 = Vector2(v4.z, v4.w) * v4.z - return Vector3( - v4.dot(kRedVec4) + v2.dot(kRedVec2), - v4.dot(kGreenVec4) + v2.dot(kGreenVec2), - v4.dot(kBlueVec4) + v2.dot(kBlueVec2) - ) -} - -private val kRedVec4 = Vector4(0.13572138, 4.61539260, -42.66032258, 132.13108234) -private val kGreenVec4 = Vector4(0.09140261, 2.19418839, 4.84296658, -14.18503333) -private val kBlueVec4 = Vector4(0.10667330, 12.64194608, -60.58204836, 110.36276771) -private val kRedVec2 = Vector2(-152.94239396, 59.28637943) -private val kGreenVec2 = Vector2(4.27729857, 2.82956604) -private val kBlueVec2 = Vector2(-89.90310912, 27.34824973) diff --git a/orx-color/src/commonMain/kotlin/colormatrix/ColorTransforms.kt b/orx-color/src/commonMain/kotlin/colormatrix/ColorTransforms.kt deleted file mode 100644 index b6299e9b..00000000 --- a/orx-color/src/commonMain/kotlin/colormatrix/ColorTransforms.kt +++ /dev/null @@ -1,138 +0,0 @@ -package org.openrndr.extra.color.colormatrix - -import org.openrndr.color.ColorRGBa -import org.openrndr.math.Matrix55 - -/** - * Creates a 5x5 matrix based on the given color values. - * - * @param color The color represented as an instance of ColorRGBa, where the r, g, b, and alpha - * components will be used to modify the matrix. - * @param ignoreAlpha A boolean flag indicating whether the alpha component of the color should be ignored. - * If true, the alpha value in the matrix will be set to 0.0. Defaults to true. - * @return A 5x5 matrix (Matrix55) with the r, g, b components set in the corresponding matrix columns, - * and the alpha value determined by the ignoreAlpha parameter. - */ -fun constant(color: ColorRGBa, ignoreAlpha: Boolean = true): Matrix55 { - return Matrix55.IDENTITY.copy(c4r0 = color.r, c4r1 = color.g, c4r2 = color.b, - c4r3 = if (ignoreAlpha) 0.0 else color.alpha - ) -} - -/** - * Applies a color tint transformation and returns a 5x5 matrix representing the transformation. - * - * @param color The `ColorRGBa` instance containing the red, green, blue, and alpha values of the color tint to apply. - * @return A 5x5 transformation matrix with the color tint applied based on the provided color. - */ -fun tint(color: ColorRGBa): Matrix55 { - return Matrix55(c0r0 = color.r, c1r1 = color.g, c2r2 = color.b, c3r3 = color.alpha, c4r4 = 1.0) -} - -/** - * A lazily initialized 5x5 matrix (Matrix55) representing a transformation matrix. - * The matrix is configured with specific coefficient values to perform an inversion transformation. - */ -val invert: Matrix55 by lazy { - Matrix55(c0r0 = -1.0, c1r1 = -1.0, c2r2 = -1.0, c3r3 = 1.0, c4r0 = 1.0, c4r1 = 1.0, c4r2 = 1.0, c4r3 = 0.0, c4r4 = 1.0) -} - -/** - * Creates a grayscale transformation matrix with the specified red, green, and blue coefficients. - * - * @param r The coefficient for the red channel. Default is 0.33. - * @param g The coefficient for the green channel. Default is 0.33. - * @param b The coefficient for the blue channel. Default is 0.33. - * @return A 5x5 matrix representing the grayscale transformation. - */ -fun grayscale(r: Double = 0.33, g: Double = 0.33, b: Double = 0.33): Matrix55 { - return Matrix55( - c0r0 = r, c1r0 = g, c2r0 = b, - c0r1 = r, c1r1 = g, c2r1 = b, - c0r2 = r, c1r2 = g, c2r2 = b, - c3r3 = 1.0, - c4r4 = 1.0) -} - -class ColorMatrixBuilder() { - @PublishedApi - internal var matrix = Matrix55.IDENTITY - - /** - * Applies a grayscale transformation to the current color matrix using the specified red, green, and blue coefficients. - * - * @param r The coefficient for the red channel. Default is 1.0/3.0. - * @param g The coefficient for the green channel. Default is 1.0/3.0. - * @param b The coefficient for the blue channel. Default is 1.0/3.0. - */ - fun grayscale(r: Double = 1.0/3.0, g: Double = 1.0/3.0, b: Double = 1.0/3.0) { - matrix *= org.openrndr.extra.color.colormatrix.grayscale(r, g, b) - } - - /** - * Adds a constant color transformation to the current color matrix. - * - * @param color The color to be added, represented as an instance of `ColorRGBa` with red, green, blue, and alpha components. - * @param ignoreAlpha A boolean flag indicating whether to ignore the alpha component of the color. - * If true, the alpha value in the matrix will be set to 0.0. Defaults to true. - */ - fun addConstant(color: ColorRGBa, ignoreAlpha: Boolean = true) { - matrix *= org.openrndr.extra.color.colormatrix.constant(color, ignoreAlpha) - } - - - /** - * Inverts the specified color channels in the current color matrix. - * - * @param invertR A boolean indicating whether to invert the red channel. Default is true. - * @param invertG A boolean indicating whether to invert the green channel. Default is true. - * @param invertB A boolean indicating whether to invert the blue channel. Default is true. - */ - fun invert(invertR: Boolean = true, invertG: Boolean = true, invertB: Boolean = true) { - matrix *= Matrix55( - c0r0 = if (invertR) -1.0 else 1.0, - c1r1 = if (invertG) -1.0 else 1.0, - c2r2 = if (invertB) -1.0 else 1.0, - c3r3 = 1.0, - c4r0 = if (invertR) 1.0 else 0.0, - c4r1 = if (invertG) 1.0 else 0.0, - c4r2 = if (invertB) 1.0 else 0.0, - c4r3 = 0.0, - c4r4 = 1.0 - ) - } - /** - * Applies a tint transformation to the color matrix using the specified color. - * - * @param color The `ColorRGBa` instance specifying the tint color, including its red, green, blue, and alpha components. - */ - fun tint(color: ColorRGBa) { - matrix *= org.openrndr.extra.color.colormatrix.tint(color) - } - - /** - * Multiplies the current transformation matrix with the specified 5x5 matrix. - * - * @param matrix A 5x5 matrix (Matrix55) to multiply with the current matrix. - */ - fun multiply(matrix: Matrix55) { - this.matrix *= matrix - } - - fun build(): Matrix55 { - return matrix - } -} - -/** - * Constructs a 5x5 color transformation matrix using the specified transformations - * defined within a [ColorMatrixBuilder] DSL. - * - * @param builder A lambda function with a receiver of type [ColorMatrixBuilder] used - * to define the series of color matrix transformations to apply. - * @return A [Matrix55] instance representing the resulting color transformation matrix - * after applying all specified operations in the builder. - */ -fun colorMatrix(builder: ColorMatrixBuilder.() -> Unit): Matrix55 { - return ColorMatrixBuilder().apply(builder).build() -} \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/fettepalette/FettePalette.kt b/orx-color/src/commonMain/kotlin/fettepalette/FettePalette.kt deleted file mode 100644 index a290799e..00000000 --- a/orx-color/src/commonMain/kotlin/fettepalette/FettePalette.kt +++ /dev/null @@ -1,256 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.color.fettepalette - -import org.openrndr.color.ColorHSLa -import org.openrndr.color.ColorHSVa -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.spaces.ColorOKHSLa -import org.openrndr.extra.color.spaces.ColorOKHSVa -import org.openrndr.extra.color.spaces.toOKHSLa -import org.openrndr.extra.parameters.* -import org.openrndr.math.Vector2 -import org.openrndr.math.clamp -import kotlin.math.* - -/* -Converted to Kotlin from https://github.com/meodai/fettepalette/blob/main/src/index.ts - -MIT License - -Copyright (c) 2021 David Aerne - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -interface Curve { - fun pointOnCurve( - i: Double, - total: Double, - curveAccent: Double, - min: Vector2 = Vector2.ZERO, - max: Vector2 = Vector2.ZERO - ): Vector2 - - fun remap(v: Vector2, min: Vector2, max: Vector2): Vector2 { - var x = v.x - var y = v.y - - x = min.x + x.coerceIn(0.0, 1.0) * (max.x - min.x) - y = min.y + y.coerceIn(0.0, 1.0) * (max.y - min.y) - - return Vector2(x, y) - } -} - -object Lamé : Curve { - override fun pointOnCurve(i: Double, total: Double, curveAccent: Double, min: Vector2, max: Vector2): Vector2 { - val limit = PI / 2 - val percentile = i / total - val t = percentile * limit - val exp = 2 / (2 + 20 * curveAccent) - val cosT = cos(t) - val sinT = sin(t) - val x = sign(cosT) * abs(cosT).pow(exp) - val y = sign(sinT) * abs(sinT).pow(exp) - return remap(Vector2(x, y), min, max) - } -} - -object Arc : Curve { - override fun pointOnCurve(i: Double, total: Double, curveAccent: Double, min: Vector2, max: Vector2): Vector2 { - val limit = PI / 2 - val slice = limit / total - val y = cos(-PI / 2 + i * slice + curveAccent) - val x = sin(PI / 2 + i * slice - curveAccent) - return remap(Vector2(x, y), min, max) - } -} - -class ColorRamp(val baseColors: List, val darkColors: List, val lightColors: List) - -@Description("Color ramp parameters") -class ColorRampParameters { - @IntParameter("total", 3, 16, 0) - var total = 3 - - @DoubleParameter("center hue", -180.0, 180.0, 3, 1) - var centerHue = 0.0 - - @DoubleParameter("hue cycle", 0.0, 1.0, 3, 2) - var hueCycle = 0.3 - - @DoubleParameter("offset tint", 0.0, 1.0, 3, 3) - var offsetTint = 0.1 - - @DoubleParameter("offset shade", 0.0, 1.0, 3, 4) - var offsetShade = 0.1 - - @DoubleParameter("curve accent", 0.0, 1.0, 3, 5) - var curveAccent = 0.0 - - @DoubleParameter("tint shade hue shift", 0.0, 1.0, 3, 6) - var tintShadeHueShift = 0.1 - - @DoubleParameter("offset mod tint", 0.0, 1.0, 3, 7) - var offsetCurveModTint = 0.03 - - @DoubleParameter("offset mod shade", 0.0, 1.0, 3, 8) - var offsetCurveModShade = 0.03 - - @Vector2Parameter("min saturation/light", min = 0.0, max = 1.0, precision = 3, order = 9) - var minSaturationLight = Vector2.ZERO - - @Vector2Parameter("max saturation/light", min = 0.0, max = 1.0, precision = 3, order = 10) - var maxSaturationLight = Vector2.ONE - - @BooleanParameter("use OKHSV", order = 11) - var useOK = false -} - - -/** - * @param total total of base colors in the ramp - * @param centerHue at what hue should the generation start at - * @param hueCycle hsl spins how much should the hue change over the curve, 0: not at all, 1: one full rainbow - * @param offsetTint offset for the tints - * @param offsetShade offset of the shades - * @param curveAccent how accentuated is the curve (depends heavily on curveMethod) - * @param tintShadeHueShift defines how shifted the hue is for the shades and the tints - * @param offsetCurveModTint modifies the tint curve - * @param offsetCurveModShade modifies the shade curve - * @param minSaturationLight defines the min saturation and light of all the colors - * @param maxSaturationLight defines the max saturation and light of all the colors - * @param useOK use OKHSV and OKHSL spaces - */ -fun generateColorRamp( - total: Int = 3, - centerHue: Double = 0.0, - hueCycle: Double = 0.3, - offsetTint: Double = 0.1, - offsetShade: Double = 0.1, - curveAccent: Double = 0.0, - tintShadeHueShift: Double = 0.1, - curveMethod: Curve = Lamé, - offsetCurveModTint: Double = 0.03, - offsetCurveModShade: Double = 0.03, - minSaturationLight: Vector2 = Vector2.ZERO, - maxSaturationLight: Vector2 = Vector2.ONE, - useOK: Boolean = false, -): ColorRamp { - val baseColors = mutableListOf() - val lightColors = mutableListOf() - val darkColors = mutableListOf() - - val okHueAdjust = if (useOK) 30.0 else 0.0 - - for (i in 1 until total + 1) { - val (x, y) = curveMethod.pointOnCurve( - i.toDouble(), - total + 1.0, - curveAccent, - minSaturationLight, - maxSaturationLight - ) - val h = (okHueAdjust + 360.0 + - (-180.0 * hueCycle + (centerHue + i * (360 / (total + 1)) * hueCycle)) - ) % 360 - - val hsv = if (useOK) { - ColorOKHSVa(h, x, y) - } else ColorHSVa(h, x, y) - val hsl = if (useOK) { - hsv.toRGBa().toOKHSLa() - } else hsv.toRGBa().toHSLa() - baseColors.add(hsl.toRGBa().toSRGB()) - - val (xl, yl) = curveMethod.pointOnCurve( - i.toDouble(), total + 1.0, curveAccent + offsetCurveModTint, - minSaturationLight, - maxSaturationLight - ) - - val hslLight = if (useOK) ColorOKHSVa(h, xl, yl).toRGBa().toOKHSLa() else ColorHSVa(h, xl, yl).toRGBa().toHSLa() - - if (useOK) { - hslLight as ColorOKHSLa - lightColors.add( - ColorOKHSLa( - (hslLight.h + 360.0 * tintShadeHueShift).mod(360.0), - (hslLight.s - offsetTint).clamp(0.0, 1.0), - (hslLight.l + offsetTint).clamp(0.0, 1.0) - ).toRGBa().toSRGB() - ) - } else { - hslLight as ColorHSLa - lightColors.add( - ColorHSLa( - (hslLight.h + 360.0 * tintShadeHueShift).mod(360.0), - (hslLight.s - offsetTint).clamp(0.0, 1.0), - (hslLight.l + offsetTint).clamp(0.0, 1.0) - ).toRGBa().toSRGB() - ) - } - - val (xd, yd) = curveMethod.pointOnCurve( - i.toDouble(), total + 1.0, curveAccent - offsetCurveModShade, - minSaturationLight, - maxSaturationLight - ) - - val hslDark = if (useOK) ColorOKHSVa(h, xd, yd).toRGBa().toOKHSLa() else ColorHSVa(h, xd, yd).toRGBa().toHSLa() - - if (useOK) { - hslDark as ColorOKHSLa - darkColors.add( - ColorOKHSLa( - (hslDark.h - 360.0 * tintShadeHueShift).mod(360.0), - (hslDark.s - offsetShade).clamp(0.0, 1.0), - (hslDark.l - offsetShade).clamp(0.0, 1.0) - ).toRGBa().toSRGB() - ) - } else { - hslDark as ColorHSLa - darkColors.add( - ColorHSLa( - (hslDark.h - 360.0 * tintShadeHueShift).mod(360.0), - (hslDark.s - offsetShade).clamp(0.0, 1.0), - (hslDark.l - offsetShade).clamp(0.0, 1.0) - ).toRGBa().toSRGB() - ) - } - } - return ColorRamp(baseColors, darkColors, lightColors) -} - -fun generateColorRamp(parameters: ColorRampParameters) - = generateColorRamp(total = parameters.total, - centerHue = parameters.centerHue, - hueCycle = parameters.hueCycle, - offsetTint = parameters.offsetTint, - offsetShade = parameters.offsetShade, - curveAccent = parameters.curveAccent, - tintShadeHueShift = parameters.tintShadeHueShift, - curveMethod = Lamé, - offsetCurveModTint = parameters.offsetCurveModTint, - offsetCurveModShade = parameters.offsetCurveModShade, - minSaturationLight = parameters.minSaturationLight, - maxSaturationLight = parameters.maxSaturationLight, - useOK = parameters.useOK -) \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/mixing/Spectral.kt b/orx-color/src/commonMain/kotlin/mixing/Spectral.kt deleted file mode 100644 index 54ceef74..00000000 --- a/orx-color/src/commonMain/kotlin/mixing/Spectral.kt +++ /dev/null @@ -1,612 +0,0 @@ -package org.openrndr.extra.color.mixing - -import org.openrndr.color.ColorRGBa -import org.openrndr.color.ColorXYZa -import org.openrndr.extra.color.tools.matchLinearity - -import kotlin.math.max -import kotlin.math.min -import kotlin.math.pow -import kotlin.math.sqrt - -/* -Direct port of https://github.com/rvanwijnen/spectral.js - */ - -/** - * Represents spectral power distribution (SPD) coefficients for the cyan channel, - * spanning 38 wavelength samples. This array is used as part of the spectral upsampling - * process to convert a linear RGB color into a reflectance spectrum. - * - * The coefficients are predefined values that define the contribution of the cyan channel - * at specific wavelengths to the resulting reflectance spectrum. - */ -private val SPD_C = doubleArrayOf( - 0.96853629, - 0.96855103, - 0.96859338, - 0.96877345, - 0.96942204, - 0.97143709, - 0.97541862, - 0.98074186, - 0.98580992, - 0.98971194, - 0.99238027, - 0.99409844, - 0.995172, - 0.99576545, - 0.99593552, - 0.99564041, - 0.99464769, - 0.99229579, - 0.98638762, - 0.96829712, - 0.89228016, - 0.53740239, - 0.15360445, - 0.05705719, - 0.03126539, - 0.02205445, - 0.01802271, - 0.0161346, - 0.01520947, - 0.01475977, - 0.01454263, - 0.01444459, - 0.01439897, - 0.0143762, - 0.01436343, - 0.01435687, - 0.0143537, - 0.01435408 -) - -/** - * Represents the spectral power distribution (SPD) data for the magenta color component. - * This array contains 38 predefined values corresponding to specific wavelengths. - * Used in the process of spectral upsampling to compute reflectance spectra from RGB color information. - */ -private val SPD_M = doubleArrayOf( - 0.51567122, - 0.5401552, - 0.62645502, - 0.75595012, - 0.92826996, - 0.97223624, - 0.98616174, - 0.98955255, - 0.98676237, - 0.97312575, - 0.91944277, - 0.32564851, - 0.13820628, - 0.05015143, - 0.02912336, - 0.02421691, - 0.02660696, - 0.03407586, - 0.04835936, - 0.0001172, - 0.00008554, - 0.85267882, - 0.93188793, - 0.94810268, - 0.94200977, - 0.91478045, - 0.87065445, - 0.78827548, - 0.65738359, - 0.59909403, - 0.56817268, - 0.54031997, - 0.52110241, - 0.51041094, - 0.50526577, - 0.5025508, - 0.50126452, - 0.50083021 -) - -/** - * Spectral power distribution (SPD) values for the Yellow primary in the spectral upsampling process. - * This array contains 38 precomputed reflectance values corresponding to specific wavelength samples. - * It is used to calculate the reflectance spectrum when converting a linear RGB color to its spectral representation. - */ -private val SPD_Y = doubleArrayOf( - 0.02055257, - 0.02059936, - 0.02062723, - 0.02073387, - 0.02114202, - 0.02233154, - 0.02556857, - 0.03330189, - 0.05185294, - 0.10087639, - 0.24000413, - 0.53589066, - 0.79874659, - 0.91186529, - 0.95399623, - 0.97137099, - 0.97939505, - 0.98345207, - 0.98553736, - 0.98648905, - 0.98674535, - 0.98657555, - 0.98611877, - 0.98559942, - 0.98507063, - 0.98460039, - 0.98425301, - 0.98403909, - 0.98388535, - 0.98376116, - 0.98368246, - 0.98365023, - 0.98361309, - 0.98357259, - 0.98353856, - 0.98351247, - 0.98350101, - 0.98350852 -) - -/** - * A predefined spectral power distribution (SPD) array for the red channel, - * used in the spectral upsampling process to convert linear RGB colors into reflectance spectra. - * - * This array contains 38 values corresponding to specific wavelengths and represents - * the relative spectral contribution of the red channel in the conversion process. - * - * The SPD_R array is utilized in conjunction with other SPDs (e.g., SPD_C, SPD_M, SPD_Y, SPD_G, SPD_B) - * and the weights derived from spectral upsampling to calculate the reflectance spectrum - * for a given color. - */ -private val SPD_R = doubleArrayOf( - 0.03147571, - 0.03146636, - 0.03140624, - 0.03119611, - 0.03053888, - 0.02856855, - 0.02459485, - 0.0192952, - 0.01423112, - 0.01033111, - 0.00765876, - 0.00593693, - 0.00485616, - 0.00426186, - 0.00409039, - 0.00438375, - 0.00537525, - 0.00772962, - 0.0136612, - 0.03181352, - 0.10791525, - 0.46249516, - 0.84604333, - 0.94275572, - 0.96860996, - 0.97783966, - 0.98187757, - 0.98377315, - 0.98470202, - 0.98515481, - 0.98537114, - 0.98546685, - 0.98550011, - 0.98551031, - 0.98550741, - 0.98551323, - 0.98551563, - 0.98551547 -) - -/** - * Represents the predefined spectral power distribution (SPD) values for the green component - * used in spectral upsampling of linear RGB colors. - * - * This array contains 38 reflectance values corresponding to specific wavelengths and - * reflects the spectral characteristics of the green primary in the color model. - * - * It is utilized as one of the SPD datasets in the linearToReflectance function, which - * converts linear RGB colors into reflectance spectra. - */ -private val SPD_G = doubleArrayOf( - 0.49108579, - 0.46944057, - 0.4016578, - 0.2449042, - 0.0682688, - 0.02732883, - 0.013606, - 0.01000187, - 0.01284127, - 0.02636635, - 0.07058713, - 0.70421692, - 0.85473994, - 0.95081565, - 0.9717037, - 0.97651888, - 0.97429245, - 0.97012917, - 0.9425863, - 0.99989207, - 0.99989891, - 0.13823139, - 0.06968113, - 0.05628787, - 0.06111561, - 0.08987709, - 0.13656016, - 0.22169624, - 0.32176956, - 0.36157329, - 0.4836192, - 0.46488579, - 0.47440306, - 0.4857699, - 0.49267971, - 0.49625685, - 0.49807754, - 0.49889859 -) - -/** - * Represents the spectral power distribution (SPD) values corresponding to the blue component - * of a linear RGB color. The array contains 38 precomputed reflectance values - * that span a specific range of wavelengths. These values are utilized in the spectral - * upsampling process to map an RGB color to its equivalent spectral reflectance distribution. - */ -private val SPD_B = doubleArrayOf( - 0.97901834, - 0.97901649, - 0.97901118, - 0.97892146, - 0.97858555, - 0.97743705, - 0.97428075, - 0.96663223, - 0.94822893, - 0.89937713, - 0.76070164, - 0.4642044, - 0.20123039, - 0.08808402, - 0.04592894, - 0.02860373, - 0.02060067, - 0.01656701, - 0.01451549, - 0.01357964, - 0.01331243, - 0.01347661, - 0.01387181, - 0.01435472, - 0.01479836, - 0.0151525, - 0.01540513, - 0.01557233, - 0.0156571, - 0.01571025, - 0.01571916, - 0.01572133, - 0.01572502, - 0.01571717, - 0.01571905, - 0.01571059, - 0.01569728, - 0.0157002 -) - -/** - * A pre-defined array representing the CIE 1931 Standard Observer's color matching function values for the X component. - * This data is used in color science calculations to transform spectral reflectance data into the CIE XYZ color space. - * The array contains 38 discrete samples corresponding to wavelengths within the visible spectrum. - * - * This constant is specifically used during computations involving spectral data to calculate the X component - * of the CIE XYZ color space via multiplication with a corresponding reflectance spectrum array. - */ -private val CIE_CMF_X = doubleArrayOf( - 0.00006469, - 0.00021941, - 0.00112057, - 0.00376661, - 0.01188055, - 0.02328644, - 0.03455942, - 0.03722379, - 0.03241838, - 0.02123321, - 0.01049099, - 0.00329584, - 0.00050704, - 0.00094867, - 0.00627372, - 0.01686462, - 0.02868965, - 0.04267481, - 0.05625475, - 0.0694704, - 0.08305315, - 0.0861261, - 0.09046614, - 0.08500387, - 0.07090667, - 0.05062889, - 0.03547396, - 0.02146821, - 0.01251646, - 0.00680458, - 0.00346457, - 0.00149761, - 0.0007697, - 0.00040737, - 0.00016901, - 0.00009522, - 0.00004903, - 0.00002 -) - -/** - * Represents the Y-component of the CIE 1931 color matching functions. - * - * The CIE 1931 color matching functions are used to convert spectral power distributions into - * CIE XYZ tristimulus values, which represent a color in a perceptually-uniform color space. - * These functions are defined over 38 discrete wavelength samples, typically covering - * the visible spectrum. - * - * The Y-component corresponds to the luminous efficiency function, - * which describes the sensitivity of human vision to different wavelengths of light. - */ -private val CIE_CMF_Y = doubleArrayOf( - 0.00000184, - 0.00000621, - 0.00003101, - 0.00010475, - 0.00035364, - 0.00095147, - 0.00228226, - 0.00420733, - 0.0066888, - 0.0098884, - 0.01524945, - 0.02141831, - 0.03342293, - 0.05131001, - 0.07040208, - 0.08783871, - 0.09424905, - 0.09795667, - 0.09415219, - 0.08678102, - 0.07885653, - 0.0635267, - 0.05374142, - 0.04264606, - 0.03161735, - 0.02088521, - 0.01386011, - 0.00810264, - 0.0046301, - 0.00249138, - 0.0012593, - 0.00054165, - 0.00027795, - 0.00014711, - 0.00006103, - 0.00003439, - 0.00001771, - 0.00000722 -) - -/** - * Represents the Z component of the CIE 1931 2° Standard Observer Color Matching Function (CMF). - * - * This array contains precomputed values representing the spectral sensitivity of the human eye's Z cone. - * It is used in color science to convert spectral reflectance data into the Z component of the CIE XYZ color space. - * - * The values in this array correspond to sampling points across the visible light spectrum - * and are used in conjunction with `CIE_CMF_X` and `CIE_CMF_Y` to perform reflectance spectrum to XYZ color conversions. - */ -private val CIE_CMF_Z = doubleArrayOf( - 0.00030502, - 0.00103681, - 0.00531314, - 0.01795439, - 0.05707758, - 0.11365162, - 0.17335873, - 0.19620658, - 0.18608237, - 0.13995048, - 0.08917453, - 0.04789621, - 0.02814563, - 0.01613766, - 0.0077591, - 0.00429615, - 0.00200551, - 0.00086147, - 0.00036904, - 0.00019143, - 0.00014956, - 0.00009231, - 0.00006813, - 0.00002883, - 0.00001577, - 0.00000394, - 0.00000158, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 -) - -/** - * Converts RGB color values into a form that represents spectral power distribution weights. - * - * @param rgb The RGB color in the form of a `ColorRGBa` object, which is to be converted to spectral weights. - * @return A `DoubleArray` representing the decomposed weights for white, cyan, magenta, yellow, red, green, and blue. - */ -private fun spectralUpsampling(rgb: ColorRGBa): DoubleArray { - var lrgb = rgb.toLinear() - val w = min(min(lrgb.r, lrgb.g), lrgb.b) - - lrgb = lrgb.copy(r = lrgb.r - w, g = lrgb.g - w, lrgb.b - w) - - val c = max(0.0, min(lrgb.g, lrgb.b)) - val m = max(0.0, min(lrgb.r, lrgb.b)) - val y = max(0.0, min(lrgb.r, lrgb.g)) - val r = max(0.0, min(lrgb.r - lrgb.b, lrgb.r - lrgb.g)) - val g = max(0.0, min(lrgb.g - lrgb.b, lrgb.g - lrgb.r)) - val b = max(0.0, min(lrgb.b - lrgb.g, lrgb.b - lrgb.r)) - - return doubleArrayOf(max(0.0, w), c, m, y, r, g, b) -} - - -/** - * Converts a linear RGB color into a reflectance spectrum represented as a `DoubleArray`. - * The resulting reflectance spectrum spans 38 wavelength samples - * and is computed using spectral upsampling based on predefined spectral distributions. - * - * @param rgb The linear RGB color to be converted, represented as a `ColorRGBa` object. - * @return A `DoubleArray` containing 38 reflectance values corresponding to the wavelengths. - */ -internal fun linearToReflectance(rgb: ColorRGBa): DoubleArray { - val eps = 0.00000001 - val weights = spectralUpsampling(rgb) - val reflectance = DoubleArray(38) - - for (i in 0 until 38) { - reflectance[i] = max( - eps, - weights[0] + - weights[1] * SPD_C[i] + - weights[2] * SPD_M[i] + - weights[3] * SPD_Y[i] + - weights[4] * SPD_R[i] + - weights[5] * SPD_G[i] + - weights[6] * SPD_B[i] - ) - } - return reflectance -} - -/** - * Computes the concentration of a component in a linear interpolation based on the specified parameters. - * - * The method calculates a concentration factor `c` using two linear values `l1` and `l2`, - * as well as a blend factor `t`. This can be used in scenarios such as spectral mixing - * or interpolation tasks where weights are dynamically computed. - * - * @param l1 The first linear value, typically derived from reflectance or intensity. - * @param l2 The second linear value, typically derived from reflectance or intensity. - * @param t The blending factor in the range [0.0, 1.0], where 0.0 represents full influence of `l1` - * and 1.0 represents full influence of `l2`. - * @return The computed concentration factor as a `Double`, representing the relative contribution - * of the components based on the blending factor. - */ -private fun linearToConcentration(l1: Double, l2: Double, t: Double): Double { - val t1 = l1 * (1 - t).pow(2.0) - val t2 = l2 * t.pow(2.0) - - return t2 / (t1 + t2) -} - -private fun DoubleArray.dot(other: DoubleArray): Double { - var d = 0.0 - for (i in indices) { - d += this[i] * other[i] - } - return d -} - -/** - * Converts a reflectance spectrum represented as a `DoubleArray` to a color in the CIE XYZ color space. - * - * This method calculates the XYZ color by performing a dot product operation between the reflectance spectrum - * and the CIE color matching functions (CIE_CMF_X, CIE_CMF_Y, CIE_CMF_Z). The result is returned as a `ColorXYZa` object. - * - * @param reflectance An array of reflectance spectrum values, typically spanning the visible spectrum. - * This is represented as a `DoubleArray` with 38 wavelength samples. - * @return A `ColorXYZa` object representing the corresponding color in the CIE XYZ color space. - */ -internal fun reflectanceToXYZ(reflectance: DoubleArray): ColorXYZa { - val x = reflectance.dot(CIE_CMF_X) - val y = reflectance.dot(CIE_CMF_Y) - val z = reflectance.dot(CIE_CMF_Z) - return ColorXYZa(x, y, z) -} - -private fun pow(x: Double, y: Double): Double = x.pow(y) - -/** - * Applies the Saunderson correction to a reflectance value to account for surface reflectance effects. - * - * Saunderson correction adjusts the measured reflectance by considering front surface reflection - * and internal reflections in the material. The correction is based on two coefficients, `k1` and `k2`. - * - * @param rInf The measured reflectance value to be corrected. - * @param k1 The first Saunderson coefficient representing front surface reflection. - * @param k2 The second Saunderson coefficient representing internal reflection effects. - * @return The corrected reflectance value as a `Double`. - */ -internal fun saundersonCorrection(rInf: Double, k1: Double, k2: Double): Double { - return k1 + ((1 - k1) * (1 - k2) * rInf) / (1 - (k2 * rInf)) -} - -/** - * Blends two colors spectrally by interpolating their reflectance spectra and returns the resulting color. - * This method uses spectral upsampling, Saunderson correction, and concentration factors to compute - * the resulting color in the RGB color space. - * - * @param color1 The first color to be mixed, represented as a `ColorRGBa`. - * @param color2 The second color to be mixed, represented as a `ColorRGBa`. - * @param t The blending factor in the range [0.0, 1.0], where 0.0 represents full influence of `color1` - * and 1.0 represents full influence of `color2`. - * @param k1 The first Saunderson correction coefficient for surface reflection. Default is 0.0. - * @param k2 The second Saunderson correction coefficient for internal reflections. Default is 0.0. - * @return The resulting blended color as a `ColorRGBa`, maintaining the linearity of the first input color (`color1`). - */ -fun mixSpectral(color1: ColorRGBa, color2: ColorRGBa, t: Double, k1: Double = 0.0, k2: Double = 0.0): ColorRGBa { - val lrgb1 = color1.toLinear() - val lrgb2 = color2.toLinear() - - val ref1 = linearToReflectance(lrgb1) - val ref2 = linearToReflectance(lrgb2) - - val l1 = ref1.dot(CIE_CMF_Y) - val l2 = ref2.dot(CIE_CMF_Y) - - val c = linearToConcentration(l1, l2, t) - - val reflectance = DoubleArray(38) - - for (i in 0 until 38) { - val ks = - (1.0 - c) * (pow(1.0 - ref1[i], 2.0) / (2.0 * ref1[i])) + c * (pow( - 1.0 - ref2[i], - 2.0 - ) / (2.0 * ref2[i])) - val km = (1.0 + ks - sqrt(pow(ks, 2.0) + 2.0 * ks)) - - reflectance[i] = saundersonCorrection(km, k1, k2) - } - - val xyz = reflectanceToXYZ(reflectance) - return xyz.toRGBa().matchLinearity(color1) -} \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/palettes/Classics.kt b/orx-color/src/commonMain/kotlin/palettes/Classics.kt deleted file mode 100644 index 36c6fa2b..00000000 --- a/orx-color/src/commonMain/kotlin/palettes/Classics.kt +++ /dev/null @@ -1,92 +0,0 @@ -package org.openrndr.extra.color.palettes - -import org.openrndr.color.ColorModel -import org.openrndr.color.ColorRGBa -import org.openrndr.color.HueShiftableColor -import org.openrndr.extra.color.tools.shiftHue - -/** - * Generates an analogous color palette based on the current color. - * - * This function creates a sequence of colors by shifting the hue of the current color - * gradually across a specified range of steps, using a particular color model that supports hue shifting. - * - * @param T The color model used for hue shifting. - * Must extend both `HueShiftableColor` and `ColorModel`. - * @param hueShift The total degree shift in hue between the first color and the last color. - * The hue shift is divided among the specified number of steps. - * @param steps The number of colors to include in the palette, including the starting color. - * Defaults to 5. - * @return A list of `ColorRGBa` instances forming the analogous palette. - */ -inline fun ColorRGBa.analogous(hueShift: Double, steps: Int = 5): List - where T : HueShiftableColor, - T : ColorModel = (0 until steps).map { - shiftHue(hueShift * it / (steps - 1.0)) -} - -/** - * Generates a split complementary color palette based on the current `ColorRGBa`. - * - * The method calculates complementary colors that are spread around the complementary - * hue axis of the original color. Depending on the parameters, the result may include - * two or four additional colors in addition to the original color. - * - * @param T The color model and hue shifting capability of the colors to generate. - * @param splitFactor A value between 0.0 and 1.0 that controls the spread of the complementary colors - * around the complementary hue. A higher value increases the angle between - * the colors on the hue wheel, while a lower value decreases it. - * @param double If `true`, the method will generate two additional colors derived by more granular - * shifts within the complementary range. If `false`, a simpler complementary palette - * is returned. - * @return A list of `ColorRGBa` objects representing the split complementary palette, with the original - * color as the first element in the list. - */ -inline fun ColorRGBa.splitComplementary(splitFactor: Double, double: Boolean = false): List - where T : HueShiftableColor, - T : ColorModel { - val c0 = shiftHue(180 - 180.0 * splitFactor) - val c1 = shiftHue(180 + 180.0 * splitFactor) - - if (!double) { - return listOf(this, c0, c1) - } else { - val c2 = shiftHue(180.0 * splitFactor) - val c3 = shiftHue(-180.0 * splitFactor) - return listOf(this, c0, c1, c2, c3) - } -} - - -/** - * Generates a triadic color palette based on the current `ColorRGBa`. - * - * Triadic colors are evenly spaced on the color wheel, forming a triangle. - * This method generates two additional colors by evenly shifting the hue of the given color - * at 120° intervals around the hue circle. - * - * @param T The color model and hue shifting capability of the colors to generate. - * @return A list of `ColorRGBa` objects representing the triadic color palette. - */ -inline fun ColorRGBa.triadic(): List - where T : HueShiftableColor, - T : ColorModel = splitComplementary(1.0 / 3.0) - - -/** - * Generates a tetradic color scheme based on the current color. - * A tetradic color scheme consists of four colors that are equidistant on the color wheel. - * - * @param aspectRatio A double value representing the aspect ratio of the tetradic scheme. - * The aspect ratio determines the angular separation between the colors in the scheme. - * Default is 1.0, resulting in equidistant colors. - * @return A list of `ColorRGBa` instances representing the tetradic color scheme. - * The list includes the original color and three additional colors derived by shifting the hue. - */ -inline fun ColorRGBa.tetradic(aspectRatio: Double = 1.0): List - where T : HueShiftableColor, - T : ColorModel { - val a0 = 180 / (aspectRatio + 1) - val a1 = 180 - a0 - return listOf(this, shiftHue(a0), shiftHue(a0 + a1), shiftHue(a0 + a1 + a0)) -} diff --git a/orx-color/src/commonMain/kotlin/palettes/ColorSequence.kt b/orx-color/src/commonMain/kotlin/palettes/ColorSequence.kt deleted file mode 100644 index 835ded5b..00000000 --- a/orx-color/src/commonMain/kotlin/palettes/ColorSequence.kt +++ /dev/null @@ -1,143 +0,0 @@ -package org.openrndr.extra.color.palettes - -import org.openrndr.color.* -import org.openrndr.draw.* -import org.openrndr.extra.color.spaces.* - - -/** - * Creates a `ColorSequence` by accepting a variable number of pairs, where each pair consists of - * a position (Double) and a color (T). The positions represent the normalized range `[0.0, 1.0]`. - * The resulting `ColorSequence` can be used for creating interpolated colors between the specified positions. - * - * @param offsets Vararg parameter of pairs, where each pair includes a position (Double) and a color (of type T). - * The position defines the location along a normalized sequence `[0.0, 1.0]`, and the color must implement `ConvertibleToColorRGBa`. - * Typically, positions must be sorted, but the function will sort them internally based on their position values. - * @return A `ColorSequence` containing the sorted sequence of colors and positions. - */ -fun colorSequence(vararg offsets: Pair): ColorSequence - where T : ConvertibleToColorRGBa { - return ColorSequence(offsets.sortedBy { it.first }) -} - -/** - * Represents a sequence of colors along with their corresponding positions in a normalized range [0.0, 1.0]. - * The `ColorSequence` allows for creating interpolated colors between the specified color points. - * - * @property colors A list of pairs where the first value is a position (ranging from 0.0 to 1.0) - * and the second value is a color that implements `ConvertibleToColorRGBa`. - */ -class ColorSequence(val colors: List>) { - infix fun blend(steps: Int): List = color(0.0, 1.0, steps) - - /** - * Converts a color sequence into a color buffer with a gradient representation. - * - * @param drawer The Drawer used to render the gradient into the color buffer. - * @param width The width of the resulting color buffer in pixels. Defaults to 256. - * @param height The height of the resulting color buffer in pixels. Defaults to 16. - * @param type The ColorType of the resulting color buffer. Defaults to UINT8_SRGB. - * @param format The ColorFormat of the resulting color buffer. Defaults to RGBa. - * @return A ColorBuffer containing the rendered color gradient. - */ - fun toColorBuffer( - drawer: Drawer, - width: Int = 256, - height: Int = 16, - type: ColorType = ColorType.UINT8_SRGB, - format: ColorFormat = ColorFormat.RGBa - ): ColorBuffer { - val cb = colorBuffer(width, height, type = type, format = format) - val rt = renderTarget(width, height) { - colorBuffer(cb) - } - - drawer.isolatedWithTarget(rt) { - defaults() - ortho(rt) - drawer.rectangles { - for (i in 0 until width) { - fill = color(i / (width.toDouble() - 1.0)) - stroke = null - rectangle(i * 1.0, 0.0, 1.0, height.toDouble()) - } - } - } - - rt.destroy() - return cb - } - - /** - * Generates a sequence of interpolated colors between two specified values. - * - * @param t0 A Double representing the start value for interpolation. - * @param t1 A Double representing the end value for interpolation. - * @param steps An Int representing the number of colors to generate in the sequence. - * @return A List of interpolated colors. - */ - fun color(t0: Double, t1: Double, steps: Int) = (0 until steps).map { - val f = (it / (steps - 1.0)) - val t = t0 * (1.0 - f) + t1 * f - color(t) - } - - /** - * Calculates a color using interpolation based on the provided parameter `t`. - * - * @param t A Double representing the position along the color sequence, typically ranging from 0.0 to 1.0. - * It indicates how far between the sequence colors the interpolation should occur, - * with 0.0 being the start of the sequence and 1.0 being the end. - * @return A ColorRGBa instance representing the interpolated color in the sRGB color space. - * If the provided `t` is outside the range of the sequence, the color at the nearest boundary will be returned. - */ - fun color(t: Double): ColorRGBa { - if (colors.size == 1) { - return colors.first().second.toRGBa().toSRGB() - } - if (t < colors[0].first) { - return colors[0].second.toRGBa().toSRGB() - } - if (t >= colors.last().first) { - return colors.last().second.toRGBa().toSRGB() - } - val rightIndex = colors.binarySearch { it.first.compareTo(t) }.let { if (it < 0) -it - 2 else it } - val leftIndex = (rightIndex + 1).coerceIn(0, colors.size - 1) - - val right = colors[rightIndex] - val left = colors[leftIndex] - - val rt = t - right.first - val dt = left.first - right.first - val nt = rt / dt - - return when (val l = left.second) { - is ColorRGBa -> right.second.toRGBa().mix(l, nt) - is ColorHSVa -> right.second.toRGBa().toHSVa().mix(l, nt).toRGBa() - is ColorHSLa -> right.second.toRGBa().toHSLa().mix(l, nt).toRGBa() - is ColorXSVa -> right.second.toRGBa().toXSVa().mix(l, nt).toRGBa() - is ColorXSLa -> right.second.toRGBa().toXSLa().mix(l, nt).toRGBa() - is ColorLABa -> right.second.toRGBa().toLABa().mix(l, nt).toRGBa() - is ColorLUVa -> right.second.toRGBa().toLUVa().mix(l, nt).toRGBa() - is ColorHSLUVa -> right.second.toRGBa().toHSLUVa().mix(l, nt).toRGBa() - is ColorHPLUVa -> right.second.toRGBa().toHPLUVa().mix(l, nt).toRGBa() - is ColorXSLUVa -> right.second.toRGBa().toXSLUVa().mix(l, nt).toRGBa() - is ColorLCHUVa -> right.second.toRGBa().toLCHUVa().mix(l, nt).toRGBa() - is ColorLCHABa -> right.second.toRGBa().toLCHABa().mix(l, nt).toRGBa() - is ColorOKLABa -> right.second.toRGBa().toOKLABa().mix(l, nt).toRGBa() - is ColorOKLCHa -> right.second.toRGBa().toOKLCHa().mix(l, nt).toRGBa() - is ColorOKHSLa -> right.second.toRGBa().toOKHSLa().mix(l, nt).toRGBa() - is ColorOKHSVa -> right.second.toRGBa().toOKHSVa().mix(l, nt).toRGBa() - else -> error("unsupported color space: ${l::class}") - }.toSRGB() - } -} - -/** - * Defines a range between two colors by creating a sequence of colors - * that transition smoothly from the start color to the end color. - * - * @param end The end color of the range. Both start and end colors must implement `ConvertibleToColorRGBa`. - * The start color is implicitly the color on which this operator is called. - */ -operator fun ConvertibleToColorRGBa.rangeTo(end: ConvertibleToColorRGBa) = colorSequence(0.0 to this, 1.0 to end) diff --git a/orx-color/src/commonMain/kotlin/phrases/Phrases.kt b/orx-color/src/commonMain/kotlin/phrases/Phrases.kt deleted file mode 100644 index 7177158e..00000000 --- a/orx-color/src/commonMain/kotlin/phrases/Phrases.kt +++ /dev/null @@ -1,88 +0,0 @@ -package org.openrndr.extra.color.phrases - -import org.openrndr.extra.shaderphrases.ShaderPhrase -import org.openrndr.extra.shaderphrases.ShaderPhraseBook - -val oklabToLinearRgbPhrase = """#ifndef ORX_COLOR_OKLAB_TO_LINEAR_RGB_PHRASE - |#define ORX_COLOR_OKLAB_TO_LINEAR_RGB_PHRASE - |vec4 oklab_to_linear_rgb(vec4 lab) { - | const mat3 kLMStoCONE = mat3( - | 1.0, 1.0, 1.0, - | 0.3963377774, -0.1055613458, -0.0894841775, - | 0.2158037573, -0.0638541728, -1.2914855480); - | const mat3 kRot = mat3( - | 4.0767416621, -1.2684380046, -0.0041960863, - | -3.3077115913, 2.6097574011, -0.7034186147, - | 0.2309699292, -0.3413193965, 1.7076147010); - | vec3 lms = kLMStoCONE * lab.rgb; - | lms = lms * lms * lms; - | vec4 res = vec4(kRot * lms,lab.a); - | return res; - |} - |#endif""".trimMargin() - -val linearRgbToOklabPhrase = """ - |#ifndef ORX_COLOR_LINEAR_RGB_TO_OKLAB_PHRASE - |#define ORX_COLOR_LINEAR_RGB_TO_OKLAB_PHRASE - |vec4 linear_rgb_to_oklab(vec4 c) { - | c.rgb = max(vec3(0.0), c.rgb); - | const mat3 kCONEtoLMS = mat3( - | 0.4122214708, 0.2119034982, 0.0883024619, - | 0.5363325363, 0.6806995451, 0.2817188376, - | 0.0514459929, 0.1073969566, 0.6299787005); - | - | const mat3 kRot = mat3( - | 0.2104542553, 1.9779984951, 0.0259040371, - | 0.7936177850, -2.4285922050, 0.7827717662, - | -0.0040720468, 0.4505937099, -0.8086757660); - | vec3 lms = pow(kCONEtoLMS * c.rgb, vec3(1.0/3.0)); - | vec4 res = vec4((kRot) * lms, c.a); - | return res; - |} - |#endif""".trimMargin() - -object ColorPhraseBook : ShaderPhraseBook("color") { - val phraseAtan2 = ShaderPhrase(""" - |float atan2(in float y, in float x) { - | bool s = (abs(x) > abs(y)); - | return mix(PI/2.0 - atan(x,y), atan(y,x), float(s)); - |}""".trimMargin()) - - val phraseLinearRgbToOKLab = ShaderPhrase(linearRgbToOklabPhrase) - - val oklabToLinearRgb = ShaderPhrase(oklabToLinearRgbPhrase) - - val phraseLabToLch = ShaderPhrase( """ - |vec4 lab_to_lch(vec4 lab) { - | float r = length(lab.yz); - | float h = atan2(lab[2], lab[1]); - | return vec4(lab[0], c, h, lab.a); - |}""".trimMargin()) - - val phraseLchToLab = ShaderPhrase(""" - |vec4 lch_to_lab(vec4 lch) { - | float a = lch[1] * cos(lch[2]); - | float b = lch[1] * sin(lch[2]); - | return vec4(lab[0], a, b, lab.a); - |}""".trimMargin()) - - val linearRgbToSRgb = ShaderPhrase(""" - |vec4 linear_rgb_to_srgb(vec4 c) { - | const float t = 0.00313066844250063; - | return vec4( - | c.r <= t ? c.r * 12.92 : 1.055 * pow(c.r, 1.0 / 2.4) - 0.055, - | c.g <= t ? c.g * 12.92 : 1.055 * pow(c.g, 1.0 / 2.4) - 0.055, - | c.b <= t ? c.b * 12.92 : 1.055 * pow(c.b, 1.0 / 2.4) - 0.055, - | c.a); - |}""".trimMargin()) - - val phraseSRgbToLinearRgb = ShaderPhrase(""" - |vec4 srgb_to_linear_rgb(vec4 c) { - | const float t = 0.0404482362771082; - | return vec4( - | c.r <= t ? c.r / 12.92 : pow( (c.r + 0.055) / 1.055, 2.4), - | c.g <= t ? c.g / 12.92 : pow( (c.g + 0.055) / 1.055, 2.4), - | c.b <= t ? c.b / 12.92 : pow( (c.b + 0.055) / 1.055, 2.4), - | c.a); - |}""".trimMargin()) -} \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/presets/Colors.kt b/orx-color/src/commonMain/kotlin/presets/Colors.kt deleted file mode 100644 index fa1df8b6..00000000 --- a/orx-color/src/commonMain/kotlin/presets/Colors.kt +++ /dev/null @@ -1,140 +0,0 @@ -@file:Suppress("unused") - -package org.openrndr.extra.color.presets - -import org.openrndr.color.ColorRGBa -import org.openrndr.color.rgb - -val ColorRGBa.Companion.ALICE_BLUE by lazy { rgb(0.941176, 0.972549, 1.0) } -val ColorRGBa.Companion.ANTIQUE_WHITE by lazy { rgb(0.980392, 0.921569, 0.843137) } -val ColorRGBa.Companion.AQUA by lazy { rgb(0.0, 1.0, 1.0) } -val ColorRGBa.Companion.AQUAMARINE by lazy { rgb(0.498039, 1.0, 0.831373) } -val ColorRGBa.Companion.AZURE by lazy { rgb(0.941176, 1.0, 1.0) } -val ColorRGBa.Companion.BEIGE by lazy { rgb(0.960784, 0.960784, 0.862745) } -val ColorRGBa.Companion.BISQUE by lazy { rgb(1.0, 0.894118, 0.768627) } -val ColorRGBa.Companion.BLANCHED_ALMOND by lazy { rgb(1.0, 0.921569, 0.803922) } -val ColorRGBa.Companion.BLUE_VIOLET by lazy { rgb(0.541176, 0.168627, 0.886275) } -val ColorRGBa.Companion.BROWN by lazy { rgb(0.647059, 0.164706, 0.164706) } -val ColorRGBa.Companion.BURLY_WOOD by lazy { rgb(0.870588, 0.721569, 0.529412) } -val ColorRGBa.Companion.CADET_BLUE by lazy { rgb(0.372549, 0.619608, 0.627451) } -val ColorRGBa.Companion.CHARTREUSE by lazy { rgb(0.498039, 1.0, 0.0) } -val ColorRGBa.Companion.CHOCOLATE by lazy { rgb(0.823529, 0.411765, 0.117647) } -val ColorRGBa.Companion.CORAL by lazy { rgb(1.0, 0.498039, 0.313726) } -val ColorRGBa.Companion.CORNFLOWER_BLUE by lazy { rgb(0.392157, 0.584314, 0.929412) } -val ColorRGBa.Companion.CORN_SILK by lazy { rgb(1.0, 0.972549, 0.862745) } -val ColorRGBa.Companion.CRIMSON by lazy { rgb(0.862745, 0.0784314, 0.235294) } -val ColorRGBa.Companion.DARK_BLUE by lazy { rgb(0.0, 0.0, 0.545098) } -val ColorRGBa.Companion.DARK_CYAN by lazy { rgb(0.0, 0.545098, 0.545098) } -val ColorRGBa.Companion.DARK_GOLDEN_ROD by lazy { rgb(0.721569, 0.52549, 0.0431373) } -val ColorRGBa.Companion.DARK_GRAY by lazy { rgb(0.662745, 0.662745, 0.662745) } -val ColorRGBa.Companion.DARK_GREY by lazy { rgb(0.662745, 0.662745, 0.662745) } -val ColorRGBa.Companion.DARK_GREEN by lazy { rgb(0.0, 0.392157, 0.0) } -val ColorRGBa.Companion.DARK_KHAKI by lazy { rgb(0.741176, 0.717647, 0.419608) } -val ColorRGBa.Companion.DARK_MAGENTA by lazy { rgb(0.545098, 0.0, 0.545098) } -val ColorRGBa.Companion.DARK_OLIVE_GREEN by lazy { rgb(0.333333, 0.419608, 0.184314) } -val ColorRGBa.Companion.DARK_ORANGE by lazy { rgb(1.0, 0.54902, 0.0) } -val ColorRGBa.Companion.DARK_ORCHID by lazy { rgb(0.6, 0.196078, 0.8) } -val ColorRGBa.Companion.DARK_RED by lazy { rgb(0.545098, 0.0, 0.0) } -val ColorRGBa.Companion.DARK_SALMON by lazy { rgb(0.913725, 0.588235, 0.478431) } -val ColorRGBa.Companion.DARK_SEA_GREEN by lazy { rgb(0.560784, 0.737255, 0.560784) } -val ColorRGBa.Companion.DARK_SLATE_BLUE by lazy { rgb(0.282353, 0.239216, 0.545098) } -val ColorRGBa.Companion.DARK_SLATE_GRAY by lazy { rgb(0.184314, 0.309804, 0.309804) } -val ColorRGBa.Companion.DARK_TURQUOISE by lazy { rgb(0.0, 0.807843, 0.819608) } -val ColorRGBa.Companion.DARK_VIOLET by lazy { rgb(0.580392, 0.0, 0.827451) } -val ColorRGBa.Companion.DEEP_PINK by lazy { rgb(1.0, 0.0784314, 0.576471) } -val ColorRGBa.Companion.DEEP_SKY_BLUE by lazy { rgb(0.0, 0.74902, 1.0) } -val ColorRGBa.Companion.DIM_GRAY by lazy { rgb(0.411765, 0.411765, 0.411765) } -val ColorRGBa.Companion.DODGER_BLUE by lazy { rgb(0.117647, 0.564706, 1.0) } -val ColorRGBa.Companion.FIREBRICK by lazy { rgb(0.698039, 0.133333, 0.133333) } -val ColorRGBa.Companion.FLORAL_WHITE by lazy { rgb(1.0, 0.980392, 0.941176) } -val ColorRGBa.Companion.FOREST_GREEN by lazy { rgb(0.133333, 0.545098, 0.133333) } -val ColorRGBa.Companion.FUCHSIA by lazy { rgb(1.0, 0.0, 1.0) } -val ColorRGBa.Companion.GAINSBORO by lazy { rgb(0.862745, 0.862745, 0.862745) } -val ColorRGBa.Companion.GHOST_WHITE by lazy { rgb(0.972549, 0.972549, 1.0) } -val ColorRGBa.Companion.GOLD by lazy { rgb(1.0, 0.843137, 0.0) } -val ColorRGBa.Companion.GOLDENROD by lazy { rgb(0.854902, 0.647059, 0.12549) } -val ColorRGBa.Companion.GREY by lazy { rgb(0.501961, 0.501961, 0.501961) } -val ColorRGBa.Companion.GREEN_YELLOW by lazy { rgb(0.678431, 1.0, 0.184314) } -val ColorRGBa.Companion.HONEYDEW by lazy { rgb(0.941176, 1.0, 0.941176) } -val ColorRGBa.Companion.HOT_PINK by lazy { rgb(1.0, 0.411765, 0.705882) } -val ColorRGBa.Companion.INDIAN_RED by lazy { rgb(0.803922, 0.360784, 0.360784) } -val ColorRGBa.Companion.INDIGO by lazy { rgb(0.294118, 0.0, 0.509804) } -val ColorRGBa.Companion.IVORY by lazy { rgb(1.0, 1.0, 0.941176) } -val ColorRGBa.Companion.KHAKI by lazy { rgb(0.941176, 0.901961, 0.54902) } -val ColorRGBa.Companion.LAVENDER by lazy { rgb(0.901961, 0.901961, 0.980392) } -val ColorRGBa.Companion.LAVENDER_BLUSH by lazy { rgb(1.0, 0.941176, 0.960784) } -val ColorRGBa.Companion.LAWN_GREEN by lazy { rgb(0.486275, 0.988235, 0.0) } -val ColorRGBa.Companion.LEMON_CHIFFON by lazy { rgb(1.0, 0.980392, 0.803922) } -val ColorRGBa.Companion.LIGHT_BLUE by lazy { rgb(0.678431, 0.847059, 0.901961) } -val ColorRGBa.Companion.LIGHT_CORAL by lazy { rgb(0.941176, 0.501961, 0.501961) } -val ColorRGBa.Companion.LIGHT_CYAN by lazy { rgb(0.878431, 1.0, 1.0) } -val ColorRGBa.Companion.LIGHT_GOLDEN_ROD_YELLOW by lazy { rgb(0.980392, 0.980392, 0.823529) } -val ColorRGBa.Companion.LIGHT_GRAY by lazy { rgb(0.827451, 0.827451, 0.827451) } -val ColorRGBa.Companion.LIGHT_GREEN by lazy { rgb(0.564706, 0.933333, 0.564706) } -val ColorRGBa.Companion.LIGHT_PINK by lazy { rgb(1.0, 0.713726, 0.756863) } -val ColorRGBa.Companion.LIGHT_SALMON by lazy { rgb(1.0, 0.627451, 0.478431) } -val ColorRGBa.Companion.LIGHT_SEA_GREEN by lazy { rgb(0.12549, 0.698039, 0.666667) } -val ColorRGBa.Companion.LIGHT_SKY_BLUE by lazy { rgb(0.529412, 0.807843, 0.980392) } -val ColorRGBa.Companion.LIGHT_SLATE_GRAY by lazy { rgb(0.466667, 0.533333, 0.6) } -val ColorRGBa.Companion.LIGHT_STEEL_BLUE by lazy { rgb(0.690196, 0.768627, 0.870588) } -val ColorRGBa.Companion.LIGHT_YELLOW by lazy { rgb(1.0, 1.0, 0.878431) } -val ColorRGBa.Companion.LIME by lazy { rgb(0.0, 1.0, 0.0) } -val ColorRGBa.Companion.LIME_GREEN by lazy { rgb(0.196078, 0.803922, 0.196078) } -val ColorRGBa.Companion.LINEN by lazy { rgb(0.980392, 0.941176, 0.901961) } -val ColorRGBa.Companion.MAROON by lazy { rgb(0.501961, 0.0, 0.0) } -val ColorRGBa.Companion.MEDIUM_AQUAMARINE by lazy { rgb(0.4, 0.803922, 0.666667) } -val ColorRGBa.Companion.MEDIUM_BLUE by lazy { rgb(0.0, 0.0, 0.803922) } -val ColorRGBa.Companion.MEDIUM_ORCHID by lazy { rgb(0.729412, 0.333333, 0.827451) } -val ColorRGBa.Companion.MEDIUM_PURPLE by lazy { rgb(0.576471, 0.439216, 0.858824) } -val ColorRGBa.Companion.MEDIUM_SEA_GREEN by lazy { rgb(0.235294, 0.701961, 0.443137) } -val ColorRGBa.Companion.MEDIUM_SLATE_BLUE by lazy { rgb(0.482353, 0.407843, 0.933333) } -val ColorRGBa.Companion.MEDIUM_SPRING_GREEN by lazy { rgb(0.0, 0.980392, 0.603922) } -val ColorRGBa.Companion.MEDIUM_TURQUOISE by lazy { rgb(0.282353, 0.819608, 0.8) } -val ColorRGBa.Companion.MEDIUM_VIOLET_RED by lazy { rgb(0.780392, 0.0823529, 0.521569) } -val ColorRGBa.Companion.MIDNIGHT_BLUE by lazy { rgb(0.0980392, 0.0980392, 0.439216) } -val ColorRGBa.Companion.MINT_CREAM by lazy { rgb(0.960784, 1.0, 0.980392) } -val ColorRGBa.Companion.MISTY_ROSE by lazy { rgb(1.0, 0.894118, 0.882353) } -val ColorRGBa.Companion.MOCCASIN by lazy { rgb(1.0, 0.894118, 0.709804) } -val ColorRGBa.Companion.NAVAJO_WHITE by lazy { rgb(1.0, 0.870588, 0.678431) } -val ColorRGBa.Companion.NAVY by lazy { rgb(0.0, 0.0, 0.501961) } -val ColorRGBa.Companion.OLD_LACE by lazy { rgb(0.992157, 0.960784, 0.901961) } -val ColorRGBa.Companion.OLIVE by lazy { rgb(0.501961, 0.501961, 0.0) } -val ColorRGBa.Companion.OLIVE_DRAB by lazy { rgb(0.419608, 0.556863, 0.137255) } -val ColorRGBa.Companion.ORANGE by lazy { rgb(1.0, 0.647059, 0.0) } -val ColorRGBa.Companion.ORANGE_RED by lazy { rgb(1.0, 0.270588, 0.0) } -val ColorRGBa.Companion.ORCHID by lazy { rgb(0.854902, 0.439216, 0.839216) } -val ColorRGBa.Companion.PALE_GOLDEN_ROD by lazy { rgb(0.933333, 0.909804, 0.666667) } -val ColorRGBa.Companion.PALE_GREEN by lazy { rgb(0.596078, 0.984314, 0.596078) } -val ColorRGBa.Companion.PALE_TURQUOISE by lazy { rgb(0.686275, 0.933333, 0.933333) } -val ColorRGBa.Companion.PALE_VIOLET_RED by lazy { rgb(0.858824, 0.439216, 0.576471) } -val ColorRGBa.Companion.PAPAYA_WHIP by lazy { rgb(1.0, 0.937255, 0.835294) } -val ColorRGBa.Companion.PEACH_PUFF by lazy { rgb(1.0, 0.854902, 0.72549) } -val ColorRGBa.Companion.PERU by lazy { rgb(0.803922, 0.521569, 0.247059) } -val ColorRGBa.Companion.PLUM by lazy { rgb(0.866667, 0.627451, 0.866667) } -val ColorRGBa.Companion.POWDER_BLUE by lazy { rgb(0.690196, 0.878431, 0.901961) } -val ColorRGBa.Companion.PURPLE by lazy { rgb(0.501961, 0.0, 0.501961) } -val ColorRGBa.Companion.ROSY_BROWN by lazy { rgb(0.737255, 0.560784, 0.560784) } -val ColorRGBa.Companion.ROYAL_BLUE by lazy { rgb(0.254902, 0.411765, 0.882353) } -val ColorRGBa.Companion.SADDLE_BROWN by lazy { rgb(0.545098, 0.270588, 0.0745098) } -val ColorRGBa.Companion.SALMON by lazy { rgb(0.980392, 0.501961, 0.447059) } -val ColorRGBa.Companion.SANDY_BROWN by lazy { rgb(0.956863, 0.643137, 0.376471) } -val ColorRGBa.Companion.SEA_GREEN by lazy { rgb(0.180392, 0.545098, 0.341176) } -val ColorRGBa.Companion.SEASHELL by lazy { rgb(1.0, 0.960784, 0.933333) } -val ColorRGBa.Companion.SIENNA by lazy { rgb(0.627451, 0.321569, 0.176471) } -val ColorRGBa.Companion.SILVER by lazy { rgb(0.752941, 0.752941, 0.752941) } -val ColorRGBa.Companion.SKY_BLUE by lazy { rgb(0.529412, 0.807843, 0.921569) } -val ColorRGBa.Companion.SLATE_BLUE by lazy { rgb(0.415686, 0.352941, 0.803922) } -val ColorRGBa.Companion.SLATE_GRAY by lazy { rgb(0.439216, 0.501961, 0.564706) } -val ColorRGBa.Companion.SNOW by lazy { rgb(1.0, 0.980392, 0.980392) } -val ColorRGBa.Companion.SPRING_GREEN by lazy { rgb(0.0, 1.0, 0.498039) } -val ColorRGBa.Companion.STEEL_BLUE by lazy { rgb(0.27451, 0.509804, 0.705882) } -val ColorRGBa.Companion.BLUE_STEEL by lazy { rgb(0.27451, 0.509804, 0.705882) } -val ColorRGBa.Companion.TAN by lazy { rgb(0.823529, 0.705882, 0.54902) } -val ColorRGBa.Companion.TEAL by lazy { rgb(0.0, 0.501961, 0.501961) } -val ColorRGBa.Companion.THISTLE by lazy { rgb(0.847059, 0.74902, 0.847059) } -val ColorRGBa.Companion.TOMATO by lazy { rgb(1.0, 0.388235, 0.278431) } -val ColorRGBa.Companion.TURQUOISE by lazy { rgb(0.25098, 0.878431, 0.815686) } -val ColorRGBa.Companion.VIOLET by lazy { rgb(0.933333, 0.509804, 0.933333) } -val ColorRGBa.Companion.WHEAT by lazy { rgb(0.960784, 0.870588, 0.701961) } -val ColorRGBa.Companion.WHITE_SMOKE by lazy { rgb(0.960784, 0.960784, 0.960784) } -val ColorRGBa.Companion.YELLOW_GREEN by lazy { rgb(0.603922, 0.803922, 0.196078) } \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorHPLUVa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorHPLUVa.kt deleted file mode 100644 index e60dac93..00000000 --- a/orx-color/src/commonMain/kotlin/spaces/ColorHPLUVa.kt +++ /dev/null @@ -1,96 +0,0 @@ -package org.openrndr.extra.color.spaces - -import kotlinx.serialization.Serializable -import org.openrndr.color.* -import org.openrndr.math.Vector4 -import org.openrndr.math.mixAngle -import kotlin.jvm.JvmRecord - -/** - * Represents a color in the HPLUVa (Hue, Perceptual Lightness, Saturation, Alpha) color space. - * This color space is based on perceptual uniformity, making it suitable for operations - * like interpolation, shading, and manipulation of hue, saturation, and lightness values. - * - * @property h The hue component of the color, representing the angle on the color wheel in degrees [0, 360). - * @property s The saturation component of the color, representing the intensity of the color [0.0, 1.0]. - * @property l The lightness component of the color, representing the relative brightness [0.0, 1.0]. - * @property alpha The alpha (opacity) component of the color, ranging from fully transparent (0.0) to fully opaque (1.0). - */ -@Serializable -@JvmRecord -data class ColorHPLUVa(val h: Double, val s: Double, val l: Double, override val alpha: Double = 1.0) : - ColorModel, - HueShiftableColor, - SaturatableColor, - ShadableColor, - LuminosityColor, - AlgebraicColor { - fun toLCHUVa(): ColorLCHUVa { - val l1 = l - if (l1 > 0.9999999) { - return ColorLCHUVa(100.0, 0.0, h) - } - if (l1 < 0.00000001) { - return ColorLCHUVa(0.0, 0.0, h) - } - val l100 = l1 * 100.0 - val max100 = maxSafeChromaForL(l100) - val c100 = max100 * s - return ColorLCHUVa(l100, c100, h) - } - - override val hue: Double - get() = h - override fun withHue(hue: Double) = copy(h = hue) - - - override fun shade(factor: Double): ColorHPLUVa = copy(l = l * factor) - - override val saturation: Double - get() = s - - override fun withSaturation(saturation: Double): ColorHPLUVa = copy(s = saturation) - - override fun toRGBa(): ColorRGBa = toLCHUVa().toRGBa() - - override fun opacify(factor: Double) = copy(alpha = alpha * factor) - - override fun minus(right: ColorHPLUVa) = copy(h = h - right.h, s = s - right.s, l = l - right.l, alpha = alpha - right.alpha) - - override fun plus(right: ColorHPLUVa) = copy(h = h + right.h, s = s + right.s, l = l + right.l, alpha = alpha + right.alpha) - - override fun times(scale: Double) = copy(h = h * scale, s = s * scale, l = l * scale, alpha = alpha * scale) - - override fun mix(other: ColorHPLUVa, factor: Double) = mix(this, other, factor) - - override fun toVector4(): Vector4 = Vector4(h, s, l, alpha) - override val luminosity: Double - get() = l - - override fun withLuminosity(luminosity: Double): ColorHPLUVa = copy(l = luminosity) -} - -fun mix(left: ColorHPLUVa, right: ColorHPLUVa, x: Double): ColorHPLUVa { - val sx = x.coerceIn(0.0, 1.0) - return ColorHPLUVa( - mixAngle(left.h, right.h, sx), - (1.0 - sx) * left.s + sx * right.s, - (1.0 - sx) * left.l + sx * right.l, - (1.0 - sx) * left.alpha + sx * right.alpha) -} - -fun ColorRGBa.toHPLUVa(): ColorHPLUVa = toLCHUVa().toHPLUVa() - -fun ColorLCHUVa.toHPLUVa(): ColorHPLUVa { - val l100 = l - if (l100 > 99.9999999) { - return ColorHPLUVa(h, 0.0, 1.0) - } - if (l100 < 0.00000001) { - return ColorHPLUVa(h, 0.0, 0.0) - - } - val max100 = maxSafeChromaForL(l) - val s1 = c / max100 - return ColorHPLUVa(h, s1, l100 / 100.0) -} diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt deleted file mode 100644 index 61778449..00000000 --- a/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt +++ /dev/null @@ -1,203 +0,0 @@ -package org.openrndr.extra.color.spaces - -import kotlinx.serialization.Serializable -import org.openrndr.color.* -import org.openrndr.math.Vector4 -import org.openrndr.math.map -import org.openrndr.math.mixAngle -import kotlin.jvm.JvmRecord -import kotlin.math.* - -private val m = arrayOf( - doubleArrayOf(3.240969941904521, -1.537383177570093, -0.498610760293), - doubleArrayOf(-0.96924363628087, 1.87596750150772, 0.041555057407175), - doubleArrayOf(0.055630079696993, -0.20397695888897, 1.056971514242878)) - -private const val kappa = 903.2962962 -private const val epsilon = 0.0088564516 - -private fun getBounds(L: Double): List { - val result = ArrayList() - val sub1 = (L + 16).pow(3.0) / 1560896 - val sub2 = if (sub1 > epsilon) sub1 else L / kappa - for (c in 0..2) { - val m1 = m[c][0] - val m2 = m[c][1] - val m3 = m[c][2] - for (t in 0..1) { - val top1 = (284517 * m1 - 94839 * m3) * sub2 - val top2 = (838422 * m3 + 769860 * m2 + 731718 * m1) * L * sub2 - 769860 * t * L - val bottom = (632260 * m3 - 126452 * m2) * sub2 + 126452 * t - result.add(doubleArrayOf(top1 / bottom, top2 / bottom)) - } - } - return result -} - -private fun intersectLineLine(lineA: DoubleArray, lineB: DoubleArray): Double { - return (lineA[1] - lineB[1]) / (lineB[0] - lineA[0]) -} - -private fun distanceFromPole(point: DoubleArray): Double { - return sqrt(point[0].pow(2.0) + point[1].pow(2.0)) -} - -private fun lengthOfRayUntilIntersect(theta: Double, line: DoubleArray): Length { - val length = line[1] / (sin(theta) - line[0] * cos(theta)) - return Length(length) -} - -private class Length(val length: Double) { - val greaterEqualZero: Boolean = length >= 0 -} - -internal fun maxSafeChromaForL(L100: Double): Double { - val bounds = getBounds(L100) - var min = Double.MAX_VALUE - for (i in 0..1) { - val m1 = bounds[i][0] - val b1 = bounds[i][1] - val line = doubleArrayOf(m1, b1) - val x = intersectLineLine(line, doubleArrayOf(-1 / m1, 0.0)) - val length = distanceFromPole(doubleArrayOf(x, b1 + x * m1)) - min = min(min, length) - } - return min -} - -private fun maxChromaForLH(L100: Double, H: Double): Double { - val hrad = H / 360 * PI * 2 - val bounds = getBounds(L100) - var min = Double.MAX_VALUE - for (bound in bounds) { - val length: Length = lengthOfRayUntilIntersect(hrad, bound) - if (length.greaterEqualZero) { - min = min(min, length.length) - } - } - return min -} - - -/** - * Represents a color in the HSLuv color space with an alpha transparency component. - * HSLuv is a perceptually uniform color space, where hues are uniformly distributed - * and the perception of color is consistent across the spectrum. - * - * @property h The hue of the color in degrees, ranging from 0.0 to 360.0. - * @property s The saturation of the color, ranging from 0.0 to 1.0. - * @property l The luminance of the color, ranging from 0.0 to 1.0. - * @property alpha The alpha transparency value, ranging from 0.0 (fully transparent) to 1.0 (fully opaque). - */ -@Serializable -@JvmRecord -data class ColorHSLUVa(val h: Double, val s: Double, val l: Double, override val alpha: Double = 1.0) : - ColorModel, - HueShiftableColor, - SaturatableColor, - ShadableColor, - LuminosityColor, - AlgebraicColor { - - - fun toLCHUVa(): ColorLCHUVa { - - val l100 = l * 100.0 - val s100 = s * 100.0 - - if (l100 > 99.9999999) { - ColorLCHUVa(100.0, 0.0, h, alpha) - } - - if (l100 < 0.00000001) { - ColorLCHUVa(0.0, 0.0, h, alpha) - } - val max100 = maxChromaForLH(l100, h) - - val c: Double = max100 / 100 * s100 - - return ColorLCHUVa(l100, c, h, alpha) - } - - fun toXSLUVa() : ColorXSLUVa { - return ColorXSLUVa(hueToX(h), s, l, alpha) - } - - override val hue: Double - get() = h - - override fun withHue(hue: Double): ColorHSLUVa = copy(h = hue) - override fun shade(factor: Double) = copy(l = l * factor) - override val saturation: Double - get() = s - - override fun withSaturation(saturation: Double): ColorHSLUVa = copy(s = saturation) - - override fun toRGBa(): ColorRGBa { - return toLCHUVa().toRGBa() - } - - override fun opacify(factor: Double) = copy(alpha = alpha * factor) - - override fun minus(right: ColorHSLUVa) = copy(h = h - right.h, s = s - right.s, l = l - right.l, alpha = alpha - right.alpha) - - override fun plus(right: ColorHSLUVa) = copy(h = h + right.h, s = s + right.s, l = l + right.l, alpha = alpha + right.alpha) - - override fun times(scale: Double) = copy(h = h * scale, s = s * scale, l = l * scale, alpha = alpha * scale) - - override fun mix(other: ColorHSLUVa, factor: Double) = mix(this, other, factor) - - override fun toVector4(): Vector4 = Vector4(h, s, l, alpha) - override val luminosity: Double - get() = l - - override fun withLuminosity(luminosity: Double): ColorHSLUVa = copy(l = luminosity) - -} - -fun mix(left: ColorHSLUVa, right: ColorHSLUVa, x: Double): ColorHSLUVa { - val sx = x.coerceIn(0.0, 1.0) - return ColorHSLUVa( - mixAngle(left.h, right.h, sx), - (1.0 - sx) * left.s + sx * right.s, - (1.0 - sx) * left.l + sx * right.l, - (1.0 - sx) * left.alpha + sx * right.alpha) -} - -internal fun map(x: Double, a: Double, b: Double, c: Double, d: Double): Double { - return ((x - a) / (b - a)) * (d - c) + c -} - -fun hueToX(hue:Double): Double { - val h = hue.mod(360.0) - return if (0 <= h && h < 35) { - h.map(0.0, 35.0, 0.0, 60.0) - } else if (35 <= h && h < 60) { - h.map(35.0, 60.0, 60.0, 120.0) - } else if (60 <= h && h < 135.0) { - h.map(60.0, 135.0, 120.0, 180.0) - } else if (135.0 <= h && h < 225.0) { - h.map(135.0, 225.0, 180.0, 240.0) - } else if (225.0 <= h && h < 275.0) { - h.map( 225.0, 275.0, 240.0, 300.0) - } else { - h.map( 275.0, 360.0, 300.0, 360.0) - } -} - -fun ColorLCHUVa.toHSLUVa(): ColorHSLUVa { - val l100 = l - - if (l100 > 99.99999) { - return ColorHSLUVa(h, 0.0, 1.0) - } - if (l < 0.000001) { - return ColorHSLUVa(h, 0.0, 0.0) - } - val max100 = maxChromaForLH(l100, h) - val c100 = c - val s1 = c100 / max100 - return ColorHSLUVa(h, s1, l100 / 100.0, alpha) -} - -fun ColorRGBa.toHSLUVa(): ColorHSLUVa = toLCHUVa().toHSLUVa() diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorOKHSLa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorOKHSLa.kt deleted file mode 100644 index b7e146f0..00000000 --- a/orx-color/src/commonMain/kotlin/spaces/ColorOKHSLa.kt +++ /dev/null @@ -1,143 +0,0 @@ -package org.openrndr.extra.color.spaces - -import kotlinx.serialization.Serializable -import org.openrndr.color.* -import org.openrndr.math.Vector4 -import org.openrndr.math.mixAngle -import kotlin.jvm.JvmRecord -import kotlin.math.* - -/** - * Represents a color in the OKHSL (hue, saturation, lightness) color space with an alpha channel. - * This color model is based on perceptual uniformity and is useful for hue, saturation, and - * lightness manipulations while maintaining consistency with human vision. - * - * @property h The hue of the color, represented as a value in degrees [0.0, 360.0). - * @property s The saturation of the color, where 0.0 is fully desaturated (gray) and 1.0 is fully saturated. - * @property l The lightness of the color, where 0.0 is completely dark and 1.0 is completely light. - * @property alpha The opacity of the color, where 0.0 is fully transparent and 1.0 is fully opaque. - */ -@Suppress("LocalVariableName") -@Serializable -@JvmRecord -data class ColorOKHSLa(val h: Double, val s: Double, val l: Double, override val alpha: Double = 1.0) : - ColorModel, - HueShiftableColor, - SaturatableColor, - ShadableColor, - AlgebraicColor { - - companion object { - fun fromColorRGBa(c: ColorRGBa): ColorOKHSLa { - val lab = c.toOKLABa() - val C = sqrt(lab.a * lab.a + lab.b * lab.b) - val a_ = lab.a / C - val b_ = lab.b / C - - val L = lab.l - val h = 0.5 + 0.5 * atan2(-lab.b, -lab.a) / PI - - val (c0, cMid, cMax) = get_Cs(L, a_, b_) - - val s = if (C < cMid) { - val k0 = 0 - val k1 = 0.8 * c0 - val k2 = (1 - k1 / cMid) - - val t = (C - k0) / (k1 + k2 * (C - k0)) - t * 0.8 - } else { - val k0 = cMid - val k1 = 0.2 * cMid * cMid * 1.25 * 1.25 / c0 - val k2 = (1 - (k1) / (cMax - cMid)) - - val t = (C - k0) / (k1 + k2 * (C - k0)) - 0.8 + 0.2 * t - } - val l = toe(L) - return ColorOKHSLa( - h * 360.0, - if (s == s) s else 0.0, - if (l == l) l else 0.0, - c.alpha - ) - } - } - - - override fun toRGBa(): ColorRGBa { - if (l == 0.0 || l == 1.0) { - return ColorRGBa(l, l, l, alpha, Linearity.SRGB) - } - val a_ = cos(2 * PI * h / 360.0) - val b_ = sin(2 * PI * h / 360.0) - val L = toeInv(l) - - val Cs = get_Cs(L, a_, b_) - val C_0 = Cs[0] - val C_mid = Cs[1] - val C_max = Cs[2] - - val C = if (s < 0.8) { - val t = 1.25 * s - val k_0 = 0.0 - val k_1 = 0.8 * C_0 - val k_2 = (1 - k_1 / C_mid) - k_0 + t * k_1 / (1 - k_2 * t) - } else { - val t = 5 * (s - 0.8) - val k_0 = C_mid - val k_1 = 0.2 * C_mid * C_mid * 1.25 * 1.25 / C_0 - val k_2 = (1 - (k_1) / (C_max - C_mid)) - k_0 + t * k_1 / (1 - k_2 * t) - } - - // If we would only use one of the Cs: - //C = s*C_0; - //C = s*1.25*C_mid; - //C = s*C_max; - - return ColorOKLABa( - if (L == L) L else 0.0, - if (C == C) C * a_ else 0.0, - if (C == C) C * b_ else 0.0, - alpha - ).toRGBa().toSRGB() - } - - override val hue: Double - get() = h - - override fun withHue(hue: Double): ColorOKHSLa = copy(h = hue) - - override fun opacify(factor: Double): ColorOKHSLa = copy(alpha = alpha * factor) - override val saturation: Double - get() = s - - override fun withSaturation(saturation: Double): ColorOKHSLa = copy(s = saturation) - - override fun shade(factor: Double): ColorOKHSLa = copy(l = l * factor) - - override fun minus(right: ColorOKHSLa) = - copy(h = h - right.h, s = s - right.s, l = l - right.l, alpha = alpha - right.alpha) - - override fun plus(right: ColorOKHSLa) = - copy(h = h + right.h, s = s + right.s, l = l + right.l, alpha = alpha + right.alpha) - - override fun times(scale: Double): ColorOKHSLa = - copy(h = h * scale, s = s * scale, l = l * scale, alpha = alpha * scale) - - override fun mix(other: ColorOKHSLa, factor: Double): ColorOKHSLa { - val sx = factor.coerceIn(0.0, 1.0) - return ColorOKHSLa( - mixAngle(h, other.h, sx), - (1.0 - sx) * s + sx * other.s, - (1.0 - sx) * l + sx * other.l, - (1.0 - sx) * alpha + sx * other.alpha - ) - } - - override fun toVector4(): Vector4 = Vector4(h, s, l, alpha) -} - -fun ColorRGBa.toOKHSLa(): ColorOKHSLa = ColorOKHSLa.fromColorRGBa(this) \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorOKHSVa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorOKHSVa.kt deleted file mode 100644 index 6fa61443..00000000 --- a/orx-color/src/commonMain/kotlin/spaces/ColorOKHSVa.kt +++ /dev/null @@ -1,150 +0,0 @@ -package org.openrndr.extra.color.spaces - -import kotlinx.serialization.Serializable -import org.openrndr.color.* -import org.openrndr.math.Vector4 -import org.openrndr.math.mixAngle -import kotlin.jvm.JvmRecord -import kotlin.math.* - -/** - * Represents a color in the OKHSVa color model. - * - * The OKHSVa color model is derived from OKLABa and provides a perceptually uniform representation - * of colors using hue (h), saturation (s), value (v), and alpha (opacity). - * - * This class supports operations and transformations such as conversion to and from RGBa, - * hue shifting, saturation adjustment, shading, and algebraic operations like addition, subtraction, - * and scaling. It is ideal for working with colors in contexts requiring accurate color mixing - * and perceptual results. - * - * @property h Hue value in degrees (0.0 - 360.0), representing the color's angle on the color wheel. - * @property s Saturation value (0.0 - 1.0), representing the intensity or purity of the color. - * @property v Value (0.0 - 1.0), representing the color's brightness. - * @property alpha Opacity value (0.0 - 1.0), with 1.0 being fully opaque. - */ -@Suppress("LocalVariableName") -@Serializable -@JvmRecord -data class ColorOKHSVa(val h: Double, val s: Double, val v: Double, override val alpha: Double = 1.0) : - ColorModel, - HueShiftableColor, - SaturatableColor, - ShadableColor, - AlgebraicColor { - - companion object { - fun fromColorRGBa(c: ColorRGBa): ColorOKHSVa { - val lab = c.toOKLABa() - var C = sqrt(lab.a * lab.a + lab.b * lab.b) - val a_ = if (C != 0.0) lab.a / C else 0.0 - val b_ = if (C != 0.0) lab.b / C else 0.0 - - var L = lab.l - val h = 0.5 + 0.5 * atan2(-lab.b, -lab.a) / PI - - val ST_max = get_ST_max(a_, b_) - val S_max = ST_max[0] - val S_0 = 0.5 - val T = ST_max[1] - val k = if (S_max != 0.0) (1 - S_0 / S_max) else 0.0 - - val t = T / (C + L * T) - val L_v = t * L - val C_v = t * C - - val L_vt = toeInv(L_v) - val C_vt = C_v * L_vt / L_v - - val rgb_scale = ColorOKLABa(L_vt, a_ * C_vt, b_ * C_vt, c.alpha).toRGBa().toLinear() - val scale_L = (1.0 / (max(rgb_scale.r, rgb_scale.g, rgb_scale.b, 0.0))).pow(1.0 / 3.0) - - L /= scale_L - C /= scale_L - - C = C * toe(L) / L - L = toe(L) - - val v = L / L_v - val s = (S_0 + T) * C_v / ((T * S_0) + T * k * C_v) - - return ColorOKHSVa(h * 360.0, if (s == s) s else 0.0, if (v == v) v else 0.0, c.alpha) - } - } - - override fun toRGBa(): ColorRGBa { - val a_ = cos(2 * PI * h / 360.0) - val b_ = sin(2 * PI * h / 360.0) - - val ST_max = get_ST_max(a_, b_) - val S_max = ST_max[0] - val S_0 = 0.5 - val T = ST_max[1] - val k = 1 - S_0 / S_max - - val L_v = 1 - s * S_0 / (S_0 + T - T * k * s) - val C_v = s * T * S_0 / (S_0 + T - T * k * s) - - var L = v * L_v - var C = v * C_v - - // to present steps along the way - //L = v; - //C = v*s*S_max; - //L = v*(1 - s*S_max/(S_max+T)); - //C = v*s*S_max*T/(S_max+T); - - val L_vt = toeInv(L_v) - val C_vt = C_v * L_vt / L_v - - val L_new = toeInv(L) // * L_v/L_vt; - C = C * L_new / L - L = L_new - - val rgb_scale = - ColorOKLABa(L_vt, a_ * C_vt, b_ * C_vt, alpha).toRGBa().toLinear()// oklab_to_linear_srgb(L_vt,a_*C_vt,b_*C_vt); - val scale_L = (1.0 / (max(rgb_scale.r, rgb_scale.g, rgb_scale.b, 0.0))).pow(1.0 / 3.0) - - // remove to see effect without rescaling - L *= scale_L - C *= scale_L - - return ColorOKLABa( - if (L == L) L else 0.0, - if (C == C) C * a_ else 0.0, - if (C == C) C * b_ else 0.0, - alpha - ).toRGBa().toSRGB() - } - - override val hue: Double - get() = h - - override fun withHue(hue: Double): ColorOKHSVa = copy(h = hue) - - override fun opacify(factor: Double): ColorOKHSVa = copy(alpha = alpha * factor) - override val saturation: Double - get() = s - override fun withSaturation(saturation: Double): ColorOKHSVa = copy(s = saturation) - - override fun shade(factor: Double): ColorOKHSVa = copy(v = v * factor) - override fun minus(right: ColorOKHSVa) = - copy(h = h - right.h, s = s - right.s, v = v - right.v, alpha = alpha - right.alpha) - override fun plus(right: ColorOKHSVa) = - copy(h = h + right.h, s = s + right.s, v = v + right.v, alpha = alpha + right.alpha) - override fun times(scale: Double): ColorOKHSVa = copy(h = h * scale, s = s * scale, v = v * scale, alpha = alpha * scale) - - override fun mix(other: ColorOKHSVa, factor: Double): ColorOKHSVa { - val sx = factor.coerceIn(0.0, 1.0) - return ColorOKHSVa( - mixAngle(h, other.h, sx), - (1.0 - sx) * s + sx * other.s, - (1.0 - sx) * v + sx * other.v, - (1.0 - sx) * alpha + sx * other.alpha - ) - } - - override fun toVector4(): Vector4 = Vector4(h, s, v, alpha) -} - -fun ColorRGBa.toOKHSVa(): ColorOKHSVa = ColorOKHSVa.fromColorRGBa(this) \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorOKLABa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorOKLABa.kt deleted file mode 100644 index 6267e83c..00000000 --- a/orx-color/src/commonMain/kotlin/spaces/ColorOKLABa.kt +++ /dev/null @@ -1,86 +0,0 @@ -package org.openrndr.extra.color.spaces - -import kotlinx.serialization.Serializable -import org.openrndr.color.* -import org.openrndr.math.Vector4 -import kotlin.jvm.JvmRecord -import kotlin.math.abs -import kotlin.math.pow -import kotlin.math.sign - - -/** - * Represents a color in the OKLAB color space with an optional alpha (transparency) value. - * OKLAB is a perceptual color space designed to represent colors in a way that aligns - * with human visual perception, offering an alternative to traditional color spaces like RGB. - * - * @property l The lightness component of the color. A value between 0 (black) and 1 (white). - * @property a The 'a' component in the OKLAB color space, representing the first chromatic axis. - * @property b The 'b' component in the OKLAB color space, representing the second chromatic axis. - * @property alpha The alpha (opacity) value of the color. A value between 0.0 (completely transparent) and 1.0 (completely opaque). - */ -@Suppress("LocalVariableName") -@Serializable -@JvmRecord -data class ColorOKLABa(val l: Double, val a: Double, val b: Double, override val alpha: Double = 1.0) : - ColorModel, - ShadableColor, - LuminosityColor, - AlgebraicColor { - - companion object { - fun fromRGBa(rgba: ColorRGBa): ColorOKLABa { - // based on https://bottosson.github.io/posts/oklab/ - val c = rgba.toLinear() - val l = 0.4122214708 * c.r + 0.5363325363 * c.g + 0.0514459929 * c.b - val m = 0.2119034982 * c.r + 0.6806995451 * c.g + 0.1073969566 * c.b - val s = 0.0883024619 * c.r + 0.2817188376 * c.g + 0.6299787005 * c.b - - val lnl = abs(l).pow(1.0 / 3.0) * sign(l) - val mnl = abs(m).pow(1.0 / 3.0) * sign(m) - val snl = abs(s).pow(1.0 / 3.0) * sign(s) - - - val L = 0.2104542553 * lnl + 0.7936177850 * mnl - 0.0040720468 * snl - val a = 1.9779984951 * lnl - 2.4285922050 * mnl + 0.4505937099 * snl - val b = 0.0259040371 * lnl + 0.7827717662 * mnl - 0.8086757660 * snl - - return ColorOKLABa(L, a, b, c.alpha) - } - } - - override fun toRGBa(): ColorRGBa { - // based on https://bottosson.github.io/posts/oklab/ - val lnl = l + 0.3963377774 * a + 0.2158037573 * b - val mnl = l - 0.1055613458 * a - 0.0638541728 * b - val snl = l - 0.0894841775 * a - 1.2914855480 * b - - val l = lnl * lnl * lnl - val m = mnl * mnl * mnl - val s = snl * snl * snl - - return ColorRGBa( - 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s, - -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s, - -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s, - alpha, - Linearity.LINEAR - ) - } - - fun toOKLCHa() = ColorOKLCHa.fromColorOKLABa(this) - - override fun shade(factor: Double) = ColorOKLABa(l * factor, a, b, alpha) - override fun opacify(factor: Double) = ColorOKLABa(l, a, b, alpha * factor) - override fun minus(right: ColorOKLABa) = ColorOKLABa(l - right.l, a - right.a, b - right.b, alpha - right.alpha) - override fun plus(right: ColorOKLABa) = ColorOKLABa(l + right.l, a + right.a, b + right.b, alpha + right.alpha) - override fun times(scale: Double) = ColorOKLABa(l * scale, a * scale, b * scale, alpha * scale) - - override fun toVector4() = Vector4(l, a, b, alpha) - override val luminosity: Double - get() = l * 100.0 - - override fun withLuminosity(luminosity: Double): ColorOKLABa = copy(l = luminosity / 100.0) -} - -fun ColorRGBa.toOKLABa() = ColorOKLABa.fromRGBa(this) \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorOKLCHa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorOKLCHa.kt deleted file mode 100644 index 1451b4fb..00000000 --- a/orx-color/src/commonMain/kotlin/spaces/ColorOKLCHa.kt +++ /dev/null @@ -1,81 +0,0 @@ -package org.openrndr.extra.color.spaces - -import kotlinx.serialization.Serializable -import org.openrndr.color.* -import org.openrndr.math.* -import kotlin.jvm.JvmRecord -import kotlin.math.* - - -/** - * Represents a color in the OKLCH color space, which is based on the OKLab color space with added polar coordinates for chroma and hue. - * - * @property l Lightness of the color. Range: 0.0 (black) to 1.0 (white). - * @property c Chroma, representing color intensity. Typically non-negative. - * @property h Hue angle in degrees. Range: 0.0 to 360.0. - * @property alpha Opacity of the color. Range: 0.0 (fully transparent) to 1.0 (fully opaque). Default is 1.0. - */ -@Serializable -@JvmRecord -data class ColorOKLCHa(val l: Double, val c: Double, val h: Double, override val alpha: Double = 1.0) : - ColorModel, - ShadableColor, - ChromaColor, - HueShiftableColor, - LuminosityColor, - AlgebraicColor { - - companion object { - fun fromColorOKLABa(oklaba: ColorOKLABa): ColorOKLCHa { - val l = oklaba.l - val c = sqrt(oklaba.a * oklaba.a + oklaba.b * oklaba.b) - var h = atan2(oklaba.b, oklaba.a) - - if (h < 0) { - h += PI * 2 - } - h = h.asDegrees - return ColorOKLCHa(l, c, h, oklaba.alpha) - } - } - - override fun opacify(factor: Double) = copy(alpha = alpha * factor) - override fun shade(factor: Double) = copy(l = l * factor) - - override fun plus(right: ColorOKLCHa) = copy(l = l + right.l, c = c + right.c, h = h + right.h, alpha = alpha + right.alpha) - override fun minus(right: ColorOKLCHa) = copy(l = l - right.l, c = c - right.c, h = h - right.h, alpha = alpha - right.alpha) - override fun times(scale: Double) = copy(l = l * scale, c = c * scale, h = h * scale, alpha = alpha * scale) - override fun mix(other: ColorOKLCHa, factor: Double) = mix(this, other, factor) - - fun toOKLABa(): ColorOKLABa { - val a = c * cos(h.asRadians) - val b = c * sin(h.asRadians) - return ColorOKLABa(l, a, b, alpha = this.alpha) - } - - override fun toRGBa(): ColorRGBa = toOKLABa().toRGBa() - override fun toVector4(): Vector4 = Vector4(l, c, h, alpha) - override val chroma: Double - get() = c * 100.0 - override fun withChroma(chroma: Double): ColorOKLCHa = copy(c = chroma / 100.0) - override val hue: Double - get() = h - - override fun withHue(hue: Double): ColorOKLCHa = copy(h = hue) - override val luminosity: Double - get() = l * 100.0 - - override fun withLuminosity(luminosity: Double): ColorOKLCHa = copy(l = luminosity / 100.0) -} - -fun mix(left: ColorOKLCHa, right: ColorOKLCHa, x: Double): ColorOKLCHa { - val sx = x.coerceIn(0.0, 1.0) - return ColorOKLCHa( - (1.0 - sx) * left.l + sx * right.l, - (1.0 - sx) * left.c + sx * right.c, - mixAngle(left.h, right.h, sx), - (1.0 - sx) * left.alpha + sx * right.alpha - ) -} - -fun ColorRGBa.toOKLCHa() = ColorOKLABa.fromRGBa(this).toOKLCHa() diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt deleted file mode 100644 index f7b6a01c..00000000 --- a/orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt +++ /dev/null @@ -1,85 +0,0 @@ -package org.openrndr.extra.color.spaces - -import kotlinx.serialization.Serializable -import org.openrndr.color.* -import org.openrndr.math.Vector4 -import org.openrndr.math.map -import org.openrndr.math.mixAngle -import kotlin.jvm.JvmRecord - -/** - * Represents a color in the XSLUV color space with an optional alpha transparency value. - * XSLUV is a cylindrical representation of the HSLUV color space using an alternative X coordinate - * instead of the standard hue. It provides a perceptually uniform color representation. - * - * @property x The X coordinate representing the hue variation in the XSLUV color space. - * @property s The saturation of the color in the XSLUV color space. - * @property l The luminance of the color in the XSLUV color space. - * @property alpha The alpha transparency value of the color, ranging from 0.0 to 1.0. Defaults to 1.0. - */ -@Serializable -@JvmRecord -data class ColorXSLUVa(val x: Double, val s: Double, val l: Double, override val alpha: Double = 1.0) : - ColorModel, - HueShiftableColor, - SaturatableColor, - ShadableColor, - AlgebraicColor { - - override val hue: Double - get() = x - override fun withHue(hue: Double): ColorXSLUVa = copy(x = hue) - - override fun shade(factor: Double) = copy(l = l * factor) - override val saturation: Double - get() = s - - override fun withSaturation(saturation: Double): ColorXSLUVa = copy(s = saturation) - - override fun toRGBa(): ColorRGBa = toHSLUVa().toRGBa() - - fun toHSLUVa(): ColorHSLUVa = ColorHSLUVa(xToHue(x), s, l, alpha) - - override fun opacify(factor: Double) = copy(alpha = alpha * factor) - - override fun minus(right: ColorXSLUVa) = - copy(x = x - right.x, s = s - right.s, l = l - right.l, alpha = alpha - right.alpha) - - override fun plus(right: ColorXSLUVa) = - copy(x = x + right.x, s = s + right.s, l = l + right.l, alpha = alpha + right.alpha) - - override fun times(scale: Double) = copy(x = x * scale, s = s * scale, l = l * scale, alpha = alpha * scale) - - override fun mix(other: ColorXSLUVa, factor: Double) = mix(this, other, factor) - - override fun toVector4(): Vector4 = Vector4(x, s, l, alpha) -} - -fun xToHue(x: Double): Double { - @Suppress("NAME_SHADOWING") val x = x.mod(360.0) - return if (0.0 <= x && x < 60.0) { - x.map(0.0, 60.0, 0.0, 35.0) - } else if (60.0 <= x && x < 120.0) { - x.map(60.0, 120.0, 35.0, 60.0) - } else if (120.0 <= x && x < 180.0) { - x.map(120.0, 180.0, 60.0, 135.0) - } else if (180.0 <= x && x < 240.0) { - x.map(180.0, 240.0, 135.0, 225.0) - } else if (240.0 <= x && x < 300.0) { - x.map(240.0, 300.0, 225.0, 275.0) - } else { - x.map( 300.0, 360.0, 275.0, 360.0) - } -} - -fun mix(left: ColorXSLUVa, right: ColorXSLUVa, x: Double): ColorXSLUVa { - val sx = x.coerceIn(0.0, 1.0) - return ColorXSLUVa( - mixAngle(left.x, right.x, sx), - (1.0 - sx) * left.s + sx * right.s, - (1.0 - sx) * left.l + sx * right.l, - (1.0 - sx) * left.alpha + sx * right.alpha - ) -} - -fun ColorRGBa.toXSLUVa() = toHSLUVa().toXSLUVa() diff --git a/orx-color/src/commonMain/kotlin/spaces/OKHelpers.kt b/orx-color/src/commonMain/kotlin/spaces/OKHelpers.kt deleted file mode 100644 index adfabfb3..00000000 --- a/orx-color/src/commonMain/kotlin/spaces/OKHelpers.kt +++ /dev/null @@ -1,281 +0,0 @@ -@file:Suppress("FunctionName", "LocalVariableName") - -package org.openrndr.extra.color.spaces - - -import kotlin.math.max -import kotlin.math.min -import kotlin.math.pow -import kotlin.math.sqrt - -// verbatim copies of https://github.com/bottosson/bottosson.github.io/blob/master/misc/colorpicker/colorconversion.js - -internal fun max(a: Double, b: Double, c: Double, d: Double): Double { - return max(max(a, b), max(c, d)) -} - -/** - * Computes a modified version of the Toe mapping function, used in color space transformations. - * - * @param x The input value for the function, representing a parameter in the transformation process. - * @return The transformed value after applying the Toe mapping function. - */ -fun toe(x: Double): Double { - val k1 = 0.206 - val k2 = 0.03 - val k3 = (1 + k1) / (1 + k2) - - val d = (k3 * x - k1) * (k3 * x - k1) + 4 * k2 * k3 * x - - return 0.5 * (k3 * x - k1 + sqrt(d.coerceAtLeast(0.0))) -} - -/** - * Computes the toe inverse transformation of a given input value based on specific constants. - * This function is typically used for calculations involving perceptual transformations - * or non-linear scaling in color spaces or other mathematical models. - * - * @param x The input value for which the toe inverse transformation is calculated. - * @return A transformed value based on the toe inverse function. - */ -fun toeInv(x: Double): Double { - val k1 = 0.206 - val k2 = 0.03 - val k3 = (1 + k1) / (1 + k2) - return (x * x + k1 * x) / (k3 * (x + k2)) -} - -internal fun compute_max_saturation(a: Double, b: Double): Double { - // Max saturation will be when one of r, g or b goes below zero. - - // Select different coefficients depending on which component goes below zero first - val k0: Double - val k1: Double - val k2: Double - val k3: Double - val k4: Double - val wl: Double - val wm: Double - val ws: Double - - if (-1.88170328 * a - 0.80936493 * b > 1) { - // Red component - k0 = +1.19086277; k1 = +1.76576728; k2 = +0.59662641; k3 = +0.75515197; k4 = +0.56771245 - wl = +4.0767416621; wm = -3.3077115913; ws = +0.2309699292 - } else if (1.81444104 * a - 1.19445276 * b > 1) { - // Green component - k0 = +0.73956515; k1 = -0.45954404; k2 = +0.08285427; k3 = +0.12541070; k4 = +0.14503204 - wl = -1.2684380046; wm = +2.6097574011; ws = -0.3413193965 - } else { - // Blue component - k0 = +1.35733652; k1 = -0.00915799; k2 = -1.15130210; k3 = -0.50559606; k4 = +0.00692167 - wl = -0.0041960863; wm = -0.7034186147; ws = +1.7076147010 - } - - // Approximate max saturation using a polynomial: - val S = k0 + k1 * a + k2 * b + k3 * a * a + k4 * a * b - - // Do one step Halley's method to get closer - // this gives an error less than 10e6, except for some blue hues where the dS/dh is close to infinite - // this should be sufficient for most applications, otherwise do two/three steps - - val k_l = +0.3963377774 * a + 0.2158037573 * b - val k_m = -0.1055613458 * a - 0.0638541728 * b - val k_s = -0.0894841775 * a - 1.2914855480 * b - - return run { - val l_ = 1 + S * k_l - val m_ = 1 + S * k_m - val s_ = 1 + S * k_s - - val l = l_ * l_ * l_ - val m = m_ * m_ * m_ - val s = s_ * s_ * s_ - - val l_dS = 3 * k_l * l_ * l_ - val m_dS = 3 * k_m * m_ * m_ - val s_dS = 3 * k_s * s_ * s_ - - val l_dS2 = 6 * k_l * k_l * l_ - val m_dS2 = 6 * k_m * k_m * m_ - val s_dS2 = 6 * k_s * k_s * s_ - - val f = wl * l + wm * m + ws * s - val f1 = wl * l_dS + wm * m_dS + ws * s_dS - val f2 = wl * l_dS2 + wm * m_dS2 + ws * s_dS2 - - S - f * f1 / (f1 * f1 - 0.5 * f * f2) - } -} - -internal fun find_cusp(a: Double, b: Double): DoubleArray { - // First, find the maximum saturation (saturation S = C/L) - val S_cusp = compute_max_saturation(a, b) - val rgb_at_max = ColorOKLABa(1.0, S_cusp * a, S_cusp * b).toRGBa().toLinear() - val L_cusp = (1.0 / max(max(rgb_at_max.r, rgb_at_max.g), rgb_at_max.b)).pow(1.0 / 3.0) - val C_cusp = L_cusp * S_cusp - - return doubleArrayOf(L_cusp, C_cusp) -} - -internal fun get_ST_max(a: Double, b: Double, cusp: DoubleArray? = null): DoubleArray { - @Suppress("NAME_SHADOWING") val cusp = cusp ?: find_cusp(a, b) - - val L = cusp[0] - val C = cusp[1] - return doubleArrayOf(C / L, C / (1.0 - L)) -} - -fun get_ST_mid(a_: Double, b_: Double): DoubleArray { - val S = 0.11516993 + 1 / ( - +7.44778970 + 4.15901240 * b_ - + a_ * (-2.19557347 + 1.75198401 * b_ - + a_ * (-2.13704948 - 10.02301043 * b_ - + a_ * (-4.24894561 + 5.38770819 * b_ + 4.69891013 * a_ - ))) - ) - - val T = 0.11239642 + 1 / ( - +1.61320320 - 0.68124379 * b_ - + a_ * (+0.40370612 + 0.90148123 * b_ - + a_ * (-0.27087943 + 0.61223990 * b_ - + a_ * (+0.00299215 - 0.45399568 * b_ - 0.14661872 * a_ - ))) - ) - - return doubleArrayOf(S, T) -} - -fun get_Cs(L: Double, a_: Double, b_: Double): DoubleArray { - val cusp = find_cusp(a_, b_) - - val C_max = find_gamut_intersection(a_, b_, L, 1.0, L, cusp) - val ST_max = get_ST_max(a_, b_, cusp) - - val S_mid = 0.11516993 + 1 / ( - +7.44778970 + 4.15901240 * b_ - + a_ * (-2.19557347 + 1.75198401 * b_ - + a_ * (-2.13704948 - 10.02301043 * b_ - + a_ * (-4.24894561 + 5.38770819 * b_ + 4.69891013 * a_ - ))) - ) - - val T_mid = 0.11239642 + 1 / ( - +1.61320320 - 0.68124379 * b_ - + a_ * (+0.40370612 + 0.90148123 * b_ - + a_ * (-0.27087943 + 0.61223990 * b_ - + a_ * (+0.00299215 - 0.45399568 * b_ - 0.14661872 * a_ - ))) - ) - - val k: Double = C_max / min((L * ST_max[0]), (1 - L) * ST_max[1]) - - val C_mid: Double - run { - val C_a = L * S_mid - val C_b = (1 - L) * T_mid - - C_mid = (0.9 * k) * sqrt(sqrt(1 / (1 / (C_a * C_a * C_a * C_a) + 1 / (C_b * C_b * C_b * C_b)))) - } - - val C_0: Double - run { - val C_a = L * 0.4 - val C_b = (1 - L) * 0.8 - - C_0 = sqrt(1 / (1 / (C_a * C_a) + 1 / (C_b * C_b))) - } - - return doubleArrayOf(C_0, C_mid, C_max) -} - -fun find_gamut_intersection( - a: Double, - b: Double, - L1: Double, - C1: Double, - L0: Double, - cusp: DoubleArray? = null -): Double { - @Suppress("NAME_SHADOWING") val cusp = cusp ?: find_cusp(a, b) - - - // Find the intersection for upper and lower half seprately - var t: Double - if (((L1 - L0) * cusp[1] - (cusp[0] - L0) * C1) <= 0) { - // Lower half - - t = cusp[1] * L0 / (C1 * cusp[0] + cusp[1] * (L0 - L1)) - } else { - // Upper half - - // First intersect with triangle - t = cusp[1] * (L0 - 1) / (C1 * (cusp[0] - 1) + cusp[1] * (L0 - L1)) - - // Then one step Halley's method - run { - val dL = L1 - L0 - val dC = C1 - - val k_l = +0.3963377774 * a + 0.2158037573 * b - val k_m = -0.1055613458 * a - 0.0638541728 * b - val k_s = -0.0894841775 * a - 1.2914855480 * b - - val l_dt = dL + dC * k_l - val m_dt = dL + dC * k_m - val s_dt = dL + dC * k_s; - - - // If higher accuracy is required, 2 or 3 iterations of the following block can be used: - { - val L = L0 * (1 - t) + t * L1 - val C = t * C1 - - val l_ = L + C * k_l - val m_ = L + C * k_m - val s_ = L + C * k_s - - val l = l_ * l_ * l_ - val m = m_ * m_ * m_ - val s = s_ * s_ * s_ - - val ldt = 3 * l_dt * l_ * l_ - val mdt = 3 * m_dt * m_ * m_ - val sdt = 3 * s_dt * s_ * s_ - - val ldt2 = 6 * l_dt * l_dt * l_ - val mdt2 = 6 * m_dt * m_dt * m_ - val sdt2 = 6 * s_dt * s_dt * s_ - - val r = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s - 1 - val r1 = 4.0767416621 * ldt - 3.3077115913 * mdt + 0.2309699292 * sdt - val r2 = 4.0767416621 * ldt2 - 3.3077115913 * mdt2 + 0.2309699292 * sdt2 - - val u_r = r1 / (r1 * r1 - 0.5 * r * r2) - var t_r = -r * u_r - - val g = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s - 1 - val g1 = -1.2684380046 * ldt + 2.6097574011 * mdt - 0.3413193965 * sdt - val g2 = -1.2684380046 * ldt2 + 2.6097574011 * mdt2 - 0.3413193965 * sdt2 - - val u_g = g1 / (g1 * g1 - 0.5 * g * g2) - var t_g = -g * u_g - - val b = -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s - 1 - val b1 = -0.0041960863 * ldt - 0.7034186147 * mdt + 1.7076147010 * sdt - val b2 = -0.0041960863 * ldt2 - 0.7034186147 * mdt2 + 1.7076147010 * sdt2 - - val u_b = b1 / (b1 * b1 - 0.5 * b * b2) - var t_b = -b * u_b - - t_r = if (u_r >= 0) t_r else 10e5 - t_g = if (u_g >= 0) t_g else 10e5 - t_b = if (u_b >= 0) t_b else 10e5 - - t += min(t_r, min(t_g, t_b)) - } - } - } - - return t -} diff --git a/orx-color/src/commonMain/kotlin/spaces/TypeAliases.kt b/orx-color/src/commonMain/kotlin/spaces/TypeAliases.kt deleted file mode 100644 index 290e1aac..00000000 --- a/orx-color/src/commonMain/kotlin/spaces/TypeAliases.kt +++ /dev/null @@ -1,21 +0,0 @@ -package org.openrndr.extra.color.spaces - -import org.openrndr.color.* - -typealias RGB = ColorRGBa -typealias LAB = ColorLABa -typealias LUV = ColorLUVa -typealias HPLuv = ColorHPLUVa -typealias HSL = ColorHSLa -typealias HSV = ColorHSVa -typealias HSLuv = ColorHSLUVa -typealias OKLab = ColorOKLABa -typealias OKHSV = ColorOKHSVa -typealias OKHSL = ColorOKHSLa -typealias OKLCH = ColorOKLCHa -typealias XSL = ColorXSLa -typealias XSV = ColorXSVa -typealias XSLuv = ColorXSLUVa -typealias CIEXYZ = ColorXYZa -typealias LCHab = ColorLCHABa -typealias LCHuv = ColorLCHUVa \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/statistics/DeltaE.kt b/orx-color/src/commonMain/kotlin/statistics/DeltaE.kt deleted file mode 100644 index af6d3b33..00000000 --- a/orx-color/src/commonMain/kotlin/statistics/DeltaE.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.openrndr.extra.color.statistics - -import org.openrndr.color.ColorLABa -import org.openrndr.color.ConvertibleToColorRGBa -import org.openrndr.math.Vector3 - -/** - * Computes the CIE76 color difference (ΔE*76) between this color and another color. - * The method calculates the Euclidean distance between the two colors in the LAB color space. - * If either of the colors is not in LAB format, it will be converted to LAB before computation. - * - * @param other The second color to compare, which should implement the ConvertibleToColorRGBa interface. - * @return The calculated CIE76 color difference as a Double. - */ -fun T.deltaE76(other: T): Double { - return if (this is ColorLABa && other is ColorLABa) { - val tv = Vector3(l, a, b) - val ov = Vector3(other.l, other.a, other.b) - tv.distanceTo(ov) - } else { - val tLab = if (this is ColorLABa) this else this.toRGBa().toLABa() - val oLab = if (other is ColorLABa) other else other.toRGBa().toLABa() - tLab.deltaE76(oLab) - } -} \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/statistics/Histogram.kt b/orx-color/src/commonMain/kotlin/statistics/Histogram.kt deleted file mode 100644 index 3744c4cf..00000000 --- a/orx-color/src/commonMain/kotlin/statistics/Histogram.kt +++ /dev/null @@ -1,13 +0,0 @@ -@file:JvmName("HistogramJvm") -package org.openrndr.extra.color.statistics - -import org.openrndr.color.ColorRGBa -import kotlin.jvm.JvmName - -internal fun ColorRGBa.binIndex(binCount: Int): Triple { - val rb = (r * binCount).toInt().coerceIn(0, binCount - 1) - val gb = (g * binCount).toInt().coerceIn(0, binCount - 1) - val bb = (b * binCount).toInt().coerceIn(0, binCount - 1) - return Triple(rb, gb, bb) -} - diff --git a/orx-color/src/commonMain/kotlin/tools/ChromaColorExtensions.kt b/orx-color/src/commonMain/kotlin/tools/ChromaColorExtensions.kt deleted file mode 100644 index ea344b8e..00000000 --- a/orx-color/src/commonMain/kotlin/tools/ChromaColorExtensions.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.openrndr.extra.color.tools - -import org.openrndr.color.ChromaColor -import org.openrndr.color.ConvertibleToColorRGBa - - -private fun binarySearchMax(min: Double, max: Double, start: Double, threshold: Double = 1E-5, f: (Double) -> Boolean): Double { - var low = min - var high = max - - var best = min - var mid = start - while (low <= high) { - - val res = f(mid) - - if (res) { - best = mid - low = mid - } else { - high = mid - } - - if (high - low < threshold) { - return best - } - mid = (low + high) / 2.0 - } - return best -} - -fun T.findMaxChroma(): Double - where T : ChromaColor, - T : ConvertibleToColorRGBa { - return binarySearchMax(0.0, 200.0, chroma, 1E-5) { - val c = withChroma(it).toRGBa() - !c.isOutOfGamut - } -} - -fun T.clipChroma(): T - where T : ChromaColor, - T : ConvertibleToColorRGBa { - - val maxChroma = findMaxChroma() - return withChroma(maxChroma) -} \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/tools/ColorRGBaExtensions.kt b/orx-color/src/commonMain/kotlin/tools/ColorRGBaExtensions.kt deleted file mode 100644 index 3a4a41e9..00000000 --- a/orx-color/src/commonMain/kotlin/tools/ColorRGBaExtensions.kt +++ /dev/null @@ -1,198 +0,0 @@ -package org.openrndr.extra.color.tools - -import org.openrndr.color.* -import org.openrndr.extra.color.spaces.* - -/** - * Indicates whether the color is out of the RGB gamut. - * - * This property evaluates if the color's red, green, or blue components are outside - * the valid range of [0.0, 1.0], accounting for a slight tolerance in the negative range (-1E-3). - * Additionally, it checks whether the alpha component is outside the range [0.0, 1.0]. - * - * This property is commonly used in color manipulation functions to detect and handle - * out-of-gamut colors, which may require adjustments (e.g., clipping or chroma adjustment) - * to fit within a valid color space. - */ -val ColorRGBa.isOutOfGamut: Boolean - get() { - return (r !in -1E-3..1.0) || (g !in -1E-3..1.0) || (b !in -1E-3..1.0) || (alpha !in 0.0..1.0) - } - -/** - * Matches the linearity of the current `ColorRGBa` instance with another `ColorRGBa` instance. - * If the linearity of `other` matches that of the current instance, the current instance is returned. - * Otherwise, it converts the current instance to match the linearity of `other`. - * - * @param other The `ColorRGBa` instance whose linearity is to be matched. - * @return A `ColorRGBa` instance with the same linearity as the `other` color. - */ -fun ColorRGBa.matchLinearity(other: ColorRGBa): ColorRGBa { - return if (other.linearity.isEquivalent(linearity)) { - this - } else { - if (other.linearity.isEquivalent(Linearity.LINEAR)) { - toLinear() - } else if (other.linearity.isEquivalent(Linearity.SRGB)) { - toSRGB() - } else { - this - } - } -} - -inline fun ColorRGBa.hue(): Double - where T : HueShiftableColor, - T : ColorModel = convertTo().hue - -inline fun ColorRGBa.blendWith(other: ColorRGBa, steps: Int): Sequence - where T : AlgebraicColor, - T : ColorModel { - return sequence { - for (step in 0 until steps) { - yield(mixedWith(other, step / (steps - 1.0))) - } - } -} - - -/** - * Converts the current `ColorRGBa` instance to the specified color model type `T`. - * - * @return An instance of the specified color model type `T` after conversion. - * @throws IllegalStateException if the specified color model is not supported. - */ -inline fun > ColorRGBa.convertTo(): T { - val converted = when (T::class) { - ColorHSLa::class -> this.toHSLa() - ColorHSVa::class -> this.toHSVa() - ColorRGBa::class -> this - ColorHPLUVa::class -> this.toHPLUVa() - ColorHSLUVa::class -> this.toHSLUVa() - ColorOKLABa::class -> this.toOKLABa() - ColorOKLCHa::class -> this.toOKLCHa() - ColorOKHSLa::class -> this.toOKHSLa() - ColorOKHSVa::class -> this.toOKHSVa() - ColorLABa::class -> this.toLABa() - ColorLUVa::class -> this.toLUVa() - ColorLCHABa::class -> this.toLCHABa() - ColorLCHUVa::class -> this.toLCHUVa() - ColorOKHSLa::class -> this.toOKHSLa() - ColorXYZa::class -> this.toXYZa() - ColorXSLUVa::class -> this.toXSLUVa() - ColorXSVa::class -> this.toXSVa() - ColorXSLa::class -> this.toXSLa() - else -> error("color model ${T::class} not supported") - } - return converted as T -} - -inline fun ColorRGBa.withLuminosity(luminosity: Double): ColorRGBa - where T : LuminosityColor, - T : ColorModel, - T : ConvertibleToColorRGBa = convertTo().withLuminosity(luminosity).toRGBa().matchLinearity(this) - -inline fun ColorRGBa.luminosity(): Double - where T : LuminosityColor, - T : ColorModel, - T : ConvertibleToColorRGBa = convertTo().luminosity - -inline fun ColorRGBa.mixLuminosity(luminosity: Double, factor: Double): ColorRGBa - where T : LuminosityColor, - T : ColorModel, - T : ConvertibleToColorRGBa = - convertTo().mixLuminosity(luminosity, factor).toRGBa().matchLinearity(this) - - -inline fun ColorRGBa.shadeLuminosity(factor: Double): ColorRGBa - where T : LuminosityColor, - T : ColorModel, - T : ConvertibleToColorRGBa = - convertTo().shadeLuminosity(factor).toRGBa().matchLinearity(this) - - -inline fun ColorRGBa.mixHue(hue: Double, factor: Double): ColorRGBa - where T : HueShiftableColor, - T : ColorModel, - T : ConvertibleToColorRGBa = convertTo().mixHue(hue, factor).toRGBa().matchLinearity(this) - -inline fun ColorRGBa.withHue(hue: Double): ColorRGBa - where T : HueShiftableColor, - T : ColorModel, - T : ConvertibleToColorRGBa = convertTo().withHue(hue).toRGBa().matchLinearity(this) - -inline fun ColorRGBa.mixSaturation(saturation: Double, factor: Double): ColorRGBa - where T : SaturatableColor, - T : ColorModel, - T : ConvertibleToColorRGBa = - convertTo().mixSaturation(saturation, factor).toRGBa().matchLinearity(this) - - -inline fun ColorRGBa.mixedWith(other: ColorRGBa, factor: Double): ColorRGBa - where T : AlgebraicColor, T : ColorModel { - val source = convertTo() - val target = other.convertTo() - return source.mix(target, factor).toRGBa().matchLinearity(this) -} - -inline fun ColorRGBa.mixChroma(chroma: Double, factor: Double): ColorRGBa - where T : ChromaColor, - T : ColorModel, - T : ConvertibleToColorRGBa = - convertTo().mixChroma(chroma, factor).toRGBa().matchLinearity(this) - -inline fun ColorRGBa.withChroma(chroma: Double): ColorRGBa - where T : ChromaColor, - T : ColorModel, - T : ConvertibleToColorRGBa = - convertTo().withChroma(chroma).toRGBa().matchLinearity(this) - -inline fun ColorRGBa.chroma(): Double - where T : ChromaColor, - T : ColorModel, - T : ConvertibleToColorRGBa = - convertTo().chroma - -inline fun ColorRGBa.modulateChroma(factor: Double): ColorRGBa - where T : ChromaColor, - T : ColorModel, - T : ConvertibleToColorRGBa = - convertTo().modulateChroma(factor).toRGBa().matchLinearity(this) - - -/** - * Adjusts the saturation of the current `ColorRGBa` based on a given factor. - * - * @param T The target color model type that supports saturation adjustments. - * @param factor The saturation adjustment factor. A value of 1.0 keeps the saturation unchanged, - * values less than 1.0 decrease saturation, and values greater than 1.0 increase it. - * @return A new `ColorRGBa` instance with the adjusted saturation, maintaining the linearity of - * the original color. - */ -inline fun ColorRGBa.saturate(factor: Double): ColorRGBa - where T : SaturatableColor, - T : ColorModel, - T : ConvertibleToColorRGBa = convertTo().saturate(factor).toRGBa().matchLinearity(this) - -/** - * Shifts the hue of the current `ColorRGBa` by the specified number of degrees. - * The method is only applicable to color models that support hue shifting and can be converted to `ColorRGBa`. - * - * @param degrees The amount of hue adjustment in degrees. Positive values shift the hue clockwise, while negative values shift it counterclockwise. - * @return A new `ColorRGBa` instance with the hue shifted by the specified degree, maintaining the same linearity as the input color. - */ -inline fun ColorRGBa.shiftHue(degrees: Double): ColorRGBa where - T : HueShiftableColor, - T : ColorModel, - T : ConvertibleToColorRGBa = convertTo().shiftHue(degrees).toRGBa().matchLinearity(this) - -inline fun ColorRGBa.clipChroma(): ColorRGBa - where T : ChromaColor, - T : ColorModel, - T : ConvertibleToColorRGBa = - - if (isOutOfGamut) { - convertTo().clipChroma().toRGBa().matchLinearity(this).clip() - } else { - this - } diff --git a/orx-color/src/commonTest/kotlin/ColorTest.kt b/orx-color/src/commonTest/kotlin/ColorTest.kt deleted file mode 100644 index 91eaf885..00000000 --- a/orx-color/src/commonTest/kotlin/ColorTest.kt +++ /dev/null @@ -1,5 +0,0 @@ -// keeping this file here will stop IntelliJ from showing warning in nested relative packages -/** - * orx-color - */ -package org.openrndr.extra.color diff --git a/orx-color/src/commonTest/kotlin/colormaps/TestSpectralZucconi6Colormap.kt b/orx-color/src/commonTest/kotlin/colormaps/TestSpectralZucconi6Colormap.kt deleted file mode 100644 index b11460f7..00000000 --- a/orx-color/src/commonTest/kotlin/colormaps/TestSpectralZucconi6Colormap.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.openrndr.extra.color.colormaps - -import io.kotest.matchers.shouldBe -import org.openrndr.color.ColorRGBa -import org.openrndr.color.Linearity -import org.openrndr.math.Vector3 -import kotlin.test.Test - -class TestSpectralZucconi6Colormap { - - @Test - fun testSpectralZucconi6Vector() { - spectralZucconi6Vector(0.0) shouldBe Vector3(0.0, 0.0, 0.026075309353279508) - spectralZucconi6Vector(0.5) shouldBe Vector3(0.49637374891706215, 0.8472371726323733, 0.18366091774095827) - spectralZucconi6Vector(1.0) shouldBe Vector3(0.0, 0.0, 0.0) - spectralZucconi6Vector(-0.1) shouldBe Vector3(0.0, 0.0, 0.0) - spectralZucconi6Vector(1.1) shouldBe Vector3(0.0, 0.0, 0.0) - } - - @Test - fun testSpectralZucconi6() { - spectralZucconi6(0.0) shouldBe ColorRGBa(0.0, 0.0, 0.026075309353279508, linearity = Linearity.LINEAR) - spectralZucconi6(0.5) shouldBe ColorRGBa(0.49637374891706215, 0.8472371726323733, 0.18366091774095827, linearity = Linearity.LINEAR) - spectralZucconi6(1.0) shouldBe ColorRGBa(0.0, 0.0, 0.0, linearity = Linearity.LINEAR) - spectralZucconi6(-0.1) shouldBe ColorRGBa(0.0, 0.0, 0.0, linearity = Linearity.LINEAR) - spectralZucconi6(1.1) shouldBe ColorRGBa(0.0, 0.0, 0.0, linearity = Linearity.LINEAR) - } - -} diff --git a/orx-color/src/commonTest/kotlin/colormaps/TestTurboColormap.kt b/orx-color/src/commonTest/kotlin/colormaps/TestTurboColormap.kt deleted file mode 100644 index 9fff4521..00000000 --- a/orx-color/src/commonTest/kotlin/colormaps/TestTurboColormap.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.openrndr.extra.color.colormaps - -import io.kotest.matchers.shouldBe -import org.openrndr.color.ColorRGBa -import org.openrndr.color.Linearity -import org.openrndr.math.Vector3 -import kotlin.test.Test - -class TestTurboColormap { - - @Test - fun testTurboColormapVector() { - turboColormapVector(0.0) shouldBe Vector3(0.13572138, 0.09140261, 0.1066733) - turboColormapVector(0.5) shouldBe Vector3(0.5885220621875007, 0.981864383125, 0.31316869781249856) - turboColormapVector(1.0) shouldBe Vector3(0.5658592099999993, 0.05038885999999998, -0.025520659999997974) - turboColormapVector(-0.1) shouldBe Vector3(0.13572138, 0.09140261, 0.1066733) - turboColormapVector(1.1) shouldBe Vector3(0.5658592099999993, 0.05038885999999998, -0.025520659999997974) - } - - @Test - fun testTurboColormap() { - turboColormap(0.0) shouldBe ColorRGBa(0.13572138, 0.09140261, 0.1066733, linearity = Linearity.LINEAR) - turboColormap(0.5) shouldBe ColorRGBa(0.5885220621875007, 0.981864383125, 0.31316869781249856, linearity = Linearity.LINEAR) - turboColormap(1.0) shouldBe ColorRGBa(0.5658592099999993, 0.05038885999999998, -0.025520659999997974, linearity = Linearity.LINEAR) - turboColormap(-0.1) shouldBe ColorRGBa(0.13572138, 0.09140261, 0.1066733, linearity = Linearity.LINEAR) - turboColormap(1.1) shouldBe ColorRGBa(0.5658592099999993, 0.05038885999999998, -0.025520659999997974, linearity = Linearity.LINEAR) - } - -} diff --git a/orx-color/src/commonTest/kotlin/mixing/TestSpectral.kt b/orx-color/src/commonTest/kotlin/mixing/TestSpectral.kt deleted file mode 100644 index b9dcb62e..00000000 --- a/orx-color/src/commonTest/kotlin/mixing/TestSpectral.kt +++ /dev/null @@ -1,30 +0,0 @@ -package org.openrndr.extra.color.mixing - -import org.openrndr.color.ColorRGBa - -import org.openrndr.extra.color.tools.matchLinearity -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -class TestSpectral { - @Test - fun testIdentity() { - val colors = listOf(ColorRGBa.RED, ColorRGBa.GREEN, ColorRGBa.BLUE) - - for (c in colors) { - val r = linearToReflectance(c) - assertEquals(38, r.size) - val xyz = reflectanceToXYZ(r) - val cp = xyz.toRGBa().matchLinearity(c) - assertTrue(cp.toVector4().distanceTo(c.toVector4()) < 5E-3) - } - } - - @Test - fun testSaunderson() { - val c = saundersonCorrection(10.15, 0.0, 0.0) - println(c) - } - -} \ No newline at end of file diff --git a/orx-color/src/commonTest/kotlin/spaces/TestHSLUVa.kt b/orx-color/src/commonTest/kotlin/spaces/TestHSLUVa.kt deleted file mode 100644 index 264087b3..00000000 --- a/orx-color/src/commonTest/kotlin/spaces/TestHSLUVa.kt +++ /dev/null @@ -1,23 +0,0 @@ -package org.openrndr.extra.color.spaces - -import org.openrndr.color.ColorRGBa -import kotlin.test.Test -import kotlin.test.assertTrue - -class TestHSLUVa { - - @Test - fun testConversions() { - val testColors = listOf(ColorRGBa.RED, ColorRGBa.BLUE, ColorRGBa.GREEN, ColorRGBa.GRAY, ColorRGBa.YELLOW) - val error = (-1E-3 .. 1E-3) - testColors.forEach { - val testColor = it - val toColor = it.toHSLUVa() - val restoreColor = toColor.toRGBa().toSRGB() - assertTrue("color $testColor, $toColor, $restoreColor") { - testColor.r - restoreColor.r in error && testColor.g - restoreColor.g in error && testColor.b - restoreColor.b in error - } - } - } - -} \ No newline at end of file diff --git a/orx-color/src/commonTest/kotlin/spaces/TestOKHSLa.kt b/orx-color/src/commonTest/kotlin/spaces/TestOKHSLa.kt deleted file mode 100644 index ff65c7e6..00000000 --- a/orx-color/src/commonTest/kotlin/spaces/TestOKHSLa.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.openrndr.extra.color.spaces - -import org.openrndr.color.ColorRGBa -import kotlin.test.Test -import kotlin.test.assertTrue - -class TestOKHSLa { - @Test - fun testConversions() { - val testColors = listOf(ColorRGBa.RED, ColorRGBa.BLUE, ColorRGBa.GREEN, ColorRGBa.GRAY, ColorRGBa.YELLOW) - val error = (-1E-5 .. 1E-5) - testColors.forEach { - val testColor = it - val toColor = it.toOKHSLa() - val restoreColor = toColor.toRGBa() - assertTrue("color $testColor, $toColor, $restoreColor") { - testColor.r - restoreColor.r in error && testColor.g - restoreColor.g in error && testColor.b - restoreColor.b in error - } - } - } - - @Test - fun testSaturationPersistence() { - val black = ColorRGBa.BLACK.toOKHSLa() - - assertTrue("resulting OKHSLa $black contains no NaNs") { - black.h == black.h && black.s == black.s && black.l == black.l - } - - val rgbBlack = black.toRGBa() - val white = ColorRGBa.WHITE.toOKHSLa() - val rgbWhite = white.toRGBa() - val epsilon = 1E-6 - assertTrue("resulting color $rgbWhite is white") { - rgbWhite.r in (1.0 - epsilon .. 1.0 + epsilon) && rgbWhite.g in (1.0 - epsilon .. 1.0 + epsilon) && rgbWhite.b in (1.0 - epsilon .. 1.0 + epsilon) - } - assertTrue("resulting color $rgbBlack is black") { - rgbBlack.r in (0.0 - epsilon .. 0.0 + epsilon) && rgbBlack.g in (0.0 - epsilon .. 0.0 + epsilon) && rgbBlack.b in (0.0 - epsilon .. 0.0 + epsilon) - } - } -} \ No newline at end of file diff --git a/orx-color/src/commonTest/kotlin/spaces/TestOKHSVa.kt b/orx-color/src/commonTest/kotlin/spaces/TestOKHSVa.kt deleted file mode 100644 index 95629126..00000000 --- a/orx-color/src/commonTest/kotlin/spaces/TestOKHSVa.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.openrndr.extra.color.spaces - -import org.openrndr.color.ColorRGBa -import kotlin.test.Test -import kotlin.test.assertTrue - -class TestOKHSVa { - @Test - fun testConversions() { - val testColors = listOf(ColorRGBa.RED, ColorRGBa.BLUE, ColorRGBa.GREEN, ColorRGBa.GRAY, ColorRGBa.YELLOW) - val error = (-1E-5 .. 1E-5) - testColors.forEach { - val testColor = it - val toColor = it.toOKHSVa() - val restoreColor = toColor.toRGBa() - assertTrue("color $testColor, $toColor, $restoreColor") { - testColor.r - restoreColor.r in error && testColor.g - restoreColor.g in error && testColor.b - restoreColor.b in error - } - } - } - - @Test - fun testSaturationPersistence() { - val black = ColorRGBa.BLACK.toOKHSVa() - val rgbBlack = black.toRGBa() - - assertTrue("resulting OKHSVa $black contains no NaNs") { - black.h == black.h && black.s == black.s && black.v == black.v - } - - val white = ColorRGBa.WHITE.toOKHSVa() - val rgbWhite = white.toRGBa() - - assertTrue("resulting OKHSVa $white contains no NaNs") { - white.h == white.h && white.s == white.s && white.v == white.v - } - - val epsilon = 1E-6 - assertTrue("resulting color is white") { - rgbWhite.r in (1.0 - epsilon .. 1.0 + epsilon) && rgbWhite.g in (1.0 - epsilon .. 1.0 + epsilon) && rgbWhite.b in (1.0 - epsilon .. 1.0 + epsilon) - } - assertTrue("resulting color is black") { - rgbBlack.r in (0.0 - epsilon .. 0.0 + epsilon) && rgbBlack.g in (0.0 - epsilon .. 0.0 + epsilon) && rgbBlack.b in (0.0 - epsilon .. 0.0 + epsilon) - } - } - -} \ No newline at end of file diff --git a/orx-color/src/commonTest/kotlin/spaces/TestOKLCHa.kt b/orx-color/src/commonTest/kotlin/spaces/TestOKLCHa.kt deleted file mode 100644 index 186564c3..00000000 --- a/orx-color/src/commonTest/kotlin/spaces/TestOKLCHa.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.openrndr.extra.color.spaces - -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.tools.chroma -import org.openrndr.extra.color.tools.withLuminosity -import kotlin.test.Test - -class TestOKLCHa { - @Test - fun testLowLuminosity() { - val c = ColorRGBa.GREEN.withLuminosity(10.0) - println(c) - println(c.chroma()) - - - val g = ColorRGBa.GREEN.toOKLCHa() - val gl10 = g.withLuminosity(10.0) - val rgb10 = gl10.toRGBa() - println(gl10) - println(rgb10) - println(rgb10.toOKLCHa()) - - } -} \ No newline at end of file diff --git a/orx-color/src/commonTest/kotlin/spaces/TestXSLUVa.kt b/orx-color/src/commonTest/kotlin/spaces/TestXSLUVa.kt deleted file mode 100644 index c95d95b2..00000000 --- a/orx-color/src/commonTest/kotlin/spaces/TestXSLUVa.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.openrndr.extra.color.spaces - -import org.openrndr.color.ColorRGBa -import kotlin.math.abs -import kotlin.test.Test -import kotlin.test.assertTrue - -class TestXSLUVa { - - @Test - fun testHueConversions() { - for (i in 0 until 3600) { - val inputHue = i/10.0 - val x = hueToX(inputHue) - val recoveredHue = xToHue(x) - assertTrue( abs(recoveredHue-inputHue) < 1E-8, "$inputHue $recoveredHue") - } - } - - @Test - fun testConversions() { - val testColors = listOf(ColorRGBa.RED, ColorRGBa.BLUE, ColorRGBa.GREEN, ColorRGBa.GRAY, ColorRGBa.YELLOW) - val error = (-1E-3..1E-3) - testColors.forEach { - val testColor = it - val hsluvColor = it.toHSLUVa() - val xsluvColor = it.toXSLUVa() - - val restoredHsluvColor = xsluvColor.toHSLUVa() - - val dh = restoredHsluvColor.h - hsluvColor.h - val dl = restoredHsluvColor.l - hsluvColor.l - val ds = restoredHsluvColor.s - hsluvColor.s - - assertTrue(abs(dh) < 1E-7) - - } - } - -} \ No newline at end of file diff --git a/orx-color/src/commonTest/kotlin/tools/TestChromaColorExtensions.kt b/orx-color/src/commonTest/kotlin/tools/TestChromaColorExtensions.kt deleted file mode 100644 index 944557ab..00000000 --- a/orx-color/src/commonTest/kotlin/tools/TestChromaColorExtensions.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.openrndr.extra.color.tools - -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.spaces.toOKLCHa -import kotlin.test.Test - -class TestChromaColorExtensions { - @Test - fun testFindMaxChroma() { - - run { - val i = ColorRGBa.BLUE.toLCHABa()//.withLuminosity(50.0) - val maxChroma = i.findMaxChroma() - println(i.chroma) - println(maxChroma) - } - run { - val i = ColorRGBa.BLUE.toOKLCHa()//.withLuminosity(50.0) - val maxChroma = i.findMaxChroma() - println(ColorRGBa.BLUE.isOutOfGamut) - println(i.chroma) - println(maxChroma) - println(i.toRGBa()) - } - - } - - -} \ No newline at end of file diff --git a/orx-color/src/commonTest/kotlin/tools/TestColorRGBaExtensions.kt b/orx-color/src/commonTest/kotlin/tools/TestColorRGBaExtensions.kt deleted file mode 100644 index be5f6cc1..00000000 --- a/orx-color/src/commonTest/kotlin/tools/TestColorRGBaExtensions.kt +++ /dev/null @@ -1,76 +0,0 @@ -package org.openrndr.extra.color.tools - -import org.openrndr.color.* -import org.openrndr.extra.color.spaces.* -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -class TestColorRGBaExtensions { - @Test - fun testShiftHue0() { - val seed = ColorRGBa.RED.shade(0.5) - val shifted = listOf( - seed.shiftHue(0.0), - seed.shiftHue(0.0), - seed.shiftHue(0.0), - seed.shiftHue(0.0) - ) - for (s in shifted) { - assertEquals(Linearity.SRGB, s.linearity) - assertTrue(seed.toVector4().distanceTo(s.toVector4()) < 1E-4) - } - } - - @Test - fun testShiftHue0Linear() { - val seed = ColorRGBa.RED.shade(0.5).toLinear() - val shifted = listOf( - seed.shiftHue(0.0), - seed.shiftHue(0.0), - seed.shiftHue(0.0), - seed.shiftHue(0.0) - ) - for (s in shifted) { - assertEquals(Linearity.LINEAR, s.linearity) - assertTrue(seed.toVector4().distanceTo(s.toVector4()) < 1E-4) - } - } - - @Test - fun testSaturate1() { - val seed = ColorRGBa.RED.shade(0.5) - val shifted = listOf( - seed.saturate(1.0), - seed.saturate(1.0) - ) - for (s in shifted) { - assertEquals(Linearity.SRGB, s.linearity) - assertTrue(seed.toVector4().distanceTo(s.toVector4()) < 1E-4) - } - } - - @Test - fun testSaturate1Linear() { - val seed = ColorRGBa.RED.shade(0.5).toLinear() - val shifted = listOf( - seed.saturate(1.0), - seed.saturate(1.0) - ) - for (s in shifted) { - assertEquals(Linearity.LINEAR, s.linearity) - assertTrue(seed.toVector4().distanceTo(s.toVector4()) < 1E-4) - } - } - - @Test - fun testMixedWith() { - val seed = ColorRGBa.RED - val mixed = listOf( - seed.mixedWith(ColorRGBa.BLUE, 0.5), - seed.mixedWith(ColorRGBa.BLUE, 0.5), - seed.mixedWith(ColorRGBa.BLUE, 0.5) - ) - } - -} \ No newline at end of file diff --git a/orx-color/src/jvmDemo/kotlin/DemoColorPalette01.kt b/orx-color/src/jvmDemo/kotlin/DemoColorPalette01.kt deleted file mode 100644 index 15e31526..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoColorPalette01.kt +++ /dev/null @@ -1,62 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.palettes.analogous -import org.openrndr.extra.color.palettes.splitComplementary -import org.openrndr.extra.color.palettes.tetradic -import org.openrndr.extra.color.palettes.triadic -import org.openrndr.extra.color.presets.ORANGE -import org.openrndr.extra.color.spaces.* - -/** - * Demonstrates the creation of color palettes using various available methods - */ -fun main() = application { - configure { - width = 720 - height = 540 - } - program { - // HueShiftableColor: - // HPLuv HSL HSV LCHab LCHuv XSL XSV XSLuv HSLuv OKHSL OKHSV OKLCH - val palette0 = RGB.PINK.analogous(360.0, 10) - val palette1 = RGB.RED.analogous(240.0, 3) - val palette2 = RGB.YELLOW.triadic() - val palette3 = RGB.CYAN.tetradic() - val palette4 = RGB.CYAN.tetradic(0.5) - val palette5 = RGB.ORANGE.splitComplementary(0.2, true) - extend { - drawer.clear(ColorRGBa.WHITE) - drawer.stroke = ColorRGBa.BLACK.opacify(0.25) - - palette0.forEachIndexed { i, c -> - drawer.fill = c - drawer.circle(100.0 + i * 50.0, 80.0, 50.0) - } - - palette1.forEachIndexed { i, c -> - drawer.fill = c - drawer.circle(100.0 + i * 50.0, 200.0, 50.0) - } - - palette2.forEachIndexed { i, c -> - drawer.fill = c - drawer.circle(440.0 + i * 50.0, 200.0, 50.0) - } - - palette3.forEachIndexed { i, c -> - drawer.fill = c - drawer.circle(100.0 + i * 50.0, 320.0, 50.0) - } - - palette4.forEachIndexed { i, c -> - drawer.fill = c - drawer.circle(400.0 + i * 50.0, 320.0, 50.0) - } - - palette5.forEachIndexed { i, c -> - drawer.fill = c - drawer.circle(100.0 + i * 50.0, 440.0, 50.0) - } - } - } -} \ No newline at end of file diff --git a/orx-color/src/jvmDemo/kotlin/DemoColorPalette02.kt b/orx-color/src/jvmDemo/kotlin/DemoColorPalette02.kt deleted file mode 100644 index 5924948e..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoColorPalette02.kt +++ /dev/null @@ -1,36 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.color.palettes.analogous -import org.openrndr.extra.color.spaces.HSLuv -import org.openrndr.extra.color.spaces.RGB -import org.openrndr.extra.color.tools.shadeLuminosity - -/** - * By default, generated palettes contain colors of varying hue - * but similar brightness and saturation. - * Here we alter the brightness of each color using .shade() for - * an increased dynamic range. - */ -fun main() = application { - configure { - width = 720 - height = 360 - } - program { - val count = 8 - val palette = RGB.PINK.analogous(360.0, count).mapIndexed { i, c -> - c.shadeLuminosity((i + 1.0) / count) - }.reversed() - extend { - drawer.stroke = null - - palette.forEachIndexed { i, c -> - drawer.fill = c - drawer.rectangle( - 0.0, i * height / count.toDouble(), - width.toDouble(), height / count.toDouble() - ) - } - - } - } -} \ No newline at end of file diff --git a/orx-color/src/jvmDemo/kotlin/DemoColorPlane01.kt b/orx-color/src/jvmDemo/kotlin/DemoColorPlane01.kt deleted file mode 100644 index a6ef052f..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoColorPlane01.kt +++ /dev/null @@ -1,93 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.color.spaces.ColorOKLCHa -import org.openrndr.extra.meshgenerators.sphereMesh -import org.openrndr.math.Vector3 -import kotlin.math.cos - -/** - * Visualizes a plane of ColorOKLCH colors as small 3D spheres - * inside a 3D box. The plane represents all available hues and chromas. - * The luminosity used to create the colors is modulated over time - * with a slow sine wave. - * Instanced rendering is used to render 90 x 100 colored spheres, - * each with a unique position based on the RGB components of the color. - * - * Since the OKLCH color space is larger than the RGB space, some - * spheres would be outside the 3D box, but they are - * actually clipped to the walls. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val mesh = sphereMesh(8, 8, radius = 0.1) - - val instanceData = vertexBuffer( - vertexFormat { - attribute("instanceColor", VertexElementType.VECTOR4_FLOAT32) - attribute("instancePosition", VertexElementType.VECTOR3_FLOAT32) - }, - 90 * 100 - ) - println(extensions.size) - extend(Orbital()) - - extend { - drawer.clear(ColorRGBa.WHITE) - drawer.stroke = null - drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 16.0) - - instanceData.put { - for (hue in 0 until 360 step 4) { - for (chroma in 0 until 100 step 1) { - val lch = ColorOKLCHa(cos(seconds * 0.1) * 0.5 + 0.5, chroma / 100.0, hue.toDouble()) - val srgb = lch.toRGBa().toSRGB().clip() - write(srgb) - write(Vector3((srgb.r - 0.5) * 10.0, (srgb.g - 0.5) * 10.0, (srgb.b - 0.5) * 10.0)) - } - } - } - drawer.isolated { - drawer.shadeStyle = shadeStyle { - - vertexTransform = """ - x_position += i_instancePosition; - """.trimIndent() - fragmentTransform = """ - x_fill = vi_instanceColor; - """.trimIndent() - } - - drawer.vertexBufferInstances(listOf(mesh), listOf(instanceData), DrawPrimitive.TRIANGLES, 90 * 100) - } - - // Draw the edges of a 3D cube - drawer.stroke = ColorRGBa.BLACK.opacify(0.25) - drawer.strokeWeight = 10.0 - drawer.lineSegments( - listOf( - Vector3(-5.0, -5.0, -5.0), Vector3(5.0, -5.0, -5.0), - Vector3(-5.0, -5.0, 5.0), Vector3(5.0, -5.0, 5.0), - Vector3(-5.0, 5.0, -5.0), Vector3(5.0, 5.0, -5.0), - Vector3(-5.0, 5.0, 5.0), Vector3(5.0, 5.0, 5.0), - - Vector3(-5.0, -5.0, -5.0), Vector3(-5.0, 5.0, -5.0), - Vector3(5.0, -5.0, -5.0), Vector3(5.0, 5.0, -5.0), - Vector3(-5.0, -5.0, 5.0), Vector3(-5.0, 5.0, 5.0), - Vector3(5.0, -5.0, 5.0), Vector3(5.0, 5.0, 5.0), - - Vector3(-5.0, -5.0, -5.0), Vector3(-5.0, -5.0, 5.0), - Vector3(5.0, -5.0, -5.0), Vector3(5.0, -5.0, 5.0), - Vector3(-5.0, 5.0, -5.0), Vector3(-5.0, 5.0, 5.0), - Vector3(5.0, 5.0, -5.0), Vector3(5.0, 5.0, 5.0), - - ) - ) - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/DemoColorPlane02.kt b/orx-color/src/jvmDemo/kotlin/DemoColorPlane02.kt deleted file mode 100644 index 27212b0a..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoColorPlane02.kt +++ /dev/null @@ -1,77 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.color.spaces.ColorOKLCHa -import org.openrndr.extra.meshgenerators.sphereMesh -import org.openrndr.math.Vector3 -import kotlin.math.cos - -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val mesh = sphereMesh(8, 8, radius = 0.1) - - val instanceData = vertexBuffer( - vertexFormat { - attribute("instanceColor", VertexElementType.VECTOR4_FLOAT32) - attribute("instancePosition", VertexElementType.VECTOR3_FLOAT32) - }, - 100 * 100 - ) - extend(Orbital()) - extend { - drawer.clear(ColorRGBa.WHITE) - - drawer.stroke = null - - drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 16.0) - - instanceData.put { - for (lumo in 0 until 100 step 1) { - for (chroma in 0 until 100 step 1) { - val lch = ColorOKLCHa(lumo / 100.0, chroma / 100.0, cos(seconds * 0.1) * 360.0) - val srgb = lch.toRGBa().toSRGB().clip() - write(srgb) - write(Vector3((srgb.r - 0.5) * 10.0, (srgb.g - 0.5) * 10.0, (srgb.b - 0.5) * 10.0)) - } - } - } - drawer.isolated { - drawer.shadeStyle = shadeStyle { - vertexTransform = """ - x_position += i_instancePosition; - """.trimIndent() - fragmentTransform = """ - x_fill = vi_instanceColor; - """.trimIndent() - } - drawer.vertexBufferInstances(listOf(mesh), listOf(instanceData), DrawPrimitive.TRIANGLES, 90 * 100) - } - - drawer.stroke = ColorRGBa.BLACK.opacify(0.25) - drawer.strokeWeight = 10.0 - drawer.lineSegments( - listOf( - Vector3(-5.0, -5.0, -5.0), Vector3(5.0, -5.0, -5.0), - Vector3(-5.0, -5.0, 5.0), Vector3(5.0, -5.0, 5.0), - Vector3(-5.0, 5.0, -5.0), Vector3(5.0, 5.0, -5.0), - Vector3(-5.0, 5.0, 5.0), Vector3(5.0, 5.0, 5.0), - - Vector3(-5.0, -5.0, -5.0), Vector3(-5.0, 5.0, -5.0), - Vector3(5.0, -5.0, -5.0), Vector3(5.0, 5.0, -5.0), - Vector3(-5.0, -5.0, 5.0), Vector3(-5.0, 5.0, 5.0), - Vector3(5.0, -5.0, 5.0), Vector3(5.0, 5.0, 5.0), - - Vector3(-5.0, -5.0, -5.0), Vector3(-5.0, -5.0, 5.0), - Vector3(5.0, -5.0, -5.0), Vector3(5.0, -5.0, 5.0), - Vector3(-5.0, 5.0, -5.0), Vector3(-5.0, 5.0, 5.0), - Vector3(5.0, 5.0, -5.0), Vector3(5.0, 5.0, 5.0), - ) - ) - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/DemoColorSequence01.kt b/orx-color/src/jvmDemo/kotlin/DemoColorSequence01.kt deleted file mode 100644 index d564e09b..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoColorSequence01.kt +++ /dev/null @@ -1,69 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.isolated -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.color.palettes.ColorSequence -import org.openrndr.extra.color.presets.MEDIUM_AQUAMARINE -import org.openrndr.extra.color.presets.ORANGE -import org.openrndr.extra.color.spaces.toOKLABa -import org.openrndr.extra.meshgenerators.sphereMesh -import org.openrndr.math.Vector3 - -/** - * A demo that demonstrates 3D objects with custom shading and color gradients. - * - * The application setup involves: - * - Configuring the application window dimensions. - * - Creating a color gradient using `ColorSequence` and converting it to a `ColorBuffer` for shading purposes. - * - Defining a 3D sphere mesh with specified resolution. - * - * The rendering process includes: - * - Setting up an orbital camera extension to provide an interactive 3D view. - * - Applying a custom fragment shader with a palette-based shading style. - * - Rendering a grid of 3D spheres, each transformed and rotated to create a dynamic pattern. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val cs = ColorSequence( - listOf( - 0.0 to ColorRGBa.PINK, - 0.25 to ColorRGBa.ORANGE.toOKLABa(), - 0.27 to ColorRGBa.WHITE.toOKLABa(), - 0.32 to ColorRGBa.BLUE, - 1.0 to ColorRGBa.MEDIUM_AQUAMARINE - ) - ) - val palette = cs.toColorBuffer(drawer, 256, 16) - val sphere = sphereMesh(sides = 48, segments = 48) - - extend(Orbital()) { - fov = 50.0 - eye = Vector3(0.0, 0.0, 13.0) - } - extend { - drawer.shadeStyle = shadeStyle { - fragmentTransform = """ - float d = normalize(va_normal).z; - x_fill = texture(p_palette, vec2(1.0-d, 0.0)); - """.trimIndent() - parameter("palette", palette) - } - for (j in -2..2) { - for (i in -2..2) { - drawer.isolated { - drawer.translate(i * 2.0, j * 2.0, 0.0) - drawer.rotate(Vector3.UNIT_Y, j * 30.0) - drawer.rotate(Vector3.UNIT_X, i * 30.0) - drawer.vertexBuffer(sphere, DrawPrimitive.TRIANGLES) - } - } - } - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/DemoDeltaE.kt b/orx-color/src/jvmDemo/kotlin/DemoDeltaE.kt deleted file mode 100644 index 784cf1b9..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoDeltaE.kt +++ /dev/null @@ -1,37 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.spaces.toOKHSVa -import org.openrndr.extra.color.statistics.deltaE76 -import org.openrndr.math.Polar - -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - extend { - drawer.clear(ColorRGBa.BLACK) - drawer.fill = null - drawer.stroke = ColorRGBa.WHITE.opacify(0.2) - for (i in 10 until 270 step 10) { - drawer.circle(drawer.bounds.center, i.toDouble()) - } - - drawer.stroke = null - - val startColor = ColorRGBa.RED.toOKHSVa().shiftHue(seconds * 36.0).toRGBa() - drawer.circles { - for (j in 99 downTo 0) { - for (i in 0 until 360 step 10) { - val color = startColor.toOKHSVa().shiftHue(i.toDouble()).saturate(j / 99.0).toRGBa() - val distance = color.deltaE76(startColor) - val p = Polar(seconds * 36.0 + i.toDouble(), distance).cartesian + drawer.bounds.center - fill = color - circle(p, 2.0) - } - } - } - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/DemoFettePalette01.kt b/orx-color/src/jvmDemo/kotlin/DemoFettePalette01.kt deleted file mode 100644 index 45ffda96..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoFettePalette01.kt +++ /dev/null @@ -1,84 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.isolated -import org.openrndr.extra.color.fettepalette.ColorRamp -import org.openrndr.extra.color.fettepalette.Lamé -import org.openrndr.extra.color.fettepalette.generateColorRamp -import org.openrndr.math.Vector2 -import kotlin.random.Random - -fun main() = application { - configure { - width = 720 - height = 720 - } - - program { - val total = 9 - - extend { - val ramp = generateColorRamp( - total = total, - centerHue = (mouse.position.x / width) * 360.0, - curveMethod = Lamé, - hueCycle = mouse.position.y / height, - curveAccent = 0.0, - offsetTint = 0.01, - offsetShade = 0.01, - tintShadeHueShift = 0.01, - offsetCurveModTint = 0.03, - offsetCurveModShade = 0.03, - minSaturationLight = Vector2.ZERO, - maxSaturationLight = Vector2.ONE, - useOK = true - ) - - fun rampSquare(ramp: ColorRamp, random: Random, position: Vector2, width: Double) { - drawer.isolated { - drawer.fill = ramp.baseColors.random(random).toRGBa() - drawer.stroke = null - drawer.rectangle(position, width, width) - - drawer.fill = ramp.lightColors.random(random).toRGBa() - drawer.rectangle(position + Vector2(width / 4.0, width / 4.0), width / 4.0, width / 2.0) - - val dc = ramp.darkColors.shuffled(random).take(2) - - drawer.fill = dc[0].toRGBa() - drawer.rectangle(position + Vector2(width / 2.0, width / 4.0), width / 4.0, width / 4.0) - - drawer.fill = dc[1].toRGBa() - drawer.rectangle(position + Vector2(width / 2.0, width / 2.0), width / 4.0, width / 4.0) - } - } - - - drawer.isolated { - for ((index, i) in ramp.lightColors.withIndex()) { - drawer.stroke = null - drawer.fill = i.toRGBa() - drawer.rectangle(20.0, 20.0, 50.0, 50.0) - drawer.translate(50.0, 0.0) - } - } - drawer.isolated { - for ((index, i) in ramp.baseColors.withIndex()) { - drawer.stroke = null - drawer.fill = i.toRGBa() - drawer.rectangle(20.0, 70.0, 50.0, 50.0) - drawer.translate(50.0, 0.0) - } - } - drawer.isolated { - for ((index, i) in ramp.darkColors.withIndex()) { - drawer.stroke = null - drawer.fill = i.toRGBa() - drawer.rectangle(20.0, 120.0, 50.0, 50.0) - drawer.translate(50.0, 0.0) - } - } - - val random = Random(seconds.toInt()) - rampSquare(ramp, random, Vector2(180.0, 180.0), 360.0) - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/DemoFettePalette02.kt b/orx-color/src/jvmDemo/kotlin/DemoFettePalette02.kt deleted file mode 100644 index f1287eba..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoFettePalette02.kt +++ /dev/null @@ -1,75 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.isolated -import org.openrndr.extra.color.fettepalette.ColorRamp -import org.openrndr.extra.color.fettepalette.ColorRampParameters -import org.openrndr.extra.color.fettepalette.generateColorRamp -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.gui.addTo -import org.openrndr.math.Vector2 -import kotlin.random.Random - -fun main() = application { - configure { - width = 720 - height = 720 - } - - program { - val gui = GUI() - val parameters = ColorRampParameters() - parameters.addTo(gui) - - extend(gui) - extend { - val ramp = generateColorRamp(parameters) - fun rampSquare(ramp: ColorRamp, random: Random, position: Vector2, width: Double) { - drawer.isolated { - drawer.fill = ramp.baseColors.random(random).toRGBa() - drawer.stroke = null - drawer.rectangle(position, width, width) - - drawer.fill = ramp.lightColors.random(random).toRGBa() - drawer.rectangle(position + Vector2(width / 4.0, width / 4.0), width / 4.0, width / 2.0) - - val dc = ramp.darkColors.shuffled(random).take(2) - - drawer.fill = dc[0].toRGBa() - drawer.rectangle(position + Vector2(width / 2.0, width / 4.0), width / 4.0, width / 4.0) - - drawer.fill = dc[1].toRGBa() - drawer.rectangle(position + Vector2(width / 2.0, width / 2.0), width / 4.0, width / 4.0) - } - } - - drawer.translate(200.0, 0.0) - - drawer.isolated { - for ((index, i) in ramp.lightColors.withIndex()) { - drawer.stroke = null - drawer.fill = i.toRGBa() - drawer.rectangle(20.0, 20.0, 50.0, 50.0) - drawer.translate(50.0, 0.0) - } - } - drawer.isolated { - for ((index, i) in ramp.baseColors.withIndex()) { - drawer.stroke = null - drawer.fill = i.toRGBa() - drawer.rectangle(20.0, 70.0, 50.0, 50.0) - drawer.translate(50.0, 0.0) - } - } - drawer.isolated { - for ((index, i) in ramp.darkColors.withIndex()) { - drawer.stroke = null - drawer.fill = i.toRGBa() - drawer.rectangle(20.0, 120.0, 50.0, 50.0) - drawer.translate(50.0, 0.0) - } - } - - val random = Random(seconds.toInt()) - rampSquare(ramp, random, Vector2(20.0, 180.0), 360.0) - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/DemoHSLUV01.kt b/orx-color/src/jvmDemo/kotlin/DemoHSLUV01.kt deleted file mode 100644 index 53a54873..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoHSLUV01.kt +++ /dev/null @@ -1,50 +0,0 @@ -// Draw rectangles shaded in RGB and HSLUV space - -import org.openrndr.application -import org.openrndr.color.ColorHSLa -import org.openrndr.color.ColorRGBa -import org.openrndr.color.rgb -import org.openrndr.draw.isolated -import org.openrndr.draw.loadFont -import org.openrndr.extra.color.spaces.ColorHSLUVa -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -fun main() = application { - configure { - width = 720 - height = 540 - } - program { - val font = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 26.0) - extend { - drawer.stroke = null - drawer.clear(rgb(0.3)) - val s = mouse.position.x / width - val l = mouse.position.y / height - for (a in 0 until 360 step 12) { - val pos = Vector2(0.0, 110.0) - drawer.isolated { - translate(bounds.center) - rotate(a * 1.0) - - fill = ColorHSLUVa(a * 1.0, s, l).toRGBa().toSRGB() - rectangle(Rectangle(pos * 1.2, 40.0, 300.0)) - - fill = ColorHSLa(a * 1.0, s, l).toRGBa() - rectangle(Rectangle.fromCenter(pos, 30.0, 60.0)) - } - } - drawer.fontMap = font - drawer.fill = if (l > 0.8) ColorRGBa.BLACK else ColorRGBa.WHITE - drawer.text("HSLa", width * 0.48, height * 0.73) - drawer.text("HSLUVa", width * 0.8, height * 0.52) - drawer.text( - "hue: 0 to 360, " + - "saturation: ${String.format("%.02f", s)}, " + - "lightness: ${String.format("%.02f", l)}", - 30.0, height - 30.0 - ) - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/DemoHSLUV02.kt b/orx-color/src/jvmDemo/kotlin/DemoHSLUV02.kt deleted file mode 100644 index 140d37d3..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoHSLUV02.kt +++ /dev/null @@ -1,46 +0,0 @@ -// Visualize HSLUV color space by drawing a phyllotaxis pattern - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.spaces.toHSLUVa -import org.openrndr.math.Polar -import org.openrndr.math.Vector2 -import kotlin.math.sqrt - -fun main() = application { - configure { - width = 720 - height = 720 - } - - val g = Math.PI * 2.0 * (1.0 - 1.0 / 1.61803398875) - fun phyllotaxis(count: Int) = sequence { - for (i in 0 until count) { - yield(Polar(Math.toDegrees(i * 1.0), g * i)) - } - } - - program { - extend { - drawer.clear(ColorRGBa.GRAY) - val color = ColorRGBa.RED - val hc = color.toHSLUVa() - drawer.stroke = null - drawer.strokeWeight = 0.0 - - val count = 400 - val bobRadius = 20.0 - - for (i in phyllotaxis(count)) { - val h = i.theta - val s = i.radius / (count * g) - for (l in 9 downTo 0) { - val position = - i.cartesian / (count * g) * (width / 2.0 - bobRadius) + Vector2(width / 2.0, height / 2.0) - drawer.fill = hc.shiftHue(h).saturate(s).shade((9 - l) / 4.5).toRGBa().toSRGB() - drawer.circle(position, sqrt(s) * 20.0 * l / 9.0) - } - } - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/DemoHueTools01.kt b/orx-color/src/jvmDemo/kotlin/DemoHueTools01.kt deleted file mode 100644 index 6ed5f060..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoHueTools01.kt +++ /dev/null @@ -1,39 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.isolated -import org.openrndr.extra.color.spaces.OKHSV -import org.openrndr.extra.color.tools.mixHue -import org.openrndr.extra.color.tools.withHue - -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - extend { - val seedColor = ColorRGBa.PINK - val targetHue = seconds * 100.0 - - val rows = 10 - val columns = 12 - - val cellWidth = width / columns.toDouble() - val cellHeight = height / rows.toDouble() - - drawer.stroke = null - for (j in 0 until 10) { - drawer.isolated { - for (i in 0 until columns) { - drawer.fill = seedColor - .withHue(i * 360.0 / columns) - .mixHue(targetHue, j / (rows.toDouble() - 1.0)) - drawer.rectangle(0.0, 0.0, cellWidth, cellHeight) - drawer.translate(cellWidth, 0.0) - } - } - drawer.translate(0.0, cellHeight) - } - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/DemoMixSpectral01.kt b/orx-color/src/jvmDemo/kotlin/DemoMixSpectral01.kt deleted file mode 100644 index 39918a7b..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoMixSpectral01.kt +++ /dev/null @@ -1,35 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.isolated -import org.openrndr.extra.color.mixing.mixSpectral -import org.openrndr.extra.color.spaces.OKHSV -import org.openrndr.extra.color.spaces.OKLab -import org.openrndr.extra.color.tools.saturate -import org.openrndr.extra.color.tools.shadeLuminosity -import org.openrndr.extra.color.tools.shiftHue - -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - extend { - val a = ColorRGBa.BLUE.shiftHue(0.0).saturate(0.3).shadeLuminosity(0.3) - val b = ColorRGBa.BLUE.shiftHue(60.0).saturate(0.8) - - drawer.isolated { - for (i in 0 until 60) { - val c = mixSpectral(a, b, i / 59.0, 0.0, 0.0).toSRGB() - drawer.fill = c - drawer.stroke = null - drawer.rectangle(0.0, 0.0, width / 60.0, 1.0 * height) - - drawer.translate(width / 60.0, 0.0) - - } - } - drawer.translate(0.0, 0.5 * height / 60.0) - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/DemoOKHSV01.kt b/orx-color/src/jvmDemo/kotlin/DemoOKHSV01.kt deleted file mode 100644 index c2054dcf..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoOKHSV01.kt +++ /dev/null @@ -1,36 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.color.rgb -import org.openrndr.extra.color.spaces.ColorOKHSLa -import org.openrndr.extra.color.spaces.ColorOKHSVa - -fun main() = application { - configure { - width = 720 - height = 160 - } - program { - extend { - drawer.clear(rgb(0.2)) - - val c = ColorRGBa.GREEN - val okhsv = ColorOKHSVa.fromColorRGBa(c) - val hsv = c.toHSVa() - val hsl = c.toHSLa() - val okhsl = ColorOKHSLa.fromColorRGBa(c) - - for (i in 0 until 36) { - drawer.fill = okhsv.shiftHue(i * 10.0).saturate(1.0).toRGBa() - drawer.rectangle(i * 10.0, 40.0, 10.0, 10.0) - drawer.fill = hsv.shiftHue(i * 10.0).saturate(1.0).toRGBa() - drawer.rectangle(i * 10.0, 60.0, 10.0, 10.0) - - drawer.fill = okhsl.shiftHue(i * 10.0).saturate(1.0).toRGBa() - drawer.rectangle(i * 10.0, 80.0, 10.0, 10.0) - drawer.fill = hsl.shiftHue(i * 10.0).saturate(1.0).toRGBa() - drawer.rectangle(i * 10.0, 100.0, 10.0, 10.0) - - } - } - } -} \ No newline at end of file diff --git a/orx-color/src/jvmDemo/kotlin/DemoXSLUV01.kt b/orx-color/src/jvmDemo/kotlin/DemoXSLUV01.kt deleted file mode 100644 index 97a19c68..00000000 --- a/orx-color/src/jvmDemo/kotlin/DemoXSLUV01.kt +++ /dev/null @@ -1,59 +0,0 @@ -// Visualize XSLUV color space by drawing a recursively subdivided arcs - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.spaces.ColorXSLUVa -import org.openrndr.math.Polar -import org.openrndr.shape.contour - -fun main() = application { - configure { - width = 720 - height = 720 - } - - class Arc(val start: Double, val radius: Double, val length: Double, val height: Double) { - fun split(offset: Double = 0.0): List { - val hl = length / 2.0 - return listOf( - Arc(start, radius + offset, hl, height), - Arc(start + hl, radius + offset, hl, height) - ) - } - - val contour - get() = contour { - moveTo(Polar(start, radius).cartesian) - arcTo(radius, radius, length, false, true, Polar(start + length, radius).cartesian) - lineTo(Polar(start + length, radius + height).cartesian) - arcTo(radius + height, radius + height, length, false, false, Polar(start, radius + height).cartesian) - lineTo(anchor) - close() - } - } - - fun List.split(depth: Int): List = if (depth == 0) { - this - } else { - this + flatMap { it.split(it.height) }.split(depth - 1) - } - - program { - val arcs = (0..4).map { Arc(it * 90.0 - 45.0, 50.0, 90.0, 50.0) }.split(5) - - extend { - drawer.clear(ColorRGBa.GRAY) - val color = ColorRGBa.RED - drawer.stroke = ColorRGBa.BLACK - drawer.strokeWeight = 1.0 - drawer.translate(drawer.bounds.center) - val l = if (System.getProperty("takeScreenshot") == "true") 0.7 else mouse.position.y / height - val s = if (System.getProperty("takeScreenshot") == "true") 1.0 else mouse.position.x / width - for (arc in arcs) { - val xsluv = ColorXSLUVa(arc.start + arc.length / 2.0, s, l, 1.0) - drawer.fill = xsluv.toRGBa() - drawer.contour(arc.contour) - } - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/colorRange/DemoColorRange01.kt b/orx-color/src/jvmDemo/kotlin/colorRange/DemoColorRange01.kt deleted file mode 100644 index 4f761c83..00000000 --- a/orx-color/src/jvmDemo/kotlin/colorRange/DemoColorRange01.kt +++ /dev/null @@ -1,47 +0,0 @@ -package colorRange - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.palettes.rangeTo -import org.openrndr.extra.color.spaces.toHSLUVa -import org.openrndr.math.Vector2 -import org.openrndr.math.map -import org.openrndr.shape.Rectangle - -/** - * Comparison of color lists generated by interpolating from - * `PINK` to `BLUE` in six different color spaces. - */ -fun main() = application { - configure { - width = 720 - height = 540 - } - program { - val numColors = 10 - val colorLists = listOf( - ColorRGBa.PINK..ColorRGBa.BLUE.toHSVa() blend numColors, - ColorRGBa.PINK..ColorRGBa.BLUE blend numColors, - ColorRGBa.PINK..ColorRGBa.BLUE.toHSLUVa() blend numColors, - ColorRGBa.PINK..ColorRGBa.BLUE.toXSVa() blend numColors, - ColorRGBa.PINK..ColorRGBa.BLUE.toLUVa() blend numColors, - ColorRGBa.PINK..ColorRGBa.BLUE.toLCHUVa() blend numColors - ) - extend { - drawer.clear(ColorRGBa.WHITE) - drawer.stroke = null - colorLists.forEachIndexed { listId, colorList -> - val x = map(0.0, 2.0, - width * 0.2, width * 0.8, listId % 3.0) - val y = map(0.0, 1.0, - height * 0.3, height * 0.7, (listId / 3) * 1.0) - colorList.forEachIndexed { colorId, color -> - drawer.fill = color - drawer.rectangle( - Rectangle.fromCenter(Vector2(x, y), - 180.0 - colorId * 150.0 / numColors)) - } - } - } - } -} \ No newline at end of file diff --git a/orx-color/src/jvmDemo/kotlin/colorRange/DemoColorRange02.kt b/orx-color/src/jvmDemo/kotlin/colorRange/DemoColorRange02.kt deleted file mode 100644 index c604d898..00000000 --- a/orx-color/src/jvmDemo/kotlin/colorRange/DemoColorRange02.kt +++ /dev/null @@ -1,38 +0,0 @@ -package colorRange - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.palettes.colorSequence -import org.openrndr.extra.color.spaces.toHSLUVa - -/** - * Demonstrates how to create a `ColorSequence` containing three colors, one of them in the HSLUV color space. - * - * Each color in the sequence is assigned a normalized position: in this program, one at the start (0.0), - * one in the middle (0.5) and one at the end (1.0). - * - * The `ColorSpace.blend()` method is used to get a list with 18 interpolated `ColorRGBa` colors, - * then those colors are drawn as vertical rectangles covering the whole window. - */ -fun main() = application { - configure { - width = 720 - height = 360 - } - program { - extend { - val cs = colorSequence( - 0.0 to ColorRGBa.PINK, - 0.5 to ColorRGBa.BLUE, - 1.0 to ColorRGBa.PINK.toHSLUVa() // <-- note this color is in HSLUV - ) - - for (c in cs blend (width / 40)) { - drawer.fill = c - drawer.stroke = null - drawer.rectangle(0.0, 0.0, 40.0, height.toDouble()) - drawer.translate(40.0, 0.0) - } - } - } -} \ No newline at end of file diff --git a/orx-color/src/jvmDemo/kotlin/colorRange/DemoColorRange03.kt b/orx-color/src/jvmDemo/kotlin/colorRange/DemoColorRange03.kt deleted file mode 100644 index 1ce39264..00000000 --- a/orx-color/src/jvmDemo/kotlin/colorRange/DemoColorRange03.kt +++ /dev/null @@ -1,60 +0,0 @@ -package colorRange - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.loadFont -import org.openrndr.extra.color.palettes.rangeTo -import org.openrndr.extra.color.spaces.* - -/** - * This program creates color interpolations from `ColorRGBa.BLUE` to - * `ColorRGBa.PINK` in 25 steps in multiple color spaces. - * - * The window height is adjusted based on the number of interpolations to show. - * - * The resulting gradients differ in saturation and brightness and apparently include more - * `BLUE` or more `PINK` depending on the chosen color space. - */ -fun main() = application { - val colorA = ColorRGBa.BLUE - val colorB = ColorRGBa.PINK - - val stepCount = 25 - - val allSteps = listOf( - "RGB" to (colorA..colorB blend stepCount), - "RGB linear" to (colorA.toLinear()..colorB.toLinear() blend stepCount), - "HSV" to (colorA..colorB.toHSVa() blend stepCount), - "Lab" to (colorA.toLABa()..colorB.toLABa() blend stepCount), - "LCh(ab)" to (colorA.toLCHABa()..colorB.toLCHABa() blend stepCount), - "OKLab" to (colorA.toOKLABa()..colorB.toOKLABa() blend stepCount), - "OKLCh" to (colorA.toOKLCHa()..colorB.toOKLCHa() blend stepCount), - "OKHSV" to (colorA.toOKHSVa()..colorB.toOKHSVa() blend stepCount), - "OKHSL" to (colorA.toOKHSLa()..colorB.toOKHSLa() blend stepCount), - "HSLUV" to (colorA.toHSLUVa()..colorB.toHSLUVa() blend stepCount), - "XSLUV" to (colorA.toXSLUVa()..colorB.toXSLUVa() blend stepCount), - ) - - configure { - width = 720 - height = 30 + 50 * allSteps.size - } - program { - extend { - drawer.clear(ColorRGBa.WHITE) - drawer.stroke = null - drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 16.0) - drawer.translate(20.0, 20.0) - for ((label, steps) in allSteps) { - drawer.fill = ColorRGBa.GRAY.shade(0.25) - drawer.text(label, 0.0, 24.0) - - for (i in steps.indices) { - drawer.fill = steps[i].toSRGB() - drawer.rectangle(100.0 + i * 20.0, 0.0, 20.0, 40.0) - } - drawer.translate(0.0, 50.0) - } - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/colorRange/DemoColorRange04.kt b/orx-color/src/jvmDemo/kotlin/colorRange/DemoColorRange04.kt deleted file mode 100644 index a46676a4..00000000 --- a/orx-color/src/jvmDemo/kotlin/colorRange/DemoColorRange04.kt +++ /dev/null @@ -1,98 +0,0 @@ -package colorRange - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.isolated -import org.openrndr.draw.loadFont -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.color.palettes.rangeTo -import org.openrndr.extra.color.spaces.toHSLUVa -import org.openrndr.extra.color.spaces.toOKLABa -import org.openrndr.extra.color.spaces.toOKLCHa -import org.openrndr.extra.color.spaces.toXSLUVa -import org.openrndr.extra.meshgenerators.sphereMesh -import org.openrndr.math.Vector3 - -/** - * A visualization of color interpolations inside a 3D RGB cube with an interactive 3D `Orbital` camera. - * - * The hues of the source and target colors are animated over time. - * - * The color interpolations are shown simultaneously in nine different color spaces, revealing how in - * each case they share common starting and ending points in 3D, but have unique paths going from - * start to end. - * - * By rotating the cube 90 degrees towards the left and slightly zooming out, one can appreciate how - * one of the points moves along the edges of the cube, while the other moves on the edges of a - * smaller, invisible cube. - * - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val mesh = sphereMesh(8, 8, radius = 0.1) - - extend(Orbital()) - - extend { - drawer.clear(ColorRGBa.WHITE) - - val colorA = ColorRGBa.BLUE.toHSVa().shiftHue(seconds * 40.0).toRGBa() - val colorB = ColorRGBa.PINK.toHSVa().shiftHue(-seconds * 34.0).toRGBa() - - val stepCount = 25 - - val allSteps = listOf( - "RGB" to (colorA..colorB blend stepCount), - "RGB linear" to (colorA.toLinear()..colorB.toLinear() blend stepCount), - "HSV" to (colorA..colorB.toHSVa() blend stepCount), - "Lab" to (colorA.toLABa()..colorB.toLABa() blend stepCount), - "LCh(ab)" to (colorA.toLCHABa()..colorB.toLCHABa() blend stepCount), - "OKLab" to (colorA.toOKLABa()..colorB.toOKLABa() blend stepCount), - "OKLCh" to (colorA.toOKLCHa()..colorB.toOKLCHa() blend stepCount), - "HSLUV" to (colorA.toHSLUVa()..colorB.toHSLUVa() blend stepCount), - "XSLUV" to (colorA.toXSLUVa()..colorB.toXSLUVa() blend stepCount), - ) - - for ((_, steps) in allSteps) { - for (i in steps.indices) { - val srgb = steps[i].toSRGB().clip() - drawer.fill = srgb - drawer.isolated { - drawer.translate((srgb.r - 0.5) * 10.0, (srgb.g - 0.5) * 10.0, (srgb.b - 0.5) * 10.0) - drawer.vertexBuffer(mesh, DrawPrimitive.TRIANGLES) - } - } - val positions = steps.map { - val l = it.toSRGB().clip() - Vector3((l.r - 0.5) * 10.0, (l.g - 0.5) * 10.0, (l.b - 0.5) * 10.0) - } - drawer.stroke = ColorRGBa.BLACK.opacify(0.25) - drawer.strokeWeight = 10.0 - drawer.lineStrip(positions) - } - drawer.lineSegments( - listOf( - Vector3(-5.0, -5.0, -5.0), Vector3(5.0, -5.0, -5.0), - Vector3(-5.0, -5.0, 5.0), Vector3(5.0, -5.0, 5.0), - Vector3(-5.0, 5.0, -5.0), Vector3(5.0, 5.0, -5.0), - Vector3(-5.0, 5.0, 5.0), Vector3(5.0, 5.0, 5.0), - - Vector3(-5.0, -5.0, -5.0), Vector3(-5.0, 5.0, -5.0), - Vector3(5.0, -5.0, -5.0), Vector3(5.0, 5.0, -5.0), - Vector3(-5.0, -5.0, 5.0), Vector3(-5.0, 5.0, 5.0), - Vector3(5.0, -5.0, 5.0), Vector3(5.0, 5.0, 5.0), - - Vector3(-5.0, -5.0, -5.0), Vector3(-5.0, -5.0, 5.0), - Vector3(5.0, -5.0, -5.0), Vector3(5.0, -5.0, 5.0), - Vector3(-5.0, 5.0, -5.0), Vector3(-5.0, 5.0, 5.0), - Vector3(5.0, 5.0, -5.0), Vector3(5.0, 5.0, 5.0), - ) - ) - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/colormap/DemoSpectralZucconiColormap.kt b/orx-color/src/jvmDemo/kotlin/colormap/DemoSpectralZucconiColormap.kt deleted file mode 100644 index 06135ba1..00000000 --- a/orx-color/src/jvmDemo/kotlin/colormap/DemoSpectralZucconiColormap.kt +++ /dev/null @@ -1,37 +0,0 @@ -package colormap - -import org.openrndr.application -import org.openrndr.extra.color.colormaps.spectralZucconi6 -import org.openrndr.extra.noise.fastFloor -import kotlin.math.sin - -/** - * This program demonstrates the `spectralZucconi6()` function, which - * takes a normalized value and returns a `ColorRGBa` using the - * accurate spectral colormap developed by Alan Zucconi. - * - * It draws a varying number of vertical bands (between 16 and 48) - * filled with various hues. - */ -fun main() = application { - configure { - width = 720 - height = 360 - } - program { - extend { - drawer.stroke = null - val stripeCount = 32 + (sin(seconds) * 16.0).fastFloor() - val bandWidth = width / stripeCount.toDouble() - repeat(stripeCount) { i -> - drawer.fill = spectralZucconi6(i / stripeCount.toDouble()) - drawer.rectangle( - x = i * bandWidth, - y = 0.0, - width = bandWidth, - height = height.toDouble(), - ) - } - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/colormap/DemoSpectralZucconiColormapPhrase.kt b/orx-color/src/jvmDemo/kotlin/colormap/DemoSpectralZucconiColormapPhrase.kt deleted file mode 100644 index d2b3fa54..00000000 --- a/orx-color/src/jvmDemo/kotlin/colormap/DemoSpectralZucconiColormapPhrase.kt +++ /dev/null @@ -1,33 +0,0 @@ -package colormap - -import org.openrndr.application -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.color.colormaps.ColormapPhraseBook -import org.openrndr.extra.shaderphrases.preprocess - -/** - * This program demonstrates how to use the shader-based version of - * the `spectral_zucconi6()` function, which - * takes a normalized value and returns an `rgb` color using the - * accurate spectral colormap developed by Alan Zucconi. - * - * It shades a full-window rectangle using its normalized `x` coordinate - * in a `ShadeStyle` to choose pixel colors. - */ -fun main() = application { - configure { - width = 720 - height = 360 - } - program { - ColormapPhraseBook.register() - val style = shadeStyle { - fragmentPreamble = "#pragma import colormap.spectral_zucconi6".preprocess() - fragmentTransform = "x_fill.rgb = spectral_zucconi6(c_boundsPosition.x);" - } - extend { - drawer.shadeStyle = style - drawer.rectangle(drawer.bounds) - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/colormap/DemoSpectralZucconiColormapPlot.kt b/orx-color/src/jvmDemo/kotlin/colormap/DemoSpectralZucconiColormapPlot.kt deleted file mode 100644 index 67f12e56..00000000 --- a/orx-color/src/jvmDemo/kotlin/colormap/DemoSpectralZucconiColormapPlot.kt +++ /dev/null @@ -1,61 +0,0 @@ -package colormap - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.color.colormaps.ColormapPhraseBook -import org.openrndr.extra.color.colormaps.spectralZucconi6 -import org.openrndr.extra.shaderphrases.preprocess -import org.openrndr.math.Vector2 - -/** - * This demo uses the shader based `spectral_zucconi6()` function to fill the background, - * then visualizes the red, green and blue components of the colors used in the background - * as red, green and blue line strips. - * - * The Vector2 points for the line strips are calculated only once when the program starts. - */ -fun main() = application { - configure { - width = 720 - height = 360 - } - program { - ColormapPhraseBook.register() - val backgroundStyle = shadeStyle { - fragmentPreamble = "#pragma import colormap.spectral_zucconi6".preprocess() - fragmentTransform = "x_fill.rgb = spectral_zucconi6(c_boundsPosition.x);" - } - - // Function that expects as an argument a function to convert a ColorRGBa into a Double, - // and returns a list of Vector2 coordinates. - fun getColormapPoints( - block: ColorRGBa.() -> Double - ) = List(width) { x -> - Vector2( - x.toDouble(), - (1.0 - block(spectralZucconi6(x / width.toDouble()))) * height - ) - } - - val redPoints = getColormapPoints { r } - val greenPoints = getColormapPoints { g } - val bluePoints = getColormapPoints { b } - extend { - drawer.run { - shadeStyle = backgroundStyle - rectangle(bounds) - shadeStyle = null - - stroke = ColorRGBa.RED - lineStrip(redPoints) - - stroke = ColorRGBa.GREEN - lineStrip(greenPoints) - - stroke = ColorRGBa.BLUE - lineStrip(bluePoints) - } - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/colormap/DemoTurboColormap.kt b/orx-color/src/jvmDemo/kotlin/colormap/DemoTurboColormap.kt deleted file mode 100644 index 074bbbb8..00000000 --- a/orx-color/src/jvmDemo/kotlin/colormap/DemoTurboColormap.kt +++ /dev/null @@ -1,37 +0,0 @@ -package colormap - -import org.openrndr.application -import org.openrndr.extra.color.colormaps.turboColormap -import org.openrndr.extra.noise.fastFloor -import kotlin.math.sin - -/** - * This program demonstrates the `turboColormap()` function, which - * takes a normalized value and returns a `ColorRGBa` using the - * Turbo colormap developed by Google. - * - * It draws a varying number of vertical bands (between 16 and 48) - * filled with various hues. - */ - -fun main() = application { - configure { - width = 720 - height = 360 - } - program { - extend { - drawer.stroke = null - val stripeCount = 32 + (sin(seconds) * 16.0).fastFloor() - repeat(stripeCount) { i -> - drawer.fill = turboColormap(i / stripeCount.toDouble()) - drawer.rectangle( - x = i * width / stripeCount.toDouble(), - y = 0.0, - width = width / stripeCount.toDouble(), - height = height.toDouble(), - ) - } - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/colormap/DemoTurboColormapPhrase.kt b/orx-color/src/jvmDemo/kotlin/colormap/DemoTurboColormapPhrase.kt deleted file mode 100644 index 57fffb21..00000000 --- a/orx-color/src/jvmDemo/kotlin/colormap/DemoTurboColormapPhrase.kt +++ /dev/null @@ -1,33 +0,0 @@ -package colormap - -import org.openrndr.application -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.color.colormaps.ColormapPhraseBook -import org.openrndr.extra.shaderphrases.preprocess - -/** - * This program demonstrates how to use the shader-based version of - * the `turbo_colormap()` function, which - * takes a normalized value and returns an `rgb` color using the - * Turbo colormap developed by Google. - * - * It shades a full-window rectangle using its normalized `x` coordinate - * in a `ShadeStyle` to choose pixel colors. - */ -fun main() = application { - configure { - width = 720 - height = 360 - } - program { - ColormapPhraseBook.register() - val style = shadeStyle { - fragmentPreamble = "#pragma import colormap.turbo_colormap".preprocess() - fragmentTransform = "x_fill.rgb = turbo_colormap(c_boundsPosition.x);" - } - extend { - drawer.shadeStyle = style - drawer.rectangle(drawer.bounds) - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/colormap/DemoTurboColormapPlot.kt b/orx-color/src/jvmDemo/kotlin/colormap/DemoTurboColormapPlot.kt deleted file mode 100644 index f0a45131..00000000 --- a/orx-color/src/jvmDemo/kotlin/colormap/DemoTurboColormapPlot.kt +++ /dev/null @@ -1,57 +0,0 @@ -package colormap - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.color.colormaps.ColormapPhraseBook -import org.openrndr.extra.color.colormaps.turboColormap -import org.openrndr.extra.shaderphrases.preprocess -import org.openrndr.math.Vector2 - -/** - * This demo uses the shader based `turbo_colormap()` function to fill the background, - * then visualizes the red, green and blue components of the colors used in the background - * as red, green and blue line strips. - * - * The Vector2 points for the line strips are calculated only once when the program starts. - */ -fun main() = application { - configure { - width = 720 - height = 360 - } - program { - ColormapPhraseBook.register() - val backgroundStyle = shadeStyle { - fragmentPreamble = "#pragma import colormap.turbo_colormap".preprocess() - fragmentTransform = "x_fill.rgb = turbo_colormap(c_boundsPosition.x);" - } - fun getColormapPoints( - block: ColorRGBa.() -> Double - ) = List(width) { x -> - Vector2( - x.toDouble(), - (1.0 - block(turboColormap(x / width.toDouble()))) * height - ) - } - val redPoints = getColormapPoints { r } - val greenPoints = getColormapPoints { g } - val bluePoints = getColormapPoints { b } - extend { - drawer.run { - shadeStyle = backgroundStyle - rectangle(bounds) - shadeStyle = null - - stroke = ColorRGBa.RED - lineStrip(redPoints) - - stroke = ColorRGBa.GREEN - lineStrip(greenPoints) - - stroke = ColorRGBa.BLUE - lineStrip(bluePoints) - } - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/colormatrix/DemoColorMatrix01.kt b/orx-color/src/jvmDemo/kotlin/colormatrix/DemoColorMatrix01.kt deleted file mode 100644 index b0ee8d4d..00000000 --- a/orx-color/src/jvmDemo/kotlin/colormatrix/DemoColorMatrix01.kt +++ /dev/null @@ -1,38 +0,0 @@ -package colormatrix - -import org.openrndr.application -import org.openrndr.draw.loadImage -import org.openrndr.extra.color.colormatrix.colorMatrix -import org.openrndr.extra.imageFit.imageFit -import org.openrndr.extra.shapes.primitives.grid - -/** - * This demo modifies the displayed image in each grid cell - * using color matrix transformations to demonstrate color channel inversions based on - * the grid cell's index. The image is adjusted to fit within each grid cell while maintaining - * alignment. - * - * Functionality: - * - Loads an image from the specified file path. - * - Splits the drawing area into an evenly spaced 4x2 grid. - * - Applies different color matrix inversions (red, green, blue) based on the position index. - * - Fits the image into each grid cell while providing horizontal alignment adjustments. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val image = loadImage("demo-data/images/image-001.png") - extend { - val cells = drawer.bounds.grid(4, 2).flatten() - for ((index, cell) in cells.withIndex()) { - drawer.drawStyle.colorMatrix = colorMatrix { - invert(index and 1 != 0, index and 2 != 0, index and 4 != 0) - } - drawer.imageFit(image, cell, horizontalPosition = -0.5) - } - } - } -} \ No newline at end of file diff --git a/orx-color/src/jvmDemo/kotlin/colormatrix/DemoColorMatrix02.kt b/orx-color/src/jvmDemo/kotlin/colormatrix/DemoColorMatrix02.kt deleted file mode 100644 index b1e31296..00000000 --- a/orx-color/src/jvmDemo/kotlin/colormatrix/DemoColorMatrix02.kt +++ /dev/null @@ -1,41 +0,0 @@ -package colormatrix - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.loadImage -import org.openrndr.extra.color.colormatrix.colorMatrix -import org.openrndr.extra.color.spaces.OKHSV -import org.openrndr.extra.color.tools.shiftHue -import org.openrndr.extra.imageFit.imageFit -import org.openrndr.extra.shapes.primitives.grid - -/** - * This demo modifies the displayed image in each grid cell - * using color matrix transformations to demonstrate color channel inversions based on - * the grid cell's index. The image is adjusted to fit within each grid cell while maintaining - * alignment. - * - * Functionality: - * - Loads an image from the specified file path. - * - Splits the drawing area into an evenly spaced 4x2 grid. - * - Applies different color matrix inversions (red, green, blue) based on the position index. - * - Fits the image into each grid cell while providing horizontal alignment adjustments. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val image = loadImage("demo-data/images/image-001.png") - extend { - val cells = drawer.bounds.grid(16, 1).flatten() - for ((index, cell) in cells.withIndex()) { - drawer.drawStyle.colorMatrix = colorMatrix { - tint(ColorRGBa.RED.shiftHue(index * 360 / 16.0)) - } - drawer.imageFit(image, cell, horizontalPosition = -1.0 + 2.0 * index / 15.0) - } - } - } -} \ No newline at end of file diff --git a/orx-color/src/jvmDemo/kotlin/colormatrix/DemoColorMatrix03.kt b/orx-color/src/jvmDemo/kotlin/colormatrix/DemoColorMatrix03.kt deleted file mode 100644 index d7bfde0f..00000000 --- a/orx-color/src/jvmDemo/kotlin/colormatrix/DemoColorMatrix03.kt +++ /dev/null @@ -1,44 +0,0 @@ -package colormatrix - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.loadImage -import org.openrndr.extra.color.colormatrix.colorMatrix -import org.openrndr.extra.color.spaces.OKHSV -import org.openrndr.extra.color.tools.shiftHue -import org.openrndr.extra.imageFit.imageFit -import org.openrndr.extra.shapes.primitives.grid - - -/** - * Entry point for an application demonstrating the use of color matrix transformations on an image. - * - * The program initializes a graphical application with a resolution of 720x720 pixels - * and processes an image to display it in a series of grid cells, applying a hue shift - * transformation based on the index of each cell. - * - * Key features: - * - Loads an image from a specified file path. - * - Configures the drawing area to consist of a horizontal grid with 16 cells. - * - Applies a color tint transformation utilizing the red channel, shifting its hue progressively - * per cell index to create a colorful gradient effect. - * - Adjusts the positions of the images within each grid cell for aesthetic alignment. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val image = loadImage("demo-data/images/image-001.png") - extend { - val cells = drawer.bounds.grid(16, 1).flatten() - for ((index, cell) in cells.withIndex()) { - drawer.drawStyle.colorMatrix = colorMatrix { - tint(ColorRGBa.RED.shiftHue(index * 360 / 16.0)) - } - drawer.imageFit(image, cell, horizontalPosition = -1.0 + 2.0 * index / 15.0) - } - } - } -} \ No newline at end of file diff --git a/orx-color/src/jvmDemo/kotlin/colormatrix/DemoColorMatrix04.kt b/orx-color/src/jvmDemo/kotlin/colormatrix/DemoColorMatrix04.kt deleted file mode 100644 index a5a12dd8..00000000 --- a/orx-color/src/jvmDemo/kotlin/colormatrix/DemoColorMatrix04.kt +++ /dev/null @@ -1,53 +0,0 @@ -package colormatrix - -import org.openrndr.application -import org.openrndr.draw.loadImage -import org.openrndr.extra.color.colormatrix.colorMatrix -import org.openrndr.extra.imageFit.imageFit -import org.openrndr.extra.shapes.primitives.grid - - -/** - * Entry point of a graphical application that demonstrates the use of color matrix - * transformations on an image displayed within a grid layout. - * - * Overview: - * - Initializes a window with a resolution of 720x720 pixels. - * - Loads an image from the specified file path. - * - Splits the drawing canvas into a 7x1 grid of cells. - * - In each grid cell, applies custom grayscale transformations to the image using - * a color matrix. The grayscale transformation coefficients for red, green, and blue - * channels are computed based on the index of the grid cell. - * - Displays the adjusted image in each grid cell with horizontal alignment modifications - * to position the images dynamically based on their index within the grid. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val image = loadImage("demo-data/images/image-001.png") - extend { - val cells = drawer.bounds.grid(7, 2) - for ((rowIndex, row) in cells.withIndex()) { - for ((index, cell) in row.withIndex()) { - drawer.drawStyle.colorMatrix = colorMatrix { - var r = if ((index + 1) and 1 != 0) 1.0 else 0.0 - var g = if ((index + 1) and 2 != 0) 1.0 else 0.0 - var b = if ((index + 1) and 4 != 0) 1.0 else 0.0 - val sum = r + g + b - r /= sum - g /= sum - b /= sum - grayscale(r, g, b) - if (rowIndex == 1) { - invert() - } - } - drawer.imageFit(image, cell, horizontalPosition = -0.5) - } - } - } - } -} \ No newline at end of file diff --git a/orx-color/src/jvmDemo/kotlin/histogram/DemoHistogram01.kt b/orx-color/src/jvmDemo/kotlin/histogram/DemoHistogram01.kt deleted file mode 100644 index 121fe573..00000000 --- a/orx-color/src/jvmDemo/kotlin/histogram/DemoHistogram01.kt +++ /dev/null @@ -1,59 +0,0 @@ -package histogram - -import org.openrndr.application -import org.openrndr.draw.loadImage -import org.openrndr.extra.color.statistics.calculateHistogramRGB - -/* - * Demonstrates how to generate a palette with the top 32 colors - * of a loaded image, sorted by luminosity. The colors are displayed - * as rectangles overlayed on top of the image. - */ -fun main() = application { - configure { - width = 720 - height = 540 - } - program { - val useColors = 32 - val image = loadImage("demo-data/images/image-001.png") - - val histogram = calculateHistogramRGB(image, binCount = 8) - print("Histogram using ${histogram.binCount} bins per RGB channel") - - val colorsSortedByFreq = histogram.sortedColors() - println(" therefore it contains ${colorsSortedByFreq.size} colors.") - - val topColors = colorsSortedByFreq.subList(0, useColors) - print("\nWe will use the most common $useColors") - - val topColorsSortedByLuminosity = topColors.sortedBy { - it.first.toHSLa().l - } - println(" and sort them by luminosity.") - - val topColorsFreqSum = topColors.sumOf { it.second } - println("\nThose top $useColors colors represent " + - String.format("%.02f", 100 * topColorsFreqSum) + - "% of the image colors.") - - extend { - drawer.image(image, 0.0, 0.0, width * 1.0, height * 1.0) - drawer.stroke = null - var x = 0.0 - topColorsSortedByLuminosity.forEachIndexed { i, (color, freq) -> - drawer.fill = color - - // draw horizontal bars - drawer.rectangle(x, 2.0, width.toDouble(), 16.0) - x += width * freq / topColorsFreqSum - - // draw vertical bars - drawer.rectangle(i * width / useColors.toDouble() + 2.0, - height - 2.0, - width / useColors.toDouble() - 4.0, - -height * freq / topColorsFreqSum) - } - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/histogram/DemoHistogram02.kt b/orx-color/src/jvmDemo/kotlin/histogram/DemoHistogram02.kt deleted file mode 100644 index 78d0bcf9..00000000 --- a/orx-color/src/jvmDemo/kotlin/histogram/DemoHistogram02.kt +++ /dev/null @@ -1,38 +0,0 @@ -package histogram - -import org.openrndr.application -import org.openrndr.draw.loadImage -import org.openrndr.extra.color.statistics.calculateHistogramRGB -import kotlin.math.pow - -/* - * Show the color histogram of an image using non-uniform weighting, - * prioritizing bright colors. - */ -fun main() = application { - configure { - width = 720 - height = 540 - } - program { - val image = loadImage("demo-data/images/image-001.png") - // -- here we use non-uniform weighting, such that bright colors are prioritized - val histogram = calculateHistogramRGB(image, weighting = { - ((r + g + b) / 3.0).pow(2.4) - }) - val colors = histogram.sortedColors() - // .subList(0, 32).sortedBy { it.first.toHSLa().h } // sort by hue - - extend { - drawer.image(image, 0.0, 0.0, width * 1.0, height * 1.0) - for (i in 0 until 32) { - drawer.fill = colors[i].first - drawer.stroke = null - drawer.rectangle(i * width / 32.0 + 2.0, - height - 2.0, - width / 32.0 - 4.0, - -16.0) - } - } - } -} diff --git a/orx-color/src/jvmDemo/kotlin/histogram/DemoHistogram03.kt b/orx-color/src/jvmDemo/kotlin/histogram/DemoHistogram03.kt deleted file mode 100644 index 33af6599..00000000 --- a/orx-color/src/jvmDemo/kotlin/histogram/DemoHistogram03.kt +++ /dev/null @@ -1,32 +0,0 @@ -package histogram - -import org.openrndr.application -import org.openrndr.draw.loadImage -import org.openrndr.extra.color.statistics.calculateHistogramRGB - -/* - * Create a simple grid-like composition based on colors sampled from image. - * The cells are 32 by 32 pixels in size and are filled with a random sample - * taken from the color histogram of the image. - * - * Note: due to its random nature the resulting animation contains flickering colors. - */ -fun main() = application { - configure { - width = 720 - height = 540 - } - program { - val image = loadImage("demo-data/images/image-001.png") - val histogram = calculateHistogramRGB(image) - extend { - for (j in 0 until height step 32) { - for (i in 0 until width step 32) { - drawer.stroke = null - drawer.fill = histogram.sample() - drawer.rectangle(i * 1.0, j * 1.0, 32.0, 32.0) - } - } - } - } -} diff --git a/orx-color/src/jvmMain/kotlin/src/statistics/Histogram.kt b/orx-color/src/jvmMain/kotlin/src/statistics/Histogram.kt deleted file mode 100644 index 014552bd..00000000 --- a/orx-color/src/jvmMain/kotlin/src/statistics/Histogram.kt +++ /dev/null @@ -1,79 +0,0 @@ -package org.openrndr.extra.color.statistics - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorBuffer -import kotlin.random.Random - -fun calculateHistogramRGB(buffer: ColorBuffer, - binCount: Int = 16, - weighting: ColorRGBa.() -> Double = { 1.0 }, - downloadShadow: Boolean = true): RGBHistogram { - val bins = Array(binCount) { Array(binCount) { DoubleArray(binCount) } } - if (downloadShadow) { - buffer.shadow.download() - } - - var totalWeight = 0.0 - val s = buffer.shadow - for (y in 0 until buffer.height) { - for (x in 0 until buffer.width) { - val c = s[x, y] - val weight = c.weighting() - val (rb, gb, bb) = c.binIndex(binCount) - bins[rb][gb][bb] += weight - totalWeight += weight - } - } - - if (totalWeight > 0) - for (r in 0 until binCount) { - for (g in 0 until binCount) { - for (b in 0 until binCount) { - bins[r][g][b] /= totalWeight - } - } - } - return RGBHistogram(bins, binCount) -} - - -class RGBHistogram(val freqs: Array>, val binCount: Int) { - fun frequency(color: ColorRGBa): Double { - val (rb, gb, bb) = color.binIndex(binCount) - return freqs[rb][gb][bb] - } - - fun color(rBin: Int, gBin: Int, bBin: Int): ColorRGBa = - ColorRGBa(rBin / (binCount - 1.0), gBin / (binCount - 1.0), bBin / (binCount - 1.0)) - - fun sample(random: Random = Random.Default): ColorRGBa { - val x = random.nextDouble() - var sum = 0.0 - for (r in 0 until binCount) { - for (g in 0 until binCount) { - for (b in 0 until binCount) { - sum += freqs[r][g][b] - if (sum >= x) { - return color(r, g, b) - } - } - } - } - return color(binCount - 1, binCount - 1, binCount - 1) - } - - fun sortedColors(): List> { - val result = mutableListOf>() - for (r in 0 until binCount) { - for (g in 0 until binCount) { - for (b in 0 until binCount) { - result += Pair( - ColorRGBa(r / (binCount - 1.0), g / (binCount - 1.0), b / (binCount - 1.0)), - freqs[r][g][b] - ) - } - } - } - return result.sortedByDescending { it.second } - } -} \ No newline at end of file diff --git a/orx-color/src/jvmTest/kotlin/TestMix.kt b/orx-color/src/jvmTest/kotlin/TestMix.kt deleted file mode 100644 index 97daf257..00000000 --- a/orx-color/src/jvmTest/kotlin/TestMix.kt +++ /dev/null @@ -1,44 +0,0 @@ -import io.kotest.core.spec.style.DescribeSpec -import org.openrndr.color.ColorRGBa -import org.openrndr.color.Linearity -import org.openrndr.extra.color.palettes.rangeTo - -import kotlin.test.assertEquals -import kotlin.test.assertSame - -class TestMix : DescribeSpec({ - - describe("two srgb colors") { - val a = ColorRGBa.BLUE - val b = ColorRGBa.RED - - assertEquals(Linearity.SRGB, a.linearity) - - - it("should mix properly") { - assertSame(a, a.mix(b, 0.0)) - assertSame(b, a.mix(b, 1.0)) - } - } - - describe("two linear rgb colors") { - val a = ColorRGBa.BLUE.toLinear() - val b = ColorRGBa.RED.toLinear() - - it("should mix properly") { - assertSame(a, a.mix(b, 0.0)) - assertSame(b, a.mix(b, 1.0)) - } - } - - describe("a 2-step range of colors") { - val a = ColorRGBa.BLUE - val b = ColorRGBa.RED - - val blend = a..b blend 2 - assertEquals(2, blend.size) - assertEquals(a, blend[0]) - assertEquals(b, blend[1]) - } - -}) \ No newline at end of file diff --git a/orx-composition/README.md b/orx-composition/README.md deleted file mode 100644 index d42e2186..00000000 --- a/orx-composition/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# orx-composition - -Shape composition library - -One can think of a Composition as a vector design made out of primitives -like ShapeContour, Shape, or LineSegment, each having its fill color, -stroke color and stroke weight. - -Compositions can be loaded from SVG files and then queried or altered via code. - -Composition can also be generated from scratch, typically using `drawComposition { ... }`, then saved as an SVG file. - -Read about Composition [in the guide](https://guide.openrndr.org/drawing/drawingSVG.html). - -_The code in `orx-composition` was previously found under `openrndr-draw` in the `openrndr` repository._ - - -## Demos -### DemoCompositionDrawer01 - -Demonstrates how to - -- Create a Composition -- Draw it on the program window -- Save it to an SVG file -- Print the SVG content as text - -![DemoCompositionDrawer01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-composition/images/DemoCompositionDrawer01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCompositionDrawer01.kt) - -### DemoCompositionDrawer02 - -Demonstrates how to draw a Composition and how to use -`ClipMode.REVERSE_DIFFERENCE` to clip shapes. - -The first shape clips part of the second one away, -producing a shape that seems to be behind the first one. - -Without clipping, the second circle would cover part of the first one. - -![DemoCompositionDrawer02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-composition/images/DemoCompositionDrawer02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCompositionDrawer02.kt) - -### DemoCompositionDrawer03 - -Draws a composition using 3 circles and `ClipMode.REVERSE_DIFFERENCE`. - -A println() demonstrates that the result contains 3 shapes: -a complete circle, a moon-like shape, and a shape with two small black areas. - -One way to verify this is by saving the design as an SVG file and opening -it in vector editing software. - - -![DemoCompositionDrawer03Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-composition/images/DemoCompositionDrawer03Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCompositionDrawer03.kt) - -### DemoCompositionDrawer04 - -Demonstrates how to add content to and how to clear an existing Composition. - -A number of circles are added when the program starts. -Dragging the mouse button adds more circles. -Right-clicking the mouse clears the Composition. - -![DemoCompositionDrawer04Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-composition/images/DemoCompositionDrawer04Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCompositionDrawer04.kt) - -### DemoCompositionDrawer05 - -Demonstrates how to - -- Create a Composition with a group -- Add XML attributes so the group appears as a layer in Inkscape - -![DemoCompositionDrawer05Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-composition/images/DemoCompositionDrawer05Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCompositionDrawer05.kt) diff --git a/orx-composition/build.gradle.kts b/orx-composition/build.gradle.kts deleted file mode 100644 index af4f8055..00000000 --- a/orx-composition/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") - alias(libs.plugins.kotlin.serialization) -} - -kotlin { - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - implementation(sharedLibs.kotlin.serialization.core) - } - } - - @Suppress("UNUSED_VARIABLE") - val jvmDemo by getting { - dependencies { - implementation(project(":orx-svg")) - } - } - } -} diff --git a/orx-composition/src/commonMain/kotlin/Composition.kt b/orx-composition/src/commonMain/kotlin/Composition.kt deleted file mode 100644 index a02c6a4e..00000000 --- a/orx-composition/src/commonMain/kotlin/Composition.kt +++ /dev/null @@ -1,641 +0,0 @@ -package org.openrndr.extra.composition - -import org.openrndr.draw.* -import org.openrndr.math.* -import org.openrndr.math.transforms.* -import org.openrndr.shape.Rectangle -import org.openrndr.shape.Shape -import org.openrndr.shape.ShapeContour -import org.openrndr.shape.bounds -import kotlin.math.* -import kotlin.reflect.* - -/** - * Describes a node in a composition - */ -sealed class CompositionNode { - - var id: String? = null - - var parent: CompositionNode? = null - - /** This CompositionNode's own style. */ - var style: Style = Style() - - /** - * This CompositionNode's computed style. - * Where every style attribute is obtained by - * overwriting the Style in the following order: - * 1. Default style attributes. - * 2. Parent Node's computed style's inheritable attributes. - * 3. This Node's own style attributes. - */ - val effectiveStyle: Style - get() = when (val p = parent) { - is CompositionNode -> style inherit p.effectiveStyle - else -> style - } - - /** - * Custom attributes to be applied to the Node in addition to the Style attributes. - */ - var attributes = mutableMapOf() - - /** - * a map that stores user data - */ - val userData = mutableMapOf() - - /** - * a [Rectangle] that describes the bounding box of the contents - */ - abstract val bounds: Rectangle - - val effectiveStroke get() = effectiveStyle.stroke.value - val effectiveStrokeOpacity get() = effectiveStyle.strokeOpacity.value - val effectiveStrokeWeight get() = effectiveStyle.strokeWeight.value - val effectiveMiterLimit get() = effectiveStyle.miterLimit.value - val effectiveLineCap get() = effectiveStyle.lineCap.value - val effectiveLineJoin get() = effectiveStyle.lineJoin.value - val effectiveFill get() = effectiveStyle.fill.value - val effectiveFillOpacity get() = effectiveStyle.fillOpacity.value - val effectiveDisplay get() = effectiveStyle.display.value - val effectiveOpacity get() = effectiveStyle.opacity.value - val effectiveVisibility get() = effectiveStyle.visibility.value - val effectiveShadeStyle get() = effectiveStyle.shadeStyle.value - - /** Calculates the absolute transformation of the current node. */ - val effectiveTransform: Matrix44 - get() = when (val p = parent) { - is CompositionNode -> transform * p.effectiveTransform - else -> transform - } - - var stroke - get() = style.stroke.value - set(value) { - style.stroke = when (value) { - null -> Paint.None - else -> Paint.RGB(value) - } - } - var strokeOpacity - get() = style.strokeOpacity.value - set(value) { - style.strokeOpacity = Numeric.Rational(value) - } - var strokeWeight - get() = style.strokeWeight.value - set(value) { - style.strokeWeight = Length.Pixels(value) - } - var miterLimit - get() = style.miterLimit.value - set(value) { - style.miterLimit = Numeric.Rational(value) - } - var lineCap - get() = style.lineCap.value - set(value) { - style.lineCap = when (value) { - org.openrndr.draw.LineCap.BUTT -> LineCap.Butt - org.openrndr.draw.LineCap.ROUND -> LineCap.Round - org.openrndr.draw.LineCap.SQUARE -> LineCap.Square - } - } - var lineJoin - get() = style.lineJoin.value - set(value) { - style.lineJoin = when (value) { - org.openrndr.draw.LineJoin.BEVEL -> LineJoin.Bevel - org.openrndr.draw.LineJoin.MITER -> LineJoin.Miter - org.openrndr.draw.LineJoin.ROUND -> LineJoin.Round - } - } - var fill - get() = style.fill.value - set(value) { - style.fill = when (value) { - null -> Paint.None - else -> Paint.RGB(value) - } - } - var fillOpacity - get() = style.fillOpacity.value - set(value) { - style.fillOpacity = Numeric.Rational(value) - } - var opacity - get() = style.opacity.value - set(value) { - style.opacity = Numeric.Rational(value) - } - var shadeStyle - get() = style.shadeStyle.value - set(value) { - style.shadeStyle = Shade.Value(value) - } - var transform - get() = style.transform.value - set(value) { - style.transform = Transform.Matrix(value) - } -} - -// TODO: Deprecate this? -operator fun KMutableProperty0.setValue(thisRef: Style, property: KProperty<*>, value: ShadeStyle) { - this.set(Shade.Value(value)) -} - -fun transform(node: CompositionNode): Matrix44 = - (node.parent?.let { transform(it) } ?: Matrix44.IDENTITY) * node.transform - -/** - * a [CompositionNode] that holds a single image [ColorBuffer] - */ -class ImageNode(var image: ColorBuffer, var x: Double, var y: Double, var width: Double, var height: Double) : - CompositionNode() { - override val bounds: Rectangle - get() = Rectangle(0.0, 0.0, width, height).contour.transform(transform(this)).bounds -} - -/** - * a [CompositionNode] that holds a single [Shape] - */ -class ShapeNode(var shape: Shape) : CompositionNode() { - override val bounds: Rectangle - get() { - val t = effectiveTransform - return if (t === Matrix44.IDENTITY) { - shape.bounds - } else { - shape.bounds.contour.transform(t).bounds - } - } - - /** - * apply transforms of all ancestor nodes and return a new detached org.openrndr.shape.ShapeNode with conflated transform - */ - fun conflate(): ShapeNode { - return ShapeNode(shape).also { - it.id = id - it.parent = parent - it.style = style - it.transform = transform(this) - it.attributes = attributes - } - } - - - /** - * apply transforms of all ancestor nodes and return a new detached shape node with identity transform and transformed Shape - * @param composition use viewport transform - */ - fun flatten(composition: Composition? = null): ShapeNode { - - val viewport = composition?.calculateViewportTransform() ?: Matrix44.IDENTITY - - return ShapeNode(shape.transform(viewport * transform(this))).also { - it.id = id - it.parent = parent - it.style = effectiveStyle - it.attributes = attributes - } - } - - fun copy( - id: String? = this.id, - parent: CompositionNode? = null, - style: Style = this.style, - attributes: MutableMap = this.attributes, - shape: Shape = this.shape - ): ShapeNode { - return ShapeNode(shape).also { - it.id = id - it.parent = parent - it.style = style - it.attributes = attributes - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is ShapeNode) return false - if (shape != other.shape) return false - return true - } - - override fun hashCode(): Int { - return shape.hashCode() - } - - /** - * the local [Shape] with the [effectiveTransform] applied to it - */ - val effectiveShape - get() = shape.transform(effectiveTransform) -} - -/** - * a [CompositionNode] that holds a single text - */ -data class TextNode(var text: String, var contour: ShapeContour?) : CompositionNode() { - // TODO: This should not be Rectangle.EMPTY - override val bounds: Rectangle - get() = Rectangle.EMPTY -} - - -/** - * Represents a group node in a composition hierarchy. - * A `GroupNode` itself does not have explicit contents but serves as a container for managing child nodes. - * It allows grouping of multiple `CompositionNode` instances and provides functionalities like calculating - * the bounds for all its child elements and copying itself with overrides. - * - * @property children A mutable list of child nodes belonging to this group. Defaults to an empty list. - */ -open class GroupNode(open val children: MutableList = mutableListOf()) : CompositionNode() { - override val bounds: Rectangle - get() { - return children.map { it.bounds }.bounds - } - - fun copy( - id: String? = this.id, - parent: CompositionNode? = null, - style: Style = this.style, - children: MutableList = this.children - ): GroupNode { - return GroupNode(children).also { - it.id = id - it.parent = parent - it.style = style - it.attributes = attributes - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is GroupNode) return false - - if (children != other.children) return false - return true - } - - override fun hashCode(): Int { - return children.hashCode() - } -} - -data class CompositionDimensions(val x: Length, val y: Length, val width: Length, val height: Length) { - val position = Vector2((x as Length.Pixels).value, (y as Length.Pixels).value) - val dimensions = Vector2((width as Length.Pixels).value, (height as Length.Pixels).value) - - constructor(rectangle: Rectangle) : this( - rectangle.corner.x.pixels, - rectangle.corner.y.pixels, - rectangle.dimensions.x.pixels, - rectangle.dimensions.y.pixels - ) - - override fun toString(): String = "$x $y $width $height" - - // I'm not entirely sure why this is needed but - // but otherwise equality checks will never succeed - override fun equals(other: Any?): Boolean { - return other is CompositionDimensions - && x.value == other.x.value - && y.value == other.y.value - && width.value == other.width.value - && height.value == other.height.value - } - - override fun hashCode(): Int { - var result = x.hashCode() - result = 31 * result + y.hashCode() - result = 31 * result + width.hashCode() - result = 31 * result + height.hashCode() - return result - } -} - -val defaultCompositionDimensions = CompositionDimensions(0.0.pixels, 0.0.pixels, 768.0.pixels, 576.0.pixels) - - -/** - * Represents a specialized type of `GroupNode` in a composition hierarchy, serving as a container for child nodes. - * - * `GroupNodeStop` inherits from `GroupNode` and extends its functionality. It can be used to define a specific - * grouping behavior or semantic grouping in a composition system. Instances of this class hold a mutable list - * of `CompositionNode` entities as children. - * - * @constructor Creates a `GroupNodeStop` with the given child nodes. - * @param children A mutable list of `CompositionNode` instances to be managed by this group. - */ -class GroupNodeStop(children: MutableList) : GroupNode(children) - -/** - * A vector composition. - * @param root the root node of the composition - * @param bounds the dimensions of the composition - */ -class Composition(val root: CompositionNode, var bounds: CompositionDimensions = defaultCompositionDimensions) { - constructor(root: CompositionNode, bounds: Rectangle) : this(root, CompositionDimensions(bounds)) - - /** SVG/XML namespaces */ - val namespaces = mutableMapOf() - - var style: Style = Style() - - /** - * The style attributes affecting the whole document, such as the viewBox area and aspect ratio. - */ - var documentStyle: DocumentStyle = DocumentStyle() - - init { - val (x, y, width, height) = bounds - style.x = x - style.y = y - style.width = width - style.height = height - } - - fun findShapes() = root.findShapes() - fun findShape(id: String): ShapeNode? { - return (root.find { it is ShapeNode && it.id == id }) as? ShapeNode - } - - fun findImages() = root.findImages() - fun findImage(id: String): ImageNode? { - return (root.find { it is ImageNode && it.id == id }) as? ImageNode - } - - fun findGroups(): List = root.findGroups() - fun findGroup(id: String): GroupNode? { - return (root.find { it is GroupNode && it.id == id }) as? GroupNode - } - - fun clear() = (root as? GroupNode)?.children?.clear() - - /** Calculates the equivalent of `1%` in pixels. */ - internal fun normalizedDiagonalLength(): Double = sqrt(bounds.dimensions.squaredLength / 2.0) - - /** - * Calculates effective viewport transformation using [viewBox] and [preserveAspectRatio]. - * As per [the SVG 2.0 spec](https://svgwg.org/svg2-draft/single-page.html#coords-ComputingAViewportsTransform). - */ - fun calculateViewportTransform(): Matrix44 { - return when (documentStyle.viewBox) { - ViewBox.None -> Matrix44.IDENTITY - is ViewBox.Value -> { - when (val vb = (documentStyle.viewBox as ViewBox.Value).value) { - Rectangle.EMPTY -> { - // The intent is to not display the element - Matrix44.ZERO - } - - else -> { - val vbCorner = vb.corner - val vbDims = vb.dimensions - val eCorner = bounds.position - val eDims = bounds.dimensions - val (align, meetOrSlice) = documentStyle.preserveAspectRatio - - val scale = (eDims / vbDims).let { - if (align != Align.NONE) { - if (meetOrSlice == MeetOrSlice.MEET) { - Vector2(min(it.x, it.y)) - } else { - Vector2(max(it.x, it.y)) - } - } else { - it - } - } - - val translate = (eCorner - (vbCorner * scale)).let { - val cx = eDims.x - vbDims.x * scale.x - val cy = eDims.y - vbDims.y * scale.y - it + when (align) { - // TODO: This first one probably doesn't comply with the spec - Align.NONE -> Vector2.ZERO - Align.X_MIN_Y_MIN -> Vector2.ZERO - Align.X_MID_Y_MIN -> Vector2(cx / 2, 0.0) - Align.X_MAX_Y_MIN -> Vector2(cx, 0.0) - Align.X_MIN_Y_MID -> Vector2(0.0, cy / 2) - Align.X_MID_Y_MID -> Vector2(cx / 2, cy / 2) - Align.X_MAX_Y_MID -> Vector2(cx, cy / 2) - Align.X_MIN_Y_MAX -> Vector2(0.0, cy) - Align.X_MID_Y_MAX -> Vector2(cx / 2, cy) - Align.X_MAX_Y_MAX -> Vector2(cx, cy) - } - } - - buildTransform { - translate(translate) - scale(scale.x, scale.y, 1.0) - } - } - } - } - } - } -} - -/** - * remove node from its parent [CompositionNode] - */ -fun CompositionNode.remove() { - require(parent != null) { "parent is null" } - val parentGroup = (parent as? GroupNode) - if (parentGroup != null) { - val filtered = parentGroup.children.filter { - it != this - } - parentGroup.children.clear() - parentGroup.children.addAll(filtered) - } - parent = null -} - -/** - * Recursively finds all terminal nodes within the composition tree starting from the current node - * and applies the provided filter to determine which nodes to include in the result. - * - * @param filter A predicate function used to filter terminal nodes. Only nodes that satisfy this - * predicate will be included in the result. - * @return A list of terminal nodes within the composition tree that satisfy the given filter. - */ -fun CompositionNode.findTerminals(filter: (CompositionNode) -> Boolean): List { - val result = mutableListOf() - fun find(node: CompositionNode) { - when (node) { - is GroupNode -> node.children.forEach { find(it) } - else -> if (filter(node)) { - result.add(node) - } - } - } - find(this) - return result -} - -/** - * Finds all `CompositionNode` instances in the current node hierarchy that satisfy the given filter. - * Traverses the hierarchy recursively, evaluating each node and its children. - * - * @param filter A predicate function to determine whether a node should be included in the result. - * It takes a `CompositionNode` as input and returns a Boolean. - * @return A list of `CompositionNode` instances that satisfy the provided filter condition. - */ -fun CompositionNode.findAll(filter: (CompositionNode) -> Boolean): List { - val result = mutableListOf() - fun find(node: CompositionNode) { - if (filter(node)) { - result.add(node) - } - if (node is GroupNode) { - node.children.forEach { find(it) } - } - } - find(this) - return result -} - -/** - * Finds first [CompositionNode] to match the given [predicate]. - */ -fun CompositionNode.find(predicate: (CompositionNode) -> Boolean): CompositionNode? { - if (predicate(this)) { - return this - } else if (this is GroupNode) { - val deque: ArrayDeque = ArrayDeque(children) - while (deque.isNotEmpty()) { - val node = deque.removeFirst() - if (predicate(node)) { - return node - } else if (node is GroupNode) { - deque.addAll(node.children) - } - } - } - return null -} - -/** - * find all descendant [ShapeNode] nodes, including potentially this node - * @return a [List] of [ShapeNode] nodes - */ -fun CompositionNode.findShapes(): List = findTerminals { it is ShapeNode }.map { it as ShapeNode } - -/** - * find all descendant [ImageNode] nodes, including potentially this node - * @return a [List] of [ImageNode] nodes - */ -fun CompositionNode.findImages(): List = findTerminals { it is ImageNode }.map { it as ImageNode } - -/** - * find all descendant [GroupNode] nodes, including potentially this node - * @return a [List] of [GroupNode] nodes - */ -fun CompositionNode.findGroups(): List = findAll { it is GroupNode }.map { it as GroupNode } - -/** - * visit this [CompositionNode] and all descendant nodes and execute [visitor] - */ -fun CompositionNode.visitAll(visitor: (CompositionNode.() -> Unit)) { - visitor() - if (this is GroupNode) { - for (child in children) { - child.visitAll(visitor) - } - } -} - -/** - * org.openrndr.shape.UserData delegate - */ -class UserData( - val name: String, val initial: T -) { - @Suppress("UNCHECKED_CAST") - operator fun getValue(node: CompositionNode, property: KProperty<*>): T { - val value: T? = node.userData[name] as? T - return value ?: initial - } - - operator fun setValue(stylesheet: CompositionNode, property: KProperty<*>, value: T) { - stylesheet.userData[name] = value - } -} - -/** - * Filters a `CompositionNode` and its hierarchy based on the provided filter function. - * The method recursively applies the filter to the node and its children, creating - * a new hierarchy that contains only the nodes for which the filter returns true. - * If the filter condition fails for the root node, null is returned. - * - * For `GroupNode` instances, the method applies the filter to its children and - * creates a new `GroupNode` containing filtered children that satisfy the filter condition. - * For `ShapeNode` instances, a copy is created if the filter condition is met. - * - * @param filter A lambda function that takes a `CompositionNode` and returns a `Boolean`. - * The function determines if a node should be included in the resulting hierarchy. - * - * @return A new filtered `CompositionNode` tree, or null if the root node does not pass the filter. - */ -fun CompositionNode.filter(filter: (CompositionNode) -> Boolean): CompositionNode? { - val f = filter(this) - - if (!f) { - return null - } - - if (this is GroupNode) { - val copies = mutableListOf() - children.forEach { - val filtered = it.filter(filter) - if (filtered != null) { - when (filtered) { - is ShapeNode -> { - copies.add(filtered.copy(parent = this)) - } - - is GroupNode -> { - copies.add(filtered.copy(parent = this)) - } - - else -> { - - } - } - } - } - return GroupNode(children = copies) - } else { - return this - } -} - -fun CompositionNode.map(mapper: (CompositionNode) -> CompositionNode): CompositionNode { - val r = mapper(this) - return when (r) { - is GroupNodeStop -> { - r.copy().also { copy -> - copy.children.forEach { - it.parent = copy - } - } - } - - is GroupNode -> { - val copy = r.copy(children = r.children.map { it.map(mapper) }.toMutableList()) - copy.children.forEach { - it.parent = copy - } - copy - } - - else -> r - } -} \ No newline at end of file diff --git a/orx-composition/src/commonMain/kotlin/CompositionDrawer.kt b/orx-composition/src/commonMain/kotlin/CompositionDrawer.kt deleted file mode 100644 index 64bb8583..00000000 --- a/orx-composition/src/commonMain/kotlin/CompositionDrawer.kt +++ /dev/null @@ -1,821 +0,0 @@ -package org.openrndr.extra.composition - -import org.openrndr.collections.pflatMap -import org.openrndr.collections.pmap -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.LineCap -import org.openrndr.draw.LineJoin -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.YPolarity -import org.openrndr.math.transforms.* -import org.openrndr.shape.* -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract -import kotlin.jvm.JvmRecord - -/** - * Used internally to define [ClipMode]s. - */ -enum class ClipOp { - DISABLED, - DIFFERENCE, - REVERSE_DIFFERENCE, - INTERSECT, - UNION -} - -/** - * Specifies if transformations should be kept separate - * or applied to the clipped object and reset to identity. - */ -enum class TransformMode { - KEEP, - APPLY -} - -/** - * Specifies in which way to combine [Shape]s - * to form a [Composition] - */ -enum class ClipMode(val grouped: Boolean, val op: ClipOp) { - DISABLED(false, ClipOp.DISABLED), - DIFFERENCE(false, ClipOp.DIFFERENCE), - DIFFERENCE_GROUP(true, ClipOp.DIFFERENCE), - REVERSE_DIFFERENCE(false, ClipOp.REVERSE_DIFFERENCE), - REVERSE_DIFFERENCE_GROUP(true, ClipOp.REVERSE_DIFFERENCE), - INTERSECT(false, ClipOp.INTERSECT), - INTERSECT_GROUP(true, ClipOp.INTERSECT), - UNION(false, ClipOp.UNION), - UNION_GROUP(true, ClipOp.UNION) -} - -/** - * The set of draw style properties used for rendering a [Composition] - */ -private data class CompositionDrawStyle( - var fill: ColorRGBa? = null, - var fillOpacity: Double = 1.0, - var stroke: ColorRGBa? = ColorRGBa.BLACK, - var strokeOpacity: Double = 1.0, - var strokeWeight: Double = 1.0, - var opacity: Double = 1.0, - var clipMode: ClipMode = ClipMode.DISABLED, - var mask: Shape? = null, - var transformMode: TransformMode = TransformMode.APPLY, - var lineCap: LineCap = LineCap.BUTT, - var lineJoin: LineJoin = LineJoin.MITER, - var miterlimit: Double = 4.0, - var visibility: Visibility = Visibility.Visible -) - -/** - * Data structure containing intersection information. - */ -@JvmRecord -data class ShapeNodeIntersection(val node: ShapeNode, val intersection: ContourIntersection) - -/** - * Data structure containing information about a point - * in a [ShapeContour] closest to some other 2D point. - */ -@JvmRecord -data class ShapeNodeNearestContour( - val node: ShapeNode, - val point: ContourPoint, - val distanceDirection: Vector2, - val distance: Double -) - -/** - * Merges two lists of [ShapeNodeIntersection] removing duplicates under the - * given [threshold]. Used internally by [intersections]. - */ -fun List.merge(threshold: Double = 0.5): List { - val result = mutableListOf() - for (i in this) { - val nearest = result.minByOrNull { it.intersection.position.squaredDistanceTo(i.intersection.position) } - if (nearest == null) { - result.add(i) - } else if (nearest.intersection.position.squaredDistanceTo(i.intersection.position) >= threshold * threshold) { - result.add(i) - } - } - return result -} - - -/** - * A Drawer-like interface for the creation of Compositions - * This should be easier than creating Compositions manually - */ -class CompositionDrawer( - documentBounds: CompositionDimensions = defaultCompositionDimensions, - composition: Composition? = null, - cursor: GroupNode? = composition?.root as? GroupNode -) { - val root = (composition?.root as? GroupNode) ?: GroupNode() - val composition = composition ?: Composition(root, documentBounds) - - var cursor = cursor ?: root - private set - - private val modelStack = ArrayDeque() - private val styleStack = ArrayDeque().apply { } - private var drawStyle = CompositionDrawStyle() - - var model = Matrix44.IDENTITY - - var fill - get() = drawStyle.fill?.opacify(drawStyle.fillOpacity)?.opacify(drawStyle.opacity) - set(value) = run { - drawStyle.fill = value?.copy(alpha = 1.0) - drawStyle.fillOpacity = value?.alpha ?: 1.0 - } - - var fillOpacity - get() = drawStyle.fillOpacity - set(value) = run { drawStyle.fillOpacity = value } - - var stroke - get() = drawStyle.stroke?.opacify(drawStyle.strokeOpacity)?.opacify(drawStyle.opacity) - set(value) = run { - drawStyle.stroke = value?.copy(alpha = 1.0) - drawStyle.strokeOpacity = value?.alpha ?: 1.0 - } - - var strokeOpacity - get() = drawStyle.strokeOpacity - set(value) = run { drawStyle.strokeOpacity = value } - - var strokeWeight - get() = drawStyle.strokeWeight - set(value) = run { drawStyle.strokeWeight = value } - - var miterlimit - get() = drawStyle.miterlimit - set(value) = run { drawStyle.miterlimit = value } - - var lineCap - get() = drawStyle.lineCap - set(value) = run { drawStyle.lineCap = value } - - var lineJoin - get() = drawStyle.lineJoin - set(value) = run { drawStyle.lineJoin = value } - - var opacity - get() = drawStyle.opacity - set(value) = run { drawStyle.opacity = value } - - var visibility - get() = drawStyle.visibility - set(value) = run { drawStyle.visibility = value } - - var clipMode - get() = drawStyle.clipMode - set(value) = run { drawStyle.clipMode = value } - - var mask: Shape? - get() = drawStyle.mask - set(value) = run { drawStyle.mask = value } - - var transformMode - get() = drawStyle.transformMode - set(value) = run { drawStyle.transformMode = value } - - fun pushModel() { - modelStack.addLast(model) - } - - fun popModel() { - model = modelStack.removeLast() - } - - fun pushStyle() { - styleStack.addLast(drawStyle.copy()) - } - - fun popStyle() { - drawStyle = styleStack.removeLast() - } - - @OptIn(ExperimentalContracts::class) - fun isolated(draw: CompositionDrawer.() -> Unit) { - contract { - callsInPlace(draw, InvocationKind.EXACTLY_ONCE) - } - pushModel() - pushStyle() - draw() - popModel() - popStyle() - } - - @OptIn(ExperimentalContracts::class) - fun GroupNode.with(builder: CompositionDrawer.() -> Unit): GroupNode { - contract { - callsInPlace(builder, InvocationKind.EXACTLY_ONCE) - } - val oldCursor = cursor - cursor = this - builder() - cursor = oldCursor - return this - } - - /** - * Create a group node and run `builder` inside its context - * @param insert if true the created group will be inserted at [cursor] - * @param id an optional identifier - * @param builder the function that is executed inside the group context - */ - @OptIn(ExperimentalContracts::class) - fun group(insert: Boolean = true, id: String? = null, builder: CompositionDrawer.() -> Unit): GroupNode { - contract { - callsInPlace(builder, InvocationKind.EXACTLY_ONCE) - } - - val group = GroupNode() - group.id = id - val oldCursor = cursor - - if (insert) { - cursor.children.add(group) - group.parent = cursor - } - cursor = group - builder() - - cursor = oldCursor - return group - } - - fun translate(x: Double, y: Double) = translate(Vector2(x, y)) - - fun rotate(rotationInDegrees: Double) { - model *= Matrix44.rotateZ(rotationInDegrees) - } - - fun scale(s: Double) { - model *= Matrix44.scale(s, s, s) - } - - fun scale(x: Double, y: Double) { - model *= Matrix44.scale(x, y, 1.0) - } - - fun translate(t: Vector2) { - model *= Matrix44.translate(t.vector3()) - } - - fun contour(contour: ShapeContour, insert: Boolean = true): ShapeNode? { - if (contour.empty) { - return null - } - val shape = Shape(listOf(contour)) - return shape(shape, insert) - } - - fun contours(contours: List, insert: Boolean = true) = contours.map { contour(it, insert) } - - /** - * Search for a point on a contour in the composition tree that's nearest to `point` - * @param point the query point - * @param searchFrom a node from which the search starts, defaults to composition root - * @return an optional org.openrndr.shape.ShapeNodeNearestContour instance - */ - fun nearest( - point: Vector2, - searchFrom: CompositionNode = composition.root as GroupNode - ): ShapeNodeNearestContour? { - return distances(point, searchFrom).firstOrNull() - } - - fun CompositionNode.nearest(point: Vector2) = nearest(point, searchFrom = this) - - fun difference( - shape: Shape, - searchFrom: CompositionNode = composition.root as GroupNode - ): Shape { - val shapes = searchFrom.findShapes() - var from = shape - - for (subtract in shapes) { - if (shape.bounds.intersects(subtract.shape.bounds)) { - from = difference(from, subtract.shape) - } - } - return from - } - - /** - * Find distances to each contour in the composition tree (or starting node) - * @param point the query point - * @param searchFrom a node from which the search starts, defaults to composition root - * @return a sorted list of [ShapeNodeNearestContour] describing distance to every contour - */ - fun distances( - point: Vector2, - searchFrom: CompositionNode = composition.root as GroupNode - ): List { - return searchFrom.findShapes().flatMap { node -> - node.shape.contours.filter { !it.empty } - .map { it.nearest(point) } - .map { ShapeNodeNearestContour(node, it, point - it.position, it.position.distanceTo(point)) } - }.sortedBy { it.distance } - } - - fun CompositionNode.distances(point: Vector2): List = distances(point, searchFrom = this) - - /** - * Test a given `contour` against org.openrndr.shape.contours in the composition tree - * @param contour the query contour - * @param searchFrom a node from which the search starts, defaults to composition root - * @param mergeThreshold minimum distance between intersections before they are merged together, - * 0.0 or lower means no org.openrndr.shape.merge - * @return a list of `org.openrndr.shape.ShapeNodeIntersection` - */ - fun intersections( - contour: ShapeContour, - searchFrom: CompositionNode = composition.root as GroupNode, - mergeThreshold: Double = 0.5 - ): List { - return searchFrom.findShapes().pflatMap { node -> - if (node.bounds.intersects(contour.bounds)) { - node.shape.contours.flatMap { nodeContour -> - intersections(contour, nodeContour).map { - ShapeNodeIntersection(node, it) - } - } - } else { - emptyList() - } - }.let { - if (mergeThreshold > 0.0) { - it.merge(mergeThreshold) - } else { - it - } - } - } - - fun CompositionNode.intersections(contour: ShapeContour, mergeThreshold: Double = 0.5) = - intersections(contour, this, mergeThreshold) - - /** - * Test a given `shape` against org.openrndr.shape.contours in the composition tree - * @param shape the query shape - * @param searchFrom a node from which the search starts, defaults to composition root - * @return a list of `org.openrndr.shape.ShapeNodeIntersection` - */ - fun intersections( - shape: Shape, - searchFrom: CompositionNode = composition.root as GroupNode, - mergeThreshold: Double = 0.5 - ): List { - return shape.contours.flatMap { - intersections(it, searchFrom, mergeThreshold) - } - } - - fun CompositionNode.intersections(shape: Shape, mergeThreshold: Double = 0.5) = - intersections(shape, this, mergeThreshold) - - - fun shape(shape: Shape, insert: Boolean = true): ShapeNode? { - if (shape.empty) { - return null - } - - val inverseModel = model.inversed - val postShape = mask?.let { intersection(shape, it.transform(inverseModel)) } ?: shape - - if (postShape.empty) { - return null - } - - // only use clipping for open shapes - val clipMode = if (postShape.topology == ShapeTopology.CLOSED) clipMode else ClipMode.DISABLED - - return when (clipMode.op) { - ClipOp.DISABLED, ClipOp.REVERSE_DIFFERENCE -> { - val shapeNode = ShapeNode(postShape) - - val shapeTransform: Matrix44 = when (transformMode) { - TransformMode.KEEP -> { - shapeNode.transform = model - Matrix44.IDENTITY - } - - TransformMode.APPLY -> { - shapeNode.transform = Matrix44.IDENTITY - model - } - } - shapeNode.shape = when (clipMode.op) { - ClipOp.DISABLED -> postShape.transform(shapeTransform) - ClipOp.REVERSE_DIFFERENCE -> { - val shapeNodes = (if (!clipMode.grouped) composition.findShapes() else cursor.findShapes()) - var toInsert = shape - val inverse = model.inversed - for (node in shapeNodes) { - if (toInsert.empty) { - break - } else { - toInsert = difference(toInsert, node.effectiveShape.transform(inverse)) - } - } - toInsert - } - - else -> error("unreachable") - } - shapeNode.stroke = stroke - shapeNode.strokeOpacity = strokeOpacity - shapeNode.strokeWeight = strokeWeight - shapeNode.miterLimit = miterlimit - shapeNode.lineCap = lineCap - shapeNode.lineJoin = lineJoin - shapeNode.fill = fill - shapeNode.fillOpacity = fillOpacity - - if (insert) { - cursor.children.add(shapeNode) - shapeNode.parent = cursor - } - shapeNode - } - - else -> { - val shapeNodes = (if (!clipMode.grouped) composition.findShapes() else cursor.findShapes()) - val toRemove = shapeNodes.pmap { shapeNode -> - val inverse = shapeNode.effectiveTransform.inversed - val transformedShape = postShape.transform(inverse * model) - val operated = - when (clipMode.op) { - ClipOp.INTERSECT -> intersection(shapeNode.shape, transformedShape) - ClipOp.UNION -> union(shapeNode.shape, transformedShape) - ClipOp.DIFFERENCE -> difference(shapeNode.shape, transformedShape) - else -> error("unsupported base op ${clipMode.op}") - } - return@pmap if (!operated.empty) { - shapeNode.shape = operated - null - } else { - shapeNode - } - } - for (node in toRemove) { - node?.remove() - } - null - } - } - } - - fun shapes(shapes: List, insert: Boolean = true) = shapes.map { shape(it, insert) } - - fun rectangle(rectangle: Rectangle, closed: Boolean = true, insert: Boolean = true) = - contour(rectangle.contour.let { - if (closed) { - it - } else { - it.open - } - }, insert = insert) - - fun rectangle(x: Double, y: Double, width: Double, height: Double, closed: Boolean = true, insert: Boolean = true) = - rectangle( - Rectangle(x, y, width, height), closed, insert - ) - - fun rectangles(rectangles: List, insert: Boolean = true) = rectangles.map { rectangle(it, insert) } - - fun rectangles(positions: List, width: Double, height: Double, insert: Boolean = true) = - rectangles(positions.map { - Rectangle(it, width, height) - }, insert) - - fun rectangles(positions: List, dimensions: List, insert: Boolean) = - rectangles((positions zip dimensions).map { - Rectangle(it.first, it.second.x, it.second.y) - }, insert) - - fun circle(x: Double, y: Double, radius: Double, closed: Boolean = true, insert: Boolean = true) = circle( - Circle( - Vector2(x, y), - radius - ), closed, insert - ) - - fun circle(position: Vector2, radius: Double, closed: Boolean = true, insert: Boolean = true) = circle( - Circle( - position, - radius - ), closed, insert - ) - - fun circle(circle: Circle, closed: Boolean = true, insert: Boolean = true) = contour(circle.contour.let { - if (closed) { - it - } else { - it.open - } - }, insert) - - fun circles(circles: List, insert: Boolean = true) = circles.map { circle(it, insert) } - - fun circles(positions: List, radius: Double, insert: Boolean = true) = circles(positions.map { - Circle( - it, - radius - ) - }, insert) - - fun circles(positions: List, radii: List, insert: Boolean = true) = - circles((positions zip radii).map { - Circle( - it.first, - it.second - ) - }, insert) - - /* - fun ellipse( - x: Double, - y: Double, - xRadius: Double, - yRadius: Double, - rotationInDegrees: Double = 0.0, - closed: Boolean = true, - insert: Boolean = true - ) = ellipse(Vector2(x, y), xRadius, yRadius, rotationInDegrees, closed, insert) - - fun ellipse( - center: Vector2, - xRadius: Double, - yRadius: Double, - rotationInDegrees: Double, - closed: Boolean = true, - insert: Boolean = true - ) = contour(OrientedEllipse(center, xRadius, yRadius, rotationInDegrees).contour.let { - if (closed) { - it - } else { - it.open - } - }, insert) - */ - - - fun lineSegment( - startX: Double, - startY: Double, - endX: Double, - endY: Double, - insert: Boolean = true - ) = lineSegment(LineSegment(startX, startY, endX, endY), insert) - - fun lineSegment( - start: Vector2, - end: Vector2, - insert: Boolean = true - ) = lineSegment(LineSegment(start, end), insert) - - fun lineSegment( - lineSegment: LineSegment, - insert: Boolean = true - ) = contour(lineSegment.contour, insert) - - fun lineSegments( - lineSegments: List, - insert: Boolean = true - ) = lineSegments.map { - lineSegment(it, insert) - } - - fun segment( - start: Vector2, - c0: Vector2, - c1: Vector2, - end: Vector2, - insert: Boolean = true - ) = segment(Segment2D(start, c0, c1, end), insert) - - fun segment( - start: Vector2, - c0: Vector2, - end: Vector2, - insert: Boolean = true - ) = segment(Segment2D(start, c0, end), insert) - - fun segment( - start: Vector2, - end: Vector2, - insert: Boolean = true - ) = segment(Segment2D(start, end), insert) - - fun segment( - segment: Segment2D, - insert: Boolean = true - ) = contour(segment.contour, insert) - - fun segments( - segments: List, - insert: Boolean = true - ) = segments.map { - segment(it, insert) - } - - fun lineStrip( - points: List, - insert: Boolean = true - ) = contour(ShapeContour.fromPoints(points, false, YPolarity.CW_NEGATIVE_Y), insert) - - fun lineLoop( - points: List, - insert: Boolean = true - ) = contour(ShapeContour.fromPoints(points, true, YPolarity.CW_NEGATIVE_Y), insert) - - fun text( - text: String, - position: Vector2, - insert: Boolean = true - ): TextNode { - val g = GroupNode() - g.style.transform = Transform.Matrix(transform { translate(position.xy0) }) - val textNode = TextNode(text, null).apply { - this.style.fill = when (val f = this@CompositionDrawer.fill) { - is ColorRGBa -> Paint.RGB(f) - else -> Paint.None - } - - } - g.children.add(textNode) - if (insert) { - cursor.children.add(g) - } - return textNode - } - - fun textOnContour( - text: String, - contour: ShapeContour, - insert: Boolean = true - ): TextNode { - val textNode = TextNode(text, contour) - if (insert) { - cursor.children.add(textNode) - } - return textNode - } - - fun texts(text: List, positions: List) = - (text zip positions).map { - text(it.first, it.second) - } - - /** - * Adds an image to the composition tree - */ - fun image( - image: ColorBuffer, - x: Double = 0.0, - y: Double = 0.0, - insert: Boolean = true - ): ImageNode { - val node = ImageNode(image, x, y, width = image.width.toDouble(), height = image.height.toDouble()) - node.style.transform = Transform.Matrix(this.model) - if (insert) { - cursor.children.add(node) - } - return node - } - - fun composition(composition: Composition): CompositionNode { - val rootContainer = GroupNode() - val newRoot = composition.root.duplicate(insert = false) - newRoot.parent = rootContainer - rootContainer.children.add(newRoot) - rootContainer.transform *= model - rootContainer.parent = cursor - cursor.children.add(rootContainer) - - return rootContainer - } - - fun CompositionNode.translate(x: Double, y: Double, z: Double = 0.0) { - transform = transform.transform { - translate(x, y, z) - } - } - - fun CompositionNode.rotate(angleInDegrees: Double, pivot: Vector2 = Vector2.ZERO) { - transform = transform.transform { - translate(pivot.xy0) - rotate(Vector3.UNIT_Z, angleInDegrees) - translate(-pivot.xy0) - } - } - - fun CompositionNode.scale(scale: Double, pivot: Vector2 = Vector2.ZERO) { - transform = transform.transform { - translate(pivot.xy0) - scale(scale, scale, scale) - translate(-pivot.xy0) - } - } - - @OptIn(ExperimentalContracts::class) - fun CompositionNode.transform(builder: TransformBuilder.() -> Unit) { - contract { - callsInPlace(builder, kotlin.contracts.InvocationKind.EXACTLY_ONCE) - } - return this.transform(builder) - } - - /** - * Returns a deep copy of a [CompositionNode]. - * If [insert] is true the copy is inserted at [cursor]. - * @return a deep copy of the node - */ - // TODO: Include new features - fun CompositionNode.duplicate(insert: Boolean = true): CompositionNode { - fun nodeCopy(node: CompositionNode): CompositionNode { - val copy = when (node) { - is ImageNode -> { - ImageNode(node.image, node.x, node.y, node.width, node.height) - } - - is ShapeNode -> { - ShapeNode(node.shape) - } - - is TextNode -> { - TextNode(node.text, node.contour) - } - - is GroupNode -> { - val children = node.children.map { nodeCopy(it) }.toMutableList() - val groupNode = GroupNode(children) - groupNode.children.forEach { - it.parent = groupNode - } - groupNode.attributes.putAll(node.attributes) - groupNode - } - } - copy.style = node.style - return copy - } - - val copy = nodeCopy(this) - if (insert) { - this@CompositionDrawer.cursor.children.add(copy) - copy.parent = cursor - } - return copy - } -} - -/** - * Draws a vector composition by applying a provided drawing function. - * - * @param documentBounds Defines the dimensions and bounds of the composition. Defaults to `defaultCompositionDimensions`. - * @param composition The target composition to be drawn on. If null, a new composition will be created. - * @param cursor Specifies the current position within the composition structure. Defaults to the root of the given composition cast as a `GroupNode`. - * @param drawFunction The actual drawing logic that will be executed in the drawing context of the `CompositionDrawer`. - * @return The resulting `Composition` after applying the drawing function. - */ -@OptIn(ExperimentalContracts::class) -fun drawComposition( - documentBounds: CompositionDimensions = defaultCompositionDimensions, - composition: Composition? = null, - cursor: GroupNode? = composition?.root as? GroupNode, - drawFunction: CompositionDrawer.() -> Unit -): Composition { - contract { - callsInPlace(drawFunction, InvocationKind.EXACTLY_ONCE) - } - return CompositionDrawer(documentBounds, composition, cursor).apply { drawFunction() }.composition -} - -/** - * Draws content into an existing composition using the provided drawing function. - * - * @param cursor an optional [GroupNode] that serves as the starting point for drawing. - * Defaults to the root of the composition if not provided. - * @param drawFunction the drawing logic to be executed using a [CompositionDrawer]. - * This function should contain instructions to draw content into the composition. - */ -@OptIn(ExperimentalContracts::class) -fun Composition.draw(cursor: GroupNode? = this.root as? GroupNode, drawFunction: CompositionDrawer.() -> Unit) { - contract { - callsInPlace(drawFunction, InvocationKind.EXACTLY_ONCE) - } - drawComposition(composition = this, drawFunction = drawFunction) -} diff --git a/orx-composition/src/commonMain/kotlin/CompositionStyleSheet.kt b/orx-composition/src/commonMain/kotlin/CompositionStyleSheet.kt deleted file mode 100644 index 0a64619c..00000000 --- a/orx-composition/src/commonMain/kotlin/CompositionStyleSheet.kt +++ /dev/null @@ -1,434 +0,0 @@ -@file:Suppress("RemoveExplicitTypeArguments") - -package org.openrndr.extra.composition -import org.openrndr.color.* -import org.openrndr.draw.* -import org.openrndr.extra.composition.AttributeOrPropertyKey.* -import org.openrndr.extra.composition.Inheritance.* -import org.openrndr.math.* -import org.openrndr.shape.Rectangle -import kotlin.jvm.JvmRecord -import kotlin.reflect.* - -enum class Inheritance { - INHERIT, - RESET -} - -sealed interface AttributeOrPropertyValue { - val value: Any? - override fun toString(): String -} - -sealed interface Paint : AttributeOrPropertyValue { - override val value: ColorRGBa? - - class RGB(override val value: ColorRGBa) : Paint { - override fun toString(): String { - val hexs = listOf(value.r, value.g, value.b).map { - (it.coerceIn(0.0, 1.0) * 255.0).toInt().toString(16).padStart(2, '0') - } - return hexs.joinToString(prefix = "#", separator = "") - } - } - - // This one is kept just in case, it's not handled in any way yet - object CurrentColor : Paint { - override val value: ColorRGBa - get() = TODO("Not yet implemented") - - override fun toString(): String = "currentcolor" - } - - object None : Paint { - override val value: ColorRGBa? = null - override fun toString(): String = "none" - } -} - -sealed interface Shade : AttributeOrPropertyValue { - override val value: ShadeStyle - - class Value(override val value: ShadeStyle) : Shade { - override fun toString(): String = "" - } -} - -sealed interface Length : AttributeOrPropertyValue { - override val value: Double - - class Pixels(override val value: Double) : Length { - companion object { - fun fromInches(value: Double) = Pixels(value * 96.0) - fun fromPicas(value: Double) = Pixels(value * 16.0) - fun fromPoints(value: Double) = Pixels(value * (4.0 / 3.0)) - fun fromCentimeters(value: Double) = Pixels(value * (96.0 / 2.54)) - fun fromMillimeters(value: Double) = Pixels(value * (96.0 / 25.4)) - fun fromQuarterMillimeters(value: Double) = Pixels(value * (96.0 / 101.6)) - } - - override fun toString(): String = "$value" - } - - class Percent(override val value: Double) : Length { - override fun toString(): String { - return "${value}%" - } - } - - enum class UnitIdentifier { - IN, - PC, - PT, - PX, - CM, - MM, - Q - } -} - -inline val Double.pixels: Length.Pixels - get() = Length.Pixels(this) -inline val Double.percent: Length.Percent - get() = Length.Percent(this) - -sealed interface Numeric : AttributeOrPropertyValue { - override val value: Double - - class Rational(override val value: Double) : Numeric { - override fun toString(): String = "$value" - } -} - -sealed interface Transform : AttributeOrPropertyValue { - override val value: Matrix44 - - class Matrix(override val value: Matrix44) : Transform { - override fun toString(): String { - return if (value == Matrix44.IDENTITY) { - "" - } else { - "matrix(${value.c0r0} ${value.c0r1} " + - "${value.c1r0} ${value.c1r1} " + - "${value.c3r0} ${value.c3r1})" - } - } - } - - object None : Transform { - override val value = Matrix44.IDENTITY - override fun toString(): String = "" - } -} - -sealed interface Visibility : AttributeOrPropertyValue { - override val value: Boolean - - object Visible : Visibility { - override val value = true - override fun toString() = "visible" - } - - object Hidden : Visibility { - override val value = false - override fun toString() = "hidden" - } - - // This exists because the spec specifies so, - // it is effectively Hidden. - object Collapse : Visibility { - override val value = false - override fun toString() = "collapse" - } -} - -sealed interface Display : AttributeOrPropertyValue { - override val value: Boolean - - object Inline : Display { - override val value = true - override fun toString() = "inline" - } - - object Block : Display { - override val value = true - override fun toString() = "block" - } - - object None : Display { - override val value = false - override fun toString() = "none" - } -} - -sealed interface LineCap : AttributeOrPropertyValue { - override val value: org.openrndr.draw.LineCap - - object Round : LineCap { - override val value = org.openrndr.draw.LineCap.ROUND - override fun toString() = "round" - } - - object Butt : LineCap { - override val value = org.openrndr.draw.LineCap.BUTT - override fun toString() = "butt" - } - - object Square : LineCap { - override val value = org.openrndr.draw.LineCap.SQUARE - override fun toString() = "square" - } -} - -sealed interface LineJoin : AttributeOrPropertyValue { - override val value: org.openrndr.draw.LineJoin - - object Miter : LineJoin { - override val value = org.openrndr.draw.LineJoin.MITER - override fun toString() = "miter" - } - - object Bevel : LineJoin { - override val value = org.openrndr.draw.LineJoin.BEVEL - override fun toString() = "bevel" - } - - object Round : LineJoin { - override val value = org.openrndr.draw.LineJoin.ROUND - override fun toString() = "round" - } -} - -enum class Align { - NONE, - X_MIN_Y_MIN, - X_MID_Y_MIN, - X_MAX_Y_MIN, - X_MIN_Y_MID, - X_MID_Y_MID, - X_MAX_Y_MID, - X_MIN_Y_MAX, - X_MID_Y_MAX, - X_MAX_Y_MAX -} - -enum class MeetOrSlice { - MEET, - SLICE -} - -@JvmRecord -data class AspectRatio(val align: Align, val meetOrSlice: MeetOrSlice) : AttributeOrPropertyValue { - override val value: AspectRatio - get() = this - - companion object { - val DEFAULT = AspectRatio(Align.X_MID_Y_MID, MeetOrSlice.MEET) - } - - override fun toString(): String { - if (this == DEFAULT) { - return "" - } - - val a = when (align) { - Align.NONE -> "none" - Align.X_MIN_Y_MIN -> "xMinYMin" - Align.X_MID_Y_MIN -> "xMidYMin" - Align.X_MAX_Y_MIN -> "xMaxYMin" - Align.X_MIN_Y_MID -> "xMinYMid" - Align.X_MID_Y_MID -> "xMidYMid" - Align.X_MAX_Y_MID -> "xMaxYMid" - Align.X_MIN_Y_MAX -> "xMinYMax" - Align.X_MID_Y_MAX -> "xMidYMax" - Align.X_MAX_Y_MAX -> "xMaxYMax" - } - val m = when (meetOrSlice) { - MeetOrSlice.MEET -> "meet" - MeetOrSlice.SLICE -> "slice" - } - - return "$a $m" - } -} - -sealed interface ViewBox : AttributeOrPropertyValue { - override val value: Rectangle? - - class Value(override val value: Rectangle) : ViewBox { - override fun toString(): String = - "${value.x.toInt()} ${value.y.toInt()} ${value.width.toInt()} ${value.height.toInt()}" - } - - /** - * The viewBox has not been defined, - * **not** that it doesn't exist. - */ - object None : ViewBox { - override val value: Rectangle? = null - override fun toString(): String = "" - } -} - -@JvmRecord -private data class PropertyBehavior(val inherit: Inheritance, val initial: AttributeOrPropertyValue) - -private object PropertyBehaviors { - val behaviors = HashMap() -} - -private class PropertyDelegate( - val name: AttributeOrPropertyKey, - inheritance: Inheritance, - val initial: T -) { - init { - PropertyBehaviors.behaviors[name] = PropertyBehavior(inheritance, initial) - } - - @Suppress("UNCHECKED_CAST") - operator fun getValue(style: Styleable, property: KProperty<*>): T { - return (style[name] ?: PropertyBehaviors.behaviors[name]!!.initial) as T - } - - operator fun setValue(style: Styleable, property: KProperty<*>, value: T?) { - style[name] = value - } -} - -sealed class Styleable { - val properties = HashMap() - - operator fun get(name: AttributeOrPropertyKey) = properties[name] - - operator fun set(name: AttributeOrPropertyKey, value: AttributeOrPropertyValue?) { - properties[name] = value - } - - infix fun inherit(from: Style): Style { - return Style().also { - from.properties.forEach { (name, value) -> - if (PropertyBehaviors.behaviors[name]?.inherit == INHERIT) { - it.properties[name] = value - } - } - it.properties.putAll(properties) - } - } - - // Because AttributeOrPropertyValue has a toString override, - // we can abuse it for equality checks. - fun isInherited(from: Styleable, attributeKey: AttributeOrPropertyKey): Boolean = - when (this.properties[attributeKey].toString()) { - from.properties[attributeKey].toString() -> true - PropertyBehaviors.behaviors[attributeKey]?.initial.toString() -> true - else -> false - } -} - -class DocumentStyle : Styleable() -class Style : Styleable() - -var DocumentStyle.viewBox by PropertyDelegate(VIEW_BOX, RESET, ViewBox.None) -var DocumentStyle.preserveAspectRatio by PropertyDelegate( - PRESERVE_ASPECT_RATIO, - RESET, AspectRatio.DEFAULT -) - -var Style.stroke by PropertyDelegate(STROKE, INHERIT, Paint.None) -var Style.strokeOpacity by PropertyDelegate(STROKE_OPACITY, INHERIT, Numeric.Rational(1.0)) -var Style.strokeWeight by PropertyDelegate(STROKE_WIDTH, INHERIT, 1.0.pixels) -var Style.miterLimit by PropertyDelegate(STROKE_MITERLIMIT, INHERIT, Numeric.Rational(4.0)) -var Style.lineCap by PropertyDelegate(STROKE_LINECAP, INHERIT, LineCap.Butt) -var Style.lineJoin by PropertyDelegate(STROKE_LINEJOIN, INHERIT, LineJoin.Miter) - -var Style.fill by PropertyDelegate(FILL, INHERIT, Paint.RGB(ColorRGBa.BLACK)) -var Style.fillOpacity by PropertyDelegate(FILL_OPACITY, INHERIT, Numeric.Rational(1.0)) - -var Style.transform by PropertyDelegate(TRANSFORM, RESET, Transform.None) - -// Okay so the spec says `display` isn't inheritable, but effectively acts so -// when the element and its children are excluded from the rendering tree. -var Style.display by PropertyDelegate(DISPLAY, RESET, Display.Inline) -var Style.opacity by PropertyDelegate(OPACITY, RESET, Numeric.Rational(1.0)) -var Style.visibility by PropertyDelegate(VISIBILITY, INHERIT, Visibility.Visible) - -var Style.x by PropertyDelegate(X, RESET, 0.0.pixels) -var Style.y by PropertyDelegate(Y, RESET, 0.0.pixels) -var Style.width by PropertyDelegate(WIDTH, RESET, 768.0.pixels) -var Style.height by PropertyDelegate(HEIGHT, RESET, 576.0.pixels) - -var Style.shadeStyle by PropertyDelegate(SHADESTYLE, INHERIT, Shade.Value(ShadeStyle())) - -enum class AttributeOrPropertyKey { - // @formatter:off - // Attributes - BASE_PROFILE { override fun toString() = "baseProfile" }, - CLASS { override fun toString() = "class" }, - CX { override fun toString() = "cx" }, - CY { override fun toString() = "cy" }, - D { override fun toString() = "d" }, - DX { override fun toString() = "dx" }, - DY { override fun toString() = "dy" }, - GRADIENT_UNITS { override fun toString() = "gradientUnits" }, - HEIGHT { override fun toString() = "height" }, - ID { override fun toString() = "id" }, - OFFSET { override fun toString() = "offset" }, - PATH_LENGTH { override fun toString() = "pathLength" }, - POINTS { override fun toString() = "points" }, - PRESERVE_ASPECT_RATIO { override fun toString() = "preserveAspectRatio" }, - R { override fun toString() = "r" }, - ROTATE { override fun toString() = "rotate" }, - RX { override fun toString() = "rx" }, - RY { override fun toString() = "ry" }, - SPACE { override fun toString() = "xml:space" }, - STYLE { override fun toString() = "style" }, - TRANSFORM { override fun toString() = "transform" }, - VERSION { override fun toString() = "version" }, - VIEW_BOX { override fun toString() = "viewBox" }, - WIDTH { override fun toString() = "width" }, - X { override fun toString() = "x" }, - X1 { override fun toString() = "x1" }, - X2 { override fun toString() = "x2" }, - Y { override fun toString() = "y" }, - Y1 { override fun toString() = "y1" }, - Y2 { override fun toString() = "y2" }, - - // Properties - COLOR { override fun toString() = "color" }, - DIRECTION { override fun toString() = "direction" }, - DISPLAY { override fun toString() = "display" }, - DISPLAY_ALIGN { override fun toString() = "display-align" }, - FILL { override fun toString() = "fill" }, - FILL_OPACITY { override fun toString() = "fill-opacity" }, - FILL_RULE { override fun toString() = "fill-rule" }, - FONT_FAMILY { override fun toString() = "font-family" }, - FONT_SIZE { override fun toString() = "font-size" }, - FONT_STYLE { override fun toString() = "font-style" }, - FONT_VARIANT { override fun toString() = "font-variant" }, - FONT_WEIGHT { override fun toString() = "font-weight" }, - OPACITY { override fun toString() = "opacity" }, - STOP_COLOR { override fun toString() = "stop-color" }, - STOP_OPACITY { override fun toString() = "stop-opacity" }, - STROKE { override fun toString() = "stroke" }, - STROKE_DASHARRAY { override fun toString() = "stroke-dasharray" }, - STROKE_DASHOFFSET { override fun toString() = "stroke-dashoffset" }, - STROKE_LINECAP { override fun toString() = "stroke-linecap" }, - STROKE_LINEJOIN { override fun toString() = "stroke-linejoin" }, - STROKE_MITERLIMIT { override fun toString() = "stroke-miterlimit" }, - STROKE_OPACITY { override fun toString() = "stroke-opacity" }, - STROKE_WIDTH { override fun toString() = "stroke-width" }, - TEXT_ALIGN { override fun toString() = "text-align" }, - TEXT_ANCHOR { override fun toString() = "text-anchor" }, - UNICODE_BIDI { override fun toString() = "unicode-bidi" }, - VECTOR_EFFECT { override fun toString() = "vector-effect" }, - VISIBILITY { override fun toString() = "visibility" }, - - // Made-up properties - // because "Compositions aren't SVGs and yadda yadda" - // this one's for you, edwin - SHADESTYLE { override fun toString() = "" }; - - abstract override fun toString(): String - // @formatter:on -} \ No newline at end of file diff --git a/orx-composition/src/commonMain/kotlin/DrawerExtensions.kt b/orx-composition/src/commonMain/kotlin/DrawerExtensions.kt deleted file mode 100644 index 1a35363c..00000000 --- a/orx-composition/src/commonMain/kotlin/DrawerExtensions.kt +++ /dev/null @@ -1,93 +0,0 @@ -package org.openrndr.extra.composition - -import org.openrndr.draw.Drawer - - -/** - * Renders a vector `Composition` onto the `Drawer`. This method applies transformations, styles, - * and renders the hierarchy of nodes from the given `Composition` object. - * - * @param composition The vector composition containing the root node and associated dimensions. - * It includes the styling and viewport transformation details necessary for rendering. - */ -fun Drawer.composition(composition: Composition) { - pushModel() - pushStyle() - - // viewBox transformation - model *= composition.calculateViewportTransform() - - fun node(compositionNode: CompositionNode) { - pushModel() - pushStyle() - model *= compositionNode.style.transform.value - - shadeStyle = (compositionNode.style.shadeStyle as Shade.Value).value - - when (compositionNode) { - is ShapeNode -> { - - compositionNode.style.stroke.let { - stroke = when (it) { - is Paint.RGB -> it.value.copy(alpha = 1.0) - Paint.None -> null - Paint.CurrentColor -> null - } - } - compositionNode.style.strokeOpacity.let { - stroke = when (it) { - is Numeric.Rational -> stroke?.opacify(it.value) - } - } - compositionNode.style.strokeWeight.let { - strokeWeight = when (it) { - is Length.Pixels -> it.value - is Length.Percent -> composition.normalizedDiagonalLength() * it.value / 100.0 - } - } - compositionNode.style.miterLimit.let { - miterLimit = when (it) { - is Numeric.Rational -> it.value - } - } - compositionNode.style.lineCap.let { - lineCap = it.value - } - compositionNode.style.lineJoin.let { - lineJoin = it.value - } - compositionNode.style.fill.let { - fill = when (it) { - is Paint.RGB -> it.value.copy(alpha = 1.0) - is Paint.None -> null - is Paint.CurrentColor -> null - } - } - compositionNode.style.fillOpacity.let { - fill = when (it) { - is Numeric.Rational -> fill?.opacify(it.value) - } - } - compositionNode.style.opacity.let { - when (it) { - is Numeric.Rational -> { - stroke = stroke?.opacify(it.value) - fill = fill?.opacify(it.value) - } - } - } - shape(compositionNode.shape) - } - is ImageNode -> { - image(compositionNode.image) - } - is TextNode -> TODO() - is GroupNode -> compositionNode.children.forEach { node(it) } - } - popModel() - popStyle() - } - node(composition.root) - popModel() - popStyle() -} \ No newline at end of file diff --git a/orx-composition/src/commonMain/kotlin/ProgramExtensions.kt b/orx-composition/src/commonMain/kotlin/ProgramExtensions.kt deleted file mode 100644 index da266380..00000000 --- a/orx-composition/src/commonMain/kotlin/ProgramExtensions.kt +++ /dev/null @@ -1,53 +0,0 @@ -package org.openrndr.extra.composition - -import org.openrndr.Program -import org.openrndr.shape.Rectangle -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract - -/** - * Draws a composition within the specified document bounds or an existing composition. - * This function utilizes a customizable draw function to define the drawing behavior. - * - * @param documentBounds Specifies the dimensions for the drawing area. Defaults to the full drawable area of the program. - * @param composition An optional existing composition to draw onto. If not provided, a new composition is created. - * @param cursor An optional cursor representing the current position in the composition hierarchy. Defaults to the root of the provided composition. - * @param drawFunction A lambda function defining the drawing operations to be performed using the `CompositionDrawer`. - * @return The resulting composition after applying the draw function. - */ -@OptIn(ExperimentalContracts::class) -fun Program.drawComposition( - documentBounds: CompositionDimensions = CompositionDimensions(0.0.pixels, 0.0.pixels, this.drawer.width.toDouble().pixels, this.drawer.height.toDouble().pixels), - composition: Composition? = null, - cursor: GroupNode? = composition?.root as? GroupNode, - drawFunction: CompositionDrawer.() -> Unit -): Composition { - contract { - callsInPlace(drawFunction, InvocationKind.EXACTLY_ONCE) - } - return CompositionDrawer(documentBounds, composition, cursor).apply { drawFunction() }.composition -} - -/** - * Draws a composition using the specified document bounds and drawing logic. - * Optionally, an existing composition and cursor can be passed to update or build upon them. - * - * @param documentBounds The bounding rectangle representing the area to be drawn. - * @param composition An optional existing composition to update. If null, a new composition will be created. - * @param cursor An optional cursor `GroupNode` used as the starting position for appending new elements. Defaults to the root of the provided composition if available. - * @param drawFunction A lambda function containing the drawing operations to be applied. - * @return The resulting `Composition` object after performing the drawing operations. - */ -@OptIn(ExperimentalContracts::class) -fun Program.drawComposition( - documentBounds: Rectangle, - composition: Composition? = null, - cursor: GroupNode? = composition?.root as? GroupNode, - drawFunction: CompositionDrawer.() -> Unit -): Composition { - contract { - callsInPlace(drawFunction, InvocationKind.EXACTLY_ONCE) - } - return CompositionDrawer(CompositionDimensions(documentBounds), composition, cursor).apply { drawFunction() }.composition -} \ No newline at end of file diff --git a/orx-composition/src/commonTest/kotlin/TestComposition.kt b/orx-composition/src/commonTest/kotlin/TestComposition.kt deleted file mode 100644 index b7374f19..00000000 --- a/orx-composition/src/commonTest/kotlin/TestComposition.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.openrndr.extra.composition - -import org.openrndr.shape.Shape -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNull - -class TestComposition { - val composition = let { _ -> - val root = GroupNode().also { it.id = "outer" } - root.children += GroupNode().also { - it.id = "inner" - } - root.children += ShapeNode(Shape.EMPTY).also { - it.id = "shape" - } - Composition(root) - } - - @Test - fun findGroup() { - assertEquals("outer", composition.findGroup("outer")?.id) - assertEquals("inner", composition.findGroup("inner")?.id) - assertNull(composition.findGroup("shape")) - } - - @Test - fun findShape() { - assertEquals("shape", composition.findShape("shape")?.id) - assertNull(composition.findShape("inner")) - assertNull(composition.findShape("outer")) - } - - @Test - fun findImage() { - assertNull(composition.findImage("inner")) - assertNull(composition.findImage("outer")) - assertNull(composition.findImage("shape")) - } -} diff --git a/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer01.kt b/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer01.kt deleted file mode 100644 index 154e9892..00000000 --- a/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer01.kt +++ /dev/null @@ -1,39 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.composition.composition -import org.openrndr.extra.composition.drawComposition -import org.openrndr.extra.svg.toSVG -import org.openrndr.math.Vector2 - -/** - * Demonstrates how to - * - * - Create a Composition - * - Draw it on the program window - * - Save it to an SVG file - * - Print the SVG content as text - */ -fun main() = application { - program { - val composition = drawComposition { - fill = ColorRGBa.PINK - stroke = ColorRGBa.BLACK - strokeWeight = 10.0 - circle(Vector2(width / 2.0, height / 2.0), 100.0) - circle(Vector2(200.0, 200.0), 50.0) - } - - // print the svg to the console - println(composition.toSVG()) - - // save svg to a File - //composition.saveToFile(File("/path/to/design.svg")) - - extend { - drawer.clear(ColorRGBa.WHITE) - - // draw the composition to the screen - drawer.composition(composition) - } - } -} diff --git a/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer02.kt b/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer02.kt deleted file mode 100644 index f8b32370..00000000 --- a/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer02.kt +++ /dev/null @@ -1,32 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.composition.ClipMode -import org.openrndr.extra.composition.composition -import org.openrndr.extra.composition.drawComposition - -/** - * Demonstrates how to draw a Composition and how to use - * `ClipMode.REVERSE_DIFFERENCE` to clip shapes. - * - * The first shape clips part of the second one away, - * producing a shape that seems to be behind the first one. - * - * Without clipping, the second circle would cover part of the first one. - */ -fun main() = application { - program { - val composition = drawComposition { - fill = null - circle(width / 2.0, height / 2.0, 100.0) - - fill = ColorRGBa.BLACK - clipMode = ClipMode.REVERSE_DIFFERENCE - circle(width / 2.0 + 50.0, height / 2.0, 100.0) - } - - extend { - drawer.clear(ColorRGBa.PINK) - drawer.composition(composition) - } - } -} diff --git a/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer03.kt b/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer03.kt deleted file mode 100644 index d9266f17..00000000 --- a/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer03.kt +++ /dev/null @@ -1,42 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.composition.ClipMode -import org.openrndr.extra.composition.composition -import org.openrndr.extra.composition.drawComposition -import org.openrndr.extra.svg.saveToFile -import java.io.File - -/** - * Draws a composition using 3 circles and `ClipMode.REVERSE_DIFFERENCE`. - * - * A println() demonstrates that the result contains 3 shapes: - * a complete circle, a moon-like shape, and a shape with two small black areas. - * - * One way to verify this is by saving the design as an SVG file and opening - * it in vector editing software. - * - */ -fun main() = application { - program { - val composition = drawComposition { - fill = null - clipMode = ClipMode.REVERSE_DIFFERENCE - - circle(width / 2.0 - 50.0, height / 2.0, 100.0) - circle(width / 2.0 + 50.0, height / 2.0, 100.0) - - fill = ColorRGBa.BLACK - circle(width / 2.0, height / 2.0, 100.0) - } - - println(composition.findShapes().size) - - // save svg to a File - //composition.saveToFile(File("/path/to/design.svg")) - - extend { - drawer.clear(ColorRGBa.PINK) - drawer.composition(composition) - } - } -} diff --git a/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer04.kt b/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer04.kt deleted file mode 100644 index 01d04c1d..00000000 --- a/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer04.kt +++ /dev/null @@ -1,61 +0,0 @@ -import org.openrndr.MouseButton -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.composition.composition -import org.openrndr.extra.composition.draw -import org.openrndr.extra.composition.drawComposition -import org.openrndr.math.Polar -import org.openrndr.math.Vector2 -import kotlin.math.sin - -/** - * Demonstrates how to add content to and how to clear an existing Composition. - * - * A number of circles are added when the program starts. - * Dragging the mouse button adds more circles. - * Right-clicking the mouse clears the Composition. - */ -fun main() = application { - program { - val composition = drawComposition { } - - // initial Composition content - repeat(360) { - composition.draw { - fill = ColorRGBa.WHITE - val r = sin(it / 90.0) * 30 + 40 - circle( - drawer.bounds.center + Polar(it * 5.0, r * 2).cartesian, - r - ) - } - } - - extend { - drawer.clear(ColorRGBa.PINK) - drawer.composition(composition) - } - - // To avoid drawing too many circles when dragging the mouse, - // we require a minimum separation between them - var lastPosition = Vector2.INFINITY - val minSeparation = 10.0 - - mouse.dragged.listen { - if(it.position.distanceTo(lastPosition) > minSeparation) { - composition.draw { - fill = ColorRGBa.WHITE - // the drag speed affects the radius - circle(it.position, 5.0 + it.dragDisplacement.length * 5.0) - } - lastPosition = it.position - } - } - - mouse.buttonDown.listen { - if (it.button == MouseButton.RIGHT) { - composition.clear() - } - } - } -} diff --git a/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer05.kt b/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer05.kt deleted file mode 100644 index 5bea2f58..00000000 --- a/orx-composition/src/jvmDemo/kotlin/DemoCompositionDrawer05.kt +++ /dev/null @@ -1,43 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.composition.composition -import org.openrndr.extra.composition.drawComposition -import org.openrndr.math.Vector2 -import org.openrndr.extra.svg.saveToFile -import java.io.File - -/** - * Demonstrates how to - * - * - Create a Composition with a group - * - Add XML attributes so the group appears as a layer in Inkscape - */ -fun main() = application { - program { - val composition = drawComposition { - val layer = group { - fill = ColorRGBa.PINK - stroke = ColorRGBa.BLACK - strokeWeight = 10.0 - circle(Vector2(width / 2.0, height / 2.0), 100.0) - circle(Vector2(200.0, 200.0), 50.0) - } - // Demonstrate how to set custom attributes on the `GroupNode` - // These are stored in the SVG file. - - layer.id = "Layer_2" - layer.attributes["inkscape:label"] = "Layer 1" - layer.attributes["inkscape:groupmode"] = "layer" - } - - // save svg to a File - //composition.saveToFile(File("/path/to/design.svg")) - - extend { - drawer.clear(ColorRGBa.WHITE) - - // draw the composition to the screen - drawer.composition(composition) - } - } -} diff --git a/orx-compositor/README.md b/orx-compositor/README.md deleted file mode 100644 index 94e34a14..00000000 --- a/orx-compositor/README.md +++ /dev/null @@ -1,275 +0,0 @@ -# orx-compositor - -Toolkit to make composite (layered) images using blend modes and filters. - -## Usage - -A `Composite` is made using the `compose {}` builder. We start with a very simple example: -```kotlin -fun main() = application { - program { - val composite = compose { - // this is only executed once - val position = Vector2(100.0, 100.0) - - draw { - // code inside draw blocks is executed whenever the composite is drawn - drawer.circle(position, 100.0) - } - } - extend { - // draw the composite - composite.draw(drawer) - } - } -} -``` - -### Layers - -A `Composite` with two layers looks like this. - -```kotlin -fun main() = application { - program { - val composite = compose { - // this layer is drawn first - layer { - val position = Vector2(100.0, 100.0) - draw { - drawer.circle(position, 100.0) - } - } - - // this layer is drawn second - layer { - val position = Vector2(150.0, 150.0) - draw { - drawer.circle(position, 100.0) - } - blend(Multiply()) - } - } - extend { - // draw the composite - composite.draw(drawer) - } - } -} -``` - -Layers can be nested: - -```kotlin -fun main() = application { - program { - val composite = compose { - layer { - layer { - // this draw is processed first - draw { } - } - layer { - // this draw is processed second - draw { } - } - val position = Vector2(100.0, 100.0) - draw { - // this draw is processed third - drawer.circle(position, 100.0) - } - } - - // this layer is drawn second - layer { - val position = Vector2(150.0, 150.0) - draw { - drawer.circle(position, 100.0) - } - blend(Multiply()) - } - } - extend { - // draw the composite - composite.draw(drawer) - } - } -} -``` - -### Asides - -An aside is a layer which output is not directly included in the composite drawing. The contents of an aside can be used in layers and post-processing. - -```kotlin -fun main() = application { - program { - val composite = compose { - - val a = aside { - val position = Vector2(250.0, 250.0) - draw { - drawer.circle(position, 100.0) - } - } - - // this layer is drawn second - layer { - val position = Vector2(150.0, 150.0) - draw { - drawer.image(a) - drawer.circle(position, 100.0) - } - blend(Multiply()) - } - } - extend { - // draw the composite - composite.draw(drawer) - } - } -} -``` - - -### Post-processing - -```kotlin -fun main() = application { - program { - val composite = compose { - layer { - draw { - - } - // the first Filter1to1 to apply - post(ApproximateGaussianBlur()) { - // here is code that is executed everytime the layer is drawn - } - - // the second Filter1to1 to apply - post(ColorCorrection()) { - // here is code that is executed everytime the layer is drawn - } - } - } - extend { - // draw the composite - composite.draw(drawer) - } - } -} -``` -#### Using filters with multiple inputs - -Some filters use more than a single input in producing their output, these filters inherit from Filter2to1, Filter3to1, Filter4to1 etc. -One such filter is `DirectionalBlur` which has image and direction field inputs. In the following example we use an aside to -draw a direction field which is fed into the blur filter. - -```kotlin -fun main() = application { - program { - val composite = compose { - val directionField = aside { - draw { - // [...] - } - } - layer { - draw { - // [...] - } - post(DirectionalBlur(), directionField) - } - } - extend { - // draw the composite - composite.draw(drawer) - } - } -} -``` - -##### Example - -```kotlin -import org.openrndr.application -import org.openrndr.draw.loadImage -import org.openrndr.extra.compositor.* -import org.openrndr.extra.fx.blend.Add -import org.openrndr.extra.fx.edges.EdgesWork -import org.openrndr.extra.gui.GUI -import org.openrndr.math.Vector2 - -fun main() = application { - configure { - width = 768 - height = 768 - } - program { - val w2 = width / 2.0 - val h2 = height / 2.0 - - val c = compose { - draw { - drawer.fill = ColorRGBa.PINK - drawer.circle(width / 2.0, height / 2.0, 10.0) - } - - layer { - blend(Add()) - - draw { - drawer.circle(width / 2.0, height / 2.0, 100.0) - } - post(ApproximateGaussianBlur()) { - window = 10 - sigma = Math.cos(seconds * 10.0) * 10.0 + 10.0 - } - } - } - extend(gui) - extend { - c.draw(drawer) - } - } -} -``` - -## Demos -### DemoAside01 - -Demonstrates how to reuse a layer in the Compositor by using `aside { }`. - -The `aside` block can make use of `draw`, `mask` and `post`. In this demo -only the latter is used to apply a full-window animated `Checkers` effect. -The `aside` is not displayed by default. - -Next, a white, centered circle is drawn. - -Finally, a `HashBlurDynamic` post-processing effect is applied. The dynamic -version of the HashBlur effect multiplies its `radius` argument by the red component -of the provided texture (containing the animated checkers in this case). - -![DemoAside01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-compositor/images/DemoAside01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoAside01.kt) - -### DemoCompositor01 - -Compositor demo showing 3 layers of moving items -with a different amount of blur in each layer, -simulating depth of field - -![DemoCompositor01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-compositor/images/DemoCompositor01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCompositor01.kt) - -### DemoCompositor02 - -Demonstration of using [BufferMultisample] on a per layer basis. -Try changing which layer has multisampling applied and observe the results. - -![DemoCompositor02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-compositor/images/DemoCompositor02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCompositor02.kt) diff --git a/orx-compositor/build.gradle.kts b/orx-compositor/build.gradle.kts deleted file mode 100644 index a62892c6..00000000 --- a/orx-compositor/build.gradle.kts +++ /dev/null @@ -1,28 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") -} - -kotlin { - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - implementation(project(":orx-fx")) - implementation(project(":orx-parameters")) - implementation(project(":orx-shader-phrases")) - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - } - } - - @Suppress("UNUSED_VARIABLE") - val jvmDemo by getting { - dependencies { - implementation(project(":orx-fx")) - implementation(project(":orx-compositor")) - } - } - } -} \ No newline at end of file diff --git a/orx-compositor/src/commonMain/kotlin/Compositor.kt b/orx-compositor/src/commonMain/kotlin/Compositor.kt deleted file mode 100644 index 43da674f..00000000 --- a/orx-compositor/src/commonMain/kotlin/Compositor.kt +++ /dev/null @@ -1,396 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.compositor - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.fx.blend.SourceIn -import org.openrndr.extra.fx.blend.SourceOut -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract -import kotlin.jvm.JvmRecord - - -enum class LayerType { - LAYER, - ASIDE -} - -private val sourceOut = persistent { SourceOut() } -private val sourceIn = persistent { SourceIn() } - -/** - * A single layer representation - */ -@Description("Layer") -open class Layer internal constructor( - val type: LayerType, - private val bufferMultisample: BufferMultisample = BufferMultisample.Disabled -) { - var maskLayer: Layer? = null - var drawFunc: () -> Unit = {} - val children: MutableList = mutableListOf() - var blendFilter: Pair Unit>? = null - val postFilters: MutableList, Filter.() -> Unit>> = mutableListOf() - var colorType = ColorType.UINT8_SRGB - private var unresolvedAccumulation: ColorBuffer? = null - var accumulation: ColorBuffer? = null - - @BooleanParameter("enabled") - var enabled = true - - @BooleanParameter("Invert mask") - var invertMask = false - var clearColor: ColorRGBa? = ColorRGBa.TRANSPARENT - private var layerTarget: RenderTarget? = null - - val result: ColorBuffer - get() { - return layerTarget?.colorBuffer(0) ?: error("layer result not ready") - } - - /** - * draw the layer - */ - protected fun drawLayer(drawer: Drawer, cache: ColorBufferCache) { - if (!enabled) { - return - } - - val activeRenderTarget = RenderTarget.active - - if (shouldCreateLayerTarget(activeRenderTarget)) { - createLayerTarget(activeRenderTarget, drawer, bufferMultisample) - } - - layerTarget?.let { target -> - maskLayer?.let { - if (it.shouldCreateLayerTarget(activeRenderTarget)) { - it.createLayerTarget(activeRenderTarget, drawer, it.bufferMultisample) - } - - it.layerTarget?.let { maskRt -> - drawer.isolatedWithTarget(maskRt) { - drawer.fill = ColorRGBa.WHITE - drawer.stroke = ColorRGBa.WHITE - drawer.clear(ColorRGBa.TRANSPARENT) - it.drawFunc() - } - } - } - - drawer.isolatedWithTarget(target) { - children.filter { it.type == LayerType.ASIDE }.forEach { - it.drawLayer(drawer, cache) - } - - clearColor?.let { - drawer.clear(it) - } - drawFunc() - children.filter { it.type == LayerType.LAYER }.forEach { - it.drawLayer(drawer, cache) - } - } - - val layerPost = if (postFilters.isEmpty()) target.colorBuffer(0) else postFilters.let { filters -> - val targets = cache[ColorBufferCacheKey(colorType, target.contentScale)] - targets.forEach { - it.fill(ColorRGBa.TRANSPARENT) - } - var localSource = target.colorBuffer(0) - for ((i, filter) in filters.withIndex()) { - filter.first.apply(filter.third) - val sources = - arrayOf(localSource) + filter.second.map { it.result } - .toTypedArray() - filter.first.apply(sources, arrayOf(targets[i % targets.size])) - localSource = targets[i % targets.size] - } - targets[postFilters.lastIndex % targets.size] - } - - maskLayer?.let { - val maskFilter = if (invertMask) sourceOut else sourceIn - maskFilter.apply(arrayOf(layerPost, it.layerTarget!!.colorBuffer(0)), layerPost) - } - - if (type == LayerType.ASIDE) { - if (postFilters.isNotEmpty()) { - require(layerPost != result) - layerPost.copyTo(result) - } - } else if (type == LayerType.LAYER) { - val localBlendFilter = blendFilter - if (localBlendFilter == null) { - drawer.isolated { - drawer.defaults() - if (bufferMultisample == BufferMultisample.Disabled) { - drawer.image(layerPost, layerPost.bounds, drawer.bounds) - } else { - layerPost.copyTo(accumulation!!) - drawer.image(accumulation!!, layerPost.bounds, drawer.bounds) - } - } - } else { - localBlendFilter.first.apply(localBlendFilter.second) - activeRenderTarget.colorBuffer(0).copyTo(unresolvedAccumulation!!) - if (bufferMultisample == BufferMultisample.Disabled) { - localBlendFilter.first.apply( - arrayOf(unresolvedAccumulation!!, layerPost), - unresolvedAccumulation!! - ) - } else { - layerPost.copyTo(accumulation!!) - localBlendFilter.first.apply( - arrayOf(unresolvedAccumulation!!, accumulation!!), - unresolvedAccumulation!! - ) - } - - if (activeRenderTarget !is ProgramRenderTarget) { - unresolvedAccumulation!!.copyTo(target.colorBuffer(0)) - } - unresolvedAccumulation!!.copyTo(activeRenderTarget.colorBuffer(0)) - } - } - } - } - - private fun shouldCreateLayerTarget(activeRenderTarget: RenderTarget): Boolean { - return layerTarget == null - || ((layerTarget?.width != activeRenderTarget.width || layerTarget?.height != activeRenderTarget.height) - && activeRenderTarget.width > 0 && activeRenderTarget.height > 0) - } - - private fun createLayerTarget( - activeRenderTarget: RenderTarget, drawer: Drawer, bufferMultisample: BufferMultisample - ) { - layerTarget?.destroy() - layerTarget = renderTarget( - activeRenderTarget.width, activeRenderTarget.height, - activeRenderTarget.contentScale, bufferMultisample - ) { - colorBuffer(type = colorType) - depthBuffer() - } - if (bufferMultisample != BufferMultisample.Disabled) { - accumulation?.destroy() - accumulation = colorBuffer( - activeRenderTarget.width, activeRenderTarget.height, - activeRenderTarget.contentScale, type = colorType - ) - } - unresolvedAccumulation?.destroy() - unresolvedAccumulation = colorBuffer( - activeRenderTarget.width, activeRenderTarget.height, - activeRenderTarget.contentScale, type = colorType - ) - layerTarget?.let { - drawer.withTarget(it) { - drawer.clear(ColorRGBa.TRANSPARENT) - } - } - } - - fun Drawer.image(layer: Layer) { - val cb = layer.result - image(cb) - } -} - -/** - * Creates a new layer within the current layer, allowing for hierarchical composition of drawings. - * The newly created layer inherits properties such as color type and multisample from the parent layer - * unless explicitly overridden. A custom lambda function can be applied to configure the new layer. - * - * @param colorType The color type for the new layer. Defaults to the color type of the parent layer. - * @param multisample Specifies the multisampling mode for the new layer to control antialiasing. - * Defaults to [BufferMultisample.Disabled]. - * @param function A configuration block where the properties and behavior of the new layer - * can be defined. - * @return The newly created layer, which is also added as a child of the parent layer. - */ -@OptIn(ExperimentalContracts::class) -fun Layer.layer( - colorType: ColorType = this.colorType, - multisample: BufferMultisample = BufferMultisample.Disabled, - function: Layer.() -> Unit -): Layer { - contract { - callsInPlace(function, InvocationKind.EXACTLY_ONCE) - } - val layer = Layer(LayerType.LAYER, multisample).apply { function() } - layer.colorType = colorType - children.add(layer) - return layer -} - -/** - * Creates a new `Layer` of type `ASIDE` as a child of the current layer, applies the specified function - * to configure it, and returns the created layer. - * - * @param colorType The color type for the new layer. Defaults to the color type of the parent layer. - * @param multisample Multisampling configuration for the new layer. Defaults to `BufferMultisample.Disabled`. - * @param function Configuration function applied to the newly created layer. - * @return The newly created `Layer` of type `ASIDE`. - */ -@OptIn(ExperimentalContracts::class) -fun Layer.aside( - colorType: ColorType = this.colorType, - multisample: BufferMultisample = BufferMultisample.Disabled, - function: Layer.() -> Unit -): Layer { - contract { - callsInPlace(function, InvocationKind.EXACTLY_ONCE) - } - val layer = Layer(LayerType.ASIDE, multisample).apply { function() } - layer.colorType = colorType - children.add(layer) - return layer -} - -fun Layer.apply(drawer: Drawer, - filter: T, source: Layer, colorType: ColorType = this.colorType, - function: T.() -> Unit -): Layer { - val layer = Layer(LayerType.ASIDE) - layer.colorType = colorType - layer.draw { - drawer.image(source.result) - } - layer.post(filter, function) - children.add(layer) - return layer -} - -fun Layer.apply(drawer: Drawer, - filter: T, source0: Layer, source1:Layer, colorType: ColorType = this.colorType, - function: T.() -> Unit -): Layer { - val layer = Layer(LayerType.ASIDE) - layer.colorType = colorType - layer.draw { - drawer.image(source0.result) - } - layer.post(filter, source1, function) - children.add(layer) - return layer -} - - -/** - * set the draw contents of the layer - */ -fun Layer.draw(function: () -> Unit) { - drawFunc = function -} - - -/** - * the drawing acts as a mask on the layer - */ -fun Layer.mask(function: () -> Unit) { - maskLayer = Layer(LayerType.LAYER).apply { - this.drawFunc = function - } -} - -/** - * add a post-processing filter to the layer - */ -fun Layer.post(filter: F, configure: F.() -> Unit = {}): F { - @Suppress("UNCHECKED_CAST") - postFilters.add(Triple(filter as Filter, emptyArray(), configure as Filter.() -> Unit)) - return filter -} - -fun Layer.post(filter: F, input1: Layer, configure: F.() -> Unit = {}): F { - require(input1.type == LayerType.ASIDE) - @Suppress("UNCHECKED_CAST") - postFilters.add(Triple(filter as Filter, arrayOf(input1), configure as Filter.() -> Unit)) - return filter -} - -fun Layer.post(filter: F, input1: Layer, input2: Layer, configure: F.() -> Unit = {}): F { - require(input1.type == LayerType.ASIDE) - require(input2.type == LayerType.ASIDE) - @Suppress("UNCHECKED_CAST") - postFilters.add(Triple(filter as Filter, arrayOf(input1, input2), configure as Filter.() -> Unit)) - return filter -} - - -/** - * add a blend filter to the layer - */ -fun Layer.blend(filter: F, configure: F.() -> Unit = {}): F { - @Suppress("UNCHECKED_CAST") - blendFilter = Pair(filter as Filter, configure as Filter.() -> Unit) - return filter -} - -@JvmRecord -data class ColorBufferCacheKey( - val colorType: ColorType, - val contentScale: Double -) - -class ColorBufferCache(val width: Int, val height: Int) { - val cache = mutableMapOf>() - - operator fun get(key: ColorBufferCacheKey): List { - return cache.getOrPut(key) { - listOf( - colorBuffer(width, height, type = key.colorType, contentScale = key.contentScale), - colorBuffer(width, height, type = key.colorType, contentScale = key.contentScale), - ) - } - } - - fun destroy() { - cache.forEach { - it.value.forEach { cb -> cb.destroy() } - } - } -} - -class Composite(val session: Session?) : Layer(LayerType.LAYER), AutoCloseable { - private var cache = ColorBufferCache(RenderTarget.active.width, RenderTarget.active.height) - fun draw(drawer: Drawer) { - - session?.push() - if (cache.width != RenderTarget.active.width || cache.height != RenderTarget.active.height) { - cache.destroy() - cache = ColorBufferCache(RenderTarget.active.width, RenderTarget.active.height) - } - drawLayer(drawer, cache) - session?.pop() - } - - override fun close() { - session?.close() - } -} - -/** - * Creates a `Composite` object and allows configuration of its layers and effects within the provided `function`. - * - * @param function the lambda function used to configure the `Composite`. It is invoked with the `Composite` instance as the receiver. - * @return the configured `Composite` object. - */ -@OptIn(ExperimentalContracts::class) -fun compose(function: Composite.() -> Unit): Composite { - contract { - callsInPlace(function, InvocationKind.EXACTLY_ONCE) - } - - val session = Session.active.fork() - val root = Composite(session) - root.function() - session.pop() - return root -} \ No newline at end of file diff --git a/orx-compositor/src/jvmDemo/kotlin/DemoAside01.kt b/orx-compositor/src/jvmDemo/kotlin/DemoAside01.kt deleted file mode 100644 index be5c078f..00000000 --- a/orx-compositor/src/jvmDemo/kotlin/DemoAside01.kt +++ /dev/null @@ -1,49 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorType -import org.openrndr.extra.compositor.* -import org.openrndr.extra.fx.blur.HashBlurDynamic -import org.openrndr.extra.fx.patterns.Checkers -import kotlin.math.cos - -/** - * Demonstrates how to reuse a layer in the Compositor by using `aside { }`. - * - * The `aside` block can make use of `draw`, `mask` and `post`. In this demo - * only the latter is used to apply a full-window animated `Checkers` effect. - * The `aside` is not displayed by default. - * - * Next, a white, centered circle is drawn. - * - * Finally, a `HashBlurDynamic` post-processing effect is applied. The dynamic - * version of the HashBlur effect multiplies its `radius` argument by the red component - * of the provided texture (containing the animated checkers in this case). - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val c = compose { - layer { - val a = aside(colorType = ColorType.FLOAT32) { - post(Checkers()) { - this.size = cos(seconds + 2.0) * 0.5 + 0.5 - } - } - draw { - drawer.clear(ColorRGBa.GRAY.shade(0.5)) - drawer.circle(width / 2.0, height / 2.0, 100.0) - } - post(HashBlurDynamic(), a) { - time = seconds - radius = 25.0 - } - } - } - extend { - c.draw(drawer) - } - } -} diff --git a/orx-compositor/src/jvmDemo/kotlin/DemoCompositor01.kt b/orx-compositor/src/jvmDemo/kotlin/DemoCompositor01.kt deleted file mode 100644 index 986ca540..00000000 --- a/orx-compositor/src/jvmDemo/kotlin/DemoCompositor01.kt +++ /dev/null @@ -1,87 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.color.rgb -import org.openrndr.draw.Drawer -import org.openrndr.extra.compositor.compose -import org.openrndr.extra.compositor.draw -import org.openrndr.extra.compositor.layer -import org.openrndr.extra.compositor.post -import org.openrndr.extra.fx.blur.ApproximateGaussianBlur -import org.openrndr.math.Vector3 -import kotlin.random.Random - -/** - * Compositor demo showing 3 layers of moving items - * with a different amount of blur in each layer, - * simulating depth of field - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - - program { - data class Item(var pos: Vector3, val color: ColorRGBa) { - fun draw(drawer: Drawer) { - pos -= Vector3(pos.z * 3.0, 0.0, 0.0) - if (pos.x < -260.0) { - pos = Vector3(width + 260.0, pos.y, pos.z) - } - drawer.fill = null - drawer.stroke = color - drawer.strokeWeight = 2.0 + 30.0 * pos.z * pos.z - drawer.circle(pos.xy, 10.0 + 250.0 * pos.z * pos.z) - } - } - - val items = List(50) { - val pos = Vector3(Random.nextDouble() * width, - Random.nextDouble(0.3, 0.7) * height, - Random.nextDouble()) - Item(pos, ColorRGBa.PINK.shade(Random.nextDouble(0.2, 0.9))) - }.sortedBy { it.pos.z } - - val composite = compose { - layer { - draw { - drawer.stroke = null - items.filter { it.pos.z < 0.33 }.forEach { - it.draw(drawer) - } - } - post(ApproximateGaussianBlur()) { - window = 25 - sigma = 5.00 - } - } - - layer { - draw { - drawer.stroke = null - items.filter { it.pos.z in 0.33..0.66 }.forEach { - it.draw(drawer) - } - } - } - - layer { - draw { - drawer.stroke = null - items.filter { it.pos.z > 0.66 }.forEach { - it.draw(drawer) - } - } - post(ApproximateGaussianBlur()) { - window = 25 - sigma = 5.00 - } - } - } - - extend { - drawer.clear(rgb(0.2)) - composite.draw(drawer) - } - } -} diff --git a/orx-compositor/src/jvmDemo/kotlin/DemoCompositor02.kt b/orx-compositor/src/jvmDemo/kotlin/DemoCompositor02.kt deleted file mode 100644 index 7f6bb58d..00000000 --- a/orx-compositor/src/jvmDemo/kotlin/DemoCompositor02.kt +++ /dev/null @@ -1,50 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.BufferMultisample -import org.openrndr.extra.compositor.blend -import org.openrndr.extra.compositor.compose -import org.openrndr.extra.compositor.draw -import org.openrndr.extra.compositor.layer -import org.openrndr.extra.fx.blend.Normal -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -/** - * Demonstration of using [BufferMultisample] on a per layer basis. - * Try changing which layer has multisampling applied and observe the results. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - - program { - val layers = compose { - layer(multisample = BufferMultisample.SampleCount(4)) { - draw { - drawer.translate(drawer.bounds.center) - drawer.rotate(seconds + 5) - drawer.fill = ColorRGBa.PINK - drawer.rectangle(Rectangle.fromCenter(Vector2.ZERO, 200.0)) - } - - layer() { - blend(Normal()) { - clip = true - } - draw { - drawer.rotate((seconds + 5) * -2) - drawer.fill = ColorRGBa.WHITE - drawer.rectangle(Rectangle.fromCenter(Vector2.ZERO, 200.0)) - } - } - } - } - - extend { - drawer.clear(ColorRGBa.WHITE) - layers.draw(drawer) - } - } -} \ No newline at end of file diff --git a/orx-delegate-magic/README.md b/orx-delegate-magic/README.md deleted file mode 100644 index 9bd42caf..00000000 --- a/orx-delegate-magic/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# orx-delegate magic - -Collection of magical property delegators. For tracking variable change or -interpolate towards the value of a variable. - -## Delegated properties - -[Kotlin documentation](https://kotlinlang.org/docs/delegated-properties.html) - -## Property smoothing - -```kotlin -val state = object { - var radius = 10.0 -} - -val smoothRadius by smoothing(state::radius) -``` - - -## Property dynamics - -```kotlin -val state = object { - var radius = 10.0 -} - -val dynamicRadius by springForcing(state::radius) -``` - -## Property tracking - -```kotlin -val state = object { - var radius = 10.0 -} - -val radiusHistory by tracking(state::radius) -``` - -## Demos -### DemoDifferencing01 - - - -![DemoDifferencing01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-delegate-magic/images/DemoDifferencing01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoDifferencing01.kt) - -### DemoFollowing01 - -Demonstrates using delegate-magic tools with -[Double] and [Vector2]. - -The white circle's position uses [following]. -The red circle's position uses [smoothing]. - -`following` uses physics (velocity and acceleration). -`smoothing` eases values towards the target. - -Variables using delegates (`by`) interpolate -toward target values, shown as gray lines. - -The behavior of the delegate-magic functions can be configured -via arguments that affect their output. - -The arguments come in pairs of similar name: -The first one, often of type [Double], is constant, -The second one contains `Property` in its name and can be -modified after its creation and even be linked to a UI -to modify the behavior of the delegate function in real time. -The `Property` argument overrides the other. - -![DemoFollowing01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-delegate-magic/images/DemoFollowing01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoFollowing01.kt) - -### DemoSmoothing01 - - - -![DemoSmoothing01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-delegate-magic/images/DemoSmoothing01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoSmoothing01.kt) - -### DemoSpring01 - - - -![DemoSpring01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-delegate-magic/images/DemoSpring01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoSpring01.kt) diff --git a/orx-delegate-magic/build.gradle.kts b/orx-delegate-magic/build.gradle.kts deleted file mode 100644 index 7713770d..00000000 --- a/orx-delegate-magic/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") -} - -kotlin { - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - implementation(project(":orx-parameters")) - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - } - } - - @Suppress("UNUSED_VARIABLE") - val jvmDemo by getting { - dependencies { - implementation(project(":orx-delegate-magic")) - implementation(project(":orx-jvm:orx-gui")) - } - } - } -} \ No newline at end of file diff --git a/orx-delegate-magic/src/commonMain/kotlin/aggregation/Aggregators.kt b/orx-delegate-magic/src/commonMain/kotlin/aggregation/Aggregators.kt deleted file mode 100644 index a17ba445..00000000 --- a/orx-delegate-magic/src/commonMain/kotlin/aggregation/Aggregators.kt +++ /dev/null @@ -1,49 +0,0 @@ -@file:Suppress("PackageDirectoryMismatch") - -package org.openrndr.extra.delegatemagic.aggregation - -import kotlin.math.abs - -/** - * Return element with largest magnitude - * @since 0.4.3 - */ -fun List.maxMag(): Double { - - this.max() - if (isEmpty()) { - error("list is empty") - } - var maxMag = Double.NEGATIVE_INFINITY - var maxMagWithSign = 0.0 - - for (i in indices) { - val a = abs(this[i]) - if (a > maxMag) { - maxMag = a - maxMagWithSign = this[i] - } - } - return maxMagWithSign -} - -/** - * Return element with smallest magnitude - * @since 0.4.3 - */ -fun List.minMag(): Double { - if (isEmpty()) { - error("list is empty") - } - var minMag = Double.POSITIVE_INFINITY - var minMagWithSign = 0.0 - - for (i in indices) { - val a = abs(this[i]) - if (a < minMag) { - minMag = a - minMagWithSign = this[i] - } - } - return minMagWithSign -} \ No newline at end of file diff --git a/orx-delegate-magic/src/commonMain/kotlin/aggregation/ListAggregation.kt b/orx-delegate-magic/src/commonMain/kotlin/aggregation/ListAggregation.kt deleted file mode 100644 index 5260db33..00000000 --- a/orx-delegate-magic/src/commonMain/kotlin/aggregation/ListAggregation.kt +++ /dev/null @@ -1,46 +0,0 @@ -@file:Suppress("PackageDirectoryMismatch") - -package org.openrndr.extra.delegatemagic.aggregation - -import org.openrndr.Clock -import kotlin.reflect.KProperty -import kotlin.reflect.KProperty0 - -/** - * Property delegation by list aggregation - */ -class ListPropertyAggregation( - private val clock: Clock, - private val property: KProperty0>, - val aggregationFunction: (List) -> R -) { - private var output: R? = null - private var lastTime: Double? = null - - operator fun getValue(any: Any?, property: KProperty<*>): R { - if (lastTime != null) { - val dt = clock.seconds - lastTime!! - if (dt > 1E-10) { - output = aggregationFunction(this.property.get()) - } - } else { - output = aggregationFunction(this.property.get()) - } - - lastTime = clock.seconds - return output!! - } -} - -/** - * Aggregate list property - * @param property the list property to aggregate - * @param aggregationFunction the function that is - * @since 0.4.3 - */ -fun Clock.aggregating( - property: KProperty0>, - aggregationFunction: (List) -> R -): ListPropertyAggregation { - return ListPropertyAggregation(this, property, aggregationFunction) -} \ No newline at end of file diff --git a/orx-delegate-magic/src/commonMain/kotlin/difference/PropertyDifferencer.kt b/orx-delegate-magic/src/commonMain/kotlin/difference/PropertyDifferencer.kt deleted file mode 100644 index 82a3e40d..00000000 --- a/orx-delegate-magic/src/commonMain/kotlin/difference/PropertyDifferencer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package difference - -import org.openrndr.Clock -import kotlin.reflect.KProperty -import kotlin.reflect.KProperty0 - -class DoublePropertyDifferencer( - private val clock: Clock, - private val property: KProperty0, -) { - private var lastValue: Double? = null - private var output: Double? = null - private var lastTime: Double? = null - operator fun getValue(any: Any?, property: KProperty<*>): Double { - if (lastTime != null) { - val dt = clock.seconds - lastTime!! - if (dt > 1E-10) { - output = this.property.get() - lastValue!! - lastValue = this.property.get() - } - } else { - lastValue = this.property.get() - output = lastValue!! - lastValue!! - } - lastTime = clock.seconds - return output ?: error("no value") - } -} - -fun Clock.differencing(property: KProperty0) = DoublePropertyDifferencer(this, property) diff --git a/orx-delegate-magic/src/commonMain/kotlin/dynamics/PropertySpringForcer.kt b/orx-delegate-magic/src/commonMain/kotlin/dynamics/PropertySpringForcer.kt deleted file mode 100644 index 44a7e579..00000000 --- a/orx-delegate-magic/src/commonMain/kotlin/dynamics/PropertySpringForcer.kt +++ /dev/null @@ -1,128 +0,0 @@ -@file:Suppress("PackageDirectoryMismatch") - -package org.openrndr.extra.delegatemagic.dynamics - -import org.openrndr.Clock -import org.openrndr.math.LinearType -import kotlin.reflect.KProperty -import kotlin.reflect.KProperty0 - -class DoublePropertySpringForcer( - private val clock: Clock, - private val property: KProperty0, - private val k: Double, - private val kProperty: KProperty0?, - private val decay: Double, - private val decayProperty: KProperty0? - -) { - private var output: Double? = null - private var lastTime: Double? = null - private var velocity = 0.0 - operator fun getValue(any: Any?, property: KProperty<*>): Double { - val k = kProperty?.get() ?: k - val decay = decayProperty?.get() ?: decay - - val anchor = this.property.get() - if (lastTime != null) { - val dt = clock.seconds - lastTime!! - if (dt > 0.0) { - val sfY = -k * (output!! - anchor) - velocity = velocity * decay + sfY * dt * 10.0 - output = output!! + velocity * dt * 10.0 - } - } else { - output = this.property.get() - } - lastTime = clock.seconds - return output ?: error("no value") - } -} - -class LinearTypePropertySpringForcer>( - private val clock: Clock, - private val property: KProperty0, - private val k: Double, - private val kProperty: KProperty0?, - private val decay: Double, - private val decayProperty: KProperty0? -) { - private var output: T? = null - private var lastTime: Double? = null - private var velocity: T? = null - operator fun getValue(any: Any?, property: KProperty<*>): T { - val k = kProperty?.get() ?: k - val decay = decayProperty?.get() ?: decay - - val anchor = this.property.get() - if (lastTime != null) { - val dt = clock.seconds - lastTime!! - if (dt > 0.0) { - val sfY = (output!! - anchor) * -k - - velocity = if (velocity != null) { - velocity!! * decay + sfY * dt * 10.0 - } else { - sfY * dt * 10.0 - } - output = output!! + velocity!! * dt * 10.0 - } - } else { - output = this.property.get() - } - lastTime = clock.seconds - return output ?: error("no value") - } -} - -/** - * Create a property spring force delegate - * @param property the property that is used as the spring anchor - * @param k the spring stiffness - * @param kProperty the spring stiffness property, overrides [k] - * @param decay velocity decay, best to set to < 1 - * @param decayProperty velocity decay property, overrides [decay] - * @since 0.4.3 - */ -fun Clock.springForcing( - property: KProperty0, - k: Double = 1.0, - kProperty: KProperty0? = null, - decay: Double = 0.9, - decayProperty: KProperty0? = null -): DoublePropertySpringForcer { - return DoublePropertySpringForcer( - clock = this, - property = property, - k = k, - kProperty = kProperty, - decay = decay, - decayProperty = decayProperty - ) -} - -/** - * Create a property spring force delegate - * @param property the property that is used as the spring anchor - * @param k the spring stiffness - * @param kProperty the spring stiffness property, overrides [k] - * @param decay velocity decay, best to set to < 1 - * @param decayProperty velocity decay property, overrides [decay] - * @since 0.4.3 - */ -fun > Clock.springForcing( - property: KProperty0, - k: Double = 1.0, - kProperty: KProperty0? = null, - decay: Double = 0.9, - decayProperty: KProperty0? = null -): LinearTypePropertySpringForcer { - return LinearTypePropertySpringForcer( - clock = this, - property = property, - k = k, - kProperty = kProperty, - decay = decay, - decayProperty = decayProperty - ) -} \ No newline at end of file diff --git a/orx-delegate-magic/src/commonMain/kotlin/smoothing/PropertyFollower.kt b/orx-delegate-magic/src/commonMain/kotlin/smoothing/PropertyFollower.kt deleted file mode 100644 index 041998f7..00000000 --- a/orx-delegate-magic/src/commonMain/kotlin/smoothing/PropertyFollower.kt +++ /dev/null @@ -1,163 +0,0 @@ -@file:Suppress("PackageDirectoryMismatch") - -package org.openrndr.extra.delegatemagic.smoothing - -import org.openrndr.Clock -import org.openrndr.math.EuclideanVector -import org.openrndr.math.LinearType -import org.openrndr.math.clamp -import org.openrndr.math.map -import kotlin.math.abs -import kotlin.math.min -import kotlin.math.sign -import kotlin.reflect.KProperty -import kotlin.reflect.KProperty0 - -class DoublePropertyFollower( - private val clock: Clock, - private val property: KProperty0, - private val maxAccel: Double, - private val maxAccelProperty: KProperty0?, - private val maxSpeed: Double, - private val maxSpeedProperty: KProperty0?, - private val dampDist: Double, - private val dampDistProperty: KProperty0? -) { - private var current: Double? = null - private var lastTime: Double? = null - private var velocity = 0.0 - operator fun getValue(any: Any?, property: KProperty<*>): Double { - if (lastTime != null) { - val dt = clock.seconds - lastTime!! - if (dt > 1E-10) { - val maxAccel = maxAccelProperty?.get() ?: maxAccel - val maxSpeed = maxSpeedProperty?.get() ?: maxSpeed - val dampDist = dampDistProperty?.get() ?: dampDist - - var offset = this.property.get() - current!! - val len = abs(offset) - val dist = min(dampDist, len) // 0.0 .. dampDist - - // convert dist to desired speed - offset = offset.sign * - dist.map(0.0, dampDist, 0.0, maxSpeed) - - val acceleration = clamp( - offset - velocity, - -maxAccel, maxAccel - ) - - velocity = clamp( - velocity + acceleration, - -maxSpeed, maxSpeed - ) - - current = current!! + velocity - } - } else { - current = this.property.get() - } - lastTime = clock.seconds - return current ?: error("no value") - } -} - -class PropertyFollower( - private val clock: Clock, - private val property: KProperty0, - private val maxAccel: Double, - private val maxAccelProperty: KProperty0?, - private val maxSpeed: Double, - private val maxSpeedProperty: KProperty0?, - private val dampDist: Double, - private val dampDistProperty: KProperty0? -) where T : LinearType, T : EuclideanVector { - private var current: T? = null - private var lastTime: Double? = null - private var velocity = property.get().zero - operator fun getValue(any: Any?, property: KProperty<*>): T { - if (lastTime != null) { - val dt = clock.seconds - lastTime!! - if (dt > 1E-10) { - val maxAccel = maxAccelProperty?.get() ?: maxAccel - val maxSpeed = maxSpeedProperty?.get() ?: maxSpeed - val dampDist = dampDistProperty?.get() ?: dampDist - - var offset = this.property.get() - current!! - val len = offset.length - val dist = min(dampDist, len) // 0.0 .. dampDist - - // convert dist to desired speed - offset = offset.normalized * - dist.map(0.0, dampDist, 0.0, maxSpeed) - - var acceleration = offset - velocity - if (acceleration.length > maxAccel) { - acceleration = acceleration.normalized * maxAccel - } - - velocity += acceleration - if (velocity.length > maxSpeed) { - velocity = velocity.normalized * maxSpeed - } - - current = current!! + velocity - } - } else { - current = this.property.get() - } - lastTime = clock.seconds - return current ?: error("no value") - } -} - -/** - * Create a property follower delegate - * @param property the property to smooth - * @param cfg the simulation parameters - * @since 0.4.3 - */ -fun Clock.following( - property: KProperty0, - maxAccel: Double = 0.1, - maxAccelProperty: KProperty0? = null, - maxSpeed: Double = 10.0, - maxSpeedProperty: KProperty0? = null, - dampDist: Double = 400.0, - dampDistProperty: KProperty0? = null -) = DoublePropertyFollower( - clock = this, - property = property, - maxAccel = maxAccel, - maxAccelProperty = maxAccelProperty, - maxSpeed = maxSpeed, - maxSpeedProperty = maxSpeedProperty, - dampDist = dampDist, - dampDistProperty = dampDistProperty -) - -/** - * Create a property follower delegate - * @param property the property to smooth - * @param cfg the simulation parameters - * @since 0.4.3 - */ -fun Clock.following( - property: KProperty0, - maxAccel: Double = 0.1, - maxAccelProperty: KProperty0? = null, - maxSpeed: Double = 10.0, - maxSpeedProperty: KProperty0? = null, - dampDist: Double = 400.0, - dampDistProperty: KProperty0? = null -) where T : LinearType, T : EuclideanVector = - PropertyFollower( - clock = this, - property = property, - maxAccel = maxAccel, - maxAccelProperty = maxAccelProperty, - maxSpeed = maxSpeed, - maxSpeedProperty = maxSpeedProperty, - dampDist = dampDist, - dampDistProperty = dampDistProperty - ) diff --git a/orx-delegate-magic/src/commonMain/kotlin/smoothing/PropertySmoother.kt b/orx-delegate-magic/src/commonMain/kotlin/smoothing/PropertySmoother.kt deleted file mode 100644 index b5c67533..00000000 --- a/orx-delegate-magic/src/commonMain/kotlin/smoothing/PropertySmoother.kt +++ /dev/null @@ -1,102 +0,0 @@ -@file:Suppress("PackageDirectoryMismatch") - -package org.openrndr.extra.delegatemagic.smoothing - -import org.openrndr.Clock -import org.openrndr.math.LinearType -import kotlin.math.pow -import kotlin.reflect.KProperty -import kotlin.reflect.KProperty0 - -class DoublePropertySmoother( - private val clock: Clock, - private val property: KProperty0, - private val factor: Double = 0.99, - private val factorProperty: KProperty0? -) { - private var output: Double? = null - private var lastTime: Double? = null - operator fun getValue(any: Any?, property: KProperty<*>): Double { - if (lastTime != null) { - val dt = clock.seconds - lastTime!! - if (dt > 1E-10) { - val steps = dt * 60.0 - val ef = (factorProperty?.get() ?: factor).pow(steps) - output = output!! * ef + this.property.get() * (1.0 - ef) - } - } else { - output = this.property.get() - } - lastTime = clock.seconds - return output ?: error("no value") - } -} - -class PropertySmoother>( - private val clock: Clock, - private val property: KProperty0, - private val factor: Double = 0.99, - private val factorProperty: KProperty0? -) { - private var output: T? = null - private var lastTime: Double? = null - operator fun getValue(any: Any?, property: KProperty<*>): T { - if (lastTime != null) { - val dt = clock.seconds - lastTime!! - if (dt > 1E-10) { - val steps = dt * 60.0 - val ef = (factorProperty?.get() ?: factor).pow(steps) - - val target = this.property.get() - output = output!! * ef + target * (1.0 - ef) - } - } else { - output = this.property.get() - } - lastTime = clock.seconds - return output ?: error("no value") - } -} - -/** - * Create a property smoother delegate - * @param property the property to smooth - * @param factor the smoothing factor - * @since 0.4.3 - */ -fun Clock.smoothing(property: KProperty0, factor: Double = 0.99): DoublePropertySmoother { - return DoublePropertySmoother(this, property, factor, null) -} - -/** - * Create a property smoother delegate - * @param property the property to smooth - * @param factor the smoothing factor property - * @since 0.4.3 - */ -fun Clock.smoothing( - property: KProperty0, - factor: KProperty0 -): DoublePropertySmoother { - return DoublePropertySmoother(this, property, 1E10, factor) -} - -/** - * Create a property smoother delegate - * @param property the property to smooth - * @param factor the smoothing factor - * @since 0.4.3 - */ -fun > Clock.smoothing(property: KProperty0, factor: Double = 0.99): PropertySmoother { - return PropertySmoother(this, property, factor, null) -} - -/** - * Create a property smoother delegate - * @param property the property to smooth - * @param factor the smoothing factor property - * @since 0.4.3 - */ -fun > Clock.smoothing(property: KProperty0, factor: KProperty0): PropertySmoother { - return PropertySmoother(this, property, 1E10, factor) -} \ No newline at end of file diff --git a/orx-delegate-magic/src/commonMain/kotlin/tracking/PropertyTracker.kt b/orx-delegate-magic/src/commonMain/kotlin/tracking/PropertyTracker.kt deleted file mode 100644 index 0c012c11..00000000 --- a/orx-delegate-magic/src/commonMain/kotlin/tracking/PropertyTracker.kt +++ /dev/null @@ -1,39 +0,0 @@ -@file:Suppress("PackageDirectoryMismatch") - -package org.openrndr.extra.delegatemagic.tracking - -import org.openrndr.Clock -import kotlin.reflect.KProperty -import kotlin.reflect.KProperty0 - -class PropertyTracker(private val clock: Clock, private val property: KProperty0, val length: Int = 30) { - private val track = mutableListOf() - private var lastTime: Double? = null - - operator fun getValue(any: Any?, property: KProperty<*>): List { - if (lastTime != null) { - val dt = clock.seconds - lastTime!! - if (dt > 1E-10) { - track.add(this.property.get()) - } - } else { - track.add(this.property.get()) - } - if (track.size > length) { - track.removeAt(0) - } - lastTime = clock.seconds - return track - } -} - -/** - * Create a property tracker - * @param property the property to track - * @param length the maximum length of the tracked history - * @return a property tracker - * @since 0.4.3 - */ -fun Clock.tracking(property: KProperty0, length: Int = 30): PropertyTracker { - return PropertyTracker(this, property, length) -} \ No newline at end of file diff --git a/orx-delegate-magic/src/jvmDemo/kotlin/DemoDifferencing01.kt b/orx-delegate-magic/src/jvmDemo/kotlin/DemoDifferencing01.kt deleted file mode 100644 index bd7b42dc..00000000 --- a/orx-delegate-magic/src/jvmDemo/kotlin/DemoDifferencing01.kt +++ /dev/null @@ -1,38 +0,0 @@ -import difference.differencing -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.delegatemagic.aggregation.aggregating -import org.openrndr.extra.delegatemagic.aggregation.maxMag -import org.openrndr.extra.delegatemagic.tracking.tracking -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 - -fun main() = application { - program { - val gui = GUI() - - val state = object { - @DoubleParameter("radius", 0.0, 200.0) - var radius = 100.0 - - val difference by differencing(::radius) - val differenceHistory by tracking(::difference) - val differenceMax by aggregating(::differenceHistory) { - it.maxMag() - } - } - - gui.add(state, "state") - - extend(gui) - extend { - drawer.circle(drawer.bounds.center, state.radius) - drawer.stroke = ColorRGBa.GREEN - drawer.lineSegment(drawer.bounds.center, drawer.bounds.center + Vector2(state.difference, 0.0)) - drawer.translate(0.0, 4.0) - drawer.stroke = ColorRGBa.BLUE - drawer.lineSegment(drawer.bounds.center, drawer.bounds.center + Vector2(state.differenceMax, 0.0)) - } - } -} diff --git a/orx-delegate-magic/src/jvmDemo/kotlin/DemoFollowing01.kt b/orx-delegate-magic/src/jvmDemo/kotlin/DemoFollowing01.kt deleted file mode 100644 index 63d93ac4..00000000 --- a/orx-delegate-magic/src/jvmDemo/kotlin/DemoFollowing01.kt +++ /dev/null @@ -1,59 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.delegatemagic.smoothing.following -import org.openrndr.extra.delegatemagic.smoothing.smoothing -import org.openrndr.math.Vector2 -import kotlin.random.Random - -/** - * Demonstrates using delegate-magic tools with - * [Double] and [Vector2]. - * - * The white circle's position uses [following]. - * The red circle's position uses [smoothing]. - * - * `following` uses physics (velocity and acceleration). - * `smoothing` eases values towards the target. - * - * Variables using delegates (`by`) interpolate - * toward target values, shown as gray lines. - * - * The behavior of the delegate-magic functions can be configured - * via arguments that affect their output. - * - * The arguments come in pairs of similar name: - * The first one, often of type [Double], is constant, - * The second one contains `Property` in its name and can be - * modified after its creation and even be linked to a UI - * to modify the behavior of the delegate function in real time. - * The `Property` argument overrides the other. - */ -fun main() = application { - program { - val target = object { - var pos = drawer.bounds.center - } - - val spos by smoothing(target::pos) - val fpos by following(target::pos) - - extend { - if (frameCount % 90 == 0) { - target.pos = Vector2( - Random.nextDouble(0.0, width.toDouble()), - Random.nextDouble(10.0, height.toDouble()) - ) - } - drawer.fill = ColorRGBa.WHITE - drawer.circle(fpos, 15.0) - - drawer.fill = ColorRGBa.RED - drawer.circle(spos, 10.0) - - drawer.fill = null - drawer.stroke = ColorRGBa.GRAY.opacify(0.5) - drawer.lineSegment(0.0, target.pos.y, width.toDouble(), target.pos.y) - drawer.lineSegment(target.pos.x, 0.0, target.pos.x, height.toDouble()) - } - } -} diff --git a/orx-delegate-magic/src/jvmDemo/kotlin/DemoSmoothing01.kt b/orx-delegate-magic/src/jvmDemo/kotlin/DemoSmoothing01.kt deleted file mode 100644 index 9708374f..00000000 --- a/orx-delegate-magic/src/jvmDemo/kotlin/DemoSmoothing01.kt +++ /dev/null @@ -1,29 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.delegatemagic.smoothing.smoothing -import kotlin.random.Random - -fun main() = application { - program { - val state = object { - var x = width / 2.0 - var y = height / 2.0 - var radius = 5.0 - } - - val sx by smoothing(state::x) - val sy by smoothing(state::y) - val sradius by smoothing(state::radius) - extend { - if (Random.nextDouble() < 0.01) { - state.radius = Random.nextDouble(10.0, 200.0) - } - if (Random.nextDouble() < 0.01) { - state.x = Random.nextDouble(0.0, width.toDouble()) - } - if (Random.nextDouble() < 0.01) { - state.y = Random.nextDouble(10.0, height.toDouble()) - } - drawer.circle(sx, sy, sradius) - } - } -} \ No newline at end of file diff --git a/orx-delegate-magic/src/jvmDemo/kotlin/DemoSpring01.kt b/orx-delegate-magic/src/jvmDemo/kotlin/DemoSpring01.kt deleted file mode 100644 index 533adddf..00000000 --- a/orx-delegate-magic/src/jvmDemo/kotlin/DemoSpring01.kt +++ /dev/null @@ -1,32 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.delegatemagic.dynamics.springForcing -import kotlin.random.Random - -fun main() = application { - program { - val state = object { - var x = width / 2.0 - var y = height / 2.0 - var radius = 5.0 - } - - val sx by springForcing(state::x, k = 10.0) - val sy by springForcing(state::y) - val sradius by springForcing(state::radius) - extend { - if (Random.nextDouble() < 0.01) { - state.radius = Random.nextDouble(10.0, 200.0) - } - - if (Random.nextDouble() < 0.01) { - state.x = Random.nextDouble(0.0, width.toDouble()) - } - - if (Random.nextDouble() < 0.01) { - state.y = Random.nextDouble(10.0, height.toDouble()) - } - - drawer.circle(sx, sy, sradius) - } - } -} \ No newline at end of file diff --git a/orx-depth-camera/build.gradle.kts b/orx-depth-camera/build.gradle.kts deleted file mode 100644 index e84da1f8..00000000 --- a/orx-depth-camera/build.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") -} - -kotlin { - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - implementation(openrndr.application.core) - implementation(openrndr.math) - } - } - } -} \ No newline at end of file diff --git a/orx-depth-camera/src/commonMain/kotlin/DepthCamera.kt b/orx-depth-camera/src/commonMain/kotlin/DepthCamera.kt deleted file mode 100644 index 07452b88..00000000 --- a/orx-depth-camera/src/commonMain/kotlin/DepthCamera.kt +++ /dev/null @@ -1,72 +0,0 @@ -package org.openrndr.extra.depth.camera - -import org.openrndr.draw.ColorBuffer -import org.openrndr.math.IntVector2 - -/** - * Defines how pixel values encoded in depth [ColorBuffer] will be interpreted. - */ -enum class DepthMeasurement { - - /** - * Raw values, but normalized to the range 0-1. - * Useful for debugging, because full range of captured values can be rendered - * as a texture. Therefore it's a default setting. - */ - RAW_NORMALIZED, - - /** - * Raw values, exactly as they are provided by the device. - * Note: it might imply that [ColorBuffer] of the depth camera frame - * is provided in integer-based format (for example in case of Kinect devices). - */ - RAW, - - /** - * Expressed in meters. - * It is using floating point numbers. - * Note: values above `1.0` will not be visible if displayed as a texture. - */ - METERS, - -} - -/** - * General API of any depth camera. - */ -interface DepthCamera { - - /** - * Current operating resolution. - */ - val resolution: IntVector2 - - /** - * The units/mapping in which depth is expressed on received frames. - */ - var depthMeasurement: DepthMeasurement - - /** - * Flips source depth data image in horizontal axis (mirror). - */ - var flipH: Boolean - - /** - * Flips source depth data image in vertical axis (upside-down). - */ - var flipV: Boolean - - /** - * The most recent frame received from the depth camera. - */ - val currentFrame: ColorBuffer - - /** - * Will execute the supplied block of code with each most recent frame - * from the depth camera as an input. - * - * @param block the code to execute when the new frame is received. - */ - fun onFrameReceived(block: (frame: ColorBuffer) -> Unit) - -} diff --git a/orx-easing/README.md b/orx-easing/README.md deleted file mode 100644 index 0c2ec173..00000000 --- a/orx-easing/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# orx-easing - -Easing functions for smooth animation or non-linear interpolation. - -Similar to those on https://easings.net - -| type | | -|:-----------|:-------------| -| linear | `easeLinear` | -| constant 0 | `easeZero` | -| constant 1 | `easeOne` | - -| type | in | in out | out | -|---------|----------------:|-------------------:|-----------------:| -| quad | `easeQuadIn` | `easeQuadInOut` | `easeQuadOut` | -| cubic | `easeCubicIn` | `easeCubicInOut` | `easeCubicOut` | -| quart | `easeQuartIn` | `easeQuartInOut` | `easeQuartOut` | -| quint | `easeQuintIn` | `easeQuintInOut` | `easeQuintOut` | -| circ | `easeCircIn` | `easeCircInOut` | `easeCircOut` | -| expo | `easeExpoIn` | `easeExpoInOut` | `easeExpoOut` | -| sine | `easeSineIn` | `easeSineInOut` | `easeSineOut` | -| back | `easeBackIn` | `easeBackInOut` | `easeBackOut` | -| bounce | `easeBounceIn` | `easeBounceInOut` | `easeBounceOut` | -| elastic | `easeElasticIn` | `easeElasticInOut` | `easeElasticOut` | - -## Usage - -```kotlin -fun easeX( - t: Double, // current time - b: Double = 0.0, // beginning (output value when t is 0.0) - c: Double = 1.0, // change (output delta) - d: Double = 1.0 // duration = end time -) -``` - -The most common usage involves repeatedly calling the easing function increasing -the `t` argument while keeping other arguments unchanged. When `t` increases from 0.0 up to `d`, the returned value slides from `b` to `b + c`. - -### Example - -For accelerating from 40.0 down to 10.0 in 10 steps: - -```kotlin -repeat(10) { - val y = easeQuadIn(it.toDouble(), 40.0, -30.0, 9.0) - println("$it -> $y") -} -``` - -Outputs - -``` -0 -> 40.0 -1 -> 39.629629629629626 -2 -> 38.51851851851852 -3 -> 36.666666666666664 -4 -> 34.074074074074076 -5 -> 30.74074074074074 -6 -> 26.666666666666668 -7 -> 21.85185185185185 -8 -> 16.2962962962963 -9 -> 10.0 -``` - -Note how most result values are closer to 40.0 than to 10.0, due to the usage of -an `In` easing function. `easeCubicIn`, `easeQuartIn` and `easeQuinticIn` functions would make this even more obvious. - -### Default arguments - -When `t` is in `[0, 1]` we can omit most arguments - -```kotlin -val e0 = easeQuadIn(t, 0.0, 1.0, 1.0) -val e1 = easeQuadIn(t) -``` - -### Using the `Easing` enumeration - -The `Easing` enum contains all easing functions. - -```kotlin -val et = Easing.QuadIn.function(t, 0.0, 1.0, 1.0) - -// list all easing function names -Easing.values().forEach { easing -> - println(easing.name) -} - -// find out how many easing functions are available -println(Easing.values().size) -``` - - -## Demos -### DemoEasings01 - -# Visualizes Easing types as a graph and as motion. - -[grid] is used to layout graphs on rows and columns. - - -![DemoEasings01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-easing/images/DemoEasings01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoEasings01.kt) diff --git a/orx-easing/build.gradle.kts b/orx-easing/build.gradle.kts deleted file mode 100644 index 9a82c702..00000000 --- a/orx-easing/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") - alias(libs.plugins.kotlin.serialization) -} - -kotlin { - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - implementation(project(":orx-parameters")) - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - implementation(sharedLibs.kotlin.serialization.core) - } - } - - @Suppress("UNUSED_VARIABLE") - val jvmDemo by getting { - dependencies { - implementation(project(":orx-shapes")) - } - } - } -} \ No newline at end of file diff --git a/orx-easing/src/commonMain/kotlin/Easing.kt b/orx-easing/src/commonMain/kotlin/Easing.kt deleted file mode 100644 index 56ef783c..00000000 --- a/orx-easing/src/commonMain/kotlin/Easing.kt +++ /dev/null @@ -1,347 +0,0 @@ -package org.openrndr.extra.easing - -import kotlinx.serialization.Serializable -import kotlin.math.* - -typealias EasingFunction = (Double, Double, Double, Double) -> Double - -/** - * # Easing function arguments - * - * @param t current Time - * @param b Beginning value - * @param c Change in value (the final value is `b+c`) - * @param d Duration (maximum time) - */ - -fun easeLinear(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0) = c * (t / d) + b - -// -- constant - -fun easeZero(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0) = b -fun easeOne(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0) = b + c - -// -- back - -fun easeBackIn(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val s = 1.70158 - val td = t / d - return c * (td) * td * ((s + 1) * td - s) + b -} - -fun easeBackInOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val s = 1.70158 * 1.525 - val s2 = s * 1.525 - val td2 = t / (d / 2) - val td22 = td2 - 2 - return if (td2 < 1) { - c / 2 * (td2 * td2 * ((s + 1) * td2 - s)) + b - } else { - c / 2 * ((td22) * td22 * (((s2) + 1) * td22 + s2) + 2) + b - } -} - -fun easeBackOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val s = 1.70158 - val td1 = t / d - 1 - return c * (td1 * td1 * ((s + 1) * td1 + s) + 1) + b -} - -// -- bounce - -fun easeBounceIn(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - var t1 = d - t - val result: Double - t1 /= d - if (t1 < 1 / 2.75) { - result = c * (7.5625 * t1 * t1) + 0.toDouble() - } else if (t1 < 2 / 2.75) { - t1 -= (1.5 / 2.75) - result = c * (7.5625 * (t1) * t1 + .75) - } else if (t1 < 2.5 / 2.75) { - t1 -= 2.25 / 2.75 - result = c * (7.5625 * (t1) * t1 + .9375) - } else { - t1 -= (2.625 / 2.75) - result = c * (7.5625 * (t1) * t1 + .984375) - } - return c - result + b -} - -fun easeBounceInOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - var t1 = d - t * 2 - val result: Double - t1 /= d - if (t1 < 1 / 2.75) { - result = c * (7.5625 * t1 * t1) + 0.toDouble() - } else if (t1 < 2 / 2.75) { - t1 -= 1.5 / 2.75 - result = c * (7.5625 * (t1) * t1 + .75) + 0.toDouble() - } else if (t1 < 2.5 / 2.75) { - t1 -= 2.25 / 2.75 - result = c * (7.5625 * (t1) * t1 + .9375) + 0.toDouble() - } else { - t1 -= 2.625 / 2.75 - result = c * (7.5625 * (t1) * t1 + .984375) + 0.toDouble() - //return c * (7.5625 * pow((t/d) -(2.625 / 2.75),2) + .984375) + b; - - } - var t2 = t * 2 - d - val result1: Double - t2 /= d - if (t2 < 1 / 2.75) result1 = c * (7.5625 * t2 * t2) + 0.toDouble() else if (t2 < 2 / 2.75) { - t2 -= 1.5 / 2.75 - result1 = c * (7.5625 * t2 * t2 + .75) - } else if (t2 < 2.5 / 2.75) { - t2 -= 2.25 / 2.75 - result1 = c * (7.5625 * t2 * t2 + .9375) - } else { - t2 -= 2.626 / 2.75 - result1 = c * (7.5625 * t2 * t2 + .984375) - } - return if (t < d / 2) - (c - result) * .5 + b - else - result1 * .5 + c * .5 + b - -} - -fun easeBounceOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - - var td = t / d - - return if (td < (1 / 2.75)) { - c * (7.5625 * td * td) + b - } else if (t < (2 / 2.75)) { - td -= 1.5 / 2.75 - c * (7.5625 * td * td + .75) + b - } else if (t < (2.5 / 2.75)) { - td -= 2.25 / 2.75 - c * (7.5625 * td * td + .9375) + b - } else { - td -= 2.625 / 2.75 - c * (7.5625 * td * td + .984375) + b - } -} - -// -- circ - -fun easeCircIn(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td = t / d - return -c * (sqrt(1 - td * td) - 1) + b -} - -fun easeCircInOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - var td2 = t / (d / 2.0) - if (td2 < 1) - return -c / 2 * (sqrt(1 - td2 * td2) - 1) + b - td2 -= 2 - return c / 2 * (sqrt(1 - td2 * td2) + 1) + b -} - -fun easeCircOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td = t / d - 1 - return c * sqrt(1 - td * td) + b -} - -// -- cubic - -fun easeCubicIn(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td = t / d - return c * td * td * td + b -} - -fun easeCubicOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td = t / d - 1.0 - return c * (td * td * td + 1) + b -} - -fun easeCubicInOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td = t / (d / 2) - val td2 = td - 2.0 - return if (td < 1) c / 2 * td * td * td + b else c / 2 * (td2 * td2 * td2 + 2) + b -} - -// -- elastic - -fun easeElasticIn(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - if (t == 0.0) { - return b - } else if (t / d == 1.0) { - return b + c - } else { - var td = t / d - val p = d * .3 - val s = p / 4 - td -= 1.0 - return -(c * 2.0.pow(10 * (td)) * sin((td * d - s) * (2 * PI) / p)) + b - } -} - -fun easeElasticInOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td2 = t / (d / 2) - - if (t == 0.0) - return b - if (td2 == 2.0) - return b + c - val p = d * (.3 * 1.5) - val s = p / 4 - val td3 = td2 - 1.0 - return if (td2 < 1) { - -.5 * (c * 2.0.pow(10 * (td3)) * sin((td3 - s) * (2 * PI) / p)) + b - } else { - c * 2.0.pow(-10 * (td3) * sin(td3 - s) * (2 * PI) / p) * .5 + c + b - } - -} - -fun easeElasticOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td = t / d - if (t == 0.0) - return b - if (td == 1.0) - return b + c - val p = d * .3 - val s = p / 4 - return c * 2.0.pow(-10 * td) * sin((td * d - s) * (2 * PI) / p) + c + b -} - -// -- expo - -fun easeExpoIn(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double = - if (t == 0.0) b else c * 2.0.pow(10 * (t / d - 1)) + b - -fun easeExpoInOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td2 = t / (d / 2) - val t2 = t * 2 - return if (t == 0.0) { - b - } else if (t == d) { - b + c - } else if (t < d / 2) { - (c / 2) * 2.0.pow(10 * (t2 - 1)) + b - } else { - - (c / 2) * (-(2.0.pow(-10 * (t2 - 1.0))) + 2) + b - } -} - -fun easeExpoOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double = - if (t == d) b + c else c * (-(2.0.pow(-10 * t / d)) + 1) + b - -// -- quad - -fun easeQuadIn(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double = c * (t / d) * (t / d) + b - -fun easeQuadInOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td = t / (d / 2) - return if (td < 1) { - c / 2 * td * td + b - } else { - -c / 2 * ((td - 1) * (td - 3) - 1) + b - } -} - -fun easeQuadOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double = -c * (t / d) * (t / d - 2) + b - -// -- quart - -fun easeQuartIn(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val n = t / d - return c * n * n * n * n + b -} - -fun easeQuartInOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td = t / (d / 2) - val td2 = td - 2.0 - return if (td < 1) c / 2 * td * td * td * td + b else -c / 2 * (td2 * td2 * td2 * td2 - 2) + b -} - -fun easeQuartOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td = t / d - 1 - return -c * (td * td * td * td - 1) + b -} - -// -- quint - -fun easeQuintIn(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td = t / d - return c * td * td * td * td * td + b -} - -fun easeQuintInOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td = t / (d / 2) - val td2 = td - 2.0 - return if (td < 1) c / 2 * td * td * td * td * td + b else c / 2 * (td2 * td2 * td2 * td2 * td2 + 2) + b -} - -fun easeQuintOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double { - val td = t / d - 1 - return c * ((td) * td * td * td * td + 1) + b -} - -// -- sine - -fun easeSineIn(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double = - -c * cos(t / d * (PI / 2)) + c + b - -fun easeSineOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double = - c * sin(t / d * (PI / 2)) + b - -fun easeSineInOut(t: Double, b: Double = 0.0, c: Double = 1.0, d: Double = 1.0): Double = - -c / 2 * (cos(PI * t / d) - 1) + b - -/** - * Enum containing all easing functions - * - * Use the `Easing.values()` list to iterate over available functions, - * query its `.size` property or get functions by index. - */ -@Serializable -enum class Easing(val function: EasingFunction) { - Linear(::easeLinear), - - Zero(::easeZero), - One(::easeOne), - - BackIn(::easeBackIn), - BackInOut(::easeBackInOut), - BackOut(::easeBackOut), - - BounceIn(::easeBounceIn), - BounceInOut(::easeBounceInOut), - BounceOut(::easeBounceOut), - - CircIn(::easeCircIn), - CircInOut(::easeCircInOut), - CircOut(::easeCircOut), - - CubicIn(::easeCubicIn), - CubicInOut(::easeCubicInOut), - CubicOut(::easeCubicOut), - - ElasticIn(::easeElasticIn), - ElasticInOut(::easeElasticInOut), - ElasticOut(::easeElasticOut), - - ExpoIn(::easeExpoIn), - ExpoInOut(::easeExpoInOut), - ExpoOut(::easeExpoOut), - - QuadIn(::easeQuadIn), - QuadInOut(::easeQuadInOut), - QuadOut(::easeQuadOut), - - QuartIn(::easeQuartIn), - QuartInOut(::easeQuartInOut), - QuartOut(::easeQuartOut), - - QuintIn(::easeQuintIn), - QuintInOut(::easeQuintInOut), - QuintOut(::easeQuintOut), - - SineIn(::easeSineIn), - SineInOut(::easeSineInOut), - SineOut(::easeSineOut), -} diff --git a/orx-easing/src/jvmDemo/kotlin/DemoEasings01.kt b/orx-easing/src/jvmDemo/kotlin/DemoEasings01.kt deleted file mode 100644 index fc0e3a45..00000000 --- a/orx-easing/src/jvmDemo/kotlin/DemoEasings01.kt +++ /dev/null @@ -1,78 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.loadFont -import org.openrndr.extra.easing.Easing -import org.openrndr.extra.shapes.primitives.grid -import org.openrndr.math.Vector2 -import org.openrndr.math.map - -/** - * # Visualizes Easing types as a graph and as motion. - * - * [grid] is used to layout graphs on rows and columns. - * - */ -fun main() = application { - configure { - width = 1280 - height = 1080 - } - program { - val font = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 20.0) - - // grid `columns * rows` must be >= Easing.values().size - val grid = drawer.bounds.grid( - 3, 11, 10.0, 10.0, 10.0, 10.0 - ).flatten() - - // make pairs of (easing function, grid rectangle) - val pairs = Easing.entries.toTypedArray() zip grid - - extend { - // ~4 seconds animation loop - val animT = (frameCount % 240) / 60.0 - - pairs.forEach { (easing, gridRect) -> - - // background rectangle - drawer.stroke = null - drawer.fill = ColorRGBa.WHITE.opacify(0.3) - drawer.rectangle(gridRect) - - // graph - drawer.stroke = ColorRGBa.PINK - val points = List(40) { - val curveT = it / 39.0 - gridRect.position( - curveT, easing.function(curveT, 1.0, -1.0, 1.0) - ) - } - drawer.lineStrip(points) - - // label - drawer.fill = ColorRGBa.WHITE - drawer.stroke = null - drawer.fontMap = font - drawer.text( - easing.name, - // text position rounded for crisp font rendering - gridRect.position(0.02, 0.25).toInt().vector2 - ) - - // animation - drawer.fill = ColorRGBa.WHITE.opacify( - when { // 4-stage opacity - animT > 3.0 -> 0.0 // invisible - animT > 2.0 -> 3.0 - animT // fade-out - animT < 1.0 -> animT // fade-in - else -> 1.0 // visible - } - ) - // move only while visible (when loop time in 1.0..2.0) - val t = animT.map(1.0, 2.0, 0.0, 1.0, true) - val xy = Vector2(1.0, easing.function(t, 1.0, -1.0, 1.0)) - drawer.circle(gridRect.position(xy), 5.0) - } - } - } -} diff --git a/orx-envelopes/README.md b/orx-envelopes/README.md deleted file mode 100644 index c271f1df..00000000 --- a/orx-envelopes/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# orx-envelopes - -ADSR (Attack, Decay, Sustain, Release) envelopes and tools. - -## ADSR - -Attack, decay, sustain, release - -## Demos -### DemoADSRTracker01 - - - -![DemoADSRTracker01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-envelopes/images/DemoADSRTracker01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoADSRTracker01.kt) - -### DemoADSRTracker02 - - - -![DemoADSRTracker02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-envelopes/images/DemoADSRTracker02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoADSRTracker02.kt) diff --git a/orx-envelopes/build.gradle.kts b/orx-envelopes/build.gradle.kts deleted file mode 100644 index 30d05825..00000000 --- a/orx-envelopes/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") -} - -kotlin { - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - implementation(project(":orx-parameters")) - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - } - } - - @Suppress("UNUSED_VARIABLE") - val jvmDemo by getting { - dependencies { - implementation(project(":orx-envelopes")) - implementation(project(":orx-noise")) - } - } - } -} \ No newline at end of file diff --git a/orx-envelopes/src/commonMain/kotlin/ADSR.kt b/orx-envelopes/src/commonMain/kotlin/ADSR.kt deleted file mode 100644 index f84fcbad..00000000 --- a/orx-envelopes/src/commonMain/kotlin/ADSR.kt +++ /dev/null @@ -1,50 +0,0 @@ -package org.openrndr.extra.envelopes - -import org.openrndr.math.mix -import kotlin.math.min - -data class ADSR( - val attackDuration: Double, - val decayDuration: Double, - val sustainValue: Double, - val releaseDuration: Double -) : Envelope() { - override fun value(t: Double, tOff: Double): Double { - return adsr(attackDuration, decayDuration, sustainValue, releaseDuration, t, tOff) - } - - override fun position(t: Double, tOff: Double): Double { - return adsrPosition(attackDuration, decayDuration, releaseDuration, t, tOff) - } - - override fun isActive(t: Double, tOff: Double): Boolean { - return !(t - tOff > releaseDuration) - } -} - -fun adsr( - attackDuration: Double, - decayDuration: Double, - sustainValue: Double, - releaseDuration: Double, - t: Double, - tOff: Double = 1E10 -): Double { - val da = t / attackDuration - val dc = (t - attackDuration) / decayDuration - val vOn = mix(min(1.0, da), sustainValue, dc.coerceIn(0.0..1.0)) - return mix(vOn, 0.0, ((t - tOff) / releaseDuration).coerceIn(0.0..1.0)) -} - -fun adsrPosition( - attackDuration: Double, - decayDuration: Double, - releaseDuration: Double, - t: Double, - tOff: Double -): Double { - val ta = (t / attackDuration).coerceIn(0.0..1.0) - val td = ((t - attackDuration) / decayDuration).coerceIn(0.0..1.0) - val tr = ((t - tOff) / releaseDuration).coerceIn(0.0..1.0) - return (ta + td + tr) / 3.0 -} \ No newline at end of file diff --git a/orx-envelopes/src/commonMain/kotlin/Envelope.kt b/orx-envelopes/src/commonMain/kotlin/Envelope.kt deleted file mode 100644 index c4c0c6a2..00000000 --- a/orx-envelopes/src/commonMain/kotlin/Envelope.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.openrndr.extra.envelopes - -abstract class Envelope { - abstract fun value(t: Double, tOff: Double): Double - - abstract fun position(t: Double, tOff: Double): Double - - abstract fun isActive(t: Double, tOff: Double): Boolean - - var objectFunction: ((time: Double, value: Double, position: Double) -> Unit) = { _, _, _ -> } -} \ No newline at end of file diff --git a/orx-envelopes/src/commonMain/kotlin/MPPSynchronize.kt b/orx-envelopes/src/commonMain/kotlin/MPPSynchronize.kt deleted file mode 100644 index 0a5f0b57..00000000 --- a/orx-envelopes/src/commonMain/kotlin/MPPSynchronize.kt +++ /dev/null @@ -1,3 +0,0 @@ -package org.openrndr.extra.envelopes - -expect fun mppSynchronized(lock:Any, f:()->V) : V \ No newline at end of file diff --git a/orx-envelopes/src/commonMain/kotlin/Tracker.kt b/orx-envelopes/src/commonMain/kotlin/Tracker.kt deleted file mode 100644 index a650a585..00000000 --- a/orx-envelopes/src/commonMain/kotlin/Tracker.kt +++ /dev/null @@ -1,108 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.envelopes - -import org.openrndr.Clock -import org.openrndr.extra.parameters.DoubleParameter - -class Trigger(val id: Int, val on: Double, var off: Double, val envelope: Envelope) - -class TrackerValue( - val time: Double, - val value: Double, - val position: Double, - val envelope: Envelope -) { - operator fun invoke() { - draw() - } - - fun draw() { - envelope.objectFunction(time, value, position) - } -} - -abstract class Tracker(val clock: Clock) { - val triggers = mutableListOf() - - protected abstract fun createEnvelope(objectFunction: (time: Double, value: Double, position: Double) -> Unit): T - - fun triggerOn( - triggerId: Int = 0, - objectFunction: (time: Double, value: Double, position: Double) -> Unit = { _, _, _ -> } - ) { - mppSynchronized(triggers) { - val t = clock.seconds - triggers.removeAll { !it.envelope.isActive(t - it.on, it.off - it.on) } - triggers.add(Trigger(triggerId, clock.seconds, 1E30, createEnvelope(objectFunction))) - } - } - - fun triggerOff(triggerId: Int = 0) { - mppSynchronized(triggers) { - val t = clock.seconds - triggers.removeAll { !it.envelope.isActive(t - it.on, it.off - it.on) } - triggers.findLast { it.id == triggerId }?.let { - it.off = clock.seconds - } - } - } - - fun values(): List { - val t = clock.seconds - return mppSynchronized(triggers) { - triggers.mapNotNull { - val tOn = t - it.on - val tOff = it.off - it.on - - if (it.envelope.isActive(tOn, tOff)) { - val v = it.envelope.value(tOn, tOff) - TrackerValue(t, v, it.envelope.position(tOn, tOff), it.envelope) - } else { - null - } - } - } - } - - fun value(): Double { - return values().sumOf { it.value } - } -} - - -class ADSRTracker(clock: Clock) : Tracker(clock) { - - /** - * The time it takes to transition to 1.0 when calling [triggerOn], usually in seconds. - */ - @DoubleParameter("attack", 0.0, 20.0, order = 1) - var attack: Double = 0.1 - - /** - * The time it takes to transition from 1.0 to the [sustain] level, usually in seconds. - * The decay happens immediately after the attack. - */ - @DoubleParameter("decay", 0.0, 20.0, order = 2) - var decay: Double = 0.1 - - /** - * The sustain level, between 0.0 and 1.0. - * The tracker will keep this value until [triggerOff] is called. - */ - @DoubleParameter("sustain", 0.0, 1.0, order = 3) - var sustain: Double = 0.9 - - /** - * The time it takes to transition back to 0.0 when calling [triggerOff], usually in seconds. - */ - @DoubleParameter("release", 0.0, 20.0, order = 4) - var release: Double = 0.9 - - override fun createEnvelope(objectFunction: (time: Double, value: Double, position: Double) -> Unit): ADSR { - return ADSR(attack, decay, sustain, release).apply { - this.objectFunction = objectFunction - } - } -} - diff --git a/orx-envelopes/src/jsMain/kotlin/MPPSynchronize.kt b/orx-envelopes/src/jsMain/kotlin/MPPSynchronize.kt deleted file mode 100644 index 23bf70ac..00000000 --- a/orx-envelopes/src/jsMain/kotlin/MPPSynchronize.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.openrndr.extra.envelopes - -actual fun mppSynchronized(lock: Any, f: () -> V): V { - return f() -} \ No newline at end of file diff --git a/orx-envelopes/src/jvmDemo/kotlin/DemoADSRTracker01.kt b/orx-envelopes/src/jvmDemo/kotlin/DemoADSRTracker01.kt deleted file mode 100644 index 8245075f..00000000 --- a/orx-envelopes/src/jvmDemo/kotlin/DemoADSRTracker01.kt +++ /dev/null @@ -1,33 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.loadFont -import org.openrndr.extra.envelopes.ADSRTracker - -fun main() = application { - program { - val tracker = ADSRTracker(this) - tracker.attack = 1.0 - tracker.decay = 0.2 - tracker.sustain = 0.8 - tracker.release = 2.0 - - keyboard.keyDown.listen { - if (it.name == "t") - tracker.triggerOn() - } - keyboard.keyUp.listen { - if (it.name == "t") - tracker.triggerOff() - } - extend { - tracker.values().forEach { - drawer.circle(40.0, 40.0, 20.0 * it.value) - drawer.translate(40.0, 0.0) - } - drawer.defaults() - drawer.circle(drawer.bounds.center, 100.0 * tracker.value()) - - drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 16.0) - drawer.text("press and hold 't'", 20.0, height - 20.0) - } - } -} diff --git a/orx-envelopes/src/jvmDemo/kotlin/DemoADSRTracker02.kt b/orx-envelopes/src/jvmDemo/kotlin/DemoADSRTracker02.kt deleted file mode 100644 index 3711a70a..00000000 --- a/orx-envelopes/src/jvmDemo/kotlin/DemoADSRTracker02.kt +++ /dev/null @@ -1,44 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.loadFont -import org.openrndr.extra.envelopes.ADSRTracker -import org.openrndr.extra.noise.shapes.uniform -import org.openrndr.shape.Rectangle - -fun main() = application { - program { - val tracker = ADSRTracker(this) - tracker.attack = 1.0 - tracker.decay = 0.2 - tracker.sustain = 0.8 - tracker.release = 2.0 - - keyboard.keyDown.listen { - if (it.name == "t") { - val center = drawer.bounds.offsetEdges(-30.0).uniform() - tracker.triggerOn(0) { time, value, position -> - drawer.circle(center, value * 100.0) - } - } - if (it.name == "r") { - val center = drawer.bounds.offsetEdges(-30.0).uniform() - tracker.triggerOn(1) { time, value, position -> - val r = Rectangle.fromCenter(center, width = value * 100.0, height = value * 100.0) - drawer.rectangle(r) - } - } - } - keyboard.keyUp.listen { - if (it.name == "t") - tracker.triggerOff(0) - if (it.name == "r") - tracker.triggerOff(1) - } - extend { - tracker.values().forEach { - it() - } - drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 16.0) - drawer.text("press and hold 't' and/or 'r'", 20.0, height - 20.0) - } - } -} diff --git a/orx-envelopes/src/jvmMain/kotlin/MPPSynchronize.kt b/orx-envelopes/src/jvmMain/kotlin/MPPSynchronize.kt deleted file mode 100644 index b23742f3..00000000 --- a/orx-envelopes/src/jvmMain/kotlin/MPPSynchronize.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.openrndr.extra.envelopes - -actual fun mppSynchronized(lock: Any, f: () -> V): V { - return synchronized(lock) { - f() - } -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/README.md b/orx-expression-evaluator-typed/README.md deleted file mode 100644 index 83024e8c..00000000 --- a/orx-expression-evaluator-typed/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# orx-expression-evaluator-typed - -Tools to evaluate strings containing typed mathematical expressions. - -# Expression evaluator - -Supported types: - * `Double` - * `String` - * `Vector2` - * `Vector3` - * `Vector4` - * `ColorRGBa` \ No newline at end of file diff --git a/orx-expression-evaluator-typed/build.gradle.kts b/orx-expression-evaluator-typed/build.gradle.kts deleted file mode 100644 index 0ed23705..00000000 --- a/orx-expression-evaluator-typed/build.gradle.kts +++ /dev/null @@ -1,24 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") -} - -kotlin { - sourceSets { - val commonMain by getting { - dependencies { - implementation(libs.antlr.kotlin.runtime) - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(sharedLibs.kotlin.coroutines) - implementation(project(":orx-property-watchers")) - implementation(project(":orx-noise")) - implementation(project(":orx-expression-evaluator")) - } - } - val jvmDemo by getting { - dependencies { - implementation(project(":orx-jvm:orx-gui")) - } - } - } -} diff --git a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/CompiledFunctions.kt b/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/CompiledFunctions.kt deleted file mode 100644 index ed888b56..00000000 --- a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/CompiledFunctions.kt +++ /dev/null @@ -1,268 +0,0 @@ -package org.openrndr.extra.expressions.typed - -import org.antlr.v4.kotlinruntime.tree.ParseTreeWalker - - -/** - * Compiles a string expression into a single-parameter function capable of evaluating the expression. - * - * @param T0 The type of the single parameter that will be passed to the compiled function. - * @param R The return type of the compiled function. - * @param expression The string representation of the expression to be compiled. - * @param parameter0 The name of the required parameter in the expression. - * @param constants A lambda function to resolve constants in the expression by their names. Defaults to a function returning null for all names. - * @param functions A set of function extensions that can be used within the expression. Defaults to an empty set. - * @return A single-parameter function that takes an argument of type [T0] and evaluates the expression to return a result of type [R]. - */ -fun compileFunction1( - expression: String, - parameter0: String, - constants: (String) -> Any? = { null }, - functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY -): ((T0) -> R) { -// require(constants(parameter0) == null) { -// "${parameter0} is in constants with value '${constants(parameter0)}" -// } - val root = expressionRoot(expression) - - var varP0: T0? = null - val constantValues = fun(p: String): Any? { - return if (p == parameter0) { - varP0 - } else { - constants(p) - } - } - val listener = TypedExpressionListener(functions, constantValues) - - return { p0 -> - varP0 = p0 - try { - ParseTreeWalker.DEFAULT.walk(listener, root) - } catch(e: Throwable) { - throw RuntimeException("Error while evaluating '$expression' with parameter $parameter0=$p0. ${e.message}", e) - } - @Suppress("UNCHECKED_CAST") - listener.state.lastExpressionResult as? R ?: error("No result while evaluating '$expression' with parameter $parameter0=$p0") - } -} - -/** - * Tries to compile a given string expression into a single-parameter function. Returns the compiled function if successful, - * or `null` if an error occurs during compilation. Errors are handled using the provided [onError] callback. - * - * @param T0 The type of the single parameter that will be passed to the compiled function. - * @param R The return type of the compiled function. - * @param expression The string representation of the expression to be compiled. - * @param parameter0 The name of the required parameter in the expression. - * @param constants A lambda function to resolve constants in the expression by their names. Defaults to a function returning null for all names. - * @param functions A set of function extensions that can be used within the expression. Defaults to an empty set. - * @param onError A callback function that will be invoked when an error occurs during compilation. - * The error is passed to this function. - * @return A single-parameter function that takes an argument of type [T0] and evaluates the expression to return a result of type [R], - * or `null` if the compilation fails. - */ -fun compileFunction1OrNull( - expression: String, - parameter0: String, - constants: (String) -> Any? = { null }, - functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY, - onError: (Throwable) -> Unit = { } -): ((T0) -> R)? { - try { - return compileFunction1(expression, parameter0, constants, functions) - } catch (e: Throwable) { - onError(e) - return null - } -} - -/** - * Compiles a string expression into a lambda function with two parameters. - * - * This function takes an expression as a string, as well as two parameter names, - * and returns a lambda that evaluates the expression with the provided parameter values. - * Optionally, a map of constants and custom functions can be provided for use in the expression. - * - * @param T0 The type of the first parameter. - * @param T1 The type of the second parameter. - * @param R The return type of the resulting lambda function. - * @param expression The string expression to compile. - * @param parameter0 The name of the first parameter in the expression. - * @param parameter1 The name of the second parameter in the expression. - * @param constants A lambda function that provides constant values by variable name. Defaults to returning null for all names. - * @param functions The custom functions available in the context of the expression. Defaults to an empty set of functions. - * @return A lambda function that takes two parameters of types T0 and T1 and returns a result of type R, - * based on the compiled expression. - * @throws IllegalArgumentException if either parameter name exists within the constants map. - */ -fun compileFunction2( - expression: String, - parameter0: String, - parameter1: String, - constants: (String) -> Any? = { null }, - functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY -): ((T0, T1) -> R) { - require(constants(parameter0) == null) { - "${parameter0} is in constants with value '${constants(parameter0)}" - } - require(constants(parameter1) == null) { - "${parameter1} is in constants with value '${constants(parameter1)}" - } - - val root = expressionRoot(expression) - - var varP0: T0? = null - var varP1: T1? = null - val constantValues = fun(p: String): Any? { - return if (p == parameter0) { - varP0 - } else if (p == parameter1) { - varP1 - } else { - constants(p) - } - } - val listener = TypedExpressionListener(functions, constantValues) - - return { p0, p1 -> - varP0 = p0 - varP1 = p1 - ParseTreeWalker.DEFAULT.walk(listener, root) - @Suppress("UNCHECKED_CAST") - listener.state.lastExpressionResult as? R ?: error("no result") - } -} - -/** - * Attempts to compile a string expression into a lambda function with two parameters, - * returning null if an error occurs during compilation. - * - * This function is a safe wrapper around `compileFunction2`, catching any exceptions - * that may be thrown during the compilation process. If an error occurs, the provided - * `onError` callback is invoked with the exception, and the function returns null. - * - * @param T0 The type of the first parameter. - * @param T1 The type of the second parameter. - * @param R The return type of the resulting lambda function. - * @param expression The string expression to compile. - * @param parameter0 The name of the first parameter in the expression. - * @param parameter1 The name of the second parameter in the expression. - * @param constants A lambda function that provides constant values for variables by name. Defaults to returning null for all names. - * @param functions The custom functions available in the context of the expression. Defaults to an empty set of functions. - * @param onError A callback invoked with the exception if an error occurs during compilation. Defaults to an empty function. - * @return A lambda function that takes two parameters of types T0 and T1 and returns a result of type R if the compilation is successful. - * Returns null if an error occurs during compilation. - */ -fun compileFunction2OrNull( - expression: String, - parameter0: String, - parameter1: String, - constants: (String) -> Any? = { null }, - functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY, - onError: (Throwable) -> Unit = { } -): ((T0, T1) -> R)? { - try { - return compileFunction2(expression, parameter0, parameter1, constants, functions) - } catch (e: Throwable) { - onError(e) - return null - } -} - -/** - * Compiles a 3-parameter function from a given string expression, allowing dynamic evaluation with specified parameters, constants, and external function extensions. - * - * @param T0 The type of the first parameter. - * @param T1 The type of the second parameter. - * @param T2 The type of the third parameter. - * @param R The return type of the compiled function. - * @param expression The string representation of the expression to be compiled. - * @param parameter0 The name of the first parameter referenced in the expression. - * @param parameter1 The name of the second parameter referenced in the expression. - * @param parameter2 The name of the third parameter referenced in the expression. - * @param constants A lambda function providing constant values for variable names used in the expression. Defaults to a function returning null. - * @param functions An optional container of external functions that can be called within the expression. Defaults to an empty set of functions. - * @return A lambda function that takes three parameters of types T0, T1, and T2, and returns a result of type R after evaluating the compiled expression. - * @throws IllegalArgumentException If any of the parameter names are found in the constants map. - * @throws ExpressionException If there is a syntax error in the provided expression. - */ -fun compileFunction3( - expression: String, - parameter0: String, - parameter1: String, - parameter2: String, - constants: (String) -> Any? = { null }, - functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY -): ((T0, T1, T2) -> R) { - require(constants(parameter0) == null) { - "${parameter0} is in constants with value '${constants(parameter0)}" - } - require(constants(parameter1) == null) { - "${parameter1} is in constants with value '${constants(parameter1)}" - } - require(constants(parameter2) == null) { - "${parameter2} is in constants with value '${constants(parameter2)}" - } - - val root = expressionRoot(expression) - - var varP0: T0? = null - var varP1: T1? = null - var varP2: T2? = null - val constantValues = fun(p: String): Any? { - return if (p == parameter0) { - varP0 - } else if (p == parameter1) { - varP1 - } else if (p == parameter2) { - varP2 - } else { - constants(p) - } - } - val listener = TypedExpressionListener(functions, constantValues) - - return { p0, p1, p2 -> - varP0 = p0 - varP1 = p1 - varP2 = p2 - ParseTreeWalker.DEFAULT.walk(listener, root) - @Suppress("UNCHECKED_CAST") - listener.state.lastExpressionResult as? R ?: error("no result") - } -} - -/** - * Compiles a 3-parameter function from a given string expression, returning null if an error occurs during compilation. - * - * @param T0 The type of the first parameter. - * @param T1 The type of the second parameter. - * @param T2 The type of the third parameter. - * @param R The return type of the compiled function. - * @param expression The string representation of the expression to be compiled. - * @param parameter0 The name of the first parameter referenced in the expression. - * @param parameter1 The name of the second parameter referenced in the expression. - * @param parameter2 The name of the third parameter referenced in the expression. - * @param constants A lambda function providing constant values for variable names used in the expression. Defaults to a function returning null. - * @param functions An optional container of external functions that can be called within the expression. Defaults to an empty set of functions. - * @param onError A lambda function that will be invoked with the exception if an error occurs during compilation. Defaults to an empty function. - * @return A lambda function that takes three parameters of types T0, T1, and T2, and returns a result of type R after evaluating the compiled expression, or null if an error occurs - * . - */ -fun compileFunction3OrNull( - expression: String, - parameter0: String, - parameter1: String, - parameter2: String, - constants: (String) -> Any? = { null }, - functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY, - onError: (Throwable) -> Unit = { } -): ((T0, T1, T2) -> R)? { - try { - return compileFunction3(expression, parameter0, parameter1, parameter2, constants, functions) - } catch (e: Throwable) { - onError(e) - return null - } -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function0.kt b/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function0.kt deleted file mode 100644 index 283379a3..00000000 --- a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function0.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.openrndr.extra.expressions.typed - -import org.openrndr.extra.noise.uniform - -/** - * Dispatches a function without arguments based on its name. - * - * @param name The name of the function to dispatch. - * @param functions A map containing functions of type `TypedFunction0` associated with their names. - * @return A callable lambda that takes an array of `Any` as input and returns a result if the function is found, - * or null if there is no match. - */ -internal fun dispatchFunction0(name: String, functions: Map): ((Array) -> Any)? { - return when (name) { - "random" -> { x -> Double.uniform(0.0, 1.0) } - else -> functions[name]?.let { { x: Array -> it.invoke() } } - } -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function1.kt b/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function1.kt deleted file mode 100644 index 162f0409..00000000 --- a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function1.kt +++ /dev/null @@ -1,210 +0,0 @@ -package org.openrndr.extra.expressions.typed - -import org.openrndr.color.ColorRGBa -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 -import org.openrndr.math.transforms.scale -import org.openrndr.math.transforms.translate -import kotlin.math.abs as abs_ -import kotlin.math.cos as cos_ -import kotlin.math.sin as sin_ -import kotlin.math.sqrt as sqrt_ - -internal fun vec2(x: Any): Vector2 { - return when (x) { - is Double -> Vector2(x, x) - is List<*> -> { - when (x.size) { - 2 -> { - @Suppress("UNCHECKED_CAST") - x as List; Vector2(x[0], x[1]) - } - - else -> error("vec2(): unsupported argument: '$x'") - } - } - - else -> error("vec2(): unsupported argument: '$x'") - } -} - -internal fun vec3(x: Any): Vector3 { - return when (x) { - is Double -> Vector3(x, x, x) - is List<*> -> { - when (x.size) { - 2 -> { - vec3(x[0]!!, x[1]!!) - } - - 3 -> { - vec3(x[0]!!, x[1]!!, x[2]!!) - } - - else -> error("vec3(): unsupported argument: '$x'") - } - } - - else -> error("vec3(): unsupported argument: '$x'") - } -} - - -internal fun vec4(x: Any): Vector4 { - return when (x) { - is Double -> Vector4(x, x, x, x) - is List<*> -> { - when (x.size) { - 2 -> { - vec4(x[0]!!, x[1]!!) - } - - 3 -> { - vec4(x[0]!!, x[1]!!, x[2]!!) - } - - 4 -> { - vec4(x[0]!!, x[1]!!, x[2]!!, x[3]!!) - } - - else -> error("vec4(): unsupported argument: '$x'") - } - } - - else -> error("vec4(): unsupported argument: '$x'") - } -} - -internal fun rgba(x: Any): ColorRGBa { - return when (x) { - is Double -> ColorRGBa(x, x, x, 1.0) - is Vector3 -> ColorRGBa(x.x, x.y, x.z, 1.0) - is Vector4 -> ColorRGBa(x.x, x.y, x.z, x.w) - else -> error("type not supported ${x::class.simpleName}") - } -} - -internal fun cos(x: Any): Any { - return when (x) { - is Double -> cos_(x) - is Vector2 -> x.map { cos_(it) } - is Vector3 -> x.map { cos_(it) } - is Vector4 -> x.map { cos_(it) } - else -> error("type not supported ${x::class.simpleName}") - } -} - -internal fun sin(x: Any): Any { - return when (x) { - is Double -> sin_(x) - is Vector2 -> x.map { sin_(it) } - is Vector3 -> x.map { sin_(it) } - is Vector4 -> x.map { sin_(it) } - else -> error("type not supported ${x::class.simpleName}") - } -} - -internal fun normalize(x: Any): Any { - return when (x) { - is Vector2 -> x.normalized - is Vector3 -> x.normalized - is Vector4 -> x.normalized - else -> error("type not supported ${x::class.simpleName}") - } -} - -internal fun inverse(x: Any): Any { - return when (x) { - is Matrix44 -> x.inversed - else -> error("type not supported ${x::class.simpleName}") - } -} - -internal fun transpose(x: Any): Any { - return when (x) { - is Matrix44 -> x.transposed - else -> error("type not supported ${x::class.simpleName}") - } -} - - -fun abs(x: Any): Any { - return when (x) { - is Double -> abs_(x) - is Vector2 -> x.map { abs_(it) } - is Vector3 -> x.map { abs_(it) } - is Vector4 -> x.map { abs_(it) } - else -> error("type not supported ${x::class.simpleName}") - } -} - -internal fun scale(scale: Any): Matrix44 { - @Suppress("NAME_SHADOWING") val scale = when (scale) { - is Double -> Vector3(scale, scale, scale) - is Vector2 -> scale.xy1 - is Vector3 -> scale - else -> error("unsupported axis argument") - } - return Matrix44.scale(scale) -} - - -internal fun sqrt(x: Any): Any { - return when (x) { - is Double -> sqrt_(x) - is Vector2 -> x.map { sqrt_(it) } - is Vector3 -> x.map { sqrt_(it) } - is Vector4 -> x.map { sqrt_(it) } - else -> error("type not supported ${x::class.simpleName}") - } -} - -internal fun translate(translation: Any): Matrix44 { - @Suppress("NAME_SHADOWING") val translation = when (translation) { - is Vector2 -> translation.xy0 - is Vector3 -> translation - else -> error("unsupported axis argument") - } - return Matrix44.translate(translation) -} - -internal fun mat4(x: Any): Matrix44 { - return when (x) { - is List<*> -> { - @Suppress("UNCHECKED_CAST") - when (x.size) { - 16 -> Matrix44.fromDoubleArray((x as List).toDoubleArray()) - 4 -> { - (x as List) - Matrix44.fromColumnVectors(x[0], x[1], x[2], x[3]) - } - - else -> error("mat4(): unsupported argument: '$x'") - } - } - - else -> error("mat4(): unsupported argument: '$x'") - } -} - -internal fun dispatchFunction1(name: String, functions: Map): ((Array) -> Any)? { - return when (name) { - "vec2" -> { x -> vec2(x[0]) } - "vec3" -> { x -> vec3(x[0]) } - "vec4" -> { x -> vec4(x[0]) } - "mat4" -> { x -> mat4(x[0]) } - "cos" -> { x -> cos(x[0]) } - "sin" -> { x -> sin(x[0]) } - "sqrt" -> { v -> sqrt(v[0]) } - "abs" -> { v -> abs(v[0]) } - "scale" -> { x -> scale(x[0]) } - "rgb", "rgba" -> { x -> rgba(x[0]) } - "translate" -> { x -> translate(x[0]) } - "transpose" -> { x -> transpose(x[0]) } - "inverse" -> { x -> inverse(x[0]) } - "normalize" -> { x -> normalize(x[0]) } - else -> functions[name]?.let { { x: Array -> it.invoke(x[0]) } } - } -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function2.kt b/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function2.kt deleted file mode 100644 index fa9711d6..00000000 --- a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function2.kt +++ /dev/null @@ -1,86 +0,0 @@ -package org.openrndr.extra.expressions.typed - -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 -import org.openrndr.math.transforms.rotate -import org.openrndr.math.max as max_ -import org.openrndr.math.min as min_ -import kotlin.math.max as max_ -import kotlin.math.min as min_ - -internal fun rotate(axis: Any, angleInDegrees:Any): Matrix44 { - require(angleInDegrees is Double) - @Suppress("NAME_SHADOWING") val axis = when(axis) { - is Vector2 -> axis.xy0 - is Vector3 -> axis - else -> error("unsupported axis argument") - } - return Matrix44.rotate(axis, angleInDegrees) -} - - -internal fun min(x: Any, y: Any): Any { - return when { - x is Double && y is Double -> min_(x, y) - x is Vector2 && y is Vector2 -> min_(x, y) - x is Vector3 && y is Vector3 -> min_(x, y) - x is Vector4 && y is Vector4 -> min_(x, y) - else -> error("unsupported arguments") - } -} - -internal fun max(x: Any, y: Any): Any { - return when { - x is Double && y is Double -> max_(x, y) - x is Vector2 && y is Vector2 -> max_(x, y) - x is Vector3 && y is Vector3 -> max_(x, y) - x is Vector4 && y is Vector4 -> max_(x, y) - else -> error("unsupported arguments") - } -} - -internal fun vec2(x: Any, y: Any): Vector2 { - require(x is Double) - require(y is Double) - return Vector2(x, y) -} - -internal fun vec3(x: Any, y: Any): Vector3 = when { - x is Double && y is Vector2 -> { - Vector3(x, y.x, y.y) - } - x is Vector2 && y is Double -> { - Vector3(x.x, x.y, y) - } - else -> { - error("unsupported arguments, '$x' (${x::class}) '$y' (${y::class}") - } -} - -internal fun vec4(x: Any, y: Any): Vector4 = when { - x is Double && y is Vector3 -> { - Vector4(x, y.x, y.y, y.z) - } - x is Vector2 && y is Vector2 -> { - Vector4(x.x, x.y, y.x, y.y) - } - x is Vector3 && y is Double -> { - Vector4(x.x, x.y, x.z, y) - } - else -> { - error("unsupported arguments, '$x' (${x::class}) '$y' (${y::class}") - } -} - -internal fun dispatchFunction2(name: String, functions: Map): ((Array) -> Any)? { - return when (name) { - "min" -> { x -> min(x[0], x[1]) } - "max" -> { x -> max(x[0], x[1]) } - "vec2" -> { x -> vec2(x[0], x[1]) } - "vec3" -> { x -> vec3(x[0], x[1]) } - "rotate" -> { x -> rotate(x[0], x[1]) } - else -> functions[name]?.let { { x: Array -> it.invoke(x[0], x[1]) } } - } -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function3.kt b/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function3.kt deleted file mode 100644 index d2345144..00000000 --- a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function3.kt +++ /dev/null @@ -1,43 +0,0 @@ -package org.openrndr.extra.expressions.typed - -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 -import org.openrndr.math.mix as mix_ - -internal fun mix(x: Any, y: Any, f: Any): Any { - return when { - x is Double && y is Double && f is Double -> mix_(x, y, f) - x is Vector2 && y is Vector2 && f is Double -> mix_(x, y, f) - x is Vector3 && y is Vector3 && f is Double -> mix_(x, y, f) - x is Vector4 && y is Vector4 && f is Double -> mix_(x, y, f) - else -> error("unsupported arguments") - } -} - -internal fun vec3(x: Any, y: Any, z: Any): Vector3 { - require(x is Double && y is Double && z is Double) - return Vector3(x, y, z) -} - -internal fun vec4(x: Any, y: Any, z: Any): Vector4 { - return when { - x is Vector2 && y is Double && z is Double -> Vector4(x.x, x.y, y, z) - x is Double && y is Vector2 && z is Double -> Vector4(x, y.x, y.y, z) - x is Double && y is Double && z is Vector2 -> Vector4(x, y, z.x, z.y) - else -> error("unsupported arguments") - } -} - -internal fun dispatchFunction3(name: String, functions: Map): ((Array) -> Any)? { - return when (name) { - "vec3" -> { x -> vec3(x[0], x[1], x[2]) } - "vec4" -> { x -> vec4(x[0], x[1], x[2]) } - "mix" -> { x -> mix(x[0], x[1], x[2]) } - else -> functions[name]?.let { - { x: Array -> - it.invoke(x[0], x[1], x[2]) - } - } - } -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function4.kt b/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function4.kt deleted file mode 100644 index 122dbb69..00000000 --- a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Function4.kt +++ /dev/null @@ -1,30 +0,0 @@ -package org.openrndr.extra.expressions.typed - -import org.openrndr.color.ColorRGBa -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector4 - -internal fun vec4(x: Any, y: Any, z: Any, w: Any): Vector4 { - require(x is Double && y is Double && z is Double && w is Double) - return Vector4(x, y, z, w) -} - -internal fun mat4(x: Any, y: Any, z: Any, w: Any): Matrix44 { - require(x is Vector4 && y is Vector4 && z is Vector4 && w is Vector4) - return Matrix44.fromColumnVectors(x, y, z, w) -} - -internal fun rgba(r: Any, g: Any, b: Any, a: Any): ColorRGBa { - require(r is Double && g is Double && b is Double && a is Double) - return ColorRGBa(r, g, b, a) -} - - -internal fun dispatchFunction4(name: String, functions: Map): ((Array) -> Any)? { - return when (name) { - "vec4" -> { x -> vec4(x[0], x[1], x[2], x[3]) } - "mat4" -> { x -> mat4(x[0], x[1], x[2], x[3]) } - "rgba" -> { x -> rgba(x[0], x[1], x[2], x[3]) } - else -> functions[name]?.let { { x: Array -> it.invoke(x[0], x[1], x[2], x[3]) } } - } -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/MemberFunctions.kt b/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/MemberFunctions.kt deleted file mode 100644 index 54f19559..00000000 --- a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/MemberFunctions.kt +++ /dev/null @@ -1,83 +0,0 @@ -package org.openrndr.extra.expressions.typed - -import kotlin.math.roundToInt - -internal fun String.memberFunctions(n: String): ((Array) -> Any)? { - return when (n) { - "take" -> { nn -> this.take((nn[0] as Number).toInt()) } - "drop" -> { nn -> this.drop((nn[0] as Number).toInt()) } - "takeLast" -> { nn -> this.takeLast((nn[0] as Number).toInt()) } - "dropLast" -> { nn -> this.takeLast((nn[0] as Number).toInt()) } - else -> null - } -} - -internal fun List<*>.memberFunctions(n: String): ((Array) -> Any)? { - return when (n) { - "first" -> { _ -> this.first() ?: error("empty list") } - "last" -> { _ -> this.last() ?: error("empty list") } - "take" -> { nn -> this.take((nn[0] as Number).toInt()) } - "drop" -> { nn -> this.drop((nn[0] as Number).toInt()) } - "takeLast" -> { nn -> this.takeLast((nn[0] as Number).toInt()) } - "dropLast" -> { nn -> this.takeLast((nn[0] as Number).toInt()) } - "map" -> { nn -> @Suppress("UNCHECKED_CAST") val lambda = (nn[0] as (Any) -> Any); this.map { lambda(it!!) } } - "filter" -> { nn -> - @Suppress("UNCHECKED_CAST", "UNCHECKED_CAST") val lambda = - (nn[0] as (Any) -> Any); this.filter { (lambda(it!!) as Double).roundToInt() != 0 } - } - - "max" -> { _ -> - @Suppress("UNCHECKED_CAST") - (this as List>).max() - } - - "min" -> { _ -> - @Suppress("UNCHECKED_CAST") - (this as List>).min() - } - - "maxBy" -> { nn -> - @Suppress("UNCHECKED_CAST") val lambda = - (nn[0] as (Any) -> Any); this.maxByOrNull { - @Suppress("UNCHECKED_CAST") - lambda(it!!) as Comparable - } ?: error("no max") - } - - "minBy" -> { nn -> - @Suppress("UNCHECKED_CAST") val lambda = - (nn[0] as (Any) -> Any); this.minByOrNull { - @Suppress("UNCHECKED_CAST") - lambda(it!!) as Comparable - } ?: error("no max") - } - - "sorted" -> { _ -> - @Suppress("UNCHECKED_CAST") - (this as List>).sorted() - } - - "sortedBy" -> { nn -> - @Suppress("UNCHECKED_CAST") val lambda = - (nn[0] as (Any) -> Any); this.sortedBy { - @Suppress("UNCHECKED_CAST") - lambda(it!!) as Comparable - } - } - - "sortedByDescending" -> { nn -> - @Suppress("UNCHECKED_CAST") val lambda = (nn[0] as (Any) -> Any); this.sortedByDescending { - @Suppress("UNCHECKED_CAST") - lambda(it!!) as Comparable - } - } - - "reversed" -> { _ -> this.reversed() } - "zip" -> { nn -> - @Suppress("UNCHECKED_CAST") - this.zip(nn[0] as List).map { listOf(it.first, it.second) } - } - - else -> null - } -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Properties.kt b/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Properties.kt deleted file mode 100644 index 4782094f..00000000 --- a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/Properties.kt +++ /dev/null @@ -1,92 +0,0 @@ -package org.openrndr.extra.expressions.typed - -import org.openrndr.color.ColorRGBa -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 - -internal fun String.property(property: String): Any { - return when (property) { - "length" -> this.length.toDouble() - "uppercase" -> this.uppercase() - "lowercase" -> this.lowercase() - "reversed" -> this.reversed() - else -> error("unknown property '$property'") - } -} - -internal fun Vector2.property(property: String): Any { - return when (property) { - "x" -> x - "y" -> y - "xx" -> xx - "yx" -> yx - "yy" -> yy - "xy" -> this - "xxx" -> Vector3(x, x, x) - "xxy" -> Vector3(x, x, y) - "length" -> length - "normalized" -> normalized - else -> error("unknown property '$property") - } -} - -internal fun Vector3.property(property: String): Any { - return when (property) { - "x" -> x - "y" -> y - "z" -> z - "xx" -> Vector2(x, x) - "yx" -> Vector2(y, x) - "yy" -> Vector2(y, y) - "xy" -> Vector2(x, y) - "zx" -> Vector2(z, x) - "xz" -> Vector2(x, z) - "xxx" -> Vector3(x, x, x) - "xxy" -> Vector3(x, x, y) - "length" -> length - "normalized" -> normalized - - else -> error("unknown property '$property") - } -} - -internal fun Vector4.property(property: String): Any { - return when (property) { - "x" -> x - "y" -> y - "z" -> z - "xx" -> Vector2(x, x) - "yx" -> Vector2(y, x) - "yy" -> Vector2(y, y) - "xy" -> Vector2(x, y) - "zx" -> Vector2(z, x) - "xz" -> Vector2(x, z) - "xyz" -> Vector3(x, y, z) - "xxy" -> Vector3(x, x, y) - "length" -> length - "normalized" -> normalized - else -> error("unknown property '$property") - } -} - -internal fun ColorRGBa.property(property: String): Any { - return when (property) { - "r" -> r - "g" -> g - "b" -> b - "a" -> alpha - "linear" -> toLinear() - "srgb" -> toSRGB() - else -> error("unknown property '$property") - } -} - -internal fun Matrix44.property(property: String): Any { - return when (property) { - "inversed" -> inversed - "transposed" -> transposed - else -> error("unknown property '$property") - } -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/TypedExpressions.kt b/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/TypedExpressions.kt deleted file mode 100644 index 7677098d..00000000 --- a/orx-expression-evaluator-typed/src/commonMain/kotlin/typed/TypedExpressions.kt +++ /dev/null @@ -1,1091 +0,0 @@ -package org.openrndr.extra.expressions.typed - -import org.antlr.v4.kotlinruntime.* -import org.antlr.v4.kotlinruntime.tree.ParseTreeWalker -import org.antlr.v4.kotlinruntime.tree.TerminalNode -import org.openrndr.collections.pop -import org.openrndr.collections.push -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.expressions.parser.KeyLangLexer -import org.openrndr.extra.expressions.parser.KeyLangLexer.Tokens.RANGE_DOWNTO -import org.openrndr.extra.expressions.parser.KeyLangLexer.Tokens.RANGE_EXCLUSIVE -import org.openrndr.extra.expressions.parser.KeyLangLexer.Tokens.RANGE_EXCLUSIVE_UNTIL -import org.openrndr.extra.expressions.parser.KeyLangLexer.Tokens.RANGE_INCLUSIVE -import org.openrndr.extra.expressions.parser.KeyLangParser -import org.openrndr.extra.expressions.parser.KeyLangParserBaseListener -import org.openrndr.math.* -import kotlin.math.PI -import kotlin.math.roundToInt - -typealias TypedFunction0 = () -> Any -typealias TypedFunction1 = (Any) -> Any -typealias TypedFunction2 = (Any, Any) -> Any -typealias TypedFunction3 = (Any, Any, Any) -> Any -typealias TypedFunction4 = (Any, Any, Any, Any) -> Any -typealias TypedFunction5 = (Any, Any, Any, Any, Any) -> Any - - -private fun ArrayDeque.pushChecked(item: Any) { -// require(item is Double || item is Vector2 || item is Vector3 || item is Vector4 || item is Map<*, *> || item is Matrix44) { -// -// "$item ${item::class}" -// } - push(item) -} - -class TypedFunctionExtensions( - val functions0: Map = emptyMap(), - val functions1: Map = emptyMap(), - val functions2: Map = emptyMap(), - val functions3: Map = emptyMap(), - val functions4: Map = emptyMap(), - val functions5: Map = emptyMap() -) { - companion object { - val EMPTY = TypedFunctionExtensions() - } -} - -enum class IDType { - VARIABLE, - PROPERTY, - MEMBER_FUNCTION0, - MEMBER_FUNCTION1, - MEMBER_FUNCTION2, - MEMBER_FUNCTION3, - FUNCTION0, - FUNCTION1, - FUNCTION2, - FUNCTION3, - FUNCTION4, - FUNCTION5, - FUNCTION_ARGUMENT -} - -/** - * A base class for handling typed expressions in a custom language parser. - * This class provides an extensive set of overrides for various parser rules - * to allow custom implementation when these rules are triggered during parsing. - * - * @property functions Represents a mapping of functions categorized by their arity. - * @property constants Tracks constants identified during parsing. - * @property state Maintains the current state of the listener, preserving contextual information. - * - * Methods primarily handle entering and exiting parser rules for expressions, statements, and - * function calls, offering hooks to extend or modify behavior for each parsing scenario. Additionally, - * utility methods are provided to handle and propagate errors during parsing. - */ -abstract class TypedExpressionListenerBase( - val functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY, - val constants: (String) -> Any? = { null } -) : - KeyLangParserBaseListener() { - - class State { - val valueStack = ArrayDeque() - val functionStack = ArrayDeque<(Array) -> Any>() - val propertyStack = ArrayDeque() - - val idTypeStack = ArrayDeque() - var lastExpressionResult: Any? = null - - val exceptionStack = ArrayDeque() - - var inFunctionLiteral = 0 - - fun reset() { - valueStack.clear() - functionStack.clear() - propertyStack.clear() - idTypeStack.clear() - lastExpressionResult = null - exceptionStack.clear() - inFunctionLiteral = 0 - } - } - - abstract val state: State - override fun enterLine(ctx: KeyLangParser.LineContext) { - val s = state - s.reset() - } - - override fun exitRangeExpression(ctx: KeyLangParser.RangeExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - val step: Any? - if (ctx.step != null) { - step = s.valueStack.pop() - } else { - step = null - } - - val right = s.valueStack.pop() - val left = s.valueStack.pop() - - - val lower = (left as Double).toInt() - val upper = (right as Double).toInt() - val list = if (step == null) { - when (ctx.operator?.type) { - RANGE_INCLUSIVE -> (lower..upper).toList().map { it.toDouble() } - RANGE_EXCLUSIVE -> (lower.. (lower until upper).toList().map { it.toDouble() } - RANGE_DOWNTO -> (lower downTo upper).toList().map { it.toDouble() } - else -> error("unsupported operator: '${ctx.operator?.text}'") - } - } else { - val stepSize = (step as Double).toInt() - when (ctx.operator?.type) { - RANGE_INCLUSIVE -> (lower..upper step stepSize).toList().map { it.toDouble() } - RANGE_EXCLUSIVE -> (lower.. (lower until upper step stepSize).toList().map { it.toDouble() } - RANGE_DOWNTO -> (lower downTo upper step stepSize).toList().map { it.toDouble() } - else -> error("unsupported operator: '${ctx.operator?.text}'") - } - } - s.valueStack.push(list) - - } - - override fun exitListLiteral(ctx: KeyLangParser.ListLiteralContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - val list = (0 until ctx.expressionRoot().size).map { s.valueStack.pop() } - s.valueStack.push(list.reversed()) - } - - override fun exitIndexExpression(ctx: KeyLangParser.IndexExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - val index = (s.valueStack.pop() as? Double)?.roundToInt() ?: error("index is not a number") - val listValue = s.valueStack.pop() - - @Suppress("UNCHECKED_CAST") val value = when (listValue) { - is List<*> -> listValue[index] ?: error("got null") - is Function<*> -> (listValue as (Int) -> Any)(index) - else -> error("can't index on '$listValue'") - } - s.valueStack.push(value) - } - - override fun enterFunctionLiteral(ctx: KeyLangParser.FunctionLiteralContext) { - val s = state - s.inFunctionLiteral++ - } - - override fun exitFunctionLiteral(ctx: KeyLangParser.FunctionLiteralContext) { - val s = state - s.inFunctionLiteral-- - val functionExpr = ctx.expression().text - - val ids = ctx.ID() - val f = when (ids.size) { - 0 -> compileFunction1(functionExpr, "it", constants, functions) - 1 -> compileFunction1(functionExpr, ids[0].text, constants, functions) - 2 -> compileFunction2(functionExpr, ids[0].text, ids[1].text, constants, functions) - else -> error("functions with ${ids.size} parameters are not supported") - - } - s.valueStack.push(f) - } - - override fun exitExpressionStatement(ctx: KeyLangParser.ExpressionStatementContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - throw ExpressionException("error in evaluation of '${ctx.text}': ${it.message ?: ""}") - } - val result = state.valueStack.pop() - s.lastExpressionResult = result - } - - override fun exitMinusExpression(ctx: KeyLangParser.MinusExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - val op = s.valueStack.pop() - s.valueStack.pushChecked( - when (op) { - is Double -> -op - is Vector3 -> -op - is Vector2 -> -op - is Vector4 -> -op - is Matrix44 -> op * -1.0 - else -> error("unsupported type") - } - ) - } - - @Suppress("IMPLICIT_CAST_TO_ANY") - override fun exitBinaryOperation1(ctx: KeyLangParser.BinaryOperation1Context) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - pushError(it.message ?: "") - return - } - - val right = s.valueStack.pop() - val left = s.valueStack.pop() - - val result = when (val operator = ctx.operator?.type) { - KeyLangLexer.Tokens.ASTERISK -> when { - left is Double && right is Double -> left * right - left is Vector2 && right is Vector2 -> left * right - left is Vector2 && right is Double -> left * right - left is Vector3 && right is Vector3 -> left * right - left is Vector3 && right is Double -> left * right - left is Vector4 && right is Vector4 -> left * right - left is Vector4 && right is Double -> left * right - left is Matrix44 && right is Matrix44 -> left * right - left is Matrix44 && right is Vector4 -> left * right - left is Matrix44 && right is Double -> left * right - left is ColorRGBa && right is Double -> left * right - left is String && right is Double -> left.repeat(right.roundToInt()) - left is List<*> && right is Double -> (0 until right.roundToInt()).flatMap { left } - else -> error("unsupported operands for * operator left:${left::class} right:${right::class}") - } - - KeyLangLexer.Tokens.DIVISION -> when { - left is Double && right is Double -> left / right - left is Vector2 && right is Vector2 -> left / right - left is Vector2 && right is Double -> left / right - left is Vector3 && right is Vector3 -> left / right - left is Vector3 && right is Double -> left / right - left is Vector4 && right is Vector4 -> left / right - left is Vector4 && right is Double -> left / right - left is ColorRGBa && right is Double -> left / right - else -> error("unsupported operands for - operator left:${left::class} right:${right::class}") - } - - KeyLangLexer.Tokens.PERCENTAGE -> when { - left is Double && right is Double -> left.mod(right) - left is Vector2 && right is Vector2 -> left.mod(right) - left is Vector3 && right is Vector3 -> left.mod(right) - left is Vector4 && right is Vector4 -> left.mod(right) - else -> error("unsupported operands for - operator left:${left::class} right:${right::class}") - } - - else -> error("operator '$operator' not implemented") - } - s.valueStack.pushChecked(result) - } - - @Suppress("IMPLICIT_CAST_TO_ANY") - override fun exitBinaryOperation2(ctx: KeyLangParser.BinaryOperation2Context) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - - ifError { - pushError(it.message ?: "") - return - } - - val right = s.valueStack.pop() - val left = s.valueStack.pop() - - val result = when (val operator = ctx.operator?.type) { - KeyLangLexer.Tokens.PLUS -> when { - left is Double && right is Double -> left + right - left is Vector2 && right is Vector2 -> left + right - left is Vector3 && right is Vector3 -> left + right - left is Vector4 && right is Vector4 -> left + right - left is Matrix44 && right is Matrix44 -> left + right - left is ColorRGBa && right is ColorRGBa -> left + right - left is String && right is String -> left + right - left is List<*> && right is List<*> -> left + right - else -> error("unsupported operands for + operator left:${left::class} right:${right::class}") - } - - KeyLangLexer.Tokens.MINUS -> when { - left is Double && right is Double -> left - right - left is Vector2 && right is Vector2 -> left - right - left is Vector3 && right is Vector3 -> left - right - left is Vector4 && right is Vector4 -> left - right - left is Matrix44 && right is Matrix44 -> left - right - left is ColorRGBa && right is ColorRGBa -> left - right - else -> error("unsupported operands for - operator left:${left::class} right:${right::class}") - } - - else -> error("operator '$operator' not implemented") - } - s.valueStack.pushChecked(result) - } - - override fun exitJoinOperation(ctx: KeyLangParser.JoinOperationContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - val right = (s.valueStack.pop() as Double).roundToInt() - val left = (s.valueStack.pop() as Double).roundToInt() - - val result = when (val operator = ctx.operator?.type) { - KeyLangLexer.Tokens.AND -> right != 0 && left != 0 - KeyLangLexer.Tokens.OR -> right != 0 || left != 0 - else -> error("operator '$operator' not implemented") - } - s.valueStack.pushChecked(if (result) 1.0 else 0.0) - } - - override fun exitComparisonOperation(ctx: KeyLangParser.ComparisonOperationContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - val right = s.valueStack.pop() - val left = s.valueStack.pop() - - val result = when (val operator = ctx.operator?.type) { - KeyLangLexer.Tokens.EQ -> when { - left is Double && right is Double -> left == right - left is Vector2 && right is Vector2 -> left == right - left is Vector3 && right is Vector3 -> left == right - left is Vector4 && right is Vector4 -> left == right - left is ColorRGBa && right is ColorRGBa -> left == right - left is String && right is String -> left == right - else -> error("unsupported operands for == operator left:${left::class} right:${right::class}") - } - - KeyLangLexer.Tokens.LTEQ -> when { - left is Double && right is Double -> left <= right - else -> error("unsupported operands for <= operator left:${left::class} right:${right::class}") - } - - KeyLangLexer.Tokens.LT -> when { - left is Double && right is Double -> left < right - else -> error("unsupported operands for < operator left:${left::class} right:${right::class}") - } - - KeyLangLexer.Tokens.GTEQ -> when { - left is Double && right is Double -> left >= right - else -> error("unsupported operands for >= operator left:${left::class} right:${right::class}") - } - - KeyLangLexer.Tokens.GT -> when { - left is Double && right is Double -> left > right - else -> error("unsupported operands for > operator left:${left::class} right:${right::class}") - } - - else -> error("operator '$operator' not implemented") - } - s.valueStack.pushChecked(if (result) 1.0 else 0.0) - } - - override fun exitNegateExpression(ctx: KeyLangParser.NegateExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - val operand = (s.valueStack.pop() as Double).roundToInt() - s.valueStack.pushChecked(if (operand == 0) 1.0 else 0.0) - } - - override fun exitTernaryExpression(ctx: KeyLangParser.TernaryExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - val right = s.valueStack.pop() - val left = s.valueStack.pop() - val comp = s.valueStack.pop() - - val result = when (comp) { - is Double -> if (comp.roundToInt() != 0) left else right - else -> error("can't compare") - } - s.valueStack.pushChecked(result) - } - - override fun enterValueReference(ctx: KeyLangParser.ValueReferenceContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - s.idTypeStack.push(IDType.VARIABLE) - } - - override fun enterMemberFunctionCall0Expression(ctx: KeyLangParser.MemberFunctionCall0ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - s.idTypeStack.push(IDType.MEMBER_FUNCTION1) - } - - override fun exitMemberFunctionCall0Expression(ctx: KeyLangParser.MemberFunctionCall0ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - pushError(it.message ?: "") - return - } - s.valueStack.pushChecked(s.functionStack.pop().invoke(emptyArray())) - } - - override fun enterMemberFunctionCall1Expression(ctx: KeyLangParser.MemberFunctionCall1ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - s.idTypeStack.push(IDType.MEMBER_FUNCTION1) - } - - override fun exitMemberFunctionCall1Expression(ctx: KeyLangParser.MemberFunctionCall1ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - pushError(it.message ?: "") - return - } - s.valueStack.pushChecked(s.functionStack.pop().invoke(arrayOf(s.valueStack.pop()))) - } - - override fun enterMemberFunctionCall2Expression(ctx: KeyLangParser.MemberFunctionCall2ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - s.idTypeStack.push(IDType.MEMBER_FUNCTION2) - } - - override fun exitMemberFunctionCall2Expression(ctx: KeyLangParser.MemberFunctionCall2ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - pushError(it.message ?: "") - return - } - val argument1 = s.valueStack.pop() - val argument0 = s.valueStack.pop() - - s.valueStack.pushChecked(s.functionStack.pop().invoke(arrayOf(argument0, argument1))) - } - - override fun enterMemberFunctionCall3Expression(ctx: KeyLangParser.MemberFunctionCall3ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - s.idTypeStack.push(IDType.MEMBER_FUNCTION3) - } - - override fun exitMemberFunctionCall3Expression(ctx: KeyLangParser.MemberFunctionCall3ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - pushError(it.message ?: "") - return - } - val argument2 = s.valueStack.pop() - val argument1 = s.valueStack.pop() - val argument0 = s.valueStack.pop() - - s.valueStack.pushChecked(s.functionStack.pop().invoke(arrayOf(argument0, argument1, argument2))) - } - - override fun enterMemberFunctionCall0LambdaExpression(ctx: KeyLangParser.MemberFunctionCall0LambdaExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - s.idTypeStack.push(IDType.MEMBER_FUNCTION1) - } - - override fun exitMemberFunctionCall0LambdaExpression(ctx: KeyLangParser.MemberFunctionCall0LambdaExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - pushError(it.message ?: "") - return - } - s.valueStack.pushChecked(s.functionStack.pop().invoke(arrayOf(s.valueStack.pop()))) - } - - - override fun enterFunctionCall0Expression(ctx: KeyLangParser.FunctionCall0ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - s.idTypeStack.push(IDType.FUNCTION0) - } - - override fun exitFunctionCall0Expression(ctx: KeyLangParser.FunctionCall0ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - pushError(it.message ?: "") - return - } - - val function = s.functionStack.pop() - val result = function.invoke(arrayOf()) - s.valueStack.pushChecked(result) - } - - override fun enterFunctionCall1Expression(ctx: KeyLangParser.FunctionCall1ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - s.idTypeStack.push(IDType.FUNCTION1) - } - - override fun exitFunctionCall1Expression(ctx: KeyLangParser.FunctionCall1ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - pushError(it.message ?: "") - return - } - - val function = s.functionStack.pop() - val argument = s.valueStack.pop() - - val result = function.invoke(arrayOf(argument)) - s.valueStack.pushChecked(result) - } - - override fun enterFunctionCall2Expression(ctx: KeyLangParser.FunctionCall2ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - s.idTypeStack.push(IDType.FUNCTION2) - } - - - override fun exitFunctionCall2Expression(ctx: KeyLangParser.FunctionCall2ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - pushError(it.message ?: "") - return - } - - val function = s.functionStack.pop() - val argument1 = s.valueStack.pop() - val argument0 = s.valueStack.pop() - - val result = function.invoke(arrayOf(argument0, argument1)) - s.valueStack.pushChecked(result) - } - - override fun enterFunctionCall3Expression(ctx: KeyLangParser.FunctionCall3ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - s.idTypeStack.push(IDType.FUNCTION3) - } - - override fun exitFunctionCall3Expression(ctx: KeyLangParser.FunctionCall3ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - pushError(it.message ?: "") - return - } - - val function = s.functionStack.pop() - val argument2 = s.valueStack.pop() - val argument1 = s.valueStack.pop() - val argument0 = s.valueStack.pop() - - val result = function.invoke(arrayOf(argument0, argument1, argument2)) - s.valueStack.pushChecked(result) - } - - override fun enterFunctionCall4Expression(ctx: KeyLangParser.FunctionCall4ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - s.idTypeStack.push(IDType.FUNCTION4) - } - - override fun exitFunctionCall4Expression(ctx: KeyLangParser.FunctionCall4ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - pushError(it.message ?: "") - return - } - - val function = s.functionStack.pop() - val argument3 = s.valueStack.pop() - val argument2 = s.valueStack.pop() - val argument1 = s.valueStack.pop() - val argument0 = s.valueStack.pop() - - val result = function.invoke(arrayOf(argument0, argument1, argument2, argument3)) - s.valueStack.pushChecked(result) - } - - - override fun enterFunctionCall5Expression(ctx: KeyLangParser.FunctionCall5ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - s.idTypeStack.push(IDType.FUNCTION5) - } - - override fun exitFunctionCall5Expression(ctx: KeyLangParser.FunctionCall5ExpressionContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - ifError { - pushError(it.message ?: "") - return - } - - val function = s.functionStack.pop() - val argument4 = s.valueStack.pop() - val argument3 = s.valueStack.pop() - val argument2 = s.valueStack.pop() - val argument1 = s.valueStack.pop() - val argument0 = s.valueStack.pop() - - val result = function.invoke(arrayOf(argument0, argument1, argument2, argument3, argument4)) - s.valueStack.pushChecked(result) - } - - private fun errorValue(message: String, value: T): T { - pushError(message) - return value - } - - private fun pushError(message: String) { - val s = state - - s.exceptionStack.push(ExpressionException(message)) - } - - private inline fun ifError(f: (e: Throwable) -> Unit) { - val s = state - if (s.exceptionStack.isNotEmpty()) { - val e = s.exceptionStack.pop() - f(e) - } - } - - override fun enterPropReference(ctx: KeyLangParser.PropReferenceContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - s.idTypeStack.push(IDType.PROPERTY) - } - - override fun exitPropReference(ctx: KeyLangParser.PropReferenceContext) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - val root = s.valueStack.pop() - var current = root - val property = s.propertyStack.pop() - @Suppress("UNCHECKED_CAST") - current = when (current) { - is Map<*, *> -> current[property] ?: error("property '$property' not found") - is Function<*> -> (current as ((String) -> Any?)).invoke(property) - ?: error("property '$property' not found") - - is Vector2 -> current.property(property) - is Vector3 -> current.property(property) - is Vector4 -> current.property(property) - is ColorRGBa -> current.property(property) - is Matrix44 -> current.property(property) - is String -> current.property(property) - else -> error("can't look up: ${current::class} '$current', root:'$root' ${ctx.text} ") - } - s.valueStack.push(current) - } - - - @Suppress("MoveLambdaOutsideParentheses") - override fun visitTerminal(node: TerminalNode) { - val s = state - if (s.inFunctionLiteral > 0) { - return - } - - - fun handleFunction( - name: String, - dispatchFunction: (String, Map) -> ((Array) -> Any)?, - functionMap: Map, - adapter: (T) -> (Array) -> Any, - errorMessage: String - ) { - val function = dispatchFunction(name, functionMap) - - if (function != null) { - s.functionStack.push(function) - } else { - val cfunction = constants(name) as? T - if (cfunction != null) { - s.functionStack.push(adapter(cfunction)) - } else { - s.functionStack.push(errorValue("unresolved function: '$errorMessage'") { _ -> - error("this is the error function") - }) - } - } - } - - - - val type = node.symbol.type - if (type == KeyLangParser.Tokens.INTLIT) { - s.valueStack.pushChecked(node.text.toDouble()) - } else if (type == KeyLangParser.Tokens.DECLIT) { - s.valueStack.pushChecked(node.text.toDouble()) - } else if (type == KeyLangParser.Tokens.STRING_CONTENT) { - s.valueStack.pushChecked(node.text) - } else if (type == KeyLangParser.Tokens.ID) { - val name = node.text.replace("`", "") - @Suppress("DIVISION_BY_ZERO") - when (val idType = s.idTypeStack.pop()) { - IDType.VARIABLE -> s.valueStack.pushChecked( - when (name) { - "PI" -> PI - else -> constants(name) - ?: errorValue("unresolved value: '${name}'. Available constant: ${constants}", Unit) - } - ) - - IDType.PROPERTY -> s.propertyStack.push(name) - - IDType.MEMBER_FUNCTION0, - IDType.MEMBER_FUNCTION1, - IDType.MEMBER_FUNCTION2, - IDType.MEMBER_FUNCTION3 -> { - val receiver = s.valueStack.pop() - when (receiver) { - is String -> { - s.functionStack.push( - receiver.memberFunctions(name) - ?: error("no member function '$receiver.$name()'") - ) - } - - is List<*> -> { - s.functionStack.push( - receiver.memberFunctions(name) - ?: error("no member function '$receiver.$name()'") - ) - } - - is ColorRGBa -> { - when (idType) { - IDType.MEMBER_FUNCTION1 -> { - s.functionStack.push( - when (name) { - "shade" -> { x -> receiver.shade(x[0] as Double) } - "opacify" -> { x -> receiver.opacify(x[0] as Double) } - else -> error("no member function '$receiver.$name()'") - }) - } - - else -> error("no member function $idType '$receiver.$name()") - } - } - - is Function<*> -> { - - fun input(): String { - return "in '${node.getParent()?.text}'" - } - - @Suppress("UNCHECKED_CAST") - receiver as (String) -> Any? - val function = - receiver.invoke(name) ?: error("Unresolved function '${name} ${input()}") - - when (idType) { - IDType.MEMBER_FUNCTION0 -> { - @Suppress("UNCHECKED_CAST") - function as () -> Any - s.functionStack.push({ function() }) - } - - IDType.MEMBER_FUNCTION1 -> { - @Suppress("UNCHECKED_CAST") - (function as? (Any) -> Any) - ?: error("Cannot cast function '$name' ($function) to (Any) -> Any ${input()}") - s.functionStack.push({ x -> function(x[0]) }) - } - - IDType.MEMBER_FUNCTION2 -> { - @Suppress("UNCHECKED_CAST") - function as? (Any, Any) -> Any - ?: error("Cannot cast function '$name' ($function) to (Any, Any) -> Any ${input()}") - s.functionStack.push({ x -> function(x[0], x[1]) }) - } - - IDType.MEMBER_FUNCTION3 -> { - @Suppress("UNCHECKED_CAST") - function as? (Any, Any, Any) -> Any - ?: error("Cannot cast function '$name' ($function) to (Any, Any, Any) -> Any ${input()}") - s.functionStack.push({ x -> function(x[0], x[1], x[2]) }) - } - - else -> error("unreachable") - } - } - else -> error( - "receiver for '$name' '${ - receiver.toString().take(30) - }' ${receiver::class} not supported" - ) - } - } - - IDType.FUNCTION0 -> handleFunction( - name, - ::dispatchFunction0, - functions.functions0, - { f -> { x -> f() } }, - "${name}()" - ) - - IDType.FUNCTION1 -> handleFunction( - name, - ::dispatchFunction1, - functions.functions1, - { f -> { x -> f(x[0]) } }, - "${name}(x0)" - ) - - IDType.FUNCTION2 -> handleFunction( - name, - ::dispatchFunction2, - functions.functions2, - { f -> { x -> f(x[0], x[1]) } }, - "${name}(x0, x1)" - ) - - IDType.FUNCTION3 -> handleFunction( - name, - ::dispatchFunction3, - functions.functions3, - { f -> { x -> f(x[0], x[1], x[2]) } }, - "${name}(x0, x1, x2)" - ) - - IDType.FUNCTION4 -> handleFunction( - name, - ::dispatchFunction4, - functions.functions4, - { f -> { x -> f(x[0], x[1], x[2], x[3]) } }, - "${name}(x0, x1, x2, x3)" - ) - - IDType.FUNCTION5 -> { - val cfunction = constants(name) as? (Any, Any, Any, Any, Any) -> Any - if (cfunction != null) { - s.functionStack.push({ x -> cfunction(x[0], x[1], x[2], x[3], x[4]) }) - } else { - s.functionStack.push(errorValue("unresolved function: '${name}(x0, x1, x2, x3, x4)'") { _ -> - error("this is the error function") - }) - } - } - - IDType.FUNCTION_ARGUMENT -> { - - } - - else -> error("unsupported id-type $idType") - } - } - } -} - -expect class TypedExpressionListener( - functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY, - constants: (String) -> Any? = { null } -) : TypedExpressionListenerBase { - - override val state: State -} - -class ExpressionException(message: String) : RuntimeException(message) - -/** - * Evaluates a typed expression based on the provided string input, constants, - * and function definitions. - * - * @param expression The string representation of the expression to evaluate. - * @param constants A lambda function providing constant values for specific - * variables. Returns null if a constant is not found. Defaults to a function - * returning null for any input. - * @param functions A `TypedFunctionExtensions` instance encapsulating function - * definitions for 0 to 5 arguments. Defaults to an empty set of functions. - * @return The result of the evaluated expression as an `Any?` type. - * Returns null if the evaluation produces no result. - * @throws ExpressionException If a syntax error occurs in the input expression - * or during expression evaluation. - */ -fun evaluateTypedExpression( - expression: String, - constants: (String) -> Any? = { null }, - functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY -): Any? { - val lexer = KeyLangLexer(CharStreams.fromString(expression)) - val parser = KeyLangParser(CommonTokenStream(lexer)) - parser.removeErrorListeners() - parser.addErrorListener(object : BaseErrorListener() { - override fun syntaxError( - recognizer: Recognizer<*, *>, - offendingSymbol: Any?, - line: Int, - charPositionInLine: Int, - msg: String, - e: RecognitionException? - ) { - throw ExpressionException("parser error in expression: '$expression'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]") - } - }) - - val root = parser.keyLangFile() - val listener = TypedExpressionListener(functions, constants) - try { - ParseTreeWalker.DEFAULT.walk(listener, root) - } catch (e: ExpressionException) { - throw ExpressionException(e.message ?: "") - } - - return listener.state.lastExpressionResult -} - -/** - * Compiles a typed expression and returns a lambda that can execute the compiled expression. - * - * @param expression The string representation of the expression to compile. - * @param constants A lambda function to resolve constants by their names. Defaults to a resolver that returns null. - * @param functions An instance of `TypedFunctionExtensions` containing the supported custom functions for the expression. Defaults to an empty set of functions. - * @return A lambda function that evaluates the compiled expression and returns its result. - * @throws ExpressionException If there is a syntax error or a parsing issue in the provided expression. - */ -fun compileTypedExpression( - expression: String, - constants: (String) -> Any? = { null }, - functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY -): () -> Any { - val lexer = KeyLangLexer(CharStreams.fromString(expression)) - val parser = KeyLangParser(CommonTokenStream(lexer)) - parser.removeErrorListeners() - parser.addErrorListener(object : BaseErrorListener() { - override fun syntaxError( - recognizer: Recognizer<*, *>, - offendingSymbol: Any?, - line: Int, - charPositionInLine: Int, - msg: String, - e: RecognitionException? - ) { - throw ExpressionException("parser error in expression: '$expression'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]") - } - }) - val root = parser.keyLangFile() - val listener = TypedExpressionListener(functions, constants) - - return { - try { - ParseTreeWalker.DEFAULT.walk(listener, root) - } catch (e: ExpressionException) { - throw ExpressionException(e.message ?: "") - } - listener.state.lastExpressionResult ?: error("no result") - } -} - -internal fun expressionRoot(expression: String): KeyLangParser.KeyLangFileContext { - val lexer = KeyLangLexer(CharStreams.fromString(expression)) - val parser = KeyLangParser(CommonTokenStream(lexer)) - parser.removeErrorListeners() - parser.addErrorListener(object : BaseErrorListener() { - override fun syntaxError( - recognizer: Recognizer<*, *>, - offendingSymbol: Any?, - line: Int, - charPositionInLine: Int, - msg: String, - e: RecognitionException? - ) { - throw ExpressionException("parser error in expression: '$expression'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]") - } - }) - return parser.keyLangFile() -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/jsMain/kotlin/typed/TypedExpressions.js.kt b/orx-expression-evaluator-typed/src/jsMain/kotlin/typed/TypedExpressions.js.kt deleted file mode 100644 index 8c0ccefe..00000000 --- a/orx-expression-evaluator-typed/src/jsMain/kotlin/typed/TypedExpressions.js.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.openrndr.extra.expressions.typed - -actual class TypedExpressionListener actual constructor( - functions: TypedFunctionExtensions, - constants: (String) -> Any? -) : TypedExpressionListenerBase(functions, constants) { - actual override val state: State = State() -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/jvmMain/kotlin/typed/TypedExpressions.jvm.kt b/orx-expression-evaluator-typed/src/jvmMain/kotlin/typed/TypedExpressions.jvm.kt deleted file mode 100644 index 8516183f..00000000 --- a/orx-expression-evaluator-typed/src/jvmMain/kotlin/typed/TypedExpressions.jvm.kt +++ /dev/null @@ -1,15 +0,0 @@ -package org.openrndr.extra.expressions.typed - -import kotlin.concurrent.getOrSet - -/* -Thread safe TypeExpressionListener - */ -actual class TypedExpressionListener actual constructor( - functions: TypedFunctionExtensions, - constants: (String) -> Any? -) : TypedExpressionListenerBase(functions, constants) { - private val threadLocalState = ThreadLocal() - actual override val state: State - get() = threadLocalState.getOrSet { State() } -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/jvmTest/kotlin/typed/TestListLiteralExpression.kt b/orx-expression-evaluator-typed/src/jvmTest/kotlin/typed/TestListLiteralExpression.kt deleted file mode 100644 index 9321b321..00000000 --- a/orx-expression-evaluator-typed/src/jvmTest/kotlin/typed/TestListLiteralExpression.kt +++ /dev/null @@ -1,24 +0,0 @@ -package typed - -import org.junit.jupiter.api.Assertions.assertEquals -import org.openrndr.extra.expressions.typed.evaluateTypedExpression -import kotlin.test.Test -import kotlin.test.assertTrue - -class TestListLiteralExpression { - - @Test - fun testSimpleList() { - val r = evaluateTypedExpression("[0, 1, 2]") - assertTrue(r is List<*>) - assertEquals(3, r.size ) - } - - @Test - fun testRangesList() { - val r = evaluateTypedExpression("[0..1, 1..2, 2..3]") - assertTrue(r is List<*>) - assertEquals(3, r.size ) - } - -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/jvmTest/kotlin/typed/TestRangeExpression.kt b/orx-expression-evaluator-typed/src/jvmTest/kotlin/typed/TestRangeExpression.kt deleted file mode 100644 index 6bf7669c..00000000 --- a/orx-expression-evaluator-typed/src/jvmTest/kotlin/typed/TestRangeExpression.kt +++ /dev/null @@ -1,72 +0,0 @@ -package typed - -import org.junit.jupiter.api.Assertions.assertEquals -import org.openrndr.extra.expressions.typed.evaluateTypedExpression -import kotlin.test.Test -import kotlin.test.assertTrue - -class TestRangeExpression { - @Test - fun testRangeInclusive() { - val r = evaluateTypedExpression("(0..10)") - assertTrue(r is List<*>) - assertEquals(11, r.size) - } - - @Test - fun testRangeDownTo() { - val r = evaluateTypedExpression("(10 downTo 0)") - assertTrue(r is List<*>) - assertEquals(11, r.size) - assertEquals(0.0, r.last()) - } - - @Test - fun testRangeExclusive() { - val r = evaluateTypedExpression("(0..<10)") - assertTrue(r is List<*>) - assertEquals(10, r.size) - } - - @Test - fun testRangeInclusivePrecedenceRight() { - val r = evaluateTypedExpression("(0..10+2)") - assertTrue(r is List<*>) - assertEquals(13, r.size) - } - - @Test - fun testRangeInclusivePrecedenceLeft() { - val r = evaluateTypedExpression("1+2..10") - assertTrue(r is List<*>) - assertEquals(8, r.size) - } - - @Test - fun testRangeInclusivePrecedenceLeftRight() { - val r = evaluateTypedExpression("1+2..10+2") - assertTrue(r is List<*>) - assertEquals(10, r.size) - } - - @Test - fun testRangeUntilPrecedenceLeftRight() { - val r = evaluateTypedExpression("1+2 until 10+2") - assertTrue(r is List<*>) - assertEquals(9, r.size) - } - - @Test - fun testRangeUntilPrecedenceLeftRightMap() { - val r = evaluateTypedExpression("(1+2 until 10+2).map { x -> x * 2 }") - assertTrue(r is List<*>) - assertEquals(9, r.size) - } - - @Test - fun testRangeExclusiveStep() { - val r = evaluateTypedExpression("(0..10 step 2)") - assertTrue(r is List<*>) - assertEquals(6, r.size) - } -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/jvmTest/kotlin/typed/TestTypedCompiledExpression.kt b/orx-expression-evaluator-typed/src/jvmTest/kotlin/typed/TestTypedCompiledExpression.kt deleted file mode 100644 index 5184c3f3..00000000 --- a/orx-expression-evaluator-typed/src/jvmTest/kotlin/typed/TestTypedCompiledExpression.kt +++ /dev/null @@ -1,117 +0,0 @@ -package typed - -import org.openrndr.extra.expressions.typed.compileFunction1OrNull -import org.openrndr.extra.noise.uniform -import org.openrndr.math.Vector2 -import kotlin.math.cos -import kotlin.test.Test -import kotlin.test.assertEquals - -class TestTypedCompiledExpression { - - @Test - fun testStringLiteral() { - run { - val c = compileFunction1OrNull(""""hoi"""", "t")!! - val v = c(0.0) - assertEquals("hoi", v) - } - run { - val c = compileFunction1OrNull(""""hoi" + " " + "doei" * t""", "t")!! - val v = c(2.0) - assertEquals("hoi doeidoei", v) - } - - run { - val c = compileFunction1OrNull(""""hoi".take(t)""", "t")!! - val v = c(2.0) - assertEquals("ho", v) - } - } - - @Test - fun testComparison() { - run { - val c = compileFunction1OrNull("""t == t""", "t")!! - val v = c(0.0) - assertEquals(1.0, v) - } - } - - @Test - fun testFunction1() { - run { - val c = compileFunction1OrNull("x + 3.0", "x")!! - assertEquals(1.0 + 3.0, c(1.0)) - assertEquals(2.0 + 3.0, c(2.0)) - } - run { - val c = compileFunction1OrNull("x.x + x.y", "x")!! - assertEquals(1.0 + 3.0, c(Vector2(1.0, 3.0))) - assertEquals(2.0 + 3.0, c(Vector2(2.0, 3.0))) - } - run { - val c = compileFunction1OrNull("x.x + x.y", "x")!! - val start = System.currentTimeMillis() - for (i in 0 until 1000) { - val r0 = Double.uniform(0.0, 1.0) - val r1 = Double.uniform(0.0, 1.0) - assertEquals(r0 + r1, c(Vector2(r0, r1))) - } - val end = System.currentTimeMillis() - println("that took ${end - start}") - } - } - - @Test - fun testFunction2() { - run { - val c = compileFunction1OrNull, Double>("x.x + 3.0", "x")!! - assertEquals(1.0 + 3.0, c(mapOf("x" to 1.0))) - //assertEquals(2.0 + 3.0, c(mapOf("x" to 2.0)) - } - } - - @Test - fun testDynamicConstants() { - - val env = { n: String -> - when (n) { - "a" -> { nn: String -> - when (nn) { - "a" -> { nnn: String -> - when (nnn) { - "b" -> 7.0 - "c" -> { x: Double -> x + 1.0 } - else -> null - } - } - - "b" -> 5.0 - "c" -> { x: Double -> x * 2.0 } - else -> null - } - - } - "c" -> { x: Double -> x * 3.0 } - else -> null - } - } - - val c0 = compileFunction1OrNull, Double>("a.a.c(2.0)", "x", constants = env)!! - val r0 = c0(emptyMap()) - assertEquals(3.0, r0) - - val c1 = compileFunction1OrNull, Double>("a.c(2.0)", "x", constants = env)!! - val r1 = c1(emptyMap()) - assertEquals(4.0, r1) - - val c2 = compileFunction1OrNull, Double>("c(2.0)", "x", constants = env)!! - val r2 = c2(emptyMap()) - assertEquals(6.0, r2) - - val c3 = compileFunction1OrNull, Double>("cos(2.0)", "x", constants = env)!! - val r3 = c3(emptyMap()) - assertEquals(cos(2.0), r3, 1E-6) - } -} \ No newline at end of file diff --git a/orx-expression-evaluator-typed/src/jvmTest/kotlin/typed/TestTypedExpression.kt b/orx-expression-evaluator-typed/src/jvmTest/kotlin/typed/TestTypedExpression.kt deleted file mode 100644 index e7a121a5..00000000 --- a/orx-expression-evaluator-typed/src/jvmTest/kotlin/typed/TestTypedExpression.kt +++ /dev/null @@ -1,178 +0,0 @@ -package typed - -import org.junit.jupiter.api.Assertions.assertEquals -import org.openrndr.extra.expressions.typed.evaluateTypedExpression -import org.openrndr.math.Vector2 -import kotlin.test.Test - -class TestTypedExpression { - - @Test - fun funTestFunction() { - run { - val r = evaluateTypedExpression("{ x -> 2.0 + x }") - @Suppress("UNCHECKED_CAST") val f = r as (Double) -> Double - println(f(3.0)) - } - run { - val r = evaluateTypedExpression("{ { 2.0 + it } }") - @Suppress("UNCHECKED_CAST") val f0 = r as (Any) -> ((Any) -> Any) - val f1 = f0(0.0) - println(f1(3.0)) - } - } - - @Test - fun funTestLambdaArg() { - run { - val r = evaluateTypedExpression("[0.0, 1.0].map { x -> 2.0 + x }") - assertEquals(listOf(2.0, 3.0), r) - } - - run { - val r = evaluateTypedExpression("[0.0, 1.0].map { x -> vec2(2.0 + x, 2.0 + x) }") - assertEquals(listOf(Vector2(2.0, 2.0), Vector2(3.0, 3.0)), r) - } - - run { - val r = evaluateTypedExpression("[0.0, 1.0, 2.0].filter { x -> x >= 1.0 }") - assertEquals(listOf(1.0, 2.0), r) - } - } - - - - @Test - fun testList() { - println("result is: ${evaluateTypedExpression("[]")}") - println("result is: ${evaluateTypedExpression("[1.0, 2.0]")}") - println("result is: ${evaluateTypedExpression("[1.0, 2.0].take(1)")}") - println("result is: ${evaluateTypedExpression("[1.0 + 2.0, 2.0 * 3.0].take(1 + 1)")}") - - println("result is: ${evaluateTypedExpression("[] + []")}") - println("result is: ${evaluateTypedExpression("([1] * 2 + [2] * 1)*5")}" ) - - println("result is: ${evaluateTypedExpression("[] + []")}") - println("result is: ${evaluateTypedExpression("[[0, 1, 2][1]]")}" ) - - println("result is: ${evaluateTypedExpression("[0, 1, 2].max()")}" ) - println("result is: ${evaluateTypedExpression("[0, 1, 2].maxBy { x -> x }")}" ) - println("result is: ${evaluateTypedExpression("""["one", "two", "three"].maxBy { x -> x.length }""")}") - - } - - - @Test - fun testTernary() { - println("result is: ${evaluateTypedExpression("2.0 > 0.5 ? 1.3 : 0.7")}") - } - - @Test - fun testJoin() { - assertEquals(0.0, evaluateTypedExpression("1.0 && 0.0")) - assertEquals(1.0, evaluateTypedExpression("1.0 && 1.0")) - assertEquals(0.0, evaluateTypedExpression("0.0 && 0.0")) - assertEquals(0.0, evaluateTypedExpression("0.0 && 1.0")) - - assertEquals(1.0, evaluateTypedExpression("1.0 || 0.0")) - assertEquals(1.0, evaluateTypedExpression("1.0 || 1.0")) - assertEquals(0.0, evaluateTypedExpression("0.0 || 0.0")) - assertEquals(1.0, evaluateTypedExpression("0.0 || 1.0")) - - assertEquals(1.0, evaluateTypedExpression("(0.0 || 1.0) && (1.0 || 0.0)")) - } - - @Test - fun testNegate() { - assertEquals(0.0, evaluateTypedExpression("!1.0")) - assertEquals(1.0, evaluateTypedExpression("!0.0")) - assertEquals(1.0, evaluateTypedExpression("!!2.0")) - } - - @Test - fun testTyped() { - println(evaluateTypedExpression("vec2(1.0, 1.0) + vec2(1.0, 1.0)")) - println(evaluateTypedExpression("vec3(1.0, 1.0, 1.0) + vec3(2.0, 3.0, 4.0)")) - println(evaluateTypedExpression("vec3(1.0, 1.0, 1.0) * vec3(2.0, 3.0, 4.0)")) - println(evaluateTypedExpression("translate(vec3(1.0, 0.0, 0.0)) * mat4(vec4(1,0,0,0), vec4(0,1,0,0), vec4(0,0,1,0), vec4(0.0, 0.0, 0.0, 1.0))")) - println(evaluateTypedExpression("(translate(vec3(1.0, 0.0, 0.0)) * vec4(0.0, 0.0, 0.0, 1.0)).xyz")) - } - - fun Map.function(): (String) -> Any? { - return fun(p: String): Any? { - val v = this[p] - if (v is Map<*, *>) { - @Suppress("UNCHECKED_CAST") - return (v as Map).function() - } else { - return v - } - } - } - - @Test - fun testPropref() { - println(evaluateTypedExpression("a.b.c", constants = mapOf("a" to mapOf("b" to mapOf("c" to 8.0))).function())) - println( - evaluateTypedExpression( - "a.yx.yx.normalized * -5.0", - constants = mapOf("a" to Vector2(1.0, 2.0)).function() - ) - ) - println( - evaluateTypedExpression( - "vec2(2.0, 3.0).normalized", - constants = mapOf("a" to Vector2(1.0, 2.0)).function() - ) - ) - } - - @Test - fun testMethodCall() { - println( - evaluateTypedExpression( - "a.b.c(5.0) + a.b.sum(3.0, 5.0)", - constants = mapOf( - "a" to - mapOf( - "b" to - mapOf( - "c" to { x: Double -> x * 5.0 }, - "sum" to { x: Double, y: Double -> x + y } - ) - ) - ).function() - ) - ) - } - - @Suppress("NAME_SHADOWING") - @Test - fun testMethodCallF() { - println( - evaluateTypedExpression( - "vec2(2.0, 3.0) * (a.b.c(5.0) + a.b.sum(3.0, 5.0))", - constants = { name: String -> - when (name) { - "a" -> { name: String -> - when (name) { - "b" -> { name: String -> - when (name) { - "c" -> { x: Double -> x * 5.0 } - "sum" -> { x: Double, y: Double -> x + y } - else -> null - } - } - - else -> null - } - } - - else -> null - } - } - ) - ) - } - -} \ No newline at end of file diff --git a/orx-expression-evaluator/.gitignore b/orx-expression-evaluator/.gitignore deleted file mode 100644 index f0dd3232..00000000 --- a/orx-expression-evaluator/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.tokens -gen/ \ No newline at end of file diff --git a/orx-expression-evaluator/README.md b/orx-expression-evaluator/README.md deleted file mode 100644 index cdfdcd1e..00000000 --- a/orx-expression-evaluator/README.md +++ /dev/null @@ -1,100 +0,0 @@ -# orx-expression-evaluator - -Tools to evaluate strings containing mathematical expressions. - -# Expression evaluator - -```kotlin -val expression = "x + y" -val constants = mapOf("x" to 1.0, "y" to 2.0) -evaluateExpression(expression, constants) -``` -## Built-in expression functions - -Unary functions: - * `abs(x)` - * `acos(x)` - * `asin(x)` - * `atan(x)` - * `ceil(x)` - * `cos(x)` - * `degrees(x)` - * `exps(x)` - * `floor(x)` - * `radians(x)` - * `round(x)` - * `saturate(x)`, clamp x to [0.0, 1.0] - * `sqrt(x)` - * `tan(x)` - -Binary functions: - * `atan2(x, y)` - * `length(x, y)`, the Euclidean length of the vector (x,y) - * `max(x, y)`, - * `min(x, y)`, - * `pow(x, n)` - * `random(x, y)`, return a random number in [x, y) - -Ternary functions: - * `length(x, y, z)`, the Euclidean length of the vector (x, y, z) - * `max(x, y, z)` - * `min(x, y, z)` - * `mix(l, r, f)` - * `smoothstep(e0, e1, x)` - * `sum(x, y, z)` - -Quaternary functions: -* `length(x, y, z, w)`, the Euclidean length of the vector (x, y, z) -* `max(a, b, c, d)` -* `min(a, b, c, d)` -* `sum(a, b, c, d)` - -Quinary functions: -* `map(x0, x1, y0, y1, v)` -* `max(a, b, c, d, e)` -* `min(a, b, c, d, e)` -* `sum(a, b, c, d, e)` - -# Compiled functions - -```kotlin -val expression = "x * 5.0 + cos(x)" -val f = compileFunction1(expression, "x") -f(0.0) -``` - -```kotlin -val expression = "x * 5.0 + cos(x) * y" -val f = compileFunction2(expression, "x", "y") -f(0.0, 0.4) -``` - -# Property delegates - -```kotlin -val constants = mutableMapOf("width" to 300.0) -val settings = object { - var xExpression = "cos(t) * 50.0 + width / 2.0" -} -val xFunction by watchingExpression1(settings::xExpression, "t", constants) - -xFunction(1.0) -``` - -## Demos -### DemoExpressionEvaluator01 - - - -![DemoExpressionEvaluator01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-expression-evaluator/images/DemoExpressionEvaluator01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoExpressionEvaluator01.kt) - -### DemoExpressionEvaluator02 - -Improved version of DemoExpressionEvaluator01, it uses [watchingExpression1] to automatically convert an expression -string into a function with a parameter "t". - -![DemoExpressionEvaluator02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-expression-evaluator/images/DemoExpressionEvaluator02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoExpressionEvaluator02.kt) diff --git a/orx-expression-evaluator/build.gradle.kts b/orx-expression-evaluator/build.gradle.kts deleted file mode 100644 index d1dfb39b..00000000 --- a/orx-expression-evaluator/build.gradle.kts +++ /dev/null @@ -1,56 +0,0 @@ -import com.strumenta.antlrkotlin.gradle.AntlrKotlinTask -import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask - -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") - alias(libs.plugins.antlr.kotlin) -} - -val generateKotlinGrammarSource = tasks.register("generateKotlinGrammarSource") { - dependsOn("cleanGenerateKotlinGrammarSource") - - source = fileTree(layout.projectDirectory.dir("src/commonMain/antlr")) { - include("**/*.g4") - } - - // We want the generated source files to have this package name - val pkgName = "org.openrndr.extra.expressions.parser" - packageName = pkgName - - // We want visitors alongside listeners. - // The Kotlin target language is implicit, as is the file encoding (UTF-8) - arguments = listOf("-visitor") - - // Generated files are outputted inside build/generatedAntlr/{package-name} - val outDir = "generatedAntlr/${pkgName.replace(".", "/")}" - outputDirectory = layout.buildDirectory.dir(outDir).get().asFile -} - - -kotlin { - sourceSets { - val commonMain by getting { - dependencies { - implementation(libs.antlr.kotlin.runtime) - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(sharedLibs.kotlin.coroutines) - implementation(project(":orx-property-watchers")) - implementation(project(":orx-noise")) - } - kotlin { - srcDir(layout.buildDirectory.dir("generatedAntlr")) - } - } - val jvmDemo by getting { - dependencies { - implementation(project(":orx-jvm:orx-gui")) - } - } - } -} - -tasks.withType> { dependsOn(generateKotlinGrammarSource) } -tasks.withType { dependsOn(generateKotlinGrammarSource) } -tasks.named("dokkaGeneratePublicationHtml") { dependsOn(generateKotlinGrammarSource) } -tasks.named("dokkaGenerateModuleHtml") { dependsOn(generateKotlinGrammarSource) } diff --git a/orx-expression-evaluator/src/commonMain/antlr/KeyLangLexer.g4 b/orx-expression-evaluator/src/commonMain/antlr/KeyLangLexer.g4 deleted file mode 100644 index 563e2c08..00000000 --- a/orx-expression-evaluator/src/commonMain/antlr/KeyLangLexer.g4 +++ /dev/null @@ -1,71 +0,0 @@ -lexer grammar KeyLangLexer; - -channels { WHITESPACE } - -// Whitespace -NEWLINE : '\r\n' | '\r' | '\n' ; -WS : [\t ]+ -> channel(WHITESPACE) ; - - -RANGE_INCLUSIVE : '..' ; -RANGE_EXCLUSIVE_UNTIL : 'until' ; -RANGE_EXCLUSIVE : '..<' ; -RANGE_DOWNTO : 'downTo' ; - -STEP : 'step' ; - - -// Identifiers -ID : [$_]*[a-zA-Z][A-Za-z0-9_]* | '`'[$_]*[A-Za-z0-9_-]*'`'; -FUNCTION_ID : [$_]*[a-z][A-Za-z0-9_]* ; - -// Literals - -DECLIT : [0-9][0-9]* '.' [0-9]+ ; -INTLIT : '0'|[0-9][0-9]* ; - -// Operators -PLUS : '+' ; -PERCENTAGE : '%' ; -MINUS : '-' ; -ASTERISK : '*' ; -DIVISION : '/' ; -ASSIGN : '=' ; -LPAREN : '(' ; -RPAREN : ')' ; -LBRACKET : '[' ; -RBRACKET : ']' ; -LCURLY : '{' ; -RCURLY : '}' ; - -QUESTION_MARK : '?' ; -COLON : ':' ; - -ARROW : '->' ; - -COMMA : ',' ; -DOT : '.' ; - -EQ : '==' ; -LT : '<' ; -LTEQ : '<=' ; -GT : '>' ; -GTEQ : '>=' ; - -AND : '&&' ; -OR : '||' ; -NOT : '!' ; - -STRING_OPEN : '"' -> pushMode(MODE_IN_STRING); - -UNMATCHED : . ; - -mode MODE_IN_STRING; - -ESCAPE_STRING_DELIMITER : '\\"' ; -ESCAPE_SLASH : '\\\\' ; -ESCAPE_NEWLINE : '\\n' ; -ESCAPE_SHARP : '\\#' ; -STRING_CLOSE : '"' -> popMode ; -STRING_CONTENT : ~["\n\r\t\\#]+ ; - diff --git a/orx-expression-evaluator/src/commonMain/antlr/KeyLangParser.g4 b/orx-expression-evaluator/src/commonMain/antlr/KeyLangParser.g4 deleted file mode 100644 index 18e370c1..00000000 --- a/orx-expression-evaluator/src/commonMain/antlr/KeyLangParser.g4 +++ /dev/null @@ -1,50 +0,0 @@ - -parser grammar KeyLangParser; - -options { tokenVocab=KeyLangLexer; } - -keyLangFile : lines=line+ ; - -line : statement (NEWLINE | EOF) ; - -statement : - expressionRoot # expressionStatement ; - -rangeExpression: expression operator=(RANGE_INCLUSIVE|RANGE_EXCLUSIVE|RANGE_EXCLUSIVE_UNTIL|RANGE_DOWNTO) expression (step=STEP expression)?; - -expressionRoot: rangeExpression - | expression - ; - -lambda: LCURLY (ID ( COMMA ID )* ARROW )? expression RCURLY # functionLiteral; - -expression : INTLIT # intLiteral - | DECLIT # decimalLiteral - | LBRACKET (expressionRoot ( COMMA expressionRoot )*)? RBRACKET # listLiteral - | expression DOT ID lambda # memberFunctionCall0LambdaExpression - | lambda # lambdaExpression - | expression DOT ID LPAREN RPAREN # memberFunctionCall0Expression - | expression DOT ID LPAREN expression RPAREN # memberFunctionCall1Expression - | expression DOT ID LPAREN expression COMMA expression RPAREN # memberFunctionCall2Expression - | expression DOT ID LPAREN expression COMMA expression COMMA expression RPAREN # memberFunctionCall3Expression - | expression DOT ID LPAREN expression COMMA expression COMMA expression COMMA expression RPAREN # memberFunctionCall4Expression - | expression LBRACKET expression RBRACKET # indexExpression - | ID LPAREN RPAREN # functionCall0Expression - | ID LPAREN expression RPAREN # functionCall1Expression - | ID LPAREN expression COMMA expression RPAREN # functionCall2Expression - | ID LPAREN expression COMMA expression COMMA expression RPAREN # functionCall3Expression - | ID LPAREN expression COMMA expression COMMA expression COMMA expression RPAREN # functionCall4Expression - | ID LPAREN expression COMMA expression COMMA expression COMMA expression COMMA expression RPAREN # functionCall5Expression - | ID # valueReference - | STRING_OPEN (parts+=stringLiteralContent)* STRING_CLOSE # stringLiteral - | expression DOT ID # propReference - | LPAREN expressionRoot RPAREN # parenExpression - | MINUS expression # minusExpression - | NOT expression # negateExpression - | expression operator=(DIVISION|ASTERISK|PERCENTAGE) expression # binaryOperation1 - | expression operator=(PLUS|MINUS) expression # binaryOperation2 - | expression operator=(EQ|LT|LTEQ|GT|GTEQ) expression # comparisonOperation - | expression operator=(AND|OR) expression # joinOperation - | expression QUESTION_MARK expression COLON expression # ternaryExpression; - -stringLiteralContent : STRING_CONTENT; \ No newline at end of file diff --git a/orx-expression-evaluator/src/commonMain/kotlin/CompiledFunctions.kt b/orx-expression-evaluator/src/commonMain/kotlin/CompiledFunctions.kt deleted file mode 100644 index 4fa07ac0..00000000 --- a/orx-expression-evaluator/src/commonMain/kotlin/CompiledFunctions.kt +++ /dev/null @@ -1,125 +0,0 @@ -package org.openrndr.extra.expressions - -import org.antlr.v4.kotlinruntime.tree.ParseTreeWalker - - -/** - * Compile a (Double)->Double function from an expression string - * @param expression the expression string to be compiled - * @param parameter0 the name of the first parameter - * @param constants a map of named constant values that can be referred from the expression - * @param functions a map of named functions that can be invoked from the expression - * @param error in case the expression fails to compile or evaluate, this function is invoked instead - */ -fun compileFunction1( - expression: String, - parameter0: String, - constants: Map = mapOf(), - functions: FunctionExtensions = FunctionExtensions.EMPTY, - error: (Double) -> Double = { 0.0 }, -): (Double) -> Double { - require(!constants.containsKey(parameter0)) - try { - val root = expressionRoot(expression) - val variables = mutableMapOf() - variables.putAll(constants) - val listener = ExpressionListener(functions, variables) - - return { p0 -> - variables[parameter0] = p0 - try { - ParseTreeWalker.DEFAULT.walk(listener, root) - listener.lastExpressionResult ?: error("no result") - } catch (e: ExpressionException) { - error(p0) - } - } - } catch (e: ExpressionException) { - return error - } -} - -/** - * Compile a (Double, Double)->Double function from an expression string - * @param expression the expression string to be compiled - * @param parameter0 the name of the first parameter - * @param parameter1 the name of the second parameter - * @param constants a map of named constant values that can be referred from the expression - * @param functions a map of named functions that can be invoked from the expression - * @param error in case the expression fails to compile or evaluate, this function is invoked instead - */ -fun compileFunction2( - expression: String, - parameter0: String, - parameter1: String, - constants: Map = mapOf(), - functions: FunctionExtensions = FunctionExtensions.EMPTY, - error: (p0: Double, p1: Double) -> Double = { _, _ -> 0.0 }, -): (p0: Double, p1: Double) -> Double { - require(!constants.containsKey(parameter0)) - require(!constants.containsKey(parameter1)) - try { - val root = expressionRoot(expression) - val variables = mutableMapOf() - variables.putAll(constants) - val listener = ExpressionListener(functions, variables) - - return { p0, p1 -> - variables[parameter0] = p0 - variables[parameter1] = p1 - try { - ParseTreeWalker.DEFAULT.walk(listener, root) - listener.lastExpressionResult ?: error("no result") - } catch (e: ExpressionException) { - error(p0, p1) - } - } - } catch (e: ExpressionException) { - return error - } -} - -/** - * Compile a (Double, Double, Double)->Double function from an expression string - * @param expression the expression string to be compiled - * @param parameter0 the name of the first parameter - * @param parameter1 the name of the second parameter - * @param parameter2 the name of the third parameter - * @param constants a map of named constant values that can be referred from the expression - * @param functions a map of named functions that can be invoked from the expression - * @param error in case the expression fails to compile or evaluate, this function is invoked instead - */ -fun compileFunction3( - expression: String, - parameter0: String, - parameter1: String, - parameter2: String, - constants: Map = mapOf(), - functions: FunctionExtensions = FunctionExtensions.EMPTY, - error: (p0: Double, p1: Double, p2: Double) -> Double = { _, _, _ -> 0.0 } -): (p0: Double, p1: Double, p2: Double) -> Double { - require(!constants.containsKey(parameter0)) - require(!constants.containsKey(parameter1)) - require(!constants.containsKey(parameter2)) - - try { - val root = expressionRoot(expression) - val variables = mutableMapOf() - variables.putAll(constants) - val listener = ExpressionListener(functions, variables) - - return { p0, p1, p2 -> - variables[parameter0] = p0 - variables[parameter1] = p1 - variables[parameter2] = p2 - try { - ParseTreeWalker.DEFAULT.walk(listener, root) - listener.lastExpressionResult ?: error("no result") - } catch (e: ExpressionException) { - error(p0, p1, p2) - } - } - } catch(e: ExpressionException) { - return error - } -} diff --git a/orx-expression-evaluator/src/commonMain/kotlin/ExpressionDelegate.kt b/orx-expression-evaluator/src/commonMain/kotlin/ExpressionDelegate.kt deleted file mode 100644 index 0b2ced2a..00000000 --- a/orx-expression-evaluator/src/commonMain/kotlin/ExpressionDelegate.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.openrndr.extra.expressions - -import org.openrndr.extra.propertywatchers.watchingProperty -import kotlin.reflect.KProperty0 - -fun watchingExpression1( - expressionProperty: KProperty0, - parameter0: String = "x", - constants: Map = emptyMap(), - functions: FunctionExtensions = FunctionExtensions.EMPTY, - error: (p0: Double) -> Double = { 0.0 } -) = - watchingProperty(expressionProperty) { - compileFunction1(it, parameter0, constants, functions, error) - } - -fun watchingExpression2( - expressionProperty: KProperty0, - parameter0: String = "x", - parameter1: String = "y", - constants: Map = emptyMap(), - functions: FunctionExtensions = FunctionExtensions.EMPTY, - error: (p0: Double, p1: Double) -> Double = { _, _ -> 0.0 } -) = - watchingProperty(expressionProperty) { - compileFunction2(it, parameter0, parameter1, constants, functions, error) - } - -fun watchingExpression3( - expressionProperty: KProperty0, - parameter0: String = "x", - parameter1: String = "y", - parameter2: String = "z", - constants: Map = emptyMap(), - functions: FunctionExtensions = FunctionExtensions.EMPTY, - error: (p0: Double, p1: Double, p2: Double) -> Double = { _, _, _ -> 0.0 } -) = - watchingProperty(expressionProperty) { - compileFunction3(it, parameter0, parameter1, parameter2, constants, functions, error) - } \ No newline at end of file diff --git a/orx-expression-evaluator/src/commonMain/kotlin/Expressions.kt b/orx-expression-evaluator/src/commonMain/kotlin/Expressions.kt deleted file mode 100644 index 6a3f6b61..00000000 --- a/orx-expression-evaluator/src/commonMain/kotlin/Expressions.kt +++ /dev/null @@ -1,488 +0,0 @@ -package org.openrndr.extra.expressions - - - -import org.antlr.v4.kotlinruntime.* -import org.antlr.v4.kotlinruntime.tree.ParseTreeWalker -import org.antlr.v4.kotlinruntime.tree.TerminalNode -import org.openrndr.collections.pop -import org.openrndr.collections.push -import org.openrndr.extra.expressions.parser.KeyLangLexer -import org.openrndr.extra.expressions.parser.KeyLangParser -import org.openrndr.extra.expressions.parser.KeyLangParserBaseListener - -import org.openrndr.extra.noise.uniform -import org.openrndr.math.* -import kotlin.math.* - -typealias Function0 = () -> Double -typealias Function1 = (Double) -> Double -typealias Function2 = (Double, Double) -> Double -typealias Function3 = (Double, Double, Double) -> Double -typealias Function4 = (Double, Double, Double, Double) -> Double -typealias Function5 = (Double, Double, Double, Double, Double) -> Double - -class FunctionExtensions( - val functions0: Map = emptyMap(), - val functions1: Map = emptyMap(), - val functions2: Map = emptyMap(), - val functions3: Map = emptyMap(), - val functions4: Map = emptyMap(), - val functions5: Map = emptyMap() -) { - companion object { - val EMPTY = FunctionExtensions() - } -} - -internal enum class IDType { - VARIABLE, - FUNCTION0, - FUNCTION1, - FUNCTION2, - FUNCTION3, - FUNCTION4, - FUNCTION5 -} - -internal class ExpressionListener( - val functions: FunctionExtensions = FunctionExtensions.EMPTY, - val constants: Map = mapOf() -) : - KeyLangParserBaseListener() { - val doubleStack = ArrayDeque() - val functionStack = ArrayDeque<(DoubleArray) -> Double>() - - - val idTypeStack = ArrayDeque() - var lastExpressionResult: Double? = null - - val exceptionStack = ArrayDeque() - - - override fun exitExpressionStatement(ctx: KeyLangParser.ExpressionStatementContext) { - ifError { - throw ExpressionException("error in evaluation of '${ctx.text}': ${it.message ?: ""}") - } - val result = doubleStack.pop() - lastExpressionResult = result - } - -// override fun exitAssignment(ctx: KeyLangParser.AssignmentContext) { -// val value = doubleStack.pop() -// variables[ctx.ID()?.text ?: error("buh")] = value -// } - - override fun exitMinusExpression(ctx: KeyLangParser.MinusExpressionContext) { - val op = doubleStack.pop() - doubleStack.push(-op) - } - - override fun exitBinaryOperation1(ctx: KeyLangParser.BinaryOperation1Context) { - ifError { - pushError(it.message ?: "") - return - } - - val right = doubleStack.pop() - val left = doubleStack.pop() - val result = when (val operator = ctx.operator?.type) { - KeyLangLexer.Tokens.PLUS -> left + right - KeyLangParser.Tokens.MINUS -> left - right - KeyLangParser.Tokens.ASTERISK -> left * right - KeyLangParser.Tokens.DIVISION -> left / right - KeyLangParser.Tokens.PERCENTAGE -> left.mod(right) - else -> error("operator '$operator' not implemented") - } - doubleStack.push(result) - } - - override fun exitBinaryOperation2(ctx: KeyLangParser.BinaryOperation2Context) { - ifError { - pushError(it.message ?: "") - return - } - - val left = doubleStack.pop() - val right = doubleStack.pop() - val result = when (val operator = ctx.operator?.type) { - KeyLangParser.Tokens.PLUS -> left + right - KeyLangParser.Tokens.MINUS -> right - left - KeyLangParser.Tokens.ASTERISK -> left * right - KeyLangParser.Tokens.DIVISION -> left / right - else -> error("operator '$operator' not implemented") - } - doubleStack.push(result) - } - - override fun enterValueReference(ctx: KeyLangParser.ValueReferenceContext) { - idTypeStack.push(IDType.VARIABLE) - } - - override fun enterFunctionCall0Expression(ctx: KeyLangParser.FunctionCall0ExpressionContext) { - idTypeStack.push(IDType.FUNCTION0) - } - - override fun exitFunctionCall0Expression(ctx: KeyLangParser.FunctionCall0ExpressionContext) { - ifError { - pushError(it.message ?: "") - return - } - - val function = functionStack.pop() - val result = function.invoke(doubleArrayOf()) - doubleStack.push(result) - } - - override fun enterFunctionCall1Expression(ctx: KeyLangParser.FunctionCall1ExpressionContext) { - idTypeStack.push(IDType.FUNCTION1) - } - - override fun exitFunctionCall1Expression(ctx: KeyLangParser.FunctionCall1ExpressionContext) { - ifError { - pushError(it.message ?: "") - return - } - - val function = functionStack.pop() - val argument = doubleStack.pop() - - val result = function.invoke(doubleArrayOf(argument)) - doubleStack.push(result) - } - - override fun enterFunctionCall2Expression(ctx: KeyLangParser.FunctionCall2ExpressionContext) { - idTypeStack.push(IDType.FUNCTION2) - } - - override fun exitFunctionCall2Expression(ctx: KeyLangParser.FunctionCall2ExpressionContext) { - ifError { - pushError(it.message ?: "") - return - } - - val function = functionStack.pop() - val argument1 = doubleStack.pop() - val argument0 = doubleStack.pop() - - val result = function.invoke(doubleArrayOf(argument0, argument1)) - doubleStack.push(result) - } - - override fun enterFunctionCall3Expression(ctx: KeyLangParser.FunctionCall3ExpressionContext) { - idTypeStack.push(IDType.FUNCTION3) - } - - override fun exitFunctionCall3Expression(ctx: KeyLangParser.FunctionCall3ExpressionContext) { - ifError { - pushError(it.message ?: "") - return - } - - val function = functionStack.pop() - val argument2 = doubleStack.pop() - val argument1 = doubleStack.pop() - val argument0 = doubleStack.pop() - - val result = function.invoke(doubleArrayOf(argument0, argument1, argument2)) - doubleStack.push(result) - } - - override fun enterFunctionCall4Expression(ctx: KeyLangParser.FunctionCall4ExpressionContext) { - idTypeStack.push(IDType.FUNCTION4) - } - - override fun exitFunctionCall4Expression(ctx: KeyLangParser.FunctionCall4ExpressionContext) { - ifError { - pushError(it.message ?: "") - return - } - - val function = functionStack.pop() - val argument3 = doubleStack.pop() - val argument2 = doubleStack.pop() - val argument1 = doubleStack.pop() - val argument0 = doubleStack.pop() - - val result = function.invoke(doubleArrayOf(argument0, argument1, argument2, argument3)) - doubleStack.push(result) - } - - - override fun enterFunctionCall5Expression(ctx: KeyLangParser.FunctionCall5ExpressionContext) { - idTypeStack.push(IDType.FUNCTION5) - } - - override fun exitFunctionCall5Expression(ctx: KeyLangParser.FunctionCall5ExpressionContext) { - ifError { - pushError(it.message ?: "") - return - } - - val function = functionStack.pop() - val argument4 = doubleStack.pop() - val argument3 = doubleStack.pop() - val argument2 = doubleStack.pop() - val argument1 = doubleStack.pop() - val argument0 = doubleStack.pop() - - val result = function.invoke(doubleArrayOf(argument0, argument1, argument2, argument3, argument4)) - doubleStack.push(result) - } - - private fun errorValue(message: String, value: T): T { - pushError(message) - return value - } - - private fun pushError(message: String) { - exceptionStack.push(ExpressionException(message)) - } - - private inline fun ifError(f: (e: Throwable) -> Unit) { - if (exceptionStack.isNotEmpty()) { - val e = exceptionStack.pop() - f(e) - } - } - - override fun visitTerminal(node: TerminalNode) { - val type = node.symbol?.type - if (type == KeyLangParser.Tokens.INTLIT) { - doubleStack.push(node.text.toDouble()) - } - if (type == KeyLangParser.Tokens.DECLIT) { - doubleStack.push(node.text.toDouble()) - } - if (type == KeyLangParser.Tokens.ID) { - val name = node.text.replace("`", "") - @Suppress("DIVISION_BY_ZERO") - when (val idType = idTypeStack.pop()) { - IDType.VARIABLE -> doubleStack.push( - when (name) { - "PI" -> PI - else -> constants[name] ?: errorValue("unresolved value: '${name}'. available values: ${constants}", 0.0 / 0.0) - } - ) - - IDType.FUNCTION0 -> { - val function: (DoubleArray) -> Double = - when (name) { - "random" -> { _ -> Double.uniform(0.0, 1.0) } - else -> functions.functions0[name]?.let { { _: DoubleArray -> it.invoke() } } - ?: errorValue( - "unresolved function: '${name}()'" - ) { _ -> error("this is the error function") } - } - functionStack.push(function) - } - - IDType.FUNCTION1 -> { - val function: (DoubleArray) -> Double = - when (name) { - "sqrt" -> { x -> sqrt(x[0]) } - "radians" -> { x -> x[0].asRadians } - "degrees" -> { x -> x[0].asDegrees } - "cos" -> { x -> cos(x[0]) } - "sin" -> { x -> sin(x[0]) } - "tan" -> { x -> tan(x[0]) } - "atan" -> { x -> atan(x[0]) } - "acos" -> { x -> acos(x[0]) } - "asin" -> { x -> asin(x[0]) } - "exp" -> { x -> exp(x[0]) } - "abs" -> { x -> abs(x[0]) } - "floor" -> { x -> floor(x[0]) } - "round" -> { x -> round(x[0]) } - "ceil" -> { x -> ceil(x[0]) } - "saturate" -> { x -> x[0].coerceIn(0.0, 1.0) } - else -> functions.functions1[name]?.let { { x: DoubleArray -> it.invoke(x[0]) } } - ?: errorValue( - "unresolved function: '${name}(x0)'" - ) { _ -> error("this is the error function") } - } - functionStack.push(function) - } - - IDType.FUNCTION2 -> { - val function: (DoubleArray) -> Double = - when (name) { - "max" -> { x -> max(x[0], x[1]) } - "min" -> { x -> min(x[0], x[1]) } - "pow" -> { x -> x[0].pow(x[1]) } - "mod" -> { x -> x[0].mod(x[1]) } - "atan2" -> { x -> atan2(x[0], x[1]) } - "random" -> { x -> Double.uniform(x[0], x[1]) } - "length" -> { x -> Vector2(x[0], x[1]).length } - else -> functions.functions2[name]?.let { { x: DoubleArray -> it.invoke(x[0], x[1]) } } - ?: errorValue( - "unresolved function: '${name}(x0, x1)'" - ) { _ -> error("this is the error function") } - } - functionStack.push(function) - } - - IDType.FUNCTION3 -> { - val function: (DoubleArray) -> Double = - when (name) { - "mix" -> { x -> mix(x[0], x[1], x[2]) } - "min" -> { x -> x.minOrNull()!! } - "max" -> { x -> x.maxOrNull()!! } - "sum" -> { x -> x.sum() } - "smoothstep" -> { x -> smoothstep(x[0], x[1], x[2]) } - "length" -> { x -> Vector3(x[0], x[1], x[2]).length } - else -> functions.functions3[name]?.let { - { x: DoubleArray -> - it.invoke( - x[0], - x[1], - x[2] - ) - } - } - ?: errorValue( - "unresolved function: '${name}(x0, x1, x2)'" - ) { _ -> error("this is the error function") } - } - functionStack.push(function) - } - - IDType.FUNCTION4 -> { - val function: (DoubleArray) -> Double = - when (name) { - "min" -> { x -> x.minOrNull()!! } - "max" -> { x -> x.maxOrNull()!! } - "sum" -> { x -> x.sum() } - else -> functions.functions4[name]?.let { - { x: DoubleArray -> - it.invoke( - x[0], - x[1], - x[2], - x[3] - ) - } - } - ?: errorValue( - "unresolved function: '${name}(x0, x1, x2, x3)'" - ) { _ -> error("this is the error function") } - } - functionStack.push(function) - } - - IDType.FUNCTION5 -> { - val function: (DoubleArray) -> Double = - when (name) { - "min" -> { x -> x.minOrNull()!! } - "max" -> { x -> x.maxOrNull()!! } - "sum" -> { x -> x.sum() } - "map" -> { x -> map(x[0], x[1], x[2], x[3], x[4]) } - else -> functions.functions5[name]?.let { - { x: DoubleArray -> - it.invoke( - x[0], - x[1], - x[2], - x[3], - x[4] - ) - } - } - ?: errorValue( - "unresolved function: '${name}(x0, x1, x2, x3, x4)'" - ) { _ -> error("this is the error function") } - } - functionStack.push(function) - } - - else -> error("unsupported id-type $idType") - } - } - } -} - -class ExpressionException(message: String) : RuntimeException(message) - -fun evaluateExpression( - expression: String, - constants: Map = emptyMap(), - functions: FunctionExtensions = FunctionExtensions.EMPTY -): Double? { - val lexer = KeyLangLexer(CharStreams.fromString(expression)) - val parser = KeyLangParser(CommonTokenStream(lexer)) - parser.removeErrorListeners() - parser.addErrorListener(object : BaseErrorListener() { - override fun syntaxError( - recognizer: Recognizer<*, *>, - offendingSymbol: Any?, - line: Int, - charPositionInLine: Int, - msg: String, - e: RecognitionException? - ) { - throw ExpressionException("parser error in expression: '$expression'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]") - } - }) - - val root = parser.keyLangFile() - val listener = ExpressionListener(functions, constants) - try { - ParseTreeWalker.DEFAULT.walk(listener, root) - } catch (e: ExpressionException) { - throw ExpressionException(e.message ?: "") - } - return listener.lastExpressionResult -} - -fun compileExpression( - expression: String, - constants: Map = emptyMap(), - functions: FunctionExtensions = FunctionExtensions.EMPTY -): () -> Double { - val lexer = KeyLangLexer(CharStreams.fromString(expression)) - val parser = KeyLangParser(CommonTokenStream(lexer)) - parser.removeErrorListeners() - parser.addErrorListener(object : BaseErrorListener() { - override fun syntaxError( - recognizer: Recognizer<*, *>, - offendingSymbol: Any?, - line: Int, - charPositionInLine: Int, - msg: String, - e: RecognitionException? - ) { - throw ExpressionException("parser error in expression: '$expression'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]") - } - }) - val root = parser.keyLangFile() - val listener = ExpressionListener(functions, constants) - - - return { - try { - ParseTreeWalker.DEFAULT.walk(listener, root) - } catch (e: ExpressionException) { - throw ExpressionException(e.message ?: "") - } - listener.lastExpressionResult ?: error("no result") - } -} - -internal fun expressionRoot(expression: String): KeyLangParser.KeyLangFileContext { - val lexer = KeyLangLexer(CharStreams.fromString(expression)) - val parser = KeyLangParser(CommonTokenStream(lexer)) - parser.removeErrorListeners() - parser.addErrorListener(object : BaseErrorListener() { - override fun syntaxError( - recognizer: Recognizer<*, *>, - offendingSymbol: Any?, - line: Int, - charPositionInLine: Int, - msg: String, - e: RecognitionException? - ) { - throw ExpressionException("parser error in expression: '$expression'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]") - } - }) - return parser.keyLangFile() -} - diff --git a/orx-expression-evaluator/src/jvmDemo/kotlin/DemoExpressionEvaluator01.kt b/orx-expression-evaluator/src/jvmDemo/kotlin/DemoExpressionEvaluator01.kt deleted file mode 100644 index d3132b23..00000000 --- a/orx-expression-evaluator/src/jvmDemo/kotlin/DemoExpressionEvaluator01.kt +++ /dev/null @@ -1,44 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.expressions.evaluateExpression -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.gui.addTo -import org.openrndr.extra.parameters.TextParameter - -fun main() = application { - program { - val gui = GUI() - gui.compartmentsCollapsedByDefault = false - - val settings = object { - @TextParameter("x expression", order = 10) - var xExpression = "cos(t) * 50.0 + width / 2.0" - - @TextParameter("y expression", order = 20) - var yExpression = "sin(t) * 50.0 + height / 2.0" - - @TextParameter("radius expression", order = 30) - var radiusExpression = "cos(t) * 50.0 + 50.0" - }.addTo(gui) - - extend(gui) - extend { - //gui.visible = mouse.position.x < 200.0 - - val expressionContext = - mapOf("t" to seconds, "width" to drawer.bounds.width, "height" to drawer.bounds.height) - - fun eval(expression: String): Double = - try { - evaluateExpression(expression, expressionContext) ?: 0.0 - } catch (e: Throwable) { - 0.0 - } - - val x = eval(settings.xExpression) - val y = eval(settings.yExpression) - val radius = eval(settings.radiusExpression) - - drawer.circle(x, y, radius) - } - } -} diff --git a/orx-expression-evaluator/src/jvmDemo/kotlin/DemoExpressionEvaluator02.kt b/orx-expression-evaluator/src/jvmDemo/kotlin/DemoExpressionEvaluator02.kt deleted file mode 100644 index 2bb1788e..00000000 --- a/orx-expression-evaluator/src/jvmDemo/kotlin/DemoExpressionEvaluator02.kt +++ /dev/null @@ -1,42 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.expressions.watchingExpression1 -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.gui.addTo -import org.openrndr.extra.parameters.TextParameter - -/** - * Improved version of DemoExpressionEvaluator01, it uses [watchingExpression1] to automatically convert an expression - * string into a function with a parameter "t". - */ -fun main() = application { - program { - val gui = GUI() - gui.compartmentsCollapsedByDefault = false - - // the constants used in our expressions - val constants = mutableMapOf("width" to drawer.width.toDouble(), "height" to drawer.height.toDouble()) - - val settings = object { - @TextParameter("x expression", order = 10) - var xExpression = "cos(t) * 50.0 + width / 2.0" - - @TextParameter("y expression", order = 20) - var yExpression = "sin(t) * 50.0 + height / 2.0" - - @TextParameter("radius expression", order = 30) - var radiusExpression = "cos(t) * 50.0 + 50.0" - }.addTo(gui) - - val xFunction by watchingExpression1(settings::xExpression, "t", constants) - val yFunction by watchingExpression1(settings::yExpression, "t", constants) - val radiusFunction by watchingExpression1(settings::radiusExpression, "t", constants) - - extend(gui) - extend { - val x = xFunction(seconds) - val y = yFunction(seconds) - val radius = radiusFunction(seconds) - drawer.circle(x, y, radius) - } - } -} diff --git a/orx-expression-evaluator/src/jvmTest/kotlin/TestCompiledExpression.kt b/orx-expression-evaluator/src/jvmTest/kotlin/TestCompiledExpression.kt deleted file mode 100644 index 3abcc478..00000000 --- a/orx-expression-evaluator/src/jvmTest/kotlin/TestCompiledExpression.kt +++ /dev/null @@ -1,32 +0,0 @@ -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.openrndr.extra.expressions.ExpressionException -import org.openrndr.extra.expressions.compileExpression -import kotlin.test.assertEquals - -class TestCompiledExpression { - @Test - fun `a simple compiled expression`() { - val expression = "someValue" - val function = compileExpression(expression, constants = mutableMapOf("someValue" to 5.0)) - assertEquals(5.0, function()) - } - - @Test - fun `a compiled expression with updated context`() { - val expression = "someValue" - val context = mutableMapOf("someValue" to 5.0) - val function = compileExpression(expression, constants = context) - assertEquals(5.0, function()) - context["someValue"] = 6.0 - assertEquals(6.0, function()) - } - - @Test - fun `an erroneous compiled expression`() { - val expression = "1bork" - assertThrows { - compileExpression(expression, constants = mutableMapOf("someValue" to 5.0)) - } - } -} \ No newline at end of file diff --git a/orx-expression-evaluator/src/jvmTest/kotlin/TestCompiledFunctions.kt b/orx-expression-evaluator/src/jvmTest/kotlin/TestCompiledFunctions.kt deleted file mode 100644 index 709db5da..00000000 --- a/orx-expression-evaluator/src/jvmTest/kotlin/TestCompiledFunctions.kt +++ /dev/null @@ -1,29 +0,0 @@ -import org.junit.jupiter.api.Test -import org.openrndr.extra.expressions.compileFunction1 -import org.openrndr.extra.expressions.compileFunction2 -import org.openrndr.extra.expressions.compileFunction3 -import kotlin.test.assertEquals - -class TestCompiledFunctions { - @Test - fun `a simple compiled function1`() { - val expression = "t" - val function = compileFunction1(expression, "t") - assertEquals(-5.0, function(-5.0)) - assertEquals(5.0, function(5.0)) - } - - @Test - fun `a simple compiled function2`() { - val expression = "x + y" - val function = compileFunction2(expression, "x", "y") - assertEquals(3.0, function(1.0, 2.0)) - } - - @Test - fun `a simple compiled function3`() { - val expression = "x + y + z" - val function = compileFunction3(expression, "x", "y", "z") - assertEquals(6.0, function(1.0, 2.0, 3.0)) - } -} \ No newline at end of file diff --git a/orx-expression-evaluator/src/jvmTest/kotlin/TestExpressionDelegates.kt b/orx-expression-evaluator/src/jvmTest/kotlin/TestExpressionDelegates.kt deleted file mode 100644 index c69b8405..00000000 --- a/orx-expression-evaluator/src/jvmTest/kotlin/TestExpressionDelegates.kt +++ /dev/null @@ -1,15 +0,0 @@ -import org.openrndr.extra.expressions.watchingExpression1 -import kotlin.test.Test -import kotlin.test.assertEquals - -class TestExpressionDelegates { - - @Test - fun test() { - val state = object { - var expression = "x * x" - val function1 by watchingExpression1(::expression, "x") - } - assertEquals(25.0, state.function1(5.0)) - } -} \ No newline at end of file diff --git a/orx-expression-evaluator/src/jvmTest/kotlin/TestExpressionErrors.kt b/orx-expression-evaluator/src/jvmTest/kotlin/TestExpressionErrors.kt deleted file mode 100644 index d4876af0..00000000 --- a/orx-expression-evaluator/src/jvmTest/kotlin/TestExpressionErrors.kt +++ /dev/null @@ -1,51 +0,0 @@ -import org.junit.jupiter.api.assertThrows -import org.openrndr.extra.expressions.ExpressionException -import org.openrndr.extra.expressions.evaluateExpression - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith - -class TestExpressionErrors { - - @Test - fun `an expression with non-sensible writing`() { - val expression = ")(" - assertThrows { - evaluateExpression(expression) - } - } - - - @Test - fun `an expression trying to reassign a number`() { - val expression = "3 = 5" - assertThrows { - evaluateExpression(expression) - } - } - - @Test - fun `an expression that uses non-existing functions`() { - val expression = "notExisting(5)" - val exception = assertFailsWith { - evaluateExpression(expression) - } - assertEquals( - "error in evaluation of 'notExisting(5)': unresolved function: 'notExisting(x0)'", - exception.message - ) - } - - @Test - fun `an expression that uses non-existing variables`() { - val expression = "notExisting + 4" - val exception = assertFailsWith { - evaluateExpression(expression) - } - assertEquals( - "error in evaluation of 'notExisting+4': unresolved value: 'notExisting'. available values: {}", - exception.message - ) - } -} diff --git a/orx-expression-evaluator/src/jvmTest/kotlin/TestExpressions.kt b/orx-expression-evaluator/src/jvmTest/kotlin/TestExpressions.kt deleted file mode 100644 index 1a4b9549..00000000 --- a/orx-expression-evaluator/src/jvmTest/kotlin/TestExpressions.kt +++ /dev/null @@ -1,198 +0,0 @@ -import org.openrndr.extra.expressions.FunctionExtensions -import org.openrndr.extra.expressions.evaluateExpression - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull - -class TestExpressions { - @Test - fun `a value reference`() { - val expression = "someValue" - val result = evaluateExpression(expression, constants = mapOf("someValue" to 5.0)) - assertEquals(5.0, result) - } - - @Test - fun `a backticked value reference`() { - val expression = "`some-value`" - val result = evaluateExpression(expression, constants = mapOf("some-value" to 5.0)) - assertEquals(5.0, result) - } - - - @Test - fun `a function call`() { - val expression = "sqrt(4.0)" - val result = evaluateExpression(expression) - assertNotNull(result) - assertEquals(2.0, result, 10E-6) - } - - @Test - fun `a function call with the name in backticks`() { - val expression = "`sqrt`(4.0)" - val result = evaluateExpression(expression) - assertNotNull(result) - assertEquals(2.0, result, 10E-6) - } - - @Test - fun `two function calls`() { - val expression = "sqrt(4.0) * sqrt(4.0)" - val result = evaluateExpression(expression) - assertNotNull(result) - assertEquals(4.0, result, 10E-6) - } - - @Test - fun `two argument max function call`() { - val expression = "max(0.0, 4.0)" - val result = evaluateExpression(expression) - assertNotNull(result) - assertEquals(4.0, result, 10E-6) - } - - @Test - fun `two argument min function call`() { - val expression = "min(8.0, 4.0)" - val result = evaluateExpression(expression) - assertNotNull(result) - assertEquals(4.0, result, 10E-6) - } - - @Test - fun `three argument function call`() { - val expression = "mix(8.0, 4.0, 0.5)" - val result = evaluateExpression(expression) - assertNotNull(result) - assertEquals(6.0, result, 10E-6) - } - - @Test - fun `five argument function call`() { - val expression = "map(0.0, 1.0, 0.0, 8.0, 0.5)" - val result = evaluateExpression(expression) - assertNotNull(result) - assertEquals(4.0, result, 10E-6) - } - - @Test - fun `two argument function call, where argument order matters`() { - val expression = "pow(2.0, 3.0)" - val result = evaluateExpression(expression) - assertNotNull(result) - assertEquals(8.0, result, 10E-6) - } - - @Test - fun `nested function call`() { - val expression = "sqrt(min(8.0, 4.0))" - val result = evaluateExpression(expression) - assertNotNull(result) - assertEquals(2.0, result, 10E-6) - } - - @Test - fun `extension function0 call`() { - val expression = "extension()" - val result = evaluateExpression( - expression, - functions = FunctionExtensions( - functions0 = mapOf("extension" to { 2.0 }) - ) - ) - assertNotNull(result) - assertEquals(2.0, result, 10E-6) - } - - @Test - fun `extension function1 call`() { - val expression = "extension(1.0)" - val result = evaluateExpression( - expression, - functions = FunctionExtensions( - functions1 = mapOf("extension" to { x -> - x * 2.0 - }) - ) - ) - assertNotNull(result) - assertEquals(2.0, result, 10E-6) - } - - @Test - fun `extension function1 call with dashed name in backticks`() { - val expression = "`extension-function`(1.0)" - val result = evaluateExpression( - expression, - functions = FunctionExtensions( - functions1 = mapOf("extension-function" to { x -> - x * 2.0 - }) - ) - ) - assertNotNull(result) - assertEquals(2.0, result, 10E-6) - } - - @Test - fun `extension function2 call`() { - val expression = "extension(1.0, 1.0)" - val result = evaluateExpression( - expression, - functions = FunctionExtensions( - functions2 = mapOf("extension" to { x, y -> - x + y - }) - ) - ) - assertNotNull(result) - assertEquals(2.0, result, 10E-6) - } - - @Test - fun `extension function3 call`() { - val expression = "extension(1.0, 1.0, 1.0)" - val result = evaluateExpression( - expression, - functions = FunctionExtensions( - functions3 = mapOf("extension" to { x, y, z -> - x + y + z - }) - ) - ) - assertNotNull(result) - assertEquals(3.0, result, 10E-6) - } - - @Test - fun `extension function4 call`() { - val expression = "extension(1.0, 1.0, 1.0, 1.0)" - val result = evaluateExpression( - expression, - functions = FunctionExtensions( - functions4 = mapOf("extension" to { x, y, z, w -> - x + y + z + w - }) - ) - ) - assertNotNull(result) - assertEquals(4.0, result, 10E-6) - } - - @Test - fun `extension function5 call`() { - val expression = "extension(1.0, 1.0, 1.0, 1.0, 1.0)" - val result = evaluateExpression( - expression, - functions = FunctionExtensions( - functions5 = mapOf("extension" to { x, y, z, w, u -> - x + y + z + w + u - }) - ) - ) - assertNotNull(result) - assertEquals(5.0, result, 10E-6) - } -} \ No newline at end of file diff --git a/orx-expression-evaluator/src/jvmTest/kotlin/TestOperators.kt b/orx-expression-evaluator/src/jvmTest/kotlin/TestOperators.kt deleted file mode 100644 index 9f01a60d..00000000 --- a/orx-expression-evaluator/src/jvmTest/kotlin/TestOperators.kt +++ /dev/null @@ -1,62 +0,0 @@ -import org.openrndr.extra.expressions.evaluateExpression -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull - -class TestOperators { - @Test - fun `an addition operation`() { - val result = evaluateExpression("1 + 2") - assertNotNull(result) - assertEquals(3.0, result, 10E-6) - } - - @Test - fun `a subtraction operation`() { - val result = evaluateExpression("1 - 2") - assertNotNull(result) - assertEquals(-1.0, result, 10E-6) - } - - @Test - fun `a modulus operation`() { - val result = evaluateExpression("4 % 2") - assertNotNull(result) - assertEquals(0.0, result, 10E-6) - } - - @Test - fun `a multiplication operation`() { - val result = evaluateExpression("4 * 2") - assertNotNull(result) - assertEquals(8.0, result, 10E-6) - } - - @Test - fun `a division operation`() { - val result = evaluateExpression("4 / 2") - assertNotNull(result) - assertEquals(2.0, result, 10E-6) - } - - @Test - fun `a multiplication and addition operation`() { - val result = evaluateExpression("4 * 2 + 1") - assertNotNull(result) - assertEquals(9.0, result, 10E-6) - } - - @Test - fun `an addition and multiplication`() { - val result = evaluateExpression("4 + 2 * 3") - assertNotNull(result) - assertEquals(10.0, result, 10E-6) - } - - @Test - fun `unary minus`() { - val result = evaluateExpression("-4.0") - assertNotNull(result) - assertEquals(-4.0, result, 10E-6) - } -} diff --git a/orx-fcurve/README.md b/orx-fcurve/README.md deleted file mode 100644 index 0e135606..00000000 --- a/orx-fcurve/README.md +++ /dev/null @@ -1,233 +0,0 @@ -# orx-fcurve - -FCurves are 1 dimensional function curves constructed from 2D bezier functions. -They are often used to control a property over time. -`x` values don't have any units, but they often represent a duration in seconds. - -The language to express FCurves is similar to SVG's path language. - -| Relative command | Absolute command | Description | -|---------------------|---------------------|--------------------------------------------------------------| -| `m y` | `M y` | move the pen only in the y-direction | -| `h x` | | hold a value to draw a horizontal line | -| | `H x` | shift curve in time by x. Can only be used as first command. | -| `l x,y` | `L x,y` | line to (x, y) | -| `q x0,y0,x,y` | `Q x0,y0,x,y` | quadratic bezier to (x,y) and control-point (x0, y0) | -| `c x0,y0,x1,y1,x,y` | `C x0,y0,x1,y1,x,y` | cubic bezier to (x,y) and control-points (x0, y0), (x1, y1) | -| `t x,y` | `T x,y` | quadratic smooth to (x, y) | -| `s x1,y1,x,y` | `S x1,y1,x,y` | cubic smooth to (x,y) and control point (x1, y1) | - -## Examples - -This is an example of a flat horizontal FCurve: - -```kotlin -// set the initial value to 0.5, hold that value for 1 seconds -val sizeCurve = fcurve("M0.5 h1") -``` - -Two horizontal segments at different heights: - -```kotlin -// hold value 0.4 for half second, then hold value 0.6 for half second -val sizeCurve = fcurve("M0.4 h0.5 M0.6 h0.5") -``` - -Note that `x` values are relative, except for `H` where `x` is absolute. -For `y` values, lower case commands are relative and upper case commands are absolute. - - -### Line - -We can interpolate from height 0.2 to 0.8 in 2 seconds like this: - -```kotlin -// set initial value to 0.2, then interpolate linearly to value 0.8 over 2 seconds -val sizeCurve = fcurve("M0.2 L2,0.8") -``` - -Easily visualize the curves by calling the `.contours()` method. It will convert -the curve into a list of `ShapeContour` instances which are easy to draw using -`drawer.contours()`: - -```kotlin -val sizeCurve = fcurve("M0.2 L2,0.8") -drawer.contours(sizeCurve.contours()) -``` - -### Drawing scale - -Note that the bounding box of this last curve will have a width of 2.0 pixels and a height under 1.0 pixel. -In other words, almost invisible at its original scale. -Since this is a common situation the `.contours()` method accepts a -`Vector2` scale argument to control the rendering size: - -```kotlin -val sizeCurve = fcurve("M0.2 L2,0.8") -drawer.contours(sizeCurve.contours(Vector2(drawer.width / sizeCurve.duration, drawer.height.toDouble()))) -``` - -### Quadratic and Cubic curves - -The `Q` and `C` commands (and their lowercase counterparts) allow us to draw quadratic (one control point) -and cubic (two control points) curves. - -```kotlin -// A quadratic curve that starts at zero, with a control point at 1,0 and ending at 1,1. -// That's a curve that stays near the 0.0 value and quickly raises to 1.0 at the end. -val easeOutCurve = fcurve("M0.0 Q1.0,0.0,1.0,1.0") - -// A cubic s-shaped curve spending more time at both ends with a quick transition between them in the middle. -val easeInOutCurve = fcurve("M0.0 C1,0,0,1,1,1") -``` - -Note that new lines, white space and commas are optional. They can help with readability: -``` -M0 h10 -c 3,10 5,-10 8,0.5 -L 5,5 -``` - -### Smooth curves - -The `T` and `S` commands (and their lowercase counterparts) allow us to create smooth curves, where -one control point is automatically calculated to maintain the curve direction. The smooth curve -commands require the presence of a previous segment, otherwise the program will not run. - -```kotlin -// Hold the value 0.5 during 0.2 seconds -// then draw a smooth curve down to 0.5, up to 0.7 down to 0.3 and up to 0.7 -val smoothCurveT = fcurve("M0.5 h0.2 T0.2,0.3 T0.2,0.7 T0.2,0.3 T0.2,0.7") - -// Hold the value 0.5 during 0.2 seconds -// then draw a smooth with 4 repetitions where we move up slowly and down quickly -val smoothCurveS = fcurve("M0.5 h0.2 S0.2,0.0,0.2,0.5 S0.2,0.0,0.2,0.5 S0.2,0.0,0.2,0.5 S0.2,0.0,0.2,0.5") -``` - -## Useful FCurve methods - -Useful methods provided by FCurve: - -- `smoothCurveS.reverse()` returns a new reversed FCurve. -- `smoothCurveS.changeSpeed(0.5)` returns a new FCurve scaled horizontally. -- `smoothCurveS.duration` returns the duration of the FCurve. - -# Sampler - -Drawing FCurves is useful for debugging, but their typical use is for animation. -The FCurve sampler allows us to query values for the given time value like this: - -```kotlin -fun main() = application { - program { - val xCurve = fcurve( - """ - M320 H0.4 - S2,0, 2,320 - S2,0, 2,320 - S2,0, 2,320 - S2,0, 2,320 - T0.6,320 - """ - ) - val xCurveSampler = xCurve.sampler() - extend { - drawer.circle( - xCurveSampler(seconds % 9.0), - 240.0, - 20.0 - ) - } - } -} -``` - -In this example we used `% 9.0` to loop the time between 0.0 and 9.0, repeating the animation over and over. - -# EFCurves - -Extended Fcurves have an additional preprocessing step in which scalar expressions are evaluated. - -## Comments - -EFCurves support comments using the `#` character. - -`M0 h10 c3,10,5,-10,8,0.5 # L5,5` - - -``` -M0 h10 # setup the initial y value and hold it for 10 units. -c3,10,5,-10,8,0.5 # relative cubic bezier curve -# and a final line-to -L5,5 -``` - -## Expressions - -Expressions within curly brackets are evaluated using `orx-expression-evaluator`. -Please refer to its [documentation](https://github.com/openrndr/orx/tree/master/orx-expression-evaluator) for details on the expression language used. - -For example: `M0 L{3 * 4},4` evaluates to `M0 L12,4`. - -## Repetitions - -EFCurves add support for repetitions. Repetitions are expanded by replacing -occurrences of `()[]` with `number-of-repetitions` copies -of `text-to-repeat`. - -For example: - * `M0 (h1 m1)[3]` expands to `M0 h1 m1 h1 m1 h1 m1` - * `M0 (h1 m1)[0]` expands to `M0` - -### Nested repetitions - -Repetitions can be nested. - -For example `(M0 (h1 m1)[3])[2]` expands to `M0 h1 m1 h1 m1 h1 m1 M0 h1 m1 h1 m1 h1 m1`. - -### Interaction between repetitions and expressions - -`M0 (H{it + 1} m1)[3]` expands to `M0 H1 m1 H2 m1 H3 m1` - -`M0 (H{index + 1} m{it}){1.2, 1.3, 1.4}` expands to `M0 H1 m1.2 H2 m1.3 H3 m1.4` - - -# References - - * https://x.com/ruby0x1/status/1258252352672247814 - * https://blender.stackexchange.com/questions/52403/what-is-the-mathematical-basis-for-f-curves/52468#52468 - * https://pomax.github.io/bezierinfo/#yforx - - -## Demos -### DemoFCurve01 - - - -![DemoFCurve01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fcurve/images/DemoFCurve01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoFCurve01.kt) - -### DemoFCurve02 - - - -![DemoFCurve02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fcurve/images/DemoFCurve02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoFCurve02.kt) - -### DemoFCurveSheet01 - - - -![DemoFCurveSheet01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fcurve/images/DemoFCurveSheet01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoFCurveSheet01.kt) - -### DemoMultiFCurve01 - - - -![DemoMultiFCurve01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fcurve/images/DemoMultiFCurve01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoMultiFCurve01.kt) diff --git a/orx-fcurve/build.gradle.kts b/orx-fcurve/build.gradle.kts deleted file mode 100644 index c738682c..00000000 --- a/orx-fcurve/build.gradle.kts +++ /dev/null @@ -1,29 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") - alias(libs.plugins.kotlin.serialization) -} - -kotlin { - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - implementation(project(":orx-parameters")) - implementation(project(":orx-expression-evaluator")) - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - implementation(sharedLibs.kotlin.serialization.core) - } - } - - @Suppress("UNUSED_VARIABLE") - val jvmDemo by getting { - dependencies { - implementation(project(":orx-fcurve")) - implementation(project(":orx-noise")) - } - } - } -} \ No newline at end of file diff --git a/orx-fcurve/src/commonMain/kotlin/CompoundFCurve.kt b/orx-fcurve/src/commonMain/kotlin/CompoundFCurve.kt deleted file mode 100644 index ddb154f0..00000000 --- a/orx-fcurve/src/commonMain/kotlin/CompoundFCurve.kt +++ /dev/null @@ -1,229 +0,0 @@ -package org.openrndr.extra.fcurve - -import org.openrndr.color.ColorRGBa -import org.openrndr.math.* - -abstract class CompoundFCurve(val compounds: List, val compoundNames: List) { - val duration: Double - get() { - return compounds.maxOf { it?.duration ?: 0.0 } - } - - abstract fun value(t: Double, overrides: Map? = null): T - abstract fun sampler(normalized: Boolean = false): (Double) -> T -} - -class BooleanFCurve(value: Pair, val default: Boolean = true) : - CompoundFCurve(listOf(value.second), listOf(value.first)) { - override fun value(t: Double, overrides: Map?): Boolean { - val d = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) - return if (d != null) { - d >= 1.0 - } else { - default - } - } - - override fun sampler(normalized: Boolean): (Double) -> Boolean { - val sampler = compounds[0]?.sampler(normalized) ?: { if (default) 1.0 else 0.0 } - return { t -> sampler(t) >= 1.0 } - } -} - -class DoubleFCurve(value: Pair, val default: Double = 0.0) : - CompoundFCurve(listOf(value.second), listOf(value.first)) { - - override fun value(t: Double, overrides: Map?): Double { - return overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default - } - - override fun sampler(normalized: Boolean): (Double) -> Double { - val sampler = compounds[0]?.sampler(normalized) ?: { default } - return { t -> sampler(t) } - } -} - -class IntFCurve(value: Pair, val default: Int = 0) : - CompoundFCurve(listOf(value.second), listOf(value.first)) { - - override fun value(t: Double, overrides: Map?): Int { - val d = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) - return if (d != null) { - d.toInt() - } else { - default - } - } - - override fun sampler(normalized: Boolean): (Double) -> Int { - val sampler = compounds[0]?.sampler(normalized) ?: { default.toDouble() } - return { t -> sampler(t).toInt() } - } -} - -class Vector2FCurve( - x: Pair, y: Pair, - val default: Vector2 = Vector2.ZERO -) : - CompoundFCurve(listOf(x.second, y.second), listOf(x.first, y.first)) { - override fun value(t: Double, overrides: Map?): Vector2 { - val x = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.x - val y = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.y - return Vector2(x, y) - } - - override fun sampler(normalized: Boolean): (Double) -> Vector2 { - val xSampler = compounds[0]?.sampler(normalized) ?: { default.x } - val ySampler = compounds[1]?.sampler(normalized) ?: { default.y } - return { t -> Vector2(xSampler(t), ySampler(t)) } - } -} - -class Vector3FCurve( - x: Pair, - y: Pair, - z: Pair, - val default: Vector3 = Vector3.ZERO -) : - CompoundFCurve(listOf(x.second, y.second, z.second), listOf(x.first, y.first, z.first)) { - override fun value(t: Double, overrides: Map?): Vector3 { - val x = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.x - val y = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.y - val z = overrides?.get(compoundNames[2]) ?: compounds[2]?.value(t) ?: default.z - return Vector3(x, y, z) - } - - override fun sampler(normalized: Boolean): (Double) -> Vector3 { - val xSampler = compounds[0]?.sampler(normalized) ?: { default.x } - val ySampler = compounds[1]?.sampler(normalized) ?: { default.y } - val zSampler = compounds[2]?.sampler(normalized) ?: { default.z } - return { t -> Vector3(xSampler(t), ySampler(t), zSampler(t)) } - } -} - -class Vector4FCurve( - x: Pair, - y: Pair, - z: Pair, - w: Pair, - val default: Vector4 = Vector4.ZERO -) : - CompoundFCurve( - listOf(x.second, y.second, z.second, w.second), - listOf(x.first, y.first, z.first, w.first) - ) { - - override fun value(t: Double, overrides: Map?): Vector4 { - val x = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.x - val y = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.y - val z = overrides?.get(compoundNames[2]) ?: compounds[2]?.value(t) ?: default.z - val w = overrides?.get(compoundNames[3]) ?: compounds[3]?.value(t) ?: default.w - return Vector4(x, y, z, w) - } - - override fun sampler(normalized: Boolean): (Double) -> Vector4 { - val xSampler = compounds[0]?.sampler(normalized) ?: { default.x } - val ySampler = compounds[1]?.sampler(normalized) ?: { default.y } - val zSampler = compounds[2]?.sampler(normalized) ?: { default.z } - val wSampler = compounds[3]?.sampler(normalized) ?: { default.w } - return { t -> Vector4(xSampler(t), ySampler(t), zSampler(t), wSampler(t)) } - } -} - -class RgbFCurve( - r: Pair, - g: Pair, - b: Pair, - val default: ColorRGBa = ColorRGBa.WHITE -) : - CompoundFCurve(listOf(r.second, g.second, b.second), listOf(r.first, g.first, b.first)) { - - override fun value(t: Double, overrides: Map?): ColorRGBa { - val r = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.r - val g = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.g - val b = overrides?.get(compoundNames[2]) ?: compounds[2]?.value(t) ?: default.g - return ColorRGBa(r, g, b) - } - - override fun sampler(normalized: Boolean): (Double) -> ColorRGBa { - val rSampler = compounds[0]?.sampler(normalized) ?: { default.r } - val gSampler = compounds[1]?.sampler(normalized) ?: { default.g } - val bSampler = compounds[2]?.sampler(normalized) ?: { default.b } - return { t -> ColorRGBa(rSampler(t), gSampler(t), bSampler(t)) } - } -} - -class RgbaFCurve( - r: Pair, - g: Pair, - b: Pair, - a: Pair, - val default: ColorRGBa = ColorRGBa.WHITE -) : - CompoundFCurve( - listOf(r.second, g.second, b.second, a.second), - listOf(r.first, g.first, b.first, a.first) - ) { - - override fun value(t: Double, overrides: Map?): ColorRGBa { - val r = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.r - val g = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.g - val b = overrides?.get(compoundNames[2]) ?: compounds[2]?.value(t) ?: default.g - val a = overrides?.get(compoundNames[3]) ?: compounds[3]?.value(t) ?: default.alpha - return ColorRGBa(r, g, b, a) - } - - override fun sampler(normalized: Boolean): (Double) -> ColorRGBa { - val rSampler = compounds[0]?.sampler(normalized) ?: { default.r } - val gSampler = compounds[1]?.sampler(normalized) ?: { default.g } - val bSampler = compounds[2]?.sampler(normalized) ?: { default.b } - val aSampler = compounds[3]?.sampler(normalized) ?: { default.alpha } - return { t -> ColorRGBa(rSampler(t), gSampler(t), bSampler(t), aSampler(t)) } - } -} - -class PolarFCurve( - angleInDegrees: Pair, - radius: Pair, - val default: Polar = Polar(0.0, 1.0) -) : - CompoundFCurve(listOf(angleInDegrees.second, radius.second), listOf(angleInDegrees.first, radius.first)) { - - override fun value(t: Double, overrides: Map?): Polar { - val theta = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.theta - val radius = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.radius - return Polar(theta, radius) - } - - override fun sampler(normalized: Boolean): (Double) -> Polar { - val angleSampler = compounds[0]?.sampler(normalized) ?: { default.theta } - val radiusSampler = compounds[1]?.sampler(normalized) ?: { default.radius } - return { t -> Polar(angleSampler(t), radiusSampler(t)) } - } -} - -class SphericalFCurve( - thetaInDegrees: Pair, - phiInDegrees: Pair, - radius: Pair, - val default: Spherical = Spherical(0.0, 1.0, 1.0) -) : - CompoundFCurve( - listOf(thetaInDegrees.second, phiInDegrees.second, radius.second), - listOf(thetaInDegrees.first, phiInDegrees.first, radius.first) - ) { - override fun value(t: Double, overrides: Map?): Spherical { - val theta = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.theta - val phi = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.phi - val radius = overrides?.get(compoundNames[2]) ?: compounds[2]?.value(t) ?: default.radius - return Spherical(theta, phi, radius) - } - - override fun sampler(normalized: Boolean): (Double) -> Spherical { - val thetaSampler = compounds[0]?.sampler(normalized) ?: { default.theta } - val phiSampler = compounds[0]?.sampler(normalized) ?: { default.theta } - val radiusSampler = compounds[2]?.sampler(normalized) ?: { default.radius } - return { t -> Spherical(thetaSampler(t), phiSampler(t), radiusSampler(t)) } - } -} - diff --git a/orx-fcurve/src/commonMain/kotlin/EFCurve.kt b/orx-fcurve/src/commonMain/kotlin/EFCurve.kt deleted file mode 100644 index e5eb1a2b..00000000 --- a/orx-fcurve/src/commonMain/kotlin/EFCurve.kt +++ /dev/null @@ -1,126 +0,0 @@ -package org.openrndr.extra.fcurve - -import org.openrndr.extra.expressions.FunctionExtensions -import org.openrndr.extra.expressions.evaluateExpression - -/** - * expand mfcurve to fcurve - */ -fun mfcurve( - mf: String, - constants: Map = emptyMap(), - functions: FunctionExtensions = FunctionExtensions.EMPTY -): String { - /** - * perform comment substitution - */ - val stripped = Regex("(#.*)$", RegexOption.MULTILINE).replace(mf, "") - - /** - * detect modifier - */ - val parts = stripped.split("|") - - val efcurve = parts.getOrElse(0) { "" } - val modifier = parts.getOrNull(1) - - var fcurve = efcurve(efcurve, constants, functions) - if (modifier != null) { - fcurve = modifyFCurve(fcurve, modifier, constants, functions) - } - return fcurve -} - -/** - * Processes and expands a formatted string based on specific expressions and rules such as comments, lists, - * and repetitions. The method allows for recursive evaluation of expressions within the string. - * - * @param ef The input string to be processed, containing expression placeholders, lists, or repetitions. - * @param constants A map of constants used for substituting and evaluating expressions. - * @param functions An object containing user-defined functions for expression evaluation. - * @return A processed string with all expressions, lists, and repetitions evaluated and expanded. - */ -fun efcurve( - ef: String, - constants: Map = emptyMap(), - functions: FunctionExtensions = FunctionExtensions.EMPTY -): String { - // IntelliJ falsely reports a redundant escape character. the escape character is required when running the regular - // expression on a javascript target. Removing the escape character will result in a `Lone quantifier brackets` - // syntax error. - - @Suppress("RegExpRedundantEscape") - val expression = Regex("\\{([^{}]+)\\}") - - @Suppress("RegExpRedundantEscape") - val repetition = Regex("""\(([^()]+)\)\[([^\[\]]+)\]""") - - @Suppress("RegExpRedundantEscape") - val list = Regex("\\(([^()]+)\\)\\{([^\\[\\]]+)\\}") - - /** - * perform comment substitution - */ - var curve = Regex("(#.*)$", RegexOption.MULTILINE).replace(ef, "") - - /** - * Allow for nested repetitions and lists - */ - do { - val referenceCurve = curve - - /** - * perform list expansion |text|{items} - */ - curve = list.replace(curve) { occ -> - val listText = expression.replace(occ.groupValues[2]) { exp -> - val expressionText = exp.groupValues[1] - evaluateExpression(expressionText, constants, functions)?.toString() - ?: error("parse error in repetition count expression '$expressionText'") - } - val listTokens = listText.split(Regex("[,;][\t\n ]*|[\t\n ]+")) - val listItems = listTokens.filter { it.isNotEmpty() } - .map { it.trim().toDoubleOrNull() ?: error("'$it' is not a number in $listTokens") } - - listItems.mapIndexed { index, value -> - expression.replace(occ.groupValues[1]) { exp -> - val expressionText = exp.groupValues[1] - evaluateExpression( - exp.groupValues[1], - constants + mapOf("index" to index.toDouble(), "it" to value), - functions - )?.toString() ?: error("parse error in repeated expression '$expressionText'") - } - }.joinToString(" ") - } - - /** - * perform repetition expansion |text|[repeat-count] - */ - curve = repetition.replace(curve) { occ -> - val repetitions = expression.replace(occ.groupValues[2]) { exp -> - val expressionText = exp.groupValues[1] - evaluateExpression(expressionText, constants)?.toInt()?.toString() - ?: error("parse error in repetition count expression '$expressionText'") - }.toInt() - List(repetitions) { repetition -> - expression.replace(occ.groupValues[1]) { exp -> - val expressionText = exp.groupValues[1] - evaluateExpression( - exp.groupValues[1], - constants + mapOf("it" to repetition.toDouble()), - functions - )?.toString() - ?: error("parse error in repeated expression '$expressionText'") - } - }.joinToString(" ") - } - } while (curve != referenceCurve) - - /** - * evaluate expression in expansion - */ - return (expression.replace(curve) { exp -> - evaluateExpression(exp.groupValues[1], constants, functions)?.toString() ?: error("parse error in '$curve") - }) -} \ No newline at end of file diff --git a/orx-fcurve/src/commonMain/kotlin/FCurve.kt b/orx-fcurve/src/commonMain/kotlin/FCurve.kt deleted file mode 100644 index 625a3312..00000000 --- a/orx-fcurve/src/commonMain/kotlin/FCurve.kt +++ /dev/null @@ -1,619 +0,0 @@ -package org.openrndr.extra.fcurve - -import kotlinx.serialization.Serializable -import org.openrndr.math.Vector2 -import org.openrndr.math.transforms.buildTransform -import org.openrndr.shape.Segment2D -import org.openrndr.shape.ShapeContour -import org.openrndr.shape.bounds -import kotlin.math.abs - -/** - * Find the (first) t value for a given [x] value - */ -private fun Segment2D.tForX(x: Double): Double { - if (x == start.x) return 0.0 - if (x == end.x) return 1.0 - - if (linear) { - return (x - start.x) / (end.x - start.x) - } else { - val cb = this.cubic - val a = cb.start.x - x - val b = cb.control[0].x - x - val c = cb.control[1].x - x - val d = cb.end.x - x - - val t = getCubicRoots(a, b, c, d).firstOrNull() ?: 0.0 - - return t - } -} - -/** - * Find the y value for a given [x] value - */ -private fun Segment2D.yForX(x: Double): Double { - val t = tForX(x) - return position(t).y -} - -/** - * Scale tangents such that tangent lines do not overlap - */ -fun Segment2D.scaleTangents(axis: Vector2 = Vector2.UNIT_X): Segment2D { - if (linear) { - return this - } else { - val c = this.cubic - val width = end.distanceTo(start) - - val cd0 = (c.control[0] - c.start).projectedOn(axis) - val cd0a = cd0.dot(axis) - val cd1 = (c.control[1] - c.end).projectedOn(-axis) - val cd1a = cd1.dot(-axis) - val handleWidth = cd0.length + cd1.length - - val r = width / handleWidth - val c0 = (if (handleWidth > width) (c.control[0] - c.start) * r + c.start else c.control[0]).let { - if (cd0a <= 0.0) { - (it - c.start).projectedOn((axis).perpendicular()) + c.start - } else { - it - } - } - val c1 = (if (handleWidth > width) (c.control[1] - c.end) * r + c.end else c.control[1]).let { - if (cd1a <= 0.0) { - (it - c.end).projectedOn((-axis).perpendicular()) + c.end - } else { - it - } - } - return copy(control = listOf(c0, c1)) - } -} - - -/** - * Represents a functional curve composed of multiple 2D segments. It provides utilities to - * manipulate and query the curve, such as reversing its direction, changing its speed, - * sampling values, and visualizing it as contours. - * - * @param segments a list of 2D segments that define the curve - */ -@Serializable -data class FCurve(val segments: List) { - - /** - * Reverse the fcurve - */ - fun reverse(): FCurve { - val d = duration - val t = buildTransform { - translate(d, 0.0) - scale(-1.0, 1.0) - } - return FCurve(segments.map { it.reverse.transform(t) }) - } - - val bounds by lazy { - segments.map { it.bounds }.bounds - } - - /** - * Represents the minimum value of the curve. - * - * This property evaluates to the minimum vertical position (y-coordinate) - * of the FCurve based on its `segments` and `bounds`. If the `segments` - * list is empty, the value defaults to `0.0`. Otherwise, it calculates - * the minimum as the y-coordinate at the starting position of the bounds. - */ - val min: Double - get() { - if (segments.isEmpty()) return 0.0 else return bounds.position(0.0, 0.0).y - } - - /** - * Represents the maximum y-coordinate value of the FCurve at its bounds. - * - * If the `segments` list of the FCurve is empty, the returned value is 0.0. - * Otherwise, it calculates the y-coordinate of the furthest extent of the curve - * by evaluating the position of the `bounds` at the upper-right corner (1.0, 1.0). - */ - val max: Double - get() { - if (segments.isEmpty()) return 0.0 else return bounds.position(1.0, 1.0).y - } - - /** - * Change the duration of the Fcurve - */ - fun changeSpeed(speed: Double): FCurve { - val c = if (speed < 0.0) reverse() else this - return if (speed == 1.0) c else { - val t = buildTransform { - scale(1.0 / speed, 1.0) - } - FCurve(c.segments.map { it.transform(t) }) - } - } - - /** - * Shifts the FCurve along the x-axis by a specified amount. - * - * @param x The amount to shift the FCurve along the x-axis. - * @return A new FCurve instance with all segments shifted by the specified amount. - */ - fun shift(x: Double): FCurve { - val t = buildTransform { - translate(x, 0.0) - } - return FCurve(segments.map { it.transform(t) }) - } - - /** - * Offsets the FCurve by a specified amount along the y-axis. - * - * @param y The amount to offset the FCurve along the y-axis. - * @return A new FCurve instance with all segments transformed by the offset. - */ - fun offset(y: Double): FCurve { - val t = buildTransform { - translate(0.0, y) - } - return FCurve(segments.map { it.transform(t) }) - } - - /** - * Scales the FCurve by the specified factors along the x-axis and y-axis. - * - * @param sx The scaling factor along the x-axis. - * @param sy The scaling factor along the y-axis. - * @return A new FCurve instance with all segments scaled by the specified factors. - */ - fun scale(sx: Double, sy: Double): FCurve { - val t = buildTransform { - scale(sx, sy) - } - return FCurve(segments.map { it.transform(t) }) - } - - /** - * Creates a function to sample the FCurve at a specific time. - * - * @param normalized Specifies whether the sampling is normalized to the range [0, 1]. - * If `true`, the time parameter will be scaled to the duration of the curve. - * If `false`, the raw time parameter is used directly. - * @return A lambda function that takes a `Double` representing the time and returns a `Double` - * corresponding to the sampled value from the FCurve at that specific time. - */ - fun sampler(normalized: Boolean = false): (Double) -> Double { - var cachedSegment: Segment2D? = null - if (!normalized) { - return { t -> - val r = valueWithSegment(t, cachedSegment) - cachedSegment = r.second - r.first - } - } else { - val d = duration - return { t -> - val r = valueWithSegment(t * d, cachedSegment) - cachedSegment = r.second - r.first - } - } - } - - /** - * The duration of the FCurve, calculated as the difference between its start and end points. - * Returns 0.0 if the FCurve has no segments. - */ - val duration: Double - get() { - return if (segments.isEmpty()) { - 0.0 - } else { - end - start - } - } - - - /** - * Represents the starting x-coordinate of the first segment in the FCurve. - * If the `segments` list is empty, it defaults to `0.0`. - */ - val start: Double - get() { - return if (segments.isEmpty()) { - 0.0 - } else { - segments.first().start.x - } - } - - - /** - * Represents the x-coordinate of the endpoint of the last segment in the FCurve. - * - * If the `segments` list is empty, the value defaults to `0.0`. - * Otherwise, it returns the x-coordinate of the endpoint (`end.x`) of the last segment. - */ - val end: Double - get() { - return if (segments.isEmpty()) { - 0.0 - } else { - segments.last().end.x - } - } - - - /** - * Evaluate the Fcurve at [t] - * @param segment an optional segment that can be used to speed up scanning for the relevant segment - * @see valueWithSegment - */ - fun value(t: Double, segment: Segment2D? = null): Double = valueWithSegment(t, segment).first - - /** - * Evaluate the Fcurve at [t] - * @param cachedSegment an optional segment that can be used to speed up scanning for the relevant segment - */ - fun valueWithSegment(t: Double, cachedSegment: Segment2D? = null): Pair { - if (cachedSegment != null) { - if (t >= cachedSegment.start.x && t < cachedSegment.end.x) { - return Pair(cachedSegment.yForX(t), cachedSegment) - } - } - - if (segments.isEmpty()) { - return Pair(0.0, null) - } - if (t <= segments.first().start.x) { - val segment = segments.first() - return Pair(segment.start.y, segment) - } else if (t > segments.last().end.x) { - val segment = segments.last() - return Pair(segment.end.y, segment) - } else { - val segmentIndex = segments.binarySearch { - if (t < it.start.x) { - 1 - } else if (t > it.end.x) { - -1 - } else { - 0 - } - } - val segment = segments.getOrNull(segmentIndex) - return Pair(segment?.yForX(t) ?: 0.0, segment) - } - } - - /** - * Return a list of contours that can be used to visualize the Fcurve - */ - fun contours(scale: Vector2 = Vector2(1.0, -1.0), offset: Vector2 = Vector2.ZERO): List { - var active = mutableListOf() - val result = mutableListOf() - - for (segment in segments) { - - val tsegment = segment.transform( - buildTransform { - translate(offset) - scale(scale.x, scale.y) - } - ) - - if (active.isEmpty()) { - active.add(tsegment) - } else { - val dy = abs(active.last().end.y - tsegment.start.y) - if (dy > 1E-3) { - result.add(ShapeContour.fromSegments(active, false)) - active = mutableListOf() - } - active.add(tsegment) - } - } - if (active.isNotEmpty()) { - result.add(ShapeContour.fromSegments(active, false)) - } - return result - } -} - -/** - * Fcurve builder - */ -class FCurveBuilder { - val segments = mutableListOf() - var cursor = Vector2(0.0, 0.0) - - var path = "" - - fun moveTo(y: Double, relative: Boolean = false) { - cursor = if (!relative) cursor.copy(y = y) else cursor.copy(y = cursor.y + y) - path += "${if (relative) "m" else "M"}$y" - } - - fun lineTo(x: Double, y: Double, relative: Boolean = false) { - val r = if (relative) 1.0 else 0.0 - segments.add(Segment2D(cursor, Vector2(x + cursor.x, y + cursor.y * r))) - cursor = Vector2(cursor.x + x, cursor.y * r + y) - path += "${if (relative) "l" else "L"}$x,$y" - } - - fun curveTo( - x0: Double, y0: Double, - x: Double, y: Double, - relative: Boolean = false - ) { - val r = if (relative) 1.0 else 0.0 - segments.add( - Segment2D( - cursor, - Vector2(cursor.x + x0, cursor.y * r + y0), - Vector2(cursor.x + x, cursor.y * r + y) - ) - ) - cursor = Vector2(cursor.x + x, cursor.y * r + y) - path += "${if (relative) "q" else "Q"}$x0,$y0,$x,$y" - } - - fun curveTo( - x0: Double, y0: Double, - x1: Double, y1: Double, - x: Double, y: Double, relative: Boolean = false - ) { - val r = if (relative) 1.0 else 0.0 - segments.add( - Segment2D( - cursor, - Vector2(cursor.x + x0, cursor.y * r + y0), - Vector2(cursor.x + x1, cursor.y * r + y1), - Vector2(cursor.x + x, cursor.y * r + y) - ).scaleTangents() - ) - cursor = Vector2(cursor.x + x, cursor.y * r + y) - path += "${if (relative) "c" else "C"}$x0,$y0,$x,$y" - } - - fun continueTo(x: Double, y: Double, relative: Boolean = false) { - val r = if (relative) 1.0 else 0.0 - - if (segments.isNotEmpty()) { - val lastSegment = segments.last() - val outTangent = if (segments.last().linear) lastSegment.end else segments.last().control.last() - val outPos = lastSegment.end - val d = outPos - outTangent - val ts = 1.0// x / lastDuration - segments.add( - Segment2D( - cursor, - cursor + d * ts, - Vector2(cursor.x + x, cursor.y * r + y) - ).scaleTangents() - ) - } else { - segments.add( - Segment2D(cursor, - Vector2(cursor.x + x, cursor.y * r + y)).quadratic - ) - } - cursor = Vector2(cursor.x + x, cursor.y * r + y) - path += "${if (relative) "t" else "T"}$x,$y" - } - - fun continueTo(x1: Double, y1: Double, x: Double, y: Double, relative: Boolean = false) { - val r = if (relative) 1.0 else 0.0 - val lastSegment = segments.last() - val outTangent = if (lastSegment.linear) lastSegment.position(0.5) else segments.last().control.last() - val dx = cursor.x - outTangent.x - val dy = cursor.y - outTangent.y - segments.add( - Segment2D( - cursor, - Vector2(cursor.x + dx, cursor.y + dy), - Vector2(cursor.x + x1, cursor.y * r + y1), - Vector2(cursor.x + x, cursor.y * r + y) - ).scaleTangents() - ) - cursor = Vector2(cursor.x + x, cursor.y * r + y) - path += "${if (relative) "s" else "S"}$x1,$y1,$x,$y" - } - - fun hold(x: Double, relative: Boolean = true) { - if (relative) { - lineTo(x, cursor.y) - } else { - require(segments.isEmpty()) { "absolute hold (H $x) is only allowed when used as first command" } - cursor = cursor.copy(x = x) - } - path += "h$x" - } - - /** - * build the Fcurve - */ - fun build(): FCurve { - return FCurve(segments) - } -} - -/** - * build an Fcurve - * @see FCurveBuilder - */ -fun fcurve(builder: FCurveBuilder.() -> Unit): FCurve { - val fb = FCurveBuilder() - fb.builder() - return fb.build() -} - - -/** - * Splits an input string containing fcurve path commands and numbers into individual components, - * preserving the order of commands and associated numbers. - * The splitting considers the relations between commands and numbers, ensuring proper separation. - * - * @param d The input string representing fcurve path commands and numbers. - * @return A list of strings where each element is either an fcurve path command or a related numerical value. - */ -fun fCurveCommands(d: String): List { - val fcurveCommands = "mMlLqQsStTcChH" - val number = "0-9.\\-E%" - - return d.split(Regex("(?:[\t ,]|\r?\n)+|(?<=[$fcurveCommands])(?=[$number])|(?<=[$number])(?=[$fcurveCommands])")) - .filter { it.isNotBlank() } -} - -private fun evaluateFCurveCommands(parts: List): FCurve { - val mparts = parts.reversed().toMutableList() - - fun popToken(): String = mparts.removeLast() - - fun popNumber(): Double = mparts.removeLast().toDoubleOrNull() ?: error("not a number") - - fun String.numberOrFactorOf(percentageOf: (Double) -> Double): Double { - return if (endsWith("%")) { - val f = (dropLast(1).toDoubleOrNull() ?: error("'$this' is not a percentage")) / 100.0 - percentageOf(f) - } else { - toDoubleOrNull() ?: error("'$this' is not a number") - } - } - - fun String.numberOrPercentageOf(percentageOf: () -> Double): Double { - return numberOrFactorOf { f -> f * percentageOf() } - } - - fun popNumberOrPercentageOf(percentageOf: () -> Double): Double { - return mparts.removeLast().numberOrPercentageOf(percentageOf) - } - - /** - * Use the [fcurve] builder to construct the FCurve - */ - return fcurve { - fun dx(): Double { - val lastSegment = segments.lastOrNull() ?: Segment2D(Vector2.ZERO, Vector2.ZERO) - return lastSegment.end.x - lastSegment.start.x - } - - while (mparts.isNotEmpty()) { - val command = mparts.removeLast() - when (command) { - - /** - * Handle move cursor command - */ - "m", "M" -> { - val isRelative = command.first().isLowerCase() - moveTo(popNumberOrPercentageOf { cursor.y }, isRelative) - } - - /** - * Handle line command - */ - "l", "L" -> { - val isRelative = command.first().isLowerCase() - val x = popNumber() - val y = popNumber() - lineTo(x, y, isRelative) - } - - /** - * Handle cubic bezier command - */ - "c", "C" -> { - val relative = command.first().isLowerCase() - val tcx0 = popToken() - val tcy0 = popToken() - val tcx1 = popToken() - val tcy1 = popToken() - val x = popNumber() - val y = popNumber() - val x0 = tcx0.numberOrPercentageOf { x } - val y0 = tcy0.numberOrFactorOf { factor -> - if (relative) y * factor else cursor.y * (1.0 - factor).coerceAtLeast(0.0) + y * factor - } - val x1 = tcx1.numberOrPercentageOf { x } - val y1 = tcy1.numberOrFactorOf { factor -> - if (relative) y * factor else cursor.y * (1.0 - factor).coerceAtLeast(0.0) + y * factor - } - curveTo(x0, y0, x1, y1, x, y, relative) - } - - /** - * Handle quadratic bezier command - */ - "q", "Q" -> { - val relative = command.first().isLowerCase() - val tcx0 = popToken() - val tcy0 = popToken() - val x = popNumberOrPercentageOf { dx() } - val y = popNumberOrPercentageOf { cursor.y } - val x0 = tcx0.numberOrPercentageOf { x } - val y0 = tcy0.numberOrFactorOf { factor -> - if (relative) y * factor else cursor.y * (1.0 - factor).coerceAtLeast(0.0) + y * factor - } - curveTo(x0, y0, x, y, relative) - } - - /** - * Handle horizontal line (or hold) command - */ - "h", "H" -> { - val isRelative = command.first().isLowerCase() - hold(popNumberOrPercentageOf { dx() }, isRelative) - } - - /** - * Handle cubic smooth to command - */ - "s", "S" -> { - val relative = command.first().isLowerCase() - val tcx0 = popToken() - val tcy0 = popToken() - val x = popNumber() - val y = popNumber() - val x1 = tcx0.numberOrPercentageOf { x } - val y1 = tcy0.numberOrPercentageOf { y } - continueTo(x1, y1, x, y, relative) - } - - /** - * Handle quadratic smooth to command - */ - "t", "T" -> { - val isRelative = command.first().isLowerCase() - val x = popNumber() - val y = popNumber() - continueTo(x, y, isRelative) - } - - else -> error("unknown command: $command in ${parts}") - } - } - } -} - -/** - * Parses the provided string to create an FCurve. This function attempts to either interpret the - * input as a constant value or evaluate it as a series of functional curve commands. - * - * @param d A string representing either a constant value or functional curve commands. - * If the string can be converted to a double, it is treated as a constant value for the FCurve. - * Otherwise, it is parsed as functional curve commands. - * @return An FCurve constructed based on the input string. - */ -fun fcurve(d: String): FCurve { - val constantExpression = d.toDoubleOrNull() - if (constantExpression != null) { - return FCurve(listOf(Segment2D(Vector2(0.0, constantExpression), Vector2(0.0, constantExpression)))) - } - return evaluateFCurveCommands(fCurveCommands(d)) -} diff --git a/orx-fcurve/src/commonMain/kotlin/FCurveModifier.kt b/orx-fcurve/src/commonMain/kotlin/FCurveModifier.kt deleted file mode 100644 index c295f96f..00000000 --- a/orx-fcurve/src/commonMain/kotlin/FCurveModifier.kt +++ /dev/null @@ -1,200 +0,0 @@ -package org.openrndr.extra.fcurve - -import org.openrndr.extra.expressions.FunctionExtensions -import org.openrndr.extra.expressions.compileFunction1 -import org.openrndr.math.Vector2 - -/** - * Modify an [fcurve] string using a [modifiers] string - */ -fun modifyFCurve( - fcurve: String, - modifiers: String, - constants: Map = emptyMap(), - functions: FunctionExtensions = FunctionExtensions.EMPTY -): String { - val parts = fCurveCommands(fcurve) - val mparts = parts.reversed().toMutableList() - - @Suppress("RegExpRedundantEscape") - val modifier = Regex("([xy])=\\{([^{}]+)\\}") - - val modifierExpressions = modifier.findAll(modifiers).map { it.groupValues[1] to it.groupValues[2] }.toMap() - - val xModifierExpression = modifierExpressions["x"] - val xModifier = - if (xModifierExpression != null) compileFunction1(xModifierExpression, "x", constants, functions) else { - { x: Double -> x } - } - - val yModifierExpression = modifierExpressions["y"] - val yModifier = - if (yModifierExpression != null) compileFunction1(yModifierExpression, "y", constants, functions) else { - { y: Double -> y } - } - - fun popToken(): String = mparts.removeLast() - fun popNumber(): Double = mparts.removeLast().toDoubleOrNull() ?: error("not a number") - - fun String.numberOrFactorOf(percentageOf: (Double) -> Double): Double { - return if (endsWith("%")) { - val f = (dropLast(1).toDoubleOrNull() ?: error("'$this' is not a percentage")) / 100.0 - percentageOf(f) - } else { - toDoubleOrNull() ?: error("'$this' is not a number") - } - } - - fun String.numberOrPercentageOf(percentageOf: () -> Double): Double { - return numberOrFactorOf { f -> f * percentageOf() } - } - - var cursor = Vector2.ZERO - var modified = "" - fun emit(command: String, vararg ops: Double, relative: Boolean, x: Double, y: Double) { - modified = modified + " " + command + " " + ops.joinToString(" ") - cursor = if (relative) { - Vector2(x + cursor.x, y + cursor.y) - } else { - Vector2(x + cursor.x, y) - } - } - - while (mparts.isNotEmpty()) { - val command = mparts.removeLast() - - when (command) { - - /** - * Handle move cursor command - */ - "m", "M" -> { - val relative = command.first().isLowerCase() - val rf = if (relative) 1.0 else 0.0 - val y = popNumber() + rf * cursor.y - emit("M", yModifier(y), relative = false, x = 0.0, y = y) - } - - /** - * Handle line command - */ - "l", "L" -> { - val relative = command.first().isLowerCase() - val rf = if (relative) 1.0 else 0.0 - val x = popNumber() - val y = popNumber() + rf * cursor.y - - emit("L", xModifier(x), yModifier(y), relative = false, x = x, y = y) - } - - /** - * Handle cubic bezier command - */ - "c", "C" -> { - val relative = command.first().isLowerCase() - val rf = if (relative) 1.0 else 0.0 - - val tcx0 = popToken() - val tcy0 = popToken() - val tcx1 = popToken() - val tcy1 = popToken() - val x = popNumber() - val y = popNumber() - val ay = y + cursor.y * rf - val x0 = tcx0.numberOrPercentageOf { x } - val y0 = tcy0.numberOrFactorOf { factor -> - if (relative) y * factor else cursor.y * (1.0 - factor).coerceAtLeast(0.0) + y * factor - } + cursor.y * rf - val x1 = tcx1.numberOrPercentageOf { x } - val y1 = tcy1.numberOrFactorOf { factor -> - if (relative) y * factor else cursor.y * (1.0 - factor).coerceAtLeast(0.0) + y * factor - } + cursor.y * rf - emit( - "C", - xModifier(x0), - yModifier(y0), - xModifier(x1), - yModifier(y1), - xModifier(x), - xModifier(ay), - relative = false, - x = x, - y = ay - ) - } - - /** - * Handle quadratic bezier command - */ - "q", "Q" -> { - val relative = command.first().isLowerCase() - val rf = if (relative) 1.0 else 0.0 - val tcx0 = popToken() - val tcy0 = popToken() - val x = popNumber() - val y = popNumber() - val ay = y + cursor.y * rf - val x0 = tcx0.numberOrPercentageOf { x } - val y0 = tcy0.numberOrFactorOf { factor -> - if (relative) y * factor else cursor.y * (1.0 - factor).coerceAtLeast(0.0) + y * factor - } + rf * cursor.y - emit("Q", xModifier(x0), yModifier(y0), xModifier(x), yModifier(ay), relative = false, x = x, y = ay) - } - - /** - * Handle horizontal line (or hold) command - */ - "h", "H" -> { - if (command == "H") { - val x = popNumber() - emit(command, xModifier(x), relative = false, x = x, y = cursor.y) - cursor = Vector2(x, cursor.y) - } else { - val x = popNumber() - emit(command, xModifier(x), relative = false, x = x, y = cursor.y) - } - } - - /** - * Handle cubic smooth to command - */ - "s", "S" -> { - val relative = command.first().isLowerCase() - val rf = if (relative) 1.0 else 0.0 - val tcx0 = popToken() - val tcy0 = popToken() - val x = popNumber() - val y = popNumber() - val ay = y + cursor.y * rf - val x1 = tcx0.numberOrPercentageOf { x } - val y1 = tcy0.numberOrFactorOf { factor -> - if (relative) y * factor else cursor.y * (1.0 - factor).coerceAtLeast(0.0) + y * factor - } + rf * cursor.y - emit("S", xModifier(x1), yModifier(y1), xModifier(x), yModifier(ay), relative = false, x = x, y = ay) - } - - /** - * Handle quadratic smooth to command - */ - "t", "T" -> { - val relative = command.first().isLowerCase() - val rf = if (relative) 1.0 else 0.0 - val x = popNumber() - val y = popNumber() + cursor.y * rf - emit("T", xModifier(x), yModifier(y), relative = false, x = x, y = y) - } - - else -> error("unknown command: $command in ${parts}") - } - } - return modified -} - -fun main() { - val f = "l 10 10 h 4 t 20.0 20.0 s 5% 50% 30.0 30.0" - println(modifyFCurve(f, "x={sqrt(x)} y={y * 2.0}")) - - val mf = "l 10 10 h 4 t 20.0 20.0 s 5% 50% 30.0 30.0 | x={2.0 * x} y={-3.0 * y}" - val f2 = mfcurve(mf) - println(f2) -} \ No newline at end of file diff --git a/orx-fcurve/src/commonMain/kotlin/MultiFCurve.kt b/orx-fcurve/src/commonMain/kotlin/MultiFCurve.kt deleted file mode 100644 index e19af9aa..00000000 --- a/orx-fcurve/src/commonMain/kotlin/MultiFCurve.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.openrndr.extra.fcurve - -/** - * Represents a collection of named `FCurve` objects, enabling the manipulation and - * querying of multiple functional curves as a unified entity. Each `FCurve` in the - * collection is identified by a unique string key, allowing structured access and control. - * - * @param compounds A map containing string keys associated with `FCurve` instances. - */ -open class MultiFCurve(val compounds: Map) { - fun changeSpeed(speed: Double): MultiFCurve { - return if (speed == 1.0) { - this - } else { - MultiFCurve(compounds.mapValues { it.value?.changeSpeed(speed) }) - } - } - - /** - * Duration of the [MultiFCurve] - */ - val duration by lazy { compounds.values.maxOfOrNull { it?.duration ?: 0.0 } ?: 0.0 } - - - /** - * Start position of the [MultiFCurve] - */ - val start by lazy { compounds.values.minOfOrNull { it?.start ?: 0.0 } ?: 0.0 } - - /** - * End position of the [MultiFCurve] - */ - val end by lazy { compounds.values.maxOfOrNull { it?.end ?: 0.0 } ?: 0.0 } - - operator fun get(name: String): FCurve? { - return compounds[name] - } -} - diff --git a/orx-fcurve/src/commonMain/kotlin/MultiFCurveExtensions.kt b/orx-fcurve/src/commonMain/kotlin/MultiFCurveExtensions.kt deleted file mode 100644 index 96bfc4e5..00000000 --- a/orx-fcurve/src/commonMain/kotlin/MultiFCurveExtensions.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.openrndr.extra.fcurve - -import org.openrndr.color.ColorRGBa -import org.openrndr.math.* - -fun MultiFCurve.boolean(value: String, default: Boolean = true) = BooleanFCurve(value to this[value], default) -fun MultiFCurve.double(value: String, default: Double = 0.0) = DoubleFCurve(value to this[value], default) - -fun MultiFCurve.int(value: String, default: Int = 0) = IntFCurve(value to this[value], default) - -fun MultiFCurve.vector2(x: String, y: String, default: Vector2 = Vector2.ZERO) = - Vector2FCurve(x to this[x], y to this[y], default) - -fun MultiFCurve.vector3(x: String, y: String, z: String, default: Vector3 = Vector3.ZERO) = - Vector3FCurve(x to this[x], y to this[y], z to this[z], default) - -fun MultiFCurve.vector4(x: String, y: String, z: String, w: String, default: Vector4 = Vector4.ZERO) = - Vector4FCurve(x to this[x], y to this[y], z to this[z], w to this[w], default) - -fun MultiFCurve.rgb(r: String, g: String, b: String, default: ColorRGBa = ColorRGBa.WHITE) = - RgbFCurve(r to this[r], g to this[g], b to this[b], default) - -fun MultiFCurve.rgba(r: String, g: String, b: String, a: String, default: ColorRGBa = ColorRGBa.WHITE) = - RgbaFCurve(r to this[r], g to this[g], b to this[b], a to this[a], default) - -fun MultiFCurve.polar(angleInDegrees: String, radius: String, default: Polar = Polar(0.0, 1.0)) = - PolarFCurve(angleInDegrees to this[angleInDegrees], radius to this[radius], default) - -fun MultiFCurve.spherical( - thetaInDegrees: String, - phiInDegrees: String, - radius: String, - default: Spherical = Spherical(0.0, 0.0, 1.0) -) = SphericalFCurve( - thetaInDegrees to this[thetaInDegrees], - phiInDegrees to this[phiInDegrees], - radius to this[radius], - default -) \ No newline at end of file diff --git a/orx-fcurve/src/commonMain/kotlin/Roots.kt b/orx-fcurve/src/commonMain/kotlin/Roots.kt deleted file mode 100644 index 66c604b1..00000000 --- a/orx-fcurve/src/commonMain/kotlin/Roots.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* -This is a direct port of the root finding code from https://pomax.github.io/bezierinfo/#extremities - -A copy of the original license: -The following license terms apply to this repository and its derivative website and repositories: - -- you may use any illustrative code found in the `docs/chapters` directories without crediting. -- you may use any illustrative graphics found in the `docs/images` directories without crediting. -- you may quote up to two paragraphs from the `docs/chapters` markdown documents without crediting. -- you may quote an entire section from any chapter, as long as it's credited and links back to the chapter that section is in on the official website. - -If you wish to quote more than one section of a chapter (such as an entire chapter, or more than one chapter), you may do so only after requesting, and getting, explicit permission. Permission should be sought by filing an issue in this repository, which will act as permanent record of the granted permissions. - -Outside of the above permissions, the following prohibitions and copyrights apply: - -- You may not put up a clone of the entire work (meaning that if you fork the project, you may not turn on gh-pages to get it automatically hosted by github itself on your own account domain). -- The code in `docs/js/graphics-element/lib` consists of third party libraries governed by their own licenses. - -Any other material not explicitly covered by this license is to be treated as having all rights reserved. -Please file an issue for license clarification questions. - - */ - -package org.openrndr.extra.fcurve -import kotlin.math.* - -// A helper function to filter for values in the [0,1] interval: -private fun accept(t: Double): Boolean { - return t in 0.0..1.0 -} - - -// A real-cuberoots-only function: -private fun cuberoot(v: Double): Double { - if (v < 0) return -(-v).pow(1.0 / 3); - return v.pow(1.0 / 3) -} - -internal fun approximately(a: Double, b: Double, epsilon: Double = 1E-8): Boolean { - return abs(a - b) < epsilon -} - -// Now then: given cubic coordinates {pa, pb, pc, pd} find all roots. -internal fun getCubicRoots(pa: Double, pb: Double, pc: Double, pd: Double): List { - var a = (3 * pa - 6 * pb + 3 * pc) - var b = (-3 * pa + 3 * pb) - var c = pa - val d = (-pa + 3 * pb - 3 * pc + pd); - - // do a check to see whether we even need cubic solving: - if (approximately(d, 0.0)) { - // this is not a cubic curve. - if (approximately(a, 0.0)) { - // in fact, this is not a quadratic curve either. - if (approximately(b, 0.0)) { - // in fact in fact, there are no solutions. - return emptyList() - } - // linear solution - return listOf(-c / b).filter(::accept) - } - // quadratic solution - val q = sqrt(b * b - 4 * a * c) - val twoA = 2 * a - return listOf((q - b) / twoA, (-b - q) / twoA).filter(::accept) - } - - // at this point, we know we need a cubic solution. - - a /= d - b /= d - c /= d - - val p = (3 * b - a * a) / 3 - val p3 = p / 3 - val q = (2 * a * a * a - 9 * a * b + 27 * c) / 27 - val q2 = q / 2 - val discriminant = q2 * q2 + p3 * p3 * p3 - - // and some variables we're going to use later on: - var u1 = 0.0 - var v1 = 0.0 - var root1 = 0.0 - var root2 = 0.0 - var root3 = 0.0 - - // three possible real roots: - if (discriminant < 0) { - var mp3 = -p / 3 - val mp33 = mp3 * mp3 * mp3 - val r = sqrt(mp33) - val t = -q / (2 * r) - val cosphi = if (t < -1) -1.0 else if (t > 1) 1.0 else t - val phi = acos(cosphi) - val crtr = cuberoot(r) - val t1 = 2 * crtr - root1 = t1 * cos(phi / 3) - a / 3 - root2 = t1 * cos((phi + 2 * PI) / 3) - a / 3 - root3 = t1 * cos((phi + 4 * PI) / 3) - a / 3 - return listOf(root1, root2, root3).filter(::accept) - } - - // three real roots, but two of them are equal: - if (discriminant == 0.0) { - u1 = if(q2 < 0) cuberoot(-q2) else -cuberoot(q2) - root1 = 2 * u1 - a / 3 - root2 = -u1 - a / 3 - return listOf(root1, root2).filter(::accept) - } - - // one real root, two complex roots - val sd = sqrt(discriminant) - u1 = cuberoot(sd - q2) - v1 = cuberoot(sd + q2) - root1 = u1 - v1 - a / 3 - return listOf(root1).filter(::accept) -} \ No newline at end of file diff --git a/orx-fcurve/src/commonTest/kotlin/TestEFCurve.kt b/orx-fcurve/src/commonTest/kotlin/TestEFCurve.kt deleted file mode 100644 index dac64d1f..00000000 --- a/orx-fcurve/src/commonTest/kotlin/TestEFCurve.kt +++ /dev/null @@ -1,68 +0,0 @@ -import org.openrndr.extra.fcurve.efcurve -import org.openrndr.extra.fcurve.fcurve -import kotlin.test.Test -import kotlin.test.assertEquals - -class TestEFCurve { - @Test - fun comments() { - val text = """M1 (h5 m3){ - |10.3 # toch wel handig zo'n comment - |11.2 - |14.5 - |} - """.trimMargin() - assertEquals("M1 h5 m3 h5 m3 h5 m3", efcurve(text)) - } - - @Test - fun expressions() { - //assertEquals("M${9.toDouble()}", efcurve("M{4.0 + 5.0}")) - } - - @Test - fun listExpansion() { - //assertEquals("M0 L1.0, ${3.toDouble()} L1.0, ${6.0}", efcurve("M0 (L1.0, {it}){3, 6}")) - } - - @Test - fun repetition() { - //assertEquals("M0 L1.0, 3.0 L1.0, 3.0", efcurve("M0 (L1.0, 3.0)[2]")) - //assertEquals("M0 L1.0, ${0.0} L1.0, ${1.0}", efcurve("M0 (L1.0, {it})[2]")) - //assertEquals("M0 L1.0, ${0.0} L1.0, ${1.0} L1.0, ${0.0} L1.0, ${1.0} L1.0, ${0.0} L1.0, ${1.0}", efcurve("M0 ((L1.0, {it})[2])[3]")) - } - - @Test - fun testContinuity() { - val fc = fcurve("Q1 25% 3 100 Q1 25% 3 0 Q1 25% 3 100 Q1 25% 3 0") - val s = fc.sampler() - assertEquals(0.0, s(6.0)) - } - - @Test - fun testConstantValue() { - val text = "10.5" - val fc = fcurve(efcurve(text)) - assertEquals(10.5, fc.value(0.0, null)) - assertEquals(10.5, fc.value(1.0, null)) - assertEquals(10.5, fc.value(-1.0, null)) - - val normalizedSampler = fc.sampler(true) - assertEquals(10.5, normalizedSampler(0.0)) - assertEquals(10.5, normalizedSampler(1.0)) - assertEquals(10.5, normalizedSampler(-1.0)) - } - @Test - fun testConstantExpression() { - val text = "${21.0 / 2.0}" - val fc = fcurve(efcurve(text)) - assertEquals(10.5, fc.value(0.0, null)) - assertEquals(10.5, fc.value(1.0, null)) - assertEquals(10.5, fc.value(-1.0, null)) - - val normalizedSampler = fc.sampler(true) - assertEquals(10.5, normalizedSampler(0.0)) - assertEquals(10.5, normalizedSampler(1.0)) - assertEquals(10.5, normalizedSampler(-1.0)) - } -} \ No newline at end of file diff --git a/orx-fcurve/src/commonTest/kotlin/TestFCurve.kt b/orx-fcurve/src/commonTest/kotlin/TestFCurve.kt deleted file mode 100644 index 962ec51b..00000000 --- a/orx-fcurve/src/commonTest/kotlin/TestFCurve.kt +++ /dev/null @@ -1,41 +0,0 @@ -import org.openrndr.extra.fcurve.fcurve -import kotlin.test.Test -import kotlin.test.assertEquals - -class TestFCurve { - @Test - fun testConstantExpression() { - val text = "10.5" - val fc = fcurve(text) - assertEquals(10.5, fc.value(0.0, null)) - assertEquals(10.5, fc.value(1.0, null)) - assertEquals(10.5, fc.value(-1.0, null)) - - val normalizedSampler = fc.sampler(true) - assertEquals(10.5, normalizedSampler(0.0)) - assertEquals(10.5, normalizedSampler(1.0)) - assertEquals(10.5, normalizedSampler(-1.0)) - } - - @Test - fun testAbsoluteHold() { - run { - val text = "H-1 L 5 5" - val fc = fcurve(text) - assertEquals(0.0, fc.value(-1.0, null)) - assertEquals(5.0, fc.value(4.0, null)) - assertEquals(-1.0, fc.start) - assertEquals(4.0, fc.end) - assertEquals(5.0, fc.duration) - } - run { - val text = "H1 L 5 5" - val fc = fcurve(text) - assertEquals(0.0, fc.value(1.0, null)) - assertEquals(5.0, fc.value(6.0, null)) - assertEquals(1.0, fc.start) - assertEquals(6.0, fc.end) - assertEquals(5.0, fc.duration) - } - } -} \ No newline at end of file diff --git a/orx-fcurve/src/jvmDemo/kotlin/DemoFCurve01.kt b/orx-fcurve/src/jvmDemo/kotlin/DemoFCurve01.kt deleted file mode 100644 index 0efec09e..00000000 --- a/orx-fcurve/src/jvmDemo/kotlin/DemoFCurve01.kt +++ /dev/null @@ -1,17 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.fcurve.fcurve - -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val xpos = fcurve("M0 Q4,360,5,720").sampler() - val ypos = fcurve("M360 h5").sampler() - - extend { - drawer.circle(xpos(seconds.mod(5.0)), ypos(seconds.mod(5.0)), 100.0) - } - } -} diff --git a/orx-fcurve/src/jvmDemo/kotlin/DemoFCurve02.kt b/orx-fcurve/src/jvmDemo/kotlin/DemoFCurve02.kt deleted file mode 100644 index cd9cce7c..00000000 --- a/orx-fcurve/src/jvmDemo/kotlin/DemoFCurve02.kt +++ /dev/null @@ -1,26 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.fcurve.fcurve -import org.openrndr.math.Vector2 - -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val xposCurve = fcurve("M0 Q4,360,5,720") - val xpos = xposCurve.sampler() - val yposCurve = fcurve("M360 h5") - val ypos = yposCurve.sampler() - - extend { - drawer.circle(xpos(seconds.mod(5.0)), ypos(seconds.mod(5.0)), 100.0) - drawer.stroke = ColorRGBa.PINK - drawer.contours(xposCurve.contours(Vector2(720.0 / 5.0, -1.0), Vector2(0.0, height * 1.0))) - drawer.contours(yposCurve.contours(Vector2(720.0 / 5.0, -1.0), Vector2(0.0, height * 1.0))) - drawer.translate(seconds.mod(5.0) * (720.0 / 5.0), 0.0) - drawer.lineSegment(0.0, 0.0, 0.0, 720.0) - } - } -} diff --git a/orx-fcurve/src/jvmDemo/kotlin/DemoFCurveSheet01.kt b/orx-fcurve/src/jvmDemo/kotlin/DemoFCurveSheet01.kt deleted file mode 100644 index 4f565d7d..00000000 --- a/orx-fcurve/src/jvmDemo/kotlin/DemoFCurveSheet01.kt +++ /dev/null @@ -1,54 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.loadFont -import org.openrndr.extra.fcurve.efcurve -import org.openrndr.extra.fcurve.fcurve -import org.openrndr.math.Vector2 - -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val fcurveTexts = listOf( - //"(l 35.0 25.0 h {175-35})[4]", // linear steps - "(c 33% 0% 67% 67% 35.0 25.0 h {175-35})[4]", // ease-in steps - "(c 50% 50% 50% 100% 35.0 25.0 h {175-35})[4]", // ease-out steps - "(c 50% 0% 50% 100% 35.0 25.0 h {175-35})[4]", // ease-in-out steps - "(c 95% 0% 100% 100% 35.0 25.0 h {175-35})[4]", // arc-in steps - "(c 0% 0% 5% 100% 35.0 25.0 h {175-35})[4]", // arc-out steps - "(c 95% 0% 100% 100% 17.5 12.5 c 0% 0% 5% 100% 17.5 12.5 h {175-35})[4]", // arc-out steps - ) - - val fcurves = fcurveTexts.map { fcurve(efcurve(it)) } - - extend { - drawer.clear(ColorRGBa.WHITE) - - drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 16.0) - drawer.translate(10.0, 20.0) - - drawer.stroke = ColorRGBa.PINK - drawer.lineSegment(mouse.position.x - 10.0, 0.0, mouse.position.x - 10.0, height * 1.0) - - fun color(i: Int): ColorRGBa = - ColorRGBa.BLUE.toHSVa().shiftHue(i * 30.0).saturate(0.5).shade(0.9).toRGBa() - - for (i in fcurveTexts.indices) { - drawer.fill = color(i) - drawer.text(fcurveTexts[i], 0.0, 120.0) - - drawer.stroke = color(i).opacify(0.25) - drawer.lineSegment(0.0, 100.0, width - 20.0, 100.0) - - drawer.stroke = color(i) - val y = 100.0 - fcurves[i].value(mouse.position.x - 10.0) - drawer.contours(fcurves[i].contours(offset = Vector2(0.0, 100.0))) - drawer.circle(mouse.position.x - 10.0, y, 10.0) - - drawer.translate(0.0, 110.0) - } - } - } -} \ No newline at end of file diff --git a/orx-fcurve/src/jvmDemo/kotlin/DemoMultiFCurve01.kt b/orx-fcurve/src/jvmDemo/kotlin/DemoMultiFCurve01.kt deleted file mode 100644 index 0941784e..00000000 --- a/orx-fcurve/src/jvmDemo/kotlin/DemoMultiFCurve01.kt +++ /dev/null @@ -1,28 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.fcurve.MultiFCurve -import org.openrndr.extra.fcurve.fcurve -import org.openrndr.extra.fcurve.vector2 - -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - class XYAnimation : MultiFCurve( - mapOf( - "x" to fcurve("M0 Q4,360,5,720"), - "y" to fcurve("M360 h5") - ) - ) { - val position = vector2("x", "y") - } - - val xyAnimation = XYAnimation() - val position = xyAnimation.position.sampler() - - extend { - drawer.circle(position(seconds.mod(5.0)), 100.0) - } - } -} diff --git a/orx-fft/README.md b/orx-fft/README.md deleted file mode 100644 index a210a2aa..00000000 --- a/orx-fft/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# orx-fft - -Simple forward and inverse FFT routine - -The FFT routine found in `orx-fft` is a Kotlin port of Minim's FFT routine. - -## Demos -### DemoFFTShape01 - -Demonstration of using FFT to filter a two-dimensional shape. Mouse xy-position is mapped -to lowpass and highpass settings of the filter. - -![DemoFFTShape01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fft/images/DemoFFTShape01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoFFTShape01.kt) diff --git a/orx-fft/build.gradle.kts b/orx-fft/build.gradle.kts deleted file mode 100644 index 14aaca0f..00000000 --- a/orx-fft/build.gradle.kts +++ /dev/null @@ -1,23 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") -} - -kotlin { - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - - } - } - - @Suppress("UNUSED_VARIABLE") - val jvmDemo by getting { - dependencies { - implementation(project(":orx-shapes")) - implementation(project(":orx-noise")) - - } - } - } -} \ No newline at end of file diff --git a/orx-fft/src/commonMain/kotlin/FFT.kt b/orx-fft/src/commonMain/kotlin/FFT.kt deleted file mode 100644 index 6bda6de5..00000000 --- a/orx-fft/src/commonMain/kotlin/FFT.kt +++ /dev/null @@ -1,225 +0,0 @@ -package org.openrndr.extra.fft - -import kotlin.math.* - -/* -Based on https://github.com/ddf/Minim/blob/e294e2881a20340603ee0156cb9188c15b5915c2/src/main/java/ddf/minim/analysis/FFT.java -I (EJ) stripped away spectrum and averages. - -This is the original license (GPLv2). I am not sure if my low-effort Kotlin port falls under the same license. - - * Copyright (c) 2007 - 2008 by Damien Di Fede - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -class FFT(val size: Int, private val windowFunction: WindowFunction = IdentityWindow()) { - - var real = FloatArray(size) - var imag = FloatArray(size) - - private fun setComplex(r: FloatArray, i: FloatArray) { - if (real.size != r.size && imag.size != i.size) { - error("FourierTransform.setComplex: the two arrays must be the same length as their member counterparts.") - } else { - r.copyInto(real) - i.copyInto(imag) - } - } - - fun magnitudeSum(includeDC: Boolean = false): Double { - var sum = 0.0 - for (i in (if (includeDC) 0 else 1)..size / 2) { - sum += magnitude(i) - } - return sum - } - - fun scaleAll(sr: Float, includeDC: Boolean = false) { - for (i in (if (includeDC) 0 else 1)..size / 2) { - scaleBand(i, sr) - } - } - - fun magnitude(i: Int): Float { - return sqrt(real[i] * real[i] + imag[i] * imag[i]) - } - - fun phase(i: Int): Float { - return atan2(imag[i], real[i]) - } - - fun shiftPhase(i: Int, shift: Double) { - val m = magnitude(i) - val phase = phase(i) - real[i] = (cos(phase + shift) * m).toFloat() - imag[i] = (sin(phase + shift) * m).toFloat() - - if (i != 0 && i != size / 2) { - real[(size - i)] = real[i] - imag[(size - i)] = -imag[i] - } - } - - fun scaleBand(i: Int, sr: Float) { - if (sr < 0) { - error("Can't scale a frequency band by a negative value.") - } - - real[i] *= sr - imag[i] *= sr - - if (i != 0 && i != size / 2) { - real[(size - i)] = real[i] - imag[(size - i)] = -imag[i] - } - } - - - // performs an in-place fft on the data in the real and imag arrays - // bit reversing is not necessary as the data will already be bit reversed - private fun fft() { - var halfSize = 1 - while (halfSize < real.size) { - // float k = -(float)Math.PI/halfSize; - // phase shift step - // float phaseShiftStepR = (float)Math.cos(k); - // float phaseShiftStepI = (float)Math.sin(k); - // using lookup table - val phaseShiftStepR = cos[halfSize] - val phaseShiftStepI = sin[halfSize] - // current phase shift - var currentPhaseShiftR = 1.0f - var currentPhaseShiftI = 0.0f - for (fftStep in 0 until halfSize) { - var i = fftStep - while (i < real.size) { - val off = i + halfSize - val tr = (currentPhaseShiftR * real[off]) - (currentPhaseShiftI * imag[off]) - val ti = (currentPhaseShiftR * imag[off]) + (currentPhaseShiftI * real[off]) - real[off] = real[i] - tr - imag[off] = imag[i] - ti - real[i] += tr - imag[i] += ti - i += 2 * halfSize - } - val tmpR = currentPhaseShiftR - currentPhaseShiftR = (tmpR * phaseShiftStepR) - (currentPhaseShiftI * phaseShiftStepI) - currentPhaseShiftI = (tmpR * phaseShiftStepI) + (currentPhaseShiftI * phaseShiftStepR) - } - halfSize *= 2 - } - } - - private fun doWindow(samples: FloatArray) { - windowFunction.apply(samples) - } - - - fun forward(buffer: FloatArray) { - if (buffer.size != size) { - error("FFT.forward: The length of the passed sample buffer must be equal to timeSize().") - } - doWindow(buffer) - // copy samples to real/imag in bit-reversed order - bitReverseSamples(buffer, 0) - // perform the fft - fft() - } - - fun forward(buffer: FloatArray, startAt: Int) { - if (buffer.size - startAt < size) { - error( - "FourierTransform.forward: not enough samples in the buffer between " + - startAt + " and " + buffer.size + " to perform a transform." - ) - } - windowFunction.apply(buffer, startAt, size) - bitReverseSamples(buffer, startAt) - fft() - } - - /** - * Performs a forward transform on the passed buffers. - * - * @param buffReal the real part of the time domain signal to transform - * @param buffImag the imaginary part of the time domain signal to transform - */ - fun forward(buffReal: FloatArray, buffImag: FloatArray) { - if (buffReal.size != size || buffImag.size != size) { - error("FFT.forward: The length of the passed buffers must be equal to timeSize().") - } - setComplex(buffReal, buffImag) - bitReverseComplex() - fft() - } - - fun inverse(buffer: FloatArray) { - if (buffer.size > real.size) { - error("FFT.inverse: the passed array's length must equal FFT.timeSize().") - } - // conjugate - for (i in 0 until size) { - imag[i] *= -1.0f - } - bitReverseComplex() - fft() - // copy the result in real into buffer, scaling as we do - for (i in buffer.indices) { - buffer[i] = real[i] / real.size - } - } - - private val reverse by lazy { buildReverseTable() } - - private fun buildReverseTable(): IntArray { - val reverse = IntArray(size) - // set up the bit reversing table - reverse[0] = 0 - var limit = 1 - var bit = size / 2 - while (limit < size) { - for (i in 0 until limit) reverse[i + limit] = reverse[i] + bit - limit = limit shl 1 - bit = bit shr 1 - } - return reverse - } - - // copies the values in the samples array into the real array - // in bit reversed order. the imag array is filled with zeros. - private fun bitReverseSamples(samples: FloatArray, startAt: Int) { - for (i in 0 until size) { - real[i] = samples[startAt + reverse[i]] - imag[i] = 0.0f - } - } - - // bit reverse real[] and imag[] - private fun bitReverseComplex() { - val revReal = FloatArray(real.size) - val revImag = FloatArray(imag.size) - for (i in real.indices) { - revReal[i] = real[reverse[i]] - revImag[i] = imag[reverse[i]] - } - real = revReal - imag = revImag - } - - // lookup tables - private val sin by lazy { FloatArray(size) { i -> sin((-PI.toFloat() / i).toDouble()).toFloat() } } - private val cos by lazy { FloatArray(size) { i -> cos((-PI.toFloat() / i).toDouble()).toFloat() } } -} \ No newline at end of file diff --git a/orx-fft/src/commonMain/kotlin/HannWindow.kt b/orx-fft/src/commonMain/kotlin/HannWindow.kt deleted file mode 100644 index 87a2c429..00000000 --- a/orx-fft/src/commonMain/kotlin/HannWindow.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.openrndr.extra.fft - -import kotlin.math.PI -import kotlin.math.cos - -class HannWindow : WindowFunction() { - override fun value(length: Int, index: Int): Float = 0.5f * (1f - cos((PI * 2.0 * index / (length - 1f))) - .toFloat()) -} \ No newline at end of file diff --git a/orx-fft/src/commonMain/kotlin/IdentityWindow.kt b/orx-fft/src/commonMain/kotlin/IdentityWindow.kt deleted file mode 100644 index a5949796..00000000 --- a/orx-fft/src/commonMain/kotlin/IdentityWindow.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.openrndr.extra.fft - -class IdentityWindow - : WindowFunction() { - override fun value(length: Int, index: Int): Float = 1.0f -} \ No newline at end of file diff --git a/orx-fft/src/commonMain/kotlin/WindowFunction.kt b/orx-fft/src/commonMain/kotlin/WindowFunction.kt deleted file mode 100644 index 71939641..00000000 --- a/orx-fft/src/commonMain/kotlin/WindowFunction.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.openrndr.extra.fft - -abstract class WindowFunction { - private var length: Int = 0 - - /** - * Apply the window function to a sample buffer. - * - * @param samples a sample buffer - */ - fun apply(samples: FloatArray) { - this.length = samples.size - - for (n in samples.indices) { - samples[n] *= value(samples.size, n) - } - } - - /** - * Apply the window to a portion of this sample buffer, - * given an offset from the beginning of the buffer - * and the number of samples to be windowed. - * - * @param samples - * float[]: the array of samples to apply the window to - * @param offset - * int: the index in the array to begin windowing - * @param length - * int: how many samples to apply the window to - */ - fun apply(samples: FloatArray, offset: Int, length: Int) { - this.length = length - - for (n in offset until offset + length) { - samples[n] *= value(length, n - offset) - } - } - protected abstract fun value(length: Int, index: Int): Float -} \ No newline at end of file diff --git a/orx-fft/src/jvmDemo/kotlin/DemoFFTShape01.kt b/orx-fft/src/jvmDemo/kotlin/DemoFFTShape01.kt deleted file mode 100644 index ad29f35f..00000000 --- a/orx-fft/src/jvmDemo/kotlin/DemoFFTShape01.kt +++ /dev/null @@ -1,156 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.fft.FFT -import org.openrndr.extra.noise.scatter -import org.openrndr.extra.shapes.hobbycurve.hobbyCurve -import org.openrndr.extra.shapes.splines.catmullRom -import org.openrndr.extra.shapes.splines.toContour -import org.openrndr.math.Vector2 -import org.openrndr.math.smoothstep -import org.openrndr.math.transforms.buildTransform -import org.openrndr.shape.LineSegment -import kotlin.math.max -import kotlin.random.Random - -/** - * Demonstration of using FFT to filter a two-dimensional shape. Mouse xy-position is mapped - * to lowpass and highpass settings of the filter. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val fftSize = 512 - val fft = FFT(fftSize) - fun List.toFloatArrays(x: FloatArray, y: FloatArray) { - for ((index, segment) in this.withIndex()) { - x[index] = segment.x.toFloat() - y[index] = segment.y.toFloat() - } - } - - fun vectorsFromFloatArrays(x: FloatArray, y: FloatArray): List { - val n = x.size - val result = mutableListOf() - for (i in 0 until n) { - result.add(Vector2(x[i].toDouble(), y[i].toDouble())) - } - return result - } - - fun lp(t: Double, c: Double): Double { - return smoothstep(c, c - 0.1, t) - } - - fun hp(t: Double, c: Double): Double { - return smoothstep(c, c + 0.1, t) - } - - val c = hobbyCurve( - drawer.bounds.scatter(30.0, distanceToEdge = 100.0, random = Random(3)).filter { - Random.nextBoolean() - }, - true - ).transform(buildTransform { translate(-drawer.bounds.center) }) - - val x = FloatArray(fftSize) - val y = FloatArray(fftSize) - - val xFiltered = FloatArray(fftSize) - val yFiltered = FloatArray(fftSize) - - extend { - c.equidistantPositions(fftSize).toFloatArrays(x, y) - - // process x-component - fft.forward(x) - - drawer.stroke = ColorRGBa.GRAY.shade(0.5) - drawer.lineSegments((0 until fft.size / 2).map { - LineSegment( - it.toDouble() * 2.0 + 0.5, - height * 0.5, - it.toDouble() * 2.0 + 0.5, - height * 0.5 - fft.magnitude(it) / 200.0, - ) - }) - - - val xpower = fft.magnitudeSum() - - val hpc = mouse.position.x / width - val lpc = mouse.position.y / height - - for (i in 1..fftSize / 2) { - val t = i.toDouble() / (fftSize / 2 - 1) - val f = if (hpc <= lpc) lp(t, lpc) * hp(t, hpc) else max(lp(t, lpc), hp(t, hpc)) - fft.scaleBand(i, f.toFloat()) - } - val xfpower = fft.magnitudeSum().coerceAtLeast(1.0) - - fft.scaleAll((xpower / xfpower).toFloat()) - drawer.stroke = ColorRGBa.PINK.opacify(0.8) - drawer.lineSegments((0 until fft.size / 2).map { - LineSegment( - it.toDouble() * 2.0 + 0.5, - height * 0.5, - it.toDouble() * 2.0 + 0.5, - height * 0.5 - fft.magnitude(it) / 200.0 - ) - }) - - fft.inverse(xFiltered) - - // process y-component - fft.forward(y) - val ypower = fft.magnitudeSum() - - drawer.stroke = ColorRGBa.GRAY.shade(0.5) - drawer.lineSegments((0 until fft.size / 2).map { - LineSegment( - it * 2.0 + 0.5, - height * 0.5, - it * 2.0 + 0.5, - height * 0.5 + fft.magnitude(it) / 200.0, - ) - }) - - - for (i in 1..fftSize / 2) { - val t = i.toDouble() / (fftSize / 2 - 1) - val f = if (hpc <= lpc) lp(t, lpc) * hp(t, hpc) else max(lp(t, lpc), hp(t, hpc)) - fft.scaleBand(i, f.toFloat()) - } - - val yfpower = fft.magnitudeSum().coerceAtLeast(1.0) - - fft.scaleAll((ypower / yfpower).toFloat()) - drawer.stroke = ColorRGBa.PINK.opacify(0.7) - drawer.lineSegments((0 until fft.size / 2).map { - LineSegment( - it * 2.0 + 0.5, - height * 0.5, - it * 2.0 + 0.5, - height * 0.5 + fft.magnitude(it) / 200.0, - ) - }) - fft.inverse(yFiltered) - - val cr = vectorsFromFloatArrays(xFiltered, yFiltered).catmullRom(closed = true).toContour() - //val cr = ShapeContour.fromPoints(vectorsFromFloatArrays(xr, yr), closed=true) - - val recenteredShape = cr.transform(buildTransform { - translate(drawer.bounds.center) - }) - drawer.fill = null - drawer.stroke = ColorRGBa.WHITE - - drawer.lineSegment(mouse.position.x / width * 512, 0.0, mouse.position.x / width * 512, height * 1.0) - drawer.lineSegment(mouse.position.y / height * 512, 0.0, mouse.position.y / height * 512, height * 1.0) - - drawer.contour(recenteredShape) - } - } -} diff --git a/orx-fx/README.md b/orx-fx/README.md deleted file mode 100644 index c7e6a64c..00000000 --- a/orx-fx/README.md +++ /dev/null @@ -1,457 +0,0 @@ -# orx-fx - -Ready-to-use GPU-based visual effects or filters. Most include -[orx-parameters](https://github.com/openrndr/orx/tree/master/orx-parameters) annotations -so they can be easily controlled via orx-gui. - -The provided filters are based on OPENRNDR's -[`Filter` class](https://api.openrndr.org/openrndr-draw/org.openrndr.draw/-filter/index.html) - -All filters provided by orx-fx assume pre-multiplied alpha inputs, which is OPENRNDR's default. - -## Effects index - -Here's a (potentially incomplete) list of the effects provided by orx-fx. Explore [the source](https://github.com/openrndr/orx/tree/master/orx-fx/src/commonMain/kotlin) for an up-to-date list. - -### Anti-alias - - - `FXAA`, fast approximate anti-aliasing. - -### Blends - -Blend filters take two inputs ("source" and "destination"), they are intended to be used in `orx-compositor`'s layer blend. All blend filters are opacity preserving. - -#### Photoshop-style blends - - - `ColorBurn` - - `ColorDodge` - - `Darken` - - `HardLight` - - `Lighten` - - `Multiply` - - `Normal` - - `Overlay` - - `Screen` - - `Add`, add source and destination inputs - - `Subtract`, substract destination color from source color - -#### Porter-Duff blends - - - `SourceIn`, Porter-Duff source-in blend, intersect source and destination opacity and keep source colors - - `SourceOut`, Porter-Duff source-out blend, subtract destination from source opacity and keep source colors - - `SourceAtop`, Porter-Duff source-atop blend, uses destination opacity, layers source on top and keeps both colors - - `DestinationIn`, Porter-Duff destination-in blend, intersect source and destination opacity and keep source colors - - `DestinationOut`, Porter-Duff destination-out blend, subtract destination from source opacity and keep destination colors - - `DestinationAtop`, Porter-Duff destination-atop blend, uses source opacity, layers destination on top and keeps both colors - - `Xor`, Porter-Duff xor blend, picks colors from input with highest opacity or none with opacities are equal - -#### Various blends - - - `Passthrough`, pass source color and opacity. - -### Blurs - -Most blur effects are opacity preserving - - - `ApproximateGaussianBlur`, a somewhat faster but less precise implementation of `GaussianBlur` - - `Bloom`, a multi-pass bloom/glow effect - - `BoxBlur`, a simple but fast box blur - - `FrameBlur` - - `GaussianBlur`, a slow but precise Gaussian blur - - `HashBlur`, a noisy blur effect - - `LaserBlur` - - `LineBlur` - - `MipBloom` - - `ZoomBlur`, a directional blur with a zooming effect - - -### Color - - - `ChromaticAberration`, a chromatic aberration effect based on RGB color separation - - `ColorCorrection`, corrections for brightness, contrast, saturation and hue - - `ColorLookup`, Color LUT filter - - `ColorMix`, filter implementation of OPENRNDR's color matrix mixing - - `Duotone`, maps luminosity to two colors, very similar to `LumaMap` but uses LAB color interpolation. - - `DuotoneGradient`, a two-point gradient version of `Duotone` - - `Invert` - - `LumaMap`, maps luminosity to two colors - - `LumaOpacity`, maps luminosity to opacity but retains source color - - `LumaThreshold`, applies a treshold on the input luminosity and maps to two colors - - `Posterize`, a posterize effect - - `Sepia`, applies a reddish-brown monochrome tint that imitates an old photograph - - `SetBackground` - - `SubtractConstant`, subtract a constant color from the source color - -### Color conversion - - - `OkLabToRgb` - - `RgbToOkLab` - -### Distortion - -All distortion effects are opacity preserving - - - `BlockRepeat` - repeats a single configurable block of the source input - - `DisplaceBlend` - - `Fisheye` - - `FluidDistort` - - `Lenses` - - `HorizontalWave` - applies a horizontal wave effect on the source input - - `VerticalWave` - applies a vertical wave effect on the source input - - `PerspectivePlane` - applies a planar perspective distortion on the source input - - `Perturb` - - `PolarToRectangular` - - `RectangularToPolar` - - `StackRepeat` - repeats the source input in a stack fashion - - `StretchWaves` - - `TapeNoise` - - `Tiles` - - `VideoGlitch` - -### Dither - - - `ADither` - a selection of dithering effects - - `CMYKHalftone` - a configurable CMYK halftoning effect - - `Crosshatch` - crosshatching effect - - `LumaHalftone` - a halftoning effect based on luminosity - -### Edges - - - `LumaSobel` - A Sobel-kernel based luminosity edge detector - - `EdgesWork` - An edges filter doubling as erosion - - `Contour` - detects multi-level contours - - New: `CannyEdgeDetector` - -### Grain - - - `FilmGrain` - adds film-like grain to the source input - -### Shadow - - - `DropShadow` - adds a drop shadow based on the opacity in the input image - -### Tonemap - - - `Uncharted2Tonemap` - implements the Uncharted2 tonemapper - -### Transform - - - `FlipVertically` - flips the source input vertically. - -## `Post` extension - -The `Post` extension provides an easy way to apply filters to your drawings. Allocating -and resizing color buffers is all taken care of by `Post`. - -To get additional intermediate color buffers one can access `intermediate[x]` -```kotlin -fun main() = application { - configure { - windowResizable = true - } - program { - extend(Post()) { - val blur = ApproximateGaussianBlur() - val add = Add() - post { input, output -> - blur.window = 50 - blur.sigma = 50.0 - blur.apply(input, intermediate[0]) - add.apply(arrayOf(input, intermediate[0]), output) - } - } - extend { - drawer.circle(width / 2.0, height / 2.0, 100.0) - } - } -} -``` - -### Colormap - -Colormap filters operate only on the RED color channel. For example -depth maps from -[orx-depth-camera](https://github.com/openrndr/orx/tree/master/orx-depth-camera). - -They allow selection of `min` / `max` value range and applying exponential -shaping `curve` within this range: - -- `GrayscaleColormap` - maps to gray tones -- `SpectralZucconiColormap` - maps to natural light dispersion spectrum as described - by Alan Zucconi in the - [Improving the Rainbow](https://www.alanzucconi.com/2017/07/15/improving-the-rainbow/) - article. -- `TurboColormap` - maps to Turbo Colormap according to - [Turbo, An Improved Rainbow Colormap for Visualization](https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html) - by Google. - - - - -## Demos -### DemoApproximateGaussianBlur01 - -Demonstrates how to use the [ApproximateGaussianBlur] effect to blur -a `colorBuffer`, in this case, an image loaded from disk. - -Notice the use of `createEquivalent()`, which creates a new `colorBuffer` -with the same size and properties as a source `colorBuffer`. - - -![DemoApproximateGaussianBlur01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoApproximateGaussianBlur01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoApproximateGaussianBlur01.kt) - -### DemoBlur01 - -Demonstrates 9 different blur effects. -The program draws two moving circles into a [RenderTarget], -then applies various blurs drawing them in 3 columns and 3 rows. - -Each type of blur has different parameters. -Not all parameters are demonstrated. - -![DemoBlur01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoBlur01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoBlur01.kt) - -### DemoCannyEdgeDetector01 - -Demonstrates the [CannyEdgeDetector] effect applied to a loaded -color photograph. - -![DemoCannyEdgeDetector01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoCannyEdgeDetector01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCannyEdgeDetector01.kt) - -### DemoColorDuotone01 - -This demo shows how to use the [Duotone] filter, -toggling the `labInterpolation` parameter every second on and off. - -The `foregroundColor` and `backgroundColor` parameters are -left to their defaults. - -![DemoColorDuotone01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoColorDuotone01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoColorDuotone01.kt) - -### DemoColorDuotoneGradient01 - -The [DuotoneGradient] effect combines the Duotone effect -and a linear gradient: two duotone colors are applied on -one part of the image, and those colors are interpolated -to two other colors, applied in a different part of the image. - -The `rotation` parameter lets us specify in which direction -the interpolation happens (vertical, horizontal, or something else). - -![DemoColorDuotoneGradient01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoColorDuotoneGradient01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoColorDuotoneGradient01.kt) - -### DemoColormapGrayscale - -The [GrayscaleColormap] uses the red channel of a colorBuffer -to produce a gray scale image. The `curve` parameter is used as -an exponent to bias the result up or down. 1.0 produces a linear -transformation. - -![DemoColormapGrayscaleKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoColormapGrayscaleKt.png) - -[source code](src/jvmDemo/kotlin/DemoColormapGrayscale.kt) - -### DemoColormapSpectralZucconi - -Demonstrates the [SpectralZucconiColormap], which -maps values of the RED color channel to the natural light dispersion -spectrum as described by Alan Zucconi in his -[Improving the Rainbow](https://www.alanzucconi.com/2017/07/15/improving-the-rainbow/) -article. - -![DemoColormapSpectralZucconiKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoColormapSpectralZucconiKt.png) - -[source code](src/jvmDemo/kotlin/DemoColormapSpectralZucconi.kt) - -### DemoColormapTurbo - -Demonstrates the use of the [TurboColormap] effect, which -maps values of the RED color channel to Turbo Colormap according to -[Turbo, An Improved Rainbow Colormap for Visualization](https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html) -by Google. - -![DemoColormapTurboKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoColormapTurboKt.png) - -[source code](src/jvmDemo/kotlin/DemoColormapTurbo.kt) - -### DemoColorPosterize01 - -Demonstration of the [Posterize] effect to reduce the number of colors -present in an image. - -![DemoColorPosterize01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoColorPosterize01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoColorPosterize01.kt) - -### DemoCompositeFilter01 - -Advanced demonstration of composite filters, created by chaining -several filters together using the `.then()` operator. - -The demo applies a [FilmGrain] effect and a [DirectionalBlur] effect twice -with different parameters. - -The [DirectionalBlur] requires a color buffer to define the displacement -directions. In this program, the direction color buffer is populated by writing -into its `shadow` property pixel by pixel. - -Notice the use of `frameCount` and `seconds` to animate the effects. - -The composite effect is installed as a post-processing effect -using `extend(Post())`, so anything drawn in following `extend` -blocks is affected by it. - -![DemoCompositeFilter01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoCompositeFilter01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoCompositeFilter01.kt) - -### DemoContour01 - -Demonstrates the [Contour] filter. -@author Edwin Jakobs - -This demo creates a grid of 2x2 to draw a loaded image four times, -each using the [Contour] effect with different parameters. - -`actions` is a variable containing a list of 4 functions. -Each of these functions sets the effect parameters to different values. - -The 4 grid cells and the 4 actions are used in pairs: -first the action is called to set the effect parameters, the -effect is applied, and the result is drawn in a cell. - -![DemoContour01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoContour01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoContour01.kt) - -### DemoDirectionalBlur01 - -Demonstrates how to use [DirectionalBlur] by creating a `direction` -ColorBuffer in which the red and green components of the pixels point -in various directions where to sample pixels from. All the pixel colors -of the ColorBuffer are set one by one using two for loops. - -Note the FLOAT32 color type of the buffer to allow for negative values, -so sampling can happen from every direction. - -Every 60 animation frames the `centerWindow` property is toggled -between true and false to demonstrate how the result changes. - - -![DemoDirectionalBlur01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoDirectionalBlur01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoDirectionalBlur01.kt) - -### DemoDirectionalDisplace01 - -Demonstrate how to use [DirectionalDisplace]. - -The direction map is populated using `drawImage` instead of -pixel by pixel. A grid of circles is drawn, each circle with a -color based on simplex noise. The R and G channels of the colors -control the direction of the sampling. By animating the sampling -distance the result oscillates between no-effect and a noticeable one. - -![DemoDirectionalDisplace01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoDirectionalDisplace01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoDirectionalDisplace01.kt) - -### DemoDirectionalDisplace02 - -Demonstrate how to use [DirectionalDisplace]. - -The program draws 12 overlapping translucent circles on the -`direction` color buffer to produce new color combinations -on the overlapping areas. Those colors specify where the -`DirectionalDisplace` effect will sample pixels from. - -![DemoDirectionalDisplace02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoDirectionalDisplace02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoDirectionalDisplace02.kt) - -### DemoDistortLenses01 - -Demonstrates the [Lenses] effect, which by default subdivides a color buffer -in 8 columns and 6 rows, and displaces the source texture inside each rectangle. -Try experimenting with some of the other parameters, like `distort`. -You can even animate them. - -![DemoDistortLenses01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoDistortLenses01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoDistortLenses01.kt) - -### DemoDitherLumaHalftone01 - -Demonstrates the [LumaHalftone] effect and moste of its parameters. -The `invert` parameter toggles between true and false once per second. -The `phase0` and `phase1` parameters depend on `seconds`, which makes -the pattern wobble slowly. - -![DemoDitherLumaHalftone01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoDitherLumaHalftone01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoDitherLumaHalftone01.kt) - -### DemoFluidDistort01 - -Demonstrates [FluidDistort], a fluid simulation real time effect. -All pixels are slowly displaced in a turbulent manner as if they were a gas or a liquid. - -![DemoFluidDistort01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoFluidDistort01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoFluidDistort01.kt) - -### DemoOkLab01 - -This demonstrates converting a [ColorBuffer] from and to (OK)LAB color space using the [RgbToOkLab] and [OkLabToRgb] -filters. The (OK)Lab representation is signed and requires a floating point representation. - -![DemoOkLab01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoOkLab01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoOkLab01.kt) - -### DemoPost01 - -Demonstrates how to create an `extend` block to apply a post-processing effect. -The effect is an [ApproximateGaussianBlur] and its `sigma` parameter -is animated. The Blur effect is combined with whatever the user draws -in the regular `extend` block using the `Add` filter, resulting in -an additive composition. - -This demo also shows how to make a program window resizable. - -![DemoPost01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoPost01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoPost01.kt) - -### DemoSpectralBlend01 - -Demonstration of how to use the [BlendSpectral] filter to combine two images, using -this pigment-simulation color mixing approach. - -The program: -- generates two images -- blurs one of them -- creates and draws a checkers-pattern as the background -- mixes and draws both images - -The `fill` factor, which controls how the top and the bottom colors are mixed, is animated. - -The `clip` parameter is also animated and toggles every 6 seconds. - -![DemoSpectralBlend01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-fx/images/DemoSpectralBlend01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoSpectralBlend01.kt) diff --git a/orx-fx/build.gradle.kts b/orx-fx/build.gradle.kts deleted file mode 100644 index 0df4f6cd..00000000 --- a/orx-fx/build.gradle.kts +++ /dev/null @@ -1,42 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") -} - -val embedShaders = tasks.register("embedShaders") { - inputDir.set(file("$projectDir/src/shaders/glsl")) - outputDir.set(layout.buildDirectory.dir("generated/shaderKotlin")) - defaultPackage.set("org.openrndr.extra.fx") - defaultVisibility.set("internal") - namePrefix.set("fx_") -}.get() - - -kotlin { - kotlin.sourceSets.getByName("commonMain").kotlin.srcDir(embedShaders.outputDir) - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - - implementation(project(":orx-parameters")) - implementation(project(":orx-shader-phrases")) - implementation(project(":orx-color")) - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - } - } - - @Suppress("UNUSED_VARIABLE") - val jvmDemo by getting { - dependencies { - implementation(project(":orx-color")) - implementation(project(":orx-fx")) - implementation(project(":orx-noise")) - implementation(project(":orx-shapes")) - implementation(project(":orx-image-fit")) - } - } - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/FilterTools.kt b/orx-fx/src/commonMain/kotlin/FilterTools.kt deleted file mode 100644 index 646e4f65..00000000 --- a/orx-fx/src/commonMain/kotlin/FilterTools.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.openrndr.extra.fx - -import org.openrndr.draw.ColorFormat -import org.openrndr.draw.ColorType -import org.openrndr.draw.Shader -import org.openrndr.draw.filterShaderFromCode - -fun mppFilterShader(code: String, name: String) : Shader = filterShaderFromCode(code, name, includeShaderConfiguration = true) - -internal data class ColorBufferDescription(val width: Int, val height: Int, val contentScale: Double, val format: ColorFormat, val type: ColorType) diff --git a/orx-fx/src/commonMain/kotlin/Post.kt b/orx-fx/src/commonMain/kotlin/Post.kt deleted file mode 100644 index ca1b93a9..00000000 --- a/orx-fx/src/commonMain/kotlin/Post.kt +++ /dev/null @@ -1,118 +0,0 @@ -package org.openrndr.extra.fx - -import org.openrndr.Extension -import org.openrndr.Program -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* - -class Post : Extension { - override var enabled = true - - private var inputTarget: RenderTarget? = null - private var resolved: ColorBuffer? = null - - /** - * The color type to use for the intermediate color buffers - */ - var intermediateType = ColorType.UINT8_SRGB - - /** - * The color type to use for the output color buffer - */ - var outputType = ColorType.UINT8_SRGB - - /** - * The color type to use for the input buffer - */ - var inputType = ColorType.UINT8_SRGB - - /** - * The depth format to use for the input buffer - */ - var inputDepthFormat = DepthFormat.DEPTH_STENCIL - - private var output: ColorBuffer? = null - private var postFunction = { input: ColorBuffer, output: ColorBuffer -> input.copyTo(output) } - - inner class IntermediateBuffers { - internal val buffers = mutableMapOf() - operator fun get(index: Int): ColorBuffer { - return buffers.getOrPut(index) { - colorBuffer(output!!.width, output!!.height, output!!.contentScale, type = intermediateType) - } - } - } - - /** - * Intermediate buffer pool, provides automatically allocated color buffers - */ - val intermediate = IntermediateBuffers() - - /** - * Set the post function - * @param function the post function - */ - fun post(function: (input: ColorBuffer, output: ColorBuffer) -> Unit) { - postFunction = function - } - - override fun beforeDraw(drawer: Drawer, program: Program) { - val art = RenderTarget.active - val lit = inputTarget - if (lit != null) { - // in case the attributes of the existing buffers no longer match those of the active render target - if (lit.width != art.width || lit.height != art.height || lit.contentScale != art.contentScale || lit.multisample != art.multisample) { - lit.colorBuffer(0).destroy() - lit.depthBuffer?.destroy() - lit.detachDepthBuffer() - lit.detachColorAttachments() - lit.destroy() - inputTarget = null - - resolved?.destroy() - resolved = null - - output?.destroy() - output = null - - for (buffer in intermediate.buffers.values) { - buffer.destroy() - } - intermediate.buffers.clear() - } - } - if (inputTarget == null) { - // create new targets and buffers - inputTarget = renderTarget(art.width, art.height, art.contentScale, multisample = art.multisample) { - colorBuffer(type = inputType) - depthBuffer(format = inputDepthFormat) - } - if (art.multisample != BufferMultisample.Disabled) { - resolved = colorBuffer(art.width, art.height, art.contentScale) - } - output = colorBuffer(art.width, art.height, art.contentScale, type = outputType) - } - // bind input target, the next extensions will draw into it - inputTarget!!.bind() - drawer.clear(ColorRGBa.TRANSPARENT) - } - - override fun afterDraw(drawer: Drawer, program: Program) { - inputTarget!!.unbind() - - if (resolved != null) { - inputTarget!!.colorBuffer(0).copyTo(resolved!!) - } - - val postInput = resolved ?: inputTarget!!.colorBuffer(0) - - // invoke the user provided post-processing function - postFunction(postInput, output!!) - - // visualize the results - drawer.isolated { - drawer.defaults() - drawer.image(output!!) - } - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/antialias/FXAA.kt b/orx-fx/src/commonMain/kotlin/antialias/FXAA.kt deleted file mode 100644 index 5ca822b2..00000000 --- a/orx-fx/src/commonMain/kotlin/antialias/FXAA.kt +++ /dev/null @@ -1,46 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.antialias - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_fxaa -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -/** - * FXAA approximate antialiasing filter. Only works on LDR inputs - */ -@Description("FXAA") -class FXAA : Filter1to1( mppFilterShader(fx_fxaa, "fxaa")) { - /** - * luma threshold, default value is 0.5 - */ - @DoubleParameter("luma threshold", 0.0, 1.0) - var lumaThreshold: Double by parameters - - /** - * max search span, default value is 8.0 - */ - @DoubleParameter("max search span", 1.0, 16.0) - var maxSpan: Double by parameters - - /** - * direction reduce multiplier, default value is 0.0 - */ - @DoubleParameter("direction reduce multiplier", 0.0, 1.0) - var directionReduceMultiplier: Double by parameters - - /** - * direction reduce minimum, default value is 0.0 - */ - @DoubleParameter("direction reduce minium", 0.0, 1.0) - var directionReduceMinimum: Double by parameters - - init { - lumaThreshold = 0.5 - maxSpan = 8.0 - directionReduceMinimum = 0.0 - directionReduceMultiplier = 0.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/blend/BlendFilters.kt b/orx-fx/src/commonMain/kotlin/blend/BlendFilters.kt deleted file mode 100644 index c72d0738..00000000 --- a/orx-fx/src/commonMain/kotlin/blend/BlendFilters.kt +++ /dev/null @@ -1,127 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.blend - -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.Filter2to1 -import org.openrndr.extra.fx.* -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.DoubleParameter - -class ColorBurn : Filter2to1(mppFilterShader(fx_color_burn, "color-burn")) { - @BooleanParameter("source clip") - var clip: Boolean by parameters - - init { - clip = false - } -} - -class ColorDodge : Filter2to1(mppFilterShader(fx_color_dodge, "color-dodge")) { - @BooleanParameter("source clip") - var clip: Boolean by parameters - - init { - clip = false - } -} - -class Darken : Filter2to1(mppFilterShader(fx_darken, "darken")) { - @BooleanParameter("source clip") - var clip: Boolean by parameters - - init { - clip = false - } -} - -class HardLight : Filter2to1(mppFilterShader(fx_hard_light, "hard-light")) { - @BooleanParameter("source clip") - var clip: Boolean by parameters - - init { - clip = false - } -} - -class Lighten : Filter2to1(mppFilterShader(fx_lighten, "lighten")) { - @BooleanParameter("source clip") - var clip: Boolean by parameters - - init { - clip = false - } -} - -class Multiply : Filter2to1(mppFilterShader(fx_multiply,"multiply")) { - @BooleanParameter("source clip") - var clip: Boolean by parameters - - init { - clip = false - } -} - -class Normal : Filter2to1(mppFilterShader(fx_normal, "normal")) { - @BooleanParameter("source clip") - var clip: Boolean by parameters - - @DoubleParameter("opacity A", 0.0, 1.0) - var opacityA: Double by parameters - - @DoubleParameter("opacity B", 0.0, 1.0) - var opacityB: Double by parameters - - init { - clip = false - opacityA = 1.0 - opacityB = 1.0 - } -} - -class Overlay : Filter2to1(mppFilterShader(fx_overlay, "overlay")) { - @BooleanParameter("source clip") - var clip: Boolean by parameters - - init { - clip = false - } -} - -class Screen : Filter2to1(mppFilterShader(fx_screen, "screen")) { - @BooleanParameter("source clip") - var clip: Boolean by parameters - - init { - clip = false - } -} - - -class SourceIn : Filter2to1(mppFilterShader(fx_source_in, "source-in")) -class SourceOut : Filter2to1(mppFilterShader(fx_source_out,"source-out")) -class SourceAtop : Filter2to1(mppFilterShader(fx_source_atop, "source-atop")) -class DestinationIn : Filter2to1(mppFilterShader(fx_destination_in, "destination-in")) -class DestinationOut : Filter2to1(mppFilterShader(fx_destination_out, "destination-out")) -class DestinationAtop : Filter2to1(mppFilterShader(fx_destination_atop, "destination-atop")) -class Xor : Filter2to1(mppFilterShader(fx_xor, "xor")) - -class MultiplyContrast : Filter2to1(mppFilterShader(fx_multiply_contrast, "multiply-contrast")) - -class Passthrough : Filter1to1(mppFilterShader(fx_passthrough, "passthrough")) -class Add : Filter2to1(mppFilterShader(fx_add, "add")) { - @BooleanParameter("source clip") - var clip: Boolean by parameters - - init { - clip = false - } -} -class Subtract : Filter2to1(mppFilterShader(fx_subtract,"subtract")) { - @BooleanParameter("source clip") - var clip: Boolean by parameters - - init { - clip = false - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/blend/BlendSpectral.kt b/orx-fx/src/commonMain/kotlin/blend/BlendSpectral.kt deleted file mode 100644 index 84837fff..00000000 --- a/orx-fx/src/commonMain/kotlin/blend/BlendSpectral.kt +++ /dev/null @@ -1,82 +0,0 @@ -package org.openrndr.extra.fx.blend - -import org.openrndr.draw.Filter2to1 -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.fx.fx_spectral -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -private val spectralBlendShader = """// includes -${fx_spectral} -// filter code -uniform sampler2D tex0; -uniform sampler2D tex1; -in vec2 v_texCoord0; - -out vec4 o_color; - -uniform float fill; -uniform bool clip; - -void main() { - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - - // depremultiply alpha - vec4 na = a.a == 0.0 ? vec4(0.0): vec4(a.rgb / a.a,a.a); - vec4 nb = b.a == 0.0 ? vec4(0.0): vec4(b.rgb / b.a,b.a); - - - - vec4 mixed = vec4(spectral_mix(na.rgb, nb.rgb, min(1.0, fill)), 1.0); - - if (!clip) { - na.rgb *= a.a; - nb.rgb *= b.a; - mixed = na * (1.0 - nb.a) + nb * (1.0 - na.a) + mixed * na.a * nb.a; - } else { - mixed = mixed * na.a * nb.a; - } - - mixed.rgb = mixed.a == 0.0 ? vec3(0.0): mixed.rgb / mixed.a; - - -// premultiply alpha - mixed.rgb *= mixed.a; - - - o_color = mixed; -} - -""" - -/** - * Blend based on pigment simulation - */ -@Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") -@Description("Blend spectral") -class BlendSpectral : Filter2to1(filterShaderFromCode(spectralBlendShader, "blend-spectral")) { - @BooleanParameter("source clip") - var clip: Boolean by parameters - - @BooleanParameter("linearize input A") - var linearizeInputA: Boolean by parameters - - @BooleanParameter("linearize input B") - var linearizeInputB: Boolean by parameters - - @BooleanParameter("delinearize output") - var delinearizeOutput: Boolean by parameters - - @DoubleParameter("fill", 0.0, 1.0) - var fill: Double by parameters - - init { - clip = false - linearizeInputA = true - linearizeInputB = true - delinearizeOutput = true - fill = 1.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/blur/ApproximateGaussianBlur.kt b/orx-fx/src/commonMain/kotlin/blur/ApproximateGaussianBlur.kt deleted file mode 100644 index b2fe5fae..00000000 --- a/orx-fx/src/commonMain/kotlin/blur/ApproximateGaussianBlur.kt +++ /dev/null @@ -1,85 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.blur - -import org.openrndr.draw.* -import org.openrndr.extra.fx.ColorBufferDescription -import org.openrndr.extra.fx.fx_approximate_gaussian_blur -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter - -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -/** - * Approximate separated Gaussian blur - */ -@Description("Approximate Gaussian blur") -class ApproximateGaussianBlur : Filter1to1(mppFilterShader(fx_approximate_gaussian_blur, "approximate gaussian blur")) { - - @BooleanParameter("wrap u") - var wrapU = false - - @BooleanParameter("wrap v") - var wrapV = false - - - /** - * blur sample window, default value is 5 - */ - @IntParameter("window size", 1, 25) - var window: Int by parameters - - /** - * spread multiplier, default value is 1.0 - */ - @DoubleParameter("kernel spread", 1.0, 4.0) - var spread: Double by parameters - - /** - * blur sigma, default value is 1.0 - */ - @DoubleParameter("kernel sigma", 0.0, 25.0) - var sigma: Double by parameters - - /** - * post blur gain, default value is 1.0 - */ - @DoubleParameter("gain", 0.0, 4.0) - var gain: Double by parameters - - private var intermediateCache = mutableMapOf() - - init { - window = 5 - spread = 1.0 - gain = 1.0 - sigma = 1.0 - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - val intermediateDescription = ColorBufferDescription(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type) - val intermediate = intermediateCache.getOrPut(intermediateDescription) { - colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type) - } - - intermediate.let { - parameters["wrapU"] = if (wrapU) 1 else 0 - parameters["wrapV"] = if (wrapV) 1 else 0 - parameters["blurDirection"] = Vector2(1.0, 0.0) - super.apply(source, arrayOf(it), clip) - - parameters["blurDirection"] = Vector2(0.0, 1.0) - super.apply(arrayOf(it), target, clip) - } - } - - override fun close() { - intermediateCache.values.forEach { it.close() } - intermediateCache.clear() - super.close() - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/blur/Bloom.kt b/orx-fx/src/commonMain/kotlin/blur/Bloom.kt deleted file mode 100644 index 530e1773..00000000 --- a/orx-fx/src/commonMain/kotlin/blur/Bloom.kt +++ /dev/null @@ -1,89 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.blur - -import org.openrndr.draw.* -import org.openrndr.extra.fx.blend.Add -import org.openrndr.extra.fx.fx_bloom -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter -import org.openrndr.shape.Rectangle - -@Description("Bloom") -class Bloom(blur: Filter = ApproximateGaussianBlur()) : Filter1to1(mppFilterShader(fx_bloom, "bloom")) { - /** - * the blur filter to use for the bloom, default is Approximate Gaussian Blur - */ - var blur: Filter = blur - - /** - * number of downsampled textures to use, default value is 2 - */ - @IntParameter("blur iterations", 1, 8) - var downsamples: Int = 2 - - /** - * rate of downsampling, f.ex: 4 -> 4x, 8x, 16x.., default value is 2 - */ - @IntParameter("downsamping rate", 1, 4) - var downsampleRate: Int = 2 - - /** - * blending amount between original image and blurred, default value is 0.5 - */ - @DoubleParameter("blend factor", 0.0, 1.0) - var blendFactor: Double by parameters - - /** - * brightness of the resulting image, default value is 0.5 - */ - @DoubleParameter("brightness", 0.0, 1.0) - var brightness: Double by parameters - - init { - blendFactor = 0.5 - brightness = 0.5 - } - - private val samplers: MutableList> = mutableListOf() - private var lastDownsampleRate = 0 - - private val blendAdd = Add() - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - val src = source[0] - val dest = target[0] - - if (samplers.isEmpty() || samplers.size != downsamples || lastDownsampleRate != downsampleRate) { - samplers.clear() - - lastDownsampleRate = downsampleRate - - for (downsample in 0 until downsamples) { - val div = 1 shl downsample - val bufferA = colorBuffer(dest.width / div, dest.height / div, 1.0, target[0].format, ColorType.FLOAT16) - val bufferB = colorBuffer(dest.width / div, dest.height / div, 1.0, target[0].format, ColorType.FLOAT16) - samplers.add(Pair(bufferA, bufferB)) - } - } - - for ((bufferA, _) in samplers) { - blur.apply(src, bufferA) - } - - for ((index, buffers) in samplers.asReversed().withIndex()) { - val (bufferCurrA) = buffers - - if (index + 1 <= samplers.lastIndex) { - val (bufferNextA, bufferNextB) = samplers.asReversed()[index + 1] - - blur.apply(bufferCurrA, bufferNextB) - blendAdd.apply(arrayOf(bufferNextA, bufferNextB), bufferNextA) - } else { - super.apply(arrayOf(src, bufferCurrA), target, clip) - } - } - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/blur/BoxBlur.kt b/orx-fx/src/commonMain/kotlin/blur/BoxBlur.kt deleted file mode 100644 index 5d37a29d..00000000 --- a/orx-fx/src/commonMain/kotlin/blur/BoxBlur.kt +++ /dev/null @@ -1,65 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.blur - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_box_blur -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter - -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -/** - * BoxBlur implemented as a separable filter - */ -@Description("Box-blur") -class BoxBlur : Filter1to1(mppFilterShader(fx_box_blur,"box-blur")) { - - data class ColorBufferDescription(val width: Int, val height: Int, val contentScale: Double, val format: ColorFormat, val type: ColorType) - - /** - * The sample window, default is 5 - */ - @IntParameter("window size", 1, 25) - var window: Int by parameters - - /** - * Spread multiplier, default is 1.0 - */ - @DoubleParameter("kernel spread", 1.0, 4.0) - var spread: Double by parameters - - /** - * Post-blur gain, default is 1.0 - */ - @DoubleParameter("gain", 0.0, 4.0) - var gain: Double by parameters - - private var intermediateCache = mutableMapOf() - - init { - window = 5 - spread = 1.0 - gain = 1.0 - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - val intermediateDescription = ColorBufferDescription(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type) - val intermediate = intermediateCache.getOrPut(intermediateDescription) { - colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type) - } - - parameters["wrapX"] = false - parameters["wrapY"] = false - intermediate.let { - parameters["blurDirection"] = Vector2(1.0, 0.0) - super.apply(source, arrayOf(it), clip) - - parameters["blurDirection"] = Vector2(0.0, 1.0) - super.apply(arrayOf(it), target, clip) - } - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/blur/DirectionalBlur.kt b/orx-fx/src/commonMain/kotlin/blur/DirectionalBlur.kt deleted file mode 100644 index d723db15..00000000 --- a/orx-fx/src/commonMain/kotlin/blur/DirectionalBlur.kt +++ /dev/null @@ -1,71 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.blur - - -import org.openrndr.draw.Filter2to1 -import org.openrndr.extra.fx.fx_directional_blur -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter - -/** - * Directional blur filter. Takes source image and direction buffer inputs - */ -@Description("Directional blur") -class DirectionalBlur : Filter2to1(mppFilterShader(fx_directional_blur, "directional-blur")) { - - /** - * Should the blur window be centered, default is false - */ - @BooleanParameter("center window") - var centerWindow: Boolean by parameters - - /** - * The sample window: how many samples to mix. The default is 5 - */ - @IntParameter("window size", 1, 25) - var window: Int by parameters - - /** - * Spread multiplier: the distance in pixels between sampled pixels. The default is 1.0 - */ - @DoubleParameter("kernel spread", 1.0, 4.0) - var spread: Double by parameters - - /** - * Post-blur gain, default is 1.0 - */ - @DoubleParameter("gain", 0.0, 4.0) - var gain: Double by parameters - - /** - * Should filter use directions perpendicular to those in the direction buffer? default is false - */ - @BooleanParameter("perpendicular") - var perpendicular: Boolean by parameters - - /** - * Wrap around left and right edges - */ - @BooleanParameter("wrapX") - var wrapX: Boolean by parameters - - /** - * Wrap around top and bottom edges - */ - @BooleanParameter("wrapY") - var wrapY: Boolean by parameters - - init { - window = 5 - spread = 1.0 - gain = 1.0 - perpendicular = false - centerWindow = false - wrapX = false - wrapY = false - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/blur/FrameBlur.kt b/orx-fx/src/commonMain/kotlin/blur/FrameBlur.kt deleted file mode 100644 index 00e5b0e9..00000000 --- a/orx-fx/src/commonMain/kotlin/blur/FrameBlur.kt +++ /dev/null @@ -1,61 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.blur - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.fx.blend.Passthrough -import org.openrndr.extra.fx.fx_frame_blur -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.shape.Rectangle - -@Description("Frame blur") -class FrameBlur(val colorType: ColorType = ColorType.FLOAT16) : - Filter1to1(mppFilterShader(fx_frame_blur, "frame-blur")) { - - @DoubleParameter("blend", 0.0, 1.0) - var blend: Double by parameters - - - val pt = Passthrough() - private var intermediate: ColorBuffer? = null - private var intermediate2: ColorBuffer? = null - - init { - blend = 0.5 - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - if (source.isNotEmpty() && target.isNotEmpty()) { - intermediate?.let { - if (it.isEquivalentTo(target[0], ignoreFormat = true, ignoreLevels = true)) { - it.destroy() - intermediate = null - } - } - intermediate2?.let { - if (it.isEquivalentTo(target[0], ignoreFormat = true, ignoreLevels = true)) { - it.destroy() - intermediate2 = null - } - } - - if (intermediate == null) { - intermediate = target[0].createEquivalent(type = colorType) - intermediate?.fill(ColorRGBa.TRANSPARENT) - } - if (intermediate2 == null) { - intermediate2 = target[0].createEquivalent(type = colorType) - intermediate2?.fill(ColorRGBa.TRANSPARENT) - } - - super.apply(arrayOf(source[0], intermediate!!), arrayOf(intermediate2!!), clip) - - pt.apply(intermediate2!!, intermediate!!) - pt.apply(intermediate!!, target[0]) - } - } -} diff --git a/orx-fx/src/commonMain/kotlin/blur/GaussianBlur.kt b/orx-fx/src/commonMain/kotlin/blur/GaussianBlur.kt deleted file mode 100644 index 0fe32c82..00000000 --- a/orx-fx/src/commonMain/kotlin/blur/GaussianBlur.kt +++ /dev/null @@ -1,49 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.blur - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_gaussian_blur -import org.openrndr.extra.fx.mppFilterShader - -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter - -/** - * Exact Gaussian blur, implemented as a single pass filter - */ -@Description("Gaussian blur") -class GaussianBlur : Filter1to1(mppFilterShader(fx_gaussian_blur,"gaussian-blur")) { - - /** - * The sample window, default value is 5 - */ - @IntParameter("window size", 1, 25) - var window: Int by parameters - - /** - * Spread multiplier, default value is 1.0 - */ - @DoubleParameter("kernel spread", 1.0, 4.0) - var spread: Double by parameters - - /** - * Blur kernel sigma, default value is 1.0 - */ - @DoubleParameter("kernel sigma", 0.0, 25.0) - var sigma: Double by parameters - - /** - * Post-blur gain, default value is 1.0 - */ - @DoubleParameter("gain", 0.0, 4.0) - var gain: Double by parameters - - init { - window = 5 - spread = 1.0 - sigma = 1.0 - gain = 1.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/blur/HashBlur.kt b/orx-fx/src/commonMain/kotlin/blur/HashBlur.kt deleted file mode 100644 index 54692899..00000000 --- a/orx-fx/src/commonMain/kotlin/blur/HashBlur.kt +++ /dev/null @@ -1,165 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.blur - -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.Filter2to1 -import org.openrndr.draw.Filter3to1 -import org.openrndr.extra.fx.fx_directional_hash_blur -import org.openrndr.extra.fx.fx_hash_blur -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter - -@Description("Hash blur") -class HashBlur : Filter1to1(mppFilterShader(fx_hash_blur, "hash-blur")) { - private var dynamic: Boolean by parameters - - - /** - * Blur radius in pixels, default is 5.0 - */ - @DoubleParameter("blur radius", 1.0, 25.0) - var radius: Double by parameters - - /** - * Time/seed, this should be fed with seconds, default is 0.0 - */ - var time: Double by parameters - - /** - * Number of samples, default is 30 - */ - @IntParameter("number of samples", 1, 100) - var samples: Int by parameters - - /** - * Post-blur gain, default is 1.0 - */ - @DoubleParameter("image gain", 0.0, 2.0) - var gain: Double by parameters - - init { - dynamic = false - radius = 5.0 - time = 0.0 - samples = 30 - gain = 1.0 - } -} - -@Description("Hash blur") -class HashBlurDynamic: Filter2to1(mppFilterShader(fx_hash_blur, "hash-blur")) { - - private var dynamic: Boolean by parameters - - /** - * Blur radius in pixels, default is 5.0 - */ - @DoubleParameter("blur radius", 1.0, 25.0) - var radius: Double by parameters - - /** - * Time/seed, this should be fed with seconds, default is 0.0 - */ - var time: Double by parameters - - /** - * Number of samples, default is 30 - */ - @IntParameter("number of samples", 1, 100) - var samples: Int by parameters - - /** - * Post-blur gain, default is 1.0 - */ - @DoubleParameter("image gain", 0.0, 2.0) - var gain: Double by parameters - - init { - dynamic = true - radius = 5.0 - time = 0.0 - samples = 30 - gain = 1.0 - } -} - -@Description("Directional hash blur") -class DirectionalHashBlur : Filter2to1(mppFilterShader(fx_directional_hash_blur, "directional-hash-blur")) { - - /** - * Blur radius in pixels, default is 5.0 - */ - @DoubleParameter("blur radius", 0.0, 25.0) - var radius: Double by parameters - - @DoubleParameter("blur spread", 0.0, 25.0) - var spread: Double by parameters - - - /** - * Time/seed, this should be fed with seconds, default is 0.0 - */ - var time: Double by parameters - - /** - * Number of samples, default is 30 - */ - @IntParameter("number of samples", 1, 100) - var samples: Int by parameters - - /** - * Post-blur gain, default is 1.0 - */ - @DoubleParameter("image gain", 0.0, 2.0) - var gain: Double by parameters - - init { - radius = 5.0 - spread = 25.0 - time = 0.0 - samples = 30 - gain = 1.0 - } -} - -@Description("Directional hash blur") -class DirectionalHashBlurDynamic : Filter3to1(mppFilterShader("#define RADIUS_FROM_TEXTURE\n${fx_directional_hash_blur}", "directional-hash-blur")) { - - /** - * Blur radius in pixels, default is 5.0 - */ - @DoubleParameter("blur radius", 0.0, 25.0) - var radius: Double by parameters - - @DoubleParameter("blur spread", 0.0, 25.0) - var spread: Double by parameters - - - /** - * Time/seed, this should be fed with seconds, default is 0.0 - */ - var time: Double by parameters - - /** - * Number of samples, default is 30 - */ - @IntParameter("number of samples", 1, 100) - var samples: Int by parameters - - /** - * Post-blur gain, default is 1.0 - */ - @DoubleParameter("image gain", 0.0, 2.0) - var gain: Double by parameters - - init { - radius = 5.0 - spread = 25.0 - time = 0.0 - samples = 30 - gain = 1.0 - } -} diff --git a/orx-fx/src/commonMain/kotlin/blur/LaserBlur.kt b/orx-fx/src/commonMain/kotlin/blur/LaserBlur.kt deleted file mode 100644 index 63b8b01a..00000000 --- a/orx-fx/src/commonMain/kotlin/blur/LaserBlur.kt +++ /dev/null @@ -1,105 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.blur - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_laser_blur -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.* -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle -import kotlin.math.pow - -private class LaserBlurPass : Filter(mppFilterShader(fx_laser_blur, "laser-blur")) { - var radius: Double by parameters - var amp0: Double by parameters - var amp1: Double by parameters - var center: Vector2 by parameters - var vignette: Double by parameters - var vignetteSize: Double by parameters - var aberration: Double by parameters - - init { - radius = 0.0 - amp0 = 1.0 - amp1 = 1.0 - center = Vector2.ZERO - vignette = 0.0 - vignetteSize = 1.0 - aberration = 0.0 - } -} - -@Description("Laser blur") -class LaserBlur : Filter1to1(null) { - @Vector2Parameter("center", order = 0) - var center = Vector2.ZERO - - @DoubleParameter("radius", -2.0, 2.0, order = 1) - var radius = -0.18 - - @DoubleParameter("amp0", 0.0, 1.0, order = 2) - var amp0 = 0.5 - - @DoubleParameter("amp1", 0.0, 1.0, order = 3) - var amp1 = 0.5 - - @DoubleParameter("vignette", 0.0, 1.0, order = 4) - var vignette = 0.0 - - @DoubleParameter("vignette size", 0.0, 1.0, order = 5) - var vignetteSize = 0.0 - - @DoubleParameter("aberration", -1.0, 1.0, order = 6) - var aberration = 0.006 - - @DoubleParameter("exp", -1.0, 1.0, order = 7) - var exp = 0.739 - - @DoubleParameter("phase", -1.0, 1.0, order = 7) - var phase = 0.0 - - - private val pass = LaserBlurPass() - - @IntParameter("passes", 2, 32, order = 4) - var passes = 15 - - val intermediates = mutableListOf() - - override fun apply(source: Array, target: Array, clip:Rectangle?) { - pass.center = center - pass.radius = radius - pass.amp0 = amp0 - pass.amp1 = amp1 - pass.vignette = vignette - pass.vignetteSize = vignetteSize - pass.aberration = aberration - - if ((!intermediates.all { it.isEquivalentTo(source[0], ignoreFormat = true, ignoreType = true) })) { - intermediates.forEach { - it.destroy() - } - intermediates.clear() - } - if (intermediates.size == 0) { - intermediates.add(source[0].createEquivalent(type = ColorType.FLOAT16)) - intermediates.add(source[0].createEquivalent(type = ColorType.FLOAT16)) - } - - pass.radius = 1.0 + pow(exp, 0.0) * radius - - pass.apply(source[0], intermediates[0], clip) - for (i in 0 until passes - 1) { - - pass.radius = 1.0 + pow(exp, i + 1.0) * radius //(1.0 + simplex(0, phase + i)) / 2.0 - pass.apply(intermediates[i % 2], intermediates[(i + 1) % 2], clip) - } - pass.radius = 1.0 + pow(exp, (passes) * 1.0) * radius - pass.apply(intermediates[(passes + 1) % 2], target[0], clip) - } -} - -private fun pow(a: Double, x: Double): Double { - return a.pow(x) -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/blur/LineBlur.kt b/orx-fx/src/commonMain/kotlin/blur/LineBlur.kt deleted file mode 100644 index a3d439a5..00000000 --- a/orx-fx/src/commonMain/kotlin/blur/LineBlur.kt +++ /dev/null @@ -1,68 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.blur - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_box_blur -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter - -import org.openrndr.math.Vector2 -import org.openrndr.math.asRadians -import org.openrndr.shape.Rectangle -import kotlin.math.cos -import kotlin.math.sin - -/** - * BoxBlur implemented as a separable filter - */ -@Description("Line blur") -class LineBlur : Filter1to1(mppFilterShader(fx_box_blur, "line-blur")) { - - /** - * The sample window, default is 5 - */ - @IntParameter("window size", 1, 25) - var window: Int by parameters - - /** - * Spread multiplier, default is 1.0 - */ - @DoubleParameter("kernel spread", 1.0, 4.0) - var spread: Double by parameters - - /** - * Post-blur gain, default is 1.0 - */ - @DoubleParameter("gain", 0.0, 4.0) - var gain: Double by parameters - - - @DoubleParameter("blur angle", -180.0, 180.0) - var blurAngle: Double by parameters - - @BooleanParameter("wrap x", order = 9) - var wrapX: Boolean by parameters - - @BooleanParameter("wrap y", order = 10) - var wrapY: Boolean by parameters - - - - init { - window = 5 - spread = 1.0 - gain = 1.0 - blurAngle = 0.0 - wrapX = false - wrapY = false - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - parameters["blurDirection"] = Vector2(cos(blurAngle.asRadians), sin(blurAngle.asRadians)) - super.apply(source, target, clip) - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/blur/MipBloom.kt b/orx-fx/src/commonMain/kotlin/blur/MipBloom.kt deleted file mode 100644 index 94849f99..00000000 --- a/orx-fx/src/commonMain/kotlin/blur/MipBloom.kt +++ /dev/null @@ -1,155 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.blur - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_bloom_combine -import org.openrndr.extra.fx.fx_bloom_downscale -import org.openrndr.extra.fx.fx_bloom_upscale -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter -import org.openrndr.shape.Rectangle - -class BloomDownscale : Filter(mppFilterShader(fx_bloom_downscale,"bloom-downscale")) - -class BloomUpscale : Filter(mppFilterShader(fx_bloom_upscale, "bloom-upscale")) { - var gain: Double by parameters - var shape: Double by parameters - var noiseSeed: Double by parameters - var noiseGain: Double by parameters - - init { - gain = 1.0 - shape = 1.0 - noiseSeed = 1.0 - noiseGain = 0.25 - } -} - -class BloomCombine : Filter(mppFilterShader(fx_bloom_combine, "bloom-combine")) { - var gain: Double by parameters - var pregain: Double by parameters - var bias: ColorRGBa by parameters - - init { - bias = ColorRGBa.BLACK - gain = 1.0 - pregain = 1.0 - } -} - -@Description("MipBloom") -open class MipBloom(val blur: T) : Filter1to1(mppFilterShader(fx_bloom_combine, "bloom-combine")) { - var passes = 6 - - @DoubleParameter("shape", 0.0, 4.0) - var shape: Double = 1.0 - - @DoubleParameter("gain", 0.0, 4.0) - var gain: Double = 1.0 - - @DoubleParameter("pregain", 0.0, 4.0) - var pregain: Double = 1.0 - - - /** - * noise gain. low noise gains will result in visible banding of the image. default value is 0.25 - */ - @DoubleParameter("noise gain", 0.0, 1.0) - var noiseGain: Double = 0.25 - - @DoubleParameter("noise seed", 0.0, 1000.0) - var noiseSeed: Double = 0.0 - var intermediates = mutableListOf() - var blurred = mutableListOf() - - val upscale = BloomUpscale() - val downScale = BloomDownscale() - val combine = BloomCombine() - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - - upscale.shape = shape - if (intermediates.size != passes - || (intermediates.isNotEmpty() && (!intermediates[0].isEquivalentTo(target[0], ignoreType = true, ignoreFormat = true)))) { - intermediates.forEach { - it.destroy() - } - blurred.forEach { - it.destroy() - } - intermediates.clear() - blurred.clear() - - for (pass in 0 until passes) { - val tdiv = 1 shl (pass + 1) - val cb = colorBuffer(target[0].width / tdiv, target[0].height / tdiv, type = ColorType.FLOAT32) - intermediates.add(cb) - val cbb = colorBuffer(target[0].width / tdiv, target[0].height / tdiv, type = ColorType.FLOAT32) - blurred.add(cbb) - } - } - - upscale.noiseGain = noiseGain - upscale.noiseSeed = noiseSeed - downScale.apply(source[0], intermediates[0], clip) - blur.apply(intermediates[0], blurred[0], clip) - - for (pass in 1 until passes) { - downScale.apply(blurred[pass - 1], intermediates[pass], clip) - blur.apply(intermediates[pass], blurred[pass], clip) - } - - upscale.apply(blurred.toTypedArray(), arrayOf(target[0]), clip) - combine.gain = gain - combine.pregain = pregain - combine.apply(arrayOf(source[0], target[0]), target, clip) - } -} - -@Description("Hash bloom") -class HashBloom : MipBloom(blur = HashBlur()) { - - /** - * Blur radius in pixels, default is 5.0 - */ - @DoubleParameter("blur radius", 1.0, 25.0) - var radius: Double = 5.0 - - /** - * Number of samples, default is 30 - */ - @IntParameter("number of samples", 1, 100) - var samples: Int = 30 - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - blur.radius = radius - blur.samples = samples - super.apply(source, target, clip) - } -} - -@Description("Gaussian bloom") -class GaussianBloom : MipBloom(blur = GaussianBlur()) { - /** - * blur sample window, default value is 5 - */ - @IntParameter("window size", 1, 25) - var window: Int = 5 - - /** - * blur sigma, default value is 1.0 - */ - @DoubleParameter("kernel sigma", 0.0, 25.0) - var sigma: Double = 1.0 - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - blur.window = window - blur.sigma = sigma - super.apply(source, target, clip) - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/blur/ZoomBlur.kt b/orx-fx/src/commonMain/kotlin/blur/ZoomBlur.kt deleted file mode 100644 index 7b8b673f..00000000 --- a/orx-fx/src/commonMain/kotlin/blur/ZoomBlur.kt +++ /dev/null @@ -1,46 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.blur - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_zoom_blur -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -@Description("Zoom Blur") -class ZoomBlur : Filter1to1(mppFilterShader(fx_zoom_blur, "zoom-blur")) { - var center: Vector2 by parameters - - @DoubleParameter("strength", 0.0, 1.0) - var strength: Double by parameters - - init { - center = Vector2.ONE / 2.0 - strength = 0.2 - } - - private var intermediate: ColorBuffer? = null - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - intermediate?.let { - if (it.width != target[0].width || it.height != target[0].height) { - intermediate = null - } - } - - if (intermediate == null) { - intermediate = - colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type) - } - - intermediate?.let { - parameters["dimensions"] = Vector2(it.effectiveWidth.toDouble(), it.effectiveHeight.toDouble()) - super.apply(source, arrayOf(it), clip) - it.copyTo(target[0]) - } - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/color/ChromaticAberration.kt b/orx-fx/src/commonMain/kotlin/color/ChromaticAberration.kt deleted file mode 100644 index 3736306b..00000000 --- a/orx-fx/src/commonMain/kotlin/color/ChromaticAberration.kt +++ /dev/null @@ -1,46 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_chromatic_aberration -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -@Description("Chromatic Aberration") -class ChromaticAberration : Filter1to1(mppFilterShader(fx_chromatic_aberration, "chromatic-aberration")) { - /** - * aberration factor, default value is 8.0 - */ - @DoubleParameter("aberration factor", 0.0, 16.0) - var aberrationFactor: Double by parameters - - init { - aberrationFactor = 8.0 - } - - private var intermediate: ColorBuffer? = null - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - - intermediate?.let { - if (it.width != target[0].width || it.height != target[0].height) { - intermediate = null - } - } - - if (intermediate == null) { - intermediate = colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type) - } - - intermediate?.let { - parameters["dimensions"] = Vector2(it.effectiveWidth.toDouble(), it.effectiveHeight.toDouble()) - super.apply(source, arrayOf(it), clip) - it.copyTo(target[0]) - } - } -} diff --git a/orx-fx/src/commonMain/kotlin/color/ColorCorrection.kt b/orx-fx/src/commonMain/kotlin/color/ColorCorrection.kt deleted file mode 100644 index 96d61386..00000000 --- a/orx-fx/src/commonMain/kotlin/color/ColorCorrection.kt +++ /dev/null @@ -1,44 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_color_correction -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Color correction") -class ColorCorrection : Filter1to1(mppFilterShader(fx_color_correction, "color-correction")) { - @DoubleParameter("brightness", -1.0, 1.0, order = 0) - var brightness: Double by parameters - - @DoubleParameter("contrast", -1.0, 1.0, order = 1) - var contrast: Double by parameters - - @DoubleParameter("saturation", -1.0, 1.0, order = 2) - var saturation: Double by parameters - - @DoubleParameter("hue shift", -180.0, 180.0, order = 3) - var hueShift: Double by parameters - - @DoubleParameter("gamma", 0.0, 5.0, order = 4) - var gamma: Double by parameters - - @DoubleParameter("opacity", 0.0, 1.0, order = 5) - var opacity: Double by parameters - - @BooleanParameter("clamp", order = 6) - var clamped: Boolean by parameters - - init { - contrast = 0.0 - brightness = 0.0 - saturation = 0.0 - hueShift = 0.0 - gamma = 1.0 - opacity = 1.0 - clamped = true - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/color/ColorLookup.kt b/orx-fx/src/commonMain/kotlin/color/ColorLookup.kt deleted file mode 100644 index 9bcef9da..00000000 --- a/orx-fx/src/commonMain/kotlin/color/ColorLookup.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.openrndr.extra.fx.color - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_color_lookup -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.shape.Rectangle - -class ColorLookup(lookup: ColorBuffer) : Filter1to1(mppFilterShader(fx_color_lookup, "color-lookup")) { - /** a color look-up texture */ - var lookup: ColorBuffer by parameters - - /** - * noise gain in look-up, default value is 0.0 - */ - var noiseGain: Double by parameters - - /** - * noise seed, default value is 0.0 - */ - var seed: Double by parameters - - init { - this.lookup = lookup - this.noiseGain = 0.0 - this.seed = 0.0 - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - lookup.filter(MinifyingFilter.LINEAR, MagnifyingFilter.LINEAR) - super.apply(source, target, clip) - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/color/ColorMix.kt b/orx-fx/src/commonMain/kotlin/color/ColorMix.kt deleted file mode 100644 index 7a92b2f1..00000000 --- a/orx-fx/src/commonMain/kotlin/color/ColorMix.kt +++ /dev/null @@ -1,23 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_color_mix -import org.openrndr.extra.fx.fx_color_tint -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description - -class ColorMix : Filter1to1(mppFilterShader(fx_color_mix, "color-mix")) - -@Description("Tint") -class ColorTint : Filter1to1(mppFilterShader(fx_color_tint, "color-tint")) { - @ColorParameter("tint") - var tint: ColorRGBa by parameters - - init { - tint = ColorRGBa.PINK - } -} diff --git a/orx-fx/src/commonMain/kotlin/color/Colorspaces.kt b/orx-fx/src/commonMain/kotlin/color/Colorspaces.kt deleted file mode 100644 index b87fe690..00000000 --- a/orx-fx/src/commonMain/kotlin/color/Colorspaces.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.openrndr.extra.fx.color - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.color.phrases.ColorPhraseBook -import org.openrndr.extra.fx.* -import org.openrndr.extra.shaderphrases.preprocess - -class RgbToYCbcr : Filter1to1(mppFilterShader(fx_rgb_to_ycbcr, "rgb-to-ycbcr")) -class YcbcrToRgb : Filter1to1(mppFilterShader(fx_ycbcr_to_rgb, "ycbcr_to_rgb")) - -class RgbToOkLab : Filter1to1(mppFilterShader(run { - ColorPhraseBook.register() - fx_rgb_to_oklab.preprocess() -}, "rgb-to-oklab")) - -class OkLabToRgb : Filter1to1(mppFilterShader(run { - ColorPhraseBook.register() - fx_oklab_to_rgb.preprocess() -}, "oklab-to-rgb")) diff --git a/orx-fx/src/commonMain/kotlin/color/Duotone.kt b/orx-fx/src/commonMain/kotlin/color/Duotone.kt deleted file mode 100644 index 36a0bb05..00000000 --- a/orx-fx/src/commonMain/kotlin/color/Duotone.kt +++ /dev/null @@ -1,37 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.color.phrases.ColorPhraseBook -import org.openrndr.extra.color.presets.CORAL -import org.openrndr.extra.color.presets.NAVY -import org.openrndr.extra.fx.fx_duotone -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.shaderphrases.preprocess - -@Description("Duotone") -class Duotone : Filter1to1(filterShaderFromCode(run { - ColorPhraseBook.register() - fx_duotone.preprocess() -}, "duotone")) { - - @ColorParameter("background", order = 0) - var backgroundColor: ColorRGBa by parameters - - @ColorParameter("foreground", order = 1) - var foregroundColor: ColorRGBa by parameters - - @BooleanParameter("LAB interpolation", order = 2) - var labInterpolation: Boolean by parameters - - init { - backgroundColor = ColorRGBa.NAVY - foregroundColor = ColorRGBa.CORAL - labInterpolation = true - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/color/DuotoneGradient.kt b/orx-fx/src/commonMain/kotlin/color/DuotoneGradient.kt deleted file mode 100644 index 52c56868..00000000 --- a/orx-fx/src/commonMain/kotlin/color/DuotoneGradient.kt +++ /dev/null @@ -1,50 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.color.phrases.ColorPhraseBook -import org.openrndr.extra.color.presets.CORAL -import org.openrndr.extra.color.presets.NAVY -import org.openrndr.extra.fx.fx_duotone_gradient -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.shaderphrases.preprocess - -@Description("Duotone Gradient") -class DuotoneGradient : Filter1to1(filterShaderFromCode(run { - ColorPhraseBook.register() - fx_duotone_gradient.preprocess() -}, "duotone-gradient")) { - - @ColorParameter("background 0", order = 0) - var backgroundColor0: ColorRGBa by parameters - - @ColorParameter("foreground 0", order = 1) - var foregroundColor0: ColorRGBa by parameters - - @ColorParameter("background 1", order = 2) - var backgroundColor1: ColorRGBa by parameters - - @ColorParameter("foreground 1", order = 3) - var foregroundColor1: ColorRGBa by parameters - - @BooleanParameter("LAB interpolation", order = 4) - var labInterpolation: Boolean by parameters - - @DoubleParameter("rotation", -180.0, 180.0, order = 5) - var rotation: Double by parameters - - init { - backgroundColor0 = ColorRGBa.NAVY - foregroundColor0 = ColorRGBa.CORAL - backgroundColor1 = ColorRGBa.BLACK - foregroundColor1 = ColorRGBa.WHITE - rotation = 0.0 - labInterpolation = true - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/color/Invert.kt b/orx-fx/src/commonMain/kotlin/color/Invert.kt deleted file mode 100644 index 8eb06357..00000000 --- a/orx-fx/src/commonMain/kotlin/color/Invert.kt +++ /dev/null @@ -1,19 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_invert -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Invert") -class Invert : Filter1to1(mppFilterShader(fx_invert, "invert")) { - @DoubleParameter("amount", 0.0, 1.0) - var amount: Double by parameters - - init { - amount = 1.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/color/LumaMap.kt b/orx-fx/src/commonMain/kotlin/color/LumaMap.kt deleted file mode 100644 index 9ad50c50..00000000 --- a/orx-fx/src/commonMain/kotlin/color/LumaMap.kt +++ /dev/null @@ -1,33 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_luma_map -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Luma map ") -class LumaMap : Filter1to1(mppFilterShader(fx_luma_map, "luma-map")) { - @ColorParameter("foreground color") - var foreground: ColorRGBa by parameters - - @ColorParameter("background color") - var background: ColorRGBa by parameters - - @DoubleParameter("background opacity", 0.0, 1.0) - var backgroundOpacity: Double by parameters - - @DoubleParameter("foreground opacity", 0.0, 1.0) - var foregroundOpacity: Double by parameters - - init { - foreground = ColorRGBa.WHITE - background = ColorRGBa.BLACK - foregroundOpacity = 1.0 - backgroundOpacity = 1.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/color/LumaOpacity.kt b/orx-fx/src/commonMain/kotlin/color/LumaOpacity.kt deleted file mode 100644 index a57f5103..00000000 --- a/orx-fx/src/commonMain/kotlin/color/LumaOpacity.kt +++ /dev/null @@ -1,31 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_luma_opacity -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Luma map ") -class LumaOpacity : Filter1to1(mppFilterShader(fx_luma_opacity, "luma-opacity")) { - @DoubleParameter("foreground luma",0.0, 1.0) - var foregroundLuma: Double by parameters - - @DoubleParameter("background luma", 0.0,1.0) - var backgroundLuma: Double by parameters - - @DoubleParameter("background opacity", 0.0, 1.0, order = 0) - var backgroundOpacity: Double by parameters - - @DoubleParameter("foreground opacity", 0.0, 1.0, order = 1) - var foregroundOpacity: Double by parameters - - init { - foregroundLuma = 1.0 - backgroundLuma = 0.0 - foregroundOpacity = 1.0 - backgroundOpacity = 0.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/color/LumaThreshold.kt b/orx-fx/src/commonMain/kotlin/color/LumaThreshold.kt deleted file mode 100644 index dcb13a0e..00000000 --- a/orx-fx/src/commonMain/kotlin/color/LumaThreshold.kt +++ /dev/null @@ -1,37 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_luma_threshold -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Luma threshold ") -class LumaThreshold : Filter1to1(mppFilterShader(fx_luma_threshold, "luma-threshold")) { - @DoubleParameter("threshold value", 0.0, 1.0) - var threshold: Double by parameters - - @ColorParameter("foreground color") - var foreground: ColorRGBa by parameters - - @ColorParameter("background color") - var background: ColorRGBa by parameters - - @DoubleParameter("background opacity", 0.0, 1.0) - var backgroundOpacity: Double by parameters - - @DoubleParameter("foreground opacity", 0.0, 1.0) - var foregroundOpacity: Double by parameters - - init { - threshold = 0.5 - foreground = ColorRGBa.WHITE - background = ColorRGBa.BLACK - foregroundOpacity = 1.0 - backgroundOpacity = 1.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/color/Pal.kt b/orx-fx/src/commonMain/kotlin/color/Pal.kt deleted file mode 100644 index a5e223db..00000000 --- a/orx-fx/src/commonMain/kotlin/color/Pal.kt +++ /dev/null @@ -1,27 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_pal -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Pal TV Effect") -class Pal : Filter1to1(mppFilterShader(fx_pal,"pal")) { - @DoubleParameter("amount", 0.0, 1.0) - var amount: Double by parameters - @DoubleParameter("pixelation", 0.0, 1.0) - var pixelation: Double by parameters - @DoubleParameter("filter_gain", 0.0, 10.0) - var filter_gain: Double by parameters - @DoubleParameter("filter_invgain", 0.0, 10.0) - var filter_invgain: Double by parameters - init { - amount = 1.0 - pixelation = 0.0 - filter_gain = 1.0 - filter_invgain = 1.6 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/color/Posterize.kt b/orx-fx/src/commonMain/kotlin/color/Posterize.kt deleted file mode 100644 index dfa340a1..00000000 --- a/orx-fx/src/commonMain/kotlin/color/Posterize.kt +++ /dev/null @@ -1,24 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.fx.fx_posterize -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.IntParameter - -@Description("Posterize") -class Posterize : Filter1to1(filterShaderFromCode(fx_posterize, "posterize")) { - - @IntParameter("levels", 2, 32, order = 0) - var levels: Int by parameters - - @IntParameter("window", 1, 8, order = 0) - var window: Int by parameters - - init { - levels = 4 - window = 1 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/color/Sepia.kt b/orx-fx/src/commonMain/kotlin/color/Sepia.kt deleted file mode 100644 index 8af108cc..00000000 --- a/orx-fx/src/commonMain/kotlin/color/Sepia.kt +++ /dev/null @@ -1,19 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_sepia -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Sepia") -class Sepia : Filter1to1(mppFilterShader(fx_sepia, "sepia")) { - @DoubleParameter("amount", 0.0, 1.0) - var amount: Double by parameters - - init { - amount = 0.5 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/color/SetBackground.kt b/orx-fx/src/commonMain/kotlin/color/SetBackground.kt deleted file mode 100644 index 9ba43772..00000000 --- a/orx-fx/src/commonMain/kotlin/color/SetBackground.kt +++ /dev/null @@ -1,25 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.color - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_set_background -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Set background") -class SetBackground : Filter1to1(mppFilterShader(fx_set_background, "set-background")) { - @ColorParameter("background color") - var background: ColorRGBa by parameters - - @DoubleParameter("background opacity", 0.0, 1.0) - var backgroundOpacity: Double by parameters - - init { - background = ColorRGBa.GRAY - backgroundOpacity = 1.0 - } -} diff --git a/orx-fx/src/commonMain/kotlin/color/SubtractConstant.kt b/orx-fx/src/commonMain/kotlin/color/SubtractConstant.kt deleted file mode 100644 index 3d2b9f68..00000000 --- a/orx-fx/src/commonMain/kotlin/color/SubtractConstant.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.openrndr.extra.fx.color - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_subtract_constant -import org.openrndr.extra.fx.mppFilterShader - -class SubtractConstant : Filter1to1(mppFilterShader(fx_subtract_constant, "subtract-constant")) { - var constant: ColorRGBa by parameters - - init { - constant = ColorRGBa(1.0, 1.0, 1.0, 0.0) - } -} diff --git a/orx-fx/src/commonMain/kotlin/colormap/ColormapFilter.kt b/orx-fx/src/commonMain/kotlin/colormap/ColormapFilter.kt deleted file mode 100644 index 1600905e..00000000 --- a/orx-fx/src/commonMain/kotlin/colormap/ColormapFilter.kt +++ /dev/null @@ -1,26 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.colormap - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.DoubleParameter - -abstract class ColormapFilter(code: String, name: String) : Filter1to1(mppFilterShader(code, name)) { - - @DoubleParameter(label = "min value", low = 0.0, high = 1.0, order = 0) - var minValue: Double by parameters - - @DoubleParameter(label = "max value", low = 0.0, high = 1.0, order = 1) - var maxValue: Double by parameters - - @DoubleParameter(label = "curve", low = 0.001, high = 10.0, order = 2) - var curve: Double by parameters - - init { - minValue = 0.0 - maxValue = 1.0 - curve = 1.0 - } - -} diff --git a/orx-fx/src/commonMain/kotlin/colormap/GrayscaleColormap.kt b/orx-fx/src/commonMain/kotlin/colormap/GrayscaleColormap.kt deleted file mode 100644 index 2a082e73..00000000 --- a/orx-fx/src/commonMain/kotlin/colormap/GrayscaleColormap.kt +++ /dev/null @@ -1,12 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.colormap - -import org.openrndr.extra.fx.fx_grayscale_colormap -import org.openrndr.extra.parameters.Description - -/** - * Maps values of the RED color channel to grayscale. - */ -@Description("grayscale colormap") -class GrayscaleColormap : ColormapFilter(fx_grayscale_colormap, "grayscale-colormap") diff --git a/orx-fx/src/commonMain/kotlin/colormap/SpectralZucconiColormap.kt b/orx-fx/src/commonMain/kotlin/colormap/SpectralZucconiColormap.kt deleted file mode 100644 index 81fd81ee..00000000 --- a/orx-fx/src/commonMain/kotlin/colormap/SpectralZucconiColormap.kt +++ /dev/null @@ -1,26 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.colormap - -import org.openrndr.extra.color.colormaps.ColormapPhraseBook -import org.openrndr.extra.fx.fx_spectral_zucconi_colormap -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.shaderphrases.preprocess - -/** - * Maps values of the RED color channel to natural light dispersion spectrum as described - * by Alan Zucconi in the - * [Improving the Rainbow](https://www.alanzucconi.com/2017/07/15/improving-the-rainbow/) - * article. - * - * @see ColormapPhraseBook.spectralZucconi6 - * @see org.openrndr.extra.color.colormaps.spectralZucconi6 - */ -@Description("spectral colormap") -class SpectralZucconiColormap : ColormapFilter( - code = run { - ColormapPhraseBook.register() - fx_spectral_zucconi_colormap.preprocess() - }, - name = "spectral-zucconi-colormap" -) diff --git a/orx-fx/src/commonMain/kotlin/colormap/TurboColormap.kt b/orx-fx/src/commonMain/kotlin/colormap/TurboColormap.kt deleted file mode 100644 index 771e5677..00000000 --- a/orx-fx/src/commonMain/kotlin/colormap/TurboColormap.kt +++ /dev/null @@ -1,25 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.colormap - -import org.openrndr.extra.color.colormaps.ColormapPhraseBook -import org.openrndr.extra.fx.fx_turbo_colormap -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.shaderphrases.preprocess - -/** - * Maps values of the RED color channel to Turbo Colormap according to - * [Turbo, An Improved Rainbow Colormap for Visualization](https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html) - * by Google. - * - * @see ColormapPhraseBook.spectralZucconi6 - * @see org.openrndr.extra.color.colormaps.spectralZucconi6 - */ -@Description("turbo colormap") -class TurboColormap : ColormapFilter( - code = run { - ColormapPhraseBook.register() - fx_turbo_colormap.preprocess() - }, - name = "turbo-colormap" -) diff --git a/orx-fx/src/commonMain/kotlin/composite/CompositeFilter.kt b/orx-fx/src/commonMain/kotlin/composite/CompositeFilter.kt deleted file mode 100644 index 9a56715b..00000000 --- a/orx-fx/src/commonMain/kotlin/composite/CompositeFilter.kt +++ /dev/null @@ -1,123 +0,0 @@ -package org.openrndr.extra.fx.composite - -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.Filter -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.isEquivalentTo -import org.openrndr.shape.Rectangle - -/** - * @param first the filter that is applied first - * @param second the filter that is applied second - * @param firstSource a function that maps source color buffers for the [first] filter - * @param secondSource a function that maps source color buffers for the [second] filter - * @param firstParameters a function that sets parameters for the [first] filter - * @param secondParameters a function that sets parameters for the [second] fillter - * @param useIntermediateBuffer should an intermediate buffer be maintained? when set to false the [first] filter will - * write to the target color buffer - */ -class CompositeFilter( - val first: F0, - val second: F1, - private val firstSource: (List) -> List, - private val secondSource: (List, ColorBuffer) -> List, - private val firstParameters: F0.() -> Unit, - private val secondParameters: F1.() -> Unit, - private val useIntermediateBuffer: Boolean = false -) : Filter(null) { - private var intermediate: ColorBuffer? = null - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - val firstSource = firstSource(source.toList()).toTypedArray() - if (!useIntermediateBuffer) { - first.firstParameters() - first.apply(firstSource, target, clip) - - second.secondParameters() - val secondSource = secondSource(source.toList(), target.first()).toTypedArray() - second.apply(secondSource, target, clip) - } else { - val li = intermediate - if (li != null && !li.isEquivalentTo(target.first())) { - li.destroy() - intermediate = null - } - if (intermediate == null) { - intermediate = target.first().createEquivalent() - } - first.firstParameters() - first.apply(firstSource, arrayOf(intermediate!!), clip) - val secondSource = secondSource(source.toList(), intermediate!!).toTypedArray() - second.secondParameters() - second.apply(secondSource, target, clip) - } - } - - override fun destroy() { - intermediate?.destroy() - super.destroy() - } -} - -class CompositeFilterBuilder(val first: F0, val second: F1) { - private var firstSourceFunction: (inputs: List) -> List = { inputs -> inputs } - private var secondSourceFunction: (inputs: List, intermediate: ColorBuffer) -> List = - { inputs, intermediate -> listOf(intermediate) + inputs.drop(1) } - - private var firstParametersFunction: (F0.() -> Unit) = {} - private var secondParametersFunction: (F1.() -> Unit) = {} - - - /** Supply the function that sets the source color buffers for the [first] filter */ - fun firstSource(function: (source: List) -> List) { - firstSourceFunction = function - } - - /** Supply the function that sets the source color buffers for the [second] filter */ - fun secondSource(function: (source: List, intermediate: ColorBuffer) -> List) { - secondSourceFunction = function - } - - /** - * Supply the function that sets the filter parameters for the [first] filter - */ - fun firstParameters(function: (F0.() -> Unit)) { - firstParametersFunction = function - } - - /** - * Supply the function that sets the filter parameter the [second] filter - */ - fun secondParameters(function: (F1.() -> Unit)) { - secondParametersFunction = function - } - - /** - * Should an intermediate color buffer be used? - */ - var useIntermediateBuffer = true - - fun build(): CompositeFilter { - return CompositeFilter( - first, - second, - firstSourceFunction, - secondSourceFunction, - firstParametersFunction, - secondParametersFunction, - useIntermediateBuffer - ) - } -} - -/** - * Create a composite filter that first applies [this] filter and then the [next] filter. - */ -fun F0.then( - next: F1, - builder: CompositeFilterBuilder.() -> Unit = {} -): CompositeFilter { - val compositeFilterBuilder = CompositeFilterBuilder(this, next) - compositeFilterBuilder.builder() - return compositeFilterBuilder.build() -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/distort/BlockRepeat.kt b/orx-fx/src/commonMain/kotlin/distort/BlockRepeat.kt deleted file mode 100644 index 6a8d61cf..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/BlockRepeat.kt +++ /dev/null @@ -1,60 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_block_repeat -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.shape.Rectangle - -@Description("Block repeat") -class BlockRepeat : Filter1to1(mppFilterShader(fx_block_repeat, "block-repeat")) { - @DoubleParameter("block width", 0.0, 1.0, order = 0) - var blockWidth: Double by parameters - - @DoubleParameter("block height", 0.0, 1.0, order = 1) - var blockHeight: Double by parameters - - @DoubleParameter("block x-offset", -0.5, 0.5, order = 2) - var blockOffsetX: Double by parameters - - @DoubleParameter("block y-offset", -0.5, 0.5, order = 3) - var blockOffsetY: Double by parameters - - /** - * Source scale, 0.0 is a 1:1 mapping, 1.0 fits entire source image in block - */ - @DoubleParameter("source scale", 0.0, 1.0, order = 4) - var sourceScale: Double by parameters - - @DoubleParameter("source x-offset", -0.5, 0.5, order = 5) - var sourceOffsetX: Double by parameters - - @DoubleParameter("source y-offset", -.5, .5, order = 6) - var sourceOffsetY: Double by parameters - - @BooleanParameter("bicubic filtering") - var bicubicFiltering: Boolean by parameters - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (bicubicFiltering && source.isNotEmpty()) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - } - super.apply(source, target, clip) - } - - init { - blockWidth = 0.25 - blockHeight = 0.25 - blockOffsetX = 0.0 - blockOffsetY = 0.0 - sourceOffsetX = 0.0 - sourceOffsetY = 0.0 - sourceScale = 0.0 - bicubicFiltering = true - } -} diff --git a/orx-fx/src/commonMain/kotlin/distort/DirectionalDisplace.kt b/orx-fx/src/commonMain/kotlin/distort/DirectionalDisplace.kt deleted file mode 100644 index cba6e9b8..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/DirectionalDisplace.kt +++ /dev/null @@ -1,55 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.Filter2to1 -import org.openrndr.extra.fx.fx_directional_displace -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -/** - * Directional displace filter. Takes source image and direction buffer inputs - */ -@Description("Directional displace") -class DirectionalDisplace : Filter2to1(mppFilterShader(fx_directional_displace, "directional-displace")) { - - /** - * The distance of the sampled pixel. The default is 1.0 - */ - @DoubleParameter("distance", 1.0, 1000.0) - var distance: Double by parameters - - /** - * Post-displace gain, default is 1.0 - */ - @DoubleParameter("gain", 0.0, 4.0) - var gain: Double by parameters - - /** - * Should filter use directions perpendicular to those in the direction buffer? default is false - */ - @BooleanParameter("perpendicular") - var perpendicular: Boolean by parameters - - /** - * Wrap around left and right edges - */ - @BooleanParameter("wrapX") - var wrapX: Boolean by parameters - - /** - * Wrap around top and bottom edges - */ - @BooleanParameter("wrapY") - var wrapY: Boolean by parameters - - init { - distance = 1.0 - gain = 1.0 - perpendicular = false - wrapX = false - wrapY = false - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/distort/DisplaceBlend.kt b/orx-fx/src/commonMain/kotlin/distort/DisplaceBlend.kt deleted file mode 100644 index fed7e1e4..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/DisplaceBlend.kt +++ /dev/null @@ -1,62 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_displace_blend -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector3 -import org.openrndr.shape.Rectangle - -@Description("Displace blend") -class DisplaceBlend : Filter2to1(mppFilterShader(fx_displace_blend, "displace-blend")) { - var seed: Vector3 by parameters - - @DoubleParameter("offset", -1.0, 1.0) - var offset: Double by parameters - - @DoubleParameter("gain", 0.0, 4.0) - var gain: Double by parameters - - @DoubleParameter("feather", 1.0, 100.0) - var feather: Double by parameters - - @DoubleParameter("rotation", -180.0, 180.0) - var rotation: Double by parameters - - @DoubleParameter("source opacity", 0.0, 1.0) - var sourceOpacity: Double by parameters - - @DoubleParameter("target opacity", 0.0, 1.0) - var targetOpacity: Double by parameters - - init { - gain = 0.1 - offset = 0.5 - rotation = 0.0 - feather = 1.0 - sourceOpacity = 1.0 - targetOpacity = 1.0 - } - - var bicubicFiltering = true - private var intermediate: ColorBuffer? = null - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - if (source.size >= 2) { - if (target[0] === source[0] || target[0] === source[1]) { - if (intermediate == null) { - intermediate = colorBuffer(target[0].width, target[0].height, type = target[0].type, format = target[0].format) - } - } - if (bicubicFiltering && source.isNotEmpty()) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - } - super.apply(source, arrayOf(intermediate ?: target[0]), clip) - intermediate?.copyTo(target[0]) - } - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/distort/Fisheye.kt b/orx-fx/src/commonMain/kotlin/distort/Fisheye.kt deleted file mode 100644 index 3c50a3f4..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/Fisheye.kt +++ /dev/null @@ -1,41 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_fisheye -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.shape.Rectangle - -@Description("Fisheye") -class Fisheye : Filter1to1(mppFilterShader(fx_fisheye, "fisheye")) { - @DoubleParameter("strength", -1.0, 1.0, order = 0) - var strength: Double by parameters - - @DoubleParameter("scale", 0.0, 2.0, order = 0) - var scale: Double by parameters - - @DoubleParameter("feather", 0.0, 100.0, order = 1) - var feather: Double by parameters - - @DoubleParameter("rotation", -180.0, 180.0, order = 1) - var rotation: Double by parameters - - init { - strength = 0.1 - feather = 1.0 - scale = 1.0 - rotation = 0.0 - } - - var bicubicFiltering = true - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (bicubicFiltering && source.isNotEmpty()) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - } - super.apply(source, target, clip) - } -} diff --git a/orx-fx/src/commonMain/kotlin/distort/FluidDistort.kt b/orx-fx/src/commonMain/kotlin/distort/FluidDistort.kt deleted file mode 100644 index e4c82980..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/FluidDistort.kt +++ /dev/null @@ -1,66 +0,0 @@ -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_fluid_distort -import org.openrndr.extra.fx.fx_uvmap -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.shape.Rectangle -import kotlin.math.cos - -private class UVMap: Filter( mppFilterShader(fx_uvmap, "uvmap")) - -private class FluidDistortFilter : Filter(mppFilterShader(fx_fluid_distort, "fluid-distort")) { - var blend : Double by parameters - var random: Double by parameters - init { - blend = 0.0 - random = 0.0 - } -} - -class FluidDistort : Filter1to1(null) { - var blend: Double = 1.0 - - var outputUV = false - - private val distort = FluidDistortFilter() - private val uvmap = UVMap() - - private var buffer0: ColorBuffer? = null - private var buffer1: ColorBuffer? = null - private var index = 0 - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - distort.blend = blend - distort.random = cos(index*0.5)*0.5+0.5 - - buffer0?.let { - if (!it.isEquivalentTo(target[0])) { - it.destroy() - } - } - if (buffer0 == null) { - buffer0 = target[0].createEquivalent() - } - - buffer1?.let { - if (!it.isEquivalentTo(target[0])) { - it.destroy() - } - } - if (buffer1 == null) { - buffer1 = target[0].createEquivalent() - } - val buffers = arrayOf(buffer0!!, buffer1!!) - distort.apply(buffers[index%2], buffers[(index+1)%2], clip) - - if (!outputUV) { - uvmap.apply(arrayOf(buffers[(index + 1) % 2], source[0]), target[0], clip) - } else { - buffers[(index+1)%2]. copyTo(target[0]) - } - index++ - blend = 0.0 - } - -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/distort/Lenses.kt b/orx-fx/src/commonMain/kotlin/distort/Lenses.kt deleted file mode 100644 index 68be3d86..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/Lenses.kt +++ /dev/null @@ -1,50 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_lenses -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter -import org.openrndr.shape.Rectangle - -@Description("Lenses") -class Lenses : Filter1to1(mppFilterShader(fx_lenses, "lenses")) { - @IntParameter("rows", 1, 64, order = 0) - var rows: Int by parameters - - @IntParameter("columns", 1, 64, order = 1) - var columns: Int by parameters - - @DoubleParameter("scale", 0.5, 1.5, order = 2) - var scale: Double by parameters - - @DoubleParameter("rotation", -180.0, 180.0, order = 3) - var rotation: Double by parameters - - @DoubleParameter("distort", -1.0, 1.0, order = 4) - var distort: Double by parameters - - @BooleanParameter("bicubic filtering") - var bicubicFiltering: Boolean by parameters - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (bicubicFiltering && source.isNotEmpty()) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - } - super.apply(source, target, clip) - } - - init { - rows = 6 - columns = 8 - distort = 0.0 - scale = 1.0 - rotation = 0.0 - bicubicFiltering = true - } -} diff --git a/orx-fx/src/commonMain/kotlin/distort/PerspectivePlane.kt b/orx-fx/src/commonMain/kotlin/distort/PerspectivePlane.kt deleted file mode 100644 index 387f5c37..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/PerspectivePlane.kt +++ /dev/null @@ -1,61 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_perspective_plane -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector3 -import org.openrndr.math.transforms.transform -import org.openrndr.shape.Rectangle - -@Description("Perspective plane") -class PerspectivePlane : Filter1to1(mppFilterShader(fx_perspective_plane, "perspective-plane")) { - // @DoubleParameter("camera x", -1.0, 1.0, order = 0) - var cameraX: Double = 0.0 - // @DoubleParameter("camera y", -1.0, 1.0, order = 1) - var cameraY: Double = 0.0 - // @DoubleParameter("camera z", -1.0, 1.0, order = 2) - var cameraZ: Double = 1.0 - - - @DoubleParameter("plane x", -1.0, 1.0, order = 3) - var planeX: Double = 0.0 - @DoubleParameter("plane y", -1.0, 1.0, order = 4) - var planeY: Double = 0.0 - @DoubleParameter("plane z", -1.0, 1.0, order = 5) - var planeZ: Double = 0.5 - - @DoubleParameter("plane yaw", -180.0, 180.0, order = 6) - var planeYaw: Double = 0.0 - @DoubleParameter("plane pitch", -180.0, 180.0, order = 7) - var planePitch: Double = 0.0 - @DoubleParameter("plane roll", -180.0, 180.0, order = 8) - var planeRoll: Double = 0.0 - - - @BooleanParameter("tile input") - var tile: Boolean by parameters - - init { - tile = false - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - source[0].wrapU = WrapMode.REPEAT - source[0].wrapV = WrapMode.REPEAT - parameters["cameraPosition"] = Vector3(cameraX, cameraY, cameraZ) - parameters["planePosition"] = Vector3(planeX, planeY, planeZ) - parameters["planeMatrix"] = transform { - rotate(Vector3.UNIT_X, planePitch) - rotate(Vector3.UNIT_Y, planeYaw) - rotate(Vector3.UNIT_Z, planeRoll) - } - super.apply(source, target, clip) - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/distort/Perturb.kt b/orx-fx/src/commonMain/kotlin/distort/Perturb.kt deleted file mode 100644 index affd06ad..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/Perturb.kt +++ /dev/null @@ -1,82 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_perturb -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.* -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.shape.Rectangle - -@Description("Perturb") -class Perturb : Filter1to1(mppFilterShader(fx_perturb, "perturb")) { - var seed: Vector3 by parameters - /** - * base noise scale, default is Vector3(1.0, 1.0, 1.0) - */ - @DoubleParameter("scale", 0.01, 8.0, order = 0) - var scale: Double by parameters - - @DoubleParameter("phase", -2.0, 2.0, order = 1) - var phase: Double by parameters - - @DoubleParameter("radius", 0.0, 2.0, order = 1) - var radius: Double by parameters - - /** - * lacunarity is the amount by which scale is modulated per octave, default is Vector3(2.0, 2.0, 2.0) - */ - @DoubleParameter("lacunarity", 0.0, 1.0, order = 2) - var lacunarity: Double by parameters - - @DoubleParameter("gain", 0.0, 1.0, order = 3) - var gain: Double by parameters - - @DoubleParameter("decay", 0.0, 1.0, order = 4) - var decay: Double by parameters - - /** - * the number of octaves of noise to generate, default is 4 - */ - @IntParameter("octaves", 1, 10, order = 5) - var octaves: Int by parameters - - @IntParameter("x segments", 0, 256, order = 6) - var xSegments: Int by parameters - - @IntParameter("y segments", 0, 256, order = 7) - var ySegments: Int by parameters - - @BooleanParameter("output UV", order = 8) - var outputUV: Boolean by parameters - - @Vector2Parameter("offset", -1.0, 1.0, order = 9) - var offset: Vector2 by parameters - - - init { - seed = Vector3.ZERO - scale = 1.0 - lacunarity = 2.0 - gain = 0.5 - decay = 0.5 - octaves = 4 - phase = 0.0 - xSegments = 0 - ySegments = 0 - outputUV = false - offset = Vector2.ZERO - radius = 1.0 - - } - var bicubicFiltering = true - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (bicubicFiltering && source.isNotEmpty()) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - } - super.apply(source, target, clip) - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/distort/PolarToRectangular.kt b/orx-fx/src/commonMain/kotlin/distort/PolarToRectangular.kt deleted file mode 100644 index e520132e..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/PolarToRectangular.kt +++ /dev/null @@ -1,33 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.MagnifyingFilter -import org.openrndr.draw.MinifyingFilter -import org.openrndr.extra.fx.fx_polar_to_rectangular -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.shape.Rectangle - -@Description("Polar to rectangular") -class PolarToRectangular : Filter1to1(mppFilterShader(fx_polar_to_rectangular, "polar-to-rectangular")) { - @BooleanParameter("log polar") - var logPolar:Boolean by parameters - - init { - logPolar = true - } - - - var bicubicFiltering = true - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (bicubicFiltering && source.isNotEmpty()) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - } - super.apply(source, target, clip) - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/distort/RectangularToPolar.kt b/orx-fx/src/commonMain/kotlin/distort/RectangularToPolar.kt deleted file mode 100644 index a5f71ed6..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/RectangularToPolar.kt +++ /dev/null @@ -1,34 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.MagnifyingFilter -import org.openrndr.draw.MinifyingFilter -import org.openrndr.extra.fx.fx_rectangular_to_polar -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.shape.Rectangle - -@Description("Rectangular to polar") -class RectangularToPolar : Filter1to1(mppFilterShader(fx_rectangular_to_polar, "rectangular-to-polar")) { - - @BooleanParameter("log polar") - var logPolar:Boolean by parameters - - init { - logPolar = true - } - - - var bicubicFiltering = true - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (bicubicFiltering && source.isNotEmpty()) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - } - super.apply(source, target, clip) - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/distort/StackRepeat.kt b/orx-fx/src/commonMain/kotlin/distort/StackRepeat.kt deleted file mode 100644 index 98d2e464..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/StackRepeat.kt +++ /dev/null @@ -1,54 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_stack_repeat -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter -import org.openrndr.shape.Rectangle - -@Description("Stack repeat") -class StackRepeat : Filter1to1(mppFilterShader(fx_stack_repeat, "stack-repeat")) { - @DoubleParameter("zoom", -1.0, 1.0, order = 0) - var zoom: Double by parameters - - @DoubleParameter("x-origin", -1.0, 1.0, order = 1) - var xOrigin: Double by parameters - - @DoubleParameter("y-origin", -1.0, 1.0, order = 2) - var yOrigin: Double by parameters - - @DoubleParameter("x-offset", -1.0, 1.0, order = 3) - var xOffset: Double by parameters - - @DoubleParameter("y-offset", -1.0, 1.0, order = 4) - var yOffset: Double by parameters - - @DoubleParameter("rotation", -180.0, 180.0, order = 5) - var rotation: Double by parameters - - @IntParameter("repeats", 0, 16, order = 6) - var repeats: Int by parameters - - init { - zoom = 0.0 - repeats = 2 - xOffset = 0.0 - yOffset = 0.0 - xOrigin = 0.0 - yOrigin = 0.0 - rotation = 0.0 - } - - var bicubicFiltering = true - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (bicubicFiltering && source.isNotEmpty()) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - } - super.apply(source, target, clip) - } -} diff --git a/orx-fx/src/commonMain/kotlin/distort/StretchWaves.kt b/orx-fx/src/commonMain/kotlin/distort/StretchWaves.kt deleted file mode 100644 index 12ce2976..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/StretchWaves.kt +++ /dev/null @@ -1,45 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_stretch_waves -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.shape.Rectangle - -@Description("Stretch waves") -class StretchWaves : Filter1to1(mppFilterShader(fx_stretch_waves, "stretch-waves")) { - @DoubleParameter("distortion", -0.0,1.0, 1) - var distortion: Double by parameters - - @DoubleParameter("rotation", -180.0, 180.0) - var rotation: Double by parameters - - @DoubleParameter("phase", -1.0, 1.0) - var phase: Double by parameters - - @DoubleParameter("frequency", 0.0, 100.0) - var frequency: Double by parameters - - @DoubleParameter("feather", 0.0, 100.0, order = 1) - var feather: Double by parameters - - init { - distortion = 0.0 - rotation = 0.0 - phase = 0.0 - frequency = 10.0 - feather = 1.0 - } - - var bicubicFiltering = true - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (bicubicFiltering && source.isNotEmpty()) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - } - super.apply(source, target, clip) - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/distort/TapeNoise.kt b/orx-fx/src/commonMain/kotlin/distort/TapeNoise.kt deleted file mode 100644 index c06d6519..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/TapeNoise.kt +++ /dev/null @@ -1,57 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_tape_noise -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Tape noise") -class TapeNoise : Filter1to1(mppFilterShader(fx_tape_noise, "tape-noise")) { - var time: Double by parameters - - @DoubleParameter("gain", 0.0, 1.0) - var gain: Double by parameters - - @DoubleParameter("noise low", 0.0, 1.0) - var noiseLow: Double by parameters - - @DoubleParameter("noise high", 0.0, 1.0) - var noiseHigh: Double by parameters - - @DoubleParameter("gap frequency", 0.0, 2.0) - var gapFrequency: Double by parameters - - @DoubleParameter("gap low", -1.0, 1.0) - var gapLow: Double by parameters - @DoubleParameter("gap high", -1.0, 1.0) - var gapHigh: Double by parameters - - @DoubleParameter("deform amplitude", 0.0, 1.0) - var deformAmplitude: Double by parameters - - @DoubleParameter("deform frequency", 0.0, 1.0) - var deformFrequency: Double by parameters - - @ColorParameter("tint") - var tint: ColorRGBa by parameters - - @BooleanParameter("monochrome") - var monochrome: Boolean by parameters - - init { - gain = 0.5 - noiseLow = 0.5 - noiseHigh = 0.8 - tint = ColorRGBa.WHITE - monochrome = false - gapFrequency = 10.0 - gapLow = -1.0 - gapHigh = -0.99 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/distort/Tiles.kt b/orx-fx/src/commonMain/kotlin/distort/Tiles.kt deleted file mode 100644 index 6cb43207..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/Tiles.kt +++ /dev/null @@ -1,38 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_tiles -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter -import org.openrndr.shape.Rectangle - -@Description("Tiles") -class Tiles : Filter1to1(mppFilterShader(fx_tiles, "tiles")) { - @DoubleParameter("rotation", -180.0, 180.0, order = 2) - var rotation: Double by parameters - - @IntParameter("x segments", 0, 256, order = 0) - var xSegments: Int by parameters - - @IntParameter("y segments", 0, 256, order = 0) - var ySegments: Int by parameters - - init { - rotation = 0.0 - xSegments = 32 - ySegments = 32 - } - - var bicubicFiltering = false - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (bicubicFiltering && source.isNotEmpty()) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - } - super.apply(source, target, clip) - } -} diff --git a/orx-fx/src/commonMain/kotlin/distort/VideoGlitch.kt b/orx-fx/src/commonMain/kotlin/distort/VideoGlitch.kt deleted file mode 100644 index 1a8f2404..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/VideoGlitch.kt +++ /dev/null @@ -1,49 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_video_glitch -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Video glitch") -class VideoGlitch : Filter1to1(mppFilterShader(fx_video_glitch, "video-glitch")) { - var time: Double by parameters - - @DoubleParameter("amplitude", 0.0, 10.0) - var amplitude: Double by parameters - - @DoubleParameter("border height", 0.0, 0.5) - var borderHeight: Double by parameters - - @DoubleParameter("vertical frequency", 0.0, 10.0) - var vfreq: Double by parameters - - @DoubleParameter("horizontal frequency", 0.0, 80.0) - var hfreq: Double by parameters - - @DoubleParameter("p frequency", 0.0, 10.0) - var pfreq: Double by parameters - - @DoubleParameter("p offset", -1.0, 1.0) - var poffset: Double by parameters - - @DoubleParameter("scroll offset 0", 0.0, 1.0) - var scrollOffset0: Double by parameters - - @DoubleParameter("scroll offset 1", 0.0, 1.0) - var scrollOffset1: Double by parameters - - init { - amplitude = 1.0 - vfreq = 4.0 - pfreq = 10.0 - hfreq = 80.0 - poffset = 0.0 - scrollOffset0 = 0.0 - scrollOffset1 = 0.0 - borderHeight = 0.05 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/distort/Wave.kt b/orx-fx/src/commonMain/kotlin/distort/Wave.kt deleted file mode 100644 index 82c84f7e..00000000 --- a/orx-fx/src/commonMain/kotlin/distort/Wave.kt +++ /dev/null @@ -1,74 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.distort - -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_horizontal_wave -import org.openrndr.extra.fx.fx_vertical_wave -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter -import org.openrndr.shape.Rectangle - -@Description("Horizontal wave") -class HorizontalWave : Filter1to1(mppFilterShader(fx_horizontal_wave, "horizontal-wave")) { - @DoubleParameter("frequency", 0.0, 64.0, order = 1) - var frequency: Double by parameters - - @DoubleParameter("amplitude", 0.0, 1.0, order = 0) - var amplitude: Double by parameters - - @DoubleParameter("phase", -0.5, 0.5, order = 2) - var phase: Double by parameters - - @IntParameter("segments", 0, 256, order = 3) - var segments: Int by parameters - - init { - frequency = 1.0 - amplitude = 0.1 - phase = 0.0 - segments = 0 - } - - var bicubicFiltering = true - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (bicubicFiltering && source.isNotEmpty()) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - } - super.apply(source, target, clip) - } -} - -@Description("Vertical wave") -class VerticalWave : Filter1to1(mppFilterShader(fx_vertical_wave, "vertical-wave")) { - @DoubleParameter("frequency", 0.0, 64.0, order = 1) - var frequency: Double by parameters - - @DoubleParameter("amplitude", 0.0, 1.0, order = 0) - var amplitude: Double by parameters - - @DoubleParameter("phase", -0.5, 0.5, order = 2) - var phase: Double by parameters - - @IntParameter("segments", 0, 256, order = 3) - var segments: Int by parameters - - init { - frequency = 1.0 - amplitude = 0.1 - phase = 0.0 - segments = 0 - } - var bicubicFiltering = true - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (bicubicFiltering && source.isNotEmpty()) { - source[0].generateMipmaps() - source[0].filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - } - super.apply(source, target, clip) - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/dither/ADither.kt b/orx-fx/src/commonMain/kotlin/dither/ADither.kt deleted file mode 100644 index dc3b274a..00000000 --- a/orx-fx/src/commonMain/kotlin/dither/ADither.kt +++ /dev/null @@ -1,23 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.dither - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_a_dither -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.IntParameter - -@Description("ADither") -class ADither: Filter1to1(mppFilterShader(fx_a_dither, "a-dither")) { - @IntParameter("pattern index", 0, 3) - var pattern: Int by parameters - - @IntParameter("levels", 1, 64) - var levels: Int by parameters - - init { - levels = 4 - pattern = 3 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/dither/CMYKHalftone.kt b/orx-fx/src/commonMain/kotlin/dither/CMYKHalftone.kt deleted file mode 100644 index f2278869..00000000 --- a/orx-fx/src/commonMain/kotlin/dither/CMYKHalftone.kt +++ /dev/null @@ -1,80 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.dither - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.fx.fx_cmyk_halftone -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.shaderphrases.sdf.sdCirclePhrase - -@Description("CMYK Halftone") -class CMYKHalftone( - domainWarpFunction: String = "vec2 domainWarp(vec2 p) { return p; }", - elementFunction: String = """ - $sdCirclePhrase - float element(in vec2 p, float v) { - return sdCircle(p, v * dotSize); - }""".trimIndent() -) : Filter1to1( - filterShaderFromCode( - fx_cmyk_halftone.split("#pragma INSERT_PHRASES").let { - listOf(it[0], elementFunction, domainWarpFunction, it[1]) - }.joinToString("\n"), - "cmyk-halftone" - ) -) { - @DoubleParameter("scale", 1.0, 30.0, precision = 4) - var scale: Double by parameters - - @DoubleParameter("dotSize", 1.0, 3.0, precision = 4) - var dotSize: Double by parameters - - @DoubleParameter("rotation", -180.0, 180.0) - var rotation: Double by parameters - - @DoubleParameter("cyan rotation", -180.0, 180.0, precision = 4) - var cyanRotation: Double by parameters - - @DoubleParameter("magenta rotation", -180.0, 180.0, precision = 4) - var magentaRotation: Double by parameters - - @DoubleParameter("yellow rotation", -180.0, 180.0, precision = 4) - var yellowRotation: Double by parameters - - @DoubleParameter("black rotation", -180.0, 180.0, precision = 4) - var blackRotation: Double by parameters - - @ColorParameter("cyan color") - var cyanColor: ColorRGBa by parameters - - @ColorParameter("magenta color") - var magentaColor: ColorRGBa by parameters - - @ColorParameter("yellow color") - var yellowColor: ColorRGBa by parameters - - @ColorParameter("black color") - var blackColor: ColorRGBa by parameters - - var phase: Double by parameters - - init { - blackRotation = 45.0 - magentaRotation = 75.0 - cyanRotation = 15.0 - yellowRotation = 0.0 - cyanColor = ColorRGBa.CYAN - magentaColor = ColorRGBa.MAGENTA - yellowColor = ColorRGBa.YELLOW - blackColor = ColorRGBa.BLACK - - scale = 3.0 - rotation = 0.0 - dotSize = 0.9 - phase = 0.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/dither/Crosshatch.kt b/orx-fx/src/commonMain/kotlin/dither/Crosshatch.kt deleted file mode 100644 index 74e858d6..00000000 --- a/orx-fx/src/commonMain/kotlin/dither/Crosshatch.kt +++ /dev/null @@ -1,31 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.dither - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_crosshatch -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Crosshatch") -class Crosshatch : Filter1to1(mppFilterShader(fx_crosshatch, "crosshatch")) { - @DoubleParameter("threshold 1", 0.0, 1.0) - var t1: Double by parameters - - @DoubleParameter("threshold 2", 0.0, 1.0) - var t2: Double by parameters - - @DoubleParameter("threshold 3", 0.0, 1.0) - var t3: Double by parameters - - @DoubleParameter("threshold 4", 0.0, 1.0) - var t4: Double by parameters - - init { - t1 = 1.0 - t2 = 0.75 - t3 = 0.5 - t4 = 0.3 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/dither/LumaHalftone.kt b/orx-fx/src/commonMain/kotlin/dither/LumaHalftone.kt deleted file mode 100644 index 865dc7bb..00000000 --- a/orx-fx/src/commonMain/kotlin/dither/LumaHalftone.kt +++ /dev/null @@ -1,55 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.dither - -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.fx.fx_luma_halftone -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - - -@Description("Luma Halftone") -class LumaHalftone: Filter1to1(filterShaderFromCode(fx_luma_halftone, "luma-halftone")) { - @DoubleParameter("scale", 1.0, 30.0, precision = 4) - var scale: Double by parameters - - @DoubleParameter("threshold", 0.0, 1.0, precision = 4) - var threshold: Double by parameters - - @DoubleParameter("rotation", -180.0, 180.0) - var rotation: Double by parameters - - @DoubleParameter("freq0", 1.0, 400.0) - var freq0: Double by parameters - - @DoubleParameter("freq1", 1.0, 400.0) - var freq1: Double by parameters - - @DoubleParameter("gain1", -2.0, 2.0) - var gain1: Double by parameters - - @DoubleParameter("phase0", -1.0, 1.0) - var phase0: Double by parameters - - @DoubleParameter("phase1", -1.0, 1.0) - var phase1: Double by parameters - - - @BooleanParameter("invert") - var invert: Boolean by parameters - - - init { - scale = 3.0 - rotation = 0.0 - threshold = 0.5 - freq1 = 20.0 - freq0 = 10.0 - gain1 = 0.1 - phase0 = 0.0 - phase1 = 0.0 - invert = true - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/edges/CannyEdgeDetector.kt b/orx-fx/src/commonMain/kotlin/edges/CannyEdgeDetector.kt deleted file mode 100644 index 01dc766c..00000000 --- a/orx-fx/src/commonMain/kotlin/edges/CannyEdgeDetector.kt +++ /dev/null @@ -1,54 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.edges - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.fx.fx_canny_edge_detector -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Canny Edge Detector") -class CannyEdgeDetector : Filter1to1( - filterShaderFromCode(fx_canny_edge_detector, "canny-edge-detector") -) { - - @DoubleParameter("threshold 0", 0.0, 100.0, order = 0) - var threshold0: Double by parameters - - @DoubleParameter("threshold 1", 0.0, 100.0, order = 1) - var threshold1: Double by parameters - - @DoubleParameter("thickness", 0.0, 10.0, order = 2) - var thickness: Double by parameters - - @ColorParameter("foreground color", order = 3) - var foregroundColor: ColorRGBa by parameters - - @DoubleParameter("foreground opacity", 0.0, 1.0, order = 4) - var foregroundOpacity: Double by parameters - - @ColorParameter("background color", order = 5) - var backgroundColor: ColorRGBa by parameters - - @DoubleParameter("background opacity", 0.0, 1.0, order = 6) - var backgroundOpacity: Double by parameters - - - @DoubleParameter("fade", 0.0, 1.0, order = 7) - var fade: Double by parameters - - init { - threshold0 = 2.0 - threshold1 = 0.0 - thickness = 1.0 - foregroundColor = ColorRGBa.WHITE - backgroundColor = ColorRGBa.BLACK - backgroundOpacity = 1.0 - foregroundOpacity = 1.0 - fade = 1.0 - } - -} diff --git a/orx-fx/src/commonMain/kotlin/edges/Contour.kt b/orx-fx/src/commonMain/kotlin/edges/Contour.kt deleted file mode 100644 index 75dece26..00000000 --- a/orx-fx/src/commonMain/kotlin/edges/Contour.kt +++ /dev/null @@ -1,51 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.edges - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_contour -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.* - -@Description("Contour") -class Contour : Filter1to1(mppFilterShader(fx_contour, "contour")) { - @DoubleParameter("levels", 0.0, 16.0) - var levels: Double by parameters - - @DoubleParameter("contour width", 0.0, 4.0) - var contourWidth: Double by parameters - - @DoubleParameter("contour opacity", 0.0, 1.0) - var contourOpacity: Double by parameters - - @DoubleParameter("background opacity", 0.0, 1.0) - var backgroundOpacity: Double by parameters - - @DoubleParameter("bias", -1.0, 1.0) - var bias: Double by parameters - - @ColorParameter("contour color") - var contourColor: ColorRGBa by parameters - - @IntParameter("window", 0, 10) - var window: Int by parameters - - @BooleanParameter("output bands", order = 100) - var outputBands: Boolean by parameters - - @DoubleParameter("fade", 0.0, 1.0, order = 200) - var fade: Double by parameters - - init { - levels = 6.0 - contourWidth = 0.4 - contourColor = ColorRGBa.BLACK - backgroundOpacity = 1.0 - contourOpacity = 1.0 - window = 1 - bias = 0.0 - outputBands = false - fade = 1.0 - } -} diff --git a/orx-fx/src/commonMain/kotlin/edges/EdgesWork.kt b/orx-fx/src/commonMain/kotlin/edges/EdgesWork.kt deleted file mode 100644 index 3d0d4535..00000000 --- a/orx-fx/src/commonMain/kotlin/edges/EdgesWork.kt +++ /dev/null @@ -1,63 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.edges - -import org.openrndr.draw.* -import org.openrndr.extra.fx.ColorBufferDescription -import org.openrndr.extra.fx.fx_edges_work_1 -import org.openrndr.extra.fx.fx_edges_work_2 -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.IntParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -internal class EdgesWork1 : Filter(mppFilterShader(fx_edges_work_1, "edges-work-1")) { - var delta: Vector2 by parameters - - init { - delta = Vector2.ZERO - } -} - -@Description("Edges Work") -open class EdgesWork : Filter1to1(mppFilterShader(fx_edges_work_2, "edges-work-2")) { - /** - * radius, default value is 1.0 - */ - @IntParameter("radius", 1, 400) - var radius: Int by parameters - - private var delta: Vector2 by parameters - - private val work1 = EdgesWork1() - - private var intermediateCache = mutableMapOf() - - init { - radius = 1 - delta = Vector2.ZERO - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - val intermediateDescription = ColorBufferDescription( - target[0].width, - target[0].height, - target[0].contentScale, - target[0].format, - target[0].type - ) - val intermediate = intermediateCache.getOrPut(intermediateDescription) { - colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type) - } - - intermediate.let { - work1.delta = Vector2(radius / it.effectiveWidth.toDouble(), 0.0) - work1.apply(source, arrayOf(it), clip) - - parameters["delta"] = Vector2(0.0, radius / it.effectiveHeight.toDouble()) - super.apply(arrayOf(it), target, clip) - } - } -} - diff --git a/orx-fx/src/commonMain/kotlin/edges/LumaLaplacian.kt b/orx-fx/src/commonMain/kotlin/edges/LumaLaplacian.kt deleted file mode 100644 index ce8a67c9..00000000 --- a/orx-fx/src/commonMain/kotlin/edges/LumaLaplacian.kt +++ /dev/null @@ -1,33 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.edges - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_luma_laplacian -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Luma Sobel") -class LumaLaplacian : Filter1to1(mppFilterShader(fx_luma_laplacian, "luma-laplacian")) { - @ColorParameter("background color") - var backgroundColor: ColorRGBa by parameters - - @ColorParameter("edge color") - var edgeColor: ColorRGBa by parameters - - @DoubleParameter("background opacity", 0.0, 1.0) - var backgroundOpacity: Double by parameters - - @DoubleParameter("edge opacity", 0.0, 1.0) - var edgeOpacity: Double by parameters - - init { - backgroundColor = ColorRGBa.BLACK - edgeColor = ColorRGBa.WHITE - edgeOpacity = 1.0 - backgroundOpacity = 1.0 - } -} diff --git a/orx-fx/src/commonMain/kotlin/edges/LumaSobel.kt b/orx-fx/src/commonMain/kotlin/edges/LumaSobel.kt deleted file mode 100644 index a871c4e9..00000000 --- a/orx-fx/src/commonMain/kotlin/edges/LumaSobel.kt +++ /dev/null @@ -1,33 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.edges - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_luma_sobel -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Luma Sobel") -class LumaSobel : Filter1to1(mppFilterShader(fx_luma_sobel, "luma-sobel")) { - @ColorParameter("background color") - var backgroundColor: ColorRGBa by parameters - - @ColorParameter("edge color") - var edgeColor: ColorRGBa by parameters - - @DoubleParameter("background opacity", 0.0, 1.0) - var backgroundOpacity: Double by parameters - - @DoubleParameter("edge opacity", 0.0, 1.0) - var edgeOpacity: Double by parameters - - init { - backgroundColor = ColorRGBa.BLACK - edgeColor = ColorRGBa.WHITE - edgeOpacity = 1.0 - backgroundOpacity = 1.0 - } -} diff --git a/orx-fx/src/commonMain/kotlin/grain/FilmGrain.kt b/orx-fx/src/commonMain/kotlin/grain/FilmGrain.kt deleted file mode 100644 index 8f39776d..00000000 --- a/orx-fx/src/commonMain/kotlin/grain/FilmGrain.kt +++ /dev/null @@ -1,45 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.grain - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_film_grain -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -/** - * Film grain filter - */ -@Description("film grain") -class FilmGrain : Filter1to1(mppFilterShader(fx_film_grain, "film-grain")) { - @BooleanParameter("use color") - var useColor: Boolean by parameters - - var time: Double by parameters; - - @DoubleParameter("grain lift ratio", 0.0, 1.0) - var grainLiftRatio: Double by parameters - - @DoubleParameter("grain strength", 0.0, 1.0) - var grainStrength: Double by parameters - - @DoubleParameter("grain rate", 0.0, 1.0) - var grainRate: Double by parameters - - @DoubleParameter("grain pitch", 0.0, 1.0) - var grainPitch: Double by parameters - - @DoubleParameter("color level", 0.0, 1.0) - var colorLevel: Double by parameters - - init { - useColor = false - grainLiftRatio = 0.5 - grainStrength = 1.0 - grainRate = 1.0 - grainPitch = 1.0 - colorLevel = 1.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/math/MultiplyU.kt b/orx-fx/src/commonMain/kotlin/math/MultiplyU.kt deleted file mode 100644 index 5064d4f0..00000000 --- a/orx-fx/src/commonMain/kotlin/math/MultiplyU.kt +++ /dev/null @@ -1,19 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.fx.fx_multiply_u -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -/** - * Multiply by u coordinate - */ -@Description("multiply u") -class MultiplyU : Filter1to1(filterShaderFromCode(fx_multiply_u, "multiply-u")) { - @DoubleParameter("multiplication bias", 0.0, 2.0) - var bias: Double by parameters - init { - bias = 0.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/math/MultiplyV.kt b/orx-fx/src/commonMain/kotlin/math/MultiplyV.kt deleted file mode 100644 index 9e24ff09..00000000 --- a/orx-fx/src/commonMain/kotlin/math/MultiplyV.kt +++ /dev/null @@ -1,25 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.fx.fx_multiply_v -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -/** - * Multiply by v coordinate - */ -@Description("multiply v") -class MultiplyV : Filter1to1(filterShaderFromCode(fx_multiply_v, "multiply-v")) { - @DoubleParameter("multiplication bias", 0.0, 2.0) - var bias: Double by parameters - - @BooleanParameter("invert v") - var invertV: Boolean by parameters - - init { - bias = 0.0 - invertV = false - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/math/Square.kt b/orx-fx/src/commonMain/kotlin/math/Square.kt deleted file mode 100644 index 9f0c7c46..00000000 --- a/orx-fx/src/commonMain/kotlin/math/Square.kt +++ /dev/null @@ -1,13 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -import org.openrndr.draw.Filter1to1 -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.fx.fx_square -import org.openrndr.extra.parameters.Description - -/** - * Square input texture values - */ -@Description("square") -class Square : Filter1to1(filterShaderFromCode(fx_square, "square")) { -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/patterns/Checkers.kt b/orx-fx/src/commonMain/kotlin/patterns/Checkers.kt deleted file mode 100644 index 8df4f53d..00000000 --- a/orx-fx/src/commonMain/kotlin/patterns/Checkers.kt +++ /dev/null @@ -1,29 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.patterns - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_checkers -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("Checkers pattern") -class Checkers : Filter1to1(mppFilterShader(fx_checkers, "checkers")) { - var background: ColorRGBa by parameters - var foreground: ColorRGBa by parameters - - @DoubleParameter("size", 0.0, 1.0) - var size: Double by parameters - - @DoubleParameter("opacity", 0.0, 1.0) - var opacity: Double by parameters - - init { - size = 1.0 / 64.0 - opacity = 1.0 - foreground = ColorRGBa.WHITE.shade(0.9) - background = ColorRGBa.WHITE.shade(0.8) - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/shadow/DropShadow.kt b/orx-fx/src/commonMain/kotlin/shadow/DropShadow.kt deleted file mode 100644 index f9288abe..00000000 --- a/orx-fx/src/commonMain/kotlin/shadow/DropShadow.kt +++ /dev/null @@ -1,78 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.shadow - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.fx.fx_dropshadow_blend -import org.openrndr.extra.fx.fx_dropshadow_blur -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -private class Blend : Filter(mppFilterShader(fx_dropshadow_blend, "dropshadow-blend")) { - var shift: Vector2 by parameters -} - -@Description("Drop shadow") -class DropShadow : Filter1to1(mppFilterShader(fx_dropshadow_blur, "dropshadow-blur")) { - @IntParameter("blur window", 1, 25) - var window: Int by parameters - var spread: Double by parameters - @DoubleParameter("gain", 0.0, 4.0) - var gain: Double by parameters - - @DoubleParameter("x shift", -30.0, 30.0) - var xShift: Double = 0.0 - - @DoubleParameter("y shift", -30.0, 30.0) - var yShift: Double = 0.0 - - @ColorParameter("color") - var color: ColorRGBa by parameters - - private var intermediate: ColorBuffer? = null - private var intermediate2: ColorBuffer? = null - private var b = Blend() - - init { - color = ColorRGBa.BLACK - window = 5 - spread = 1.0 - gain = 1.0 - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - intermediate?.let { - if (it.width != target[0].width || it.height != target[0].height) { - intermediate = null - } - } - intermediate2?.let { - if (it.width != target[0].width || it.height != target[0].height) { - intermediate2 = null - } - } - if (intermediate == null) { - intermediate = colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type) - } - if (intermediate2 == null) { - intermediate2 = colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type) - } - - intermediate?.let { - parameters["blurDirection"] = Vector2(1.0, 0.0) - super.apply(source, arrayOf(it), clip) - - parameters["blurDirection"] = Vector2(0.0, 1.0) - super.apply(arrayOf(it), arrayOf(intermediate2!!), clip) - - b.shift = (Vector2(xShift,yShift)) / Vector2(target[0].width * 1.0, target[0].height * 1.0) - b.apply(arrayOf(intermediate2!!, source[0]), target, clip) - } - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/tonemap/ACESTonemap.kt b/orx-fx/src/commonMain/kotlin/tonemap/ACESTonemap.kt deleted file mode 100644 index 2ed0b061..00000000 --- a/orx-fx/src/commonMain/kotlin/tonemap/ACESTonemap.kt +++ /dev/null @@ -1,18 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.tonemap - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_aces_tonemap -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("ACES tonemap") -class ACESTonemap : Filter1to1(mppFilterShader(fx_aces_tonemap, "aces-tonemap")) { - @DoubleParameter("exposure bias", 0.0, 128.0) - var exposureBias:Double by parameters - init { - exposureBias = 1.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/tonemap/ReinhardTonemap.kt b/orx-fx/src/commonMain/kotlin/tonemap/ReinhardTonemap.kt deleted file mode 100644 index d4adb55a..00000000 --- a/orx-fx/src/commonMain/kotlin/tonemap/ReinhardTonemap.kt +++ /dev/null @@ -1,22 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.tonemap - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_reinhard_tonemap -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("ACES tonemap") -class ReinhardTonemap : Filter1to1(mppFilterShader(fx_reinhard_tonemap, "reinhard-tonemap")) { - @DoubleParameter("exposure bias", 0.0, 128.0) - var exposureBias:Double by parameters - - @DoubleParameter("maximum luminance", 0.0, 128.0) - var maxLuminance:Double by parameters - init { - exposureBias = 1.0 - maxLuminance = 1.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/tonemap/Uncharted2Tonemap.kt b/orx-fx/src/commonMain/kotlin/tonemap/Uncharted2Tonemap.kt deleted file mode 100644 index fd175595..00000000 --- a/orx-fx/src/commonMain/kotlin/tonemap/Uncharted2Tonemap.kt +++ /dev/null @@ -1,21 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.fx.tonemap - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_uncharted2_tonemap -import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -/** - * Uncharted 2 tonemap filter - */ -@Description("Uncharted 2 tonemap") -class Uncharted2Tonemap : Filter1to1(mppFilterShader(fx_uncharted2_tonemap, "uncharted2-tonemap")) { - @DoubleParameter("exposure bias", 0.0, 128.0) - var exposureBias:Double by parameters - init { - exposureBias = 2.0 - } -} \ No newline at end of file diff --git a/orx-fx/src/commonMain/kotlin/transform/FlipVertically.kt b/orx-fx/src/commonMain/kotlin/transform/FlipVertically.kt deleted file mode 100644 index 6f7ee767..00000000 --- a/orx-fx/src/commonMain/kotlin/transform/FlipVertically.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.openrndr.extra.fx.transform - -import org.openrndr.draw.Filter1to1 -import org.openrndr.extra.fx.fx_flip_vertically -import org.openrndr.extra.fx.mppFilterShader - -/** - * Vertically flips in the input image - */ -class FlipVertically : Filter1to1(mppFilterShader(fx_flip_vertically, "flip-vertically")) \ No newline at end of file diff --git a/orx-fx/src/jvmDemo/kotlin/DemoApproximateGaussianBlur01.kt b/orx-fx/src/jvmDemo/kotlin/DemoApproximateGaussianBlur01.kt deleted file mode 100644 index 7e8258a2..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoApproximateGaussianBlur01.kt +++ /dev/null @@ -1,39 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.blur.ApproximateGaussianBlur -import org.openrndr.extra.imageFit.imageFit - -/** - * Demonstrates how to use the [ApproximateGaussianBlur] effect to blur - * a `colorBuffer`, in this case, an image loaded from disk. - * - * Notice the use of `createEquivalent()`, which creates a new `colorBuffer` - * with the same size and properties as a source `colorBuffer`. - * - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val image = loadImage("demo-data/images/image-001.png") - val blurred = image.createEquivalent() - val blur = ApproximateGaussianBlur() - - var enableWrap = false - - mouse.buttonDown.listen { - enableWrap = !enableWrap - } - extend { - blur.wrapU = enableWrap - blur.wrapV = enableWrap - blur.sigma = 15.0 - blur.window = 15 - blur.apply(image, blurred) - drawer.imageFit(blurred, drawer.bounds) - } - } -} \ No newline at end of file diff --git a/orx-fx/src/jvmDemo/kotlin/DemoBlend01.kt b/orx-fx/src/jvmDemo/kotlin/DemoBlend01.kt deleted file mode 100644 index c8e49d06..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoBlend01.kt +++ /dev/null @@ -1,28 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.fx.blend.* - -fun main() = application { - program { - val add = Add() - val colorBurn = ColorBurn() - val colorDodge = ColorDodge() - val darken = Darken() - val destIn = DestinationIn() - val destOut = DestinationOut() - val destAtop = DestinationAtop() - val hardLight = HardLight() - val lighten = Lighten() - val multiply = Multiply() - val multiplyContrast = MultiplyContrast() - val normal = Normal() - val overlay = Overlay() - val passthrough = Passthrough() - val screen = Screen() - val sourceIn = SourceIn() - val sourceAtop = SourceAtop() - val sourceOut = SourceOut() - val subtract = Subtract() - val xor = Xor() - application.exit() - } -} diff --git a/orx-fx/src/jvmDemo/kotlin/DemoBlur01.kt b/orx-fx/src/jvmDemo/kotlin/DemoBlur01.kt deleted file mode 100644 index 355812cc..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoBlur01.kt +++ /dev/null @@ -1,136 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.fx.blur.* -import org.openrndr.math.Polar -import kotlin.math.sin - -/** - * Demonstrates 9 different blur effects. - * The program draws two moving circles into a [RenderTarget], - * then applies various blurs drawing them in 3 columns and 3 rows. - * - * Each type of blur has different parameters. - * Not all parameters are demonstrated. - */ -fun main() = application { - program { - // In this buffer we will draw some simple shapes - val dry = renderTarget(width / 3, height / 3) { - colorBuffer() - } - - // The list of effects to demo - val effects = listOf( - BoxBlur(), - ApproximateGaussianBlur(), - HashBlur(), - GaussianBlur(), - GaussianBloom(), - FrameBlur(), - ZoomBlur(), - LaserBlur(), - LineBlur() - ) - - // On this buffer we will draw the dry buffer with an effect applied - val wet = colorBuffer(dry.width, dry.height) - - val font = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 16.0) - - extend { - // Draw two moving circles - drawer.isolatedWithTarget(dry) { - ortho(dry) - - clear(ColorRGBa.BLACK) - - fill = null - stroke = ColorRGBa.PINK - strokeWeight = 20.0 - circle( - bounds.center + - Polar(seconds * 50.0, 100.0).cartesian, - 100.0 + 50.0 * sin(seconds * 2.0) - ) - - fill = ColorRGBa.PINK - stroke = null - circle( - bounds.center + - Polar(seconds * 50.0 + 60, 100.0).cartesian, - 70.0 + 20.0 * sin(seconds * 2.0 + 1) - ) - } - - effects.forEachIndexed { i, blur -> - // Adjust the effect settings. - // All the values could be animated. - when (blur) { - is BoxBlur -> { - blur.window = 30 - } - - is ApproximateGaussianBlur -> { - blur.window = 25 - blur.sigma = 15.0 - } - - is HashBlur -> { - blur.samples = 50 - blur.radius = 5.0 - blur.time = seconds - } - - is GaussianBlur -> { - blur.window = 25 - blur.sigma = 15.0 - } - - is GaussianBloom -> { - blur.window = 5 - blur.sigma = 3.0 - blur.gain = 3.0 - blur.noiseSeed = seconds - } - - is FrameBlur -> { - blur.blend = 0.05 - } - - is ZoomBlur -> { - blur.center = Polar(seconds * 77.0, 0.5) - .cartesian - blur.strength = 0.8 - } - - is LaserBlur -> { - blur.center = Polar(seconds * 77.0, 0.5) - .cartesian - blur.aberration = 0.03 - blur.radius = 0.5 - - } - is LineBlur -> { - blur.blurAngle = seconds * 30.0 - blur.spread = 8.0 - } - } - - // Apply the effect on `dry` writing the result to `wet` - blur.apply(dry.colorBuffer(0), wet) - - // Draw `wet` and write the effect name on top - drawer.isolated { - translate( - (i % 3) * width / 3.0, - (i / 3) * height / 3.0 - ) - image(wet) - fontMap = font - text(blur.javaClass.simpleName, 20.0, 30.0) - } - } - } - } -} diff --git a/orx-fx/src/jvmDemo/kotlin/DemoCannyEdgeDetector01.kt b/orx-fx/src/jvmDemo/kotlin/DemoCannyEdgeDetector01.kt deleted file mode 100644 index 59ffe983..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoCannyEdgeDetector01.kt +++ /dev/null @@ -1,20 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.edges.CannyEdgeDetector - -/** - * Demonstrates the [CannyEdgeDetector] effect applied to a loaded - * color photograph. - */ -fun main() = application { - program { - val image = loadImage("demo-data/images/image-001.png") - val ced = CannyEdgeDetector() - val edges = image.createEquivalent() - extend { - ced.apply(image, edges) - drawer.image(edges) - } - } -} \ No newline at end of file diff --git a/orx-fx/src/jvmDemo/kotlin/DemoColorDuotone01.kt b/orx-fx/src/jvmDemo/kotlin/DemoColorDuotone01.kt deleted file mode 100644 index f2739cab..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoColorDuotone01.kt +++ /dev/null @@ -1,26 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.color.Duotone - -/** - * This demo shows how to use the [Duotone] filter, - * toggling the `labInterpolation` parameter every second on and off. - * - * The `foregroundColor` and `backgroundColor` parameters are - * left to their defaults. - */ -fun main() = application { - program { - - val image = loadImage("demo-data/images/image-001.png") - val filteredImage = image.createEquivalent() - val duotone = Duotone() - - extend { - duotone.labInterpolation = seconds.mod(2.0) < 1.0 - duotone.apply(image, filteredImage) - drawer.image(filteredImage) - } - } -} diff --git a/orx-fx/src/jvmDemo/kotlin/DemoColorDuotoneGradient01.kt b/orx-fx/src/jvmDemo/kotlin/DemoColorDuotoneGradient01.kt deleted file mode 100644 index fd0a2fd5..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoColorDuotoneGradient01.kt +++ /dev/null @@ -1,34 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.color.DuotoneGradient - -/** - * The [DuotoneGradient] effect combines the Duotone effect - * and a linear gradient: two duotone colors are applied on - * one part of the image, and those colors are interpolated - * to two other colors, applied in a different part of the image. - * - * The `rotation` parameter lets us specify in which direction - * the interpolation happens (vertical, horizontal, or something else). - */ -fun main() = application { - program { - val image = loadImage("demo-data/images/image-001.png") - val filteredImage = image.createEquivalent() - val duotone = DuotoneGradient() - duotone.labInterpolation = false - - extend { - duotone.labInterpolation = true - duotone.backgroundColor0 = ColorRGBa.BLACK - duotone.foregroundColor0 = ColorRGBa.RED - duotone.backgroundColor1 = ColorRGBa.BLUE - duotone.foregroundColor1 = ColorRGBa.WHITE - duotone.rotation = seconds * 45.0 - duotone.apply(image, filteredImage) - drawer.image(filteredImage) - } - } -} \ No newline at end of file diff --git a/orx-fx/src/jvmDemo/kotlin/DemoColorPosterize01.kt b/orx-fx/src/jvmDemo/kotlin/DemoColorPosterize01.kt deleted file mode 100644 index 88e6f794..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoColorPosterize01.kt +++ /dev/null @@ -1,22 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.color.Posterize - -/** - * Demonstration of the [Posterize] effect to reduce the number of colors - * present in an image. - */ -fun main() = application { - program { - val image = loadImage("demo-data/images/image-001.png") - val filteredImage = image.createEquivalent() - val posterize = Posterize() - - extend { - posterize.levels = 2 - posterize.apply(image, filteredImage) - drawer.image(filteredImage) - } - } -} diff --git a/orx-fx/src/jvmDemo/kotlin/DemoColormapGrayscale.kt b/orx-fx/src/jvmDemo/kotlin/DemoColormapGrayscale.kt deleted file mode 100644 index 06dda92c..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoColormapGrayscale.kt +++ /dev/null @@ -1,25 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.ColorType -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.colormap.GrayscaleColormap -import kotlin.math.sin - -/** - * The [GrayscaleColormap] uses the red channel of a colorBuffer - * to produce a gray scale image. The `curve` parameter is used as - * an exponent to bias the result up or down. 1.0 produces a linear - * transformation. - */ -fun main() = application { - program { - val colormap = GrayscaleColormap() - val image = loadImage("demo-data/images/image-001.png") - val colormapImage = image.createEquivalent(type = ColorType.FLOAT32) - extend { - colormap.curve = 1.0 + sin(seconds) * .5 - colormap.apply(image, colormapImage) - drawer.image(colormapImage) - } - } -} diff --git a/orx-fx/src/jvmDemo/kotlin/DemoColormapSpectralZucconi.kt b/orx-fx/src/jvmDemo/kotlin/DemoColormapSpectralZucconi.kt deleted file mode 100644 index 61a64c9d..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoColormapSpectralZucconi.kt +++ /dev/null @@ -1,26 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.ColorType -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.colormap.SpectralZucconiColormap -import kotlin.math.sin - -/** - * Demonstrates the [SpectralZucconiColormap], which - * maps values of the RED color channel to the natural light dispersion - * spectrum as described by Alan Zucconi in his - * [Improving the Rainbow](https://www.alanzucconi.com/2017/07/15/improving-the-rainbow/) - * article. - */ -fun main() = application { - program { - val colormap = SpectralZucconiColormap() - val image = loadImage("demo-data/images/image-001.png") - val colormapImage = image.createEquivalent(type = ColorType.FLOAT32) - extend { - colormap.curve = 1.0 + sin(seconds) * .5 - colormap.apply(image, colormapImage) - drawer.image(colormapImage) - } - } -} diff --git a/orx-fx/src/jvmDemo/kotlin/DemoColormapTurbo.kt b/orx-fx/src/jvmDemo/kotlin/DemoColormapTurbo.kt deleted file mode 100644 index fcdb6930..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoColormapTurbo.kt +++ /dev/null @@ -1,25 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.ColorType -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.colormap.TurboColormap -import kotlin.math.sin - -/** - * Demonstrates the use of the [TurboColormap] effect, which - * maps values of the RED color channel to Turbo Colormap according to - * [Turbo, An Improved Rainbow Colormap for Visualization](https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html) - * by Google. - */ -fun main() = application { - program { - val colormap = TurboColormap() - val image = loadImage("demo-data/images/image-001.png") - val colormapImage = image.createEquivalent(type = ColorType.FLOAT32) - extend { - colormap.curve = 1.0 + sin(seconds) * .5 - colormap.apply(image, colormapImage) - drawer.image(colormapImage) - } - } -} diff --git a/orx-fx/src/jvmDemo/kotlin/DemoCompositeFilter01.kt b/orx-fx/src/jvmDemo/kotlin/DemoCompositeFilter01.kt deleted file mode 100644 index 547ea766..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoCompositeFilter01.kt +++ /dev/null @@ -1,85 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorType -import org.openrndr.draw.colorBuffer -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.Post -import org.openrndr.extra.fx.blur.DirectionalBlur -import org.openrndr.extra.fx.composite.then -import org.openrndr.extra.fx.grain.FilmGrain -import org.openrndr.extra.noise.* -import org.openrndr.math.smoothstep -import kotlin.math.cos -import kotlin.math.sin - -/** - * Advanced demonstration of composite filters, created by chaining - * several filters together using the `.then()` operator. - * - * The demo applies a [FilmGrain] effect and a [DirectionalBlur] effect twice - * with different parameters. - * - * The [DirectionalBlur] requires a color buffer to define the displacement - * directions. In this program, the direction color buffer is populated by writing - * into its `shadow` property pixel by pixel. - * - * Notice the use of `frameCount` and `seconds` to animate the effects. - * - * The composite effect is installed as a post-processing effect - * using `extend(Post())`, so anything drawn in following `extend` - * blocks is affected by it. - */ -fun main() = application { - program { - // -- create a color buffer and fill it with random direction vectors - val direction = colorBuffer(width, height, type = ColorType.FLOAT32) - val s = direction.shadow - val n = simplex2D.bipolar().fbm().scaleShiftInput(0.01, 0.0, 0.01, 0.0).withVector2Output() - val ng = simplex2D.unipolar().scaleShiftInput(0.005, 0.0, 0.005, 0.0) - for (y in 0 until height) { - for (x in 0 until width) { - val nv = n(2320, x.toDouble(), y.toDouble()) * smoothstep( - 0.45, - 0.55, - ng(1032, x.toDouble(), y.toDouble()) - ) - s[x, y] = ColorRGBa(nv.x, nv.y, 0.0, 1.0) - } - } - s.upload() - - val directional = DirectionalBlur() - - // -- create a bidirectional composite filter by using a directional filter twice - val bidirectional = directional.then(directional) { - firstParameters { - window = 50 - spread = 1.5 + sin(seconds) - perpendicular = false - } - secondParameters { - window = 3 - spread = 1.5 + cos(seconds) - perpendicular = true - } - } - - val grain = FilmGrain() - grain.grainStrength = 1.0 - - // -- create a grain-blur composite filter - val grainBlur = bidirectional.then(grain) - - extend(Post()) { - post { input, output -> - grain.time = frameCount * 1.0 - grainBlur.apply(arrayOf(input, direction), output) - } - } - - val image = loadImage("demo-data/images/image-001.png") - extend { - drawer.image(image) - } - } -} diff --git a/orx-fx/src/jvmDemo/kotlin/DemoContour01.kt b/orx-fx/src/jvmDemo/kotlin/DemoContour01.kt deleted file mode 100644 index ee81f248..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoContour01.kt +++ /dev/null @@ -1,64 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.edges.Contour -import org.openrndr.extra.imageFit.imageFit -import org.openrndr.extra.shapes.primitives.grid - -/** - * Demonstrates the [Contour] filter. - * @author Edwin Jakobs - * - * This demo creates a grid of 2x2 to draw a loaded image four times, - * each using the [Contour] effect with different parameters. - * - * `actions` is a variable containing a list of 4 functions. - * Each of these functions sets the effect parameters to different values. - * - * The 4 grid cells and the 4 actions are used in pairs: - * first the action is called to set the effect parameters, the - * effect is applied, and the result is drawn in a cell. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val image = loadImage("demo-data/images/image-001.png") - val contour = Contour() - contour.window = 1 - contour.contourColor = ColorRGBa.PINK - contour.backgroundOpacity = 0.0 - - val edges = image.createEquivalent() - - val cells = drawer.bounds.grid(2, 2).flatten() - val actions = listOf( - { - contour.outputBands = true - contour.levels = 2.0 - }, - { - contour.outputBands = false - contour.levels = 2.0 - }, - { - contour.outputBands = false - contour.levels = 8.0 - }, - { - contour.outputBands = true - contour.levels = 8.0 - }, - ) - extend { - for ((cell, action) in cells zip actions) { - action() - contour.apply(image, edges) - drawer.imageFit(edges, cell) - } - } - } -} \ No newline at end of file diff --git a/orx-fx/src/jvmDemo/kotlin/DemoDirectionalBlur01.kt b/orx-fx/src/jvmDemo/kotlin/DemoDirectionalBlur01.kt deleted file mode 100644 index d305b18f..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoDirectionalBlur01.kt +++ /dev/null @@ -1,52 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.fx.blur.DirectionalBlur -import org.openrndr.math.smoothstep -import kotlin.math.cos -import kotlin.math.sin - -/** - * Demonstrates how to use [DirectionalBlur] by creating a `direction` - * ColorBuffer in which the red and green components of the pixels point - * in various directions where to sample pixels from. All the pixel colors - * of the ColorBuffer are set one by one using two for loops. - * - * Note the FLOAT32 color type of the buffer to allow for negative values, - * so sampling can happen from every direction. - * - * Every 60 animation frames the `centerWindow` property is toggled - * between true and false to demonstrate how the result changes. - * - */ -fun main() = application { - program { - val db = DirectionalBlur() - val rt = renderTarget(width, height) { - colorBuffer() - } - - val blurred = colorBuffer(width, height) - val direction = colorBuffer(width, height, type = ColorType.FLOAT32) - val s = direction.shadow - for (y in 0 until height) { - for (x in 0 until width) { - val a = smoothstep(0.45, 0.55, cos((x + y) * 0.01) * 0.5 + 0.5) - s[x, y] = ColorRGBa(cos(y * .1) * a, sin(x * 0.1) * a, 0.0, 1.0) - } - } - - s.upload() - val image = loadImage("demo-data/images/image-001.png") - extend { - drawer.isolatedWithTarget(rt) { - clear(ColorRGBa.BLACK) - drawer.image(image) - } - db.window = 10 - db.centerWindow = frameCount % 120 > 60 - db.apply(arrayOf(rt.colorBuffer(0), direction), blurred) - drawer.image(blurred) - } - } -} \ No newline at end of file diff --git a/orx-fx/src/jvmDemo/kotlin/DemoDirectionalDisplace01.kt b/orx-fx/src/jvmDemo/kotlin/DemoDirectionalDisplace01.kt deleted file mode 100644 index fa65dbe5..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoDirectionalDisplace01.kt +++ /dev/null @@ -1,50 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.color.rgb -import org.openrndr.draw.ColorType -import org.openrndr.draw.colorBuffer -import org.openrndr.draw.loadImage -import org.openrndr.drawImage -import org.openrndr.extra.fx.distort.DirectionalDisplace -import org.openrndr.extra.noise.simplex -import org.openrndr.extra.shapes.primitives.grid -import kotlin.math.cos - -/** - * Demonstrate how to use [DirectionalDisplace]. - * - * The direction map is populated using `drawImage` instead of - * pixel by pixel. A grid of circles is drawn, each circle with a - * color based on simplex noise. The R and G channels of the colors - * control the direction of the sampling. By animating the sampling - * distance the result oscillates between no-effect and a noticeable one. - */ -fun main() = application { - program { - val displace = DirectionalDisplace() - - val displaced = colorBuffer(width, height) - val direction = drawImage(width, height, type = ColorType.FLOAT32) { - clear(ColorRGBa.BLACK) - bounds.grid(32, 24).flatten().forEach { - fill = rgb( - simplex(133, it.center * 0.004), - simplex(197, it.center * 0.004), - 0.0 - ) - stroke = null - - //rectangle(it) - circle(it.center, 8.0) - } - } - val image = loadImage("demo-data/images/image-001.png") - extend { - displace.distance = 100.0 + 100.0 * cos(seconds) - displace.wrapX = true - displace.wrapX = true - displace.apply(arrayOf(image, direction), displaced) - drawer.image(displaced) - } - } -} \ No newline at end of file diff --git a/orx-fx/src/jvmDemo/kotlin/DemoDirectionalDisplace02.kt b/orx-fx/src/jvmDemo/kotlin/DemoDirectionalDisplace02.kt deleted file mode 100644 index 50cc280d..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoDirectionalDisplace02.kt +++ /dev/null @@ -1,45 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.color.rgb -import org.openrndr.draw.ColorType -import org.openrndr.draw.colorBuffer -import org.openrndr.draw.loadImage -import org.openrndr.drawImage -import org.openrndr.extra.fx.distort.DirectionalDisplace -import org.openrndr.math.Polar - -/** - * Demonstrate how to use [DirectionalDisplace]. - * - * The program draws 12 overlapping translucent circles on the - * `direction` color buffer to produce new color combinations - * on the overlapping areas. Those colors specify where the - * `DirectionalDisplace` effect will sample pixels from. - */ - -fun main() = application { - program { - val displace = DirectionalDisplace() - - val displaced = colorBuffer(width, height) - val direction = drawImage(width, height, type = ColorType.FLOAT32) { - clear(ColorRGBa.BLACK) - for(x in 0 until 6) { - val offset = Polar(x * 60.0).cartesian - fill = rgb(offset.y, offset.x, 0.0, 0.3) - stroke = null - - val pos = bounds.center - offset * 110.0 - circle(pos, 120.0) - circle(pos, 80.0) - } - } - val image = loadImage("demo-data/images/image-001.png") - extend { - displace.distance = 250.0 - displace.apply(arrayOf(image, direction), displaced) - drawer.image(displaced) - //drawer.image(direction) - } - } -} \ No newline at end of file diff --git a/orx-fx/src/jvmDemo/kotlin/DemoDistortLenses01.kt b/orx-fx/src/jvmDemo/kotlin/DemoDistortLenses01.kt deleted file mode 100644 index 8b5fa656..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoDistortLenses01.kt +++ /dev/null @@ -1,30 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.distort.Lenses - -/** - * Demonstrates the [Lenses] effect, which by default subdivides a color buffer - * in 8 columns and 6 rows, and displaces the source texture inside each rectangle. - * Try experimenting with some of the other parameters, like `distort`. - * You can even animate them. - */ -fun main() = application { - configure { - width = 640 - height = 480 - - } - program { - val image = loadImage("demo-data/images/image-001.png") - val lenses = Lenses() - val edges = image.createEquivalent() - extend { - lenses.rotation = 30.0 - lenses.scale = 1.5 - - lenses.apply(image, edges) - drawer.image(edges) - } - } -} \ No newline at end of file diff --git a/orx-fx/src/jvmDemo/kotlin/DemoDitherLumaHalftone01.kt b/orx-fx/src/jvmDemo/kotlin/DemoDitherLumaHalftone01.kt deleted file mode 100644 index b1ea9558..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoDitherLumaHalftone01.kt +++ /dev/null @@ -1,29 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.dither.LumaHalftone - -/** - * Demonstrates the [LumaHalftone] effect and moste of its parameters. - * The `invert` parameter toggles between true and false once per second. - * The `phase0` and `phase1` parameters depend on `seconds`, which makes - * the pattern wobble slowly. - */ -fun main() = application { - program { - val image = loadImage("demo-data/images/image-001.png") - val filteredImage = image.createEquivalent() - val lumaHalftone = LumaHalftone() - extend { - lumaHalftone.rotation = -15.0 - lumaHalftone.freq0 = 100.0 - lumaHalftone.gain1 = 1.0 - lumaHalftone.threshold = 0.5 - lumaHalftone.phase0 = seconds * 0.1 - lumaHalftone.phase1 = -seconds * 0.1 - lumaHalftone.apply(image, filteredImage) - lumaHalftone.invert = seconds.mod(2.0) < 1.0 - drawer.image(filteredImage) - } - } -} diff --git a/orx-fx/src/jvmDemo/kotlin/DemoFluidDistort01.kt b/orx-fx/src/jvmDemo/kotlin/DemoFluidDistort01.kt deleted file mode 100644 index f30c3bb4..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoFluidDistort01.kt +++ /dev/null @@ -1,34 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.colorBuffer -import org.openrndr.draw.createEquivalent -import org.openrndr.extensions.SingleScreenshot -import org.openrndr.extra.fx.distort.FluidDistort -import org.openrndr.extra.fx.patterns.Checkers - -/** - * Demonstrates [FluidDistort], a fluid simulation real time effect. - * All pixels are slowly displaced in a turbulent manner as if they were a gas or a liquid. - */ -fun main() = application { - program { - val fd = FluidDistort() - val checkers = Checkers() - - val image = colorBuffer(width, height) - val distorted = image.createEquivalent() - checkers.size = 64.0 - checkers.apply(emptyArray(), image) - - if (System.getProperty("takeScreenshot") == "true") { - extensions.filterIsInstance().forEach { - it.delayFrames = 150 - } - } - extend { - // Ensure >0.01 for a better screenshot - fd.blend = (mouse.position.x / width).coerceAtLeast(0.01) - fd.apply(image, distorted) - drawer.image(distorted) - } - } -} diff --git a/orx-fx/src/jvmDemo/kotlin/DemoLaserBlur01.kt b/orx-fx/src/jvmDemo/kotlin/DemoLaserBlur01.kt deleted file mode 100644 index 2c844a5a..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoLaserBlur01.kt +++ /dev/null @@ -1,57 +0,0 @@ -//import org.openrndr.application -//import org.openrndr.color.ColorRGBa -//import org.openrndr.extra.compositor.compose -//import org.openrndr.extra.compositor.draw -//import org.openrndr.extra.compositor.layer -//import org.openrndr.extra.compositor.post -//import org.openrndr.extra.fx.blur.GaussianBloom -//import org.openrndr.extra.fx.blur.LaserBlur -//import org.openrndr.extra.gui.GUI -//import org.openrndr.extra.gui.addTo -//import org.openrndr.extra.noise.simplex -//import org.openrndr.math.Vector2 -//import kotlin.math.absoluteValue -// -//fun main() = application { -// configure { -// width = 1280 -// height = 720 -// } -// -// program { -// val gui = GUI() -// val c = compose { -// layer { -// draw { -// drawer.fill = null -// drawer.strokeWeight = 4.0 -// drawer.translate(width/2.0, height/2.0) -// drawer.rotate(seconds*45.0 + simplex(0, seconds)*45.0) -// drawer.translate(-width/2.0, -height/2.0) -// for (y in -1..1) { -// for (x in -1..1) { -// drawer.stroke = ColorRGBa.RED.toHSVa() -// .shiftHue(0.0 + simplex(500+x+y,seconds)*5.0) -// .shade(0.5 + 0.5 * simplex(300+x+y,seconds*4.0).absoluteValue) -// .toRGBa() -// val r = simplex(400+x+y, seconds) * 150.0 + 150.0 -// drawer.circle(width / 2.0 + x * 100.0, height / 2.0 + y * 100.0, r) -// } -// } -// } -// post(LaserBlur()) { -// center = Vector2(simplex(2, seconds*0.1), simplex(100, seconds*0.1)) -// aberration = simplex(5, seconds) * 0.01 -// radius = simplex(7, seconds) -// }.addTo(gui) -// post(GaussianBloom()).addTo(gui) -// } -// } -// extend(gui) { -// doubleBind = true -// } -// extend { -// c.draw(drawer) -// } -// } -//} \ No newline at end of file diff --git a/orx-fx/src/jvmDemo/kotlin/DemoOkLab01.kt b/orx-fx/src/jvmDemo/kotlin/DemoOkLab01.kt deleted file mode 100644 index d987d22c..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoOkLab01.kt +++ /dev/null @@ -1,25 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.ColorType -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.loadImage -import org.openrndr.extra.fx.color.OkLabToRgb -import org.openrndr.extra.fx.color.RgbToOkLab - -/** - * This demonstrates converting a [ColorBuffer] from and to (OK)LAB color space using the [RgbToOkLab] and [OkLabToRgb] - * filters. The (OK)Lab representation is signed and requires a floating point representation. - */ - -fun main() = application { - program { - val rgbToOkLab = RgbToOkLab() - val okLabToRgb = OkLabToRgb() - val image = loadImage("demo-data/images/image-001.png") - val labImage = image.createEquivalent(type = ColorType.FLOAT32) - rgbToOkLab.apply(image, labImage) - okLabToRgb.apply(labImage, image) - extend { - drawer.image(image) - } - } -} diff --git a/orx-fx/src/jvmDemo/kotlin/DemoPost01.kt b/orx-fx/src/jvmDemo/kotlin/DemoPost01.kt deleted file mode 100644 index 0de32bb4..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoPost01.kt +++ /dev/null @@ -1,36 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.fx.Post -import org.openrndr.extra.fx.blend.Add -import org.openrndr.extra.fx.blur.ApproximateGaussianBlur -import kotlin.math.cos - -/** - * Demonstrates how to create an `extend` block to apply a post-processing effect. - * The effect is an [ApproximateGaussianBlur] and its `sigma` parameter - * is animated. The Blur effect is combined with whatever the user draws - * in the regular `extend` block using the `Add` filter, resulting in - * an additive composition. - * - * This demo also shows how to make a program window resizable. - */ -fun main() = application { - configure { - windowResizable = true - } - program { - extend(Post()) { - val blur = ApproximateGaussianBlur() - val add = Add() - post { input, output -> - blur.window = 50 - blur.sigma = 50.0 * (cos(seconds) * 0.5 + 0.5) - blur.apply(input, intermediate[0]) - add.apply(arrayOf(input, intermediate[0]), output) - } - } - extend { - drawer.circle(width / 2.0, height / 2.0, 100.0) - } - } -} - diff --git a/orx-fx/src/jvmDemo/kotlin/DemoSpectralBlend01.kt b/orx-fx/src/jvmDemo/kotlin/DemoSpectralBlend01.kt deleted file mode 100644 index d343107b..00000000 --- a/orx-fx/src/jvmDemo/kotlin/DemoSpectralBlend01.kt +++ /dev/null @@ -1,57 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.createEquivalent -import org.openrndr.drawImage -import org.openrndr.extra.fx.blend.BlendSpectral -import org.openrndr.extra.fx.blur.BoxBlur -import org.openrndr.extra.fx.patterns.Checkers -import org.openrndr.math.Vector2 -import kotlin.math.sin - -/** - * Demonstration of how to use the [BlendSpectral] filter to combine two images, using - * this pigment-simulation color mixing approach. - * - * The program: - * - generates two images - * - blurs one of them - * - creates and draws a checkers-pattern as the background - * - mixes and draws both images - * - * The `fill` factor, which controls how the top and the bottom colors are mixed, is animated. - * - * The `clip` parameter is also animated and toggles every 6 seconds. - */ -fun main() = application { - configure { - width = 800 - height = 800 - } - program { - val a = drawImage(width, height) { - drawer.stroke = null - drawer.fill = ColorRGBa.BLUE - drawer.circle(drawer.bounds.center - Vector2(100.0, 0.0), drawer.width * 0.25) - } - val b = drawImage(width, height) { - drawer.clear(ColorRGBa.TRANSPARENT) - drawer.stroke = ColorRGBa.RED - drawer.strokeWeight = 10.0 - drawer.fill = ColorRGBa.YELLOW.opacify(1.0) - drawer.circle(drawer.bounds.center + Vector2(100.0, 0.0), drawer.width * 0.25) - } - BoxBlur().apply { window = 10 }.apply(b, b) - val checked = a.createEquivalent() - Checkers().apply(emptyArray(), checked) - - val mixed = a.createEquivalent() - val blendSpectral = BlendSpectral() - extend { - drawer.image(checked) - blendSpectral.fill = sin(seconds) * 0.5 + 0.5 - blendSpectral.clip = seconds.mod(12.0) > 6.0 - blendSpectral.apply(a, b, mixed) - drawer.image(mixed) - } - } -} diff --git a/orx-fx/src/shaders/glsl/antialias/fxaa.frag b/orx-fx/src/shaders/glsl/antialias/fxaa.frag deleted file mode 100644 index bf854232..00000000 --- a/orx-fx/src/shaders/glsl/antialias/fxaa.frag +++ /dev/null @@ -1,111 +0,0 @@ -uniform float lumaThreshold; -uniform float maxSpan; -uniform float directionReduceMultiplier; -uniform float directionReduceMinimum; - -uniform sampler2D tex0; - -in vec2 v_texCoord0; - - -out vec4 fragColor; - -// see FXAA -// http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf -// http://iryoku.com/aacourse/downloads/09-FXAA-3.11-in-15-Slides.pdf -// http://horde3d.org/wiki/index.php5?title=Shading_Technique_-_FXAA - -void main(void) { - const int u_showEdges = 0; - const int u_fxaaOn = 1; - - vec2 u_texelStep = 1.0 / textureSize(tex0, 0); - vec3 rgbM = min(vec3(1), texture(tex0, v_texCoord0).rgb); - - // Possibility to toggle FXAA on and off. - if (u_fxaaOn == 0) - { - fragColor = vec4(rgbM, 1.0); - return; - } - - // Sampling neighbour texels. Offsets are adapted to OpenGL texture coordinates. - vec3 rgbNW = min(vec3(1),textureOffset(tex0, v_texCoord0, ivec2(-1, 1)).rgb); - vec3 rgbNE = min(vec3(1),textureOffset(tex0, v_texCoord0, ivec2(1, 1)).rgb); - vec3 rgbSW = min(vec3(1),textureOffset(tex0, v_texCoord0, ivec2(-1, -1)).rgb); - vec3 rgbSE = min(vec3(1),textureOffset(tex0, v_texCoord0, ivec2(1, -1)).rgb); - - // see http://en.wikipedia.org/wiki/Grayscale - const vec3 toLuma = vec3(0.299, 0.587, 0.114); - - // Convert from RGB to luma. - float lumaNW = dot(rgbNW, toLuma); - float lumaNE = dot(rgbNE, toLuma); - float lumaSW = dot(rgbSW, toLuma); - float lumaSE = dot(rgbSE, toLuma); - float lumaM = dot(rgbM, toLuma); - - // Gather minimum and maximum luma. - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); - - // If contrast is lower than a maximum threshold ... - if (lumaMax - lumaMin < lumaMax * lumaThreshold) - { - // ... do no AA and return. - fragColor = vec4(rgbM, 1.0); - - return; - } - - // Sampling is done along the gradient. - vec2 samplingDirection; - samplingDirection.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); - samplingDirection.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); - - // Sampling step distance depends on the luma: The brighter the sampled texels, the smaller the final sampling step direction. - // This results, that brighter areas are less blurred/more sharper than dark areas. - float samplingDirectionReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * 0.25 * directionReduceMultiplier, directionReduceMinimum); - - // Factor for norming the sampling direction plus adding the brightness influence. - float minSamplingDirectionFactor = 1.0 / (min(abs(samplingDirection.x), abs(samplingDirection.y)) + samplingDirectionReduce); - - // Calculate final sampling direction vector by reducing, clamping to a range and finally adapting to the texture size. - samplingDirection = clamp(samplingDirection * minSamplingDirectionFactor, vec2(-maxSpan, -maxSpan), vec2(maxSpan, maxSpan)) * u_texelStep; - - // Inner samples on the tab. - vec3 rgbSampleNeg = min(vec3(1),texture(tex0, v_texCoord0 + samplingDirection * (1.0/3.0 - 0.5)).rgb); - vec3 rgbSamplePos = min(vec3(1),texture(tex0, v_texCoord0 + samplingDirection * (2.0/3.0 - 0.5)).rgb); - - vec3 rgbTwoTab = (rgbSamplePos + rgbSampleNeg) * 0.5; - - // Outer samples on the tab. - vec3 rgbSampleNegOuter = min(vec3(1),texture(tex0, v_texCoord0 + samplingDirection * (0.0/3.0 - 0.5)).rgb); - vec3 rgbSamplePosOuter = min(vec3(1),texture(tex0, v_texCoord0 + samplingDirection * (3.0/3.0 - 0.5)).rgb); - - vec3 rgbFourTab = (rgbSamplePosOuter + rgbSampleNegOuter) * 0.25 + rgbTwoTab * 0.5; - - // Calculate luma for checking against the minimum and maximum value. - float lumaFourTab = dot(rgbFourTab, toLuma); - - // Are outer samples of the tab beyond the edge ... - if (lumaFourTab < lumaMin || lumaFourTab > lumaMax) - { - // ... yes, so use only two samples. - fragColor = vec4(rgbTwoTab, 1.0); -// fragColor.r = 1.0; - } - else - { - // ... no, so use four samples. - fragColor = vec4(rgbFourTab, 1.0); -// fragColor.g = 1.0; - } - - // Show edges for debug purposes. - if (u_showEdges != 0) - { - fragColor.r = 1.0; - } - //fragColor.rgb = vec3(fragColor.rgb-rgbM)*40.0; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/add.frag b/orx-fx/src/shaders/glsl/blend/add.frag deleted file mode 100644 index f557b46e..00000000 --- a/orx-fx/src/shaders/glsl/blend/add.frag +++ /dev/null @@ -1,40 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform bool clip; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - vec4 b = texture2D(tex1, v_texCoord0); - #endif - - vec3 na = a.a > 0.0 ? a.rgb/a.a : vec3(0.0); - vec3 nb = b.a > 0.0 ? b.rgb/b.a : vec3(0.0); - - vec3 addColor = b.rgb; - - vec4 result; - if (clip) { - result = vec4((na + addColor), 1.0) * a.a; - } else { - result = (1.0-a.a) * b + a.a * b.a * vec4(min(na + nb, vec3(1.0)), 1.0) + (1.0-b.a) * a; - } - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/color-burn.frag b/orx-fx/src/shaders/glsl/blend/color-burn.frag deleted file mode 100644 index 509bb390..00000000 --- a/orx-fx/src/shaders/glsl/blend/color-burn.frag +++ /dev/null @@ -1,48 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform bool clip; - -float blendColorBurn(float base, float blend) { - return (blend==0.0) ? blend : max((1.0 - ((1.0 - base) / blend)), 0.0); -} - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - vec4 b = texture2D(tex1, v_texCoord0); - #endif - - vec3 na = a.a == 0.0 ? vec3(0.0): a.rgb / a.a; - vec3 nb = b.a == 0.0 ? vec3(0.0): b.rgb / b.a; - - vec3 m = vec3( - blendColorBurn(na.r, nb.r), - blendColorBurn(na.g, nb.g), - blendColorBurn(na.b, nb.b) - ); - - vec4 result; - if (clip) { - result = vec4(na * (1.0 - b.a) + b.a * m, 1.0) * a.a; - } else { - result = (1.0-a.a) * b + a.a * b.a * vec4(m, 1.0) + (1.0-b.a) * a; - } - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/color-dodge.frag b/orx-fx/src/shaders/glsl/blend/color-dodge.frag deleted file mode 100644 index 6effa55d..00000000 --- a/orx-fx/src/shaders/glsl/blend/color-dodge.frag +++ /dev/null @@ -1,50 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform bool clip; - -float dodge(float base, float blend) { - return (blend==1.0)?blend:min(base/(1.0-blend),1.0); -} - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - vec4 b = texture2D(tex1, v_texCoord0); - #endif - - vec3 na = a.a == 0.0 ? vec3(0.0): a.rgb / a.a; - vec3 nb = b.a == 0.0 ? vec3(0.0): b.rgb / b.a; - - vec3 m = vec3( - dodge(na.r, nb.r), - dodge(na.g, nb.g), - dodge(na.b, nb.b) - ); - - vec4 result; - if (clip) { - result = vec4(na * (1.0 - b.a) + b.a * m, 1.0) * a.a; - } else { - result = (1.0-a.a) * b + a.a * b.a * vec4(m, 1.0) + (1.0-b.a) * a; - } - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif - -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/darken.frag b/orx-fx/src/shaders/glsl/blend/darken.frag deleted file mode 100644 index b2b6e8d4..00000000 --- a/orx-fx/src/shaders/glsl/blend/darken.frag +++ /dev/null @@ -1,43 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform bool clip; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - vec4 b = texture2D(tex1, v_texCoord0); - #endif - vec3 na = a.a == 0.0 ? vec3(0.0): a.rgb / a.a; - vec3 nb = b.a == 0.0 ? vec3(0.0): b.rgb / b.a; - - vec3 m = vec3( - nb.r <= na.r? nb.r : na.r, - nb.g <= na.g? nb.g : na.g, - nb.b <= na.b? nb.b : na.b); - - vec4 result; - if (clip) { - result = vec4(na * (1.0 - b.a) + b.a * m, 1.0) * a.a; - } else { - result = (1.0-a.a) * b + a.a * b.a * vec4(m, 1.0) + (1.0-b.a) * a; - } - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/destination-atop.frag b/orx-fx/src/shaders/glsl/blend/destination-atop.frag deleted file mode 100644 index d727961d..00000000 --- a/orx-fx/src/shaders/glsl/blend/destination-atop.frag +++ /dev/null @@ -1,32 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 src = texture(tex0, v_texCoord0); - vec4 dest = texture(tex1, v_texCoord0); - #else - vec4 src = texture2D(tex0, v_texCoord0); - vec4 dest = texture2D(tex1, v_texCoord0); - #endif - - float lsrc = src.a * (1.0 - dest.a); - float lboth = src.a * dest.a; - - vec4 result = src * lsrc + dest * 0.0 + dest * lboth; - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/destination-in.frag b/orx-fx/src/shaders/glsl/blend/destination-in.frag deleted file mode 100644 index 6b0a58c6..00000000 --- a/orx-fx/src/shaders/glsl/blend/destination-in.frag +++ /dev/null @@ -1,30 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 src = texture(tex0, v_texCoord0); - vec4 dest = texture(tex1, v_texCoord0); - #else - vec4 src = texture2D(tex0, v_texCoord0); - vec4 dest = texture2D(tex1, v_texCoord0); - #endif - - float lboth = src.a * dest.a; - vec4 result = dest * lboth; - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/destination-out.frag b/orx-fx/src/shaders/glsl/blend/destination-out.frag deleted file mode 100644 index ab5a6d3e..00000000 --- a/orx-fx/src/shaders/glsl/blend/destination-out.frag +++ /dev/null @@ -1,31 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 src = texture(tex0, v_texCoord0); - vec4 dest = texture(tex1, v_texCoord0); - #else - vec4 src = texture2D(tex0, v_texCoord0); - vec4 dest = texture2D(tex1, v_texCoord0); - #endif - - float ldest = dest.a * (1.0 - src.a); - - vec4 result = dest * ldest; - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/hard-light.frag b/orx-fx/src/shaders/glsl/blend/hard-light.frag deleted file mode 100644 index ca8cb2bc..00000000 --- a/orx-fx/src/shaders/glsl/blend/hard-light.frag +++ /dev/null @@ -1,44 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform bool clip; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - vec4 b = texture2D(tex1, v_texCoord0); - #endif - - vec3 na = a.a == 0.0 ? vec3(0.0): a.rgb / a.a; - vec3 nb = b.a == 0.0 ? vec3(0.0): b.rgb / b.a; - - vec3 m = vec3( - nb.r <= 0.5? 2.0*na.r * nb.r : 1.0 - 2.0*(1.0 - na.r)*(1.0 - nb.r), - nb.g <= 0.5? 2.0*na.g * nb.g : 1.0 - 2.0*(1.0 - na.g)*(1.0 - nb.g), - nb.b <= 0.5? 2.0*na.b * nb.b : 1.0 - 2.0*(1.0 - na.b)*(1.0 - nb.b) - ); - - vec4 result; - if (clip) { - result = vec4(na * (1.0 - b.a) + b.a * m, 1.0) * a.a; - } else { - result = (1.0-a.a) * b + a.a * b.a * vec4(m, 1.0) + (1.0-b.a) * a; - } - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/lighten.frag b/orx-fx/src/shaders/glsl/blend/lighten.frag deleted file mode 100644 index 5484d5b1..00000000 --- a/orx-fx/src/shaders/glsl/blend/lighten.frag +++ /dev/null @@ -1,44 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform bool clip; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - vec4 b = texture2D(tex1, v_texCoord0); - #endif - - vec3 na = a.a == 0.0 ? vec3(0.0): a.rgb / a.a; - vec3 nb = b.a == 0.0 ? vec3(0.0): b.rgb / b.a; - - vec3 m = vec3( - nb.r >= na.r? nb.r : na.r, - nb.g >= na.g? nb.g : na.g, - nb.b >= na.b? nb.b : na.b); - - vec4 result; - if (clip) { - result = vec4(na * (1.0 - b.a) + b.a * m, 1.0) * a.a; - } else { - result = (1.0-a.a) * b + a.a * b.a * vec4(m, 1.0) + (1.0-b.a) * a; - } - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/multiply-contrast.frag b/orx-fx/src/shaders/glsl/blend/multiply-contrast.frag deleted file mode 100644 index a0b1e12e..00000000 --- a/orx-fx/src/shaders/glsl/blend/multiply-contrast.frag +++ /dev/null @@ -1,37 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - vec4 b = texture2D(tex1, v_texCoord0); - #endif - - float ai = max(a.z, max(a.x, a.y)); - float bi = max(b.z, max(b.x, b.y)); - - vec3 f = a.rgb - (1.0-b.rgb)*2.0*b.a; - - vec4 result; - result.rgb = max(vec3(0.0), f) * (1.0) + b.rgb * (1.0-a.a); - result.a = 1.0; - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/multiply.frag b/orx-fx/src/shaders/glsl/blend/multiply.frag deleted file mode 100644 index 0c25658f..00000000 --- a/orx-fx/src/shaders/glsl/blend/multiply.frag +++ /dev/null @@ -1,46 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; - -uniform bool clip; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -vec3 u(vec4 x) { - return x.a == 0.0? vec3(0.0) : x.rgb / x.a; -} - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - vec4 b = texture2D(tex1, v_texCoord0); - #endif - - vec3 na = u(a); - vec3 nb = u(b); - vec3 mulColor = mix(vec3(1.0), nb, b.a); - - vec4 result; - if (clip) { - result = vec4(a.rgb * mulColor, a.a); - } else { - result = (1.0-a.a) * b + a.a * b.a * vec4(na * nb, 1.0) + (1.0-b.a) * a; - } - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} - diff --git a/orx-fx/src/shaders/glsl/blend/normal.frag b/orx-fx/src/shaders/glsl/blend/normal.frag deleted file mode 100644 index cd9f6a5e..00000000 --- a/orx-fx/src/shaders/glsl/blend/normal.frag +++ /dev/null @@ -1,46 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform bool clip; - -uniform float opacityA; -uniform float opacityB; - - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - vec4 b = texture2D(tex1, v_texCoord0); - #endif - - a *= opacityA; - b *= opacityB; - - float alpha = min(1.0, max(0.0, b.a)); - - vec4 result; - if (!clip) { - result = a * (1.0 - alpha) + b; - result.a = clamp(result.a, 0.0, 1.0); - } else { - result = a * (1.0 - alpha) + b * clamp(a.a, 0.0, 1.0); - } - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/overlay.frag b/orx-fx/src/shaders/glsl/blend/overlay.frag deleted file mode 100644 index 11b14196..00000000 --- a/orx-fx/src/shaders/glsl/blend/overlay.frag +++ /dev/null @@ -1,55 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform bool clip; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -vec3 demul(vec4 c) { - if (c.a == 0.0) { - return vec3(0.0); - } else { - return c.rgb / c.a; - } -} - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - vec4 b = texture2D(tex1, v_texCoord0); - #endif - - vec3 na = demul(a); - vec3 nb = demul(b); - - vec3 m = vec3( - na.r <= 0.5? 2.0 * na.r * nb.r : (1.0 - 2.0 * (1.0 - na.r) * (1.0 - nb.r)), - na.g <= 0.5? 2.0 * na.g * nb.g : (1.0 - 2.0 * (1.0 - na.g) * (1.0 - nb.g)), - na.b <= 0.5? 2.0 * na.b * nb.b : (1.0 - 2.0 * (1.0 - na.b) * (1.0 - nb.b)) - ); - - vec4 result; - if (clip) { - vec3 fc = na * (1.0 - b.a) + m * b.a; - result = vec4(fc, 1.0) * a.a; - } else { - result = (1.0-a.a) * b + a.a * b.a * vec4(m, 1.0) + (1.0-b.a) * a; - } - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif - -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/passthrough.frag b/orx-fx/src/shaders/glsl/blend/passthrough.frag deleted file mode 100644 index 667ad4fb..00000000 --- a/orx-fx/src/shaders/glsl/blend/passthrough.frag +++ /dev/null @@ -1,25 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 result = texture(tex0, v_texCoord0); - #else - vec4 result = texture2D(tex0, v_texCoord0); - #endif - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/screen.frag b/orx-fx/src/shaders/glsl/blend/screen.frag deleted file mode 100644 index cd946913..00000000 --- a/orx-fx/src/shaders/glsl/blend/screen.frag +++ /dev/null @@ -1,44 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform bool clip; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - vec4 b = texture2D(tex1, v_texCoord0); - #endif - - vec3 na = a.a == 0.0 ? vec3(0.0): a.rgb / a.a; - vec3 nb = b.a == 0.0 ? vec3(0.0): b.rgb / b.a; - - vec3 m = vec3( - 1.0-((1.0-na.r)*(1.0-nb.r)), - 1.0-((1.0-na.g)*(1.0-nb.g)), - 1.0-((1.0-na.b)*(1.0-nb.b))); - - vec4 result; - if (clip) { - result = vec4(na * (1.0 - b.a) + b.a * m, 1.0) * a.a; - } else { - result = (1.0-a.a) * b + a.a * b.a * vec4(m, 1.0) + (1.0-b.a) * a; - } - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/source-atop.frag b/orx-fx/src/shaders/glsl/blend/source-atop.frag deleted file mode 100644 index e157a6b0..00000000 --- a/orx-fx/src/shaders/glsl/blend/source-atop.frag +++ /dev/null @@ -1,32 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 src = texture(tex0, v_texCoord0); - vec4 dest = texture(tex1, v_texCoord0); - #else - vec4 src = texture2D(tex0, v_texCoord0); - vec4 dest = texture2D(tex1, v_texCoord0); - #endif - - float ldest = dest.a * (1.0 - src.a); - float lboth = src.a * dest.a; - - vec4 result = dest * ldest + src * lboth; - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/source-in.frag b/orx-fx/src/shaders/glsl/blend/source-in.frag deleted file mode 100644 index 572786bd..00000000 --- a/orx-fx/src/shaders/glsl/blend/source-in.frag +++ /dev/null @@ -1,31 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 src = texture(tex0, v_texCoord0); - vec4 dest = texture(tex1, v_texCoord0); - #else - vec4 src = texture2D(tex0, v_texCoord0); - vec4 dest = texture2D(tex1, v_texCoord0); - #endif - - float lboth = src.a * dest.a; - vec4 result = src * lboth; - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/source-out.frag b/orx-fx/src/shaders/glsl/blend/source-out.frag deleted file mode 100644 index 49ff7168..00000000 --- a/orx-fx/src/shaders/glsl/blend/source-out.frag +++ /dev/null @@ -1,31 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 src = texture(tex0, v_texCoord0); - vec4 dest = texture(tex1, v_texCoord0); - #else - vec4 src = texture2D(tex0, v_texCoord0); - vec4 dest = texture2D(tex1, v_texCoord0); - #endif - - float lsrc = src.a * (1.0 - dest.a); - - vec4 result = src * lsrc; - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/spectral.frag b/orx-fx/src/shaders/glsl/blend/spectral.frag deleted file mode 100644 index eb63d200..00000000 --- a/orx-fx/src/shaders/glsl/blend/spectral.frag +++ /dev/null @@ -1,209 +0,0 @@ -// Based on -// https://github.com/rvanwijnen/spectral.js/blob/5fb74962ea348f7385edb32bfd0fd55d8585451c/shaders/spectral.glsl - -// MIT License -// -// Copyright (c) 2023 Ronald van Wijnen -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#ifndef SPECTRAL -#define SPECTRAL - -const int SPECTRAL_SIZE = 38; -const float SPECTRAL_GAMMA = 2.4; -const float SPECTRAL_EPSILON = 0.0001; - -float spectral_uncompand(float x) { - return (x < 0.04045) ? x / 12.92 : pow((x + 0.055) / 1.055, SPECTRAL_GAMMA); -} - -float spectral_compand(float x) { - return (x < 0.0031308) ? x * 12.92 : 1.055 * pow(x, 1.0 / SPECTRAL_GAMMA) - 0.055; -} - -vec3 spectral_srgb_to_linear(vec3 srgb) { - return vec3(spectral_uncompand(srgb[0]), spectral_uncompand(srgb[1]), spectral_uncompand(srgb[2])); -} - -vec3 spectral_linear_to_srgb(vec3 lrgb) { - return clamp(vec3(spectral_compand(lrgb[0]), spectral_compand(lrgb[1]), spectral_compand(lrgb[2])), 0.0, 1.0); -} - -void spectral_upsampling(vec3 lrgb, out float w, out float c, out float m, out float y, out float r, out float g, out float b) { - // EJ: I patched this up to handle out of gamut RGB better. - w = min(lrgb.r, min(lrgb.g, lrgb.b)); - - lrgb -= w; - w = max(0.0, w); - c = max(0.0, min(lrgb.g, lrgb.b)); - m = max(0.0, min(lrgb.r, lrgb.b)); - y = max(0.0, min(lrgb.r, lrgb.g)); - r = min(max(0., lrgb.r - lrgb.b), max(0., lrgb.r - lrgb.g)); - g = min(max(0., lrgb.g - lrgb.b), max(0., lrgb.g - lrgb.r)); - b = min(max(0., lrgb.b - lrgb.g), max(0., lrgb.b - lrgb.r)); -} - -void spectral_linear_to_reflectance(vec3 lrgb, inout float R[SPECTRAL_SIZE]) { - float w, c, m, y, r, g, b; - - spectral_upsampling(lrgb, w, c, m, y, r, g, b); - - R[0] = max(SPECTRAL_EPSILON, w + c * 0.96853629 + m * 0.51567122 + y * 0.02055257 + r * 0.03147571 + g * 0.49108579 + b * 0.97901834); - R[1] = max(SPECTRAL_EPSILON, w + c * 0.96855103 + m * 0.54015520 + y * 0.02059936 + r * 0.03146636 + g * 0.46944057 + b * 0.97901649); - R[2] = max(SPECTRAL_EPSILON, w + c * 0.96859338 + m * 0.62645502 + y * 0.02062723 + r * 0.03140624 + g * 0.40165780 + b * 0.97901118); - R[3] = max(SPECTRAL_EPSILON, w + c * 0.96877345 + m * 0.75595012 + y * 0.02073387 + r * 0.03119611 + g * 0.24490420 + b * 0.97892146); - R[4] = max(SPECTRAL_EPSILON, w + c * 0.96942204 + m * 0.92826996 + y * 0.02114202 + r * 0.03053888 + g * 0.06826880 + b * 0.97858555); - R[5] = max(SPECTRAL_EPSILON, w + c * 0.97143709 + m * 0.97223624 + y * 0.02233154 + r * 0.02856855 + g * 0.02732883 + b * 0.97743705); - R[6] = max(SPECTRAL_EPSILON, w + c * 0.97541862 + m * 0.98616174 + y * 0.02556857 + r * 0.02459485 + g * 0.01360600 + b * 0.97428075); - R[7] = max(SPECTRAL_EPSILON, w + c * 0.98074186 + m * 0.98955255 + y * 0.03330189 + r * 0.01929520 + g * 0.01000187 + b * 0.96663223); - R[8] = max(SPECTRAL_EPSILON, w + c * 0.98580992 + m * 0.98676237 + y * 0.05185294 + r * 0.01423112 + g * 0.01284127 + b * 0.94822893); - R[9] = max(SPECTRAL_EPSILON, w + c * 0.98971194 + m * 0.97312575 + y * 0.10087639 + r * 0.01033111 + g * 0.02636635 + b * 0.89937713); - R[10] = max(SPECTRAL_EPSILON, w + c * 0.99238027 + m * 0.91944277 + y * 0.24000413 + r * 0.00765876 + g * 0.07058713 + b * 0.76070164); - R[11] = max(SPECTRAL_EPSILON, w + c * 0.99409844 + m * 0.32564851 + y * 0.53589066 + r * 0.00593693 + g * 0.70421692 + b * 0.46420440); - R[12] = max(SPECTRAL_EPSILON, w + c * 0.99517200 + m * 0.13820628 + y * 0.79874659 + r * 0.00485616 + g * 0.85473994 + b * 0.20123039); - R[13] = max(SPECTRAL_EPSILON, w + c * 0.99576545 + m * 0.05015143 + y * 0.91186529 + r * 0.00426186 + g * 0.95081565 + b * 0.08808402); - R[14] = max(SPECTRAL_EPSILON, w + c * 0.99593552 + m * 0.02912336 + y * 0.95399623 + r * 0.00409039 + g * 0.97170370 + b * 0.04592894); - R[15] = max(SPECTRAL_EPSILON, w + c * 0.99564041 + m * 0.02421691 + y * 0.97137099 + r * 0.00438375 + g * 0.97651888 + b * 0.02860373); - R[16] = max(SPECTRAL_EPSILON, w + c * 0.99464769 + m * 0.02660696 + y * 0.97939505 + r * 0.00537525 + g * 0.97429245 + b * 0.02060067); - R[17] = max(SPECTRAL_EPSILON, w + c * 0.99229579 + m * 0.03407586 + y * 0.98345207 + r * 0.00772962 + g * 0.97012917 + b * 0.01656701); - R[18] = max(SPECTRAL_EPSILON, w + c * 0.98638762 + m * 0.04835936 + y * 0.98553736 + r * 0.01366120 + g * 0.94258630 + b * 0.01451549); - R[19] = max(SPECTRAL_EPSILON, w + c * 0.96829712 + m * 0.00011720 + y * 0.98648905 + r * 0.03181352 + g * 0.99989207 + b * 0.01357964); - R[20] = max(SPECTRAL_EPSILON, w + c * 0.89228016 + m * 0.00008554 + y * 0.98674535 + r * 0.10791525 + g * 0.99989891 + b * 0.01331243); - R[21] = max(SPECTRAL_EPSILON, w + c * 0.53740239 + m * 0.85267882 + y * 0.98657555 + r * 0.46249516 + g * 0.13823139 + b * 0.01347661); - R[22] = max(SPECTRAL_EPSILON, w + c * 0.15360445 + m * 0.93188793 + y * 0.98611877 + r * 0.84604333 + g * 0.06968113 + b * 0.01387181); - R[23] = max(SPECTRAL_EPSILON, w + c * 0.05705719 + m * 0.94810268 + y * 0.98559942 + r * 0.94275572 + g * 0.05628787 + b * 0.01435472); - R[24] = max(SPECTRAL_EPSILON, w + c * 0.03126539 + m * 0.94200977 + y * 0.98507063 + r * 0.96860996 + g * 0.06111561 + b * 0.01479836); - R[25] = max(SPECTRAL_EPSILON, w + c * 0.02205445 + m * 0.91478045 + y * 0.98460039 + r * 0.97783966 + g * 0.08987709 + b * 0.01515250); - R[26] = max(SPECTRAL_EPSILON, w + c * 0.01802271 + m * 0.87065445 + y * 0.98425301 + r * 0.98187757 + g * 0.13656016 + b * 0.01540513); - R[27] = max(SPECTRAL_EPSILON, w + c * 0.01613460 + m * 0.78827548 + y * 0.98403909 + r * 0.98377315 + g * 0.22169624 + b * 0.01557233); - R[28] = max(SPECTRAL_EPSILON, w + c * 0.01520947 + m * 0.65738359 + y * 0.98388535 + r * 0.98470202 + g * 0.32176956 + b * 0.01565710); - R[29] = max(SPECTRAL_EPSILON, w + c * 0.01475977 + m * 0.59909403 + y * 0.98376116 + r * 0.98515481 + g * 0.36157329 + b * 0.01571025); - R[30] = max(SPECTRAL_EPSILON, w + c * 0.01454263 + m * 0.56817268 + y * 0.98368246 + r * 0.98537114 + g * 0.48361920 + b * 0.01571916); - R[31] = max(SPECTRAL_EPSILON, w + c * 0.01444459 + m * 0.54031997 + y * 0.98365023 + r * 0.98546685 + g * 0.46488579 + b * 0.01572133); - R[32] = max(SPECTRAL_EPSILON, w + c * 0.01439897 + m * 0.52110241 + y * 0.98361309 + r * 0.98550011 + g * 0.47440306 + b * 0.01572502); - R[33] = max(SPECTRAL_EPSILON, w + c * 0.01437620 + m * 0.51041094 + y * 0.98357259 + r * 0.98551031 + g * 0.48576990 + b * 0.01571717); - R[34] = max(SPECTRAL_EPSILON, w + c * 0.01436343 + m * 0.50526577 + y * 0.98353856 + r * 0.98550741 + g * 0.49267971 + b * 0.01571905); - R[35] = max(SPECTRAL_EPSILON, w + c * 0.01435687 + m * 0.50255080 + y * 0.98351247 + r * 0.98551323 + g * 0.49625685 + b * 0.01571059); - R[36] = max(SPECTRAL_EPSILON, w + c * 0.01435370 + m * 0.50126452 + y * 0.98350101 + r * 0.98551563 + g * 0.49807754 + b * 0.01569728); - R[37] = max(SPECTRAL_EPSILON, w + c * 0.01435408 + m * 0.50083021 + y * 0.98350852 + r * 0.98551547 + g * 0.49889859 + b * 0.01570020); -} - -vec3 spectral_xyz_to_srgb(vec3 xyz) { - mat3 XYZ_RGB; - - XYZ_RGB[0] = vec3( 3.24306333, -1.53837619, -0.49893282); - XYZ_RGB[1] = vec3(-0.96896309, 1.87542451, 0.04154303); - XYZ_RGB[2] = vec3( 0.05568392, -0.20417438, 1.05799454); - - float r = dot(XYZ_RGB[0], xyz); - float g = dot(XYZ_RGB[1], xyz); - float b = dot(XYZ_RGB[2], xyz); - - return spectral_linear_to_srgb(vec3(r, g, b)); -} - -vec3 spectral_reflectance_to_xyz(float R[SPECTRAL_SIZE]) { - vec3 xyz = vec3(0.0); - - xyz += R[0] * vec3(0.00006469, 0.00000184, 0.00030502); - xyz += R[1] * vec3(0.00021941, 0.00000621, 0.00103681); - xyz += R[2] * vec3(0.00112057, 0.00003101, 0.00531314); - xyz += R[3] * vec3(0.00376661, 0.00010475, 0.01795439); - xyz += R[4] * vec3(0.01188055, 0.00035364, 0.05707758); - xyz += R[5] * vec3(0.02328644, 0.00095147, 0.11365162); - xyz += R[6] * vec3(0.03455942, 0.00228226, 0.17335873); - xyz += R[7] * vec3(0.03722379, 0.00420733, 0.19620658); - xyz += R[8] * vec3(0.03241838, 0.00668880, 0.18608237); - xyz += R[9] * vec3(0.02123321, 0.00988840, 0.13995048); - xyz += R[10] * vec3(0.01049099, 0.01524945, 0.08917453); - xyz += R[11] * vec3(0.00329584, 0.02141831, 0.04789621); - xyz += R[12] * vec3(0.00050704, 0.03342293, 0.02814563); - xyz += R[13] * vec3(0.00094867, 0.05131001, 0.01613766); - xyz += R[14] * vec3(0.00627372, 0.07040208, 0.00775910); - xyz += R[15] * vec3(0.01686462, 0.08783871, 0.00429615); - xyz += R[16] * vec3(0.02868965, 0.09424905, 0.00200551); - xyz += R[17] * vec3(0.04267481, 0.09795667, 0.00086147); - xyz += R[18] * vec3(0.05625475, 0.09415219, 0.00036904); - xyz += R[19] * vec3(0.06947040, 0.08678102, 0.00019143); - xyz += R[20] * vec3(0.08305315, 0.07885653, 0.00014956); - xyz += R[21] * vec3(0.08612610, 0.06352670, 0.00009231); - xyz += R[22] * vec3(0.09046614, 0.05374142, 0.00006813); - xyz += R[23] * vec3(0.08500387, 0.04264606, 0.00002883); - xyz += R[24] * vec3(0.07090667, 0.03161735, 0.00001577); - xyz += R[25] * vec3(0.05062889, 0.02088521, 0.00000394); - xyz += R[26] * vec3(0.03547396, 0.01386011, 0.00000158); - xyz += R[27] * vec3(0.02146821, 0.00810264, 0.00000000); - xyz += R[28] * vec3(0.01251646, 0.00463010, 0.00000000); - xyz += R[29] * vec3(0.00680458, 0.00249138, 0.00000000); - xyz += R[30] * vec3(0.00346457, 0.00125930, 0.00000000); - xyz += R[31] * vec3(0.00149761, 0.00054165, 0.00000000); - xyz += R[32] * vec3(0.00076970, 0.00027795, 0.00000000); - xyz += R[33] * vec3(0.00040737, 0.00014711, 0.00000000); - xyz += R[34] * vec3(0.00016901, 0.00006103, 0.00000000); - xyz += R[35] * vec3(0.00009522, 0.00003439, 0.00000000); - xyz += R[36] * vec3(0.00004903, 0.00001771, 0.00000000); - xyz += R[37] * vec3(0.00002000, 0.00000722, 0.00000000); - - return xyz; -} - -float spectral_linear_to_concentration(float l1, float l2, float t) { - float t1 = l1 * pow(1.0 - t, 2.0); - float t2 = l2 * pow(t, 2.0); - - return t2 / (t1 + t2); -} - -vec3 spectral_mix(vec3 color1, vec3 color2, float t) { - // EJ: Assume provided arguments are linear rgb - vec3 lrgb1 = color1; - vec3 lrgb2 = color2; - - float R1[SPECTRAL_SIZE]; - float R2[SPECTRAL_SIZE]; - - spectral_linear_to_reflectance(lrgb1, R1); - spectral_linear_to_reflectance(lrgb2, R2); - - float l1 = spectral_reflectance_to_xyz(R1)[1]; - float l2 = spectral_reflectance_to_xyz(R2)[1]; - - t = spectral_linear_to_concentration(l1, l2, t); - - float R[SPECTRAL_SIZE]; - - for (int i = 0; i < SPECTRAL_SIZE; i++) { - float KS = (1.0 - t) * (pow(1.0 - R1[i], 2.0) / (2.0 * R1[i])) + t * (pow(1.0 - R2[i], 2.0) / (2.0 * R2[i])); - float KM = 1.0 + KS - sqrt(pow(KS, 2.0) + 2.0 * KS); - - //Saunderson correction - // let S = ((1.0 - K1) * (1.0 - K2) * KM) / (1.0 - K2 * KM); - - R[i] = KM; - } - - return spectral_xyz_to_srgb(spectral_reflectance_to_xyz(R)); -} - -vec4 spectral_mix(vec4 color1, vec4 color2, float t) { - return vec4(spectral_mix(color1.rgb, color2.rgb, t), mix(color1.a, color2.a, t)); -} - -#endif \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/subtract.frag b/orx-fx/src/shaders/glsl/blend/subtract.frag deleted file mode 100644 index d4f2189a..00000000 --- a/orx-fx/src/shaders/glsl/blend/subtract.frag +++ /dev/null @@ -1,39 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform bool clip; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - vec4 b = texture2D(tex1, v_texCoord0); - #endif - - vec3 na = a.a > 0.0 ? a.rgb/a.a : vec3(0.0); - vec3 nb = b.a > 0.0 ? b.rgb/b.a : vec3(0.0); - vec3 subColor = b.rgb; - vec4 result; - if (clip) { - result = vec4(max(na - subColor, vec3(0.0)), 1) * a.a; - } else { - result = (1.0-a.a) * b + a.a * b.a * vec4(max(na - nb, vec3(0.0)), 1.0) + (1.0-b.a) * a; - } - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blend/xor.frag b/orx-fx/src/shaders/glsl/blend/xor.frag deleted file mode 100644 index 7159d843..00000000 --- a/orx-fx/src/shaders/glsl/blend/xor.frag +++ /dev/null @@ -1,32 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 src = texture(tex0, v_texCoord0); - vec4 dest = texture(tex1, v_texCoord0); - #else - vec4 src = texture2D(tex0, v_texCoord0); - vec4 dest = texture2D(tex1, v_texCoord0); - #endif - - float lsrc = src.a * (1.0 - dest.a); - float ldest = dest.a * (1.0 - src.a); - - vec4 result = src * lsrc + dest * ldest; - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/approximate-gaussian-blur.frag b/orx-fx/src/shaders/glsl/blur/approximate-gaussian-blur.frag deleted file mode 100644 index d4b7562f..00000000 --- a/orx-fx/src/shaders/glsl/blur/approximate-gaussian-blur.frag +++ /dev/null @@ -1,36 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; -uniform vec2 blurDirection; - -uniform int window; -uniform float sigma; -uniform float spread; -uniform float gain; -uniform int wrapU; -uniform int wrapV; - -uniform int sourceLevel; - -out vec4 o_color; -void main() { - vec2 s = 1.0 / vec2(textureSize(tex0, sourceLevel).xy); - int w = window; - - vec4 sum = vec4(0.0); - float weight = 0.0; - for (int x = -w; x <= w; ++x) { - float lw = exp( float(-(x*x)) / (2.0 * sigma * sigma) ) ; - vec2 tc = v_texCoord0 + float(x) * blurDirection * s;// * spread; - - if (wrapU != 0) { - tc.x = mod(tc.x, 1.0); - } - if (wrapV != 0) { - tc.y = mod(tc.y, 1.0); - } - - sum += texture(tex0, tc) * lw; - weight += lw; - } - o_color = (sum / weight) * gain; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/bloom-combine.frag b/orx-fx/src/shaders/glsl/blur/bloom-combine.frag deleted file mode 100644 index 44e68223..00000000 --- a/orx-fx/src/shaders/glsl/blur/bloom-combine.frag +++ /dev/null @@ -1,14 +0,0 @@ -out vec4 o_output; -in vec2 v_texCoord0; - -uniform sampler2D tex0; -uniform sampler2D tex1; - -uniform float gain; -uniform float pregain; -uniform vec4 bias; - -void main() { - o_output = texture(tex0, v_texCoord0) * pregain + texture(tex1, v_texCoord0)*gain; - o_output.a = clamp(o_output.a, 0.0, 1.0); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/bloom-downscale.frag b/orx-fx/src/shaders/glsl/blur/bloom-downscale.frag deleted file mode 100644 index 42d3e15b..00000000 --- a/orx-fx/src/shaders/glsl/blur/bloom-downscale.frag +++ /dev/null @@ -1,19 +0,0 @@ -highp out vec4 o_output; -highp in vec2 v_texCoord0; -uniform highp sampler2D tex0; - - -// -- based on https://github.com/excess-demogroup/even-laster-engine/blob/a451a89f6bd6d3c6017d5890b92d9f72823bc742/src/shaders/bloom.fra -void main() -{ - float centerWeight = 0.16210282163712664; - vec2 diagonalOffsets = vec2(0.3842896354828526, 1.2048616327242379); - vec4 offsets = vec4(-diagonalOffsets.xy, +diagonalOffsets.xy) / vec2(textureSize(tex0, 0)).xyxy; - float diagonalWeight = 0.2085034734347498; - - o_output = texture(tex0, v_texCoord0) * centerWeight + - texture(tex0, v_texCoord0 + offsets.xy) * diagonalWeight + - texture(tex0, v_texCoord0 + offsets.wx) * diagonalWeight + - texture(tex0, v_texCoord0 + offsets.zw) * diagonalWeight + - texture(tex0, v_texCoord0 + offsets.yz) * diagonalWeight; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/bloom-upscale.frag b/orx-fx/src/shaders/glsl/blur/bloom-upscale.frag deleted file mode 100644 index 1a806d39..00000000 --- a/orx-fx/src/shaders/glsl/blur/bloom-upscale.frag +++ /dev/null @@ -1,80 +0,0 @@ -float nrand(vec2 n) { - return fract(sin(dot(n.xy, vec2(12.9898, 78.233))) * 43758.5453); -} - -// -- based on https://github.com/excess-demogroup/even-laster-engine/blob/a451a89f6bd6d3c6017d5890b92d9f72823bc742/src/shaders/bloom_upscale.frag -uniform float noiseSeed; -uniform float shape; -uniform float gain; -uniform float noiseGain; - -in vec2 v_texCoord0; -out vec4 o_output; - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform sampler2D tex2; -uniform sampler2D tex3; -uniform sampler2D tex4; -uniform sampler2D tex5; - -vec4 sampleBloom(vec2 pos, float shape) { - vec4 sum = vec4(0.0); - float total = 0.0; - - { - float weight = pow(0.0, shape); - vec2 rnd = vec2(nrand(3.0 + 0.0 + pos.xy + noiseSeed), - nrand(5.0 + 0.0 + pos.yx - noiseSeed)); - rnd = (rnd * 2.0 - 1.0) / vec2(textureSize(tex0, 0)); - sum += texture(tex0, pos + rnd * noiseGain) * weight; - total += weight; - } - { - float weight = pow(1.0, shape); - vec2 rnd = vec2(nrand(3.0 + 0.0 + pos.xy + noiseSeed), - nrand(5.0 + 0.0 + pos.yx - noiseSeed)); - rnd = (rnd * 2.0 - 1.0) / vec2(textureSize(tex0, 0)); - sum += texture(tex1, pos + rnd * noiseGain, 0.0) * weight; - total += weight; - } - { - float weight = pow(2.0, shape); - vec2 rnd = vec2(nrand(3.0 + 0.0 + pos.xy + noiseSeed), - nrand(5.0 + 0.0 + pos.yx - noiseSeed)); - rnd = (rnd * 2.0 - 1.0) / vec2(textureSize(tex0, 0)); - sum += texture(tex2, pos + rnd * noiseGain) * weight; - total += weight; - } - - { - float weight = pow(3.0, shape); - vec2 rnd = vec2(nrand(3.0 + 0.0 + pos.xy + noiseSeed), - nrand(5.0 + 0.0 + pos.yx - noiseSeed)); - rnd = (rnd * 3.0 - 1.0) / vec2(textureSize(tex0, 0)); - sum += texture(tex3, pos + rnd * noiseGain) * weight; - total += weight; - } - { - float weight = pow(4.0, shape); - vec2 rnd = vec2(nrand(3.0 + 0.0 + pos.xy + noiseSeed), - nrand(5.0 + 0.0 + pos.yx - noiseSeed)); - rnd = (rnd * 3.0 - 1.0) / vec2(textureSize(tex0, 0)); - sum += texture(tex4, pos + rnd * noiseGain) * weight; - total += weight; - } - { - float weight = pow(5.0, shape); - vec2 rnd = vec2(nrand(3.0 + 0.0 + pos.xy + noiseSeed), - nrand(5.0 + 0.0 + pos.yx - noiseSeed)); - rnd = (rnd * 3.0 - 1.0) / vec2(textureSize(tex0, 0)); - sum += texture(tex5, pos + rnd * noiseGain) * weight; - total += weight; - } - - return sum / total; -} - -void main() { - o_output = sampleBloom(v_texCoord0, shape) * gain; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/bloom.frag b/orx-fx/src/shaders/glsl/blur/bloom.frag deleted file mode 100644 index 607c0733..00000000 --- a/orx-fx/src/shaders/glsl/blur/bloom.frag +++ /dev/null @@ -1,18 +0,0 @@ -in vec2 v_texCoord0; -out vec4 o_color; - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform float blendFactor; -uniform float brightness; - -void main() { - vec3 original = texture(tex0, v_texCoord0).rgb; - vec3 bloom = texture(tex1, v_texCoord0).rgb; - - vec3 hdrColor = mix(original, bloom, blendFactor); - - vec3 result = vec3(1.0) - exp(-hdrColor * brightness); - - o_color = vec4(result, 1.0); -} diff --git a/orx-fx/src/shaders/glsl/blur/box-blur.frag b/orx-fx/src/shaders/glsl/blur/box-blur.frag deleted file mode 100644 index 83c48c98..00000000 --- a/orx-fx/src/shaders/glsl/blur/box-blur.frag +++ /dev/null @@ -1,69 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform vec2 textureSize0; -uniform vec2 blurDirection; - -uniform int window; -uniform float sigma; -uniform float gain; -uniform vec4 subtract; -uniform float spread; - -uniform bool wrapX; -uniform bool wrapY; - - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -vec2 wrap(vec2 uv) { - vec2 res = uv; - if (wrapX) { - res.x = mod(res.x, 1.0); - } - if (wrapY) { - res.y = mod(res.y, 1.0); - } - return res; - -} -void main() { - vec2 s = textureSize0; - s = vec2(1.0 / s.x, 1.0 / s.y); - - #ifndef OR_WEBGL1 - int w = window; - int WS = -window; - int WE = window; - #else - int w = 3; - #define WS -3 - #define WE 3 - #endif - - vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); - float weight = 0.0; - for (int x = WS; x<= WE; ++x) { - float lw = 1.0; - #ifndef OR_GL_TEXTURE2D - sum += texture(tex0, wrap(v_texCoord0 + float(x) * blurDirection * s * spread)); - #else - sum += texture2D(tex0, wrap(v_texCoord0 + float(x) * blurDirection * s * spread)); - #endif - - weight += lw; - } - - vec4 result = (sum / weight) * gain; - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/directional-blur.frag b/orx-fx/src/shaders/glsl/blur/directional-blur.frag deleted file mode 100644 index 447f8329..00000000 --- a/orx-fx/src/shaders/glsl/blur/directional-blur.frag +++ /dev/null @@ -1,46 +0,0 @@ -in vec2 v_texCoord0; - -uniform bool centerWindow; -uniform sampler2D tex0; // image -uniform sampler2D tex1; // blurDirection -uniform vec2 textureSize0; - -uniform int window; -uniform float gain; -uniform float spread; - -uniform bool wrapX; -uniform bool wrapY; -uniform bool perpendicular; - -out vec4 o_color; - -vec2 wrap(vec2 uv) { - vec2 res = uv; - if (wrapX) { res.x = fract(res.x); } - if (wrapY) { res.y = fract(res.y); } - return res; -} - -void main() { - vec2 s = textureSize0; - s = vec2(1.0 / s.x, 1.0 / s.y); - - vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); - vec2 blurDirection = texture(tex1, v_texCoord0).xy; - if (perpendicular) { - blurDirection = vec2(-blurDirection.y, blurDirection.x); - } - float weight = 0.0; - - int start = centerWindow ? -window / 2 : 0; - int end = centerWindow ? window / 2 + 1 : window; - - for (int x = start; x < end; ++x) { - sum += texture(tex0, wrap(v_texCoord0 + float(x) * blurDirection * s * spread)); - weight += 1.0; - } - - vec4 result = (sum / weight) * gain; - o_color = result; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/directional-hash-blur.frag b/orx-fx/src/shaders/glsl/blur/directional-hash-blur.frag deleted file mode 100644 index 8b8a143a..00000000 --- a/orx-fx/src/shaders/glsl/blur/directional-hash-blur.frag +++ /dev/null @@ -1,79 +0,0 @@ -// based on Hashed blur by David Hoskins. -// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. - -in vec2 v_texCoord0; - -layout(binding = 0) uniform sampler2D tex0; -layout(binding = 1) uniform sampler2D tex1; - -#ifdef RADIUS_FROM_TEXTURE -layout(binding = 2) uniform sampler2D tex2; -#endif - - -uniform vec2 textureSize0; -uniform float radius; -uniform float spread; - -uniform float time; -uniform int samples; -uniform float gain; - -out vec4 o_color; - -#define TAU 6.28318530718 - -//------------------------------------------------------------------------------------------- -#define HASHSCALE 443.8975 -vec2 hash22(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); - p3 += dot(p3, p3.yzx+19.19); - return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); -} - - -vec2 sampleOffset(inout vec2 r, vec2 direction) { - r = fract(r * vec2(33.3983, 43.4427)); - return (r.x+.001) * direction; -} - -vec2 sampleCircle(inout vec2 r) { - r = fract(r * vec2(33.3983, 43.4427)); - return sqrt(r.x+.001) * vec2(sin(r.y * TAU), cos(r.y * TAU))*.5; // <<=== circular sampling. -} - - -//------------------------------------------------------------------------------------------- -vec4 blur(vec2 uv, float r) { - float radius = r; - #ifdef RADIUS_FROM_TEXTURE - radius *= texture(tex2, uv).r; - #endif - vec2 direction = texture(tex1, uv).xy; - - vec2 line = vec2(spread) * (vec2(1.0) / textureSize0); - vec2 circle = vec2(radius) * (vec2(1.0) / textureSize0); - vec2 randomL = hash22(uv + vec2(time)); - vec2 randomC = hash22(uv + vec2(time)); - - vec4 acc = vec4(0.0); - - for (int i = 0; i < samples; i++) { - vec2 lineOffset = line * sampleOffset(randomL, direction); - vec2 circleOffset = circle * sampleCircle(randomC); - acc += textureLod(tex0, uv + circleOffset + lineOffset, 0 ); - } - return acc / float(samples); -} - -//------------------------------------------------------------------------------------------- -void main() { - vec2 uv = v_texCoord0; - float radiusSqr = pow(radius, 2.0); - - vec4 result = blur(uv, radiusSqr); - result.rgb *= gain; - - - o_color = result; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/frame-blur.frag b/orx-fx/src/shaders/glsl/blur/frame-blur.frag deleted file mode 100644 index 15d55795..00000000 --- a/orx-fx/src/shaders/glsl/blur/frame-blur.frag +++ /dev/null @@ -1,10 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; // input image -uniform sampler2D tex1; // accumulator image -uniform float blend; -out vec4 o_color; -void main() { - vec4 inputColor = texture(tex0, v_texCoord0); - vec4 accumulator = texture(tex1, v_texCoord0); - o_color = accumulator * (1.0 - blend) + inputColor * blend; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/gaussian-blur.frag b/orx-fx/src/shaders/glsl/blur/gaussian-blur.frag deleted file mode 100644 index 13cb3d0f..00000000 --- a/orx-fx/src/shaders/glsl/blur/gaussian-blur.frag +++ /dev/null @@ -1,25 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; - -uniform int window; -uniform float sigma; -uniform float spread; -uniform float gain; - -out vec4 o_color; -void main() { - vec2 s = vec2(textureSize(tex0, 0).xy); - s = vec2(1.0 / s.x, 1.0 / s.y); - int w = window; - - vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); - float weight = 0.0; - for (int y = -w; y <= w; ++y) { - for (int x = -w; x <= w; ++x) { - float lw = exp(-float(x * x + y * y) / (2.0 * sigma * sigma)); - sum += texture(tex0, v_texCoord0 + vec2(x, y) * s * 1.0) * lw; - weight += lw; - } - } - o_color = (sum / weight) * gain; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/hash-blur.frag b/orx-fx/src/shaders/glsl/blur/hash-blur.frag deleted file mode 100644 index 2f52eb88..00000000 --- a/orx-fx/src/shaders/glsl/blur/hash-blur.frag +++ /dev/null @@ -1,71 +0,0 @@ -// based on Hashed blur by David Hoskins. -// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. - -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform vec2 textureSize0; -uniform float radius; -uniform float time; -uniform int samples; -uniform float gain; -uniform bool dynamic; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -#define TAU 6.28318530718 - -//------------------------------------------------------------------------------------------- -#define HASHSCALE 443.8975 -vec2 hash22(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); - p3 += dot(p3, p3.yzx+19.19); - return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); -} - -vec2 sampleTexture(inout vec2 r) { - r = fract(r * vec2(33.3983, 43.4427)); - //return r-.5; - return sqrt(r.x+.001) * vec2(sin(r.y * TAU), cos(r.y * TAU))*.5; // <<=== circular sampling. -} - - -//------------------------------------------------------------------------------------------- -vec4 blur(vec2 uv, float radius) { - float r = radius; - if (dynamic) { - r *= texture(tex1, uv).r; - } - - vec2 circle = vec2(r) * (vec2(1.0) / textureSize0); - vec2 random = hash22(uv + vec2(time)); - - vec4 acc = vec4(0.0); - - for (int i = 0; i < samples; i++) { - acc += texture(tex0, uv + circle * sampleTexture(random)); - } - return acc / float(samples); -} - -//------------------------------------------------------------------------------------------- -void main() { - vec2 uv = v_texCoord0; - float radiusSqr = pow(radius, 2.0); - - vec4 result = blur(uv, radiusSqr); - result.rgb *= gain; - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/kawase-blur.frag b/orx-fx/src/shaders/glsl/blur/kawase-blur.frag deleted file mode 100644 index e88794b9..00000000 --- a/orx-fx/src/shaders/glsl/blur/kawase-blur.frag +++ /dev/null @@ -1,23 +0,0 @@ -out vec4 o_color; -in vec2 v_texCoord0; -uniform sampler2D tex0; -uniform int iteration; -uniform float spread; - -void main() { - ivec2 size = textureSize(tex0, 0); - vec2 pixelSize = vec2(1.0/float(size.x), 1.0/float(size.y)); - vec2 halfPixelSize = pixelSize / 2.0f; - vec2 d = (pixelSize.xy * vec2(iteration, iteration)) + halfPixelSize.xy; - d *= spread; - - vec4 dec = vec4(2.2); - vec4 enc = vec4(1.0/2.2); - - vec4 cOut = pow(texture(tex0, v_texCoord0+ vec2(-1, 1)*d), dec); - cOut += pow(texture(tex0, v_texCoord0 + vec2(1, 1)*d), dec); - cOut += pow(texture(tex0, v_texCoord0 + vec2(1, -1)*d), dec); - cOut += pow(texture(tex0, v_texCoord0+ vec2(-1, -1)*d), dec); - - o_color = pow(cOut/4.0, enc); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/laser-blur.frag b/orx-fx/src/shaders/glsl/blur/laser-blur.frag deleted file mode 100644 index 30e43a9c..00000000 --- a/orx-fx/src/shaders/glsl/blur/laser-blur.frag +++ /dev/null @@ -1,36 +0,0 @@ -out vec4 o_output; -uniform sampler2D tex0; -in vec2 v_texCoord0; -uniform float radius; -uniform float amp0; -uniform float amp1; -uniform vec2 center; -uniform float vignette; -uniform float vignetteSize; -uniform float aberration; - -void main() { - vec4 i0 = texture(tex0, v_texCoord0); - vec2 vt = (v_texCoord0 - vec2(0.5, 0.5) + center) * radius + vec2(0.5, 0.5) - center; - - vec2 size = vec2(textureSize(tex0, 0)); - vec2 l = (v_texCoord0 - vec2(0.5, 0.5) + center) * vec2(1.0, size.y/size.x); - float d = length(l); - - if (vt.x >= 0.0 && vt.y >= 0.0 && vt.x <= 1.0 && vt.y <= 1.0) { - vec4 i1r = texture(tex0, (v_texCoord0 - vec2(0.5, 0.5) + center) * (radius*(1.0 + aberration)) + vec2(0.5, 0.5) - center); - vec4 i1g = texture(tex0, (v_texCoord0 - vec2(0.5, 0.5) + center) * (radius*(1.0)) + vec2(0.5, 0.5) - center); - vec4 i1b = texture(tex0, (v_texCoord0 - vec2(0.5, 0.5) + center) * (radius*(1.0 - aberration)) + vec2(0.5, 0.5) - center); - - i1r.rgb = i1r.a > 0.0 ? i1r.rgb / i1r.a : vec3(0.0); - i1g.rgb = i1g.a > 0.0 ? i1g.rgb / i1g.a : vec3(0.0); - i1b.rgb = i1b.a > 0.0 ? i1b.rgb / i1b.a : vec3(0.0); - - vec4 i1 = vec4(i1r.r, i1g.g, i1b.b, 1.0) * (i1r.a + i1g.a + i1b.a) / 3.0; - o_output = i0 * amp0 + i1 * amp1; - } else { - o_output = i0 * 0.5; - } - - o_output.rgb *= mix(1.0, smoothstep(vignetteSize, 0.0, d), vignette); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/blur/zoom-blur.frag b/orx-fx/src/shaders/glsl/blur/zoom-blur.frag deleted file mode 100644 index 7230d4f3..00000000 --- a/orx-fx/src/shaders/glsl/blur/zoom-blur.frag +++ /dev/null @@ -1,39 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; // input -uniform vec2 center; -uniform float strength; -uniform vec2 dimensions; - -out vec4 o_color; - -float random(vec3 scale, float seed) { - /* use the fragment position for a different seed per-pixel */ - return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed); -} - -// Implementation by Evan Wallace (glfx.js) -void main() { - vec4 color = vec4(0.0); - float total = 0.0; - vec2 toCenter = center - v_texCoord0; - - /* randomize the lookup values to hide the fixed number of samples */ - float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); - - for (float t = 0.0; t <= 40.0; t++) { - float percent = (t + offset) / 40.0; - float weight = 4.0 * (percent - percent * percent); - vec4 tex = texture(tex0, v_texCoord0 + toCenter * percent * strength); - - /* switch to pre-multiplied alpha to correctly blur transparent images */ - tex.rgb *= tex.a; - - color += tex * weight; - total += weight; - } - - o_color = color / total; - - /* switch back from pre-multiplied alpha */ - o_color.rgb /= o_color.a + 0.00001; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/chromatic-aberration.frag b/orx-fx/src/shaders/glsl/color/chromatic-aberration.frag deleted file mode 100644 index 52d5490e..00000000 --- a/orx-fx/src/shaders/glsl/color/chromatic-aberration.frag +++ /dev/null @@ -1,21 +0,0 @@ -in vec2 v_texCoord0; - -uniform sampler2D tex0; - -uniform float aberrationFactor; -uniform vec2 dimensions; - -out vec4 o_color; - -void main() { - vec2 uv = v_texCoord0; - float factor = (1.0 / dimensions.x) * aberrationFactor; - - vec4 tex = texture(tex0, uv); - - float r = texture(tex0, vec2(uv.x - factor, uv.y)).r; - float g = tex.g; - float b = texture(tex0, vec2(uv.x + factor, uv.y)).b; - - o_color = vec4(vec3(r, g, b), tex.a); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/color-correction.frag b/orx-fx/src/shaders/glsl/color/color-correction.frag deleted file mode 100644 index 8c3a5ba1..00000000 --- a/orx-fx/src/shaders/glsl/color/color-correction.frag +++ /dev/null @@ -1,67 +0,0 @@ -/* based on "Brightness, contrast, saturation" by WojtaZam: https://www.shadertoy.com/view/XdcXzn */ -uniform float brightness; -uniform float saturation; -uniform float contrast; -uniform float hueShift; -uniform float gamma; -uniform float opacity; -uniform bool clamped; - -uniform sampler2D tex0; -in vec2 v_texCoord0; -out vec4 o_color; - -mat4 brightnessMatrix(float brightness) { - return mat4(1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - brightness, brightness, brightness, 1); -} - -mat4 contrastMatrix(float contrast) { - float t = (1.0 - contrast) / 2.0; - return mat4(contrast, 0, 0, 0, - 0, contrast, 0, 0, - 0, 0, contrast, 0, - t, t, t, 1 ); -} - -mat4 saturationMatrix(float saturation) { - vec3 luminance = vec3(0.3086, 0.6094, 0.0820); - float oneMinusSat = 1.0 - saturation; - vec3 red = vec3(luminance.x * oneMinusSat); - red += vec3(saturation, 0, 0); - - vec3 green = vec3(luminance.y * oneMinusSat); - green += vec3(0, saturation, 0); - - vec3 blue = vec3(luminance.z * oneMinusSat); - blue += vec3(0, 0, saturation); - - return mat4(red, 0, - green, 0, - blue, 0, - 0, 0, 0, 1 ); -} - -// from starea's https://www.shadertoy.com/view/MdjBRy, which in turn remixed it from mAlk's https://www.shadertoy.com/view/MsjXRt -vec3 shiftHue(in vec3 col, in float Shift) { - vec3 P = vec3(0.55735) * dot(vec3(0.55735), col); - vec3 U = col - P; - vec3 V = cross(vec3(0.55735), U); - col = U * cos(Shift * 6.2832) + V * sin(Shift * 6.2832) + P; - return col; -} - -void main() { - vec4 color = texture(tex0, v_texCoord0); - vec4 nc = (color.a == 0.0) ? vec4(0.0) : vec4(color.rgb / color.a, color.a); - nc.rgb = pow(nc.rgb, vec3(gamma)); - nc.rgb = shiftHue(nc.rgb, (hueShift/360.0)); - vec4 cc = brightnessMatrix(brightness) * contrastMatrix((contrast + 1.0)) * saturationMatrix(saturation + 1.0) * nc; - if(clamped) { - o_color = clamp(vec4(cc.rgb, 1.0) * color.a * opacity, 0.0, 1.0); - } else { - o_color = vec4(cc.rgb, 1.0) * color.a * opacity; - } -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/color-lookup.frag b/orx-fx/src/shaders/glsl/color/color-lookup.frag deleted file mode 100644 index 7e57abf3..00000000 --- a/orx-fx/src/shaders/glsl/color/color-lookup.frag +++ /dev/null @@ -1,40 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; -uniform sampler2D lookup; -uniform float seed; -uniform float noiseGain; - - -out vec4 o_color; - -#define HASHSCALE 443.8975 -vec2 hash22(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); - p3 += dot(p3, p3.yzx+19.19); - return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); -} - -// -- from https://github.com/jeromeetienne/threex.coloradjust/blob/master/threex.coloradjust.js -vec4 sampleAs3DTexture(sampler2D lut, vec3 uv, float width) { - float sliceSize = 1.0 / width; // space of 1 slice - float slicePixelSize = sliceSize / width; // space of 1 pixel - float sliceInnerSize = slicePixelSize * (width - 1.0); // space of width pixels - float zSlice0 = min(floor(uv.z * width), width - 1.0); - float zSlice1 = min(zSlice0 + 1.0, width - 1.0); - float xOffset = slicePixelSize * 0.5 + uv.x * sliceInnerSize; - float s0 = xOffset + (zSlice0 * sliceSize); - float s1 = xOffset + (zSlice1 * sliceSize); - vec4 slice0Color = texture(lut, vec2(s0, 1.0-uv.y)); - vec4 slice1Color = texture(lut, vec2(s1, 1.0-uv.y)); - float zOffset = mod(uv.z * width, 1.0); - vec4 result = mix(slice0Color, slice1Color, zOffset); - return result; -} - -void main() { - vec4 color = texture(tex0, v_texCoord0); - vec3 noise = vec3(hash22(v_texCoord0-vec2(seed)), hash22(-v_texCoord0+vec2(seed)).x); - vec3 graded = sampleAs3DTexture(lookup, min(vec3(1.0), max(vec3(0.0),color.rgb + noise * noiseGain)), 16.0).rgb; - o_color.rgb = min(vec3(1.0), max(vec3(0.0), graded)); - o_color.a = color.a; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/color-mix.frag b/orx-fx/src/shaders/glsl/color/color-mix.frag deleted file mode 100644 index 9c1edbf9..00000000 --- a/orx-fx/src/shaders/glsl/color/color-mix.frag +++ /dev/null @@ -1,18 +0,0 @@ -uniform float[25] colorMatrix; - -vec4 colorTransform(vec4 color, float[25] matrix) { - float r = color.r * matrix[0] + color.g * matrix[5] + color.b * matrix[10] + color.a * matrix[15] + matrix[20]; - float g = color.r * matrix[1] + color.g * matrix[6] + color.b * matrix[11] + color.a * matrix[16] + matrix[21]; - float b = color.r * matrix[2] + color.g * matrix[7] + color.b * matrix[12] + color.a * matrix[17] + matrix[22]; - float a = color.r * matrix[3] + color.g * matrix[8] + color.b * matrix[13] + color.a * matrix[18] + matrix[23]; - return vec4(r, g, b, a); -} - -in vec2 v_texCoord0; -uniform sampler2D tex0; - -out vec4 o_color; -void main() { - vec4 c = texture(tex0, v_texCoord0); - o_color = colorTransform(c, colorMatrix); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/color-tint.frag b/orx-fx/src/shaders/glsl/color/color-tint.frag deleted file mode 100644 index b7377985..00000000 --- a/orx-fx/src/shaders/glsl/color/color-tint.frag +++ /dev/null @@ -1,9 +0,0 @@ -uniform vec4 tint; -in vec2 v_texCoord0; -uniform sampler2D tex0; - -out vec4 o_color; -void main() { - vec4 c = texture(tex0, v_texCoord0); - o_color = vec4(c.rgb * tint.rgb, c.a) * tint.a; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/duotone-gradient.frag b/orx-fx/src/shaders/glsl/color/duotone-gradient.frag deleted file mode 100644 index 23c6a356..00000000 --- a/orx-fx/src/shaders/glsl/color/duotone-gradient.frag +++ /dev/null @@ -1,64 +0,0 @@ -#pragma import color.oklab_to_linear_rgb -#pragma import color.linear_rgb_to_oklab -#pragma import color.linear_rgb_to_srgb -#pragma import color.srgb_to_linear_rgb - -uniform vec4 tint; -in vec2 v_texCoord0; -uniform sampler2D tex0; -uniform vec4 backgroundColor0; -uniform vec4 foregroundColor0; - -uniform vec4 backgroundColor1; -uniform vec4 foregroundColor1; - -uniform bool labInterpolation; -uniform float rotation; -out vec4 o_color; -void main() { - vec4 c = texture(tex0, v_texCoord0); - if (c.a != 0.0) { - c.rgb /= c.a; - } - float ca = cos(radians(rotation)); - float sa = sin(radians(rotation)); - mat2 rm = mat2(vec2(ca, sa), vec2(-sa, ca)); - - float f = (rm * (v_texCoord0 - vec2(0.5)) + vec2(0.5)).x; - - vec4 bg0 = backgroundColor0; - bg0.rgb *= backgroundColor0.a; - vec4 fg0 = foregroundColor0; - fg0.rgb *= foregroundColor0.a; - - vec4 bg1 = backgroundColor1; - bg1.rgb *= backgroundColor1.a; - vec4 fg1 = foregroundColor1; - fg1.rgb *= foregroundColor1.a; - - - if (!labInterpolation) { - vec4 bg = mix(bg0, bg1, f); - vec4 fg = mix(fg0, fg1, f); - - o_color = mix(bg, fg, c.r) * c.a; - } else { - bg0 = srgb_to_linear_rgb(bg0); - bg0 = linear_rgb_to_oklab(bg0); - fg0 = srgb_to_linear_rgb(fg0); - fg0 = linear_rgb_to_oklab(fg0); - bg1 = srgb_to_linear_rgb(bg1); - bg1 = linear_rgb_to_oklab(bg1); - fg1 = srgb_to_linear_rgb(fg1); - fg1 = linear_rgb_to_oklab(fg1); - - vec4 bg = mix(bg0, bg1, f); - vec4 fg = mix(fg0, fg1, f); - - vec4 m = mix(bg, fg, c.r); - m = oklab_to_linear_rgb(m); - m *= c.a; - m = linear_rgb_to_srgb(m); - o_color = m; - } -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/duotone.frag b/orx-fx/src/shaders/glsl/color/duotone.frag deleted file mode 100644 index e80b1b37..00000000 --- a/orx-fx/src/shaders/glsl/color/duotone.frag +++ /dev/null @@ -1,35 +0,0 @@ -#pragma import color.oklab_to_linear_rgb -#pragma import color.linear_rgb_to_oklab -#pragma import color.linear_rgb_to_srgb -#pragma import color.srgb_to_linear_rgb - -uniform vec4 tint; -in vec2 v_texCoord0; -uniform sampler2D tex0; -uniform vec4 backgroundColor; -uniform vec4 foregroundColor; -uniform bool labInterpolation; -out vec4 o_color; -void main() { - vec4 c = texture(tex0, v_texCoord0); - if (c.a != 0.0) { - c.rgb /= c.a; - } - - vec4 bg = backgroundColor; - bg.rgb *= backgroundColor.a; - vec4 fg = foregroundColor; - fg.rgb *= foregroundColor.a; - - if (!labInterpolation) { - o_color = mix(bg, fg, c.r) * c.a; - } else { - bg = linear_rgb_to_oklab(bg); - fg = linear_rgb_to_oklab(fg); - - vec4 m = mix(bg, fg, c.r); - m = oklab_to_linear_rgb(m); - m *= c.a; - o_color = m; - } -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/invert.frag b/orx-fx/src/shaders/glsl/color/invert.frag deleted file mode 100644 index 414cd401..00000000 --- a/orx-fx/src/shaders/glsl/color/invert.frag +++ /dev/null @@ -1,14 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; // input -uniform float amount; -out vec4 o_color; - -void main() { - vec4 color = texture(tex0, v_texCoord0); - - float a = color.a; - vec3 rgb = a > 0.0 ? color.rgb / a : vec3(0.0); - rgb = mix(rgb, 1.0 - rgb, amount); - - o_color = vec4(rgb * a, a); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/luma-map.frag b/orx-fx/src/shaders/glsl/color/luma-map.frag deleted file mode 100644 index 64c63ed2..00000000 --- a/orx-fx/src/shaders/glsl/color/luma-map.frag +++ /dev/null @@ -1,15 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; // input -uniform vec4 foreground; -uniform vec4 background; -uniform float foregroundOpacity; -uniform float backgroundOpacity; - -out vec4 o_color; -void main() { - vec4 c = texture(tex0, v_texCoord0); - vec4 fgc = foreground * foregroundOpacity; - vec4 bgc = background * backgroundOpacity; - float luma = dot( (c.a> 0.0? c.rgb/c.a : vec3(0.0)), vec3(1.0/3.0)); - o_color = mix(bgc, fgc, luma) * c.a; -} diff --git a/orx-fx/src/shaders/glsl/color/luma-opacity.frag b/orx-fx/src/shaders/glsl/color/luma-opacity.frag deleted file mode 100644 index 2cbb16b0..00000000 --- a/orx-fx/src/shaders/glsl/color/luma-opacity.frag +++ /dev/null @@ -1,15 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; // input -uniform float foregroundLuma; -uniform float backgroundLuma; -uniform float foregroundOpacity; -uniform float backgroundOpacity; - -out vec4 o_color; -void main() { - vec4 c = texture(tex0, v_texCoord0); - float l = dot( (c.a> 0.0? c.rgb/c.a : vec3(0.0)), vec3(1.0/3.0)); - float mf = smoothstep(backgroundLuma, foregroundLuma, l); - float o = mix(backgroundOpacity, foregroundOpacity, mf); - o_color = c * o; -} diff --git a/orx-fx/src/shaders/glsl/color/luma-threshold.frag b/orx-fx/src/shaders/glsl/color/luma-threshold.frag deleted file mode 100644 index 557c803a..00000000 --- a/orx-fx/src/shaders/glsl/color/luma-threshold.frag +++ /dev/null @@ -1,16 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; // input -uniform vec4 foreground; -uniform vec4 background; -uniform float foregroundOpacity; -uniform float backgroundOpacity; -uniform float threshold; - -out vec4 o_color; -void main() { - vec4 c = texture(tex0, v_texCoord0); - vec4 fgc = foreground * foregroundOpacity; - vec4 bgc = background * backgroundOpacity; - float luma = dot(c.rgb, vec3(1.0/3.0)); - o_color = mix(bgc, fgc, step(threshold, luma )); -} diff --git a/orx-fx/src/shaders/glsl/color/oklab-to-rgb.frag b/orx-fx/src/shaders/glsl/color/oklab-to-rgb.frag deleted file mode 100644 index 501e03f6..00000000 --- a/orx-fx/src/shaders/glsl/color/oklab-to-rgb.frag +++ /dev/null @@ -1,12 +0,0 @@ -#pragma import color.oklab_to_linear_rgb -#pragma import color.linear_rgb_to_srgb -out vec4 o_output; - -in vec2 v_texCoord0; -uniform sampler2D tex0; - -void main() { - vec4 lab = texture(tex0, v_texCoord0); - vec4 rgba = oklab_to_linear_rgb(lab); - o_output = linear_rgb_to_srgb(rgba); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/pal.frag b/orx-fx/src/shaders/glsl/color/pal.frag deleted file mode 100644 index 6d24f0c2..00000000 --- a/orx-fx/src/shaders/glsl/color/pal.frag +++ /dev/null @@ -1,121 +0,0 @@ -// based on https://github.com/svofski/CRT - -in vec2 v_texCoord0; -uniform sampler2D tex0; // input -uniform float amount; -uniform float pixelation; -out vec4 o_color; - -// Implementation by Evan Wallace (glfx.js) - -uniform float filter_gain; // 1.0 is kind of normal -uniform float filter_invgain; // 1.6 is normal - -#define PI 3.14159265358 -#define FSC 4433618.75 -#define FLINE 15625 -#define VISIBLELINES 312 - -#define RGB_to_YIQ mat3x3( 0.299 , 0.595716 , 0.211456 , 0.587 , -0.274453 , -0.522591 , 0.114 , -0.321263 , 0.311135 ) -#define YIQ_to_RGB mat3x3( 1.0 , 1.0 , 1.0 , 0.9563 , -0.2721 , -1.1070 , 0.6210 , -0.6474 , 1.7046 ) - -#define RGB_to_YUV mat3x3( 0.299 , -0.14713 , 0.615 , 0.587 , -0.28886 , -0.514991 , 0.114 , 0.436 , -0.10001 ) -#define YUV_to_RGB mat3x3( 1.0 , 1.0 , 1.0 , 0.0 , -0.39465 , 2.03211 , 1.13983 , -0.58060 , 0.0 ) - -#define fetch(ofs,center,invx) texture(tex0, vec2((ofs) * (invx) + center.x, center.y)) - -#define FIRTAPS 20 -const float FIR[FIRTAPS] = float[FIRTAPS] (-0.008030271,0.003107906,0.016841352,0.032545161,0.049360136,0.066256720,0.082120150,0.095848433,0.106453014,0.113151423,0.115441842,0.113151423,0.106453014,0.095848433,0.082120150,0.066256720,0.049360136,0.032545161,0.016841352,0.003107906); - -//#define FIR_GAIN 2.0 -//#define FIR_INVGAIN 1.02 -#define FIR_GAIN filter_gain -#define FIR_INVGAIN filter_invgain - -float width_ratio; -float height_ratio; -float altv; -float invx; - - -float modulated(vec2 xy, float sinwt, float coswt) { - vec3 rgb = fetch(0.0, xy, invx).xyz; - vec3 yuv = RGB_to_YUV * rgb; - - // scanline modulation hack - // yuv.x *= 0.8 + 0.2 * sin(xy.y*2.0*3.1415*200.0); - - return clamp(yuv.x + yuv.y * sinwt + yuv.z * coswt, 0.0, 1.0); -} - -vec2 modem_uv(vec2 xy, int ofs) { - float t = (xy.x + float(ofs) * invx) * float(textureSize(tex0, 0).x); - float wt = t * 2.0 * PI / width_ratio; - - float sinwt = sin(wt); - float coswt = cos(wt + altv); - - vec3 rgb = fetch(float(ofs), xy, invx).xyz; - vec3 yuv = RGB_to_YUV * rgb; - float signal = clamp(yuv.x + yuv.y * sinwt + yuv.z * coswt, 0.0, 1.0); - - return vec2(signal * sinwt, signal * coswt); -} - - -vec3 shadow_mask(vec2 pos){ - const mat2 rot = mat2(0.707,0.707,-0.707,0.707); - vec3 offset = vec3( 0. , 1./3. , 2./3. ); - vec2 spos = pos * rot * vec2(200.0); - vec3 ret = vec3(1); - ret.r = length( fract( spos + vec2(offset.r) ) -.5); - ret.g = length( fract( spos + vec2(offset.g) ) -.5); - ret.b = length( fract( spos + vec2(offset.b) ) -.5); - return clamp( 1.5-ret*2.5 , 0.0, 1.0 ); -} - -// -//void mainmaskImage(out vec4 fragColor, in vec2 fragCoord ){ -// vec2 xy = fragCoord.st / iResolution.xy; -// -// fragColor.rgb = shadow_mask( fragCoord.st/ iResolution.y ) * texture(iChannel0, xy).rgb; -// -// -// if ( fragCoord.y > iResolution.y*.5 ) { -// fragColor = texture(iChannel0, xy); -// } -//} - - -void main() { - // vec2 xy = fragCoord.st / iResolution.xy; - vec2 xy = v_texCoord0; - width_ratio = float(textureSize(tex0, 0).x) / (float(FSC) / float(FLINE)); - height_ratio = float(textureSize(tex0, 0).y) / float(VISIBLELINES); - altv = mod(floor(xy.y * float(VISIBLELINES) + 0.5), 2.0) * PI; - invx = 0.25 / (float(FSC)/float(FLINE)); // equals 4 samples per Fsc period - - // lowpass U/V at baseband - vec2 filtered = vec2(0.0, 0.0); - for (int i = 0; i < FIRTAPS; i++) { - vec2 uv = modem_uv(xy, i - FIRTAPS/2); - filtered += FIR_GAIN * uv * FIR[i]; - } - - float t = xy.x * float(textureSize(tex0, 0).x); - float wt = t * 2.0 * PI / width_ratio; - - float sinwt = sin(wt); - float coswt = cos(wt + altv); - - float luma = modulated(xy, sinwt, coswt) - FIR_INVGAIN * (filtered.x * sinwt + filtered.y * coswt); - vec3 yuv_result = vec3(luma, filtered.x, filtered.y); - - vec3 rgbmask = shadow_mask( xy * vec2(1.0, float(textureSize(tex0,0).x) / float(textureSize(tex0,0).y)) ); // needs anisotropy like: fragCoord.st/ iResolution.y ); - rgbmask = vec3(1.0,1.0,1.0) * (1.0-pixelation) + rgbmask * pixelation; - o_color = texture(tex0,xy) * (1.0-amount) + amount * vec4(rgbmask * ( YUV_to_RGB * yuv_result ), 1.0); - -// if (xy.y>0.5) { -// o_color = texture(tex0, xy); -// } -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/posterize.frag b/orx-fx/src/shaders/glsl/color/posterize.frag deleted file mode 100644 index 40f1ba58..00000000 --- a/orx-fx/src/shaders/glsl/color/posterize.frag +++ /dev/null @@ -1,24 +0,0 @@ -in vec2 v_texCoord0; -uniform int window; -uniform sampler2D tex0; -uniform int levels; -out vec4 o_output; -void main() { - vec4 c = texture(tex0, v_texCoord0); - vec2 step = 1.0 / vec2(textureSize(tex0, 0)); - float w = 0.0; - vec3 s = vec3(0.0); - for (int v = -window; v <= window; ++v) { - for (int u = -window; u <= window; ++u) { - vec4 c = texture(tex0, v_texCoord0 + (step/(2.0*float(window))) * vec2(u,v) ); - if (c.a != 0.0) { - c.rgb /= c.a; - } - vec3 q = min(floor(c.rgb * float(levels))/float(levels-1), vec3(1.0)); - s += q; - w += 1.0; - } - } - vec3 q = s / w; - o_output = vec4(q * c.a, c.a); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/rgb-to-oklab.frag b/orx-fx/src/shaders/glsl/color/rgb-to-oklab.frag deleted file mode 100644 index 41192b3f..00000000 --- a/orx-fx/src/shaders/glsl/color/rgb-to-oklab.frag +++ /dev/null @@ -1,12 +0,0 @@ -#pragma import color.linear_rgb_to_oklab -#pragma import color.srgb_to_linear_rgb -out vec4 o_output; - -in vec2 v_texCoord0; -uniform sampler2D tex0; - -void main() { - vec4 srgba = texture(tex0, v_texCoord0); - vec4 rgba = srgb_to_linear_rgb(srgba); - o_output = linear_rgb_to_oklab(rgba); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/rgb-to-ycbcr.frag b/orx-fx/src/shaders/glsl/color/rgb-to-ycbcr.frag deleted file mode 100644 index 546fe9bf..00000000 --- a/orx-fx/src/shaders/glsl/color/rgb-to-ycbcr.frag +++ /dev/null @@ -1,16 +0,0 @@ -uniform vec4 tint; -in vec2 v_texCoord0; -uniform sampler2D tex0; - -out vec4 o_color; -void main() { - vec4 c = texture(tex0, v_texCoord0); - if (c.a != 0.0) { - c.rgb /= c.a; - } - c.rgb *= 255.0; - float y = 0.0 + 0.299 * c.r + 0.587 * c.g + 0.114 * c.b; - float cb = 128.0 - (0.168736 * c.r) - (0.331264 * c.g) + (0.5 * c.b); - float cr = 128.0 + (0.5 * c.r) - 0.418688 * c.g - 0.081312 * c.b; - o_color = vec4(y/255.0, cb/255.0, cr/255.0, 1.0) * c.a; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/sepia.frag b/orx-fx/src/shaders/glsl/color/sepia.frag deleted file mode 100644 index 0149c8fb..00000000 --- a/orx-fx/src/shaders/glsl/color/sepia.frag +++ /dev/null @@ -1,18 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; // input -uniform float amount; -out vec4 o_color; - -// Implementation by Evan Wallace (glfx.js) -void main() { - vec4 color = texture(tex0, v_texCoord0); - float r = color.r; - float g = color.g; - float b = color.b; - - color.r = min(1.0, (r * (1.0 - (0.607 * amount))) + (g * (0.769 * amount)) + (b * (0.189 * amount))); - color.g = min(1.0, (r * 0.349 * amount) + (g * (1.0 - (0.314 * amount))) + (b * 0.168 * amount)); - color.b = min(1.0, (r * 0.272 * amount) + (g * 0.534 * amount) + (b * (1.0 - (0.869 * amount)))); - - o_color = color; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/set-background.frag b/orx-fx/src/shaders/glsl/color/set-background.frag deleted file mode 100644 index f00e7f85..00000000 --- a/orx-fx/src/shaders/glsl/color/set-background.frag +++ /dev/null @@ -1,10 +0,0 @@ -uniform vec4 background; -uniform float backgroundOpacity; -in vec2 v_texCoord0; -uniform sampler2D tex0; - -out vec4 o_color; -void main() { - vec4 c = texture(tex0, v_texCoord0); - o_color = c + (1.0 - c.a) * background * backgroundOpacity; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/color/subtract-constant.frag b/orx-fx/src/shaders/glsl/color/subtract-constant.frag deleted file mode 100644 index 3c865a43..00000000 --- a/orx-fx/src/shaders/glsl/color/subtract-constant.frag +++ /dev/null @@ -1,9 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; -uniform vec4 constant; - -out vec4 o_color; -void main() { - vec4 c = texture(tex0, v_texCoord0); - o_color = max(vec4(0.0), c - constant); -} diff --git a/orx-fx/src/shaders/glsl/color/ycbcr-to-rgb.frag b/orx-fx/src/shaders/glsl/color/ycbcr-to-rgb.frag deleted file mode 100644 index 28cabf82..00000000 --- a/orx-fx/src/shaders/glsl/color/ycbcr-to-rgb.frag +++ /dev/null @@ -1,24 +0,0 @@ -uniform vec4 tint; -in vec2 v_texCoord0; -uniform sampler2D tex0; - -out vec4 o_color; -void main() { - vec2 ts = vec2(textureSize(tex0, 0)); - vec4 c = texture(tex0, v_texCoord0); - - if (c.a != 0.0) { - c.rgb /= c.a; - } - c.rgb *= 255.0; - - float y = c.r; - float cb = c.g; - float cr = c.b; - - float r = y + 1.402 * (cr - 128.0); - float g = y - 0.344136 * (cb - 128.0) - 0.714136 * (cr - 128.0); - float b = y + 1.772 * (cb - 128.0); - - o_color = vec4(r/255.0, g/255.0, b/255.0, 1.0) * c.a; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/colormap/grayscale-colormap.frag b/orx-fx/src/shaders/glsl/colormap/grayscale-colormap.frag deleted file mode 100644 index 9bd502d1..00000000 --- a/orx-fx/src/shaders/glsl/colormap/grayscale-colormap.frag +++ /dev/null @@ -1,31 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform float minValue; -uniform float maxValue; -uniform float curve; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - float red = texture(tex0, v_texCoord0).r; - #else - float red = texture2D(tex0, v_texCoord0).r; - #endif - float value = (red - minValue) / (maxValue - minValue); - vec3 color = vec3(pow(value, curve)); - color *= step(value, 1.) * step(0., value); - vec4 result = vec4(color, 1.); - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} diff --git a/orx-fx/src/shaders/glsl/colormap/spectral-zucconi-colormap.frag b/orx-fx/src/shaders/glsl/colormap/spectral-zucconi-colormap.frag deleted file mode 100644 index 5d562f01..00000000 --- a/orx-fx/src/shaders/glsl/colormap/spectral-zucconi-colormap.frag +++ /dev/null @@ -1,33 +0,0 @@ -#pragma import colormap.spectral_zucconi6 - -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; // kinect raw -uniform float minValue; -uniform float maxValue; -uniform float curve; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - float red = texture(tex0, v_texCoord0).r; - #else - float red = texture2D(tex0, v_texCoord0).r; - #endif - float value = (red - minValue) / (maxValue - minValue); - vec3 color = spectral_zucconi6(pow(value, curve)); - color *= step(value, 1.) * step(0., value); - vec4 result = vec4(color, 1.); - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} diff --git a/orx-fx/src/shaders/glsl/colormap/turbo-colormap.frag b/orx-fx/src/shaders/glsl/colormap/turbo-colormap.frag deleted file mode 100644 index 0d2c8c7e..00000000 --- a/orx-fx/src/shaders/glsl/colormap/turbo-colormap.frag +++ /dev/null @@ -1,33 +0,0 @@ -#pragma import colormap.turbo_colormap - -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform float minValue; -uniform float maxValue; -uniform float curve; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - float red = texture(tex0, v_texCoord0).r; - #else - float red = texture2D(tex0, v_texCoord0).r; - #endif - float value = (red - minValue) / (maxValue - minValue); - vec3 color = turbo_colormap(pow(value, curve)); - color *= step(value, 1.) * step(0., value); - vec4 result = vec4(color, 1.); - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} diff --git a/orx-fx/src/shaders/glsl/distort/block-repeat.frag b/orx-fx/src/shaders/glsl/distort/block-repeat.frag deleted file mode 100644 index 27fb27bf..00000000 --- a/orx-fx/src/shaders/glsl/distort/block-repeat.frag +++ /dev/null @@ -1,41 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0;// input -uniform float blockWidth; -uniform float blockHeight; -uniform float blockOffsetX; -uniform float blockOffsetY; -uniform float sourceOffsetX; -uniform float sourceOffsetY; -uniform float sourceScale; - -out vec4 o_color; -void main() { - vec2 uv = v_texCoord0; - vec2 blockSize = vec2(blockWidth, blockHeight); - vec2 blockOffset = vec2(blockOffsetX, blockOffsetY); - vec2 blockCoord = uv / blockSize + blockOffset; - - ivec2 blockIndex = ivec2(blockCoord); - vec2 blockUV = mod(blockCoord - vec2(blockIndex), vec2(1.0)); - vec2 blockAspect = vec2(1.0); - - - if (blockWidth < blockHeight) { - blockAspect = vec2(blockWidth / blockHeight, 1.0); - } - - if (blockHeight < blockWidth) { - blockAspect = vec2(1.0, blockHeight/blockWidth); - } - - vec2 tUV = mix(blockUV * blockSize, blockUV * blockAspect, sourceScale); - -// vec2 fw = fwidth(blockCoord); -// float f = smoothstep(0.0, 0.01, blockUV.x) * smoothstep(0.0, 0.01, blockUV.y); - - vec2 sourceOffset = vec2(sourceOffsetX, sourceOffsetY); - vec2 gx = dFdx(tUV); - vec2 gy = dFdy(tUV); - vec4 c = textureGrad(tex0, mod(tUV + sourceOffset, vec2(1.0)), gx, gy ); - o_color = c; -} diff --git a/orx-fx/src/shaders/glsl/distort/directional-displace.frag b/orx-fx/src/shaders/glsl/distort/directional-displace.frag deleted file mode 100644 index 4b4757f0..00000000 --- a/orx-fx/src/shaders/glsl/distort/directional-displace.frag +++ /dev/null @@ -1,37 +0,0 @@ -in vec2 v_texCoord0; - -uniform sampler2D tex0; // image -uniform sampler2D tex1; // displaceDirection -uniform vec2 textureSize0; - -uniform float gain; -uniform float distance; - -uniform bool wrapX; -uniform bool wrapY; -uniform bool perpendicular; - -out vec4 o_color; - -vec2 wrap(vec2 uv) { - vec2 res = uv; - if (wrapX) { res.x = fract(res.x); } - if (wrapY) { res.y = fract(res.y); } - return res; -} - -void main() { - vec2 s = textureSize0; - s = vec2(1.0 / s.x, 1.0 / s.y); - - vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); - vec2 blurDirection = texture(tex1, v_texCoord0).xy; - if (perpendicular) { - blurDirection = vec2(-blurDirection.y, blurDirection.x); - } - - vec4 result = texture(tex0, wrap(v_texCoord0 + blurDirection * s * distance)) - * gain; - - o_color = result; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/distort/displace-blend.frag b/orx-fx/src/shaders/glsl/distort/displace-blend.frag deleted file mode 100644 index 2990a9be..00000000 --- a/orx-fx/src/shaders/glsl/distort/displace-blend.frag +++ /dev/null @@ -1,42 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0;// input -uniform sampler2D tex1;// input -uniform float offset; -uniform float gain; -uniform vec2 targetSize; -uniform float rotation; -uniform float feather; -uniform float sourceOpacity; -uniform float targetOpacity; -out vec4 o_color; -void main() { - - - float phi = radians(rotation); - float cp = cos(phi); - float sp = sin(phi); - mat2 rm = mat2(vec2(cp,sp), vec2(-sp,cp)); - - vec4 oa = texture(tex0, v_texCoord0); - vec4 b = texture(tex1, v_texCoord0); - - float ar = targetSize.y / targetSize.x; - - vec4 nb = b.a > 0.0? b/b.a : vec4(0.0); - - vec2 offset = (nb.rg - vec2(offset))*vec2(gain) * nb.a; - offset = rm * offset * vec2(1.0, ar); - - - vec2 step = fwidth(v_texCoord0) * feather; - - vec2 displaced = v_texCoord0 + offset; - - float fx = smoothstep(0.0, step.x, displaced.x) * smoothstep(1.0, 1.0-step.x, displaced.x); - float fy = smoothstep(0.0, step.y, displaced.y) * smoothstep(1.0, 1.0-step.y, displaced.y); - - vec4 a = texture(tex0, displaced) * mix(1.0, fx * fy, b.a); - - o_color = (a + (1.0-a.a) * oa * sourceOpacity) * b.a * targetOpacity + (1.0-b.a*targetOpacity) * oa * sourceOpacity; - -} diff --git a/orx-fx/src/shaders/glsl/distort/fisheye.frag b/orx-fx/src/shaders/glsl/distort/fisheye.frag deleted file mode 100644 index 7faca0b5..00000000 --- a/orx-fx/src/shaders/glsl/distort/fisheye.frag +++ /dev/null @@ -1,41 +0,0 @@ -uniform sampler2D tex0; -uniform float strength; -uniform float feather; -uniform float scale; -uniform float rotation; -in vec2 v_texCoord0; -out vec4 o_color; - -void main() { - vec2 uv = v_texCoord0; - vec2 ts = vec2(textureSize(tex0, 0)); - vec2 step = 1.0 / ts; - - float phi = radians(rotation); - float cp = cos(phi); - float sp = sin(phi); - mat2 rm = mat2(vec2(cp, sp), vec2(-sp, cp)); - - float aspectRatio = ts.y / ts.x; - step.y /= aspectRatio; - step *= feather; - - vec2 intensity = vec2(strength, strength); - - vec2 coords = uv; - coords = (coords - 0.5) * 2.0; - - coords = rm * coords; - - vec2 realCoordOffs; - realCoordOffs.x = (1.0 - coords.y * coords.y) * intensity.y * (coords.x); - realCoordOffs.y = (1.0 - coords.x * coords.x) * intensity.x * (coords.y); - - vec2 fuv = ((uv - realCoordOffs) - vec2(0.5)) * scale + vec2(0.5); - - float fx = smoothstep(0.0, step.x, fuv.x) * smoothstep(1.0, 1.0 - step.x, fuv.x); - float fy = smoothstep(0.0, step.y, fuv.y) * smoothstep(1.0, 1.0 - step.y, fuv.y); - - vec4 color = texture(tex0, fuv) * fx * fy; - o_color = color; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/distort/fluid-distort.frag b/orx-fx/src/shaders/glsl/distort/fluid-distort.frag deleted file mode 100644 index da87c220..00000000 --- a/orx-fx/src/shaders/glsl/distort/fluid-distort.frag +++ /dev/null @@ -1,71 +0,0 @@ -// created by florian berger (flockaroo) - 2016 -// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. - -// single pass CFD -// --------------- -// this is some "computational flockarooid dynamics" ;) -// the self-advection is done purely rotational on all scales. -// therefore i dont need any divergence-free velocity field. -// with stochastic sampling i get the proper "mean values" of rotations -// over time for higher order scales. -// -// try changing "RotNum" for different accuracies of rotation calculation -// for even RotNum uncomment the line #define SUPPORT_EVEN_ROTNUM - -#define RotNum 5 -//#define SUPPORT_EVEN_ROTNUM - -//#define keyTex iChannel3 -//#define KEY_I texture(keyTex,vec2((105.5-32.0)/256.0,(0.5+0.0)/3.0)).x - -const float ang = 2.0*3.1415926535/float(RotNum); -mat2 m = mat2(cos(ang), sin(ang), -sin(ang), cos(ang)); -mat2 mh = mat2(cos(ang*0.5), sin(ang*0.5), -sin(ang*0.5), cos(ang*0.5)); - -uniform sampler2D tex0; -uniform float time; -uniform float random; - -in vec2 v_texCoord0; -uniform vec2 targetSize; - -uniform float blend; - -out vec4 o_color; - -float getRot(vec2 pos, vec2 b) { - vec2 Res = vec2(textureSize(tex0, 0)); - vec2 p = b; - float rot = 0.0; - for (int i = 0; i < RotNum; i++) { - rot += dot(texture(tex0, fract((pos + p) / Res.xy)).xy -vec2(0.5), p.yx * vec2(1.0, -1.0)); - p = m * p; - } - return rot / float(RotNum)/dot(b, b); -} - -void main() { - vec2 pos = v_texCoord0 * targetSize; - vec2 Res = vec2(textureSize(tex0, 0)); - - vec2 b = vec2(cos(ang * random), sin(ang * random)); - vec2 v = vec2(0.0); - float bbMax = 0.5 * Res.y; - bbMax *= bbMax; - for (int l = 0; l < 20; l++) { - if (dot(b, b) > bbMax) break; - vec2 p = b; - for (int i = 0; i < RotNum; i++) { - #ifdef SUPPORT_EVEN_ROTNUM - v += p.yx * getRot(pos + p, -mh * b); - #else - // this is faster but works only for odd RotNum - v += p.yx * getRot(pos + p, b); - #endif - p = m*p; - } - b *= 2.0; - } - o_color = vec4(0.0, 0.0, 0.0, 1.0); - o_color.xy = texture(tex0, fract((pos + v * vec2(-1, 1) * 2.0) / Res.xy)).xy * (1.0-blend) + v_texCoord0 * blend; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/distort/horizontal-wave.frag b/orx-fx/src/shaders/glsl/distort/horizontal-wave.frag deleted file mode 100644 index cb14eadf..00000000 --- a/orx-fx/src/shaders/glsl/distort/horizontal-wave.frag +++ /dev/null @@ -1,30 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0;// input -uniform float phase; -uniform float amplitude; -uniform float frequency; - -out vec4 o_color; - -uniform int segments; -float truncate(float x, int segments) { - if (segments == 0) { - return x; - } else { - return floor(x * float(segments)) / float(segments); - } -} - -void main() { - vec2 uv = v_texCoord0; - uv.x += amplitude * cos(truncate(uv.y, segments) * 3.1415926535 * frequency + phase * 3.1415926535); - if (uv.x >= 0.0 && uv.x < 1.0) { - if (segments == 0) { - o_color = texture(tex0, uv); - } else { - o_color = textureLod(tex0, uv, 0.0); - } - } else { - o_color = vec4(0.0); - } -} diff --git a/orx-fx/src/shaders/glsl/distort/lenses.frag b/orx-fx/src/shaders/glsl/distort/lenses.frag deleted file mode 100644 index 8240e9fe..00000000 --- a/orx-fx/src/shaders/glsl/distort/lenses.frag +++ /dev/null @@ -1,34 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0;// input -uniform float scale; -uniform float rotation; -uniform int rows; -uniform int columns; -uniform float distort; - -out vec4 o_color; -void main() { - vec2 uv = v_texCoord0; - vec2 blockSize = vec2(1.0 / float(columns), 1.0 / float(rows)); - vec2 blockIndex = floor(uv / blockSize); - vec2 blockUV = mod(uv/blockSize, vec2(1.0)); - vec2 blockUVC1 = (blockUV - vec2(0.5)) * 2.0; - vec2 blockCenter = (blockIndex + 0.5) * blockSize; - - float ca = cos(radians(rotation)); - float sa = sin(radians(rotation)); - - vec2 ts = vec2(textureSize(tex0, 0)); - mat2 rm = mat2(1.0, 0.0, 0.0, ts.x / ts.y) * mat2(vec2(ca, sa), vec2(-sa, ca)) * mat2(1.0, 0.0, 0.0, ts.y / ts.x); - vec2 ruv = (uv - blockCenter); - vec2 luv; - luv.x = (1.0 - blockUVC1.y * blockUVC1.y * distort) * ruv.x; - luv.y = (1.0 - blockUVC1.x * blockUVC1.x * distort) * ruv.y; - vec2 cuv = (rm * luv * scale + blockCenter); - - float sx = step(0.0, cuv.x) * (1.0 - step(1.0, cuv.x)); - float sy = step(0.0, cuv.y) * (1.0 - step(1.0, cuv.y)); - vec4 c = texture(tex0, cuv) * sx * sy; - - o_color = c; -} diff --git a/orx-fx/src/shaders/glsl/distort/perspective-plane.frag b/orx-fx/src/shaders/glsl/distort/perspective-plane.frag deleted file mode 100644 index e1fde908..00000000 --- a/orx-fx/src/shaders/glsl/distort/perspective-plane.frag +++ /dev/null @@ -1,43 +0,0 @@ -// based on https://www.shadertoy.com/view/wsBSWG by bloxard - -uniform sampler2D tex0; -in vec2 v_texCoord0; -uniform vec3 cameraPosition; -uniform vec3 planePosition; -uniform mat4 planeMatrix; -uniform bool tile; -uniform vec2 targetSize; -out vec4 o_color; - -void main() { - vec3 vCamPos = cameraPosition; - vec3 vPlanePos = planePosition; - vec3 vPlaneRight = vec3(1.0, 0.0, 0.0); - vec3 vPlaneUp = vec3(0.0, 1.0, 0.0); - - mat3 m = mat3(planeMatrix); - vPlaneUp *= m; - vPlaneRight *= m; - - vec3 vPlaneNormal = normalize(cross(vPlaneRight, vPlaneUp)); - float fPlaneDeltaNormalDistance = dot(vPlanePos, vPlaneNormal) - dot(vPlaneNormal, vCamPos); - vec4 color = vec4(0.); - for (int m = 0; m < 2; m++) { - for (int n = 0; n < 2; n++) { - vec2 s = (v_texCoord0 - vec2(0.5)) * 2.0; - s*= vec2(1.0, targetSize.y / targetSize.x); - vec3 vRayDir = normalize(vec3(s, -1.0)); - float t = fPlaneDeltaNormalDistance / dot(vPlaneNormal, vRayDir); - vec3 hitPos = vCamPos + vRayDir * t; - vec3 delta = hitPos - vPlanePos; - vec2 bary = vec2(dot(delta, vPlaneRight), dot(delta, vPlaneUp)); - - bary /= vec2(1.0, targetSize.y / targetSize.x); - bary += vec2(0.5); - if ((tile || (bary.x >= 0.0 && bary.x <= 1.0 && bary.y >=0.0 && bary.y <= 1.0)) && t > 0.0) { - color += texture(tex0, bary); - } - } - } - o_color = color * 0.25; -} diff --git a/orx-fx/src/shaders/glsl/distort/perturb.frag b/orx-fx/src/shaders/glsl/distort/perturb.frag deleted file mode 100644 index c5305df9..00000000 --- a/orx-fx/src/shaders/glsl/distort/perturb.frag +++ /dev/null @@ -1,173 +0,0 @@ -// uniforms -uniform float gain; -uniform vec3 seed; -uniform float phase; -uniform float radius; -uniform float scale; - -uniform float lacunarity; -uniform float decay; - -uniform int octaves; -uniform sampler2D tex0; - -uniform int xSegments; -uniform int ySegments; - -uniform bool outputUV; -uniform vec2 offset; - -// varyings -in vec2 v_texCoord0; - -// outputs -out vec4 o_output; - -// Simplex Noise 3D Implementation -// Description : Array and textureless GLSL 2D/3D/4D simplex -// noise functions. -// Author : Ian McEwan, Ashima Arts. -// Maintainer : ijm -// Lastmod : 20110822 (ijm) -// License : Copyright (C) 2011 Ashima Arts. All rights reserved. -// Distributed under the MIT License. See LICENSE file. -// https://github.com/ashima/webgl-noise -// https://github.com/stegu/webgl-noise -// -// -vec3 mod289(vec3 x) { - return x - floor(x * (1.0 / 289.0)) * 289.0; -} - -vec4 mod289(vec4 x) { - return x - floor(x * (1.0 / 289.0)) * 289.0; -} - -vec4 permute(vec4 x) { - return mod289(((x*34.0)+1.0)*x); -} - -vec4 taylorInvSqrt(vec4 r) -{ - return 1.79284291400159 - 0.85373472095314 * r; -} - -float snoise(vec3 v) -{ - const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; - const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); - - // First corner - vec3 i = floor(v + dot(v, C.yyy) ); - vec3 x0 = v - i + dot(i, C.xxx) ; - - // Other corners - vec3 g = step(x0.yzx, x0.xyz); - vec3 l = 1.0 - g; - vec3 i1 = min( g.xyz, l.zxy ); - vec3 i2 = max( g.xyz, l.zxy ); - - // x0 = x0 - 0.0 + 0.0 * C.xxx; - // x1 = x0 - i1 + 1.0 * C.xxx; - // x2 = x0 - i2 + 2.0 * C.xxx; - // x3 = x0 - 1.0 + 3.0 * C.xxx; - vec3 x1 = x0 - i1 + C.xxx; - vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y - vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y - - // Permutations - i = mod289(i); - vec4 p = permute( permute( permute( - i.z + vec4(0.0, i1.z, i2.z, 1.0 )) - + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) - + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); - - // Gradients: 7x7 points over a square, mapped onto an octahedron. - // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) - float n_ = 0.142857142857; // 1.0/7.0 - vec3 ns = n_ * D.wyz - D.xzx; - - vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) - - vec4 x_ = floor(j * ns.z); - vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) - - vec4 x = x_ *ns.x + ns.yyyy; - vec4 y = y_ *ns.x + ns.yyyy; - vec4 h = 1.0 - abs(x) - abs(y); - - vec4 b0 = vec4( x.xy, y.xy ); - vec4 b1 = vec4( x.zw, y.zw ); - - //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; - //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; - vec4 s0 = floor(b0)*2.0 + 1.0; - vec4 s1 = floor(b1)*2.0 + 1.0; - vec4 sh = -step(h, vec4(0.0)); - - vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; - vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; - - vec3 p0 = vec3(a0.xy,h.x); - vec3 p1 = vec3(a0.zw,h.y); - vec3 p2 = vec3(a1.xy,h.z); - vec3 p3 = vec3(a1.zw,h.w); - - //Normalise gradients - vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); - p0 *= norm.x; - p1 *= norm.y; - p2 *= norm.z; - p3 *= norm.w; - - // Mix final noise value - vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); - m = m * m; - return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), - dot(p2,x2), dot(p3,x3) ) ); -} - -vec3 segment(vec3 t, int x, int y) { - float sx = x == 0? t.x : floor(t.x * float(x)) / float(x); - float sy = y == 0? t.y : floor(t.y * float(y)) / float(y); - return vec3(sx,sy, t.z); -} - -void main() { - float tx = 0.0; - float ty = 0.0; - - float _gain = gain; - float shift = 100.0; - - vec3 xseed = vec3(seed.xy, seed.z+radius*cos(phase*3.1415926535)); - vec3 yseed = vec3(seed.yx, seed.z+radius*sin(phase*3.1415926535)); - - vec3 uv = vec3(v_texCoord0 + offset, 1.0) * 2.0 - 1.0; - vec3 px = ((segment(uv, xSegments, ySegments) + xseed) * scale); - vec3 py = ((segment(uv, xSegments, ySegments) + yseed + vec3(100.37, 40.51, 9.43)) * scale); - - for (int o = 0; o < octaves; ++o) { - tx += snoise(px) * _gain; - ty += snoise(py) * _gain; - px = px * lacunarity + shift; - py = py * lacunarity + shift; - _gain *= decay; - } - - vec2 distCoord = v_texCoord0 + vec2(tx, ty); - - if (!outputUV) { - if (distCoord.x >= 0.0 && distCoord.y >= 0.0 && distCoord.x < 1.0 && distCoord.y < 1.0) { - if (xSegments == 0 && ySegments == 0) { - o_output = texture(tex0, distCoord); - } else { - o_output = textureLod(tex0, distCoord, 0.0); - } - } else { - o_output = vec4(0.0); - } - } else { - o_output = vec4(distCoord, 0.0, 1.0); - } -} diff --git a/orx-fx/src/shaders/glsl/distort/polar-to-rectangular.frag b/orx-fx/src/shaders/glsl/distort/polar-to-rectangular.frag deleted file mode 100644 index 8f617524..00000000 --- a/orx-fx/src/shaders/glsl/distort/polar-to-rectangular.frag +++ /dev/null @@ -1,38 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform vec2 textureSize0; -uniform sampler2D tex0; - -uniform bool logPolar; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -#define PI 3.141592653589793 - -void main() { - vec2 uv = v_texCoord0 - vec2(0.5); - float arg = atan(uv.y, uv.x); - - float bias = 0.0; - float radius = logPolar? log(1.0 + length(uv)*(exp(1.0)-bias)) / log(1.0+(exp(1.0)-bias)*sqrt(0.5)) : (length(uv) / sqrt(0.5)); - - vec2 sourceUV = vec2(arg / (2.0 * PI) + 0.5, radius); - - #ifndef OR_GL_TEXTURE2D - vec4 result = texture(tex0, sourceUV); - #else - vec4 result = texture2D(tex0, sourceUV); - #endif - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/distort/rectangular-to-polar.frag b/orx-fx/src/shaders/glsl/distort/rectangular-to-polar.frag deleted file mode 100644 index 6286dd8b..00000000 --- a/orx-fx/src/shaders/glsl/distort/rectangular-to-polar.frag +++ /dev/null @@ -1,41 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform vec2 textureSize0; -uniform sampler2D tex0; -uniform vec2 origin; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -#define PI 3.141592653589793 - -uniform int angleLevels; -uniform int radiusLevels; - -uniform bool logPolar; - -void main() { - vec2 uv = v_texCoord0; - float arg = (uv.x-0.5) * 2.0 * PI; - float radius = logPolar? (((exp(uv.y)-1.0) / (exp(1.0)-1.0))) : uv.y; - - vec2 sourceUV = (radius * sqrt(0.5) * vec2(cos(arg), sin(arg)) + vec2(0.5)); - - #ifndef OR_GL_TEXTURE2D - vec4 result = texture(tex0, sourceUV); - #else - vec4 result = texture2D(tex0, sourceUV); - #endif - - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} - diff --git a/orx-fx/src/shaders/glsl/distort/stack-repeat.frag b/orx-fx/src/shaders/glsl/distort/stack-repeat.frag deleted file mode 100644 index debd0ba6..00000000 --- a/orx-fx/src/shaders/glsl/distort/stack-repeat.frag +++ /dev/null @@ -1,38 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0;// input -uniform int repeats; -uniform float zoom; -uniform float xOrigin; -uniform float yOrigin; -uniform float xOffset; -uniform float yOffset; -uniform float rotation; - -out vec4 o_color; -void main() { - vec2 origin = vec2((xOrigin+1.0)/2.0, (yOrigin+1.0)/2.0); - vec2 ts = vec2(textureSize(tex0, 0)); - float r = ts.x/ts.y; - vec2 offset = vec2(1.0, r) * vec2(xOffset, yOffset); - vec2 uv = v_texCoord0 - vec2(origin); - float rad = (rotation / 180.0) * 3.1415926535; - vec2 cs0 = vec2(cos(rad), -sin(rad)); - vec2 cs1 = vec2(sin(rad), cos(rad)); - mat2 rotStep = mat2(cs0, cs1); - - mat2 rot = rotStep; - vec4 c = texture(tex0, v_texCoord0); - for (int i = 1; i <= repeats; ++i) { - //vec2 s = (uv * (1.0 + zoom) * i) + vec2(0.5); - vec2 s = (rot * uv * pow(1.0 + zoom, float(i) * 1.0))+ vec2(origin) + vec2(offset) * float(i); - float f = s.x >= 0.0 && s.y > 0.0 && s.x < 1.0 && s.y < 1.0? 1.0 : 0.0; - vec4 sc = texture(tex0, s) * f; - - c = c * (1.0-sc.a) + sc; - if (c.a > 1.0) { - c.a = 1.0; - } - rot *= rotStep; - } - o_color = c; -} diff --git a/orx-fx/src/shaders/glsl/distort/stretch-waves.frag b/orx-fx/src/shaders/glsl/distort/stretch-waves.frag deleted file mode 100644 index 62d4fbe5..00000000 --- a/orx-fx/src/shaders/glsl/distort/stretch-waves.frag +++ /dev/null @@ -1,40 +0,0 @@ -uniform sampler2D tex0; -in vec2 v_texCoord0; - -uniform float phase; -uniform float rotation; -uniform float distortion; -uniform float frequency; -uniform float feather; -out vec4 o_color; - -void main() { - float phi = radians(rotation); - float cp = cos(phi); - float sp = sin(phi); - mat2 rm = mat2(vec2(cp, sp), vec2(-sp, cp)); - mat2 irm = transpose(rm); - - float tw = 1.0 / frequency; - vec2 uv = rm * (v_texCoord0 - vec2(0.5)) + vec2(0.5) + vec2(phase * tw, 0.0); - - float xd = (uv.x) * frequency; - float xo = (fract(xd) - 0.5) * 2.0; - float xf = fract(xd); - - float offs = (1.0- xo * xo) * 1.0 * xo * distortion * 0.5; - float f = mix(1.0, (1.0 - xo * xo), distortion); - - vec2 fuv = uv; - fuv.x = floor(uv.x * frequency) / frequency; - fuv.x += (xf - offs) * tw; - - fuv = irm * (fuv - vec2(0.5) - vec2(phase * tw, 0.0)) + vec2(0.5); - - vec2 step = fwidth(fuv) * feather; - float fx = smoothstep(0.0, step.x, fuv.x) * smoothstep(1.0, 1.0 - step.x, fuv.x); - float fy = smoothstep(0.0, step.y, fuv.y) * smoothstep(1.0, 1.0 - step.y, fuv.y); - - vec4 c = texture(tex0, fuv) * f * fx * fy; - o_color = c; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/distort/tape-noise.frag b/orx-fx/src/shaders/glsl/distort/tape-noise.frag deleted file mode 100644 index edc00ef8..00000000 --- a/orx-fx/src/shaders/glsl/distort/tape-noise.frag +++ /dev/null @@ -1,64 +0,0 @@ -out vec4 o_output; -uniform sampler2D tex0; -in vec2 v_texCoord0; -uniform float time; - -uniform float gain; -uniform float noiseLow; -uniform float noiseHigh; -uniform vec4 tint; -uniform bool monochrome; -uniform float deformAmplitude; -uniform float deformFrequency; -uniform float gapFrequency; -uniform float gapLow; -uniform float gapHigh; - -#define HASHSCALE 443.8975 -vec2 hash22(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); - p3 += dot(p3, p3.yzx+19.19); - return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); -} - -vec3 saturate(vec3 x) { - return clamp(x, vec3(0.0), vec3(1.0)); -} - -vec3 aberrationColor(float f) { - f = f * 3.0 - 1.5; - return saturate(vec3(-f, 1.0 - abs(f), f)); -} - -void main() { - float dk = 1.0/600.0; - o_output = vec4(0.0); - for (int k = 0; k < 10; ++k ) { - vec2 duv = v_texCoord0; - duv.y += smoothstep(pow(cos(time+float(k)*dk+v_texCoord0.y*1.0),10.0)*0.1+0.1, 0.0, v_texCoord0.x)*deformAmplitude * cos((time+float(k)*dk)*deformFrequency); - duv.y += smoothstep(pow(1.0-cos(time+float(k)*dk+v_texCoord0.y*1.0),10.0)*0.1+0.1, 0.9, v_texCoord0.x)*deformAmplitude * cos((time+float(k)*dk)*deformFrequency); - duv.y += sin(v_texCoord0.x*3.1415926535)*0.0; - float bc = floor(hash22(vec2(time+float(k)*dk, (time+float(k)*dk)*0.1)).x*20.0); - - float gb3 = floor(duv.y*bc)/bc; - - vec2 v = hash22(duv.xy*0.003+time+float(k)*dk); - vec2 v2 = hash22(duv.xy*0.03+time+float(k)*dk); - vec2 v2b = hash22(duv.yx*0.03+time+float(k)*dk); - float stretch = (cos(time+float(k)*dk)*0.001+0.002)*0.3+0.001; - vec2 h = hash22(duv.yy*stretch+time+float(k)*dk); - float gap = smoothstep(gapLow, gapHigh, cos(gb3*(gapFrequency+duv.y*gapFrequency + (time+float(k)*dk)*gapFrequency) +duv.x*gapFrequency)) * (cos(gb3)*0.5+0.5); - - float r = smoothstep(noiseLow, noiseHigh, h.x*gap*v2.x)*1.0; - float g = smoothstep(noiseLow, noiseHigh, h.x*gap*v2.y)*1.0; - float b = smoothstep(noiseLow, noiseHigh, h.x*gap*v2b.x)*1.0; - float a = smoothstep(noiseLow, noiseHigh, h.x*gap*v2b.y)*1.0; - if (!monochrome) { - o_output += vec4(r, g, b, a)*gain * tint; - } else { - o_output += vec4(r, r, r, a)*gain * tint; - } - } - o_output *= o_output.a; - o_output += texture(tex0, v_texCoord0); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/distort/tiles.frag b/orx-fx/src/shaders/glsl/distort/tiles.frag deleted file mode 100644 index 39173079..00000000 --- a/orx-fx/src/shaders/glsl/distort/tiles.frag +++ /dev/null @@ -1,35 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; // input -uniform float rotation; -uniform int xSegments; -uniform int ySegments; - -out vec4 o_color; - -uniform int segments; -float truncate(float x, int segments) { - if (segments == 0) { - return x; - } else { - return floor(x * float(segments)) / float(segments); - } -} - -void main() { - vec2 uv = v_texCoord0 - vec2(0.5); - float cr = cos(radians(rotation)); - float sr = sin(radians(rotation)); - - mat2 rm = mat2(cr, -sr, sr, cr); - - vec2 ruv = rm * uv; - vec2 truv = vec2(truncate(ruv.x, xSegments), truncate(ruv.y, ySegments)); - vec2 tuv = transpose(rm) * truv + vec2(0.5); - - vec4 c = vec4(0.0); - tuv.x = clamp(tuv.x, 0.0, 1.0); - tuv.y = clamp(tuv.y, 0.0, 1.0); - c = texture(tex0, tuv); - - o_color = c * texture(tex0, v_texCoord0).a; -} diff --git a/orx-fx/src/shaders/glsl/distort/uvmap.frag b/orx-fx/src/shaders/glsl/distort/uvmap.frag deleted file mode 100644 index 482012cf..00000000 --- a/orx-fx/src/shaders/glsl/distort/uvmap.frag +++ /dev/null @@ -1,9 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0;// uvmap -uniform sampler2D tex1;// input -out vec4 o_color; - -void main() { - vec2 uv = texture(tex0, v_texCoord0).xy; - o_color = texture(tex1, uv); -} diff --git a/orx-fx/src/shaders/glsl/distort/vertical-wave.frag b/orx-fx/src/shaders/glsl/distort/vertical-wave.frag deleted file mode 100644 index 1eff2f4d..00000000 --- a/orx-fx/src/shaders/glsl/distort/vertical-wave.frag +++ /dev/null @@ -1,30 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; // input -uniform float phase; -uniform float amplitude; -uniform float frequency; - -out vec4 o_color; - -uniform int segments; -float truncate(float x, int segments) { - if (segments == 0) { - return x; - } else { - return floor(x * float(segments)) / float(segments); - } -} - -void main() { - vec2 uv = v_texCoord0; - uv.y += amplitude * sin(truncate(uv.x, segments) * 3.1415926535 * frequency + phase * 3.1415926535); - if (uv.y >= 0.0 && uv.y < 1.0) { - if (segments == 0) { - o_color = texture(tex0, uv); - } else { - o_color = textureLod(tex0, uv, 0.0); - } - } else { - o_color = vec4(0.0); - } -} diff --git a/orx-fx/src/shaders/glsl/distort/video-glitch.frag b/orx-fx/src/shaders/glsl/distort/video-glitch.frag deleted file mode 100644 index 9e5f514e..00000000 --- a/orx-fx/src/shaders/glsl/distort/video-glitch.frag +++ /dev/null @@ -1,56 +0,0 @@ -out vec4 o_output; -uniform sampler2D tex0; -in vec2 v_texCoord0; -uniform float time; -uniform float amplitude; -uniform float vfreq; -uniform float pfreq; -uniform float hfreq; -uniform float poffset; -uniform float scrollOffset0; -uniform float scrollOffset1; - -uniform float borderHeight; - -#define HASHSCALE 443.8975 -vec2 hash22(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); - p3 += dot(p3, p3.yzx+19.19); - return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); -} - -vec3 saturate(vec3 x) { - return clamp(x, vec3(0.0), vec3(1.0)); -} - -vec4 getVideo(vec2 uv, float amplitude, float seconds) { - float iTime = seconds; - vec2 look = mod(uv, vec2(1.0)); - float window = 1.0/(1.0 + 20.0*(look.y-mod(iTime*vfreq, 1.0))*(look.y-mod(iTime*vfreq, 1.))); - look.x = look.x + sin(look.y*pfreq + poffset * 3.1415)/50.0 *(1.0+cos(iTime*hfreq))*window*amplitude; - look.y = mod(look.y, 1.0); - - vec4 video = texture(tex0, look); - return video; -} - -vec4 aberrationColor(float f) { - f = f * 3.0 - 1.5; - return vec4(saturate(vec3(-f, 1.0 - abs(f), f)), 1.0); -} - -void main() { - vec4 c = vec4(0.0); - float aa = amplitude + smoothstep(borderHeight, 0.0, v_texCoord0.y)*4.0 + smoothstep(1.0-borderHeight, 1.0, v_texCoord0.y)*4.0; - float ds = scrollOffset1 - scrollOffset0; - if (aa > 0.0 || ds > 0.0) { - for (int i = 1; i < 16; ++i) { - vec4 lc = getVideo(v_texCoord0 + vec2(0.0, scrollOffset0+ds*float(i)), aa, time-float(i)/(16.0*60.0)); - c += lc * (3.0/16.0) * aberrationColor(float(i)/16.0); - } - o_output = c; - } else { - vec4 lc = texture(tex0, mod(v_texCoord0 + vec2(0.0, scrollOffset1), vec2(1.0))); - o_output = lc; - } -} diff --git a/orx-fx/src/shaders/glsl/dither/a-dither.frag b/orx-fx/src/shaders/glsl/dither/a-dither.frag deleted file mode 100644 index 8789895e..00000000 --- a/orx-fx/src/shaders/glsl/dither/a-dither.frag +++ /dev/null @@ -1,49 +0,0 @@ -// this shader is based on the "a dither" work by Øyvind Kolås -// https://pippin.gimp.org/a_dither/ - -in vec2 v_texCoord0; -uniform sampler2D tex0; -uniform int pattern; -uniform int levels; - -float mask1(int levels, float l, int x, int y, int c) { - float mask = float((x ^ y * 149) * 1234& 511)/511.0; - return floor(float(levels) * l + mask)/float(levels); -} - -float mask2(int levels, float l, int x, int y, int c) { - float mask = float(((x+c*17) ^ y * 149) * 1234 & 511)/511.0; - return floor(float(levels) * l + mask)/float(levels); -} -float mask3(int levels, float l, int x, int y, int c) { - float mask = float((x + y * 237) * 119 & 255)/255.0; - return floor(float(levels) * float(l) + mask)/float(levels); -} - -float mask4(int levels, float l, int x, int y, int c) { - float mask = float(((x+c*67) + y * 236) * 119 & 255)/255.0; - return floor(float(levels) * float(l) + mask)/float(levels); -} - -out vec4 o_color; -void main() { - vec4 c = texture(tex0, v_texCoord0); - if (c.a > 0.0) { - c.rgb/=c.a; - } - ivec2 ic = ivec2(v_texCoord0 * vec2(textureSize(tex0, 0))); - - vec4 rgba = vec4(0.0); - if (pattern == 0) { - rgba = vec4(mask1(levels, c.r, ic.x, ic.y, 0), mask1(levels, c.g, ic.x, ic.y, 1), mask1(levels, c.b, ic.x, ic.y, 2), mask1(levels, c.a, ic.x, ic.y, 3)); - } else if (pattern == 1) { - rgba = vec4(mask2(levels, c.r, ic.x, ic.y, 0), mask2(levels, c.g, ic.x, ic.y, 1), mask2(levels, c.b, ic.x, ic.y, 2), mask2(levels, c.a, ic.x, ic.y, 3)); - } else if (pattern == 2) { - rgba = vec4(mask3(levels, c.r, ic.x, ic.y, 0), mask3(levels, c.g, ic.x, ic.y, 1), mask3(levels, c.b, ic.x, ic.y, 2), mask3(levels, c.a, ic.x, ic.y, 3)); - } else { - rgba = vec4(mask4(levels, c.r, ic.x, ic.y, 0), mask4(levels, c.g, ic.x, ic.y, 1), mask4(levels, c.b, ic.x, ic.y, 2), mask4(levels, c.a, ic.x, ic.y, 3)); - } - - rgba.rgb *= rgba.a; - o_color = rgba; -} diff --git a/orx-fx/src/shaders/glsl/dither/cmyk-halftone.frag b/orx-fx/src/shaders/glsl/dither/cmyk-halftone.frag deleted file mode 100644 index bd99ff28..00000000 --- a/orx-fx/src/shaders/glsl/dither/cmyk-halftone.frag +++ /dev/null @@ -1,111 +0,0 @@ -/* Based on CMYK Halftone by tsone https://www.shadertoy.com/view/Mdf3Dn */ -uniform float dotSize; - -uniform float scale; -uniform float rotation; -uniform float phase; - -uniform float blackRotation; -uniform float yellowRotation; -uniform float magentaRotation; -uniform float cyanRotation; - -uniform vec4 blackColor; -uniform vec4 yellowColor; -uniform vec4 magentaColor; -uniform vec4 cyanColor; - -in vec2 v_texCoord0; -uniform sampler2D tex0; - -out vec4 o_color; - -#pragma INSERT_PHRASES - -vec4 rgb2cmyki(in vec3 c) -{ - float k = max(max(c.r, c.g), c.b); - return min(vec4(c.rgb / k, k), 1.0); -} - -vec3 cmyki2rgb(in vec4 c) -{ - return c.rgb * c.a; -} -vec3 u(vec4 c) { - if (c.a == 0.0) { - return vec3(0.0); - } else { - return c.rgb / c.a; - } - -} -vec4 cmyki2rgba(in vec4 cmyk) { - vec4 c = cyanColor * (1.0 - cmyk.r); - vec4 m = magentaColor * (1.0 - cmyk.g); - vec4 y = yellowColor * (1.0 - cmyk.b); - vec4 k = blackColor * (1.0 - cmyk.a); - - vec4 f = c; - f = (1.0 - f.a) * m + f.a * vec4(u(f) * u(m), 1.0) * m.a + (1.0 - m.a) * f; - f = (1.0 - f.a) * y + f.a * vec4(u(f) * u(y), 1.0) * y.a + (1.0 - y.a) * f; - f = (1.0 - f.a) * k + f.a * vec4(u(f) * u(k), 1.0) * k.a + (1.0 - k.a) * f; - return f; -} - - -vec2 px2uv(in vec2 px) -{ - return vec2(px / vec2(textureSize(tex0, 0))); -} - -vec2 grid(in vec2 px) -{ - return px - mod(px, scale); -} - -vec4 ss(in vec4 v) -{ - vec4 vw = fwidth(v); - return smoothstep(vec4(-vw), vec4(vw), v); -} - - -float halftone(in vec2 fc, in mat2 m, int channel) -{ - vec2 smp = (grid(m * fc) + 0.5 * scale) * m; - - float s = length(fc - smp) / (dotSize * 2.0 * scale); - vec2 d2 = m * ((fc) - smp) / (dotSize * 0.5 * scale); - vec3 texc = texture(tex0, px2uv(smp + vec2(textureSize(tex0, 0)) / 2.0)).rgb; - float c = 1.0 - rgb2cmyki(texc)[channel]; - return element(d2, c); -} - -mat2 rotm(in float r) { - float cr = cos(r); - float sr = sin(r); - return mat2(cr, -sr, sr, cr); -} - -void main() { - vec2 fc = (v_texCoord0 - vec2(0.5)) * vec2(textureSize(tex0, 0)); - fc = domainWarp(fc); - - mat2 mc = rotm(rotation + radians(cyanRotation)); - mat2 mm = rotm(rotation + radians(magentaRotation)); - mat2 my = rotm(rotation + radians(yellowRotation)); - mat2 mk = rotm(rotation + radians(blackRotation)); - - vec4 c = cmyki2rgba( - ss(vec4( - halftone(fc, mc, 0), - halftone(fc, mm, 1), - halftone(fc, my, 2), - halftone(fc, mk, 3) - ) - ) - ); - - o_color = c; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/dither/crosshatch.frag b/orx-fx/src/shaders/glsl/dither/crosshatch.frag deleted file mode 100644 index f2847c17..00000000 --- a/orx-fx/src/shaders/glsl/dither/crosshatch.frag +++ /dev/null @@ -1,53 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; -out vec4 o_color; - -// thresholds -uniform float t1; -uniform float t2; -uniform float t3; -uniform float t4; - - -// glsl-luma -float luma(vec3 color) { - return dot(color, vec3(0.299, 0.587, 0.114)); -} - -float luma(vec4 color) { - return dot(color.rgb, vec3(0.299, 0.587, 0.114)); -} - -// glsl-crosshatch -vec3 crosshatch(vec3 texColor, float t1, float t2, float t3, float t4) { - float lum = luma(texColor); - vec3 color = vec3(1.0); - if (lum < t1) { - if (mod(gl_FragCoord.x + gl_FragCoord.y, 10.0) == 0.0) { - color = vec3(0.0); - } - } - if (lum < t2) { - if (mod(gl_FragCoord.x - gl_FragCoord.y, 10.0) == 0.0) { - color = vec3(0.0); - } - } - if (lum < t3) { - if (mod(gl_FragCoord.x + gl_FragCoord.y - 5.0, 10.0) == 0.0) { - color = vec3(0.0); - } - } - if (lum < t4) { - if (mod(gl_FragCoord.x - gl_FragCoord.y - 5.0, 10.0) == 0.0) { - color = vec3(0.0); - } - } - return color; -} - - -void main() { - vec4 color = texture(tex0, v_texCoord0); - vec3 demultiplied = color.a == 0.0 ? vec3(0.0) : color.rgb/color.a; - o_color = vec4(crosshatch(demultiplied, t1, t2, t3, t4), 1.0) * color.a; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/dither/luma-halftone.frag b/orx-fx/src/shaders/glsl/dither/luma-halftone.frag deleted file mode 100644 index ba835e74..00000000 --- a/orx-fx/src/shaders/glsl/dither/luma-halftone.frag +++ /dev/null @@ -1,62 +0,0 @@ -uniform float scale; -uniform float rotation; -in vec2 v_texCoord0; -uniform sampler2D tex0; -out vec4 o_color; -uniform float threshold; - -uniform float freq0; -uniform float freq1; -uniform float gain1; -uniform float phase0; -uniform float phase1; -uniform bool invert; - -float cosine_sample(vec2 uv){ - float ca = cos(radians(rotation)); - float sa = sin(radians(rotation)); - - vec2 ts = vec2(textureSize(tex0, 0)); - mat2 rm = mat2(1.0, 0.0, 0.0, ts.x/ts.y) * mat2(vec2(ca, sa), vec2(-sa, ca)) * mat2(1.0, 0.0, 0.0, ts.y/ts.x); - - vec2 cuv = (rm * (uv - vec2(0.5))) + vec2(0.5); - - float m = fract(phase0 + cuv.x*freq0 + cos(cuv.y*freq1+phase1*3.141592653)*gain1); - vec4 c = texture(tex0, v_texCoord0); - if (c.a != 0.0) { - c.rgb /= c.a; - } - float l = dot(vec3(1.0/3.0), c.rgb); - if (invert) { - l = 1.0 - l; - } - - float t = 0.0; - t = step(threshold, l * m); - return t; -} - -float cosine_halftone(vec2 uv) { - int w = 3; - vec2 step = 1.0 / vec2(textureSize(tex0, 0)); - step /= (2.0*float(w)); - float weight = 0.0; - float sum = 0.0; - for (int v = -w; v <= w; ++v) { - for (int u = -w; u <= w; ++u) { - sum += cosine_sample(uv + step * vec2(u, v)); - weight+=1.0; - } - } - return sum / weight; -} - - -void main() { - vec4 c = texture(tex0, v_texCoord0); - float t = cosine_halftone(v_texCoord0); - if (invert) { - t = 1.0 - t; - } - o_color = vec4(t, t, t, 1.0) * c.a; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/edges/canny-edge-detector.frag b/orx-fx/src/shaders/glsl/edges/canny-edge-detector.frag deleted file mode 100644 index 6915556c..00000000 --- a/orx-fx/src/shaders/glsl/edges/canny-edge-detector.frag +++ /dev/null @@ -1,116 +0,0 @@ -// https://www.shadertoy.com/view/sdcSz2 -// ref: (in japanese) -// https://imagingsolution.net/imaging/canny-edge-detector/ -uniform float thickness; -uniform sampler2D tex0; - -in vec2 v_texCoord0; -out vec4 o_output; - -uniform float threshold0; -uniform float threshold1; - -uniform vec4 backgroundColor; -uniform vec4 foregroundColor; -uniform float backgroundOpacity; -uniform float foregroundOpacity; - -uniform float fade; - -vec2 iResolution; - -float getAve(vec2 uv) { - vec3 rgb = texture(tex0, uv).rgb; - vec3 lum = vec3(0.299, 0.587, 0.114); - return dot(lum, rgb); -} - -// Detect edge. -vec4 sobel(vec2 fragCoord, vec2 dir) { - vec2 uv = fragCoord / iResolution.xy; - vec2 texel = 1. / iResolution.xy; - float np = getAve(uv + (vec2(-1, + 1) + dir) * texel * thickness); - float zp = getAve(uv + (vec2(0, + 1) + dir) * texel * thickness); - float pp = getAve(uv + (vec2(+ 1, + 1) + dir) * texel * thickness); - - float nz = getAve(uv + (vec2(-1, 0) + dir) * texel * thickness); - // zz = 0 - float pz = getAve(uv + (vec2(+ 1, 0) + dir) * texel * thickness); - - float nn = getAve(uv + (vec2(-1, -1) + dir) * texel * thickness); - float zn = getAve(uv + (vec2(0, -1) + dir) * texel * thickness); - float pn = getAve(uv + (vec2(+ 1, -1) + dir) * texel * thickness); - - // np zp pp - // nz zz pz - // nn zn pn - - #if 0 - float gx = (np * -1. + nz * -2. + nn * -1. + pp * 1. + pz * 2. + pn * 1.); - float gy = (np * -1. + zp * -2. + pp * -1. + nn * 1. + zn * 2. + pn * 1.); - #else - // https://www.shadertoy.com/view/Wds3Rl - float gx = (np * -3. + nz * -10. + nn * -3. + pp * 3. + pz * 10. + pn * 3.); - float gy = (np * -3. + zp * -10. + pp * -3. + nn * 3. + zn * 10. + pn * 3.); - #endif - - vec2 G = vec2(gx, gy); - - float grad = length(G); - - float angle = atan(G.y, G.x); - - return vec4(G, grad, angle); -} - -// Make edge thinner. -vec2 hysteresisThr(vec2 fragCoord, float mn, float mx) { - - vec4 edge = sobel(fragCoord, vec2(0)); - - vec2 dir = vec2(cos(edge.w), sin(edge.w)); - dir *= vec2(-1, 1); // rotate 90 degrees. - - vec4 edgep = sobel(fragCoord, dir); - vec4 edgen = sobel(fragCoord, -dir); - - if (edge.z < edgep.z || edge.z < edgen.z) edge.z = 0.; - - return vec2( - (edge.z > mn) ? edge.z : 0., - (edge.z > mx) ? edge.z : 0. - ); -} - -float cannyEdge(vec2 fragCoord, float mn, float mx) { - - vec2 np = hysteresisThr(fragCoord + vec2(-1, + 1), mn, mx); - vec2 zp = hysteresisThr(fragCoord + vec2(0, + 1), mn, mx); - vec2 pp = hysteresisThr(fragCoord + vec2(+ 1, + 1), mn, mx); - - vec2 nz = hysteresisThr(fragCoord + vec2(-1, 0), mn, mx); - vec2 zz = hysteresisThr(fragCoord + vec2(0, 0), mn, mx); - vec2 pz = hysteresisThr(fragCoord + vec2(+ 1, 0), mn, mx); - - vec2 nn = hysteresisThr(fragCoord + vec2(-1, -1), mn, mx); - vec2 zn = hysteresisThr(fragCoord + vec2(0, -1), mn, mx); - vec2 pn = hysteresisThr(fragCoord + vec2(+ 1, -1), mn, mx); - - // np zp pp - // nz zz pz - // nn zn pn - //return min(1., step(1e-3, zz.x) * (zp.y + nz.y + pz.y + zn.y)*8.); - //return min(1., step(1e-3, zz.x) * (np.y + pp.y + nn.y + pn.y)*8.); - return min(1., step(1e-2, zz.x * 8.) * smoothstep(.0, .3, np.y + zp.y + pp.y + nz.y + pz.y + nn.y + zn.y + pn.y) * 8.); -} - -void main() { - iResolution = vec2(textureSize(tex0, 0)); - vec4 original = texture(tex0, v_texCoord0); - vec2 fragCoord = v_texCoord0 * iResolution; - float edge = cannyEdge(fragCoord, threshold0, threshold1); - o_output = mix(original, - mix(foregroundColor * foregroundOpacity, - backgroundColor * backgroundOpacity, 1. - edge), - fade); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/edges/contour.frag b/orx-fx/src/shaders/glsl/edges/contour.frag deleted file mode 100644 index ef54cf33..00000000 --- a/orx-fx/src/shaders/glsl/edges/contour.frag +++ /dev/null @@ -1,51 +0,0 @@ -uniform sampler2D tex0; -in vec2 v_texCoord0; -out vec4 o_output; -uniform float levels; -uniform float contourWidth; -uniform float contourOpacity; -uniform vec4 contourColor; -uniform float backgroundOpacity; -uniform int window; -uniform float bias; -uniform bool outputBands; -uniform float fade; - -vec2 calc_contour(vec2 uv) { - vec4 box = texture(tex0, uv); - float v = sin(3.1415926535 * levels * (dot(vec3(1.0 / 3.0), box.xyz) + bias)); - float level = floor((dot(vec3(1.0 / 3.0), box.xyz) + bias) * levels); - float contour = 1.0 - smoothstep(0., contourWidth, 0.5 * abs(v) / fwidth(v)); - return vec2(contour, level); -} - -void main() { - vec2 step = 1.0 / vec2(textureSize(tex0, 0)); - float contour = 0.0; - float weight = 0.0; - float level = 0.0; - - for (int i = -window; i <= window; ++i) { - for (int j = -window; j <= window; ++j) { - vec2 c = calc_contour(v_texCoord0 + step / (float(window) + 1.0) * vec2(float(i), float(j))); - contour += c.x; - level += c.y; - weight += 1.0; - } - } - contour /= weight; - - vec4 t = texture(tex0, v_texCoord0); - - if (outputBands) { - level /= weight; - - level = 1.0 - max(0.0, fract(level / 2.0) * 2.0); - contour = level; - } - - o_output = mix(t, - t * backgroundOpacity * (1.0 - contour) + contour * contourColor * contourOpacity * clamp(t.a, 0.0, 1.0), - fade); - -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/edges/edges-work-1.frag b/orx-fx/src/shaders/glsl/edges/edges-work-1.frag deleted file mode 100644 index f47c2f21..00000000 --- a/orx-fx/src/shaders/glsl/edges/edges-work-1.frag +++ /dev/null @@ -1,37 +0,0 @@ -uniform sampler2D tex0; -in vec2 v_texCoord0; -out vec4 o_color; - -uniform vec2 delta; - -float random(vec3 scale, float seed) { - /* use the fragment position for a different seed per-pixel */ - return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed); -} - -// Implementation by Evan Wallace (glfx.js) -void main() { - vec4 center = texture(tex0, v_texCoord0); - vec2 color = vec2(0.0); - vec2 total = vec2(0.0); - - /* randomize the lookup values to hide the fixed number of samples */ - float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); - - for (float t = -30.0; t <= 30.0; t++) { - float percent = (t + offset - 0.5) / 30.0; - float weight = 1.0 - abs(percent); - vec3 tex = texture(tex0, v_texCoord0 + delta * percent).rgb; - float average = (tex.r + tex.g + tex.b) / 3.0; - color.x += average * weight; - total.x += weight; - - if (abs(t) < 15.0) { - weight = weight * 2.0 - 1.0; - color.y += average * weight; - total.y += weight; - } - } - - o_color = vec4(color / total, 0.0, 1.0) * center.a; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/edges/edges-work-2.frag b/orx-fx/src/shaders/glsl/edges/edges-work-2.frag deleted file mode 100644 index 35d697e9..00000000 --- a/orx-fx/src/shaders/glsl/edges/edges-work-2.frag +++ /dev/null @@ -1,38 +0,0 @@ -uniform sampler2D tex0; -in vec2 v_texCoord0; -out vec4 o_color; - -uniform vec2 delta; -uniform int radius; - -float random(vec3 scale, float seed) { - /* use the fragment position for a different seed per-pixel */ - return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed); -} - -// Implementation by Evan Wallace (glfx.js) -void main() { - vec4 center = texture(tex0, v_texCoord0); - vec2 color = vec2(0.0); - vec2 total = vec2(0.0); - - /* randomize the lookup values to hide the fixed number of samples */ - float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); - - for (float t = -30.0; t <= 30.0; t++) { - float percent = (t + offset - 0.5) / 30.0; - float weight = 1.0 - abs(percent); - vec2 tex = texture(tex0, v_texCoord0 + delta * percent).xy; - color.x += tex.x * weight; - total.x += weight; - - if (abs(t) < 15.0) { - weight = weight * 2.0 - 1.0; - color.y += tex.y * weight; - total.y += weight; - } - } - float c = clamp(10000.0 * (color.y / total.y - color.x / total.x) + 0.5, 0.0, 1.0); - - o_color = vec4(c, c, c, 1.0) * center.a; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/edges/luma-laplacian.frag b/orx-fx/src/shaders/glsl/edges/luma-laplacian.frag deleted file mode 100644 index 577fc5d6..00000000 --- a/orx-fx/src/shaders/glsl/edges/luma-laplacian.frag +++ /dev/null @@ -1,62 +0,0 @@ -in vec2 v_texCoord0; - -uniform sampler2D tex0; - -uniform vec4 backgroundColor; -uniform vec4 edgeColor; -uniform float backgroundOpacity; -uniform float edgeOpacity; -out vec4 o_output; - -float step = 1.0; - -float luma(vec4 color){ - vec3 n = color.a == 0.0? vec3(0.0) : color.rgb/color.a; - return dot(n, vec3(1.0/3.0)); -} - -/** Denotes if UV coordinates falls outside an image. - \param pos the UV coordinates - \return true if the UV are outside of the image - */ -bool isOutside(vec2 pos){ - return (pos.x < 0.0 || pos.y < 0.0 || pos.x > 1.0 || pos.y > 1.0); -} - -/** Compute the Laplacian field of an input RGB image, adding a 1px black border around it before computing the gradients and divergence. */ -void main(){ - float div = 0.0; - ivec2 size = textureSize(tex0, 0).xy; - - vec3 pixelShift = vec3(0.0); - pixelShift.xy = 1.0/vec2(size); - - vec2 uvs = v_texCoord0; - if(!isOutside(uvs)){ - float col = luma(textureLod(tex0, uvs, 0.0)); - div = 4.0 * col; - } - - vec2 uvs110 = uvs + pixelShift.xz; - if(!isOutside(uvs110)){ - float col110 = luma(textureLod(tex0, uvs110, 0.0)); - div -= col110; - } - vec2 uvs101 = uvs + pixelShift.zy; - if(!isOutside(uvs101)){ - float col101 = luma(textureLod(tex0, uvs101, 0.0)); - div -= col101; - } - vec2 uvs010 = uvs - pixelShift.xz; - if(!isOutside(uvs010)){ - float col010 = luma(textureLod(tex0, uvs010, 0.0)); - div -= col010; - } - vec2 uvs001 = uvs - pixelShift.zy; - if(!isOutside(uvs001)){ - float col001 = luma(textureLod(tex0, uvs001, 0.0)); - div -= col001; - } - o_output.rgb = vec3(div); - o_output.a = 1.0f; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/edges/luma-sobel.frag b/orx-fx/src/shaders/glsl/edges/luma-sobel.frag deleted file mode 100644 index 3220633e..00000000 --- a/orx-fx/src/shaders/glsl/edges/luma-sobel.frag +++ /dev/null @@ -1,47 +0,0 @@ -in vec2 v_texCoord0; - -uniform sampler2D tex0; - -uniform vec4 backgroundColor; -uniform vec4 edgeColor; -uniform float backgroundOpacity; -uniform float edgeOpacity; -out vec4 o_color; - -float step = 1.0; - -float luma(vec4 color){ - vec3 n = color.a == 0.0? vec3(0.0) : color.rgb/color.a; - return dot(n, vec3(1.0/3.0)); -} - -void main() { - vec2 step = 1.0 / vec2(textureSize(tex0, 0)); - - float tl = luma(texture(tex0, v_texCoord0 + vec2(-step.x, step.y))); - float l = luma(texture(tex0, v_texCoord0 + vec2(-step.x, 0))); - float bl = luma(texture(tex0, v_texCoord0 + vec2(-step.x, -step.y))); - float t = luma(texture(tex0, v_texCoord0 + vec2(0, step.y))); - float b = luma(texture(tex0, v_texCoord0 + vec2(0, -step.y))); - float tr = luma(texture(tex0, v_texCoord0 + vec2(step.x, step.y))); - float r = luma(texture(tex0, v_texCoord0 + vec2(step.x, 0))); - float br = luma(texture(tex0, v_texCoord0 + vec2(step.x, -step.y))); - - // Sobel masks (see http://en.wikipedia.org/wiki/Sobel_operator) - // 1 0 -1 -1 -2 -1 - // X = 2 0 -2 Y = 0 0 0 - // 1 0 -1 1 2 1 - - // You could also use Scharr operator: - // 3 0 -3 3 10 3 - // X = 10 0 -10 Y = 0 0 0 - // 3 0 -3 -3 -10 -3 - - float x = tl + 2.0 * l + bl - tr - 2.0 * r - br; - float y = -tl - 2.0 * t - tr + bl + 2.0 * b + br; - float intensity = sqrt(x*x + y*y) / sqrt(2.0); - vec4 color = mix(vec4(backgroundColor.rgb, backgroundOpacity), vec4(edgeColor.rgb, edgeOpacity), intensity); - - vec4 a = texture(tex0, v_texCoord0); - o_color = vec4(color.rgb, 1.0) * color.a * a.a; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/grain/film-grain.frag b/orx-fx/src/shaders/glsl/grain/film-grain.frag deleted file mode 100644 index b51d7231..00000000 --- a/orx-fx/src/shaders/glsl/grain/film-grain.frag +++ /dev/null @@ -1,107 +0,0 @@ -// Licensed under the MIT license: -// https://opensource.org/licenses/MIT. - -// Ad[a|o]pted from shader by "noby" https://www.shadertoy.com/view/3sGSWV -uniform sampler2D tex0; - -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform bool useColor;// false -uniform float time; -uniform float grainLiftRatio;// = 0.5; -uniform float grainStrength;//= 1.0; -uniform float grainRate;// = 1.0; -// Range: [0.5, 1.0]. -uniform float grainPitch;// = 1.0; - -uniform float colorLevel;// = 1.0; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_output; -#endif - -// From Dave Hoskins: https://www.shadertoy.com/view/4djSRW. -float hash(vec3 p3){ - p3 = fract(p3 * 0.1031); - p3 += dot(p3, p3.yzx + 19.19); - return fract((p3.x + p3.y) * p3.z); -} - -// From iq: https://www.shadertoy.com/view/4sfGzS. -float noise(vec3 x){ - vec3 i = floor(x); - vec3 f = fract(x); - f = f*f*(3.0-2.0*f); - return mix(mix(mix(hash(i+vec3(0, 0, 0)), - hash(i+vec3(1, 0, 0)), f.x), - mix(hash(i+vec3(0, 1, 0)), - hash(i+vec3(1, 1, 0)), f.x), f.y), - mix(mix(hash(i+vec3(0, 0, 1)), - hash(i+vec3(1, 0, 1)), f.x), - mix(hash(i+vec3(0, 1, 1)), - hash(i+vec3(1, 1, 1)), f.x), f.y), f.z); -} - -// Slightly high-passed continuous value-noise. -float grain_source(vec3 x, float strength, float pitch){ - float center = noise(x); - float v1 = center - noise(vec3(1, 0, 0)/pitch + x) + 0.5; - float v2 = center - noise(vec3(0, 1, 0)/pitch + x) + 0.5; - float v3 = center - noise(vec3(-1, 0, 0)/pitch + x) + 0.5; - float v4 = center - noise(vec3(0, -1, 0)/pitch + x) + 0.5; - - float total = (v1 + v2 + v3 + v4) / 4.0; - return mix(1.0, 0.5 + total, strength); -} - -void main() { - vec2 uv = v_texCoord0; - vec2 x = gl_FragCoord.xy; - - // Alternatively use iTime here instead and change the grain_rate - // parameter to correspond to frames-per-second. - float t = time; - #ifndef OR_GL_TEXTURE2D - vec4 colorAlpha = texture(tex0, uv); - #else - vec4 colorAlpha = texture2D(tex0, uv); - #endif - - vec3 color = colorAlpha.rgb; - vec3 grain = vec3(0); - - if (useColor) { - float rg = grain_source(vec3(x, floor(grainRate*(t))), grainStrength, grainPitch); - float gg = grain_source(vec3(x, floor(grainRate*(t+9.0))), grainStrength, grainPitch); - float bg = grain_source(vec3(x, floor(grainRate*(t-9.0))), grainStrength, grainPitch); - - // Consider using values outside the [0, 1] range as well - // to introduce interesting color shifts to the source. - - vec3 color_grain = vec3(rg, gg, bg); - color_grain = mix(vec3(dot(color_grain, vec3(0.2126, 0.7152, 0.0722))), color_grain, colorLevel); - grain = color_grain; - } else { - const float neutral_grain_factor = sqrt(2.0); - grain = vec3(grain_source(vec3(x, floor(grainRate*t)), grainStrength/neutral_grain_factor, grainPitch)); - } - - // Control whether to add or multiply or lift the source with the grain. - // Multiply (0.0) should be more true to life, but adjust to taste. - - color = max(mix(color*grain, color+(grain-1.0), grainLiftRatio), 0.0); - - // After this you would normally perform tone mapping, - // apply the grain before that. - #ifndef OR_GL_FRAGCOLOR - o_output.rgb = color; - o_output.a = 1.0; - #else - gl_FragColor.rgb = color; - gl_FragColor.a = 1.0; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/math/multiply-u.frag b/orx-fx/src/shaders/glsl/math/multiply-u.frag deleted file mode 100644 index 46b18ef5..00000000 --- a/orx-fx/src/shaders/glsl/math/multiply-u.frag +++ /dev/null @@ -1,27 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform float bias; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - #endif - - vec4 result = a * (v_texCoord0.x + bias); - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/math/multiply-v.frag b/orx-fx/src/shaders/glsl/math/multiply-v.frag deleted file mode 100644 index 10b29d7c..00000000 --- a/orx-fx/src/shaders/glsl/math/multiply-v.frag +++ /dev/null @@ -1,28 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform float bias; -uniform bool invertV; -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - #endif - - float v = invertV ? (1.0 - v_texCoord0.y) : v_texCoord0.y; - vec4 result = a * (v + bias); - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/math/square.frag b/orx-fx/src/shaders/glsl/math/square.frag deleted file mode 100644 index c20b92f3..00000000 --- a/orx-fx/src/shaders/glsl/math/square.frag +++ /dev/null @@ -1,26 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform sampler2D tex0; -uniform float bias; -uniform float invertV; -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - #ifndef OR_GL_TEXTURE2D - vec4 a = texture(tex0, v_texCoord0); - #else - vec4 a = texture2D(tex0, v_texCoord0); - #endif - vec4 result = a * a; - #ifdef OR_GL_FRAGCOLOR - gl_FragColor = result; - #else - o_color = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/patterns/checkers.frag b/orx-fx/src/shaders/glsl/patterns/checkers.frag deleted file mode 100644 index c6102654..00000000 --- a/orx-fx/src/shaders/glsl/patterns/checkers.frag +++ /dev/null @@ -1,49 +0,0 @@ -#ifdef OR_IN_OUT -in vec2 v_texCoord0; -#else -varying vec2 v_texCoord0; -#endif - -uniform vec4 foreground; -uniform vec4 background; -uniform vec2 targetSize; -uniform float size; -uniform float opacity; - -#ifndef OR_GL_FRAGCOLOR -out vec4 o_color; -#endif - -void main() { - float r = targetSize.x / targetSize.y; - vec2 uv = v_texCoord0 - vec2(0.5); - uv.x *= r; - - vec2 cell = (uv / size); - ivec2 cellIndex = ivec2(floor(cell)); - vec2 cellUV = cell - vec2(cellIndex); - - int c = (cellIndex.x + cellIndex.y) % 2; - vec2 w = fwidth(cell); - - vec4 ca; - vec4 cb; - if (c == 0) { - ca = background; - cb = foreground; - } else { - ca = foreground; - cb = background; - } - float s = w.x; - float fx = smoothstep(s, 0.0, cellUV.x) + smoothstep(1.0 - s, 1.0, cellUV.x); - float fy = smoothstep(s, 0.0, cellUV.y) + smoothstep(1.0 - s, 1.0, cellUV.y); - - vec4 result = mix(ca, cb, min(0.5, fx * 0.5 + fy * 0.5)) * opacity; - - #ifndef OR_GL_FRAGCOLOR - o_color = result; - #else - gl_FragCoord = result; - #endif -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/shadow/dropshadow-blend.frag b/orx-fx/src/shaders/glsl/shadow/dropshadow-blend.frag deleted file mode 100644 index 99168750..00000000 --- a/orx-fx/src/shaders/glsl/shadow/dropshadow-blend.frag +++ /dev/null @@ -1,12 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform vec2 shift; - -out vec4 o_color; -void main() { - vec4 a = texture(tex0, v_texCoord0-shift); - vec4 b = texture(tex1, v_texCoord0); - float alpha = min(1.0,max(0.0, b.a)); - o_color = a * (1.0-alpha) + b; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/shadow/dropshadow-blur.frag b/orx-fx/src/shaders/glsl/shadow/dropshadow-blur.frag deleted file mode 100644 index 42aa1e0e..00000000 --- a/orx-fx/src/shaders/glsl/shadow/dropshadow-blur.frag +++ /dev/null @@ -1,28 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; -uniform vec2 blurDirection; - -uniform int window; -uniform float sigma; -uniform float gain; -uniform vec4 subtract; -uniform float spread; -uniform vec4 color; -out vec4 o_color; -void main() { - vec2 s = vec2(textureSize(tex0, 0)).xy; - s = vec2(1.0/s.x, 1.0/s.y); - - int w = window; - - vec4 sum = vec4(0, 0, 0, 0); - float weight = 0.0; - for (int x = -w; x<= w; ++x) { - float lw = 1.0; - sum += texture(tex0, v_texCoord0 + float(x) * blurDirection * s * spread); - weight += lw; - } - - o_color = (sum / weight) * gain; - o_color.rgb = color.rgb * o_color.a; -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/tonemap/aces-tonemap.frag b/orx-fx/src/shaders/glsl/tonemap/aces-tonemap.frag deleted file mode 100644 index f08f5936..00000000 --- a/orx-fx/src/shaders/glsl/tonemap/aces-tonemap.frag +++ /dev/null @@ -1,25 +0,0 @@ -uniform sampler2D tex0; -uniform float exposureBias; - -in vec2 v_texCoord0; -out vec4 o_output; - -vec3 saturate(vec3 x) { - return clamp(x, vec3(0.0), vec3(1.0)); -} - -vec3 ACESFilm(vec3 x) { - float a = 2.51f; - float b = 0.03f; - float c = 2.43f; - float d = 0.59f; - float e = 0.14f; - return saturate((x*(a*x+b))/(x*(c*x+d)+e)); -} - -void main() { - vec3 texColor = texture(tex0,v_texCoord0).rgb; - vec3 color = ACESFilm(texColor * exposureBias); - - o_output = vec4(color, 1.0); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/tonemap/reinhard-tonemap.frag b/orx-fx/src/shaders/glsl/tonemap/reinhard-tonemap.frag deleted file mode 100644 index 834e2e50..00000000 --- a/orx-fx/src/shaders/glsl/tonemap/reinhard-tonemap.frag +++ /dev/null @@ -1,33 +0,0 @@ -uniform sampler2D tex0; -uniform float exposureBias; -uniform float maxLuminance; - -in vec2 v_texCoord0; -out vec4 o_output; - -vec3 saturate(vec3 x) { - return clamp(x, vec3(0.0), vec3(1.0)); -} - -float luminance(vec3 v) { - return dot(v, vec3(0.2126f, 0.7152f, 0.0722f)); -} - -vec3 change_luminance(vec3 c_in, float l_out) { - float l_in = luminance(c_in); - return c_in * (l_out / l_in); -} - -vec3 reinhard_extended_luminance(vec3 v, float max_white_l) { - float l_old = luminance(v); - float numerator = l_old * (1.0f + (l_old / (max_white_l * max_white_l))); - float l_new = numerator / (1.0f + l_old); - return change_luminance(v, l_new); -} - -void main() { - vec3 texColor = texture(tex0,v_texCoord0).rgb; - vec3 color = reinhard_extended_luminance(texColor * exposureBias, maxLuminance); - vec3 retColor = pow(color, vec3(1.0/2.2)); - o_output = vec4(retColor, 1); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/tonemap/uncharted2-tonemap.frag b/orx-fx/src/shaders/glsl/tonemap/uncharted2-tonemap.frag deleted file mode 100644 index ca5ad2c9..00000000 --- a/orx-fx/src/shaders/glsl/tonemap/uncharted2-tonemap.frag +++ /dev/null @@ -1,26 +0,0 @@ -// ad[a|o]pted from http://filmicworlds.com/blog/filmic-tonemapping-operators/ -float A = 0.15; -float B = 0.50; -float C = 0.10; -float D = 0.20; -float E = 0.02; -float F = 0.30; -float W = 11.2; - -uniform sampler2D tex0; -uniform float exposureBias; -in vec2 v_texCoord0; -out vec4 o_output; - -vec3 Uncharted2Tonemap(vec3 x) { - return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; -} - -void main() { - vec3 texColor = texture(tex0,v_texCoord0).rgb; - vec3 curr = Uncharted2Tonemap(exposureBias*texColor); - vec3 whiteScale = 1.0f/Uncharted2Tonemap(vec3(W)); - vec3 color = curr*whiteScale; - - o_output = vec4(color, 1); -} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/transform/flip-vertically.frag b/orx-fx/src/shaders/glsl/transform/flip-vertically.frag deleted file mode 100644 index f5f8a68d..00000000 --- a/orx-fx/src/shaders/glsl/transform/flip-vertically.frag +++ /dev/null @@ -1,10 +0,0 @@ -in vec2 v_texCoord0; -uniform sampler2D tex0; -uniform vec4 constant; - -out vec4 o_color; -void main() { - vec2 uv = v_texCoord0; - uv.y = 1.0 - uv.y; - o_color = texture(tex0, uv); -} diff --git a/orx-gradient-descent/README.md b/orx-gradient-descent/README.md deleted file mode 100644 index 0ac82ffc..00000000 --- a/orx-gradient-descent/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# orx-gradient-descent - -Finds equation inputs that output a minimum value: easy to use gradient descent based minimizer. - -## Usage - -```kotlin -// define a model -class Model { - var x = 0.0 - var y = 0.0 -} - -val model = Model() -minimizeModel(model) { m -> - (m.x-4.0)*(m.x-4.0) + (m.y-3.0)*(m.y-3.0) -} - -// model.x is close to 4 and model y is close to 3 at this point -``` - -## Data binding - -Currently we support minimizing model classes that contain -`Double`, `Vector2`, `Vector3` and `Vector4` typed properties, -other types are silently ignored. - -An example of a supported model: -```kotlin -class Model { - var x = 0.0 - var y = 0.0 - var v2 = Vector2.ZERO -} -``` diff --git a/orx-gradient-descent/build.gradle.kts b/orx-gradient-descent/build.gradle.kts deleted file mode 100644 index a5640c12..00000000 --- a/orx-gradient-descent/build.gradle.kts +++ /dev/null @@ -1,41 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") - alias(libs.plugins.kotest.multiplatform) -} - -kotlin { - jvm { - testRuns["test"].executionTask { - useJUnitPlatform { - - } - } - } - sourceSets { - val commonMain by getting { - dependencies { - implementation(project(":orx-parameters")) - implementation(project(":orx-shader-phrases")) - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - } - } - - val commonTest by getting { - dependencies { - implementation(sharedLibs.kotest.assertions) - implementation(sharedLibs.kotest.framework.engine) - } - } - - val jvmTest by getting { - dependencies { - implementation(sharedLibs.kotest.assertions) - implementation(sharedLibs.kotest.framework.engine) - runtimeOnly(sharedLibs.kotlin.reflect) - } - } - } -} \ No newline at end of file diff --git a/orx-gradient-descent/src/commonMain/kotlin/GradientDescent.kt b/orx-gradient-descent/src/commonMain/kotlin/GradientDescent.kt deleted file mode 100644 index 341466e0..00000000 --- a/orx-gradient-descent/src/commonMain/kotlin/GradientDescent.kt +++ /dev/null @@ -1,177 +0,0 @@ -// Adapted from the numeric.js gradient and uncmin functions -// Numeric Javascript -// Copyright (C) 2011 by Sébastien Loisel - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -package org.openrndr.extra.gradientdescent - -import kotlin.math.abs -import kotlin.math.max -import kotlin.math.min -import kotlin.math.sqrt - -private fun ten(a: DoubleArray, b: DoubleArray): Array = Array(a.size) { mul(b, a[it]) } -private fun max(a: Double, b: Double, c: Double): Double = max(max(a, b), c) - -private fun max(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double, g: Double, h: Double): Double { - return max(max(max(max(max(max(max(a, b), c), d), e), f), g), h) -} - -fun gradient(x: DoubleArray, objective: (parameters: DoubleArray) -> Double): DoubleArray { - var k = 0 - val tempX = x.copyOf() - val f1 = objective(x) - val grad = DoubleArray(x.size) - require(f1 == f1) - for (i in 0 until x.size) { - var delta = max(1e-6 * f1, 1e-8) - - while (true) { - require(k != 20) { "gradient failed" } - tempX[i] = x[i] + delta - val f0 = objective(tempX) - tempX[i] = x[i] - delta - val f2 = objective(tempX) - tempX[i] = x[i] - - if (f0 == f0 && f2 == f2) { - grad[i] = (f0 - f2) / (2 * delta) - val t0 = x[i] - delta - val t1 = x[i] - val t2 = x[i] + delta - val d1 = (f0 - f1) / delta - val d2 = (f1 - f2) / delta - val err = min(max(abs(d1 - grad[i]), abs(d2 - grad[i]), abs(d1 - d2)), delta) - val normalize = max(abs(grad[i]), abs(f0), abs(f1), abs(f2), abs(t0), abs(t1), abs(t2), 1e-8) - if (err / normalize < 1e-3) - break - } - delta /= 16.0 - k++ - } - } - return grad -} - -private fun identity(n: Int): Array = Array(n) { j -> - DoubleArray(n) { i -> if (i == j) 1.0 else 0.0 } -} - -private fun neg(x: DoubleArray): DoubleArray = DoubleArray(x.size) { -x[it] } -private fun add(x: DoubleArray, y: DoubleArray): DoubleArray = DoubleArray(x.size) { x[it] + y[it] } -private fun sub(x: DoubleArray, y: DoubleArray): DoubleArray = DoubleArray(x.size) { x[it] - y[it] } -private fun add(x: Array, y: Array) = Array(x.size) { add(x[it], y[it]) } -private fun sub(x: Array, y: Array) = Array(x.size) { sub(x[it], y[it]) } -private fun mul(x: Array, y: Double) = Array(x.size) { mul(x[it], y) } -private fun mul(x: DoubleArray, y: Double) = DoubleArray(x.size) { x[it] * y } -private fun mul(x: DoubleArray, y: DoubleArray) = DoubleArray(x.size) { x[it] * y[it] } -private fun div(x: Array, y: Double) = Array(x.size) { div(x[it], y) } -private fun div(x: DoubleArray, y: Double) = DoubleArray(x.size) { x[it] / y } -private fun norm2(x: DoubleArray): Double { - return sqrt(x.sumOf { it * it }) -} - -internal fun dot(x: DoubleArray, y: DoubleArray): Double = (x.mapIndexed { index, it -> it * y[index] }).sum() - -internal fun dot(x: Array, y: DoubleArray): DoubleArray = DoubleArray(x.size) { dot(x[it], y) } - -class MinimizationResult(val solution: DoubleArray, val value: Double, val gradient: DoubleArray, - val inverseHessian: Array, val iterations: Int) - -fun minimize(_x0: DoubleArray, - weights: DoubleArray = DoubleArray(_x0.size) { 1.0 }, - endOnLineSearch: Boolean = false, tol: Double = 1e-8, maxIterations: Int = 1000, f: (DoubleArray) -> Double): MinimizationResult { - val grad = { a: DoubleArray -> gradient(a, f) } - var x0 = _x0.copyOf() - var g0 = grad(x0) - var f0 = f(x0) - require(f0 == f0) - - var H1 = identity(_x0.size) - var iteration = 0 - while (iteration < maxIterations) { - require(g0.all { it == it && it != Double.POSITIVE_INFINITY && it != Double.NEGATIVE_INFINITY }) - val pstep = dot(H1, g0) - require(pstep.all { it == it }) { "pstep contains NaNs" } - require(pstep.all { it != Double.POSITIVE_INFINITY && it != Double.NEGATIVE_INFINITY }) { "pstep contains infs" } - val step = neg(pstep) - - val nstep = norm2(step) - require(nstep == nstep) - if (nstep < tol) { - break - } - var t = 1.0 - val df0 = dot(g0, step) - var x1 = x0 - var s = DoubleArray(0) - var f1 = Double.POSITIVE_INFINITY - while (iteration < maxIterations && t * nstep >= tol) { - s = mul(step, t) - x1 = add(x0, s) - f1 = f(x1) - - require(f1 == f1) { "f1 is NaN" } - if (!(f1 - f0 >= 0.1 * t * df0)) { - break - } - t *= 0.5 - iteration++ - - } - require(s.isNotEmpty()) - if (t * nstep < tol && endOnLineSearch) { - break - } - if (iteration >= maxIterations) break - val g1 = grad(x1) - require(g1.all { it == it }) - val y = sub(g1, g0) - val ys = dot(y, s) - if (ys == 0.0) { - break - } - val Hy = dot(H1, y) - H1 = sub( - add( - H1, - mul( - ten(s, s), - (ys + dot(y, Hy)) / (ys * ys) - ) - ), - div( - add( - ten(Hy, s), - ten(s, Hy) - ), - ys - ) - ) - x0 = x1 - f0 = f1 - g0 = g1 - iteration++ - } - - return MinimizationResult(x0, f0, g0, H1, iteration) -} - diff --git a/orx-gradient-descent/src/jvmMain/kotlin/DataBinding.kt b/orx-gradient-descent/src/jvmMain/kotlin/DataBinding.kt deleted file mode 100644 index 9397dbe1..00000000 --- a/orx-gradient-descent/src/jvmMain/kotlin/DataBinding.kt +++ /dev/null @@ -1,90 +0,0 @@ -package org.openrndr.extra.gradientdescent - -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 - -/** - * converts a model to an array of doubles - */ -fun modelToArray(model: T): DoubleArray { - val doubles = mutableListOf() - model::class.java.declaredFields.forEach { - when { - it.type == DoubleArray::class.java -> { - it.isAccessible = true - val da = it.get(model) as DoubleArray - for (d in da) { - doubles.add(d) - } - } - - it.type == Double::class.java -> { - it.isAccessible = true - doubles.add(it.getDouble(model)) - } - it.type == Vector2::class.java -> { - it.isAccessible = true - val v2 = it.get(model) as Vector2 - doubles.add(v2.x) - doubles.add(v2.y) - } - it.type == Vector3::class.java -> { - it.isAccessible = true - val v3 = it.get(model) as Vector3 - doubles.add(v3.x) - doubles.add(v3.y) - doubles.add(v3.z) - } - it.type == Vector4::class.java -> { - it.isAccessible = true - val v4 = it.get(model) as Vector4 - doubles.add(v4.x) - doubles.add(v4.y) - doubles.add(v4.z) - doubles.add(v4.w) - } - } - } - return doubles.toDoubleArray() -} - -/** - * converts array of doubles to model values - */ -fun arrayToModel(data: DoubleArray, model: T) { - var index = 0 - model::class.java.declaredFields.forEach { - when { - it.type == DoubleArray::class.java -> { - it.isAccessible = true - //it.setDouble(model, data[index]) - val da = it.get(model) as DoubleArray - for (i in 0 until da.size) { - da[i] = data[index] - index++ - } - } - it.type == Double::class.java -> { - it.isAccessible = true - it.setDouble(model, data[index]) - index++ - } - it.type == Vector2::class.java -> { - it.isAccessible = true - it.set(model, Vector2(data[index], data[index+1])) - index+=2 - } - it.type == Vector3::class.java -> { - it.isAccessible = true - it.set(model, Vector3(data[index], data[index+1],data[index+2])) - index+=3 - } - it.type == Vector4::class.java -> { - it.isAccessible = true - it.set(model, Vector4(data[index], data[index+1],data[index+2],data[index+3])) - index+=3 - } - } - } -} \ No newline at end of file diff --git a/orx-gradient-descent/src/jvmMain/kotlin/GradientDescent.kt b/orx-gradient-descent/src/jvmMain/kotlin/GradientDescent.kt deleted file mode 100644 index 0e5729a7..00000000 --- a/orx-gradient-descent/src/jvmMain/kotlin/GradientDescent.kt +++ /dev/null @@ -1,12 +0,0 @@ -@file:JvmName("GradientDescentJvmKt") -package org.openrndr.extra.gradientdescent - -fun minimizeModel(model: T, endOnLineSearch: Boolean = false, tol: Double = 1e-8, maxIterations: Int = 1000, function: (T) -> Double) { - val doubles = modelToArray(model) - val weights = DoubleArray(doubles.size) { 1.0 } - val solution = minimize(doubles, weights, endOnLineSearch, tol, maxIterations) { - arrayToModel(it, model) - function(model) - } - arrayToModel(solution.solution, model) -} \ No newline at end of file diff --git a/orx-gradient-descent/src/jvmTest/kotlin/TestDot.kt b/orx-gradient-descent/src/jvmTest/kotlin/TestDot.kt deleted file mode 100644 index c8ab4cba..00000000 --- a/orx-gradient-descent/src/jvmTest/kotlin/TestDot.kt +++ /dev/null @@ -1,26 +0,0 @@ -import io.kotest.core.spec.style.DescribeSpec -import io.kotest.matchers.equals.shouldBeEqual -import org.openrndr.extra.gradientdescent.dot - - -class TestDot : DescribeSpec({ - describe("some vectors") { - val a = doubleArrayOf(10.0) - val b = doubleArrayOf(4.0) - dot(a, b).shouldBeEqual(40.0) - } - - describe("a matrix and a vector") { - val a = arrayOf(doubleArrayOf(10.0)) - val b = doubleArrayOf(1.0) - val d = dot(a, b) - d[0].shouldBeEqual(10.0) - } - - describe("another matrix and a vector") { - val a = arrayOf(doubleArrayOf(1.0)) - val b = doubleArrayOf(19.99999999995339) - val d = dot(a, b) - d[0].shouldBeEqual(19.99999999995339) - } -}) \ No newline at end of file diff --git a/orx-gradient-descent/src/jvmTest/kotlin/TestGradient.kt b/orx-gradient-descent/src/jvmTest/kotlin/TestGradient.kt deleted file mode 100644 index 91a9ea10..00000000 --- a/orx-gradient-descent/src/jvmTest/kotlin/TestGradient.kt +++ /dev/null @@ -1,41 +0,0 @@ -import io.kotest.core.spec.style.DescribeSpec -import io.kotest.matchers.equals.shouldBeEqual -import org.openrndr.extra.gradientdescent.gradient - -class TestGradient : DescribeSpec({ - describe("a simple 1d function") { - fun parabola(x: DoubleArray): Double { - return x[0] * x[0] - } - it("its gradient at x=0 is 0.0") { - val g0 = gradient(doubleArrayOf(0.0), ::parabola) - g0.size.shouldBeEqual(1) - g0[0].shouldBeEqual(0.0) - } - it("its gradient at x=1 is ~2.0") { - val g1 = gradient(doubleArrayOf(1.0), ::parabola) - } - it("its gradient at x=-1 is ~-2.0") { - val g1 = gradient(doubleArrayOf(-1.0), ::parabola) - } - } - - describe("a simple 2d function") { - fun parabola(x: DoubleArray): Double { - return x[0] * x[0] + x[1] * x[1] - } - - it("its gradient at x=0 is 0.0") { - val g0 = gradient(doubleArrayOf(0.0, 0.0), ::parabola) - g0.size.shouldBeEqual(2) - g0[0].shouldBeEqual(0.0) - } - - it("its gradient at x=1 is ~2.0") { - val g1 = gradient(doubleArrayOf(1.0, 1.0), ::parabola) - } - it("its gradient at x=-1 is ~-2.0") { - val g1 = gradient(doubleArrayOf(-1.0, -1.0), ::parabola) - } - } -}) \ No newline at end of file diff --git a/orx-gradient-descent/src/jvmTest/kotlin/TestMinimize.kt b/orx-gradient-descent/src/jvmTest/kotlin/TestMinimize.kt deleted file mode 100644 index 03de5c7d..00000000 --- a/orx-gradient-descent/src/jvmTest/kotlin/TestMinimize.kt +++ /dev/null @@ -1,13 +0,0 @@ -import io.kotest.core.spec.style.DescribeSpec -import org.openrndr.extra.gradientdescent.minimize - -class TestMinimize : DescribeSpec({ - describe("a simple 1d function") { - fun parabola(x: DoubleArray): Double { - return (x[0] + 1) * (x[0] + 1) - } - it("it can be minimized") { - val result = minimize(doubleArrayOf(10.0), f = ::parabola) - } - } -}) \ No newline at end of file diff --git a/orx-gradient-descent/src/jvmTest/kotlin/TestMinimizeModel.kt b/orx-gradient-descent/src/jvmTest/kotlin/TestMinimizeModel.kt deleted file mode 100644 index 255e3b4b..00000000 --- a/orx-gradient-descent/src/jvmTest/kotlin/TestMinimizeModel.kt +++ /dev/null @@ -1,34 +0,0 @@ -import io.kotest.core.spec.style.DescribeSpec -import io.kotest.matchers.doubles.plusOrMinus -import io.kotest.matchers.shouldBe -import org.openrndr.extra.gradientdescent.minimizeModel -import org.openrndr.math.Vector2 - -class TestMinimizeModel : DescribeSpec({ - describe("a model") { - val m = object { - var x = 0.0 - var y = 0.0 - } - it("can be minimized") { - minimizeModel(m) { m-> - (m.x - 4.0) * (m.x - 4.0) + (m.y - 3.0) * (m.y - 3.0) - } - m.x.shouldBe(4.0.plusOrMinus(0.01)) - m.y.shouldBe(3.0.plusOrMinus(0.01)) - } - } - - describe("a model with a Vector2 property") { - val m = object { - var position = Vector2.ZERO - } - it("can be minimized") { - minimizeModel(m) { m-> - (m.position.x - 4.0) * (m.position.x - 4.0) + (m.position.y - 3.0) * (m.position.y - 3.0) - } - m.position.x.shouldBe(4.0.plusOrMinus(0.01)) - m.position.y.shouldBe(3.0.plusOrMinus(0.01)) - } - } -}) \ No newline at end of file diff --git a/orx-gradient-descent/src/jvmTest/kotlin/TestPlaceholder.kt b/orx-gradient-descent/src/jvmTest/kotlin/TestPlaceholder.kt deleted file mode 100644 index 53683ddf..00000000 --- a/orx-gradient-descent/src/jvmTest/kotlin/TestPlaceholder.kt +++ /dev/null @@ -1,8 +0,0 @@ -import kotlin.test.Test - -class TestPlaceholder { - @Test - fun testPlaceholder() { - assert(true) - } -} \ No newline at end of file diff --git a/orx-hash-grid/README.md b/orx-hash-grid/README.md deleted file mode 100644 index b262c5a3..00000000 --- a/orx-hash-grid/README.md +++ /dev/null @@ -1,114 +0,0 @@ -# orx-hash-grid - -2D space partitioning for fast point queries. - -## Usage - -`orx-hash-grid` provides the classes `HashGrid` and `Cell`, in most cases only `HashGrid` is used. - -Create a hash grid for a given radius. -```kotlin -val grid = HashGrid(radius) -``` - -Check for a given query point if the grid is free, i.e. there is no point in the grid at distance less than `radius` away from the -query point. - -```kotlin -grid.isFree(query) -``` - -Add a point to the hash grid structure: -```kotlin -grid.insert(point) -``` - -Iterate over all points in the hash grid: -```kotlin -for (point in grid.points()) { - // do something with point -} -``` - -## Extensions to standard library - -`orx-hash-grid` provides short-hand extension functions to `List` - -
- -```kotlin -fun List.filter(radius: Double) : List - ``` - -filters the points in the list such that only points with an inter-distance of `radius` remain. - -```kotlin -val points = (0 until 10_000).map { drawer.bounds.uniform() } -val filtered = points.filter(20.0) -``` - -
- -```kotlin -fun List.hashGrid(radius: Double) : HashGrid -``` -constructs a (mutable) `HashGrid` containing all points in the list. - -```kotlin -val points = (0 until 10_000).map { drawer.bounds.uniform() } -val hashGrid = points.hashGrid(20.0) -``` - -
- -## References - - * `orx-noise` uses `HashGrid` to generate Poisson distributed points. [Link](https://github.com/openrndr/orx/blob/master/orx-noise/src/commonMain/kotlin/PoissonDisk.kt) - - -## Demos -### DemoFilter01 - - -The program performs the following steps: -- Generates 10,000 random points uniformly distributed within the drawable bounds. -- Filters the generated points to enforce a minimum distance of 20.0 units between them. -- Visualizes the filtered points as circles with a radius of 10.0 units on the canvas. - -The `filter` method is provided by `orx-hash-grid`. - -![DemoFilter01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-hash-grid/images/DemoFilter01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoFilter01.kt) - -### DemoFilter3D01 - -Demonstrates how to use a 3D hash-grid `filter` operation to remove points from a random 3D point-collection -that are too close to each other. The resulting points are displayed as small spheres. - -The program performs the following key steps: -- Generates 10,000 random 3D points located between a minimum and maximum radius. -- Filters the points to ensure a minimum distance between any two points using a spatial hash grid. -- Creates a small sphere mesh that will be instanced for each filtered point. -- Sets up an orbital camera to allow viewing the 3D scene interactively. -- Renders the filtered points by translating the sphere mesh to each point's position and applying a shader that modifies the fragment color based on the view normal. - -![DemoFilter3D01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-hash-grid/images/DemoFilter3D01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoFilter3D01.kt) - -### DemoHashGrid01 - -This demo creates a `HashGrid` to manage points in a 2D space. -Notice the desired cell size in the HashGrid constructor. - -On every animation frame, it attempts to insert 100 random points into the HashGrid. -When a HashGrid cell is free, a point is inserted. - -The visual output includes: -- Rectangles representing the bounds of the occupied cells in the grid. -- Circles representing the generated random points. - -![DemoHashGrid01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-hash-grid/images/DemoHashGrid01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoHashGrid01.kt) diff --git a/orx-hash-grid/build.gradle.kts b/orx-hash-grid/build.gradle.kts deleted file mode 100644 index d322b652..00000000 --- a/orx-hash-grid/build.gradle.kts +++ /dev/null @@ -1,31 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") -} - -kotlin { - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - implementation(project(":orx-parameters")) - implementation(project(":orx-shader-phrases")) - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - } - } - - @Suppress("UNUSED_VARIABLE") - val jvmDemo by getting { - dependencies { - implementation(project(":orx-color")) - implementation(project(":orx-fx")) - implementation(project(":orx-noise")) - implementation(project(":orx-camera")) - implementation(project(":orx-mesh-generators")) - } - } - - } -} \ No newline at end of file diff --git a/orx-hash-grid/src/commonMain/kotlin/HashGrid.kt b/orx-hash-grid/src/commonMain/kotlin/HashGrid.kt deleted file mode 100644 index de85d910..00000000 --- a/orx-hash-grid/src/commonMain/kotlin/HashGrid.kt +++ /dev/null @@ -1,254 +0,0 @@ -package org.openrndr.extra.hashgrid - -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle -import kotlin.jvm.JvmRecord -import kotlin.math.abs -import kotlin.math.max -import kotlin.math.min -import kotlin.math.sqrt -import kotlin.random.Random - -private fun Double.fastFloor(): Int { - return if (this >= 0) this.toInt() else this.toInt() - 1 -} - -@JvmRecord -private data class GridCoords(val x: Int, val y: Int) { - fun offset(i: Int, j: Int): GridCoords = copy(x = x + i, y = y + j) -} - -/** - * Represents a cell in a 2D space, defined by its position and size. - * - * @property x The x-coordinate of the cell in the grid. - * @property y The y-coordinate of the cell in the grid. - * @property cellSize The size of the cell along each axis. - */ -class Cell(val x: Int, val y: Int, val cellSize: Double) { - var xMin: Double = Double.POSITIVE_INFINITY - private set - var xMax: Double = Double.NEGATIVE_INFINITY - private set - var yMin: Double = Double.POSITIVE_INFINITY - private set - var yMax: Double = Double.NEGATIVE_INFINITY - private set - - /** - * Calculates and returns the rectangular bounds of the cell in the 2D grid. - * The bounds are represented as a rectangle with its top-left position and size derived - * from the cell's position (`x`, `y`) and `cellSize`. - */ - val bounds: Rectangle - get() { - return Rectangle(x * cellSize, y * cellSize, cellSize, cellSize) - } - - /** - * Computes the bounds of the content within the cell, considering the points stored in it. - * If no points are present in the cell, the bounds will be represented as an empty rectangle. - * Otherwise, the bounds are determined by the minimum and maximum x and y coordinates - * among the points in the cell. - */ - val contentBounds: Rectangle - get() { - if (points.isEmpty()) { - return Rectangle.EMPTY - } else { - return Rectangle(xMin, yMin, xMax - xMin, yMax - yMin) - } - } - - - internal val points = mutableListOf>() - internal fun insert(point: Vector2, owner: Any?) { - points.add(Pair(point, owner)) - xMin = min(xMin, point.x) - xMax = max(xMax, point.x) - yMin = min(yMin, point.y) - yMax = max(yMax, point.y) - } - - internal fun squaredDistanceTo(query: Vector2): Double { - val width = xMax - xMin - val height = yMax - yMin - val x = (xMin + xMax) / 2.0 - val y = (yMin + yMax) / 2.0 - val dx = max(abs(query.x - x) - width / 2, 0.0) - val dy = max(abs(query.y - y) - height / 2, 0.0) - return dx * dx + dy * dy - } - - /** - * Generates a sequence of points contained within the current cell. - * Iterates over the points stored in the cell and yields each point one by one. - * - * @return A sequence of points in the cell. - */ - fun points() = sequence { - for (point in points) { - yield(point) - } - } -} - -/** - * Represents a 2D spatial hash grid used for efficiently managing and querying points in a sparse space. - * - * @property radius The maximum distance between points for them to be considered neighbors. - */ -class HashGrid(val radius: Double) { - private val cells = mutableMapOf() - - /** - * Returns a sequence of all cells stored in the grid. - * Iterates through the values in the internal `cells` map and yields each cell. - */ - fun cells() = sequence { - for (cell in cells.values) { - yield(cell) - } - } - - /** - * Represents the total number of elements (points or data) that are currently stored in the grid. - * - * This property is managed internally and reflects the current size of the grid data structure. - * It cannot be modified directly from outside the class. - */ - var size: Int = 0 - private set - - /** - * Represents the size of a single cell in the hash grid. - * - * Computed as the radius divided by the square root of 2. - * This value determines the spatial resolution of each cell in the grid. - */ - val cellSize = radius / sqrt(2.0) - private fun coords(v: Vector2): GridCoords { - val x = (v.x / cellSize).fastFloor() - val y = (v.y / cellSize).fastFloor() - return GridCoords(x, y) - } - - /** - * Generates a sequence of all points stored within the grid. - * - * Iterates through each cell in the grid's `cells` map, yielding all points - * contained within each cell. - * - * @return A sequence of points from all cells in the grid. - */ - fun points() = sequence { - for (cell in cells.values) { - for (point in cell.points) { - yield(point) - } - } - } - - /** - * Selects a random point from the grid using the provided random number generator. - * - * @param random The random number generator to use. Defaults to `Random.Default`. - * @return A randomly selected point, represented as a `Vector2`, from the grid's cells. - */ - fun random(random: Random = Random.Default): Vector2 { - return cells.values.random(random).points.random().first - } - - /** - * Inserts a point into the grid, associating it with an owner if provided. - * The method calculates the grid cell corresponding to the provided point and inserts - * the point into that cell. If the cell does not exist, it is created. - * - * @param point The point to insert, represented as a `Vector2` object. - * @param owner An optional object to associate with the point. Defaults to `null` if no owner is specified. - */ - fun insert(point: Vector2, owner: Any? = null) { - val gc = coords(point) - val cell = cells.getOrPut(gc) { Cell(gc.x, gc.y, cellSize) } - cell.insert(point, owner) - size += 1 - } - - /** - * Retrieves the cell corresponding to the given query point in the grid. - * The method calculates the grid coordinates for the query point and returns - * the cell found at those coordinates, if it exists. - * - * @param query The point in 2D space, represented as a `Vector2`, for which - * to retrieve the corresponding cell. - * @return The `Cell` corresponding to the given query point, or `null` if - * no cell exists at the calculated coordinates. - */ - fun cell(query: Vector2): Cell? = cells[coords(query)] - - /** - * Checks if a specific query point in 2D space is free from any nearby points or owners, - * according to the internal grid structure and other constraints. - * - * @param query The 2D point represented as a Vector2 to check for available space. - * @param ignoreOwners A set of owners to be ignored while checking for nearby points. Defaults to an empty set. - * @return `true` if the query point is free, `false` otherwise. - */ - fun isFree(query: Vector2, ignoreOwners: Set = emptySet()): Boolean { - val c = coords(query) - if (cells[c] == null) { - for (j in -2..2) { - for (i in -2..2) { - if (i == 0 && j == 0) { - continue - } - val n = c.offset(i, j) - val nc = cells[n] - if (nc != null && nc.squaredDistanceTo(query) <= radius * radius) { - for (p in nc.points) { - - if (p.second == null || p.second !in ignoreOwners) { - if (p.first.squaredDistanceTo(query) <= radius * radius) { - return false - } - } - } - } - } - } - return true - } else { - return cells[c]!!.points.all { it.second != null && it.second in ignoreOwners } - } - } -} - -/** - * Construct a hash grid containing all points in the list - * @param radius radius of the hash grid - */ -fun List.hashGrid(radius: Double): HashGrid { - val grid = HashGrid(radius) - for (point in this) { - grid.insert(point) - } - return grid -} - -/** - * Return a list that only contains points at a minimum distance. - * @param radius the minimum distance between any two points in the returned list - */ -fun List.filter(radius: Double): List { - return if (size <= 1) { - this - } else { - val grid = HashGrid(radius) - for (point in this) { - if (grid.isFree(point)) { - grid.insert(point) - } - } - grid.points().map { it.first }.toList() - } -} \ No newline at end of file diff --git a/orx-hash-grid/src/commonMain/kotlin/HashGrid3D.kt b/orx-hash-grid/src/commonMain/kotlin/HashGrid3D.kt deleted file mode 100644 index 1b148ade..00000000 --- a/orx-hash-grid/src/commonMain/kotlin/HashGrid3D.kt +++ /dev/null @@ -1,269 +0,0 @@ -package org.openrndr.extra.hashgrid -import org.openrndr.math.Vector3 -import org.openrndr.shape.Box -import kotlin.jvm.JvmRecord -import kotlin.math.abs -import kotlin.math.max -import kotlin.math.min -import kotlin.math.sqrt -import kotlin.random.Random - -private fun Double.fastFloor(): Int { - return if (this >= 0) this.toInt() else this.toInt() - 1 -} - -@JvmRecord -private data class GridCoords3D(val x: Int, val y: Int, val z: Int) { - fun offset(i: Int, j: Int, k : Int): GridCoords3D = copy(x = x + i, y = y + j, z = z + k) -} - -/** - * Represents a 3D cell with a fixed size in a spatial hash grid structure. A `Cell3D` is aligned - * along a grid using its integer coordinates and supports operations to manage points within - * its bounds, calculate distances to a query point, and retrieve its own bounding boxes. - * - * @property x The x-coordinate of the cell within the grid. - * @property y The y-coordinate of the cell within the grid. - * @property z The z-coordinate of the cell within the grid. - * @property cellSize The size of the cell in all dimensions. - */ -class Cell3D(val x: Int, val y: Int, val z: Int, val cellSize: Double) { - var xMin: Double = Double.POSITIVE_INFINITY - private set - var xMax: Double = Double.NEGATIVE_INFINITY - private set - var yMin: Double = Double.POSITIVE_INFINITY - private set - var yMax: Double = Double.NEGATIVE_INFINITY - private set - var zMin: Double = Double.POSITIVE_INFINITY - private set - var zMax: Double = Double.NEGATIVE_INFINITY - private set - - /** - * Represents the 3D bounding box of the cell. - * - * The bounds are calculated based on the cell's position (`x`, `y`, `z`) and - * the uniform size of the cell (`cellSize`). It defines a cuboid in 3D space - * with its origin at `(x * cellSize, y * cellSize, z * cellSize)` and dimensions - * defined by `cellSize` along all three axes. - * - * @return A `Box` representing the spatial boundary of the cell. - */ - val bounds: Box - get() { - return Box(Vector3(x * cellSize, y * cellSize, z * cellSize), cellSize, cellSize, cellSize) - } - - /** - * Provides the bounding 3D box that contains all the points within the cell. - * If the `points` collection is empty, it returns an empty box. Otherwise, - * it calculates the bounding box based on the minimum and maximum coordinates - * of the stored points (`xMin`, `xMax`, `yMin`, `yMax`, `zMin`, `zMax`). - */ - val contentBounds: Box - get() { - return if (points.isEmpty()) { - Box.EMPTY - } else { - Box(Vector3(xMin, yMin, zMin), xMax - xMin, yMax - yMin, zMax - zMin) - } - } - - internal val points = mutableListOf>() - internal fun insert(point: Vector3, owner: Any?) { - points.add(Pair(point, owner)) - xMin = min(xMin, point.x) - xMax = max(xMax, point.x) - yMin = min(yMin, point.y) - yMax = max(yMax, point.y) - zMin = min(zMin, point.z) - zMax = max(zMax, point.z) - } - - internal fun squaredDistanceTo(query: Vector3): Double { - val width = xMax - xMin - val height = yMax - yMin - val depth = zMax - zMin - val x = (xMin + xMax) / 2.0 - val y = (yMin + yMax) / 2.0 - val z = (zMin + zMax) / 2.0 - val dx = max(abs(query.x - x) - width / 2, 0.0) - val dy = max(abs(query.y - y) - height / 2, 0.0) - val dz = max(abs(query.z - z) - depth / 2, 0.0) - return dx * dx + dy * dy + dz * dz - } - - /** - * Generates a sequence of all the points stored in the `points` collection. - * - * This method iterates over the `points` collection and yields each element. - * Useful for lazily accessing the points in the order they are stored. - * - * @return A sequence of points contained within the `points` collection. - */ - fun points() = sequence { - for (point in points) { - yield(point) - } - } -} - -/** - * Represents a 3D Hash Grid structure used for spatial partitioning of points in 3D space. - * This structure organizes points into grid-based cells, enabling efficient spatial querying - * and insertion operations. - * - * @property radius The radius used to determine proximity checks within the grid. - * Points are considered neighbors if their spatial distance is less than or equal to this radius. - */ -class HashGrid3D(val radius: Double) { - private val cells = mutableMapOf() - - - /** - * Returns a sequence of all the cells present in the hash grid. - * Each cell is yielded individually from the internal mapping. - */ - fun cells() = sequence { - for (cell in cells.values) { - yield(cell) - } - } - - /** - * Represents the total number of points currently stored in the hash grid. - * This property is incremented whenever a new point is inserted into the grid. - * It's read-only for external access and cannot be modified outside the class. - */ - var size: Int = 0 - private set - - /** - * The size of a single cell in the 3D hash grid. - * - * The cell size is computed as the radius of the grid divided by the square root of 3, - * which ensures that the cell dimensions are scaled appropriately in a 3D space. - * This value influences the spatial resolution of the grid and determines - * how points are grouped into cells during computations such as insertion or querying. - */ - val cellSize = radius / sqrt(3.0) - private fun coords(v: Vector3): GridCoords3D { - val x = (v.x / cellSize).fastFloor() - val y = (v.y / cellSize).fastFloor() - val z = (v.z / cellSize).fastFloor() - return GridCoords3D(x, y, z) - } - - /** - * Returns a sequence of all points contained in the hash grid. - * - * Iterates over all cells in the grid and yields each contained point. - * Each point is represented as a value yielded by the sequence. - * - * @return A sequence of all points stored in the hash grid. - */ - fun points() = sequence { - for (cell in cells.values) { - for (point in cell.points) { - yield(point) - } - } - } - - /** - * Selects a random 3D vector from the points stored in the hash grid. - * - * @param random A random number generator to use for selection. Defaults to `Random.Default`. - * @return A randomly selected `Vector3` from the hash grid. - */ - fun random(random: Random = Random.Default): Vector3 { - return cells.values.random(random).points.random().first - } - - fun insert(point: Vector3, owner: Any? = null) { - val gc = coords(point) - val cell = cells.getOrPut(gc) { Cell3D(gc.x, gc.y, gc.z, cellSize) } - cell.insert(point, owner) - size += 1 - } - - /** - * Retrieves the 3D cell corresponding to the given query point in the spatial hash grid. - * - * This method computes the grid coordinates of the query vector and attempts to fetch - * the corresponding cell from the internal cell mapping. - * - * @param query A `Vector3` object representing the point used to locate the corresponding cell. - * @return A `Cell3D` object if a cell exists for the given query point, or `null` if no such cell is found. - */ - fun cell(query: Vector3): Cell3D? = cells[coords(query)] - - /** - * Determines whether a specific point in the 3D grid is free, considering the proximity - * to other points and optionally ignoring specified owners. - * - * @param query The `Vector3` representing the point to check for availability. - * @param ignoreOwners A set of owners to ignore during the proximity check. Default is an empty set. - * @return `true` if the point is considered free or not occupied; otherwise, `false`. - */ - fun isFree(query: Vector3, ignoreOwners: Set = emptySet()): Boolean { - val c = coords(query) - if (cells[c] == null) { - for (k in -2..2) { - for (j in -2..2) { - for (i in -2..2) { - if (i == 0 && j == 0 && k == 0) { - continue - } - val n = c.offset(i, j, k) - val nc = cells[n] - if (nc != null && nc.squaredDistanceTo(query) <= radius * radius) { - for (p in nc.points) { - if (p.second == null || p.second !in ignoreOwners) { - if (p.first.squaredDistanceTo(query) <= radius * radius) { - return false - } - } - } - } - } - } - } - return true - } else { - return cells[c]!!.points.all { it.second != null && it.second in ignoreOwners } - } - } -} - -/** - * Construct a hash grid containing all points in the list - * @param radius radius of the hash grid - */ -fun List.hashGrid(radius: Double): HashGrid3D { - val grid = HashGrid3D(radius) - for (point in this) { - grid.insert(point) - } - return grid -} - -/** - * Return a list that only contains points at a minimum distance. - * @param radius the minimum distance between any two points in the returned list - */ -fun List.filter(radius: Double): List { - return if (size <= 1) { - this - } else { - val grid = HashGrid3D(radius) - for (point in this) { - if (grid.isFree(point)) { - grid.insert(point) - } - } - grid.points().map { it.first }.toList() - } -} \ No newline at end of file diff --git a/orx-hash-grid/src/jvmDemo/kotlin/DemoFilter01.kt b/orx-hash-grid/src/jvmDemo/kotlin/DemoFilter01.kt deleted file mode 100644 index 188f5cd4..00000000 --- a/orx-hash-grid/src/jvmDemo/kotlin/DemoFilter01.kt +++ /dev/null @@ -1,30 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.hashgrid.filter -import org.openrndr.extra.noise.shapes.uniform -import kotlin.random.Random - -/** A demo to generate and display filtered random points. - * - * The program performs the following steps: - * - Generates 10,000 random points uniformly distributed within the drawable bounds. - * - Filters the generated points to enforce a minimum distance of 20.0 units between them. - * - Visualizes the filtered points as circles with a radius of 10.0 units on the canvas. - * - * The `filter` method is provided by `orx-hash-grid`. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val r = Random(0) - val points = (0 until 10000).map { - drawer.bounds.uniform(random = r) - } - val filteredPoints = points.filter(20.0) - extend { - drawer.circles(filteredPoints, 10.0) - } - } -} diff --git a/orx-hash-grid/src/jvmDemo/kotlin/DemoFilter3D01.kt b/orx-hash-grid/src/jvmDemo/kotlin/DemoFilter3D01.kt deleted file mode 100644 index 5713bba4..00000000 --- a/orx-hash-grid/src/jvmDemo/kotlin/DemoFilter3D01.kt +++ /dev/null @@ -1,53 +0,0 @@ -import org.openrndr.WindowMultisample -import org.openrndr.application -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.isolated -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.hashgrid.filter -import org.openrndr.extra.meshgenerators.sphereMesh -import org.openrndr.extra.noise.uniformRing -import org.openrndr.math.Vector3 -import kotlin.random.Random - -/** - * Demonstrates how to use a 3D hash-grid `filter` operation to remove points from a random 3D point-collection - * that are too close to each other. The resulting points are displayed as small spheres. - * - * The program performs the following key steps: - * - Generates 10,000 random 3D points located between a minimum and maximum radius. - * - Filters the points to ensure a minimum distance between any two points using a spatial hash grid. - * - Creates a small sphere mesh that will be instanced for each filtered point. - * - Sets up an orbital camera to allow viewing the 3D scene interactively. - * - Renders the filtered points by translating the sphere mesh to each point's position and applying a shader that modifies the fragment color based on the view normal. - */ -fun main() = application { - configure { - width = 720 - height = 720 - multisample = WindowMultisample.SampleCount(4) - } - program { - val r = Random(0) - val points = (0 until 10000).map { - Vector3.uniformRing(0.0, 10.0, r) - } - val sphere = sphereMesh(radius = 0.25) - val filteredPoints = points.filter(0.5) - - extend(Orbital()) { - eye = Vector3(0.0, 0.0, 15.0) - } - extend { - drawer.shadeStyle = shadeStyle { - fragmentTransform = """x_fill.rgb *= abs(v_viewNormal.z);""" - } - for (point in filteredPoints) { - drawer.isolated { - drawer.translate(point) - drawer.vertexBuffer(sphere, DrawPrimitive.TRIANGLES) - } - } - } - } -} \ No newline at end of file diff --git a/orx-hash-grid/src/jvmDemo/kotlin/DemoHashGrid01.kt b/orx-hash-grid/src/jvmDemo/kotlin/DemoHashGrid01.kt deleted file mode 100644 index 45d8be0e..00000000 --- a/orx-hash-grid/src/jvmDemo/kotlin/DemoHashGrid01.kt +++ /dev/null @@ -1,42 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.hashgrid.HashGrid -import org.openrndr.extra.noise.shapes.uniform -import kotlin.random.Random - -/** - * This demo creates a `HashGrid` to manage points in a 2D space. - * Notice the desired cell size in the HashGrid constructor. - * - * On every animation frame, it attempts to insert 100 random points into the HashGrid. - * When a HashGrid cell is free, a point is inserted. - * - * The visual output includes: - * - Rectangles representing the bounds of the occupied cells in the grid. - * - Circles representing the generated random points. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val r = Random(0) - val hashGrid = HashGrid(72.0) - - extend { - for (i in 0 until 100) { - val p = drawer.bounds.uniform(random = r) - if (hashGrid.isFree(p)) { - hashGrid.insert(p) - } - } - - drawer.fill = null - drawer.stroke = ColorRGBa.WHITE - drawer.rectangles(hashGrid.cells().map { it.bounds }.toList()) - drawer.stroke = ColorRGBa.PINK - drawer.circles(hashGrid.points().map { it.first }.toList(), 36.0) - } - } -} diff --git a/orx-image-fit/README.md b/orx-image-fit/README.md deleted file mode 100644 index faab99dd..00000000 --- a/orx-image-fit/README.md +++ /dev/null @@ -1,103 +0,0 @@ -# orx-image-fit - -Draws an image ensuring it fits or covers the specified `Rectangle`. - -Similar to CSS object-fit (https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) - -`orx-image-fit` provides the `Drawer.imageFit` extension function. - -## Usage - -```kotlin -drawer.imageFit( - img: ColorBuffer, - x: Double, y: Double, w: Double, h: Double, - horizontalPosition: Double, - verticalPosition: Double, - fitMethod: FitMethod) -``` - -or - -```kotlin -drawer.imageFit( - img: ColorBuffer, - bounds: Rectangle, - horizontalPosition: Double, - verticalPosition: Double, - fitMethod: FitMethod) -``` - -- `img`: the image to draw -- `x`, `y`, `w`, `h` or `bounds`: the target area where to draw the image -- `fitMethod`: - - `FitMethod.Contain`: fits `img` in the target area. If the aspect ratio of `img` and `bounds` differ it leaves blank horizontal or vertical margins to avoid deforming the image. - - `FitMethod.Cover`: covers the target area. . If the aspect ratio of `img` and `bounds` differ part of the image will be cropped away. - - `FitMethod.Fill`: deforms the image to exactly match the target area. - - `FitMethod.None`: draws the image on the target area without scaling it. -- `horizontalPosition` and `verticalPosition`: controls which part of the image is visible (`Cover`, `None`) or the alignment of the image (`Contain`). - - `horizontalPosition`: `-1.0` = left, `0.0` = center, `1.0` = right. - - `verticalPosition`: `-1.0` = top, `0.0` = center, `1.0` = bottom. - -## Examples - -A quick example that fits an image to the window rectangle with a 10 pixel margin. By default -`imageFit` uses the cover mode, which fills the target rectangle with an image. - -```kotlin -fun main() = application { - program { - val image = loadImage("data/images/pm5544.png") - extend { - drawer.imageFit(image, 10.0, 10.0, width - 20.0, height - 20.0) - } - } -} -``` - -or - -```kotlin -fun main() = application { - program { - val image = loadImage("data/images/pm5544.png") - extend { - drawer.imageFit(image, drawer.bounds.offsetEdges(-10.0)) - } - } -} -``` - - -## Demos -### DemoImageFit01 - -This program uses `drawer.imageFit()` to draw images using nested grid layout. -The main grid features 4 columns for the `Cover`, `Contain`, `Fill` and `None` fit methods, -and two rows for portrait and landscape images. -Each of those 8 cells feature a 3x3 grid, with cells combining `left`, `center` and `right` alignment -with `top`, `center` and `bottom` alignment. - -The image drawn in each cell is a simple image with a white background and two touching circles: -a pink one and a gray one. In some of the cells part of this image is cropped out (due to the fit method used). -In other cells the image does not fully cover the available area, revealing a dark gray background. - -![DemoImageFit01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-image-fit/images/DemoImageFit01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoImageFit01.kt) - -### DemoImageFitSub01 - -Demonstrates the `imageFitSub()` method, which allows specifying not only a target `Rectangle`, -but also a source `Rectangle`, which is used to set the area of the original image we want to fit -into the target. - -The program also demonstrates the `Rectangle.uniformSub` method, which returns a random sub-rectangle -taking into consideration the minimum and maximum width and height arguments. - -Notice the trick used to generate unique random results changing only once per second by using -the current seconds as an integer seed. - -![DemoImageFitSub01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-image-fit/images/DemoImageFitSub01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoImageFitSub01.kt) diff --git a/orx-image-fit/build.gradle.kts b/orx-image-fit/build.gradle.kts deleted file mode 100644 index 3a84ef1c..00000000 --- a/orx-image-fit/build.gradle.kts +++ /dev/null @@ -1,28 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") -} - -kotlin { - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - implementation(project(":orx-parameters")) - implementation(project(":orx-shader-phrases")) - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - } - } - - @Suppress("UNUSED_VARIABLE") - val jvmDemo by getting { - dependencies { - implementation(project(":orx-shapes")) - implementation(project(":orx-image-fit")) - implementation(project(":orx-noise")) - } - } - } -} diff --git a/orx-image-fit/src/commonMain/kotlin/ImageFit.kt b/orx-image-fit/src/commonMain/kotlin/ImageFit.kt deleted file mode 100644 index 7dcdc5fb..00000000 --- a/orx-image-fit/src/commonMain/kotlin/ImageFit.kt +++ /dev/null @@ -1,187 +0,0 @@ -package org.openrndr.extra.imageFit - -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.Drawer -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector2 -import org.openrndr.math.transforms.transform -import org.openrndr.shape.Rectangle -import kotlin.math.max -import kotlin.math.min - -/** - * Available `object-fit` methods (borrowed from CSS) - */ -enum class FitMethod { - /** Cover target area. Crop the source image if needed. */ - Cover, - - /** Fit image in target area. Add margins if needed. */ - Contain, - - /** Deform source image to match the target area. */ - Fill, - - /** Maintain original image scale, crop to target area size. */ - None - - /** Not implemented */ - // ScaleDown -} - -/** - * Transforms [src] and [dest] into a Pair in which one of the - * two rectangles is modified to conform with the [fitMethod]. It uses - * [horizontalPosition] and [verticalPosition] to control positioning / cropping. - */ -fun fitRectangle( - src: Rectangle, - dest: Rectangle, - horizontalPosition: Double = 0.0, - verticalPosition: Double = 0.0, - fitMethod: FitMethod = FitMethod.Cover -): Pair { - val positionNorm = Vector2(horizontalPosition, verticalPosition) * 0.5 + 0.5 - val (scaleX, scaleY) = dest.dimensions / src.dimensions - - return when (fitMethod) { - FitMethod.Cover -> { - val actualDimensions = dest.dimensions / max(scaleX, scaleY) - val actualSrc = Rectangle( - src.corner + (src.dimensions - actualDimensions) * positionNorm, - actualDimensions.x, actualDimensions.y - ) - Pair(actualSrc, dest) - } - - FitMethod.Contain -> { - val actualDimensions = src.dimensions * min(scaleX, scaleY) - val actualDest = Rectangle( - dest.corner + (dest.dimensions - actualDimensions) * positionNorm, - actualDimensions.x, actualDimensions.y - ) - Pair(src, actualDest) - } - - FitMethod.Fill -> Pair(src, dest) - FitMethod.None -> { - val actualSrc = Rectangle( - src.corner + (src.dimensions - dest.dimensions) * positionNorm, - dest.width, dest.height - ) - Pair(actualSrc, dest) - } - } -} - -/** - * Helper function that calls [fitRectangle] and returns a [Matrix44] instead - * of a `Pair`. The returned matrix can be used to draw - * scaled `Shape` or `ShapeContour` objects. - * - * Example scaling and centering a collection of ShapeContours inside - * `drawer.bounds` leaving a margin of 50 pixels: - * - * val src = shapeContours.map { it.bounds }.bounds - * val dest = drawer.bounds.offsetEdges(-50.0) - * val mat = src.fit(dest, fitMethod = FitMethod.Contain) - * drawer.view *= mat - * drawer.contours(shapeContours) - */ -fun Rectangle.fit( - dest: Rectangle, - horizontalPosition: Double = 0.0, - verticalPosition: Double = 0.0, - fitMethod: FitMethod = FitMethod.Cover -): Matrix44 { - val (source, target) = fitRectangle( - this, - dest, - horizontalPosition, - verticalPosition, - fitMethod - ) - return transform { - translate(target.corner) - scale((target.dimensions / source.dimensions).vector3(z = 1.0)) - translate(-source.corner) - } -} - -/** - * Draws [img] into the bounding box defined by [x], [y], [width] and [height] - * using the specified [fitMethod] - * and aligned or cropped using [horizontalPosition] and [verticalPosition]. - */ -fun Drawer.imageFit( - img: ColorBuffer, - x: Double = 0.0, - y: Double = 0.0, - width: Double = img.width.toDouble(), - height: Double = img.height.toDouble(), - horizontalPosition: Double = 0.0, - verticalPosition: Double = 0.0, - fitMethod: FitMethod = FitMethod.Cover -) = imageFit( - img, - Rectangle(x, y, width, height), - horizontalPosition, - verticalPosition, - fitMethod -) - -/** - * Draws [img] into the bounding box defined by [bounds] - * using the specified [fitMethod] - * and aligned or cropped using [horizontalPosition] and [verticalPosition]. - */ -fun Drawer.imageFit( - img: ColorBuffer, - bounds: Rectangle = img.bounds, - horizontalPosition: Double = 0.0, - verticalPosition: Double = 0.0, - fitMethod: FitMethod = FitMethod.Cover -): Pair { - val (source, target) = fitRectangle( - img.bounds, - bounds, - horizontalPosition, - verticalPosition, - fitMethod - ) - - image(img, source, target) - return Pair(source, target) -} - -/** - * Draws a subsection of the given image into a target rectangle within the current `Drawer` bounds, - * using the specified fit method and alignment. - * - * @param img The `ColorBuffer` representing the image to draw. - * @param source The subsection of the image to be fitted, defined as a `Rectangle`. Defaults to the full bounds of the image. - * @param target The rectangle within the `Drawer` bounds where the image will be drawn. Defaults to the full bounds of the `Drawer`. - * @param horizontalPosition Horizontal alignment or cropping position for the image as a normalized value from -1.0 to 1.0. - * @param verticalPosition Vertical alignment or cropping position for the image as a normalized value from -1.0 to 1.0. - * @param fitMethod The method to use for fitting the image into the target rectangle. Defaults to `FitMethod.Cover`. - * @return A `Pair` of `Rectangle` objects, where the first element is the transformed source rectangle, and the second element is the target rectangle. - */ -fun Drawer.imageFitSub( - img: ColorBuffer, - source: Rectangle = img.bounds, - target: Rectangle = this.bounds, - horizontalPosition: Double = 0.0, - verticalPosition: Double = 0.0, - fitMethod: FitMethod = FitMethod.Cover -): Pair { - val (fitSource, fitTarget) = fitRectangle( - source, - target, - horizontalPosition, - verticalPosition, - fitMethod - ) - - image(img, fitSource, fitTarget) - return Pair(source, target) -} \ No newline at end of file diff --git a/orx-image-fit/src/jvmDemo/kotlin/DemoImageFit01.kt b/orx-image-fit/src/jvmDemo/kotlin/DemoImageFit01.kt deleted file mode 100644 index 3f64a980..00000000 --- a/orx-image-fit/src/jvmDemo/kotlin/DemoImageFit01.kt +++ /dev/null @@ -1,72 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.loadFont -import org.openrndr.drawImage -import org.openrndr.extra.imageFit.FitMethod -import org.openrndr.extra.imageFit.imageFit -import org.openrndr.extra.shapes.primitives.grid - -/** - * This program uses `drawer.imageFit()` to draw images using nested grid layout. - * The main grid features 4 columns for the `Cover`, `Contain`, `Fill` and `None` fit methods, - * and two rows for portrait and landscape images. - * Each of those 8 cells feature a 3x3 grid, with cells combining `left`, `center` and `right` alignment - * with `top`, `center` and `bottom` alignment. - * - * The image drawn in each cell is a simple image with a white background and two touching circles: - * a pink one and a gray one. In some of the cells part of this image is cropped out (due to the fit method used). - * In other cells the image does not fully cover the available area, revealing a dark gray background. - */ -fun main() = application { - configure { - width = 1600 - height = 900 - } - - program { - val font = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 18.0) - - // Create an image with a gray and a pink circle - fun makeImage(cols: Int, rows: Int, side: Int = 400) = drawImage(cols * side, rows * side) { - clear(ColorRGBa.WHITE) - stroke = null - bounds.grid(cols, rows).flatten().forEachIndexed { i, it -> - fill = if (i % 2 == 0) ColorRGBa.PINK else ColorRGBa.GRAY - circle(it.center, side / 2.0) - } - } - - val namedImages = mapOf( - "portrait" to makeImage(1, 2), - "landscape" to makeImage(2, 1) - ) - val fitMethods = FitMethod.entries.toTypedArray() - - val grid = drawer.bounds.grid(fitMethods.size, namedImages.size, 30.0, 30.0, 30.0, 30.0) - - extend { - drawer.fontMap = font - drawer.stroke = null - fitMethods.forEachIndexed { y, fitMethod -> - namedImages.entries.forEachIndexed { x, (layoutName, img) -> - val cell = grid[x][y] - // In each grid cell draw 9 fitted images combining - // [left, center, right] and [top, center, bottom] alignment - val subgrid = cell.grid(3, 3, 0.0, 0.0, 4.0, 4.0) - subgrid.forEachIndexed { yy, rects -> - rects.forEachIndexed { xx, rect -> - // Draw a dark background - drawer.fill = ColorRGBa.WHITE.shade(0.25) - drawer.rectangle(rect) - - // Draw the image using `imageFit` - drawer.imageFit(img, rect, xx - 1.0, yy - 1.0, fitMethod) - } - } - drawer.fill = ColorRGBa.WHITE - drawer.text("${fitMethod.name}, $layoutName", cell.position(0.0, 1.038).toInt().vector2) - } - } - } - } -} \ No newline at end of file diff --git a/orx-image-fit/src/jvmDemo/kotlin/DemoImageFitSub01.kt b/orx-image-fit/src/jvmDemo/kotlin/DemoImageFitSub01.kt deleted file mode 100644 index ad1bc630..00000000 --- a/orx-image-fit/src/jvmDemo/kotlin/DemoImageFitSub01.kt +++ /dev/null @@ -1,39 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.loadImage -import org.openrndr.extra.imageFit.imageFitSub - -import org.openrndr.extra.noise.shapes.uniformSub -import org.openrndr.extra.shapes.primitives.grid -import kotlin.random.Random - -/** - * Demonstrates the `imageFitSub()` method, which allows specifying not only a target `Rectangle`, - * but also a source `Rectangle`, which is used to set the area of the original image we want to fit - * into the target. - * - * The program also demonstrates the `Rectangle.uniformSub` method, which returns a random sub-rectangle - * taking into consideration the minimum and maximum width and height arguments. - * - * Notice the trick used to generate unique random results changing only once per second by using - * the current seconds as an integer seed. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val image = loadImage("demo-data/images/image-001.png") - extend { - val grid = drawer.bounds.grid(5, 5).flatten() - val r = Random(seconds.toInt()) - for (cell in grid) { - drawer.imageFitSub( - image, - image.bounds.uniformSub(0.25, 0.75, 0.25, 0.75, random = r), - cell - ) - } - } - } -} \ No newline at end of file diff --git a/orx-integral-image/README.md b/orx-integral-image/README.md deleted file mode 100644 index 085b5680..00000000 --- a/orx-integral-image/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# orx-integral-image - -CPU and GPU-based implementation for integral images (summed area tables) - - -## Demos -### DemoFII01 - -Apply box blurs with large windows - -![DemoFII01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-integral-image/images/DemoFII01Kt.png) - -[source code](src/demo/kotlin/DemoFII01.kt) - -### DemoFII02 - -Implement an FM like video synthesizer using [FastIntegralImage] - -![DemoFII02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-integral-image/images/DemoFII02Kt.png) - -[source code](src/demo/kotlin/DemoFII02.kt) diff --git a/orx-integral-image/build.gradle.kts b/orx-integral-image/build.gradle.kts deleted file mode 100644 index a2404108..00000000 --- a/orx-integral-image/build.gradle.kts +++ /dev/null @@ -1,10 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(project(":orx-fx")) - implementation(openrndr.application.core) - implementation(openrndr.math) - demoImplementation(project(":orx-image-fit")) -} \ No newline at end of file diff --git a/orx-integral-image/src/demo/kotlin/DemoFII01.kt b/orx-integral-image/src/demo/kotlin/DemoFII01.kt deleted file mode 100644 index 82408ad1..00000000 --- a/orx-integral-image/src/demo/kotlin/DemoFII01.kt +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Apply box blurs with large windows - */ - -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.integralimage.* - -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val fii = FastIntegralImage() - val integralImage = colorBuffer(width, height, 1.0, ColorFormat.RGBa, ColorType.FLOAT32) - val rt = renderTarget(width, height) { - colorBuffer() - } - extend { - drawer.clear(ColorRGBa.PINK) - drawer.isolatedWithTarget(rt) { - drawer.ortho(rt) - drawer.clear(ColorRGBa.BLACK) - drawer.fill = ColorRGBa.PINK.shade(1.0) - drawer.circle(mouse.position, 128.0) - } - fii.apply(rt.colorBuffer(0), integralImage) - - // -- here we sample from the integral image - drawer.shadeStyle = shadeStyle { - fragmentTransform = """ - float w = 64.0; - vec2 step = 1.0 / vec2(textureSize(image, 0)); - vec4 t11 = texture(image, va_texCoord0 + step * vec2(w+1.0,w+1.0)); - vec4 t01 = texture(image, va_texCoord0 + step * vec2(-w,w+1.0)); - vec4 t00 = texture(image, va_texCoord0 + step * vec2(-w,-w)); - vec4 t10 = texture(image, va_texCoord0 + step * vec2(w+1.0,-w)); - x_fill = (t11 - t01 - t10 + t00) / ((2.0 * w +1.0) * (2.0 * w + 1.0)); - """.trimIndent() - } - drawer.image(integralImage) - } - } -} \ No newline at end of file diff --git a/orx-integral-image/src/demo/kotlin/DemoFII02.kt b/orx-integral-image/src/demo/kotlin/DemoFII02.kt deleted file mode 100644 index 30ebf96b..00000000 --- a/orx-integral-image/src/demo/kotlin/DemoFII02.kt +++ /dev/null @@ -1,85 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.imageFit.imageFit -import org.openrndr.extra.integralimage.* -import kotlin.math.PI - -/** - * Implement an FM like video synthesizer using [FastIntegralImage] - */ - -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - - val image = loadImage("demo-data/images/image-001.png") - val fii = FastIntegralImage() - val integralImage = colorBuffer(width, height, 1.0, ColorFormat.RGBa, ColorType.FLOAT32) - val rt = renderTarget(width, height) { - colorBuffer() - } - extend { - drawer.clear(ColorRGBa.PINK) - - /* - Draw an input image - */ - drawer.isolatedWithTarget(rt) { - drawer.ortho(rt) - drawer.clear(ColorRGBa.BLACK) - drawer.imageFit(image, drawer.bounds) - drawer.fill = ColorRGBa.PINK.shade(1.0) - drawer.circle(mouse.position, 256.0) - } - - /* - Update the integral image - */ - fii.apply(rt.colorBuffer(0), integralImage) - - - /* - Use a shade style to sample from the integral image - */ - - drawer.shadeStyle = shadeStyle { - fragmentPreamble = """ - vec3 linePhase(vec2 uv) { - vec2 step = 1.0 / vec2(textureSize(image, 0)); - vec4 t11 = texture(image, uv + step * vec2(1.0,1.0)); - vec4 t01 = texture(image, vec2(0.0, uv.y) + step * vec2(0,1.0)); - vec4 t00 = texture(image, vec2(0.0, uv.y)); - vec4 t10 = texture(image, uv + step * vec2(1.0, 0.0)); - vec4 r = (t11 - t01 - t10 + t00); - return r.xyz; - } - - """.trimIndent() - - fragmentTransform = """ - vec2 s = 1.0 / vec2(textureSize(image, 0)); - - float spread = 1.0; - - vec3 phase0 = linePhase(va_texCoord0 + s * vec2(-spread, 0.0)); - vec3 phase1 = linePhase(va_texCoord0); - - float carrierFreq = 40.0 * 2.0 * ${PI}; - float carrierPhase = va_texCoord0.x + va_texCoord0.y; - float signalFreq = s.x * 100.0 * 2.0 * ${PI}; - - vec3 mo0 = cos(phase0 * signalFreq + carrierPhase * carrierFreq); - vec3 mo1 = cos(phase1 * signalFreq + (carrierPhase - s.x * spread) * carrierFreq); - - x_fill.rgb = (mo1 - mo0) * 2.0; - x_fill.a = 1.0; - """.trimIndent() - } - drawer.image(integralImage) - } - } -} \ No newline at end of file diff --git a/orx-integral-image/src/main/kotlin/FastIntegralImage.kt b/orx-integral-image/src/main/kotlin/FastIntegralImage.kt deleted file mode 100644 index 32c986d6..00000000 --- a/orx-integral-image/src/main/kotlin/FastIntegralImage.kt +++ /dev/null @@ -1,146 +0,0 @@ -package org.openrndr.extra.integralimage - -import org.openrndr.draw.* -import org.openrndr.extra.fx.blend.Passthrough - -import org.openrndr.math.Vector2 -import org.openrndr.resourceUrl -import org.openrndr.shape.IntRectangle -import org.openrndr.shape.Rectangle -import kotlin.math.ceil -import kotlin.math.log - - -internal class FastIntegralImageFilter : Filter( - filterShaderFromUrl( - resourceUrl( - "/shaders/gl3/integral-image.frag" - ) - ) -) { - var passIndex: Int by parameters - var passDirection: Vector2 by parameters - var sampleCount: Int by parameters - var sampleCountBase: Int by parameters -} - -/** - * Compute an integral image for the source image - */ -class FastIntegralImage : Filter( - filterShaderFromUrl( - resourceUrl( - "/shaders/gl3/integral-image.frag" - ) - ) -) { - private val passthrough = Passthrough() - - var intermediate: ColorBuffer? = null - var sourceCropped: ColorBuffer? = null - var targetPadded: ColorBuffer? = null - private val filter = FastIntegralImageFilter() - - private fun sampleCounts(size: Int, sampleCountBase: Int): List { - var remainder = size - val sampleCounts = mutableListOf() - while (remainder > 0) { - sampleCounts += if (remainder >= sampleCountBase) { - sampleCountBase - } else { - remainder - } - remainder /= sampleCountBase - } - return sampleCounts - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - require(source[0].isEquivalentTo(target[0], ignoreFormat = true, ignoreType = true)) - - val npotx = ceil(log(source[0].effectiveWidth.toDouble(), 2.0)).toInt() - val npoty = ceil(log(source[0].effectiveHeight.toDouble(), 2.0)).toInt() - - val recWidth = 1 shl npotx - val recHeight = 1 shl npoty - - if (recWidth != source[0].effectiveWidth || recHeight != source[0].effectiveHeight) { - if (sourceCropped?.effectiveWidth != recWidth || sourceCropped?.effectiveHeight != recHeight) { - sourceCropped?.destroy() - targetPadded?.destroy() - } - - if (sourceCropped == null) { - sourceCropped = source[0].createEquivalent(width = recWidth, height = recHeight, contentScale = 1.0) - targetPadded = target[0].createEquivalent( - width = (recWidth / target[0].contentScale).toInt(), - height = (recHeight / target[0].contentScale).toInt(), - contentScale = 1.0 - ) - } - source[0].copyTo(sourceCropped!!, - sourceRectangle = IntRectangle(0, 0, source[0].effectiveWidth, source[0].effectiveHeight), - targetRectangle = IntRectangle(0, recHeight-source[0].effectiveHeight, source[0].effectiveWidth, source[0].effectiveHeight) - ) - } - - val sampleCountBase = 16 - val xSampleCounts = sampleCounts(recWidth, sampleCountBase) - val ySampleCounts = sampleCounts(recHeight, sampleCountBase) - - val li = intermediate - if (li == null || (li.effectiveWidth != recWidth || li.effectiveHeight != recHeight)) { - intermediate?.destroy() - intermediate = colorBuffer(recWidth, recHeight, 1.0, ColorFormat.RGBa, ColorType.FLOAT32) - } - - val targets = arrayOf(if (targetPadded == null) target else arrayOf(targetPadded!!), arrayOf(intermediate!!)) - - var targetIndex = 0 - - filter.sampleCountBase = sampleCountBase - - /* - Perform horizontal steps - */ - filter.passDirection = Vector2.UNIT_X - for (pass in xSampleCounts.indices) { - filter.sampleCount = xSampleCounts[pass] - filter.passIndex = pass - filter.apply( - if (pass == 0) { - if (sourceCropped == null) source else arrayOf(sourceCropped!!) - } else targets[targetIndex % 2], targets[(targetIndex + 1) % 2] - ) - targetIndex++ - } - - - /* - Perform vertical steps - */ - filter.passDirection = Vector2.UNIT_Y - for (pass in ySampleCounts.indices) { - filter.sampleCount = ySampleCounts[pass] - filter.passIndex = pass - filter.apply(targets[targetIndex % 2], targets[(targetIndex + 1) % 2]) - targetIndex++ - } - - // this is a bit wasteful - if (targetIndex % 2 == 1) { - passthrough.apply(targets[1], targets[0]) - } - - /* - When the source is not a power of two we copy from the padded target to the target - */ - if (targetPadded != null) { - targetPadded!!.copyTo(target[0], - sourceRectangle = IntRectangle(0, recHeight-source[0].effectiveHeight, source[0].effectiveWidth, source[0].effectiveHeight), - targetRectangle = IntRectangle(0, 0, source[0].effectiveWidth, source[0].effectiveHeight) - ) - } - } -} \ No newline at end of file diff --git a/orx-integral-image/src/main/kotlin/IntegralImage.kt b/orx-integral-image/src/main/kotlin/IntegralImage.kt deleted file mode 100644 index 4c1df7f2..00000000 --- a/orx-integral-image/src/main/kotlin/IntegralImage.kt +++ /dev/null @@ -1,80 +0,0 @@ -package studio.rndnr.packture - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorBufferShadow -import org.openrndr.shape.IntRectangle - -class IntegralImage(val width: Int, val height: Int, val integral: LongArray) { - - internal val maximum: Long - get() = integral[integral.size - 1] - - companion object { - fun fromColorBufferShadow(shadow: ColorBufferShadow, sampler: (ColorRGBa) -> Long = { (it.r * 255.0).toLong() }): IntegralImage { - val integral = LongArray(shadow.colorBuffer.width * shadow.colorBuffer.height) - - val width = shadow.colorBuffer.width - val height = shadow.colorBuffer.height - - for (i in integral.indices) { - integral[i] = 0 - } - - for (y in 0 until height) { - for (x in 0 until width) { - val i = sampler(shadow.read(x, y)) - - var i10: Long = 0 - if (x > 0) - i10 = integral[x - 1 + y * width] - - var i01: Long = 0 - if (y > 0) { - i01 = integral[x + (y - 1) * width] - } - - var i11: Long = 0 - if (y > 0 && x > 0) { - i11 = integral[x - 1 + (y - 1) * width] - } - - integral[y * width + x] = i + i10 + i01 - i11 - } - } - return IntegralImage(width, height, integral) - } - } - - - private fun clip(x: Int, left: Int, right: Int): Int { - return Math.min(right, Math.max(left, x)) - } - - fun sum(area: IntRectangle): Long { - return sum(area.x, area.y, area.x + area.width - 1, area.y + area.height - 1) - } - - private fun sum(left: Int, top: Int, right: Int, bottom: Int): Long { - var left = left - var top = top - var right = right - var bottom = bottom - top = clip(top, 0, height - 1) - bottom = clip(bottom, 0, height - 1) - - left = clip(left, 0, width - 1) - right = clip(right, 0, width - 1) - - val a = integral[left + top * width] - val b = integral[right + top * width] - val c = integral[right + bottom * width] - val d = integral[left + bottom * width] - - return a + c - b - d - } - - private fun average(left: Int, top: Int, right: Int, bottom: Int): Double { - val area = ((right - left) * (bottom - top)).toDouble() - return sum(left, top, right, bottom) / area - } -} \ No newline at end of file diff --git a/orx-integral-image/src/main/resources/shaders/gl3/integral-image.frag b/orx-integral-image/src/main/resources/shaders/gl3/integral-image.frag deleted file mode 100644 index ade32a47..00000000 --- a/orx-integral-image/src/main/resources/shaders/gl3/integral-image.frag +++ /dev/null @@ -1,26 +0,0 @@ -uniform sampler2D tex0; -in vec2 v_texCoord0; -out vec4 o_color; - -uniform int passIndex; -uniform int sampleCount; -uniform int sampleCountBase; -uniform vec2 passDirection; - - -void main() { - vec2 passOffset = vec2( - pow(float(sampleCountBase), - float(passIndex))) * (1.0 / vec2(textureSize(tex0, 0)) - ) * passDirection; - - vec2 uv0 = v_texCoord0; -// uv0.y = 1.0 - uv0.y; - vec4 result = vec4(0.0); - for (int i = 0; i < sampleCount; ++i) { - vec2 readUV = v_texCoord0 - vec2(float(i) * passOffset); - float factor = step(0.0, readUV.x) * step(0.0, readUV.y); - result += factor * texture(tex0, readUV); - } - o_color = result; -} \ No newline at end of file diff --git a/orx-interval-tree/README.md b/orx-interval-tree/README.md deleted file mode 100644 index bffad5db..00000000 --- a/orx-interval-tree/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# orx-interval-tree - -For querying a data set containing time segments (start time and end time) -when we need all entries containing a specific time value. Useful when creating a timeline. - -For more information on interval trees read the [wikipedia page](https://en.wikipedia.org/wiki/Interval_tree). - -## Usage - -```kotlin -// -- the item class we want to search for -class Item(val start: Double, val end: Double) - -// -- the items we want to search in -val items = List(100000) { Item(Math.random(), 1.0 + Math.random()) } - -// -- build the interval tree, note how buildIntervalTree accepts a function that returns the start and end of the interval. -val tree = buildIntervalTree(items) { - Pair(it.start, it.end) -} - -// -- search for all items that intersect 0.05 -val results = tree.queryPoint(0.05) -``` - diff --git a/orx-interval-tree/build.gradle.kts b/orx-interval-tree/build.gradle.kts deleted file mode 100644 index d695795a..00000000 --- a/orx-interval-tree/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} \ No newline at end of file diff --git a/orx-interval-tree/src/main/kotlin/IntervalTree.kt b/orx-interval-tree/src/main/kotlin/IntervalTree.kt deleted file mode 100644 index a6e17b72..00000000 --- a/orx-interval-tree/src/main/kotlin/IntervalTree.kt +++ /dev/null @@ -1,95 +0,0 @@ -class IntervalNode(val center: Double) { - val overlapBegin = mutableListOf>() - val overlapEnd = mutableListOf>() - - var left: IntervalNode? = null - var right: IntervalNode? = null - - fun queryPoint(x: Double): MutableList { - val results = mutableListOf() - queryPoint(x, results) - return results - } - - private fun queryPoint(x: Double, results: MutableList) { - if (x < center) { - for ((start, item) in overlapBegin) { - if (start <= x) { - results.add(item) - } else { - break - } - } - left?.queryPoint(x, results) - } else if (x > center) { - for ((end, item) in overlapEnd) { - if (end > x) { - results.add(item) - } else { - break - } - } - right?.queryPoint(x, results) - } else if (x == center) { - results.addAll(overlapBegin.map { it.second }) - } - } -} - - -fun buildIntervalTree(items: List, intervalFunction: (T) -> Pair): IntervalNode { - val ranges = items.map { intervalFunction(it) } - val center = ranges.sumOf { (it.first + it.second) / 2.0 } / ranges.size - val node = IntervalNode(center) - val leftItems = mutableListOf() - val rightItems = mutableListOf() - for (item in items) { - val interval = intervalFunction(item) - if (interval.first <= center && interval.second > center) { - node.overlapBegin.add(Pair(interval.first, item)) - node.overlapEnd.add(Pair(interval.second, item)) - } else if (interval.second <= center) { - leftItems.add(item) - } else if (interval.first > center) { - rightItems.add(item) - } - } - node.overlapBegin.sortBy { it.first } - node.overlapEnd.sortByDescending { it.first } - - if (leftItems.isNotEmpty()) { - node.left = buildIntervalTree(leftItems, intervalFunction) - } - if (rightItems.isNotEmpty()) { - node.right = buildIntervalTree(rightItems, intervalFunction) - } - return node -} - -fun time(f: () -> Unit) { - val start = System.currentTimeMillis() - f() - val end = System.currentTimeMillis() - println("that took ${end - start}ms") -} - -fun main() { - class Test(val start: Double, val end: Double) - - val items = List(100000) { Test(Math.random(), 1.0 + Math.random()) } - val root = buildIntervalTree(items) { - Pair(it.start, it.end) - } - - time { - for (i in 0 until 10000) { - val results = items.filter { it.start <= 0.05 && it.end > 0.05 } - } - } - - time { - for (i in 0 until 10000) { - root.queryPoint(0.05) - } - } -} \ No newline at end of file diff --git a/orx-jumpflood/README.md b/orx-jumpflood/README.md deleted file mode 100644 index 524d42b3..00000000 --- a/orx-jumpflood/README.md +++ /dev/null @@ -1,277 +0,0 @@ -# orx-jumpflood - -Calculates distance or direction fields from an image. -GPU accelerated, 2D. Results are provided as an image. - -[Original jump flooding algorithm](https://www.comp.nus.edu.sg/~tants/jfa.html) - -`orx-jumpflood` focusses on finding 2d distance and directional distance fields. - -## Distance field example - -`distanceFieldFromBitmap()` calculates distances to bitmap contours it stores -the distance in red and the original bitmap in green. - - -```kotlin -import org.openrndr.application -import org.openrndr.draw.* -import org.openrndr.extra.fx.blur.ApproximateGaussianBlur -import org.openrndr.extra.jumpfill.DistanceField -import org.openrndr.extra.jumpfill.Threshold -import org.openrndr.ffmpeg.VideoPlayerFFMPEG - -fun main() = application { - configure { - width = 1280 - height = 720 - } - - program { - val blurFilter = ApproximateGaussianBlur() - val blurred = colorBuffer(width, height) - - val thresholdFilter = Threshold() - val thresholded = colorBuffer(width, height) - - val distanceField = DistanceField() - val distanceFieldBuffer = colorBuffer(width, height, type = ColorType.FLOAT32) - - val videoCopy = renderTarget(width, height) { - colorBuffer() - } - val videoPlayer = VideoPlayerFFMPEG.fromDevice(imageWidth = width, imageHeight = height) - videoPlayer.play() - - extend { - // -- copy videoplayer output - drawer.isolatedWithTarget(videoCopy) { - drawer.ortho(videoCopy) - videoPlayer.draw(drawer) - } - - // -- blur the input a bit, this produces less noisy bitmap images - blurFilter.sigma = 9.0 - blurFilter.window = 18 - blurFilter.apply(videoCopy.colorBuffer(0), blurred) - - // -- threshold the blurred image - thresholdFilter.threshold = 0.5 - thresholdFilter.apply(blurred, thresholded) - - distanceField.apply(thresholded, distanceFieldBuffer) - - drawer.isolated { - // -- use a shadestyle to visualize the distance field - drawer.shadeStyle = shadeStyle { - fragmentTransform = """ - float d = x_fill.r; - if (x_fill.g > 0.5) { - x_fill.rgb = vec3(cos(d) * 0.5 + 0.5); - } else { - x_fill.rgb = 0.25 * vec3(1.0 - (cos(d) * 0.5 + 0.5)); - } - """ - } - drawer.image(distanceFieldBuffer) - } - } - } -} -``` - -## Direction field example - -`directionFieldFromBitmap()` calculates directions to bitmap contours it stores -x-direction in red, y-direction in green, and the original bitmap in blue. - - -```kotlin -import org.openrndr.application -import org.openrndr.draw.* -import org.openrndr.extra.fx.blur.ApproximateGaussianBlur -import org.openrndr.extra.jumpfill.DirectionalField -import org.openrndr.extra.jumpfill.Threshold -import org.openrndr.ffmpeg.VideoPlayerFFMPEG - -fun main() = application { - configure { - width = 1280 - height = 720 - } - - program { - val blurFilter = ApproximateGaussianBlur() - val blurred = colorBuffer(width, height) - - val thresholdFilter = Threshold() - val thresholded = colorBuffer(width, height) - - val directionField = DirectionalField() - val directionalFieldBuffer = colorBuffer(width, height, type = ColorType.FLOAT32) - - val videoPlayer = VideoPlayerFFMPEG.fromDevice(imageWidth = width, imageHeight = height) - videoPlayer.play() - - val videoCopy = renderTarget(width, height) { - colorBuffer() - } - - extend { - // -- copy videoplayer output - drawer.isolatedWithTarget(videoCopy) { - drawer.ortho(videoCopy) - videoPlayer.draw(drawer) - } - - // -- blur the input a bit, this produces less noisy bitmap images - blurFilter.sigma = 9.0 - blurFilter.window = 18 - blurFilter.apply(videoCopy.colorBuffer(0), blurred) - - // -- threshold the blurred image - thresholdFilter.threshold = 0.5 - thresholdFilter.apply(blurred, thresholded) - - directionField.apply(thresholded, directionalFieldBuffer) - - drawer.isolated { - // -- use a shadestyle to visualize the direction field - drawer.shadeStyle = shadeStyle { - fragmentTransform = """ - float a = atan(x_fill.r, x_fill.g); - if (x_fill.b > 0.5) { - x_fill.rgb = vec3(cos(a)*0.5+0.5, 1.0, sin(a)*0.5+0.5); - } else { - x_fill.rgb = vec3(cos(a)*0.5+0.5, 0.0, sin(a)*0.5+0.5); - } - """ - } - drawer.image(directionalFieldBuffer) - } - } - } -} -``` - -## Demos -### DemoDirectionField01 - -Shows how to use the [DirectionalField] filter. -Draws moving white shapes on black background, -then applies the DirectionalField filter which returns a [ColorBuffer] in which -the red and green components encode the direction to the closest black/white edge. - -Hold down a mouse button to see the raw animation. - -![DemoDirectionField01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoDirectionField01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoDirectionField01.kt) - -### DemoDirectionField02 - -Create directional distance field and demonstrate signed distance - -![DemoDirectionField02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoDirectionField02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoDirectionField02.kt) - -### DemoDistanceField01 - -Shows how to use the [DistanceField] filter. - -Draws moving white shapes on black background, -then applies the DistanceField filter which returns a [ColorBuffer] in which -the red component encodes the distance to the closest black/white edge. - -The value of the green component is negative when on the black background -and positive when inside white shapes. The sign is used in the [shadeStyle] to choose -between two colors. - -The inverse of the distance is used to obtain a non-linear brightness. - -Hold down a mouse button to see the raw animation. - -![DemoDistanceField01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoDistanceField01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoDistanceField01.kt) - -### DemoShapeSDF01 - - - -![DemoShapeSDF01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoShapeSDF01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoShapeSDF01.kt) - -### DemoShapeSDF02 - - - -![DemoShapeSDF02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoShapeSDF02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoShapeSDF02.kt) - -### DemoShapeSDF03 - - - -![DemoShapeSDF03Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoShapeSDF03Kt.png) - -[source code](src/jvmDemo/kotlin/DemoShapeSDF03.kt) - -### DemoShapeSDF04 - - - -![DemoShapeSDF04Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoShapeSDF04Kt.png) - -[source code](src/jvmDemo/kotlin/DemoShapeSDF04.kt) - -### DemoShapeSDF05 - - - -![DemoShapeSDF05Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoShapeSDF05Kt.png) - -[source code](src/jvmDemo/kotlin/DemoShapeSDF05.kt) - -### DemoSkeleton01 - - - -![DemoSkeleton01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoSkeleton01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoSkeleton01.kt) - -### DemoStraightSkeleton01 - - - -![DemoStraightSkeleton01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoStraightSkeleton01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoStraightSkeleton01.kt) - -### DemoVoronoi01 - - - -![DemoVoronoi01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoVoronoi01Kt.png) - -[source code](src/jvmDemo/kotlin/DemoVoronoi01.kt) - -### DemoVoronoi02 - - - -![DemoVoronoi02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoVoronoi02Kt.png) - -[source code](src/jvmDemo/kotlin/DemoVoronoi02.kt) - -### DemoVoronoi03 - - - -![DemoVoronoi03Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jumpflood/images/DemoVoronoi03Kt.png) - -[source code](src/jvmDemo/kotlin/DemoVoronoi03.kt) diff --git a/orx-jumpflood/build.gradle.kts b/orx-jumpflood/build.gradle.kts deleted file mode 100644 index eb6c9fc1..00000000 --- a/orx-jumpflood/build.gradle.kts +++ /dev/null @@ -1,42 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-multiplatform") -} - -val embedShaders = tasks.register("embedShaders") { - inputDir.set(file("$projectDir/src/shaders/glsl")) - outputDir.set(layout.buildDirectory.dir("generated/shaderKotlin")) - defaultPackage.set("org.openrndr.extra.jumpflood") - defaultVisibility.set("internal") - namePrefix.set("jf_") -}.get() - -kotlin { - kotlin.sourceSets.getByName("commonMain").kotlin.srcDir(embedShaders.outputDir) - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - implementation(project(":orx-parameters")) - implementation(project(":orx-fx")) - implementation(openrndr.application.core) - implementation(openrndr.draw) - implementation(openrndr.filter) - implementation(sharedLibs.kotlin.reflect) - } - } - - @Suppress("UNUSED_VARIABLE") - val jvmDemo by getting { - dependencies { - implementation(project(":orx-color")) - implementation(project(":orx-fx")) - implementation(project(":orx-noise")) - implementation(project(":orx-jumpflood")) - implementation(project(":orx-compositor")) - implementation(project(":orx-jvm:orx-gui")) - implementation(project(":orx-composition")) - implementation(project(":orx-svg")) - } - } - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/commonMain/kotlin/ClusteredField.kt b/orx-jumpflood/src/commonMain/kotlin/ClusteredField.kt deleted file mode 100644 index 98766d86..00000000 --- a/orx-jumpflood/src/commonMain/kotlin/ClusteredField.kt +++ /dev/null @@ -1,127 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.jumpfill - -import org.openrndr.draw.* -import org.openrndr.extra.fx.blend.Passthrough -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.IntRectangle -import org.openrndr.shape.Rectangle -import kotlin.math.ceil -import kotlin.math.log2 -import kotlin.math.max -import kotlin.math.pow - -@Description("Clustered field") -class ClusteredField(decodeMode: DecodeMode = DecodeMode.DIRECTION, - private val outputDistanceToContours: Boolean = true) : Filter1to1(null) { - @DoubleParameter("threshold", 0.0, 1.0) - var threshold = 0.5 - - @DoubleParameter("distance scale", 0.0, 1.0) - var distanceScale = 1.0 - - @BooleanParameter("normalized distance") - var normalizedDistance = false - - @BooleanParameter("unit direction") - var unitDirection = false - - @BooleanParameter("flip v direction") - var flipV = true - - private val encodeFilter = EncodePoints() - private var encoded: ColorBuffer? = null - private val contourFilter = IdContourPoints() - private var contoured: ColorBuffer? = null - private var jumpFlooder: JumpFlooder? = null - - private val decodeFilter = PixelDirection(decodeMode) - - private var fit: ColorBuffer? = null - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - val advisedWidth = 2.0.pow(ceil(log2(source[0].effectiveWidth.toDouble()))).toInt() - val advisedHeight = 2.0.pow(ceil(log2(source[0].effectiveHeight.toDouble()))).toInt() - val advisedSize = max(advisedWidth, advisedHeight) - - fit?.let { - if (it.effectiveWidth != advisedSize || it.effectiveHeight != advisedSize) { - it.destroy() - fit = null - encoded?.destroy() - encoded = null - contoured?.destroy() - contoured = null - jumpFlooder?.destroy() - jumpFlooder = null - } - } - - if (fit == null) { - fit = colorBuffer(advisedSize, advisedSize, type=ColorType.FLOAT32) - } - - source[0].copyTo( - fit!!, - sourceRectangle = IntRectangle(0, 0, source[0].effectiveWidth, source[0].effectiveHeight), - targetRectangle = IntRectangle( - 0, - advisedSize - source[0].effectiveHeight, - source[0].effectiveWidth, - source[0].effectiveHeight - ) - ) - - if (encoded == null) { - encoded = colorBuffer(advisedSize, advisedSize, format = ColorFormat.RGBa, type = ColorType.FLOAT32) - } - if (jumpFlooder == null) { - jumpFlooder = JumpFlooder(advisedSize, advisedSize, encodePoints = Passthrough()) - } - - if (outputDistanceToContours && contoured == null) { - contoured = colorBuffer(advisedSize, advisedSize, type = ColorType.FLOAT32) - } - - encodeFilter.apply(fit!!, encoded!!) - var result = jumpFlooder!!.jumpFlood(encoded!!) - - if (outputDistanceToContours) { - contourFilter.apply(result, contoured!!) - result = jumpFlooder!!.jumpFlood(contoured!!) - } - - decodeFilter.outputIds = true - decodeFilter.originalSize = Vector2(source[0].width.toDouble(), source[0].height.toDouble()) - decodeFilter.distanceScale = distanceScale - decodeFilter.normalizedDistance = normalizedDistance - decodeFilter.unitDirection = unitDirection - decodeFilter.flipV = flipV - decodeFilter.apply(arrayOf(result, encoded!!), arrayOf(result), clip) - - result.copyTo( - target[0], - sourceRectangle = IntRectangle( - 0, - advisedSize - source[0].effectiveHeight, - source[0].effectiveWidth, - source[0].effectiveHeight - ), - targetRectangle = IntRectangle(0, 0, source[0].effectiveWidth, source[0].effectiveHeight) - ) - } - - override fun destroy() { - encodeFilter.destroy() - contourFilter.destroy() - fit?.destroy() - encoded?.destroy() - contoured?.destroy() - jumpFlooder?.destroy() - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/commonMain/kotlin/DirectionalField.kt b/orx-jumpflood/src/commonMain/kotlin/DirectionalField.kt deleted file mode 100644 index cc71d8ad..00000000 --- a/orx-jumpflood/src/commonMain/kotlin/DirectionalField.kt +++ /dev/null @@ -1,143 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.jumpfill - -import org.openrndr.draw.* -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.IntRectangle -import org.openrndr.shape.Rectangle -import kotlin.math.ceil -import kotlin.math.log2 -import kotlin.math.max -import kotlin.math.pow - - -/** - * DirectionalField is a filter that generates a directional field representation - * of an input image, utilizing operations such as thresholding, contour detection, - * jump flooding, and direction decoding. The generated output encodes directional - * and distance information from the contours of the input. - * - * The filter supports a variety of configurable properties such as thresholds, - * scaling, and different modes for direction and magnitude representation. - * - * This class extends Filter1to1, processing one input `ColorBuffer` and producing - * one output `ColorBuffer`. - * - * Parameters: - * - `threshold`: The threshold value used during the binary segmentation of the input image. - * - `distanceScale`: The scale factor applied to the distance values encoded in the output. - * - `normalizedDistance`: Whether to normalize the distance values in the output. - * - `unitDirection`: Whether to represent gradient directions as unit vectors. - * - `signedMagnitude`: Whether to encode magnitude with signed values. - * - `flipV`: Whether to flip the vertical component of the direction vectors in the output. - * - * Lifecycle: - * - Resources such as intermediate `ColorBuffer` instances are created dynamically - * based on the dimensions of the input image. These resources are cleaned up - * in the `destroy` method to prevent memory leaks. - * - * Responsibilities: - * - Threshold the input to create a binary image. - * - Detect contours from the thresholded image. - * - Generate a jump flood field to calculate distance and direction information. - * - Decode directional data into the final output. - * - */ -@Description("Directional field") -class DirectionalField : Filter1to1(null) { - @DoubleParameter("threshold", 0.0, 1.0) - var threshold = 0.5 - - @DoubleParameter("distance scale", 0.0, 1.0) - var distanceScale = 1.0 - - @BooleanParameter("normalized distance") - var normalizedDistance = false - - @BooleanParameter("unit direction") - var unitDirection = false - - @BooleanParameter("signed magnitude") - var signedMagnitude = false - - - @BooleanParameter("flip v direction") - var flipV = true - - private val thresholdFilter = Threshold() - private var thresholded: ColorBuffer? = null - private val contourFilter = ContourPoints() - private var contoured: ColorBuffer? = null - private var jumpFlooder: JumpFlooder? = null - - private val decodeFilter = PixelDirection() - - private var fit: ColorBuffer? = null - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - - val advisedWidth = 2.0.pow(ceil(log2(source[0].effectiveWidth.toDouble()))).toInt() - val advisedHeight = 2.0.pow(ceil(log2(source[0].effectiveHeight.toDouble()))).toInt() - val advisedSize = max(advisedWidth, advisedHeight) - - fit?.let { - if (it.effectiveWidth != advisedSize || it.effectiveHeight != advisedSize) { - it.destroy() - fit = null - thresholded?.destroy() - thresholded = null - contoured?.destroy() - contoured = null - jumpFlooder?.destroy() - jumpFlooder = null - } - } - - if (fit == null) { - fit = colorBuffer(advisedSize, advisedSize) - } - - source[0].copyTo(fit!!, - sourceRectangle = IntRectangle(0, 0, source[0].effectiveWidth, source[0].effectiveHeight), - targetRectangle = IntRectangle(0, advisedSize-source[0].effectiveHeight, source[0].effectiveWidth, source[0].effectiveHeight) - ) - - if (thresholded == null) { - thresholded = colorBuffer(advisedSize, advisedSize, format = ColorFormat.R) - } - if (contoured == null) { - contoured = colorBuffer(advisedSize, advisedSize, format = ColorFormat.R) - } - if (jumpFlooder == null) { - jumpFlooder = JumpFlooder(advisedSize, advisedSize) - } - thresholdFilter.threshold = threshold - thresholdFilter.apply(fit!!, thresholded!!) - contourFilter.apply(thresholded!!, contoured!!) - val result = jumpFlooder!!.jumpFlood(contoured!!) - decodeFilter.originalSize = Vector2(source[0].width.toDouble(), source[0].height.toDouble()) - decodeFilter.distanceScale = distanceScale - decodeFilter.normalizedDistance = normalizedDistance - decodeFilter.unitDirection = unitDirection - decodeFilter.signedMagnitude = signedMagnitude - decodeFilter.flipV = flipV - decodeFilter.apply(arrayOf(result, thresholded!!), arrayOf(result)) - result.copyTo(target[0], - sourceRectangle = IntRectangle(0, advisedSize-source[0].effectiveHeight, source[0].effectiveWidth, source[0].effectiveHeight), - targetRectangle = IntRectangle(0, 0, source[0].effectiveWidth, source[0].effectiveHeight)) - } - - override fun destroy() { - thresholdFilter.destroy() - contourFilter.destroy() - fit?.destroy() - thresholded?.destroy() - contoured?.destroy() - jumpFlooder?.destroy() - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/commonMain/kotlin/DistanceField.kt b/orx-jumpflood/src/commonMain/kotlin/DistanceField.kt deleted file mode 100644 index d4a70afe..00000000 --- a/orx-jumpflood/src/commonMain/kotlin/DistanceField.kt +++ /dev/null @@ -1,107 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.jumpfill - -import org.openrndr.draw.* -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.IntRectangle -import org.openrndr.shape.Rectangle -import kotlin.math.ceil -import kotlin.math.log2 -import kotlin.math.max -import kotlin.math.pow - -/** - * The `DistanceField` class provides an implementation for computing a distance field representation of an image. - * The distance field calculation is achieved via mechanisms like thresholding, contour tracing, and jump flooding. - * This class can operate on a single source image and produce a single target image. - * - * The distance field process involves: - * - Applying a threshold filter to the source image to create a binary image representation. - * - Computing the contours of the binary representation. - * - Using a jump flooding algorithm to compute distances from each pixel to the nearest contour point. - * - Optionally utilizing signed distances by distinguishing between pixels inside and outside the contour. - * - * The class uses several configurable parameters and intermediate processing steps: - * - `threshold`: Controls the binary threshold level used in the threshold filter. - * - `distanceScale`: Scales the computed distance field values. - * - `signedDistance`: Indicates whether the distance field should contain signed or unsigned distances. - * - * Internal optimizations include resizing the input to power-of-two dimensions for efficient processing, and reusing - * intermediate buffers to reduce memory allocation overhead. - */ -@Description("Distance field") -class DistanceField : Filter1to1(null) { - @DoubleParameter("threshold", 0.0, 1.0) - var threshold = 0.5 - - @DoubleParameter("distance scale", 0.0, 1.0) - var distanceScale = 1.0 - - private val thresholdFilter = Threshold() - private var thresholded: ColorBuffer? = null - private val contourFilter = ContourPoints() - private var contoured: ColorBuffer? = null - private var jumpFlooder: JumpFlooder? = null - - private val decodeFilter = PixelDistance() - - private var fit: ColorBuffer? = null - - @BooleanParameter("signed distance") - var signedDistance = true - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - val advisedWidth = 2.0.pow(ceil(log2(source[0].effectiveWidth.toDouble()))).toInt() - val advisedHeight = 2.0.pow(ceil(log2(source[0].effectiveHeight.toDouble()))).toInt() - val advisedSize = max(advisedWidth, advisedHeight) - - fit?.let { - if (it.effectiveWidth != advisedSize || it.effectiveHeight != advisedSize) { - it.destroy() - fit = null - thresholded?.destroy() - thresholded = null - contoured?.destroy() - contoured = null - jumpFlooder?.destroy() - jumpFlooder = null - } - } - - if (fit == null) { - fit = colorBuffer(advisedSize, advisedSize) - } - - source[0].copyTo(fit!!, - sourceRectangle = IntRectangle(0, 0, source[0].effectiveWidth, source[0].effectiveHeight), - targetRectangle = IntRectangle(0, advisedSize-source[0].effectiveHeight, source[0].effectiveWidth, source[0].effectiveHeight) - ) - - if (thresholded == null) { - thresholded = colorBuffer(advisedSize, advisedSize, format = ColorFormat.R) - } - if (contoured == null) { - contoured = colorBuffer(advisedSize, advisedSize, format = ColorFormat.R) - } - if (jumpFlooder == null) { - jumpFlooder = JumpFlooder(advisedSize, advisedSize) - } - - thresholdFilter.threshold = threshold - thresholdFilter.apply(fit!!, thresholded!!) - contourFilter.apply(thresholded!!, contoured!!) - val result = jumpFlooder!!.jumpFlood(contoured!!) - decodeFilter.originalSize = Vector2(source[0].width.toDouble(), source[0].height.toDouble()) - decodeFilter.distanceScale = distanceScale - decodeFilter.apply(arrayOf(result, thresholded!!), arrayOf(result)) - result.copyTo(target[0], - sourceRectangle = IntRectangle(0, advisedSize-source[0].effectiveHeight, source[0].effectiveWidth, source[0].effectiveHeight), - targetRectangle = IntRectangle(0, 0, source[0].effectiveWidth, source[0].effectiveHeight) - ) - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/commonMain/kotlin/JumpFlood.kt b/orx-jumpflood/src/commonMain/kotlin/JumpFlood.kt deleted file mode 100644 index 8a94bd1c..00000000 --- a/orx-jumpflood/src/commonMain/kotlin/JumpFlood.kt +++ /dev/null @@ -1,190 +0,0 @@ -package org.openrndr.extra.jumpfill - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.fx.blend.Passthrough -import org.openrndr.extra.jumpflood.* - -import org.openrndr.math.Vector2 -import kotlin.math.* - -class EncodePoints : Filter(filterShaderFromCode(jf_encode_points, "encode-points")) - -class EncodeSubpixel : Filter(filterShaderFromCode(jf_encode_subpixel, "encode-subpixel")) { - var threshold by parameters - - init { - threshold = 0.5 - } -} - -class JumpFlood : Filter(filterShaderFromCode(jf_jumpflood, "jumpflood")) { - var maxSteps: Int by parameters - var step: Int by parameters -} - -enum class DecodeMode(val shaderDefine: String) { - DISTANCE("OUTPUT_DISTANCE"), - DIRECTION("OUTPUT_DIRECTION") -} - -class PixelDirection(val decodeMode: DecodeMode = DecodeMode.DIRECTION) : - Filter( - filterShaderFromCode( - "#define ${decodeMode.shaderDefine}\n $jf_pixel_direction", - "pixel-direction") - ) { - var distanceScale: Double by parameters - var originalSize: Vector2 by parameters - var normalizedDistance: Boolean by parameters - var unitDirection: Boolean by parameters - var signedMagnitude: Boolean by parameters - var flipV: Boolean by parameters - var outputIds: Boolean by parameters - - - init { - distanceScale = 1.0 - originalSize = Vector2(512.0, 512.0) - normalizedDistance = false - unitDirection = false - flipV = true - outputIds = false - signedMagnitude = false - } -} - -class PixelDistance : Filter(filterShaderFromCode(jf_pixel_distance, "pixel-distance")) { - var distanceScale: Double by parameters - var originalSize: Vector2 by parameters - var signedBit: Boolean by parameters - var signedDistance: Boolean by parameters - - init { - distanceScale = 1.0 - originalSize = Vector2(512.0, 512.0) - signedBit = true - signedDistance = false - } -} - -class ContourPoints : Filter(filterShaderFromCode(jf_contour_points, "contour-points")) - -class IdContourPoints : Filter(filterShaderFromCode(jf_id_contours, "id-contour-points")) - - -class Threshold : Filter(filterShaderFromCode(jf_threshold, "threshold")) { - var threshold: Double by parameters - - init { - threshold = 0.5 - } -} - -class AlphaThreshold : Filter(filterShaderFromCode(jf_alpha_threshold, "alpha-threshold")) { - var threshold: Double by parameters - - init { - threshold = 0.5 - } -} - - -private val encodePoints by lazy { persistent { EncodePoints() } } -private val pixelDistance by lazy { persistent { PixelDistance() } } -private val pixelDirection by lazy { persistent { PixelDirection() } } -private val contourPoints by lazy { persistent { ContourPoints() } } -private val threshold by lazy { persistent { Threshold() } } -private val passthrough by lazy { persistent { Passthrough() } } - -class JumpFlooder( - val width: Int, val height: Int, format: ColorFormat = ColorFormat.RGBa, type: ColorType = ColorType.FLOAT32, - val encodePoints: Filter = EncodePoints() -) { - - private val dimension = max(width, height) - private val exp = ceil(log2(dimension.toDouble())).toInt() - val squareDim = 2.0.pow(exp.toDouble()).toInt() - val jumpFlood = JumpFlood() - - private val coordinates = - listOf( - colorBuffer(squareDim, squareDim, format = format, type = type), - colorBuffer(squareDim, squareDim, format = format, type = type) - ) - - - val final = colorBuffer(squareDim, squareDim, format = format, type = type) - - private val square = colorBuffer(squareDim, squareDim, format = ColorFormat.RGBa, type = type).apply { - fill(ColorRGBa.BLACK.opacify(0.0)) - } - - - fun jumpFlood(input: ColorBuffer): ColorBuffer { - if (input.width != width || input.height != height) { - throw IllegalArgumentException("dimensions mismatch") - } - - input.copyTo(square) - encodePoints.apply(square, coordinates[0]) - - jumpFlood.maxSteps = exp - for (i in 0 until exp) { - jumpFlood.step = i - jumpFlood.apply(coordinates[i % 2], coordinates[(i + 1) % 2]) - } - - coordinates[exp % 2].copyTo(final) - - return final - } - - fun destroy() { - coordinates.forEach { it.destroy() } - square.destroy() - final.destroy() - } -} - -private fun encodeDecodeBitmap( - preprocess: Filter, decoder: Filter, bitmap: ColorBuffer, - jumpFlooder: JumpFlooder? = null, - result: ColorBuffer? = null -): ColorBuffer { - val _jumpFlooder = jumpFlooder ?: JumpFlooder(bitmap.width, bitmap.height) - val _result = result ?: colorBuffer(bitmap.width, bitmap.height, type = ColorType.FLOAT16) - - preprocess.apply(bitmap, _result) - - val encoded = _jumpFlooder.jumpFlood(_result) - - decoder.parameters["originalSize"] = Vector2(_jumpFlooder.squareDim.toDouble(), _jumpFlooder.squareDim.toDouble()) - decoder.apply(arrayOf(encoded, bitmap), _result) - if (jumpFlooder == null) { - _jumpFlooder.destroy() - } - return _result -} - -/** - * Creates a color buffer containing the coordinates of the nearest centroids - * @param bitmap a ColorBuffer with centroids in red (> 0) - */ -fun centroidsFromBitmap( - bitmap: ColorBuffer, - jumpFlooder: JumpFlooder? = null, - result: ColorBuffer? = null -): ColorBuffer = encodeDecodeBitmap(passthrough, passthrough, bitmap, jumpFlooder, result) - -fun distanceFieldFromBitmap( - bitmap: ColorBuffer, - jumpFlooder: JumpFlooder? = null, - result: ColorBuffer? = null -): ColorBuffer = encodeDecodeBitmap(contourPoints, pixelDistance, bitmap, jumpFlooder, result) - -fun directionFieldFromBitmap( - bitmap: ColorBuffer, - jumpFlooder: JumpFlooder? = null, - result: ColorBuffer? = null -): ColorBuffer = encodeDecodeBitmap(contourPoints, pixelDirection, bitmap, jumpFlooder, result) \ No newline at end of file diff --git a/orx-jumpflood/src/commonMain/kotlin/draw/SDFDraw.kt b/orx-jumpflood/src/commonMain/kotlin/draw/SDFDraw.kt deleted file mode 100644 index 4cc867ff..00000000 --- a/orx-jumpflood/src/commonMain/kotlin/draw/SDFDraw.kt +++ /dev/null @@ -1,38 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.jumpfill.draw - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Filter -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.jumpflood.jf_sdf_stroke_fill -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter - -@Description("SDF stroke and fill") -class SDFStrokeFill : Filter(filterShaderFromCode(jf_sdf_stroke_fill, "sdf-stroke-fill")) { - @DoubleParameter("stroke weight", 0.0, 20.0, order = 0) - var strokeWeight: Double by parameters - - @DoubleParameter("stroke feather", 0.0, 20.0, order = 0) - var strokeFeather: Double by parameters - - @ColorParameter("stroke color", order = 1) - var strokeColor: ColorRGBa by parameters - - @DoubleParameter("fill feather", 0.0, 20.0, order = 0) - var fillFeather: Double by parameters - - - @ColorParameter("fill color", order = 2) - var fillColor: ColorRGBa by parameters - init { - fillFeather = 1.0 - strokeFeather = 1.0 - strokeWeight = 1.0 - strokeColor = ColorRGBa.BLACK - fillColor = ColorRGBa.WHITE - - } -} diff --git a/orx-jumpflood/src/commonMain/kotlin/fx/InnerBevel.kt b/orx-jumpflood/src/commonMain/kotlin/fx/InnerBevel.kt deleted file mode 100644 index 9433dc61..00000000 --- a/orx-jumpflood/src/commonMain/kotlin/fx/InnerBevel.kt +++ /dev/null @@ -1,67 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.jumpfill.fx - -import org.openrndr.draw.* -import org.openrndr.extra.jumpfill.EncodeSubpixel -import org.openrndr.extra.jumpfill.JumpFlooder -import org.openrndr.extra.jumpfill.PixelDirection -import org.openrndr.extra.jumpflood.jf_inner_bevel -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -private class InnerBevelFilter : Filter(filterShaderFromCode(jf_inner_bevel, "inner-bevel")) { - var angle: Double by parameters - var width: Double by parameters - - var noise:Double by parameters - init { - angle = 0.0 - width = 5.0 - noise = 0.0 - } -} - -@Description("Inner bevel") -class InnerBevel : Filter1to1(null) { - @DoubleParameter("threshold", 0.0, 1.0) - var threshold = 0.01 - - @DoubleParameter("distance scale", 0.0, 1.0) - var distanceScale = 1.0 - - @DoubleParameter("angle", -180.0, 180.0) - var angle = 0.0 - - @DoubleParameter("width", 0.0, 50.0) - var width = 5.0 - - @DoubleParameter("noise", 0.0, 1.0) - var noise = 0.1 - - private var jumpFlooder: JumpFlooder? = null - private val decodeFilter = PixelDirection() - private val bevelFilter = InnerBevelFilter() - - private var distance: ColorBuffer? = null - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (jumpFlooder == null) { - jumpFlooder = JumpFlooder(target[0].width, target[0].height, encodePoints = EncodeSubpixel()) - } - if (distance == null) { - distance = colorBuffer(target[0].width, target[0].height, type = ColorType.FLOAT32) - } - val result = jumpFlooder!!.jumpFlood(source[0]) - decodeFilter.originalSize = Vector2(target[0].width * 1.0, target[0].height * 1.0) - decodeFilter.distanceScale = distanceScale - decodeFilter.apply(result, result) - result.copyTo(distance!!) - bevelFilter.angle = angle - bevelFilter.width = width - bevelFilter.noise = noise - bevelFilter.apply(arrayOf(source[0], distance!!), target[0], clip) - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/commonMain/kotlin/fx/InnerGlow.kt b/orx-jumpflood/src/commonMain/kotlin/fx/InnerGlow.kt deleted file mode 100644 index 9144c49d..00000000 --- a/orx-jumpflood/src/commonMain/kotlin/fx/InnerGlow.kt +++ /dev/null @@ -1,80 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.jumpfill.fx - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.jumpfill.EncodeSubpixel -import org.openrndr.extra.jumpfill.JumpFlooder -import org.openrndr.extra.jumpfill.PixelDirection -import org.openrndr.extra.jumpflood.jf_inner_glow -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -private class InnerGlowFilter : Filter(filterShaderFromCode(jf_inner_glow, "inner-glow")) { - var angle: Double by parameters - var width: Double by parameters - - var noise: Double by parameters - var color: ColorRGBa by parameters - - var shape: Double by parameters - var imageOpacity: Double by parameters - - init { - angle = 0.0 - width = 5.0 - noise = 0.0 - shape = 1.0 - imageOpacity = 1.0 - } -} - -@Description("Inner glow") -class InnerGlow : Filter1to1(null) { - @DoubleParameter("width", 0.0, 50.0) - var width = 5.0 - - @DoubleParameter("noise", 0.0, 1.0) - var noise = 0.1 - - @DoubleParameter("shape", 0.0, 10.0) - var shape = 1.0 - - @DoubleParameter("opacity", 0.0, 1.0) - var opacity = 1.0 - - @DoubleParameter("image opacity", 0.0, 1.0) - var imageOpacity = 1.0 - - @ColorParameter("color") - var color = ColorRGBa.WHITE - - private var jumpFlooder: JumpFlooder? = null - private val decodeFilter = PixelDirection() - private val glowFilter = InnerGlowFilter() - private var distance: ColorBuffer? = null - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (jumpFlooder == null) { - jumpFlooder = JumpFlooder(target[0].width, target[0].height, encodePoints = EncodeSubpixel()) - } - if (distance == null) { - distance = colorBuffer(target[0].width, target[0].height, type = ColorType.FLOAT32) - } - val result = jumpFlooder!!.jumpFlood(source[0]) - decodeFilter.originalSize = Vector2(target[0].width * 1.0, target[0].height * 1.0) - decodeFilter.distanceScale = 1.0 - decodeFilter.apply(result, result) - result.copyTo(distance!!) - glowFilter.color = color.opacify(opacity) - glowFilter.width = width - glowFilter.noise = noise - glowFilter.shape = shape - glowFilter.imageOpacity = imageOpacity - glowFilter.apply(arrayOf(source[0], distance!!), target[0], clip) - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/commonMain/kotlin/fx/Inpaint.kt b/orx-jumpflood/src/commonMain/kotlin/fx/Inpaint.kt deleted file mode 100644 index a7c40cc1..00000000 --- a/orx-jumpflood/src/commonMain/kotlin/fx/Inpaint.kt +++ /dev/null @@ -1,74 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.jumpfill.fx - -import org.openrndr.draw.* -import org.openrndr.extra.jumpfill.EncodeSubpixel -import org.openrndr.extra.jumpfill.JumpFlooder -import org.openrndr.extra.jumpfill.PixelDirection -import org.openrndr.extra.jumpflood.jf_inpaint -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -private class InpaintFilter : Filter(filterShaderFromCode(jf_inpaint, "inpaint")) { - - var noise: Double by parameters - var imageOpacity: Double by parameters - var opacity: Double by parameters - var shape: Double by parameters - var width: Double by parameters - - init { - noise = 0.0 - imageOpacity = 1.0 - opacity = 1.0 - shape = 0.0 - width = 0.5 - } -} - -@Description("Inpaint") -class Inpaint : Filter1to1(null) { - @DoubleParameter("width", 0.0, 1.0) - var width = 0.5 - - @DoubleParameter("noise", 0.0, 1.0) - var noise = 0.1 - - @DoubleParameter("opacity", 0.0, 1.0) - var opacity = 1.0 - - @DoubleParameter("image opacity", 0.0, 1.0) - var imageOpacity = 1.0 - - @DoubleParameter("shape", 0.0, 10.0) - var shape = 0.0 - - private var jumpFlooder: JumpFlooder? = null - private val decodeFilter = PixelDirection() - private val inpaintFilter = InpaintFilter() - - private var distance: ColorBuffer? = null - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (jumpFlooder == null) { - jumpFlooder = JumpFlooder(target[0].width, target[0].height, encodePoints = EncodeSubpixel()) - } - if (distance == null) { - distance = colorBuffer(target[0].width, target[0].height, type = ColorType.FLOAT32) - } - val result = jumpFlooder!!.jumpFlood(source[0]) - decodeFilter.originalSize = Vector2(target[0].width * 1.0, target[0].height * 1.0) - decodeFilter.distanceScale = 1.0 - decodeFilter.apply(result, result) - result.copyTo(distance!!) - inpaintFilter.noise = noise - inpaintFilter.imageOpacity = imageOpacity - inpaintFilter.opacity = opacity - inpaintFilter.shape = shape - inpaintFilter.width = width - inpaintFilter.apply(arrayOf(source[0], distance!!), target[0], clip) - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/commonMain/kotlin/fx/OuterGlow.kt b/orx-jumpflood/src/commonMain/kotlin/fx/OuterGlow.kt deleted file mode 100644 index 6804a1cb..00000000 --- a/orx-jumpflood/src/commonMain/kotlin/fx/OuterGlow.kt +++ /dev/null @@ -1,81 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.jumpfill.fx - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.jumpfill.EncodeSubpixel -import org.openrndr.extra.jumpfill.JumpFlooder -import org.openrndr.extra.jumpfill.PixelDirection -import org.openrndr.extra.jumpflood.jf_outer_glow -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -private class OuterGlowFilter : Filter(filterShaderFromCode(jf_outer_glow, "outer-glow")) { - var angle: Double by parameters - var width: Double by parameters - - var noise: Double by parameters - var color: ColorRGBa by parameters - - var shape: Double by parameters - var imageOpacity: Double by parameters - - init { - angle = 0.0 - width = 5.0 - noise = 0.0 - shape = 1.0 - imageOpacity = 1.0 - } -} - -@Description("Outer glow") -class OuterGlow : Filter1to1(null) { - @DoubleParameter("width", 0.0, 50.0) - var width = 5.0 - - @DoubleParameter("noise", 0.0, 1.0) - var noise = 0.1 - - @DoubleParameter("shape", 0.0, 10.0) - var shape = 1.0 - - @DoubleParameter("opacity", 0.0, 1.0) - var opacity = 1.0 - - @DoubleParameter("image opacity", 0.0, 1.0) - var imageOpacity = 1.0 - - @ColorParameter("color") - var color = ColorRGBa.WHITE - - private var jumpFlooder: JumpFlooder? = null - private val decodeFilter = PixelDirection() - private val glowFilter = OuterGlowFilter() - - private var distance: ColorBuffer? = null - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (jumpFlooder == null) { - jumpFlooder = JumpFlooder(target[0].width, target[0].height, encodePoints = EncodeSubpixel()) - } - if (distance == null) { - distance = colorBuffer(target[0].width, target[0].height, type = ColorType.FLOAT32) - } - val result = jumpFlooder!!.jumpFlood(source[0]) - decodeFilter.originalSize = Vector2(target[0].width * 1.0, target[0].height * 1.0) - decodeFilter.distanceScale = 1.0 - decodeFilter.apply(result, result) - result.copyTo(distance!!) - glowFilter.color = color.opacify(opacity) - glowFilter.width = width - glowFilter.noise = noise - glowFilter.shape = shape - glowFilter.imageOpacity = imageOpacity - glowFilter.apply(arrayOf(source[0], distance!!), target[0], clip) - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/commonMain/kotlin/fx/Skeleton.kt b/orx-jumpflood/src/commonMain/kotlin/fx/Skeleton.kt deleted file mode 100644 index a6385c21..00000000 --- a/orx-jumpflood/src/commonMain/kotlin/fx/Skeleton.kt +++ /dev/null @@ -1,90 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.jumpfill.fx - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.jumpfill.ContourPoints -import org.openrndr.extra.jumpfill.JumpFlooder -import org.openrndr.extra.jumpfill.PixelDistance -import org.openrndr.extra.jumpfill.Threshold -import org.openrndr.extra.jumpflood.jf_skeleton -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle - -private class SkeletonFilter : Filter(filterShaderFromCode(jf_skeleton, "skeleton")) { - var skeletonColor: ColorRGBa by parameters - var foregroundColor: ColorRGBa by parameters - var backgroundColor: ColorRGBa by parameters - - init { - skeletonColor = ColorRGBa.WHITE - foregroundColor = ColorRGBa.GRAY - backgroundColor = ColorRGBa.TRANSPARENT - } -} - -@Description("Skeleton") -class Skeleton : Filter(null) { - @DoubleParameter("threshold", 0.0, 1.0, order = 0) - var threshold = 0.5 - - @DoubleParameter("distance scale", 0.0, 1.0, order = 1) - var distanceScale = 1.0 - - @ColorParameter("skeleton color", order = 2) - var skeletonColor = ColorRGBa.WHITE - - @ColorParameter("foreground color", order = 3) - var foregroundColor = ColorRGBa.GRAY - - @ColorParameter("background color", order = 4) - var backgroundColor = ColorRGBa.TRANSPARENT - - private val thresholdFilter = Threshold() - private var thresholded: ColorBuffer? = null - private val contourFilter = ContourPoints() - private var contoured: ColorBuffer? = null - private var copied: ColorBuffer? = null - private var jumpFlooder: JumpFlooder? = null - - private val decodeFilter = PixelDistance() - private val skeletonFilter = SkeletonFilter() - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - - if (thresholded == null) { - thresholded = colorBuffer(target[0].width, target[0].height, format = ColorFormat.R) - } - if (contoured == null) { - contoured = colorBuffer(target[0].width, target[0].height, format = ColorFormat.R) - } - if (jumpFlooder == null) { - jumpFlooder = JumpFlooder(target[0].width, target[0].height) - } - if (copied == null) { - copied = target[0].createEquivalent(type = ColorType.FLOAT32) - } - - thresholdFilter.threshold = threshold - thresholdFilter.apply(source[0], thresholded!!) - contourFilter.apply(thresholded!!, contoured!!) - val result = jumpFlooder!!.jumpFlood(contoured!!) - - decodeFilter.signedDistance = true - decodeFilter.originalSize = Vector2(target[0].width * 1.0, target[0].height * 1.0) - decodeFilter.distanceScale = distanceScale - decodeFilter.signedBit = false - decodeFilter.apply(arrayOf(result, thresholded!!), arrayOf(result)) - - result.copyTo(copied!!) - skeletonFilter.skeletonColor = skeletonColor - skeletonFilter.backgroundColor = backgroundColor - skeletonFilter.foregroundColor = foregroundColor - skeletonFilter.apply(copied!!, target[0]) - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/commonMain/kotlin/fx/StraightSkeleton.kt b/orx-jumpflood/src/commonMain/kotlin/fx/StraightSkeleton.kt deleted file mode 100644 index 0ba25cc9..00000000 --- a/orx-jumpflood/src/commonMain/kotlin/fx/StraightSkeleton.kt +++ /dev/null @@ -1,92 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.jumpfill.fx - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.jumpfill.ContourPoints -import org.openrndr.extra.jumpfill.JumpFlooder -import org.openrndr.extra.jumpfill.PixelDirection -import org.openrndr.extra.jumpfill.Threshold -import org.openrndr.extra.jumpflood.jf_straight_skeleton -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle -import kotlin.math.sqrt - -private class StraightSkeletonFilter : Filter(filterShaderFromCode(jf_straight_skeleton, "straight-skeleton")) { - var angleThreshold: Double by parameters - var skeletonColor: ColorRGBa by parameters - var foregroundColor: ColorRGBa by parameters - var backgroundColor: ColorRGBa by parameters - - init { - skeletonColor = ColorRGBa.WHITE - foregroundColor = ColorRGBa.GRAY - backgroundColor = ColorRGBa.TRANSPARENT - angleThreshold = sqrt(2.0) / 2.0; - } -} - -@Description("Skeleton") -class StraightSkeleton : Filter(null) { - @DoubleParameter("threshold", 0.0, 1.0, order = 0) - var threshold = 0.5 - - @DoubleParameter("distance scale", 0.0, 1.0, order = 1) - var distanceScale = 1.0 - - @DoubleParameter("angle threshold", 0.0, 1.0, order = 2) - var angleThreshold = sqrt(2.0) / 2.0 - - @ColorParameter("skeleton color", order = 3) - var skeletonColor = ColorRGBa.WHITE - - @ColorParameter("foreground color", order = 4) - var foregroundColor = ColorRGBa.GRAY - - @ColorParameter("background color", order = 5) - var backgroundColor = ColorRGBa.TRANSPARENT - - private val thresholdFilter = Threshold() - private var thresholded: ColorBuffer? = null - private val contourFilter = ContourPoints() - private var contoured: ColorBuffer? = null - private var copied: ColorBuffer? = null - private var jumpFlooder: JumpFlooder? = null - - private val decodeFilter = PixelDirection() - private val skeletonFilter = StraightSkeletonFilter() - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - if (thresholded == null) { - thresholded = colorBuffer(target[0].width, target[0].height, format = ColorFormat.R) - } - if (contoured == null) { - contoured = colorBuffer(target[0].width, target[0].height, format = ColorFormat.R) - } - if (jumpFlooder == null) { - jumpFlooder = JumpFlooder(target[0].width, target[0].height) - } - if (copied == null) { - copied = target[0].createEquivalent(type = ColorType.FLOAT32) - } - - thresholdFilter.threshold = threshold - thresholdFilter.apply(source[0], thresholded!!) - contourFilter.apply(thresholded!!, contoured!!) - val result = jumpFlooder!!.jumpFlood(contoured!!) - decodeFilter.originalSize = Vector2(target[0].width * 1.0, target[0].height * 1.0) - decodeFilter.distanceScale = distanceScale - decodeFilter.apply(arrayOf(result, thresholded!!), arrayOf(result)) - result.copyTo(copied!!) - - skeletonFilter.angleThreshold = angleThreshold - skeletonFilter.skeletonColor = skeletonColor - skeletonFilter.backgroundColor = backgroundColor - skeletonFilter.foregroundColor = foregroundColor - skeletonFilter.apply(copied!!, target[0], clip) - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/commonMain/kotlin/ops/SDFOps.kt b/orx-jumpflood/src/commonMain/kotlin/ops/SDFOps.kt deleted file mode 100644 index 4d3f310f..00000000 --- a/orx-jumpflood/src/commonMain/kotlin/ops/SDFOps.kt +++ /dev/null @@ -1,104 +0,0 @@ -@file:Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED") - -package org.openrndr.extra.jumpfill.ops - -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.ColorType -import org.openrndr.draw.Filter -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.jumpflood.* -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.shape.Rectangle - -class SDFSmoothUnion : Filter(filterShaderFromCode(jf_sdf_smooth_union, "sdf-smooth-union")) { - var radius: Double by parameters - - init { - radius = 0.0 - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(target[0].type == ColorType.FLOAT16 || target[0].type == ColorType.FLOAT32) { - "needs a floating point target" - } - super.apply(source, target, clip) - } -} - -class SDFSmoothIntersection : Filter(filterShaderFromCode(jf_sdf_smooth_intersection, "sdf-smooth-intersection")) { - var radius: Double by parameters - - init { - radius = 0.0 - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(target[0].type == ColorType.FLOAT16 || target[0].type == ColorType.FLOAT32) { - "needs a floating point target" - } - super.apply(source, target, clip) - } -} -@Description("SDF smooth difference") -class SDFSmoothDifference : Filter(filterShaderFromCode(jf_sdf_smooth_difference, "sdf-smooth-differecnce")) { - @DoubleParameter("smooth radius", 0.0, 200.0, order = 0) - var radius: Double by parameters - - init { - radius = 0.0 - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(target[0].type == ColorType.FLOAT16 || target[0].type == ColorType.FLOAT32) { - "needs a floating point target" - } - super.apply(source, target, clip) - } -} - -class SDFRound : Filter(filterShaderFromCode(jf_sdf_round, "sdf-round")) { - @DoubleParameter("rounding radius", 0.0, 200.0, order = 0) - var radius: Double by parameters - - init { - radius = 0.0 - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(target[0].type == ColorType.FLOAT16 || target[0].type == ColorType.FLOAT32) { - "needs a floating point target" - } - super.apply(source, target, clip) - } -} - -class SDFOnion : Filter(filterShaderFromCode(jf_sdf_onion, "sdf-onion")) { - var radius: Double by parameters - - init { - radius = 0.0 - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(target[0].type == ColorType.FLOAT16 || target[0].type == ColorType.FLOAT32) { - "needs a floating point target" - } - super.apply(source, target, clip) - } -} - -class SDFBlend : Filter(filterShaderFromCode(jf_sdf_blend, "sdf-blend")) { - var factor: Double by parameters - - init { - factor = 0.5 - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(target[0].type == ColorType.FLOAT16 || target[0].type == ColorType.FLOAT32) { - "needs a floating point target" - } - super.apply(source, target, clip) - } -} diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoDirectionField01.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoDirectionField01.kt deleted file mode 100644 index 296f1715..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoDirectionField01.kt +++ /dev/null @@ -1,82 +0,0 @@ -import org.openrndr.MouseTracker -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.jumpfill.DirectionalField -import org.openrndr.extra.noise.simplex -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.shape.Rectangle - -/** - * Shows how to use the [DirectionalField] filter. - * Draws moving white shapes on black background, - * then applies the DirectionalField filter which returns a [ColorBuffer] in which - * the red and green components encode the direction to the closest black/white edge. - * - * Hold down a mouse button to see the raw animation. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - - program { - val rt = renderTarget(width, height) { colorBuffer() } - val directionalField = DirectionalField().also { - it.distanceScale = 0.004 - } - - // Needs to be FLOAT32 so we can have negative values - val result = colorBuffer(width, height, type = ColorType.FLOAT32) - val shader = shadeStyle { - fragmentTransform = """ - x_fill.rgb = vec3(x_fill.rg + 0.5, x_fill.b); - - // interesting when distanceScale = 1.0 - //x_fill.rgb = vec3(1.0 / (x_fill.r + x_fill.g)); - """ - } - val mouseTracker = MouseTracker(mouse) - - extend { - // Draw moving white shapes on a black background - drawer.isolatedWithTarget(rt) { - clear(ColorRGBa.BLACK) - stroke = null - fill = ColorRGBa.WHITE - repeat(10) { - val pos = Vector2.simplex(it, seconds * 0.2) * - bounds.center + bounds.center - val size = (it * it + 5.0) * 2.0 - - isolated { - translate(pos) - if (it % 2 == 0) { - circle(Vector2.ZERO, size) - } else { - rotate(Vector3.UNIT_Z, pos.x) - rectangle( - Rectangle.fromCenter( - Vector2.ZERO, size, size * 2 - ) - ) - } - } - } - } - - directionalField.apply(rt.colorBuffer(0), result) - - drawer.isolated { - if (mouseTracker.pressedButtons.isEmpty()) { - shadeStyle = shader - image(result) - } else { - image(rt.colorBuffer(0)) - } - } - } - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoDirectionField02.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoDirectionField02.kt deleted file mode 100644 index 9c101f4a..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoDirectionField02.kt +++ /dev/null @@ -1,55 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorType -import org.openrndr.draw.createEquivalent -import org.openrndr.drawImage -import org.openrndr.extra.color.colormatrix.constant -import org.openrndr.extra.color.colormatrix.tint -import org.openrndr.extra.jumpfill.DirectionalField -import org.openrndr.extra.noise.scatter -import org.openrndr.math.IntVector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.clamp - -/** - * Create directional distance field and demonstrate signed distance - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - - program { - val input = drawImage(width, height, contentScale = 1.0) { - val points = drawer.bounds.scatter(100.0) - drawer.circles(points, 50.0) - } - - val filter = DirectionalField() - val ddf = input.createEquivalent(type = ColorType.FLOAT32) - - filter.signedMagnitude = true - filter.unitDirection = true - filter.apply(input, ddf) - - ddf.shadow.download() - extend { - val p = (mouse.position * ddf.contentScale).toInt().clamp( - IntVector2.ZERO, - IntVector2(width - 1, height - 1) - ) - val c = ddf.shadow[p.x, p.y] - val sdf3 = Vector3(c.r, c.g, c.b) - - drawer.drawStyle.colorMatrix = constant(ColorRGBa.WHITE.shade(0.5)) * tint(ColorRGBa.WHITE.shade(0.5)) - - drawer.image(ddf) - drawer.fill = null - drawer.stroke = ColorRGBa.WHITE - - drawer.circle(mouse.position, sdf3.z / ddf.contentScale) - drawer.lineSegment(mouse.position, mouse.position + sdf3.xy * sdf3.z) - } - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoDistanceField01.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoDistanceField01.kt deleted file mode 100644 index 2d01bc82..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoDistanceField01.kt +++ /dev/null @@ -1,90 +0,0 @@ -import org.openrndr.MouseTracker -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.jumpfill.DistanceField -import org.openrndr.extra.noise.simplex -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.shape.Rectangle - -/** - * Shows how to use the [DistanceField] filter. - * - * Draws moving white shapes on black background, - * then applies the DistanceField filter which returns a [ColorBuffer] in which - * the red component encodes the distance to the closest black/white edge. - * - * The value of the green component is negative when on the black background - * and positive when inside white shapes. The sign is used in the [shadeStyle] to choose - * between two colors. - * - * The inverse of the distance is used to obtain a non-linear brightness. - * - * Hold down a mouse button to see the raw animation. - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - - program { - val rt = renderTarget(width, height) { colorBuffer() } - val distanceField = DistanceField() - - // Needs to be FLOAT32 so we can have negative values - val result = colorBuffer(width, height, type = ColorType.FLOAT32) - val shader = shadeStyle { - fragmentTransform = """ - float distance = abs(x_fill.r); - float bri = 1.0 / (1.0 + 0.03 * distance); - - // wavy effect - // bri *= (1.0 + 0.2 * sin(distance * 0.2)); - - x_fill.rgb = bri * (x_fill.g > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 1.0)); - """ - } - val mouseTracker = MouseTracker(mouse) - - extend { - // Draw moving white shapes on a black background - drawer.isolatedWithTarget(rt) { - clear(ColorRGBa.BLACK) - stroke = null - fill = ColorRGBa.WHITE - repeat(10) { - val pos = Vector2.simplex(it, seconds * 0.2) * - bounds.center + bounds.center - val size = (it * it + 5.0) * 2.0 - - isolated { - translate(pos) - if (it % 2 == 0) { - circle(Vector2.ZERO, size) - } else { - rotate(Vector3.UNIT_Z, pos.x) - rectangle( - Rectangle.fromCenter( - Vector2.ZERO, size, size * 2 - ) - ) - } - } - } - } - - distanceField.apply(rt.colorBuffer(0), result) - - drawer.isolated { - if (mouseTracker.pressedButtons.isEmpty()) { - shadeStyle = shader - image(result) - } else { - image(rt.colorBuffer(0)) - } - } - } - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoInnerGlow01.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoInnerGlow01.kt deleted file mode 100644 index 5dac1966..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoInnerGlow01.kt +++ /dev/null @@ -1,39 +0,0 @@ -//import org.openrndr.application -//import org.openrndr.color.ColorRGBa -//import org.openrndr.extra.compositor.compose -//import org.openrndr.extra.compositor.draw -//import org.openrndr.extra.compositor.layer -//import org.openrndr.extra.compositor.post -//import org.openrndr.extra.fx.patterns.Checkers -//import org.openrndr.extra.jumpfill.fx.InnerGlow - -// Temporarily commented out until JumpFlood.kt is compatible with -// https://github.com/openrndr/openrndr/commit/9871b80161b155bf61371301ce1b7b9cf9a10adf - -//fun main() = application { -// configure { -// width = 720 -// height = 720 -// } -// program { -// val c = compose { -// layer { -// post(Checkers()) -// } -// layer { -// draw { -// drawer.fill = ColorRGBa.PINK.shade(0.5) -// drawer.stroke = null -// drawer.circle(width / 2.0, height / 2.0, width * 0.35) -// } -// post(InnerGlow()) { -// color = ColorRGBa(-1.0, -1.0, -1.0, 0.25); -// width = 30.0 -// } -// } -// } -// extend { -// c.draw(drawer) -// } -// } -//} \ No newline at end of file diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoInnerGlow02.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoInnerGlow02.kt deleted file mode 100644 index f1429256..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoInnerGlow02.kt +++ /dev/null @@ -1,39 +0,0 @@ -//import org.openrndr.application -//import org.openrndr.color.ColorRGBa -//import org.openrndr.extra.compositor.compose -//import org.openrndr.extra.compositor.draw -//import org.openrndr.extra.compositor.layer -//import org.openrndr.extra.compositor.post -//import org.openrndr.extra.fx.patterns.Checkers -//import org.openrndr.extra.jumpfill.fx.InnerGlow - -// Temporarily commented out until JumpFlood.kt is compatible with -// https://github.com/openrndr/openrndr/commit/9871b80161b155bf61371301ce1b7b9cf9a10adf - -//fun main() = application { -// configure { -// width = 720 -// height = 720 -// } -// program { -// val c = compose { -// layer { -// post(Checkers()) -// } -// layer { -// draw { -// drawer.fill = ColorRGBa.PINK.shade(0.5) -// drawer.stroke = null -// drawer.circle(width / 2.0, height / 2.0, width * 0.35) -// } -// post(InnerGlow()) { -// color = ColorRGBa(1.0, 1.0, 1.0, 0.25); -// width = 30.0 -// } -// } -// } -// extend { -// c.draw(drawer) -// } -// } -//} \ No newline at end of file diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF01.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF01.kt deleted file mode 100644 index 0aa182c3..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF01.kt +++ /dev/null @@ -1,31 +0,0 @@ -import org.openrndr.MouseTracker -import org.openrndr.application -import org.openrndr.draw.ColorFormat -import org.openrndr.draw.ColorType -import org.openrndr.draw.colorBuffer -import org.openrndr.extra.jumpfill.ShapeSDF -import org.openrndr.extra.svg.loadSVG - -fun main() = application { - configure { - width = 720 - height = 405 - } - program { - val sdf = ShapeSDF() - val df = colorBuffer(width, height, format = ColorFormat.RGBa, type = ColorType.FLOAT32) - - val shapes = loadSVG("orx-jumpflood/src/jvmDemo/resources/name.svg").findShapes().map { it.shape } - sdf.setShapes(shapes) - sdf.apply(emptyArray(), df) - - val mouseTracker = MouseTracker(mouse) - - extend { - if(mouseTracker.pressedButtons.isEmpty()) - drawer.image(df) - else - drawer.shapes(shapes) - } - } -} diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF02.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF02.kt deleted file mode 100644 index 1a6cf928..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF02.kt +++ /dev/null @@ -1,60 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorFormat -import org.openrndr.draw.ColorType -import org.openrndr.draw.colorBuffer -import org.openrndr.extra.jumpfill.ShapeSDF -import org.openrndr.extra.jumpfill.draw.SDFStrokeFill -import org.openrndr.extra.jumpfill.ops.SDFOnion -import org.openrndr.extra.jumpfill.ops.SDFSmoothIntersection -import org.openrndr.extra.svg.loadSVG -import org.openrndr.math.Vector3 -import org.openrndr.math.transforms.transform - -import kotlin.math.min - -fun main() = application { - configure { - width = 720 - height = 405 - } - program { - val sdf0 = ShapeSDF() - val df0 = colorBuffer(width, height, format = ColorFormat.RGBa, type = ColorType.FLOAT32) - - val sdf1 = ShapeSDF() - val df1 = colorBuffer(width, height, format = ColorFormat.RGBa, type = ColorType.FLOAT32) - - val shapes = loadSVG("orx-jumpflood/src/jvmDemo/resources/name.svg").findShapes().map { it.shape } - - val union = SDFSmoothIntersection() - val onion = SDFOnion() - - - val strokeFill = SDFStrokeFill() - - extend { - drawer.clear(ColorRGBa.PINK) - - sdf0.setShapes(shapes) - - sdf1.setShapes(shapes.map { - it.transform(transform { - translate(drawer.bounds.center) - rotate(Vector3.Companion.UNIT_Z, seconds * 45.0 - 30.0) - translate(-drawer.bounds.center) - }) - }) - - sdf0.apply(emptyArray(), df0) - sdf1.apply(emptyArray(), df1) - union.radius = 10.0 + min(mouse.position.y, 100.0) - union.apply(arrayOf(df0, df1), df0) - onion.radius = 20.0 - onion.apply(df0, df0) - strokeFill.strokeWeight = 2.0 - strokeFill.apply(df0, df0) - drawer.image(df0) - } - } -} diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF03.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF03.kt deleted file mode 100644 index e1b52ad0..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF03.kt +++ /dev/null @@ -1,52 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorFormat -import org.openrndr.draw.ColorType -import org.openrndr.draw.colorBuffer -import org.openrndr.extra.fx.distort.FluidDistort -import org.openrndr.extra.jumpfill.ShapeSDF -import org.openrndr.extra.jumpfill.draw.SDFStrokeFill -import org.openrndr.extra.jumpfill.ops.SDFSmoothDifference -import org.openrndr.extra.svg.loadSVG - -fun main() = application { - configure { - width = 720 - height = 405 - } - program { - val sdf0 = ShapeSDF() - val sdf1 = ShapeSDF() - val df0 = colorBuffer(width, height, format = ColorFormat.RGBa, type = ColorType.FLOAT32) - val df1 = colorBuffer(width, height, format = ColorFormat.RGBa, type = ColorType.FLOAT32) - - val fd = FluidDistort() - fd.outputUV = true - - val uvmap = colorBuffer(width, height, type = ColorType.FLOAT16) - - val shapes = loadSVG("orx-jumpflood/src/jvmDemo/resources/name.svg").findShapes().map { it.shape } - val union = SDFSmoothDifference() - - sdf0.setShapes(shapes) - sdf1.setShapes(shapes) - - val strokeFill = SDFStrokeFill() - - extend { - drawer.clear(ColorRGBa.PINK) - - fd.apply(emptyArray(), uvmap) - - sdf0.useUV = true - sdf0.apply(uvmap, df0) - sdf1.apply(uvmap, df1) - union.radius = 10.0 - union.apply(arrayOf(df0, df1), df0) - - strokeFill.strokeWeight = 10.0 - strokeFill.apply(df0, df0) - drawer.image(df0) - } - } -} diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF04.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF04.kt deleted file mode 100644 index c354d45f..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF04.kt +++ /dev/null @@ -1,59 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorFormat -import org.openrndr.draw.ColorType -import org.openrndr.draw.colorBuffer -import org.openrndr.extra.fx.distort.Perturb -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.jumpfill.ShapeSDF -import org.openrndr.extra.jumpfill.draw.SDFStrokeFill -import org.openrndr.extra.jumpfill.ops.SDFSmoothDifference -import org.openrndr.extra.svg.loadSVG -import org.openrndr.shape.Circle - -fun main() = application { - configure { - width = 720 - height = 405 - } - program { - val gui = GUI() - val sdf0 = ShapeSDF() - val sdf1 = ShapeSDF() - val df0 = colorBuffer(width, height, format = ColorFormat.RGBa, type = ColorType.FLOAT32) - val df1 = colorBuffer(width, height, format = ColorFormat.RGBa, type = ColorType.FLOAT32) - - val perturb = Perturb() - perturb.outputUV = true - - val uvmap = colorBuffer(width, height, type = ColorType.FLOAT16) - - val circleShapes = List(1) { Circle(drawer.bounds.center, 200.0).shape } - val shapes = loadSVG("orx-jumpflood/src/jvmDemo/resources/name.svg").findShapes().map { it.shape } - - sdf0.setShapes(circleShapes) - sdf1.setShapes(shapes) - - val difference = SDFSmoothDifference() - val strokeFill = SDFStrokeFill() - - gui.add(perturb) - extend(gui) - extend { - drawer.clear(ColorRGBa.PINK) - - perturb.phase = seconds * 0.1 - perturb.apply(uvmap, uvmap) - - sdf0.useUV = true - sdf0.apply(uvmap, df0) - sdf1.apply(uvmap, df1) - difference.radius = 10.0 - difference.apply(arrayOf(df0, df1), df0) - - strokeFill.strokeWeight = 10.0 - strokeFill.apply(df0, df0) - drawer.image(df0) - } - } -} diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF05.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF05.kt deleted file mode 100644 index a54dd184..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoShapeSDF05.kt +++ /dev/null @@ -1,73 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorFormat -import org.openrndr.draw.ColorType -import org.openrndr.draw.colorBuffer -import org.openrndr.extra.fx.distort.Perturb -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.jumpfill.ShapeSDF -import org.openrndr.extra.jumpfill.draw.SDFStrokeFill -import org.openrndr.extra.jumpfill.ops.SDFSmoothDifference -import org.openrndr.extra.svg.loadSVG -import org.openrndr.math.Vector2 -import org.openrndr.shape.Circle -import kotlin.math.cos -import kotlin.math.sin - -fun main() = application { - configure { - width = 720 - height = 405 - } - program { - val gui = GUI() - val sdf0 = ShapeSDF() - val sdf1 = ShapeSDF() - val df0 = colorBuffer(width, height, format = ColorFormat.RGBa, type = ColorType.FLOAT32) - val df1 = colorBuffer(width, height, format = ColorFormat.RGBa, type = ColorType.FLOAT32) - - val perturb = Perturb() - - perturb.outputUV = true - - val uvmap = colorBuffer(width, height, type = ColorType.FLOAT16) - val uvmap2 = colorBuffer(width, height, type = ColorType.FLOAT16) - - val circleShapes = List(1) { Circle(drawer.bounds.center, 200.0).shape } - val shapes = loadSVG("orx-jumpflood/src/jvmDemo/resources/name.svg").findShapes().map { it.shape } - - sdf0.setShapes(circleShapes) - sdf1.setShapes(shapes) - - val difference = SDFSmoothDifference() - val strokeFill = SDFStrokeFill() - sdf0.useUV = true - gui.add(sdf0) - gui.add(perturb) - gui.add(strokeFill) - gui.add(difference) - - extend(gui) - extend { - drawer.clear(ColorRGBa.PINK) - - perturb.offset = Vector2(cos(seconds * 0.2), sin(seconds * 0.2)) - perturb.outputUV = true - perturb.phase = seconds * 0.1 - perturb.apply(uvmap, uvmap) - - perturb.offset = Vector2.ZERO - perturb.outputUV = false - perturb.phase = seconds * 0.05 - perturb.apply(uvmap, uvmap2) - - sdf0.apply(uvmap2, df0) - sdf1.apply(uvmap2, df1) - - difference.apply(arrayOf(df0, df1), df0) - - strokeFill.apply(df0, df0) - drawer.image(df0) - } - } -} diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoSkeleton01.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoSkeleton01.kt deleted file mode 100644 index fb2e0b40..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoSkeleton01.kt +++ /dev/null @@ -1,44 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorType -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.isolatedWithTarget -import org.openrndr.draw.renderTarget -import org.openrndr.extra.jumpfill.fx.Skeleton -import org.openrndr.extra.noise.simplex - -fun main() = application { - configure { - width = 720 - height = 540 - } - program { - val skeleton = Skeleton() - - val input = renderTarget(width, height) { - colorBuffer() - } - val field = input.colorBuffer(0).createEquivalent(type = ColorType.FLOAT32) - extend { - drawer.isolatedWithTarget(input) { - // -- draw something interesting - drawer.stroke = null - drawer.clear(ColorRGBa.BLACK) - drawer.fill = ColorRGBa.WHITE - drawer.circle(mouse.position, 300.0) - drawer.fill = ColorRGBa.BLACK - drawer.circle(mouse.position, 150.0) - drawer.fill = ColorRGBa.WHITE - for (i in 0 until 30) { - val time = seconds * 0.25 - val x = simplex(i * 20, time) * width / 2 + width / 2 - val y = simplex(i * 20 + 5, time) * height / 2 + height / 2 - val r = simplex(i * 30, time) * 50.0 + 50.0 - drawer.circle(x, y, r) - } - } - skeleton.apply(input.colorBuffer(0), field) - drawer.image(field) - } - } -} diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoStraightSkeleton01.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoStraightSkeleton01.kt deleted file mode 100644 index f8e4c83a..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoStraightSkeleton01.kt +++ /dev/null @@ -1,44 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorType -import org.openrndr.draw.createEquivalent -import org.openrndr.draw.isolatedWithTarget -import org.openrndr.draw.renderTarget -import org.openrndr.extra.jumpfill.fx.StraightSkeleton -import org.openrndr.extra.noise.simplex - -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - val straightSkeleton = StraightSkeleton() - val input = renderTarget(width, height) { - colorBuffer() - } - val field = input.colorBuffer(0).createEquivalent(type = ColorType.FLOAT32) - - extend { - drawer.isolatedWithTarget(input) { - // -- draw something interesting - drawer.stroke = null - drawer.clear(ColorRGBa.BLACK) - drawer.fill = ColorRGBa.WHITE - drawer.circle(mouse.position, 300.0) - drawer.fill = ColorRGBa.BLACK - drawer.circle(mouse.position, 150.0) - drawer.fill = ColorRGBa.WHITE - for (i in 0 until 30) { - val time = seconds * 0.25 - val x = simplex(i * 20, time) * width / 2 + width / 2 - val y = simplex(i * 20 + 5, time) * height / 2 + height / 2 - val r = simplex(i * 30, time) * 50.0 + 50.0 - drawer.circle(x, y, r) - } - } - straightSkeleton.apply(input.colorBuffer(0), field) - drawer.image(field) - } - } -} diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoVoronoi01.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoVoronoi01.kt deleted file mode 100644 index d57a3307..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoVoronoi01.kt +++ /dev/null @@ -1,68 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.fx.blend.Passthrough -import org.openrndr.extra.jumpfill.EncodePoints -import org.openrndr.extra.jumpfill.IdContourPoints -import org.openrndr.extra.jumpfill.JumpFlooder -import kotlin.math.cos - -fun main() = application { - configure { - width = 512 - height = 512 - } - program { - val rt = renderTarget(width, height, 1.0) { - colorBuffer(type = ColorType.FLOAT32) - } - val encoder = EncodePoints() - val jf = JumpFlooder(width, height, encodePoints = Passthrough()) - val jf2 = JumpFlooder(width, height, encodePoints = Passthrough()) - val idcontours = IdContourPoints() - val contoured = colorBuffer(width, height, type = ColorType.FLOAT32) - extend { - fun plot(x: Double, y: Double, id: Double) { - drawer.fill = ColorRGBa(id, 0.0, 0.0, 1.0) - drawer.point(x, y) - } - - drawer.isolatedWithTarget(rt) { - drawer.clear(ColorRGBa(-1.0, -1.0, -1.0, 0.0)) - val o = cos(seconds) * 200.0 + 200.0 - - for (i in 0 until 20) { - plot(o + 100.0 + i * 4, 100.0, 0.25) - } - - for (i in 0 until 20) { - plot(200.0 + i * 4, 150.0 + i, 0.5) - } - for (i in 0 until 20) { - plot(300.0 + i * 4, 250.0 + i, 0.7) - } - - for (i in 0 until 20) { - plot(400.0 + i * 4, 250.0 + i, 0.75) - } - } - encoder.apply(rt.colorBuffer(0), rt.colorBuffer(0)) - val flooded = jf.jumpFlood(rt.colorBuffer(0)) - drawer.image(flooded) - idcontours.apply(flooded, contoured) - drawer.image(contoured) - val flooded2 = jf2.jumpFlood(contoured) - - drawer.image(flooded2, width * 1.0, 0.0) - - drawer.shadeStyle = shadeStyle { - fragmentTransform = """ - float d = length(va_texCoord0.xy - x_fill.xy); - x_fill = vec4(d,d,x_fill.z, 1.0); - """.trimIndent() - } - drawer.image(flooded2, 0.0, 0.0) - - } - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoVoronoi02.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoVoronoi02.kt deleted file mode 100644 index f3efaead..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoVoronoi02.kt +++ /dev/null @@ -1,56 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.color.colormatrix.tint -import org.openrndr.extra.jumpfill.* -import kotlin.math.cos - -fun main() = application { - configure { - width = 512 - height = 512 - } - program { - val rt = renderTarget(width, height, 1.0) { - colorBuffer(type = ColorType.FLOAT32) - } - - val flowfield = colorBuffer(width, height, type = ColorType.FLOAT32) - val cluster = ClusteredField(decodeMode = DecodeMode.DISTANCE, outputDistanceToContours = true) - - - cluster.normalizedDistance = true - - extend { - fun plot(x: Double, y: Double, id: Double) { - drawer.fill = ColorRGBa(id, 0.0, 0.0, 1.0) - drawer.point(x, y) - } - - drawer.isolatedWithTarget(rt) { - drawer.clear(ColorRGBa(-1.0, -1.0, -1.0, 0.0)) - val o = cos(seconds) * 200.0 + 200.0 - - for (i in 0 until 20) { - plot(o + 100.0 + i * 4, 100.0, 0.25) - } - - for (i in 0 until 20) { - plot(200.0 + i * 4, 150.0 + i, 0.5) - } - for (i in 0 until 20) { - plot(300.0 + i * 4, 250.0 + i, 0.7) - } - - for (i in 0 until 20) { - plot(400.0 + i * 4, 250.0 + i, 0.75) - } - } - cluster.apply(rt.colorBuffer(0), flowfield) - drawer.drawStyle.colorMatrix = tint(ColorRGBa(10.0, 10.0, 1.0)) - drawer.image(flowfield) - - - } - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/jvmDemo/kotlin/DemoVoronoi03.kt b/orx-jumpflood/src/jvmDemo/kotlin/DemoVoronoi03.kt deleted file mode 100644 index c694ee83..00000000 --- a/orx-jumpflood/src/jvmDemo/kotlin/DemoVoronoi03.kt +++ /dev/null @@ -1,44 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.color.colormatrix.tint -import org.openrndr.extra.jumpfill.ClusteredField -import org.openrndr.extra.jumpfill.DecodeMode -import org.openrndr.extra.noise.scatter -import org.openrndr.extra.noise.uniformRing -import org.openrndr.math.Vector2 - -fun main() = application { - configure { - width = 512 - height = 512 - } - program { - val rt = renderTarget(width, height, 1.0) { - colorBuffer(type = ColorType.FLOAT32) - } - val flowfield = colorBuffer(width, height, type = ColorType.FLOAT32) - val cluster = ClusteredField(decodeMode = DecodeMode.DISTANCE, outputDistanceToContours = true) - - cluster.normalizedDistance = true - - extend { - drawer.isolatedWithTarget(rt) { - drawer.ortho(rt) - drawer.clear(ColorRGBa(-1.0, -1.0, -1.0, 0.0)) - val points = drawer.bounds.scatter(20.0) - drawer.points { - for ((index, point) in points.withIndex()) { - fill = ColorRGBa((index + 1.0) / points.size, 0.0, 0.0, 1.0) - for (i in 0 until 30) { - point(point + Vector2.uniformRing(15.0, 25.0) * Vector2(1.0, 1.0)) - } - } - } - } - cluster.apply(rt.colorBuffer(0), flowfield) - drawer.drawStyle.colorMatrix = tint(ColorRGBa(100.0, 100.0, 0.0)) - drawer.image(flowfield) - } - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/jvmDemo/resources/name.svg b/orx-jumpflood/src/jvmDemo/resources/name.svg deleted file mode 100644 index 58076b04..00000000 --- a/orx-jumpflood/src/jvmDemo/resources/name.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/orx-jumpflood/src/jvmMain/kotlin/ShapeSDF.kt b/orx-jumpflood/src/jvmMain/kotlin/ShapeSDF.kt deleted file mode 100644 index b949b584..00000000 --- a/orx-jumpflood/src/jvmMain/kotlin/ShapeSDF.kt +++ /dev/null @@ -1,90 +0,0 @@ -package org.openrndr.extra.jumpfill - -import org.openrndr.draw.* -import org.openrndr.extra.jumpflood.jf_shape_sdf -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector4 -import org.openrndr.shape.Rectangle -import org.openrndr.shape.Shape -import org.openrndr.shape.ShapeContour - - -class ShapeSDF : Filter(filterShaderFromCode(jf_shape_sdf, "shape-sdf")) { - private val fromBuffer = bufferTexture(1024, format = ColorFormat.RGBa, type = ColorType.FLOAT32) - private val toBuffer = bufferTexture(1024, format = ColorFormat.RGBa, type = ColorType.FLOAT32) - private var segmentCount = 0 - - @BooleanParameter("use UV map") - var useUV: Boolean by parameters - - @BooleanParameter("rectify distance") - var rectify: Boolean by parameters - - private var modelViewMatrixInverse by parameters - - var modelViewMatrix = Matrix44.IDENTITY - set(value) { - modelViewMatrixInverse = modelViewMatrix.inversed - field = value - } - - init { - useUV = false - rectify = false - modelViewMatrix = Matrix44.IDENTITY - } - - fun setShapes(shapes: List) { - setContours(shapes.flatMap { it.contours }) - } - - fun setContours(contours: List) { - val from = mutableListOf() - val to = mutableListOf() - - for (contour in contours) { - val lin = contour.sampleLinear() - var contourLength = 0.0 - for (segment in lin.segments) { - contourLength += segment.length - } - var offset = 0.0 - for (segment in lin.segments) { - from.add(Vector4(segment.start.x, segment.start.y, offset, contourLength)) - offset += segment.length - to.add(Vector4(segment.end.x, segment.end.y, offset, contourLength)) - } - } - - val fromShadow = fromBuffer.shadow - val fromWriter = fromShadow.writer() - fromWriter.rewind() - for (v in from) { - fromWriter.write(v) - } - fromShadow.upload(0, from.size * 4 * 4) - - val toShadow = toBuffer.shadow - val toWriter = toShadow.writer() - toWriter.rewind() - for (v in to) { - toWriter.write(v) - } - toShadow.upload(0, to.size * 4 * 4) - - segmentCount = from.size - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(target[0].type == ColorType.FLOAT16 || target[0].type == ColorType.FLOAT32) { - "needs a floating point target" - } - parameters["fromBuffer"] = fromBuffer - parameters["toBuffer"] = toBuffer - parameters["segmentCount"] = segmentCount - // -- bit of an hack - val effectiveSource = if (source.isNotEmpty()) source else target - super.apply(effectiveSource, target, clip) - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/alpha-threshold.frag b/orx-jumpflood/src/shaders/glsl/alpha-threshold.frag deleted file mode 100644 index ad4ce7d0..00000000 --- a/orx-jumpflood/src/shaders/glsl/alpha-threshold.frag +++ /dev/null @@ -1,9 +0,0 @@ -uniform sampler2D tex0; -in vec2 v_texCoord0; -uniform float threshold; -out vec4 o_color; - -void main() { - float ref = step(threshold , texture(tex0, v_texCoord0).a); - o_color = vec4(ref, ref, ref, 1.0); -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/contour-points.frag b/orx-jumpflood/src/shaders/glsl/contour-points.frag deleted file mode 100644 index 21868278..00000000 --- a/orx-jumpflood/src/shaders/glsl/contour-points.frag +++ /dev/null @@ -1,20 +0,0 @@ -uniform sampler2D tex0; -in vec2 v_texCoord0; - -out vec4 o_color; - -void main() { - vec2 stepSize = 1.0 / vec2(textureSize(tex0, 0)); - float ref = step(0.5 , texture(tex0, v_texCoord0).r); - - float laplacian = -4.0 * ref; - - laplacian += step(0.5, texture(tex0, v_texCoord0 + vec2(stepSize.x, 0.0)).r); - laplacian += step(0.5, texture(tex0, v_texCoord0 - vec2(stepSize.x, 0.0)).r); - laplacian += step(0.5, texture(tex0, v_texCoord0 + vec2(0.0, stepSize.y)).r); - laplacian += step(0.5, texture(tex0, v_texCoord0 - vec2(0.0, stepSize.y)).r); - - float contour = 1.0 - step(0.0, laplacian); - - o_color = vec4(contour, contour, contour, 1.0); -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/encode-points.frag b/orx-jumpflood/src/shaders/glsl/encode-points.frag deleted file mode 100644 index ad4a1f68..00000000 --- a/orx-jumpflood/src/shaders/glsl/encode-points.frag +++ /dev/null @@ -1,14 +0,0 @@ -uniform sampler2D tex0; -in vec2 v_texCoord0; - -out vec4 o_color; - -void main() { - vec4 t = texture(tex0, v_texCoord0); - vec4 outc = vec4(-1.0, -1.0, t.r, 1.0); - - if (t.r > 0.0) { - outc.xy = v_texCoord0.xy; - } - o_color = outc; -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/encode-subpixel.frag b/orx-jumpflood/src/shaders/glsl/encode-subpixel.frag deleted file mode 100644 index 299c9ffb..00000000 --- a/orx-jumpflood/src/shaders/glsl/encode-subpixel.frag +++ /dev/null @@ -1,122 +0,0 @@ -uniform sampler2D tex0; -in vec2 v_texCoord0; -uniform float threshold; - -out vec4 o_color; - -float zd(float d) { - if (d < 0.0001) { - return 1.0; - } else { - return d; - } -} - -void main() { - vec2 stepSize = 1.0 / vec2(textureSize(tex0, 0)); - float ref = step(threshold, texture(tex0, v_texCoord0).a); - - - vec2 o = vec2(0.0); //stepSize/2.0; - float t00 = texture(tex0, v_texCoord0 + o + vec2(0.0, 0.0)).a; - float t10 = texture(tex0, v_texCoord0 + o + vec2(stepSize.x, 0.0)).a; - float t01 = texture(tex0, v_texCoord0 + o + vec2(0.0, stepSize.y)).a; - float t11 = texture(tex0, v_texCoord0 + o + vec2(stepSize.x, stepSize.y)).a; - - int mask = 0; - - if (t00 >= threshold) { - mask += 1; - } - if (t10 >= threshold) { - mask += 2; - } - if (t01 >= threshold) { - mask += 4; - } - if (t11 >= threshold) { - mask += 8; - } - - vec2 offset = vec2(0.0); - if (mask == 1) { - offset.x = 1.0 - (threshold-t10) / zd(t00-t10); - offset.y = 1.0 - ((threshold-t01) / zd(t00-t01)); - offset /= 2.0; - } - if (mask == 2) { - offset.x = ((threshold-t00) / zd(t10-t00)); - offset.y = 1.0-(threshold-t11) / zd(t10-t11); - offset /= 2.0; - } - if (mask == 3) { // OK - float dy0 = 1.0 - (threshold - t01) / zd(t00 - t01); - float dy1 = 1.0 - (threshold - t11) / zd(t10 - t11); - offset.y = dy0 + dy1; - offset.x = 1.0; - offset /= 2.0; - } - if (mask == 4) { // OK - offset.x = 1.0 - (threshold-t11) / zd(t01-t11); - offset.y = (threshold-t00) / zd(t01-t00); - offset /= 2.0; - } - if (mask == 5) { // OK - float dx0 = 1.0- (threshold - t10) / zd(t00 - t10); - float dx1 = 1.0-(threshold - t11) / zd(t01 - t11); - offset.x = dx0 + dx1; - offset.y = 1.0; - offset /= 2.0; - } - if (mask == 6 || mask == 9) { - offset = vec2(0.5); - } - if (mask == 7) { // OK - offset.x = 1.0 - (threshold-t11) / zd(t01-t11); - offset.y = 1.0 - (threshold-t11) / zd(t10-t11); - offset /= 2.0; - } - if (mask == 8) { // OK - offset.x = (threshold-t01) / zd(t11-t01); - offset.y = (threshold-t10) / zd(t11-t10); - offset /= 2.0; - } - if (mask == 10) { // OK - float dx0 = (threshold - t00) / zd(t10 - t00); - float dx1 = (threshold - t01) / zd(t11 - t01); - offset.x = (dx0 + dx1); - offset.y = 1.0; - offset /= 2.0; - } - if (mask == 11) { // OK - offset.x = (threshold-t01) / zd(t11-t01); - offset.y = (threshold-t01) / zd(t00-t01); - offset /= 2.0; - } - if (mask == 12) { // OK - float dy0 = (threshold - t00) / zd(t01 - t00); - float dy1 = (threshold - t10) / zd(t11 - t10); - offset.y = dy0 + dy1; - offset.x = 1.0; - offset /= 2.0; - } - if (mask == 13) { // OK - offset.x = 1.0 - (threshold-t10) / zd(t00-t10); - offset.y = (threshold-t10) / zd(t11-t10); - offset /= 2.0; - } - if (mask == 14) { // OK - offset.x = (threshold-t00) / zd(t10-t00); - offset.y = (threshold-t00) / zd(t01-t00); - offset /= 2.0; - } - - float contour = (mask != 0 && mask != 15)?1.0:0.0; - - //float contour = (mask == 14 || mask == 11 || mask == 7 || mask == 13) ? 1.0 : 0.0; - if (contour > 0.0) { - o_color = vec4(v_texCoord0 /*+ offset*stepSize*/ , ref, 1.0); - } else { - o_color = vec4(-1.0, -1.0, 0.0, 1.0); - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/id-contours.frag b/orx-jumpflood/src/shaders/glsl/id-contours.frag deleted file mode 100644 index 8a636944..00000000 --- a/orx-jumpflood/src/shaders/glsl/id-contours.frag +++ /dev/null @@ -1,25 +0,0 @@ -uniform sampler2D tex0; -in vec2 v_texCoord0; - -out vec4 o_color; - -void main() { -vec4 colorMask = vec4(0.0, 0.0, 1.0, 0.0); - vec2 stepSize = 1.0 / vec2(textureSize(tex0, 0)); - vec4 ref = texture(tex0, v_texCoord0); - - float laplacian = 0.0; - - laplacian += abs(texture(tex0, v_texCoord0 + vec2(stepSize.x, 0.0)).b-ref.b); - laplacian += abs(texture(tex0, v_texCoord0 - vec2(stepSize.x, 0.0)).b-ref.b); - laplacian += abs(texture(tex0, v_texCoord0 + vec2(0.0, stepSize.y)).b-ref.b); - laplacian += abs(texture(tex0, v_texCoord0 - vec2(0.0, stepSize.y)).b-ref.b); - - float contour = step(0.0, laplacian); - - if (laplacian > 0.001) { - o_color = vec4(v_texCoord0.x, v_texCoord0.y, ref.b, 1.0); - } else { - o_color = vec4(-1.0, -1.0, -1.0, 1.0); - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/inner-bevel.frag b/orx-jumpflood/src/shaders/glsl/inner-bevel.frag deleted file mode 100644 index 1da77eb0..00000000 --- a/orx-jumpflood/src/shaders/glsl/inner-bevel.frag +++ /dev/null @@ -1,55 +0,0 @@ -uniform sampler2D tex0; // image -uniform sampler2D tex1; // distance - -uniform float angle; -uniform float width; -uniform float noise; - -in vec2 v_texCoord0; - -out vec4 o_color; -#define HASHSCALE 443.8975 -vec2 hash22(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); - p3 += dot(p3, p3.yzx+19.19); - return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); -} - - -void main() { - float r = radians(angle); - - vec4 color = texture(tex0, v_texCoord0); - - - vec2 step = 1.0 / vec2(textureSize(tex0, 0)); - - vec2 distance = vec2(0.0); - float totalWeight = 0.0; - for (int j = 0; j < 1; ++j) { - for (int i =0; i < 1; ++i) { - vec2 hn = (hash22(v_texCoord0)-0.5) * noise; - vec2 s = texture(tex1, v_texCoord0 + step * vec2(i,j)).xy + hn*0.0; - distance += s; - totalWeight += 1.0; - } - } - distance /= totalWeight; - - //vec2 distance = texture(tex1, v_texCoord0).xy + hn; - - float d = length(distance); - vec2 n = normalize(distance); - vec2 l = vec2(cos(r), sin(r)); - - float e = smoothstep(0.0, width, d) * smoothstep(width*2.0, width, d); - float o = max(0.0,dot(n, l))*e ; - float o2 = max(0.0,-dot(n, l))*e ; - //o_color = vec4(vec3(o),1.0) * color.a; - - vec3 nc = color.a > 0.0? - color.rgb/color.a : vec3(0.0); - - - o_color = vec4(nc+vec3(o)-vec3(o2),1.0) * color.a; -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/inner-glow.frag b/orx-jumpflood/src/shaders/glsl/inner-glow.frag deleted file mode 100644 index 4dca66f0..00000000 --- a/orx-jumpflood/src/shaders/glsl/inner-glow.frag +++ /dev/null @@ -1,37 +0,0 @@ -uniform sampler2D tex0; // image -uniform sampler2D tex1; // distance - -uniform float width; -uniform float noise; -uniform vec4 color; -uniform float shape; -uniform float imageOpacity; -in vec2 v_texCoord0; - -out vec4 o_color; -#define HASHSCALE 443.8975 -vec2 hash22(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); - p3 += dot(p3, p3.yzx+19.19); - return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); -} - -void main() { - vec4 original = texture(tex0, v_texCoord0); - vec2 step = 1.0 / vec2(textureSize(tex0, 0)); - vec2 distance = texture(tex1, v_texCoord0).rg; - float d = length(distance); - vec2 n = normalize(distance); - - vec2 h = hash22(v_texCoord0)*10.0; - float e = exp(-( pow((d+h.x*noise)*1.0/width, shape)) ); - - vec3 norginal = original.a > 0.0 ? original.rgb / original.a : vec3(0.0); - - vec3 add = norginal + color.rgb * e * color.a; - o_color = vec4(add, 1.0) * original.a; - -// //o_color = original * imageOpacity + original.a* vec4(color.rgb, 1.0) * e * color.a; -// o_color.rgb = max(vec3(0.0), o_color.rgb); -// o_color.a = min(o_color.a, 1.0); -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/inpaint.frag b/orx-jumpflood/src/shaders/glsl/inpaint.frag deleted file mode 100644 index aa347583..00000000 --- a/orx-jumpflood/src/shaders/glsl/inpaint.frag +++ /dev/null @@ -1,47 +0,0 @@ -uniform sampler2D tex0;// image -uniform sampler2D tex1;// distance - -uniform float width; -uniform float noise; -uniform float shape; -uniform float imageOpacity; -uniform float opacity; -in vec2 v_texCoord0; - -out vec4 o_color; -#define HASHSCALE 443.8975 -vec2 hash22(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); - p3 += dot(p3, p3.yzx+19.19); - return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); -} - -void main() { - vec4 original = texture(tex0, v_texCoord0); - vec2 ts = vec2(textureSize(tex0, 0)); - vec2 step = 1.0 / ts; - vec2 distance = texture(tex1, v_texCoord0).rg; - - vec2 n = normalize(distance); - - vec2 uvOff = distance * step * vec2(1.0, -1.0); - - vec4 border = vec4(0.0); - - float w = 0.0; - for (int j = -1; j <= 1; ++j) { - for (int i = -1; i <= 1; ++i) { - vec4 smp = texture(tex0, v_texCoord0 + uvOff + step * vec2(i, j)); - border += smp; - } - } - - vec4 nborder = border.a>0.0?vec4(border.rgb/border.a, 1.0):vec4(0.0); - float d = length(distance); - - vec2 h = hash22(v_texCoord0)*10.0; - float rwidth = max(ts.x, ts.y) * width; - float e = shape > 0.0 ? exp(-( pow((d+h.x*noise)*1.0/rwidth, shape))) : 1.0; - o_color = original * imageOpacity + (1.0-original.a)* nborder * e * opacity; - -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/jumpflood.frag b/orx-jumpflood/src/shaders/glsl/jumpflood.frag deleted file mode 100644 index 5c6e3c96..00000000 --- a/orx-jumpflood/src/shaders/glsl/jumpflood.frag +++ /dev/null @@ -1,38 +0,0 @@ -in vec2 v_texCoord0; - -uniform sampler2D tex0; -uniform int maxSteps; -uniform int step; - -out vec4 o_color; -void main() { - - float stepwidth = 1.0 / pow(2.0, min(float(maxSteps), float(step+1))); - - float bestDistance = 1E10; - vec2 bestCoord = vec2(-100.0); - vec2 bestColor = vec2(-1.0); - - vec2 is = vec2(1.0) / vec2(textureSize(tex0, 0)); - - float found = 0.0; - for (int y = -1; y <= 1; ++y) { - for (int x = -1; x <= 1; ++x) { - vec2 sampleCoord = v_texCoord0 + vec2(stepwidth) * vec2(x,y); - vec4 data = texture( tex0, sampleCoord); - vec2 seedCoord = data.xy; - vec2 seedColor = data.zw; - float dist = length(seedCoord - v_texCoord0); - if ((seedCoord.x >= 0.0 && seedCoord.y >= 0.0) && dist <= bestDistance) - { - found = 1.0; - bestDistance = dist; - bestCoord = seedCoord; - bestColor = seedColor; - } - } - } - - o_color = vec4(bestCoord, bestColor.r, 1.0); - -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/outer-glow.frag b/orx-jumpflood/src/shaders/glsl/outer-glow.frag deleted file mode 100644 index 674232b1..00000000 --- a/orx-jumpflood/src/shaders/glsl/outer-glow.frag +++ /dev/null @@ -1,29 +0,0 @@ -uniform sampler2D tex0; // image -uniform sampler2D tex1; // distance - -uniform float width; -uniform float noise; -uniform vec4 color; -uniform float shape; -uniform float imageOpacity; -in vec2 v_texCoord0; - -out vec4 o_color; -#define HASHSCALE 443.8975 -vec2 hash22(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); - p3 += dot(p3, p3.yzx+19.19); - return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); -} - -void main() { - vec4 original = texture(tex0, v_texCoord0); - vec2 step = 1.0 / vec2(textureSize(tex0, 0)); - vec2 distance = texture(tex1, v_texCoord0).rg; - float d = length(distance); - vec2 n = normalize(distance); - - vec2 h = hash22(v_texCoord0)*10.0; - float e = exp(-( pow((d+h.x*noise)*1.0/width, shape)) ); - o_color = original * imageOpacity + (1.0-original.a)* vec4(color.rgb, 1.0) * e * color.a; -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/pixel-direction.frag b/orx-jumpflood/src/shaders/glsl/pixel-direction.frag deleted file mode 100644 index ed723805..00000000 --- a/orx-jumpflood/src/shaders/glsl/pixel-direction.frag +++ /dev/null @@ -1,67 +0,0 @@ -/* -use #define OUTPUT_DISTANCE to output distance -use #define OUTPUT_DIRECTION to output direction -*/ - -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform vec2 originalSize; -uniform vec2 directionalField; -uniform float distanceScale; -uniform bool normalizedDistance; -uniform bool unitDirection; -uniform bool flipV; -uniform bool outputIds; -uniform bool signedMagnitude; -in vec2 v_texCoord0; - -out vec4 o_color; - -void main() { - vec2 sizeDF = vec2(textureSize(tex0, 0)); // this is always square - vec2 sizeTF = vec2(textureSize(tex1, 0)); // this can be non-square - - vec2 pixelPosition = v_texCoord0; - vec4 textureData = texture(tex0, v_texCoord0); - vec2 centroidPixelPosition = textureData.xy; - - - vec2 pixelDistance = (centroidPixelPosition - pixelPosition) * sizeDF; - - if (flipV) { - pixelDistance *= vec2(1.0, -1.0); - } - - float length_ = length(pixelDistance); - if (unitDirection) { - if (length_ >= 1E-6) { - pixelDistance /= length_; - } - } - - vec2 dfTf = sizeDF / sizeTF; // texture adjusment factor - - float outputData = (!outputIds) ? texture(tex1, v_texCoord0 * dfTf).r : textureData.b; - - #ifdef OUTPUT_DIRECTION - if (!normalizedDistance) { - o_color = vec4(pixelDistance * distanceScale, outputData, 1.0); - } else if (!unitDirection) { - o_color = vec4(pixelDistance / originalSize, outputData, 1.0); - } - #else - if (!normalizedDistance) { - o_color = vec4(vec2(length(pixelDistance * distanceScale)), outputData, 1.0); - } else if (!unitDirection) { - o_color = vec4(vec2(length(pixelDistance / originalSize)), outputData, 1.0); - } - #endif - - if (!outputIds) { - if (signedMagnitude) { - float s = -sign(o_color.b - 0.5); - o_color.rg *= s; - o_color.b = s * length_; - } - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/pixel-distance.frag b/orx-jumpflood/src/shaders/glsl/pixel-distance.frag deleted file mode 100644 index d7a444aa..00000000 --- a/orx-jumpflood/src/shaders/glsl/pixel-distance.frag +++ /dev/null @@ -1,37 +0,0 @@ -uniform sampler2D tex0; -uniform sampler2D tex1; - -uniform vec2 originalSize; -uniform float distanceScale; -uniform bool signedBit; -uniform bool signedDistance; - -in vec2 v_texCoord0; - -out vec4 o_color; - -void main() { - vec2 sizeDF = vec2(textureSize(tex0, 0)); // this is always square - vec2 sizeTF = vec2(textureSize(tex1, 0)); // this can be non-square - - vec2 pixelPosition = v_texCoord0; - vec2 centroidPixelPosition = texture(tex0, v_texCoord0).xy; - vec2 pixelDistance = (centroidPixelPosition - pixelPosition) * sizeDF * vec2(1.0, -1.0); - - vec2 dfTf = sizeDF / sizeTF; // texture adjusment factor - - float threshold = texture(tex1, v_texCoord0 * dfTf).r; - float distance = length(pixelDistance) * distanceScale; - - if (signedDistance) { - if (threshold > 0.5) { - distance *= -1.0; - } - } - - if (signedBit) { - o_color = vec4(distance, threshold, 0.0, 1.0); - } else { - o_color = vec4(vec3(distance), 1.0); - } -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/sdf-blend.frag b/orx-jumpflood/src/shaders/glsl/sdf-blend.frag deleted file mode 100644 index a9626e11..00000000 --- a/orx-jumpflood/src/shaders/glsl/sdf-blend.frag +++ /dev/null @@ -1,13 +0,0 @@ -uniform sampler2D tex0;// signed distance -uniform sampler2D tex1;// signed distance -uniform float factor; - -in vec2 v_texCoord0; -out vec4 o_color; - -void main() { - float d0 = texture(tex0, v_texCoord0).r; - float d1 = texture(tex1, v_texCoord0).r; - float d = mix(d0, d1, factor); - o_color = vec4(d, 0.0, 0.0, 1.0); -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/sdf-onion.frag b/orx-jumpflood/src/shaders/glsl/sdf-onion.frag deleted file mode 100644 index 9ff36444..00000000 --- a/orx-jumpflood/src/shaders/glsl/sdf-onion.frag +++ /dev/null @@ -1,10 +0,0 @@ -uniform sampler2D tex0;// signed distance -uniform float radius; - -in vec2 v_texCoord0; -out vec4 o_color; - -void main() { - float d0 = texture(tex0, v_texCoord0).r; - o_color = vec4(abs(d0)- radius, 0.0, 0.0, 1.0); -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/sdf-round.frag b/orx-jumpflood/src/shaders/glsl/sdf-round.frag deleted file mode 100644 index 8c644084..00000000 --- a/orx-jumpflood/src/shaders/glsl/sdf-round.frag +++ /dev/null @@ -1,10 +0,0 @@ -uniform sampler2D tex0; // signed distance -uniform float radius; - -in vec2 v_texCoord0; -out vec4 o_color; - -void main() { - float d0 = texture(tex0, v_texCoord0).r - radius; - o_color = vec4(d0, 0.0, 0.0, 1.0); -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/sdf-smooth-difference.frag b/orx-jumpflood/src/shaders/glsl/sdf-smooth-difference.frag deleted file mode 100644 index 2111e989..00000000 --- a/orx-jumpflood/src/shaders/glsl/sdf-smooth-difference.frag +++ /dev/null @@ -1,17 +0,0 @@ -uniform sampler2D tex0;// signed distance -uniform sampler2D tex1;// signed distance -uniform float radius; - -in vec2 v_texCoord0; -out vec4 o_color; - -float opSmoothDifference( float d1, float d2, float k ) { - float h = clamp( 0.5 - 0.5*(d2+d1)/k, 0.0, 1.0 ); - return mix( d2, -d1, h ) + k*h*(1.0-h); } - - -void main() { - float d0 = texture(tex0, v_texCoord0).r; - float d1 = texture(tex1, v_texCoord0).r; - o_color = vec4(opSmoothDifference(d0, d1, radius), 0.0, 0.0, 1.0); -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/sdf-smooth-intersection.frag b/orx-jumpflood/src/shaders/glsl/sdf-smooth-intersection.frag deleted file mode 100644 index 414de506..00000000 --- a/orx-jumpflood/src/shaders/glsl/sdf-smooth-intersection.frag +++ /dev/null @@ -1,17 +0,0 @@ -uniform sampler2D tex0;// signed distance -uniform sampler2D tex1;// signed distance -uniform float radius; - -in vec2 v_texCoord0; -out vec4 o_color; - -float opSmoothIntersection(float d1, float d2, float k) { - float h = clamp(0.5 - 0.5*(d2-d1)/k, 0.0, 1.0); - return mix(d2, d1, h) + k*h*(1.0-h); } - - -void main() { - float d0 = texture(tex0, v_texCoord0).r; - float d1 = texture(tex1, v_texCoord0).r; - o_color = vec4(opSmoothIntersection(d0, d1, radius), 0.0, 0.0, 1.0); -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/sdf-smooth-union.frag b/orx-jumpflood/src/shaders/glsl/sdf-smooth-union.frag deleted file mode 100644 index 58ff9450..00000000 --- a/orx-jumpflood/src/shaders/glsl/sdf-smooth-union.frag +++ /dev/null @@ -1,17 +0,0 @@ -uniform sampler2D tex0; // signed distance -uniform sampler2D tex1; // signed distance -uniform float radius; - -in vec2 v_texCoord0; -out vec4 o_color; - -float opSmoothUnion(float d1, float d2, float k) { - float h = clamp(0.5 + 0.5*(d2-d1)/k, 0.0, 1.0); - return mix(d2, d1, h) - k*h*(1.0-h); -} - -void main() { - float d0 = texture(tex0, v_texCoord0).r; - float d1 = texture(tex1, v_texCoord0).r; - o_color = vec4(opSmoothUnion(d0, d1, radius), 0.0, 0.0, 1.0); -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/sdf-stroke-fill.frag b/orx-jumpflood/src/shaders/glsl/sdf-stroke-fill.frag deleted file mode 100644 index ad47087f..00000000 --- a/orx-jumpflood/src/shaders/glsl/sdf-stroke-fill.frag +++ /dev/null @@ -1,22 +0,0 @@ -uniform sampler2D tex0;// signed distance -uniform float radius; - -uniform vec4 strokeColor; -uniform float strokeWeight; -uniform float strokeFeather; - -uniform float fillFeather; -uniform vec4 fillColor; - -in vec2 v_texCoord0; -out vec4 o_color; - -void main() { - float d = texture(tex0, v_texCoord0).r; - float strokeFactor = smoothstep(strokeWeight + strokeFeather, strokeWeight, abs(d)); - float fillFactor = smoothstep(0.0, fillFeather, -d); - - vec4 fc = (fillColor * fillColor.a) * fillFactor; - fc = fc * (1.0 - strokeFactor) + strokeFactor * (strokeColor * strokeColor.a); - o_color = fc; -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/shape-sdf.frag b/orx-jumpflood/src/shaders/glsl/shape-sdf.frag deleted file mode 100644 index 77c5f54d..00000000 --- a/orx-jumpflood/src/shaders/glsl/shape-sdf.frag +++ /dev/null @@ -1,109 +0,0 @@ -in vec2 v_texCoord0; -uniform float iTime; -out vec4 o_color; - -uniform bool useUV; -uniform bool rectify; - -uniform mat4 modelViewMatrixInverse; - -uniform samplerBuffer toBuffer; -uniform samplerBuffer fromBuffer; -uniform int segmentCount; -uniform vec2 targetSize; - -uniform sampler2D tex0; // uv-map - -float isLeft( vec2 P0, vec2 P1, vec2 P2 ) { - return ( (P1.x - P0.x) * (P2.y - P0.y) - - (P2.x - P0.x) * (P1.y - P0.y) ); -} - -float length_squared( vec2 v, vec2 w ) { - return dot(w-v, w-v); -} - -int winding_number( vec2 v, vec2 w, vec2 p ) { - if (v.y <= p.y) { // start y <= P.y - if (w.y > p.y) // an upward crossing - if (isLeft( v, w, p) > 0.0) // P left of edge - return 1; // ++wn; // have a valid up intersect - } - else { // start y > P.y (no test needed) - if (w.y <= p.y) // a downward crossing - if (isLeft( v,w,p) < 0.0) // P right of edge - return -1; //--wn; // have a valid down intersect - } - return 0; -} - -float minimum_distance(vec2 v, vec2 w, vec2 p) { - // Return minimum distance between line segment vw and point p - float l2 = length_squared(v, w); // i.e. |w-v|^2 - avoid a sqrt - if (l2 == 0.0) return distance(p, v); // v == w case - // Consider the line extending the segment, parameterized as v + t (w - v). - // We find projection of point p onto the line. - // It falls where t = [(p-v) . (w-v)] / |w-v|^2 - // We clamp t from [0,1] to handle points outside the segment vw. - float t = max(0.0, min(1.0, dot(p - v, w - v) / l2)); - vec2 projection = v + t * (w - v); // Projection falls on the segment - return distance(p, projection); -} - -vec3 minimum_distance_and_perpendicular(vec4 v, vec4 w, vec2 p) { - // Return minimum distance between line segment vw and point p - float l2 = length_squared(v.xy, w.xy); // i.e. |w-v|^2 - avoid a sqrt - if (l2 == 0.0) return vec3(distance(p, v.xy), v.z, v.w); // v == w case - // Consider the line extending the segment, parameterized as v + t (w - v). - // We find projection of point p onto the line. - // It falls where t = [(p-v) . (w-v)] / |w-v|^2 - // We clamp t from [0,1] to handle points outside the segment vw. - float t = max(0.0, min(1.0, dot(p - v.xy, w.xy - v.xy) / l2)); - vec3 projection = v.xyz + t * (w.xyz - v.xyz); // Projection falls on the segment - return vec3(distance(p.xy, projection.xy), projection.z, v.w); -} - -float shapeDistance(vec2 uv, out float perpDistOut, out float contourLengthOut ) { - float mindist = 10E10; - float perpdist = 0.0; - float contourLength = 0.0; - int windingNr = 0; - for (int i = 0; i < segmentCount; i++) { - vec4 from = texelFetch(fromBuffer, i); - vec4 to = texelFetch(toBuffer, i); - vec3 distline_and_perp = minimum_distance_and_perpendicular(from, to, uv.xy); - windingNr += winding_number( from.xy, to.xy, uv.xy ); - float distline = distline_and_perp.x; - if (abs(distline) <= mindist) { - mindist = distline; - perpdist = distline_and_perp.y; - contourLength = distline_and_perp.z; - } - } - float signedDistance = mindist * (windingNr==0 ? 1.0 : -1.0); - contourLengthOut = contourLength; - perpDistOut = perpdist; - return signedDistance; -} - -void main() { - vec2 uv = v_texCoord0; - - vec2 fixDistance = vec2(1.0); - - if (useUV) { - vec2 o = 0.5 / vec2(textureSize(tex0, 0)); - uv = texture(tex0, v_texCoord0 + o).xy; - if (rectify) { - fixDistance = (fwidth(uv))*vec2(1280.0, 720.0); - } - } - uv.y = 1.0 - uv.y; - uv *= targetSize; - uv = (modelViewMatrixInverse * vec4(uv, 0.0, 1.0)).xy; - - float perpdist; - float contourLength; - float signedDistance = shapeDistance(uv, perpdist, contourLength); - o_color = vec4(signedDistance / length(fixDistance), perpdist/contourLength, contourLength, 1.0); -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/skeleton.frag b/orx-jumpflood/src/shaders/glsl/skeleton.frag deleted file mode 100644 index 0f1edc9f..00000000 --- a/orx-jumpflood/src/shaders/glsl/skeleton.frag +++ /dev/null @@ -1,42 +0,0 @@ -uniform sampler2D tex0;// signed distance -uniform vec4 skeletonColor; -uniform vec4 backgroundColor; -uniform vec4 foregroundColor; -uniform float angleTreshold; - -in vec2 v_texCoord0; - -out vec4 o_color; - -void main() { - float centerDistance = texture(tex0, v_texCoord0).r; - vec2 step = 1.0 / vec2(textureSize(tex0, 0)); - - float minDistance = 1000.0; - - float nd = texture(tex0, v_texCoord0 + step * vec2(0.0, -1.0)).r; - float ed = texture(tex0, v_texCoord0 + step * vec2(1.0, 0.0)).r; - float wd = texture(tex0, v_texCoord0 + step * vec2(-1.0, 0.0)).r; - float sd = texture(tex0, v_texCoord0 + step * vec2(0.0, 1.0)).r; - - float nd2 = texture(tex0, v_texCoord0 + step * vec2(-1.0, -1.0)).r; - float ed2 = texture(tex0, v_texCoord0 + step * vec2(-1.0, 1.0)).r; - float wd2 = texture(tex0, v_texCoord0 + step * vec2(1.0, -1.0)).r; - float sd2 = texture(tex0, v_texCoord0 + step * vec2(1.0, 1.0)).r; - - float r = -centerDistance * 8.0 + nd + ed + wd + sd + nd2 + ed2 + wd2 + sd2; - - vec4 fc = vec4(0.0); - - if (centerDistance < 0.0) { - fc += foregroundColor * foregroundColor.a; - } else { - fc += backgroundColor * backgroundColor.a; - } - - if (r > 0.0 && centerDistance < 0.0) { - fc = fc * (1.0 - skeletonColor.a) + (skeletonColor * skeletonColor.a); - } - - o_color = fc; -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/straight-skeleton.frag b/orx-jumpflood/src/shaders/glsl/straight-skeleton.frag deleted file mode 100644 index 3dc16bc2..00000000 --- a/orx-jumpflood/src/shaders/glsl/straight-skeleton.frag +++ /dev/null @@ -1,46 +0,0 @@ -uniform sampler2D tex0;// signed distance -uniform vec4 skeletonColor; -uniform vec4 backgroundColor; -uniform vec4 foregroundColor; -uniform float angleTreshold; - -in vec2 v_texCoord0; - -out vec4 o_color; - -void main() { - vec4 ct = texture(tex0, v_texCoord0); - vec2 cd = normalize(ct.xy); - vec2 step = 1.0 / vec2(textureSize(tex0, 0)); - - float minDistance = 1000.0; - - vec4 nt = texture(tex0, v_texCoord0 + step * vec2(0.0, -1.0)); - vec2 nd = normalize(nt.xy); - vec4 et = texture(tex0, v_texCoord0 + step * vec2(1.0, 0.0)); - vec2 ed = normalize(et.xy); - vec4 wt = texture(tex0, v_texCoord0 + step * vec2(-1.0, 0.0)); - vec2 wd = normalize(wt.xy); - vec4 st = texture(tex0, v_texCoord0 + step * vec2(0.0, 1.0)); - vec2 sd = normalize(st.xy); - - float d0 = dot(cd, nd); - float d1 = dot(cd, ed); - float d2 = dot(cd, wd); - float d3 = dot(cd, sd); - - float r = (d0+d1+d2+d3); - - vec4 fc = vec4(0.0); - - if (ct.z > 0.0) { - fc += foregroundColor * foregroundColor.a; - } else { - fc += backgroundColor * backgroundColor.a; - } - - if ((d0 < angleTreshold || d1 < angleTreshold || d2 < angleTreshold || d3 < angleTreshold) && ct.z > 0.0 && length(ct.xy) > 4.0) { - fc = fc * (1.0 - skeletonColor.a) + (skeletonColor * skeletonColor.a); - } - o_color = fc; -} \ No newline at end of file diff --git a/orx-jumpflood/src/shaders/glsl/threshold.frag b/orx-jumpflood/src/shaders/glsl/threshold.frag deleted file mode 100644 index fb545a43..00000000 --- a/orx-jumpflood/src/shaders/glsl/threshold.frag +++ /dev/null @@ -1,9 +0,0 @@ -uniform sampler2D tex0; -in vec2 v_texCoord0; -uniform float threshold; -out vec4 o_color; - -void main() { - float ref = step(threshold , dot( vec3(1.0/3.0), texture(tex0, v_texCoord0).rgb )); - o_color = vec4(ref, ref, ref, 1.0); -} \ No newline at end of file diff --git a/orx-jvm/build.gradle.kts b/orx-jvm/build.gradle.kts deleted file mode 100644 index ed1cf45a..00000000 --- a/orx-jvm/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.dokka") -} \ No newline at end of file diff --git a/orx-jvm/orx-axidraw/README.md b/orx-jvm/orx-axidraw/README.md deleted file mode 100644 index 18247210..00000000 --- a/orx-jvm/orx-axidraw/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# orx-axidraw - -GUI for configuring and plotting with an Axidraw pen-plotter. - -Uses the [AxiCLI](https://axidraw.com/doc/cli_api/#introduction) command line tool -to communicate with the pen plotter. - -Requires: Python 3.8 or higher. - -This orx create a Python virtual environment and downloads AxiCLI automatically. - -## Usage - -```kotlin -fun main() = application { - program { - val axi = Axidraw(this, PaperSize.A5) - axi.resizeWindow() - - val gui = WindowedGUI() - gui.add(axi) - - axi.draw { - fill = null - axi.bounds.grid(4, 6).flatten().forEach { - circle(it.center, Double.uniform(20.0, 50.0)) - } - } - - extend(gui) - extend { - drawer.clear(ColorRGBa.WHITE) - axi.display(drawer) - } - } -} -``` - -Study the inputs available in the GUI. Most are explained in the [AxiCLI](https://axidraw.com/doc/cli_api/#introduction) documentation page. - -### Important - -* Choose the correct pen-plotter model and servo type in the GUI before plotting. -* Always make sure the pen is at the home position before starting to plot. If it's not, unpower the steppers, -drag the carriage home (near the Axidraw's CPU), then power the steppers back on. - -### Tips - -* One can repeatedly click on `toggle up/down` and adjust `pen pos down` and `pen pos up` -to find the ideal heights for the pen. -* Enable `fills occlude strokes` and increase margin value to hide elements near -the borders of the paper. -* Click `save` to save your SVG file. -* Click `plot` to plot the visible design using the current settings. -* A [2D camera](https://guide.openrndr.org/extensions/camera2D.html) is enabled by default to place your design on the paper. -* Click `resume plotting` after pressing the hardware pause button (or including a pause -command on a layer) to continue. -* To get a plotting time estimate, enable `preview` and click `plot`. Nothing will be plotted, but the estimate will be shown in the IDE console. - -The `Load` and `Save` buttons *at the top of the GUI* can be used to load and save the plotting settings. In a future version we may embed the plotting settings into the SVG file. - -### Multi color plots - -orx-axidraw makes it easy to create multi-pen plots. To do that, use two or more stroke colors in your design. The order of the lines does not matter. Then, before plotting, call `axi.groupStrokeColors()`. This will group curves into layers based on their stroke colors and insert a pause between layers, allowing you to change the pen. - -When the plotter pauses during plotting, change the pen and click `resume plotting` to continue. diff --git a/orx-jvm/orx-axidraw/build.gradle.kts b/orx-jvm/orx-axidraw/build.gradle.kts deleted file mode 100644 index 1392407a..00000000 --- a/orx-jvm/orx-axidraw/build.gradle.kts +++ /dev/null @@ -1,18 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(openrndr.application.core) - implementation(openrndr.dialogs) - implementation(project(":orx-jvm:orx-gui")) - api(project(":orx-composition")) - implementation(project(":orx-svg")) - implementation(project(":orx-image-fit")) - implementation(project(":orx-shapes")) - implementation(project(":orx-camera")) - demoImplementation(project(":orx-camera")) - demoImplementation(project(":orx-noise")) - demoImplementation(project(":orx-parameters")) - demoImplementation(project(":orx-jvm:orx-axidraw")) -} diff --git a/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw01.kt b/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw01.kt deleted file mode 100644 index 3e869020..00000000 --- a/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw01.kt +++ /dev/null @@ -1,59 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.axidraw.Axidraw -import org.openrndr.extra.axidraw.PaperSize -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.noise.uniform -import org.openrndr.extra.parameters.ActionParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.IntParameter -import kotlin.math.min - -/** - * Demonstrates: - * - how to create an AxiDraw GUI - * - how to add a slider and a button to that GUI - * - how to include code to generate new random designs that match - * the paper size via `axi.bounds`. - * - how to display the generated design using `axi.display`. - * - * Toggle the GUI by pressing F11. - */ -fun main() = application { - configure { - width = PaperSize.A5.size.x * 5 - height = PaperSize.A5.size.y * 5 - } - program { - val axi = Axidraw(this, PaperSize.A5) - axi.resizeWindow() - - val gui = GUI() - gui.add(axi) - - val settings = @Description("Main") object { - @IntParameter("count", 1, 50) - var count = 20 - - @ActionParameter("generate") - fun generate() { - axi.clear() - axi.draw { - val l = min(axi.bounds.width, axi.bounds.height) / 2.0 - repeat(count) { - circle(axi.bounds.center, Double.uniform(l / 4.0, l)) - } - } - } - } - gui.add(settings) - - settings.generate() - - extend(gui) - extend { - drawer.clear(ColorRGBa.WHITE) - axi.display(drawer) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw02.kt b/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw02.kt deleted file mode 100644 index 60bc2b4b..00000000 --- a/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw02.kt +++ /dev/null @@ -1,45 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.axidraw.Axidraw -import org.openrndr.extra.axidraw.PaperOrientation -import org.openrndr.extra.axidraw.PaperSize -import org.openrndr.extra.gui.WindowedGUI -import org.openrndr.extra.noise.uniform -import org.openrndr.extra.parameters.ActionParameter -import org.openrndr.extra.parameters.Description - -/** - * Demonstrates: - * - How to set the window size based on the chosen paper size. - * - How to use a windowed GUI. - * - */ -fun main() = application { - program { - val axi = Axidraw(this, PaperSize.A5, PaperOrientation.LANDSCAPE) - axi.resizeWindow() - - val gui = WindowedGUI() - gui.add(axi) - - val settings = @Description("Main") object { - - @ActionParameter("generate") - fun generate() { - axi.clear() - axi.draw { - repeat(20) { - circle(axi.bounds.center, Double.uniform(50.0, 200.0)) - } - } - } - } - gui.add(settings) - - extend(gui) - extend { - drawer.clear(ColorRGBa.WHITE) - axi.display(drawer) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw03.kt b/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw03.kt deleted file mode 100644 index 9ee7ca5c..00000000 --- a/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw03.kt +++ /dev/null @@ -1,44 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.axidraw.Axidraw -import org.openrndr.extra.axidraw.PaperOrientation -import org.openrndr.extra.axidraw.PaperSize -import org.openrndr.extra.axidraw.configure -import org.openrndr.extra.gui.WindowedGUI -import org.openrndr.extra.noise.uniform -import org.openrndr.extra.shapes.primitives.grid - -/** - * Demonstrates: - * - How to create layers via `group` and give each layer - * a unique pen height and pen speed. - * - */ -fun main() = application { - program { - val axi = Axidraw(this, PaperSize.A5, PaperOrientation.PORTRAIT) - axi.resizeWindow(100.0) - - val gui = WindowedGUI() - gui.add(axi) - - axi.clear() - axi.draw { - fill = null - axi.bounds.grid(4, 6).flatten().forEach { - group { - circle(it.center, 50.0) - }.configure( - penHeight = Int.uniform(30, 60), - penSpeed = Int.uniform(20, 50) - ) - } - } - - extend(gui) - extend { - drawer.clear(ColorRGBa.WHITE) - axi.display(drawer) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw04.kt b/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw04.kt deleted file mode 100644 index 59f643b8..00000000 --- a/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw04.kt +++ /dev/null @@ -1,48 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.axidraw.* -import org.openrndr.extra.gui.WindowedGUI -import org.openrndr.extra.shapes.primitives.grid - -/** - * Demonstrates: - * - How to create a flattened grid of with 24 items - * - How to randomize the order of those items - * - How to take chunks of 10 items, then make - * a pause to change the pen after plotting each chunk - * - * Operation: After plotting ten circles, plotting will stop to let you change the pen. - * With the second pen installed, click `resume`. It will plot ten circles more. - * Change the pen again and click `resume` to plot the remaining 4 circles. - * Once done, click `resume` one more time to bring the pen home. - */ -fun main() = application { - program { - val axi = Axidraw(this, PaperSize.A5, PaperOrientation.PORTRAIT) - axi.resizeWindow(100.0) - - val gui = WindowedGUI() - gui.add(axi) - - axi.clear() - axi.draw { - fill = null - axi.bounds.grid(4, 6).flatten() - .shuffled().chunked(10).forEach { chunk -> - group { - chunk.forEach { - circle(it.center, 50.0) - } - } - group { - }.configure(layerMode = AxiLayerMode.PAUSE) - } - } - - extend(gui) - extend { - drawer.clear(ColorRGBa.WHITE) - axi.display(drawer) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw05.kt b/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw05.kt deleted file mode 100644 index a6506043..00000000 --- a/orx-jvm/orx-axidraw/src/demo/kotlin/DemoAxidraw05.kt +++ /dev/null @@ -1,46 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.axidraw.Axidraw -import org.openrndr.extra.axidraw.PaperOrientation -import org.openrndr.extra.axidraw.PaperSize -import org.openrndr.extra.gui.WindowedGUI -import org.openrndr.extra.shapes.primitives.grid - -/** - * Demonstrates: - * - How to create a flattened grid of with 24 items - * - How to apply random colors from a palette to each item. - * - How to use `groupStrokeColors()` to plot a multi-pen design. - * - */ -fun main() = application { - program { - val axi = Axidraw(this, PaperSize.A5, PaperOrientation.PORTRAIT) - axi.resizeWindow(100.0) - - val gui = WindowedGUI() - gui.add(axi) - - val palette = listOf( - ColorRGBa.RED, - ColorRGBa.GREEN, - ColorRGBa.BLUE - ) - - axi.clear() - axi.draw { - fill = null - axi.bounds.grid(4, 6).flatten().forEach { - stroke = palette.random() - circle(it.center, 50.0) - } - } - axi.groupStrokeColors() - - extend(gui) - extend { - drawer.clear(ColorRGBa.WHITE) - axi.display(drawer) - } - } -} diff --git a/orx-jvm/orx-axidraw/src/main/kotlin/Axidraw.kt b/orx-jvm/orx-axidraw/src/main/kotlin/Axidraw.kt deleted file mode 100644 index 39c51c8e..00000000 --- a/orx-jvm/orx-axidraw/src/main/kotlin/Axidraw.kt +++ /dev/null @@ -1,506 +0,0 @@ -package org.openrndr.extra.axidraw - -import io.github.oshai.kotlinlogging.KotlinLogging -import offset.offset -import org.openrndr.Program -import org.openrndr.color.ColorRGBa -import org.openrndr.dialogs.openFileDialog -import org.openrndr.dialogs.saveFileDialog -import org.openrndr.draw.Drawer -import org.openrndr.draw.isolated -import org.openrndr.extra.camera.Camera2D -import org.openrndr.extra.composition.* -import org.openrndr.extra.imageFit.fit -import org.openrndr.extra.parameters.* -import org.openrndr.extra.svg.loadSVG -import org.openrndr.extra.svg.toSVG -import org.openrndr.math.IntVector2 -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector2 -import org.openrndr.shape.IntRectangle -import org.openrndr.shape.SegmentJoin -import org.openrndr.shape.Shape -import java.io.File -import java.util.* -import kotlin.io.path.createTempFile - -private val logger = KotlinLogging.logger {} - -/** - * Axidraw reordering optimization types. - * See: https://axidraw.com/doc/cli_api/#reordering - */ -@Suppress("unused") -enum class AxidrawOptimizationTypes(val id: Int) { - /** - * No optimization. Strictly preserve file order. - */ - None(4), - - /** - * Least; Only connect adjoining paths. - */ - ConnectPaths(0), - - /** - * Basic; Also reorder paths for speed - */ - ReorderPaths(1), - - /** - * Full; Also allow path reversal - */ - ReversePaths(2) -} - -@Suppress("unused") -enum class AxidrawModel(val id: Int) { - AxiDrawV2(1), - AxidrawV3(1), - AxidrawSE_A4(1), - AxiDrawV3_A3(2), - AxidrawSE_A3(2), - AxiDrawV3_XLX(3), - AxiDrawMiniKit(4), - AxiDrawSE_A1(5), - AxiDrawSE_A2(6), - AxiDrawV3_B6(7), -} - -@Suppress("unused") -enum class AxidrawServo(val id: Int) { - Standard(2), - Brushless(3), -} - -@Suppress("unused") -enum class PaperSize(val size: IntVector2) { - `A-1`(IntVector2(1682, 2378)), - `A-2`(IntVector2(1189, 1682)), - A0(IntVector2(841, 1189)), - A1(IntVector2(594, 841)), - A2(IntVector2(420, 594)), - A3(IntVector2(297, 420)), - A4(IntVector2(210, 297)), - A5(IntVector2(148, 210)), - A6(IntVector2(105, 148)), - A7(IntVector2(74, 105)), - A8(IntVector2(52, 74)), - A9(IntVector2(37, 52)), - A10(IntVector2(26, 37)) -} - -enum class PaperOrientation { - LANDSCAPE, - PORTRAIT -} - -/** - * Class to talk to the axicli command line program - * - */ -@Description("Axidraw") -class Axidraw(val program: Program, paperSize: PaperSize, orientation: PaperOrientation = PaperOrientation.PORTRAIT) { - - fun setupAxidrawCli() { - - if (!File("axidraw-venv").exists()) { - logger.info { "installing axidraw-cli virtual environment" } - invokePython(listOf("-m", "venv", "axidraw-venv")) - } - val python = venvPython(File("axidraw-venv")) - logger.info { "installing axidraw-cli in virtual environment $python" } - invokePython( - listOf("-m", "pip", "install", "https://cdn.evilmadscientist.com/dl/ad/public/AxiDraw_API.zip"), - python - ) - } - - init { - setupAxidrawCli() - } - - val actualPaperSize = when (orientation) { - PaperOrientation.LANDSCAPE -> paperSize.size.yx.vector2 - PaperOrientation.PORTRAIT -> paperSize.size.vector2 - } - - /** - * API URL to call once plotting is complete. If the string contains - * `[filename]` it will be replaced by the name of the file being plotted. - * This URL should be URL encoded (for instance use %20 instead of a space). - */ - var apiURL = "" - - @OptionParameter("model", 50) - var model = AxidrawModel.AxiDrawV3_A3 - - @OptionParameter("servo", 60) - var servo = AxidrawServo.Standard - - @IntParameter("speed pen down", 1, 110, 100) - var speedPenDown = 25 - - @IntParameter("speed pen up", 1, 110, 110) - var speedPenUp = 70 - - @IntParameter("acceleration", 1, 100, 120) - var acceleration = 75 - - /** - * Toggle the pen up/down state by powering the pen plotter servo. - * Useful for calibrating the pen height. Cover the paper with a - * plastic sheet before running this command to avoid accidentally - * leaving ink on the paper. - */ - @ActionParameter("toggle up/down", 125) - fun toggleUpDown() { - runCMD( - listOf( - "--mode", "toggle", - "--penlift", servo.id.toString(), - "--model", model.id.toString(), - "--pen_pos_down", "$penPosDown", - "--pen_pos_up", "$penPosUp", - ), false - ) - } - - @IntParameter("pen pos down", 1, 100, 130) - var penPosDown = 40 - - @IntParameter("pen pos up", 1, 100, 140) - var penPosUp = 60 - - @IntParameter("pen rate lower", 1, 100, 150) - var penRateLower = 50 - - @IntParameter("pen rate raise", 1, 100, 160) - var penRateRaise = 75 - - @IntParameter("pen delay down", -500, 500, 170) - var penDelayDown = 0 - - @IntParameter("pen delay up", -500, 500, 180) - var penDelayUp = 0 - - @OptionParameter("optimization", 185) - var optimization = AxidrawOptimizationTypes.ConnectPaths - - @BooleanParameter("random start", 190) - var randomStart = false - - @BooleanParameter("fills occlude strokes", 200) - var occlusion = false - - @IntParameter("margin", 0, 100, 205) - var margin = 0 - - @BooleanParameter("preview", 210) - var preview = false - - @BooleanParameter("const speed", 220) - var constSpeed = false - - @BooleanParameter("webhook", 230) - var webhook = false - - /** - * Creates a temporary SVG file. Used by the AxiCLI "resume" methods. When plotting, - * the temporary SVG file is updated to keep track of progress and allow resuming. - */ - private fun makeTempSVGFile(): File { - val tmpFile = createTempFile("axi_${UUID.randomUUID()}", ".svg").toFile() - tmpFile.deleteOnExit() - return tmpFile - } - - /** - * Keeps track of the most recent output file. Used to resume plotting after a pause. - */ - private var lastOutputFile = makeTempSVGFile() - - private fun plotArgs(plotFile: File, outputFile: File): List { - lastOutputFile = outputFile - return listOf( - plotFile.absolutePath, - "--progress", - "--report_time", - "--reordering", optimization.id.toString(), - if (randomStart) "--random_start" else "", - if (occlusion) "--hiding" else "", - if (preview) "--preview" else "", - if (webhook && apiURL.isNotEmpty()) - "--webhook" else "", - if (webhook && apiURL.isNotEmpty()) - "--webhook_url ${apiURL.replace("[filename]", plotFile.name)}" else "", - "--speed_pendown", "$speedPenDown", - "--speed_penup", "$speedPenUp", - "--accel", "$acceleration", - if (constSpeed) "--const_speed" else "", - "--pen_pos_down", "$penPosDown", - "--pen_pos_up", "$penPosUp", - "--pen_rate_lower", "$penRateLower", - "--pen_rate_raise", "$penRateRaise", - "--pen_delay_down", "$penDelayDown", - "--pen_delay_up", "$penDelayUp", - "--penlift", servo.id.toString(), - "--model", model.id.toString(), - "--output_file", outputFile.absolutePath, - ).filter { it.isNotEmpty() } - } - - private fun compositionDimensions(): CompositionDimensions { - return CompositionDimensions( - 0.0.pixels, - 0.0.pixels, - Length.Pixels.fromMillimeters(actualPaperSize.x), - Length.Pixels.fromMillimeters(actualPaperSize.y) - ) - } - - /** - * Main variable holding the design to save or plot. - */ - private val design = drawComposition(compositionDimensions()) { } - - /** - * Returns the bounds of the drawable area so user code can draw things - * whithout leaving the paper. - */ - val bounds = IntRectangle( - 0, 0, - (96.0 * actualPaperSize.x / 25.4).toInt(), - (96.0 * actualPaperSize.y / 25.4).toInt() - ).rectangle - - /** - * Clears the current design wiping any shapes the user might have - * added. - * - */ - fun clear() = design.clear() - - /** - * The core method that allows the user to append content to the design. - * Use any methods and properties like contour(), segment(), fill, stroke, etc. - */ - fun draw(f: CompositionDrawer.() -> Unit) { - design.draw(drawFunction = f) - } - - private fun runCMD(args: List, hold: Boolean = true) { - val python = venvPython(File("axidraw-venv")) - invokePython(listOf("-m", "axicli") + args, python) - } - - /** - * Display Axidraw software version - */ - @ActionParameter("info: version", 300) - fun version() = runCMD(listOf("--mode", "version")) - - /** - * Display Axidraw system info - */ - @ActionParameter("info: system", 310) - fun sysInfo() = runCMD(listOf("--mode", "sysinfo")) - - @ActionParameter("load", 330) - fun onLoad() = openFileDialog(supportedExtensions = listOf("SVG" to listOf("svg"))) { - clear() - camera.view = Matrix44.IDENTITY - val loaded = loadSVG(it) - draw { - loaded.findGroups().forEach { gn -> - if (gn.findGroups().size == 1) { - val g = group { - gn.findShapes().forEach { shp -> - if (shp.attributes["type"] != "margin") { - stroke = shp.stroke - fill = shp.fill - shape(shp.shape) - } - } - } - g.attributes.putAll(gn.attributes) - } - } - } - } - - /** - * Save current design as SVG - */ - @ActionParameter("save", 340) - fun onSave() = saveFileDialog(supportedExtensions = listOf("SVG" to listOf("svg"))) { save(it) } - - private fun save(svgFile: File) { - // Create a new SVG with the frame and camera applied - val designRendered = drawComposition(compositionDimensions()) { - val m = camera.view - - design.findGroups().forEach { gn -> - if (gn.findGroups().size == 1) { - val g = group { - gn.findShapes().forEach { shp -> - stroke = shp.stroke - fill = shp.fill - shape(shp.shape.transform(m)) - } - } - g.attributes.putAll(gn.attributes) - } - } - - // If the user wants a frame covering the design... - if (occlusion) { - fill = ColorRGBa.WHITE - stroke = null - shape(makeFrame(margin.toDouble()))?.attributes?.put("type", "margin") - } - } - designRendered.saveToInkscapeFile(svgFile) - } - - /** - * Plot design using the current settings - */ - @ActionParameter("plot", 350) - fun onPlot() { - val svgFile = makeTempSVGFile() - save(svgFile) - runCMD(plotArgs(svgFile, makeTempSVGFile())) - } - - /** - * After hitting pause, use this to move the pen home - */ - @ActionParameter("resume to home", 360) - fun goHome() { - runCMD(plotArgs(lastOutputFile, makeTempSVGFile()) + listOf("--mode", "res_home")) - } - - /** - * After hitting pause, use this to continue plotting - * - */ - @ActionParameter("resume plotting", 370) - fun resume() { - runCMD(plotArgs(lastOutputFile, makeTempSVGFile()) + listOf("--mode", "res_plot")) - } - - /** - * Optimization. This can be applied to a lambda function that takes one argument - * so it caches the calculation while the argument does not change. - */ - private fun ((A) -> B).lastArgMemo(): (A) -> B { - var lastArg: A? = null - var lastResult: B? = null - - return { arg -> - if (arg == lastArg) { - @Suppress("UNCHECKED_CAST") - lastResult as B - } else { - val result = this(arg) - lastArg = arg - lastResult = result - result - } - } - } - - /** - * Makes a white frame to cover the borders of the page, to avoid plotting - * on the edge of papers, which may damage the pen or make a mess. - */ - private val makeFrame = { width: Double -> - Shape( - listOf( - bounds.contour.offset(1000.0, SegmentJoin.MITER), - bounds.contour.offset(-width).reversed - ) - ) - }.lastArgMemo() - - /** - * Display the composition using [drawer]. - */ - fun display(drawer: Drawer) { - drawer.isolated { - view *= bounds.fit(drawer.bounds) - - isolated { - view *= camera.view - composition(design) - } - - // Draw frame - if (occlusion) { - fill = ColorRGBa.WHITE - stroke = null - shape(makeFrame(margin.toDouble())) - } - } - } - - /** - * Resizes the program window to match - * the paper size according to the - * [ppi] (Pixels Per Inch) value. - */ - fun resizeWindow(ppi: Double = 96.0) { - val app = program.application - val resizable = app.windowResizable - app.windowResizable = true - app.windowSize = Vector2( - ppi * actualPaperSize.x / 25.4, - ppi * actualPaperSize.y / 25.4 - ) - app.windowResizable = resizable - } - - val camera by lazy { - Camera2D().also { - it.setup(program) - } - } - - /** - * Rebuilds the design putting shapes under groups based on stroke colors and inserts a pause - * after each group. - * - * Call this method after creating a draw composition that uses several stroke colors. - * When plotting, change pens after each pause, then click "resume plotting". - * - * NOTE: this method changes line order. Therefore, avoid it if order is important, - * for instance with designs using fill colors to occlude. - * - */ - fun groupStrokeColors() { - val colorGroups = design.findShapes().filter { it.stroke != null }.groupBy { it.stroke!! } - design.clear() - design.draw { - var i = 0 - colorGroups.forEach { (color, nodes) -> - val hexColor = "%06x".format( - ((color.r * 255).toInt() shl 16) + ((color.g * 255).toInt() shl 8) + ((color.b * 255).toInt()) - ) - group { cursor.children.addAll(nodes) }.configure(hexColor) - - // Add a pause if it's not the last layer - if(++i < colorGroups.size) { - group { }.configure(layerMode = AxiLayerMode.PAUSE) - } - } - } - } - - /** - * Read-only String variable to inspect the current design in SVG format for debugging purposes. - */ - var svg: String = "" - get() = design.toSVG() - private set -} diff --git a/orx-jvm/orx-axidraw/src/main/kotlin/SVG.kt b/orx-jvm/orx-axidraw/src/main/kotlin/SVG.kt deleted file mode 100644 index ec5bf438..00000000 --- a/orx-jvm/orx-axidraw/src/main/kotlin/SVG.kt +++ /dev/null @@ -1,122 +0,0 @@ -package org.openrndr.extra.axidraw -import org.openrndr.extra.composition.Composition -import org.openrndr.extra.composition.GroupNode -import org.openrndr.extra.composition.findGroups -import org.openrndr.extra.svg.toSVG -import java.io.File - -/** - * Axidraw layer mode. The [command] argument will be prepended to the layer name. - */ -@Suppress("unused") -enum class AxiLayerMode(val command: String) { - /** - * The default mode prepends nothing. - */ - DEFAULT(""), - - /** - * Layer names starting with `%` are not plotted. - */ - IGNORE("%"), - - /** - * Layer names starting with `!` trigger a pause. - */ - PAUSE("!") -} - -/** - * Configure an SVG layer name. Certain character sequences are used - * by the Axidraw software to control layer speed, height and delay. - * Other characters make the layer be ignored, or trigger a pause. - * The arguments in this function provide a typed approach to construct - * the layer name. - * See https://wiki.evilmadscientist.com/AxiDraw_Layer_Control - * - * @param layerName Human-readable layer name. Multiple layer can use the same name. - * @param penSpeed Pen down speed (1..100) - * @param penHeight Pen down height (0..100) - * @param plotDelay Delay before plotting this layer, in milliseconds - * @param layerMode The plotting mode for this layer. See [AxiLayerMode]. - */ -fun GroupNode.configure( - layerName: String = "layer", - penSpeed: Int? = null, - penHeight: Int? = null, - plotDelay: Int? = null, - layerMode: AxiLayerMode = AxiLayerMode.DEFAULT -) { - val layerNumber = (parent?.findGroups()?.size ?: 2) - 1 - - require(penSpeed == null || penSpeed in 1..100) { "Speed out of 1 .. 100 range" } - val actualSpeed = penSpeed?.let { "+S$it" } ?: "" - - require(penHeight == null || penHeight in 0..100) { "Height out of 0 .. 100 range" } - val actualHeight = penHeight?.let { "+H$it" } ?: "" - - require(plotDelay == null || plotDelay > 0) { "Delay value should null or above 0" } - val actualDelay = plotDelay?.let { "+D$it" } ?: "" - - attributes["inkscape:groupmode"] = "layer" - - attributes["inkscape:label"] = layerMode.command + layerNumber + - actualSpeed + actualHeight + actualDelay + " " + layerName -} - -/** - * Save a [Composition] to an Inkscape file. Includes expected XML namespaces - * and sets an XML header with the view window size. Strips an extra wrapping `` tag to - * make special layer names work with the Axidraw pen plotter. - * - * @param file Should point to the desired file name and path. - * @param postProcess Optional function to do post-processing on the SVG XML before saving it. - */ -fun Composition.saveToInkscapeFile( - file: File, - postProcess: (String) -> String = { xml -> xml } -) { - namespaces["xmlns:inkscape"] = "http://www.inkscape.org/namespaces/inkscape" - namespaces["xmlns:sodipodi"] = "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - namespaces["xmlns:svg"] = "http://www.w3.org/2000/svg" - - val svg = StringBuilder(toSVG()) - - val header = """ - - """.trimIndent() - - // Remove the wrapping , otherwise layers don't work. - // Also remove duplicated and which show up when - // drawing a composition into another composition. - val updated = svg.replace( - Regex("""((.*))""", RegexOption.DOT_MATCHES_ALL), "$2" - ).replace( - "(\\W?)+)+".toRegex(setOf(RegexOption.MULTILINE, RegexOption.DOT_MATCHES_ALL)), - "\n" - ).replace( - Regex("""()""", RegexOption.DOT_MATCHES_ALL), "$1$header" - ) - file.writeText(postProcess(updated)) -} diff --git a/orx-jvm/orx-axidraw/src/main/kotlin/python.kt b/orx-jvm/orx-axidraw/src/main/kotlin/python.kt deleted file mode 100644 index 727136be..00000000 --- a/orx-jvm/orx-axidraw/src/main/kotlin/python.kt +++ /dev/null @@ -1,71 +0,0 @@ -package org.openrndr.extra.axidraw - -import java.io.BufferedInputStream -import java.io.File -import java.io.IOException - -/** - * Determines the appropriate Python executable name based on the operating system. - * - * On Windows systems, it returns "python.exe", while on other operating systems, it returns "python3". - * - * @return The name of the Python executable appropriate for the current operating system. - */ -fun systemPython(): String { - val executable = if (System.getProperty("os.name").lowercase().contains("windows")) { - "python.exe" - } else { - "python3" - } - return executable -} - -/** - * Returns the path to the Python executable in a given virtual environment. - * The path varies depending on the operating system. - * - * @param venv the directory of the virtual environment - * @return the absolute path to the Python executable within the virtual environment - */ -fun venvPython(venv: File): String { - val executable = if (System.getProperty("os.name").lowercase().contains("windows")) { - "${venv.absolutePath}/Scripts/python.exe" - } else { - "${venv.absolutePath}/bin/python" - } - return executable -} - - -fun invokePython(arguments: List, executable: String = systemPython()): String { - val result: String - try { - - val pb = ProcessBuilder() - .let { - it.command(listOf(executable) + arguments) - //it.redirectError(File("python.error.txt")) - it.inheritIO() - } - .start() - .let { - val `is` = it.inputStream - val bis = BufferedInputStream(`is`) - val br = bis.bufferedReader() - result = br.readText().trim() - val error = it.waitFor() - println("Python returned: $error") - - // Error detection disabled because pressing the pause button on the Axidraw - // returns "1", and we don't want the program to close when that happens. - // There's no obvious way to distinguish between actual errors and pressing the pause button. - // if (error != 0) { - // error("Python invoke failed with error $error") - // } - } - } catch (e: IOException) { - error("\n\nPython 3.8 or higher is required but failed to run. Is it installed?\n\n") - } - - return result -} diff --git a/orx-jvm/orx-boofcv/README.md b/orx-jvm/orx-boofcv/README.md deleted file mode 100644 index be621267..00000000 --- a/orx-jvm/orx-boofcv/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# orx-boofcv - -Helper functions to ease working with the BoofCV computer vision library -and its data types. - -BoofCV is an open source library written from scratch for real-time -computer vision. Its functionality covers a range of subjects, -low-level image processing, camera calibration, feature detection/tracking, -structure-from-motion, fiducial detection, and recognition. -BoofCV has been released under an Apache 2.0 license for both -academic and commercial use. - -Examples of what BoofCV offers can be found at -[http://boofcv.org/](http://boofcv.org/) - -As BoofCV implements it's own data types for images, lines, points, etc. -this addon provides some helper functions to convert them to OPENRNDR types: - -- Bindings: converts to and from `ColorBuffer`. -- Drawing: allows directly drawing BoofCV line segments and other shapes. -- Point conversion to and from `Vector2`. -- Contour conversion from `BoofCV.Contour` to `Shape` and `ShapeContour`. -- `ImageFlow` to `ColorBuffer` conversion. - - -## Demos -### DemoContours01 - -Demonstrates how to convert a PNG image into `ShapeContour`s using BoofCV. - -Two helper methods help convert data types between BoofCV and OPENRNDR. - -The `ColorBuffer.toGrayF32()` method converts an OPENRNDR `ColorBuffer` to `GrayF32` format, -required by BoofCV. - -The `.toShapeContours()` converts BoofCV contours to OPENRNDR `ShapeContour` instances. - -The resulting contours are animated zooming in and out while their colors change slowly. - -![DemoContours01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-boofcv/images/DemoContours01Kt.png) - -[source code](src/demo/kotlin/DemoContours01.kt) - -### DemoResize01 - -Demonstrates how to scale down images using the `resizeBy` BoofCV-based -method. - -![DemoResize01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-boofcv/images/DemoResize01Kt.png) - -[source code](src/demo/kotlin/DemoResize01.kt) - -### DemoResize02 - -Demonstrates how to scale down images using the `resizeTo` BoofCV-based -method. - -If only the `newWidth` or the `newHeight` arguments are specified, -the resizing happens maintaining the original aspect ratio. - -![DemoResize02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-boofcv/images/DemoResize02Kt.png) - -[source code](src/demo/kotlin/DemoResize02.kt) - -### DemoSimplified01 - -When converting a `ColorBuffer` to `ShapeContour` instances using -`BoofCV`, simple shapes can have hundreds of segments and vertices. - -This demo shows how to use the `simplify()` method to greatly -reduce the number of vertices. - -Then it uses the simplified vertex lists to create smooth curves -(using `CatmullRomChain2`) and polygonal curves (using `ShapeContour.fromPoints`). - -Study the console to learn about the number of segments before and after simplification. - -![DemoSimplified01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-boofcv/images/DemoSimplified01Kt.png) - -[source code](src/demo/kotlin/DemoSimplified01.kt) diff --git a/orx-jvm/orx-boofcv/build.gradle.kts b/orx-jvm/orx-boofcv/build.gradle.kts deleted file mode 100644 index 9b0fa324..00000000 --- a/orx-jvm/orx-boofcv/build.gradle.kts +++ /dev/null @@ -1,10 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(openrndr.application.core) - implementation(openrndr.math) - demoImplementation(project(":orx-shapes")) - api(libs.boofcv) -} \ No newline at end of file diff --git a/orx-jvm/orx-boofcv/src/demo/kotlin/DemoContours01.kt b/orx-jvm/orx-boofcv/src/demo/kotlin/DemoContours01.kt deleted file mode 100644 index 8219c07b..00000000 --- a/orx-jvm/orx-boofcv/src/demo/kotlin/DemoContours01.kt +++ /dev/null @@ -1,79 +0,0 @@ -import boofcv.alg.filter.binary.BinaryImageOps -import boofcv.alg.filter.binary.GThresholdImageOps -import boofcv.alg.filter.binary.ThresholdImageOps -import boofcv.struct.ConnectRule -import boofcv.struct.image.GrayU8 -import org.openrndr.application -import org.openrndr.boofcv.binding.toGrayF32 -import org.openrndr.boofcv.binding.toShapeContours -import org.openrndr.color.ColorRGBa -import org.openrndr.color.rgb -import org.openrndr.draw.loadImage -import kotlin.math.cos -import kotlin.math.sin - -/** - * Demonstrates how to convert a PNG image into `ShapeContour`s using BoofCV. - * - * Two helper methods help convert data types between BoofCV and OPENRNDR. - * - * The `ColorBuffer.toGrayF32()` method converts an OPENRNDR `ColorBuffer` to `GrayF32` format, - * required by BoofCV. - * - * The `.toShapeContours()` converts BoofCV contours to OPENRNDR `ShapeContour` instances. - * - * The resulting contours are animated zooming in and out while their colors change slowly. - */ -fun main() = application { - program { - // Load an image, convert to BoofCV format using orx-boofcv - val input = loadImage("demo-data/images/image-001.png").toGrayF32() - - // BoofCV: calculate a good threshold for the loaded image - val threshold = GThresholdImageOps.computeOtsu(input, 0.0, 255.0) - - // BoofCV: use the threshold to convert the image to black and white - val binary = GrayU8(input.width, input.height) - ThresholdImageOps.threshold(input, binary, threshold.toFloat(), false) - - // BoofCV: Contract and expand the white areas to remove noise - var filtered = BinaryImageOps.erode8(binary, 1, null) - filtered = BinaryImageOps.dilate8(filtered, 1, null) - - // BoofCV: Calculate contours as vector data - val contours = BinaryImageOps.contour(filtered, ConnectRule.EIGHT, null) - - // orx-boofcv: convert vector data to OPENRNDR ShapeContours - val externalShapes = contours.toShapeContours( - true, - internal = false, external = true - ) - val internalShapes = contours.toShapeContours( - true, - internal = true, external = false - ) - - extend { - drawer.run { - // Zoom in and out over time - translate(bounds.center) - scale(1.5 + 0.5 * cos(seconds * 0.2)) - translate(-bounds.center) - - stroke = null - - // Draw all external shapes - fill = rgb(0.2) - contours(externalShapes) - - // Draw internal shapes one by one to set unique colors - internalShapes.forEachIndexed { i, shp -> - val shade = 0.2 + (i % 7) * 0.1 + - 0.1 * sin(i + seconds) - fill = ColorRGBa.PINK.shade(shade) - contour(shp) - } - } - } - } -} diff --git a/orx-jvm/orx-boofcv/src/demo/kotlin/DemoResize01.kt b/orx-jvm/orx-boofcv/src/demo/kotlin/DemoResize01.kt deleted file mode 100644 index ca1e6f69..00000000 --- a/orx-jvm/orx-boofcv/src/demo/kotlin/DemoResize01.kt +++ /dev/null @@ -1,35 +0,0 @@ -import org.openrndr.application -import org.openrndr.boofcv.binding.resizeBy -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.loadImage -import org.openrndr.math.Vector2 - -/** - * Demonstrates how to scale down images using the `resizeBy` BoofCV-based - * method. - */ -fun main() = application { - program { - val input = loadImage("demo-data/images/image-001.png") - - val scaled = input.resizeBy(0.5) - val scaled2 = input.resizeBy(0.25, convertToGray = true) - val scaled3 = input.resizeBy(0.1) - - println("${input.width} x ${input.height}") - println("${scaled.width} x ${scaled.height}") - - extend { - drawer.clear(ColorRGBa.BLACK) - drawer.translate(0.0, (height - scaled.bounds.height) / 2.0) - - // Display the loaded image to the right of `scaled` matching its size - drawer.image(input, scaled.bounds.movedBy(Vector2.UNIT_X * scaled.bounds.width)) - - // Display actually scaled down versions of the loaded image - drawer.image(scaled) - drawer.image(scaled2, scaled.bounds.width, scaled.bounds.height - scaled2.height) - drawer.image(scaled3, scaled.bounds.width + scaled2.bounds.width, scaled.bounds.height - scaled3.height) - } - } -} diff --git a/orx-jvm/orx-boofcv/src/demo/kotlin/DemoResize02.kt b/orx-jvm/orx-boofcv/src/demo/kotlin/DemoResize02.kt deleted file mode 100644 index 283daa3d..00000000 --- a/orx-jvm/orx-boofcv/src/demo/kotlin/DemoResize02.kt +++ /dev/null @@ -1,34 +0,0 @@ -import org.openrndr.application -import org.openrndr.boofcv.binding.resizeTo -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.loadImage - -/** - * Demonstrates how to scale down images using the `resizeTo` BoofCV-based - * method. - * - * If only the `newWidth` or the `newHeight` arguments are specified, - * the resizing happens maintaining the original aspect ratio. - */ -fun main() = application { - program { - val input = loadImage("demo-data/images/image-001.png") - - val scaled = input.resizeTo(input.width / 3) - val scaled2 = input.resizeTo(newHeight = input.height / 4, convertToGray = true) - val scaled3 = input.resizeTo(input.width / 5, input.height / 5) - - println("${input.width} x ${input.height}") - println("${scaled.width} x ${scaled.height}") - - extend { - drawer.clear(ColorRGBa.BLACK) - drawer.translate(0.0, (height - scaled.bounds.height) / 2.0) - - // Display actually scaled down versions of the loaded image - drawer.image(scaled) - drawer.image(scaled2, scaled.bounds.width, scaled.bounds.height - scaled2.height) - drawer.image(scaled3, scaled.bounds.width + scaled2.bounds.width, scaled.bounds.height - scaled3.height) - } - } -} diff --git a/orx-jvm/orx-boofcv/src/demo/kotlin/DemoSimplified01.kt b/orx-jvm/orx-boofcv/src/demo/kotlin/DemoSimplified01.kt deleted file mode 100644 index 67879418..00000000 --- a/orx-jvm/orx-boofcv/src/demo/kotlin/DemoSimplified01.kt +++ /dev/null @@ -1,118 +0,0 @@ -import boofcv.alg.filter.binary.BinaryImageOps -import boofcv.alg.filter.binary.GThresholdImageOps -import boofcv.alg.filter.binary.ThresholdImageOps -import boofcv.struct.ConnectRule -import boofcv.struct.image.GrayU8 -import org.openrndr.application -import org.openrndr.boofcv.binding.toGrayF32 -import org.openrndr.boofcv.binding.toShapeContours -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.isolatedWithTarget -import org.openrndr.draw.renderTarget -import org.openrndr.extra.shapes.simplify.simplify -import org.openrndr.extra.shapes.splines.CatmullRomChain2 -import org.openrndr.extra.shapes.splines.toContour -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle -import org.openrndr.shape.ShapeContour - -/** - * When converting a `ColorBuffer` to `ShapeContour` instances using - * `BoofCV`, simple shapes can have hundreds of segments and vertices. - * - * This demo shows how to use the `simplify()` method to greatly - * reduce the number of vertices. - * - * Then it uses the simplified vertex lists to create smooth curves - * (using `CatmullRomChain2`) and polygonal curves (using `ShapeContour.fromPoints`). - * - * Study the console to learn about the number of segments before and after simplification. - */ -fun main() = application { - program { - // Create a buffer where to draw something for boofcv - val rt = renderTarget(width, height) { - colorBuffer() - depthBuffer() - } - // Draw some shapes on that buffer - drawer.isolatedWithTarget(rt) { - clear(ColorRGBa.BLACK) - fill = ColorRGBa.WHITE - stroke = null - rectangle( - Rectangle.fromCenter( - bounds.position(0.33, 0.5), - 150.0, 150.0 - ) - ) - translate(bounds.position(0.62, 0.5)) - rotate(30.0) - rectangle(Rectangle.fromCenter(Vector2.ZERO, 200.0, 200.0)) - rectangle(0.0, -200.0, 60.0, 60.0) - circle(0.0, 190.0, 60.0) - } - - // Convert the bitmap buffer into ShapeContours - val vectorized = imageToContours(rt.colorBuffer(0)) - - // Print the number of segments in each shape (high number) - vectorized.forEachIndexed { i, it -> - println("boofcv shape $i: ${it.segments.size} segments") - } - - // Make a simplified list of points - val simplePoints = vectorized.map { - simplify(it.adaptivePositions(), 4.0) - }.filter { it.size >= 3 } - - // Use the simplified list to make a smooth contour - val smooth = simplePoints.map { - CatmullRomChain2(it, 0.0, true).toContour() - } - - // Use the simplified list to make a polygonal contour - val polygonal = simplePoints.map { - ShapeContour.fromPoints(it, true) - } - - // Print the number of segments in simplified shapes (low number). - // Note: `smooth` and `polygonal` have the same number of segments - smooth.forEachIndexed { i, it -> - println("simplified shape $i: ${it.segments.size} segments") - } - - extend { - drawer.run { - fill = null // ColorRGBa.PINK.opacify(0.15) - - stroke = ColorRGBa.PINK.opacify(0.7) - contours(polygonal) - - stroke = ColorRGBa.GREEN.opacify(0.7) - contours(smooth) - } - } - } -} - -fun imageToContours(input: ColorBuffer): List { - val bitmap = input.toGrayF32() - // BoofCV: calculate a good threshold for the loaded image - val threshold = GThresholdImageOps.computeOtsu(bitmap, 0.0, 255.0) - - // BoofCV: use the threshold to convert the image to black and white - val binary = GrayU8(bitmap.width, bitmap.height) - ThresholdImageOps.threshold(bitmap, binary, threshold.toFloat(), false) - - // BoofCV: Contract and expand the white areas to remove noise - var filtered = BinaryImageOps.erode8(binary, 1, null) - filtered = BinaryImageOps.dilate8(filtered, 1, null) - - // BoofCV: Calculate contours as vector data - val contours = BinaryImageOps.contour(filtered, ConnectRule.EIGHT, null) - - // orx-boofcv: convert vector data to OPENRNDR ShapeContours - return contours.toShapeContours(true, internal = true, external = true) -} \ No newline at end of file diff --git a/orx-jvm/orx-boofcv/src/main/kotlin/Binding.kt b/orx-jvm/orx-boofcv/src/main/kotlin/Binding.kt deleted file mode 100644 index e593ce46..00000000 --- a/orx-jvm/orx-boofcv/src/main/kotlin/Binding.kt +++ /dev/null @@ -1,186 +0,0 @@ -package org.openrndr.boofcv.binding - -import boofcv.struct.image.GrayF32 -import boofcv.struct.image.GrayF64 -import boofcv.struct.image.GrayU8 -import boofcv.struct.image.Planar -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.ColorFormat -import org.openrndr.draw.ColorType -import org.openrndr.draw.colorBuffer - -fun ColorBuffer.toGrayF32() : GrayF32 { - val p = GrayF32(width, height) - shadow.download() - - var offset = 0 - for (y in 0 until height) { - for (x in 0 until width) { - val c = shadow.read(x, y) - p.data[offset] = (c.r * 255).toFloat() - offset++ - } - } - return p -} - -fun ColorBuffer.toGrayF64() : GrayF64 { - val p = GrayF64(width, height) - shadow.download() - - var offset = 0 - for (y in 0 until height) { - for (x in 0 until width) { - val c = shadow.read(x, y) - p.data[offset] = (c.r * 255) - offset++ - } - } - return p -} - -fun ColorBuffer.toPlanarF32() : Planar { - val p = Planar(GrayF32::class.java, width, height, format.componentCount) - shadow.download() - - val bands = p.bands - - var offset = 0 - for (y in 0 until height) { - for (x in 0 until width) { - val c = shadow.read(x, y) - bands[0].data[offset] = (c.r * 255).toFloat() - bands[1].data[offset] = (c.g * 255).toFloat() - bands[2].data[offset] = (c.b * 255).toFloat() - offset++ - } - } - return p -} - -fun ColorBuffer.toPlanarU8() : Planar { - val p = Planar(GrayU8::class.java, width, height, format.componentCount) - shadow.download() - - val bands = p.bands - - var offset = 0 - for (y in 0 until height) { - for (x in 0 until width) { - val c = shadow.read(x, y) - bands[0].data[offset] = (c.r * 255).toInt().toByte() - bands[1].data[offset] = (c.g * 255).toInt().toByte() - bands[2].data[offset] = (c.b * 255).toInt().toByte() - offset++ - } - } - return p -} - -fun ColorBuffer.toGrayU8() : GrayU8 { - val p = GrayU8(width, height) - shadow.download() - - var offset = 0 - for (y in 0 until height) { - for (x in 0 until width) { - val c = shadow.read(x, y) - p.data[offset] = (c.r * 255).toInt().coerceIn(0, 255).toByte() - offset++ - } - } - return p -} - - -fun GrayU8.toColorBuffer() : ColorBuffer { - val cb = colorBuffer(width, height, 1.0, ColorFormat.RGB, ColorType.UINT8) - val shadow = cb.shadow - shadow.buffer.rewind() - var offset = 0 - for (y in 0 until height) { - for (x in 0 until width) { - val r = (data[offset].toInt() and 0xff).toDouble() / 255.0 - offset++ - shadow.write(x, y, ColorRGBa(r, r, r, 1.0)) - } - } - shadow.upload() - return cb -} - -fun GrayF32.toColorBuffer() : ColorBuffer { - val cb = colorBuffer(width, height, 1.0, ColorFormat.RGB, ColorType.FLOAT32) - val shadow = cb.shadow - shadow.buffer.rewind() - var offset = 0 - for (y in 0 until height) { - for (x in 0 until width) { - val r = data[offset].toDouble() / 255.0 - offset++ - shadow.write(x, y, ColorRGBa(r, r, r)) - } - } - shadow.upload() - return cb -} - -fun Planar.toColorBuffer() : ColorBuffer { - val bandCount = bands.size - val format = when (bandCount) { - 1 -> ColorFormat.R - 2 -> ColorFormat.RG - 3 -> ColorFormat.RGB - 4 -> ColorFormat.RGBa - else -> throw IllegalArgumentException("only 1 to 4 bands supported") - } - - val bands = bands - val cb = colorBuffer(width, height, 1.0, format, ColorType.UINT8) - val shadow = cb.shadow - shadow.buffer.rewind() - var offset = 0 - for (y in 0 until height) { - for (x in 0 until width) { - val r = (bands[0].data[offset].toInt() and 0xff).toDouble() / 255.0 - val g = if (bandCount >= 2) (bands[1].data[offset].toInt() and 0xff).toDouble() / 255.0 else 0.0 - val b = if (bandCount >= 3) (bands[2].data[offset].toInt() and 0xff).toDouble() / 255.0 else 0.0 - val a = if (bandCount >= 4) (bands[2].data[offset].toInt() and 0xff).toDouble() / 255.0 else 1.0 - offset++ - shadow.write(x, y, ColorRGBa(r, g, b, a)) - } - } - shadow.upload() - return cb -} - -@JvmName("grayF32ToColorBuffer") -fun Planar.toColorBuffer() : ColorBuffer { - val bandCount = bands.size - val format = when (bandCount) { - 1 -> ColorFormat.R - 2 -> ColorFormat.RG - 3 -> ColorFormat.RGB - 4 -> ColorFormat.RGBa - else -> throw IllegalArgumentException("only 1 to 4 bands supported") - } - - val bands = bands - val cb = colorBuffer(width, height, 1.0, format, ColorType.UINT8) - val shadow = cb.shadow - shadow.buffer.rewind() - var offset = 0 - for (y in 0 until height) { - for (x in 0 until width) { - val r = bands[0].data[offset] / 255.0 - val g = if (bandCount >= 2) bands[1].data[offset] / 255.0 else 0.0 - val b = if (bandCount >= 3) bands[2].data[offset] / 255.0 else 0.0 - val a = if (bandCount >= 4) bands[3].data[offset] / 255.0 else 1.0 - offset++ - shadow.write(x, y, ColorRGBa(r, g, b, a)) - } - } - shadow.upload() - return cb -} \ No newline at end of file diff --git a/orx-jvm/orx-boofcv/src/main/kotlin/ContourConversion.kt b/orx-jvm/orx-boofcv/src/main/kotlin/ContourConversion.kt deleted file mode 100644 index 89d3d185..00000000 --- a/orx-jvm/orx-boofcv/src/main/kotlin/ContourConversion.kt +++ /dev/null @@ -1,51 +0,0 @@ -package org.openrndr.boofcv.binding - -import boofcv.alg.filter.binary.Contour -import org.openrndr.shape.Shape -import org.openrndr.shape.ShapeContour - -fun Contour.toShape(closed: Boolean = false, getInternal: Boolean, getExternal: Boolean): Shape { - val contours = mutableListOf() - - if (getExternal) { - val externalPoints = external.toVector2s() - contours.addAll(listOf(ShapeContour.fromPoints(externalPoints, closed))) - } - if (getInternal) { - val internalCurves = internal.filter { it.size >= 3 }.map { it.toVector2s() } - contours.addAll(internalCurves.map { internalCurve -> - ShapeContour.fromPoints(internalCurve, closed) - }) - } - return Shape(contours) -} - -fun List.toShapes(closed: Boolean = false, - internal: Boolean = true, - external: Boolean = true): List { - return this.filter { it.external.size >= 3 }.map { - it.toShape(closed, internal, external) - } -} - -fun List.toShapeContours(closed: Boolean = false, - internal: Boolean = true, - external: Boolean = true): List { - val contours = mutableListOf() - this.forEach { contour -> - if(contour.external.size >= 3) { - if (external) { - val externalPoints = contour.external.toVector2s() - contours.add(ShapeContour.fromPoints(externalPoints, closed)) - } - if (internal) { - val internalCurves = contour.internal.filter { it.size >= 3 } - .map { it.toVector2s() } - internalCurves.forEach { internalContour -> - contours.add(ShapeContour.fromPoints(internalContour, closed)) - } - } - } - } - return contours -} diff --git a/orx-jvm/orx-boofcv/src/main/kotlin/Distortion.kt b/orx-jvm/orx-boofcv/src/main/kotlin/Distortion.kt deleted file mode 100644 index 3b79b948..00000000 --- a/orx-jvm/orx-boofcv/src/main/kotlin/Distortion.kt +++ /dev/null @@ -1,70 +0,0 @@ -package org.openrndr.boofcv.binding - -import boofcv.abst.distort.FDistort -import boofcv.struct.image.ImageBase -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.ColorType -import kotlin.math.roundToInt - -fun >?> ImageBase.resizeBy(scaleX: Double, scaleY: Double = scaleX): T { - val scaled = this.createNew((this.width * scaleX).toInt(), (this.height * scaleY).toInt()) - - FDistort(this, scaled).scaleExt().apply() - - return scaled -} - -fun >?> ImageBase.resizeTo(newWidth: Int? = null, newHeight: Int? = null): T { - val ar = this.width / this.height.toDouble() - - val scaled = (if (newWidth != null && newHeight != null) { - val w = newWidth - val h = newHeight - - this.createNew(w, h) - } else if (newWidth != null && newHeight == null) { - val w = newWidth - val h = newWidth / ar - - this.createNew(w, h.roundToInt()) - } else if (newWidth == null && newHeight != null) { - val w = newHeight * ar - val h = newHeight - - this.createNew(w.roundToInt(), h) - } else { - this.createNew(this.width, this.height) - }) - - FDistort(this, scaled).scaleExt().apply() - - return scaled -} - -fun ColorBuffer.resizeBy(scaleX: Double, scaleY: Double = scaleX, convertToGray: Boolean = false): ColorBuffer { - return if (convertToGray) { - when (this.type) { - ColorType.FLOAT32, ColorType.FLOAT16 -> this.toGrayF32().resizeBy(scaleX, scaleY).toColorBuffer() - else -> this.toGrayU8().resizeBy(scaleX, scaleY).toColorBuffer() - } - } else { - when (this.type) { - ColorType.FLOAT32, ColorType.FLOAT16 -> this.toPlanarF32().resizeBy(scaleX, scaleY).toColorBuffer() - else -> this.toPlanarU8().resizeBy(scaleX, scaleY).toColorBuffer() - } - } -} - -fun ColorBuffer.resizeTo(newWidth: Int? = null, newHeight: Int? = null, convertToGray: Boolean = false): ColorBuffer { - return if (convertToGray) { - when (this.type) { - ColorType.FLOAT32, ColorType.FLOAT16 -> this.toGrayF32().resizeTo(newWidth, newHeight).toColorBuffer() - else -> this.toGrayU8().resizeTo(newWidth, newHeight).toColorBuffer() - } - } else { - when (this.type) { - ColorType.FLOAT32, ColorType.FLOAT16 -> this.toPlanarF32().resizeTo(newWidth, newHeight).toColorBuffer() - else -> this.toPlanarU8().resizeTo(newWidth, newHeight).toColorBuffer() - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-boofcv/src/main/kotlin/Drawing.kt b/orx-jvm/orx-boofcv/src/main/kotlin/Drawing.kt deleted file mode 100644 index 06df7654..00000000 --- a/orx-jvm/orx-boofcv/src/main/kotlin/Drawing.kt +++ /dev/null @@ -1,83 +0,0 @@ -package org.openrndr.boofcv.binding - -import georegression.struct.line.LineSegment2D_F32 -import georegression.struct.line.LineSegment2D_F64 -import georegression.struct.trig.Circle2D_F32 -import georegression.struct.trig.Circle2D_F64 -import org.openrndr.draw.Drawer -import org.openrndr.math.Vector2 -import org.openrndr.shape.Circle - -fun Drawer.lineSegment(segment: LineSegment2D_F32) { - lineSegment( - segment.a.x.toDouble(), - segment.a.y.toDouble(), - segment.b.x.toDouble(), - segment.b.y.toDouble() - ) -} - -@JvmName("lineSegments2D_F32") -fun Drawer.lineSegments(segments: List) { - lineSegments( - segments.flatMap { segment -> - listOf( - Vector2(segment.a.x.toDouble(), segment.a.y.toDouble()), - Vector2(segment.b.x.toDouble(), segment.b.y.toDouble()) - ) - } - ) -} - -fun Drawer.lineSegment(segment: LineSegment2D_F64) { - lineSegment( - segment.a.x, - segment.a.y, - segment.b.x, - segment.b.y - ) -} - -@JvmName("lineSegments2D_F64") -fun Drawer.lineSegments(segments: List) { - lineSegments( - segments.flatMap { segment -> - listOf( - Vector2(segment.a.x, segment.a.y), - Vector2(segment.b.x, segment.b.y) - ) - } - ) -} - -fun Drawer.circle(circle: Circle2D_F32) { - circle( - circle.center.x.toDouble(), circle.center.y.toDouble(), - circle.radius.toDouble() - ) -} - -fun Drawer.circle(circle: Circle2D_F64) { - circle( - circle.center.x, circle.center.y, - circle.radius - ) -} - -@JvmName("circles2D_F32") -fun Drawer.circles(circles: List) { - circles( - circles.map { - Circle(it.center.x.toDouble(), it.center.y.toDouble(), it.radius.toDouble()) - } - ) -} - -@JvmName("circles2D_F64") -fun Drawer.circles(circles: List) { - circles( - circles.map { - Circle(it.center.x.toDouble(), it.center.y.toDouble(), it.radius.toDouble()) - } - ) -} \ No newline at end of file diff --git a/orx-jvm/orx-boofcv/src/main/kotlin/ImageFlowConversion.kt b/orx-jvm/orx-boofcv/src/main/kotlin/ImageFlowConversion.kt deleted file mode 100644 index 2e6f63d6..00000000 --- a/orx-jvm/orx-boofcv/src/main/kotlin/ImageFlowConversion.kt +++ /dev/null @@ -1,33 +0,0 @@ -package org.openrndr.boofcv.binding - -import boofcv.struct.flow.ImageFlow -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.ColorFormat -import org.openrndr.draw.ColorType -import org.openrndr.draw.colorBuffer -import java.nio.Buffer -import java.nio.ByteBuffer -import java.nio.ByteOrder - -fun ImageFlow.toColorBuffer(): ColorBuffer { - - val cb = colorBuffer( - width, height, format = ColorFormat.RG, - type = ColorType.FLOAT32 - ) - - val bb = ByteBuffer.allocateDirect(width * height * 8) - bb.order(ByteOrder.nativeOrder()) - for (y in 0 until height) { - for (x in 0 until width) { - val f = get(x, y) - bb.putFloat(f.x) - bb.putFloat(f.y) - } - } - - (bb as Buffer).rewind() - cb.write(bb) - cb.flipV = true - return cb -} \ No newline at end of file diff --git a/orx-jvm/orx-boofcv/src/main/kotlin/MatrixConversion.kt b/orx-jvm/orx-boofcv/src/main/kotlin/MatrixConversion.kt deleted file mode 100644 index dc569b79..00000000 --- a/orx-jvm/orx-boofcv/src/main/kotlin/MatrixConversion.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.openrndr.boofcv.binding - -import georegression.struct.affine.Affine2D_F32 -import georegression.struct.affine.Affine2D_F64 -import org.openrndr.math.Matrix44 - -fun Affine2D_F32.toMatrix44() = Matrix44( - c0r0 = a11.toDouble(), c1r0 = a12.toDouble(), c3r0 = tx.toDouble(), - c0r1 = a21.toDouble(), c1r1 = a22.toDouble(), c3r1 = ty.toDouble(), - c2r2 = 1.0, - c3r3 = 1.0 -) - -fun Affine2D_F64.toMatrix44() = Matrix44( - c0r0 = a11, c1r0 = a12, c3r0 = tx, - c0r1 = a21, c1r1 = a22, c3r1 = ty, - c2r2 = 1.0, - c3r3 = 1.0 -) \ No newline at end of file diff --git a/orx-jvm/orx-boofcv/src/main/kotlin/PointConversion.kt b/orx-jvm/orx-boofcv/src/main/kotlin/PointConversion.kt deleted file mode 100644 index 7a8d06b9..00000000 --- a/orx-jvm/orx-boofcv/src/main/kotlin/PointConversion.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.openrndr.boofcv.binding - -import georegression.struct.point.Point2D_F32 -import georegression.struct.point.Point2D_F64 -import georegression.struct.point.Point2D_I32 -import org.openrndr.math.Vector2 - -fun Point2D_I32.toVector2() = Vector2(x.toDouble(), y.toDouble()) -fun Point2D_F32.toVector2() = Vector2(x.toDouble(), y.toDouble()) -fun Point2D_F64.toVector2() = Vector2(x.toDouble(), y.toDouble()) -fun List.toVector2s() = this.map { it.toVector2() } diff --git a/orx-jvm/orx-chataigne/README.md b/orx-jvm/orx-chataigne/README.md deleted file mode 100644 index a3fd4834..00000000 --- a/orx-jvm/orx-chataigne/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# orx-chataigne - -Expose variables to [Chataigne](http://benjamin.kuperberg.fr/chataigne/en) and any other applications that can interface with it. -The current implementation makes use of the OSC protocol and supports `Double` and `ColorRGBa`. - -## Usage - -Defining the variables -```kotlin -class SceneVariables : ChataigneOSC(OSC(portIn = 9005, portOut = 12001)) { - val myRadius: Double by DoubleChannel("/myRadius") - val myOpacity: Double by DoubleChannel("/myOpacity") - val myColor: ColorRGBa by ColorChannel("/myColor") -} -``` - -Initiate - -```kotlin - val animation = SceneVariables() -``` - -Update time - -```kotlin -animation.update(seconds) -``` - -Use the variables - -```kotlin -animation.myRadius -animation.myOpacity -animation.myColor -``` - -## Example project - -Find the Chataigne example project in `/resources/timeline_example_chataigne.noisette` which works together with demo project `/src/demo/kotlin/ChataigneOSCDemo.kt` - - -## Demos -### ChataigneOSCDemo - - - -![ChataigneOSCDemoKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-chataigne/images/ChataigneOSCDemoKt.png) - -[source code](src/demo/kotlin/ChataigneOSCDemo.kt) diff --git a/orx-jvm/orx-chataigne/build.gradle.kts b/orx-jvm/orx-chataigne/build.gradle.kts deleted file mode 100644 index 3a9e7eba..00000000 --- a/orx-jvm/orx-chataigne/build.gradle.kts +++ /dev/null @@ -1,12 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(openrndr.application.core) - implementation(openrndr.math) - api(project(":orx-jvm:orx-osc")) - implementation(libs.gson) - demoImplementation(openrndr.ffmpeg) - demoImplementation(project(":orx-fx")) -} \ No newline at end of file diff --git a/orx-jvm/orx-chataigne/resources/timeline_example_chataigne.noisette b/orx-jvm/orx-chataigne/resources/timeline_example_chataigne.noisette deleted file mode 100644 index ae8a7381..00000000 --- a/orx-jvm/orx-chataigne/resources/timeline_example_chataigne.noisette +++ /dev/null @@ -1 +0,0 @@ -{"metaData": {"version": "1.7.2", "versionNumber": 67330}, "projectSettings": {"containers": {"dashboardSettings": {"parameters": [{"value": false, "controlAddress": "/enableDashboardServer"}, {"value": 9999, "hexMode": false, "controlAddress": "/serverPort"}]}}}, "layout": {"mainLayout": {"type": 1, "width": 1496, "height": 1371, "direction": 2, "shifters": [{"type": 1, "width": 1496, "height": 1371, "direction": 2, "shifters": [{"type": 1, "width": 1496, "height": 761, "direction": 1, "shifters": [{"type": 1, "width": 307, "height": 761, "direction": 2, "shifters": [{"type": 0, "width": 307, "height": 387, "currentContent": "Modules", "tabs": [{"name": "Modules"}]}, {"type": 0, "width": 307, "height": 367, "currentContent": "Custom Variables", "tabs": [{"name": "Custom Variables"}]}]}, {"type": 0, "width": 749, "height": 761, "currentContent": "State Machine", "tabs": [{"name": "State Machine"}, {"name": "Dashboard"}, {"name": "Module Router"}, {"name": "Morpher"}]}, {"type": 0, "width": 428, "height": 761, "currentContent": "Inspector", "tabs": [{"name": "Inspector"}]}]}, {"type": 1, "width": 1496, "height": 603, "direction": 1, "shifters": [{"type": 0, "width": 178, "height": 603, "currentContent": "Sequences", "tabs": [{"name": "Sequences"}]}, {"type": 0, "width": 867, "height": 603, "currentContent": "Sequence Editor", "tabs": [{"name": "Sequence Editor"}]}, {"type": 0, "width": 439, "height": 603, "currentContent": "Logger", "tabs": [{"name": "Help"}, {"name": "Logger"}, {"name": "Warnings"}]}]}]}]}, "windows": null}, "modules": {"items": [{"parameters": [{"value": true, "controlAddress": "/enabled"}, {"value": true, "controlAddress": "/logIncoming"}, {"value": true, "controlAddress": "/logOutgoing"}], "niceName": "OSC", "type": "OSC", "scripts": {}, "params": {"parameters": [{"value": false, "controlAddress": "/splitArguments"}, {"value": true, "controlAddress": "/autoFeedback"}], "containers": {"oscInput": {"parameters": [{"value": 12001, "hexMode": false, "controlAddress": "/localPort"}]}, "oscOutputs": {"editorIsCollapsed": true, "items": [{"parameters": [{"value": "127.0.0.1", "controlAddress": "/remoteHost"}, {"value": 9005, "hexMode": false, "controlAddress": "/remotePort"}], "niceName": "OSC Output", "type": "BaseItem"}]}, "pass_through": {}}}, "values": {"parameters": [{"value": [0.0, 0.501960813999176, 0.0, 1.0], "controlMode": 2, "reference": {"value": "/sequences/sequence/layers/color/colors/color", "controlAddress": "/reference"}, "controlAddress": "/_myColor", "type": "Color", "niceName": "/myColor", "customizable": true, "removable": true, "description": "", "hideInEditor": false, "feedbackOnly": false}, {"value": 0.8833333253860474, "controlMode": 2, "reference": {"value": "/sequences/sequence/layers/opacity/automation/value", "controlAddress": "/reference"}, "controlAddress": "/_myOpacity", "type": "Float", "niceName": "/myOpacity", "customizable": true, "removable": true, "description": "", "hideInEditor": false, "feedbackOnly": false}, {"value": 0.8666667342185974, "controlMode": 2, "reference": {"value": "/sequences/sequence/layers/radius/automation/value", "controlAddress": "/reference"}, "controlAddress": "/_myRadius", "type": "Float", "niceName": "/myRadius", "customizable": true, "removable": true, "description": "", "hideInEditor": false, "feedbackOnly": false}, {"value": 11.77089214324951, "controlAddress": "/_setTime", "type": "Float", "niceName": "/setTime", "customizable": true, "removable": true, "description": "", "hideInEditor": false, "feedbackOnly": false, "shortName": "_setTime"}]}}]}, "states": {"items": [{"parameters": [{"value": [-80.0, -142.0], "controlAddress": "/viewUIPosition"}, {"value": [287.0, 243.0], "controlAddress": "/viewUISize"}, {"value": true, "controlAddress": "/active"}], "niceName": "State", "type": "State", "processors": {"items": [{"niceName": "OSC Trigger", "editorIsCollapsed": true, "type": "Mapping", "im": {"items": [{"parameters": [{"value": "/modules/osc/values/_setTime", "controlAddress": "/inputValue"}], "niceName": "Input", "type": "BaseItem"}]}, "filters": {"items": [{"niceName": "Math", "type": "Math", "filterParams": {"parameters": [{"value": "Modulo", "controlAddress": "/operation"}, {"value": "Adjust", "controlAddress": "/rangeRemapMode"}]}, "operationValue": {"value": 10.0, "controlMode": 2, "reference": {"value": "/sequences/sequence/totalTime", "controlAddress": "/reference"}, "controlAddress": "/states/state/processors/oscTrigger/filters/math/filterParams/value"}}]}, "outputs": {"items": [{"niceName": "MappingOutput 1", "type": "BaseItem", "commandModule": "sequences", "commandPath": "Time", "commandType": "Set Current Time", "command": {"parameters": [{"value": "/sequences/sequence", "controlAddress": "/target"}, {"value": 1.770892143249512, "controlAddress": "/time"}, {"value": false, "controlAddress": "/play"}]}}]}}]}}]}, "sequences": {"items": [{"parameters": [{"value": 5.0, "controlAddress": "/listSize"}, {"value": 10.0, "controlAddress": "/totalTime"}, {"value": false, "controlAddress": "/loop"}, {"value": 10.0, "controlAddress": "/viewEndTime"}, {"value": ["", ""], "controlAddress": "/syncDevices"}], "niceName": "Sequence", "type": "Sequence", "layers": {"items": [{"parameters": [{"value": 87.0, "controlAddress": "/listSize"}, {"value": 120, "hexMode": false, "controlAddress": "/uiHeight"}, {"value": [0.2117647081613541, 0.2117647081613541, 0.2117647081613541, 1.0], "controlAddress": "/layerColor"}, {"value": 0.8666667342185974, "controlAddress": "/value"}], "niceName": "Radius", "containers": {"automation": {"parameters": [{"value": 10.0, "controlAddress": "/length"}, {"value": 0.0, "controlAddress": "/position"}, {"value": 0.8666667342185974, "controlAddress": "/value"}, {"value": [0.0, 1.0], "controlAddress": "/viewValueRange"}, {"value": [0.0, 1.0], "controlAddress": "/range", "enabled": true}], "items": [{"parameters": [{"value": 0.0, "controlAddress": "/position"}, {"value": 0.8666667342185974, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 18", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 1.089901447296143, "controlAddress": "/position"}, {"value": 0.3083333373069763, "controlAddress": "/value"}, {"value": "Bezier", "controlAddress": "/easingType"}], "niceName": "Key", "containers": {"easing": {"parameters": [{"value": [0.5597290992736816, 0.0], "controlAddress": "/anchor1"}, {"value": [-0.1908860206604004, -0.0166667103767395], "controlAddress": "/anchor2"}]}}, "type": "Key"}, {"parameters": [{"value": 1.896896004676819, "controlAddress": "/position"}, {"value": 0.4416666030883789, "controlAddress": "/value"}, {"value": "Bezier", "controlAddress": "/easingType"}], "niceName": "Key 1", "containers": {"easing": {"parameters": [{"value": [0.1908860206604004, 0.0166667103767395], "controlAddress": "/anchor1"}, {"value": [-0.1785714626312256, 0.008333325386047363], "controlAddress": "/anchor2"}]}}, "type": "Key"}, {"parameters": [{"value": 2.278325080871582, "controlAddress": "/position"}, {"value": 0.8500000238418579, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 2", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 3.004926204681396, "controlAddress": "/position"}, {"value": 0.8583332896232605, "controlAddress": "/value"}, {"value": "Sine", "controlAddress": "/easingType"}], "niceName": "Key 3", "containers": {"easing": {"parameters": [{"value": [1.0, 0.25], "controlAddress": "/frequencyAmplitude"}]}}, "type": "Key"}, {"parameters": [{"value": 4.051724433898926, "controlAddress": "/position"}, {"value": 0.449999988079071, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 4", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 4.753694534301758, "controlAddress": "/position"}, {"value": 0.4583333134651184, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 5", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 5.01231575012207, "controlAddress": "/position"}, {"value": 0.7083333134651184, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 6", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 5.123153209686279, "controlAddress": "/position"}, {"value": 0.8000000715255737, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 7", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 5.264778137207031, "controlAddress": "/position"}, {"value": 0.5916666388511658, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 8", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 5.671182155609131, "controlAddress": "/position"}, {"value": 0.2083332538604736, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 9", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 6.133005142211914, "controlAddress": "/position"}, {"value": 0.5083333253860474, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 10", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 6.280788421630859, "controlAddress": "/position"}, {"value": 0.6916666626930237, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 11", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 6.508620738983154, "controlAddress": "/position"}, {"value": 0.4166666269302368, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 12", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 7.068964958190918, "controlAddress": "/position"}, {"value": 0.4166666269302368, "controlAddress": "/value"}, {"value": "Hold", "controlAddress": "/easingType"}], "niceName": "Key 13", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 8.084975242614746, "controlAddress": "/position"}, {"value": 0.9166666865348816, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 14", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 8.392856597900391, "controlAddress": "/position"}, {"value": 0.4333332777023315, "controlAddress": "/value"}, {"value": "Bezier", "controlAddress": "/easingType"}], "niceName": "Key 15", "containers": {"easing": {"parameters": [{"value": [0.2050492316484451, 0.0], "controlAddress": "/anchor1"}, {"value": [-0.2050492316484451, 0.0], "controlAddress": "/anchor2"}]}}, "type": "Key"}, {"parameters": [{"value": 9.076354026794434, "controlAddress": "/position"}, {"value": 0.8750000596046448, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 16", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 9.636699676513672, "controlAddress": "/position"}, {"value": 0.449999988079071, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 17", "containers": {"easing": {}}, "type": "Key"}]}, "recorder": {"parameters": [{"value": false, "controlAddress": "/arm"}, {"value": false, "controlAddress": "/isRecording"}], "editorIsCollapsed": true}, "mapping": {"niceName": "Mapping", "type": "Mapping", "im": {"items": [{"parameters": [{"value": "", "controlAddress": "/inputValue"}], "niceName": "Input", "type": "BaseItem"}]}, "filters": {}, "outputs": {}}}, "type": "Mapping"}, {"parameters": [{"value": 87.0, "controlAddress": "/listSize"}, {"value": 120, "hexMode": false, "controlAddress": "/uiHeight"}, {"value": [0.2117647081613541, 0.2117647081613541, 0.2117647081613541, 1.0], "controlAddress": "/layerColor"}, {"value": 0.8833333253860474, "controlAddress": "/value"}], "niceName": "Opacity", "containers": {"automation": {"parameters": [{"value": 10.0, "controlAddress": "/length"}, {"value": 0.0, "controlAddress": "/position"}, {"value": 0.8833333253860474, "controlAddress": "/value"}, {"value": [0.0, 1.0], "controlAddress": "/viewValueRange"}, {"value": [0.0, 1.0], "controlAddress": "/range", "enabled": true}], "items": [{"parameters": [{"value": 0.0, "controlAddress": "/position"}, {"value": 0.8833333253860474, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 1.68719220161438, "controlAddress": "/position"}, {"value": 0.449999988079071, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 1", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 3.152709245681763, "controlAddress": "/position"}, {"value": 0.2749999761581421, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 2", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 4.193350315093994, "controlAddress": "/position"}, {"value": 0.9583333730697632, "controlAddress": "/value"}, {"value": "Bezier", "controlAddress": "/easingType"}], "niceName": "Key 3", "containers": {"easing": {"parameters": [{"value": [0.5110831260681152, 0.0], "controlAddress": "/anchor1"}, {"value": [-0.5110831260681152, 0.0], "controlAddress": "/anchor2"}]}}, "type": "Key"}, {"parameters": [{"value": 4.913793087005615, "controlAddress": "/position"}, {"value": 0.3833333849906921, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 4", "containers": {"easing": {}}, "type": "Key"}, {"parameters": [{"value": 6.293103694915771, "controlAddress": "/position"}, {"value": 0.3583333492279053, "controlAddress": "/value"}, {"value": "Sine", "controlAddress": "/easingType"}], "niceName": "Key 5", "containers": {"easing": {"parameters": [{"value": [0.04926061630249023, 0.6416666507720947], "controlAddress": "/frequencyAmplitude"}]}}, "type": "Key"}, {"parameters": [{"value": 7.173645496368408, "controlAddress": "/position"}, {"value": 0.3833333253860474, "controlAddress": "/value"}, {"value": "Linear", "controlAddress": "/easingType"}], "niceName": "Key 6", "containers": {"easing": {}}, "type": "Key"}]}, "recorder": {"parameters": [{"value": false, "controlAddress": "/isRecording"}], "editorIsCollapsed": true}, "mapping": {"niceName": "Mapping", "type": "Mapping", "im": {"items": [{"parameters": [{"value": "", "controlAddress": "/inputValue"}], "niceName": "Input", "type": "BaseItem"}]}, "filters": {}, "outputs": {}}}, "type": "Mapping"}, {"parameters": [{"value": 27.0, "controlAddress": "/listSize"}, {"value": 60, "hexMode": false, "controlAddress": "/uiHeight"}, {"value": [0.2117647081613541, 0.2117647081613541, 0.2117647081613541, 1.0], "controlAddress": "/layerColor"}, {"value": [0.0, 0.501960813999176, 0.0, 1.0], "controlAddress": "/color"}], "niceName": "Color", "containers": {"mapping": {"niceName": "Mapping", "type": "Mapping", "im": {"items": [{"parameters": [{"value": "", "controlAddress": "/inputValue"}], "niceName": "Input", "type": "BaseItem"}]}, "filters": {}, "outputs": {}}, "colors": {"editorIsCollapsed": true, "items": [{"parameters": [{"value": 2.0, "controlAddress": "/time"}, {"value": [0.0, 0.501960813999176, 0.0, 1.0], "controlAddress": "/color"}], "niceName": "Color 1", "type": "BaseItem"}, {"parameters": [{"value": 4.0, "controlAddress": "/time"}, {"value": [1.0, 1.0, 0.0, 1.0], "controlAddress": "/color"}], "niceName": "Color 2", "type": "BaseItem"}, {"parameters": [{"value": 6.0, "controlAddress": "/time"}, {"value": "None", "controlAddress": "/interpolation"}, {"value": [1.0, 0.0, 0.0, 1.0], "controlAddress": "/color"}], "niceName": "Color 3", "type": "BaseItem"}, {"parameters": [{"value": 8.0, "controlAddress": "/time"}, {"value": [0.0, 0.0, 1.0, 1.0], "controlAddress": "/color"}], "niceName": "Color 4", "type": "BaseItem"}]}}, "type": "Color"}]}, "cues": {}, "editing": true}]}} \ No newline at end of file diff --git a/orx-jvm/orx-chataigne/src/demo/kotlin/ChataigneOSCDemo.kt b/orx-jvm/orx-chataigne/src/demo/kotlin/ChataigneOSCDemo.kt deleted file mode 100644 index 82c36c08..00000000 --- a/orx-jvm/orx-chataigne/src/demo/kotlin/ChataigneOSCDemo.kt +++ /dev/null @@ -1,29 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.osc.OSC - -fun main() = application { - - configure { - width = 500 - height = 500 - } - - /* Find the Chataigne example project in /resources */ - class SceneVariables : ChataigneOSC(OSC(portIn = 9005, portOut = 12001)) { - val myRadius: Double by DoubleChannel("/myRadius") - val myOpacity: Double by DoubleChannel("/myOpacity") - val myColor: ColorRGBa by ColorChannel("/myColor") - } - - program { - val animation = SceneVariables() - - extend { - animation.update(seconds) - - drawer.fill = animation.myColor.opacify(animation.myOpacity) - drawer.circle(width/2.0, height/2.0, animation.myRadius * 250) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-chataigne/src/main/kotlin/ChataigneOSC.kt b/orx-jvm/orx-chataigne/src/main/kotlin/ChataigneOSC.kt deleted file mode 100644 index 189417f4..00000000 --- a/orx-jvm/orx-chataigne/src/main/kotlin/ChataigneOSC.kt +++ /dev/null @@ -1,55 +0,0 @@ -import io.github.oshai.kotlinlogging.KotlinLogging -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.osc.OSC -import kotlin.reflect.KProperty - -private val logger = KotlinLogging.logger {} - -open class ChataigneOSC( - val osc: OSC -) { - inner class DoubleChannel(key: String) { - private var currentDouble = 0.0 - - init { - osc.listen(key) { _, message -> - currentDouble = (message[0] as Float).toDouble() - } - } - - operator fun getValue(thisRef: Any?, property: KProperty<*>): Double { - return currentDouble - } - - } - - inner class ColorChannel(key: String) { - private var currentColor = ColorRGBa.BLACK - - init { - osc.listen(key) { _, message -> - val red = message[0] as Float - val green = message[1] as Float - val blue = message[2] as Float - val alpha = message[3] as Float - - currentColor = ColorRGBa(red.toDouble(), green.toDouble(), blue.toDouble(), alpha.toDouble()) - } - } - - operator fun getValue(thisRef: Any?, property: KProperty<*>): ColorRGBa { - return currentColor - } - - } - - fun update(seconds: Double) { - osc.send("/setTime", seconds.toFloat()) - } - - init { - logger.info { - "setup Chataigne with OSC ${osc.address} in:${osc.portIn} out:${osc.portOut}" - } - } -} diff --git a/orx-jvm/orx-crash-handler/README.md b/orx-jvm/orx-crash-handler/README.md deleted file mode 100644 index 6a356e3f..00000000 --- a/orx-jvm/orx-crash-handler/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# orx-crash-handler - -Extension for reporting unhandled exceptions \ No newline at end of file diff --git a/orx-jvm/orx-crash-handler/build.gradle.kts b/orx-jvm/orx-crash-handler/build.gradle.kts deleted file mode 100644 index cd9c26d3..00000000 --- a/orx-jvm/orx-crash-handler/build.gradle.kts +++ /dev/null @@ -1,11 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") - alias(libs.plugins.kotlin.serialization) -} - -dependencies { - implementation(sharedLibs.kotlin.serialization.json) - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(libs.okhttp) -} \ No newline at end of file diff --git a/orx-jvm/orx-crash-handler/src/demo/kotlin/DemoCrashHandler01.kt b/orx-jvm/orx-crash-handler/src/demo/kotlin/DemoCrashHandler01.kt deleted file mode 100644 index a3cd08d5..00000000 --- a/orx-jvm/orx-crash-handler/src/demo/kotlin/DemoCrashHandler01.kt +++ /dev/null @@ -1,26 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.crashhandler.CrashHandler -import org.openrndr.extra.crashhandler.slack - -fun main() { - application { - configure { - width = 1280 - height = 720 - } - program { - extend(CrashHandler()) { - name = "jump-scare" - vncHost = "localhost" - slack { - authToken = System.getenv("SLACK_AUTH_TOKEN") - channelId = System.getenv("SLACK_CHANNEL_ID") - } - } - - extend { - error("something bad happened") - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-crash-handler/src/main/kotlin/CrashHandler.kt b/orx-jvm/orx-crash-handler/src/main/kotlin/CrashHandler.kt deleted file mode 100644 index 7551f0b9..00000000 --- a/orx-jvm/orx-crash-handler/src/main/kotlin/CrashHandler.kt +++ /dev/null @@ -1,49 +0,0 @@ -package org.openrndr.extra.crashhandler - -import io.github.oshai.kotlinlogging.KotlinLogging -import org.openrndr.Extension -import org.openrndr.Program -import java.io.File - -private val logger = KotlinLogging.logger { } - -class CrashHandler : Extension { - override var enabled: Boolean = true - - var name: String? = null - var vncHost: String? = null - - - val reporters = mutableListOf() - - - override fun setup(program: Program) { - if (name == null) - name = program.name - - Thread.setDefaultUncaughtExceptionHandler { t, e: Throwable -> - logger.error(e) { "Uncaught exception in thread $t" } - - for (reporter in reporters) { - try { - reporter.reportCrash(e) - } catch (e: Exception) { - println("error while reporting") - logger.error(e) { "reporter threw an exception" } - } - } - - val crashFile = File("${program.name}.crash") - val lastCrash = if (crashFile.exists()) crashFile.readText().toLongOrNull() ?: 0L else 0L - - crashFile.writeText("${System.currentTimeMillis()}") - if (System.currentTimeMillis() - lastCrash < 60 * 1000) { - logger.info { "crashed less than 60 seconds ago, sleeping for 60 seconds" } - Thread.sleep(60 * 1000L) - } - - System.exit(1) - } - } - -} \ No newline at end of file diff --git a/orx-jvm/orx-crash-handler/src/main/kotlin/Reporter.kt b/orx-jvm/orx-crash-handler/src/main/kotlin/Reporter.kt deleted file mode 100644 index 79e14d2f..00000000 --- a/orx-jvm/orx-crash-handler/src/main/kotlin/Reporter.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.openrndr.extra.crashhandler - -abstract class Reporter(val handler: CrashHandler) { - abstract fun reportCrash(throwable: Throwable) -} \ No newline at end of file diff --git a/orx-jvm/orx-crash-handler/src/main/kotlin/SlackReporter.kt b/orx-jvm/orx-crash-handler/src/main/kotlin/SlackReporter.kt deleted file mode 100644 index 85b15ac3..00000000 --- a/orx-jvm/orx-crash-handler/src/main/kotlin/SlackReporter.kt +++ /dev/null @@ -1,141 +0,0 @@ -package org.openrndr.extra.crashhandler - -import io.github.oshai.kotlinlogging.KotlinLogging -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.Response - - -@Serializable -private data class BlockResponse( - val type: String, - val text: TextElementResponse? = null, - val elements: List = listOf() -) - -@Serializable -private data class TextElementResponse( - val text: String, - val type: String, - val emoji: Boolean = false -) - -@Serializable -private data class BlockElementResponse( - val type: String, - val text: TextElementResponse, - val url: String? = null, - val style: String = "primary", -) - -@Serializable -private data class ChatPostMessageRequest( - val channel: String, - val blocks: List? = null, - val text: String? = null, - val thread_ts: String? = null -) - -@Serializable -private data class ChatPostMessageResponse( - val ok: Boolean, - val ts: String? = null, - val channel: String? = null -) - - -private val logger = KotlinLogging.logger { } - -class SlackReporter(handler: CrashHandler) : Reporter(handler) { - - var channelId: String = "" - var authToken: String = "" - - private val monitorJson = Json { - ignoreUnknownKeys = true - } - - private fun makeRequest(client: OkHttpClient, messageRequest: ChatPostMessageRequest): Response { - val body = monitorJson.encodeToString(messageRequest) - val requestBody = body.toRequestBody("application/json".toMediaType()) - - val replySlackRequest: Request = Request.Builder() - .url("https://slack.com/api/chat.postMessage") - .method("POST", requestBody) - .addHeader("Content-Type", "application/json") - .addHeader("Authorization", "Bearer $authToken") - .build() - - val response = client.newCall(replySlackRequest).execute() - require(response.isSuccessful) { - "request failed: ${response.code} ${response.message}" - } - - return response - } - - private fun plainText(text: String): TextElementResponse { - return TextElementResponse(text, "plain_text", false) - } - - private fun slackMessage(client: OkHttpClient, endpoint: String, error: Boolean = false, errorLog: String? = null) { - - val messageRequest = if (error) { - ChatPostMessageRequest(channel = channelId!!, thread_ts = null, - blocks = listOf( - BlockResponse("section", plainText("There is a problem with $endpoint. Please check.")), - BlockResponse("actions", elements = listOfNotNull( - - if (handler.vncHost != null) { - BlockElementResponse( - type = "button", - text = plainText("VNC into $endpoint"), - url = "vnc://${handler.vncHost}" - ) - } else { null } - ) - ) - ) - ) - } else { - ChatPostMessageRequest(channel = channelId!!, thread_ts = null, - blocks = listOf(BlockResponse("section", plainText("$endpoint is back online!"))) - ) - } - - val response = makeRequest(client, messageRequest) - - - if (error && response.isSuccessful) { - val cmr = monitorJson.decodeFromString(response.body?.string() ?: "") - - val logMessage = errorLog ?: "No log could be retrieved. Machine is likely unreachable" - - val replyRequest = ChatPostMessageRequest(channel = channelId, thread_ts = cmr.ts, - blocks = listOf( - BlockResponse( - type = "section", - text = TextElementResponse("```${logMessage}```", "mrkdwn", false) - ) - ), - ) - - makeRequest(client, replyRequest) - } - } - - - override fun reportCrash(throwable: Throwable) { - logger.info { "reporting " } - val client = OkHttpClient().newBuilder().build() - slackMessage(client, handler.name ?: "no name", true, throwable.stackTraceToString()) - } -} - -fun CrashHandler.slack(config: SlackReporter.() -> Unit) { - reporters.add(SlackReporter(this).apply(config)) -} \ No newline at end of file diff --git a/orx-jvm/orx-depth-camera-calibrator/README.md b/orx-jvm/orx-depth-camera-calibrator/README.md deleted file mode 100644 index c7c3f6cc..00000000 --- a/orx-jvm/orx-depth-camera-calibrator/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# orx-depth-camera-calibrator - -Class to help callibrate depth and transformation matrices when using one or more depth cameras. - -See [Kinect1Demo10DepthCameraCalibration.kt](https://github.com/openrndr/orx/blob/master/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo10DepthCameraCalibration.kt) for an example. diff --git a/orx-jvm/orx-depth-camera-calibrator/build.gradle.kts b/orx-jvm/orx-depth-camera-calibrator/build.gradle.kts deleted file mode 100644 index 94435b2c..00000000 --- a/orx-jvm/orx-depth-camera-calibrator/build.gradle.kts +++ /dev/null @@ -1,11 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(project(":orx-fx")) - api(project(":orx-depth-camera")) - api(project(":orx-jvm:orx-gui")) -} \ No newline at end of file diff --git a/orx-jvm/orx-depth-camera-calibrator/src/main/kotlin/DepthCameraCalibrator.kt b/orx-jvm/orx-depth-camera-calibrator/src/main/kotlin/DepthCameraCalibrator.kt deleted file mode 100644 index d29487e5..00000000 --- a/orx-jvm/orx-depth-camera-calibrator/src/main/kotlin/DepthCameraCalibrator.kt +++ /dev/null @@ -1,267 +0,0 @@ -package org.openrndr.extra.depth.camera.calibrator - -import org.openrndr.* -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.Drawer -import org.openrndr.draw.colorBuffer -import org.openrndr.draw.isolated -import org.openrndr.extra.depth.camera.DepthCamera -import org.openrndr.extra.depth.camera.DepthMeasurement -import org.openrndr.extra.fx.colormap.TurboColormap -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.parameters.* -import org.openrndr.math.IntVector2 -import org.openrndr.math.Vector2 - -/** - * Depth camera calibrator extension. - * - * @param program the program using this extension, Note: normally - * we would pass program in [setup], however there is a - * cyclic dependency between GUI and calibrator, so some - * dimensions have to be established before setup. - * See Kinect1Demo10DepthCameraCalibration.kt. - * @param depthCameras depth cameras to calibrate. - */ -class DepthCameraCalibrator( - private val program: Program, - vararg depthCameras: DepthCamera -) : Extension { - - init { - check(depthCameras.isNotEmpty()) { - "depthCameras cannot be empty" - } - depthCameras.forEach { - check(it.depthMeasurement == DepthMeasurement.METERS) { - "depthCameras: calibration requires depthMeasurement of each camera to be set to METERS" - } - } - } - - override var enabled: Boolean - get() = commonParameters.calibratorView - set(value) { commonParameters.calibratorView = value } - - private val resolution = IntVector2(program.width, program.height).vector2 - - private val calibrations = depthCameras.map { Calibration(it) }.toList() - - private val colormap = TurboColormap() - - private var onCalibrationChange: (calibration: Calibration) -> Unit = - { _ -> } // empty on startup - - override fun setup(program: Program) { - program.keyboard.keyDown.listen { - if (enabled) { - handleKeyDown(it) - } - } - } - - override fun afterDraw(drawer: Drawer, program: Program) { - calibrations.forEach { - colormap.minValue = it.minDepth - colormap.maxValue = it.maxDepth - colormap.apply(it.camera.currentFrame, it.colorBuffer) - drawer.isolatedWithCalibration(it) { - image( - colorBuffer = it.colorBuffer, - position = it.position, - width = it.width, - height = it.height - ) - } - } - } - - fun handleKeyDown(event: KeyEvent) { - when(event.name) { - "1" -> commonParameters.allMinDepth -= CENTIMETER - "2" -> commonParameters.allMinDepth += CENTIMETER - "3" -> commonParameters.allMaxDepth -= CENTIMETER - "4" -> commonParameters.allMaxDepth += CENTIMETER - } - calibrations - .filter { it.tuneWithKeyboard } - .forEach { - when(event.key) { - KEY_ARROW_LEFT -> it.offset += Direction.LEFT * OFFSET_CHANGE_SCALE - KEY_ARROW_RIGHT -> it.offset += Direction.RIGHT * OFFSET_CHANGE_SCALE - KEY_ARROW_UP -> it.offset += Direction.UP * OFFSET_CHANGE_SCALE - KEY_ARROW_DOWN -> it.offset += Direction.DOWN * OFFSET_CHANGE_SCALE - } - when(event.name) { - "-" -> it.scale -= SCALE_CHANGE - "=" -> it.scale += SCALE_CHANGE - "l" -> it.rotation -= ROTATION_CHANGE - "r" -> it.rotation += ROTATION_CHANGE - "a" -> it.minDepth -= CENTIMETER - "s" -> it.minDepth += CENTIMETER - "d" -> it.maxDepth -= CENTIMETER - "f" -> it.maxDepth += CENTIMETER - } - } - } - - fun addControlsTo(gui: GUI) { - gui.add(commonParameters) - calibrations.forEachIndexed { index, calibration -> - gui.add(calibration, label = "depth camera $index") - } - } - - fun getCalibration(camera: DepthCamera): Calibration = calibrations - .find { it.camera === camera } - ?: throw IllegalArgumentException("No calibration for provided depth camera") - - fun onCalibrationChange(block: (calibration: Calibration) -> Unit) { - onCalibrationChange = block - calibrations.forEach { // run on first install - block(it) - } - } - - private val commonParameters = @Description("calibration: all depth cameras") object { - - @BooleanParameter(label = "calibrator view [k]", order = 0) - var calibratorView: Boolean = false - - @DoubleParameter(label = "min depth [1/2]", low = 0.2, high = 10.0, order = 1) - var allMinDepth: Double = 0.1 - set(value) { - field = value - calibrations.forEach { - it.minDepth = value - } - } - - @DoubleParameter(label = "max depth [3/4]", low = 0.2, high = 10.0, order = 2) - var allMaxDepth: Double = 10.0 - set(value) { - field = value - calibrations.forEach { - it.maxDepth = value - } - } - - } - - @Suppress("unused") // used by reflection - inner class Calibration( - val camera: DepthCamera, - val colorBuffer: ColorBuffer = colorBuffer( - camera.resolution.x, - camera.resolution.y - ) - ) { - - @BooleanParameter(label = "tune with keyboard", order = 0) - var tuneWithKeyboard: Boolean = true - - @BooleanParameter(label = "flipH", order = 1) - var flipH - get() = camera.flipH - set(value) { camera.flipH = value } - - @BooleanParameter(label = "flipV", order = 2) - var flipV - get() = camera.flipV - set(value) { camera.flipV = value } - - @XYParameter( - label = "offset [arrows]", - minX = -1.0, - minY = -1.0, - maxX = 1.0, - maxY = 1.0, - order = 3, - invertY = true - ) - var offset: Vector2 = Vector2.ZERO - set(value) { - field = value - onCalibrationChange(this) - } - - @DoubleParameter(label = "rotation [l/r]", low = -360.0, high = 360.0, order = 4) - var rotation: Double = 0.0 - set(value) { - field = value - onCalibrationChange(this) - } - - @DoubleParameter(label = "scale [+/-]", low = 0.0, high = 10.0, order = 5) - var scale: Double = 1.0 - set(value) { - field = value - onCalibrationChange(this) - } - - @DoubleParameter(label = "min depth [a/s]", low = 0.0, high = 10.0, order = 6) - var minDepth: Double = 0.2 - set(value) { - field = value - onCalibrationChange(this) - } - - @DoubleParameter(label = "max depth [d/f]", low = 0.0, high = 10.0, order = 7) - var maxDepth: Double = 10.0 - set(value) { - field = value - onCalibrationChange(this) - } - - @ActionParameter(label = "reset", order = 8) - fun reset() { - offset = Vector2.ZERO - rotation = 0.0 - scale = 1.0 - minDepth = 0.2 - maxDepth = 10.0 - } - - val width: Double = - camera.resolution.x * resolution.y / - camera.resolution.y - - val height: Double = resolution.y - - val position: Vector2 = - -(resolution - Vector2(resolution.x - width, 0.0)) / 2.0 - - } - -} - -fun Drawer.isolatedWithCalibration( - calibration: DepthCameraCalibrator.Calibration, - block: Drawer.() -> Unit -) { - this.isolated { - translate( - IntVector2(width, height).vector2 / 2.0 - + calibration.offset * Vector2(1.0, -1.0) * height.toDouble() - ) - rotate(calibration.rotation) - scale(calibration.scale) - block() - } -} - -enum class Direction(val vector: Vector2) { - LEFT(Vector2(-1.0, 0.0)), - RIGHT(Vector2(1.0, 0.0)), - UP(Vector2(0.0, 1.0)), - DOWN(Vector2(0.0, -1.0)); - - operator fun times(scale: Double): Vector2 = this.vector * scale - -} - -private const val CENTIMETER = .01 -private const val OFFSET_CHANGE_SCALE = .001 -private const val ROTATION_CHANGE = .1 -private const val SCALE_CHANGE = .001 - diff --git a/orx-jvm/orx-dnk3/README.md b/orx-jvm/orx-dnk3/README.md deleted file mode 100644 index 57296671..00000000 --- a/orx-jvm/orx-dnk3/README.md +++ /dev/null @@ -1,117 +0,0 @@ -# orx-dnk3 - -A scene graph based 3d renderer with support for Gltf based assets - -Status: in development - -Supported Gltf features -- [x] Scene hierarchy -- [x] Loading mesh data -- [x] Glb -- [ ] Materials - - [x] Basic materials - - [x] Normal maps - - [x] Metallic/roughness maps - - [x] Skinning - - [x] Double-sided materials - - [ ] Transparency -- [x] Animations -- [ ] Cameras -- [ ] Lights - -## Demos -### DemoAnimations01 - - - -![DemoAnimations01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-dnk3/images/DemoAnimations01Kt.png) - -[source code](src/demo/kotlin/DemoAnimations01.kt) - -### DemoCamera01 - - - -![DemoCamera01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-dnk3/images/DemoCamera01Kt.png) - -[source code](src/demo/kotlin/DemoCamera01.kt) - -### DemoIrrProbe01 - - - -![DemoIrrProbe01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-dnk3/images/DemoIrrProbe01Kt.png) - -[source code](src/demo/kotlin/DemoIrrProbe01.kt) - -### DemoLights01 - - - -![DemoLights01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-dnk3/images/DemoLights01Kt.png) - -[source code](src/demo/kotlin/DemoLights01.kt) - -### DemoLights02 - - - -![DemoLights02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-dnk3/images/DemoLights02Kt.png) - -[source code](src/demo/kotlin/DemoLights02.kt) - -### DemoLights03 - - - -![DemoLights03Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-dnk3/images/DemoLights03Kt.png) - -[source code](src/demo/kotlin/DemoLights03.kt) - -### DemoObject01 - - - -![DemoObject01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-dnk3/images/DemoObject01Kt.png) - -[source code](src/demo/kotlin/DemoObject01.kt) - -### DemoScene01 - - - -![DemoScene01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-dnk3/images/DemoScene01Kt.png) - -[source code](src/demo/kotlin/DemoScene01.kt) - -### DemoScene02 - - - -![DemoScene02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-dnk3/images/DemoScene02Kt.png) - -[source code](src/demo/kotlin/DemoScene02.kt) - -### DemoScene03 - - - -![DemoScene03Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-dnk3/images/DemoScene03Kt.png) - -[source code](src/demo/kotlin/DemoScene03.kt) - -### DemoSegmentContours01 - - - -![DemoSegmentContours01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-dnk3/images/DemoSegmentContours01Kt.png) - -[source code](src/demo/kotlin/DemoSegmentContours01.kt) - -### DemoSkinning01 - - - -![DemoSkinning01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-dnk3/images/DemoSkinning01Kt.png) - -[source code](src/demo/kotlin/DemoSkinning01.kt) diff --git a/orx-jvm/orx-dnk3/build.gradle.kts b/orx-jvm/orx-dnk3/build.gradle.kts deleted file mode 100644 index ce1d6e49..00000000 --- a/orx-jvm/orx-dnk3/build.gradle.kts +++ /dev/null @@ -1,24 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") - alias(libs.plugins.kotlin.serialization) - -} - -dependencies { - implementation(sharedLibs.kotlin.serialization.core) - implementation(sharedLibs.kotlin.serialization.json) - implementation(project(":orx-fx")) - implementation(project(":orx-jvm:orx-keyframer")) - implementation(project(":orx-easing")) - implementation(project(":orx-shader-phrases")) - implementation(project(":orx-mesh-generators")) - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(sharedLibs.kotlin.coroutines) - demoImplementation(project(":orx-mesh-generators")) - demoImplementation(project(":orx-camera")) - demoImplementation(project(":orx-noise")) - demoImplementation(project(":orx-shader-phrases")) - demoImplementation(openrndr.ffmpeg) - demoImplementation(openrndr.filter) -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoAnimations01.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoAnimations01.kt deleted file mode 100644 index f46a833c..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoAnimations01.kt +++ /dev/null @@ -1,54 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.dnk3.DirectionalLight -import org.openrndr.extra.dnk3.HemisphereLight -import org.openrndr.extra.dnk3.Scene -import org.openrndr.extra.dnk3.SceneNode -import org.openrndr.extra.dnk3.gltf.buildSceneNodes -import org.openrndr.extra.dnk3.gltf.loadGltfFromFile -import org.openrndr.extra.dnk3.renderers.dryRenderer -import org.openrndr.math.Vector3 -import org.openrndr.math.transforms.transform -import java.io.File - -fun main() = application { - configure { - width = 1280 - height = 720 - //multisample = WindowMultisample.SampleCount(8) - } - - program { - val gltf = loadGltfFromFile(File("demo-data/gltf-models/box-animated/BoxAnimated.glb")) - val scene = Scene(SceneNode()) - - // -- add some lights - val lightNode = SceneNode() - lightNode.transform = transform { - translate(0.0, 10.0, 0.0) - rotate(Vector3.UNIT_X, -65.0) - } - lightNode.entities.add(DirectionalLight()) - scene.root.entities.add(HemisphereLight().apply { - upColor = ColorRGBa.BLUE.shade(0.4) - downColor = ColorRGBa.GRAY.shade(0.1) - }) - scene.root.children.add(lightNode) - val sceneData = gltf.buildSceneNodes() - scene.root.children.addAll(sceneData.scenes.first()) - - // -- create a renderer - val renderer = dryRenderer() - extend(Orbital()) { - far = 50.0 - eye = Vector3(1.5, 0.0, 3.0) - fov = 40.0 - } - extend { - sceneData.animations[0].applyToTargets(seconds.mod(sceneData.animations[0].duration)) - drawer.clear(ColorRGBa.PINK) - renderer.draw(drawer, scene) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoCamera01.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoCamera01.kt deleted file mode 100644 index 9fec6c2d..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoCamera01.kt +++ /dev/null @@ -1,40 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.dnk3.* -import org.openrndr.extra.dnk3.gltf.buildSceneNodes -import org.openrndr.extra.dnk3.gltf.loadGltfFromFile -import org.openrndr.extra.dnk3.renderers.dryRenderer -import java.io.File - -fun main() = application { - configure { - width = 1280 - height = 720 - } - - program { - val gltf = loadGltfFromFile(File("demo-data/gltf-models/camera/Scene.glb")) - val scene = Scene(SceneNode()) - - scene.root.entities.add(HemisphereLight().apply { - upColor = ColorRGBa(0.1, 0.1, 0.4) - downColor = ColorRGBa(0.1, 0.0, 0.0) - }) - - val sceneData = gltf.buildSceneNodes() - scene.root.children.addAll(sceneData.scenes.first()) - - // -- create a renderer - val renderer = dryRenderer() - - val cameras = scene.root.findContent { this as? PerspectiveCamera } - - extend { - sceneData.animations[0].applyToTargets(seconds.mod(sceneData.animations[0].duration)) - drawer.view = cameras[0].content.viewMatrix - drawer.projection = cameras[0].content.projectionMatrix - drawer.clear(ColorRGBa.PINK) - renderer.draw(drawer, scene) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoDSL01.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoDSL01.kt deleted file mode 100644 index b399cefe..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoDSL01.kt +++ /dev/null @@ -1,80 +0,0 @@ -//import org.openrndr.application -//import org.openrndr.color.ColorRGBa -//import org.openrndr.extra.dnk3.dsl.* -//import org.openrndr.extra.dnk3.renderers.dryRenderer -//import org.openrndr.extra.dnk3.tools.addSkybox -//import org.openrndr.extra.camera.Orbital -//import org.openrndr.extra.meshgenerators.boxMesh -//import org.openrndr.extra.meshgenerators.groundPlaneMesh -//import org.openrndr.math.Vector3 -//import org.openrndr.math.transforms.transform -// -//fun main() = application { -// configure { -// width = 1280 -// height = 720 -// } -// -// program { -// extend(Orbital()) { -// eye = Vector3(4.0, 4.0, 4.0) -// } -// -// val renderer = dryRenderer() -// val scene = scene { -// -// addSkybox("file:demo-data/cubemaps/garage_iem.dds") -// -// root.hemisphereLight { -// upColor = ColorRGBa.WHITE.shade(0.1) -// downColor = ColorRGBa.BLACK -// } -// -// root.node { -// transform = transform { -// translate(0.0, 2.0, 0.0) -// } -// -// pointLight { -// constantAttenuation = 0.0 -// quadraticAttenuation = 1.0 -// } -// } -// -// root.node { -// simpleMesh { -// vertexBuffer = groundPlaneMesh(100.0, 100.0) -// material = pbrMaterial { -// color = ColorRGBa.GREEN -// } -// } -// } -// -// for (j in -3..3) { -// for (i in -3..3) { -// root.node { -// transform = transform { -// translate(i * 2.0, 1.0, j * 2.0) -// } -// update { -// transform = transform { -// translate(i * 2.0, 1.0, j * 2.0) -// rotate(Vector3.UNIT_Z, seconds* 45.0 + i * 20.0 + j * 50.0) -// } -// } -// simpleMesh { -// vertexBuffer = boxMesh() -// material = pbrMaterial { -// color = ColorRGBa.WHITE -// } -// } -// } -// } -// } -// } -// extend { -// drawer.clear(ColorRGBa.BLACK) -// renderer.draw(drawer, scene) -// } -// } -//} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoDSL02.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoDSL02.kt deleted file mode 100644 index 25a006d7..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoDSL02.kt +++ /dev/null @@ -1,81 +0,0 @@ -//import org.openrndr.application -//import org.openrndr.color.ColorRGBa -//import org.openrndr.extra.dnk3.dsl.* -//import org.openrndr.extra.dnk3.renderers.dryRenderer -//import org.openrndr.extra.dnk3.tools.addSkybox -//import org.openrndr.extra.noise.simplex -//import org.openrndr.extra.camera.Orbital -//import org.openrndr.extra.meshgenerators.groundPlaneMesh -//import org.openrndr.math.Vector3 -//import org.openrndr.math.transforms.transform -//import org.openrndr.shape.path3D -// -//fun main() = application { -// configure { -// width = 1280 -// height = 720 -// } -// -// program { -// extend(Orbital()) { -// eye = Vector3(4.0, 4.0, 4.0) -// } -// -// val renderer = dryRenderer() -// val scene = scene { -// -// addSkybox("file:demo-data/cubemaps/garage_iem.dds") -// -// root.hemisphereLight { -// upColor = ColorRGBa.WHITE.shade(0.1) -// downColor = ColorRGBa.BLACK -// } -// -// root.node { -// transform = transform { -// translate(0.0, 2.0, 0.0) -// } -// -// pointLight { -// constantAttenuation = 0.0 -// quadraticAttenuation = 1.0 -// } -// } -// -// root.node { -// simpleMesh { -// vertexBuffer = groundPlaneMesh(100.0, 100.0) -// material = pbrMaterial { -// color = ColorRGBa.GREEN -// } -// } -// } -// -// root.node { -// pathMesh { -// weight = 10.0 -// material = pbrMaterial { -// color = ColorRGBa.PINK -// } -// update { -// paths = mutableListOf( -// path3D { -// val t = seconds * 0.1 -// moveTo(Vector3.ZERO) -// val control = Vector3.simplex(3032, t).let { it.copy(y = it.y * 0.5 + 0.5) } * 4.0 -// val target = Vector3.simplex(5077, t).let { it.copy(y = it.y * 0.5 + 0.5) } * 4.0 -// val end = Vector3.simplex(9041, t).let { it.copy(y = it.y * 0.5 + 0.5) } * 4.0 -// curveTo(control, target) -// continueTo(end) -// } -// ) -// } -// } -// } -// } -// extend { -// drawer.clear(ColorRGBa.BLACK) -// renderer.draw(drawer, scene) -// } -// } -//} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoIrrProbe01.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoIrrProbe01.kt deleted file mode 100644 index 05156987..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoIrrProbe01.kt +++ /dev/null @@ -1,100 +0,0 @@ -import kotlinx.coroutines.yield -import org.openrndr.WindowMultisample -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.ColorFormat -import org.openrndr.draw.ColorType -import org.openrndr.draw.DrawPrimitive -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.dnk3.* -import org.openrndr.extra.dnk3.features.addIrradianceSH -import org.openrndr.extra.dnk3.gltf.buildSceneNodes -import org.openrndr.extra.dnk3.gltf.loadGltfFromFile -import org.openrndr.extra.dnk3.renderers.postRenderer -import org.openrndr.extra.meshgenerators.sphereMesh -import org.openrndr.filter.color.Delinearize -import org.openrndr.launch -import org.openrndr.math.Spherical -import org.openrndr.math.Vector3 -import org.openrndr.math.transforms.transform -import java.io.File -import kotlin.math.cos -import kotlin.math.sin - -fun main() = application { - configure { - width = 1280 - height = 720 - multisample = WindowMultisample.SampleCount(8) - } - - program { - val gltf = loadGltfFromFile(File("demo-data/gltf-models/irradiance-probes/model.glb")) - val scene = Scene(SceneNode()) - - val probeBox = sphereMesh(16, 16, 0.1) - val probeGeometry = Geometry(listOf(probeBox), null, DrawPrimitive.TRIANGLES, 0, probeBox.vertexCount) - - val c = 5 - scene.addIrradianceSH(c, c, c, 3.0 / c, cubemapSize = 32, offset = Vector3(0.0, 0.0, 0.0)) - - - val sceneData = gltf.buildSceneNodes() - scene.root.children.addAll(sceneData.scenes.first()) - - // -- create a renderer - val renderer = postRenderer() - - -// renderer.postSteps.add( -// FilterPostStep(1.0, ScreenspaceReflections(), listOf("color", "clipDepth", "viewNormal"), "reflections", ColorFormat.RGB, ColorType.FLOAT16) { -// val p = Matrix44.scale(drawer.width / 2.0, drawer.height / 2.0, 1.0) * Matrix44.translate(Vector3(1.0, 1.0, 0.0)) * drawer.projection -// this.projection = p -// this.projectionMatrixInverse = drawer.projection.inversed -// } -// ) - -// renderer.postSteps.add( -// FilterPostStep(1.0, VolumetricIrradiance(), listOf("color", "clipDepth"), "volumetric-irradiance", ColorFormat.RGB, ColorType.FLOAT16) { -// this.irradianceSH = scene.features[0] as IrradianceSH -// this.projectionMatrixInverse = drawer.projection.inversed -// this.viewMatrixInverse = drawer.view.inversed -// } -// ) - - renderer.postSteps.add( - FilterPostStep(1.0, Delinearize(), listOf("color"), "ldr", ColorFormat.RGB, ColorType.FLOAT16) - ) - - val orb = extend(Orbital()) { - this.fov = 20.0 - camera.setView(Vector3(-0.49, -0.24, 0.20), Spherical(26.56, 90.0, 6.533), 40.0) - } - - renderer.draw(drawer, scene) - - val dynNode = SceneNode() - val dynMaterial = PBRMaterial() - val dynPrimitive = MeshPrimitive(probeGeometry, dynMaterial) - val dynMesh = Mesh(listOf(dynPrimitive)) - dynNode.entities.add(dynMesh) - scene.root.children.add(dynNode) - - scene.dispatcher.launch { - while (true) { - dynNode.transform = transform { - translate(cos(seconds) * 0.5, 0.5, sin(seconds) * 0.5) - scale(2.0) - } - yield() - } - } - - extend { - drawer.clear(ColorRGBa.BLACK) - renderer.draw(drawer, scene) - drawer.defaults() - - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoLights01.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoLights01.kt deleted file mode 100644 index 431c18b1..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoLights01.kt +++ /dev/null @@ -1,45 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.dnk3.HemisphereLight -import org.openrndr.extra.dnk3.Scene -import org.openrndr.extra.dnk3.SceneNode -import org.openrndr.extra.dnk3.gltf.buildSceneNodes -import org.openrndr.extra.dnk3.gltf.loadGltfFromFile -import org.openrndr.extra.dnk3.renderers.dryRenderer -import org.openrndr.math.Spherical -import org.openrndr.math.Vector3 -import java.io.File - -fun main() = application { - configure { - width = 1280 - height = 720 - //multisample = WindowMultisample.SampleCount(8) - } - - program { - val gltf = loadGltfFromFile(File("demo-data/gltf-models/point-light/Scene.glb")) - val scene = Scene(SceneNode()) - - scene.root.entities.add(HemisphereLight().apply { - upColor = ColorRGBa(0.1, 0.1, 0.4) - downColor = ColorRGBa(0.1, 0.0, 0.0) - }) - - val sceneData = gltf.buildSceneNodes() - scene.root.children.addAll(sceneData.scenes.first()) - - // -- create a renderer - val renderer = dryRenderer() - val orb = extend(Orbital()) { - far = 50.0 - camera.setView(Vector3.ZERO, Spherical(30.50, 26.0, 5.6), 40.0) - } - extend { - sceneData.animations[0].applyToTargets(seconds.mod(sceneData.animations[0].duration)) - drawer.clear(ColorRGBa.PINK) - renderer.draw(drawer, scene) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoLights02.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoLights02.kt deleted file mode 100644 index d1a57447..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoLights02.kt +++ /dev/null @@ -1,46 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.dnk3.HemisphereLight -import org.openrndr.extra.dnk3.Scene -import org.openrndr.extra.dnk3.SceneNode -import org.openrndr.extra.dnk3.gltf.buildSceneNodes -import org.openrndr.extra.dnk3.gltf.loadGltfFromFile -import org.openrndr.extra.dnk3.renderers.dryRenderer -import org.openrndr.math.Spherical -import org.openrndr.math.Vector3 -import java.io.File - -fun main() = application { - configure { - width = 1280 - height = 720 - //multisample = WindowMultisample.SampleCount(8) - } - - program { - val gltf = loadGltfFromFile(File("demo-data/gltf-models/spot-light/Scene.glb")) - val scene = Scene(SceneNode()) - - scene.root.entities.add(HemisphereLight().apply { - upColor = ColorRGBa(0.1, 0.1, 0.4) - downColor = ColorRGBa(0.1, 0.0, 0.0) - }) - - - val sceneData = gltf.buildSceneNodes() - scene.root.children.addAll(sceneData.scenes.first()) - - // -- create a renderer - val renderer = dryRenderer() - val orb = extend(Orbital()) { - far = 50.0 - camera.setView(Vector3(-0.514, -0.936, -1.122), Spherical(454.346, 25.0, 8.444), 40.0) - } - extend { - sceneData.animations[0].applyToTargets(seconds.mod(sceneData.animations[0].duration)) - drawer.clear(ColorRGBa.PINK) - renderer.draw(drawer, scene) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoLights03.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoLights03.kt deleted file mode 100644 index 11d65de5..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoLights03.kt +++ /dev/null @@ -1,45 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.dnk3.HemisphereLight -import org.openrndr.extra.dnk3.Scene -import org.openrndr.extra.dnk3.SceneNode -import org.openrndr.extra.dnk3.gltf.buildSceneNodes -import org.openrndr.extra.dnk3.gltf.loadGltfFromFile -import org.openrndr.extra.dnk3.renderers.dryRenderer -import org.openrndr.math.Spherical -import org.openrndr.math.Vector3 -import java.io.File - -fun main() = application { - configure { - width = 1280 - height = 720 - //multisample = WindowMultisample.SampleCount(8) - } - - program { - val gltf = loadGltfFromFile(File("demo-data/gltf-models/directional-light/Scene.glb")) - val scene = Scene(SceneNode()) - - scene.root.entities.add(HemisphereLight().apply { - upColor = ColorRGBa(0.1, 0.1, 0.4) - downColor = ColorRGBa(0.1, 0.0, 0.0) - }) - - val sceneData = gltf.buildSceneNodes() - scene.root.children.addAll(sceneData.scenes.first()) - - // -- create a renderer - val renderer = dryRenderer() - val orb = extend(Orbital()) { - camera.setView(Vector3(-0.49, -0.24, 0.20), Spherical(26.56, 90.0, 6.533), 40.0) - } - - extend { - sceneData.animations[0].applyToTargets(seconds.mod(sceneData.animations[0].duration)) - drawer.clear(ColorRGBa.PINK) - renderer.draw(drawer, scene) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoObject01.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoObject01.kt deleted file mode 100644 index 39bce5d0..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoObject01.kt +++ /dev/null @@ -1,40 +0,0 @@ -import org.openrndr.application -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.dnk3.gltf.loadGltfFromFile -import org.openrndr.math.Vector3 -import java.io.File - -fun main() = application { - program { - val gltf = loadGltfFromFile(File("demo-data/gltf-models/duck/Duck.gltf")) - val meshes = gltf.meshes.map { - it.createDrawCommands(gltf) - } - - extend(Orbital()) { - far = 400.0 - lookAt = Vector3(0.0, 50.0, 0.0) - eye = Vector3(100.0, 200.0, 150.0) - fov = 45.0 - } - - extend { - drawer.shadeStyle = shadeStyle { - fragmentTransform = """ - x_fill.rgb = vec3(v_viewNormal.z); - """.trimIndent() - } - for (mesh in meshes) { - for (primitive in mesh) { - if (primitive.indexBuffer == null) { - drawer.vertexBuffer(primitive.vertexBuffer, DrawPrimitive.TRIANGLES) - } else { - drawer.vertexBuffer(primitive.indexBuffer!!, listOf(primitive.vertexBuffer), DrawPrimitive.TRIANGLES) - } - } - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoScene01.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoScene01.kt deleted file mode 100644 index 3fec9087..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoScene01.kt +++ /dev/null @@ -1,52 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.dnk3.DirectionalLight -import org.openrndr.extra.dnk3.HemisphereLight -import org.openrndr.extra.dnk3.Scene -import org.openrndr.extra.dnk3.SceneNode -import org.openrndr.extra.dnk3.gltf.buildSceneNodes -import org.openrndr.extra.dnk3.gltf.loadGltfFromFile -import org.openrndr.extra.dnk3.renderers.dryRenderer -import org.openrndr.math.Vector3 -import org.openrndr.math.transforms.transform -import java.io.File - -fun main() = application { - configure { - width = 1280 - height = 720 - //multisample = WindowMultisample.SampleCount(8) - } - - program { - val gltf = loadGltfFromFile(File("demo-data/gltf-models/suzanne/Suzanne.gltf")) - val scene = Scene(SceneNode()) - - // -- add some lights - val lightNode = SceneNode() - lightNode.transform = transform { - translate(0.0, 10.0, 0.0) - rotate(Vector3.UNIT_X, -65.0) - } - lightNode.entities.add(DirectionalLight()) - scene.root.entities.add(HemisphereLight().apply { - upColor = ColorRGBa.BLUE.shade(0.4) - downColor = ColorRGBa.GRAY.shade(0.1) - }) - scene.root.children.add(lightNode) - scene.root.children.addAll(gltf.buildSceneNodes().scenes.first()) - - // -- create a renderer - val renderer = dryRenderer() - extend(Orbital()) { - far = 50.0 - eye = Vector3(1.5, 0.0, 3.0) - fov = 40.0 - } - extend { - drawer.clear(ColorRGBa.PINK) - renderer.draw(drawer, scene) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoScene02.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoScene02.kt deleted file mode 100644 index d5c81207..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoScene02.kt +++ /dev/null @@ -1,51 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.dnk3.* - -import org.openrndr.extra.dnk3.gltf.buildSceneNodes -import org.openrndr.extra.dnk3.gltf.loadGltfFromFile -import org.openrndr.extra.dnk3.renderers.dryRenderer -import org.openrndr.extra.camera.Orbital -import org.openrndr.math.Vector3 -import org.openrndr.math.transforms.transform -import java.io.File - -fun main() = application { - configure { - width = 1280 - height = 720 - } - - program { - val gltf = loadGltfFromFile(File("demo-data/gltf-models/duck/Duck.gltf")) - val scene = Scene(SceneNode()) - - // -- add some lights - val lightNode = SceneNode() - lightNode.transform = transform { - translate(0.0, 10.0, 0.0) - rotate(Vector3.UNIT_X, -90.0) - } - lightNode.entities.add(DirectionalLight()) - - scene.root.entities.add(HemisphereLight().apply { - upColor = ColorRGBa.WHITE.shade(1.0) - downColor = ColorRGBa.WHITE.shade(0.1) - }) - scene.root.children.add(lightNode) - scene.root.children.addAll(gltf.buildSceneNodes().scenes.first()) - - // -- create a renderer - val renderer = dryRenderer() - extend(Orbital()) { - far = 500.0 - lookAt = Vector3(0.0, 0.8, 0.0) - eye = Vector3(3.0, 0.8, -2.0) - fov = 30.0 - } - extend { - drawer.clear(ColorRGBa.PINK) - renderer.draw(drawer, scene) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoScene03.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoScene03.kt deleted file mode 100644 index f0d6b97d..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoScene03.kt +++ /dev/null @@ -1,53 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.dnk3.* -import org.openrndr.extra.dnk3.renderers.dryRenderer -import org.openrndr.extra.meshgenerators.sphereMesh -import org.openrndr.math.Vector3 -import org.openrndr.math.transforms.transform - -fun main() = application { - configure { - width = 1280 - height = 720 - //multisample = WindowMultisample.SampleCount(8) - } - - program { - - val root = SceneNode() - val scene = Scene(root) - - val lightNode = SceneNode() - lightNode.transform = transform { - translate(0.0, 10.0, 0.0) - } - lightNode.entities.add(PointLight()) - lightNode.entities.add(HemisphereLight(upColor = ColorRGBa.PINK, downColor = ColorRGBa(0.1,0.1,0.1))) - scene.root.children.add(lightNode) - - val meshNode = SceneNode() - val box = sphereMesh(32, 32) - val geometry = Geometry(listOf(box), null, DrawPrimitive.TRIANGLES, 0, box.vertexCount) - val material = PBRMaterial() - val primitive = MeshPrimitive(geometry, material) - val mesh = Mesh(listOf(primitive)) - meshNode.entities.add(mesh) - root.children.add(meshNode) - - // -- create a renderer - val renderer = dryRenderer() - extend(Orbital()) { - far = 500.0 - lookAt = Vector3(0.0, 0.0, 0.0) - eye = Vector3(3.0, 2.0, -3.0) - fov = 30.0 - } - extend { - drawer.clear(ColorRGBa.PINK) - renderer.draw(drawer, scene) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoSegmentContours01.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoSegmentContours01.kt deleted file mode 100644 index f1af38f8..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoSegmentContours01.kt +++ /dev/null @@ -1,44 +0,0 @@ -import org.openrndr.WindowMultisample -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.BufferMultisample -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.dnk3.Scene -import org.openrndr.extra.dnk3.SceneNode -import org.openrndr.extra.dnk3.gltf.buildSceneNodes -import org.openrndr.extra.dnk3.gltf.loadGltfFromFile -import org.openrndr.extra.dnk3.renderers.segmentContourRenderer -import org.openrndr.math.Vector3 -import java.io.File - -fun main() = application { - configure { - width = 1280 - height = 720 - multisample = WindowMultisample.SampleCount(8) - } - - program { - - val gltf = loadGltfFromFile(File("demo-data/gltf-models/fox/Fox.glb")) - val scene = Scene(SceneNode()) - - val sceneData = gltf.buildSceneNodes() - scene.root.children.addAll(sceneData.scenes.first()) - - // -- create a renderer, try it with BufferMultisample.SampleCount(8) for better results - val renderer = segmentContourRenderer(BufferMultisample.Disabled) - extend(Orbital()) { - far = 500.0 - lookAt = Vector3(0.0, 40.0, 0.0) - eye = Vector3(150.0, 40.0, 200.0) - fov = 40.0 - } - - extend { - sceneData.animations[2].applyToTargets(seconds.mod(sceneData.animations[2].duration)) - drawer.clear(ColorRGBa.PINK) - renderer.draw(drawer, scene) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoSkinning01.kt b/orx-jvm/orx-dnk3/src/demo/kotlin/DemoSkinning01.kt deleted file mode 100644 index 9c5df1e9..00000000 --- a/orx-jvm/orx-dnk3/src/demo/kotlin/DemoSkinning01.kt +++ /dev/null @@ -1,47 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.dnk3.HemisphereLight -import org.openrndr.extra.dnk3.Scene -import org.openrndr.extra.dnk3.SceneNode -import org.openrndr.extra.dnk3.gltf.buildSceneNodes -import org.openrndr.extra.dnk3.gltf.loadGltfFromFile -import org.openrndr.extra.dnk3.renderers.dryRenderer -import org.openrndr.math.Vector3 -import java.io.File - -fun main() = application { - configure { - width = 1280 - height = 720 - //multisample = WindowMultisample.SampleCount(8) - } - - program { - val gltf = loadGltfFromFile(File("demo-data/gltf-models/fox/Fox.glb")) - val scene = Scene(SceneNode()) - - scene.root.entities.add(HemisphereLight().apply { - upColor = ColorRGBa.WHITE.shade(0.4) - downColor = ColorRGBa.GRAY.shade(0.1) - }) - val sceneData = gltf.buildSceneNodes() - scene.root.children.addAll(sceneData.scenes.first()) - - - // -- create a renderer - val renderer = dryRenderer() - extend(Orbital()) { - far = 500.0 - lookAt = Vector3(0.0, 40.0, 0.0) - eye = Vector3(150.0, 40.0, 200.0) - fov = 40.0 - } - - extend { - sceneData.animations[2].applyToTargets(seconds.mod(sceneData.animations[2].duration)) - drawer.clear(ColorRGBa.PINK) - renderer.draw(drawer, scene) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/Camera.kt b/orx-jvm/orx-dnk3/src/main/kotlin/Camera.kt deleted file mode 100644 index 4b014da9..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/Camera.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.openrndr.extra.dnk3 - -import org.openrndr.math.Matrix44 -import org.openrndr.math.transforms.ortho -import org.openrndr.math.transforms.perspective - -class PerspectiveCamera(var node: SceneNode) : Camera() { - override val projectionMatrix: Matrix44 - get() = perspective(fov, aspectRatio, near, far) - - override val viewMatrix: Matrix44 - get() = node.worldTransform.inversed - - var aspectRatio: Double = 16.0 / 9.0 - var fov = 45.0 - var far = 100.0 - var near = 0.1 - - override fun hashCode(): Int { - var result = aspectRatio.hashCode() - result = 31 * result + fov.hashCode() - result = 31 * result + far.hashCode() - result = 31 * result + near.hashCode() - return result - } -} - -class OrthographicCamera(var node: SceneNode) : Camera() { - override val projectionMatrix: Matrix44 - get() = ortho(xMag, yMag, near, far) - - override val viewMatrix: Matrix44 - get() = node.worldTransform.inversed - - var xMag = 1.0 - var yMag = 1.0 - var near = 0.1 - var far = 100.0 - - override fun hashCode(): Int { - var result = xMag.hashCode() - result = 31 * result + yMag.hashCode() - result = 31 * result + near.hashCode() - result = 31 * result + far.hashCode() - return result - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/Entity.kt b/orx-jvm/orx-dnk3/src/main/kotlin/Entity.kt deleted file mode 100644 index e9ff407d..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/Entity.kt +++ /dev/null @@ -1,103 +0,0 @@ -package org.openrndr.extra.dnk3 - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.IndexBuffer -import org.openrndr.draw.VertexBuffer -import org.openrndr.math.Matrix44 -import org.openrndr.math.transforms.perspective -import org.openrndr.shape.Path3D - - -class Geometry(val vertexBuffers: List, - val indexBuffer: IndexBuffer?, - val primitive: DrawPrimitive, - val offset: Int, - val vertexCount: Int) { - - override fun toString(): String { - return "Geometry(vertexBuffers: $vertexBuffers, indexBuffers: $indexBuffer, primitive: $primitive, offset: $offset, vertexCount: $vertexCount)" - } - - override fun hashCode(): Int { - var result = 0 - result = 31 * result + primitive.ordinal.hashCode() - result = 31 * result + offset.hashCode() - result = 31 * result + vertexCount.hashCode() - return result - } -} - -val DummyGeometry = Geometry(emptyList(), null, DrawPrimitive.TRIANGLES, 0, 0) - -sealed class Entity -class MeshPrimitive(var geometry: Geometry, var material: Material) { - override fun toString(): String { - return "MeshPrimitive(geometry: $geometry, material: $material)" - } - - override fun hashCode(): Int { - var result = geometry.hashCode() - result = 31 * result + material.hashCode() - return result - } -} - -class MeshPrimitiveInstance(val primitive: MeshPrimitive, val instances: Int, val attributes: List) - -class PathMesh(var paths: MutableList, var material: Material, var weight: Double) : Entity() { - override fun toString(): String { - return "PathMesh(paths=$paths)" - } - - override fun hashCode(): Int { - return paths.hashCode() - } -} - -abstract class MeshBase(var primitives: List) : Entity() -class Mesh(primitives: List) : MeshBase(primitives) { - override fun toString(): String { - return "Mesh(primitives: $primitives)" - } - - override fun hashCode(): Int { - return primitives.hashCode() - } -} - -class SkinnedMesh(primitives: List, - val joints: List, - val skeleton: SceneNode, - val inverseBindMatrices: List -) : MeshBase(primitives) - -class InstancedMesh(primitives: List, - var instances: Int, - var attributes: List) : MeshBase(primitives) - - -data class Fog(var color: ColorRGBa = ColorRGBa.WHITE, var end: Double = 100.0) : Entity() - -abstract class Light : Entity() { - var color: ColorRGBa = ColorRGBa.WHITE -} - -abstract class Camera : Entity() { - abstract val projectionMatrix: Matrix44 - abstract val viewMatrix: Matrix44 -} - -abstract class CubemapProbe : Entity() { - open val projectionMatrix: Matrix44 - get() { - return perspective(90.0, 1.0, 0.1, 150.0) - } - var dirty = true -} - -class IrradianceProbe : CubemapProbe() { - override fun hashCode(): Int { - return true.hashCode() - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/Facet.kt b/orx-jvm/orx-dnk3/src/main/kotlin/Facet.kt deleted file mode 100644 index 0cc99a62..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/Facet.kt +++ /dev/null @@ -1,158 +0,0 @@ -package org.openrndr.extra.dnk3 - -import org.openrndr.draw.BlendMode -import org.openrndr.draw.ColorFormat -import org.openrndr.draw.ColorType - -enum class FacetType(val shaderFacet: String) { - WORLD_POSITION("f_worldPosition"), - VIEW_POSITION("f_viewPosition"), - CLIP_POSITION("f_clipPosition"), - WORLD_NORMAL("f_worldNormal"), - VIEW_NORMAL("f_viewNormal"), - SPECULAR("f_specular"), - DIFFUSE("f_diffuse"), - EMISSIVE("f_emission"), - AMBIENT("f_ambient"), - OCCLUSION("f_occlusion"), - FRAGMENT_ID("f_fragmentID"), - COLOR("m_color"), -} - -abstract class FacetCombiner(val facets: Set, val targetOutput: String) { - abstract fun generateShader(): String - override fun toString(): String { - return "FacetCombiner(facets=$facets, targetOutput='$targetOutput')" - } -} - -abstract class ColorBufferFacetCombiner(facets: Set, - targetOutput: String, - val format: ColorFormat, - val type: ColorType, - val blendMode: BlendMode = BlendMode.BLEND) : FacetCombiner(facets, targetOutput) { - -} - -class MomentsFacet : ColorBufferFacetCombiner(setOf(FacetType.WORLD_POSITION), "moments", ColorFormat.RG, ColorType.FLOAT16) { - override fun generateShader(): String { - return """ - float depth = length(v_viewPosition); - float dx = dFdx(depth); - float dy = dFdy(depth); - o_$targetOutput = vec4(depth, depth*depth + 0.25 * dx*dx+dy*dy, 0.0, 1.0); - """ - } -} - -class DiffuseSpecularFacet : ColorBufferFacetCombiner(setOf(FacetType.DIFFUSE, FacetType.SPECULAR), - "diffuseSpecular", ColorFormat.RGB, ColorType.FLOAT16) { - override fun generateShader(): String = - "o_$targetOutput = vec4( max(vec3(0.0), f_diffuse.rgb) + max(vec3(0.0), f_specular.rgb), 1.0);" -} -class DiffuseSpecularAlphaFacet : ColorBufferFacetCombiner(setOf(FacetType.DIFFUSE, FacetType.SPECULAR), - "diffuseSpecular", ColorFormat.RGB, ColorType.FLOAT16) { - override fun generateShader(): String = - "o_$targetOutput = vec4( (max(vec3(0.0), f_diffuse.rgb) + max(vec3(0.0), f_specular.rgb)) * f_alpha, f_alpha);" -} - -class AmbientOcclusionFacet : ColorBufferFacetCombiner(setOf(FacetType.AMBIENT, FacetType.OCCLUSION), - "ambientOcclusion", ColorFormat.RGBa, ColorType.FLOAT16) { - override fun generateShader(): String = - "o_$targetOutput = vec4(f_ambient, f_occlusion);" -} - -class MaterialFacet : ColorBufferFacetCombiner(setOf(FacetType.DIFFUSE), - "material", ColorFormat.RGBa, ColorType.UINT8) { - override fun generateShader(): String = - "o_$targetOutput = vec4(m_metalness, m_roughness, 0.0, 1.0);" -} - -class BaseColorFacet : ColorBufferFacetCombiner(setOf(FacetType.COLOR), - "baseColor", ColorFormat.RGB, ColorType.UINT8) { - override fun generateShader(): String = "o_$targetOutput = vec4(m_color.rgb, 1.0);" -} - -class DiffuseFacet : ColorBufferFacetCombiner(setOf(FacetType.DIFFUSE), - "diffuse", ColorFormat.RGB, ColorType.FLOAT16) { - override fun generateShader(): String = - "o_$targetOutput = vec4( max(vec3(0.0), f_diffuse.rgb), 1.0 );" -} - -class SpecularFacet : ColorBufferFacetCombiner(setOf(FacetType.SPECULAR), - "diffuseSpecular", ColorFormat.RGB, ColorType.FLOAT16) { - override fun generateShader(): String = - "o_$targetOutput = vec4( max(vec3(0.0), f_specular.rgb), 1.0);" -} - -class EmissiveFacet: ColorBufferFacetCombiner(setOf(FacetType.EMISSIVE), - "emissive", ColorFormat.RGB, ColorType.FLOAT16) { - override fun generateShader(): String = - "o_$targetOutput = vec4(f_emission, 1.0);" -} - -class EmissiveAlphaFacet: ColorBufferFacetCombiner(setOf(FacetType.EMISSIVE), - "emissive", ColorFormat.RGB, ColorType.FLOAT16, BlendMode.OVER) { - override fun generateShader(): String = - "o_$targetOutput = vec4(f_emission, f_alpha);" -} - -class PositionFacet : ColorBufferFacetCombiner(setOf(FacetType.WORLD_POSITION), "position", ColorFormat.RGB, ColorType.FLOAT16) { - override fun generateShader(): String = "o_$targetOutput = vec4(v_worldPosition.rgb, 1.0);" -} - -class NormalFacet : ColorBufferFacetCombiner(setOf(FacetType.WORLD_NORMAL), "normal", ColorFormat.RGB, ColorType.FLOAT16) { - override fun generateShader(): String = "o_$targetOutput = vec4(v_worldNormal.rgb, 1.0);" -} - -class ViewDepthFacet : ColorBufferFacetCombiner(setOf(FacetType.VIEW_POSITION), "viewDepth", ColorFormat.R, ColorType.FLOAT16) { - override fun generateShader(): String = "o_$targetOutput.r = v_viewPosition.z;" -} -class ClipDepthFacet : ColorBufferFacetCombiner(setOf(FacetType.CLIP_POSITION), "clipDepth", ColorFormat.R, ColorType.FLOAT32) { - override fun generateShader(): String = "o_$targetOutput = gl_FragCoord.z;" -} - - -class ViewPositionFacet : ColorBufferFacetCombiner(setOf(FacetType.VIEW_POSITION), "viewPosition", ColorFormat.RGB, ColorType.FLOAT32) { - override fun generateShader(): String = "o_$targetOutput.rgb = v_viewPosition.rgb;" -} - -class ViewNormalFacet : ColorBufferFacetCombiner(setOf(FacetType.VIEW_NORMAL), "viewNormal", ColorFormat.RGB, ColorType.FLOAT16) { - override fun generateShader(): String = "o_$targetOutput.rgb = normalize( (u_viewNormalMatrix * vec4(f_worldNormal,0.0)).xyz );" -} - -class ClipPositionFacet : ColorBufferFacetCombiner(setOf(FacetType.CLIP_POSITION), "position", ColorFormat.RGB, ColorType.FLOAT16) { - override fun generateShader() = "o_$targetOutput.rgb = gl_FragCoord.xyz;" -} - -class FragmentIDFacet: ColorBufferFacetCombiner(setOf(FacetType.FRAGMENT_ID), "fragmentID", ColorFormat.R, ColorType.UINT16_INT) { - override fun generateShader(): String { - return "o_$targetOutput = f_fragmentID;" - } -} - -class LDRColorFacet : ColorBufferFacetCombiner(setOf(FacetType.DIFFUSE, FacetType.SPECULAR, FacetType.EMISSIVE), "color", ColorFormat.RGBa, ColorType.UINT8) { - override fun generateShader() = """ - vec3 finalColor = (max(vec3(0.0), f_diffuse.rgb) + max(vec3(0.0),f_specular.rgb) + max(vec3(0.0), f_emission.rgb) + max(vec3(0.0), f_ambient.rgb)) * (1.0 - f_fog.a) + f_fog.rgb * f_fog.a; - o_$targetOutput = pow(vec4(finalColor.rgb, 1.0), vec4(1.0/2.2)); - o_$targetOutput *= m_color.a; - - """ -} - -class HDRColorFacet : ColorBufferFacetCombiner(setOf(FacetType.DIFFUSE, FacetType.SPECULAR, FacetType.EMISSIVE), "color", ColorFormat.RGBa, ColorType.FLOAT16) { - override fun generateShader() = """ - vec3 finalColor = (max(vec3(0.0), f_diffuse.rgb) + max(vec3(0.0),f_specular.rgb) + max(vec3(0.0), f_emission.rgb) + max(vec3(0.0), f_ambient.rgb)) * (1.0 - f_fog.a) + f_fog.rgb * f_fog.a; - o_$targetOutput = vec4(finalColor.rgb, 1.0); - o_$targetOutput *= m_color.a; - """ -} - -class DiffuseIrradianceFacet : ColorBufferFacetCombiner(setOf(FacetType.DIFFUSE, FacetType.SPECULAR), "color", ColorFormat.RGBa, ColorType.UINT8) { - override fun generateShader() = """ - vec3 finalColor = (max(vec3(0.0), f_diffuse.rgb) + max(vec3(0.0), f_emission.rgb)); - o_$targetOutput = vec4(finalColor.rgb, 1.0); - - - """ -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/Feature.kt b/orx-jvm/orx-dnk3/src/main/kotlin/Feature.kt deleted file mode 100644 index 27ecd22e..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/Feature.kt +++ /dev/null @@ -1,13 +0,0 @@ -package org.openrndr.extra.dnk3 - -import org.openrndr.draw.Drawer - -interface Feature { - fun update( - drawer: Drawer, - sceneRenderer: SceneRenderer, - scene: Scene, - feature: T, - context: RenderContext - ) -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/Light.kt b/orx-jvm/orx-dnk3/src/main/kotlin/Light.kt deleted file mode 100644 index f57f57b7..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/Light.kt +++ /dev/null @@ -1,83 +0,0 @@ -package org.openrndr.extra.dnk3 - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Cubemap -import org.openrndr.draw.RenderTarget -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector3 -import org.openrndr.math.transforms.ortho -import org.openrndr.math.transforms.perspective - -@JvmRecord -data class LightContext(val lights: List>, - val shadowMaps: Map) - -interface AttenuatedLight { - var constantAttenuation: Double - var linearAttenuation: Double - var quadraticAttenuation: Double -} - -class DirectionalLight(var direction: Vector3 = -Vector3.UNIT_Z, override var shadows: Shadows = Shadows.None) : Light(), ShadowLight { - var projectionSize = 50.0 - - override fun projection(renderTarget: RenderTarget): Matrix44 { - return ortho(-projectionSize / 2.0, projectionSize / 2.0, -projectionSize / 2.0, projectionSize / 2.0, 1.0, 150.0) - } - - override fun hashCode(): Int { - return color.hashCode() - } -} - -class SpotLight(var direction: Vector3 = -Vector3.UNIT_Z, var innerAngle: Double = 45.0, var outerAngle: Double = 90.0) : Light(), ShadowLight, AttenuatedLight { - override var constantAttenuation = 1.0 - override var linearAttenuation = 0.0 - override var quadraticAttenuation = 0.0 - override var shadows: Shadows = Shadows.None - override fun projection(renderTarget: RenderTarget): Matrix44 { - return perspective(outerAngle * 2.0, renderTarget.width * 1.0 / renderTarget.height, 1.0, 150.0) - } - - override fun hashCode(): Int { - var result = direction.hashCode() - result = 31 * result + innerAngle.hashCode() - result = 31 * result + outerAngle.hashCode() - result = 31 * result + constantAttenuation.hashCode() - result = 31 * result + linearAttenuation.hashCode() - result = 31 * result + quadraticAttenuation.hashCode() - return result - } -} - -class HemisphereLight(var direction: Vector3 = Vector3.UNIT_Y, - var upColor: ColorRGBa = ColorRGBa.WHITE, - var downColor: ColorRGBa = ColorRGBa.BLACK) : Light() { - var irradianceMap: Cubemap? = null - override fun hashCode(): Int { - var result = direction.hashCode() - result = 31 * result + upColor.hashCode() - result = 31 * result + downColor.hashCode() - return result - } - -} - -class PointLight(var constantAttenuation: Double = 1.0, - var linearAttenuation: Double = 0.0, - var quadraticAttenuation: Double = 1.0) : Light() { - override fun hashCode(): Int { - var result = constantAttenuation.hashCode() - result = 31 * result + linearAttenuation.hashCode() - result = 31 * result + quadraticAttenuation.hashCode() - result = 31 * result + color.hashCode() - return result - } -} - -class AmbientLight : Light() { - - override fun hashCode(): Int { - return color.hashCode() - } -} diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/Material.kt b/orx-jvm/orx-dnk3/src/main/kotlin/Material.kt deleted file mode 100644 index fa9e85ff..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/Material.kt +++ /dev/null @@ -1,61 +0,0 @@ -package org.openrndr.extra.dnk3 - -import org.openrndr.draw.Cubemap -import org.openrndr.draw.RenderTarget -import org.openrndr.draw.ShadeStyle -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.dnk3.features.IrradianceSH - -interface Material { - val name: String? - var doubleSided: Boolean - var transparent: Boolean - val fragmentID: Int - fun generateShadeStyle(context: MaterialContext, primitiveContext: PrimitiveContext): ShadeStyle - fun applyToShadeStyle(context: MaterialContext, shadeStyle: ShadeStyle) -} - -class DummyMaterial : Material { - override var name: String? = null - override var doubleSided: Boolean = true - override var transparent: Boolean = false - override var fragmentID = 0 - - override fun generateShadeStyle(context: MaterialContext, primitiveContext: PrimitiveContext): ShadeStyle { - return shadeStyle { - fragmentPreamble = """ - int f_fragmentID = p_fragmentID; - """.trimIndent() - - fragmentTransform = """ - x_fill.rgb = vec3(normalize(v_viewNormal).z); - """.trimIndent() - - parameter("fragmentID", fragmentID) - } - } - - override fun applyToShadeStyle(context: MaterialContext, shadeStyle: ShadeStyle) { - - } - -} - -data class MaterialContext(val pass: RenderPass, - val lights: List>, - val fogs: List>, - val shadowMaps: Map, - val meshCubemaps: Map, - val irradianceProbeCount: Int - ) { - - var irradianceSH: IrradianceSH? = null -} - - - -@JvmRecord -data class PrimitiveContext(val hasNormalAttribute: Boolean, val hasSkinning: Boolean) - -@JvmRecord -data class ContextKey(val materialContext: MaterialContext, val primitiveContext: PrimitiveContext) diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/PBRMaterial.kt b/orx-jvm/orx-dnk3/src/main/kotlin/PBRMaterial.kt deleted file mode 100644 index 6e8a7e50..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/PBRMaterial.kt +++ /dev/null @@ -1,731 +0,0 @@ -package org.openrndr.extra.dnk3 - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.dnk3.cubemap.glslEvaluateSH -import org.openrndr.extra.dnk3.cubemap.glslFetchSH -import org.openrndr.extra.dnk3.cubemap.genGlslGatherSH -import org.openrndr.extra.shaderphrases.phrases.phraseTbnMatrix -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 -import org.openrndr.math.transforms.normalMatrix -import java.nio.ByteBuffer -import kotlin.math.cos - -private val noise128 by lazy { - val cb = colorBuffer(128, 128) - val items = cb.width * cb.height * cb.format.componentCount - val buffer = ByteBuffer.allocateDirect(items) - for (y in 0 until cb.height) { - for (x in 0 until cb.width) { - for (i in 0 until 4) - buffer.put((Math.random() * 255).toInt().toByte()) - } - } - buffer.rewind() - cb.write(buffer) - cb.generateMipmaps() - cb.filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - cb.wrapU = WrapMode.REPEAT - cb.wrapV = WrapMode.REPEAT - cb -} - -private fun PointLight.fs(index: Int, hasNormalAttribute: Boolean): String = """ -|{ -| vec3 Lr = p_lightPosition$index - v_worldPosition; -| float distance = length(Lr); -| float attenuation = 1.0 / (p_lightConstantAttenuation$index + -| p_lightLinearAttenuation$index * distance + p_lightQuadraticAttenuation$index * distance * distance); -| vec3 L = normalize(Lr); -| -| float side = ${if (hasNormalAttribute) "dot(L, N)" else "3.1415"}; -| f_diffuse += attenuation * max(0.0, side / 3.1415) * p_lightColor$index.rgb * m_color.rgb; -| f_specular += attenuation * ggx(N, V, L, m_roughness, m_f0) * p_lightColor$index.rgb * m_color.rgb; -} -""".trimMargin() - -private fun AmbientLight.fs(index: Int): String = "f_ambient += p_lightColor$index.rgb * ((1.0 - m_metalness) * m_color.rgb);" - -private fun DirectionalLight.fs(index: Int, hasNormalAttribute: Boolean) = """ -|{ -| vec3 L = normalize(-p_lightDirection$index); -| float attenuation = 1.0; -| vec3 H = normalize(V + L); -| float NoL = ${if (hasNormalAttribute) "clamp(dot(N, L), 0.0, 1.0)" else "1"}; -| float LoH = clamp(dot(L, H), 0.0, 1.0); -| float NoH = ${if (hasNormalAttribute) "clamp(dot(N, H), 0.0, 1.0)" else "1"}; -| vec3 Lr = (p_lightPosition$index - v_worldPosition); -//| vec3 L = normalize(Lr); -| ${shadows.fs(index)} -| -| f_diffuse += NoL * attenuation * Fd_Burley(m_roughness * m_roughness, NoV, NoL, LoH) * p_lightColor$index.rgb * m_color.rgb * m_ambientOcclusion;; -| float Dg = D_GGX(m_roughness * m_roughness, NoH, H); -| float Vs = V_SmithGGXCorrelated(m_roughness * m_roughness, NoV, NoL); -| vec3 F = F_Schlick(m_color.rgb * (m_metalness) + 0.04 * (1.0-m_metalness), LoH); -| vec3 Fr = (Dg * Vs) * F; -| f_specular += NoL * attenuation * Fr * p_lightColor$index.rgb * m_ambientOcclusion;; -|} -""".trimMargin() - -private fun HemisphereLight.fs(index: Int, hasNormalAttribute: Boolean): String = """ -|{ -| float f = ${if (hasNormalAttribute) "dot(N, p_lightDirection$index) * 0.5 + 0.5" else "1.0"}; -| vec3 irr = ${irradianceMap?.let { "texture(p_lightIrradianceMap$index, N).rgb" } ?: "vec3(1.0)"}; -| f_diffuse += mix(p_lightDownColor$index.rgb, p_lightUpColor$index.rgb, f) * irr * ((1.0 - m_metalness) * m_color.rgb) * m_ambientOcclusion; -|} -""".trimMargin() - -private fun SpotLight.fs(index: Int, hasNormalAttribute: Boolean): String { - val shadows = shadows - return """ -|{ -| vec3 Lr = p_lightPosition$index - v_worldPosition; -| float distance = length(Lr); -| float attenuation = 1.0 / (p_lightConstantAttenuation$index + -| p_lightLinearAttenuation$index * distance + p_lightQuadraticAttenuation$index * distance * distance); -| attenuation = 1.0; -| vec3 L = normalize(Lr); - -| float NoL = ${if (hasNormalAttribute) "clamp(dot(N, L), 0.0, 1.0)" else "1"}; -| float side = dot(L, N); -| float hit = max(dot(-L, p_lightDirection$index), 0.0); -| float falloff = clamp((hit - p_lightOuterCos$index) / (p_lightInnerCos$index - p_lightOuterCos$index), 0.0, 1.0); -| attenuation *= falloff; -| ${shadows.fs(index)} -| { -| vec3 H = normalize(V + L); -| float LoH = clamp(dot(L, H), 0.0, 1.0); -| float NoH = ${if (hasNormalAttribute) "clamp(dot(N, H), 0.0, 1.0)" else 1.0}; -| f_diffuse += NoL * (0.1+0.9*attenuation) * Fd_Burley(m_roughness * m_roughness, NoV, NoL, LoH) * p_lightColor$index.rgb * m_color.rgb ; -| float Dg = D_GGX(m_roughness * m_roughness, NoH, H); -| float Vs = V_SmithGGXCorrelated(m_roughness * m_roughness, NoV, NoL); -| vec3 F = F_Schlick(m_color.rgb * (m_metalness) + 0.04 * (1.0-m_metalness), LoH); -| vec3 Fr = (Dg * Vs) * F; -| f_specular += NoL * attenuation * Fr * p_lightColor$index.rgb; -| } -} -""".trimMargin() -} - -private fun Fog.fs(index: Int): String = """ -|{ -| float dz = min(1.0, -v_viewPosition.z/p_fogEnd$index); -| f_fog = vec4(p_fogColor$index.rgb, dz); -|} -""".trimMargin() - -sealed class TextureSource -object DummySource : TextureSource() { - override fun toString(): String { - return "DummySource()" - } -} - -abstract class TextureFromColorBuffer(var texture: ColorBuffer, var textureFunction: TextureFunction) : TextureSource() - -class TextureFromCode(val code: String) : TextureSource() { - override fun hashCode(): Int { - return code.hashCode() - } -} - -private fun TextureFromCode.fs(index: Int, target: TextureTarget) = """ -|vec4 tex$index = vec4(0.0, 0.0, 0.0, 1.0); -|{ -|vec4 texOut; -|$code; -|tex$index = texOut; -|} -""" - -enum class TextureFunction(val function: (String, String) -> String) { - TILING({ texture, uv -> "texture($texture, $uv)" }), - NOT_TILING({ texture, uv -> "textureNoTile(p_textureNoise, $texture, x_noTileOffset, $uv)" }) - ; -} - -/** - * @param texture the texture to sample from - * @param input input coordinates, default is "va_texCoord0.xy" - * @param textureFunction the texture function to use, default is TextureFunction.TILING - * @param pre the pre-fetch shader code to inject, can only adjust "x_texCoord" - * @param post the post-fetch shader code to inject, can only adjust "x_texture" - */ -class ModelCoordinates(texture: ColorBuffer, - var input: String = "va_texCoord0.xy", - var tangentInput: String? = null, - textureFunction: TextureFunction = TextureFunction.TILING, - var pre: String? = null, - var post: String? = null) : TextureFromColorBuffer(texture, textureFunction) { - override fun toString(): String { - return "ModelCoordinates(texture: $texture, input: $input, $tangentInput: $tangentInput, textureFunction: $textureFunction, pre: $pre, post: $post)" - } - - override fun hashCode(): Int { - var result = input.hashCode() - result = 31 * result + (tangentInput?.hashCode() ?: 0) - result = 31 * result + (pre?.hashCode() ?: 0) - result = 31 * result + (post?.hashCode() ?: 0) - return result - } -} - - -class Triplanar(texture: ColorBuffer, - var scale: Double = 1.0, - var offset: Vector3 = Vector3.ZERO, - var sharpness: Double = 2.0, - textureFunction: TextureFunction = TextureFunction.TILING, - var pre: String? = null, - var post: String? = null) : TextureFromColorBuffer(texture, textureFunction) { - - init { - texture.filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - texture.wrapU = WrapMode.REPEAT - texture.wrapV = WrapMode.REPEAT - } - - override fun hashCode(): Int { - var result = scale.hashCode() - result = 31 * result + offset.hashCode() - result = 31 * result + sharpness.hashCode() - result = 31 * result + (pre?.hashCode() ?: 0) - result = 31 * result + (post?.hashCode() ?: 0) - return result - } - - -} - -private fun ModelCoordinates.fs(index: Int) = """ -|vec4 tex$index = vec4(0.0, 0.0, 0.0, 1.0); -|{ -| vec2 x_texCoord = $input; -| vec2 x_noTileOffset = vec2(0.0); -| vec4 x_texture; -| ${if (pre != null) "{ $pre } " else ""} -| x_texture = ${textureFunction.function("p_texture$index", "x_texCoord")}; -| ${if (post != null) "{ $post } " else ""} -| ${if (tangentInput != null) { - """ -| vec3 normal = normalize(va_normal.xyz); -| vec3 tangent = normalize(${tangentInput}.xyz); -| vec3 bitangent = cross(normal, tangent) * ${tangentInput}.w; -| mat3 tbn = mat3(tangent, bitangent, normal); -| x_texture.rgb = tbn * normalize( (x_texture.rgb - vec3(0.5, 0.5, 0.0))*vec3(2.0, 2.0, 1.0)) ; -""".trimMargin() - -} else ""} -| tex$index = x_texture; -|} -""".trimMargin() - -private fun Triplanar.fs(index: Int, target: TextureTarget) = """ -|vec4 tex$index = vec4(0.0, 0.0, 0.0, 1.0); -|{ -| vec3 x_normal = va_normal; -| vec3 x_position = va_position; -| float x_scale = p_textureTriplanarScale$index; -| vec3 x_offset = p_textureTriplanarOffset$index; -| vec2 x_noTileOffset = vec2(0.0); -| ${if (pre != null) "{ $pre } " else ""} -| vec3 n = normalize(x_normal); -| vec3 an = abs(n); -| vec2 uvY = x_position.xz * x_scale + x_offset.x; -| vec2 uvX = x_position.zy * x_scale + x_offset.y; -| vec2 uvZ = x_position.xy * x_scale + x_offset.z; -| vec4 tY = ${textureFunction.function("p_texture$index", "uvY")}; -| vec4 tX = ${textureFunction.function("p_texture$index", "uvX")}; -| vec4 tZ = ${textureFunction.function("p_texture$index", "uvZ")}; -| vec3 weights = pow(an, vec3(p_textureTriplanarSharpness$index)); -| weights = weights / (weights.x + weights.y + weights.z); -| tex$index = tX * weights.x + tY * weights.y + weights.z * tZ; -| ${if (target == TextureTarget.NORMAL) """ - | vec3 tnX = normalize( tX.xyz - vec3(0.5, 0.5, 0.0)); - | vec3 tnY = normalize( tY.xyz - vec3(0.5, 0.5, 0.0)) * vec3(1.0, -1.0, 1.0); - | vec3 tnZ = normalize( tZ.xyz - vec3(0.5, 0.5, 0.0)); - | vec3 nX = vec3(0.0, tnX.yx); - | vec3 nY = vec3(tnY.x, 0.0, tnY.y); - | vec3 nZ = vec3(tnZ.xy, 0.0); - | vec3 normal = normalize(nX * weights.x + nY * weights.y + nZ * weights.z + n); - | tex$index = vec4(normal, 0.0); -""".trimMargin() else ""} -|} - ${if (post != null) """ - vec4 x_texture = tex$index; - { - $post - } - tex$index = x_texture; - """.trimIndent() else ""} -""".trimMargin() - -sealed class TextureTarget(val name: String) { - object NONE : TextureTarget("NONE") - object COLOR : TextureTarget("COLOR") - object ROUGHNESS : TextureTarget("ROUGHNESS") - object METALNESS : TextureTarget("METALNESS") - object METALNESS_ROUGHNESS : TextureTarget("METALNESS_ROUGHNESS") - object EMISSION : TextureTarget("EMISSION") - object NORMAL : TextureTarget("NORMAL") - object AMBIENT_OCCLUSION : TextureTarget("AMBIENT_OCCLUSION") - class Height(var scale: Double = 1.0) : TextureTarget("Height") - - override fun toString(): String { - return "TextureTarget(name: $name)" - } - - override fun hashCode(): Int { - return name.hashCode() - } -} - -class Texture(var source: TextureSource, - var target: TextureTarget) { - fun copy(): Texture { - val copied = Texture(source, target) - return copied - } - - override fun toString(): String { - return "Texture(source: $source, target: $target)" - } - - override fun hashCode(): Int { - var result = source.hashCode() - result = 31 * result + target.hashCode() - return result - } -} - -private var fragmentIDCounter = 1 - -data class SubsurfaceScatter(var enabled: Boolean) { - var color: ColorRGBa = ColorRGBa.WHITE - var shape = 1.0 - - fun fs(): String { - return if (enabled) """ - f_diffuse.rgb += pow(smoothstep(1.0, 0.0, abs(dot(normalize(N),normalize(V)))), p_sssShape) * clamp(evaluateSH(-V, sh), vec3(0.0), vec3(1.0)) * p_sssColor.rgb; - """ else "" - } - - fun applyToShadeStyle(shadeStyle: ShadeStyle) { - if (enabled) { - shadeStyle.parameter("sssColor", color) - shadeStyle.parameter("sssShape", shape) - } - } -} - -data class CubemapReflection(var cubemap: Cubemap? = null) { - var color: ColorRGBa = ColorRGBa.WHITE - - fun fs(): String { - return if (cubemap != null) { - """ - vec2 dfg = PrefilteredDFG_Karis(m_roughness, NoV); - vec3 sc = m_metalness * m_color.rgb + (1.0-m_metalness) * vec3(0.04); - f_specular.rgb += sc * (texture(p_radianceMap, reflect(-V, normalize(f_worldNormal)), m_roughness*7.0 ).rgb * dfg.x + dfg.y) * p_radianceColor.rgb; - """ - } else { "" } - } - fun applyToShadeStyle(shadeStyle: ShadeStyle) { - if (cubemap != null) { - shadeStyle.parameter("radianceMap", cubemap!!) - shadeStyle.parameter("radianceColor", color) - } - } -} - - -class PBRMaterial : Material { - override var name: String? = null - override fun toString(): String { - return "PBRMaterial(name: $name, fragmentID: $fragmentID, doubleSided: $doubleSided, textures: $textures, color: $color, metalness: $metalness, roughness: $roughness, emissive: $emission))" - } - - override var fragmentID = fragmentIDCounter.apply { - fragmentIDCounter++ - } - - override var doubleSided: Boolean = false - override var transparent: Boolean = false - var environmentMap = false - var color = ColorRGBa.WHITE - var metalness = 0.5 - var roughness = 1.0 - var emission = ColorRGBa.BLACK - - var subsurfaceScatter = SubsurfaceScatter(false) - var cubemapReflection = CubemapReflection(null) - - - var fragmentPreamble: String? = null - var vertexPreamble: String? = null - var vertexTransform: String? = null - var parameters = mutableMapOf() - var textures = mutableListOf() - - val shadeStyles = mutableMapOf() - - override fun generateShadeStyle(materialContext: MaterialContext, primitiveContext: PrimitiveContext): ShadeStyle { - val cached = shadeStyles.getOrPut(ContextKey(materialContext, primitiveContext)) { - val needLight = needLight(materialContext) - val preambleFS = """ - vec4 m_color = p_color; - uint f_fragmentID = uint(p_fragmentID); - float m_f0 = 0.5; - float m_roughness = p_roughness; - float m_metalness = p_metalness; - float m_ambientOcclusion = 1.0; - vec3 m_emission = p_emission.rgb; - vec3 m_normal = vec3(0.0, 0.0, 1.0); - vec4 f_fog = vec4(0.0, 0.0, 0.0, 0.0); - vec3 f_worldNormal = v_worldNormal; - vec3 f_emission = m_emission; - """.trimIndent() - - val textureFs = if (needLight) { - (textures.mapIndexed { index, it -> - when (val source = it.source) { - DummySource -> "vec4 tex$index = vec4(1.0);" - is ModelCoordinates -> source.fs(index) - is Triplanar -> source.fs(index, it.target) - is TextureFromCode -> source.fs(index, it.target) - else -> TODO() - } - } + textures.mapIndexed { index, texture -> - when (texture.target) { - TextureTarget.NONE -> "" - TextureTarget.COLOR -> "m_color.rgb *= pow(tex$index.rgb, vec3(2.2)); m_color.a *= tex$index.a;" - TextureTarget.METALNESS -> "m_metalness = tex$index.r;" - TextureTarget.ROUGHNESS -> "m_roughness = tex$index.r;" - TextureTarget.METALNESS_ROUGHNESS -> "m_metalness = tex$index.r; m_roughness = tex$index.g;" - TextureTarget.EMISSION -> "m_emission *= tex$index.rgb;" - TextureTarget.NORMAL -> "f_worldNormal = normalize((v_modelNormalMatrix * vec4(tex$index.xyz,0.0)).xyz);" - TextureTarget.AMBIENT_OCCLUSION -> "m_ambientOcclusion *= tex$index.r;" - is TextureTarget.Height -> "" - } - }).joinToString("\n") - } else "" - val displacers = textures.filter { it.target is TextureTarget.Height } - - val skinVS = if (primitiveContext.hasSkinning) """ - uvec4 j = a_joints; - mat4 skinTransform = p_jointTransforms[j.x] * a_weights.x - + p_jointTransforms[j.y] * a_weights.y - + p_jointTransforms[j.z] * a_weights.z - + p_jointTransforms[j.w] * a_weights.w; - ${if (primitiveContext.hasNormalAttribute) """ - x_normal = normalize(mat3(skinTransform) * x_normal); - """.trimIndent() else ""} - - x_position = (skinTransform * vec4(x_position,1)).xyz; - """.trimIndent() else "" - - val textureVS = if (displacers.isNotEmpty()) textures.mapIndexed { index, it -> - if (it.target is TextureTarget.Height) { - when (val source = it.source) { - DummySource -> "vec4 tex$index = vec4(1.0);" - is ModelCoordinates -> source.fs(index) - is Triplanar -> source.fs(index, it.target) - is TextureFromCode -> source.fs(index, it.target) - else -> TODO() - } + """ - x_position += x_normal * tex$index.r * p_textureHeightScale$index; - """.trimIndent() - } else "" - }.joinToString("\n") else "" - - val lights = materialContext.lights - - val doubleSidedFS = if (doubleSided) { - """ - if (dot(V, N) <0) { - N *= -1.0; - } - """.trimIndent() - } else "" - val lightFS = if (needLight) """ - vec3 f_diffuse = vec3(0.0); - vec3 f_specular = vec3(0.0); - vec3 f_ambient = vec3(0.0); - float f_occlusion = 1.0; - vec3 N = normalize(f_worldNormal); - - vec3 ep = (p_viewMatrixInverse * vec4(0.0, 0.0, 0.0, 1.0)).xyz; - vec3 Vr = ep - v_worldPosition; - vec3 V = normalize(Vr); - - float NoV = ${if (primitiveContext.hasNormalAttribute) "abs(dot(N, V)) + 1e-5" else "1.0"}; - - ${if (environmentMap && materialContext.meshCubemaps.isNotEmpty() && primitiveContext.hasNormalAttribute) """ - { - vec2 dfg = PrefilteredDFG_Karis(m_roughness, NoV); - vec3 sc = m_metalness * m_color.rgb + (1.0-m_metalness) * vec3(0.04); - - f_specular.rgb += sc * (texture(p_environmentMap, reflect(-V, normalize(f_worldNormal))).rgb * dfg.x + dfg.y) * m_ambientOcclusion; - } - """.trimIndent() else ""} - - ${lights.mapIndexed { index, (node, light) -> - when (light) { - is AmbientLight -> light.fs(index) - is PointLight -> light.fs(index, primitiveContext.hasNormalAttribute) - is SpotLight -> light.fs(index, primitiveContext.hasNormalAttribute) - is DirectionalLight -> light.fs(index, primitiveContext.hasNormalAttribute) - is HemisphereLight -> light.fs(index, primitiveContext.hasNormalAttribute) - else -> TODO() - } - }.joinToString("\n")} - - - ${if (materialContext.irradianceSH?.shMap != null) """ - vec3[9] sh; - gatherSH(p_shMap, v_worldPosition, sh); - vec3 irradiance = clamp(evaluateSH(normalize(N), sh), vec3(0.0), vec3(1.0)) * m_color.rgb; - vec3 ks = F_SchlickRoughness(m_color.rgb * (m_metalness) + 0.04 * (1.0-m_metalness), m_roughness+0.1, min(NoV, 1.0-1.0e-6)); - f_diffuse.rgb = irradiance * ks; - f_ambient.rgb = (1.0-ks) * irradiance; - ${subsurfaceScatter.fs()} - ${cubemapReflection.fs()} - """.trimIndent() else "" - } - - ${materialContext.fogs.mapIndexed { index, (node, fog) -> - - fog.fs(index) - }.joinToString("\n")} - - """.trimIndent() else "" - val rt = RenderTarget.active - - val combinerFS = materialContext.pass.combiners.map { - it.generateShader() - }.joinToString("\n") - - val fs = preambleFS + textureFs + lightFS + combinerFS - val vs = (this@PBRMaterial.vertexTransform ?: "") + textureVS + skinVS - - shadeStyle { - fragmentPreamble = this@PBRMaterial.fragmentPreamble ?: "" - vertexPreamble = """ - $shaderNoRepetitionVert - ${(this@PBRMaterial.vertexPreamble) ?: ""} - """.trimIndent() - fragmentPreamble += """ - ${if (materialContext.irradianceSH?.shMap != null) { - """ - $glslEvaluateSH - $glslFetchSH - ${genGlslGatherSH(materialContext.irradianceSH!!.xCount, materialContext.irradianceSH!!.yCount, - materialContext.irradianceSH!!.zCount, materialContext.irradianceSH!!.spacing, materialContext.irradianceSH!!.offset)} - """ - } else { - "" - } - } - |$shaderLinePlaneIntersect - |$shaderProjectOnPlane - |$shaderSideOfPlane - |$shaderGGX - |$shaderVSM - |$shaderNoRepetition - |$phraseTbnMatrix - """.trimMargin() - this.suppressDefaultOutput = true - this.vertexTransform = vs - fragmentTransform = fs - - materialContext.pass.combiners.map { - if (rt is ProgramRenderTarget || materialContext.pass === DefaultPass || materialContext.pass === DefaultOpaquePass || materialContext.pass == DefaultTransparentPass || materialContext.pass == IrradianceProbePass || materialContext.pass.skipTarget ) { - this.output(it.targetOutput, ShadeStyleOutput(0)) - } else { - val index = rt.colorAttachmentIndexByName(it.targetOutput)?:error("attachment ${it.targetOutput} not found") - val type = rt.colorBuffer(index).type - val format = rt.colorBuffer(index).format - this.output(it.targetOutput, ShadeStyleOutput(index, format, type)) - } - } - } - } - return cached - } - - private fun needLight(context: MaterialContext): Boolean { - val needSpecular = context.pass.combiners.any { FacetType.SPECULAR in it.facets } - val needDiffuse = context.pass.combiners.any { FacetType.DIFFUSE in it.facets } - val needLight = needSpecular || needDiffuse - return needLight - } - - override fun applyToShadeStyle(context: MaterialContext, shadeStyle: ShadeStyle) { - shadeStyle.parameter("emission", emission) - shadeStyle.parameter("color", color) - shadeStyle.parameter("metalness", metalness) - shadeStyle.parameter("roughness", roughness) - shadeStyle.parameter("fragmentID", fragmentID) - - if (context.irradianceProbeCount > 0) { - shadeStyle.parameter("shMap", context.irradianceSH?.shMap!!) - } - - parameters.forEach { (k, v) -> - when (v) { - is Double -> shadeStyle.parameter(k, v) - is Int -> shadeStyle.parameter(k, v) - is Vector2 -> shadeStyle.parameter(k, v) - is Vector3 -> shadeStyle.parameter(k, v) - is Vector4 -> shadeStyle.parameter(k, v) - is BufferTexture -> shadeStyle.parameter(k, v) - is ColorBuffer -> shadeStyle.parameter(k, v) - else -> TODO("support ${v::class.java}") - } - } - if (needLight(context)) { - - subsurfaceScatter.applyToShadeStyle(shadeStyle) - cubemapReflection.applyToShadeStyle(shadeStyle) - - textures.forEachIndexed { index, texture -> - when (val source = texture.source) { - is TextureFromColorBuffer -> { - shadeStyle.parameter("texture$index", source.texture) - if (source.textureFunction == TextureFunction.NOT_TILING) { - shadeStyle.parameter("textureNoise", noise128) - } - } - else -> {} - } - when (val source = texture.source) { - is Triplanar -> { - shadeStyle.parameter("textureTriplanarSharpness$index", source.sharpness) - shadeStyle.parameter("textureTriplanarScale$index", source.scale) - shadeStyle.parameter("textureTriplanarOffset$index", source.offset) - } - else -> {} - } - if (texture.target is TextureTarget.Height) { - val target = texture.target as TextureTarget.Height - shadeStyle.parameter("textureHeightScale$index", target.scale) - } - - } - - val lights = context.lights - lights.forEachIndexed { index, (node, light) -> - shadeStyle.parameter("lightColor$index", light.color) - when (light) { - is AmbientLight -> { - } - is PointLight -> { - shadeStyle.parameter("lightPosition$index", (node.worldTransform * Vector4.UNIT_W).xyz) - shadeStyle.parameter("lightConstantAttenuation$index", light.constantAttenuation) - shadeStyle.parameter("lightLinearAttenuation$index", light.linearAttenuation) - shadeStyle.parameter("lightQuadraticAttenuation$index", light.quadraticAttenuation) - } - - is SpotLight -> { - shadeStyle.parameter("lightPosition$index", (node.worldTransform * Vector4.UNIT_W).xyz) - shadeStyle.parameter("lightDirection$index", ((normalMatrix(node.worldTransform)) * light.direction.xyz0).normalized.xyz) - shadeStyle.parameter("lightConstantAttenuation$index", light.constantAttenuation) - shadeStyle.parameter("lightLinearAttenuation$index", light.linearAttenuation) - shadeStyle.parameter("lightQuadraticAttenuation$index", light.quadraticAttenuation) - shadeStyle.parameter("lightInnerCos$index", cos(Math.toRadians(light.innerAngle))) - shadeStyle.parameter("lightOuterCos$index", cos(Math.toRadians(light.outerAngle))) - - if (light.shadows is Shadows.MappedShadows) { - context.shadowMaps[light]?.let { - val look = light.view(node) - shadeStyle.parameter("lightTransform$index", - light.projection(it) * look) - - if (light.shadows is Shadows.DepthMappedShadows) { - shadeStyle.parameter("lightShadowMap$index", it.depthBuffer?:error("no depth buffer for $it")) - } - - if (light.shadows is Shadows.ColorMappedShadows) { - shadeStyle.parameter("lightShadowMap$index", it.colorBuffer(0)) - } - } - } - } - is DirectionalLight -> { - shadeStyle.parameter("lightPosition$index", (node.worldTransform * Vector4.UNIT_W).xyz) - shadeStyle.parameter("lightDirection$index", ((normalMatrix(node.worldTransform)) * light.direction.xyz0).normalized.xyz) - if (light.shadows is Shadows.MappedShadows) { - context.shadowMaps[light]?.let { - val look = light.view(node) - shadeStyle.parameter("lightTransform$index", - light.projection(it) * look) - - if (light.shadows is Shadows.DepthMappedShadows) { - shadeStyle.parameter("lightShadowMap$index", it.depthBuffer ?: TODO()) - } - - if (light.shadows is Shadows.ColorMappedShadows) { - shadeStyle.parameter("lightShadowMap$index", it.colorBuffer(0)) - } - } - } - } - - is HemisphereLight -> { - shadeStyle.parameter("lightDirection$index", ((normalMatrix(node.worldTransform)) * light.direction.xyz0).normalized.xyz) - shadeStyle.parameter("lightUpColor$index", light.upColor) - shadeStyle.parameter("lightDownColor$index", light.downColor) - - light.irradianceMap?.let { - shadeStyle.parameter("lightIrradianceMap$index", it) - } - } - } - } - context.fogs.forEachIndexed { index, (node, fog) -> - shadeStyle.parameter("fogColor$index", fog.color) - shadeStyle.parameter("fogEnd$index", fog.end) - } - } else { - textures.forEachIndexed { index, texture -> - if (texture.target is TextureTarget.Height) { - when (val source = texture.source) { - is TextureFromColorBuffer -> shadeStyle.parameter("texture$index", source.texture) - else -> {} - } - when (val source = texture.source) { - is Triplanar -> { - shadeStyle.parameter("textureTriplanarSharpness$index", source.sharpness) - shadeStyle.parameter("textureTriplanarScale$index", source.scale) - shadeStyle.parameter("textureTriplanarOffset$index", source.offset) - } - else -> {} - } - val target = texture.target as TextureTarget.Height - shadeStyle.parameter("textureHeightScale$index", target.scale) - } - } - } - } - - override fun hashCode(): Int { - var result = fragmentID.hashCode() - result = 31 * doubleSided.hashCode() - result = 31 * result + transparent.hashCode() -// result = 31 * result + environmentMap.hashCode() - result = 31 * result + color.hashCode() - result = 31 * result + metalness.hashCode() - result = 31 * result + roughness.hashCode() - result = 31 * result + emission.hashCode() - result = 31 * result + (fragmentPreamble?.hashCode() ?: 0) - result = 31 * result + (vertexPreamble?.hashCode() ?: 0) - result = 31 * result + (vertexTransform?.hashCode() ?: 0) -// result = 31 * result + parameters.hashCode() -// result = 31 * result + textures.hashCode() -// result = 31 * result + shadeStyles.hashCode() - return result - } -} - diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/Post.kt b/orx-jvm/orx-dnk3/src/main/kotlin/Post.kt deleted file mode 100644 index 37215e11..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/Post.kt +++ /dev/null @@ -1,62 +0,0 @@ -package org.openrndr.extra.dnk3 - -import org.openrndr.draw.* -import org.openrndr.math.Matrix44 - -@JvmRecord -data class PostContext(val lightContext: LightContext, val inverseViewMatrix: Matrix44) - -interface PostStep { - fun apply(buffers: MutableMap, postContext: PostContext) -} - -class FilterPostStep(val outputScale: Double, - val filter: T, - val inputs: List, - val output: String, - val outputFormat: ColorFormat, - val outputType: ColorType, - val update: (T.(PostContext) -> Unit)? = null) : PostStep { - - override fun apply(buffers: MutableMap, postContext: PostContext) { - val inputBuffers = inputs.map { buffers[it]?: error("buffer not found: $it") } - val outputBuffer = buffers.getOrPut(output) { - colorBuffer((inputBuffers[0].width * outputScale).toInt(), - (inputBuffers[0].height * outputScale).toInt(), - format = outputFormat, - type = outputType) - } - update?.invoke(filter, postContext) - filter.apply(inputBuffers.toTypedArray(), outputBuffer) - } -} - -class FunctionPostStep(val function:(MutableMap)->Unit) : PostStep { - override fun apply(buffers: MutableMap, postContext: PostContext) { - function(buffers) - } -} - -class FilterPostStepBuilder(val filter: T) { - var outputScale = 1.0 - val inputs = mutableListOf() - var output = "untitled" - var outputFormat = ColorFormat.RGBa - var outputType = ColorType.UINT8 - var update: (T.(PostContext) -> Unit)? = null - - internal fun build(): PostStep { - @Suppress("UNCHECKED_CAST", "PackageDirectoryMismatch") - return FilterPostStep(outputScale, filter, inputs, output, outputFormat, outputType, update as (Filter.(PostContext) -> Unit)?) - } -} - -fun postStep(filter: T, configure: FilterPostStepBuilder.() -> Unit) : PostStep { - val psb = FilterPostStepBuilder(filter) - psb.configure() - return psb.build() -} - -fun postStep(function: (MutableMap)->Unit) : PostStep { - return FunctionPostStep(function) -} diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/RenderPass.kt b/orx-jvm/orx-dnk3/src/main/kotlin/RenderPass.kt deleted file mode 100644 index 8b324c40..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/RenderPass.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.openrndr.extra.dnk3 - -import org.openrndr.draw.BufferMultisample -import org.openrndr.draw.DepthFormat -import org.openrndr.draw.RenderTarget -import org.openrndr.draw.renderTarget - -@JvmRecord -data class RenderPass(val combiners: List, - val renderOpaque: Boolean = true, - val renderTransparent: Boolean = false, - val depthWrite: Boolean = true, - val multisample: BufferMultisample = BufferMultisample.Disabled, - val skipTarget: Boolean = false -) - - -val DefaultPass = RenderPass(listOf(LDRColorFacet())) -val IrradianceProbePass = RenderPass(listOf(DiffuseIrradianceFacet())) - -val DefaultOpaquePass = RenderPass(listOf(LDRColorFacet()), renderOpaque = true, renderTransparent = false) -val DefaultTransparentPass = RenderPass(listOf(LDRColorFacet()), renderOpaque = false, renderTransparent = true, depthWrite = false) -val LightPass = RenderPass(listOf(ClipDepthFacet())) -val VSMLightPass = RenderPass(listOf(MomentsFacet())) - -fun RenderPass.createPassTarget(width: Int, height: Int, depthFormat: DepthFormat = DepthFormat.DEPTH24, multisample: BufferMultisample = this.multisample): RenderTarget { - return renderTarget(width, height, multisample = multisample) { - for (combiner in combiners) { - when (combiner) { - is ColorBufferFacetCombiner -> - colorBuffer(combiner.targetOutput, combiner.format, combiner.type) - } - } - // Temporary fix for GLES back-end - if (combiners.isEmpty()) { - colorBuffer() - } - depthBuffer(depthFormat) - } -} diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/Scene.kt b/orx-jvm/orx-dnk3/src/main/kotlin/Scene.kt deleted file mode 100644 index b516df97..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/Scene.kt +++ /dev/null @@ -1,95 +0,0 @@ -package org.openrndr.extra.dnk3 - -import org.openrndr.Dispatcher -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 -import java.util.* - -class Scene(val root: SceneNode = SceneNode(), val dispatcher: Dispatcher = Dispatcher()) { - val features = mutableListOf() - override fun hashCode(): Int { - var result = root.hashCode() - result = result * 31 + features.hashCode() - return result - } - fun hash(): String = Base64.getEncoder().encodeToString(hashCode().toString().toByteArray()) -} - -open class SceneNode { - var name: String = "" - var entities: MutableList = mutableListOf() - var parent: SceneNode? = null - open var transform = Matrix44.IDENTITY - var worldTransform = Matrix44.IDENTITY - val children = mutableListOf() - var disposed = false - - override fun hashCode(): Int { - var result = name.hashCode() - result = 31 * result + entities.hashCode() -// result = 31 * result + (parent?.hashCode() ?: 0) - result = 31 * result + transform.hashCode() - result = 31 * result + worldTransform.hashCode() - result = 31 * result + children.hashCode() - result = 31 * result + disposed.hashCode() - return result - } -} - -val SceneNode.worldPosition: Vector3 - get() { - return (worldTransform * Vector4.UNIT_W).xyz - } - -class NodeContent(val node: SceneNode, val content: T) { - operator fun component1() = node - operator fun component2() = content - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - other as NodeContent<*> - if (node != other.node) return false - if (content != other.content) return false - return true - } - - override fun hashCode(): Int { - var result = node.hashCode() - result = 31 * result + content.hashCode() - return result - } -} - -fun SceneNode.visit(visitor: SceneNode.() -> Unit) { - visitor() - children.forEach { it.visit(visitor) } -} - -fun

SceneNode.scan(initial: P, scanner: SceneNode.(P) -> P) { - val p = scanner(initial) - children.forEach { it.scan(p, scanner) } -} - -fun SceneNode.findNodes(selector: SceneNode.() -> Boolean): List { - val result = mutableListOf() - visit { - if (selector()) result.add(this) - } - return result -} - -fun

SceneNode.findContent(selector: Entity.() -> P?): List> { - val result = mutableListOf>() - - visit { - entities.forEach { - val s = it.selector() - if (s != null) { - result.add(NodeContent(this, s)) - } - } - } - return result -} - diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/SceneRenderer.kt b/orx-jvm/orx-dnk3/src/main/kotlin/SceneRenderer.kt deleted file mode 100644 index e62672c5..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/SceneRenderer.kt +++ /dev/null @@ -1,341 +0,0 @@ -package org.openrndr.extra.dnk3 - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.dnk3.features.IrradianceSH -import org.openrndr.extra.fx.blur.ApproximateGaussianBlur -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector3 -import java.nio.ByteBuffer - -class RenderContext( - val lights: List>, - val meshes: List>, - val skinnedMeshes: List>, - val instancedMeshes: List>, - val pathMeshes: List>, - val fogs: List> -) - -class SceneRenderer { - class Configuration { - var multisampleLines = false - } - - val configuration = Configuration() - - val blur = ApproximateGaussianBlur() - - var shadowLightTargets = mutableMapOf() - var meshCubemaps = mutableMapOf() - - - var outputPasses = mutableListOf(DefaultOpaquePass, DefaultTransparentPass) - var outputPassTarget: RenderTarget? = null - var outputPassTargetMS: RenderTarget? = null - - val postSteps = mutableListOf() - val buffers = mutableMapOf() - - var drawFinalBuffer = true - - var first = true - fun draw(drawer: Drawer, scene: Scene) { - drawer.pushStyle() - drawer.depthWrite = true - drawer.depthTestPass = DepthTestPass.LESS_OR_EQUAL - - drawer.cullTestPass = CullTestPass.FRONT - - scene.dispatcher.execute() - - // update all the transforms - scene.root.scan(Matrix44.IDENTITY) { p -> - if (p !== Matrix44.IDENTITY) { - worldTransform = p * transform - } else { - worldTransform = transform - } - worldTransform - } - - val context = RenderContext( - lights = scene.root.findContent { this as? Light }, - meshes = scene.root.findContent { this as? Mesh }, - skinnedMeshes = scene.root.findContent { this as? SkinnedMesh }, - fogs = scene.root.findContent { this as? Fog }, - instancedMeshes = scene.root.findContent { this as? InstancedMesh }, - pathMeshes = scene.root.findContent { this as? PathMesh} - ) - - // shadow passes - run { - context.lights.filter { it.content is ShadowLight && (it.content as ShadowLight).shadows is Shadows.MappedShadows }.forEach { - val shadowLight = it.content as ShadowLight - val pass: RenderPass - pass = when (shadowLight.shadows) { - is Shadows.PCF, is Shadows.Simple -> { - LightPass - } - is Shadows.VSM -> { - VSMLightPass - } - else -> TODO() - } - val target = shadowLightTargets.getOrPut(shadowLight) { - val mapSize = (shadowLight.shadows as Shadows.MappedShadows).mapSize - pass.createPassTarget(mapSize, mapSize, DepthFormat.DEPTH_STENCIL) - } - val look = shadowLight.view(it.node) - val materialContext = MaterialContext(pass, context.lights, context.fogs, shadowLightTargets, emptyMap(), 0) - drawer.isolatedWithTarget(target) { - drawer.projection = shadowLight.projection(target) - drawer.view = look - drawer.model = Matrix44.IDENTITY - - drawer.clear(ColorRGBa.BLACK) - drawer.cullTestPass = CullTestPass.FRONT - drawPass(drawer, pass, materialContext, context) - } - when (shadowLight.shadows) { - is Shadows.VSM -> { - blur.gain = 1.0 - blur.sigma = 3.0 - blur.window = 9 - blur.spread = 1.0 - blur.apply(target.colorBuffer(0), target.colorBuffer(0)) - } - else -> {} - } - } - } - - // -- feature passes - for (feature in scene.features) { - feature.update(drawer, this, scene, feature, context) - } - - // -- output passes - run { - val irradianceSH = scene.features.find { it is IrradianceSH } as? IrradianceSH - for (pass in outputPasses) { - val materialContext = MaterialContext(pass, context.lights, context.fogs, shadowLightTargets, meshCubemaps, irradianceSH?.probeCount - ?: 0) - materialContext.irradianceSH = irradianceSH - - val defaultPasses = setOf(DefaultTransparentPass, DefaultOpaquePass) - - if ((pass !in defaultPasses || postSteps.isNotEmpty()) && outputPassTarget == null) { - outputPassTarget = pass.createPassTarget(RenderTarget.active.width, RenderTarget.active.height) - } - - if (pass == outputPasses[0]) { - outputPassTarget?.let { - drawer.withTarget(it) { - clear(ColorRGBa.TRANSPARENT) - } - } - } - outputPassTarget?.let { target -> - pass.combiners.forEach { - if (it is ColorBufferFacetCombiner) { - val index = target.colorAttachmentIndexByName(it.targetOutput) - ?: error("attachment not found ${it.targetOutput}") - target.blendMode(index, it.blendMode) - } - } - } - outputPassTarget?.bind() - drawPass(drawer, pass, materialContext, context) - outputPassTarget?.unbind() - - outputPassTarget?.let { output -> - for (combiner in pass.combiners) { - buffers[combiner.targetOutput] = (output.colorAttachmentByName(combiner.targetOutput) as? ColorBufferAttachment)?.colorBuffer - ?: error("attachment not found ${combiner.targetOutput}") - } - } - } - val lightContext = LightContext(context.lights, shadowLightTargets) - val postContext = PostContext(lightContext, drawer.view.inversed) - - for (postStep in postSteps) { - postStep.apply(buffers, postContext) - } - } - - drawer.popStyle() - if (drawFinalBuffer) { - outputPassTarget?.let { output -> - drawer.isolated { - drawer.defaults() - drawer.ortho() - val outputName = (postSteps.lastOrNull() as? FilterPostStep<*>)?.output ?: "color" - val outputBuffer = buffers[outputName] - ?: throw IllegalArgumentException("can't find $outputName buffer") - drawer.image(outputBuffer) - } - } - } - } - - internal fun drawPass(drawer: Drawer, pass: RenderPass, materialContext: MaterialContext, - context: RenderContext, shadeStyleTransformer: ((ShadeStyle)->Unit)? = null - ) { - - drawer.depthWrite = pass.depthWrite - val primitives = context.meshes.flatMap { mesh -> - mesh.content.primitives.map { primitive -> - NodeContent(mesh.node, primitive) - } - } - - // -- draw all meshes - primitives - .filter { (it.content.material.transparent && pass.renderTransparent) || (!it.content.material.transparent && pass.renderOpaque) } - .forEach { - val primitive = it.content - drawer.isolated { - if (primitive.material.doubleSided) { - drawer.drawStyle.cullTestPass = CullTestPass.ALWAYS - } - val hasNormalAttribute = primitive.geometry.vertexBuffers.any { it.vertexFormat.hasAttribute("normal") } - val primitiveContext = PrimitiveContext(hasNormalAttribute, false) - val shadeStyle = primitive.material.generateShadeStyle(materialContext, primitiveContext) - shadeStyle.parameter("viewMatrixInverse", drawer.view.inversed) - primitive.material.applyToShadeStyle(materialContext, shadeStyle) - shadeStyleTransformer?.invoke(shadeStyle) - - drawer.shadeStyle = shadeStyle - drawer.model = it.node.worldTransform - - if (primitive.geometry.indexBuffer == null) { - drawer.vertexBuffer(primitive.geometry.vertexBuffers, - primitive.geometry.primitive, - primitive.geometry.offset, - primitive.geometry.vertexCount) - } else { - drawer.vertexBuffer(primitive.geometry.indexBuffer!!, - primitive.geometry.vertexBuffers, - primitive.geometry.primitive, - primitive.geometry.offset, - primitive.geometry.vertexCount) - } - } - } - - - val skinnedPrimitives = context.skinnedMeshes.flatMap { mesh -> - mesh.content.primitives.map { primitive -> - NodeContent(mesh.node, Pair(primitive, mesh)) - } - } - - skinnedPrimitives - .filter { - (it.content.first.material.transparent && pass.renderTransparent) || - (!it.content.first.material.transparent && pass.renderOpaque) - } - .forEach { - val primitive = it.content.first - val skinnedMesh = it.content.second.content - drawer.isolated { - if (primitive.material.doubleSided) { - drawer.drawStyle.cullTestPass = CullTestPass.ALWAYS - } - val hasNormalAttribute = primitive.geometry.vertexBuffers.any { it.vertexFormat.hasAttribute("normal") } - val primitiveContext = PrimitiveContext(hasNormalAttribute, true) - - val nodeInverse = it.node.worldTransform.inversed - - - val jointTransforms = (skinnedMesh.joints zip skinnedMesh.inverseBindMatrices) - .map { (nodeInverse * it.first.worldTransform * it.second) } - val shadeStyle = primitive.material.generateShadeStyle(materialContext, primitiveContext) - - shadeStyle.parameter("jointTransforms", jointTransforms.toTypedArray()) - - shadeStyle.parameter("viewMatrixInverse", drawer.view.inversed) - primitive.material.applyToShadeStyle(materialContext, shadeStyle) - drawer.shadeStyle = shadeStyle - drawer.model = it.node.worldTransform - - if (primitive.geometry.indexBuffer == null) { - drawer.vertexBuffer(primitive.geometry.vertexBuffers, - primitive.geometry.primitive, - primitive.geometry.offset, - primitive.geometry.vertexCount) - } else { - drawer.vertexBuffer(primitive.geometry.indexBuffer!!, - primitive.geometry.vertexBuffers, - primitive.geometry.primitive, - primitive.geometry.offset, - primitive.geometry.vertexCount) - } - } - } - - - val instancedPrimitives = context.instancedMeshes.flatMap { mesh -> - mesh.content.primitives.map { primitive -> - NodeContent(mesh.node, MeshPrimitiveInstance(primitive, mesh.content.instances, mesh.content.attributes)) - } - } - - // -- draw all instanced meshes - instancedPrimitives - .filter { (it.content.primitive.material.transparent && pass.renderTransparent) || (!it.content.primitive.material.transparent && pass.renderOpaque) } - .forEach { - val primitive = it.content - drawer.isolated { - val primitiveContext = PrimitiveContext(true, false) - val shadeStyle = primitive.primitive.material.generateShadeStyle(materialContext, primitiveContext) - shadeStyle.parameter("viewMatrixInverse", drawer.view.inversed) - primitive.primitive.material.applyToShadeStyle(materialContext, shadeStyle) - if (primitive.primitive.material.doubleSided) { - drawer.drawStyle.cullTestPass = CullTestPass.ALWAYS - } - drawer.shadeStyle = shadeStyle - drawer.model = it.node.worldTransform - drawer.vertexBufferInstances(primitive.primitive.geometry.vertexBuffers, - primitive.attributes, - DrawPrimitive.TRIANGLES, - primitive.instances, - primitive.primitive.geometry.offset, - primitive.primitive.geometry.vertexCount) - } - } - - context.pathMeshes.filter { (it.content.material.transparent && pass.renderTransparent) || (!it.content.material.transparent && pass.renderOpaque) } - .forEach { - drawer.isolated { - val primitiveContext = PrimitiveContext(true, false) - val shadeStyle = it.content.material.generateShadeStyle(materialContext, primitiveContext) - shadeStyle.parameter("viewMatrixInverse", drawer.view.inversed) - it.content.material.applyToShadeStyle(materialContext, shadeStyle) - drawer.drawStyle.cullTestPass = CullTestPass.ALWAYS - drawer.shadeStyle = shadeStyle - drawer.model = it.node.worldTransform - drawer.strokeWeight = it.content.weight - for (path in it.content.paths) { - drawer.path(path.sampleLinear(0.0005)) - } - } - } - - - drawer.depthWrite = true - } -} - -fun sceneRenderer(builder: SceneRenderer.() -> Unit): SceneRenderer { - val sceneRenderer = SceneRenderer() - sceneRenderer.builder() - return sceneRenderer -} - -internal fun ByteBuffer.putVector3(v: Vector3) { - putFloat(v.x.toFloat()) - putFloat(v.y.toFloat()) - putFloat(v.z.toFloat()) -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/ShaderUtilities.kt b/orx-jvm/orx-dnk3/src/main/kotlin/ShaderUtilities.kt deleted file mode 100644 index 80ed275b..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/ShaderUtilities.kt +++ /dev/null @@ -1,237 +0,0 @@ -package org.openrndr.extra.dnk3 - -val shaderNoRepetition = """ -// -- shaderNoRepetition -float sum( vec3 v ) { return v.x+v.y+v.z; } - -// based on https://www.shadertoy.com/view/Xtl3zf -vec4 textureNoTile(in sampler2D noiseTex, in sampler2D tex, in vec2 noiseOffset, in vec2 x) -{ - float v = 1.0; - float k = texture(noiseTex, noiseOffset + x*0.01 ).x; // cheap (cache friendly) lookup - - vec2 duvdx = dFdx( x ); - vec2 duvdy = dFdx( x ); - - float l = k*8.0; - float f = fract(l); - -#if 0 - float ia = floor(l); // my method - float ib = ia + 1.0; -#else - float ia = floor(l+0.5); // suslik's method (see comments) - float ib = floor(l); - f = min(f, 1.0-f)*2.0; -#endif - - vec2 offa = sin(vec2(3.0,7.0)*ia); // can replace with any other hash - vec2 offb = sin(vec2(3.0,7.0)*ib); // can replace with any other hash - - vec3 cola = textureGrad( tex, x + v*offa, duvdx, duvdy ).xyz; - vec3 colb = textureGrad( tex, x + v*offb, duvdx, duvdy ).xyz; - - return vec4(mix( cola, colb, smoothstep(0.2,0.8,f-0.1*sum(cola-colb)) ), 1.0); -} -""" - -val shaderNoRepetitionVert = """ -// -- shaderNoRepetitionVert -float sum( vec3 v ) { return v.x+v.y+v.z; } - -// based on https://www.shadertoy.com/view/Xtl3zf -vec4 textureNoTile(in sampler2D tex, in vec2 noiseOffset, in vec2 x) -{ - float v = 1.0; - float k = texture(tex, noiseOffset + 0.005*x ).x; // cheap (cache friendly) lookup - - float l = k*8.0; - float f = fract(l); - -#if 0 - float ia = floor(l); // my method - float ib = ia + 1.0; -#else - float ia = floor(l+0.5); // suslik's method (see comments) - float ib = floor(l); - f = min(f, 1.0-f)*2.0; -#endif - - vec2 offa = sin(vec2(3.0,7.0)*ia); // can replace with any other hash - vec2 offb = sin(vec2(3.0,7.0)*ib); // can replace with any other hash - - vec3 cola = texture( tex, x + v*offa).xyz; - vec3 colb = texture( tex, x + v*offb).xyz; - - return vec4(mix( cola, colb, smoothstep(0.2,0.8,f-0.1*sum(cola-colb)) ), 1.0); -} -""" - -val shaderProjectOnPlane = """ -// -- shaderProjectOnPlane -vec3 projectOnPlane(vec3 p, vec3 pc, vec3 pn) { - float distance = dot(pn, p-pc); - return p - distance * pn; -} -""".trimIndent() - -val shaderSideOfPlane = """ -// -- shaderSideOfPlane -int sideOfPlane(in vec3 p, in vec3 pc, in vec3 pn){ - if (dot(p-pc,pn) >= 0.0) return 1; else return 0; -} -""".trimIndent() - -val shaderLinePlaneIntersect = """ -// -- shaderLinePlaneIntersect -vec3 linePlaneIntersect(in vec3 lp, in vec3 lv, in vec3 pc, in vec3 pn){ - return lp+lv*(dot(pn,pc-lp)/dot(pn,lv)); -} -""".trimIndent() - -val shaderVSM = """ -|// -- shaderVSM -|float linstep(float min, float max, float v) -|{ -| return clamp((v - min) / (max - min), 0.0, 1.0); -|} -|// https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch08.html -|float chebyshevUpperBound(vec2 moments, float t, float minVariance) { -| // One-tailed inequality valid if t > Moments.x -| float p = (t <= moments.x) ? 1.0 : 0.0; -| // Compute variance. -| float variance = moments.y - (moments.x * moments.x); -| variance = max(variance, minVariance); -| // Compute probabilistic upper bound. -| float d = t - moments.x; -| float p_max = variance / (variance + d*d); -| p_max = smoothstep(0.6, 1.0, p_max); -| return max(p, p_max); -} -""".trimIndent() - -/* -N - world space normal -V - eye - world vertex position -L - world light pos - world vertex position - */ -val shaderGGX = """ -// -- shaderGGX -#define bias 0.125 -#define HASHSCALE 443.8975 -vec2 hash22(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); - p3 += dot(p3, p3.yzx+19.19); - return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); -} - -#define PI 3.1415926535 - -float pow5(float x) { - float x2 = x * x; - return x2 * x2 * x; -} - -float D_GGX(float linearRoughness, float NoH, const vec3 h) { - // Walter et al. 2007, "Microfacet Models for Refraction through Rough Surfaces" - float oneMinusNoHSquared = 1.0 - NoH * NoH; - float a = NoH * linearRoughness; - float k = linearRoughness / (oneMinusNoHSquared + a * a); - float d = k * k * (1.0 / PI); - return d; -} - -float D_GGXm(float linearRoughness, float NoH, const vec3 h, const vec3 n) { - vec3 NxH = cross(n, h); - float oneMinusNoHSquared = dot(NxH, NxH); - - - // Walter et al. 2007, "Microfacet Models for Refraction through Rough Surfaces" - //float oneMinusNoHSquared = 1.0 - NoH * NoH; - float a = NoH * linearRoughness; - float k = linearRoughness / (oneMinusNoHSquared + a * a); - float d = k * k * (1.0 / PI); - return d; -} - - -float V_SmithGGXCorrelated(float linearRoughness, float NoV, float NoL) { - // Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs" - float a2 = linearRoughness * linearRoughness; - float GGXV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2); - float GGXL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2); - return 0.5 / (GGXV + GGXL); -} - -vec3 F_Schlick(const vec3 f0, float VoH) { - // Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering" - return f0 + (vec3(1.0) - f0) * pow5(1.0 - VoH); -} -vec3 F_SchlickRoughness(vec3 F0, float roughness, float VoH) -{ - return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - VoH, 5.0); -} - -float F_Schlick(float f0, float f90, float VoH) { - return f0 + (f90 - f0) * pow5(1.0 - VoH); -} - -float Fd_Burley(float linearRoughness, float NoV, float NoL, float LoH) { - // Burley 2012, "Physically-Based Shading at Disney" - float f90 = 0.5 + 2.0 * linearRoughness * LoH * LoH; - float lightScatter = F_Schlick(1.0, f90, NoL); - float viewScatter = F_Schlick(1.0, f90, NoV); - return lightScatter * viewScatter * (1.0 / PI); -} - -vec2 PrefilteredDFG_Karis(float roughness, float NoV) { - //https://www.shadertoy.com/view/XlKSDR - // Karis 2014, "Physically Based Material on Mobile" - const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); - const vec4 c1 = vec4( 1.0, 0.0425, 1.040, -0.040); - - vec4 r = roughness * c0 + c1; - float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y; - return vec2(-1.04, 1.04) * a004 + r.zw; -} - -float saturate(float x) { - return clamp(x, 0.0, 1.0); -} - -float G1V(float dotNV, float k) -{ - return 1.0f/(dotNV*(1.0f-k)+k); -} - -float ggx(vec3 N, vec3 V, vec3 L, float roughness, float F0) -{ - float alpha = roughness*roughness; - - vec3 H = normalize(V+L); - - float dotNL = saturate(dot(N,L)); - float dotNV = saturate(dot(N,V)); - float dotNH = saturate(dot(N,H)); - float dotLH = saturate(dot(L,H)); - - float F, D, vis; - - // D - float alphaSqr = alpha*alpha; - float pi = 3.14159f; - float denom = dotNH * dotNH *(alphaSqr-1.0) + 1.0f; - D = alphaSqr/(pi * denom * denom); - - // F - float dotLH5 = pow(1.0f-dotLH,5.0); - F = F0 + (1.0-F0)*(dotLH5); - - // V - float k = alpha/2.0f; - vis = G1V(dotNL,k)*G1V(dotNV,k); - - float specular = dotNL * D * F * vis; - return specular; -} -""".trimIndent() \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/Shadows.kt b/orx-jvm/orx-dnk3/src/main/kotlin/Shadows.kt deleted file mode 100644 index 35fedf98..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/Shadows.kt +++ /dev/null @@ -1,99 +0,0 @@ -package org.openrndr.extra.dnk3 - -import org.openrndr.draw.RenderTarget -import org.openrndr.math.Matrix44 - -sealed class Shadows { - object None : Shadows() - abstract class MappedShadows(val mapSize: Int) : Shadows() - abstract class DepthMappedShadows(mapSize: Int) : MappedShadows(mapSize) - abstract class ColorMappedShadows(mapSize: Int) : MappedShadows(mapSize) - class Simple(mapSize: Int = 1024) : DepthMappedShadows(mapSize) - class PCF(mapSize: Int = 1024, val sampleCount: Int = 12) : DepthMappedShadows(mapSize) - class VSM(mapSize: Int = 1024) : ColorMappedShadows(mapSize) -} - -interface ShadowLight { - var shadows: Shadows - fun projection(renderTarget: RenderTarget): Matrix44 - fun view(node: SceneNode): Matrix44 { - return node.worldTransform.inversed - } -} - -// shaders - -fun Shadows.VSM.fs(index: Int) : String = """ -|{ -| vec4 smc = (p_lightTransform$index * vec4(v_worldPosition,1.0)); -| vec3 lightProj = (smc.xyz/smc.w) * 0.5 + 0.5; -| if (lightProj.x > 0.0 && lightProj.x < 1.0 && lightProj.y > 0.0 && lightProj.y < 1.0) { -| vec2 moments = texture(p_lightShadowMap$index, lightProj.xy).xy; -| attenuation *= (chebyshevUpperBound(moments, length(Lr), 50.0)); -| } -|} -""".trimMargin() - -fun Shadows.Simple.fs(index: Int): String = """ -|{ -| vec4 smc = (p_lightTransform$index * vec4(v_worldPosition,1.0)); -| vec3 lightProj = (smc.xyz/smc.w) * 0.5 + 0.5; -| if (lightProj.x > 0.0 && lightProj.x < 1.0 && lightProj.y > 0.0 && lightProj.y < 1.0) { -| vec3 smz = texture(p_lightShadowMap$index, lightProj.xy).rgb; -| vec2 step = 1.0 / vec2(textureSize(p_lightShadowMap$index,0)); -| float result = 0.0; -| float compToZ = (lightProj.z- 0.0020 * tan(acos(NoL))) - 0.0003; -| float currentDepth = lightProj.z; -| float closestDepth = smz.x; -| float shadow = (currentDepth - 0.0020 * tan(acos(NoL))) - 0.0003 >= closestDepth ? 0.0 : 1.0; -| attenuation *= shadow; -| } -|} -""".trimMargin() - -fun Shadows.PCF.fs(index: Int): String = """ -|{ -| float lrl = length(Lr)/100.0; -| vec2 fTaps_Poisson[12]; -| fTaps_Poisson[0] = vec2(-.326,-.406); -| fTaps_Poisson[1] = vec2(-.840,-.074); -| fTaps_Poisson[2] = vec2(-.696, .457); -| fTaps_Poisson[3] = vec2(-.203, .621); -| fTaps_Poisson[4] = vec2( .962,-.195); -| fTaps_Poisson[5] = vec2( .473,-.480); -| fTaps_Poisson[6] = vec2( .519, .767); -| fTaps_Poisson[7] = vec2( .185,-.893); -| fTaps_Poisson[8] = vec2( .507, .064); -| fTaps_Poisson[9] = vec2( .896, .412); -| fTaps_Poisson[10] = vec2(-.322,-.933); -| fTaps_Poisson[11] = vec2(-.792,-.598); -| vec4 smc = (p_lightTransform$index * vec4(v_worldPosition,1.0)); -| vec3 lightProj = (smc.xyz/smc.w) * 0.5 + 0.5; -| if (lightProj.x > 0.0 && lightProj.x < 1.0 && lightProj.y > 0.0 && lightProj.y < 1.0) { -| vec3 smz = texture(p_lightShadowMap$index, lightProj.xy).rgb; -| vec2 stepSize = 1.0 / vec2(textureSize(p_lightShadowMap$index,0)); -| float result = 0.0; -| float compToZ = (lightProj.z- 0.0020 * tan(acos(NoL))) - 0.0003; -| float noise = hash22(lightProj.xy*10.0).x; -| float r = noise * 3.1415926535 * 2.0; -| mat2 rot = mat2( vec2(cos(r), -sin(r)), vec2(sin(r),cos(r))); -| for (int i = 0; i < 12; ++i) { -| float depth = texture(p_lightShadowMap$index, lightProj.xy + rot*fTaps_Poisson[i]*float(i)*lrl*stepSize ).r; -| result += step(compToZ, depth); -| } -| result /= 12.0; -| float currentDepth = lightProj.z; -| float closestDepth = smz.x; -| float shadow = result;// (currentDepth - 0.0020 * tan(acos(NoL))) - 0.0003 >= closestDepth ? 0.0 : 1.0; -| attenuation *= shadow; -| } -|} -""".trimMargin() - -fun Shadows.fs(index: Int): String = when (this) { - is Shadows.PCF -> this.fs(index) - is Shadows.Simple -> this.fs(index) - is Shadows.VSM -> this.fs(index) - is Shadows.None -> "" - else -> TODO() -} diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/cubemap/CubemapFilter.kt b/orx-jvm/orx-dnk3/src/main/kotlin/cubemap/CubemapFilter.kt deleted file mode 100644 index d08ad740..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/cubemap/CubemapFilter.kt +++ /dev/null @@ -1,189 +0,0 @@ -package org.openrndr.extra.dnk3.cubemap - -import org.openrndr.draw.* - -import org.openrndr.color.ColorRGBa -import org.openrndr.internal.Driver -import org.openrndr.math.* -import org.openrndr.math.transforms.ortho - -private val filterDrawStyle = DrawStyle().apply { - blendMode = BlendMode.REPLACE - depthWrite = false - depthTestPass = DepthTestPass.ALWAYS - stencil.stencilTest = StencilTest.DISABLED -} - -private var filterQuad: VertexBuffer? = null -private var filterQuadFormat = vertexFormat { - position(2) - textureCoordinate(2) -} - - -/** - * Filter base class. Renders "full-screen" quads. - */ -open class CubemapFilter(private val shader: Shader? = null) { - - /** - * parameter map - */ - val parameters = mutableMapOf() - var padding = 0 - - var depthBufferOut: DepthBuffer? = null - - companion object { - val filterVertexCode: String get() = Driver.instance.internalShaderResource("filter.vert") - } - - - - - open fun apply(source: Array, target: Array) { - if (target.isEmpty()) { - return - } - - for (side in CubemapSide.values()) { - val renderTarget = renderTarget(target[0].width, target[0].width, 1.0) {} - - shader?.begin() - shader?.uniform("sideNormal", side.forward) - shader?.uniform("sideUp", side.up) - shader?.uniform("sideRight", (side.forward cross side.up)) - shader?.end() - - - target.forEach { - renderTarget.attach(it, side, 0) - } - - for (i in 1 until target.size) { - renderTarget.blendMode(i, BlendMode.REPLACE) - } - - apply(source, renderTarget) - depthBufferOut?.let { - renderTarget.attach(it) - } - - if (depthBufferOut != null) { - renderTarget.detachDepthBuffer() - } - - renderTarget.detachColorAttachments() - renderTarget.destroy() - } - } - - fun apply(source: Array, target: RenderTarget) { - if (shader == null) { - return - } - target.bind() - - if (filterQuad == null) { - val fq = VertexBuffer.createDynamic(filterQuadFormat, 6, Session.root) - - fq.shadow.writer().apply { - write(Vector2(0.0, 1.0)); write(Vector2(0.0, 0.0)) - write(Vector2(0.0, 0.0)); write(Vector2(0.0, 1.0)) - write(Vector2(1.0, 0.0)); write(Vector2(1.0, 1.0)) - - write(Vector2(0.0, 1.0)); write(Vector2(0.0, 0.0)) - write(Vector2(1.0, 1.0)); write(Vector2(1.0, 0.0)) - write(Vector2(1.0, 0.0)); write(Vector2(1.0, 1.0)) - } - fq.shadow.upload() - fq.shadow.destroy() - filterQuad = fq - } - - shader.begin() - - source.forEachIndexed { index, cubemap -> - cubemap.bind(index) - cubemap.filter(MinifyingFilter.LINEAR, MagnifyingFilter.LINEAR) - shader.uniform("tex$index", index) - } - - Driver.instance.setState(filterDrawStyle) - - shader.uniform("projectionMatrix", ortho(0.0, target.width.toDouble(), target.height.toDouble(), 0.0, -1.0, 1.0)) - shader.uniform("targetSize", Vector2(target.width.toDouble(), target.height.toDouble())) - shader.uniform("padding", Vector2(padding.toDouble(), padding.toDouble())) - - var textureIndex = source.size + 0 - parameters.forEach { (uniform, value) -> - @Suppress("UNCHECKED_CAST") - when (value) { - is Boolean -> shader.uniform(uniform, value) - is Float -> shader.uniform(uniform, value) - is Double -> shader.uniform(uniform, value.toFloat()) - is Matrix44 -> shader.uniform(uniform, value) - is Vector2 -> shader.uniform(uniform, value) - is Vector3 -> shader.uniform(uniform, value) - is Vector4 -> shader.uniform(uniform, value) - is ColorRGBa -> shader.uniform(uniform, value) - is Int -> shader.uniform(uniform, value) - is Matrix55 -> shader.uniform(uniform, value.floatArray) - is FloatArray -> shader.uniform(uniform, value) - - // EJ: this is not so nice but I have no other ideas for this - is Array<*> -> if (value.size > 0) when (value[0]) { - is Vector2 -> shader.uniform(uniform, value as Array) - is Vector3 -> shader.uniform(uniform, value as Array) - is Vector4 -> shader.uniform(uniform, value as Array) - else -> throw IllegalArgumentException("unsupported array value: ${value[0]!!::class.java}") - //is ColorRGBa -> shader.uniform(uniform, value as Array) - } - - is DepthBuffer -> { - shader.uniform("$uniform", textureIndex) - value.bind(textureIndex) - textureIndex++ - } - - is ColorBuffer -> { - shader.uniform("$uniform", textureIndex) - value.bind(textureIndex) - textureIndex++ - } - - is Cubemap -> { - shader.uniform("$uniform", textureIndex) - value.bind(textureIndex) - textureIndex++ - } - - is ArrayTexture -> { - shader.uniform("$uniform", textureIndex) - value.bind(textureIndex) - textureIndex++ - } - - is BufferTexture -> { - shader.uniform("$uniform", textureIndex) - value.bind(textureIndex) - textureIndex++ - } - } - } - - Driver.instance.drawVertexBuffer(shader, listOf(filterQuad!!), DrawPrimitive.TRIANGLES, 0, 6) - shader.end() - target.unbind() - } - - fun apply(source: Cubemap, target: Cubemap) = apply(arrayOf(source), arrayOf(target)) - fun apply(source: Cubemap, target: Array) = apply(arrayOf(source), target) - fun apply(source: Array, target: Cubemap) = apply(source, arrayOf(target)) - - fun untrack() { - shader?.let { Session.active.untrack(shader) } - } - - protected val format get() = filterQuadFormat -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/cubemap/CubemapPassthrough.kt b/orx-jvm/orx-dnk3/src/main/kotlin/cubemap/CubemapPassthrough.kt deleted file mode 100644 index cc9f5299..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/cubemap/CubemapPassthrough.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.openrndr.extra.dnk3.cubemap - -import org.openrndr.draw.filterShaderFromUrl -import org.openrndr.resourceUrl - -class CubemapPassthrough : CubemapFilter(filterShaderFromUrl(resourceUrl("/shaders/cubemap-filters/cubemap-passthrough.frag"))) diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/cubemap/IrradianceConvolution.kt b/orx-jvm/orx-dnk3/src/main/kotlin/cubemap/IrradianceConvolution.kt deleted file mode 100644 index 325de7d2..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/cubemap/IrradianceConvolution.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.openrndr.extra.dnk3.cubemap - -import org.openrndr.draw.filterShaderFromUrl -import org.openrndr.resourceUrl - -class IrradianceConvolution : CubemapFilter(filterShaderFromUrl(resourceUrl("/shaders/cubemap-filters/irradiance-convolution.frag"))) diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/cubemap/SphericalHarmonics.kt b/orx-jvm/orx-dnk3/src/main/kotlin/cubemap/SphericalHarmonics.kt deleted file mode 100644 index 788fe297..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/cubemap/SphericalHarmonics.kt +++ /dev/null @@ -1,187 +0,0 @@ -@file:ShaderPhrases([]) - -package org.openrndr.extra.dnk3.cubemap - -import org.openrndr.draw.* -import org.openrndr.extra.shaderphrases.annotations.ShaderPhrases -import org.openrndr.extra.shaderphrases.phraseResource -import org.openrndr.math.Vector3 -import org.openrndr.math.max -import org.openrndr.resourceUrl -import java.nio.ByteBuffer -import java.nio.ByteOrder -import kotlin.math.sqrt - -class SphericalHarmonics : Filter(filterShaderFromUrl(resourceUrl("/shaders/cubemap-filters/spherical-harmonics.frag"))) { - var input: Cubemap by parameters -} - -/** based on https://andrew-pham.blog/2019/08/26/spherical-harmonics/ */ -fun Cubemap.irradianceCoefficients(): Array { - val cubemap = this - require(cubemap.format == ColorFormat.RGB) - require(cubemap.type == ColorType.FLOAT32) - - val result = Array(9) { Vector3.ZERO } - - var buffer = ByteBuffer.allocateDirect(cubemap.width * cubemap.width * cubemap.format.componentCount * cubemap.type.componentSize) - buffer.order(ByteOrder.nativeOrder()) - - var weightSum = 0.0 - - for (side in CubemapSide.values()) { - //cubemap.side(side).read(buffer) - buffer.rewind() - cubemap.read(side, buffer) - - buffer.rewind() - for (y in 0 until cubemap.width) { - for (x in 0 until cubemap.width) { - val rf = buffer.float.toDouble() - val gf = buffer.float.toDouble() - val bf = buffer.float.toDouble() - - val L = Vector3(rf, gf, bf) - - var u = (x + 0.5) / cubemap.width; - var v = (y + 0.5) / cubemap.width; - u = u * 2.0 - 1.0 - v = v * 2.0 - 1.0 - - val temp = 1.0 + u * u + v * v - val weight = 4.0 / (sqrt(temp) * temp) - - val N = cubemap.mapUVSToN(u, v, side) - val coefficients = genLightingCoefficientsForNormal(N, L) - - for (i in 0 until 9) { - result[i] += coefficients[i] * weight - } - weightSum += weight - } - } - } - - for (i in 0 until 9) { - result[i] = result[i] * (4.0 * Math.PI) / weightSum - } - - return result; -} - -fun genSHCoefficients(N: Vector3): DoubleArray { - val result = DoubleArray(9) - - // Band 0 - result[0] = 0.282095; - - // Band 1 - result[1] = 0.488603 * N.y - result[2] = 0.488603 * N.z - result[3] = 0.488603 * N.x - - // Band 2 - result[4] = 1.092548 * N.x * N.y - result[5] = 1.092548 * N.y * N.z - result[6] = 0.315392 * (3.0 * N.z * N.z - 1.0) - result[7] = 1.092548 * N.x * N.z - result[8] = 0.546274 * (N.x * N.x - N.y * N.y) - - return result; -} - - -fun genLightingCoefficientsForNormal(N: Vector3, L: Vector3): Array { - val coefficients = genSHCoefficients(N) - val result = Array(9) { Vector3.ZERO } - for (i in 0 until 9) { - result[i] = L * coefficients[i] - } - return result -} - -fun Cubemap.mapUVSToN(u: Double, v: Double, side: CubemapSide): Vector3 { - return (side.right * u + side.up * v + side.forward).normalized -} - - -// Evaluates the irradiance perceived in the provided direction -// Analytic method from http://www1.cs.columbia.edu/~ravir/papers/envmap/envmap.pdf eq. 13 -// -fun evaluateSHIrradiance(direction: Vector3, _SH: Array): Vector3 { - val c1 = 0.42904276540489171563379376569857; // 4 * Â2.Y22 = 1/4 * sqrt(15.PI) - val c2 = 0.51166335397324424423977581244463; // 0.5 * Â1.Y10 = 1/2 * sqrt(PI/3) - val c3 = 0.24770795610037568833406429782001; // Â2.Y20 = 1/16 * sqrt(5.PI) - val c4 = 0.88622692545275801364908374167057; // Â0.Y00 = 1/2 * sqrt(PI) - - val x = direction.x; - val y = direction.y; - val z = direction.z; - - return max(Vector3.ZERO, - _SH[8] * (c1 * (x * x - y * y)) // c1.L22.(x²-y²) - + _SH[6] * (c3 * (3.0 * z * z - 1)) // c3.L20.(3.z² - 1) - + _SH[0] * c4 // c4.L00 - + (_SH[4] * x * y + _SH[7] * x * z + _SH[5] * y * z) * 2.0 * c1 // 2.c1.(L2-2.xy + L21.xz + L2-1.yz) - + (_SH[3] * x + _SH[1] * y + _SH[2] * z) * c2 * 2.0); // 2.c2.(L11.x + L1-1.y + L10.z) -} - -val glslEvaluateSH: String by phraseResource("/phrases/irradiance-sh/evaluate-sh.frag") - -val glslFetchSH: String by phraseResource("/phrases/irradiance-sh/fetch-sh.frag") -val glslFetchSH0: String by phraseResource("/phrases/irradiance-sh/fetch-sh0.frag") - -fun genGlslGatherSH(xProbes: Int, yProbes: Int, zProbes: Int, spacing: Double = 1.0, offset: Vector3) = """ -ivec3 gridCoordinates(vec3 p, out vec3 f) { - float x = (p.x - ${offset.x}) / $spacing; - float y = (p.y - ${offset.y})/ $spacing; - float z = (p.z - ${offset.z}) / $spacing; - - int ix = int(floor(x)) + $xProbes / 2; - int iy = int(floor(y)) + $yProbes / 2; - int iz = int(floor(z)) + $zProbes / 2; - - f.x = fract((x)); - f.y = fract((y)); - f.z = fract((z)); - - return ivec3(ix, iy, iz); -} - -int gridIndex(ivec3 p) { - ivec3 c = clamp(p, ivec3(0), ivec3(${xProbes - 1}, ${yProbes - 1}, ${zProbes - 1})); - return c.x + c.y * $xProbes + c.z * ${xProbes * yProbes}; -} - -void gatherSH(samplerBuffer btex, vec3 p, out vec3[9] blend) { - vec3[9] c000; - vec3[9] c001; - vec3[9] c010; - vec3[9] c011; - vec3[9] c100; - vec3[9] c101; - vec3[9] c110; - vec3[9] c111; - - vec3 f; - ivec3 io = gridCoordinates(p, f); - - fetchSH(btex, gridIndex(io + ivec3(0,0,0)), c000); - fetchSH(btex, gridIndex(io + ivec3(0,0,1)), c001); - fetchSH(btex, gridIndex(io + ivec3(0,1,0)), c010); - fetchSH(btex, gridIndex(io + ivec3(0,1,1)), c011); - fetchSH(btex, gridIndex(io + ivec3(1,0,0)), c100); - fetchSH(btex, gridIndex(io + ivec3(1,0,1)), c101); - fetchSH(btex, gridIndex(io + ivec3(1,1,0)), c110); - fetchSH(btex, gridIndex(io + ivec3(1,1,1)), c111); - - for (int i = 0; i < 9; ++i) { - blend[i] = mix( mix( mix(c000[i], c001[i], f.z), mix(c010[i], c011[i], f.z), f.y), mix( mix(c100[i], c101[i], f.z), mix(c110[i], c111[i], f.z), f.y), f.x); - } -} -""".trimIndent() - -val glslGridCoordinates: String by phraseResource("/phrases/irradiance-sh/grid-coordinates.frag") -val glslGridIndex: String by phraseResource("/phrases/irradiance-sh/grid-index.frag") -val glslGatherSH: String by phraseResource("/phrases/irradiance-sh/gather-sh.frag") -val glslGatherSH0: String by phraseResource("/phrases/irradiance-sh/gather-sh0.frag") \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/dsl/PBRMaterialBuilder.kt b/orx-jvm/orx-dnk3/src/main/kotlin/dsl/PBRMaterialBuilder.kt deleted file mode 100644 index e128ae94..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/dsl/PBRMaterialBuilder.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.openrndr.extra.dnk3.dsl - -import org.openrndr.extra.dnk3.PBRMaterial - -fun pbrMaterial(builder: PBRMaterial.() -> Unit): PBRMaterial { - return PBRMaterial().apply { builder() } -} - -fun test() { - pbrMaterial { - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/dsl/SceneBuilder.kt b/orx-jvm/orx-dnk3/src/main/kotlin/dsl/SceneBuilder.kt deleted file mode 100644 index 26b966c5..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/dsl/SceneBuilder.kt +++ /dev/null @@ -1,88 +0,0 @@ -package org.openrndr.extra.dnk3.dsl - -import kotlinx.coroutines.yield -import org.openrndr.draw.DrawPrimitive -import org.openrndr.draw.VertexBuffer -import org.openrndr.extra.dnk3.* -import org.openrndr.launch - -fun scene(builder: Scene.() -> Unit): Scene { - val scene = Scene() - scene.builder() - return scene -} - -fun SceneNode.node(builder: SceneNode.() -> Unit): SceneNode { - val node = SceneNode() - node.builder() - children.add(node) - return node -} - -fun SceneNode.hemisphereLight(builder: HemisphereLight.() -> Unit): HemisphereLight { - val hemisphereLight = HemisphereLight() - hemisphereLight.builder() - entities.add(hemisphereLight) - return hemisphereLight -} - -fun SceneNode.directionalLight(buider: DirectionalLight.() -> Unit): DirectionalLight { - val directionalLight = DirectionalLight() - directionalLight.buider() - this.entities.add(directionalLight) - return directionalLight -} - -fun SceneNode.pointLight(builder: PointLight.() -> Unit): PointLight { - val pointLight = PointLight() - pointLight.builder() - this.entities.add(pointLight) - return pointLight -} - -fun SceneNode.spotLight(builder: SpotLight.() -> Unit): SpotLight { - val spotLight = SpotLight() - spotLight.builder() - this.entities.add(spotLight) - return spotLight -} - -class SimpleMeshBuilder { - var vertexBuffer: VertexBuffer? = null - var primitive = DrawPrimitive.TRIANGLES - var material: Material? = null - fun build(): Mesh { - val geometry = Geometry( - listOf(vertexBuffer ?: error("no vertex buffer")), - null, - primitive, - 0, - vertexBuffer?.vertexCount ?: error("no vertex buffer") - ) - val primitive = MeshPrimitive(geometry, material ?: error("no material")) - return Mesh(listOf(primitive)) - } -} - -fun SceneNode.simpleMesh(builder: SimpleMeshBuilder.() -> Unit): Mesh { - val mesh = SimpleMeshBuilder().apply { builder() }.build() - entities.add(mesh) - return mesh -} - - -fun SceneNode.pathMesh(builder: PathMesh.() -> Unit): PathMesh { - val pathMesh = PathMesh(mutableListOf(), DummyMaterial(), 1.0) - pathMesh.builder() - entities.add(pathMesh) - return pathMesh -} - -fun Scene.update(function: () -> Unit) { - dispatcher.launch { - while (true) { - function() - yield() - } - } -} diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/features/IrradianceSH.kt b/orx-jvm/orx-dnk3/src/main/kotlin/features/IrradianceSH.kt deleted file mode 100644 index bfc37b58..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/features/IrradianceSH.kt +++ /dev/null @@ -1,112 +0,0 @@ -package org.openrndr.extra.dnk3.features - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.dnk3.* -import org.openrndr.extra.dnk3.cubemap.irradianceCoefficients -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector3 -import org.openrndr.math.transforms.transform -import java.io.File -import java.nio.ByteBuffer -import java.nio.ByteOrder - -data class IrradianceSH(val xCount: Int, val yCount: Int, val zCount: Int, val spacing: Double, val offset: Vector3, val cubemapSize: Int) : Feature { - override fun update(drawer: Drawer, sceneRenderer: SceneRenderer, scene: Scene, feature: T, context: RenderContext) { - sceneRenderer.processIrradiance(drawer, scene, feature as IrradianceSH, context) - } - - var shMap: BufferTexture? = null - val probeCount - get() = xCount * yCount * zCount - -} - -fun Scene.addIrradianceSH(xCount: Int, - yCount: Int, - zCount: Int, - spacing: Double, - offset: Vector3 = Vector3.ZERO, - cubemapSize: Int = 256 -) { - features.add(IrradianceSH(xCount * 2 + 1, yCount * 2 + 1, zCount * 2 + 1, spacing, offset, cubemapSize)) - var probeID = 0 - for (k in -zCount..zCount) { - for (j in -yCount..yCount) { - for (i in -xCount..xCount) { - val probeNode = SceneNode() - probeNode.transform = transform { - translate(offset) - translate(i * spacing, j * spacing, k * spacing) - } - probeNode.entities.add(IrradianceProbe()) - probeID++ - root.children.add(probeNode) - } - } - } -} - -private fun SceneRenderer.processIrradiance(drawer: Drawer, scene: Scene, feature: IrradianceSH, context: RenderContext) { - val irradianceProbes = scene.root.findContent { this as? IrradianceProbe } - val irradianceProbePositions = irradianceProbes.map { it.node.worldPosition } - - if (feature.shMap == null && irradianceProbes.isNotEmpty()) { - val hash = scene.hash() - val cached = File("data/scene-cache/sh-$hash.orb") - if (cached.exists()) { - feature.shMap = loadBufferTexture(cached) - } else { - var probeID = 0 - val tempCubemap = cubemap(feature.cubemapSize, format = ColorFormat.RGB, type = ColorType.FLOAT32) - var cubemapDepthBuffer = depthBuffer(feature.cubemapSize, feature.cubemapSize, DepthFormat.DEPTH16, BufferMultisample.Disabled) - - feature.shMap = bufferTexture(irradianceProbes.size * 9, format = ColorFormat.RGB, type = ColorType.FLOAT32) - val buffer = ByteBuffer.allocateDirect(irradianceProbePositions.size * 9 * 3 * 4) - buffer.order(ByteOrder.nativeOrder()) - - for ((node, probe) in irradianceProbes) { - if (probe.dirty) { - val pass = IrradianceProbePass - val materialContext = MaterialContext(pass, context.lights, emptyList(), shadowLightTargets, emptyMap(), 0) - val position = node.worldPosition - - for (side in CubemapSide.values()) { - val target = renderTarget(feature.cubemapSize, feature.cubemapSize) { - //this.colorBuffer(tempCubemap.side(side)) - this.cubemap(tempCubemap, side) - this.depthBuffer(cubemapDepthBuffer) - } - drawer.isolatedWithTarget(target) { - drawer.clear(ColorRGBa.BLACK) - drawer.projection = probe.projectionMatrix - drawer.view = Matrix44.IDENTITY - drawer.model = Matrix44.IDENTITY - drawer.lookAt(position, position + side.forward, side.up) - drawPass(drawer, pass, materialContext, context) - } - - target.detachDepthBuffer() - target.detachColorAttachments() - target.destroy() - } - val coefficients = tempCubemap.irradianceCoefficients() - for (coef in coefficients) { - buffer.putVector3((coef)) - } - probeID++ - //println("$probeID / ${irradianceProbePositions.size}") - probe.dirty = false - } - } - feature.shMap?.let { - buffer.rewind() - it.write(buffer) - val f = File("data/scene-cache/sh-$hash.orb") - if (f.canWrite()) { - it.saveToFile(f) - } - } - } - } -} diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/features/VoxelConeTracing.kt b/orx-jvm/orx-dnk3/src/main/kotlin/features/VoxelConeTracing.kt deleted file mode 100644 index 7c1ad8fc..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/features/VoxelConeTracing.kt +++ /dev/null @@ -1,71 +0,0 @@ -package org.openrndr.extra.dnk3.features - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.dnk3.* -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector3 - -data class VoxelConeTracing(val xCount: Int, val yCount: Int, val zCount: Int, val spacing: Double, val offset: Vector3) : Feature { - var voxelMap: VolumeTexture? = null - var voxelRenderTarget = null as? RenderTarget? - override fun update(drawer: Drawer, sceneRenderer: SceneRenderer, scene: Scene, feature: T, context: RenderContext) { - sceneRenderer.processVoxelConeTracing(drawer, scene, this, context) - } - - var initialized = false - val voxelPass = RenderPass(listOf(VoxelFacet(this)), renderOpaque = true, renderTransparent = false, depthWrite = false, skipTarget = true) -} - -fun Scene.addVoxelConeTracing(xCount: Int, yCount: Int, zCount: Int, spacing: Double, offset: Vector3 = Vector3.ZERO) : VoxelConeTracing { - val feature = VoxelConeTracing(xCount, yCount, zCount, spacing, offset) - features.add(feature) - return feature -} - -class VoxelFacet(val voxelConeTracing: VoxelConeTracing) : ColorBufferFacetCombiner(setOf(FacetType.DIFFUSE, FacetType.SPECULAR, FacetType.EMISSIVE), "color", ColorFormat.RGBa, ColorType.FLOAT16) { - override fun generateShader() = """ - vec3 finalColor = (max(vec3(0.0), f_diffuse.rgb) + max(vec3(0.0), f_emission.rgb) + max(vec3(0.0), f_ambient.rgb)); - vec3 p = v_worldPosition; - { - float x = (p.x - ${voxelConeTracing.offset.x}) / ${voxelConeTracing.spacing}; - float y = (p.y - ${voxelConeTracing.offset.y}) / ${voxelConeTracing.spacing}; - float z = (p.z - ${voxelConeTracing.offset.z}) / ${voxelConeTracing.spacing}; - - int ix = int(floor(x+0.5)) + ${voxelConeTracing.xCount} / 2; - int iy = int(floor(y+0.5)) + ${voxelConeTracing.yCount} / 2; - int iz = int(floor(z+0.5)) + ${voxelConeTracing.zCount} / 2; - imageStore(p_voxelMap, ivec3(ix, iy, iz), vec4(finalColor, 1.0)); - } - """ -} - -private fun SceneRenderer.processVoxelConeTracing(drawer: Drawer, scene: Scene, feature: VoxelConeTracing, context: RenderContext) { - if (feature.voxelMap == null) { - feature.voxelMap = volumeTexture(feature.xCount * 2 + 1, feature.yCount * 2 + 1, feature.zCount * 2 + 1, format = ColorFormat.RGBa, type = ColorType.FLOAT16) - } - if (feature.voxelRenderTarget == null) { - feature.voxelRenderTarget = renderTarget(2048, 2048, 1.0, BufferMultisample.SampleCount(8)) { - colorBuffer() - } - } - if (!feature.initialized) { - println("drawing voxelmap") - for (side in CubemapSide.values()) { - drawer.isolatedWithTarget(feature.voxelRenderTarget ?: error("no render target")) { - val pass = feature.voxelPass - val materialContext = MaterialContext(pass, context.lights, emptyList(), shadowLightTargets, emptyMap(), 0) - drawer.clear(ColorRGBa.BLACK) - drawer.ortho(-10.0, 10.0, -10.0, 10.0, -40.0, 40.0) - drawer.view = Matrix44.IDENTITY - drawer.model = Matrix44.IDENTITY - val position = Vector3.ZERO - drawer.lookAt(position + side.forward*40.0, position , side.up) - drawPass(drawer, pass, materialContext, context) { - it.image("voxelMap", feature.voxelMap!!.imageBinding(0, ImageAccess.WRITE)) - } - } - } - feature.initialized = true - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/gltf/Glb.kt b/orx-jvm/orx-dnk3/src/main/kotlin/gltf/Glb.kt deleted file mode 100644 index 599ca7ef..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/gltf/Glb.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.openrndr.extra.dnk3.gltf - -import kotlinx.serialization.json.Json -import java.io.File -import java.io.RandomAccessFile -import java.nio.ByteBuffer -import java.nio.ByteOrder - -fun loadGltfFromGlbFile(file: File): GltfFile { - val channel = RandomAccessFile(file, "r").channel - val headerBuffer = ByteBuffer.allocate(12).order(ByteOrder.nativeOrder()) - - headerBuffer.rewind() - channel.read(headerBuffer) - headerBuffer.rewind() - - val magic = headerBuffer.int - val version = headerBuffer.int - val length = headerBuffer.int - - fun readChunk(): ByteBuffer { - val chunkHeader = ByteBuffer.allocate(8).order(ByteOrder.nativeOrder()) - channel.read(chunkHeader) - chunkHeader.rewind() - val chunkLength = chunkHeader.int - val chunkType = chunkHeader.int - val chunkBuffer = - if (chunkType == 0x004E4942) ByteBuffer.allocateDirect(chunkLength) else ByteBuffer.allocate(chunkLength) - (chunkBuffer as ByteBuffer) - channel.read(chunkBuffer) - chunkBuffer.order(ByteOrder.nativeOrder()) - return chunkBuffer - } - - val jsonBuffer = readChunk() - jsonBuffer.rewind() - val jsonByteArray = ByteArray(jsonBuffer.capacity()) - jsonBuffer.get(jsonByteArray) - val json = String(jsonByteArray) - val bufferBuffer = if (channel.position() < length) readChunk() else null - - val gltFile = Json { ignoreUnknownKeys = true }.decodeFromString(json) - gltFile.file = file - gltFile.bufferBuffer = bufferBuffer - - return gltFile -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/gltf/Gltf.kt b/orx-jvm/orx-dnk3/src/main/kotlin/gltf/Gltf.kt deleted file mode 100644 index bf1812ea..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/gltf/Gltf.kt +++ /dev/null @@ -1,438 +0,0 @@ -@file:Suppress("MemberVisibilityCanBePrivate", "unused") - -package org.openrndr.extra.dnk3.gltf - -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonBuilder -import kotlinx.serialization.json.JsonIgnoreUnknownKeys -import org.openrndr.draw.* -import java.io.File -import java.io.RandomAccessFile -import java.nio.Buffer -import java.nio.ByteBuffer -import java.nio.ByteOrder -import java.util.* -import kotlin.collections.LinkedHashMap -import kotlin.math.max - -const val GLTF_FLOAT = 5126 -const val GLTF_UNSIGNED_INT = 5125 -const val GLTF_INT = 5124 -const val GLTF_UNSIGNED_SHORT = 5123 -const val GLTF_SHORT = 5122 -const val GLTF_UNSIGNED_BYTE = 5121 -const val GLTF_BYTE = 5120 - -const val GLTF_ARRAY_BUFFER = 34962 -const val GLTF_ELEMENT_ARRAY_BUFFER = 34963 - -@Serializable -data class GltfAsset(val generator: String? = null, val version: String? = null) - -@JvmRecord -@Serializable -data class GltfScene(val nodes: IntArray, val name: String? = null) - -@JvmRecord -@Serializable -data class GltfNode( - val name: String? = null, - val children: IntArray? = null, - val matrix: DoubleArray? = null, - val scale: DoubleArray? = null, - val rotation: DoubleArray? = null, - val translation: DoubleArray? = null, - val mesh: Int? = null, - val skin: Int? = null, - val camera: Int? = null, - val extensions: GltfNodeExtensions? = null -) - -@JvmRecord -@Serializable -data class KHRLightsPunctualIndex(val light: Int) - -@JvmRecord -@Serializable -data class GltfNodeExtensions(val KHR_lights_punctual: KHRLightsPunctualIndex?) { - -} - -@Serializable -data class GltfPrimitive( - val attributes: LinkedHashMap, - val indices: Int? = null, - val mode: Int? = null, - val material: Int? = null -) { - fun createDrawCommand(gltfFile: GltfFile): GltfDrawCommand { - - val indexBuffer = indices?.let { indices -> - val accessor = gltfFile.accessors[indices] - val indexType = when (accessor.componentType) { - GLTF_UNSIGNED_SHORT -> IndexType.INT16 - GLTF_UNSIGNED_INT -> IndexType.INT32 - else -> error("unsupported index type: ${accessor.componentType}") - } - val bufferView = gltfFile.bufferViews[accessor.bufferView] - val buffer = gltfFile.buffers[bufferView.buffer] - val contents = buffer.contents(gltfFile) - (contents as Buffer).limit(contents.capacity()) - (contents as Buffer).position((bufferView.byteOffset ?: 0) + (accessor.byteOffset)) - (contents as Buffer).limit( - (bufferView.byteOffset ?: 0) + (accessor.byteOffset) - + accessor.count * indexType.sizeInBytes - ) - val ib = indexBuffer(accessor.count, indexType) - ib.write(contents) - ib - } - - var maxCount = 0 - - abstract class Convertor { - abstract fun convert(buffer: ByteBuffer, offset: Int, size: Int, writer: BufferWriter) - } - - class CopyConvertor : Convertor() { - override fun convert(buffer: ByteBuffer, offset: Int, size: Int, writer: BufferWriter) { - writer.copyBuffer(buffer, offset, size) - } - } - - class Uint8ToUint32Convertor : Convertor() { - override fun convert(buffer: ByteBuffer, offset: Int, size: Int, writer: BufferWriter) { - for (i in 0 until 4) { - val ui = buffer.get(offset).toInt() - writer.write(ui) - } - } - } - - class Uint16ToUint32Convertor : Convertor() { - override fun convert(buffer: ByteBuffer, offset: Int, size: Int, writer: BufferWriter) { - for (i in 0 until 4) { - val ui = buffer.getShort(offset).toInt() - writer.write(ui) - } - } - } - - class CopyPadConvertor(val padFloats: Int) : Convertor() { - override fun convert(buffer: ByteBuffer, offset: Int, size: Int, writer: BufferWriter) { - writer.copyBuffer(buffer, offset, size) - for (i in 0 until padFloats) { - writer.write(0.0f) - } - } - } - - - val accessors = mutableListOf() - val convertors = mutableListOf() - val format = vertexFormat { - for ((name, index) in attributes.toSortedMap()) { - val accessor = gltfFile.accessors[index] - maxCount = max(accessor.count, maxCount) - when (name) { - "NORMAL" -> { - normal(3) - paddingFloat(1) - accessors.add(accessor) - convertors.add(CopyPadConvertor(1)) - } - - "POSITION" -> { - position(3) - paddingFloat(1) - accessors.add(accessor) - convertors.add(CopyPadConvertor(1)) - } - - "TANGENT" -> { - attribute("tangent", VertexElementType.VECTOR4_FLOAT32) - accessors.add(accessor) - convertors.add(CopyConvertor()) - } - - "TEXCOORD_0" -> { - val dimensions = when (accessor.type) { - "SCALAR" -> 1 - "VEC2" -> 2 - "VEC3" -> 3 - else -> error("unsupported texture coordinate type ${accessor.type}") - } - textureCoordinate(4, 0) - //paddingFloat(4 - dimensions) - accessors.add(accessor) - convertors.add(CopyPadConvertor(4 - dimensions)) - } - - "JOINTS_0" -> { - attribute("joints", VertexElementType.VECTOR4_UINT32) - accessors.add(accessor) - convertors.add( - when (Pair(accessor.type, accessor.componentType)) { - Pair("VEC4", GLTF_UNSIGNED_BYTE) -> Uint8ToUint32Convertor() - Pair("VEC4", GLTF_UNSIGNED_SHORT) -> Uint16ToUint32Convertor() - else -> error("not supported ${accessor.type} / ${accessor.componentType}") - } - ) - } - - "WEIGHTS_0" -> { - val type = when (Pair(accessor.type, accessor.componentType)) { - Pair("VEC4", GLTF_FLOAT) -> VertexElementType.VECTOR4_FLOAT32 - else -> error("not supported ${accessor.type} / ${accessor.componentType}") - } - attribute("weights", type) - accessors.add(accessor) - convertors.add(CopyConvertor()) - } - } - } - } - - val buffers = - accessors.map { it.bufferView } - .distinct() - .associate { - Pair( - gltfFile.bufferViews[it].buffer, - gltfFile.buffers[gltfFile.bufferViews[it].buffer].contents(gltfFile) - ) - } - - val vb = vertexBuffer(format, maxCount) - vb.put { - for (i in 0 until maxCount) { - for ((a, conv) in accessors zip convertors) { - val bufferView = gltfFile.bufferViews[a.bufferView] - val buffer = buffers[bufferView.buffer] ?: error("no buffer ${bufferView.buffer}") - val componentSize = when (a.componentType) { - GLTF_BYTE, GLTF_UNSIGNED_BYTE -> 1 - GLTF_SHORT, GLTF_UNSIGNED_SHORT -> 2 - GLTF_FLOAT, GLTF_UNSIGNED_INT, GLTF_INT -> 4 - else -> error("unsupported type") - } - val componentCount = when (a.type) { - "SCALAR" -> 1 - "VEC2" -> 2 - "VEC3" -> 3 - "VEC4" -> 4 - "MAT2" -> 4 - "MAT3" -> 9 - "MAT4" -> 16 - else -> error("unsupported type") - } - val size = componentCount * componentSize - val offset = (bufferView.byteOffset ?: 0) + a.byteOffset + i * (bufferView.byteStride ?: size) - conv.convert(buffer, offset, size, this) - //copyBuffer(buffer, offset, size) - } - } - } - val drawPrimitive = when (mode) { - null, 4 -> DrawPrimitive.TRIANGLES - 5 -> DrawPrimitive.TRIANGLE_STRIP - else -> error("unsupported mode $mode") - } - return GltfDrawCommand(vb, indexBuffer, drawPrimitive, indexBuffer?.indexCount ?: maxCount) - } -} -@Serializable -data class GltfMesh(val primitives: List, val name: String) { - fun createDrawCommands(gltfFile: GltfFile): List { - return primitives.map { it.createDrawCommand(gltfFile) } - } -} - -@Serializable -data class GltfPbrMetallicRoughness( - val baseColorFactor: DoubleArray? = null, - val baseColorTexture: GltfMaterialTexture? = null, - var metallicRoughnessTexture: GltfMaterialTexture? = null, - val roughnessFactor: Double? = null, - val metallicFactor: Double? = null -) - -@Serializable -data class GltfMaterialTexture(val index: Int, val scale: Double? = null, val texCoord: Int? = null) - -@Serializable -data class GltfImage(val uri: String? = null, val bufferView: Int? = null) - -@Serializable -data class GltfSampler(val magFilter: Int? = null, val minFilter: Int? = null, val wrapS: Int? = null, val wrapT: Int? = null) - -@Serializable -data class GltfTexture(val sampler: Int, val source: Int) - -@Serializable -data class GltfMaterial( - val name: String, - val alphaMode: String? = null, - val doubleSided: Boolean? = null, - val normalTexture: GltfMaterialTexture? = null, - val occlusionTexture: GltfMaterialTexture? = null, - val emissiveTexture: GltfMaterialTexture? = null, - val emissiveFactor: DoubleArray? = null, - val pbrMetallicRoughness: GltfPbrMetallicRoughness? = null, - val extensions: GltfMaterialExtensions? = null -) - -@Serializable -data class GltfMaterialExtensions( - val KHR_materials_pbrSpecularGlossiness: KhrMaterialsPbrSpecularGlossiness? -) - -@Serializable -class KhrMaterialsPbrSpecularGlossiness(val diffuseFactor: DoubleArray?, val diffuseTexture: GltfMaterialTexture?) - -@Serializable -data class GltfBufferView( - val buffer: Int, - val byteOffset: Int? = null, - val byteLength: Int, - val byteStride: Int? = null, - val target: Int? = null -) - -@Serializable -data class GltfBuffer(val byteLength: Int, val uri: String? = null) { - fun contents(gltfFile: GltfFile): ByteBuffer = if (uri != null) { - if (uri.startsWith("data:")) { - val base64 = uri.substring(uri.indexOf(",") + 1) - val decoded = Base64.getDecoder().decode(base64) - val buffer = ByteBuffer.allocateDirect(decoded.size) - buffer.order(ByteOrder.nativeOrder()) - buffer.put(decoded) - buffer.rewind() - buffer - } else { - val raf = RandomAccessFile(File(gltfFile.file.parentFile, uri), "r") - val buffer = ByteBuffer.allocateDirect(byteLength) - buffer.order(ByteOrder.nativeOrder()) - buffer.rewind() - raf.channel.read(buffer) - buffer.rewind() - buffer - } - } else { - gltfFile.bufferBuffer ?: error("no embedded buffer from glb") - } -} - -data class GltfDrawCommand( - val vertexBuffer: VertexBuffer, - val indexBuffer: IndexBuffer?, - val primitive: DrawPrimitive, - var vertexCount: Int -) - -@Serializable -data class GltfAccessor( - val bufferView: Int, - val byteOffset: Int = 0, - val componentType: Int, - val count: Int, - val max: DoubleArray? = null, - val min: DoubleArray? = null, - val type: String -) - -@Serializable -data class GltfAnimation(val name: String? = null, val channels: List, val samplers: List) - -@Serializable -data class GltfAnimationSampler(val input: Int, val interpolation: String? = null, val output: Int) - -@Serializable -data class GltfChannelTarget(val node: Int?, val path: String?) - -@Serializable -data class GltfChannel(val sampler: Int, val target: GltfChannelTarget) - -@Serializable -data class GltfSkin(val inverseBindMatrices: Int, val joints: IntArray, val skeleton: Int) - -@Serializable -data class KHRLightsPunctualLight( - val color: DoubleArray?, - val type: String, - val name: String, - val intensity: Double?, - val range: Double? = null, - val spot: KHRLightsPunctualLightSpot? = null -) - -@Serializable -data class KHRLightsPunctualLightSpot(val innerConeAngle: Double?, val outerConeAngle: Double?) - -@Serializable -data class KHRLightsPunctual(val lights: List) - -@Serializable -@JsonIgnoreUnknownKeys -data class GltfExtensions(val KHR_lights_punctual: KHRLightsPunctual? = null) - -@Serializable -data class GltfCameraPerspective(val aspectRatio: Double? = null, val yfov: Double, val zfar: Double?, val znear: Double) - -@Serializable -data class GltfCameraOrthographic(val xmag: Double, val ymag: Double, val zfar: Double, val znear: Double) - -@Serializable -data class GltfCamera( - val name: String? = null, - val type: String, - val perspective: GltfCameraPerspective? = null, - val orthographic: GltfCameraOrthographic? = null -) - -@Serializable -class GltfFile( - val asset: GltfAsset?, - val scene: Int? = null, - val scenes: List, - val nodes: List, - val meshes: List, - val accessors: List, - val materials: List, - val bufferViews: List, - val buffers: List, - val images: List? = null, - val textures: List? = null, - val samplers: List? = null, - val animations: List? = null, - val skins: List? = null, - val extensions: GltfExtensions? = null, - val extensionsUsed: List? = null, - val extensionsRequired: List? = null, - val cameras: List? = null -) { - @Transient - lateinit var file: File - - @Transient - var bufferBuffer: ByteBuffer? = null -} - -fun loadGltfFromFile(file: File): GltfFile = when (file.extension) { - "gltf" -> { - val gltfFile = Json{ - ignoreUnknownKeys = true - }.decodeFromString(file.readText()) - gltfFile.file = file - gltfFile - } - - "glb" -> { - loadGltfFromGlbFile(file) - } - - else -> error("extension ${file.extension} not supported in ${file}") -} - diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/gltf/GltfScene.kt b/orx-jvm/orx-dnk3/src/main/kotlin/gltf/GltfScene.kt deleted file mode 100644 index f16998dd..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/gltf/GltfScene.kt +++ /dev/null @@ -1,439 +0,0 @@ -package org.openrndr.extra.dnk3.gltf - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.dnk3.* -import org.openrndr.extra.keyframer.KeyframerChannelQuaternion -import org.openrndr.extra.keyframer.KeyframerChannelVector3 -import org.openrndr.math.Matrix44 -import org.openrndr.math.Quaternion -import org.openrndr.math.Vector3 -import org.openrndr.math.transforms.transform -import org.openrndr.utils.buffer.MPPBuffer -import java.io.File -import java.nio.Buffer -import java.nio.ByteOrder -import kotlin.reflect.KMutableProperty0 - -class SceneAnimation(var channels: List) { - val duration: Double - get() { - return channels.maxByOrNull { it.duration }?.duration ?: 0.0 - } - - fun applyToTargets(input: Double) { - for (channel in channels) { - channel.applyToTarget(input) - } - } -} - -sealed class AnimationChannel { - abstract val duration: Double - abstract fun applyToTarget(input: Double) -} - - -class QuaternionChannel( - val target: KMutableProperty0, - val keyframer: KeyframerChannelQuaternion -) : AnimationChannel() { - override fun applyToTarget(input: Double) { - target.set(keyframer.value(input) ?: Quaternion.IDENTITY) - } - - override val duration: Double - get() = keyframer.duration() -} - -class Vector3Channel( - val target: KMutableProperty0, - val keyframer: KeyframerChannelVector3, val default: Vector3 -) : AnimationChannel() { - override fun applyToTarget(input: Double) { - target.set(keyframer.value(input) ?: default) - } - - override val duration: Double - get() = keyframer.duration() -} - -class GltfSceneNode : SceneNode() { - var translation = Vector3.ZERO - var scale = Vector3.ONE - var rotation = Quaternion.IDENTITY - - override fun toString(): String { - return "translation: $translation, scale: $scale, rotation: $rotation, children: ${children.size}, entities: ${entities} " - } - - override var transform: Matrix44 = Matrix44.IDENTITY - get() = transform { - translate(translation) - multiply(rotation.matrix.matrix44) - scale(scale) - } * field -} - -class GltfSceneData(val scenes: List>, val animations: List) - - -/** Tools to convert GltfFile into a DNK3 scene */ -fun GltfFile.buildSceneNodes(): GltfSceneData { - val sceneImages = mutableMapOf() - fun GltfImage.createSceneImage(): ColorBuffer { - return sceneImages.getOrPut(this) { - if (uri == null) { - - bufferView?.let { bv -> - val localBufferView = bufferViews[bv] - - val localBuffer = buffers[localBufferView.buffer].contents(this@buildSceneNodes) - require(localBufferView.byteOffset != null) - localBuffer.position(localBufferView.byteOffset) - localBuffer.limit(localBufferView.byteOffset + localBufferView.byteLength) - - val cb = loadImage(MPPBuffer(localBuffer)) - cb.generateMipmaps() - cb.filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - cb.anisotropy = 10.0 - localBuffer.limit(localBuffer.capacity()) - cb - } ?: error("no uri and no bufferview") - - } else { - if (uri.startsWith("data:")) { - loadImage(uri) - } else { - loadImage(File(file.parent, uri)) - } - } - } - } - - val sceneMaterials = mutableMapOf() - fun GltfMaterial.createSceneMaterial(): Material = sceneMaterials.getOrPut(this) { - val material = PBRMaterial() - material.name = this.name - - material.doubleSided = this.doubleSided ?: false - material.transparent = this.alphaMode != null - - pbrMetallicRoughness?.let { pbr -> - material.roughness = pbr.roughnessFactor ?: 1.0 - material.metalness = pbr.metallicFactor ?: 1.0 - - material.color = ColorRGBa.WHITE - pbr.baseColorFactor?.let { - material.color = ColorRGBa(it[0], it[1], it[2], it[3]) - } - - pbr.baseColorTexture?.let { texture -> - val cb = images!![textures!![texture.index].source].createSceneImage() - cb.filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - cb.wrapU = WrapMode.REPEAT - cb.wrapV = WrapMode.REPEAT - val sceneTexture = Texture( - ModelCoordinates(texture = cb, pre = "x_texCoord.y = 1.0-x_texCoord.y;"), - TextureTarget.COLOR - ) - material.textures.add(sceneTexture) - } - pbr.metallicRoughnessTexture?.let { texture -> - val cb = images!![textures!![texture.index].source].createSceneImage() - cb.filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - cb.wrapU = WrapMode.REPEAT - cb.wrapV = WrapMode.REPEAT - val sceneTexture = Texture( - ModelCoordinates(texture = cb, pre = "x_texCoord.y = 1.0-x_texCoord.y;"), - TextureTarget.METALNESS_ROUGHNESS - ) - material.textures.add(sceneTexture) - } - } - - occlusionTexture?.let { texture -> - val cb = images!![textures!![texture.index].source].createSceneImage() - cb.filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - cb.wrapU = WrapMode.REPEAT - cb.wrapV = WrapMode.REPEAT - val sceneTexture = Texture( - ModelCoordinates(texture = cb, pre = "x_texCoord.y = 1.0-x_texCoord.y;"), - TextureTarget.AMBIENT_OCCLUSION - ) - material.textures.add(sceneTexture) - } - - normalTexture?.let { texture -> - val cb = images!![textures!![texture.index].source].createSceneImage() - cb.filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - cb.wrapU = WrapMode.REPEAT - cb.wrapV = WrapMode.REPEAT - - val sceneTexture = Texture( - ModelCoordinates( - texture = cb, - tangentInput = "va_tangent", - pre = "x_texCoord.y = 1.0-x_texCoord.y;" - ), TextureTarget.NORMAL - ) - material.textures.add(sceneTexture) - } - - emissiveFactor?.let { - material.emission = ColorRGBa(it[0], it[1], it[2]) - } - - emissiveTexture?.let { - val cb = images!![textures!![it.index].source].createSceneImage() - val sceneTexture = Texture( - ModelCoordinates(texture = cb, pre = "x_texCoord.y = 1.0-x_texCoord.y;"), - TextureTarget.EMISSION - ) - material.textures.add(sceneTexture) - } - - extensions?.let { ext -> - ext.KHR_materials_pbrSpecularGlossiness?.let { sg -> - sg.diffuseFactor?.let { - material.color = ColorRGBa(it[0], it[1], it[2], it[3]) - } - sg.diffuseTexture?.let { - val cb = images!![textures!![it.index].source].createSceneImage() - cb.filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - cb.wrapU = WrapMode.REPEAT - cb.wrapV = WrapMode.REPEAT - val sceneTexture = Texture( - ModelCoordinates(texture = cb, pre = "x_texCoord.y = 1.0-x_texCoord.y;"), - TextureTarget.COLOR - ) - material.textures.add(sceneTexture) - } - occlusionTexture?.let { texture -> - val cb = images!![textures!![texture.index].source].createSceneImage() - cb.filter(MinifyingFilter.LINEAR_MIPMAP_LINEAR, MagnifyingFilter.LINEAR) - cb.wrapU = WrapMode.REPEAT - cb.wrapV = WrapMode.REPEAT - val sceneTexture = Texture( - ModelCoordinates(texture = cb, pre = "x_texCoord.y = 1.0-x_texCoord.y;"), - TextureTarget.AMBIENT_OCCLUSION - ) - material.textures.add(sceneTexture) - } - } - } - - emissiveFactor?.let { - material.emission = ColorRGBa(it[0], it[1], it[2], 1.0) - } - material - } - - fun GltfPrimitive.createScenePrimitive(): MeshPrimitive { - val drawCommand = createDrawCommand(this@buildSceneNodes) - val geometry = Geometry( - listOf(drawCommand.vertexBuffer), - drawCommand.indexBuffer, - drawCommand.primitive, - 0, - drawCommand.vertexCount - ) - val material = materials.getOrNull(material ?: -1 )?.createSceneMaterial() ?: PBRMaterial() - return MeshPrimitive(geometry, material) - } - - - val sceneNodes = mutableMapOf() - fun GltfNode.createSceneNode(): SceneNode = sceneNodes.getOrPut(this) { - val node = GltfSceneNode() - node.name = name ?: "no name" - node.translation = translation?.let { Vector3(it[0], it[1], it[2]) } ?: Vector3.ZERO - node.scale = scale?.let { Vector3(it[0], it[1], it[2]) } ?: Vector3.ONE - node.rotation = rotation?.let { Quaternion(it[0], it[1], it[2], it[3]) } ?: Quaternion.IDENTITY - - matrix?.let { - node.transform = Matrix44.fromDoubleArray(it).transposed - } - for (child in children.orEmpty) { - val childNode = nodes.getOrNull(child) ?: error("child node not found: $child") - node.children.add(childNode.createSceneNode()) - } - node - } - - val sceneMeshes = mutableMapOf() - fun GltfMesh.createSceneMesh(skin: GltfSkin?): MeshBase = sceneMeshes.getOrPut(this) { - if (skin == null) { - Mesh(primitives.map { - it.createScenePrimitive() - }) - } else { - val joints = skin.joints.map { nodes[it].createSceneNode() } - val skeleton = nodes[skin.skeleton].createSceneNode() - val ibmAccessor = accessors[skin.inverseBindMatrices] - val ibmBufferView = bufferViews[ibmAccessor.bufferView] - val ibmBuffer = buffers[ibmBufferView.buffer] - - val ibmData = ibmBuffer.contents(this@buildSceneNodes) - ibmData.order(ByteOrder.nativeOrder()) - (ibmData as Buffer).position(ibmAccessor.byteOffset + (ibmBufferView.byteOffset ?: 0)) - - require(ibmAccessor.type == "MAT4") - require(ibmAccessor.componentType == GLTF_FLOAT) - require(ibmAccessor.count == joints.size) - val ibms = (0 until ibmAccessor.count).map { - val array = DoubleArray(16) - for (i in 0 until 16) { - array[i] = ibmData.float.toDouble() - } - Matrix44.fromDoubleArray(array).transposed - } - - SkinnedMesh(primitives.map { - it.createScenePrimitive() - }, joints, skeleton, ibms) - } - } - - fun GltfCamera.createSceneCamera(sceneNode: SceneNode): Camera { - return when (type) { - "perspective" -> { - PerspectiveCamera(sceneNode).apply { - aspectRatio = perspective?.aspectRatio ?: aspectRatio - far = perspective?.zfar ?: far - near = perspective?.znear ?: near - fov = perspective?.yfov?.let { Math.toDegrees(it) } ?: fov - } - } - "orthographic" -> { - OrthographicCamera(sceneNode).apply { - xMag = orthographic?.xmag ?: xMag - yMag = orthographic?.ymag ?: yMag - near = orthographic?.znear ?: near - far = orthographic?.zfar ?: far - } - } - else -> error("unsupported camera type: $type") - } - } - - val scenes = scenes.map { scene -> - scene.nodes.map { node -> - val gltfNode = nodes.getOrNull(node) ?: error("node not found: $node") - val sceneNode = gltfNode.createSceneNode() - sceneNode - } - } - for ((gltfNode, sceneNode) in sceneNodes) { - gltfNode.mesh?.let { - val skin = gltfNode.skin?.let { (skins!!)[it] } - sceneNode.entities.add(meshes[it].createSceneMesh(skin)) - } - - gltfNode.camera?.let { - sceneNode.entities.add(cameras!![it].createSceneCamera(sceneNode)) - } - - gltfNode.extensions?.let { exts -> - exts.KHR_lights_punctual?.let { lightIndex -> - extensions?.KHR_lights_punctual?.lights?.get(lightIndex.light)?.let { light -> - val sceneLight = when (light.type) { - "point" -> { - PointLight() - } - "directional" -> { - DirectionalLight().apply { - shadows = Shadows.PCF() - } - } - "spot" -> { - SpotLight().apply { - innerAngle = Math.toDegrees(light.spot!!.innerConeAngle ?: 0.0) - outerAngle = Math.toDegrees(light.spot.outerConeAngle ?: Math.PI / 4.0) - shadows = Shadows.PCF() - } - - } - else -> error("unsupported light type ${light.type}") - } - sceneLight.apply { - val lightColor = (light.color ?: doubleArrayOf(1.0, 1.0, 1.0)) - color = ColorRGBa(lightColor[0], lightColor[1], lightColor[2]) - } - sceneNode.entities.add(sceneLight) - } - } - } - } - - val sceneAnimations = animations?.map { animation -> - val animationChannels = animation.channels.mapNotNull { channel -> - val candidate = channel.target.node?.let { nodes[it] }?.createSceneNode() as? GltfSceneNode - candidate?.let { sceneNode -> - val sampler = animation.samplers[channel.sampler] - - val inputAccessor = accessors[sampler.input] - val inputBufferView = bufferViews[inputAccessor.bufferView] - val inputData = buffers[inputBufferView.buffer].contents(this) - - val outputAccessor = accessors[sampler.output] - val outputBufferView = bufferViews[outputAccessor.bufferView] - val outputData = buffers[outputBufferView.buffer].contents(this) - - inputData.order(ByteOrder.nativeOrder()) - outputData.order(ByteOrder.nativeOrder()) - - require(inputAccessor.count == outputAccessor.count) - when (channel.target.path) { - "scale", "translation" -> { - require(inputAccessor.type == "SCALAR") - require(outputAccessor.type == "VEC3") - val keyframer = KeyframerChannelVector3() - val inputOffset = (inputBufferView.byteOffset ?: 0) + (inputAccessor.byteOffset ?: 0) - val outputOffset = (outputBufferView.byteOffset ?: 0) + (outputAccessor.byteOffset ?: 0) - val inputStride = (inputBufferView.byteStride ?: 4) - val outputStride = (outputBufferView.byteStride ?: 12) - - inputData.limit(inputData.capacity()) - for (i in 0 until outputAccessor.count) { - val input = inputData.getFloat(inputOffset + i * inputStride).toDouble() - val outputX = outputData.getFloat(outputOffset + i * outputStride).toDouble() - val outputY = outputData.getFloat(outputOffset + i * outputStride + 4).toDouble() - val outputZ = outputData.getFloat(outputOffset + i * outputStride + 8).toDouble() - keyframer.add(input, Vector3(outputX, outputY, outputZ)) - } - val target = - if (channel.target.path == "translation") sceneNode::translation else sceneNode::scale - val default = if (channel.target.path == "translation") Vector3.ZERO else Vector3.ONE - Vector3Channel(target, keyframer, default) - } - "rotation" -> { - require(inputAccessor.type == "SCALAR") - require(outputAccessor.type == "VEC4") { - "${outputAccessor.type}" - } - val keyframer = KeyframerChannelQuaternion() - val inputOffset = (inputBufferView.byteOffset ?: 0) + (inputAccessor.byteOffset ?: 0) - val outputOffset = (outputBufferView.byteOffset ?: 0) + (outputAccessor.byteOffset ?: 0) - val inputStride = (inputBufferView.byteStride ?: 4) - val outputStride = (outputBufferView.byteStride ?: 16) - for (i in 0 until outputAccessor.count) { - val input = inputData.getFloat(inputOffset + i * inputStride).toDouble() - val outputX = outputData.getFloat(outputOffset + i * outputStride).toDouble() - val outputY = outputData.getFloat(outputOffset + i * outputStride + 4).toDouble() - val outputZ = outputData.getFloat(outputOffset + i * outputStride + 8).toDouble() - val outputW = outputData.getFloat(outputOffset + i * outputStride + 12).toDouble() - keyframer.add(input, Quaternion(outputX, outputY, outputZ, outputW)) - } - QuaternionChannel(sceneNode::rotation, keyframer) - } - else -> error("unsupported path ${channel.target.path}") - } - } - } - SceneAnimation(animationChannels) - } - return GltfSceneData(scenes, sceneAnimations.orEmpty()) -} - -private val IntArray?.orEmpty: IntArray get() = this ?: IntArray(0) \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/materials/IrradianceDebugMaterial.kt b/orx-jvm/orx-dnk3/src/main/kotlin/materials/IrradianceDebugMaterial.kt deleted file mode 100644 index 9bac0e77..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/materials/IrradianceDebugMaterial.kt +++ /dev/null @@ -1,71 +0,0 @@ -package org.openrndr.extra.dnk3.materials - -import org.openrndr.draw.ShadeStyle -import org.openrndr.draw.shadeStyle -import org.openrndr.extra.dnk3.Material -import org.openrndr.extra.dnk3.MaterialContext -import org.openrndr.extra.dnk3.PrimitiveContext -import org.openrndr.extra.dnk3.cubemap.glslEvaluateSH -import org.openrndr.extra.dnk3.cubemap.glslFetchSH -import org.openrndr.extra.dnk3.cubemap.genGlslGatherSH - -class IrradianceDebugMaterial : Material { - override val name: String? = null - - override var doubleSided: Boolean = false - override var transparent: Boolean = false - override val fragmentID: Int = 0 - - override fun generateShadeStyle(context: MaterialContext, primitiveContext: PrimitiveContext): ShadeStyle { - return shadeStyle { - fragmentPreamble = """ - $glslEvaluateSH - $glslFetchSH - ${genGlslGatherSH(context.irradianceSH!!.xCount, context.irradianceSH!!.yCount, context.irradianceSH!!.zCount, context.irradianceSH!!.spacing, context.irradianceSH!!.offset)} - vec3 f_emission = vec3(0.0); - """ - - if (context.irradianceSH != null) { - fragmentTransform = """ - vec3[9] sh; - gatherSH(p_shMap, v_worldPosition, sh); - x_fill.rgb = evaluateSH(normalize(v_worldNormal), sh); - - """.trimIndent() - } else { - fragmentTransform = """ - discard; - """ - } - } - } - - override fun applyToShadeStyle(context: MaterialContext, shadeStyle: ShadeStyle) { - context.irradianceSH?.shMap?.let { - shadeStyle.parameter("shMap", it) - } - - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is IrradianceDebugMaterial) return false - - if (name != other.name) return false - if (doubleSided != other.doubleSided) return false - if (transparent != other.transparent) return false - if (fragmentID != other.fragmentID) return false - - return true - } - - override fun hashCode(): Int { - var result = name?.hashCode() ?: 0 - result = 31 * result + doubleSided.hashCode() - result = 31 * result + transparent.hashCode() - result = 31 * result + fragmentID - return result - } - - -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/post/ScreenspaceReflections.kt b/orx-jvm/orx-dnk3/src/main/kotlin/post/ScreenspaceReflections.kt deleted file mode 100644 index 77961c34..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/post/ScreenspaceReflections.kt +++ /dev/null @@ -1,36 +0,0 @@ -package org.openrndr.extra.dnk3.post - -import org.openrndr.draw.Filter -import org.openrndr.math.Matrix44 -import org.openrndr.resourceUrl - -class ScreenspaceReflections : Filter(preprocessedFilterShaderFromUrl(resourceUrl("/shaders/screenspace-reflections.frag"))) { - var projection: Matrix44 by parameters - var projectionMatrixInverse: Matrix44 by parameters - - var colors: Int by parameters - var projDepth: Int by parameters - var normals: Int by parameters - - var jitterOriginGain: Double by parameters - var iterationLimit: Int by parameters - var distanceLimit: Double by parameters - var gain: Double by parameters - var borderWidth: Double by parameters - - init { - colors = 0 - projDepth = 1 - normals = 2 - - projection = Matrix44.IDENTITY - projectionMatrixInverse = Matrix44.IDENTITY - - distanceLimit = 100.0 - iterationLimit = 128 - jitterOriginGain = 0.0 - - gain = 1.0 - borderWidth = 130.0 - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/post/SegmentContours.kt b/orx-jvm/orx-dnk3/src/main/kotlin/post/SegmentContours.kt deleted file mode 100644 index f9527acf..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/post/SegmentContours.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.openrndr.extra.dnk3.post - -import org.openrndr.draw.Filter -import org.openrndr.draw.filterShaderFromUrl -import org.openrndr.resourceUrl - -class SegmentContoursMSAA8 : Filter(filterShaderFromUrl(resourceUrl("/shaders/segment-contours-msaa-8.frag"))) -class SegmentContours : Filter(filterShaderFromUrl(resourceUrl("/shaders/segment-contours.frag"))) diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/post/VolumetricIrradiance.kt b/orx-jvm/orx-dnk3/src/main/kotlin/post/VolumetricIrradiance.kt deleted file mode 100644 index c6d20dbc..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/post/VolumetricIrradiance.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.openrndr.extra.dnk3.post - -import org.openrndr.draw.* -import org.openrndr.extra.dnk3.features.IrradianceSH -import org.openrndr.extra.shaderphrases.preprocessShader -import org.openrndr.math.IntVector3 -import org.openrndr.math.Matrix44 -import org.openrndr.resourceUrl -import org.openrndr.shape.Rectangle -import java.net.URL - -fun preprocessedFilterShaderFromUrl(url: String): Shader { - return filterShaderFromCode( preprocessShader(URL(url).readText()), "filter-shader: $url") -} - -fun preprocessedFilterShaderFromCode(fragmentShaderCode: String, name: String): Shader { - return Shader.createFromCode(vsCode = Filter.filterVertexCode, fsCode = fragmentShaderCode, name = name) -} - -class VolumetricIrradiance : Filter(preprocessedFilterShaderFromUrl(resourceUrl("/shaders/volumetric-irradiance.frag"))) { - - var stepLength: Double by parameters - var irradianceSH: IrradianceSH? = null - - var viewMatrixInverse: Matrix44 by parameters - var projectionMatrixInverse: Matrix44 by parameters - - init { - stepLength = 0.1 - viewMatrixInverse = Matrix44.IDENTITY - projectionMatrixInverse = Matrix44.IDENTITY - } - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - irradianceSH?.shMap?.let { - parameters["shMap"] = it - } - irradianceSH?.let { - parameters["shMapDimensions"] = IntVector3(it.xCount, it.yCount, it.zCount) - parameters["shMapOffset"] = it.offset - parameters["shMapSpacing"] = it.spacing - } - super.apply(source, target, clip) - } -} - diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/query/Query.kt b/orx-jvm/orx-dnk3/src/main/kotlin/query/Query.kt deleted file mode 100644 index f062d7fa..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/query/Query.kt +++ /dev/null @@ -1,56 +0,0 @@ -package org.openrndr.extra.dnk3.query - -import org.openrndr.extra.dnk3.Material -import org.openrndr.extra.dnk3.Mesh -import org.openrndr.extra.dnk3.Scene -import org.openrndr.extra.dnk3.SceneNode - -fun Scene.findNodeByName(name: String): SceneNode? { - return root.findNodeByName(name) -} - -fun SceneNode.findNodeByName(name: String): SceneNode? { - - if (this.name == name) { - return this - } else { - for (child in children) { - val candidate = child.findNodeByName(name) - if (candidate != null) { - return candidate - } - } - } - return null -} - -fun SceneNode.findMaterialByName(name: String): Material? { - return allMaterials().find { it.name == name } -} - -fun Scene.allMaterials(): Set { - return root.allMaterials() -} - -fun SceneNode.allMaterials(): Set { - val materials = mutableSetOf() - fun processNode(node: SceneNode) { - for (entity in node.entities) { - when (entity) { - is Mesh -> { - materials.addAll(entity.primitives.map { it.material }) - } - else -> { - } - } - } - - for (child in node.children) { - processNode(child) - } - } - processNode(this) - return materials -} - - diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/renderers/DryRenderer.kt b/orx-jvm/orx-dnk3/src/main/kotlin/renderers/DryRenderer.kt deleted file mode 100644 index 10b73024..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/renderers/DryRenderer.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.openrndr.extra.dnk3.renderers - -import org.openrndr.extra.dnk3.SceneRenderer - -fun dryRenderer() : SceneRenderer { - val sr = SceneRenderer() - return sr -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/renderers/PostRenderer.kt b/orx-jvm/orx-dnk3/src/main/kotlin/renderers/PostRenderer.kt deleted file mode 100644 index 86d1809b..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/renderers/PostRenderer.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.openrndr.extra.dnk3.renderers - -import org.openrndr.draw.BufferMultisample -import org.openrndr.extra.dnk3.* - -fun postRenderer(multisample: BufferMultisample = BufferMultisample.Disabled): SceneRenderer { - val sr = SceneRenderer() - sr.outputPasses.clear() - sr.outputPasses.add( - RenderPass( - listOf(HDRColorFacet(),FragmentIDFacet(), ClipDepthFacet(), ViewNormalFacet()), - multisample = multisample - ) - ) - - sr.drawFinalBuffer = true - return sr -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/renderers/SegmentContourRenderer.kt b/orx-jvm/orx-dnk3/src/main/kotlin/renderers/SegmentContourRenderer.kt deleted file mode 100644 index ad555fd2..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/renderers/SegmentContourRenderer.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.openrndr.extra.dnk3.renderers - -import org.openrndr.draw.BufferMultisample -import org.openrndr.draw.ColorFormat -import org.openrndr.draw.ColorType -import org.openrndr.extra.dnk3.* -import org.openrndr.extra.dnk3.post.SegmentContours -import org.openrndr.extra.dnk3.post.SegmentContoursMSAA8 - -fun segmentContourRenderer(multisample: BufferMultisample = BufferMultisample.Disabled): SceneRenderer { - val sr = SceneRenderer() - sr.outputPasses.clear() - sr.outputPasses.add( - RenderPass( - listOf(LDRColorFacet(),FragmentIDFacet()), - multisample = multisample - ) - ) - sr.postSteps.add( - FilterPostStep(1.0, - when (multisample) { - BufferMultisample.Disabled -> SegmentContours() - BufferMultisample.SampleCount(8) -> SegmentContoursMSAA8() - else -> error("unsupported multisampling mode $multisample") - }, - listOf("fragmentID"), - "segments", - ColorFormat.RGB, - ColorType.UINT8 - ) - ) - sr.drawFinalBuffer = true - return sr -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/tools/MeshCollapse.kt b/orx-jvm/orx-dnk3/src/main/kotlin/tools/MeshCollapse.kt deleted file mode 100644 index 80bb4dc6..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/tools/MeshCollapse.kt +++ /dev/null @@ -1,98 +0,0 @@ -package org.openrndr.extra.dnk3.tools - -import org.openrndr.draw.* -import org.openrndr.extra.dnk3.Geometry -import org.openrndr.extra.dnk3.Mesh -import org.openrndr.extra.dnk3.MeshPrimitive -import org.openrndr.extra.dnk3.PBRMaterial -import java.nio.ByteBuffer -import java.nio.ByteOrder - - -private data class CollapseItem(val vertexFormats: List, - val drawPrimitive: DrawPrimitive, - val hasIndexBuffer: Boolean) - -fun Mesh.collapse() { - val grouped = primitives.groupBy { - CollapseItem(it.geometry.vertexBuffers.map { it.vertexFormat }, it.geometry.primitive, it.geometry.indexBuffer != null) - } - - grouped.map { - val vertexCount = it.value.sumOf { primitive -> - primitive.geometry.vertexCount - } - - val indexCount = if (it.key.hasIndexBuffer) - it.value.sumOf { primitive -> - primitive.geometry.indexBuffer?.indexCount ?: 0 - } - else 0 - - val collapsedVertices = it.key.vertexFormats.map { - vertexBuffer(it, vertexCount) - } + vertexBuffer(vertexFormat { attribute("fragmentID", VertexElementType.INT16) }, vertexCount) - - - val fragmentBuffer = ByteBuffer.allocateDirect(vertexCount * 2) - fragmentBuffer.order(ByteOrder.nativeOrder()) - - for (i in 0 until collapsedVertices.size) { - var offset = 0 - for (fromPrimitive in it.value) { - val fromBuffer = fromPrimitive.geometry.vertexBuffers[i] - - val copy = ByteBuffer.allocateDirect(fromBuffer.vertexCount * fromBuffer.vertexFormat.size) - copy.order(ByteOrder.nativeOrder()) - fromBuffer.read(copy) - copy.rewind() - - collapsedVertices[i].write(copy, offset) - offset += copy.capacity() - - for (v in 0 until fromBuffer.vertexCount) { - fragmentBuffer.putShort(fromPrimitive.material.fragmentID.toShort()) - } - } - } - - val collapsedIndices = if (it.key.hasIndexBuffer) indexBuffer(indexCount, IndexType.INT32) else null - - if (it.key.hasIndexBuffer) { - var offset = 0 - val result = ByteBuffer.allocateDirect(4 * indexCount) - result.order(ByteOrder.nativeOrder()) - - for (fromPrimitive in it.value) { - val fromBuffer = fromPrimitive.geometry.indexBuffer!! - when (fromBuffer.type) { - IndexType.INT16 -> { - val copy = ByteBuffer.allocateDirect(fromBuffer.indexCount * 2) - fromBuffer.read(copy) - copy.rewind() - for (i in 0 until fromBuffer.indexCount) { - val index = (copy.getShort().toInt() and 0xffff) + offset - result.putInt(index) - } - } - IndexType.INT32 -> { - val copy = ByteBuffer.allocateDirect(fromBuffer.indexCount * 4) - fromBuffer.read(copy) - copy.rewind() - for (i in 0 until fromBuffer.indexCount) { - val index = copy.getInt() + offset - result.putInt(index) - } - } - } - offset += fromPrimitive.geometry.vertexCount - } - } - - val collapsedGeometry = Geometry(collapsedVertices, collapsedIndices, it.key.drawPrimitive, 0, if (collapsedIndices == null) - vertexCount else indexCount - ) - - MeshPrimitive(collapsedGeometry, PBRMaterial()) - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/kotlin/tools/Skybox.kt b/orx-jvm/orx-dnk3/src/main/kotlin/tools/Skybox.kt deleted file mode 100644 index 606868a7..00000000 --- a/orx-jvm/orx-dnk3/src/main/kotlin/tools/Skybox.kt +++ /dev/null @@ -1,84 +0,0 @@ -package org.openrndr.extra.dnk3.tools - -import org.openrndr.draw.* -import org.openrndr.extra.dnk3.* -import org.openrndr.extra.meshgenerators.boxMesh - - -data class SkyboxMaterial(val cubemap: Cubemap, val intensity: Double = 0.0) : Material { - override val name: String = "skybox" - override var doubleSided: Boolean = false - override var transparent: Boolean = false - override val fragmentID: Int = 0 - - override fun generateShadeStyle(materialContext: MaterialContext, primitiveContext: PrimitiveContext): ShadeStyle { - return shadeStyle { - vertexTransform = """ - vec2 i = vec2(1.0, 0.0); - x_viewMatrix = x_viewNormalMatrix; - """.trimIndent() - - val combinerFS = materialContext.pass.combiners.map { - it.generateShader() - }.joinToString("\n") - - fragmentPreamble = """ - vec4 f_diffuse = vec4(0.0, 0.0, 0.0, 1.0); - vec3 f_specular = vec3(0.0); - vec3 f_ambient = vec3(0.0); - vec3 f_emission = vec3(0.0); - int f_fragmentID = 0; - vec4 m_color = vec4(1.0); - vec4 f_fog = vec4(0.0); - - """.trimIndent() - fragmentTransform = """ - f_diffuse = texture(p_skybox, va_position); - f_diffuse.rgb *= p_intensity; - """ + combinerFS - - suppressDefaultOutput = true - val rt = RenderTarget.active - materialContext.pass.combiners.map { - if (rt is ProgramRenderTarget || materialContext.pass === DefaultPass || materialContext.pass === DefaultOpaquePass || materialContext.pass == DefaultTransparentPass || materialContext.pass == IrradianceProbePass) { - this.output(it.targetOutput, ShadeStyleOutput(0)) - } else { - val index = rt.colorAttachmentIndexByName(it.targetOutput) - ?: error("attachment ${it.targetOutput} not found") - val type = rt.colorBuffer(index).type - val format = rt.colorBuffer(index).format - this.output(it.targetOutput, ShadeStyleOutput(index, format, type)) - } - } - } - } - - override fun applyToShadeStyle(context: MaterialContext, shadeStyle: ShadeStyle) { - shadeStyle.parameter("skybox", cubemap) - shadeStyle.parameter("intensity", intensity) - } - - - override fun hashCode(): Int { - var result = intensity.hashCode() - result = 31 * result + name.hashCode() - result = 31 * result + doubleSided.hashCode() - result = 31 * result + transparent.hashCode() - result = 31 * result + fragmentID - return result - } - - -} - -fun Scene.addSkybox(cubemapUrl: String, size: Double = 100.0, intensity: Double = 1.0) { - val cubemap = loadCubemap(cubemapUrl, null, Session.active).apply { generateMipmaps() } - val box = boxMesh(size, size, size, 1, 1, 1, true) - val node = SceneNode() - val material = SkyboxMaterial(cubemap, intensity) - val geometry = Geometry(listOf(box), null, DrawPrimitive.TRIANGLES, 0, box.vertexCount) - val primitive = MeshPrimitive(geometry, material) - val mesh = Mesh(listOf(primitive)) - node.entities.add(mesh) - root.children.add(node) -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/evaluate-sh.frag b/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/evaluate-sh.frag deleted file mode 100644 index 6dbd4aa7..00000000 --- a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/evaluate-sh.frag +++ /dev/null @@ -1,17 +0,0 @@ -vec3 evaluateSH(vec3 direction, vec3[9] _SH) { - const float c1 = 0.42904276540489171563379376569857; // 4 * Â2.Y22 = 1/4 * sqrt(15.PI) - const float c2 = 0.51166335397324424423977581244463; // 0.5 * Â1.Y10 = 1/2 * sqrt(PI/3) - const float c3 = 0.24770795610037568833406429782001; // Â2.Y20 = 1/16 * sqrt(5.PI) - const float c4 = 0.88622692545275801364908374167057; // Â0.Y00 = 1/2 * sqrt(PI) - - float x = direction.x; - float y = direction.y; - float z = direction.z; - - return max(vec3(0.0), - _SH[8] * (c1 * (x * x - y * y)) // c1.L22.(x²-y²) - + _SH[6] * (c3 * (3.0 * z * z - 1)) // c3.L20.(3.z² - 1) - + _SH[0] * c4 // c4.L00 - + (_SH[4] * x * y + _SH[7] * x * z + _SH[5] * y * z) * 2.0 * c1 // 2.c1.(L2-2.xy + L21.xz + L2-1.yz) - + (_SH[3] * x + _SH[1] * y + _SH[2] * z) * c2 * 2.0); // 2.c2.(L11.x + L1-1.y + L10.z) -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/fetch-sh.frag b/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/fetch-sh.frag deleted file mode 100644 index 5e4bc087..00000000 --- a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/fetch-sh.frag +++ /dev/null @@ -1,12 +0,0 @@ -void fetchSH(samplerBuffer btex, int probeID, out vec3[9] _SH) { - int offset = probeID * 9; - _SH[0] = texelFetch(btex, offset).rgb; - _SH[1] = texelFetch(btex, offset+1).rgb; - _SH[2] = texelFetch(btex, offset+2).rgb; - _SH[3] = texelFetch(btex, offset+3).rgb; - _SH[4] = texelFetch(btex, offset+4).rgb; - _SH[5] = texelFetch(btex, offset+5).rgb; - _SH[6] = texelFetch(btex, offset+6).rgb; - _SH[7] = texelFetch(btex, offset+7).rgb; - _SH[8] = texelFetch(btex, offset+8).rgb; -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/fetch-sh0.frag b/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/fetch-sh0.frag deleted file mode 100644 index b07cd066..00000000 --- a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/fetch-sh0.frag +++ /dev/null @@ -1,4 +0,0 @@ -void fetchSH0(samplerBuffer btex, int probeID, out vec3 _SH) { - int offset = probeID * 9; - _SH = texelFetch(btex, offset).rgb; -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/gather-sh.frag b/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/gather-sh.frag deleted file mode 100644 index 2bce506a..00000000 --- a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/gather-sh.frag +++ /dev/null @@ -1,26 +0,0 @@ -void gatherSH(samplerBuffer btex, vec3 p, ivec3 probeCounts, vec3 offset, float spacing, out vec3[9] blend) { - vec3[9] c000; - vec3[9] c001; - vec3[9] c010; - vec3[9] c011; - vec3[9] c100; - vec3[9] c101; - vec3[9] c110; - vec3[9] c111; - - vec3 f; - ivec3 io = gridCoordinates(p, f, probeCounts, offset, spacing); - - fetchSH(btex, gridIndex(io + ivec3(0,0,0), probeCounts), c000); - fetchSH(btex, gridIndex(io + ivec3(0,0,1), probeCounts), c001); - fetchSH(btex, gridIndex(io + ivec3(0,1,0), probeCounts), c010); - fetchSH(btex, gridIndex(io + ivec3(0,1,1), probeCounts), c011); - fetchSH(btex, gridIndex(io + ivec3(1,0,0), probeCounts), c100); - fetchSH(btex, gridIndex(io + ivec3(1,0,1), probeCounts), c101); - fetchSH(btex, gridIndex(io + ivec3(1,1,0), probeCounts), c110); - fetchSH(btex, gridIndex(io + ivec3(1,1,1), probeCounts), c111); - - for (int i = 0; i < 9; ++i) { - blend[i] = mix( mix( mix(c000[i], c001[i], f.z), mix(c010[i], c011[i], f.z), f.y), mix( mix(c100[i], c101[i], f.z), mix(c110[i], c111[i], f.z), f.y), f.x); - } -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/gather-sh0.frag b/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/gather-sh0.frag deleted file mode 100644 index 28926e59..00000000 --- a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/gather-sh0.frag +++ /dev/null @@ -1,25 +0,0 @@ -void gatherSH0(samplerBuffer btex, vec3 p, ivec3 probeCounts, vec3 offset, float spacing, out vec3 blend) { - vec3 c000; - vec3 c001; - vec3 c010; - vec3 c011; - vec3 c100; - vec3 c101; - vec3 c110; - vec3 c111; - - vec3 f; - ivec3 io = gridCoordinates(p, f, probeCounts, offset, spacing); - - fetchSH0(btex, gridIndex(io + ivec3(0,0,0), probeCounts), c000); - fetchSH0(btex, gridIndex(io + ivec3(0,0,1), probeCounts), c001); - fetchSH0(btex, gridIndex(io + ivec3(0,1,0), probeCounts), c010); - fetchSH0(btex, gridIndex(io + ivec3(0,1,1), probeCounts), c011); - fetchSH0(btex, gridIndex(io + ivec3(1,0,0), probeCounts), c100); - fetchSH0(btex, gridIndex(io + ivec3(1,0,1), probeCounts), c101); - fetchSH0(btex, gridIndex(io + ivec3(1,1,0), probeCounts), c110); - fetchSH0(btex, gridIndex(io + ivec3(1,1,1), probeCounts), c111); - - blend = mix( mix( mix(c000, c001, f.z), mix(c010, c011, f.z), f.y), mix( mix(c100, c101, f.z), mix(c110, c111, f.z), f.y), f.x); - -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/grid-coordinates.frag b/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/grid-coordinates.frag deleted file mode 100644 index 82ec0204..00000000 --- a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/grid-coordinates.frag +++ /dev/null @@ -1,15 +0,0 @@ -ivec3 gridCoordinates(vec3 p, out vec3 f, ivec3 probeCounts, vec3 offset, float spacing) { - float x = (p.x - offset.x) / spacing; - float y = (p.y - offset.y)/ spacing; - float z = (p.z - offset.z) / spacing; - - int ix = int(floor(x)) + probeCounts.x / 2; - int iy = int(floor(y)) + probeCounts.y / 2; - int iz = int(floor(z)) + probeCounts.z / 2; - - f.x = fract((x)); - f.y = fract((y)); - f.z = fract((z)); - - return ivec3(ix, iy, iz); -} diff --git a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/grid-index.frag b/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/grid-index.frag deleted file mode 100644 index 58582e60..00000000 --- a/orx-jvm/orx-dnk3/src/main/resources/phrases/irradiance-sh/grid-index.frag +++ /dev/null @@ -1,4 +0,0 @@ -int gridIndex(ivec3 p, ivec3 probeCounts) { - ivec3 c = clamp(p, ivec3(0), probeCounts - ivec3(1)); - return c.x + c.y * probeCounts.x + c.z * probeCounts.x * probeCounts.y; -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/resources/shaders/cubemap-filters/cubemap-passthrough.frag b/orx-jvm/orx-dnk3/src/main/resources/shaders/cubemap-filters/cubemap-passthrough.frag deleted file mode 100644 index 4de2509e..00000000 --- a/orx-jvm/orx-dnk3/src/main/resources/shaders/cubemap-filters/cubemap-passthrough.frag +++ /dev/null @@ -1,22 +0,0 @@ -#version 330 - -uniform samplerCube tex0; -uniform vec3 sideUp; -uniform vec3 sideRight; -uniform vec3 sideNormal; -in vec2 v_texCoord0; - -out vec4 o_output; - -#define PI 3.1415926536 - -void main() { - vec3 irradiance = vec3(0.0); - - vec2 uv = (v_texCoord0 - vec2(0.5))*2.0; - vec3 normal = normalize(uv.x * sideRight + uv.y * sideUp + sideNormal); - - o_output.rgb = texture(tex0, normal).rgb; - o_output.a = 1.0; - -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/resources/shaders/cubemap-filters/irradiance-convolution.frag b/orx-jvm/orx-dnk3/src/main/resources/shaders/cubemap-filters/irradiance-convolution.frag deleted file mode 100644 index 044b1ba3..00000000 --- a/orx-jvm/orx-dnk3/src/main/resources/shaders/cubemap-filters/irradiance-convolution.frag +++ /dev/null @@ -1,40 +0,0 @@ -#version 330 - -uniform samplerCube tex0; -uniform vec3 sideUp; -uniform vec3 sideRight; -uniform vec3 sideNormal; -in vec2 v_texCoord0; - -out vec4 o_output; - -#define PI 3.1415926536 - -void main() { - vec3 irradiance = vec3(0.0); - - vec2 uv = (v_texCoord0 - vec2(0.5))*2.0; - vec3 normal = normalize(uv.x * sideRight + uv.y * sideUp + sideNormal); - - vec3 up = vec3(0.0, 1.0, 0.0); - vec3 right = cross(up, normal); - up = cross(normal, right); - - float sampleDelta = 0.025; - int nrSamples = 0; - for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta) { - for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta) { - // spherical to cartesian (in tangent space) - vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); - // tangent space to world - vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * normal; - - irradiance += texture(tex0, sampleVec).rgb * cos(theta) * sin(theta); - nrSamples++; - } - } - irradiance = PI * irradiance * (1.0 / float(nrSamples)); - o_output.rgb = irradiance.rgb; - o_output.a = 1.0; - -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/resources/shaders/cubemap-filters/spherical-harmonics.frag b/orx-jvm/orx-dnk3/src/main/resources/shaders/cubemap-filters/spherical-harmonics.frag deleted file mode 100644 index 882d7ba7..00000000 --- a/orx-jvm/orx-dnk3/src/main/resources/shaders/cubemap-filters/spherical-harmonics.frag +++ /dev/null @@ -1,11 +0,0 @@ -uniform samplerCube tex0; - -in v_texCoord0; -uniform vec2 targetSize; - -out o_term0; -out o_term1; -out o_term2; -void main() { - -} \ No newline at end of file diff --git a/orx-jvm/orx-dnk3/src/main/resources/shaders/screenspace-reflections.frag b/orx-jvm/orx-dnk3/src/main/resources/shaders/screenspace-reflections.frag deleted file mode 100644 index 98fbc423..00000000 --- a/orx-jvm/orx-dnk3/src/main/resources/shaders/screenspace-reflections.frag +++ /dev/null @@ -1,349 +0,0 @@ -#version 330 -// --- varyings --- -in vec2 v_texCoord0; - -// --- G buffer --- -uniform sampler2D colors; -uniform sampler2D projDepth; -uniform sampler2D normals; - -// --- transforms --- -uniform mat4 projection; -uniform mat4 projectionMatrixInverse; - -// --- output --- -layout(location = 0) out vec4 o_color; - - -// --- parameters --- -uniform float jitterOriginGain; -uniform int iterationLimit; -uniform float distanceLimit; -uniform float gain; -uniform float borderWidth; - -float distanceSquared(vec2 a, vec2 b) { - vec2 d = b-a; - return dot(d,d); -} - -#pragma import org.openrndr.extra.shaderphrases.phrases.Depth.projectionToViewCoordinate; -#pragma import org.openrndr.extra.shaderphrases.phrases.Depth.projectionToViewDepth; - -#pragma import org.openrndr.extra.noise.phrases.NoisePhrasesKt.phraseHash22; - - -// this is from http://casual-effects.blogspot.nl/2014/08/screen-space-ray-tracing.html - -void swap(inout float a, inout float b) { - float temp = a; - a = b; - b = temp; -} - - -bool traceScreenSpaceRay1 - (vec3 csOrigin, - vec3 csDirection, - mat4x4 projectToPixelMatrix, - sampler2D csZBuffer, - vec2 csZBufferSize, - float csZThickness, - float nearPlaneZ, - float stride, - float jitterFraction, - float maxSteps, - in float maxRayTraceDistance, - out vec2 hitPixel, - out vec3 csHitPoint, - out vec3 csHitNormal -// ,out vec3 debugColor - ) { - vec3 debugColor = vec3(0); - // Clip ray to a near plane in 3D (doesn't have to be *the* near plane, although that would be a good idea) - float rayLength = ((csOrigin.z + csDirection.z * maxRayTraceDistance) > nearPlaneZ) ? - (nearPlaneZ - csOrigin.z) / csDirection.z : - maxRayTraceDistance; - vec3 csEndPoint = csDirection * rayLength + csOrigin; - - // Project into screen space - vec4 H0 = projectToPixelMatrix * vec4(csOrigin, 1.0); - vec4 H1 = projectToPixelMatrix * vec4(csEndPoint, 1.0); - - // There are a lot of divisions by w that can be turned into multiplications - // at some minor precision loss...and we need to interpolate these 1/w values - // anyway. - // - // Because the caller was required to clip to the near plane, - // this homogeneous division (projecting from 4D to 2D) is guaranteed - // to succeed. - float k0 = 1.0 / H0.w; - float k1 = 1.0 / H1.w; - - // Switch the original points to values that interpolate linearly in 2D - vec3 Q0 = csOrigin * k0; - vec3 Q1 = csEndPoint * k1; - - // Screen-space endpoints - vec2 P0 = H0.xy * k0; - vec2 P1 = H1.xy * k1; - - // [Optional clipping to frustum sides here] - - // Initialize to off screen - hitPixel = vec2(-1.0, -1.0); - - // If the line is degenerate, make it cover at least one pixel - // to avoid handling zero-pixel extent as a special case later - P1 += vec2((distanceSquared(P0, P1) < 0.0001) ? 0.01 : 0.0); - - vec2 delta = P1 - P0; - - // Permute so that the primary iteration is in x to reduce - // large branches later - bool permute = (abs(delta.x) < abs(delta.y)); - if (permute) { - // More-vertical line. Create a permutation that swaps x and y in the output - // by directly swizzling the inputs. - delta = delta.yx; - P1 = P1.yx; - P0 = P0.yx; - } - - // From now on, "x" is the primary iteration direction and "y" is the secondary one - float stepDirection = sign(delta.x); - float invdx = stepDirection / delta.x; - vec2 dP = vec2(stepDirection, invdx * delta.y); - - // Track the derivatives of Q and k - vec3 dQ = (Q1 - Q0) * invdx; - float dk = (k1 - k0) * invdx; - - // Because we test 1/2 a texel forward along the ray, on the very last iteration - // the interpolation can go past the end of the ray. Use these bounds to clamp it. - float zMin = min(csEndPoint.z, csOrigin.z); - float zMax = max(csEndPoint.z, csOrigin.z); - - // Scale derivatives by the desired pixel stride - dP *= stride; dQ *= stride; dk *= stride; - - // Offset the starting values by the jitter fraction - P0 += dP * jitterFraction; Q0 += dQ * jitterFraction; k0 += dk * jitterFraction; - - // Slide P from P0 to P1, (now-homogeneous) Q from Q0 to Q1, and k from k0 to k1 - vec3 Q = Q0; - float k = k0; - - // We track the ray depth at +/- 1/2 pixel to treat pixels as clip-space solid - // voxels. Because the depth at -1/2 for a given pixel will be the same as at - // +1/2 for the previous iteration, we actually only have to compute one value - // per iteration. - float prevZMaxEstimate = csOrigin.z; - float stepCount = 0.0; - float rayZMax = prevZMaxEstimate, rayZMin = prevZMaxEstimate; - float sceneZMax = rayZMax + 1e4; - - // P1.x is never modified after this point, so pre-scale it by - // the step direction for a signed comparison - float end = P1.x * stepDirection; - - // We only advance the z field of Q in the inner loop, since - // Q.xy is never used until after the loop terminates. - - vec2 P; - for (P = P0; - ((P.x * stepDirection) <= end) && - (stepCount < maxSteps) && - ((rayZMax < sceneZMax - csZThickness) || - (rayZMin > sceneZMax)) && - (sceneZMax != 0.0); - P += dP, Q.z += dQ.z, k += dk, stepCount += 1.0) { - - // The depth range that the ray covers within this loop - // iteration. Assume that the ray is moving in increasing z - // and swap if backwards. Because one end of the interval is - // shared between adjacent iterations, we track the previous - // value and then swap as needed to ensure correct ordering - rayZMin = prevZMaxEstimate; - - // Compute the value at 1/2 step into the future - rayZMax = (dQ.z * 0.5 + Q.z) / (dk * 0.5 + k); - - // -- this is not in the other implementation - rayZMax = clamp(rayZMax, zMin, zMax); - - prevZMaxEstimate = rayZMax; - - // Since we don't know if the ray is stepping forward or backward in depth, - // maybe swap. Note that we preserve our original z "max" estimate first. - if (rayZMin > rayZMax) { swap(rayZMin, rayZMax); } - - // Camera-space z of the background - hitPixel = permute ? P.yx : P; - - vec4 depthData = texelFetch(csZBuffer, ivec2(hitPixel), 0); - sceneZMax = projectionToViewCoordinate(v_texCoord0, depthData.x, projectionMatrixInverse).z; - - } // pixel on ray - - // Undo the last increment, which ran after the test variables - // were set up. - P -= dP; Q.z -= dQ.z; k -= dk; stepCount -= 1.0; - - bool hit = (rayZMax >= sceneZMax - csZThickness) && (rayZMin <= sceneZMax); - - // If using non-unit stride and we hit a depth surface... - if ((stride > 1) && hit) { - // Refine the hit point within the last large-stride step - - // Retreat one whole stride step from the previous loop so that - // we can re-run that iteration at finer scale - P -= dP; Q.z -= dQ.z; k -= dk; stepCount -= 1.0; - - // Take the derivatives back to single-pixel stride - float invStride = 1.0 / stride; - dP *= invStride; dQ.z *= invStride; dk *= invStride; - - // For this test, we don't bother checking thickness or passing the end, since we KNOW there will - // be a hit point. As soon as - // the ray passes behind an object, call it a hit. Advance (stride + 1) steps to fully check this - // interval (we could skip the very first iteration, but then we'd need identical code to prime the loop) - float refinementStepCount = 0; - - // This is the current sample point's z-value, taken back to camera space - prevZMaxEstimate = Q.z / k; - rayZMin = prevZMaxEstimate; - - // Ensure that the FOR-loop test passes on the first iteration since we - // won't have a valid value of sceneZMax to test. - sceneZMax = rayZMin - 1e7; - - for (; - (refinementStepCount <= stride*1.4) && - (rayZMin > sceneZMax) && (sceneZMax != 0.0); - P += dP, Q.z += dQ.z, k += dk, refinementStepCount += 1.0) { - - rayZMin = prevZMaxEstimate; - - // Compute the ray camera-space Z value at 1/2 fine step (pixel) into the future - rayZMax = (dQ.z * 0.5 + Q.z) / (dk * 0.5 + k); - rayZMax = clamp(rayZMax, zMin, zMax); - - prevZMaxEstimate = rayZMax; - rayZMin = min(rayZMax, rayZMin); - - hitPixel = permute ? P.yx : P; - - vec4 depthData = texelFetch(csZBuffer, ivec2(hitPixel), 0); - sceneZMax = projectionToViewCoordinate(v_texCoord0, depthData.x, projectionMatrixInverse).z; - - - csHitNormal = texelFetch(normals, ivec2(hitPixel), 0).xyz; - -// sceneZMax = texelFetch(csZBuffer, ivec2(hitPixel), 0).r; - - } - - // Undo the last increment, which happened after the test variables were set up - Q.z -= dQ.z; refinementStepCount -= 1; - - // Count the refinement steps as fractions of the original stride. Save a register - // by not retaining invStride until here - stepCount += refinementStepCount / stride; - // debugColor = vec3(refinementStepCount / stride); - } // refinement - - Q.xy += dQ.xy * stepCount; - csHitPoint = Q * (1.0 / k); - - // Support debugging. This will compile away if debugColor is unused - if ((P.x * stepDirection) > end) { - // Hit the max ray distance -> blue - debugColor = vec3(0,0,1); - } else if (stepCount >= maxSteps) { - // Ran out of steps -> red - debugColor = vec3(1,0,0); - } else if (sceneZMax == 0.0) { - // Went off screen -> yellow - debugColor = vec3(1,1,0); - } else { - // Encountered a valid hit -> green - // ((rayZMax >= sceneZMax - csZThickness) && (rayZMin <= sceneZMax)) - debugColor = vec3(0,1,0); - } - - // Does the last point discovered represent a valid hit? - return hit; -} - - -void main() { - vec2 hitPixel = vec2(0.0, 0.0); - vec3 hitPoint = vec3(0.0, 0.0, 0.0); - vec3 hitNormal = vec3(0.0, 0.0, 0.0); - - vec2 jitter = abs(hash22(v_texCoord0)); - - - vec2 ts = vec2(textureSize(projDepth, 0).xy); - vec3 viewNormal = normalize(texture(normals, v_texCoord0).xyz);// + (texture(noise, v_texCoord0*0.1).xyz - 0.5) * 0.0; - float depth = texture(projDepth, v_texCoord0).r; - vec3 viewPos = projectionToViewCoordinate(v_texCoord0, depth, projectionMatrixInverse); - - - vec3 reflected = normalize(reflect(normalize(viewPos), normalize(-viewNormal))); - - - float angle = abs(dot(reflected, viewNormal)); - float frontalFade = clamp(-reflected.z,0.0, 1.0); - if ( true ) { - bool hit = traceScreenSpaceRay1( - viewPos, - reflected, - projection, - projDepth, - ts, - 0.1, - 0.0, // near plane z - 1.0,// + projPos.z*2.0, // stride - 10.0, // jitterfraction - iterationLimit*8,// + int((1.0-projPos.z)*iterationLimit), - 100.0, // max distance - - hitPixel, - hitPoint, hitNormal); - - float distanceFade = 1.0;//max( 0.0, (distanceLimit -length(hitPoint-viewPos))/ distanceLimit); - vec4 p = projection * vec4(hitPoint, 1.0); - - float k = 1.0 / p.w; - - vec2 pos = vec2(p.xy*k); - vec2 ad = vec2(ts/2- abs(pos - ts/2)); - float borderFade = 1.0; //smoothstep(0, borderWidth, min(ad.x, ad.y)); - - float l = 0.0; - int l0 = int(l); - int l1 = l0 + 1; - - float lf = l - l0; - - vec4 reflectedColor0 = texelFetch(colors, ivec2(p.xy*k)/(1< 10.0) { - traverse = direction*10.0; - worldCoordinate = cameraPosition - traverse; - } - - int steps = min(100, int(length(traverse) / 0.1)); - vec3 step = traverse / steps; - - vec3 marchPosition = worldCoordinate; - vec3 accumulated = inputColor; - float jitter = hash22(v_texCoord0).x; - marchPosition += jitter * step*0.5; - for (int stepIndex = 0; stepIndex < steps; ++stepIndex) { - float density = pow(abs(simplex31(marchPosition*0.25)), 4.0) * 0.1; - vec3 sh0; - gatherSH0(shMap, marchPosition, shMapDimensions, shMapOffset, shMapSpacing, sh0); - accumulated = accumulated * (1.0-density) + sh0 * density; - marchPosition += step; - } - o_output = vec4(accumulated, 1.0); -} diff --git a/orx-jvm/orx-file-watcher/README.md b/orx-jvm/orx-file-watcher/README.md deleted file mode 100644 index 2f1b43de..00000000 --- a/orx-jvm/orx-file-watcher/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# orx-file-watcher - -Monitor files on disk and auto-reload them if they change. - -## Usage - -Monitoring a single file. - -```kotlin -application { - program { - val watchedText = watchFile(File("someFile.txt")) { - it.readText() - } - extend { - val theText = watchedText() - } - } -} -``` - -Making a map of monitored files. - -```kotlin -application { - program { - val watchedTexts = mutableMapString>() - watchedTexts["text"] = watchFile(File("someFile.txt")) { - it.readText() - } - - extend { - val theText = watchedTexts.getValue("text")() - } - } -} -``` diff --git a/orx-jvm/orx-file-watcher/build.gradle.kts b/orx-jvm/orx-file-watcher/build.gradle.kts deleted file mode 100644 index 059fc78c..00000000 --- a/orx-jvm/orx-file-watcher/build.gradle.kts +++ /dev/null @@ -1,9 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(sharedLibs.kotlin.coroutines) -} \ No newline at end of file diff --git a/orx-jvm/orx-file-watcher/src/main/kotlin/FileWatcher.kt b/orx-jvm/orx-file-watcher/src/main/kotlin/FileWatcher.kt deleted file mode 100644 index 0351aab2..00000000 --- a/orx-jvm/orx-file-watcher/src/main/kotlin/FileWatcher.kt +++ /dev/null @@ -1,127 +0,0 @@ -package org.openrndr.extra.filewatcher - -import com.sun.nio.file.SensitivityWatchEventModifier -import io.github.oshai.kotlinlogging.KotlinLogging -import kotlinx.coroutines.* -import org.openrndr.events.Event -import java.io.File -import java.nio.file.FileSystems -import java.nio.file.Path -import java.nio.file.StandardWatchEventKinds -import java.nio.file.WatchKey -import java.util.WeakHashMap -import kotlin.concurrent.thread - -private val logger = KotlinLogging.logger {} - -private val watching = mutableMapOf>() -private val pathKeys = mutableMapOf() -private val keyPaths = WeakHashMap() -private val waiting = mutableMapOf() - -private val watchService by lazy { - FileSystems.getDefault().newWatchService() -} - -@OptIn(DelicateCoroutinesApi::class) -private val watchThread by lazy { - thread(isDaemon = true) { - while (true) { - val key = watchService.take() - val path = keyPaths[key] - - key.pollEvents().forEach { - val contextPath = it.context() as Path - val fullPath = path?.resolve(contextPath) - - fullPath?.let { - waiting[fullPath]?.cancel() - waiting[fullPath] = GlobalScope.launch { - delay(100) - watching[fullPath]?.forEach { w -> - w.triggerChange() - } - } - } - } - key.reset() - } - } -} - -/** - * @property file - * @property fileChangedEvent - * @param requestStopEvent - */ -class FileWatcher( - private val file: File, - private val fileChangedEvent: Event, - requestStopEvent: Event? = null -) { - private val path = file.absoluteFile.toPath() - private val parent = path.parent - private val key = pathKeys.getOrPut(parent) { - parent.register( - watchService, arrayOf(StandardWatchEventKinds.ENTRY_MODIFY), - SensitivityWatchEventModifier.HIGH - ) - } - - init { - watchThread - watching.getOrPut(path) { - mutableListOf() - }.add(this) - keyPaths.getOrPut(key) { parent } - requestStopEvent?.listenOnce { - stop() - } - } - - @Suppress("MemberVisibilityCanBePrivate") - fun stop() { - synchronized(watching) { - logger.info { "stopping, watcher stop requested" } - watching[path]?.remove(this) - } - } - - internal fun triggerChange() { - fileChangedEvent.trigger(file) - } -} - -/** - * Watch a file for changes - * @param file the file to watch - * @param valueChangedEvent the event that is triggered when the value (after transforming) has changed - * @param requestStopEvent an event that can be triggered to request the watcher to stop - * @param transducer a function that transforms a [File] into a value of type [R] - */ -fun watchFile( - file: File, - valueChangedEvent: Event? = null, - requestStopEvent: Event? = null, - transducer: (File) -> R -): () -> R { - var result = transducer(file) - val fileChangedEvent = Event() - - @Suppress("UNUSED_VARIABLE") val watcher = FileWatcher(file, fileChangedEvent, requestStopEvent) - - fileChangedEvent.listen { - @Suppress("MemberVisibilityCanBePrivate") - try { - result = transducer(file) - valueChangedEvent?.trigger(result) - } catch (e: Throwable) { - logger.error(e) { - """exception while transforming file ${file.absolutePath}""" - } - } - } - return { - result - } -} \ No newline at end of file diff --git a/orx-jvm/orx-file-watcher/src/main/kotlin/FileWatcherDelegate.kt b/orx-jvm/orx-file-watcher/src/main/kotlin/FileWatcherDelegate.kt deleted file mode 100644 index 54dd9653..00000000 --- a/orx-jvm/orx-file-watcher/src/main/kotlin/FileWatcherDelegate.kt +++ /dev/null @@ -1,61 +0,0 @@ -package org.openrndr.extra.filewatcher - -import kotlinx.coroutines.yield -import org.openrndr.Program -import org.openrndr.events.Event -import org.openrndr.launch -import java.io.File -import kotlin.reflect.KProperty - -/** - * Property delegator that watches a file. Changes are propagated right before the [Program] updates its extensions - * @param program the program to synchronise updates with - * @param file the file to watch - * @param valueChangedEvent the event that is triggered when the value (after transformation) has changed - * @param requestStopEvent an event that can be triggered to request the watcher to stop - * @since 0.4.3 - * @see watchingFile - */ -class FileWatcherDelegate( - program: Program, - file: File, - valueChangedEvent: Event? = null, - requestStopEvent: Event? = null, - transducer: (File) -> T -) { - private val watchValue = watchFile(file, valueChangedEvent, requestStopEvent, transducer) - private var value = watchValue() - - init { - // make sure that `value` is updated at the beginning of a draw cycle and not mid-cycle. - program.launch { - while (true) { - value = watchValue() - yield() - } - } - } - - /** - * Return transformed value - */ - operator fun getValue(any: Any?, property: KProperty<*>): T { - return value - } -} - -/** - * Delegate value to a file watcher - * @param file the file to watch - * @param valueChangedEvent the event that is triggered when the value (after transformation) has changed - * @param requestStopEvent an event that can be triggered to request the watcher to stop - * @param transducer a function that transforms a [File] into a value of type [R] - * @since 0.4.3 - * @see FileWatcherDelegate - */ -fun Program.watchingFile( - file: File, - valueChangedEvent: Event? = null, - requestStopEvent: Event? = null, - transducer: (File) -> R -) = FileWatcherDelegate(this, file, valueChangedEvent, requestStopEvent, transducer) diff --git a/orx-jvm/orx-git-archiver-gradle/README.md b/orx-jvm/orx-git-archiver-gradle/README.md deleted file mode 100644 index 9dff40ce..00000000 --- a/orx-jvm/orx-git-archiver-gradle/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# orx-git-archive-gradle - -A Gradle plugin that turns a git history and `screenshots` directory into a markdown file. - -## Usage - -`openrndr-template` uses this plugin by default. All you need to do is use [orx-git-archiver](../orx-git-archiver) such -that screenshots will have the Git commit id in their filename. - diff --git a/orx-jvm/orx-git-archiver-gradle/build.gradle.kts b/orx-jvm/orx-git-archiver-gradle/build.gradle.kts deleted file mode 100644 index f707d567..00000000 --- a/orx-jvm/orx-git-archiver-gradle/build.gradle.kts +++ /dev/null @@ -1,60 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") - `java-gradle-plugin` -} - -apply(plugin = "maven-publish") - -dependencies { - implementation(project(":orx-jvm:orx-git-archiver")) -} - -gradlePlugin { - plugins { - create("gitArchiveToMarkdown") { - id = "org.openrndr.extra.gitarchiver.tomarkdown" - implementationClass = "org.openrndr.extra.gitarchiver.GitArchiveToMarkdown" - } - } -} - -publishing { - afterEvaluate { - publications { - withType(MavenPublication::class) { - pom { - name.set("gitarchiver tomarkdown") - description.set("gitarchiver to markdown gradle plugin") - url.set("https://openrndr.org") - developers { - developer { - id.set("edwinjakobs") - name.set("Edwin Jakobs") - email.set("edwin@openrndr.org") - } - } - - licenses { - license { - name.set("BSD-2-Clause") - url.set("https://github.com/openrndr/openrndr/blob/master/LICENSE") - distribution.set("repo") - } - } - - scm { - connection.set("scm:git:git@github.com:openrndr/orx.git") - developerConnection.set("scm:git:ssh://github.com/openrndr/orx.git") - url.set("https://github.com/openrndr/openrndr") - } - } - } - } - } - -} -val isReleaseVersion = !(version.toString()).endsWith("SNAPSHOT") -signing { - setRequired({ isReleaseVersion && gradle.taskGraph.hasTask("publish") }) - sign(publishing.publications) -} \ No newline at end of file diff --git a/orx-jvm/orx-git-archiver-gradle/src/main/kotlin/GitArchiveToMarkdown.kt b/orx-jvm/orx-git-archiver-gradle/src/main/kotlin/GitArchiveToMarkdown.kt deleted file mode 100644 index da02b1aa..00000000 --- a/orx-jvm/orx-git-archiver-gradle/src/main/kotlin/GitArchiveToMarkdown.kt +++ /dev/null @@ -1,59 +0,0 @@ -package org.openrndr.extra.gitarchiver - -import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.* -import org.gradle.work.InputChanges -import java.io.File -import javax.inject.Inject - -abstract class GitArchiveToMarkdown @Inject constructor() : DefaultTask() { - @get:OutputDirectory - abstract val outputDir: DirectoryProperty - - @get:InputDirectory - abstract val gitDir: DirectoryProperty - - @get:InputDirectory - abstract val screenshotsDir: DirectoryProperty - - @get:Input - abstract val historySize: Property - - @TaskAction - fun execute(inputChanges: InputChanges) { - val parent = outputDir.asFile.get() - - val git = GitProvider.create() - val references = git.logReferences(historySize.get()) - - val text = references.map { reference -> - val screenshots = screenshotsDir.asFile.get().listFiles().filter { file -> - file.extension == "png" && file.name.contains(reference) - } - println(screenshots) - screenshots.forEach { - it.copyTo(File(outputDir.asFile.get(), it.name)) - } - val screenShotsMD = screenshots.map { - "![${it.nameWithoutExtension}](${it.name})" - }.joinToString("\n") - - """# $reference - |$screenShotsMD - |``` - |${git.show(reference)}} - |``` - """.trimMargin() - }.joinToString("\n") - File(parent, "README.md").writeText(text) - } - - init { - outputDir.set(File("build/git-archive-markdown")) - gitDir.set(File(".git")) - screenshotsDir.set(File("screenshots")) - historySize.set(20) - } -} \ No newline at end of file diff --git a/orx-jvm/orx-git-archiver/README.md b/orx-jvm/orx-git-archiver/README.md deleted file mode 100644 index a9b6607f..00000000 --- a/orx-jvm/orx-git-archiver/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# orx-git-archiver - -An extension that hooks into `Program.requestAssets` to commit -changed code to Git and provide filenames based on the commit hash. - -## How do I use it? - -```kotlin -application { - program { - extend(GitArchiver()) { - - - } - extend(Screenshots()) - } -} -``` -Now when a screenshot is taken, first all uncommitted code is committed to git. -The screenshot is saved with the first 7 characters of the commit hash in the filename. - -## Demos \ No newline at end of file diff --git a/orx-jvm/orx-git-archiver/build.gradle.kts b/orx-jvm/orx-git-archiver/build.gradle.kts deleted file mode 100644 index 9d9b978c..00000000 --- a/orx-jvm/orx-git-archiver/build.gradle.kts +++ /dev/null @@ -1,13 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(libs.jgit) - demoImplementation(project(":orx-mesh-generators")) - demoImplementation(project(":orx-noise")) - demoImplementation(openrndr.ffmpeg) - demoImplementation(openrndr.filter) -} \ No newline at end of file diff --git a/orx-jvm/orx-git-archiver/src/demo/kotlin/GitArchiverDemo01.kt b/orx-jvm/orx-git-archiver/src/demo/kotlin/GitArchiverDemo01.kt deleted file mode 100644 index 94ed7ba2..00000000 --- a/orx-jvm/orx-git-archiver/src/demo/kotlin/GitArchiverDemo01.kt +++ /dev/null @@ -1,19 +0,0 @@ -// -//import org.openrndr.application -//import org.openrndr.extensions.Screenshots -//import org.openrndr.extra.gitarchiver.GitArchiver -// -//fun main() = application { -// program { -// val ga = extend(GitArchiver()) { -// commitOnRun = true -// commitOnRequestAssets = false -// } -// extend(Screenshots()) -// extend { -// -// -// } -// } -// -//} \ No newline at end of file diff --git a/orx-jvm/orx-git-archiver/src/main/kotlin/GitArchiver.kt b/orx-jvm/orx-git-archiver/src/main/kotlin/GitArchiver.kt deleted file mode 100644 index eb719797..00000000 --- a/orx-jvm/orx-git-archiver/src/main/kotlin/GitArchiver.kt +++ /dev/null @@ -1,67 +0,0 @@ -package org.openrndr.extra.gitarchiver - -import io.github.oshai.kotlinlogging.KotlinLogging -import org.openrndr.AssetMetadata -import org.openrndr.Extension -import org.openrndr.Program - -interface GitProvider { - fun commitChanges(commitMessage: String) - fun headReference(): String - fun logReferences(count: Int): List - fun show(reference: String) : String - - companion object { - fun create() : GitProvider { - return if (nativeGitInstalled()) NativeGit() else JavaGit() - } - } -} - -val logger = KotlinLogging.logger { } - -class GitArchiver : Extension { - override var enabled: Boolean = true - - var commitOnRun = false - var commitOnRequestAssets = true - - var autoCommitMessage = "auto commit" - - private val git: GitProvider = GitProvider.create() - - override fun setup(program: Program) { - logger.info { - "Using ${ - when (git) { - is NativeGit -> "native Git" - is JavaGit -> "Java Git" - else -> "unknown Git" - } - }" - } - - autoCommitMessage = "auto commit from ${program.name}" - - val oldMetadataFunction = program.assetMetadata - program.assetMetadata = { - val oldMetadata = oldMetadataFunction() - val commitHash = git.headReference() - program.assetProperties["git-commit-hash"] = commitHash - AssetMetadata( - oldMetadata.programName, - "${oldMetadata.assetBaseName}-$commitHash", - program.assetProperties.mapValues { it.value }) - } - - program.requestAssets.listeners.add(0, { - if (commitOnRequestAssets) { - git.commitChanges(autoCommitMessage) - } - }) - - if (commitOnRun) { - git.commitChanges(autoCommitMessage) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-git-archiver/src/main/kotlin/JavaGit.kt b/orx-jvm/orx-git-archiver/src/main/kotlin/JavaGit.kt deleted file mode 100644 index 7b1cca9b..00000000 --- a/orx-jvm/orx-git-archiver/src/main/kotlin/JavaGit.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.openrndr.extra.gitarchiver - -import org.eclipse.jgit.api.Git -import org.eclipse.jgit.api.errors.EmptyCommitException -import org.eclipse.jgit.lib.Constants -import org.eclipse.jgit.storage.file.FileRepositoryBuilder -import java.io.File - -class JavaGit : GitProvider { - private val repo = FileRepositoryBuilder().setGitDir(File("./.git")).build() - private val git = Git(repo) - - override fun commitChanges(commitMessage: String) { - try { - git.commit().setAll(true).setAllowEmpty(false).setMessage(commitMessage).call() - logger.info { "git repository is now at ${headReference()}" } - } catch (e: EmptyCommitException) { - logger.info { "no changes" } - } - } - - override fun headReference() : String { - val id = repo.resolve(Constants.HEAD) - return id.name.take(7) - } - - override fun logReferences(count: Int): List { - TODO("Not yet implemented") - } - - override fun show(reference: String): String { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/orx-jvm/orx-git-archiver/src/main/kotlin/NativeGit.kt b/orx-jvm/orx-git-archiver/src/main/kotlin/NativeGit.kt deleted file mode 100644 index 47ee0e58..00000000 --- a/orx-jvm/orx-git-archiver/src/main/kotlin/NativeGit.kt +++ /dev/null @@ -1,62 +0,0 @@ -package org.openrndr.extra.gitarchiver - -import java.io.File -import java.io.IOException -import java.util.concurrent.TimeUnit - -private val dir = File(".") - -class NativeGit : GitProvider { - override fun commitChanges(commitMessage: String) { - val gitStatus = listOf("git", "status", "--porcelain").runCommand(dir)!! - if (gitStatus.first.isNotBlank()){ - if (gitStatus.first.contains("Not a git repository")){ - logger.error { "Can't commit changes because the working directory is not a git repository" } - } else { - listOf("git", "add", ".").runCommand(dir) - listOf("git", "commit", "-m", commitMessage).runCommand(dir) - logger.info { "git repository is at ${headReference()} after commit" } - } - } else { - logger.info { "no changes" } - } - } - - override fun headReference(): String { - return listOf("git", "rev-parse", "--short", "HEAD").runCommand(dir)!!.first.trimEnd() - } - - override fun logReferences(count: Int): List { - val (out, err) = listOf("git", "log", "-$count", "--pretty=format:%h").runCommand(dir) ?: error("failed to get log references") - return out.split("\n").map { it.trim() } - } - - override fun show(reference: String): String { - val (out, err) = listOf("git", "show", reference, "-U0").runCommand(dir) ?: error("failed to get diff") - return out - } - - -} - -internal fun nativeGitInstalled(): Boolean { - return listOf("git", "--version").runCommand(dir) != null -} - -// Adapted from https://stackoverflow.com/questions/35421699/how-to-invoke-external-command-from-within-kotlin-code -private fun List.runCommand(workingDir: File): Pair? { - try { - val proc = ProcessBuilder(*toTypedArray()) - .directory(workingDir) - .redirectOutput(ProcessBuilder.Redirect.PIPE) - .redirectError(ProcessBuilder.Redirect.PIPE) - .start() - - proc.waitFor(60, TimeUnit.MINUTES) - return Pair(proc.inputStream.bufferedReader().readText(), proc.errorStream.bufferedReader().readText()) - } catch(e: IOException) { - logger.error { e.message } - e.printStackTrace() - return null - } -} \ No newline at end of file diff --git a/orx-jvm/orx-gui/README.md b/orx-jvm/orx-gui/README.md deleted file mode 100644 index 8c21fd00..00000000 --- a/orx-jvm/orx-gui/README.md +++ /dev/null @@ -1,294 +0,0 @@ -# orx-gui - -Automatic UI (sliders, buttons, etc.) generated from annotated classes and properties. Uses `orx-panel` and `orx-parameters`. - -A quick-and-dirty user interface toolkit. - -`orx-gui` uses class and property annotations to generate simple interfaces. The annotations used -are provided by [`orx-parameters`](../orx-parameters/README.md) and most filters in [`orx-fx`](../orx-fx/README.md) have been annotated. - -`orx-gui` is made with an [`orx-olive`](../orx-olive/README.md) workflow in mind but can be used in normal OPENRNDR programs -just as well. - -## Usage - -Preparation: make sure `orx-gui` is in the `orxFeatures` of your project (if you working on a template based project) - -The essence of `orx-gui` lies in the provided a `GUI` extension, which can be used in your program using the `extend {}` function. -The `GUI` class has an `add()` function that allows any annotated object to be passed in. - -The visibility of the side bar can be toggled by pressing the F11 key on your keyboard. - -### UIs for parameter objects - -A simple UI can be created by creating an annotated `object`. - -```kotlin -import org.openrndr.application -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.parameters.* -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 - -enum class Option { - Option1, - Option2, - Option3 -} - - -fun main() = application { - program { - // -- this @Description annotation is optional - val parameters = @Description("parameters") object { - @DoubleParameter("radius", 20.0, 200.0, precision = 2, order = 0) - var radius = 50.0 - - @TextParameter("A string", order = 1) - var s = "Hello" - - @BooleanParameter("A bool", order = 2) - var b = true - - @IntParameter("An int", 0, 127, order = 3) - var i = 64 - - @ColorParameter("A fill color", order = 4) - var fill = ColorRGBa.PINK - - @XYParameter("Position", minX = 0.0, maxX = 640.0, - minY = 0.0, maxY = 480.0, order = 5) - var pos = Vector2.ZERO - - @Vector2Parameter("A Vector2", order = 6) - var v2 = Vector2(200.0, 200.0) - - @Vector3Parameter("A Vector3", order = 7) - var v3 = Vector3(200.0, 200.0, 200.0) - - @Vector4Parameter("A Vector4", order = 8) - var v4 = Vector4(200.0, 200.0, 200.0, 200.0) - - @DoubleListParameter("Mixer", order = 9) - var mixer = MutableList(5) { 0.5 } - - @ActionParameter("Action test", order = 10) - fun clicked() { - println("GUI says hi!") - } - - @OptionParameter("An option", order = 11) - var option = Option.Option1 - } - - extend(GUI()) { - add(parameters) - } - extend { - drawer.fill = parameters.fill - drawer.circle(parameters.pos, parameters.radius) - } - } -} -``` - -### UIs for filters - -In a similar fashion to the previous example we can create a simple UI for most filters in `orx-fx` - -```kotlin -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.isolatedWithTarget -import org.openrndr.draw.renderTarget -import org.openrndr.extra.fx.blur.BoxBlur -import org.openrndr.extra.gui.GUI - -fun main() = application { - program { - val blur = BoxBlur() - val rt = renderTarget(width, height) { - colorBuffer() - } - extend(GUI()) { - add(blur) - } - extend { - drawer.isolatedWithTarget(rt) { - drawer.background(ColorRGBa.BLACK) - drawer.fill = ColorRGBa.PINK - drawer.circle(width / 2.0, height / 2.0, 200.0) - } - blur.apply(rt.colorBuffer(0), rt.colorBuffer(0)) - drawer.image(rt.colorBuffer(0)) - } - } -} -``` - -### UIs in Olive - -Using `orx-gui` in Olive (`orx-olive`) is very similar to how one would use it in a normal OPENRNDR program. There is -one detail that doesn't occur in normal programs: the UI state is reset when a -script is changed and re-evaluated. This is overcome by using an annotated `Reloadable` object. - -An example `live.kts` script that uses `orx-gui` and `Reloadable`: - -```kotlin -@file:Suppress("UNUSED_LAMBDA_EXPRESSION") -import org.openrndr.Program -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.olive.Reloadable -import org.openrndr.extra.parameters.DoubleParameter - -{ program: Program -> - program.apply { - val p = object : Reloadable() { - @DoubleParameter("x-position", 0.0, 640.0, order = 0) - var x = 0.5 - @DoubleParameter("y-position", 0.0, 480.0, order = 1) - var y = 0.5 - @DoubleParameter("radius", 0.0, 480.0, order = 2) - var radius = 100.0 - } - p.reload() - - extend(GUI()) { - add(p) - } - extend { - drawer.circle(p.x, p.y, p.radius) - } - } -} -``` - -## Credits - -`orx-gui` is based on a proof-of-concept by [Ricardo Matias](https://github.com/ricardomatias/) - -## Demos -### DemoAppearance01 - -Demonstrates how to customize the appearance of the GUI by using -`GUIAppearance()`. - -In this demo, we make the GUI wider (400 pixels) and translucent. - -![DemoAppearance01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-gui/images/DemoAppearance01Kt.png) - -[source code](src/demo/kotlin/DemoAppearance01.kt) - -### DemoHide01 - -Demonstrates how to hide the GUI when the mouse pointer is outside of it. - -![DemoHide01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-gui/images/DemoHide01Kt.png) - -[source code](src/demo/kotlin/DemoHide01.kt) - -### DemoOptions01 - -A simple demonstration of a GUI with a drop-down menu. - -The entries in the drop-down menu are taken from an `enum class`. - -![DemoOptions01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-gui/images/DemoOptions01Kt.png) - -[source code](src/demo/kotlin/DemoOptions01.kt) - -### DemoOptions02 - -A simple demonstration of a GUI with a drop-down menu. - -The entries in the drop-down menu are taken from an `enum class`. -The `enum class` entries contain both a name (used in the drop-down) -and a `ColorRGBa` instance (used for rendering). - -![DemoOptions02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-gui/images/DemoOptions02Kt.png) - -[source code](src/demo/kotlin/DemoOptions02.kt) - -### DemoPath01 - -Demonstrates how to include a button for loading images in a GUI, and how to display -the loaded image. - -The program applies the `@PathParameter` annotation to a `String` variable, which gets -rendered by the GUI as an image-picker button. Note the allowed file `extensions`. - -This mechanism only updates the `String` containing the path of an image file. - -The `watchingImagePath()` delegate property is used to automatically load an image -when its `String` argument changes. - -![DemoPath01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-gui/images/DemoPath01Kt.png) - -[source code](src/demo/kotlin/DemoPath01.kt) - -### DemoPresets01 - -Shows how to store and retrieve in-memory GUI presets, -each containing two integer values and two colors. - -Keyboard controls: -[Left Shift] + [0]..[9] => store current GUI values to a preset -[0]..[9] => recall a preset - -![DemoPresets01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-gui/images/DemoPresets01Kt.png) - -[source code](src/demo/kotlin/DemoPresets01.kt) - -### DemoSideCanvas01 - -Demonstrates the `GUI.enableSideCanvas` feature. - -When set to true, the `GUI` provides a `canvas` property where one can draw. -The size of this canvas is the window size minus the GUI size. - -That's why if we draw a circle at `drawer.width / 2.0` it is centered -on the `canvas`, not on the window. - -This demo sets the window to resizable, so if you resize the window -you should see tha the circle stays at the center of the canvas. - - -![DemoSideCanvas01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-gui/images/DemoSideCanvas01Kt.png) - -[source code](src/demo/kotlin/DemoSideCanvas01.kt) - -### DemoSimple01 - -Demonstrates how to create a simple GUI with 4 inputs: -- A `ColorParameter` which creates a color picker. -- A `DoubleParameter` to control the radius of a circle. -- A `Vector2Parameter` to set the position of that circle. -- A `DoubleListParameter` which sets the radii of six circles. - -The demo also shows how to use the variables controlled by the GUI -inside the program, so changes to those variables affect -the rendering in real time. - -![DemoSimple01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-gui/images/DemoSimple01Kt.png) - -[source code](src/demo/kotlin/DemoSimple01.kt) - -### DemoXYParameter - -Demonstrates the use of the `@XYParameter` annotation applied to a `Vector2` variable. - -This annotation creates an interactive XY control in a GUI that can be used to update -a `Vector2` variable. In this demo it sets the position of a circle. - - -![DemoXYParameterKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-gui/images/DemoXYParameterKt.png) - -[source code](src/demo/kotlin/DemoXYParameter.kt) diff --git a/orx-jvm/orx-gui/build.gradle.kts b/orx-jvm/orx-gui/build.gradle.kts deleted file mode 100644 index ca8d9d00..00000000 --- a/orx-jvm/orx-gui/build.gradle.kts +++ /dev/null @@ -1,18 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - api(project(":orx-parameters")) - api(project(":orx-jvm:orx-panel")) - api(project(":orx-noise")) - demoImplementation(project(":orx-property-watchers")) - implementation(sharedLibs.kotlin.coroutines) - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(openrndr.filter) - implementation(openrndr.dialogs) - implementation(libs.gson) - implementation(sharedLibs.kotlin.reflect) - demoRuntimeOnly(sharedLibs.slf4j.simple) -} \ No newline at end of file diff --git a/orx-jvm/orx-gui/src/demo/kotlin/DemoAppearance01.kt b/orx-jvm/orx-gui/src/demo/kotlin/DemoAppearance01.kt deleted file mode 100644 index 7df69064..00000000 --- a/orx-jvm/orx-gui/src/demo/kotlin/DemoAppearance01.kt +++ /dev/null @@ -1,52 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.gui.GUIAppearance -import org.openrndr.extra.parameters.* -import org.openrndr.math.Vector2 -import org.openrndr.shape.Circle - -/** - * Demonstrates how to customize the appearance of the GUI by using - * `GUIAppearance()`. - * - * In this demo, we make the GUI wider (400 pixels) and translucent. - */ -fun main() = application { - program { - val gui = GUI(GUIAppearance(baseColor = ColorRGBa.GRAY.opacify(0.9), barWidth = 400)) - gui.compartmentsCollapsedByDefault = false - - val settings = @Description("Settings") object { - @DoubleParameter("radius", 0.0, 100.0) - var radius = 50.0 - - @Vector2Parameter("position", 0.0, 1.0) - var position = Vector2(0.6, 0.5) - - @ColorParameter("color") - var color = ColorRGBa.PINK - - @DoubleListParameter("radii", 5.0, 30.0) - var radii = mutableListOf(5.0, 6.0, 8.0, 14.0, 20.0, 30.0) - } - gui.add(settings) - extend(gui) - - // note we can only change the visibility after the extend - gui.visible = true - - extend { - // determine visibility through mouse x-coordinate - //gui.visible = mouse.position.x < gui.appearance.barWidth - - drawer.fill = settings.color - drawer.circle(settings.position * drawer.bounds.position(1.0, 1.0), settings.radius) - drawer.circles( - settings.radii.mapIndexed { i, radius -> - Circle(width - 50.0, 60.0 + i * 70.0, radius) - } - ) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-gui/src/demo/kotlin/DemoHide01.kt b/orx-jvm/orx-gui/src/demo/kotlin/DemoHide01.kt deleted file mode 100644 index 740e328e..00000000 --- a/orx-jvm/orx-gui/src/demo/kotlin/DemoHide01.kt +++ /dev/null @@ -1,48 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.parameters.* -import org.openrndr.math.Vector2 -import org.openrndr.shape.Circle - -/** - * Demonstrates how to hide the GUI when the mouse pointer is outside of it. - */ -fun main() = application { - program { - val gui = GUI() - gui.compartmentsCollapsedByDefault = false - - val settings = @Description("Settings") object { - @DoubleParameter("radius", 0.0, 100.0) - var radius = 50.0 - - @Vector2Parameter("position", 0.0, 1.0) - var position = Vector2(0.6, 0.5) - - @ColorParameter("color") - var color = ColorRGBa.PINK - - @DoubleListParameter("radii", 5.0, 30.0) - var radii = mutableListOf(5.0, 6.0, 8.0, 14.0, 20.0, 30.0) - } - gui.add(settings) - extend(gui) - - // note we can only change the visibility after the `extend` - gui.visible = false - - extend { - // determine visibility through mouse x-coordinate - gui.visible = mouse.position.x < 200.0 - - drawer.fill = settings.color - drawer.circle(settings.position * drawer.bounds.position(1.0, 1.0), settings.radius) - drawer.circles( - settings.radii.mapIndexed { i, radius -> - Circle(width - 50.0, 60.0 + i * 70.0, radius) - } - ) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-gui/src/demo/kotlin/DemoMultiWindow01.kt b/orx-jvm/orx-gui/src/demo/kotlin/DemoMultiWindow01.kt deleted file mode 100644 index a81ede39..00000000 --- a/orx-jvm/orx-gui/src/demo/kotlin/DemoMultiWindow01.kt +++ /dev/null @@ -1,32 +0,0 @@ -import org.openrndr.WindowConfiguration -import org.openrndr.application -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.parameters.* -import org.openrndr.window -import kotlin.system.exitProcess - -/** - * Demonstration of a multi window GUI in the manual way - */ -fun main() { - // skip this demo on CI - if (System.getProperty("takeScreenshot") == "true") { - exitProcess(0) - } - application { - program { - val settings = object { - @DoubleParameter("radius", 10.0, 100.0) - var radius = 10.0 - } - window(WindowConfiguration(width = 200, resizable = true)) { - val gui = GUI() - gui.add(settings) - extend(gui) - } - extend { - drawer.circle(drawer.bounds.center, settings.radius) - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-gui/src/demo/kotlin/DemoMultiWindow02.kt b/orx-jvm/orx-gui/src/demo/kotlin/DemoMultiWindow02.kt deleted file mode 100644 index d351b2ca..00000000 --- a/orx-jvm/orx-gui/src/demo/kotlin/DemoMultiWindow02.kt +++ /dev/null @@ -1,29 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.gui.WindowedGUI -import org.openrndr.extra.parameters.DoubleParameter -import kotlin.system.exitProcess - -/** - * Demonstration of a multi window GUI using the `WindowedGUI` extension - */ -fun main() { - // skip this demo on CI - if (System.getProperty("takeScreenshot") == "true") { - exitProcess(0) - } - application { - program { - val settings = object { - @DoubleParameter("radius", 10.0, 100.0) - var radius = 10.0 - } - val gui = WindowedGUI() - gui.add(settings) - extend(gui) - - extend { - drawer.circle(drawer.bounds.center, settings.radius) - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-gui/src/demo/kotlin/DemoOptions01.kt b/orx-jvm/orx-gui/src/demo/kotlin/DemoOptions01.kt deleted file mode 100644 index b9e2ee12..00000000 --- a/orx-jvm/orx-gui/src/demo/kotlin/DemoOptions01.kt +++ /dev/null @@ -1,45 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.OptionParameter - -/** - * A simple demonstration of a GUI with a drop-down menu. - * - * The entries in the drop-down menu are taken from an `enum class`. - */ - -enum class BackgroundColors { - Pink, - Black, - Yellow -} - -fun main() = application { - configure { - width = 720 - height = 360 - } - program { - val gui = GUI() - gui.compartmentsCollapsedByDefault = false - val settings = @Description("Settings") object { - @OptionParameter("Background color") - var option = BackgroundColors.Pink - } - - gui.add(settings) - extend(gui) - gui.onChange { name, value -> - println("$name: $value") - } - extend { - when (settings.option) { - BackgroundColors.Pink -> drawer.clear(ColorRGBa.PINK) - BackgroundColors.Black -> drawer.clear(ColorRGBa.BLACK) - BackgroundColors.Yellow -> drawer.clear(ColorRGBa.YELLOW) - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-gui/src/demo/kotlin/DemoOptions02.kt b/orx-jvm/orx-gui/src/demo/kotlin/DemoOptions02.kt deleted file mode 100644 index b31921ed..00000000 --- a/orx-jvm/orx-gui/src/demo/kotlin/DemoOptions02.kt +++ /dev/null @@ -1,43 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.OptionParameter - -/** - * A simple demonstration of a GUI with a drop-down menu. - * - * The entries in the drop-down menu are taken from an `enum class`. - * The `enum class` entries contain both a name (used in the drop-down) - * and a `ColorRGBa` instance (used for rendering). - */ - -enum class BackgroundColors2(val color: ColorRGBa) { - Pink(ColorRGBa.PINK), - Black(ColorRGBa.BLACK), - Yellow(ColorRGBa.YELLOW) -} - -fun main() = application { - configure { - width = 720 - height = 360 - } - program { - val gui = GUI() - gui.compartmentsCollapsedByDefault = false - val settings = @Description("Settings") object { - @OptionParameter("Background color") - var option = BackgroundColors2.Pink - } - - gui.add(settings) - extend(gui) - gui.onChange { name, value -> - println("$name: $value") - } - extend { - drawer.clear(settings.option.color) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-gui/src/demo/kotlin/DemoPath01.kt b/orx-jvm/orx-gui/src/demo/kotlin/DemoPath01.kt deleted file mode 100644 index b4819353..00000000 --- a/orx-jvm/orx-gui/src/demo/kotlin/DemoPath01.kt +++ /dev/null @@ -1,38 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.PathParameter -import org.openrndr.extra.propertywatchers.watchingImagePath - -/** - * Demonstrates how to include a button for loading images in a GUI, and how to display - * the loaded image. - * - * The program applies the `@PathParameter` annotation to a `String` variable, which gets - * rendered by the GUI as an image-picker button. Note the allowed file `extensions`. - * - * This mechanism only updates the `String` containing the path of an image file. - * - * The `watchingImagePath()` delegate property is used to automatically load an image - * when its `String` argument changes. - */ -fun main() = application { - program { - val gui = GUI() - gui.compartmentsCollapsedByDefault = false - - val settings = @Description("Settings") object { - @PathParameter("image", extensions = ["jpg", "png"], order = 10) - var imagePath = "demo-data/images/image-001.png" - - val image by watchingImagePath(::imagePath) { - it - } - } - gui.add(settings) - extend(gui) - extend { - drawer.image(settings.image) - } - } -} diff --git a/orx-jvm/orx-gui/src/demo/kotlin/DemoPresets01.kt b/orx-jvm/orx-gui/src/demo/kotlin/DemoPresets01.kt deleted file mode 100644 index 01767da0..00000000 --- a/orx-jvm/orx-gui/src/demo/kotlin/DemoPresets01.kt +++ /dev/null @@ -1,72 +0,0 @@ -import org.openrndr.KeyModifier -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.IntParameter - -/** - * Shows how to store and retrieve in-memory GUI presets, - * each containing two integer values and two colors. - * - * Keyboard controls: - * [Left Shift] + [0]..[9] => store current GUI values to a preset - * [0]..[9] => recall a preset - */ -fun main() = application { - configure { - width = 720 - height = 480 - } - program { - val gui = GUI() - gui.compartmentsCollapsedByDefault = false - - val presets = MutableList(10) { - gui.toObject() - } - - val settings = @Description("Settings") object { - @IntParameter("a", 1, 10) - var a = 7 - - @IntParameter("b", 1, 10) - var b = 3 - - @ColorParameter("foreground") - var foreground = ColorRGBa.fromHex("654062") - - @ColorParameter("background") - var background = ColorRGBa.fromHex("ff9c71") - } - gui.add(settings) - extend(gui) - extend { - drawer.clear(settings.background) - drawer.stroke = settings.background - drawer.fill = settings.foreground - // Draw a pattern based on modulo - for (i in 0 until 100) { - if (i % settings.a == 0 || i % settings.b == 0) { - val x = (i % 10) * 72.0 - val y = (i / 10) * 48.0 - drawer.rectangle(x, y, 72.0, 48.0) - } - } - } - keyboard.keyDown.listen { - when (it.name) { - in "0".."9" -> { - if (KeyModifier.SHIFT in it.modifiers) { - // 1. Get the current gui state, store it in a list - presets[it.name.toInt()] = gui.toObject() - } else { - // 2. Set the gui state - gui.fromObject(presets[it.name.toInt()]) - } - } - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-gui/src/demo/kotlin/DemoSideCanvas01.kt b/orx-jvm/orx-gui/src/demo/kotlin/DemoSideCanvas01.kt deleted file mode 100644 index d2b4bf8f..00000000 --- a/orx-jvm/orx-gui/src/demo/kotlin/DemoSideCanvas01.kt +++ /dev/null @@ -1,53 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.gui.GUIAppearance -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.panel.elements.draw - - -/** - * Demonstrates the `GUI.enableSideCanvas` feature. - * - * When set to true, the `GUI` provides a `canvas` property where one can draw. - * The size of this canvas is the window size minus the GUI size. - * - * That's why if we draw a circle at `drawer.width / 2.0` it is centered - * on the `canvas`, not on the window. - * - * This demo sets the window to resizable, so if you resize the window - * you should see tha the circle stays at the center of the canvas. - * - */ -fun main() = application { - configure { - width = 720 - height = 720 - windowResizable = true - } - - program { - val gui = GUI(GUIAppearance(baseColor = ColorRGBa.GRAY.shade(0.25))) - gui.compartmentsCollapsedByDefault = false - gui.enableSideCanvas = true - - val settings = @Description("Settings") object { - @DoubleParameter("radius", 0.0, 200.0) - var radius = 50.0 - - @ColorParameter("color") - var color = ColorRGBa.PINK - } - gui.add(settings) - extend(gui) - - gui.canvas?.draw { - val width = drawer.width - val height = drawer.height - drawer.fill = settings.color - drawer.circle(width / 2.0, height / 2.0, settings.radius) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-gui/src/demo/kotlin/DemoSimple01.kt b/orx-jvm/orx-gui/src/demo/kotlin/DemoSimple01.kt deleted file mode 100644 index e8c084bc..00000000 --- a/orx-jvm/orx-gui/src/demo/kotlin/DemoSimple01.kt +++ /dev/null @@ -1,53 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.parameters.* -import org.openrndr.math.Vector2 -import org.openrndr.shape.Circle - -/** - * Demonstrates how to create a simple GUI with 4 inputs: - * - A `ColorParameter` which creates a color picker. - * - A `DoubleParameter` to control the radius of a circle. - * - A `Vector2Parameter` to set the position of that circle. - * - A `DoubleListParameter` which sets the radii of six circles. - * - * The demo also shows how to use the variables controlled by the GUI - * inside the program, so changes to those variables affect - * the rendering in real time. - */ -fun main() = application { - configure { - width = 720 - height = 450 - } - program { - val gui = GUI() - gui.compartmentsCollapsedByDefault = false - - val settings = @Description("Settings") object { - @DoubleParameter("radius", 0.0, 100.0) - var radius = 50.0 - - @Vector2Parameter("position", 0.0, 1.0) - var position = Vector2(0.6, 0.5) - - @ColorParameter("color") - var color = ColorRGBa.PINK - - @DoubleListParameter("radii", 5.0, 30.0) - var radii = mutableListOf(5.0, 6.0, 8.0, 14.0, 20.0, 30.0) - } - gui.add(settings) - extend(gui) - extend { - drawer.fill = settings.color - drawer.circle(settings.position * drawer.bounds.position(1.0, 1.0), settings.radius) - drawer.circles( - settings.radii.mapIndexed { i, radius -> - Circle(width - 50.0, 60.0 + i * 70.0, radius) - } - ) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-gui/src/demo/kotlin/DemoXYParameter.kt b/orx-jvm/orx-gui/src/demo/kotlin/DemoXYParameter.kt deleted file mode 100644 index 9ee97791..00000000 --- a/orx-jvm/orx-gui/src/demo/kotlin/DemoXYParameter.kt +++ /dev/null @@ -1,41 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.XYParameter -import org.openrndr.math.Vector2 - -/** - * Demonstrates the use of the `@XYParameter` annotation applied to a `Vector2` variable. - * - * This annotation creates an interactive XY control in a GUI that can be used to update - * a `Vector2` variable. In this demo it sets the position of a circle. - * - */ -fun main() = application { - configure { - width = 800 - height = 800 - } - - program { - val gui = GUI() - gui.compartmentsCollapsedByDefault = false - - val settings = @Description("Settings") object { - @XYParameter( - "Position", 0.0, 800.0, 0.0, 800.0, - precision = 2, - invertY = false, - showVector = true - ) - var position: Vector2 = Vector2(0.0, 0.0) - } - - gui.add(settings) - - extend(gui) - extend { - drawer.circle(settings.position, 50.0) - } - } -} diff --git a/orx-jvm/orx-gui/src/main/kotlin/Gui.kt b/orx-jvm/orx-gui/src/main/kotlin/Gui.kt deleted file mode 100644 index c500dbf4..00000000 --- a/orx-jvm/orx-gui/src/main/kotlin/Gui.kt +++ /dev/null @@ -1,1226 +0,0 @@ -package org.openrndr.extra.gui - -import com.google.gson.Gson -import com.google.gson.JsonSyntaxException -import com.google.gson.reflect.TypeToken -import io.github.oshai.kotlinlogging.KotlinLogging -import org.openrndr.* -import org.openrndr.color.ColorRGBa -import org.openrndr.dialogs.* -import org.openrndr.draw.Drawer -import org.openrndr.extra.noise.uniform -import org.openrndr.extra.parameters.* -import org.openrndr.internal.Driver -import org.openrndr.math.* -import org.openrndr.panel.ControlManager -import org.openrndr.panel.controlManager -import org.openrndr.panel.elements.* -import org.openrndr.panel.style.* -import org.openrndr.panel.style.Display -import java.io.File -import kotlin.math.roundToInt -import kotlin.reflect.KMutableProperty1 - -/** Dear contributor, just in case you are here looking to add a new parameter type. -There is a 6-step incantation to add a new parameter type -0) Add your parameter type to orx-parameters, follow the instructions provided there. - -1) Set up a control style, very likely analogous to the styles already in place. -2) Add control creation code. -3) Add value serialization code, may need to update ParameterValue too. -4) Add value deserialization code. -5) Add value randomization code. -6) Add control update code. - -You can use your editor's search functionality to jump to "1)", "2)". - */ -private data class LabeledObject(val label: String, val obj: Any) - -private class CompartmentState(var collapsed: Boolean, val parameterValues: MutableMap = mutableMapOf()) -private class SidebarState(var hidden: Boolean = false, var collapsed: Boolean = false, var scrollTop: Double = 0.0) -private class TrackedObjectBinding( - val parameters: List, - val parameterControls: MutableMap = mutableMapOf() -) - -private val persistentCompartmentStates = mutableMapOf>() -private val persistentSidebarStates = mutableMapOf() - -private fun compartmentState(): MutableMap = persistentCompartmentStates.getOrPut(Driver.instance.contextID) { - mutableMapOf() -} - -private fun sidebarState(): SidebarState = persistentSidebarStates.getOrPut(Driver.instance.contextID) { - SidebarState() -} - -private fun getPersistedOrDefault( - compartmentLabel: String, - property: KMutableProperty1, - obj: Any -): T { - val state = compartmentState()[compartmentLabel] - if (state == null) { - return property.get(obj) - } else { - @Suppress("UNCHECKED_CAST") - return (state.parameterValues[property.name] as? T?) ?: return property.get(obj) - } -} - -private fun setAndPersist(compartmentLabel: String, property: KMutableProperty1, obj: Any, value: T) { - property.set(obj, value) - val state = compartmentState()[compartmentLabel] ?: error("item '$compartmentLabel' not in state (${compartmentState()}. ContextID ${Driver.instance.contextID} )") - state.parameterValues[property.name] = value -} - -private val logger = KotlinLogging.logger { } - - -class GUIAppearance( - val baseColor: ColorRGBa = ColorRGBa.GRAY.opacify(0.99), - val barWidth: Int = 200) - -@Suppress("unused", "UNCHECKED_CAST") -open class GUI( - val appearance: GUIAppearance = GUIAppearance(), - val defaultStyles: List = defaultStyles(), -) : Extension { - private var onChangeListener: ((name: String, value: Any?) -> Unit)? = null - override var enabled = true - - var listenToProduceAssetsEvent = true - - var visible = true - set(value) { - if (field != value) { - field = value - if (field) { - panel?.body?.classes?.remove(collapsed) - } else { - panel?.body?.classes?.add(collapsed) - } - sidebarState().hidden = !field - } - } - - var compartmentsCollapsedByDefault = true - var doubleBind = true - var defaultSaveFolder = "gui-parameters" - var persistState = true - var enableSideCanvas = false - var showToolbar = true - - var canvas: Canvas? = null - private var panel: ControlManager? = null - - // Randomize button - private var shiftDown = false - private var randomizeButton: Button? = null - - fun onChange(listener: (name: String, value: Any?) -> Unit) { - onChangeListener = listener - } - - val collapsed = ElementClass("collapsed") - - override fun setup(program: Program) { - if (persistState) { - val guiState = File(defaultSaveFolder, "${program.name}-latest.json") - if (guiState.exists()) { - loadParameters(guiState) - } - } - - program.produceAssets.listen { - if (listenToProduceAssetsEvent) { - val folderFile = File(defaultSaveFolder) - val targetFile = File(defaultSaveFolder, "${it.assetMetadata.assetBaseName}.json") - if (folderFile.exists() && folderFile.isDirectory) { - logger.info { "Saving parameters to '${targetFile.absolutePath}" } - saveParameters(targetFile) - } else { - if (folderFile.mkdirs()) { - logger.info { "Saving parameters to '${targetFile.absolutePath}" } - saveParameters(targetFile) - } else { - logger.error { "Could not save parameters because could not create directory ${folderFile.absolutePath}" } - } - } - } - } - - program.keyboard.keyDown.listen { - if (it.key == KEY_F11) { - visible = !visible - } - - if (it.key == KEY_LEFT_SHIFT) { - shiftDown = true - randomizeButton?.classes?.add(ElementClass("randomize-strong")) - } - } - - program.keyboard.keyUp.listen { - if (it.key == KEY_LEFT_SHIFT) { - shiftDown = false - randomizeButton?.classes?.remove(ElementClass("randomize-strong")) - } - } - - panel = program.controlManager(defaultStyles = defaultStyles) { - styleSheet(has class_ "fullscreen") { - this.width = 100.percent - this.height = 100.percent - this.flexDirection = FlexDirection.Row - this.display = Display.FLEX - } - styleSheet(has class_ "full-canvas") { - this.background = Color.RGBa(ColorRGBa.RED) - - this.flexShrink = FlexGrow.Ratio(1.0) - this.flexGrow = FlexGrow.Ratio(1.0) - this.height = 100.percent - this.width = 100.px - } - - styleSheet(has class_ "container") { - this.display = Display.FLEX - this.flexDirection = FlexDirection.Column - this.width = appearance.barWidth.px - this.height = 100.percent - } - - styleSheet(has class_ "collapse-border") { - this.display = Display.FLEX - this.flexDirection = FlexDirection.Column - this.height = 5.px - this.width = 100.percent - this.background = Color.RGBa(appearance.baseColor.shade(0.9)) - - and(has state "hover") { - this.background = Color.RGBa(appearance.baseColor.shade(1.1)) - } - } - - styleSheet(has class_ "toolbar") { - this.height = 42.px - this.width = 100.percent - this.display = Display.FLEX - this.flexDirection = FlexDirection.Row - this.background = Color.RGBa(appearance.baseColor) - } - - styleSheet(has class_ "collapsed") { - this.display = Display.NONE - } - - styleSheet(has class_ "compartment") { - this.paddingBottom = 20.px - } - - styleSheet(has class_ "sidebar") { - this.width = appearance.barWidth.px - this.paddingBottom = 20.px - this.paddingTop = 10.px - this.paddingLeft = 10.px - this.paddingRight = 10.px - this.marginRight = 2.px - this.height = 100.percent - this.background = Color.RGBa(appearance.baseColor) - this.overflow = Overflow.Scroll - - // - descendant(has type "colorpicker-button") { - this.width = (appearance.barWidth - 25).px - } - - descendant(has type "slider") { - this.width = (appearance.barWidth - 25).px - } - - descendant(has type "button") { - this.width = (appearance.barWidth - 25).px - } - - descendant(has type "textfield") { - this.width = (appearance.barWidth - 25).px - } - - descendant(has type "toggle") { - this.width = (appearance.barWidth - 25).px - } - - descendant(has type "xy-pad") { - this.width = (appearance.barWidth - 25).px - this.height = (appearance.barWidth - 25).px - } - - descendant( - has type listOf( - "sequence-editor", - "sliders-vector2", - "sliders-vector3", - "sliders-vector4" - ) - ) { - this.width = (appearance.barWidth - 25).px - this.height = 100.px - } - // - } - - styleSheet(has class_ "randomize-strong") { - color = Color.RGBa(ColorRGBa.PINK) - - and(has state "hover") { - color = Color.RGBa(ColorRGBa.BLACK) - background = Color.RGBa(ColorRGBa.PINK) - } - } - - styleSheet(has type "dropdown-button") { - this.width = 175.px - } - layout { - div("fullscreen") { - div("container") { - id = "container" - if (showToolbar) { - @Suppress("UNUSED_VARIABLE") - val header = div("toolbar") { - randomizeButton = button { - label = "Randomize" - clicked { - randomize(strength = if (shiftDown) .75 else .05) - } - } - button { - label = "Load" - clicked { - openFileDialog( - supportedExtensions = listOf("GUI parameters" to listOf("json")), - contextID = "gui.parameters" - ) { - loadParameters(it) - } - } - } - button { - label = "Save" - clicked { - val defaultPath = getDefaultPathForContext(contextID = "gui.parameters") - - if (defaultPath == null) { - val local = File(".") - val parameters = File(local, defaultSaveFolder) - if (parameters.exists() && parameters.isDirectory) { - setDefaultPathForContext( - contextID = "gui.parameters", - file = parameters - ) - } else { - if (parameters.mkdirs()) { - setDefaultPathForContext( - contextID = "gui.parameters", - file = parameters - ) - } else { - logger.warn { "Could not create directory ${parameters.absolutePath}" } - } - } - } - - saveFileDialog( - suggestedFilename = "parameters.json", - contextID = "gui.parameters", - supportedExtensions = listOf("GUI parameters" to listOf("json")) - ) { - saveParameters(it) - } - } - } - } - } - val collapseBorder = div("collapse-border") { - - } - - val collapsibles = mutableSetOf

() - val sidebar = div("sidebar") { - id = "sidebar" - scrollTop = sidebarState().scrollTop - for ((labeledObject, binding) in trackedObjects) { - val (label, _) = labeledObject - - val h3Header = h3 { label } - val collapsible = div("compartment") { - for (parameter in binding.parameters) { - val element = addControl(labeledObject, parameter) - binding.parameterControls[parameter] = element - } - } - collapsibles.add(collapsible) - val collapseClass = ElementClass("collapsed") - - /* this is guaranteed to be in the dictionary after insertion through add() */ - val collapseState = compartmentState()[label]!! - if (collapseState.collapsed) { - collapsible.classes.add(collapseClass) - } - - h3Header.mouse.pressed.listen { - it.cancelPropagation() - } - h3Header.mouse.clicked.listen { me -> - - if (KeyModifier.CTRL in me.modifiers) { - collapsible.classes.remove(collapseClass) - compartmentState().forEach { - it.value.collapsed = true - } - collapseState.collapsed = false - - (collapsibles - collapsible).forEach { - it.classes.add(collapseClass) - } - } else { - - if (collapseClass in collapsible.classes) { - collapsible.classes.remove(collapseClass) - collapseState.collapsed = false - } else { - collapsible.classes.add(collapseClass) - collapseState.collapsed = true - } - } - } - } - } - collapseBorder.mouse.pressed.listen { - it.cancelPropagation() - } - - collapseBorder.mouse.clicked.listen { - val collapsed = ElementClass("collapsed") - if (collapsed in sidebar.classes) { - sidebar.classes.remove(collapsed) - sidebarState().collapsed = false - } else { - sidebar.classes.add(collapsed) - sidebarState().collapsed = true - } - it.cancelPropagation() - } - sidebar.mouse.scrolled.listen { - sidebarState().scrollTop = sidebar.scrollTop - } - if (sidebarState().collapsed) { - sidebar.classes.add(ElementClass("collapsed")) - } - sidebar.scrollTop = sidebarState().scrollTop - } - if (enableSideCanvas) { - canvas = canvas("full-canvas") { - } - } - } - } - } - - visible = !sidebarState().hidden - - program.extend(panel ?: error("no panel")) - } - - /* 2) control creation. create control, set label, set range, setup event-handler, load values */ - // - private fun Div.addControl(compartment: LabeledObject, parameter: Parameter): Element { - val obj = compartment.obj - - return when (parameter.parameterType) { - - ParameterType.Int -> { - slider { - label = parameter.label - range = Range(parameter.intRange!!.first.toDouble(), parameter.intRange!!.last.toDouble()) - precision = 0 - events.valueChanged.listen { - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1, - obj, - it.newValue.toInt() - ) - (parameter.property as KMutableProperty1).set(obj, value.toInt()) - onChangeListener?.invoke(parameter.property!!.name, it.newValue) - } - getPersistedOrDefault( - compartment.label, - parameter.property as KMutableProperty1, - obj - ).let { - value = it.toDouble() - setAndPersist(compartment.label, parameter.property as KMutableProperty1, obj, it) - } - } - } - - ParameterType.Double -> { - slider { - label = parameter.label - range = Range(parameter.doubleRange!!.start, parameter.doubleRange!!.endInclusive) - precision = parameter.precision!! - events.valueChanged.listen { - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1, - obj, - it.newValue - ) - onChangeListener?.invoke(parameter.property!!.name, it.newValue) - } - getPersistedOrDefault( - compartment.label, - parameter.property as KMutableProperty1, - obj - ).let { - value = it - /* this is generally not needed, but when the persisted value is equal to the slider default - it will not emit the newly set value */ - setAndPersist(compartment.label, parameter.property as KMutableProperty1, obj, it) - } - } - } - - ParameterType.Action -> { - button { - label = parameter.label - events.clicked.listen { - /* the `obj` we pass in here is the receiver */ - parameter.function!!.call(obj) - onChangeListener?.invoke(parameter.function!!.name, null) - } - } - } - - ParameterType.Boolean -> { - toggle { - label = parameter.label - events.valueChanged.listen { - value = it.newValue - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1, - obj, - it.newValue - ) - onChangeListener?.invoke(parameter.property!!.name, it.newValue) - } - getPersistedOrDefault( - compartment.label, - parameter.property as KMutableProperty1, - obj - ).let { - value = it - setAndPersist(compartment.label, parameter.property as KMutableProperty1, obj, it) - } - } - } - - ParameterType.Text -> { - textfield { - label = parameter.label - events.valueChanged.listen { - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1, - obj, - it.newValue - ) - onChangeListener?.invoke(parameter.property!!.name, it.newValue) - } - getPersistedOrDefault( - compartment.label, - parameter.property as KMutableProperty1, - obj - ).let { - value = it - } - } - } - - ParameterType.Color -> { - colorpickerButton { - label = parameter.label - events.valueChanged.listen { - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1, - obj, - it.color - ) - onChangeListener?.invoke(parameter.property!!.name, it.color) - } - getPersistedOrDefault( - compartment.label, - parameter.property as KMutableProperty1, - obj - ).let { - color = it - } - } - } - - ParameterType.XY -> { - xyPad { - minX = parameter.vectorRange!!.first.x - minY = parameter.vectorRange!!.first.y - maxX = parameter.vectorRange!!.second.x - maxY = parameter.vectorRange!!.second.y - precision = parameter.precision!! - showVector = parameter.showVector!! - invertY = parameter.invertY!! - label = parameter.label - - events.valueChanged.listen { - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1, - obj, - it.newValue - ) - onChangeListener?.invoke(parameter.property!!.name, it.newValue) - } - } - } - - ParameterType.Path -> { - button { - label = "Load ${parameter.label}" - clicked { - - if (parameter.pathIsDirectory == false) { - openFileDialog( - supportedExtensions = parameter.pathExtensions?.let { listOf("supported extensions" to it.toList()) } - ?: emptyList(), - contextID = parameter.pathContext ?: "null" - ) { - val resolvedPath = if (parameter.absolutePath == true) { - it.absolutePath - } else { - it.relativeTo(File(".").absoluteFile).path - } - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1, - obj, - resolvedPath - ) - } - } else { - openFolderDialog(contextID = parameter.pathContext ?: "null") { - val resolvedPath = if (parameter.absolutePath == true) { - it.absolutePath - } else { - it.relativeTo(File(".").absoluteFile).path - } - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1, - obj, - resolvedPath - ) - } - } - } - } - } - - ParameterType.DoubleList -> { - sequenceEditor { - range = parameter.doubleRange!! - label = parameter.label - minimumSequenceLength = parameter.sizeRange!!.start - maximumSequenceLength = parameter.sizeRange!!.endInclusive - precision = parameter.precision!! - - events.valueChanged.listen { - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1>, - obj, - it.newValue.toMutableList() - ) - onChangeListener?.invoke(parameter.property!!.name, it.newValue) - } - getPersistedOrDefault( - compartment.label, - parameter.property as KMutableProperty1>, - obj - ).let { - value = it - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1>, - obj, - it - ) - } - } - } - - ParameterType.Vector2 -> { - slidersVector2 { - range = parameter.doubleRange!! - label = parameter.label - precision = parameter.precision!! - - events.valueChanged.listen { - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1, - obj, - it.newValue - ) - - onChangeListener?.invoke(parameter.property!!.name, it.newValue) - } - getPersistedOrDefault( - compartment.label, - parameter.property as KMutableProperty1, - obj - ).let { - value = it - setAndPersist(compartment.label, parameter.property as KMutableProperty1, obj, it) - } - } - } - - ParameterType.Vector3 -> { - slidersVector3 { - range = parameter.doubleRange!! - label = parameter.label - precision = parameter.precision!! - - events.valueChanged.listen { - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1, - obj, - it.newValue - ) - - onChangeListener?.invoke(parameter.property!!.name, it.newValue) - } - getPersistedOrDefault( - compartment.label, - parameter.property as KMutableProperty1, - obj - ).let { - value = it - setAndPersist(compartment.label, parameter.property as KMutableProperty1, obj, it) - } - } - } - - ParameterType.Vector4 -> { - slidersVector4 { - range = parameter.doubleRange!! - label = parameter.label - precision = parameter.precision!! - - events.valueChanged.listen { - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1, - obj, - it.newValue - ) - - onChangeListener?.invoke(parameter.property!!.name, it.newValue) - } - getPersistedOrDefault( - compartment.label, - parameter.property as KMutableProperty1, - obj - ).let { - value = it - setAndPersist(compartment.label, parameter.property as KMutableProperty1, obj, it) - } - } - } - - ParameterType.Option -> { - dropdownButton { - val enumProperty = parameter.property as KMutableProperty1> - val value = enumProperty.get(obj) - label = parameter.label - // -- this is dirty, but it is the only way to get the constants for arbitrary enums - // -- (that I know of, at least) - @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") val jEnum = value as java.lang.Enum<*> - // -- we don't use the property syntax here because that leads to compilation errors - @Suppress("UsePropertyAccessSyntax") val constants = jEnum.getDeclaringClass().getEnumConstants() - constants.forEach { - item { - label = it.name - data = it - } - } - events.valueChanged.listen { - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1>, - obj, - it.value.data as? Enum<*> ?: error("no data") - ) - - onChangeListener?.invoke( - parameter.property!!.name, - it.value.data as? Enum<*> ?: error("no data") - ) - } - getPersistedOrDefault( - compartment.label, - parameter.property as KMutableProperty1>, - obj - ).let { enum -> - (this@dropdownButton).value = items().find { item -> item.data == enum } - ?: error("no matching item found") - setAndPersist( - compartment.label, - parameter.property as KMutableProperty1>, - obj, - enum - ) - } - } - } - } - } - // - - private val trackedObjects = mutableMapOf() - - private fun updateControls() { - for ((labeledObject, binding) in trackedObjects) { - for ((parameter, control) in binding.parameterControls) { - updateControl(labeledObject, parameter, control) - } - } - } - - class ParameterValue( - var doubleValue: Double? = null, - var intValue: Int? = null, - var booleanValue: Boolean? = null, - var colorValue: ColorRGBa? = null, - var vector2Value: Vector2? = null, - var vector3Value: Vector3? = null, - var vector4Value: Vector4? = null, - var doubleListValue: MutableList? = null, - var textValue: String? = null, - var optionValue: String? = null, - var minValue: Double? = null, - var maxValue: Double? = null - ) - - - /** - * Can be called by the user to obtain an object to be serialized - * externally. This allows the user to combine custom data with gui - * state and save it all to one file. Complements `.fromObject()`. - */ - fun toObject(): Map> { - fun KMutableProperty1?.qget(obj: Any): T { - return (this as KMutableProperty1).get(obj) - } - - return trackedObjects.entries.associate { (lo, b) -> - Pair(lo.label, b.parameterControls.keys.associate { k -> - Pair( - k.property?.name ?: k.function?.name - ?: error("no name"), when (k.parameterType) { - /* 3) setup serializers */ - ParameterType.Double -> ParameterValue( - doubleValue = k.property.qget(lo.obj) as Double, - minValue = k.doubleRange?.start, - maxValue = k.doubleRange?.endInclusive - ) - - ParameterType.Int -> ParameterValue( - intValue = k.property.qget(lo.obj) as Int, - minValue = k.intRange?.start?.toDouble(), - maxValue = k.intRange?.endInclusive?.toDouble() - ) - - ParameterType.Action -> ParameterValue() - ParameterType.Color -> ParameterValue(colorValue = k.property.qget(lo.obj) as ColorRGBa) - ParameterType.Text -> ParameterValue(textValue = k.property.qget(lo.obj) as String) - ParameterType.Boolean -> ParameterValue(booleanValue = k.property.qget(lo.obj) as Boolean) - ParameterType.XY -> ParameterValue(vector2Value = k.property.qget(lo.obj) as Vector2) - ParameterType.DoubleList -> ParameterValue( - doubleListValue = k.property.qget( - lo.obj - ) as MutableList, - minValue = k.doubleRange?.start, - maxValue = k.doubleRange?.endInclusive - ) - - ParameterType.Vector2 -> ParameterValue( - vector2Value = k.property.qget(lo.obj) as Vector2, - minValue = k.doubleRange?.start, - maxValue = k.doubleRange?.endInclusive - ) - - ParameterType.Vector3 -> ParameterValue( - vector3Value = k.property.qget(lo.obj) as Vector3, - minValue = k.doubleRange?.start, - maxValue = k.doubleRange?.endInclusive - ) - - ParameterType.Vector4 -> ParameterValue( - vector4Value = k.property.qget(lo.obj) as Vector4, - minValue = k.doubleRange?.start, - maxValue = k.doubleRange?.endInclusive - ) - - ParameterType.Path -> ParameterValue(textValue = k.property.qget(lo.obj) as String) - - ParameterType.Option -> ParameterValue(optionValue = (k.property.qget(lo.obj) as Enum<*>).name) - } - ) - }) - } - } - - fun saveParameters(file: File) { - file.writeText(Gson().toJson(toObject())) - } - - /** - * Can be called by the user to update the gui using an object - * deserialized externally. Allows the user to load a larger json object, - * deserialize it, and use part of it to update the GUI. - * Complements `.toObject()`. - */ - fun fromObject(labeledValues: Map>) { - fun KMutableProperty1?.qset(obj: Any, value: T) = - (this as KMutableProperty1).set(obj, value) - - fun KMutableProperty1?.enumSet(obj: Any, value: String) { - val v = (this as KMutableProperty1>).get(obj) - - @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UsePropertyAccessSyntax") - val enumValue = (v as java.lang.Enum<*>).getDeclaringClass().getEnumConstants().find { it.name == value } - ?: error("cannot map value $value to enum") - (this as KMutableProperty1>).set(obj, enumValue) - } - - labeledValues.forEach { (label, ps) -> - trackedObjects.keys.find { it.label == label }?.let { lo -> - val binding = trackedObjects[lo]!! - ps.forEach { (parameterName, parameterValue) -> - binding.parameters.find { it.property?.name == parameterName }?.let { parameter -> - when (parameter.parameterType) { - /* 4) Set up deserializers */ - ParameterType.Double -> parameterValue.doubleValue?.let { - parameter.property.qset(lo.obj, it) - } - - ParameterType.Int -> parameterValue.intValue?.let { - parameter.property.qset(lo.obj, it) - } - - ParameterType.Text -> parameterValue.textValue?.let { - parameter.property.qset(lo.obj, it) - } - - ParameterType.Color -> parameterValue.colorValue?.let { - parameter.property.qset(lo.obj, it) - } - - ParameterType.XY -> parameterValue.vector2Value?.let { - parameter.property.qset(lo.obj, it) - } - - ParameterType.DoubleList -> parameterValue.doubleListValue?.let { - parameter.property.qset(lo.obj, it) - } - - ParameterType.Boolean -> parameterValue.booleanValue?.let { - parameter.property.qset(lo.obj, it) - } - - ParameterType.Vector2 -> parameterValue.vector2Value?.let { - parameter.property.qset(lo.obj, it) - } - - ParameterType.Vector3 -> parameterValue.vector3Value?.let { - parameter.property.qset(lo.obj, it) - } - - ParameterType.Vector4 -> parameterValue.vector4Value?.let { - parameter.property.qset(lo.obj, it) - } - - ParameterType.Option -> parameterValue.optionValue?.let { - parameter.property.enumSet(lo.obj, it) - } - - ParameterType.Path -> parameterValue.textValue?.let { - parameter.property.qset(lo.obj, it) - } - - ParameterType.Action -> { - // intentionally do nothing - } - } - } - } - } - } - updateControls() - } - - fun loadParameters(file: File) { - val json = file.readText() - val typeToken = object : TypeToken>>() {} - val labeledValues: Map> = try { - Gson().fromJson(json, typeToken.type) - } catch (e: JsonSyntaxException) { - println("could not parse json: $json") - throw e - } - - fromObject(labeledValues) - } - - private fun updateControl(labeledObject: LabeledObject, parameter: Parameter, control: Element) { - when (parameter.parameterType) { - /* 5) Update control from property value */ - ParameterType.Double -> { - (control as Slider).value = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - } - - ParameterType.Int -> { - (control as Slider).value = - (parameter.property as KMutableProperty1).get(labeledObject.obj).toDouble() - } - - ParameterType.Text -> { - (control as Textfield).value = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - } - - ParameterType.Color -> { - (control as ColorpickerButton).color = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - } - - ParameterType.XY -> { - (control as XYPad).value = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - } - - ParameterType.DoubleList -> { - (control as SequenceEditor).value = - (parameter.property as KMutableProperty1>).get(labeledObject.obj) - } - - ParameterType.Boolean -> { - (control as Toggle).value = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - } - - ParameterType.Vector2 -> { - (control as SlidersVector2).value = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - } - - ParameterType.Vector3 -> { - (control as SlidersVector3).value = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - } - - ParameterType.Vector4 -> { - (control as SlidersVector4).value = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - } - - ParameterType.Option -> { - val ddb = control as DropdownButton - ddb.value = ddb.items().find { item -> - item.data == (parameter.property as KMutableProperty1>).get(labeledObject.obj) - } ?: error("could not find item") - } - - ParameterType.Path -> { - - } - - ParameterType.Action -> { - // intentionally do nothing - } - } - } - - fun randomize(strength: Double = 0.05) { - for ((labeledObject, binding) in trackedObjects) { - // -- only randomize visible parameters - for (parameter in binding.parameterControls.keys) { - when (parameter.parameterType) { - /* 6) Set up value randomizers */ - ParameterType.Double -> { - val min = parameter.doubleRange!!.start - val max = parameter.doubleRange!!.endInclusive - val currentValue = (parameter.property as KMutableProperty1).get(labeledObject.obj) - val randomValue = Double.uniform(min, max) - val newValue = mix(currentValue, randomValue, strength) - (parameter.property as KMutableProperty1).set(labeledObject.obj, newValue) - } - - ParameterType.Int -> { - val min = parameter.intRange!!.first - val max = parameter.intRange!!.last - val currentValue = (parameter.property as KMutableProperty1).get(labeledObject.obj) - val randomValue = Double.uniform(min.toDouble(), max.toDouble()) - val newValue = mix(currentValue.toDouble(), randomValue, strength).roundToInt() - (parameter.property as KMutableProperty1).set(labeledObject.obj, newValue) - } - - ParameterType.Boolean -> { - //I am not sure about randomizing boolean values here - //(parameter.property as KMutableProperty1).set(labeledObject.obj, (Math.random() < 0.5)) - } - - ParameterType.Color -> { - val currentValue = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - val randomValue = - ColorRGBa.fromVector(Vector3.uniform(0.0, 1.0), currentValue.alpha, currentValue.linearity) - val newValue = currentValue.mix(randomValue, strength) - (parameter.property as KMutableProperty1).set(labeledObject.obj, newValue) - } - - ParameterType.Vector2 -> { - val min = parameter.doubleRange!!.start - val max = parameter.doubleRange!!.endInclusive - val currentValue = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - val randomValue = Vector2.uniform(min, max) - val newValue = currentValue.mix(randomValue, strength) - (parameter.property as KMutableProperty1).set(labeledObject.obj, newValue) - } - - ParameterType.XY -> { - val min = parameter.vectorRange!!.first - val max = parameter.vectorRange!!.second - val currentValue = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - val randomValue = Vector2.uniform(min, max) - val newValue = currentValue.mix(randomValue, strength) - (parameter.property as KMutableProperty1).set(labeledObject.obj, newValue) - } - - ParameterType.Vector3 -> { - val min = parameter.doubleRange!!.start - val max = parameter.doubleRange!!.endInclusive - val currentValue = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - val randomValue = Vector3.uniform(min, max) - val newValue = currentValue.mix(randomValue, strength) - (parameter.property as KMutableProperty1).set(labeledObject.obj, newValue) - } - - ParameterType.Vector4 -> { - val min = parameter.doubleRange!!.start - val max = parameter.doubleRange!!.endInclusive - val currentValue = - (parameter.property as KMutableProperty1).get(labeledObject.obj) - val randomValue = Vector4.uniform(min, max) - val newValue = currentValue.mix(randomValue, strength) - (parameter.property as KMutableProperty1).set(labeledObject.obj, newValue) - } - - else -> { - // intentionally do nothing - } - } - } - } - updateControls() - } - - /** - * Recursively find a unique label - * @param label to find an alternate for in case it already exist - */ - private fun resolveUniqueLabel(label: String): String { - return trackedObjects.keys.find { it.label == label }?.let { lo -> - resolveUniqueLabel(Regex("(.*) / ([0-9]+)").matchEntire(lo.label)?.let { - "${it.groupValues[1]} / ${1 + it.groupValues[2].toInt()}" - } ?: "$label / 2") - } ?: label - } - - /** - * Add an object to the GUI - * @param objectWithParameters an object of a class that annotated parameters - * @param label an optional label that overrides the label supplied in a [Description] annotation - * @return pass-through of [objectWithParameters] - */ - fun add(objectWithParameters: T, label: String? = objectWithParameters.title()): T { - val parameters = objectWithParameters.listParameters() - val uniqueLabel = resolveUniqueLabel(label ?: "No name") - - if (parameters.isNotEmpty()) { - val collapseStates = persistentCompartmentStates.getOrPut(Driver.instance.contextID) { - mutableMapOf() - } - collapseStates.getOrPut(uniqueLabel) { - CompartmentState(compartmentsCollapsedByDefault) - } - trackedObjects[LabeledObject(uniqueLabel, objectWithParameters)] = TrackedObjectBinding(parameters) - } - return objectWithParameters - } - - /** - * Add an object to the GUI using a builder. - * @param label an optional label that overrides the label supplied in a [Description] annotation - * @return the built object - */ - fun add(label: String? = null, builder: () -> T): T { - val t = builder() - return add(t, label ?: t.title()) - } - - override fun afterDraw(drawer: Drawer, program: Program) { - if (doubleBind) { - updateControls() - } - } - - override fun shutdown(program: Program) { - if (persistState) { - val folderFile = File(defaultSaveFolder) - if (folderFile.exists() && folderFile.isDirectory) { - saveParameters(File(defaultSaveFolder, "${program.name}-latest.json")) - } else { - if (folderFile.mkdirs()) { - saveParameters(File(defaultSaveFolder, "${program.name}-latest.json")) - } else { - logger.error { "Could not persist GUI state because could not create directory ${folderFile.absolutePath}" } - } - } - } - } -} - -@JvmName("addToGui") -fun T.addTo(gui: GUI, label: String? = this.title()): T { - gui.add(this, label) - return this -} diff --git a/orx-jvm/orx-gui/src/main/kotlin/WindowedGUI.kt b/orx-jvm/orx-gui/src/main/kotlin/WindowedGUI.kt deleted file mode 100644 index 55219a3a..00000000 --- a/orx-jvm/orx-gui/src/main/kotlin/WindowedGUI.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.openrndr.extra.gui - -import org.openrndr.* -import org.openrndr.extra.parameters.title -import org.openrndr.internal.Driver -import org.openrndr.math.IntVector2 -import org.openrndr.panel.style.StyleSheet -import org.openrndr.panel.style.defaultStyles - -private val childWindows = mutableMapOf() - -class WindowedGUI( - val appearance: GUIAppearance = GUIAppearance(), - val defaultStyles: List = defaultStyles(), - val windowClosable: Boolean = false, - val windowAlwaysOntop: Boolean = false, -) : Extension { - override var enabled: Boolean = true - val gui: GUI = GUI(appearance, defaultStyles) - - private val addedObjects = mutableListOf>() - - fun add(objectWithParameters: T, label: String? = objectWithParameters.title()): T { - addedObjects.add(Pair(objectWithParameters, label)) - return objectWithParameters - } - - override fun setup(program: Program) { - val window = childWindows[Driver.instance.contextID] - if (window != null) { - window.program.mouse.exited.listeners.clear() - window.program.mouse.entered.listeners.clear() - window.program.mouse.buttonUp.listeners.clear() - window.program.mouse.buttonDown.listeners.clear() - window.program.mouse.dragged.listeners.clear() - window.program.mouse.scrolled.listeners.clear() - window.program.mouse.moved.listeners.clear() - window.program.keyboard.keyUp.listeners.clear() - window.program.keyboard.keyDown.listeners.clear() - window.program.keyboard.keyRepeat.listeners.clear() - window.program.keyboard.character.listeners.clear() - window.program.extensions.clear() - window.program.produceAssets.listeners.clear() - window.program.requestAssets.listeners.clear() - } - - val cw = childWindows.getOrPut(Driver.instance.contextID) { - program.window( - WindowConfiguration( - closable = windowClosable, - alwaysOnTop = windowAlwaysOntop, - width = appearance.barWidth, - height = program.height, - position = program.window.position.toInt() - IntVector2(200, 0) - ) - ) { - // - } - } - - // launch because at this stage Driver.instance.contextID points to the context of the parent window - cw.program.launch { - for ((obj, label) in addedObjects) { - gui.add(obj, label) - } - cw.program.extend(gui) - program.produceAssets.listen { - cw.program.produceAssets.trigger(it) - } - } - } -} - -@JvmName("addToWindowedGui") -fun T.addTo(gui: WindowedGUI, label: String? = this.title()): T { - gui.add(this, label) - return this -} diff --git a/orx-jvm/orx-keyframer/README.md b/orx-jvm/orx-keyframer/README.md deleted file mode 100644 index a2ddc534..00000000 --- a/orx-jvm/orx-keyframer/README.md +++ /dev/null @@ -1,238 +0,0 @@ -# orx-keyframer - -Create animated timelines by specifying properties and times in keyframes, then play it back at any speed (even -backwards) automatically interpolating properties. Save, load, use mathematical expressions and callbacks. Powerful and -highly reusable. - -What this allows you to do: - -1. Create a keyframed animation in a json file. - -```json -[ - { - "time": 0.0, - "easing": "cubic-in-out", - "x": 3.0, - "y": 4.0, - "z": 9.0, - "r": 0.1, - "g": 0.5, - "b": 0.2, - "radius": 50 - }, - { - "time": 2.0, - "easing": "cubic-in-out", - "r": 0.6, - "g": 0.5, - "b": 0.1 - }, - { - "time": 4.0, - "easing": "cubic-in-out", - "x": 10.0, - "y": 4.0, - "radius": 400 - }, - { - "time": 5.0, - "easing": "cubic-in-out", - "x": 100.0, - "y": 320.0, - "radius": 400 - }, - { - "time": 5.3, - "easing": "cubic-in-out", - "x": 100.0, - "y": 320.0, - "radius": { - "value": 50.0, - "easing": "linear" - } - } -] -``` - -2. Map the animation data to Kotlin types: - -```kotlin -class Animation : Keyframer() { - val position by Vector2Channel(arrayOf("x", "y")) - val radius by DoubleChannel("radius") - val color by RGBChannel(arrayOf("r", "g", "b")) -} - -val animation = Animation() -animation.loadFromJson(File("data/keyframes/animation.json")) -``` - -3. Animate! (from an OPENRNDR program) - -```kotlin -extend { - animation(seconds) - drawer.fill = animation.color - drawer.circle(animation.position, animation.radius) -} -``` - -## Easing - -All the easing functions of orx-easing are available - -- linear -- back-in -- back-out -- back-in-out -- bounce-in -- bounce-out -- bounce-in-out -- circ-in -- circ-out -- circ-in-out -- cubic-in -- cubic-out -- cubic-in-out -- elastic-in -- elastic-out -- elastic-in-out -- expo-in -- expo-out -- expo-in-out -- quad-in -- quad-out -- quad-in-out -- quart-in -- quart-out -- quart-in-out -- quint-in -- quint-out -- quint-in-out -- sine-in -- sine-out -- sine-in-out -- one -- zero - -## More expressive interface - -orx-keyframer has two ways of programming key frames. The first is the `"x": ` style we have seen before. The -second way uses a dictionary instead of a number value. - -For example: - -```json -[ - { - "time": 0.0, - "x": 320.0, - "y": 240.0 - }, - { - "time": 10.0, - "easing": "cubic-out", - "x": { - "easing": "cubic-in-out", - "value": 0.0 - }, - "y": { - "duration": -5.0, - "easing": "cubic-in", - "value": 0.0 - } - }, - { - "time": 20.0, - "x": 640.0, - "y": 480.0, - "easing": "cubic-in-out" - } -] -``` - -Inside the value dictionary one can set `value`, `easing`, `duration` and `envelope`. - - * `value` the target value, required value - * `easing` easing method that overrides the key's easing method, optional value - * `duration` an optional duration for the animation, set to `0` to jump from the previous -value to the new value, a negative value will start the interpolation before `time`. A positive value - wil start the interpolation at `time` and end at `time + duration` -* `envelope` optional 2-point envelope that modifies the playback of the animation. The default envelope is -`[0.0, 1.0]`. Reverse playback is achieved by supplying `[1.0, 0.0]`. To start the animation later try `[0.1, 1.0]`, - to end the animation earlier try `[0.0, 0.9]` - -## Advanced features - -orx-keyframer uses two file formats. A `SIMPLE` format and a `FULL` format. For reference check -the [example full format .json](src/demo/resources/demo-full-01.json) and -the [example program](src/demo/kotlin/DemoFull01.kt). The full format adds a `parameters` block and a `prototypes` -block. - -[Expressions](src/demo/resources/demo-simple-expressions-01.json), expression mechanism. Currently uses values `r` to -indicate repeat index and `t` the last used key time, `v` the last used value (for the animated attribute). - -Supported functions in expressions: - -- `min(x, y)`, `max(x, y)` -- `cos(x)`, `sin(x)`, `acos(x)`, `asin(x)`, `tan(x)`, `atan(x)`, `atan2(y, x)` -- `abs(x)`, `saturate(x)` -- `degrees(x)`, `radians(x)` -- `pow(x, y)`, `sqrt(x)`, `exp(x)` -- `mix(left, right, x)` -- `smoothstep(t0, t1, x)` -- `map(leftBefore, rightBefore, leftAfter, rightAfter, x)` -- `random()`, `random(min, max)` - -[Parameters and prototypes](src/demo/resources/demo-full-01.json) - - -## Demos -### DemoEvelope01 - - - -![DemoEvelope01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-keyframer/images/DemoEvelope01Kt.png) - -[source code](src/demo/kotlin/DemoEvelope01.kt) - -### DemoFull01 - - - -![DemoFull01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-keyframer/images/DemoFull01Kt.png) - -[source code](src/demo/kotlin/DemoFull01.kt) - -### DemoScrub01 - - - -![DemoScrub01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-keyframer/images/DemoScrub01Kt.png) - -[source code](src/demo/kotlin/DemoScrub01.kt) - -### DemoSimple01 - - - -![DemoSimple01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-keyframer/images/DemoSimple01Kt.png) - -[source code](src/demo/kotlin/DemoSimple01.kt) - -### DemoSimple02 - - - -![DemoSimple02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-keyframer/images/DemoSimple02Kt.png) - -[source code](src/demo/kotlin/DemoSimple02.kt) - -### DemoSimpleExpressions01 - - - -![DemoSimpleExpressions01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-keyframer/images/DemoSimpleExpressions01Kt.png) - -[source code](src/demo/kotlin/DemoSimpleExpressions01.kt) diff --git a/orx-jvm/orx-keyframer/build.gradle.kts b/orx-jvm/orx-keyframer/build.gradle.kts deleted file mode 100644 index 146367c5..00000000 --- a/orx-jvm/orx-keyframer/build.gradle.kts +++ /dev/null @@ -1,14 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(libs.gson) - implementation(sharedLibs.kotlin.reflect) - implementation(project(":orx-noise")) - implementation(project(":orx-easing")) - api(project(":orx-expression-evaluator")) - demoImplementation(project(":orx-jvm:orx-panel")) -} diff --git a/orx-jvm/orx-keyframer/src/demo/kotlin/DemoEvelope01.kt b/orx-jvm/orx-keyframer/src/demo/kotlin/DemoEvelope01.kt deleted file mode 100644 index c8b78441..00000000 --- a/orx-jvm/orx-keyframer/src/demo/kotlin/DemoEvelope01.kt +++ /dev/null @@ -1,18 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.keyframer.Keyframer -import org.openrndr.resourceUrl -import java.net.URL - -fun main() = application { - program { - class Animation: Keyframer() { - val position by Vector2Channel(arrayOf("x", "y")) - } - val animation = Animation() - animation.loadFromJson(URL(resourceUrl("/demo-envelope-01.json"))) - extend { - animation(seconds) - drawer.circle(animation.position, 100.0) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/demo/kotlin/DemoFull01.kt b/orx-jvm/orx-keyframer/src/demo/kotlin/DemoFull01.kt deleted file mode 100644 index 7455f1a3..00000000 --- a/orx-jvm/orx-keyframer/src/demo/kotlin/DemoFull01.kt +++ /dev/null @@ -1,22 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.keyframer.Keyframer -import org.openrndr.extra.keyframer.KeyframerFormat -import org.openrndr.resourceUrl -import java.net.URL - -fun main() = application { - program { - class Animation: Keyframer() { - val position by Vector2Channel(arrayOf("x", "y")) - val radius by DoubleChannel("radius") - val color by RGBChannel(arrayOf("r", "g", "b")) - } - val animation = Animation() - animation.loadFromJson(URL(resourceUrl("/demo-full-01.json")), format = KeyframerFormat.FULL) - extend { - animation(seconds) - drawer.fill = animation.color - drawer.circle(animation.position, animation.radius) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/demo/kotlin/DemoScrub01.kt b/orx-jvm/orx-keyframer/src/demo/kotlin/DemoScrub01.kt deleted file mode 100644 index 3bbd424b..00000000 --- a/orx-jvm/orx-keyframer/src/demo/kotlin/DemoScrub01.kt +++ /dev/null @@ -1,46 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.keyframer.Keyframer -import org.openrndr.panel.controlManager -import org.openrndr.panel.elements.Range -import org.openrndr.panel.elements.Slider -import org.openrndr.panel.elements.slider -import org.openrndr.resourceUrl -import java.net.URL - -fun main() = application { - program { - - // -- replace the default clock with an offset clock - var clockOffset = 0.0 - val oldClock = clock - clock = { oldClock() - clockOffset } - var clockSlider: Slider? = null - - // -- setup a simple UI - val cm = controlManager { - layout { - clockSlider = slider { - range = Range(0.0, 30.0) - events.valueChanged.listen { - if (it.interactive) { - clockOffset = oldClock() - it.newValue - } - } - } - } - } - extend(cm) - class Animation: Keyframer() { - val position by Vector2Channel(arrayOf("x", "y")) - } - val animation = Animation() - animation.loadFromJson(URL(resourceUrl("/demo-simple-01.json"))) - - extend { - // -- update the slider - clockSlider?.value = seconds - animation(seconds) - drawer.circle(animation.position, 100.0) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/demo/kotlin/DemoSimple01.kt b/orx-jvm/orx-keyframer/src/demo/kotlin/DemoSimple01.kt deleted file mode 100644 index 556b6fe5..00000000 --- a/orx-jvm/orx-keyframer/src/demo/kotlin/DemoSimple01.kt +++ /dev/null @@ -1,18 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.keyframer.Keyframer -import org.openrndr.resourceUrl -import java.net.URL - -fun main() = application { - program { - class Animation: Keyframer() { - val position by Vector2Channel(arrayOf("x", "y")) - } - val animation = Animation() - animation.loadFromJson(URL(resourceUrl("/demo-simple-01.json"))) - extend { - animation(seconds) - drawer.circle(animation.position, 100.0) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/demo/kotlin/DemoSimple02.kt b/orx-jvm/orx-keyframer/src/demo/kotlin/DemoSimple02.kt deleted file mode 100644 index 65048b4a..00000000 --- a/orx-jvm/orx-keyframer/src/demo/kotlin/DemoSimple02.kt +++ /dev/null @@ -1,21 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.keyframer.Keyframer -import org.openrndr.resourceUrl -import java.net.URL - -fun main() = application { - program { - class Animation: Keyframer() { - val position by Vector2Channel(arrayOf("x", "y")) - val radius by DoubleChannel("radius") - val color by RGBChannel(arrayOf("r", "g", "b")) - } - val animation = Animation() - animation.loadFromJson(URL(resourceUrl("/demo-simple-02.json"))) - extend { - animation(seconds) - drawer.fill = animation.color - drawer.circle(animation.position, animation.radius) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/demo/kotlin/DemoSimpleExpressions01.kt b/orx-jvm/orx-keyframer/src/demo/kotlin/DemoSimpleExpressions01.kt deleted file mode 100644 index df631102..00000000 --- a/orx-jvm/orx-keyframer/src/demo/kotlin/DemoSimpleExpressions01.kt +++ /dev/null @@ -1,21 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.keyframer.Keyframer -import org.openrndr.resourceUrl -import java.net.URL - -fun main() = application { - program { - class Animation : Keyframer() { - val position by Vector2Channel(arrayOf("x", "y")) - val radius by DoubleChannel("x") - } - - val animation = Animation() - animation.loadFromJson(URL(resourceUrl("/demo-simple-expressions-01.json")), - parameters = mapOf("cycleDuration" to 2.0)) - extend { - animation(seconds) - drawer.circle(animation.position, animation.radius) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/demo/resources/demo-envelope-01.json b/orx-jvm/orx-keyframer/src/demo/resources/demo-envelope-01.json deleted file mode 100644 index aa8b9b3a..00000000 --- a/orx-jvm/orx-keyframer/src/demo/resources/demo-envelope-01.json +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "time": 0.0, - "x": 320.0, - "y": 240.0 - }, - { - "time": 10.0, - "easing": "cubic-in-out", - "x": { - "envelope": [0.5, 1.0], - "value": 0.0 - }, - "y": { - "envelope": [0.4, 1.0], - "value": 0.0 - } - }, - { - "time": 20.0, - "x": 640.0, - "y": 480.0, - "easing": "cubic-in-out" - } -] \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/demo/resources/demo-full-01.json b/orx-jvm/orx-keyframer/src/demo/resources/demo-full-01.json deleted file mode 100644 index 1f9f8bcc..00000000 --- a/orx-jvm/orx-keyframer/src/demo/resources/demo-full-01.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - // this is breaking with proper json but.. gson accepts comments and they are invaluable - // in the parameters block you can add custom values, which can be used in expressions - "parameters": { - "smallRadius": 5.0, - "repetitionCount": 10, - "width": 640.0, - "height": 480.0, - // you can have expressions inside parameters too, they are evaluated once, on load - "resolvedOnLoad" : "width * 2.0" - }, - // in the prototypes you can set up key prototypes - "prototypes": { - "red": { - "r": 1.0, - "g": 0.0, - "b": 0.0 - }, - "blue": { - "r": 0.0, - "g": 0.0, - "b": 1.0 - }, - "center": { - // prototypes can have expressions too, they are evaluated as late as possible - // thus, they are evaluated more than once - "x": "width / 2", - "y": "height / 2" - }, - "small": { - "radius": "smallRadius" - }, - "large": { - "radius": "smallRadius * 10.0" - } - }, - "keys": [ - { - "time": 0.0, - "easing": "cubic-in-out", - "x": 3.0, - "y": 4.0, - "z": 9.0, - "r": 0.0, - "g": 1.0, - "b": 0.0, - "radius": 50, - "foo" : 0.0 - }, - { - "time": 2.0, - "easing": "cubic-in-out", - // here we apply the prototypes in cascading fashion from left to right - "prototypes": "red center small" - }, - { - "time": 3.0, - "repeat": { - "count": "repetitionCount", - "keys": [ - { - "time": "(rep * 2.0) + 3.0", - "prototypes": "blue large", - "easing": "cubic-in-out" - }, - { - "time": "t + 1.0", - "prototypes": "red small" - } - ] - } - } - ] -} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/demo/resources/demo-simple-01.json b/orx-jvm/orx-keyframer/src/demo/resources/demo-simple-01.json deleted file mode 100644 index f0197538..00000000 --- a/orx-jvm/orx-keyframer/src/demo/resources/demo-simple-01.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "time": 0.0, - "x": 320.0, - "y": 240.0 - }, - { - "time": 10.0, - "x": 0.0, - "y": 0.0, - "easing": "cubic-in-out" - }, - { - "time": 20.0, - "x": 640.0, - "y": 480.0, - "easing": "cubic-in-out" - } - -] \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/demo/resources/demo-simple-02.json b/orx-jvm/orx-keyframer/src/demo/resources/demo-simple-02.json deleted file mode 100644 index eed5716c..00000000 --- a/orx-jvm/orx-keyframer/src/demo/resources/demo-simple-02.json +++ /dev/null @@ -1,32 +0,0 @@ -[ - { - "time": 0.0, - "x": 320.0, - "y": 240.0, - "radius": 0.0, - "r": 1.0, - "g": 1.0, - "b": 1.0 - }, - { - "time": 5.0, - "radius": 200.0, - "r": 0.0 - }, - { - "time": 10.0, - "g": 0.0, - "x": 0.0, - "y": 0.0, - "easing": "cubic-in-out" - }, - { - "time": 20.0, - "x": 640.0, - "y": 480.0, - "radius": 50.0, - "easing": "cubic-in-out", - "g": 1.0, - "b": 0.0 - } -] \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/demo/resources/demo-simple-expressions-01.json b/orx-jvm/orx-keyframer/src/demo/resources/demo-simple-expressions-01.json deleted file mode 100644 index d216cb91..00000000 --- a/orx-jvm/orx-keyframer/src/demo/resources/demo-simple-expressions-01.json +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "time": 0.0, - "x": 320.0, - "y": 240.0, - "radius": 0.0 - }, - { - "time": 3.0, - "repeat": { - "count": 5, - "keys": [ - { - "duration": "cycleDuration * 0.5", - "easing": "cubic-in-out", - "x": 10.0, - "y": 4.0, - "radius": 400 - }, - { - "duration": "cycleDuration * 0.5", - "easing": "cubic-in-out", - "x": 630.0, - "y": 470.0, - "radius": 40 - } - ] - } - } -] \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/main/kotlin/Key.kt b/orx-jvm/orx-keyframer/src/main/kotlin/Key.kt deleted file mode 100644 index 268b0938..00000000 --- a/orx-jvm/orx-keyframer/src/main/kotlin/Key.kt +++ /dev/null @@ -1,75 +0,0 @@ -package org.openrndr.extra.keyframer - -import org.openrndr.extra.easing.Easing -import org.openrndr.extra.easing.EasingFunction -import org.openrndr.math.map - -internal val defaultEnvelope = doubleArrayOf(0.0, 1.0) - -class Key(val time: Double, val value: Double, val easing: EasingFunction, val envelope: DoubleArray = defaultEnvelope) - - -class KeyframerChannel { - val keys = mutableListOf() - - operator fun invoke() : Double { - return 0.0 - } - - fun add( - time: Double, - value: Double?, - easing: EasingFunction = Easing.Linear.function, - envelope: DoubleArray = defaultEnvelope - ) { - require(envelope.size >= 2) { - "envelope should contain at least 2 entries" - } - value?.let { - keys.add(Key(time, it, easing, envelope)) - } - } - - fun lastValue(): Double? { - return keys.lastOrNull()?.value - } - - fun lastTime(): Double? { - return keys.lastOrNull()?.time - } - - fun duration(): Double { - return keys.last().time - } - - fun value(time: Double): Double? { - if (keys.size == 0) { - return null - } - if (keys.size == 1) { - return if (time < keys.first().time) { - null - } else { - keys[0].value - } - } - - if (time < keys.first().time) { - return null - } - - val rightIndex = keys.indexOfFirst { it.time > time } - return if (rightIndex == -1) { - keys.last().value - } else { - val leftIndex = (rightIndex - 1).coerceAtLeast(0) - val rightKey = keys[rightIndex] - val leftKey = keys[leftIndex] - val t0 = (time - leftKey.time) / (rightKey.time - leftKey.time) - val te = t0.map(rightKey.envelope[0], rightKey.envelope[1], 0.0, 1.0, clamp = true) - val e0 = rightKey.easing(te, 0.0, 1.0, 1.0) - leftKey.value * (1.0 - e0) + rightKey.value * (e0) - } - } -} - diff --git a/orx-jvm/orx-keyframer/src/main/kotlin/KeyQuaternion.kt b/orx-jvm/orx-keyframer/src/main/kotlin/KeyQuaternion.kt deleted file mode 100644 index 532994af..00000000 --- a/orx-jvm/orx-keyframer/src/main/kotlin/KeyQuaternion.kt +++ /dev/null @@ -1,60 +0,0 @@ -package org.openrndr.extra.keyframer - -import org.openrndr.extra.easing.Easing -import org.openrndr.extra.easing.EasingFunction -import org.openrndr.math.Quaternion -import org.openrndr.math.slerp - -class KeyQuaternion(val time: Double, val value: Quaternion, val easing: EasingFunction) - -class KeyframerChannelQuaternion { - val keys = mutableListOf() - - operator fun invoke() : Double { - return 0.0 - } - - fun add(time: Double, value: Quaternion?, easing: EasingFunction = Easing.Linear.function) { - - value?.let { - keys.add(KeyQuaternion(time, it, easing)) - } - } - - fun lastValue(): Quaternion? { - return keys.lastOrNull()?.value - } - - fun duration(): Double { - return keys.last().time - } - - fun value(time: Double): Quaternion? { - if (keys.size == 0) { - return null - } - if (keys.size == 1) { - return if (time < keys.first().time) { - keys[0].value.normalized - } else { - keys[0].value.normalized - } - } - - if (time < keys.first().time) { - return null - } - - val rightIndex = keys.indexOfFirst { it.time > time } - return if (rightIndex == -1) { - keys.last().value.normalized - } else { - val leftIndex = (rightIndex - 1).coerceAtLeast(0) - val rightKey = keys[rightIndex] - val leftKey = keys[leftIndex] - val t0 = (time - leftKey.time) / (rightKey.time - leftKey.time) - val e0 = rightKey.easing(t0, 0.0, 1.0, 1.0) - slerp(leftKey.value, rightKey.value, e0).normalized - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/main/kotlin/KeyVector3.kt b/orx-jvm/orx-keyframer/src/main/kotlin/KeyVector3.kt deleted file mode 100644 index 64d53dc3..00000000 --- a/orx-jvm/orx-keyframer/src/main/kotlin/KeyVector3.kt +++ /dev/null @@ -1,58 +0,0 @@ -package org.openrndr.extra.keyframer - -import org.openrndr.extra.easing.Easing -import org.openrndr.extra.easing.EasingFunction -import org.openrndr.math.Vector3 - -class KeyVector3(val time: Double, val value: Vector3, val easing: EasingFunction) - -class KeyframerChannelVector3 { - val keys = mutableListOf() - - operator fun invoke() : Double { - return 0.0 - } - - fun add(time: Double, value: Vector3?, easing: EasingFunction = Easing.Linear.function) { - value?.let { - keys.add(KeyVector3(time, it, easing)) - } - } - - fun lastValue(): Vector3? { - return keys.lastOrNull()?.value - } - - fun duration(): Double { - return keys.last().time - } - - fun value(time: Double): Vector3? { - if (keys.size == 0) { - return null - } - if (keys.size == 1) { - return if (time < keys.first().time) { - null - } else { - keys[0].value - } - } - - if (time < keys.first().time) { - return null - } - - val rightIndex = keys.indexOfFirst { it.time > time } - return if (rightIndex == -1) { - keys.last().value - } else { - val leftIndex = (rightIndex - 1).coerceAtLeast(0) - val rightKey = keys[rightIndex] - val leftKey = keys[leftIndex] - val t0 = (time - leftKey.time) / (rightKey.time - leftKey.time) - val e0 = rightKey.easing(t0, 0.0, 1.0, 1.0) - leftKey.value * (1.0 - e0) + rightKey.value * (e0) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/main/kotlin/Keyframer.kt b/orx-jvm/orx-keyframer/src/main/kotlin/Keyframer.kt deleted file mode 100644 index 541e10a7..00000000 --- a/orx-jvm/orx-keyframer/src/main/kotlin/Keyframer.kt +++ /dev/null @@ -1,468 +0,0 @@ -package org.openrndr.extra.keyframer - -import com.google.gson.Gson -import com.google.gson.JsonSyntaxException -import com.google.gson.reflect.TypeToken -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.easing.Easing -import org.openrndr.extra.easing.EasingFunction -import org.openrndr.extra.expressions.ExpressionException -import org.openrndr.extra.expressions.FunctionExtensions -import org.openrndr.extra.expressions.evaluateExpression -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 -import java.io.File -import java.lang.IllegalStateException -import java.lang.NullPointerException -import java.net.URL -import kotlin.math.max -import kotlin.reflect.KProperty -import kotlin.reflect.KProperty1 -import kotlin.reflect.full.memberProperties -import kotlin.reflect.jvm.isAccessible - -enum class KeyframerFormat { - SIMPLE, - FULL -} - -open class Keyframer { - private var currentTime = 0.0 - operator fun invoke(time: Double) { - currentTime = time - } - - open inner class CompoundChannel(val keys: Array, private val defaultValues: Array) { - private var channelTimes: Array = Array(keys.size) { Double.NEGATIVE_INFINITY } - private var compoundChannels: Array = Array(keys.size) { null } - private var cachedValues: Array = Array(keys.size) { null } - - open fun reset() { - for (i in channelTimes.indices) { - channelTimes[i] = Double.NEGATIVE_INFINITY - } - } - - fun getValue(compound: Int): Double { - if (compoundChannels[compound] == null) { - compoundChannels[compound] = channels[keys[compound]] - } - return if (compoundChannels[compound] != null) { - if (channelTimes[compound] == currentTime && cachedValues[compound] != null) { - cachedValues[compound] ?: defaultValues[compound] - } else { - val value = compoundChannels[compound]?.value(currentTime) ?: defaultValues[compound] - cachedValues[compound] = value - value - } - } else { - defaultValues[compound] - } - } - } - - val duration: Double - get() = channels.values.maxByOrNull { it.duration() }?.duration() ?: 0.0 - - - inner class DoubleChannel(key: String, defaultValue: Double = 0.0) : - CompoundChannel(arrayOf(key), arrayOf(defaultValue)) { - operator fun getValue(keyframer: Keyframer, property: KProperty<*>): Double = getValue(0) - } - - inner class Vector2Channel(keys: Array, defaultValue: Vector2 = Vector2.ZERO) : - CompoundChannel(keys, arrayOf(defaultValue.x, defaultValue.y)) { - operator fun getValue(keyframer: Keyframer, property: KProperty<*>): Vector2 = Vector2(getValue(0), getValue(1)) - } - - inner class Vector3Channel(keys: Array, defaultValue: Vector3 = Vector3.ZERO) : - CompoundChannel(keys, arrayOf(defaultValue.x, defaultValue.y, defaultValue.z)) { - operator fun getValue(keyframer: Keyframer, property: KProperty<*>): Vector3 = - Vector3(getValue(0), getValue(1), getValue(2)) - } - - inner class Vector4Channel(keys: Array, defaultValue: Vector4 = Vector4.ZERO) : - CompoundChannel(keys, arrayOf(defaultValue.x, defaultValue.y, defaultValue.z, defaultValue.w)) { - operator fun getValue(keyframer: Keyframer, property: KProperty<*>): Vector4 = - Vector4(getValue(0), getValue(1), getValue(2), getValue(3)) - } - - inner class RGBaChannel(keys: Array, defaultValue: ColorRGBa = ColorRGBa.WHITE) : - CompoundChannel(keys, arrayOf(defaultValue.r, defaultValue.g, defaultValue.b, defaultValue.alpha)) { - operator fun getValue(keyframer: Keyframer, property: KProperty<*>): ColorRGBa = - ColorRGBa(getValue(0), getValue(1), getValue(2), getValue(3)) - } - - inner class RGBChannel(keys: Array, defaultValue: ColorRGBa = ColorRGBa.WHITE) : - CompoundChannel(keys, arrayOf(defaultValue.r, defaultValue.g, defaultValue.b)) { - operator fun getValue(keyframer: Keyframer, property: KProperty<*>): ColorRGBa = - ColorRGBa(getValue(0), getValue(1), getValue(2)) - } - - inner class DoubleArrayChannel(keys: Array, defaultValue: DoubleArray = DoubleArray(keys.size)) : - CompoundChannel(keys, defaultValue.toTypedArray()) { - operator fun getValue(keyframer: Keyframer, property: KProperty<*>): DoubleArray { - val result = DoubleArray(keys.size) - for (i in keys.indices) { - result[i] = getValue(i) - } - return result - } - } - - val channels = mutableMapOf() - - fun loadFromJson( - file: File, - format: KeyframerFormat = KeyframerFormat.SIMPLE, - parameters: Map = emptyMap(), - functions: FunctionExtensions = FunctionExtensions.EMPTY - ) { - require(file.exists()) { - "failed to load keyframer from json: '${file.absolutePath}' does not exist." - } - try { - loadFromJsonString(file.readText(), format, parameters, functions) - } catch (e: ExpressionException) { - throw ExpressionException("Error loading from '${file.path}': ${e.message ?: ""}") - } - } - - fun loadFromJson( - url: URL, - format: KeyframerFormat = KeyframerFormat.SIMPLE, - parameters: Map = emptyMap(), - functions: FunctionExtensions = FunctionExtensions.EMPTY - ) { - try { - loadFromJsonString(url.readText(), format, parameters, functions) - } catch (e: ExpressionException) { - throw ExpressionException("Error loading $format from '${url}': ${e.message ?: ""}") - } catch (e: IllegalStateException) { - throw ExpressionException("Error loading $format from '${url}': ${e.message ?: ""}") - } - } - - fun loadFromJsonString( - json: String, - format: KeyframerFormat = KeyframerFormat.SIMPLE, - parameters: Map = emptyMap(), - functions: FunctionExtensions = FunctionExtensions.EMPTY - ) { - when (format) { - KeyframerFormat.SIMPLE -> { - try { - val type = object : TypeToken>>() {}.type - val keys: List> = Gson().fromJson(json, type) - loadFromKeyObjects(keys, parameters, functions) - } catch (e: JsonSyntaxException) { - error("Error parsing simple Keyframer data: ${e.cause?.message}") - } catch (e: NullPointerException) { - error("Error parsing simple Keyframer data: ${e.cause?.message}") - } - } - - KeyframerFormat.FULL -> { - try { - val type = object : TypeToken>() {}.type - val keys: Map = Gson().fromJson(json, type) - loadFromObjects(keys, parameters, functions) - } catch (e: JsonSyntaxException) { - error("Error parsing full Keyframer data: ${e.cause?.message}") - } - } - } - } - - private val parameters = mutableMapOf() - private val prototypes = mutableMapOf>() - - fun loadFromObjects( - dict: Map, - externalParameters: Map = emptyMap(), - functions: FunctionExtensions = FunctionExtensions.EMPTY - ) { - this.parameters.clear() - this.parameters.putAll(externalParameters) - - prototypes.clear() - @Suppress("UNCHECKED_CAST") - (dict["parameters"] as? Map)?.let { lp -> - for (entry in lp) { - this.parameters[entry.key] = try { - when (val candidate = entry.value) { - is Double -> candidate - is String -> evaluateExpression(candidate, parameters, functions) - ?: error("could not evaluate expression: '$candidate'") - - is Int -> candidate.toDouble() - is Float -> candidate.toDouble() - else -> error("unknown type for parameter '${entry.key}'") - } - } catch (e: ExpressionException) { - throw ExpressionException("error in 'parameters': ${e.message ?: ""} ") - } - } - } - this.parameters.putAll(externalParameters) - - @Suppress("UNCHECKED_CAST") - (dict["prototypes"] as? Map>)?.let { - prototypes.putAll(it) - } - - @Suppress("UNCHECKED_CAST") - (dict["keys"] as? List>)?.let { keys -> - loadFromKeyObjects(keys, parameters, functions) - } - } - - private fun resolvePrototype(prototypeNames: String): Map { - val prototypeTokens = prototypeNames.split(" ").map { it.trim() }.filter { it.isNotBlank() } - val prototypeRefs = prototypeTokens.mapNotNull { prototypes[it] } - - val computed = mutableMapOf() - for (ref in prototypeRefs) { - computed.putAll(ref) - } - return computed - } - - fun loadFromKeyObjects( - keys: List>, - externalParameters: Map, - functions: FunctionExtensions - ) { - if (externalParameters !== parameters) { - parameters.clear() - parameters.putAll(externalParameters) - } - - var lastTime = 0.0 - - val channelDelegates = this::class.memberProperties - .mapNotNull { - @Suppress("UNCHECKED_CAST") - it as? KProperty1 - } - .filter { it.isAccessible = true; it.getDelegate(this) is CompoundChannel } - .associate { Pair(it.name, it.getDelegate(this) as CompoundChannel) } - - val channelKeys = channelDelegates.values.flatMap { channel -> - channel.keys.map { it } - }.toSet() - - for (delegate in channelDelegates.values) { - delegate.reset() - } - - val expressionContext = mutableMapOf() - expressionContext.putAll(parameters) - expressionContext["t"] = 0.0 - - fun easingFunctionFromName(easingCandidate: String): EasingFunction { - return when (easingCandidate) { - "linear" -> Easing.Linear.function - "back-in" -> Easing.BackIn.function - "back-out" -> Easing.BackOut.function - "back-in-out" -> Easing.BackInOut.function - "bounce-in" -> Easing.BounceIn.function - "bounce-out" -> Easing.BounceOut.function - "bounce-in-out" -> Easing.BounceInOut.function - "circ-in" -> Easing.CircIn.function - "circ-out" -> Easing.CircOut.function - "circ-in-out" -> Easing.CircInOut.function - "cubic-in" -> Easing.CubicIn.function - "cubic-out" -> Easing.CubicOut.function - "cubic-in-out" -> Easing.CubicInOut.function - "elastic-in" -> Easing.ElasticIn.function - "elastic-out" -> Easing.ElasticInOut.function - "elastic-in-out" -> Easing.ElasticOut.function - "expo-in" -> Easing.ExpoIn.function - "expo-out" -> Easing.ExpoOut.function - "expo-in-out" -> Easing.ExpoInOut.function - "quad-in" -> Easing.QuadIn.function - "quad-out" -> Easing.QuadOut.function - "quad-in-out" -> Easing.QuadInOut.function - "quart-in" -> Easing.QuartIn.function - "quart-out" -> Easing.QuartOut.function - "quart-in-out" -> Easing.QuartInOut.function - "quint-in" -> Easing.QuintIn.function - "quint-out" -> Easing.QuintOut.function - "quint-in-out" -> Easing.QuintInOut.function - "sine-in" -> Easing.SineIn.function - "sine-out" -> Easing.SineOut.function - "sine-in-out" -> Easing.SineInOut.function - "one" -> Easing.One.function - "zero" -> Easing.Zero.function - else -> error("unknown easing name '$easingCandidate'") - } - } - - fun handleKey(key: Map, path: String) { - - val prototype = (key["prototypes"] as? String)?.let { - resolvePrototype(it) - } ?: emptyMap() - - val computed = mutableMapOf() - computed.putAll(prototype) - computed.putAll(key) - - val time = try { - when (val candidate = computed["time"]) { - null -> lastTime - is String -> evaluateExpression(candidate, expressionContext, functions) - ?: error { "unknown value format for time : $candidate" } - - is Double -> candidate - is Int -> candidate.toDouble() - is Float -> candidate.toDouble() - else -> error("unknown time format for '$candidate'") - } - } catch (e: ExpressionException) { - throw ExpressionException("error in $path.'time': ${e.message ?: ""}") - } - - val duration = try { - when (val candidate = computed["duration"]) { - null -> 0.0 - is String -> evaluateExpression(candidate, expressionContext, functions) - ?: error { "unknown value format for time : $candidate" } - - is Int -> candidate.toDouble() - is Float -> candidate.toDouble() - is Double -> candidate - else -> error("unknown duration type for '$candidate") - } - } catch (e: ExpressionException) { - throw ExpressionException("error in $path.'duration': ${e.message ?: ""}") - } - - val easing = try { - when (val easingCandidate = computed["easing"]) { - null -> Easing.Linear.function - is String -> easingFunctionFromName(easingCandidate) - else -> error("unknown easing for '$easingCandidate'") - } - } catch (e: IllegalStateException) { - throw ExpressionException("error in $path.'easing': ${e.message ?: ""}") - } - - val envelope = try { - when (val candidate = computed["envelope"]) { - null -> defaultEnvelope - is DoubleArray -> candidate - is List<*> -> candidate.map { it.toString().toDouble() }.toDoubleArray() - is Array<*> -> candidate.map { it.toString().toDouble() }.toDoubleArray() - else -> error("unknown envelope for '$candidate") - } - } catch (e: IllegalStateException) { - throw ExpressionException("error in $path.'envelope': ${e.message ?: ""}") - } - - - val reservedKeys = setOf("time", "easing", "envelope") - - for (channelCandidate in computed.filter { it.key !in reservedKeys }) { - if (channelCandidate.key in channelKeys) { - val channel = channels.getOrPut(channelCandidate.key) { - KeyframerChannel() - } - - val lastValue = channel.lastValue() ?: 0.0 - expressionContext["v"] = lastValue - - val lastTime = (channel.lastTime()) ?: 0.0 - expressionContext["d"] = time - lastTime - - if (channelCandidate.value is Map<*, *>) { - @Suppress("UNCHECKED_CAST") - val valueMap = channelCandidate.value as Map - - val value = try { - when (val candidate = valueMap["value"]) { - null -> error("no value for '${channelCandidate.key}'") - is Double -> candidate - is String -> evaluateExpression(candidate, expressionContext, functions) - ?: error("unknown value format for key '${channelCandidate.key}' : $candidate") - - is Int -> candidate.toDouble() - else -> error("unknown value type for key '${channelCandidate.key}' : $candidate") - } - } catch (e: ExpressionException) { - throw ExpressionException("error in $path.'${channelCandidate.key}': ${e.message ?: ""}") - } - - val dictEasing = when (val candidate = valueMap["easing"]) { - null -> easing - is String -> easingFunctionFromName(candidate) - else -> error("unknown easing for '$candidate'") - } - - val dictEnvelope = when (val candidate = valueMap["envelope"]) { - null -> envelope - is DoubleArray -> candidate - is List<*> -> candidate.map { it.toString().toDouble() }.toDoubleArray() - is Array<*> -> candidate.map { it.toString().toDouble() }.toDoubleArray() - else -> error("unknown envelope for '$candidate") - - } - val dictDuration = try { - when (val candidate = valueMap["duration"]) { - null -> null - is Double -> candidate - is String -> evaluateExpression(candidate, expressionContext, functions) - ?: error("unknown value format for key '${channelCandidate.key}' : $candidate") - - is Int -> candidate.toDouble() - else -> error("unknown value type for key '${channelCandidate.key}' : $candidate") - } - } catch (e: ExpressionException) { - throw ExpressionException("error in $path.'${channelCandidate.key}': ${e.message ?: ""}") - } - - if (dictDuration != null) { - if (dictDuration <= 0.0) { - channel.add( - max(lastTime, time + dictDuration), - lastValue, - Easing.Linear.function, - defaultEnvelope - ) - channel.add(time, value, dictEasing, dictEnvelope) - } else { - channel.add(time, lastValue, Easing.Linear.function, defaultEnvelope) - channel.add(time + dictDuration, value, dictEasing, dictEnvelope) - } - } else { - channel.add(time, value, dictEasing, dictEnvelope) - } - - } else { - val value = try { - when (val candidate = channelCandidate.value) { - is Double -> candidate - is String -> evaluateExpression(candidate, expressionContext, functions) - ?: error("unknown value format for key '${channelCandidate.key}' : $candidate") - - is Int -> candidate.toDouble() - else -> error("unknown value type for key '${channelCandidate.key}' : $candidate") - } - } catch (e: ExpressionException) { - throw ExpressionException("error in $path.'${channelCandidate.key}': ${e.message ?: ""}") - } - channel.add(time, value, easing, envelope) - } - } - } - lastTime = time + duration - expressionContext["t"] = lastTime - } - - for ((index, key) in keys.withIndex()) { - handleKey(key, "keys[$index]") - } - } -} diff --git a/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerChannel.kt b/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerChannel.kt deleted file mode 100644 index fca59311..00000000 --- a/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerChannel.kt +++ /dev/null @@ -1,37 +0,0 @@ -import org.openrndr.extra.easing.Easing -import org.openrndr.extra.keyframer.KeyframerChannel -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNull - -class TestKeyframerChannel { - @Test - fun `a keyframer channel without keys`() { - val kfc = KeyframerChannel() - assertNull(kfc.value(0.0)) - } - - @Test - fun `a keyframer channel with a single key`() { - val kfc = KeyframerChannel() - - kfc.add(0.0, 1.0, Easing.Linear.function) - val value = kfc.value(0.0) - if (value != null) { - assertEquals(1.0, value, 10E-6) - } - assertNull(kfc.value(-1.0)) - } - - @Test - fun `a keyframer channel with two keys`() { - val kfc = KeyframerChannel() - kfc.add(0.0, 1.0, Easing.Linear.function) - kfc.add(1.0, 2.0, Easing.Linear.function) - val value = kfc.value(0.0) - if (value != null) { - assertEquals(1.0, value, 10E-6) - } - assertNull(kfc.value(-1.0)) - } -} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerErrors.kt b/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerErrors.kt deleted file mode 100644 index 634ec926..00000000 --- a/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerErrors.kt +++ /dev/null @@ -1,100 +0,0 @@ -import org.junit.jupiter.api.assertThrows -import org.openrndr.extra.expressions.ExpressionException -import kotlin.test.Test - -import org.openrndr.extra.keyframer.Keyframer -import org.openrndr.extra.keyframer.KeyframerFormat -import java.io.File -import kotlin.IllegalStateException - -private fun testFile(path: String): File { - val test = File(".") - return if (test.absolutePath.replace("\\","/").endsWith("orx-keyframer/.")) { - File(path) - } else { - File("orx-keyframer/$path") - } -} - -class TestKeyframerErrors { - class Animation : Keyframer() { - val position by Vector2Channel(arrayOf("x", "y")) - } - - @Test - fun `loading a faulty json`() { - val animation = Animation() - val json = """ - """ - assertThrows { - animation.loadFromJsonString(json) - } - } - - @Test - fun `loading a non existing json`() { - val animation = Animation() - - assertThrows { - animation.loadFromJson(testFile("this-does-not-exist")) - } - - } - @Test - fun `loading a json with a faulty time expression (1)`() { - - File(".").apply { - println(this.absolutePath) - } - - - val animation = Animation() - - assertThrows { - animation.loadFromJson( - testFile("src/test/resources/error-reporting/time-01.json"), - format = KeyframerFormat.SIMPLE - ) - } //`with message` "Error loading from '${testName("src/test/resources/error-reporting/time-01.json")}': error in keys[0].'time': parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]" - - } - - // Paths.sep - // - //Expected ,1:0] ]>, - // actual ,1:0] ]>. - - @Test - fun `loading a json with a faulty time expression (2) `() { - val animation = Animation() - assertThrows { - animation.loadFromJson( - testFile("src/test/resources/error-reporting/time-02.json"), - format = KeyframerFormat.SIMPLE - ) - } //`with message` "Error loading from '${testName("src/test/resources/error-reporting/time-02.json")}': error in keys[0].'time': error in evaluation of 'doesNotExist': unresolved variable: 'doesNotExist'" - - } - @Test - fun `loading a json with a non-existing easing`() { - val animation = Animation() - assertThrows { - animation.loadFromJson( - testFile("src/test/resources/error-reporting/easing.json"), - format = KeyframerFormat.SIMPLE - ) - } //`with message` "Error loading from '${testName("src/test/resources/error-reporting/easing.json")}': error in keys[0].'easing': unknown easing name 'garble'" - } - - @Test - fun `loading a json with a faulty value (1)`() { - val animation = Animation() - - assertThrows { - animation.loadFromJson( - testFile("src/test/resources/error-reporting/value-01.json"), - format = KeyframerFormat.SIMPLE - ) - } //`with message` "Error loading from '${testName("src/test/resources/error-reporting/value-01.json")}': error in keys[0].'x': error in evaluation of 'garble': unresolved variable: 'garble'" - } -} diff --git a/orx-jvm/orx-keyframer/src/test/resources/error-reporting/easing.json b/orx-jvm/orx-keyframer/src/test/resources/error-reporting/easing.json deleted file mode 100644 index 4a8f64ca..00000000 --- a/orx-jvm/orx-keyframer/src/test/resources/error-reporting/easing.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - { - "easing": "garble" - } -] \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/test/resources/error-reporting/time-01.json b/orx-jvm/orx-keyframer/src/test/resources/error-reporting/time-01.json deleted file mode 100644 index 15abf1f2..00000000 --- a/orx-jvm/orx-keyframer/src/test/resources/error-reporting/time-01.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - { - "time": ")(" - } -] \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/test/resources/error-reporting/time-02.json b/orx-jvm/orx-keyframer/src/test/resources/error-reporting/time-02.json deleted file mode 100644 index 5c5c4cdb..00000000 --- a/orx-jvm/orx-keyframer/src/test/resources/error-reporting/time-02.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - { - "time": "doesNotExist" - } -] \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/test/resources/error-reporting/value-01.json b/orx-jvm/orx-keyframer/src/test/resources/error-reporting/value-01.json deleted file mode 100644 index aae93639..00000000 --- a/orx-jvm/orx-keyframer/src/test/resources/error-reporting/value-01.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "time": "0.0", - "x": "garble", - "y": "garble" - } -] \ No newline at end of file diff --git a/orx-jvm/orx-kinect-common/build.gradle.kts b/orx-jvm/orx-kinect-common/build.gradle.kts deleted file mode 100644 index 84da91a0..00000000 --- a/orx-jvm/orx-kinect-common/build.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} -dependencies { - api(project(":orx-depth-camera")) - implementation(openrndr.application.core) - implementation(openrndr.math) -} \ No newline at end of file diff --git a/orx-jvm/orx-kinect-common/src/main/kotlin/Kinect.kt b/orx-jvm/orx-kinect-common/src/main/kotlin/Kinect.kt deleted file mode 100644 index 53ddea81..00000000 --- a/orx-jvm/orx-kinect-common/src/main/kotlin/Kinect.kt +++ /dev/null @@ -1,140 +0,0 @@ -package org.openrndr.extra.kinect - -import org.openrndr.draw.* -import org.openrndr.extra.depth.camera.DepthCamera -import org.openrndr.math.IntVector2 -import org.openrndr.resourceUrl -import java.lang.RuntimeException -import java.net.URL -import java.nio.ByteBuffer -import java.nio.ByteOrder -import kotlin.reflect.KClass - -/** - * Represents all the accessible kinects handled by a specific driver (V1, V2, etc.). - */ -interface Kinect { - - /** - * Lists available kinect devices. - */ - fun listDevices(): List - - /** - * Opens kinect device of a given index. - * - * @param index the kinect device index (starts with 0). If no value specified, - * it will default to 0. - * @throws KinectException if device of such an index does not exist, - * or it was already started. - * @see listDevices - */ - fun openDevice(index: Int = 0): Device - - /** - * Opens kinect device of a given serial number. - * - * @param serialNumber the kinect device serialNumber. - * @throws KinectException if device of such a serial number does not exist - * , or it was already started. - * @see listDevices - */ - fun openDevice(serialNumber: String): Device - - /** - * The list of kinect devices which are already opened and haven't been closed. - */ - val activeDevices: List - - /** - * Represents physical kinect device. - */ - interface Device { - - /** - * Provides information about kinect device. - * - * Note: in implementation it can be extended with any - * additional information next to the serial number. - */ - interface Info { - val serialNumber: String - } - - val info: Info - - val depthCamera: KinectDepthCamera - - fun close() - - } - -} - -/** - * Generic interface for all the kinect cameras. - */ -interface KinectCamera { - - var enabled: Boolean - -} - -interface KinectDepthCamera : KinectCamera, DepthCamera { - /* no special attributes at the moment */ -} - -open class KinectException(msg: String) : RuntimeException(msg) - -fun kinectRawDepthByteBuffer(resolution: IntVector2): ByteBuffer = - ByteBuffer.allocateDirect( - resolution.x * resolution.y * 2 - ).also { - it.order(ByteOrder.nativeOrder()) - } - -fun KClass.filterFrom(resource: String, flipH: Boolean, flipV: Boolean): Filter { - val url = resourceUrl(resource, this) - val preamble = - (if (flipH) "#define KINECT_FLIPH\n" else "") + - (if (flipV) "#define KINECT_FLIPV\n" else "") - return Filter( - filterShaderFromCode( - "$preamble\n${URL(url).readText()}", - "kinect-shader: $url + flipH: $flipH, flipV: $flipV" - ) - ) -} - -class KinectDepthMappers(resource: String, `class`: KClass) { - - private val flipHFalseVFalse = `class`.filterFrom(resource, flipH = false, flipV = false) - private val flipHFalseVTrue = `class`.filterFrom(resource, flipH = false, flipV = true) - private val flipHTrueVFalse = `class`.filterFrom(resource, flipH = true, flipV = false) - private val flipHTrueVTrue = `class`.filterFrom(resource, flipH = true, flipV = true) - - fun select(flipH: Boolean, flipV: Boolean): Filter = - if (flipH) { - if (flipV) flipHTrueVTrue - else flipHTrueVFalse - } else { - if (flipV) flipHFalseVTrue - else flipHFalseVFalse - } - - fun update(resolution: IntVector2) { - val resolutionXMinus1 = resolution.x - 1 - flipHTrueVFalse.parameters["resolutionXMinus1"] = resolutionXMinus1 - flipHTrueVTrue.parameters["resolutionXMinus1"] = resolutionXMinus1 - } - - fun forEach(block: (filter: Filter) -> Unit) { - block(flipHFalseVFalse) - block(flipHFalseVTrue) - block(flipHTrueVFalse) - block(flipHTrueVTrue) - } - -} - -fun depthToRawNormalizedMappers() = KinectDepthMappers("depth-to-raw-normalized.frag", Kinect::class) diff --git a/orx-jvm/orx-kinect-common/src/main/resources/org/openrndr/extra/kinect/depth-to-raw-normalized.frag b/orx-jvm/orx-kinect-common/src/main/resources/org/openrndr/extra/kinect/depth-to-raw-normalized.frag deleted file mode 100644 index 025ae902..00000000 --- a/orx-jvm/orx-kinect-common/src/main/resources/org/openrndr/extra/kinect/depth-to-raw-normalized.frag +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef KINECT_FLIPV -layout(origin_upper_left) in vec4 gl_FragCoord; -#endif - -uniform usampler2D tex0; // kinect raw -uniform float maxDepthValue; -#ifdef KINECT_FLIPH -uniform int resolutionXMinus1; -#endif -out float outDepth; // measured in meters - -void main() { - ivec2 uv = ivec2(gl_FragCoord); - #ifdef KINECT_FLIPH - uv = ivec2(resolutionXMinus1 - uv.x, uv.y); - #endif - uint uintDepth = texelFetch(tex0, uv, 0).r; - outDepth = float(uintDepth) / maxDepthValue; -} diff --git a/orx-jvm/orx-kinect-v1-demo/build.gradle.kts b/orx-jvm/orx-kinect-v1-demo/build.gradle.kts deleted file mode 100644 index d566af50..00000000 --- a/orx-jvm/orx-kinect-v1-demo/build.gradle.kts +++ /dev/null @@ -1,14 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(project(":orx-jvm:orx-kinect-v1")) - implementation(project(":orx-jvm:orx-depth-camera-calibrator")) - implementation(project(":orx-fx")) - implementation(project(":orx-jvm:orx-gui")) - runtimeOnly(project(":orx-jvm:orx-kinect-v1")) - runtimeOnly(openrndr.application.glfw) -} \ No newline at end of file diff --git a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo01BasicUseCase.kt b/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo01BasicUseCase.kt deleted file mode 100644 index a90dd0b9..00000000 --- a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo01BasicUseCase.kt +++ /dev/null @@ -1,26 +0,0 @@ -package org.openrndr.extra.kinect.v1.demo - -import org.openrndr.application -import org.openrndr.extra.kinect.v1.Kinect1 - -/** - * Basic kinect1 use case showing continuous stream from the depth camera. - * - * Note: kinect depth map is stored only on the RED color channel to save - * space. Therefore depth map is displayed only in the red tones. - */ -fun main() = application { - configure { // default resolution of the Kinect v1 depth camera - width = 640 - height = 480 - } - program { - val kinect = extend(Kinect1()) - val device = kinect.openDevice() - device.depthCamera.flipH = true // to make a mirror - device.depthCamera.enabled = true - extend { - drawer.image(device.depthCamera.currentFrame) - } - } -} diff --git a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo02MotionCaptureUseCase.kt b/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo02MotionCaptureUseCase.kt deleted file mode 100644 index 95308cdc..00000000 --- a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo02MotionCaptureUseCase.kt +++ /dev/null @@ -1,111 +0,0 @@ -package org.openrndr.extra.kinect.v1.demo - -import org.openrndr.application -import org.openrndr.draw.colorBuffer -import org.openrndr.extra.depth.camera.DepthMeasurement -import org.openrndr.extra.fx.colormap.TurboColormap -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.kinect.v1.Kinect1 -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.extra.parameters.DoubleParameter - -/** - * A use case where "virtual walls" can be established within certain - * depth ranges. Useful for actual installations, like interactive - * projections in the form of a "mirror" for the human silhouette. - * The measurement in meters helps in calibration. - */ -fun main() = application { - configure { // default resolution of the Kinect v1 depth camera - width = 640 - height = 480 - } - program { - val kinect = extend(Kinect1()) - val device = kinect.openDevice() - val camera = device.depthCamera - camera.flipH = true // to make a mirror - camera.depthMeasurement = DepthMeasurement.METERS - val turboColormap = TurboColormap().apply { - minValue = .5 - maxValue = 5.0 - curve = 1.0 - } - val outputBuffer = colorBuffer( - camera.resolution.x, - camera.resolution.y - ) - - /* - * Note: the code specified in onFrameReceived will be executed as soon as - * possible, also when GPU is idle. - * - * Also TurboColormap filter will be applied only after actual new frame - * from kinect is received instead of being applied for each - * program frame. Kinect has different refresh rate (30 fps) than usual - * display. - */ - camera.onFrameReceived { frame -> - turboColormap.apply(frame, outputBuffer) - } - camera.enabled = true - - @Suppress("unused") - val settings = object { - - @BooleanParameter(label = "enabled", order = 0) - var enabled: Boolean - get() = camera.enabled - set(value) { - camera.enabled = value - } - - @BooleanParameter(label = "flipH", order = 1) - var flipH: Boolean - get() = camera.flipH - set(value) { - camera.flipH = value - } - - @BooleanParameter(label = "flipV", order = 2) - var flipV: Boolean - get() = camera.flipV - set(value) { - camera.flipV = value - } - - /* - Note: we could use turboColormap parameters directly in the GUI, however the - high range is cap to 1.0 there, and we want to use calibration in meters. - Increase 5.0 to something higher if you are calibrating for a bigger space. - */ - @DoubleParameter(label = "min distance", order = 3, low = 0.2, high = 5.0) - var minDistance: Double - get() = turboColormap.minValue - set(value) { - turboColormap.minValue = value - } - - @DoubleParameter(label = "max distance", order = 4, low = 0.2, high = 5.0) - var maxDistance: Double - get() = turboColormap.maxValue - set(value) { turboColormap.maxValue = value } - - @DoubleParameter(label = "distance curve", order = 5, low = 0.01, high = 10.0) - var curve: Double - get() = turboColormap.curve - set(value) { - turboColormap.curve = value - } - - } - extend(GUI()) { - persistState = false - compartmentsCollapsedByDefault = false - add(settings, label = "depth camera") - } - extend { - drawer.image(outputBuffer) - } - } -} diff --git a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo03DepthToColorMaps.kt b/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo03DepthToColorMaps.kt deleted file mode 100644 index 888fe6a6..00000000 --- a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo03DepthToColorMaps.kt +++ /dev/null @@ -1,93 +0,0 @@ -package org.openrndr.extra.kinect.v1.demo - -import org.openrndr.application -import org.openrndr.draw.ColorFormat -import org.openrndr.draw.colorBuffer -import org.openrndr.extra.fx.colormap.GrayscaleColormap -import org.openrndr.extra.fx.colormap.SpectralZucconiColormap -import org.openrndr.extra.fx.colormap.TurboColormap -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.kinect.v1.Kinect1 -import org.openrndr.extra.parameters.BooleanParameter -import org.openrndr.math.Vector2 - -/** - * Shows 4 different color representations of the depth map: - * - * * the original depth map stored as RED channel values - * * the same values expressed as gray tones - * * zucconi6 color map according to natural light dispersion as described - * by Alan Zucconi in - * [Improving the Rainbow](https://www.alanzucconi.com/2017/07/15/improving-the-rainbow/) - * article - * * turbo color map according to - * [Turbo, An Improved Rainbow Colormap for Visualization](https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html) - * by Google. - * - * Note: the values are normalized in range 0-1, not in meters. - * @see GrayscaleColormap - * @see SpectralZucconiColormap - * @see TurboColormap - */ -fun main() = application { - val guiOffset = 200 - configure { - width = 2 * 640 + guiOffset - height = 2 * 480 - } - program { - val kinect = extend(Kinect1()) - val device = kinect.openDevice() - val camera = device.depthCamera - fun outputBuffer() = colorBuffer( - camera.resolution.x, - camera.resolution.y, - format = ColorFormat.RGB - ) - val grayscaleColormap = GrayscaleColormap() - val spectralZucconiColormap = SpectralZucconiColormap() - val turboColormap = TurboColormap() - val grayscaleBuffer = outputBuffer() - val zucconiBuffer = outputBuffer() - val turboBuffer = outputBuffer() - @Suppress("unused") - val settings = object { - - @BooleanParameter(label = "enabled", order = 0) - var enabled: Boolean - get() = camera.enabled - set(value) { camera.enabled = value } - - @BooleanParameter(label = "flipH", order = 1) - var flipH: Boolean - get() = camera.flipH - set(value) { camera.flipH = value } - - @BooleanParameter(label = "flipV", order = 2) - var flipV: Boolean - get() = camera.flipV - set(value) { camera.flipV = value } - - } - camera.onFrameReceived { frame -> - grayscaleColormap.apply(frame, grayscaleBuffer) - spectralZucconiColormap.apply(frame, zucconiBuffer) - turboColormap.apply(frame, turboBuffer) - } - camera.enabled = true - extend(GUI()) { - persistState = false - compartmentsCollapsedByDefault = false - add(settings, label = "depth camera") - add(grayscaleColormap) - add(spectralZucconiColormap) - add(turboColormap) - } - extend { - drawer.image(camera.currentFrame, guiOffset.toDouble(), 0.0) - drawer.image(grayscaleBuffer, guiOffset + camera.resolution.x.toDouble(), 0.0) - drawer.image(turboBuffer, guiOffset.toDouble(), camera.resolution.y.toDouble()) - drawer.image(zucconiBuffer, Vector2(guiOffset.toDouble(), 0.0) + camera.resolution.vector2) - } - } -} diff --git a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo04SwitchOffLed.kt b/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo04SwitchOffLed.kt deleted file mode 100644 index 9eff2b41..00000000 --- a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo04SwitchOffLed.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.openrndr.extra.kinect.v1.demo - -import org.bytedeco.libfreenect.global.freenect -import org.openrndr.application -import org.openrndr.extra.kinect.v1.Kinect1 - -/** - * This demo shows how to execute freenect commands directly, either globally - * or on the device. In this case demo is switching off LED light completely, - * which might be desirable for the aesthetics of an installation, - * however LED turned on might be still a useful indicator during development. - */ -fun main() = application { - configure { // default resolution of the Kinect v1 depth camera - width = 640 - height = 480 - } - program { - val kinect = extend(Kinect1()) - val device = kinect.openDevice() - device.executeInFreenectDeviceContext( - "turn off led" - ) { _, _, dev -> - freenect.freenect_set_led(dev, freenect.LED_OFF) - } - device.depthCamera.enabled = true - extend { - drawer.image(device.depthCamera.currentFrame) - } - } -} diff --git a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo05MultipleDevices.kt b/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo05MultipleDevices.kt deleted file mode 100644 index 0efea25f..00000000 --- a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo05MultipleDevices.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.openrndr.extra.kinect.v1.demo - -import org.openrndr.application -import org.openrndr.extra.kinect.v1.Kinect1 - -/** - * Render depth data from 2 kinect1 devices side-by-side. - */ -fun main() = application { - configure { - width = 640 * 2 - height = 480 - } - program { - val kinect = extend(Kinect1()) - /* - on production system you might consider using stable kinect serial numbers, - instead of index numbers, to avoid reordering of devices already installed - in physical space. - */ - val depthCamera1 = kinect.openDevice(0).depthCamera - val depthCamera2 = kinect.openDevice(1).depthCamera - depthCamera1.enabled = true - depthCamera1.flipH = true - depthCamera2.enabled = true - depthCamera2.flipH = true - extend { - drawer.image(depthCamera1.currentFrame) - drawer.image(depthCamera2.currentFrame, depthCamera1.resolution.x.toDouble(), 0.0) - } - } -} diff --git a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo07NativeFreenectCommands.kt b/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo07NativeFreenectCommands.kt deleted file mode 100644 index 432281b0..00000000 --- a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo07NativeFreenectCommands.kt +++ /dev/null @@ -1,44 +0,0 @@ -package org.openrndr.extra.kinect.v1.demo - -import org.bytedeco.libfreenect.global.freenect -import org.bytedeco.libfreenect.global.freenect.* -import org.openrndr.application -import org.openrndr.extra.kinect.v1.Kinect1 - -/** - * Even though this library is abstracting freenect access, it is still - * possible to call any low level kinect API through execute methods. - * The calls are executed in separate kinect runner thread but they will - * block the calling thread until the result is returned. - */ -fun main() = application { - program { - val kinect = extend(Kinect1()) - /* - Blocking version will wait for the result, specifying the name - makes it easier to identify this call in logs when it is finally - executed on kinect. Note: enabling TRACE log level is required. - */ - val numberOfKinectDevices = kinect.executeInFreenectContextBlocking( - name = "numberOfKinectDevices" - ) { ctx, _ -> - freenect.freenect_num_devices(ctx) - } - println("numberOfKinectDevices: $numberOfKinectDevices") - val device = kinect.openDevice() - val maxTilt = 90.0 - var tilt = 0.0 - extend { - device.executeInFreenectDeviceContext("disco LED") { _, _, dev -> - freenect_set_led(dev, (seconds * 10).toInt() % 7) // disco - } - val newTilt = if ((seconds % 10) < 5) -maxTilt else maxTilt - if (tilt != newTilt) { - device.executeInFreenectDeviceContext("tilt change") { _, _, dev -> - freenect_set_tilt_degs(dev, tilt) - } - tilt = newTilt - } - } - } -} diff --git a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo08LogLevelFlood.kt b/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo08LogLevelFlood.kt deleted file mode 100644 index 48940cbe..00000000 --- a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo08LogLevelFlood.kt +++ /dev/null @@ -1,30 +0,0 @@ -package org.openrndr.extra.kinect.v1.demo - -import org.openrndr.application -import org.openrndr.extra.kinect.v1.Kinect1 - -/** - * Here you can see freenect FLOOD log level in action. - * - * Note: technically it would be possible to redirect kinect log to - * slf4j logger in the implementation of [Kinect1], however I removed - * this callback and left logs on the standard out, because it might get so noisy, - * that native-to-JVM round trip with conversion into [String] for JVM - * logging might completely kill the performance and result in - * stack overflow exception. - */ -fun main() = application { - configure { // default resolution of the Kinect v1 depth camera - width = 640 - height = 480 - } - program { - val kinect = extend(Kinect1()) - kinect.logLevel = Kinect1.LogLevel.FLOOD - val device = kinect.openDevice() - device.depthCamera.enabled = true - extend { - drawer.image(device.depthCamera.currentFrame) - } - } -} diff --git a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo09RawDepthProcessing.kt b/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo09RawDepthProcessing.kt deleted file mode 100644 index a214e172..00000000 --- a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo09RawDepthProcessing.kt +++ /dev/null @@ -1,49 +0,0 @@ -package org.openrndr.extra.kinect.v1.demo - -import org.openrndr.application -import org.openrndr.draw.Filter -import org.openrndr.draw.colorBuffer -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.depth.camera.DepthMeasurement -import org.openrndr.extra.kinect.v1.Kinect1 - -/** - * It is possible to rewrite raw kinect value interpretation completely - * while keeping all the performance characteristics. - * - * Note: when depth measurement is set to RAW, the flip options does not apply. - */ -fun main() = application { - configure { // default resolution of the Kinect v1 depth camera - width = 640 - height = 480 - } - program { - val kinect = extend(Kinect1()) - val device = kinect.openDevice() - val camera = device.depthCamera - camera.depthMeasurement = DepthMeasurement.RAW - val outputBuffer = colorBuffer(camera.resolution.x, camera.resolution.y) - val filter = Filter(filterShaderFromCode(""" - layout(origin_upper_left) in vec4 gl_FragCoord; - uniform usampler2D tex0; // kinect raw - out vec4 o_color; - - void main() { - ivec2 uv = ivec2(gl_FragCoord); - uint uintDepth = texelFetch(tex0, uv, 0).r; - float depth = float(uintDepth) / 2047.; - o_color = vec4(vec3(depth), 1.); - } - """.trimIndent(), - "raw filter") - ) - camera.onFrameReceived { frame -> - filter.apply(frame, outputBuffer) - } - device.depthCamera.enabled = true - extend { - drawer.image(outputBuffer) - } - } -} diff --git a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo10DepthCameraCalibration.kt b/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo10DepthCameraCalibration.kt deleted file mode 100644 index 25fa68e8..00000000 --- a/orx-jvm/orx-kinect-v1-demo/src/main/kotlin/Kinect1Demo10DepthCameraCalibration.kt +++ /dev/null @@ -1,117 +0,0 @@ -package org.openrndr.extra.kinect.v1.demo - -import org.openrndr.Fullscreen -import org.openrndr.application -import org.openrndr.draw.Filter -import org.openrndr.draw.colorBuffer -import org.openrndr.draw.filterShaderFromCode -import org.openrndr.extra.depth.camera.DepthMeasurement -import org.openrndr.extra.depth.camera.calibrator.DepthCameraCalibrator -import org.openrndr.extra.depth.camera.calibrator.isolatedWithCalibration -import org.openrndr.extra.gui.GUI -import org.openrndr.extra.kinect.v1.Kinect1 - -/** - * How to use [DepthCameraCalibrator] with [Kinect1]? - */ -fun main() = application { - configure { - fullscreen = Fullscreen.CURRENT_DISPLAY_MODE - } - program { - - val kinect = extend(Kinect1()) - val device = kinect.openDevice() - val camera = device.depthCamera - // depth measurement in meters is required by the calibrator - camera.depthMeasurement = DepthMeasurement.METERS - val kinectResolution = camera.resolution - - val outputBuffer = colorBuffer( - kinectResolution.x, - kinectResolution.y - ) - - // simple visual effect applied to kinect data - val spaceRangeExtractor = SpaceRangeExtractor() - camera.onFrameReceived { frame -> - spaceRangeExtractor.apply(frame, outputBuffer) - } - val calibrator = DepthCameraCalibrator(this, camera) - - val gui = GUI() - calibrator.addControlsTo(gui) - - /* - Note: remember that extend(gui) has to be called after all the parameter - controls are added. - - Also extensions are rendered in reverse order, if we start with gui, - it will not be covered by calibrator view when calibrator is enabled - */ - extend(gui) - - /* - if it's an interactive installation, probably we don't want to - show GUI on startup. It can be shown by pressing F11. - */ - gui.visible = false - - /* - Registering this callback here after gui will prevent it from - being triggered multiple times when GUI parameters are restored - on startup. - */ - calibrator.onCalibrationChange { calibration -> - spaceRangeExtractor.minDepth = calibration.minDepth - spaceRangeExtractor.maxDepth = calibration.maxDepth - } - extend(calibrator) - camera.enabled = true - - extend { - val calibration = calibrator.getCalibration(camera) - drawer.isolatedWithCalibration(calibration) { - image( - colorBuffer = outputBuffer, - position = calibration.position, - width = calibration.width, - height = calibration.height - ) - } - } - - // switching calibrator view on and off with keyboard - program.keyboard.keyDown.listen { - if (it.name == "k") { - calibrator.enabled = !calibrator.enabled - } - } - - } - -} - -/** - * A visual effect applied to kinect data in this demonstration. - * Everything is black, except for the white pixels within the range - * of 2 virtual walls positioned at [minDepth] at front of the - * viewer and [maxDepth] behind the viewer. - */ -class SpaceRangeExtractor : Filter(filterShaderFromCode(""" - uniform sampler2D tex0; // kinect raw - uniform float minDepth; - uniform float maxDepth; - out vec4 o_color; - void main() { - ivec2 uv = ivec2(gl_FragCoord.xy); - float depth = texelFetch(tex0, uv, 0).r; - float luma = ((depth >= minDepth) && (depth <= maxDepth)) ? 1.0 : 0.0; - o_color = vec4(vec3(luma), 1.0); - } - """.trimIndent(), - "space range extractor" -)) { - var minDepth: Double by parameters - var maxDepth: Double by parameters -} diff --git a/orx-jvm/orx-kinect-v1-demo/src/main/resources/logback.xml b/orx-jvm/orx-kinect-v1-demo/src/main/resources/logback.xml deleted file mode 100644 index 3d7044e6..00000000 --- a/orx-jvm/orx-kinect-v1-demo/src/main/resources/logback.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - true - - - - %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - diff --git a/orx-jvm/orx-kinect-v1/README.md b/orx-jvm/orx-kinect-v1/README.md deleted file mode 100644 index ab692097..00000000 --- a/orx-jvm/orx-kinect-v1/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# orx-kinect-v1 - -Support for the Kinect V1 RGB and depth cameras. - -If using Linux, add the [udev rules](https://github.com/OpenKinect/libfreenect/tree/master/platform/linux/udev) to be able to access the camera without being a root user. - -## Example usage - -```kotlin -fun main() = application { - configure { - fullscreen = Fullscreen.CURRENT_DISPLAY_MODE - } - program { - val kinects = getKinectsV1() - val kinect = kinects.startDevice() - kinect.depthCamera.enabled = true - kinect.depthCamera.mirror = true - extend(kinect) - extend { - drawer.image(kinect.depthCamera.currentFrame) - } - } -} -``` diff --git a/orx-jvm/orx-kinect-v1/build.gradle.kts b/orx-jvm/orx-kinect-v1/build.gradle.kts deleted file mode 100644 index 4f80a155..00000000 --- a/orx-jvm/orx-kinect-v1/build.gradle.kts +++ /dev/null @@ -1,32 +0,0 @@ - -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") - id("org.openrndr.extra.convention.variant") -} -variants { - val nativeLibs = listOf(libs.libfreenect, sharedLibs.javacpp) - - val platforms = listOf( - Triple(OperatingSystemFamily.WINDOWS, MachineArchitecture.X86_64, "windows-x86_64"), - Triple(OperatingSystemFamily.MACOS, MachineArchitecture.X86_64, "macosx-x86_64"), - Triple(OperatingSystemFamily.LINUX, MachineArchitecture.X86_64, "linux-x86_64"), - Triple(OperatingSystemFamily.LINUX, MachineArchitecture.ARM64, "linux-arm64"), - ) - - for ((os, arch, classifier) in platforms) { - platform(os, arch) { - dependencies { - nativeLibs.forEach { - runtimeOnly(it.get().withClassifier(classifier)) - } - } - } - } -} -dependencies { - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(sharedLibs.kotlin.coroutines) - api(project(":orx-jvm:orx-kinect-common")) - api(libs.libfreenect) -} \ No newline at end of file diff --git a/orx-jvm/orx-kinect-v1/src/main/kotlin/Kinect1.kt b/orx-jvm/orx-kinect-v1/src/main/kotlin/Kinect1.kt deleted file mode 100644 index 482a456c..00000000 --- a/orx-jvm/orx-kinect-v1/src/main/kotlin/Kinect1.kt +++ /dev/null @@ -1,571 +0,0 @@ -package org.openrndr.extra.kinect.v1 - -import io.github.oshai.kotlinlogging.KotlinLogging -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.* -import org.bytedeco.javacpp.Pointer -import org.bytedeco.libfreenect.* -import org.bytedeco.libfreenect.global.freenect.* -import org.openrndr.Extension -import org.openrndr.Program -import org.openrndr.draw.* -import org.openrndr.extra.depth.camera.DepthMeasurement -import org.openrndr.extra.kinect.* -import org.openrndr.launch -import org.openrndr.math.IntVector2 -import java.util.* -import java.util.concurrent.* -import kotlin.concurrent.thread - -class Kinect1Exception(msg: String) : KinectException(msg) - -class Kinect1 : Kinect, Extension { - - override var enabled: Boolean = true - - /** - * Without the delay between starting depth camera and - * registering depth callback, no frames are transferred - * at all. However this problem happens only on the first - * try with freshly connected kinect. - * Subsequent runs of the same program don't require - * this delay at all. - */ - private var cameraInitializationDelay: Long = 100 - - class DeviceInfo( - override val serialNumber: String, - ) : Kinect.Device.Info { - override fun toString(): String { - return "Kinect1[$serialNumber]" - } - } - - /** - * Log level for native freenect logging. - * - * Note: logs will appear on standard out for performance reasons. - * - * @param code the code of corresponding freenect log level. - * @see Kinect1.logLevel - */ - @Suppress("unused") - enum class LogLevel(val code: Int) { - - /** Crashing/non-recoverable errors. */ - FATAL(FREENECT_LOG_FATAL), - - /** Major errors. */ - ERROR(FREENECT_LOG_ERROR), - - /** Warning messages. */ - WARNING(FREENECT_LOG_WARNING), - - /** Important messages. */ - NOTICE(FREENECT_LOG_NOTICE), - - /** Log for normal messages. */ - INFO(FREENECT_LOG_INFO), - - /** Log for useful development messages. */ - DEBUG(FREENECT_LOG_DEBUG), - - /** Log for slightly less useful messages. */ - SPEW(FREENECT_LOG_SPEW), - - /** Log EVERYTHING. May slow performance. */ - FLOOD(FREENECT_LOG_FLOOD); - - } - - /** - * Kinect native log level, defaults to `INFO`. - */ - var logLevel: LogLevel - get() = freenect.logLevel - set(value) { - freenect.logLevel = value - } - - private val logger = KotlinLogging.logger {} - - private lateinit var program: Program - - private lateinit var freenect: Freenect - - override fun setup(program: Program) { - if (!enabled) { - return - } - logger.info { "Starting Kinect1 support" } - this.program = program - freenect = Freenect(initialLogLevel = LogLevel.INFO) - } - - override fun listDevices(): List = freenect.callBlocking( - "listDevices" - ) { _, _ -> - freenect.listDevices() - } - - override fun openDevice(index: Int): V1Device { - val result = freenect.callBlocking("openDeviceByIndex") { ctx, _ -> - val devices = freenect.listDevices() - if (devices.isEmpty()) { - throw KinectException("No kinect devices detected, cannot open any") - } else if (index >= devices.size) { - throw KinectException("Invalid device index, number of kinect1 devices: ${devices.size}") - } - Pair( - openFreenectDevice( - ctx, - devices[index].serialNumber - ), - devices[index] - ) - } - val device = V1Device(result.first, result.second) - mutableActiveDevices.add(device) - return device - } - - override fun openDevice(serialNumber: String): V1Device { - val dev = freenect.callBlocking("openDeviceBySerial") { ctx, _ -> - openFreenectDevice(ctx, serialNumber) - } - val device = V1Device(dev, DeviceInfo(serialNumber)) - mutableActiveDevices.add(device) - return device - } - - private val mutableActiveDevices = LinkedList() - - override val activeDevices: List - get() = mutableActiveDevices - - private fun openFreenectDevice( - ctx: freenect_context, - serialNumber: String, - ): freenect_device { - val dev = freenect_device() - freenect.checkReturn( - freenect_open_device_by_camera_serial(ctx, dev, serialNumber) - ) - return dev - } - - override fun shutdown(program: Program) { - if (!enabled) { - return - } - logger.info { "Shutting down Kinect1 support" } - logger.debug { "Closing active devices, count: ${mutableActiveDevices.size}" } - mutableActiveDevices.forEach { - it.close() - } - mutableActiveDevices.clear() - freenect.close() - } - - @Suppress("unused") - fun executeInFreenectContext( - name: String, - block: (ctx: freenect_context, usbCtx: freenect_usb_context) -> Unit - ) { - freenect.call(name) { ctx, usbCtx -> - block(ctx, usbCtx) - } - } - - fun executeInFreenectContextBlocking( - name: String, - block: (ctx: freenect_context, usbCtx: freenect_usb_context) -> T - ): T = freenect.callBlocking(name) { ctx, usbCtx -> - block(ctx, usbCtx) - } - - inner class V1Device( - private val dev: freenect_device, - override val info: DeviceInfo - ) : Kinect.Device { - - inner class V1DepthCamera( - override val resolution: IntVector2, - ) : KinectDepthCamera { - - private var firstStart = true - private var started = false - - private var bytesFront = kinectRawDepthByteBuffer(resolution) - private var bytesBack = kinectRawDepthByteBuffer(resolution) - private val bytesFlow = MutableStateFlow(bytesBack) // the first frame will come from bytesFront - - private val rawBuffer = colorBuffer( - resolution.x, - resolution.y, - format = ColorFormat.R, - type = ColorType.UINT16_INT - ).also { - it.filter(MinifyingFilter.NEAREST, MagnifyingFilter.NEAREST) - } - - private val processedFrameBuffer = colorBuffer( - resolution.x, - resolution.y, - format = ColorFormat.R, - type = ColorType.FLOAT16 // in the future we might want to choose the precision here - ).also { - it.filter(MinifyingFilter.LINEAR, MagnifyingFilter.LINEAR) - } - - private var mutableCurrentFrame = processedFrameBuffer - - private val depthMappers = Kinect1DepthMappers().apply { - update(resolution) - } - - override val currentFrame get() = mutableCurrentFrame - - private var onFrameReceived: (frame: ColorBuffer) -> Unit = {} - - // working on rendering thread - private val frameReceiverJob: Job = program.launch { - bytesFlow.collect { bytes -> - rawBuffer.write(bytes) - depthMappers.mapper?.apply(rawBuffer, processedFrameBuffer) - onFrameReceived(mutableCurrentFrame) - } - } - - private val freenectDepthCallback = object : freenect_depth_cb() { - override fun call( - dev: freenect_device, - depth: Pointer, - timestamp: Int - ) { - bytesFlow.tryEmit(bytesFront) - val bytesTmp = bytesBack - bytesBack = bytesFront - bytesFront = bytesTmp - freenect.checkReturn( - freenect_set_depth_buffer(dev, Pointer(bytesFront)) - ) - } - } - - override var enabled: Boolean = false - set(value) { - logger.debug { "$info.enabled = $value" } - if (value == field) { - logger.debug { "$info.enabled: doing nothing, already in state: $value" } - return - } - field = value - freenect.call("$info.enabled = $value") { _, _ -> - if (value) start() else stop() - } - } - - override var depthMeasurement: DepthMeasurement - get() = depthMappers.depthMeasurement - set(value) { - logger.debug { "$info.depthMeasurement = $value" } - depthMappers.depthMeasurement = value - mutableCurrentFrame = - if (value == DepthMeasurement.RAW) rawBuffer - else processedFrameBuffer - } - - override var flipH: Boolean - get() = depthMappers.flipH - set(value) { - logger.debug { "$info.flipH = $value" } - depthMappers.flipH = value - } - - override var flipV: Boolean - get() = depthMappers.flipV - set(value) { - logger.debug { "$info.flipV = $value" } - depthMappers.flipV = value - } - - private fun start() { - logger.info { "$info.start()" } - freenect.checkReturn( - freenect_set_depth_mode( - dev, freenect_find_depth_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_DEPTH_11BIT) - ) - ) - freenect.checkReturn(freenect_set_depth_buffer(dev, Pointer(bytesFront))) - freenect.checkReturn(freenect_start_depth(dev)) - if (firstStart) { // workaround, see comments above - Thread.sleep(cameraInitializationDelay) - firstStart = false - } - freenect_set_depth_callback(dev, freenectDepthCallback) - started = true - freenect.expectingEvents = true - } - - internal fun stop() { - logger.info { "$info.stop()" } - if (started) { - freenect.expectingEvents = false - freenect.checkReturn(freenect_stop_depth(dev)) - started = false - } else { - logger.warn { "$info.stop(): cannot stop already stopped depth camera" } - } - } - - internal fun close() { - frameReceiverJob.cancel() - } - - override fun onFrameReceived(block: (frame: ColorBuffer) -> Unit) { - onFrameReceived = block - } - - } - - override val depthCamera: V1DepthCamera = V1DepthCamera( - resolution = KINECT1_DEPTH_RESOLUTION - ) - - fun executeInFreenectDeviceContext( - name: String, - block: (ctx: freenect_context, usbCtx: freenect_usb_context, dev: freenect_device) -> Unit - ) { - freenect.call("$info: $name") { ctx, usbCtx -> - block(ctx, usbCtx, dev) - } - } - - @Suppress("unused") - fun executeInFreenectDeviceContextBlocking( - name: String, - block: (ctx: freenect_context, usbCtx: freenect_usb_context, dev: freenect_device) -> T - ): T = freenect.callBlocking("$info: $name") { ctx, usbCtx -> - block(ctx, usbCtx, dev) - } - - override fun close() { - logger.info { "$info.close()" } - freenect.callBlocking("$info.closeDevice") { _, _ -> - depthCamera.stop() - freenect.checkReturn(freenect_close_device(dev)) - mutableActiveDevices.remove(this) - } - depthCamera.close() - } - - } - -} - -/** - * This class provides a low level API for accessing a kinect1 device. - * All the operations are executed in a single thread responsible for calling - * freenect API. - * - * @param initialLogLevel the log level to use when freenect is initialized. - */ -class Freenect(private val initialLogLevel: Kinect1.LogLevel) { - - private val logger = KotlinLogging.logger {} - - var logLevel: Kinect1.LogLevel = initialLogLevel - set(value) { - call("logLevel[$value]") { ctx, _ -> - freenect_set_log_level(ctx, value.code) - } - field = value - } - - internal var expectingEvents: Boolean = false - - private val ctx = freenect_context() - - private val usbCtx = freenect_usb_context() - - private var running: Boolean = true - - private val runner = thread(name = "kinect1", start = true) { - logger.info { "Starting Kinect1 thread" } - checkReturn(freenect_init(ctx, usbCtx)) - freenect_set_log_level(ctx, logLevel.code) - val num = checkReturn(freenect_num_devices(ctx)) - if (num == 0) { - logger.warn { "Could not find any Kinect1 devices, calling openDevice() will throw exception" } - } else { - val devices = listDevices() - logger.info { "Kinect1 detected, device count: ${devices.size}" } - devices.forEachIndexed { index, info -> - logger.info { " |-[$index]: ${info.serialNumber}" } - } - } - - while (running) { - if (expectingEvents) { - val ret = freenect_process_events(ctx) - if (ret != 0) { - logger.error { "freenect_process_events returned non-zero value: $ret" } - } - val tasks = freenectCallQueue.iterator() - for (task in tasks) { - tasks.remove() - task.run() - } - } else { - freenectCallQueue.pollFirst()?.run() - } - } - - checkReturn(freenect_shutdown(ctx)) - } - - private val freenectCallQueue = LinkedBlockingDeque>() - - fun call( - name: String, - block: ( - ctx: freenect_context, - usbCtx: freenect_usb_context - ) -> Unit - ) { - logger.debug { "'$name' requested (non-blocking)" } - val task = FutureTask { - logger.trace { "'$name': started" } - try { - block(ctx, usbCtx) - logger.trace { "'$name': ended" } - } catch (e: Exception) { - logger.error(e) { "'$name': failed" } - } - } - freenectCallQueue.add(task) - } - - fun callBlocking( - name: String, - block: ( - ctx: freenect_context, - usbCtx: freenect_usb_context - ) -> T - ): T { - logger.debug { "'$name' requested (blocking)" } - val task = FutureTask { - logger.trace { "'$name': started" } - try { - val result = block(ctx, usbCtx) - logger.trace { "'$name': ended" } - Result.success(result) - } catch (e: Exception) { - logger.error(e) { "'$name': failed" } - Result.failure(e) - } - } - freenectCallQueue.add(task) - val result = task.get() - logger.trace { "'$name': returned result" } - return result.getOrThrow() - } - - fun listDevices(): List { - val attributes = freenect_device_attributes() - freenect_list_device_attributes(ctx, attributes) - try { - val devices = buildList { - var item: freenect_device_attributes? = - if (attributes.isNull) null - else attributes - while (item != null) { - val serialNumber = item.camera_serial().string - add(Kinect1.DeviceInfo(serialNumber)) - item = item.next() - } - } - return devices - } finally { - if (!attributes.isNull) { - freenect_free_device_attributes(attributes) - } - } - } - - fun close() { - logger.debug { "Closing Kinect1 runner" } - running = false - logger.debug { "Waiting for runner thread to finish" } - runner.join() - } - - fun checkReturn(ret: Int): Int = - if (ret >= 0) ret - else { - throw Kinect1Exception("Freenect error: ret=$ret") - } - -} - -internal const val KINECT1_MAX_DEPTH_VALUE: Double = 2047.0 - -internal val KINECT1_DEPTH_RESOLUTION: IntVector2 = IntVector2(640, 480) - -internal class Kinect1DepthMappers { - - private val depthToRawNormalized = depthToRawNormalizedMappers().apply { - forEach { - it.parameters["maxDepthValue"] = KINECT1_MAX_DEPTH_VALUE - } - } - - private val depthToMeters = KinectDepthMappers( - "kinect1-depth-to-meters.frag", - Kinect1::class - ) - - var depthMeasurement: DepthMeasurement = DepthMeasurement.RAW_NORMALIZED - set(value) { - field = value - selectMapper() - } - - var flipH: Boolean = false - set(value) { - field = value - selectMapper() - } - - var flipV: Boolean = false - set(value) { - field = value - selectMapper() - } - - var mapperState: Filter? = depthToRawNormalized.select( - flipH = false, - flipV = false - ) - val mapper: Filter? get() = mapperState - - fun update(resolution: IntVector2) { - depthToRawNormalized.update(resolution) - depthToMeters.update(resolution) - } - - private fun selectMapper() { - mapperState = when (depthMeasurement) { - DepthMeasurement.RAW -> null - DepthMeasurement.RAW_NORMALIZED -> { - depthToRawNormalized.select(flipH, flipV) - } - - DepthMeasurement.METERS -> { - depthToMeters.select(flipH, flipV) - } - } - } - -} diff --git a/orx-jvm/orx-kinect-v1/src/main/resources/org/openrndr/extra/kinect/v1/kinect1-depth-to-meters.frag b/orx-jvm/orx-kinect-v1/src/main/resources/org/openrndr/extra/kinect/v1/kinect1-depth-to-meters.frag deleted file mode 100644 index 69e27b58..00000000 --- a/orx-jvm/orx-kinect-v1/src/main/resources/org/openrndr/extra/kinect/v1/kinect1-depth-to-meters.frag +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef KINECT_FLIPV -layout(origin_upper_left) in vec4 gl_FragCoord; -#endif - -uniform usampler2D tex0; // kinect raw -#ifdef KINECT_FLIPH -uniform int resolutionXMinus1; -#endif -out float outDepth; // measured in meters - -const uint UINT_MAX_KINECT_DEPTH = 2047u; - -void main() { - ivec2 uv = ivec2(gl_FragCoord); - #ifdef KINECT_FLIPH - uv = ivec2(resolutionXMinus1 - uv.x, uv.y); - #endif - uint uintDepth = texelFetch(tex0, uv, 0).r; - float depth = float(uintDepth); - outDepth = (uintDepth < UINT_MAX_KINECT_DEPTH) - ? 1.0 / (depth * -0.0030711016 + 3.3309495161) - : 0.0; -} diff --git a/orx-jvm/orx-kotlin-parser/build.gradle.kts b/orx-jvm/orx-kotlin-parser/build.gradle.kts deleted file mode 100644 index 3238ea9f..00000000 --- a/orx-jvm/orx-kotlin-parser/build.gradle.kts +++ /dev/null @@ -1,21 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") - antlr -} - -tasks.generateGrammarSource { - maxHeapSize = "64m" - arguments.addAll(listOf("-visitor", "-long-messages")) -} - -dependencies { - antlr(libs.antlr.core) - implementation(libs.antlr.runtime) -} - -tasks.getByName("compileDemoKotlin").dependsOn("generateDemoGrammarSource") -tasks.getByName("compileTestKotlin").dependsOn("generateTestGrammarSource") -tasks.getByName("compileKotlin").dependsOn("generateGrammarSource") -tasks.getByName("sourcesJar").dependsOn("generateGrammarSource") -tasks.named("dokkaGeneratePublicationHtml") { dependsOn("generateGrammarSource") } -tasks.named("dokkaGenerateModuleHtml") { dependsOn("generateGrammarSource") } diff --git a/orx-jvm/orx-kotlin-parser/src/main/antlr/KotlinLexer.g4 b/orx-jvm/orx-kotlin-parser/src/main/antlr/KotlinLexer.g4 deleted file mode 100755 index d36cdbba..00000000 --- a/orx-jvm/orx-kotlin-parser/src/main/antlr/KotlinLexer.g4 +++ /dev/null @@ -1,527 +0,0 @@ -/** - * Kotlin lexical grammar in ANTLR4 notation - */ - -lexer grammar KotlinLexer; - -import UnicodeClasses; - -// SECTION: lexicalGeneral - -ShebangLine - : '#!' ~[\r\n]* - ; - -DelimitedComment - : '/*' ( DelimitedComment | . )*? '*/' - -> channel(HIDDEN) - ; - -LineComment - : '//' ~[\r\n]* - -> channel(HIDDEN) - ; - -WS - : [\u0020\u0009\u000C] - -> channel(HIDDEN) - ; - -NL: '\n' | '\r' '\n'?; - -fragment Hidden: DelimitedComment | LineComment | WS; - -// SECTION: separatorsAndOperations - -RESERVED: '...'; -DOT: '.'; -COMMA: ','; -LPAREN: '(' -> pushMode(Inside); -RPAREN: ')'; -LSQUARE: '[' -> pushMode(Inside); -RSQUARE: ']'; -LCURL: '{' -> pushMode(DEFAULT_MODE); -/* - * When using another programming language (not Java) to generate a parser, - * please replace this code with the corresponding code of a programming language you are using. - */ -RCURL: '}' { if (!_modeStack.isEmpty()) { popMode(); } }; -MULT: '*'; -MOD: '%'; -DIV: '/'; -ADD: '+'; -SUB: '-'; -INCR: '++'; -DECR: '--'; -CONJ: '&&'; -DISJ: '||'; -EXCL_WS: '!' Hidden; -EXCL_NO_WS: '!'; -COLON: ':'; -SEMICOLON: ';'; -ASSIGNMENT: '='; -ADD_ASSIGNMENT: '+='; -SUB_ASSIGNMENT: '-='; -MULT_ASSIGNMENT: '*='; -DIV_ASSIGNMENT: '/='; -MOD_ASSIGNMENT: '%='; -ARROW: '->'; -DOUBLE_ARROW: '=>'; -RANGE: '..'; -COLONCOLON: '::'; -DOUBLE_SEMICOLON: ';;'; -HASH: '#'; -AT_NO_WS: '@'; -AT_POST_WS: '@' (Hidden | NL); -AT_PRE_WS: (Hidden | NL) '@' ; -AT_BOTH_WS: (Hidden | NL) '@' (Hidden | NL); -QUEST_WS: '?' Hidden; -QUEST_NO_WS: '?'; -LANGLE: '<'; -RANGLE: '>'; -LE: '<='; -GE: '>='; -EXCL_EQ: '!='; -EXCL_EQEQ: '!=='; -AS_SAFE: 'as?'; -EQEQ: '=='; -EQEQEQ: '==='; -SINGLE_QUOTE: '\''; -AMP: '&'; - -// SECTION: keywords - -RETURN_AT: 'return@' Identifier; -CONTINUE_AT: 'continue@' Identifier; -BREAK_AT: 'break@' Identifier; - -THIS_AT: 'this@' Identifier; -SUPER_AT: 'super@' Identifier; - -FILE: 'file'; -FIELD: 'field'; -PROPERTY: 'property'; -GET: 'get'; -SET: 'set'; -RECEIVER: 'receiver'; -PARAM: 'param'; -SETPARAM: 'setparam'; -DELEGATE: 'delegate'; - -PACKAGE: 'package'; -IMPORT: 'import'; -CLASS: 'class'; -INTERFACE: 'interface'; -FUN: 'fun'; -OBJECT: 'object'; -VAL: 'val'; -VAR: 'var'; -TYPE_ALIAS: 'typealias'; -CONSTRUCTOR: 'constructor'; -BY: 'by'; -COMPANION: 'companion'; -INIT: 'init'; -THIS: 'this'; -SUPER: 'super'; -TYPEOF: 'typeof'; -WHERE: 'where'; -IF: 'if'; -ELSE: 'else'; -WHEN: 'when'; -TRY: 'try'; -CATCH: 'catch'; -FINALLY: 'finally'; -FOR: 'for'; -DO: 'do'; -WHILE: 'while'; -THROW: 'throw'; -RETURN: 'return'; -CONTINUE: 'continue'; -BREAK: 'break'; -AS: 'as'; -IS: 'is'; -IN: 'in'; -NOT_IS: '!is' (Hidden | NL); -NOT_IN: '!in' (Hidden | NL); -OUT: 'out'; -DYNAMIC: 'dynamic'; - -// SECTION: lexicalModifiers - -PUBLIC: 'public'; -PRIVATE: 'private'; -PROTECTED: 'protected'; -INTERNAL: 'internal'; -ENUM: 'enum'; -SEALED: 'sealed'; -ANNOTATION: 'annotation'; -DATA: 'data'; -INNER: 'inner'; -VALUE: 'value'; -TAILREC: 'tailrec'; -OPERATOR: 'operator'; -INLINE: 'inline'; -INFIX: 'infix'; -EXTERNAL: 'external'; -SUSPEND: 'suspend'; -OVERRIDE: 'override'; -ABSTRACT: 'abstract'; -FINAL: 'final'; -OPEN: 'open'; -CONST: 'const'; -LATEINIT: 'lateinit'; -VARARG: 'vararg'; -NOINLINE: 'noinline'; -CROSSINLINE: 'crossinline'; -REIFIED: 'reified'; -EXPECT: 'expect'; -ACTUAL: 'actual'; - -// SECTION: literals - -fragment DecDigit: '0'..'9'; -fragment DecDigitNoZero: '1'..'9'; -fragment DecDigitOrSeparator: DecDigit | '_'; - -fragment DecDigits - : DecDigit DecDigitOrSeparator* DecDigit - | DecDigit - ; - -fragment DoubleExponent: [eE] [+-]? DecDigits; - -RealLiteral - : FloatLiteral - | DoubleLiteral - ; - -FloatLiteral - : DoubleLiteral [fF] - | DecDigits [fF] - ; - -DoubleLiteral - : DecDigits? '.' DecDigits DoubleExponent? - | DecDigits DoubleExponent - ; - -IntegerLiteral - : DecDigitNoZero DecDigitOrSeparator* DecDigit - | DecDigit - ; - -fragment HexDigit: [0-9a-fA-F]; -fragment HexDigitOrSeparator: HexDigit | '_'; - -HexLiteral - : '0' [xX] HexDigit HexDigitOrSeparator* HexDigit - | '0' [xX] HexDigit - ; - -fragment BinDigit: [01]; -fragment BinDigitOrSeparator: BinDigit | '_'; - -BinLiteral - : '0' [bB] BinDigit BinDigitOrSeparator* BinDigit - | '0' [bB] BinDigit - ; - -UnsignedLiteral - : (IntegerLiteral | HexLiteral | BinLiteral) [uU] [lL]? - ; - -LongLiteral - : (IntegerLiteral | HexLiteral | BinLiteral) [lL] - ; - -BooleanLiteral: 'true'| 'false'; - -NullLiteral: 'null'; - -CharacterLiteral - : '\'' (EscapeSeq | ~[\n\r'\\]) '\'' - ; - -// SECTION: lexicalIdentifiers - -fragment UnicodeDigit: UNICODE_CLASS_ND; - -Identifier - : (Letter | '_') (Letter | '_' | UnicodeDigit)* - | '`' ~([\r\n] | '`')+ '`' - ; - -IdentifierOrSoftKey - : Identifier - /* Soft keywords */ - | ABSTRACT - | ANNOTATION - | BY - | CATCH - | COMPANION - | CONSTRUCTOR - | CROSSINLINE - | DATA - | DYNAMIC - | ENUM - | EXTERNAL - | FINAL - | FINALLY - | IMPORT - | INFIX - | INIT - | INLINE - | INNER - | INTERNAL - | LATEINIT - | NOINLINE - | OPEN - | OPERATOR - | OUT - | OVERRIDE - | PRIVATE - | PROTECTED - | PUBLIC - | REIFIED - | SEALED - | TAILREC - | VARARG - | WHERE - | GET - | SET - | FIELD - | PROPERTY - | RECEIVER - | PARAM - | SETPARAM - | DELEGATE - | FILE - | EXPECT - | ACTUAL - | VALUE - /* Strong keywords */ - | CONST - | SUSPEND - ; - -FieldIdentifier - : '$' IdentifierOrSoftKey - ; - -fragment UniCharacterLiteral - : '\\' 'u' HexDigit HexDigit HexDigit HexDigit - ; - -fragment EscapedIdentifier - : '\\' ('t' | 'b' | 'r' | 'n' | '\'' | '"' | '\\' | '$') - ; - -fragment EscapeSeq - : UniCharacterLiteral - | EscapedIdentifier - ; - -// SECTION: characters - -fragment Letter - : UNICODE_CLASS_LU - | UNICODE_CLASS_LL - | UNICODE_CLASS_LT - | UNICODE_CLASS_LM - | UNICODE_CLASS_LO - ; - -// SECTION: strings - -QUOTE_OPEN: '"' -> pushMode(LineString); - -TRIPLE_QUOTE_OPEN: '"""' -> pushMode(MultiLineString); - -mode LineString; - -QUOTE_CLOSE - : '"' -> popMode - ; - -LineStrRef - : FieldIdentifier - ; - -LineStrText - : ~('\\' | '"' | '$')+ | '$' - ; - -LineStrEscapedChar - : EscapedIdentifier - | UniCharacterLiteral - ; - -LineStrExprStart - : '${' -> pushMode(DEFAULT_MODE) - ; - -mode MultiLineString; - -TRIPLE_QUOTE_CLOSE - : MultiLineStringQuote? '"""' -> popMode - ; - -MultiLineStringQuote - : '"'+ - ; - -MultiLineStrRef - : FieldIdentifier - ; - -MultiLineStrText - : ~('"' | '$')+ | '$' - ; - -MultiLineStrExprStart - : '${' -> pushMode(DEFAULT_MODE) - ; - -// SECTION: inside - -mode Inside; - -Inside_RPAREN: RPAREN -> popMode, type(RPAREN); -Inside_RSQUARE: RSQUARE -> popMode, type(RSQUARE); -Inside_LPAREN: LPAREN -> pushMode(Inside), type(LPAREN); -Inside_LSQUARE: LSQUARE -> pushMode(Inside), type(LSQUARE); -Inside_LCURL: LCURL -> pushMode(DEFAULT_MODE), type(LCURL); -Inside_RCURL: RCURL -> popMode, type(RCURL); - -Inside_DOT: DOT -> type(DOT); -Inside_COMMA: COMMA -> type(COMMA); -Inside_MULT: MULT -> type(MULT); -Inside_MOD: MOD -> type(MOD); -Inside_DIV: DIV -> type(DIV); -Inside_ADD: ADD -> type(ADD); -Inside_SUB: SUB -> type(SUB); -Inside_INCR: INCR -> type(INCR); -Inside_DECR: DECR -> type(DECR); -Inside_CONJ: CONJ -> type(CONJ); -Inside_DISJ: DISJ -> type(DISJ); -Inside_EXCL_WS: '!' (Hidden|NL) -> type(EXCL_WS); -Inside_EXCL_NO_WS: EXCL_NO_WS -> type(EXCL_NO_WS); -Inside_COLON: COLON -> type(COLON); -Inside_SEMICOLON: SEMICOLON -> type(SEMICOLON); -Inside_ASSIGNMENT: ASSIGNMENT -> type(ASSIGNMENT); -Inside_ADD_ASSIGNMENT: ADD_ASSIGNMENT -> type(ADD_ASSIGNMENT); -Inside_SUB_ASSIGNMENT: SUB_ASSIGNMENT -> type(SUB_ASSIGNMENT); -Inside_MULT_ASSIGNMENT: MULT_ASSIGNMENT -> type(MULT_ASSIGNMENT); -Inside_DIV_ASSIGNMENT: DIV_ASSIGNMENT -> type(DIV_ASSIGNMENT); -Inside_MOD_ASSIGNMENT: MOD_ASSIGNMENT -> type(MOD_ASSIGNMENT); -Inside_ARROW: ARROW -> type(ARROW); -Inside_DOUBLE_ARROW: DOUBLE_ARROW -> type(DOUBLE_ARROW); -Inside_RANGE: RANGE -> type(RANGE); -Inside_RESERVED: RESERVED -> type(RESERVED); -Inside_COLONCOLON: COLONCOLON -> type(COLONCOLON); -Inside_DOUBLE_SEMICOLON: DOUBLE_SEMICOLON -> type(DOUBLE_SEMICOLON); -Inside_HASH: HASH -> type(HASH); -Inside_AT_NO_WS: AT_NO_WS -> type(AT_NO_WS); -Inside_AT_POST_WS: AT_POST_WS -> type(AT_POST_WS); -Inside_AT_PRE_WS: AT_PRE_WS -> type(AT_PRE_WS); -Inside_AT_BOTH_WS: AT_BOTH_WS -> type(AT_BOTH_WS); -Inside_QUEST_WS: '?' (Hidden | NL) -> type(QUEST_WS); -Inside_QUEST_NO_WS: QUEST_NO_WS -> type(QUEST_NO_WS); -Inside_LANGLE: LANGLE -> type(LANGLE); -Inside_RANGLE: RANGLE -> type(RANGLE); -Inside_LE: LE -> type(LE); -Inside_GE: GE -> type(GE); -Inside_EXCL_EQ: EXCL_EQ -> type(EXCL_EQ); -Inside_EXCL_EQEQ: EXCL_EQEQ -> type(EXCL_EQEQ); -Inside_IS: IS -> type(IS); -Inside_NOT_IS: NOT_IS -> type(NOT_IS); -Inside_NOT_IN: NOT_IN -> type(NOT_IN); -Inside_AS: AS -> type(AS); -Inside_AS_SAFE: AS_SAFE -> type(AS_SAFE); -Inside_EQEQ: EQEQ -> type(EQEQ); -Inside_EQEQEQ: EQEQEQ -> type(EQEQEQ); -Inside_SINGLE_QUOTE: SINGLE_QUOTE -> type(SINGLE_QUOTE); -Inside_AMP: AMP -> type(AMP); -Inside_QUOTE_OPEN: QUOTE_OPEN -> pushMode(LineString), type(QUOTE_OPEN); -Inside_TRIPLE_QUOTE_OPEN: TRIPLE_QUOTE_OPEN -> pushMode(MultiLineString), type(TRIPLE_QUOTE_OPEN); - -Inside_VAL: VAL -> type(VAL); -Inside_VAR: VAR -> type(VAR); -Inside_FUN: FUN -> type(FUN); -Inside_OBJECT: OBJECT -> type(OBJECT); -Inside_SUPER: SUPER -> type(SUPER); -Inside_IN: IN -> type(IN); -Inside_OUT: OUT -> type(OUT); -Inside_FIELD: FIELD -> type(FIELD); -Inside_FILE: FILE -> type(FILE); -Inside_PROPERTY: PROPERTY -> type(PROPERTY); -Inside_GET: GET -> type(GET); -Inside_SET: SET -> type(SET); -Inside_RECEIVER: RECEIVER -> type(RECEIVER); -Inside_PARAM: PARAM -> type(PARAM); -Inside_SETPARAM: SETPARAM -> type(SETPARAM); -Inside_DELEGATE: DELEGATE -> type(DELEGATE); -Inside_THROW: THROW -> type(THROW); -Inside_RETURN: RETURN -> type(RETURN); -Inside_CONTINUE: CONTINUE -> type(CONTINUE); -Inside_BREAK: BREAK -> type(BREAK); -Inside_RETURN_AT: RETURN_AT -> type(RETURN_AT); -Inside_CONTINUE_AT: CONTINUE_AT -> type(CONTINUE_AT); -Inside_BREAK_AT: BREAK_AT -> type(BREAK_AT); -Inside_IF: IF -> type(IF); -Inside_ELSE: ELSE -> type(ELSE); -Inside_WHEN: WHEN -> type(WHEN); -Inside_TRY: TRY -> type(TRY); -Inside_CATCH: CATCH -> type(CATCH); -Inside_FINALLY: FINALLY -> type(FINALLY); -Inside_FOR: FOR -> type(FOR); -Inside_DO: DO -> type(DO); -Inside_WHILE: WHILE -> type(WHILE); - -Inside_PUBLIC: PUBLIC -> type(PUBLIC); -Inside_PRIVATE: PRIVATE -> type(PRIVATE); -Inside_PROTECTED: PROTECTED -> type(PROTECTED); -Inside_INTERNAL: INTERNAL -> type(INTERNAL); -Inside_ENUM: ENUM -> type(ENUM); -Inside_SEALED: SEALED -> type(SEALED); -Inside_ANNOTATION: ANNOTATION -> type(ANNOTATION); -Inside_DATA: DATA -> type(DATA); -Inside_INNER: INNER -> type(INNER); -Inside_VALUE: VALUE -> type(VALUE); -Inside_TAILREC: TAILREC -> type(TAILREC); -Inside_OPERATOR: OPERATOR -> type(OPERATOR); -Inside_INLINE: INLINE -> type(INLINE); -Inside_INFIX: INFIX -> type(INFIX); -Inside_EXTERNAL: EXTERNAL -> type(EXTERNAL); -Inside_SUSPEND: SUSPEND -> type(SUSPEND); -Inside_OVERRIDE: OVERRIDE -> type(OVERRIDE); -Inside_ABSTRACT: ABSTRACT -> type(ABSTRACT); -Inside_FINAL: FINAL -> type(FINAL); -Inside_OPEN: OPEN -> type(OPEN); -Inside_CONST: CONST -> type(CONST); -Inside_LATEINIT: LATEINIT -> type(LATEINIT); -Inside_VARARG: VARARG -> type(VARARG); -Inside_NOINLINE: NOINLINE -> type(NOINLINE); -Inside_CROSSINLINE: CROSSINLINE -> type(CROSSINLINE); -Inside_REIFIED: REIFIED -> type(REIFIED); -Inside_EXPECT: EXPECT -> type(EXPECT); -Inside_ACTUAL: ACTUAL -> type(ACTUAL); - -Inside_BooleanLiteral: BooleanLiteral -> type(BooleanLiteral); -Inside_IntegerLiteral: IntegerLiteral -> type(IntegerLiteral); -Inside_HexLiteral: HexLiteral -> type(HexLiteral); -Inside_BinLiteral: BinLiteral -> type(BinLiteral); -Inside_CharacterLiteral: CharacterLiteral -> type(CharacterLiteral); -Inside_RealLiteral: RealLiteral -> type(RealLiteral); -Inside_NullLiteral: NullLiteral -> type(NullLiteral); -Inside_LongLiteral: LongLiteral -> type(LongLiteral); -Inside_UnsignedLiteral: UnsignedLiteral -> type(UnsignedLiteral); - -Inside_Identifier: Identifier -> type(Identifier); -Inside_Comment: (LineComment | DelimitedComment) -> channel(HIDDEN); -Inside_WS: WS -> channel(HIDDEN); -Inside_NL: NL -> channel(HIDDEN); - -mode DEFAULT_MODE; - -ErrorCharacter: .; diff --git a/orx-jvm/orx-kotlin-parser/src/main/antlr/KotlinParser.g4 b/orx-jvm/orx-kotlin-parser/src/main/antlr/KotlinParser.g4 deleted file mode 100644 index 53356a1f..00000000 --- a/orx-jvm/orx-kotlin-parser/src/main/antlr/KotlinParser.g4 +++ /dev/null @@ -1,926 +0,0 @@ -/** - * Kotlin syntax grammar in ANTLR4 notation - */ - -parser grammar KotlinParser; - -options { tokenVocab = KotlinLexer; } - -// SECTION: general - -kotlinFile - : shebangLine? NL* fileAnnotation* packageHeader importList topLevelObject* EOF - ; - -script - : shebangLine? NL* fileAnnotation* packageHeader importList (statement semi)* EOF - ; - -shebangLine - : ShebangLine NL+ - ; - -fileAnnotation - : (AT_NO_WS | AT_PRE_WS) FILE NL* COLON NL* (LSQUARE unescapedAnnotation+ RSQUARE | unescapedAnnotation) NL* - ; - -packageHeader - : (PACKAGE identifier semi?)? - ; - -importList - : importHeader* - ; - -importHeader - : IMPORT identifier (DOT MULT | importAlias)? semi? - ; - -importAlias - : AS simpleIdentifier - ; - -topLevelObject - : declaration semis? - ; - -typeAlias - : modifiers? TYPE_ALIAS NL* simpleIdentifier (NL* typeParameters)? NL* ASSIGNMENT NL* type - ; - -declaration - : classDeclaration - | objectDeclaration - | functionDeclaration - | propertyDeclaration - | typeAlias - ; - -// SECTION: classes - -classDeclaration - : modifiers? (CLASS | (FUN NL*)? INTERFACE) NL* simpleIdentifier - (NL* typeParameters)? (NL* primaryConstructor)? - (NL* COLON NL* delegationSpecifiers)? - (NL* typeConstraints)? - (NL* classBody | NL* enumClassBody)? - ; - -primaryConstructor - : (modifiers? CONSTRUCTOR NL*)? classParameters - ; - -classBody - : LCURL NL* classMemberDeclarations NL* RCURL - ; - -classParameters - : LPAREN NL* (classParameter (NL* COMMA NL* classParameter)* (NL* COMMA)?)? NL* RPAREN - ; - -classParameter - : modifiers? (VAL | VAR)? NL* simpleIdentifier COLON NL* type (NL* ASSIGNMENT NL* expression)? - ; - -delegationSpecifiers - : annotatedDelegationSpecifier (NL* COMMA NL* annotatedDelegationSpecifier)* - ; - -delegationSpecifier - : constructorInvocation - | explicitDelegation - | userType - | functionType - | SUSPEND NL* functionType - ; - -constructorInvocation - : userType valueArguments - ; - -annotatedDelegationSpecifier - : annotation* NL* delegationSpecifier - ; - -explicitDelegation - : (userType | functionType) NL* BY NL* expression - ; - -typeParameters - : LANGLE NL* typeParameter (NL* COMMA NL* typeParameter)* (NL* COMMA)? NL* RANGLE - ; - -typeParameter - : typeParameterModifiers? NL* simpleIdentifier (NL* COLON NL* type)? - ; - -typeConstraints - : WHERE NL* typeConstraint (NL* COMMA NL* typeConstraint)* - ; - -typeConstraint - : annotation* simpleIdentifier NL* COLON NL* type - ; - -// SECTION: classMembers - -classMemberDeclarations - : (classMemberDeclaration semis?)* - ; - -classMemberDeclaration - : declaration - | companionObject - | anonymousInitializer - | secondaryConstructor - ; - -anonymousInitializer - : INIT NL* block - ; - -companionObject - : modifiers? COMPANION NL* OBJECT - (NL* simpleIdentifier)? - (NL* COLON NL* delegationSpecifiers)? - (NL* classBody)? - ; - -functionValueParameters - : LPAREN NL* (functionValueParameter (NL* COMMA NL* functionValueParameter)* (NL* COMMA)?)? NL* RPAREN - ; - -functionValueParameter - : parameterModifiers? parameter (NL* ASSIGNMENT NL* expression)? - ; - -functionDeclaration - : modifiers? - FUN (NL* typeParameters)? (NL* receiverType NL* DOT)? NL* simpleIdentifier - NL* functionValueParameters - (NL* COLON NL* type)? - (NL* typeConstraints)? - (NL* functionBody)? - ; - -functionBody - : block - | ASSIGNMENT NL* expression - ; - -variableDeclaration - : annotation* NL* simpleIdentifier (NL* COLON NL* type)? - ; - -multiVariableDeclaration - : LPAREN NL* variableDeclaration (NL* COMMA NL* variableDeclaration)* (NL* COMMA)? NL* RPAREN - ; - -propertyDeclaration - : modifiers? (VAL | VAR) - (NL* typeParameters)? - (NL* receiverType NL* DOT)? - (NL* (multiVariableDeclaration | variableDeclaration)) - (NL* typeConstraints)? - (NL* (ASSIGNMENT NL* expression | propertyDelegate))? - (NL+ SEMICOLON)? NL* (getter? (NL* semi? setter)? | setter? (NL* semi? getter)?) - ; - -propertyDelegate - : BY NL* expression - ; - -getter - : modifiers? GET - (NL* LPAREN NL* RPAREN (NL* COLON NL* type)? NL* functionBody)? - ; - -setter - : modifiers? SET - (NL* LPAREN NL* functionValueParameterWithOptionalType (NL* COMMA)? NL* RPAREN (NL* COLON NL* type)? NL* functionBody)? - ; - -parametersWithOptionalType - : LPAREN NL* (functionValueParameterWithOptionalType (NL* COMMA NL* functionValueParameterWithOptionalType)* (NL* COMMA)?)? NL* RPAREN - ; - -functionValueParameterWithOptionalType - : parameterModifiers? parameterWithOptionalType (NL* ASSIGNMENT NL* expression)? - ; - -parameterWithOptionalType - : simpleIdentifier NL* (COLON NL* type)? - ; - -parameter - : simpleIdentifier NL* COLON NL* type - ; - -objectDeclaration - : modifiers? OBJECT - NL* simpleIdentifier - (NL* COLON NL* delegationSpecifiers)? - (NL* classBody)? - ; - -secondaryConstructor - : modifiers? CONSTRUCTOR NL* functionValueParameters (NL* COLON NL* constructorDelegationCall)? NL* block? - ; - -constructorDelegationCall - : (THIS | SUPER) NL* valueArguments - ; - -// SECTION: enumClasses - -enumClassBody - : LCURL NL* enumEntries? (NL* SEMICOLON NL* classMemberDeclarations)? NL* RCURL - ; - -enumEntries - : enumEntry (NL* COMMA NL* enumEntry)* NL* COMMA? - ; - -enumEntry - : (modifiers NL*)? simpleIdentifier (NL* valueArguments)? (NL* classBody)? - ; - -// SECTION: types - -type - : typeModifiers? (functionType | parenthesizedType | nullableType | typeReference | definitelyNonNullableType) - ; - -typeReference - : userType - | DYNAMIC - ; - -nullableType - : (typeReference | parenthesizedType) NL* quest+ - ; - -quest - : QUEST_NO_WS - | QUEST_WS - ; - -userType - : simpleUserType (NL* DOT NL* simpleUserType)* - ; - -simpleUserType - : simpleIdentifier (NL* typeArguments)? - ; - -typeProjection - : typeProjectionModifiers? type - | MULT - ; - -typeProjectionModifiers - : typeProjectionModifier+ - ; - -typeProjectionModifier - : varianceModifier NL* - | annotation - ; - -functionType - : (receiverType NL* DOT NL*)? functionTypeParameters NL* ARROW NL* type - ; - -functionTypeParameters - : LPAREN NL* (parameter | type)? (NL* COMMA NL* (parameter | type))* (NL* COMMA)? NL* RPAREN - ; - -parenthesizedType - : LPAREN NL* type NL* RPAREN - ; - -receiverType - : typeModifiers? (parenthesizedType | nullableType | typeReference) - ; - -parenthesizedUserType - : LPAREN NL* (userType | parenthesizedUserType) NL* RPAREN - ; - -definitelyNonNullableType - : typeModifiers? (userType | parenthesizedUserType) NL* AMP NL* typeModifiers? (userType | parenthesizedUserType) - ; - -// SECTION: statements - -statements - : (statement (semis statement)*)? semis? - ; - -statement - : (label | annotation)* ( declaration | assignment | loopStatement | expression) - ; - -label - : simpleIdentifier (AT_NO_WS | AT_POST_WS) NL* - ; - -controlStructureBody - : block - | statement - ; - -block - : LCURL NL* statements NL* RCURL - ; - -loopStatement - : forStatement - | whileStatement - | doWhileStatement - ; - -forStatement - : FOR NL* LPAREN annotation* (variableDeclaration | multiVariableDeclaration) - IN expression RPAREN NL* controlStructureBody? - ; - -whileStatement - : WHILE NL* LPAREN expression RPAREN NL* (controlStructureBody | SEMICOLON) - ; - -doWhileStatement - : DO NL* controlStructureBody? NL* WHILE NL* LPAREN expression RPAREN - ; - -assignment - : (directlyAssignableExpression ASSIGNMENT | assignableExpression assignmentAndOperator) NL* expression - ; - -semi - : (SEMICOLON | NL) NL* - ; - -semis - : (SEMICOLON | NL)+ - ; - -// SECTION: expressions - -expression - : disjunction - ; - -disjunction - : conjunction (NL* DISJ NL* conjunction)* - ; - -conjunction - : equality (NL* CONJ NL* equality)* - ; - -equality - : comparison (equalityOperator NL* comparison)* - ; - -comparison - : genericCallLikeComparison (comparisonOperator NL* genericCallLikeComparison)* - ; - -genericCallLikeComparison - : infixOperation callSuffix* - ; - -infixOperation - : elvisExpression (inOperator NL* elvisExpression | isOperator NL* type)* - ; - -elvisExpression - : infixFunctionCall (NL* elvis NL* infixFunctionCall)* - ; - -elvis - : QUEST_NO_WS COLON - ; - -infixFunctionCall - : rangeExpression (simpleIdentifier NL* rangeExpression)* - ; - -rangeExpression - : additiveExpression (RANGE NL* additiveExpression)* - ; - -additiveExpression - : multiplicativeExpression (additiveOperator NL* multiplicativeExpression)* - ; - -multiplicativeExpression - : asExpression (multiplicativeOperator NL* asExpression)* - ; - -asExpression - : prefixUnaryExpression (NL* asOperator NL* type)* - ; - -prefixUnaryExpression - : unaryPrefix* postfixUnaryExpression - ; - -unaryPrefix - : annotation - | label - | prefixUnaryOperator NL* - ; - -postfixUnaryExpression - : primaryExpression postfixUnarySuffix* - ; - -postfixUnarySuffix - : postfixUnaryOperator - | typeArguments - | callSuffix - | indexingSuffix - | navigationSuffix - ; - -directlyAssignableExpression - : postfixUnaryExpression assignableSuffix - | simpleIdentifier - | parenthesizedDirectlyAssignableExpression - ; - -parenthesizedDirectlyAssignableExpression - : LPAREN NL* directlyAssignableExpression NL* RPAREN - ; - -assignableExpression - : prefixUnaryExpression - | parenthesizedAssignableExpression - ; - -parenthesizedAssignableExpression - : LPAREN NL* assignableExpression NL* RPAREN - ; - -assignableSuffix - : typeArguments - | indexingSuffix - | navigationSuffix - ; - -indexingSuffix - : LSQUARE NL* expression (NL* COMMA NL* expression)* (NL* COMMA)? NL* RSQUARE - ; - -navigationSuffix - : memberAccessOperator NL* (simpleIdentifier | parenthesizedExpression | CLASS) - ; - -callSuffix - : typeArguments? (valueArguments? annotatedLambda | valueArguments) - ; - -annotatedLambda - : annotation* label? NL* lambdaLiteral - ; - -typeArguments - : LANGLE NL* typeProjection (NL* COMMA NL* typeProjection)* (NL* COMMA)? NL* RANGLE - ; - -valueArguments - : LPAREN NL* (valueArgument (NL* COMMA NL* valueArgument)* (NL* COMMA)? NL*)? RPAREN - ; - -valueArgument - : annotation? NL* (simpleIdentifier NL* ASSIGNMENT NL*)? MULT? NL* expression - ; - -primaryExpression - : parenthesizedExpression - | simpleIdentifier - | literalConstant - | stringLiteral - | callableReference - | functionLiteral - | objectLiteral - | collectionLiteral - | thisExpression - | superExpression - | ifExpression - | whenExpression - | tryExpression - | jumpExpression - ; - -parenthesizedExpression - : LPAREN NL* expression NL* RPAREN - ; - -collectionLiteral - : LSQUARE NL* (expression (NL* COMMA NL* expression)* (NL* COMMA)? NL*)? RSQUARE - ; - -literalConstant - : BooleanLiteral - | IntegerLiteral - | HexLiteral - | BinLiteral - | CharacterLiteral - | RealLiteral - | NullLiteral - | LongLiteral - | UnsignedLiteral - ; - -stringLiteral - : lineStringLiteral - | multiLineStringLiteral - ; - -lineStringLiteral - : QUOTE_OPEN (lineStringContent | lineStringExpression)* QUOTE_CLOSE - ; - -multiLineStringLiteral - : TRIPLE_QUOTE_OPEN (multiLineStringContent | multiLineStringExpression | MultiLineStringQuote)* TRIPLE_QUOTE_CLOSE - ; - -lineStringContent - : LineStrText - | LineStrEscapedChar - | LineStrRef - ; - -lineStringExpression - : LineStrExprStart NL* expression NL* RCURL - ; - -multiLineStringContent - : MultiLineStrText - | MultiLineStringQuote - | MultiLineStrRef - ; - -multiLineStringExpression - : MultiLineStrExprStart NL* expression NL* RCURL - ; - -lambdaLiteral - : LCURL NL* (lambdaParameters? NL* ARROW NL*)? statements NL* RCURL - ; - -lambdaParameters - : lambdaParameter (NL* COMMA NL* lambdaParameter)* (NL* COMMA)? - ; - -lambdaParameter - : variableDeclaration - | multiVariableDeclaration (NL* COLON NL* type)? - ; - -anonymousFunction - : FUN - (NL* type NL* DOT)? - NL* parametersWithOptionalType - (NL* COLON NL* type)? - (NL* typeConstraints)? - (NL* functionBody)? - ; - -functionLiteral - : lambdaLiteral - | anonymousFunction - ; - -objectLiteral - : OBJECT (NL* COLON NL* delegationSpecifiers NL*)? (NL* classBody)? - ; - -thisExpression - : THIS - | THIS_AT - ; - -superExpression - : SUPER (LANGLE NL* type NL* RANGLE)? (AT_NO_WS simpleIdentifier)? - | SUPER_AT - ; - -ifExpression - : IF NL* LPAREN NL* expression NL* RPAREN NL* - ( controlStructureBody - | controlStructureBody? NL* SEMICOLON? NL* ELSE NL* (controlStructureBody | SEMICOLON) - | SEMICOLON) - ; - -whenSubject - : LPAREN (annotation* NL* VAL NL* variableDeclaration NL* ASSIGNMENT NL*)? expression RPAREN - ; - -whenExpression - : WHEN NL* whenSubject? NL* LCURL NL* (whenEntry NL*)* NL* RCURL - ; - -whenEntry - : whenCondition (NL* COMMA NL* whenCondition)* (NL* COMMA)? NL* ARROW NL* controlStructureBody semi? - | ELSE NL* ARROW NL* controlStructureBody semi? - ; - -whenCondition - : expression - | rangeTest - | typeTest - ; - -rangeTest - : inOperator NL* expression - ; - -typeTest - : isOperator NL* type - ; - -tryExpression - : TRY NL* block ((NL* catchBlock)+ (NL* finallyBlock)? | NL* finallyBlock) - ; - -catchBlock - : CATCH NL* LPAREN annotation* simpleIdentifier COLON type (NL* COMMA)? RPAREN NL* block - ; - -finallyBlock - : FINALLY NL* block - ; - -jumpExpression - : THROW NL* expression - | (RETURN | RETURN_AT) expression? - | CONTINUE - | CONTINUE_AT - | BREAK - | BREAK_AT - ; - -callableReference - : receiverType? COLONCOLON NL* (simpleIdentifier | CLASS) - ; - -assignmentAndOperator - : ADD_ASSIGNMENT - | SUB_ASSIGNMENT - | MULT_ASSIGNMENT - | DIV_ASSIGNMENT - | MOD_ASSIGNMENT - ; - -equalityOperator - : EXCL_EQ - | EXCL_EQEQ - | EQEQ - | EQEQEQ - ; - -comparisonOperator - : LANGLE - | RANGLE - | LE - | GE - ; - -inOperator - : IN - | NOT_IN - ; - -isOperator - : IS - | NOT_IS - ; - -additiveOperator - : ADD - | SUB - ; - -multiplicativeOperator - : MULT - | DIV - | MOD - ; - -asOperator - : AS - | AS_SAFE - ; - -prefixUnaryOperator - : INCR - | DECR - | SUB - | ADD - | excl - ; - -postfixUnaryOperator - : INCR - | DECR - | EXCL_NO_WS excl - ; - -excl - : EXCL_NO_WS - | EXCL_WS - ; - -memberAccessOperator - : NL* DOT - | NL* safeNav - | COLONCOLON - ; - -safeNav - : QUEST_NO_WS DOT - ; - -// SECTION: modifiers - -modifiers - : (annotation | modifier)+ - ; - -parameterModifiers - : (annotation | parameterModifier)+ - ; - -modifier - : (classModifier - | memberModifier - | visibilityModifier - | functionModifier - | propertyModifier - | inheritanceModifier - | parameterModifier - | platformModifier) NL* - ; - -typeModifiers - : typeModifier+ - ; - -typeModifier - : annotation - | SUSPEND NL* - ; - -classModifier - : ENUM - | SEALED - | ANNOTATION - | DATA - | INNER - | VALUE - ; - -memberModifier - : OVERRIDE - | LATEINIT - ; - -visibilityModifier - : PUBLIC - | PRIVATE - | INTERNAL - | PROTECTED - ; - -varianceModifier - : IN - | OUT - ; - -typeParameterModifiers - : typeParameterModifier+ - ; - -typeParameterModifier - : reificationModifier NL* - | varianceModifier NL* - | annotation - ; - -functionModifier - : TAILREC - | OPERATOR - | INFIX - | INLINE - | EXTERNAL - | SUSPEND - ; - -propertyModifier - : CONST - ; - -inheritanceModifier - : ABSTRACT - | FINAL - | OPEN - ; - -parameterModifier - : VARARG - | NOINLINE - | CROSSINLINE - ; - -reificationModifier - : REIFIED - ; - -platformModifier - : EXPECT - | ACTUAL - ; - -// SECTION: annotations - -annotation - : (singleAnnotation | multiAnnotation) NL* - ; - -singleAnnotation - : (annotationUseSiteTarget NL* | AT_NO_WS | AT_PRE_WS) unescapedAnnotation - ; - -multiAnnotation - : (annotationUseSiteTarget NL* | AT_NO_WS | AT_PRE_WS) LSQUARE unescapedAnnotation+ RSQUARE - ; - -annotationUseSiteTarget - : (AT_NO_WS | AT_PRE_WS) (FIELD | PROPERTY | GET | SET | RECEIVER | PARAM | SETPARAM | DELEGATE) NL* COLON - ; - -unescapedAnnotation - : constructorInvocation - | userType - ; - -// SECTION: identifiers - -simpleIdentifier - : Identifier - | ABSTRACT - | ANNOTATION - | BY - | CATCH - | COMPANION - | CONSTRUCTOR - | CROSSINLINE - | DATA - | DYNAMIC - | ENUM - | EXTERNAL - | FINAL - | FINALLY - | GET - | IMPORT - | INFIX - | INIT - | INLINE - | INNER - | INTERNAL - | LATEINIT - | NOINLINE - | OPEN - | OPERATOR - | OUT - | OVERRIDE - | PRIVATE - | PROTECTED - | PUBLIC - | REIFIED - | SEALED - | TAILREC - | SET - | VARARG - | WHERE - | FIELD - | PROPERTY - | RECEIVER - | PARAM - | SETPARAM - | DELEGATE - | FILE - | EXPECT - | ACTUAL - | CONST - | SUSPEND - | VALUE - ; - -identifier - : simpleIdentifier (NL* DOT simpleIdentifier)* - ; diff --git a/orx-jvm/orx-kotlin-parser/src/main/antlr/README.md b/orx-jvm/orx-kotlin-parser/src/main/antlr/README.md deleted file mode 100644 index 94c736a4..00000000 --- a/orx-jvm/orx-kotlin-parser/src/main/antlr/README.md +++ /dev/null @@ -1 +0,0 @@ -Synced from https://github.com/Kotlin/kotlin-spec/ @ 8e3f52e5ee01c03c1884cfd34d5ed7e3e2f016c1 diff --git a/orx-jvm/orx-kotlin-parser/src/main/antlr/UnicodeClasses.g4 b/orx-jvm/orx-kotlin-parser/src/main/antlr/UnicodeClasses.g4 deleted file mode 100644 index 53728480..00000000 --- a/orx-jvm/orx-kotlin-parser/src/main/antlr/UnicodeClasses.g4 +++ /dev/null @@ -1,1649 +0,0 @@ -/** - * Kotlin lexical grammar in ANTLR4 notation (Unicode classes) - * - * Taken from http://www.antlr3.org/grammar/1345144569663/AntlrUnicode.txt - */ - -lexer grammar UnicodeClasses; - -UNICODE_CLASS_LL: - '\u0061'..'\u007A' | - '\u00B5' | - '\u00DF'..'\u00F6' | - '\u00F8'..'\u00FF' | - '\u0101' | - '\u0103' | - '\u0105' | - '\u0107' | - '\u0109' | - '\u010B' | - '\u010D' | - '\u010F' | - '\u0111' | - '\u0113' | - '\u0115' | - '\u0117' | - '\u0119' | - '\u011B' | - '\u011D' | - '\u011F' | - '\u0121' | - '\u0123' | - '\u0125' | - '\u0127' | - '\u0129' | - '\u012B' | - '\u012D' | - '\u012F' | - '\u0131' | - '\u0133' | - '\u0135' | - '\u0137' | - '\u0138' | - '\u013A' | - '\u013C' | - '\u013E' | - '\u0140' | - '\u0142' | - '\u0144' | - '\u0146' | - '\u0148' | - '\u0149' | - '\u014B' | - '\u014D' | - '\u014F' | - '\u0151' | - '\u0153' | - '\u0155' | - '\u0157' | - '\u0159' | - '\u015B' | - '\u015D' | - '\u015F' | - '\u0161' | - '\u0163' | - '\u0165' | - '\u0167' | - '\u0169' | - '\u016B' | - '\u016D' | - '\u016F' | - '\u0171' | - '\u0173' | - '\u0175' | - '\u0177' | - '\u017A' | - '\u017C' | - '\u017E'..'\u0180' | - '\u0183' | - '\u0185' | - '\u0188' | - '\u018C' | - '\u018D' | - '\u0192' | - '\u0195' | - '\u0199'..'\u019B' | - '\u019E' | - '\u01A1' | - '\u01A3' | - '\u01A5' | - '\u01A8' | - '\u01AA' | - '\u01AB' | - '\u01AD' | - '\u01B0' | - '\u01B4' | - '\u01B6' | - '\u01B9' | - '\u01BA' | - '\u01BD'..'\u01BF' | - '\u01C6' | - '\u01C9' | - '\u01CC' | - '\u01CE' | - '\u01D0' | - '\u01D2' | - '\u01D4' | - '\u01D6' | - '\u01D8' | - '\u01DA' | - '\u01DC' | - '\u01DD' | - '\u01DF' | - '\u01E1' | - '\u01E3' | - '\u01E5' | - '\u01E7' | - '\u01E9' | - '\u01EB' | - '\u01ED' | - '\u01EF' | - '\u01F0' | - '\u01F3' | - '\u01F5' | - '\u01F9' | - '\u01FB' | - '\u01FD' | - '\u01FF' | - '\u0201' | - '\u0203' | - '\u0205' | - '\u0207' | - '\u0209' | - '\u020B' | - '\u020D' | - '\u020F' | - '\u0211' | - '\u0213' | - '\u0215' | - '\u0217' | - '\u0219' | - '\u021B' | - '\u021D' | - '\u021F' | - '\u0221' | - '\u0223' | - '\u0225' | - '\u0227' | - '\u0229' | - '\u022B' | - '\u022D' | - '\u022F' | - '\u0231' | - '\u0233'..'\u0239' | - '\u023C' | - '\u023F' | - '\u0240' | - '\u0242' | - '\u0247' | - '\u0249' | - '\u024B' | - '\u024D' | - '\u024F'..'\u0293' | - '\u0295'..'\u02AF' | - '\u0371' | - '\u0373' | - '\u0377' | - '\u037B'..'\u037D' | - '\u0390' | - '\u03AC'..'\u03CE' | - '\u03D0' | - '\u03D1' | - '\u03D5'..'\u03D7' | - '\u03D9' | - '\u03DB' | - '\u03DD' | - '\u03DF' | - '\u03E1' | - '\u03E3' | - '\u03E5' | - '\u03E7' | - '\u03E9' | - '\u03EB' | - '\u03ED' | - '\u03EF'..'\u03F3' | - '\u03F5' | - '\u03F8' | - '\u03FB' | - '\u03FC' | - '\u0430'..'\u045F' | - '\u0461' | - '\u0463' | - '\u0465' | - '\u0467' | - '\u0469' | - '\u046B' | - '\u046D' | - '\u046F' | - '\u0471' | - '\u0473' | - '\u0475' | - '\u0477' | - '\u0479' | - '\u047B' | - '\u047D' | - '\u047F' | - '\u0481' | - '\u048B' | - '\u048D' | - '\u048F' | - '\u0491' | - '\u0493' | - '\u0495' | - '\u0497' | - '\u0499' | - '\u049B' | - '\u049D' | - '\u049F' | - '\u04A1' | - '\u04A3' | - '\u04A5' | - '\u04A7' | - '\u04A9' | - '\u04AB' | - '\u04AD' | - '\u04AF' | - '\u04B1' | - '\u04B3' | - '\u04B5' | - '\u04B7' | - '\u04B9' | - '\u04BB' | - '\u04BD' | - '\u04BF' | - '\u04C2' | - '\u04C4' | - '\u04C6' | - '\u04C8' | - '\u04CA' | - '\u04CC' | - '\u04CE' | - '\u04CF' | - '\u04D1' | - '\u04D3' | - '\u04D5' | - '\u04D7' | - '\u04D9' | - '\u04DB' | - '\u04DD' | - '\u04DF' | - '\u04E1' | - '\u04E3' | - '\u04E5' | - '\u04E7' | - '\u04E9' | - '\u04EB' | - '\u04ED' | - '\u04EF' | - '\u04F1' | - '\u04F3' | - '\u04F5' | - '\u04F7' | - '\u04F9' | - '\u04FB' | - '\u04FD' | - '\u04FF' | - '\u0501' | - '\u0503' | - '\u0505' | - '\u0507' | - '\u0509' | - '\u050B' | - '\u050D' | - '\u050F' | - '\u0511' | - '\u0513' | - '\u0515' | - '\u0517' | - '\u0519' | - '\u051B' | - '\u051D' | - '\u051F' | - '\u0521' | - '\u0523' | - '\u0525' | - '\u0527' | - '\u0561'..'\u0587' | - '\u1D00'..'\u1D2B' | - '\u1D6B'..'\u1D77' | - '\u1D79'..'\u1D9A' | - '\u1E01' | - '\u1E03' | - '\u1E05' | - '\u1E07' | - '\u1E09' | - '\u1E0B' | - '\u1E0D' | - '\u1E0F' | - '\u1E11' | - '\u1E13' | - '\u1E15' | - '\u1E17' | - '\u1E19' | - '\u1E1B' | - '\u1E1D' | - '\u1E1F' | - '\u1E21' | - '\u1E23' | - '\u1E25' | - '\u1E27' | - '\u1E29' | - '\u1E2B' | - '\u1E2D' | - '\u1E2F' | - '\u1E31' | - '\u1E33' | - '\u1E35' | - '\u1E37' | - '\u1E39' | - '\u1E3B' | - '\u1E3D' | - '\u1E3F' | - '\u1E41' | - '\u1E43' | - '\u1E45' | - '\u1E47' | - '\u1E49' | - '\u1E4B' | - '\u1E4D' | - '\u1E4F' | - '\u1E51' | - '\u1E53' | - '\u1E55' | - '\u1E57' | - '\u1E59' | - '\u1E5B' | - '\u1E5D' | - '\u1E5F' | - '\u1E61' | - '\u1E63' | - '\u1E65' | - '\u1E67' | - '\u1E69' | - '\u1E6B' | - '\u1E6D' | - '\u1E6F' | - '\u1E71' | - '\u1E73' | - '\u1E75' | - '\u1E77' | - '\u1E79' | - '\u1E7B' | - '\u1E7D' | - '\u1E7F' | - '\u1E81' | - '\u1E83' | - '\u1E85' | - '\u1E87' | - '\u1E89' | - '\u1E8B' | - '\u1E8D' | - '\u1E8F' | - '\u1E91' | - '\u1E93' | - '\u1E95'..'\u1E9D' | - '\u1E9F' | - '\u1EA1' | - '\u1EA3' | - '\u1EA5' | - '\u1EA7' | - '\u1EA9' | - '\u1EAB' | - '\u1EAD' | - '\u1EAF' | - '\u1EB1' | - '\u1EB3' | - '\u1EB5' | - '\u1EB7' | - '\u1EB9' | - '\u1EBB' | - '\u1EBD' | - '\u1EBF' | - '\u1EC1' | - '\u1EC3' | - '\u1EC5' | - '\u1EC7' | - '\u1EC9' | - '\u1ECB' | - '\u1ECD' | - '\u1ECF' | - '\u1ED1' | - '\u1ED3' | - '\u1ED5' | - '\u1ED7' | - '\u1ED9' | - '\u1EDB' | - '\u1EDD' | - '\u1EDF' | - '\u1EE1' | - '\u1EE3' | - '\u1EE5' | - '\u1EE7' | - '\u1EE9' | - '\u1EEB' | - '\u1EED' | - '\u1EEF' | - '\u1EF1' | - '\u1EF3' | - '\u1EF5' | - '\u1EF7' | - '\u1EF9' | - '\u1EFB' | - '\u1EFD' | - '\u1EFF'..'\u1F07' | - '\u1F10'..'\u1F15' | - '\u1F20'..'\u1F27' | - '\u1F30'..'\u1F37' | - '\u1F40'..'\u1F45' | - '\u1F50'..'\u1F57' | - '\u1F60'..'\u1F67' | - '\u1F70'..'\u1F7D' | - '\u1F80'..'\u1F87' | - '\u1F90'..'\u1F97' | - '\u1FA0'..'\u1FA7' | - '\u1FB0'..'\u1FB4' | - '\u1FB6' | - '\u1FB7' | - '\u1FBE' | - '\u1FC2'..'\u1FC4' | - '\u1FC6' | - '\u1FC7' | - '\u1FD0'..'\u1FD3' | - '\u1FD6' | - '\u1FD7' | - '\u1FE0'..'\u1FE7' | - '\u1FF2'..'\u1FF4' | - '\u1FF6' | - '\u1FF7' | - '\u210A' | - '\u210E' | - '\u210F' | - '\u2113' | - '\u212F' | - '\u2134' | - '\u2139' | - '\u213C' | - '\u213D' | - '\u2146'..'\u2149' | - '\u214E' | - '\u2184' | - '\u2C30'..'\u2C5E' | - '\u2C61' | - '\u2C65' | - '\u2C66' | - '\u2C68' | - '\u2C6A' | - '\u2C6C' | - '\u2C71' | - '\u2C73' | - '\u2C74' | - '\u2C76'..'\u2C7B' | - '\u2C81' | - '\u2C83' | - '\u2C85' | - '\u2C87' | - '\u2C89' | - '\u2C8B' | - '\u2C8D' | - '\u2C8F' | - '\u2C91' | - '\u2C93' | - '\u2C95' | - '\u2C97' | - '\u2C99' | - '\u2C9B' | - '\u2C9D' | - '\u2C9F' | - '\u2CA1' | - '\u2CA3' | - '\u2CA5' | - '\u2CA7' | - '\u2CA9' | - '\u2CAB' | - '\u2CAD' | - '\u2CAF' | - '\u2CB1' | - '\u2CB3' | - '\u2CB5' | - '\u2CB7' | - '\u2CB9' | - '\u2CBB' | - '\u2CBD' | - '\u2CBF' | - '\u2CC1' | - '\u2CC3' | - '\u2CC5' | - '\u2CC7' | - '\u2CC9' | - '\u2CCB' | - '\u2CCD' | - '\u2CCF' | - '\u2CD1' | - '\u2CD3' | - '\u2CD5' | - '\u2CD7' | - '\u2CD9' | - '\u2CDB' | - '\u2CDD' | - '\u2CDF' | - '\u2CE1' | - '\u2CE3' | - '\u2CE4' | - '\u2CEC' | - '\u2CEE' | - '\u2CF3' | - '\u2D00'..'\u2D25' | - '\u2D27' | - '\u2D2D' | - '\uA641' | - '\uA643' | - '\uA645' | - '\uA647' | - '\uA649' | - '\uA64B' | - '\uA64D' | - '\uA64F' | - '\uA651' | - '\uA653' | - '\uA655' | - '\uA657' | - '\uA659' | - '\uA65B' | - '\uA65D' | - '\uA65F' | - '\uA661' | - '\uA663' | - '\uA665' | - '\uA667' | - '\uA669' | - '\uA66B' | - '\uA66D' | - '\uA681' | - '\uA683' | - '\uA685' | - '\uA687' | - '\uA689' | - '\uA68B' | - '\uA68D' | - '\uA68F' | - '\uA691' | - '\uA693' | - '\uA695' | - '\uA697' | - '\uA723' | - '\uA725' | - '\uA727' | - '\uA729' | - '\uA72B' | - '\uA72D' | - '\uA72F'..'\uA731' | - '\uA733' | - '\uA735' | - '\uA737' | - '\uA739' | - '\uA73B' | - '\uA73D' | - '\uA73F' | - '\uA741' | - '\uA743' | - '\uA745' | - '\uA747' | - '\uA749' | - '\uA74B' | - '\uA74D' | - '\uA74F' | - '\uA751' | - '\uA753' | - '\uA755' | - '\uA757' | - '\uA759' | - '\uA75B' | - '\uA75D' | - '\uA75F' | - '\uA761' | - '\uA763' | - '\uA765' | - '\uA767' | - '\uA769' | - '\uA76B' | - '\uA76D' | - '\uA76F' | - '\uA771'..'\uA778' | - '\uA77A' | - '\uA77C' | - '\uA77F' | - '\uA781' | - '\uA783' | - '\uA785' | - '\uA787' | - '\uA78C' | - '\uA78E' | - '\uA791' | - '\uA793' | - '\uA7A1' | - '\uA7A3' | - '\uA7A5' | - '\uA7A7' | - '\uA7A9' | - '\uA7FA' | - '\uFB00'..'\uFB06' | - '\uFB13'..'\uFB17' | - '\uFF41'..'\uFF5A'; - -UNICODE_CLASS_LM: - '\u02B0'..'\u02C1' | - '\u02C6'..'\u02D1' | - '\u02E0'..'\u02E4' | - '\u02EC' | - '\u02EE' | - '\u0374' | - '\u037A' | - '\u0559' | - '\u0640' | - '\u06E5' | - '\u06E6' | - '\u07F4' | - '\u07F5' | - '\u07FA' | - '\u081A' | - '\u0824' | - '\u0828' | - '\u0971' | - '\u0E46' | - '\u0EC6' | - '\u10FC' | - '\u17D7' | - '\u1843' | - '\u1AA7' | - '\u1C78'..'\u1C7D' | - '\u1D2C'..'\u1D6A' | - '\u1D78' | - '\u1D9B'..'\u1DBF' | - '\u2071' | - '\u207F' | - '\u2090'..'\u209C' | - '\u2C7C' | - '\u2C7D' | - '\u2D6F' | - '\u2E2F' | - '\u3005' | - '\u3031'..'\u3035' | - '\u303B' | - '\u309D' | - '\u309E' | - '\u30FC'..'\u30FE' | - '\uA015' | - '\uA4F8'..'\uA4FD' | - '\uA60C' | - '\uA67F' | - '\uA717'..'\uA71F' | - '\uA770' | - '\uA788' | - '\uA7F8' | - '\uA7F9' | - '\uA9CF' | - '\uAA70' | - '\uAADD' | - '\uAAF3' | - '\uAAF4' | - '\uFF70' | - '\uFF9E' | - '\uFF9F'; - -UNICODE_CLASS_LO: - '\u00AA' | - '\u00BA' | - '\u01BB' | - '\u01C0'..'\u01C3' | - '\u0294' | - '\u05D0'..'\u05EA' | - '\u05F0'..'\u05F2' | - '\u0620'..'\u063F' | - '\u0641'..'\u064A' | - '\u066E' | - '\u066F' | - '\u0671'..'\u06D3' | - '\u06D5' | - '\u06EE' | - '\u06EF' | - '\u06FA'..'\u06FC' | - '\u06FF' | - '\u0710' | - '\u0712'..'\u072F' | - '\u074D'..'\u07A5' | - '\u07B1' | - '\u07CA'..'\u07EA' | - '\u0800'..'\u0815' | - '\u0840'..'\u0858' | - '\u08A0' | - '\u08A2'..'\u08AC' | - '\u0904'..'\u0939' | - '\u093D' | - '\u0950' | - '\u0958'..'\u0961' | - '\u0972'..'\u0977' | - '\u0979'..'\u097F' | - '\u0985'..'\u098C' | - '\u098F' | - '\u0990' | - '\u0993'..'\u09A8' | - '\u09AA'..'\u09B0' | - '\u09B2' | - '\u09B6'..'\u09B9' | - '\u09BD' | - '\u09CE' | - '\u09DC' | - '\u09DD' | - '\u09DF'..'\u09E1' | - '\u09F0' | - '\u09F1' | - '\u0A05'..'\u0A0A' | - '\u0A0F' | - '\u0A10' | - '\u0A13'..'\u0A28' | - '\u0A2A'..'\u0A30' | - '\u0A32' | - '\u0A33' | - '\u0A35' | - '\u0A36' | - '\u0A38' | - '\u0A39' | - '\u0A59'..'\u0A5C' | - '\u0A5E' | - '\u0A72'..'\u0A74' | - '\u0A85'..'\u0A8D' | - '\u0A8F'..'\u0A91' | - '\u0A93'..'\u0AA8' | - '\u0AAA'..'\u0AB0' | - '\u0AB2' | - '\u0AB3' | - '\u0AB5'..'\u0AB9' | - '\u0ABD' | - '\u0AD0' | - '\u0AE0' | - '\u0AE1' | - '\u0B05'..'\u0B0C' | - '\u0B0F' | - '\u0B10' | - '\u0B13'..'\u0B28' | - '\u0B2A'..'\u0B30' | - '\u0B32' | - '\u0B33' | - '\u0B35'..'\u0B39' | - '\u0B3D' | - '\u0B5C' | - '\u0B5D' | - '\u0B5F'..'\u0B61' | - '\u0B71' | - '\u0B83' | - '\u0B85'..'\u0B8A' | - '\u0B8E'..'\u0B90' | - '\u0B92'..'\u0B95' | - '\u0B99' | - '\u0B9A' | - '\u0B9C' | - '\u0B9E' | - '\u0B9F' | - '\u0BA3' | - '\u0BA4' | - '\u0BA8'..'\u0BAA' | - '\u0BAE'..'\u0BB9' | - '\u0BD0' | - '\u0C05'..'\u0C0C' | - '\u0C0E'..'\u0C10' | - '\u0C12'..'\u0C28' | - '\u0C2A'..'\u0C33' | - '\u0C35'..'\u0C39' | - '\u0C3D' | - '\u0C58' | - '\u0C59' | - '\u0C60' | - '\u0C61' | - '\u0C85'..'\u0C8C' | - '\u0C8E'..'\u0C90' | - '\u0C92'..'\u0CA8' | - '\u0CAA'..'\u0CB3' | - '\u0CB5'..'\u0CB9' | - '\u0CBD' | - '\u0CDE' | - '\u0CE0' | - '\u0CE1' | - '\u0CF1' | - '\u0CF2' | - '\u0D05'..'\u0D0C' | - '\u0D0E'..'\u0D10' | - '\u0D12'..'\u0D3A' | - '\u0D3D' | - '\u0D4E' | - '\u0D60' | - '\u0D61' | - '\u0D7A'..'\u0D7F' | - '\u0D85'..'\u0D96' | - '\u0D9A'..'\u0DB1' | - '\u0DB3'..'\u0DBB' | - '\u0DBD' | - '\u0DC0'..'\u0DC6' | - '\u0E01'..'\u0E30' | - '\u0E32' | - '\u0E33' | - '\u0E40'..'\u0E45' | - '\u0E81' | - '\u0E82' | - '\u0E84' | - '\u0E87' | - '\u0E88' | - '\u0E8A' | - '\u0E8D' | - '\u0E94'..'\u0E97' | - '\u0E99'..'\u0E9F' | - '\u0EA1'..'\u0EA3' | - '\u0EA5' | - '\u0EA7' | - '\u0EAA' | - '\u0EAB' | - '\u0EAD'..'\u0EB0' | - '\u0EB2' | - '\u0EB3' | - '\u0EBD' | - '\u0EC0'..'\u0EC4' | - '\u0EDC'..'\u0EDF' | - '\u0F00' | - '\u0F40'..'\u0F47' | - '\u0F49'..'\u0F6C' | - '\u0F88'..'\u0F8C' | - '\u1000'..'\u102A' | - '\u103F' | - '\u1050'..'\u1055' | - '\u105A'..'\u105D' | - '\u1061' | - '\u1065' | - '\u1066' | - '\u106E'..'\u1070' | - '\u1075'..'\u1081' | - '\u108E' | - '\u10D0'..'\u10FA' | - '\u10FD'..'\u1248' | - '\u124A'..'\u124D' | - '\u1250'..'\u1256' | - '\u1258' | - '\u125A'..'\u125D' | - '\u1260'..'\u1288' | - '\u128A'..'\u128D' | - '\u1290'..'\u12B0' | - '\u12B2'..'\u12B5' | - '\u12B8'..'\u12BE' | - '\u12C0' | - '\u12C2'..'\u12C5' | - '\u12C8'..'\u12D6' | - '\u12D8'..'\u1310' | - '\u1312'..'\u1315' | - '\u1318'..'\u135A' | - '\u1380'..'\u138F' | - '\u13A0'..'\u13F4' | - '\u1401'..'\u166C' | - '\u166F'..'\u167F' | - '\u1681'..'\u169A' | - '\u16A0'..'\u16EA' | - '\u1700'..'\u170C' | - '\u170E'..'\u1711' | - '\u1720'..'\u1731' | - '\u1740'..'\u1751' | - '\u1760'..'\u176C' | - '\u176E'..'\u1770' | - '\u1780'..'\u17B3' | - '\u17DC' | - '\u1820'..'\u1842' | - '\u1844'..'\u1877' | - '\u1880'..'\u18A8' | - '\u18AA' | - '\u18B0'..'\u18F5' | - '\u1900'..'\u191C' | - '\u1950'..'\u196D' | - '\u1970'..'\u1974' | - '\u1980'..'\u19AB' | - '\u19C1'..'\u19C7' | - '\u1A00'..'\u1A16' | - '\u1A20'..'\u1A54' | - '\u1B05'..'\u1B33' | - '\u1B45'..'\u1B4B' | - '\u1B83'..'\u1BA0' | - '\u1BAE' | - '\u1BAF' | - '\u1BBA'..'\u1BE5' | - '\u1C00'..'\u1C23' | - '\u1C4D'..'\u1C4F' | - '\u1C5A'..'\u1C77' | - '\u1CE9'..'\u1CEC' | - '\u1CEE'..'\u1CF1' | - '\u1CF5' | - '\u1CF6' | - '\u2135'..'\u2138' | - '\u2D30'..'\u2D67' | - '\u2D80'..'\u2D96' | - '\u2DA0'..'\u2DA6' | - '\u2DA8'..'\u2DAE' | - '\u2DB0'..'\u2DB6' | - '\u2DB8'..'\u2DBE' | - '\u2DC0'..'\u2DC6' | - '\u2DC8'..'\u2DCE' | - '\u2DD0'..'\u2DD6' | - '\u2DD8'..'\u2DDE' | - '\u3006' | - '\u303C' | - '\u3041'..'\u3096' | - '\u309F' | - '\u30A1'..'\u30FA' | - '\u30FF' | - '\u3105'..'\u312D' | - '\u3131'..'\u318E' | - '\u31A0'..'\u31BA' | - '\u31F0'..'\u31FF' | - '\u3400' | - '\u4DB5' | - '\u4E00' | - '\u9FCC' | - '\uA000'..'\uA014' | - '\uA016'..'\uA48C' | - '\uA4D0'..'\uA4F7' | - '\uA500'..'\uA60B' | - '\uA610'..'\uA61F' | - '\uA62A' | - '\uA62B' | - '\uA66E' | - '\uA6A0'..'\uA6E5' | - '\uA7FB'..'\uA801' | - '\uA803'..'\uA805' | - '\uA807'..'\uA80A' | - '\uA80C'..'\uA822' | - '\uA840'..'\uA873' | - '\uA882'..'\uA8B3' | - '\uA8F2'..'\uA8F7' | - '\uA8FB' | - '\uA90A'..'\uA925' | - '\uA930'..'\uA946' | - '\uA960'..'\uA97C' | - '\uA984'..'\uA9B2' | - '\uAA00'..'\uAA28' | - '\uAA40'..'\uAA42' | - '\uAA44'..'\uAA4B' | - '\uAA60'..'\uAA6F' | - '\uAA71'..'\uAA76' | - '\uAA7A' | - '\uAA80'..'\uAAAF' | - '\uAAB1' | - '\uAAB5' | - '\uAAB6' | - '\uAAB9'..'\uAABD' | - '\uAAC0' | - '\uAAC2' | - '\uAADB' | - '\uAADC' | - '\uAAE0'..'\uAAEA' | - '\uAAF2' | - '\uAB01'..'\uAB06' | - '\uAB09'..'\uAB0E' | - '\uAB11'..'\uAB16' | - '\uAB20'..'\uAB26' | - '\uAB28'..'\uAB2E' | - '\uABC0'..'\uABE2' | - '\uAC00' | - '\uD7A3' | - '\uD7B0'..'\uD7C6' | - '\uD7CB'..'\uD7FB' | - '\uF900'..'\uFA6D' | - '\uFA70'..'\uFAD9' | - '\uFB1D' | - '\uFB1F'..'\uFB28' | - '\uFB2A'..'\uFB36' | - '\uFB38'..'\uFB3C' | - '\uFB3E' | - '\uFB40' | - '\uFB41' | - '\uFB43' | - '\uFB44' | - '\uFB46'..'\uFBB1' | - '\uFBD3'..'\uFD3D' | - '\uFD50'..'\uFD8F' | - '\uFD92'..'\uFDC7' | - '\uFDF0'..'\uFDFB' | - '\uFE70'..'\uFE74' | - '\uFE76'..'\uFEFC' | - '\uFF66'..'\uFF6F' | - '\uFF71'..'\uFF9D' | - '\uFFA0'..'\uFFBE' | - '\uFFC2'..'\uFFC7' | - '\uFFCA'..'\uFFCF' | - '\uFFD2'..'\uFFD7' | - '\uFFDA'..'\uFFDC'; - -UNICODE_CLASS_LT: - '\u01C5' | - '\u01C8' | - '\u01CB' | - '\u01F2' | - '\u1F88'..'\u1F8F' | - '\u1F98'..'\u1F9F' | - '\u1FA8'..'\u1FAF' | - '\u1FBC' | - '\u1FCC' | - '\u1FFC'; - -UNICODE_CLASS_LU: - '\u0041'..'\u005A' | - '\u00C0'..'\u00D6' | - '\u00D8'..'\u00DE' | - '\u0100' | - '\u0102' | - '\u0104' | - '\u0106' | - '\u0108' | - '\u010A' | - '\u010C' | - '\u010E' | - '\u0110' | - '\u0112' | - '\u0114' | - '\u0116' | - '\u0118' | - '\u011A' | - '\u011C' | - '\u011E' | - '\u0120' | - '\u0122' | - '\u0124' | - '\u0126' | - '\u0128' | - '\u012A' | - '\u012C' | - '\u012E' | - '\u0130' | - '\u0132' | - '\u0134' | - '\u0136' | - '\u0139' | - '\u013B' | - '\u013D' | - '\u013F' | - '\u0141' | - '\u0143' | - '\u0145' | - '\u0147' | - '\u014A' | - '\u014C' | - '\u014E' | - '\u0150' | - '\u0152' | - '\u0154' | - '\u0156' | - '\u0158' | - '\u015A' | - '\u015C' | - '\u015E' | - '\u0160' | - '\u0162' | - '\u0164' | - '\u0166' | - '\u0168' | - '\u016A' | - '\u016C' | - '\u016E' | - '\u0170' | - '\u0172' | - '\u0174' | - '\u0176' | - '\u0178' | - '\u0179' | - '\u017B' | - '\u017D' | - '\u0181' | - '\u0182' | - '\u0184' | - '\u0186' | - '\u0187' | - '\u0189'..'\u018B' | - '\u018E'..'\u0191' | - '\u0193' | - '\u0194' | - '\u0196'..'\u0198' | - '\u019C' | - '\u019D' | - '\u019F' | - '\u01A0' | - '\u01A2' | - '\u01A4' | - '\u01A6' | - '\u01A7' | - '\u01A9' | - '\u01AC' | - '\u01AE' | - '\u01AF' | - '\u01B1'..'\u01B3' | - '\u01B5' | - '\u01B7' | - '\u01B8' | - '\u01BC' | - '\u01C4' | - '\u01C7' | - '\u01CA' | - '\u01CD' | - '\u01CF' | - '\u01D1' | - '\u01D3' | - '\u01D5' | - '\u01D7' | - '\u01D9' | - '\u01DB' | - '\u01DE' | - '\u01E0' | - '\u01E2' | - '\u01E4' | - '\u01E6' | - '\u01E8' | - '\u01EA' | - '\u01EC' | - '\u01EE' | - '\u01F1' | - '\u01F4' | - '\u01F6'..'\u01F8' | - '\u01FA' | - '\u01FC' | - '\u01FE' | - '\u0200' | - '\u0202' | - '\u0204' | - '\u0206' | - '\u0208' | - '\u020A' | - '\u020C' | - '\u020E' | - '\u0210' | - '\u0212' | - '\u0214' | - '\u0216' | - '\u0218' | - '\u021A' | - '\u021C' | - '\u021E' | - '\u0220' | - '\u0222' | - '\u0224' | - '\u0226' | - '\u0228' | - '\u022A' | - '\u022C' | - '\u022E' | - '\u0230' | - '\u0232' | - '\u023A' | - '\u023B' | - '\u023D' | - '\u023E' | - '\u0241' | - '\u0243'..'\u0246' | - '\u0248' | - '\u024A' | - '\u024C' | - '\u024E' | - '\u0370' | - '\u0372' | - '\u0376' | - '\u0386' | - '\u0388'..'\u038A' | - '\u038C' | - '\u038E' | - '\u038F' | - '\u0391'..'\u03A1' | - '\u03A3'..'\u03AB' | - '\u03CF' | - '\u03D2'..'\u03D4' | - '\u03D8' | - '\u03DA' | - '\u03DC' | - '\u03DE' | - '\u03E0' | - '\u03E2' | - '\u03E4' | - '\u03E6' | - '\u03E8' | - '\u03EA' | - '\u03EC' | - '\u03EE' | - '\u03F4' | - '\u03F7' | - '\u03F9' | - '\u03FA' | - '\u03FD'..'\u042F' | - '\u0460' | - '\u0462' | - '\u0464' | - '\u0466' | - '\u0468' | - '\u046A' | - '\u046C' | - '\u046E' | - '\u0470' | - '\u0472' | - '\u0474' | - '\u0476' | - '\u0478' | - '\u047A' | - '\u047C' | - '\u047E' | - '\u0480' | - '\u048A' | - '\u048C' | - '\u048E' | - '\u0490' | - '\u0492' | - '\u0494' | - '\u0496' | - '\u0498' | - '\u049A' | - '\u049C' | - '\u049E' | - '\u04A0' | - '\u04A2' | - '\u04A4' | - '\u04A6' | - '\u04A8' | - '\u04AA' | - '\u04AC' | - '\u04AE' | - '\u04B0' | - '\u04B2' | - '\u04B4' | - '\u04B6' | - '\u04B8' | - '\u04BA' | - '\u04BC' | - '\u04BE' | - '\u04C0' | - '\u04C1' | - '\u04C3' | - '\u04C5' | - '\u04C7' | - '\u04C9' | - '\u04CB' | - '\u04CD' | - '\u04D0' | - '\u04D2' | - '\u04D4' | - '\u04D6' | - '\u04D8' | - '\u04DA' | - '\u04DC' | - '\u04DE' | - '\u04E0' | - '\u04E2' | - '\u04E4' | - '\u04E6' | - '\u04E8' | - '\u04EA' | - '\u04EC' | - '\u04EE' | - '\u04F0' | - '\u04F2' | - '\u04F4' | - '\u04F6' | - '\u04F8' | - '\u04FA' | - '\u04FC' | - '\u04FE' | - '\u0500' | - '\u0502' | - '\u0504' | - '\u0506' | - '\u0508' | - '\u050A' | - '\u050C' | - '\u050E' | - '\u0510' | - '\u0512' | - '\u0514' | - '\u0516' | - '\u0518' | - '\u051A' | - '\u051C' | - '\u051E' | - '\u0520' | - '\u0522' | - '\u0524' | - '\u0526' | - '\u0531'..'\u0556' | - '\u10A0'..'\u10C5' | - '\u10C7' | - '\u10CD' | - '\u1E00' | - '\u1E02' | - '\u1E04' | - '\u1E06' | - '\u1E08' | - '\u1E0A' | - '\u1E0C' | - '\u1E0E' | - '\u1E10' | - '\u1E12' | - '\u1E14' | - '\u1E16' | - '\u1E18' | - '\u1E1A' | - '\u1E1C' | - '\u1E1E' | - '\u1E20' | - '\u1E22' | - '\u1E24' | - '\u1E26' | - '\u1E28' | - '\u1E2A' | - '\u1E2C' | - '\u1E2E' | - '\u1E30' | - '\u1E32' | - '\u1E34' | - '\u1E36' | - '\u1E38' | - '\u1E3A' | - '\u1E3C' | - '\u1E3E' | - '\u1E40' | - '\u1E42' | - '\u1E44' | - '\u1E46' | - '\u1E48' | - '\u1E4A' | - '\u1E4C' | - '\u1E4E' | - '\u1E50' | - '\u1E52' | - '\u1E54' | - '\u1E56' | - '\u1E58' | - '\u1E5A' | - '\u1E5C' | - '\u1E5E' | - '\u1E60' | - '\u1E62' | - '\u1E64' | - '\u1E66' | - '\u1E68' | - '\u1E6A' | - '\u1E6C' | - '\u1E6E' | - '\u1E70' | - '\u1E72' | - '\u1E74' | - '\u1E76' | - '\u1E78' | - '\u1E7A' | - '\u1E7C' | - '\u1E7E' | - '\u1E80' | - '\u1E82' | - '\u1E84' | - '\u1E86' | - '\u1E88' | - '\u1E8A' | - '\u1E8C' | - '\u1E8E' | - '\u1E90' | - '\u1E92' | - '\u1E94' | - '\u1E9E' | - '\u1EA0' | - '\u1EA2' | - '\u1EA4' | - '\u1EA6' | - '\u1EA8' | - '\u1EAA' | - '\u1EAC' | - '\u1EAE' | - '\u1EB0' | - '\u1EB2' | - '\u1EB4' | - '\u1EB6' | - '\u1EB8' | - '\u1EBA' | - '\u1EBC' | - '\u1EBE' | - '\u1EC0' | - '\u1EC2' | - '\u1EC4' | - '\u1EC6' | - '\u1EC8' | - '\u1ECA' | - '\u1ECC' | - '\u1ECE' | - '\u1ED0' | - '\u1ED2' | - '\u1ED4' | - '\u1ED6' | - '\u1ED8' | - '\u1EDA' | - '\u1EDC' | - '\u1EDE' | - '\u1EE0' | - '\u1EE2' | - '\u1EE4' | - '\u1EE6' | - '\u1EE8' | - '\u1EEA' | - '\u1EEC' | - '\u1EEE' | - '\u1EF0' | - '\u1EF2' | - '\u1EF4' | - '\u1EF6' | - '\u1EF8' | - '\u1EFA' | - '\u1EFC' | - '\u1EFE' | - '\u1F08'..'\u1F0F' | - '\u1F18'..'\u1F1D' | - '\u1F28'..'\u1F2F' | - '\u1F38'..'\u1F3F' | - '\u1F48'..'\u1F4D' | - '\u1F59' | - '\u1F5B' | - '\u1F5D' | - '\u1F5F' | - '\u1F68'..'\u1F6F' | - '\u1FB8'..'\u1FBB' | - '\u1FC8'..'\u1FCB' | - '\u1FD8'..'\u1FDB' | - '\u1FE8'..'\u1FEC' | - '\u1FF8'..'\u1FFB' | - '\u2102' | - '\u2107' | - '\u210B'..'\u210D' | - '\u2110'..'\u2112' | - '\u2115' | - '\u2119'..'\u211D' | - '\u2124' | - '\u2126' | - '\u2128' | - '\u212A'..'\u212D' | - '\u2130'..'\u2133' | - '\u213E' | - '\u213F' | - '\u2145' | - '\u2183' | - '\u2C00'..'\u2C2E' | - '\u2C60' | - '\u2C62'..'\u2C64' | - '\u2C67' | - '\u2C69' | - '\u2C6B' | - '\u2C6D'..'\u2C70' | - '\u2C72' | - '\u2C75' | - '\u2C7E'..'\u2C80' | - '\u2C82' | - '\u2C84' | - '\u2C86' | - '\u2C88' | - '\u2C8A' | - '\u2C8C' | - '\u2C8E' | - '\u2C90' | - '\u2C92' | - '\u2C94' | - '\u2C96' | - '\u2C98' | - '\u2C9A' | - '\u2C9C' | - '\u2C9E' | - '\u2CA0' | - '\u2CA2' | - '\u2CA4' | - '\u2CA6' | - '\u2CA8' | - '\u2CAA' | - '\u2CAC' | - '\u2CAE' | - '\u2CB0' | - '\u2CB2' | - '\u2CB4' | - '\u2CB6' | - '\u2CB8' | - '\u2CBA' | - '\u2CBC' | - '\u2CBE' | - '\u2CC0' | - '\u2CC2' | - '\u2CC4' | - '\u2CC6' | - '\u2CC8' | - '\u2CCA' | - '\u2CCC' | - '\u2CCE' | - '\u2CD0' | - '\u2CD2' | - '\u2CD4' | - '\u2CD6' | - '\u2CD8' | - '\u2CDA' | - '\u2CDC' | - '\u2CDE' | - '\u2CE0' | - '\u2CE2' | - '\u2CEB' | - '\u2CED' | - '\u2CF2' | - '\uA640' | - '\uA642' | - '\uA644' | - '\uA646' | - '\uA648' | - '\uA64A' | - '\uA64C' | - '\uA64E' | - '\uA650' | - '\uA652' | - '\uA654' | - '\uA656' | - '\uA658' | - '\uA65A' | - '\uA65C' | - '\uA65E' | - '\uA660' | - '\uA662' | - '\uA664' | - '\uA666' | - '\uA668' | - '\uA66A' | - '\uA66C' | - '\uA680' | - '\uA682' | - '\uA684' | - '\uA686' | - '\uA688' | - '\uA68A' | - '\uA68C' | - '\uA68E' | - '\uA690' | - '\uA692' | - '\uA694' | - '\uA696' | - '\uA722' | - '\uA724' | - '\uA726' | - '\uA728' | - '\uA72A' | - '\uA72C' | - '\uA72E' | - '\uA732' | - '\uA734' | - '\uA736' | - '\uA738' | - '\uA73A' | - '\uA73C' | - '\uA73E' | - '\uA740' | - '\uA742' | - '\uA744' | - '\uA746' | - '\uA748' | - '\uA74A' | - '\uA74C' | - '\uA74E' | - '\uA750' | - '\uA752' | - '\uA754' | - '\uA756' | - '\uA758' | - '\uA75A' | - '\uA75C' | - '\uA75E' | - '\uA760' | - '\uA762' | - '\uA764' | - '\uA766' | - '\uA768' | - '\uA76A' | - '\uA76C' | - '\uA76E' | - '\uA779' | - '\uA77B' | - '\uA77D' | - '\uA77E' | - '\uA780' | - '\uA782' | - '\uA784' | - '\uA786' | - '\uA78B' | - '\uA78D' | - '\uA790' | - '\uA792' | - '\uA7A0' | - '\uA7A2' | - '\uA7A4' | - '\uA7A6' | - '\uA7A8' | - '\uA7AA' | - '\uFF21'..'\uFF3A'; - -UNICODE_CLASS_ND: - '\u0030'..'\u0039' | - '\u0660'..'\u0669' | - '\u06F0'..'\u06F9' | - '\u07C0'..'\u07C9' | - '\u0966'..'\u096F' | - '\u09E6'..'\u09EF' | - '\u0A66'..'\u0A6F' | - '\u0AE6'..'\u0AEF' | - '\u0B66'..'\u0B6F' | - '\u0BE6'..'\u0BEF' | - '\u0C66'..'\u0C6F' | - '\u0CE6'..'\u0CEF' | - '\u0D66'..'\u0D6F' | - '\u0E50'..'\u0E59' | - '\u0ED0'..'\u0ED9' | - '\u0F20'..'\u0F29' | - '\u1040'..'\u1049' | - '\u1090'..'\u1099' | - '\u17E0'..'\u17E9' | - '\u1810'..'\u1819' | - '\u1946'..'\u194F' | - '\u19D0'..'\u19D9' | - '\u1A80'..'\u1A89' | - '\u1A90'..'\u1A99' | - '\u1B50'..'\u1B59' | - '\u1BB0'..'\u1BB9' | - '\u1C40'..'\u1C49' | - '\u1C50'..'\u1C59' | - '\uA620'..'\uA629' | - '\uA8D0'..'\uA8D9' | - '\uA900'..'\uA909' | - '\uA9D0'..'\uA9D9' | - '\uAA50'..'\uAA59' | - '\uABF0'..'\uABF9' | - '\uFF10'..'\uFF19'; - -UNICODE_CLASS_NL: - '\u16EE'..'\u16F0' | - '\u2160'..'\u2182' | - '\u2185'..'\u2188' | - '\u3007' | - '\u3021'..'\u3029' | - '\u3038'..'\u303A' | - '\uA6E6'..'\uA6EF'; \ No newline at end of file diff --git a/orx-jvm/orx-kotlin-parser/src/main/kotlin/ExtractProgram.kt b/orx-jvm/orx-kotlin-parser/src/main/kotlin/ExtractProgram.kt deleted file mode 100644 index e0d172f7..00000000 --- a/orx-jvm/orx-kotlin-parser/src/main/kotlin/ExtractProgram.kt +++ /dev/null @@ -1,77 +0,0 @@ -package org.openrndr.extra.kotlinparser - -import KotlinLexer -import KotlinParser -import KotlinParserBaseListener -import org.antlr.v4.runtime.CharStreams -import org.antlr.v4.runtime.CommonTokenStream -import org.antlr.v4.runtime.ParserRuleContext -import org.antlr.v4.runtime.RuleContext -import org.antlr.v4.runtime.misc.Interval -import org.antlr.v4.runtime.tree.ParseTreeWalker - - -fun ParserRuleContext.verbatimText(marginLeft: Int = 0, marginRight: Int = 0): String { - if (start == null || stop == null) { - return "" - } - - val startIndex = start.startIndex + marginLeft - val stopIndex = stop.stopIndex - marginRight - val interval = Interval(startIndex, stopIndex) - return start.inputStream.getText(interval) -} - -class PackageExtractor() : KotlinParserBaseListener() { - var result: String? = null - override fun enterPackageHeader(ctx: KotlinParser.PackageHeaderContext) { - result = ctx.verbatimText() - } -} - -class ImportsExtractor(val ruleNames: List) : KotlinParserBaseListener() { - var result: String? = null - - override fun enterImportList(ctx: KotlinParser.ImportListContext) { - result = ctx.verbatimText() - } -} - -class LambdaExtractor(val ruleNames: List, val lambdaName: String) : KotlinParserBaseListener() { - fun RuleContext.named(): String { - return ruleNames[this.ruleIndex] - } - - var result: String? = null - override fun enterAnnotatedLambda(ctx: KotlinParser.AnnotatedLambdaContext?) { - val puec = ctx?.parent?.parent?.parent as? KotlinParser.PostfixUnaryExpressionContext - if (puec != null) { - val identifier = puec.primaryExpression()?.simpleIdentifier()?.Identifier()?.text - if (identifier == lambdaName) { - if (result == null) { - result = ctx.verbatimText(1, 1) - } - } - } - } -} - -class ProgramSource(val packageName: String?, val imports: String, val programLambda: String) - -fun extractProgram(source: String, programIdentifier: String = "program"): ProgramSource { - val parser = KotlinParser(CommonTokenStream(KotlinLexer(CharStreams.fromString(source)))) - val root = parser.kotlinFile() -// val rules = parser.ruleNames.toList() -// val pt = TreeUtils.toPrettyTree(root, rules) - val ruleNames = parser.ruleNames.toList() - - val packageExtractor = PackageExtractor() - ParseTreeWalker.DEFAULT.walk(packageExtractor, root) - - val importsExtractor = ImportsExtractor(ruleNames) - ParseTreeWalker.DEFAULT.walk(importsExtractor, root) - - val lambdaExtractor = LambdaExtractor(ruleNames, programIdentifier) - ParseTreeWalker.DEFAULT.walk(lambdaExtractor, root) - return ProgramSource(packageExtractor.result, importsExtractor.result ?: "", lambdaExtractor.result ?: "") -} diff --git a/orx-jvm/orx-kotlin-parser/src/main/kotlin/TreeUtils.kt b/orx-jvm/orx-kotlin-parser/src/main/kotlin/TreeUtils.kt deleted file mode 100644 index 095bef48..00000000 --- a/orx-jvm/orx-kotlin-parser/src/main/kotlin/TreeUtils.kt +++ /dev/null @@ -1,49 +0,0 @@ -package org.openrndr.extra.kotlinparser - -import org.antlr.v4.runtime.misc.Utils -import org.antlr.v4.runtime.tree.Tree -import org.antlr.v4.runtime.tree.Trees - -object TreeUtils { - /** Platform dependent end-of-line marker */ - val Eol = System.lineSeparator() - - /** The literal indent char(s) used for pretty-printing */ - const val Indents = " " - private var level = 0 - - /** - * Pretty print out a whole tree. [.getNodeText] is used on the node payloads to get the text - * for the nodes. (Derived from Trees.toStringTree(....)) - */ - fun toPrettyTree(t: Tree, ruleNames: List): String { - level = 0 - return process(t, ruleNames).replace("(?m)^\\s+$".toRegex(), "").replace("\\r?\\n\\r?\\n".toRegex(), Eol) - } - - private fun process(t: Tree, ruleNames: List): String { - if (t.getChildCount() == 0) return Utils.escapeWhitespace(Trees.getNodeText(t, ruleNames), false) - val sb = StringBuilder() - sb.append(lead(level)) - level++ - val s: String = Utils.escapeWhitespace(Trees.getNodeText(t, ruleNames), false) - sb.append("$s ") - for (i in 0 until t.getChildCount()) { - sb.append(process(t.getChild(i), ruleNames)) - } - level-- - sb.append(lead(level)) - return sb.toString() - } - - private fun lead(level: Int): String { - val sb = StringBuilder() - if (level > 0) { - sb.append(Eol) - for (cnt in 0 until level) { - sb.append(Indents) - } - } - return sb.toString() - } -} diff --git a/orx-jvm/orx-midi/README.md b/orx-jvm/orx-midi/README.md deleted file mode 100644 index def1a44b..00000000 --- a/orx-jvm/orx-midi/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# orx-midi - -MIDI support for keyboards and controllers. Send and receive note and control change events. -Bind inputs to variables. - -Orx-midi is a wrapper around `javax.midi`. - -## Usage - -```kotlin - -// -- list all midi devices -listMidiDevices().forEach { - println("${it.name}, ${it.vendor} r:${it.receive} t:${it.transmit}") -} - -// -- open a midi controller and listen for control changes -val dev = openMidiDevice("BCR2000 [hw:2,0,0]") -dev.controlChanged.listen { - println("${it.channel} ${it.control} ${it.value}") -} - -// or program changes -dev.programChange.listen { - println("${it.channel} ${it.program}") -} -``` - -## Further reading - -The OPENRNDR guide has a [section on orx-midi](https://guide.openrndr.org/#/10_OPENRNDR_Extras/C04_Midi_controllers) that provides step-by-step documentation for using orx-midi in combination with OPENRNDR. diff --git a/orx-jvm/orx-midi/build.gradle.kts b/orx-jvm/orx-midi/build.gradle.kts deleted file mode 100644 index 5f2f811f..00000000 --- a/orx-jvm/orx-midi/build.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(sharedLibs.kotlin.reflect) - implementation(sharedLibs.kotlin.coroutines) - implementation(project(":orx-property-watchers")) - implementation(project(":orx-parameters")) - - testImplementation(libs.mockk) - testImplementation(sharedLibs.kotest.assertions) -} diff --git a/orx-jvm/orx-midi/src/demo/kotlin/DemoMidiBinding01.kt b/orx-jvm/orx-midi/src/demo/kotlin/DemoMidiBinding01.kt deleted file mode 100644 index deae31c1..00000000 --- a/orx-jvm/orx-midi/src/demo/kotlin/DemoMidiBinding01.kt +++ /dev/null @@ -1,39 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.midi.bindMidiControl -import org.openrndr.extra.midi.openMidiDevice -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.math.Vector2 - -/** - * Demonstration of two-way binding using [bindMidiControl] - */ -fun main() = application { - program { - val midi = openMidiDevice("MIDI2x2 [hw:3,0,0]") - val settings = object { - @DoubleParameter("radius", 0.0, 100.0) - var radius = 0.0 - - @DoubleParameter("x", -100.0, 100.0) - var x = 0.0 - - @DoubleParameter("y", -100.0, 100.0) - var y = 0.0 - - @ColorParameter("fill") - var color = ColorRGBa.WHITE - } - - bindMidiControl(settings::radius, midi, 0, 1) - bindMidiControl(settings::x, midi, 0, 2) - bindMidiControl(settings::y, midi, 0, 3) - bindMidiControl(settings::color, midi, 0, 4) - - extend { - drawer.fill = settings.color - drawer.circle(drawer.bounds.center + Vector2(settings.x, settings.y), settings.radius) - } - } -} diff --git a/orx-jvm/orx-midi/src/demo/kotlin/DemoMidiConsole01.kt b/orx-jvm/orx-midi/src/demo/kotlin/DemoMidiConsole01.kt deleted file mode 100644 index d80db6db..00000000 --- a/orx-jvm/orx-midi/src/demo/kotlin/DemoMidiConsole01.kt +++ /dev/null @@ -1,17 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.midi.MidiConsole -import org.openrndr.extra.midi.listMidiDevices -import org.openrndr.extra.midi.openMidiDevice - -/** - * Demonstration of [MidiConsole] - */ -fun main() = application { - program { - listMidiDevices().forEach { println(it.toString()) } - val midi = openMidiDevice("Launchpad [hw:4,0,0]") - extend(MidiConsole()) { - register(midi) - } - } -} diff --git a/orx-jvm/orx-midi/src/main/kotlin/MidiBindings.kt b/orx-jvm/orx-midi/src/main/kotlin/MidiBindings.kt deleted file mode 100644 index 1e7660dc..00000000 --- a/orx-jvm/orx-midi/src/main/kotlin/MidiBindings.kt +++ /dev/null @@ -1,381 +0,0 @@ -package org.openrndr.extra.midi - -import kotlinx.coroutines.yield -import org.openrndr.Program -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.Vector2Parameter -import org.openrndr.extra.parameters.Vector3Parameter -import org.openrndr.launch -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 -import org.openrndr.math.map - -import kotlin.reflect.KMutableProperty0 -import kotlin.reflect.full.findAnnotations - -fun bindMidiNote(on: () -> Unit, off: () -> Unit, transceiver: MidiTransceiver, channel: Int, note: Int) { - transceiver.noteOn.listen { - if ((channel == -1 || it.channel == channel) && it.note == note) { - on() - } - } - transceiver.noteOff.listen { - if ((channel == -1 || it.channel == channel) && it.note == note) { - off() - } - } -} - -/** - * Bind MIDI control change to [Double] property - * @param property the [KMutableProperty0] to bind to - * @param transceiver the midi device to bind to - * @param channel the midi channel to use - * @param control the midi control to use - * @since 0.4.3 - */ -@JvmName("bindMidiControlDouble") -fun Program.bindMidiControl( - property: KMutableProperty0, - transceiver: MidiTransceiver, - channel: Int, - control: Int -) { - val anno = property.findAnnotations(DoubleParameter::class).firstOrNull() - - val low = anno?.low ?: 0.0 - val high = anno?.high ?: 1.0 - transceiver.controlChanged.listen { - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channel && it.control == control) { - val value = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - property.set(value) - } - } - launch { - var propertyValue = property.get() - while (true) { - val candidateValue = property.get() - if (candidateValue != propertyValue) { - propertyValue = candidateValue - val value = propertyValue.map(low, high, 0.0, 127.0, clamp = true).toInt() - transceiver.controlChange(channel, control, value) - } - yield() - } - } -} - -/** - * Bind MIDI control change to [Boolean] property - * @param property the [KMutableProperty0] to bind to - * @param transceiver the midi device to bind to - * @param channel the midi channel to use - * @param control the midi control to use - * @since 0.4.3 - */ -@JvmName("bindMidiControlBoolean") -fun Program.bindMidiControl( - property: KMutableProperty0, - transceiver: MidiTransceiver, - channel: Int, - control: Int -) { - transceiver.controlChanged.listen { - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channel && it.control == control) { - property.set(it.value >= 64) - } - } - launch { - var propertyValue = property.get() - while (true) { - val candidateValue = property.get() - if (candidateValue != propertyValue) { - propertyValue = candidateValue - transceiver.controlChange(channel, control, if (propertyValue) 127 else 0) - } - yield() - } - } -} - -/** - * Bind MIDI control change to [Vector2] property - * @param property the [KMutableProperty0] to bind to - * @param transceiver the midi device to bind to - * @param channelX the midi channel to use for the [Vector2.x] component - * @param controlX the midi control to use for the [Vector2.x] component - * @param channelY the midi channel to use for the [Vector2.y] component - * @param controlY the midi control to use for the [Vector2.y] component - * @since 0.4.3 - */ -@JvmName("bindMidiControlVector2") -fun Program.bindMidiControl( - property: KMutableProperty0, transceiver: MidiTransceiver, - channelX: Int, controlX: Int, - channelY: Int = channelX, controlY: Int = controlX + 1 -) { - val anno = property.findAnnotations(Vector2Parameter::class).firstOrNull() - - val low = anno?.min ?: 0.0 - val high = anno?.max ?: 1.0 - transceiver.controlChanged.listen { - val v = property.get() - var x = v.x - var y = v.y - var changed = false - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelX && it.control == controlX) { - changed = true - x = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelY && it.control == controlY) { - changed = true - y = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (changed) { - val nv = Vector2(x, y) - property.set(nv) - } - } - launch { - var propertyValue = property.get() - while (true) { - val candidateValue = property.get() - if (candidateValue != propertyValue) { - propertyValue = candidateValue - val valueX = propertyValue.x.map(low, high, 0.0, 127.0, clamp = true).toInt() - val valueY = propertyValue.y.map(low, high, 0.0, 127.0, clamp = true).toInt() - transceiver.controlChange(channelX, controlX, valueX) - transceiver.controlChange(channelY, controlY, valueY) - } - yield() - } - } -} - -/** - * Bind MIDI control change to [Vector3] property - * @param property the [KMutableProperty0] to bind to - * @param transceiver the midi device to bind to - * @param channelX the midi channel to use for the [Vector3.x] component - * @param controlX the midi control to use for the [Vector3.x] component - * @param channelY the midi channel to use for the [Vector3.y] component - * @param controlY the midi control to use for the [Vector3.y] component - * @param channelZ the midi channel to use for the [Vector3.z] component - * @param controlZ the midi control to use for the [Vector3.z] component - * @since 0.4.3 - */ -@JvmName("bindMidiControlVector3") -fun Program.bindMidiControl( - property: KMutableProperty0, transceiver: MidiTransceiver, - channelX: Int, controlX: Int, - channelY: Int = channelX, controlY: Int = controlX + 1, - channelZ: Int = channelY, controlZ: Int = controlY + 1 -) { - val anno = property.findAnnotations(Vector3Parameter::class).firstOrNull() - - val low = anno?.min ?: 0.0 - val high = anno?.max ?: 1.0 - transceiver.controlChanged.listen { - val v = property.get() - var x = v.x - var y = v.y - var z = v.z - var changed = false - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelX && it.control == controlX) { - changed = true - x = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelY && it.control == controlY) { - changed = true - y = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelZ && it.control == controlZ) { - changed = true - z = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (changed) { - val nv = Vector3(x, y, z) - property.set(nv) - } - } - launch { - var propertyValue = property.get() - while (true) { - val candidateValue = property.get() - if (candidateValue != propertyValue) { - propertyValue = candidateValue - val valueX = propertyValue.x.map(low, high, 0.0, 127.0, clamp = true).toInt() - val valueY = propertyValue.y.map(low, high, 0.0, 127.0, clamp = true).toInt() - val valueZ = propertyValue.z.map(low, high, 0.0, 127.0, clamp = true).toInt() - transceiver.controlChange(channelX, controlX, valueX) - transceiver.controlChange(channelY, controlY, valueY) - transceiver.controlChange(channelZ, controlZ, valueZ) - } - yield() - } - } -} - -/** - * Bind MIDI control change to [ColorRGBa] property - * @param property the [KMutableProperty0] to bind to - * @param transceiver the midi device to bind to - * @param channelR the midi channel to use for the [ColorRGBa.r] component - * @param controlR the midi control to use for the [ColorRGBa.r] component - * @param channelG the midi channel to use for the [ColorRGBa.g] component - * @param controlG the midi control to use for the [ColorRGBa.g] component - * @param channelB the midi channel to use for the [ColorRGBa.b] component - * @param controlB the midi control to use for the [ColorRGBa.b] component - * @param channelA the midi channel to use for the [ColorRGBa.alpha] component - * @param controlA the midi control to use for the [ColorRGBa.alpha] component - * @since 0.4.3 - */ -@JvmName("bindMidiControlColorRGBa") -fun Program.bindMidiControl( - property: KMutableProperty0, transceiver: MidiTransceiver, - channelR: Int, controlR: Int, - channelG: Int = channelR, controlG: Int = controlR + 1, - channelB: Int = channelG, controlB: Int = controlG + 1, - channelA: Int = channelB, controlA: Int = controlB + 1, -) { - val low = 0.0 - val high = 1.0 - transceiver.controlChanged.listen { - val v = property.get() - var r = v.r - var g = v.g - var b = v.b - var a = v.alpha - var changed = false - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelR && it.control == controlR) { - changed = true - r = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelG && it.control == controlG) { - changed = true - g = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelB && it.control == controlB) { - changed = true - b = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelA && it.control == controlA) { - changed = true - a = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (changed) { - val nv = ColorRGBa(r, g, b, a) - property.set(nv) - } - } - launch { - var propertyValue = property.get() - while (true) { - val candidateValue = property.get() - if (candidateValue != propertyValue) { - propertyValue = candidateValue - val valueR = propertyValue.r.map(low, high, 0.0, 127.0, clamp = true).toInt() - val valueG = propertyValue.g.map(low, high, 0.0, 127.0, clamp = true).toInt() - val valueB = propertyValue.b.map(low, high, 0.0, 127.0, clamp = true).toInt() - val valueA = propertyValue.alpha.map(low, high, 0.0, 127.0, clamp = true).toInt() - transceiver.controlChange(channelR, controlR, valueR) - transceiver.controlChange(channelG, controlG, valueG) - transceiver.controlChange(channelB, controlB, valueB) - transceiver.controlChange(channelA, controlA, valueA) - } - yield() - } - } -} - - -/** - * Bind MIDI control change to [Vector4] property - * @param property the [KMutableProperty0] to bind to - * @param transceiver the midi device to bind to - * @param channelX the midi channel to use for the [Vector4.x] component - * @param controlX the midi control to use for the [Vector4.x] component - * @param channelY the midi channel to use for the [Vector4.y] component - * @param controlY the midi control to use for the [Vector4.y] component - * @param channelZ the midi channel to use for the [Vector4.z] component - * @param controlZ the midi control to use for the [Vector4.z] component - * @param channelW the midi channel to use for the [Vector4.w] component - * @param controlW the midi control to use for the [Vector4.w] component - * @since 0.4.3 - */ -@JvmName("bindMidiControlVector4") -fun Program.bindMidiControl( - property: KMutableProperty0, transceiver: MidiTransceiver, - channelX: Int, controlX: Int, - channelY: Int = channelX, controlY: Int = controlX + 1, - channelZ: Int = channelY, controlZ: Int = controlY + 1, - channelW: Int = channelZ, controlW: Int = controlZ + 1, -) { - val low = 0.0 - val high = 1.0 - transceiver.controlChanged.listen { - val v = property.get() - var x = v.x - var y = v.y - var z = v.z - var w = v.w - var changed = false - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelX && it.control == controlX) { - changed = true - x = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelY && it.control == controlY) { - changed = true - y = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelZ && it.control == controlZ) { - changed = true - z = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (it.eventType == MidiEventType.CONTROL_CHANGE && it.channel == channelW && it.control == controlW) { - changed = true - w = it.value.toDouble().map(0.0, 127.0, low, high, clamp = true) - } - - if (changed) { - val nv = Vector4(x, y, z, w) - property.set(nv) - } - } - launch { - var propertyValue = property.get() - while (true) { - val candidateValue = property.get() - if (candidateValue != propertyValue) { - propertyValue = candidateValue - val valueR = propertyValue.x.map(low, high, 0.0, 127.0, clamp = true).toInt() - val valueG = propertyValue.y.map(low, high, 0.0, 127.0, clamp = true).toInt() - val valueB = propertyValue.z.map(low, high, 0.0, 127.0, clamp = true).toInt() - val valueA = propertyValue.w.map(low, high, 0.0, 127.0, clamp = true).toInt() - transceiver.controlChange(channelX, controlX, valueR) - transceiver.controlChange(channelY, controlY, valueG) - transceiver.controlChange(channelZ, controlZ, valueB) - transceiver.controlChange(channelW, controlW, valueA) - } - yield() - } - } -} diff --git a/orx-jvm/orx-midi/src/main/kotlin/MidiConsole.kt b/orx-jvm/orx-midi/src/main/kotlin/MidiConsole.kt deleted file mode 100644 index 6e3a535b..00000000 --- a/orx-jvm/orx-midi/src/main/kotlin/MidiConsole.kt +++ /dev/null @@ -1,77 +0,0 @@ -package org.openrndr.extra.midi - -import org.openrndr.Extension -import org.openrndr.Program -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Drawer -import org.openrndr.draw.loadFontImageMap -import org.openrndr.math.Vector2 -import org.openrndr.shape.Rectangle -import java.io.File - -/** - * A console for monitoring MIDI events - */ -class MidiConsole : Extension { - override var enabled = true - - /** - * placement of the console text - */ - var box = Rectangle(0.0, 0.0, 130.0, 200.0) - - private val messages = mutableListOf() - - /** - * number of entries in the visible history - */ - var historySize = 2 - - private val demoFont = File("demo-data/fonts/IBMPlexMono-Regular.ttf").exists() - - /** - * register a Midi device for monitoring - */ - fun register(transceiver: MidiTransceiver) { - transceiver.controlChanged.listen { - synchronized(messages) { - messages.add("Ch=${it.channel} CC=${it.control}: ${it.value}") - if (messages.size > historySize) { - messages.removeAt(0) - } - } - } - transceiver.noteOn.listen { - synchronized(messages) { - messages.add("NOTE ON ${it.note}: ${it.velocity}") - if (messages.size > historySize) { - messages.removeAt(0) - } - } - } - - transceiver.noteOff.listen { - synchronized(messages) { - messages.add("NOTE OFF ${it.note}") - if (messages.size > historySize) { - messages.removeAt(0) - } - } - } - } - - override fun afterDraw(drawer: Drawer, program: Program) { - drawer.defaults() - synchronized(messages) { - box = Rectangle(drawer.width - box.width, 0.0, box.width, drawer.height * 1.0) - val positions = List(messages.size) { index -> - Vector2(box.x, box.y + index * 16.0 + 16.0) - } - if (demoFont) { - drawer.fontMap = loadFontImageMap("demo-data/fonts/IBMPlexMono-Regular.ttf", 16.0) - } - drawer.fill = ColorRGBa.WHITE - drawer.texts(messages, positions) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-midi/src/main/kotlin/MidiEvent.kt b/orx-jvm/orx-midi/src/main/kotlin/MidiEvent.kt deleted file mode 100644 index 4f16a73f..00000000 --- a/orx-jvm/orx-midi/src/main/kotlin/MidiEvent.kt +++ /dev/null @@ -1,120 +0,0 @@ -package org.openrndr.extra.midi - -import javax.sound.midi.MidiMessage -import javax.sound.midi.ShortMessage - -enum class MidiEventType(val status: Int) { - - MIDI_TIME_CODE(ShortMessage.MIDI_TIME_CODE), - SONG_POSITION_POINTER(ShortMessage.SONG_POSITION_POINTER), - SONG_SELECT(ShortMessage.SONG_SELECT), - TUNE_REQUEST(ShortMessage.TUNE_REQUEST), - END_OF_EXCLUSIVE(ShortMessage.END_OF_EXCLUSIVE), - TIMING_CLOCK(ShortMessage.TIMING_CLOCK), - START(ShortMessage.START), - CONTINUE(ShortMessage.CONTINUE), - STOP(ShortMessage.STOP), - ACTIVE_SENSING(ShortMessage.ACTIVE_SENSING), - SYSTEM_RESET(ShortMessage.SYSTEM_RESET), - NOTE_ON(ShortMessage.NOTE_ON), - NOTE_OFF(ShortMessage.NOTE_OFF), - CONTROL_CHANGE(ShortMessage.CONTROL_CHANGE), - PROGRAM_CHANGE(ShortMessage.PROGRAM_CHANGE), - CHANNEL_PRESSURE(ShortMessage.CHANNEL_PRESSURE), - PITCH_BEND(ShortMessage.PITCH_BEND); - - companion object { - - private val statusMap: Map = - entries.associateBy { it.status } - - fun fromStatus( - status: Int - ): MidiEventType = requireNotNull( - statusMap[if (status >= 0xf0) status else status and 0xf0] - ) { - "Invalid MIDI status: $status" - } - - } - -} - -val MidiMessage.eventType: MidiEventType get() = MidiEventType.fromStatus(status) - -class MidiEvent(val eventType: MidiEventType) { - var origin = Origin.DEVICE - var control: Int = 0 - var program: Int = 0 - var note: Int = 0 - var channel: Int = 0 - var pitchBend: Int = 0 - var pressure: Int = 0 - var value: Int = 0 - var velocity: Int = 0 - - enum class Origin { - DEVICE, - USER - } - - companion object { - fun noteOn(channel: Int, note: Int, velocity: Int): MidiEvent { - val midiEvent = MidiEvent(MidiEventType.NOTE_ON) - midiEvent.velocity = velocity - midiEvent.note = note - midiEvent.channel = channel - return midiEvent - } - - fun noteOff(channel: Int, note: Int, velocity: Int): MidiEvent { - val midiEvent = MidiEvent(MidiEventType.NOTE_OFF) - midiEvent.note = note - midiEvent.channel = channel - midiEvent.velocity = velocity - return midiEvent - } - - fun controlChange(channel: Int, control: Int, value: Int): MidiEvent { - val midiEvent = MidiEvent(MidiEventType.CONTROL_CHANGE) - midiEvent.channel = channel - midiEvent.control = control - midiEvent.value = value - return midiEvent - } - - fun programChange(channel: Int, program: Int): MidiEvent { - val midiEvent = MidiEvent(MidiEventType.PROGRAM_CHANGE) - midiEvent.channel = channel - midiEvent.program = program - return midiEvent - } - - fun channelPressure(channel: Int, pressure: Int): MidiEvent { - val midiEvent = MidiEvent(MidiEventType.CHANNEL_PRESSURE) - midiEvent.channel = channel - midiEvent.pressure = pressure - return midiEvent - } - - fun pitchBend(channel: Int, pitchBend: Int): MidiEvent { - val midiEvent = MidiEvent(MidiEventType.PITCH_BEND) - midiEvent.channel = channel - midiEvent.pitchBend = pitchBend - return midiEvent - } - } - - override fun toString(): String { - return "MidiEvent(eventType=$eventType, " + - "origin=$origin, " + - "program=$program, " + - "control=$control, " + - "note=$note, " + - "channel=$channel, " + - "pitchBend=$pitchBend, " + - "pressure=$pressure, " + - "value=$value, " + - "velocity=$velocity)" - } -} \ No newline at end of file diff --git a/orx-jvm/orx-midi/src/main/kotlin/MidiTransceiver.kt b/orx-jvm/orx-midi/src/main/kotlin/MidiTransceiver.kt deleted file mode 100644 index 386daa63..00000000 --- a/orx-jvm/orx-midi/src/main/kotlin/MidiTransceiver.kt +++ /dev/null @@ -1,305 +0,0 @@ -package org.openrndr.extra.midi - -import io.github.oshai.kotlinlogging.KotlinLogging -import org.openrndr.Program -import org.openrndr.events.Event -import javax.sound.midi.* - -private val logger = KotlinLogging.logger { } - -@JvmRecord -data class MidiDeviceName(val name: String, val vendor: String) - -class MidiDeviceCapabilities { - var receive: Boolean = false - var transmit: Boolean = false - - override fun toString(): String { - return "MidiDeviceCapabilities(receive=$receive, transmit=$transmit)" - } -} - -@JvmRecord -data class MidiDeviceDescription( - val name: String, - val vendor: String, - val receive: Boolean, - val transmit: Boolean -) { - companion object { - fun list(): List { - val caps = mutableMapOf() - - val infos = MidiSystem.getMidiDeviceInfo() - for (info in infos) { - val device = MidiSystem.getMidiDevice(info) - val name = MidiDeviceName(info.name, info.vendor) - val deviceCaps = - caps.getOrPut(name) { MidiDeviceCapabilities() } - - if (device !is Sequencer && device !is Synthesizer) { - if (device.maxReceivers != 0 && device.maxTransmitters == 0) { - deviceCaps.receive = true - } - if (device.maxTransmitters != 0 && device.maxReceivers == 0) { - deviceCaps.transmit = true - } - } - } - return caps.map { - MidiDeviceDescription( - it.key.name, - it.key.vendor, - it.value.receive, - it.value.transmit - ) - } - } - } - - fun open(program: Program): MidiTransceiver { - require(receive && transmit) { - "MIDI device should be a receiver and transmitter" - } - - return MidiTransceiver.fromDeviceVendor(program, name, vendor) - } -} - -class MidiTransceiver(program: Program, val receiverDevice: MidiDevice?, val transmitterDevicer: MidiDevice?) { - companion object { - fun fromDeviceVendor(program: Program, name: String, vendor: String? = null): MidiTransceiver { - val infos = MidiSystem.getMidiDeviceInfo() - - var receiverDevice: MidiDevice? = null - var transmitterDevice: MidiDevice? = null - - for (info in infos) { - try { - val device = MidiSystem.getMidiDevice(info) - if (device !is Sequencer && device !is Synthesizer) { - if ((vendor == null || info.vendor == vendor) && info.name == name) { - logger.info { "found matching MIDI device $name / $vendor" } - if (device.maxTransmitters != 0 && device.maxReceivers == 0) { - transmitterDevice = device - logger.debug { - "found MIDI transmitter" - } - } - if (device.maxReceivers != 0 && device.maxTransmitters == 0) { - receiverDevice = device - logger.debug { - "found MIDI receiver" - } - } - } - } - } catch (e: MidiUnavailableException) { - error("no MIDI available") - } - } - - if (receiverDevice != null && transmitterDevice != null) { - receiverDevice.open() - transmitterDevice.open() - return MidiTransceiver(program, receiverDevice, transmitterDevice) - } else { - error("MIDI device not found ${name}:${vendor} $receiverDevice $transmitterDevice") - } - } - } - - private val receiver = receiverDevice?.receiver - private val transmitter = transmitterDevicer?.transmitter - - private inner class Destroyer : Thread() { - override fun run() { - destroy() - } - } - - private fun trigger(message: MidiMessage) { - val cmd = message.message - val channel = (cmd[0].toInt() and 0xff) and 0x0f - when (val eventType = message.eventType) { - - MidiEventType.NOTE_ON -> { - val key = cmd[1].toInt() and 0xff - val velocity = cmd[2].toInt() and 0xff - if (velocity > 0) { - noteOn.trigger(MidiEvent.noteOn(channel, key, velocity)) - } else { - noteOff.trigger(MidiEvent.noteOff(channel, key, velocity)) - } - } - - MidiEventType.NOTE_OFF -> noteOff.trigger( - MidiEvent.noteOff( - channel, - cmd[1].toInt() and 0xff, - cmd[2].toInt() and 0xff - ) - ) - - MidiEventType.CONTROL_CHANGE -> controlChanged.trigger( - MidiEvent.controlChange( - channel, - cmd[1].toInt() and 0xff, - cmd[2].toInt() and 0xff - ) - ) - - MidiEventType.PROGRAM_CHANGE -> programChanged.trigger( - MidiEvent.programChange( - channel, - cmd[1].toInt() and 0xff - ) - ) - - MidiEventType.CHANNEL_PRESSURE -> channelPressure.trigger( - MidiEvent.channelPressure( - channel, - cmd[1].toInt() and 0xff - ) - ) - - // https://sites.uci.edu/camp2014/2014/04/30/managing-midi-pitchbend-messages/ - // The next operation to combine two 7bit values - // was verified to give the same results as the Linux - // `midisnoop` program while using an `Alesis Vortex - // Wireless 2` device. This MIDI device does not provide a - // full range 14 bit pitch-bend resolution though, so - // a different device is needed to confirm the pitch bend - // values slide as expected from -8192 to +8191. - MidiEventType.PITCH_BEND -> pitchBend.trigger( - MidiEvent.pitchBend( - channel, - (cmd[2].toInt() shl 25 shr 18) + cmd[1].toInt() - ) - ) - - else -> { - logger.trace { "Unsupported MIDI event type: $eventType" } - } - - } - } - - init { - transmitter?.receiver = object : MidiDeviceReceiver { - override fun getMidiDevice(): MidiDevice? { - return null - } - override fun send(message: MidiMessage, timeStamp: Long) { - trigger(message) - } - override fun close() { - } - } - - val destroyer = Destroyer() - program.ended.listen { - destroyer.start() - } - - } - - val controlChanged = Event("midi-transceiver::controller-changed") - val programChanged = Event("midi-transceiver::program-changed") - val noteOn = Event("midi-transceiver::note-on") - val noteOff = Event("midi-transceiver::note-off") - val channelPressure = Event("midi-transceiver::channel-pressure") - val pitchBend = Event("midi-transceiver::pitch-bend") - - fun controlChange(channel: Int, control: Int, value: Int) { - send { ShortMessage(ShortMessage.CONTROL_CHANGE, channel, control, value) } - } - - fun programChange(channel: Int, program: Int) { - send { ShortMessage(ShortMessage.PROGRAM_CHANGE, channel, program) } - } - - fun noteOn(channel: Int, key: Int, velocity: Int) { - send { ShortMessage(ShortMessage.NOTE_ON, channel, key, velocity) } - } - - fun noteOff(channel: Int, key: Int, velocity: Int) { - send { ShortMessage(ShortMessage.NOTE_OFF, channel, key, velocity) } - } - - fun channelPressure(channel: Int, value: Int) { - send { ShortMessage(ShortMessage.CHANNEL_PRESSURE, channel, value) } - } - - fun pitchBend(channel: Int, value: Int) { - send { ShortMessage(ShortMessage.PITCH_BEND, channel, value) } - } - - fun destroy() { - receiverDevice?.close() - transmitterDevicer?.close() - } - - private fun send(block: () -> MidiMessage) { - if (receiver != null && receiverDevice != null) { - try { - val msg = block() - receiver.send(msg, receiverDevice.microsecondPosition) - } catch (e: InvalidMidiDataException) { - logger.warn { e.message } - } - } - } - -} - -/** - * List all available MIDI devices - * @since 0.4.3 - */ -fun listMidiDevices() = MidiDeviceDescription.list() - -/** - * Open a MIDI device by name - * @param name the name of the MIDI device to open. Either the - * exact name or the first characters of the name. - * Throws an exception if the device name is not found. - * @since 0.4.3 - */ -fun Program.openMidiDevice(name: String) = - openMidiDeviceOrNull(name) ?: error("MIDI device not found for query '$name'") - -/** - * Open a MIDI device by name - * - * @param name the name of the MIDI device to open. Either the - * exact name or the first characters of the name. - * Returns null if the device name is not found. - * @since 0.4.3 - */ -fun Program.openMidiDeviceOrNull(name: String): MidiTransceiver? { - val devices = listMidiDevices() - - val matchingDevice = devices.firstOrNull { - // Existing device name matches `name` - it.name == name - } ?: devices.firstOrNull { - // Existing device name starts with `name` - it.name.startsWith(name) - } - - return if(matchingDevice != null) - MidiTransceiver.fromDeviceVendor(this, matchingDevice.name) - else - null -} - -/** - * Open a dummy MIDI device - * - * Enables running programs that depend on a specific MIDI device - * when that device is not available. - * Usage: `val dev = openMidiDeviceOrNull("Twister") ?: dummyMidiDevice()` - * @since 0.4.3 - */ -fun Program.dummyMidiDevice() = MidiTransceiver(this, null, null) diff --git a/orx-jvm/orx-midi/src/test/kotlin/MidiTests.kt b/orx-jvm/orx-midi/src/test/kotlin/MidiTests.kt deleted file mode 100644 index 2ba0ea6c..00000000 --- a/orx-jvm/orx-midi/src/test/kotlin/MidiTests.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.openrndr.extra.midi - -import javax.sound.midi.Receiver -import javax.sound.midi.Transmitter - -class TestTransmitter : Transmitter { - - private var receiver: Receiver? = null - - override fun setReceiver(receiver: Receiver?) { - this.receiver = receiver - } - - override fun getReceiver(): Receiver? = receiver - - override fun close() { - receiver?.close() - } - -} \ No newline at end of file diff --git a/orx-jvm/orx-midi/src/test/kotlin/MidiTransceiverTest.kt b/orx-jvm/orx-midi/src/test/kotlin/MidiTransceiverTest.kt deleted file mode 100644 index 92473d4e..00000000 --- a/orx-jvm/orx-midi/src/test/kotlin/MidiTransceiverTest.kt +++ /dev/null @@ -1,118 +0,0 @@ -package org.openrndr.extra.midi - -import io.kotest.matchers.should -import io.kotest.matchers.shouldBe -import io.kotest.matchers.types.beInstanceOf -import io.mockk.* -import org.openrndr.Program -import java.util.concurrent.atomic.AtomicReference -import javax.sound.midi.MidiDevice -import javax.sound.midi.MidiMessage -import javax.sound.midi.Receiver -import javax.sound.midi.ShortMessage -import kotlin.test.Test - -@Suppress("MemberVisibilityCanBePrivate") -class MidiTransceiverTest { - - // given - val program = mockk(relaxed = true) - val receiver = mockk() - val receiverDevice = mockk(relaxed = true) - val messageSlot = slot() - - val transmitter = TestTransmitter() - val transmitterDevice = mockk() - - init { - every { receiverDevice.receiver } returns receiver - every { receiver.send(capture(messageSlot), any()) } just runs - every { transmitterDevice.transmitter } returns transmitter - } - - val transceiver = MidiTransceiver( - program, - receiverDevice, - transmitterDevice - ) - - @Test - fun `should send out NOTE_ON message`() { - // when - transceiver.noteOn(5, 10, 100) - - // then - messageSlot.captured should beInstanceOf() - (messageSlot.captured as ShortMessage).apply { - command shouldBe ShortMessage.NOTE_ON - channel shouldBe 5 - data1 shouldBe 10 - data2 shouldBe 100 - } - - } - - @Test - fun `should send out NOTE_OFF message`() { - // when - transceiver.noteOff(1, 10, 62) - - // then - messageSlot.captured should beInstanceOf() - (messageSlot.captured as ShortMessage).apply { - command shouldBe ShortMessage.NOTE_OFF - channel shouldBe 1 - data1 shouldBe 10 - data2 shouldBe 62 - } - } - - @Test - fun `should receive NOTE_ON event on receiving NOTE_ON message`() { - // given - val eventSlot = AtomicReference() - transceiver.noteOn.listen { - eventSlot.set(it) - } - - // when - transmitter.receiver!!.send( - ShortMessage(ShortMessage.NOTE_ON, 1, 2, 3), 1042 - ) - val noteOnEvent = eventSlot.get() - - // then - noteOnEvent.apply { - eventType shouldBe MidiEventType.NOTE_ON - origin shouldBe MidiEvent.Origin.DEVICE - channel shouldBe 1 - note shouldBe 2 - velocity shouldBe 3 - } - } - - @Test - fun `should receive NOTE_OFF event on receiving NOTE_ON message with velocity 0`() { - // given - val eventSlot = AtomicReference() - transceiver.noteOff.listen { - eventSlot.set(it) - } - - // when - transmitter.receiver!!.send( - ShortMessage(ShortMessage.NOTE_ON, 2, 3, 0), 1042 - ) - val noteOnEvent = eventSlot.get() - - // then - noteOnEvent.apply { - eventType shouldBe MidiEventType.NOTE_OFF - origin shouldBe MidiEvent.Origin.DEVICE - channel shouldBe 2 - note shouldBe 3 - velocity shouldBe 0 - } - } - -} diff --git a/orx-jvm/orx-minim/README.md b/orx-jvm/orx-minim/README.md deleted file mode 100644 index f020a11a..00000000 --- a/orx-jvm/orx-minim/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# orx-minim - -Simplifies working with the Minim sound library. -Provides sound synthesis and analysis. - -## Usage - -Add `orx-minim` to `orxFeatures` - -Check the examples: - -* [FFT visualization of live audio input](src/demo/kotlin/DemoFFT01.kt) -* [Additive synthesizer](src/demo/kotlin/DemoAdditive01.kt) - -## Further reading - - * [Minim website](https://code.compartmental.net/tools/minim/) - * [Minim's UGens](https://code.compartmental.net/minim/index_ugens.html) - * [Minim Github](https://github.com/ddf/Minim) - * [Minim Processing examples](https://github.com/ddf/Minim/tree/master/examples) \ No newline at end of file diff --git a/orx-jvm/orx-minim/build.gradle.kts b/orx-jvm/orx-minim/build.gradle.kts deleted file mode 100644 index 9950a88c..00000000 --- a/orx-jvm/orx-minim/build.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - api(project(":orx-parameters")) - api(project(":orx-jvm:orx-panel")) - api(libs.minim) { - exclude(group = "org.apache.maven.plugins", module = "maven-javadoc-plugin") - } - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(sharedLibs.kotlin.reflect) - demoRuntimeOnly(sharedLibs.slf4j.simple) -} \ No newline at end of file diff --git a/orx-jvm/orx-minim/src/demo/kotlin/DemoAdditive01.kt b/orx-jvm/orx-minim/src/demo/kotlin/DemoAdditive01.kt deleted file mode 100644 index c98bdee8..00000000 --- a/orx-jvm/orx-minim/src/demo/kotlin/DemoAdditive01.kt +++ /dev/null @@ -1,108 +0,0 @@ -import ddf.minim.ugens.Oscil -import ddf.minim.ugens.Pan -import org.openrndr.MouseTracker -import org.openrndr.application -import org.openrndr.color.rgb -import org.openrndr.extra.minim.minim -import org.openrndr.math.Polar -import kotlin.math.pow -import kotlin.random.Random - -/** - * Random drone generator and visualizer with 20 stereo voices. - * Hold the mouse button to randomize the frequencies. - * Press keys 'a' or 'b' for less random frequencies. - */ -fun main() = application { - program { - val minim = minim() - val out = minim.lineOut - - if (out == null) { - application.exit() - } - - // generates a random frequency value biased down - fun randomFreq() = 20f + Random.nextFloat().pow(3) * 1000 - - // If one didn't want to visualize or control the synths we - // wouldn't need a data structure to store them. Here we store - // Pairs, so we have access both to the frequency of the wave - // and the current amplitude defined by the lfo (low frequency - // oscillator). - val synths = List(20) { - // By default, Oscil creates sine waves, but it can be changed. - val lfo = Oscil( - Random.nextFloat() * 0.1f + 0.005f, - 0.05f - ).apply { - // Here we set the center of the lfo to 0.05f. - // Since the amplitude is also 0.05f, it moves between - // 0.00f and 0.10f. - offset.lastValue = 0.05f - - // Have the sine waves to not start in sync. - //phase.lastValue = Random.nextFloat() * 6.28f - } - val wave = Oscil(randomFreq(), 0f) - // The `lfo` Oscil controls the `wave` Oscil's amplitude. - lfo.patch(wave.amplitude) - // Random pan to avoid a mono sound. - val pan = Pan(Random.nextFloat() * 2 - 1) - wave.patch(pan) - pan.patch(out) - // Store a [Pair] in `synths`. - Pair(wave, lfo) - } - val bgColor = rgb(0.094, 0.188, 0.349) - val lineColor = rgb(0.992, 0.918, 0.671) - val mouseTracker = MouseTracker(mouse) - - extend { - drawer.clear(bgColor) - drawer.translate(drawer.bounds.center) - drawer.rotate(seconds) - // A CircleBatchBuilder for faster drawing of circles. - drawer.circles { - // For each synth draw a circle. - synths.forEachIndexed { i, (wave, lfo) -> - stroke = lineColor.opacify(Random.nextDouble(0.4) + 0.6) - fill = lineColor.opacify(Random.nextDouble() * 0.04) - // A Polar arrangement centered on the screen. - // Higher pitch circles are farther away from the center. - val pos = Polar( - 360.0 * i / synths.size, - 50.0 + wave.frequency.lastValue * 0.2 - ).cartesian - // The size of the circle depends on the current volume - // set by the lfo. - circle(pos, 500 * lfo.lastValues.last().toDouble()) - } - } - if (mouseTracker.pressedButtons.isNotEmpty()) { - synths.random().first.setFrequency(randomFreq()) - } - } - keyboard.keyDown.listen { key -> - when (key.name) { - "a" -> { - // make all frequencies close to a base frequency - // (circular arrangement) - val baseFreq = 20 + Random.nextFloat() * 200 - synths.forEach { - it.first.setFrequency(baseFreq + Random.nextFloat() * 20) - } - } - - "b" -> { - // make all frequencies follow an exponential series - // (spiral arrangement) - val inc = Random.nextFloat() * 0.1f - synths.forEachIndexed { i, (wave, _) -> - wave.setFrequency(25f.pow(1f + i * inc)) - } - } - } - } - } -} diff --git a/orx-jvm/orx-minim/src/demo/kotlin/DemoFFT01.kt b/orx-jvm/orx-minim/src/demo/kotlin/DemoFFT01.kt deleted file mode 100644 index 9927728a..00000000 --- a/orx-jvm/orx-minim/src/demo/kotlin/DemoFFT01.kt +++ /dev/null @@ -1,35 +0,0 @@ -import ddf.minim.Minim -import ddf.minim.analysis.FFT -import ddf.minim.analysis.LanczosWindow -import org.openrndr.application -import org.openrndr.extra.minim.minim -import org.openrndr.math.map -import kotlin.math.ln - -fun main() = application { - configure { - width = 1280 - height = 720 - } - - program { - val minim = minim() - if (minim.lineOut == null) { - application.exit() - } - - val lineIn = minim.getLineIn(Minim.MONO, 2048, 48000f) - if (lineIn == null) { - application.exit() - } - val fft = FFT(lineIn.bufferSize(), lineIn.sampleRate()) - fft.window(LanczosWindow()) - extend { - fft.forward(lineIn.mix) - for (i in 0 until 200) { - val bandDB = 20.0 * ln(2.0 * fft.getBand(i) / fft.timeSize()) - drawer.rectangle(i * 5.0, height / 2.0, 5.0, bandDB.map(0.0, -150.0, 0.0, -height / 8.0)) - } - } - } -} diff --git a/orx-jvm/orx-minim/src/demo/kotlin/DemoPlaySound01.kt b/orx-jvm/orx-minim/src/demo/kotlin/DemoPlaySound01.kt deleted file mode 100644 index 696c36de..00000000 --- a/orx-jvm/orx-minim/src/demo/kotlin/DemoPlaySound01.kt +++ /dev/null @@ -1,26 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.minim.minim - -fun main() = application { - program { - val minim = minim() - if (minim.lineOut == null) { - application.exit() - } - - val player = minim.loadFile( - "demo-data/sounds/26777__junggle__btn402.mp3" - ) - - // fade gain to -40dB in 15 seconds - player.shiftGain(player.gain, -40f, 15000) - - extend { - if (frameCount % 30 == 0) { - player.rewind() - //player.gain = Random.nextDouble(-20.0, 0.0).toFloat() - player.play() - } - } - } -} diff --git a/orx-jvm/orx-minim/src/main/kotlin/Minim.kt b/orx-jvm/orx-minim/src/main/kotlin/Minim.kt deleted file mode 100644 index 876e9abc..00000000 --- a/orx-jvm/orx-minim/src/main/kotlin/Minim.kt +++ /dev/null @@ -1,21 +0,0 @@ -package org.openrndr.extra.minim - -import ddf.minim.Minim -import org.openrndr.Program -import java.io.File -import java.io.InputStream - -class MinimObject { - @Suppress("UNUSED_PARAMETER") - fun sketchPath(fileName: String) = fileName - fun createInput(fileName: String) = File(fileName).inputStream() as InputStream -} - -fun Program.minim(): Minim { - val minim = Minim(MinimObject()) - ended.listen { - minim.stop() - - } - return minim -} \ No newline at end of file diff --git a/orx-jvm/orx-olive/README.md b/orx-jvm/orx-olive/README.md deleted file mode 100644 index 9148b890..00000000 --- a/orx-jvm/orx-olive/README.md +++ /dev/null @@ -1,154 +0,0 @@ -# orx-olive - -Provides live coding functionality: updates a running OPENRNDR program when you save your changes. - -## usage - -make sure that you add the following to your list of dependencies (next to orx-olive) -```gradle -implementation "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.3.31" -``` - -Then a simple live setup can created as follows: - -```kotlin -import org.openrndr.Program -import org.openrndr.application -import org.openrndr.extra.olive.Olive - -fun main() = application { - configure { - width = 768 - height = 576 - } - program { - extend(Olive()) - } -} -``` - -The extension will create a template script for you in `src/main/kotlin/live.kts`. You can -edit this to see how the program updates automatically. - -## Shade style errors - -Recent versions of `orx-olive` automatically set the `org.openrndr.ignoreShadeStyleErrors` property which -makes OPENRNDR ignore errors in the shade style and return the default shader. To get this behaviour in -older versions add `-Dorg.openrndr.ignoreShadeStyleErrors=true` to the JVM arguments. - -## Reloadable State - -Along with the extension comes a mechanism that allows state to be reloaded from a store on script reload. -This functionality is offered by the `Reloadable` class. - -An example `live.kts` in which the reloadable state is used: - -```kotlin -@file:Suppress("UNUSED_LAMBDA_EXPRESSION") -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* - -{ program: PersistentProgram -> - program.apply { - val a = object : Reloadable() { - var x : Double = 0.0 - } - a.reload() - - extend { - // do something with a.x here - } - } -} -``` - -The Reloadable store can be cleared using the `clearReloadables` function. - -### Reloadable GPU resources - -To store GPU resources or objects that use GPU resources (a.o. `ColorBuffer`, `VertexBuffer`, `Shader`, `BufferTexture`) in a `Reloadable` object one uses OPENRNDR's -`persistent {}` builder function. - -```kotlin -@file:Suppress("UNUSED_LAMBDA_EXPRESSION") -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* - -{ program: PersistentProgram -> - program.apply { - val a = object : Reloadable() { - var image = persistent { loadImage("data/images/pm5544.png" ) } - } - a.reload() - - extend { - drawer.image(a.image) - } - } -} -``` - - - - -Keep in mind that `Reloadable` should only be used for singleton classes. - -## Persistent Data -Sometimes you want to keep parts of your application persistent. In the following example -we show how you can prepare the host program to contain a persistent camera device. - -```kotlin -import org.openrndr.Program -import org.openrndr.application - -class PersistentProgram: Program() { - lateinit var camera: FFMPEGVideoPlayer -} - -fun main() = application{ - program(PersistentProgram()) { - camera = FFMPEGVideoPlayer.fromDevice() - camera.start() - - extend(Olive()) { - script = "src/main/PersistentCamera.Kt" - } - } -} -``` - -The live script `src/main/PersistentCamera.kts` then looks like this: - -```kotlin -@file:Suppress("UNUSED_LAMBDA_EXPRESSION") -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* - -{ program: PersistentProgram -> - program.apply { - extend { - camera.next() - drawer.drawStyle.colorMatrix = tint(ColorRGBa.GREEN) * grayscale(0.0, 0.0, 1.0) - camera.draw(drawer) - } - } -} -``` - - -## Demos -### DemoOlive01 - -Live-coding with [oliveProgram] - -![DemoOlive01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-olive/images/DemoOlive01Kt.png) - -[source code](src/demo/kotlin/DemoOlive01.kt) - -### DemoOliveFromScript01 - -Live-coding with [Olive], an older, not recommended, way to do things - -![DemoOliveFromScript01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-olive/images/DemoOliveFromScript01Kt.png) - -[source code](src/demo/kotlin/DemoOliveFromScript01.kt) diff --git a/orx-jvm/orx-olive/build.gradle.kts b/orx-jvm/orx-olive/build.gradle.kts deleted file mode 100644 index d6de6176..00000000 --- a/orx-jvm/orx-olive/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -tasks.test { - useJUnitPlatform { - } -} - -dependencies { - implementation(project(":orx-jvm:orx-file-watcher")) - implementation(project(":orx-jvm:orx-kotlin-parser")) - demoImplementation(project(":orx-jvm:orx-gui")) - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(libs.kotlin.scriptingJvm) - implementation(libs.kotlin.scriptingJvmHost) - implementation(sharedLibs.kotlin.reflect) - implementation(libs.kotlin.scriptingJSR223) - implementation(sharedLibs.kotlin.coroutines) - demoImplementation(sharedLibs.kotlin.coroutines) - testImplementation(sharedLibs.kotest.runner) - testImplementation(sharedLibs.kotest.assertions) - testRuntimeOnly(sharedLibs.kotlin.reflect) -} \ No newline at end of file diff --git a/orx-jvm/orx-olive/src/demo/kotlin/DemoOlive01.kt b/orx-jvm/orx-olive/src/demo/kotlin/DemoOlive01.kt deleted file mode 100644 index ef57f5a6..00000000 --- a/orx-jvm/orx-olive/src/demo/kotlin/DemoOlive01.kt +++ /dev/null @@ -1,44 +0,0 @@ -import org.openrndr.Extension -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extensions.SingleScreenshot -import org.openrndr.extra.olive.oliveProgram -import kotlin.math.cos - -/** - * Live-coding with [oliveProgram] - */ -fun main() = application { - configure { - width = 1280 - height = 720 - } - oliveProgram { - extend { - drawer.clear(ColorRGBa.PINK) - drawer.fill = ColorRGBa.WHITE - for (i in 0 until 100) { - drawer.circle( - width / 2.0 + cos(seconds + i) * 320.0, - i * 7.2, - cos(i + seconds * 0.5) * 20.0 + 20.0 - ) - } - } - } - // -- this is only needed for the automated screenshots - .olive.scriptLoaded.listen { - if (System.getProperty("takeScreenshot") == "true") { - // -- this is a bit of hack, we need to push the screenshot extension in front of the olive one - fun extendHead(extension: T, configure: T.() -> Unit): T { - program.extensions.add(0, extension) - extension.configure() - extension.setup(program) - return extension - } - extendHead(SingleScreenshot()) { - this.outputFile = System.getProperty("screenshotPath") - } - } - } -} diff --git a/orx-jvm/orx-olive/src/demo/kotlin/DemoOliveFromScript01.kt b/orx-jvm/orx-olive/src/demo/kotlin/DemoOliveFromScript01.kt deleted file mode 100644 index 9753e314..00000000 --- a/orx-jvm/orx-olive/src/demo/kotlin/DemoOliveFromScript01.kt +++ /dev/null @@ -1,37 +0,0 @@ -import org.openrndr.Extension -import org.openrndr.Program -import org.openrndr.application - -import org.openrndr.extensions.SingleScreenshot -import org.openrndr.extra.olive.Olive - -/** - * Live-coding with [Olive], an older, not recommended, way to do things - */ -fun main() = application { - configure { - width = 768 - height = 576 - } - program { - - extend(Olive()) { - script = "orx-jvm/orx-olive/src/demo/kotlin/demo-olive-01.kts" - // -- this block is for automation purposes only - if (System.getProperty("takeScreenshot") == "true") { - scriptLoaded.listen { - // -- this is a bit of hack, we need to push the screenshot extension in front of the olive one - fun Program.extendHead(extension: T, configure: T.() -> Unit): T { - extensions.add(0, extension) - extension.configure() - extension.setup(this) - return extension - } - extendHead(SingleScreenshot()) { - this.outputFile = System.getProperty("screenshotPath") - } - } - } - } - } -} diff --git a/orx-jvm/orx-olive/src/demo/kotlin/DemoWindowedGUI01.kt b/orx-jvm/orx-olive/src/demo/kotlin/DemoWindowedGUI01.kt deleted file mode 100644 index a2344817..00000000 --- a/orx-jvm/orx-olive/src/demo/kotlin/DemoWindowedGUI01.kt +++ /dev/null @@ -1,60 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.gui.WindowedGUI -import org.openrndr.extra.olive.oliveProgram -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter -import kotlin.math.cos -import kotlin.system.exitProcess - -/** - * Live-coding with [oliveProgram] and [WindowedGUI] - */ -fun main() { - // skip this demo on CI - if (System.getProperty("takeScreenshot") == "true") { - exitProcess(0) - } - application { - configure { - width = 720 - height = 720 - } - oliveProgram() { - val gui = WindowedGUI() - - val settings = @Description("Settings") object { - @DoubleParameter("radius", 0.0, 80.0) - var radius = 30.0 - - @ColorParameter("color") - var fill = ColorRGBa.RED - - @ColorParameter("background") - var background = ColorRGBa.BLACK - - @DoubleParameter("speed", 0.1, 10.0) - var speed = 1.0 - - @IntParameter("count", 1, 400) - var count = 100 - } - gui.add(settings) - - extend(gui) - extend { - drawer.clear(settings.background) - drawer.fill = settings.fill - for (i in 0 until settings.count) { - drawer.circle( - width / 2.0 + cos(settings. speed * seconds + i) * 320.0, - i * 7.2, - (cos(i + seconds * 0.5) * 1.0 + 1.0) * settings.radius - ) - } - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-olive/src/demo/kotlin/demo-olive-01.kts b/orx-jvm/orx-olive/src/demo/kotlin/demo-olive-01.kts deleted file mode 100644 index d7350400..00000000 --- a/orx-jvm/orx-olive/src/demo/kotlin/demo-olive-01.kts +++ /dev/null @@ -1,14 +0,0 @@ -@file:Suppress("UNUSED_LAMBDA_EXPRESSION") -import org.openrndr.Program -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* - -{ program: Program -> - program.apply { - extend { - drawer.clear(ColorRGBa.GRAY) - drawer.fill = ColorRGBa.PINK - drawer.circle(width/2.0, height/2.0 ,200.0) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-olive/src/main/kotlin/Olive.kt b/orx-jvm/orx-olive/src/main/kotlin/Olive.kt deleted file mode 100644 index 07cc2cd9..00000000 --- a/orx-jvm/orx-olive/src/main/kotlin/Olive.kt +++ /dev/null @@ -1,213 +0,0 @@ -package org.openrndr.extra.olive - -import io.github.oshai.kotlinlogging.KotlinLogging -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async - -import org.openrndr.Extension -import org.openrndr.Program -import org.openrndr.draw.Session -import org.openrndr.events.Event -import org.openrndr.exceptions.stackRootClassName -import org.openrndr.extra.kotlinparser.extractProgram -import org.openrndr.launch -import org.openrndr.extra.filewatcher.watchFile -import java.io.File - -private val logger = KotlinLogging.logger {} - - -private fun Event.saveListeners(store: MutableMap, List<(Any) -> Unit>>) { - @Suppress("UNCHECKED_CAST") - store[this] = listeners.map { it } as List<(Any) -> Unit> -} - -private fun Event.restoreListeners(store: Map, List<(Any) -> Unit>>) { - listeners.retainAll(store[this] ?: emptyList()) -} - -enum class OliveScriptHost { - JSR223, - JSR223_REUSE, - KOTLIN_SCRIPT -} - -@JvmRecord -data class ScriptLoadedEvent(val scriptFile: String) - -enum class ScriptMode { - KOTLIN_SCRIPT, - OLIVE_PROGRAM -} - -class Olive

(val resources: Resources? = null, private var scriptMode: ScriptMode = ScriptMode.KOTLIN_SCRIPT) : Extension { - override var enabled: Boolean = true - var session: Session? = null - var scriptHost = OliveScriptHost.JSR223_REUSE - - val scriptLoaded = Event() - - internal var scriptChange: (String) -> Unit = {} - - var script = when (scriptMode) { - ScriptMode.KOTLIN_SCRIPT -> "src/main/kotlin/${stackRootClassName().split(".").last()}.kts" - else -> "src/main/kotlin/${stackRootClassName().split(".").last()}.kt" - } - set(value) { - field = value - scriptChange(value) - } - - /** - * reloads the active script - */ - fun reload() { - // watcher?.triggerChange() - } - - class ScriptWatcher - - - - private var watcherRequestStopEvent = Event() - private var watcher: (() -> Unit)? = null - - @OptIn(DelicateCoroutinesApi::class) - override fun setup(program: Program) { - System.setProperty("idea.io.use.fallback", "true") - System.setProperty("org.openrndr.ignoreShadeStyleErrors", "true") - - val store = mutableMapOf, List<(Any) -> Unit>>() - val originalExtensions = program.extensions.map { it } - val trackedListeners = listOf>(program.mouse.buttonDown, - program.mouse.buttonUp, - program.mouse.dragged, - program.mouse.moved, - program.mouse.scrolled, - program.keyboard.keyUp, - program.keyboard.keyDown, - program.keyboard.keyRepeat, - program.keyboard.character, - program.window.drop, - program.window.focused, - program.window.minimized, - program.window.moved, - program.window.sized, - program.window.unfocused, - program.requestAssets, - program.produceAssets - ) - - trackedListeners.forEach { it.saveListeners(store) } - - val originalAssetMetadata = program.assetMetadata - val originalAssetProperties = program.assetProperties.toMutableMap() - - fun setupScript(scriptFile: String) { - if (watcher != null) { - logger.info { "requesting watcher stop" } - watcherRequestStopEvent.trigger(Unit) - } else { - logger.info { "no existing watcher" } - } - val f = File(scriptFile) - if (!f.exists()) { - f.parentFile.mkdirs() - var className = program.javaClass.name - if (className.contains("$")) - className = "Program" - - f.writeText(""" - @file:Suppress("UNUSED_LAMBDA_EXPRESSION") - import org.openrndr.Program - import org.openrndr.draw.* - - { program: $className -> - program.apply { - extend { - - } - } - } - """.trimIndent()) - } - - val jsr233ObjectLoader = if (scriptHost == OliveScriptHost.JSR223_REUSE) ScriptObjectLoader() else null - - watcher = watchFile(File(script), requestStopEvent = watcherRequestStopEvent) { - try { - logger.info { "change detected, reloading script" } - - val scriptContents = when (scriptMode) { - ScriptMode.KOTLIN_SCRIPT -> it.readText() - ScriptMode.OLIVE_PROGRAM -> { - val source = it.readText() - val programSource = extractProgram(source, programIdentifier = "oliveProgram") - generateScript(programSource) - } - } - - val futureFunc = GlobalScope.async { - val start = System.currentTimeMillis() - val loadedFunction = when (scriptHost) { - OliveScriptHost.JSR223_REUSE -> loadFromScriptContents(scriptContents, jsr233ObjectLoader!!) - OliveScriptHost.JSR223 -> loadFromScriptContents(scriptContents) - OliveScriptHost.KOTLIN_SCRIPT -> loadFromScriptContentsKSH Unit>(scriptContents) - } - - val end = System.currentTimeMillis() - logger.info { "loading script took ${end - start}ms" } - loadedFunction - } - - program.launch { - val func = futureFunc.await() - program.extensions.forEach {extension -> - extension.shutdown(program) - } - program.extensions.clear() - program.extensions.addAll(originalExtensions) - program.assetMetadata = originalAssetMetadata - program.assetProperties = originalAssetProperties - - trackedListeners.forEach { l -> l.restoreListeners(store) } - session?.end() - session = Session.root.fork() - - @Suppress("UNCHECKED_CAST") - func(program as P) - scriptLoaded.trigger(ScriptLoadedEvent(scriptFile)) - Unit - } - Unit - } catch (e: Throwable) { - e.printStackTrace() - } - } - } - setupScript(script) - scriptChange = ::setupScript - - if (resources != null) { - val srcPath = "src/main/resources" - var src = File(srcPath) - - resources.watch(src) { file -> - val dest = "build/resources/main" - val filePath = file.path.split(Regex(srcPath), 2).getOrNull(1) - - val destFile = File("$dest/${filePath}").absoluteFile - - watchFile(file) { - if (resources[file]!! && filePath != null) { - file.copyTo(destFile, overwrite = true) - reload() - } else { - resources[file] = true - } - } - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-olive/src/main/kotlin/OliveProgram.kt b/orx-jvm/orx-olive/src/main/kotlin/OliveProgram.kt deleted file mode 100644 index 64507807..00000000 --- a/orx-jvm/orx-olive/src/main/kotlin/OliveProgram.kt +++ /dev/null @@ -1,57 +0,0 @@ -package org.openrndr.extra.olive - -import org.openrndr.ApplicationBuilder -import org.openrndr.ProgramImplementation -import java.io.File -import java.nio.file.Files -import java.nio.file.Paths -import kotlin.reflect.KProperty - -open class OliveProgram(private val sourceLocation: String, private val scriptHost: OliveScriptHost, resources: Resources?) : ProgramImplementation() { - val olive = extend(Olive(scriptMode = ScriptMode.OLIVE_PROGRAM, resources = resources)) { - script = sourceLocation - scriptHost = this@OliveProgram.scriptHost - } -} - -/** - * Delegate used to create instances exactly once. Instances survive a script reload. - */ -class Once(val build:() -> T) { - companion object { - private val values = mutableMapOf() - } - @Suppress("UNCHECKED_CAST") - operator fun getValue(thisRef:Any?, property:KProperty<*>) : T = values.getOrPut(property.name) { build() } as T -} - -fun stackRootClassName(thread: Thread = Thread.currentThread(), sanitize: Boolean = true): String { - val root = Thread.currentThread().stackTrace.last() - val rootClass = root.className - return if (sanitize) rootClass.replace(Regex("Kt$"), "") else rootClass -} - -fun ApplicationBuilder.oliveProgram(scriptHost: OliveScriptHost = OliveScriptHost.JSR223, resources: Resources? = null, init: OliveProgram.() -> Unit): OliveProgram { - val rootClassName = stackRootClassName(sanitize = true).split(".").last() - - var sourceLocation = "src/main/kotlin/$rootClassName.kt" - val candidateFile = File(sourceLocation) - val rootClassNameCleaned = if (rootClassName.startsWith("_")) rootClassName.drop(1) else rootClassName - - if (!candidateFile.exists()) { - val otherCandidates = Files.walk(Paths.get(".")) - .filter { Files.isRegularFile(it) && it.toString().endsWith("$rootClassNameCleaned.kt") }.toList() - if (otherCandidates.size == 1) { - sourceLocation = otherCandidates.first().toString() - } else { - error("multiple source candidates found for $rootClassName: $otherCandidates") - } - } - program = object : OliveProgram(sourceLocation, scriptHost, resources) { - override suspend fun setup() { - super.setup() - init() - } - } - return program as OliveProgram -} \ No newline at end of file diff --git a/orx-jvm/orx-olive/src/main/kotlin/Reloadable.kt b/orx-jvm/orx-olive/src/main/kotlin/Reloadable.kt deleted file mode 100644 index 09e7e4a8..00000000 --- a/orx-jvm/orx-olive/src/main/kotlin/Reloadable.kt +++ /dev/null @@ -1,57 +0,0 @@ -package org.openrndr.extra.olive - -import io.github.oshai.kotlinlogging.KotlinLogging -import kotlin.reflect.KMutableProperty1 -import kotlin.reflect.KProperty1 -import kotlin.reflect.full.declaredMemberProperties -import kotlin.reflect.jvm.jvmName - -private val logger = KotlinLogging.logger {} - -private val store = mutableMapOf() - -/** - * Clear reloadable values - */ -fun clearReloadables() { - store.clear() -} - -/** - * A class with which persistent state can be reloaded from inside Olive scripts. - */ -open class Reloadable { - private fun normalizeClassName(name: String): String { - return name.replace( - Regex("ScriptingHost[0-9a-f]+_"), // -- since kotlin 1.3.61 the scripting host prepends class names with the host id - "" - ).replace(Regex("Line_[0-9]+"), "") // -- when reusing the script engine the line number increments. - } - - /** - * reload property values from store - */ - @Suppress("UNCHECKED_CAST") - fun reload() { - val className = normalizeClassName(this::class.jvmName) - val existing = store[className] - if (existing != null) { - for (p in this::class.declaredMemberProperties) { - val e = existing::class.declaredMemberProperties.find { it.name == p.name } - if (e != null) { - try { - val value = (e as KProperty1).get(existing) - val mp = (p as KMutableProperty1) - mp.set(this, value as Any) - logger.info { "reloaded property ${p.name} <- ${value}" } - } catch (e: Throwable) { - logger.warn { "error while reloading property ${p.name}: ${e.message}" } - } - } - } - } else { - logger.info { "no existing store found for $className" } - } - store[normalizeClassName(this::class.jvmName)] = this - } -} diff --git a/orx-jvm/orx-olive/src/main/kotlin/Resources.kt b/orx-jvm/orx-olive/src/main/kotlin/Resources.kt deleted file mode 100644 index c64bc3fd..00000000 --- a/orx-jvm/orx-olive/src/main/kotlin/Resources.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.openrndr.extra.olive - -import java.io.File - -class Resources(val filterOutExtensions: List = listOf()) { - private val watchedResources = mutableMapOf() - - fun watch(src: File, watchFn: (file: File) -> Unit) { - src.listFiles()!!.forEach {file -> - if (file.isFile && !filterOutExtensions.contains(file.extension)) { - watchedResources[file] = false - - watchFn(file) - } else if (file.isDirectory) { - watch(file, watchFn) - } - } - } - - operator fun get(file: File): Boolean? { - return watchedResources[file] - } - - operator fun set(file: File, value: Boolean) { - if (watchedResources.containsKey(file)) { - watchedResources[file] = value - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-olive/src/main/kotlin/ScriptGenerator.kt b/orx-jvm/orx-olive/src/main/kotlin/ScriptGenerator.kt deleted file mode 100644 index 36d655aa..00000000 --- a/orx-jvm/orx-olive/src/main/kotlin/ScriptGenerator.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.openrndr.extra.olive - -import org.openrndr.extra.kotlinparser.ProgramSource - -inline fun generateScript(programSource: ProgramSource): String { - val script = """ - -//${programSource.packageName?:""} - -import org.openrndr.extra.olive.OliveProgram -${programSource.imports} - -{ program: ${T::class.qualifiedName} -> - program.apply { - ${programSource.programLambda} - } -} -""" - return script -} \ No newline at end of file diff --git a/orx-jvm/orx-olive/src/main/kotlin/ScriptObjectLoaderJSR233.kt b/orx-jvm/orx-olive/src/main/kotlin/ScriptObjectLoaderJSR233.kt deleted file mode 100644 index f061bf9a..00000000 --- a/orx-jvm/orx-olive/src/main/kotlin/ScriptObjectLoaderJSR233.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.openrndr.extra.olive - -import io.github.oshai.kotlinlogging.KotlinLogging -import java.io.File -import java.io.InputStream -import java.io.Reader -import java.net.MalformedURLException -import java.net.URI -import java.net.URL -import javax.script.ScriptEngineManager - -private val logger = KotlinLogging.logger {} - -class LoadException(message: String? = null, cause: Throwable? = null) : RuntimeException(message, cause) - -class ScriptObjectLoader(classLoader: ClassLoader? = Thread.currentThread().contextClassLoader) { - val engine = run { - val start = System.currentTimeMillis() - val engine = ScriptEngineManager(classLoader).getEngineByExtension("kts") - val end = System.currentTimeMillis() - logger.info { "creating scripting engine took ${end-start}ms" } - engine - } - - init { - require(engine != null) { "could not create scripting engine" } - } - - fun safeEval(evaluation: () -> R?) = try { - evaluation() - } catch (e: Exception) { - e.printStackTrace() - throw LoadException("Cannot load script", e) - } - - inline fun Any?.castOrError() = takeIf { it is T }?.let { it as T } - ?: throw IllegalArgumentException("Cannot cast $this to expected type ${T::class}") - - inline fun load(script: String): T = safeEval { engine.eval(script) }.castOrError() - - inline fun load(reader: Reader): T = safeEval { engine.eval(reader) }.castOrError() - - inline fun load(inputStream: InputStream): T = load(inputStream.reader()) - - inline fun loadAll(vararg inputStream: InputStream): List = inputStream.map(::load) -} - - -/** - * Load an object from script. - */ -inline fun loadFromScript(fileOrUrl: String, loader: ScriptObjectLoader = ScriptObjectLoader()): T { - val isUrl = try { - URL(fileOrUrl); true - } catch (e: MalformedURLException) { - false - } - - val script = if (isUrl) { - URL(fileOrUrl).readText() - } else { - File(fileOrUrl).readText() - } - return loader.load(script) -} - -/** - * Load an object from script file - */ -inline fun loadFromScript(file: File, loader: ScriptObjectLoader = ScriptObjectLoader()): T = - loader.load(file.readText()) - - -/** - * Load an object from script file - */ -inline fun loadFromScriptContents(contents:String, loader: ScriptObjectLoader = ScriptObjectLoader()): T = - loader.load(contents) diff --git a/orx-jvm/orx-olive/src/main/kotlin/ScriptObjectLoaderKSH.kt b/orx-jvm/orx-olive/src/main/kotlin/ScriptObjectLoaderKSH.kt deleted file mode 100644 index cb89f4f0..00000000 --- a/orx-jvm/orx-olive/src/main/kotlin/ScriptObjectLoaderKSH.kt +++ /dev/null @@ -1,49 +0,0 @@ -package org.openrndr.extra.olive - -import java.io.File -import kotlin.script.experimental.api.* -import kotlin.script.experimental.host.BasicScriptingHost -import kotlin.script.experimental.host.toScriptSource -import kotlin.script.experimental.jvm.dependenciesFromCurrentContext -import kotlin.script.experimental.jvm.jvm -import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost -import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate -import kotlin.script.templates.standard.SimpleScriptTemplate - -internal fun evalScriptWithConfiguration( - script: String, - host: BasicScriptingHost = BasicJvmScriptingHost(), - body: ScriptCompilationConfiguration.Builder.() -> Unit = {} -): ResultWithDiagnostics { - val compilationConfiguration = createJvmCompilationConfigurationFromTemplate(body = body) - return host.eval(script.toScriptSource(), compilationConfiguration, null) -} - -@Suppress("UNCHECKED_CAST") -fun loadFromScriptKSH( - script: File, - host: BasicScriptingHost = BasicJvmScriptingHost(), - body: ScriptCompilationConfiguration.Builder.() -> Unit = { - - jvm { - dependenciesFromCurrentContext(wholeClasspath = true) - } - - } -): T = loadFromScriptContentsKSH(script.readText(), host, body) - -@Suppress("UNCHECKED_CAST") -fun loadFromScriptContentsKSH( - script: String, - host: BasicScriptingHost = BasicJvmScriptingHost(), - body: ScriptCompilationConfiguration.Builder.() -> Unit = { - - jvm { - dependenciesFromCurrentContext(wholeClasspath = true) - } - - } -): T = (evalScriptWithConfiguration(script, host, body).valueOrThrow().returnValue as ResultValue.Value).value as T - - - diff --git a/orx-jvm/orx-olive/src/test/kotlin/TestLoadScript.kt b/orx-jvm/orx-olive/src/test/kotlin/TestLoadScript.kt deleted file mode 100644 index abff20c6..00000000 --- a/orx-jvm/orx-olive/src/test/kotlin/TestLoadScript.kt +++ /dev/null @@ -1,16 +0,0 @@ -import io.kotest.core.spec.style.DescribeSpec -import io.kotest.matchers.equals.shouldBeEqual -import org.openrndr.extra.olive.ScriptObjectLoader - -class TestLoadScript : DescribeSpec({ - - describe("some script") { - val loader = ScriptObjectLoader() - - val number = loader.load("5") - - it("should evaluate properly") { - number shouldBeEqual 5 - } - } -}) \ No newline at end of file diff --git a/orx-jvm/orx-olive/src/test/kotlin/TestLoadScriptKSH.kt b/orx-jvm/orx-olive/src/test/kotlin/TestLoadScriptKSH.kt deleted file mode 100644 index e539f925..00000000 --- a/orx-jvm/orx-olive/src/test/kotlin/TestLoadScriptKSH.kt +++ /dev/null @@ -1,14 +0,0 @@ -import io.kotest.core.spec.style.DescribeSpec -import io.kotest.matchers.equals.shouldBeEqual -import org.openrndr.extra.olive.loadFromScriptContentsKSH - -class TestLoadScriptKSH : DescribeSpec({ - - describe("some script") { - val number = loadFromScriptContentsKSH("5") - - it("should evaluate properly") { - number shouldBeEqual 5 - } - } -}) \ No newline at end of file diff --git a/orx-jvm/orx-osc/README.md b/orx-jvm/orx-osc/README.md deleted file mode 100644 index c066866d..00000000 --- a/orx-jvm/orx-osc/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# orx-osc - -Open Sound Control makes it possible to send and receive messages -from other OSC enabled programs in the same or a different computer. -Used to create multi-application or multi-device software. - -Can be used to remote control your program from a mobile device, -to produce sound in a audio programming environment, make games -and networked experiments. - -Orx-osc is a wrapper around javaOSC - -## Usage - -```kotlin -// PORT IN and OUT: 57110 -val osc = OSC() - -osc.listen("/live/track2") { addr, msg -> - // do something -} - -osc.send("/maxmsp/filter", 500, "hz") -``` - -For more examples please visit the [guide](https://guide.openrndr.org/OPENRNDRExtras/osc.html). diff --git a/orx-jvm/orx-osc/build.gradle.kts b/orx-jvm/orx-osc/build.gradle.kts deleted file mode 100644 index 821ffe02..00000000 --- a/orx-jvm/orx-osc/build.gradle.kts +++ /dev/null @@ -1,10 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(libs.javaosc.core) { - exclude(group = "org.slf4j") - exclude(group = "log4j") - } -} \ No newline at end of file diff --git a/orx-jvm/orx-osc/src/main/kotlin/OSC.kt b/orx-jvm/orx-osc/src/main/kotlin/OSC.kt deleted file mode 100644 index 805917c7..00000000 --- a/orx-jvm/orx-osc/src/main/kotlin/OSC.kt +++ /dev/null @@ -1,83 +0,0 @@ -package org.openrndr.extra.osc - -import com.illposed.osc.OSCMessage -import com.illposed.osc.OSCMessageListener -import com.illposed.osc.messageselector.OSCPatternAddressMessageSelector -import com.illposed.osc.transport.OSCPort -import com.illposed.osc.transport.OSCPortIn -import com.illposed.osc.transport.OSCPortOut -import io.github.oshai.kotlinlogging.KotlinLogging -import java.net.InetAddress -import java.net.PortUnreachableException -import kotlin.reflect.KMutableProperty0 - -private typealias OSCListener = Pair - -private val logger = KotlinLogging.logger {} - -@Suppress("unused") -class OSC ( - val address: InetAddress = InetAddress.getLocalHost(), - val portIn: Int = OSCPort.DEFAULT_SC_OSC_PORT, - val portOut: Int = portIn -) { - private val receiver: OSCPortIn = OSCPortIn(portIn) - private val sender: OSCPortOut = OSCPortOut(address, portOut) - private val listeners: MutableMap = mutableMapOf() - - fun send(channel: String, vararg message: T) { - if (!sender.isConnected) sender.connect() - - val msg = OSCMessage(channel, message.toList()) - - try { - sender.send(msg) - } catch (ex: PortUnreachableException) { - logger.error(ex) { "Error: Could not connect to OUT port" } - } catch (ex: IllegalStateException) { - logger.error(ex) { "Error: Couldn't send message to channel: $channel" } - } - } - - fun listen(channel: String, callback: (String, List) -> Unit) { - val selector = OSCPatternAddressMessageSelector(channel); - - val cb = OSCMessageListener { - callback(it.message.address, it.message.arguments) - } - - receiver.dispatcher.addListener(selector, cb) - - listeners[channel] = Pair(selector, cb) - - if (!receiver.isListening) this.startListening() - } - - infix fun String.bind(prop: KMutableProperty0) { - val channel = this - - listen(channel) { _, it -> - when (val message = it.first()) { - is Double -> prop.set(message) - is Float -> prop.set(message.toDouble()) - } - } - } - - // Cannot be called inside a listener's callback - fun removeListener(channel: String?) { - val listener = listeners[channel] - - if (listener != null) { - receiver.dispatcher.removeListener(listener.first, listener.second) - } - } - - private fun startListening() { - receiver.dispatcher.isAlwaysDispatchingImmediately = true; - - receiver.startListening() - - if (receiver.isListening) logger.info { "OSC is listening on port: $portIn" } - } -} diff --git a/orx-jvm/orx-panel/README.md b/orx-jvm/orx-panel/README.md deleted file mode 100644 index a3264d54..00000000 --- a/orx-jvm/orx-panel/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# orx-panel - -The OPENRNDR UI toolkit. Provides buttons, sliders, text, a color picker and much more. HTML/CSS-like. - - -## Demos -### DemoColorPickerButton01 - -A simple demonstration of a ColorPickerButton - -![DemoColorPickerButton01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-panel/images/DemoColorPickerButton01Kt.png) - -[source code](src/demo/kotlin/DemoColorPickerButton01.kt) - -### DemoHorizontalLayout01 - - - -![DemoHorizontalLayout01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-panel/images/DemoHorizontalLayout01Kt.png) - -[source code](src/demo/kotlin/DemoHorizontalLayout01.kt) - -### DemoVerticalLayout01 - - - -![DemoVerticalLayout01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-panel/images/DemoVerticalLayout01Kt.png) - -[source code](src/demo/kotlin/DemoVerticalLayout01.kt) - -### DemoWatchDiv01 - - - -![DemoWatchDiv01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-panel/images/DemoWatchDiv01Kt.png) - -[source code](src/demo/kotlin/DemoWatchDiv01.kt) - -### DemoWatchObjectDiv01 - - - -![DemoWatchObjectDiv01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-panel/images/DemoWatchObjectDiv01Kt.png) - -[source code](src/demo/kotlin/DemoWatchObjectDiv01.kt) diff --git a/orx-jvm/orx-panel/build.gradle.kts b/orx-jvm/orx-panel/build.gradle.kts deleted file mode 100644 index a3050edc..00000000 --- a/orx-jvm/orx-panel/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -tasks.test { - useJUnitPlatform { - } -} - -dependencies { - implementation(project(":orx-expression-evaluator")) - implementation(project(":orx-color")) - implementation(project(":orx-text-writer")) - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(sharedLibs.kotlin.coroutines) - implementation(sharedLibs.kotlin.reflect) - testRuntimeOnly(sharedLibs.kotlin.reflect) - demoImplementation(openrndr.dialogs) - demoImplementation(libs.gson) - demoImplementation(project(":orx-jvm:orx-panel")) -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/demo/kotlin/DemoColorPickerButton01.kt b/orx-jvm/orx-panel/src/demo/kotlin/DemoColorPickerButton01.kt deleted file mode 100644 index 4a021324..00000000 --- a/orx-jvm/orx-panel/src/demo/kotlin/DemoColorPickerButton01.kt +++ /dev/null @@ -1,33 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.panel.controlManager -import org.openrndr.panel.elements.colorpickerButton - -/** - * A simple demonstration of a ColorPickerButton - */ -fun main() = application { - configure { - width = 720 - height = 720 - } - program { - var bgColor = ColorRGBa.PINK - - val cm = controlManager { - layout { - colorpickerButton { - label = "Pick color" - color = bgColor - events.valueChanged.listen { - bgColor = it.color - } - } - } - } - extend(cm) - extend { - drawer.clear(bgColor) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/demo/kotlin/DemoHorizontalLayout01.kt b/orx-jvm/orx-panel/src/demo/kotlin/DemoHorizontalLayout01.kt deleted file mode 100644 index 3dfab7d7..00000000 --- a/orx-jvm/orx-panel/src/demo/kotlin/DemoHorizontalLayout01.kt +++ /dev/null @@ -1,77 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.color.Linearity -import org.openrndr.math.Spherical -import org.openrndr.math.Vector3 -import org.openrndr.panel.controlManager -import org.openrndr.panel.elements.button -import org.openrndr.panel.elements.div -import org.openrndr.panel.elements.h1 -import org.openrndr.panel.style.* - -fun main() = application { - program { - val cm = controlManager { - styleSheet(has class_ "horizontal") { - paddingLeft = 10.px - paddingTop = 10.px - - // ---------------------------------------------- - // The next two lines produce a horizontal layout - // ---------------------------------------------- - display = Display.FLEX - flexDirection = FlexDirection.Row - width = 100.percent - } - - styleSheet(has type "h1") { - marginTop = 10.px - marginLeft = 7.px - marginBottom = 10.px - } - - layout { - val header = h1 { "click a button..." } - - div("horizontal") { - // A bunch of names for generating buttons - listOf( - "load", "save", "redo", "stretch", "bounce", - "twist", "swim", "roll", "fly", "dance" - ) - .forEachIndexed { i, word -> - - // A fun way of generating a set of colors - // of similar brightness: - // Grab a point on the surface of a sphere - // and treat its coordinates as an rgb color. - val pos = Vector3.fromSpherical( - Spherical(i * 19.0, i * 17.0, 0.4) - ) - val rgb = ColorRGBa.fromVector(pos + 0.4) - - button { - label = word - style = styleSheet { - // Use Color.RGBa() to convert a ColorRGBa - // color (the standard color datatype) - // into "CSS" format: - background = Color.RGBa(rgb) - } - - // When the button is clicked replace - // the header text with the button's label - events.clicked.listen { - header.replaceText(it.source.label) - } - } - } - } - } - } - extend(cm) - extend { - drawer.clear(ColorRGBa(0.2, 0.18, 0.16, 1.0, Linearity.SRGB)) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/demo/kotlin/DemoVerticalLayout01.kt b/orx-jvm/orx-panel/src/demo/kotlin/DemoVerticalLayout01.kt deleted file mode 100644 index e83a37a1..00000000 --- a/orx-jvm/orx-panel/src/demo/kotlin/DemoVerticalLayout01.kt +++ /dev/null @@ -1,40 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.panel.controlManager -import org.openrndr.panel.elements.div -import org.openrndr.panel.elements.slider -import org.openrndr.panel.style.* - -fun main() = application { - program { - val cm = controlManager { - styleSheet(has class_ "side-bar") { - this.height = 100.percent - this.width = 200.px - this.display = Display.FLEX - this.flexDirection = FlexDirection.Column - this.paddingLeft = 10.px - this.paddingRight = 10.px - this.background = Color.RGBa(ColorRGBa.GRAY) - } - styleSheet(has type "slider") { - this.marginTop = 25.px - this.marginBottom = 25.px - } - layout { - div("side-bar") { - slider { - label = "Slider 1" - } - slider { - label = "Slider 2" - } - } - } - } - extend(cm) - extend { - - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/demo/kotlin/DemoWatchDiv01.kt b/orx-jvm/orx-panel/src/demo/kotlin/DemoWatchDiv01.kt deleted file mode 100644 index ada29fac..00000000 --- a/orx-jvm/orx-panel/src/demo/kotlin/DemoWatchDiv01.kt +++ /dev/null @@ -1,123 +0,0 @@ -import com.google.gson.Gson -import org.openrndr.application -import org.openrndr.dialogs.openFileDialog -import org.openrndr.dialogs.saveFileDialog -import org.openrndr.panel.controlManager -import org.openrndr.panel.elements.* -import org.openrndr.panel.style.* -import java.io.File - -// -- these have to be top-level classes or Gson will silently fail. -private class ConfigItem { - var value: Double = 0.0 -} - -private class ProgramState { - var rows = 1 - var columns = 1 - val matrix = mutableListOf(mutableListOf(ConfigItem())) - - fun copyTo(programState: ProgramState) { - programState.rows = rows - programState.columns = columns - programState.matrix.clear() - programState.matrix.addAll(matrix) - } - - fun save(file: File) { - file.writeText(Gson().toJson(this)) - } - - fun load(file: File) { - Gson().fromJson(file.readText(), ProgramState::class.java).copyTo(this) - } -} - -fun main() = application { - configure { - width = 900 - height = 720 - } - - program { - val programState = ProgramState() - val cm = controlManager { - layout { - styleSheet(has class_ "matrix") { - this.width = 100.percent - } - - styleSheet(has class_ "row") { - this.display = Display.FLEX - this.flexDirection = FlexDirection.Row - this.width = 100.percent - - child(has type "slider") { - this.width = 80.px - } - } - - button { - label = "save" - clicked { - saveFileDialog(supportedExtensions = listOf("JSON" to listOf("json"))) { - programState.save(it) - } - } - } - - button { - label = "load" - clicked { - openFileDialog(supportedExtensions = listOf("JSON" to listOf("json"))) { - programState.load(it) - } - } - } - - slider { - label = "rows" - precision = 0 - bind(programState::rows) - - events.valueChanged.listen { - while (programState.matrix.size > programState.rows) { - programState.matrix.removeAt(programState.matrix.size - 1) - } - while (programState.matrix.size < programState.rows) { - programState.matrix.add(MutableList(programState.columns) { ConfigItem() }) - } - } - } - - slider { - label = "columns" - precision = 0 - bind(programState::columns) - events.valueChanged.listen { - for (row in programState.matrix) { - while (row.size > programState.columns) { - row.removeAt(row.size - 1) - } - while (row.size < programState.columns) { - row.add(ConfigItem()) - } - } - } - } - - watchListDiv("matrix", watchList = programState.matrix) { row -> - watchListDiv("row", watchList = row) { item -> - this.id = "some-row" - slider { - label = "value" - bind(item::value) - } - } - } - } - } - extend(cm) - } -} - diff --git a/orx-jvm/orx-panel/src/demo/kotlin/DemoWatchObjectDiv01.kt b/orx-jvm/orx-panel/src/demo/kotlin/DemoWatchObjectDiv01.kt deleted file mode 100644 index 6c078dc1..00000000 --- a/orx-jvm/orx-panel/src/demo/kotlin/DemoWatchObjectDiv01.kt +++ /dev/null @@ -1,68 +0,0 @@ -import org.openrndr.application -import org.openrndr.panel.controlManager -import org.openrndr.panel.elements.* -import org.openrndr.panel.style.* - - -fun main() = application { - configure { - width = 900 - height = 720 - } - // A very simple state - class State { - var x = 0 - var y = 0 - var z = 0 - } - program { - val programState = State() - val cm = controlManager { - layout { - styleSheet(has class_ "matrix") { - this.width = 100.percent - } - - styleSheet(has class_ "row") { - this.display = Display.FLEX - this.flexDirection = FlexDirection.Row - this.width = 100.percent - - child(has type "slider") { - this.width = 80.px - } - } - - slider { - label = "x" - precision = 0 - bind(programState::x) - } - - slider { - label = "y" - precision = 0 - bind(programState::y) - } - - watchObjectDiv("matrix", watchObject = object { - // for primitive types we have to use property references - val x = programState::x - val y = programState::y - }) { - for (y in 0 until watchObject.y.get()) { - div("row") { - for (x in 0 until watchObject.x.get()) { - button() { - label = "$x, $y" - } - } - } - } - } - } - } - extend(cm) - } -} - diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/ControlManager.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/ControlManager.kt deleted file mode 100644 index 9a3ba3c7..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/ControlManager.kt +++ /dev/null @@ -1,603 +0,0 @@ -package org.openrndr.panel - -import io.github.oshai.kotlinlogging.KotlinLogging -import org.openrndr.* -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector2 -import org.openrndr.panel.elements.* -import org.openrndr.panel.layout.Layouter -import org.openrndr.panel.style.* -import org.openrndr.panel.style.Display -import org.openrndr.shape.Rectangle -import org.w3c.dom.Node -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract - -private val logger = KotlinLogging.logger {} - -class ControlManager : Extension { - var body: Element? = null - val layouter = Layouter() - val fontManager = FontManager() - lateinit var window: Window - private val renderTargetCache = HashMap() - - lateinit var program: Program - override var enabled: Boolean = true - - var contentScale = 1.0 - var renderTarget: RenderTarget? = null - - init { - fontManager.register("default", resourceUrl("/fonts/Roboto-Regular.ttf")) - layouter.styleSheets.addAll(defaultStyles().flatMap { it.flatten() }) - } - - inner class DropInput { - var target: Element? = null - fun drop(event: DropEvent) { - target?.drop?.dropped?.trigger(event) - } - } - - val dropInput = DropInput() - - - inner class KeyboardInput { - private var lastTarget: Element? = null - var target: Element? = null - set(value) { - if (value != field) { - field?.pseudoClasses?.remove(ElementPseudoClass("active")) - field?.keyboard?.focusLost?.trigger(FocusEvent()) - value?.keyboard?.focusGained?.trigger(FocusEvent()) - field = value - field?.pseudoClasses?.add(ElementPseudoClass("active")) - value?.let { - lastTarget = it - } - } - } - - fun press(event: KeyEvent) { - target?.let { - if (it.isHidden()) { - return - } - var current: Element? = it - while (current != null) { - if (!event.propagationCancelled) { - current.keyboard.pressed.trigger(event) - } - current = current.parent - } - checkForManualRedraw() - } - - if (!event.propagationCancelled) { - if (event.key == KEY_TAB) { - val focusableControls = body?.findAllVisible { it.handlesKeyboardFocus } ?: emptyList() - - val index = target?.let { focusableControls.indexOf(it) } - ?: lastTarget?.let { focusableControls.indexOf(it) } ?: -1 - if (focusableControls.isNotEmpty()) { - - target = if (target != null) { - if (KeyModifier.SHIFT in event.modifiers) { - focusableControls[(index - 1).mod(focusableControls.size)] - } else { - focusableControls[(index + 1).mod(focusableControls.size)] - } - } else { - lastTarget ?: focusableControls[0] - } - } - } - } - } - - fun release(event: KeyEvent) { - if (target?.isHidden() == true) { - return - } - - target?.keyboard?.released?.trigger(event) - if (target != null) { - checkForManualRedraw() - } - } - - fun repeat(event: KeyEvent) { - if (target?.isHidden() == true) { - return - } - - target?.keyboard?.repeated?.trigger(event) - if (target != null) { - checkForManualRedraw() - } - } - - fun character(event: CharacterEvent) { - if (target?.isHidden() == true) { - return - } - - target?.keyboard?.character?.trigger(event) - if (target != null) { - checkForManualRedraw() - } - } - - fun requestFocus(element: Element) { - target = element - } - } - - val keyboardInput = KeyboardInput() - - inner class MouseInput { - var dragTarget: Element? = null - var clickTarget: Element? = null - var lastClick = System.currentTimeMillis() - - fun scroll(event: MouseEvent) { - fun traverse(element: Element) { - if (element.computedStyle.display == Display.NONE) { - return - } - for (child in element.children) { - traverse(child) - } - if (!event.propagationCancelled) { - if (event.position in element.screenArea && element.computedStyle.display != Display.NONE) { - element.mouse.scrolled.trigger(event) - if (event.propagationCancelled) { - keyboardInput.target = element - } - } - } - } - body?.let(::traverse) - checkForManualRedraw() - } - - fun click(event: MouseEvent) { - logger.debug { "click event: $event" } - dragTarget = null - val ct = System.currentTimeMillis() - logger.debug { "click target: $clickTarget" } - - clickTarget?.let { - if (it.isHidden()) { - return - } - if (it.handlesDoubleClick) { - if (ct - lastClick > 500) { - logger.debug { "normal click on $clickTarget" } - it.mouse.clicked.trigger(event) - } else { - if (clickTarget != null) { - logger.debug { "double-click on $clickTarget" } - it.mouse.doubleClicked.trigger(event) - } - } - lastClick = ct - } else { - logger.debug { "normal click on $clickTarget" } - it.mouse.clicked.trigger(event) - } - } - checkForManualRedraw() - } - - fun press(event: MouseEvent) { - logger.debug { "press event: $event" } - val candidates = mutableListOf>() - fun traverse(element: Element, depth: Int = 0) { - if (element.computedStyle.display == Display.NONE) { - return - } - if (element.computedStyle.overflow == Overflow.Scroll) { - if (event.position !in element.screenArea) { - return - } - } - - if (element.computedStyle.display != Display.NONE) { - element.children.forEach { traverse(it, depth + 1) } - } - - if (!event.propagationCancelled && event.position in element.screenArea && element.computedStyle.display != Display.NONE) { - candidates.add(Pair(element, depth)) - } - } - - body?.let { traverse(it) } - //candidates.sortByDescending { it.second } - clickTarget = null - candidates.sortWith(compareBy({ -it.first.layout.zIndex }, { -it.second })) - for (c in candidates) { - if (!event.propagationCancelled) { - c.first.mouse.pressed.trigger(event) - if (event.propagationCancelled) { - logger.debug { "propagation cancelled by ${c.first}" } - dragTarget = c.first - clickTarget = c.first - keyboardInput.target = c.first - } - } - } - - if (clickTarget == null) { - dragTarget = null - keyboardInput.target = null - } - - checkForManualRedraw() - } - - fun drag(event: MouseEvent) { - logger.debug { "drag event $event" } - dragTarget?.let { - if (it.isHidden()) { - dragTarget = null - return - } - it.mouse.dragged.trigger(event) - } - - if (event.propagationCancelled) { - logger.debug { "propagation cancelled by $dragTarget setting clickTarget to null" } - clickTarget = null - } - checkForManualRedraw() - } - - val insideElements = mutableSetOf() - fun move(event: MouseEvent) { - val hover = ElementPseudoClass("hover") - val toRemove = insideElements.filter { (event.position !in it.screenArea) } - - toRemove.forEach { - it.mouse.exited.trigger( - MouseEvent( - event.position, - Vector2.ZERO, - Vector2.ZERO, - MouseEventType.MOVED, - MouseButton.NONE, - event.modifiers - ) - ) - } - - insideElements.removeAll(toRemove) - - fun traverse(element: Element) { - if (event.position in element.screenArea) { - if (element !in insideElements) { - element.mouse.entered.trigger(event) - } - insideElements.add(element) - if (hover !in element.pseudoClasses) { - element.pseudoClasses.add(hover) - } - element.mouse.moved.trigger(event) - } else { - if (hover in element.pseudoClasses) { - element.pseudoClasses.remove(hover) - } - } - element.children.forEach(::traverse) - } - body?.let(::traverse) - checkForManualRedraw() - } - } - - fun checkForManualRedraw() { - if (window.presentationMode == PresentationMode.MANUAL) { - val redraw = body?.any { - it.draw.dirty - } ?: false - if (redraw) { - window.requestDraw() - } - } - } - - val mouseInput = MouseInput() - override fun setup(program: Program) { - - fontManager.program = program - this.program = program - - contentScale = program.window.contentScale - window = program.window - - fontManager.contentScale = contentScale - program.mouse.buttonUp.listen { mouseInput.click(it) } - program.mouse.moved.listen { mouseInput.move(it) } - program.mouse.scrolled.listen { mouseInput.scroll(it) } - program.mouse.dragged.listen { mouseInput.drag(it) } - program.mouse.buttonDown.listen { mouseInput.press(it) } - - program.keyboard.keyDown.listen { keyboardInput.press(it) } - program.keyboard.keyUp.listen { keyboardInput.release(it) } - program.keyboard.keyRepeat.listen { keyboardInput.repeat(it) } - program.keyboard.character.listen { keyboardInput.character(it) } - - program.window.drop.listen { dropInput.drop(it) } - program.window.sized.listen { resize(program, it.size.x.toInt(), it.size.y.toInt()) } - - width = program.width - height = program.height - - body?.draw?.dirty = true - } - - var width: Int = 0 - var height: Int = 0 - - private fun resize(program: Program, width: Int, height: Int) { - this.width = width - this.height = height - - // check if user did not minimize window - if (width > 0 && height > 0) { - body?.draw?.dirty = true - val lrc = renderTarget - if (lrc != null) { - if (lrc.colorAttachments.isNotEmpty()) { - lrc.colorBuffer(0).destroy() - lrc.depthBuffer?.destroy() - lrc.detachColorAttachments() - lrc.detachDepthBuffer() - lrc.destroy() - } else { - logger.error { "that is strange. no color buffers" } - } - } - - renderTarget = renderTarget(program.width, program.height, contentScale) { - colorBuffer() - depthBuffer() - } - - renderTarget?.bind() - program.drawer.clear(ColorRGBa.BLACK.opacify(0.0)) - renderTarget?.unbind() - - renderTargetCache.forEach { (_, u) -> u.destroy() } - renderTargetCache.clear() - } - } - - private fun drawElement(element: Element, drawer: Drawer, zIndex: Int, zComp: Int) { - val newZComp = - element.computedStyle.zIndex.let { - when (it) { - is ZIndex.Value -> it.value - else -> zComp - } - } - - if (element.computedStyle.display != Display.NONE) { - if (element.computedStyle.overflow == Overflow.Visible) { - drawer.isolated { - drawer.drawStyle.textSetting = TextSettingMode.PIXEL - drawer.translate(element.screenPosition) - if (newZComp == zIndex) { - element.draw(drawer) - } - } - element.children.forEach { - drawElement(it, drawer, zIndex, newZComp) - } - } else { - val area = element.screenArea - val rt = renderTargetCache.computeIfAbsent(element) { - renderTarget(width, height, contentScale) { - colorBuffer() - depthBuffer() - } - } - - rt.bind() - drawer.clear(ColorRGBa.BLACK.opacify(0.0)) - - drawer.pushProjection() - drawer.ortho(rt) - element.children.forEach { - drawElement(it, drawer, zIndex, newZComp) - } - rt.unbind() - drawer.popProjection() - - drawer.pushTransforms() - drawer.pushStyle() - drawer.translate(element.screenPosition) - - if (newZComp == zIndex) { - element.draw(drawer) - } - drawer.popStyle() - drawer.popTransforms() - - drawer.drawStyle.blendMode = BlendMode.OVER - //drawer.image(rt.colorMap(0)) - drawer.image( - rt.colorBuffer(0), Rectangle(Vector2(area.x, area.y), area.width, area.height), - Rectangle(Vector2(area.x, area.y), area.width, area.height) - ) - } - } - element.draw.dirty = false - - } - - class ProfileData(var hits: Int = 0, var time: Long = 0) - - private val profiles = mutableMapOf() - private fun profile(name: String, f: () -> Unit) { - val start = System.currentTimeMillis() - f() - val end = System.currentTimeMillis() - val pd = profiles.getOrPut(name) { ProfileData(0, 0L) } - pd.hits++ - pd.time += (end - start) - - if (pd.hits == 100) { - //println("name: $name, avg: ${pd.time / pd.hits}ms, ${pd.hits}") - pd.hits = 0 - pd.time = 0 - } - } - - var drawCount = 0 - override fun afterDraw(drawer: Drawer, program: Program) { - if (program.width > 0 && program.height > 0) { - if (program.width != renderTarget?.width || program.height != renderTarget?.height) { - body?.draw?.dirty = true - - renderTarget?.colorBuffer(0)?.destroy() - renderTarget?.destroy() - renderTarget = null - - } - - if (renderTarget == null) { - renderTarget = renderTarget(program.width, program.height, contentScale) { - colorBuffer() - } - renderTarget!!.bind() - program.drawer.clear(ColorRGBa.BLACK.opacify(0.0)) - renderTarget!!.unbind() - } - - val redraw = body?.any { - it.draw.dirty - } ?: false - - if (redraw) { - drawer.ortho() - drawer.view = Matrix44.IDENTITY - drawer.defaults() - - renderTarget!!.bind() - body?.style = StyleSheet(CompoundSelector()) - body?.style?.width = program.width.px - body?.style?.height = program.height.px - - body?.let { - program.drawer.clear(ColorRGBa.BLACK.opacify(0.0)) - layouter.computeStyles(it) - layouter.layout(it) - drawElement(it, program.drawer, 0, 0) - drawElement(it, program.drawer, 1, 0) - drawElement(it, program.drawer, 1000, 0) - } - renderTarget!!.unbind() - } - - body?.visit { - draw.dirty = false - } - - drawer.ortho(RenderTarget.active) - drawer.view = Matrix44.IDENTITY - drawer.defaults() - program.drawer.image(renderTarget!!.colorBuffer(0), 0.0, 0.0) - - drawCount++ - } - } -} - -class ControlManagerBuilder(val controlManager: ControlManager) { - fun styleSheet(selector: CompoundSelector, init: StyleSheet.() -> Unit): StyleSheet { - val styleSheet = StyleSheet(selector).apply { init() } - controlManager.layouter.styleSheets.addAll(styleSheet.flatten()) - return styleSheet - } - - fun styleSheets(styleSheets: List) { - controlManager.layouter.styleSheets.addAll(styleSheets.flatMap { it.flatten() }) - } - - @OptIn(ExperimentalContracts::class) - fun layout(init: Body.() -> Unit) { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - val body = Body(controlManager) - body.init() - controlManager.body = body - } -} - - -fun ControlManager.styleSheet(selector: CompoundSelector, init: StyleSheet.() -> Unit): StyleSheet { - val styleSheet = StyleSheet(selector).apply { init() } - layouter.styleSheets.addAll(styleSheet.flatten()) - return styleSheet -} - -fun ControlManager.styleSheets(styleSheets: List) { - layouter.styleSheets.addAll(styleSheets.flatMap { it.flatten() }) -} - -fun ControlManager.layout(init: Body.() -> Unit) { - val body = Body(this) - body.init() - this.body = body -} - -@OptIn(ExperimentalContracts::class) -fun Program.controlManager( - defaultStyles: List = defaultStyles(), - builder: ControlManagerBuilder.() -> Unit -): ControlManager { - contract { - callsInPlace(builder, InvocationKind.EXACTLY_ONCE) - } - val cm = ControlManager() - cm.program = this - cm.fontManager.register("default", resourceUrl("/fonts/Roboto-Regular.ttf")) - cm.layouter.styleSheets.addAll(defaultStyles.flatMap { it.flatten() }) - val cmb = ControlManagerBuilder(cm) - cmb.builder() - return cm -} - -private fun Element.any(function: (Element) -> Boolean): Boolean { - if (function(this)) { - return true - } else { - children.forEach { - if (it.any(function)) { - return true - } - } - return false - } -} - -private fun Element.anyVisible(function: (Element) -> Boolean): Boolean { - if (computedStyle.display != Display.NONE && function(this)) { - return true - } - - if (computedStyle.display != Display.NONE) { - children.forEach { - if (it.anyVisible(function)) { - return true - } - } - } - return false -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/FontManager.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/FontManager.kt deleted file mode 100644 index 43020928..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/FontManager.kt +++ /dev/null @@ -1,27 +0,0 @@ -package org.openrndr.panel - -import org.openrndr.Program -import org.openrndr.draw.FontImageMap -import org.openrndr.draw.loadFont -import org.openrndr.panel.style.LinearDimension -import org.openrndr.panel.style.StyleSheet -import org.openrndr.panel.style.fontFamily -import org.openrndr.panel.style.fontSize - -class FontManager() { - var program: Program? = null - val registry: MutableMap = mutableMapOf() - var contentScale: Double = 1.0 - - fun resolve(name: String): String? = registry[name] - - fun font(cs: StyleSheet): FontImageMap { - val fontUrl = resolve(cs.fontFamily) ?: "cp:fonts/Roboto-Medium.ttf" - val fontSize = (cs.fontSize as? LinearDimension.PX)?.value ?: 16.0 - return program?.loadFont(fontUrl, fontSize) ?: error("no program") - } - - fun register(name: String, url: String) { - registry[name] = url - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/collections/ObservableCopyOnWriteArrayList.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/collections/ObservableCopyOnWriteArrayList.kt deleted file mode 100644 index 017d2ac0..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/collections/ObservableCopyOnWriteArrayList.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.openrndr.panel.collections - -import org.openrndr.events.Event -import java.util.concurrent.CopyOnWriteArrayList - -class ObservableCopyOnWriteArrayList : CopyOnWriteArrayList() { - - val changed = Event>() - override fun add(element: E): Boolean { - return if (super.add(element)) { - changed.trigger(this) - true - } else { - false - } - } - - override fun remove(element: E): Boolean { - return if (super.remove(element)) { - changed.trigger(this) - true - } else { - false - } - } - - override fun clear() { - super.clear() - changed.trigger(this) - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/collections/ObservableHashSet.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/collections/ObservableHashSet.kt deleted file mode 100644 index bcb093c1..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/collections/ObservableHashSet.kt +++ /dev/null @@ -1,36 +0,0 @@ -package org.openrndr.panel.collections - -import org.openrndr.events.Event -import java.util.* - -class ObservableHashSet : HashSet() { - - class ChangeEvent(val source: ObservableHashSet, val added: Set, val removed: Set) - - val changed = Event>() - - override fun add(element: E): Boolean { - return if (super.add(element)) { - changed.trigger(ChangeEvent(this, setOf(element), emptySet())) - true - } else { - false - } - } - - override fun remove(element: E): Boolean { - return if (super.remove(element)) { - changed.trigger(ChangeEvent(this, emptySet(), setOf(element))) - true - } else { - false - } - } - - override fun clear() { - val old = this.toSet() - super.clear() - changed.trigger(ChangeEvent(this, emptySet(), old)) - } - -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Body.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Body.kt deleted file mode 100644 index 6f53b6be..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Body.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.openrndr.panel.elements - -import org.openrndr.panel.ControlManager - -class Body(val controlManager: ControlManager) : Element(ElementType("Body")) \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Button.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Button.kt deleted file mode 100644 index c0e3a5ff..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Button.kt +++ /dev/null @@ -1,108 +0,0 @@ -package org.openrndr.panel.elements - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Drawer - -import org.openrndr.draw.isolated -import org.openrndr.draw.loadFont -import org.openrndr.events.Event -import org.openrndr.extra.textwriter.TextWriter -import org.openrndr.panel.style.* -import org.openrndr.shape.Rectangle - -import kotlin.math.round - - -class Button : Element(ElementType("button")) { - - override val handlesKeyboardFocus = true - var label: String = "OK" - - class ButtonEvent(val source: Button) - class Events(val clicked: Event = Event()) - - var data: Any? = null - - val events = Events() - - init { - mouse.pressed.listen { - it.cancelPropagation() - } - - mouse.clicked.listen { - if (disabled !in pseudoClasses) { - events.clicked.trigger(ButtonEvent(this)) - } - it.cancelPropagation() - } - - keyboard.pressed.listen { - if (it.key == 32) { - it.cancelPropagation() - if (disabled !in pseudoClasses) { - events.clicked.trigger(ButtonEvent(this)) - } - } - } - } - - override val widthHint: Double - get() { - computedStyle.let { style -> - val fontUrl = (root() as? Body)?.controlManager?.fontManager?.resolve(style.fontFamily) ?: "broken" - val fontSize = (style.fontSize as? LinearDimension.PX)?.value ?: 14.0 - - val program = (root() as? Body)?.controlManager?.program ?: error("no program") - val fontMap = program.loadFont(fontUrl, fontSize) - - val writer = TextWriter(null) - - writer.box = Rectangle(0.0, - 0.0, - Double.POSITIVE_INFINITY, - Double.POSITIVE_INFINITY) - - writer.drawStyle.fontMap = fontMap - writer.newLine() - writer.text(label, visible = false) - - return writer.cursor.x - } - } - - override fun draw(drawer: Drawer) { - - computedStyle.let { - - drawer.pushTransforms() - drawer.pushStyle() - drawer.fill = ((it.background as? Color.RGBa)?.color ?: ColorRGBa.PINK) - - drawer.isolated { - drawer.stroke = computedStyle.effectiveBorderColor - drawer.strokeWeight = computedStyle.effectiveBorderWidth - drawer.rectangle(0.0, 0.0, layout.screenWidth, layout.screenHeight) - } - - (root() as? Body)?.controlManager?.fontManager?.let { - val font = it.font(computedStyle) - val writer = TextWriter(drawer) - drawer.fontMap = (font) - val textWidth = writer.textWidth(label) - val textHeight = font.ascenderLength - - val offset = round((layout.screenWidth - textWidth) / 2.0) - val yOffset = round((layout.screenHeight / 2) + textHeight / 2.0 - 2.0) * 1.0 - - drawer.fill = ((computedStyle.color as? Color.RGBa)?.color ?: ColorRGBa.WHITE).opacify( - if (disabled in pseudoClasses) 0.25 else 1.0 - ) - drawer.text(label, 0.0 + offset, 0.0 + yOffset) - } - - drawer.popStyle() - drawer.popTransforms() - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Canvas.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Canvas.kt deleted file mode 100644 index 0ae9526a..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Canvas.kt +++ /dev/null @@ -1,43 +0,0 @@ -package org.openrndr.panel.elements - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.math.Matrix44 - -class Canvas : Element(ElementType("canvas")) { - var userDraw: ((Drawer) -> Unit)? = null - private var renderTarget: RenderTarget? = null - - override fun draw(drawer: Drawer) { - val width = screenArea.width.toInt() - val height = screenArea.height.toInt() - - if (renderTarget != null) { - if (renderTarget?.width != width || renderTarget?.height != height) { - renderTarget?.colorBuffer(0)?.destroy() - renderTarget?.destroy() - renderTarget = null - } - } - - if (screenArea.width >= 1 && screenArea.height >= 1) { - if (renderTarget == null) { - renderTarget = renderTarget(screenArea.width.toInt(), screenArea.height.toInt(), drawer.context.contentScale) { - colorBuffer() - depthBuffer() - } - } - - renderTarget?.let { rt -> - drawer.isolatedWithTarget(rt) { - model = Matrix44.IDENTITY - view = Matrix44.IDENTITY - clear(ColorRGBa.TRANSPARENT) - ortho(rt) - userDraw?.invoke(this) - } - drawer.image(rt.colorBuffer(0), 0.0, 0.0) - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Colorpicker.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Colorpicker.kt deleted file mode 100644 index 5537c016..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Colorpicker.kt +++ /dev/null @@ -1,172 +0,0 @@ -package org.openrndr.panel.elements - -import org.openrndr.* -import org.openrndr.color.ColorHSVa -import org.openrndr.color.ColorRGBa -import org.openrndr.color.Linearity -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.Drawer -import org.openrndr.draw.colorBuffer -import org.openrndr.events.Event -import org.openrndr.panel.style.Color -import org.openrndr.panel.style.color - -class Colorpicker : Element { - - internal var colorMap: ColorBuffer? = null - - var label: String = "Color" - - var saturation = 0.5 - var color: ColorRGBa - set(value) { - realColor = value - saturation = color.toHSVa().s - generateColorMap() - draw.dirty = true - } - get() { - return realColor - } - - private var realColor = ColorRGBa.WHITE - private var focussed = false - - class ColorChangedEvent(val source: Colorpicker, - val oldColor: ColorRGBa, - val newColor: ColorRGBa) - - class Events { - val colorChanged = Event() - } - - val events = Events() - - private var keyboardInput = "" - private fun pick(e: MouseEvent) { - val dx = e.position.x - layout.screenX - var dy = e.position.y - layout.screenY - - dy = 50.0 - dy - val oldColor = color - val hsv = ColorHSVa(360.0 / layout.screenWidth * dx, saturation, dy / 50.0) - realColor = hsv.toRGBa() - draw.dirty = true - events.colorChanged.trigger(ColorChangedEvent(this, oldColor, realColor)) - e.cancelPropagation() - } - constructor() : super(ElementType("colorpicker")) { - generateColorMap() - - mouse.exited.listen { - focussed = false - } - - mouse.scrolled.listen { - if (colorMap != null) { - //if (focussed) { - saturation = (saturation - it.rotation.y * 0.01).coerceIn(0.0, 1.0) - generateColorMap() - colorMap?.shadow?.upload() - it.cancelPropagation() - pick(it) - requestRedraw() - //} - } - } - - keyboard.focusLost.listen { - keyboardInput = "" - draw.dirty = true - } - - keyboard.character.listen { - keyboardInput += it.character - draw.dirty = true - it.cancelPropagation() - } - - keyboard.pressed.listen { - - if (KeyModifier.CTRL in it.modifiers || KeyModifier.SUPER in it.modifiers) { - if (it.name == "v") { - (root() as Body).controlManager.program.clipboard.contents?.let { - keyboardInput += it - draw.dirty = true - } - it.cancelPropagation() - } - } - if (it.key == KEY_BACKSPACE) { - if (!keyboardInput.isEmpty()) { - keyboardInput = keyboardInput.substring(0, keyboardInput.length - 1) - draw.dirty = true - - } - it.cancelPropagation() - } - - if (it.key == KEY_ESCAPE) { - keyboardInput = "" - draw.dirty = true - it.cancelPropagation() - } - - - if (it.key == KEY_ENTER) { - val cleanKeyboardInput = keyboardInput.replace(Regex("^#"), "") - val number = if (cleanKeyboardInput.length == 6) cleanKeyboardInput.toIntOrNull(16) else null - - number?.let { - val r = (number shr 16) and 0xff - val g = (number shr 8) and 0xff - val b = number and 0xff - val oldColor = color - color = ColorRGBa(r / 255.0, g / 255.0, b / 255.0, 1.0, Linearity.SRGB) - events.colorChanged.trigger(ColorChangedEvent(this, oldColor, realColor)) - keyboardInput = "" - draw.dirty = true - } - it.cancelPropagation() - } - } - - - mouse.pressed.listen { it.cancelPropagation(); focussed = true } - mouse.clicked.listen { it.cancelPropagation(); pick(it); focussed = true; } - mouse.dragged.listen { it.cancelPropagation(); pick(it); focussed = true; } - } - - private fun generateColorMap() { - colorMap?.shadow?.let { - for (y in 0..49) { - for (x in 0 until it.colorBuffer.width) { - val hsv = ColorHSVa(360.0 / it.colorBuffer.width * x, saturation, (49 - y) / 49.0) - it.write(x, y, hsv.toRGBa().toLinear()) - } - } - it.upload() - } - } - - override fun draw(drawer: Drawer) { - if (colorMap == null) { - colorMap = colorBuffer(layout.screenWidth.toInt(), 50, 1.0) - generateColorMap() - } - - drawer.image(colorMap!!, 0.0, 0.0) - drawer.fill = color - drawer.stroke = null - drawer.shadeStyle = null - drawer.rectangle(0.0, 50.0, layout.screenWidth, 20.0) - - val f = (root() as? Body)?.controlManager?.fontManager?.font(computedStyle)!! - drawer.fontMap = f - drawer.fill = ((computedStyle.color as Color.RGBa).color) - - if (keyboardInput.isNotBlank()) { - drawer.text("input: $keyboardInput", 0.0, layout.screenHeight) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/ColorpickerButton.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/ColorpickerButton.kt deleted file mode 100644 index 21381057..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/ColorpickerButton.kt +++ /dev/null @@ -1,156 +0,0 @@ -package org.openrndr.panel.elements - -import kotlinx.coroutines.yield -import org.openrndr.color.ColorRGBa -import org.openrndr.color.Linearity -import org.openrndr.draw.Drawer -import org.openrndr.draw.LineCap - -import org.openrndr.events.Event -import org.openrndr.extra.textwriter.TextWriter -import org.openrndr.launch -import org.openrndr.panel.style.* - -import kotlin.reflect.KMutableProperty0 - -class ColorpickerButton : Element(ElementType("colorpicker-button")), DisposableElement { - override var disposed: Boolean = false - - var label: String = "OK" - var color: ColorRGBa = ColorRGBa(0.5, 0.5, 0.5, linearity = Linearity.SRGB) - set(value) { - if (value != field) { - field = value - requestRedraw() - events.valueChanged.trigger(ColorChangedEvent(this, value)) - } - } - - class ColorChangedEvent(val source: ColorpickerButton, val color: ColorRGBa) - - class Events { - val valueChanged = Event() - } - - val events = Events() - - init { - mouse.pressed.listen { - it.cancelPropagation() - } - mouse.clicked.listen { - append(SlideOut(0.0, screenArea.height, screenArea.width, 200.0, color, this)) - it.cancelPropagation() - } - } - - override fun append(element: Element) { - when (element) { - is Item, is SlideOut -> super.append(element) - else -> throw RuntimeException("only item and slideout") - } - super.append(element) - } - - fun items(): List = children.filter { it is Item }.map { it as Item } - - override fun draw(drawer: Drawer) { - - drawer.fill = ((computedStyle.background as? Color.RGBa)?.color ?: ColorRGBa.PINK) - drawer.stroke = null - drawer.strokeWeight = 0.0 - drawer.rectangle(0.0, 0.0, screenArea.width, screenArea.height) - - (root() as? Body)?.controlManager?.fontManager?.let { - val font = it.font(computedStyle) - - val writer = TextWriter(drawer) - drawer.fontMap = (font) - - val text = "$label" - - val textWidth = writer.textWidth(text) - val textHeight = font.ascenderLength - - val offset = Math.round((layout.screenWidth - textWidth) / 2.0) - val yOffset = Math.round((layout.screenHeight / 2) + textHeight / 2.0) - 2.0 - - drawer.fill = (computedStyle.color as? Color.RGBa)?.color ?: ColorRGBa.WHITE - drawer.fontMap = font - drawer.text(text, 0.0 + offset, 0.0 + yOffset) - drawer.stroke = color - drawer.pushStyle() - drawer.strokeWeight = 4.0 - drawer.lineCap = LineCap.ROUND - drawer.lineSegment(2.0, layout.screenHeight - 2.0, layout.screenWidth - 2.0, layout.screenHeight - 2.0) - drawer.popStyle() - } - } - - class SlideOut(val x: Double, val y: Double, val width: Double, val height: Double, color: ColorRGBa, parent: Element) : Element(ElementType("slide-out")) { - - init { - style = StyleSheet(CompoundSelector.DUMMY).apply { - position = Position.ABSOLUTE - left = LinearDimension.PX(x) - top = LinearDimension.PX(y) - width = LinearDimension.PX(this@SlideOut.width) - height = LinearDimension.Auto//LinearDimension.PX(this@SlideOut.height) - overflow = Overflow.Scroll - zIndex = ZIndex.Value(1000) - background = Color.RGBa(ColorRGBa(0.3, 0.3, 0.3)) - } - - val colorPicker = Colorpicker().apply { - this.color = color - label = (parent as ColorpickerButton).label - events.colorChanged.listen { - parent.color = it.newColor - parent.events.valueChanged.trigger(ColorChangedEvent(parent, parent.color)) - } - } - append(colorPicker) - - mouse.exited.listen { - dispose() - } - } - - override fun draw(drawer: Drawer) { - (root() as Body).controlManager.keyboardInput.requestFocus(children[0]) - drawer.fill = ((computedStyle.background as? Color.RGBa)?.color ?: ColorRGBa.PINK) - drawer.rectangle(0.0, 0.0, screenArea.width, screenArea.height) - } - - fun dispose() { - parent?.remove(this) - } - } -} - -fun ColorpickerButton.bind(property: KMutableProperty0) { - var currentValue: ColorRGBa? = null - - events.valueChanged.listen { - currentValue = color - property.set(it.color) - } - if (root() as? Body == null) { - throw RuntimeException("no body") - } - - fun update() { - if (property.get() != currentValue) { - val lcur = property.get() - currentValue = lcur - color = lcur - } - } - update() - (root() as? Body)?.controlManager?.program?.launch { - while (!disposed) { - update() - yield() - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Div.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Div.kt deleted file mode 100644 index 4e523608..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Div.kt +++ /dev/null @@ -1,43 +0,0 @@ -package org.openrndr.panel.elements - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Drawer -import org.openrndr.panel.style.* -import kotlin.math.max - -open class Div : TextElement(ElementType("div")) { - init { - mouse.pressed.listen { - if (computedStyle.effectiveBackground?.alpha?:0.0 > 0.0) { - it.cancelPropagation() - } - } - mouse.scrolled.listen { - computedStyle.let { cs -> - if (cs.overflow != Overflow.Visible) { - scrollTop -= it.rotation.y * 10 - scrollTop = max(0.0, scrollTop) - draw.dirty = true - it.cancelPropagation() - } - } - } - } - - override fun draw(drawer: Drawer) { - computedStyle.let { style -> - style.background.let { - drawer.fill = ((it as? Color.RGBa)?.color ?: ColorRGBa.BLACK) - drawer.stroke = null - drawer.strokeWeight = 0.0 - //drawer.smooth(false) - drawer.rectangle(0.0, 0.0, layout.screenWidth, layout.screenHeight) - //drawer.smooth(true) - } - } - } - - override fun toString(): String { - return "Div(id=${id})" - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/DropdownButton.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/DropdownButton.kt deleted file mode 100644 index 0fc920ba..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/DropdownButton.kt +++ /dev/null @@ -1,287 +0,0 @@ -package org.openrndr.panel.elements - -import kotlinx.coroutines.yield -import org.openrndr.KEY_ARROW_DOWN -import org.openrndr.KEY_ARROW_UP -import org.openrndr.KEY_ENTER -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Drawer -import org.openrndr.draw.loadFont -import org.openrndr.events.Event -import org.openrndr.extra.textwriter.TextWriter -import org.openrndr.launch -import org.openrndr.panel.style.* -import org.openrndr.shape.Rectangle -import kotlin.math.max -import kotlin.math.min -import kotlin.math.roundToInt -import kotlin.reflect.KMutableProperty0 - -class Item : Element(ElementType("item")) { - var label: String = "" - var data: Any? = null - - class PickedEvent(val source: Item) - - class Events { - val picked = Event() - } - - val events = Events() - - fun picked() { - events.picked.trigger(PickedEvent(this)) - } -} - -class DropdownButton : Element(ElementType("dropdown-button")), DisposableElement { - override var disposed = false - - var label: String = "OK" - var value: Item? = null - - class ValueChangedEvent(val source: DropdownButton, val value: Item) - - class Events { - val valueChanged = Event() - } - - val events = Events() - - init { - mouse.pressed.listen { - it.cancelPropagation() - } - - mouse.clicked.listen { - val itemCount = items().size - - if (children.none { it is SlideOut }) { - val height = min(240.0, itemCount * 24.0) - if (screenPosition.y < root().layout.screenHeight - height) { - val so = SlideOut(0.0, screenArea.height, screenArea.width, height, this, value) - append(so) - (root() as Body).controlManager.keyboardInput.requestFocus(so) - } else { - val so = SlideOut(0.0, screenArea.height - height, screenArea.width, height, this, value) - append(so) - (root() as Body).controlManager.keyboardInput.requestFocus(so) - } - } else { - (children.first { it is SlideOut } as SlideOut?)?.dispose() - } - } - } - - override val widthHint: Double? - get() { - computedStyle.let { style -> - val fontUrl = (root() as? Body)?.controlManager?.fontManager?.resolve(style.fontFamily) ?: "broken" - val fontSize = (style.fontSize as? LinearDimension.PX)?.value ?: 16.0 - - val program = (root() as? Body)?.controlManager?.program ?: error("no program") - val fontMap = program.loadFont(fontUrl, fontSize) - - val writer = TextWriter(null) - - writer.box = Rectangle(0.0, - 0.0, - Double.POSITIVE_INFINITY, - Double.POSITIVE_INFINITY) - - val text = "$label ${(value?.label) ?: ""}" - writer.drawStyle.fontMap = fontMap - writer.newLine() - writer.text(text, visible = false) - - return writer.cursor.x + 10.0 - } - } - - - override fun append(element: Element) { - when (element) { - is Item, is SlideOut -> super.append(element) - else -> throw RuntimeException("only item and slideout") - } - super.append(element) - } - - fun items(): List = children.filterIsInstance().map { it } - - override fun draw(drawer: Drawer) { - - drawer.fill = ((computedStyle.background as? Color.RGBa)?.color ?: ColorRGBa.PINK) - drawer.stroke = null - drawer.rectangle(0.0, 0.0, screenArea.width, screenArea.height) - - (root() as? Body)?.controlManager?.fontManager?.let { - val font = it.font(computedStyle) - - val writer = TextWriter(drawer) - drawer.fontMap = (font) - - val text = (value?.label) ?: "" - - val textWidth = writer.textWidth(text) - val textHeight = font.ascenderLength - - val offset = Math.round((layout.screenWidth - textWidth)) - val yOffset = ((layout.screenHeight / 2) + textHeight / 2.0).roundToInt() - 2.0 - - drawer.fill = ((computedStyle.color as? Color.RGBa)?.color ?: ColorRGBa.WHITE) - - drawer.text(label, 5.0, 0.0 + yOffset) - drawer.text(text, -5.0 + offset, 0.0 + yOffset) - } - } - - class SlideOut(val x: Double, val y: Double, val width: Double, val height: Double, parent: Element, active: Item?) : Element(ElementType("slide-out")) { - init { - - val itemButtons = mutableMapOf() - - var activeIndex = - if (active != null) { - (parent as DropdownButton).items().indexOf(active) - } else { - -1 - } - - keyboard.pressed.listen { - - if (it.key == KEY_ENTER) { - it.cancelPropagation() - dispose() - } - - if (it.key == KEY_ARROW_DOWN) { - activeIndex = (activeIndex + 1).coerceAtMost((parent as DropdownButton).items().size - 1) - it.cancelPropagation() - val newValue = parent.items()[activeIndex] - - parent.value?.let { item -> - itemButtons[item]?.pseudoClasses?.remove(ElementPseudoClass("selected")) - } - parent.value?.let { - itemButtons[newValue]?.pseudoClasses?.add(ElementPseudoClass("selected")) - } - - parent.value = newValue - parent.events.valueChanged.trigger(ValueChangedEvent(parent, newValue)) - newValue.picked() - draw.dirty = true - - val ypos = 24.0 * activeIndex - if (ypos >= scrollTop + 10 * 24.0) { - scrollTop += 24.0 - } - - } - - if (it.key == KEY_ARROW_UP) { - activeIndex = (activeIndex - 1).coerceAtLeast(0) - - - val newValue = (parent as DropdownButton).items()[activeIndex] - - val ypos = 24.0 * activeIndex - if (ypos < scrollTop) { - scrollTop -= 24.0 - } - - parent.value?.let { item -> - itemButtons[item]?.pseudoClasses?.remove(ElementPseudoClass("selected")) - } - parent.value?.let { - itemButtons[newValue]?.pseudoClasses?.add(ElementPseudoClass("selected")) - } - - parent.value = newValue - parent.events.valueChanged.trigger(ValueChangedEvent(parent, newValue)) - newValue.picked() - draw.dirty = true - } - } - - mouse.scrolled.listen { - scrollTop -= it.rotation.y - scrollTop = max(0.0, scrollTop) - draw.dirty = true - it.cancelPropagation() - } - - mouse.exited.listen { - it.cancelPropagation() - dispose() - } - - style = StyleSheet(CompoundSelector.DUMMY).apply { - position = Position.ABSOLUTE - left = LinearDimension.PX(x) - top = LinearDimension.PX(y) - width = LinearDimension.PX(this@SlideOut.width) - height = LinearDimension.PX(this@SlideOut.height) - overflow = Overflow.Scroll - zIndex = ZIndex.Value(1000) - background = Color.Inherit - } - - (parent as DropdownButton).items().forEach { - append(Button().apply { - data = it - label = it.label - itemButtons[it] = this - events.clicked.listen { - parent.value = it.source.data as Item - parent.events.valueChanged.trigger(ValueChangedEvent(parent, it.source.data as Item)) - (data as Item).picked() - dispose() - } - }) - } - active?.let { - itemButtons[active]?.pseudoClasses?.add(ElementPseudoClass("selected")) - } - } - - override fun draw(drawer: Drawer) { - drawer.fill = ((computedStyle.background as? Color.RGBa)?.color ?: ColorRGBa.PINK) - drawer.stroke = null - drawer.strokeWeight = 0.0 - drawer.rectangle(0.0, 0.0, screenArea.width, screenArea.height) - drawer.strokeWeight = 1.0 - } - - fun dispose() { - parent?.remove(this) - } - } -} - -fun > DropdownButton.bind(property: KMutableProperty0, map: Map) { - val options = mutableMapOf() - map.forEach { (k, v) -> - options[k] = item { - label = v - events.picked.listen { - property.set(k) - } - } - } - var currentValue = property.get() - value = options[currentValue] - draw.dirty = true - - (root() as? Body)?.controlManager?.program?.launch { - while (!disposed) { - val cval = property.get() - if (cval != currentValue) { - currentValue = cval - value = options[cval] - draw.dirty = true - } - yield() - } - } -} diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Element.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Element.kt deleted file mode 100644 index 5500cff4..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Element.kt +++ /dev/null @@ -1,376 +0,0 @@ -package org.openrndr.panel.elements - -import org.openrndr.* -import org.openrndr.draw.Drawer -import org.openrndr.events.Event -import org.openrndr.math.Vector2 -import org.openrndr.panel.collections.ObservableCopyOnWriteArrayList -import org.openrndr.panel.collections.ObservableHashSet -import org.openrndr.panel.style.CompoundSelector -import org.openrndr.panel.style.Display -import org.openrndr.panel.style.StyleSheet -import org.openrndr.panel.style.display -import org.openrndr.shape.Rectangle - -import java.util.* - -@JvmRecord -data class ElementClass(val name: String) -@JvmRecord -data class ElementPseudoClass(val name: String) -@JvmRecord -data class ElementType(val name: String) - -val disabled = ElementPseudoClass("disabled") - -class FocusEvent - -interface DisposableElement { - var disposed: Boolean - - fun dispose() { - disposed = true - } -} - -open class Element(val type: ElementType) { - - var scrollTop = 0.0 - open val handlesDoubleClick = false - open val handlesKeyboardFocus = false - - open val widthHint: Double? - get() { - return null - } - - - open val heightHint: Double? - get() { - return null - } - - class MouseObservables { - val clicked = Event("element-mouse-clicked") - val doubleClicked = Event("element-mouse-double-clicked") - val entered = Event("element-mouse-entered") - val exited = Event("element-mouse-exited") - val dragged = Event("element-mouse-dragged") - val moved = Event("element-mouse-moved") - val scrolled = Event("element-mouse-scrolled") - val pressed = Event("element-mouse-pressed") - } - - class DropObserverables { - val dropped = Event("element-dropped") - } - - val drop = DropObserverables() - val mouse = MouseObservables() - - class KeyboardObservables { - val pressed = Event("element-keyboard-pressed") - val released = Event("element-keyboard-released") - val repeated = Event("element-keyboard-repeated") - val character = Event("element-keyboard-character") - val focusGained = Event("element-keyboard-focus-gained") - val focusLost = Event("element-keyboard-focus-lost") - } - - val keyboard = KeyboardObservables() - - class Layout { - var zIndex = 0 - var screenX = 0.0 - var screenY = 0.0 - var screenWidth = 0.0 - var screenHeight = 0.0 - var growWidth = 0.0 - var growHeight = 0.0 - override fun toString(): String { - return "Layout(screenX=$screenX, screenY=$screenY, screenWidth=$screenWidth, screenHeight=$screenHeight, growWidth=$growWidth, growHeight=$growHeight)" - } - } - - class Draw { - var dirty = true - } - - val draw = Draw() - val layout = Layout() - - class ClassEvent(val source: Element, val `class`: ElementClass) - class ClassObserverables { - val classAdded = Event("element-class-added") - val classRemoved = Event("element-class-removed") - } - - val classEvents = ClassObserverables() - - - var id: String? = null - val classes: ObservableHashSet = ObservableHashSet() - val pseudoClasses: ObservableHashSet = ObservableHashSet() - - var parent: Element? = null - val children: ObservableCopyOnWriteArrayList = ObservableCopyOnWriteArrayList() - get() = field - - var computedStyle: StyleSheet = StyleSheet(CompoundSelector.DUMMY) - var style: StyleSheet? = null - - init { - pseudoClasses.changed.listen { - draw.dirty = true - } - classes.changed.listen { - draw.dirty = true - it.added.forEach { - classEvents.classAdded.trigger(ClassEvent(this, it)) - } - it.removed.forEach { - classEvents.classRemoved.trigger(ClassEvent(this, it)) - } - - } - - children.changed.listen { - draw.dirty = true - } - } - - - /** - * Determines whether the current element, or any of its ancestors, has a display style of `Display.NONE`. - * - * @return `true` if the element or any of its ancestors is hidden (has `Display.NONE` style), `false` otherwise. - */ - fun isHidden() : Boolean { - var current: Element? = this - - while (current != null) { - if (current.computedStyle.display == Display.NONE) { - return true - } - current = current.parent - } - - return false - } - - fun root(): Element { - return parent?.root() ?: this - } - - open fun append(element: Element) { - if (element !in children) { - element.parent = this - children.add(element) - } - } - - fun remove(element: Element) { - if (element in children) { - element.parent = null - children.remove(element) - } - } - - open fun draw(drawer: Drawer) { - - } - - fun filter(f: (Element) -> Boolean): List { - val result = ArrayList() - val stack = Stack() - - stack.add(this) - while (!stack.isEmpty()) { - val node = stack.pop() - if (f(node)) { - result.add(node) - stack.addAll(node.children) - } - } - return result - } - - fun flatten(): List { - val result = ArrayList() - val stack = Stack() - - stack.add(this) - while (!stack.isEmpty()) { - val node = stack.pop() - - result.add(node) - stack.addAll(node.children) - } - return result - } - - fun previousSibling(): Element? { - parent?.let { p -> - p.childIndex(this)?.let { - if (it > 0) { - return p.children[it - 1] - } - } - } - return null - } - - fun childIndex(element: Element): Int? { - if (element in children) { - return children.indexOf(element) - } else { - return null - } - } - - fun ancestors(): List { - var c = this - val result = ArrayList() - - while (c.parent != null) { - c.parent?.let { - result.add(it) - c = it - } - } - return result - } - - fun previous(): Element? { - return parent?.let { p -> - val index = p.children.indexOf(this) - when (index) { - -1, 0 -> null - else -> p.children[index - 1] - } - } - } - - fun next(): Element? { - return parent?.let { p -> - when (val index = p.children.indexOf(this)) { - -1, p.children.size - 1 -> null - else -> p.children[index + 1] - } - } - } - - fun findNext(premise: (Element) -> Boolean): Element? { - return parent?.let { p -> - val index = p.children.indexOf(this) - val siblingCount = p.children.size - for (i in index + 1 until siblingCount) { - if (premise(p.children[i])) { - return p.children[i] - } - } - return null - } - } - - fun findPrevious(premise: (Element) -> Boolean): Element? { - return parent?.let { p -> - val index = p.children.indexOf(this) - for (i in index - 1 downTo 0) { - if (premise(p.children[i])) { - return p.children[i] - } - } - return null - } - } - - - fun move(steps: Int) { - parent?.let { p -> - if (steps != 0) { - val index = p.children.indexOf(this) - p.children.add(index + steps, this) - if (steps > 0) { - p.children.removeAt(index) - } else { - p.children.removeAt(index + 1) - } - } - } - } - - fun findFirst(element: Element, matches: (Element) -> Boolean): Element? { - if (matches.invoke(element)) { - return element - } else { - element.children.forEach { c -> - findFirst(c, matches)?.let { return it } - } - return null - } - } - - inline fun elementWithId(id: String): T? { - return findFirst(this) { e -> e.id == id && e is T } as T - } - - val screenPosition: Vector2 - get() = Vector2(layout.screenX, layout.screenY) - - val screenArea: Rectangle - get() = Rectangle(Vector2(layout.screenX, - layout.screenY), - layout.screenWidth, - layout.screenHeight) - - -} - -fun Element.requestRedraw() { - draw.dirty = true -} - -fun Element.disable() { - pseudoClasses.add(disabled) - requestRedraw() -} - -fun Element.enable() { - pseudoClasses.remove(disabled) - requestRedraw() -} - -fun Element.isDisabled(): Boolean = disabled in pseudoClasses - -fun Element.findAll(predicate: (Element) -> Boolean): List { - val results = mutableListOf() - visit { - if (predicate(this)) { - results.add(this) - } - } - return results -} - -fun Element.findAllVisible(predicate: (Element) -> Boolean): List { - val results = mutableListOf() - visitVisible { - if (predicate(this)) { - results.add(this) - } - } - return results -} - -fun Element.visit(function: Element.() -> Unit) { - this.function() - children.forEach { it.visit(function) } -} - -fun Element.visitVisible(function: Element.() -> Unit) { - if (this.computedStyle.display != Display.NONE) { - this.function() - children.forEach { it.visitVisible(function) } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/EnvelopeButton.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/EnvelopeButton.kt deleted file mode 100644 index 52a80578..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/EnvelopeButton.kt +++ /dev/null @@ -1,141 +0,0 @@ -package org.openrndr.panel.elements - -import org.openrndr.color.ColorRGBa - -import org.openrndr.draw.Drawer -import org.openrndr.extra.textwriter.Cursor -import org.openrndr.extra.textwriter.TextWriter - -import org.openrndr.math.Vector2 -import org.openrndr.panel.style.* - -class EnvelopeButton : Element(ElementType("envelope-button")) { - - var label = "OK" - var envelope = Envelope() - set(value) { - field = value - envelopeSubscription?.let { - value.events.envelopeChanged.cancel(it) - } - envelopeSubscription = value.events.envelopeChanged.listen { - draw.dirty = true - } - } - - - var envelopeSubscription: ((Envelope.EnvelopeChangedEvent)->Unit)? = null - - init { - mouse.clicked.listen { - append(SlideOut(0.0, screenArea.height, screenArea.width, 200.0, this)) - } - envelopeSubscription = envelope.events.envelopeChanged.listen { - draw.dirty = true - } - } - - override fun append(element: Element) { - when (element) { - is Item, is SlideOut -> super.append(element) - else -> throw RuntimeException("only item and slideout") - } - super.append(element) - } - - fun items(): List = children.filter { it is Item }.map { it as Item } - - override fun draw(drawer: Drawer) { - drawer.fill = ((computedStyle.background as? Color.RGBa)?.color ?: ColorRGBa.PINK) - drawer.rectangle(0.0, 0.0, screenArea.width, screenArea.height) - - (root() as? Body)?.controlManager?.fontManager?.let { - var chartHeight = 0.0 - - (root() as? Body)?.controlManager?.fontManager?.let { - val font = it.font(computedStyle) - - val writer = TextWriter(drawer) - drawer.fontMap = (font) - drawer.fill = (ColorRGBa.BLACK) - writer.cursor = Cursor(0.0,layout.screenHeight - 4.0) - chartHeight = writer.cursor.y - font.height-4 - writer.text("$label") - } - - - val w = layout.screenWidth - val h = chartHeight - val m = envelope.points.map { - val v = (Vector2(w, h) * it) - Vector2(v.x, h - v.y) - } - - if (m.size > 1) { - drawer.stroke = (ColorRGBa.WHITE) - drawer.strokeWeight = (2.0) - drawer.lineStrip(m) - } - if (m.size == 1) { - drawer.stroke = (ColorRGBa.WHITE) - drawer.strokeWeight = (2.0) - drawer.lineSegment(0.0, m[0].y, layout.screenWidth, m[0].y) - } - - drawer.stroke = (ColorRGBa.BLACK.opacify(0.25)) - drawer.strokeWeight = (1.0) - drawer.lineSegment(envelope.offset * w, 0.0, envelope.offset * w, chartHeight) - - drawer.lineSegment(0.0, 0.0, 3.0, 0.0) - drawer.lineSegment(0.0, 0.0, 0.0, chartHeight) - drawer.lineSegment(0.0, chartHeight, 3.0, chartHeight) - - drawer.lineSegment(w, 0.0, w-3.0, 0.0) - drawer.lineSegment(w, 0.0, w, chartHeight) - drawer.lineSegment(w, chartHeight, w-3.0, chartHeight) - } - } - - - class SlideOut(val x: Double, val y: Double, val width: Double, val height: Double, parent: EnvelopeButton) : Element(ElementType("envelope-slide-out")) { - - init { - - mouse.clicked.listen { - it.cancelPropagation() - } - style = StyleSheet(CompoundSelector.DUMMY).apply { - position = Position.ABSOLUTE - left = LinearDimension.PX(x) - top = LinearDimension.PX(y) - width = LinearDimension.PX(this@SlideOut.width) - height = LinearDimension.Auto//LinearDimension.PX(this@SlideOut.height) - overflow = Overflow.Scroll - zIndex = ZIndex.Value(1) - background = Color.RGBa(ColorRGBa(0.3, 0.3, 0.3)) - } - - append(EnvelopeEditor().apply { - envelope = parent.envelope - }) - - append(Button().apply { - label = "done" - events.clicked.listen { - //parent.value = it.source.data as Item - //parent.events.valueChanged.onNext(ValueChangedEvent(parent, it.source.data as Item)) - dispose() - } - }) - } - - override fun draw(drawer: Drawer) { - drawer.fill = ((computedStyle.background as? Color.RGBa)?.color ?: ColorRGBa.PINK) - drawer.rectangle(0.0, 0.0, screenArea.width, screenArea.height) - } - - fun dispose() { - parent?.remove(this) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/EnvelopeEditor.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/EnvelopeEditor.kt deleted file mode 100644 index 54e3d9ce..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/EnvelopeEditor.kt +++ /dev/null @@ -1,229 +0,0 @@ -package org.openrndr.panel.elements - -import org.openrndr.MouseButton -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Drawer -import org.openrndr.math.Vector2 -import org.openrndr.KeyModifier -import org.openrndr.events.Event - -class Envelope(constant:Double = 0.5) { - - val points = mutableListOf(Vector2(0.5, constant)) - var activePoint: Vector2? = null - - var offset:Double = 0.0 - set(value) { field = value; events.envelopeChanged.trigger(EnvelopeChangedEvent(this))} - - class EnvelopeChangedEvent(val envelope: Envelope) - - class Events { - val envelopeChanged = Event("envelope-changed") - } - val events = Events() - - fun insertPoint(v: Vector2) { - for (i in 0 until points.size) { - if (points[i].x > v.x) { - points.add(i, v) - activePoint = v - events.envelopeChanged.trigger(EnvelopeChangedEvent(this)) - return - } - } - points.add(v) - activePoint = v - fixBounds() - events.envelopeChanged.trigger(EnvelopeChangedEvent(this)) - } - - fun findNearestPoint(v: Vector2) = points.minByOrNull { (it - v).length } - - fun removePoint(v: Vector2) { - points.remove(v) - if (v === activePoint) { - activePoint = null - } - fixBounds() - events.envelopeChanged.trigger(EnvelopeChangedEvent(this)) - } - - private fun fixBounds() { - if (points.size >= 2) { - if (points[0].x != 0.0) { - points[0].copy(x=0.0).let { - if (activePoint === points[0]) { - activePoint = it - } - points[0] = it - } - } - if (points[points.size-1].x != 1.0) { - points[points.size-1].copy(x=1.0).let { - if (activePoint === points[points.size-1]) { - activePoint = it - } - points[points.size-1] = it - } - } - } - } - - fun updatePoint(old: Vector2, new: Vector2) { - val index = points.indexOf(old) - if (index != -1) { - points[index] = new - } - if (old === activePoint) { - activePoint = new - } - points.sortBy { it.x } - - fixBounds() - events.envelopeChanged.trigger(EnvelopeChangedEvent(this)) - } - - fun value(t: Double): Double { - - val st = t.coerceIn(0.0, 1.0) - - if (points.size == 1) { - return points[0].y - } - else if (points.size == 2) { - return points[0].y * (1.0-st) + points[1].y * st - } else { - if (st == 0.0) { - return points[0].y - } - if (st == 1.0) { - return points[points.size-1].y - } - - for (i in 0 until points.size-1) { - if (points[i].x <= st && points[i+1].x > st) { - val left = points[i] - var right = points[i+1] - - val dt = right.x - left.x - if (dt > 0.0) { - val f = (t - left.x) / dt - return left.y * (1.0-f) + right.y * f - } else { - return left.y - } - - } - } - return points[0].y - - } - - } - -} - -// -- - -class EnvelopeEditor : Element(ElementType("envelope-editor")) { - - var envelope = Envelope() - - init { - - fun query(position: Vector2): Vector2 { - val x = (position.x - layout.screenX) / layout.screenWidth - val y = 1.0 - ((position.y - layout.screenY) / layout.screenHeight) - - return Vector2(x, y) - } - - mouse.clicked.listen { - val query = query(it.position) - val nearest = envelope.findNearestPoint(query) - val distance = nearest?.let { (it - query).length } - - if (it.button == MouseButton.LEFT && !it.modifiers.contains(KeyModifier.CTRL)) { - when { - distance == null -> { - envelope.insertPoint(query) - draw.dirty = true - } - distance < 0.05 -> { - envelope.activePoint = nearest - } - else -> { - envelope.insertPoint(query) - draw.dirty = true - } - } - } else if (it.button == MouseButton.LEFT) { - if (distance != null && distance < 0.1) { - envelope.removePoint(nearest) - draw.dirty = true - } - } - it.cancelPropagation() - } - - mouse.pressed.listen { - val query = query(it.position) - val nearest = envelope.findNearestPoint(query) - val distance = nearest?.let { it - query }?.length - - if (distance == null) { - envelope.activePoint = null - draw.dirty = true - } else if (distance < 0.1) { - envelope.activePoint = nearest - } else { - envelope.activePoint = null - } - it.cancelPropagation() - } - - mouse.dragged.listen { - envelope.activePoint?.let { activePoint -> - val query = query(it.position) - if (!it.modifiers.contains(KeyModifier.SHIFT)) { - envelope.updatePoint(activePoint, query) - } else { - envelope.updatePoint(activePoint, Vector2(activePoint.x, query.y)) - } - draw.dirty = true - } - it.cancelPropagation() - } - } - - override fun draw(drawer: Drawer) { - val w = layout.screenWidth - val h = layout.screenHeight - - val m = envelope.points.map { - val v = (it * Vector2(w, h)) - Vector2(v.x, h - v.y) - } - - drawer.stroke = (ColorRGBa.BLACK.opacify(0.25)) - drawer.strokeWeight = (1.0) - drawer.lineSegment(layout.screenWidth/2.0, 0.0, layout.screenWidth/2.0,layout.screenHeight) - drawer.lineSegment(0.0,layout.screenHeight/2.0,layout.screenWidth, layout.screenHeight/2.0) - - if (m.size > 1) { - drawer.stroke = (ColorRGBa.WHITE) - drawer.strokeWeight = (2.0) - drawer.lineStrip(m) - drawer.fill = (ColorRGBa.WHITE) - drawer.stroke = null - drawer.circles(m, 4.0) - } else if (m.size == 1) { - drawer.stroke = (ColorRGBa.WHITE) - drawer.strokeWeight = (2.0) - drawer.lineSegment(0.0, m[0].y, layout.screenWidth, m[0].y) - drawer.fill = (ColorRGBa.WHITE) - drawer.stroke = null - drawer.circle(m[0], 4.0) - } - } -} diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/LayoutBuilder.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/LayoutBuilder.kt deleted file mode 100644 index 28d89bc0..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/LayoutBuilder.kt +++ /dev/null @@ -1,248 +0,0 @@ -package org.openrndr.panel.elements - -import org.openrndr.draw.Drawer -import org.openrndr.panel.ControlManager -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract - -@OptIn(ExperimentalContracts::class) -fun Element.layout(init: Element.() -> Unit) { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - init() -} - -@OptIn(ExperimentalContracts::class) -fun layout(controlManager: ControlManager, init: Body.() -> Unit): Body { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - val body = Body(controlManager) - body.init() - return body -} - -@OptIn(ExperimentalContracts::class) -fun Element.initElement(classes: Array, element: T, init: T.() -> Unit): T { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - - append(element) - element.classes.addAll(classes.map { ElementClass(it) }) - element.init() - return element -} - -@OptIn(ExperimentalContracts::class) -fun Element.button(vararg classes: String, label: String = "button", init: Button.() -> Unit): Button { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - val button = Button().apply { - this.classes.addAll(classes.map { ElementClass(it) }) - this.id = id - this.label = label - } - initElement(classes, button, init) - return button -} - -fun Button.clicked(listener: (Button.ButtonEvent) -> Unit) { - events.clicked.listen(listener) -} - -@OptIn(ExperimentalContracts::class) -fun Element.slider(vararg classes: String, init: Slider.() -> Unit) : Slider { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, Slider(), init) as Slider -} - -@OptIn(ExperimentalContracts::class) -fun Element.toggle(vararg classes: String, init: Toggle.() -> Unit): Toggle { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, Toggle(), init) as Toggle -} - -@OptIn(ExperimentalContracts::class) -fun Element.colorpicker(vararg classes: String, init: Colorpicker.() -> Unit): Colorpicker { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, Colorpicker(), init) -} - -@OptIn(ExperimentalContracts::class) -fun Element.colorpickerButton(vararg classes: String, init: ColorpickerButton.() -> Unit): ColorpickerButton { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, ColorpickerButton(), init) -} - - -@OptIn(ExperimentalContracts::class) -fun Element.xyPad(vararg classes: String, init: XYPad.() -> Unit): XYPad { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, XYPad(), init) as XYPad -} - -fun Canvas.draw(f: (Drawer) -> Unit) { - this.userDraw = f -} - -@OptIn(ExperimentalContracts::class) -fun Element.canvas(vararg classes: String, init: Canvas.() -> Unit): Canvas { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - val canvas = Canvas() - classes.forEach { canvas.classes.add(ElementClass(it)) } - canvas.init() - append(canvas) - return canvas -} - -@OptIn(ExperimentalContracts::class) -fun Element.dropdownButton( - vararg classes: String, - id: String? = null, - label: String = "button", - init: DropdownButton.() -> Unit -): DropdownButton { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, DropdownButton().apply { - this.id = id - this.label = label - }, init) -} - -@OptIn(ExperimentalContracts::class) -fun Element.envelopeButton(vararg classes: String, init: EnvelopeButton.() -> Unit): EnvelopeButton { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, EnvelopeButton().apply {}, init) -} - -@OptIn(ExperimentalContracts::class) -fun Element.envelopeEditor(vararg classes: String, init: EnvelopeEditor.() -> Unit): EnvelopeEditor { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, EnvelopeEditor().apply {}, init) -} - -@OptIn(ExperimentalContracts::class) -fun Element.sequenceEditor(vararg classes: String, init: SequenceEditor.() -> Unit): SequenceEditor { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, SequenceEditor().apply {}, init) -} -@OptIn(ExperimentalContracts::class) -fun Element.slidersVector2(vararg classes: String, init: SlidersVector2.() -> Unit): SlidersVector2 { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, SlidersVector2().apply {}, init) -} - - -@OptIn(ExperimentalContracts::class) -fun Element.slidersVector3(vararg classes: String, init: SlidersVector3.() -> Unit): SlidersVector3 { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, SlidersVector3().apply {}, init) -} - -@OptIn(ExperimentalContracts::class) -fun Element.slidersVector4(vararg classes: String, init: SlidersVector4.() -> Unit): SlidersVector4 { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, SlidersVector4().apply {}, init) -} - - -@OptIn(ExperimentalContracts::class) -fun Element.textfield(vararg classes: String, init: Textfield.() -> Unit): Textfield { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return initElement(classes, Textfield(), init) -} - -@OptIn(ExperimentalContracts::class) -fun DropdownButton.item(init: Item.() -> Unit): Item { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - val item = Item().apply(init) - append(item) - return item -} - -@OptIn(ExperimentalContracts::class) -fun Element.div(vararg classes: String, init: Div.() -> Unit): Div { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - val div = Div() - initElement(classes, div, init) - return div -} - -@OptIn(ExperimentalContracts::class) -inline fun Element.textElement(classes: Array, init: T.() -> String): T { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - @Suppress("DEPRECATION") val te = T::class.java.newInstance() - te.classes.addAll(classes.map { ElementClass(it) }) - te.text(te.init()) - append(te) - return te -} - -@OptIn(ExperimentalContracts::class) -fun Element.p(vararg classes: String, init: P.() -> String): P { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return textElement(classes, init) -} -@OptIn(ExperimentalContracts::class) -fun Element.h1(vararg classes: String, init: H1.() -> String): H1 { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return textElement(classes, init) -} -@OptIn(ExperimentalContracts::class) -fun Element.h2(vararg classes: String, init: H2.() -> String): H2 { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return textElement(classes, init) -} - -@OptIn(ExperimentalContracts::class) -fun Element.h3(vararg classes: String, init: H3.() -> String): H3 { - contract { - callsInPlace(init, InvocationKind.EXACTLY_ONCE) - } - return textElement(classes, init) -} - diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/SequenceEditor.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/SequenceEditor.kt deleted file mode 100644 index 46e70213..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/SequenceEditor.kt +++ /dev/null @@ -1,213 +0,0 @@ -package org.openrndr.panel.elements - -import kotlinx.coroutines.* -import org.openrndr.KeyModifier -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.events.Event -import org.openrndr.extra.textwriter.Cursor -import org.openrndr.extra.textwriter.TextWriter -import org.openrndr.math.Vector2 -import org.openrndr.math.map -import org.openrndr.panel.style.effectiveColor -import org.openrndr.panel.tools.Tooltip -import org.openrndr.shape.Rectangle -import kotlin.math.abs -import kotlin.math.round -import kotlin.math.roundToInt - -class SequenceEditor : SequenceEditorBase("sequence-editor") { - var value - get() = baseValue - set(value) { - baseValue = value - } - - public override var maximumSequenceLength = 16 - public override var minimumSequenceLength = 1 - - class ValueChangedEvent(val source: SequenceEditorBase, - val oldValue: List, - val newValue: List) - - class Events { - val valueChanged = Event("sequence-editor-value-changed") - } - - val events = Events() - - init { - baseEvents.valueChanged.listen { - events.valueChanged.trigger(ValueChangedEvent(this, it.oldValue, it.newValue)) - } - } -} - -@OptIn(DelicateCoroutinesApi::class) -open class SequenceEditorBase(type: String = "sequence-editor-base") : Element(ElementType(type)), DisposableElement { - override var disposed = false - - internal var baseValue = mutableListOf(0.0) - var label = "sequence" - var precision = 2 - internal open var maximumSequenceLength = 16 - internal open var minimumSequenceLength = 1 - var range: ClosedRange = -1.0..1.0 - - private var selectedIndex: Int? = null - private var tooltip: Tooltip? = null - - private val footerHeight = 20.0 - - internal class ValueChangedEvent(val source: SequenceEditorBase, - val oldValue: List, - val newValue: List) - - internal class Events { - val valueChanged = Event("sequence-editor-base-value-changed") - } - - internal val baseEvents = Events() - - init { - fun query(position: Vector2): Vector2 { - val x = (position.x - layout.screenX) / layout.screenWidth - val y = 1.0 - ((position.y - layout.screenY) / ((layout.screenHeight - footerHeight) * 0.5)) - return Vector2(x, y) - } - - mouse.clicked.listen { - it.cancelPropagation() - requestRedraw() - } - mouse.pressed.listen { - if (baseValue.isNotEmpty()) { - val dx = (layout.screenWidth / (baseValue.size + 1)) - val index = (it.position.x - layout.screenX) / dx - - val d = index - round(index) - val dp = d * dx - val dpa = abs(dp) - - if (dpa < 10.0) { - selectedIndex = if (KeyModifier.CTRL !in it.modifiers) { - round(index).toInt() - } else { - if (baseValue.size > minimumSequenceLength) { - val oldValue = baseValue.map { it } - baseValue.removeAt(round(index).toInt() - 1) - baseEvents.valueChanged.trigger(ValueChangedEvent(this, oldValue, baseValue)) - } - null - } - } else { - if (KeyModifier.CTRL !in it.modifiers) { - if (baseValue.size < maximumSequenceLength) { - val q = query(it.position) - val oldValue = baseValue.map { it } - baseValue.add(index.toInt(), q.y.map(-1.0, 1.0, range.start, range.endInclusive)) - baseEvents.valueChanged.trigger(ValueChangedEvent(this, oldValue, baseValue)) - } - } - } - } - it.cancelPropagation() - } - - var hoverJob: Job? = null - - mouse.exited.listen { - hoverJob?.cancel() - if (tooltip != null) { - tooltip = null - requestRedraw() - } - } - - mouse.moved.listen { - hoverJob?.let { job -> - job.cancel() - } - if (tooltip != null) { - tooltip = null - requestRedraw() - } - - if (baseValue.isNotEmpty()) { - val dx = (layout.screenWidth / (baseValue.size + 1)) - val index = (it.position.x - layout.screenX) / dx - val d = index - round(index) - val dp = d * dx - val dpa = abs(dp) - - if (dpa < 10.0) { - hoverJob = GlobalScope.launch { - val readIndex = index.roundToInt() - 1 - if (readIndex >= 0 && readIndex < baseValue.size) { - val value = String.format("%.0${precision}f", baseValue[readIndex]) - tooltip = Tooltip(this@SequenceEditorBase, it.position - Vector2(layout.screenX, layout.screenY), "$value") - requestRedraw() - } - } - } - } - } - mouse.dragged.listen { - val q = query(it.position) - selectedIndex?.let { index -> - val writeIndex = index - 1 - if (writeIndex >= 0 && writeIndex < baseValue.size) { - val oldValue = baseValue.map { it } - baseValue[writeIndex] = q.y.coerceIn(-1.0, 1.0).map(-1.0, 1.0, range.start, range.endInclusive) - baseEvents.valueChanged.trigger(ValueChangedEvent(this, oldValue, baseValue)) - } - requestRedraw() - } - } - } - - override fun draw(drawer: Drawer) { - val controlArea = Rectangle(0.0, 0.0, layout.screenWidth, layout.screenHeight - footerHeight) - - drawer.stroke = computedStyle.effectiveColor?.opacify(0.25) - drawer.strokeWeight = (1.0) - - - val zeroHeight = 0.0.map(range.start, range.endInclusive, -1.0, 1.0).coerceIn(-1.0, 1.0) * controlArea.height / -2.0 - drawer.lineSegment(0.0, controlArea.height / 2.0 + zeroHeight, layout.screenWidth, controlArea.height / 2.0 + zeroHeight) - - drawer.strokeWeight = 7.0 - drawer.fill = computedStyle.effectiveColor - - for (i in baseValue.indices) { - val dx = layout.screenWidth / (baseValue.size + 1) - val height = -baseValue[i].map(range.start, range.endInclusive, -1.0, 1.0).coerceIn(-1.0, 1.0) * controlArea.height / 2.0 - - val x = dx * (i + 1) - drawer.lineCap = LineCap.ROUND - drawer.stroke = computedStyle.effectiveColor - drawer.lineSegment(x, controlArea.height / 2.0 + zeroHeight, x, controlArea.height / 2.0 + height) - - drawer.stroke = computedStyle.effectiveColor?.shade(1.1) - drawer.fill = ColorRGBa.PINK - drawer.circle(x, controlArea.height / 2.0 + height, 7.0) - } - - drawer.isolated { - drawer.translate(0.0, controlArea.height) - drawer.fill = computedStyle.effectiveColor - (root() as? Body)?.controlManager?.fontManager?.let { - val font = it.font(computedStyle) - val writer = TextWriter(drawer) - drawer.fontMap = (font) - drawer.fill = computedStyle.effectiveColor - writer.cursor = Cursor(0.0, 4.0) - writer.box = Rectangle(0.0, 4.0, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY) - writer.newLine() - writer.text(label) - } - } - - tooltip?.draw(drawer) - } -} diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Slider.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Slider.kt deleted file mode 100644 index c8354b39..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Slider.kt +++ /dev/null @@ -1,359 +0,0 @@ -package org.openrndr.panel.elements - -import io.github.oshai.kotlinlogging.KotlinLogging -import kotlinx.coroutines.* -import org.openrndr.* - -import org.openrndr.draw.Drawer -import org.openrndr.draw.LineCap - -import org.openrndr.events.Event -import org.openrndr.extra.textwriter.Cursor -import org.openrndr.extra.textwriter.TextWriter -import org.openrndr.math.Vector2 -import org.openrndr.panel.style.Color -import org.openrndr.panel.style.color -import org.openrndr.panel.style.effectiveColor -import org.openrndr.shape.Rectangle -import java.text.NumberFormat -import java.text.ParseException -import kotlin.reflect.KMutableProperty0 - -private val logger = KotlinLogging.logger {} - -@JvmRecord -data class Range(val min: Double, val max: Double) { - val span: Double get() = max - min -} - -enum class SliderMode { - RANGE, - POINT, - SEGMENT -} - -class Slider : Element(ElementType("slider")), DisposableElement { - override var disposed = false - override val handlesKeyboardFocus = true - - var label = "" - var precision = 3 - var mode = SliderMode.RANGE - - var value: Double - set(v) { - val oldV = realValue - realValue = clean(v) - if (realValue != oldV) { - draw.dirty = true - events.valueChanged.trigger(ValueChangedEvent(this, false, oldV, realValue)) - } - } - get() = realValue - - private var interactiveValue: Double - set(v) { - val oldV = realValue - realValue = clean(v) - if (realValue != oldV) { - draw.dirty = true - events.valueChanged.trigger(ValueChangedEvent(this, true, oldV, realValue)) - } - } - get() = realValue - - - var range = Range(0.0, 10.0) - set(value) { - field = value - this.value = this.value - } - private var realValue = 0.0 - - fun clean(value: Double): Double { - val cleanV = value.coerceIn(range.min, range.max) - val quantized = String.format("%.0${precision}f", cleanV).replace(",", ".").toDouble() - return quantized - } - - class ValueChangedEvent(val source: Slider, - val interactive: Boolean, - val oldValue: Double, - val newValue: Double) - - class Events { - val valueChanged = Event("slider-value-changed") - } - - val events = Events() - - private val margin = 7.0 - private var keyboardInput = "" - - init { - mouse.pressed.listen { - val t = (it.position.x - layout.screenX - margin) / (layout.screenWidth - 2.0 * margin) - interactiveValue = t * range.span + range.min - it.cancelPropagation() - } - mouse.clicked.listen { - val t = (it.position.x - layout.screenX - margin) / (layout.screenWidth - 2.0 * margin) - interactiveValue = t * range.span + range.min - it.cancelPropagation() - } - mouse.dragged.listen { - val t = (it.position.x - layout.screenX - margin) / (layout.screenWidth - 2.0 * margin) - interactiveValue = t * range.span + range.min - it.cancelPropagation() - } - - mouse.scrolled.listen { - if (Math.abs(it.rotation.y) < 0.001) { - interactiveValue += range.span * 0.001 * it.rotation.x - it.cancelPropagation() - } - } - - keyboard.focusLost.listen { - keyboardInput = "" - draw.dirty = true - } - - keyboard.character.listen { - if (it.character in setOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', ',', '-')) { - try { - val candidate = keyboardInput + it.character.toString() - if (candidate.length > 1) { - NumberFormat.getInstance().parse(candidate).toDouble() - } - keyboardInput = candidate - requestRedraw() - } catch (e: ParseException) { - } - } - it.cancelPropagation() - } - - - keyboard.repeated.listen { - val delta = Math.pow(10.0, -(precision - 0.0)) - if (it.key == KEY_ARROW_RIGHT) { - interactiveValue += delta - it.cancelPropagation() - } - - if (it.key == KEY_ARROW_LEFT) { - interactiveValue -= delta - it.cancelPropagation() - } - - } - keyboard.pressed.listen { - val delta = Math.pow(10.0, -(precision - 0.0)) - - if (it.key == KEY_ARROW_RIGHT) { - interactiveValue += delta - it.cancelPropagation() - } - - if (it.key == KEY_ARROW_LEFT) { - interactiveValue -= delta - it.cancelPropagation() - } - - if (it.key == KEY_BACKSPACE) { - if (!keyboardInput.isEmpty()) { - keyboardInput = keyboardInput.substring(0, keyboardInput.length - 1) - draw.dirty = true - } - it.cancelPropagation() - } - - if (it.key == KEY_ESCAPE) { - keyboardInput = "" - draw.dirty = true - it.cancelPropagation() - } - - if (it.key == KEY_ENTER) { - try { - val number = NumberFormat.getInstance().parse(keyboardInput).toDouble() - interactiveValue = number.coerceIn(range.min, range.max) - } catch (e: ParseException) { - // -- silently (but safely) ignore the exception - } - keyboardInput = "" - draw.dirty = true - it.cancelPropagation() - } - - if (it.key == KEY_HOME) { - interactiveValue = range.min - keyboardInput = "" - it.cancelPropagation() - } - - if (it.key == KEY_END) { - interactiveValue = range.max - keyboardInput = "" - it.cancelPropagation() - } - } - } - - override fun draw(drawer: Drawer) { - val f = (root() as? Body)?.controlManager?.fontManager?.font(computedStyle)!! - drawer.translate(0.0, (layout.screenHeight - (10.0 + f.height)) / 2) - - drawer.fill = ((computedStyle.color as Color.RGBa).color) - drawer.stroke = ((computedStyle.color as Color.RGBa).color) - drawer.strokeWeight = (8.0) - drawer.lineCap = (LineCap.ROUND) - val x = ((value - range.min) / range.span) * (layout.screenWidth - 2 * margin) - - drawer.stroke = ((computedStyle.color as Color.RGBa).color.opacify(0.25)) - drawer.lineSegment(margin + 0.0, 2.0, margin + layout.screenWidth - 2 * margin, 2.0) - - if (mode == SliderMode.RANGE) { - drawer.stroke = ((computedStyle.color as Color.RGBa).color.opacify(1.0)) - drawer.lineSegment(margin, 2.0, margin + x, 2.0) - - drawer.fill = ((computedStyle.color as Color.RGBa).color.opacify(1.0)) - drawer.stroke = null - drawer.strokeWeight = 0.0 - drawer.circle(margin + x, 2.0, 5.0) - } - - if (mode == SliderMode.POINT && precision == 0) { - val lineSegments = mutableListOf() - for (i in range.min.toInt()..range.max.toInt()) { - val lx = ((i - range.min) / range.span) * (layout.screenWidth - 2 * margin) - drawer.strokeWeight = 1.0 - drawer.stroke = ((computedStyle.color as Color.RGBa).color.opacify(0.5)) - lineSegments.add(Vector2(margin + lx, -2.0)) - lineSegments.add(Vector2(margin + lx, 4.0)) - } - drawer.lineSegments(lineSegments) - } - - if (mode == SliderMode.SEGMENT) { - drawer.stroke = ((computedStyle.color as Color.RGBa).color.opacify(1.0)) - - val sx = ((value - range.min) / (range.span+1.0)) * (layout.screenWidth - 2 * margin) + margin - val ex = (((value+1) - range.min) / (range.span+1.0)) * (layout.screenWidth - 2 * margin) + margin - - drawer.strokeWeight = 8.0 - drawer.lineSegment(sx, 2.0, ex, 2.0) - - drawer.stroke = null - drawer.strokeWeight = 0.0 - - - val lineSegments = mutableListOf() - for (i in range.min.toInt()..(range.max.toInt()+1)) { - val lx = ((i - range.min) / (range.span+1.0)) * (layout.screenWidth - 2 * margin) - drawer.strokeWeight = 1.0 - drawer.stroke = ((computedStyle.color as Color.RGBa).color.opacify(0.5)) - lineSegments.add(Vector2(margin + lx, -2.0)) - lineSegments.add(Vector2(margin + lx, 4.0)) - } - drawer.lineSegments(lineSegments) - } - - - if (mode == SliderMode.POINT) { - drawer.fill = ((computedStyle.color as Color.RGBa).color.opacify(1.0)) - drawer.stroke = null - drawer.circle(margin + x, 2.0, 8.0) - } - - - (root() as? Body)?.controlManager?.fontManager?.let { - val font = it.font(computedStyle) - val writer = TextWriter(drawer) - drawer.fontMap = (font) - drawer.fill = computedStyle.effectiveColor - writer.cursor = Cursor(0.0, 8.0) - writer.box = Rectangle(0.0, 8.0, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY) - writer.newLine() - writer.text(label) - - if (keyboardInput.isEmpty()) { - val valueFormatted = String.format("%.0${precision}f", value) - val tw = writer.textWidth(valueFormatted) - writer.cursor.x = (layout.screenWidth - tw) - writer.text(valueFormatted) - } else { - val tw = writer.textWidth(keyboardInput) - writer.cursor.x = (layout.screenWidth - tw) - writer.text(keyboardInput) - } - } - } -} - -@OptIn(DelicateCoroutinesApi::class) -fun Slider.bind(property: KMutableProperty0) { - var currentValue: Double? = null - - events.valueChanged.listen { - currentValue = it.newValue - property.set(it.newValue) - } - GlobalScope.launch { - while(!disposed) { - val body = (root() as? Body) - if (body != null) { - fun update() { - if (property.get() != currentValue) { - val lcur = property.get() - currentValue = lcur - value = lcur.toDouble() - } - } - update() - body.controlManager.program.launch { - while (!disposed) { - update() - yield() - } - } - break - } - yield() - } - } -} - -@OptIn(DelicateCoroutinesApi::class) -@JvmName("bindInt") -fun Slider.bind(property: KMutableProperty0) { - var currentValue: Int? = null - events.valueChanged.listen { - currentValue = it.newValue.toInt() - property.set(it.newValue.toInt()) - } - GlobalScope.launch { - while(!disposed) { - val body = (root() as? Body) - if (body != null) { - fun update() { - if (property.get() != currentValue) { - val lcur = property.get() - currentValue = lcur - value = lcur.toDouble() - } - } - update() - body.controlManager.program.launch { - while (!disposed) { - update() - yield() - } - } - break - } - yield() - } - } -} diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/SlidersVector.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/SlidersVector.kt deleted file mode 100644 index d6ad57f7..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/SlidersVector.kt +++ /dev/null @@ -1,192 +0,0 @@ -package org.openrndr.panel.elements - -import kotlinx.coroutines.yield -import org.openrndr.events.Event -import org.openrndr.launch -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 -import kotlin.reflect.KMutableProperty0 - -class SlidersVector2 : SequenceEditorBase("sliders-vector2") { - var value : Vector2 - get() { - return Vector2(baseValue[0], baseValue[1]) - } - set(value) { - baseValue[0] = value.x - baseValue[1] = value.y - requestRedraw() - } - - class ValueChangedEvent(val source: SequenceEditorBase, - val oldValue: Vector2, - val newValue: Vector2) - - class Events { - val valueChanged = Event("sequence-editor-value-changed") - } - - val events = Events() - - init { - baseValue = mutableListOf(0.0, 0.0) - minimumSequenceLength = 2 - maximumSequenceLength = 2 - baseEvents.valueChanged.listen { - events.valueChanged.trigger(ValueChangedEvent(this, - Vector2(it.oldValue[0], it.oldValue[1]), - Vector2(it.newValue[0], it.newValue[1])) - ) - } - } -} - -fun SlidersVector2.bind(property: KMutableProperty0) { - var currentValue: Vector2? = null - - events.valueChanged.listen { - currentValue = value - property.set(it.newValue) - } - if (root() as? Body == null) { - throw RuntimeException("no body") - } - fun update() { - if (property.get() != currentValue) { - val lcur = property.get() - currentValue = lcur - value = lcur - } - } - update() - (root() as? Body)?.controlManager?.program?.launch { - while (!disposed) { - update() - yield() - } - } -} - -class SlidersVector3 : SequenceEditorBase("sliders-vector3") { - var value : Vector3 - get() { - return Vector3(baseValue[0], baseValue[1], baseValue[2]) - } - set(value) { - baseValue[0] = value.x - baseValue[1] = value.y - baseValue[2] = value.z - requestRedraw() - } - - class ValueChangedEvent(val source: SequenceEditorBase, - val oldValue: Vector3, - val newValue: Vector3) - - class Events { - val valueChanged = Event("sliders-vector3-value-changed") - } - - val events = Events() - - init { - baseValue = mutableListOf(0.0, 0.0, 0.0) - minimumSequenceLength = 3 - maximumSequenceLength = 3 - baseEvents.valueChanged.listen { - events.valueChanged.trigger(ValueChangedEvent(this, - Vector3(it.oldValue[0], it.oldValue[1], it.oldValue[2]), - Vector3(it.newValue[0], it.newValue[1], it.newValue[2])) - ) - } - } -} - -fun SlidersVector3.bind(property: KMutableProperty0) { - var currentValue: Vector3? = null - - events.valueChanged.listen { - currentValue = value - property.set(it.newValue) - } - if (root() as? Body == null) { - throw RuntimeException("no body") - } - fun update() { - if (property.get() != currentValue) { - val lcur = property.get() - currentValue = lcur - value = lcur - } - } - update() - (root() as? Body)?.controlManager?.program?.launch { - while (!disposed) { - update() - yield() - } - } -} - -class SlidersVector4 : SequenceEditorBase("sliders-vector4") { - var value : Vector4 - get() { - return Vector4(baseValue[0], baseValue[1], baseValue[2], baseValue[3]) - } - set(value) { - baseValue[0] = value.x - baseValue[1] = value.y - baseValue[2] = value.z - baseValue[3] = value.w - requestRedraw() - } - - class ValueChangedEvent(val source: SequenceEditorBase, - val oldValue: Vector4, - val newValue: Vector4) - - class Events { - val valueChanged = Event("sliders-vector4-value-changed") - } - - val events = Events() - - init { - baseValue = mutableListOf(0.0, 0.0, 0.0, 0.0) - minimumSequenceLength = 4 - maximumSequenceLength = 4 - baseEvents.valueChanged.listen { - events.valueChanged.trigger(ValueChangedEvent(this, - Vector4(it.oldValue[0], it.oldValue[1], it.oldValue[2], it.oldValue[3]), - Vector4(it.newValue[0], it.newValue[1], it.newValue[2], it.newValue[3])) - ) - } - } -} - -fun SlidersVector4.bind(property: KMutableProperty0) { - var currentValue: Vector4? = null - - events.valueChanged.listen { - currentValue = value - property.set(it.newValue) - } - if (root() as? Body == null) { - throw RuntimeException("no body") - } - fun update() { - if (property.get() != currentValue) { - val lcur = property.get() - currentValue = lcur - value = lcur - } - } - update() - (root() as? Body)?.controlManager?.program?.launch { - while (!disposed) { - update() - yield() - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/TextElements.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/TextElements.kt deleted file mode 100644 index 885bc70a..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/TextElements.kt +++ /dev/null @@ -1,106 +0,0 @@ -package org.openrndr.panel.elements - -import kotlinx.coroutines.yield -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Drawer -import org.openrndr.draw.loadFont -import org.openrndr.extra.textwriter.TextWriter - -import org.openrndr.launch -import org.openrndr.math.Vector2 -import org.openrndr.panel.style.* -import org.openrndr.shape.Rectangle -import kotlin.reflect.KMutableProperty0 - -class TextNode(var text: String) : Element(ElementType("text")) { - - override fun draw(drawer: Drawer) { - computedStyle.let { style -> - style.color.let { - val fill = (it as? Color.RGBa)?.color ?: ColorRGBa.WHITE - drawer.fill = (fill) - } - val fontMap = (root() as Body).controlManager.fontManager.font(computedStyle) - val writer = TextWriter(drawer) - drawer.fontMap = (fontMap) - - writer.box = Rectangle(Vector2(layout.screenX * 0.0, layout.screenY * 0.0), layout.screenWidth, layout.screenHeight) - writer.newLine() - writer.text(text) - } - } - - fun sizeHint(): Rectangle { - computedStyle.let { style -> - val fontUrl = (root() as? Body)?.controlManager?.fontManager?.resolve(style.fontFamily)?:"broken" - val fontSize = (style.fontSize as? LinearDimension.PX)?.value?: 14.0 - val program = (root() as? Body)?.controlManager?.program ?: error("no program") - val fontMap = program.loadFont(fontUrl, fontSize) - - val writer = TextWriter(null) - - writer.box = Rectangle(layout.screenX, - layout.screenY, - layout.screenWidth, - layout.screenHeight) - - writer.drawStyle.fontMap = fontMap - writer.newLine() - writer.text(text, visible = false) - - return Rectangle(layout.screenX, - layout.screenY, - layout.screenWidth, - (writer.cursor.y - layout.screenY) - fontMap.descenderLength*2) - } - - } - - override fun toString(): String { - return "TextNode(id='$id',text='$text')" - } -} - -class H1 : TextElement(ElementType("h1")) -class H2 : TextElement(ElementType("h2")) -class H3 : TextElement(ElementType("h3")) -class H4 : TextElement(ElementType("h4")) -class H5 : TextElement(ElementType("h5")) - -class P : TextElement(ElementType("p")) - -abstract class TextElement(et: ElementType) : Element(et) { - fun text(text: String) { - append(TextNode(text)) - requestRedraw() - } - fun replaceText(text : String) { - if (children.isEmpty()) { - text(text) - } else { - (children.first() as? TextNode)?.text = text - requestRedraw() - } - } -} - -fun TextElement.bind(property: KMutableProperty0) { - if (root() as? Body == null) { - throw RuntimeException("no body") - } - var lastText = "" - fun update() { - if (property.get() != lastText) { - replaceText(property.get()) - lastText = property.get() - } - } - - (root() as? Body)?.controlManager?.program?.launch { - update() - while (true) { - update() - yield() - } - } -} diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Textfield.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Textfield.kt deleted file mode 100644 index 640d25bc..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Textfield.kt +++ /dev/null @@ -1,167 +0,0 @@ -package org.openrndr.panel.elements - -import kotlinx.coroutines.* -import org.openrndr.KEY_BACKSPACE -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Drawer -import org.openrndr.draw.LineCap -import org.openrndr.panel.style.* -import org.openrndr.KeyModifier -import org.openrndr.events.Event -import org.openrndr.extra.textwriter.Cursor -import org.openrndr.extra.textwriter.writer -import org.openrndr.launch -import org.openrndr.shape.Rectangle -import kotlin.reflect.KMutableProperty0 - -class Textfield : Element(ElementType("textfield")), DisposableElement { - - var value: String = "" - var label: String = "label" - - class ValueChangedEvent(val source: Textfield, val oldValue: String, val newValue: String) - class Events { - val valueChanged = Event("textfield-value-changed") - } - - val events = Events() - - init { - keyboard.repeated.listen { - if (it.key == KEY_BACKSPACE) { - if (value.isNotEmpty()) { - val oldValue = value - value = value.substring(0, value.length - 1) - events.valueChanged.trigger(ValueChangedEvent(this, oldValue, value)) - requestRedraw() - } - - } - it.cancelPropagation() - } - - keyboard.pressed.listen { - if (KeyModifier.CTRL in it.modifiers || KeyModifier.SUPER in it.modifiers) { - if (it.name == "v") { - val oldValue = value - (root() as Body).controlManager.program.clipboard.contents?.let { - value += it - - } - events.valueChanged.trigger(ValueChangedEvent(this, oldValue, value)) - it.cancelPropagation() - } - } - if (it.key == KEY_BACKSPACE) { - if (value.isNotEmpty()) { - val oldValue = value - value = value.substring(0, value.length - 1) - events.valueChanged.trigger(ValueChangedEvent(this, oldValue, value)) - } - } - requestRedraw() - it.cancelPropagation() - } - - keyboard.character.listen { - val oldValue = value - value += it.character - events.valueChanged.trigger(ValueChangedEvent(this, oldValue, value)) - requestRedraw() - it.cancelPropagation() - } - - mouse.pressed.listen { - it.cancelPropagation() - } - mouse.clicked.listen { - it.cancelPropagation() - } - } - - override fun draw(drawer: Drawer) { - drawer.fill = computedStyle.effectiveBackground - drawer.stroke = null - drawer.rectangle(0.0, 0.0, layout.screenWidth, layout.screenHeight) - - (root() as? Body)?.controlManager?.fontManager?.let { - val font = it.font(computedStyle) - - drawer.fontMap = (font) - val textHeight = font.ascenderLength - - val offset = 5.0 - val yOffset = Math.round((layout.screenHeight / 2) + textHeight / 2.0 - 2.0) * 1.0 - - drawer.fill = ((computedStyle.color as? Color.RGBa)?.color ?: ColorRGBa.WHITE) - drawer.text(label, 0.0 + offset, 0.0 + yOffset - textHeight * 1.5) - - drawer.fill = (((computedStyle.color as? Color.RGBa)?.color ?: ColorRGBa.WHITE).opacify(0.05)) - drawer.rectangle(0.0 + offset, 0.0 + yOffset - (textHeight + 2), layout.screenWidth - 10.0, textHeight + 8.0) - - drawer.drawStyle.clip = Rectangle(screenPosition.x + offset, screenPosition.y + yOffset - (textHeight + 2), layout.screenWidth - 10.0, textHeight + 8.0) - - drawer.fill = ((computedStyle.color as? Color.RGBa)?.color ?: ColorRGBa.WHITE) - - var cursorX = 0.0 - writer(drawer) { - val emWidth = textWidth("m") * 2 - cursor = Cursor(offset, yOffset) - text(value, visible = false) - val width = cursor.x - offset - val scroll = - if (width > screenArea.width - emWidth) { - screenArea.width - emWidth - width - } else { - 0.0 - } - cursor = Cursor(offset + scroll, yOffset) - text(value) - cursorX = cursor.x - } - - if (ElementPseudoClass("active") in pseudoClasses) { - drawer.stroke = ColorRGBa.WHITE - drawer.lineSegment(cursorX + 1.0, yOffset, cursorX + 1.0, yOffset - textHeight) - } - drawer.drawStyle.clip = null - - drawer.stroke = ((computedStyle.color as? Color.RGBa)?.color ?: ColorRGBa.WHITE) - drawer.strokeWeight = 1.0 - - drawer.stroke = computedStyle.effectiveColor?.shade(0.25) - drawer.lineCap = LineCap.ROUND - } - } - - override var disposed: Boolean = false -} - -@OptIn(DelicateCoroutinesApi::class) -fun Textfield.bind(property: KMutableProperty0) { - GlobalScope.launch { - install@ while (!disposed) { - val body = (root() as? Body) - if (body != null) { - events.valueChanged.listen { - property.set(it.newValue) - } - fun update() { - val propertyValue = property.get() - if (propertyValue != value) { - value = propertyValue - } - } - update() - (root() as Body).controlManager.program.launch { - while (!disposed) { - update() - yield() - } - } - break@install - } - yield() - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Toggle.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Toggle.kt deleted file mode 100644 index 6565bbd2..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/Toggle.kt +++ /dev/null @@ -1,134 +0,0 @@ -package org.openrndr.panel.elements - -import kotlinx.coroutines.* -import org.openrndr.draw.Drawer -import org.openrndr.draw.LineCap -import org.openrndr.draw.loadFont -import org.openrndr.panel.style.* -import org.openrndr.shape.Rectangle - - -import org.openrndr.events.Event -import org.openrndr.extra.textwriter.TextWriter -import org.openrndr.launch -import kotlin.reflect.KMutableProperty0 - -class Toggle : Element(ElementType("toggle")), DisposableElement { - override var disposed = false - - var label = "" - var value = false - - class ValueChangedEvent(val source: Toggle, - val oldValue: Boolean, - val newValue: Boolean) - - class Events { - val valueChanged = Event("toggle-value-changed") - } - - val events = Events() - - override val widthHint: Double? - get() { - computedStyle.let { style -> - val fontUrl = (root() as? Body)?.controlManager?.fontManager?.resolve(style.fontFamily) ?: "broken" - val fontSize = (style.fontSize as? LinearDimension.PX)?.value ?: 14.0 - val program = (root() as? Body)?.controlManager?.program ?: error("no program") - val fontMap = program.loadFont(fontUrl, fontSize) - - val writer = TextWriter(null) - - writer.box = Rectangle(0.0, - 0.0, - Double.POSITIVE_INFINITY, - Double.POSITIVE_INFINITY) - - writer.drawStyle.fontMap = fontMap - writer.newLine() - writer.text(label, visible = false) - - return writer.cursor.x + (computedStyle.height as LinearDimension.PX).value - 8.0 + 5.0 - } - } - - init { - mouse.pressed.listen { - it.cancelPropagation() - } - mouse.clicked.listen { - value = !value - draw.dirty = true - events.valueChanged.trigger(Toggle.ValueChangedEvent(this, !value, value)) - it.cancelPropagation() - } - } - - /** - * Emits the current value through the valueChanged event - */ - fun emit() { - events.valueChanged.trigger(Toggle.ValueChangedEvent(this, value, value)) - } - - override fun draw(drawer: Drawer) { - drawer.pushModel() - val checkBoxSize = layout.screenHeight - 8.0 - drawer.translate(0.0, (layout.screenHeight - checkBoxSize) / 2.0) - drawer.strokeWeight = 1.0 - drawer.stroke = computedStyle.effectiveColor - drawer.fill = null - drawer.rectangle(0.0, 0.0, checkBoxSize, checkBoxSize) - - if (value) { - drawer.strokeWeight = 2.0 - drawer.stroke = computedStyle.effectiveColor - drawer.fill = null - drawer.lineCap = LineCap.ROUND - drawer.lineSegment(5.0, 5.0, checkBoxSize / 2.0 - 2.0, checkBoxSize / 2.0 - 2.0) - drawer.lineSegment(checkBoxSize / 2.0 + 2.0, checkBoxSize / 2.0 + 2.0, checkBoxSize - 5.0, checkBoxSize - 5.0) - drawer.lineSegment(checkBoxSize - 5.0, 5.0, checkBoxSize / 2.0 + 2.0, checkBoxSize / 2.0 - 2.0) - drawer.lineSegment(checkBoxSize / 2.0 - 2.0, checkBoxSize / 2.0 + 2.0, 5.0, checkBoxSize - 5.0) - } - - drawer.popModel() - drawer.fontMap = (root() as? Body)?.controlManager?.fontManager?.font(computedStyle)!! - drawer.translate(5.0 + checkBoxSize, (layout.screenHeight / 2.0) + drawer.fontMap!!.height / 2.0) - drawer.stroke = null - drawer.fill = computedStyle.effectiveColor - drawer.text(label, 0.0, 0.0) - } -} - -@OptIn(DelicateCoroutinesApi::class) -fun Toggle.bind(property: KMutableProperty0) { - var currentValue = property.get() - value = currentValue - - events.valueChanged.listen { - currentValue = it.newValue - property.set(it.newValue) - } - GlobalScope.launch { - while (!disposed) { - val body = (root() as? Body) - if (body != null) { - fun update() { - if (property.get() != currentValue) { - val lcur = property.get() - currentValue = lcur - value = lcur - } - } - update() - (root() as Body).controlManager.program.launch { - while (!disposed) { - update() - yield() - } - } - break - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/WatchListDiv.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/WatchListDiv.kt deleted file mode 100644 index c52731ba..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/WatchListDiv.kt +++ /dev/null @@ -1,71 +0,0 @@ -package org.openrndr.panel.elements - -import kotlinx.coroutines.Job -import kotlinx.coroutines.yield -import org.openrndr.draw.Drawer -import org.openrndr.launch - -class WatchListDiv(private val watchList: List, private val builder: WatchListDiv.(T) -> Unit) : Div(), DisposableElement { - override var disposed: Boolean = false - private var listState = emptyList() - private var watchJob: Job? = null - - override fun dispose() { - super.dispose() - for (child in children) { - child.parent = null - (child as? DisposableElement)?.dispose() - } - children.clear() - } - - fun regenerate() { - var regenerate = false - if (listState.size != watchList.size) { - regenerate = true - } - if (!regenerate) { - for (i in watchList.indices) { - if (watchList[i] !== listState[i]) { - regenerate = true - break - } - } - } - if (regenerate) { - for (child in children) { - child.parent = null - (child as? DisposableElement)?.dispose() - } - children.clear() - listState = watchList.map { it } - for (i in watchList) { - builder(i) - } - requestRedraw() - } - } - - fun checkJob() { - if (watchJob == null) { - watchJob = (root() as Body).controlManager.program.launch { - while (!disposed) { - regenerate() - yield() - } - } - } - } - override fun draw(drawer: Drawer) { - checkJob() - super.draw(drawer) - } -} - -fun Element.watchListDiv(vararg classes: String, watchList: List, builder: WatchListDiv.(T) -> Unit) { - val wd = WatchListDiv(watchList, builder) - wd.classes.addAll(classes.map { ElementClass(it) }) - this.append(wd) - wd.regenerate() - wd.checkJob() -} diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/WatchObjectDiv.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/WatchObjectDiv.kt deleted file mode 100644 index f79f6ab7..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/WatchObjectDiv.kt +++ /dev/null @@ -1,75 +0,0 @@ -package org.openrndr.panel.elements - -import kotlinx.coroutines.Job -import kotlinx.coroutines.yield -import org.openrndr.draw.Drawer -import org.openrndr.launch -import org.openrndr.panel.hash.watchHash - -class WatchObjectDiv( - val watchObject: T, - private val builder: WatchObjectDiv.(T) -> Unit -) : Div(), - DisposableElement { - override var disposed: Boolean = false - private var objectStateHash = watchHash(watchObject) - private var watchJob: Job? = null - - - override fun dispose() { - super.dispose() - for (child in children) { - child.parent = null - (child as? DisposableElement)?.dispose() - } - children.clear() - } - - fun regenerate(force: Boolean = false) { - var regenerate = force - if (watchHash(watchObject) != objectStateHash) { - regenerate = true - } - - if (regenerate) { - for (child in children) { - child.parent = null - (child as? DisposableElement)?.dispose() - } - objectStateHash = watchHash(watchObject) - children.clear() - builder(watchObject) - - requestRedraw() - } - } - - fun checkJob() { - if (watchJob == null) { - watchJob = (root() as? Body)?.controlManager?.program?.launch { - while (!disposed) { - regenerate() - yield() - } - } - } - } - - override fun draw(drawer: Drawer) { - checkJob() - super.draw(drawer) - } -} - -fun Element.watchObjectDiv( - vararg classes: String, - watchObject: T, - builder: WatchObjectDiv.(T) -> Unit -) : WatchObjectDiv { - val wd = WatchObjectDiv(watchObject, builder) - wd.classes.addAll(classes.map { ElementClass(it) }) - this.append(wd) - wd.regenerate(true) - wd.checkJob() - return wd -} diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/WatchPropertyDiv.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/WatchPropertyDiv.kt deleted file mode 100644 index 233f98e5..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/WatchPropertyDiv.kt +++ /dev/null @@ -1,74 +0,0 @@ -package org.openrndr.panel.elements - -import kotlinx.coroutines.Job -import kotlinx.coroutines.yield -import org.openrndr.draw.Drawer -import org.openrndr.launch -import kotlin.reflect.KMutableProperty0 - -class WatchPropertyDiv( - private val watchProperty: KMutableProperty0, - private val builder: WatchPropertyDiv.(T) -> Unit -) : Div(), - DisposableElement { - override var disposed: Boolean = false - private var propertyState = watchProperty.get() - private var watchJob: Job? = null - - - override fun dispose() { - super.dispose() - for (child in children) { - child.parent = null - (child as? DisposableElement)?.dispose() - } - children.clear() - } - - fun regenerate(force: Boolean = false) { - var regenerate = force - if (watchProperty.get() != propertyState) { - regenerate = true - } - - if (regenerate) { - for (child in children) { - child.parent = null - (child as? DisposableElement)?.dispose() - } - propertyState = watchProperty.get() - children.clear() - builder(propertyState) - - requestRedraw() - } - } - - fun checkJob() { - if (watchJob == null) { - watchJob = (root() as? Body)?.controlManager?.program?.launch { - while (!disposed) { - regenerate() - yield() - } - } - } - } - - override fun draw(drawer: Drawer) { - checkJob() - super.draw(drawer) - } -} - -fun Element.watchPropertyDiv( - vararg classes: String, - watchProperty: KMutableProperty0, - builder: WatchPropertyDiv.(T) -> Unit -) { - val wd = WatchPropertyDiv(watchProperty, builder) - wd.classes.addAll(classes.map { ElementClass(it) }) - this.append(wd) - wd.regenerate(true) - wd.checkJob() -} diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/XYPad.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/XYPad.kt deleted file mode 100644 index 5cd146fd..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/elements/XYPad.kt +++ /dev/null @@ -1,264 +0,0 @@ -package org.openrndr.panel.elements - -import kotlinx.coroutines.yield -import org.openrndr.* -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Drawer - -import org.openrndr.events.Event -import org.openrndr.extra.textwriter.TextWriter -import org.openrndr.math.Vector2 -import org.openrndr.math.clamp -import org.openrndr.math.map -import org.openrndr.panel.style.Color -import org.openrndr.panel.style.color -import org.openrndr.shape.LineSegment -import kotlin.math.pow -import kotlin.math.round -import kotlin.reflect.KMutableProperty0 - - -class XYPad : Element(ElementType("xy-pad")) { - var minX = -1.0 - var minY = -1.0 - var maxX = 1.0 - var maxY = 1.0 - - /** - * The label - */ - var label = "" - - /** - * The precision of the control, default is 2 - */ - var precision = 2 - - /** - * Should the control visualize the value as a vector?, default is false - */ - var showVector = false - - /** - * Should the control invert the Y-axis?, default is true - */ - var invertY = true - - // The value is derived from the normalized value... - var normalizedValue = Vector2(0.0, 0.0) - - var value: Vector2 - get() = Vector2( - map(-1.0, 1.0, minX, maxX, normalizedValue.x).round(precision), - map(-1.0, 1.0, minY, maxY, normalizedValue.y).round(precision) - ) - set(newValue) { - normalizedValue = Vector2( - clamp(map(minX, maxX, -1.0, 1.0, newValue.x), -1.0, 1.0), - clamp(map(minY, maxY, -1.0, 1.0, newValue.y), -1.0, 1.0) - ) - requestRedraw() - } - - init { - mouse.clicked.listen { - it.cancelPropagation() - pick(it) - } - - mouse.dragged.listen { - it.cancelPropagation() - pick(it) - } - - mouse.pressed.listen { - it.cancelPropagation() - } - - keyboard.pressed.listen { handleKeyEvent(it) } - keyboard.repeated.listen { handleKeyEvent(it) } - } - - class ValueChangedEvent(val source: XYPad, - val oldValue: Vector2, - val newValue: Vector2) - - - val events = Events() - - class Events { - val valueChanged = Event("xypad-value-changed") - } - - - private fun handleKeyEvent(keyEvent: KeyEvent) { - val keyboardIncrementX = if (KeyModifier.SHIFT in keyEvent.modifiers) { - (maxX - minX) / 10.0 - } else { - 10.0.pow(-(precision - 0.0)) - } - - val keyboardIncrementY = if (KeyModifier.SHIFT in keyEvent.modifiers) { - (maxY - minY) / 10.0 - } else { - 10.0.pow(-(precision - 0.0)) - } - - val old = value - - if (keyEvent.key == KEY_ARROW_RIGHT) { - value = Vector2(value.x + keyboardIncrementX, value.y) - } - - if (keyEvent.key == KEY_ARROW_LEFT) { - value = Vector2(value.x - keyboardIncrementX, value.y) - } - - if (keyEvent.key == KEY_ARROW_UP) { - value = Vector2(value.x, value.y - keyboardIncrementY * if (invertY) -1.0 else 1.0) - } - - if (keyEvent.key == KEY_ARROW_DOWN) { - value = Vector2(value.x, value.y + keyboardIncrementY * if (invertY) -1.0 else 1.0) - } - - requestRedraw() - events.valueChanged.trigger(ValueChangedEvent(this, old, value)) - keyEvent.cancelPropagation() - } - - private fun pick(e: MouseEvent) { - val old = value - - // Difference - val dx = e.position.x - layout.screenX - val dy = e.position.y - layout.screenY - - // Normalize to -1 - 1 - val nx = clamp(dx / layout.screenWidth * 2.0 - 1.0, -1.0, 1.0) - val ny = clamp(dy / layout.screenHeight * 2.0 - 1.0, -1.0, 1.0) * if (invertY) -1.0 else 1.0 - - normalizedValue = Vector2(nx, ny) - - events.valueChanged.trigger(ValueChangedEvent(this, old, value)) - requestRedraw() - } - - override val widthHint: Double? - get() = 200.0 - - - private val ballPosition: Vector2 - get() = Vector2( - map(-1.0, 1.0, 0.0, layout.screenWidth, normalizedValue.x), - if (invertY) { - map(1.0, -1.0, 0.0, layout.screenHeight, normalizedValue.y) - } else { - map(-1.0, 1.0, 0.0, layout.screenHeight, normalizedValue.y) - } - ) - - private val grid = mutableListOf() - - override fun draw(drawer: Drawer) { - if(grid.isEmpty()) { - repeat(21) { n -> - grid.add( - LineSegment( - 0.0, - layout.screenHeight / 20 * n, - layout.screenWidth - 1.0, - layout.screenHeight / 20 * n - ) - ) - grid.add( - LineSegment( - layout.screenWidth / 20 * n, - 0.0, - layout.screenWidth / 20 * n, - layout.screenHeight - 1.0 - ) - ) - } - - } - computedStyle.let { - drawer.pushTransforms() - drawer.pushStyle() - - drawer.fill = ColorRGBa.GRAY - drawer.stroke = null - drawer.strokeWeight = 0.0 - drawer.rectangle(0.0, 0.0, layout.screenWidth, layout.screenHeight) - - // lines grid - drawer.stroke = ColorRGBa.GRAY.shade(1.1) - drawer.strokeWeight = 1.0 - drawer.lineSegments(grid) - - // cross - drawer.stroke = ColorRGBa.GRAY.shade(1.6) -// drawer.lineSegment(0.0, layout.screenHeight / 2.0, layout.screenWidth, layout.screenHeight / 2.0) -// drawer.lineSegment(layout.screenWidth / 2.0, 0.0, layout.screenWidth / 2.0, layout.screenHeight) - - // angle line from center - if (showVector) { - drawer.lineSegment(Vector2(layout.screenHeight / 2.0, layout.screenWidth / 2.0), ballPosition) - } - - // ball - drawer.fill = ColorRGBa.PINK - drawer.stroke = ColorRGBa.WHITE - drawer.circle(ballPosition, 8.0) - - val valueLabel = "${String.format("%.0${precision}f", value.x)}, ${String.format("%.0${precision}f", value.y)}" - - (root() as? Body)?.controlManager?.fontManager?.let { - val writer = TextWriter(drawer) - drawer.fontMap = it.font(computedStyle) - val textWidth = writer.textWidth(valueLabel) - - drawer.fill = ((computedStyle.color as? Color.RGBa)?.color ?: ColorRGBa.WHITE).opacify( - if (disabled in pseudoClasses) 0.25 else 1.0 - ) - - drawer.text(valueLabel,layout.screenWidth - textWidth - 4.0, layout.screenHeight - 4.0) - drawer.text(label, 0.0, layout.screenHeight + 18.0) - } - - drawer.popStyle() - drawer.popTransforms() - } - } -} - -fun XYPad.bind(property: KMutableProperty0) { - var currentValue: Vector2? = null - - events.valueChanged.listen { - currentValue = it.newValue - property.set(it.newValue) - } - if (root() as? Body == null) { - throw RuntimeException("no body") - } - fun update() { - if (property.get() != currentValue) { - val lcur = property.get() - currentValue = lcur - value = lcur - } - } - update() - (root() as? Body)?.controlManager?.program?.launch { - while (true) { - update() - yield() - } - } -} - -fun Double.round(decimals: Int): Double { - val multiplier = 10.0.pow(decimals) - return round(this * multiplier) / multiplier -} diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/hash/WatchHash.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/hash/WatchHash.kt deleted file mode 100644 index bbb2ec1e..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/hash/WatchHash.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.openrndr.panel.hash - -import kotlin.reflect.KProperty0 -import kotlin.reflect.KProperty1 -import kotlin.reflect.full.declaredMemberProperties - -fun watchHash(toHash: Any): Int { - var hash = 0 - for (property in toHash::class.declaredMemberProperties) { - @Suppress("UNCHECKED_CAST") val v = ((property as KProperty1).getter).invoke(toHash) - if (v is KProperty0<*>) { - val pv = v.get() - hash = 31 * hash + (pv?.hashCode() ?: 0) - } else { - hash = 31 * hash + (v?.hashCode() ?: 0) - } - } - return hash -} - diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/layout/Layouter.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/layout/Layouter.kt deleted file mode 100644 index 1b180deb..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/layout/Layouter.kt +++ /dev/null @@ -1,281 +0,0 @@ -package org.openrndr.panel.layout - -import org.openrndr.math.Vector2 -import org.openrndr.panel.elements.Element -import org.openrndr.panel.elements.TextNode -import org.openrndr.panel.style.* -import org.openrndr.shape.Rectangle -import java.util.* -import kotlin.comparisons.compareBy -import kotlin.math.max - -class Layouter { - val styleSheets = ArrayList() - val blockLike = setOf(Display.BLOCK, Display.FLEX) - val manualPosition = setOf(Position.FIXED, Position.ABSOLUTE) - - fun positionChildren(element: Element, knownWidth:Double? = null): Rectangle { - - return element.computedStyle.let { cs -> - var y = element.layout.screenY - element.scrollTop + element.computedStyle.effectivePaddingTop - - when (cs.display) { - Display.FLEX -> { - when (cs.flexDirection) { - FlexDirection.Row -> { - var maxHeight = 0.0 - var x = element.layout.screenX + element.computedStyle.effectivePaddingLeft - - val totalWidth = element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.map { width(it) }.sum() - val remainder = (knownWidth?: element.layout.screenWidth) - totalWidth - val totalGrow = element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.map { (it.computedStyle.flexGrow as FlexGrow.Ratio).value }.sum() - val totalShrink = element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.map { (it.computedStyle.flexShrink as FlexGrow.Ratio).value }.sum() - - - element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.forEach { child -> - val elementGrow = (child.computedStyle.flexGrow as FlexGrow.Ratio).value - val elementShrink = (child.computedStyle.flexShrink as FlexGrow.Ratio).value - val growWidth = if (totalGrow > 0) (elementGrow / totalGrow) * remainder else 0.0 - val shrinkWidth = if (totalShrink > 0) (elementShrink / totalShrink) * remainder else 0.0 - - child.layout.screenY = y + ((child.computedStyle.marginTop as? LinearDimension.PX)?.value - ?: 0.0) - child.layout.screenX = x + ((child.computedStyle.marginLeft as? LinearDimension.PX)?.value - ?: 0.0) - - child.layout.growWidth = if (remainder > 0) growWidth else shrinkWidth - - val effectiveWidth = width(child) + (if (remainder > 0) growWidth else shrinkWidth) - x += effectiveWidth - maxHeight = max(height(child, effectiveWidth), maxHeight) - } - Rectangle(Vector2(x, y), x - element.layout.screenX, maxHeight) - } - FlexDirection.Column -> { - var maxWidth = 0.0 - var ly = element.layout.screenY + element.computedStyle.effectivePaddingTop - val lx = element.layout.screenX + element.computedStyle.effectivePaddingLeft - - val verticalPadding = element.computedStyle.effectivePaddingTop + element.computedStyle.effectivePaddingBottom - val totalHeight = element.children - .filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition } - .sumOf { height(it, width(it)) } - val remainder = ((element.layout.screenHeight - verticalPadding) - totalHeight) - val totalGrow = element.children - .filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition } - .sumOf { (it.computedStyle.flexGrow as FlexGrow.Ratio).value } - - element.children.filter { it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition }.forEach { child -> - val elementGrow = (child.computedStyle.flexGrow as FlexGrow.Ratio).value - val growHeight = if (totalGrow > 0) (elementGrow / totalGrow) * remainder else 0.0 - - child.layout.screenY = ly + ((child.computedStyle.marginTop as? LinearDimension.PX)?.value - ?: 0.0) - child.layout.screenX = lx + ((child.computedStyle.marginLeft as? LinearDimension.PX)?.value - ?: 0.0) - - child.layout.growHeight = growHeight - - val effectHeight = height(child) + growHeight - ly += effectHeight - maxWidth = max(width(child), maxWidth) - } - - Rectangle(Vector2(lx, ly), maxWidth, ly - element.layout.screenY) - } - else -> Rectangle(Vector2(element.layout.screenX, element.layout.screenY), 0.0, 0.0) - } - } - else -> { - val x = element.layout.screenX + element.computedStyle.effectivePaddingLeft - var maxWidth = 0.0 - element.children.forEach { - if (it.computedStyle.display in blockLike && it.computedStyle.position !in manualPosition) { - it.layout.screenY = y + ((it.computedStyle.marginTop as? LinearDimension.PX)?.value ?: 0.0) - it.layout.screenX = x + ((it.computedStyle.marginLeft as? LinearDimension.PX)?.value ?: 0.0) - val effectiveWidth = width(it) - maxWidth = max(effectiveWidth, maxWidth) - y += height(it, effectiveWidth) - } else if (it.computedStyle.position == Position.ABSOLUTE) { - it.layout.screenX = element.layout.screenX + ((it.computedStyle.left as? LinearDimension.PX)?.value - ?: 0.0) - it.layout.screenY = element.layout.screenY + ((it.computedStyle.top as? LinearDimension.PX)?.value - ?: 0.0) - } - } - Rectangle(Vector2(element.layout.screenX, element.layout.screenY), maxWidth, y - element.layout.screenY) - } - } - } - } - - fun computeStyles(element: Element) { - val matcher = Matcher() - - if (element is TextNode) { - // TODO: figure out why this is needed - element.computedStyle = element.parent?.computedStyle?.cascadeOnto(StyleSheet(CompoundSelector.DUMMY)) - ?: StyleSheet(CompoundSelector.DUMMY) - } else { - element.computedStyle = - styleSheets - .filter { - it.selector.let { - matcher.matches(it, element) - } - } - .sortedWith(compareBy({ it.precedence.component1() }, - { it.precedence.component2() }, - { it.precedence.component3() }, - { it.precedence.component4() })) - .reversed() - .fold(StyleSheet(CompoundSelector.DUMMY), { a, b -> a.cascadeOnto(b) }) - - element.style?.let { - element.computedStyle = it.cascadeOnto(element.computedStyle) - } - } - element.computedStyle.let { cs -> - - element.parent?.let { p -> - cs.properties.forEach { (k, v) -> - if ((v.value as? PropertyValue)?.inherit == true) { - cs.properties[k] = p.computedStyle.getProperty(k) ?: v - } - } - PropertyBehaviours.behaviours.forEach { (k, v) -> - if (v.inheritance == PropertyInheritance.INHERIT && k !in cs.properties) { - if (k in p.computedStyle.properties) { - cs.properties[k] = p.computedStyle.getProperty(k)!! - } - } - } - } - } - - element.children.forEach { computeStyles(it) } - } - - fun margin(element: Element, f: (StyleSheet) -> LinearDimension): Double { - val value = f(element.computedStyle) - return when (value) { - is LinearDimension.PX -> value.value - else -> 0.0 - } - } - - fun padding(element: Element?, f: (StyleSheet) -> LinearDimension): Double { - return if (element != null) { - val value = f(element.computedStyle) - when (value) { - is LinearDimension.PX -> value.value - else -> 0.0 - } - } else 0.0 - } - - fun marginTop(element: Element) = margin(element, StyleSheet::marginTop) - fun marginBottom(element: Element) = margin(element, StyleSheet::marginBottom) - fun marginLeft(element: Element) = margin(element, StyleSheet::marginLeft) - fun marginRight(element: Element) = margin(element, StyleSheet::marginRight) - - fun paddingTop(element: Element?) = padding(element, StyleSheet::paddingTop) - fun paddingBottom(element: Element?) = padding(element, StyleSheet::paddingBottom) - fun paddingLeft(element: Element?) = padding(element, StyleSheet::paddingLeft) - fun paddingRight(element: Element?) = padding(element, StyleSheet::paddingRight) - - fun height(element: Element, width: Double? = null, includeMargins: Boolean = true): Double { - if (element.computedStyle.display == Display.NONE) { - return 0.0 - } - - if (element is TextNode) { - return element.sizeHint().height + if (includeMargins) marginBottom(element) + marginTop(element) else 0.0 - } - - return element.computedStyle.let { - it.height.let { ld -> - when (val it = ld) { - is LinearDimension.PX -> it.value - is LinearDimension.Percent -> { - val parentHeight = element.parent?.layout?.screenHeight ?: 0.0 - val parentPadding = element.parent?.computedStyle?.effectivePaddingHeight ?: 0.0 - val margins = marginTop(element) + marginBottom(element) - val effectiveHeight = (parentHeight - parentPadding) * (it.value / 100.0) - margins - effectiveHeight - } - is LinearDimension.Auto -> { - val padding = paddingTop(element) + paddingBottom(element) - (element.heightHint ?: positionChildren(element, width).height) + padding - } - is LinearDimension.Calculate -> { - val context = CalculateContext(width, null) - it.function(context) - - } - else -> throw RuntimeException("not supported") - } - } + if (includeMargins) ((it.marginTop as? LinearDimension.PX)?.value - ?: 0.0) + ((it.marginBottom as? LinearDimension.PX)?.value ?: 0.0) else 0.0 - } - } - - fun width(element: Element, height: Double? = null, includeMargins: Boolean = true): Double = element.computedStyle.let { - if (element.computedStyle.display == Display.NONE) { - return 0.0 - } - val result = - it.width.let { - when (it) { - is LinearDimension.PX -> it.value - is LinearDimension.Percent -> { - val parentWidth = element.parent?.layout?.screenWidth ?: 0.0 - val parentPadding = element.parent?.computedStyle?.effectivePaddingWidth ?: 0.0 - val margins = marginLeft(element) + marginRight(element) - val effectiveWidth = (parentWidth - parentPadding) * (it.value / 100.0) - margins - effectiveWidth - } -// is LinearDimension.Calculate -> { -// val context = CalculateContext(null, height) -// it.function(context) -// -// } - is LinearDimension.Auto -> (element.widthHint ?: positionChildren(element).width) + - paddingRight(element) + paddingLeft(element) - else -> throw RuntimeException("not supported") - } - } + if (includeMargins) marginLeft(element) + marginRight(element) else 0.0 - - // TODO: find out why this hack is needed, I added this because somewhere in the layout process - // this information is lost - element.layout.screenWidth = result - if (includeMargins) marginLeft(element) + marginRight(element) else 0.0 - result - } - - fun layout(element: Element) { - element.computedStyle.also { cs -> - cs.display.let { if (it == Display.NONE) return } - element.layout.screenWidth = width(element, includeMargins = false) - element.layout.screenWidth += element.layout.growWidth - element.layout.screenHeight = height(element, element.layout.screenWidth, includeMargins = false) - element.layout.screenHeight += element.layout.growHeight - - when (cs.position) { - Position.FIXED -> { - element.layout.screenX = (cs.left as? LinearDimension.PX)?.value ?: 0.0 - element.layout.screenY = (cs.top as? LinearDimension.PX)?.value ?: 0.0 - } - else -> { - } - } - val lzi = cs.zIndex - element.layout.zIndex = when (lzi) { - is ZIndex.Value -> lzi.value - is ZIndex.Auto -> element.parent?.layout?.zIndex ?: 0 - is ZIndex.Inherit -> element.parent?.layout?.zIndex ?: 0 - } - val result = positionChildren(element) - } - element.children.forEach { layout(it) } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/style/DefaultStyles.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/style/DefaultStyles.kt deleted file mode 100644 index be02f005..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/style/DefaultStyles.kt +++ /dev/null @@ -1,210 +0,0 @@ -package org.openrndr.panel.style - -import org.openrndr.color.ColorRGBa - -fun defaultStyles( - controlBackground: ColorRGBa = ColorRGBa(0.5, 0.5, 0.5), - controlHoverBackground: ColorRGBa = controlBackground.shade(1.5), - controlTextColor: Color = Color.RGBa(ColorRGBa.WHITE.shade(0.8)), - controlActiveColor : Color = Color.RGBa(ColorRGBa.fromHex(0xf88379 )), - controlFontSize: Double = 14.0 -) = listOf( - styleSheet(has type "item") { - display = Display.NONE - }, - - styleSheet(has type "textfield") { - width = 100.percent - height = 64.px - and(has state "active") { - color = controlActiveColor - } - }, - - styleSheet(has type "dropdown-button") { - width = LinearDimension.Auto - height = 32.px - background = Color.RGBa(controlBackground) - marginLeft = 5.px - marginRight = 5.px - marginTop = 5.px - marginBottom = 5.px - fontSize = controlFontSize.px - - and(has state "hover") { - background = Color.RGBa(controlHoverBackground) - } - - descendant(has type "button") { - width = 100.percent - height = 24.px - marginBottom = 0.px - marginTop = 0.px - marginLeft = 0.px - marginRight = 0.px - } - }, - - styleSheet(has type "colorpicker-button") { - width = 100.px - height = 32.px - background = Color.RGBa(controlBackground) - marginLeft = 5.px - marginRight = 5.px - marginTop = 5.px - marginBottom = 5.px - - and(has state "hover") { - background = Color.RGBa(controlHoverBackground) - } - }, - - styleSheet(has type "envelope-button") { - width = 100.px - height = 40.px - background = Color.RGBa(controlBackground) - marginLeft = 5.px - marginRight = 5.px - marginTop = 5.px - marginBottom = 5.px - }, - - styleSheet(has type "body") { - fontSize = 18.px - fontFamily = "default" - }, - - styleSheet(has type "slider") { - height = 32.px - width = 100.percent - marginTop = 5.px - marginBottom = 5.px - marginLeft = 5.px - marginRight = 5.px - fontSize = controlFontSize.px - color = controlTextColor - - and(has state "active") { - color = controlActiveColor - } - }, - - styleSheet(has type "envelope-editor") { - height = 60.px - width = 100.percent - marginTop = 5.px - marginBottom = 15.px - marginLeft = 5.px - marginRight = 5.px - }, - - styleSheet(has type listOf( - "sequence-editor", - "sliders-vector2", - "sliders-vector3", - "sliders-vector4" - )) { - height = 60.px - width = 100.percent - marginTop = 5.px - marginBottom = 15.px - marginLeft = 5.px - marginRight = 5.px - color = controlTextColor - and(has state "active") { - color = controlActiveColor - } - }, - - styleSheet(has type "colorpicker") { - height = 80.px - width = 100.percent - marginTop = 5.px - marginBottom = 15.px - marginLeft = 5.px - marginRight = 5.px - }, - - styleSheet(has type "xy-pad") { - display = Display.BLOCK - background = Color.RGBa(ColorRGBa.GRAY) - width = 175.px - height = 175.px - marginLeft = 5.px - marginRight = 5.px - marginTop = 5.px - marginBottom = 25.px - fontFamily = "default" - color = controlTextColor - - and(has state "hover") { - display = Display.BLOCK - background = Color.RGBa(ColorRGBa.GRAY.shade(1.5)) - } - }, - - styleSheet(has type "overlay") { - zIndex = ZIndex.Value(1) - }, - - styleSheet(has type "toggle") { - height = 32.px - width = LinearDimension.Auto - marginTop = 5.px - marginBottom = 5.px - marginLeft = 5.px - marginRight = 5.px - fontSize = controlFontSize.px - color = controlTextColor - }, - - styleSheet(has type "h1") { - fontSize = 24.px - width = 100.percent - height = LinearDimension.Auto - display = Display.BLOCK - }, - - styleSheet(has type "h2") { - fontSize = 20.px - width = 100.percent - height = LinearDimension.Auto - display = Display.BLOCK - }, - - styleSheet(has type "h3") { - fontSize = 16.px - width = 100.percent - height = LinearDimension.Auto - display = Display.BLOCK - }, - - styleSheet(has type "p") { - fontSize = 16.px - width = 100.percent - height = LinearDimension.Auto - display = Display.BLOCK - }, - styleSheet(has type "button") { - display = Display.BLOCK - background = Color.RGBa(controlBackground) - width = LinearDimension.Auto - height = 32.px - paddingLeft = 10.px - paddingRight = 10.px - marginLeft = 5.px - marginRight = 5.px - marginTop = 5.px - marginBottom = 5.px - fontSize = controlFontSize.px - - and(has state "active") { - display = Display.BLOCK - background = controlActiveColor - } - and(has state "hover") { - display = Display.BLOCK - background = Color.RGBa(controlHoverBackground) - } - } -) diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/style/Matcher.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/style/Matcher.kt deleted file mode 100644 index d44ca590..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/style/Matcher.kt +++ /dev/null @@ -1,57 +0,0 @@ -package org.openrndr.panel.style - -import org.openrndr.panel.elements.Element - -class Matcher { - enum class MatchingResult { - MATCHED, NOT_MATCHED, RESTART_FROM_CLOSEST_DESCENDANT, RESTART_FROM_CLOSEST_LATER_SIBLING - } - - fun matches(selector: CompoundSelector, element: Element): Boolean { - return matchesCompound(selector, element) == MatchingResult.MATCHED - } - - private fun matchesCompound(selector: CompoundSelector, element: Element): MatchingResult { - if (selector.selectors.any { !it.accept(element) }) { - return MatchingResult.RESTART_FROM_CLOSEST_LATER_SIBLING - } - - if (selector.previous == null) { - return MatchingResult.MATCHED - } - - val (siblings, candidateNotFound) = - when (selector.previous?.first) { - Combinator.NEXT_SIBLING, Combinator.LATER_SIBLING -> Pair(true, MatchingResult.RESTART_FROM_CLOSEST_DESCENDANT) - else -> Pair(false, MatchingResult.NOT_MATCHED) - } - - var node = element - while (true) { - val nextNode = if (siblings) node.previousSibling() else node.parent - - if (nextNode == null) { - return candidateNotFound - } else { - node = nextNode - } - - val result = matchesCompound(selector.previous?.second!!, node) - - if (result == MatchingResult.MATCHED || result == MatchingResult.NOT_MATCHED) { - return result - } - - when (selector.previous?.first) { - Combinator.CHILD -> return MatchingResult.RESTART_FROM_CLOSEST_DESCENDANT - Combinator.NEXT_SIBLING -> return result - Combinator.LATER_SIBLING -> if (result == MatchingResult.RESTART_FROM_CLOSEST_DESCENDANT) { - return result - } - Combinator.DESCENDANT, null -> { - // intentionally do nothing - } - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/style/Selector.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/style/Selector.kt deleted file mode 100644 index a62c3a17..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/style/Selector.kt +++ /dev/null @@ -1,145 +0,0 @@ -package org.openrndr.panel.style - -import org.openrndr.panel.elements.Element -import org.openrndr.panel.elements.ElementClass -import org.openrndr.panel.elements.ElementPseudoClass -import org.openrndr.panel.elements.ElementType - -data class SelectorPrecedence(var inlineStyle: Int = 0, var id: Int = 0, var classOrAttribute: Int = 0, var type: Int = 0) - -abstract class Selector { - abstract fun accept(element: Element): Boolean -} - -class CompoundSelector { - companion object { - val DUMMY = CompoundSelector() - } - - var previous: Pair? - var selectors: MutableList - - constructor() { - previous = null - selectors = mutableListOf() - } - - constructor(previous: Pair?, selectors: List) { - this.previous = previous - this.selectors = ArrayList() - selectors.forEach { this.selectors.add(it) } - } - - fun precedence(p: SelectorPrecedence = SelectorPrecedence()): SelectorPrecedence { - - selectors.forEach { - when (it) { - is IdentitySelector -> p.id++ - is ClassSelector, is PseudoClassSelector -> p.classOrAttribute++ - is TypeSelector -> p.type++ - else -> { - } - } - } - var r = p - previous?.let { - r = it.second.precedence(p) - } - return r - } - - override fun toString(): String { - return "CompoundSelector(previous=$previous, selectors=$selectors)" - } - -} - -enum class Combinator { - CHILD, DESCENDANT, NEXT_SIBLING, LATER_SIBLING -} - -class IdentitySelector(val id: String) : Selector() { - override fun accept(element: Element): Boolean = if (element.id != null) { - element.id.equals(id) - } else { - false - } - - override fun toString(): String { - return "IdentitySelector(id='$id')" - } - -} - -class ClassSelector(val c: ElementClass) : Selector() { - override fun accept(element: Element): Boolean = c in element.classes - override fun toString(): String { - return "ClassSelector(c=$c)" - } -} - -class TypeSelector(val type: ElementType) : Selector() { - override fun accept(element: Element): Boolean = element.type == type - override fun toString(): String { - return "TypeSelector(type=$type)" - } -} - -class TypesSelector(vararg types: ElementType) : Selector() { - private val typeSet = types.toSet() - override fun accept(element: Element): Boolean = element.type in typeSet - override fun toString(): String { - return "TypesSelector(types=$typeSet)" - } -} - -class PseudoClassSelector(val c: ElementPseudoClass) : Selector() { - override fun accept(element: Element): Boolean = c in element.pseudoClasses - override fun toString(): String { - return "PseudoClassSelector(c=$c)" - } - -} - -object has { - operator fun invoke(vararg selectors: CompoundSelector): CompoundSelector { - val active = CompoundSelector() - selectors.forEach { - active.selectors.addAll(it.selectors) - } - return active - } - - infix fun state(q: String): CompoundSelector { - val active = CompoundSelector() - active.selectors.add(PseudoClassSelector(ElementPseudoClass((q)))) - return active - } - - infix fun class_(q: String): CompoundSelector { - val active = CompoundSelector() - active.selectors.add(ClassSelector(ElementClass(q))) - return active - } - - infix fun type(q: String): CompoundSelector { - val active = CompoundSelector() - active.selectors.add(TypeSelector(ElementType(q))) - return active - } - - infix fun type(qs: Iterable): CompoundSelector { - val active = CompoundSelector() - val aqs = qs.map { ElementType(it) }.toList().toTypedArray() - active.selectors.add(TypesSelector(*aqs)) - return active - } -} - -infix fun CompoundSelector.and(other: CompoundSelector): CompoundSelector { - val c = CompoundSelector() - c.previous = previous - c.selectors.addAll(selectors) - c.selectors.addAll(other.selectors) - return c -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/style/StyleSheet.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/style/StyleSheet.kt deleted file mode 100644 index aebd4b0b..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/style/StyleSheet.kt +++ /dev/null @@ -1,245 +0,0 @@ -package org.openrndr.panel.style - -import org.openrndr.color.ColorRGBa -import org.openrndr.panel.style.PropertyInheritance.INHERIT -import org.openrndr.panel.style.PropertyInheritance.RESET -import java.util.* -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.contract -import kotlin.reflect.KProperty - -enum class PropertyInheritance { - INHERIT, - RESET -} - -@JvmRecord -data class Property(val name: String, - val value: Any?) - -open class PropertyValue(val inherit: Boolean = false) - -sealed class Color(inherit: Boolean = false) : PropertyValue(inherit) { - class RGBa(val color: ColorRGBa) : Color() { - override fun toString(): String { - return "RGBa(color=$color)" - } - } - - object Inherit : Color(inherit = true) -} - -class CalculateContext(val elementWidth: Double?, val elementHeight: Double?) - -sealed class LinearDimension(inherit: Boolean = false) : PropertyValue(inherit) { - class PX(val value: Double) : LinearDimension() { - override fun toString(): String { - return "PX(value=$value)" - } - } - - class Percent(val value: Double) : LinearDimension() - class Calculate(val function: (CalculateContext) -> Double) : LinearDimension() - object Auto : LinearDimension() - object Inherit : LinearDimension(inherit = true) -} - -@JvmRecord -data class PropertyBehaviour(val inheritance: PropertyInheritance, val intitial: Any) - -object PropertyBehaviours { - - val behaviours = HashMap() -} - -class PropertyHandler( - val name: String, val inheritance: PropertyInheritance, val initial: T -) { - - init { - PropertyBehaviours.behaviours[name] = PropertyBehaviour(inheritance, initial as Any) - } - - @Suppress("USELESS_CAST", "UNCHECKED_CAST") - operator fun getValue(stylesheet: StyleSheet, property: KProperty<*>): T { - val value: T? = stylesheet.getProperty(name)?.value as T? - return value ?: PropertyBehaviours.behaviours[name]!!.intitial as T - - } - - operator fun setValue(stylesheet: StyleSheet, property: KProperty<*>, value: T?) { - stylesheet.setProperty(name, value) - } -} - -enum class Display { - INLINE, - BLOCK, - FLEX, - NONE -} - -enum class Position { - STATIC, - ABSOLUTE, - RELATIVE, - FIXED, - INHERIT -} - -sealed class FlexDirection(inherit: Boolean = false) : PropertyValue(inherit) { - object Row : FlexDirection() - object Column : FlexDirection() - object RowReverse : FlexDirection() - object ColumnReverse : FlexDirection() - object Inherit : FlexDirection(inherit = true) -} - -sealed class Overflow(inherit: Boolean = false) : PropertyValue(inherit) { - object Visible : Overflow() - object Hidden : Overflow() - object Scroll : Overflow() - object Inherit : Overflow(inherit = true) -} - -sealed class ZIndex(inherit: Boolean = false) : PropertyValue(inherit) { - object Auto : ZIndex() - class Value(val value: Int) : ZIndex() - object Inherit : ZIndex(inherit = true) -} - -sealed class FlexGrow(inherit: Boolean = false) : PropertyValue(inherit) { - class Ratio(val value: Double) : FlexGrow() - object Inherit : FlexGrow(inherit = true) -} - -private val dummySelector = CompoundSelector() - -class StyleSheet(val selector: CompoundSelector = CompoundSelector.DUMMY) { - val children = mutableListOf() - val properties = HashMap() - - val precedence by lazy { - selector.precedence() - } - - fun getProperty(name: String) = properties.get(name) - - fun setProperty(name: String, value: Any?) { - properties[name] = Property(name, value) - } - - fun cascadeOnto(onto: StyleSheet): StyleSheet { - val cascaded = StyleSheet(dummySelector) - - cascaded.properties.putAll(onto.properties) - cascaded.properties.putAll(properties) - return cascaded - } - - override fun toString(): String { - return "StyleSheet(properties=$properties)" - } -} - -var StyleSheet.width by PropertyHandler("width", RESET, LinearDimension.Auto) -var StyleSheet.height by PropertyHandler("height", RESET, LinearDimension.Auto) -var StyleSheet.top by PropertyHandler("top", RESET, 0.px) // css default is auto -var StyleSheet.left by PropertyHandler("left", RESET, 0.px) // css default is auto - -var StyleSheet.marginTop by PropertyHandler("margin-top", RESET, 0.px) -var StyleSheet.marginBottom by PropertyHandler("margin-bottom", RESET, 0.px) -var StyleSheet.marginLeft by PropertyHandler("margin-left", RESET, 0.px) -var StyleSheet.marginRight by PropertyHandler("margin-right", RESET, 0.px) - - -var StyleSheet.paddingTop by PropertyHandler("padding-top", RESET, 0.px) -var StyleSheet.paddingBottom by PropertyHandler("padding-bottom", RESET, 0.px) -var StyleSheet.paddingLeft by PropertyHandler("padding-left", RESET, 0.px) -var StyleSheet.paddingRight by PropertyHandler("padding-right", RESET, 0.px) - - -var StyleSheet.position by PropertyHandler("position", RESET, Position.STATIC) -var StyleSheet.display by PropertyHandler("display", RESET, Display.BLOCK) // css default is inline - -var StyleSheet.flexDirection by PropertyHandler("flex-direction", RESET, FlexDirection.Row) -var StyleSheet.flexGrow by PropertyHandler("flex-grow", RESET, FlexGrow.Ratio(0.0)) -var StyleSheet.flexShrink by PropertyHandler("flex-shrink", RESET, FlexGrow.Ratio(1.0)) - -var StyleSheet.borderWidth by PropertyHandler("border-width", RESET, 0.px) -var StyleSheet.borderColor by PropertyHandler("border-color", INHERIT, Color.RGBa(ColorRGBa.TRANSPARENT)) - -var StyleSheet.background by PropertyHandler("background-color", RESET, Color.RGBa(ColorRGBa.BLACK.opacify(0.0))) -val StyleSheet.effectiveBackground: ColorRGBa? - get() = (background as? Color.RGBa)?.color - -var StyleSheet.color by PropertyHandler("color", INHERIT, Color.RGBa(ColorRGBa.WHITE)) -val StyleSheet.effectiveColor: ColorRGBa? - get() = (color as? Color.RGBa)?.color - - -val StyleSheet.effectivePaddingLeft: Double - get() = (paddingLeft as? LinearDimension.PX)?.value ?: 0.0 - -val StyleSheet.effectivePaddingRight: Double - get() = (paddingRight as? LinearDimension.PX)?.value ?: 0.0 - -val StyleSheet.effectivePaddingTop: Double - get() = (paddingTop as? LinearDimension.PX)?.value ?: 0.0 - -val StyleSheet.effectivePaddingBottom: Double - get() = (paddingBottom as? LinearDimension.PX)?.value ?: 0.0 - - -val StyleSheet.effectivePaddingHeight: Double - get() = effectivePaddingBottom + effectivePaddingTop - -val StyleSheet.effectivePaddingWidth: Double - get() = effectivePaddingLeft + effectivePaddingRight - - -val StyleSheet.effectiveBorderWidth: Double - get() = (borderWidth as? LinearDimension.PX)?.value ?: 0.0 - -val StyleSheet.effectiveBorderColor: ColorRGBa? - get() = (borderColor as? Color.RGBa)?.color - - -var StyleSheet.fontSize by PropertyHandler("font-size", INHERIT, 14.px) -var StyleSheet.fontFamily by PropertyHandler("font-family", INHERIT, "default") -var StyleSheet.overflow by PropertyHandler("overflow", RESET, Overflow.Visible) -var StyleSheet.zIndex by PropertyHandler("z-index", RESET, ZIndex.Auto) - -val Number.px: LinearDimension.PX get() = LinearDimension.PX(this.toDouble()) -val Number.percent: LinearDimension.Percent get() = LinearDimension.Percent(this.toDouble()) - -fun StyleSheet.child(selector: CompoundSelector, init: StyleSheet.() -> Unit) { - val stylesheet = StyleSheet(selector).apply(init) - stylesheet.selector.previous = Pair(Combinator.CHILD, this.selector) - children.add(stylesheet) -} - -fun StyleSheet.descendant(selector: CompoundSelector, init: StyleSheet.() -> Unit) { - val stylesheet = StyleSheet(selector).apply(init) - stylesheet.selector.previous = Pair(Combinator.DESCENDANT, this.selector) - children.add(stylesheet) -} - -fun StyleSheet.and(selector: CompoundSelector, init: StyleSheet.() -> Unit) { - val stylesheet = StyleSheet(this.selector and selector).apply(init) - this.children.add(stylesheet) -} - -fun StyleSheet.flatten(): List { - return listOf(this) + children.flatMap { it.flatten() } -} - -@OptIn(ExperimentalContracts::class) -fun styleSheet(selector: CompoundSelector = CompoundSelector.DUMMY, init: StyleSheet.() -> Unit): StyleSheet { - contract { - callsInPlace(init, kotlin.contracts.InvocationKind.EXACTLY_ONCE) - } - return StyleSheet(selector).apply { - init() - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/tools/Tooltip.kt b/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/tools/Tooltip.kt deleted file mode 100644 index d6b78daf..00000000 --- a/orx-jvm/orx-panel/src/main/kotlin/org/openrndr/panel/tools/Tooltip.kt +++ /dev/null @@ -1,56 +0,0 @@ -package org.openrndr.panel.tools - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.Drawer -import org.openrndr.draw.isolated -import org.openrndr.draw.loadFont -import org.openrndr.extra.textwriter.writer - -import org.openrndr.math.Vector2 -import org.openrndr.panel.elements.Body -import org.openrndr.panel.elements.Element -import kotlin.math.max - -class Tooltip(val parent: Element, val position: Vector2, val message: String) { - fun draw(drawer: Drawer) { - - val fontUrl = (parent.root() as Body).controlManager.fontManager.resolve("default") ?: error("no font") - val fontSize = 14.0 - val program = (parent.root() as? Body)?.controlManager?.program ?: error("no program") - val fontMap = program.loadFont(fontUrl, fontSize) - - val lines = message.split("\n") - - drawer.isolated { - drawer.fontMap = fontMap - - var maxX = 0.0 - var maxY = 0.0 - writer(drawer) { - for (line in lines) { - newLine() - text(line, false) - maxX = max(maxX, cursor.x) - maxY = cursor.y - } - gaplessNewLine() - maxY = cursor.y - } - - drawer.translate(position) - drawer.translate(10.0, 0.0) - drawer.strokeWeight = 0.5 - drawer.stroke = ColorRGBa.WHITE.opacify(0.25) - drawer.fill = ColorRGBa.GRAY - drawer.rectangle(0.0, 0.0, maxX + 20.0, maxY) - drawer.fill = ColorRGBa.BLACK - drawer.translate(10.0, 0.0) - writer(drawer) { - for (line in lines) { - newLine() - text(line) - } - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-panel/src/main/resources/fonts/Roboto-Medium.ttf b/orx-jvm/orx-panel/src/main/resources/fonts/Roboto-Medium.ttf deleted file mode 100644 index 39c63d74..00000000 Binary files a/orx-jvm/orx-panel/src/main/resources/fonts/Roboto-Medium.ttf and /dev/null differ diff --git a/orx-jvm/orx-panel/src/main/resources/fonts/Roboto-Regular.ttf b/orx-jvm/orx-panel/src/main/resources/fonts/Roboto-Regular.ttf deleted file mode 100644 index 8c082c8d..00000000 Binary files a/orx-jvm/orx-panel/src/main/resources/fonts/Roboto-Regular.ttf and /dev/null differ diff --git a/orx-jvm/orx-panel/src/test/java/org/openrndr/panel/test/SelectorBuilderTest.kt b/orx-jvm/orx-panel/src/test/java/org/openrndr/panel/test/SelectorBuilderTest.kt deleted file mode 100644 index 1d260a05..00000000 --- a/orx-jvm/orx-panel/src/test/java/org/openrndr/panel/test/SelectorBuilderTest.kt +++ /dev/null @@ -1,57 +0,0 @@ -//package org.openrndr.panel.test -// -//import net.lustlab.panel.elements.Element -//import net.lustlab.panel.elements.ElementClass -//import net.lustlab.panel.elements.ElementType -//import net.lustlab.panel.style.* -//import org.jetbrains.spek.api.Spek -//import org.jetbrains.spek.api.dsl.describe -//import org.jetbrains.spek.api.dsl.it -//import kotlin.test.assertEquals -//import kotlin.test.assertFalse -//import kotlin.test.assertNotNull -//import kotlin.test.assertTrue -// -///** -// * Created by voorbeeld on 11/20/16. -// */ -//class SomeTest : Spek({ -// -// describe("a thing") { -// -// // .panel > button -// val cs = selector(class_="panel") withChild selector(type="button", class_="fancy") -// -// val root = Element(ElementType("body")) -// val panel = Element(ElementType("div")).apply { -// classes+= ElementClass("panel") -// } -// val button = Element(ElementType("button")) -// val button2 = Element(ElementType("button")).apply { -// classes+= ElementClass("fancy") -// } -// -// root.append(panel) -// panel.append(button) -// panel.append(button2) -// -// it("should work") { -// assert(cs.selectors.size == 1) -// assertTrue(cs.selectors[0] is TypeSelector) -// assertNotNull(cs.previous) -// -// assertFalse(Matcher().matches(cs, button)) -// assertFalse(Matcher().matches(cs, panel)) -// assertTrue(Matcher().matches(cs, button2)) -// } -// -// it("should have precedences") { -// println(cs.precedence()) -// -// -// } -// -// } -// -//}) -// diff --git a/orx-jvm/orx-panel/src/test/java/org/openrndr/panel/test/StyleSheetTest.kt b/orx-jvm/orx-panel/src/test/java/org/openrndr/panel/test/StyleSheetTest.kt deleted file mode 100644 index f01aded4..00000000 --- a/orx-jvm/orx-panel/src/test/java/org/openrndr/panel/test/StyleSheetTest.kt +++ /dev/null @@ -1,18 +0,0 @@ -//package org.openrndr.panel.test -// -//import net.lustlab.panel.style.* -//import org.jetbrains.spek.api.Spek -//import org.jetbrains.spek.api.dsl.describe -// -//class StyleSheetTest : Spek({ -// -// describe("stylesheet") { -// val styleSheet = StyleSheet() -// styleSheet.width = 5.px -// styleSheet.height = 5.px -// styleSheet.left = 10.px -// styleSheet.top = 10.percent -// styleSheet.position = Position.FIXED -// var a = styleSheet.precedence -// } -//}) diff --git a/orx-jvm/orx-panel/src/test/java/org/openrndr/panel/test/TestPlaceholder.kt b/orx-jvm/orx-panel/src/test/java/org/openrndr/panel/test/TestPlaceholder.kt deleted file mode 100644 index 6bfb33ca..00000000 --- a/orx-jvm/orx-panel/src/test/java/org/openrndr/panel/test/TestPlaceholder.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.openrndr.panel.test - -import kotlin.test.Test - -class TestPlaceholder { - @Test - fun testPlaceholder() { - assert(true) - } -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/README.md b/orx-jvm/orx-poisson-fill/README.md deleted file mode 100644 index a30a530f..00000000 --- a/orx-jvm/orx-poisson-fill/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# orx-poisson-fill - -Post processing effect that fills transparent parts of the image interpolating the edge pixel colors. GPU-based. - - -## Demos -### DemoPoissonFill01 - - - -![DemoPoissonFill01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-poisson-fill/images/DemoPoissonFill01Kt.png) - -[source code](src/demo/kotlin/DemoPoissonFill01.kt) - -### DemoPoissonFill02 - - - -![DemoPoissonFill02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-poisson-fill/images/DemoPoissonFill02Kt.png) - -[source code](src/demo/kotlin/DemoPoissonFill02.kt) diff --git a/orx-jvm/orx-poisson-fill/build.gradle.kts b/orx-jvm/orx-poisson-fill/build.gradle.kts deleted file mode 100644 index 814513ce..00000000 --- a/orx-jvm/orx-poisson-fill/build.gradle.kts +++ /dev/null @@ -1,12 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(project(":orx-fx")) - implementation(project(":orx-noise")) - implementation(openrndr.filter) - demoRuntimeOnly(sharedLibs.slf4j.simple) -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/demo/kotlin/DemoPoissonFill01.kt b/orx-jvm/orx-poisson-fill/src/demo/kotlin/DemoPoissonFill01.kt deleted file mode 100644 index 589f9dea..00000000 --- a/orx-jvm/orx-poisson-fill/src/demo/kotlin/DemoPoissonFill01.kt +++ /dev/null @@ -1,84 +0,0 @@ -import org.openrndr.MouseButton -import org.openrndr.MouseTracker -import org.openrndr.application -import org.openrndr.color.ColorHSVa -import org.openrndr.color.ColorRGBa -import org.openrndr.color.rgb -import org.openrndr.draw.ColorType -import org.openrndr.draw.colorBuffer -import org.openrndr.draw.isolatedWithTarget -import org.openrndr.draw.renderTarget -import org.openrndr.extra.noise.uniform -import org.openrndr.math.Polar -import org.openrndr.math.clamp -import org.openrndr.poissonfill.PoissonFill -import org.openrndr.shape.Rectangle -import kotlin.math.sin - - -fun main() { - data class Thing(val color: ColorRGBa, var pos: Polar, val speed: Polar) - application { - program { - val dry = renderTarget(width, height) { - colorBuffer(type = ColorType.FLOAT32) - } - val wet = colorBuffer(width, height, type = ColorType.FLOAT32) - - val fx = PoissonFill() - - var borderOpacity = 0.0 - - // Create a list of things with - // color, polar position and polar speed - val things = List(10) { - Thing( - ColorHSVa(it * 182.0, - Double.uniform(0.3, 0.6), - Double.uniform(0.1, 0.9)).toRGBa(), - Polar(Double.uniform(0.0, 360.0), - 100.0 + it * 10.0), - Polar(Double.uniform(-1.0, 1.0), 0.0)) - } - val mouseTracker = MouseTracker(mouse) - - extend { - drawer.isolatedWithTarget(dry) { - stroke = null - clear(ColorRGBa.TRANSPARENT) - - // draw color circles - things.forEach { thing -> - fill = thing.color.shade(0.9 + - 0.1 * sin(thing.pos.theta * 0.3)) - circle(thing.pos.cartesian + bounds.center, 5.0) - // A. Use after fix in Polar.kt - //thing.pos += thing.speed - // B. temporary solution - thing.pos = Polar(thing.pos.theta + - thing.speed.theta, thing.pos.radius) - } - - // draw dark gray window border. - // hold mouse button to fade in. - borderOpacity += if (MouseButton.LEFT in mouseTracker.pressedButtons) 0.01 else -0.01 - borderOpacity = borderOpacity.clamp(0.0, 1.0) - stroke = rgb(0.2).opacify(borderOpacity) - fill = null - strokeWeight = 3.0 - rectangle(bounds) - } - - fx.apply(dry.colorBuffer(0), wet) - drawer.image(wet) - - // draw white rectangle - drawer.stroke = ColorRGBa.WHITE.opacify(0.9) - drawer.fill = null - drawer.strokeWeight = 6.0 - drawer.rectangle(Rectangle.fromCenter(drawer.bounds.center, - 300.0, 300.0)) - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/demo/kotlin/DemoPoissonFill02.kt b/orx-jvm/orx-poisson-fill/src/demo/kotlin/DemoPoissonFill02.kt deleted file mode 100644 index 772e02e9..00000000 --- a/orx-jvm/orx-poisson-fill/src/demo/kotlin/DemoPoissonFill02.kt +++ /dev/null @@ -1,27 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.fx.Post -import org.openrndr.math.Polar -import org.openrndr.poissonfill.PoissonFill - -fun main() = application { - program { - extend(Post()) { - val pf = PoissonFill() - post { input, output -> - pf.apply(input, output) - } - } - - extend { - drawer.stroke = null - drawer.clear(ColorRGBa.TRANSPARENT) - - drawer.fill = ColorRGBa.RED - drawer.circle(Polar(60.0 * seconds, 200.0).cartesian + drawer.bounds.center, 20.0) - - drawer.fill = ColorRGBa.BLUE - drawer.circle(Polar(-60.0 * seconds, 200.0).cartesian + drawer.bounds.center, 20.0) - } - } -} diff --git a/orx-jvm/orx-poisson-fill/src/main/kotlin/ConvolutionPyramid.kt b/orx-jvm/orx-poisson-fill/src/main/kotlin/ConvolutionPyramid.kt deleted file mode 100644 index e732ec0b..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/kotlin/ConvolutionPyramid.kt +++ /dev/null @@ -1,140 +0,0 @@ -package org.openrndr.poissonfill - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.fx.blend.Passthrough -import org.openrndr.math.IntVector2 -import org.openrndr.resourceUrl -import kotlin.math.ceil -import kotlin.math.log2 -import kotlin.math.min - -internal class Downscale(filterUrl: String = "/shaders/gl3/poisson/downscale.frag") - : Filter(filterShaderFromUrl(resourceUrl(filterUrl))) { - var h1: FloatArray by parameters - init { - h1 = floatArrayOf(0.0f, 0.0f, 0.0f, 0.0f, 0.0f) - } -} - -internal class Upscale(filterUrl: String = "/shaders/gl3/poisson/upscale.frag") - : Filter(filterShaderFromUrl(resourceUrl(filterUrl))) { - var h1: FloatArray by parameters - var h2: Float by parameters - var g: FloatArray by parameters - - init { - h1 = floatArrayOf(0.0f, 0.0f, 0.0f, 0.0f, 0.0f) - h2 = 0.0f - g = floatArrayOf(0.0f, 0.0f, 0.0f) - } -} - -internal class Convolution(filterUrl: String = "/shaders/gl3/poisson/filter.frag") - : Filter(filterShaderFromUrl(resourceUrl(filterUrl))) { - var g: FloatArray by parameters - - init { - g = floatArrayOf(0.0f, 0.0f, 0.0f) - } -} - -private val passthrough by lazy { Passthrough() } -internal class ConvolutionPyramid(width: Int, height: Int, - private val padding: Int = 0, cutOff: Int = 10000, - private val downscale: Downscale = Downscale(), - private val upscale: Upscale = Upscale(), - private val filter: Convolution = Convolution(), - val type: ColorType = ColorType.FLOAT32) { - var h1 = floatArrayOf(0.0f, 0.0f, 0.0f, 0.0f, 0.0f) - var h2 = 0.0f - var g = floatArrayOf(0.0f, 0.0f, 0.0f) - - private val size = 5 - private val resolution = IntVector2(width + 2 * padding, height + 2 * padding) - private val minResolution = min(resolution.x, resolution.y) - private val depth = min(cutOff, ceil(log2(minResolution.toDouble())).toInt()) - - private val levelsIn = mutableListOf() - private val levelsOut = mutableListOf() - - private val result = colorBuffer(width, height, type = type) - - init { - var levelWidth = resolution.x + 2 * size - var levelHeight = resolution.y + 2 * size - - for (i in 0 until depth) { - levelsIn.add(renderTarget(levelWidth, levelHeight) { - colorBuffer(type = type) - }) - - levelsOut.add(renderTarget(levelWidth, levelHeight) { - colorBuffer(type = type) - }) - - levelWidth /= 2 - levelHeight /= 2 - levelWidth += 2 * size - levelHeight += 2 * size - } - } - - fun process(input: ColorBuffer): ColorBuffer { - for (l in levelsIn) { - l.clearColor(0, ColorRGBa.TRANSPARENT) - } - - for (l in levelsOut) { - l.clearColor(0, ColorRGBa.TRANSPARENT) - } - - downscale.h1 = h1 - - upscale.g = g - upscale.h1 = h1 - upscale.h2 = h2 - - filter.g = g - - passthrough.padding = (levelsIn[0].width - input.width) / 2 - passthrough.apply(input, levelsIn[0].colorBuffer(0)) - passthrough.padding = 0 - - for (i in 1 until levelsIn.size) { - downscale.padding = 0 - downscale.apply(levelsIn[i - 1].colorBuffer(0), - levelsIn[i].colorBuffer(0) - ) - } - - filter.apply(levelsIn.last().colorBuffer(0), levelsOut.last().colorBuffer(0)) - - for (i in levelsOut.size - 2 downTo 0) { - upscale.padding = 0 - upscale.apply(arrayOf(levelsIn[i].colorBuffer(0), levelsOut[i + 1].colorBuffer(0)), arrayOf(levelsOut[i].colorBuffer(0))) - } - - passthrough.padding = -size - padding - passthrough.apply(levelsOut[0].colorBuffer(0), result) - passthrough.padding = 0 - return result - } - - fun destroy() { - result.destroy() - (levelsIn+levelsOut).forEach { - it.colorAttachments.forEach { - when(it) { - is ColorBufferAttachment -> it.colorBuffer.destroy() - is CubemapAttachment -> it.cubemap.destroy() - is ArrayTextureAttachment -> it.arrayTexture.destroy() - is ArrayCubemapAttachment -> it.arrayCubemap.destroy() - else -> {} - } - } - it.detachColorAttachments() - it.destroy() - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/kotlin/LaplacianIntegrator.kt b/orx-jvm/orx-poisson-fill/src/main/kotlin/LaplacianIntegrator.kt deleted file mode 100644 index d8a02af0..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/kotlin/LaplacianIntegrator.kt +++ /dev/null @@ -1,46 +0,0 @@ -package org.openrndr.poissonfill - -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.resourceUrl - -internal class PassthroughNoAlpha : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/passthrough-noalpha.frag"))) - -/** - * Laplacian filter - */ -class Laplacian : Filter1to1(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/laplacian.frag"))) - -class LaplacianIntegrator(width: Int, height: Int, downscaling: Int = 1, type: ColorType = ColorType.FLOAT32) { - private val pyramid = ConvolutionPyramid(2 + width / downscaling, 2 + height / downscaling, type = type) - private val h1 = floatArrayOf(0.15f, 0.5f, 0.7f, 0.5f, 0.15f) - private val h2 = 1.0f - private val g = floatArrayOf(0.175f, 0.547f, 0.175f) - private val preproc = colorBuffer(width + 2, height + 2, type = type) - private val combined = colorBuffer(width, height) - private val passthrough = PassthroughNoAlpha() - - init { - pyramid.h1 = h1 - pyramid.g = g - pyramid.h2 = h2 - } - - fun process(input: ColorBuffer): ColorBuffer { - preproc.fill(ColorRGBa.TRANSPARENT) - - pyramid.h1 = h1 - pyramid.g = g - pyramid.h2 = h2 - - passthrough.padding = 1 - passthrough.apply(input, preproc) - passthrough.padding = 0 - - val result = pyramid.process(preproc) - passthrough.padding = -1 - passthrough.apply(result, combined) - passthrough.padding = 0 - return combined - } -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/kotlin/PoissonBlender.kt b/orx-jvm/orx-poisson-fill/src/main/kotlin/PoissonBlender.kt deleted file mode 100644 index b814deb0..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/kotlin/PoissonBlender.kt +++ /dev/null @@ -1,119 +0,0 @@ -package org.openrndr.poissonfill - -import org.openrndr.draw.* -import org.openrndr.extra.fx.blend.Passthrough -import org.openrndr.extra.fx.blend.Subtract -import org.openrndr.filter.color.delinearize -import org.openrndr.filter.color.linearize -import org.openrndr.resourceUrl -import org.openrndr.shape.Rectangle - -internal class BlendBoundary : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/blend-boundary.frag"))) -class AlphaToBitmap : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/alpha-to-bitmap.frag"))) - -internal class BlendCombine : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/blend-combine.frag"))) { - var softMaskGain: Double by parameters - init { - softMaskGain = 1.0 - } -} - -internal class Clamp : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/clamp.frag"))) { - var minValue: Double by parameters - var maxValue: Double by parameters -} - -private val passthrough by lazy { Passthrough() } -private val subtract by lazy { Subtract() } - -class PoissonBlender(val width: Int, val height: Int, type: ColorType = ColorType.FLOAT32) { - private val pyramid = ConvolutionPyramid(width, height, 0, type = type) - private val preprocess = colorBuffer(width, height, type = type) - private val combined = colorBuffer(width, height, type = type) - - private val fillBoundary = BlendBoundary() - private val fillCombine = BlendCombine() - - private val difference = colorBuffer(width, height, type = type) - - private val h1 = floatArrayOf(0.1507146f, 0.6835785f, 1.0334191f, 0.6836f, 0.1507f) - private val h2 = 0.0269546f - private val g = floatArrayOf(0.0311849f, 0.7752854f, 0.0311849f) - - - init { - pyramid.h1 = h1 - pyramid.g = g - pyramid.h2 = h2 - } - - fun process(target: ColorBuffer, source: ColorBuffer, mask: ColorBuffer, - softMask: ColorBuffer = mask, softMaskGain: Double = 1.0): ColorBuffer { - subtract.apply(arrayOf(target, source), difference) - fillBoundary.apply(arrayOf(difference, mask), preprocess) - val result = pyramid.process(preprocess) - fillCombine.softMaskGain = softMaskGain - fillCombine.apply(arrayOf(result, target, source, mask, softMask), arrayOf(combined)) - return combined - } - - fun destroy() { - pyramid.destroy() - preprocess.destroy() - combined.destroy() - difference.destroy() - } - -} - -class PoissonBlend: Filter2to1(null) { - private var blender: PoissonBlender? = null - - val alphaToBitmap = AlphaToBitmap() - var mask: ColorBuffer? = null - - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - if (target.isNotEmpty()) { - - mask?.let { - if (it.width != target[0].width || it.height != target[0].height) { - it.destroy() - mask = null - } - } - - if (mask == null) { - mask = colorBuffer(target[0].width, target[0].height) - } - - blender?.let { - if (it.width != target[0].width || it.height != target[0].height) { - it.destroy() - blender = null - } - } - - if (blender == null) { - blender = PoissonBlender(target[0].width, target[0].height) - } - - mask?.let { - alphaToBitmap.apply(source[1], it) - } - - - blender?.let { - - linearize.apply(source[0], source[0]) - linearize.apply(source[1], source[1]) - - val result = it.process(source[0], source[1], mask ?: error("no mask")) - result.copyTo(target[0]) - - delinearize.apply(target[0], target[0]) - } - - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/kotlin/PoissonFiller.kt b/orx-jvm/orx-poisson-fill/src/main/kotlin/PoissonFiller.kt deleted file mode 100644 index 89bcf1b8..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/kotlin/PoissonFiller.kt +++ /dev/null @@ -1,65 +0,0 @@ -package org.openrndr.poissonfill - -import org.openrndr.draw.* -import org.openrndr.resourceUrl -import org.openrndr.shape.Rectangle - -internal class FillBoundary : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/fill-boundary.frag"))) -internal class FillCombine : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/fill-combine.frag"))) - -class PoissonFiller(val width: Int, val height: Int, type: ColorType = ColorType.FLOAT32) { - private val pyramid = ConvolutionPyramid(width, height, 0, type = type) - private val preproc = colorBuffer(width, height, type = type) - private val combined = colorBuffer(width, height, type = type) - private val fillBoundary = FillBoundary() - private val fillCombine = FillCombine() - - private val h1 = floatArrayOf(0.1507146f, 0.6835785f, 1.0334191f, 0.6836f, 0.1507f) - private val h2 = 0.0269546f - private val g = floatArrayOf(0.0311849f, 0.7752854f, 0.0311849f) - - init { - pyramid.h1 = h1 - pyramid.g = g - pyramid.h2 = h2 - } - - fun process(input: ColorBuffer): ColorBuffer { - fillBoundary.apply(input, preproc) - val result = pyramid.process(preproc) - fillCombine.apply(arrayOf(result, input), arrayOf(combined)) - return combined - } - - fun destroy() { - preproc.destroy() - combined.destroy() - } -} - -/** - * Poison filling as a filter - */ -class PoissonFill : Filter1to1(null) { - private var filler: PoissonFiller? = null - override fun apply(source: Array, target: Array, clip: Rectangle?) { - require(clip == null) - if (target.isNotEmpty()) { - filler?.let { - if (it.width != target[0].effectiveWidth || it.height != target[0].effectiveHeight) { - it.destroy() - filler = null - } - } - - if (filler == null) { - filler = PoissonFiller(target[0].effectiveWidth, target[0].effectiveHeight) - } - - filler?.let { - val result = it.process(source[0]) - result.copyTo(target[0]) - } - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/alpha-to-bitmap.frag b/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/alpha-to-bitmap.frag deleted file mode 100644 index 07d85216..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/alpha-to-bitmap.frag +++ /dev/null @@ -1,11 +0,0 @@ -#version 330 - -in vec2 v_texCoord0; -uniform sampler2D tex0; -out vec4 o_output; - -void main(){ - vec4 c = texture(tex0, v_texCoord0); - o_output.rgb = vec3(step(0.01, c.a)); - o_output.a = 1.0; -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/blend-boundary.frag b/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/blend-boundary.frag deleted file mode 100644 index d6829ee5..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/blend-boundary.frag +++ /dev/null @@ -1,37 +0,0 @@ -// adapted from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/fill-boundary.frag - -#version 330 - -in vec2 v_texCoord0; -uniform sampler2D tex0; // input image -uniform sampler2D tex1; // mask - -out vec4 o_output; - -/** Output color only on the edges of the black regions in the input image, along with a 1.0 alpha. */ -void main(){ - - o_output = vec4(0.0); - - vec4 fullColor = textureLod(tex0, v_texCoord0, 0.0); - float maskColor = textureLod(tex1, v_texCoord0, 0.0).r; - - float isInMask = maskColor == 1.0 ? 1.0 : 0.0; - - float maskLaplacian = -4.0 * isInMask; - float mask110 = textureLodOffset(tex1, v_texCoord0, 0.0, ivec2( 1, 0)).r; - float mask101 = textureLodOffset(tex1, v_texCoord0, 0.0, ivec2( 0, 1)).r; - float mask010 = textureLodOffset(tex1, v_texCoord0, 0.0, ivec2(-1, 0)).r; - float mask001 = textureLodOffset(tex1, v_texCoord0, 0.0, ivec2( 0,-1)).r; - - maskLaplacian += mask110 == 1.0 ? 1.0 : 0.0; - maskLaplacian += mask101 == 1.0 ? 1.0 : 0.0; - maskLaplacian += mask010 == 1.0 ? 1.0 : 0.0; - maskLaplacian += mask001 == 1.0 ? 1.0 : 0.0; - - if(maskLaplacian > 0.0){ - o_output.rgb = fullColor.rgb; - o_output.a = 1.0; - } - -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/blend-combine.frag b/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/blend-combine.frag deleted file mode 100644 index a8b7d5b6..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/blend-combine.frag +++ /dev/null @@ -1,46 +0,0 @@ -#version 330 - -in vec2 v_texCoord0; - -uniform sampler2D tex0; // membrane -uniform sampler2D tex1; // target image -uniform sampler2D tex2; // source image -uniform sampler2D tex3; // mask -uniform sampler2D tex4; // soft mask - -uniform float softMaskGain; -out vec4 o_output; - -void main(){ - - vec4 targetColor = textureLod(tex1, v_texCoord0, 0.0).rgba; - float maskColor = textureLod(tex3, v_texCoord0, 0.0).r; - float mask = maskColor == 1.0 ? 1.0 : 0.0; - float softMask = textureLod(tex4, v_texCoord0, 0.0).r; - - float maskLaplacian = -4.0 * mask; - float mask110 = textureLodOffset(tex3, v_texCoord0, 0.0, ivec2( 1, 0)).r; - float mask101 = textureLodOffset(tex3, v_texCoord0, 0.0, ivec2( 0, 1)).r; - float mask010 = textureLodOffset(tex3, v_texCoord0, 0.0, ivec2(-1, 0)).r; - float mask001 = textureLodOffset(tex3, v_texCoord0, 0.0, ivec2( 0,-1)).r; - - maskLaplacian += mask110 == 1.0 ? 1.0 : 0.0; - maskLaplacian += mask101 == 1.0 ? 1.0 : 0.0; - maskLaplacian += mask010 == 1.0 ? 1.0 : 0.0; - maskLaplacian += mask001 == 1.0 ? 1.0 : 0.0; - - if (maskLaplacian > 0) { - mask = 1; - } - { - vec4 sourceColor = textureLod(tex2, v_texCoord0, 0.0); - vec4 membraneColor = textureLod(tex0, v_texCoord0, 0.0); - membraneColor.rgb /= membraneColor.a; - - vec3 blend = membraneColor.rgb + sourceColor.rgb; - - o_output.rgb = mix(targetColor.rgb, blend, mask*max(0.0,min(1.0, softMask * softMaskGain))); - o_output.a = 1.0; - - } -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/clamp.frag b/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/clamp.frag deleted file mode 100644 index f70628a3..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/clamp.frag +++ /dev/null @@ -1,25 +0,0 @@ -// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/fill-boundary.frag - -#version 330 - -in vec2 v_texCoord0; -uniform sampler2D tex0; -uniform int levels; -out vec4 o_output; -uniform float scale; -uniform float phase; - -uniform float minValue; -uniform float maxValue; - -void main(){ - - vec4 c = texture(tex0, v_texCoord0); - - c.rgb = clamp(c.rgb, vec3(minValue), vec3(maxValue)); - - o_output = c; - - - -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/downscale.frag b/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/downscale.frag deleted file mode 100644 index 90260d11..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/downscale.frag +++ /dev/null @@ -1,48 +0,0 @@ -// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/downscale.frag -in vec2 v_texCoord0; - -uniform vec2 targetSize; -uniform sampler2D tex0; -out vec4 o_output; -uniform vec2 padding; -uniform float h1[5]; ///< h1 filter parameters. - -/** Denotes if a pixel falls outside an image. - \param pos the pixel position - \param size the image size - \return true if the pixel is outside of the image - */ -bool isOutside(ivec2 pos, ivec2 size){ - - return (pos.x < 0 || pos.y < 0 || pos.x > size.x || pos.y > size.y); -} - -/** Apply the h1 filter and downscale the input data by a factor of 2. */ -void main(){ - - vec4 accum = vec4(0.0); - - ivec2 size = textureSize(tex0, 0).xy; - - ivec2 ts = size; - - //ivec2 ts = ivec2(targetSize - 2 * padding); - - // Our current size is half this one, so we have to scale by 2. - - ivec2 coords = ivec2(floor( targetSize * v_texCoord0)) * 2 - ivec2(10); - - - for(int dy = -2; dy <=2; dy++){ - for(int dx = -2; dx <=2; dx++){ - ivec2 newPix = coords+ivec2(dx,dy); - if(isOutside(newPix, size)){ - continue; - //accum = vec4(1.0, 0.0, 0.0, 1.0); - } - accum += h1[dx+2] * h1[dy+2] * texelFetch(tex0, newPix,0 - ); - } - } - o_output = accum; -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/fill-boundary.frag b/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/fill-boundary.frag deleted file mode 100644 index 684db8b6..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/fill-boundary.frag +++ /dev/null @@ -1,61 +0,0 @@ -// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/fill-boundary.frag - -in vec2 v_texCoord0; -uniform sampler2D tex0; - -//layout(binding = 0) uniform sampler2D screenTexture; ///< Image to process. - -//layout(location = 0) out vec4 fragColor; ///< Color. - -out vec4 o_output; - -/** Denotes if a pixel falls outside an image. - \param pos the pixel position - \param size the image size - \return true if the pixel is outside of the image - */ - -/** Output color only on the edges of the black regions in the input image, along with a 1.0 alpha. */ -void main(){ - - o_output = vec4(0.0); - - vec4 fullColor = textureLod(tex0, v_texCoord0, 0.0); - - float isInMask = fullColor.a == 1.0 ? 0.0 : 1.0; //float(all(equal(fullColor.rgb, vec3(0.0)))); - float maskLaplacian = -4.0*(1.0-fullColor.a); - -// float maskLaplacian = -4.0 * isInMask; - vec4 cola110 = textureLodOffset(tex0, v_texCoord0, 0.0, ivec2( 1, 0)); - vec4 cola101 = textureLodOffset(tex0, v_texCoord0, 0.0, ivec2( 0, 1)); - vec4 cola010 = textureLodOffset(tex0, v_texCoord0, 0.0, ivec2(-1, 0)); - vec4 cola001 = textureLodOffset(tex0, v_texCoord0, 0.0, ivec2( 0,-1)); - - vec3 col110 = cola110.rgb; // cola110.a; - vec3 col101 = cola101.rgb; // cola101.a; - vec3 col010 = cola010.rgb; // cola010.a; - vec3 col001 = cola001.rgb; // cola001.a; -// maskLaplacian += float(all(equal(col110, vec3(0.0)))); -// maskLaplacian += float(all(equal(col101, vec3(0.0)))); -// maskLaplacian += float(all(equal(col010, vec3(0.0)))); -// maskLaplacian += float(all(equal(col001, vec3(0.0)))); - -// maskLaplacian += cola110.a == 1.0 ? 0.0 : 1.0; -// maskLaplacian += cola101.a == 1.0 ? 0.0 : 1.0; -// maskLaplacian += cola010.a == 1.0 ? 0.0 : 1.0; -// maskLaplacian += cola001.a == 1.0 ? 0.0 : 1.0; - - maskLaplacian += (1.0-cola110.a); - maskLaplacian += (1.0-cola101.a); - maskLaplacian += (1.0-cola010.a); - maskLaplacian += (1.0-cola001.a); - - if(maskLaplacian > 0.0){ - o_output.rgb = fullColor.rgb;///fullColor.a;; - o_output.a = fullColor.a; - - } - - - -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/fill-combine.frag b/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/fill-combine.frag deleted file mode 100644 index 106b5683..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/fill-combine.frag +++ /dev/null @@ -1,21 +0,0 @@ -// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/fill-combine.frag - -in vec2 v_texCoord0; - -uniform sampler2D tex0; // result of pyramid convolution -uniform sampler2D tex1; // input image - -out vec4 o_output; - -/** Composite the initial image and the filled image in the regions where the initial image is black. */ -void main(){ - - vec4 inputColor = textureLod(tex1, v_texCoord0, 0.0); - float mask = 1.0 - inputColor.a; - - vec4 fillColor = textureLod(tex0, v_texCoord0, 0.0); - fillColor.rgb /= fillColor.a; - - o_output.rgb = fillColor.rgb * (mask) + inputColor.rgb; //mix(inputColor.rgb, fillColor.rgb, mask); - o_output.a = 1.0; -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/filter.frag b/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/filter.frag deleted file mode 100644 index b6fea945..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/filter.frag +++ /dev/null @@ -1,39 +0,0 @@ -// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/filter.frag - -//layout(binding = 0) uniform sampler2D screenTexture; ///< Level to filter. - -uniform sampler2D tex0; -in vec2 v_texCoord0; -out vec4 o_output; - -uniform float g[3]; ///< g filter parameters. - -/** Denotes if a pixel falls outside an image. - \param pos the pixel position - \param size the image size - \return true if the pixel is outside of the image - */ -bool isOutside(ivec2 pos, ivec2 size){ - return (pos.x < 0 || pos.y < 0 || pos.x >= size.x || pos.y >= size.y); -} - -/** Apply the g filter to the input data. */ -void main(){ - vec4 accum = vec4(0.0); - ivec2 size = textureSize(tex0, 0).xy; - - ivec2 coords = ivec2(v_texCoord0 * vec2(size)); - - for(int dy = -1; dy <=1; dy++){ - for(int dx = -1; dx <=1; dx++){ - - ivec2 newPix = coords + ivec2(dx,dy); - - if(isOutside(newPix, size)){ - continue; - } - accum += g[dx+1] * g[dy+1] * texelFetch(tex0, newPix,0 ); - } - } - o_output = accum; -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/laplacian.frag b/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/laplacian.frag deleted file mode 100644 index 9df11eca..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/laplacian.frag +++ /dev/null @@ -1,52 +0,0 @@ -// adapted from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/laplacian.frag -#version 330 - -in vec2 v_texCoord0; -uniform sampler2D tex0; -out vec4 o_output; - -/** Denotes if UV coordinates falls outside an image. - \param pos the UV coordinates - \return true if the UV are outside of the image - */ -bool isOutside(vec2 pos){ - return (pos.x < 0.0 || pos.y < 0.0 || pos.x > 1.0 || pos.y > 1.0); -} - -/** Compute the Laplacian field of an input RGB image, adding a 1px black border around it before computing the gradients and divergence. */ -void main(){ - vec3 div = vec3(0.0); - ivec2 size = textureSize(tex0, 0).xy; - - vec3 pixelShift = vec3(0.0); - pixelShift.xy = 1.0/vec2(size); - - vec2 uvs = v_texCoord0; - if(!isOutside(uvs)){ - vec3 col = textureLod(tex0, uvs, 0.0).rgb; - div = 4.0 * col; - } - - vec2 uvs110 = uvs + pixelShift.xz; - if(!isOutside(uvs110)){ - vec3 col110 = textureLod(tex0, uvs110, 0.0).rgb; - div -= col110; - } - vec2 uvs101 = uvs + pixelShift.zy; - if(!isOutside(uvs101)){ - vec3 col101 = textureLod(tex0, uvs101, 0.0).rgb; - div -= col101; - } - vec2 uvs010 = uvs - pixelShift.xz; - if(!isOutside(uvs010)){ - vec3 col010 = textureLod(tex0, uvs010, 0.0).rgb; - div -= col010; - } - vec2 uvs001 = uvs - pixelShift.zy; - if(!isOutside(uvs001)){ - vec3 col001 = textureLod(tex0, uvs001, 0.0).rgb; - div -= col001; - } - o_output.rgb = div; - o_output.a = 1.0f; -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/passthrough-noalpha.frag b/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/passthrough-noalpha.frag deleted file mode 100644 index 16a5cac6..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/passthrough-noalpha.frag +++ /dev/null @@ -1,20 +0,0 @@ -#version 330 - -in vec2 v_texCoord0; -uniform sampler2D tex0; - -out vec4 o_output; - -/** Denotes if a pixel falls outside an image. - \param pos the pixel position - \param size the image size - \return true if the pixel is outside of the image - */ - -/** Output an image translated by a fixed number of pixels on each axis. useful for padding when rendering in a larger framebuffer. */ -void main(){ - vec4 c = texture(tex0, v_texCoord0); - - o_output.rgb = c.rgb; - o_output.a = 1.0; -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/remove-alpha.frag b/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/remove-alpha.frag deleted file mode 100644 index 99378517..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/remove-alpha.frag +++ /dev/null @@ -1,18 +0,0 @@ -// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/fill-boundary.frag - -#version 330 - -in vec2 v_texCoord0; -uniform sampler2D tex0; - -out vec4 o_output; - -void main(){ - o_output = vec4(0.0); - vec4 fullColor = textureLod(tex0, v_texCoord0, 0.0); - - if (fullColor.a == 1.0) { - o_output = fullColor; - } - -} \ No newline at end of file diff --git a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/upscale.frag b/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/upscale.frag deleted file mode 100644 index 8dfaf990..00000000 --- a/orx-jvm/orx-poisson-fill/src/main/resources/shaders/gl3/poisson/upscale.frag +++ /dev/null @@ -1,58 +0,0 @@ -// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/upscale.frag - -in vec2 v_texCoord0; - -uniform sampler2D tex0; ///< Current h1 filtered level. -uniform sampler2D tex1; ///< Previous h1+g filtered level. - -out vec4 o_output; ///< Color. - -uniform float h1[5]; ///< h1 filter parameters. -uniform float h2; ///< h2 scaling parameter. -uniform float g[3]; ///< g filter parameters. - -/** Denotes if a pixel falls outside an image. - \param pos the pixel position - \param size the image size - \return true if the pixel is outside of the image - */ -bool isOutside(ivec2 pos, ivec2 size){ - return (pos.x < 0 || pos.y < 0 || pos.x >= size.x || pos.y >= size.y); -} - -/** Combine previous level filtered with h2 (applying a 0-filled upscaling) and the current level filtered with g. - */ -void main(){ - vec4 accum = vec4(0.0); - ivec2 size = textureSize(tex0, 0).xy; - ivec2 coords = ivec2(v_texCoord0 * vec2(size)); - - for(int dy = -1; dy <=1; dy++){ - for(int dx = -1; dx <=1; dx++){ - ivec2 newPix = coords+ivec2(dx,dy); - if(isOutside(newPix, size)){ - continue; - } - accum += g[dx+1] * g[dy+1] * texelFetch(tex0, newPix,0); - } - } - - ivec2 sizeSmall = textureSize(tex1, 0).xy; - - for(int dy = -2; dy <=2; dy++){ - for(int dx = -2; dx <=2; dx++){ - ivec2 newPix = coords+ivec2(dx,dy); - // The filter is applied to a texture upscaled by inserting zeros. - if(newPix.x%2 != 0 || newPix.y%2 != 0){ - continue; - } - newPix /= 2; - newPix += 5; - if(isOutside(newPix, sizeSmall)){ - accum = vec4(0.0, 0.0, 1.0, 1.0); - } - accum += h2 * h1[dx+2] * h1[dy+2] * texelFetch(tex1, newPix, 0); - } - } - o_output = accum; -} \ No newline at end of file diff --git a/orx-jvm/orx-processing/README.md b/orx-jvm/orx-processing/README.md deleted file mode 100644 index c9bf8ac7..00000000 --- a/orx-jvm/orx-processing/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# orx-processing - -orx-processing is a module designed to facilitate seamless type conversions -between Processing's types and OPENRNDR's types. It provides utilities and -methods that allow developers to integrate the two graphics frameworks -effectively by bridging the gap between their respective data structures. - -For example, orx-processing enables you to: - - Convert Processing's PVector to OPENRNDR's Vector2 or Vector3. - - Transform OPENRNDR Shape and ShapeContour into their Processing equivalents. - -This module is particularly useful in projects that require the features or -APIs of both Processing and OPENRNDR, simplifying interoperability and reducing boilerplate code for type translation. - -## Demos -### DemoPShape01 - - - -![DemoPShape01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-processing/images/DemoPShape01Kt.png) - -[source code](src/demo/kotlin/DemoPShape01.kt) - -### DemoPShape02 - - - -![DemoPShape02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-processing/images/DemoPShape02Kt.png) - -[source code](src/demo/kotlin/DemoPShape02.kt) diff --git a/orx-jvm/orx-processing/build.gradle.kts b/orx-jvm/orx-processing/build.gradle.kts deleted file mode 100644 index 102296ca..00000000 --- a/orx-jvm/orx-processing/build.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - api(libs.processing.core) { - exclude(group = "org.jogamp.gluegen") - exclude(group = "org.jogamp.jogl") - } - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(sharedLibs.kotlin.reflect) - demoRuntimeOnly(sharedLibs.slf4j.simple) - demoImplementation(project(":orx-shapes")) -} \ No newline at end of file diff --git a/orx-jvm/orx-processing/src/demo/kotlin/DemoPShape01.kt b/orx-jvm/orx-processing/src/demo/kotlin/DemoPShape01.kt deleted file mode 100644 index 51ba5ffa..00000000 --- a/orx-jvm/orx-processing/src/demo/kotlin/DemoPShape01.kt +++ /dev/null @@ -1,14 +0,0 @@ -import org.openrndr.application -import org.openrndr.extra.processing.PShape -import org.openrndr.extra.processing.toShape - -fun main() = application { - program { - val c = drawer.bounds.offsetEdges(-100.0).shape - val ps = PShape(c) - val rc = ps.toShape() - extend { - drawer.shape(rc) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-processing/src/demo/kotlin/DemoPShape02.kt b/orx-jvm/orx-processing/src/demo/kotlin/DemoPShape02.kt deleted file mode 100644 index cc95a3dc..00000000 --- a/orx-jvm/orx-processing/src/demo/kotlin/DemoPShape02.kt +++ /dev/null @@ -1,26 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.processing.PShape -import org.openrndr.extra.processing.toShape -import org.openrndr.extra.shapes.primitives.regularStarRounded - -fun main() = application { - program { - val c = regularStarRounded( - points = 5, - innerRadius = 100.0, - outerRadius = 200.0, - innerFactor = 0.25, - outerFactor = 0.75, - center = drawer.bounds.center - ) - val ps = PShape(c) - val rc = ps.toShape() - extend { - drawer.fill = ColorRGBa.PINK.opacify(0.5) - drawer.shape(rc) - drawer.translate(15.0, 15.0) - drawer.contour(c) - } - } -} \ No newline at end of file diff --git a/orx-jvm/orx-processing/src/main/kotlin/PShapeExtensions.kt b/orx-jvm/orx-processing/src/main/kotlin/PShapeExtensions.kt deleted file mode 100644 index 4890c542..00000000 --- a/orx-jvm/orx-processing/src/main/kotlin/PShapeExtensions.kt +++ /dev/null @@ -1,276 +0,0 @@ -package org.openrndr.extra.processing - -import org.openrndr.color.ColorRGBa -import org.openrndr.math.Vector2 -import org.openrndr.shape.Segment2D -import org.openrndr.shape.SegmentType -import org.openrndr.shape.Shape -import org.openrndr.shape.ShapeContour -import processing.core.PShape - -/** - * Appends a vertex to the current shape using a 2D vector. - * - * @param v the [Vector2] instance containing the x and y coordinates of the vertex to be added. - * The coordinates are converted to Float for use in the shape. - */ -fun PShape.vertex(v: Vector2) { - vertex(v.x.toFloat(), v.y.toFloat()) -} - -/** - * Adds a quadratic Bezier vertex to the shape. The method specifies control and anchor points - * for the curve using [Vector2] instances. - * - * @param v2 The control point of the quadratic Bezier curve, used to define its curvature. - * @param v3 The anchor point of the quadratic Bezier curve, which is the endpoint of the curve. - */ -fun PShape.quadraticVertex(v2: Vector2, v3: Vector2) { - quadraticVertex( - v2.x.toFloat(), v2.y.toFloat(), - v3.x.toFloat(), v3.y.toFloat() - ) -} - -fun PShape.fill(c: ColorRGBa) { - fill(c.r.toFloat() * 255.0f, c.g.toFloat() * 255.0f, c.b.toFloat() * 255.0f, c.alpha.toFloat() * 255.0f) -} - -fun PShape.stroke(c: ColorRGBa) { - stroke(c.r.toFloat() * 255.0f, c.g.toFloat() * 255.0f, c.b.toFloat() * 255.0f, c.alpha.toFloat() * 255.0f) -} - -/** - * Adds a cubic Bézier curve vertex to the current shape. The curve is defined - * by two control points and an end point. - * - * @param v2 The first control point that influences the direction and shape of the curve. - * @param v3 The second control point that affects the curvature of the Bézier curve. - * @param v4 The end point where the Bézier curve terminates. - */ -fun PShape.bezierVertex(v2: Vector2, v3: Vector2, v4: Vector2) { - bezierVertex( - v2.x.toFloat(), v2.y.toFloat(), - v3.x.toFloat(), v3.y.toFloat(), - v4.x.toFloat(), v4.y.toFloat() - ) -} - -/** - * Combines a list of [Shape] objects into a single [PShape] object of type `GROUP` - * by adding each shape as a child. - * - * Each [Shape] in the input list is converted to a [PShape], and these are added as - * children to a parent [PShape] of type `GROUP`. This allows for easy grouping and management - * of multiple [Shape] objects while using the Processing library. - * - * @param shapes the list of [Shape] objects to combine. Each shape is converted and added - * as a child to the resulting [PShape] object. - * @return a [PShape] object of type `GROUP` containing the provided shapes as children. - */ -fun PShape(shapes: List): PShape { - val ps = PShape(PShape.GROUP) - for (shape in shapes) { - ps.addChild(PShape(shape)) - } - return ps -} - -/** - * Converts a given [Shape] instance into a [PShape] object. If the shape contains a single - * contour, it is directly converted. Otherwise, the method constructs a complex [PShape] - * with paths and contours, mapping each segment type appropriately. - * - * @param shape the [Shape] instance to be converted into a [PShape]. The shape may consist - * of one or more contours, each containing multiple segments. - * @return a [PShape] object representing the input shape. The resulting [PShape] - * will contain vertices, contours, and paths corresponding to the geometry of the input. - */ -fun PShape(shape: Shape): PShape { - if (shape.contours.size == 1) { - return PShape(shape.contours[0]) - } else { - val ps = PShape(PShape.PATH) - ps.beginShape() - for (contour in shape.contours) { - ps.beginContour() - ps.vertex(contour.segments[0].start) - for (segment in contour.segments) { - when (segment.type) { - SegmentType.LINEAR -> ps.vertex(segment.end) - SegmentType.QUADRATIC -> ps.quadraticVertex(segment.control[0], segment.end) - SegmentType.CUBIC -> ps.bezierVertex(segment.control[0], segment.control[1], segment.end) - } - } - ps.endContour() - } - ps.endShape(PShape.CLOSE) - return ps - } -} - -/** - * Converts a given [ShapeContour] into a [PShape] object. The method translates contour - * segments (linear, quadratic, cubic) into corresponding vertices and shapes suitable - * for use with the Processing library. - * - * @param contour the [ShapeContour] to convert into a [PShape]. It may consist of multiple - * segments of varying types and can be open or closed. - * @return a [PShape] object representing the given [ShapeContour]. - */ -fun PShape(contour: ShapeContour): PShape { - val ps = PShape(PShape.PATH) - if (!contour.empty) { - ps.beginShape() - val start = contour.segments[0].start - ps.vertex(start) - for ((index, segment) in contour.segments.withIndex()) { - when (segment.type) { - SegmentType.LINEAR -> { - if (!(contour.closed && index == contour.segments.size - 1)) { - ps.vertex(segment.end) - } - } - SegmentType.QUADRATIC -> ps.quadraticVertex(segment.control[0], segment.end) - SegmentType.CUBIC -> { - ps.bezierVertex(segment.control[0], segment.control[1], segment.end) - } - } - } - ps.endShape(if (contour.closed) PShape.CLOSE else PShape.OPEN) - } - return ps -} - - -/** - * Converts a [PShape] of type `PATH` into a [ShapeContour]. - * - * This function processes the vertices and vertex codes of the `PShape` to construct a - * corresponding [ShapeContour]. The function supports vertex types including `VERTEX`, - * `BEZIER_VERTEX`, and `QUADRATIC_VERTEX`. Other vertex codes will result in an error. - * The contour will reflect whether the `PShape` is closed or open. - * - * @return A [ShapeContour] that represents the geometry of the given `PShape.PATH`. - * @throws IllegalArgumentException if the `PShape` is not of type `PATH`. - * @throws IllegalStateException for unsupported vertex codes. - */ -fun PShape.pathToShapeContours(): List { - require(family == PShape.PATH) { - "can only convert PShape.PATH to ShapeContour" - } - if (vertexCodeCount == 0) { - val vertices = mutableListOf() - for (i in 0 until vertexCount) { - val pv = getVertex(i) - vertices.add(pv.toVector2()) - } - val contour = ShapeContour.fromPoints(vertices, isClosed) - return listOf(contour) - } else { - val result = mutableListOf() - - var segments = mutableListOf() - var vertexIndex = 0 - var vertex:Vector2? = null - - for (i in 0 until vertexCodeCount) { - val code = vertexCodes[i] - when (code) { - PShape.VERTEX -> { - val pv = getVertex(vertexIndex).toVector2() - vertexIndex++ - if (vertex != null) { - segments.add(Segment2D(vertex, pv)) - } - vertex = pv - } - PShape.BEZIER_VERTEX -> { - val c0 = getVertex(vertexIndex).toVector2(); vertexIndex++ - val c1 = getVertex(vertexIndex).toVector2(); vertexIndex++ - val pv = getVertex(vertexIndex).toVector2(); vertexIndex++ - segments.add(Segment2D(vertex ?: error("no vertex set"), c0, c1, pv)) - vertex = pv - } - PShape.QUADRATIC_VERTEX -> { - val c0 = getVertex(vertexIndex).toVector2(); vertexIndex++ - val pv = getVertex(vertexIndex).toVector2(); vertexIndex++ - segments.add(Segment2D(vertex ?: error("no vertex set"), c0, pv)) - vertex = pv - } - PShape.BREAK -> { - segments.add(Segment2D(vertex ?: error("no vertex set"), segments.first().start)) - result.add(ShapeContour(segments, closed = isClosed)) - segments = mutableListOf() - vertex = getVertex(vertexIndex).toVector2() - } - else -> error("unsupported code $code") - } - } - if (isClosed && segments.last().end.distanceTo(segments.first().start) > 1E-6) { - segments.add(Segment2D(segments.last().end, segments.first().start)) - } - - if (segments.isNotEmpty()) { - result.add(ShapeContour(segments, closed = isClosed)) - } - return result - } -} - -/** - * Converts this [PShape] instance into a list of [ShapeContour] objects. - * - * This function processes the shape based on its family type: - * - If the shape is a `GROUP`, it recursively converts its children into contours. - * - If the shape is a `PATH`, it converts it directly to a single [ShapeContour]. - * - If the shape is `GEOMETRY`, it constructs contours based on vertex information. - * - * Unsupported shape families will throw an error. - * - * @return A list of [ShapeContour] objects representing the contours of this [PShape]. - */ -fun PShape.toShapeContours(): List { - return when (this.family) { - PShape.GROUP -> { - children.flatMap { it.toShapeContours() } - } - - PShape.PATH -> { - pathToShapeContours() - } - - PShape.GEOMETRY -> { - val contourPoints = mutableListOf>() - //https://github.com/processing/processing4/blob/d35f4de58936d41946d253f37986127fd100654c/core/src/processing/core/PShape.java#L1772 - - var codeIndex = 0 - var activeContour = mutableListOf() - for (i in 0 until vertexCount) { - if (vertexCodes[codeIndex++] == PShape.BREAK) { - contourPoints.add(activeContour) - activeContour = mutableListOf() - codeIndex++ - } - } - if (activeContour.isNotEmpty()) { - contourPoints.add(activeContour) - } - contourPoints.map { ShapeContour.fromPoints(it, false) } - } - - else -> error("unsupported shape family: ${this.family}") - } -} - -/** - * Converts this [PShape] instance into a [Shape] instance. - * - * This function processes the contours of the [PShape] and transforms them into - * the corresponding contours of a [Shape] object. - * - * @return A [Shape] object representing the converted [PShape]. - */ -fun PShape.toShape(): Shape { - return Shape(toShapeContours()) -} \ No newline at end of file diff --git a/orx-jvm/orx-processing/src/main/kotlin/PVectorExtensions.kt b/orx-jvm/orx-processing/src/main/kotlin/PVectorExtensions.kt deleted file mode 100644 index 5492fa67..00000000 --- a/orx-jvm/orx-processing/src/main/kotlin/PVectorExtensions.kt +++ /dev/null @@ -1,66 +0,0 @@ -package org.openrndr.extra.processing - -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import processing.core.PVector - -/** - * Converts a given `Vector2` instance into a `PVector` instance. - * - * @param v the source Vector2 whose x and y coordinates will be used to create the PVector. - * @return a new PVector instance initialized with the x and y components of the given Vector2, - * cast to Float. - */ -fun PVector(v: Vector2): PVector = PVector(v.x.toFloat(), v.y.toFloat()) - -/** - * Converts a `Vector3` object into a `PVector` instance by converting its components to `Float`. - * - * @param v the `Vector3` instance to convert - * @return a `PVector` instance with corresponding x, y, and z components in `Float` - */ -fun PVector(v: Vector3): PVector = PVector(v.x.toFloat(), v.y.toFloat(), v.z.toFloat()) - -/** - * Converts an instance of [Vector2] to a [PVector] by transforming its x and y values - * into floating-point numbers. - * - * @receiver The [Vector2] instance to be converted. - * @return A new [PVector] containing the x and y components of the receiver as floats. - */ -fun Vector2.toPVector() = PVector(this.x.toFloat(), this.y.toFloat()) - -/** - * Converts a [Vector3] instance to a [PVector] instance. - * - * Each component of the [Vector3] (x, y, z) is cast to a float and used to - * construct a new [PVector]. - * - * @receiver The [Vector3] to be converted. - * @return A [PVector] with the corresponding float components. - */ -fun Vector3.toPVector() = PVector(this.x.toFloat(), this.y.toFloat(), this.z.toFloat()) - - -/** - * Converts this [PVector] instance into a [Vector2] instance. - * - * The `x` and `y` components of the [PVector] are converted to `Double` and used - * to create a new [Vector2]. - * - * @return A [Vector2] instance with the `x` and `y` components of this [PVector] - * converted to `Double`. - */ -fun PVector.toVector2(): Vector2 { - return Vector2(x.toDouble(), y.toDouble()) -} - -/** - * Converts a [PVector] into an [Vector3] instance. - * - * @return a [Vector3] object with the same x, y, and z values as the original [PVector], - * converted to Double. - */ -fun PVector.toVector3(): Vector3 { - return Vector3(x.toDouble(), y.toDouble(), z.toDouble()) -} diff --git a/orx-jvm/orx-rabbit-control/.gitignore b/orx-jvm/orx-rabbit-control/.gitignore deleted file mode 100644 index e43b0f98..00000000 --- a/orx-jvm/orx-rabbit-control/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.DS_Store diff --git a/orx-jvm/orx-rabbit-control/README.md b/orx-jvm/orx-rabbit-control/README.md deleted file mode 100644 index 13ba2892..00000000 --- a/orx-jvm/orx-rabbit-control/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# orx-rabbit-control - -Creates a web-based remote UI to control your OPENRNDR program from a mobile device or a different computer. Alternative to `orx-gui`. - -`orx-rabbit-control` uses `orx-parameters` annotations to generate a control interface, just like `orx-gui`. -The main difference is that with `orx-gui` the UI is shown on a layer above your program while `orx-rabbit-control` -shows it on a web browser (potentially on another device). Ideal for tweaking parameters on interactive installations -without needing to attach a mouse or keyboard to the rendering computer. It also avoids difficulties caused by -UIs showed on rotated displays or projections. - - - - - -Find examples under the [demo](./src/demo/kotlin) folder. - -### Accessing the generated web UI - -Once you start a program that uses orx-rabbit-control, a QR code will be displayed on a layer above your visuals -until someone accesses the web UI. - -The UI can be accessed in a web browser in three different ways: - -- scan the QR code with a mobile device connected to the same wireless network, -- or click on the URL displayed in the IDE console, -- or go to [client.rabbitcontrol.cc](http://client.rabbitcontrol.cc) and enter your IP-address and port (displayed at the end of the URL shown in the IDE console) - -Once the UI is visible in a web browser one can interact with the sliders, buttons, checkboxes etc. -to control the OPENRNDR program remotely. - -More info about the web client: -[rabbitcontrol.cc/apps/webclient/](http://rabbitcontrol.cc/apps/webclient/) - -### Screenshot of a simple web UI - - - -### Frequently asked questions - -[https://rabbitcontrol.cc/faq/](https://rabbitcontrol.cc/faq/) - -## Demos -### DemoRabbitControl - -Demonstrates how to use RabbitControl to create a web-based user interface for your program. - -A `settings` object is created using the same syntax used for `orx-gui`, including -annotations for different variable types. - -The program then passes these `settings` to the `RabbitControlServer`. A QR-code is displayed -to open the web user interface. A clickable URL is also displayed in the console. - -Once the UI is visible in a web browser we can use it to control the OPENRNDR program. - -![DemoRabbitControlKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-rabbit-control/images/DemoRabbitControlKt.png) - -[source code](src/demo/kotlin/DemoRabbitControl.kt) - -### DemoRabbitControlManualOverlay - -Demonstrates how the QR-code pointing at the Rabbit Control web-based user interface -can be displayed and hidden manually. - -To display the QR-code overlay in this demo, hold down the HOME key in the keyboard. - -![DemoRabbitControlManualOverlayKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-rabbit-control/images/DemoRabbitControlManualOverlayKt.png) - -[source code](src/demo/kotlin/DemoRabbitControlManualOverlay.kt) - -### DemoRabbitHole - -Starts the RabbitControlServer with a `Rabbithole` using the key 'orxtest'. - -`Rabbithole` allows you to access your exposed parameters from Internet -connected computers that are not in the same network. - -To use it with this example use 'orxtest' as the tunnel-name in https://rabbithole.rabbitcontrol.cc - - -![DemoRabbitHoleKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-jvm/orx-rabbit-control/images/DemoRabbitHoleKt.png) - -[source code](src/demo/kotlin/DemoRabbitHole.kt) diff --git a/orx-jvm/orx-rabbit-control/build.gradle.kts b/orx-jvm/orx-rabbit-control/build.gradle.kts deleted file mode 100644 index c1c1e333..00000000 --- a/orx-jvm/orx-rabbit-control/build.gradle.kts +++ /dev/null @@ -1,17 +0,0 @@ -plugins { - id("org.openrndr.extra.convention.kotlin-jvm") -} - -dependencies { - implementation(project(":orx-parameters")) - implementation(project(":orx-compositor")) - implementation(project(":orx-image-fit")) - implementation(project(":orx-fx")) - implementation(openrndr.application.core) - implementation(openrndr.math) - implementation(libs.rabbitcontrol.rcp) - implementation(libs.netty.all) - implementation(libs.zxing.core) - implementation(libs.ktor.server.core) - implementation(libs.ktor.server.netty) -} \ No newline at end of file diff --git a/orx-jvm/orx-rabbit-control/src/demo/kotlin/DemoRabbitControl.kt b/orx-jvm/orx-rabbit-control/src/demo/kotlin/DemoRabbitControl.kt deleted file mode 100644 index 1dc1687e..00000000 --- a/orx-jvm/orx-rabbit-control/src/demo/kotlin/DemoRabbitControl.kt +++ /dev/null @@ -1,72 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.loadFont -import org.openrndr.extra.parameters.* -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 - -/** - * Demonstrates how to use RabbitControl to create a web-based user interface for your program. - * - * A `settings` object is created using the same syntax used for `orx-gui`, including - * annotations for different variable types. - * - * The program then passes these `settings` to the `RabbitControlServer`. A QR-code is displayed - * to open the web user interface. A clickable URL is also displayed in the console. - * - * Once the UI is visible in a web browser we can use it to control the OPENRNDR program. - */ -fun main() = application { - configure { - width = 800 - height = 800 - } - - program { - val rabbit = RabbitControlServer() - val font = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 20.0) - val settings = object { - @TextParameter("A string") - var s: String = "Hello" - - @DoubleParameter("A double", 0.0, 10.0) - var d: Double = 10.0 - - @BooleanParameter("A bool") - var b: Boolean = true - - @ColorParameter("A fill color") - var fill = ColorRGBa.PINK - - @ColorParameter("A stroke color") - var stroke = ColorRGBa.WHITE - - @Vector2Parameter("A vector2") - var v2 = Vector2(200.0,200.0) - - @Vector3Parameter("A vector3") - var v3 = Vector3(200.0, 200.0, 200.0) - - @Vector4Parameter("A vector4") - var v4 = Vector4(200.0, 200.0, 200.0, 200.0) - - @ActionParameter("Action test") - fun clicked() { - d += 10.0 - println("Clicked from RabbitControl") - } - } - - rabbit.add(settings) - extend(rabbit) - extend { - drawer.clear(if (settings.b) ColorRGBa.BLUE else ColorRGBa.BLACK) - drawer.fontMap = font - drawer.fill = settings.fill - drawer.stroke = settings.stroke - drawer.circle(settings.v2, settings.d) - drawer.text(settings.s, 10.0, 20.0) - } - } -} diff --git a/orx-jvm/orx-rabbit-control/src/demo/kotlin/DemoRabbitControlManualOverlay.kt b/orx-jvm/orx-rabbit-control/src/demo/kotlin/DemoRabbitControlManualOverlay.kt deleted file mode 100644 index c996f3f3..00000000 --- a/orx-jvm/orx-rabbit-control/src/demo/kotlin/DemoRabbitControlManualOverlay.kt +++ /dev/null @@ -1,51 +0,0 @@ -import org.openrndr.KEY_HOME -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.extra.parameters.BooleanParameter - - -/** - * Demonstrates how the QR-code pointing at the Rabbit Control web-based user interface - * can be displayed and hidden manually. - * - * To display the QR-code overlay in this demo, hold down the HOME key in the keyboard. - */ -fun main() = application { - configure { - width = 800 - height = 800 - } - - program { - val rabbit = RabbitControlServer(showQRUntilClientConnects = false) - - val settings = object { - @BooleanParameter("White on black") - var whiteOnBlack: Boolean = true - } - - rabbit.add(settings) - extend(rabbit) - - /** - * Example: only show the QR code when the [KEY_HOME] button is pressed - */ - keyboard.keyDown.listen { - when (it.key) { - KEY_HOME -> rabbit.showQRCode = true - } - } - - keyboard.keyUp.listen { - when (it.key) { - KEY_HOME -> rabbit.showQRCode = false - } - } - - extend { - drawer.clear(if (settings.whiteOnBlack) ColorRGBa.BLACK else ColorRGBa.WHITE) - drawer.fill = if (settings.whiteOnBlack) ColorRGBa.WHITE else ColorRGBa.BLACK - drawer.circle(drawer.bounds.center, 250.0) - } - } -} diff --git a/orx-jvm/orx-rabbit-control/src/demo/kotlin/DemoRabbitHole.kt b/orx-jvm/orx-rabbit-control/src/demo/kotlin/DemoRabbitHole.kt deleted file mode 100644 index 2a8d7b29..00000000 --- a/orx-jvm/orx-rabbit-control/src/demo/kotlin/DemoRabbitHole.kt +++ /dev/null @@ -1,69 +0,0 @@ -import org.openrndr.application -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.loadFont -import org.openrndr.extra.parameters.* -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 - -/** - * Starts the RabbitControlServer with a `Rabbithole` using the key 'orxtest'. - * - * `Rabbithole` allows you to access your exposed parameters from Internet - * connected computers that are not in the same network. - * - * To use it with this example use 'orxtest' as the tunnel-name in https://rabbithole.rabbitcontrol.cc - * - */ -fun main() = application { - configure { - width = 800 - height = 800 - } - - program { - val rabbit = RabbitControlServer(false, 10000, 8080, "wss://rabbithole.rabbitcontrol.cc/public/rcpserver/connect?key=orxtest") - val font = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 20.0) - val settings = object { - @TextParameter("A string") - var s: String = "Hello" - - @DoubleParameter("A double", 0.0, 10.0) - var d: Double = 10.0 - - @BooleanParameter("A bool") - var b: Boolean = true - - @ColorParameter("A fill color") - var fill = ColorRGBa.PINK - - @ColorParameter("A stroke color") - var stroke = ColorRGBa.WHITE - - @Vector2Parameter("A vector2") - var v2 = Vector2(200.0,200.0) - - @Vector3Parameter("A vector3") - var v3 = Vector3(200.0, 200.0, 200.0) - - @Vector4Parameter("A vector4") - var v4 = Vector4(200.0, 200.0, 200.0, 200.0) - - @ActionParameter("Action test") - fun clicked() { - println("Clicked from RabbitControl") - } - } - - rabbit.add(settings) - extend(rabbit) - extend { - drawer.clear(if (settings.b) ColorRGBa.BLUE else ColorRGBa.BLACK) - drawer.fontMap = font - drawer.fill = settings.fill - drawer.stroke = settings.stroke - drawer.circle(settings.v2, settings.d) - drawer.text(settings.s, 10.0, 20.0) - } - } -} diff --git a/orx-jvm/orx-rabbit-control/src/main/kotlin/RabbitControlServer.kt b/orx-jvm/orx-rabbit-control/src/main/kotlin/RabbitControlServer.kt deleted file mode 100644 index b6043ed2..00000000 --- a/orx-jvm/orx-rabbit-control/src/main/kotlin/RabbitControlServer.kt +++ /dev/null @@ -1,288 +0,0 @@ -import com.google.zxing.BarcodeFormat -import com.google.zxing.qrcode.QRCodeWriter -import io.ktor.server.engine.* -import io.ktor.server.http.content.* -import io.ktor.server.netty.* -import io.ktor.server.routing.* -import org.openrndr.Extension -import org.openrndr.Program -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.compositor.* -import org.openrndr.extra.fx.blend.Darken -import org.openrndr.extra.imageFit.FitMethod -import org.openrndr.extra.imageFit.imageFit -import org.openrndr.extra.parameters.Parameter -import org.openrndr.extra.parameters.ParameterType -import org.openrndr.extra.parameters.listParameters -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.Vector4 -import org.openrndr.math.mix -import org.rabbitcontrol.rcp.RCPServer -import org.rabbitcontrol.rcp.model.interfaces.IParameter -import org.rabbitcontrol.rcp.model.parameter.* -import org.rabbitcontrol.rcp.transport.websocket.server.RabbitHoleWsServerTransporterNetty -import org.rabbitcontrol.rcp.transport.websocket.server.WebsocketServerTransporterNetty -import java.awt.Color -import java.net.InetSocketAddress -import java.net.Socket -import java.net.URI -import java.net.URISyntaxException -import kotlin.reflect.KMutableProperty1 - - -class RabbitControlServer(private val showQRUntilClientConnects: Boolean = true, rcpPort: Int = - 10000, staticFilesPort: Int = 8080, rabbithole: String = "") : Extension { - private val rabbitServer = RCPServer() - private val transporter = WebsocketServerTransporterNetty() - private var rabbitholeTransporter: RabbitHoleWsServerTransporterNetty? = null - private var webServer: NettyApplicationEngine? = null - - private var parameterMap = mutableMapOf>() - - private var qrCodeImage: ColorBuffer? = null - private var qrOverlayComposition: Composite? = null - - - /** - * Animate the opacity to make it look smooooth - */ - private var currentOpacity = 0.0 - - private val targetOpacity: Double - get() = if (shouldShowQR) 0.8 else 0.0 - - private val shouldShowQR - get() = (rabbitServer.connectionCount == 0 && showQRUntilClientConnects) || showQRCode - - - /** - * Used to manually show and hide the QR code and override the default - * behaviour of (only) showing the code when no clients are connected - */ - var showQRCode = false - - init { - rabbitServer.addTransporter(transporter) - transporter.bind(rcpPort) - - /** - * add rabbithole transporter - */ - if (rabbithole.isNotEmpty()) { - try { - val rhlTransporter = RabbitHoleWsServerTransporterNetty(URI(rabbithole)) - rabbitServer.addTransporter(rhlTransporter) - rhlTransporter.bind(0) - rabbitholeTransporter = rhlTransporter - } catch (e: URISyntaxException) { - // - println("invalid URI for rabbithole: $rabbithole") - } - } - - /** - * Start KTOR to serve the static files of the RabbitControl client - */ - val server = embeddedServer(Netty, port = staticFilesPort) { - routing { - staticResources("/rabbit-client", "rabbit-client") - } - } - server.start() - webServer = server.engine - - /** - * Print the address - */ - val socket = Socket() - socket.connect(InetSocketAddress("google.com", 80)) - val ip = socket.localAddress.toString().replace("/", "") - val clientUrlWithHash = "http://$ip:$staticFilesPort/rabbit-client/index.html#$ip:$rcpPort" - qrCodeImage = getQRCodeImage(barcodeText = clientUrlWithHash) - println("RabbitControl Web Client: $clientUrlWithHash") - - /** - * Update the object when it has been updated in RabbitControl - */ - rabbitServer.addUpdateListener { - val (obj, orxParameter) = parameterMap[it]!! - when(it) { - is Int32Parameter -> { - val v = it.value - orxParameter.property.qset(obj, v) - } - is Float64Parameter -> { - val v = it.value - orxParameter.property.qset(obj, v) - } - is BooleanParameter -> { - val v = it.value - orxParameter.property.qset(obj, v) - } - is StringParameter -> { - val v = it.value - orxParameter.property.qset(obj, v) - } - is RGBAParameter -> { - val c = it.value - val cc = ColorRGBa(c.red.toDouble() / 255.0, c.green.toDouble() / 255.0, c.blue.toDouble() / 255.0, c.alpha.toDouble() / 255.0) - orxParameter.property.qset(obj, cc) - } - is Vector2Float32Parameter -> { - val v = it.value - orxParameter.property.qset(obj, Vector2(v.x.toDouble(), v.y.toDouble())) - } - is Vector3Float32Parameter -> { - val v = it.value - orxParameter.property.qset(obj, Vector3(v.x.toDouble(), v.y.toDouble(), v.z.toDouble())) - } - is Vector4Float32Parameter -> { - val v = it.value - orxParameter.property.qset(obj, Vector4(v.x.toDouble(), v.y.toDouble(), v.z.toDouble(), v.t.toDouble())) - } - } - } - } - - - override fun setup(program: Program) { - /** - * Creating the Composite for the overlay needs to happen in setup(), - * as we need access to [Program.drawer] - */ - qrOverlayComposition = compose { - layer { - draw { - program.drawer.isolated { - fill = ColorRGBa.WHITE.opacify(currentOpacity) - stroke = null - rectangle(0.0,0.0, width.toDouble(), height.toDouble()) - } - } - - layer { - blend(Darken()) { - clip = true - } - - draw { - qrCodeImage?.let { - program.drawer.imageFit(it, program.width / 4.0,program.height / 4.0, program.width * .5, program.height * .5, 0.0,0.0, FitMethod.Contain) - } - } - } - } - } - } - - - @Suppress("UNCHECKED_CAST") - fun add(objectWithParameters: Any) { - val parameters = objectWithParameters.listParameters() - - parameters.forEach { - val rabbitParam = when (it.parameterType) { - ParameterType.Int -> { - val param = rabbitServer.createInt32Parameter(it.label) - param.value = (it.property as KMutableProperty1).get(objectWithParameters) - param - } - ParameterType.Double -> { - val param = rabbitServer.createFloat64Parameter(it.label) - param.value = (it.property as KMutableProperty1).get(objectWithParameters) - param - } - ParameterType.Action -> { - val param = rabbitServer.createBangParameter(it.label) - param.setFunction { - it.function!!.call(objectWithParameters) - } - param - } - ParameterType.Boolean -> { - val param = rabbitServer.createBooleanParameter(it.label) - param.value = (it.property as KMutableProperty1).get(objectWithParameters) - param - } - ParameterType.Text -> { - val param =rabbitServer.createStringParameter(it.label) - param.value = (it.property as KMutableProperty1).get(objectWithParameters) - param - } - ParameterType.Color -> { - val param = rabbitServer.createRGBAParameter(it.label) - val c = (it.property as KMutableProperty1).get(objectWithParameters) - param.value = Color(c.r.toFloat(), c.g.toFloat(), c.b.toFloat(), c.alpha.toFloat()) - param - } - ParameterType.Vector2 -> { - val param = rabbitServer.createVector2Float32Parameter(it.label) - val v2 = (it.property as KMutableProperty1).get(objectWithParameters) - param.value = org.rabbitcontrol.rcp.model.types.Vector2(v2.x.toFloat(), v2.y.toFloat()) - param - } - ParameterType.Vector3 -> { - val param = rabbitServer.createVector3Float32Parameter(it.label) - val v3 = (it.property as KMutableProperty1).get(objectWithParameters) - param.value = org.rabbitcontrol.rcp.model.types.Vector3(v3.x.toFloat(), v3.y.toFloat(), v3.z.toFloat()) - param - } - ParameterType.Vector4 -> { - val param = rabbitServer.createVector4Float32Parameter(it.label) - val v4 = (it.property as KMutableProperty1).get(objectWithParameters) - param.value = org.rabbitcontrol.rcp.model.types.Vector4(v4.x.toFloat(), v4.y.toFloat(), v4.z.toFloat(), v4.w.toFloat()) - param - } - else -> rabbitServer.createBangParameter(it.label) - } - - // We need to store a mapping from Rabbit parameter to target object + orx parameter - // so we can update the object later - parameterMap[rabbitParam] = Pair(objectWithParameters, it) - } - - rabbitServer.update() - } - - override var enabled = true - - override fun shutdown(program: Program) { - transporter.dispose() - rabbitholeTransporter?.dispose() - webServer?.stop(50, 50) - } - - private fun getQRCodeImage(barcodeText: String): ColorBuffer { - val qrCodeWriter = QRCodeWriter() - val bitMatrix = qrCodeWriter.encode(barcodeText, BarcodeFormat.QR_CODE, 30, 30) - val cb = colorBuffer(bitMatrix.width, bitMatrix.height) - cb.filterMag = MagnifyingFilter.NEAREST - val shad = cb.shadow - - - for (y in 0 until bitMatrix.width) { - for (x in 0 until bitMatrix.height) { - shad[x, y] = if (bitMatrix[x, y]) ColorRGBa.BLACK else ColorRGBa.WHITE - } - } - - shad.upload() - return cb - } - - override fun afterDraw(drawer: Drawer, program: Program) { - currentOpacity = mix(targetOpacity, currentOpacity, 0.8) - - // Don't draw if it isn't necessary - if (currentOpacity > 0.0) { - qrOverlayComposition?.draw(drawer) - } - } -} - -fun KMutableProperty1?.qset(obj: Any, value: T) { - @Suppress("UNCHECKED_CAST") - return (this as KMutableProperty1).set(obj, value) -} diff --git a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/asset-manifest.json b/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/asset-manifest.json deleted file mode 100644 index 301da445..00000000 --- a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/asset-manifest.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "main.css": "./static/css/main.6b57d6a6.chunk.css", - "main.js": "./static/js/main.3c08e7ef.chunk.js", - "main.js.map": "./static/js/main.3c08e7ef.chunk.js.map", - "static/css/1.a970d4a6.chunk.css": "./static/css/1.a970d4a6.chunk.css", - "static/js/1.ab137fd1.chunk.js": "./static/js/1.ab137fd1.chunk.js", - "static/js/1.ab137fd1.chunk.js.map": "./static/js/1.ab137fd1.chunk.js.map", - "runtime~main.js": "./static/js/runtime~main.4a686d48.js", - "runtime~main.js.map": "./static/js/runtime~main.4a686d48.js.map", - "static/media/blueprint-icons.css": "./static/media/icons-20.cef8cdbb.woff", - "static/css/main.6b57d6a6.chunk.css.map": "./static/css/main.6b57d6a6.chunk.css.map", - "static/css/1.a970d4a6.chunk.css.map": "./static/css/1.a970d4a6.chunk.css.map", - "index.html": "./index.html", - "precache-manifest.8b50f7152eba97a8a3a748d36319a411.js": "./precache-manifest.8b50f7152eba97a8a3a748d36319a411.js", - "service-worker.js": "./service-worker.js" -} \ No newline at end of file diff --git a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/favicon.ico b/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/favicon.ico deleted file mode 100644 index 9f144de9..00000000 Binary files a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/favicon.ico and /dev/null differ diff --git a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/index.html b/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/index.html deleted file mode 100644 index f47bb4a2..00000000 --- a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/index.html +++ /dev/null @@ -1 +0,0 @@ -RabbitControl Web Carrot

\ No newline at end of file diff --git a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/manifest.json b/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/manifest.json deleted file mode 100644 index af8d9f0c..00000000 --- a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/manifest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "short_name": "RCP Web Client", - "name": "RabbitControl Web Client", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/precache-manifest.8b50f7152eba97a8a3a748d36319a411.js b/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/precache-manifest.8b50f7152eba97a8a3a748d36319a411.js deleted file mode 100644 index a670cf10..00000000 --- a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/precache-manifest.8b50f7152eba97a8a3a748d36319a411.js +++ /dev/null @@ -1,50 +0,0 @@ -self.__precacheManifest = [ - { - "revision": "3c08e7ef80fee0668771", - "url": "./static/css/main.6b57d6a6.chunk.css" - }, - { - "revision": "3c08e7ef80fee0668771", - "url": "./static/js/main.3c08e7ef.chunk.js" - }, - { - "revision": "ab137fd189140783fea3", - "url": "./static/css/1.a970d4a6.chunk.css" - }, - { - "revision": "ab137fd189140783fea3", - "url": "./static/js/1.ab137fd1.chunk.js" - }, - { - "revision": "4a686d48d5a089750c49", - "url": "./static/js/runtime~main.4a686d48.js" - }, - { - "revision": "3cde8748332d1de6b1ae1c2dc5850754", - "url": "./static/media/icons-16.3cde8748.ttf" - }, - { - "revision": "05f1cdadfe476395f60e233b15c22155", - "url": "./static/media/icons-16.05f1cdad.eot" - }, - { - "revision": "3c1c220e7a18286503fb431c7a7fe183", - "url": "./static/media/icons-16.3c1c220e.woff" - }, - { - "revision": "0a5c76518a68c185baa2c6744456918c", - "url": "./static/media/icons-20.0a5c7651.eot" - }, - { - "revision": "51ec31f302d0072808e1f83f85fea4cd", - "url": "./static/media/icons-20.51ec31f3.ttf" - }, - { - "revision": "cef8cdbb9d0ba82e6e19fb0eeba2ac3d", - "url": "./static/media/icons-20.cef8cdbb.woff" - }, - { - "revision": "a84699318edf0b28678797d32740ca67", - "url": "./index.html" - } -]; \ No newline at end of file diff --git a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/service-worker.js b/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/service-worker.js deleted file mode 100644 index e1a0ad99..00000000 --- a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/service-worker.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Welcome to your Workbox-powered service worker! - * - * You'll need to register this file in your web app and you should - * disable HTTP caching for this file too. - * See https://goo.gl/nhQhGp - * - * The rest of the code is auto-generated. Please don't update this file - * directly; instead, make changes to your Workbox build configuration - * and re-run your build process. - * See https://goo.gl/2aRDsh - */ - -importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.6.3/workbox-sw.js"); - -importScripts( - "./precache-manifest.8b50f7152eba97a8a3a748d36319a411.js" -); - -workbox.clientsClaim(); - -/** - * The workboxSW.precacheAndRoute() method efficiently caches and responds to - * requests for URLs in the manifest. - * See https://goo.gl/S9QRab - */ -self.__precacheManifest = [].concat(self.__precacheManifest || []); -workbox.precaching.suppressWarnings(); -workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); - -workbox.routing.registerNavigationRoute("./index.html", { - - blacklist: [/^\/_/,/\/[^/]+\.[^/]+$/], -}); diff --git a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/css/1.a970d4a6.chunk.css b/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/css/1.a970d4a6.chunk.css deleted file mode 100644 index 4010a2d5..00000000 --- a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/css/1.a970d4a6.chunk.css +++ /dev/null @@ -1,2 +0,0 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:initial;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:initial}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:initial}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}@font-face{font-family:Icons16;font-style:normal;font-weight:400;src:url(../../static/media/icons-16.05f1cdad.eot?#iefix) format("embedded-opentype"),url(../../static/media/icons-16.3c1c220e.woff) format("woff"),url(../../static/media/icons-16.3cde8748.ttf) format("truetype")}@font-face{font-family:Icons20;font-style:normal;font-weight:400;src:url(../../static/media/icons-20.0a5c7651.eot?#iefix) format("embedded-opentype"),url(../../static/media/icons-20.cef8cdbb.woff) format("woff"),url(../../static/media/icons-20.51ec31f3.ttf) format("truetype")}html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}body{font-size:14px;font-weight:400;letter-spacing:0;line-height:1.28581;text-transform:none;color:#182026;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,Icons16,sans-serif}p{margin-bottom:10px;margin-top:0}small{font-size:12px}strong{font-weight:600}::selection{background:rgba(125,188,255,.6)}.bp3-heading{color:#182026;font-weight:600;margin:0 0 10px;padding:0}.bp3-dark .bp3-heading{color:#f5f8fa}.bp3-running-text h1,h1.bp3-heading{font-size:36px;line-height:40px}.bp3-running-text h2,h2.bp3-heading{font-size:28px;line-height:32px}.bp3-running-text h3,h3.bp3-heading{font-size:22px;line-height:25px}.bp3-running-text h4,h4.bp3-heading{font-size:18px;line-height:21px}.bp3-running-text h5,h5.bp3-heading{font-size:16px;line-height:19px}.bp3-running-text h6,h6.bp3-heading{font-size:14px;line-height:16px}.bp3-ui-text{font-size:14px;font-weight:400;letter-spacing:0;line-height:1.28581;text-transform:none}.bp3-monospace-text{font-family:monospace;text-transform:none}.bp3-text-muted{color:#5c7080}.bp3-dark .bp3-text-muted{color:#a7b6c2}.bp3-text-disabled{color:rgba(92,112,128,.6)}.bp3-dark .bp3-text-disabled{color:rgba(167,182,194,.6)}.bp3-text-overflow-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal}.bp3-running-text{font-size:14px;line-height:1.5}.bp3-running-text h1{color:#182026;font-weight:600;margin-bottom:20px;margin-top:40px}.bp3-dark .bp3-running-text h1{color:#f5f8fa}.bp3-running-text h2{color:#182026;font-weight:600;margin-bottom:20px;margin-top:40px}.bp3-dark .bp3-running-text h2{color:#f5f8fa}.bp3-running-text h3{color:#182026;font-weight:600;margin-bottom:20px;margin-top:40px}.bp3-dark .bp3-running-text h3{color:#f5f8fa}.bp3-running-text h4{color:#182026;font-weight:600;margin-bottom:20px;margin-top:40px}.bp3-dark .bp3-running-text h4{color:#f5f8fa}.bp3-running-text h5{color:#182026;font-weight:600;margin-bottom:20px;margin-top:40px}.bp3-dark .bp3-running-text h5{color:#f5f8fa}.bp3-running-text h6{color:#182026;font-weight:600;margin-bottom:20px;margin-top:40px}.bp3-dark .bp3-running-text h6{color:#f5f8fa}.bp3-running-text hr{border:none;border-bottom:1px solid rgba(16,22,26,.15);margin:20px 0}.bp3-dark .bp3-running-text hr{border-color:hsla(0,0%,100%,.15)}.bp3-running-text p{margin:0 0 10px;padding:0}.bp3-text-large{font-size:16px}.bp3-text-small{font-size:12px}a{text-decoration:none}a,a:hover{color:#106ba3}a:hover{cursor:pointer;text-decoration:underline}.bp3-dark a code,a .bp3-icon,a .bp3-icon-large,a .bp3-icon-standard,a code{color:inherit}.bp3-dark a,.bp3-dark a:hover{color:#48aff0}.bp3-dark a .bp3-icon,.bp3-dark a .bp3-icon-large,.bp3-dark a .bp3-icon-standard,.bp3-dark a:hover .bp3-icon,.bp3-dark a:hover .bp3-icon-large,.bp3-dark a:hover .bp3-icon-standard{color:inherit}.bp3-code,.bp3-running-text code{font-family:monospace;text-transform:none;background:hsla(0,0%,100%,.7);border-radius:3px;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2);color:#5c7080;font-size:smaller;padding:2px 5px}.bp3-dark .bp3-code,.bp3-dark .bp3-running-text code,.bp3-running-text .bp3-dark code{background:rgba(16,22,26,.3);box-shadow:inset 0 0 0 1px rgba(16,22,26,.4);color:#a7b6c2}.bp3-running-text a>code,a>.bp3-code{color:#137cbd}.bp3-dark .bp3-running-text a>code,.bp3-dark a>.bp3-code,.bp3-running-text .bp3-dark a>code{color:inherit}.bp3-code-block,.bp3-running-text pre{font-family:monospace;text-transform:none;background:hsla(0,0%,100%,.7);border-radius:3px;box-shadow:inset 0 0 0 1px rgba(16,22,26,.15);color:#182026;display:block;font-size:13px;line-height:1.4;margin:10px 0;padding:13px 15px 12px;word-break:break-all;word-wrap:break-word}.bp3-dark .bp3-code-block,.bp3-dark .bp3-running-text pre,.bp3-running-text .bp3-dark pre{background:rgba(16,22,26,.3);box-shadow:inset 0 0 0 1px rgba(16,22,26,.4);color:#f5f8fa}.bp3-code-block>code,.bp3-running-text pre>code{background:none;box-shadow:none;color:inherit;font-size:inherit;padding:0}.bp3-key,.bp3-running-text kbd{align-items:center;background:#fff;border-radius:3px;box-shadow:0 0 0 1px rgba(16,22,26,.1),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.2);color:#5c7080;display:inline-flex;font-family:inherit;font-size:12px;height:24px;justify-content:center;line-height:24px;min-width:24px;padding:3px 6px;vertical-align:middle}.bp3-key .bp3-icon,.bp3-key .bp3-icon-large,.bp3-key .bp3-icon-standard,.bp3-running-text kbd .bp3-icon,.bp3-running-text kbd .bp3-icon-large,.bp3-running-text kbd .bp3-icon-standard{margin-right:5px}.bp3-dark .bp3-key,.bp3-dark .bp3-running-text kbd,.bp3-running-text .bp3-dark kbd{background:#394b59;box-shadow:0 0 0 1px rgba(16,22,26,.2),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.4);color:#a7b6c2}.bp3-blockquote,.bp3-running-text blockquote{border-left:4px solid rgba(167,182,194,.5);margin:0 0 10px;padding:0 20px}.bp3-dark .bp3-blockquote,.bp3-dark .bp3-running-text blockquote,.bp3-running-text .bp3-dark blockquote{border-color:rgba(115,134,148,.5)}.bp3-list,.bp3-running-text ol,.bp3-running-text ul{margin:10px 0;padding-left:30px}.bp3-list li:not(:last-child),.bp3-running-text ol li:not(:last-child),.bp3-running-text ul li:not(:last-child){margin-bottom:5px}.bp3-list ol,.bp3-list ul,.bp3-running-text ol ol,.bp3-running-text ol ul,.bp3-running-text ul ol,.bp3-running-text ul ul{margin-top:5px}.bp3-list-unstyled{list-style:none;margin:0;padding:0}.bp3-list-unstyled li{padding:0}.bp3-rtl{text-align:right}.bp3-dark{color:#f5f8fa}:focus{outline:2px auto rgba(19,124,189,.6);outline-offset:2px;-moz-outline-radius:6px}.bp3-focus-disabled :focus,.bp3-focus-disabled :focus~.bp3-control-indicator{outline:none!important}.bp3-alert{max-width:400px;padding:20px}.bp3-alert-body{display:flex}.bp3-alert-body .bp3-icon{font-size:40px;margin-right:20px;margin-top:0}.bp3-alert-contents{word-break:break-word}.bp3-alert-footer{display:flex;flex-direction:row-reverse;margin-top:10px}.bp3-alert-footer .bp3-button{margin-left:10px}.bp3-breadcrumbs{cursor:default;flex-wrap:wrap;height:30px;list-style:none;margin:0;padding:0}.bp3-breadcrumbs,.bp3-breadcrumbs>li{align-items:center;display:flex}.bp3-breadcrumbs>li:after{background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.71 7.29l-4-4a1.003 1.003 0 0 0-1.42 1.42L8.59 8 5.3 11.29c-.19.18-.3.43-.3.71a1.003 1.003 0 0 0 1.71.71l4-4c.18-.18.29-.43.29-.71 0-.28-.11-.53-.29-.71z' fill='%235C7080'/%3E%3C/svg%3E");content:"";display:block;height:16px;margin:0 5px;width:16px}.bp3-breadcrumbs>li:last-of-type:after{display:none}.bp3-breadcrumb,.bp3-breadcrumb-current,.bp3-breadcrumbs-collapsed{align-items:center;display:inline-flex;font-size:16px}.bp3-breadcrumb,.bp3-breadcrumbs-collapsed{color:#5c7080}.bp3-breadcrumb:hover{text-decoration:none}.bp3-breadcrumb.bp3-disabled{color:rgba(92,112,128,.6);cursor:not-allowed}.bp3-breadcrumb .bp3-icon{margin-right:5px}.bp3-breadcrumb-current{color:inherit;font-weight:600}.bp3-breadcrumb-current .bp3-input{font-size:inherit;font-weight:inherit;vertical-align:initial}.bp3-breadcrumbs-collapsed{background:#ced9e0;border:none;border-radius:3px;cursor:pointer;margin-right:2px;padding:1px 5px;vertical-align:text-bottom}.bp3-breadcrumbs-collapsed:before{background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cg fill='%235C7080'%3E%3Ccircle cx='2' cy='8.03' r='2'/%3E%3Ccircle cx='14' cy='8.03' r='2'/%3E%3Ccircle cx='8' cy='8.03' r='2'/%3E%3C/g%3E%3C/svg%3E") 50% no-repeat;content:"";display:block;height:16px;width:16px}.bp3-breadcrumbs-collapsed:hover{background:#bfccd6;color:#182026;text-decoration:none}.bp3-dark .bp3-breadcrumb,.bp3-dark .bp3-breadcrumbs-collapsed,.bp3-dark .bp3-breadcrumbs>li:after{color:#a7b6c2}.bp3-dark .bp3-breadcrumb.bp3-disabled{color:rgba(167,182,194,.6)}.bp3-dark .bp3-breadcrumb-current{color:#f5f8fa}.bp3-dark .bp3-breadcrumbs-collapsed{background:rgba(16,22,26,.4)}.bp3-dark .bp3-breadcrumbs-collapsed:hover{background:rgba(16,22,26,.6);color:#f5f8fa}.bp3-button{display:inline-flex;flex-direction:row;align-items:center;border:none;border-radius:3px;cursor:pointer;font-size:14px;justify-content:center;padding:5px 10px;text-align:left;vertical-align:middle;min-height:30px;min-width:30px}.bp3-button>*{flex-grow:0;flex-shrink:0}.bp3-button>.bp3-fill{flex-grow:1;flex-shrink:1}.bp3-button:before,.bp3-button>*{margin-right:7px}.bp3-button:empty:before,.bp3-button>:last-child{margin-right:0}.bp3-button:empty{padding:0!important}.bp3-button.bp3-disabled,.bp3-button:disabled{cursor:not-allowed}.bp3-button.bp3-fill{display:flex;width:100%}.bp3-align-right .bp3-button,.bp3-button.bp3-align-right{text-align:right}.bp3-align-left .bp3-button,.bp3-button.bp3-align-left{text-align:left}.bp3-button:not([class*=bp3-intent-]){background-color:#f5f8fa;background-image:linear-gradient(180deg,hsla(0,0%,100%,.8),hsla(0,0%,100%,0));box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);color:#182026}.bp3-button:not([class*=bp3-intent-]):hover{background-clip:padding-box;background-color:#ebf1f5;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1)}.bp3-button:not([class*=bp3-intent-]).bp3-active,.bp3-button:not([class*=bp3-intent-]):active{background-color:#d8e1e8;background-image:none;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-button:not([class*=bp3-intent-]).bp3-disabled,.bp3-button:not([class*=bp3-intent-]):disabled{background-color:rgba(206,217,224,.5);background-image:none;box-shadow:none;color:rgba(92,112,128,.6);cursor:not-allowed;outline:none}.bp3-button:not([class*=bp3-intent-]).bp3-disabled.bp3-active,.bp3-button:not([class*=bp3-intent-]).bp3-disabled.bp3-active:hover,.bp3-button:not([class*=bp3-intent-]):disabled.bp3-active,.bp3-button:not([class*=bp3-intent-]):disabled.bp3-active:hover{background:rgba(206,217,224,.7)}.bp3-button.bp3-intent-primary{background-color:#137cbd;background-image:linear-gradient(180deg,hsla(0,0%,100%,.1),hsla(0,0%,100%,0));box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);color:#fff}.bp3-button.bp3-intent-primary.bp3-active,.bp3-button.bp3-intent-primary:active,.bp3-button.bp3-intent-primary:hover{color:#fff}.bp3-button.bp3-intent-primary:hover{background-color:#106ba3;box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2)}.bp3-button.bp3-intent-primary.bp3-active,.bp3-button.bp3-intent-primary:active{background-color:#0e5a8a;background-image:none;box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-button.bp3-intent-primary.bp3-disabled,.bp3-button.bp3-intent-primary:disabled{background-color:rgba(19,124,189,.5);background-image:none;border-color:transparent;box-shadow:none;color:hsla(0,0%,100%,.6)}.bp3-button.bp3-intent-success{background-color:#0f9960;background-image:linear-gradient(180deg,hsla(0,0%,100%,.1),hsla(0,0%,100%,0));box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);color:#fff}.bp3-button.bp3-intent-success.bp3-active,.bp3-button.bp3-intent-success:active,.bp3-button.bp3-intent-success:hover{color:#fff}.bp3-button.bp3-intent-success:hover{background-color:#0d8050;box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2)}.bp3-button.bp3-intent-success.bp3-active,.bp3-button.bp3-intent-success:active{background-color:#0a6640;background-image:none;box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-button.bp3-intent-success.bp3-disabled,.bp3-button.bp3-intent-success:disabled{background-color:rgba(15,153,96,.5);background-image:none;border-color:transparent;box-shadow:none;color:hsla(0,0%,100%,.6)}.bp3-button.bp3-intent-warning{background-color:#d9822b;background-image:linear-gradient(180deg,hsla(0,0%,100%,.1),hsla(0,0%,100%,0));box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);color:#fff}.bp3-button.bp3-intent-warning.bp3-active,.bp3-button.bp3-intent-warning:active,.bp3-button.bp3-intent-warning:hover{color:#fff}.bp3-button.bp3-intent-warning:hover{background-color:#bf7326;box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2)}.bp3-button.bp3-intent-warning.bp3-active,.bp3-button.bp3-intent-warning:active{background-color:#a66321;background-image:none;box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-button.bp3-intent-warning.bp3-disabled,.bp3-button.bp3-intent-warning:disabled{background-color:rgba(217,130,43,.5);background-image:none;border-color:transparent;box-shadow:none;color:hsla(0,0%,100%,.6)}.bp3-button.bp3-intent-danger{background-color:#db3737;background-image:linear-gradient(180deg,hsla(0,0%,100%,.1),hsla(0,0%,100%,0));box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);color:#fff}.bp3-button.bp3-intent-danger.bp3-active,.bp3-button.bp3-intent-danger:active,.bp3-button.bp3-intent-danger:hover{color:#fff}.bp3-button.bp3-intent-danger:hover{background-color:#c23030;box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2)}.bp3-button.bp3-intent-danger.bp3-active,.bp3-button.bp3-intent-danger:active{background-color:#a82a2a;background-image:none;box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-button.bp3-intent-danger.bp3-disabled,.bp3-button.bp3-intent-danger:disabled{background-color:rgba(219,55,55,.5);background-image:none;border-color:transparent;box-shadow:none;color:hsla(0,0%,100%,.6)}.bp3-button[class*=bp3-intent-] .bp3-button-spinner .bp3-spinner-head{stroke:#fff}.bp3-button.bp3-large,.bp3-large .bp3-button{min-height:40px;min-width:40px;font-size:16px;padding:5px 15px}.bp3-button.bp3-large:before,.bp3-button.bp3-large>*,.bp3-large .bp3-button:before,.bp3-large .bp3-button>*{margin-right:10px}.bp3-button.bp3-large:empty:before,.bp3-button.bp3-large>:last-child,.bp3-large .bp3-button:empty:before,.bp3-large .bp3-button>:last-child{margin-right:0}.bp3-button.bp3-small,.bp3-small .bp3-button{min-height:24px;min-width:24px;padding:0 7px}.bp3-button.bp3-loading{position:relative}.bp3-button.bp3-loading[class*=bp3-icon-]:before{visibility:hidden}.bp3-button.bp3-loading .bp3-button-spinner{margin:0;position:absolute}.bp3-button.bp3-loading>:not(.bp3-button-spinner){visibility:hidden}.bp3-button[class*=bp3-icon-]:before{font-family:Icons16,sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#5c7080}.bp3-button .bp3-icon,.bp3-button .bp3-icon-large,.bp3-button .bp3-icon-standard{color:#5c7080}.bp3-button .bp3-icon-large.bp3-align-right,.bp3-button .bp3-icon-standard.bp3-align-right,.bp3-button .bp3-icon.bp3-align-right{margin-left:7px}.bp3-button .bp3-icon:first-child:last-child,.bp3-button .bp3-spinner+.bp3-icon:last-child{margin:0 -7px}.bp3-dark .bp3-button:not([class*=bp3-intent-]){background-color:#394b59;background-image:linear-gradient(180deg,hsla(0,0%,100%,.05),hsla(0,0%,100%,0));box-shadow:0 0 0 1px rgba(16,22,26,.4);color:#f5f8fa}.bp3-dark .bp3-button:not([class*=bp3-intent-]).bp3-active,.bp3-dark .bp3-button:not([class*=bp3-intent-]):active,.bp3-dark .bp3-button:not([class*=bp3-intent-]):hover{color:#f5f8fa}.bp3-dark .bp3-button:not([class*=bp3-intent-]):hover{background-color:#30404d;box-shadow:0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-button:not([class*=bp3-intent-]).bp3-active,.bp3-dark .bp3-button:not([class*=bp3-intent-]):active{background-color:#202b33;background-image:none;box-shadow:0 0 0 1px rgba(16,22,26,.6),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-dark .bp3-button:not([class*=bp3-intent-]).bp3-disabled,.bp3-dark .bp3-button:not([class*=bp3-intent-]):disabled{background-color:rgba(57,75,89,.5);background-image:none;box-shadow:none;color:rgba(167,182,194,.6)}.bp3-dark .bp3-button:not([class*=bp3-intent-]).bp3-disabled.bp3-active,.bp3-dark .bp3-button:not([class*=bp3-intent-]):disabled.bp3-active{background:rgba(57,75,89,.7)}.bp3-dark .bp3-button:not([class*=bp3-intent-]) .bp3-button-spinner .bp3-spinner-head{background:rgba(16,22,26,.5);stroke:#8a9ba8}.bp3-dark .bp3-button:not([class*=bp3-intent-]) .bp3-icon,.bp3-dark .bp3-button:not([class*=bp3-intent-]) .bp3-icon-large,.bp3-dark .bp3-button:not([class*=bp3-intent-]) .bp3-icon-standard,.bp3-dark .bp3-button:not([class*=bp3-intent-])[class*=bp3-icon-]:before{color:#a7b6c2}.bp3-dark .bp3-button[class*=bp3-intent-],.bp3-dark .bp3-button[class*=bp3-intent-]:hover{box-shadow:0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-button[class*=bp3-intent-].bp3-active,.bp3-dark .bp3-button[class*=bp3-intent-]:active{box-shadow:0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-dark .bp3-button[class*=bp3-intent-].bp3-disabled,.bp3-dark .bp3-button[class*=bp3-intent-]:disabled{background-image:none;box-shadow:none;color:hsla(0,0%,100%,.3)}.bp3-dark .bp3-button[class*=bp3-intent-] .bp3-button-spinner .bp3-spinner-head{stroke:#8a9ba8}.bp3-button.bp3-disabled .bp3-icon,.bp3-button.bp3-disabled .bp3-icon-large,.bp3-button.bp3-disabled .bp3-icon-standard,.bp3-button.bp3-disabled:before,.bp3-button:disabled .bp3-icon,.bp3-button:disabled .bp3-icon-large,.bp3-button:disabled .bp3-icon-standard,.bp3-button:disabled:before,.bp3-button[class*=bp3-intent-] .bp3-icon,.bp3-button[class*=bp3-intent-] .bp3-icon-large,.bp3-button[class*=bp3-intent-] .bp3-icon-standard,.bp3-button[class*=bp3-intent-]:before{color:inherit!important}.bp3-button.bp3-minimal{background:none;box-shadow:none}.bp3-button.bp3-minimal:hover{background:rgba(167,182,194,.3);box-shadow:none;color:#182026;text-decoration:none}.bp3-button.bp3-minimal.bp3-active,.bp3-button.bp3-minimal:active{background:rgba(115,134,148,.3);box-shadow:none;color:#182026}.bp3-button.bp3-minimal.bp3-disabled,.bp3-button.bp3-minimal.bp3-disabled:hover,.bp3-button.bp3-minimal:disabled,.bp3-button.bp3-minimal:disabled:hover{background:none;color:rgba(92,112,128,.6);cursor:not-allowed}.bp3-button.bp3-minimal.bp3-disabled.bp3-active,.bp3-button.bp3-minimal.bp3-disabled:hover.bp3-active,.bp3-button.bp3-minimal:disabled.bp3-active,.bp3-button.bp3-minimal:disabled:hover.bp3-active{background:rgba(115,134,148,.3)}.bp3-dark .bp3-button.bp3-minimal{background:none;box-shadow:none;color:inherit}.bp3-dark .bp3-button.bp3-minimal.bp3-active,.bp3-dark .bp3-button.bp3-minimal:active,.bp3-dark .bp3-button.bp3-minimal:hover{background:none;box-shadow:none}.bp3-dark .bp3-button.bp3-minimal:hover{background:rgba(138,155,168,.15)}.bp3-dark .bp3-button.bp3-minimal.bp3-active,.bp3-dark .bp3-button.bp3-minimal:active{background:rgba(138,155,168,.3);color:#f5f8fa}.bp3-dark .bp3-button.bp3-minimal.bp3-disabled,.bp3-dark .bp3-button.bp3-minimal.bp3-disabled:hover,.bp3-dark .bp3-button.bp3-minimal:disabled,.bp3-dark .bp3-button.bp3-minimal:disabled:hover{background:none;color:rgba(167,182,194,.6);cursor:not-allowed}.bp3-dark .bp3-button.bp3-minimal.bp3-disabled.bp3-active,.bp3-dark .bp3-button.bp3-minimal.bp3-disabled:hover.bp3-active,.bp3-dark .bp3-button.bp3-minimal:disabled.bp3-active,.bp3-dark .bp3-button.bp3-minimal:disabled:hover.bp3-active{background:rgba(138,155,168,.3)}.bp3-button.bp3-minimal.bp3-intent-primary{color:#106ba3}.bp3-button.bp3-minimal.bp3-intent-primary.bp3-active,.bp3-button.bp3-minimal.bp3-intent-primary:active,.bp3-button.bp3-minimal.bp3-intent-primary:hover{background:none;box-shadow:none;color:#106ba3}.bp3-button.bp3-minimal.bp3-intent-primary:hover{background:rgba(19,124,189,.15);color:#106ba3}.bp3-button.bp3-minimal.bp3-intent-primary.bp3-active,.bp3-button.bp3-minimal.bp3-intent-primary:active{background:rgba(19,124,189,.3);color:#106ba3}.bp3-button.bp3-minimal.bp3-intent-primary.bp3-disabled,.bp3-button.bp3-minimal.bp3-intent-primary:disabled{background:none;color:rgba(16,107,163,.5)}.bp3-button.bp3-minimal.bp3-intent-primary.bp3-disabled.bp3-active,.bp3-button.bp3-minimal.bp3-intent-primary:disabled.bp3-active{background:rgba(19,124,189,.3)}.bp3-button.bp3-minimal.bp3-intent-primary .bp3-button-spinner .bp3-spinner-head{stroke:#106ba3}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary{color:#48aff0}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary:hover{background:rgba(19,124,189,.2);color:#48aff0}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary.bp3-active,.bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary:active{background:rgba(19,124,189,.3);color:#48aff0}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary.bp3-disabled,.bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary:disabled{background:none;color:rgba(72,175,240,.5)}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary.bp3-disabled.bp3-active,.bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary:disabled.bp3-active{background:rgba(19,124,189,.3)}.bp3-button.bp3-minimal.bp3-intent-success{color:#0d8050}.bp3-button.bp3-minimal.bp3-intent-success.bp3-active,.bp3-button.bp3-minimal.bp3-intent-success:active,.bp3-button.bp3-minimal.bp3-intent-success:hover{background:none;box-shadow:none;color:#0d8050}.bp3-button.bp3-minimal.bp3-intent-success:hover{background:rgba(15,153,96,.15);color:#0d8050}.bp3-button.bp3-minimal.bp3-intent-success.bp3-active,.bp3-button.bp3-minimal.bp3-intent-success:active{background:rgba(15,153,96,.3);color:#0d8050}.bp3-button.bp3-minimal.bp3-intent-success.bp3-disabled,.bp3-button.bp3-minimal.bp3-intent-success:disabled{background:none;color:rgba(13,128,80,.5)}.bp3-button.bp3-minimal.bp3-intent-success.bp3-disabled.bp3-active,.bp3-button.bp3-minimal.bp3-intent-success:disabled.bp3-active{background:rgba(15,153,96,.3)}.bp3-button.bp3-minimal.bp3-intent-success .bp3-button-spinner .bp3-spinner-head{stroke:#0d8050}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-success{color:#3dcc91}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-success:hover{background:rgba(15,153,96,.2);color:#3dcc91}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-success.bp3-active,.bp3-dark .bp3-button.bp3-minimal.bp3-intent-success:active{background:rgba(15,153,96,.3);color:#3dcc91}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-success.bp3-disabled,.bp3-dark .bp3-button.bp3-minimal.bp3-intent-success:disabled{background:none;color:rgba(61,204,145,.5)}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-success.bp3-disabled.bp3-active,.bp3-dark .bp3-button.bp3-minimal.bp3-intent-success:disabled.bp3-active{background:rgba(15,153,96,.3)}.bp3-button.bp3-minimal.bp3-intent-warning{color:#bf7326}.bp3-button.bp3-minimal.bp3-intent-warning.bp3-active,.bp3-button.bp3-minimal.bp3-intent-warning:active,.bp3-button.bp3-minimal.bp3-intent-warning:hover{background:none;box-shadow:none;color:#bf7326}.bp3-button.bp3-minimal.bp3-intent-warning:hover{background:rgba(217,130,43,.15);color:#bf7326}.bp3-button.bp3-minimal.bp3-intent-warning.bp3-active,.bp3-button.bp3-minimal.bp3-intent-warning:active{background:rgba(217,130,43,.3);color:#bf7326}.bp3-button.bp3-minimal.bp3-intent-warning.bp3-disabled,.bp3-button.bp3-minimal.bp3-intent-warning:disabled{background:none;color:rgba(191,115,38,.5)}.bp3-button.bp3-minimal.bp3-intent-warning.bp3-disabled.bp3-active,.bp3-button.bp3-minimal.bp3-intent-warning:disabled.bp3-active{background:rgba(217,130,43,.3)}.bp3-button.bp3-minimal.bp3-intent-warning .bp3-button-spinner .bp3-spinner-head{stroke:#bf7326}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning{color:#ffb366}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning:hover{background:rgba(217,130,43,.2);color:#ffb366}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning.bp3-active,.bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning:active{background:rgba(217,130,43,.3);color:#ffb366}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning.bp3-disabled,.bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning:disabled{background:none;color:rgba(255,179,102,.5)}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning.bp3-disabled.bp3-active,.bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning:disabled.bp3-active{background:rgba(217,130,43,.3)}.bp3-button.bp3-minimal.bp3-intent-danger{color:#c23030}.bp3-button.bp3-minimal.bp3-intent-danger.bp3-active,.bp3-button.bp3-minimal.bp3-intent-danger:active,.bp3-button.bp3-minimal.bp3-intent-danger:hover{background:none;box-shadow:none;color:#c23030}.bp3-button.bp3-minimal.bp3-intent-danger:hover{background:rgba(219,55,55,.15);color:#c23030}.bp3-button.bp3-minimal.bp3-intent-danger.bp3-active,.bp3-button.bp3-minimal.bp3-intent-danger:active{background:rgba(219,55,55,.3);color:#c23030}.bp3-button.bp3-minimal.bp3-intent-danger.bp3-disabled,.bp3-button.bp3-minimal.bp3-intent-danger:disabled{background:none;color:rgba(194,48,48,.5)}.bp3-button.bp3-minimal.bp3-intent-danger.bp3-disabled.bp3-active,.bp3-button.bp3-minimal.bp3-intent-danger:disabled.bp3-active{background:rgba(219,55,55,.3)}.bp3-button.bp3-minimal.bp3-intent-danger .bp3-button-spinner .bp3-spinner-head{stroke:#c23030}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger{color:#ff7373}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger:hover{background:rgba(219,55,55,.2);color:#ff7373}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger.bp3-active,.bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger:active{background:rgba(219,55,55,.3);color:#ff7373}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger.bp3-disabled,.bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger:disabled{background:none;color:rgba(255,115,115,.5)}.bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger.bp3-disabled.bp3-active,.bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger:disabled.bp3-active{background:rgba(219,55,55,.3)}.bp3-button.bp3-outlined{background:none;box-shadow:none;border:1px solid rgba(24,32,38,.2);box-sizing:border-box}.bp3-button.bp3-outlined:hover{background:rgba(167,182,194,.3);box-shadow:none;color:#182026;text-decoration:none}.bp3-button.bp3-outlined.bp3-active,.bp3-button.bp3-outlined:active{background:rgba(115,134,148,.3);box-shadow:none;color:#182026}.bp3-button.bp3-outlined.bp3-disabled,.bp3-button.bp3-outlined.bp3-disabled:hover,.bp3-button.bp3-outlined:disabled,.bp3-button.bp3-outlined:disabled:hover{background:none;color:rgba(92,112,128,.6);cursor:not-allowed}.bp3-button.bp3-outlined.bp3-disabled.bp3-active,.bp3-button.bp3-outlined.bp3-disabled:hover.bp3-active,.bp3-button.bp3-outlined:disabled.bp3-active,.bp3-button.bp3-outlined:disabled:hover.bp3-active{background:rgba(115,134,148,.3)}.bp3-dark .bp3-button.bp3-outlined{background:none;box-shadow:none;color:inherit}.bp3-dark .bp3-button.bp3-outlined.bp3-active,.bp3-dark .bp3-button.bp3-outlined:active,.bp3-dark .bp3-button.bp3-outlined:hover{background:none;box-shadow:none}.bp3-dark .bp3-button.bp3-outlined:hover{background:rgba(138,155,168,.15)}.bp3-dark .bp3-button.bp3-outlined.bp3-active,.bp3-dark .bp3-button.bp3-outlined:active{background:rgba(138,155,168,.3);color:#f5f8fa}.bp3-dark .bp3-button.bp3-outlined.bp3-disabled,.bp3-dark .bp3-button.bp3-outlined.bp3-disabled:hover,.bp3-dark .bp3-button.bp3-outlined:disabled,.bp3-dark .bp3-button.bp3-outlined:disabled:hover{background:none;color:rgba(167,182,194,.6);cursor:not-allowed}.bp3-dark .bp3-button.bp3-outlined.bp3-disabled.bp3-active,.bp3-dark .bp3-button.bp3-outlined.bp3-disabled:hover.bp3-active,.bp3-dark .bp3-button.bp3-outlined:disabled.bp3-active,.bp3-dark .bp3-button.bp3-outlined:disabled:hover.bp3-active{background:rgba(138,155,168,.3)}.bp3-button.bp3-outlined.bp3-intent-primary{color:#106ba3}.bp3-button.bp3-outlined.bp3-intent-primary.bp3-active,.bp3-button.bp3-outlined.bp3-intent-primary:active,.bp3-button.bp3-outlined.bp3-intent-primary:hover{background:none;box-shadow:none;color:#106ba3}.bp3-button.bp3-outlined.bp3-intent-primary:hover{background:rgba(19,124,189,.15);color:#106ba3}.bp3-button.bp3-outlined.bp3-intent-primary.bp3-active,.bp3-button.bp3-outlined.bp3-intent-primary:active{background:rgba(19,124,189,.3);color:#106ba3}.bp3-button.bp3-outlined.bp3-intent-primary.bp3-disabled,.bp3-button.bp3-outlined.bp3-intent-primary:disabled{background:none;color:rgba(16,107,163,.5)}.bp3-button.bp3-outlined.bp3-intent-primary.bp3-disabled.bp3-active,.bp3-button.bp3-outlined.bp3-intent-primary:disabled.bp3-active{background:rgba(19,124,189,.3)}.bp3-button.bp3-outlined.bp3-intent-primary .bp3-button-spinner .bp3-spinner-head{stroke:#106ba3}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary{color:#48aff0}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary:hover{background:rgba(19,124,189,.2);color:#48aff0}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary.bp3-active,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary:active{background:rgba(19,124,189,.3);color:#48aff0}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary.bp3-disabled,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary:disabled{background:none;color:rgba(72,175,240,.5)}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary.bp3-disabled.bp3-active,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary:disabled.bp3-active{background:rgba(19,124,189,.3)}.bp3-button.bp3-outlined.bp3-intent-success{color:#0d8050}.bp3-button.bp3-outlined.bp3-intent-success.bp3-active,.bp3-button.bp3-outlined.bp3-intent-success:active,.bp3-button.bp3-outlined.bp3-intent-success:hover{background:none;box-shadow:none;color:#0d8050}.bp3-button.bp3-outlined.bp3-intent-success:hover{background:rgba(15,153,96,.15);color:#0d8050}.bp3-button.bp3-outlined.bp3-intent-success.bp3-active,.bp3-button.bp3-outlined.bp3-intent-success:active{background:rgba(15,153,96,.3);color:#0d8050}.bp3-button.bp3-outlined.bp3-intent-success.bp3-disabled,.bp3-button.bp3-outlined.bp3-intent-success:disabled{background:none;color:rgba(13,128,80,.5)}.bp3-button.bp3-outlined.bp3-intent-success.bp3-disabled.bp3-active,.bp3-button.bp3-outlined.bp3-intent-success:disabled.bp3-active{background:rgba(15,153,96,.3)}.bp3-button.bp3-outlined.bp3-intent-success .bp3-button-spinner .bp3-spinner-head{stroke:#0d8050}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-success{color:#3dcc91}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-success:hover{background:rgba(15,153,96,.2);color:#3dcc91}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-success.bp3-active,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-success:active{background:rgba(15,153,96,.3);color:#3dcc91}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-success.bp3-disabled,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-success:disabled{background:none;color:rgba(61,204,145,.5)}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-success.bp3-disabled.bp3-active,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-success:disabled.bp3-active{background:rgba(15,153,96,.3)}.bp3-button.bp3-outlined.bp3-intent-warning{color:#bf7326}.bp3-button.bp3-outlined.bp3-intent-warning.bp3-active,.bp3-button.bp3-outlined.bp3-intent-warning:active,.bp3-button.bp3-outlined.bp3-intent-warning:hover{background:none;box-shadow:none;color:#bf7326}.bp3-button.bp3-outlined.bp3-intent-warning:hover{background:rgba(217,130,43,.15);color:#bf7326}.bp3-button.bp3-outlined.bp3-intent-warning.bp3-active,.bp3-button.bp3-outlined.bp3-intent-warning:active{background:rgba(217,130,43,.3);color:#bf7326}.bp3-button.bp3-outlined.bp3-intent-warning.bp3-disabled,.bp3-button.bp3-outlined.bp3-intent-warning:disabled{background:none;color:rgba(191,115,38,.5)}.bp3-button.bp3-outlined.bp3-intent-warning.bp3-disabled.bp3-active,.bp3-button.bp3-outlined.bp3-intent-warning:disabled.bp3-active{background:rgba(217,130,43,.3)}.bp3-button.bp3-outlined.bp3-intent-warning .bp3-button-spinner .bp3-spinner-head{stroke:#bf7326}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning{color:#ffb366}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning:hover{background:rgba(217,130,43,.2);color:#ffb366}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning.bp3-active,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning:active{background:rgba(217,130,43,.3);color:#ffb366}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning.bp3-disabled,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning:disabled{background:none;color:rgba(255,179,102,.5)}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning.bp3-disabled.bp3-active,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning:disabled.bp3-active{background:rgba(217,130,43,.3)}.bp3-button.bp3-outlined.bp3-intent-danger{color:#c23030}.bp3-button.bp3-outlined.bp3-intent-danger.bp3-active,.bp3-button.bp3-outlined.bp3-intent-danger:active,.bp3-button.bp3-outlined.bp3-intent-danger:hover{background:none;box-shadow:none;color:#c23030}.bp3-button.bp3-outlined.bp3-intent-danger:hover{background:rgba(219,55,55,.15);color:#c23030}.bp3-button.bp3-outlined.bp3-intent-danger.bp3-active,.bp3-button.bp3-outlined.bp3-intent-danger:active{background:rgba(219,55,55,.3);color:#c23030}.bp3-button.bp3-outlined.bp3-intent-danger.bp3-disabled,.bp3-button.bp3-outlined.bp3-intent-danger:disabled{background:none;color:rgba(194,48,48,.5)}.bp3-button.bp3-outlined.bp3-intent-danger.bp3-disabled.bp3-active,.bp3-button.bp3-outlined.bp3-intent-danger:disabled.bp3-active{background:rgba(219,55,55,.3)}.bp3-button.bp3-outlined.bp3-intent-danger .bp3-button-spinner .bp3-spinner-head{stroke:#c23030}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger{color:#ff7373}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger:hover{background:rgba(219,55,55,.2);color:#ff7373}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger.bp3-active,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger:active{background:rgba(219,55,55,.3);color:#ff7373}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger.bp3-disabled,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger:disabled{background:none;color:rgba(255,115,115,.5)}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger.bp3-disabled.bp3-active,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger:disabled.bp3-active{background:rgba(219,55,55,.3)}.bp3-button.bp3-outlined.bp3-disabled,.bp3-button.bp3-outlined.bp3-disabled:hover,.bp3-button.bp3-outlined:disabled,.bp3-button.bp3-outlined:disabled:hover{border-color:rgba(92,112,128,.1)}.bp3-dark .bp3-button.bp3-outlined{border-color:hsla(0,0%,100%,.4)}.bp3-dark .bp3-button.bp3-outlined.bp3-disabled,.bp3-dark .bp3-button.bp3-outlined.bp3-disabled:hover,.bp3-dark .bp3-button.bp3-outlined:disabled,.bp3-dark .bp3-button.bp3-outlined:disabled:hover{border-color:hsla(0,0%,100%,.2)}.bp3-button.bp3-outlined.bp3-intent-primary{border-color:rgba(16,107,163,.6)}.bp3-button.bp3-outlined.bp3-intent-primary.bp3-disabled,.bp3-button.bp3-outlined.bp3-intent-primary:disabled{border-color:rgba(16,107,163,.2)}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary{border-color:rgba(72,175,240,.6)}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary.bp3-disabled,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary:disabled{border-color:rgba(72,175,240,.2)}.bp3-button.bp3-outlined.bp3-intent-success{border-color:rgba(13,128,80,.6)}.bp3-button.bp3-outlined.bp3-intent-success.bp3-disabled,.bp3-button.bp3-outlined.bp3-intent-success:disabled{border-color:rgba(13,128,80,.2)}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-success{border-color:rgba(61,204,145,.6)}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-success.bp3-disabled,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-success:disabled{border-color:rgba(61,204,145,.2)}.bp3-button.bp3-outlined.bp3-intent-warning{border-color:rgba(191,115,38,.6)}.bp3-button.bp3-outlined.bp3-intent-warning.bp3-disabled,.bp3-button.bp3-outlined.bp3-intent-warning:disabled{border-color:rgba(191,115,38,.2)}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning{border-color:rgba(255,179,102,.6)}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning.bp3-disabled,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning:disabled{border-color:rgba(255,179,102,.2)}.bp3-button.bp3-outlined.bp3-intent-danger{border-color:rgba(194,48,48,.6)}.bp3-button.bp3-outlined.bp3-intent-danger.bp3-disabled,.bp3-button.bp3-outlined.bp3-intent-danger:disabled{border-color:rgba(194,48,48,.2)}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger{border-color:rgba(255,115,115,.6)}.bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger.bp3-disabled,.bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger:disabled{border-color:rgba(255,115,115,.2)}a.bp3-button{text-align:center;text-decoration:none;transition:none}a.bp3-button,a.bp3-button:active,a.bp3-button:hover{color:#182026}a.bp3-button.bp3-disabled{color:rgba(92,112,128,.6)}.bp3-button-text{flex:0 1 auto}.bp3-button-group.bp3-align-left .bp3-button-text,.bp3-button-group.bp3-align-right .bp3-button-text,.bp3-button.bp3-align-left .bp3-button-text,.bp3-button.bp3-align-right .bp3-button-text{flex:1 1 auto}.bp3-button-group{display:inline-flex}.bp3-button-group .bp3-button{flex:0 0 auto;position:relative;z-index:4}.bp3-button-group .bp3-button:focus{z-index:5}.bp3-button-group .bp3-button:hover{z-index:6}.bp3-button-group .bp3-button.bp3-active,.bp3-button-group .bp3-button:active{z-index:7}.bp3-button-group .bp3-button.bp3-disabled,.bp3-button-group .bp3-button:disabled{z-index:3}.bp3-button-group .bp3-button[class*=bp3-intent-]{z-index:9}.bp3-button-group .bp3-button[class*=bp3-intent-]:focus{z-index:10}.bp3-button-group .bp3-button[class*=bp3-intent-]:hover{z-index:11}.bp3-button-group .bp3-button[class*=bp3-intent-].bp3-active,.bp3-button-group .bp3-button[class*=bp3-intent-]:active{z-index:12}.bp3-button-group .bp3-button[class*=bp3-intent-].bp3-disabled,.bp3-button-group .bp3-button[class*=bp3-intent-]:disabled{z-index:8}.bp3-button-group:not(.bp3-minimal)>.bp3-button:not(:first-child),.bp3-button-group:not(.bp3-minimal)>.bp3-popover-wrapper:not(:first-child) .bp3-button{border-bottom-left-radius:0;border-top-left-radius:0}.bp3-button-group:not(.bp3-minimal)>.bp3-button:not(:last-child),.bp3-button-group:not(.bp3-minimal)>.bp3-popover-wrapper:not(:last-child) .bp3-button{border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.bp3-button-group.bp3-minimal .bp3-button{background:none;box-shadow:none}.bp3-button-group.bp3-minimal .bp3-button:hover{background:rgba(167,182,194,.3);box-shadow:none;color:#182026;text-decoration:none}.bp3-button-group.bp3-minimal .bp3-button.bp3-active,.bp3-button-group.bp3-minimal .bp3-button:active{background:rgba(115,134,148,.3);box-shadow:none;color:#182026}.bp3-button-group.bp3-minimal .bp3-button.bp3-disabled,.bp3-button-group.bp3-minimal .bp3-button.bp3-disabled:hover,.bp3-button-group.bp3-minimal .bp3-button:disabled,.bp3-button-group.bp3-minimal .bp3-button:disabled:hover{background:none;color:rgba(92,112,128,.6);cursor:not-allowed}.bp3-button-group.bp3-minimal .bp3-button.bp3-disabled.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-disabled:hover.bp3-active,.bp3-button-group.bp3-minimal .bp3-button:disabled.bp3-active,.bp3-button-group.bp3-minimal .bp3-button:disabled:hover.bp3-active{background:rgba(115,134,148,.3)}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button{background:none;box-shadow:none;color:inherit}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button:active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button:hover{background:none;box-shadow:none}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button:hover{background:rgba(138,155,168,.15)}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button:active{background:rgba(138,155,168,.3);color:#f5f8fa}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-disabled,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-disabled:hover,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button:disabled,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button:disabled:hover{background:none;color:rgba(167,182,194,.6);cursor:not-allowed}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-disabled.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-disabled:hover.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button:disabled.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button:disabled:hover.bp3-active{background:rgba(138,155,168,.3)}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary{color:#106ba3}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:hover{background:none;box-shadow:none;color:#106ba3}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:hover{background:rgba(19,124,189,.15);color:#106ba3}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:active{background:rgba(19,124,189,.3);color:#106ba3}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-disabled,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:disabled{background:none;color:rgba(16,107,163,.5)}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-disabled.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:disabled.bp3-active{background:rgba(19,124,189,.3)}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary .bp3-button-spinner .bp3-spinner-head{stroke:#106ba3}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary{color:#48aff0}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:hover{background:rgba(19,124,189,.2);color:#48aff0}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:active{background:rgba(19,124,189,.3);color:#48aff0}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-disabled,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:disabled{background:none;color:rgba(72,175,240,.5)}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-disabled.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:disabled.bp3-active{background:rgba(19,124,189,.3)}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success{color:#0d8050}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:hover{background:none;box-shadow:none;color:#0d8050}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:hover{background:rgba(15,153,96,.15);color:#0d8050}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:active{background:rgba(15,153,96,.3);color:#0d8050}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-disabled,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:disabled{background:none;color:rgba(13,128,80,.5)}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-disabled.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:disabled.bp3-active{background:rgba(15,153,96,.3)}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success .bp3-button-spinner .bp3-spinner-head{stroke:#0d8050}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success{color:#3dcc91}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:hover{background:rgba(15,153,96,.2);color:#3dcc91}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:active{background:rgba(15,153,96,.3);color:#3dcc91}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-disabled,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:disabled{background:none;color:rgba(61,204,145,.5)}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-disabled.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:disabled.bp3-active{background:rgba(15,153,96,.3)}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning{color:#bf7326}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:hover{background:none;box-shadow:none;color:#bf7326}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:hover{background:rgba(217,130,43,.15);color:#bf7326}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:active{background:rgba(217,130,43,.3);color:#bf7326}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-disabled,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:disabled{background:none;color:rgba(191,115,38,.5)}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-disabled.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:disabled.bp3-active{background:rgba(217,130,43,.3)}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning .bp3-button-spinner .bp3-spinner-head{stroke:#bf7326}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning{color:#ffb366}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:hover{background:rgba(217,130,43,.2);color:#ffb366}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:active{background:rgba(217,130,43,.3);color:#ffb366}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-disabled,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:disabled{background:none;color:rgba(255,179,102,.5)}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-disabled.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:disabled.bp3-active{background:rgba(217,130,43,.3)}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger{color:#c23030}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:hover{background:none;box-shadow:none;color:#c23030}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:hover{background:rgba(219,55,55,.15);color:#c23030}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:active{background:rgba(219,55,55,.3);color:#c23030}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-disabled,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:disabled{background:none;color:rgba(194,48,48,.5)}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-disabled.bp3-active,.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:disabled.bp3-active{background:rgba(219,55,55,.3)}.bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger .bp3-button-spinner .bp3-spinner-head{stroke:#c23030}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger{color:#ff7373}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:hover{background:rgba(219,55,55,.2);color:#ff7373}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:active{background:rgba(219,55,55,.3);color:#ff7373}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-disabled,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:disabled{background:none;color:rgba(255,115,115,.5)}.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-disabled.bp3-active,.bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:disabled.bp3-active{background:rgba(219,55,55,.3)}.bp3-button-group .bp3-popover-target,.bp3-button-group .bp3-popover-wrapper{display:flex;flex:1 1 auto}.bp3-button-group.bp3-fill{display:flex;width:100%}.bp3-button-group .bp3-button.bp3-fill,.bp3-button-group.bp3-fill .bp3-button:not(.bp3-fixed){flex:1 1 auto}.bp3-button-group.bp3-vertical{align-items:stretch;flex-direction:column;vertical-align:top}.bp3-button-group.bp3-vertical.bp3-fill{height:100%;width:unset}.bp3-button-group.bp3-vertical .bp3-button{margin-right:0!important;width:100%}.bp3-button-group.bp3-vertical:not(.bp3-minimal)>.bp3-button:first-child,.bp3-button-group.bp3-vertical:not(.bp3-minimal)>.bp3-popover-wrapper:first-child .bp3-button{border-radius:3px 3px 0 0}.bp3-button-group.bp3-vertical:not(.bp3-minimal)>.bp3-button:last-child,.bp3-button-group.bp3-vertical:not(.bp3-minimal)>.bp3-popover-wrapper:last-child .bp3-button{border-radius:0 0 3px 3px}.bp3-button-group.bp3-vertical:not(.bp3-minimal)>.bp3-button:not(:last-child),.bp3-button-group.bp3-vertical:not(.bp3-minimal)>.bp3-popover-wrapper:not(:last-child) .bp3-button{margin-bottom:-1px}.bp3-button-group.bp3-align-left .bp3-button{text-align:left}.bp3-dark .bp3-button-group:not(.bp3-minimal)>.bp3-button:not(:last-child),.bp3-dark .bp3-button-group:not(.bp3-minimal)>.bp3-popover-wrapper:not(:last-child) .bp3-button{margin-right:1px}.bp3-dark .bp3-button-group.bp3-vertical>.bp3-button:not(:last-child),.bp3-dark .bp3-button-group.bp3-vertical>.bp3-popover-wrapper:not(:last-child) .bp3-button{margin-bottom:1px}.bp3-callout{font-size:14px;line-height:1.5;background-color:rgba(138,155,168,.15);border-radius:3px;padding:10px 12px 9px;position:relative;width:100%}.bp3-callout[class*=bp3-icon-]{padding-left:40px}.bp3-callout[class*=bp3-icon-]:before{font-family:Icons20,sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#5c7080;left:10px;position:absolute;top:10px}.bp3-callout.bp3-callout-icon{padding-left:40px}.bp3-callout.bp3-callout-icon>.bp3-icon:first-child{color:#5c7080;left:10px;position:absolute;top:10px}.bp3-callout .bp3-heading{line-height:20px;margin-bottom:5px;margin-top:0}.bp3-callout .bp3-heading:last-child{margin-bottom:0}.bp3-dark .bp3-callout{background-color:rgba(138,155,168,.2)}.bp3-dark .bp3-callout[class*=bp3-icon-]:before{color:#a7b6c2}.bp3-callout.bp3-intent-primary{background-color:rgba(19,124,189,.15)}.bp3-callout.bp3-intent-primary .bp3-heading,.bp3-callout.bp3-intent-primary>.bp3-icon:first-child,.bp3-callout.bp3-intent-primary[class*=bp3-icon-]:before{color:#106ba3}.bp3-dark .bp3-callout.bp3-intent-primary{background-color:rgba(19,124,189,.25)}.bp3-dark .bp3-callout.bp3-intent-primary .bp3-heading,.bp3-dark .bp3-callout.bp3-intent-primary>.bp3-icon:first-child,.bp3-dark .bp3-callout.bp3-intent-primary[class*=bp3-icon-]:before{color:#48aff0}.bp3-callout.bp3-intent-success{background-color:rgba(15,153,96,.15)}.bp3-callout.bp3-intent-success .bp3-heading,.bp3-callout.bp3-intent-success>.bp3-icon:first-child,.bp3-callout.bp3-intent-success[class*=bp3-icon-]:before{color:#0d8050}.bp3-dark .bp3-callout.bp3-intent-success{background-color:rgba(15,153,96,.25)}.bp3-dark .bp3-callout.bp3-intent-success .bp3-heading,.bp3-dark .bp3-callout.bp3-intent-success>.bp3-icon:first-child,.bp3-dark .bp3-callout.bp3-intent-success[class*=bp3-icon-]:before{color:#3dcc91}.bp3-callout.bp3-intent-warning{background-color:rgba(217,130,43,.15)}.bp3-callout.bp3-intent-warning .bp3-heading,.bp3-callout.bp3-intent-warning>.bp3-icon:first-child,.bp3-callout.bp3-intent-warning[class*=bp3-icon-]:before{color:#bf7326}.bp3-dark .bp3-callout.bp3-intent-warning{background-color:rgba(217,130,43,.25)}.bp3-dark .bp3-callout.bp3-intent-warning .bp3-heading,.bp3-dark .bp3-callout.bp3-intent-warning>.bp3-icon:first-child,.bp3-dark .bp3-callout.bp3-intent-warning[class*=bp3-icon-]:before{color:#ffb366}.bp3-callout.bp3-intent-danger{background-color:rgba(219,55,55,.15)}.bp3-callout.bp3-intent-danger .bp3-heading,.bp3-callout.bp3-intent-danger>.bp3-icon:first-child,.bp3-callout.bp3-intent-danger[class*=bp3-icon-]:before{color:#c23030}.bp3-dark .bp3-callout.bp3-intent-danger{background-color:rgba(219,55,55,.25)}.bp3-dark .bp3-callout.bp3-intent-danger .bp3-heading,.bp3-dark .bp3-callout.bp3-intent-danger>.bp3-icon:first-child,.bp3-dark .bp3-callout.bp3-intent-danger[class*=bp3-icon-]:before{color:#ff7373}.bp3-running-text .bp3-callout{margin:20px 0}.bp3-card{background-color:#fff;border-radius:3px;box-shadow:0 0 0 1px rgba(16,22,26,.15),0 0 0 rgba(16,22,26,0),0 0 0 rgba(16,22,26,0);padding:20px;transition:transform .2s cubic-bezier(.4,1,.75,.9),box-shadow .2s cubic-bezier(.4,1,.75,.9)}.bp3-card.bp3-dark,.bp3-dark .bp3-card{background-color:#30404d;box-shadow:0 0 0 1px rgba(16,22,26,.4),0 0 0 rgba(16,22,26,0),0 0 0 rgba(16,22,26,0)}.bp3-elevation-0{box-shadow:0 0 0 1px rgba(16,22,26,.15),0 0 0 rgba(16,22,26,0),0 0 0 rgba(16,22,26,0)}.bp3-dark .bp3-elevation-0,.bp3-elevation-0.bp3-dark{box-shadow:0 0 0 1px rgba(16,22,26,.4),0 0 0 rgba(16,22,26,0),0 0 0 rgba(16,22,26,0)}.bp3-elevation-1{box-shadow:0 0 0 1px rgba(16,22,26,.1),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.2)}.bp3-dark .bp3-elevation-1,.bp3-elevation-1.bp3-dark{box-shadow:0 0 0 1px rgba(16,22,26,.2),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.4)}.bp3-elevation-2{box-shadow:0 0 0 1px rgba(16,22,26,.1),0 1px 1px rgba(16,22,26,.2),0 2px 6px rgba(16,22,26,.2)}.bp3-dark .bp3-elevation-2,.bp3-elevation-2.bp3-dark{box-shadow:0 0 0 1px rgba(16,22,26,.2),0 1px 1px rgba(16,22,26,.4),0 2px 6px rgba(16,22,26,.4)}.bp3-elevation-3{box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2)}.bp3-dark .bp3-elevation-3,.bp3-elevation-3.bp3-dark{box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4)}.bp3-elevation-4{box-shadow:0 0 0 1px rgba(16,22,26,.1),0 4px 8px rgba(16,22,26,.2),0 18px 46px 6px rgba(16,22,26,.2)}.bp3-dark .bp3-elevation-4,.bp3-elevation-4.bp3-dark{box-shadow:0 0 0 1px rgba(16,22,26,.2),0 4px 8px rgba(16,22,26,.4),0 18px 46px 6px rgba(16,22,26,.4)}.bp3-card.bp3-interactive:hover{box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2);cursor:pointer}.bp3-card.bp3-interactive:hover.bp3-dark,.bp3-dark .bp3-card.bp3-interactive:hover{box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4)}.bp3-card.bp3-interactive:active{box-shadow:0 0 0 1px rgba(16,22,26,.1),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.2);opacity:.9;transition-duration:0}.bp3-card.bp3-interactive:active.bp3-dark,.bp3-dark .bp3-card.bp3-interactive:active{box-shadow:0 0 0 1px rgba(16,22,26,.2),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.4)}.bp3-collapse{height:0;overflow-y:hidden;transition:height .2s cubic-bezier(.4,1,.75,.9)}.bp3-collapse .bp3-collapse-body{transition:transform .2s cubic-bezier(.4,1,.75,.9)}.bp3-collapse .bp3-collapse-body[aria-hidden=true]{display:none}.bp3-context-menu .bp3-popover-target{display:block}.bp3-context-menu-popover-target{position:fixed}.bp3-dialog-container{opacity:1;transform:scale(1);align-items:center;display:flex;justify-content:center;min-height:100%;pointer-events:none;-webkit-user-select:none;user-select:none;width:100%}.bp3-dialog-container.bp3-overlay-appear>.bp3-dialog,.bp3-dialog-container.bp3-overlay-enter>.bp3-dialog{opacity:0;transform:scale(.5)}.bp3-dialog-container.bp3-overlay-appear-active>.bp3-dialog,.bp3-dialog-container.bp3-overlay-enter-active>.bp3-dialog{opacity:1;transform:scale(1);transition-delay:0;transition-duration:.3s;transition-property:opacity,transform;transition-timing-function:cubic-bezier(.54,1.12,.38,1.11)}.bp3-dialog-container.bp3-overlay-exit>.bp3-dialog{opacity:1;transform:scale(1)}.bp3-dialog-container.bp3-overlay-exit-active>.bp3-dialog{opacity:0;transform:scale(.5);transition-delay:0;transition-duration:.3s;transition-property:opacity,transform;transition-timing-function:cubic-bezier(.54,1.12,.38,1.11)}.bp3-dialog{background:#ebf1f5;border-radius:6px;box-shadow:0 0 0 1px rgba(16,22,26,.1),0 4px 8px rgba(16,22,26,.2),0 18px 46px 6px rgba(16,22,26,.2);display:flex;flex-direction:column;margin:30px 0;padding-bottom:20px;pointer-events:all;-webkit-user-select:text;user-select:text;width:500px}.bp3-dialog:focus{outline:0}.bp3-dark .bp3-dialog,.bp3-dialog.bp3-dark{background:#293742;box-shadow:0 0 0 1px rgba(16,22,26,.2),0 4px 8px rgba(16,22,26,.4),0 18px 46px 6px rgba(16,22,26,.4);color:#f5f8fa}.bp3-dialog-header{align-items:center;background:#fff;border-radius:6px 6px 0 0;box-shadow:0 1px 0 rgba(16,22,26,.15);display:flex;flex:0 0 auto;min-height:40px;padding-left:20px;padding-right:5px}.bp3-dialog-header .bp3-icon,.bp3-dialog-header .bp3-icon-large{color:#5c7080;flex:0 0 auto;margin-right:10px}.bp3-dialog-header .bp3-heading{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;flex:1 1 auto;line-height:inherit;margin:0}.bp3-dialog-header .bp3-heading:last-child{margin-right:20px}.bp3-dark .bp3-dialog-header{background:#30404d;box-shadow:0 1px 0 rgba(16,22,26,.4)}.bp3-dark .bp3-dialog-header .bp3-icon,.bp3-dark .bp3-dialog-header .bp3-icon-large{color:#a7b6c2}.bp3-dialog-body{flex:1 1 auto;line-height:18px;margin:20px}.bp3-dialog-footer{flex:0 0 auto;margin:0 20px}.bp3-dialog-footer-actions{display:flex;justify-content:flex-end}.bp3-dialog-footer-actions .bp3-button{margin-left:10px}.bp3-drawer{background:#fff;box-shadow:0 0 0 1px rgba(16,22,26,.1),0 4px 8px rgba(16,22,26,.2),0 18px 46px 6px rgba(16,22,26,.2);display:flex;flex-direction:column;margin:0;padding:0}.bp3-drawer:focus{outline:0}.bp3-drawer.bp3-position-top{height:50%;left:0;right:0;top:0}.bp3-drawer.bp3-position-top.bp3-overlay-appear,.bp3-drawer.bp3-position-top.bp3-overlay-enter{transform:translateY(-100%)}.bp3-drawer.bp3-position-top.bp3-overlay-appear-active,.bp3-drawer.bp3-position-top.bp3-overlay-enter-active{transform:translateY(0);transition-delay:0;transition-duration:.2s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-drawer.bp3-position-top.bp3-overlay-exit{transform:translateY(0)}.bp3-drawer.bp3-position-top.bp3-overlay-exit-active{transform:translateY(-100%);transition-delay:0;transition-duration:.1s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-drawer.bp3-position-bottom{bottom:0;height:50%;left:0;right:0}.bp3-drawer.bp3-position-bottom.bp3-overlay-appear,.bp3-drawer.bp3-position-bottom.bp3-overlay-enter{transform:translateY(100%)}.bp3-drawer.bp3-position-bottom.bp3-overlay-appear-active,.bp3-drawer.bp3-position-bottom.bp3-overlay-enter-active{transform:translateY(0);transition-delay:0;transition-duration:.2s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-drawer.bp3-position-bottom.bp3-overlay-exit{transform:translateY(0)}.bp3-drawer.bp3-position-bottom.bp3-overlay-exit-active{transform:translateY(100%);transition-delay:0;transition-duration:.1s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-drawer.bp3-position-left{bottom:0;left:0;top:0;width:50%}.bp3-drawer.bp3-position-left.bp3-overlay-appear,.bp3-drawer.bp3-position-left.bp3-overlay-enter{transform:translateX(-100%)}.bp3-drawer.bp3-position-left.bp3-overlay-appear-active,.bp3-drawer.bp3-position-left.bp3-overlay-enter-active{transform:translateX(0);transition-delay:0;transition-duration:.2s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-drawer.bp3-position-left.bp3-overlay-exit{transform:translateX(0)}.bp3-drawer.bp3-position-left.bp3-overlay-exit-active{transform:translateX(-100%);transition-delay:0;transition-duration:.1s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-drawer.bp3-position-right{bottom:0;right:0;top:0;width:50%}.bp3-drawer.bp3-position-right.bp3-overlay-appear,.bp3-drawer.bp3-position-right.bp3-overlay-enter{transform:translateX(100%)}.bp3-drawer.bp3-position-right.bp3-overlay-appear-active,.bp3-drawer.bp3-position-right.bp3-overlay-enter-active{transform:translateX(0);transition-delay:0;transition-duration:.2s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-drawer.bp3-position-right.bp3-overlay-exit{transform:translateX(0)}.bp3-drawer.bp3-position-right.bp3-overlay-exit-active{transform:translateX(100%);transition-delay:0;transition-duration:.1s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical){bottom:0;right:0;top:0;width:50%}.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical).bp3-overlay-appear,.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical).bp3-overlay-enter{transform:translateX(100%)}.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical).bp3-overlay-appear-active,.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical).bp3-overlay-enter-active{transform:translateX(0);transition-delay:0;transition-duration:.2s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical).bp3-overlay-exit{transform:translateX(0)}.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical).bp3-overlay-exit-active{transform:translateX(100%);transition-delay:0;transition-duration:.1s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical{bottom:0;height:50%;left:0;right:0}.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical.bp3-overlay-appear,.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical.bp3-overlay-enter{transform:translateY(100%)}.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical.bp3-overlay-appear-active,.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical.bp3-overlay-enter-active{transform:translateY(0);transition-delay:0;transition-duration:.2s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical.bp3-overlay-exit{transform:translateY(0)}.bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical.bp3-overlay-exit-active{transform:translateY(100%);transition-delay:0;transition-duration:.1s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-dark .bp3-drawer,.bp3-drawer.bp3-dark{background:#30404d;box-shadow:0 0 0 1px rgba(16,22,26,.2),0 4px 8px rgba(16,22,26,.4),0 18px 46px 6px rgba(16,22,26,.4);color:#f5f8fa}.bp3-drawer-header{align-items:center;border-radius:0;box-shadow:0 1px 0 rgba(16,22,26,.15);display:flex;flex:0 0 auto;min-height:40px;padding:5px 5px 5px 20px;position:relative}.bp3-drawer-header .bp3-icon,.bp3-drawer-header .bp3-icon-large{color:#5c7080;flex:0 0 auto;margin-right:10px}.bp3-drawer-header .bp3-heading{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;flex:1 1 auto;line-height:inherit;margin:0}.bp3-drawer-header .bp3-heading:last-child{margin-right:20px}.bp3-dark .bp3-drawer-header{box-shadow:0 1px 0 rgba(16,22,26,.4)}.bp3-dark .bp3-drawer-header .bp3-icon,.bp3-dark .bp3-drawer-header .bp3-icon-large{color:#a7b6c2}.bp3-drawer-body{flex:1 1 auto;line-height:18px;overflow:auto}.bp3-drawer-footer{box-shadow:inset 0 1px 0 rgba(16,22,26,.15);flex:0 0 auto;padding:10px 20px;position:relative}.bp3-dark .bp3-drawer-footer{box-shadow:inset 0 1px 0 rgba(16,22,26,.4)}.bp3-editable-text{cursor:text;display:inline-block;max-width:100%;position:relative;vertical-align:top;white-space:nowrap}.bp3-editable-text:before{bottom:-3px;left:-3px;position:absolute;right:-3px;top:-3px;border-radius:3px;content:"";transition:background-color .1s cubic-bezier(.4,1,.75,.9),box-shadow .1s cubic-bezier(.4,1,.75,.9)}.bp3-editable-text:hover:before{box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(16,22,26,.15)}.bp3-editable-text.bp3-editable-text-editing:before{background-color:#fff;box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-editable-text.bp3-disabled:before{box-shadow:none}.bp3-editable-text.bp3-intent-primary .bp3-editable-text-content,.bp3-editable-text.bp3-intent-primary .bp3-editable-text-input{color:#137cbd}.bp3-editable-text.bp3-intent-primary:hover:before{box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(19,124,189,.4)}.bp3-editable-text.bp3-intent-primary.bp3-editable-text-editing:before{box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-editable-text.bp3-intent-success .bp3-editable-text-content,.bp3-editable-text.bp3-intent-success .bp3-editable-text-input{color:#0f9960}.bp3-editable-text.bp3-intent-success:hover:before{box-shadow:0 0 0 0 rgba(15,153,96,0),0 0 0 0 rgba(15,153,96,0),inset 0 0 0 1px rgba(15,153,96,.4)}.bp3-editable-text.bp3-intent-success.bp3-editable-text-editing:before{box-shadow:0 0 0 1px #0f9960,0 0 0 3px rgba(15,153,96,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-editable-text.bp3-intent-warning .bp3-editable-text-content,.bp3-editable-text.bp3-intent-warning .bp3-editable-text-input{color:#d9822b}.bp3-editable-text.bp3-intent-warning:hover:before{box-shadow:0 0 0 0 rgba(217,130,43,0),0 0 0 0 rgba(217,130,43,0),inset 0 0 0 1px rgba(217,130,43,.4)}.bp3-editable-text.bp3-intent-warning.bp3-editable-text-editing:before{box-shadow:0 0 0 1px #d9822b,0 0 0 3px rgba(217,130,43,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-editable-text.bp3-intent-danger .bp3-editable-text-content,.bp3-editable-text.bp3-intent-danger .bp3-editable-text-input{color:#db3737}.bp3-editable-text.bp3-intent-danger:hover:before{box-shadow:0 0 0 0 rgba(219,55,55,0),0 0 0 0 rgba(219,55,55,0),inset 0 0 0 1px rgba(219,55,55,.4)}.bp3-editable-text.bp3-intent-danger.bp3-editable-text-editing:before{box-shadow:0 0 0 1px #db3737,0 0 0 3px rgba(219,55,55,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-dark .bp3-editable-text:hover:before{box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px hsla(0,0%,100%,.15)}.bp3-dark .bp3-editable-text.bp3-editable-text-editing:before{background-color:rgba(16,22,26,.3);box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-editable-text.bp3-disabled:before{box-shadow:none}.bp3-dark .bp3-editable-text.bp3-intent-primary .bp3-editable-text-content{color:#48aff0}.bp3-dark .bp3-editable-text.bp3-intent-primary:hover:before{box-shadow:0 0 0 0 rgba(72,175,240,0),0 0 0 0 rgba(72,175,240,0),inset 0 0 0 1px rgba(72,175,240,.4)}.bp3-dark .bp3-editable-text.bp3-intent-primary.bp3-editable-text-editing:before{box-shadow:0 0 0 1px #48aff0,0 0 0 3px rgba(72,175,240,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-editable-text.bp3-intent-success .bp3-editable-text-content{color:#3dcc91}.bp3-dark .bp3-editable-text.bp3-intent-success:hover:before{box-shadow:0 0 0 0 rgba(61,204,145,0),0 0 0 0 rgba(61,204,145,0),inset 0 0 0 1px rgba(61,204,145,.4)}.bp3-dark .bp3-editable-text.bp3-intent-success.bp3-editable-text-editing:before{box-shadow:0 0 0 1px #3dcc91,0 0 0 3px rgba(61,204,145,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-editable-text.bp3-intent-warning .bp3-editable-text-content{color:#ffb366}.bp3-dark .bp3-editable-text.bp3-intent-warning:hover:before{box-shadow:0 0 0 0 rgba(255,179,102,0),0 0 0 0 rgba(255,179,102,0),inset 0 0 0 1px rgba(255,179,102,.4)}.bp3-dark .bp3-editable-text.bp3-intent-warning.bp3-editable-text-editing:before{box-shadow:0 0 0 1px #ffb366,0 0 0 3px rgba(255,179,102,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-editable-text.bp3-intent-danger .bp3-editable-text-content{color:#ff7373}.bp3-dark .bp3-editable-text.bp3-intent-danger:hover:before{box-shadow:0 0 0 0 rgba(255,115,115,0),0 0 0 0 rgba(255,115,115,0),inset 0 0 0 1px rgba(255,115,115,.4)}.bp3-dark .bp3-editable-text.bp3-intent-danger.bp3-editable-text-editing:before{box-shadow:0 0 0 1px #ff7373,0 0 0 3px rgba(255,115,115,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-editable-text-content,.bp3-editable-text-input{color:inherit;display:inherit;font:inherit;letter-spacing:inherit;max-width:inherit;min-width:inherit;position:relative;resize:none;text-transform:inherit;vertical-align:top}.bp3-editable-text-input{background:none;border:none;box-shadow:none;padding:0;white-space:pre-wrap;width:100%}.bp3-editable-text-input::-webkit-input-placeholder{color:rgba(92,112,128,.6);opacity:1}.bp3-editable-text-input::placeholder{color:rgba(92,112,128,.6);opacity:1}.bp3-editable-text-input:focus{outline:none}.bp3-editable-text-input::-ms-clear{display:none}.bp3-editable-text-content{overflow:hidden;padding-right:2px;text-overflow:ellipsis;white-space:pre}.bp3-editable-text-editing>.bp3-editable-text-content{left:0;position:absolute;visibility:hidden}.bp3-editable-text-placeholder>.bp3-editable-text-content{color:rgba(92,112,128,.6)}.bp3-dark .bp3-editable-text-placeholder>.bp3-editable-text-content{color:rgba(167,182,194,.6)}.bp3-editable-text.bp3-multiline{display:block}.bp3-editable-text.bp3-multiline .bp3-editable-text-content{overflow:auto;white-space:pre-wrap;word-wrap:break-word}.bp3-divider{border-bottom:1px solid rgba(16,22,26,.15);border-right:1px solid rgba(16,22,26,.15);margin:5px}.bp3-dark .bp3-divider{border-color:rgba(16,22,26,.4)}.bp3-control-group{transform:translateZ(0);display:flex;flex-direction:row;align-items:stretch}.bp3-control-group>*{flex-grow:0;flex-shrink:0}.bp3-control-group>.bp3-fill{flex-grow:1;flex-shrink:1}.bp3-control-group .bp3-button,.bp3-control-group .bp3-html-select,.bp3-control-group .bp3-input,.bp3-control-group .bp3-select{position:relative}.bp3-control-group .bp3-input{border-radius:inherit;z-index:2}.bp3-control-group .bp3-input:focus{border-radius:3px;z-index:14}.bp3-control-group .bp3-input[class*=bp3-intent]{z-index:13}.bp3-control-group .bp3-input[class*=bp3-intent]:focus{z-index:15}.bp3-control-group .bp3-input.bp3-disabled,.bp3-control-group .bp3-input:disabled,.bp3-control-group .bp3-input[readonly]{z-index:1}.bp3-control-group .bp3-input-group[class*=bp3-intent] .bp3-input{z-index:13}.bp3-control-group .bp3-input-group[class*=bp3-intent] .bp3-input:focus{z-index:15}.bp3-control-group .bp3-button,.bp3-control-group .bp3-html-select select,.bp3-control-group .bp3-select select{transform:translateZ(0);border-radius:inherit;z-index:4}.bp3-control-group .bp3-button:focus,.bp3-control-group .bp3-html-select select:focus,.bp3-control-group .bp3-select select:focus{z-index:5}.bp3-control-group .bp3-button:hover,.bp3-control-group .bp3-html-select select:hover,.bp3-control-group .bp3-select select:hover{z-index:6}.bp3-control-group .bp3-button:active,.bp3-control-group .bp3-html-select select:active,.bp3-control-group .bp3-select select:active{z-index:7}.bp3-control-group .bp3-button.bp3-disabled,.bp3-control-group .bp3-button:disabled,.bp3-control-group .bp3-button[readonly],.bp3-control-group .bp3-html-select select.bp3-disabled,.bp3-control-group .bp3-html-select select:disabled,.bp3-control-group .bp3-html-select select[readonly],.bp3-control-group .bp3-select select.bp3-disabled,.bp3-control-group .bp3-select select:disabled,.bp3-control-group .bp3-select select[readonly]{z-index:3}.bp3-control-group .bp3-button[class*=bp3-intent],.bp3-control-group .bp3-html-select select[class*=bp3-intent],.bp3-control-group .bp3-select select[class*=bp3-intent]{z-index:9}.bp3-control-group .bp3-button[class*=bp3-intent]:focus,.bp3-control-group .bp3-html-select select[class*=bp3-intent]:focus,.bp3-control-group .bp3-select select[class*=bp3-intent]:focus{z-index:10}.bp3-control-group .bp3-button[class*=bp3-intent]:hover,.bp3-control-group .bp3-html-select select[class*=bp3-intent]:hover,.bp3-control-group .bp3-select select[class*=bp3-intent]:hover{z-index:11}.bp3-control-group .bp3-button[class*=bp3-intent]:active,.bp3-control-group .bp3-html-select select[class*=bp3-intent]:active,.bp3-control-group .bp3-select select[class*=bp3-intent]:active{z-index:12}.bp3-control-group .bp3-button[class*=bp3-intent].bp3-disabled,.bp3-control-group .bp3-button[class*=bp3-intent]:disabled,.bp3-control-group .bp3-button[class*=bp3-intent][readonly],.bp3-control-group .bp3-html-select select[class*=bp3-intent].bp3-disabled,.bp3-control-group .bp3-html-select select[class*=bp3-intent]:disabled,.bp3-control-group .bp3-html-select select[class*=bp3-intent][readonly],.bp3-control-group .bp3-select select[class*=bp3-intent].bp3-disabled,.bp3-control-group .bp3-select select[class*=bp3-intent]:disabled,.bp3-control-group .bp3-select select[class*=bp3-intent][readonly]{z-index:8}.bp3-control-group .bp3-input-group>.bp3-button,.bp3-control-group .bp3-input-group>.bp3-icon,.bp3-control-group .bp3-input-group>.bp3-input-action{z-index:16}.bp3-control-group .bp3-html-select:after,.bp3-control-group .bp3-html-select>.bp3-icon,.bp3-control-group .bp3-select:after,.bp3-control-group .bp3-select>.bp3-icon{z-index:17}.bp3-control-group .bp3-select:focus-within{z-index:5}.bp3-control-group:not(.bp3-vertical)>:not(.bp3-divider){margin-right:-1px}.bp3-control-group:not(.bp3-vertical)>.bp3-divider:not(:first-child){margin-left:6px}.bp3-dark .bp3-control-group:not(.bp3-vertical)>:not(.bp3-divider){margin-right:0}.bp3-dark .bp3-control-group:not(.bp3-vertical)>.bp3-button+.bp3-button{margin-left:1px}.bp3-control-group .bp3-popover-target,.bp3-control-group .bp3-popover-wrapper{border-radius:inherit}.bp3-control-group>:first-child{border-radius:3px 0 0 3px}.bp3-control-group>:last-child{border-radius:0 3px 3px 0;margin-right:0}.bp3-control-group>:only-child{border-radius:3px;margin-right:0}.bp3-control-group .bp3-input-group .bp3-button{border-radius:3px}.bp3-control-group .bp3-numeric-input:not(:first-child) .bp3-input-group{border-bottom-left-radius:0;border-top-left-radius:0}.bp3-control-group.bp3-fill{width:100%}.bp3-control-group.bp3-fill>:not(.bp3-fixed),.bp3-control-group>.bp3-fill{flex:1 1 auto}.bp3-control-group.bp3-vertical{flex-direction:column}.bp3-control-group.bp3-vertical>*{margin-top:-1px}.bp3-control-group.bp3-vertical>:first-child{border-radius:3px 3px 0 0;margin-top:0}.bp3-control-group.bp3-vertical>:last-child{border-radius:0 0 3px 3px}.bp3-control{cursor:pointer;display:block;margin-bottom:10px;position:relative;text-transform:none}.bp3-control input:checked~.bp3-control-indicator{background-color:#137cbd;background-image:linear-gradient(180deg,hsla(0,0%,100%,.1),hsla(0,0%,100%,0));box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);color:#fff}.bp3-control:hover input:checked~.bp3-control-indicator{background-color:#106ba3;box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2)}.bp3-control input:not(:disabled):active:checked~.bp3-control-indicator{background:#0e5a8a;box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-control input:disabled:checked~.bp3-control-indicator{background:rgba(19,124,189,.5);box-shadow:none}.bp3-dark .bp3-control input:checked~.bp3-control-indicator{box-shadow:0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-control:hover input:checked~.bp3-control-indicator{background-color:#106ba3;box-shadow:0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-control input:not(:disabled):active:checked~.bp3-control-indicator{background-color:#0e5a8a;box-shadow:0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-dark .bp3-control input:disabled:checked~.bp3-control-indicator{background:rgba(14,90,138,.5);box-shadow:none}.bp3-control:not(.bp3-align-right){padding-left:26px}.bp3-control:not(.bp3-align-right) .bp3-control-indicator{margin-left:-26px}.bp3-control.bp3-align-right{padding-right:26px}.bp3-control.bp3-align-right .bp3-control-indicator{margin-right:-26px}.bp3-control.bp3-disabled{color:rgba(92,112,128,.6);cursor:not-allowed}.bp3-control.bp3-inline{display:inline-block;margin-right:20px}.bp3-control input{left:0;opacity:0;position:absolute;top:0;z-index:-1}.bp3-control .bp3-control-indicator{background-clip:padding-box;background-color:#f5f8fa;background-image:linear-gradient(180deg,hsla(0,0%,100%,.8),hsla(0,0%,100%,0));border:none;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);cursor:pointer;display:inline-block;font-size:16px;height:1em;margin-right:10px;margin-top:-3px;position:relative;-webkit-user-select:none;user-select:none;vertical-align:middle;width:1em}.bp3-control .bp3-control-indicator:before{content:"";display:block;height:1em;width:1em}.bp3-control:hover .bp3-control-indicator{background-color:#ebf1f5}.bp3-control input:not(:disabled):active~.bp3-control-indicator{background:#d8e1e8;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-control input:disabled~.bp3-control-indicator{background:rgba(206,217,224,.5);box-shadow:none;cursor:not-allowed}.bp3-control input:focus~.bp3-control-indicator{outline:2px auto rgba(19,124,189,.6);outline-offset:2px;-moz-outline-radius:6px}.bp3-control.bp3-align-right .bp3-control-indicator{float:right;margin-left:10px;margin-top:1px}.bp3-control.bp3-large{font-size:16px}.bp3-control.bp3-large:not(.bp3-align-right){padding-left:30px}.bp3-control.bp3-large:not(.bp3-align-right) .bp3-control-indicator{margin-left:-30px}.bp3-control.bp3-large.bp3-align-right{padding-right:30px}.bp3-control.bp3-large.bp3-align-right .bp3-control-indicator{margin-right:-30px}.bp3-control.bp3-large .bp3-control-indicator{font-size:20px}.bp3-control.bp3-large.bp3-align-right .bp3-control-indicator{margin-top:0}.bp3-control.bp3-checkbox input:indeterminate~.bp3-control-indicator{background-color:#137cbd;background-image:linear-gradient(180deg,hsla(0,0%,100%,.1),hsla(0,0%,100%,0));box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);color:#fff}.bp3-control.bp3-checkbox:hover input:indeterminate~.bp3-control-indicator{background-color:#106ba3;box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2)}.bp3-control.bp3-checkbox input:not(:disabled):active:indeterminate~.bp3-control-indicator{background:#0e5a8a;box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-control.bp3-checkbox input:disabled:indeterminate~.bp3-control-indicator{background:rgba(19,124,189,.5);box-shadow:none}.bp3-dark .bp3-control.bp3-checkbox input:indeterminate~.bp3-control-indicator{box-shadow:0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-control.bp3-checkbox:hover input:indeterminate~.bp3-control-indicator{background-color:#106ba3;box-shadow:0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-control.bp3-checkbox input:not(:disabled):active:indeterminate~.bp3-control-indicator{background-color:#0e5a8a;box-shadow:0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-dark .bp3-control.bp3-checkbox input:disabled:indeterminate~.bp3-control-indicator{background:rgba(14,90,138,.5);box-shadow:none}.bp3-control.bp3-checkbox .bp3-control-indicator{border-radius:3px}.bp3-control.bp3-checkbox input:checked~.bp3-control-indicator:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M12 5c-.28 0-.53.11-.71.29L7 9.59l-2.29-2.3a1.003 1.003 0 0 0-1.42 1.42l3 3c.18.18.43.29.71.29s.53-.11.71-.29l5-5A1.003 1.003 0 0 0 12 5z' fill='%23fff'/%3E%3C/svg%3E")}.bp3-control.bp3-checkbox input:indeterminate~.bp3-control-indicator:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M11 7H5c-.55 0-1 .45-1 1s.45 1 1 1h6c.55 0 1-.45 1-1s-.45-1-1-1z' fill='%23fff'/%3E%3C/svg%3E")}.bp3-control.bp3-radio .bp3-control-indicator{border-radius:50%}.bp3-control.bp3-radio input:checked~.bp3-control-indicator:before{background-image:radial-gradient(#fff,#fff 28%,transparent 32%)}.bp3-control.bp3-radio input:checked:disabled~.bp3-control-indicator:before{opacity:.5}.bp3-control.bp3-radio input:focus~.bp3-control-indicator{-moz-outline-radius:16px}.bp3-control.bp3-switch input~.bp3-control-indicator{background:rgba(167,182,194,.5)}.bp3-control.bp3-switch:hover input~.bp3-control-indicator{background:rgba(115,134,148,.5)}.bp3-control.bp3-switch input:not(:disabled):active~.bp3-control-indicator{background:rgba(92,112,128,.5)}.bp3-control.bp3-switch input:disabled~.bp3-control-indicator{background:rgba(206,217,224,.5)}.bp3-control.bp3-switch input:disabled~.bp3-control-indicator:before{background:hsla(0,0%,100%,.8)}.bp3-control.bp3-switch input:checked~.bp3-control-indicator{background:#137cbd}.bp3-control.bp3-switch:hover input:checked~.bp3-control-indicator{background:#106ba3}.bp3-control.bp3-switch input:checked:not(:disabled):active~.bp3-control-indicator{background:#0e5a8a}.bp3-control.bp3-switch input:checked:disabled~.bp3-control-indicator{background:rgba(19,124,189,.5)}.bp3-control.bp3-switch input:checked:disabled~.bp3-control-indicator:before{background:hsla(0,0%,100%,.8)}.bp3-control.bp3-switch:not(.bp3-align-right){padding-left:38px}.bp3-control.bp3-switch:not(.bp3-align-right) .bp3-control-indicator{margin-left:-38px}.bp3-control.bp3-switch.bp3-align-right{padding-right:38px}.bp3-control.bp3-switch.bp3-align-right .bp3-control-indicator{margin-right:-38px}.bp3-control.bp3-switch .bp3-control-indicator{border:none;border-radius:1.75em;box-shadow:none!important;min-width:1.75em;transition:background-color .1s cubic-bezier(.4,1,.75,.9);width:auto}.bp3-control.bp3-switch .bp3-control-indicator:before{background:#fff;border-radius:50%;box-shadow:0 0 0 1px rgba(16,22,26,.2),0 1px 1px rgba(16,22,26,.2);height:calc(1em - 4px);left:0;margin:2px;position:absolute;transition:left .1s cubic-bezier(.4,1,.75,.9);width:calc(1em - 4px)}.bp3-control.bp3-switch input:checked~.bp3-control-indicator:before{left:calc(100% - 1em)}.bp3-control.bp3-switch.bp3-large:not(.bp3-align-right){padding-left:45px}.bp3-control.bp3-switch.bp3-large:not(.bp3-align-right) .bp3-control-indicator{margin-left:-45px}.bp3-control.bp3-switch.bp3-large.bp3-align-right{padding-right:45px}.bp3-control.bp3-switch.bp3-large.bp3-align-right .bp3-control-indicator{margin-right:-45px}.bp3-dark .bp3-control.bp3-switch input~.bp3-control-indicator{background:rgba(16,22,26,.5)}.bp3-dark .bp3-control.bp3-switch:hover input~.bp3-control-indicator{background:rgba(16,22,26,.7)}.bp3-dark .bp3-control.bp3-switch input:not(:disabled):active~.bp3-control-indicator{background:rgba(16,22,26,.9)}.bp3-dark .bp3-control.bp3-switch input:disabled~.bp3-control-indicator{background:rgba(57,75,89,.5)}.bp3-dark .bp3-control.bp3-switch input:disabled~.bp3-control-indicator:before{background:rgba(16,22,26,.4)}.bp3-dark .bp3-control.bp3-switch input:checked~.bp3-control-indicator{background:#137cbd}.bp3-dark .bp3-control.bp3-switch:hover input:checked~.bp3-control-indicator{background:#106ba3}.bp3-dark .bp3-control.bp3-switch input:checked:not(:disabled):active~.bp3-control-indicator{background:#0e5a8a}.bp3-dark .bp3-control.bp3-switch input:checked:disabled~.bp3-control-indicator{background:rgba(14,90,138,.5)}.bp3-dark .bp3-control.bp3-switch input:checked:disabled~.bp3-control-indicator:before{background:rgba(16,22,26,.4)}.bp3-dark .bp3-control.bp3-switch .bp3-control-indicator:before{background:#394b59;box-shadow:0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-control.bp3-switch input:checked~.bp3-control-indicator:before{box-shadow:inset 0 0 0 1px rgba(16,22,26,.4)}.bp3-control.bp3-switch .bp3-switch-inner-text{font-size:.7em;text-align:center}.bp3-control.bp3-switch .bp3-control-indicator-child:first-child{line-height:0;margin-left:.5em;margin-right:1.2em;visibility:hidden}.bp3-control.bp3-switch .bp3-control-indicator-child:last-child{line-height:1em;margin-left:1.2em;margin-right:.5em;visibility:visible}.bp3-control.bp3-switch input:checked~.bp3-control-indicator .bp3-control-indicator-child:first-child{line-height:1em;visibility:visible}.bp3-control.bp3-switch input:checked~.bp3-control-indicator .bp3-control-indicator-child:last-child{line-height:0;visibility:hidden}.bp3-dark .bp3-control{color:#f5f8fa}.bp3-dark .bp3-control.bp3-disabled{color:rgba(167,182,194,.6)}.bp3-dark .bp3-control .bp3-control-indicator{background-color:#394b59;background-image:linear-gradient(180deg,hsla(0,0%,100%,.05),hsla(0,0%,100%,0));box-shadow:0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-control:hover .bp3-control-indicator{background-color:#30404d}.bp3-dark .bp3-control input:not(:disabled):active~.bp3-control-indicator{background:#202b33;box-shadow:0 0 0 1px rgba(16,22,26,.6),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-dark .bp3-control input:disabled~.bp3-control-indicator{background:rgba(57,75,89,.5);box-shadow:none;cursor:not-allowed}.bp3-dark .bp3-control.bp3-checkbox input:disabled:checked~.bp3-control-indicator,.bp3-dark .bp3-control.bp3-checkbox input:disabled:indeterminate~.bp3-control-indicator{color:rgba(167,182,194,.6)}.bp3-file-input{cursor:pointer;display:inline-block;height:30px;position:relative}.bp3-file-input input{margin:0;min-width:200px;opacity:0}.bp3-file-input input.bp3-disabled+.bp3-file-upload-input,.bp3-file-input input:disabled+.bp3-file-upload-input{background:rgba(206,217,224,.5);box-shadow:none;color:rgba(92,112,128,.6);cursor:not-allowed;resize:none}.bp3-file-input input.bp3-disabled+.bp3-file-upload-input:after,.bp3-file-input input:disabled+.bp3-file-upload-input:after{background-color:rgba(206,217,224,.5);background-image:none;box-shadow:none;color:rgba(92,112,128,.6);cursor:not-allowed;outline:none}.bp3-file-input input.bp3-disabled+.bp3-file-upload-input:after.bp3-active,.bp3-file-input input.bp3-disabled+.bp3-file-upload-input:after.bp3-active:hover,.bp3-file-input input:disabled+.bp3-file-upload-input:after.bp3-active,.bp3-file-input input:disabled+.bp3-file-upload-input:after.bp3-active:hover{background:rgba(206,217,224,.7)}.bp3-dark .bp3-file-input input.bp3-disabled+.bp3-file-upload-input,.bp3-dark .bp3-file-input input:disabled+.bp3-file-upload-input{background:rgba(57,75,89,.5);box-shadow:none;color:rgba(167,182,194,.6)}.bp3-dark .bp3-file-input input.bp3-disabled+.bp3-file-upload-input:after,.bp3-dark .bp3-file-input input:disabled+.bp3-file-upload-input:after{background-color:rgba(57,75,89,.5);background-image:none;box-shadow:none;color:rgba(167,182,194,.6)}.bp3-dark .bp3-file-input input.bp3-disabled+.bp3-file-upload-input:after.bp3-active,.bp3-dark .bp3-file-input input:disabled+.bp3-file-upload-input:after.bp3-active{background:rgba(57,75,89,.7)}.bp3-file-input.bp3-file-input-has-selection .bp3-file-upload-input{color:#182026}.bp3-dark .bp3-file-input.bp3-file-input-has-selection .bp3-file-upload-input{color:#f5f8fa}.bp3-file-input.bp3-fill{width:100%}.bp3-file-input.bp3-large,.bp3-large .bp3-file-input{height:40px}.bp3-file-input .bp3-file-upload-input-custom-text:after{content:attr(bp3-button-text)}.bp3-file-upload-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:#fff;border:none;border-radius:3px;box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2);color:#182026;font-size:14px;font-weight:400;height:30px;line-height:30px;outline:none;transition:box-shadow .1s cubic-bezier(.4,1,.75,.9);vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;color:rgba(92,112,128,.6);left:0;padding:0 80px 0 10px;position:absolute;right:0;top:0;-webkit-user-select:none;user-select:none}.bp3-file-upload-input::-webkit-input-placeholder{color:rgba(92,112,128,.6);opacity:1}.bp3-file-upload-input::placeholder{color:rgba(92,112,128,.6);opacity:1}.bp3-file-upload-input.bp3-active,.bp3-file-upload-input:focus{box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-file-upload-input.bp3-round,.bp3-file-upload-input[type=search]{border-radius:30px;box-sizing:border-box;padding-left:10px}.bp3-file-upload-input[readonly]{box-shadow:inset 0 0 0 1px rgba(16,22,26,.15)}.bp3-file-upload-input.bp3-disabled,.bp3-file-upload-input:disabled{background:rgba(206,217,224,.5);box-shadow:none;color:rgba(92,112,128,.6);cursor:not-allowed;resize:none}.bp3-file-upload-input:after{background-color:#f5f8fa;background-image:linear-gradient(180deg,hsla(0,0%,100%,.8),hsla(0,0%,100%,0));color:#182026;min-height:24px;min-width:24px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;border-radius:3px;content:"Browse";line-height:24px;margin:3px;position:absolute;right:0;text-align:center;top:0;width:70px}.bp3-file-upload-input:after:hover{background-clip:padding-box;background-color:#ebf1f5;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1)}.bp3-file-upload-input:after.bp3-active,.bp3-file-upload-input:after:active{background-color:#d8e1e8;background-image:none;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-file-upload-input:after.bp3-disabled,.bp3-file-upload-input:after:disabled{background-color:rgba(206,217,224,.5);background-image:none;box-shadow:none;color:rgba(92,112,128,.6);cursor:not-allowed;outline:none}.bp3-file-upload-input:after.bp3-disabled.bp3-active,.bp3-file-upload-input:after.bp3-disabled.bp3-active:hover,.bp3-file-upload-input:after:disabled.bp3-active,.bp3-file-upload-input:after:disabled.bp3-active:hover{background:rgba(206,217,224,.7)}.bp3-file-upload-input:hover:after{background-clip:padding-box;background-color:#ebf1f5;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1)}.bp3-file-upload-input:active:after{background-color:#d8e1e8;background-image:none;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-large .bp3-file-upload-input{font-size:16px;height:40px;line-height:40px;padding-right:95px}.bp3-large .bp3-file-upload-input.bp3-round,.bp3-large .bp3-file-upload-input[type=search]{padding:0 15px}.bp3-large .bp3-file-upload-input:after{min-height:30px;min-width:30px;line-height:30px;margin:5px;width:85px}.bp3-dark .bp3-file-upload-input{background:rgba(16,22,26,.3);box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);color:#f5f8fa;color:rgba(167,182,194,.6)}.bp3-dark .bp3-file-upload-input::-webkit-input-placeholder{color:rgba(167,182,194,.6)}.bp3-dark .bp3-file-upload-input::placeholder{color:rgba(167,182,194,.6)}.bp3-dark .bp3-file-upload-input:focus{box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-file-upload-input[readonly]{box-shadow:inset 0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-file-upload-input.bp3-disabled,.bp3-dark .bp3-file-upload-input:disabled{background:rgba(57,75,89,.5);box-shadow:none;color:rgba(167,182,194,.6)}.bp3-dark .bp3-file-upload-input:after{background-color:#394b59;background-image:linear-gradient(180deg,hsla(0,0%,100%,.05),hsla(0,0%,100%,0));box-shadow:0 0 0 1px rgba(16,22,26,.4);color:#f5f8fa}.bp3-dark .bp3-file-upload-input:after.bp3-active,.bp3-dark .bp3-file-upload-input:after:active,.bp3-dark .bp3-file-upload-input:after:hover{color:#f5f8fa}.bp3-dark .bp3-file-upload-input:after:hover{background-color:#30404d;box-shadow:0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-file-upload-input:after.bp3-active,.bp3-dark .bp3-file-upload-input:after:active{background-color:#202b33;background-image:none;box-shadow:0 0 0 1px rgba(16,22,26,.6),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-dark .bp3-file-upload-input:after.bp3-disabled,.bp3-dark .bp3-file-upload-input:after:disabled{background-color:rgba(57,75,89,.5);background-image:none;box-shadow:none;color:rgba(167,182,194,.6)}.bp3-dark .bp3-file-upload-input:after.bp3-disabled.bp3-active,.bp3-dark .bp3-file-upload-input:after:disabled.bp3-active{background:rgba(57,75,89,.7)}.bp3-dark .bp3-file-upload-input:after .bp3-button-spinner .bp3-spinner-head{background:rgba(16,22,26,.5);stroke:#8a9ba8}.bp3-dark .bp3-file-upload-input:hover:after{background-color:#30404d;box-shadow:0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-file-upload-input:active:after{background-color:#202b33;background-image:none;box-shadow:0 0 0 1px rgba(16,22,26,.6),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-file-upload-input:after{box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1)}.bp3-form-group{display:flex;flex-direction:column;margin:0 0 15px}.bp3-form-group label.bp3-label{margin-bottom:5px}.bp3-form-group .bp3-control{margin-top:7px}.bp3-form-group .bp3-form-helper-text{color:#5c7080;font-size:12px;margin-top:5px}.bp3-form-group.bp3-intent-primary .bp3-form-helper-text{color:#106ba3}.bp3-form-group.bp3-intent-success .bp3-form-helper-text{color:#0d8050}.bp3-form-group.bp3-intent-warning .bp3-form-helper-text{color:#bf7326}.bp3-form-group.bp3-intent-danger .bp3-form-helper-text{color:#c23030}.bp3-form-group.bp3-inline{align-items:flex-start;flex-direction:row}.bp3-form-group.bp3-inline.bp3-large label.bp3-label{line-height:40px;margin:0 10px 0 0}.bp3-form-group.bp3-inline label.bp3-label{line-height:30px;margin:0 10px 0 0}.bp3-form-group.bp3-disabled .bp3-form-helper-text,.bp3-form-group.bp3-disabled .bp3-label,.bp3-form-group.bp3-disabled .bp3-text-muted{color:rgba(92,112,128,.6)!important}.bp3-dark .bp3-form-group.bp3-intent-primary .bp3-form-helper-text{color:#48aff0}.bp3-dark .bp3-form-group.bp3-intent-success .bp3-form-helper-text{color:#3dcc91}.bp3-dark .bp3-form-group.bp3-intent-warning .bp3-form-helper-text{color:#ffb366}.bp3-dark .bp3-form-group.bp3-intent-danger .bp3-form-helper-text{color:#ff7373}.bp3-dark .bp3-form-group .bp3-form-helper-text{color:#a7b6c2}.bp3-dark .bp3-form-group.bp3-disabled .bp3-form-helper-text,.bp3-dark .bp3-form-group.bp3-disabled .bp3-label,.bp3-dark .bp3-form-group.bp3-disabled .bp3-text-muted{color:rgba(167,182,194,.6)!important}.bp3-input-group{display:block;position:relative}.bp3-input-group .bp3-input{position:relative;width:100%}.bp3-input-group .bp3-input:not(:first-child){padding-left:30px}.bp3-input-group .bp3-input:not(:last-child){padding-right:30px}.bp3-input-group .bp3-input-action,.bp3-input-group>.bp3-button,.bp3-input-group>.bp3-icon,.bp3-input-group>.bp3-input-left-container{position:absolute;top:0}.bp3-input-group .bp3-input-action:first-child,.bp3-input-group>.bp3-button:first-child,.bp3-input-group>.bp3-icon:first-child,.bp3-input-group>.bp3-input-left-container:first-child{left:0}.bp3-input-group .bp3-input-action:last-child,.bp3-input-group>.bp3-button:last-child,.bp3-input-group>.bp3-icon:last-child,.bp3-input-group>.bp3-input-left-container:last-child{right:0}.bp3-input-group .bp3-button{min-height:24px;min-width:24px;margin:3px;padding:0 7px}.bp3-input-group .bp3-button:empty{padding:0}.bp3-input-group>.bp3-icon,.bp3-input-group>.bp3-input-left-container{z-index:1}.bp3-input-group>.bp3-icon,.bp3-input-group>.bp3-input-left-container>.bp3-icon{color:#5c7080}.bp3-input-group>.bp3-icon:empty,.bp3-input-group>.bp3-input-left-container>.bp3-icon:empty{font-family:Icons16,sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}.bp3-input-group .bp3-input-action>.bp3-spinner,.bp3-input-group>.bp3-icon,.bp3-input-group>.bp3-input-left-container>.bp3-icon{margin:7px}.bp3-input-group .bp3-tag{margin:5px}.bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:not(:hover):not(:focus),.bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:not(:hover):not(:focus){color:#5c7080}.bp3-dark .bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:not(:hover):not(:focus),.bp3-dark .bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:not(:hover):not(:focus){color:#a7b6c2}.bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:not(:hover):not(:focus) .bp3-icon,.bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:not(:hover):not(:focus) .bp3-icon-large,.bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:not(:hover):not(:focus) .bp3-icon-standard,.bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:not(:hover):not(:focus) .bp3-icon,.bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:not(:hover):not(:focus) .bp3-icon-large,.bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:not(:hover):not(:focus) .bp3-icon-standard{color:#5c7080}.bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:disabled,.bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:disabled .bp3-icon,.bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:disabled .bp3-icon-large,.bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:disabled .bp3-icon-standard,.bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:disabled,.bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:disabled .bp3-icon,.bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:disabled .bp3-icon-large,.bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:disabled .bp3-icon-standard{color:rgba(92,112,128,.6)!important}.bp3-input-group.bp3-disabled{cursor:not-allowed}.bp3-input-group.bp3-disabled .bp3-icon{color:rgba(92,112,128,.6)}.bp3-input-group.bp3-large .bp3-button{min-height:30px;min-width:30px;margin:5px}.bp3-input-group.bp3-large .bp3-input-action>.bp3-spinner,.bp3-input-group.bp3-large>.bp3-icon,.bp3-input-group.bp3-large>.bp3-input-left-container>.bp3-icon{margin:12px}.bp3-input-group.bp3-large .bp3-input{font-size:16px;height:40px;line-height:40px}.bp3-input-group.bp3-large .bp3-input.bp3-round,.bp3-input-group.bp3-large .bp3-input[type=search]{padding:0 15px}.bp3-input-group.bp3-large .bp3-input:not(:first-child){padding-left:40px}.bp3-input-group.bp3-large .bp3-input:not(:last-child){padding-right:40px}.bp3-input-group.bp3-small .bp3-button,.bp3-input-group.bp3-small .bp3-tag{min-height:20px;min-width:20px;margin:2px}.bp3-input-group.bp3-small .bp3-input-action>.bp3-spinner,.bp3-input-group.bp3-small>.bp3-icon,.bp3-input-group.bp3-small>.bp3-input-left-container>.bp3-icon{margin:4px}.bp3-input-group.bp3-small .bp3-input{font-size:12px;height:24px;line-height:24px;padding-left:8px;padding-right:8px}.bp3-input-group.bp3-small .bp3-input.bp3-round,.bp3-input-group.bp3-small .bp3-input[type=search]{padding:0 12px}.bp3-input-group.bp3-small .bp3-input:not(:first-child){padding-left:24px}.bp3-input-group.bp3-small .bp3-input:not(:last-child){padding-right:24px}.bp3-input-group.bp3-fill{flex:1 1 auto;width:100%}.bp3-input-group.bp3-round .bp3-button,.bp3-input-group.bp3-round .bp3-input,.bp3-input-group.bp3-round .bp3-tag{border-radius:30px}.bp3-dark .bp3-input-group .bp3-icon{color:#a7b6c2}.bp3-dark .bp3-input-group.bp3-disabled .bp3-icon{color:rgba(167,182,194,.6)}.bp3-input-group.bp3-intent-primary .bp3-input{box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px #137cbd,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input-group.bp3-intent-primary .bp3-input:focus{box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input-group.bp3-intent-primary .bp3-input[readonly]{box-shadow:inset 0 0 0 1px #137cbd}.bp3-input-group.bp3-intent-primary .bp3-input.bp3-disabled,.bp3-input-group.bp3-intent-primary .bp3-input:disabled{box-shadow:none}.bp3-input-group.bp3-intent-primary>.bp3-icon{color:#106ba3}.bp3-dark .bp3-input-group.bp3-intent-primary>.bp3-icon{color:#48aff0}.bp3-input-group.bp3-intent-success .bp3-input{box-shadow:0 0 0 0 rgba(15,153,96,0),0 0 0 0 rgba(15,153,96,0),inset 0 0 0 1px #0f9960,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input-group.bp3-intent-success .bp3-input:focus{box-shadow:0 0 0 1px #0f9960,0 0 0 3px rgba(15,153,96,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input-group.bp3-intent-success .bp3-input[readonly]{box-shadow:inset 0 0 0 1px #0f9960}.bp3-input-group.bp3-intent-success .bp3-input.bp3-disabled,.bp3-input-group.bp3-intent-success .bp3-input:disabled{box-shadow:none}.bp3-input-group.bp3-intent-success>.bp3-icon{color:#0d8050}.bp3-dark .bp3-input-group.bp3-intent-success>.bp3-icon{color:#3dcc91}.bp3-input-group.bp3-intent-warning .bp3-input{box-shadow:0 0 0 0 rgba(217,130,43,0),0 0 0 0 rgba(217,130,43,0),inset 0 0 0 1px #d9822b,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input-group.bp3-intent-warning .bp3-input:focus{box-shadow:0 0 0 1px #d9822b,0 0 0 3px rgba(217,130,43,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input-group.bp3-intent-warning .bp3-input[readonly]{box-shadow:inset 0 0 0 1px #d9822b}.bp3-input-group.bp3-intent-warning .bp3-input.bp3-disabled,.bp3-input-group.bp3-intent-warning .bp3-input:disabled{box-shadow:none}.bp3-input-group.bp3-intent-warning>.bp3-icon{color:#bf7326}.bp3-dark .bp3-input-group.bp3-intent-warning>.bp3-icon{color:#ffb366}.bp3-input-group.bp3-intent-danger .bp3-input{box-shadow:0 0 0 0 rgba(219,55,55,0),0 0 0 0 rgba(219,55,55,0),inset 0 0 0 1px #db3737,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input-group.bp3-intent-danger .bp3-input:focus{box-shadow:0 0 0 1px #db3737,0 0 0 3px rgba(219,55,55,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input-group.bp3-intent-danger .bp3-input[readonly]{box-shadow:inset 0 0 0 1px #db3737}.bp3-input-group.bp3-intent-danger .bp3-input.bp3-disabled,.bp3-input-group.bp3-intent-danger .bp3-input:disabled{box-shadow:none}.bp3-input-group.bp3-intent-danger>.bp3-icon{color:#c23030}.bp3-dark .bp3-input-group.bp3-intent-danger>.bp3-icon{color:#ff7373}.bp3-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:#fff;border:none;border-radius:3px;box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2);color:#182026;font-size:14px;font-weight:400;height:30px;line-height:30px;outline:none;padding:0 10px;transition:box-shadow .1s cubic-bezier(.4,1,.75,.9);vertical-align:middle}.bp3-input::-webkit-input-placeholder{color:rgba(92,112,128,.6);opacity:1}.bp3-input::placeholder{color:rgba(92,112,128,.6);opacity:1}.bp3-input.bp3-active,.bp3-input:focus{box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input.bp3-round,.bp3-input[type=search]{border-radius:30px;box-sizing:border-box;padding-left:10px}.bp3-input[readonly]{box-shadow:inset 0 0 0 1px rgba(16,22,26,.15)}.bp3-input.bp3-disabled,.bp3-input:disabled{background:rgba(206,217,224,.5);box-shadow:none;color:rgba(92,112,128,.6);cursor:not-allowed;resize:none}.bp3-input.bp3-large{font-size:16px;height:40px;line-height:40px}.bp3-input.bp3-large.bp3-round,.bp3-input.bp3-large[type=search]{padding:0 15px}.bp3-input.bp3-small{font-size:12px;height:24px;line-height:24px;padding-left:8px;padding-right:8px}.bp3-input.bp3-small.bp3-round,.bp3-input.bp3-small[type=search]{padding:0 12px}.bp3-input.bp3-fill{flex:1 1 auto;width:100%}.bp3-dark .bp3-input{background:rgba(16,22,26,.3);box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);color:#f5f8fa}.bp3-dark .bp3-input::-webkit-input-placeholder{color:rgba(167,182,194,.6)}.bp3-dark .bp3-input::placeholder{color:rgba(167,182,194,.6)}.bp3-dark .bp3-input:focus{box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-input[readonly]{box-shadow:inset 0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-input.bp3-disabled,.bp3-dark .bp3-input:disabled{background:rgba(57,75,89,.5);box-shadow:none;color:rgba(167,182,194,.6)}.bp3-input.bp3-intent-primary{box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px #137cbd,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input.bp3-intent-primary:focus{box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input.bp3-intent-primary[readonly]{box-shadow:inset 0 0 0 1px #137cbd}.bp3-input.bp3-intent-primary.bp3-disabled,.bp3-input.bp3-intent-primary:disabled{box-shadow:none}.bp3-dark .bp3-input.bp3-intent-primary{box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px #137cbd,inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-input.bp3-intent-primary:focus{box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-input.bp3-intent-primary[readonly]{box-shadow:inset 0 0 0 1px #137cbd}.bp3-dark .bp3-input.bp3-intent-primary.bp3-disabled,.bp3-dark .bp3-input.bp3-intent-primary:disabled{box-shadow:none}.bp3-input.bp3-intent-success{box-shadow:0 0 0 0 rgba(15,153,96,0),0 0 0 0 rgba(15,153,96,0),inset 0 0 0 1px #0f9960,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input.bp3-intent-success:focus{box-shadow:0 0 0 1px #0f9960,0 0 0 3px rgba(15,153,96,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input.bp3-intent-success[readonly]{box-shadow:inset 0 0 0 1px #0f9960}.bp3-input.bp3-intent-success.bp3-disabled,.bp3-input.bp3-intent-success:disabled{box-shadow:none}.bp3-dark .bp3-input.bp3-intent-success{box-shadow:0 0 0 0 rgba(15,153,96,0),0 0 0 0 rgba(15,153,96,0),0 0 0 0 rgba(15,153,96,0),inset 0 0 0 1px #0f9960,inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-input.bp3-intent-success:focus{box-shadow:0 0 0 1px #0f9960,0 0 0 1px #0f9960,0 0 0 3px rgba(15,153,96,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-input.bp3-intent-success[readonly]{box-shadow:inset 0 0 0 1px #0f9960}.bp3-dark .bp3-input.bp3-intent-success.bp3-disabled,.bp3-dark .bp3-input.bp3-intent-success:disabled{box-shadow:none}.bp3-input.bp3-intent-warning{box-shadow:0 0 0 0 rgba(217,130,43,0),0 0 0 0 rgba(217,130,43,0),inset 0 0 0 1px #d9822b,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input.bp3-intent-warning:focus{box-shadow:0 0 0 1px #d9822b,0 0 0 3px rgba(217,130,43,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input.bp3-intent-warning[readonly]{box-shadow:inset 0 0 0 1px #d9822b}.bp3-input.bp3-intent-warning.bp3-disabled,.bp3-input.bp3-intent-warning:disabled{box-shadow:none}.bp3-dark .bp3-input.bp3-intent-warning{box-shadow:0 0 0 0 rgba(217,130,43,0),0 0 0 0 rgba(217,130,43,0),0 0 0 0 rgba(217,130,43,0),inset 0 0 0 1px #d9822b,inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-input.bp3-intent-warning:focus{box-shadow:0 0 0 1px #d9822b,0 0 0 1px #d9822b,0 0 0 3px rgba(217,130,43,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-input.bp3-intent-warning[readonly]{box-shadow:inset 0 0 0 1px #d9822b}.bp3-dark .bp3-input.bp3-intent-warning.bp3-disabled,.bp3-dark .bp3-input.bp3-intent-warning:disabled{box-shadow:none}.bp3-input.bp3-intent-danger{box-shadow:0 0 0 0 rgba(219,55,55,0),0 0 0 0 rgba(219,55,55,0),inset 0 0 0 1px #db3737,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input.bp3-intent-danger:focus{box-shadow:0 0 0 1px #db3737,0 0 0 3px rgba(219,55,55,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-input.bp3-intent-danger[readonly]{box-shadow:inset 0 0 0 1px #db3737}.bp3-input.bp3-intent-danger.bp3-disabled,.bp3-input.bp3-intent-danger:disabled{box-shadow:none}.bp3-dark .bp3-input.bp3-intent-danger{box-shadow:0 0 0 0 rgba(219,55,55,0),0 0 0 0 rgba(219,55,55,0),0 0 0 0 rgba(219,55,55,0),inset 0 0 0 1px #db3737,inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-input.bp3-intent-danger:focus{box-shadow:0 0 0 1px #db3737,0 0 0 1px #db3737,0 0 0 3px rgba(219,55,55,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-input.bp3-intent-danger[readonly]{box-shadow:inset 0 0 0 1px #db3737}.bp3-dark .bp3-input.bp3-intent-danger.bp3-disabled,.bp3-dark .bp3-input.bp3-intent-danger:disabled{box-shadow:none}.bp3-input::-ms-clear{display:none}textarea.bp3-input{max-width:100%;padding:10px}textarea.bp3-input,textarea.bp3-input.bp3-large,textarea.bp3-input.bp3-small{height:auto;line-height:inherit}textarea.bp3-input.bp3-small{padding:8px}.bp3-dark textarea.bp3-input{background:rgba(16,22,26,.3);box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);color:#f5f8fa}.bp3-dark textarea.bp3-input::-webkit-input-placeholder{color:rgba(167,182,194,.6)}.bp3-dark textarea.bp3-input::placeholder{color:rgba(167,182,194,.6)}.bp3-dark textarea.bp3-input:focus{box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark textarea.bp3-input[readonly]{box-shadow:inset 0 0 0 1px rgba(16,22,26,.4)}.bp3-dark textarea.bp3-input.bp3-disabled,.bp3-dark textarea.bp3-input:disabled{background:rgba(57,75,89,.5);box-shadow:none;color:rgba(167,182,194,.6)}label.bp3-label{display:block;margin-bottom:15px;margin-top:0}label.bp3-label .bp3-html-select,label.bp3-label .bp3-input,label.bp3-label .bp3-popover-wrapper,label.bp3-label .bp3-select,label.bp3-label .bp3-slider{display:block;margin-top:5px;text-transform:none}label.bp3-label .bp3-button-group{margin-top:5px}label.bp3-label .bp3-html-select select,label.bp3-label .bp3-select select{font-weight:400;vertical-align:top;width:100%}label.bp3-label.bp3-disabled,label.bp3-label.bp3-disabled .bp3-text-muted{color:rgba(92,112,128,.6)}label.bp3-label.bp3-inline{line-height:30px}label.bp3-label.bp3-inline .bp3-html-select,label.bp3-label.bp3-inline .bp3-input,label.bp3-label.bp3-inline .bp3-input-group,label.bp3-label.bp3-inline .bp3-popover-wrapper,label.bp3-label.bp3-inline .bp3-select{display:inline-block;margin:0 0 0 5px;vertical-align:top}label.bp3-label.bp3-inline .bp3-button-group{margin:0 0 0 5px}label.bp3-label.bp3-inline .bp3-input-group .bp3-input{margin-left:0}label.bp3-label.bp3-inline.bp3-large{line-height:40px}label.bp3-label:not(.bp3-inline) .bp3-popover-target{display:block}.bp3-dark label.bp3-label{color:#f5f8fa}.bp3-dark label.bp3-label.bp3-disabled,.bp3-dark label.bp3-label.bp3-disabled .bp3-text-muted{color:rgba(167,182,194,.6)}.bp3-numeric-input .bp3-button-group.bp3-vertical>.bp3-button{flex:1 1 14px;min-height:0;padding:0;width:30px}.bp3-numeric-input .bp3-button-group.bp3-vertical>.bp3-button:first-child{border-radius:0 3px 0 0}.bp3-numeric-input .bp3-button-group.bp3-vertical>.bp3-button:last-child{border-radius:0 0 3px 0}.bp3-numeric-input .bp3-button-group.bp3-vertical:first-child>.bp3-button:first-child{border-radius:3px 0 0 0}.bp3-numeric-input .bp3-button-group.bp3-vertical:first-child>.bp3-button:last-child{border-radius:0 0 0 3px}.bp3-numeric-input.bp3-large .bp3-button-group.bp3-vertical>.bp3-button{width:40px}form{display:block}.bp3-html-select select,.bp3-select select{display:inline-flex;flex-direction:row;align-items:center;border:none;cursor:pointer;font-size:14px;justify-content:center;text-align:left;vertical-align:middle;background-color:#f5f8fa;background-image:linear-gradient(180deg,hsla(0,0%,100%,.8),hsla(0,0%,100%,0));box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);color:#182026;-moz-appearance:none;-webkit-appearance:none;border-radius:3px;height:30px;padding:0 25px 0 10px;width:100%}.bp3-html-select select>*,.bp3-select select>*{flex-grow:0;flex-shrink:0}.bp3-html-select select>.bp3-fill,.bp3-select select>.bp3-fill{flex-grow:1;flex-shrink:1}.bp3-html-select select:before,.bp3-html-select select>*,.bp3-select select:before,.bp3-select select>*{margin-right:7px}.bp3-html-select select:empty:before,.bp3-html-select select>:last-child,.bp3-select select:empty:before,.bp3-select select>:last-child{margin-right:0}.bp3-html-select select:hover,.bp3-select select:hover{background-clip:padding-box;background-color:#ebf1f5;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1)}.bp3-html-select select.bp3-active,.bp3-html-select select:active,.bp3-select select.bp3-active,.bp3-select select:active{background-color:#d8e1e8;background-image:none;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-html-select select.bp3-disabled,.bp3-html-select select:disabled,.bp3-select select.bp3-disabled,.bp3-select select:disabled{background-color:rgba(206,217,224,.5);background-image:none;box-shadow:none;color:rgba(92,112,128,.6);cursor:not-allowed;outline:none}.bp3-html-select select.bp3-disabled.bp3-active,.bp3-html-select select.bp3-disabled.bp3-active:hover,.bp3-html-select select:disabled.bp3-active,.bp3-html-select select:disabled.bp3-active:hover,.bp3-select select.bp3-disabled.bp3-active,.bp3-select select.bp3-disabled.bp3-active:hover,.bp3-select select:disabled.bp3-active,.bp3-select select:disabled.bp3-active:hover{background:rgba(206,217,224,.7)}.bp3-html-select.bp3-minimal select,.bp3-select.bp3-minimal select{background:none;box-shadow:none}.bp3-html-select.bp3-minimal select:hover,.bp3-select.bp3-minimal select:hover{background:rgba(167,182,194,.3);box-shadow:none;color:#182026;text-decoration:none}.bp3-html-select.bp3-minimal select.bp3-active,.bp3-html-select.bp3-minimal select:active,.bp3-select.bp3-minimal select.bp3-active,.bp3-select.bp3-minimal select:active{background:rgba(115,134,148,.3);box-shadow:none;color:#182026}.bp3-html-select.bp3-minimal select.bp3-disabled,.bp3-html-select.bp3-minimal select.bp3-disabled:hover,.bp3-html-select.bp3-minimal select:disabled,.bp3-html-select.bp3-minimal select:disabled:hover,.bp3-select.bp3-minimal select.bp3-disabled,.bp3-select.bp3-minimal select.bp3-disabled:hover,.bp3-select.bp3-minimal select:disabled,.bp3-select.bp3-minimal select:disabled:hover{background:none;color:rgba(92,112,128,.6);cursor:not-allowed}.bp3-html-select.bp3-minimal select.bp3-disabled.bp3-active,.bp3-html-select.bp3-minimal select.bp3-disabled:hover.bp3-active,.bp3-html-select.bp3-minimal select:disabled.bp3-active,.bp3-html-select.bp3-minimal select:disabled:hover.bp3-active,.bp3-select.bp3-minimal select.bp3-disabled.bp3-active,.bp3-select.bp3-minimal select.bp3-disabled:hover.bp3-active,.bp3-select.bp3-minimal select:disabled.bp3-active,.bp3-select.bp3-minimal select:disabled:hover.bp3-active{background:rgba(115,134,148,.3)}.bp3-dark .bp3-html-select.bp3-minimal select,.bp3-dark .bp3-select.bp3-minimal select,.bp3-html-select.bp3-minimal .bp3-dark select,.bp3-select.bp3-minimal .bp3-dark select{background:none;box-shadow:none;color:inherit}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select:active,.bp3-dark .bp3-html-select.bp3-minimal select:hover,.bp3-dark .bp3-select.bp3-minimal select.bp3-active,.bp3-dark .bp3-select.bp3-minimal select:active,.bp3-dark .bp3-select.bp3-minimal select:hover,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select:active,.bp3-html-select.bp3-minimal .bp3-dark select:hover,.bp3-select.bp3-minimal .bp3-dark select.bp3-active,.bp3-select.bp3-minimal .bp3-dark select:active,.bp3-select.bp3-minimal .bp3-dark select:hover{background:none;box-shadow:none}.bp3-dark .bp3-html-select.bp3-minimal select:hover,.bp3-dark .bp3-select.bp3-minimal select:hover,.bp3-html-select.bp3-minimal .bp3-dark select:hover,.bp3-select.bp3-minimal .bp3-dark select:hover{background:rgba(138,155,168,.15)}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select:active,.bp3-dark .bp3-select.bp3-minimal select.bp3-active,.bp3-dark .bp3-select.bp3-minimal select:active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select:active,.bp3-select.bp3-minimal .bp3-dark select.bp3-active,.bp3-select.bp3-minimal .bp3-dark select:active{background:rgba(138,155,168,.3);color:#f5f8fa}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-disabled,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-disabled:hover,.bp3-dark .bp3-html-select.bp3-minimal select:disabled,.bp3-dark .bp3-html-select.bp3-minimal select:disabled:hover,.bp3-dark .bp3-select.bp3-minimal select.bp3-disabled,.bp3-dark .bp3-select.bp3-minimal select.bp3-disabled:hover,.bp3-dark .bp3-select.bp3-minimal select:disabled,.bp3-dark .bp3-select.bp3-minimal select:disabled:hover,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-disabled,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-disabled:hover,.bp3-html-select.bp3-minimal .bp3-dark select:disabled,.bp3-html-select.bp3-minimal .bp3-dark select:disabled:hover,.bp3-select.bp3-minimal .bp3-dark select.bp3-disabled,.bp3-select.bp3-minimal .bp3-dark select.bp3-disabled:hover,.bp3-select.bp3-minimal .bp3-dark select:disabled,.bp3-select.bp3-minimal .bp3-dark select:disabled:hover{background:none;color:rgba(167,182,194,.6);cursor:not-allowed}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-disabled.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-disabled:hover.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select:disabled.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select:disabled:hover.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-disabled.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-disabled:hover.bp3-active,.bp3-dark .bp3-select.bp3-minimal select:disabled.bp3-active,.bp3-dark .bp3-select.bp3-minimal select:disabled:hover.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-disabled.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-disabled:hover.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select:disabled.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select:disabled:hover.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-disabled.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-disabled:hover.bp3-active,.bp3-select.bp3-minimal .bp3-dark select:disabled.bp3-active,.bp3-select.bp3-minimal .bp3-dark select:disabled:hover.bp3-active{background:rgba(138,155,168,.3)}.bp3-html-select.bp3-minimal select.bp3-intent-primary,.bp3-select.bp3-minimal select.bp3-intent-primary{color:#106ba3}.bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-active,.bp3-html-select.bp3-minimal select.bp3-intent-primary:active,.bp3-html-select.bp3-minimal select.bp3-intent-primary:hover,.bp3-select.bp3-minimal select.bp3-intent-primary.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-primary:active,.bp3-select.bp3-minimal select.bp3-intent-primary:hover{background:none;box-shadow:none;color:#106ba3}.bp3-html-select.bp3-minimal select.bp3-intent-primary:hover,.bp3-select.bp3-minimal select.bp3-intent-primary:hover{background:rgba(19,124,189,.15);color:#106ba3}.bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-active,.bp3-html-select.bp3-minimal select.bp3-intent-primary:active,.bp3-select.bp3-minimal select.bp3-intent-primary.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-primary:active{background:rgba(19,124,189,.3);color:#106ba3}.bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-disabled,.bp3-html-select.bp3-minimal select.bp3-intent-primary:disabled,.bp3-select.bp3-minimal select.bp3-intent-primary.bp3-disabled,.bp3-select.bp3-minimal select.bp3-intent-primary:disabled{background:none;color:rgba(16,107,163,.5)}.bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-disabled.bp3-active,.bp3-html-select.bp3-minimal select.bp3-intent-primary:disabled.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-primary.bp3-disabled.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-primary:disabled.bp3-active{background:rgba(19,124,189,.3)}.bp3-html-select.bp3-minimal select.bp3-intent-primary .bp3-button-spinner .bp3-spinner-head,.bp3-select.bp3-minimal select.bp3-intent-primary .bp3-button-spinner .bp3-spinner-head{stroke:#106ba3}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary{color:#48aff0}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary:hover,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary:hover,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary:hover,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary:hover{background:rgba(19,124,189,.2);color:#48aff0}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary:active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary:active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary:active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary:active{background:rgba(19,124,189,.3);color:#48aff0}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-disabled,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary:disabled,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary.bp3-disabled,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary:disabled,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary.bp3-disabled,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary:disabled,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary.bp3-disabled,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary:disabled{background:none;color:rgba(72,175,240,.5)}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-disabled.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary:disabled.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary.bp3-disabled.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary:disabled.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary.bp3-disabled.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary:disabled.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary.bp3-disabled.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary:disabled.bp3-active{background:rgba(19,124,189,.3)}.bp3-html-select.bp3-minimal select.bp3-intent-success,.bp3-select.bp3-minimal select.bp3-intent-success{color:#0d8050}.bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-active,.bp3-html-select.bp3-minimal select.bp3-intent-success:active,.bp3-html-select.bp3-minimal select.bp3-intent-success:hover,.bp3-select.bp3-minimal select.bp3-intent-success.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-success:active,.bp3-select.bp3-minimal select.bp3-intent-success:hover{background:none;box-shadow:none;color:#0d8050}.bp3-html-select.bp3-minimal select.bp3-intent-success:hover,.bp3-select.bp3-minimal select.bp3-intent-success:hover{background:rgba(15,153,96,.15);color:#0d8050}.bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-active,.bp3-html-select.bp3-minimal select.bp3-intent-success:active,.bp3-select.bp3-minimal select.bp3-intent-success.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-success:active{background:rgba(15,153,96,.3);color:#0d8050}.bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-disabled,.bp3-html-select.bp3-minimal select.bp3-intent-success:disabled,.bp3-select.bp3-minimal select.bp3-intent-success.bp3-disabled,.bp3-select.bp3-minimal select.bp3-intent-success:disabled{background:none;color:rgba(13,128,80,.5)}.bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-disabled.bp3-active,.bp3-html-select.bp3-minimal select.bp3-intent-success:disabled.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-success.bp3-disabled.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-success:disabled.bp3-active{background:rgba(15,153,96,.3)}.bp3-html-select.bp3-minimal select.bp3-intent-success .bp3-button-spinner .bp3-spinner-head,.bp3-select.bp3-minimal select.bp3-intent-success .bp3-button-spinner .bp3-spinner-head{stroke:#0d8050}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success{color:#3dcc91}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success:hover,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success:hover,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success:hover,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success:hover{background:rgba(15,153,96,.2);color:#3dcc91}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success:active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success:active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success:active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success:active{background:rgba(15,153,96,.3);color:#3dcc91}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-disabled,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success:disabled,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success.bp3-disabled,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success:disabled,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success.bp3-disabled,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success:disabled,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success.bp3-disabled,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success:disabled{background:none;color:rgba(61,204,145,.5)}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-disabled.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success:disabled.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success.bp3-disabled.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success:disabled.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success.bp3-disabled.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success:disabled.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success.bp3-disabled.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success:disabled.bp3-active{background:rgba(15,153,96,.3)}.bp3-html-select.bp3-minimal select.bp3-intent-warning,.bp3-select.bp3-minimal select.bp3-intent-warning{color:#bf7326}.bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-active,.bp3-html-select.bp3-minimal select.bp3-intent-warning:active,.bp3-html-select.bp3-minimal select.bp3-intent-warning:hover,.bp3-select.bp3-minimal select.bp3-intent-warning.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-warning:active,.bp3-select.bp3-minimal select.bp3-intent-warning:hover{background:none;box-shadow:none;color:#bf7326}.bp3-html-select.bp3-minimal select.bp3-intent-warning:hover,.bp3-select.bp3-minimal select.bp3-intent-warning:hover{background:rgba(217,130,43,.15);color:#bf7326}.bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-active,.bp3-html-select.bp3-minimal select.bp3-intent-warning:active,.bp3-select.bp3-minimal select.bp3-intent-warning.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-warning:active{background:rgba(217,130,43,.3);color:#bf7326}.bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-disabled,.bp3-html-select.bp3-minimal select.bp3-intent-warning:disabled,.bp3-select.bp3-minimal select.bp3-intent-warning.bp3-disabled,.bp3-select.bp3-minimal select.bp3-intent-warning:disabled{background:none;color:rgba(191,115,38,.5)}.bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-disabled.bp3-active,.bp3-html-select.bp3-minimal select.bp3-intent-warning:disabled.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-warning.bp3-disabled.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-warning:disabled.bp3-active{background:rgba(217,130,43,.3)}.bp3-html-select.bp3-minimal select.bp3-intent-warning .bp3-button-spinner .bp3-spinner-head,.bp3-select.bp3-minimal select.bp3-intent-warning .bp3-button-spinner .bp3-spinner-head{stroke:#bf7326}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning{color:#ffb366}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning:hover,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning:hover,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning:hover,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning:hover{background:rgba(217,130,43,.2);color:#ffb366}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning:active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning:active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning:active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning:active{background:rgba(217,130,43,.3);color:#ffb366}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-disabled,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning:disabled,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning.bp3-disabled,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning:disabled,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning.bp3-disabled,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning:disabled,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning.bp3-disabled,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning:disabled{background:none;color:rgba(255,179,102,.5)}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-disabled.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning:disabled.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning.bp3-disabled.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning:disabled.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning.bp3-disabled.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning:disabled.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning.bp3-disabled.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning:disabled.bp3-active{background:rgba(217,130,43,.3)}.bp3-html-select.bp3-minimal select.bp3-intent-danger,.bp3-select.bp3-minimal select.bp3-intent-danger{color:#c23030}.bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-active,.bp3-html-select.bp3-minimal select.bp3-intent-danger:active,.bp3-html-select.bp3-minimal select.bp3-intent-danger:hover,.bp3-select.bp3-minimal select.bp3-intent-danger.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-danger:active,.bp3-select.bp3-minimal select.bp3-intent-danger:hover{background:none;box-shadow:none;color:#c23030}.bp3-html-select.bp3-minimal select.bp3-intent-danger:hover,.bp3-select.bp3-minimal select.bp3-intent-danger:hover{background:rgba(219,55,55,.15);color:#c23030}.bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-active,.bp3-html-select.bp3-minimal select.bp3-intent-danger:active,.bp3-select.bp3-minimal select.bp3-intent-danger.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-danger:active{background:rgba(219,55,55,.3);color:#c23030}.bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-disabled,.bp3-html-select.bp3-minimal select.bp3-intent-danger:disabled,.bp3-select.bp3-minimal select.bp3-intent-danger.bp3-disabled,.bp3-select.bp3-minimal select.bp3-intent-danger:disabled{background:none;color:rgba(194,48,48,.5)}.bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-disabled.bp3-active,.bp3-html-select.bp3-minimal select.bp3-intent-danger:disabled.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-danger.bp3-disabled.bp3-active,.bp3-select.bp3-minimal select.bp3-intent-danger:disabled.bp3-active{background:rgba(219,55,55,.3)}.bp3-html-select.bp3-minimal select.bp3-intent-danger .bp3-button-spinner .bp3-spinner-head,.bp3-select.bp3-minimal select.bp3-intent-danger .bp3-button-spinner .bp3-spinner-head{stroke:#c23030}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger{color:#ff7373}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger:hover,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger:hover,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger:hover,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger:hover{background:rgba(219,55,55,.2);color:#ff7373}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger:active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger:active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger:active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger:active{background:rgba(219,55,55,.3);color:#ff7373}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-disabled,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger:disabled,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger.bp3-disabled,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger:disabled,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger.bp3-disabled,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger:disabled,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger.bp3-disabled,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger:disabled{background:none;color:rgba(255,115,115,.5)}.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-disabled.bp3-active,.bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger:disabled.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger.bp3-disabled.bp3-active,.bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger:disabled.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger.bp3-disabled.bp3-active,.bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger:disabled.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger.bp3-disabled.bp3-active,.bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger:disabled.bp3-active{background:rgba(219,55,55,.3)}.bp3-html-select.bp3-large select,.bp3-select.bp3-large select{font-size:16px;height:40px;padding-right:35px}.bp3-dark .bp3-html-select select,.bp3-dark .bp3-select select{background-color:#394b59;background-image:linear-gradient(180deg,hsla(0,0%,100%,.05),hsla(0,0%,100%,0));box-shadow:0 0 0 1px rgba(16,22,26,.4);color:#f5f8fa}.bp3-dark .bp3-html-select select.bp3-active,.bp3-dark .bp3-html-select select:active,.bp3-dark .bp3-html-select select:hover,.bp3-dark .bp3-select select.bp3-active,.bp3-dark .bp3-select select:active,.bp3-dark .bp3-select select:hover{color:#f5f8fa}.bp3-dark .bp3-html-select select:hover,.bp3-dark .bp3-select select:hover{background-color:#30404d;box-shadow:0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-html-select select.bp3-active,.bp3-dark .bp3-html-select select:active,.bp3-dark .bp3-select select.bp3-active,.bp3-dark .bp3-select select:active{background-color:#202b33;background-image:none;box-shadow:0 0 0 1px rgba(16,22,26,.6),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-dark .bp3-html-select select.bp3-disabled,.bp3-dark .bp3-html-select select:disabled,.bp3-dark .bp3-select select.bp3-disabled,.bp3-dark .bp3-select select:disabled{background-color:rgba(57,75,89,.5);background-image:none;box-shadow:none;color:rgba(167,182,194,.6)}.bp3-dark .bp3-html-select select.bp3-disabled.bp3-active,.bp3-dark .bp3-html-select select:disabled.bp3-active,.bp3-dark .bp3-select select.bp3-disabled.bp3-active,.bp3-dark .bp3-select select:disabled.bp3-active{background:rgba(57,75,89,.7)}.bp3-dark .bp3-html-select select .bp3-button-spinner .bp3-spinner-head,.bp3-dark .bp3-select select .bp3-button-spinner .bp3-spinner-head{background:rgba(16,22,26,.5);stroke:#8a9ba8}.bp3-html-select select:disabled,.bp3-select select:disabled{background-color:rgba(206,217,224,.5);box-shadow:none;color:rgba(92,112,128,.6);cursor:not-allowed}.bp3-html-select .bp3-icon,.bp3-select .bp3-icon,.bp3-select:after{color:#5c7080;pointer-events:none;position:absolute;right:7px;top:7px}.bp3-disabled.bp3-select:after,.bp3-html-select .bp3-disabled.bp3-icon,.bp3-select .bp3-disabled.bp3-icon{color:rgba(92,112,128,.6)}.bp3-html-select,.bp3-select{display:inline-block;letter-spacing:normal;position:relative;vertical-align:middle}.bp3-html-select select::-ms-expand,.bp3-select select::-ms-expand{display:none}.bp3-html-select .bp3-icon,.bp3-select .bp3-icon{color:#5c7080}.bp3-html-select .bp3-icon:hover,.bp3-select .bp3-icon:hover{color:#182026}.bp3-dark .bp3-html-select .bp3-icon,.bp3-dark .bp3-select .bp3-icon{color:#a7b6c2}.bp3-dark .bp3-html-select .bp3-icon:hover,.bp3-dark .bp3-select .bp3-icon:hover{color:#f5f8fa}.bp3-html-select.bp3-large .bp3-icon,.bp3-html-select.bp3-large:after,.bp3-select.bp3-large .bp3-icon,.bp3-select.bp3-large:after{right:12px;top:12px}.bp3-html-select.bp3-fill,.bp3-html-select.bp3-fill select,.bp3-select.bp3-fill,.bp3-select.bp3-fill select{width:100%}.bp3-dark .bp3-html-select option,.bp3-dark .bp3-select option{background-color:#30404d;color:#f5f8fa}.bp3-dark .bp3-html-select option:disabled,.bp3-dark .bp3-select option:disabled{color:rgba(167,182,194,.6)}.bp3-dark .bp3-html-select:after,.bp3-dark .bp3-select:after{color:#a7b6c2}.bp3-select:after{font-family:Icons16,sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;content:"\E6C6"}.bp3-running-text table,table.bp3-html-table{border-spacing:0;font-size:14px}.bp3-running-text table td,.bp3-running-text table th,table.bp3-html-table td,table.bp3-html-table th{padding:11px;text-align:left;vertical-align:top}.bp3-running-text table th,table.bp3-html-table th{color:#182026;font-weight:600}.bp3-running-text table td,table.bp3-html-table td{color:#182026}.bp3-running-text table tbody tr:first-child td,.bp3-running-text table tbody tr:first-child th,table.bp3-html-table tbody tr:first-child td,table.bp3-html-table tbody tr:first-child th{box-shadow:inset 0 1px 0 0 rgba(16,22,26,.15)}.bp3-dark .bp3-running-text table td,.bp3-dark .bp3-running-text table th,.bp3-dark table.bp3-html-table td,.bp3-dark table.bp3-html-table th,.bp3-running-text .bp3-dark table td,.bp3-running-text .bp3-dark table th{color:#f5f8fa}.bp3-dark .bp3-running-text table tbody tr:first-child td,.bp3-dark .bp3-running-text table tbody tr:first-child th,.bp3-dark table.bp3-html-table tbody tr:first-child td,.bp3-dark table.bp3-html-table tbody tr:first-child th,.bp3-running-text .bp3-dark table tbody tr:first-child td,.bp3-running-text .bp3-dark table tbody tr:first-child th{box-shadow:inset 0 1px 0 0 hsla(0,0%,100%,.15)}table.bp3-html-table.bp3-html-table-condensed td,table.bp3-html-table.bp3-html-table-condensed th,table.bp3-html-table.bp3-small td,table.bp3-html-table.bp3-small th{padding-bottom:6px;padding-top:6px}table.bp3-html-table.bp3-html-table-striped tbody tr:nth-child(odd) td{background:rgba(191,204,214,.15)}table.bp3-html-table.bp3-html-table-bordered th:not(:first-child){box-shadow:inset 1px 0 0 0 rgba(16,22,26,.15)}table.bp3-html-table.bp3-html-table-bordered tbody tr td{box-shadow:inset 0 1px 0 0 rgba(16,22,26,.15)}table.bp3-html-table.bp3-html-table-bordered tbody tr td:not(:first-child){box-shadow:inset 1px 1px 0 0 rgba(16,22,26,.15)}table.bp3-html-table.bp3-html-table-bordered.bp3-html-table-striped tbody tr:not(:first-child) td{box-shadow:none}table.bp3-html-table.bp3-html-table-bordered.bp3-html-table-striped tbody tr:not(:first-child) td:not(:first-child){box-shadow:inset 1px 0 0 0 rgba(16,22,26,.15)}table.bp3-html-table.bp3-interactive tbody tr:hover td{background-color:rgba(191,204,214,.3);cursor:pointer}table.bp3-html-table.bp3-interactive tbody tr:active td{background-color:rgba(191,204,214,.4)}.bp3-dark table.bp3-html-table.bp3-html-table-striped tbody tr:nth-child(odd) td{background:rgba(92,112,128,.15)}.bp3-dark table.bp3-html-table.bp3-html-table-bordered th:not(:first-child){box-shadow:inset 1px 0 0 0 hsla(0,0%,100%,.15)}.bp3-dark table.bp3-html-table.bp3-html-table-bordered tbody tr td{box-shadow:inset 0 1px 0 0 hsla(0,0%,100%,.15)}.bp3-dark table.bp3-html-table.bp3-html-table-bordered tbody tr td:not(:first-child){box-shadow:inset 1px 1px 0 0 hsla(0,0%,100%,.15)}.bp3-dark table.bp3-html-table.bp3-html-table-bordered.bp3-html-table-striped tbody tr:not(:first-child) td{box-shadow:inset 1px 0 0 0 hsla(0,0%,100%,.15)}.bp3-dark table.bp3-html-table.bp3-html-table-bordered.bp3-html-table-striped tbody tr:not(:first-child) td:first-child{box-shadow:none}.bp3-dark table.bp3-html-table.bp3-interactive tbody tr:hover td{background-color:rgba(92,112,128,.3);cursor:pointer}.bp3-dark table.bp3-html-table.bp3-interactive tbody tr:active td{background-color:rgba(92,112,128,.4)}.bp3-key-combo{display:flex;flex-direction:row;align-items:center}.bp3-key-combo>*{flex-grow:0;flex-shrink:0}.bp3-key-combo>.bp3-fill{flex-grow:1;flex-shrink:1}.bp3-key-combo:before,.bp3-key-combo>*{margin-right:5px}.bp3-key-combo:empty:before,.bp3-key-combo>:last-child{margin-right:0}.bp3-hotkey-dialog{padding-bottom:0;top:40px}.bp3-hotkey-dialog .bp3-dialog-body{margin:0;padding:0}.bp3-hotkey-dialog .bp3-hotkey-label{flex-grow:1}.bp3-hotkey-column{margin:auto;max-height:80vh;overflow-y:auto;padding:30px}.bp3-hotkey-column .bp3-heading{margin-bottom:20px}.bp3-hotkey-column .bp3-heading:not(:first-child){margin-top:40px}.bp3-hotkey{align-items:center;display:flex;justify-content:space-between;margin-left:0;margin-right:0}.bp3-hotkey:not(:last-child){margin-bottom:10px}.bp3-icon{display:inline-block;flex:0 0 auto;vertical-align:text-bottom}.bp3-icon:not(:empty):before{content:""!important;content:unset!important}.bp3-icon>svg{display:block}.bp3-icon>svg:not([fill]){fill:currentColor}.bp3-icon-large.bp3-intent-primary,.bp3-icon-standard.bp3-intent-primary,.bp3-icon.bp3-intent-primary{color:#106ba3}.bp3-dark .bp3-icon-large.bp3-intent-primary,.bp3-dark .bp3-icon-standard.bp3-intent-primary,.bp3-dark .bp3-icon.bp3-intent-primary{color:#48aff0}.bp3-icon-large.bp3-intent-success,.bp3-icon-standard.bp3-intent-success,.bp3-icon.bp3-intent-success{color:#0d8050}.bp3-dark .bp3-icon-large.bp3-intent-success,.bp3-dark .bp3-icon-standard.bp3-intent-success,.bp3-dark .bp3-icon.bp3-intent-success{color:#3dcc91}.bp3-icon-large.bp3-intent-warning,.bp3-icon-standard.bp3-intent-warning,.bp3-icon.bp3-intent-warning{color:#bf7326}.bp3-dark .bp3-icon-large.bp3-intent-warning,.bp3-dark .bp3-icon-standard.bp3-intent-warning,.bp3-dark .bp3-icon.bp3-intent-warning{color:#ffb366}.bp3-icon-large.bp3-intent-danger,.bp3-icon-standard.bp3-intent-danger,.bp3-icon.bp3-intent-danger{color:#c23030}.bp3-dark .bp3-icon-large.bp3-intent-danger,.bp3-dark .bp3-icon-standard.bp3-intent-danger,.bp3-dark .bp3-icon.bp3-intent-danger{color:#ff7373}span.bp3-icon-standard{font-family:Icons16,sans-serif;font-size:16px}span.bp3-icon-large,span.bp3-icon-standard{font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block}span.bp3-icon-large{font-family:Icons20,sans-serif;font-size:20px}span.bp3-icon:empty{font-family:Icons20;font-size:inherit;font-style:normal;font-weight:400;line-height:1}span.bp3-icon:empty:before{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}.bp3-icon-add:before{content:"\E63E"}.bp3-icon-add-column-left:before{content:"\E6F9"}.bp3-icon-add-column-right:before{content:"\E6FA"}.bp3-icon-add-row-bottom:before{content:"\E6F8"}.bp3-icon-add-row-top:before{content:"\E6F7"}.bp3-icon-add-to-artifact:before{content:"\E67C"}.bp3-icon-add-to-folder:before{content:"\E6D2"}.bp3-icon-airplane:before{content:"\E74B"}.bp3-icon-align-center:before{content:"\E603"}.bp3-icon-align-justify:before{content:"\E605"}.bp3-icon-align-left:before{content:"\E602"}.bp3-icon-align-right:before{content:"\E604"}.bp3-icon-alignment-bottom:before{content:"\E727"}.bp3-icon-alignment-horizontal-center:before{content:"\E726"}.bp3-icon-alignment-left:before{content:"\E722"}.bp3-icon-alignment-right:before{content:"\E724"}.bp3-icon-alignment-top:before{content:"\E725"}.bp3-icon-alignment-vertical-center:before{content:"\E723"}.bp3-icon-annotation:before{content:"\E6F0"}.bp3-icon-application:before{content:"\E735"}.bp3-icon-applications:before{content:"\E621"}.bp3-icon-archive:before{content:"\E907"}.bp3-icon-arrow-bottom-left:before{content:"\2199"}.bp3-icon-arrow-bottom-right:before{content:"\2198"}.bp3-icon-arrow-down:before{content:"\2193"}.bp3-icon-arrow-left:before{content:"\2190"}.bp3-icon-arrow-right:before{content:"\2192"}.bp3-icon-arrow-top-left:before{content:"\2196"}.bp3-icon-arrow-top-right:before{content:"\2197"}.bp3-icon-arrow-up:before{content:"\2191"}.bp3-icon-arrows-horizontal:before{content:"\2194"}.bp3-icon-arrows-vertical:before{content:"\2195"}.bp3-icon-asterisk:before{content:"*"}.bp3-icon-automatic-updates:before{content:"\E65F"}.bp3-icon-badge:before{content:"\E6E3"}.bp3-icon-ban-circle:before{content:"\E69D"}.bp3-icon-bank-account:before{content:"\E76F"}.bp3-icon-barcode:before{content:"\E676"}.bp3-icon-blank:before{content:"\E900"}.bp3-icon-blocked-person:before{content:"\E768"}.bp3-icon-bold:before{content:"\E606"}.bp3-icon-book:before{content:"\E6B8"}.bp3-icon-bookmark:before{content:"\E61A"}.bp3-icon-box:before{content:"\E6BF"}.bp3-icon-briefcase:before{content:"\E674"}.bp3-icon-bring-data:before{content:"\E90A"}.bp3-icon-build:before{content:"\E72D"}.bp3-icon-calculator:before{content:"\E70B"}.bp3-icon-calendar:before{content:"\E62B"}.bp3-icon-camera:before{content:"\E69E"}.bp3-icon-caret-down:before{content:"\2304"}.bp3-icon-caret-left:before{content:"\2329"}.bp3-icon-caret-right:before{content:"\232A"}.bp3-icon-caret-up:before{content:"\2303"}.bp3-icon-cell-tower:before{content:"\E770"}.bp3-icon-changes:before{content:"\E623"}.bp3-icon-chart:before{content:"\E67E"}.bp3-icon-chat:before{content:"\E689"}.bp3-icon-chevron-backward:before{content:"\E6DF"}.bp3-icon-chevron-down:before{content:"\E697"}.bp3-icon-chevron-forward:before{content:"\E6E0"}.bp3-icon-chevron-left:before{content:"\E694"}.bp3-icon-chevron-right:before{content:"\E695"}.bp3-icon-chevron-up:before{content:"\E696"}.bp3-icon-circle:before{content:"\E66A"}.bp3-icon-circle-arrow-down:before{content:"\E68E"}.bp3-icon-circle-arrow-left:before{content:"\E68C"}.bp3-icon-circle-arrow-right:before{content:"\E68B"}.bp3-icon-circle-arrow-up:before{content:"\E68D"}.bp3-icon-citation:before{content:"\E61B"}.bp3-icon-clean:before{content:"\E7C5"}.bp3-icon-clipboard:before{content:"\E61D"}.bp3-icon-cloud:before{content:"\2601"}.bp3-icon-cloud-download:before{content:"\E690"}.bp3-icon-cloud-upload:before{content:"\E691"}.bp3-icon-code:before{content:"\E661"}.bp3-icon-code-block:before{content:"\E6C5"}.bp3-icon-cog:before{content:"\E645"}.bp3-icon-collapse-all:before{content:"\E763"}.bp3-icon-column-layout:before{content:"\E6DA"}.bp3-icon-comment:before{content:"\E68A"}.bp3-icon-comparison:before{content:"\E637"}.bp3-icon-compass:before{content:"\E79C"}.bp3-icon-compressed:before{content:"\E6C0"}.bp3-icon-confirm:before{content:"\E639"}.bp3-icon-console:before{content:"\E79B"}.bp3-icon-contrast:before{content:"\E6CB"}.bp3-icon-control:before{content:"\E67F"}.bp3-icon-credit-card:before{content:"\E649"}.bp3-icon-cross:before{content:"\2717"}.bp3-icon-crown:before{content:"\E7B4"}.bp3-icon-cube:before{content:"\E7C8"}.bp3-icon-cube-add:before{content:"\E7C9"}.bp3-icon-cube-remove:before{content:"\E7D0"}.bp3-icon-curved-range-chart:before{content:"\E71B"}.bp3-icon-cut:before{content:"\E6EF"}.bp3-icon-dashboard:before{content:"\E751"}.bp3-icon-data-lineage:before{content:"\E908"}.bp3-icon-database:before{content:"\E683"}.bp3-icon-delete:before{content:"\E644"}.bp3-icon-delta:before{content:"\394"}.bp3-icon-derive-column:before{content:"\E739"}.bp3-icon-desktop:before{content:"\E6AF"}.bp3-icon-diagnosis:before{content:"\E90D"}.bp3-icon-diagram-tree:before{content:"\E7B3"}.bp3-icon-direction-left:before{content:"\E681"}.bp3-icon-direction-right:before{content:"\E682"}.bp3-icon-disable:before{content:"\E600"}.bp3-icon-document:before{content:"\E630"}.bp3-icon-document-open:before{content:"\E71E"}.bp3-icon-document-share:before{content:"\E71F"}.bp3-icon-dollar:before{content:"$"}.bp3-icon-dot:before{content:"\2022"}.bp3-icon-double-caret-horizontal:before{content:"\E6C7"}.bp3-icon-double-caret-vertical:before{content:"\E6C6"}.bp3-icon-double-chevron-down:before{content:"\E703"}.bp3-icon-double-chevron-left:before{content:"\E6FF"}.bp3-icon-double-chevron-right:before{content:"\E701"}.bp3-icon-double-chevron-up:before{content:"\E702"}.bp3-icon-doughnut-chart:before{content:"\E6CE"}.bp3-icon-download:before{content:"\E62F"}.bp3-icon-drag-handle-horizontal:before{content:"\E716"}.bp3-icon-drag-handle-vertical:before{content:"\E715"}.bp3-icon-draw:before{content:"\E66B"}.bp3-icon-drive-time:before{content:"\E615"}.bp3-icon-duplicate:before{content:"\E69C"}.bp3-icon-edit:before{content:"\270E"}.bp3-icon-eject:before{content:"\23CF"}.bp3-icon-endorsed:before{content:"\E75F"}.bp3-icon-envelope:before{content:"\2709"}.bp3-icon-equals:before{content:"\E7D9"}.bp3-icon-eraser:before{content:"\E773"}.bp3-icon-error:before{content:"\E648"}.bp3-icon-euro:before{content:"\20AC"}.bp3-icon-exchange:before{content:"\E636"}.bp3-icon-exclude-row:before{content:"\E6EA"}.bp3-icon-expand-all:before{content:"\E764"}.bp3-icon-export:before{content:"\E633"}.bp3-icon-eye-off:before{content:"\E6CC"}.bp3-icon-eye-on:before{content:"\E75A"}.bp3-icon-eye-open:before{content:"\E66F"}.bp3-icon-fast-backward:before{content:"\E6A8"}.bp3-icon-fast-forward:before{content:"\E6AC"}.bp3-icon-feed:before{content:"\E656"}.bp3-icon-feed-subscribed:before{content:"\E78F"}.bp3-icon-film:before{content:"\E6A1"}.bp3-icon-filter:before{content:"\E638"}.bp3-icon-filter-keep:before{content:"\E78C"}.bp3-icon-filter-list:before{content:"\E6EE"}.bp3-icon-filter-open:before{content:"\E7D7"}.bp3-icon-filter-remove:before{content:"\E78D"}.bp3-icon-flag:before{content:"\2691"}.bp3-icon-flame:before{content:"\E7A9"}.bp3-icon-flash:before{content:"\E6B3"}.bp3-icon-floppy-disk:before{content:"\E6B7"}.bp3-icon-flow-branch:before{content:"\E7C1"}.bp3-icon-flow-end:before{content:"\E7C4"}.bp3-icon-flow-linear:before{content:"\E7C0"}.bp3-icon-flow-review:before{content:"\E7C2"}.bp3-icon-flow-review-branch:before{content:"\E7C3"}.bp3-icon-flows:before{content:"\E659"}.bp3-icon-folder-close:before{content:"\E652"}.bp3-icon-folder-new:before{content:"\E7B0"}.bp3-icon-folder-open:before{content:"\E651"}.bp3-icon-folder-shared:before{content:"\E653"}.bp3-icon-folder-shared-open:before{content:"\E670"}.bp3-icon-follower:before{content:"\E760"}.bp3-icon-following:before{content:"\E761"}.bp3-icon-font:before{content:"\E6B4"}.bp3-icon-fork:before{content:"\E63A"}.bp3-icon-form:before{content:"\E795"}.bp3-icon-full-circle:before{content:"\E685"}.bp3-icon-full-stacked-chart:before{content:"\E75E"}.bp3-icon-fullscreen:before{content:"\E699"}.bp3-icon-function:before{content:"\E6E5"}.bp3-icon-gantt-chart:before{content:"\E6F4"}.bp3-icon-geolocation:before{content:"\E640"}.bp3-icon-geosearch:before{content:"\E613"}.bp3-icon-git-branch:before{content:"\E72A"}.bp3-icon-git-commit:before{content:"\E72B"}.bp3-icon-git-merge:before{content:"\E729"}.bp3-icon-git-new-branch:before{content:"\E749"}.bp3-icon-git-pull:before{content:"\E728"}.bp3-icon-git-push:before{content:"\E72C"}.bp3-icon-git-repo:before{content:"\E748"}.bp3-icon-glass:before{content:"\E6B1"}.bp3-icon-globe:before{content:"\E666"}.bp3-icon-globe-network:before{content:"\E7B5"}.bp3-icon-graph:before{content:"\E673"}.bp3-icon-graph-remove:before{content:"\E609"}.bp3-icon-greater-than:before{content:"\E7E1"}.bp3-icon-greater-than-or-equal-to:before{content:"\E7E2"}.bp3-icon-grid:before{content:"\E6D0"}.bp3-icon-grid-view:before{content:"\E6E4"}.bp3-icon-group-objects:before{content:"\E60A"}.bp3-icon-grouped-bar-chart:before{content:"\E75D"}.bp3-icon-hand:before{content:"\E6DE"}.bp3-icon-hand-down:before{content:"\E6BB"}.bp3-icon-hand-left:before{content:"\E6BC"}.bp3-icon-hand-right:before{content:"\E6B9"}.bp3-icon-hand-up:before{content:"\E6BA"}.bp3-icon-header:before{content:"\E6B5"}.bp3-icon-header-one:before{content:"\E793"}.bp3-icon-header-two:before{content:"\E794"}.bp3-icon-headset:before{content:"\E6DC"}.bp3-icon-heart:before{content:"\2665"}.bp3-icon-heart-broken:before{content:"\E7A2"}.bp3-icon-heat-grid:before{content:"\E6F3"}.bp3-icon-heatmap:before{content:"\E614"}.bp3-icon-help:before{content:"?"}.bp3-icon-helper-management:before{content:"\E66D"}.bp3-icon-highlight:before{content:"\E6ED"}.bp3-icon-history:before{content:"\E64A"}.bp3-icon-home:before{content:"\2302"}.bp3-icon-horizontal-bar-chart:before{content:"\E70C"}.bp3-icon-horizontal-bar-chart-asc:before{content:"\E75C"}.bp3-icon-horizontal-bar-chart-desc:before{content:"\E71D"}.bp3-icon-horizontal-distribution:before{content:"\E720"}.bp3-icon-id-number:before{content:"\E771"}.bp3-icon-image-rotate-left:before{content:"\E73A"}.bp3-icon-image-rotate-right:before{content:"\E73B"}.bp3-icon-import:before{content:"\E632"}.bp3-icon-inbox:before{content:"\E629"}.bp3-icon-inbox-filtered:before{content:"\E7D1"}.bp3-icon-inbox-geo:before{content:"\E7D2"}.bp3-icon-inbox-search:before{content:"\E7D3"}.bp3-icon-inbox-update:before{content:"\E7D4"}.bp3-icon-info-sign:before{content:"\2139"}.bp3-icon-inheritance:before{content:"\E7D5"}.bp3-icon-inner-join:before{content:"\E7A3"}.bp3-icon-insert:before{content:"\E66C"}.bp3-icon-intersection:before{content:"\E765"}.bp3-icon-ip-address:before{content:"\E772"}.bp3-icon-issue:before{content:"\E774"}.bp3-icon-issue-closed:before{content:"\E776"}.bp3-icon-issue-new:before{content:"\E775"}.bp3-icon-italic:before{content:"\E607"}.bp3-icon-join-table:before{content:"\E738"}.bp3-icon-key:before{content:"\E78E"}.bp3-icon-key-backspace:before{content:"\E707"}.bp3-icon-key-command:before{content:"\E705"}.bp3-icon-key-control:before{content:"\E704"}.bp3-icon-key-delete:before{content:"\E708"}.bp3-icon-key-enter:before{content:"\E70A"}.bp3-icon-key-escape:before{content:"\E709"}.bp3-icon-key-option:before{content:"\E742"}.bp3-icon-key-shift:before{content:"\E706"}.bp3-icon-key-tab:before{content:"\E757"}.bp3-icon-known-vehicle:before{content:"\E73C"}.bp3-icon-lab-test:before{content:"\E90E"}.bp3-icon-label:before{content:"\E665"}.bp3-icon-layer:before{content:"\E6CF"}.bp3-icon-layers:before{content:"\E618"}.bp3-icon-layout:before{content:"\E60C"}.bp3-icon-layout-auto:before{content:"\E60D"}.bp3-icon-layout-balloon:before{content:"\E6D3"}.bp3-icon-layout-circle:before{content:"\E60E"}.bp3-icon-layout-grid:before{content:"\E610"}.bp3-icon-layout-group-by:before{content:"\E611"}.bp3-icon-layout-hierarchy:before{content:"\E60F"}.bp3-icon-layout-linear:before{content:"\E6C3"}.bp3-icon-layout-skew-grid:before{content:"\E612"}.bp3-icon-layout-sorted-clusters:before{content:"\E6D4"}.bp3-icon-learning:before{content:"\E904"}.bp3-icon-left-join:before{content:"\E7A4"}.bp3-icon-less-than:before{content:"\E7E3"}.bp3-icon-less-than-or-equal-to:before{content:"\E7E4"}.bp3-icon-lifesaver:before{content:"\E7C7"}.bp3-icon-lightbulb:before{content:"\E6B0"}.bp3-icon-link:before{content:"\E62D"}.bp3-icon-list:before{content:"\2630"}.bp3-icon-list-columns:before{content:"\E7B9"}.bp3-icon-list-detail-view:before{content:"\E743"}.bp3-icon-locate:before{content:"\E619"}.bp3-icon-lock:before{content:"\E625"}.bp3-icon-log-in:before{content:"\E69A"}.bp3-icon-log-out:before{content:"\E64C"}.bp3-icon-manual:before{content:"\E6F6"}.bp3-icon-manually-entered-data:before{content:"\E74A"}.bp3-icon-map:before{content:"\E662"}.bp3-icon-map-create:before{content:"\E741"}.bp3-icon-map-marker:before{content:"\E67D"}.bp3-icon-maximize:before{content:"\E635"}.bp3-icon-media:before{content:"\E62C"}.bp3-icon-menu:before{content:"\E762"}.bp3-icon-menu-closed:before{content:"\E655"}.bp3-icon-menu-open:before{content:"\E654"}.bp3-icon-merge-columns:before{content:"\E74F"}.bp3-icon-merge-links:before{content:"\E60B"}.bp3-icon-minimize:before{content:"\E634"}.bp3-icon-minus:before{content:"\2212"}.bp3-icon-mobile-phone:before{content:"\E717"}.bp3-icon-mobile-video:before{content:"\E69F"}.bp3-icon-moon:before{content:"\E754"}.bp3-icon-more:before{content:"\E62A"}.bp3-icon-mountain:before{content:"\E7B1"}.bp3-icon-move:before{content:"\E693"}.bp3-icon-mugshot:before{content:"\E6DB"}.bp3-icon-multi-select:before{content:"\E680"}.bp3-icon-music:before{content:"\E6A6"}.bp3-icon-new-drawing:before{content:"\E905"}.bp3-icon-new-grid-item:before{content:"\E747"}.bp3-icon-new-layer:before{content:"\E902"}.bp3-icon-new-layers:before{content:"\E903"}.bp3-icon-new-link:before{content:"\E65C"}.bp3-icon-new-object:before{content:"\E65D"}.bp3-icon-new-person:before{content:"\E6E9"}.bp3-icon-new-prescription:before{content:"\E78B"}.bp3-icon-new-text-box:before{content:"\E65B"}.bp3-icon-ninja:before{content:"\E675"}.bp3-icon-not-equal-to:before{content:"\E7E0"}.bp3-icon-notifications:before{content:"\E624"}.bp3-icon-notifications-updated:before{content:"\E7B8"}.bp3-icon-numbered-list:before{content:"\E746"}.bp3-icon-numerical:before{content:"\E756"}.bp3-icon-office:before{content:"\E69B"}.bp3-icon-offline:before{content:"\E67A"}.bp3-icon-oil-field:before{content:"\E73F"}.bp3-icon-one-column:before{content:"\E658"}.bp3-icon-outdated:before{content:"\E7A8"}.bp3-icon-page-layout:before{content:"\E660"}.bp3-icon-panel-stats:before{content:"\E777"}.bp3-icon-panel-table:before{content:"\E778"}.bp3-icon-paperclip:before{content:"\E664"}.bp3-icon-paragraph:before{content:"\E76C"}.bp3-icon-path:before{content:"\E753"}.bp3-icon-path-search:before{content:"\E65E"}.bp3-icon-pause:before{content:"\E6A9"}.bp3-icon-people:before{content:"\E63D"}.bp3-icon-percentage:before{content:"\E76A"}.bp3-icon-person:before{content:"\E63C"}.bp3-icon-phone:before{content:"\260E"}.bp3-icon-pie-chart:before{content:"\E684"}.bp3-icon-pin:before{content:"\E646"}.bp3-icon-pivot:before{content:"\E6F1"}.bp3-icon-pivot-table:before{content:"\E6EB"}.bp3-icon-play:before{content:"\E6AB"}.bp3-icon-plus:before{content:"+"}.bp3-icon-polygon-filter:before{content:"\E6D1"}.bp3-icon-power:before{content:"\E6D9"}.bp3-icon-predictive-analysis:before{content:"\E617"}.bp3-icon-prescription:before{content:"\E78A"}.bp3-icon-presentation:before{content:"\E687"}.bp3-icon-print:before{content:"\2399"}.bp3-icon-projects:before{content:"\E622"}.bp3-icon-properties:before{content:"\E631"}.bp3-icon-property:before{content:"\E65A"}.bp3-icon-publish-function:before{content:"\E752"}.bp3-icon-pulse:before{content:"\E6E8"}.bp3-icon-random:before{content:"\E698"}.bp3-icon-record:before{content:"\E6AE"}.bp3-icon-redo:before{content:"\E6C4"}.bp3-icon-refresh:before{content:"\E643"}.bp3-icon-regression-chart:before{content:"\E758"}.bp3-icon-remove:before{content:"\E63F"}.bp3-icon-remove-column:before{content:"\E755"}.bp3-icon-remove-column-left:before{content:"\E6FD"}.bp3-icon-remove-column-right:before{content:"\E6FE"}.bp3-icon-remove-row-bottom:before{content:"\E6FC"}.bp3-icon-remove-row-top:before{content:"\E6FB"}.bp3-icon-repeat:before{content:"\E692"}.bp3-icon-reset:before{content:"\E7D6"}.bp3-icon-resolve:before{content:"\E672"}.bp3-icon-rig:before{content:"\E740"}.bp3-icon-right-join:before{content:"\E7A5"}.bp3-icon-ring:before{content:"\E6F2"}.bp3-icon-rotate-document:before{content:"\E6E1"}.bp3-icon-rotate-page:before{content:"\E6E2"}.bp3-icon-satellite:before{content:"\E76B"}.bp3-icon-saved:before{content:"\E6B6"}.bp3-icon-scatter-plot:before{content:"\E73E"}.bp3-icon-search:before{content:"\E64B"}.bp3-icon-search-around:before{content:"\E608"}.bp3-icon-search-template:before{content:"\E628"}.bp3-icon-search-text:before{content:"\E663"}.bp3-icon-segmented-control:before{content:"\E6EC"}.bp3-icon-select:before{content:"\E616"}.bp3-icon-selection:before{content:"\29BF"}.bp3-icon-send-to:before{content:"\E66E"}.bp3-icon-send-to-graph:before{content:"\E736"}.bp3-icon-send-to-map:before{content:"\E737"}.bp3-icon-series-add:before{content:"\E796"}.bp3-icon-series-configuration:before{content:"\E79A"}.bp3-icon-series-derived:before{content:"\E799"}.bp3-icon-series-filtered:before{content:"\E798"}.bp3-icon-series-search:before{content:"\E797"}.bp3-icon-settings:before{content:"\E6A2"}.bp3-icon-share:before{content:"\E62E"}.bp3-icon-shield:before{content:"\E7B2"}.bp3-icon-shop:before{content:"\E6C2"}.bp3-icon-shopping-cart:before{content:"\E6C1"}.bp3-icon-signal-search:before{content:"\E909"}.bp3-icon-sim-card:before{content:"\E718"}.bp3-icon-slash:before{content:"\E769"}.bp3-icon-small-cross:before{content:"\E6D7"}.bp3-icon-small-minus:before{content:"\E70E"}.bp3-icon-small-plus:before{content:"\E70D"}.bp3-icon-small-tick:before{content:"\E6D8"}.bp3-icon-snowflake:before{content:"\E7B6"}.bp3-icon-social-media:before{content:"\E671"}.bp3-icon-sort:before{content:"\E64F"}.bp3-icon-sort-alphabetical:before{content:"\E64D"}.bp3-icon-sort-alphabetical-desc:before{content:"\E6C8"}.bp3-icon-sort-asc:before{content:"\E6D5"}.bp3-icon-sort-desc:before{content:"\E6D6"}.bp3-icon-sort-numerical:before{content:"\E64E"}.bp3-icon-sort-numerical-desc:before{content:"\E6C9"}.bp3-icon-split-columns:before{content:"\E750"}.bp3-icon-square:before{content:"\E686"}.bp3-icon-stacked-chart:before{content:"\E6E7"}.bp3-icon-star:before{content:"\2605"}.bp3-icon-star-empty:before{content:"\2606"}.bp3-icon-step-backward:before{content:"\E6A7"}.bp3-icon-step-chart:before{content:"\E70F"}.bp3-icon-step-forward:before{content:"\E6AD"}.bp3-icon-stop:before{content:"\E6AA"}.bp3-icon-stopwatch:before{content:"\E901"}.bp3-icon-strikethrough:before{content:"\E7A6"}.bp3-icon-style:before{content:"\E601"}.bp3-icon-swap-horizontal:before{content:"\E745"}.bp3-icon-swap-vertical:before{content:"\E744"}.bp3-icon-symbol-circle:before{content:"\E72E"}.bp3-icon-symbol-cross:before{content:"\E731"}.bp3-icon-symbol-diamond:before{content:"\E730"}.bp3-icon-symbol-square:before{content:"\E72F"}.bp3-icon-symbol-triangle-down:before{content:"\E733"}.bp3-icon-symbol-triangle-up:before{content:"\E732"}.bp3-icon-tag:before{content:"\E61C"}.bp3-icon-take-action:before{content:"\E6CA"}.bp3-icon-taxi:before{content:"\E79E"}.bp3-icon-text-highlight:before{content:"\E6DD"}.bp3-icon-th:before{content:"\E667"}.bp3-icon-th-derived:before{content:"\E669"}.bp3-icon-th-disconnect:before{content:"\E7D8"}.bp3-icon-th-filtered:before{content:"\E7C6"}.bp3-icon-th-list:before{content:"\E668"}.bp3-icon-thumbs-down:before{content:"\E6BE"}.bp3-icon-thumbs-up:before{content:"\E6BD"}.bp3-icon-tick:before{content:"\2713"}.bp3-icon-tick-circle:before{content:"\E779"}.bp3-icon-time:before{content:"\23F2"}.bp3-icon-timeline-area-chart:before{content:"\E6CD"}.bp3-icon-timeline-bar-chart:before{content:"\E620"}.bp3-icon-timeline-events:before{content:"\E61E"}.bp3-icon-timeline-line-chart:before{content:"\E61F"}.bp3-icon-tint:before{content:"\E6B2"}.bp3-icon-torch:before{content:"\E677"}.bp3-icon-tractor:before{content:"\E90C"}.bp3-icon-train:before{content:"\E79F"}.bp3-icon-translate:before{content:"\E759"}.bp3-icon-trash:before{content:"\E63B"}.bp3-icon-tree:before{content:"\E7B7"}.bp3-icon-trending-down:before{content:"\E71A"}.bp3-icon-trending-up:before{content:"\E719"}.bp3-icon-truck:before{content:"\E90B"}.bp3-icon-two-columns:before{content:"\E657"}.bp3-icon-unarchive:before{content:"\E906"}.bp3-icon-underline:before{content:"\2381"}.bp3-icon-undo:before{content:"\238C"}.bp3-icon-ungroup-objects:before{content:"\E688"}.bp3-icon-unknown-vehicle:before{content:"\E73D"}.bp3-icon-unlock:before{content:"\E626"}.bp3-icon-unpin:before{content:"\E650"}.bp3-icon-unresolve:before{content:"\E679"}.bp3-icon-updated:before{content:"\E7A7"}.bp3-icon-upload:before{content:"\E68F"}.bp3-icon-user:before{content:"\E627"}.bp3-icon-variable:before{content:"\E6F5"}.bp3-icon-vertical-bar-chart-asc:before{content:"\E75B"}.bp3-icon-vertical-bar-chart-desc:before{content:"\E71C"}.bp3-icon-vertical-distribution:before{content:"\E721"}.bp3-icon-video:before{content:"\E6A0"}.bp3-icon-volume-down:before{content:"\E6A4"}.bp3-icon-volume-off:before{content:"\E6A3"}.bp3-icon-volume-up:before{content:"\E6A5"}.bp3-icon-walk:before{content:"\E79D"}.bp3-icon-warning-sign:before{content:"\E647"}.bp3-icon-waterfall-chart:before{content:"\E6E6"}.bp3-icon-widget:before{content:"\E678"}.bp3-icon-widget-button:before{content:"\E790"}.bp3-icon-widget-footer:before{content:"\E792"}.bp3-icon-widget-header:before{content:"\E791"}.bp3-icon-wrench:before{content:"\E734"}.bp3-icon-zoom-in:before{content:"\E641"}.bp3-icon-zoom-out:before{content:"\E642"}.bp3-icon-zoom-to-fit:before{content:"\E67B"}.bp3-submenu .bp3-popover-target,.bp3-submenu>.bp3-popover-wrapper{display:block}.bp3-submenu.bp3-popover{box-shadow:none;padding:0 5px}.bp3-submenu.bp3-popover>.bp3-popover-content{box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2)}.bp3-dark .bp3-submenu.bp3-popover,.bp3-submenu.bp3-popover.bp3-dark{box-shadow:none}.bp3-dark .bp3-submenu.bp3-popover>.bp3-popover-content,.bp3-submenu.bp3-popover.bp3-dark>.bp3-popover-content{box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4)}.bp3-menu{background:#fff;border-radius:3px;color:#182026;list-style:none;margin:0;min-width:180px;padding:5px;text-align:left}.bp3-menu-divider{border-top:1px solid rgba(16,22,26,.15);display:block;margin:5px}.bp3-dark .bp3-menu-divider{border-top-color:hsla(0,0%,100%,.15)}.bp3-menu-item{display:flex;flex-direction:row;align-items:flex-start;border-radius:2px;color:inherit;line-height:20px;padding:5px 7px;text-decoration:none;-webkit-user-select:none;user-select:none}.bp3-menu-item>*{flex-grow:0;flex-shrink:0}.bp3-menu-item>.bp3-fill{flex-grow:1;flex-shrink:1}.bp3-menu-item:before,.bp3-menu-item>*{margin-right:7px}.bp3-menu-item:empty:before,.bp3-menu-item>:last-child{margin-right:0}.bp3-menu-item>.bp3-fill{word-break:break-word}.bp3-menu-item:hover,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-menu-item{background-color:rgba(167,182,194,.3);cursor:pointer;text-decoration:none}.bp3-menu-item.bp3-disabled{background-color:inherit;color:rgba(92,112,128,.6);cursor:not-allowed}.bp3-dark .bp3-menu-item{color:inherit}.bp3-dark .bp3-menu-item:hover,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-menu-item,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-menu-item{background-color:rgba(138,155,168,.15);color:inherit}.bp3-dark .bp3-menu-item.bp3-disabled{background-color:inherit;color:rgba(167,182,194,.6)}.bp3-menu-item.bp3-intent-primary{color:#106ba3}.bp3-menu-item.bp3-intent-primary .bp3-icon{color:inherit}.bp3-menu-item.bp3-intent-primary .bp3-menu-item-label,.bp3-menu-item.bp3-intent-primary:after,.bp3-menu-item.bp3-intent-primary:before{color:#106ba3}.bp3-menu-item.bp3-intent-primary.bp3-active,.bp3-menu-item.bp3-intent-primary:hover,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item{background-color:#137cbd}.bp3-menu-item.bp3-intent-primary:active{background-color:#106ba3}.bp3-menu-item.bp3-intent-primary.bp3-active,.bp3-menu-item.bp3-intent-primary.bp3-active .bp3-menu-item-label,.bp3-menu-item.bp3-intent-primary.bp3-active:after,.bp3-menu-item.bp3-intent-primary.bp3-active:before,.bp3-menu-item.bp3-intent-primary:active,.bp3-menu-item.bp3-intent-primary:active .bp3-menu-item-label,.bp3-menu-item.bp3-intent-primary:active:after,.bp3-menu-item.bp3-intent-primary:active:before,.bp3-menu-item.bp3-intent-primary:hover,.bp3-menu-item.bp3-intent-primary:hover .bp3-menu-item-label,.bp3-menu-item.bp3-intent-primary:hover:after,.bp3-menu-item.bp3-intent-primary:hover:before,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item .bp3-menu-item-label,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item:after,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item:before{color:#fff}.bp3-menu-item.bp3-intent-success{color:#0d8050}.bp3-menu-item.bp3-intent-success .bp3-icon{color:inherit}.bp3-menu-item.bp3-intent-success .bp3-menu-item-label,.bp3-menu-item.bp3-intent-success:after,.bp3-menu-item.bp3-intent-success:before{color:#0d8050}.bp3-menu-item.bp3-intent-success.bp3-active,.bp3-menu-item.bp3-intent-success:hover,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item{background-color:#0f9960}.bp3-menu-item.bp3-intent-success:active{background-color:#0d8050}.bp3-menu-item.bp3-intent-success.bp3-active,.bp3-menu-item.bp3-intent-success.bp3-active .bp3-menu-item-label,.bp3-menu-item.bp3-intent-success.bp3-active:after,.bp3-menu-item.bp3-intent-success.bp3-active:before,.bp3-menu-item.bp3-intent-success:active,.bp3-menu-item.bp3-intent-success:active .bp3-menu-item-label,.bp3-menu-item.bp3-intent-success:active:after,.bp3-menu-item.bp3-intent-success:active:before,.bp3-menu-item.bp3-intent-success:hover,.bp3-menu-item.bp3-intent-success:hover .bp3-menu-item-label,.bp3-menu-item.bp3-intent-success:hover:after,.bp3-menu-item.bp3-intent-success:hover:before,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item .bp3-menu-item-label,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item:after,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item:before{color:#fff}.bp3-menu-item.bp3-intent-warning{color:#bf7326}.bp3-menu-item.bp3-intent-warning .bp3-icon{color:inherit}.bp3-menu-item.bp3-intent-warning .bp3-menu-item-label,.bp3-menu-item.bp3-intent-warning:after,.bp3-menu-item.bp3-intent-warning:before{color:#bf7326}.bp3-menu-item.bp3-intent-warning.bp3-active,.bp3-menu-item.bp3-intent-warning:hover,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item{background-color:#d9822b}.bp3-menu-item.bp3-intent-warning:active{background-color:#bf7326}.bp3-menu-item.bp3-intent-warning.bp3-active,.bp3-menu-item.bp3-intent-warning.bp3-active .bp3-menu-item-label,.bp3-menu-item.bp3-intent-warning.bp3-active:after,.bp3-menu-item.bp3-intent-warning.bp3-active:before,.bp3-menu-item.bp3-intent-warning:active,.bp3-menu-item.bp3-intent-warning:active .bp3-menu-item-label,.bp3-menu-item.bp3-intent-warning:active:after,.bp3-menu-item.bp3-intent-warning:active:before,.bp3-menu-item.bp3-intent-warning:hover,.bp3-menu-item.bp3-intent-warning:hover .bp3-menu-item-label,.bp3-menu-item.bp3-intent-warning:hover:after,.bp3-menu-item.bp3-intent-warning:hover:before,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item .bp3-menu-item-label,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item:after,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item:before{color:#fff}.bp3-menu-item.bp3-intent-danger{color:#c23030}.bp3-menu-item.bp3-intent-danger .bp3-icon{color:inherit}.bp3-menu-item.bp3-intent-danger .bp3-menu-item-label,.bp3-menu-item.bp3-intent-danger:after,.bp3-menu-item.bp3-intent-danger:before{color:#c23030}.bp3-menu-item.bp3-intent-danger.bp3-active,.bp3-menu-item.bp3-intent-danger:hover,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item{background-color:#db3737}.bp3-menu-item.bp3-intent-danger:active{background-color:#c23030}.bp3-menu-item.bp3-intent-danger.bp3-active,.bp3-menu-item.bp3-intent-danger.bp3-active .bp3-menu-item-label,.bp3-menu-item.bp3-intent-danger.bp3-active:after,.bp3-menu-item.bp3-intent-danger.bp3-active:before,.bp3-menu-item.bp3-intent-danger:active,.bp3-menu-item.bp3-intent-danger:active .bp3-menu-item-label,.bp3-menu-item.bp3-intent-danger:active:after,.bp3-menu-item.bp3-intent-danger:active:before,.bp3-menu-item.bp3-intent-danger:hover,.bp3-menu-item.bp3-intent-danger:hover .bp3-menu-item-label,.bp3-menu-item.bp3-intent-danger:hover:after,.bp3-menu-item.bp3-intent-danger:hover:before,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item .bp3-menu-item-label,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item:after,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item:before{color:#fff}.bp3-menu-item:before{font-family:Icons16,sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;margin-right:7px}.bp3-menu-item:before,.bp3-menu-item>.bp3-icon{color:#5c7080;margin-top:2px}.bp3-menu-item .bp3-menu-item-label{color:#5c7080}.bp3-menu-item:hover,.bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-menu-item{color:inherit}.bp3-menu-item.bp3-active,.bp3-menu-item:active{background-color:rgba(115,134,148,.3)}.bp3-menu-item.bp3-disabled{background-color:inherit!important;cursor:not-allowed!important;outline:none!important}.bp3-menu-item.bp3-disabled,.bp3-menu-item.bp3-disabled .bp3-menu-item-label,.bp3-menu-item.bp3-disabled:before,.bp3-menu-item.bp3-disabled>.bp3-icon{color:rgba(92,112,128,.6)!important}.bp3-large .bp3-menu-item{font-size:16px;line-height:22px;padding:9px 7px}.bp3-large .bp3-menu-item .bp3-icon{margin-top:3px}.bp3-large .bp3-menu-item:before{font-family:Icons20,sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;margin-right:10px;margin-top:1px}button.bp3-menu-item{background:none;border:none;text-align:left;width:100%}.bp3-menu-header{border-top:1px solid rgba(16,22,26,.15);display:block;margin:5px;cursor:default;padding-left:2px}.bp3-dark .bp3-menu-header{border-top-color:hsla(0,0%,100%,.15)}.bp3-menu-header:first-of-type{border-top:none}.bp3-menu-header>h6{color:#182026;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;line-height:17px;margin:0;padding:10px 7px 0 1px}.bp3-menu-header:first-of-type>h6{padding-top:0}.bp3-large .bp3-menu-header>h6{font-size:18px;padding-bottom:5px;padding-top:15px}.bp3-large .bp3-menu-header:first-of-type>h6{padding-top:0}.bp3-dark .bp3-menu{background:#30404d;color:#f5f8fa}.bp3-dark .bp3-menu-item.bp3-intent-primary{color:#48aff0}.bp3-dark .bp3-menu-item.bp3-intent-primary .bp3-icon{color:inherit}.bp3-dark .bp3-menu-item.bp3-intent-primary .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-primary:after,.bp3-dark .bp3-menu-item.bp3-intent-primary:before{color:#48aff0}.bp3-dark .bp3-menu-item.bp3-intent-primary.bp3-active,.bp3-dark .bp3-menu-item.bp3-intent-primary:hover,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item{background-color:#137cbd}.bp3-dark .bp3-menu-item.bp3-intent-primary:active{background-color:#106ba3}.bp3-dark .bp3-menu-item.bp3-intent-primary.bp3-active,.bp3-dark .bp3-menu-item.bp3-intent-primary.bp3-active .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-primary.bp3-active:after,.bp3-dark .bp3-menu-item.bp3-intent-primary.bp3-active:before,.bp3-dark .bp3-menu-item.bp3-intent-primary:active,.bp3-dark .bp3-menu-item.bp3-intent-primary:active .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-primary:active:after,.bp3-dark .bp3-menu-item.bp3-intent-primary:active:before,.bp3-dark .bp3-menu-item.bp3-intent-primary:hover,.bp3-dark .bp3-menu-item.bp3-intent-primary:hover .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-primary:hover:after,.bp3-dark .bp3-menu-item.bp3-intent-primary:hover:before,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item .bp3-menu-item-label,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item:after,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item:before,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item .bp3-menu-item-label,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item:after,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item:before{color:#fff}.bp3-dark .bp3-menu-item.bp3-intent-success{color:#3dcc91}.bp3-dark .bp3-menu-item.bp3-intent-success .bp3-icon{color:inherit}.bp3-dark .bp3-menu-item.bp3-intent-success .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-success:after,.bp3-dark .bp3-menu-item.bp3-intent-success:before{color:#3dcc91}.bp3-dark .bp3-menu-item.bp3-intent-success.bp3-active,.bp3-dark .bp3-menu-item.bp3-intent-success:hover,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item{background-color:#0f9960}.bp3-dark .bp3-menu-item.bp3-intent-success:active{background-color:#0d8050}.bp3-dark .bp3-menu-item.bp3-intent-success.bp3-active,.bp3-dark .bp3-menu-item.bp3-intent-success.bp3-active .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-success.bp3-active:after,.bp3-dark .bp3-menu-item.bp3-intent-success.bp3-active:before,.bp3-dark .bp3-menu-item.bp3-intent-success:active,.bp3-dark .bp3-menu-item.bp3-intent-success:active .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-success:active:after,.bp3-dark .bp3-menu-item.bp3-intent-success:active:before,.bp3-dark .bp3-menu-item.bp3-intent-success:hover,.bp3-dark .bp3-menu-item.bp3-intent-success:hover .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-success:hover:after,.bp3-dark .bp3-menu-item.bp3-intent-success:hover:before,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item .bp3-menu-item-label,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item:after,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item:before,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item .bp3-menu-item-label,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item:after,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item:before{color:#fff}.bp3-dark .bp3-menu-item.bp3-intent-warning{color:#ffb366}.bp3-dark .bp3-menu-item.bp3-intent-warning .bp3-icon{color:inherit}.bp3-dark .bp3-menu-item.bp3-intent-warning .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-warning:after,.bp3-dark .bp3-menu-item.bp3-intent-warning:before{color:#ffb366}.bp3-dark .bp3-menu-item.bp3-intent-warning.bp3-active,.bp3-dark .bp3-menu-item.bp3-intent-warning:hover,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item{background-color:#d9822b}.bp3-dark .bp3-menu-item.bp3-intent-warning:active{background-color:#bf7326}.bp3-dark .bp3-menu-item.bp3-intent-warning.bp3-active,.bp3-dark .bp3-menu-item.bp3-intent-warning.bp3-active .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-warning.bp3-active:after,.bp3-dark .bp3-menu-item.bp3-intent-warning.bp3-active:before,.bp3-dark .bp3-menu-item.bp3-intent-warning:active,.bp3-dark .bp3-menu-item.bp3-intent-warning:active .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-warning:active:after,.bp3-dark .bp3-menu-item.bp3-intent-warning:active:before,.bp3-dark .bp3-menu-item.bp3-intent-warning:hover,.bp3-dark .bp3-menu-item.bp3-intent-warning:hover .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-warning:hover:after,.bp3-dark .bp3-menu-item.bp3-intent-warning:hover:before,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item .bp3-menu-item-label,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item:after,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item:before,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item .bp3-menu-item-label,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item:after,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item:before{color:#fff}.bp3-dark .bp3-menu-item.bp3-intent-danger{color:#ff7373}.bp3-dark .bp3-menu-item.bp3-intent-danger .bp3-icon{color:inherit}.bp3-dark .bp3-menu-item.bp3-intent-danger .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-danger:after,.bp3-dark .bp3-menu-item.bp3-intent-danger:before{color:#ff7373}.bp3-dark .bp3-menu-item.bp3-intent-danger.bp3-active,.bp3-dark .bp3-menu-item.bp3-intent-danger:hover,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item{background-color:#db3737}.bp3-dark .bp3-menu-item.bp3-intent-danger:active{background-color:#c23030}.bp3-dark .bp3-menu-item.bp3-intent-danger.bp3-active,.bp3-dark .bp3-menu-item.bp3-intent-danger.bp3-active .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-danger.bp3-active:after,.bp3-dark .bp3-menu-item.bp3-intent-danger.bp3-active:before,.bp3-dark .bp3-menu-item.bp3-intent-danger:active,.bp3-dark .bp3-menu-item.bp3-intent-danger:active .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-danger:active:after,.bp3-dark .bp3-menu-item.bp3-intent-danger:active:before,.bp3-dark .bp3-menu-item.bp3-intent-danger:hover,.bp3-dark .bp3-menu-item.bp3-intent-danger:hover .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-intent-danger:hover:after,.bp3-dark .bp3-menu-item.bp3-intent-danger:hover:before,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item .bp3-menu-item-label,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item:after,.bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item:before,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item .bp3-menu-item-label,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item:after,.bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item:before{color:#fff}.bp3-dark .bp3-menu-item .bp3-menu-item-label,.bp3-dark .bp3-menu-item:before,.bp3-dark .bp3-menu-item>.bp3-icon{color:#a7b6c2}.bp3-dark .bp3-menu-item.bp3-active,.bp3-dark .bp3-menu-item:active{background-color:rgba(138,155,168,.3)}.bp3-dark .bp3-menu-item.bp3-disabled,.bp3-dark .bp3-menu-item.bp3-disabled .bp3-menu-item-label,.bp3-dark .bp3-menu-item.bp3-disabled:before,.bp3-dark .bp3-menu-item.bp3-disabled>.bp3-icon{color:rgba(167,182,194,.6)!important}.bp3-dark .bp3-menu-divider,.bp3-dark .bp3-menu-header{border-color:hsla(0,0%,100%,.15)}.bp3-dark .bp3-menu-header>h6{color:#f5f8fa}.bp3-label .bp3-menu{margin-top:5px}.bp3-navbar{background-color:#fff;box-shadow:0 0 0 1px rgba(16,22,26,.1),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.2);height:50px;padding:0 15px;position:relative;width:100%;z-index:10}.bp3-dark .bp3-navbar,.bp3-navbar.bp3-dark{background-color:#394b59}.bp3-navbar.bp3-dark{box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-navbar{box-shadow:0 0 0 1px rgba(16,22,26,.2),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.4)}.bp3-navbar.bp3-fixed-top{left:0;position:fixed;right:0;top:0}.bp3-navbar-heading{font-size:16px;margin-right:15px}.bp3-navbar-group{align-items:center;display:flex;height:50px}.bp3-navbar-group.bp3-align-left{float:left}.bp3-navbar-group.bp3-align-right{float:right}.bp3-navbar-divider{border-left:1px solid rgba(16,22,26,.15);height:20px;margin:0 10px}.bp3-dark .bp3-navbar-divider{border-left-color:hsla(0,0%,100%,.15)}.bp3-non-ideal-state{display:flex;flex-direction:column;align-items:center;height:100%;justify-content:center;text-align:center;width:100%}.bp3-non-ideal-state>*{flex-grow:0;flex-shrink:0}.bp3-non-ideal-state>.bp3-fill{flex-grow:1;flex-shrink:1}.bp3-non-ideal-state:before,.bp3-non-ideal-state>*{margin-bottom:20px}.bp3-non-ideal-state:empty:before,.bp3-non-ideal-state>:last-child{margin-bottom:0}.bp3-non-ideal-state>*{max-width:400px}.bp3-non-ideal-state-visual{color:rgba(92,112,128,.6);font-size:60px}.bp3-dark .bp3-non-ideal-state-visual{color:rgba(167,182,194,.6)}.bp3-overflow-list{display:flex;flex-wrap:nowrap;min-width:0}.bp3-overflow-list-spacer{flex-shrink:1;width:1px}body.bp3-overlay-open{overflow:hidden}.bp3-overlay{bottom:0;left:0;position:static;right:0;top:0;z-index:20}.bp3-overlay:not(.bp3-overlay-open){pointer-events:none}.bp3-overlay.bp3-overlay-container{overflow:hidden;position:fixed}.bp3-overlay.bp3-overlay-container.bp3-overlay-inline{position:absolute}.bp3-overlay.bp3-overlay-scroll-container{overflow:auto;position:fixed}.bp3-overlay.bp3-overlay-scroll-container.bp3-overlay-inline{position:absolute}.bp3-overlay.bp3-overlay-inline{display:inline;overflow:visible}.bp3-overlay-content{position:fixed;z-index:20}.bp3-overlay-inline .bp3-overlay-content,.bp3-overlay-scroll-container .bp3-overlay-content{position:absolute}.bp3-overlay-backdrop{bottom:0;left:0;position:fixed;right:0;top:0;opacity:1;background-color:rgba(16,22,26,.7);overflow:auto;-webkit-user-select:none;user-select:none;z-index:20}.bp3-overlay-backdrop.bp3-overlay-appear,.bp3-overlay-backdrop.bp3-overlay-enter{opacity:0}.bp3-overlay-backdrop.bp3-overlay-appear-active,.bp3-overlay-backdrop.bp3-overlay-enter-active{opacity:1;transition-delay:0;transition-duration:.2s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-overlay-backdrop.bp3-overlay-exit{opacity:1}.bp3-overlay-backdrop.bp3-overlay-exit-active{opacity:0;transition-delay:0;transition-duration:.2s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-overlay-backdrop:focus{outline:none}.bp3-overlay-inline .bp3-overlay-backdrop{position:absolute}.bp3-panel-stack{overflow:hidden;position:relative}.bp3-panel-stack-header{align-items:center;box-shadow:0 1px rgba(16,22,26,.15);display:flex;flex-shrink:0;height:30px;z-index:1}.bp3-dark .bp3-panel-stack-header{box-shadow:0 1px hsla(0,0%,100%,.15)}.bp3-panel-stack-header>span{align-items:stretch;display:flex;flex:1 1}.bp3-panel-stack-header .bp3-heading{margin:0 5px}.bp3-button.bp3-panel-stack-header-back{margin-left:5px;padding-left:0;white-space:nowrap}.bp3-button.bp3-panel-stack-header-back .bp3-icon{margin:0 2px}.bp3-panel-stack-view{bottom:0;left:0;position:absolute;right:0;top:0;background-color:#fff;border-right:1px solid rgba(16,22,26,.15);display:flex;flex-direction:column;margin-right:-1px;overflow-y:auto;z-index:1}.bp3-dark .bp3-panel-stack-view{background-color:#30404d}.bp3-panel-stack-view:nth-last-child(n+4){display:none}.bp3-panel-stack-push .bp3-panel-stack-appear,.bp3-panel-stack-push .bp3-panel-stack-enter{transform:translateX(100%);opacity:0}.bp3-panel-stack-push .bp3-panel-stack-appear-active,.bp3-panel-stack-push .bp3-panel-stack-enter-active{transform:translate(0);opacity:1;transition-delay:0;transition-duration:.4s;transition-property:transform,opacity;transition-timing-function:ease}.bp3-panel-stack-push .bp3-panel-stack-exit{transform:translate(0);opacity:1}.bp3-panel-stack-push .bp3-panel-stack-exit-active{transform:translateX(-50%);opacity:0;transition-delay:0;transition-duration:.4s;transition-property:transform,opacity;transition-timing-function:ease}.bp3-panel-stack-pop .bp3-panel-stack-appear,.bp3-panel-stack-pop .bp3-panel-stack-enter{transform:translateX(-50%);opacity:0}.bp3-panel-stack-pop .bp3-panel-stack-appear-active,.bp3-panel-stack-pop .bp3-panel-stack-enter-active{transform:translate(0);opacity:1;transition-delay:0;transition-duration:.4s;transition-property:transform,opacity;transition-timing-function:ease}.bp3-panel-stack-pop .bp3-panel-stack-exit{transform:translate(0);opacity:1}.bp3-panel-stack-pop .bp3-panel-stack-exit-active{transform:translateX(100%);opacity:0;transition-delay:0;transition-duration:.4s;transition-property:transform,opacity;transition-timing-function:ease}.bp3-popover{box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2);transform:scale(1);border-radius:3px;display:inline-block;z-index:20}.bp3-popover .bp3-popover-arrow{height:30px;position:absolute;width:30px}.bp3-popover .bp3-popover-arrow:before{height:20px;margin:5px;width:20px}.bp3-tether-element-attached-bottom.bp3-tether-target-attached-top>.bp3-popover{margin-bottom:17px;margin-top:-17px}.bp3-tether-element-attached-bottom.bp3-tether-target-attached-top>.bp3-popover>.bp3-popover-arrow{bottom:-11px}.bp3-tether-element-attached-bottom.bp3-tether-target-attached-top>.bp3-popover>.bp3-popover-arrow svg{transform:rotate(-90deg)}.bp3-tether-element-attached-left.bp3-tether-target-attached-right>.bp3-popover{margin-left:17px}.bp3-tether-element-attached-left.bp3-tether-target-attached-right>.bp3-popover>.bp3-popover-arrow{left:-11px}.bp3-tether-element-attached-left.bp3-tether-target-attached-right>.bp3-popover>.bp3-popover-arrow svg{transform:rotate(0)}.bp3-tether-element-attached-top.bp3-tether-target-attached-bottom>.bp3-popover{margin-top:17px}.bp3-tether-element-attached-top.bp3-tether-target-attached-bottom>.bp3-popover>.bp3-popover-arrow{top:-11px}.bp3-tether-element-attached-top.bp3-tether-target-attached-bottom>.bp3-popover>.bp3-popover-arrow svg{transform:rotate(90deg)}.bp3-tether-element-attached-right.bp3-tether-target-attached-left>.bp3-popover{margin-left:-17px;margin-right:17px}.bp3-tether-element-attached-right.bp3-tether-target-attached-left>.bp3-popover>.bp3-popover-arrow{right:-11px}.bp3-tether-element-attached-right.bp3-tether-target-attached-left>.bp3-popover>.bp3-popover-arrow svg{transform:rotate(180deg)}.bp3-tether-element-attached-middle>.bp3-popover>.bp3-popover-arrow{top:50%;transform:translateY(-50%)}.bp3-tether-element-attached-center>.bp3-popover>.bp3-popover-arrow{right:50%;transform:translateX(50%)}.bp3-tether-element-attached-top.bp3-tether-target-attached-top>.bp3-popover>.bp3-popover-arrow{top:-.3934px}.bp3-tether-element-attached-right.bp3-tether-target-attached-right>.bp3-popover>.bp3-popover-arrow{right:-.3934px}.bp3-tether-element-attached-left.bp3-tether-target-attached-left>.bp3-popover>.bp3-popover-arrow{left:-.3934px}.bp3-tether-element-attached-bottom.bp3-tether-target-attached-bottom>.bp3-popover>.bp3-popover-arrow{bottom:-.3934px}.bp3-tether-element-attached-top.bp3-tether-element-attached-left>.bp3-popover{transform-origin:top left}.bp3-tether-element-attached-top.bp3-tether-element-attached-center>.bp3-popover{transform-origin:top center}.bp3-tether-element-attached-top.bp3-tether-element-attached-right>.bp3-popover{transform-origin:top right}.bp3-tether-element-attached-middle.bp3-tether-element-attached-left>.bp3-popover{transform-origin:center left}.bp3-tether-element-attached-middle.bp3-tether-element-attached-center>.bp3-popover{transform-origin:center center}.bp3-tether-element-attached-middle.bp3-tether-element-attached-right>.bp3-popover{transform-origin:center right}.bp3-tether-element-attached-bottom.bp3-tether-element-attached-left>.bp3-popover{transform-origin:bottom left}.bp3-tether-element-attached-bottom.bp3-tether-element-attached-center>.bp3-popover{transform-origin:bottom center}.bp3-tether-element-attached-bottom.bp3-tether-element-attached-right>.bp3-popover{transform-origin:bottom right}.bp3-popover .bp3-popover-content{background:#fff;color:inherit}.bp3-popover .bp3-popover-arrow:before{box-shadow:1px 1px 6px rgba(16,22,26,.2)}.bp3-popover .bp3-popover-arrow-border{fill:#10161a;fill-opacity:.1}.bp3-popover .bp3-popover-arrow-fill{fill:#fff}.bp3-popover-appear>.bp3-popover,.bp3-popover-enter>.bp3-popover{transform:scale(.3)}.bp3-popover-appear-active>.bp3-popover,.bp3-popover-enter-active>.bp3-popover{transform:scale(1);transition-delay:0;transition-duration:.3s;transition-property:transform;transition-timing-function:cubic-bezier(.54,1.12,.38,1.11)}.bp3-popover-exit>.bp3-popover{transform:scale(1)}.bp3-popover-exit-active>.bp3-popover{transform:scale(.3);transition-delay:0;transition-duration:.3s;transition-property:transform;transition-timing-function:cubic-bezier(.54,1.12,.38,1.11)}.bp3-popover .bp3-popover-content{border-radius:3px;position:relative}.bp3-popover.bp3-popover-content-sizing .bp3-popover-content{max-width:350px;padding:20px}.bp3-popover-target+.bp3-overlay .bp3-popover.bp3-popover-content-sizing{width:350px}.bp3-popover.bp3-minimal{margin:0!important}.bp3-popover.bp3-minimal .bp3-popover-arrow{display:none}.bp3-popover-appear>.bp3-popover.bp3-minimal.bp3-popover,.bp3-popover-enter>.bp3-popover.bp3-minimal.bp3-popover,.bp3-popover.bp3-minimal.bp3-popover{transform:scale(1)}.bp3-popover-appear-active>.bp3-popover.bp3-minimal.bp3-popover,.bp3-popover-enter-active>.bp3-popover.bp3-minimal.bp3-popover{transform:scale(1);transition-delay:0;transition-duration:.1s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-popover-exit>.bp3-popover.bp3-minimal.bp3-popover{transform:scale(1)}.bp3-popover-exit-active>.bp3-popover.bp3-minimal.bp3-popover{transform:scale(1);transition-delay:0;transition-duration:.1s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-dark .bp3-popover,.bp3-popover.bp3-dark{box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4)}.bp3-dark .bp3-popover .bp3-popover-content,.bp3-popover.bp3-dark .bp3-popover-content{background:#30404d;color:inherit}.bp3-dark .bp3-popover .bp3-popover-arrow:before,.bp3-popover.bp3-dark .bp3-popover-arrow:before{box-shadow:1px 1px 6px rgba(16,22,26,.4)}.bp3-dark .bp3-popover .bp3-popover-arrow-border,.bp3-popover.bp3-dark .bp3-popover-arrow-border{fill:#10161a;fill-opacity:.2}.bp3-dark .bp3-popover .bp3-popover-arrow-fill,.bp3-popover.bp3-dark .bp3-popover-arrow-fill{fill:#30404d}.bp3-popover-arrow:before{border-radius:2px;content:"";display:block;position:absolute;transform:rotate(45deg)}.bp3-tether-pinned .bp3-popover-arrow{display:none}.bp3-popover-backdrop{background:hsla(0,0%,100%,0)}.bp3-transition-container{opacity:1;display:flex;z-index:20}.bp3-transition-container.bp3-popover-appear,.bp3-transition-container.bp3-popover-enter{opacity:0}.bp3-transition-container.bp3-popover-appear-active,.bp3-transition-container.bp3-popover-enter-active{opacity:1;transition-delay:0;transition-duration:.1s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-transition-container.bp3-popover-exit{opacity:1}.bp3-transition-container.bp3-popover-exit-active{opacity:0;transition-delay:0;transition-duration:.1s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-transition-container:focus{outline:none}.bp3-transition-container.bp3-popover-leave .bp3-popover-content{pointer-events:none}.bp3-transition-container[data-x-out-of-boundaries]{display:none}span.bp3-popover-target{display:inline-block}.bp3-popover-wrapper.bp3-fill{width:100%}.bp3-portal{left:0;position:absolute;right:0;top:0}@keyframes linear-progress-bar-stripes{0%{background-position:0 0}to{background-position:30px 0}}.bp3-progress-bar{background:rgba(92,112,128,.2);border-radius:40px;display:block;height:8px;overflow:hidden;position:relative;width:100%}.bp3-progress-bar .bp3-progress-meter{background:linear-gradient(-45deg,hsla(0,0%,100%,.2) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.2) 0,hsla(0,0%,100%,.2) 75%,transparent 0);background-color:rgba(92,112,128,.8);background-size:30px 30px;border-radius:40px;height:100%;position:absolute;transition:width .2s cubic-bezier(.4,1,.75,.9);width:100%}.bp3-progress-bar:not(.bp3-no-animation):not(.bp3-no-stripes) .bp3-progress-meter{animation:linear-progress-bar-stripes .3s linear infinite reverse}.bp3-progress-bar.bp3-no-stripes .bp3-progress-meter{background-image:none}.bp3-dark .bp3-progress-bar{background:rgba(16,22,26,.5)}.bp3-dark .bp3-progress-bar .bp3-progress-meter{background-color:#8a9ba8}.bp3-progress-bar.bp3-intent-primary .bp3-progress-meter{background-color:#137cbd}.bp3-progress-bar.bp3-intent-success .bp3-progress-meter{background-color:#0f9960}.bp3-progress-bar.bp3-intent-warning .bp3-progress-meter{background-color:#d9822b}.bp3-progress-bar.bp3-intent-danger .bp3-progress-meter{background-color:#db3737}@keyframes skeleton-glow{0%{background:rgba(206,217,224,.2);border-color:rgba(206,217,224,.2)}to{background:rgba(92,112,128,.2);border-color:rgba(92,112,128,.2)}}.bp3-skeleton{animation:skeleton-glow 1s linear infinite alternate;background:rgba(206,217,224,.2);background-clip:padding-box!important;border-color:rgba(206,217,224,.2)!important;border-radius:2px;box-shadow:none!important;color:transparent!important;cursor:default;pointer-events:none;-webkit-user-select:none;user-select:none}.bp3-skeleton *,.bp3-skeleton:after,.bp3-skeleton:before{visibility:hidden!important}.bp3-slider{height:40px;min-width:150px;width:100%;cursor:default;outline:none;position:relative;-webkit-user-select:none;user-select:none}.bp3-slider:hover{cursor:pointer}.bp3-slider:active{cursor:-webkit-grabbing;cursor:grabbing}.bp3-slider.bp3-disabled{cursor:not-allowed;opacity:.5}.bp3-slider.bp3-slider-unlabeled{height:16px}.bp3-slider-progress,.bp3-slider-track{height:6px;left:0;right:0;top:5px;position:absolute}.bp3-slider-track{border-radius:3px;overflow:hidden}.bp3-slider-progress{background:rgba(92,112,128,.2)}.bp3-dark .bp3-slider-progress{background:rgba(16,22,26,.5)}.bp3-slider-progress.bp3-intent-primary{background-color:#137cbd}.bp3-slider-progress.bp3-intent-success{background-color:#0f9960}.bp3-slider-progress.bp3-intent-warning{background-color:#d9822b}.bp3-slider-progress.bp3-intent-danger{background-color:#db3737}.bp3-slider-handle{background-color:#f5f8fa;background-image:linear-gradient(180deg,hsla(0,0%,100%,.8),hsla(0,0%,100%,0));box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);color:#182026;border-radius:3px;box-shadow:0 0 0 1px rgba(16,22,26,.2),0 1px 1px rgba(16,22,26,.2);cursor:pointer;height:16px;left:0;position:absolute;top:0;width:16px}.bp3-slider-handle.bp3-active,.bp3-slider-handle:active{background-color:#d8e1e8;background-image:none;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-slider-handle.bp3-disabled,.bp3-slider-handle:disabled{background-color:rgba(206,217,224,.5);background-image:none;box-shadow:none;color:rgba(92,112,128,.6);cursor:not-allowed;outline:none}.bp3-slider-handle.bp3-disabled.bp3-active,.bp3-slider-handle.bp3-disabled.bp3-active:hover,.bp3-slider-handle:disabled.bp3-active,.bp3-slider-handle:disabled.bp3-active:hover{background:rgba(206,217,224,.7)}.bp3-slider-handle:focus{z-index:1}.bp3-slider-handle:hover{background-clip:padding-box;background-color:#ebf1f5;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:0 0 0 1px rgba(16,22,26,.2),0 1px 1px rgba(16,22,26,.2);cursor:-webkit-grab;cursor:grab;z-index:2}.bp3-slider-handle.bp3-active{background-color:#d8e1e8;background-image:none;box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:0 0 0 1px rgba(16,22,26,.2),inset 0 1px 1px rgba(16,22,26,.1);cursor:-webkit-grabbing;cursor:grabbing}.bp3-disabled .bp3-slider-handle{background:#bfccd6;box-shadow:none;pointer-events:none}.bp3-dark .bp3-slider-handle{background-color:#394b59;background-image:linear-gradient(180deg,hsla(0,0%,100%,.05),hsla(0,0%,100%,0));box-shadow:0 0 0 1px rgba(16,22,26,.4);color:#f5f8fa}.bp3-dark .bp3-slider-handle.bp3-active,.bp3-dark .bp3-slider-handle:active,.bp3-dark .bp3-slider-handle:hover{color:#f5f8fa}.bp3-dark .bp3-slider-handle:hover{background-color:#30404d;box-shadow:0 0 0 1px rgba(16,22,26,.4)}.bp3-dark .bp3-slider-handle.bp3-active,.bp3-dark .bp3-slider-handle:active{background-color:#202b33;background-image:none;box-shadow:0 0 0 1px rgba(16,22,26,.6),inset 0 1px 2px rgba(16,22,26,.2)}.bp3-dark .bp3-slider-handle.bp3-disabled,.bp3-dark .bp3-slider-handle:disabled{background-color:rgba(57,75,89,.5);background-image:none;box-shadow:none;color:rgba(167,182,194,.6)}.bp3-dark .bp3-slider-handle.bp3-disabled.bp3-active,.bp3-dark .bp3-slider-handle:disabled.bp3-active{background:rgba(57,75,89,.7)}.bp3-dark .bp3-slider-handle .bp3-button-spinner .bp3-spinner-head{background:rgba(16,22,26,.5);stroke:#8a9ba8}.bp3-dark .bp3-slider-handle,.bp3-dark .bp3-slider-handle:hover{background-color:#394b59}.bp3-dark .bp3-slider-handle.bp3-active{background-color:#293742}.bp3-dark .bp3-disabled .bp3-slider-handle{background:#5c7080;border-color:#5c7080;box-shadow:none}.bp3-slider-handle .bp3-slider-label{background:#394b59;border-radius:3px;box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2);color:#f5f8fa;margin-left:8px}.bp3-dark .bp3-slider-handle .bp3-slider-label{background:#e1e8ed;box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4);color:#394b59}.bp3-disabled .bp3-slider-handle .bp3-slider-label{box-shadow:none}.bp3-slider-handle.bp3-end,.bp3-slider-handle.bp3-start{width:8px}.bp3-slider-handle.bp3-start{border-bottom-right-radius:0;border-top-right-radius:0}.bp3-slider-handle.bp3-end{border-bottom-left-radius:0;border-top-left-radius:0;margin-left:8px}.bp3-slider-handle.bp3-end .bp3-slider-label{margin-left:0}.bp3-slider-label{transform:translate(-50%,20px);display:inline-block;font-size:12px;line-height:1;padding:2px 5px;position:absolute;vertical-align:top}.bp3-slider.bp3-vertical{height:150px;min-width:40px;width:40px}.bp3-slider.bp3-vertical .bp3-slider-progress,.bp3-slider.bp3-vertical .bp3-slider-track{bottom:0;height:auto;left:5px;top:0;width:6px}.bp3-slider.bp3-vertical .bp3-slider-progress{top:auto}.bp3-slider.bp3-vertical .bp3-slider-label{transform:translate(20px,50%)}.bp3-slider.bp3-vertical .bp3-slider-handle{top:auto}.bp3-slider.bp3-vertical .bp3-slider-handle .bp3-slider-label{margin-left:0;margin-top:-8px}.bp3-slider.bp3-vertical .bp3-slider-handle.bp3-end,.bp3-slider.bp3-vertical .bp3-slider-handle.bp3-start{height:8px;margin-left:0;width:16px}.bp3-slider.bp3-vertical .bp3-slider-handle.bp3-start{border-bottom-right-radius:3px;border-top-left-radius:0}.bp3-slider.bp3-vertical .bp3-slider-handle.bp3-start .bp3-slider-label{transform:translate(20px)}.bp3-slider.bp3-vertical .bp3-slider-handle.bp3-end{border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:3px;margin-bottom:8px}@keyframes pt-spinner-animation{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.bp3-spinner{align-items:center;display:flex;justify-content:center;overflow:visible;vertical-align:middle}.bp3-spinner svg{display:block}.bp3-spinner path{fill-opacity:0}.bp3-spinner .bp3-spinner-head{stroke:rgba(92,112,128,.8);stroke-linecap:round;transform-origin:center;transition:stroke-dashoffset .2s cubic-bezier(.4,1,.75,.9)}.bp3-spinner .bp3-spinner-track{stroke:rgba(92,112,128,.2)}.bp3-spinner-animation{animation:pt-spinner-animation .5s linear infinite}.bp3-no-spin>.bp3-spinner-animation{animation:none}.bp3-dark .bp3-spinner .bp3-spinner-head{stroke:#8a9ba8}.bp3-dark .bp3-spinner .bp3-spinner-track{stroke:rgba(16,22,26,.5)}.bp3-spinner.bp3-intent-primary .bp3-spinner-head{stroke:#137cbd}.bp3-spinner.bp3-intent-success .bp3-spinner-head{stroke:#0f9960}.bp3-spinner.bp3-intent-warning .bp3-spinner-head{stroke:#d9822b}.bp3-spinner.bp3-intent-danger .bp3-spinner-head{stroke:#db3737}.bp3-tabs.bp3-vertical{display:flex}.bp3-tabs.bp3-vertical>.bp3-tab-list{align-items:flex-start;flex-direction:column}.bp3-tabs.bp3-vertical>.bp3-tab-list .bp3-tab{border-radius:3px;padding:0 10px;width:100%}.bp3-tabs.bp3-vertical>.bp3-tab-list .bp3-tab[aria-selected=true]{background-color:rgba(19,124,189,.2);box-shadow:none}.bp3-tabs.bp3-vertical>.bp3-tab-list .bp3-tab-indicator-wrapper .bp3-tab-indicator{background-color:rgba(19,124,189,.2);border-radius:3px;bottom:0;height:auto;left:0;right:0;top:0}.bp3-tabs.bp3-vertical>.bp3-tab-panel{margin-top:0;padding-left:20px}.bp3-tab-list{align-items:flex-end;border:none;display:flex;flex:0 0 auto;list-style:none;margin:0;padding:0;position:relative}.bp3-tab-list>:not(:last-child){margin-right:20px}.bp3-tab{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;color:#182026;cursor:pointer;flex:0 0 auto;font-size:14px;line-height:30px;max-width:100%;position:relative;vertical-align:top}.bp3-tab a{color:inherit;display:block;text-decoration:none}.bp3-tab-indicator-wrapper~.bp3-tab{background-color:initial!important;box-shadow:none!important}.bp3-tab[aria-disabled=true]{color:rgba(92,112,128,.6);cursor:not-allowed}.bp3-tab[aria-selected=true]{border-radius:0;box-shadow:inset 0 -3px 0 #106ba3}.bp3-tab:not([aria-disabled=true]):hover,.bp3-tab[aria-selected=true]{color:#106ba3}.bp3-tab:focus{-moz-outline-radius:0}.bp3-large>.bp3-tab{font-size:16px;line-height:40px}.bp3-tab-panel{margin-top:20px}.bp3-tab-panel[aria-hidden=true]{display:none}.bp3-tab-indicator-wrapper{left:0;pointer-events:none;position:absolute;top:0;transform:translateX(0),translateY(0);transition:height,transform,width;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-tab-indicator-wrapper .bp3-tab-indicator{background-color:#106ba3;bottom:0;height:3px;left:0;position:absolute;right:0}.bp3-tab-indicator-wrapper.bp3-no-animation{transition:none}.bp3-dark .bp3-tab{color:#f5f8fa}.bp3-dark .bp3-tab[aria-disabled=true]{color:rgba(167,182,194,.6)}.bp3-dark .bp3-tab[aria-selected=true]{box-shadow:inset 0 -3px 0 #48aff0}.bp3-dark .bp3-tab:not([aria-disabled=true]):hover,.bp3-dark .bp3-tab[aria-selected=true]{color:#48aff0}.bp3-dark .bp3-tab-indicator{background-color:#48aff0}.bp3-flex-expander{flex:1 1}.bp3-tag{display:inline-flex;flex-direction:row;align-items:center;background-color:#5c7080;border:none;border-radius:3px;box-shadow:none;color:#f5f8fa;font-size:12px;line-height:16px;max-width:100%;min-height:20px;min-width:20px;padding:2px 6px;position:relative}.bp3-tag.bp3-interactive{cursor:pointer}.bp3-tag.bp3-interactive:hover{background-color:rgba(92,112,128,.85)}.bp3-tag.bp3-interactive.bp3-active,.bp3-tag.bp3-interactive:active{background-color:rgba(92,112,128,.7)}.bp3-tag>*{flex-grow:0;flex-shrink:0}.bp3-tag>.bp3-fill{flex-grow:1;flex-shrink:1}.bp3-tag:before,.bp3-tag>*{margin-right:4px}.bp3-tag:empty:before,.bp3-tag>:last-child{margin-right:0}.bp3-tag:focus{outline:2px auto rgba(19,124,189,.6);outline-offset:0;-moz-outline-radius:6px}.bp3-tag.bp3-round{border-radius:30px;padding-left:8px;padding-right:8px}.bp3-dark .bp3-tag{background-color:#bfccd6;color:#182026}.bp3-dark .bp3-tag.bp3-interactive{cursor:pointer}.bp3-dark .bp3-tag.bp3-interactive:hover{background-color:rgba(191,204,214,.85)}.bp3-dark .bp3-tag.bp3-interactive.bp3-active,.bp3-dark .bp3-tag.bp3-interactive:active{background-color:rgba(191,204,214,.7)}.bp3-dark .bp3-tag .bp3-icon-large,.bp3-dark .bp3-tag .bp3-icon-standard,.bp3-dark .bp3-tag>.bp3-icon{fill:currentColor}.bp3-tag .bp3-icon-large,.bp3-tag .bp3-icon-standard,.bp3-tag>.bp3-icon{fill:#fff}.bp3-large .bp3-tag,.bp3-tag.bp3-large{font-size:14px;line-height:20px;min-height:30px;min-width:30px;padding:5px 10px}.bp3-large .bp3-tag:before,.bp3-large .bp3-tag>*,.bp3-tag.bp3-large:before,.bp3-tag.bp3-large>*{margin-right:7px}.bp3-large .bp3-tag:empty:before,.bp3-large .bp3-tag>:last-child,.bp3-tag.bp3-large:empty:before,.bp3-tag.bp3-large>:last-child{margin-right:0}.bp3-large .bp3-tag.bp3-round,.bp3-tag.bp3-large.bp3-round{padding-left:12px;padding-right:12px}.bp3-tag.bp3-intent-primary{background:#137cbd;color:#fff}.bp3-tag.bp3-intent-primary.bp3-interactive{cursor:pointer}.bp3-tag.bp3-intent-primary.bp3-interactive:hover{background-color:rgba(19,124,189,.85)}.bp3-tag.bp3-intent-primary.bp3-interactive.bp3-active,.bp3-tag.bp3-intent-primary.bp3-interactive:active{background-color:rgba(19,124,189,.7)}.bp3-tag.bp3-intent-success{background:#0f9960;color:#fff}.bp3-tag.bp3-intent-success.bp3-interactive{cursor:pointer}.bp3-tag.bp3-intent-success.bp3-interactive:hover{background-color:rgba(15,153,96,.85)}.bp3-tag.bp3-intent-success.bp3-interactive.bp3-active,.bp3-tag.bp3-intent-success.bp3-interactive:active{background-color:rgba(15,153,96,.7)}.bp3-tag.bp3-intent-warning{background:#d9822b;color:#fff}.bp3-tag.bp3-intent-warning.bp3-interactive{cursor:pointer}.bp3-tag.bp3-intent-warning.bp3-interactive:hover{background-color:rgba(217,130,43,.85)}.bp3-tag.bp3-intent-warning.bp3-interactive.bp3-active,.bp3-tag.bp3-intent-warning.bp3-interactive:active{background-color:rgba(217,130,43,.7)}.bp3-tag.bp3-intent-danger{background:#db3737;color:#fff}.bp3-tag.bp3-intent-danger.bp3-interactive{cursor:pointer}.bp3-tag.bp3-intent-danger.bp3-interactive:hover{background-color:rgba(219,55,55,.85)}.bp3-tag.bp3-intent-danger.bp3-interactive.bp3-active,.bp3-tag.bp3-intent-danger.bp3-interactive:active{background-color:rgba(219,55,55,.7)}.bp3-tag.bp3-fill{display:flex;width:100%}.bp3-tag.bp3-minimal .bp3-icon-large,.bp3-tag.bp3-minimal .bp3-icon-standard,.bp3-tag.bp3-minimal>.bp3-icon{fill:#5c7080}.bp3-tag.bp3-minimal:not([class*=bp3-intent-]){background-color:rgba(138,155,168,.2);color:#182026}.bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive{cursor:pointer}.bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive:hover{background-color:rgba(92,112,128,.3)}.bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive.bp3-active,.bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive:active{background-color:rgba(92,112,128,.4)}.bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]){color:#f5f8fa}.bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive{cursor:pointer}.bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive:hover{background-color:rgba(191,204,214,.3)}.bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive.bp3-active,.bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive:active{background-color:rgba(191,204,214,.4)}.bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]) .bp3-icon-large,.bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]) .bp3-icon-standard,.bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-])>.bp3-icon{fill:#a7b6c2}.bp3-tag.bp3-minimal.bp3-intent-primary{background-color:rgba(19,124,189,.15);color:#106ba3}.bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive{cursor:pointer}.bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive:hover{background-color:rgba(19,124,189,.25)}.bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive.bp3-active,.bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive:active{background-color:rgba(19,124,189,.35)}.bp3-tag.bp3-minimal.bp3-intent-primary .bp3-icon-large,.bp3-tag.bp3-minimal.bp3-intent-primary .bp3-icon-standard,.bp3-tag.bp3-minimal.bp3-intent-primary>.bp3-icon{fill:#137cbd}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-primary{background-color:rgba(19,124,189,.25);color:#48aff0}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive{cursor:pointer}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive:hover{background-color:rgba(19,124,189,.35)}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive.bp3-active,.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive:active{background-color:rgba(19,124,189,.45)}.bp3-tag.bp3-minimal.bp3-intent-success{background-color:rgba(15,153,96,.15);color:#0d8050}.bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive{cursor:pointer}.bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive:hover{background-color:rgba(15,153,96,.25)}.bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive.bp3-active,.bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive:active{background-color:rgba(15,153,96,.35)}.bp3-tag.bp3-minimal.bp3-intent-success .bp3-icon-large,.bp3-tag.bp3-minimal.bp3-intent-success .bp3-icon-standard,.bp3-tag.bp3-minimal.bp3-intent-success>.bp3-icon{fill:#0f9960}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-success{background-color:rgba(15,153,96,.25);color:#3dcc91}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive{cursor:pointer}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive:hover{background-color:rgba(15,153,96,.35)}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive.bp3-active,.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive:active{background-color:rgba(15,153,96,.45)}.bp3-tag.bp3-minimal.bp3-intent-warning{background-color:rgba(217,130,43,.15);color:#bf7326}.bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive{cursor:pointer}.bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive:hover{background-color:rgba(217,130,43,.25)}.bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive.bp3-active,.bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive:active{background-color:rgba(217,130,43,.35)}.bp3-tag.bp3-minimal.bp3-intent-warning .bp3-icon-large,.bp3-tag.bp3-minimal.bp3-intent-warning .bp3-icon-standard,.bp3-tag.bp3-minimal.bp3-intent-warning>.bp3-icon{fill:#d9822b}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-warning{background-color:rgba(217,130,43,.25);color:#ffb366}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive{cursor:pointer}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive:hover{background-color:rgba(217,130,43,.35)}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive.bp3-active,.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive:active{background-color:rgba(217,130,43,.45)}.bp3-tag.bp3-minimal.bp3-intent-danger{background-color:rgba(219,55,55,.15);color:#c23030}.bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive{cursor:pointer}.bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive:hover{background-color:rgba(219,55,55,.25)}.bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive.bp3-active,.bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive:active{background-color:rgba(219,55,55,.35)}.bp3-tag.bp3-minimal.bp3-intent-danger .bp3-icon-large,.bp3-tag.bp3-minimal.bp3-intent-danger .bp3-icon-standard,.bp3-tag.bp3-minimal.bp3-intent-danger>.bp3-icon{fill:#db3737}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-danger{background-color:rgba(219,55,55,.25);color:#ff7373}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive{cursor:pointer}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive:hover{background-color:rgba(219,55,55,.35)}.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive.bp3-active,.bp3-dark .bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive:active{background-color:rgba(219,55,55,.45)}.bp3-tag-remove{background:none;border:none;color:inherit;cursor:pointer;display:flex;margin-bottom:-2px;margin-right:-6px!important;margin-top:-2px;opacity:.5;padding:2px 2px 2px 0}.bp3-tag-remove:hover{background:none;opacity:.8;text-decoration:none}.bp3-tag-remove:active{opacity:1}.bp3-tag-remove:empty:before{font-family:Icons16,sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;content:"\E6D7"}.bp3-large .bp3-tag-remove{margin-right:-10px!important;padding:5px 5px 5px 0}.bp3-large .bp3-tag-remove:empty:before{font-family:Icons20,sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1}.bp3-tag-input{display:flex;flex-direction:row;align-items:flex-start;cursor:text;height:auto;line-height:inherit;min-height:30px;padding-left:5px;padding-right:0}.bp3-tag-input>*{flex-grow:0;flex-shrink:0}.bp3-tag-input>.bp3-tag-input-values{flex-grow:1;flex-shrink:1}.bp3-tag-input .bp3-tag-input-icon{color:#5c7080;margin-left:2px;margin-right:7px;margin-top:7px}.bp3-tag-input .bp3-tag-input-values{display:flex;flex-direction:row;align-items:center;align-self:stretch;flex-wrap:wrap;margin-right:7px;margin-top:5px;min-width:0}.bp3-tag-input .bp3-tag-input-values>*{flex-grow:0;flex-shrink:0}.bp3-tag-input .bp3-tag-input-values>.bp3-fill{flex-grow:1;flex-shrink:1}.bp3-tag-input .bp3-tag-input-values:before,.bp3-tag-input .bp3-tag-input-values>*{margin-right:5px}.bp3-tag-input .bp3-tag-input-values:empty:before,.bp3-tag-input .bp3-tag-input-values>:last-child{margin-right:0}.bp3-tag-input .bp3-tag-input-values:first-child .bp3-input-ghost:first-child{padding-left:5px}.bp3-tag-input .bp3-tag-input-values>*{margin-bottom:5px}.bp3-tag-input .bp3-tag{overflow-wrap:break-word}.bp3-tag-input .bp3-tag.bp3-active{outline:2px auto rgba(19,124,189,.6);outline-offset:0;-moz-outline-radius:6px}.bp3-tag-input .bp3-input-ghost{flex:1 1 auto;line-height:20px;width:80px}.bp3-tag-input .bp3-input-ghost.bp3-disabled,.bp3-tag-input .bp3-input-ghost:disabled{cursor:not-allowed}.bp3-tag-input .bp3-button,.bp3-tag-input .bp3-spinner{margin:3px 3px 3px 0}.bp3-tag-input .bp3-button{min-height:24px;min-width:24px;padding:0 7px}.bp3-tag-input.bp3-large{height:auto;min-height:40px}.bp3-tag-input.bp3-large:before,.bp3-tag-input.bp3-large>*{margin-right:10px}.bp3-tag-input.bp3-large:empty:before,.bp3-tag-input.bp3-large>:last-child{margin-right:0}.bp3-tag-input.bp3-large .bp3-tag-input-icon{margin-left:5px;margin-top:10px}.bp3-tag-input.bp3-large .bp3-input-ghost{line-height:30px}.bp3-tag-input.bp3-large .bp3-button{min-height:30px;min-width:30px;padding:5px 10px;margin:5px 5px 5px 0}.bp3-tag-input.bp3-large .bp3-spinner{margin:8px 8px 8px 0}.bp3-tag-input.bp3-active{background-color:#fff;box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-tag-input.bp3-active.bp3-intent-primary{box-shadow:0 0 0 1px #106ba3,0 0 0 3px rgba(16,107,163,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-tag-input.bp3-active.bp3-intent-success{box-shadow:0 0 0 1px #0d8050,0 0 0 3px rgba(13,128,80,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-tag-input.bp3-active.bp3-intent-warning{box-shadow:0 0 0 1px #bf7326,0 0 0 3px rgba(191,115,38,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-tag-input.bp3-active.bp3-intent-danger{box-shadow:0 0 0 1px #c23030,0 0 0 3px rgba(194,48,48,.3),inset 0 1px 1px rgba(16,22,26,.2)}.bp3-dark .bp3-tag-input .bp3-tag-input-icon,.bp3-tag-input.bp3-dark .bp3-tag-input-icon{color:#a7b6c2}.bp3-dark .bp3-tag-input .bp3-input-ghost,.bp3-tag-input.bp3-dark .bp3-input-ghost{color:#f5f8fa}.bp3-dark .bp3-tag-input .bp3-input-ghost::-webkit-input-placeholder,.bp3-tag-input.bp3-dark .bp3-input-ghost::-webkit-input-placeholder{color:rgba(167,182,194,.6)}.bp3-dark .bp3-tag-input .bp3-input-ghost::placeholder,.bp3-tag-input.bp3-dark .bp3-input-ghost::placeholder{color:rgba(167,182,194,.6)}.bp3-dark .bp3-tag-input.bp3-active,.bp3-tag-input.bp3-dark.bp3-active{background-color:rgba(16,22,26,.3);box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-tag-input.bp3-active.bp3-intent-primary,.bp3-tag-input.bp3-dark.bp3-active.bp3-intent-primary{box-shadow:0 0 0 1px #106ba3,0 0 0 3px rgba(16,107,163,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-tag-input.bp3-active.bp3-intent-success,.bp3-tag-input.bp3-dark.bp3-active.bp3-intent-success{box-shadow:0 0 0 1px #0d8050,0 0 0 3px rgba(13,128,80,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-tag-input.bp3-active.bp3-intent-warning,.bp3-tag-input.bp3-dark.bp3-active.bp3-intent-warning{box-shadow:0 0 0 1px #bf7326,0 0 0 3px rgba(191,115,38,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-dark .bp3-tag-input.bp3-active.bp3-intent-danger,.bp3-tag-input.bp3-dark.bp3-active.bp3-intent-danger{box-shadow:0 0 0 1px #c23030,0 0 0 3px rgba(194,48,48,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4)}.bp3-input-ghost{background:none;border:none;box-shadow:none;padding:0}.bp3-input-ghost::-webkit-input-placeholder{color:rgba(92,112,128,.6);opacity:1}.bp3-input-ghost::placeholder{color:rgba(92,112,128,.6);opacity:1}.bp3-input-ghost:focus{outline:none!important}.bp3-toast{align-items:flex-start;background-color:#fff;border-radius:3px;box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2);display:flex;margin:20px 0 0;max-width:500px;min-width:300px;pointer-events:all;position:relative!important}.bp3-toast.bp3-toast-appear,.bp3-toast.bp3-toast-enter{transform:translateY(-40px)}.bp3-toast.bp3-toast-appear-active,.bp3-toast.bp3-toast-enter-active{transform:translateY(0);transition-delay:0;transition-duration:.3s;transition-property:transform;transition-timing-function:cubic-bezier(.54,1.12,.38,1.11)}.bp3-toast.bp3-toast-appear~.bp3-toast,.bp3-toast.bp3-toast-enter~.bp3-toast{transform:translateY(-40px)}.bp3-toast.bp3-toast-appear-active~.bp3-toast,.bp3-toast.bp3-toast-enter-active~.bp3-toast{transform:translateY(0);transition-delay:0;transition-duration:.3s;transition-property:transform;transition-timing-function:cubic-bezier(.54,1.12,.38,1.11)}.bp3-toast.bp3-toast-exit{opacity:1;-webkit-filter:blur(0);filter:blur(0)}.bp3-toast.bp3-toast-exit-active{opacity:0;-webkit-filter:blur(10px);filter:blur(10px);transition-delay:0;transition-duration:.3s;transition-property:opacity,-webkit-filter;transition-property:opacity,filter;transition-property:opacity,filter,-webkit-filter;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-toast.bp3-toast-exit~.bp3-toast{transform:translateY(0)}.bp3-toast.bp3-toast-exit-active~.bp3-toast{transform:translateY(-40px);transition-delay:50ms;transition-duration:.1s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-toast .bp3-button-group{flex:0 0 auto;padding:5px 5px 5px 0}.bp3-toast>.bp3-icon{color:#5c7080;margin:12px 0 12px 12px}.bp3-dark .bp3-toast,.bp3-toast.bp3-dark{background-color:#394b59;box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4)}.bp3-dark .bp3-toast>.bp3-icon,.bp3-toast.bp3-dark>.bp3-icon{color:#a7b6c2}.bp3-toast[class*=bp3-intent-] a{color:hsla(0,0%,100%,.7)}.bp3-toast[class*=bp3-intent-]>.bp3-icon,.bp3-toast[class*=bp3-intent-] a:hover{color:#fff}.bp3-toast[class*=bp3-intent-] .bp3-button,.bp3-toast[class*=bp3-intent-] .bp3-button .bp3-icon,.bp3-toast[class*=bp3-intent-] .bp3-button:active,.bp3-toast[class*=bp3-intent-] .bp3-button:before{color:hsla(0,0%,100%,.7)!important}.bp3-toast[class*=bp3-intent-] .bp3-button:focus{outline-color:hsla(0,0%,100%,.5)}.bp3-toast[class*=bp3-intent-] .bp3-button:hover{background-color:hsla(0,0%,100%,.15)!important;color:#fff!important}.bp3-toast[class*=bp3-intent-] .bp3-button:active{background-color:hsla(0,0%,100%,.3)!important;color:#fff!important}.bp3-toast[class*=bp3-intent-] .bp3-button:after{background:hsla(0,0%,100%,.3)!important}.bp3-toast.bp3-intent-primary{background-color:#137cbd;color:#fff}.bp3-toast.bp3-intent-success{background-color:#0f9960;color:#fff}.bp3-toast.bp3-intent-warning{background-color:#d9822b;color:#fff}.bp3-toast.bp3-intent-danger{background-color:#db3737;color:#fff}.bp3-toast-message{flex:1 1 auto;padding:11px;word-break:break-word}.bp3-toast-container{align-items:center;display:flex!important;flex-direction:column;left:0;overflow:hidden;padding:0 20px 20px;pointer-events:none;position:fixed;right:0;z-index:40}.bp3-toast-container.bp3-toast-container-top{top:0}.bp3-toast-container.bp3-toast-container-bottom{bottom:0;flex-direction:column-reverse;top:auto}.bp3-toast-container.bp3-toast-container-left{align-items:flex-start}.bp3-toast-container.bp3-toast-container-right{align-items:flex-end}.bp3-toast-container-bottom .bp3-toast.bp3-toast-appear:not(.bp3-toast-appear-active),.bp3-toast-container-bottom .bp3-toast.bp3-toast-appear:not(.bp3-toast-appear-active)~.bp3-toast,.bp3-toast-container-bottom .bp3-toast.bp3-toast-enter:not(.bp3-toast-enter-active),.bp3-toast-container-bottom .bp3-toast.bp3-toast-enter:not(.bp3-toast-enter-active)~.bp3-toast,.bp3-toast-container-bottom .bp3-toast.bp3-toast-exit-active~.bp3-toast,.bp3-toast-container-bottom .bp3-toast.bp3-toast-leave-active~.bp3-toast{transform:translateY(60px)}.bp3-tooltip{box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2);transform:scale(1)}.bp3-tooltip .bp3-popover-arrow{height:22px;position:absolute;width:22px}.bp3-tooltip .bp3-popover-arrow:before{height:14px;margin:4px;width:14px}.bp3-tether-element-attached-bottom.bp3-tether-target-attached-top>.bp3-tooltip{margin-bottom:11px;margin-top:-11px}.bp3-tether-element-attached-bottom.bp3-tether-target-attached-top>.bp3-tooltip>.bp3-popover-arrow{bottom:-8px}.bp3-tether-element-attached-bottom.bp3-tether-target-attached-top>.bp3-tooltip>.bp3-popover-arrow svg{transform:rotate(-90deg)}.bp3-tether-element-attached-left.bp3-tether-target-attached-right>.bp3-tooltip{margin-left:11px}.bp3-tether-element-attached-left.bp3-tether-target-attached-right>.bp3-tooltip>.bp3-popover-arrow{left:-8px}.bp3-tether-element-attached-left.bp3-tether-target-attached-right>.bp3-tooltip>.bp3-popover-arrow svg{transform:rotate(0)}.bp3-tether-element-attached-top.bp3-tether-target-attached-bottom>.bp3-tooltip{margin-top:11px}.bp3-tether-element-attached-top.bp3-tether-target-attached-bottom>.bp3-tooltip>.bp3-popover-arrow{top:-8px}.bp3-tether-element-attached-top.bp3-tether-target-attached-bottom>.bp3-tooltip>.bp3-popover-arrow svg{transform:rotate(90deg)}.bp3-tether-element-attached-right.bp3-tether-target-attached-left>.bp3-tooltip{margin-left:-11px;margin-right:11px}.bp3-tether-element-attached-right.bp3-tether-target-attached-left>.bp3-tooltip>.bp3-popover-arrow{right:-8px}.bp3-tether-element-attached-right.bp3-tether-target-attached-left>.bp3-tooltip>.bp3-popover-arrow svg{transform:rotate(180deg)}.bp3-tether-element-attached-middle>.bp3-tooltip>.bp3-popover-arrow{top:50%;transform:translateY(-50%)}.bp3-tether-element-attached-center>.bp3-tooltip>.bp3-popover-arrow{right:50%;transform:translateX(50%)}.bp3-tether-element-attached-top.bp3-tether-target-attached-top>.bp3-tooltip>.bp3-popover-arrow{top:-.22183px}.bp3-tether-element-attached-right.bp3-tether-target-attached-right>.bp3-tooltip>.bp3-popover-arrow{right:-.22183px}.bp3-tether-element-attached-left.bp3-tether-target-attached-left>.bp3-tooltip>.bp3-popover-arrow{left:-.22183px}.bp3-tether-element-attached-bottom.bp3-tether-target-attached-bottom>.bp3-tooltip>.bp3-popover-arrow{bottom:-.22183px}.bp3-tether-element-attached-top.bp3-tether-element-attached-left>.bp3-tooltip{transform-origin:top left}.bp3-tether-element-attached-top.bp3-tether-element-attached-center>.bp3-tooltip{transform-origin:top center}.bp3-tether-element-attached-top.bp3-tether-element-attached-right>.bp3-tooltip{transform-origin:top right}.bp3-tether-element-attached-middle.bp3-tether-element-attached-left>.bp3-tooltip{transform-origin:center left}.bp3-tether-element-attached-middle.bp3-tether-element-attached-center>.bp3-tooltip{transform-origin:center center}.bp3-tether-element-attached-middle.bp3-tether-element-attached-right>.bp3-tooltip{transform-origin:center right}.bp3-tether-element-attached-bottom.bp3-tether-element-attached-left>.bp3-tooltip{transform-origin:bottom left}.bp3-tether-element-attached-bottom.bp3-tether-element-attached-center>.bp3-tooltip{transform-origin:bottom center}.bp3-tether-element-attached-bottom.bp3-tether-element-attached-right>.bp3-tooltip{transform-origin:bottom right}.bp3-tooltip .bp3-popover-content{background:#394b59;color:#f5f8fa}.bp3-tooltip .bp3-popover-arrow:before{box-shadow:1px 1px 6px rgba(16,22,26,.2)}.bp3-tooltip .bp3-popover-arrow-border{fill:#10161a;fill-opacity:.1}.bp3-tooltip .bp3-popover-arrow-fill{fill:#394b59}.bp3-popover-appear>.bp3-tooltip,.bp3-popover-enter>.bp3-tooltip{transform:scale(.8)}.bp3-popover-appear-active>.bp3-tooltip,.bp3-popover-enter-active>.bp3-tooltip{transform:scale(1);transition-delay:0;transition-duration:.1s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-popover-exit>.bp3-tooltip{transform:scale(1)}.bp3-popover-exit-active>.bp3-tooltip{transform:scale(.8);transition-delay:0;transition-duration:.1s;transition-property:transform;transition-timing-function:cubic-bezier(.4,1,.75,.9)}.bp3-tooltip .bp3-popover-content{padding:10px 12px}.bp3-dark .bp3-tooltip,.bp3-tooltip.bp3-dark{box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4)}.bp3-dark .bp3-tooltip .bp3-popover-content,.bp3-tooltip.bp3-dark .bp3-popover-content{background:#e1e8ed;color:#394b59}.bp3-dark .bp3-tooltip .bp3-popover-arrow:before,.bp3-tooltip.bp3-dark .bp3-popover-arrow:before{box-shadow:1px 1px 6px rgba(16,22,26,.4)}.bp3-dark .bp3-tooltip .bp3-popover-arrow-border,.bp3-tooltip.bp3-dark .bp3-popover-arrow-border{fill:#10161a;fill-opacity:.2}.bp3-dark .bp3-tooltip .bp3-popover-arrow-fill,.bp3-tooltip.bp3-dark .bp3-popover-arrow-fill{fill:#e1e8ed}.bp3-tooltip.bp3-intent-primary .bp3-popover-content{background:#137cbd;color:#fff}.bp3-tooltip.bp3-intent-primary .bp3-popover-arrow-fill{fill:#137cbd}.bp3-tooltip.bp3-intent-success .bp3-popover-content{background:#0f9960;color:#fff}.bp3-tooltip.bp3-intent-success .bp3-popover-arrow-fill{fill:#0f9960}.bp3-tooltip.bp3-intent-warning .bp3-popover-content{background:#d9822b;color:#fff}.bp3-tooltip.bp3-intent-warning .bp3-popover-arrow-fill{fill:#d9822b}.bp3-tooltip.bp3-intent-danger .bp3-popover-content{background:#db3737;color:#fff}.bp3-tooltip.bp3-intent-danger .bp3-popover-arrow-fill{fill:#db3737}.bp3-tooltip-indicator{border-bottom:1px dotted;cursor:help}.bp3-tree .bp3-icon,.bp3-tree .bp3-icon-large,.bp3-tree .bp3-icon-standard{color:#5c7080}.bp3-tree .bp3-icon-large.bp3-intent-primary,.bp3-tree .bp3-icon-standard.bp3-intent-primary,.bp3-tree .bp3-icon.bp3-intent-primary{color:#137cbd}.bp3-tree .bp3-icon-large.bp3-intent-success,.bp3-tree .bp3-icon-standard.bp3-intent-success,.bp3-tree .bp3-icon.bp3-intent-success{color:#0f9960}.bp3-tree .bp3-icon-large.bp3-intent-warning,.bp3-tree .bp3-icon-standard.bp3-intent-warning,.bp3-tree .bp3-icon.bp3-intent-warning{color:#d9822b}.bp3-tree .bp3-icon-large.bp3-intent-danger,.bp3-tree .bp3-icon-standard.bp3-intent-danger,.bp3-tree .bp3-icon.bp3-intent-danger{color:#db3737}.bp3-tree-node-list{list-style:none;margin:0;padding-left:0}.bp3-tree-root{background-color:initial;cursor:default;padding-left:0;position:relative}.bp3-tree-node-content-0{padding-left:0}.bp3-tree-node-content-1{padding-left:23px}.bp3-tree-node-content-2{padding-left:46px}.bp3-tree-node-content-3{padding-left:69px}.bp3-tree-node-content-4{padding-left:92px}.bp3-tree-node-content-5{padding-left:115px}.bp3-tree-node-content-6{padding-left:138px}.bp3-tree-node-content-7{padding-left:161px}.bp3-tree-node-content-8{padding-left:184px}.bp3-tree-node-content-9{padding-left:207px}.bp3-tree-node-content-10{padding-left:230px}.bp3-tree-node-content-11{padding-left:253px}.bp3-tree-node-content-12{padding-left:276px}.bp3-tree-node-content-13{padding-left:299px}.bp3-tree-node-content-14{padding-left:322px}.bp3-tree-node-content-15{padding-left:345px}.bp3-tree-node-content-16{padding-left:368px}.bp3-tree-node-content-17{padding-left:391px}.bp3-tree-node-content-18{padding-left:414px}.bp3-tree-node-content-19{padding-left:437px}.bp3-tree-node-content-20{padding-left:460px}.bp3-tree-node-content{align-items:center;display:flex;height:30px;padding-right:5px;width:100%}.bp3-tree-node-content:hover{background-color:rgba(191,204,214,.4)}.bp3-tree-node-caret,.bp3-tree-node-caret-none{min-width:30px}.bp3-tree-node-caret{color:#5c7080;cursor:pointer;padding:7px;transform:rotate(0deg);transition:transform .2s cubic-bezier(.4,1,.75,.9)}.bp3-tree-node-caret:hover{color:#182026}.bp3-dark .bp3-tree-node-caret{color:#a7b6c2}.bp3-dark .bp3-tree-node-caret:hover{color:#f5f8fa}.bp3-tree-node-caret.bp3-tree-node-caret-open{transform:rotate(90deg)}.bp3-tree-node-caret.bp3-icon-standard:before{content:"\E695"}.bp3-tree-node-icon{margin-right:7px;position:relative}.bp3-tree-node-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;flex:1 1 auto;position:relative;-webkit-user-select:none;user-select:none}.bp3-tree-node-label span{display:inline}.bp3-tree-node-secondary-label{padding:0 5px;-webkit-user-select:none;user-select:none}.bp3-tree-node-secondary-label .bp3-popover-target,.bp3-tree-node-secondary-label .bp3-popover-wrapper{align-items:center;display:flex}.bp3-tree-node.bp3-disabled .bp3-tree-node-content{background-color:inherit;color:rgba(92,112,128,.6);cursor:not-allowed}.bp3-tree-node.bp3-disabled .bp3-tree-node-caret,.bp3-tree-node.bp3-disabled .bp3-tree-node-icon{color:rgba(92,112,128,.6);cursor:not-allowed}.bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content{background-color:#137cbd}.bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content,.bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content .bp3-icon,.bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content .bp3-icon-large,.bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content .bp3-icon-standard{color:#fff}.bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content .bp3-tree-node-caret:before{color:hsla(0,0%,100%,.7)}.bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content .bp3-tree-node-caret:hover:before{color:#fff}.bp3-dark .bp3-tree-node-content:hover{background-color:rgba(92,112,128,.3)}.bp3-dark .bp3-tree .bp3-icon,.bp3-dark .bp3-tree .bp3-icon-large,.bp3-dark .bp3-tree .bp3-icon-standard{color:#a7b6c2}.bp3-dark .bp3-tree .bp3-icon-large.bp3-intent-primary,.bp3-dark .bp3-tree .bp3-icon-standard.bp3-intent-primary,.bp3-dark .bp3-tree .bp3-icon.bp3-intent-primary{color:#137cbd}.bp3-dark .bp3-tree .bp3-icon-large.bp3-intent-success,.bp3-dark .bp3-tree .bp3-icon-standard.bp3-intent-success,.bp3-dark .bp3-tree .bp3-icon.bp3-intent-success{color:#0f9960}.bp3-dark .bp3-tree .bp3-icon-large.bp3-intent-warning,.bp3-dark .bp3-tree .bp3-icon-standard.bp3-intent-warning,.bp3-dark .bp3-tree .bp3-icon.bp3-intent-warning{color:#d9822b}.bp3-dark .bp3-tree .bp3-icon-large.bp3-intent-danger,.bp3-dark .bp3-tree .bp3-icon-standard.bp3-intent-danger,.bp3-dark .bp3-tree .bp3-icon.bp3-intent-danger{color:#db3737}.bp3-dark .bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content{background-color:#137cbd} -/*# sourceMappingURL=1.a970d4a6.chunk.css.map */ \ No newline at end of file diff --git a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/css/1.a970d4a6.chunk.css.map b/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/css/1.a970d4a6.chunk.css.map deleted file mode 100644 index 257fd0fa..00000000 --- a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/css/1.a970d4a6.chunk.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/normalize.css/normalize.css","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/icons/src/_font-face.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/icons/src/_font-imports.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/_reset.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/common/_mixins.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/common/_variables.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/common/_colors.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/common/_color-aliases.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/_typography.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/accessibility/_focus-states.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/alert/_alert.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/breadcrumbs/_breadcrumbs.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/button/_button.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/common/_flex.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/button/_common.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/node_modules/@blueprintjs/icons/src/_icons.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/button/_button-group.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/callout/_callout.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/card/_card.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/collapse/_collapse.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/context-menu/_context-menu.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/dialog/_dialog.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/common/_react-transition.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/drawer/_drawer.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/editable-text/_editable-text.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/forms/_common.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/divider/_divider.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/forms/_control-group.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/forms/_controls.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/forms/_file-input.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/forms/_form-group.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/forms/_input-group.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/forms/_input.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/forms/_label.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/forms/_numeric-input.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/forms/_index.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/html-select/_html-select.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/html-select/_common.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/node_modules/@blueprintjs/icons/src/generated/_icon-variables.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/html-table/_html-table.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/hotkeys/_hotkeys.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/icon/_icon.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/menu/_submenu.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/menu/_common.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/menu/_menu.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/navbar/_navbar.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/non-ideal-state/_non-ideal-state.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/overflow-list/_overflow-list.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/overlay/_overlay.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/panel-stack/_panel-stack.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/node_modules/@blueprintjs/core/src/common/_react-transition.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/popover/_popover.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/popover/_common.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/portal/_portal.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/progress-bar/_progress-bar.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/skeleton/_skeleton.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/skeleton/_common.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/slider/_slider.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/slider/_common.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/spinner/_spinner.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/tabs/_tabs.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/tag/_tag.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/tag/_common.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/tag-input/_tag-input.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/toast/_toast.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/tooltip/_tooltip.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/tooltip/_common.scss","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/node_modules/@blueprintjs/core/src/components/tree/_tree.scss"],"names":[],"mappings":"AAAA,4EAA4E,AAU5E,KACE,iBAAkB,AAClB,6BAA+B,CAChC,AASD,KACE,QAAU,CACX,AAMD,KACE,aAAe,CAChB,AAOD,GACE,cAAe,AACf,cAAiB,CAClB,AAUD,GACE,mBAAwB,AACxB,SAAU,AACV,gBAAkB,CACnB,AAOD,IACE,gCAAkC,AAClC,aAAe,CAChB,AASD,EACE,wBAA8B,CAC/B,AAOD,YACE,mBAAoB,AACpB,0BAA2B,AAC3B,yCAAkC,AAAlC,gCAAkC,CACnC,AAMD,SAEE,kBAAoB,CACrB,AAOD,cAGE,gCAAkC,AAClC,aAAe,CAChB,AAMD,MACE,aAAe,CAChB,AAOD,QAEE,cAAe,AACf,cAAe,AACf,kBAAmB,AACnB,sBAAyB,CAC1B,AAED,IACE,aAAgB,CACjB,AAED,IACE,SAAY,CACb,AASD,IACE,iBAAmB,CACpB,AAUD,sCAKE,oBAAqB,AACrB,eAAgB,AAChB,iBAAkB,AAClB,QAAU,CACX,AAOD,aAEE,gBAAkB,CACnB,AAOD,cAEE,mBAAqB,CACtB,AAMD,gDAIE,yBAA2B,CAC5B,AAMD,wHAIE,kBAAmB,AACnB,SAAW,CACZ,AAMD,4GAIE,6BAA+B,CAChC,AAMD,SACE,0BAA+B,CAChC,AASD,OACE,sBAAuB,AACvB,cAAe,AACf,cAAe,AACf,eAAgB,AAChB,UAAW,AACX,kBAAoB,CACrB,AAMD,SACE,sBAAyB,CAC1B,AAMD,SACE,aAAe,CAChB,AAOD,6BAEE,sBAAuB,AACvB,SAAW,CACZ,AAMD,kFAEE,WAAa,CACd,AAOD,cACE,6BAA8B,AAC9B,mBAAqB,CACtB,AAMD,yCACE,uBAAyB,CAC1B,AAOD,6BACE,0BAA2B,AAC3B,YAAc,CACf,AASD,QACE,aAAe,CAChB,AAMD,QACE,iBAAmB,CACpB,AAiBD,kBACE,YAAc,CACf,AC1TC,WACE,oBC3BwB,AD4BxB,kBAJiB,AAKjB,gBANkB,AAQlB,mNAiB6C,CAAA,AAtB/C,WACE,oBC1BwB,AD2BxB,kBAJiB,AAKjB,gBANkB,AAQlB,mNAiB6C,CAAA,AEjDjD,KACE,qBAAsB,CAAA,AAIxB,iBAGE,kBAAmB,CAAA,AAKrB,KCoBE,eCXgC,ADYhC,gBAAgB,AAChB,iBAAiB,AACjB,oBCToD,ADUpD,oBAAoB,ADtBpB,cGfkB,AHgBlB,gIEEyB,CAAA,AFC3B,EACE,mBEfiB,AFgBjB,YAAa,CAAA,AAGf,MACE,cEDsC,CAAA,AFIxC,OACE,eAAgB,CAAA,AAIlB,YACE,+BIjBgD,CAAA,ACClD,aJ4BE,cE9CkB,AF+ClB,gBAAgB,AI3BhB,gBHfiB,AGgBjB,SAAU,CAAA,AJ4BV,uBACE,aElCiB,CAAA,AEmBnB,oCACE,eAVS,AAWT,gBAXe,CAAA,AASjB,oCACE,eATS,AAUT,gBAVe,CAAA,AAQjB,oCACE,eARS,AAST,gBATe,CAAA,AAOjB,oCACE,eAPS,AAQT,gBARe,CAAA,AAMjB,oCACE,eANS,AAOT,gBAPe,CAAA,AAKjB,oCACE,eALS,AAMT,gBANe,CAAA,AAqCnB,aJnCE,eCXgC,ADYhC,gBAAgB,AAChB,iBAAiB,AACjB,oBCToD,ADUpD,mBAAoB,CAAA,AImCtB,oBJjBE,sBCnCkC,ADoClC,mBAAoB,CAAA,AIsBtB,gBACE,aFzEa,CAAA,AE2Eb,0BACE,aFzEW,CAAA,AE6Ef,mBACE,yBFjFa,CAAA,AEmFb,6BACE,0BFjFW,CAAA,AEqFf,4BJlCE,gBAAgB,AAChB,uBAAuB,AACvB,mBAAmB,AACnB,gBAAiB,CAAA,AI+DnB,kBJrFE,eCnBgC,ADoBhC,eAAgB,CAAA,AIoFlB,qBJhFE,cE9CkB,AF+ClB,gBAAgB,AIsFZ,mBAAgC,AAChC,eAA6B,CAAA,AJrFjC,+BACE,aElCiB,CAAA,AE8GrB,qBJhFE,cE9CkB,AF+ClB,gBAAgB,AIsFZ,mBAAgC,AAChC,eAA6B,CAAA,AJrFjC,+BACE,aElCiB,CAAA,AE8GrB,qBJhFE,cE9CkB,AF+ClB,gBAAgB,AIsFZ,mBAAgC,AAChC,eAA6B,CAAA,AJrFjC,+BACE,aElCiB,CAAA,AE8GrB,qBJhFE,cE9CkB,AF+ClB,gBAAgB,AIsFZ,mBAAgC,AAChC,eAA6B,CAAA,AJrFjC,+BACE,aElCiB,CAAA,AE8GrB,qBJhFE,cE9CkB,AF+ClB,gBAAgB,AIsFZ,mBAAgC,AAChC,eAA6B,CAAA,AJrFjC,+BACE,aElCiB,CAAA,AE8GrB,qBJhFE,cE9CkB,AF+ClB,gBAAgB,AIsFZ,mBAAgC,AAChC,eAA6B,CAAA,AJrFjC,+BACE,aElCiB,CAAA,AE8GrB,qBAcI,YF9IW,AE8IX,2CF9IW,AE+IX,aAA6B,CAAA,AAE7B,+BACE,gCF9HS,CAAA,AE4Gf,oBAuBI,gBHhJe,AGiJf,SAAU,CAAA,AA8Bd,gBACE,cH9JsC,CAAA,AGkKxC,gBACE,cHlKsC,CAAA,AGiLxC,EAEE,oBAAqB,CAAA,AAFvB,UACE,aFnLa,CEyLe,AAP9B,QAMI,eAAe,AACf,yBAA0B,CAAA,AAP9B,2EAgBI,aAAc,CAAA,AAGhB,8BAEE,aFpMW,CAAA,AEkMb,oLAKI,aAAc,CAAA,AAnGpB,iCJvEE,sBCnCkC,ADoClC,oBAAoB,AIsMpB,8BF5Oa,AE8Ob,kBHzNyC,AG0NzC,6CFnQa,AEoQb,cF5Pa,AE6Pb,kBAAkB,AAClB,eAAgB,CAAA,AAEhB,sFACE,6BFzQW,AE0QX,6CF1QW,AE2QX,aFhQW,CAAA,AEqHf,qCAiJI,aFvPW,CAAA,AEyPX,4FACE,aAAc,CAAA,AApJpB,sCJvEE,sBCnCkC,ADoClC,oBAAoB,AIiOpB,8BFvQa,AEwQb,kBHnPyC,AGoPzC,8CF7Ra,AE8Rb,cF5RkB,AE8RlB,cAAc,AACd,eAA8B,AAC9B,gBAAgB,AAChB,cAAuB,AACvB,uBAA0E,AAC1E,qBAAqB,AACrB,oBAAqB,CAAA,AAErB,0FACE,6BFzSW,AE0SX,6CF1SW,AE2SX,aFzRiB,CAAA,AE8GrB,gDA+KI,gBAAgB,AAChB,gBAAgB,AAChB,cAAc,AACd,kBAAkB,AAClB,SAAU,CAAA,AAnLd,+BAgME,mBAAmB,AACnB,gBF7Sa,AE8Sb,kBHzRyC,AG0RzC,0FFnUa,AEoUb,cF5Ta,AE6Tb,oBAAoB,AACpB,oBAAoB,AACpB,eH7SsC,AG8StC,YH3R0C,AG4R1C,uBAAuB,AACvB,iBH7R0C,AG8R1C,eH9R0C,AG+R1C,gBAAkD,AAClD,qBAAsB,CAAA,AA7MxB,uLAgNI,gBAA+B,CAAA,AAGjC,mFACE,mBF9UgB,AE+UhB,0FFrVW,AEsVX,aF3UW,CAAA,AEqHf,6CA6OE,2CFlWa,AEmWb,gBHvWiB,AGwWjB,cAA8B,CAAA,AAE9B,wGACE,iCFzWW,CAAA,AEuHf,oDAiRE,cAAuB,AACvB,iBAA+B,CAAA,AAlRjC,gHAqRI,iBAAgC,CAAA,AArRpC,0HA2RI,cAA6B,CAAA,AAQjC,mBACE,gBAAgB,AAChB,SAAS,AACT,SAAU,CAAA,AAHZ,sBAMI,SAAU,CAAA,AAsBd,SACE,gBAAiB,CAAA,AAGnB,UACE,aFlbmB,CAAA,AGpBrB,OLuEE,qCAAmC,AACnC,mBAF+B,AAG/B,uBAAwB,CAAA,AKpE1B,6EAMI,sBAAwB,CAAA,ACT5B,WACE,gBAA6B,AAC7B,YAA0B,CAAA,AAG5B,gBACE,YAAa,CAAA,AADf,0BAII,eAAkC,AAClC,kBAA+B,AAC/B,YAAa,CAAA,AAIjB,oBACE,qBAAsB,CAAA,AAGxB,kBACE,aAAa,AACb,2BAA2B,AAC3B,eLfiB,CAAA,AKYnB,8BAMI,gBLlBe,CAAA,AMSnB,iBAEE,eAAe,AAEf,eAAe,AACf,YN6BiC,AM5BjC,gBAAgB,AAEhB,SAAS,AACT,SAAU,CAAA,AATZ,qCACE,mBAAmB,AAEnB,YAAa,CAWE,AAdjB,0BAiBM,mWAA8E,AAC9E,WAAW,AACX,cAAc,AACd,YNAsB,AMCtB,aAA6B,AAC7B,UNFsB,CAAA,AMpB5B,uCA0BM,YAAa,CAAA,AAKnB,mEAGE,mBAAmB,AACnB,oBAAoB,AACpB,cN3BsC,CAAA,AM8BxC,2CAEE,aLjDa,CAAA,AKoDf,sBAEI,oBAAqB,CAAA,AAFzB,6BAMI,0BL1DW,AK2DX,kBAAmB,CAAA,AAPvB,0BAWI,gBAA+B,CAAA,AAInC,wBACE,cAAc,AACd,eAAgB,CAAA,AAFlB,mCAKI,kBAAkB,AAClB,oBAAoB,AACpB,sBAAwB,CAAA,AAI5B,2BACE,mBLzEmB,AK0EnB,YAAY,AACZ,kBNhDyC,AMiDzC,eAAe,AACf,iBAAiB,AACjB,gBAAgC,AAChC,0BAA2B,CAAA,AAP7B,kCAUI,yRAAwF,AACxF,WAAW,AACX,cAAc,AACd,YN/DwB,AMgExB,UNhEwB,CAAA,AMkD5B,iCAkBI,mBL5FW,AK6FX,cLvGgB,AKwGhB,oBAAqB,CAAA,AAIzB,mGAOI,aL1GW,CAAA,AKmGf,uCAWI,0BL9GW,CAAA,AKmGf,kCAeI,aL3GiB,CAAA,AK4FrB,qCAmBI,4BLjIW,CAAA,AK8Gf,2CAsBM,6BLpIS,AKqIT,aLnHe,CAAA,AMKrB,YCnBI,oBAAoB,AAItB,mBCuF8B,AAC9B,mBAAmB,AAEnB,YAAY,AACZ,kBT1DyC,AS2DzC,eAAe,AACf,eT7EgC,AS8EhC,uBAAuB,AACvB,iBThGiB,ASiGjB,gBAAgB,AAChB,sBAAsB,AAItB,gBTjEkC,ASkElC,cTlEkC,CAAA,AQlClC,cACE,YAAY,AACZ,aAAc,CAAA,AAGhB,sBACE,YAAY,AACZ,aAAc,CAAA,AAehB,iCAGE,gBC7BkE,CAAA,ADiCpE,iDAEE,cAAkB,CAAA,ADlBtB,kBAOI,mBAAqB,CAAA,AAPzB,8CAYI,kBAAmB,CAAA,AAZvB,qBAgBI,aAAa,AACb,UAAW,CAAA,AAjBf,yDAsBI,gBAAiB,CAAA,AAtBrB,uDA2BI,eAAgB,CAAA,AA3BpB,sCE4GE,yBRjHmB,AQkHnB,8EA7E8E,AA8E9E,8ERrIa,AQsIb,aRpIkB,CAAA,AMqBpB,4CEiIE,4BAA4B,AAC5B,yBRxImB,AQyInB,6ER1Ja,CAAA,AMuBf,8FEuIE,yBR/ImB,AQgJnB,sBAAsB,AACtB,8ERhKa,CAAA,AMuBf,kGE6IE,sCRtJmB,AQuJnB,sBAAsB,AACtB,gBAAgB,AAChB,0BR/Ja,AQgKb,mBAAmB,AACnB,YAAa,CAAA,AFlJf,4PEsJI,+BR/JiB,CAAA,AMSrB,+BE2JE,yBRxJa,AQyJb,8EA3HqF,AA4HrF,8ERpLa,AQqLb,URjKa,CAAA,AMGf,qHEmKI,URtKW,CAAA,AMGf,qCEuKI,yBRrKW,AQsKX,6ER/LW,CAAA,AMuBf,gFE6KI,yBR5KW,AQ6KX,sBAAsB,AACtB,8ERtMW,CAAA,AMuBf,oFEyLE,qCRtLa,AQuLb,sBAAsB,AACtB,yBAAyB,AACzB,gBAAgB,AAChB,wBRhMa,CAAA,AMGf,+BE2JE,yBRlJc,AQmJd,8EA3HqF,AA4HrF,8ERpLa,AQqLb,URjKa,CAAA,AMGf,qHEmKI,URtKW,CAAA,AMGf,qCEuKI,yBR/JY,AQgKZ,6ER/LW,CAAA,AMuBf,gFE6KI,yBRtKY,AQuKZ,sBAAsB,AACtB,8ERtMW,CAAA,AMuBf,oFEyLE,oCRhLc,AQiLd,sBAAsB,AACtB,yBAAyB,AACzB,gBAAgB,AAChB,wBRhMa,CAAA,AMGf,+BE2JE,yBR5Ie,AQ6If,8EA3HqF,AA4HrF,8ERpLa,AQqLb,URjKa,CAAA,AMGf,qHEmKI,URtKW,CAAA,AMGf,qCEuKI,yBRzJa,AQ0Jb,6ER/LW,CAAA,AMuBf,gFE6KI,yBRhKa,AQiKb,sBAAsB,AACtB,8ERtMW,CAAA,AMuBf,oFEyLE,qCR1Ke,AQ2Kf,sBAAsB,AACtB,yBAAyB,AACzB,gBAAgB,AAChB,wBRhMa,CAAA,AMGf,8BE2JE,yBRtIY,AQuIZ,8EA3HqF,AA4HrF,8ERpLa,AQqLb,URjKa,CAAA,AMGf,kHEmKI,URtKW,CAAA,AMGf,oCEuKI,yBRnJU,AQoJV,6ER/LW,CAAA,AMuBf,8EE6KI,yBR1JU,AQ2JV,sBAAsB,AACtB,8ERtMW,CAAA,AMuBf,kFEyLE,oCRpKY,AQqKZ,sBAAsB,AACtB,yBAAyB,AACzB,gBAAgB,AAChB,wBRhMa,CAAA,AMGf,sEA2CI,WN9CW,CAAA,AMGf,6CEsFE,gBT9DwC,AS+DxC,eT/DwC,ASqExC,eT3FsC,AS4FtC,gBAhH8D,CAAA,AD2B9D,4GAGE,iBC5B2E,CAAA,ADgC7E,4IAEE,cAAkB,CAAA,ADlBtB,6CEsFE,gBThE0C,ASiE1C,eTjE0C,ASkF1C,aA3H4C,CAAA,AFmB9C,wBA2DI,iBAAkB,CAAA,AA3DtB,iDA8DM,iBAAkB,CAAA,AA9DxB,4CAkEM,SAAS,AAET,iBAAkB,CAAA,AApExB,kDAwEM,iBAAkB,CAAA,AAxExB,qCGLE,+BAAoD,AACpD,eViB0B,AUhB1B,kBAAkB,AAClB,gBAAgB,AAChB,cAAc,AAZd,kCAAkC,AAClC,mCAAmC,AH4F/B,aN/FS,CAAA,AMef,iFAqFI,aNpGW,CAAA,AMef,iIAwFM,eEzGgE,CAAA,AFiBtE,2FAiGI,aAA6D,CAAA,AAI/D,gDE4FA,yBRlNkB,AQmNlB,+EAhKoF,AAiKpF,uCR1Na,AQ2Nb,aRzMmB,CAAA,AM0GnB,wKEoGE,aR9MiB,CAAA,AM0GnB,sDE4HA,yBRnPkB,AQoPlB,sCRzPa,CAAA,AM4Hb,kHEiIA,yBR1PkB,AQ2PlB,sBAAsB,AACtB,wER/Pa,CAAA,AM4Hb,sHEuIA,mCR7PkB,AQ8PlB,sBAAsB,AACtB,gBAAgB,AAChB,0BR3Pa,CAAA,AMiHb,4IE6IE,4BRnQgB,CAAA,AMsHlB,sFEsHE,6BRlPW,AQmPX,cRzOW,CAAA,AMkHb,sQASM,aN1HO,CAAA,AMiHb,0FEqJE,sCRjRW,CAAA,AM4Hb,sGE0JE,wERtRW,CAAA,AM4Hb,0GEoKA,sBAAsB,AACtB,gBAAgB,AAChB,wBR9Qa,CAAA,AMwGb,gFAiBM,cNnIO,CAAA,AMaf,odAkIM,uBAAyB,CAAA,AAlI/B,wBE+QE,gBA3NoC,AA4NpC,eAAgB,CAAA,AFhRlB,8BEmRI,gCR/RW,AQgSX,gBAAgB,AAChB,cR1SgB,AQ2ShB,oBAAqB,CAAA,AFtRzB,kEE2RI,gCRzSW,AQ0SX,gBAAgB,AAChB,aRlTgB,CAAA,AMqBpB,wJEoSI,gBAAgB,AAChB,0BRpTW,AQqTX,kBAAmB,CAAA,AFtSvB,oMEySM,+BRvTS,CAAA,AQ2Tb,kCAgBA,gBAtQyC,AAuQzC,gBAAgB,AAChB,aAAc,CAAA,AAlBd,8HAuBE,gBAAgB,AAChB,eAAgB,CAAA,AAxBlB,wCA4BE,gCRtVW,CAAA,AQ0Tb,sFAiCE,gCR3VW,AQ4VX,aRpViB,CAAA,AQkTnB,gMAyCE,gBAAgB,AAChB,2BRnWW,AQoWX,kBAAmB,CAAA,AA3CrB,4OA8CI,+BRxWS,CAAA,AMaf,2CEiWE,aR/Va,CAAA,AMFf,yJEsWI,gBAAgB,AAChB,gBAAgB,AAChB,aRtWW,CAAA,AMFf,iDE4WI,gCRzWW,AQ0WX,aR3WW,CAAA,AMFf,wGEkXI,+BR/WW,AQgXX,aRjXW,CAAA,AMFf,4GEwXI,gBAAgB,AAChB,yBRvXW,CAAA,AMFf,kIE4XM,8BRzXS,CAAA,AMHf,iFEiYI,cR/XW,CAAA,AQkYb,qDACE,aRhYW,CAAA,AQ+Xb,2DAII,+BRrYS,AQsYT,aRpYS,CAAA,AQ+Xb,4HAUI,+BR3YS,AQ4YT,aR1YS,CAAA,AQ+Xb,gIAgBI,gBAAgB,AAChB,yBRhZS,CAAA,AQ+Xb,sJAoBM,8BRrZO,CAAA,AMHf,2CEiWE,aRzVc,CAAA,AMRhB,yJEsWI,gBAAgB,AAChB,gBAAgB,AAChB,aRhWY,CAAA,AMRhB,iDE4WI,+BRnWY,AQoWZ,aRrWY,CAAA,AMRhB,wGEkXI,8BRzWY,AQ0WZ,aR3WY,CAAA,AMRhB,4GEwXI,gBAAgB,AAChB,wBRjXY,CAAA,AMRhB,kIE4XM,6BRnXU,CAAA,AMThB,iFEiYI,cRzXY,CAAA,AQ4Xd,qDACE,aR1XY,CAAA,AQyXd,2DAII,8BR/XU,AQgYV,aR9XU,CAAA,AQyXd,4HAUI,8BRrYU,AQsYV,aRpYU,CAAA,AQyXd,gIAgBI,gBAAgB,AAChB,yBR1YU,CAAA,AQyXd,sJAoBM,6BR/YQ,CAAA,AMThB,2CEiWE,aRnVe,CAAA,AMdjB,yJEsWI,gBAAgB,AAChB,gBAAgB,AAChB,aR1Va,CAAA,AMdjB,iDE4WI,gCR7Va,AQ8Vb,aR/Va,CAAA,AMdjB,wGEkXI,+BRnWa,AQoWb,aRrWa,CAAA,AMdjB,4GEwXI,gBAAgB,AAChB,yBR3Wa,CAAA,AMdjB,kIE4XM,8BR7WW,CAAA,AMfjB,iFEiYI,cRnXa,CAAA,AQsXf,qDACE,aRpXa,CAAA,AQmXf,2DAII,+BRzXW,AQ0XX,aRxXW,CAAA,AQmXf,4HAUI,+BR/XW,AQgYX,aR9XW,CAAA,AQmXf,gIAgBI,gBAAgB,AAChB,0BRpYW,CAAA,AQmXf,sJAoBM,8BRzYS,CAAA,AMfjB,0CEiWE,aR7UY,CAAA,AMpBd,sJEsWI,gBAAgB,AAChB,gBAAgB,AAChB,aRpVU,CAAA,AMpBd,gDE4WI,+BRvVU,AQwVV,aRzVU,CAAA,AMpBd,sGEkXI,8BR7VU,AQ8VV,aR/VU,CAAA,AMpBd,0GEwXI,gBAAgB,AAChB,wBRrWU,CAAA,AMpBd,gIE4XM,6BRvWQ,CAAA,AMrBd,gFEiYI,cR7WU,CAAA,AQgXZ,oDACE,aR9WU,CAAA,AQ6WZ,0DAII,8BRnXQ,AQoXR,aRlXQ,CAAA,AQ6WZ,0HAUI,8BRzXQ,AQ0XR,aRxXQ,CAAA,AQ6WZ,8HAgBI,gBAAgB,AAChB,0BR9XQ,CAAA,AQ6WZ,oJAoBM,6BRnYM,CAAA,AMrBd,yBE+QE,gBA3NoC,AA4NpC,gBAAgB,AA2JhB,mCRhckB,AQiclB,qBAAsB,CAAA,AF5axB,+BEmRI,gCR/RW,AQgSX,gBAAgB,AAChB,cR1SgB,AQ2ShB,oBAAqB,CAAA,AFtRzB,oEE2RI,gCRzSW,AQ0SX,gBAAgB,AAChB,aRlTgB,CAAA,AMqBpB,4JEoSI,gBAAgB,AAChB,0BRpTW,AQqTX,kBAAmB,CAAA,AFtSvB,wMEySM,+BRvTS,CAAA,AQ2Tb,mCAgBA,gBAtQyC,AAuQzC,gBAAgB,AAChB,aAAc,CAAA,AAlBd,iIAuBE,gBAAgB,AAChB,eAAgB,CAAA,AAxBlB,yCA4BE,gCRtVW,CAAA,AQ0Tb,wFAiCE,gCR3VW,AQ4VX,aRpViB,CAAA,AQkTnB,oMAyCE,gBAAgB,AAChB,2BRnWW,AQoWX,kBAAmB,CAAA,AA3CrB,gPA8CI,+BRxWS,CAAA,AMaf,4CEiWE,aR/Va,CAAA,AMFf,4JEsWI,gBAAgB,AAChB,gBAAgB,AAChB,aRtWW,CAAA,AMFf,kDE4WI,gCRzWW,AQ0WX,aR3WW,CAAA,AMFf,0GEkXI,+BR/WW,AQgXX,aRjXW,CAAA,AMFf,8GEwXI,gBAAgB,AAChB,yBRvXW,CAAA,AMFf,oIE4XM,8BRzXS,CAAA,AMHf,kFEiYI,cR/XW,CAAA,AQkYb,sDACE,aRhYW,CAAA,AQ+Xb,4DAII,+BRrYS,AQsYT,aRpYS,CAAA,AQ+Xb,8HAUI,+BR3YS,AQ4YT,aR1YS,CAAA,AQ+Xb,kIAgBI,gBAAgB,AAChB,yBRhZS,CAAA,AQ+Xb,wJAoBM,8BRrZO,CAAA,AMHf,4CEiWE,aRzVc,CAAA,AMRhB,4JEsWI,gBAAgB,AAChB,gBAAgB,AAChB,aRhWY,CAAA,AMRhB,kDE4WI,+BRnWY,AQoWZ,aRrWY,CAAA,AMRhB,0GEkXI,8BRzWY,AQ0WZ,aR3WY,CAAA,AMRhB,8GEwXI,gBAAgB,AAChB,wBRjXY,CAAA,AMRhB,oIE4XM,6BRnXU,CAAA,AMThB,kFEiYI,cRzXY,CAAA,AQ4Xd,sDACE,aR1XY,CAAA,AQyXd,4DAII,8BR/XU,AQgYV,aR9XU,CAAA,AQyXd,8HAUI,8BRrYU,AQsYV,aRpYU,CAAA,AQyXd,kIAgBI,gBAAgB,AAChB,yBR1YU,CAAA,AQyXd,wJAoBM,6BR/YQ,CAAA,AMThB,4CEiWE,aRnVe,CAAA,AMdjB,4JEsWI,gBAAgB,AAChB,gBAAgB,AAChB,aR1Va,CAAA,AMdjB,kDE4WI,gCR7Va,AQ8Vb,aR/Va,CAAA,AMdjB,0GEkXI,+BRnWa,AQoWb,aRrWa,CAAA,AMdjB,8GEwXI,gBAAgB,AAChB,yBR3Wa,CAAA,AMdjB,oIE4XM,8BR7WW,CAAA,AMfjB,kFEiYI,cRnXa,CAAA,AQsXf,sDACE,aRpXa,CAAA,AQmXf,4DAII,+BRzXW,AQ0XX,aRxXW,CAAA,AQmXf,8HAUI,+BR/XW,AQgYX,aR9XW,CAAA,AQmXf,kIAgBI,gBAAgB,AAChB,0BRpYW,CAAA,AQmXf,wJAoBM,8BRzYS,CAAA,AMfjB,2CEiWE,aR7UY,CAAA,AMpBd,yJEsWI,gBAAgB,AAChB,gBAAgB,AAChB,aRpVU,CAAA,AMpBd,iDE4WI,+BRvVU,AQwVV,aRzVU,CAAA,AMpBd,wGEkXI,8BR7VU,AQ8VV,aR/VU,CAAA,AMpBd,4GEwXI,gBAAgB,AAChB,wBRrWU,CAAA,AMpBd,kIE4XM,6BRvWQ,CAAA,AMrBd,iFEiYI,cR7WU,CAAA,AQgXZ,qDACE,aR9WU,CAAA,AQ6WZ,2DAII,8BRnXQ,AQoXR,aRlXQ,CAAA,AQ6WZ,4HAUI,8BRzXQ,AQ0XR,aRxXQ,CAAA,AQ6WZ,gIAgBI,gBAAgB,AAChB,0BR9XQ,CAAA,AQ6WZ,sJAoBM,6BRnYM,CAAA,AMrBd,4JEkbI,gCRjcW,CAAA,AQ4Tb,mCAuJA,+BRvca,CAAA,AQgTb,oMA6JE,+BR7cW,CAAA,AMGf,4CE+cE,gCR7ca,CAAA,AMFf,8GEmdI,gCRjdW,CAAA,AQkYb,sDAmFE,gCRldW,CAAA,AQ+Xb,kIAuFI,gCRtdS,CAAA,AMLf,4CE+cE,+BRvcc,CAAA,AMRhB,8GEmdI,+BR3cY,CAAA,AQ4Xd,sDAmFE,gCR5cY,CAAA,AQyXd,kIAuFI,gCRhdU,CAAA,AMXhB,4CE+cE,gCRjce,CAAA,AMdjB,8GEmdI,gCRrca,CAAA,AQsXf,sDAmFE,iCRtca,CAAA,AQmXf,kIAuFI,iCR1cW,CAAA,AMjBjB,2CE+cE,+BR3bY,CAAA,AMpBd,4GEmdI,+BR/bU,CAAA,AQgXZ,qDAmFE,iCRhcU,CAAA,AQ6WZ,gIAuFI,iCRpcQ,CAAA,AM2Hd,aACE,kBAAkB,AAClB,qBAAqB,AACrB,eAAgB,CAAA,AAHlB,oDASI,aNhLgB,CAAA,AMuKpB,0BAaI,yBN9KW,CAAA,AMkLf,iBAEE,aAAc,CAAA,AAIhB,8LAKM,aAAc,CAAA,AIhKpB,kBACE,mBAAoB,CAwIlB,AAzIJ,8BAKI,cAAc,AACd,kBAAkB,AAClB,SAAsD,CAAA,AAP1D,oCAeM,SAAoD,CAAA,AAf1D,oCAmBM,SAAoD,CAAA,AAnB1D,8EAwBM,SAAqD,CAAA,AAxB3D,kFA6BM,SAAuD,CAAA,AA7B7D,kDAiCM,SAA6D,CAAA,AAjCnE,wDAoCQ,UAA2D,CAAA,AApCnE,wDAwCQ,UAA2D,CAAA,AAxCnE,sHA6CQ,UAA4D,CAAA,AA7CpE,0HAkDQ,SAA8D,CAAA,AAlDtE,yJA2DM,4BAA4B,AAC5B,wBAAyB,CAAA,AA5D/B,uJAiEM,6BAA6B,AAC7B,0BAA0B,AAC1B,iBFtGmB,CAAA,AEmCzB,0CFiQE,gBA3NoC,AA4NpC,eAAgB,CAAA,AAEhB,gDACE,gCR/RW,AQgSX,gBAAgB,AAChB,cR1SgB,AQ2ShB,oBAAqB,CAAA,AAGvB,sGAEE,gCRzSW,AQ0SX,gBAAgB,AAChB,aRlTgB,CAAA,AQqTlB,gOAIE,gBAAgB,AAChB,0BRpTW,AQqTX,kBAAmB,CAAA,AAEnB,4QACE,+BRvTS,CAAA,AQ2Tb,oDAgBA,gBAtQyC,AAuQzC,gBAAgB,AAChB,aAAc,CAAA,AAlBd,oLAuBE,gBAAgB,AAChB,eAAgB,CAAA,AAxBlB,0DA4BE,gCRtVW,CAAA,AQ0Tb,0HAiCE,gCR3VW,AQ4VX,aRpViB,CAAA,AQkTnB,wQAyCE,gBAAgB,AAChB,2BRnWW,AQoWX,kBAAmB,CAAA,AA3CrB,oTA8CI,+BRxWS,CAAA,AQ+TX,6DA+CF,aR/Va,CAAA,AQiWb,+MAGE,gBAAgB,AAChB,gBAAgB,AAChB,aRtWW,CAAA,AQyWb,mEACE,gCRzWW,AQ0WX,aR3WW,CAAA,AQ8Wb,4IAEE,+BR/WW,AQgXX,aRjXW,CAAA,AQoXb,gJAEE,gBAAgB,AAChB,yBRvXW,CAAA,AQyXX,sKACE,8BRzXS,CAAA,AQ6Xb,mGACE,cR/XW,CAAA,AQkYb,uEACE,aRhYW,CAAA,AQ+Xb,6EAII,+BRrYS,AQsYT,aRpYS,CAAA,AQ+Xb,gKAUI,+BR3YS,AQ4YT,aR1YS,CAAA,AQ+Xb,oKAgBI,gBAAgB,AAChB,yBRhZS,CAAA,AQ+Xb,0LAoBM,8BRrZO,CAAA,AQ+SX,6DA+CF,aRzVc,CAAA,AQ2Vd,+MAGE,gBAAgB,AAChB,gBAAgB,AAChB,aRhWY,CAAA,AQmWd,mEACE,+BRnWY,AQoWZ,aRrWY,CAAA,AQwWd,4IAEE,8BRzWY,AQ0WZ,aR3WY,CAAA,AQ8Wd,gJAEE,gBAAgB,AAChB,wBRjXY,CAAA,AQmXZ,sKACE,6BRnXU,CAAA,AQuXd,mGACE,cRzXY,CAAA,AQ4Xd,uEACE,aR1XY,CAAA,AQyXd,6EAII,8BR/XU,AQgYV,aR9XU,CAAA,AQyXd,gKAUI,8BRrYU,AQsYV,aRpYU,CAAA,AQyXd,oKAgBI,gBAAgB,AAChB,yBR1YU,CAAA,AQyXd,0LAoBM,6BR/YQ,CAAA,AQySZ,6DA+CF,aRnVe,CAAA,AQqVf,+MAGE,gBAAgB,AAChB,gBAAgB,AAChB,aR1Va,CAAA,AQ6Vf,mEACE,gCR7Va,AQ8Vb,aR/Va,CAAA,AQkWf,4IAEE,+BRnWa,AQoWb,aRrWa,CAAA,AQwWf,gJAEE,gBAAgB,AAChB,yBR3Wa,CAAA,AQ6Wb,sKACE,8BR7WW,CAAA,AQiXf,mGACE,cRnXa,CAAA,AQsXf,uEACE,aRpXa,CAAA,AQmXf,6EAII,+BRzXW,AQ0XX,aRxXW,CAAA,AQmXf,gKAUI,+BR/XW,AQgYX,aR9XW,CAAA,AQmXf,oKAgBI,gBAAgB,AAChB,0BRpYW,CAAA,AQmXf,0LAoBM,8BRzYS,CAAA,AQmSb,4DA+CF,aR7UY,CAAA,AQ+UZ,4MAGE,gBAAgB,AAChB,gBAAgB,AAChB,aRpVU,CAAA,AQuVZ,kEACE,+BRvVU,AQwVV,aRzVU,CAAA,AQ4VZ,0IAEE,8BR7VU,AQ8VV,aR/VU,CAAA,AQkWZ,8IAEE,gBAAgB,AAChB,wBRrWU,CAAA,AQuWV,oKACE,6BRvWQ,CAAA,AQ2WZ,kGACE,cR7WU,CAAA,AQgXZ,sEACE,aR9WU,CAAA,AQ6WZ,4EAII,8BRnXQ,AQoXR,aRlXQ,CAAA,AQ6WZ,8JAUI,8BRzXQ,AQ0XR,aRxXQ,CAAA,AQ6WZ,kKAgBI,gBAAgB,AAChB,0BR9XQ,CAAA,AQ6WZ,wLAoBM,6BRnYM,CAAA,AUPd,6EA+EI,aAAa,AACb,aAAc,CAAA,AAhFlB,2BAyGI,aAAa,AACb,UAAW,CAAA,AA1Gf,8FA+GI,aAAc,CAAA,AA/GlB,+BA4II,oBAAoB,AACpB,sBAAsB,AACtB,kBAAmB,CAAA,AA9IvB,wCAiJM,YAAY,AACZ,WAAY,CAAA,AAlJlB,2CAuJM,yBAA0B,AAE1B,UAAW,CAAA,AAzJjB,uKA+JQ,yBAAsD,CAAA,AA/J9D,qKAoKQ,yBXhKmC,CAAA,AWJ3C,iLAyKQ,kBF5MiB,CAAA,AEmCzB,6CA+KI,eAAgB,CAAA,AAGlB,2KAQM,gBF7NiB,CAAA,AEqNvB,iKAeM,iBFpOiB,CAAA,AGgBzB,abyBE,eCnBgC,ADoBhC,gBAAgB,AaxBhB,uCXVa,AWWb,kBZoByC,AYnBzC,sBAAkE,AAClE,kBAAkB,AAClB,UAAW,CAAA,AANb,+BAUI,iBAAuD,CAAA,AAV3D,sCFAE,+BAAoD,AACpD,eVkBuB,AUjBvB,kBAAkB,AAClB,gBAAgB,AAChB,cAAc,AAZd,kCAAkC,AAClC,mCAAmC,AEqB/B,cXxBS,AWyBT,UZ1Ba,AY2Bb,kBAAkB,AAClB,QZ5Ba,CAAA,AYWnB,8BAsBI,iBAAuD,CAAA,AAtB3D,oDAyBM,cXnCS,AWoCT,UZrCa,AYsCb,kBAAkB,AAClB,QZvCa,CAAA,AYWnB,0BAiCI,iBZdqB,AYerB,kBAAgC,AAChC,YAAa,CAAA,AAnCjB,qCAsCM,eAAgB,CAAA,AAIpB,uBACE,qCXnDW,CAAA,AWkDb,gDAII,aXrDS,CAAA,AWOf,gCAoDM,qCX5CS,CAAA,AWRf,4JAyDQ,aXlDO,CAAA,AWqDT,0CACE,qCXrDO,CAAA,AWoDT,0LAMI,aXxDK,CAAA,AWVf,gCAoDM,oCXtCU,CAAA,AWdhB,4JAyDQ,aX5CQ,CAAA,AW+CV,0CACE,oCX/CQ,CAAA,AW8CV,0LAMI,aXlDM,CAAA,AWhBhB,gCAoDM,qCXhCW,CAAA,AWpBjB,4JAyDQ,aXtCS,CAAA,AWyCX,0CACE,qCXzCS,CAAA,AWwCX,0LAMI,aX5CO,CAAA,AWtBjB,+BAoDM,oCX1BQ,CAAA,AW1Bd,yJAyDQ,aXhCM,CAAA,AWmCR,yCACE,oCXnCM,CAAA,AWkCR,uLAMI,aXtCI,CAAA,AW4CZ,+BACE,aAA6B,CAAA,ACrDjC,UACE,sBZnBa,AYoBb,kBbCyC,AaAzC,sFZzCa,AY0Cb,aAxB8B,AAyB9B,2Fb4DkD,CAAA,AajEpD,uCAUI,yBZ3CgB,AY4ChB,oFZjDW,CAAA,AYsDb,iBACE,qFZvDW,CAAA,AYsDb,qDAKI,oFZ3DS,CAAA,AYsDb,iBACE,yFZvDW,CAAA,AYsDb,qDAKI,yFZ3DS,CAAA,AYsDb,iBACE,8FZvDW,CAAA,AYsDb,qDAKI,8FZ3DS,CAAA,AYsDb,iBACE,+FZvDW,CAAA,AYsDb,qDAKI,+FZ3DS,CAAA,AYsDb,iBACE,oGZvDW,CAAA,AYsDb,qDAKI,oGZ3DS,CAAA,AYgEf,gCAEI,gGZlEW,AYmEX,cAAe,CAAA,AAHnB,mFAOM,+FZvES,CAAA,AYgEf,iCAYI,0FZ5EW,AY6EX,WAAY,AACZ,qBAAsB,CAAA,AAd1B,qFAkBM,yFZlFS,CAAA,AaEf,cACE,SAAS,AACT,kBAAkB,AAClB,+CdkGkD,CAAA,AcrGpD,iCAMI,kDd+FgD,CAAA,AcrGpD,mDASM,YAAa,CAAA,ACbnB,sCACE,aAAc,CAAA,AAGhB,iCACE,cAAe,CAAA,ACgCjB,sBC0DI,UDxDc,ACwDd,mBDvDgC,AAWlC,mBAAmB,AACnB,aAAa,AACb,uBAAuB,AACvB,gBAAgB,AAChB,oBAAoB,AACpB,yBAAiB,AAAjB,iBAAiB,AACjB,UAAW,CAAA,AApBb,yGC0DI,UDxDW,ACwDX,mBDvDsB,CAAA,AAH1B,uHC0DI,UDxDc,ACwDd,mBDvDgC,ACwChC,mBArDO,AAsDP,wBDnCsC,ACoCtC,sCD1CS,AC2CT,0DjBuB4D,CAAA,AgBrEhE,mDC0DI,UDxDc,ACwDd,kBDvDgC,CAAA,AAHpC,0DC0DI,UDxDW,ACwDX,oBDvDsB,ACwCtB,mBArDO,AAsDP,wBDnCsC,ACoCtC,sCD1CS,AC2CT,0DjBuB4D,CAAA,AgB9ChE,YACE,mBf1CmB,Ae2CnB,kBA7B0C,AA8B1C,qGf7Da,Ae8Db,aAAa,AACb,sBAAsB,AACtB,cAhCmC,AAiCnC,oBAAiC,AACjC,mBAAmB,AACnB,yBAAiB,AAAjB,iBAAiB,AACjB,WAAyB,CAAA,AAV3B,kBAaI,SAAU,CAAA,AAbd,2CAkBI,mBfxEgB,AeyEhB,qGf7EW,Ae8EX,af5DiB,CAAA,AegErB,mBACE,mBAAmB,AACnB,gBfhEa,AeiEb,0BAA8D,AAC9D,sCftFa,AeuFb,aAAa,AACb,cAAc,AACd,gBAAiD,AACjD,kBAzDgC,AA0DhC,iBAAkC,CAAA,AATpC,gEAaI,cfvFW,AewFX,cAAc,AACd,iBAAiC,CAAA,AAfrC,gCjBpBE,gBAAgB,AAChB,uBAAuB,AACvB,mBAAmB,AACnB,iBAAiB,AiBqCf,cAAc,AACd,oBAAoB,AACpB,QAAS,CAAA,AAtBb,2CAyBM,iBA1E4B,CAAA,AA8EhC,6BACE,mBf3GgB,Ae4GhB,oCfjHW,CAAA,Ae+Gb,oFAMI,af1GS,CAAA,Ae+Gf,iBACE,cAAc,AACd,iBAAgC,AAChC,WA5FgC,CAAA,AA+FlC,mBACE,cAAc,AACd,aAjGgC,CAAA,AAoGlC,2BACE,aAAa,AACb,wBAAyB,CAAA,AAF3B,uCAKI,gBhBnIe,CAAA,AkBInB,YACE,gBjBQa,AiBPb,qGjBba,AiBcb,aAAa,AACb,sBAAsB,AACtB,SAAS,AACT,SAAU,CAAA,AANZ,kBASI,SAAU,CAAA,AATd,6BA4BI,WAjCqB,AAkCrB,OAAO,AACP,QAAQ,AAER,KAAM,CAAA,AD6BR,+FAqBE,2BClEgC,CAAA,ADiDlC,6GAiBE,wBClE+C,ADmD/C,mBApBO,AAqBP,wBCnD6B,ADoD7B,8BCrDY,ADsDZ,oDjBsBgD,CAAA,AiB/BlD,8CAqBE,uBC1D+C,CAAA,ADyCjD,qDAiBE,4BC1DgC,AD2ChC,mBApBO,AAqBP,wBjB0B0B,AiBzB1B,8BC7CY,AD8CZ,oDjBsBgD,CAAA,AkB5FpD,gCAmDI,SAAS,AACT,WAzDqB,AA0DrB,OAAO,AAEP,OAAQ,CAAA,ADMV,qGAqBE,0BC3C+B,CAAA,AD0BjC,mHAiBE,wBC3C8C,AD4B9C,mBApBO,AAqBP,wBC5B6B,AD6B7B,8BC9BY,AD+BZ,oDjBsBgD,CAAA,AiB/BlD,iDAqBE,uBCnC8C,CAAA,ADkBhD,wDAiBE,2BCnC+B,ADoB/B,mBApBO,AAqBP,wBjB0B0B,AiBzB1B,8BCtBY,ADuBZ,oDjBsBgD,CAAA,AkB5FpD,8BA0EI,SAAS,AACT,OAAO,AAEP,MAAM,AACN,SAnFqB,CAAA,ADkEvB,iGAqBE,2BCpBgC,CAAA,ADGlC,+GAiBE,wBCpB+C,ADK/C,mBApBO,AAqBP,wBCL6B,ADM7B,8BCPY,ADQZ,oDjBsBgD,CAAA,AiB/BlD,+CAqBE,uBCZ+C,CAAA,ADLjD,sDAiBE,4BCZgC,ADHhC,mBApBO,AAqBP,wBjB0B0B,AiBzB1B,8BCCY,ADAZ,oDjBsBgD,CAAA,AkB5FpD,+BAiGI,SAAS,AACT,QAAQ,AAER,MAAM,AACN,SA1GqB,CAAA,ADkEvB,mGAqBE,0BCG+B,CAAA,ADpBjC,iHAiBE,wBCG8C,ADlB9C,mBApBO,AAqBP,wBCkB6B,ADjB7B,8BCgBY,ADfZ,oDjBsBgD,CAAA,AiB/BlD,gDAqBE,uBCW8C,CAAA,AD5BhD,uDAiBE,2BCW+B,AD1B/B,mBApBO,AAqBP,wBjB0B0B,AiBzB1B,8BCwBY,ADvBZ,oDjBsBgD,CAAA,AkB5FpD,iIA0HM,SAAS,AACT,QAAQ,AAER,MAAM,AACN,SAnImB,CAAA,ADkEvB,uSAqBE,0BC4BiC,CAAA,AD7CnC,qTAiBE,wBC4BgD,AD3ChD,mBApBO,AAqBP,wBC2C+B,AD1C/B,8BCyCc,ADxCd,oDjBsBgD,CAAA,AiB/BlD,kJAqBE,uBCoCgD,CAAA,ADrDlD,yJAiBE,2BCoCiC,ADnDjC,mBApBO,AAqBP,wBjB0B0B,AiBzB1B,8BCiDc,ADhDd,oDjBsBgD,CAAA,AkB5FpD,2HAiJM,SAAS,AACT,WAvJmB,AAwJnB,OAAO,AAEP,OAAQ,CAAA,ADxFZ,2RAqBE,0BCmDiC,CAAA,ADpEnC,ySAiBE,wBCmDgD,ADlEhD,mBApBO,AAqBP,wBCkE+B,ADjE/B,8BCgEc,AD/Dd,oDjBsBgD,CAAA,AiB/BlD,4IAqBE,uBC2DgD,CAAA,AD5ElD,mJAiBE,2BC2DiC,AD1EjC,mBApBO,AAqBP,wBjB0B0B,AiBzB1B,8BCwEc,ADvEd,oDjBsBgD,CAAA,AkB5FpD,2CA2JI,mBjBjKgB,AiBkKhB,qGjBvKW,AiBwKX,ajBtJiB,CAAA,AiB0JrB,mBACE,mBAAmB,AACnB,gBAAgB,AAChB,sCjB/Ka,AiBgLb,aAAa,AACb,cAAc,AACd,gBAAiD,AAEjD,yBAhLgC,AAiLhC,iBAAkB,CAAA,AATpB,gEAaI,cjBjLW,AiBkLX,cAAc,AACd,iBAAiC,CAAA,AAfrC,gCnB9GE,gBAAgB,AAChB,uBAAuB,AACvB,mBAAmB,AACnB,iBAAiB,AmB+Hf,cAAc,AACd,oBAAoB,AACpB,QAAS,CAAA,AAtBb,2CAyBM,iBAjM4B,CAAA,AAqMhC,6BACE,oCjB1MW,CAAA,AiByMb,oFAKI,ajBnMS,CAAA,AiBwMf,iBACE,cAAc,AACd,iBAAgC,AAChC,aAAc,CAAA,AAGhB,mBACE,4CjB1Na,AiB2Nb,cAAc,AACd,kBAxNgC,AAyNhC,iBAAkB,CAAA,AAElB,6BACE,0CjBhOW,CAAA,AkBCf,mBACE,YAAY,AACZ,qBAAqB,AACrB,eAAe,AACf,kBAAkB,AAClB,mBAAmB,AACnB,kBAAmB,CAAA,AANrB,0BpB0BE,YCcyC,ADbzC,UCayC,ADZzC,kBoBlBgC,ApBmBhC,WCWyC,ADVzC,SCUyC,AmB7BvC,kBnB6BuC,AmB5BvC,WAAW,AACX,kGnByFgD,CAAA,AmBtGpD,gCAkBI,mGlBnBW,CAAA,AkBCf,oDAuBI,sBlBJW,AkBKX,4FlBzBW,CAAA,AkBCf,uCA4BI,eAAgB,CAAA,AA5BpB,gIAmCQ,alBVO,CAAA,AkBzBf,mDAuCQ,oGlBdO,CAAA,AkBzBf,uEA2CQ,4FlB5CO,CAAA,AkBCf,gIAmCQ,alBJQ,CAAA,AkB/BhB,mDAuCQ,iGlBRQ,CAAA,AkB/BhB,uEA2CQ,2FlB5CO,CAAA,AkBCf,gIAmCQ,alBES,CAAA,AkBrCjB,mDAuCQ,oGlBFS,CAAA,AkBrCjB,uEA2CQ,4FlB5CO,CAAA,AkBCf,8HAmCQ,alBQM,CAAA,AkB3Cd,kDAuCQ,iGlBIM,CAAA,AkB3Cd,sEA2CQ,2FlB5CO,CAAA,AkBiDb,0CAEI,oGlB/BS,CAAA,AkB6Bb,8DAOI,mClBxDS,AkByDT,8HlBzDS,CAAA,AkBiDb,iDAaI,eAAgB,CAAA,AAbpB,2EAmBQ,alBxCK,CAAA,AkBqBb,6DAuBQ,oGlB5CK,CAAA,AkBqBb,iFA2BQ,8HlB5EK,CAAA,AkBiDb,2EAmBQ,alBlCM,CAAA,AkBed,6DAuBQ,oGlBtCM,CAAA,AkBed,iFA2BQ,8HlB5EK,CAAA,AkBiDb,2EAmBQ,alB5BO,CAAA,AkBSf,6DAuBQ,uGlBhCO,CAAA,AkBSf,iFA2BQ,+HlB5EK,CAAA,AkBiDb,0EAmBQ,alBtBI,CAAA,AkBGZ,4DAuBQ,uGlB1BI,CAAA,AkBGZ,gFA2BQ,+HlB5EK,CAAA,AkBmFf,oDAEE,cAAc,AACd,gBAAgB,AAChB,aAAa,AACb,uBAAuB,AACvB,kBAAkB,AAElB,kBAAkB,AAClB,kBAAkB,AAElB,YAAY,AACZ,uBAAuB,AACvB,kBAAmB,CAAA,AAGrB,yBAEE,gBAAgB,AAEhB,YAAY,AACZ,gBAAgB,AAChB,UAAU,AAEV,qBAAqB,AACrB,UAAW,CAAA,ACaX,oDACE,0BnBlHW,AmBoHX,SAAU,CAAA,AAHZ,sCACE,0BnBlHW,AmBoHX,SAAU,CAAA,ADzBd,+BAYI,YAAa,CAAA,AAZjB,oCAgBI,YAAa,CAAA,AAIjB,2BACE,gBAAgB,AAEhB,kBAAkB,AAClB,uBAAuB,AAEvB,eAAgB,CAAA,AAEhB,sDACE,OAAO,AACP,kBAAkB,AAClB,iBAAkB,CAAA,AAGpB,0DACE,yBlB9HW,CAAA,AkBgIX,oEACE,0BlB9HS,CAAA,AkBmIf,iCACE,aAAc,CAAA,AADhB,4DAII,cAAc,AACd,qBAAqB,AACrB,oBAAqB,CAAA,AElJzB,aACE,2CpBHa,AoBMb,0CpBNa,AoBOb,UAPgC,CAAA,AAUhC,uBACE,8BpBXW,CAAA,AqBkEf,mBvBiDE,wBAAwB,AS7GtB,aAAa,AAEf,mBc6D8B,AAE9B,mBAAoB,CAmOlB,AdhSF,qBACE,YAAY,AACZ,aAAc,CAAA,AAGhB,6BACE,YAAY,AACZ,aAAc,CAAA,AciDlB,gIAkBI,iBAAkB,CAAA,AAlBtB,8BAuBI,sBAAsB,AACtB,SAAqD,CAAA,AAxBzD,oCA2BM,kBtBpDqC,AsBqDrC,UAAmD,CAAA,AA5BzD,iDAgCM,UAA4D,CAAA,AAhClE,uDAmCQ,UAA0D,CAAA,AAnClE,0HA0CM,SAAsD,CAAA,AA1C5D,kEA+CI,UAA4D,CAAA,AA/ChE,wEAkDM,UAA0D,CAAA,AAlDhE,gHvBiDE,wBAAwB,AuBUtB,sBAAsB,AACtB,SAAsD,CAAA,AA5D1D,kIA+DM,SAAoD,CAAA,AA/D1D,kIAmEM,SAAoD,CAAA,AAnE1D,qIAuEM,SAAqD,CAAA,AAvE3D,gbA6EM,SAAuD,CAAA,AA7E7D,yKAiFM,SAA6D,CAAA,AAjFnE,2LAoFQ,UAA2D,CAAA,AApFnE,2LAwFQ,UAA2D,CAAA,AAxFnE,8LA4FQ,UAA4D,CAAA,AA5FpE,2lBAkGQ,SAA8D,CAAA,AAlGtE,oJA2GI,UAA4D,CAAA,AA3GhE,sKAoHI,UAAoD,CAAA,AApHxD,4CA0HI,SAAoD,CAAA,AA1HxD,yDAgIM,iBbhMmB,CAAA,AagEzB,qEAoIM,eAAmD,CAAA,AAGrD,mEAEI,cAAe,CAAA,AAFnB,wEAUI,ebjNiB,CAAA,AagEzB,+EAyJI,qBAAsB,CAAA,AAzJ1B,gCA8JI,yBtBvLuC,CAAA,AsByB3C,+BAmKI,0BAAsD,AACtD,cAAe,CAAA,AApKnB,+BAyKI,kBtBlMuC,AsBmMvC,cAAe,CAAA,AA1KnB,gDA+KI,iBtBxMuC,CAAA,AsByB3C,yEAoLI,4BAA4B,AAC5B,wBAAyB,CAAA,AArL7B,4BA4MI,UAAW,CAAA,AA5Mf,0EAoNI,aAAc,CAAA,AApNlB,gCA2OI,qBAAsB,CAAA,AA3O1B,kCA8OM,eb9SmB,CAAA,AagEzB,6CAkPM,0BAAsD,AACtD,YAAa,CAAA,AAnPnB,4CAuPM,yBtBhRqC,CAAA,AuByC3C,aAGE,eAAe,AAEf,cAAc,AACd,mBvBjFiB,AuBkFjB,kBAAkB,AAClB,mBAAoB,CA2alB,AAnfF,kDACE,yBtBOW,AsBNX,8EdoCmF,AcnCnF,8EtBrBW,AsBsBX,UtBFW,CAAA,AsBKb,wDACE,yBtBDW,AsBEX,6EtB3BW,CAAA,AsB8Bb,wEACE,mBtBPW,AsBQX,8EtBhCW,CAAA,AsBmCb,2DACE,+BtBVW,AsBWX,eAAgB,CAAA,AAGlB,4DAEI,sCtB1CS,CAAA,AsBwCb,kEAMI,yBtBrBS,AsBsBT,sCtB/CS,CAAA,AsBwCb,kFAWI,yBtB3BS,AsB4BT,wEtBpDS,CAAA,AsBwCb,qEAgBI,8BtBhCS,AsBiCT,eAAgB,CAAA,AAQpB,mCACE,iBAH0C,CAAA,AAK1C,0DACE,iBANwC,CAAA,AAU5C,6BACE,kBAX0C,CAAA,AAa1C,oDACE,kBAdwC,CAAA,AAmB9C,0BAWI,0BtBrFW,AsBsFX,kBAAmB,CAAA,AAZvB,wBAgBI,qBAAqB,AACrB,iBAA+B,CAAA,AAjBnC,mBAqBI,OAAO,AACP,UAAU,AACV,kBAAkB,AAClB,MAAM,AACN,UAAW,CAAA,AAzBf,oCA6BI,4BAA4B,AAC5B,yBtB9FiB,AsB+FjB,8Ed1D4E,Ac2D5E,YAAY,AACZ,8EtBnHW,AsBoHX,eAAe,AACf,qBAAqB,AAGrB,evBpFwB,AuBqFxB,WAAW,AACX,kBvBnHe,AuBoHf,gBAAgB,AAChB,kBAAkB,AAClB,yBAAiB,AAAjB,iBAAiB,AACjB,sBAAsB,AACtB,SAAU,CAAA,AA7Cd,2CAgDM,WAAW,AACX,cAAc,AACd,WAAW,AACX,SAAU,CAAA,AAnDhB,0CAwDI,wBtBzHiB,CAAA,AsBiErB,gEA4DI,mBtB/HiB,AsBgIjB,8EtB/IW,CAAA,AsBkFf,mDAiEI,gCtBrIiB,AsBsIjB,gBAAgB,AAChB,kBAAmB,CAAA,AAnEvB,gDxBbE,qCAAmC,AACnC,mBAF+B,AAG/B,uBAAwB,CAAA,AwBKtB,oDAkFA,YAAY,AACZ,iBvBxJe,AuByJf,cAAe,CAAA,AA9EnB,uBAoFI,cvB7IoC,CAAA,AuBwCtC,6CACE,iBAH0C,CAAA,AAK1C,oEACE,iBANwC,CAAA,AAU5C,uCACE,kBAX0C,CAAA,AAa1C,8DACE,kBAdwC,CAAA,AAmB9C,8CAwFM,cvBrImB,CAAA,AuBuCrB,8DAkGE,YAAa,CAAA,AA5JjB,qEACE,yBtBOW,AsBNX,8EdoCmF,AcnCnF,8EtBrBW,AsBsBX,UtBFW,CAAA,AsBKb,2EACE,yBtBDW,AsBEX,6EtB3BW,CAAA,AsB8Bb,2FACE,mBtBPW,AsBQX,8EtBhCW,CAAA,AsBmCb,8EACE,+BtBVW,AsBWX,eAAgB,CAAA,AAGlB,+EAEI,sCtB1CS,CAAA,AsBwCb,qFAMI,yBtBrBS,AsBsBT,sCtB/CS,CAAA,AsBwCb,qGAWI,yBtB3BS,AsB4BT,wEtBpDS,CAAA,AsBwCb,wFAgBI,8BtBhCS,AsBiCT,eAAgB,CAAA,AAyBtB,iDAiIM,iBvB1KqC,CAAA,AuByC3C,sEAyHQ,kVAAuE,CAAA,AAzH/E,4EAyHQ,yQAAuE,CAAA,AAzH/E,8CAiKM,iBAAkB,CAAA,AAjKxB,mEAqKM,+DAAsE,CAAA,AArK5E,4EAyKM,UAAY,CAAA,AAzKlB,0DA6KM,wBvB3NsB,CAAA,AuB8C5B,qDA+OQ,+BtBtTO,CAAA,AsBuEf,2DAmPQ,+BtB5TO,CAAA,AsByEf,2EAuPQ,8BtBjUO,CAAA,AsB0Ef,8DA2PQ,+BtB/Ta,CAAA,AsBoErB,qEA8PU,6BtB5TK,CAAA,AsB8Df,6DA+OQ,kBtBvSO,CAAA,AsBwDf,mEAmPQ,kBtB5SO,CAAA,AsByDf,mFAuPQ,kBtBjTO,CAAA,AsB0Df,sEA2PQ,8BtBnTO,CAAA,AsBwDf,6EA8PU,6BtB5TK,CAAA,AsB6Cb,8CACE,iBAH0C,CAAA,AAK1C,qEACE,iBANwC,CAAA,AAU5C,wCACE,kBAX0C,CAAA,AAa1C,+DACE,kBAdwC,CAAA,AAmB9C,+CAuRM,YAAY,AACZ,qBApFiB,AAuFjB,0BAA2B,AAC3B,iBAxFiB,AAyFjB,0DvBxQ8C,AuByQ9C,UAAW,CAAA,AA9RjB,sDAiSQ,gBtB/VO,AsBgWP,kBAAkB,AAClB,mEtBrXO,AsBsXP,uBA9F6D,AA+F7D,OAAO,AACP,WAjGuB,AAkGvB,kBAAkB,AAClB,8CvBnR4C,AuBoR5C,qBAnG6D,CAAA,AAtMrE,oEA+SM,qBAAsB,CAAA,AAhU1B,wDACE,iBAH0C,CAAA,AAK1C,+EACE,iBANwC,CAAA,AAU5C,kDACE,kBAX0C,CAAA,AAa1C,yEACE,kBAdwC,CAAA,AAiQxC,+DACE,4BtBjUO,CAAA,AsBoUT,qEACE,4BtBrUO,CAAA,AsBwUT,qFACE,4BtBzUO,CAAA,AsB4UT,wEACE,4BtBvUY,CAAA,AsByUZ,+EACE,4BtBhVK,CAAA,AsBgUT,uEACE,kBtBvSO,CAAA,AsB0ST,6EACE,kBtB5SO,CAAA,AsB+ST,6FACE,kBtBjTO,CAAA,AsBoTT,gFACE,6BtBrTO,CAAA,AsBuTP,uFACE,4BtBhVK,CAAA,AsBwYX,gEAmBI,mBtBrZY,AsBsZZ,sCtB5ZO,CAAA,AsBwYX,8EAyBI,4CtBjaO,CAAA,AsBkFf,+CAoVM,eAxImC,AAyInC,iBAAkB,CAAA,AArVxB,iEA0VQ,cAAc,AACd,iBAlJuC,AAmJvC,mBAlJsC,AAmJtC,iBAAkB,CAAA,AA7V1B,gEAiWQ,gBAzJ6B,AA0J7B,kBAxJsC,AAyJtC,kBA1JuC,AA2JvC,kBAAmB,CAAA,AApW3B,sGA0WQ,gBAlK6B,AAmK7B,kBAAmB,CAAA,AA3W3B,qGA+WQ,cAAc,AACd,iBAAkB,CAAA,AAKxB,uBACE,atBtbiB,CAAA,AsBqbnB,oCAII,0BtBhcS,CAAA,AsB4bb,8CAQI,yBtBzcc,AsB0cd,+EdvZgF,AcwZhF,sCtBjdS,CAAA,AsBucb,oDAcI,wBtBhdc,CAAA,AsBkclB,0EAkBI,mBtBtdc,AsBudd,wEtB1dS,CAAA,AsBucb,6DAuBI,6BtBxdc,AsBydd,gBAAgB,AAChB,kBAAmB,CAAA,AAzBvB,0KAgCQ,0BtB5dK,CAAA,AuBaf,gBACE,eAAe,AACf,qBAAqB,AACrB,YxBuBiC,AwBtBjC,iBAAkB,CAAA,AAJpB,sBAOI,SAAS,AACT,gBAA6B,AAC7B,SAAU,CAAA,AATd,gHJyGE,gCnBnHmB,AmBoHnB,gBAAgB,AAChB,0BnB3Ha,AmB4Hb,mBAAmB,AACnB,WAAY,CAAA,AI7Gd,4Hf4IE,sCRtJmB,AQuJnB,sBAAsB,AACtB,gBAAgB,AAChB,0BR/Ja,AQgKb,mBAAmB,AACnB,YAAa,CAAA,AAEb,gTAEE,+BR/JiB,CAAA,AuBkCf,oIJiHJ,6BnB3JkB,AmB4JlB,gBAAgB,AAChB,0BnBxJa,CAAA,AuBqCT,gJfmNJ,mCR7PkB,AQ8PlB,sBAAsB,AACtB,gBAAgB,AAChB,0BR3Pa,CAAA,AQ6Pb,sKACE,4BRnQgB,CAAA,AuBkBpB,oEAoCM,avB1Dc,CAAA,AuB6DhB,8EACE,avB9Ce,CAAA,AuBMrB,yBA6CI,UAAW,CAAA,AA7Cf,qDAkDI,WxBvBqC,CAAA,AwB3BzC,yDAsDI,6BAA6C,CAAA,AAIjD,uBJDE,wBAAgB,AAAhB,qBAAgB,AAAhB,gBAAgB,AAChB,gBnB9Da,AmB+Db,YAAY,AACZ,kBpB3CyC,AoB4CzC,sInBrFa,AmBsFb,cnBpFkB,AmBqFlB,epB/DgC,AoBgEhC,gBApFqB,AAqFrB,YpBvCiC,AoBwCjC,iBpBxCiC,AoB0CjC,aAAa,AAEb,oDpBSkD,AoBRlD,sBAAsB,ArBjCtB,gBAAgB,AAChB,uBAAuB,AACvB,mBAAmB,AACnB,iBAAiB,AyBoBjB,0BvB7Ea,AuB8Eb,OAAO,AACP,sBAAmE,AACnE,kBAAkB,AAClB,QAAQ,AACR,MAAM,AACN,yBAAiB,AAAjB,gBAAiB,CAAA,AJ8BjB,kDACE,0BnBlHW,AmBoHX,SAAU,CAAA,AAHZ,oCACE,0BnBlHW,AmBoHX,SAAU,CAAA,AA3BZ,+DAEE,4FnBnGW,CAAA,AmBsGb,qEAEE,mBpBtD+B,AoBwD/B,sBAAsB,AACtB,iBpBpGe,CAAA,AoBuGjB,iCACE,6CnB/GW,CAAA,AmBkHb,oEAeA,gCnBnHmB,AmBoHnB,gBAAgB,AAChB,0BnB3Ha,AmB4Hb,mBAAmB,AACnB,WAAY,CAAA,AInDd,6BfiDE,yBRjHmB,AQkHnB,8EA7E8E,AA+E9E,cRpIkB,AQ2GlB,gBThE0C,ASiE1C,eTjE0C,ADiB1C,gBAAgB,AAChB,uBAAuB,AACvB,mBAAmB,AACnB,iBAAiB,AyBgCf,kBxBxDuC,AwByDvC,iBAAiB,AACjB,iBxBtDwC,AwBuDxC,WA/EwE,AAgFxE,kBAAkB,AAClB,QAAQ,AACR,kBAAkB,AAClB,MAAM,AACN,UAtFuC,CAAA,AfqHzC,mCAgBA,4BAA4B,AAC5B,yBRxImB,AQyInB,6ER1Ja,CAAA,AQ4Ib,4EAkBA,yBR/ImB,AQgJnB,sBAAsB,AACtB,8ERhKa,CAAA,AQiJb,gFAmBA,sCRtJmB,AQuJnB,sBAAsB,AACtB,gBAAgB,AAChB,0BR/Ja,AQgKb,mBAAmB,AACnB,YAAa,CAAA,AAEb,wNAEE,+BR/JiB,CAAA,AuBoErB,mCfsEE,4BAA4B,AAC5B,yBRxImB,AQyInB,6ER1Ja,CAAA,AuBkFf,oCf4EE,yBR/ImB,AQgJnB,sBAAsB,AACtB,8ERhKa,CAAA,AuBoHb,kCJqBA,epBhHsC,AoBiHtC,YpBvFuC,AoBwFvC,iBpBxFuC,AwBmErC,kBAAyE,CAAA,AJuB3E,2FAEE,cAA4C,CAAA,AI3B9C,wCfPA,gBTjEkC,ASkElC,eTlEkC,AwB8E9B,iBxB9E8B,AwB+E9B,WArG4E,AAsG5E,UAxG6C,CAAA,AA4GjD,iCJ8CA,6BnB9Ka,AmBgLb,gKnBhLa,AmBkLb,cnBhKmB,AuBgHjB,0BvBvHW,CAAA,AmB4Jb,4DACE,0BnB7JW,CAAA,AmB4Jb,8CACE,0BnB7JW,CAAA,AmByKb,uCACE,gJnBrLW,CAAA,AmByLb,2CACE,4CnB1LW,CAAA,AmB6Lb,wFA5BA,6BnB3JkB,AmB4JlB,gBAAgB,AAChB,0BnBxJa,CAAA,AuBqHb,uCfwFA,yBRlNkB,AQmNlB,+EAhKoF,AAiKpF,uCR1Na,AQ2Nb,aRzMmB,CAAA,AQ2MnB,6IAGE,aR9MiB,CAAA,AQiNnB,6CAqBA,yBRnPkB,AQoPlB,sCRzPa,CAAA,AQuOb,gGAsBA,yBR1PkB,AQ2PlB,sBAAsB,AACtB,wER/Pa,CAAA,AQ4Ob,oGAuBA,mCR7PkB,AQ8PlB,sBAAsB,AACtB,gBAAgB,AAChB,0BR3Pa,CAAA,AQ6Pb,0HACE,4BRnQgB,CAAA,AQ2OlB,6EACE,6BRlPW,AQmPX,cRzOW,CAAA,AuBsHb,6CfwHA,yBRnPkB,AQoPlB,sCRzPa,CAAA,AuBgIb,8Cf6HA,yBR1PkB,AQ2PlB,sBAAsB,AACtB,wER/Pa,CAAA,AuBkFf,6BAmEmC,6EvBrJpB,CAAA,AwB6Bf,gBACE,aAAa,AACb,sBAAsB,AACtB,eAAiC,CAgB8B,AAnBjE,gCAMI,iBAAgC,CAAA,AANpC,6BAUI,cAA4D,CAAA,AAVhE,sCAcI,cxBnCW,AwBoCX,ezBlBoC,AyBmBpC,cAA6B,CAAA,AAhBjC,yDAuBQ,axB3BO,CAAA,AwBIf,yDAuBQ,axBrBQ,CAAA,AwBFhB,yDAuBQ,axBfS,CAAA,AwBRjB,wDAuBQ,axBTM,CAAA,AwBdd,2BA6BI,uBAAuB,AACvB,kBAAmB,CAAA,AA9BvB,qDAiCM,iBzBXmC,AyBYnC,iBAA2B,CAAA,AAlCjC,2CAsCM,iBzBjB6B,AyBkB7B,iBAA2B,CAAA,AAvCjC,wIAiDM,mCAAyC,CAAA,AAI7C,mEAIQ,axB1DK,CAAA,AwBsDb,mEAIQ,axBpDM,CAAA,AwBgDd,mEAIQ,axB9CO,CAAA,AwB0Cf,kEAIQ,axBxCI,CAAA,AwBoCZ,gDAUI,axBjFS,CAAA,AwBuEb,sKAmBM,oCAA8C,CAAA,ACjEtD,iBACE,cAAc,AACd,iBAAkB,CAAA,AAFpB,4BAMI,kBAAkB,AAClB,UAAW,CAAA,AAPf,8CAWM,iB1BG6B,CAAA,A0BdnC,6CAeM,kB1BD6B,CAAA,A0BdnC,sIAuBI,kBAAkB,AAClB,KAAM,CAAA,AAxBV,sLA4BM,MAAO,CAAA,AA5Bb,kLAgCM,OAAQ,CAAA,AAhCd,6BjByEE,gBThE0C,ASiE1C,eTjE0C,A0B6BxC,WAAqD,AACrD,ajBvE0C,CAAA,AiBgC9C,mCA0Cc,SAAU,CAAA,AA1CxB,sEAgDI,SAAU,CAAA,AAhDd,gFAsDI,azBlFW,CAAA,AyB4Bf,4FhBlBE,+BAAoD,AACpD,eViB0B,AUhB1B,kBAAkB,AAClB,gBAAgB,AAChB,cAAc,AAZd,kCAAkC,AAClC,kCAAmC,CAAA,AgByBrC,gIAkEI,UAAuD,CAAA,AAlE3D,0BAsEI,UAAyB,CAAA,AAtE7B,kMA8EM,azB1GS,CyB6GwD,AACjE,sNACE,azB5GO,CAAA,AyByBf,gqBAuFQ,azBnHO,CAAA,AyB4Bf,0uBAiGQ,mCAAyC,CAAA,AAjGjD,8BAyGI,kBAAmB,CAAA,AAzGvB,wCA4GM,yBzBxIS,CAAA,AyB4Bf,uCjByEE,gBTjEkC,ASkElC,eTlEkC,A0B2G9B,UAAiE,CAAA,AAnHvE,8JAyHM,WAA6D,CAAA,AAzHnE,sCNqGE,epBhHsC,AoBiHtC,YpBvFuC,AoBwFvC,gBpBxFuC,CAAA,AoB0FvC,mGAEE,cAA4C,CAAA,AM3GhD,wDAgIQ,iB1BrHkC,CAAA,A0BX1C,uDAoIQ,kB1BzHkC,CAAA,A0BX1C,2EjByEE,gBT/D0C,ASgE1C,eThE0C,A0BuItC,UAAgE,CAAA,AAjJtE,8JAuJM,UAA6D,CAAA,AAvJnE,sCNgHE,epB1HsC,AoB2HtC,YpBjGyC,AoBkGzC,iBpBlGyC,AoBmGzC,iBApJmE,AAqJnE,iBArJmE,CAAA,AAuJnE,mGAEE,cAAuC,CAAA,AMxH3C,wDA8JQ,iBAA2D,CAAA,AA9JnE,uDAkKQ,kBAA4D,CAAA,AAlKpE,0BAwKI,cAAc,AACd,UAAW,CAAA,AAzKf,iHAgLM,kB1BlK6B,CAAA,A0BsKjC,qCAEI,azB/MS,CAAA,AyB6Mb,kDAMI,0BzBnNS,CAAA,AyByBf,+CNgKE,6JnBpMa,CAAA,AmBwMb,qDACE,4FnBzMW,CAAA,AmB6Mb,yDACE,kCnBpLW,CAAA,AmBuLb,oHAEE,eAAgB,CAAA,AM/KpB,8CAqMQ,azBhNO,CAAA,AyBkNP,wDACE,azBhNK,CAAA,AyBQf,+CNgKE,2JnBpMa,CAAA,AmBwMb,qDACE,2FnBzMW,CAAA,AmB6Mb,yDACE,kCnB9KY,CAAA,AmBiLd,oHAEE,eAAgB,CAAA,AM/KpB,8CAqMQ,azB1MQ,CAAA,AyB4MR,wDACE,azB1MM,CAAA,AyBEhB,+CNgKE,6JnBpMa,CAAA,AmBwMb,qDACE,4FnBzMW,CAAA,AmB6Mb,yDACE,kCnBxKa,CAAA,AmB2Kf,oHAEE,eAAgB,CAAA,AM/KpB,8CAqMQ,azBpMS,CAAA,AyBsMT,wDACE,azBpMO,CAAA,AyBJjB,8CNgKE,2JnBpMa,CAAA,AmBwMb,oDACE,2FnBzMW,CAAA,AmB6Mb,wDACE,kCnBlKU,CAAA,AmBqKZ,kHAEE,eAAgB,CAAA,AM/KpB,6CAqMQ,azB9LM,CAAA,AyBgMN,uDACE,azB9LI,CAAA,A0B1Bd,WP6DE,wBAAgB,AAAhB,qBAAgB,AAAhB,gBAAgB,AAChB,gBnB9Da,AmB+Db,YAAY,AACZ,kBpB3CyC,AoB4CzC,sInBrFa,AmBsFb,cnBpFkB,AmBqFlB,epB/DgC,AoBgEhC,gBApFqB,AAqFrB,YpBvCiC,AoBwCjC,iBpBxCiC,AoB0CjC,aAAa,AACb,epBtFiB,AoBuFjB,oDpBSkD,AoBRlD,qBAAsB,CAAA,AA0BtB,sCACE,0BnBlHW,AmBoHX,SAAU,CAAA,AAHZ,wBACE,0BnBlHW,AmBoHX,SAAU,CAAA,AA3BZ,uCAEE,4FnBnGW,CAAA,AmBsGb,6CAEE,mBpBtD+B,AoBwD/B,sBAAsB,AACtB,iBpBpGe,CAAA,AoBuGjB,qBACE,6CnB/GW,CAAA,AmBkHb,4CAeA,gCnBnHmB,AmBoHnB,gBAAgB,AAChB,0BnB3Ha,AmB4Hb,mBAAmB,AACnB,WAAY,CAAA,AOjHd,qBPqHE,epBhHsC,AoBiHtC,YpBvFuC,AoBwFvC,gBpBxFuC,CAAA,AoB0FvC,iEAEE,cAA4C,CAAA,AO3HhD,qBPgIE,epB1HsC,AoB2HtC,YpBjGyC,AoBkGzC,iBpBlGyC,AoBmGzC,iBApJmE,AAqJnE,iBArJmE,CAAA,AAuJnE,iEAEE,cAAuC,CAAA,AOxI3C,oBAYI,cAAc,AACd,UAAW,CAAA,AAGb,qBP0IA,6BnB9Ka,AmBgLb,gKnBhLa,AmBkLb,anBhKmB,CAAA,AmBqJnB,gDACE,0BnB7JW,CAAA,AmB4Jb,kCACE,0BnB7JW,CAAA,AmByKb,2BACE,gJnBrLW,CAAA,AmByLb,+BACE,4CnB1LW,CAAA,AmB6Lb,gEA5BA,6BnB3JkB,AmB4JlB,gBAAgB,AAChB,0BnBxJa,CAAA,A0BSf,8BPgLE,6JnBpMa,CAAA,AmBwMb,oCACE,4FnBzMW,CAAA,AmB6Mb,wCACE,kCnBpLW,CAAA,AmBuLb,kFAEE,eAAgB,CAAA,AOvKd,wCP4KJ,uLnBxNa,CAAA,AmB4Nb,8CACE,gJnB7NW,CAAA,AmBiOb,kDACE,kCnBxMW,CAAA,AmB2Mb,sGAEE,eAAgB,CAAA,AOnNpB,8BPgLE,2JnBpMa,CAAA,AmBwMb,oCACE,2FnBzMW,CAAA,AmB6Mb,wCACE,kCnB9KY,CAAA,AmBiLd,kFAEE,eAAgB,CAAA,AOvKd,wCP4KJ,oLnBxNa,CAAA,AmB4Nb,8CACE,+InB7NW,CAAA,AmBiOb,kDACE,kCnBlMY,CAAA,AmBqMd,sGAEE,eAAgB,CAAA,AOnNpB,8BPgLE,6JnBpMa,CAAA,AmBwMb,oCACE,4FnBzMW,CAAA,AmB6Mb,wCACE,kCnBxKa,CAAA,AmB2Kf,kFAEE,eAAgB,CAAA,AOvKd,wCP4KJ,uLnBxNa,CAAA,AmB4Nb,8CACE,gJnB7NW,CAAA,AmBiOb,kDACE,kCnB5La,CAAA,AmB+Lf,sGAEE,eAAgB,CAAA,AOnNpB,6BPgLE,2JnBpMa,CAAA,AmBwMb,mCACE,2FnBzMW,CAAA,AmB6Mb,uCACE,kCnBlKU,CAAA,AmBqKZ,gFAEE,eAAgB,CAAA,AOvKd,uCP4KJ,oLnBxNa,CAAA,AmB4Nb,6CACE,+InB7NW,CAAA,AmBiOb,iDACE,kCnBtLU,CAAA,AmByLZ,oGAEE,eAAgB,CAAA,AOnNpB,sBA+BI,YAAa,CAAA,AAsCjB,mBACE,eAAe,AACf,Y3BpFiB,CAAA,A2BkFnB,6EASI,YAAY,AACZ,mBAAoB,CAAA,AAVxB,6BAcI,WPpGiE,CAAA,AOuGnE,6BPoEA,6BnB9Ka,AmBgLb,gKnBhLa,AmBkLb,anBhKmB,CAAA,AmBqJnB,wDACE,0BnB7JW,CAAA,AmB4Jb,0CACE,0BnB7JW,CAAA,AmByKb,mCACE,gJnBrLW,CAAA,AmByLb,uCACE,4CnB1LW,CAAA,AmB6Lb,gFA5BA,6BnB3JkB,AmB4JlB,gBAAgB,AAChB,0BnBxJa,CAAA,A2BwBf,gBACE,cAAc,AACd,mBAAoC,AACpC,YAAa,CAAA,AAHf,yJAUI,cAAc,AACd,eAA6B,AAC7B,mBAAoB,CAAA,AAZxB,kCAgBI,cAA6B,CAAA,AAhBjC,2EAqBI,gBAAgB,AAChB,mBAAmB,AACnB,UAAW,CAAA,AAvBf,0EA6BM,yB3BxDS,CAAA,A2B2Bf,2BAkCI,gB5BnB+B,CAAA,A4BfnC,qNAyCM,qBAAqB,AACrB,iBAAiC,AACjC,kBAAmB,CAAA,AA3CzB,6CA+CM,gBAAiC,CAAA,AA/CvC,uDAmDM,aAAc,CAAA,AAnDpB,qCAuDM,gB5BvCmC,CAAA,A4BhBzC,qDA4DI,aAAc,CAAA,AAGhB,0BACE,a3BjFiB,CAAA,A2BgFnB,8FAMM,0B3B7FO,CAAA,A4BZf,8DAKI,cAAqC,AACrC,aAAa,AACb,UAAU,AACV,U7BqCgC,CAAA,A6B7CpC,0EAWM,uBAAsC,CAAA,AAX5C,yEAeM,uBAAsC,CAAA,AAf5C,sFAuBQ,uBAAsC,CAAA,AAvB9C,qFA2BQ,uB7BemC,CAAA,A6B1C3C,wEAiCI,U7BesC,CAAA,A8BnC1C,KACE,aAAc,CAAA,ACWhB,2CvBpBI,oBAAoB,AAItB,mBCuF8B,AAC9B,mBAAmB,AAEnB,YAAY,AAEZ,eAAe,AACf,eT7EgC,AS8EhC,uBAAuB,AAEvB,gBAAgB,AAChB,sBAAsB,AA0BtB,yBRjHmB,AQkHnB,8EA7E8E,AA8E9E,8ERrIa,AQsIb,cRpIkB,A+BGlB,qBAAqB,AACrB,wBAAwB,AACxB,kBhCkCyC,AgCjCzC,YhCoCkC,AgCnClC,sBhCFiB,AgCIjB,UAAW,CACqC,AxBFhD,+CACE,YAAY,AACZ,aAAc,CAAA,AAGhB,+DACE,YAAY,AACZ,aAAc,CAAA,AAehB,wGAGE,gBC7BkE,CAAA,ADiCpE,wIAEE,cAAkB,CAAA,AC+FpB,uDAgBA,4BAA4B,AAC5B,yBRxImB,AQyInB,6ER1Ja,CAAA,AQ4Ib,0HAkBA,yBR/ImB,AQgJnB,sBAAsB,AACtB,8ERhKa,CAAA,AQiJb,kIAmBA,sCRtJmB,AQuJnB,sBAAsB,AACtB,gBAAgB,AAChB,0BR/Ja,AQgKb,mBAAmB,AACnB,YAAa,CAAA,AAEb,oXAEE,+BR/JiB,CAAA,A8BUrB,mEtB8QE,gBA3NoC,AA4NpC,eAAgB,CAAA,AAEhB,+EACE,gCR/RW,AQgSX,gBAAgB,AAChB,cR1SgB,AQ2ShB,oBAAqB,CAAA,AAGvB,0KAEE,gCRzSW,AQ0SX,gBAAgB,AAChB,aRlTgB,CAAA,AQqTlB,4XAIE,gBAAgB,AAChB,0BRpTW,AQqTX,kBAAmB,CAAA,AAEnB,odACE,+BRvTS,CAAA,AQ2Tb,8KAgBA,gBAtQyC,AAuQzC,gBAAgB,AAChB,aAAc,CAAA,AAlBd,0mBAuBE,gBAAgB,AAChB,eAAgB,CAAA,AAxBlB,sMA4BE,gCRtVW,CAAA,AQ0Tb,oaAiCE,gCR3VW,AQ4VX,aRpViB,CAAA,AQkTnB,w5BAyCE,gBAAgB,AAChB,2BRnWW,AQoWX,kBAAmB,CAAA,AA3CrB,wkCA8CI,+BRxWS,CAAA,AQ+TX,yGA+CF,aR/Va,CAAA,AQiWb,2WAGE,gBAAgB,AAChB,gBAAgB,AAChB,aRtWW,CAAA,AQyWb,qHACE,gCRzWW,AQ0WX,aR3WW,CAAA,AQ8Wb,sPAEE,+BR/WW,AQgXX,aRjXW,CAAA,AQoXb,8PAEE,gBAAgB,AAChB,yBRvXW,CAAA,AQyXX,0SACE,8BRzXS,CAAA,AQ6Xb,qLACE,cR/XW,CAAA,AQkYb,0PACE,aRhYW,CAAA,AQ+Xb,kRAII,+BRrYS,AQsYT,aRpYS,CAAA,AQ+Xb,4jBAUI,+BR3YS,AQ4YT,aR1YS,CAAA,AQ+Xb,4kBAgBI,gBAAgB,AAChB,yBRhZS,CAAA,AQ+Xb,oqBAoBM,8BRrZO,CAAA,AQ+SX,yGA+CF,aRzVc,CAAA,AQ2Vd,2WAGE,gBAAgB,AAChB,gBAAgB,AAChB,aRhWY,CAAA,AQmWd,qHACE,+BRnWY,AQoWZ,aRrWY,CAAA,AQwWd,sPAEE,8BRzWY,AQ0WZ,aR3WY,CAAA,AQ8Wd,8PAEE,gBAAgB,AAChB,wBRjXY,CAAA,AQmXZ,0SACE,6BRnXU,CAAA,AQuXd,qLACE,cRzXY,CAAA,AQ4Xd,0PACE,aR1XY,CAAA,AQyXd,kRAII,8BR/XU,AQgYV,aR9XU,CAAA,AQyXd,4jBAUI,8BRrYU,AQsYV,aRpYU,CAAA,AQyXd,4kBAgBI,gBAAgB,AAChB,yBR1YU,CAAA,AQyXd,oqBAoBM,6BR/YQ,CAAA,AQySZ,yGA+CF,aRnVe,CAAA,AQqVf,2WAGE,gBAAgB,AAChB,gBAAgB,AAChB,aR1Va,CAAA,AQ6Vf,qHACE,gCR7Va,AQ8Vb,aR/Va,CAAA,AQkWf,sPAEE,+BRnWa,AQoWb,aRrWa,CAAA,AQwWf,8PAEE,gBAAgB,AAChB,yBR3Wa,CAAA,AQ6Wb,0SACE,8BR7WW,CAAA,AQiXf,qLACE,cRnXa,CAAA,AQsXf,0PACE,aRpXa,CAAA,AQmXf,kRAII,+BRzXW,AQ0XX,aRxXW,CAAA,AQmXf,4jBAUI,+BR/XW,AQgYX,aR9XW,CAAA,AQmXf,4kBAgBI,gBAAgB,AAChB,0BRpYW,CAAA,AQmXf,oqBAoBM,8BRzYS,CAAA,AQmSb,uGA+CF,aR7UY,CAAA,AQ+UZ,qWAGE,gBAAgB,AAChB,gBAAgB,AAChB,aRpVU,CAAA,AQuVZ,mHACE,+BRvVU,AQwVV,aRzVU,CAAA,AQ4VZ,kPAEE,8BR7VU,AQ8VV,aR/VU,CAAA,AQkWZ,0PAEE,gBAAgB,AAChB,wBRrWU,CAAA,AQuWV,sSACE,6BRvWQ,CAAA,AQ2WZ,mLACE,cR7WU,CAAA,AQgXZ,sPACE,aR9WU,CAAA,AQ6WZ,8QAII,8BRnXQ,AQoXR,aRlXQ,CAAA,AQ6WZ,ojBAUI,8BRzXQ,AQ0XR,aRxXQ,CAAA,AQ6WZ,okBAgBI,gBAAgB,AAChB,0BR9XQ,CAAA,AQ6WZ,4pBAoBM,6BRnYM,CAAA,A8BpBd,+DCJE,ehCKsC,AgCJtC,YhC0BwC,AgCzBxC,kBAA8C,CAAA,ADkD9C,+DtBgJA,yBRlNkB,AQmNlB,+EAhKoF,AAiKpF,uCR1Na,AQ2Nb,aRzMmB,CAAA,AQ2MnB,6OAGE,aR9MiB,CAAA,AQiNnB,2EAqBA,yBRnPkB,AQoPlB,sCRzPa,CAAA,AQuOb,kKAsBA,yBR1PkB,AQ2PlB,sBAAsB,AACtB,wER/Pa,CAAA,AQ4Ob,0KAuBA,mCR7PkB,AQ8PlB,sBAAsB,AACtB,gBAAgB,AAChB,0BR3Pa,CAAA,AQ6Pb,sNACE,4BRnQgB,CAAA,AQ2OlB,2IACE,6BRlPW,AQmPX,cRzOW,CAAA,A8Bcf,6DCME,sC/BhBmB,A+BiBnB,gBAAgB,AAChB,0B/BxBa,A+ByBb,kBAAmB,CAAA,ADTrB,mECaE,c/B7Ba,A+B8Bb,oBAAoB,AACpB,kBAAkB,AAClB,UAAsC,AACtC,OAAqD,CAAA,ADjBvD,0GCoBI,yB/BpCW,CAAA,A8BgBf,6BAEE,qBAAqB,AACrB,sBAAsB,AACtB,kBAAkB,AAClB,qBAAsB,CAAA,AALxB,mEAgBM,YAAa,CAAA,AAhBnB,iDrBEE,aTlBa,CAAA,ASoBb,6DACE,aT3BgB,CAAA,AS8BlB,qEACE,aTtBW,CAAA,ASqBb,iFAII,aTlBe,CAAA,A8BMrB,kIAoCM,WAA0B,AAC1B,QAA2D,CAAA,AArCjE,4GA4CM,UAAW,CAAA,AAIf,+DAMI,yB9BzEc,A8B0Ed,a9B7De,CAAA,A8BsDnB,iFAWI,0B9BxES,CAAA,A8B6Db,6DAeI,a9B5ES,CAAA,A8BiFf,kBrB1EE,+BAAoD,AACpD,eViB0B,AUhB1B,kBAAkB,AAClB,gBAAgB,AAChB,cAAc,AAZd,kCAAkC,AAClC,mCAAmC,AqBqFjC,eEiB4B,CAAO,A9BevC,6C+BhFE,iBAAiB,AACjB,clCzBgC,CkCoEsB,A/BoCxD,sG+B3EI,anC6ByC,AmC5BzC,gBAAgB,AAChB,kBAAmB,CAAA,A/ByEvB,mD+BrEI,cjCzDgB,AiC0DhB,eAAgB,CAAA,A/BoEpB,mD+BhEI,ajC9DgB,CAAA,AE8HpB,0L+B1DM,6CjCtES,CAAA,AiC4Eb,wNAMI,ajChEe,CAAA,AiC0DnB,sVAYM,8CjCpEO,CAAA,AiC2Ef,sKASM,mBnCtBuC,AmCuBvC,enCvBuC,CAAA,AmCa7C,uEAgBM,gCjCnGS,CAAA,AiCmFf,kEAuBM,6CjCtHS,CAAA,AiC+Ff,yDA2BM,6CjC1HS,CAAA,AiC+Ff,2EA8BQ,+CjC7HO,CAAA,AiC+Ff,kGAoCQ,eAAgB,CAAA,AApCxB,oHAuCU,6CjCtIK,CAAA,AiC+Ff,uDAgDQ,sCjCnIO,AiCoIP,cAAe,CAAA,AAjDvB,wDAqDQ,qCjCxIO,CAAA,AiC6Ib,iFAIM,+BjCrJO,CAAA,AiCiJb,4EAWM,8CjChJO,CAAA,AiCqIb,mEAeM,8CjCpJO,CAAA,AiCqIb,qFAkBQ,gDjCvJK,CAAA,AiCqIb,4GAwBQ,8CjC7JK,CiCgKoC,AA3BjD,wHA6BU,eAAgB,CAAA,AA7B1B,iEAsCQ,qCjCvLK,AiCwLL,cAAe,CAAA,AAvCvB,kEA2CQ,oCjC5LK,CAAA,AkCRf,e3BMI,aAAa,AAEf,mB2BP8B,AAC9B,kBAAmB,CAAA,A3BQnB,iBACE,YAAY,AACZ,aAAc,CAAA,AAGhB,yBACE,YAAY,AACZ,aAAc,CAAA,AAehB,uCAGE,gB2BlC+C,CAAA,A3BsCjD,uDAEE,cAAkB,CAAA,A2BpCtB,mBACE,iBAAiB,AACjB,QAAsB,CAAA,AAFxB,oCAKI,SAAS,AACT,SAAU,CAAA,AANd,qCAUI,WAAY,CAAA,AAIhB,mBACE,YAAY,AACZ,gBAAgB,AAChB,gBAAgB,AAChB,YAA0B,CAAA,AAJ5B,gCAOI,kBAAgC,CAAA,AAPpC,kDAUM,eAA6B,CAAA,AAKnC,YACE,mBAAmB,AACnB,aAAa,AACb,8BAA8B,AAC9B,cAAc,AACd,cAAe,CAAA,AALjB,6BAQI,kBnCnCe,CAAA,AoCNnB,UAGE,qBAAqB,AAErB,cAAc,AAEd,0BAA2B,CAAA,AAP7B,6BAYI,qBAAsB,AACtB,uBAAyB,CACH,AAd1B,cAmBI,aAAc,CAAA,AAnBlB,0BAuBM,iBAAkB,CAAA,AAMxB,sGAGM,anCRS,CAAA,AmCUT,oIACE,anCRO,CAAA,AmCEf,sGAGM,anCFU,CAAA,AmCIV,oIACE,anCFQ,CAAA,AmCJhB,sGAGM,anCIW,CAAA,AmCFX,oIACE,anCIS,CAAA,AmCVjB,mGAGM,anCUQ,CAAA,AmCRR,iIACE,anCUM,CAAA,AmCAd,uB1B5BE,+BAAoD,AACpD,cViB0B,CoCYL,AAGvB,2C1B/BE,kBAAkB,AAClB,gBAAgB,AAChB,cAAc,AAZd,kCAAkC,AAClC,mCAAmC,A0BqCnC,oBAAqB,CAKA,AAFvB,oB1BjCE,+BAAoD,AACpD,cVkBuB,CoCgBF,AAIvB,oBACE,oBpCxBwB,AoCyBxB,kBAAkB,AAClB,kBAAkB,AAClB,gBAAgB,AAChB,aAAc,CAAA,AALhB,2B1B/CE,kCAAkC,AAClC,kCAAmC,CAAA,A0B2DnC,qBACE,eHxEU,CAAO,AGuEnB,iCACE,eHvEsB,CAAO,AGsE/B,kCACE,eHtEuB,CAAO,AGqEhC,gCACE,eHrEqB,CAAO,AGoE9B,6BACE,eHpEkB,CAAO,AGmE3B,iCACE,eHnEsB,CAAO,AGkE/B,+BACE,eHlEoB,CAAO,AGiE7B,0BACE,eHjEe,CAAO,AGgExB,8BACE,eHhEmB,CAAO,AG+D5B,+BACE,eH/DoB,CAAO,AG8D7B,4BACE,eH9DiB,CAAO,AG6D1B,6BACE,eH7DkB,CAAO,AG4D3B,kCACE,eH5DuB,CAAO,AG2DhC,6CACE,eH3DkC,CAAO,AG0D3C,gCACE,eH1DqB,CAAO,AGyD9B,iCACE,eHzDsB,CAAO,AGwD/B,+BACE,eHxDoB,CAAO,AGuD7B,2CACE,eHvDgC,CAAO,AGsDzC,4BACE,eHtDiB,CAAO,AGqD1B,6BACE,eHrDkB,CAAO,AGoD3B,8BACE,eHpDmB,CAAO,AGmD5B,yBACE,eHnDc,CAAO,AGkDvB,mCACE,eHlDwB,CAAO,AGiDjC,oCACE,eHjDyB,CAAO,AGgDlC,4BACE,eHhDiB,CAAQ,AG+C3B,4BACE,eH/CiB,CAAO,AG8C1B,6BACE,eH9CkB,CAAO,AG6C3B,gCACE,eH7CqB,CAAO,AG4C9B,iCACE,eH5CsB,CAAO,AG2C/B,0BACE,eH3Ce,CAAQ,AG0CzB,mCACE,eH1CwB,CAAQ,AGyClC,iCACE,eHzCsB,CAAQ,AGwChC,0BACE,WHxCsB,CAAA,AGuCxB,mCACE,eHvCwB,CAAO,AGsCjC,uBACE,eHtCY,CAAO,AGqCrB,4BACE,eHrCiB,CAAO,AGoC1B,8BACE,eHpCmB,CAAO,AGmC5B,yBACE,eHnCc,CAAO,AGkCvB,uBACE,eHlCY,CAAO,AGiCrB,gCACE,eHjCqB,CAAO,AGgC9B,sBACE,eHhCW,CAAO,AG+BpB,sBACE,eH/BW,CAAO,AG8BpB,0BACE,eH9Be,CAAO,AG6BxB,qBACE,eH7BU,CAAO,AG4BnB,2BACE,eH5BgB,CAAO,AG2BzB,4BACE,eH3BiB,CAAO,AG0B1B,uBACE,eH1BY,CAAO,AGyBrB,4BACE,eHzBiB,CAAO,AGwB1B,0BACE,eHxBe,CAAO,AGuBxB,wBACE,eHvBa,CAAO,AGsBtB,4BACE,eHtBiB,CAAO,AGqB1B,4BACE,eHrBiB,CAAO,AGoB1B,6BACE,eHpBkB,CAAO,AGmB3B,0BACE,eHnBe,CAAO,AGkBxB,4BACE,eHlBiB,CAAO,AGiB1B,yBACE,eHjBc,CAAO,AGgBvB,uBACE,eHhBY,CAAO,AGerB,sBACE,eHfW,CAAO,AGcpB,kCACE,eHduB,CAAO,AGahC,8BACE,eHbmB,CAAO,AGY5B,iCACE,eHZsB,CAAO,AGW/B,8BACE,eHXmB,CAAO,AGU5B,+BACE,eHVoB,CAAO,AGS7B,4BACE,eHTiB,CAAO,AGQ1B,wBACE,eHRa,CAAO,AGOtB,mCACE,eHPwB,CAAO,AGMjC,mCACE,eHNwB,CAAO,AGKjC,oCACE,eHLyB,CAAO,AGIlC,iCACE,eHJsB,CAAO,AGG/B,0BACE,eHHe,CAAO,AGExB,uBACE,eHFY,CAAO,AGCrB,2BACE,eHDgB,CAAO,AGAzB,uBACE,eHAY,CAAO,AGDrB,gCACE,eHCqB,CAAO,AGF9B,8BACE,eHEmB,CAAO,AGH5B,sBACE,eHGW,CAAO,AGJpB,4BACE,eHIiB,CAAO,AGL1B,qBACE,eHKU,CAAO,AGNnB,8BACE,eHMmB,CAAO,AGP5B,+BACE,eHOoB,CAAO,AGR7B,yBACE,eHQc,CAAO,AGTvB,4BACE,eHSiB,CAAO,AGV1B,yBACE,eHUc,CAAO,AGXvB,4BACE,eHWiB,CAAO,AGZ1B,yBACE,eHYc,CAAO,AGbvB,yBACE,eHac,CAAO,AGdvB,0BACE,eHce,CAAO,AGfxB,yBACE,eHec,CAAO,AGhBvB,6BACE,eHgBkB,CAAO,AGjB3B,uBACE,eHiBY,CAAO,AGlBrB,uBACE,eHkBY,CAAO,AGnBrB,sBACE,eHmBW,CAAO,AGpBpB,0BACE,eHoBe,CAAO,AGrBxB,6BACE,eHqBkB,CAAO,AGtB3B,oCACE,eHsByB,CAAO,AGvBlC,qBACE,eHuBU,CAAO,AGxBnB,2BACE,eHwBgB,CAAO,AGzBzB,8BACE,eHyBmB,CAAO,AG1B5B,0BACE,eH0Be,CAAO,AG3BxB,wBACE,eH2Ba,CAAO,AG5BtB,uBACE,cH4BmB,CAAA,AG7BrB,+BACE,eH6BoB,CAAO,AG9B7B,yBACE,eH8Bc,CAAO,AG/BvB,2BACE,eH+BgB,CAAO,AGhCzB,8BACE,eHgCmB,CAAO,AGjC5B,gCACE,eHiCqB,CAAO,AGlC9B,iCACE,eHkCsB,CAAO,AGnC/B,yBACE,eHmCc,CAAO,AGpCvB,0BACE,eHoCe,CAAO,AGrCxB,+BACE,eHqCoB,CAAO,AGtC7B,gCACE,eHsCqB,CAAO,AGvC9B,wBACE,WHuCoB,CAAA,AGxCtB,qBACE,eHwCU,CAAO,AGzCnB,yCACE,eHyC8B,CAAO,AG1CvC,uCACE,eH0C4B,CAAO,AG3CrC,qCACE,eH2C0B,CAAO,AG5CnC,qCACE,eH4C0B,CAAO,AG7CnC,sCACE,eH6C2B,CAAO,AG9CpC,mCACE,eH8CwB,CAAO,AG/CjC,gCACE,eH+CqB,CAAO,AGhD9B,0BACE,eHgDe,CAAO,AGjDxB,wCACE,eHiD6B,CAAO,AGlDtC,sCACE,eHkD2B,CAAO,AGnDpC,sBACE,eHmDW,CAAO,AGpDpB,4BACE,eHoDiB,CAAO,AGrD1B,2BACE,eHqDgB,CAAO,AGtDzB,sBACE,eHsDW,CAAO,AGvDpB,uBACE,eHuDY,CAAO,AGxDrB,0BACE,eHwDe,CAAO,AGzDxB,0BACE,eHyDe,CAAO,AG1DxB,wBACE,eH0Da,CAAO,AG3DtB,wBACE,eH2Da,CAAO,AG5DtB,uBACE,eH4DY,CAAO,AG7DrB,sBACE,eH6DW,CAAO,AG9DpB,0BACE,eH8De,CAAO,AG/DxB,6BACE,eH+DkB,CAAO,AGhE3B,4BACE,eHgEiB,CAAO,AGjE1B,wBACE,eHiEa,CAAO,AGlEtB,yBACE,eHkEc,CAAO,AGnEvB,wBACE,eHmEa,CAAO,AGpEtB,0BACE,eHoEe,CAAO,AGrExB,+BACE,eHqEoB,CAAO,AGtE7B,8BACE,eHsEmB,CAAO,AGvE5B,sBACE,eHuEW,CAAO,AGxEpB,iCACE,eHwEsB,CAAO,AGzE/B,sBACE,eHyEW,CAAO,AG1EpB,wBACE,eH0Ea,CAAO,AG3EtB,6BACE,eH2EkB,CAAO,AG5E3B,6BACE,eH4EkB,CAAO,AG7E3B,6BACE,eH6EkB,CAAO,AG9E3B,+BACE,eH8EoB,CAAO,AG/E7B,sBACE,eH+EW,CAAO,AGhFpB,uBACE,eHgFY,CAAO,AGjFrB,uBACE,eHiFY,CAAO,AGlFrB,6BACE,eHkFkB,CAAO,AGnF3B,6BACE,eHmFkB,CAAO,AGpF3B,0BACE,eHoFe,CAAO,AGrFxB,6BACE,eHqFkB,CAAO,AGtF3B,6BACE,eHsFkB,CAAO,AGvF3B,oCACE,eHuFyB,CAAO,AGxFlC,uBACE,eHwFY,CAAO,AGzFrB,8BACE,eHyFmB,CAAO,AG1F5B,4BACE,eH0FiB,CAAO,AG3F1B,6BACE,eH2FkB,CAAO,AG5F3B,+BACE,eH4FoB,CAAO,AG7F7B,oCACE,eH6FyB,CAAO,AG9FlC,0BACE,eH8Fe,CAAO,AG/FxB,2BACE,eH+FgB,CAAO,AGhGzB,sBACE,eHgGW,CAAO,AGjGpB,sBACE,eHiGW,CAAO,AGlGpB,sBACE,eHkGW,CAAO,AGnGpB,6BACE,eHmGkB,CAAO,AGpG3B,oCACE,eHoGyB,CAAO,AGrGlC,4BACE,eHqGiB,CAAO,AGtG1B,0BACE,eHsGe,CAAO,AGvGxB,6BACE,eHuGkB,CAAO,AGxG3B,6BACE,eHwGkB,CAAO,AGzG3B,2BACE,eHyGgB,CAAO,AG1GzB,4BACE,eH0GiB,CAAO,AG3G1B,4BACE,eH2GiB,CAAO,AG5G1B,2BACE,eH4GgB,CAAO,AG7GzB,gCACE,eH6GqB,CAAO,AG9G9B,0BACE,eH8Ge,CAAO,AG/GxB,0BACE,eH+Ge,CAAO,AGhHxB,0BACE,eHgHe,CAAO,AGjHxB,uBACE,eHiHY,CAAO,AGlHrB,uBACE,eHkHY,CAAO,AGnHrB,+BACE,eHmHoB,CAAO,AGpH7B,uBACE,eHoHY,CAAO,AGrHrB,8BACE,eHqHmB,CAAO,AGtH5B,8BACE,eHsHmB,CAAO,AGvH5B,0CACE,eHuH+B,CAAO,AGxHxC,sBACE,eHwHW,CAAO,AGzHpB,2BACE,eHyHgB,CAAO,AG1HzB,+BACE,eH0HoB,CAAO,AG3H7B,mCACE,eH2HwB,CAAO,AG5HjC,sBACE,eH4HW,CAAO,AG7HpB,2BACE,eH6HgB,CAAO,AG9HzB,2BACE,eH8HgB,CAAO,AG/HzB,4BACE,eH+HiB,CAAO,AGhI1B,yBACE,eHgIc,CAAO,AGjIvB,wBACE,eHiIa,CAAO,AGlItB,4BACE,eHkIiB,CAAO,AGnI1B,4BACE,eHmIiB,CAAO,AGpI1B,yBACE,eHoIc,CAAO,AGrIvB,uBACE,eHqIY,CAAO,AGtIrB,8BACE,eHsImB,CAAO,AGvI5B,2BACE,eHuIgB,CAAO,AGxIzB,yBACE,eHwIc,CAAO,AGzIvB,sBACE,WHyIkB,CAAA,AG1IpB,mCACE,eH0IwB,CAAO,AG3IjC,2BACE,eH2IgB,CAAO,AG5IzB,yBACE,eH4Ic,CAAO,AG7IvB,sBACE,eH6IW,CAAO,AG9IpB,sCACE,eH8I2B,CAAO,AG/IpC,0CACE,eH+I+B,CAAO,AGhJxC,2CACE,eHgJgC,CAAO,AGjJzC,yCACE,eHiJ8B,CAAO,AGlJvC,2BACE,eHkJgB,CAAO,AGnJzB,mCACE,eHmJwB,CAAO,AGpJjC,oCACE,eHoJyB,CAAO,AGrJlC,wBACE,eHqJa,CAAO,AGtJtB,uBACE,eHsJY,CAAO,AGvJrB,gCACE,eHuJqB,CAAO,AGxJ9B,2BACE,eHwJgB,CAAO,AGzJzB,8BACE,eHyJmB,CAAO,AG1J5B,8BACE,eH0JmB,CAAO,AG3J5B,2BACE,eH2JgB,CAAO,AG5JzB,6BACE,eH4JkB,CAAO,AG7J3B,4BACE,eH6JiB,CAAO,AG9J1B,wBACE,eH8Ja,CAAO,AG/JtB,8BACE,eH+JmB,CAAO,AGhK5B,4BACE,eHgKiB,CAAO,AGjK1B,uBACE,eHiKY,CAAO,AGlKrB,8BACE,eHkKmB,CAAO,AGnK5B,2BACE,eHmKgB,CAAO,AGpKzB,wBACE,eHoKa,CAAO,AGrKtB,4BACE,eHqKiB,CAAO,AGtK1B,qBACE,eHsKU,CAAO,AGvKnB,+BACE,eHuKoB,CAAO,AGxK7B,6BACE,eHwKkB,CAAO,AGzK3B,6BACE,eHyKkB,CAAO,AG1K3B,4BACE,eH0KiB,CAAO,AG3K1B,2BACE,eH2KgB,CAAO,AG5KzB,4BACE,eH4KiB,CAAO,AG7K1B,4BACE,eH6KiB,CAAO,AG9K1B,2BACE,eH8KgB,CAAO,AG/KzB,yBACE,eH+Kc,CAAO,AGhLvB,+BACE,eHgLoB,CAAO,AGjL7B,0BACE,eHiLe,CAAO,AGlLxB,uBACE,eHkLY,CAAO,AGnLrB,uBACE,eHmLY,CAAO,AGpLrB,wBACE,eHoLa,CAAO,AGrLtB,wBACE,eHqLa,CAAO,AGtLtB,6BACE,eHsLkB,CAAO,AGvL3B,gCACE,eHuLqB,CAAO,AGxL9B,+BACE,eHwLoB,CAAO,AGzL7B,6BACE,eHyLkB,CAAO,AG1L3B,iCACE,eH0LsB,CAAO,AG3L/B,kCACE,eH2LuB,CAAO,AG5LhC,+BACE,eH4LoB,CAAO,AG7L7B,kCACE,eH6LuB,CAAO,AG9LhC,wCACE,eH8L6B,CAAO,AG/LtC,0BACE,eH+Le,CAAO,AGhMxB,2BACE,eHgMgB,CAAO,AGjMzB,2BACE,eHiMgB,CAAO,AGlMzB,uCACE,eHkM4B,CAAO,AGnMrC,2BACE,eHmMgB,CAAO,AGpMzB,2BACE,eHoMgB,CAAO,AGrMzB,sBACE,eHqMW,CAAO,AGtMpB,sBACE,eHsMW,CAAO,AGvMpB,8BACE,eHuMmB,CAAO,AGxM5B,kCACE,eHwMuB,CAAO,AGzMhC,wBACE,eHyMa,CAAO,AG1MtB,sBACE,eH0MW,CAAO,AG3MpB,wBACE,eH2Ma,CAAO,AG5MtB,yBACE,eH4Mc,CAAO,AG7MvB,wBACE,eH6Ma,CAAO,AG9MtB,uCACE,eH8M4B,CAAO,AG/MrC,qBACE,eH+MU,CAAO,AGhNnB,4BACE,eHgNiB,CAAO,AGjN1B,4BACE,eHiNiB,CAAO,AGlN1B,0BACE,eHkNe,CAAO,AGnNxB,uBACE,eHmNY,CAAO,AGpNrB,sBACE,eHoNW,CAAO,AGrNpB,6BACE,eHqNkB,CAAO,AGtN3B,2BACE,eHsNgB,CAAO,AGvNzB,+BACE,eHuNoB,CAAO,AGxN7B,6BACE,eHwNkB,CAAO,AGzN3B,0BACE,eHyNe,CAAO,AG1NxB,uBACE,eH0NY,CAAO,AG3NrB,8BACE,eH2NmB,CAAO,AG5N5B,8BACE,eH4NmB,CAAO,AG7N5B,sBACE,eH6NW,CAAO,AG9NpB,sBACE,eH8NW,CAAO,AG/NpB,0BACE,eH+Ne,CAAO,AGhOxB,sBACE,eHgOW,CAAO,AGjOpB,yBACE,eHiOc,CAAO,AGlOvB,8BACE,eHkOmB,CAAO,AGnO5B,uBACE,eHmOY,CAAO,AGpOrB,6BACE,eHoOkB,CAAO,AGrO3B,+BACE,eHqOoB,CAAO,AGtO7B,2BACE,eHsOgB,CAAO,AGvOzB,4BACE,eHuOiB,CAAO,AGxO1B,0BACE,eHwOe,CAAO,AGzOxB,4BACE,eHyOiB,CAAO,AG1O1B,4BACE,eH0OiB,CAAO,AG3O1B,kCACE,eH2OuB,CAAO,AG5OhC,8BACE,eH4OmB,CAAO,AG7O5B,uBACE,eH6OY,CAAO,AG9OrB,8BACE,eH8OmB,CAAO,AG/O5B,+BACE,eH+OoB,CAAO,AGhP7B,uCACE,eHgP4B,CAAO,AGjPrC,+BACE,eHiPoB,CAAO,AGlP7B,2BACE,eHkPgB,CAAO,AGnPzB,wBACE,eHmPa,CAAO,AGpPtB,yBACE,eHoPc,CAAO,AGrPvB,2BACE,eHqPgB,CAAO,AGtPzB,4BACE,eHsPiB,CAAO,AGvP1B,0BACE,eHuPe,CAAO,AGxPxB,6BACE,eHwPkB,CAAO,AGzP3B,6BACE,eHyPkB,CAAO,AG1P3B,6BACE,eH0PkB,CAAO,AG3P3B,2BACE,eH2PgB,CAAO,AG5PzB,2BACE,eH4PgB,CAAO,AG7PzB,sBACE,eH6PW,CAAO,AG9PpB,6BACE,eH8PkB,CAAO,AG/P3B,uBACE,eH+PY,CAAO,AGhQrB,wBACE,eHgQa,CAAO,AGjQtB,4BACE,eHiQiB,CAAO,AGlQ1B,wBACE,eHkQa,CAAO,AGnQtB,uBACE,eHmQY,CAAO,AGpQrB,2BACE,eHoQgB,CAAO,AGrQzB,qBACE,eHqQU,CAAO,AGtQnB,uBACE,eHsQY,CAAO,AGvQrB,6BACE,eHuQkB,CAAO,AGxQ3B,sBACE,eHwQW,CAAO,AGzQpB,sBACE,WHyQkB,CAAA,AG1QpB,gCACE,eH0QqB,CAAO,AG3Q9B,uBACE,eH2QY,CAAO,AG5QrB,qCACE,eH4Q0B,CAAO,AG7QnC,8BACE,eH6QmB,CAAO,AG9Q5B,8BACE,eH8QmB,CAAO,AG/Q5B,uBACE,eH+QY,CAAO,AGhRrB,0BACE,eHgRe,CAAO,AGjRxB,4BACE,eHiRiB,CAAO,AGlR1B,0BACE,eHkRe,CAAO,AGnRxB,kCACE,eHmRuB,CAAO,AGpRhC,uBACE,eHoRY,CAAO,AGrRrB,wBACE,eHqRa,CAAO,AGtRtB,wBACE,eHsRa,CAAO,AGvRtB,sBACE,eHuRW,CAAO,AGxRpB,yBACE,eHwRc,CAAO,AGzRvB,kCACE,eHyRuB,CAAO,AG1RhC,wBACE,eH0Ra,CAAO,AG3RtB,+BACE,eH2RoB,CAAO,AG5R7B,oCACE,eH4RyB,CAAO,AG7RlC,qCACE,eH6R0B,CAAO,AG9RnC,mCACE,eH8RwB,CAAO,AG/RjC,gCACE,eH+RqB,CAAO,AGhS9B,wBACE,eHgSa,CAAO,AGjStB,uBACE,eHiSY,CAAO,AGlSrB,yBACE,eHkSc,CAAO,AGnSvB,qBACE,eHmSU,CAAO,AGpSnB,4BACE,eHoSiB,CAAO,AGrS1B,sBACE,eHqSW,CAAO,AGtSpB,iCACE,eHsSsB,CAAO,AGvS/B,6BACE,eHuSkB,CAAO,AGxS3B,2BACE,eHwSgB,CAAO,AGzSzB,uBACE,eHySY,CAAO,AG1SrB,8BACE,eH0SmB,CAAO,AG3S5B,wBACE,eH2Sa,CAAO,AG5StB,+BACE,eH4SoB,CAAO,AG7S7B,iCACE,eH6SsB,CAAO,AG9S/B,6BACE,eH8SkB,CAAO,AG/S3B,mCACE,eH+SwB,CAAO,AGhTjC,wBACE,eHgTa,CAAO,AGjTtB,2BACE,eHiTgB,CAAO,AGlTzB,yBACE,eHkTc,CAAO,AGnTvB,+BACE,eHmToB,CAAO,AGpT7B,6BACE,eHoTkB,CAAO,AGrT3B,4BACE,eHqTiB,CAAO,AGtT1B,sCACE,eHsT2B,CAAO,AGvTpC,gCACE,eHuTqB,CAAO,AGxT9B,iCACE,eHwTsB,CAAO,AGzT/B,+BACE,eHyToB,CAAO,AG1T7B,0BACE,eH0Te,CAAO,AG3TxB,uBACE,eH2TY,CAAO,AG5TrB,wBACE,eH4Ta,CAAO,AG7TtB,sBACE,eH6TW,CAAO,AG9TpB,+BACE,eH8ToB,CAAO,AG/T7B,+BACE,eH+ToB,CAAO,AGhU7B,0BACE,eHgUe,CAAO,AGjUxB,uBACE,eHiUY,CAAO,AGlUrB,6BACE,eHkUkB,CAAO,AGnU3B,6BACE,eHmUkB,CAAO,AGpU3B,4BACE,eHoUiB,CAAO,AGrU1B,4BACE,eHqUiB,CAAO,AGtU1B,2BACE,eHsUgB,CAAO,AGvUzB,8BACE,eHuUmB,CAAO,AGxU5B,sBACE,eHwUW,CAAO,AGzUpB,mCACE,eHyUwB,CAAO,AG1UjC,wCACE,eH0U6B,CAAO,AG3UtC,0BACE,eH2Ue,CAAO,AG5UxB,2BACE,eH4UgB,CAAO,AG7UzB,gCACE,eH6UqB,CAAO,AG9U9B,qCACE,eH8U0B,CAAO,AG/UnC,+BACE,eH+UoB,CAAO,AGhV7B,wBACE,eHgVa,CAAO,AGjVtB,+BACE,eHiVoB,CAAO,AGlV7B,sBACE,eHkVW,CAAO,AGnVpB,4BACE,eHmViB,CAAO,AGpV1B,+BACE,eHoVoB,CAAO,AGrV7B,4BACE,eHqViB,CAAO,AGtV1B,8BACE,eHsVmB,CAAO,AGvV5B,sBACE,eHuVW,CAAO,AGxVpB,2BACE,eHwVgB,CAAO,AGzVzB,+BACE,eHyVoB,CAAO,AG1V7B,uBACE,eH0VY,CAAO,AG3VrB,iCACE,eH2VsB,CAAO,AG5V/B,+BACE,eH4VoB,CAAO,AG7V7B,+BACE,eH6VoB,CAAO,AG9V7B,8BACE,eH8VmB,CAAO,AG/V5B,gCACE,eH+VqB,CAAO,AGhW9B,+BACE,eHgWoB,CAAO,AGjW7B,sCACE,eHiW2B,CAAO,AGlWpC,oCACE,eHkWyB,CAAO,AGnWlC,qBACE,eHmWU,CAAO,AGpWnB,6BACE,eHoWkB,CAAO,AGrW3B,sBACE,eHqWW,CAAO,AGtWpB,gCACE,eHsWqB,CAAO,AGvW9B,oBACE,eHuWS,CAAO,AGxWlB,4BACE,eHwWiB,CAAO,AGzW1B,+BACE,eHyWoB,CAAO,AG1W7B,6BACE,eH0WkB,CAAO,AG3W3B,yBACE,eH2Wc,CAAO,AG5WvB,6BACE,eH4WkB,CAAO,AG7W3B,2BACE,eH6WgB,CAAO,AG9WzB,sBACE,eH8WW,CAAO,AG/WpB,6BACE,eH+WkB,CAAO,AGhX3B,sBACE,eHgXW,CAAO,AGjXpB,qCACE,eHiX0B,CAAO,AGlXnC,oCACE,eHkXyB,CAAO,AGnXlC,iCACE,eHmXsB,CAAO,AGpX/B,qCACE,eHoX0B,CAAO,AGrXnC,sBACE,eHqXW,CAAO,AGtXpB,uBACE,eHsXY,CAAO,AGvXrB,yBACE,eHuXc,CAAO,AGxXvB,uBACE,eHwXY,CAAO,AGzXrB,2BACE,eHyXgB,CAAO,AG1XzB,uBACE,eH0XY,CAAO,AG3XrB,sBACE,eH2XW,CAAO,AG5XpB,+BACE,eH4XoB,CAAO,AG7X7B,6BACE,eH6XkB,CAAO,AG9X3B,uBACE,eH8XY,CAAO,AG/XrB,6BACE,eH+XkB,CAAO,AGhY3B,2BACE,eHgYgB,CAAO,AGjYzB,2BACE,eHiYgB,CAAO,AGlYzB,sBACE,eHkYW,CAAO,AGnYpB,iCACE,eHmYsB,CAAO,AGpY/B,iCACE,eHoYsB,CAAO,AGrY/B,wBACE,eHqYa,CAAO,AGtYtB,uBACE,eHsYY,CAAO,AGvYrB,2BACE,eHuYgB,CAAO,AGxYzB,yBACE,eHwYc,CAAO,AGzYvB,wBACE,eHyYa,CAAO,AG1YtB,sBACE,eH0YW,CAAO,AG3YpB,0BACE,eH2Ye,CAAO,AG5YxB,wCACE,eH4Y6B,CAAO,AG7YtC,yCACE,eH6Y8B,CAAO,AG9YvC,uCACE,eH8Y4B,CAAO,AG/YrC,uBACE,eH+YY,CAAO,AGhZrB,6BACE,eHgZkB,CAAO,AGjZ3B,4BACE,eHiZiB,CAAO,AGlZ1B,2BACE,eHkZgB,CAAO,AGnZzB,sBACE,eHmZW,CAAO,AGpZpB,8BACE,eHoZmB,CAAO,AGrZ5B,iCACE,eHqZsB,CAAO,AGtZ/B,wBACE,eHsZa,CAAO,AGvZtB,+BACE,eHuZoB,CAAO,AGxZ7B,+BACE,eHwZoB,CAAO,AGzZ7B,+BACE,eHyZoB,CAAO,AG1Z7B,wBACE,eH0Za,CAAO,AG3ZtB,yBACE,eH2Zc,CAAO,AG5ZvB,0BACE,eH4Ze,CAAO,AG7ZxB,6BACE,eH6ZkB,CAAO,AIle7B,mEAMI,aAAc,CAAA,AANlB,yBAgBI,gBAAgB,AAEhB,aCtB8B,CAAA,ADIlC,8CAqBM,+FpCvBS,CAAA,AoC0BX,qEAEE,eAAgB,CAAA,AAFlB,+GAKI,+FpC/BO,CAAA,AsC8Bf,UACE,gBtCXa,AsCYb,kBvCSyC,AuCRzC,ctC/BkB,AsCgClB,gBAAgB,AAChB,SAAS,AACT,gBDzBiC,AC0BjC,YDvCgC,ACwChC,eAAgB,CAAA,AAGlB,kBD8EE,wCrCvHa,AqCwHb,cAAc,AACd,UA3HgC,CAAA,AA6HhC,4BACE,oCrCxGW,CAAA,AsCyBf,e/BvCI,aAAa,AAEf,mB8BsB8B,AAC9B,uBAAuB,AACvB,kBAhC6C,AAiC7C,cAAc,AACd,iBA1B0E,AA2B1E,gBAvBkE,AAwBlE,qBAAqB,AACrB,yBAAiB,AAAjB,gBAAiB,CCoD8B,A/B/E/C,iBACE,YAAY,AACZ,aAAc,CAAA,AAGhB,yBACE,YAAY,AACZ,aAAc,CAAA,AAehB,uCAGE,gB8BvBgE,CAAA,A9B2BlE,uDAEE,cAAkB,CAAA,AA1BpB,yB8ByBE,qBAAsB,CAAA,ACK1B,sFDDI,sCrCjCW,AqCkCX,eAAe,AACf,oBAAqB,CAAA,ACDzB,4BDKI,yBAAyB,AACzB,0BrC3CW,AqC4CX,kBAAmB,CAAA,AAGrB,yBAMA,aAAc,CAAA,AANd,qLASE,uCrCtDW,AqCuDX,aAAc,CAAA,AAVhB,sCAcE,yBAAyB,AACzB,0BrC3DW,CAAA,AsCkCf,kCDgCM,arCpDS,CAAA,AsCoBf,4CDmCQ,aAAc,CAAA,ACnCtB,wIDyCQ,arC7DO,CAAA,AsCoBf,yKD8CQ,wBrCjEO,CAAA,AsCmBf,yCDkDQ,wBrCtEO,CAAA,AsCoBf,g9BD4DU,UrCrFK,CAAA,AsCyBf,kCDgCM,arC9CU,CAAA,AsCchB,4CDmCQ,aAAc,CAAA,ACnCtB,wIDyCQ,arCvDQ,CAAA,AsCchB,yKD8CQ,wBrC3DQ,CAAA,AsCahB,yCDkDQ,wBrChEQ,CAAA,AsCchB,g9BD4DU,UrCrFK,CAAA,AsCyBf,kCDgCM,arCxCW,CAAA,AsCQjB,4CDmCQ,aAAc,CAAA,ACnCtB,wIDyCQ,arCjDS,CAAA,AsCQjB,yKD8CQ,wBrCrDS,CAAA,AsCOjB,yCDkDQ,wBrC1DS,CAAA,AsCQjB,g9BD4DU,UrCrFK,CAAA,AsCyBf,iCDgCM,arClCQ,CAAA,AsCEd,2CDmCQ,aAAc,CAAA,ACnCtB,qIDyCQ,arC3CM,CAAA,AsCEd,sKD8CQ,wBrC/CM,CAAA,AsCCd,wCDkDQ,wBrCpDM,CAAA,AsCEd,g8BD4DU,UrCrFK,CAAA,AsCyBf,sB7B3BE,+BAAoD,AACpD,eViB0B,AUhB1B,kBAAkB,AAClB,gBAAgB,AAChB,cAAc,AAZd,kCAAkC,AAClC,mCAAmC,A6ByCjC,gBDxCgE,CAAA,ACiCpE,+CAYI,ctCjDW,AsCkDX,cAAiE,CAAA,AAbrE,oCAiBI,atCtDW,CAAA,AsCqCf,sFAqBI,aAAc,CAAA,AArBlB,gDA0BI,qCtC9DW,CAAA,AsCoCf,4BAgCI,mCAAoC,AAEpC,6BAA8B,AAE9B,sBAAwB,CAAA,AApC5B,sJAiCI,mCAAyC,CAQE,AAK7C,0BDsBA,etCxFsC,AsCyFtC,iBAzGsF,AA0GtF,eAvGkE,CAAA,AC+ElE,oCAKI,cAAuE,CAAA,AAL3E,iC7BzEA,+BAAoD,AACpD,eVkBuB,AUjBvB,kBAAkB,AAClB,gBAAgB,AAChB,cAAc,AAZd,kCAAkC,AAClC,mCAAmC,A6B0F/B,kBDxFuE,ACyFvE,cAAoE,CAAA,AAK1E,qBACE,gBAAgB,AAChB,YAAY,AACZ,gBAAgB,AAChB,UAAW,CAAA,AAqBb,iBDbE,wCrCvHa,AqCwHb,cAAc,AACd,WA3HgC,AAoIhC,eAAe,AACf,gBAAkD,CAAA,AARlD,2BACE,oCrCxGW,CAAA,AsCgHf,+BDGM,eAAgB,CAAA,ACHtB,oBxCpFE,cE9CkB,AF+ClB,gBAAgB,AAahB,gBAAgB,AAChB,uBAAuB,AACvB,mBAAmB,AACnB,iBAAiB,AuCuFjB,iBAAyC,AACzC,SAAS,AACT,sBAA+C,CAAA,ACtBjD,kCDWM,aAAc,CAAA,ACRlB,+BDwBE,eAA8B,AAC9B,mBAAiC,AACjC,gBAAgC,CAAA,AC1BlC,6CD8BE,aAAc,CAAA,ACxBlB,oBAEI,mBtC1IgB,AsC2IhB,atC9HiB,CAAA,AsC2HrB,4CDhEM,arCjDS,CAAA,AsCiHf,sDD7DQ,aAAc,CAAA,AC6DtB,sKDvDQ,arC1DO,CAAA,AsCiHf,qSDlDQ,wBrCjEO,CAAA,AsCmHf,mDD9CQ,wBrCtEO,CAAA,AsCoHf,0gDDpCU,UrCrFK,CAAA,AsCyHf,4CDhEM,arC3CU,CAAA,AsC2GhB,sDD7DQ,aAAc,CAAA,AC6DtB,sKDvDQ,arCpDQ,CAAA,AsC2GhB,qSDlDQ,wBrC3DQ,CAAA,AsC6GhB,mDD9CQ,wBrChEQ,CAAA,AsC8GhB,0gDDpCU,UrCrFK,CAAA,AsCyHf,4CDhEM,arCrCW,CAAA,AsCqGjB,sDD7DQ,aAAc,CAAA,AC6DtB,sKDvDQ,arC9CS,CAAA,AsCqGjB,qSDlDQ,wBrCrDS,CAAA,AsCuGjB,mDD9CQ,wBrC1DS,CAAA,AsCwGjB,0gDDpCU,UrCrFK,CAAA,AsCyHf,2CDhEM,arC/BQ,CAAA,AsC+Fd,qDD7DQ,aAAc,CAAA,AC6DtB,mKDvDQ,arCxCM,CAAA,AsC+Fd,iSDlDQ,wBrC/CM,CAAA,AsCiGd,kDD9CQ,wBrCpDM,CAAA,AsCkGd,s/CDpCU,UrCrFK,CAAA,AsCyHf,iHAeM,atCjJS,CAAA,AsCkIf,oEAoBM,qCtCvJS,CAAA,AsCmIf,8LA+BQ,oCAA8C,CAAA,AA/BtD,uDAuCI,gCtChKW,CAAA,AF+Bb,8BwCqIE,atCtKiB,CAAA,AsC2KrB,qBACE,cAA6B,CAAA,ACjK/B,YACE,sBvCVa,AuCWb,0FvC/Ba,AuCgCb,YxCuBkC,AwCtBlC,eATkC,AAUlC,kBAAkB,AAClB,WAAW,AACX,UxCsBiB,CAAA,AwC7BnB,2CAWI,wBvClCgB,CAAA,AuCuBpB,qBAgBI,+FvC7CW,CAAA,AuCiDb,sBACE,yFvClDW,CAAA,AuC6Bf,0BAyBI,OAAO,AACP,eAAe,AACf,QAAQ,AACR,KAAM,CAAA,AAIV,oBACE,exCrCsC,AwCsCtC,iBAvCkC,CAAA,AA0CpC,kBACE,mBAAmB,AACnB,aAAa,AACb,WxCdkC,CAAA,AwCWpC,iCAMI,UAAW,CAAA,AANf,kCAUI,WAAY,CAAA,AAIhB,oBACE,yCvCjFa,AuCkFb,YAA6C,AAC7C,axC5EiB,CAAA,AwC8EjB,8BACE,qCvClEW,CAAA,AwCHf,qBjCXI,aAAa,AAEf,sBiCUiC,AACjC,mBAAmB,AACnB,YAAY,AACZ,uBAAuB,AACvB,kBAAkB,AAClB,UAAW,CAAA,AjCbX,uBACE,YAAY,AACZ,aAAc,CAAA,AAGhB,+BACE,YAAY,AACZ,aAAc,CAAA,AAehB,mDAGE,kBiCjBkD,CAAA,AjCqBpD,mEAEE,eAAkB,CAAA,AA/BpB,uBiCgBE,eAA6B,CAAA,AAIjC,4BACE,0BxCvBa,AwCwBb,cAAkC,CAAA,AAElC,sCACE,0BxCxBW,CAAA,AyCbf,mBACE,aAAa,AACb,iBAAiB,AACjB,WAAY,CAAA,AAGd,0BACE,cAAc,AACd,SAAU,CAAA,ACHZ,sBACE,eAAgB,CAAA,AAGlB,a5CoBE,S4ClB+B,A5CmB/B,O4CnB+B,A5CoB/B,gB4CpB4B,A5CqB5B,Q4CrB+B,A5CsB/B,M4CtB+B,AAC/B,U3CgDiB,CAAA,A2CnDnB,oCAQI,mBAAoB,CAAA,AARxB,mCAYI,gBAAgB,AAEhB,cAAe,CAAA,AAdnB,sDAoBM,iBAAkB,CAAA,AApBxB,0CAyBI,cAAc,AAEd,cAAe,CAAA,AA3BnB,6DAiCM,iBAAkB,CAAA,AAjCxB,gCAsCI,eAAe,AAEf,gBAAiB,CAAA,AAMrB,qBAEE,eAAe,AACf,U3CEiB,CAAA,A2CAjB,4FAIE,iBAAkB,CAAA,AAKtB,sB5CxCE,S4CyC8B,A5CxC9B,O4CwC8B,A5CvC9B,e4CuC2B,A5CtC3B,Q4CsC8B,A5CrC9B,M4CqC8B,A1ByB5B,U0BtBa,AAIf,mC1C3Ea,A0C4Eb,cAAc,AACd,yBAAiB,AAAjB,iBAAiB,AACjB,U3CpBiB,CAAA,AiBcjB,iFAqBE,S0BtBW,CAAA,A1BKb,+FAiBE,U0BtBa,A1BOb,mBArDO,AAsDP,wB0BP2B,A1BQ3B,4B0BTQ,A1BUR,oDjBsBgD,CAAA,AiB/BlD,uCAqBE,S0BtBa,CAAA,A1BKf,8CAiBE,U0BtBW,A1BOX,mBArDO,AAsDP,wB0BP2B,A1BQ3B,4B0BTQ,A1BUR,oDjBsBgD,CAAA,A2CpCpD,4BAcI,YAAa,CAAA,AAIf,0CACE,iBAAkB,CAAA,ACrFtB,iBACE,gBAAgB,AAChB,iBAAkB,CAAA,AAGpB,wBACE,mBAAmB,AACnB,oC3CRa,A2CSb,aAAa,AACb,cAAc,AACd,YAAyB,AACzB,SAAU,CAAA,AAEV,kCACE,oC3CKW,CAAA,A2Cdf,6BAcI,oBAAoB,AACpB,aAAa,AACb,QAAO,CAAA,AAhBX,qCAoBI,YAA6B,CAAA,AAIjC,wCACE,gBAA8B,AAC9B,eAAe,AACf,kBAAmB,CAAA,AAHrB,kDAOI,YAAa,CAAA,AAIjB,sB7CdE,S6CeiC,A7CdjC,O6CciC,A7CbjC,kB6Ca8B,A7CZ9B,Q6CYiC,A7CXjC,M6CWiC,AAEjC,sB3CxBa,A2CyBb,0C3C7Ca,A2C8Cb,aAAa,AACb,sBAAsB,AAGtB,kBAAkB,AAClB,gBAAgB,AAChB,SAAU,CAAA,AAEV,gCACE,wB3ClDgB,CAAA,A2CoCpB,0CAkBI,YAAa,CAAA,AAKjB,2FC6BI,2BDzB4B,ACyB5B,SDzBsD,CAAA,AAJ1D,yGC6BI,uBDzB0C,ACyB1C,UDzBwD,ACUxD,mBApBO,AAqBP,wBDTsC,ACUtC,sCDZmD,ACanD,+BDZa,CAAA,AALjB,4CC6BI,uBDlB0C,ACkB1C,SDlBwD,CAAA,AAX5D,mDC6BI,2BDlB4B,ACkB5B,UDlBsD,ACGtD,mBApBO,AAqBP,wBDFsC,ACGtC,sCDLmD,ACMnD,+BDLa,CAAA,AAMjB,yFCWI,2BDP4B,ACO5B,SDPsD,CAAA,AAJ1D,uGCWI,uBDP0C,ACO1C,UDPwD,ACRxD,mBApBO,AAqBP,wBDSsC,ACRtC,sCDMmD,ACLnD,+BDMa,CAAA,AALjB,2CCWI,uBDA0C,ACA1C,SDAwD,CAAA,AAX5D,kDCWI,2BDA4B,ACA5B,UDAsD,ACftD,mBApBO,AAqBP,wBDgBsC,ACftC,sCDamD,ACZnD,+BDaa,CAAA,AE5FjB,aCkIE,gG9CpIa,AgB6FX,mB8BmE+B,ADhJjC,kB9CyByC,A8CxBzC,qBAAqB,AACrB,U9CwCiB,CAAA,A+CrCjB,gCACE,YDlBwB,ACmBxB,kBAAkB,AAClB,UDpBwB,CAAA,ACsBxB,uCACE,YAA4C,AAC5C,WAAoE,AAEpE,UAA2C,CAAA,AAK/C,gFACE,mBAvBsE,AAwBtE,gBAxBsE,CAAA,AAsBxE,mGAKI,YAxBsD,CAAA,AAmB1D,uGAQM,wBAAyB,CAAA,AAK/B,gFACE,gBApCsE,CAAA,AAmCxE,mGAII,UApCsD,CAAA,AAgC1D,uGAOM,mBAAoB,CAAA,AAK1B,gFACE,eAhDsE,CAAA,AA+CxE,mGAII,SAhDsD,CAAA,AA4C1D,uGAOM,uBAAwB,CAAA,AAK9B,gFAGE,kBA9DsE,AAgEtE,iBAhEsE,CAAA,AA2DxE,mGAQI,WAhEsD,CAAA,AAwD1D,uGAWM,wBAAyB,CAAA,AAM/B,oEACE,QAAQ,AACR,0BAA2B,CAAA,AAG7B,oEACE,UAAU,AACV,yBAA0B,CAAA,AAO1B,gGACE,YArF0F,CAAA,AAoF5F,oGACE,cArF0F,CAAA,AAoF5F,kGACE,aArF0F,CAAA,AAoF5F,sGACE,eArF0F,CAAA,AA2F1F,+EAKI,yBANc,CAAA,AAClB,iFAKI,2BANsB,CAAA,AAC1B,gFAKI,0BAN6B,CAAA,AACjC,kFAGI,4BAJc,CAAA,AAClB,oFAGI,8BAJsB,CAAA,AAC1B,mFAGI,6BAJ6B,CAAA,AACjC,kFAKI,4BANc,CAAA,AAClB,oFAKI,8BANsB,CAAA,AAC1B,mFAKI,6BAN6B,CAAA,AAyBrC,kCACE,gB9CnHW,A8CoHX,aD9HO,CAAA,ACgBP,uCAkHA,wC9C5IW,CAAA,A8C+Ib,uCACE,a9ChJW,A8CiJX,e/ClF0B,CAAA,A+CqF5B,qCACE,S9CjIW,CAAA,AgBoDb,iEAqBE,mB8BmEsB,CAAA,A9BpFxB,+EAiBE,mB8BmE+B,A9BlF/B,mBArDO,AAsDP,wB8BkFsC,A9BjFtC,8B8BgFU,A9B/EV,0DjBuB4D,CAAA,AiBhC9D,+BAqBE,kB8BmE+B,CAAA,A9BpFjC,sCAiBE,oB8BmEsB,A9BlFtB,mBArDO,AAsDP,wB8BkFsC,A9BjFtC,8B8BgFU,A9B/EV,0DjBuB4D,CAAA,A+C8B9D,kCDjHE,kB9CoBuC,A8CnBvC,iBAAkB,CAAA,AApBtB,6DAyBM,gBA3B4B,AA4B5B,YAA0B,CAAA,AAM5B,yEACE,WAnC4B,CAAA,AAElC,yBAwCI,kBAAoB,CAAuD,AAxC/E,4CA2CM,YAAa,CAAA,A7B2BjB,sJAqBE,kB6B1CwB,CAAA,A7ByB1B,+HAiBE,mB6B1CiC,A7B2BjC,mBArDO,AAsDP,wBjB0B0B,AiBzB1B,8B6B7Bc,A7B8Bd,oDjBsBgD,CAAA,AiB/BlD,uDAqBE,kB6B1CiC,CAAA,A7ByBnC,8DAiBE,mB6B1CwB,A7B2BxB,mBArDO,AAsDP,wBjB0B0B,AiBzB1B,8B6B7Bc,A7B8Bd,oDjBsBgD,CAAA,A8CrGpD,6CCkIE,+F9CpIa,CAAA,A8CsIb,uFACE,mB9ClIgB,A8CmIhB,aD1ES,CAAA,AC6EX,iGACE,wC9C5IW,CAAA,A8C+Ib,iGACE,a9ChJW,A8CiJX,e/ClF0B,CAAA,A+CqF5B,6FACE,Y9ChJgB,CAAA,A6CkEpB,0BACE,kBAAoC,AACpC,WAAW,AACX,cAAc,AACd,kBAAkB,AAClB,uBAAwB,CAAA,AAI1B,sCACE,YAAa,CAAA,AAGf,sBACE,4B7CjEa,CAAA,A6CoEf,0B7BKI,U8B6DuD,AD7DzD,aAAa,AACb,U9CpCiB,CAAA,AiBcjB,yFAqBE,S8B6DqD,CAAA,A9B9EvD,uGAiBE,U8B6DuD,A9B5EvD,mBArDO,AAsDP,wBjB0B0B,AiBzB1B,4B8B0EkD,A9BzElD,oDjBsBgD,CAAA,AiB/BlD,2CAqBE,S8B6DuD,CAAA,A9B9EzD,kDAiBE,U8B6DqD,A9B5ErD,mBArDO,AAsDP,wBjB0B0B,AiBzB1B,4B8B0EkD,A9BzElD,oDjBsBgD,CAAA,A8CfpD,gCASI,YAAa,CAAA,AATjB,iEAcI,mBAAoB,CAAA,AAdxB,oDAmBI,YAAa,CAAA,AAIjB,wBAEE,oBAAqB,CAAA,AAMvB,8BACE,UAAW,CAAA,AE1Hb,YACE,OAAO,AAKP,kBAAkB,AAElB,QAAQ,AAER,KAAM,CAAA,ACgCR,uCACE,GACE,uBAAwB,CAAA,AAG1B,GACE,0BAAiD,CAAA,CAAA,AAIrD,kBACE,+BhD3Ca,AgD4Cb,mBAxB4C,AAyB5C,cAAc,AACd,WA5B8C,AA6B9C,gBAAgB,AAChB,kBAAkB,AAClB,UAAW,CAAA,AAPb,sCAUI,kJAtBH,AAuBG,qChDrDW,AgDsDX,0BAnCyC,AAoCzC,mBAnC0C,AAoC1C,YAAY,AACZ,kBAAkB,AAClB,+CjDqCgD,AiDnChD,UAAW,CAAA,AAlBf,kFAsBI,iEAA4F,CAAA,AAtBhG,qDA0BI,qBAAsB,CAAA,AAI1B,4BACE,4BhDjFa,CAAA,AgDgFf,gDAII,wBhD1EW,CAAA,AgD+Eb,yDACE,wBhDhEW,CAAA,AgD+Db,yDACE,wBhD1DY,CAAA,AgDyDd,yDACE,wBhDpDa,CAAA,AgDmDf,wDACE,wBhD9CU,CAAA,AiD1Bd,yBACE,GACE,gCjDNiB,AiDOjB,iCjDPiB,CAAA,AiDUnB,GACE,+BjDjBW,AiDkBX,gCjDlBW,CAAA,CAAA,AiDyBf,cACE,qDClCsE,ADqCtE,gCjDvBmB,AiD0BnB,sCAAuC,AACvC,4CAA8C,AAC9C,kBAAkB,AAClB,0BAA2B,AAG3B,4BAA6B,AAC7B,eAAe,AACf,oBAAoB,AACpB,yBAAiB,AAAjB,gBAAiB,CAAA,AAhBnB,yDAsBI,2BAA6B,CAAA,AE9CjC,YCFI,YrD4CqC,AqD3CrC,gBAJkC,AAKlC,WAAW,ADEb,eAAe,AACf,aAAa,AACb,kBAAkB,AAClB,yBAAiB,AAAjB,gBAAiB,CAAA,AALnB,kBAQI,cAAe,CAAA,AARnB,mBAYI,wBAAgB,AAAhB,eAAgB,CAAA,AAZpB,yBAgBI,mBAAmB,AACnB,UAAY,CAAA,AAjBhB,iCAqBI,WpDMwB,CAAA,AoDF5B,uCCXI,WDrBqC,ACsBrC,OAAO,AACP,QAAQ,AACR,QANoD,ADiBtD,iBAAkB,CAAA,AAGpB,kBACE,kBpDAyC,AoDCzC,eAAgB,CAAA,AAGlB,qBACE,8BnDtCa,CAAA,AmDwCb,+BACE,4BnDjDW,CAAA,AmD6Cf,wCASM,wBnD5BS,CAAA,AmDmBf,wCASM,wBnDtBU,CAAA,AmDahB,wCASM,wBnDhBW,CAAA,AmDOjB,uCASM,wBnDVQ,CAAA,AmDed,mB3CwEE,yBRjHmB,AQkHnB,8EA7E8E,AA8E9E,8ERrIa,AQsIb,cRpIkB,AmD2DlB,kBpDpByC,AoDqBzC,mEnD9Da,AmD+Db,eAAe,AACf,YpD5B0B,AoD6B1B,OAAO,AACP,kBAAkB,AAClB,MAAM,AACN,UpDhC0B,CAAA,ASwG1B,wDAkBA,yBR/ImB,AQgJnB,sBAAsB,AACtB,8ERhKa,CAAA,AQiJb,4DAmBA,sCRtJmB,AQuJnB,sBAAsB,AACtB,gBAAgB,AAChB,0BR/Ja,AQgKb,mBAAmB,AACnB,YAAa,CAAA,AAEb,gLAEE,+BR/JiB,CAAA,AmD6CrB,yBAaI,SAAU,CAAA,A3CgEZ,yBAgBA,4BAA4B,AAC5B,yBRxImB,AQyInB,8ER1Ja,AmD6EX,mEnD7EW,AmD8EX,oBAAY,AAAZ,YAAY,AACZ,SAAU,CAAA,AApBd,8B3CmGE,yBR/ImB,AQgJnB,sBAAsB,AACtB,+ERhKa,AmDoFX,yEnDpFW,AmDqFX,wBAAgB,AAAhB,eAAgB,CAAA,AAGlB,iCACE,mBnD7EW,AmD8EX,gBAAgB,AAEhB,mBAAoB,CAAA,AAGtB,6B3CyHA,yBRlNkB,AQmNlB,+EAhKoF,AAiKpF,uCR1Na,AQ2Nb,aRzMmB,CAAA,AQ2MnB,+GAGE,aR9MiB,CAAA,AQiNnB,mCAqBA,yBRnPkB,AQoPlB,sCRzPa,CAAA,AQuOb,4EAsBA,yBR1PkB,AQ2PlB,sBAAsB,AACtB,wER/Pa,CAAA,AQ4Ob,gFAuBA,mCR7PkB,AQ8PlB,sBAAsB,AACtB,gBAAgB,AAChB,0BR3Pa,CAAA,AQ6Pb,sGACE,4BRnQgB,CAAA,AQ2OlB,mEACE,6BRlPW,AQmPX,cRzOW,CAAA,AmDqFb,gEAKI,wBnD9Fc,CAAA,AmDyFlB,wCASI,wBnDpGc,CAAA,AmDwGlB,2CACE,mBnDrGW,AmDsGX,qBnDtGW,AmDuGX,eAAgB,CAAA,AApDpB,qCAwDI,mBnD7GgB,AmD8GhB,kBpD3EuC,AoD4EvC,gGnDrHW,AmDsHX,cnDpGiB,AmDqGjB,eAA6B,CAAA,AAE7B,+CACE,mBnD1Ge,AmD2Gf,gGnD3HS,AmD4HT,anDtHc,CAAA,AmDyHhB,mDACE,eAAgB,CAAA,AArEtB,wDA2EI,SAAuB,CAAA,AA3E3B,6BA+EI,6BAA6B,AAC7B,yBAA0B,CAAA,AAhF9B,2BAoFI,4BAA4B,AAC5B,yBAAyB,AACzB,eAA6B,CAAA,AAtFjC,6CAyFM,aAAc,CAAA,AAKpB,kBCnHI,+BAAyC,ADqH3C,qBAAqB,AACrB,epDlIsC,AoDmItC,cAAc,AACd,gBAAgD,AAChD,kBAAkB,AAClB,kBAAmB,CAAA,AAGrB,yBCtJI,aATkC,AAUlC,erDqCqC,AqDpCrC,UrDoCqC,CAAA,AoDgHzC,yFCvII,SAAS,AACT,YAAY,AACZ,SAVoD,AAWpD,MAAM,AACN,SD9BqC,CAAA,AAiKzC,8CASI,QAAS,CAAA,AATb,2CC3HI,6BAAwC,CAAA,AD2H5C,4CAiBI,QAAS,CAAA,AAjBb,8DAoBM,cAAc,AACd,eAA6B,CAAA,AArBnC,0GA0BM,WAAwB,AACxB,cAAc,AACd,UpD3JsB,CAAA,AoD+H5B,sDAgCM,+BpD1JqC,AoD2JrC,wBAAyB,CAAA,AAjC/B,wEAoCQ,yBAAmC,CAAA,AApC3C,oDAyCM,4BAA4B,AAC5B,6BAA6B,AAC7B,2BpDrKqC,AoDsKrC,iBAA+B,CAAA,AE9MrC,gCACE,GAAO,sBAAuB,CAAA,AAC9B,GAAO,uBAAyB,CAAA,CAAA,AAGlC,aACE,mBAAmB,AAEnB,aAAa,AACb,uBAAuB,AAGvB,iBAAiB,AACjB,qBAAsB,CAAA,AARxB,iBAWI,aAAc,CAAA,AAXlB,kBAeI,cAAe,CAAA,AAfnB,+BAmBI,2BrDjBW,AqDkBX,qBAAqB,AACrB,wBAAwB,AACxB,0DtD2EgD,CAAA,AsDjGpD,gCA0BI,0BrDxBW,CAAA,AqD6Bf,uBACE,kDAA6E,CAAA,AAE7E,oCACE,cAAe,CAAA,AAInB,yCAEI,crDrCW,CAAA,AqDmCf,0CAMI,wBrDnDW,CAAA,AqDwDb,kDACE,crD/BW,CAAA,AqD8Bb,kDACE,crDzBY,CAAA,AqDwBd,kDACE,crDnBa,CAAA,AqDkBf,iDACE,crDbU,CAAA,AsDlBd,uBACE,YAAa,CAAA,AADf,qCAOI,uBAAuB,AACvB,qBAAsB,CAAA,AAR1B,8CAWM,kBvDIqC,AuDHrC,evD/Ba,AuDgCb,UAAW,CAAA,AAbjB,kEAgBQ,qCtDhBO,AsDiBP,eAAgB,CAAA,AAjBxB,mFAsBM,qCtDtBS,AsDuBT,kBvDRqC,AuDSrC,SAAS,AACT,YAAY,AACZ,OAAO,AACP,QAAQ,AACR,KAAM,CAAA,AA5BZ,sCAoCI,aAAa,AACb,iBAA+B,CAAA,AAInC,cACE,qBAAqB,AACrB,YAAY,AACZ,aAAa,AACb,cAAc,AACd,gBAAgB,AAChB,SAAS,AACT,UAAU,AACV,iBAAkB,CAGqC,AAXzD,gCAaI,iBAA+B,CAAA,AAInC,SxDtBE,gBAAgB,AAChB,uBAAuB,AACvB,mBAAmB,AACnB,iBAAiB,AwDqBjB,ctDpFkB,AsDqFlB,eAAe,AACf,cAAc,AACd,evDjEgC,AuDkEhC,iBvD9CkC,AuD+ClC,eAAe,AACf,kBAAkB,AAClB,kBAAmB,CAAA,AATrB,WAaI,cAAc,AACd,cAAc,AACd,oBAAqB,CAAA,AAGvB,oCAIE,mCAAwC,AACxC,yBAA2B,CACoB,AAxBnD,6BA4BI,0BtDxGW,AsDyGX,kBAAmB,CAAA,AA7BvB,6BAiCI,gBAAgB,AAChB,iCtD7FW,CAAA,AsD2Df,sEAuCI,atDlGW,CAAA,AsD2Df,eA2CI,qBAAsB,CAAA,AAGxB,oBACE,evD1GoC,AuD2GpC,gBvDrFsC,CAAA,AuDyF1C,eACE,eAA6B,CAAA,AAD/B,iCAII,YAAa,CAAA,AAIjB,2BACE,OAAO,AACP,oBAAoB,AACpB,kBAAkB,AAClB,MAAM,AACN,sCAAuC,AACvC,kCAAoC,AACpC,wBAAgD,AAChD,oDvDjDkD,CAAA,AuDyCpD,8CAWI,yBtDlIW,AsDmIX,SAAS,AACT,WArIqB,AAsIrB,OAAO,AACP,kBAAkB,AAClB,OAAQ,CAAA,AAhBZ,4CAoBI,eAAgB,CAAA,AAIpB,mBAEI,atDxJiB,CAAA,AsDsJrB,uCAKM,0BtDlKS,CAAA,AsD6Jf,uCASM,iCtDrJS,CAAA,AsD4If,0FAcM,atD1JS,CAAA,AsD4If,6BAmBI,wBtD/JW,CAAA,AsDmKf,mBACE,QAAS,CAAA,AC/JX,ShD7BI,oBAAoB,AAItB,mBiDU8B,AAC9B,mBAAmB,AACnB,yBxDZa,AwDab,YAAY,AACZ,kBzDmByC,AyDlBzC,gBAAgB,AAChB,cxDNmB,AwDOnB,ezDCsC,AyDAtC,iBzDU0B,AyDT1B,eAAe,AACf,gBA3B4B,AA4B5B,eA5B4B,AA6B5B,gBA1BgC,AA4BhC,iBAAkB,CAAA,ADCpB,yBC6FI,cAAe,CAAA,AD7FnB,+BCgGM,qCxDzHS,CAAA,AuDyBf,oECqGM,oCxD9HS,CAAA,AOEb,WACE,YAAY,AACZ,aAAc,CAAA,AAGhB,mBACE,YAAY,AACZ,aAAc,CAAA,AAehB,2BAGE,gBiDxBuC,CAAA,AjD4BzC,2CAEE,cAAkB,CAAA,AgDRtB,ezDoCE,qCAAmC,AACnC,iB0DnC0B,A1DoC1B,uBAAwB,CAAA,AyDtC1B,mBCMI,mBAhCgC,AAiChC,iBAAkD,AAElD,iBAAmD,CAAA,AAGrD,mBAGE,yBxDpCW,AwDqCX,axD/CgB,CAAA,AwD2ClB,mCAiFE,cAAe,CAAA,AAjFjB,yCAoFI,sCxDrHS,CAAA,AwDiCb,wFAyFI,qCxD1HS,CAAA,AwDiCb,sGAOI,iBAAkB,CAAA,ADnBxB,wECwBI,SxDrCW,CAAA,AuDaf,uCC8BE,ezDvCgC,AyDwChC,iBzD3BuB,AyD4BvB,gBA1DkC,AA2DlC,eA3DkC,AA4DlC,gBA1D8D,CAAA,AjDuB9D,gGAGE,gBiDvBqE,CAAA,AjD2BvE,gIAEE,cAAkB,CAAA,AgDRtB,2DCqCI,kBAAwD,AAExD,kBAAyD,CAAA,ADvC7D,4BC8CE,mBxDrDa,AwDsDb,UxD5Da,CAAA,AuDaf,4CC6FI,cAAe,CAAA,AD7FnB,kDCgGM,qCxDvGS,CAAA,AuDOf,0GCqGM,oCxD5GS,CAAA,AuDOf,4BC8CE,mBxD/Cc,AwDgDd,UxD5Da,CAAA,AuDaf,4CC6FI,cAAe,CAAA,AD7FnB,kDCgGM,oCxDjGU,CAAA,AuDChB,0GCqGM,mCxDtGU,CAAA,AuDChB,4BC8CE,mBxDzCe,AwD0Cf,UxD5Da,CAAA,AuDaf,4CC6FI,cAAe,CAAA,AD7FnB,kDCgGM,qCxD3FW,CAAA,AuDLjB,0GCqGM,oCxDhGW,CAAA,AuDLjB,2BC8CE,mBxDnCY,AwDoCZ,UxD5Da,CAAA,AuDaf,2CC6FI,cAAe,CAAA,AD7FnB,iDCgGM,oCxDrFQ,CAAA,AuDXd,wGCqGM,mCxD1FQ,CAAA,AuDXd,kBAeI,aAAa,AACb,UAAW,CAAA,AAhBf,4GCoDI,YxD7EW,CAAA,AuDyBf,+CC0DI,sCxDjFW,AwDkFX,axD1FgB,CAAA,AuD+BpB,+DC4GI,cAAe,CAAA,AD5GnB,qEC+GM,oCxDxIS,CAAA,AuDyBf,gJCoHM,oCxD7IS,CAAA,AwDsFX,yDAGE,axD/Ee,CAAA,AwD4EjB,yEA+CA,cAAe,CAAA,AA/Cf,+EAkDE,qCxDpIS,CAAA,AwDkFX,oKAuDE,qCxDzIS,CAAA,AwDkFX,wNAMI,YxDzFO,CAAA,AuDsBf,wCC4EE,sCxDnFa,AwDoFb,axDrFa,CAAA,AuDQf,wDC4GI,cAAe,CAAA,AD5GnB,8DC+GM,qCxDtHS,CAAA,AuDOf,kICoHM,qCxD3HS,CAAA,AuDOf,qKCgFI,YxDvFW,CAAA,AwD0Fb,kDAGE,sCxD7FW,AwD8FX,axD5FW,CAAA,AwDwFb,kEAyBE,cAAe,CAAA,AAzBjB,wEA4BI,qCxDtHS,CAAA,AwD0Fb,sJAiCI,qCxD3HS,CAAA,AuDOf,wCC4EE,qCxD7Ec,AwD8Ed,axD/Ec,CAAA,AuDEhB,wDC4GI,cAAe,CAAA,AD5GnB,8DC+GM,oCxDhHU,CAAA,AuDChB,kICoHM,oCxDrHU,CAAA,AuDChB,qKCgFI,YxDjFY,CAAA,AwDoFd,kDAGE,qCxDvFY,AwDwFZ,axDtFY,CAAA,AwDkFd,kEAyBE,cAAe,CAAA,AAzBjB,wEA4BI,oCxDhHU,CAAA,AwDoFd,sJAiCI,oCxDrHU,CAAA,AuDChB,wCC4EE,sCxDvEe,AwDwEf,axDzEe,CAAA,AuDJjB,wDC4GI,cAAe,CAAA,AD5GnB,8DC+GM,qCxD1GW,CAAA,AuDLjB,kICoHM,qCxD/GW,CAAA,AuDLjB,qKCgFI,YxD3Ea,CAAA,AwD8Ef,kDAGE,sCxDjFa,AwDkFb,axDhFa,CAAA,AwD4Ef,kEAyBE,cAAe,CAAA,AAzBjB,wEA4BI,qCxD1GW,CAAA,AwD8Ef,sJAiCI,qCxD/GW,CAAA,AuDLjB,uCC4EE,qCxDjEY,AwDkEZ,axDnEY,CAAA,AuDVd,uDC4GI,cAAe,CAAA,AD5GnB,6DC+GM,oCxDpGQ,CAAA,AuDXd,gICoHM,oCxDzGQ,CAAA,AuDXd,kKCgFI,YxDrEU,CAAA,AwDwEZ,iDAGE,qCxD3EU,AwD4EV,axD1EU,CAAA,AwDsEZ,iEAyBE,cAAe,CAAA,AAzBjB,uEA4BI,oCxDpGQ,CAAA,AwDwEZ,oJAiCI,oCxDzGQ,CAAA,AuDuBd,gBCwFE,gBAAgB,AAChB,YAAY,AACZ,cAAc,AACd,eAAe,AACf,aAAa,AACb,mBA7JoD,AA+JpD,4BAAsC,AAGtC,gBAlKoD,AAmKpD,WAAY,AAEZ,qBAAe,CAAA,ADrGjB,sBCwGI,gBAAgB,AAChB,WAAY,AACZ,oBAAqB,CAAA,AD1GzB,uBC8GI,SAAU,CAAA,AD9Gd,6B9CjDE,+BAAoD,AACpD,eViB0B,AUhB1B,kBAAkB,AAClB,gBAAgB,AAChB,cAAc,AAZd,kCAAkC,AAClC,mCAAmC,A+C4KjC,exBkNkB,CAAO,AwB/M3B,2BAEE,6BAA4C,AAE5C,qBAAe,CAAA,AAJjB,wC/CxKA,+BAAoD,AACpD,eVkBuB,AUjBvB,kBAAkB,AAClB,gBAAgB,AAChB,aAAc,CAAA,AgDfhB,elDDI,aAAa,AAEf,mBkDA8B,AAC9B,uBAAuB,AACvB,YAAY,AACZ,YAAY,AACZ,oBAAoB,AACpB,gB1DqCiC,A0DpCjC,iBAZsD,AAatD,eAAgB,CAAA,AlDLhB,iBACE,YAAY,AACZ,aAAc,CAAA,AAGhB,qCACE,YAAY,AACZ,aAAc,CAAA,AkDVlB,mCAWI,czDVW,AyDWX,gBAAyD,AACzD,iBAhBoE,AAkBpE,cAlBoE,CAAA,AAGxE,qClDDI,aAAa,AAEf,mBkDkBgC,AAC9B,mBAAmB,AAEnB,mBAAmB,AACnB,eAAe,AACf,iBA3BoE,AA4BpE,eA9BoD,AAgCpD,WAAY,CAAA,AlDxBd,uCACE,YAAY,AACZ,aAAc,CAAA,AAGhB,+CACE,YAAY,AACZ,aAAc,CAAA,AAehB,mFAGE,gBkDjCoD,CAAA,AlDqCtD,mGAEE,cAAkB,CAAA,AkDlCtB,8EAiCM,gBAA4D,CAAA,AlD9BhE,uCkDkCI,iBA1CkD,CAAA,AAKxD,wBA4CI,wBAAyB,CAAA,AA5C7B,mC3D8DE,qCAAmC,AACnC,iB2DhB4B,A3DiB5B,uBAAwB,CAAA,A2DhE1B,gCAqDI,cAAc,AACd,iBD5D0B,AC8D1B,UAAwB,CAAA,AAxD5B,sFA4DM,kBAAmB,CAAA,AA5DzB,uDAmEI,oBAAc,CAAA,AAnElB,2BjDsGE,gBThE0C,ASiE1C,eTjE0C,ASkF1C,aA3H4C,CAAA,AiDG9C,yBA4EI,YAAY,AACZ,e1DjCqC,CAAA,AQnBvC,2DAGE,iBkD9B6E,CAAA,AlDkC/E,2EAEE,cAAkB,CAAA,AkDlCtB,6CAgFM,gBAA+D,AAC/D,eAnF2E,CAAA,AAEjF,0CAqFM,gBDrF8B,CAAA,ACApC,qCjDsGE,gBTjEkC,ASkElC,eTlEkC,AS8ElC,iBTnHiB,A0D2Fb,oBAAc,CAAA,AA3FpB,sCAgGM,oBAAc,CAAA,AAhGpB,0BAqGI,sBzDxFW,AyDyFX,4FzD7GW,CAAA,AyDOf,6CA0GQ,4FzDjHO,CAAA,AyDOf,6CA0GQ,2FzDjHO,CAAA,AyDOf,6CA0GQ,4FzDjHO,CAAA,AyDOf,4CA0GQ,2FzDjHO,CAAA,AyDsHb,yFAGI,azD9GS,CAAA,AyD2Gb,mFASI,azD7Ge,CAAA,AmBqJnB,yIACE,0BnB7JW,CAAA,AmB4Jb,6GACE,0BnB7JW,CAAA,AyD2Gb,uEAaI,mCzDnIS,AyDoIT,gJzDpIS,CAAA,AyDsHb,6GAmBQ,8HzDzIK,CAAA,AyDsHb,6GAmBQ,6HzDzIK,CAAA,AyDsHb,6GAmBQ,8HzDzIK,CAAA,AyDsHb,2GAmBQ,6HzDzIK,CAAA,AyDiJf,iBAEE,gBAAgB,AAGhB,YAAY,AACZ,gBAAgB,AAChB,SAAU,CAAA,AtC/BV,4CACE,0BnBlHW,AmBoHX,SAAU,CAAA,AAHZ,8BACE,0BnBlHW,AmBoHX,SAAU,CAAA,AsCqBd,uBAYI,sBAAwB,CAAA,ACvJ5B,WAwCE,uBAAuB,AACvB,sB1D3Ba,A0D4Bb,kB3DPyC,A2DQzC,gG1DjDa,A0DkDb,aAAa,AACb,gBAAyB,AACzB,gBAjDkC,AAkDlC,gBAnDkC,AAsDlC,mBAAmB,AAInB,2BAA6B,CAmEkB,A1CvD/C,uDAqBE,2B0CrFsD,CAAA,A1CoExD,qEAiBE,wB0CrFoE,A1CsEpE,mBApBO,AAqBP,wB0C/DsC,A1CgEtC,8B0CxE0B,A1CyE1B,0DjBuB4D,CAAA,AiBhC9D,6EAqBE,2B0CrFsD,CAAA,A1CoExD,2FAiBE,wB0CrFoE,A1CsEpE,mBApBO,AAqBP,wB0CvDsC,A1CwDtC,8B0CxE0B,A1CyE1B,0DjBuB4D,CAAA,AiBhC9D,0BAqBE,U0CpFwB,A1CoFxB,uB0CpF6D,A1CoF7D,c0CpF6D,CAAA,A1CmE/D,iCAiBE,U0CpFsB,A1CoFtB,0B0CpFqD,A1CoFrD,kB0CpFqD,A1CqErD,mBApBO,AAqBP,wB0C7CsC,A1C8CtC,2C0CvEgC,A1CuEhC,mC0CvEgC,A1CuEhC,kD0CvEgC,A1CwEhC,oDjBsBgD,CAAA,AiB/BlD,qCAqBE,uB0CrFoE,CAAA,A1CoEtE,4CAiBE,4B0CrFsD,A1CsEtD,sB0CpCmC,A1CqCnC,wBjB0B0B,AiBzB1B,8B0CxE0B,A1CyE1B,oDjBsBgD,CAAA,A2DjGpD,6BAyDI,cAAc,AAEd,qBAAe,CAAA,AA3DnB,qBA+DI,c1D7DW,A0D+DX,uBAAe,CAAA,AAjEnB,yCAsEI,yB1DtEgB,A0DuEhB,+F1D7EW,CAAA,A0DMf,6DA0EM,a1DrES,CAAA,A0DLf,iCAgFM,wB1DlES,CAAA,A0Ddf,gFAwFM,U1D1ES,CAAA,A0Ddf,oMAmGM,kCAAmC,CAAA,AAnGzC,iDAwGM,gC1D1FS,CAAA,A0Ddf,iDA4GM,+CAA+C,AAC/C,oBAAwB,CAAA,AA7G9B,kDAiHM,8CAA8C,AAC9C,oBAAwB,CAAA,AAlH9B,iDAsHM,uCAAwC,CAAA,AAtH9C,8BA8HM,yB1D1GS,A0D2GT,U1DjHS,CAAA,A0Ddf,8BA8HM,yB1DpGU,A0DqGV,U1DjHS,CAAA,A0Ddf,8BA8HM,yB1D9FW,A0D+FX,U1DjHS,CAAA,A0Ddf,6BA8HM,yB1DxFQ,A0DyFR,U1DjHS,CAAA,A0DsHf,mBACE,cAAc,AACd,a5D1D2C,A4D2D3C,qBAAsB,CAAA,AAGxB,qBACE,mBAAmB,AAGnB,uBAAwB,AACxB,sBAAsB,AACtB,OAAO,AAIP,gBAAgB,AAGhB,oBAzJ8B,AA4J9B,oBAAoB,AAEpB,eAAe,AACf,QAAQ,AAGR,U3D5GiB,CAAA,A2DsFnB,6CAyBI,KAAM,CAAA,AAzBV,gDA6BI,SAAS,AACT,8BAA8B,AAC9B,QAAS,CAAA,AA/Bb,8CAmCI,sBAAuB,CAAA,AAnC3B,+CAuCI,oBAAqB,CAAA,AAIzB,2fAQI,0BAAoD,CAAA,ACjMxD,abkIE,gG9CpIa,AgB6FX,kB2C3E+B,CAAA,AbGjC,gCACE,YalBwB,AbmBxB,kBAAkB,AAClB,UapBwB,CAAA,AbsBxB,uCACE,YAA4C,AAC5C,WAAoE,AAEpE,UAA2C,CAAA,AAK/C,gFACE,mBAvBsE,AAwBtE,gBAxBsE,CAAA,AAsBxE,mGAKI,WAxBsD,CAAA,AAmB1D,uGAQM,wBAAyB,CAAA,AAK/B,gFACE,gBApCsE,CAAA,AAmCxE,mGAII,SApCsD,CAAA,AAgC1D,uGAOM,mBAAoB,CAAA,AAK1B,gFACE,eAhDsE,CAAA,AA+CxE,mGAII,QAhDsD,CAAA,AA4C1D,uGAOM,uBAAwB,CAAA,AAK9B,gFAGE,kBA9DsE,AAgEtE,iBAhEsE,CAAA,AA2DxE,mGAQI,UAhEsD,CAAA,AAwD1D,uGAWM,wBAAyB,CAAA,AAM/B,oEACE,QAAQ,AACR,0BAA2B,CAAA,AAG7B,oEACE,UAAU,AACV,yBAA0B,CAAA,AAO1B,gGACE,aArF0F,CAAA,AAoF5F,oGACE,eArF0F,CAAA,AAoF5F,kGACE,cArF0F,CAAA,AAoF5F,sGACE,gBArF0F,CAAA,AA2F1F,+EAKI,yBANc,CAAA,AAClB,iFAKI,2BANsB,CAAA,AAC1B,gFAKI,0BAN6B,CAAA,AACjC,kFAGI,4BAJc,CAAA,AAClB,oFAGI,8BAJsB,CAAA,AAC1B,mFAGI,6BAJ6B,CAAA,AACjC,kFAKI,4BANc,CAAA,AAClB,oFAKI,8BANsB,CAAA,AAC1B,mFAKI,6BAN6B,CAAA,AAyBrC,kCACE,mB9CjIgB,A8CkIhB,a9CtHiB,CAAA,A8CQjB,uCAkHA,wC9C5IW,CAAA,A8C+Ib,uCACE,a9ChJW,A8CiJX,e/ClF0B,CAAA,A+CqF5B,qCACE,Y9C/IgB,CAAA,AgBkElB,iEAqBE,mB2C3EsB,CAAA,A3C0DxB,+EAiBE,mB2C3E+B,A3C4D/B,mBArDO,AAsDP,wBjB0B0B,AiBzB1B,8B2C9DU,A3C+DV,oDjBsBgD,CAAA,AiB/BlD,+BAqBE,kB2C3E+B,CAAA,A3C0DjC,sCAiBE,oB2C3EsB,A3C4DtB,mBArDO,AAsDP,wBjB0B0B,AiBzB1B,8B2C9DU,A3C+DV,oDjBsBgD,CAAA,A+C+BlD,kCa9GE,iBCnB4C,CAAA,ADHhD,6CbkIE,+F9CpIa,CAAA,A8CsIb,uFACE,mB9CvHiB,A8CwHjB,a9ClIgB,CAAA,A8CqIlB,iGACE,wC9C5IW,CAAA,A8C+Ib,iGACE,a9ChJW,A8CiJX,e/ClF0B,CAAA,A+CqF5B,6FACE,Y9CrIiB,CAAA,A2DdrB,qDAuCQ,mB3DfO,A2DgBP,U3DtBO,CAAA,A2DlBf,wDA4CQ,Y3DpBO,CAAA,A2DxBf,qDAuCQ,mB3DTQ,A2DUR,U3DtBO,CAAA,A2DlBf,wDA4CQ,Y3DdQ,CAAA,A2D9BhB,qDAuCQ,mB3DHS,A2DIT,U3DtBO,CAAA,A2DlBf,wDA4CQ,Y3DRS,CAAA,A2DpCjB,oDAuCQ,mB3DGM,A2DFN,U3DtBO,CAAA,A2DlBf,uDA4CQ,Y3DFM,CAAA,A2DQd,uBACE,yBAAyB,AACzB,WAAY,CAAA,AEbd,2EAEI,a7DnCW,CAAA,A6DiCf,oI/DlBE,aEGa,CAAA,A6Def,oI/DlBE,aESc,CAAA,A6DShB,oI/DlBE,aEee,CAAA,A6DGjB,iI/DlBE,aEqBY,CAAA,A6DSd,oBACE,gBAAgB,AAChB,SAAS,AACT,cAAe,CAAA,AAGjB,eACE,yBAA6B,AAC7B,eAAe,AACf,eAAe,AAEf,iBAAkB,CAAA,AAIlB,yBACE,cAA0D,CAAA,AAD5D,yBACE,iBAA0D,CAAA,AAD5D,yBACE,iBAA0D,CAAA,AAD5D,yBACE,iBAA0D,CAAA,AAD5D,yBACE,iBAA0D,CAAA,AAD5D,yBACE,kBAA0D,CAAA,AAD5D,yBACE,kBAA0D,CAAA,AAD5D,yBACE,kBAA0D,CAAA,AAD5D,yBACE,kBAA0D,CAAA,AAD5D,yBACE,kBAA0D,CAAA,AAD5D,0BACE,kBAA0D,CAAA,AAD5D,0BACE,kBAA0D,CAAA,AAD5D,0BACE,kBAA0D,CAAA,AAD5D,0BACE,kBAA0D,CAAA,AAD5D,0BACE,kBAA0D,CAAA,AAD5D,0BACE,kBAA0D,CAAA,AAD5D,0BACE,kBAA0D,CAAA,AAD5D,0BACE,kBAA0D,CAAA,AAD5D,0BACE,kBAA0D,CAAA,AAD5D,0BACE,kBAA0D,CAAA,AAD5D,0BACE,kBAA0D,CAAA,AAI9D,uBACE,mBAAmB,AACnB,aAAa,AACb,YAtCiC,AAuCjC,kBAAgC,AAChC,UAAW,CAAA,AALb,6BAQI,qC7DrEW,CAAA,A6DyEf,+CAEE,cAjDiC,CAAA,AAoDnC,qBpDhEE,cTlBa,A6DoFb,eAAe,AACf,YAtDiE,AAuDjE,uBAAuB,AACvB,kD9DQkD,CAAA,AU3ElD,2BACE,aT3BgB,CAAA,AS8BlB,+BACE,aTtBW,CAAA,ASqBb,qCAII,aTlBe,CAAA,A6DwErB,8CAQI,uBAAwB,CAAA,AAR5B,8CAaI,e7B1CoB,CAAO,A6B8C/B,oBACE,iBArEiE,AAsEjE,iBAAkB,CAAA,AAGpB,qB/DlDE,gBAAgB,AAChB,uBAAuB,AACvB,mBAAmB,AACnB,iBAAiB,A+DiDjB,cAAc,AACd,kBAAkB,AAClB,yBAAiB,AAAjB,gBAAiB,CAAA,AAJnB,0BAQI,cAAe,CAAA,AAInB,+BACE,cAA8B,AAC9B,yBAAiB,AAAjB,gBAAiB,CAAA,AAFnB,uGAMI,mBAAmB,AACnB,YAAa,CAAA,AAIjB,mDAEI,yBAAyB,AACzB,0B7DlIW,A6DmIX,kBAAmB,CAAA,AAJvB,iGASI,0B7DxIW,A6DyIX,kBAAmB,CAAA,AAIvB,6DACE,wB7D5Ha,CAAA,A6D2Hf,iSAKI,U7DtIW,CAAA,A6DiIf,yFAUM,wB7D3IS,CAAA,A6DiIf,+FAcM,U7D/IS,CAAA,A6DoJf,uCAEI,oC7DlKW,CAAA,A6DgKf,yGAOM,a7DpKS,CAAA,A6D6Jf,kK/DjJE,aEGa,CAAA,A6D8If,kK/DjJE,aESc,CAAA,A6DwIhB,kK/DjJE,aEee,CAAA,A6DkIjB,+J/DjJE,aEqBY,CAAA,A6D4Hd,uEAkBI,wB7DhKW,CAAA","file":"1.a970d4a6.chunk.css","sourcesContent":["/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]} \ No newline at end of file diff --git a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/css/main.6b57d6a6.chunk.css b/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/css/main.6b57d6a6.chunk.css deleted file mode 100644 index 1411c770..00000000 --- a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/css/main.6b57d6a6.chunk.css +++ /dev/null @@ -1,2 +0,0 @@ -body{margin:0;padding:0;font-family:sans-serif;background-color:#232a2f}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0}.bp3-slider-handle:focus{outline:0}.App{text-align:left;margin:4px 4px -30px;display:flex;flex-direction:column;justify-content:center}.bp3-slider-label{transform:translateY(20px)}.bp3-slider-axis :nth-child(2),.bp3-slider-axis :nth-child(3){transform:translate(-100%,20px)}.bp3-slider-axis :nth-child(3){right:0 px}.bp3-slider-handle .bp3-slider-label{transform:translate(-50%,-20px)}.bp3-tab-list{flex-flow:row wrap}.bp3-overlay{pointer-events:none}.parameter-label{white-space:nowrap;color:grey;font-size:16;margin-bottom:4px}.parameter-wrapper{padding-top:4px;padding-bottom:0}.parameter-wrapper .inner{padding:5px;border-radius:4px}.rootgroup-wrapper{padding:0 5px 4px;border-radius:4px;border:1px solid #454545}.serverid{font-size:.8em;margin-top:5px;margin-left:2px}.credits{margin-top:20;margin-bottom:5;width:100%;text-align:center;font-size:.8em} -/*# sourceMappingURL=main.6b57d6a6.chunk.css.map */ \ No newline at end of file diff --git a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/css/main.6b57d6a6.chunk.css.map b/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/css/main.6b57d6a6.chunk.css.map deleted file mode 100644 index 621e5525..00000000 --- a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/css/main.6b57d6a6.chunk.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/src/index.css","main.6b57d6a6.chunk.css","/Users/inx/Documents/_rabbitControl/_src/rcp-ts-client/src/App.css"],"names":[],"mappings":"AAAA,KACE,SAAU,AACV,UAAW,AACX,uBAAwB,AAMxB,wBAAkC,CACnC,AAED,KACE,uEACY,CACb,AAED,GACE,cAAe,AACf,WAAY,AACZ,SAAU,AACV,0BAA2B,AAC3B,aAAc,AACd,SAAW,CACZ,AAED,yBACE,SAAW,CCCZ,AC5BD,KACE,gBAAiB,AAEjB,qBAAqB,AACrB,aAAc,AACd,sBAAuB,AACvB,sBAAwB,CACzB,AAGD,kBAEU,0BAA8B,CACvC,AAOD,8DAHU,+BAAiC,CAQ1C,AALD,+BAIE,UAAY,CACb,AAED,qCAEU,+BAAiC,CAC1C,AAED,cACE,kBAAoB,CACrB,AAED,aACE,mBAAqB,CACtB,AAGD,iBACE,mBAAoB,AACpB,WAAY,AACZ,aAAc,AACd,iBAAmB,CACpB,AAED,mBACE,gBAAiB,AACjB,gBAAoB,CACrB,AAED,0BACE,YAAa,AACb,iBAAmB,CACpB,AAED,mBAGE,kBAAoB,AACpB,kBAAmB,AACnB,wBAA0B,CAC3B,AAED,UACE,eAAiB,AACjB,eAAgB,AAChB,eAAiB,CAClB,AAED,SAEE,cAAe,AACf,gBAAiB,AACjB,WAAY,AACZ,kBAAmB,AACnB,cAAiB,CD0BlB","file":"main.6b57d6a6.chunk.css","sourcesContent":["body {\n margin: 0;\n padding: 0;\n font-family: sans-serif;\n /* font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif; */\n /* -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale; */\n background-color: rgb(35, 42, 47);\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n\nhr {\n display: block;\n height: 1px;\n border: 0;\n border-top: 1px solid #ccc;\n margin: 1em 0;\n padding: 0; \n}\n\n.bp3-slider-handle:focus {\n outline: 0;\n}","body {\n margin: 0;\n padding: 0;\n font-family: sans-serif;\n /* font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif; */\n /* -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale; */\n background-color: rgb(35, 42, 47);\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n\nhr {\n display: block;\n height: 1px;\n border: 0;\n border-top: 1px solid #ccc;\n margin: 1em 0;\n padding: 0; \n}\n\n.bp3-slider-handle:focus {\n outline: 0;\n}\n.App {\n text-align: left;\n margin: 4px;\n margin-bottom: -30px;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n/* bluepring style overwrites*/\n.bp3-slider-label{\n transform:translate(0%, 20px);\n}\n\n.bp3-slider-axis :nth-child(2){\n transform:translate(-100%, 20px);\n}\n\n.bp3-slider-axis :nth-child(3){\n transform:translate(-100%, 20px);\n\n right: 0 px;\n}\n\n.bp3-slider-handle .bp3-slider-label{\n transform:translate(-50%, -20px);\n}\n\n.bp3-tab-list {\n flex-flow: row wrap;\n}\n\n.bp3-overlay {\n pointer-events: none;\n}\n\n/* gui styles */\n.parameter-label {\n white-space: nowrap;\n color: gray;\n font-size: 16; \n margin-bottom: 4px;\n}\n\n.parameter-wrapper {\n padding-top: 4px;\n padding-bottom: 0px; \n}\n\n.parameter-wrapper .inner{\n padding: 5px;\n border-radius: 4px;\n}\n\n.rootgroup-wrapper {\n padding: 5px;\n padding-top: 0px;\n padding-bottom: 4px;\n border-radius: 4px;\n border: 1px solid #454545;\n}\n\n.serverid {\n font-size: 0.8em;\n margin-top: 5px;\n margin-left: 2px;\n}\n\n.credits {\n font-size: 0.8em;\n margin-top: 20;\n margin-bottom: 5;\n width: 100%;\n text-align: center;\n font-size: 0.8em;\n}\n",".App {\n text-align: left;\n margin: 4px;\n margin-bottom: -30px;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n/* bluepring style overwrites*/\n.bp3-slider-label{\n -webkit-transform:translate(0%, 20px);\n transform:translate(0%, 20px);\n}\n\n.bp3-slider-axis :nth-child(2){\n -webkit-transform:translate(-100%, 20px);\n transform:translate(-100%, 20px);\n}\n\n.bp3-slider-axis :nth-child(3){\n -webkit-transform:translate(-100%, 20px);\n transform:translate(-100%, 20px);\n\n right: 0 px;\n}\n\n.bp3-slider-handle .bp3-slider-label{\n -webkit-transform:translate(-50%, -20px);\n transform:translate(-50%, -20px);\n}\n\n.bp3-tab-list {\n flex-flow: row wrap;\n}\n\n.bp3-overlay {\n pointer-events: none;\n}\n\n/* gui styles */\n.parameter-label {\n white-space: nowrap;\n color: gray;\n font-size: 16; \n margin-bottom: 4px;\n}\n\n.parameter-wrapper {\n padding-top: 4px;\n padding-bottom: 0px; \n}\n\n.parameter-wrapper .inner{\n padding: 5px;\n border-radius: 4px;\n}\n\n.rootgroup-wrapper {\n padding: 5px;\n padding-top: 0px;\n padding-bottom: 4px;\n border-radius: 4px;\n border: 1px solid #454545;\n}\n\n.serverid {\n font-size: 0.8em;\n margin-top: 5px;\n margin-left: 2px;\n}\n\n.credits {\n font-size: 0.8em;\n margin-top: 20;\n margin-bottom: 5;\n width: 100%;\n text-align: center;\n font-size: 0.8em;\n}"]} \ No newline at end of file diff --git a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/js/1.ab137fd1.chunk.js b/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/js/1.ab137fd1.chunk.js deleted file mode 100644 index 9c9fd4e1..00000000 --- a/orx-jvm/orx-rabbit-control/src/main/resources/rabbit-client/static/js/1.ab137fd1.chunk.js +++ /dev/null @@ -1,2 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[1],[function(l,n,e){"use strict";l.exports=e(165)},function(l,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=e(4);n.RcpTypes=t.RcpTypes;var u=e(84);n.Client=u.Client;var r=e(194);n.WebSocketClientTransporter=r.WebSocketClientTransporter;var o=e(76);n.ClientTransporter=o.ClientTransporter;var i=e(24);n.Parameter=i.Parameter;var a=e(17);n.ValueParameter=a.ValueParameter;var c=e(43);n.BangParameter=c.BangParameter;var s=e(110);n.BooleanParameter=s.BooleanParameter;var p=e(60);n.RGBParameter=p.RGBParameter,n.RGBAParameter=p.RGBAParameter;var h=e(58);n.EnumParameter=h.EnumParameter;var f=e(59);n.GroupParameter=f.GroupParameter;var d=e(195);n.InvalidParameter=d.InvalidParameter;var v=e(62);n.IPv4Parameter=v.IPv4Parameter;var m=e(69);n.NumberParameter=m.NumberParameter;var y=e(65);n.RangeParameter=y.RangeParameter;var g=e(68);n.StringParameter=g.StringParameter;var z=e(61);n.UriParameter=z.UriParameter;var b=e(72);n.Vector2F32Parameter=b.Vector2F32Parameter,n.Vector2I32Parameter=b.Vector2I32Parameter;var E=e(70);n.Vector3F32Parameter=E.Vector3F32Parameter,n.Vector3I32Parameter=E.Vector3I32Parameter;var T=e(73);n.Vector4F32Parameter=T.Vector4F32Parameter,n.Vector4I32Parameter=T.Vector4I32Parameter;var M=e(71);n.ImageParameter=M.ImageParameter;var _=e(32);n.TypeDefinition=_.TypeDefinition;var O=e(44);n.BangDefinition=O.BangDefinition;var w=e(40);n.BooleanDefinition=w.BooleanDefinition;var V=e(45);n.RGBADefinition=V.RGBADefinition,n.RGBDefinition=V.RGBDefinition;var L=e(19);n.DefaultDefinition=L.DefaultDefinition;var C=e(41);n.EnumDefinition=C.EnumDefinition;var A=e(42);n.GroupDefinition=A.GroupDefinition;var S=e(50);n.Int32Definition=S.Int32Definition;var I=e(77);n.InvalidDefinition=I.InvalidDefinition;var R=e(47);n.IPv4Definition=R.IPv4Definition;var H=e(67);n.IPv6Definition=H.IPv6Definition;var P=e(28);n.NumberDefinition=P.NumberDefinition,n.Float32Definition=P.Float32Definition,n.Float64Definition=P.Float64Definition,n.Int16Definition=P.Int16Definition,n.Int64Definition=P.Int64Definition,n.Int8Definition=P.Int8Definition;var N=e(66);n.Range=N.Range,n.RangeDefinition=N.RangeDefinition;var D=e(51);n.StringDefinition=D.StringDefinition;var x=e(46);n.UriDefinition=x.UriDefinition;var k=e(25);n.Vector2=k.Vector2,n.Vector3=k.Vector3,n.Vector4=k.Vector4;var U=e(53);n.Vector2F32Definition=U.Vector2F32Definition,n.Vector2I32Definition=U.Vector2I32Definition;var B=e(52);n.Vector3F32Definition=B.Vector3F32Definition,n.Vector3I32Definition=B.Vector3I32Definition;var F=e(54);n.Vector4F32Definition=F.Vector4F32Definition,n.Vector4I32Definition=F.Vector4I32Definition;var j=e(12);n.Widget=j.Widget;var W=e(104);n.BangWidget=W.BangWidget;var G=e(97);n.CustomWidget=G.CustomWidget;var K=e(107);n.DefaultWidget=K.DefaultWidget;var Y=e(99);n.DialWidget=Y.DialWidget;var q=e(103);n.InfoWidget=q.InfoWidget;var X=e(101);n.NumberboxWidget=X.NumberboxWidget;var $=e(105);n.PressWidget=$.PressWidget;var Z=e(100);n.SliderWidget=Z.SliderWidget;var J=e(102);n.TextboxWidget=J.TextboxWidget;var Q=e(106);n.ToggleWidget=Q.ToggleWidget;var ll=e(89);n.ColorboxWidget=ll.ColorboxWidget;var nl=e(86);n.DirectorychooserWidget=nl.DirectorychooserWidget;var el=e(91);n.DropdownWidget=el.DropdownWidget;var tl=e(87);n.FilechooserWidget=tl.FilechooserWidget;var ul=e(85);n.IpWidget=ul.IpWidget;var rl=e(90);n.RadiobuttonWidget=rl.RadiobuttonWidget;var ol=e(92);n.RangeWidget=ol.RangeWidget;var il=e(93);n.Slider2dWidget=il.Slider2dWidget;var al=e(88);n.TableWidget=al.TableWidget;var cl=e(96);n.ListWidget=cl.ListWidget;var sl=e(95);n.ListPageWidget=sl.ListPageWidget;var pl=e(94);n.TabsWidget=pl.TabsWidget;var hl=e(98);n.UUID=hl.UUID},function(l,n,e){"use strict";function t(){var l=this.constructor.getDerivedStateFromProps(this.props,this.state);null!==l&&void 0!==l&&this.setState(l)}function u(l){this.setState(function(n){var e=this.constructor.getDerivedStateFromProps(l,n);return null!==e&&void 0!==e?e:null}.bind(this))}function r(l,n){try{var e=this.props,t=this.state;this.props=l,this.state=n,this.__reactInternalSnapshotFlag=!0,this.__reactInternalSnapshot=this.getSnapshotBeforeUpdate(e,t)}finally{this.props=e,this.state=t}}function o(l){var n=l.prototype;if(!n||!n.isReactComponent)throw new Error("Can only polyfill class components");if("function"!==typeof l.getDerivedStateFromProps&&"function"!==typeof n.getSnapshotBeforeUpdate)return l;var e=null,o=null,i=null;if("function"===typeof n.componentWillMount?e="componentWillMount":"function"===typeof n.UNSAFE_componentWillMount&&(e="UNSAFE_componentWillMount"),"function"===typeof n.componentWillReceiveProps?o="componentWillReceiveProps":"function"===typeof n.UNSAFE_componentWillReceiveProps&&(o="UNSAFE_componentWillReceiveProps"),"function"===typeof n.componentWillUpdate?i="componentWillUpdate":"function"===typeof n.UNSAFE_componentWillUpdate&&(i="UNSAFE_componentWillUpdate"),null!==e||null!==o||null!==i){var a=l.displayName||l.name,c="function"===typeof l.getDerivedStateFromProps?"getDerivedStateFromProps()":"getSnapshotBeforeUpdate()";throw Error("Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n"+a+" uses "+c+" but also contains the following legacy lifecycles:"+(null!==e?"\n "+e:"")+(null!==o?"\n "+o:"")+(null!==i?"\n "+i:"")+"\n\nThe above lifecycles should be removed. Learn more about this warning here:\nhttps://fb.me/react-async-component-lifecycle-hooks")}if("function"===typeof l.getDerivedStateFromProps&&(n.componentWillMount=t,n.componentWillReceiveProps=u),"function"===typeof n.getSnapshotBeforeUpdate){if("function"!==typeof n.componentDidUpdate)throw new Error("Cannot polyfill getSnapshotBeforeUpdate() for components that do not define componentDidUpdate() on the prototype");n.componentWillUpdate=r;var s=n.componentDidUpdate;n.componentDidUpdate=function(l,n,e){var t=this.__reactInternalSnapshotFlag?this.__reactInternalSnapshot:e;s.call(this,l,n,t)}}return l}e.r(n),e.d(n,"polyfill",function(){return o}),t.__suppressDeprecationWarning=!0,u.__suppressDeprecationWarning=!0,r.__suppressDeprecationWarning=!0},function(l,n,e){var t;!function(){"use strict";var e={}.hasOwnProperty;function u(){for(var l=[],n=0;n cancelButtonText and onCancel should be set together.",o=t+" canEscapeKeyCancel enabled without onCancel or onClose handler.",i=t+" canOutsideClickCancel enbaled without onCancel or onClose handler.",a=t+" leftElement and leftIcon prop are mutually exclusive, with leftElement taking priority.",c=t+" requires min to be no greater than max if both are defined.",s=t+" requires minorStepSize to be no greater than stepSize.",p=t+" requires stepSize to be no greater than majorStepSize.",h=t+" requires minorStepSize to be strictly greater than zero.",f=t+" requires majorStepSize to be strictly greater than zero.",d=t+" requires stepSize to be strictly greater than zero.",v=t+" controlled value prop does not adhere to stepSize, min, and/or max constraints.",m=t+" requires target prop or at least one child element.",y=t+" requires interactionKind={PopoverInteractionKind.CLICK}.",g=t+" supports one or two children; additional children are ignored. First child is the target, second child is the content. You may instead supply these two as props.",z=t+" with two children ignores content prop; use either prop or children.",b=t+" with children ignores target prop; use either prop or children.",E=t+" Disabling with empty/whitespace content...",T=t+" ignores hasBackdrop",M=t+" onInteraction is ignored when uncontrolled.",_=t+" context blueprintPortalClassName must be string",O=t+" children and options prop are mutually exclusive, with options taking priority.",w=t+" stepSize must be greater than zero.",V=t+" labelStepSize must be greater than zero.",L=t+" value prop must be an array of two non-null numbers.",C=t+" children must be s or s",A=t+" labelStepSize and labelValues prop are mutually exclusive, with labelStepSize taking priority.",S=t+" Classes.SMALL/LARGE are ignored if size prop is set.",I=t+" iconName is ignored if title is omitted.",R=t+" isCloseButtonShown prop is ignored if title is omitted."},function(l,n,e){"use strict";function t(l,n){if(null==l)return{};var e,t,u=function(l,n){if(null==l)return{};var e,t,u={},r=Object.keys(l);for(t=0;t=0||(u[e]=l[e]);return u}(l,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(l);for(t=0;t=0||Object.prototype.propertyIsEnumerable.call(l,e)&&(u[e]=l[e])}return u}e.d(n,"a",function(){return t})},function(l,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=e(4),u=e(15),r=function(){function l(l){this._enabled=!0,this._labelVisible=!0,this._valueVisible=!0,this._needsConfirmation=!1,this.changed=new Map,this.widgetType=l}return l.prototype.parseOptions=function(l){for(;;){var n=l.readU1();if(n===t.RcpTypes.TERMINATOR)break;switch(n){case t.RcpTypes.WidgetOptions.ENABLED:this._enabled=l.readU1()>0;break;case t.RcpTypes.WidgetOptions.LABEL_VISIBLE:this._labelVisible=l.readU1()>0;break;case t.RcpTypes.WidgetOptions.VALUE_VISIBLE:this._valueVisible=l.readU1()>0;break;case t.RcpTypes.WidgetOptions.NEEDS_CONFIRMATION:this._needsConfirmation=l.readU1()>0;break;default:if(!this.handleOption(n,l))throw new Error("widget option not handled: "+n)}}},l.prototype.write=function(n,e){var r=this;u.pushIn16ToArrayBe(this.widgetType,n);var o=this.changed;e&&(o=l.allOptions),o.forEach(function(l,e){switch(e){case t.RcpTypes.WidgetOptions.ENABLED:n.push(t.RcpTypes.WidgetOptions.ENABLED),void 0!=r._enabled?n.push(r._enabled?1:0):n.push(1);break;case t.RcpTypes.WidgetOptions.LABEL_VISIBLE:n.push(t.RcpTypes.WidgetOptions.LABEL_VISIBLE),void 0!=r._labelVisible?n.push(r._labelVisible?1:0):n.push(1);break;case t.RcpTypes.WidgetOptions.VALUE_VISIBLE:n.push(t.RcpTypes.WidgetOptions.VALUE_VISIBLE),void 0!=r._valueVisible?n.push(r._valueVisible?1:0):n.push(1);break;case t.RcpTypes.WidgetOptions.NEEDS_CONFIRMATION:n.push(t.RcpTypes.WidgetOptions.NEEDS_CONFIRMATION),void 0!=r._needsConfirmation?n.push(r._needsConfirmation?1:0):n.push(0)}}),this.writeOptions(n,e),e||this.changed.clear(),n.push(t.RcpTypes.TERMINATOR)},l.prototype.setDirty=function(){this.parameter&&this.parameter.setDirty()},Object.defineProperty(l.prototype,"enabled",{get:function(){return this._enabled},set:function(l){this._enabled!==l&&(this._enabled=l,this.changed.set(t.RcpTypes.WidgetOptions.ENABLED,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(l.prototype,"labelVisible",{get:function(){return this._labelVisible},set:function(l){this._labelVisible!==l&&(this._labelVisible=l,this.changed.set(t.RcpTypes.WidgetOptions.LABEL_VISIBLE,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(l.prototype,"valueVisible",{get:function(){return this._valueVisible},set:function(l){this._valueVisible!==l&&(this._valueVisible=l,this.changed.set(t.RcpTypes.WidgetOptions.VALUE_VISIBLE,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(l.prototype,"needsConfirmation",{get:function(){return this._needsConfirmation},set:function(l){this._needsConfirmation!==l&&(this._needsConfirmation=l,this.changed.set(t.RcpTypes.WidgetOptions.NEEDS_CONFIRMATION,!0),this.setDirty())},enumerable:!0,configurable:!0}),l.allOptions=(new Map).set(t.RcpTypes.WidgetOptions.ENABLED,!0).set(t.RcpTypes.WidgetOptions.LABEL_VISIBLE,!0).set(t.RcpTypes.WidgetOptions.VALUE_VISIBLE,!0).set(t.RcpTypes.WidgetOptions.NEEDS_CONFIRMATION,!0),l}();n.Widget=r},function(l,n){l.exports=function(l){if(void 0===l)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return l}},,function(l,n,e){"use strict";function t(l,n){var e=new DataView(new Uint32Array([l]).buffer);n.push(e.getUint8(1)),n.push(e.getUint8(0))}function u(l,n){var e=new DataView(new Uint32Array([l]).buffer);n.push(e.getUint8(3)),n.push(e.getUint8(2)),n.push(e.getUint8(1)),n.push(e.getUint8(0))}Object.defineProperty(n,"__esModule",{value:!0}),n.pushIn16ToArrayBe=t,n.pushIn32ToArrayBe=u,n.pushIn64ToArrayBe=function(l,n){var e=new DataView(new Uint32Array([l]).buffer);n.push(e.getUint8(7)),n.push(e.getUint8(6)),n.push(e.getUint8(5)),n.push(e.getUint8(4)),n.push(e.getUint8(3)),n.push(e.getUint8(2)),n.push(e.getUint8(1)),n.push(e.getUint8(0))},n.pushFloat64ToArrayBe=function(l,n){var e=new Float64Array([l]),t=new DataView(e.buffer);n.push(t.getUint8(7)),n.push(t.getUint8(6)),n.push(t.getUint8(5)),n.push(t.getUint8(4)),n.push(t.getUint8(3)),n.push(t.getUint8(2)),n.push(t.getUint8(1)),n.push(t.getUint8(0))},n.pushFloat32ToArrayBe=function(l,n){var e=new Float32Array([l]),t=new DataView(e.buffer);n.push(t.getUint8(3)),n.push(t.getUint8(2)),n.push(t.getUint8(1)),n.push(t.getUint8(0))},n.writeTinyString=function(l,n){var e=(new TextEncoder).encode(l);e.length>255&&(e=e.slice(1,256)),n.push(e.length),e.forEach(function(l){n.push(l)})},n.writeShortString=function(l,n){var e=(new TextEncoder).encode(l);e.length>65535&&(e=e.slice(1,65536)),t(e.length,n),e.forEach(function(l){n.push(l)})},n.writeLongString=function(l,n){var e=(new TextEncoder).encode(l);u(e.length,n),e.forEach(function(l){n.push(l)})}},function(l,n,e){"use strict";!function l(){if("undefined"!==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"===typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE)try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(l)}catch(n){console.error(n)}}(),l.exports=e(166)},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(24),r=e(4),o=function(l){function n(n,e){var t=l.call(this,n,e)||this;return t.valueChangedListeners=[],t.defaultTypeDefintion=e,t}return t(n,l),n.prototype.valueConstrained=function(){return this._value},n.prototype.dispose=function(){l.prototype.dispose.call(this),this.valueChangedListeners=[]},n.prototype.addValueChangeListener=function(l){this.valueChangedListeners.indexOf(l)>=0||this.valueChangedListeners.push(l)},n.prototype.removeValueChangedListener=function(l){var n=this.valueChangedListeners.indexOf(l);n<0||this.valueChangedListeners.splice(n,1)},n.prototype.update=function(n){var e=this;if(this.id!==n.id)throw new Error("can not update with parameter with wrong id");if(this.typeDefinition.datatype!==n.typeDefinition.datatype)throw new Error("can not update with parameter of wrong type");l.prototype.update.call(this,n),void 0!=n._value&&(this._value=n._value,this.valueChangedListeners.forEach(function(l){return l(e)}))},n.prototype.writeOptions=function(n,e){(e||this.changed.has(r.RcpTypes.ParameterOptions.VALUE))&&(n.push(r.RcpTypes.ParameterOptions.VALUE),this.defaultTypeDefintion.writeValue(n,this._value)),l.prototype.writeOptions.call(this,n,e)},n.prototype.writeValueUpdate=function(n){l.prototype.writeValueUpdate.call(this,n),this.defaultTypeDefintion.writeValue(n,this._value)},n.prototype.handleOption=function(l,n){return l===r.RcpTypes.ParameterOptions.VALUE&&(this._value=this.defaultTypeDefintion.readValue(n),!0)},Object.defineProperty(n.prototype,"value",{get:function(){return this._value?this._value:this.defaultTypeDefintion.getTypeDefault()},set:function(l){var n=this;this._value!==l&&(this._value=l,this.changed.set(r.RcpTypes.ParameterOptions.VALUE,!0),this.setDirty(),this.valueChangedListeners.forEach(function(l){l(n)}))},enumerable:!0,configurable:!0}),n}(u.Parameter);n.ValueParameter=o},function(l,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.intLog2=n.cidrPrefixToMaskBinaryString=n.leftPadWithZeroBit=n.dottedDecimalNotationToBinaryString=n.parseBinaryStringToBigInteger=n.decimalNumberToOctetString=n.bigIntegerNumberToBinaryString=n.decimalNumberToBinaryString=void 0;var t=e(20),u=e(33);n.decimalNumberToBinaryString=function(l){return Number(l).toString(2)},n.bigIntegerNumberToBinaryString=function(l){return l.toString(2)},n.decimalNumberToOctetString=function(l){var e=n.decimalNumberToBinaryString(l);if(e.length>8)throw new Error("Given decimal in binary contains digits greater than an octet");return n.leftPadWithZeroBit(e,8)},n.parseBinaryStringToBigInteger=function(l){return t(l,2)},n.dottedDecimalNotationToBinaryString=function(l){return l.split(".").reduce(function(l,e){return l.concat(n.decimalNumberToOctetString(parseInt(e)))},"")},n.leftPadWithZeroBit=function(l,n){if(l.length>n)throw new Error("Given string is already longer than given final length after padding: "+n);return"0".repeat(n-l.length).concat(l)},n.cidrPrefixToMaskBinaryString=function(l,n){var e;if(l>(e=n==u.IPNumType.IPv4?32:128))throw Error("Value is greater than "+e);return""+"1".repeat(l)+"0".repeat(e-l)},n.intLog2=function(l){for(var n=0;l.isEven();){if(l.equals(t(2))){n++;break}if((l=l.shiftRight(t(1))).isOdd()){n=0;break}n++}if(0==n)throw new Error("The value of log2 for "+l.toString()+" is not an integer");return n}},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=function(l){function n(n){var e=l.call(this,n)||this;return e.changed=new Map,e}return t(n,l),n.prototype.didChange=function(){return this.changed.size>0},n.prototype.setDirty=function(){this.parameter&&this.parameter.setDirty()},Object.defineProperty(n.prototype,"defaultValue",{get:function(){return this._defaultValue?this._defaultValue:this.getTypeDefault()},set:function(l){this._defaultValue!==l&&(this._defaultValue=l,this.changed.set(this.getDefaultId(),!0),this.setDirty())},enumerable:!0,configurable:!0}),n}(e(32).TypeDefinition);n.DefaultDefinition=u},function(l,n,e){(function(l){var t,u=function(l){"use strict";var n=1e7,e=7,t=9007199254740992,r=f(t),o="0123456789abcdefghijklmnopqrstuvwxyz",i="function"===typeof BigInt;function a(l,n,e,t){return"undefined"===typeof l?a[0]:"undefined"!==typeof n&&(10!==+n||e)?W(l,n,e,t):X(l)}function c(l,n){this.value=l,this.sign=n,this.isSmall=!1}function s(l){this.value=l,this.sign=l<0,this.isSmall=!0}function p(l){this.value=l}function h(l){return-t0?Math.floor(l):Math.ceil(l)}function g(l,e){var t,u,r=l.length,o=e.length,i=new Array(r),a=0,c=n;for(u=0;u=c?1:0,i[u]=t-a*c;for(;u0&&i.push(a),i}function z(l,n){return l.length>=n.length?g(l,n):g(n,l)}function b(l,e){var t,u,r=l.length,o=new Array(r),i=n;for(u=0;u0;)o[u++]=e%i,e=Math.floor(e/i);return o}function E(l,e){var t,u,r=l.length,o=e.length,i=new Array(r),a=0,c=n;for(t=0;t0;)o[u++]=a%i,a=Math.floor(a/i);return o}function O(l,n){for(var e=[];n-- >0;)e.push(0);return e.concat(l)}function w(l,e,t){return new c(l=0;--t)r=(o=r*c+l[t])-(u=y(o/e))*e,a[t]=0|u;return[a,0|r]}function C(l,e){var t,u=X(e);if(i)return[new p(l.value/u.value),new p(l.value%u.value)];var r,o=l.value,h=u.value;if(0===h)throw new Error("Cannot divide by zero");if(l.isSmall)return u.isSmall?[new s(y(o/h)),new s(o%h)]:[a[0],l];if(u.isSmall){if(1===h)return[l,a[0]];if(-1==h)return[l.negate(),a[0]];var g=Math.abs(h);if(g=0;u--){for(t=h-1,g[u+p]!==v&&(t=Math.floor((g[u+p]*h+g[u+p-1])/v)),r=0,o=0,a=z.length,i=0;ic&&(r=(r+1)*h),t=Math.ceil(r/o);do{if(A(i=_(e,t),p)<=0)break;t--}while(t);s.push(t),p=E(p,i)}return s.reverse(),[d(s),d(p)]}(o,h))[0];var T=l.sign!==u.sign,M=t[1],O=l.sign;return"number"===typeof r?(T&&(r=-r),r=new s(r)):r=new c(r,T),"number"===typeof M?(O&&(M=-M),M=new s(M)):M=new c(M,O),[r,M]}function A(l,n){if(l.length!==n.length)return l.length>n.length?1:-1;for(var e=l.length-1;e>=0;e--)if(l[e]!==n[e])return l[e]>n[e]?1:-1;return 0}function S(l){var n=l.abs();return!n.isUnit()&&(!!(n.equals(2)||n.equals(3)||n.equals(5))||!(n.isEven()||n.isDivisibleBy(3)||n.isDivisibleBy(5))&&(!!n.lesser(49)||void 0))}function I(l,n){for(var e,t,r,o=l.prev(),i=o,a=0;i.isEven();)i=i.divide(2),a++;l:for(t=0;t=0?t=E(l,n):(t=E(n,l),e=!e),"number"===typeof(t=d(t))?(e&&(t=-t),new s(t)):new c(t,e)}(e,t,this.sign)},c.prototype.minus=c.prototype.subtract,s.prototype.subtract=function(l){var n=X(l),e=this.value;if(e<0!==n.sign)return this.add(n.negate());var t=n.value;return n.isSmall?new s(e-t):T(t,Math.abs(e),e>=0)},s.prototype.minus=s.prototype.subtract,p.prototype.subtract=function(l){return new p(this.value-X(l).value)},p.prototype.minus=p.prototype.subtract,c.prototype.negate=function(){return new c(this.value,!this.sign)},s.prototype.negate=function(){var l=this.sign,n=new s(-this.value);return n.sign=!l,n},p.prototype.negate=function(){return new p(-this.value)},c.prototype.abs=function(){return new c(this.value,!1)},s.prototype.abs=function(){return new s(Math.abs(this.value))},p.prototype.abs=function(){return new p(this.value>=0?this.value:-this.value)},c.prototype.multiply=function(l){var e,t,u,r=X(l),o=this.value,i=r.value,s=this.sign!==r.sign;if(r.isSmall){if(0===i)return a[0];if(1===i)return this;if(-1===i)return this.negate();if((e=Math.abs(i))0?function l(n,e){var t=Math.max(n.length,e.length);if(t<=30)return M(n,e);t=Math.ceil(t/2);var u=n.slice(t),r=n.slice(0,t),o=e.slice(t),i=e.slice(0,t),a=l(r,i),c=l(u,o),s=l(z(r,u),z(i,o)),p=z(z(a,O(E(E(s,a),c),t)),O(c,2*t));return v(p),p}(o,i):M(o,i),s)},c.prototype.times=c.prototype.multiply,s.prototype._multiplyBySmall=function(l){return h(l.value*this.value)?new s(l.value*this.value):w(Math.abs(l.value),f(Math.abs(this.value)),this.sign!==l.sign)},c.prototype._multiplyBySmall=function(l){return 0===l.value?a[0]:1===l.value?this:-1===l.value?this.negate():w(Math.abs(l.value),this.value,this.sign!==l.sign)},s.prototype.multiply=function(l){return X(l)._multiplyBySmall(this)},s.prototype.times=s.prototype.multiply,p.prototype.multiply=function(l){return new p(this.value*X(l).value)},p.prototype.times=p.prototype.multiply,c.prototype.square=function(){return new c(V(this.value),!1)},s.prototype.square=function(){var l=this.value*this.value;return h(l)?new s(l):new c(V(f(Math.abs(this.value))),!1)},p.prototype.square=function(l){return new p(this.value*this.value)},c.prototype.divmod=function(l){var n=C(this,l);return{quotient:n[0],remainder:n[1]}},p.prototype.divmod=s.prototype.divmod=c.prototype.divmod,c.prototype.divide=function(l){return C(this,l)[0]},p.prototype.over=p.prototype.divide=function(l){return new p(this.value/X(l).value)},s.prototype.over=s.prototype.divide=c.prototype.over=c.prototype.divide,c.prototype.mod=function(l){return C(this,l)[1]},p.prototype.mod=p.prototype.remainder=function(l){return new p(this.value%X(l).value)},s.prototype.remainder=s.prototype.mod=c.prototype.remainder=c.prototype.mod,c.prototype.pow=function(l){var n,e,t,u=X(l),r=this.value,o=u.value;if(0===o)return a[1];if(0===r)return a[0];if(1===r)return a[1];if(-1===r)return u.isEven()?a[1]:a[-1];if(u.sign)return a[0];if(!u.isSmall)throw new Error("The exponent "+u.toString()+" is too large.");if(this.isSmall&&h(n=Math.pow(r,o)))return new s(y(n));for(e=this,t=a[1];!0&o&&(t=t.times(e),--o),0!==o;)o/=2,e=e.square();return t},s.prototype.pow=c.prototype.pow,p.prototype.pow=function(l){var n=X(l),e=this.value,t=n.value,u=BigInt(0),r=BigInt(1),o=BigInt(2);if(t===u)return a[1];if(e===u)return a[0];if(e===r)return a[1];if(e===BigInt(-1))return n.isEven()?a[1]:a[-1];if(n.isNegative())return new p(u);for(var i=this,c=a[1];(t&r)===r&&(c=c.times(i),--t),t!==u;)t/=o,i=i.square();return c},c.prototype.modPow=function(l,n){if(l=X(l),(n=X(n)).isZero())throw new Error("Cannot take modPow with modulus 0");var e=a[1],t=this.mod(n);for(l.isNegative()&&(l=l.multiply(a[-1]),t=t.modInv(n));l.isPositive();){if(t.isZero())return a[0];l.isOdd()&&(e=e.multiply(t).mod(n)),l=l.divide(2),t=t.square().mod(n)}return e},p.prototype.modPow=s.prototype.modPow=c.prototype.modPow,c.prototype.compareAbs=function(l){var n=X(l),e=this.value,t=n.value;return n.isSmall?1:A(e,t)},s.prototype.compareAbs=function(l){var n=X(l),e=Math.abs(this.value),t=n.value;return n.isSmall?e===(t=Math.abs(t))?0:e>t?1:-1:-1},p.prototype.compareAbs=function(l){var n=this.value,e=X(l).value;return(n=n>=0?n:-n)===(e=e>=0?e:-e)?0:n>e?1:-1},c.prototype.compare=function(l){if(l===1/0)return-1;if(l===-1/0)return 1;var n=X(l),e=this.value,t=n.value;return this.sign!==n.sign?n.sign?1:-1:n.isSmall?this.sign?-1:1:A(e,t)*(this.sign?-1:1)},c.prototype.compareTo=c.prototype.compare,s.prototype.compare=function(l){if(l===1/0)return-1;if(l===-1/0)return 1;var n=X(l),e=this.value,t=n.value;return n.isSmall?e==t?0:e>t?1:-1:e<0!==n.sign?e<0?-1:1:e<0?1:-1},s.prototype.compareTo=s.prototype.compare,p.prototype.compare=function(l){if(l===1/0)return-1;if(l===-1/0)return 1;var n=this.value,e=X(l).value;return n===e?0:n>e?1:-1},p.prototype.compareTo=p.prototype.compare,c.prototype.equals=function(l){return 0===this.compare(l)},p.prototype.eq=p.prototype.equals=s.prototype.eq=s.prototype.equals=c.prototype.eq=c.prototype.equals,c.prototype.notEquals=function(l){return 0!==this.compare(l)},p.prototype.neq=p.prototype.notEquals=s.prototype.neq=s.prototype.notEquals=c.prototype.neq=c.prototype.notEquals,c.prototype.greater=function(l){return this.compare(l)>0},p.prototype.gt=p.prototype.greater=s.prototype.gt=s.prototype.greater=c.prototype.gt=c.prototype.greater,c.prototype.lesser=function(l){return this.compare(l)<0},p.prototype.lt=p.prototype.lesser=s.prototype.lt=s.prototype.lesser=c.prototype.lt=c.prototype.lesser,c.prototype.greaterOrEquals=function(l){return this.compare(l)>=0},p.prototype.geq=p.prototype.greaterOrEquals=s.prototype.geq=s.prototype.greaterOrEquals=c.prototype.geq=c.prototype.greaterOrEquals,c.prototype.lesserOrEquals=function(l){return this.compare(l)<=0},p.prototype.leq=p.prototype.lesserOrEquals=s.prototype.leq=s.prototype.lesserOrEquals=c.prototype.leq=c.prototype.lesserOrEquals,c.prototype.isEven=function(){return 0===(1&this.value[0])},s.prototype.isEven=function(){return 0===(1&this.value)},p.prototype.isEven=function(){return(this.value&BigInt(1))===BigInt(0)},c.prototype.isOdd=function(){return 1===(1&this.value[0])},s.prototype.isOdd=function(){return 1===(1&this.value)},p.prototype.isOdd=function(){return(this.value&BigInt(1))===BigInt(1)},c.prototype.isPositive=function(){return!this.sign},s.prototype.isPositive=function(){return this.value>0},p.prototype.isPositive=s.prototype.isPositive,c.prototype.isNegative=function(){return this.sign},s.prototype.isNegative=function(){return this.value<0},p.prototype.isNegative=s.prototype.isNegative,c.prototype.isUnit=function(){return!1},s.prototype.isUnit=function(){return 1===Math.abs(this.value)},p.prototype.isUnit=function(){return this.abs().value===BigInt(1)},c.prototype.isZero=function(){return!1},s.prototype.isZero=function(){return 0===this.value},p.prototype.isZero=function(){return this.value===BigInt(0)},c.prototype.isDivisibleBy=function(l){var n=X(l);return!n.isZero()&&(!!n.isUnit()||(0===n.compareAbs(2)?this.isEven():this.mod(n).isZero()))},p.prototype.isDivisibleBy=s.prototype.isDivisibleBy=c.prototype.isDivisibleBy,c.prototype.isPrime=function(l){var n=S(this);if(void 0!==n)return n;var e=this.abs(),t=e.bitLength();if(t<=64)return I(e,[2,3,5,7,11,13,17,19,23,29,31,37]);for(var r=Math.log(2)*t.toJSNumber(),o=Math.ceil(!0===l?2*Math.pow(r,2):r),i=[],a=0;a-t?new s(l-1):new c(r,!0)},p.prototype.prev=function(){return new p(this.value-BigInt(1))};for(var R=[1];2*R[R.length-1]<=n;)R.push(2*R[R.length-1]);var H=R.length,P=R[H-1];function N(l){return Math.abs(l)<=n}function D(l,n,e){n=X(n);for(var t=l.isNegative(),r=n.isNegative(),o=t?l.not():l,i=r?n.not():n,a=0,c=0,s=null,p=null,h=[];!o.isZero()||!i.isZero();)a=(s=C(o,P))[1].toJSNumber(),t&&(a=P-1-a),c=(p=C(i,P))[1].toJSNumber(),r&&(c=P-1-c),o=s[0],i=p[0],h.push(e(a,c));for(var f=0!==e(t?1:0,r?1:0)?u(-1):u(0),d=h.length-1;d>=0;d-=1)f=f.multiply(P).add(u(h[d]));return f}c.prototype.shiftLeft=function(l){var n=X(l).toJSNumber();if(!N(n))throw new Error(String(n)+" is too large for shifting.");if(n<0)return this.shiftRight(-n);var e=this;if(e.isZero())return e;for(;n>=H;)e=e.multiply(P),n-=H-1;return e.multiply(R[n])},p.prototype.shiftLeft=s.prototype.shiftLeft=c.prototype.shiftLeft,c.prototype.shiftRight=function(l){var n,e=X(l).toJSNumber();if(!N(e))throw new Error(String(e)+" is too large for shifting.");if(e<0)return this.shiftLeft(-e);for(var t=this;e>=H;){if(t.isZero()||t.isNegative()&&t.isUnit())return t;t=(n=C(t,P))[1].isNegative()?n[0].prev():n[0],e-=H-1}return(n=C(t,R[e]))[1].isNegative()?n[0].prev():n[0]},p.prototype.shiftRight=s.prototype.shiftRight=c.prototype.shiftRight,c.prototype.not=function(){return this.negate().prev()},p.prototype.not=s.prototype.not=c.prototype.not,c.prototype.and=function(l){return D(this,l,function(l,n){return l&n})},p.prototype.and=s.prototype.and=c.prototype.and,c.prototype.or=function(l){return D(this,l,function(l,n){return l|n})},p.prototype.or=s.prototype.or=c.prototype.or,c.prototype.xor=function(l){return D(this,l,function(l,n){return l^n})},p.prototype.xor=s.prototype.xor=c.prototype.xor;var x=1<<30,k=(n&-n)*(n&-n)|x;function U(l){var e=l.value,t="number"===typeof e?e|x:"bigint"===typeof e?e|BigInt(x):e[0]+e[1]*n|k;return t&-t}function B(l,n){return l=X(l),n=X(n),l.greater(n)?l:n}function F(l,n){return l=X(l),n=X(n),l.lesser(n)?l:n}function j(l,n){if(l=X(l).abs(),n=X(n).abs(),l.equals(n))return l;if(l.isZero())return n;if(n.isZero())return l;for(var e,t,u=a[1];l.isEven()&&n.isEven();)e=F(U(l),U(n)),l=l.divide(e),n=n.divide(e),u=u.multiply(e);for(;l.isEven();)l=l.divide(U(l));do{for(;n.isEven();)n=n.divide(U(n));l.greater(n)&&(t=n,n=l,l=t),n=n.subtract(l)}while(!n.isZero());return u.isUnit()?l:l.multiply(u)}c.prototype.bitLength=function(){var l=this;return l.compareTo(u(0))<0&&(l=l.negate().subtract(u(1))),0===l.compareTo(u(0))?u(0):u(function l(n,e){if(e.compareTo(n)<=0){var t=l(n,e.square(e)),r=t.p,o=t.e,i=r.multiply(e);return i.compareTo(n)<=0?{p:i,e:2*o+1}:{p:r,e:2*o}}return{p:u(1),e:0}}(l,u(2)).e).add(u(1))},p.prototype.bitLength=s.prototype.bitLength=c.prototype.bitLength;var W=function(l,n,e,t){e=e||o,l=String(l),t||(l=l.toLowerCase(),e=e.toLowerCase());var u,r=l.length,i=Math.abs(n),a={};for(u=0;u=i)){if("1"===p&&1===i)continue;throw new Error(p+" is not a valid digit in base "+n+".")}}n=X(n);var c=[],s="-"===l[0];for(u=s?1:0;u"!==l[u]&&u=0;t--)u=u.add(l[t].times(r)),r=r.times(n);return e?u.negate():u}function K(l,n){if((n=u(n)).isZero()){if(l.isZero())return{value:[0],isNegative:!1};throw new Error("Cannot convert nonzero numbers to base 0.")}if(n.equals(-1)){if(l.isZero())return{value:[0],isNegative:!1};if(l.isNegative())return{value:[].concat.apply([],Array.apply(null,Array(-l.toJSNumber())).map(Array.prototype.valueOf,[1,0])),isNegative:!1};var e=Array.apply(null,Array(l.toJSNumber()-1)).map(Array.prototype.valueOf,[0,1]);return e.unshift([1]),{value:[].concat.apply([],e),isNegative:!1}}var t=!1;if(l.isNegative()&&n.isPositive()&&(t=!0,l=l.abs()),n.isUnit())return l.isZero()?{value:[0],isNegative:!1}:{value:Array.apply(null,Array(l.toJSNumber())).map(Number.prototype.valueOf,1),isNegative:t};for(var r,o=[],i=l;i.isNegative()||i.compareAbs(n)>=0;){r=i.divmod(n),i=r.quotient;var a=r.remainder;a.isNegative()&&(a=n.minus(a).abs(),i=i.next()),o.push(a.toJSNumber())}return o.push(i.toJSNumber()),{value:o.reverse(),isNegative:t}}function Y(l,n,e){var t=K(l,n);return(t.isNegative?"-":"")+t.value.map(function(l){return function(l,n){return l<(n=n||o).length?n[l]:"<"+l+">"}(l,e)}).join("")}function q(l){if(h(+l)){var n=+l;if(n===y(n))return i?new p(BigInt(n)):new s(n);throw new Error("Invalid integer: "+l)}var t="-"===l[0];t&&(l=l.slice(1));var u=l.split(/e/i);if(u.length>2)throw new Error("Invalid integer: "+u.join("e"));if(2===u.length){var r=u[1];if("+"===r[0]&&(r=r.slice(1)),(r=+r)!==y(r)||!h(r))throw new Error("Invalid integer: "+r+" is not a valid exponent.");var o=u[0],a=o.indexOf(".");if(a>=0&&(r-=o.length-a-1,o=o.slice(0,a)+o.slice(a+1)),r<0)throw new Error("Cannot include negative exponent part for integers");l=o+=new Array(r+1).join("0")}if(!/^([0-9][0-9]*)$/.test(l))throw new Error("Invalid integer: "+l);if(i)return new p(BigInt(t?"-"+l:l));for(var f=[],d=l.length,m=e,g=d-m;d>0;)f.push(+l.slice(g,d)),(g-=m)<0&&(g=0),d-=m;return v(f),new c(f,t)}function X(l){return"number"===typeof l?function(l){if(i)return new p(BigInt(l));if(h(l)){if(l!==y(l))throw new Error(l+" is not an integer.");return new s(l)}return q(l.toString())}(l):"string"===typeof l?q(l):"bigint"===typeof l?new p(l):l}c.prototype.toArray=function(l){return K(this,l)},s.prototype.toArray=function(l){return K(this,l)},p.prototype.toArray=function(l){return K(this,l)},c.prototype.toString=function(l,n){if(void 0===l&&(l=10),10!==l)return Y(this,l,n);for(var e,t=this.value,u=t.length,r=String(t[--u]);--u>=0;)e=String(t[u]),r+="0000000".slice(e.length)+e;return(this.sign?"-":"")+r},s.prototype.toString=function(l,n){return void 0===l&&(l=10),10!=l?Y(this,l,n):String(this.value)},p.prototype.toString=s.prototype.toString,p.prototype.toJSON=c.prototype.toJSON=s.prototype.toJSON=function(){return this.toString()},c.prototype.valueOf=function(){return parseInt(this.toString(),10)},c.prototype.toJSNumber=c.prototype.valueOf,s.prototype.valueOf=function(){return this.value},s.prototype.toJSNumber=s.prototype.valueOf,p.prototype.valueOf=p.prototype.toJSNumber=function(){return parseInt(this.toString(),10)};for(var $=0;$<1e3;$++)a[$]=X($),$>0&&(a[-$]=X(-$));return a.one=a[1],a.zero=a[0],a.minusOne=a[-1],a.max=B,a.min=F,a.gcd=j,a.lcm=function(l,n){return l=X(l).abs(),n=X(n).abs(),l.divide(j(l,n)).multiply(n)},a.isInstance=function(l){return l instanceof c||l instanceof s||l instanceof p},a.randBetween=function(l,e,t){l=X(l),e=X(e);var u=t||Math.random,r=F(l,e),o=B(l,e).subtract(r).add(1);if(o.isSmall)return r.add(Math.floor(u()*o));for(var i=K(o,n).value,c=[],s=!0,p=0;p=0||this.changedListeners.push(l)},l.prototype.removeChangedListener=function(l){var n=this.changedListeners.indexOf(l);n<0||this.changedListeners.splice(n,1)},l.prototype.update=function(l){var n=this;if(this.id===l.id){var e=this.typeDefinition.update(l.typeDefinition);void 0!==l._label&&(this._label=l._label,e=!0),l.languageLabels.size>0&&(this.languageLabels.clear(),l.languageLabels.forEach(function(l,e){n.languageLabels.set(e,l)}),e=!0),void 0!==l._description&&(this._description=l._description,e=!0),l.languageDescriptions.size>0&&(this.languageDescriptions.clear(),l.languageDescriptions.forEach(function(l,e){n.languageDescriptions.set(e,l)}),e=!0),void 0!==l._tags&&(this._tags=l._tags,e=!0),void 0!==l._order&&(this._order=l._order,e=!0),void 0!==l._parent&&(this.parent=l._parent,e=!0),void 0!==l._widget&&(this._widget=l._widget,e=!0),void 0!==l._userdata&&(this._userdata=l._userdata,e=!0),void 0!==l._userid&&(this._userid=l._userid,e=!0),void 0!==l._readonly&&(this._readonly=l._readonly,e=!0),e&&this.changedListeners.forEach(function(l){l(n)})}},l.prototype.removeFromParent=function(){void 0!==this._parent&&(this._parent.removeChild(this),this._parent=void 0)},l.prototype.writeValueUpdate=function(l){u.pushIn16ToArrayBe(this.id,l),l.push(this.typeDefinition.datatype),this.typeDefinition.writeMandatory(l)},l.prototype.writeLabel=function(l){this._label&&(l.push("any".charCodeAt(0)),l.push("any".charCodeAt(1)),l.push("any".charCodeAt(2)),u.writeTinyString(this._label,l)),this.languageLabels.size>0&&this.languageLabels.forEach(function(n,e){e.length<3||(l.push(e.charCodeAt(0)),l.push(e.charCodeAt(1)),l.push(e.charCodeAt(2)),u.writeTinyString(n,l))}),l.push(r.RcpTypes.TERMINATOR)},l.prototype.writeDescription=function(l){this._description&&(l.push("any".charCodeAt(0)),l.push("any".charCodeAt(1)),l.push("any".charCodeAt(2)),u.writeShortString(this._description,l)),this.languageDescriptions.size>0&&this.languageDescriptions.forEach(function(n,e){e.length<3||(l.push(e.charCodeAt(0)),l.push(e.charCodeAt(1)),l.push(e.charCodeAt(2)),u.writeShortString(n,l))}),l.push(r.RcpTypes.TERMINATOR)},l.prototype.writeOptions=function(n,e){var t=this,o=this.changed;e&&(o=l.allOptions),o.forEach(function(l,o){switch(o){case r.RcpTypes.ParameterOptions.VALUE:break;case r.RcpTypes.ParameterOptions.LABEL:n.push(r.RcpTypes.ParameterOptions.LABEL),t._label||t.languageLabels.size>0?t.writeLabel(n):n.push(r.RcpTypes.TERMINATOR);break;case r.RcpTypes.ParameterOptions.DESCRIPTION:n.push(r.RcpTypes.ParameterOptions.DESCRIPTION),t._description||t.languageDescriptions.size>0?t.writeDescription(n):n.push(r.RcpTypes.TERMINATOR);break;case r.RcpTypes.ParameterOptions.TAGS:n.push(r.RcpTypes.ParameterOptions.TAGS),t._tags?u.writeTinyString(t._tags,n):u.writeTinyString("",n);break;case r.RcpTypes.ParameterOptions.ORDER:n.push(r.RcpTypes.ParameterOptions.ORDER),void 0!=t._order?u.pushIn32ToArrayBe(t._order,n):u.pushIn32ToArrayBe(0,n);break;case r.RcpTypes.ParameterOptions.PARENTID:n.push(r.RcpTypes.ParameterOptions.PARENTID),t._parent?u.pushIn16ToArrayBe(t._parent.id,n):u.pushIn16ToArrayBe(0,n);break;case r.RcpTypes.ParameterOptions.WIDGET:n.push(r.RcpTypes.ParameterOptions.WIDGET),t._widget?t._widget.write(n,e):n.push(r.RcpTypes.TERMINATOR);break;case r.RcpTypes.ParameterOptions.READONLY:n.push(r.RcpTypes.ParameterOptions.READONLY),t._readonly?n.push(t._readonly?1:0):n.push(0);break;case r.RcpTypes.ParameterOptions.USERDATA:t._userdata;break;case r.RcpTypes.ParameterOptions.USERID:n.push(r.RcpTypes.ParameterOptions.USERID),t._userid?u.writeTinyString(t._userid,n):u.writeTinyString("",n)}}),e||this.changed.clear()},l.prototype.write=function(l,n){u.pushIn16ToArrayBe(this.id,l),this.typeDefinition.write(l,n),this.writeOptions(l,n),l.push(r.RcpTypes.TERMINATOR)},l.prototype.handleOption=function(l,n){return!1},l.prototype.parseOptions=function(l){for(this.typeDefinition.readMandatory(l),this.typeDefinition.parseOptions(l);;){var n=l.readU1();if(n==r.RcpTypes.TERMINATOR)break;switch(n){case r.RcpTypes.ParameterOptions.LABEL:for(var e=l.pos,u=l.readS1();0!=u;){l.seek(e);var i=t.default.bytesToStr(l.readBytes(3),"utf-8"),a=new r.TinyString(l).data;a&&("any"===i?this._label=a:(console.log("setting language label "+i+" : "+a),this.languageLabels.set(i,a))),e=l.pos,u=l.readS1()}break;case r.RcpTypes.ParameterOptions.DESCRIPTION:for(e=l.pos,u=l.readS1();0!=u;){l.seek(e);i=t.default.bytesToStr(l.readBytes(3),"utf-8");var c=new r.ShortString(l).data;c&&("any"===i?this._description=c:(console.log("setting language label "+i+" : "+c),this.languageDescriptions.set(i,c))),e=l.pos,u=l.readS1()}break;case r.RcpTypes.ParameterOptions.TAGS:this._tags=new r.TinyString(l).data;break;case r.RcpTypes.ParameterOptions.ORDER:this._order=l.readS4be();break;case r.RcpTypes.ParameterOptions.PARENTID:var s=l.readS2be();if(this.manager)if(0===s)this._parent=this.manager.getRootGroup();else{var p=this.manager.getParameter(s);p&&(this._parent=p)}break;case r.RcpTypes.ParameterOptions.WIDGET:this._widget=o.parseWidget(l,this);break;case r.RcpTypes.ParameterOptions.READONLY:this._readonly=l.readS1()>0;break;case r.RcpTypes.ParameterOptions.USERDATA:this._userdata=new r.Userdata(l).data;break;case r.RcpTypes.ParameterOptions.USERID:this._userid=new r.TinyString(l).data;break;case r.RcpTypes.ParameterOptions.VALUE:default:if(!this.handleOption(n,l))throw new Error("parameter option not handled: "+n)}}},l.prototype.setDirty=function(){console.log("set dirty: "+this.label),this.manager&&this.manager.setParameterDirty(this)},Object.defineProperty(l.prototype,"label",{get:function(){return this._label},set:function(l){this._label!==l&&(this._label=l,this.changed.set(r.RcpTypes.ParameterOptions.LABEL,!0),this.setDirty())},enumerable:!0,configurable:!0}),l.prototype.getLabelLanguages=function(){return this.languageLabels.keys()},l.prototype.getLanguageLabel=function(l){return this.languageLabels.get(l)},l.prototype.clearLanguageLabels=function(){this.languageLabels.clear(),this.changed.set(r.RcpTypes.ParameterOptions.LABEL,!0),this.setDirty()},l.prototype.setLanguageLabel=function(l,n){this.languageLabels.set(l,n),this.changed.set(r.RcpTypes.ParameterOptions.LABEL,!0),this.setDirty()},l.prototype.removeLanguageLabel=function(l){this.languageLabels.delete(l),this.changed.set(r.RcpTypes.ParameterOptions.LABEL,!0),this.setDirty()},Object.defineProperty(l.prototype,"description",{get:function(){return this._description},set:function(l){this._description!==l&&(this._description=l,this.changed.set(r.RcpTypes.ParameterOptions.DESCRIPTION,!0),this.setDirty())},enumerable:!0,configurable:!0}),l.prototype.getDescriptionLanguages=function(){return this.languageDescriptions.keys()},l.prototype.getLanguageDescription=function(l){return this.languageDescriptions.get(l)},l.prototype.clearLanguageDescriptions=function(){this.languageDescriptions.clear(),this.changed.set(r.RcpTypes.ParameterOptions.DESCRIPTION,!0),this.setDirty()},l.prototype.setLanguageDescription=function(l,n){this.languageDescriptions.set(l,n),this.changed.set(r.RcpTypes.ParameterOptions.DESCRIPTION,!0),this.setDirty()},l.prototype.removeLanguageDescription=function(l){this.languageDescriptions.delete(l),this.changed.set(r.RcpTypes.ParameterOptions.DESCRIPTION,!0),this.setDirty()},Object.defineProperty(l.prototype,"tags",{get:function(){return this._tags},set:function(l){this._tags!==l&&(this._tags=l,this.changed.set(r.RcpTypes.ParameterOptions.TAGS,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(l.prototype,"order",{get:function(){return this._order},set:function(l){this._order!==l&&(this._order=l,this.changed.set(r.RcpTypes.ParameterOptions.ORDER,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(l.prototype,"parent",{get:function(){return this._parent},set:function(l){void 0!==this._parent&&void 0!==l&&this._parent.id===l.id||(this.setParentDirect(l),this.changed.set(r.RcpTypes.ParameterOptions.PARENTID,!0),this.setDirty())},enumerable:!0,configurable:!0}),l.prototype.setParentDirect=function(l){this.removeFromParent(),this._parent=l,void 0!==this._parent&&this._parent.addChild(this)},Object.defineProperty(l.prototype,"widget",{get:function(){return this._widget},set:function(l){this._widget=l,this._widget&&(this._widget.parameter=this),this.changed.set(r.RcpTypes.ParameterOptions.WIDGET,!0),this.setDirty()},enumerable:!0,configurable:!0}),Object.defineProperty(l.prototype,"userdata",{get:function(){return this._userdata},set:function(l){this._userdata!==l&&(this._userdata=l,this.changed.set(r.RcpTypes.ParameterOptions.USERDATA,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(l.prototype,"userid",{get:function(){return this._userid},set:function(l){this._userid!==l&&(this._userid=l,this.changed.set(r.RcpTypes.ParameterOptions.USERID,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(l.prototype,"readonly",{get:function(){return this._readonly},set:function(l){this._readonly!==l&&(this._readonly=l,this.changed.set(r.RcpTypes.ParameterOptions.READONLY,!0),this.setDirty())},enumerable:!0,configurable:!0}),l.LANGUAGE_ANY="any",l.allOptions=(new Map).set(r.RcpTypes.ParameterOptions.VALUE,!0).set(r.RcpTypes.ParameterOptions.LABEL,!0).set(r.RcpTypes.ParameterOptions.DESCRIPTION,!0).set(r.RcpTypes.ParameterOptions.TAGS,!0).set(r.RcpTypes.ParameterOptions.ORDER,!0).set(r.RcpTypes.ParameterOptions.PARENTID,!0).set(r.RcpTypes.ParameterOptions.WIDGET,!0).set(r.RcpTypes.ParameterOptions.USERDATA,!0).set(r.RcpTypes.ParameterOptions.USERID,!0).set(r.RcpTypes.ParameterOptions.READONLY,!0),l}();n.Parameter=i},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(19),r=e(27),o=e(15),i=e(4),a=function(){function l(l,n){this.x=l,this.y=n}return l.prototype.toString=function(){return this.x+", "+this.y},l.prototype.clone=function(){return new l(this.x,this.y)},l.prototype.add=function(l){return this.x+=l.x,this.y+=l.y,this},l.prototype.sub=function(l){return this.x-=l.x,this.y-=l.y,this},l}();n.Vector2=a;var c=function(){function l(l,n,e){this.x=l,this.y=n,this.z=e}return l.prototype.toString=function(){return this.x+", "+this.y+", "+this.z},l.prototype.clone=function(){return new l(this.x,this.y,this.z)},l.prototype.add=function(l){return this.x+=l.x,this.y+=l.y,this.z+=l.z,this},l.prototype.sub=function(l){return this.x-=l.x,this.y-=l.y,this.z-=l.z,this},l}();n.Vector3=c;var s=function(){function l(l,n,e,t){this.x=l,this.y=n,this.z=e,this.t=t}return l.prototype.toString=function(){return this.x+", "+this.y+", "+this.z+", "+this.t},l.prototype.clone=function(){return new l(this.x,this.y,this.z,this.t)},l.prototype.add=function(l){return this.x+=l.x,this.y+=l.y,this.z+=l.z,this.t+=l.t,this},l.prototype.sub=function(l){return this.x-=l.x,this.y-=l.y,this.z-=l.z,this.t-=l.t,this},l}();n.Vector4=s;var p=function(l){function n(){return null!==l&&l.apply(this,arguments)||this}return t(n,l),n.prototype.update=function(l){var e=!1;return l instanceof n&&(void 0!==l._defaultValue&&(this._defaultValue=l._defaultValue,e=!0),void 0!==l._minimum&&(this._minimum=l._minimum,e=!0),void 0!==l._maximum&&(this._maximum=l._maximum,e=!0),void 0!==l._multipleof&&(this._multipleof=l._multipleof,e=!0),void 0!==l._scale&&(this._scale=l._scale,e=!0),void 0!==l._unit&&(this._unit=l._unit,e=!0)),e},n.prototype.handleOption=function(l,n){switch(l){case i.RcpTypes.NumberOptions.DEFAULT:return this._defaultValue=this.readValue(n),!0;case i.RcpTypes.NumberOptions.MINIMUM:return this._minimum=this.readValue(n),!0;case i.RcpTypes.NumberOptions.MAXIMUM:return this._maximum=this.readValue(n),!0;case i.RcpTypes.NumberOptions.MULTIPLEOF:return this._multipleof=this.readValue(n),!0;case i.RcpTypes.NumberOptions.SCALE:var e=n.readU1();return ei.RcpTypes.NumberScale.EXP2?this._scale=i.RcpTypes.NumberScale.LINEAR:this._scale=e,!0;case i.RcpTypes.NumberOptions.UNIT:var t=n.readU1();return this._unit=r.default.bytesToStr(n.readBytes(t),"UTF-8"),!0}return!1},n.prototype.getDefaultId=function(){return i.RcpTypes.VectorOptions.DEFAULT},n.prototype.writeOptions=function(l,e){var t=this,u=this.changed;e&&(u=n.allOptions),u.forEach(function(n,e){switch(e){case i.RcpTypes.NumberOptions.DEFAULT:l.push(i.RcpTypes.NumberOptions.DEFAULT),t.writeValue(l,t._defaultValue);break;case i.RcpTypes.NumberOptions.MINIMUM:l.push(i.RcpTypes.NumberOptions.MINIMUM),t.writeValue(l,t._minimum);break;case i.RcpTypes.NumberOptions.MAXIMUM:l.push(i.RcpTypes.NumberOptions.MAXIMUM),t.writeValue(l,t._maximum);break;case i.RcpTypes.NumberOptions.MULTIPLEOF:l.push(i.RcpTypes.NumberOptions.MULTIPLEOF),t.writeValue(l,t._multipleof);break;case i.RcpTypes.NumberOptions.SCALE:l.push(i.RcpTypes.NumberOptions.SCALE),t._scale?l.push(t._scale):l.push(i.RcpTypes.NumberScale.LINEAR);break;case i.RcpTypes.NumberOptions.UNIT:l.push(i.RcpTypes.NumberOptions.UNIT),t._unit?o.writeTinyString(t._unit,l):o.writeTinyString("",l)}}),e||this.changed.clear()},Object.defineProperty(n.prototype,"minimum",{get:function(){return this._minimum},set:function(l){this._minimum!==l&&(this._minimum=l,this.changed.set(i.RcpTypes.VectorOptions.MINIMUM,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"maximum",{get:function(){return this._maximum},set:function(l){this._maximum!==l&&(this._maximum=l,this.changed.set(i.RcpTypes.VectorOptions.MAXIMUM,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"multipleof",{get:function(){return this._multipleof},set:function(l){this._multipleof!==l&&(this._multipleof=l,this.changed.set(i.RcpTypes.VectorOptions.MULTIPLEOF,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"scale",{get:function(){return this._scale},set:function(l){this._scale!==l&&(this._scale=l,this.changed.set(i.RcpTypes.VectorOptions.SCALE,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"unit",{get:function(){return this._unit},set:function(l){this._unit!==l&&(this._unit=l,this.changed.set(i.RcpTypes.VectorOptions.UNIT,!0),this.setDirty())},enumerable:!0,configurable:!0}),n.allOptions=(new Map).set(i.RcpTypes.VectorOptions.DEFAULT,!0).set(i.RcpTypes.VectorOptions.MINIMUM,!0).set(i.RcpTypes.VectorOptions.MAXIMUM,!0).set(i.RcpTypes.VectorOptions.MULTIPLEOF,!0).set(i.RcpTypes.VectorOptions.SCALE,!0).set(i.RcpTypes.VectorOptions.UNIT,!0),n}(u.DefaultDefinition);n.default=p},function(l,n){function e(){return l.exports=e=Object.assign||function(l){for(var n=1;n=this.size},n.prototype.seek=function(l){var n=Math.max(0,Math.min(this.size,l));this.pos=isNaN(n)||!isFinite(n)?0:n},Object.defineProperty(n.prototype,"size",{get:function(){return this._byteLength-this._byteOffset},enumerable:!0,configurable:!0}),n.prototype.readS1=function(){var l=this._dataView.getInt8(this.pos);return this.pos+=1,l},n.prototype.readS2be=function(){var l=this._dataView.getInt16(this.pos);return this.pos+=2,l},n.prototype.readS4be=function(){var l=this._dataView.getInt32(this.pos);return this.pos+=4,l},n.prototype.readS8be=function(){var l=this.readU4be(),n=this.readU4be();return 0!=(2147483648&l)?-(4294967296*(4294967295^l)+(4294967295^n))-1:4294967296*l+n},n.prototype.readS2le=function(){var l=this._dataView.getInt16(this.pos,!0);return this.pos+=2,l},n.prototype.readS4le=function(){var l=this._dataView.getInt32(this.pos,!0);return this.pos+=4,l},n.prototype.readS8le=function(){var l=this.readU4le(),n=this.readU4le();return 0!=(2147483648&n)?-(4294967296*(4294967295^n)+(4294967295^l))-1:4294967296*n+l},n.prototype.readU1=function(){var l=this._dataView.getUint8(this.pos);return this.pos+=1,l},n.prototype.readU2be=function(){var l=this._dataView.getUint16(this.pos);return this.pos+=2,l},n.prototype.readU4be=function(){var l=this._dataView.getUint32(this.pos);return this.pos+=4,l},n.prototype.readU8be=function(){return 4294967296*this.readU4be()+this.readU4be()},n.prototype.readU2le=function(){var l=this._dataView.getUint16(this.pos,!0);return this.pos+=2,l},n.prototype.readU4le=function(){var l=this._dataView.getUint32(this.pos,!0);return this.pos+=4,l},n.prototype.readU8le=function(){var l=this.readU4le();return 4294967296*this.readU4le()+l},n.prototype.readF4be=function(){var l=this._dataView.getFloat32(this.pos);return this.pos+=4,l},n.prototype.readF8be=function(){var l=this._dataView.getFloat64(this.pos);return this.pos+=8,l},n.prototype.readF4le=function(){var l=this._dataView.getFloat32(this.pos,!0);return this.pos+=4,l},n.prototype.readF8le=function(){var l=this._dataView.getFloat64(this.pos,!0);return this.pos+=8,l},n.prototype.alignToByte=function(){this.bits=0,this.bitsLeft=0},n.prototype.readBitsInt=function(l){if(l>32)throw new Error("readBitsInt: the maximum supported bit length is 32 (tried to read "+l+" bits)");var n=l-this.bitsLeft;if(n>0)for(var e=Math.ceil(n/8),t=this.readBytes(e),u=0;u>>o;return this.bitsLeft-=l,r=(1<=t&&(u=0);return e},n.processRotateLeft=function(l,n,e){if(1!=e)throw"unable to rotate group of "+e+" bytes yet";for(var t=-n&8*e-1,u=new Uint8Array(l.length),r=0;r>t;return u},n.prototype.mapUint8Array=function(l){if(l|=0,this.pos+l>this.size)throw new r(l,this.size-this.pos);var n=new Uint8Array(this._buffer,this.byteOffset+this.pos,l);return this.pos+=l,n},n.createStringFromArray=function(l){for(var n=[],e=0;e0,n}();n.default=i}).call(this,e(179).Buffer)},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(19),r=e(27),o=e(15),i=e(4),a=function(l){function n(){return null!==l&&l.apply(this,arguments)||this}return t(n,l),n.prototype.update=function(l){var e=!1;return l instanceof n&&(void 0!==l._defaultValue&&(this._defaultValue=l._defaultValue,e=!0),void 0!==l._minimum&&(this._minimum=l._minimum,e=!0),void 0!==l._maximum&&(this._maximum=l._maximum,e=!0),void 0!==l._multipleof&&(this._multipleof=l._multipleof,e=!0),void 0!==l._scale&&(this._scale=l._scale,e=!0),void 0!==l._unit&&(this._unit=l._unit,e=!0)),e},n.prototype.handleOption=function(l,n){switch(l){case i.RcpTypes.NumberOptions.DEFAULT:return this._defaultValue=this.readValue(n),!0;case i.RcpTypes.NumberOptions.MINIMUM:return this._minimum=this.readValue(n),!0;case i.RcpTypes.NumberOptions.MAXIMUM:return this._maximum=this.readValue(n),!0;case i.RcpTypes.NumberOptions.MULTIPLEOF:return this._multipleof=this.readValue(n),!0;case i.RcpTypes.NumberOptions.SCALE:var e=n.readU1();return ei.RcpTypes.NumberScale.EXP2?this._scale=i.RcpTypes.NumberScale.LINEAR:this._scale=e,!0;case i.RcpTypes.NumberOptions.UNIT:var t=n.readU1();return this._unit=r.default.bytesToStr(n.readBytes(t),"UTF-8"),!0}return!1},n.prototype.getDefaultId=function(){return i.RcpTypes.NumberOptions.DEFAULT},n.prototype.getTypeDefault=function(){return 0},n.prototype.writeOptions=function(l,e){var t=this,u=this.changed;e&&(u=n.allOptions),u.forEach(function(n,e){switch(e){case i.RcpTypes.NumberOptions.DEFAULT:l.push(i.RcpTypes.NumberOptions.DEFAULT),t.writeValue(l,t._defaultValue);break;case i.RcpTypes.NumberOptions.MINIMUM:l.push(i.RcpTypes.NumberOptions.MINIMUM),t.writeValue(l,t._minimum);break;case i.RcpTypes.NumberOptions.MAXIMUM:l.push(i.RcpTypes.NumberOptions.MAXIMUM),t.writeValue(l,t._maximum);break;case i.RcpTypes.NumberOptions.MULTIPLEOF:l.push(i.RcpTypes.NumberOptions.MULTIPLEOF),t.writeValue(l,t._multipleof);break;case i.RcpTypes.NumberOptions.SCALE:l.push(i.RcpTypes.NumberOptions.SCALE),t._scale?l.push(t._scale):l.push(i.RcpTypes.NumberScale.LINEAR);break;case i.RcpTypes.NumberOptions.UNIT:l.push(i.RcpTypes.NumberOptions.UNIT),t._unit?o.writeTinyString(t._unit,l):o.writeTinyString("",l)}}),e||this.changed.clear()},n.prototype.constrainValue=function(l){return void 0!==this.maximum&&l>this.maximum?this.maximum:void 0!==this.minimum&&l0)&&!(t=r.next()).done;)o.push(t.value)}catch(i){u={error:i}}finally{try{t&&!t.done&&(e=r.return)&&e.call(r)}finally{if(u)throw u.error}}return o};Object.defineProperty(n,"__esModule",{value:!0}),n.Validator=void 0;var u=e(18),r=e(18),o=e(20),i=e(33),a=e(38),c=e(30),s=e(30),p=function(){function l(){}return l.isWithinRange=function(l,n,e){return l.greaterOrEquals(n)&&l.lesserOrEquals(e)},l.isValidAsnNumber=function(n){var e=this.isWithinRange(n,o.zero,this.THIRTY_TWO_BIT_SIZE);return[e,e?[]:[l.invalidAsnRangeMessage]]},l.isValid16BitAsnNumber=function(n){var e=l.isWithinRange(n,o.zero,l.SIXTEEN_BIT_SIZE);return[e,e?[]:[l.invalid16BitAsnRangeMessage]]},l.isValidIPv4Number=function(n){var e=this.isWithinRange(n,o.zero,this.THIRTY_TWO_BIT_SIZE);return e?[e,[]]:[e,[l.invalidIPv4NumberMessage]]},l.isValidIPv6Number=function(n){var e=this.isWithinRange(n,o.zero,this.ONE_HUNDRED_AND_TWENTY_EIGHT_BIT_SIZE);return e?[e,[]]:[e,[l.invalidIPv6NumberMessage]]},l.isValidIPv4Octet=function(n){var e=this.isWithinRange(n,o.zero,this.EIGHT_BIT_SIZE);return[e,e?[]:[l.invalidOctetRangeMessage]]},l.isValidIPv6Hexadecatet=function(n){var e=this.isWithinRange(n,o.zero,this.SIXTEEN_BIT_SIZE);return e?[e,[]]:[e,[l.invalidHexadecatetMessage]]},l.isValidIPv4String=function(n){var e=n.split(".");if(4!=e.length||e.includes(""))return[!1,[l.invalidOctetCountMessage]];var t=e.every(function(n){return!!l.isNumeric(n)&&l.isValidIPv4Octet(o(n))[0]});return[t,t?[]:[l.invalidOctetRangeMessage]]},l.isValidIPv6String=function(n){try{var e=a.expandIPv6Number(n).split(":");if(8!=e.length)return[!1,[l.invalidHexadecatetCountMessage]];var t=e.every(function(n){return!!l.isHexadecatet(n)&&l.isValidIPv6Hexadecatet(o(parseInt(n,16)))[0]});return[t,t?[]:[l.invalidHexadecatetMessage]]}catch(u){return[!1,[u]]}},l.isValidPrefixValue=function(n,e){var t;return i.IPNumType.IPv4===e?[t=l.isWithinRange(o(n),o.zero,o(32)),t?[]:[l.invalidPrefixValueMessage]]:i.IPNumType.IPv6===e?[t=l.isWithinRange(o(n),o.zero,o(128)),t?[]:[l.invalidPrefixValueMessage]]:[!1,[l.invalidInetNumType]]},l.isValidIPv4Mask=function(n){var e=u.dottedDecimalNotationToBinaryString(n),t=l.IPV4_CONTIGUOUS_MASK_BIT_PATTERN.test(e);return t?[t,[]]:[t,[l.invalidMaskMessage]]},l.isValidIPv6Mask=function(n){var e=s.hexadectetNotationToBinaryString(n),t=l.IPV6_CONTIGUOUS_MASK_BIT_PATTERN.test(e);return t?[t,[]]:[t,[l.invalidMaskMessage]]},l.isValidIPv4CidrNotation=function(n){var e=n.split("/");if(2!==e.length||0===e[0].length||0===e[1].length)return[!1,[l.invalidIPv4CidrNotationMessage]];var u=e[0],r=e[1];if(isNaN(Number(r)))return[!1,[l.invalidIPv4CidrNotationMessage]];var o=t(l.isValidIPv4String(u),2),a=o[0],c=o[1],s=t(l.isValidPrefixValue(Number(r),i.IPNumType.IPv4),2),p=s[0],h=s[1],f=a&&p,d=c.concat(h);return f?[f,[]]:[f,d]},l.isValidIPv4CidrRange=function(n){return l.isValidCidrRange(n,l.isValidIPv4CidrNotation,u.dottedDecimalNotationToBinaryString,function(l){return r.cidrPrefixToMaskBinaryString(l,i.IPNumType.IPv4)})},l.isValidIPv6CidrRange=function(n){return l.isValidCidrRange(n,l.isValidIPv6CidrNotation,c.colonHexadecimalNotationToBinaryString,function(l){return r.cidrPrefixToMaskBinaryString(l,i.IPNumType.IPv6)})},l.isValidCidrRange=function(n,e,t,u){var r=e(n);if(!r[0])return r;var i=n.split("/"),a=i[0],c=i[1],s=o(t(a),2),p=o(u(parseInt(c)),2),h=s.and(p).equals(s);return h?[h,[]]:[h,[l.InvalidIPCidrRangeMessage]]},l.isValidIPv4RangeString=function(n){return this.isValidRange(n,l.isValidIPv4String,function(l,n){return o(u.dottedDecimalNotationToBinaryString(l)).greaterOrEquals(u.dottedDecimalNotationToBinaryString(n))})},l.isValidIPv6RangeString=function(n){return this.isValidRange(n,l.isValidIPv6String,function(l,n){return o(s.hexadectetNotationToBinaryString(l)).greaterOrEquals(s.hexadectetNotationToBinaryString(n))})},l.isValidRange=function(n,e,u){var r=n.split("-").map(function(l){return l.trim()});if(2!==r.length||0===r[0].length||0===r[1].length)return[!1,[l.invalidRangeNotationMessage]];var o=r[0],i=r[1],a=t(e(o),2),c=a[0],s=a[1],p=t(e(i),2),h=p[0],f=p[1],d=c&&h;if(d&&u(o,i))return[!1,[l.invalidRangeFirstNotGreaterThanLastMessage]];var v=s.concat(f);return d?[d,[]]:[d,v]},l.isValidIPv6CidrNotation=function(n){var e=l.IPV6_RANGE_PATTERN.test(n);return e?[e,[]]:[e,[l.invalidIPv6CidrNotationString]]},l.isValidBinaryString=function(n){return/^([10])+$/.test(n)?[!0,[]]:[!1,[l.invalidBinaryStringErrorMessage]]},l.isNumeric=function(l){return/^(\d+)$/.test(l)},l.isHexadecatet=function(l){return/^[0-9A-Fa-f]{4}$/.test(l)},l.IPV4_PATTERN=new RegExp(/^(0?[0-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.(0?[0-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.(0?[0-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.(0?[0-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$/),l.IPV4_RANGE_PATTERN=new RegExp(/^(0?[0-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.(0?[0-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.(0?[0-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.(0?[0-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\/)([1-9]|[1-2][0-9]|3[0-2])$/),l.IPV6_RANGE_PATTERN=new RegExp(/^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/),l.IPV4_CONTIGUOUS_MASK_BIT_PATTERN=new RegExp(/^(1){0,32}(0){0,32}$/),l.IPV6_CONTIGUOUS_MASK_BIT_PATTERN=new RegExp(/^(1){0,128}(0){0,128}$/),l.EIGHT_BIT_SIZE=o("1".repeat(8),2),l.SIXTEEN_BIT_SIZE=o("1".repeat(16),2),l.THIRTY_TWO_BIT_SIZE=o("1".repeat(32),2),l.ONE_HUNDRED_AND_TWENTY_EIGHT_BIT_SIZE=o("1".repeat(128),2),l.IPV4_SIZE=o("4294967296"),l.IPV6_SIZE=o("340282366920938463463374607431768211456"),l.invalidAsnRangeMessage="ASN number given less than zero or is greater than 32bit",l.invalid16BitAsnRangeMessage="ASN number given less than zero or is greater than 16bit",l.invalidIPv4NumberMessage="IPv4 number given less than zero or is greater than 32bit",l.invalidIPv6NumberMessage="IPv6 number given less than zero or is greater than 128bit",l.invalidOctetRangeMessage="Value given contains an invalid Octet; Value is less than zero or is greater than 8bit",l.invalidHexadecatetMessage="The value given is less than zero or is greater than 16bit",l.invalidOctetCountMessage="An IP4 number cannot have less or greater than 4 octets",l.invalidHexadecatetCountMessage="An IP6 number must have exactly 8 hexadecatet",l.invalidMaskMessage="The Mask is invalid",l.invalidPrefixValueMessage="A Prefix value cannot be less than 0 or greater than 32",l.invalidIPv4CidrNotationMessage="Cidr notation should be in the form [ip number]/[range]",l.InvalidIPCidrRangeMessage="Given IP number portion must is not the start of the range",l.invalidRangeNotationMessage="Range notation should be in the form [first ip]-[last ip]",l.invalidRangeFirstNotGreaterThanLastMessage="First IP in [first ip]-[last ip] must be less than Last IP",l.invalidIPv6CidrNotationString="A Cidr notation string should contain an IPv6 number and prefix",l.takeOutOfRangeSizeMessage="$count is greater than $size, the size of the range",l.cannotSplitSingleRangeErrorMessage="Cannot split an IP range with a single IP number",l.invalidInetNumType="Given ipNumType must be either InetNumType.IPv4 or InetNumType.IPv6",l.invalidBinaryStringErrorMessage="Binary string should contain only contiguous 1s and 0s",l.invalidIPRangeSizeMessage="Given size is zero or greater than maximum size of $iptype",l.invalidIPRangeSizeForCidrMessage="Given size can't be created via cidr prefix",l}();n.Validator=p},function(l,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.hexadectetNotationToBinaryString=n.binaryStringToHexadecimalString=n.colonHexadecimalNotationToBinaryString=n.hexadecimalStringToHexadecatetString=n.hexadecimalStringToBinaryString=n.bigIntegerNumberToHexadecimalString=void 0;var t=e(20),u=e(38),r=e(18);n.bigIntegerNumberToHexadecimalString=function(l){return l.toString(16)},n.hexadecimalStringToBinaryString=function(l){return t(l,16).toString(2)},n.hexadecimalStringToHexadecatetString=function(l){var e=n.hexadecimalStringToBinaryString(l);if(e.length>16)throw new Error("Given decimal in binary contains digits greater than an Hexadecatet");return r.leftPadWithZeroBit(e,16)},n.colonHexadecimalNotationToBinaryString=function(l){return u.expandIPv6Number(l).split(":").reduce(function(l,e){return l.concat(n.hexadecimalStringToHexadecatetString(e))},"")},n.binaryStringToHexadecimalString=function(l){return t(l,2).toString(16)},n.hexadectetNotationToBinaryString=function(l){return u.expandIPv6Number(l).split(":").reduce(function(l,e){return l.concat(r.leftPadWithZeroBit(n.hexadecimalStringToBinaryString(e),16))},"")}},function(l,n){l.exports=function(l,n){l.prototype=Object.create(n.prototype),l.prototype.constructor=l,l.__proto__=n}},function(l,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=e(4),u=function(){function l(l){this.datatype=l}return l.prototype.readMandatory=function(l){},l.prototype.parseOptions=function(l){for(;;){var n=l.readU1();if(n===t.RcpTypes.TERMINATOR)break;if(!this.handleOption(n,l))throw new Error("TypeDefinition option not handled: "+n)}},l.prototype.writeMandatory=function(l){},l.prototype.write=function(l,n){l.push(this.datatype),this.writeMandatory(l),this.writeOptions(l,n),l.push(t.RcpTypes.TERMINATOR)},l.errorMessage={invalidDefaultValue:"Invalid defaultValue provided."},l}();n.TypeDefinition=u},function(l,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.IPNumType=void 0,function(l){l[l.ASN=0]="ASN",l[l.IPv4=1]="IPv4",l[l.IPv6=2]="IPv6"}(n.IPNumType||(n.IPNumType={}))},function(l,n,e){"use strict";var t=e(213),u="function"===typeof Symbol&&"symbol"===typeof Symbol("foo"),r=Object.prototype.toString,o=Array.prototype.concat,i=Object.defineProperty,a=i&&function(){var l={};try{for(var n in i(l,"x",{enumerable:!1,value:l}),l)return!1;return l.x===l}catch(e){return!1}}(),c=function(l,n,e,t){var u;n in l&&("function"!==typeof(u=t)||"[object Function]"!==r.call(u)||!t())||(a?i(l,n,{configurable:!0,enumerable:!1,value:e,writable:!0}):l[n]=e)},s=function(l,n){var e=arguments.length>2?arguments[2]:{},r=t(n);u&&(r=o.call(r,Object.getOwnPropertySymbols(n)));for(var i=0;i0)&&!(t=r.next()).done;)o.push(t.value)}catch(i){u={error:i}}finally{try{t&&!t.done&&(e=r.return)&&e.call(r)}finally{if(u)throw u.error}}return o};Object.defineProperty(n,"__esModule",{value:!0}),n.isIPv4=n.IPv6Mask=n.IPv4Mask=n.IPv6=n.Asn=n.IPv4=n.AbstractIPNum=void 0;var r=e(64),o=e(29),i=e(20),a=e(18),c=e(18),s=e(18),p=e(18),h=e(33),f=e(18),d=e(48),v=e(30),m=e(38),y=e(30),g=function(){function l(){}return l.prototype.getValue=function(){return this.value},l.prototype.toBinaryString=function(){return p.leftPadWithZeroBit(this.value.toString(2),this.bitSize)},l.prototype.hasNext=function(){return this.value.lesser(this.maximumBitSize)},l.prototype.hasPrevious=function(){return this.value.greater(i.zero)},l.prototype.isEquals=function(l){return this.value.equals(l.value)},l.prototype.isLessThan=function(l){return this.value.lt(l.value)},l.prototype.isGreaterThan=function(l){return this.value.gt(l.value)},l.prototype.isLessThanOrEquals=function(l){return this.value.lesserOrEquals(l.value)},l.prototype.isGreaterThanOrEquals=function(l){return this.value.greaterOrEquals(l.value)},l}();n.AbstractIPNum=g;var z=function(l){function n(n){var e=l.call(this)||this;if(e.bitSize=32,e.maximumBitSize=o.Validator.THIRTY_TWO_BIT_SIZE,e.type=h.IPNumType.IPv4,e.octets=[],e.separator=".","string"===typeof n){var t=u(e.constructFromDecimalDottedString(n),2),r=t[0],i=t[1];e.value=r,e.octets=i}else{var a=u(e.constructFromBigIntegerValue(n),2);r=a[0],i=a[1];e.value=r,e.octets=i}return e}return t(n,l),n.fromBigInteger=function(l){return new n(l)},n.fromDecimalDottedString=function(l){return new n(l)},n.fromString=function(l){return n.fromDecimalDottedString(l)},n.fromBinaryString=function(l){var e=o.Validator.isValidBinaryString(l);if(e[0])return new n(s.parseBinaryStringToBigInteger(l));throw Error(e[1].join(","))},n.prototype.toString=function(){return this.octets.map(function(l){return l.toString()}).join(this.separator)},n.prototype.getOctets=function(){return this.octets},n.prototype.nextIPNumber=function(){return n.fromBigInteger(this.getValue().add(1))},n.prototype.previousIPNumber=function(){return n.fromBigInteger(this.getValue().minus(1))},n.prototype.toIPv4MappedIPv6=function(){var l="1".repeat(16)+this.toBinaryString();return E.fromBinaryString(l)},n.prototype.constructFromDecimalDottedString=function(l){var n,e=u(o.Validator.isValidIPv4String(l),2),t=e[0],c=e[1];if(!t)throw new Error(c.filter(function(l){return""!==l}).toString());return n=l.split(".").map(function(l){return r.Octet.fromString(l)}),[i(a.dottedDecimalNotationToBinaryString(l),2),n]},n.prototype.constructFromBigIntegerValue=function(l){var n=u(o.Validator.isValidIPv4Number(l),2),e=n[0],t=n[1];if(!e)throw new Error(t.filter(function(l){return""!==l}).toString());var r=c.bigIntegerNumberToBinaryString(l);return[l,this.binaryStringToDecimalOctets(r)]},n.prototype.binaryStringToDecimalOctets=function(l){return l.length<32&&(l=p.leftPadWithZeroBit(l,32)),l.match(/.{1,8}/g).map(function(l){return r.Octet.fromString(s.parseBinaryStringToBigInteger(l).toString())})},n}(g);n.IPv4=z;var b=function(l){function n(e){var t=l.call(this)||this;if(t.bitSize=32,t.maximumBitSize=o.Validator.THIRTY_TWO_BIT_SIZE,t.type=h.IPNumType.ASN,"string"===typeof e)n.startWithASprefix(e)?t.value=i(parseInt(e.substring(2))):-1!=e.indexOf(".")?t.value=i(t.parseFromDotNotation(e)):t.value=i(parseInt(e));else{var r=i(e),a=u(o.Validator.isValidAsnNumber(r),2),c=a[0],s=a[1];if(!c)throw Error(s.filter(function(l){return""!==l}).toString());t.value=r}return t}return t(n,l),n.fromString=function(l){return new n(l)},n.fromNumber=function(l){return new n(l)},n.fromBinaryString=function(l){var e=o.Validator.isValidBinaryString(l);if(e[0])return new n(parseInt(l,2));throw Error(e[1].join(","))},n.prototype.toString=function(){var l=this.value.toString();return""+n.AS_PREFIX+l},n.prototype.toASPlain=function(){return this.value.toString()},n.prototype.toASDot=function(){return this.value.valueOf()>=65536?this.toASDotPlus():this.toASPlain()},n.prototype.toASDotPlus=function(){var l=Math.floor(this.value.valueOf()/65535);return l+"."+(this.value.valueOf()%65535-l)},n.prototype.toBinaryString=function(){return f.decimalNumberToBinaryString(this.value.valueOf())},n.prototype.is16Bit=function(){return u(o.Validator.isValid16BitAsnNumber(this.value),1)[0]},n.prototype.is32Bit=function(){return!this.is16Bit()},n.prototype.nextIPNumber=function(){return new n(this.value.valueOf()+1)},n.prototype.previousIPNumber=function(){return new n(this.value.valueOf()-1)},n.startWithASprefix=function(l){return 0===l.indexOf(n.AS_PREFIX)},n.prototype.parseFromDotNotation=function(l){var n=l.split("."),e=parseInt(n[0]);return 65535*e+(parseInt(n[1])+e)},n.AS_PREFIX="AS",n}(g);n.Asn=b;var E=function(l){function n(n){var e=l.call(this)||this;if(e.bitSize=128,e.maximumBitSize=o.Validator.ONE_HUNDRED_AND_TWENTY_EIGHT_BIT_SIZE,e.type=h.IPNumType.IPv6,e.hexadecatet=[],e.separator=":","string"===typeof n){var t=m.expandIPv6Number(n),r=u(e.constructFromHexadecimalDottedString(t),2),i=r[0],a=r[1];e.value=i,e.hexadecatet=a}else{var c=u(e.constructFromBigIntegerValue(n),2);i=c[0],a=c[1];e.value=i,e.hexadecatet=a}return e}return t(n,l),n.fromBigInteger=function(l){return new n(l)},n.fromHexadecimalString=function(l){return new n(l)},n.fromString=function(l){return n.fromHexadecimalString(l)},n.fromBinaryString=function(l){var e=o.Validator.isValidBinaryString(l);if(e[0]){var t=p.leftPadWithZeroBit(l,128);return new n(s.parseBinaryStringToBigInteger(t))}throw Error(e[1].join(","))},n.fromIPv4=function(l){return l.toIPv4MappedIPv6()},n.fromIPv4DotDecimalString=function(l){return new z(l).toIPv4MappedIPv6()},n.prototype.toString=function(){var l=this.hexadecatet.map(function(l){return l.toString()}).join(":");return this.hexadecatet.length<8?"::"+l:l},n.prototype.getHexadecatet=function(){return this.hexadecatet},n.prototype.nextIPNumber=function(){return n.fromBigInteger(this.getValue().add(1))},n.prototype.previousIPNumber=function(){return n.fromBigInteger(this.getValue().minus(1))},n.prototype.constructFromBigIntegerValue=function(l){var n=u(o.Validator.isValidIPv6Number(l),2),e=n[0],t=n[1];if(!e)throw new Error(t.filter(function(l){return""!==l}).toString());var r=c.bigIntegerNumberToBinaryString(l);return[l,this.binaryStringToHexadecatets(r)]},n.prototype.constructFromHexadecimalDottedString=function(l){var n=u(o.Validator.isValidIPv6String(l),2),e=n[0],t=n[1];if(!e)throw new Error(t.filter(function(l){return""!==l}).toString());var r=l.split(":").map(function(l){return d.Hexadecatet.fromString(l)});return[i(y.hexadectetNotationToBinaryString(l),2),r]},n.prototype.binaryStringToHexadecatets=function(l){for(var n=v.binaryStringToHexadecimalString(l);n.length%4!=0;)n="0"+n;return n.match(/.{1,4}/g).map(function(l){return d.Hexadecatet.fromString(l)})},n}(g);n.IPv6=E;var T=function(l){function n(n){var e,t,c,s=l.call(this,n)||this;if(s.octets=[],t=(e=u(o.Validator.isValidIPv4Mask(n),2))[0],c=e[1],!t)throw new Error(c.filter(function(l){return""!==l}).toString());var p=n.split(".");s.octets=p.map(function(l){return r.Octet.fromString(l)});var h=a.dottedDecimalNotationToBinaryString(n);return s.prefix=(h.match(/1/g)||[]).length,s.value=i(h,2),s}return t(n,l),n.fromDecimalDottedString=function(l){return new n(l)},n}(z);n.IPv4Mask=T;var M=function(l){function n(n){var e,t,r,a=l.call(this,n)||this;if(a.hexadecatet=[],t=(e=u(o.Validator.isValidIPv6Mask(n),2))[0],r=e[1],!t)throw new Error(r.filter(function(l){return""!==l}).toString());var c=n.split(":");a.hexadecatet=c.map(function(l){return d.Hexadecatet.fromString(l)});var s=y.hexadectetNotationToBinaryString(n);return a.prefix=(s.match(/1/g)||[]).length,a.value=i(s,2),a.value=i(y.hexadectetNotationToBinaryString(n),2),a}return t(n,l),n.fromHexadecimalString=function(l){return new n(l)},n}(E);n.IPv6Mask=M,n.isIPv4=function(l){return 32===l.bitSize}},function(l,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.collapseIPv6Number=n.expandIPv6Number=void 0;var t=e(18);n.expandIPv6Number=function(l){var n=function(l){return l.map(function(l){return t.leftPadWithZeroBit(l,4)}).join(":")};if(/(:){3,}/.test(l))throw"given IPv6 contains consecutive : more than two";if(l.includes("::")){var e=l.split("::"),u=e[0],r=e[1],o=u.split(":").filter(function(l){return""!==l}),i=r.split(":").filter(function(l){return""!==l}),a=function(l){for(var n=[],e=0;e0},n.prototype.writeValue=function(l,n){void 0!=n?l.push(n?1:0):this._defaultValue?l.push(this._defaultValue?1:0):l.push(0)},n.prototype.getDefaultId=function(){return r.RcpTypes.BooleanOptions.DEFAULT},n.prototype.getTypeDefault=function(){return!1},n.prototype.writeOptions=function(l,n){(n||this.changed.has(r.RcpTypes.BooleanOptions.DEFAULT))&&(l.push(r.RcpTypes.BooleanOptions.DEFAULT),this.writeValue(l,this._defaultValue)),n||this.changed.clear()},n.allOptions=(new Map).set(r.RcpTypes.BooleanOptions.DEFAULT,!0),n}(u.DefaultDefinition);n.BooleanDefinition=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(19),r=e(4),o=e(15),i=function(l){function n(){return l.call(this,r.RcpTypes.Datatype.ENUM)||this}return t(n,l),n.prototype.update=function(l){var e=!1;return l instanceof n&&(void 0!==l._defaultValue&&(this._defaultValue=l._defaultValue,e=!0),void 0!==l._entries&&(this._entries=l._entries,e=!0),void 0!==l._multiselect&&(this._multiselect=l._multiselect,e=!0)),e},n.prototype.handleOption=function(l,n){switch(l){case r.RcpTypes.EnumOptions.DEFAULT:return this._defaultValue=this.readValue(n),!0;case r.RcpTypes.EnumOptions.ENTRIES:for(this._entries=[];;){var e=new r.TinyString(n).data;if(0==e.length||""===e)break;this._entries.push(e)}return!0;case r.RcpTypes.EnumOptions.MULTISELECT:return this._multiselect=n.readU1()>0,!0}return!1},n.prototype.readValue=function(l){return new r.TinyString(l).data},n.prototype.writeValue=function(l,n){void 0!=n?o.writeTinyString(n,l):this._defaultValue?o.writeTinyString(this._defaultValue,l):o.writeTinyString("",l)},n.prototype.getDefaultId=function(){return r.RcpTypes.EnumOptions.DEFAULT},n.prototype.getTypeDefault=function(){return this._entries?this._entries[0]:""},n.prototype.writeOptions=function(l,e){var t=this,u=this.changed;e&&(u=n.allOptions),u.forEach(function(n,e){switch(e){case r.RcpTypes.EnumOptions.DEFAULT:l.push(r.RcpTypes.EnumOptions.DEFAULT),t.writeValue(l,t._defaultValue);break;case r.RcpTypes.EnumOptions.ENTRIES:if(l.push(r.RcpTypes.EnumOptions.ENTRIES),t._entries)for(var u=0;u-1)},Object.defineProperty(n.prototype,"entries",{get:function(){return this._entries},set:function(l){this._entries=l,this.changed.set(r.RcpTypes.EnumOptions.ENTRIES,!0),this.setDirty()},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"multiselect",{get:function(){return this._multiselect},set:function(l){this._multiselect!==l&&(this._multiselect=l,this.changed.set(r.RcpTypes.EnumOptions.MULTISELECT,!0),this.setDirty())},enumerable:!0,configurable:!0}),n.allOptions=(new Map).set(r.RcpTypes.EnumOptions.DEFAULT,!0).set(r.RcpTypes.EnumOptions.ENTRIES,!0).set(r.RcpTypes.EnumOptions.MULTISELECT,!0),n}(u.DefaultDefinition);n.EnumDefinition=i},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(32),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Datatype.GROUP)||this}return t(n,l),n.prototype.didChange=function(){return!1},n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n.prototype.update=function(l){return!1},n}(u.TypeDefinition);n.GroupDefinition=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(24),r=e(44),o=function(l){function n(n){return l.call(this,n,new r.BangDefinition)||this}return t(n,l),n.prototype.doBang=function(){this.setDirty()},n}(u.Parameter);n.BangParameter=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(32),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Datatype.BANG)||this}return t(n,l),n.prototype.didChange=function(){return!1},n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n.prototype.update=function(l){return!1},n}(u.TypeDefinition);n.BangDefinition=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(19),r=e(4),o=e(15);function i(l){return("0"+l.toString(16)).slice(-2)}function a(l){if(!l.startsWith("#"))throw new Error("not a valid color: "+l);for(;l.length<7;)l+="f";var n=l.slice(1,3),e=l.slice(3,5),t=l.slice(5,7);return parseInt(n,16)+(parseInt(e,16)<<8)+(parseInt(t,16)<<16)}function c(l){if(!l.startsWith("#"))throw new Error("not a valid color: "+l);for(;l.length<9;)l+="f";var n=l.slice(1,3),e=l.slice(3,5),t=l.slice(5,7),u=l.slice(7,9);return parseInt(n,16)+(parseInt(e,16)<<8)+(parseInt(t,16)<<16)+(parseInt(u,16)<<24)}var s=function(l){function n(){return null!==l&&l.apply(this,arguments)||this}return t(n,l),n.prototype.handleOption=function(l,n){switch(l){case r.RcpTypes.ColorOptions.DEFAULT:return this._defaultValue=this.readValue(n),!0}return!1},n.prototype.getDefaultId=function(){return r.RcpTypes.ColorOptions.DEFAULT},n.prototype.writeOptions=function(l,n){(n||this.changed.has(r.RcpTypes.ColorOptions.DEFAULT))&&(l.push(r.RcpTypes.ColorOptions.DEFAULT),this.writeValue(l,this._defaultValue)),n||this.changed.clear()},n.allOptions=(new Map).set(r.RcpTypes.ColorOptions.DEFAULT,!0),n}(u.DefaultDefinition),p=function(l){function n(){return l.call(this,r.RcpTypes.Datatype.RGBA)||this}return t(n,l),n.prototype.getTypeDefault=function(){return"#00000000"},n.prototype.update=function(l){var e=!1;return l instanceof n&&void 0!==l._defaultValue&&(this._defaultValue=l._defaultValue,e=!0),e},n.prototype.readValue=function(l){return function(l){var n=l>>8&255,e=l>>16&255,t=l>>24&255;return"#"+i(255&l)+i(n)+i(e)+i(t)}(l.readU4be())},n.prototype.writeValue=function(l,n){void 0!=n?o.pushIn32ToArrayBe(c(n),l):this._defaultValue?o.pushIn32ToArrayBe(c(this._defaultValue),l):o.pushIn32ToArrayBe(0,l)},n}(s);n.RGBADefinition=p;var h=function(l){function n(){return l.call(this,r.RcpTypes.Datatype.RGB)||this}return t(n,l),n.prototype.getTypeDefault=function(){return"#000000"},n.prototype.update=function(l){var e=!1;return l instanceof n&&void 0!==l._defaultValue&&(this._defaultValue=l._defaultValue,e=!0),e},n.prototype.readValue=function(l){return function(l){var n=l>>8&255,e=l>>16&255;return"#"+i(255&l)+i(n)+i(e)}(l.readU4be())},n.prototype.writeValue=function(l,n){void 0!=n?o.pushIn32ToArrayBe(a(n),l):this._defaultValue?o.pushIn32ToArrayBe(a(this._defaultValue),l):o.pushIn32ToArrayBe(0,l)},n}(s);n.RGBDefinition=h},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(19),r=e(4),o=e(15),i=function(l){function n(){return l.call(this,r.RcpTypes.Datatype.URI)||this}return t(n,l),n.prototype.update=function(l){var e=!1;return l instanceof n&&(void 0!==l._defaultValue&&(this._defaultValue=l._defaultValue,e=!0),void 0!==l._filter&&(this._filter=l._filter,e=!0),void 0!==l._schema&&(this._schema=l._schema,e=!0)),e},n.prototype.handleOption=function(l,n){switch(l){case r.RcpTypes.UriOptions.DEFAULT:return this._defaultValue=new r.LongString(n).data,!0;case r.RcpTypes.UriOptions.FILTER:return this._filter=new r.TinyString(n).data,!0;case r.RcpTypes.UriOptions.SCHEMA:return this._schema=new r.TinyString(n).data,!0}return!1},n.prototype.readValue=function(l){return new r.LongString(l).data},n.prototype.writeValue=function(l,n){void 0!=n?o.writeLongString(n,l):this._defaultValue?o.writeLongString(this._defaultValue,l):o.writeLongString("",l)},n.prototype.getDefaultId=function(){return r.RcpTypes.UriOptions.DEFAULT},n.prototype.getTypeDefault=function(){return""},n.prototype.writeOptions=function(l,e){var t=this,u=this.changed;e&&(u=n.allOptions),u.forEach(function(n,e){switch(e){case r.RcpTypes.UriOptions.DEFAULT:l.push(r.RcpTypes.UriOptions.DEFAULT),t.writeValue(l,t._defaultValue);break;case r.RcpTypes.UriOptions.FILTER:l.push(r.RcpTypes.UriOptions.FILTER),t._filter?o.writeTinyString(t._filter,l):o.writeTinyString("",l);break;case r.RcpTypes.UriOptions.SCHEMA:l.push(r.RcpTypes.UriOptions.SCHEMA),t._schema?o.writeTinyString(t._schema,l):o.writeTinyString("",l)}}),e||this.changed.clear()},Object.defineProperty(n.prototype,"filter",{get:function(){return this._filter},set:function(l){this._filter!==l&&(this._filter=l,this.changed.set(r.RcpTypes.UriOptions.FILTER,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"schema",{get:function(){return this._schema},set:function(l){this._schema!==l&&(this._schema=l,this.changed.set(r.RcpTypes.UriOptions.SCHEMA,!0),this.setDirty())},enumerable:!0,configurable:!0}),n.allOptions=(new Map).set(r.RcpTypes.UriOptions.DEFAULT,!0).set(r.RcpTypes.UriOptions.FILTER,!0).set(r.RcpTypes.UriOptions.SCHEMA,!0),n}(u.DefaultDefinition);n.UriDefinition=i},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(19),r=e(4),o=e(15),i=e(63),a=e(20),c=function(l){function n(){return l.call(this,r.RcpTypes.Datatype.IPV4)||this}return t(n,l),n.prototype.update=function(l){var e=!1;return l instanceof n&&void 0!==l._defaultValue&&(this._defaultValue=l._defaultValue,e=!0),e},n.prototype.handleOption=function(l,n){switch(l){case r.RcpTypes.Ipv4Options.DEFAULT:return this._defaultValue=this.readValue(n),!0}return!1},n.prototype.readValue=function(l){return new i.IPv4(a(l.readU4be()))},n.prototype.writeValue=function(l,n){void 0!=n?o.pushIn32ToArrayBe(n.value.toJSNumber(),l):this._defaultValue?o.pushIn32ToArrayBe(this._defaultValue.value.toJSNumber(),l):o.pushFloat32ToArrayBe(0,l)},n.prototype.getDefaultId=function(){return r.RcpTypes.Ipv4Options.DEFAULT},n.prototype.getTypeDefault=function(){return new i.IPv4(a(0))},n.prototype.writeOptions=function(l,n){(n||this.changed.has(r.RcpTypes.Ipv4Options.DEFAULT))&&(l.push(r.RcpTypes.Ipv4Options.DEFAULT),this.writeValue(l,this._defaultValue)),n||this.changed.clear()},n.allOptions=(new Map).set(r.RcpTypes.Ipv4Options.DEFAULT,!0),n}(u.DefaultDefinition);n.IPv4Definition=c},function(l,n,e){"use strict";var t=this&&this.__read||function(l,n){var e="function"===typeof Symbol&&l[Symbol.iterator];if(!e)return l;var t,u,r=e.call(l),o=[];try{for(;(void 0===n||n-- >0)&&!(t=r.next()).done;)o.push(t.value)}catch(i){u={error:i}}finally{try{t&&!t.done&&(e=r.return)&&e.call(r)}finally{if(u)throw u.error}}return o};Object.defineProperty(n,"__esModule",{value:!0}),n.Hexadecatet=void 0;var u=e(29),r=e(20),o=function(){function l(l){var n;n="string"===typeof l?parseInt(l,16):parseInt(String(l),16);var e=t(u.Validator.isValidIPv6Hexadecatet(r(n)),2),o=e[0],i=e[1];if(!o)throw Error(i.filter(function(l){return""!==l}).toString());this.value=n}return l.fromString=function(n){return new l(n)},l.fromNumber=function(n){return new l(n)},l.prototype.getValue=function(){return this.value},l.prototype.toString=function(){return this.value.toString(16)},l}();n.Hexadecatet=o},function(l,n,e){"use strict";var t=this&&this.__read||function(l,n){var e="function"===typeof Symbol&&l[Symbol.iterator];if(!e)return l;var t,u,r=e.call(l),o=[];try{for(;(void 0===n||n-- >0)&&!(t=r.next()).done;)o.push(t.value)}catch(i){u={error:i}}finally{try{t&&!t.done&&(e=r.return)&&e.call(r)}finally{if(u)throw u.error}}return o};Object.defineProperty(n,"__esModule",{value:!0}),n.isIPv4Prefix=n.IPv6Prefix=n.IPv4Prefix=void 0;var u=e(29),r=e(37),o=e(18),i=e(33),a=e(30),c=e(48),s=e(20),p=function(){function l(l){var n,e,r;if(this.type="IPv4",this.bitValue=s(32),e=(n=t(u.Validator.isValidPrefixValue(l,i.IPNumType.IPv4),2))[0],r=n[1],!e)throw new Error(r.filter(function(l){return""!==l}).toString());this.value=l}return l.fromNumber=function(n){return new l(n)},l.fromRangeSize=function(n){var e=n.equals(s.one)?32:32-f(n,u.Validator.IPV4_SIZE);return l.fromNumber(e)},l.prototype.getValue=function(){return this.value},l.prototype.toString=function(){return this.value.toString()},l.prototype.toMask=function(){var l="1".repeat(this.value),n="0".repeat(32-this.value);return r.IPv4Mask.fromDecimalDottedString(this.toDecimalNotation(""+l+n))},l.prototype.toRangeSize=function(){return s(2).pow(this.bitValue.minus(s(this.getValue())))},l.prototype.merge=function(){return new l(this.value-1)},l.prototype.split=function(){return new l(this.value+1)},l.prototype.toDecimalNotation=function(l){return o.parseBinaryStringToBigInteger(l.substr(0,8))+"."+o.parseBinaryStringToBigInteger(l.substr(8,8))+"."+o.parseBinaryStringToBigInteger(l.substr(16,8))+"."+o.parseBinaryStringToBigInteger(l.substr(24,8))},l}();n.IPv4Prefix=p;var h=function(){function l(l){var n,e,r;if(this.type="IPv6",this.bitValue=s(128),e=(n=t(u.Validator.isValidPrefixValue(l,i.IPNumType.IPv6),2))[0],r=n[1],!e)throw new Error(r.filter(function(l){return""!==l}).toString());this.value=l}return l.fromNumber=function(n){return new l(n)},l.fromRangeSize=function(n){var e=n.equals(s.one)?128:128-f(n,u.Validator.IPV6_SIZE);return l.fromNumber(e)},l.prototype.getValue=function(){return this.value},l.prototype.toString=function(){return this.value.toString()},l.prototype.toMask=function(){var l="1".repeat(this.value),n="0".repeat(128-this.value);return r.IPv6Mask.fromHexadecimalString(this.toHexadecatetNotation(""+l+n))},l.prototype.toRangeSize=function(){return s(2).pow(this.bitValue.minus(s(this.getValue())))},l.prototype.merge=function(){return new l(this.value-1)},l.prototype.split=function(){return new l(this.value+1)},l.prototype.toHexadecatetNotation=function(l){return l.match(/.{1,16}/g).map(function(l){return c.Hexadecatet.fromString(a.binaryStringToHexadecimalString(l))}).map(function(l){return l.toString()}).join(":")},l}();function f(l,n){var e=n.greater(u.Validator.IPV4_SIZE)?"IPv6":"IPv4";if(l.greater(n)||l.equals(s(0)))throw new Error(u.Validator.invalidIPRangeSizeMessage.replace("$iptype",e));try{return o.intLog2(l)}catch(t){throw new Error(u.Validator.invalidIPRangeSizeForCidrMessage)}}n.IPv6Prefix=h,n.isIPv4Prefix=function(l){return"IPv4"===l.type}},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(28),r=e(4),o=e(15),i=function(l){function n(){return l.call(this,r.RcpTypes.Datatype.INT32)||this}return t(n,l),n.prototype.typeMax=function(){return 2147483647},n.prototype.typeMin=function(){return-2147483648},n.prototype.readValue=function(l){return l.readS4be()},n.prototype.writeValue=function(l,n){void 0!=n?o.pushIn32ToArrayBe(n,l):this._defaultValue?o.pushIn32ToArrayBe(this._defaultValue,l):o.pushIn32ToArrayBe(0,l)},n}(u.NumberDefinition);n.Int32Definition=i},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(19),r=e(15),o=e(4),i=function(l){function n(){return l.call(this,o.RcpTypes.Datatype.STRING)||this}return t(n,l),n.prototype.update=function(l){var e=!1;return l instanceof n&&(void 0!==l._defaultValue&&(this._defaultValue=l._defaultValue,e=!0),void 0!==l._regex&&(this._regex=l._regex,e=!0)),e},n.prototype.handleOption=function(l,n){switch(l){case o.RcpTypes.StringOptions.DEFAULT:return this._defaultValue=this.readValue(n),!0;case o.RcpTypes.StringOptions.REGULAR_EXPRESSION:return this._regex=this.readValue(n),!0}return!1},n.prototype.readValue=function(l){return new o.LongString(l).data},n.prototype.writeValue=function(l,n){void 0!=n?r.writeLongString(n,l):this._defaultValue?r.writeLongString(this._defaultValue,l):r.writeLongString("",l)},n.prototype.getDefaultId=function(){return o.RcpTypes.StringOptions.DEFAULT},n.prototype.getTypeDefault=function(){return""},n.prototype.writeOptions=function(l,e){var t=this,u=this.changed;e&&(u=n.allOptions),u.forEach(function(n,e){switch(e){case o.RcpTypes.StringOptions.DEFAULT:l.push(o.RcpTypes.StringOptions.DEFAULT),t.writeValue(l,t._defaultValue);break;case o.RcpTypes.StringOptions.REGULAR_EXPRESSION:l.push(o.RcpTypes.StringOptions.REGULAR_EXPRESSION),t._regex?r.writeLongString(t._regex,l):r.writeLongString("",l)}}),e||this.changed.clear()},Object.defineProperty(n.prototype,"regex",{get:function(){return this._regex},set:function(l){this._regex!==l&&(this._regex=l,this.changed.set(o.RcpTypes.StringOptions.REGULAR_EXPRESSION,!0),this.setDirty())},enumerable:!0,configurable:!0}),n.allOptions=(new Map).set(o.RcpTypes.StringOptions.DEFAULT,!0).set(o.RcpTypes.StringOptions.REGULAR_EXPRESSION,!0),n}(u.DefaultDefinition);n.StringDefinition=i},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(15),r=e(4),o=e(25),i=function(l){function n(){return null!==l&&l.apply(this,arguments)||this}return t(n,l),n.prototype.constrainValue=function(l){return void 0!==this.maximum&&(l.x>this.maximum.x&&(l.x=this.maximum.x),l.y>this.maximum.y&&(l.y=this.maximum.y),l.z>this.maximum.z&&(l.z=this.maximum.z)),void 0!==this.minimum&&(l.xthis.maximum.x&&(l.x=this.maximum.x),l.y>this.maximum.y&&(l.y=this.maximum.y)),void 0!==this.minimum&&(l.xthis.maximum.x&&(l.x=this.maximum.x),l.y>this.maximum.y&&(l.y=this.maximum.y),l.z>this.maximum.z&&(l.z=this.maximum.z),l.t>this.maximum.t&&(l.t=this.maximum.t)),void 0!==this.minimum&&(l.x0},l.prototype.connect_=function(){t&&!this.connected_&&(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),c?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},l.prototype.disconnect_=function(){t&&this.connected_&&(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},l.prototype.onTransitionEnd_=function(l){var n=l.propertyName,e=void 0===n?"":n;a.some(function(l){return!!~e.indexOf(l)})&&this.refresh()},l.getInstance=function(){return this.instance_||(this.instance_=new l),this.instance_},l.instance_=null,l}(),p=function(l,n){for(var e=0,t=Object.keys(n);e0},l}(),M="undefined"!==typeof WeakMap?new WeakMap:new e,_=function(){return function l(n){if(!(this instanceof l))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var e=s.getInstance(),t=new T(n,e,this);M.set(this,t)}}();["observe","unobserve","disconnect"].forEach(function(l){_.prototype[l]=function(){var n;return(n=M.get(this))[l].apply(n,arguments)}});var O="undefined"!==typeof u.ResizeObserver?u.ResizeObserver:_;n.default=O}.call(this,e(23))},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(17),r=e(41),o=function(l){function n(n){var e=l.call(this,n,new r.EnumDefinition)||this;return e.enumDefinition=e.typeDefinition,e}return t(n,l),n.prototype.setStringValue=function(l){return!!this.enumDefinition.contains(l)&&(this.value=l,!0)},n}(u.ValueParameter);n.EnumParameter=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(24),r=e(42),o=function(l){function n(n){var e=l.call(this,n,new r.GroupDefinition)||this;return e.children=[],e}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.addChild=function(l){void 0!==l&&(this.children.indexOf(l)>=0||this.children.push(l))},n.prototype.removeChild=function(l){if(void 0!==l){var n=this.children.indexOf(l);n>=0&&this.children.splice(n,1)}},n}(u.Parameter);n.GroupParameter=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(17),r=e(45),o=function(l){function n(n){return l.call(this,n,new r.RGBADefinition)||this}return t(n,l),n.prototype.setStringValue=function(l){return!!l.startsWith("#")&&(this.value=l,!0)},n}(u.ValueParameter);n.RGBAParameter=o;var i=function(l){function n(n){return l.call(this,n,new r.RGBDefinition)||this}return t(n,l),n.prototype.setStringValue=function(l){return!!l.startsWith("#")&&(this.value=l,!0)},n}(u.ValueParameter);n.RGBParameter=i},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(17),r=e(46),o=function(l){function n(n){return l.call(this,n,new r.UriDefinition)||this}return t(n,l),n.prototype.setStringValue=function(l){return this.value=l,!0},n}(u.ValueParameter);n.UriParameter=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(17),r=e(47),o=function(l){function n(n){return l.call(this,n,new r.IPv4Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){return!1},n}(u.ValueParameter);n.IPv4Parameter=o},function(l,n,e){"use strict";var t=this&&this.__createBinding||(Object.create?function(l,n,e,t){void 0===t&&(t=e),Object.defineProperty(l,t,{enumerable:!0,get:function(){return n[e]}})}:function(l,n,e,t){void 0===t&&(t=e),l[t]=n[e]}),u=this&&this.__exportStar||function(l,n){for(var e in l)"default"===e||n.hasOwnProperty(e)||t(n,l,e)};Object.defineProperty(n,"__esModule",{value:!0}),u(e(189),n),u(e(37),n),u(e(18),n),u(e(48),n),u(e(30),n),u(e(33),n),u(e(38),n),u(e(64),n),u(e(49),n),u(e(29),n)},function(l,n,e){"use strict";var t=this&&this.__read||function(l,n){var e="function"===typeof Symbol&&l[Symbol.iterator];if(!e)return l;var t,u,r=e.call(l),o=[];try{for(;(void 0===n||n-- >0)&&!(t=r.next()).done;)o.push(t.value)}catch(i){u={error:i}}finally{try{t&&!t.done&&(e=r.return)&&e.call(r)}finally{if(u)throw u.error}}return o};Object.defineProperty(n,"__esModule",{value:!0}),n.Octet=void 0;var u=e(29),r=e(20),o=function(){function l(l){var n;n="string"===typeof l?parseInt(l):l;var e=t(u.Validator.isValidIPv4Octet(r(n)),2),o=e[0],i=e[1];if(!o)throw Error(i.filter(function(l){return""!==l}).toString());this.value=n}return l.fromString=function(n){return new l(n)},l.fromNumber=function(n){return new l(n)},l.prototype.getValue=function(){return this.value},l.prototype.toString=function(){return this.value.toString(10)},l}();n.Octet=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(17),r=e(66),o=e(4),i=function(l){function n(n){return l.call(this,n,new r.RangeDefinition)||this}return t(n,l),n.prototype.valueConstrained=function(){return this.typeDefinition.constrainValue(this.value)},n.prototype.setStringValue=function(l){return!1},Object.defineProperty(n.prototype,"value1",{get:function(){if(this._value)return this._value.value1},set:function(l){l&&(this._value||(this._value=new r.Range(0,0)),this._value.value1!==l&&(this._value.value1=l,this.changed.set(o.RcpTypes.ParameterOptions.VALUE,!0),this.setDirty()))},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"value2",{get:function(){if(this._value)return this._value.value2},set:function(l){l&&(this._value||(this._value=new r.Range(0,0)),this._value.value2!==l&&(this._value.value2=l,this.changed.set(o.RcpTypes.ParameterOptions.VALUE,!0),this.setDirty()))},enumerable:!0,configurable:!0}),n}(u.ValueParameter);n.RangeParameter=i},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(19),r=e(4),o=e(28),i=e(191),a=function(){return function(l,n){this.value1=l,this.value2=n}}();n.Range=a;var c=function(l){function n(){return l.call(this,r.RcpTypes.Datatype.RANGE)||this}return t(n,l),n.prototype.update=function(l){var e=!1;return l instanceof n&&(l.elementType&&this.elementType&&(e=this.elementType.update(l.elementType)),void 0!==l._defaultValue&&(this._defaultValue=l._defaultValue,e=!0)),e},n.prototype.constrainValue=function(l){return this.elementType&&(l.value1=this.elementType.constrainValue(l.value1),l.value2=this.elementType.constrainValue(l.value2)),l},n.prototype.didChange=function(){var l=!1;return this.elementType&&(l=this.elementType.didChange()),this.changed.size>0||l},n.prototype.readMandatory=function(l){var n=i.createTypeDefinition(l.readU1());if(n.readMandatory(l),!(n instanceof o.NumberDefinition))throw Error("RangeDefinition: wrong element type: "+n.datatype);this.elementType=n},n.prototype.parseOptions=function(n){if(!this.elementType)throw new Error("cannot parse elementType options without elementType");this.elementType.parseOptions(n),l.prototype.parseOptions.call(this,n)},n.prototype.handleOption=function(l,n){switch(l){case r.RcpTypes.BooleanOptions.DEFAULT:return this._defaultValue=this.readValue(n),!0}return!1},n.prototype.readValue=function(l){if(!this.elementType)throw new Error("could not read from elementType");return new a(this.elementType.readValue(l),this.elementType.readValue(l))},n.prototype.writeValue=function(l,n){if(!this.elementType)throw new Error("could not write value without elementType");void 0!=n?(this.elementType.writeValue(l,n.value1),this.elementType.writeValue(l,n.value2)):this._defaultValue?(this.elementType.writeValue(l,this._defaultValue.value1),this.elementType.writeValue(l,this._defaultValue.value2)):(this.elementType.writeValue(l,void 0),this.elementType.writeValue(l,void 0))},n.prototype.getDefaultId=function(){return r.RcpTypes.RangeOptions.DEFAULT},n.prototype.getTypeDefault=function(){return new a(0,0)},n.prototype.writeMandatory=function(l){if(!this.elementType)throw new Error("RangeDefinition without elementType!");l.push(this.elementType.datatype),this.elementType.writeMandatory(l)},n.prototype.writeOptions=function(l,n){if(!this.elementType)throw new Error("RangeDefinition without elementType!");this.elementType.writeOptions(l,n),l.push(r.RcpTypes.TERMINATOR),(n||this.changed.has(r.RcpTypes.RangeOptions.DEFAULT))&&(l.push(r.RcpTypes.RangeOptions.DEFAULT),this.writeValue(l,this._defaultValue)),n||this.changed.clear()},n}(u.DefaultDefinition);n.RangeDefinition=c},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(19),r=e(4),o=e(63),i=e(20),a=function(l){function n(){return l.call(this,r.RcpTypes.Datatype.IPV6)||this}return t(n,l),n.prototype.update=function(l){var e=!1;return l instanceof n&&void 0!==l._defaultValue&&(this._defaultValue=l._defaultValue,e=!0),e},n.prototype.handleOption=function(l,n){switch(l){case r.RcpTypes.Ipv6Options.DEFAULT:return this._defaultValue=this.readValue(n),!0}return!1},n.prototype.readValue=function(l){throw l.readS4be(),new Error("Method not implemented.")},n.prototype.writeValue=function(l,n){throw new Error("Method not implemented.")},n.prototype.getDefaultId=function(){return r.RcpTypes.Ipv6Options.DEFAULT},n.prototype.getTypeDefault=function(){return new o.IPv6(i(0))},n.prototype.writeOptions=function(l,n){throw new Error("Method not implemented.")},n.allOptions=(new Map).set(r.RcpTypes.Ipv6Options.DEFAULT,!0),n}(u.DefaultDefinition);n.IPv6Definition=a},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(17),r=e(51),o=function(l){function n(n){return l.call(this,n,new r.StringDefinition)||this}return t(n,l),n.prototype.setStringValue=function(l){return this.value=l,!0},n}(u.ValueParameter);n.StringParameter=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(28),r=e(50),o=function(l){function n(n,e){var t=l.call(this,n,e)||this;return t.numberDefinition=e,t}return t(n,l),n.prototype.valueConstrained=function(){return this.numberDefinition.constrainValue(this.value)},n}(e(17).ValueParameter);n.NumberParameter=o;var i=function(l){function n(n){return l.call(this,n,new u.Int8Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=parseInt(l);return!isNaN(n)&&(this.value=n,!0)},n}(o);n.Int8Parameter=i;var a=function(l){function n(n){return l.call(this,n,new u.Int16Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=parseInt(l);return!isNaN(n)&&(this.value=n,!0)},n}(o);n.Int16Parameter=a;var c=function(l){function n(n){return l.call(this,n,new r.Int32Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=parseInt(l);return!isNaN(n)&&(this.value=n,!0)},n}(o);n.Int32Parameter=c;var s=function(l){function n(n){return l.call(this,n,new u.Int64Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=parseInt(l);return!isNaN(n)&&(this.value=n,!0)},n}(o);n.Int64Parameter=s;var p=function(l){function n(n){return l.call(this,n,new u.Float32Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=parseFloat(l);return!isNaN(n)&&(this.value=n,!0)},n}(o);n.Float32Parameter=p;var h=function(l){function n(n){return l.call(this,n,new u.Float64Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=parseFloat(l);return!isNaN(n)&&(this.value=n,!0)},n}(o);n.Float64Parameter=h},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(17),r=e(52),o=e(25),i=function(l){function n(n,e){var t=l.call(this,n,e)||this;return t.vectorDefinition=e,t}return t(n,l),n.prototype.valueConstrained=function(){return this.vectorDefinition.constrainValue(this.value)},n}(u.ValueParameter);n.Vector3ParameterBase=i;var a=function(l){function n(n){return l.call(this,n,new r.Vector3F32Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=l.split(",");if(n.length<3)return!1;var e=parseFloat(n[0]);if(isNaN(e))return!1;var t=parseFloat(n[1]);if(isNaN(t))return!1;var u=parseFloat(n[2]);return!isNaN(u)&&(this.value=new o.Vector3(e,t,u),!0)},n}(i);n.Vector3F32Parameter=a;var c=function(l){function n(n){return l.call(this,n,new r.Vector3I32Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=l.split(",");if(n.length<3)return!1;var e=parseInt(n[0]);if(isNaN(e))return!1;var t=parseInt(n[1]);if(isNaN(t))return!1;var u=parseInt(n[2]);return!isNaN(u)&&(this.value=new o.Vector3(e,t,u),!0)},n}(i);n.Vector3I32Parameter=c},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(17),r=e(192),o=function(l){function n(n){return l.call(this,n,new r.ImageDefinition)||this}return t(n,l),n.prototype.setStringValue=function(l){return!1},n}(u.ValueParameter);n.ImageParameter=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(17),r=e(25),o=e(53),i=function(l){function n(n,e){var t=l.call(this,n,e)||this;return t.vectorDefinition=e,t}return t(n,l),n.prototype.valueConstrained=function(){return this.vectorDefinition.constrainValue(this.value)},n}(u.ValueParameter);n.Vector2ParameterBase=i;var a=function(l){function n(n){return l.call(this,n,new o.Vector2F32Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=l.split(",");if(n.length<2)return!1;var e=parseFloat(n[0]);if(isNaN(e))return!1;var t=parseFloat(n[1]);return!isNaN(t)&&(this.value=new r.Vector2(e,t),!0)},n}(i);n.Vector2F32Parameter=a;var c=function(l){function n(n){return l.call(this,n,new o.Vector2I32Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=l.split(",");if(n.length<2)return!1;var e=parseInt(n[0]);if(isNaN(e))return!1;var t=parseInt(n[1]);return!isNaN(t)&&(this.value=new r.Vector2(e,t),!0)},n}(i);n.Vector2I32Parameter=c},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(17),r=e(25),o=e(54),i=function(l){function n(n,e){var t=l.call(this,n,e)||this;return t.vectorDefinition=e,t}return t(n,l),n.prototype.valueConstrained=function(){return this.vectorDefinition.constrainValue(this.value)},n}(u.ValueParameter);n.Vector4ParameterBase=i;var a=function(l){function n(n){return l.call(this,n,new o.Vector4F32Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=l.split(",");if(n.length<4)return!1;var e=parseFloat(n[0]);if(isNaN(e))return!1;var t=parseFloat(n[1]);if(isNaN(t))return!1;var u=parseFloat(n[2]);if(isNaN(u))return!1;var o=parseFloat(n[3]);return!isNaN(o)&&(this.value=new r.Vector4(e,t,u,o),!0)},n}(i);n.Vector4F32Parameter=a;var c=function(l){function n(n){return l.call(this,n,new o.Vector4I32Definition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=l.split(",");if(n.length<4)return!1;var e=parseInt(n[0]);if(isNaN(e))return!1;var t=parseInt(n[1]);if(isNaN(t))return!1;var u=parseInt(n[2]);if(isNaN(u))return!1;var o=parseInt(n[3]);return!isNaN(o)&&(this.value=new r.Vector4(e,t,u,o),!0)},n}(i);n.Vector4I32Parameter=c},function(l,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=e(15),u=function(){function l(l){this.id=l}return l.prototype.write=function(l,n){t.pushIn16ToArrayBe(this.id,l)},l}();n.IdData=u,n.parseIdData=function(l){var n=l.readS2be();return new u(n)}},function(l,n){var e,t,u=l.exports={};function r(){throw new Error("setTimeout has not been defined")}function o(){throw new Error("clearTimeout has not been defined")}function i(l){if(e===setTimeout)return setTimeout(l,0);if((e===r||!e)&&setTimeout)return e=setTimeout,setTimeout(l,0);try{return e(l,0)}catch(n){try{return e.call(null,l,0)}catch(n){return e.call(this,l,0)}}}!function(){try{e="function"===typeof setTimeout?setTimeout:r}catch(l){e=r}try{t="function"===typeof clearTimeout?clearTimeout:o}catch(l){t=o}}();var a,c=[],s=!1,p=-1;function h(){s&&a&&(s=!1,a.length?c=a.concat(c):p=-1,c.length&&f())}function f(){if(!s){var l=i(h);s=!0;for(var n=c.length;n;){for(a=c,c=[];++p1)for(var e=1;e=0)return 1;return 0}();var u=e&&window.Promise?function(l){var n=!1;return function(){n||(n=!0,window.Promise.resolve().then(function(){n=!1,l()}))}}:function(l){var n=!1;return function(){n||(n=!0,setTimeout(function(){n=!1,l()},t))}};function r(l){return l&&"[object Function]"==={}.toString.call(l)}function o(l,n){if(1!==l.nodeType)return[];var e=l.ownerDocument.defaultView.getComputedStyle(l,null);return n?e[n]:e}function i(l){return"HTML"===l.nodeName?l:l.parentNode||l.host}function a(l){if(!l)return document.body;switch(l.nodeName){case"HTML":case"BODY":return l.ownerDocument.body;case"#document":return l.body}var n=o(l),e=n.overflow,t=n.overflowX,u=n.overflowY;return/(auto|scroll|overlay)/.test(e+u+t)?l:a(i(l))}function c(l){return l&&l.referenceNode?l.referenceNode:l}var s=e&&!(!window.MSInputMethodContext||!document.documentMode),p=e&&/MSIE 10/.test(navigator.userAgent);function h(l){return 11===l?s:10===l?p:s||p}function f(l){if(!l)return document.documentElement;for(var n=h(10)?document.body:null,e=l.offsetParent||null;e===n&&l.nextElementSibling;)e=(l=l.nextElementSibling).offsetParent;var t=e&&e.nodeName;return t&&"BODY"!==t&&"HTML"!==t?-1!==["TH","TD","TABLE"].indexOf(e.nodeName)&&"static"===o(e,"position")?f(e):e:l?l.ownerDocument.documentElement:document.documentElement}function d(l){return null!==l.parentNode?d(l.parentNode):l}function v(l,n){if(!l||!l.nodeType||!n||!n.nodeType)return document.documentElement;var e=l.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING,t=e?l:n,u=e?n:l,r=document.createRange();r.setStart(t,0),r.setEnd(u,0);var o=r.commonAncestorContainer;if(l!==o&&n!==o||t.contains(u))return function(l){var n=l.nodeName;return"BODY"!==n&&("HTML"===n||f(l.firstElementChild)===l)}(o)?o:f(o);var i=d(l);return i.host?v(i.host,n):v(l,d(n).host)}function m(l){var n="top"===(arguments.length>1&&void 0!==arguments[1]?arguments[1]:"top")?"scrollTop":"scrollLeft",e=l.nodeName;if("BODY"===e||"HTML"===e){var t=l.ownerDocument.documentElement;return(l.ownerDocument.scrollingElement||t)[n]}return l[n]}function y(l,n){var e="x"===n?"Left":"Top",t="Left"===e?"Right":"Bottom";return parseFloat(l["border"+e+"Width"])+parseFloat(l["border"+t+"Width"])}function g(l,n,e,t){return Math.max(n["offset"+l],n["scroll"+l],e["client"+l],e["offset"+l],e["scroll"+l],h(10)?parseInt(e["offset"+l])+parseInt(t["margin"+("Height"===l?"Top":"Left")])+parseInt(t["margin"+("Height"===l?"Bottom":"Right")]):0)}function z(l){var n=l.body,e=l.documentElement,t=h(10)&&getComputedStyle(e);return{height:g("Height",n,e,t),width:g("Width",n,e,t)}}var b=function(l,n){if(!(l instanceof n))throw new TypeError("Cannot call a class as a function")},E=function(){function l(l,n){for(var e=0;e2&&void 0!==arguments[2]&&arguments[2],t=h(10),u="HTML"===n.nodeName,r=O(l),i=O(n),c=a(l),s=o(n),p=parseFloat(s.borderTopWidth),f=parseFloat(s.borderLeftWidth);e&&u&&(i.top=Math.max(i.top,0),i.left=Math.max(i.left,0));var d=_({top:r.top-i.top-p,left:r.left-i.left-f,width:r.width,height:r.height});if(d.marginTop=0,d.marginLeft=0,!t&&u){var v=parseFloat(s.marginTop),y=parseFloat(s.marginLeft);d.top-=p-v,d.bottom-=p-v,d.left-=f-y,d.right-=f-y,d.marginTop=v,d.marginLeft=y}return(t&&!e?n.contains(c):n===c&&"BODY"!==c.nodeName)&&(d=function(l,n){var e=arguments.length>2&&void 0!==arguments[2]&&arguments[2],t=m(n,"top"),u=m(n,"left"),r=e?-1:1;return l.top+=t*r,l.bottom+=t*r,l.left+=u*r,l.right+=u*r,l}(d,n)),d}function V(l){if(!l||!l.parentElement||h())return document.documentElement;for(var n=l.parentElement;n&&"none"===o(n,"transform");)n=n.parentElement;return n||document.documentElement}function L(l,n,e,t){var u=arguments.length>4&&void 0!==arguments[4]&&arguments[4],r={top:0,left:0},s=u?V(l):v(l,c(n));if("viewport"===t)r=function(l){var n=arguments.length>1&&void 0!==arguments[1]&&arguments[1],e=l.ownerDocument.documentElement,t=w(l,e),u=Math.max(e.clientWidth,window.innerWidth||0),r=Math.max(e.clientHeight,window.innerHeight||0),o=n?0:m(e),i=n?0:m(e,"left");return _({top:o-t.top+t.marginTop,left:i-t.left+t.marginLeft,width:u,height:r})}(s,u);else{var p=void 0;"scrollParent"===t?"BODY"===(p=a(i(n))).nodeName&&(p=l.ownerDocument.documentElement):p="window"===t?l.ownerDocument.documentElement:t;var h=w(p,s,u);if("HTML"!==p.nodeName||function l(n){var e=n.nodeName;if("BODY"===e||"HTML"===e)return!1;if("fixed"===o(n,"position"))return!0;var t=i(n);return!!t&&l(t)}(s))r=h;else{var f=z(l.ownerDocument),d=f.height,y=f.width;r.top+=h.top-h.marginTop,r.bottom=d+h.top,r.left+=h.left-h.marginLeft,r.right=y+h.left}}var g="number"===typeof(e=e||0);return r.left+=g?e:e.left||0,r.top+=g?e:e.top||0,r.right-=g?e:e.right||0,r.bottom-=g?e:e.bottom||0,r}function C(l,n,e,t,u){var r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0;if(-1===l.indexOf("auto"))return l;var o=L(e,t,r,u),i={top:{width:o.width,height:n.top-o.top},right:{width:o.right-n.right,height:o.height},bottom:{width:o.width,height:o.bottom-n.bottom},left:{width:n.left-o.left,height:o.height}},a=Object.keys(i).map(function(l){return M({key:l},i[l],{area:(n=i[l],n.width*n.height)});var n}).sort(function(l,n){return n.area-l.area}),c=a.filter(function(l){var n=l.width,t=l.height;return n>=e.clientWidth&&t>=e.clientHeight}),s=c.length>0?c[0].key:a[0].key,p=l.split("-")[1];return s+(p?"-"+p:"")}function A(l,n,e){var t=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;return w(e,t?V(n):v(n,c(e)),t)}function S(l){var n=l.ownerDocument.defaultView.getComputedStyle(l),e=parseFloat(n.marginTop||0)+parseFloat(n.marginBottom||0),t=parseFloat(n.marginLeft||0)+parseFloat(n.marginRight||0);return{width:l.offsetWidth+t,height:l.offsetHeight+e}}function I(l){var n={left:"right",right:"left",bottom:"top",top:"bottom"};return l.replace(/left|right|bottom|top/g,function(l){return n[l]})}function R(l,n,e){e=e.split("-")[0];var t=S(l),u={width:t.width,height:t.height},r=-1!==["right","left"].indexOf(e),o=r?"top":"left",i=r?"left":"top",a=r?"height":"width",c=r?"width":"height";return u[o]=n[o]+n[a]/2-t[a]/2,u[i]=e===i?n[i]-t[c]:n[I(i)],u}function H(l,n){return Array.prototype.find?l.find(n):l.filter(n)[0]}function P(l,n,e){return(void 0===e?l:l.slice(0,function(l,n,e){if(Array.prototype.findIndex)return l.findIndex(function(l){return l[n]===e});var t=H(l,function(l){return l[n]===e});return l.indexOf(t)}(l,"name",e))).forEach(function(l){l.function&&console.warn("`modifier.function` is deprecated, use `modifier.fn`!");var e=l.function||l.fn;l.enabled&&r(e)&&(n.offsets.popper=_(n.offsets.popper),n.offsets.reference=_(n.offsets.reference),n=e(n,l))}),n}function N(l,n){return l.some(function(l){var e=l.name;return l.enabled&&e===n})}function D(l){for(var n=[!1,"ms","Webkit","Moz","O"],e=l.charAt(0).toUpperCase()+l.slice(1),t=0;t1&&void 0!==arguments[1]&&arguments[1],e=K.indexOf(l),t=K.slice(e+1).concat(K.slice(0,e));return n?t.reverse():t}var q={FLIP:"flip",CLOCKWISE:"clockwise",COUNTERCLOCKWISE:"counterclockwise"};function X(l,n,e,t){var u=[0,0],r=-1!==["right","left"].indexOf(t),o=l.split(/(\+|\-)/).map(function(l){return l.trim()}),i=o.indexOf(H(o,function(l){return-1!==l.search(/,|\s/)}));o[i]&&-1===o[i].indexOf(",")&&console.warn("Offsets separated by white space(s) are deprecated, use a comma (,) instead.");var a=/\s*,\s*|\s+/,c=-1!==i?[o.slice(0,i).concat([o[i].split(a)[0]]),[o[i].split(a)[1]].concat(o.slice(i+1))]:[o];return(c=c.map(function(l,t){var u=(1===t?!r:r)?"height":"width",o=!1;return l.reduce(function(l,n){return""===l[l.length-1]&&-1!==["+","-"].indexOf(n)?(l[l.length-1]=n,o=!0,l):o?(l[l.length-1]+=n,o=!1,l):l.concat(n)},[]).map(function(l){return function(l,n,e,t){var u=l.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+u[1],o=u[2];if(!r)return l;if(0===o.indexOf("%")){var i=void 0;switch(o){case"%p":i=e;break;case"%":case"%r":default:i=t}return _(i)[n]/100*r}if("vh"===o||"vw"===o)return("vh"===o?Math.max(document.documentElement.clientHeight,window.innerHeight||0):Math.max(document.documentElement.clientWidth,window.innerWidth||0))/100*r;return r}(l,u,n,e)})})).forEach(function(l,n){l.forEach(function(e,t){B(e)&&(u[n]+=e*("-"===l[t-1]?-1:1))})}),u}var $={placement:"bottom",positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(l){var n=l.placement,e=n.split("-")[0],t=n.split("-")[1];if(t){var u=l.offsets,r=u.reference,o=u.popper,i=-1!==["bottom","top"].indexOf(e),a=i?"left":"top",c=i?"width":"height",s={start:T({},a,r[a]),end:T({},a,r[a]+r[c]-o[c])};l.offsets.popper=M({},o,s[t])}return l}},offset:{order:200,enabled:!0,fn:function(l,n){var e=n.offset,t=l.placement,u=l.offsets,r=u.popper,o=u.reference,i=t.split("-")[0],a=void 0;return a=B(+e)?[+e,0]:X(e,r,o,i),"left"===i?(r.top+=a[0],r.left-=a[1]):"right"===i?(r.top+=a[0],r.left+=a[1]):"top"===i?(r.left+=a[0],r.top-=a[1]):"bottom"===i&&(r.left+=a[0],r.top+=a[1]),l.popper=r,l},offset:0},preventOverflow:{order:300,enabled:!0,fn:function(l,n){var e=n.boundariesElement||f(l.instance.popper);l.instance.reference===e&&(e=f(e));var t=D("transform"),u=l.instance.popper.style,r=u.top,o=u.left,i=u[t];u.top="",u.left="",u[t]="";var a=L(l.instance.popper,l.instance.reference,n.padding,e,l.positionFixed);u.top=r,u.left=o,u[t]=i,n.boundaries=a;var c=n.priority,s=l.offsets.popper,p={primary:function(l){var e=s[l];return s[l]a[l]&&!n.escapeWithReference&&(t=Math.min(s[e],a[l]-("right"===l?s.width:s.height))),T({},e,t)}};return c.forEach(function(l){var n=-1!==["left","top"].indexOf(l)?"primary":"secondary";s=M({},s,p[n](l))}),l.offsets.popper=s,l},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(l){var n=l.offsets,e=n.popper,t=n.reference,u=l.placement.split("-")[0],r=Math.floor,o=-1!==["top","bottom"].indexOf(u),i=o?"right":"bottom",a=o?"left":"top",c=o?"width":"height";return e[i]r(t[i])&&(l.offsets.popper[a]=r(t[i])),l}},arrow:{order:500,enabled:!0,fn:function(l,n){var e;if(!W(l.instance.modifiers,"arrow","keepTogether"))return l;var t=n.element;if("string"===typeof t){if(!(t=l.instance.popper.querySelector(t)))return l}else if(!l.instance.popper.contains(t))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),l;var u=l.placement.split("-")[0],r=l.offsets,i=r.popper,a=r.reference,c=-1!==["left","right"].indexOf(u),s=c?"height":"width",p=c?"Top":"Left",h=p.toLowerCase(),f=c?"left":"top",d=c?"bottom":"right",v=S(t)[s];a[d]-vi[d]&&(l.offsets.popper[h]+=a[h]+v-i[d]),l.offsets.popper=_(l.offsets.popper);var m=a[h]+a[s]/2-v/2,y=o(l.instance.popper),g=parseFloat(y["margin"+p]),z=parseFloat(y["border"+p+"Width"]),b=m-l.offsets.popper[h]-g-z;return b=Math.max(Math.min(i[s]-v,b),0),l.arrowElement=t,l.offsets.arrow=(T(e={},h,Math.round(b)),T(e,f,""),e),l},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(l,n){if(N(l.instance.modifiers,"inner"))return l;if(l.flipped&&l.placement===l.originalPlacement)return l;var e=L(l.instance.popper,l.instance.reference,n.padding,n.boundariesElement,l.positionFixed),t=l.placement.split("-")[0],u=I(t),r=l.placement.split("-")[1]||"",o=[];switch(n.behavior){case q.FLIP:o=[t,u];break;case q.CLOCKWISE:o=Y(t);break;case q.COUNTERCLOCKWISE:o=Y(t,!0);break;default:o=n.behavior}return o.forEach(function(i,a){if(t!==i||o.length===a+1)return l;t=l.placement.split("-")[0],u=I(t);var c=l.offsets.popper,s=l.offsets.reference,p=Math.floor,h="left"===t&&p(c.right)>p(s.left)||"right"===t&&p(c.left)p(s.top)||"bottom"===t&&p(c.top)p(e.right),v=p(c.top)p(e.bottom),y="left"===t&&f||"right"===t&&d||"top"===t&&v||"bottom"===t&&m,g=-1!==["top","bottom"].indexOf(t),z=!!n.flipVariations&&(g&&"start"===r&&f||g&&"end"===r&&d||!g&&"start"===r&&v||!g&&"end"===r&&m),b=!!n.flipVariationsByContent&&(g&&"start"===r&&d||g&&"end"===r&&f||!g&&"start"===r&&m||!g&&"end"===r&&v),E=z||b;(h||y||E)&&(l.flipped=!0,(h||y)&&(t=o[a+1]),E&&(r=function(l){return"end"===l?"start":"start"===l?"end":l}(r)),l.placement=t+(r?"-"+r:""),l.offsets.popper=M({},l.offsets.popper,R(l.instance.popper,l.offsets.reference,l.placement)),l=P(l.instance.modifiers,l,"flip"))}),l},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(l){var n=l.placement,e=n.split("-")[0],t=l.offsets,u=t.popper,r=t.reference,o=-1!==["left","right"].indexOf(e),i=-1===["top","left"].indexOf(e);return u[o?"left":"top"]=r[e]-(i?u[o?"width":"height"]:0),l.placement=I(n),l.offsets.popper=_(u),l}},hide:{order:800,enabled:!0,fn:function(l){if(!W(l.instance.modifiers,"hide","preventOverflow"))return l;var n=l.offsets.reference,e=H(l.instance.modifiers,function(l){return"preventOverflow"===l.name}).boundaries;if(n.bottome.right||n.top>e.bottom||n.right2&&void 0!==arguments[2]?arguments[2]:{};b(this,l),this.scheduleUpdate=function(){return requestAnimationFrame(t.update)},this.update=u(this.update.bind(this)),this.options=M({},l.Defaults,o),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=n&&n.jquery?n[0]:n,this.popper=e&&e.jquery?e[0]:e,this.options.modifiers={},Object.keys(M({},l.Defaults.modifiers,o.modifiers)).forEach(function(n){t.options.modifiers[n]=M({},l.Defaults.modifiers[n]||{},o.modifiers?o.modifiers[n]:{})}),this.modifiers=Object.keys(this.options.modifiers).map(function(l){return M({name:l},t.options.modifiers[l])}).sort(function(l,n){return l.order-n.order}),this.modifiers.forEach(function(l){l.enabled&&r(l.onLoad)&&l.onLoad(t.reference,t.popper,t.options,l,t.state)}),this.update();var i=this.options.eventsEnabled;i&&this.enableEventListeners(),this.state.eventsEnabled=i}return E(l,[{key:"update",value:function(){return function(){if(!this.state.isDestroyed){var l={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};l.offsets.reference=A(this.state,this.popper,this.reference,this.options.positionFixed),l.placement=C(this.options.placement,l.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),l.originalPlacement=l.placement,l.positionFixed=this.options.positionFixed,l.offsets.popper=R(this.popper,l.offsets.reference,l.placement),l.offsets.popper.position=this.options.positionFixed?"fixed":"absolute",l=P(this.modifiers,l),this.state.isCreated?this.options.onUpdate(l):(this.state.isCreated=!0,this.options.onCreate(l))}}.call(this)}},{key:"destroy",value:function(){return function(){return this.state.isDestroyed=!0,N(this.modifiers,"applyStyle")&&(this.popper.removeAttribute("x-placement"),this.popper.style.position="",this.popper.style.top="",this.popper.style.left="",this.popper.style.right="",this.popper.style.bottom="",this.popper.style.willChange="",this.popper.style[D("transform")]=""),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}.call(this)}},{key:"enableEventListeners",value:function(){return function(){this.state.eventsEnabled||(this.state=k(this.reference,this.options,this.state,this.scheduleUpdate))}.call(this)}},{key:"disableEventListeners",value:function(){return U.call(this)}}]),l}();Z.Utils=("undefined"!==typeof window?window:l).PopperUtils,Z.placements=G,Z.Defaults=$,n.a=Z}).call(this,e(23))},function(l,n,e){"use strict";var t=Object.getOwnPropertySymbols,u=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;l.exports=function(){try{if(!Object.assign)return!1;var l=new String("abc");if(l[5]="de","5"===Object.getOwnPropertyNames(l)[0])return!1;for(var n={},e=0;e<10;e++)n["_"+String.fromCharCode(e)]=e;if("0123456789"!==Object.getOwnPropertyNames(n).map(function(l){return n[l]}).join(""))return!1;var t={};return"abcdefghijklmnopqrst".split("").forEach(function(l){t[l]=l}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},t)).join("")}catch(u){return!1}}()?Object.assign:function(l,n){for(var e,o,i=function(l){if(null===l||void 0===l)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(l)}(l),a=1;a=0&&t>=0&&u>=0)return!0}return console.error("version missmatch!"),!1},l.prototype.initialize=function(){if(this.transporter.isConnected()){var l=new r.Packet(o.RcpTypes.Command.INITIALIZE);this.sendPacket(l)}else console.log("initialize: transporter not connected")},l.prototype.update=function(){var n=this;try{this.transporter.isConnected()?(console.log("this.dirtyParams: "+this.dirtyParams.length),this.dirtyParams.forEach(function(e){var t=o.RcpTypes.Command.UPDATE;l.serverVersionGt("0.0.1")&&(e instanceof c.BangParameter||e.onlyValueChanged())&&(t=o.RcpTypes.Command.UPDATEVALUE);var u=new r.Packet(t);u.data=e,n.sendPacket(u)}),this.dirtyParams=[]):console.log("transporter not connected")}catch(e){throw e}},l.prototype.getParameter=function(l){return this.valueCache.get(l)},l.prototype.setParameterDirty=function(l){this.dirtyParams.indexOf(l)>-1||this.dirtyParams.push(l)},l.prototype.getRootGroup=function(){return this._rootGroup},l.prototype.sendPacket=function(n){var e=new Int8Array(n.serialize(!1));l.VERBOSE&&console.log("client writing: ",e),this.transporter.send(e)},l.prototype._update=function(n){if(this.valueCache.has(n.id)){var e=this.valueCache.get(n.id);e&&e.update(n),n.dispose(),l.VERBOSE&&e&&console.log("CLIENT: updated paramter: "+e.label+" ["+e.id+"]")}else n.parent?n.parent.addChild(n):n.setParentDirect(this._rootGroup),this.valueCache.set(n.id,n),console.log("CLIENT _update: "+n.logChanged()),n.clearChanged(),console.log("CLIENT _update1: "+n.logChanged()+" dirty: "+(this.dirtyParams.indexOf(n)>-1)),this.parameterAdded&&this.parameterAdded(n),l.VERBOSE&&console.log("CLIENT: paramter added to cache: "+n.label+" ["+n.id+"]")},l.prototype._remove=function(n){var e=this.valueCache.get(n);void 0!==e?(l.VERBOSE&&console.log("CLIENT: remove: "+n),e.removeFromParent(),this.valueCache.delete(n),this.parameterRemoved&&this.parameterRemoved(e),e.dispose()):l.VERBOSE&&console.log("CLIENT: no parameter to remove with id: "+n)},l.VERBOSE=!1,l.rcpVersion="0.1.0",l}();n.Client=f},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.IP)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.IpWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.DIRECTORYCHOOSER)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.DirectorychooserWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.FILECHOOSER)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.FilechooserWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.TABLE)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.TableWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.COLORBOX)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.ColorboxWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.RADIOBUTTON)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.RadiobuttonWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.DROPDOWN)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.DropdownWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.RANGE)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.RangeWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.SLIDER2D)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.Slider2dWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.TABS)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.TabsWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.LISTPAGE)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.ListPageWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.LIST)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.ListWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=e(27),i=e(15),a=e(98),c=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.CUSTOM)||this}return t(n,l),n.prototype.handleOption=function(l,n){switch(l){case r.RcpTypes.CustomwidgetOptions.UUID:return this._uuid=new a.UUID(n.readBytes(16)),console.log("custom widget: uuid: "+this._uuid),!0;case r.RcpTypes.CustomwidgetOptions.CONFIG:return this._config=new r.Userdata(n).data,console.log("custom widget: config: "+o.default.createStringFromArray(this._config)),!0}return!1},n.prototype.writeOptions=function(l,n){(n||this.changed.has(r.RcpTypes.CustomwidgetOptions.UUID))&&(l.push(r.RcpTypes.CustomwidgetOptions.UUID),this._uuid?this._uuid.data.forEach(function(n){l.push(n)}):(i.pushIn64ToArrayBe(0,l),i.pushIn64ToArrayBe(0,l))),(n||this.changed.has(r.RcpTypes.CustomwidgetOptions.CONFIG))&&(l.push(r.RcpTypes.CustomwidgetOptions.CONFIG),this._config?(i.pushFloat32ToArrayBe(this._config.length,l),this._config.forEach(function(n){l.push(n)})):i.pushFloat32ToArrayBe(0,l))},Object.defineProperty(n.prototype,"uuid",{get:function(){return this._uuid},set:function(l){l&&16===l.data.length&&(this._uuid.compareRaw(l.data)||(this._uuid=l,this.changed.set(r.RcpTypes.CustomwidgetOptions.UUID,!0),this.setDirty()))},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"config",{get:function(){return this._config},set:function(l){this._config!==l&&(this._config=l,this.changed.set(r.RcpTypes.CustomwidgetOptions.CONFIG,!0),this.setDirty())},enumerable:!0,configurable:!0}),n}(u.Widget);n.CustomWidget=c},function(l,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=new(function(){return function(){this.byteToHex=[],this.hexToByte={};for(var l=0;l<256;l++)this.byteToHex[l]=(l+256).toString(16).substr(1),this.hexToByte[this.byteToHex[l]]=l}}()),u=function(){function l(l){this._data=l}return Object.defineProperty(l.prototype,"data",{get:function(){return this._data},set:function(l){this._data=l},enumerable:!0,configurable:!0}),l.prototype.toString=function(){if(this._data.byteLength<16)return"";var l=0,n=t.byteToHex;return n[this._data[l++]]+n[this._data[l++]]+n[this._data[l++]]+n[this._data[l++]]+"-"+n[this._data[l++]]+n[this._data[l++]]+"-"+n[this._data[l++]]+n[this._data[l++]]+"-"+n[this._data[l++]]+n[this._data[l++]]+"-"+n[this._data[l++]]+n[this._data[l++]]+n[this._data[l++]]+n[this._data[l++]]+n[this._data[l++]]+n[this._data[l++]]},l.prototype.compare=function(l){return this.toString().toUpperCase()===l.toUpperCase()},l.prototype.compareRaw=function(l){if(this._data==l)return!0;if(this._data.byteLength!==l.byteLength)return!1;for(var n=0;n!=this._data.byteLength;n++)if(this._data[n]!=l[n])return!1;return!0},l}();n.UUID=u},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.DIAL)||this}return t(n,l),n.prototype.handleOption=function(l,n){return l===r.RcpTypes.DialOptions.CYCLIC&&(this._cyclic=n.readU1()>0,!0)},n.prototype.writeOptions=function(l,n){(n||this.changed.has(r.RcpTypes.DialOptions.CYCLIC))&&(l.push(r.RcpTypes.DialOptions.CYCLIC),this._cyclic?l.push(this._cyclic?1:0):l.push(0))},Object.defineProperty(n.prototype,"cyclic",{get:function(){return this._cyclic},set:function(l){this._cyclic!==l&&(this._cyclic=l,this.changed.set(r.RcpTypes.DialOptions.CYCLIC,!0),this.setDirty())},enumerable:!0,configurable:!0}),n}(u.Widget);n.DialWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.SLIDER)||this}return t(n,l),n.prototype.handleOption=function(l,n){return l===r.RcpTypes.SliderOptions.HORIZONTAL&&(this._horizontal=n.readU1()>0,!0)},n.prototype.writeOptions=function(l,n){(n||this.changed.has(r.RcpTypes.SliderOptions.HORIZONTAL))&&(l.push(r.RcpTypes.SliderOptions.HORIZONTAL),this._horizontal?l.push(this._horizontal?1:0):l.push(0))},Object.defineProperty(n.prototype,"horizontal",{get:function(){return this._horizontal},set:function(l){this._horizontal!==l&&(this._horizontal=l,this.changed.set(r.RcpTypes.SliderOptions.HORIZONTAL,!0),this.setDirty())},enumerable:!0,configurable:!0}),n}(u.Widget);n.SliderWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=e(28),i=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.NUMBERBOX)||this}return t(n,l),n.prototype.handleOption=function(l,n){switch(l){case r.RcpTypes.NumberboxOptions.PRECISION:return this._precision=n.readU1(),!0;case r.RcpTypes.NumberboxOptions.FORMAT:return this._format=n.readU1(),!0;case r.RcpTypes.NumberboxOptions.STEPSIZE:var e=this.parameter;if(e){var t=e.typeDefinition;if(!(t instanceof o.NumberDefinition))throw new Error("numberbox widget with non-number-parameter: can not read stepsize!");return this._stepsize=t.readValue(n),!0}case r.RcpTypes.NumberboxOptions.CYCLIC:return this._cyclic=n.readU1()>0,!0}return!1},n.prototype.writeOptions=function(l,n){if((n||this.changed.has(r.RcpTypes.NumberboxOptions.FORMAT))&&(l.push(r.RcpTypes.NumberboxOptions.FORMAT),this._format?l.push(this._format):l.push(r.RcpTypes.NumberboxFormat.DEC)),(n||this.changed.has(r.RcpTypes.NumberboxOptions.PRECISION))&&(l.push(r.RcpTypes.NumberboxOptions.PRECISION),this._precision?l.push(this._precision):l.push(2)),n||this.changed.has(r.RcpTypes.NumberboxOptions.STEPSIZE)){l.push(r.RcpTypes.NumberboxOptions.STEPSIZE);var e=this.parameter;if(e){var t=e.typeDefinition;if(!(t instanceof o.NumberDefinition))throw new Error("numberbox widget with non-number-parameter: can not write stepsize!");t.writeValue(l,this._stepsize)}}(n||this.changed.has(r.RcpTypes.NumberboxOptions.CYCLIC))&&(l.push(r.RcpTypes.NumberboxOptions.CYCLIC),this._cyclic?l.push(this._cyclic?1:0):l.push(0))},Object.defineProperty(n.prototype,"precision",{get:function(){return this._precision},set:function(l){this._precision!==l&&(this._precision=l,this.changed.set(r.RcpTypes.NumberboxOptions.PRECISION,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"format",{get:function(){return this._format},set:function(l){this._format!==l&&(this._format=l,this.changed.set(r.RcpTypes.NumberboxOptions.FORMAT,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"stepsize",{get:function(){return this._stepsize},set:function(l){this._stepsize!==l&&(this._stepsize=l,this.changed.set(r.RcpTypes.NumberboxOptions.STEPSIZE,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"cyclic",{get:function(){return this._cyclic},set:function(l){this._cyclic!==l&&(this._cyclic=l,this.changed.set(r.RcpTypes.NumberboxOptions.CYCLIC,!0),this.setDirty())},enumerable:!0,configurable:!0}),n}(u.Widget);n.NumberboxWidget=i},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.TEXTBOX)||this}return t(n,l),n.prototype.handleOption=function(l,n){return l===r.RcpTypes.TextboxOptions.MULTILINE?(this._multiline=n.readU1()>0,!0):l===r.RcpTypes.TextboxOptions.WORDWRAP?(this._wordwrap=n.readU1()>0,!0):l===r.RcpTypes.TextboxOptions.PASSWORD&&(this._password=n.readU1()>0,!0)},n.prototype.writeOptions=function(l,n){(n||this.changed.has(r.RcpTypes.TextboxOptions.MULTILINE))&&(l.push(r.RcpTypes.TextboxOptions.MULTILINE),this._multiline?l.push(this._multiline?1:0):l.push(0)),(n||this.changed.has(r.RcpTypes.TextboxOptions.WORDWRAP))&&(l.push(r.RcpTypes.TextboxOptions.WORDWRAP),this._wordwrap?l.push(this._wordwrap?1:0):l.push(0)),(n||this.changed.has(r.RcpTypes.TextboxOptions.PASSWORD))&&(l.push(r.RcpTypes.TextboxOptions.PASSWORD),this._password?l.push(this._password?1:0):l.push(0))},Object.defineProperty(n.prototype,"multiline",{get:function(){return this._multiline},set:function(l){this._multiline!==l&&(this._multiline=l,this.changed.set(r.RcpTypes.TextboxOptions.MULTILINE,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"wordwrap",{get:function(){return this._wordwrap},set:function(l){this._wordwrap!==l&&(this._wordwrap=l,this.changed.set(r.RcpTypes.TextboxOptions.WORDWRAP,!0),this.setDirty())},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"password",{get:function(){return this._password},set:function(l){this._password!==l&&(this._password=l,this.changed.set(r.RcpTypes.TextboxOptions.PASSWORD,!0),this.setDirty())},enumerable:!0,configurable:!0}),n}(u.Widget);n.TextboxWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.INFO)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.InfoWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.BANG)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.BangWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.PRESS)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.PressWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.TOGGLE)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.ToggleWidget=o},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(12),r=e(4),o=function(l){function n(){return l.call(this,r.RcpTypes.Widgettype.DEFAULT)||this}return t(n,l),n.prototype.handleOption=function(l,n){return!1},n.prototype.writeOptions=function(l,n){},n}(u.Widget);n.DefaultWidget=o},function(l,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=e(15),u=e(4),r=e(24),o=function(){function l(l){this.command=l}return l.prototype.serialize=function(l){var n=new Array;return this.write(n,l),n},l.prototype.write=function(l,n){if(l.push(this.command),this.command===u.RcpTypes.Command.UPDATEVALUE){if(!(this.data instanceof r.Parameter))throw new Error("wrong data in updatevalue packet");this.data.writeValueUpdate(l)}else this.timestamp&&(l.push(u.RcpTypes.PacketOptions.TIMESTAMP),t.pushIn64ToArrayBe(this.timestamp,l)),this.data&&(l.push(u.RcpTypes.PacketOptions.DATA),this.data.write(l,n)),l.push(u.RcpTypes.TERMINATOR)},l}();n.Packet=o},function(l,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=e(15),u=e(1),r=e(4),o=function(){function l(l,n){this.version="0.0.0",this.applicationid="",this.version=l,this.applicationid=n}return l.prototype.write=function(l,n){t.writeTinyString(this.version,l),this.applicationid&&""!==this.applicationid&&(l.push(u.RcpTypes.InfodataOptions.APPLICATIONID),t.writeTinyString(this.applicationid,l))},l}();n.InfoData=o,n.parseInfoData=function(l){for(var n=new r.TinyString(l).data,e="";;){var t=l.readU1();if(t===u.RcpTypes.TERMINATOR)break;switch(t){case u.RcpTypes.InfodataOptions.APPLICATIONID:e=new r.TinyString(l).data}}return new o(n,e)}},function(l,n,e){"use strict";var t=this&&this.__extends||function(){var l=function(n,e){return(l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,n){l.__proto__=n}||function(l,n){for(var e in n)n.hasOwnProperty(e)&&(l[e]=n[e])})(n,e)};return function(n,e){function t(){this.constructor=n}l(n,e),n.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}}();Object.defineProperty(n,"__esModule",{value:!0});var u=e(40),r=function(l){function n(n){return l.call(this,n,new u.BooleanDefinition)||this}return t(n,l),n.prototype.setStringValue=function(l){var n=parseInt(l);if(!isNaN(n))return this.value=n>0,!0;switch(l.toLowerCase().trim()){case"true":case"yes":return this.value=!0,!0;case"false":case"no":case null:return this.value=!1,!0}return!1},n}(e(17).ValueParameter);n.BooleanParameter=r},function(l,n,e){"use strict";n.__esModule=!0,n.default=n.EXITING=n.ENTERED=n.ENTERING=n.EXITED=n.UNMOUNTED=void 0;var t=function(l){if(l&&l.__esModule)return l;var n={};if(null!=l)for(var e in l)if(Object.prototype.hasOwnProperty.call(l,e)){var t=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(l,e):{};t.get||t.set?Object.defineProperty(n,e,t):n[e]=l[e]}return n.default=l,n}(e(22)),u=i(e(0)),r=i(e(16)),o=e(2);e(112);function i(l){return l&&l.__esModule?l:{default:l}}var a="unmounted";n.UNMOUNTED=a;var c="exited";n.EXITED=c;var s="entering";n.ENTERING=s;var p="entered";n.ENTERED=p;n.EXITING="exiting";var h=function(l){var n,e;function t(n,e){var t;t=l.call(this,n,e)||this;var u,r=e.transitionGroup,o=r&&!r.isMounting?n.enter:n.appear;return t.appearStatus=null,n.in?o?(u=c,t.appearStatus=s):u=p:u=n.unmountOnExit||n.mountOnEnter?a:c,t.state={status:u},t.nextCallback=null,t}e=l,(n=t).prototype=Object.create(e.prototype),n.prototype.constructor=n,n.__proto__=e;var o=t.prototype;return o.getChildContext=function(){return{transitionGroup:null}},t.getDerivedStateFromProps=function(l,n){return l.in&&n.status===a?{status:c}:null},o.componentDidMount=function(){this.updateStatus(!0,this.appearStatus)},o.componentDidUpdate=function(l){var n=null;if(l!==this.props){var e=this.state.status;this.props.in?e!==s&&e!==p&&(n=s):e!==s&&e!==p||(n="exiting")}this.updateStatus(!1,n)},o.componentWillUnmount=function(){this.cancelNextCallback()},o.getTimeouts=function(){var l,n,e,t=this.props.timeout;return l=n=e=t,null!=t&&"number"!==typeof t&&(l=t.exit,n=t.enter,e=void 0!==t.appear?t.appear:n),{exit:l,enter:n,appear:e}},o.updateStatus=function(l,n){if(void 0===l&&(l=!1),null!==n){this.cancelNextCallback();var e=r.default.findDOMNode(this);n===s?this.performEnter(e,l):this.performExit(e)}else this.props.unmountOnExit&&this.state.status===c&&this.setState({status:a})},o.performEnter=function(l,n){var e=this,t=this.props.enter,u=this.context.transitionGroup?this.context.transitionGroup.isMounting:n,r=this.getTimeouts(),o=u?r.appear:r.enter;n||t?(this.props.onEnter(l,u),this.safeSetState({status:s},function(){e.props.onEntering(l,u),e.onTransitionEnd(l,o,function(){e.safeSetState({status:p},function(){e.props.onEntered(l,u)})})})):this.safeSetState({status:p},function(){e.props.onEntered(l)})},o.performExit=function(l){var n=this,e=this.props.exit,t=this.getTimeouts();e?(this.props.onExit(l),this.safeSetState({status:"exiting"},function(){n.props.onExiting(l),n.onTransitionEnd(l,t.exit,function(){n.safeSetState({status:c},function(){n.props.onExited(l)})})})):this.safeSetState({status:c},function(){n.props.onExited(l)})},o.cancelNextCallback=function(){null!==this.nextCallback&&(this.nextCallback.cancel(),this.nextCallback=null)},o.safeSetState=function(l,n){n=this.setNextCallback(n),this.setState(l,n)},o.setNextCallback=function(l){var n=this,e=!0;return this.nextCallback=function(t){e&&(e=!1,n.nextCallback=null,l(t))},this.nextCallback.cancel=function(){e=!1},this.nextCallback},o.onTransitionEnd=function(l,n,e){this.setNextCallback(e);var t=null==n&&!this.props.addEndListener;l&&!t?(this.props.addEndListener&&this.props.addEndListener(l,this.nextCallback),null!=n&&setTimeout(this.nextCallback,n)):setTimeout(this.nextCallback,0)},o.render=function(){var l=this.state.status;if(l===a)return null;var n=this.props,e=n.children,t=function(l,n){if(null==l)return{};var e,t,u={},r=Object.keys(l);for(t=0;t=0||(u[e]=l[e]);return u}(n,["children"]);if(delete t.in,delete t.mountOnEnter,delete t.unmountOnExit,delete t.appear,delete t.enter,delete t.exit,delete t.timeout,delete t.addEndListener,delete t.onEnter,delete t.onEntering,delete t.onEntered,delete t.onExit,delete t.onExiting,delete t.onExited,"function"===typeof e)return e(l,t);var r=u.default.Children.only(e);return u.default.cloneElement(r,t)},t}(u.default.Component);function f(){}h.contextTypes={transitionGroup:t.object},h.childContextTypes={transitionGroup:function(){}},h.propTypes={},h.defaultProps={in:!1,mountOnEnter:!1,unmountOnExit:!1,appear:!1,enter:!0,exit:!0,onEnter:f,onEntering:f,onEntered:f,onExit:f,onExiting:f,onExited:f},h.UNMOUNTED=0,h.EXITED=1,h.ENTERING=2,h.ENTERED=3,h.EXITING=4;var d=(0,o.polyfill)(h);n.default=d},function(l,n,e){"use strict";n.__esModule=!0,n.classNamesShape=n.timeoutsShape=void 0;var t;(t=e(22))&&t.__esModule;n.timeoutsShape=null;n.classNamesShape=null},function(l,n,e){"use strict";n.__esModule=!0,n.default=void 0;var t=i(e(22)),u=i(e(0)),r=e(2),o=e(208);function i(l){return l&&l.__esModule?l:{default:l}}function a(){return(a=Object.assign||function(l){for(var n=1;n=0||(u[e]=l[e]);return u}(l,["component","childFactory"]),r=s(this.state.children).map(e);return delete t.appear,delete t.enter,delete t.exit,null===n?r:u.default.createElement(n,t,r)},t}(u.default.Component);p.childContextTypes={transitionGroup:t.default.object.isRequired},p.propTypes={},p.defaultProps={component:"div",childFactory:function(l){return l}};var h=(0,r.polyfill)(p);n.default=h,l.exports=n.default},function(l,n,e){"use strict";var t=Object.prototype.toString;l.exports=function(l){var n=t.call(l),e="[object Arguments]"===n;return e||(e="[object Array]"!==n&&null!==l&&"object"===typeof l&&"number"===typeof l.length&&l.length>=0&&"[object Function]"===t.call(l.callee)),e}},function(l,n,e){"use strict";var t=e(35);l.exports=t.call(Function.call,Object.prototype.hasOwnProperty)},function(l,n,e){"use strict";var t=function(l){return l!==l};l.exports=function(l,n){return 0===l&&0===n?1/l===1/n:l===n||!(!t(l)||!t(n))}},function(l,n,e){"use strict";var t=e(116);l.exports=function(){return"function"===typeof Object.is?Object.is:t}},function(l,n,e){"use strict";var t=Object,u=TypeError;l.exports=function(){if(null!=this&&this!==t(this))throw new u("RegExp.prototype.flags getter called on non-object");var l="";return this.global&&(l+="g"),this.ignoreCase&&(l+="i"),this.multiline&&(l+="m"),this.dotAll&&(l+="s"),this.unicode&&(l+="u"),this.sticky&&(l+="y"),l}},function(l,n,e){"use strict";var t=e(118),u=e(34).supportsDescriptors,r=Object.getOwnPropertyDescriptor,o=TypeError;l.exports=function(){if(!u)throw new o("RegExp.prototype.flags requires a true ES5 environment that supports property descriptors");if("gim"===/a/gim.flags){var l=r(RegExp.prototype,"flags");if(l&&"function"===typeof l.get&&"boolean"===typeof/a/.dotAll)return l.get}return t}},function(l,n,e){"use strict";e.d(n,"a",function(){return t});var t={BLACK:"#10161A",BLUE1:"#0E5A8A",BLUE2:"#106BA3",BLUE3:"#137CBD",BLUE4:"#2B95D6",BLUE5:"#48AFF0",COBALT1:"#1F4B99",COBALT2:"#2458B3",COBALT3:"#2965CC",COBALT4:"#4580E6",COBALT5:"#669EFF",DARK_GRAY1:"#182026",DARK_GRAY2:"#202B33",DARK_GRAY3:"#293742",DARK_GRAY4:"#30404D",DARK_GRAY5:"#394B59",FOREST1:"#1D7324",FOREST2:"#238C2C",FOREST3:"#29A634",FOREST4:"#43BF4D",FOREST5:"#62D96B",GOLD1:"#A67908",GOLD2:"#BF8C0A",GOLD3:"#D99E0B",GOLD4:"#F2B824",GOLD5:"#FFC940",GRAY1:"#5C7080",GRAY2:"#738694",GRAY3:"#8A9BA8",GRAY4:"#A7B6C2",GRAY5:"#BFCCD6",GREEN1:"#0A6640",GREEN2:"#0D8050",GREEN3:"#0F9960",GREEN4:"#15B371",GREEN5:"#3DCC91",INDIGO1:"#5642A6",INDIGO2:"#634DBF",INDIGO3:"#7157D9",INDIGO4:"#9179F2",INDIGO5:"#AD99FF",LIGHT_GRAY1:"#CED9E0",LIGHT_GRAY2:"#D8E1E8",LIGHT_GRAY3:"#E1E8ED",LIGHT_GRAY4:"#EBF1F5",LIGHT_GRAY5:"#F5F8FA",LIME1:"#728C23",LIME2:"#87A629",LIME3:"#9BBF30",LIME4:"#B6D94C",LIME5:"#D1F26D",ORANGE1:"#A66321",ORANGE2:"#BF7326",ORANGE3:"#D9822B",ORANGE4:"#F29D49",ORANGE5:"#FFB366",RED1:"#A82A2A",RED2:"#C23030",RED3:"#DB3737",RED4:"#F55656",RED5:"#FF7373",ROSE1:"#A82255",ROSE2:"#C22762",ROSE3:"#DB2C6F",ROSE4:"#F5498B",ROSE5:"#FF66A1",SEPIA1:"#63411E",SEPIA2:"#7D5125",SEPIA3:"#96622D",SEPIA4:"#B07B46",SEPIA5:"#C99765",TURQUOISE1:"#008075",TURQUOISE2:"#00998C",TURQUOISE3:"#00B3A4",TURQUOISE4:"#14CCBD",TURQUOISE5:"#2EE6D6",VERMILION1:"#9E2B0E",VERMILION2:"#B83211",VERMILION3:"#D13913",VERMILION4:"#EB532D",VERMILION5:"#FF6E4A",VIOLET1:"#5C255C",VIOLET2:"#752F75",VIOLET3:"#8F398F",VIOLET4:"#A854A8",VIOLET5:"#C274C2",WHITE:"#FFFFFF"}},function(l,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=Object.assign||function(l){for(var n=1;n=0||Object.prototype.hasOwnProperty.call(l,t)&&(e[t]=l[t]);return e}(l,["innerRef","onResize"]));return(0,r.createElement)(n,t({},e,{measureRef:this._handleRef,measure:this.measure,contentRect:this.state.contentRect}))}}]),o}(),e.propTypes={client:o.default.bool,offset:o.default.bool,scroll:o.default.bool,bounds:o.default.bool,margin:o.default.bool,innerRef:o.default.func,onResize:o.default.func},s}}},function(l,n){l.exports=function(l,n){if(null==l)return{};var e,t,u={},r=Object.keys(l);for(t=0;t=0||(u[e]=l[e]);return u}},function(l,n,e){var t=e(209),u=e(211),r=e(212),o=e(221),i=e(222),a=e(228),c=Date.prototype.getTime;function s(l,n,e){var f=e||{};return!(f.strict?!r(l,n):l!==n)||(!l||!n||"object"!==typeof l&&"object"!==typeof n?f.strict?r(l,n):l==n:function(l,n,e){var r,f;if(typeof l!==typeof n)return!1;if(p(l)||p(n))return!1;if(l.prototype!==n.prototype)return!1;if(u(l)!==u(n))return!1;var d=o(l),v=o(n);if(d!==v)return!1;if(d||v)return l.source===n.source&&i(l)===i(n);if(a(l)&&a(n))return c.call(l)===c.call(n);var m=h(l),y=h(n);if(m!==y)return!1;if(m||y){if(l.length!==n.length)return!1;for(r=0;r=0;r--)if(g[r]!=z[r])return!1;for(r=g.length-1;r>=0;r--)if(f=g[r],!s(l[f],n[f],e))return!1;return!0}(l,n,f))}function p(l){return null===l||void 0===l}function h(l){return!(!l||"object"!==typeof l||"number"!==typeof l.length)&&("function"===typeof l.copy&&"function"===typeof l.slice&&!(l.length>0&&"number"!==typeof l[0]))}l.exports=s},function(l,n,e){"use strict";function t(l,n){return function(l){if(Array.isArray(l))return l}(l)||function(l,n){var e=[],t=!0,u=!1,r=void 0;try{for(var o,i=l[Symbol.iterator]();!(t=(o=i.next()).done)&&(e.push(o.value),!n||e.length!==n);t=!0);}catch(a){u=!0,r=a}finally{try{t||null==i.return||i.return()}finally{if(u)throw r}}return e}(l,n)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}e.d(n,"a",function(){return t})},,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,function(l,n,e){"use strict";var t=e(83),u="function"===typeof Symbol&&Symbol.for,r=u?Symbol.for("react.element"):60103,o=u?Symbol.for("react.portal"):60106,i=u?Symbol.for("react.fragment"):60107,a=u?Symbol.for("react.strict_mode"):60108,c=u?Symbol.for("react.profiler"):60114,s=u?Symbol.for("react.provider"):60109,p=u?Symbol.for("react.context"):60110,h=u?Symbol.for("react.concurrent_mode"):60111,f=u?Symbol.for("react.forward_ref"):60112,d=u?Symbol.for("react.suspense"):60113,v=u?Symbol.for("react.memo"):60115,m=u?Symbol.for("react.lazy"):60116,y="function"===typeof Symbol&&Symbol.iterator;function g(l){for(var n=arguments.length-1,e="https://reactjs.org/docs/error-decoder.html?invariant="+l,t=0;tS.length&&S.push(l)}function H(l,n,e){return null==l?0:function l(n,e,t,u){var i=typeof n;"undefined"!==i&&"boolean"!==i||(n=null);var a=!1;if(null===n)a=!0;else switch(i){case"string":case"number":a=!0;break;case"object":switch(n.$$typeof){case r:case o:a=!0}}if(a)return t(u,n,""===e?"."+P(n,0):e),1;if(a=0,e=""===e?".":e+":",Array.isArray(n))for(var c=0;cthis.eventPool.length&&this.eventPool.push(l)}function pl(l){l.eventPool=[],l.getPooled=cl,l.release=sl}u(al.prototype,{preventDefault:function(){this.defaultPrevented=!0;var l=this.nativeEvent;l&&(l.preventDefault?l.preventDefault():"unknown"!==typeof l.returnValue&&(l.returnValue=!1),this.isDefaultPrevented=ol)},stopPropagation:function(){var l=this.nativeEvent;l&&(l.stopPropagation?l.stopPropagation():"unknown"!==typeof l.cancelBubble&&(l.cancelBubble=!0),this.isPropagationStopped=ol)},persist:function(){this.isPersistent=ol},isPersistent:il,destructor:function(){var l,n=this.constructor.Interface;for(l in n)this[l]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null,this.isPropagationStopped=this.isDefaultPrevented=il,this._dispatchInstances=this._dispatchListeners=null}}),al.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(l){return l.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null},al.extend=function(l){function n(){}function e(){return t.apply(this,arguments)}var t=this;n.prototype=t.prototype;var r=new n;return u(r,e.prototype),e.prototype=r,e.prototype.constructor=e,e.Interface=u({},t.Interface,l),e.extend=t.extend,pl(e),e},pl(al);var hl=al.extend({data:null}),fl=al.extend({data:null}),dl=[9,13,27,32],vl=G&&"CompositionEvent"in window,ml=null;G&&"documentMode"in document&&(ml=document.documentMode);var yl=G&&"TextEvent"in window&&!ml,gl=G&&(!vl||ml&&8=ml),zl=String.fromCharCode(32),bl={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["compositionend","keypress","textInput","paste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"blur compositionend keydown keypress keyup mousedown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:"blur compositionstart keydown keypress keyup mousedown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"blur compositionupdate keydown keypress keyup mousedown".split(" ")}},El=!1;function Tl(l,n){switch(l){case"keyup":return-1!==dl.indexOf(n.keyCode);case"keydown":return 229!==n.keyCode;case"keypress":case"mousedown":case"blur":return!0;default:return!1}}function Ml(l){return"object"===typeof(l=l.detail)&&"data"in l?l.data:null}var _l=!1;var Ol={eventTypes:bl,extractEvents:function(l,n,e,t){var u=void 0,r=void 0;if(vl)l:{switch(l){case"compositionstart":u=bl.compositionStart;break l;case"compositionend":u=bl.compositionEnd;break l;case"compositionupdate":u=bl.compositionUpdate;break l}u=void 0}else _l?Tl(l,e)&&(u=bl.compositionEnd):"keydown"===l&&229===e.keyCode&&(u=bl.compositionStart);return u?(gl&&"ko"!==e.locale&&(_l||u!==bl.compositionStart?u===bl.compositionEnd&&_l&&(r=rl()):(tl="value"in(el=t)?el.value:el.textContent,_l=!0)),u=hl.getPooled(u,n,e,t),r?u.data=r:null!==(r=Ml(e))&&(u.data=r),W(u),r=u):r=null,(l=yl?function(l,n){switch(l){case"compositionend":return Ml(n);case"keypress":return 32!==n.which?null:(El=!0,zl);case"textInput":return(l=n.data)===zl&&El?null:l;default:return null}}(l,e):function(l,n){if(_l)return"compositionend"===l||!vl&&Tl(l,n)?(l=rl(),ul=tl=el=null,_l=!1,l):null;switch(l){case"paste":return null;case"keypress":if(!(n.ctrlKey||n.altKey||n.metaKey)||n.ctrlKey&&n.altKey){if(n.char&&1