Code

axonometric grid: angles cannot be larger than 89 degrees.
[inkscape.git] / src / display / canvas-axonomgrid.cpp
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     g_message("arg");\r
139     \r
140     switch (arg_id) {\r
141     case ARG_ORIGINX:\r
142         grid->origin[NR::X] = GTK_VALUE_DOUBLE (* arg);\r
143         sp_canvas_item_request_update (item);\r
144         break;\r
145     case ARG_ORIGINY:\r
146         grid->origin[NR::Y] = GTK_VALUE_DOUBLE (* arg);\r
147         sp_canvas_item_request_update (item);\r
148         break;\r
149     case ARG_ANGLEX:\r
150         grid->angle_deg[X] = GTK_VALUE_DOUBLE (* arg);\r
151         if (grid->angle_deg[X] < 0.0) grid->angle_deg[X] = 0.0;\r
152         if (grid->angle_deg[X] > 89.0) grid->angle_deg[X] = 89.0;\r
153         grid->angle_rad[X] = deg_to_rad(grid->angle_deg[X]);\r
154         grid->tan_angle[X] = tan(grid->angle_rad[X]);\r
155         sp_canvas_item_request_update (item);\r
156         break;\r
157     case ARG_SPACINGY:\r
158         grid->lengthy = GTK_VALUE_DOUBLE (* arg);\r
159         if (grid->lengthy < 0.01) grid->lengthy = 0.01;\r
160         sp_canvas_item_request_update (item);\r
161         break;\r
162     case ARG_ANGLEZ:\r
163         grid->angle_deg[Z] = GTK_VALUE_DOUBLE (* arg);\r
164         if (grid->angle_deg[Z] < 0.0) grid->angle_deg[Z] = 0.0;\r
165         if (grid->angle_deg[X] > 89.0) grid->angle_deg[X] = 89.0;\r
166         grid->angle_rad[Z] = deg_to_rad(grid->angle_deg[Z]);\r
167         grid->tan_angle[Z] = tan(grid->angle_rad[Z]);\r
168         sp_canvas_item_request_update (item);\r
169         break;\r
170     case ARG_COLOR:\r
171         grid->color = GTK_VALUE_INT (* arg);\r
172         sp_canvas_item_request_update (item);\r
173         break;\r
174     case ARG_EMPCOLOR:\r
175         grid->empcolor = GTK_VALUE_INT (* arg);\r
176         sp_canvas_item_request_update (item);\r
177         break;\r
178     case ARG_EMPSPACING:\r
179         grid->empspacing = GTK_VALUE_INT (* arg);\r
180         // std::cout << "Emphasis Spacing: " << grid->empspacing << std::endl;\r
181         sp_canvas_item_request_update (item);\r
182         break;\r
183     default:\r
184         break;\r
185     }\r
186 }\r
187 \r
188 \r
189 \r
190 /**\r
191     \brief  This function renders a pixel on a particular buffer.\r
192                 \r
193     The topleft of the buffer equals\r
194                         ( rect.x0 , rect.y0 )  in screen coordinates\r
195                         ( 0 , 0 )  in setpixel coordinates\r
196     The bottomright of the buffer equals\r
197                         ( rect.x1 , rect,y1 )  in screen coordinates\r
198                         ( rect.x1 - rect.x0 , rect.y1 - rect.y0 )  in setpixel coordinates\r
199 */\r
200 static void \r
201 sp_caxonomgrid_setpixel (SPCanvasBuf *buf, gint x, gint y, guint32 rgba) {\r
202 #ifdef SAFE_SETPIXEL\r
203     if ( (x >= buf->rect.x0) && (x < buf->rect.x1) && (y >= buf->rect.y0) && (y < buf->rect.y1) ) {\r
204 #endif        \r
205         guint r, g, b, a;          \r
206         r = NR_RGBA32_R (rgba);\r
207         g = NR_RGBA32_G (rgba);\r
208         b = NR_RGBA32_B (rgba);\r
209         a = NR_RGBA32_A (rgba);  \r
210         guchar * p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;\r
211         p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);\r
212         p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);\r
213         p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);\r
214 #ifdef SAFE_SETPIXEL\r
215     }\r
216 #endif    \r
217 }\r
218 \r
219 /**\r
220     \brief  This function renders a line on a particular canvas buffer,\r
221             using Bresenham's line drawing function.\r
222             http://www.cs.unc.edu/~mcmillan/comp136/Lecture6/Lines.html \r
223             Coordinates are interpreted as SCREENcoordinates\r
224 */\r
225 static void \r
226 sp_caxonomgrid_drawline (SPCanvasBuf *buf, gint x0, gint y0, gint x1, gint y1, guint32 rgba) {\r
227     int dy = y1 - y0;\r
228     int dx = x1 - x0;\r
229     int stepx, stepy;\r
230 \r
231     if (dy < 0) { dy = -dy;  stepy = -1; } else { stepy = 1; }\r
232     if (dx < 0) { dx = -dx;  stepx = -1; } else { stepx = 1; }\r
233     dy <<= 1;                                                  // dy is now 2*dy\r
234     dx <<= 1;                                                  // dx is now 2*dx\r
235 \r
236     sp_caxonomgrid_setpixel(buf, x0, y0, rgba);\r
237     if (dx > dy) {\r
238         int fraction = dy - (dx >> 1);                         // same as 2*dy - dx\r
239         while (x0 != x1) {\r
240             if (fraction >= 0) {\r
241                 y0 += stepy;\r
242                 fraction -= dx;                                // same as fraction -= 2*dx\r
243             }\r
244             x0 += stepx;\r
245             fraction += dy;                                    // same as fraction -= 2*dy\r
246             sp_caxonomgrid_setpixel(buf, x0, y0, rgba);\r
247         }\r
248     } else {\r
249         int fraction = dx - (dy >> 1);\r
250         while (y0 != y1) {\r
251             if (fraction >= 0) {\r
252                 x0 += stepx;\r
253                 fraction -= dy;\r
254             }\r
255             y0 += stepy;\r
256             fraction += dx;\r
257             sp_caxonomgrid_setpixel(buf, x0, y0, rgba);\r
258         }\r
259     }\r
260     \r
261 }\r
262 \r
263 static void\r
264 sp_grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba)\r
265 {\r
266     if ((x >= buf->rect.x0) && (x < buf->rect.x1)) {\r
267         guint r, g, b, a;\r
268         gint y0, y1, y;\r
269         guchar *p;\r
270         r = NR_RGBA32_R(rgba);\r
271         g = NR_RGBA32_G (rgba);\r
272         b = NR_RGBA32_B (rgba);\r
273         a = NR_RGBA32_A (rgba);\r
274         y0 = MAX (buf->rect.y0, ys);\r
275         y1 = MIN (buf->rect.y1, ye + 1);\r
276         p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;\r
277         for (y = y0; y < y1; y++) {\r
278             p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);\r
279             p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);\r
280             p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);\r
281             p += buf->buf_rowstride;\r
282         }\r
283     }\r
284 }\r
285 \r
286 /**\r
287     \brief  This function renders the grid on a particular canvas buffer\r
288     \param  item  The grid to render on the buffer\r
289     \param  buf   The buffer to render the grid on\r
290     \r
291     This function gets called a touch more than you might believe,\r
292     about once per tile.  This means that it could probably be optimized\r
293     and help things out.\r
294 \r
295     Basically this function has to determine where in the canvas it is,\r
296     and how that associates with the grid.  It does this first by looking\r
297     at the bounding box of the buffer, and then calculates where the grid\r
298     starts in that buffer.  It will then step through grid lines until\r
299     it is outside of the buffer.\r
300 \r
301     For each grid line it is drawn using the function \c sp_grid_hline\r
302     or \c sp_grid_vline.  These are convience functions for the sake\r
303     of making the function easier to read.\r
304 \r
305     Also, there are emphasized lines on the grid.  While the \c syg and\r
306     \c sxg variable track grid positioning, the \c xlinestart and \c\r
307     ylinestart variables track the 'count' of what lines they are.  If\r
308     that count is a multiple of the line seperation between emphasis\r
309     lines, then that line is drawn in the emphasis color.\r
310 */\r
311 static void\r
312 sp_caxonomgrid_render (SPCanvasItem * item, SPCanvasBuf * buf)\r
313 {\r
314     SPCAxonomGrid *grid = SP_CAXONOMGRID (item);\r
315 \r
316     sp_canvas_prepare_buffer (buf);\r
317               \r
318      // gc = gridcoordinates (the coordinates calculated from the grids origin 'grid->ow'.\r
319      // sc = screencoordinates ( for example "buf->rect.x0" is in screencoordinates )\r
320      // bc = buffer patch coordinates \r
321      \r
322      // tl = topleft ; br = bottomright\r
323     NR::Point buf_tl_gc;\r
324     NR::Point buf_br_gc;\r
325     buf_tl_gc[NR::X] = buf->rect.x0 - grid->ow[NR::X];\r
326     buf_tl_gc[NR::Y] = buf->rect.y0 - grid->ow[NR::Y];\r
327     buf_br_gc[NR::X] = buf->rect.x1 - grid->ow[NR::X];\r
328     buf_br_gc[NR::Y] = buf->rect.y1 - grid->ow[NR::Y];\r
329 \r
330 \r
331     gdouble x;\r
332     gdouble y;\r
333 \r
334     // render the three separate line groups representing the main-axes:\r
335     // x-axis always goes from topleft to bottomright. (0,0) - (1,1)  \r
336     const gdouble xintercept_y_bc = (buf_tl_gc[NR::X] * grid->tan_angle[X]) - buf_tl_gc[NR::Y] ;\r
337     const gdouble xstart_y_sc = ( xintercept_y_bc - floor(xintercept_y_bc/grid->lyw)*grid->lyw ) + buf->rect.y0;\r
338     const gint  xlinestart = (gint) Inkscape::round( (xstart_y_sc - grid->ow[NR::Y]) / grid->lyw );\r
339     gint xlinenum;\r
340     // lijnen vanaf linker zijkant.\r
341     for (y = xstart_y_sc, xlinenum = xlinestart; y < buf->rect.y1; y += grid->lyw, xlinenum++) {\r
342         const gint x0 = buf->rect.x0;\r
343         const gint y0 = (gint) Inkscape::round(y);\r
344         const gint x1 = x0 + (gint) Inkscape::round( (buf->rect.y1 - y) / grid->tan_angle[X] );\r
345         const gint y1 = buf->rect.y1;\r
346             \r
347         if (!grid->scaled && (xlinenum % grid->empspacing) == 0) {\r
348             sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->empcolor);\r
349         } else {\r
350             sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->color);\r
351         }\r
352     }\r
353     // lijnen vanaf bovenkant.\r
354     const gdouble xstart_x_sc = buf->rect.x0 + (grid->lxw_x - (xstart_y_sc - buf->rect.y0) / grid->tan_angle[X]) ;\r
355     for (x = xstart_x_sc, xlinenum = xlinestart; x < buf->rect.x1; x += grid->lxw_x, xlinenum--) {\r
356         const gint y0 = buf->rect.y0;\r
357         const gint y1 = buf->rect.y1;\r
358         const gint x0 = (gint) Inkscape::round(x);\r
359         const gint x1 = x0 + (gint) Inkscape::round( (y1 - y0) / grid->tan_angle[X] );\r
360             \r
361         if (!grid->scaled && (xlinenum % grid->empspacing) == 0) {\r
362             sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->empcolor);\r
363         } else {\r
364             sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->color);\r
365         }\r
366     }\r
367     \r
368 \r
369     // y-axis lines (vertical)\r
370     const gdouble ystart_x_sc = floor (buf_tl_gc[NR::X] / grid->spacing_ylines) * grid->spacing_ylines + grid->ow[NR::X];\r
371     const gint  ylinestart = (gint) Inkscape::round((ystart_x_sc - grid->ow[NR::X]) / grid->spacing_ylines);\r
372     gint ylinenum;\r
373     for (x = ystart_x_sc, ylinenum = ylinestart; x < buf->rect.x1; x += grid->spacing_ylines, ylinenum++) {\r
374         const gint x0 = (gint) Inkscape::round(x);\r
375 \r
376         if (!grid->scaled && (ylinenum % grid->empspacing) == 0) {\r
377             sp_grid_vline (buf, x0, buf->rect.y0, buf->rect.y1 - 1, grid->empcolor);\r
378         } else {\r
379             sp_grid_vline (buf, x0, buf->rect.y0, buf->rect.y1 - 1, grid->color);\r
380         }\r
381     }\r
382 \r
383     // z-axis always goes from bottomleft to topright. (0,1) - (1,0)  \r
384     const gdouble zintercept_y_bc = (buf_tl_gc[NR::X] * -grid->tan_angle[Z]) - buf_tl_gc[NR::Y] ;\r
385     const gdouble zstart_y_sc = ( zintercept_y_bc - floor(zintercept_y_bc/grid->lyw)*grid->lyw ) + buf->rect.y0;\r
386     const gint  zlinestart = (gint) Inkscape::round( (zstart_y_sc - grid->ow[NR::Y]) / grid->lyw );\r
387     gint zlinenum;\r
388     // lijnen vanaf linker zijkant.\r
389     for (y = zstart_y_sc, zlinenum = zlinestart; y < buf->rect.y1; y += grid->lyw, zlinenum++) {\r
390         const gint x0 = buf->rect.x0;\r
391         const gint y0 = (gint) Inkscape::round(y);\r
392         const gint x1 = x0 + (gint) Inkscape::round( (y - buf->rect.y0 ) / grid->tan_angle[Z] );\r
393         const gint y1 = buf->rect.y0;\r
394             \r
395         if (!grid->scaled && (zlinenum % grid->empspacing) == 0) {\r
396             sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->empcolor);\r
397         } else {\r
398             sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->color);\r
399         }\r
400     }\r
401     // lijnen vanaf onderkant.\r
402     const gdouble zstart_x_sc = buf->rect.x0 + (y - buf->rect.y1) / grid->tan_angle[Z] ;\r
403     for (x = zstart_x_sc; x < buf->rect.x1; x += grid->lxw_z, zlinenum--) {\r
404         const gint y0 = buf->rect.y1;\r
405         const gint y1 = buf->rect.y0;\r
406         const gint x0 = (gint) Inkscape::round(x);\r
407         const gint x1 = x0 + (gint) Inkscape::round( (buf->rect.y1 - buf->rect.y0) / grid->tan_angle[Z] );\r
408             \r
409         if (!grid->scaled && (zlinenum % grid->empspacing) == 0) {\r
410             sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->empcolor);\r
411         } else {\r
412             sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, grid->color);\r
413         }\r
414     }\r
415     \r
416 }\r
417 \r
418 static void\r
419 sp_caxonomgrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)\r
420 {\r
421     SPCAxonomGrid *grid = SP_CAXONOMGRID (item);\r
422 \r
423     if (parent_class->update)\r
424         (* parent_class->update) (item, affine, flags);\r
425 \r
426     grid->ow = grid->origin * affine;\r
427     grid->sw = NR::Point(fabs(affine[0]),fabs(affine[3]));\r
428     \r
429     for(int dim = 0; dim < 2; dim++) {\r
430         gint scaling_factor = grid->empspacing;\r
431 \r
432         if (scaling_factor <= 1)\r
433             scaling_factor = 5;\r
434 \r
435         grid->scaled = FALSE;\r
436         while (grid->sw[dim] < 8.0) {\r
437             grid->scaled = TRUE;\r
438             grid->sw[dim] *= scaling_factor;\r
439             // First pass, go up to the major line spacing, then\r
440             // keep increasing by two.\r
441             scaling_factor = 2;\r
442         }\r
443     }\r
444 \r
445     grid->spacing_ylines = grid->sw[NR::X] * grid->lengthy  /(grid->tan_angle[X] + grid->tan_angle[Z]);\r
446     grid->lyw            = grid->lengthy * grid->sw[NR::Y];\r
447     grid->lxw_x          = (grid->lengthy / grid->tan_angle[X]) * grid->sw[NR::X];\r
448     grid->lxw_z          = (grid->lengthy / grid->tan_angle[Z]) * grid->sw[NR::X];\r
449 \r
450     if (grid->empspacing == 0) {\r
451         grid->scaled = TRUE;\r
452     }\r
453 \r
454     sp_canvas_request_redraw (item->canvas,\r
455                      -1000000, -1000000,\r
456                      1000000, 1000000);\r
457                      \r
458     item->x1 = item->y1 = -1000000;\r
459     item->x2 = item->y2 = 1000000;\r
460 }\r
461 \r
462 /*\r
463   Local Variables:\r
464   mode:c++\r
465   c-file-style:"stroustrup"\r
466   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
467   indent-tabs-mode:nil\r
468   fill-column:99\r
469   End:\r
470 */\r
471 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r