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.
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:
48 mutable ale_pos diag_per_depth
;
56 pt() : t(d2::transformation::eu_identity()) {
57 _view_angle
= M_PI
/ 4;
62 pt(d2::transformation t
, et e
, ale_pos va
, ale_pos sf
= 1) : t(t
) {
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.
92 void scale(ale_pos sf
) {
97 * Modify or get view angle
99 void view_angle(ale_pos va
) {
104 ale_pos
view_angle() {
109 * Get the 2D scale factor
111 ale_pos
scale_2d() const {
118 point
wc(point p
) const {
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
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.
155 struct point
wp_generic(struct point p
, ale_pos w
, ale_pos h
) const {
156 return cp_generic(wc(p
), w
, h
);
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
;
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());
216 point
pc_generic(point p
, ale_pos w
, ale_pos h
) const {
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
246 point
cw(point p
) const {
247 return euclidean
.inverse_transform(p
);
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
) {
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
);
310 return c_density_scaled(c2
);
314 void calculate_diag_per_depth() const {
315 if (diag_per_depth
!= 0)
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
);
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
;
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 {
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]));
420 for (int d
= 0; d
< 2; d
++) {
421 if (p
[d
] <= pc_max
[d
])
423 if (p
[d
] >= pc_min
[d
])
428 for (int d
= 0; d
< 2; d
++)
429 for (int c
= 0; c
< 2; c
++)
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
++) {
474 * Clip bounding box to image extents.
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;
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 {
514 get_view_local_bb_unscaled(volume_min
, volume_max
, bb
);
519 for (int d
= 0; d
< 2; d
++)
521 return point::undefined();
524 return point::undefined();
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.
541 return cw(point(0, 0, 0));