Blame c++/perspective/src/perspectivesandboxview.cpp

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
	
43cb04
	const Real sum = a*d + b*c - real_precision_sqr;
b4eaab
	Vector2 scale;
43cb04
	if (2*a*b >= sum && 2*c*d >= sum) {
43cb04
		if (a*b < c*d) {
43cb04
			scale.x = sqrt(2*b)*e;
43cb04
			scale.y = sqrt(2*a)*e;
43cb04
		} else {
43cb04
			scale.x = sqrt(2*d)*e;
43cb04
			scale.y = sqrt(2*c)*e;
43cb04
		}
43cb04
	} else
43cb04
	if (2*a*b >= sum) {
b4eaab
		scale.x = sqrt(2*b)*e;
b4eaab
		scale.y = sqrt(2*a)*e;
b4eaab
	} else
43cb04
	if (2*c*d >= 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
43cb04
43cb04
void PerspectiveSandBoxView::update_background_best_perimeter(
43cb04
	const Matrix3 &matrix,
43cb04
	int width,
43cb04
	int height )
43cb04
{
43cb04
	const int log_min = -10000;
43cb04
	const int log_max =  10000;
43cb04
	const Real log_base = log(1.001);
43cb04
	
43cb04
	std::vector<real> map_pk_max(log_max - log_min + 2, 0);</real>
43cb04
	std::vector<real> map_pk_min(log_max - log_min + 2, 1);</real>
43cb04
	std::vector<real> map_pk(width*height, 0);</real>
43cb04
	std::vector<int> map_log_index(width*height, 0);</int>
43cb04
	
43cb04
	
43cb04
	// fill map
43cb04
	for(int r = 0; r < height; ++r) {
43cb04
		for(int c = 0; c < width; ++c) {
43cb04
			Vector3 v = matrix*Vector3(c, r, 1);
43cb04
			if (v.z <= real_precision)
43cb04
				continue;
43cb04
			
43cb04
			Real l = round(log(v.z)/log_base);
43cb04
			if (l-1 < log_min || l+1 > log_max)
43cb04
				continue;
43cb04
			int log_index = (int)l - log_min + 1;
43cb04
			assert(log_index > 0 && log_index < (int)map_pk_max.size() && log_index < (int)map_pk_min.size());
43cb04
43cb04
			const Real k = 1/(v.z*v.z);
43cb04
			const Vector2 ox = Vector2( v.z*matrix.m00 - matrix.m02*v.x,
43cb04
										v.z*matrix.m10 - matrix.m12*v.x )*k;
43cb04
			const Vector2 oy = Vector2( v.z*matrix.m01 - matrix.m02*v.y,
43cb04
										v.z*matrix.m11 - matrix.m12*v.y )*k;
43cb04
			
43cb04
			const Real area = fabs(ox.x*oy.y - ox.y*oy.x);
43cb04
			const Real optimal_side = sqrt(area);
43cb04
			const Real optimal_perimeter = 4*optimal_side;
43cb04
			const Real perimeter = (ox.length() + oy.length())*2;
43cb04
			
43cb04
			const Real pk = optimal_perimeter/perimeter;
43cb04
			
43cb04
			map_pk[r*width + c] = pk;
43cb04
			map_log_index[r*width + c] = log_index;
43cb04
			if (map_pk_max[log_index] < pk)
43cb04
				map_pk_max[log_index] = pk;
43cb04
			if (map_pk_min[log_index] > pk)
43cb04
				map_pk_min[log_index] = pk;
43cb04
		}
43cb04
	}
43cb04
43cb04
	// paint
43cb04
	DataSurface surface(width, height);
43cb04
	for(int r = 0; r < height; ++r) {
43cb04
		for(int c = 0; c < width; ++c) {
43cb04
			const int log_index = map_log_index[r*width + c];
43cb04
			assert(log_index >= 0 && log_index < (int)map_pk_max.size() && log_index < (int)map_pk_min.size());
43cb04
			if (log_index) {
43cb04
				const Real v = map_pk[r*width + c];
43cb04
				const Real v0 = map_pk_min[log_index];
43cb04
				const Real v1 = map_pk_max[log_index];
43cb04
				if (v0 + real_precision < v1) {
43cb04
					Real k = (v - v0)/(v1 - v0);
43cb04
					const Real p = 0.5;
43cb04
					k -= p;
43cb04
					if (k > 0) {
43cb04
						k /= 1 - p;
43cb04
						k = pow(k, 20);
43cb04
						surface[r][c] = Color(k, k, k, 1);
43cb04
					}
43cb04
				}
43cb04
			}
43cb04
		}
43cb04
	}
43cb04
	
43cb04
	// paint centers
43cb04
	Perspective::LayerList layers;
43cb04
	Perspective::create_layers(layers, matrix.inverted(), IntPair2(IntVector2(), IntVector2(width, height)), 2);
43cb04
	for(Perspective::LayerList::const_iterator i = layers.begin(); i != layers.end(); ++i)
43cb04
		Perspective::paint_cross(surface, i->center);
43cb04
	std::cout << "layers count: " << layers.size() << std::endl;
43cb04
43cb04
	background = surface.to_cairo_surface();
43cb04
}
43cb04
43cb04
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 ) );
db8a1f
		Matrix3 back_matrix = Perspective::normalize_matrix_by_det( m3.inverted() );
43cb04
		update_background_best_perimeter(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