You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-**Raymarcher** — sphere-tracing renderer with materials, lighting, refraction, and anti-aliasing
18
+
-**Differentiable rendering** — gradients flow through the full render pipeline via JAX
19
+
-**Constraint system** — geometric constraints (distance, angle, coincident) with Riemannian gradient descent and Newton projection onto the constraint manifold
20
+
-**JAX-native** — every scene is a pure function; `jit`, `grad`, and `vmap` work out of the box
## Constraint example — Riemannian gradient descent
100
-
101
-
Move a point along a constraint manifold using Riemannian gradient descent: gradient steps stay on the tangent plane and a Newton projection snaps back to the manifold after each step.
102
-
103
-
```python
104
-
import jax
105
-
import jax.numpy as jnp
106
-
import optax
107
-
108
-
from jaxcad.constraints import (
109
-
DistanceConstraint, Vector,
110
-
null_space, make_manifold_projection,
111
-
)
112
-
from jaxcad.extraction import extract_parameters
113
-
114
-
# Constrain p to lie on the sphere |p| = 2
115
-
anchor = Vector(jnp.array([0.0, 0.0, 0.0]))
116
-
p = Vector(jnp.array([2.0, 0.0, 0.0]), free=True, name="p")
117
-
DistanceConstraint(anchor, p, distance=2.0)
118
-
119
-
free_params, _, metadata = extract_parameters(p)
120
-
target = jnp.array([1.0, 1.5, 0.0])
121
-
122
-
defobjective(params):
123
-
return jnp.sum((params["p"] - target) **2)
124
-
125
-
value_and_grad = jax.value_and_grad(objective)
126
-
127
-
defriemannian_grad(params):
128
-
"""Project gradient onto the tangent plane at the current point."""
129
-
N = null_space(params, metadata) # tangent-space basis (relinearized)
130
-
loss, g = value_and_grad(params)
131
-
return loss, N @ (g @ N) # Riemannian gradient
132
-
133
-
# Riemannian GD: tangent-plane steps + manifold projection after each update
134
-
optimizer = optax.chain(
135
-
optax.sgd(0.15),
136
-
make_manifold_projection(metadata), # Newton snap-back onto |p|=2
137
-
)
138
-
139
-
params = free_params
140
-
state = optimizer.init(params)
141
-
for step inrange(20):
142
-
loss, g = riemannian_grad(params)
143
-
updates, state = optimizer.update(g, state, params)
144
-
params = optax.apply_updates(params, updates)
145
-
146
-
print(params["p"]) # [1.109, 1.664, 0.] — optimal point on |p|=2 closest to target
147
-
```
148
-
149
-
`null_space` recomputes the constraint Jacobian at the current point each step, so the gradient is always projected onto the correct tangent plane. `make_manifold_projection` chains as a standard optax transform and works with any base optimizer.
150
-
151
-
---
152
-
153
52
Inspired by [Fidget](https://www.mattkeeter.com/projects/fidget/) and [Inigo Quilez's distance functions](https://iquilezles.org/articles/distfunctions/).
154
53
155
54
---
156
55
157
-

56
+

158
57
159
58
## License
160
59
161
-
[GNU Affero General Public License v3.0](LICENSE) — free for open source use; commercial use requires a separate license. Contact the authors if you want to use jaxcad in a proprietary product.
60
+
[Elastic License 2.0](LICENSE) — free for personal, research, and internal business use. Offering jaxcad as a hosted or managed service requires a commercial license. Contact [andrin.rehmann@simulation.science](mailto:andrin.rehmann@simulation.science) for commercial enquiries.
0 commit comments