BuildingSymbolizer: draw wall edges at corners
This commit is contained in:
parent
2b9849a25b
commit
a0f90496fa
4 changed files with 125 additions and 4 deletions
|
@ -29,6 +29,10 @@
|
|||
#include <mapnik/vertex_adapters.hpp>
|
||||
#include <mapnik/path.hpp>
|
||||
#include <mapnik/transform_path_adapter.hpp>
|
||||
#include <mapnik/util/math.hpp>
|
||||
|
||||
// stl
|
||||
#include <cmath>
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
|
@ -44,6 +48,7 @@ private:
|
|||
|
||||
renderer_common const& rencom_;
|
||||
double const height_;
|
||||
double flat_wall_cos2_ = 0.9921875; // approx. cos²(5.0709°)
|
||||
bool render_back_side_ = false;
|
||||
path_type roof_vertices_{path_type::types::Polygon};
|
||||
|
||||
|
@ -67,9 +72,31 @@ public:
|
|||
// colors are not always needed so they're not extracted here
|
||||
}
|
||||
|
||||
bool has_transparent_fill() const
|
||||
bool has_transparent_roof() const
|
||||
{
|
||||
return (fill_color.alpha_ & wall_fill_color.alpha_) != 255;
|
||||
return fill_color.alpha_ < 255;
|
||||
}
|
||||
|
||||
bool has_transparent_walls() const
|
||||
{
|
||||
return wall_fill_color.alpha_ < 255;
|
||||
}
|
||||
|
||||
void flat_wall_tolerance(double angle)
|
||||
{
|
||||
if (angle > -180 && angle < 180)
|
||||
{
|
||||
double cosa = std::cos(util::radians(angle));
|
||||
// Preserve the sign of the cosine, so that comparisons against
|
||||
// this value are consistent, including when the tolerance is
|
||||
// greater than 90 degrees. Still, setting it above 45 degrees
|
||||
// is not recommended.
|
||||
flat_wall_cos2_ = std::abs(cosa) * cosa;
|
||||
}
|
||||
else
|
||||
{
|
||||
flat_wall_cos2_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool render_back_side() const
|
||||
|
@ -166,6 +193,35 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
bool is_flat_wall(double ax, double ay, double bx, double by,
|
||||
double cx, double cy) const
|
||||
{
|
||||
// less than `stroke-width` pixels away from the previous corner;
|
||||
// use the points' Euclidean distance, not just delta-x, because
|
||||
// slight overlap at east||west-facing wall corners is desirable
|
||||
double ux = bx - ax;
|
||||
double uy = by - ay;
|
||||
double u2 = ux * ux + uy * uy;
|
||||
if (u2 < stroke_width * stroke_width)
|
||||
return true;
|
||||
|
||||
// less than 1/16 of a pixel away from the next joint (E. distance)
|
||||
// is too close for the angle to be reliable, could just be jittery
|
||||
// geometry;
|
||||
// so if such a short segment also starts less than `stroke-width`
|
||||
// away from the previous corner (measuring only delta-x to prevent
|
||||
// stroke overlap), assume zero deflection
|
||||
double vx = cx - bx;
|
||||
double vy = cy - by;
|
||||
double v2 = vx * vx + vy * vy;
|
||||
if (v2 < 1.0 / (16 * 16) && std::abs(ux) < stroke_width)
|
||||
return true;
|
||||
|
||||
// true if deflection angle is within flat wall tolerance
|
||||
double dot = ux * vx + uy * vy;
|
||||
return std::abs(dot) * dot >= u2 * v2 * flat_wall_cos2_;
|
||||
}
|
||||
|
||||
bool is_visible(int orientation) const
|
||||
{
|
||||
return orientation > 0 || (orientation < 0 && render_back_side_);
|
||||
|
@ -360,6 +416,54 @@ private:
|
|||
if (is_visible(orientation))
|
||||
{
|
||||
paint_wall_faces(begin1, end1, begin2, end2, painter);
|
||||
paint_wall_edges(begin1, end1, begin2, end2, painter);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
void paint_wall_edge(double x, double y, Context & painter) const
|
||||
{
|
||||
painter.stroke_from(x, y);
|
||||
painter.stroke_to(x, y + height_);
|
||||
painter.stroke();
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
void paint_wall_edges(size_t begin1, size_t end1,
|
||||
size_t begin2, size_t end2,
|
||||
Context & painter) const
|
||||
{
|
||||
if (begin1 >= end1 || flat_wall_cos2_ <= -1)
|
||||
return;
|
||||
|
||||
double x, y;
|
||||
roof_vertices_->get_vertex(begin1, &x, &y);
|
||||
x = std::floor(x) + 0.5; // snap to pixel center
|
||||
|
||||
double x0 = x, y0 = y; // last vertex
|
||||
double x1 = x, y1 = y; // last stroked joint
|
||||
|
||||
for (size_t i = begin1; ++i < end1; )
|
||||
{
|
||||
roof_vertices_->get_vertex(i, &x, &y);
|
||||
x = std::floor(x) + 0.5; // snap to pixel center
|
||||
if (!is_flat_wall(x1, y1, x0, y0, x, y))
|
||||
{
|
||||
paint_wall_edge(x1 = x0, y1 = y0, painter);
|
||||
}
|
||||
x0 = x;
|
||||
y0 = y;
|
||||
}
|
||||
for (size_t i = begin2; i < end2; ++i)
|
||||
{
|
||||
roof_vertices_->get_vertex(i, &x, &y);
|
||||
x = std::floor(x) + 0.5; // snap to pixel center
|
||||
if (!is_flat_wall(x1, y1, x0, y0, x, y))
|
||||
{
|
||||
paint_wall_edge(x1 = x0, y1 = y0, painter);
|
||||
}
|
||||
x0 = x;
|
||||
y0 = y;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,7 +100,15 @@ void agg_renderer<T0,T1>::process(building_symbolizer const& sym,
|
|||
ctx.stroke_gen.line_join(agg::round_join);
|
||||
|
||||
rebus.setup_colors(sym, feature);
|
||||
rebus.render_back_side(rebus.has_transparent_fill());
|
||||
if (rebus.has_transparent_walls())
|
||||
{
|
||||
rebus.render_back_side(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
rebus.render_back_side(rebus.has_transparent_roof());
|
||||
rebus.flat_wall_tolerance(181);
|
||||
}
|
||||
rebus.apply(feature, prj_trans, ctx);
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,15 @@ void cairo_renderer<T>::process(building_symbolizer const& sym,
|
|||
context_.set_line_join(ROUND_JOIN);
|
||||
|
||||
rebus.setup_colors(sym, feature);
|
||||
rebus.render_back_side(rebus.has_transparent_fill());
|
||||
if (rebus.has_transparent_walls())
|
||||
{
|
||||
rebus.render_back_side(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
rebus.render_back_side(rebus.has_transparent_roof());
|
||||
rebus.flat_wall_tolerance(181);
|
||||
}
|
||||
rebus.apply(feature, prj_trans, ctx);
|
||||
}
|
||||
|
||||
|
|
|
@ -90,6 +90,7 @@ void grid_renderer<T>::process(building_symbolizer const& sym,
|
|||
ctx.set_feature(feature.id());
|
||||
|
||||
rebus.render_back_side(false);
|
||||
rebus.flat_wall_tolerance(181);
|
||||
rebus.apply(feature, prj_trans, ctx);
|
||||
|
||||
pixmap_.add_feature(feature);
|
||||
|
|
Loading…
Reference in a new issue