1 //------------------------------------------------------------------------
2 //  EDITING CANVAS
3 //------------------------------------------------------------------------
4 //
5 //  Eureka DOOM Editor
6 //
7 //  Copyright (C) 2006-2019 Andrew Apted
8 //
9 //  This program is free software; you can redistribute it and/or
10 //  modify it under the terms of the GNU General Public License
11 //  as published by the Free Software Foundation; either version 2
12 //  of the License, or (at your option) any later version.
13 //
14 //  This program is distributed in the hope that it will be useful,
15 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 //  GNU General Public License for more details.
18 //
19 //------------------------------------------------------------------------
20 
21 #include "main.h"
22 
23 #include <algorithm>
24 
25 #ifndef NO_OPENGL
26 #include "FL/gl.h"
27 #endif
28 
29 #include "ui_window.h"
30 
31 #include "m_events.h"
32 #include "e_main.h"
33 #include "e_hover.h"
34 #include "e_linedef.h"
35 #include "e_sector.h"
36 #include "e_things.h"
37 #include "e_path.h"	  // SoundPropagation
38 #include "im_color.h"
39 #include "im_img.h"
40 #include "m_config.h"
41 #include "m_game.h"
42 #include "r_grid.h"
43 #include "r_subdiv.h"
44 #include "r_render.h"
45 #include "w_rawdef.h"	// MLF_xxx
46 #include "w_texture.h"
47 
48 
49 #define CAMERA_COLOR  fl_rgb_color(255, 192, 255)
50 
51 
52 typedef enum
53 {
54 	LINFO_Nothing = 0,
55 	LINFO_Length,
56 	LINFO_Angle,
57 	LINFO_Ratio,
58 	LINFO_Length_Angle,
59 	LINFO_Length_Ratio
60 
61 } line_info_mode_e;
62 
63 
64 // config items
65 rgb_color_t dotty_axis_col  = RGB_MAKE(0, 128, 255);
66 rgb_color_t dotty_major_col = RGB_MAKE(0, 0, 238);
67 rgb_color_t dotty_minor_col = RGB_MAKE(0, 0, 187);
68 rgb_color_t dotty_point_col = RGB_MAKE(0, 0, 255);
69 
70 rgb_color_t normal_axis_col  = RGB_MAKE(0, 128, 255);
71 rgb_color_t normal_main_col  = RGB_MAKE(0, 0, 238);
72 rgb_color_t normal_flat_col  = RGB_MAKE(60, 60, 120);
73 rgb_color_t normal_small_col = RGB_MAKE(60, 60, 120);
74 
75 int highlight_line_info = (int)LINFO_Length;
76 
77 
78 int vertex_radius(double scale);
79 
80 
UI_Canvas(int X,int Y,int W,int H,const char * label)81 UI_Canvas::UI_Canvas(int X, int Y, int W, int H, const char *label) :
82 #ifdef NO_OPENGL
83 	Fl_Widget(X, Y, W, H, label),
84 #else
85 	Fl_Gl_Window(X, Y, W, H),
86 #endif
87 	last_highlight(),
88 	last_splitter(-1),
89 	last_split_x(), last_split_y(),
90 	snap_x(-1), snap_y(-1),
91 	seen_sectors()
92 {
93 #ifdef NO_OPENGL
94 	rgb_buf = NULL;
95 #endif
96 }
97 
98 
~UI_Canvas()99 UI_Canvas::~UI_Canvas()
100 { }
101 
102 
DeleteContext()103 void UI_Canvas::DeleteContext()
104 {
105 #ifndef NO_OPENGL
106 	context(NULL, 0);
107 
108 	// ensure W_UnloadAllTextures() gets called on next draw()
109 	invalidate();
110 #endif
111 }
112 
113 
resize(int X,int Y,int W,int H)114 void UI_Canvas::resize(int X, int Y, int W, int H)
115 {
116 #ifdef NO_OPENGL
117 	Fl_Widget::resize(X, Y, W, H);
118 #else
119 	Fl_Gl_Window::resize(X, Y, W, H);
120 #endif
121 }
122 
123 
draw()124 void UI_Canvas::draw()
125 {
126 #ifndef NO_OPENGL
127 	if (! valid())
128 	{
129 		// reset the 'gl_tex' field of all loaded images, as the value
130 		// belongs to a context which was (probably) just deleted and
131 		// hence refer to textures which no longer exist.
132 		W_UnloadAllTextures();
133 	}
134 #endif
135 
136 	if (edit.render3d)
137 	{
138 		Render3D_Draw(x(), y(), w(), h());
139 		return;
140 	}
141 
142 #ifdef NO_OPENGL
143 	xx = x();
144 	yy = y();
145 
146 	map_lx = floor(MAPX(xx));
147 	map_ly = floor(MAPY(yy + h()));
148 
149 	map_hx = ceil(MAPX(xx + w()));
150 	map_hy = ceil(MAPY(yy));
151 
152 #else // OpenGL
153 	xx = yy = 0;
154 
155 	map_lx = floor(MAPX(0));
156 	map_ly = floor(MAPY(0));
157 
158 	map_hx = ceil(MAPX(w()));
159 	map_hy = ceil(MAPY(h()));
160 
161 	// setup projection matrix for 2D drawing
162 
163 	// Note: this crud is a workaround for retina displays on MacOS
164 	Fl::use_high_res_GL(true);
165 	int pix = I_ROUND(main_win->canvas->pixels_per_unit());
166 	Fl::use_high_res_GL(false);
167 
168 	glLoadIdentity();
169 	glViewport(0, 0, w() * pix, h() * pix);
170 	glOrtho(0, w(), 0, h(), -1, 1);
171 #endif
172 
173 	PrepareToDraw();
174 
175 	RenderColor(FL_WHITE);
176 	RenderThickness(1);
177 
178 	// default font (for showing object numbers)
179 	int font_size = (grid.Scale < 0.9) ? 14 : 19;
180 	RenderFontSize(font_size);
181 
182 	DrawEverything();
183 
184 	Blit();
185 }
186 
187 
handle(int event)188 int UI_Canvas::handle(int event)
189 {
190 	if (EV_HandleEvent(event))
191 		return 1;
192 
193 	return Fl_Widget::handle(event);
194 }
195 
196 
NORMALX(int len,double dx,double dy)197 int UI_Canvas::NORMALX(int len, double dx, double dy)
198 {
199 #ifdef NO_OPENGL
200 	double res = -dy;
201 #else
202 	double res = dy;
203 #endif
204 
205 	double got_len = hypotf(dx, dy);
206 	if (got_len < 0.01)
207 		return 0;
208 
209 	return I_ROUND(res * len / got_len);
210 }
211 
NORMALY(int len,double dx,double dy)212 int UI_Canvas::NORMALY(int len, double dx, double dy)
213 {
214 #ifdef NO_OPENGL
215 	double res = dx;
216 #else
217 	double res = -dx;
218 #endif
219 
220 	double got_len = hypotf(dx, dy);
221 	if (got_len < 0.01)
222 		return 0;
223 
224 	return I_ROUND(res * len / got_len);
225 }
226 
227 
PointerPos(bool in_event)228 void UI_Canvas::PointerPos(bool in_event)
229 {
230 	if (edit.render3d)
231 		return;
232 
233 	// we read current position outside of FLTK's event propagation.
234 	int raw_x, raw_y;
235 	Fl::get_mouse(raw_x, raw_y);
236 
237 #ifdef NO_OPENGL
238 	raw_x -= main_win->x_root();
239 	raw_y -= main_win->y_root();
240 
241 	edit.map_x = MAPX(raw_x);
242 	edit.map_y = MAPY(raw_y);
243 
244 #else // OpenGL
245 	raw_x -= x_root();
246 	raw_y -= y_root();
247 
248 	edit.map_x = MAPX(raw_x);
249 	edit.map_y = MAPY(h() - 1 - raw_y);
250 #endif
251 
252 	grid.NaturalSnapXY(edit.map_x, edit.map_y);
253 
254 	// no Z coord with the 2D map view
255 	edit.map_z = -1;
256 }
257 
258 
ApproxBoxSize(int mx1,int my1,int mx2,int my2)259 int UI_Canvas::ApproxBoxSize(int mx1, int my1, int mx2, int my2)
260 {
261 	if (mx2 < mx1) std::swap(mx1, mx2);
262 	if (my2 < my1) std::swap(my1, my2);
263 
264 	int x1 = SCREENX(mx1);
265 	int x2 = SCREENX(mx2);
266 
267 	int y1 = SCREENY(my2);
268 	int y2 = SCREENY(my1);
269 
270 	if (x1 < 8 || x2 > w() - 8 ||
271 		y1 < 8 || y2 > h() - 8)
272 		return 1; // too big
273 
274 	float x_ratio = MAX(4, x2 - x1) / (float) MAX(4, w());
275 	float y_ratio = MAX(4, y2 - y1) / (float) MAX(4, h());
276 
277 	if (MAX(x_ratio, y_ratio) < 0.25)
278 		return -1;  // too small
279 
280 	return 0;
281 }
282 
283 
284 //------------------------------------------------------------------------
285 
286 
DrawEverything()287 void UI_Canvas::DrawEverything()
288 {
289 	// setup for drawing sector numbers
290 	if (edit.show_object_numbers && edit.mode == OBJ_SECTORS)
291 	{
292 		seen_sectors.clear_all();
293 	}
294 
295 	DrawMap();
296 
297 	DrawSelection(edit.Selected);
298 
299 	if (edit.action == ACT_DRAG && !edit.dragged.valid() && edit.drag_lines != NULL)
300 		DrawSelection(edit.drag_lines);
301 	else if (edit.action == ACT_TRANSFORM && edit.trans_lines != NULL)
302 		DrawSelection(edit.trans_lines);
303 
304 	if (edit.action == ACT_DRAG && edit.dragged.valid())
305 	{
306 		double dx = 0;
307 		double dy = 0;
308 		DragDelta(&dx, &dy);
309 
310 		if (edit.mode == OBJ_VERTICES)
311 			RenderColor(HI_AND_SEL_COL);
312 		else
313 			RenderColor(HI_COL);
314 
315 		if (edit.mode == OBJ_LINEDEFS || edit.mode == OBJ_SECTORS)
316 			RenderThickness(2);
317 
318 		DrawHighlight(edit.mode, edit.dragged.num, false /* skip_lines */, dx, dy);
319 
320 		if (edit.mode == OBJ_VERTICES && edit.highlight.valid())
321 		{
322 			RenderColor(HI_COL);
323 			DrawHighlight(edit.highlight.type, edit.highlight.num);
324 		}
325 
326 		RenderThickness(1);
327 
328 		// when ratio lock is on, want to see the new line
329 		if (edit.mode == OBJ_VERTICES && grid.ratio > 0 && edit.drag_other_vert >= 0)
330 		{
331 			const Vertex *v0 = Vertices[edit.drag_other_vert];
332 			const Vertex *v1 = Vertices[edit.dragged.num];
333 
334 			RenderColor(RED);
335 			DrawKnobbyLine(v0->x(), v0->y(), v1->x() + dx, v1->y() + dy);
336 
337 			DrawLineInfo(v0->x(), v0->y(), v1->x() + dx, v1->y() + dy, true);
338 		}
339 	}
340 	else if (edit.highlight.valid())
341 	{
342 		if (edit.action != ACT_DRAW_LINE && edit.Selected->get(edit.highlight.num))
343 			RenderColor(HI_AND_SEL_COL);
344 		else
345 			RenderColor(HI_COL);
346 
347 		if (edit.highlight.type == OBJ_LINEDEFS || edit.highlight.type == OBJ_SECTORS)
348 			RenderThickness(2);
349 
350 		DrawHighlight(edit.highlight.type, edit.highlight.num);
351 
352 		if (! edit.error_mode)
353 		{
354 			RenderColor(LIGHTRED);
355 			DrawTagged(edit.highlight.type, edit.highlight.num);
356 		}
357 
358 		if (edit.mode == OBJ_LINEDEFS && !edit.show_object_numbers)
359 		{
360 			const LineDef *L = LineDefs[edit.highlight.num];
361 			DrawLineInfo(L->Start()->x(), L->Start()->y(), L->End()->x(), L->End()->y(), false);
362 		}
363 
364 		RenderThickness(1);
365 	}
366 
367 	if (edit.action == ACT_SELBOX)
368 		SelboxDraw();
369 
370 	if (edit.action == ACT_DRAW_LINE)
371 		DrawCurrentLine();
372 }
373 
374 
375 //
376 // draw the whole map, except for hilight/selection/selbox
377 //
DrawMap()378 void UI_Canvas::DrawMap()
379 {
380 	RenderColor(FL_BLACK);
381 	RenderRect(xx, yy, w(), h());
382 
383 	if (edit.sector_render_mode && ! edit.error_mode)
384 	{
385 		for (int n = 0 ; n < NumSectors ; n++)
386 			RenderSector(n);
387 	}
388 
389 	// draw the grid first since it's in the background
390 	if (grid.shown)
391 	{
392 		if (grid_style == 0)
393 			DrawGrid_Normal();
394 		else
395 			DrawGrid_Dotty();
396 	}
397 
398 	if (Debugging)
399 		DrawMapBounds();
400 
401 	DrawCamera();
402 
403 	if (edit.mode != OBJ_THINGS)
404 		DrawThings();
405 
406 	if (grid.snap && grid_snap_indicator)
407 		DrawSnapPoint();
408 
409 	DrawLinedefs();
410 
411 	if (edit.mode == OBJ_VERTICES)
412 		DrawVertices();
413 
414 	if (edit.mode == OBJ_THINGS)
415 	{
416 		if (edit.thing_render_mode > 0)
417 		{
418 			DrawThings();
419 			DrawThingSprites();
420 		}
421 		else
422 		{
423 			DrawThingBodies();
424 			DrawThings();
425 		}
426 	}
427 }
428 
429 
430 //
431 //  draw the grid in the background of the edit window
432 //
DrawGrid_Normal()433 void UI_Canvas::DrawGrid_Normal()
434 {
435 	float pixels_1 = grid.step * grid.Scale;
436 
437 	if (pixels_1 < 1.6)
438 	{
439 		RenderColor(DarkerColor(DarkerColor(normal_main_col)));
440 		RenderRect(xx, yy, w(), h());
441 
442 		DrawAxes(normal_axis_col);
443 		return;
444 	}
445 
446 
447 	int flat_step = 64;
448 
449 	float pixels_2 = flat_step * grid.Scale;
450 
451 	Fl_Color flat_col = (grid.step < 64) ? normal_main_col : normal_flat_col;
452 
453 	if (pixels_2 < 2.2)
454 		flat_col = DarkerColor(flat_col);
455 
456 	RenderColor(flat_col);
457 
458 	if (pixels_2 < 1.6)
459 	{
460 		RenderRect(xx, yy, w(), h());
461 	}
462 	else
463 	{
464 		int gx = floor(map_lx / flat_step) * flat_step;
465 
466 		for (; gx <= map_hx; gx += flat_step)
467 			DrawMapLine(gx, map_ly, gx, map_hy);
468 
469 		int gy = floor(map_ly / flat_step) * flat_step;
470 
471 		for (; gy <= map_hy; gy += flat_step)
472 			DrawMapLine(map_lx, gy, map_hx, gy);
473 	}
474 
475 
476 	Fl_Color main_col = (grid.step < 64) ? normal_small_col : normal_main_col;
477 
478 	float pixels_3 = grid.step * grid.Scale;
479 
480 	if (pixels_3 < 4.2)
481 		main_col = DarkerColor(main_col);
482 
483 	RenderColor(main_col);
484 
485 	{
486 		int gx = floor(map_lx / grid.step) * grid.step;
487 
488 		for (; gx <= map_hx; gx += grid.step)
489 			if ((grid.step >= 64 || (gx & 63) != 0) && (gx != 0))
490 				DrawMapLine(gx, map_ly, gx, map_hy);
491 
492 		int gy = floor(map_ly / grid.step) * grid.step;
493 
494 		for (; gy <= map_hy; gy += grid.step)
495 			if ((grid.step >= 64 || (gy & 63) != 0) && (gy != 0))
496 				DrawMapLine(map_lx, gy, map_hx, gy);
497 	}
498 
499 
500 	DrawAxes(normal_axis_col);
501 }
502 
503 
DrawGrid_Dotty()504 void UI_Canvas::DrawGrid_Dotty()
505 {
506 	int grid_step_1 = 1 * grid.step;    // Map units between dots
507 	int grid_step_2 = 8 * grid_step_1;  // Map units between dim lines
508 	int grid_step_3 = 8 * grid_step_2;  // Map units between bright lines
509 
510 	float pixels_1 = grid.step * grid.Scale;
511 
512 
513 	if (pixels_1 < 1.6)
514 	{
515 		RenderColor(DarkerColor(DarkerColor(dotty_point_col)));
516 		RenderRect(xx, yy, w(), h());
517 
518 		DrawAxes(dotty_axis_col);
519 		return;
520 	}
521 
522 
523 	RenderColor(dotty_major_col);
524 	{
525 		int gx = floor(map_lx / grid_step_3) * grid_step_3;
526 
527 		for (; gx <= map_hx; gx += grid_step_3)
528 			DrawMapLine(gx, map_ly-2, gx, map_hy+2);
529 
530 		int gy = floor(map_ly / grid_step_3) * grid_step_3;
531 
532 		for (; gy <= map_hy; gy += grid_step_3)
533 			DrawMapLine(map_lx, gy, map_hx, gy);
534 	}
535 
536 
537 	DrawAxes(dotty_axis_col);
538 
539 
540 	RenderColor(dotty_minor_col);
541 	{
542 		int gx = floor(map_lx / grid_step_2) * grid_step_2;
543 
544 		for (; gx <= map_hx; gx += grid_step_2)
545 			if (gx % grid_step_3 != 0)
546 				DrawMapLine(gx, map_ly, gx, map_hy);
547 
548 		int gy = floor(map_ly / grid_step_2) * grid_step_2;
549 
550 		for (; gy <= map_hy; gy += grid_step_2)
551 			if (gy % grid_step_3 != 0)
552 				DrawMapLine(map_lx, gy, map_hx, gy);
553 	}
554 
555 
556 	if (pixels_1 < 4.02)
557 		RenderColor(DarkerColor(dotty_point_col));
558 	else
559 		RenderColor(dotty_point_col);
560 
561 	{
562 		int gx = floor(map_lx / grid_step_1) * grid_step_1;
563 		int gy = floor(map_ly / grid_step_1) * grid_step_1;
564 
565 		for (int ny = gy; ny <= map_hy; ny += grid_step_1)
566 		for (int nx = gx; nx <= map_hx; nx += grid_step_1)
567 		{
568 			int sx = SCREENX(nx);
569 			int sy = SCREENY(ny);
570 
571 			if (pixels_1 < 24.1)
572 				RenderRect(sx, sy, 1, 1);
573 			else
574 				RenderRect(sx, sy, 2, 2);
575 		}
576 	}
577 }
578 
579 
DrawAxes(Fl_Color col)580 void UI_Canvas::DrawAxes(Fl_Color col)
581 {
582 	RenderColor(col);
583 
584 	DrawMapLine(0, map_ly, 0, map_hy);
585 	DrawMapLine(map_lx, 0, map_hx, 0);
586 }
587 
588 
DrawMapBounds()589 void UI_Canvas::DrawMapBounds()
590 {
591 	RenderColor(FL_RED);
592 
593 	DrawMapLine(Map_bound_x1, Map_bound_y1, Map_bound_x2, Map_bound_y1);
594 	DrawMapLine(Map_bound_x1, Map_bound_y2, Map_bound_x2, Map_bound_y2);
595 
596 	DrawMapLine(Map_bound_x1, Map_bound_y1, Map_bound_x1, Map_bound_y2);
597 	DrawMapLine(Map_bound_x2, Map_bound_y1, Map_bound_x2, Map_bound_y2);
598 }
599 
600 
601 //
602 //  the apparent radius of a vertex, in pixels
603 //
vertex_radius(double scale)604 int vertex_radius(double scale)
605 {
606 	int r = 6 * (0.26 + scale / 2);
607 
608 	if (r > 12) r = 12;
609 
610 	return r;
611 }
612 
613 
614 
615 //
616 //  draw the vertices, and possibly their numbers
617 //
DrawVertex(double map_x,double map_y,int r)618 void UI_Canvas::DrawVertex(double map_x, double map_y, int r)
619 {
620 	int scrx = SCREENX(map_x);
621 	int scry = SCREENY(map_y);
622 
623 // BLOBBY TEST
624 #if 0
625 	RenderLine(scrx - 1, scry - 2, scrx + 1, scry - 2);
626 	RenderLine(scrx - 2, scry - 1, scrx + 2, scry - 1);
627 	RenderLine(scrx - 2, scry + 0, scrx + 2, scry + 0);
628 	RenderLine(scrx - 2, scry + 1, scrx + 2, scry + 1);
629 	RenderLine(scrx - 1, scry + 2, scrx + 1, scry + 2);
630 #else
631 	RenderLine(scrx - r, scry - r, scrx + r, scry + r);
632 	RenderLine(scrx + r, scry - r, scrx - r, scry + r);
633 
634 	RenderLine(scrx - 1, scry, scrx + 1, scry);
635 	RenderLine(scrx, scry - 1, scrx, scry + 1);
636 #endif
637 }
638 
639 
DrawVertices()640 void UI_Canvas::DrawVertices()
641 {
642 	const int r = vertex_radius(grid.Scale);
643 
644 	RenderColor(FL_GREEN);
645 
646 	for (int n = 0 ; n < NumVertices ; n++)
647 	{
648 		double x = Vertices[n]->x();
649 		double y = Vertices[n]->y();
650 
651 		if (Vis(x, y, r))
652 		{
653 			DrawVertex(x, y, r);
654 		}
655 	}
656 
657 	if (edit.show_object_numbers)
658 	{
659 		for (int n = 0 ; n < NumVertices ; n++)
660 		{
661 			double x = Vertices[n]->x();
662 			double y = Vertices[n]->y();
663 
664 			if (! Vis(x, y, r))
665 				continue;
666 
667 			int sx = SCREENX(x) + r * 3;
668 			int sy = SCREENY(y) + r * 3;
669 
670 			DrawNumber(sx, sy, n);
671 		}
672 	}
673 }
674 
675 
676 //
677 //  draw all the linedefs
678 //
DrawLinedefs()679 void UI_Canvas::DrawLinedefs()
680 {
681 	for (int n = 0 ; n < NumLineDefs ; n++)
682 	{
683 		const LineDef *L = LineDefs[n];
684 
685 		double x1 = L->Start()->x();
686 		double y1 = L->Start()->y();
687 		double x2 = L->End  ()->x();
688 		double y2 = L->End  ()->y();
689 
690 		if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
691 			continue;
692 
693 		bool one_sided = (! L->Left());
694 
695 		Fl_Color col = LIGHTGREY;
696 
697 		// 'p' for plain, 'k' for knobbly, 's' for split
698 		char line_kind = 'p';
699 
700 		switch (edit.mode)
701 		{
702 			case OBJ_VERTICES:
703 			{
704 				if (n == edit.split_line.num)
705 					col = HI_AND_SEL_COL;
706 				else if (edit.error_mode)
707 					col = LIGHTGREY;
708 				else if (L->right < 0)
709 					col = RED;
710 				else if (one_sided)
711 					col = WHITE;
712 
713 				if (n == edit.split_line.num)
714 					line_kind = 's';
715 				else
716 					line_kind = 'k';
717 
718 				// show info of last four added lines
719 				if (n != edit.split_line.num && n >= (NumLineDefs - 4) &&
720 					!edit.show_object_numbers)
721 				{
722 					DrawLineInfo(x1, y1, x2, y2, false);
723 				}
724 			}
725 			break;
726 
727 			case OBJ_LINEDEFS:
728 			{
729 				if (edit.error_mode)
730 					col = LIGHTGREY;
731 				else if (! L->Right()) // no first sidedef?
732 					col = RED;
733 				else if (L->type != 0)
734 				{
735 					if (L->tag != 0)
736 						col = LIGHTMAGENTA;
737 					else
738 						col = LIGHTGREEN;
739 				}
740 				else if (one_sided)
741 					col = WHITE;
742 				else if (L->flags & MLF_Blocking)
743 					col = FL_CYAN;
744 
745 				line_kind = 'k';
746 			}
747 			break;
748 
749 			case OBJ_SECTORS:
750 			{
751 				int sd1 = L->right;
752 				int sd2 = L->left;
753 
754 				int s1  = (sd1 < 0) ? NIL_OBJ : SideDefs[sd1]->sector;
755 				int s2  = (sd2 < 0) ? NIL_OBJ : SideDefs[sd2]->sector;
756 
757 				if (edit.error_mode)
758 					col = LIGHTGREY;
759 				else if (sd1 < 0)
760 					col = RED;
761 				else if (edit.sector_render_mode == SREND_SoundProp)
762 				{
763 					if (L->flags & MLF_SoundBlock)
764 						col = FL_MAGENTA;
765 					else if (one_sided)
766 						col = WHITE;
767 				}
768 				else
769 				{
770 					bool have_tag  = false;
771 					bool have_type = false;
772 
773 					if (Sectors[s1]->tag != 0)
774 						have_tag = true;
775 					if (Sectors[s1]->type != 0)
776 						have_type = true;
777 
778 					if (s2 >= 0)
779 					{
780 						if (Sectors[s2]->tag != 0)
781 							have_tag = true;
782 
783 						if (Sectors[s2]->type != 0)
784 							have_type = true;
785 					}
786 
787 					if (have_tag && have_type)
788 						col = SECTOR_TAGTYPE;
789 					else if (have_tag)
790 						col = SECTOR_TAG;
791 					else if (have_type)
792 						col = SECTOR_TYPE;
793 					else if (one_sided)
794 						col = WHITE;
795 				}
796 
797 				if (edit.show_object_numbers)
798 				{
799 					if (s1 != NIL_OBJ)
800 						DrawSectorNum(x1, y1, x2, y2, SIDE_RIGHT, s1);
801 
802 					if (s2 != NIL_OBJ)
803 						DrawSectorNum(x1, y1, x2, y2, SIDE_LEFT,  s2);
804 				}
805 			}
806 			break;
807 
808 			// OBJ_THINGS
809 			default:
810 			{
811 				if (one_sided && ! edit.error_mode)
812 					col = WHITE;
813 			}
814 			break;
815 		}
816 
817 		RenderColor(col);
818 
819 		switch (line_kind)
820 		{
821 			case 'p':
822 				DrawMapLine(x1, y1, x2, y2);
823 				break;
824 
825 			case 'k':
826 				DrawKnobbyLine(x1, y1, x2, y2);
827 				break;
828 
829 			case 's':
830 				DrawSplitLine(x1, y1, x2, y2);
831 				break;
832 		}
833 	}
834 
835 	// draw the linedef numbers
836 	if (edit.mode == OBJ_LINEDEFS && edit.show_object_numbers)
837 	{
838 		for (int n = 0 ; n < NumLineDefs ; n++)
839 		{
840 			double x1 = LineDefs[n]->Start()->x();
841 			double y1 = LineDefs[n]->Start()->y();
842 			double x2 = LineDefs[n]->End  ()->x();
843 			double y2 = LineDefs[n]->End  ()->y();
844 
845 			if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
846 				continue;
847 
848 			DrawLineNumber(x1, y1, x2, y2, 0, n);
849 		}
850 	}
851 }
852 
853 
DrawThing(double x,double y,int r,int angle,bool big_arrow)854 void UI_Canvas::DrawThing(double x, double y, int r, int angle, bool big_arrow)
855 {
856 	DrawMapLine(x-r, y-r, x-r, y+r);
857 	DrawMapLine(x-r, y+r, x+r, y+r);
858 	DrawMapLine(x+r, y+r, x+r, y-r);
859 	DrawMapLine(x+r, y-r, x-r, y-r);
860 
861 	if (big_arrow)
862 	{
863 		DrawMapArrow(x, y, r * 2, angle);
864 	}
865 	else
866 	{
867 		int dir = angle_to_direction(angle);
868 
869 		static const short xsign[] = {  1,  1,  0, -1, -1, -1,  0,  1,  0 };
870 		static const short ysign[] = {  0,  1,  1,  1,  0, -1, -1, -1,  0 };
871 
872 		int corner_x = r * xsign[dir];
873 		int corner_y = r * ysign[dir];
874 
875 		DrawMapLine(x, y, x + corner_x, y + corner_y);
876 	}
877 }
878 
879 
880 //
881 //  draw things as squares (outlines)
882 //
DrawThings()883 void UI_Canvas::DrawThings()
884 {
885 	if (edit.mode != OBJ_THINGS)
886 		RenderColor(DARKGREY);
887 	else if (edit.error_mode)
888 		RenderColor(LIGHTGREY);
889 
890 	for (int n = 0 ; n < NumThings ; n++)
891 	{
892 		double x = Things[n]->x();
893 		double y = Things[n]->y();
894 
895 		if (! Vis(x, y, MAX_RADIUS))
896 			continue;
897 
898 		const thingtype_t *info = M_GetThingType(Things[n]->type);
899 
900 		if (edit.mode == OBJ_THINGS && !edit.error_mode)
901 		{
902 			Fl_Color col = (Fl_Color)info->color;
903 			RenderColor(col);
904 		}
905 
906 		int r = info->radius;
907 
908 		DrawThing(x, y, r, Things[n]->angle, false);
909 	}
910 
911 	// draw the thing numbers
912 	if (edit.mode == OBJ_THINGS && edit.show_object_numbers)
913 	{
914 		for (int n = 0 ; n < NumThings ; n++)
915 		{
916 			double x = Things[n]->x();
917 			double y = Things[n]->y();
918 
919 			if (! Vis(x, y, MAX_RADIUS))
920 				continue;
921 
922 			const thingtype_t *info = M_GetThingType(Things[n]->type);
923 
924 			x += info->radius + 8;
925 			y += info->radius + 8;
926 
927 			DrawNumber(SCREENX(x), SCREENY(y), n);
928 		}
929 	}
930 }
931 
932 
933 //
934 //  draw bodies of things (solid boxes, darker than the outline)
935 //
DrawThingBodies()936 void UI_Canvas::DrawThingBodies()
937 {
938 	if (edit.error_mode)
939 		return;
940 
941 	for (int n = 0 ; n < NumThings ; n++)
942 	{
943 		double x = Things[n]->x();
944 		double y = Things[n]->y();
945 
946 		if (! Vis(x, y, MAX_RADIUS))
947 			continue;
948 
949 		const thingtype_t *info = M_GetThingType(Things[n]->type);
950 
951 		Fl_Color col = (Fl_Color)info->color;
952 		RenderColor(DarkerColor(DarkerColor(col)));
953 
954 		int r = info->radius;
955 
956 		int sx1 = SCREENX(x - r);
957 		int sy1 = SCREENY(y + r);
958 		int sx2 = SCREENX(x + r);
959 		int sy2 = SCREENY(y - r);
960 
961 		RenderRect(sx1, sy1, sx2 - sx1 + 1, sy2 - sy1 + 1);
962 	}
963 }
964 
965 
DrawThingSprites()966 void UI_Canvas::DrawThingSprites()
967 {
968 #ifndef NO_OPENGL
969 	glEnable(GL_TEXTURE_2D);
970 	glEnable(GL_ALPHA_TEST);
971 
972 	glAlphaFunc(GL_GREATER, 0.5);
973 #endif
974 
975 	for (int n = 0 ; n < NumThings ; n++)
976 	{
977 		double x = Things[n]->x();
978 		double y = Things[n]->y();
979 
980 		if (! Vis(x, y, MAX_RADIUS))
981 			continue;
982 
983 		const thingtype_t *info = M_GetThingType(Things[n]->type);
984 		float scale = info->scale;
985 
986 		Img_c *sprite = W_GetSprite(Things[n]->type);
987 
988 		if (! sprite)
989 		{
990 			sprite = IM_UnknownSprite();
991 			scale = 0.66;
992 		}
993 
994 		int sx = SCREENX(x);
995 		int sy = SCREENY(y);
996 
997 		RenderSprite(sx, sy, scale * grid.Scale, sprite);
998 	}
999 
1000 #ifndef NO_OPENGL
1001 	glDisable(GL_ALPHA_TEST);
1002 	glDisable(GL_TEXTURE_2D);
1003 #endif
1004 }
1005 
1006 
RenderSprite(int sx,int sy,float scale,Img_c * img)1007 void UI_Canvas::RenderSprite(int sx, int sy, float scale, Img_c *img)
1008 {
1009 	int W = img->width();
1010 	int H = img->height();
1011 
1012 	scale = scale * 0.5;
1013 
1014 #ifdef NO_OPENGL
1015 	// software rendering
1016 
1017 	int bx1 = sx + (int)floor(-W * scale) - rgb_x;
1018 	int bx2 = sx + (int)ceil ( W * scale) - rgb_x;
1019 
1020 	int by1 = sy + (int)floor(-H * scale) - rgb_y;
1021 	int by2 = sy + (int)ceil ( H * scale) - rgb_y;
1022 
1023 	// prevent division by zero
1024 	if (bx2 <= bx1) bx2 = bx1 + 1;
1025 	if (by2 <= by1) by2 = by1 + 1;
1026 
1027 	// clip to screen
1028 	int rx1 = MAX(bx1, 0);
1029 	int ry1 = MAX(by1, 0);
1030 
1031 	int rx2 = MIN(bx2, rgb_w) - 1;
1032 	int ry2 = MIN(by2, rgb_h) - 1;
1033 
1034 	if (rx1 >= rx2 || ry1 >= ry2)
1035 		return;
1036 
1037 	for (int ry = ry1 ; ry <= ry2 ; ry++)
1038 	{
1039 		byte *dest = rgb_buf + 3 * (rx1 + ry * rgb_w);
1040 
1041 		for (int rx = rx1 ; rx <= rx2 ; rx++, dest += 3)
1042 		{
1043 			int ix = W * (rx - bx1) / (bx2 - bx1);
1044 			int iy = H * (ry - by1) / (by2 - by1);
1045 
1046 			ix = CLAMP(0, ix, W - 1);
1047 			iy = CLAMP(0, iy, H - 1);
1048 
1049 			img_pixel_t pix = img->buf()[iy * W + ix];
1050 
1051 			if (pix != TRANS_PIXEL)
1052 			{
1053 				IM_DecodePixel(pix, dest[0], dest[1], dest[2]);
1054 			}
1055 		}
1056 	}
1057 
1058 #else // OpenGL
1059 	int bx1 = sx + (int)floor(-W * scale);
1060 	int bx2 = sx + (int)ceil ( W * scale);
1061 
1062 	int by1 = sy + (int)floor(-H * scale);
1063 	int by2 = sy + (int)ceil ( H * scale);
1064 
1065 	// don't make too small
1066 	if (bx2 <= bx1) bx2 = bx1 + 1;
1067 	if (by2 <= by1) by2 = by1 + 1;
1068 
1069 	// bind the sprite image (upload it to OpenGL if needed)
1070 	img->bind_gl();
1071 
1072 	// choose texture coords based on image size
1073 	float tx1 = 0.0;
1074 	float ty1 = 0.0;
1075 	float tx2 = (float)img->width()  / (float)RoundPOW2(img->width());
1076 	float ty2 = (float)img->height() / (float)RoundPOW2(img->height());
1077 
1078 	glColor3f(1, 1, 1);
1079 
1080 	glBegin(GL_QUADS);
1081 
1082 	glTexCoord2f(tx1, ty1); glVertex2i(bx1, by1);
1083 	glTexCoord2f(tx1, ty2); glVertex2i(bx1, by2);
1084 	glTexCoord2f(tx2, ty2); glVertex2i(bx2, by2);
1085 	glTexCoord2f(tx2, ty1); glVertex2i(bx2, by1);
1086 
1087 	glEnd();
1088 #endif
1089 }
1090 
1091 
DrawSectorNum(int mx1,int my1,int mx2,int my2,int side,int n)1092 void UI_Canvas::DrawSectorNum(int mx1, int my1, int mx2, int my2, int side, int n)
1093 {
1094 	// only draw a number for the first linedef actually visible
1095 	if (seen_sectors.get(n))
1096 		return;
1097 
1098 	seen_sectors.set(n);
1099 
1100 	DrawLineNumber(mx1, my1, mx2, my2, side, n);
1101 }
1102 
1103 
DrawLineNumber(int mx1,int my1,int mx2,int my2,int side,int n)1104 void UI_Canvas::DrawLineNumber(int mx1, int my1, int mx2, int my2, int side, int n)
1105 {
1106 	int x1 = SCREENX(mx1);
1107 	int y1 = SCREENY(my1);
1108 	int x2 = SCREENX(mx2);
1109 	int y2 = SCREENY(my2);
1110 
1111 	int sx = (x1 + x2) / 2;
1112 	int sy = (y1 + y2) / 2;
1113 
1114 	// normally draw line numbers on back of line
1115 	int want_len = -16 * CLAMP(0.25, grid.Scale, 1.0);
1116 
1117 	// for sectors, draw closer and on sector side
1118 	if (side != 0)
1119 	{
1120 		want_len = 2 + 12 * CLAMP(0.25, grid.Scale, 1.0);
1121 
1122 		if (side == SIDE_LEFT)
1123 			want_len = -want_len;
1124 	}
1125 
1126 	sx += NORMALX(want_len*2, x2 - x1, y2 - y1);
1127 	sy += NORMALY(want_len,   x2 - x1, y2 - y1);
1128 
1129 	DrawNumber(sx, sy, n);
1130 }
1131 
1132 
DrawLineInfo(double map_x1,double map_y1,double map_x2,double map_y2,bool force_ratio)1133 void UI_Canvas::DrawLineInfo(double map_x1, double map_y1, double map_x2, double map_y2,
1134 							 bool force_ratio)
1135 {
1136 	line_info_mode_e info = (line_info_mode_e)highlight_line_info;
1137 
1138 	if (info == LINFO_Nothing)
1139 		return;
1140 
1141 	int x1 = SCREENX(map_x1);
1142 	int y1 = SCREENY(map_y1);
1143 	int x2 = SCREENX(map_x2);
1144 	int y2 = SCREENY(map_y2);
1145 
1146 	int sx = (x1 + x2) / 2;
1147 	int sy = (y1 + y2) / 2;
1148 
1149 	// if midpoint is off the screen, try to find a better one
1150 	double mx = (map_x1 + map_x2) / 2.0;
1151 	double my = (map_y1 + map_y2) / 2.0;
1152 
1153 	if (mx < map_lx + 4 || mx > map_hx - 4 ||
1154 		my < map_ly + 4 || my > map_hy - 4)
1155 	{
1156 		double best_dist = 9e9;
1157 
1158 		for (double p = 0.1 ; p < 0.91 ; p += 0.1)
1159 		{
1160 			mx = map_x1 + (map_x2 - map_x1) * p;
1161 			my = map_y1 + (map_y2 - map_y1) * p;
1162 
1163 			double dist_x = mx * 2.0 - (map_lx + map_hx);
1164 			double dist_y = my * 2.0 - (map_ly + map_hy);
1165 			double dist = hypot(dist_x, dist_y);
1166 
1167 			if (dist < best_dist)
1168 			{
1169 				sx = x1 + (x2 - x1) * p;
1170 				sy = y1 + (y2 - y1) * p;
1171 				best_dist = dist;
1172 			}
1173 		}
1174 	}
1175 
1176 	// back of line is best place, no knob getting in the way
1177 	int want_len = -16 * CLAMP(0.25, grid.Scale, 1.0);
1178 
1179 	sx += NORMALX(want_len*2, x2 - x1, y2 - y1);
1180 	sy += NORMALY(want_len,   x2 - x1, y2 - y1);
1181 
1182 	/* length */
1183 
1184 	fixcoord_t idx = MakeValidCoord(map_x2) - MakeValidCoord(map_x1);
1185 	fixcoord_t idy = MakeValidCoord(map_y2) - MakeValidCoord(map_y1);
1186 
1187 	if (info == LINFO_Length || info >= LINFO_Length_Angle)
1188 	{
1189 		double length = hypot(FROM_COORD(idx), FROM_COORD(idy));
1190 
1191 		if (length > 0.1)
1192 		{
1193 			char buffer[64];
1194 			snprintf(buffer, sizeof(buffer), "%1.1f", length);
1195 
1196 			RenderNumString(sx, sy, buffer);
1197 
1198 			sy = sy - cur_font;
1199 		}
1200 	}
1201 
1202 	/* angle */
1203 
1204 	if (info == LINFO_Angle || info == LINFO_Length_Angle)
1205 	{
1206 		double dx = FROM_COORD(idx);
1207 		double dy = FROM_COORD(idy);
1208 
1209 		int degrees = (int)round(atan2(dy, dx) * 180.0 / M_PI);
1210 		if (degrees < 0)
1211 			degrees += 360;
1212 
1213 		char buffer[64];
1214 		snprintf(buffer, sizeof(buffer), "%d^", degrees);
1215 
1216 		RenderNumString(sx, sy, buffer);
1217 	}
1218 
1219 	/* ratio */
1220 
1221 	if (info == LINFO_Ratio || info == LINFO_Length_Ratio)
1222 	{
1223 		if (idx != 0 && idy != 0)
1224 		{
1225 			std::string ratio_name = LD_RatioName(idx, idy, true);
1226 
1227 			RenderNumString(sx, sy, ratio_name.c_str());
1228 		}
1229 	}
1230 }
1231 
1232 
1233 //
1234 //  draw a number centered at screen coordinate (x, y)
1235 //
DrawNumber(int x,int y,int num)1236 void UI_Canvas::DrawNumber(int x, int y, int num)
1237 {
1238 	char buffer[64];
1239 	snprintf(buffer, sizeof(buffer), "%d", num);
1240 
1241 #if 0 /* DEBUG */
1242 	RenderColor(FL_RED);
1243 	RenderRect(x - 1, y - 1, 3, 3);
1244 	return;
1245 #endif
1246 
1247 	RenderNumString(x, y, buffer);
1248 }
1249 
1250 
CheckGridSnap()1251 void UI_Canvas::CheckGridSnap()
1252 {
1253 	if (!grid.snap || !grid_snap_indicator)
1254 		return;
1255 
1256 	double new_snap_x = grid.SnapX(edit.map_x);
1257 	double new_snap_y = grid.SnapY(edit.map_y);
1258 
1259 	if (snap_x == new_snap_x && snap_y == new_snap_y)
1260 		return;
1261 
1262 	snap_x = new_snap_x;
1263 	snap_y = new_snap_y;
1264 
1265 	redraw();
1266 }
1267 
1268 
UpdateHighlight()1269 void UI_Canvas::UpdateHighlight()
1270 {
1271 	bool changes = false;
1272 
1273 	if (! (last_highlight == edit.highlight))
1274 	{
1275 		last_highlight = edit.highlight;
1276 		changes = true;
1277 	}
1278 
1279 	int new_ld = edit.split_line.valid() ? edit.split_line.num : -1;
1280 
1281 	if (! (last_splitter == new_ld && last_split_x == edit.split_x && last_split_y == edit.split_y))
1282 	{
1283 		last_splitter = new_ld;
1284 		last_split_x  = edit.split_x;
1285 		last_split_y  = edit.split_y;
1286 		changes = true;
1287 	}
1288 
1289 	if (changes)
1290 		redraw();
1291 }
1292 
1293 
1294 //
1295 //  draw the given object in highlight color
1296 //
DrawHighlight(int objtype,int objnum,bool skip_lines,double dx,double dy)1297 void UI_Canvas::DrawHighlight(int objtype, int objnum, bool skip_lines,
1298 							  double dx, double dy)
1299 {
1300 	// color and line thickness have been set by caller
1301 
1302 	// fprintf(stderr, "DrawHighlight: %d\n", objnum);
1303 
1304 	switch (objtype)
1305 	{
1306 		case OBJ_THINGS:
1307 		{
1308 			double x = dx + Things[objnum]->x();
1309 			double y = dy + Things[objnum]->y();
1310 
1311 			if (! Vis(x, y, MAX_RADIUS))
1312 				break;
1313 
1314 			const thingtype_t *info = M_GetThingType(Things[objnum]->type);
1315 
1316 			int r = info->radius;
1317 
1318 			if (edit.error_mode)
1319 				DrawThing(x, y, r, Things[objnum]->angle, false /* big_arrow */);
1320 
1321 			r += r / 10 + 4;
1322 
1323 			DrawThing(x, y, r, Things[objnum]->angle, true);
1324 		}
1325 		break;
1326 
1327 		case OBJ_LINEDEFS:
1328 		{
1329 			double x1 = dx + LineDefs[objnum]->Start()->x();
1330 			double y1 = dy + LineDefs[objnum]->Start()->y();
1331 			double x2 = dx + LineDefs[objnum]->End  ()->x();
1332 			double y2 = dy + LineDefs[objnum]->End  ()->y();
1333 
1334 			if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
1335 				break;
1336 
1337 			DrawMapVector(x1, y1, x2, y2);
1338 		}
1339 		break;
1340 
1341 		case OBJ_VERTICES:
1342 		{
1343 			double x = dx + Vertices[objnum]->x();
1344 			double y = dy + Vertices[objnum]->y();
1345 
1346 			int vert_r = vertex_radius(grid.Scale);
1347 
1348 			if (! Vis(x, y, vert_r))
1349 				break;
1350 
1351 			DrawVertex(x, y, vert_r);
1352 
1353 			int r = vert_r * 3 / 2;
1354 
1355 			int sx1 = SCREENX(x) - r;
1356 			int sy1 = SCREENY(y) - r;
1357 			int sx2 = SCREENX(x) + r;
1358 			int sy2 = SCREENY(y) + r;
1359 
1360 			RenderLine(sx1, sy1, sx2, sy1);
1361 			RenderLine(sx2, sy1, sx2, sy2);
1362 			RenderLine(sx2, sy2, sx1, sy2);
1363 			RenderLine(sx1, sy2, sx1, sy1);
1364 		}
1365 		break;
1366 
1367 		case OBJ_SECTORS:
1368 		{
1369 			for (int n = 0 ; n < NumLineDefs ; n++)
1370 			{
1371 				const LineDef *L = LineDefs[n];
1372 
1373 				if (! L->TouchesSector(objnum))
1374 					continue;
1375 
1376 				bool reverse = false;
1377 
1378 				// skip lines if both sides are in the selection
1379 				if (skip_lines && L->TwoSided())
1380 				{
1381 					int sec1 = L->Right()->sector;
1382 					int sec2 = L->Left ()->sector;
1383 
1384 					if ((sec1 == objnum || edit.Selected->get(sec1)) &&
1385 					    (sec2 == objnum || edit.Selected->get(sec2)))
1386 						continue;
1387 
1388 					if (sec1 != objnum)
1389 						reverse = true;
1390 				}
1391 
1392 				double x1 = dx + L->Start()->x();
1393 				double y1 = dy + L->Start()->y();
1394 				double x2 = dx + L->End  ()->x();
1395 				double y2 = dy + L->End  ()->y();
1396 
1397 				if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
1398 					continue;
1399 
1400 				if (skip_lines)
1401 					DrawKnobbyLine(x1, y1, x2, y2, reverse);
1402 				else
1403 					DrawMapLine(x1, y1, x2, y2);
1404 			}
1405 		}
1406 		break;
1407 	}
1408 }
1409 
1410 
DrawHighlightTransform(int objtype,int objnum)1411 void UI_Canvas::DrawHighlightTransform(int objtype, int objnum)
1412 {
1413 	// color and line thickness have been set by caller
1414 
1415 	switch (objtype)
1416 	{
1417 		case OBJ_THINGS:
1418 		{
1419 			double x = Things[objnum]->x();
1420 			double y = Things[objnum]->y();
1421 
1422 			edit.trans_param.Apply(&x, &y);
1423 
1424 			if (! Vis(x, y, MAX_RADIUS))
1425 				break;
1426 
1427 			const thingtype_t *info = M_GetThingType(Things[objnum]->type);
1428 
1429 			int r = info->radius;
1430 
1431 			DrawThing(x, y, r * 3 / 2, Things[objnum]->angle, true);
1432 		}
1433 		break;
1434 
1435 		case OBJ_VERTICES:
1436 		{
1437 			double x = Vertices[objnum]->x();
1438 			double y = Vertices[objnum]->y();
1439 
1440 			int vert_r = vertex_radius(grid.Scale);
1441 
1442 			edit.trans_param.Apply(&x, &y);
1443 
1444 			if (! Vis(x, y, vert_r))
1445 				break;
1446 
1447 			DrawVertex(x, y, vert_r);
1448 
1449 			int r = vert_r * 3 / 2;
1450 
1451 			int sx1 = SCREENX(x) - r;
1452 			int sy1 = SCREENY(y) - r;
1453 			int sx2 = SCREENX(x) + r;
1454 			int sy2 = SCREENY(y) + r;
1455 
1456 			RenderLine(sx1, sy1, sx2, sy1);
1457 			RenderLine(sx2, sy1, sx2, sy2);
1458 			RenderLine(sx2, sy2, sx1, sy2);
1459 			RenderLine(sx1, sy2, sx1, sy1);
1460 		}
1461 		break;
1462 
1463 		case OBJ_LINEDEFS:
1464 		{
1465 			double x1 = LineDefs[objnum]->Start()->x();
1466 			double y1 = LineDefs[objnum]->Start()->y();
1467 			double x2 = LineDefs[objnum]->End  ()->x();
1468 			double y2 = LineDefs[objnum]->End  ()->y();
1469 
1470 			edit.trans_param.Apply(&x1, &y1);
1471 			edit.trans_param.Apply(&x2, &y2);
1472 
1473 			if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
1474 				break;
1475 
1476 			DrawMapVector(x1, y1, x2, y2);
1477 		}
1478 		break;
1479 
1480 		case OBJ_SECTORS:
1481 		{
1482 			for (int n = 0 ; n < NumLineDefs ; n++)
1483 			{
1484 				if (! LineDefs[n]->TouchesSector(objnum))
1485 					continue;
1486 
1487 				double x1 = LineDefs[n]->Start()->x();
1488 				double y1 = LineDefs[n]->Start()->y();
1489 				double x2 = LineDefs[n]->End  ()->x();
1490 				double y2 = LineDefs[n]->End  ()->y();
1491 
1492 				edit.trans_param.Apply(&x1, &y1);
1493 				edit.trans_param.Apply(&x2, &y2);
1494 
1495 				if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
1496 					continue;
1497 
1498 				DrawMapLine(x1, y1, x2, y2);
1499 			}
1500 		}
1501 		break;
1502 	}
1503 }
1504 
1505 
DrawTagged(int objtype,int objnum)1506 void UI_Canvas::DrawTagged(int objtype, int objnum)
1507 {
1508 	// color has been set by caller
1509 
1510 	// handle tagged linedefs : show matching sector(s)
1511 	if (objtype == OBJ_LINEDEFS && LineDefs[objnum]->tag > 0)
1512 	{
1513 		for (int m = 0 ; m < NumSectors ; m++)
1514 			if (Sectors[m]->tag == LineDefs[objnum]->tag)
1515 				DrawHighlight(OBJ_SECTORS, m);
1516 	}
1517 
1518 	// handle tagged sectors : show matching line(s)
1519 	if (objtype == OBJ_SECTORS && Sectors[objnum]->tag > 0)
1520 	{
1521 		for (int m = 0 ; m < NumLineDefs ; m++)
1522 			if (LineDefs[m]->tag == Sectors[objnum]->tag)
1523 				DrawHighlight(OBJ_LINEDEFS, m);
1524 	}
1525 }
1526 
1527 
DrawSectorSelection(selection_c * list,double dx,double dy)1528 void UI_Canvas::DrawSectorSelection(selection_c *list, double dx, double dy)
1529 {
1530 	// color and line thickness have been set by caller
1531 
1532 	for (int n = 0 ; n < NumLineDefs ; n++)
1533 	{
1534 		const LineDef *L = LineDefs[n];
1535 
1536 		double x1 = dx + L->Start()->x();
1537 		double y1 = dy + L->Start()->y();
1538 		double x2 = dx + L->End  ()->x();
1539 		double y2 = dy + L->End  ()->y();
1540 
1541 		if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
1542 			continue;
1543 
1544 		if (L->right < 0 && L->left < 0)
1545 			continue;
1546 
1547 		int sec1 = -1;
1548 		int sec2 = -1;
1549 
1550 		if (L->right >= 0) sec1 = L->Right()->sector;
1551 		if (L->left  >= 0) sec2 = L->Left() ->sector;
1552 
1553 		bool touches1 = (sec1 >= 0) && list->get(sec1);
1554 		bool touches2 = (sec2 >= 0) && list->get(sec2);
1555 
1556 		if (! (touches1 || touches2))
1557 			continue;
1558 
1559 		// skip lines if both sides are in the selection
1560 		if (touches1 && touches2)
1561 			continue;
1562 
1563 		bool reverse = !touches1;
1564 
1565 		DrawKnobbyLine(x1, y1, x2, y2, reverse);
1566 	}
1567 }
1568 
1569 //
1570 //  draw selected objects in light blue
1571 //
DrawSelection(selection_c * list)1572 void UI_Canvas::DrawSelection(selection_c * list)
1573 {
1574 	if (! list || list->empty())
1575 		return;
1576 
1577 	if (edit.action == ACT_TRANSFORM)
1578 	{
1579 		RenderColor(SEL_COL);
1580 
1581 		if (list->what_type() == OBJ_LINEDEFS || list->what_type() == OBJ_SECTORS)
1582 			RenderThickness(2);
1583 
1584 		for (sel_iter_c it(list) ; !it.done() ; it.next())
1585 		{
1586 			DrawHighlightTransform(list->what_type(), *it);
1587 		}
1588 
1589 		RenderThickness(1);
1590 		return;
1591 	}
1592 
1593 	double dx = 0;
1594 	double dy = 0;
1595 
1596 	if (edit.action == ACT_DRAG && edit.dragged.is_nil())
1597 	{
1598 		DragDelta(&dx, &dy);
1599 	}
1600 
1601 	RenderColor(edit.error_mode ? FL_RED : SEL_COL);
1602 
1603 	if (list->what_type() == OBJ_LINEDEFS || list->what_type() == OBJ_SECTORS)
1604 		RenderThickness(2);
1605 
1606 	// special case when we have many sectors
1607 	if (list->what_type() == OBJ_SECTORS && list->count_obj() > MAX_STORE_SEL)
1608 	{
1609 		DrawSectorSelection(list, dx, dy);
1610 	}
1611 	else
1612 	{
1613 		for (sel_iter_c it(list) ; !it.done() ; it.next())
1614 		{
1615 			DrawHighlight(list->what_type(), *it, true /* skip_lines */, dx, dy);
1616 		}
1617 	}
1618 
1619 	if (! edit.error_mode && dx == 0 && dy == 0)
1620 	{
1621 		RenderColor(LIGHTRED);
1622 
1623 		for (sel_iter_c it(list) ; !it.done() ; it.next())
1624 		{
1625 			DrawTagged(list->what_type(), *it);
1626 		}
1627 	}
1628 
1629 	RenderThickness(1);
1630 }
1631 
1632 
1633 //
1634 //  draw a plain line at the given map coords
1635 //
DrawMapLine(double map_x1,double map_y1,double map_x2,double map_y2)1636 void UI_Canvas::DrawMapLine(double map_x1, double map_y1, double map_x2, double map_y2)
1637 {
1638     RenderLine(SCREENX(map_x1), SCREENY(map_y1),
1639             SCREENX(map_x2), SCREENY(map_y2));
1640 }
1641 
1642 
1643 //
1644 //  draw a line with a "knob" showing the right (front) side
1645 //
DrawKnobbyLine(double map_x1,double map_y1,double map_x2,double map_y2,bool reverse)1646 void UI_Canvas::DrawKnobbyLine(double map_x1, double map_y1, double map_x2, double map_y2,
1647                                bool reverse)
1648 {
1649 	// color and thickness has been set by caller
1650 
1651 	int x1 = SCREENX(map_x1);
1652 	int y1 = SCREENY(map_y1);
1653 	int x2 = SCREENX(map_x2);
1654 	int y2 = SCREENY(map_y2);
1655 
1656 	if (reverse)
1657 	{
1658 		std::swap(x1, x2);
1659 		std::swap(y1, y2);
1660 	}
1661 
1662     RenderLine(x1, y1, x2, y2);
1663 
1664 	// indicate direction of line
1665    	int mx = (x1 + x2) / 2;
1666    	int my = (y1 + y2) / 2;
1667 
1668 	int len = MAX(abs(x2 - x1), abs(y2 - y1));
1669 	int want_len = MIN(12, len / 5);
1670 
1671 	int dx = NORMALX(want_len, x2 - x1, y2 - y1);
1672 	int dy = NORMALY(want_len, x2 - x1, y2 - y1);
1673 
1674 	if (! (dx == 0 && dy == 0))
1675 	{
1676 		RenderLine(mx, my, mx + dx, my + dy);
1677 	}
1678 }
1679 
1680 
DrawSplitPoint(double map_x,double map_y)1681 void UI_Canvas::DrawSplitPoint(double map_x, double map_y)
1682 {
1683 	int sx = SCREENX(map_x);
1684 	int sy = SCREENY(map_y);
1685 
1686 	int size = (grid.Scale >= 5.0) ? 9 : (grid.Scale >= 1.0) ? 7 : 5;
1687 
1688 	// color set by caller
1689 
1690 #ifdef NO_OPENGL
1691 	RenderRect(sx - size/2, sy - size/2, size, size);
1692 #else
1693 	glPointSize(size);
1694 
1695 	glBegin(GL_POINTS);
1696 	glVertex2i(sx, sy);
1697 	glEnd();
1698 
1699 	glPointSize(1.0);
1700 #endif
1701 }
1702 
1703 
DrawSplitLine(double map_x1,double map_y1,double map_x2,double map_y2)1704 void UI_Canvas::DrawSplitLine(double map_x1, double map_y1, double map_x2, double map_y2)
1705 {
1706 	// show how and where the line will be split
1707 
1708 	// color has been set by caller
1709 
1710 	int scr_x1 = SCREENX(map_x1);
1711 	int scr_y1 = SCREENY(map_y1);
1712 	int scr_x2 = SCREENX(map_x2);
1713 	int scr_y2 = SCREENY(map_y2);
1714 
1715 	int scr_mx = SCREENX(edit.split_x);
1716 	int scr_my = SCREENY(edit.split_y);
1717 
1718 	RenderLine(scr_x1, scr_y1, scr_mx, scr_my);
1719 	RenderLine(scr_x2, scr_y2, scr_mx, scr_my);
1720 
1721 	if (! edit.show_object_numbers)
1722 	{
1723 		double len1 = hypot(map_x1 - edit.split_x, map_y1 - edit.split_y);
1724 		double len2 = hypot(map_x2 - edit.split_x, map_y2 - edit.split_y);
1725 
1726 		DrawLineNumber(map_x1, map_y1, edit.split_x, edit.split_y, 0, I_ROUND(len1));
1727 		DrawLineNumber(map_x2, map_y2, edit.split_x, edit.split_y, 0, I_ROUND(len2));
1728 	}
1729 
1730 	RenderColor(HI_AND_SEL_COL);
1731 
1732 	DrawSplitPoint(edit.split_x, edit.split_y);
1733 }
1734 
1735 
1736 //
1737 // draw a bolder linedef with an arrow on the end
1738 // (used for highlighted / selected lines)
1739 //
DrawMapVector(double map_x1,double map_y1,double map_x2,double map_y2)1740 void UI_Canvas::DrawMapVector(double map_x1, double map_y1, double map_x2, double map_y2)
1741 {
1742 	int x1 = SCREENX(map_x1);
1743 	int y1 = SCREENY(map_y1);
1744 	int x2 = SCREENX(map_x2);
1745 	int y2 = SCREENY(map_y2);
1746 
1747 	RenderLine(x1, y1, x2, y2);
1748 
1749 	// knob
1750 	int mx = (x1 + x2) / 2;
1751 	int my = (y1 + y2) / 2;
1752 
1753 	int klen = MAX(abs(x2 - x1), abs(y2 - y1));
1754 	int want_len = CLAMP(12, klen / 4, 40);
1755 
1756 	int kx = NORMALX(want_len, x2 - x1, y2 - y1);
1757 	int ky = NORMALY(want_len, x2 - x1, y2 - y1);
1758 
1759 	RenderLine(mx, my, mx + kx, my + ky);
1760 
1761 	// arrow
1762 	double r2 = hypot((double) (x1 - x2), (double) (y1 - y2));
1763 
1764 	if (r2 < 1.0)
1765 		r2 = 1.0;
1766 
1767 	double len = CLAMP(6.0, r2 / 10.0, 24.0);
1768 
1769 	int dx = (int) (len * (x1 - x2) / r2);
1770 	int dy = (int) (len * (y1 - y2) / r2);
1771 
1772 	x1 = x2 + 2 * dx;
1773 	y1 = y2 + 2 * dy;
1774 
1775 	RenderLine(x1 - dy, y1 + dx, x2, y2);
1776 	RenderLine(x1 + dy, y1 - dx, x2, y2);
1777 }
1778 
1779 
1780 //
1781 //  draw an arrow
1782 //
DrawMapArrow(double map_x1,double map_y1,int r,int angle)1783 void UI_Canvas::DrawMapArrow(double map_x1, double map_y1, int r, int angle)
1784 {
1785 	float dx = r * cos(angle * M_PI / 180.0);
1786 	float dy = r * sin(angle * M_PI / 180.0);
1787 
1788 	float map_x2 = map_x1 + dx;
1789 	float map_y2 = map_y1 + dy;
1790 
1791 	DrawMapLine(map_x1, map_y1, map_x2, map_y2);
1792 
1793 	// arrow head
1794 	float x3 = map_x2 - dx * 0.3 + dy * 0.3;
1795 	float y3 = map_y2 - dy * 0.3 - dx * 0.3;
1796 
1797 	DrawMapLine(map_x2, map_y2, x3, y3);
1798 
1799 	x3 = map_x2 - dx * 0.3 - dy * 0.3;
1800 	y3 = map_y2 - dy * 0.3 + dx * 0.3;
1801 
1802 	DrawMapLine(map_x2, map_y2, x3, y3);
1803 }
1804 
1805 
DrawCamera()1806 void UI_Canvas::DrawCamera()
1807 {
1808 	double map_x, map_y;
1809 	float angle;
1810 
1811 	Render3D_GetCameraPos(&map_x, &map_y, &angle);
1812 
1813 	float mx = map_x;
1814 	float my = map_y;
1815 
1816 	float r = 40.0 / sqrt(grid.Scale);
1817 
1818 	float dx = r * cos(angle * M_PI / 180.0);
1819 	float dy = r * sin(angle * M_PI / 180.0);
1820 
1821 	// arrow body
1822 	float x1 = mx - dx;
1823 	float y1 = my - dy;
1824 
1825 	float x2 = mx + dx;
1826 	float y2 = my + dy;
1827 
1828 	RenderColor(CAMERA_COLOR);
1829 	RenderThickness(1);
1830 
1831 	DrawMapLine(x1, y1, x2, y2);
1832 
1833 	// arrow head
1834 	float x3 = x2 - dx * 0.6 + dy * 0.4;
1835 	float y3 = y2 - dy * 0.6 - dx * 0.4;
1836 
1837 	DrawMapLine(x2, y2, x3, y3);
1838 
1839 	x3 = x2 - dx * 0.6 - dy * 0.4;
1840 	y3 = y2 - dy * 0.6 + dx * 0.4;
1841 
1842 	DrawMapLine(x2, y2, x3, y3);
1843 
1844 	// notches on body
1845 	DrawMapLine(mx - dy * 0.4, my + dx * 0.4,
1846 				mx + dy * 0.4, my - dx * 0.4);
1847 
1848 	mx = mx - dx * 0.2;
1849 	my = my - dy * 0.2;
1850 
1851 	DrawMapLine(mx - dy * 0.4, my + dx * 0.4,
1852 				mx + dy * 0.4, my - dx * 0.4);
1853 
1854 	RenderThickness(1);
1855 }
1856 
1857 
DrawSnapPoint()1858 void UI_Canvas::DrawSnapPoint()
1859 {
1860 	// don't draw if an action is occurring
1861 	if (edit.action != ACT_NOTHING)
1862 		return;
1863 
1864 	if (edit.split_line.valid())
1865 		return;
1866 
1867 	if (! Vis(snap_x, snap_y, 10))
1868 		return;
1869 
1870 	RenderColor(FL_CYAN);
1871 
1872 	int sx = SCREENX(snap_x);
1873 	int sy = SCREENY(snap_y);
1874 
1875 	RenderRect(sx,   sy-2, 2, 6);
1876 	RenderRect(sx-2, sy,   6, 2);
1877 }
1878 
1879 
DrawCurrentLine()1880 void UI_Canvas::DrawCurrentLine()
1881 {
1882 	if (edit.draw_from.is_nil())
1883 		return;
1884 
1885 	const Vertex * V = Vertices[edit.draw_from.num];
1886 
1887 	double new_x = edit.draw_to_x;
1888 	double new_y = edit.draw_to_y;
1889 
1890 	// should draw a vertex?
1891 	if (! (edit.highlight.valid() || edit.split_line.valid()))
1892 	{
1893 		RenderColor(FL_GREEN);
1894 		DrawVertex(new_x, new_y, vertex_radius(grid.Scale));
1895 	}
1896 
1897 	RenderColor(RED);
1898 	DrawKnobbyLine(V->x(), V->y(), new_x, new_y);
1899 
1900 	DrawLineInfo(V->x(), V->y(), new_x, new_y, grid.ratio > 0);
1901 
1902 	// draw all the crossing points
1903 	crossing_state_c cross;
1904 
1905 	FindCrossingPoints(cross,
1906 					   V->x(), V->y(), edit.draw_from.num,
1907 					   new_x, new_y, edit.highlight.valid() ? edit.highlight.num : -1);
1908 
1909 	for (unsigned int k = 0 ; k < cross.points.size() ; k++)
1910 	{
1911 		cross_point_t& point = cross.points[k];
1912 
1913 		// ignore current split line (what new vertex is sitting on)
1914 		if (point.ld >= 0 && point.ld == edit.split_line.num)
1915 			continue;
1916 
1917 		if (point.vert >= 0)
1918 			RenderColor(FL_GREEN);
1919 		else
1920 			RenderColor(HI_AND_SEL_COL);
1921 
1922 		DrawSplitPoint(point.x, point.y);
1923 	}
1924 }
1925 
1926 
SelboxGet(double & x1,double & y1,double & x2,double & y2)1927 bool UI_Canvas::SelboxGet(double& x1, double& y1, double& x2, double& y2)
1928 {
1929 	x1 = MIN(edit.selbox_x1, edit.selbox_x2);
1930 	y1 = MIN(edit.selbox_y1, edit.selbox_y2);
1931 	x2 = MAX(edit.selbox_x1, edit.selbox_x2);
1932 	y2 = MAX(edit.selbox_y1, edit.selbox_y2);
1933 
1934 	int scr_dx = abs(SCREENX(x2) - SCREENX(x1));
1935 	int scr_dy = abs(SCREENY(y2) - SCREENY(y1));
1936 
1937 	// small boxes should be ignored (treated as a click + release)
1938 	if (scr_dx < 5 && scr_dy < 5)
1939 		return false;
1940 
1941 	return true; // Ok
1942 }
1943 
1944 
SelboxDraw()1945 void UI_Canvas::SelboxDraw()
1946 {
1947 	double x1 = MIN(edit.selbox_x1, edit.selbox_x2);
1948 	double x2 = MAX(edit.selbox_x1, edit.selbox_x2);
1949 	double y1 = MIN(edit.selbox_y1, edit.selbox_y2);
1950 	double y2 = MAX(edit.selbox_y1, edit.selbox_y2);
1951 
1952 	RenderColor(FL_CYAN);
1953 
1954 	DrawMapLine(x1, y1, x2, y1);
1955 	DrawMapLine(x2, y1, x2, y2);
1956 	DrawMapLine(x2, y2, x1, y2);
1957 	DrawMapLine(x1, y2, x1, y1);
1958 }
1959 
1960 
DragDelta(double * dx,double * dy)1961 void UI_Canvas::DragDelta(double *dx, double *dy)
1962 {
1963 	*dx = edit.drag_cur_x - edit.drag_start_x;
1964 	*dy = edit.drag_cur_y - edit.drag_start_y;
1965 
1966 	float pixel_dx = *dx * grid.Scale;
1967 	float pixel_dy = *dy * grid.Scale;
1968 
1969 	// check that we have moved far enough from the start position,
1970 	// giving the user the option to select the original place.
1971 	if (MAX(abs(pixel_dx), abs(pixel_dy)) < minimum_drag_pixels*2)
1972 	{
1973 		*dx = *dy = 0;
1974 		return;
1975 	}
1976 
1977 	// handle ratio-lock of a single dragged vertex
1978 	if (edit.mode == OBJ_VERTICES && grid.ratio > 0 &&
1979 		edit.dragged.num >= 0 && edit.drag_other_vert >= 0)
1980 	{
1981 		const Vertex *v0 = Vertices[edit.drag_other_vert];
1982 		const Vertex *v1 = Vertices[edit.dragged.num];
1983 
1984 		double new_x = edit.drag_cur_x;
1985 		double new_y = edit.drag_cur_y;
1986 
1987 		grid.RatioSnapXY(new_x, new_y, v0->x(), v0->y());
1988 
1989 		*dx = new_x - v1->x();
1990 		*dy = new_y - v1->y();
1991 		return;
1992 	}
1993 
1994 	if (grid.ratio > 0)
1995 	{
1996 		double new_x = edit.drag_cur_x;
1997 		double new_y = edit.drag_cur_y;
1998 
1999 		grid.RatioSnapXY(new_x, new_y, edit.drag_start_x, edit.drag_start_y);
2000 
2001 		*dx = new_x - edit.drag_start_x;
2002 		*dy = new_y - edit.drag_start_y;
2003 		return;
2004 	}
2005 
2006 	if (grid.snap)
2007 	{
2008 		double focus_x = edit.drag_focus_x + *dx;
2009 		double focus_y = edit.drag_focus_y + *dy;
2010 
2011 		*dx = grid.SnapX(focus_x) - edit.drag_focus_x;
2012 		*dy = grid.SnapY(focus_y) - edit.drag_focus_y;
2013 	}
2014 }
2015 
2016 
2017 //------------------------------------------------------------------------
2018 
RenderSector(int num)2019 void UI_Canvas::RenderSector(int num)
2020 {
2021 	if (! Subdiv_SectorOnScreen(num, map_lx, map_ly, map_hx, map_hy))
2022 		return;
2023 
2024 	sector_subdivision_c *subdiv = Subdiv_PolygonsForSector(num);
2025 
2026 	if (! subdiv)
2027 		return;
2028 
2029 
2030 ///  fprintf(stderr, "RenderSector %d\n", num);
2031 
2032 	rgb_color_t light_col = SectorLightColor(Sectors[num]->light);
2033 	bool light_and_tex = false;
2034 
2035 	const char * tex_name = NULL;
2036 
2037 	Img_c * img = NULL;
2038 
2039 	if (edit.sector_render_mode == SREND_Lighting)
2040 	{
2041 		RenderColor(light_col);
2042 	}
2043 	else if (edit.sector_render_mode == SREND_SoundProp)
2044 	{
2045 		if (edit.mode != OBJ_SECTORS || !edit.highlight.valid())
2046 			return;
2047 
2048 		const byte * prop = SoundPropagation(edit.highlight.num);
2049 
2050 		switch ((propagate_level_e) prop[num])
2051 		{
2052 			case PGL_Never:   return;
2053 			case PGL_Maybe:   RenderColor(fl_rgb_color(64,64,192)); break;
2054 			case PGL_Level_1: RenderColor(fl_rgb_color(192,32,32)); break;
2055 			case PGL_Level_2: RenderColor(fl_rgb_color(192,96,32)); break;
2056 		}
2057 	}
2058 	else
2059 	{
2060 		if (edit.sector_render_mode <= SREND_Ceiling)
2061 			light_and_tex = true;
2062 
2063 		if (edit.sector_render_mode == SREND_Ceiling ||
2064 			edit.sector_render_mode == SREND_CeilBright)
2065 			tex_name = Sectors[num]->CeilTex();
2066 		else
2067 			tex_name = Sectors[num]->FloorTex();
2068 
2069 		if (is_sky(tex_name))
2070 		{
2071 			RenderColor(palette[Misc_info.sky_color]);
2072 		}
2073 		else
2074 		{
2075 			img = W_GetFlat(tex_name);
2076 
2077 			if (! img)
2078 			{
2079 				img = IM_UnknownTex();
2080 			}
2081 		}
2082 	}
2083 
2084 #ifdef NO_OPENGL
2085 	int tw = img ? img->width()  : 1;
2086 	int th = img ? img->height() : 1;
2087 
2088 	const img_pixel_t *src_pix = img ? img->buf() : NULL;
2089 
2090 	for (unsigned int i = 0 ; i < subdiv->polygons.size() ; i++)
2091 	{
2092 		sector_polygon_t *poly = &subdiv->polygons[i];
2093 
2094 		float py1 = poly->my[1];  // north most
2095 		float py2 = poly->my[0];
2096 
2097 		int sy1 = SCREENY(py1);
2098 		int sy2 = SCREENY(py2);
2099 
2100 		// clip range to screen
2101 		sy1 = MAX(sy1, y());
2102 		sy2 = MIN(sy2, y() + h() - 1);
2103 
2104 		// reject polygons vertically off the screen
2105 		if (sy1 > sy2)
2106 			continue;
2107 
2108 		// get left and right edges, unpacking a triangle if necessary
2109 		float lx1 = poly->mx[1];
2110 		float lx2 = poly->mx[0];
2111 
2112 		float rx1 = poly->mx[2];
2113 		float rx2 = poly->mx[3];
2114 
2115 		if (poly->count == 3)
2116 		{
2117 			if (poly->my[2] == poly->my[0])
2118 			{
2119 				rx1 = poly->mx[1];
2120 				rx2 = poly->mx[2];
2121 			}
2122 			else // my[2] == my[1]
2123 			{
2124 				rx2 = poly->mx[0];
2125 			}
2126 		}
2127 
2128 		// visit each screen row
2129 		for (short y = (short)sy1 ; y <= (short)sy2 ; y++)
2130 		{
2131 			// compute horizontal span
2132 			float map_y = MAPY(y);
2133 
2134 			float lx = lx1 + (lx2 - lx1) * (map_y - py1) / (py2 - py1);
2135 			float rx = rx1 + (rx2 - rx1) * (map_y - py1) / (py2 - py1);
2136 
2137 			int sx1 = SCREENX(lx);
2138 			int sx2 = SCREENX(rx);
2139 
2140 			// clip span to screen
2141 			sx1 = MAX(sx1, x());
2142 			sx2 = MIN(sx2, x() + w() - 1);
2143 
2144 			// reject spans completely off the screen
2145 			if (sx2 < sx1)
2146 				continue;
2147 
2148 ///  fprintf(stderr, "  span : y=%d  x=%d..%d\n", y, x1, x2);
2149 
2150 			// solid color?
2151 			if (! img)
2152 			{
2153 				RenderRect(sx1, y, sx2 - sx1 + 1, 1);
2154 				continue;
2155 			}
2156 
2157 			int x = sx1;
2158 			int span_w = sx2 - sx1 + 1;
2159 
2160 			u8_t *dest = rgb_buf + ((x - rgb_x) + (y - rgb_y) * rgb_w) * 3;
2161 			u8_t *dest_end = dest + span_w * 3;
2162 
2163 			// the logic here for non-64x64 textures matches the software
2164 			// 3D renderer, but is different than ZDoom (which scales them).
2165 			int ty = (0 - (int)MAPY(y)) & (th - 1);
2166 
2167 			if (light_and_tex)
2168 			{
2169 				int r = RGB_RED(light_col)   * 0x101;
2170 				int g = RGB_GREEN(light_col) * 0x101;
2171 				int b = RGB_BLUE(light_col)  * 0x101;
2172 
2173 				for (; dest < dest_end ; dest += 3, x++)
2174 				{
2175 					int tx = (int)MAPX(x) & (tw - 1);
2176 
2177 					img_pixel_t pix = src_pix[ty * tw + tx];
2178 
2179 					IM_DecodePixel(pix, dest[0], dest[1], dest[2]);
2180 
2181 					dest[0] = ((int)dest[0] * r) >> 16;
2182 					dest[1] = ((int)dest[1] * g) >> 16;
2183 					dest[2] = ((int)dest[2] * b) >> 16;
2184 				}
2185 			}
2186 			else  // fullbright version
2187 			{
2188 				for (; dest < dest_end ; dest += 3, x++)
2189 				{
2190 					int tx = (int)MAPX(x) & (tw - 1);
2191 
2192 					img_pixel_t pix = src_pix[ty * tw + tx];
2193 
2194 					IM_DecodePixel(pix, dest[0], dest[1], dest[2]);
2195 				}
2196 			}
2197 		}
2198 	}
2199 
2200 #else // OpenGL
2201 	if (img)
2202 	{
2203 		if (light_and_tex)
2204 			RenderColor(light_col);
2205 		else
2206 			glColor3f(1, 1, 1);
2207 
2208 		glEnable(GL_TEXTURE_2D);
2209 		glEnable(GL_ALPHA_TEST);
2210 
2211 		glAlphaFunc(GL_GREATER, 0.5);
2212 
2213 		img->bind_gl();
2214 	}
2215 	else
2216 	{
2217 		// color was set above, set texture to solid white
2218 		glBindTexture(GL_TEXTURE_2D, 0);
2219 	}
2220 
2221 	for (unsigned int i = 0 ; i < subdiv->polygons.size() ; i++)
2222 	{
2223 		sector_polygon_t *poly = &subdiv->polygons[i];
2224 
2225 		// draw polygon
2226 		glBegin(GL_POLYGON);
2227 
2228 		for (int p = 0 ; p < poly->count ; p++)
2229 		{
2230 			int sx = SCREENX(poly->mx[p]);
2231 			int sy = SCREENY(poly->my[p]);
2232 
2233 			if (img)
2234 			{
2235 				// this logic follows ZDoom, which scales large flats to
2236 				// occupy a 64x64 unit area.  I presume wall textures are
2237 				// handled similarily....
2238 				glTexCoord2f(poly->mx[p] / 64.0, poly->my[p] / 64.0);
2239 			}
2240 
2241 			glVertex2i(sx, sy);
2242 		}
2243 
2244 		glEnd();
2245 	}
2246 
2247 	if (img)
2248 	{
2249 		glDisable(GL_TEXTURE_2D);
2250 		glDisable(GL_ALPHA_TEST);
2251 	}
2252 #endif
2253 }
2254 
2255 
2256 //------------------------------------------------------------------------
2257 //  CUSTOM S/W DRAWING CODE
2258 //------------------------------------------------------------------------
2259 
PrepareToDraw()2260 void UI_Canvas::PrepareToDraw()
2261 {
2262 #ifdef NO_OPENGL
2263 	rgb_x = x();
2264 	rgb_y = y();
2265 
2266 	if (rgb_w != w() || rgb_h != h())
2267 	{
2268 		if (rgb_buf)
2269 			delete[] rgb_buf;
2270 
2271 		rgb_w = w();
2272 		rgb_h = h();
2273 
2274 		rgb_buf = new byte[rgb_w * rgb_h * 3];
2275 	}
2276 #endif
2277 }
2278 
2279 
Blit()2280 void UI_Canvas::Blit()
2281 {
2282 #ifdef NO_OPENGL
2283 	fl_draw_image(rgb_buf, x(), y(), w(), h());
2284 #endif
2285 }
2286 
2287 
RenderColor(Fl_Color c)2288 void UI_Canvas::RenderColor(Fl_Color c)
2289 {
2290 #ifdef NO_OPENGL
2291 	Fl::get_color(c, cur_col.r, cur_col.g, cur_col.b);
2292 #else
2293 	gl_color(c);
2294 #endif
2295 }
2296 
RenderFontSize(int size)2297 void UI_Canvas::RenderFontSize(int size)
2298 {
2299 	cur_font = size;
2300 }
2301 
2302 
RenderThickness(int w)2303 void UI_Canvas::RenderThickness(int w)
2304 {
2305 #ifdef NO_OPENGL
2306 	thickness = (w < 2) ? 1 : 2;
2307 #else
2308 	glLineWidth(w);
2309 #endif
2310 }
2311 
2312 
RenderRect(int rx,int ry,int rw,int rh)2313 void UI_Canvas::RenderRect(int rx, int ry, int rw, int rh)
2314 {
2315 #ifndef NO_OPENGL
2316 	gl_rectf(rx, ry, rw, rh);
2317 
2318 #else
2319 	// software version
2320 	rx -= rgb_x;
2321 	ry -= rgb_y;
2322 
2323 	// clip to screen
2324 	if (rx + rw > rgb_w)
2325 	{
2326 		rw = rgb_w - rx;
2327 	}
2328 	if (rx < 0)
2329 	{
2330 		rw += rx;
2331 		rx = 0;
2332 	}
2333 	if (rw <= 0)
2334 		return;
2335 
2336 	if (ry + rh > rgb_h)
2337 	{
2338 		rh = rgb_h - ry;
2339 	}
2340 	if (ry < 0)
2341 	{
2342 		rh += ry;
2343 		ry = 0;
2344 	}
2345 	if (rh <= 0)
2346 		return;
2347 
2348 	// fast method for greyscale (especially BLACK)
2349 	if (cur_col.r == cur_col.g && cur_col.g == cur_col.b)
2350 	{
2351 		byte *dest = rgb_buf + (ry * rgb_w * 3) + (rx * 3);
2352 
2353 		for ( ; rh > 0 ; rh--, dest += (rgb_w * 3))
2354 			memset(dest, cur_col.r, rw * 3);
2355 
2356 		return;
2357 	}
2358 
2359 	// slower method for all other colors
2360 	byte *base = rgb_buf + (ry * rgb_w * 3) + (rx * 3);
2361 
2362 	for ( ; rh > 0 ; rh--, base += (rgb_w * 3))
2363 	{
2364 		byte *dest = base;
2365 
2366 		for (int w2 = rw ; w2 > 0 ; w2--)
2367 		{
2368 			*dest++ = cur_col.r;
2369 			*dest++ = cur_col.g;
2370 			*dest++ = cur_col.b;
2371 		}
2372 	}
2373 #endif
2374 }
2375 
2376 
2377 #ifdef NO_OPENGL
2378 enum outcode_flags_e
2379 {
2380 	O_TOP    = 1,
2381 	O_BOTTOM = 2,
2382 	O_LEFT   = 4,
2383 	O_RIGHT  = 8,
2384 };
2385 
Calc_Outcode(int x,int y)2386 int UI_Canvas::Calc_Outcode(int x, int y)
2387 {
2388 	return
2389 		((y < 0)      ? O_TOP    : 0) |
2390 		((y >= rgb_h) ? O_BOTTOM : 0) |
2391 		((x < 0)      ? O_LEFT   : 0) |
2392 		((x >= rgb_w) ? O_RIGHT  : 0);
2393 }
2394 #endif // NO_OPENGL
2395 
2396 
RenderLine(int x1,int y1,int x2,int y2)2397 void UI_Canvas::RenderLine(int x1, int y1, int x2, int y2)
2398 {
2399 #ifndef NO_OPENGL
2400 	glBegin(GL_LINE_STRIP);
2401 	glVertex2i(x1, y1);
2402 	glVertex2i(x2, y2);
2403 	glEnd();
2404 #else
2405 	// software line drawing
2406 	if (x1 == x2)
2407 	{
2408 		if (y1 > y2)
2409 			std::swap(y1, y2);
2410 
2411 		RenderRect(x1, y1, thickness, y2 - y1 + thickness);
2412 		return;
2413 	}
2414 	if (y1 == y2)
2415 	{
2416 		if (x1 > x2)
2417 			std::swap(x1, x2);
2418 
2419 		RenderRect(x1, y1, x2 - x1 + thickness, thickness);
2420 		return;
2421 	}
2422 
2423 
2424 	// completely off the screen?
2425 	x1 -= rgb_x; y1 -= rgb_y;
2426 	x2 -= rgb_x; y2 -= rgb_y;
2427 
2428 	int out1 = Calc_Outcode(x1, y1);
2429 	int out2 = Calc_Outcode(x2, y2);
2430 
2431 	if (out1 & out2)
2432 		return;
2433 
2434 
2435 	// clip diagonal line to the map
2436 	// (this is the Cohen-Sutherland clipping algorithm)
2437 
2438 	while (out1 | out2)
2439 	{
2440 		// may be partially inside box, find an outside point
2441 		int outside = (out1 ? out1 : out2);
2442 
2443 		int dx = x2 - x1;
2444 		int dy = y2 - y1;
2445 
2446 		// this almost certainly cannot happen, but for the sake of
2447 		// robustness we check anyway (just in case)
2448 		if (dx == 0 && dy == 0)
2449 			return;
2450 
2451 		int new_x, new_y;
2452 
2453 		// clip to each side
2454 		if (outside & O_TOP)
2455 		{
2456 			new_y = 0;
2457 			new_x = x1 + dx * (new_y - y1) / dy;
2458 		}
2459 		else if (outside & O_BOTTOM)
2460 		{
2461 			new_y = rgb_h-1;
2462 			new_x = x1 + dx * (new_y - y1) / dy;
2463 		}
2464 		else if (outside & O_LEFT)
2465 		{
2466 			new_x = 0;
2467 			new_y = y1 + dy * (new_x - x1) / dx;
2468 		}
2469 		else
2470 		{
2471 			SYS_ASSERT(outside & O_RIGHT);
2472 
2473 			new_x = rgb_w-1;
2474 			new_y = y1 + dy * (new_x - x1) / dx;
2475 		}
2476 
2477 		if (out1)
2478 		{
2479 			x1 = new_x;
2480 			y1 = new_y;
2481 
2482 			out1 = Calc_Outcode(x1, y1);
2483 		}
2484 		else
2485 		{
2486 			SYS_ASSERT(out2);
2487 
2488 			x2 = new_x;
2489 			y2 = new_y;
2490 
2491 			out2 = Calc_Outcode(x2, y2);
2492 		}
2493 
2494 		if (out1 & out2)
2495 			return;
2496 	}
2497 
2498 
2499 	// this is the Bresenham line drawing algorithm
2500 	// (based on code from am_map.c in the GPL DOOM source)
2501 
2502 	int dx = x2 - x1;
2503 	int dy = y2 - y1;
2504 
2505 	int ax = 2 * (dx < 0 ? -dx : dx);
2506 	int ay = 2 * (dy < 0 ? -dy : dy);
2507 
2508 	int sx = dx < 0 ? -1 : 1;
2509 	int sy = dy < 0 ? -1 : 1;
2510 
2511 	int x = x1;
2512 	int y = y1;
2513 
2514 	if (ax > ay)  // horizontal stepping
2515 	{
2516 		int d = ay - ax/2;
2517 
2518 		raw_pixel(x, y);
2519 		if (thickness == 2 && y+1 < rgb_h) raw_pixel(x, y+1);
2520 
2521 		while (x != x2)
2522 		{
2523 			if (d>=0)
2524 			{
2525 				y += sy;
2526 				d -= ax;
2527 			}
2528 
2529 			x += sx;
2530 			d += ay;
2531 
2532 			raw_pixel(x, y);
2533 			if (thickness == 2 && y+1 < rgb_h) raw_pixel(x, y+1);
2534 		}
2535 	}
2536 	else   // vertical stepping
2537 	{
2538 		int d = ax - ay/2;
2539 
2540 		raw_pixel(x, y);
2541 		if (thickness == 2 && x+1 < rgb_w) raw_pixel(x+1, y);
2542 
2543 		while (y != y2)
2544 		{
2545 			if (d >= 0)
2546 			{
2547 				x += sx;
2548 				d -= ay;
2549 			}
2550 
2551 			y += sy;
2552 			d += ax;
2553 
2554 			raw_pixel(x, y);
2555 			if (thickness == 2 && x+1 < rgb_w) raw_pixel(x+1, y);
2556 		}
2557 	}
2558 #endif
2559 }
2560 
2561 
RenderNumString(int x,int y,const char * s)2562 void UI_Canvas::RenderNumString(int x, int y, const char *s)
2563 {
2564 	// NOTE: string is limited to the digits '0' to '9', spaces,
2565 	//       and the characters '-', '.' and ':'.
2566 
2567 	Img_c *font_img;
2568 	int font_cw;
2569 	int font_ch;
2570 	int font_step;
2571 
2572 	if (cur_font < 17)
2573 	{
2574 		font_img  = IM_DigitFont_11x14();
2575 		font_cw   = 11;
2576 		font_ch   = 14;
2577 		font_step = font_cw - 2;
2578 	}
2579 	else
2580 	{
2581 		font_img  = IM_DigitFont_14x19();
2582 		font_cw   = 14;
2583 		font_ch   = 19;
2584 		font_step = font_cw - 2;
2585 	}
2586 
2587 #ifndef NO_OPENGL
2588 	glEnable(GL_TEXTURE_2D);
2589 	glEnable(GL_ALPHA_TEST);
2590 
2591 	glAlphaFunc(GL_GREATER, 0.5);
2592 
2593 	// bind the sprite image (upload it to OpenGL if needed)
2594 	font_img->bind_gl();
2595 #endif
2596 
2597 	// compute total size
2598 	int total_w = static_cast<int>(strlen(s) * font_step + 2);
2599 
2600 	// center the string at the given coordinate
2601 	x -= total_w / 2;
2602 	y -= font_ch / 2;
2603 
2604 	for ( ; *s ; s++, x += font_step)
2605 	{
2606 		int ch = (*s & 0x7f);
2607 		if (ch == ' ')
2608 			continue;
2609 
2610 		if ('0' <= ch && ch <= '9')
2611 			ch -= '0';
2612 		else if (ch == ':')
2613 			ch = 10;
2614 		else if (ch == '.')
2615 			ch = 11;
2616 		else if (ch == '^')
2617 			ch = 13;
2618 		else // '-'
2619 			ch = 12;
2620 
2621 		RenderFontChar(x, y, font_img, ch * font_cw, 0, font_cw, font_ch);
2622 	}
2623 
2624 #ifndef NO_OPENGL
2625 	glDisable(GL_ALPHA_TEST);
2626 	glDisable(GL_TEXTURE_2D);
2627 #endif
2628 }
2629 
2630 
RenderFontChar(int rx,int ry,Img_c * img,int ix,int iy,int iw,int ih)2631 void UI_Canvas::RenderFontChar(int rx, int ry, Img_c *img, int ix, int iy, int iw, int ih)
2632 {
2633 #ifdef NO_OPENGL
2634 	// software rendering
2635 
2636 	rx -= rgb_x;
2637 	ry -= rgb_y;
2638 
2639 	// clip to screen
2640 	int sx1 = MAX(rx, 0);
2641 	int sy1 = MAX(ry, 0);
2642 
2643 	int sx2 = MIN(rx + iw, rgb_w) - 1;
2644 	int sy2 = MIN(ry + ih, rgb_h) - 1;
2645 
2646 	if (sx1 >= sx2 || sy1 >= sy2)
2647 		return;
2648 
2649 	for (int sy = sy1 ; sy <= sy2 ; sy++, iy++)
2650 	{
2651 		const img_pixel_t *src = img->buf() + (ix + iy * img->width());
2652 
2653 		byte *dest = rgb_buf + 3 * (sx1 + sy * rgb_w);
2654 
2655 		for (int sx = sx1 ; sx <= sx2 ; sx++, dest += 3)
2656 		{
2657 			img_pixel_t pix = *src++;
2658 
2659 			if (pix != TRANS_PIXEL)
2660 			{
2661 				IM_DecodePixel(pix, dest[0], dest[1], dest[2]);
2662 			}
2663 		}
2664 	}
2665 
2666 #else // OpenGL
2667 	int rx2 = rx + iw;
2668 	int ry2 = ry + ih;
2669 
2670 	int pow2_width  = RoundPOW2(img->width());
2671 	int pow2_height = RoundPOW2(img->height());
2672 
2673 	float tx1 = (float)ix / (float)pow2_width;
2674 	float ty1 = (float)iy / (float)pow2_height;
2675 	float tx2 = (float)(ix + iw) / (float)pow2_width;
2676 	float ty2 = (float)(iy + ih) / (float)pow2_height;
2677 
2678 	glColor3f(1, 1, 1);
2679 
2680 	glBegin(GL_QUADS);
2681 
2682 	glTexCoord2f(tx1, ty1); glVertex2i(rx,  ry);
2683 	glTexCoord2f(tx1, ty2); glVertex2i(rx,  ry2);
2684 	glTexCoord2f(tx2, ty2); glVertex2i(rx2, ry2);
2685 	glTexCoord2f(tx2, ty1); glVertex2i(rx2, ry);
2686 
2687 	glEnd();
2688 #endif
2689 }
2690 
2691 //--- editor settings ---
2692 // vi:ts=4:sw=4:noexpandtab
2693