Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
canvas-item-grid.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Author:
4 * Tavmjong Bah
5 *
6 * Copyright (C) 2020 Tavmjong Bah
7 *
8 * Rewrite of GridCanvasItem.
9 *
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13#include <2geom/line.h>
14#include <cairomm/enums.h>
15#include <cmath>
16
17#include "colors/color.h"
18#include "canvas-item-grid.h"
19#include "helper/geom.h"
20
21enum Dim3 { X, Y, Z };
22
23static int calculate_scaling_factor(double length, int major)
24{
25 int multiply = 1;
26 int step = std::max(major, 1);
27 int watchdog = 0;
28
29 while (length * multiply < 8.0 && watchdog < 100) {
30 multiply *= step;
31 // First pass, go up to the major line spacing, then keep increasing by two.
32 step = 2;
33 watchdog++;
34 }
35
36 return multiply;
37}
38
39namespace Inkscape {
40
45 : CanvasItem(group)
46 , _origin(0, 0)
47 , _spacing(1, 1)
48 , _minor_color(0x0)
49 , _major_color(0x0)
50 , _major_line_interval(5)
51 , _dotted(false)
52{
53 _no_emp_when_zoomed_out = Preferences::get()->getBool("/options/grids/no_emphasize_when_zoomedout");
54 _pref_tracker = Preferences::PreferencesObserver::create("/options/grids/no_emphasize_when_zoomedout", [this] (auto &entry) {
55 set_no_emp_when_zoomed_out(entry.getBool());
56 });
57
59}
60
64bool CanvasItemGrid::contains(Geom::Point const &p, double tolerance)
65{
66 return false; // We're not pickable!
67}
68
69// Find the signed distance of a point to a line. The distance is negative if
70// the point lies to the left of the line considering the line's versor.
71static double signed_distance(Geom::Point const &point, Geom::Line const &line)
72{
73 return Geom::cross(point - line.initialPoint(), line.versor());
74}
75
76// Find intersections of line with rectangle. There should be zero or two.
77// If line is degenerate with rectangle side, two corner points are returned.
78static std::vector<Geom::Point> intersect_line_rectangle(Geom::Line const &line, Geom::Rect const &rect)
79{
80 std::vector<Geom::Point> intersections;
81 for (int i = 0; i < 4; ++i) {
82 Geom::LineSegment side(rect.corner(i), rect.corner((i + 1) % 4));
83 try {
84 if (auto oc = Geom::intersection(line, side)) {
85 intersections.emplace_back(line.pointAt(oc->ta));
86 }
87 } catch (Geom::InfiniteSolutions const &) {
88 return { side.pointAt(0), side.pointAt(1) };
89 }
90 }
91 return intersections;
92}
93
95{
96 defer([=, this] {
97 if (_origin == point) return;
98 _origin = point;
100 });
101}
102
104{
105 defer([=, this] {
106 if (_major_color == color) return;
107 _major_color = color;
109 });
110}
111
113{
114 defer([=, this] {
115 if (_minor_color == color) return;
116 _minor_color = color;
118 });
119}
120
122{
123 defer([=, this] {
124 if (_dotted == dotted) return;
125 _dotted = dotted;
127 });
128}
129
131{
132 defer([=, this] {
133 if (_spacing == point) return;
134 _spacing = point;
136 });
137}
138
140{
141 if (n < 1) return;
142 defer([=, this] {
143 if (_major_line_interval == n) return;
146 });
147}
148
150{
151 if (_no_emp_when_zoomed_out != noemp) {
154 }
155}
156
160 : CanvasItemGrid(group)
161{
162 _name = "CanvasItemGridXY";
163}
164
166{
168
169 // Queue redraw of grid area
170 ow = _origin * affine();
173
174 // Find suitable grid spacing for display
175 for (int dim : {0, 1}) {
176 int const scaling_factor = calculate_scaling_factor(sw[dim].length(), _major_line_interval);
177 sw[dim] *= scaling_factor;
178 scaled[dim] = scaling_factor > 1;
179 }
180
182}
183
185{
186 // no_emphasize_when_zoomedout determines color (minor or major) when only major grid lines/dots shown.
188 uint32_t color = _minor_color;
189
190 buf.cr->save();
191 buf.cr->translate(-buf.rect.left(), -buf.rect.top());
192 buf.cr->set_line_width(1.0);
193 buf.cr->set_line_cap(Cairo::Context::LineCap::SQUARE);
194
195 // Add a 2px margin to the buffer rectangle to avoid missing intersections (in case of rounding errors, and due to adding 0.5 below)
196 auto const buf_rect_with_margin = expandedBy(buf.rect, 2);
197
198 for (int dim : {0, 1}) {
199 int const nrm = dim ^ 0x1;
200
201 // Construct an axis line through origin with direction normal to grid spacing.
204
205 double spacing = sw[nrm].length(); // Spacing between grid lines.
206 double dash = sw[dim].length(); // Total length of dash pattern.
207
208 // Find the minimum and maximum distances of the buffer corners from axis.
209 double min = Geom::infinity();
210 double max = -Geom::infinity();
211 for (int c = 0; c < 4; ++c) {
212
213 // We need signed distance... lib2geom offers only positive distance.
214 double distance = signed_distance(buf_rect_with_margin.corner(c), axis);
215
216 // Correct it for coordinate flips (inverts handedness).
217 if (Geom::cross(axis.vector(), orth.vector()) > 0) {
219 }
220
221 min = std::min(min, distance);
222 max = std::max(max, distance);
223 }
224 int start = std::floor(min / spacing);
225 int stop = std::floor(max / spacing);
226
227 // Loop over grid lines that intersected buf rectangle.
228 for (int j = start + 1; j <= stop; ++j) {
229
230 Geom::Line grid_line = Geom::make_parallel_line(ow + j * sw[nrm], axis);
231
232 std::vector<Geom::Point> x = intersect_line_rectangle(grid_line, buf_rect_with_margin);
233
234 // If we have two intersections, grid line intersects buffer rectangle.
235 if (x.size() == 2) {
236 // Make sure lines are always drawn in the same direction (or dashes misplaced).
237 Geom::Line vector(x[0], x[1]);
238 if (Geom::dot(vector.vector(), axis.vector()) < 0.0) {
239 std::swap(x[0], x[1]);
240 }
241
242 // Set up line. Need to use floor()+0.5 such that Cairo will draw us lines with a width of a single pixel, without any aliasing.
243 // For this we need to position the lines at exactly half pixels, see https://www.cairographics.org/FAQ/#sharp_lines
244 // Must be consistent with the pixel alignment of the guide lines, see CanvasItemGridXY::render(), and the drawing of the rulers
245 buf.cr->move_to(floor(x[0].x()) + 0.5, floor(x[0].y()) + 0.5);
246 buf.cr->line_to(floor(x[1].x()) + 0.5, floor(x[1].y()) + 0.5);
247
248 // Determine whether to draw with the emphasis color.
249 bool const noemp = !scaled[dim] && j % _major_line_interval != 0;
250
251 // Set dash pattern and color.
252 if (_dotted) {
253 // alpha needs to be larger than in the line case to maintain a similar
254 // visual impact but setting it to the maximal value makes the dots
255 // dominant in some cases. Solution, increase the alpha by a factor of
256 // 4. This then allows some user adjustment.
257 uint32_t _empdot = (empcolor & 0xff) << 2;
258 if (_empdot > 0xff)
259 _empdot = 0xff;
260 _empdot += (empcolor & 0xffffff00);
261
262 uint32_t _colordot = (color & 0xff) << 2;
263 if (_colordot > 0xff)
264 _colordot = 0xff;
265 _colordot += (color & 0xffffff00);
266
267 // Dash pattern must use spacing from orthogonal direction.
268 // Offset is to center dash on orthogonal lines.
269 double offset = std::fmod(signed_distance(x[0], orth), sw[dim].length());
270 if (Geom::cross(axis.vector(), orth.vector()) > 0) {
271 offset = -offset;
272 }
273
274 std::vector<double> dashes;
275 if (noemp) {
276 // Minor lines
277 dashes.push_back(1.0);
278 dashes.push_back(dash - 1.0);
279 offset -= 0.5;
280 buf.cr->set_source_rgba(SP_RGBA32_R_F(_colordot), SP_RGBA32_G_F(_colordot),
281 SP_RGBA32_B_F(_colordot), SP_RGBA32_A_F(_colordot));
282 } else {
283 // Major lines
284 dashes.push_back(3.0);
285 dashes.push_back(dash - 3.0);
286 offset -= 1.5; // Center dash on intersection.
287 buf.cr->set_source_rgba(SP_RGBA32_R_F(_empdot), SP_RGBA32_G_F(_empdot),
288 SP_RGBA32_B_F(_empdot), SP_RGBA32_A_F(_empdot));
289 }
290
291 buf.cr->set_line_cap(Cairo::Context::LineCap::BUTT);
292 buf.cr->set_dash(dashes, -offset);
293
294 } else {
295 // Solid lines
296 uint32_t col = noemp ? color : empcolor;
297 buf.cr->set_source_rgba(SP_RGBA32_R_F(col), SP_RGBA32_G_F(col),
298 SP_RGBA32_B_F(col), SP_RGBA32_A_F(col));
299 }
300
301 buf.cr->stroke();
302
303 } else {
304 std::cerr << "CanvasItemGridXY::render: Grid line doesn't intersect!" << std::endl;
305 }
306 }
307 }
308
309 buf.cr->restore();
310}
311
314/*
315 * Current limits are: one axis (y-axis) is always vertical. The other two
316 * axes are bound to a certain range of angles. The z-axis always has an angle
317 * smaller than 90 degrees (measured from horizontal, 0 degrees being a line extending
318 * to the right). The x-axis will always have an angle between 0 and 90 degrees.
319 */
321 : CanvasItemGrid(group)
322{
323 _name = "CanvasItemGridAxonom";
324
325 angle_deg[X] = 30.0;
326 angle_deg[Y] = 30.0;
327 angle_deg[Z] = 0.0;
328
329 angle_rad[X] = Geom::rad_from_deg(angle_deg[X]);
330 angle_rad[Y] = Geom::rad_from_deg(angle_deg[Y]);
331 angle_rad[Z] = Geom::rad_from_deg(angle_deg[Z]);
332
333 tan_angle[X] = std::tan(angle_rad[X]);
334 tan_angle[Y] = std::tan(angle_rad[Y]);
335 tan_angle[Z] = std::tan(angle_rad[Z]);
336}
337
339{
341
342 ow = _origin * affine();
343 lyw = _spacing.y() * affine().descrim();
344
345 int const scaling_factor = calculate_scaling_factor(lyw, _major_line_interval);
346 lyw *= scaling_factor;
347 scaled = scaling_factor > 1;
348
352
353 if (_major_line_interval == 0) {
354 scaled = true;
355 }
356
358}
359
360// expects value given to be in degrees
362{
363 defer([=, this] {
364 angle_deg[X] = std::clamp(deg, 0.0, 89.0); // setting to 90 and values close cause extreme slowdowns
365 angle_rad[X] = Geom::rad_from_deg(angle_deg[X]);
366 tan_angle[X] = std::tan(angle_rad[X]);
368 });
369}
370
371// expects value given to be in degrees
373{
374 defer([=, this] {
375 angle_deg[Z] = std::clamp(deg, 0.0, 89.0); // setting to 90 and values close cause extreme slowdowns
376 angle_rad[Z] = Geom::rad_from_deg(angle_deg[Z]);
377 tan_angle[Z] = std::tan(angle_rad[Z]);
379 });
380}
381
382static void drawline(Inkscape::CanvasItemBuffer &buf, int x0, int y0, int x1, int y1, uint32_t rgba)
383{
384 buf.cr->move_to(0.5 + x0, 0.5 + y0);
385 buf.cr->line_to(0.5 + x1, 0.5 + y1);
386 buf.cr->set_source_rgba(SP_RGBA32_R_F(rgba), SP_RGBA32_G_F(rgba),
387 SP_RGBA32_B_F(rgba), SP_RGBA32_A_F(rgba));
388 buf.cr->stroke();
389}
390
391static void vline(Inkscape::CanvasItemBuffer &buf, int x, int ys, int ye, uint32_t rgba)
392{
393 if (x < buf.rect.left() || x >= buf.rect.right())
394 return;
395
396 buf.cr->move_to(0.5 + x, 0.5 + ys);
397 buf.cr->line_to(0.5 + x, 0.5 + ye);
398 buf.cr->set_source_rgba(SP_RGBA32_R_F(rgba), SP_RGBA32_G_F(rgba),
399 SP_RGBA32_B_F(rgba), SP_RGBA32_A_F(rgba));
400 buf.cr->stroke();
401}
402
408{
409 // Set correct coloring, depending preference (when zoomed out, always major coloring or minor coloring)
410 uint32_t empcolor = (scaled && _no_emp_when_zoomed_out) ? _minor_color : _major_color;
411 uint32_t color = _minor_color;
412
413 buf.cr->save();
414 buf.cr->translate(-buf.rect.left(), -buf.rect.top());
415 buf.cr->set_line_width(1.0);
416 buf.cr->set_line_cap(Cairo::Context::LineCap::SQUARE);
417
418 // gc = gridcoordinates (the coordinates calculated from the grids origin 'grid->ow'.
419 // sc = screencoordinates ( for example "buf.rect.left()" is in screencoordinates )
420 // bc = buffer patch coordinates (x=0 on left side of page, y=0 on bottom of page)
421
422 // tl = topleft
423 auto const buf_tl_gc = buf.rect.min() - ow;
424
425 // render the three separate line groups representing the main-axes
426
427 // x-axis always goes from topleft to bottomright. (0,0) - (1,1)
428 double const xintercept_y_bc = (buf_tl_gc.x() * tan_angle[X]) - buf_tl_gc.y();
429 double const xstart_y_sc = (xintercept_y_bc - std::floor(xintercept_y_bc / lyw) * lyw) + buf.rect.top();
430 int const xlinestart = std::round((xstart_y_sc - buf_tl_gc.x() * tan_angle[X] - ow.y()) / lyw);
431 int xlinenum = xlinestart;
432
433 // lines starting on left side.
434 for (double y = xstart_y_sc; y < buf.rect.bottom(); y += lyw, xlinenum++) {
435 int const x0 = buf.rect.left();
436 int const y0 = round(y);
437 int x1 = x0 + round((buf.rect.bottom() - y) / tan_angle[X]);
438 int y1 = buf.rect.bottom();
439 if (Geom::are_near(tan_angle[X], 0)) {
440 x1 = buf.rect.right();
441 y1 = y0;
442 }
443
444 bool const noemp = !scaled && xlinenum % _major_line_interval != 0;
445 drawline(buf, x0, y0, x1, y1, noemp ? color : empcolor);
446 }
447
448 // lines starting from top side
449 if (!Geom::are_near(tan_angle[X], 0)) {
450 double const xstart_x_sc = buf.rect.left() + (lxw_x - (xstart_y_sc - buf.rect.top()) / tan_angle[X]);
451 xlinenum = xlinestart-1;
452 for (double x = xstart_x_sc; x < buf.rect.right(); x += lxw_x, xlinenum--) {
453 int const y0 = buf.rect.top();
454 int const y1 = buf.rect.bottom();
455 int const x0 = round(x);
456 int const x1 = x0 + round((y1 - y0) / tan_angle[X]);
457
458 bool const noemp = !scaled && xlinenum % _major_line_interval != 0;
459 drawline(buf, x0, y0, x1, y1, noemp ? color : empcolor);
460 }
461 }
462
463 // y-axis lines (vertical)
464 double const ystart_x_sc = floor (buf_tl_gc[Geom::X] / spacing_ylines) * spacing_ylines + ow[Geom::X];
465 int const ylinestart = round((ystart_x_sc - ow[Geom::X]) / spacing_ylines);
466 int ylinenum = ylinestart;
467 for (double x = ystart_x_sc; x < buf.rect.right(); x += spacing_ylines, ylinenum++) {
468 int const x0 = floor(x); // sp_grid_vline will add 0.5 again, so we'll pre-emptively use floor()
469 // instead of round() to avoid biasing the vertical lines to the right by half a pixel; see
470 // CanvasItemGridXY::render() for more details
471 bool const noemp = !scaled && ylinenum % _major_line_interval != 0;
472 vline(buf, x0, buf.rect.top(), buf.rect.bottom() - 1, noemp ? color : empcolor);
473 }
474
475 // z-axis always goes from bottomleft to topright. (0,1) - (1,0)
476 double const zintercept_y_bc = (buf_tl_gc.x() * -tan_angle[Z]) - buf_tl_gc.y();
477 double const zstart_y_sc = (zintercept_y_bc - std::floor(zintercept_y_bc / lyw) * lyw) + buf.rect.top();
478 int const zlinestart = std::round((zstart_y_sc + buf_tl_gc.x() * tan_angle[Z] - ow.y()) / lyw);
479 int zlinenum = zlinestart;
480 // lines starting from left side
481 double next_y = zstart_y_sc;
482 for (double y = zstart_y_sc; y < buf.rect.bottom(); y += lyw, zlinenum++, next_y = y) {
483 int const x0 = buf.rect.left();
484 int const y0 = round(y);
485 int x1 = x0 + round((y - buf.rect.top()) / tan_angle[Z]);
486 int y1 = buf.rect.top();
487 if (Geom::are_near(tan_angle[Z], 0)) {
488 x1 = buf.rect.right();
489 y1 = y0;
490 }
491
492 bool const noemp = !scaled && zlinenum % _major_line_interval != 0;
493 drawline(buf, x0, y0, x1, y1, noemp ? color : empcolor);
494 }
495
496 // draw lines from bottom-up
497 if (!Geom::are_near(tan_angle[Z], 0)) {
498 double const zstart_x_sc = buf.rect.left() + (next_y - buf.rect.bottom()) / tan_angle[Z];
499 for (double x = zstart_x_sc; x < buf.rect.right(); x += lxw_z, zlinenum++) {
500 int const y0 = buf.rect.bottom();
501 int const y1 = buf.rect.top();
502 int const x0 = round(x);
503 int const x1 = x0 + round(buf.rect.height() / tan_angle[Z]);
504
505 bool const noemp = !scaled && zlinenum % _major_line_interval != 0;
506 drawline(buf, x0, y0, x1, y1, noemp ? color : empcolor);
507 }
508 }
509
510 buf.cr->restore();
511}
512
514 : CanvasItemGrid(group)
515{
516 _name = "CanvasItemGridTiles";
517}
518
520 defer([=, this] {
521 if (_gap == gap_size) return;
522 _gap = gap_size;
524 });
525}
526
528 defer([=, this] {
529 if (_margin == margin_size) return;
530 _margin = margin_size;
532 });
533}
534
537
538 // Queue redraw of grid area
540 auto tile = _spacing;
541 auto pitch = tile + _gap;
542
547
549}
550
552 // minute tiles bring no real value, they look like noise, skip them
553 if (_world_pitch.x() < 3 || _world_pitch.y() < 3) return;
554
555 buf.cr->save();
556 buf.cr->translate(-buf.rect.left(), -buf.rect.top());
557 buf.cr->set_line_width(1.0);
558 buf.cr->set_line_cap(Cairo::Context::LineCap::BUTT);
559
560 // Add a 2px margin to the buffer rectangle to avoid missing rectangles (in case of rounding errors, and due to adding 0.5 below)
561 auto const buf_rect_with_margin = expandedBy(buf.rect, 2);
562
563 auto mod = [](double v, double m){
564 return v >= 0 ? fmod(v, m) : m - fmod(-v, m);
565 };
566 auto min = Geom::Point(buf_rect_with_margin.min());
567 auto start = min;
568 start.x() -= mod(start.x(), _world_pitch.x());
569 start.y() -= mod(start.y(), _world_pitch.y());
570 start.x() += mod(_world_origin.x(), _world_pitch.x());
571 start.y() += mod(_world_origin.y(), _world_pitch.y());
572 if (start.x() > min.x()) start.x() -= _world_pitch.x();
573 if (start.y() > min.y()) start.y() -= _world_pitch.y();
574
575 auto end = Geom::Point(buf_rect_with_margin.max());
576 // pixel grid align coordinates for drawing lines with Cairo
577 auto pix_grid = [](double p){ return floor(p) + 0.5; };
578
579 if (_world_tile.x() >= 2 && _world_tile.y() >= 2) {
580 for (auto x = start.x(); x < end.x(); x += _world_pitch.x()) {
581 for (auto y = start.y(); y < end.y(); y += _world_pitch.y()) {
582 auto left = pix_grid(x + _world_gap.x() / 2);
583 auto top = pix_grid(y + _world_gap.y() / 2);
584 auto right = pix_grid(x + _world_gap.x() / 2 + _world_tile.x());
585 auto bottom = pix_grid(y + _world_gap.y() / 2 + _world_tile.y());
586 auto width = right - left;
587 auto height = bottom - top;
588 if (width > 0 && height > 0) {
589 buf.cr->rectangle(left, top, width, height);
590 }
591 }
592 }
593
594 uint32_t rgba = _major_color;
595 buf.cr->set_source_rgba(SP_RGBA32_R_F(rgba), SP_RGBA32_G_F(rgba),
596 SP_RGBA32_B_F(rgba), SP_RGBA32_A_F(rgba));
597 buf.cr->stroke();
598 }
599
601 if (size.x() >= 2 && size.y() >= 2 && (_world_margin.x() != 0 || _world_margin.y() != 0)) {
602 for (auto x = start.x(); x < end.x(); x += _world_pitch.x()) {
603 for (auto y = start.y(); y < end.y(); y += _world_pitch.y()) {
604 auto left = pix_grid(x + _world_gap.x() / 2 - _world_margin.x());
605 auto top = pix_grid(y + _world_gap.y() / 2 - _world_margin.y());
606 auto right = pix_grid(x + _world_gap.x() / 2 + _world_margin.x() + _world_tile.x());
607 auto bottom = pix_grid(y + _world_gap.y() / 2 + _world_margin.y() + _world_tile.y());
608 auto width = right - left;
609 auto height = bottom - top;
610 if (width > 0 && height > 0) {
611 buf.cr->rectangle(left, top, width, height);
612 }
613 }
614 }
615
616 uint32_t rgba = _minor_color;
617 buf.cr->set_source_rgba(SP_RGBA32_R_F(rgba), SP_RGBA32_G_F(rgba),
618 SP_RGBA32_B_F(rgba), SP_RGBA32_A_F(rgba));
619 buf.cr->stroke();
620 }
621
622 buf.cr->restore();
623}
624
625
626} // namespace Inkscape
627
628/*
629 Local Variables:
630 mode:c++
631 c-file-style:"stroustrup"
632 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
633 indent-tabs-mode:nil
634 fill-column:99
635 End:
636*/
637// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
double distance(Shape const *s, Geom::Point const &p)
Definition Shape.cpp:2136
static int calculate_scaling_factor(double length, int major)
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
Affine withoutTranslation() const
Definition affine.h:169
Point pointAt(Coord t) const override
Evaluate the curve at a specified time value.
CPoint corner(unsigned i) const
Return the n-th corner of the rectangle.
Infinite line on a plane.
Definition line.h:53
Point vector() const
Get the line's raw direction vector.
Definition line.h:132
static Line from_origin_and_vector(Point const &o, Point const &v)
Create a line from origin and unit vector.
Definition line.h:114
Point initialPoint() const
Definition line.h:225
Point pointAt(Coord t) const
Definition line.h:231
Point versor() const
Get the line's normalized direction vector.
Definition line.h:135
Two-dimensional point that doubles as a vector.
Definition point.h:66
Coord length() const
Compute the distance from origin.
Definition point.h:118
constexpr Coord y() const noexcept
Definition point.h:106
constexpr Coord x() const noexcept
Definition point.h:104
Axis aligned, non-empty rectangle.
Definition rect.h:92
double angle_rad[3]
Angle of each axis (note that angle[2] == 0)
void _update(bool propagate) override
bool scaled
Whether the grid is in scaled mode.
double lyw
Transformed length y by the affine for the zoom.
CanvasItemGridAxonom(CanvasItemGroup *group)
========= Axonometric Grids ========
void _render(CanvasItemBuffer &buf) const override
This function calls Cairo to render a line on a particular canvas buffer.
Geom::Point ow
Transformed origin by the affine for the zoom.
double angle_deg[3]
Angle of each axis (note that angle[2] == 0)
void set_margin_size(Geom::Point margin_size)
CanvasItemGridTiles(CanvasItemGroup *group)
void set_gap_size(Geom::Point gap_size)
void _update(bool propagate) override
void _render(CanvasItemBuffer &buf) const override
Geom::Point ow
Transformed origin by the affine for the zoom.
CanvasItemGridXY(CanvasItemGroup *group)
====== Rectangular Grid ======
void _update(bool propagate) override
void _render(CanvasItemBuffer &buf) const override
Geom::Point sw[2]
Transformed spacing by the affine for the zoom.
bool scaled[2]
Whether the grid is in scaled mode, which can be different in the X or Y direction,...
void set_no_emp_when_zoomed_out(bool noemp)
void set_spacing(Geom::Point const &point)
CanvasItemGrid(CanvasItemGroup *group)
Create a null control grid.
void set_minor_color(uint32_t color)
void set_origin(Geom::Point const &point)
Geom::Point _spacing
Spacing between elements of the grid.
std::unique_ptr< Preferences::PreferencesObserver > _pref_tracker
bool contains(Geom::Point const &p, double tolerance=0) override
Returns true if point p (in canvas units) is within tolerance (canvas units) distance of grid.
void set_major_color(uint32_t color)
Geom::OptRect _bounds
Geom::Affine const & affine() const
static std::unique_ptr< PreferencesObserver > create(Glib::ustring path, std::function< void(const Preferences::Entry &new_value)> callback)
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
constexpr double SP_RGBA32_G_F(uint32_t v)
Definition utils.h:47
constexpr double SP_RGBA32_R_F(uint32_t v)
Definition utils.h:43
constexpr double SP_RGBA32_A_F(uint32_t v)
Definition utils.h:55
constexpr double SP_RGBA32_B_F(uint32_t v)
Definition utils.h:51
double c[8][4]
constexpr Coord infinity()
Get a value representing infinity.
Definition coord.h:88
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Specific geometry functions for Inkscape, not provided my lib2geom.
auto floor(Geom::Rect const &rect)
Definition geom.h:130
auto expandedBy(Geom::IntRect rect, int amount)
Definition geom.h:66
Infinite straight line.
double offset
Geom::Point start
Geom::Point end
Line make_parallel_line(Point const &p, Line const &line)
Definition line.h:488
OptCrossing intersection(Ray const &r1, Line const &l2)
Definition line.h:545
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
T dot(D2< T > const &a, D2< T > const &b)
Definition d2.h:355
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Helper class to stream background task notifications as a series of messages.
static double signed_distance(Geom::Point const &point, Geom::Line const &line)
static void drawline(Inkscape::CanvasItemBuffer &buf, int x0, int y0, int x1, int y1, uint32_t rgba)
static void vline(Inkscape::CanvasItemBuffer &buf, int x, int ys, int ye, uint32_t rgba)
static std::vector< Geom::Point > intersect_line_rectangle(Geom::Line const &line, Geom::Rect const &rect)
int buf
Class used when rendering canvas items.
double height
double width