1 #define SP_CANVAS_GRID_C
3 /*
4 * SPCGrid
5 *
6 * Copyright (C) Lauris Kaplinski 2000
7 *
8 */
11 #include "sp-canvas-util.h"
12 #include "canvas-grid.h"
13 #include "display-forward.h"
14 #include <libnr/nr-pixops.h>
16 enum {
17 ARG_0,
18 ARG_ORIGINX,
19 ARG_ORIGINY,
20 ARG_SPACINGX,
21 ARG_SPACINGY,
22 ARG_COLOR,
23 ARG_EMPCOLOR,
24 ARG_EMPSPACING
25 };
28 static void sp_cgrid_class_init (SPCGridClass *klass);
29 static void sp_cgrid_init (SPCGrid *grid);
30 static void sp_cgrid_destroy (GtkObject *object);
31 static void sp_cgrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);
33 static void sp_cgrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
34 static void sp_cgrid_render (SPCanvasItem *item, SPCanvasBuf *buf);
36 static SPCanvasItemClass * parent_class;
38 GtkType
39 sp_cgrid_get_type (void)
40 {
41 static GtkType cgrid_type = 0;
43 if (!cgrid_type) {
44 GtkTypeInfo cgrid_info = {
45 "SPCGrid",
46 sizeof (SPCGrid),
47 sizeof (SPCGridClass),
48 (GtkClassInitFunc) sp_cgrid_class_init,
49 (GtkObjectInitFunc) sp_cgrid_init,
50 NULL, NULL,
51 (GtkClassInitFunc) NULL
52 };
53 cgrid_type = gtk_type_unique (sp_canvas_item_get_type (), &cgrid_info);
54 }
55 return cgrid_type;
56 }
58 static void
59 sp_cgrid_class_init (SPCGridClass *klass)
60 {
61 GtkObjectClass *object_class;
62 SPCanvasItemClass *item_class;
64 object_class = (GtkObjectClass *) klass;
65 item_class = (SPCanvasItemClass *) klass;
67 parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
69 gtk_object_add_arg_type ("SPCGrid::originx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINX);
70 gtk_object_add_arg_type ("SPCGrid::originy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINY);
71 gtk_object_add_arg_type ("SPCGrid::spacingx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGX);
72 gtk_object_add_arg_type ("SPCGrid::spacingy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGY);
73 gtk_object_add_arg_type ("SPCGrid::color", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_COLOR);
74 gtk_object_add_arg_type ("SPCGrid::empcolor", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPCOLOR);
75 gtk_object_add_arg_type ("SPCGrid::empspacing", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPSPACING);
77 object_class->destroy = sp_cgrid_destroy;
78 object_class->set_arg = sp_cgrid_set_arg;
80 item_class->update = sp_cgrid_update;
81 item_class->render = sp_cgrid_render;
82 }
84 static void
85 sp_cgrid_init (SPCGrid *grid)
86 {
87 grid->origin[NR::X] = grid->origin[NR::Y] = 0.0;
88 grid->spacing[NR::X] = grid->spacing[NR::Y] = 8.0;
89 grid->color = 0x0000ff7f;
90 grid->empcolor = 0x3F3FFF40;
91 grid->empspacing = 5;
92 }
94 static void
95 sp_cgrid_destroy (GtkObject *object)
96 {
97 g_return_if_fail (object != NULL);
98 g_return_if_fail (SP_IS_CGRID (object));
100 if (GTK_OBJECT_CLASS (parent_class)->destroy)
101 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
102 }
104 static void
105 sp_cgrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
106 {
107 SPCanvasItem *item = SP_CANVAS_ITEM (object);
108 SPCGrid *grid = SP_CGRID (object);
110 switch (arg_id) {
111 case ARG_ORIGINX:
112 grid->origin[NR::X] = GTK_VALUE_DOUBLE (* arg);
113 sp_canvas_item_request_update (item);
114 break;
115 case ARG_ORIGINY:
116 grid->origin[NR::Y] = GTK_VALUE_DOUBLE (* arg);
117 sp_canvas_item_request_update (item);
118 break;
119 case ARG_SPACINGX:
120 grid->spacing[NR::X] = GTK_VALUE_DOUBLE (* arg);
121 if (grid->spacing[NR::X] < 0.01) grid->spacing[NR::X] = 0.01;
122 sp_canvas_item_request_update (item);
123 break;
124 case ARG_SPACINGY:
125 grid->spacing[NR::Y] = GTK_VALUE_DOUBLE (* arg);
126 if (grid->spacing[NR::Y] < 0.01) grid->spacing[NR::Y] = 0.01;
127 sp_canvas_item_request_update (item);
128 break;
129 case ARG_COLOR:
130 grid->color = GTK_VALUE_INT (* arg);
131 sp_canvas_item_request_update (item);
132 break;
133 case ARG_EMPCOLOR:
134 grid->empcolor = GTK_VALUE_INT (* arg);
135 sp_canvas_item_request_update (item);
136 break;
137 case ARG_EMPSPACING:
138 grid->empspacing = GTK_VALUE_INT (* arg);
139 // std::cout << "Emphasis Spacing: " << grid->empspacing << std::endl;
140 sp_canvas_item_request_update (item);
141 break;
142 default:
143 break;
144 }
145 }
147 static void
148 sp_grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba)
149 {
150 if ((y >= buf->rect.y0) && (y < buf->rect.y1)) {
151 guint r, g, b, a;
152 gint x0, x1, x;
153 guchar *p;
154 r = NR_RGBA32_R (rgba);
155 g = NR_RGBA32_G (rgba);
156 b = NR_RGBA32_B (rgba);
157 a = NR_RGBA32_A (rgba);
158 x0 = MAX (buf->rect.x0, xs);
159 x1 = MIN (buf->rect.x1, xe + 1);
160 p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 3;
161 for (x = x0; x < x1; x++) {
162 p[0] = NR_COMPOSEN11 (r, a, p[0]);
163 p[1] = NR_COMPOSEN11 (g, a, p[1]);
164 p[2] = NR_COMPOSEN11 (b, a, p[2]);
165 p += 3;
166 }
167 }
168 }
170 static void
171 sp_grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba)
172 {
173 if ((x >= buf->rect.x0) && (x < buf->rect.x1)) {
174 guint r, g, b, a;
175 gint y0, y1, y;
176 guchar *p;
177 r = NR_RGBA32_R(rgba);
178 g = NR_RGBA32_G (rgba);
179 b = NR_RGBA32_B (rgba);
180 a = NR_RGBA32_A (rgba);
181 y0 = MAX (buf->rect.y0, ys);
182 y1 = MIN (buf->rect.y1, ye + 1);
183 p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;
184 for (y = y0; y < y1; y++) {
185 p[0] = NR_COMPOSEN11 (r, a, p[0]);
186 p[1] = NR_COMPOSEN11 (g, a, p[1]);
187 p[2] = NR_COMPOSEN11 (b, a, p[2]);
188 p += buf->buf_rowstride;
189 }
190 }
191 }
193 /**
194 \brief This function renders the grid on a particular canvas buffer
195 \param item The grid to render on the buffer
196 \param buf The buffer to render the grid on
198 This function gets called a touch more than you might believe,
199 about once per tile. This means that it could probably be optimized
200 and help things out.
202 Basically this function has to determine where in the canvas it is,
203 and how that associates with the grid. It does this first by looking
204 at the bounding box of the buffer, and then calculates where the grid
205 starts in that buffer. It will then step through grid lines until
206 it is outside of the buffer.
208 For each grid line it is drawn using the function \c sp_grid_hline
209 or \c sp_grid_vline. These are convience functions for the sake
210 of making the function easier to read.
212 Also, there are emphisized lines on the grid. While the \c syg and
213 \c sxg variable track grid positioning, the \c xlinestart and \c
214 ylinestart variables track the 'count' of what lines they are. If
215 that count is a multiple of the line seperation between emphisis
216 lines, then that line is drawn in the emphisis color.
217 */
218 static void
219 sp_cgrid_render (SPCanvasItem * item, SPCanvasBuf * buf)
220 {
221 SPCGrid *grid = SP_CGRID (item);
223 sp_canvas_prepare_buffer (buf);
225 const gdouble sxg = floor ((buf->rect.x0 - grid->ow[NR::X]) / grid->sw[NR::X]) * grid->sw[NR::X] + grid->ow[NR::X];
226 const gint xlinestart = (gint) Inkscape::round((sxg - grid->ow[NR::X]) / grid->sw[NR::X]);
227 const gdouble syg = floor ((buf->rect.y0 - grid->ow[NR::Y]) / grid->sw[NR::Y]) * grid->sw[NR::Y] + grid->ow[NR::Y];
228 const gint ylinestart = (gint) Inkscape::round((syg - grid->ow[NR::Y]) / grid->sw[NR::Y]);
230 gint ylinenum;
231 gdouble y;
232 for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += grid->sw[NR::Y], ylinenum++) {
233 const gint y0 = (gint) Inkscape::round(y);
234 const gint y1 = (gint) Inkscape::round(y + grid->sw[NR::Y]);
236 if (!grid->scaled[NR::Y] &&
237 (ylinenum % grid->empspacing) == 0) {
238 sp_grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, grid->empcolor);
239 } else {
240 sp_grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, grid->color);
241 }
243 gint xlinenum;
244 gdouble x;
245 for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += grid->sw[NR::X], xlinenum++) {
246 const gint ix = (gint) Inkscape::round(x);
247 if (!grid->scaled[NR::X] &&
248 (xlinenum % grid->empspacing) == 0) {
249 sp_grid_vline (buf, ix, y0 + 1, y1 - 1, grid->empcolor);
250 } else {
251 sp_grid_vline (buf, ix, y0 + 1, y1 - 1, grid->color);
252 }
253 }
254 }
255 }
257 static void
258 sp_cgrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
259 {
260 SPCGrid *grid = SP_CGRID (item);
262 if (parent_class->update)
263 (* parent_class->update) (item, affine, flags);
265 grid->ow = grid->origin * affine;
266 grid->sw = grid->spacing * affine;
267 grid->sw -= NR::Point(affine[4], affine[5]);
269 for(int dim = 0; dim < 2; dim++) {
270 gint scaling_factor = grid->empspacing;
272 if (scaling_factor <= 1)
273 scaling_factor = 5;
275 grid->scaled[dim] = FALSE;
276 grid->sw[dim] = fabs (grid->sw[dim]);
277 while (grid->sw[dim] < 8.0) {
278 grid->scaled[dim] = TRUE;
279 grid->sw[dim] *= scaling_factor;
280 /* First pass, go up to the major line spacing, then
281 keep increasing by two. */
282 scaling_factor = 2;
283 }
284 }
286 if (grid->empspacing == 0) {
287 grid->scaled[NR::Y] = TRUE;
288 grid->scaled[NR::X] = TRUE;
289 }
291 sp_canvas_request_redraw (item->canvas,
292 -1000000, -1000000,
293 1000000, 1000000);
295 item->x1 = item->y1 = -1000000;
296 item->x2 = item->y2 = 1000000;
297 }
299 /*
300 Local Variables:
301 mode:c++
302 c-file-style:"stroustrup"
303 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
304 indent-tabs-mode:nil
305 fill-column:99
306 End:
307 */
308 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :