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