1 //
2 //	canvas.cc
3 //
4 
5 #include "wx/brush.h"
6 #include "wx/colour.h"
7 #include "wx/dc.h"
8 #include "wx/dcclient.h"
9 #include "wx/font.h"
10 #include "wx/gdicmn.h"
11 #include "wx/pen.h"
12 #include "wx/scrolwin.h"
13 #include "wx/textdlg.h"
14 
15 #include "canvas.h"
16 #include "edge.h"
17 #include "graph.h"
18 #include "gui.h"
19 #include "lang.h"
20 #include "paramdialog.h"
21 #include "vertex.h"
22 
23 
BEGIN_EVENT_TABLE(Canvas,wxScrolledWindow)24 BEGIN_EVENT_TABLE(Canvas, wxScrolledWindow)
25 	EVT_PAINT	(Canvas::OnPaint)
26 	EVT_ERASE_BACKGROUND(Canvas::OnEraseBackground)
27 	EVT_LEFT_DOWN	(Canvas::OnClick)
28 	EVT_LEFT_DCLICK	(Canvas::OnClick)
29 	EVT_RIGHT_DOWN	(Canvas::OnClick)
30 	EVT_MOTION	(Canvas::OnMouseMove)
31 	EVT_CHAR	(Canvas::OnKeyPress)
32 END_EVENT_TABLE()
33 
34 
35 void Canvas::draw (wxDC &dc, Vertex *v)
36 {
37 	int r = Canvas::vertex_radius;
38 	wxColour col = black;
39 
40 	if (v->selected) {
41 		unsigned int idx = v->selection_colour %
42 						selection_colours.size ();
43 		col = selection_colours[idx];
44 	}
45 
46 	dc.SetBrush (wxBrush (col));
47 	dc.SetPen (wxPen (col));
48 
49 	dc.DrawCircle (v->x, v->y, r);
50 
51 	if (do_labels) {
52 		dc.SetFont (*bold_font);
53 		dc.SetTextForeground (col);
54 		dc.DrawText (v->label, v->x + r, v->y - 2 * r);
55 	}
56 }
57 
draw(wxDC & dc,Edge * e,bool curved)58 void Canvas::draw (wxDC &dc, Edge *e, bool curved)
59 {
60 	double dx, dy, r = Canvas::vertex_radius;
61 	double theta;
62 	wxColour col = black;
63 
64 	if (e->selected) {
65 		unsigned int idx = e->selection_colour %
66 						selection_colours.size ();
67 		col = selection_colours[idx];
68 	}
69 	wxPen pen (col, Canvas::edge_width);
70 	pen.SetCap (wxCAP_BUTT);
71 	pen.SetJoin (wxJOIN_MITER);
72 	dc.SetPen (pen);
73 	dc.SetBrush (wxBrush (col));
74 
75 	theta = atan2 (e->w->y - e->v->y, e->w->x - e->v->x);
76 	dx = r * cos (theta);
77 	dy = r * sin (theta);
78 
79 	if (e->v->y == e->w->y)		// horizontal line (dy = 0)
80 		dy = 0;
81 	else if (e->v->x == e->w->x)	// vertical line (dx = 0)
82 		dx = 0;
83 
84 	if (!curved)
85 		dc.DrawLine (int (e->v->x + dx), int (e->v->y + dy),
86 				int (e->w->x - dx), int (e->w->y - dy));
87 	else {
88 		dc.SetBrush (wxBrush (white));
89 		// TODO: get this right!
90 		double cx, cy;	// mid-spline control point
91 		cx = (e->v->x + e->w->x) / 2 - 2 * dy;
92 		cy = (e->v->y + e->w->y) / 2 - 2 * dx;
93 		dc.DrawSpline (int (e->v->x + dx), int (e->v->y + dy),
94 				int (cx), int (cy),
95 				int (e->w->x - dx), int (e->w->y - dy));
96 
97 		dc.SetBrush (wxBrush (col));
98 	}
99 
100 	if (e->directed) {
101 		wxPoint tri[3];
102 		double scale = 0.7;
103 		tri[0] = wxPoint (0, 0);
104 		tri[1] = wxPoint (int (scale * (dy - dx)),
105 				int (-scale * (dx + dy)));
106 		tri[2] = wxPoint (int (-scale * (dx + dy)),
107 				int (scale * (dx - dy)));
108 		dc.DrawPolygon (3, tri, int (e->w->x - dx), int (e->w->y - dy));
109 	}
110 
111 	if (do_weights) {
112 		wxString str;
113 		if (!do_flows)
114 			str = wxString::Format (wxT("%i"), e->weight);
115 		else
116 			str = wxString::Format (wxT("%i/%i"), e->flow, e->weight);
117 		double rx, ry;
118 		wxCoord w, h;
119 
120 		dc.SetFont (*normal_font);
121 		dc.GetTextExtent (str, &w, &h);
122 
123 		rx = (e->v->x + e->w->x) / 2 + dy;
124 		ry = (e->v->y + e->w->y) / 2 - dx;
125 		if (curved) {
126 			rx += dy;
127 			ry -= dy;
128 		}
129 		if (theta < 0)
130 			rx += w * sin (theta);
131 		ry -= h / 2;
132 
133 		if (fabs (theta) > M_PI_2)
134 			ry -= dx / 2;
135 
136 		dc.SetTextForeground (col);
137 		dc.DrawText (str, int (rx), int (ry));
138 	}
139 }
140 
findVertex(int x,int y) const141 Vertex *Canvas::findVertex (int x, int y) const
142 {
143 	Graph::v_const_iterator vit;
144 	int r2 = Canvas::vertex_radius * Canvas::vertex_radius;
145 	Graph *g = *gg;
146 
147 	for (vit = g->v_begin (); vit != g->v_end (); ++vit) {
148 		int dx = x - (*vit)->x, dy = y - (*vit)->y;
149 		if ((dx * dx + dy * dy) < r2)
150 			return *vit;
151 	}
152 
153 	return 0;
154 }
155 
findEdge(int x,int y) const156 Edge *Canvas::findEdge (int x, int y) const
157 {
158 	Graph::e_const_iterator eit;
159 	double rad = Canvas::vertex_radius;
160 	double r2 = rad * rad;
161 	double best_dist = 0;		// actually, best distance squared
162 	Edge *best_edge = 0;
163 	Graph *g = *gg;
164 
165 	// find straight-line edge closest to (x,y)
166 	for (eit = g->e_begin (); eit != g->e_end (); ++eit) {
167 		double x1, y1, x2, y2, s2, num, d2;
168 		x1 = (*eit)->v->x;
169 		y1 = (*eit)->v->y;
170 		x2 = (*eit)->w->x;
171 		y2 = (*eit)->w->y;
172 		if (x1 < x2) {
173 			if ((x < (x1 - rad)) || ((x2 + rad) < x))
174 				continue;
175 		} else if (x2 < x1) {
176 			if ((x < (x2 - rad)) || ((x1 + rad) < x))
177 				continue;
178 		}
179 		if (y1 < y2) {
180 			if ((y < (y1 - rad)) || ((y2 + rad) < y))
181 				continue;
182 		} else if (y2 < y1) {
183 			if ((y < (y2 - rad)) || ((y1 + rad) < y))
184 				continue;
185 		}
186 		s2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
187 		if (s2 < 0.01)
188 			continue;	// degenerate edge
189 		num = (y2 - y1) * (x - x1) - (x2 - x1) * (y - y1);
190 		d2 = num * num / s2;
191 		if ((d2 < best_dist) || !best_edge) {
192 			best_edge = *eit;
193 			best_dist = d2;
194 		}
195 	}
196 
197 	// still only accept it if within a vertex of (x,y)
198 	if (best_dist <= r2)
199 		return best_edge;
200 	return 0;
201 }
202 
cb_Properties()203 void Canvas::cb_Properties ()
204 {
205 	Edge *e;
206 	Graph *g = *gg;
207 	Vertex *v;
208 
209 	v = g->v_selected_head;
210 	e = g->e_selected_head;
211 
212 	if (v && !e) {
213 		// Vertex Properties
214 
215 		wxString str = wxGetTextFromUser (_("Label:"),
216 					_("Vertex Properties"),
217 					v->label, gui);
218 		if (str.IsEmpty ())
219 			return;
220 
221 		// Handle duplicate labels
222 		while (1) {
223 			Vertex *f = g->find (str);
224 			if (!f || (f == v))
225 				break;
226 			str += wxT("-dup");
227 		}
228 
229 		if (str != v->label) {
230 			gui->undoableAction (_("Rename vertex"));
231 			g->rename (v, str);
232 			redraw ();
233 		}
234 	} else if (e) {
235 		// Edge Properties
236 		ParamDialogEdge dlg (gui, _("Edge Properties"), e);
237 		if (dlg.ShowModal () == wxID_CANCEL)
238 			return;
239 
240 		int curr_dir = e->directed ? 1 : 0;
241 		int new_wt = dlg.GetWeight (), new_dir = dlg.GetDirection ();
242 		int new_flow = dlg.GetFlow ();
243 		if ((new_wt == e->weight) && (new_dir == curr_dir)) {
244 			if (e->flow != new_flow) {
245 				e->flow = new_flow;
246 				redraw ();
247 			}
248 			return;
249 		}
250 		if (new_wt == e->weight)
251 			gui->undoableAction (_("Change edge direction"));
252 		else if (new_dir == curr_dir)
253 			gui->undoableAction (_("Change edge weight"));
254 		else
255 			gui->undoableAction (_("Change edge weight and direction"));
256 		e->weight = new_wt;
257 		if (new_dir == 0)
258 			e->directed = false;
259 		else if (new_dir == 1)
260 			e->directed = true;
261 		else {
262 			e->directed = true;
263 			Vertex *tmp = e->v;
264 			e->v = e->w;
265 			e->w = tmp;
266 		}
267 		redraw ();
268 	}
269 }
270 
Canvas(GTFrame * gui,Graph ** g)271 Canvas::Canvas (GTFrame *gui, Graph **g)
272 	: wxScrolledWindow (gui, -1, wxPoint (0, 0), wxDefaultSize,
273 //			0),
274 			wxNO_FULL_REPAINT_ON_RESIZE | wxCLIP_CHILDREN),
275 		gui (gui), gg (g)
276 {
277 	vertex_mode = true;
278 	do_labels = true;
279 	do_weights = do_flows = false;
280 
281 	white = wxColour (255, 255, 255);
282 	black = wxColour (0, 0, 0);
283 
284 	// Selection colours
285 	const char *cols[] = {
286 		"red", "blue", "magenta", "green",
287 		"brown", "cyan", "black"
288 	};
289 	for (unsigned int i = 0; i < (sizeof (cols) / sizeof (cols[0])); ++i) {
290 		wxColour col = wxTheColourDatabase->Find
291 					(wxString (cols[i], wxConvUTF8));
292 		selection_colours.push_back (col);
293 	}
294 	red = selection_colours[0];
295 
296 
297 	int pointSize = Canvas::font_height;
298 	normal_font = new wxFont (pointSize, wxFONTFAMILY_DEFAULT,
299 				wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
300 	bold_font = new wxFont (pointSize, wxFONTFAMILY_DEFAULT,
301 				wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
302 
303 	SetScrollRate (10, 10);
304 
305 }
306 
307 #define USE_WXMEMORYDC
308 
309 #ifdef USE_WXMEMORYDC
310 #include "wx/dcmemory.h"
311 #endif
312 
OnPaint(wxPaintEvent & event)313 void Canvas::OnPaint (wxPaintEvent &event)
314 {
315 #ifndef USE_WXMEMORYDC
316 	wxPaintDC dc (this);
317 #else
318 	wxPaintDC dest_dc (this);
319 	int blit_width, blit_height;
320 	GetClientSize (&blit_width, &blit_height);
321 	wxBitmap bmp_blit (blit_width, blit_height);
322 	wxMemoryDC dc;
323 	dc.SelectObject (bmp_blit);
324 #endif
325 	Graph *g = *gg;
326 
327 #ifndef USE_WXMEMORYDC
328 	DoPrepareDC (dc);
329 #else
330 	DoPrepareDC (dc);
331 	DoPrepareDC (dest_dc);
332 #endif
333 
334 	dc.SetBackground (wxBrush (white));
335 	dc.Clear ();
336 
337 	Graph::e_const_iterator eit;
338 	for (eit = g->e_begin (); eit != g->e_end (); ++eit) {
339 		Edge *e = *eit;
340 		bool curved = false;
341 		if (e->directed) {
342 			if (g->find (e->w, e->v, true))
343 				curved = true;
344 		}
345 		draw (dc, e, curved);
346 	}
347 
348 	Graph::v_const_iterator vit;
349 	int maxx = 0, maxy = 0;
350 	for (vit = g->v_begin (); vit != g->v_end (); ++vit) {
351 		Vertex *v = *vit;
352 		draw (dc, v);
353 		if (v->x > maxx)
354 			maxx = v->x;
355 		if (v->y > maxy)
356 			maxy = v->y;
357 	}
358 #ifdef USE_WXMEMORYDC
359 	// Blit!
360 	dest_dc.Blit (0, 0, blit_width, blit_height, &dc, 0, 0);
361 #endif
362 
363 	SetVirtualSize (maxx + 30, maxy + 30);
364 }
365 
OnEraseBackground(wxEraseEvent & event)366 void Canvas::OnEraseBackground (wxEraseEvent &event)
367 {
368 	wxDC *dc = event.GetDC ();
369 
370 	dc->SetBackground (wxBrush (white));
371 	dc->Clear ();
372 }
373 
OnClick(wxMouseEvent & event)374 void Canvas::OnClick (wxMouseEvent &event)
375 {
376 	Graph *g = *gg;
377 	Vertex *v;
378 	int m_x, m_y;
379 
380 	bool left_click = event.LeftDown () || event.LeftDClick (),
381 		shifted = event.ShiftDown (),
382 		double_click = event.ButtonDClick ();
383 
384 	CalcUnscrolledPosition (int (event.m_x), int (event.m_y), &m_x, &m_y);
385 	v = findVertex (m_x, m_y);
386 
387 	// Assumptions: [lr]-click (or double click)
388 
389 	if (vertex_mode && left_click) {
390 		if (!shifted) {
391 			// no shift key
392 			if (!double_click) {
393 				// Single click
394 				g->unselect_all ();
395 				if (v)
396 					g->select (v);
397 				redraw ();
398 				return;
399 			} else {
400 				// Double click
401 				if (v)
402 					cb_Properties ();
403 				else {
404 					gui->undoableAction (_("Add vertex"));
405 					g->add (new Vertex ("", m_x, m_y));
406 					redraw ();
407 				}
408 				return;
409 			}
410 		} else {
411 			// shift key
412 			if (!double_click) {
413 				if (v) {
414 					v->selected ?
415 						g->unselect (v) :
416 						g->select (v);
417 					redraw ();
418 				}
419 			}
420 			return;
421 		}
422 	} else if (vertex_mode && !left_click)
423 		return;		// do nothing
424 
425 	// edge mode
426 	if (!v) {
427 		Edge *e = findEdge (m_x, m_y);
428 		if (!e) {
429 			if (left_click && !shifted) {
430 				g->unselect_all ();
431 				redraw ();
432 			}
433 			return;
434 		}
435 		// clicking on an edge
436 		if (!double_click) {
437 			// Single click
438 			if (left_click) {
439 				if (!shifted) {
440 					g->unselect_all ();
441 					g->select (e);
442 				} else {
443 					e->selected ?
444 						g->unselect (e) :
445 						g->select (e);
446 				}
447 			} else
448 				e->cycle_orientations ();
449 		} else {
450 			// Double click
451 			if (left_click)
452 				cb_Properties ();
453 		}
454 		redraw ();
455 		return;
456 	}
457 	if (!left_click)
458 		return;
459 	if (!v->selected) {
460 		Vertex *valt = g->v_selected_head;
461 		if (!valt) {
462 			g->select (v);
463 			redraw ();
464 			return;
465 		}
466 		if (!g->are_adjacent (valt, v)) {
467 			gui->undoableAction (_("Add edge"));
468 			g->add (new Edge (valt, v));
469 		}
470 		g->unselect_all ();
471 		g->select (v);
472 		redraw ();
473 		return;
474 	}
475 	g->unselect (v);
476 	redraw ();
477 }
478 
OnMouseMove(wxMouseEvent & event)479 void Canvas::OnMouseMove (wxMouseEvent &event)
480 {
481 	Graph *g = *gg;
482 	Vertex *v;
483 	int m_x, m_y;
484 
485 	CalcUnscrolledPosition (int (event.m_x), int (event.m_y), &m_x, &m_y);
486 
487 	if (event.Moving ()) {
488 		// Button not down => nothing to do
489 		motion_last_x = -1;
490 		motion_last_y = -1;
491 		return;
492 	}
493 
494 	if (motion_last_x == -1) {
495 		// First motion event; record start only
496 		motion_last_x = m_x;
497 		motion_last_y = m_y;
498 		return;
499 	}
500 
501 	// Move stuff
502 	for (v = g->v_selected_head; v; v = v->next) {
503 		v->x += (m_x - motion_last_x);
504 		v->y += (m_y - motion_last_y);
505 	}
506 
507 	motion_last_x = m_x;
508 	motion_last_y = m_y;
509 
510 	redraw ();
511 }
512 
OnKeyPress(wxKeyEvent & event)513 void Canvas::OnKeyPress (wxKeyEvent &event)
514 {
515 	Graph *g = *gg;
516 
517 	if (!event.HasModifiers () && (event.GetKeyCode () == WXK_DELETE)) {
518 		// delete selected objects
519 		// TODO: make this message more accurate
520 		gui->undoableAction (_("Delete objects"));
521 
522 		Edge *prevE, *e = g->e_selected_head;
523 		while (e) {
524 			prevE = e;
525 			e = e->next;
526 			g->remove (prevE);
527 		}
528 
529 		Vertex *prevV, *v = g->v_selected_head;
530 		while (v) {
531 			prevV = v;
532 			v = v->next;
533 			g->remove (prevV);
534 		}
535 
536 		redraw ();
537 		return;
538 	}
539 
540 	if (event.GetKeyCode () == ' ') {
541 		gui->toggleMode ();
542 		return;
543 	}
544 
545 	// We don't want it - pass it back up
546 	event.Skip ();
547 }
548 
redraw()549 void Canvas::redraw ()
550 {
551 	Refresh ();
552 	Update ();
553 }
554 
setVertexMode(bool v_mode)555 void Canvas::setVertexMode (bool v_mode)
556 {
557 	vertex_mode = v_mode;
558 }
559 
setEdgeMode()560 void Canvas::setEdgeMode ()
561 {
562 	vertex_mode = false;
563 }
564 
setParam(bool labels,bool weights,bool flows)565 void Canvas::setParam (bool labels, bool weights, bool flows)
566 {
567 	do_labels = labels;
568 	do_weights = weights;
569 	do_flows = flows;
570 }
571