More honesty about bug 144394, it's not quite fixed yet.
[dia.git] / app / grid.c
blob38341e71fa2d7e5701a477d5193169c7932bb4cb
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <math.h>
26 #include <glib.h>
28 #include "intl.h"
29 #include "grid.h"
30 #include "preferences.h"
32 /** Calculate the width (in cm) of the gap between grid lines in dynamic
33 * grid mode.
35 static void
36 calculate_dynamic_grid(DDisplay *ddisp, real *width_x, real *width_y)
38 real zoom = ddisplay_untransform_length(ddisp, 1.0);
39 real ret, tmp;
40 /* Twiddle zoom to make change-over appropriate */
41 zoom *= 5;
42 ret = pow(10, ceil(log10(zoom)));
43 /* dont' make it too small or huge (this is in pixels) */
44 tmp = ddisplay_transform_length(ddisp, ret);
45 if (tmp < 10.0)
46 ret *= 2.0;
47 else if (tmp > 35.0)
48 ret /= 2.0;
49 *width_x = ret;
50 *width_y = ret;
53 static void
54 grid_draw_horizontal_lines(DDisplay *ddisp, Rectangle *update, real length)
56 int x, y;
57 real pos;
58 int height, width;
59 guint major_lines = ddisp->diagram->grid.major_lines;
60 DiaRenderer *renderer = ddisp->renderer;
61 DiaInteractiveRendererInterface *irenderer;
62 int major_count = 0;
64 irenderer = DIA_GET_INTERACTIVE_RENDERER_INTERFACE (renderer);
66 pos = ceil( update->top / length ) * length;
67 ddisplay_transform_coords(ddisp, update->left, pos, &x, &y);
68 ddisplay_transform_coords(ddisp, update->right, update->bottom, &width, &height);
71 Explanatory note from Lawrence Withers (lwithers@users.sf.net):
72 Think about it in terms of the maths and the modulo arithmetic; we
73 have a number P such that P < 0, and we want to find a number Q
74 such that Q >= 0, but (P % major_count) == (Q % major_count). To
75 do this, we say Q = P - (P * major_count), which is guaranteed to
76 give a correct answer if major_count > 0 (since the addition of
77 anf multiple of major_count won't alter the modulo).
80 if (major_lines) {
81 major_count = (int)ceil(pos/length);
82 if(major_count < 0) major_count -= major_lines * major_count;
83 major_count %= major_lines;
86 while (y < height) {
87 if (major_lines) {
88 if (major_count == 0)
89 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
90 else
91 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_DOTTED);
92 major_count = (major_count+1)%major_lines;
94 irenderer->draw_pixel_line(renderer, x, y, width, y,
95 &ddisp->diagram->grid.colour);
96 pos += length;
97 ddisplay_transform_coords(ddisp, update->left, pos, &x, &y);
101 static void
102 grid_draw_vertical_lines(DDisplay *ddisp, Rectangle *update, real length)
104 int x = 0, y = 0;
105 real pos;
106 int height, width;
107 guint major_lines = ddisp->diagram->grid.major_lines;
108 DiaRenderer *renderer = ddisp->renderer;
109 DiaInteractiveRendererInterface *irenderer;
110 int major_count = 0;
112 irenderer = DIA_GET_INTERACTIVE_RENDERER_INTERFACE (renderer);
114 pos = ceil( update->left / length ) * length;
115 ddisplay_transform_coords(ddisp, update->right, update->bottom, &width, &height);
117 if (major_lines) {
118 major_count = (int)ceil(pos/length);
119 if(major_count < 0) major_count -= major_lines * major_count;
120 major_count %= major_lines;
123 while (x < width) {
124 ddisplay_transform_coords(ddisp, pos, update->top, &x, &y);
125 if (major_lines) {
126 if (major_count == 0)
127 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
128 else
129 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_DOTTED);
130 major_count = (major_count+1)%major_lines;
132 irenderer->draw_pixel_line(renderer, x, y, x, height,
133 &ddisp->diagram->grid.colour);
134 pos += length;
138 static void
139 grid_draw_hex(DDisplay *ddisp, Rectangle *update, real length)
141 real horiz_pos, vert_pos;
142 int to_x, to_y, x, y;
143 DiaRenderer *renderer = ddisp->renderer;
144 DiaInteractiveRendererInterface *irenderer;
146 irenderer = DIA_GET_INTERACTIVE_RENDERER_INTERFACE (renderer);
148 /* First horizontal lines: */
149 vert_pos = ceil( update->top / (length * sqrt(3)) ) * length * sqrt(3);
150 while (vert_pos <= update->bottom) {
151 horiz_pos = ceil( (update->left) / (3 * length) ) * length * 3 - length * 2.5;
152 while (horiz_pos <= update->right) {
153 ddisplay_transform_coords(ddisp, horiz_pos, vert_pos, &x, &y);
154 ddisplay_transform_coords(ddisp, horiz_pos + length, vert_pos, &to_x, &y);
156 irenderer->draw_pixel_line(renderer,
157 x, y, to_x, y,
158 &ddisp->diagram->grid.colour);
159 horiz_pos += 3 * length;
162 vert_pos += sqrt(3) * length;
165 /* Second horizontal lines: */
166 vert_pos = ceil( update->top / (length * sqrt(3)) ) * length * sqrt(3) - 0.5 * sqrt(3) * length;
167 while (vert_pos <= update->bottom) {
168 horiz_pos = ceil( (update->left) / (3 * length) ) * length * 3 - length;
169 while (horiz_pos <= update->right) {
170 ddisplay_transform_coords(ddisp, horiz_pos, vert_pos, &x, &y);
171 ddisplay_transform_coords(ddisp, horiz_pos+length, vert_pos, &to_x, &y);
173 irenderer->draw_pixel_line(renderer,
174 x, y, to_x, y,
175 &ddisp->diagram->grid.colour);
176 horiz_pos += 3 * length;
179 vert_pos += sqrt(3) * length;
182 /* First \'s and /'s */
183 vert_pos = ceil( update->top / (length * sqrt(3)) ) * length * sqrt(3) - length * sqrt(3);
184 while (vert_pos <= update->bottom) {
185 horiz_pos = ceil( (update->left) / (3 * length) ) * length * 3 - length * 2.5;
186 while (horiz_pos <= update->right) {
187 ddisplay_transform_coords(ddisp, horiz_pos + length, vert_pos, &x, &y);
188 ddisplay_transform_coords(ddisp, horiz_pos + 1.5 * length, vert_pos + length * sqrt(3) * 0.5, &to_x, &to_y);
190 irenderer->draw_pixel_line(renderer,
191 x, y, to_x, to_y,
192 &ddisp->diagram->grid.colour);
194 ddisplay_transform_coords(ddisp, horiz_pos, vert_pos, &x, &y);
195 ddisplay_transform_coords(ddisp, horiz_pos - 0.5 * length, vert_pos + length * sqrt(3) * 0.5, &to_x, &to_y);
197 irenderer->draw_pixel_line(renderer,
198 x, y, to_x, to_y,
199 &ddisp->diagram->grid.colour);
200 horiz_pos += 3 * length;
203 vert_pos += sqrt(3) * length;
206 /* Second \'s and /'s */
207 vert_pos = ceil( update->top / (length * sqrt(3)) ) * length * sqrt(3) - 0.5 * sqrt(3) * length;
208 while (vert_pos <= update->bottom) {
209 horiz_pos = ceil( (update->left) / (3 * length) ) * length * 3 - length;
210 while (horiz_pos <= update->right) {
211 ddisplay_transform_coords(ddisp, horiz_pos, vert_pos, &x, &y);
212 ddisplay_transform_coords(ddisp, horiz_pos - 0.5 * length, vert_pos + 0.5 * sqrt(3) * length, &to_x, &to_y);
214 irenderer->draw_pixel_line(renderer,
215 x, y, to_x, to_y,
216 &ddisp->diagram->grid.colour);
218 ddisplay_transform_coords(ddisp, horiz_pos + length, vert_pos, &x, &y);
219 ddisplay_transform_coords(ddisp, horiz_pos + 1.5 * length, vert_pos + 0.5 * sqrt(3) * length, &to_x, &to_y);
221 irenderer->draw_pixel_line(renderer,
222 x, y, to_x, to_y,
223 &ddisp->diagram->grid.colour);
224 horiz_pos += 3 * length;
227 vert_pos += sqrt(3) * length;
232 void
233 grid_draw(DDisplay *ddisp, Rectangle *update)
235 Grid *grid = &ddisp->grid;
236 DiaRenderer *renderer = ddisp->renderer;
238 if (grid->visible) {
239 /* distance between visible grid lines */
240 real width_x = ddisp->diagram->grid.width_x;
241 real width_y = ddisp->diagram->grid.width_y;
242 real width_w = ddisp->diagram->grid.width_w;
243 if (ddisp->diagram->grid.dynamic) {
244 calculate_dynamic_grid(ddisp, &width_x, &width_y);
245 } else {
246 width_x = ddisp->diagram->grid.width_x *
247 ddisp->diagram->grid.visible_x;
248 width_y = ddisp->diagram->grid.width_y *
249 ddisp->diagram->grid.visible_y;
252 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, 0.0);
253 DIA_RENDERER_GET_CLASS(renderer)->set_dashlength(renderer,
254 ddisplay_untransform_length(ddisp, 31));
256 if (ddisp->diagram->grid.hex) {
257 grid_draw_hex(ddisp, update, width_w);
258 } else {
259 if (ddisplay_transform_length(ddisp, width_y) >= 2.0 &&
260 ddisplay_transform_length(ddisp, width_x) >= 2.0) {
261 /* Vertical lines: */
262 grid_draw_vertical_lines(ddisp, update, width_x);
263 /* Horizontal lines: */
264 grid_draw_horizontal_lines(ddisp, update, width_y);
270 void
271 pagebreak_draw(DDisplay *ddisp, Rectangle *update)
273 DiaRenderer *renderer = ddisp->renderer;
274 DiaInteractiveRendererInterface *irenderer;
276 int width = dia_renderer_get_width_pixels(ddisp->renderer);
277 int height = dia_renderer_get_height_pixels(ddisp->renderer);
279 irenderer = DIA_GET_INTERACTIVE_RENDERER_INTERFACE (renderer);
280 if (prefs.pagebreak.visible) {
281 Diagram *dia = ddisp->diagram;
282 real origx = 0, origy = 0, pos;
283 real pwidth = dia->data->paper.width;
284 real pheight = dia->data->paper.height;
285 int x,y;
287 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, 0.0);
288 DIA_RENDERER_GET_CLASS(renderer)->set_dashlength(renderer,
289 ddisplay_untransform_length(ddisp, 31));
290 if (prefs.pagebreak.solid)
291 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
292 else
293 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_DOTTED);
295 if (dia->data->paper.fitto) {
296 origx = dia->data->extents.left;
297 origy = dia->data->extents.top;
300 /* vertical lines ... */
301 pos = origx + ceil((update->left - origx) / pwidth) * pwidth;
302 while (pos <= update->right) {
303 ddisplay_transform_coords(ddisp, pos,0,&x,&y);
304 irenderer->draw_pixel_line(renderer,
305 x, 0, x, height,
306 &dia->pagebreak_color);
307 pos += pwidth;
309 /* Horizontal lines: */
310 pos = origy + ceil((update->top - origy) / pheight) * pheight;
311 while (pos <= update->bottom) {
312 ddisplay_transform_coords(ddisp, 0,pos,&x,&y);
313 irenderer->draw_pixel_line(renderer,
314 0, y, width, y,
315 &dia->pagebreak_color);
316 pos += pheight;
321 void
322 snap_to_grid(DDisplay *ddisp, coord *x, coord *y)
324 if (ddisp->grid.snap) {
325 if (ddisp->diagram->grid.hex) {
326 real width_x = ddisp->diagram->grid.width_w;
327 real x_mod = (*x - 1*width_x) - floor((*x - 1*width_x) / (3*width_x)) * 3 * width_x;
328 real y_mod = (*y - 0.25*sqrt(3) * width_x) -
329 floor((*y - 0.25 * sqrt(3) * width_x) / (sqrt(3)*width_x)) * sqrt(3) * width_x;
331 if ( x_mod < (1.5 * width_x) ) {
332 if ( y_mod < 0.5 * sqrt(3) * width_x ) {
333 *x = floor((*x + 0.5*width_x) / (3*width_x)) * 3 * width_x + 2 * width_x;
334 *y = floor((*y - 0.25 * sqrt(3) * width_x) / (sqrt(3)*width_x)) * sqrt(3) * width_x + 0.5 * sqrt(3) * width_x;
335 } else {
336 *x = floor((*x + 0.5*width_x) / (3*width_x)) * 3 * width_x + 1.5 * width_x;
337 *y = floor((*y - 0.25 * sqrt(3) * width_x) / (sqrt(3)*width_x)) * sqrt(3) * width_x + sqrt(3) * width_x;
339 } else {
340 if ( y_mod < 0.5 * sqrt(3) * width_x ) {
341 *x = floor((*x + 0.5*width_x) / (3*width_x)) * 3 * width_x ;
342 *y = floor((*y - 0.25 * sqrt(3) * width_x) / (sqrt(3)*width_x)) * sqrt(3) * width_x + 0.5 * sqrt(3) * width_x;
343 } else {
344 *x = floor((*x + 0.5*width_x) / (3*width_x)) * 3 * width_x + 0.5 * width_x;
345 *y = floor((*y - 0.25 * sqrt(3) * width_x) / (sqrt(3)*width_x)) * sqrt(3) * width_x + sqrt(3) * width_x;
348 } else {
349 real width_x = ddisp->diagram->grid.width_x;
350 real width_y = ddisp->diagram->grid.width_y;
351 if (ddisp->diagram->grid.dynamic) {
352 calculate_dynamic_grid(ddisp, &width_x, &width_y);
354 *x = ROUND((*x) / width_x) * width_x;
355 *y = ROUND((*y) / width_y) * width_y;