去除所有 orx 模块
22
.gitignore
vendored
@@ -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
|
||||
162
CONTRIBUTING.md
@@ -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
|
||||
...
|
||||
|
||||
<!-- __demos__ -->
|
||||
```
|
||||
|
||||
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 `<!-- __demos__ -->`
|
||||
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`
|
||||
25
LICENSE
@@ -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.
|
||||
120
README.md
@@ -1,120 +0,0 @@
|
||||
# ORX (OPENRNDR EXTRA)
|
||||
|
||||
[](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.
|
||||
|
||||
<!-- __orxListBegin__ -->
|
||||
|
||||
## 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__ --> ## 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`. |
|
||||
<!-- __orxListEnd__ -->
|
||||
|
||||
# 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.
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.icegps.orx
|
||||
package com.icegps.geotools
|
||||
|
||||
import com.icegps.math.geometry.Angle
|
||||
import com.icegps.math.geometry.Vector3D
|
||||
@@ -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
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.icegps.orx
|
||||
package com.icegps.geotools
|
||||
|
||||
import android.graphics.PointF
|
||||
import android.util.Log
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.icegps.orx
|
||||
package com.icegps.geotools
|
||||
|
||||
import com.icegps.common.helper.GeoHelper
|
||||
import com.icegps.math.geometry.Vector2D
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.icegps.orx
|
||||
package com.icegps.geotools
|
||||
|
||||
import com.icegps.math.geometry.Vector2D
|
||||
import com.icegps.math.geometry.Vector3D
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.icegps.orx
|
||||
package com.icegps.geotools
|
||||
|
||||
import com.icegps.math.geometry.Vector2D
|
||||
import com.icegps.math.geometry.Vector3D
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.icegps.orx
|
||||
package com.icegps.geotools
|
||||
|
||||
import android.util.Log
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.icegps.orx.ktx
|
||||
package com.icegps.geotools.ktx
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.icegps.orx
|
||||
package com.icegps.geotools
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
val preload: SourceSet by project.sourceSets.creating
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
val libs = extensions.getByType<VersionCatalogsExtension>().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")
|
||||
@@ -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<FileCollection>
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@get:Input
|
||||
@get:Optional
|
||||
abstract val ignore: ListProperty<String>
|
||||
|
||||
@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 == "<!-- __demos__ -->" }
|
||||
if (screenshotsLine != -1) {
|
||||
readmeLines = readmeLines.subList(0, screenshotsLine)
|
||||
}
|
||||
readmeLines.add("<!-- __demos__ -->")
|
||||
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
|
||||
|
|
||||
|
|
||||
|
|
||||
|[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<CollectScreenshotsTask>("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
|
||||
}
|
||||
}
|
||||
@@ -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<String>
|
||||
|
||||
@get:Input
|
||||
abstract val defaultVisibility: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val namePrefix: Property<String>
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package org.openrndr.extra.convention
|
||||
|
||||
addHostMachineAttributesToRuntimeConfigurations()
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<KotlinCompile> {
|
||||
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<MavenPublication>("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)
|
||||
}
|
||||
}
|
||||
@@ -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<KotlinCompilationTask<*>> {
|
||||
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<KotlinJvmCompile>().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<CollectScreenshotsTask>("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<JavaExec>().matching { it.name == "jvmRun" }.configureEach {
|
||||
workingDir = rootDir
|
||||
val os: OperatingSystem? = DefaultNativePlatform.getCurrentOperatingSystem()
|
||||
if (os?.name == "Mac OS X") {
|
||||
setJvmArgs(listOf("-XstartOnFirstThread"))
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package org.openrndr.extra.convention
|
||||
|
||||
plugins {
|
||||
id("orx-variant")
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("orxVariants") {
|
||||
id = "orx-variant"
|
||||
implementationClass = "org.openrndr.extra.variant.plugin.VariantPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<in JvmComponentDependencies>) {
|
||||
action.execute(getDependencies())
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that this variant comes with a resource bundle.
|
||||
*/
|
||||
fun jar(action: Action<Unit>) {
|
||||
sourceSet.resources.srcDirs.add(sourceSet.java.srcDirs.first().parentFile.resolve("resources"))
|
||||
sourceSet.resources.includes.add("**/*.*")
|
||||
tasks.named<Jar>(sourceSet.jarTaskName).configure {
|
||||
include("**/*.*")
|
||||
dependsOn(tasks.named<ProcessResources>(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<Project> {
|
||||
override fun apply(target: Project) {
|
||||
val project = target
|
||||
project.extensions.create("variants", VariantExtension::class.java)
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#/bin/bash
|
||||
xvfb-run -e /dev/stdout java "$@"
|
||||
138
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 == "<!-- __orxListBegin__ -->" }
|
||||
def end = lines.findIndexOf { it == "<!-- __orxListEnd__ -->" }
|
||||
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<DependencyReportTask>("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}\"")
|
||||
}
|
||||
1
demo-data/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
**exported*
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 577 KiB |
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 840 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 826 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 26 KiB |
@@ -1,2 +0,0 @@
|
||||
# Blender 4.1.1 MTL File: 'None'
|
||||
# www.blender.org
|
||||
@@ -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/
|
||||
@@ -1,14 +0,0 @@
|
||||
smiling
|
||||
dumb
|
||||
happy
|
||||
red
|
||||
green
|
||||
blue
|
||||
pink
|
||||
white
|
||||
black
|
||||
purple
|
||||
crying
|
||||
running
|
||||
jumping
|
||||
eating
|
||||
@@ -1,14 +0,0 @@
|
||||
man
|
||||
woman
|
||||
child
|
||||
boy
|
||||
girl
|
||||
house
|
||||
flower
|
||||
chair
|
||||
table
|
||||
car
|
||||
ground
|
||||
garden
|
||||
airplane
|
||||
school
|
||||
@@ -1,14 +0,0 @@
|
||||
in
|
||||
under
|
||||
at
|
||||
on
|
||||
near
|
||||
amongst
|
||||
in front of
|
||||
behind
|
||||
below
|
||||
next to
|
||||
on top of
|
||||
opposite
|
||||
with
|
||||
close to
|
||||
@@ -1,14 +0,0 @@
|
||||
car
|
||||
fish
|
||||
flower
|
||||
eagle
|
||||
garbage
|
||||
computer
|
||||
beautiful
|
||||
ugly
|
||||
random
|
||||
man
|
||||
woman
|
||||
child
|
||||
missing
|
||||
magic
|
||||
@@ -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"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Vector2, Double> = 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<Segment2D>()
|
||||
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<List<ShapeContour>> = 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<Vector2>): 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<Vector3>): 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>): 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<Double, Double, Double> {
|
||||
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<Segment2D>): List<List<Segment2D>> {
|
||||
val remaining = segments.toMutableList()
|
||||
val allPaths = mutableListOf<List<Segment2D>>()
|
||||
|
||||
while (remaining.isNotEmpty()) {
|
||||
val path = mutableListOf<Segment2D>()
|
||||
|
||||
// 开始新路径
|
||||
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<Segment2D>): List<Segment2D> {
|
||||
if (segments.isEmpty()) return emptyList()
|
||||
|
||||
val remaining = segments.toMutableList()
|
||||
val connected = mutableListOf<Segment2D>()
|
||||
|
||||
// 构建端点查找表
|
||||
val startMap = mutableMapOf<Vector2, MutableList<Segment2D>>()
|
||||
val endMap = mutableMapOf<Vector2, MutableList<Segment2D>>()
|
||||
|
||||
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<Segment2D>): List<Segment2D> {
|
||||
if (segments.isEmpty()) return emptyList()
|
||||
|
||||
val remaining = segments.toMutableList()
|
||||
val connected = mutableListOf<Segment2D>()
|
||||
|
||||
// 从第一个线段开始,保持原方向
|
||||
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<Segment2D>): List<Segment2D> {
|
||||
if (lines.isEmpty()) return emptyList()
|
||||
|
||||
// 创建起点到线段的映射
|
||||
val startMap = lines.associateBy { it.start }
|
||||
val sorted = mutableListOf<Segment2D>()
|
||||
|
||||
// 找到起点(没有其他线段的终点指向它的起点)
|
||||
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<Segment2D>): List<Segment2D> {
|
||||
if (lines.isEmpty()) return emptyList()
|
||||
|
||||
val remaining = lines.toMutableList()
|
||||
val sorted = mutableListOf<Segment2D>()
|
||||
|
||||
// 从第一个线段开始
|
||||
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<Segment2D>): List<List<Segment2D>> {
|
||||
val remaining = lines.toMutableList()
|
||||
val loops = mutableListOf<List<Segment2D>>()
|
||||
|
||||
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<Segment2D>): List<Segment2D> {
|
||||
if (remaining.isEmpty()) return emptyList()
|
||||
|
||||
val loop = mutableListOf<Segment2D>()
|
||||
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<Vector3D> {
|
||||
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<Vector3D> {
|
||||
val center = Vector3D(0.0, 0.0, 0.0)
|
||||
val direction = Vector3D(0.0, 1.0, -1.0)
|
||||
return (0..360).step(36).map<Int, List<Vector3D>> { 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<Vector3D>.niceStr(): String {
|
||||
return joinToString(", ", "[", "]") {
|
||||
it.niceStr()
|
||||
}
|
||||
}
|
||||
@@ -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<Vector2, Double> = 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<Segment3D>()
|
||||
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<Vector3> = listOf(x1, x2, x3)
|
||||
}
|
||||
|
||||
fun connectAllSegments(segments: List<Segment3D>): List<List<Segment3D>> {
|
||||
val remaining = segments.toMutableList()
|
||||
val allPaths = mutableListOf<List<Segment3D>>()
|
||||
|
||||
while (remaining.isNotEmpty()) {
|
||||
val path = mutableListOf<Segment3D>()
|
||||
|
||||
// 开始新路径
|
||||
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
|
||||
}
|
||||
@@ -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<Vector2>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Vector3D> {
|
||||
val points = mutableListOf<Vector3D>()
|
||||
|
||||
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<Vector3D> {
|
||||
val points = mutableListOf<Vector3D>()
|
||||
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<Vector3D> {
|
||||
val points = mutableListOf<Vector3D>()
|
||||
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<Vector3D> {
|
||||
val points = mutableListOf<Vector3D>()
|
||||
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<Vector3D> {
|
||||
val points = mutableListOf<Vector3D>()
|
||||
|
||||
// 在一条线上生成多个火山
|
||||
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<LavaFlowInfo>): 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<VolcanoInfo> {
|
||||
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<LavaFlowInfo> {
|
||||
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()
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="48" height="48" version="1.2" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg"><rect width="12.7" height="12.7" fill="#ffc0cb" stroke-width="1.027"/><g stop-color="#000000" stroke-width=".6"><path d="m4.135 8.32-0.333-1.123h-1.383l-0.333 1.123h-0.4911l1.208-3.94h0.6265l1.208 3.94zm-0.9991-3.437h-0.0508l-0.5532 1.902h1.157z"/><path d="m5.314 8.32v-3.94h1.434q0.5475 0 0.8354 0.2992 0.2879 0.2992 0.2879 0.8354t-0.2879 0.8354q-0.2879 0.2992-0.8354 0.2992h-0.9596v1.671zm0.4741-2.083h0.9483q0.2992 0 0.4628-0.1468 0.1693-0.1524 0.1693-0.429v-0.2935q0-0.2766-0.1693-0.4233-0.1637-0.1524-0.4628-0.1524h-0.9483z"/><path d="m8.667 8.32v-0.3782h0.9821v-3.183h-0.9821v-0.3782h2.438v0.3782h-0.9821v3.183h0.9821v0.3782z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 785 B |
@@ -1,4 +0,0 @@
|
||||
|
||||
.library-name--link { text-transform: uppercase; }
|
||||
.library-version { top: 0px; margin-left: 10px; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" }
|
||||
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
5
gradlew
vendored
@@ -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" \
|
||||
"$@"
|
||||
|
||||
|
||||
3
gradlew.bat
vendored
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||