Code

Monster commit. New grid infrastructure. The old gridmanagement code is still there...
[inkscape.git] / src / display / canvas-grid.cpp
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 "canvas-grid.h"
13 #include "display-forward.h"
14 #include <libnr/nr-pixops.h>
15 #include "desktop-handles.h"
16 #include "helper/units.h"
17 #include "svg/svg-color.h"
18 #include "xml/node-event-vector.h"
19 #include "sp-object.h"
21 #include "sp-namedview.h"
22 #include "inkscape.h"
23 #include "desktop.h"
24 #include "display/canvas-grid.h"
25 #include "display/canvas-axonomgrid.h"
26 #include "../document.h"
30 namespace Inkscape {
32 static void grid_canvasitem_class_init (GridCanvasItemClass *klass);
33 static void grid_canvasitem_init (GridCanvasItem *grid);
34 static void grid_canvasitem_destroy (GtkObject *object);
36 static void grid_canvasitem_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
37 static void grid_canvasitem_render (SPCanvasItem *item, SPCanvasBuf *buf);
39 static SPCanvasItemClass * parent_class;
41 GtkType
42 grid_canvasitem_get_type (void)
43 {
44     static GtkType grid_canvasitem_type = 0;
46     if (!grid_canvasitem_type) {
47         GtkTypeInfo grid_canvasitem_info = {
48             "GridCanvasItem",
49             sizeof (GridCanvasItem),
50             sizeof (GridCanvasItemClass),
51             (GtkClassInitFunc) grid_canvasitem_class_init,
52             (GtkObjectInitFunc) grid_canvasitem_init,
53             NULL, NULL,
54             (GtkClassInitFunc) NULL
55         };
56         grid_canvasitem_type = gtk_type_unique (sp_canvas_item_get_type (), &grid_canvasitem_info);
57     }
58     return grid_canvasitem_type;
59 }
61 static void
62 grid_canvasitem_class_init (GridCanvasItemClass *klass)
63 {
64     GtkObjectClass *object_class;
65     SPCanvasItemClass *item_class;
67     object_class = (GtkObjectClass *) klass;
68     item_class = (SPCanvasItemClass *) klass;
70     parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
72     object_class->destroy = grid_canvasitem_destroy;
74     item_class->update = grid_canvasitem_update;
75     item_class->render = grid_canvasitem_render;
76 }
78 static void
79 grid_canvasitem_init (GridCanvasItem *griditem)
80 {
81     griditem->grid = NULL;
82 }
84 static void
85 grid_canvasitem_destroy (GtkObject *object)
86 {
87     g_return_if_fail (object != NULL);
88     g_return_if_fail (INKSCAPE_IS_GRID_CANVASITEM (object));
90     if (GTK_OBJECT_CLASS (parent_class)->destroy)
91         (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
92 }
94 /**
95 */
96 static void
97 grid_canvasitem_render (SPCanvasItem * item, SPCanvasBuf * buf)
98 {
99     GridCanvasItem *gridcanvasitem = INKSCAPE_GRID_CANVASITEM (item);
101     sp_canvas_prepare_buffer (buf);
103     if (gridcanvasitem->grid) gridcanvasitem->grid->Render(buf);
106 static void
107 grid_canvasitem_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
109     GridCanvasItem *gridcanvasitem = INKSCAPE_GRID_CANVASITEM (item);
111     if (parent_class->update)
112         (* parent_class->update) (item, affine, flags);
114     if (gridcanvasitem->grid) {
115         gridcanvasitem->grid->Update(affine, flags);
117         sp_canvas_request_redraw (item->canvas,
118                          -1000000, -1000000,
119                          1000000, 1000000);
121         item->x1 = item->y1 = -1000000;
122         item->x2 = item->y2 = 1000000;
123     }
128 // ##########################################################
129 //   CanvasGrid
131     static Inkscape::XML::NodeEventVector const _repr_events = {
132         NULL, /* child_added */
133         NULL, /* child_removed */
134         CanvasGrid::on_repr_attr_changed,
135         NULL, /* content_changed */
136         NULL  /* order_changed */
137     };
139 CanvasGrid::CanvasGrid(SPDesktop *desktop, Inkscape::XML::Node * in_repr)
141     //create canvasitem
142     // 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.
143     canvasitem = INKSCAPE_GRID_CANVASITEM( sp_canvas_item_new(sp_desktop_grid(desktop), INKSCAPE_TYPE_GRID_CANVASITEM, NULL) );
144     gtk_object_ref(GTK_OBJECT(canvasitem));    // since we're keeping a copy, we need to bump up the ref count
145     canvasitem->grid = this;
147     enabled = false;
148     visible = false;
150 //    sp_canvas_item_hide(canvasitem);
152     repr = in_repr;
153     if (repr) {
154         repr->addListener (&_repr_events, this);
155     }
156     
157     namedview = sp_desktop_namedview(desktop);
160 CanvasGrid::~CanvasGrid()
162     if (repr) {
163         repr->removeListenerByData (this);
164     }
166     sp_canvas_item_hide(canvasitem);
167    // deref canvasitem
168    gtk_object_unref(GTK_OBJECT(canvasitem));
169    g_free(canvasitem);
170    g_message("~CanvasGrid");
173 /*
174 *  writes an <inkscape:grid> child to repr. 
175 */
176 void 
177 CanvasGrid::writeNewGridToRepr(Inkscape::XML::Node * repr, const char * gridtype)
179     if (!repr) return;
180     if (!gridtype) return;
181     
182     // 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     
184     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(sp_desktop_document(SP_ACTIVE_DESKTOP));
185     Inkscape::XML::Node *newnode;
186     newnode = xml_doc->createElement("inkscape:grid");
187     if (!strcmp(gridtype,"xygrid")) {
188         newnode->setAttribute("type","xygrid");
189     }
190     
191     repr->appendChild(newnode);
193     // FIXME: add this to history?
194 //    sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
195 //                     _("Create new element node"));
198 /*
199 * Creates a new CanvasGrid object of type gridtype 
200 */
201 CanvasGrid* 
202 CanvasGrid::NewGrid(SPDesktop *desktop, Inkscape::XML::Node * in_repr, const char * gridtype)
204     if (!desktop) return NULL;
205     if (!in_repr) return NULL;
206     if (!gridtype) return NULL;
207     
208     if (!strcmp(gridtype,"xygrid")) {
209         return (CanvasGrid*) new CanvasXYGrid(desktop, in_repr);
210     }
211     
212     return NULL;
216 void
217 CanvasGrid::hide()
219     sp_canvas_item_hide(canvasitem);
220     visible = false;
223 void
224 CanvasGrid::show()
226     sp_canvas_item_show(canvasitem);
227     visible = true;
230 void
231 CanvasGrid::on_repr_attr_changed (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive, void * data)
233     if (!data)
234         return;
236     ((CanvasGrid*) data)->onReprAttrChanged(repr, key, oldval, newval, is_interactive);
241 // ##########################################################
242 //   CanvasXYGrid
244 static void grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba);
245 static void grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba);
248 /**
249 * A DIRECT COPY-PASTE FROM DOCUMENT-PROPERTIES.CPP  TO QUICKLY GET RESULTS
251  * Helper function that attachs widgets in a 3xn table. The widgets come in an
252  * array that has two entries per table row. The two entries code for four
253  * possible cases: (0,0) means insert space in first column; (0, non-0) means
254  * widget in columns 2-3; (non-0, 0) means label in columns 1-3; and
255  * (non-0, non-0) means two widgets in columns 2 and 3.
256 **/
257 #define SPACE_SIZE_X 15
258 #define SPACE_SIZE_Y 10
259 static inline void
260 attach_all (Gtk::Table &table, const Gtk::Widget *arr[], unsigned size, int start = 0)
262     for (unsigned i=0, r=start; i<size/sizeof(Gtk::Widget*); i+=2)
263     {
264         if (arr[i] && arr[i+1])
265         {
266             table.attach (const_cast<Gtk::Widget&>(*arr[i]),   1, 2, r, r+1,
267                       Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
268             table.attach (const_cast<Gtk::Widget&>(*arr[i+1]), 2, 3, r, r+1,
269                       Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
270         }
271         else
272         {
273             if (arr[i+1])
274                 table.attach (const_cast<Gtk::Widget&>(*arr[i+1]), 1, 3, r, r+1,
275                       Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
276             else if (arr[i])
277             {
278                 Gtk::Label& label = reinterpret_cast<Gtk::Label&> (const_cast<Gtk::Widget&>(*arr[i]));
279                 label.set_alignment (0.0);
280                 table.attach (label, 0, 3, r, r+1,
281                       Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
282             }
283             else
284             {
285                 Gtk::HBox *space = manage (new Gtk::HBox);
286                 space->set_size_request (SPACE_SIZE_X, SPACE_SIZE_Y);
287                 table.attach (*space, 0, 1, r, r+1,
288                       (Gtk::AttachOptions)0, (Gtk::AttachOptions)0,0,0);
289             }
290         }
291         ++r;
292     }
295 CanvasXYGrid::CanvasXYGrid (SPDesktop *desktop, Inkscape::XML::Node * in_repr)
296     : CanvasGrid(desktop, in_repr), table(1, 1)
298     origin[NR::X] = origin[NR::Y] = 0.0;
299 //            nv->gridcolor = (nv->gridcolor & 0xff) | (DEFAULTGRIDCOLOR & 0xffffff00);
300 //      case SP_ATTR_GRIDOPACITY:
301 //            nv->gridcolor = (nv->gridcolor & 0xffffff00) | (DEFAULTGRIDCOLOR & 0xff);
302     color = 0xff3f3f20;
303     empcolor = 0xFF3F3F40;
304     empspacing = 5;
305     spacing[NR::X] = spacing[NR::Y] = 8.0;
306     gridunit = &sp_unit_get_by_id(SP_UNIT_PX);
307     
308     snapper = new CanvasXYGridSnapper(this, namedview, 0);
309     
310     // initialize widgets:
311     vbox.set_border_width(2);
312     table.set_spacings(2);
313     vbox.pack_start(table, false, false, 0);
315     _rumg.init (_("Grid _units:"), "units", _wr, repr);
316     _rsu_ox.init (_("_Origin X:"), _("X coordinate of grid origin"),
317                   "originx", _rumg, _wr, repr);
318     _rsu_oy.init (_("O_rigin Y:"), _("Y coordinate of grid origin"),
319                   "originy", _rumg, _wr, repr);
320     _rsu_sx.init (_("Spacing _X:"), _("Distance between vertical grid lines"),
321                   "spacingx", _rumg, _wr, repr);
322     _rsu_sy.init (_("Spacing _Y:"), _("Distance between horizontal grid lines"),
323                   "spacingy", _rumg, _wr, repr);
324     _rcp_gcol.init (_("Grid line _color:"), _("Grid line color"),
325                     _("Color of grid lines"), "color", "opacity", _wr, repr);
326     _rcp_gmcol.init (_("Ma_jor grid line color:"), _("Major grid line color"),
327                      _("Color of the major (highlighted) grid lines"),
328                      "empcolor", "empopacity", _wr, repr);
329     _rsi.init (_("_Major grid line every:"), _("lines"), "empspacing", _wr, repr);
331     const Gtk::Widget* widget_array[] =
332     {
333         0,                  _rcbgrid._button,
334         0,                  _rrb_gridtype._hbox,
335         _rumg._label,       _rumg._sel,
336         0,                  _rsu_ox.getSU(),
337         0,                  _rsu_oy.getSU(),
338         0,                  _rsu_sx.getSU(),
339         0,                  _rsu_sy.getSU(),
340         _rcp_gcol._label,   _rcp_gcol._cp,
341         0,                  0,
342         _rcp_gmcol._label,  _rcp_gmcol._cp,
343         _rsi._label,        &_rsi._hbox,
344     };
346     attach_all (table, widget_array, sizeof(widget_array));
348     vbox.show();
350     if (repr) readRepr();
351     updateWidgets();
354 CanvasXYGrid::~CanvasXYGrid ()
356    if (snapper) delete snapper;
360 /* fixme: Collect all these length parsing methods and think common sane API */
362 static gboolean sp_nv_read_length(const gchar *str, guint base, gdouble *val, const SPUnit **unit)
364     if (!str) {
365         return FALSE;
366     }
368     gchar *u;
369     gdouble v = g_ascii_strtod(str, &u);
370     if (!u) {
371         return FALSE;
372     }
373     while (isspace(*u)) {
374         u += 1;
375     }
377     if (!*u) {
378         /* No unit specified - keep default */
379         *val = v;
380         return TRUE;
381     }
383     if (base & SP_UNIT_DEVICE) {
384         if (u[0] && u[1] && !isalnum(u[2]) && !strncmp(u, "px", 2)) {
385             *unit = &sp_unit_get_by_id(SP_UNIT_PX);
386             *val = v;
387             return TRUE;
388         }
389     }
391     if (base & SP_UNIT_ABSOLUTE) {
392         if (!strncmp(u, "pt", 2)) {
393             *unit = &sp_unit_get_by_id(SP_UNIT_PT);
394         } else if (!strncmp(u, "mm", 2)) {
395             *unit = &sp_unit_get_by_id(SP_UNIT_MM);
396         } else if (!strncmp(u, "cm", 2)) {
397             *unit = &sp_unit_get_by_id(SP_UNIT_CM);
398         } else if (!strncmp(u, "m", 1)) {
399             *unit = &sp_unit_get_by_id(SP_UNIT_M);
400         } else if (!strncmp(u, "in", 2)) {
401             *unit = &sp_unit_get_by_id(SP_UNIT_IN);
402         } else {
403             return FALSE;
404         }
405         *val = v;
406         return TRUE;
407     }
409     return FALSE;
412 static gboolean sp_nv_read_opacity(const gchar *str, guint32 *color)
414     if (!str) {
415         return FALSE;
416     }
418     gchar *u;
419     gdouble v = g_ascii_strtod(str, &u);
420     if (!u) {
421         return FALSE;
422     }
423     v = CLAMP(v, 0.0, 1.0);
425     *color = (*color & 0xffffff00) | (guint32) floor(v * 255.9999);
427     return TRUE;
432 void
433 CanvasXYGrid::readRepr()
435     gchar const* value;
436     if ( (value = repr->attribute("originx")) ) {
437         sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[NR::X], &gridunit);
438         origin[NR::X] = sp_units_get_pixels(origin[NR::X], *(gridunit));
439     }
440     if ( (value = repr->attribute("originy")) ) {
441         sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[NR::Y], &gridunit);
442         origin[NR::Y] = sp_units_get_pixels(origin[NR::Y], *(gridunit));
443     }
445     if ( (value = repr->attribute("spacingx")) ) {
446         sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &spacing[NR::X], &gridunit);
447         spacing[NR::X] = sp_units_get_pixels(spacing[NR::X], *(gridunit));
448     }
449     if ( (value = repr->attribute("spacingy")) ) {
450         sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &spacing[NR::Y], &gridunit);
451         spacing[NR::Y] = sp_units_get_pixels(spacing[NR::Y], *(gridunit));
452     }
454     if ( (value = repr->attribute("color")) ) {
455         color = (color & 0xff) | sp_svg_read_color(value, color);
456     }
458     if ( (value = repr->attribute("empcolor")) ) {
459         empcolor = (empcolor & 0xff) | sp_svg_read_color(value, empcolor);
460     }
462     if ( (value = repr->attribute("opacity")) ) {
463         sp_nv_read_opacity(value, &color);
464     }
465     if ( (value = repr->attribute("empopacity")) ) {
466         sp_nv_read_opacity(value, &empcolor);
467     }
469     if ( (value = repr->attribute("empspacing")) ) {
470         empspacing = atoi(value);
471     }
473     sp_canvas_item_request_update (canvasitem);
475     return;
478 /**
479  * Called when XML node attribute changed; updates dialog widgets if change was not done by widgets themselves.
480  */
481 void
482 CanvasXYGrid::onReprAttrChanged (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive)
484     readRepr();
486     if ( ! (_wr.isUpdating()) )
487         updateWidgets();
493 Gtk::Widget &
494 CanvasXYGrid::getWidget()
496     return vbox;
500 /**
501  * Update dialog widgets from object's values.
502  */
503 void
504 CanvasXYGrid::updateWidgets()
506     if (_wr.isUpdating()) return;
508     _wr.setUpdating (true);
510 //    _rrb_gridtype.setValue (nv->gridtype);
511     _rumg.setUnit (gridunit);
513     gdouble val;
514     val = origin[NR::X];
515     val = sp_pixels_get_units (val, *(gridunit));
516     _rsu_ox.setValue (val);
517     val = origin[NR::Y];
518     val = sp_pixels_get_units (val, *(gridunit));
519     _rsu_oy.setValue (val);
520     val = spacing[NR::X];
521     double gridx = sp_pixels_get_units (val, *(gridunit));
522     _rsu_sx.setValue (gridx);
523     val = spacing[NR::Y];
524     double gridy = sp_pixels_get_units (val, *(gridunit));
525     _rsu_sy.setValue (gridy);
527     _rcp_gcol.setRgba32 (color);
528     _rcp_gmcol.setRgba32 (empcolor);
529     _rsi.setValue (empspacing);
531     _wr.setUpdating (false);
532     
533     return;
538 void
539 CanvasXYGrid::Update (NR::Matrix const &affine, unsigned int flags)
541     ow = origin * affine;
542     sw = spacing * affine;
543     sw -= NR::Point(affine[4], affine[5]);
545     for(int dim = 0; dim < 2; dim++) {
546         gint scaling_factor = 5; //empspacing;
548         if (scaling_factor <= 1)
549             scaling_factor = 5;
551         scaled[dim] = FALSE;
552         sw[dim] = fabs (sw[dim]);
553         while (sw[dim] < 8.0) {
554             scaled[dim] = TRUE;
555             sw[dim] *= scaling_factor;
556             /* First pass, go up to the major line spacing, then
557                keep increasing by two. */
558             scaling_factor = 2;
559         }
560     }
563 void
564 CanvasXYGrid::Render (SPCanvasBuf *buf)
566     const gdouble sxg = floor ((buf->rect.x0 - ow[NR::X]) / sw[NR::X]) * sw[NR::X] + ow[NR::X];
567     const gint  xlinestart = (gint) Inkscape::round((sxg - ow[NR::X]) / sw[NR::X]);
568     const gdouble syg = floor ((buf->rect.y0 - ow[NR::Y]) / sw[NR::Y]) * sw[NR::Y] + ow[NR::Y];
569     const gint  ylinestart = (gint) Inkscape::round((syg - ow[NR::Y]) / sw[NR::Y]);
571     gint ylinenum;
572     gdouble y;
573     for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += sw[NR::Y], ylinenum++) {
574         const gint y0 = (gint) Inkscape::round(y);
576         if (!scaled[NR::Y] && (ylinenum % 5 /*empspacing*/) == 0) {
577             grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, empcolor);
578         } else {
579             grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, color);
580         }
581     }
583     gint xlinenum;
584     gdouble x;
585     for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += sw[NR::X], xlinenum++) {
586         const gint ix = (gint) Inkscape::round(x);
587         if (!scaled[NR::X] && (xlinenum % 5 /*empspacing*/) == 0) {
588             grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, empcolor);
589         } else {
590             grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, color);
591         }
592     }
606 /**
607  * \return x rounded to the nearest multiple of c1 plus c0.
608  *
609  * \note
610  * If c1==0 (and c0 is finite), then returns +/-inf.  This makes grid spacing of zero
611  * mean "ignore the grid in this dimention".  We're currently discussing "good" semantics
612  * for guide/grid snapping.
613  */
615 /* FIXME: move this somewhere else, perhaps */
616 static double round_to_nearest_multiple_plus(double x, double const c1, double const c0)
618     return floor((x - c0) / c1 + .5) * c1 + c0;
621 CanvasXYGridSnapper::CanvasXYGridSnapper(CanvasXYGrid *grid, SPNamedView const *nv, NR::Coord const d) : LineSnapper(nv, d)
623     this->grid = grid;
626 LineSnapper::LineList 
627 CanvasXYGridSnapper::_getSnapLines(NR::Point const &p) const
629     LineList s;
631     if ( grid == NULL ) {
632         return s;
633     }
635     for (unsigned int i = 0; i < 2; ++i) {
637         /* This is to make sure we snap to only visible grid lines */
638         double scaled_spacing = grid->sw[i]; // this is spacing of visible lines if screen pixels
640         // convert screen pixels to px
641         // FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary
642         if (SP_ACTIVE_DESKTOP) {
643             scaled_spacing /= SP_ACTIVE_DESKTOP->current_zoom();
644         }
646         NR::Coord const rounded = round_to_nearest_multiple_plus(p[i],
647                                                                  scaled_spacing,
648                                                                  grid->origin[i]);
650         s.push_back(std::make_pair(NR::Dim2(i), rounded));
651     }
653     return s;
686 enum {
687     ARG_0,
688     ARG_ORIGINX,
689     ARG_ORIGINY,
690     ARG_SPACINGX,
691     ARG_SPACINGY,
692     ARG_COLOR,
693     ARG_EMPCOLOR,
694     ARG_EMPSPACING
695 };
698 static void cxygrid_class_init (CXYGridClass *klass);
699 static void cxygrid_init (CXYGrid *grid);
700 static void cxygrid_destroy (GtkObject *object);
701 static void cxygrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);
703 static void cxygrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
704 static void cxygrid_render (SPCanvasItem *item, SPCanvasBuf *buf);
706 //static SPCanvasItemClass * parent_class;
708 GtkType
709 cxygrid_get_type (void)
711     static GtkType cxygrid_type = 0;
713     if (!cxygrid_type) {
714         GtkTypeInfo cxygrid_info = {
715             "CXYGrid",
716             sizeof (CXYGrid),
717             sizeof (CXYGridClass),
718             (GtkClassInitFunc) cxygrid_class_init,
719             (GtkObjectInitFunc) cxygrid_init,
720             NULL, NULL,
721             (GtkClassInitFunc) NULL
722         };
723         cxygrid_type = gtk_type_unique (sp_canvas_item_get_type (), &cxygrid_info);
724     }
725     return cxygrid_type;
728 static void
729 cxygrid_class_init (CXYGridClass *klass)
731     GtkObjectClass *object_class;
732     SPCanvasItemClass *item_class;
734     object_class = (GtkObjectClass *) klass;
735     item_class = (SPCanvasItemClass *) klass;
737     parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
739     gtk_object_add_arg_type ("CXYGrid::originx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINX);
740     gtk_object_add_arg_type ("CXYGrid::originy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINY);
741     gtk_object_add_arg_type ("CXYGrid::spacingx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGX);
742     gtk_object_add_arg_type ("CXYGrid::spacingy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGY);
743     gtk_object_add_arg_type ("CXYGrid::color", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_COLOR);
744     gtk_object_add_arg_type ("CXYGrid::empcolor", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPCOLOR);
745     gtk_object_add_arg_type ("CXYGrid::empspacing", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPSPACING);
747     object_class->destroy = cxygrid_destroy;
748     object_class->set_arg = cxygrid_set_arg;
750     item_class->update = cxygrid_update;
751     item_class->render = cxygrid_render;
754 static void
755 cxygrid_init (CXYGrid *grid)
757     grid->origin[NR::X] = grid->origin[NR::Y] = 0.0;
758     grid->spacing[NR::X] = grid->spacing[NR::Y] = 8.0;
759     grid->color = 0x0000ff7f;
760     grid->empcolor = 0x3F3FFF40;
761     grid->empspacing = 5;
764 static void
765 cxygrid_destroy (GtkObject *object)
767     g_return_if_fail (object != NULL);
768     g_return_if_fail (INKSCAPE_IS_CXYGRID (object));
770     if (GTK_OBJECT_CLASS (parent_class)->destroy)
771         (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
774 static void
775 cxygrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
777     SPCanvasItem *item = SP_CANVAS_ITEM (object);
778     CXYGrid *grid = INKSCAPE_CXYGRID (object);
780     switch (arg_id) {
781     case ARG_ORIGINX:
782         grid->origin[NR::X] = GTK_VALUE_DOUBLE (* arg);
783         sp_canvas_item_request_update (item);
784         break;
785     case ARG_ORIGINY:
786         grid->origin[NR::Y] = GTK_VALUE_DOUBLE (* arg);
787         sp_canvas_item_request_update (item);
788         break;
789     case ARG_SPACINGX:
790         grid->spacing[NR::X] = GTK_VALUE_DOUBLE (* arg);
791         if (grid->spacing[NR::X] < 0.01) grid->spacing[NR::X] = 0.01;
792         sp_canvas_item_request_update (item);
793         break;
794     case ARG_SPACINGY:
795         grid->spacing[NR::Y] = GTK_VALUE_DOUBLE (* arg);
796         if (grid->spacing[NR::Y] < 0.01) grid->spacing[NR::Y] = 0.01;
797         sp_canvas_item_request_update (item);
798         break;
799     case ARG_COLOR:
800         grid->color = GTK_VALUE_INT (* arg);
801         sp_canvas_item_request_update (item);
802         break;
803     case ARG_EMPCOLOR:
804         grid->empcolor = GTK_VALUE_INT (* arg);
805         sp_canvas_item_request_update (item);
806         break;
807     case ARG_EMPSPACING:
808         grid->empspacing = GTK_VALUE_INT (* arg);
809         // std::cout << "Emphasis Spacing: " << grid->empspacing << std::endl;
810         sp_canvas_item_request_update (item);
811         break;
812     default:
813         break;
814     }
817 static void
818 grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba)
820     if ((y >= buf->rect.y0) && (y < buf->rect.y1)) {
821         guint r, g, b, a;
822         gint x0, x1, x;
823         guchar *p;
824         r = NR_RGBA32_R (rgba);
825         g = NR_RGBA32_G (rgba);
826         b = NR_RGBA32_B (rgba);
827         a = NR_RGBA32_A (rgba);
828         x0 = MAX (buf->rect.x0, xs);
829         x1 = MIN (buf->rect.x1, xe + 1);
830         p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 3;
831         for (x = x0; x < x1; x++) {
832             p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);
833             p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);
834             p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);
835             p += 3;
836         }
837     }
840 static void
841 grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba)
843     if ((x >= buf->rect.x0) && (x < buf->rect.x1)) {
844         guint r, g, b, a;
845         gint y0, y1, y;
846         guchar *p;
847         r = NR_RGBA32_R(rgba);
848         g = NR_RGBA32_G (rgba);
849         b = NR_RGBA32_B (rgba);
850         a = NR_RGBA32_A (rgba);
851         y0 = MAX (buf->rect.y0, ys);
852         y1 = MIN (buf->rect.y1, ye + 1);
853         p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;
854         for (y = y0; y < y1; y++) {
855             p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);
856             p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);
857             p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);
858             p += buf->buf_rowstride;
859         }
860     }
863 /**
864     \brief  This function renders the grid on a particular canvas buffer
865     \param  item  The grid to render on the buffer
866     \param  buf   The buffer to render the grid on
868     This function gets called a touch more than you might believe,
869     about once per tile.  This means that it could probably be optimized
870     and help things out.
872     Basically this function has to determine where in the canvas it is,
873     and how that associates with the grid.  It does this first by looking
874     at the bounding box of the buffer, and then calculates where the grid
875     starts in that buffer.  It will then step through grid lines until
876     it is outside of the buffer.
878     For each grid line it is drawn using the function \c sp_grid_hline
879     or \c sp_grid_vline.  These are convience functions for the sake
880     of making the function easier to read.
882     Also, there are emphisized lines on the grid.  While the \c syg and
883     \c sxg variable track grid positioning, the \c xlinestart and \c
884     ylinestart variables track the 'count' of what lines they are.  If
885     that count is a multiple of the line seperation between emphisis
886     lines, then that line is drawn in the emphisis color.
887 */
888 static void
889 cxygrid_render (SPCanvasItem * item, SPCanvasBuf * buf)
891     CXYGrid *grid = INKSCAPE_CXYGRID (item);
893     sp_canvas_prepare_buffer (buf);
895     const gdouble sxg = floor ((buf->rect.x0 - grid->ow[NR::X]) / grid->sw[NR::X]) * grid->sw[NR::X] + grid->ow[NR::X];
896     const gint  xlinestart = (gint) Inkscape::round((sxg - grid->ow[NR::X]) / grid->sw[NR::X]);
897     const gdouble syg = floor ((buf->rect.y0 - grid->ow[NR::Y]) / grid->sw[NR::Y]) * grid->sw[NR::Y] + grid->ow[NR::Y];
898     const gint  ylinestart = (gint) Inkscape::round((syg - grid->ow[NR::Y]) / grid->sw[NR::Y]);
900     gint ylinenum;
901     gdouble y;
902     for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += grid->sw[NR::Y], ylinenum++) {
903         const gint y0 = (gint) Inkscape::round(y);
905         if (!grid->scaled[NR::Y] && (ylinenum % grid->empspacing) == 0) {
906             grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, grid->empcolor);
907         } else {
908             grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, grid->color);
909         }
910     }
912     gint xlinenum;
913     gdouble x;
914     for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += grid->sw[NR::X], xlinenum++) {
915         const gint ix = (gint) Inkscape::round(x);
916         if (!grid->scaled[NR::X] && (xlinenum % grid->empspacing) == 0) {
917             grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, grid->empcolor);
918         } else {
919             grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, grid->color);
920         }
921     }
924 static void
925 cxygrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
927     CXYGrid *grid = INKSCAPE_CXYGRID (item);
929     if (parent_class->update)
930         (* parent_class->update) (item, affine, flags);
932     grid->ow = grid->origin * affine;
933     grid->sw = grid->spacing * affine;
934     grid->sw -= NR::Point(affine[4], affine[5]);
936     for(int dim = 0; dim < 2; dim++) {
937         gint scaling_factor = grid->empspacing;
939         if (scaling_factor <= 1)
940             scaling_factor = 5;
942         grid->scaled[dim] = FALSE;
943         grid->sw[dim] = fabs (grid->sw[dim]);
944         while (grid->sw[dim] < 8.0) {
945             grid->scaled[dim] = TRUE;
946             grid->sw[dim] *= scaling_factor;
947             /* First pass, go up to the major line spacing, then
948                keep increasing by two. */
949             scaling_factor = 2;
950         }
951     }
953     if (grid->empspacing == 0) {
954         grid->scaled[NR::Y] = TRUE;
955         grid->scaled[NR::X] = TRUE;
956     }
958     sp_canvas_request_redraw (item->canvas,
959                      -1000000, -1000000,
960                      1000000, 1000000);
962     item->x1 = item->y1 = -1000000;
963     item->x2 = item->y2 = 1000000;
967 }; /* namespace Inkscape */
969 /*
970   Local Variables:
971   mode:c++
972   c-file-style:"stroustrup"
973   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
974   indent-tabs-mode:nil
975   fill-column:99
976   End:
977 */
978 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :