d2/align: Migrate code (indicated as broken by GCC) to Libale.
[Ale.git] / d3 / pt.h
blob45a1b9b346bf1e3892e5edab7780d9e284d32d08
1 // Copyright 2005 David Hilvert <dhilvert@auricle.dyndns.org>,
2 // <dhilvert@ugcs.caltech.edu>
4 /* This file is part of the Anti-Lamenessing Engine.
6 The Anti-Lamenessing Engine is free software; you can redistribute it
7 and/or modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 3 of the License,
9 or (at your option) any later version.
11 The Anti-Lamenessing Engine is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with the Anti-Lamenessing Engine; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * d3/et.h: Represent 3D->2D projective transformations.
25 #ifndef __pt_h__
26 #define __pt_h__
28 #include "space.h"
29 #include "et.h"
32 * Structure to describe a 3D->2D projective transformation. 3D information is
33 * preserved by adding a depth element to the result.
35 * The following coordinate systems are considered:
37 * P: projective
38 * C: local cartesian
39 * W: world
42 struct pt {
43 private:
44 d2::transformation t;
45 et euclidean;
46 ale_pos _view_angle;
47 ale_pos scale_factor;
48 mutable ale_pos diag_per_depth;
50 public:
53 * Constructor
56 pt() : t(d2::transformation::eu_identity()) {
57 _view_angle = M_PI / 4;
58 scale_factor = 1;
59 diag_per_depth = 0;
62 pt(d2::transformation t, et e, ale_pos va, ale_pos sf = 1) : t(t) {
63 this->t = t;
64 euclidean = e;
65 _view_angle = va;
66 scale_factor = sf;
67 diag_per_depth = 0;
71 * Output function
74 void debug_output() {
75 t.debug_output();
76 euclidean.debug_output();
77 fprintf(stderr, "[pt.do va=%f sf=%f dpd=%f\n]",
78 (double) _view_angle, (double) scale_factor, (double) diag_per_depth);
82 * Get euclidean transformation reference.
85 et &e() {
86 return euclidean;
90 * Modify scale factor
92 void scale(ale_pos sf) {
93 scale_factor = sf;
97 * Modify or get view angle
99 void view_angle(ale_pos va) {
100 diag_per_depth = 0;
101 _view_angle = va;
104 ale_pos view_angle() {
105 return _view_angle;
109 * Get the 2D scale factor
111 ale_pos scale_2d() const {
112 return t.scale();
116 * Transform W to C.
118 point wc(point p) const {
119 return euclidean(p);
123 * Transform C to P for given width and height.
125 point cp_generic(point p, ale_pos w, ale_pos h) const {
127 * Divide x and y by negative z
130 p[0] /= -p[2];
131 p[1] /= -p[2];
134 * Scale x and y
137 ale_pos scaling_factor = (ale_pos) sqrt(w*w + h*h)
138 / (ale_pos) (2 * tan(_view_angle / 2));
139 p[0] *= scaling_factor;
140 p[1] *= scaling_factor;
143 * Add an offset so that the upper-left corner is the origin.
146 p[0] += h / 2;
147 p[1] += w / 2;
149 return p;
153 * Transform point p.
155 struct point wp_generic(struct point p, ale_pos w, ale_pos h) const {
156 return cp_generic(wc(p), w, h);
160 * Width and height
163 ale_pos scaled_width() const {
164 return t.scaled_width() * scale_factor;
167 ale_pos scaled_height() const {
168 return t.scaled_height() * scale_factor;
171 int scaled_in_bounds(point p) const {
172 return (p[0] >= 0 && p[0] <= scaled_height() - 1
173 && p[1] >= 0 && p[1] <= scaled_width() - 1);
176 int unscaled_in_bounds(point p) const {
177 return (p[0] >= 0 && p[0] <= unscaled_height() - 1
178 && p[1] >= 0 && p[1] <= unscaled_width() - 1);
181 ale_pos unscaled_width() const {
182 return t.unscaled_width() * scale_factor;
185 ale_pos unscaled_height() const {
186 return t.unscaled_height() * scale_factor;
190 * Scaled transforms
193 point cp_scaled(point p) const {
194 return cp_generic(p, scaled_width(), scaled_height());
197 point wp_scaled(point p) const {
198 return wp_generic(p, scaled_width(), scaled_height());
202 * Unscaled transforms
205 point cp_unscaled(point p) const {
206 return cp_generic(p, unscaled_width(), unscaled_height());
209 point wp_unscaled(point p) const {
210 return wp_generic(p, unscaled_width(), unscaled_height());
214 * Transform P to C.
216 point pc_generic(point p, ale_pos w, ale_pos h) const {
218 * Subtract offset
221 p[0] -= h / 2;
222 p[1] -= w / 2;
225 * Scale x and y
228 ale_pos scaling_factor = (ale_pos) sqrt(w*w + h*h)
229 / (ale_pos) (2 * tan(_view_angle / 2));
230 p[0] /= scaling_factor;
231 p[1] /= scaling_factor;
234 * Multiply x and y by negative z
237 p[0] *= -p[2];
238 p[1] *= -p[2];
240 return p;
244 * Transform C to W
246 point cw(point p) const {
247 return euclidean.inverse_transform(p);
251 * Transform P to W
253 point pw_generic(point p, ale_pos w, ale_pos h) const {
254 return cw(pc_generic(p, w, h));
258 * Inverse transforms for scaled points.
261 point pc_scaled(point p) const {
262 return pc_generic(p, scaled_width(), scaled_height());
265 point pw_scaled(point p) const {
266 return pw_generic(p, scaled_width(), scaled_height());
270 * Inverse transforms for unscaled points.
273 point pc_unscaled(point p) const {
274 return pc_generic(p, unscaled_width(), unscaled_height());
277 point pw_unscaled(point p) const {
278 return pw_generic(p, unscaled_width(), unscaled_height());
282 * Density calculation
285 ale_pos c_density_scaled(point p) const {
286 ale_pos one_density = 1 / (pc_scaled(point(0, 0, -1)).lengthto(pc_scaled(point(0, 1, -1)))
287 * pc_scaled(point(0, 0, -1)).lengthto(pc_scaled(point(1, 0, -1))));
289 return one_density / (p[2] * p[2]);
292 ale_pos w_density_scaled(point p) const {
293 return c_density_scaled(wc(p));
296 ale_pos w_density_scaled_max(point w0, point w1, point w2) {
297 point c0 = wc(w0);
298 point c1 = wc(w1);
299 point c2 = wc(w2);
302 * Select the point closest to the camera.
305 if (c0[2] > c1[2] && c0[2] > c2[2])
306 return c_density_scaled(c0);
307 else if (c1[2] > c2[2])
308 return c_density_scaled(c1);
309 else
310 return c_density_scaled(c2);
313 private:
314 void calculate_diag_per_depth() const {
315 if (diag_per_depth != 0)
316 return;
317 ale_pos w = unscaled_width();
318 ale_pos h = unscaled_height();
320 diag_per_depth = sqrt(2) * (2 * tan(_view_angle / 2)) / sqrt(w*w + h*h);
323 public:
326 * Get a trilinear coordinate for a given depth.
328 ale_pos trilinear_coordinate(ale_pos depth, ale_pos diagonal) {
329 calculate_diag_per_depth();
331 return log(diagonal / (fabs(depth) * diag_per_depth)) / log(2);
336 * Get a trilinear coordinate for a given position in the world and
337 * a given 2D diagonal distance.
339 ale_pos trilinear_coordinate(point w, ale_pos diagonal) {
340 return trilinear_coordinate(wc(w)[2], diagonal);
344 * Get a trilinear coordinate for a given subspace.
346 ale_pos trilinear_coordinate(const space::traverse &st) {
347 point min = st.get_min();
348 point max = st.get_max();
349 point avg = (min + max) / (ale_pos) 2;
351 ale_pos diagonal = min.lengthto(max) * (ale_pos) (sqrt(2) / sqrt(3));
353 return trilinear_coordinate(avg, diagonal);
357 * Get a diagonal distance for a given position in the world
358 * and a given trilinear coordinate.
360 ale_pos diagonal_distance(point w, ale_pos coordinate) const {
361 calculate_diag_per_depth();
363 ale_pos depth = fabs(wc(w)[2]);
364 ale_pos diagonal = pow(2, coordinate) * depth * diag_per_depth;
366 return diagonal;
370 * Get the 3D diagonal for a given depth and trilinear coordinate.
372 ale_pos diagonal_distance_3d(ale_pos depth, ale_pos coordinate) const {
373 calculate_diag_per_depth();
374 return pow(2, coordinate) * fabs(depth) * diag_per_depth * (ale_pos) (sqrt(3) / sqrt(2));
378 * Get the 1D distance for a given depth and trilinear coordinate.
380 ale_pos distance_1d(ale_pos depth, ale_pos coordinate) const {
381 calculate_diag_per_depth();
382 return pow(2, coordinate) * fabs(depth) * diag_per_depth / (ale_pos) (sqrt(2));
385 ale_pos distance_1d(point iw, ale_pos coordinate) const {
386 if (wc(iw)[2] >= 0)
387 return point::undefined()[0];
388 return distance_1d(-wc(iw)[2], coordinate);
392 * Check for inclusion of a point in the bounding box of projected
393 * vertices. This function returns non-zero when a point is included,
394 * when one of the vertices is infinite or undefined, or when a vertex
395 * is behind the point of projection.
397 * WBB is assumed to contain {volume_min, volume_max}.
400 int check_inclusion(const point *wbb, const d2::point &pc_min, const d2::point &pc_max, int scaled) const {
402 assert(pc_min[0] <= pc_max[0]);
403 assert(pc_min[1] <= pc_max[1]);
405 int test[2][2] = {{0, 0}, {0, 0}};
407 for (int x = 0; x < 2; x++)
408 for (int y = 0; y < 2; y++)
409 for (int z = 0; z < 2; z++) {
411 point p = scaled ? wp_scaled(point(wbb[x][0], wbb[y][1], wbb[z][2]))
412 : wp_unscaled(point(wbb[x][0], wbb[y][1], wbb[z][2]));
414 if (!p.finite())
415 return 1;
417 if (p[2] > 0)
418 return 1;
420 for (int d = 0; d < 2; d++) {
421 if (p[d] <= pc_max[d])
422 test[d][0] = 1;
423 if (p[d] >= pc_min[d])
424 test[d][1] = 1;
428 for (int d = 0; d < 2; d++)
429 for (int c = 0; c < 2; c++)
430 if (test[d][c] == 0)
431 return 0;
433 return 1;
436 int check_inclusion_scaled(const point *wbb, d2::point pc_min, d2::point pc_max) const {
437 return check_inclusion(wbb, pc_min, pc_max, 1);
440 int check_inclusion_scaled(const space::traverse &st, d2::point pc_min, d2::point pc_max) const {
441 return check_inclusion_scaled(st.get_bounds(), pc_min, pc_max);
444 int check_inclusion_scaled(const space::traverse &st, d2::point pc) {
445 return check_inclusion_scaled(st, pc, pc);
449 * Get bounding box for projection of a subspace.
452 void get_view_local_bb(point volume_min, point volume_max, point bb[2], int scaled) const {
454 point min = point::posinf();
455 point max = point::neginf();
457 point wbb[2] = { volume_min, volume_max };
459 for (int x = 0; x < 2; x++)
460 for (int y = 0; y < 2; y++)
461 for (int z = 0; z < 2; z++) {
462 point p = scaled ? wp_scaled(point(wbb[x][0], wbb[y][1], wbb[z][2]))
463 : wp_unscaled(point(wbb[x][0], wbb[y][1], wbb[z][2]));
465 for (int d = 0; d < 3; d++) {
466 if (p[d] < min[d])
467 min[d] = p[d];
468 if (p[d] > max[d])
469 max[d] = p[d];
474 * Clip bounding box to image extents.
477 if (min[0] < 0)
478 min[0] = 0;
479 if (min[1] < 0)
480 min[1] = 0;
481 if (max[0] > scaled_height() - 1)
482 max[0] = scaled_height() - 1;
483 if (max[1] > scaled_width() - 1)
484 max[1] = scaled_width() - 1;
486 bb[0] = min;
487 bb[1] = max;
490 void get_view_local_bb_unscaled(point volume_min, point volume_max, point bb[2]) const {
491 get_view_local_bb(volume_min, volume_max, bb, 0);
494 void get_view_local_bb_scaled(point volume_min, point volume_max, point bb[2]) const {
495 get_view_local_bb(volume_min, volume_max, bb, 1);
498 void get_view_local_bb_scaled(const space::traverse &st, point bb[2]) const {
499 get_view_local_bb_scaled(st.get_min(), st.get_max(), bb);
502 void get_view_local_bb_unscaled(const space::traverse &t, point bb[2]) const {
503 get_view_local_bb_unscaled(t.get_min(), t.get_max(), bb);
507 * Get the in-bounds centroid for a subspace, if one exists.
510 point centroid(point volume_min, point volume_max) const {
512 point bb[2];
514 get_view_local_bb_unscaled(volume_min, volume_max, bb);
516 point min = bb[0];
517 point max = bb[1];
519 for (int d = 0; d < 2; d++)
520 if (min[d] > max[d])
521 return point::undefined();
523 if (min[2] >= 0)
524 return point::undefined();
526 if (max[2] > 0)
527 max[2] = 0;
529 return (max + min) / 2;
532 point centroid(const space::traverse &t) {
533 return centroid(t.get_min(), t.get_max());
537 * Get the local space origin in world space.
540 point origin() {
541 return cw(point(0, 0, 0));
545 #endif