|
4 | 4 | [](https://HolyLab.github.io/FlyThroughPaths.jl/dev/) |
5 | 5 | [](https://github.com/HolyLab/FlyThroughPaths.jl/actions/workflows/CI.yml?query=branch%3Amain) |
6 | 6 | [](https://codecov.io/gh/HolyLab/FlyThroughPaths.jl) |
| 7 | + |
| 8 | + |
| 9 | +All of the examples below assume you've loaded the package with `using FlyThroughPaths`. |
| 10 | + |
| 11 | +## Generic tools |
| 12 | + |
| 13 | +### Representation of paths and view state |
| 14 | + |
| 15 | +Paths are parametrized by time `t`, represented in units of seconds. All paths implicitly start at `t=0`. |
| 16 | + |
| 17 | +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): |
| 18 | + |
| 19 | +- `eyeposition`: the 3d coordinates of the camera |
| 20 | +- `lookat`: the 3d coordinates of the point of the camera's "focus" (center of gaze) |
| 21 | +- `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. |
| 22 | +- `fov`: the angle (in degrees) of the cone centered on `lookat - eyeposition` that should be captured. |
| 23 | + |
| 24 | +Set these as follows: |
| 25 | + |
| 26 | +```julia |
| 27 | +julia> state = ViewState(eyeposition=[-10, 0, 0], lookat=[0, 0, 0], upvector=[0, 0, 1], fov=45) |
| 28 | +ViewState{Float32}(eyeposition=[-10.0, 0.0, 0.0], lookat=[0.0, 0.0, 0.0], upvector=[0.0, 0.0, 1.0], fov=45.0) |
| 29 | +``` |
| 30 | + |
| 31 | +You can set just a subset of these: |
| 32 | +```julia |
| 33 | +julia> newstate = ViewState(eyeposition=[-5, 0, 0]) |
| 34 | +ViewState{Float32}(eyeposition=[-5.0, 0.0, 0.0]) |
| 35 | +``` |
| 36 | + |
| 37 | +This syntax is often used for updating a previous view; for the unspecified settings, the previous value is left intact. |
| 38 | + |
| 39 | + |
| 40 | +### Initializing a path |
| 41 | + |
| 42 | +```julia |
| 43 | +julia> path = Path(state) |
| 44 | +Path{Float32}(ViewState{Float32}(eyeposition=[-10.0, 0.0, 0.0], lookat=[0.0, 0.0, 0.0], upvector=[0.0, 0.0, 1.0], fov=45.0), FlyThroughPaths.PathChange{Float32}[]) |
| 45 | +``` |
| 46 | + |
| 47 | +The path starts at `state` at time `t=0`. |
| 48 | + |
| 49 | +### Evaluating at a particular time |
| 50 | + |
| 51 | +Once you have a path, you can get the current `ViewState` with `path(t)`: |
| 52 | + |
| 53 | +```julia |
| 54 | +julia> path(0) |
| 55 | +ViewState{Float32}(eyeposition=[-10.0, 0.0, 0.0], lookat=[0.0, 0.0, 0.0], upvector=[0.0, 0.0, 1.0], fov=45.0) |
| 56 | + |
| 57 | +julia> path(10) |
| 58 | +ViewState{Float32}(eyeposition=[-10.0, 0.0, 0.0], lookat=[0.0, 0.0, 0.0], upvector=[0.0, 0.0, 1.0], fov=45.0) |
| 59 | +``` |
| 60 | + |
| 61 | +So far, nothing much is happening. Things get more interesting when we add movements. |
| 62 | + |
| 63 | +### Holding steady |
| 64 | + |
| 65 | +The simplest thing you can do is insert a pause: |
| 66 | + |
| 67 | +```julia |
| 68 | +julia> path2 = path * Pause(5) |
| 69 | +Path{Float32}(ViewState{Float32}(eyeposition=[-10.0, 0.0, 0.0], lookat=[0.0, 0.0, 0.0], upvector=[0.0, 0.0, 1.0], fov=45.0), FlyThroughPaths.PathChange{Float32}[Pause{Float32}(5.0f0, nothing)]) |
| 70 | +``` |
| 71 | + |
| 72 | +The view will hold steady for 5 seconds. Typically you add `Pause` when you also plan to add other movements later. |
| 73 | + |
| 74 | +### Moving the camera, option 1: constrained movements |
| 75 | + |
| 76 | +This option is typically used for things like rotations around a center point. |
| 77 | + |
| 78 | +```julia |
| 79 | +julia> path2 = path * ConstrainedMove(5, newstate; constraint=:none, speed=:constant) |
| 80 | +Path{Float32}(ViewState{Float32}(eyeposition=[-10.0, 0.0, 0.0], lookat=[0.0, 0.0, 0.0], upvector=[0.0, 0.0, 1.0], fov=45.0), FlyThroughPaths.PathChange{Float32}[ConstrainedMove{Float32}(5.0f0, ViewState{Float32}(eyeposition=[-5.0, 0.0, 0.0]), :none, :constant, nothing)]) |
| 81 | +``` |
| 82 | + |
| 83 | +This indicates that over a 5-second period, the camera state gradually adopts any values specified in `newstate`. |
| 84 | + |
| 85 | +```julia |
| 86 | +julia> path2(0) |
| 87 | +ViewState{Float32}(eyeposition=[-10.0, 0.0, 0.0], lookat=[0.0, 0.0, 0.0], upvector=[0.0, 0.0, 1.0], fov=45.0) |
| 88 | + |
| 89 | +julia> path2(5) |
| 90 | +ViewState{Float32}(eyeposition=[-5.0, 0.0, 0.0], lookat=[0.0, 0.0, 0.0], upvector=[0.0, 0.0, 1.0], fov=45.0) |
| 91 | + |
| 92 | +julia> path2(2.5) |
| 93 | +ViewState{Float32}(eyeposition=[-7.5, 0.0, 0.0], lookat=[0.0, 0.0, 0.0], upvector=[0.0, 0.0, 1.0], fov=45.0) |
| 94 | +``` |
| 95 | + |
| 96 | +Keyword options include: |
| 97 | + |
| 98 | +- `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. |
| 99 | +- `speed` controls how the change is made across time: |
| 100 | + - `:constant`: speed is instantaneously set to a new constant value that will arrive at the endpoint at the specified time |
| 101 | + - `:sinusoidal`: speed will initially increase (starting at a speed of 0), achieve a maximum at the midpoint, and then decrease back to 0. |
| 102 | + |
| 103 | + |
| 104 | +### Moving the camera, option 2: Bezier movements |
| 105 | + |
| 106 | +With this option, you can approximately simulate the feeling of flight, preserving momentum: |
| 107 | + |
| 108 | +``` |
| 109 | +path2 = path * BezierMove(Δt::Real, P1::ViewState, P2::ViewState...) |
| 110 | +``` |
| 111 | + |
| 112 | +where a `bezier` path is specified as indicated in this diagram: |
| 113 | + |
| 114 | + |
| 115 | + |
| 116 | +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. |
| 117 | + |
| 118 | +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: |
| 119 | + |
| 120 | +```julia |
| 121 | +julia> move = BezierMove(5, ViewState(eyeposition=[0, 10, 0]), [ViewState(eyeposition=[-20, 20, 0])]) |
| 122 | +BezierMove{Float32}(5.0f0, ViewState{Float32}(eyeposition=[0.0, 10.0, 0.0]), ViewState{Float32}[ViewState{Float32}(eyeposition=[-20.0, 20.0, 0.0])], nothing) |
| 123 | + |
| 124 | +julia> path2 = path * move; |
| 125 | + |
| 126 | +julia> path2(2.5) |
| 127 | +ViewState{Float32}(eyeposition=[-12.5, 12.5, 0.0], lookat=[0.0, 0.0, 0.0], upvector=[0.0, 0.0, 1.0], fov=45.0) |
| 128 | +``` |
| 129 | + |
| 130 | +## Backend-specific tools |
| 131 | + |
| 132 | +These require interaction with a plotting package supported by one of the extensions. Currently supported: |
| 133 | + |
| 134 | +- [Makie](https://docs.makie.org/stable/) |
| 135 | + |
| 136 | +You need to load the visualization package, e.g., `using GLMakie`, in your session before any of the commands below will work. |
| 137 | + |
| 138 | +### Capturing the current view state |
| 139 | + |
| 140 | +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. |
| 141 | + |
| 142 | +``` |
| 143 | +state = capture_view(scene) |
| 144 | +``` |
| 145 | + |
| 146 | +`state` is a `ViewState` object. |
| 147 | + |
| 148 | +### Setting the current view state |
| 149 | + |
| 150 | +``` |
| 151 | +oldstate = set_view!(camera, path, t) |
| 152 | +``` |
| 153 | + |
| 154 | +This updates the current `camera` settings from `path` at time `t`. |
| 155 | + |
| 156 | +### Displaying the path |
| 157 | + |
| 158 | +``` |
| 159 | +plot(path) |
| 160 | +``` |
0 commit comments