1 //------------------------------------------------------------------------
2 // GRID STUFF
3 //------------------------------------------------------------------------
4 //
5 // Eureka DOOM Editor
6 //
7 // Copyright (C) 2001-2019 Andrew Apted
8 // Copyright (C) 1997-2003 André Majorel et al
9 //
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License
12 // as published by the Free Software Foundation; either version 2
13 // of the License, or (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 //------------------------------------------------------------------------
21 //
22 // Based on Yadex which incorporated code from DEU 5.21 that was put
23 // in the public domain in 1994 by Raphaël Quinet and Brendon Wyber.
24 //
25 //------------------------------------------------------------------------
26
27 #include "main.h"
28
29 #include "r_grid.h"
30 #include "e_main.h"
31 #include "ui_window.h"
32
33
34 Grid_State_c grid;
35
36 // config items
37 int grid_default_size = 64;
38 bool grid_default_snap = false;
39 int grid_default_mode = 0; // off
40
41 int grid_style; // 0 = squares, 1 = dotty
42 bool grid_hide_in_free_mode = false;
43 bool grid_snap_indicator = true;
44
45 int grid_ratio_high = 3; // custom ratio (high must be >= low)
46 int grid_ratio_low = 1; // (low must be > 0)
47
48
Grid_State_c()49 Grid_State_c::Grid_State_c() :
50 step(64 /* dummy */), snap(true),
51 ratio(0), shown(true),
52 orig_x(0.0), orig_y(0.0),
53 Scale(1.0)
54 { }
55
~Grid_State_c()56 Grid_State_c::~Grid_State_c()
57 { }
58
59
Init()60 void Grid_State_c::Init()
61 {
62 step = grid_default_size;
63
64 if (step < 1)
65 step = 1;
66
67 if (step > grid_values[0])
68 step = grid_values[0];
69
70 shown = true; // prevent a beep in AdjustStep
71
72 AdjustStep(+1);
73
74 if (grid_default_mode == 0)
75 {
76 shown = false;
77
78 if (main_win)
79 main_win->info_bar->SetGrid(-1);
80 }
81 else
82 {
83 shown = true;
84 }
85
86 snap = grid_default_snap;
87
88 if (main_win)
89 main_win->info_bar->UpdateSnap();
90 }
91
92
MoveTo(double x,double y)93 void Grid_State_c::MoveTo(double x, double y)
94 {
95 // no change?
96 if (fabs(x - orig_x) < 0.01 &&
97 fabs(y - orig_y) < 0.01)
98 return;
99
100 orig_x = x;
101 orig_y = y;
102
103 if (main_win)
104 {
105 main_win->scroll->AdjustPos();
106 main_win->canvas->PointerPos();
107
108 RedrawMap();
109 }
110 }
111
112
Scroll(double delta_x,double delta_y)113 void Grid_State_c::Scroll(double delta_x, double delta_y)
114 {
115 MoveTo(orig_x + delta_x, orig_y + delta_y);
116 }
117
118
ForceSnapX(double map_x) const119 int Grid_State_c::ForceSnapX(double map_x) const
120 {
121 return grid.step * round(map_x / (double)grid.step);
122 }
123
ForceSnapY(double map_y) const124 int Grid_State_c::ForceSnapY(double map_y) const
125 {
126 return grid.step * round(map_y / (double)grid.step);
127 }
128
129
SnapX(double map_x) const130 double Grid_State_c::SnapX(double map_x) const
131 {
132 if (! snap || grid.step == 0)
133 return map_x;
134
135 return ForceSnapX(map_x);
136 }
137
SnapY(double map_y) const138 double Grid_State_c::SnapY(double map_y) const
139 {
140 if (! snap || grid.step == 0)
141 return map_y;
142
143 return ForceSnapY(map_y);
144 }
145
146
RatioSnapXY(double & var_x,double & var_y,double start_x,double start_y) const147 void Grid_State_c::RatioSnapXY(double& var_x, double& var_y,
148 double start_x, double start_y) const
149 {
150 // snap first, otherwise we lose the ratio
151 var_x = grid.SnapX(var_x);
152 var_y = grid.SnapY(var_y);
153
154 double dx = var_x - start_x;
155 double dy = var_y - start_y;
156
157 double len = MAX(abs(dx), abs(dy));
158
159 int sign_x = (dx >= 0) ? +1 : -1;
160 int sign_y = (dy >= 0) ? +1 : -1;
161
162 double custom;
163
164 switch (ratio)
165 {
166 case 0: // unlocked
167 break;
168
169 case 1: // 1:1 (45 degrees) + axis aligned
170 if (fabs(dx) * 2 < fabs(dy))
171 {
172 var_x = start_x;
173 }
174 else if (fabs(dy) * 2 < fabs(dx))
175 {
176 var_y = start_y;
177 }
178 else
179 {
180 var_x = start_x + sign_x * len;
181 var_y = start_y + sign_y * len;
182 }
183 break;
184
185 case 2: // 2:1 + axis aligned
186 if (fabs(dx) * 4 < fabs(dy))
187 {
188 var_x = start_x;
189 }
190 else if (fabs(dy) * 4 < fabs(dx))
191 {
192 var_y = start_y;
193 }
194 else if (fabs(dx) < fabs(dy))
195 {
196 var_x = start_x + sign_x * len * 0.5;
197 var_y = start_y + sign_y * len;
198 }
199 else
200 {
201 var_x = start_x + sign_x * len;
202 var_y = start_y + sign_y * len * 0.5;
203 }
204 break;
205
206 case 3: // 4:1 + axis aligned
207 if (fabs(dx) * 8 < fabs(dy))
208 {
209 var_x = start_x;
210 }
211 else if (fabs(dy) * 8 < fabs(dx))
212 {
213 var_y = start_y;
214 }
215 else if (fabs(dx) < fabs(dy))
216 {
217 var_x = start_x + sign_x * len * 0.25;
218 var_y = start_y + sign_y * len;
219 }
220 else
221 {
222 var_x = start_x + sign_x * len;
223 var_y = start_y + sign_y * len * 0.25;
224 }
225 break;
226
227 case 4: // 8:1 + axis aligned
228 if (fabs(dx) * 16 < fabs(dy))
229 {
230 var_x = start_x;
231 }
232 else if (fabs(dy) * 16 < fabs(dx))
233 {
234 var_y = start_y;
235 }
236 else if (fabs(dx) < fabs(dy))
237 {
238 var_x = start_x + sign_x * len * 0.125;
239 var_y = start_y + sign_y * len;
240 }
241 else
242 {
243 var_x = start_x + sign_x * len;
244 var_y = start_y + sign_y * len * 0.125;
245 }
246 break;
247
248 case 5: // 5:4 + axis aligned
249 if (fabs(dx) * 3 < fabs(dy))
250 {
251 var_x = start_x;
252 }
253 else if (fabs(dy) * 3 < fabs(dx))
254 {
255 var_y = start_y;
256 }
257 else if (fabs(dx) < fabs(dy))
258 {
259 var_x = start_x + sign_x * len * 0.8;
260 var_y = start_y + sign_y * len;
261 }
262 else
263 {
264 var_x = start_x + sign_x * len;
265 var_y = start_y + sign_y * len * 0.8;
266 }
267 break;
268
269 case 6: // 7:4 + axis aligned
270 if (fabs(dx) * 3 < fabs(dy))
271 {
272 var_x = start_x;
273 }
274 else if (fabs(dy) * 3 < fabs(dx))
275 {
276 var_y = start_y;
277 }
278 else if (fabs(dx) < fabs(dy))
279 {
280 var_x = start_x + sign_x * len * 4 / 7;
281 var_y = start_y + sign_y * len;
282 }
283 else
284 {
285 var_x = start_x + sign_x * len;
286 var_y = start_y + sign_y * len * 4 / 7;
287 }
288 break;
289
290 default: // USER SETTING
291 if (grid_ratio_low < 1)
292 grid_ratio_low = 1;
293 if (grid_ratio_high < grid_ratio_low)
294 grid_ratio_high = grid_ratio_low;
295
296 custom = (double)grid_ratio_low / (double)grid_ratio_high;
297
298 if (custom > 0.1 && fabs(dx) < fabs(dy) * custom * 0.3)
299 {
300 var_x = start_x;
301 }
302 else if (custom > 0.1 && fabs(dy) < fabs(dx) * custom * 0.3)
303 {
304 var_y = start_y;
305 }
306 else if (fabs(dx) < fabs(dy))
307 {
308 var_x = start_x + sign_x * len * custom;
309 var_y = start_y + sign_y * len;
310 }
311 else
312 {
313 var_x = start_x + sign_x * len;
314 var_y = start_y + sign_y * len * custom;
315 }
316 }
317 }
318
319
QuantSnapX(double map_x,bool want_furthest,int * dir) const320 int Grid_State_c::QuantSnapX(double map_x, bool want_furthest, int *dir) const
321 {
322 if (OnGridX(map_x))
323 {
324 if (dir)
325 *dir = 0;
326 return map_x;
327 }
328
329 int new_x = ForceSnapX(map_x);
330
331 if (dir)
332 {
333 if (new_x < map_x)
334 *dir = -1;
335 else
336 *dir = +1;
337 }
338
339 if (! want_furthest)
340 return new_x;
341
342 if (new_x < map_x)
343 return ForceSnapX(map_x + (step - 1));
344 else
345 return ForceSnapX(map_x - (step - 1));
346 }
347
QuantSnapY(double map_y,bool want_furthest,int * dir) const348 int Grid_State_c::QuantSnapY(double map_y, bool want_furthest, int *dir) const
349 {
350 // this is sufficient since the grid is always square
351
352 return QuantSnapX(map_y, want_furthest, dir);
353 }
354
355
NaturalSnapXY(double & var_x,double & var_y) const356 void Grid_State_c::NaturalSnapXY(double& var_x, double& var_y) const
357 {
358 // this is only used by UI_Canvas::PointerPos()
359
360 double nat_step = 1.0;
361
362 while (nat_step * 2.0 <= Scale)
363 nat_step = nat_step * 2.0;
364
365 while (nat_step * 0.5 >= Scale)
366 nat_step = nat_step * 0.5;
367
368 var_x = round(var_x * nat_step) / nat_step;
369 var_y = round(var_y * nat_step) / nat_step;
370 }
371
372
OnGridX(double map_x) const373 bool Grid_State_c::OnGridX(double map_x) const
374 {
375 if (map_x < 0)
376 map_x = -map_x;
377
378 int map_x2 = (int)map_x;
379
380 if (map_x != (double)map_x2)
381 return false;
382
383 return (map_x2 % step) == 0;
384 }
385
OnGridY(double map_y) const386 bool Grid_State_c::OnGridY(double map_y) const
387 {
388 if (map_y < 0)
389 map_y = -map_y;
390
391 int map_y2 = (int)map_y;
392
393 if (map_y != (double)map_y2)
394 return false;
395
396 return (map_y2 % step) == 0;
397 }
398
OnGrid(double map_x,double map_y) const399 bool Grid_State_c::OnGrid(double map_x, double map_y) const
400 {
401 return OnGridX(map_x) && OnGridY(map_y);
402 }
403
404
RefocusZoom(double map_x,double map_y,float before_Scale)405 void Grid_State_c::RefocusZoom(double map_x, double map_y, float before_Scale)
406 {
407 double dist_factor = (1.0 - before_Scale / Scale);
408
409 orig_x += (map_x - orig_x) * dist_factor;
410 orig_y += (map_y - orig_y) * dist_factor;
411
412 if (main_win)
413 {
414 main_win->canvas->PointerPos();
415 RedrawMap();
416 }
417 }
418
419
420 const double Grid_State_c::scale_values[] =
421 {
422 32.0, 16.0, 8.0, 6.0, 4.0, 3.0, 2.0, 1.5, 1.0,
423
424 1.0 / 1.5, 1.0 / 2.0, 1.0 / 3.0, 1.0 / 4.0,
425 1.0 / 6.0, 1.0 / 8.0, 1.0 / 16.0, 1.0 / 32.0,
426 1.0 / 64.0
427 };
428
429
430 const int Grid_State_c::digit_scales[] =
431 {
432 1, 3, 5, 7, 9, 11, 13, 14, 15 /* index into scale_values[] */
433 };
434
435 const int Grid_State_c::grid_values[] =
436 {
437 1024, 512, 256, 192, 128, 64, 32, 16, 8, 4, 2,
438
439 -1 /* OFF */,
440 };
441
442 #define NUM_SCALE_VALUES 18
443 #define NUM_GRID_VALUES 12
444
445
RawSetScale(int i)446 void Grid_State_c::RawSetScale(int i)
447 {
448 SYS_ASSERT(0 <= i && i < NUM_SCALE_VALUES);
449
450 Scale = scale_values[i];
451
452 if (! main_win)
453 return;
454
455 main_win->scroll->AdjustPos();
456 main_win->canvas->PointerPos();
457 main_win->info_bar->SetScale(Scale);
458
459 RedrawMap();
460 }
461
462
RawSetStep(int i)463 void Grid_State_c::RawSetStep(int i)
464 {
465 SYS_ASSERT(0 <= i && i < NUM_GRID_VALUES);
466
467 if (i == NUM_GRID_VALUES-1) /* OFF */
468 {
469 shown = false;
470
471 if (main_win)
472 main_win->info_bar->SetGrid(-1);
473 }
474 else
475 {
476 shown = true;
477 step = grid_values[i];
478
479 if (main_win)
480 main_win->info_bar->SetGrid(step);
481 }
482
483 if (grid_hide_in_free_mode)
484 SetSnap(shown);
485
486 RedrawMap();
487 }
488
489
ForceStep(int new_step)490 void Grid_State_c::ForceStep(int new_step)
491 {
492 step = new_step;
493 shown = true;
494
495 if (main_win)
496 main_win->info_bar->SetGrid(step);
497
498 if (grid_hide_in_free_mode)
499 SetSnap(shown);
500
501 RedrawMap();
502 }
503
504
StepFromScale()505 void Grid_State_c::StepFromScale()
506 {
507 int pixels_min = 16;
508
509 int result = 0;
510
511 for (int i = 0 ; i < NUM_GRID_VALUES-1 ; i++)
512 {
513 result = i;
514
515 if (grid_values[i] * Scale / 2 < pixels_min)
516 break;
517 }
518
519 if (step == grid_values[result])
520 return; // no change
521
522 step = grid_values[result];
523
524 RedrawMap();
525 }
526
527
AdjustStep(int delta)528 void Grid_State_c::AdjustStep(int delta)
529 {
530 if (! shown)
531 {
532 Beep("Grid is off (cannot change step)");
533 return;
534 }
535
536 int result = -1;
537
538 if (delta > 0)
539 {
540 for (int i = NUM_GRID_VALUES-2 ; i >= 0 ; i--)
541 {
542 if (grid_values[i] > step)
543 {
544 result = i;
545 break;
546 }
547 }
548 }
549 else // (delta < 0)
550 {
551 for (int i = 0 ; i < NUM_GRID_VALUES-1 ; i++)
552 {
553 if (grid_values[i] < step)
554 {
555 result = i;
556 break;
557 }
558 }
559 }
560
561 // already at the extreme end?
562 if (result < 0)
563 return;
564
565 RawSetStep(result);
566 }
567
568
AdjustScale(int delta)569 void Grid_State_c::AdjustScale(int delta)
570 {
571 int result = -1;
572
573 if (delta > 0)
574 {
575 for (int i = NUM_SCALE_VALUES-1 ; i >= 0 ; i--)
576 {
577 if (scale_values[i] > Scale*1.01)
578 {
579 result = i;
580 break;
581 }
582 }
583 }
584 else // (delta < 0)
585 {
586 for (int i = 0 ; i < NUM_SCALE_VALUES ; i++)
587 {
588 if (scale_values[i] < Scale*0.99)
589 {
590 result = i;
591 break;
592 }
593 }
594 }
595
596 // already at the extreme end?
597 if (result < 0)
598 return;
599
600 RawSetScale(result);
601 }
602
603
RawSetShown(bool new_value)604 void Grid_State_c::RawSetShown(bool new_value)
605 {
606 shown = new_value;
607
608 if (! main_win)
609 return;
610
611 if (! shown)
612 {
613 main_win->info_bar->SetGrid(-1);
614 RedrawMap();
615 return;
616 }
617
618 // update the info-bar
619 main_win->info_bar->SetGrid(step);
620
621 RedrawMap();
622 }
623
624
SetShown(bool enable)625 void Grid_State_c::SetShown(bool enable)
626 {
627 RawSetShown(enable);
628
629 if (grid_hide_in_free_mode)
630 SetSnap(enable);
631 }
632
ToggleShown()633 void Grid_State_c::ToggleShown()
634 {
635 SetShown(!shown);
636 }
637
638
SetSnap(bool enable)639 void Grid_State_c::SetSnap(bool enable)
640 {
641 if (snap == enable)
642 return;
643
644 snap = enable;
645
646 if (grid_hide_in_free_mode && snap != shown)
647 SetShown(snap);
648
649 if (main_win)
650 main_win->info_bar->UpdateSnap();
651
652 RedrawMap();
653 }
654
ToggleSnap()655 void Grid_State_c::ToggleSnap()
656 {
657 SetSnap(! snap);
658 }
659
660
NearestScale(double want_scale)661 void Grid_State_c::NearestScale(double want_scale)
662 {
663 int best = 0;
664
665 for (int i = 0 ; i < NUM_SCALE_VALUES ; i++)
666 {
667 best = i;
668
669 if (scale_values[i] < want_scale * 1.1)
670 break;
671 }
672
673 RawSetScale(best);
674 }
675
676
Grid_ParseUser(const char ** tokens,int num_tok)677 bool Grid_ParseUser(const char ** tokens, int num_tok)
678 {
679 if (strcmp(tokens[0], "map_pos") == 0 && num_tok >= 4)
680 {
681 double x = atof(tokens[1]);
682 double y = atof(tokens[2]);
683
684 grid.MoveTo(x, y);
685
686 double new_scale = atof(tokens[3]);
687
688 grid.NearestScale(new_scale);
689
690 RedrawMap();
691 return true;
692 }
693
694 if (strcmp(tokens[0], "grid") == 0 && num_tok >= 4)
695 {
696 bool t_shown = atoi(tokens[1]) ? true : false;
697
698 grid.step = atoi(tokens[3]);
699
700 // tokens[2] was grid.mode, currently unused
701
702 grid.RawSetShown(t_shown);
703
704 RedrawMap();
705
706 return true;
707 }
708
709 if (strcmp(tokens[0], "snap") == 0 && num_tok >= 2)
710 {
711 grid.snap = atoi(tokens[1]) ? true : false;
712
713 if (main_win)
714 main_win->info_bar->UpdateSnap();
715
716 return true;
717 }
718
719 return false;
720 }
721
722
Grid_WriteUser(FILE * fp)723 void Grid_WriteUser(FILE *fp)
724 {
725 fprintf(fp, "map_pos %1.0f %1.0f %1.6f\n",
726 grid.orig_x,
727 grid.orig_y,
728 grid.Scale);
729
730 fprintf(fp, "grid %d %d %d\n",
731 grid.shown ? 1 : 0,
732 grid_style ? 0 : 1, /* was grid.mode, now unused */
733 grid.step);
734
735 fprintf(fp, "snap %d\n",
736 grid.snap ? 1 : 0);
737 }
738
739
740 //--- editor settings ---
741 // vi:ts=4:sw=4:noexpandtab
742