Code

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