missing NULL terminator in set_config_x
[geda-gaf.git] / gschem / src / x_grid.c
blob8647c0ce7d653bbea9493383b303cce27515fc08
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include <config.h>
22 #include <math.h>
23 #ifdef HAVE_STRING_H
24 #include <string.h>
25 #endif
27 #include "gschem.h"
29 #define DOTS_VARIABLE_MODE_SPACING 30
31 #define MESH_COARSE_GRID_MULTIPLIER 5
34 /*! \brief Query the spacing in world coordinates at which the dots grid is drawn.
36 * \par Function Description
37 * Returns the world spacing of the rendered grid, taking into account where
38 * the grid drawing code may drop elements which are too densely packed for a
39 * given zoom level.
41 * \param [in] w_current The GschemToplevel.
42 * \returns The grid spacing in world units of the grid as rendered, or -1
43 * if there are no items drawn.
45 static int query_dots_grid_spacing (GschemToplevel *w_current)
47 int incr, screen_incr;
49 g_assert (w_current != NULL);
51 int snap_size = gschem_options_get_snap_size (w_current->options);
53 GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current);
54 g_return_val_if_fail (page_view != NULL, -1);
56 GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (page_view);
58 /* geometry may be NULL if page_view has no underlying page */
59 if (geometry == NULL) {
60 return -1;
63 if (w_current->dots_grid_mode == DOTS_GRID_VARIABLE_MODE) {
64 /* In the variable mode around every (DOTS_VARIABLE_MODE_SPACING)'th
65 * screenpixel will be grid-point. */
66 /* adding 0.1 for correct cast*/
67 incr = round_5_2_1 (geometry->to_world_x_constant * DOTS_VARIABLE_MODE_SPACING) + 0.1;
69 /* limit minimum grid spacing to grid to snap_size */
70 if (incr < snap_size) {
71 incr = snap_size;
73 } else {
74 /* Fixed size grid in world coorinates */
75 incr = snap_size;
76 screen_incr = gschem_page_view_SCREENabs (page_view, incr);
77 if (screen_incr < w_current->dots_grid_fixed_threshold) {
78 /* No grid drawn if the on-screen spacing is less than the threshold */
79 incr = -1;
82 return incr;
85 /*! \brief Draw an area of the screen with a dotted grid pattern
87 * \par Function Description
88 * Draws the dotted grid pattern over a given region of the screen.
90 * \param [in] w_current The GschemToplevel.
91 * \param [in] x The left screen coordinate for the drawing.
92 * \param [in] y The top screen coordinate for the drawing.
93 * \param [in] width The width of the region to draw.
94 * \param [in] height The height of the region to draw.
96 static void
97 draw_dots_grid_region (GschemToplevel *w_current, cairo_t *cr, int x, int y, int width, int height)
99 int incr = query_dots_grid_spacing (w_current);
101 if (incr == -1)
102 return;
104 int dot_size = min (w_current->dots_grid_dot_size, 5);
106 COLOR *color = x_color_lookup (DOTS_GRID_COLOR);
107 cairo_set_source_rgba (cr,
108 color->r / 255.0,
109 color->g / 255.0,
110 color->b / 255.0,
111 color->a / 255.0);
113 cairo_matrix_t user_to_device_matrix;
114 double x_start = x - 1;
115 double y_start = y + height + 1;
116 double x_end = x + width + 1;
117 double y_end = y - 1;
118 int xs, ys, xe, ye;
119 int i, j;
120 double x1, y1;
122 cairo_device_to_user (cr, &x_start, &y_start);
123 cairo_device_to_user (cr, &x_end, &y_end);
125 cairo_get_matrix (cr, &user_to_device_matrix);
126 cairo_save (cr);
127 cairo_identity_matrix (cr);
129 /* figure starting grid coordinates, work by taking the start
130 * and end coordinates and rounding down to the nearest increment */
131 xs = (floor (x_start/incr) - 1) * incr;
132 ys = (floor (y_start/incr) - 1) * incr;
133 xe = ceil (x_end);
134 ye = ceil (y_end);
136 for (j = ys; j < ye; j = j + incr) {
137 for (i = xs; i < xe; i = i + incr) {
138 x1 = i;
139 y1 = j;
141 cairo_matrix_transform_point (&user_to_device_matrix, &x1, &y1);
143 if (w_current->dots_grid_dot_size == 1) {
144 cairo_rectangle (cr, round (x1), round (y1), 1, 1);
145 } else {
146 cairo_move_to (cr, round (x1), round (y1));
147 cairo_arc (cr, round (x1), round (y1),
148 dot_size/2,
150 2*M_PI);
155 cairo_fill (cr);
156 cairo_restore (cr);
160 /*! \brief Helper function for draw_mesh_grid_region
162 static void draw_mesh (GschemToplevel *w_current,
163 cairo_t *cr,
164 cairo_matrix_t *user_to_device_matrix,
165 int color,
166 int x_start, int y_start, int x_end, int y_end,
167 int incr, int coarse_mult,
168 gboolean leave_out_origin)
170 int i, j;
171 double x1, y1, x2, y2;
172 int next_coarse_x, next_coarse_y;
173 int coarse_incr = incr * coarse_mult;
174 COLOR *c;
176 /* figure starting grid coordinates, work by taking the start
177 * and end coordinates and rounding down to the nearest increment */
178 x_start -= (x_start % incr) + (x_start < 0 ? incr : 0);
179 y_start -= (y_start % incr) + (y_start < 0 ? incr : 0);
181 if (coarse_incr == 0) {
182 next_coarse_x = x_start - 1; /* Ensures we never hit this when looping */
183 next_coarse_y = y_start - 1; /* Ensures we never hit this when looping */
184 } else {
185 next_coarse_x = x_start - (x_start % coarse_incr);
186 next_coarse_y = y_start - (y_start % coarse_incr);
187 if (next_coarse_x < x_start) next_coarse_x += coarse_incr;
188 if (next_coarse_y < y_start) next_coarse_y += coarse_incr;
191 c = x_color_lookup (color);
192 cairo_set_source_rgba (cr,
193 (double)c->r / 255.0,
194 (double)c->g / 255.0,
195 (double)c->b / 255.0,
196 (double)c->a / 255.0);
198 cairo_set_line_width (cr, 1.);
199 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
201 for (j = y_start; j < y_end; j = j + incr) {
203 /* Skip lines which will be drawn in the coarser grid */
204 if (j == next_coarse_y) {
205 next_coarse_y += coarse_incr;
206 continue;
208 if (j == 0 && leave_out_origin)
209 continue;
211 x1 = x_start;
212 y1 = j;
213 x2 = x_end;
214 y2 = j;
216 cairo_matrix_transform_point (user_to_device_matrix, &x1, &y1);
217 cairo_matrix_transform_point (user_to_device_matrix, &x2, &y2);
219 cairo_move_to (cr, (int)(x1+0.5), (int)(y1+0.5));
220 cairo_line_to (cr, (int)(x2+0.5), (int)(y2+0.5));
222 cairo_stroke (cr);
225 for (i = x_start; i < x_end; i = i + incr) {
227 /* Skip lines which will be drawn in the coarser grid */
228 if (i == next_coarse_x) {
229 next_coarse_x += coarse_incr;
230 continue;
232 if (i == 0 && leave_out_origin)
233 continue;
235 x1 = i;
236 y1 = y_start;
237 x2 = i;
238 y2 = y_end;
240 cairo_matrix_transform_point (user_to_device_matrix, &x1, &y1);
241 cairo_matrix_transform_point (user_to_device_matrix, &x2, &y2);
243 cairo_move_to (cr, (int)(x1+0.5), (int)(y1+0.5));
244 cairo_line_to (cr, (int)(x2+0.5), (int)(y2+0.5));
246 cairo_stroke (cr);
251 /*! \brief Helper function for draw_mesh_grid_region
253 static void draw_origin (GschemToplevel *w_current,
254 cairo_t *cr,
255 cairo_matrix_t *user_to_device_matrix,
256 int x_start, int y_start, int x_end, int y_end)
258 COLOR *c = x_color_lookup (ORIGIN_COLOR);
259 cairo_set_source_rgba (cr, (double)c->r / 255.,
260 (double)c->g / 255.,
261 (double)c->b / 255.,
262 (double)c->a / 255.);
263 cairo_set_line_width (cr, 1.);
264 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
266 double x1, y1, x2, y2;
268 x1 = x_start; y1 = 0;
269 x2 = x_end; y2 = 0;
270 cairo_matrix_transform_point (user_to_device_matrix, &x1, &y1);
271 cairo_matrix_transform_point (user_to_device_matrix, &x2, &y2);
273 cairo_move_to (cr, (int)(x1 + .5), (int)(y1 + .5));
274 cairo_line_to (cr, (int)(x2 + .5), (int)(y2 + .5));
275 cairo_stroke (cr);
277 x1 = 0; y1 = y_start;
278 x2 = 0; y2 = y_end;
279 cairo_matrix_transform_point (user_to_device_matrix, &x1, &y1);
280 cairo_matrix_transform_point (user_to_device_matrix, &x2, &y2);
282 cairo_move_to (cr, (int)(x1 + .5), (int)(y1 + .5));
283 cairo_line_to (cr, (int)(x2 + .5), (int)(y2 + .5));
284 cairo_stroke (cr);
288 /*! \brief Query the spacing in world coordinates at which the mesh grid is drawn.
290 * \par Function Description
291 * Returns the world spacing of the rendered grid, taking into account where
292 * the grid drawing code may drop elements which are too densely packed for a
293 * given zoom level.
295 * \param [in] w_current The GschemToplevel.
296 * \returns The grid spacing in world units of the grid as rendered, or -1
297 * if there are no items drawn.
299 static int query_mesh_grid_spacing (GschemToplevel *w_current)
301 int incr, screen_incr;
303 GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current);
304 g_return_val_if_fail (page_view != NULL, -1);
306 incr = gschem_options_get_snap_size (w_current->options);
307 screen_incr = gschem_page_view_SCREENabs (page_view, incr);
309 /* We draw a fine grid if its on-screen spacing is large enough */
310 if (screen_incr >= w_current->mesh_grid_display_threshold) {
311 return incr;
314 incr *= MESH_COARSE_GRID_MULTIPLIER;
315 screen_incr = gschem_page_view_SCREENabs (page_view, incr);
317 /* We draw a coarse grid if its on-screen spacing is large enough */
318 if (screen_incr >= w_current->mesh_grid_display_threshold)
319 return incr;
321 return -1;
324 /*! \brief Draw an area of the screen with a mesh grid pattern
326 * \par Function Description
327 * Draws the mesh grid pattern over a given region of the screen.
329 * \param [in] w_current The GschemToplevel.
330 * \param [in] cr The cairo context.
331 * \param [in] x The left screen coordinate for the drawing.
332 * \param [in] y The top screen coordinate for the drawing.
333 * \param [in] width The width of the region to draw.
334 * \param [in] height The height of the region to draw.
336 static void
337 draw_mesh_grid_region (GschemToplevel *w_current, cairo_t *cr, int x, int y, int width, int height)
339 int snap_size = gschem_options_get_snap_size (w_current->options);
340 int coarse_increment = MESH_COARSE_GRID_MULTIPLIER * snap_size;
341 double dummy = 0.0;
342 double threshold = w_current->mesh_grid_display_threshold;
343 gboolean show_origin = gschem_options_get_show_origin (w_current->options);
345 cairo_device_to_user_distance (cr, &threshold, &dummy);
347 if (coarse_increment >= threshold || show_origin) {
348 cairo_matrix_t user_to_device_matrix;
349 double x_start = x - 1;
350 double y_start = y + height + 1;
351 double x_end = x + width + 1;
352 double y_end = y - 1;
354 cairo_device_to_user (cr, &x_start, &y_start);
355 cairo_device_to_user (cr, &x_end, &y_end);
357 cairo_get_matrix (cr, &user_to_device_matrix);
358 cairo_save (cr);
359 cairo_identity_matrix (cr);
360 cairo_translate (cr, 0.5, 0.5);
362 /* Draw the fine grid if its on-screen spacing is large enough */
363 if (snap_size >= threshold) {
364 draw_mesh (w_current,
366 &user_to_device_matrix,
367 MESH_GRID_MINOR_COLOR,
368 floor (x_start),
369 floor (y_start),
370 ceil (x_end),
371 ceil (y_end),
372 snap_size,
373 MESH_COARSE_GRID_MULTIPLIER,
374 show_origin);
377 if (coarse_increment >= threshold)
378 draw_mesh (w_current,
380 &user_to_device_matrix,
381 MESH_GRID_MAJOR_COLOR,
382 floor (x_start),
383 floor (y_start),
384 ceil (x_end),
385 ceil (y_end),
386 coarse_increment,
388 show_origin);
390 if (show_origin)
391 draw_origin (w_current,
393 &user_to_device_matrix,
394 floor (x_start),
395 floor (y_start),
396 ceil (x_end),
397 ceil (y_end));
399 cairo_restore (cr);
404 /*! \brief Draw an area of the screen with the current grid pattern.
406 * \par Function Description
407 * Draws the desired grid pattern over a given region of the screen.
409 * \param [in] w_current The GschemToplevel.
410 * \param [in] cr The cairo context.
411 * \param [in] x The left screen coordinate for the drawing.
412 * \param [in] y The top screen coordinate for the drawing.
413 * \param [in] width The width of the region to draw.
414 * \param [in] height The height of the region to draw.
416 void x_grid_draw_region (GschemToplevel *w_current,
417 cairo_t *cr,
418 int x,
419 int y,
420 int width,
421 int height)
423 GRID_MODE grid_mode;
425 g_return_if_fail (w_current != NULL);
427 grid_mode = gschem_options_get_grid_mode (w_current->options);
429 switch (grid_mode) {
430 case GRID_MODE_NONE:
431 return;
433 case GRID_MODE_DOTS:
434 draw_dots_grid_region (w_current, cr, x, y, width, height);
435 break;
437 case GRID_MODE_MESH:
438 draw_mesh_grid_region (w_current, cr, x, y, width, height);
439 break;
440 default: g_assert_not_reached ();
445 /*! \brief Query the spacing in world coordinates at which the grid is drawn.
447 * \par Function Description
448 * Returns the world spacing of the rendered grid, taking into account where
449 * the grid drawing code may drop elements which are too densely packed for a
450 * given zoom level.
452 * \param [in] w_current The GschemToplevel.
453 * \returns The grid spacing in world units of the grid as rendered, or -1
454 * if there are no items drawn.
456 int x_grid_query_drawn_spacing (GschemToplevel *w_current)
458 GRID_MODE grid_mode;
460 g_return_val_if_fail (w_current != NULL, -1);
462 grid_mode = gschem_options_get_grid_mode (w_current->options);
464 switch (grid_mode) {
465 default:
466 case GRID_MODE_NONE: return -1;
467 case GRID_MODE_DOTS: return query_dots_grid_spacing (w_current);
468 case GRID_MODE_MESH: return query_mesh_grid_spacing (w_current);