/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
* mazen-ng
* Copyright (C) Jacob Zimmermann 2009 <jacob@jzimm.net>
*
* mazen-ng is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mazen-ng is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma implementation
#include "grid-maze.h"
#include "polar-view.h"
#include "util.h"
#include <stdexcept>
static const double cent_room_radius = 4;
static const double radius = cent_room_radius * M_SQRT2;
PolarView::PolarView (): GridView () {
cent_room_rect.x = cent_room_rect.y = -cent_room_radius;
cent_room_rect.width = cent_room_rect.height = 2 * cent_room_radius;
}
double PolarView::gap () const {
return 0.4;
}
double PolarView::radius_for_x (double x) const {
return radius + x;
}
void PolarView::point_coord (int y, double& dx, double& dy) const {
dx = radius_for_x (dx);
rot[y % maze->height ()].transform_point (dx, dy);
}
void PolarView::set_maze (Glib::RefPtr<GridMaze> maze) {
if (maze->height () < 3)
throw std::runtime_error ("PolarView only works for mazes with height >= 3");
GridView::set_maze (maze);
// initialise the rotation matrices
angle = 2 * M_PI / maze->height ();
rot.clear ();
for (int y = 0; y < maze->height (); y++)
rot.push_back (Cairo::rotation_matrix (y * angle));
}
void PolarView::set_size (int width, int height) {
double s = MIN (width / 2, height);
this->width = width;
this->height = height;
double maze_rad = radius_for_x (maze->width ()) - 1;
zfact = s / (2 * maze_rad);
maze_x = width / 2;
maze_y = height / 2;
force_redraw ();
}
void PolarView::rline (Cairo::RefPtr<Cairo::Context> cr, int y, double r, bool off) const {
double dy = off? -gap (): 0;
double r1 = r + 1, dy1 = dy;
point_coord (y, r, dy);
cr->move_to (r, dy);
point_coord (y, r1, dy1);
cr->line_to (r1, dy1);
cr->stroke ();
}
void PolarView::render (Cairo::RefPtr<Cairo::Context> cr, Artwork::Renderer& renderer) {
cr->save ();
cr->translate (maze_x, maze_y);
cr->scale (zfact, zfact);
cr->set_source_rgb (0, 0, 0);
set_line_width (cr, 1);
// Render the maze
for (int y = 0; y < maze->height (); y++) {
for (int x = 0; x < maze->width (); x++) {
cline (cr, y, x, (maze->vert (x, y)? CLOSED: OPEN));
if (maze->horiz (x, y))
rline (cr, y, x, true);
}
cr->stroke ();
}
// render the reward while still on maze scale
renderer.reward (a_handle, cr, cent_room_rect, cent_room_radius, cent_room_radius, Artwork::CENTRE);
cr->restore ();
// render the hero
Cairo::Rectangle hrect = { 0, 0, width / 4, height };
renderer.hero (a_handle, cr, hrect, width / 4, height / 2, Artwork::LEFT);
}
void PolarView::sol_path_add (Cairo::RefPtr<Cairo::Context> cr, const xy& p) {
if (prev_y == p.y)
rline (cr, prev_y, MIN (p.x, prev_x) + 0.5, false);
else {
int y = p.y;
if (prev_y == 0 && y == maze->height () - 1)
prev_y = maze->height ();
else if (y == 0 && prev_y == maze->height () - 1)
y = maze->height ();
cline (cr, MAX (y, prev_y), p.x + 0.5, NORMAL);
}
}
void PolarView::render_solution (Cairo::RefPtr<Cairo::Context> cr, Artwork::Renderer& renderer) {
cr->translate (maze_x, maze_y);
cr->scale (zfact, zfact);
set_line_width (cr, 1);
cr->set_source_rgb (1, 0, 0);
std::list<xy> solution = maze->get_solution ();
std::list<xy>::const_iterator p = solution.begin ();
std::list<xy>::const_iterator last = solution.end ();
prev_x = p->x;
prev_y = p->y;
double dx = p->x, dy = 0;
point_coord (prev_y, dx, dy);
cr->move_to (dx, dy);
while ((++p) != solution.end ()) {
sol_path_add (cr, *p);
prev_x = p->x;
prev_y = p->y;
}
xy ex = maze->get_exit ();
sol_path_add (cr, ex);
prev_x = ex.x;
prev_y = ex.y;
sol_path_add (cr, maze->get_exit ().left ());
cr->stroke ();
}