643ffccdc1aac7b9ab80b77e785caec59386283a
1 #define INKSCAPE_CANVAS_GRID_C
3 /*
4 *
5 * Copyright (C) Johan Engelen 2006-2007 <johan@shouraizou.nl>
6 * Copyright (C) Lauris Kaplinski 2000
7 *
8 */
11 #include "sp-canvas-util.h"
12 #include "display-forward.h"
13 #include <libnr/nr-pixops.h>
14 #include "desktop-handles.h"
15 #include "helper/units.h"
16 #include "svg/svg-color.h"
17 #include "xml/node-event-vector.h"
18 #include "sp-object.h"
20 #include "sp-namedview.h"
21 #include "inkscape.h"
22 #include "desktop.h"
24 #include "../document.h"
26 #include "canvas-grid.h"
27 #include "canvas-axonomgrid.h"
29 namespace Inkscape {
31 static void grid_canvasitem_class_init (GridCanvasItemClass *klass);
32 static void grid_canvasitem_init (GridCanvasItem *grid);
33 static void grid_canvasitem_destroy (GtkObject *object);
35 static void grid_canvasitem_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
36 static void grid_canvasitem_render (SPCanvasItem *item, SPCanvasBuf *buf);
38 static SPCanvasItemClass * parent_class;
40 GtkType
41 grid_canvasitem_get_type (void)
42 {
43 static GtkType grid_canvasitem_type = 0;
45 if (!grid_canvasitem_type) {
46 GtkTypeInfo grid_canvasitem_info = {
47 "GridCanvasItem",
48 sizeof (GridCanvasItem),
49 sizeof (GridCanvasItemClass),
50 (GtkClassInitFunc) grid_canvasitem_class_init,
51 (GtkObjectInitFunc) grid_canvasitem_init,
52 NULL, NULL,
53 (GtkClassInitFunc) NULL
54 };
55 grid_canvasitem_type = gtk_type_unique (sp_canvas_item_get_type (), &grid_canvasitem_info);
56 }
57 return grid_canvasitem_type;
58 }
60 static void
61 grid_canvasitem_class_init (GridCanvasItemClass *klass)
62 {
63 GtkObjectClass *object_class;
64 SPCanvasItemClass *item_class;
66 object_class = (GtkObjectClass *) klass;
67 item_class = (SPCanvasItemClass *) klass;
69 parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
71 object_class->destroy = grid_canvasitem_destroy;
73 item_class->update = grid_canvasitem_update;
74 item_class->render = grid_canvasitem_render;
75 }
77 static void
78 grid_canvasitem_init (GridCanvasItem *griditem)
79 {
80 griditem->grid = NULL;
81 }
83 static void
84 grid_canvasitem_destroy (GtkObject *object)
85 {
86 g_return_if_fail (object != NULL);
87 g_return_if_fail (INKSCAPE_IS_GRID_CANVASITEM (object));
89 if (GTK_OBJECT_CLASS (parent_class)->destroy)
90 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
91 }
93 /**
94 */
95 static void
96 grid_canvasitem_render (SPCanvasItem * item, SPCanvasBuf * buf)
97 {
98 GridCanvasItem *gridcanvasitem = INKSCAPE_GRID_CANVASITEM (item);
100 sp_canvas_prepare_buffer (buf);
102 if (gridcanvasitem->grid) gridcanvasitem->grid->Render(buf);
103 }
105 static void
106 grid_canvasitem_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
107 {
108 GridCanvasItem *gridcanvasitem = INKSCAPE_GRID_CANVASITEM (item);
110 if (parent_class->update)
111 (* parent_class->update) (item, affine, flags);
113 if (gridcanvasitem->grid) {
114 gridcanvasitem->grid->Update(affine, flags);
116 sp_canvas_request_redraw (item->canvas,
117 -1000000, -1000000,
118 1000000, 1000000);
120 item->x1 = item->y1 = -1000000;
121 item->x2 = item->y2 = 1000000;
122 }
123 }
127 // ##########################################################
128 // CanvasGrid
130 static Inkscape::XML::NodeEventVector const _repr_events = {
131 NULL, /* child_added */
132 NULL, /* child_removed */
133 CanvasGrid::on_repr_attr_changed,
134 NULL, /* content_changed */
135 NULL /* order_changed */
136 };
138 CanvasGrid::CanvasGrid(SPDesktop *desktop, Inkscape::XML::Node * in_repr)
139 {
140 //create canvasitem
141 // FIXME: probably this creation has to be done on demand. I think for multiple desktops it is best if each has their own canvasitem, but share the same CanvasGrid object.
142 canvasitem = INKSCAPE_GRID_CANVASITEM( sp_canvas_item_new(sp_desktop_grid(desktop), INKSCAPE_TYPE_GRID_CANVASITEM, NULL) );
143 gtk_object_ref(GTK_OBJECT(canvasitem)); // since we're keeping a copy, we need to bump up the ref count
144 canvasitem->grid = this;
146 enabled = false;
147 visible = false;
149 // sp_canvas_item_hide(canvasitem);
151 repr = in_repr;
152 if (repr) {
153 repr->addListener (&_repr_events, this);
154 }
156 namedview = sp_desktop_namedview(desktop);
157 }
159 CanvasGrid::~CanvasGrid()
160 {
161 if (repr) {
162 repr->removeListenerByData (this);
163 }
165 sp_canvas_item_hide(canvasitem);
166 // deref canvasitem
167 gtk_object_unref(GTK_OBJECT(canvasitem));
168 g_free(canvasitem);
169 g_message("~CanvasGrid");
170 }
172 /*
173 * writes an <inkscape:grid> child to repr.
174 */
175 void
176 CanvasGrid::writeNewGridToRepr(Inkscape::XML::Node * repr, const char * gridtype)
177 {
178 if (!repr) return;
179 if (!gridtype) return;
181 // first create the child xml node, then hook it to repr. This order is important, to not set off listeners to repr before the new node is complete.
183 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(sp_desktop_document(SP_ACTIVE_DESKTOP));
184 Inkscape::XML::Node *newnode;
185 newnode = xml_doc->createElement("inkscape:grid");
186 newnode->setAttribute("type",gridtype);
188 repr->appendChild(newnode);
190 // FIXME: add this to history?
191 // sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
192 // _("Create new element node"));
193 }
195 /*
196 * Creates a new CanvasGrid object of type gridtype
197 */
198 CanvasGrid*
199 CanvasGrid::NewGrid(SPDesktop *desktop, Inkscape::XML::Node * in_repr, const char * gridtype)
200 {
201 if (!desktop) return NULL;
202 if (!in_repr) return NULL;
203 if (!gridtype) return NULL;
205 if (!strcmp(gridtype,"xygrid")) {
206 return (CanvasGrid*) new CanvasXYGrid(desktop, in_repr);
207 } else if (!strcmp(gridtype,"axonometric")) {
208 return (CanvasGrid*) new CanvasAxonomGrid(desktop, in_repr);
209 }
211 return NULL;
212 }
215 void
216 CanvasGrid::hide()
217 {
218 sp_canvas_item_hide(canvasitem);
219 visible = false;
220 }
222 void
223 CanvasGrid::show()
224 {
225 sp_canvas_item_show(canvasitem);
226 visible = true;
227 }
229 void
230 CanvasGrid::on_repr_attr_changed (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive, void * data)
231 {
232 if (!data)
233 return;
235 ((CanvasGrid*) data)->onReprAttrChanged(repr, key, oldval, newval, is_interactive);
236 }
240 // ##########################################################
241 // CanvasXYGrid
243 static void grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba);
244 static void grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba);
247 /**
248 * A DIRECT COPY-PASTE FROM DOCUMENT-PROPERTIES.CPP TO QUICKLY GET RESULTS
249 *
250 * Helper function that attachs widgets in a 3xn table. The widgets come in an
251 * array that has two entries per table row. The two entries code for four
252 * possible cases: (0,0) means insert space in first column; (0, non-0) means
253 * widget in columns 2-3; (non-0, 0) means label in columns 1-3; and
254 * (non-0, non-0) means two widgets in columns 2 and 3.
255 **/
256 #define SPACE_SIZE_X 15
257 #define SPACE_SIZE_Y 10
258 static inline void
259 attach_all (Gtk::Table &table, const Gtk::Widget *arr[], unsigned size, int start = 0)
260 {
261 for (unsigned i=0, r=start; i<size/sizeof(Gtk::Widget*); i+=2)
262 {
263 if (arr[i] && arr[i+1])
264 {
265 table.attach (const_cast<Gtk::Widget&>(*arr[i]), 1, 2, r, r+1,
266 Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
267 table.attach (const_cast<Gtk::Widget&>(*arr[i+1]), 2, 3, r, r+1,
268 Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
269 }
270 else
271 {
272 if (arr[i+1])
273 table.attach (const_cast<Gtk::Widget&>(*arr[i+1]), 1, 3, r, r+1,
274 Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
275 else if (arr[i])
276 {
277 Gtk::Label& label = reinterpret_cast<Gtk::Label&> (const_cast<Gtk::Widget&>(*arr[i]));
278 label.set_alignment (0.0);
279 table.attach (label, 0, 3, r, r+1,
280 Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
281 }
282 else
283 {
284 Gtk::HBox *space = manage (new Gtk::HBox);
285 space->set_size_request (SPACE_SIZE_X, SPACE_SIZE_Y);
286 table.attach (*space, 0, 1, r, r+1,
287 (Gtk::AttachOptions)0, (Gtk::AttachOptions)0,0,0);
288 }
289 }
290 ++r;
291 }
292 }
294 CanvasXYGrid::CanvasXYGrid (SPDesktop *desktop, Inkscape::XML::Node * in_repr)
295 : CanvasGrid(desktop, in_repr), table(1, 1)
296 {
297 origin[NR::X] = origin[NR::Y] = 0.0;
298 // nv->gridcolor = (nv->gridcolor & 0xff) | (DEFAULTGRIDCOLOR & 0xffffff00);
299 // case SP_ATTR_GRIDOPACITY:
300 // nv->gridcolor = (nv->gridcolor & 0xffffff00) | (DEFAULTGRIDCOLOR & 0xff);
301 color = 0xff3f3f20;
302 empcolor = 0xFF3F3F40;
303 empspacing = 5;
304 spacing[NR::X] = spacing[NR::Y] = 8.0;
305 gridunit = &sp_unit_get_by_id(SP_UNIT_PX);
307 snapper = new CanvasXYGridSnapper(this, namedview, 0);
309 // initialize widgets:
310 vbox.set_border_width(2);
311 table.set_spacings(2);
312 vbox.pack_start(table, false, false, 0);
314 _rumg.init (_("Grid _units:"), "units", _wr, repr);
315 _rsu_ox.init (_("_Origin X:"), _("X coordinate of grid origin"),
316 "originx", _rumg, _wr, repr);
317 _rsu_oy.init (_("O_rigin Y:"), _("Y coordinate of grid origin"),
318 "originy", _rumg, _wr, repr);
319 _rsu_sx.init (_("Spacing _X:"), _("Distance between vertical grid lines"),
320 "spacingx", _rumg, _wr, repr);
321 _rsu_sy.init (_("Spacing _Y:"), _("Distance between horizontal grid lines"),
322 "spacingy", _rumg, _wr, repr);
323 _rcp_gcol.init (_("Grid line _color:"), _("Grid line color"),
324 _("Color of grid lines"), "color", "opacity", _wr, repr);
325 _rcp_gmcol.init (_("Ma_jor grid line color:"), _("Major grid line color"),
326 _("Color of the major (highlighted) grid lines"),
327 "empcolor", "empopacity", _wr, repr);
328 _rsi.init (_("_Major grid line every:"), _("lines"), "empspacing", _wr, repr);
330 const Gtk::Widget* widget_array[] =
331 {
332 0, _rcbgrid._button,
333 _rumg._label, _rumg._sel,
334 0, _rsu_ox.getSU(),
335 0, _rsu_oy.getSU(),
336 0, _rsu_sx.getSU(),
337 0, _rsu_sy.getSU(),
338 _rcp_gcol._label, _rcp_gcol._cp,
339 0, 0,
340 _rcp_gmcol._label, _rcp_gmcol._cp,
341 _rsi._label, &_rsi._hbox,
342 };
344 attach_all (table, widget_array, sizeof(widget_array));
346 vbox.show();
348 if (repr) readRepr();
349 updateWidgets();
350 }
352 CanvasXYGrid::~CanvasXYGrid ()
353 {
354 if (snapper) delete snapper;
355 }
358 /* fixme: Collect all these length parsing methods and think common sane API */
360 static gboolean sp_nv_read_length(const gchar *str, guint base, gdouble *val, const SPUnit **unit)
361 {
362 if (!str) {
363 return FALSE;
364 }
366 gchar *u;
367 gdouble v = g_ascii_strtod(str, &u);
368 if (!u) {
369 return FALSE;
370 }
371 while (isspace(*u)) {
372 u += 1;
373 }
375 if (!*u) {
376 /* No unit specified - keep default */
377 *val = v;
378 return TRUE;
379 }
381 if (base & SP_UNIT_DEVICE) {
382 if (u[0] && u[1] && !isalnum(u[2]) && !strncmp(u, "px", 2)) {
383 *unit = &sp_unit_get_by_id(SP_UNIT_PX);
384 *val = v;
385 return TRUE;
386 }
387 }
389 if (base & SP_UNIT_ABSOLUTE) {
390 if (!strncmp(u, "pt", 2)) {
391 *unit = &sp_unit_get_by_id(SP_UNIT_PT);
392 } else if (!strncmp(u, "mm", 2)) {
393 *unit = &sp_unit_get_by_id(SP_UNIT_MM);
394 } else if (!strncmp(u, "cm", 2)) {
395 *unit = &sp_unit_get_by_id(SP_UNIT_CM);
396 } else if (!strncmp(u, "m", 1)) {
397 *unit = &sp_unit_get_by_id(SP_UNIT_M);
398 } else if (!strncmp(u, "in", 2)) {
399 *unit = &sp_unit_get_by_id(SP_UNIT_IN);
400 } else {
401 return FALSE;
402 }
403 *val = v;
404 return TRUE;
405 }
407 return FALSE;
408 }
410 static gboolean sp_nv_read_opacity(const gchar *str, guint32 *color)
411 {
412 if (!str) {
413 return FALSE;
414 }
416 gchar *u;
417 gdouble v = g_ascii_strtod(str, &u);
418 if (!u) {
419 return FALSE;
420 }
421 v = CLAMP(v, 0.0, 1.0);
423 *color = (*color & 0xffffff00) | (guint32) floor(v * 255.9999);
425 return TRUE;
426 }
430 void
431 CanvasXYGrid::readRepr()
432 {
433 gchar const* value;
434 if ( (value = repr->attribute("originx")) ) {
435 sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[NR::X], &gridunit);
436 origin[NR::X] = sp_units_get_pixels(origin[NR::X], *(gridunit));
437 }
438 if ( (value = repr->attribute("originy")) ) {
439 sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[NR::Y], &gridunit);
440 origin[NR::Y] = sp_units_get_pixels(origin[NR::Y], *(gridunit));
441 }
443 if ( (value = repr->attribute("spacingx")) ) {
444 sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &spacing[NR::X], &gridunit);
445 spacing[NR::X] = sp_units_get_pixels(spacing[NR::X], *(gridunit));
446 }
447 if ( (value = repr->attribute("spacingy")) ) {
448 sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &spacing[NR::Y], &gridunit);
449 spacing[NR::Y] = sp_units_get_pixels(spacing[NR::Y], *(gridunit));
450 }
452 if ( (value = repr->attribute("color")) ) {
453 color = (color & 0xff) | sp_svg_read_color(value, color);
454 }
456 if ( (value = repr->attribute("empcolor")) ) {
457 empcolor = (empcolor & 0xff) | sp_svg_read_color(value, empcolor);
458 }
460 if ( (value = repr->attribute("opacity")) ) {
461 sp_nv_read_opacity(value, &color);
462 }
463 if ( (value = repr->attribute("empopacity")) ) {
464 sp_nv_read_opacity(value, &empcolor);
465 }
467 if ( (value = repr->attribute("empspacing")) ) {
468 empspacing = atoi(value);
469 }
471 sp_canvas_item_request_update (canvasitem);
473 return;
474 }
476 /**
477 * Called when XML node attribute changed; updates dialog widgets if change was not done by widgets themselves.
478 */
479 void
480 CanvasXYGrid::onReprAttrChanged (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive)
481 {
482 readRepr();
484 if ( ! (_wr.isUpdating()) )
485 updateWidgets();
486 }
491 Gtk::Widget &
492 CanvasXYGrid::getWidget()
493 {
494 return vbox;
495 }
498 /**
499 * Update dialog widgets from object's values.
500 */
501 void
502 CanvasXYGrid::updateWidgets()
503 {
504 if (_wr.isUpdating()) return;
506 _wr.setUpdating (true);
508 // _rrb_gridtype.setValue (nv->gridtype);
509 _rumg.setUnit (gridunit);
511 gdouble val;
512 val = origin[NR::X];
513 val = sp_pixels_get_units (val, *(gridunit));
514 _rsu_ox.setValue (val);
515 val = origin[NR::Y];
516 val = sp_pixels_get_units (val, *(gridunit));
517 _rsu_oy.setValue (val);
518 val = spacing[NR::X];
519 double gridx = sp_pixels_get_units (val, *(gridunit));
520 _rsu_sx.setValue (gridx);
521 val = spacing[NR::Y];
522 double gridy = sp_pixels_get_units (val, *(gridunit));
523 _rsu_sy.setValue (gridy);
525 _rcp_gcol.setRgba32 (color);
526 _rcp_gmcol.setRgba32 (empcolor);
527 _rsi.setValue (empspacing);
529 _wr.setUpdating (false);
531 return;
532 }
536 void
537 CanvasXYGrid::Update (NR::Matrix const &affine, unsigned int flags)
538 {
539 ow = origin * affine;
540 sw = spacing * affine;
541 sw -= NR::Point(affine[4], affine[5]);
543 for(int dim = 0; dim < 2; dim++) {
544 gint scaling_factor = 5; //empspacing;
546 if (scaling_factor <= 1)
547 scaling_factor = 5;
549 scaled[dim] = FALSE;
550 sw[dim] = fabs (sw[dim]);
551 while (sw[dim] < 8.0) {
552 scaled[dim] = TRUE;
553 sw[dim] *= scaling_factor;
554 /* First pass, go up to the major line spacing, then
555 keep increasing by two. */
556 scaling_factor = 2;
557 }
558 }
559 }
561 void
562 CanvasXYGrid::Render (SPCanvasBuf *buf)
563 {
564 const gdouble sxg = floor ((buf->rect.x0 - ow[NR::X]) / sw[NR::X]) * sw[NR::X] + ow[NR::X];
565 const gint xlinestart = (gint) Inkscape::round((sxg - ow[NR::X]) / sw[NR::X]);
566 const gdouble syg = floor ((buf->rect.y0 - ow[NR::Y]) / sw[NR::Y]) * sw[NR::Y] + ow[NR::Y];
567 const gint ylinestart = (gint) Inkscape::round((syg - ow[NR::Y]) / sw[NR::Y]);
569 gint ylinenum;
570 gdouble y;
571 for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += sw[NR::Y], ylinenum++) {
572 const gint y0 = (gint) Inkscape::round(y);
574 if (!scaled[NR::Y] && (ylinenum % 5 /*empspacing*/) == 0) {
575 grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, empcolor);
576 } else {
577 grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, color);
578 }
579 }
581 gint xlinenum;
582 gdouble x;
583 for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += sw[NR::X], xlinenum++) {
584 const gint ix = (gint) Inkscape::round(x);
585 if (!scaled[NR::X] && (xlinenum % 5 /*empspacing*/) == 0) {
586 grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, empcolor);
587 } else {
588 grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, color);
589 }
590 }
591 }
604 /**
605 * \return x rounded to the nearest multiple of c1 plus c0.
606 *
607 * \note
608 * If c1==0 (and c0 is finite), then returns +/-inf. This makes grid spacing of zero
609 * mean "ignore the grid in this dimention". We're currently discussing "good" semantics
610 * for guide/grid snapping.
611 */
613 /* FIXME: move this somewhere else, perhaps */
614 static double round_to_nearest_multiple_plus(double x, double const c1, double const c0)
615 {
616 return floor((x - c0) / c1 + .5) * c1 + c0;
617 }
619 CanvasXYGridSnapper::CanvasXYGridSnapper(CanvasXYGrid *grid, SPNamedView const *nv, NR::Coord const d) : LineSnapper(nv, d)
620 {
621 this->grid = grid;
622 }
624 LineSnapper::LineList
625 CanvasXYGridSnapper::_getSnapLines(NR::Point const &p) const
626 {
627 LineList s;
629 if ( grid == NULL ) {
630 return s;
631 }
633 for (unsigned int i = 0; i < 2; ++i) {
635 /* This is to make sure we snap to only visible grid lines */
636 double scaled_spacing = grid->sw[i]; // this is spacing of visible lines if screen pixels
638 // convert screen pixels to px
639 // FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary
640 if (SP_ACTIVE_DESKTOP) {
641 scaled_spacing /= SP_ACTIVE_DESKTOP->current_zoom();
642 }
644 NR::Coord const rounded = round_to_nearest_multiple_plus(p[i],
645 scaled_spacing,
646 grid->origin[i]);
648 s.push_back(std::make_pair(NR::Dim2(i), rounded));
649 }
651 return s;
652 }
684 enum {
685 ARG_0,
686 ARG_ORIGINX,
687 ARG_ORIGINY,
688 ARG_SPACINGX,
689 ARG_SPACINGY,
690 ARG_COLOR,
691 ARG_EMPCOLOR,
692 ARG_EMPSPACING
693 };
696 static void cxygrid_class_init (CXYGridClass *klass);
697 static void cxygrid_init (CXYGrid *grid);
698 static void cxygrid_destroy (GtkObject *object);
699 static void cxygrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);
701 static void cxygrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
702 static void cxygrid_render (SPCanvasItem *item, SPCanvasBuf *buf);
704 //static SPCanvasItemClass * parent_class;
706 GtkType
707 cxygrid_get_type (void)
708 {
709 static GtkType cxygrid_type = 0;
711 if (!cxygrid_type) {
712 GtkTypeInfo cxygrid_info = {
713 "CXYGrid",
714 sizeof (CXYGrid),
715 sizeof (CXYGridClass),
716 (GtkClassInitFunc) cxygrid_class_init,
717 (GtkObjectInitFunc) cxygrid_init,
718 NULL, NULL,
719 (GtkClassInitFunc) NULL
720 };
721 cxygrid_type = gtk_type_unique (sp_canvas_item_get_type (), &cxygrid_info);
722 }
723 return cxygrid_type;
724 }
726 static void
727 cxygrid_class_init (CXYGridClass *klass)
728 {
729 GtkObjectClass *object_class;
730 SPCanvasItemClass *item_class;
732 object_class = (GtkObjectClass *) klass;
733 item_class = (SPCanvasItemClass *) klass;
735 parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
737 gtk_object_add_arg_type ("CXYGrid::originx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINX);
738 gtk_object_add_arg_type ("CXYGrid::originy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINY);
739 gtk_object_add_arg_type ("CXYGrid::spacingx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGX);
740 gtk_object_add_arg_type ("CXYGrid::spacingy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGY);
741 gtk_object_add_arg_type ("CXYGrid::color", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_COLOR);
742 gtk_object_add_arg_type ("CXYGrid::empcolor", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPCOLOR);
743 gtk_object_add_arg_type ("CXYGrid::empspacing", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPSPACING);
745 object_class->destroy = cxygrid_destroy;
746 object_class->set_arg = cxygrid_set_arg;
748 item_class->update = cxygrid_update;
749 item_class->render = cxygrid_render;
750 }
752 static void
753 cxygrid_init (CXYGrid *grid)
754 {
755 grid->origin[NR::X] = grid->origin[NR::Y] = 0.0;
756 grid->spacing[NR::X] = grid->spacing[NR::Y] = 8.0;
757 grid->color = 0x0000ff7f;
758 grid->empcolor = 0x3F3FFF40;
759 grid->empspacing = 5;
760 }
762 static void
763 cxygrid_destroy (GtkObject *object)
764 {
765 g_return_if_fail (object != NULL);
766 g_return_if_fail (INKSCAPE_IS_CXYGRID (object));
768 if (GTK_OBJECT_CLASS (parent_class)->destroy)
769 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
770 }
772 static void
773 cxygrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
774 {
775 SPCanvasItem *item = SP_CANVAS_ITEM (object);
776 CXYGrid *grid = INKSCAPE_CXYGRID (object);
778 switch (arg_id) {
779 case ARG_ORIGINX:
780 grid->origin[NR::X] = GTK_VALUE_DOUBLE (* arg);
781 sp_canvas_item_request_update (item);
782 break;
783 case ARG_ORIGINY:
784 grid->origin[NR::Y] = GTK_VALUE_DOUBLE (* arg);
785 sp_canvas_item_request_update (item);
786 break;
787 case ARG_SPACINGX:
788 grid->spacing[NR::X] = GTK_VALUE_DOUBLE (* arg);
789 if (grid->spacing[NR::X] < 0.01) grid->spacing[NR::X] = 0.01;
790 sp_canvas_item_request_update (item);
791 break;
792 case ARG_SPACINGY:
793 grid->spacing[NR::Y] = GTK_VALUE_DOUBLE (* arg);
794 if (grid->spacing[NR::Y] < 0.01) grid->spacing[NR::Y] = 0.01;
795 sp_canvas_item_request_update (item);
796 break;
797 case ARG_COLOR:
798 grid->color = GTK_VALUE_INT (* arg);
799 sp_canvas_item_request_update (item);
800 break;
801 case ARG_EMPCOLOR:
802 grid->empcolor = GTK_VALUE_INT (* arg);
803 sp_canvas_item_request_update (item);
804 break;
805 case ARG_EMPSPACING:
806 grid->empspacing = GTK_VALUE_INT (* arg);
807 // std::cout << "Emphasis Spacing: " << grid->empspacing << std::endl;
808 sp_canvas_item_request_update (item);
809 break;
810 default:
811 break;
812 }
813 }
815 static void
816 grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba)
817 {
818 if ((y >= buf->rect.y0) && (y < buf->rect.y1)) {
819 guint r, g, b, a;
820 gint x0, x1, x;
821 guchar *p;
822 r = NR_RGBA32_R (rgba);
823 g = NR_RGBA32_G (rgba);
824 b = NR_RGBA32_B (rgba);
825 a = NR_RGBA32_A (rgba);
826 x0 = MAX (buf->rect.x0, xs);
827 x1 = MIN (buf->rect.x1, xe + 1);
828 p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 3;
829 for (x = x0; x < x1; x++) {
830 p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);
831 p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);
832 p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);
833 p += 3;
834 }
835 }
836 }
838 static void
839 grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba)
840 {
841 if ((x >= buf->rect.x0) && (x < buf->rect.x1)) {
842 guint r, g, b, a;
843 gint y0, y1, y;
844 guchar *p;
845 r = NR_RGBA32_R(rgba);
846 g = NR_RGBA32_G (rgba);
847 b = NR_RGBA32_B (rgba);
848 a = NR_RGBA32_A (rgba);
849 y0 = MAX (buf->rect.y0, ys);
850 y1 = MIN (buf->rect.y1, ye + 1);
851 p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;
852 for (y = y0; y < y1; y++) {
853 p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);
854 p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);
855 p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);
856 p += buf->buf_rowstride;
857 }
858 }
859 }
861 /**
862 \brief This function renders the grid on a particular canvas buffer
863 \param item The grid to render on the buffer
864 \param buf The buffer to render the grid on
866 This function gets called a touch more than you might believe,
867 about once per tile. This means that it could probably be optimized
868 and help things out.
870 Basically this function has to determine where in the canvas it is,
871 and how that associates with the grid. It does this first by looking
872 at the bounding box of the buffer, and then calculates where the grid
873 starts in that buffer. It will then step through grid lines until
874 it is outside of the buffer.
876 For each grid line it is drawn using the function \c sp_grid_hline
877 or \c sp_grid_vline. These are convience functions for the sake
878 of making the function easier to read.
880 Also, there are emphisized lines on the grid. While the \c syg and
881 \c sxg variable track grid positioning, the \c xlinestart and \c
882 ylinestart variables track the 'count' of what lines they are. If
883 that count is a multiple of the line seperation between emphisis
884 lines, then that line is drawn in the emphisis color.
885 */
886 static void
887 cxygrid_render (SPCanvasItem * item, SPCanvasBuf * buf)
888 {
889 CXYGrid *grid = INKSCAPE_CXYGRID (item);
891 sp_canvas_prepare_buffer (buf);
893 const gdouble sxg = floor ((buf->rect.x0 - grid->ow[NR::X]) / grid->sw[NR::X]) * grid->sw[NR::X] + grid->ow[NR::X];
894 const gint xlinestart = (gint) Inkscape::round((sxg - grid->ow[NR::X]) / grid->sw[NR::X]);
895 const gdouble syg = floor ((buf->rect.y0 - grid->ow[NR::Y]) / grid->sw[NR::Y]) * grid->sw[NR::Y] + grid->ow[NR::Y];
896 const gint ylinestart = (gint) Inkscape::round((syg - grid->ow[NR::Y]) / grid->sw[NR::Y]);
898 gint ylinenum;
899 gdouble y;
900 for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += grid->sw[NR::Y], ylinenum++) {
901 const gint y0 = (gint) Inkscape::round(y);
903 if (!grid->scaled[NR::Y] && (ylinenum % grid->empspacing) == 0) {
904 grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, grid->empcolor);
905 } else {
906 grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, grid->color);
907 }
908 }
910 gint xlinenum;
911 gdouble x;
912 for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += grid->sw[NR::X], xlinenum++) {
913 const gint ix = (gint) Inkscape::round(x);
914 if (!grid->scaled[NR::X] && (xlinenum % grid->empspacing) == 0) {
915 grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, grid->empcolor);
916 } else {
917 grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, grid->color);
918 }
919 }
920 }
922 static void
923 cxygrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
924 {
925 CXYGrid *grid = INKSCAPE_CXYGRID (item);
927 if (parent_class->update)
928 (* parent_class->update) (item, affine, flags);
930 grid->ow = grid->origin * affine;
931 grid->sw = grid->spacing * affine;
932 grid->sw -= NR::Point(affine[4], affine[5]);
934 for(int dim = 0; dim < 2; dim++) {
935 gint scaling_factor = grid->empspacing;
937 if (scaling_factor <= 1)
938 scaling_factor = 5;
940 grid->scaled[dim] = FALSE;
941 grid->sw[dim] = fabs (grid->sw[dim]);
942 while (grid->sw[dim] < 8.0) {
943 grid->scaled[dim] = TRUE;
944 grid->sw[dim] *= scaling_factor;
945 /* First pass, go up to the major line spacing, then
946 keep increasing by two. */
947 scaling_factor = 2;
948 }
949 }
951 if (grid->empspacing == 0) {
952 grid->scaled[NR::Y] = TRUE;
953 grid->scaled[NR::X] = TRUE;
954 }
956 sp_canvas_request_redraw (item->canvas,
957 -1000000, -1000000,
958 1000000, 1000000);
960 item->x1 = item->y1 = -1000000;
961 item->x2 = item->y2 = 1000000;
962 }
965 }; /* namespace Inkscape */
967 /*
968 Local Variables:
969 mode:c++
970 c-file-style:"stroustrup"
971 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
972 indent-tabs-mode:nil
973 fill-column:99
974 End:
975 */
976 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :