[orx-fcurve] Update readme

This commit is contained in:
Abe Pazos
2024-03-29 14:04:38 +01:00
parent 48bdcc0c60
commit 67f6ab82cd

View File

@@ -1,10 +1,10 @@
# orx-fcurve
FCurves are 1 dimensional function curves constructed from 2D bezier functions.
They are often used to control a property over time.
`x` values don't have any units, but they often represent a duration in seconds.
The language to express Fcurves is similar to SVG's path language.
`x` values usually represent duration in seconds.
The language to express FCurves is similar to SVG's path language.
| Relative command | Absolute command | Description |
|---------------------|-----------------------|-------------------------------------------------------------|
@@ -16,26 +16,147 @@ The language to express Fcurves is similar to SVG's path language.
| `t x,y` | `T x,y` | quadratic smooth to (x, y) |
| `s x1,y1,x,y` | `S x1,y1,x,y` | cubic smooth to (x,y) and control point (x1, y1) |
## Example Fcurves
## Examples
`M0 l5,10 q4,-10` or `M0 l5 10 q4 -10`
This is an example of a flat horizontal FCurve:
`M0 h10 c3,10,5,-10,8,0.5 L5,5`
```kotlin
// set the initial value to 0.5, hold that value for 1 seconds
val sizeCurve = fcurve("M0.5 H1")
```
New lines and commas are optional. They can help with readability.
Two horizontal segments at different heights:
```kotlin
// hold value 0.4 for half second, then hold value 0.6 for half second
val sizeCurve = fcurve("M0.4 h0.5 M0.6 h0.5")
```
Note that `x` values are relative, except for `H` where `x` is absolute.
For `y` values, lower case commands are relative and upper case commands are absolute.
The last example can be written with absolute times as:
```kotlin
// hold value 0.4 until time 0.5, then hold value 0.6 until time 1.0
val sizeCurve = fcurve("M0.4 H0.5 M0.6 H1.0")
```
### Line
We can interpolate from height 0.2 to 0.8 in 2 seconds like this:
```kotlin
// set initial value to 0.2, then interpolate linearly to value 0.8 over 2 seconds
val sizeCurve = fcurve("M0.2 L2,0.8")
```
Easily visualize the curves by calling the `.contours()` method. It will convert
the curve into a list of `ShapeContour` instances which are easy to draw using
`drawer.contours()`:
```kotlin
val sizeCurve = fcurve("M0.2 L2,0.8")
drawer.contours(sizeCurve.contours())
```
### Drawing scale
Note that the bounding box of this last curve will have a width of 2.0 pixels and a height under 1.0 pixel.
In other words, almost invisible at its original scale.
Since this is a common situation the `.contours()` method accepts a
`Vector2` scale argument to control the rendering size:
```kotlin
val sizeCurve = fcurve("M0.2 L2,0.8")
drawer.contours(sizeCurve.contours(Vector2(drawer.width / sizeCurve.duration, drawer.height.toDouble())))
```
### Quadratic and Cubic curves
The `Q` and `C` commands (and their lowercase counterparts) allow us to draw quadratic (one control point)
and cubic (two control points) curves.
```kotlin
// A quadratic curve that starts at zero, with a control point at 1,0 and ending at 1,1.
// That's a curve that stays near the 0.0 value and quickly raises to 1.0 at the end.
val easeOutCurve = fcurve("M0.0 Q1.0,0.0,1.0,1.0")
// A cubic s-shaped curve spending more time at both ends with a quick transition between them in the middle.
val easeInOutCurve = fcurve("M0.0 C1,0,0,1,1,1")
```
Note that new lines, white space and commas are optional. They can help with readability:
```
M0 h10
c3,10,5,-10,8,0.5
L5,5
c 3,10 5,-10 8,0.5
L 5,5
```
### Smooth curves
The `T` and `S` commands (and their lowercase counterparts) allow us to create smooth curves, where
one control point is automatically calculated to maintain the curve direction. The smooth curve
commands require the presence of a previous segment, otherwise the program will not run.
```kotlin
// Hold the value 0.5 during 0.2 seconds
// then draw a smooth curve down to 0.5, up to 0.7 down to 0.3 and up to 0.7
val smoothCurveT = fcurve("M0.5 H0.2 T0.2,0.3 T0.2,0.7 T0.2,0.3 T0.2,0.7")
// Hold the value 0.5 during 0.2 seconds
// then draw a smooth with 4 repetitions where we move up slowly and down quickly
val smoothCurveS = fcurve("M0.5 H0.2 S0.2,0.0,0.2,0.5 S0.2,0.0,0.2,0.5 S0.2,0.0,0.2,0.5 S0.2,0.0,0.2,0.5")
```
## Useful FCurve methods
Useful methods provided by FCurve:
- `smoothCurveS.reverse()` returns a new reversed FCurve.
- `smoothCurveS.changeSpeed(0.5)` returns a new FCurve scaled horizontally.
- `smoothCurveS.duration` returns the duration of the FCurve.
# Sampler
Drawing FCurves is useful for debugging, but their typical use is for animation.
The FCurve sampler allows us to query values for the given time value like this:
```kotlin
fun main() {
application {
program {
val xCurve = fcurve(
"""
M320 H0.4
S2,0, 2,320
S2,0, 2,320
S2,0, 2,320
S2,0, 2,320
T0.6,320
""".trimIndent()
).sampler() // <--
extend {
drawer.circle(
xCurve(seconds % 9.0),
height * 0.5,
20.0
)
}
}
}
}
```
In this example we used `% 9.0` to loop the time between 0.0 and 9.0, repeating the animation over and over.
# EFCurves
EFCurves are Fcurves with an additional preprocessing step in which scalar expressions are evaluated.
Extended Fcurves have a additional preprocessing step in which scalar expressions are evaluated.
## Comments
EFCurves add support for comments using the `#` character.
EFCurves support comments using the `#` character.
`M0 h10 c3,10,5,-10,8,0.5 # L5,5`
@@ -49,10 +170,10 @@ L5,5
## Expressions
For example: `M0 L_3 * 4_,4` evaluates to `M0 L12,4`.
Expressions wrapped in underscore characters (`_`) are evaluated using `orx-expression-evaluator`.
Please refer to its [documentation](https://github.com/openrndr/orx/tree/master/orx-expression-evaluator) for details on the expression language used.
`orx-expression-evaluator` is used to evaluate the expressions, please refer to its
documentation for details on the expression language used.
For example: `M0 L_3 * 4_,4` evaluates to `M0 L12,4`.
## Repetitions
@@ -74,16 +195,14 @@ For example `|M0 |h1 m1|[3]|[2]` expands to `M0 h1 m1 h1 m1 h1 m1 M0 h1 m1 h1 m1
`M0 |H_it + 1_ m1][3]` expands to `M0 H1 m1 H2 m1 H3 m1`
`M0 |H_index + 1_ m_it_]{1.2, 1.3, 1.4}` expands to `M0 H1 m1.2 H2 m1.3 H3 m1.4`
# References
* https://x.com/ruby0x1/status/1258252352672247814
* https://blender.stackexchange.com/questions/52403/what-is-the-mathematical-basis-for-f-curves/52468#52468
* https://pomax.github.io/bezierinfo/#yforx
<!-- __demos__ -->
## Demos
### DemoFCurve01