Add subspace candidate resolution checking code to d3::scene.
[Ale.git] / d3 / pt.h
blob6b356abd282e7496f9552af7674d5cc3da73ba9c
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.
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_real _view_angle; /* XXX: should be ale_pos */
47 ale_pos scale_factor;
48 ale_pos diag_per_depth;
50 public:
53 * Constructor
56 pt() {
57 _view_angle = M_PI / 4;
58 scale_factor = 1;
59 diag_per_depth = 0;
62 pt(d2::transformation t, et e, ale_real va, ale_pos sf = 1) {
63 this->t = t;
64 euclidean = e;
65 _view_angle = va;
66 scale_factor = sf;
67 diag_per_depth = 0;
71 * Get euclidean transformation reference.
74 et &e() {
75 return euclidean;
79 * Modify scale factor
81 void scale(ale_pos sf) {
82 scale_factor = sf;
86 * Modify or get view angle
88 void view_angle(ale_pos va) {
89 diag_per_depth = 0;
90 _view_angle = va;
93 ale_pos view_angle() {
94 return _view_angle;
98 * Get the 2D scale factor
100 ale_pos scale_2d() const {
101 return t.scale();
105 * Transform W to C.
107 point wc(point p) const {
108 return euclidean(p);
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
119 p[0] /= -p[2];
120 p[1] /= -p[2];
123 * Scale x and y
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.
134 p[0] += h / 2;
135 p[1] += w / 2;
137 return p;
141 * Transform point p.
143 struct point wp_generic(struct point p, ale_pos w, ale_pos h) const {
144 return cp_generic(wc(p), w, h);
148 * Width and height
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 int unscaled_in_bounds(point p) const {
165 return (p[0] >= 0 && p[0] <= unscaled_height() - 1
166 && p[1] >= 0 && p[1] <= unscaled_width() - 1);
169 ale_pos unscaled_width() const {
170 return t.unscaled_width() * scale_factor;
173 ale_pos unscaled_height() const {
174 return t.unscaled_height() * scale_factor;
178 * Scaled transforms
181 point cp_scaled(point p) const {
182 return cp_generic(p, scaled_width(), scaled_height());
185 point wp_scaled(point p) const {
186 return wp_generic(p, scaled_width(), scaled_height());
190 * Unscaled transforms
193 point cp_unscaled(point p) const {
194 return cp_generic(p, unscaled_width(), unscaled_height());
197 point wp_unscaled(point p) const {
198 return wp_generic(p, unscaled_width(), unscaled_height());
202 * Transform P to C.
204 point pc_generic(point p, ale_pos w, ale_pos h) const {
206 * Subtract offset
209 p[0] -= h / 2;
210 p[1] -= w / 2;
213 * Scale x and y
216 ale_pos scaling_factor = sqrt(w*w + h*h) / (2 * tan(_view_angle / 2));
217 p[0] /= scaling_factor;
218 p[1] /= scaling_factor;
221 * Multiply x and y by negative z
224 p[0] *= -p[2];
225 p[1] *= -p[2];
227 return p;
231 * Transform C to W
233 point cw(point p) const {
234 return euclidean.inverse_transform(p);
238 * Transform P to W
240 point pw_generic(point p, ale_pos w, ale_pos h) const {
241 return cw(pc_generic(p, w, h));
245 * Inverse transforms for scaled points.
248 point pc_scaled(point p) const {
249 return pc_generic(p, scaled_width(), scaled_height());
252 point pw_scaled(point p) const {
253 return pw_generic(p, scaled_width(), scaled_height());
257 * Inverse transforms for unscaled points.
260 point pc_unscaled(point p) const {
261 return pc_generic(p, unscaled_width(), unscaled_height());
264 point pw_unscaled(point p) const {
265 return pw_generic(p, unscaled_width(), unscaled_height());
269 * Density calculation
272 ale_pos c_density_scaled(point p) const {
273 ale_pos one_density = 1 / (pc_scaled(point(0, 0, -1)).lengthto(pc_scaled(point(0, 1, -1)))
274 * pc_scaled(point(0, 0, -1)).lengthto(pc_scaled(point(1, 0, -1))));
276 return one_density / (p[2] * p[2]);
279 ale_pos w_density_scaled(point p) const {
280 return c_density_scaled(wc(p));
283 ale_pos w_density_scaled_max(point w0, point w1, point w2) {
284 point c0 = wc(w0);
285 point c1 = wc(w1);
286 point c2 = wc(w2);
289 * Select the point closest to the camera.
292 if (c0[2] > c1[2] && c0[2] > c2[2])
293 return c_density_scaled(c0);
294 else if (c1[2] > c2[2])
295 return c_density_scaled(c1);
296 else
297 return c_density_scaled(c2);
300 private:
301 void calculate_diag_per_depth() {
302 if (diag_per_depth != 0)
303 return;
304 ale_pos w = unscaled_width();
305 ale_pos h = unscaled_height();
307 diag_per_depth = sqrt(2) * (2 * tan(_view_angle / 2)) / sqrt(w*w + h*h);
310 public:
313 * Get a trilinear coordinate for a given position in the world and
314 * a given 2D diagonal distance.
316 ale_pos trilinear_coordinate(point w, ale_pos diagonal) {
317 calculate_diag_per_depth();
319 ale_pos depth = fabs(wc(w)[2]);
321 ale_pos coord = log(diagonal / (depth * diag_per_depth)) / log(2);
323 return coord;
327 * Get a trilinear coordinate for a given subspace.
329 ale_pos trilinear_coordinate(const space::traverse &st) {
330 point min = st.get_min();
331 point max = st.get_max();
332 point avg = (min + max) / (ale_pos) 2;
334 ale_pos diagonal = min.lengthto(max) * sqrt(2) / sqrt(3);
336 return trilinear_coordinate(avg, diagonal);
340 * Get a diagonal distance for a given position in the world
341 * and a given trilinear coordinate.
343 ale_pos diagonal_distance(point w, ale_pos coordinate) {
344 calculate_diag_per_depth();
346 ale_pos depth = fabs(wc(w)[2]);
347 ale_pos diagonal = pow(2, coordinate) * depth * diag_per_depth;
349 return diagonal;
353 * Get the 3D diagonal for a given depth and trilinear coordinate.
355 ale_pos diagonal_distance_3d(ale_pos depth, ale_pos coordinate) {
356 return pow(2, coordinate) * fabs(depth) * diag_per_depth * sqrt(3) / sqrt(2);
360 * Get bounding box for projection of a subspace.
363 void get_view_local_bb(const space::traverse &st, point bb[2]) {
365 point min = point::posinf();
366 point max = point::neginf();
368 point wbb[2] = { st.get_min(), st.get_max() };
371 for (int x = 0; x < 2; x++)
372 for (int y = 0; y < 2; y++)
373 for (int z = 0; z < 2; z++) {
374 point p = wp_scaled(point(wbb[x][0], wbb[y][1], wbb[z][2]));
376 if (p[0] < min[0])
377 min[0] = p[0];
378 if (p[0] > max[0])
379 max[0] = p[0];
380 if (p[1] < min[1])
381 min[1] = p[1];
382 if (p[1] > max[1])
383 max[1] = p[1];
384 if (p[2] < min[2])
385 min[2] = p[2];
386 if (p[2] > max[2])
387 max[2] = p[2];
391 * Clip bounding box to image extents.
394 if (min[0] < 0)
395 min[0] = 0;
396 if (min[1] < 0)
397 min[1] = 0;
398 if (max[0] > scaled_height() - 1)
399 max[0] = scaled_height() - 1;
400 if (max[1] > scaled_width() - 1)
401 max[1] = scaled_width() - 1;
403 bb[0] = min;
404 bb[1] = max;
408 * Get the in-bounds centroid for a subspace, if one exists.
411 point centroid(point min, point max) {
412 for (int d = 0; d < 2; d++)
413 if (min[d] > max[d])
414 return point::undefined();
416 if (min[2] > 0)
417 return point::undefined();
419 return (bb[0] + bb[1]) / 2;
422 point centroid(const space::traverse &t) {
423 point bb[2];
425 get_view_local_bb(t, bb);
427 return centroid(bb[0], bb[1]);
431 * Get the local space origin in world space.
434 point origin() {
435 return cw(point(0, 0, 0));
440 #endif