forked from curv3d/curv
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTODO-Geom
325 lines (269 loc) · 11.5 KB
/
TODO-Geom
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# Redesign Shape API
## New Shape Type
`make_shape` is superceded by `Shape` which has a new interface.
dist[x,y,z,t] is replaced by distance[x,y,z] // reactive time variable
colour[x,y,z,t] is replaced by colour[x,y,z] // reactive time variable
bbox is replaced by bounds // more legible name
## Scalar Fields
A scalar field maps [x,y] or [x,y,z] onto a number.
(Like a generalized intensity field.)
Scalar fields can replace numbers as arguments to shape operators.
## TODO:
* repeat_x repeat_xy repeat_xyz
* Fix distance field. Evaluate cell field *2 for _x, *4 for _xy, *8 for _xyz.
Maybe a 'fast:' option to disable this.
* API for finite repetition. range: option.
* Support parallelogram shaped cells like ShapeJS? Then maybe repeat_x name
is misleading. Also, what is name for hex shaped cells?
rep_strip, rep_rect, rep_box.
* repeat_mirror_x
Maybe use `repeat_mirror normal`, after `reflect` API.
* `cone {d, h}`:
* What is API for infinite cone?
Maybe cone{h,normal} or cone{h,slope} or cone{h,angle},
with apex at origin, and h can be inf.
* Maybe finite cone should be centred vertically.
* Change bbox to {min:vec,max:vec}? Use bbox.min instead of bbox[MIN].
3 overloadings of rect:
rect(dx,dy)
rect{min:(xmin,ymin), max:(xmax,ymax)}
rect{xmin=-inf,ymin=-inf,xmax=inf,ymax=inf}
## Intensity Fields and Transformations
An ifield is represented by (a) a function, (b) a record containing an
`intensity` field, which is a function. And other optional metadata?
Like, the gradient of the ifield at a given point?
Transformations of ifields. There should be a way to apply a stock
transformation to an ifield.
1. transform.f is a function mapping [x,y,z,t] to [x,y,z,t].
compose[transform.f, ifield]
2. ifields are records.
A transform maps a shape-or-ifield to another shape-or-ifield.
This complicates the implementation of transforms, but maybe we introduce
a helper function to reduce the code.
make_transform {
f(x,y,z,t)=...; // forward coordinate transform
inv(x,y,z,t)=...; // inverse coordinate transform
make_dist shape = ...; // optional; map shape to dist function
make_bbox shape = ...; // optional; map shape to bbox
}
make_transform warp shape =
If we gonna generalize transformations, then how about:
* Apply a transformation to a point, or a list of points.
* Compose transformations.
## Tilt
* `tilt axis transform shape`
Modify 'transform' so that the local +Z axis is 'axis'.
Same as: `tilt{from: Z_axis, to: axis} ...`.
Eg, torus{minor:1, major:2} >> tilt X_axis (twist (tau/4))
* `tilt{from: v1, to: v2} transform shape`
tilt{from: v1, to: v2} transform shape =
if (v1 == v2)
transform shape
else
shape >> rotate{from:v1, to:v2} >> transform >> rotate{from:v2, to:v1};
Transforms that benefit from tilt (or, add an axis argument to each one):
repeat_x, repeat_xy, repeat_xyz, repeat_mirror_x, repeat_radial
align
shear_x, shear_xy
local_taper_x, local_taper_xy
twist
bend
swirl
2D->3D transforms: can't use tilt, need to post-rotate
pancake
perimeter_extrude
extrude
revolve
3D->2D transforms: can't use tilt, need to pre-rotate the 3D shape.
slice
One downside is that some transformations have a default axis of Z_axis
(eg, rotate), while others have a default axis of X_axis.
Tilt, as specified here, doesn't work for local_bend because it doesn't
specify an 'up' vector.
## 3D->2D
existing: slice_xy, slice_xz, slice_yz
Can these be combined into a single, more general primitive?
Ideas:
1. `slice shape`
Intersect shape with the XY plane, don't move result out of XY plane.
This is the most natural case for `slice`. To intersect a 3D shape with
other planes and leave result in the XY plane, pre-rotate the shape.
Eg, shape>>rotate{from:Z_axis,to:X_axis}>>slice is like slice_yz shape.
2. `slice axis shape`.
'axis' is normal to the plane used to slice 'shape'. Plane intersects
origin. Similar to `reflect` API. Eg, `slice Z_axis shape` is like slice_xy.
`slice X_axis` is like slice_yz.
3. `slice shape`
`slice axis shape`
Overloaded function.
## Interpolators
(An interpolator is a function that maps an intensity value between 0 and 1
onto a value.)
d3-interpolate is awesome:
https://github.com/d3/d3-interpolate
Curried lerp: lerp (a,b) i
## Transformations
### Scale vs Stretch
Similarity Transformations (including `scale`) preserve the structure of
the distance field.
Deformations (including `stretch`) do not preserve distance field structure.
* distinguish `scale` from `stretch`
### Shear
Is there a generalization of `shear_x` and `shear_xy`? Yes.
Should they be combined into a single generic operation `shear`? Not sure.
A 2D shear is characterized by an invariant line L and a Num shear factor K.
Lines parallel to L are translated proportional to K and their perpendicular
distance from L. Eg, existing `shear_x` fixes L as the X axis.
A 3D shear is characterized by an invariant plane P and a Vec2 shear factor K.
Planes parallel to P are translated proportional to K and their perpendicular
distance from P. Eg, existing `shear_xy` fixes P as the XY plane.
We can generalize `shear_x` and `shear_xy` into `shear2d` and `shear3d`,
by allowing the invariant line/plane to be specified as an optional argument.
We'll restrict the invariant line/plane to pass through the origin. Then,
shear2d{is_num k, is_vec2 invar = X_axis}
shear3d{is_vec2 k, invar:(v1,v2)}
I could combine shear2d and shear3d into a single overloaded `shear` function,
but maybe I shouldn't.
Here's another path, which I won't follow:
* shear(kx,ky) or skew(anglex,angley) is commonly used by 2D graphics APIs.
* shear([xy,xz],[yx,yz],[zx,zy]) is the 3D generalization (since a 3D
transformation matrix has 6 shear parameters). Too many parameters.
### Reflect
reflect axis = with_rotation{from:axis, to:X_axis} reflect_x
where
reflect_x shape = ...;
with_rotation{from: v1, to: v2} transform shape =
if (v1 == v2)
transform shape
else
shape >> rotate{from:v1, to:v2} >> transform >> rotate{from:v2, to:v1};
rotate a =
a >> match [
{from: (is_vec2 v1), to: (is_vec2 v2)} ->
...
{from: (make_vec3 >> v1), to: (make_vec3 >> v2)} ->
...
...
];
X_axis = [1,0];
Y_axis = [0,1];
Z_axis = [0,0,1];
or use current axis definitions with make_vec2.
## Row
row d shapes
row {d,axis} shapes
## Rotate_repeat
Better if the cell to repeat is above the origin.
Consistent with cylinder_transform.
Also, this constructs a shape that is mirror-symmetric about the Y axis,
which is the best axis to choose for human psychology reasons.
## Barr's Bend (local bend)
local_bend{range:[xs,xe], centre|fixed_point: x0, angle}
A more general interface would let you specify the bend axis.
Let's specify the bending line as {origin, axis}, where origin is also the
fixed point. Then {range:[xs,xe]} are interpreted as coordinates on the
bending line. But then, you also need an 'up' vector.
local_bend{range:[xs,xe], angle, origin=[0,0,0], axis=X_axis, up=Y_axis}
As long as the Z components of origin, axis and up are 0, then this works on
2D and 3D shapes. Otherwise, it is 3D only.
Finally, can I simplify this by eliminating the origin and axis arguments,
and using `at` and `tilt` instead? But `tilt` doesn't specify an `up` vector.
## Convention: bend/radial-repeat parameters
Here's a consistent convention:
* repeat_radial takes a shape centred on the Y axis, below the X axis,
and duplicates it N times around a circle. This is the convention needed
by regular_polygon.
* bend takes a strip centred on the Y axis, below the X axis,
and wraps it around a cylinder. The two ends meet at the top.
Same bending direction as local_bend. Same convention as RingWrap.
* local_bend takes a strip parallel to the X axis, and bends it.
Positive angle means bend upwards (same direction as bend).
Same convention as las/Mercury's Barr bend.
## Linear Repeat
repeat_x (dx) shape
repeat_xy (dx,dy) shape
repeat_xyz (dx,dy,dz) shape
Need a finite version. In repeat_x, the cell centred at the origin has
ordinal 0, and the general sequence is ...,-3,-2,-1,-,1,2,3,...
repeat_x {size:dx, range:(first,last)} shape
repeat_x {d:dx, range:(first,last)} shape
repeat_xy {d:(dx,dy), range:[(firstx,lastx), (firsty,lasty)]}
repeat_xy {d:(dx,dy), xrange:(firstx,lastx), yrange:(firsty,lasty)}
-- if you omit xrange or yrange, they default to infinite
repeat_xy {d:(dx,dy), from:(firstx,firsty), to:(lastx,lasty)}
## Axis Parameters.
Should I add optional 'axis' parameters to transformations where it is
appropriate?
* 'rotate' already has an axis parameter. This is due to existing industry
convention where a 3D rotation may be represented by a quaternion,
by {axis,angle}, or other ways.
* reflect_x, reflect_y and reflect_z? Or reflect axis shape?
* Replace row_x with row, add optional axis argument. 'row {d,axis} shape'.
* I considered 'along axis transform shape', similar to 'at'.
Different transformations have different default axes, for good reason,
so this does not end up being simpler in any way.
* 'with_rotation{from: v1, to: v2} transform shape'
* X_axis, Y_axis, Z_axis are all vec3. This creates a type error when a 2D
axis is needed. So maybe I accept a vec3 with a 0 Z component as a vec2
as an axis argument. How?
* is_axis2 a = is_vec2 a || (is_vec3 a && a[Z]==0);
Then ignore the Z component of 'a', or use a[[X,Y]], or:
let axis = make_axis2 a;
* Implement cast patterns?
reflect axis shape =
//Huge case analysis to optimize axis values that are aligned with the X, Y
//or Z axes. Or, rotate the axis onto the X axis, then implement one case,
//then rotate back. Put the optimizations into rotate or with_rotation.
with_rotation{from:axis, to:X_axis} reflect_x
where
reflect_x = ...;
with_rotation{from: v1, to: v2} transform shape =
if (v1 == v2)
transform shape
else
shape >> rotate{from:v1, to:v2} >> transform >> rotate{from:v2, to:v1};
## axis-aligned rectangle (bbox2)
A bbox2 is represented as a matrix, ((xmin,ymin),(xmax,ymax)).
The top level elements are referenced using bb[MIN] and bb[MAX].
Maybe use a record instead?
{min:(xmin,ymin), max:(xmax,ymax)}
Then use bb.min and bb.max.
Maybe support a variant,
{xmin=-inf,ymin=-inf,zmin=-inf,xmax=inf,ymax=inf,zmax=inf}
which is useful for cropping along specified boundaries using `intersection`.
make_bbox2 is a constructor that accepts either form.
rect b =
if (is_vec2 b)
...
else
let bb = make_bbox2 b;
in ...;
Or BBox2 if I implement coercions.
rect = switch [
(Vec2 b) -> ...;
(BBox2 b) -> ...;
];
BBox2 = switch [
b@(Vec2 _, Vec2 _) -> b;
{xmin=-inf, ymin=-inf, xmax=inf, ymax=inf} -> ((xmin,ymin),(xmax,ymax));
];
Vec2 = switch [
(Num n) -> (n,n);
(v@(Num _, Num _)) -> v;
];
With these definitions,
BBox2{} == BBox2(-inf,inf) == BBox2((-inf,-inf),(inf,inf))
and it's necessary for the definition of `rect` to list the Vec2 case first,
otherwise it's matched by BBox2. It's easy to get carried away by too many
default conversions. Let's use the "predicate pattern" design instead.
rect = switch [
(Vec2 b) -> ...;
(BBox2 b) -> ...;
{Num xmin=-inf, Num ymin=-inf, Num xmax=inf, Num ymax=inf} ->
rect((xmin,ymin),(xmax,ymax));
];
## rect
3 overloadings of rect:
rect(dx,dy)
rect{min:(xmin,ymin), max:(xmax,ymax)}
rect{xmin=-inf,ymin=-inf,zmin=-inf,xmax=inf,ymax=inf,zmax=inf}