summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/display/canvas-grid.cpp249
-rw-r--r--src/display/canvas-grid.h2
2 files changed, 147 insertions, 104 deletions
diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp
index 2dadde336..c8951525f 100644
--- a/src/display/canvas-grid.cpp
+++ b/src/display/canvas-grid.cpp
@@ -5,6 +5,7 @@
* Copyright (C) Lauris Kaplinski 2000
* Abhishek Sharma
* Jon A. Cruz <jon@joncruz.org>
+ * Copyright (C) Tavmong Bah 2017 <tavmjong@free.fr>
*/
/* As a general comment, I am not exactly proud of how things are done.
@@ -841,21 +842,19 @@ void
CanvasXYGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/)
{
ow = origin * affine;
- // Temp hack to insure grid doesn't collapse with rotation.
- sw[0] = spacing[0] * sqrt(affine[0]*affine[0] + affine[1]*affine[1]);
- sw[1] = spacing[1] * sqrt(affine[2]*affine[2] + affine[3]*affine[3]);
- sw -= Geom::Point(affine[4], affine[5]);
+ sw[0] = Geom::Point(spacing[0], 0) * affine.withoutTranslation();
+ sw[1] = Geom::Point(0, spacing[1]) * affine.withoutTranslation();
+ // Find suitable grid spacing for display
for(int dim = 0; dim < 2; dim++) {
gint scaling_factor = empspacing;
if (scaling_factor <= 1)
scaling_factor = 5;
- scaled[dim] = FALSE;
- sw[dim] = fabs (sw[dim]);
- while (sw[dim] < 8.0) {
- scaled[dim] = TRUE;
+ scaled[dim] = false;
+ while (sw[dim].length() < 8.0) {
+ scaled[dim] = true;
sw[dim] *= scaling_factor;
/* First pass, go up to the major line spacing, then
keep increasing by two. */
@@ -865,49 +864,45 @@ CanvasXYGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/)
}
-static void
-grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba)
-{
- if ((y < buf->rect.top()) || (y >= buf->rect.bottom()))
- return;
-
- cairo_move_to(buf->ct, 0.5 + xs, 0.5 + y);
- cairo_line_to(buf->ct, 0.5 + xe, 0.5 + y);
- ink_cairo_set_source_rgba32(buf->ct, rgba);
- cairo_stroke(buf->ct);
-}
-
-static void
-grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba)
+// Find intersections of line with rectangle. There should be zero or two.
+// If line is degenerate with rectangle side, two corner points are returned.
+static std::vector<Geom::Point>
+intersect_line_rectangle( Geom::Line const &line, Geom::Rect const &rect )
{
- if ((x < buf->rect.left()) || (x >= buf->rect.right()))
- return;
-
- cairo_move_to(buf->ct, 0.5 + x, 0.5 + ys);
- cairo_line_to(buf->ct, 0.5 + x, 0.5 + ye);
- ink_cairo_set_source_rgba32(buf->ct, rgba);
- cairo_stroke(buf->ct);
+ std::vector<Geom::Point> intersections;
+ for (unsigned i = 0; i < 4; ++i) {
+ Geom::LineSegment side( rect.corner(i), rect.corner((i+1)%4) );
+ try {
+ Geom::OptCrossing oc = Geom::intersection(line, side);
+ if (oc) {
+ intersections.push_back( line.pointAt((*oc).ta));
+ }
+ } catch (Geom::InfiniteSolutions) {
+ intersections.clear();
+ intersections.push_back( side.pointAt(0) );
+ intersections.push_back( side.pointAt(1) );
+ return intersections;
+ }
+ }
+ return intersections;
}
-static void
-grid_dot (SPCanvasBuf *buf, gint x, gint y, guint32 rgba)
-{
- if ( (y < buf->rect.top()) || (y >= buf->rect.bottom())
- || (x < buf->rect.left()) || (x >= buf->rect.right()) )
- return;
-
- cairo_rectangle(buf->ct, x, y, 1, 1);
- ink_cairo_set_source_rgba32(buf->ct, rgba);
- cairo_fill(buf->ct);
+// Find the signed distance of a point to a line. The distance is negative if
+// the point lies to the left of the line considering the line's versor.
+static double
+signed_distance( Geom::Point const &point, Geom::Line const &line ) {
+ Geom::Coord t = line.nearestTime( point );
+ Geom::Point p = line.pointAt(t);
+ double distance = Geom::distance( p, point );
+ if ( Geom::cross( Geom::Line( p, point ).versor(), line.versor() ) < 0.0 ) {
+ distance = -distance;
+ }
+ return distance;
}
void
CanvasXYGrid::Render (SPCanvasBuf *buf)
{
- gdouble const sxg = floor ((buf->rect.left() - ow[Geom::X]) / sw[Geom::X]) * sw[Geom::X] + ow[Geom::X];
- gint const xlinestart = round((sxg - ow[Geom::X]) / sw[Geom::X]);
- gdouble const syg = floor ((buf->rect.top() - ow[Geom::Y]) / sw[Geom::Y]) * sw[Geom::Y] + ow[Geom::Y];
- gint const ylinestart = round((syg - ow[Geom::Y]) / sw[Geom::Y]);
// no_emphasize_when_zoomedout determines color (minor or major) when only major grid lines/dots shown.
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
@@ -924,75 +919,123 @@ CanvasXYGrid::Render (SPCanvasBuf *buf)
cairo_set_line_width(buf->ct, 1.0);
cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_SQUARE);
- if (!render_dotted) {
- // Line grid
- gint ylinenum;
- gdouble y;
- for (y = syg, ylinenum = ylinestart; y < buf->rect.bottom(); y += sw[Geom::Y], ylinenum++) {
- gint const y0 = round(y);
- if (!scaled[Geom::Y] && (ylinenum % empspacing) != 0) {
- grid_hline (buf, y0, buf->rect.left(), buf->rect.right() - 1, color);
- } else {
- grid_hline (buf, y0, buf->rect.left(), buf->rect.right() - 1, _empcolor);
- }
- }
+ for (unsigned dim = 0; dim < 2; ++dim) {
- gint xlinenum;
- gdouble x;
- for (x = sxg, xlinenum = xlinestart; x < buf->rect.right(); x += sw[Geom::X], xlinenum++) {
- gint const ix = round(x);
- if (!scaled[Geom::X] && (xlinenum % empspacing) != 0) {
- grid_vline (buf, ix, buf->rect.top(), buf->rect.bottom(), color);
- } else {
- grid_vline (buf, ix, buf->rect.top(), buf->rect.bottom(), _empcolor);
+ // std::cout << "\n " << (dim==0?"Horizontal":"Vertical") << " ------------" << std::endl;
+
+ // Construct an axis line through origin with direction normal to grid spacing.
+ Geom::Line axis = Geom::Line::from_origin_and_vector( ow, sw[dim] );
+ Geom::Line orth = Geom::Line::from_origin_and_vector( ow, sw[(dim+1)%2] );
+
+ double spacing = fabs( sw[(dim+1)%2].length()); // Spacing between grid lines.
+ double dash = fabs( sw[dim].length()); // Total length of dash pattern.
+
+ // std::cout << " axis: " << axis.origin() << ", " << axis.vector() << std::endl;
+ // std::cout << " spacing: " << spacing << std::endl;
+ // std::cout << " dash period: " << dash << std::endl;
+
+ // Find the minimum and maximum distances of the buffer corners from axis.
+ double min = 1000000;
+ double max = -1000000;
+ for (unsigned c = 0; c < 4; ++c) {
+
+ // We need signed distance... lib2geom offers only positive distance.
+ double distance = signed_distance( buf->rect.corner(c), axis );
+ if (dim == 1 ) { // YFLIP?
+ distance = -distance;
}
+ if (distance < min)
+ min = distance;
+ if (distance > max)
+ max = distance;
}
- } else {
- // Dotted grid
- gint ylinenum;
- gdouble y;
-
- // alpha needs to be larger than in the line case to maintain a similar visual impact but
- // setting it to the maximal value makes the dots dominant in some cases. Solution,
- // increase the alpha by a factor of 4. This then allows some user adjustment.
- guint32 _empdot = (_empcolor & 0xff) << 2;
- if (_empdot > 0xff)
- _empdot = 0xff;
- _empdot += (_empcolor & 0xffffff00);
-
- guint32 _colordot = (color & 0xff) << 2;
- if (_colordot > 0xff)
- _colordot = 0xff;
- _colordot += (color & 0xffffff00);
-
- for (y = syg, ylinenum = ylinestart; y < buf->rect.bottom(); y += sw[Geom::Y], ylinenum++) {
- gint const iy = round(y);
-
- gint xlinenum;
- gdouble x;
- for (x = sxg, xlinenum = xlinestart; x < buf->rect.right(); x += sw[Geom::X], xlinenum++) {
- gint const ix = round(x);
- if ( (!scaled[Geom::X] && (xlinenum % empspacing) != 0)
- || (!scaled[Geom::Y] && (ylinenum % empspacing) != 0)
- || ((scaled[Geom::X] || scaled[Geom::Y]) && no_emp_when_zoomed_out) )
- {
- // Minor point: dot only
- grid_dot (buf, ix, iy, _colordot); // | (guint32)0x000000FF); // put alpha to max value
+ int start = floor( min/spacing );
+ int stop = floor( max/spacing );
+
+ // std::cout << " rect: " << buf->rect << std::endl;
+ // std::cout << " min: " << min << " max: " << max << std::endl;
+ // std::cout << " start: " << start << " stop: " << stop << std::endl;
+
+ // Loop over grid lines that intersected buf rectangle.
+ for (int j = start+1; j <= stop; ++j) {
+
+ Geom::Line grid_line = Geom::make_parallel_line( ow + j * sw[(dim+1)%2], axis );
+
+ std::vector<Geom::Point> x = intersect_line_rectangle( grid_line, buf->rect );
+
+ // If we have two intersections, grid line intersects buffer rectangle.
+ if (x.size() == 2 ) {
+
+ // Make sure lines are always drawn in the same direction (or dashes misplaced).
+ Geom::Line vector( x[0], x[1]);
+ if (Geom::dot( vector.vector(), axis.vector() ) < 0.0) {
+ std::swap(x[0], x[1]);
+ }
+
+ // Set up line.
+ cairo_move_to(buf->ct, x[0][Geom::X] + 0.5, x[0][Geom::Y] + 0.5);
+ cairo_line_to(buf->ct, x[1][Geom::X] + 0.5, x[1][Geom::Y] + 0.5);
+
+ // Set dash pattern and color.
+ if (render_dotted) {
+
+ // alpha needs to be larger than in the line case to maintain a similar
+ // visual impact but setting it to the maximal value makes the dots
+ // dominant in some cases. Solution, increase the alpha by a factor of
+ // 4. This then allows some user adjustment.
+ guint32 _empdot = (_empcolor & 0xff) << 2;
+ if (_empdot > 0xff)
+ _empdot = 0xff;
+ _empdot += (_empcolor & 0xffffff00);
+
+ guint32 _colordot = (color & 0xff) << 2;
+ if (_colordot > 0xff)
+ _colordot = 0xff;
+ _colordot += (color & 0xffffff00);
+
+ // Dash pattern must use spacing from orthogonal direction.
+ // Offset is to center dash on orthogonal lines.
+ double offset = fmod( signed_distance( x[0], orth ), sw[dim].length());
+ if (dim == 1) offset = -offset;
+
+ double dashes[2];
+ if (!scaled[dim] && (j % empspacing) != 0) {
+ // Minor lines
+ dashes[0] = 1;
+ dashes[1] = dash -1;
+ offset -= 0.5;
+ ink_cairo_set_source_rgba32(buf->ct, _colordot);
+ } else {
+ // Major lines
+ dashes[0] = 3;
+ dashes[1] = dash - 3;
+ offset -= 1.5; // Center dash on intersection.
+ ink_cairo_set_source_rgba32(buf->ct, _empdot);
+ }
+
+ cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_BUTT);
+ cairo_set_dash(buf->ct, dashes, 2, -offset);
+
} else {
- // Major point: small cross
- gint const pitch = 1;
- grid_dot (buf, ix-pitch, iy, _empcolor);
- grid_dot (buf, ix+pitch, iy, _empcolor);
- grid_dot (buf, ix, iy, _empdot ); // | (guint32)0x000000FF); // put alpha to max value
+ // Solid lines
- grid_dot (buf, ix, iy-pitch, _empcolor);
- grid_dot (buf, ix, iy+pitch, _empcolor);
+ // Set color
+ if (!scaled[dim] && (j % empspacing) != 0) {
+ ink_cairo_set_source_rgba32(buf->ct, color );
+ } else {
+ ink_cairo_set_source_rgba32(buf->ct, _empcolor );
+ }
}
- }
+ cairo_stroke(buf->ct);
+
+ } else {
+ std::cerr << "CanvasXYGrid::render: Grid line doesn't intersect!" << std::endl;
+ }
}
}
+
cairo_restore(buf->ct);
}
@@ -1031,7 +1074,7 @@ CanvasXYGridSnapper::_getSnapLines(Geom::Point const &p) const
if (getSnapVisibleOnly()) {
// Only snapping to visible grid lines
- spacing = grid->sw[i]; // this is the spacing of the visible grid lines measured in screen pixels
+ spacing = grid->sw[i].length(); // this is the spacing of the visible grid lines measured in screen pixels
// convert screen pixels to px
// FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary
SPDesktop const *dt = _snapmanager->getDesktop();
diff --git a/src/display/canvas-grid.h b/src/display/canvas-grid.h
index bf520467a..91fa43e69 100644
--- a/src/display/canvas-grid.h
+++ b/src/display/canvas-grid.h
@@ -148,7 +148,7 @@ public:
be different in the X or Y direction, hence two
variables */
Geom::Point ow; /**< Transformed origin by the affine for the zoom */
- Geom::Point sw; /**< Transformed spacing by the affine for the zoom */
+ Geom::Point sw[2]; /**< Transformed spacing by the affine for the zoom */
protected:
virtual Gtk::Widget * newSpecificWidget();