Code

New Grids are now ready to go. bug testing can start i think.
[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 "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);
105 static void
106 grid_canvasitem_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
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     }
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(SPNamedView * nv, Inkscape::XML::Node * in_repr)
140     repr = in_repr;
141     if (repr) {
142         repr->addListener (&_repr_events, this);
143     }
145     namedview = nv;
146     canvasitems = NULL;
149 CanvasGrid::~CanvasGrid()
151     if (repr) {
152         repr->removeListenerByData (this);
153     }
155     while (canvasitems) {
156         gtk_object_destroy(GTK_OBJECT(canvasitems->data));
157         canvasitems = g_slist_remove(canvasitems, canvasitems->data);
158     }
161 /*
162 *  writes an <inkscape:grid> child to repr.
163 */
164 void
165 CanvasGrid::writeNewGridToRepr(Inkscape::XML::Node * repr, const char * gridtype)
167     if (!repr) return;
168     if (!gridtype) return;
170     // 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.
172     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(sp_desktop_document(SP_ACTIVE_DESKTOP));
173     Inkscape::XML::Node *newnode;
174     newnode = xml_doc->createElement("inkscape:grid");
175     newnode->setAttribute("type",gridtype);
177     repr->appendChild(newnode);
179     // FIXME: add this to history?
180 //    sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
181 //                     _("Create new element node"));
184 /*
185 * Creates a new CanvasGrid object of type gridtype
186 */
187 CanvasGrid*
188 CanvasGrid::NewGrid(SPNamedView * nv, Inkscape::XML::Node * in_repr, const char * gridtype)
190     if (!in_repr) return NULL;
191     if (!gridtype) return NULL;
193     if (!strcmp(gridtype,"xygrid")) {
194         return (CanvasGrid*) new CanvasXYGrid(nv, in_repr);
195     } else if (!strcmp(gridtype,"axonometric")) {
196         return (CanvasGrid*) new CanvasAxonomGrid(nv, in_repr);
197     }
199     return NULL;
203 /**
204 *  creates a new grid canvasitem for the SPDesktop given as parameter. Keeps a link to this canvasitem in the canvasitems list.
205 */
206 GridCanvasItem *
207 CanvasGrid::createCanvasItem(SPDesktop * desktop)
209     if (!desktop) return NULL;
210     //Johan: I think for multiple desktops it is best if each has their own canvasitem, but share the same CanvasGrid object; that is what this function is for.
212     // check if there is already a canvasitem on this desktop linking to this grid
213     for (GSList *l = canvasitems; l != NULL; l = l->next) {
214         if ( sp_desktop_gridgroup(desktop) == SP_CANVAS_GROUP(SP_CANVAS_ITEM(l->data)->parent) ) {
215             return NULL;
216         }
217     }
219     GridCanvasItem * item = INKSCAPE_GRID_CANVASITEM( sp_canvas_item_new(sp_desktop_gridgroup(desktop), INKSCAPE_TYPE_GRID_CANVASITEM, NULL) );
220     item->grid = this;
221     sp_canvas_item_show(SP_CANVAS_ITEM(item));
223     gtk_object_ref(GTK_OBJECT(item));    // since we're keeping a link to this item, we need to bump up the ref count
224     canvasitems = g_slist_prepend(canvasitems, item);
226     return item;
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)
232     if (!data)
233         return;
235     ((CanvasGrid*) data)->onReprAttrChanged(repr, key, oldval, newval, is_interactive);
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
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)
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     }
294 CanvasXYGrid::CanvasXYGrid (SPNamedView * nv, Inkscape::XML::Node * in_repr)
295     : CanvasGrid(nv, in_repr), table(1, 1)
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();
352 CanvasXYGrid::~CanvasXYGrid ()
354    if (snapper) delete snapper;
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)
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;
410 static gboolean sp_nv_read_opacity(const gchar *str, guint32 *color)
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;
430 void
431 CanvasXYGrid::readRepr()
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     for (GSList *l = canvasitems; l != NULL; l = l->next) {
472         sp_canvas_item_request_update ( SP_CANVAS_ITEM(l->data) );
473     }
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);
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 :