Code

643ffccdc1aac7b9ab80b77e785caec59386283a
[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(SPDesktop *desktop, Inkscape::XML::Node * in_repr)
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     }
155     
156     namedview = sp_desktop_namedview(desktop);
159 CanvasGrid::~CanvasGrid()
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");
172 /*
173 *  writes an <inkscape:grid> child to repr. 
174 */
175 void 
176 CanvasGrid::writeNewGridToRepr(Inkscape::XML::Node * repr, const char * gridtype)
178     if (!repr) return;
179     if (!gridtype) return;
180     
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.
182     
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);
187     
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"));
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)
201     if (!desktop) return NULL;
202     if (!in_repr) return NULL;
203     if (!gridtype) return NULL;
204     
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     }
210     
211     return NULL;
215 void
216 CanvasGrid::hide()
218     sp_canvas_item_hide(canvasitem);
219     visible = false;
222 void
223 CanvasGrid::show()
225     sp_canvas_item_show(canvasitem);
226     visible = true;
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 (SPDesktop *desktop, Inkscape::XML::Node * in_repr)
295     : CanvasGrid(desktop, 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);
306     
307     snapper = new CanvasXYGridSnapper(this, namedview, 0);
308     
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     sp_canvas_item_request_update (canvasitem);
473     return;
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)
482     readRepr();
484     if ( ! (_wr.isUpdating()) )
485         updateWidgets();
491 Gtk::Widget &
492 CanvasXYGrid::getWidget()
494     return vbox;
498 /**
499  * Update dialog widgets from object's values.
500  */
501 void
502 CanvasXYGrid::updateWidgets()
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);
530     
531     return;
536 void
537 CanvasXYGrid::Update (NR::Matrix const &affine, unsigned int flags)
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     }
561 void
562 CanvasXYGrid::Render (SPCanvasBuf *buf)
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     }
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)
616     return floor((x - c0) / c1 + .5) * c1 + c0;
619 CanvasXYGridSnapper::CanvasXYGridSnapper(CanvasXYGrid *grid, SPNamedView const *nv, NR::Coord const d) : LineSnapper(nv, d)
621     this->grid = grid;
624 LineSnapper::LineList 
625 CanvasXYGridSnapper::_getSnapLines(NR::Point const &p) const
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;
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)
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;
726 static void
727 cxygrid_class_init (CXYGridClass *klass)
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;
752 static void
753 cxygrid_init (CXYGrid *grid)
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;
762 static void
763 cxygrid_destroy (GtkObject *object)
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);
772 static void
773 cxygrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
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     }
815 static void
816 grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba)
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     }
838 static void
839 grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba)
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     }
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)
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     }
922 static void
923 cxygrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
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;
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 :