Code

6644092dffb3298d2f6e034b016655652999aa7f
[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     snapenabled = true;
141     visible = true;
143     repr = in_repr;
144     if (repr) {
145         repr->addListener (&_repr_events, this);
146     }
148     namedview = nv;
149     canvasitems = NULL;
152 CanvasGrid::~CanvasGrid()
154     if (repr) {
155         repr->removeListenerByData (this);
156     }
158     while (canvasitems) {
159         gtk_object_destroy(GTK_OBJECT(canvasitems->data));
160         canvasitems = g_slist_remove(canvasitems, canvasitems->data);
161     }
164 /*
165 *  writes an <inkscape:grid> child to repr.
166 */
167 void
168 CanvasGrid::writeNewGridToRepr(Inkscape::XML::Node * repr, const char * gridtype)
170     if (!repr) return;
171     if (!gridtype) return;
173     // 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.
175     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(sp_desktop_document(SP_ACTIVE_DESKTOP));
176     Inkscape::XML::Node *newnode;
177     newnode = xml_doc->createElement("inkscape:grid");
178     newnode->setAttribute("type",gridtype);
180     repr->appendChild(newnode);
182     // FIXME: add this to history?
183 //    sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
184 //                     _("Create new element node"));
187 /*
188 * Creates a new CanvasGrid object of type gridtype
189 */
190 CanvasGrid*
191 CanvasGrid::NewGrid(SPNamedView * nv, Inkscape::XML::Node * in_repr, const char * gridtype)
193     if (!in_repr) return NULL;
194     if (!gridtype) return NULL;
196     if (!strcmp(gridtype,"xygrid")) {
197         return (CanvasGrid*) new CanvasXYGrid(nv, in_repr);
198     } else if (!strcmp(gridtype,"axonometric")) {
199         return (CanvasGrid*) new CanvasAxonomGrid(nv, in_repr);
200     }
202     return NULL;
206 /**
207 *  creates a new grid canvasitem for the SPDesktop given as parameter. Keeps a link to this canvasitem in the canvasitems list.
208 */
209 GridCanvasItem *
210 CanvasGrid::createCanvasItem(SPDesktop * desktop)
212     if (!desktop) return NULL;
213     //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.
215     // check if there is already a canvasitem on this desktop linking to this grid
216     for (GSList *l = canvasitems; l != NULL; l = l->next) {
217         if ( sp_desktop_gridgroup(desktop) == SP_CANVAS_GROUP(SP_CANVAS_ITEM(l->data)->parent) ) {
218             return NULL;
219         }
220     }
222     GridCanvasItem * item = INKSCAPE_GRID_CANVASITEM( sp_canvas_item_new(sp_desktop_gridgroup(desktop), INKSCAPE_TYPE_GRID_CANVASITEM, NULL) );
223     item->grid = this;
224     if (desktop->gridsEnabled()) {
225         sp_canvas_item_show(SP_CANVAS_ITEM(item));
226     } else {
227         sp_canvas_item_hide(SP_CANVAS_ITEM(item));
228     }
230     gtk_object_ref(GTK_OBJECT(item));    // since we're keeping a link to this item, we need to bump up the ref count
231     canvasitems = g_slist_prepend(canvasitems, item);
233     return item;
237 void
238 CanvasGrid::hide()
240     for (GSList *l = canvasitems; l != NULL; l = l->next) {
241         sp_canvas_item_hide ( SP_CANVAS_ITEM(l->data) );
242     }
243     visible = false;
244     disable_snapping(); // temporary hack, because at the moment visibilty and snapping are linked
247 void
248 CanvasGrid::show()
250     for (GSList *l = canvasitems; l != NULL; l = l->next) {
251         sp_canvas_item_show ( SP_CANVAS_ITEM(l->data) );
252     }
253     visible = true;
254     enable_snapping(); // temporary hack, because at the moment visibilty and snapping are linked
257 void
258 CanvasGrid::set_visibility(bool visible)
260     this->visible = visible;
261     if(visible) {
262         show();
263     } else {
264         hide();
265     }
268 void
269 CanvasGrid::toggle_visibility()
271     visible = !visible;
272     set_visibility(visible);
275 void
276 CanvasGrid::on_repr_attr_changed (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive, void * data)
278     if (!data)
279         return;
281     ((CanvasGrid*) data)->onReprAttrChanged(repr, key, oldval, newval, is_interactive);
286 // ##########################################################
287 //   CanvasXYGrid
289 static void grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba);
290 static void grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba);
293 /**
294 * A DIRECT COPY-PASTE FROM DOCUMENT-PROPERTIES.CPP  TO QUICKLY GET RESULTS
296  * Helper function that attachs widgets in a 3xn table. The widgets come in an
297  * array that has two entries per table row. The two entries code for four
298  * possible cases: (0,0) means insert space in first column; (0, non-0) means
299  * widget in columns 2-3; (non-0, 0) means label in columns 1-3; and
300  * (non-0, non-0) means two widgets in columns 2 and 3.
301 **/
302 #define SPACE_SIZE_X 15
303 #define SPACE_SIZE_Y 10
304 static inline void
305 attach_all (Gtk::Table &table, const Gtk::Widget *arr[], unsigned size, int start = 0)
307     for (unsigned i=0, r=start; i<size/sizeof(Gtk::Widget*); i+=2)
308     {
309         if (arr[i] && arr[i+1])
310         {
311             table.attach (const_cast<Gtk::Widget&>(*arr[i]),   1, 2, r, r+1,
312                       Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
313             table.attach (const_cast<Gtk::Widget&>(*arr[i+1]), 2, 3, r, r+1,
314                       Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
315         }
316         else
317         {
318             if (arr[i+1])
319                 table.attach (const_cast<Gtk::Widget&>(*arr[i+1]), 1, 3, r, r+1,
320                       Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
321             else if (arr[i])
322             {
323                 Gtk::Label& label = reinterpret_cast<Gtk::Label&> (const_cast<Gtk::Widget&>(*arr[i]));
324                 label.set_alignment (0.0);
325                 table.attach (label, 0, 3, r, r+1,
326                       Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
327             }
328             else
329             {
330                 Gtk::HBox *space = manage (new Gtk::HBox);
331                 space->set_size_request (SPACE_SIZE_X, SPACE_SIZE_Y);
332                 table.attach (*space, 0, 1, r, r+1,
333                       (Gtk::AttachOptions)0, (Gtk::AttachOptions)0,0,0);
334             }
335         }
336         ++r;
337     }
340 CanvasXYGrid::CanvasXYGrid (SPNamedView * nv, Inkscape::XML::Node * in_repr)
341     : CanvasGrid(nv, in_repr), table(1, 1)
343     origin[NR::X] = origin[NR::Y] = 0.0;
344 //            nv->gridcolor = (nv->gridcolor & 0xff) | (DEFAULTGRIDCOLOR & 0xffffff00);
345 //      case SP_ATTR_GRIDOPACITY:
346 //            nv->gridcolor = (nv->gridcolor & 0xffffff00) | (DEFAULTGRIDCOLOR & 0xff);
347     color = 0xff3f3f20;
348     empcolor = 0xFF3F3F40;
349     empspacing = 5;
350     spacing[NR::X] = spacing[NR::Y] = 8.0;
351     gridunit = &sp_unit_get_by_id(SP_UNIT_PX);
353     snapper = new CanvasXYGridSnapper(this, namedview, 0);
355     // initialize widgets:
356     vbox.set_border_width(2);
357     table.set_spacings(2);
358     vbox.pack_start(table, false, false, 0);
360     _rumg.init (_("Grid _units:"), "units", _wr, repr);
361     _rsu_ox.init (_("_Origin X:"), _("X coordinate of grid origin"),
362                   "originx", _rumg, _wr, repr);
363     _rsu_oy.init (_("O_rigin Y:"), _("Y coordinate of grid origin"),
364                   "originy", _rumg, _wr, repr);
365     _rsu_sx.init (_("Spacing _X:"), _("Distance between vertical grid lines"),
366                   "spacingx", _rumg, _wr, repr);
367     _rsu_sy.init (_("Spacing _Y:"), _("Distance between horizontal grid lines"),
368                   "spacingy", _rumg, _wr, repr);
369     _rcp_gcol.init (_("Grid line _color:"), _("Grid line color"),
370                     _("Color of grid lines"), "color", "opacity", _wr, repr);
371     _rcp_gmcol.init (_("Ma_jor grid line color:"), _("Major grid line color"),
372                      _("Color of the major (highlighted) grid lines"),
373                      "empcolor", "empopacity", _wr, repr);
374     _rsi.init (_("_Major grid line every:"), _("lines"), "empspacing", _wr, repr);
376     const Gtk::Widget* widget_array[] =
377     {
378         0,                  _rcbgrid._button,
379         _rumg._label,       _rumg._sel,
380         0,                  _rsu_ox.getSU(),
381         0,                  _rsu_oy.getSU(),
382         0,                  _rsu_sx.getSU(),
383         0,                  _rsu_sy.getSU(),
384         _rcp_gcol._label,   _rcp_gcol._cp,
385         0,                  0,
386         _rcp_gmcol._label,  _rcp_gmcol._cp,
387         _rsi._label,        &_rsi._hbox,
388     };
390     attach_all (table, widget_array, sizeof(widget_array));
392     vbox.show();
394     if (repr) readRepr();
395     updateWidgets();
398 CanvasXYGrid::~CanvasXYGrid ()
400    if (snapper) delete snapper;
404 /* fixme: Collect all these length parsing methods and think common sane API */
406 static gboolean sp_nv_read_length(const gchar *str, guint base, gdouble *val, const SPUnit **unit)
408     if (!str) {
409         return FALSE;
410     }
412     gchar *u;
413     gdouble v = g_ascii_strtod(str, &u);
414     if (!u) {
415         return FALSE;
416     }
417     while (isspace(*u)) {
418         u += 1;
419     }
421     if (!*u) {
422         /* No unit specified - keep default */
423         *val = v;
424         return TRUE;
425     }
427     if (base & SP_UNIT_DEVICE) {
428         if (u[0] && u[1] && !isalnum(u[2]) && !strncmp(u, "px", 2)) {
429             *unit = &sp_unit_get_by_id(SP_UNIT_PX);
430             *val = v;
431             return TRUE;
432         }
433     }
435     if (base & SP_UNIT_ABSOLUTE) {
436         if (!strncmp(u, "pt", 2)) {
437             *unit = &sp_unit_get_by_id(SP_UNIT_PT);
438         } else if (!strncmp(u, "mm", 2)) {
439             *unit = &sp_unit_get_by_id(SP_UNIT_MM);
440         } else if (!strncmp(u, "cm", 2)) {
441             *unit = &sp_unit_get_by_id(SP_UNIT_CM);
442         } else if (!strncmp(u, "m", 1)) {
443             *unit = &sp_unit_get_by_id(SP_UNIT_M);
444         } else if (!strncmp(u, "in", 2)) {
445             *unit = &sp_unit_get_by_id(SP_UNIT_IN);
446         } else {
447             return FALSE;
448         }
449         *val = v;
450         return TRUE;
451     }
453     return FALSE;
456 static gboolean sp_nv_read_opacity(const gchar *str, guint32 *color)
458     if (!str) {
459         return FALSE;
460     }
462     gchar *u;
463     gdouble v = g_ascii_strtod(str, &u);
464     if (!u) {
465         return FALSE;
466     }
467     v = CLAMP(v, 0.0, 1.0);
469     *color = (*color & 0xffffff00) | (guint32) floor(v * 255.9999);
471     return TRUE;
476 void
477 CanvasXYGrid::readRepr()
479     gchar const* value;
480     if ( (value = repr->attribute("originx")) ) {
481         sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[NR::X], &gridunit);
482         origin[NR::X] = sp_units_get_pixels(origin[NR::X], *(gridunit));
483     }
484     if ( (value = repr->attribute("originy")) ) {
485         sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[NR::Y], &gridunit);
486         origin[NR::Y] = sp_units_get_pixels(origin[NR::Y], *(gridunit));
487     }
489     if ( (value = repr->attribute("spacingx")) ) {
490         sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &spacing[NR::X], &gridunit);
491         spacing[NR::X] = sp_units_get_pixels(spacing[NR::X], *(gridunit));
492     }
493     if ( (value = repr->attribute("spacingy")) ) {
494         sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &spacing[NR::Y], &gridunit);
495         spacing[NR::Y] = sp_units_get_pixels(spacing[NR::Y], *(gridunit));
496     }
498     if ( (value = repr->attribute("color")) ) {
499         color = (color & 0xff) | sp_svg_read_color(value, color);
500     }
502     if ( (value = repr->attribute("empcolor")) ) {
503         empcolor = (empcolor & 0xff) | sp_svg_read_color(value, empcolor);
504     }
506     if ( (value = repr->attribute("opacity")) ) {
507         sp_nv_read_opacity(value, &color);
508     }
509     if ( (value = repr->attribute("empopacity")) ) {
510         sp_nv_read_opacity(value, &empcolor);
511     }
513     if ( (value = repr->attribute("empspacing")) ) {
514         empspacing = atoi(value);
515     }
517     for (GSList *l = canvasitems; l != NULL; l = l->next) {
518         sp_canvas_item_request_update ( SP_CANVAS_ITEM(l->data) );
519     }
521     return;
524 /**
525  * Called when XML node attribute changed; updates dialog widgets if change was not done by widgets themselves.
526  */
527 void
528 CanvasXYGrid::onReprAttrChanged (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive)
530     readRepr();
532     if ( ! (_wr.isUpdating()) )
533         updateWidgets();
539 Gtk::Widget &
540 CanvasXYGrid::getWidget()
542     return vbox;
546 /**
547  * Update dialog widgets from object's values.
548  */
549 void
550 CanvasXYGrid::updateWidgets()
552     if (_wr.isUpdating()) return;
554     _wr.setUpdating (true);
556 //    _rrb_gridtype.setValue (nv->gridtype);
557     _rumg.setUnit (gridunit);
559     gdouble val;
560     val = origin[NR::X];
561     val = sp_pixels_get_units (val, *(gridunit));
562     _rsu_ox.setValue (val);
563     val = origin[NR::Y];
564     val = sp_pixels_get_units (val, *(gridunit));
565     _rsu_oy.setValue (val);
566     val = spacing[NR::X];
567     double gridx = sp_pixels_get_units (val, *(gridunit));
568     _rsu_sx.setValue (gridx);
569     val = spacing[NR::Y];
570     double gridy = sp_pixels_get_units (val, *(gridunit));
571     _rsu_sy.setValue (gridy);
573     _rcp_gcol.setRgba32 (color);
574     _rcp_gmcol.setRgba32 (empcolor);
575     _rsi.setValue (empspacing);
577     _wr.setUpdating (false);
579     return;
584 void
585 CanvasXYGrid::Update (NR::Matrix const &affine, unsigned int flags)
587     ow = origin * affine;
588     sw = spacing * affine;
589     sw -= NR::Point(affine[4], affine[5]);
591     for(int dim = 0; dim < 2; dim++) {
592         gint scaling_factor = 5; //empspacing;
594         if (scaling_factor <= 1)
595             scaling_factor = 5;
597         scaled[dim] = FALSE;
598         sw[dim] = fabs (sw[dim]);
599         while (sw[dim] < 8.0) {
600             scaled[dim] = TRUE;
601             sw[dim] *= scaling_factor;
602             /* First pass, go up to the major line spacing, then
603                keep increasing by two. */
604             scaling_factor = 2;
605         }
606     }
609 void
610 CanvasXYGrid::Render (SPCanvasBuf *buf)
612     const gdouble sxg = floor ((buf->rect.x0 - ow[NR::X]) / sw[NR::X]) * sw[NR::X] + ow[NR::X];
613     const gint  xlinestart = (gint) Inkscape::round((sxg - ow[NR::X]) / sw[NR::X]);
614     const gdouble syg = floor ((buf->rect.y0 - ow[NR::Y]) / sw[NR::Y]) * sw[NR::Y] + ow[NR::Y];
615     const gint  ylinestart = (gint) Inkscape::round((syg - ow[NR::Y]) / sw[NR::Y]);
617     gint ylinenum;
618     gdouble y;
619     for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += sw[NR::Y], ylinenum++) {
620         const gint y0 = (gint) Inkscape::round(y);
622         if (!scaled[NR::Y] && (ylinenum % 5 /*empspacing*/) == 0) {
623             grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, empcolor);
624         } else {
625             grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, color);
626         }
627     }
629     gint xlinenum;
630     gdouble x;
631     for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += sw[NR::X], xlinenum++) {
632         const gint ix = (gint) Inkscape::round(x);
633         if (!scaled[NR::X] && (xlinenum % 5 /*empspacing*/) == 0) {
634             grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, empcolor);
635         } else {
636             grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, color);
637         }
638     }
652 /**
653  * \return x rounded to the nearest multiple of c1 plus c0.
654  *
655  * \note
656  * If c1==0 (and c0 is finite), then returns +/-inf.  This makes grid spacing of zero
657  * mean "ignore the grid in this dimention".  We're currently discussing "good" semantics
658  * for guide/grid snapping.
659  */
661 /* FIXME: move this somewhere else, perhaps */
662 static double round_to_nearest_multiple_plus(double x, double const c1, double const c0)
664     return floor((x - c0) / c1 + .5) * c1 + c0;
667 CanvasXYGridSnapper::CanvasXYGridSnapper(CanvasXYGrid *grid, SPNamedView const *nv, NR::Coord const d) : LineSnapper(nv, d)
669     this->grid = grid;
672 LineSnapper::LineList
673 CanvasXYGridSnapper::_getSnapLines(NR::Point const &p) const
675     LineList s;
677     if ( grid == NULL ) {
678         return s;
679     }
681     for (unsigned int i = 0; i < 2; ++i) {
683         /* This is to make sure we snap to only visible grid lines */
684         double scaled_spacing = grid->sw[i]; // this is spacing of visible lines if screen pixels
686         // convert screen pixels to px
687         // FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary
688         if (SP_ACTIVE_DESKTOP) {
689             scaled_spacing /= SP_ACTIVE_DESKTOP->current_zoom();
690         }
692         NR::Coord const rounded = round_to_nearest_multiple_plus(p[i],
693                                                                  scaled_spacing,
694                                                                  grid->origin[i]);
696         s.push_back(std::make_pair(NR::Dim2(i), rounded));
697     }
699     return s;
732 enum {
733     ARG_0,
734     ARG_ORIGINX,
735     ARG_ORIGINY,
736     ARG_SPACINGX,
737     ARG_SPACINGY,
738     ARG_COLOR,
739     ARG_EMPCOLOR,
740     ARG_EMPSPACING
741 };
744 static void cxygrid_class_init (CXYGridClass *klass);
745 static void cxygrid_init (CXYGrid *grid);
746 static void cxygrid_destroy (GtkObject *object);
747 static void cxygrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);
749 static void cxygrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
750 static void cxygrid_render (SPCanvasItem *item, SPCanvasBuf *buf);
752 //static SPCanvasItemClass * parent_class;
754 GtkType
755 cxygrid_get_type (void)
757     static GtkType cxygrid_type = 0;
759     if (!cxygrid_type) {
760         GtkTypeInfo cxygrid_info = {
761             "CXYGrid",
762             sizeof (CXYGrid),
763             sizeof (CXYGridClass),
764             (GtkClassInitFunc) cxygrid_class_init,
765             (GtkObjectInitFunc) cxygrid_init,
766             NULL, NULL,
767             (GtkClassInitFunc) NULL
768         };
769         cxygrid_type = gtk_type_unique (sp_canvas_item_get_type (), &cxygrid_info);
770     }
771     return cxygrid_type;
774 static void
775 cxygrid_class_init (CXYGridClass *klass)
777     GtkObjectClass *object_class;
778     SPCanvasItemClass *item_class;
780     object_class = (GtkObjectClass *) klass;
781     item_class = (SPCanvasItemClass *) klass;
783     parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
785     gtk_object_add_arg_type ("CXYGrid::originx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINX);
786     gtk_object_add_arg_type ("CXYGrid::originy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINY);
787     gtk_object_add_arg_type ("CXYGrid::spacingx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGX);
788     gtk_object_add_arg_type ("CXYGrid::spacingy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGY);
789     gtk_object_add_arg_type ("CXYGrid::color", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_COLOR);
790     gtk_object_add_arg_type ("CXYGrid::empcolor", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPCOLOR);
791     gtk_object_add_arg_type ("CXYGrid::empspacing", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPSPACING);
793     object_class->destroy = cxygrid_destroy;
794     object_class->set_arg = cxygrid_set_arg;
796     item_class->update = cxygrid_update;
797     item_class->render = cxygrid_render;
800 static void
801 cxygrid_init (CXYGrid *grid)
803     grid->origin[NR::X] = grid->origin[NR::Y] = 0.0;
804     grid->spacing[NR::X] = grid->spacing[NR::Y] = 8.0;
805     grid->color = 0x0000ff7f;
806     grid->empcolor = 0x3F3FFF40;
807     grid->empspacing = 5;
810 static void
811 cxygrid_destroy (GtkObject *object)
813     g_return_if_fail (object != NULL);
814     g_return_if_fail (INKSCAPE_IS_CXYGRID (object));
816     if (GTK_OBJECT_CLASS (parent_class)->destroy)
817         (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
820 static void
821 cxygrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
823     SPCanvasItem *item = SP_CANVAS_ITEM (object);
824     CXYGrid *grid = INKSCAPE_CXYGRID (object);
826     switch (arg_id) {
827     case ARG_ORIGINX:
828         grid->origin[NR::X] = GTK_VALUE_DOUBLE (* arg);
829         sp_canvas_item_request_update (item);
830         break;
831     case ARG_ORIGINY:
832         grid->origin[NR::Y] = GTK_VALUE_DOUBLE (* arg);
833         sp_canvas_item_request_update (item);
834         break;
835     case ARG_SPACINGX:
836         grid->spacing[NR::X] = GTK_VALUE_DOUBLE (* arg);
837         if (grid->spacing[NR::X] < 0.01) grid->spacing[NR::X] = 0.01;
838         sp_canvas_item_request_update (item);
839         break;
840     case ARG_SPACINGY:
841         grid->spacing[NR::Y] = GTK_VALUE_DOUBLE (* arg);
842         if (grid->spacing[NR::Y] < 0.01) grid->spacing[NR::Y] = 0.01;
843         sp_canvas_item_request_update (item);
844         break;
845     case ARG_COLOR:
846         grid->color = GTK_VALUE_INT (* arg);
847         sp_canvas_item_request_update (item);
848         break;
849     case ARG_EMPCOLOR:
850         grid->empcolor = GTK_VALUE_INT (* arg);
851         sp_canvas_item_request_update (item);
852         break;
853     case ARG_EMPSPACING:
854         grid->empspacing = GTK_VALUE_INT (* arg);
855         // std::cout << "Emphasis Spacing: " << grid->empspacing << std::endl;
856         sp_canvas_item_request_update (item);
857         break;
858     default:
859         break;
860     }
863 static void
864 grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba)
866     if ((y >= buf->rect.y0) && (y < buf->rect.y1)) {
867         guint r, g, b, a;
868         gint x0, x1, x;
869         guchar *p;
870         r = NR_RGBA32_R (rgba);
871         g = NR_RGBA32_G (rgba);
872         b = NR_RGBA32_B (rgba);
873         a = NR_RGBA32_A (rgba);
874         x0 = MAX (buf->rect.x0, xs);
875         x1 = MIN (buf->rect.x1, xe + 1);
876         p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 3;
877         for (x = x0; x < x1; x++) {
878             p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);
879             p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);
880             p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);
881             p += 3;
882         }
883     }
886 static void
887 grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba)
889     if ((x >= buf->rect.x0) && (x < buf->rect.x1)) {
890         guint r, g, b, a;
891         gint y0, y1, y;
892         guchar *p;
893         r = NR_RGBA32_R(rgba);
894         g = NR_RGBA32_G (rgba);
895         b = NR_RGBA32_B (rgba);
896         a = NR_RGBA32_A (rgba);
897         y0 = MAX (buf->rect.y0, ys);
898         y1 = MIN (buf->rect.y1, ye + 1);
899         p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;
900         for (y = y0; y < y1; y++) {
901             p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);
902             p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);
903             p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);
904             p += buf->buf_rowstride;
905         }
906     }
909 /**
910     \brief  This function renders the grid on a particular canvas buffer
911     \param  item  The grid to render on the buffer
912     \param  buf   The buffer to render the grid on
914     This function gets called a touch more than you might believe,
915     about once per tile.  This means that it could probably be optimized
916     and help things out.
918     Basically this function has to determine where in the canvas it is,
919     and how that associates with the grid.  It does this first by looking
920     at the bounding box of the buffer, and then calculates where the grid
921     starts in that buffer.  It will then step through grid lines until
922     it is outside of the buffer.
924     For each grid line it is drawn using the function \c sp_grid_hline
925     or \c sp_grid_vline.  These are convience functions for the sake
926     of making the function easier to read.
928     Also, there are emphisized lines on the grid.  While the \c syg and
929     \c sxg variable track grid positioning, the \c xlinestart and \c
930     ylinestart variables track the 'count' of what lines they are.  If
931     that count is a multiple of the line seperation between emphisis
932     lines, then that line is drawn in the emphisis color.
933 */
934 static void
935 cxygrid_render (SPCanvasItem * item, SPCanvasBuf * buf)
937     CXYGrid *grid = INKSCAPE_CXYGRID (item);
939     sp_canvas_prepare_buffer (buf);
941     const gdouble sxg = floor ((buf->rect.x0 - grid->ow[NR::X]) / grid->sw[NR::X]) * grid->sw[NR::X] + grid->ow[NR::X];
942     const gint  xlinestart = (gint) Inkscape::round((sxg - grid->ow[NR::X]) / grid->sw[NR::X]);
943     const gdouble syg = floor ((buf->rect.y0 - grid->ow[NR::Y]) / grid->sw[NR::Y]) * grid->sw[NR::Y] + grid->ow[NR::Y];
944     const gint  ylinestart = (gint) Inkscape::round((syg - grid->ow[NR::Y]) / grid->sw[NR::Y]);
946     gint ylinenum;
947     gdouble y;
948     for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += grid->sw[NR::Y], ylinenum++) {
949         const gint y0 = (gint) Inkscape::round(y);
951         if (!grid->scaled[NR::Y] && (ylinenum % grid->empspacing) == 0) {
952             grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, grid->empcolor);
953         } else {
954             grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, grid->color);
955         }
956     }
958     gint xlinenum;
959     gdouble x;
960     for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += grid->sw[NR::X], xlinenum++) {
961         const gint ix = (gint) Inkscape::round(x);
962         if (!grid->scaled[NR::X] && (xlinenum % grid->empspacing) == 0) {
963             grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, grid->empcolor);
964         } else {
965             grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, grid->color);
966         }
967     }
970 static void
971 cxygrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
973     CXYGrid *grid = INKSCAPE_CXYGRID (item);
975     if (parent_class->update)
976         (* parent_class->update) (item, affine, flags);
978     grid->ow = grid->origin * affine;
979     grid->sw = grid->spacing * affine;
980     grid->sw -= NR::Point(affine[4], affine[5]);
982     for(int dim = 0; dim < 2; dim++) {
983         gint scaling_factor = grid->empspacing;
985         if (scaling_factor <= 1)
986             scaling_factor = 5;
988         grid->scaled[dim] = FALSE;
989         grid->sw[dim] = fabs (grid->sw[dim]);
990         while (grid->sw[dim] < 8.0) {
991             grid->scaled[dim] = TRUE;
992             grid->sw[dim] *= scaling_factor;
993             /* First pass, go up to the major line spacing, then
994                keep increasing by two. */
995             scaling_factor = 2;
996         }
997     }
999     if (grid->empspacing == 0) {
1000         grid->scaled[NR::Y] = TRUE;
1001         grid->scaled[NR::X] = TRUE;
1002     }
1004     sp_canvas_request_redraw (item->canvas,
1005                      -1000000, -1000000,
1006                      1000000, 1000000);
1008     item->x1 = item->y1 = -1000000;
1009     item->x2 = item->y2 = 1000000;
1013 }; /* namespace Inkscape */
1015 /*
1016   Local Variables:
1017   mode:c++
1018   c-file-style:"stroustrup"
1019   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1020   indent-tabs-mode:nil
1021   fill-column:99
1022   End:
1023 */
1024 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :