1 #define SP_CANVAS_AXONOMGRID_C\r
2 \r
3 /*\r
4 * SPCAxonomGrid\r
5 *\r
6 * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>\r
7 * Copyright (C) 2000 Lauris Kaplinski\r
8 *\r
9 */ \r
10 \r
11 /* \r
12 * Current limits are: one axis (y-axis) is always vertical. The other two\r
13 * axes are bound to a certain range of angles. The z-axis always has an angle \r
14 * smaller than 90 degrees (measured from horizontal, 0 degrees being a line extending\r
15 * to the right). The x-axis will always have an angle between 0 and 90 degrees.\r
16 * When I quickly think about it: all possibilities are probably covered this way. Eg.\r
17 * a z-axis with negative angle can be replaced with an x-axis, etc.\r
18 */ \r
19 \r
20 /*\r
21 * TODO: LOTS LOTS LOTS. Optimization etc.\r
22 *\r
23 */\r
24 \r
25 #include "sp-canvas-util.h"\r
26 #include "canvas-axonomgrid.h"\r
27 #include "display-forward.h"\r
28 #include <libnr/nr-pixops.h>\r
29 \r
30 #define SAFE_SETPIXEL //undefine this when it is certain that setpixel is never called with invalid params\r
31 \r
32 enum {\r
33 ARG_0,\r
34 ARG_ORIGINX,\r
35 ARG_ORIGINY,\r
36 ARG_ANGLEX,\r
37 ARG_SPACINGY,\r
38 ARG_ANGLEZ,\r
39 ARG_COLOR,\r
40 ARG_EMPCOLOR,\r
41 ARG_EMPSPACING\r
42 };\r
43 \r
44 enum Dim3 { X=0, Y, Z };\r
45 \r
46 #ifndef M_PI\r
47 #define M_PI 3.14159265358979323846\r
48 #endif\r
49 \r
50 static double deg_to_rad(double deg) { return deg*M_PI/180.0;}\r
51 \r
52 \r
53 static void sp_caxonomgrid_class_init (SPCAxonomGridClass *klass);\r
54 static void sp_caxonomgrid_init (SPCAxonomGrid *grid);\r
55 static void sp_caxonomgrid_destroy (GtkObject *object);\r
56 static void sp_caxonomgrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);\r
57 \r
58 static void sp_caxonomgrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);\r
59 static void sp_caxonomgrid_render (SPCanvasItem *item, SPCanvasBuf *buf);\r
60 \r
61 static SPCanvasItemClass * parent_class;\r
62 \r
63 GtkType\r
64 sp_caxonomgrid_get_type (void)\r
65 {\r
66 static GtkType caxonomgrid_type = 0;\r
67 \r
68 if (!caxonomgrid_type) {\r
69 GtkTypeInfo caxonomgrid_info = {\r
70 "SPCAxonomGrid",\r
71 sizeof (SPCAxonomGrid),\r
72 sizeof (SPCAxonomGridClass),\r
73 (GtkClassInitFunc) sp_caxonomgrid_class_init,\r
74 (GtkObjectInitFunc) sp_caxonomgrid_init,\r
75 NULL, NULL,\r
76 (GtkClassInitFunc) NULL\r
77 };\r
78 caxonomgrid_type = gtk_type_unique (sp_canvas_item_get_type (), &caxonomgrid_info);\r
79 }\r
80 return caxonomgrid_type;\r
81 }\r
82 \r
83 static void\r
84 sp_caxonomgrid_class_init (SPCAxonomGridClass *klass)\r
85 {\r
86 \r
87 GtkObjectClass *object_class;\r
88 SPCanvasItemClass *item_class;\r
89 \r
90 object_class = (GtkObjectClass *) klass;\r
91 item_class = (SPCanvasItemClass *) klass;\r
92 \r
93 parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());\r
94 \r
95 gtk_object_add_arg_type ("SPCAxonomGrid::originx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINX);\r
96 gtk_object_add_arg_type ("SPCAxonomGrid::originy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINY);\r
97 gtk_object_add_arg_type ("SPCAxonomGrid::anglex", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ANGLEX);\r
98 gtk_object_add_arg_type ("SPCAxonomGrid::spacingy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGY);\r
99 gtk_object_add_arg_type ("SPCAxonomGrid::anglez", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ANGLEZ);\r
100 gtk_object_add_arg_type ("SPCAxonomGrid::color", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_COLOR);\r
101 gtk_object_add_arg_type ("SPCAxonomGrid::empcolor", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPCOLOR);\r
102 gtk_object_add_arg_type ("SPCAxonomGrid::empspacing", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPSPACING);\r
103 \r
104 object_class->destroy = sp_caxonomgrid_destroy;\r
105 object_class->set_arg = sp_caxonomgrid_set_arg;\r
106 \r
107 item_class->update = sp_caxonomgrid_update;\r
108 item_class->render = sp_caxonomgrid_render;\r
109 \r
110 }\r
111 \r
112 static void\r
113 sp_caxonomgrid_init (SPCAxonomGrid *grid)\r
114 {\r
115 grid->origin[NR::X] = grid->origin[NR::Y] = 0.0;\r
116 // grid->spacing[X] = grid->spacing[Y] = grid->spacing[Z] = 8.0;\r
117 grid->color = 0x0000ff7f;\r
118 grid->empcolor = 0x3F3FFF40;\r
119 grid->empspacing = 5;\r
120 }\r
121 \r
122 static void\r
123 sp_caxonomgrid_destroy (GtkObject *object)\r
124 {\r
125 g_return_if_fail (object != NULL);\r
126 g_return_if_fail (SP_IS_CAXONOMGRID (object));\r
127 \r
128 if (GTK_OBJECT_CLASS (parent_class)->destroy)\r
129 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);\r
130 }\r
131 \r
132 static void\r
133 sp_caxonomgrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)\r
134 {\r
135 SPCanvasItem *item = SP_CANVAS_ITEM (object);\r
136 SPCAxonomGrid *grid = SP_CAXONOMGRID (object);\r
137 \r
138 switch (arg_id) {\r
139 case ARG_ORIGINX:\r
140 grid->origin[NR::X] = GTK_VALUE_DOUBLE (* arg);\r
141 sp_canvas_item_request_update (item);\r
142 break;\r
143 case ARG_ORIGINY:\r
144 grid->origin[NR::Y] = GTK_VALUE_DOUBLE (* arg);\r
145 sp_canvas_item_request_update (item);\r
146 break;\r
147 case ARG_ANGLEX:\r
148 grid->angle_deg[X] = GTK_VALUE_DOUBLE (* arg);\r
149 if (grid->angle_deg[X] < 0.0) grid->angle_deg[X] = 0.0;\r
150 if (grid->angle_deg[X] > 89.0) grid->angle_deg[X] = 89.0;\r
151 grid->angle_rad[X] = deg_to_rad(grid->angle_deg[X]);\r
152 grid->tan_angle[X] = tan(grid->angle_rad[X]);\r
153 sp_canvas_item_request_update (item);\r
154 break;\r
155 case ARG_SPACINGY:\r
156 grid->lengthy = GTK_VALUE_DOUBLE (* arg);\r
157 if (grid->lengthy < 0.01) grid->lengthy = 0.01;\r
158 sp_canvas_item_request_update (item);\r
159 break;\r
160 case ARG_ANGLEZ:\r
161 grid->angle_deg[Z] = GTK_VALUE_DOUBLE (* arg);\r
162 if (grid->angle_deg[Z] < 0.0) grid->angle_deg[Z] = 0.0;\r
163 if (grid->angle_deg[X] > 89.0) grid->angle_deg[X] = 89.0;\r
164 grid->angle_rad[Z] = deg_to_rad(grid->angle_deg[Z]);\r
165 grid->tan_angle[Z] = tan(grid->angle_rad[Z]);\r
166 sp_canvas_item_request_update (item);\r
167 break;\r
168 case ARG_COLOR:\r
169 grid->color = GTK_VALUE_INT (* arg);\r
170 sp_canvas_item_request_update (item);\r
171 break;\r
172 case ARG_EMPCOLOR:\r
173 grid->empcolor = GTK_VALUE_INT (* arg);\r
174 sp_canvas_item_request_update (item);\r
175 break;\r
176 case ARG_EMPSPACING:\r
177 grid->empspacing = GTK_VALUE_INT (* arg);\r
178 // std::cout << "Emphasis Spacing: " << grid->empspacing << std::endl;\r
179 sp_canvas_item_request_update (item);\r
180 break;\r
181 default:\r
182 break;\r
183 }\r
184 }\r
185 \r
186 \r
187 \r
188 /**\r
189 \brief This function renders a pixel on a particular buffer.\r
190 \r
191 The topleft of the buffer equals\r
192 ( rect.x0 , rect.y0 ) in screen coordinates\r
193 ( 0 , 0 ) in setpixel coordinates\r
194 The bottomright of the buffer equals\r
195 ( rect.x1 , rect,y1 ) in screen coordinates\r
196 ( rect.x1 - rect.x0 , rect.y1 - rect.y0 ) in setpixel coordinates\r
197 */\r
198 static void \r
199 sp_caxonomgrid_setpixel (SPCanvasBuf *buf, gint x, gint y, guint32 rgba) {\r
200 #ifdef SAFE_SETPIXEL\r
201 if ( (x >= buf->rect.x0) && (x < buf->rect.x1) && (y >= buf->rect.y0) && (y < buf->rect.y1) ) {\r
202 #endif \r
203 guint r, g, b, a; \r
204 r = NR_RGBA32_R (rgba);\r
205 g = NR_RGBA32_G (rgba);\r
206 b = NR_RGBA32_B (rgba);\r
207 a = NR_RGBA32_A (rgba); \r
208 guchar * p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;\r
209 p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);\r
210 p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);\r
211 p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);\r
212 #ifdef SAFE_SETPIXEL\r
213 }\r
214 #endif \r
215 }\r
216 \r
217 /**\r
218 \brief This function renders a line on a particular canvas buffer,\r
219 using Bresenham's line drawing function.\r
220 http://www.cs.unc.edu/~mcmillan/comp136/Lecture6/Lines.html \r
221 Coordinates are interpreted as SCREENcoordinates\r
222 */\r
223 static void \r
224 sp_caxonomgrid_drawline (SPCanvasBuf *buf, gint x0, gint y0, gint x1, gint y1, guint32 rgba) {\r
225 int dy = y1 - y0;\r
226 int dx = x1 - x0;\r
227 int stepx, stepy;\r
228 \r
229 if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; }\r
230 if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; }\r
231 dy <<= 1; // dy is now 2*dy\r
232 dx <<= 1; // dx is now 2*dx\r
233 \r
234 sp_caxonomgrid_setpixel(buf, x0, y0, rgba);\r
235 if (dx > dy) {\r
236 int fraction = dy - (dx >> 1); // same as 2*dy - dx\r
237 while (x0 != x1) {\r
238 if (fraction >= 0) {\r
239 y0 += stepy;\r
240 fraction -= dx; // same as fraction -= 2*dx\r
241 }\r
242 x0 += stepx;\r
243 fraction += dy; // same as fraction -= 2*dy\r
244 sp_caxonomgrid_setpixel(buf, x0, y0, rgba);\r
245 }\r
246 } else {\r
247 int fraction = dx - (dy >> 1);\r
248 while (y0 != y1) {\r
249 if (fraction >= 0) {\r
250 x0 += stepx;\r
251 fraction -= dy;\r
252 }\r
253 y0 += stepy;\r
254 fraction += dx;\r
255 sp_caxonomgrid_setpixel(buf, x0, y0, rgba);\r
256 }\r
257 }\r
258 \r
259 }\r
260 \r
261 static void\r
262 sp_grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba)\r
263 {\r
264 if ((x >= buf->rect.x0) && (x < buf->rect.x1)) {\r
265 guint r, g, b, a;\r
266 gint y0, y1, y;\r
267 guchar *p;\r
268 r = NR_RGBA32_R(rgba);\r
269 g = NR_RGBA32_G (rgba);\r
270 b = NR_RGBA32_B (rgba);\r
271 a = NR_RGBA32_A (rgba);\r
272 y0 = MAX (buf->rect.y0, ys);\r
273 y1 = MIN (buf->rect.y1, ye + 1);\r
274 p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;\r
275 for (y = y0; y < y1; y++) {\r
276 p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);\r
277 p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);\r
278 p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);\r
279 p += buf->buf_rowstride;\r
280 }\r
281 }\r
282 }\r
283 \r
284 /**\r
285 \brief This function renders the grid on a particular canvas buffer\r
286 \param item The grid to render on the buffer\r
287 \param buf The buffer to render the grid on\r
288 \r
289 This function gets called a touch more than you might believe,\r
290 about once per tile. This means that it could probably be optimized\r
291 and help things out.\r
292 \r
293 Basically this function has to determine where in the canvas it is,\r
294 and how that associates with the grid. It does this first by looking\r
295 at the bounding box of the buffer, and then calculates where the grid\r
296 starts in that buffer. It will then step through grid lines until\r
297 it is outside of the buffer.\r
298 \r
299 For each grid line it is drawn using the function \c sp_grid_hline\r
300 or \c sp_grid_vline. These are convience functions for the sake\r
301 of making the function easier to read.\r
302 \r
303 Also, there are emphasized lines on the grid. While the \c syg and\r
304 \c sxg variable track grid positioning, the \c xlinestart and \c\r
305 ylinestart variables track the 'count' of what lines they are. If\r
306 that count is a multiple of the line seperation between emphasis\r
307 lines, then that line is drawn in the emphasis color.\r
308 */\r
309 static void\r
310 sp_caxonomgrid_render (SPCanvasItem * item, SPCanvasBuf * buf)\r
311 {\r
312 SPCAxonomGrid *grid = SP_CAXONOMGRID (item);\r
313 \r
314 sp_canvas_prepare_buffer (buf);\r
315 \r
316 // gc = gridcoordinates (the coordinates calculated from the grids origin 'grid->ow'.\r
317 // sc = screencoordinates ( for example "buf->rect.x0" is in screencoordinates )\r
318 // bc = buffer patch coordinates \r
319 \r
320 // tl = topleft ; br = bottomright\r
321 NR::Point buf_tl_gc;\r
322 NR::Point buf_br_gc;\r
323 buf_tl_gc[NR::X] = buf->rect.x0 - grid->ow[NR::X];\r
324 buf_tl_gc[NR::Y] = buf->rect.y0 - grid->ow[NR::Y];\r
325 buf_br_gc[NR::X] = buf->rect.x1 - grid->ow[NR::X];\r
326 buf_br_gc[NR::Y] = buf->rect.y1 - grid->ow[NR::Y];\r
327 \r
328 \r
329 gdouble x;\r
330 gdouble y;\r
331 \r
332 // render the three separate line groups representing the main-axes:\r
333 // x-axis always goes from topleft to bottomright. (0,0) - (1,1) \r
334 const gdouble xintercept_y_bc = (buf_tl_gc[NR::X] * grid->tan_angle[X]) - buf_tl_gc[NR::Y] ;\r
335 const gdouble xstart_y_sc = ( xintercept_y_bc - floor(xintercept_y_bc/grid->lyw)*grid->lyw ) + buf->rect.y0;\r
336 const gint xlinestart = (gint) Inkscape::round( (xstart_y_sc - grid->ow[NR::Y]) / grid->lyw );\r
337 gint xlinenum;\r
338 // lijnen vanaf linker zijkant.\r
339 for (y = xstart_y_sc, xlinenum = xlinestart; y < buf->rect.y1; y += grid->lyw, xlinenum++) {\r
340 const gint x0 = buf->rect.x0;\r
341 const gint y0 = (gint) Inkscape::round(y);\r
342 const gint x1 = x0 + (gint) Inkscape::round( (buf->rect.y1 - y) / grid->tan_angle[X] );\r
343 const gint y1 = buf->rect.y1;\r
344 \r
345 if (!grid->scaled && (xlinenum % grid->empspacing) == 0) {\r
346 sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->empcolor);\r
347 } else {\r
348 sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->color);\r
349 }\r
350 }\r
351 // lijnen vanaf bovenkant.\r
352 const gdouble xstart_x_sc = buf->rect.x0 + (grid->lxw_x - (xstart_y_sc - buf->rect.y0) / grid->tan_angle[X]) ;\r
353 for (x = xstart_x_sc, xlinenum = xlinestart; x < buf->rect.x1; x += grid->lxw_x, xlinenum--) {\r
354 const gint y0 = buf->rect.y0;\r
355 const gint y1 = buf->rect.y1;\r
356 const gint x0 = (gint) Inkscape::round(x);\r
357 const gint x1 = x0 + (gint) Inkscape::round( (y1 - y0) / grid->tan_angle[X] );\r
358 \r
359 if (!grid->scaled && (xlinenum % grid->empspacing) == 0) {\r
360 sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->empcolor);\r
361 } else {\r
362 sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->color);\r
363 }\r
364 }\r
365 \r
366 \r
367 // y-axis lines (vertical)\r
368 const gdouble ystart_x_sc = floor (buf_tl_gc[NR::X] / grid->spacing_ylines) * grid->spacing_ylines + grid->ow[NR::X];\r
369 const gint ylinestart = (gint) Inkscape::round((ystart_x_sc - grid->ow[NR::X]) / grid->spacing_ylines);\r
370 gint ylinenum;\r
371 for (x = ystart_x_sc, ylinenum = ylinestart; x < buf->rect.x1; x += grid->spacing_ylines, ylinenum++) {\r
372 const gint x0 = (gint) Inkscape::round(x);\r
373 \r
374 if (!grid->scaled && (ylinenum % grid->empspacing) == 0) {\r
375 sp_grid_vline (buf, x0, buf->rect.y0, buf->rect.y1 - 1, grid->empcolor);\r
376 } else {\r
377 sp_grid_vline (buf, x0, buf->rect.y0, buf->rect.y1 - 1, grid->color);\r
378 }\r
379 }\r
380 \r
381 // z-axis always goes from bottomleft to topright. (0,1) - (1,0) \r
382 const gdouble zintercept_y_bc = (buf_tl_gc[NR::X] * -grid->tan_angle[Z]) - buf_tl_gc[NR::Y] ;\r
383 const gdouble zstart_y_sc = ( zintercept_y_bc - floor(zintercept_y_bc/grid->lyw)*grid->lyw ) + buf->rect.y0;\r
384 const gint zlinestart = (gint) Inkscape::round( (zstart_y_sc - grid->ow[NR::Y]) / grid->lyw );\r
385 gint zlinenum;\r
386 // lijnen vanaf linker zijkant.\r
387 for (y = zstart_y_sc, zlinenum = zlinestart; y < buf->rect.y1; y += grid->lyw, zlinenum++) {\r
388 const gint x0 = buf->rect.x0;\r
389 const gint y0 = (gint) Inkscape::round(y);\r
390 const gint x1 = x0 + (gint) Inkscape::round( (y - buf->rect.y0 ) / grid->tan_angle[Z] );\r
391 const gint y1 = buf->rect.y0;\r
392 \r
393 if (!grid->scaled && (zlinenum % grid->empspacing) == 0) {\r
394 sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->empcolor);\r
395 } else {\r
396 sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->color);\r
397 }\r
398 }\r
399 // lijnen vanaf onderkant.\r
400 const gdouble zstart_x_sc = buf->rect.x0 + (y - buf->rect.y1) / grid->tan_angle[Z] ;\r
401 for (x = zstart_x_sc; x < buf->rect.x1; x += grid->lxw_z, zlinenum--) {\r
402 const gint y0 = buf->rect.y1;\r
403 const gint y1 = buf->rect.y0;\r
404 const gint x0 = (gint) Inkscape::round(x);\r
405 const gint x1 = x0 + (gint) Inkscape::round( (buf->rect.y1 - buf->rect.y0) / grid->tan_angle[Z] );\r
406 \r
407 if (!grid->scaled && (zlinenum % grid->empspacing) == 0) {\r
408 sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->empcolor);\r
409 } else {\r
410 sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->color);\r
411 }\r
412 }\r
413 \r
414 }\r
415 \r
416 static void\r
417 sp_caxonomgrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)\r
418 {\r
419 SPCAxonomGrid *grid = SP_CAXONOMGRID (item);\r
420 \r
421 if (parent_class->update)\r
422 (* parent_class->update) (item, affine, flags);\r
423 \r
424 grid->ow = grid->origin * affine;\r
425 grid->sw = NR::Point(fabs(affine[0]),fabs(affine[3]));\r
426 \r
427 for(int dim = 0; dim < 2; dim++) {\r
428 gint scaling_factor = grid->empspacing;\r
429 \r
430 if (scaling_factor <= 1)\r
431 scaling_factor = 5;\r
432 \r
433 grid->scaled = FALSE;\r
434 while (grid->sw[dim] < 8.0) {\r
435 grid->scaled = TRUE;\r
436 grid->sw[dim] *= scaling_factor;\r
437 // First pass, go up to the major line spacing, then\r
438 // keep increasing by two.\r
439 scaling_factor = 2;\r
440 }\r
441 }\r
442 \r
443 grid->spacing_ylines = grid->sw[NR::X] * grid->lengthy /(grid->tan_angle[X] + grid->tan_angle[Z]);\r
444 grid->lyw = grid->lengthy * grid->sw[NR::Y];\r
445 grid->lxw_x = (grid->lengthy / grid->tan_angle[X]) * grid->sw[NR::X];\r
446 grid->lxw_z = (grid->lengthy / grid->tan_angle[Z]) * grid->sw[NR::X];\r
447 \r
448 if (grid->empspacing == 0) {\r
449 grid->scaled = TRUE;\r
450 }\r
451 \r
452 sp_canvas_request_redraw (item->canvas,\r
453 -1000000, -1000000,\r
454 1000000, 1000000);\r
455 \r
456 item->x1 = item->y1 = -1000000;\r
457 item->x2 = item->y2 = 1000000;\r
458 }\r
459 \r
460 /*\r
461 Local Variables:\r
462 mode:c++\r
463 c-file-style:"stroustrup"\r
464 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
465 indent-tabs-mode:nil\r
466 fill-column:99\r
467 End:\r
468 */\r
469 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r