Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More Makie integration #6

Merged
merged 29 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
30dbde4
Run all documentation steps under xvfb
asinghvi17 May 20, 2024
12af00d
Add Makie packages to docs project
asinghvi17 May 20, 2024
1154a4b
Copy the README over to docs/index.md
asinghvi17 May 20, 2024
1a39a20
Add a Makie demo page
asinghvi17 May 20, 2024
7e53145
Ensure all camera attributes are updated in Makie
asinghvi17 May 20, 2024
587c797
Add recipe header function definitions
asinghvi17 May 20, 2024
5cf2b9e
Define `duration` for paths
asinghvi17 May 20, 2024
cdef405
Define a Makie recipe to plot camera paths
asinghvi17 May 20, 2024
371bdaf
Add a Makie recipe to visualize camera paths
asinghvi17 May 20, 2024
2ff8622
Add a basic example to the doc page
asinghvi17 May 20, 2024
d40ce35
Add camera animation
asinghvi17 May 20, 2024
e69d663
Add converts and a `record` shortcut
asinghvi17 May 20, 2024
36a20b5
Add a view frustum visualization too
asinghvi17 May 20, 2024
7d3f852
Remove pipes
asinghvi17 May 20, 2024
6d11a18
Add developer docs
asinghvi17 May 24, 2024
7396e6c
julia -> repl blocks in docs md
asinghvi17 May 24, 2024
50e4486
Move interface definitions to `interfaces.jl` as function stubs
asinghvi17 May 24, 2024
a2fab89
Remove recipe.jl
asinghvi17 May 24, 2024
f259b56
Add comments + more methods to method error hint
asinghvi17 May 24, 2024
28a6e03
Document `PathChange`
asinghvi17 May 24, 2024
016629f
Update CI.yml
asinghvi17 May 24, 2024
88f2935
Enable preview docs
asinghvi17 May 26, 2024
bcdff2b
Fix YAML syntax error
asinghvi17 May 26, 2024
450131a
More PathChange docs
asinghvi17 May 26, 2024
84eb0c0
Build docs with `warnonly=true`
asinghvi17 May 26, 2024
2879d35
Add docstring to `Path`
asinghvi17 May 26, 2024
6b8e9f1
Update makie.md
asinghvi17 May 27, 2024
6cae4cf
Try to fix doctest CI
asinghvi17 May 28, 2024
432b8c0
Add more docstrings + document functions not structs
asinghvi17 May 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 25 additions & 22 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,33 +45,36 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
docs:
name: Documentation
name: Build and deploy documentation
runs-on: ubuntu-latest
permissions:
actions: write # needed to allow julia-actions/cache to proactively delete old caches that it has created
contents: write
statuses: write
steps:
- name: Install binary dependencies
run: sudo apt-get update && sudo apt-get install -y xorg-dev mesa-utils xvfb libgl1 freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
- name: Download all workflow run artifacts
uses: actions/download-artifact@v4
- uses: julia-actions/setup-julia@latest
with:
version: '1'
- uses: julia-actions/cache@v2
- name: Configure doc environment
shell: julia --project=docs --color=yes {0}
- name: Install documentation dependencies
run: |
using Pkg
Pkg.develop(PackageSpec(path=pwd()))
Pkg.instantiate()
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-docdeploy@v1
xvfb-run -s '-screen 0 1024x768x24' julia --project=docs -e '
using Pkg
Pkg.develop(PackageSpec(path=pwd()))
Pkg.add(name = "DocumenterVitepress", rev = "master")
Pkg.instantiate()'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}
- name: Run doctests
shell: julia --project=docs --color=yes {0}
run: |
using Documenter: DocMeta, doctest
using FlyThroughPaths
DocMeta.setdocmeta!(FlyThroughPaths, :DocTestSetup, :(using FlyThroughPaths); recursive=true)
doctest(FlyThroughPaths)
DISPLAY: ':0'
- name: Build and deploy
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # If authenticating with GitHub Actions token
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # If authenticating with SSH deploy key
DISPLAY: ':0'
run: xvfb-run -s '-screen 0 1024x768x24' julia --project=docs/ docs/make.jl deploy
- name: Run doctests
run: julia --project=docs -e '
using Documenter;
using FlyThroughPaths;
Documenter.DocMeta.setdocmeta!(FlyThroughPaths, :DocTestSetup, :(using FlyThroughPaths); recursive=true);
Documenter.doctest(FlyThroughPaths);'
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,22 +139,22 @@ You need to load the visualization package, e.g., `using GLMakie`, in your sessi

This can be handy for constructing a path, for example you can interactively set the approximate position and view parameters and then query them for use by the tools above.

```
```julia
state = capture_view(scene)
```

`state` is a `ViewState` object.

### Setting the current view state

```
```julia
oldstate = set_view!(camera, path, t)
```

This updates the current `camera` settings from `path` at time `t`.

### Displaying the path

```
```julia
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the move to Documenter docs, I think we could also slim the README a lot. Otherwise we have two places we have to keep updated.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can definitely slim the README down (and translate the examples to Documenter syntax)!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example translation should be done, but I still have to slim down the README.

plot(path)
```
4 changes: 4 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[deps]
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
FlyThroughPaths = "c11bb9a7-2755-425a-88f3-ebe93bbdb91f"
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
MakieCore = "20f20a25-4f0e-4fdf-b5d1-57303727442b"
4 changes: 4 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ makedocs(;
),
pages=[
"Home" => "index.md",
"Makie integration" => "makie.md",
"Developer documentation" => "devdocs.md"
],
warnonly=true,
)

deploydocs(;
repo="github.com/HolyLab/FlyThroughPaths.jl",
devbranch="main",
push_preview=true,
)
16 changes: 16 additions & 0 deletions docs/src/devdocs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Developer documentation

## Implementing support for FlyThroughPaths

FlyThroughPaths operates on the [`ViewState`](@ref) model. In order to implement support for this in a plotting package, you must implement dispatches for the following two functions:
- `capture_view(obj)::ViewState`: extract the current `ViewState`, i.e., camera settings, from `obj`.
- `set_view!(obj, viewstate::ViewState)`: set the camera to the given `ViewState`.

Integration is already implemented for Makie; you can see that in `ext/FlyThroughPathsMakieExt.jl`. The first ~20 lines are the most instructive, beyond which lie utility functions and visualization specializations.

## The `PathChange` interface

```@docs
PathChange
duration
```
140 changes: 137 additions & 3 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,143 @@ CurrentModule = FlyThroughPaths

Documentation for [FlyThroughPaths](https://github.com/HolyLab/FlyThroughPaths.jl).

```@index

All of the examples below assume you've loaded the package with `using FlyThroughPaths`.

# Quick start

## Generic tools

### Representation of paths and view state

Paths are parametrized by time `t`, represented in units of seconds. All paths implicitly start at `t=0`.

The representation of view state is independent of any particular plotting package, although our parametrization is inspired by [Makie's 3D camera](https://docs.makie.org/stable/explanations/cameras/#3d_camera):

- `eyeposition`: the 3d coordinates of the camera
- `lookat`: the 3d coordinates of the point of the camera's "focus" (center of gaze)
- `upvector`: the 3d direction that will correspond to the top of the view. Any component of this vector in the direction of `lookat - eyeposition` is ignored/discarded.
- `fov`: the angle (in degrees) of the cone centered on `lookat - eyeposition` that should be captured.

Set these as follows:

```@repl main
using FlyThroughPaths
state = ViewState(eyeposition=[-10, 0, 0], lookat=[0, 0, 0], upvector=[0, 0, 1], fov=45)
```

You can set just a subset of these:
```@repl main
newstate = ViewState(eyeposition=[-5, 0, 0])
```

This syntax is often used for updating a previous view; for the unspecified settings, the previous value is left intact.


### Initializing a path

```@repl main
path = Path(state)
```

The path starts at `state` at time `t=0`.

### Evaluating at a particular time

Once you have a path, you can get the current `ViewState` with `path(t)`:

```@repl main
path(0)

path(10)
```

So far, nothing much is happening. Things get more interesting when we add movements.

### Holding steady

The simplest thing you can do is insert a pause:

```@repl main
path2 = path * Pause(5)
```

The view will hold steady for 5 seconds. Typically you add `Pause` when you also plan to add other movements later.

### Moving the camera, option 1: constrained movements

This option is typically used for things like rotations around a center point.

```@repl main
path2 = path * ConstrainedMove(5, newstate; constraint=:none, speed=:constant)
```

```@autodocs
Modules = [FlyThroughPaths]
This indicates that over a 5-second period, the camera state gradually adopts any values specified in `newstate`.

```@repl main
path2(0)
path2(5)
path2(2.5)
```

Keyword options include:

- `constraint` (`:none` or `:rotation`): specify a value to keep constant during motion. `:rotation` performs a rotation around `lookat`. Note that if the separation between `eyeposition` and `lookat` is not constant, then the trajectory will be elliptical rather than circular.
- `speed` controls how the change is made across time:
- `:constant`: speed is instantaneously set to a new constant value that will arrive at the endpoint at the specified time
- `:sinusoidal`: speed will initially increase (starting at a speed of 0), achieve a maximum at the midpoint, and then decrease back to 0.


### Moving the camera, option 2: Bezier movements

With this option, you can approximately simulate the feeling of flight, preserving momentum:

```
path2 = path * BezierMove(Δt::Real, P1::ViewState, P2::ViewState...)
```

where a `bezier` path is specified as indicated in this diagram:

![bezier diagram](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Bezier_curve.svg/640px-Bezier_curve.svg.png)

The starting state, `P0` in the diagram, is taken from the endpoint of `path`. Over the next `Δt` seconds, one then moves towards the last `ViewState` argument of `bezier`, orienting successively towards any prior arguments. Probably the most robust option is to use `bezier(Δt, P1, P2, P3)`, which can be interpreted as "depart `P0` traveling towards `P1`, and arrive at `P3` as if you had come from `P2`." The view does not actually pass through `P1` and `P2`, but these set the initial and final tangents of the curve.

To see this in action, let's create a move that "rotates" around the origin but moves outward (to a more distant orbit) on its way there:

```@repl main
move = BezierMove(5, ViewState(eyeposition=[0, 10, 0]), [ViewState(eyeposition=[-20, 20, 0])])
path2 = path * move;
path2(2.5)
```

## Backend-specific tools

These require interaction with a plotting package supported by one of the extensions. Currently supported:

- [Makie](https://docs.makie.org/stable/)

You need to load the visualization package, e.g., `using GLMakie`, in your session before any of the commands below will work.

### Capturing the current view state

This can be handy for constructing a path, for example you can interactively set the approximate position and view parameters and then query them for use by the tools above.

```julia
state = capture_view(scenelike::Union{Scene, LScene})
```

`state` is a `ViewState` object.

### Setting the current view state

```julia
oldstate = set_view!(scenelike, path, t)
```

This updates the current `camera` settings from `path` at time `t`.

### Displaying the path

```julia
plot(path)
```
Loading