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
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
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
) {
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
) {
74 /* Fixed size grid in world coorinates */
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 */
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.
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
);
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
,
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;
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
);
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
;
136 for (j
= ys
; j
< ye
; j
= j
+ incr
) {
137 for (i
= xs
; i
< xe
; i
= i
+ incr
) {
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);
146 cairo_move_to (cr
, round (x1
), round (y1
));
147 cairo_arc (cr
, round (x1
), round (y1
),
160 /*! \brief Helper function for draw_mesh_grid_region
162 static void draw_mesh (GschemToplevel
*w_current
,
164 cairo_matrix_t
*user_to_device_matrix
,
166 int x_start
, int y_start
, int x_end
, int y_end
,
167 int incr
, int coarse_mult
,
168 gboolean leave_out_origin
)
171 double x1
, y1
, x2
, y2
;
172 int next_coarse_x
, next_coarse_y
;
173 int coarse_incr
= incr
* coarse_mult
;
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 */
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
;
208 if (j
== 0 && leave_out_origin
)
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));
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
;
232 if (i
== 0 && leave_out_origin
)
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));
251 /*! \brief Helper function for draw_mesh_grid_region
253 static void draw_origin (GschemToplevel
*w_current
,
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.,
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;
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));
277 x1
= 0; y1
= y_start
;
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));
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
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
) {
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
)
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.
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
;
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
);
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
,
373 MESH_COARSE_GRID_MULTIPLIER
,
377 if (coarse_increment
>= threshold
)
378 draw_mesh (w_current
,
380 &user_to_device_matrix
,
381 MESH_GRID_MAJOR_COLOR
,
391 draw_origin (w_current
,
393 &user_to_device_matrix
,
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
,
425 g_return_if_fail (w_current
!= NULL
);
427 grid_mode
= gschem_options_get_grid_mode (w_current
->options
);
434 draw_dots_grid_region (w_current
, cr
, x
, y
, width
, height
);
438 draw_mesh_grid_region (w_current
, cr
, x
, y
, width
, height
);
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
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
)
460 g_return_val_if_fail (w_current
!= NULL
, -1);
462 grid_mode
= gschem_options_get_grid_mode (w_current
->options
);
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
);