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 2 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:
46 ale_real _view_angle
; /* XXX: should be ale_pos */
48 ale_pos diag_per_depth
;
57 _view_angle
= M_PI
/ 4;
62 pt(d2::transformation t
, et e
, ale_real va
, ale_pos sf
= 1) {
71 * Get euclidean transformation reference.
81 void scale(ale_pos sf
) {
86 * Modify or get view angle
88 void view_angle(ale_pos va
) {
93 ale_pos
view_angle() {
98 * Get the 2D scale factor
100 ale_pos
scale_2d() const {
107 point
wc(point p
) const {
112 * Transform C to P for given width and height.
114 point
cp_generic(point p
, ale_pos w
, ale_pos h
) const {
116 * Divide x and y by negative z
126 ale_pos scaling_factor
= sqrt(w
*w
+ h
*h
) / (2 * tan(_view_angle
/ 2));
127 p
[0] *= scaling_factor
;
128 p
[1] *= scaling_factor
;
131 * Add an offset so that the upper-left corner is the origin.
143 struct point
wp_generic(struct point p
, ale_pos w
, ale_pos h
) const {
144 return cp_generic(wc(p
), w
, h
);
151 ale_pos
scaled_width() const {
152 return t
.scaled_width() * scale_factor
;
155 ale_pos
scaled_height() const {
156 return t
.scaled_height() * scale_factor
;
159 int scaled_in_bounds(point p
) const {
160 return (p
[0] >= 0 && p
[0] <= scaled_height() - 1
161 && p
[1] >= 0 && p
[1] <= scaled_width() - 1);
164 ale_pos
unscaled_width() const {
165 return t
.unscaled_width() * scale_factor
;
168 ale_pos
unscaled_height() const {
169 return t
.unscaled_height() * scale_factor
;
176 point
cp_scaled(point p
) const {
177 return cp_generic(p
, scaled_width(), scaled_height());
180 point
wp_scaled(point p
) const {
181 return wp_generic(p
, scaled_width(), scaled_height());
185 * Unscaled transforms
188 point
cp_unscaled(point p
) const {
189 return cp_generic(p
, unscaled_width(), unscaled_height());
192 point
wp_unscaled(point p
) const {
193 return wp_generic(p
, unscaled_width(), unscaled_height());
199 point
pc_generic(point p
, ale_pos w
, ale_pos h
) const {
211 ale_pos scaling_factor
= sqrt(w
*w
+ h
*h
) / (2 * tan(_view_angle
/ 2));
212 p
[0] /= scaling_factor
;
213 p
[1] /= scaling_factor
;
216 * Multiply x and y by negative z
228 point
cw(point p
) const {
229 return euclidean
.inverse_transform(p
);
235 point
pw_generic(point p
, ale_pos w
, ale_pos h
) const {
236 return cw(pc_generic(p
, w
, h
));
240 * Inverse transforms for scaled points.
243 point
pc_scaled(point p
) const {
244 return pc_generic(p
, scaled_width(), scaled_height());
247 point
pw_scaled(point p
) const {
248 return pw_generic(p
, scaled_width(), scaled_height());
252 * Inverse transforms for unscaled points.
255 point
pc_unscaled(point p
) const {
256 return pc_generic(p
, unscaled_width(), unscaled_height());
259 point
pw_unscaled(point p
) const {
260 return pw_generic(p
, unscaled_width(), unscaled_height());
264 * Density calculation
267 ale_pos
c_density_scaled(point p
) const {
268 ale_pos one_density
= 1 / (pc_scaled(point(0, 0, -1)).lengthto(pc_scaled(point(0, 1, -1)))
269 * pc_scaled(point(0, 0, -1)).lengthto(pc_scaled(point(1, 0, -1))));
271 return one_density
/ (p
[2] * p
[2]);
274 ale_pos
w_density_scaled(point p
) const {
275 return c_density_scaled(wc(p
));
278 ale_pos
w_density_scaled_max(point w0
, point w1
, point w2
) {
284 * Select the point closest to the camera.
287 if (c0
[2] > c1
[2] && c0
[2] > c2
[2])
288 return c_density_scaled(c0
);
289 else if (c1
[2] > c2
[2])
290 return c_density_scaled(c1
);
292 return c_density_scaled(c2
);
296 void calculate_diag_per_depth() {
297 if (diag_per_depth
!= 0)
299 ale_pos w
= unscaled_width();
300 ale_pos h
= unscaled_height();
302 diag_per_depth
= sqrt(2) * (2 * tan(_view_angle
/ 2)) / sqrt(w
*w
+ h
*h
);
308 * Get a trilinear coordinate for a given position in the world and
309 * a given 2D diagonal distance.
311 ale_pos
trilinear_coordinate(point w
, ale_pos diagonal
) {
312 calculate_diag_per_depth();
314 ale_pos depth
= wc(w
)[2];
316 ale_pos coord
= log(diagonal
/ (depth
* diag_per_depth
)) / log(2);
322 * Get a trilinear coordinate for a given subspace.
324 ale_pos
trilinear_coordinate(const space::traverse
&st
) {
325 point min
= st
.get_min();
326 point max
= st
.get_max();
327 point avg
= (min
+ max
) / (ale_pos
) 2;
329 ale_pos diagonal
= min
.lengthto(max
) * sqrt(2) / sqrt(3);
331 return trilinear_coordinate(avg
, diagonal
);
335 * Get a diagonal distance for a given position in the world
336 * and a given trilinear coordinate.
338 ale_pos
diagonal_distance(point w
, ale_pos coordinate
) {
339 calculate_diag_per_depth();
341 ale_pos depth
= wc(w
)[2];
342 ale_pos diagonal
= pow(2, coordinate
) * depth
* diag_per_depth
;
348 * Get bounding box for projection of a subspace.
351 void get_view_local_bb(const space::traverse
&st
, point bb
[2]) {
353 point min
= point::posinf();
354 point max
= point::neginf();
356 point wbb
[2] = { st
.get_min(), st
.get_max() };
359 for (int x
= 0; x
< 2; x
++)
360 for (int y
= 0; y
< 2; y
++)
361 for (int z
= 0; z
< 2; z
++) {
362 point p
= wp_scaled(point(wbb
[x
][0], wbb
[y
][1], wbb
[z
][2]));
379 * Clip bounding box to image extents.
386 if (max
[0] > scaled_height() - 1)
387 max
[0] = scaled_height() - 1;
388 if (max
[1] > scaled_width() - 1)
389 max
[1] = scaled_width() - 1;
396 * Get the in-bounds centroid for a subspace, if one exists.
399 point
centroid(const space::traverse
&t
) {
402 get_view_local_bb(t
, bb
);
407 for (int d
= 0; d
< 2; d
++)
409 return point::undefined();
412 return point::undefined();
414 return (bb
[0] + bb
[1]) / 2;
418 * Get the local space origin in world space.
422 return cw(point(0, 0, 0));