|
|
0662a2 |
|
|
|
4dc49c |
#include <iostream></iostream>
|
|
|
4dc49c |
|
|
|
b4eaab |
#include "perspective.h"
|
|
|
0662a2 |
#include "perspectivesandboxview.h"
|
|
|
0662a2 |
|
|
|
0662a2 |
|
|
|
0662a2 |
PerspectiveSandBoxView::PerspectiveSandBoxView():
|
|
|
0662a2 |
persp_p0 (new View::Point( Vector2(-1, -1) )),
|
|
|
0662a2 |
persp_px (new View::Point( Vector2( 1, -1) )),
|
|
|
0662a2 |
persp_py (new View::Point( Vector2(-1, 1) )),
|
|
|
0662a2 |
persp_p1 (new View::Point( Vector2( 1, 1) )),
|
|
|
0662a2 |
bounds_p0 (new View::Point( Vector2(-2, -2) )),
|
|
|
0662a2 |
bounds_p1 (new View::Point( Vector2( 2, 2) ))
|
|
|
0662a2 |
{
|
|
|
0662a2 |
transform.scale(50, 50);
|
|
|
0662a2 |
points.push_back(persp_p0);
|
|
|
0662a2 |
points.push_back(persp_px);
|
|
|
0662a2 |
points.push_back(persp_py);
|
|
|
0662a2 |
points.push_back(persp_p1);
|
|
|
0662a2 |
points.push_back(bounds_p0);
|
|
|
0662a2 |
points.push_back(bounds_p1);
|
|
|
0662a2 |
}
|
|
|
0662a2 |
|
|
|
b4eaab |
|
|
|
b4eaab |
static Vector2 calc_optimal_resolution(
|
|
|
b4eaab |
const Vector2 &ox,
|
|
|
b4eaab |
const Vector2 &oy )
|
|
|
b4eaab |
{
|
|
|
b4eaab |
const Real a = ox.x * ox.x;
|
|
|
b4eaab |
const Real b = ox.y * ox.y;
|
|
|
b4eaab |
const Real c = oy.x * oy.x;
|
|
|
b4eaab |
const Real d = oy.y * oy.y;
|
|
|
b4eaab |
Real e = fabs(ox.x*oy.y - ox.y*oy.x);
|
|
|
b4eaab |
if (e < real_precision_sqr)
|
|
|
b4eaab |
return Vector2(); // paranoid check, e must be non-zero when matrix is invertible
|
|
|
b4eaab |
e = 1.0/e;
|
|
|
b4eaab |
|
|
|
b4eaab |
const Real sum = a*d + b*c;
|
|
|
b4eaab |
Vector2 scale;
|
|
|
b4eaab |
if (2*a*b + real_precision_sqr >= sum) {
|
|
|
b4eaab |
scale.x = sqrt(2*b)*e;
|
|
|
b4eaab |
scale.y = sqrt(2*a)*e;
|
|
|
b4eaab |
} else
|
|
|
b4eaab |
if (2*c*d + real_precision_sqr >= sum) {
|
|
|
b4eaab |
scale.x = sqrt(2*d)*e;
|
|
|
b4eaab |
scale.y = sqrt(2*c)*e;
|
|
|
b4eaab |
} else {
|
|
|
b4eaab |
const Real dif = a*d - b*c;
|
|
|
b4eaab |
scale.x = sqrt(dif/(a - c))*e;
|
|
|
b4eaab |
scale.y = sqrt(dif/(d - b))*e;
|
|
|
b4eaab |
}
|
|
|
b4eaab |
|
|
|
b4eaab |
return scale;
|
|
|
b4eaab |
}
|
|
|
b4eaab |
|
|
|
b4eaab |
|
|
|
b4eaab |
void PerspectiveSandBoxView::update_background(
|
|
|
b4eaab |
const Matrix3 &matrix,
|
|
|
b4eaab |
int width,
|
|
|
b4eaab |
int height )
|
|
|
b4eaab |
{
|
|
|
b4eaab |
DataSurface surface(width, height);
|
|
|
b4eaab |
for(int r = 0; r < height; ++r) {
|
|
|
b4eaab |
for(int c = 0; c < width; ++c) {
|
|
|
b4eaab |
Vector3 v = matrix*Vector3(c, r, 1);
|
|
|
b4eaab |
if (v.z <= real_precision)
|
|
|
b4eaab |
continue;
|
|
|
b4eaab |
|
|
|
b4eaab |
const Real k = 1/(v.z*v.z);
|
|
|
b4eaab |
const Vector2 ox = Vector2( v.z*matrix.m00 - matrix.m02*v.x,
|
|
|
b4eaab |
v.z*matrix.m10 - matrix.m12*v.x )*k;
|
|
|
b4eaab |
const Vector2 oy = Vector2( v.z*matrix.m01 - matrix.m02*v.y,
|
|
|
b4eaab |
v.z*matrix.m11 - matrix.m12*v.y )*k;
|
|
|
b4eaab |
const Vector2 resolution = calc_optimal_resolution(ox, oy)*100;
|
|
|
b4eaab |
if (resolution.x <= real_precision || resolution.y <= real_precision)
|
|
|
b4eaab |
continue;
|
|
|
b4eaab |
|
|
|
b4eaab |
const int lx = (int)round(log2(resolution.x));
|
|
|
b4eaab |
const int ly = (int)round(log2(resolution.y));
|
|
|
b4eaab |
Color color;
|
|
|
b4eaab |
color.r = lx < 0 ? lx%2 + 1 : lx%2;
|
|
|
b4eaab |
color.g = 0;
|
|
|
b4eaab |
color.b = ly < 0 ? ly%2 + 1 : ly%2;
|
|
|
b4eaab |
color.a = 1;
|
|
|
b4eaab |
surface[r][c] = color;
|
|
|
b4eaab |
}
|
|
|
b4eaab |
}
|
|
|
b4eaab |
background = surface.to_cairo_surface();
|
|
|
b4eaab |
}
|
|
|
b4eaab |
|
|
|
0662a2 |
void
|
|
|
0662a2 |
PerspectiveSandBoxView::draw_grid(
|
|
|
0662a2 |
const Cairo::RefPtr<cairo::context> &context,</cairo::context>
|
|
|
0662a2 |
const Matrix &matrix,
|
|
|
0662a2 |
const Color &color )
|
|
|
0662a2 |
{
|
|
|
0662a2 |
const int count = 100;
|
|
|
0662a2 |
const int subcount = 10;
|
|
|
0662a2 |
|
|
|
0662a2 |
const Real ps = get_pixel_size();
|
|
|
0662a2 |
|
|
|
0662a2 |
for(int i = -count; i < count; ++i) {
|
|
|
0662a2 |
for(int j = -count; j < count; ++j) {
|
|
|
0662a2 |
Vector4 src(i/(Real)subcount, j/(Real)subcount, 0, 1);
|
|
|
0662a2 |
Vector4 dst = matrix*src;
|
|
|
0662a2 |
Real w = dst.w;
|
|
|
0662a2 |
if (w > real_precision) {
|
|
|
0662a2 |
Vector2 pos(dst.x/w, dst.y/w);
|
|
|
0662a2 |
|
|
|
0662a2 |
Real ka = clamp(1/w, 0, 1);
|
|
|
0662a2 |
|
|
|
0662a2 |
Real a = color.a*ka;
|
|
|
0662a2 |
Real r = 4*ps;
|
|
|
0662a2 |
if (i) r *= 0.5;
|
|
|
0662a2 |
if (j) r *= 0.5;
|
|
|
0662a2 |
if (i % 10) r *= 0.75;
|
|
|
0662a2 |
if (j % 10) r *= 0.75;
|
|
|
0662a2 |
context->set_source_rgba(color.r, color.g, color.b, a);
|
|
|
0662a2 |
context->arc(pos.x, pos.y, r, 0, 2.0*M_PI);
|
|
|
0662a2 |
context->fill();
|
|
|
0662a2 |
}
|
|
|
0662a2 |
}
|
|
|
0662a2 |
}
|
|
|
0662a2 |
}
|
|
|
0662a2 |
|
|
|
0662a2 |
void
|
|
|
0662a2 |
PerspectiveSandBoxView::draw_line(
|
|
|
0662a2 |
const Cairo::RefPtr<cairo::context> &context,</cairo::context>
|
|
|
0662a2 |
const Pair2 &bounds,
|
|
|
0662a2 |
Real a,
|
|
|
0662a2 |
Real b,
|
|
|
0662a2 |
Real c,
|
|
|
0662a2 |
const Color &color )
|
|
|
0662a2 |
{
|
|
|
4dc49c |
// equatation of line is a*x + b*y = c
|
|
|
4dc49c |
|
|
|
4dc49c |
int count = 0;
|
|
|
4dc49c |
Vector2 points[4];
|
|
|
0662a2 |
|
|
|
4dc49c |
if (fabs(a) > real_precision) {
|
|
|
4dc49c |
Real x0 = (c - bounds.p0.y*b)/a;
|
|
|
4dc49c |
if ( x0 + real_precision >= bounds.p0.x
|
|
|
4dc49c |
&& x0 - real_precision <= bounds.p1.x )
|
|
|
4dc49c |
points[count++] = Vector2(x0, bounds.p0.y);
|
|
|
4dc49c |
|
|
|
4dc49c |
Real x1 = (c - bounds.p1.y*b)/a;
|
|
|
4dc49c |
if ( x1 + real_precision >= bounds.p0.x
|
|
|
4dc49c |
&& x1 - real_precision <= bounds.p1.x )
|
|
|
4dc49c |
points[count++] = Vector2(x1, bounds.p1.y);
|
|
|
4dc49c |
}
|
|
|
4dc49c |
|
|
|
4dc49c |
if (fabs(b) > real_precision) {
|
|
|
4dc49c |
Real y0 = (c - bounds.p0.x*a)/b;
|
|
|
4dc49c |
if ( y0 + real_precision >= bounds.p0.y
|
|
|
4dc49c |
&& y0 - real_precision <= bounds.p1.y )
|
|
|
4dc49c |
points[count++] = Vector2(bounds.p0.x, y0);
|
|
|
4dc49c |
|
|
|
4dc49c |
Real y1 = (c - bounds.p1.x*a)/b;
|
|
|
4dc49c |
if ( y1 + real_precision >= bounds.p0.y
|
|
|
4dc49c |
&& y1 - real_precision <= bounds.p1.y )
|
|
|
4dc49c |
points[count++] = Vector2(bounds.p1.x, y1);
|
|
|
4dc49c |
}
|
|
|
4dc49c |
|
|
|
4dc49c |
if (count >= 2) {
|
|
|
4dc49c |
context->set_source_rgba(color.r, color.g, color.b, color.a);
|
|
|
4dc49c |
context->move_to(points[0].x, points[0].y);
|
|
|
4dc49c |
context->line_to(points[1].x, points[1].y);
|
|
|
4dc49c |
context->stroke();
|
|
|
4dc49c |
}
|
|
|
4dc49c |
}
|
|
|
4dc49c |
|
|
|
4dc49c |
void
|
|
|
4dc49c |
PerspectiveSandBoxView::draw_subdivisions(
|
|
|
4dc49c |
const Cairo::RefPtr<cairo::context> &context,</cairo::context>
|
|
|
4dc49c |
const Matrix &matrix,
|
|
|
4dc49c |
const Pair2 &bounds,
|
|
|
4dc49c |
const Color &color )
|
|
|
4dc49c |
{
|
|
|
4dc49c |
const Real ps = get_pixel_size();
|
|
|
4dc49c |
|
|
|
4dc49c |
Vector4 a = matrix.row_x();
|
|
|
4dc49c |
Vector4 b = matrix.row_y();
|
|
|
4dc49c |
Vector4 c = matrix.row_w();
|
|
|
4dc49c |
|
|
|
4dc49c |
// calc coefficient for equatation of "horizontal" line: A*x + B*y = C + D/w
|
|
|
4dc49c |
// equatation of line of horizon is: A*x + B*y = C
|
|
|
4dc49c |
Real A = a.y*b.w - a.w*b.y;
|
|
|
4dc49c |
Real B = a.w*b.x - a.x*b.w;
|
|
|
4dc49c |
Real C = a.y*b.x - a.x*b.y;
|
|
|
4dc49c |
Real D = a.w*(b.x*c.y - b.y*c.x) + b.w*(a.y*c.x - a.x*c.y) - C*c.w;
|
|
|
4dc49c |
|
|
|
4dc49c |
if (D < 0) {
|
|
|
4dc49c |
A = -A;
|
|
|
4dc49c |
B = -B;
|
|
|
4dc49c |
C = -C;
|
|
|
4dc49c |
D = -D;
|
|
|
4dc49c |
}
|
|
|
4dc49c |
|
|
|
4dc49c |
Real hd = ps*sqrt(A*A + B*B);
|
|
|
4dc49c |
if (hd <= real_precision)
|
|
|
4dc49c |
return; // orthogonal projection - no prespective - no subdiviosions
|
|
|
4dc49c |
hd = D/hd;
|
|
|
4dc49c |
Real horizonw = hd;
|
|
|
4dc49c |
Real horizonw2 = hd/4;
|
|
|
4dc49c |
|
|
|
4dc49c |
Real maxw = -INFINITY, minw = INFINITY;
|
|
|
4dc49c |
Vector2 corners[] = {
|
|
|
4dc49c |
Vector2(bounds.p0.x, bounds.p0.y),
|
|
|
4dc49c |
Vector2(bounds.p1.x, bounds.p0.y),
|
|
|
4dc49c |
Vector2(bounds.p1.x, bounds.p1.y),
|
|
|
4dc49c |
Vector2(bounds.p0.x, bounds.p1.y) };
|
|
|
4dc49c |
for(int i = 0; i < 4; ++i) {
|
|
|
4dc49c |
Real w = A*corners[i].x + B*corners[i].y - C;
|
|
|
4dc49c |
if (fabs(w) > real_precision) {
|
|
|
4dc49c |
w = D/w;
|
|
|
4dc49c |
if (w < 0 || w > horizonw) w = horizonw;
|
|
|
4dc49c |
if (minw > w) minw = w;
|
|
|
4dc49c |
if (maxw < w) maxw = w;
|
|
|
4dc49c |
}
|
|
|
4dc49c |
}
|
|
|
4dc49c |
|
|
|
4dc49c |
if (minw >= maxw - real_precision)
|
|
|
4dc49c |
return; // all bounds too thin
|
|
|
4dc49c |
Real maxw2 = maxw < horizonw2 ? maxw : horizonw2;
|
|
|
4dc49c |
|
|
|
4dc49c |
// draw limits
|
|
|
4dc49c |
Color limits_color = color;
|
|
|
4dc49c |
limits_color.a *= 0.5;
|
|
|
4dc49c |
draw_line(context, get_bounds(), A, B, C + D/minw, limits_color);
|
|
|
4dc49c |
draw_line(context, get_bounds(), A, B, C + D/maxw, limits_color);
|
|
|
4dc49c |
|
|
|
4dc49c |
// split
|
|
|
4dc49c |
int minlog = (int)ceil (log2(minw ) + real_precision);
|
|
|
4dc49c |
int maxlog = (int)floor(log2(maxw2) - real_precision);
|
|
|
4dc49c |
for(int i = minlog; i <= maxlog; ++i)
|
|
|
4dc49c |
draw_line(context, bounds, A, B, C + D/exp2(i), color);
|
|
|
0662a2 |
}
|
|
|
0662a2 |
|
|
|
0662a2 |
void
|
|
|
0662a2 |
PerspectiveSandBoxView::on_draw_view(const Cairo::RefPtr<cairo::context> &context) {</cairo::context>
|
|
|
0662a2 |
context->save();
|
|
|
0662a2 |
|
|
|
0662a2 |
const Real ps = get_pixel_size();
|
|
|
0662a2 |
context->set_line_width(ps);
|
|
|
0662a2 |
|
|
|
0662a2 |
Vector2 p0 = persp_p0->position;
|
|
|
0662a2 |
Vector2 px = persp_px->position;
|
|
|
0662a2 |
Vector2 py = persp_py->position;
|
|
|
0662a2 |
Vector2 p1 = persp_p1->position;
|
|
|
0662a2 |
|
|
|
0662a2 |
Pair2 bounds(bounds_p0->position, bounds_p1->position);
|
|
|
0662a2 |
|
|
|
0662a2 |
|
|
|
0662a2 |
// perspective matrix
|
|
|
0662a2 |
Matrix matrix;
|
|
|
0662a2 |
{
|
|
|
0662a2 |
Vector2 A = px - p1;
|
|
|
0662a2 |
Vector2 B = py - p1;
|
|
|
0662a2 |
Vector2 C = p0 + p1 - px - py;
|
|
|
0662a2 |
|
|
|
0662a2 |
Real cw = A.y*B.x - A.x*B.y;
|
|
|
0662a2 |
Real aw = B.x*C.y - B.y*C.x;
|
|
|
0662a2 |
Real bw = A.y*C.x - A.x*C.y;
|
|
|
0662a2 |
|
|
|
4dc49c |
if (cw < 0) {
|
|
|
4dc49c |
aw = -aw;
|
|
|
4dc49c |
bw = -bw;
|
|
|
4dc49c |
cw = -cw;
|
|
|
4dc49c |
}
|
|
|
4dc49c |
|
|
|
0662a2 |
Vector2 c = p0*cw;
|
|
|
0662a2 |
Vector2 a = px*(cw + aw) - c;
|
|
|
0662a2 |
Vector2 b = py*(cw + bw) - c;
|
|
|
0662a2 |
|
|
|
0662a2 |
matrix.row_x() = Vector4(a, 0, aw);
|
|
|
0662a2 |
matrix.row_y() = Vector4(b, 0, bw);
|
|
|
0662a2 |
matrix.row_w() = Vector4(c, 0, cw);
|
|
|
0662a2 |
}
|
|
|
b4eaab |
|
|
|
b4eaab |
{ // update background
|
|
|
b4eaab |
Matrix4 m4 = transform_to_pixels()*matrix;
|
|
|
b4eaab |
Matrix3 m3(
|
|
|
b4eaab |
Vector3( m4.m00, m4.m01, m4.m03 ),
|
|
|
b4eaab |
Vector3( m4.m10, m4.m11, m4.m13 ),
|
|
|
b4eaab |
Vector3( m4.m30, m4.m31, m4.m33 ) );
|
|
|
b4eaab |
Matrix3 back_matrix = Perspective::normalize_matrix( m3.inverted() );
|
|
|
b4eaab |
update_background(back_matrix, get_width(), get_height());
|
|
|
b4eaab |
}
|
|
|
b4eaab |
|
|
|
b4eaab |
if (background) {
|
|
|
b4eaab |
context->save();
|
|
|
b4eaab |
context->transform(transform_from_pixels().to_cairo());
|
|
|
b4eaab |
context->set_source(background, 0, 0);
|
|
|
b4eaab |
context->paint();
|
|
|
b4eaab |
context->restore();
|
|
|
b4eaab |
}
|
|
|
b4eaab |
|
|
|
0662a2 |
draw_grid(context, matrix, Color(0, 1, 0, 1));
|
|
|
4dc49c |
draw_subdivisions(context, matrix, bounds, Color(0, 0, 1, 1));
|
|
|
0662a2 |
|
|
|
0662a2 |
// draw frames
|
|
|
0662a2 |
context->set_source_rgba(0, 0, 1, 0.75);
|
|
|
0662a2 |
context->move_to(p0.x, p0.y);
|
|
|
0662a2 |
context->line_to(px.x, px.y);
|
|
|
0662a2 |
context->line_to(p1.x, p1.y);
|
|
|
0662a2 |
context->line_to(py.x, py.y);
|
|
|
0662a2 |
context->close_path();
|
|
|
0662a2 |
context->stroke();
|
|
|
0662a2 |
|
|
|
0662a2 |
context->move_to(bounds.p0.x, bounds.p0.y);
|
|
|
0662a2 |
context->line_to(bounds.p1.x, bounds.p0.y);
|
|
|
0662a2 |
context->line_to(bounds.p1.x, bounds.p1.y);
|
|
|
0662a2 |
context->line_to(bounds.p0.x, bounds.p1.y);
|
|
|
0662a2 |
context->close_path();
|
|
|
0662a2 |
context->stroke();
|
|
|
0662a2 |
|
|
|
0662a2 |
context->restore();
|
|
|
0662a2 |
}
|
|
|
0662a2 |
|