]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/ogl/basic2.cpp
e12fa3e07caaaa6a653e1be1b882d38bd711027e
[wxWidgets.git] / contrib / src / ogl / basic2.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: basic2.cpp
3 // Purpose: Basic OGL classes (2)
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 12/07/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "basicp.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include <wx/wx.h>
25 #endif
26
27 #include <wx/wxexpr.h>
28
29 #ifdef new
30 #undef new
31 #endif
32
33 #if wxUSE_IOSTREAMH
34 #include <iostream.h>
35 #else
36 #include <iostream>
37 #endif
38
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <math.h>
42
43 #include <wx/ogl/basic.h>
44 #include <wx/ogl/basicp.h>
45 #include <wx/ogl/composit.h>
46 #include <wx/ogl/lines.h>
47 #include <wx/ogl/canvas.h>
48 #include <wx/ogl/divided.h>
49 #include <wx/ogl/misc.h>
50
51 // Control point types
52 // Rectangle and most other shapes
53 #define CONTROL_POINT_VERTICAL 1
54 #define CONTROL_POINT_HORIZONTAL 2
55 #define CONTROL_POINT_DIAGONAL 3
56
57 // Line
58 #define CONTROL_POINT_ENDPOINT_TO 4
59 #define CONTROL_POINT_ENDPOINT_FROM 5
60 #define CONTROL_POINT_LINE 6
61
62 // Two stage construction: need to call Create
63 IMPLEMENT_DYNAMIC_CLASS(wxPolygonShape, wxShape)
64
65 wxPolygonShape::wxPolygonShape()
66 {
67 m_points = NULL;
68 m_originalPoints = NULL;
69 }
70
71 void wxPolygonShape::Create(wxList *the_points)
72 {
73 ClearPoints();
74
75 m_originalPoints = the_points;
76
77 // Duplicate the list of points
78 m_points = new wxList;
79
80 wxNode *node = the_points->First();
81 while (node)
82 {
83 wxRealPoint *point = (wxRealPoint *)node->Data();
84 wxRealPoint *new_point = new wxRealPoint(point->x, point->y);
85 m_points->Append((wxObject*) new_point);
86 node = node->Next();
87 }
88 CalculateBoundingBox();
89 m_originalWidth = m_boundWidth;
90 m_originalHeight = m_boundHeight;
91 SetDefaultRegionSize();
92 }
93
94 wxPolygonShape::~wxPolygonShape()
95 {
96 ClearPoints();
97 }
98
99 void wxPolygonShape::ClearPoints()
100 {
101 if (m_points)
102 {
103 wxNode *node = m_points->First();
104 while (node)
105 {
106 wxRealPoint *point = (wxRealPoint *)node->Data();
107 delete point;
108 delete node;
109 node = m_points->First();
110 }
111 delete m_points;
112 m_points = NULL;
113 }
114 if (m_originalPoints)
115 {
116 wxNode *node = m_originalPoints->First();
117 while (node)
118 {
119 wxRealPoint *point = (wxRealPoint *)node->Data();
120 delete point;
121 delete node;
122 node = m_originalPoints->First();
123 }
124 delete m_originalPoints;
125 m_originalPoints = NULL;
126 }
127 }
128
129
130 // Width and height. Centre of object is centre of box.
131 void wxPolygonShape::GetBoundingBoxMin(double *width, double *height)
132 {
133 *width = m_boundWidth;
134 *height = m_boundHeight;
135 }
136
137 void wxPolygonShape::CalculateBoundingBox()
138 {
139 // Calculate bounding box at construction (and presumably resize) time
140 double left = 10000;
141 double right = -10000;
142 double top = 10000;
143 double bottom = -10000;
144
145 wxNode *node = m_points->First();
146 while (node)
147 {
148 wxRealPoint *point = (wxRealPoint *)node->Data();
149 if (point->x < left) left = point->x;
150 if (point->x > right) right = point->x;
151
152 if (point->y < top) top = point->y;
153 if (point->y > bottom) bottom = point->y;
154
155 node = node->Next();
156 }
157 m_boundWidth = right - left;
158 m_boundHeight = bottom - top;
159 }
160
161 // Recalculates the centre of the polygon, and
162 // readjusts the point offsets accordingly.
163 // Necessary since the centre of the polygon
164 // is expected to be the real centre of the bounding
165 // box.
166 void wxPolygonShape::CalculatePolygonCentre()
167 {
168 double left = 10000;
169 double right = -10000;
170 double top = 10000;
171 double bottom = -10000;
172
173 wxNode *node = m_points->First();
174 while (node)
175 {
176 wxRealPoint *point = (wxRealPoint *)node->Data();
177 if (point->x < left) left = point->x;
178 if (point->x > right) right = point->x;
179
180 if (point->y < top) top = point->y;
181 if (point->y > bottom) bottom = point->y;
182
183 node = node->Next();
184 }
185 double bwidth = right - left;
186 double bheight = bottom - top;
187
188 double newCentreX = (double)(left + (bwidth/2.0));
189 double newCentreY = (double)(top + (bheight/2.0));
190
191 node = m_points->First();
192 while (node)
193 {
194 wxRealPoint *point = (wxRealPoint *)node->Data();
195 point->x -= newCentreX;
196 point->y -= newCentreY;
197 node = node->Next();
198 }
199 m_xpos += newCentreX;
200 m_ypos += newCentreY;
201 }
202
203 bool PolylineHitTest(double n, double xvec[], double yvec[],
204 double x1, double y1, double x2, double y2)
205 {
206 bool isAHit = FALSE;
207 int i;
208 double lastx = xvec[0];
209 double lasty = yvec[0];
210
211 double min_ratio = 1.0;
212 double line_ratio;
213 double other_ratio;
214
215 // char buf[300];
216 for (i = 1; i < n; i++)
217 {
218 oglCheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i],
219 &line_ratio, &other_ratio);
220 if (line_ratio != 1.0)
221 isAHit = TRUE;
222 // sprintf(buf, "Line ratio = %.2f, other ratio = %.2f\n", line_ratio, other_ratio);
223 // ClipsErrorFunction(buf);
224 lastx = xvec[i];
225 lasty = yvec[i];
226
227 if (line_ratio < min_ratio)
228 min_ratio = line_ratio;
229 }
230
231 // Do last (implicit) line if last and first doubles are not identical
232 if (!(xvec[0] == lastx && yvec[0] == lasty))
233 {
234 oglCheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0],
235 &line_ratio, &other_ratio);
236 if (line_ratio != 1.0)
237 isAHit = TRUE;
238 // sprintf(buf, "Line ratio = %.2f, other ratio = %.2f\n", line_ratio, other_ratio);
239 // ClipsErrorFunction(buf);
240
241 if (line_ratio < min_ratio)
242 min_ratio = line_ratio;
243 }
244 // ClipsErrorFunction("\n");
245 return isAHit;
246 }
247
248 bool wxPolygonShape::HitTest(double x, double y, int *attachment, double *distance)
249 {
250 // Imagine four lines radiating from this point. If all of these lines hit the polygon,
251 // we're inside it, otherwise we're not. Obviously we'd need more radiating lines
252 // to be sure of correct results for very strange (concave) shapes.
253 double endPointsX[4];
254 double endPointsY[4];
255 // North
256 endPointsX[0] = x;
257 endPointsY[0] = (double)(y - 1000.0);
258 // East
259 endPointsX[1] = (double)(x + 1000.0);
260 endPointsY[1] = y;
261 // South
262 endPointsX[2] = x;
263 endPointsY[2] = (double)(y + 1000.0);
264 // West
265 endPointsX[3] = (double)(x - 1000.0);
266 endPointsY[3] = y;
267
268 // Store polygon points in an array
269 int np = m_points->Number();
270 double *xpoints = new double[np];
271 double *ypoints = new double[np];
272 wxNode *node = m_points->First();
273 int i = 0;
274 while (node)
275 {
276 wxRealPoint *point = (wxRealPoint *)node->Data();
277 xpoints[i] = point->x + m_xpos;
278 ypoints[i] = point->y + m_ypos;
279 node = node->Next();
280 i ++;
281 }
282
283 // We assume it's inside the polygon UNLESS one or more
284 // lines don't hit the outline.
285 bool isContained = TRUE;
286
287 int noPoints = 4;
288 for (i = 0; i < noPoints; i++)
289 {
290 if (!PolylineHitTest(np, xpoints, ypoints, x, y, endPointsX[i], endPointsY[i]))
291 isContained = FALSE;
292 }
293 /*
294 if (isContained)
295 ClipsErrorFunction("It's a hit!\n");
296 else
297 ClipsErrorFunction("No hit.\n");
298 */
299 delete[] xpoints;
300 delete[] ypoints;
301
302 if (!isContained)
303 return FALSE;
304
305 int nearest_attachment = 0;
306
307 // If a hit, check the attachment points within the object.
308 int n = GetNumberOfAttachments();
309 double nearest = 999999.0;
310
311 for (i = 0; i < n; i++)
312 {
313 double xp, yp;
314 if (GetAttachmentPositionEdge(i, &xp, &yp))
315 {
316 double l = (double)sqrt(((xp - x) * (xp - x)) +
317 ((yp - y) * (yp - y)));
318 if (l < nearest)
319 {
320 nearest = l;
321 nearest_attachment = i;
322 }
323 }
324 }
325 *attachment = nearest_attachment;
326 *distance = nearest;
327 return TRUE;
328 }
329
330 // Really need to be able to reset the shape! Otherwise, if the
331 // points ever go to zero, we've lost it, and can't resize.
332 void wxPolygonShape::SetSize(double new_width, double new_height, bool recursive)
333 {
334 SetAttachmentSize(new_width, new_height);
335
336 // Multiply all points by proportion of new size to old size
337 double x_proportion = (double)(fabs(new_width/m_originalWidth));
338 double y_proportion = (double)(fabs(new_height/m_originalHeight));
339
340 wxNode *node = m_points->First();
341 wxNode *original_node = m_originalPoints->First();
342 while (node && original_node)
343 {
344 wxRealPoint *point = (wxRealPoint *)node->Data();
345 wxRealPoint *original_point = (wxRealPoint *)original_node->Data();
346
347 point->x = (original_point->x * x_proportion);
348 point->y = (original_point->y * y_proportion);
349
350 node = node->Next();
351 original_node = original_node->Next();
352 }
353
354 // CalculateBoundingBox();
355 m_boundWidth = (double)fabs(new_width);
356 m_boundHeight = (double)fabs(new_height);
357 SetDefaultRegionSize();
358 }
359
360 // Make the original points the same as the working points
361 void wxPolygonShape::UpdateOriginalPoints()
362 {
363 if (!m_originalPoints) m_originalPoints = new wxList;
364 wxNode *original_node = m_originalPoints->First();
365 while (original_node)
366 {
367 wxNode *next_node = original_node->Next();
368 wxRealPoint *original_point = (wxRealPoint *)original_node->Data();
369 delete original_point;
370 delete original_node;
371
372 original_node = next_node;
373 }
374
375 wxNode *node = m_points->First();
376 while (node)
377 {
378 wxRealPoint *point = (wxRealPoint *)node->Data();
379 wxRealPoint *original_point = new wxRealPoint(point->x, point->y);
380 m_originalPoints->Append((wxObject*) original_point);
381
382 node = node->Next();
383 }
384 CalculateBoundingBox();
385 m_originalWidth = m_boundWidth;
386 m_originalHeight = m_boundHeight;
387 }
388
389 void wxPolygonShape::AddPolygonPoint(int pos)
390 {
391 wxNode *node = m_points->Nth(pos);
392 if (!node) node = m_points->First();
393 wxRealPoint *firstPoint = (wxRealPoint *)node->Data();
394
395 wxNode *node2 = m_points->Nth(pos + 1);
396 if (!node2) node2 = m_points->First();
397 wxRealPoint *secondPoint = (wxRealPoint *)node2->Data();
398
399 double x = (double)((secondPoint->x - firstPoint->x)/2.0 + firstPoint->x);
400 double y = (double)((secondPoint->y - firstPoint->y)/2.0 + firstPoint->y);
401 wxRealPoint *point = new wxRealPoint(x, y);
402
403 if (pos >= (m_points->Number() - 1))
404 m_points->Append((wxObject*) point);
405 else
406 m_points->Insert(node2, (wxObject*) point);
407
408 UpdateOriginalPoints();
409
410 if (m_selected)
411 {
412 DeleteControlPoints();
413 MakeControlPoints();
414 }
415 }
416
417 void wxPolygonShape::DeletePolygonPoint(int pos)
418 {
419 wxNode *node = m_points->Nth(pos);
420 if (node)
421 {
422 wxRealPoint *point = (wxRealPoint *)node->Data();
423 delete point;
424 delete node;
425 UpdateOriginalPoints();
426 if (m_selected)
427 {
428 DeleteControlPoints();
429 MakeControlPoints();
430 }
431 }
432 }
433
434 // Assume (x1, y1) is centre of box (most generally, line end at box)
435 bool wxPolygonShape::GetPerimeterPoint(double x1, double y1,
436 double x2, double y2,
437 double *x3, double *y3)
438 {
439 int n = m_points->Number();
440
441 // First check for situation where the line is vertical,
442 // and we would want to connect to a point on that vertical --
443 // oglFindEndForPolyline can't cope with this (the arrow
444 // gets drawn to the wrong place).
445 if ((m_attachmentMode == ATTACHMENT_MODE_NONE) && (x1 == x2))
446 {
447 // Look for the point we'd be connecting to. This is
448 // a heuristic...
449 wxNode *node = m_points->First();
450 while (node)
451 {
452 wxRealPoint *point = (wxRealPoint *)node->Data();
453 if (point->x == 0.0)
454 {
455 if ((y2 > y1) && (point->y > 0.0))
456 {
457 *x3 = point->x + m_xpos;
458 *y3 = point->y + m_ypos;
459 return TRUE;
460 }
461 else if ((y2 < y1) && (point->y < 0.0))
462 {
463 *x3 = point->x + m_xpos;
464 *y3 = point->y + m_ypos;
465 return TRUE;
466 }
467 }
468 node = node->Next();
469 }
470 }
471
472 double *xpoints = new double[n];
473 double *ypoints = new double[n];
474
475 wxNode *node = m_points->First();
476 int i = 0;
477 while (node)
478 {
479 wxRealPoint *point = (wxRealPoint *)node->Data();
480 xpoints[i] = point->x + m_xpos;
481 ypoints[i] = point->y + m_ypos;
482 node = node->Next();
483 i ++;
484 }
485
486 oglFindEndForPolyline(n, xpoints, ypoints,
487 x1, y1, x2, y2, x3, y3);
488
489 delete[] xpoints;
490 delete[] ypoints;
491
492 return TRUE;
493 }
494
495 void wxPolygonShape::OnDraw(wxDC& dc)
496 {
497 int n = m_points->Number();
498 wxPoint *intPoints = new wxPoint[n];
499 int i;
500 for (i = 0; i < n; i++)
501 {
502 wxRealPoint* point = (wxRealPoint*) m_points->Nth(i)->Data();
503 intPoints[i].x = WXROUND(point->x);
504 intPoints[i].y = WXROUND(point->y);
505 }
506
507 if (m_shadowMode != SHADOW_NONE)
508 {
509 if (m_shadowBrush)
510 dc.SetBrush(* m_shadowBrush);
511 dc.SetPen(* g_oglTransparentPen);
512
513 dc.DrawPolygon(n, intPoints, WXROUND(m_xpos + m_shadowOffsetX), WXROUND(m_ypos + m_shadowOffsetY));
514 }
515
516 if (m_pen)
517 {
518 if (m_pen->GetWidth() == 0)
519 dc.SetPen(* g_oglTransparentPen);
520 else
521 dc.SetPen(* m_pen);
522 }
523 if (m_brush)
524 dc.SetBrush(* m_brush);
525 dc.DrawPolygon(n, intPoints, WXROUND(m_xpos), WXROUND(m_ypos));
526
527 delete[] intPoints;
528 }
529
530 void wxPolygonShape::OnDrawOutline(wxDC& dc, double x, double y, double w, double h)
531 {
532 dc.SetBrush(* wxTRANSPARENT_BRUSH);
533 // Multiply all points by proportion of new size to old size
534 double x_proportion = (double)(fabs(w/m_originalWidth));
535 double y_proportion = (double)(fabs(h/m_originalHeight));
536
537 int n = m_originalPoints->Number();
538 wxPoint *intPoints = new wxPoint[n];
539 int i;
540 for (i = 0; i < n; i++)
541 {
542 wxRealPoint* point = (wxRealPoint*) m_originalPoints->Nth(i)->Data();
543 intPoints[i].x = WXROUND(x_proportion * point->x);
544 intPoints[i].y = WXROUND(y_proportion * point->y);
545 }
546 dc.DrawPolygon(n, intPoints, WXROUND(x), WXROUND(y));
547 delete[] intPoints;
548 }
549
550 // Make as many control points as there are vertices.
551 void wxPolygonShape::MakeControlPoints()
552 {
553 wxNode *node = m_points->First();
554 while (node)
555 {
556 wxRealPoint *point = (wxRealPoint *)node->Data();
557 wxPolygonControlPoint *control = new wxPolygonControlPoint(m_canvas, this, CONTROL_POINT_SIZE,
558 point, point->x, point->y);
559 m_canvas->AddShape(control);
560 m_controlPoints.Append(control);
561 node = node->Next();
562 }
563 }
564
565 void wxPolygonShape::ResetControlPoints()
566 {
567 wxNode *node = m_points->First();
568 wxNode *controlPointNode = m_controlPoints.First();
569 while (node && controlPointNode)
570 {
571 wxRealPoint *point = (wxRealPoint *)node->Data();
572 wxPolygonControlPoint *controlPoint = (wxPolygonControlPoint *)controlPointNode->Data();
573
574 controlPoint->m_xoffset = point->x;
575 controlPoint->m_yoffset = point->y;
576 controlPoint->m_polygonVertex = point;
577
578 node = node->Next();
579 controlPointNode = controlPointNode->Next();
580 }
581 }
582
583
584 #if wxUSE_PROLOGIO
585 void wxPolygonShape::WriteAttributes(wxExpr *clause)
586 {
587 wxShape::WriteAttributes(clause);
588
589 clause->AddAttributeValue(wxT("x"), m_xpos);
590 clause->AddAttributeValue(wxT("y"), m_ypos);
591
592 // Make a list of lists for the coordinates
593 wxExpr *list = new wxExpr(wxExprList);
594 wxNode *node = m_points->First();
595 while (node)
596 {
597 wxRealPoint *point = (wxRealPoint *)node->Data();
598 wxExpr *point_list = new wxExpr(wxExprList);
599 wxExpr *x_expr = new wxExpr((double)point->x);
600 wxExpr *y_expr = new wxExpr((double)point->y);
601
602 point_list->Append(x_expr);
603 point_list->Append(y_expr);
604 list->Append(point_list);
605
606 node = node->Next();
607 }
608 clause->AddAttributeValue(wxT("points"), list);
609
610 // Save the original (unscaled) points
611 list = new wxExpr(wxExprList);
612 node = m_originalPoints->First();
613 while (node)
614 {
615 wxRealPoint *point = (wxRealPoint *)node->Data();
616 wxExpr *point_list = new wxExpr(wxExprList);
617 wxExpr *x_expr = new wxExpr((double) point->x);
618 wxExpr *y_expr = new wxExpr((double) point->y);
619 point_list->Append(x_expr);
620 point_list->Append(y_expr);
621 list->Append(point_list);
622
623 node = node->Next();
624 }
625 clause->AddAttributeValue(wxT("m_originalPoints"), list);
626 }
627
628 void wxPolygonShape::ReadAttributes(wxExpr *clause)
629 {
630 wxShape::ReadAttributes(clause);
631
632 // Read a list of lists
633 m_points = new wxList;
634 m_originalPoints = new wxList;
635
636 wxExpr *points_list = NULL;
637 clause->AssignAttributeValue(wxT("points"), &points_list);
638
639 // If no points_list, don't crash!! Assume a diamond instead.
640 double the_height = 100.0;
641 double the_width = 100.0;
642 if (!points_list)
643 {
644 wxRealPoint *point = new wxRealPoint(0.0, (-the_height/2));
645 m_points->Append((wxObject*) point);
646
647 point = new wxRealPoint((the_width/2), 0.0);
648 m_points->Append((wxObject*) point);
649
650 point = new wxRealPoint(0.0, (the_height/2));
651 m_points->Append((wxObject*) point);
652
653 point = new wxRealPoint((-the_width/2), 0.0);
654 m_points->Append((wxObject*) point);
655
656 point = new wxRealPoint(0.0, (-the_height/2));
657 m_points->Append((wxObject*) point);
658 }
659 else
660 {
661 wxExpr *node = points_list->value.first;
662
663 while (node)
664 {
665 wxExpr *xexpr = node->value.first;
666 long x = xexpr->IntegerValue();
667
668 wxExpr *yexpr = xexpr->next;
669 long y = yexpr->IntegerValue();
670
671 wxRealPoint *point = new wxRealPoint((double)x, (double)y);
672 m_points->Append((wxObject*) point);
673
674 node = node->next;
675 }
676 }
677
678 points_list = NULL;
679 clause->AssignAttributeValue(wxT("m_originalPoints"), &points_list);
680
681 // If no points_list, don't crash!! Assume a diamond instead.
682 if (!points_list)
683 {
684 wxRealPoint *point = new wxRealPoint(0.0, (-the_height/2));
685 m_originalPoints->Append((wxObject*) point);
686
687 point = new wxRealPoint((the_width/2), 0.0);
688 m_originalPoints->Append((wxObject*) point);
689
690 point = new wxRealPoint(0.0, (the_height/2));
691 m_originalPoints->Append((wxObject*) point);
692
693 point = new wxRealPoint((-the_width/2), 0.0);
694 m_originalPoints->Append((wxObject*) point);
695
696 point = new wxRealPoint(0.0, (-the_height/2));
697 m_originalPoints->Append((wxObject*) point);
698
699 m_originalWidth = the_width;
700 m_originalHeight = the_height;
701 }
702 else
703 {
704 wxExpr *node = points_list->value.first;
705 double min_x = 1000;
706 double min_y = 1000;
707 double max_x = -1000;
708 double max_y = -1000;
709 while (node)
710 {
711 wxExpr *xexpr = node->value.first;
712 long x = xexpr->IntegerValue();
713
714 wxExpr *yexpr = xexpr->next;
715 long y = yexpr->IntegerValue();
716
717 wxRealPoint *point = new wxRealPoint((double)x, (double)y);
718 m_originalPoints->Append((wxObject*) point);
719
720 if (x < min_x)
721 min_x = (double)x;
722 if (y < min_y)
723 min_y = (double)y;
724 if (x > max_x)
725 max_x = (double)x;
726 if (y > max_y)
727 max_y = (double)y;
728
729 node = node->next;
730 }
731 m_originalWidth = max_x - min_x;
732 m_originalHeight = max_y - min_y;
733 }
734
735 CalculateBoundingBox();
736 }
737 #endif
738
739 void wxPolygonShape::Copy(wxShape& copy)
740 {
741 wxShape::Copy(copy);
742
743 wxASSERT( copy.IsKindOf(CLASSINFO(wxPolygonShape)) );
744
745 wxPolygonShape& polyCopy = (wxPolygonShape&) copy;
746
747 polyCopy.ClearPoints();
748
749 polyCopy.m_points = new wxList;
750 polyCopy.m_originalPoints = new wxList;
751
752 wxNode *node = m_points->First();
753 while (node)
754 {
755 wxRealPoint *point = (wxRealPoint *)node->Data();
756 wxRealPoint *new_point = new wxRealPoint(point->x, point->y);
757 polyCopy.m_points->Append((wxObject*) new_point);
758 node = node->Next();
759 }
760 node = m_originalPoints->First();
761 while (node)
762 {
763 wxRealPoint *point = (wxRealPoint *)node->Data();
764 wxRealPoint *new_point = new wxRealPoint(point->x, point->y);
765 polyCopy.m_originalPoints->Append((wxObject*) new_point);
766 node = node->Next();
767 }
768 polyCopy.m_boundWidth = m_boundWidth;
769 polyCopy.m_boundHeight = m_boundHeight;
770 polyCopy.m_originalWidth = m_originalWidth;
771 polyCopy.m_originalHeight = m_originalHeight;
772 }
773
774 int wxPolygonShape::GetNumberOfAttachments() const
775 {
776 int maxN = (m_points ? (m_points->Number() - 1) : 0);
777 wxNode *node = m_attachmentPoints.First();
778 while (node)
779 {
780 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
781 if (point->m_id > maxN)
782 maxN = point->m_id;
783 node = node->Next();
784 }
785 return maxN+1;;
786 }
787
788 bool wxPolygonShape::GetAttachmentPosition(int attachment, double *x, double *y,
789 int nth, int no_arcs, wxLineShape *line)
790 {
791 if ((m_attachmentMode == ATTACHMENT_MODE_EDGE) && m_points && attachment < m_points->Number())
792 {
793 wxRealPoint *point = (wxRealPoint *)m_points->Nth(attachment)->Data();
794 *x = point->x + m_xpos;
795 *y = point->y + m_ypos;
796 return TRUE;
797 }
798 else
799 { return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line); }
800 }
801
802 bool wxPolygonShape::AttachmentIsValid(int attachment)
803 {
804 if (!m_points)
805 return FALSE;
806
807 if ((attachment >= 0) && (attachment < m_points->Number()))
808 return TRUE;
809
810 wxNode *node = m_attachmentPoints.First();
811 while (node)
812 {
813 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
814 if (point->m_id == attachment)
815 return TRUE;
816 node = node->Next();
817 }
818 return FALSE;
819 }
820
821 // Rotate about the given axis by the given amount in radians
822 void wxPolygonShape::Rotate(double x, double y, double theta)
823 {
824 double actualTheta = theta-m_rotation;
825
826 // Rotate attachment points
827 double sinTheta = (double)sin(actualTheta);
828 double cosTheta = (double)cos(actualTheta);
829 wxNode *node = m_attachmentPoints.First();
830 while (node)
831 {
832 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
833 double x1 = point->m_x;
834 double y1 = point->m_y;
835 point->m_x = x1*cosTheta - y1*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
836 point->m_y = x1*sinTheta + y1*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
837 node = node->Next();
838 }
839
840 node = m_points->First();
841 while (node)
842 {
843 wxRealPoint *point = (wxRealPoint *)node->Data();
844 double x1 = point->x;
845 double y1 = point->y;
846 point->x = x1*cosTheta - y1*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
847 point->y = x1*sinTheta + y1*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
848 node = node->Next();
849 }
850 node = m_originalPoints->First();
851 while (node)
852 {
853 wxRealPoint *point = (wxRealPoint *)node->Data();
854 double x1 = point->x;
855 double y1 = point->y;
856 point->x = x1*cosTheta - y1*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
857 point->y = x1*sinTheta + y1*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
858 node = node->Next();
859 }
860
861 m_rotation = theta;
862
863 CalculatePolygonCentre();
864 CalculateBoundingBox();
865 ResetControlPoints();
866 }
867
868 // Rectangle object
869
870 IMPLEMENT_DYNAMIC_CLASS(wxRectangleShape, wxShape)
871
872 wxRectangleShape::wxRectangleShape(double w, double h)
873 {
874 m_width = w; m_height = h; m_cornerRadius = 0.0;
875 SetDefaultRegionSize();
876 }
877
878 void wxRectangleShape::OnDraw(wxDC& dc)
879 {
880 double x1 = (double)(m_xpos - m_width/2.0);
881 double y1 = (double)(m_ypos - m_height/2.0);
882
883 if (m_shadowMode != SHADOW_NONE)
884 {
885 if (m_shadowBrush)
886 dc.SetBrush(* m_shadowBrush);
887 dc.SetPen(* g_oglTransparentPen);
888
889 if (m_cornerRadius != 0.0)
890 dc.DrawRoundedRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY),
891 WXROUND(m_width), WXROUND(m_height), m_cornerRadius);
892 else
893 dc.DrawRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY), WXROUND(m_width), WXROUND(m_height));
894 }
895
896 if (m_pen)
897 {
898 if (m_pen->GetWidth() == 0)
899 dc.SetPen(* g_oglTransparentPen);
900 else
901 dc.SetPen(* m_pen);
902 }
903 if (m_brush)
904 dc.SetBrush(* m_brush);
905
906 if (m_cornerRadius != 0.0)
907 dc.DrawRoundedRectangle(WXROUND(x1), WXROUND(y1), WXROUND(m_width), WXROUND(m_height), m_cornerRadius);
908 else
909 dc.DrawRectangle(WXROUND(x1), WXROUND(y1), WXROUND(m_width), WXROUND(m_height));
910 }
911
912 void wxRectangleShape::GetBoundingBoxMin(double *the_width, double *the_height)
913 {
914 *the_width = m_width;
915 *the_height = m_height;
916 }
917
918 void wxRectangleShape::SetSize(double x, double y, bool recursive)
919 {
920 SetAttachmentSize(x, y);
921 m_width = (double)wxMax(x, 1.0);
922 m_height = (double)wxMax(y, 1.0);
923 SetDefaultRegionSize();
924 }
925
926 void wxRectangleShape::SetCornerRadius(double rad)
927 {
928 m_cornerRadius = rad;
929 }
930
931 // Assume (x1, y1) is centre of box (most generally, line end at box)
932 bool wxRectangleShape::GetPerimeterPoint(double x1, double y1,
933 double x2, double y2,
934 double *x3, double *y3)
935 {
936 double bound_x, bound_y;
937 GetBoundingBoxMax(&bound_x, &bound_y);
938 oglFindEndForBox(bound_x, bound_y, m_xpos, m_ypos, x2, y2, x3, y3);
939
940 return TRUE;
941 }
942
943 #if wxUSE_PROLOGIO
944 void wxRectangleShape::WriteAttributes(wxExpr *clause)
945 {
946 wxShape::WriteAttributes(clause);
947 clause->AddAttributeValue(wxT("x"), m_xpos);
948 clause->AddAttributeValue(wxT("y"), m_ypos);
949
950 clause->AddAttributeValue(wxT("width"), m_width);
951 clause->AddAttributeValue(wxT("height"), m_height);
952 if (m_cornerRadius != 0.0)
953 clause->AddAttributeValue(wxT("corner"), m_cornerRadius);
954 }
955
956 void wxRectangleShape::ReadAttributes(wxExpr *clause)
957 {
958 wxShape::ReadAttributes(clause);
959 clause->AssignAttributeValue(wxT("width"), &m_width);
960 clause->AssignAttributeValue(wxT("height"), &m_height);
961 clause->AssignAttributeValue(wxT("corner"), &m_cornerRadius);
962
963 // In case we're reading an old file, set the region's size
964 if (m_regions.Number() == 1)
965 {
966 wxShapeRegion *region = (wxShapeRegion *)m_regions.First()->Data();
967 region->SetSize(m_width, m_height);
968 }
969 }
970 #endif
971
972 void wxRectangleShape::Copy(wxShape& copy)
973 {
974 wxShape::Copy(copy);
975
976 wxASSERT( copy.IsKindOf(CLASSINFO(wxRectangleShape)) );
977
978 wxRectangleShape& rectCopy = (wxRectangleShape&) copy;
979 rectCopy.m_width = m_width;
980 rectCopy.m_height = m_height;
981 rectCopy.m_cornerRadius = m_cornerRadius;
982 }
983
984 int wxRectangleShape::GetNumberOfAttachments() const
985 {
986 return wxShape::GetNumberOfAttachments();
987 }
988
989
990 // There are 4 attachment points on a rectangle - 0 = top, 1 = right, 2 = bottom,
991 // 3 = left.
992 bool wxRectangleShape::GetAttachmentPosition(int attachment, double *x, double *y,
993 int nth, int no_arcs, wxLineShape *line)
994 {
995 return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line);
996 }
997
998 // Text object (no box)
999
1000 IMPLEMENT_DYNAMIC_CLASS(wxTextShape, wxRectangleShape)
1001
1002 wxTextShape::wxTextShape(double width, double height):
1003 wxRectangleShape(width, height)
1004 {
1005 }
1006
1007 void wxTextShape::OnDraw(wxDC& dc)
1008 {
1009 }
1010
1011 void wxTextShape::Copy(wxShape& copy)
1012 {
1013 wxRectangleShape::Copy(copy);
1014 }
1015
1016 #if wxUSE_PROLOGIO
1017 void wxTextShape::WriteAttributes(wxExpr *clause)
1018 {
1019 wxRectangleShape::WriteAttributes(clause);
1020 }
1021 #endif
1022
1023 // Ellipse object
1024
1025 IMPLEMENT_DYNAMIC_CLASS(wxEllipseShape, wxShape)
1026
1027 wxEllipseShape::wxEllipseShape(double w, double h)
1028 {
1029 m_width = w; m_height = h;
1030 SetDefaultRegionSize();
1031 }
1032
1033 void wxEllipseShape::GetBoundingBoxMin(double *w, double *h)
1034 {
1035 *w = m_width; *h = m_height;
1036 }
1037
1038 bool wxEllipseShape::GetPerimeterPoint(double x1, double y1,
1039 double x2, double y2,
1040 double *x3, double *y3)
1041 {
1042 double bound_x, bound_y;
1043 GetBoundingBoxMax(&bound_x, &bound_y);
1044
1045 // oglFindEndForBox(bound_x, bound_y, m_xpos, m_ypos, x2, y2, x3, y3);
1046 oglDrawArcToEllipse(m_xpos, m_ypos, bound_x, bound_y, x2, y2, x1, y1, x3, y3);
1047
1048 return TRUE;
1049 }
1050
1051 void wxEllipseShape::OnDraw(wxDC& dc)
1052 {
1053 if (m_shadowMode != SHADOW_NONE)
1054 {
1055 if (m_shadowBrush)
1056 dc.SetBrush(* m_shadowBrush);
1057 dc.SetPen(* g_oglTransparentPen);
1058 dc.DrawEllipse((long) ((m_xpos - GetWidth()/2) + m_shadowOffsetX),
1059 (long) ((m_ypos - GetHeight()/2) + m_shadowOffsetY),
1060 (long) GetWidth(), (long) GetHeight());
1061 }
1062
1063 if (m_pen)
1064 {
1065 if (m_pen->GetWidth() == 0)
1066 dc.SetPen(* g_oglTransparentPen);
1067 else
1068 dc.SetPen(* m_pen);
1069 }
1070 if (m_brush)
1071 dc.SetBrush(* m_brush);
1072 dc.DrawEllipse((long) (m_xpos - GetWidth()/2), (long) (m_ypos - GetHeight()/2), (long) GetWidth(), (long) GetHeight());
1073 }
1074
1075 void wxEllipseShape::SetSize(double x, double y, bool recursive)
1076 {
1077 SetAttachmentSize(x, y);
1078 m_width = x;
1079 m_height = y;
1080 SetDefaultRegionSize();
1081 }
1082
1083 #if wxUSE_PROLOGIO
1084 void wxEllipseShape::WriteAttributes(wxExpr *clause)
1085 {
1086 wxShape::WriteAttributes(clause);
1087 clause->AddAttributeValue(wxT("x"), m_xpos);
1088 clause->AddAttributeValue(wxT("y"), m_ypos);
1089
1090 clause->AddAttributeValue(wxT("width"), m_width);
1091 clause->AddAttributeValue(wxT("height"), m_height);
1092 }
1093
1094 void wxEllipseShape::ReadAttributes(wxExpr *clause)
1095 {
1096 wxShape::ReadAttributes(clause);
1097 clause->AssignAttributeValue(wxT("width"), &m_width);
1098 clause->AssignAttributeValue(wxT("height"), &m_height);
1099
1100 // In case we're reading an old file, set the region's size
1101 if (m_regions.Number() == 1)
1102 {
1103 wxShapeRegion *region = (wxShapeRegion *)m_regions.First()->Data();
1104 region->SetSize(m_width, m_height);
1105 }
1106 }
1107 #endif
1108
1109 void wxEllipseShape::Copy(wxShape& copy)
1110 {
1111 wxShape::Copy(copy);
1112
1113 wxASSERT( copy.IsKindOf(CLASSINFO(wxEllipseShape)) );
1114
1115 wxEllipseShape& ellipseCopy = (wxEllipseShape&) copy;
1116
1117 ellipseCopy.m_width = m_width;
1118 ellipseCopy.m_height = m_height;
1119 }
1120
1121 int wxEllipseShape::GetNumberOfAttachments() const
1122 {
1123 return wxShape::GetNumberOfAttachments();
1124 }
1125
1126 // There are 4 attachment points on an ellipse - 0 = top, 1 = right, 2 = bottom,
1127 // 3 = left.
1128 bool wxEllipseShape::GetAttachmentPosition(int attachment, double *x, double *y,
1129 int nth, int no_arcs, wxLineShape *line)
1130 {
1131 if (m_attachmentMode == ATTACHMENT_MODE_BRANCHING)
1132 return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line);
1133
1134 if (m_attachmentMode != ATTACHMENT_MODE_NONE)
1135 {
1136 double top = (double)(m_ypos + m_height/2.0);
1137 double bottom = (double)(m_ypos - m_height/2.0);
1138 double left = (double)(m_xpos - m_width/2.0);
1139 double right = (double)(m_xpos + m_width/2.0);
1140
1141 int physicalAttachment = LogicalToPhysicalAttachment(attachment);
1142
1143 switch (physicalAttachment)
1144 {
1145 case 0:
1146 {
1147 if (m_spaceAttachments)
1148 *x = left + (nth + 1)*m_width/(no_arcs + 1);
1149 else *x = m_xpos;
1150 *y = top;
1151 // We now have the point on the bounding box: but get the point on the ellipse
1152 // by imagining a vertical line from (*x, m_ypos - m_height- 500) to (*x, m_ypos) intersecting
1153 // the ellipse.
1154 oglDrawArcToEllipse(m_xpos, m_ypos, m_width, m_height, *x, (double)(m_ypos-m_height-500), *x, m_ypos, x, y);
1155 break;
1156 }
1157 case 1:
1158 {
1159 *x = right;
1160 if (m_spaceAttachments)
1161 *y = bottom + (nth + 1)*m_height/(no_arcs + 1);
1162 else *y = m_ypos;
1163 oglDrawArcToEllipse(m_xpos, m_ypos, m_width, m_height, (double)(m_xpos+m_width+500), *y, m_xpos, *y, x, y);
1164 break;
1165 }
1166 case 2:
1167 {
1168 if (m_spaceAttachments)
1169 *x = left + (nth + 1)*m_width/(no_arcs + 1);
1170 else *x = m_xpos;
1171 *y = bottom;
1172 oglDrawArcToEllipse(m_xpos, m_ypos, m_width, m_height, *x, (double)(m_ypos+m_height+500), *x, m_ypos, x, y);
1173 break;
1174 }
1175 case 3:
1176 {
1177 *x = left;
1178 if (m_spaceAttachments)
1179 *y = bottom + (nth + 1)*m_height/(no_arcs + 1);
1180 else *y = m_ypos;
1181 oglDrawArcToEllipse(m_xpos, m_ypos, m_width, m_height, (double)(m_xpos-m_width-500), *y, m_xpos, *y, x, y);
1182 break;
1183 }
1184 default:
1185 {
1186 return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line);
1187 break;
1188 }
1189 }
1190 return TRUE;
1191 }
1192 else
1193 { *x = m_xpos; *y = m_ypos; return TRUE; }
1194 }
1195
1196
1197 // Circle object
1198 IMPLEMENT_DYNAMIC_CLASS(wxCircleShape, wxEllipseShape)
1199
1200 wxCircleShape::wxCircleShape(double diameter):wxEllipseShape(diameter, diameter)
1201 {
1202 SetMaintainAspectRatio(TRUE);
1203 }
1204
1205 void wxCircleShape::Copy(wxShape& copy)
1206 {
1207 wxEllipseShape::Copy(copy);
1208 }
1209
1210 bool wxCircleShape::GetPerimeterPoint(double x1, double y1,
1211 double x2, double y2,
1212 double *x3, double *y3)
1213 {
1214 oglFindEndForCircle(m_width/2,
1215 m_xpos, m_ypos, // Centre of circle
1216 x2, y2, // Other end of line
1217 x3, y3);
1218
1219 return TRUE;
1220 }
1221
1222 // Control points
1223
1224 double wxControlPoint::sm_controlPointDragStartX = 0.0;
1225 double wxControlPoint::sm_controlPointDragStartY = 0.0;
1226 double wxControlPoint::sm_controlPointDragStartWidth = 0.0;
1227 double wxControlPoint::sm_controlPointDragStartHeight = 0.0;
1228 double wxControlPoint::sm_controlPointDragEndWidth = 0.0;
1229 double wxControlPoint::sm_controlPointDragEndHeight = 0.0;
1230 double wxControlPoint::sm_controlPointDragPosX = 0.0;
1231 double wxControlPoint::sm_controlPointDragPosY = 0.0;
1232
1233 IMPLEMENT_DYNAMIC_CLASS(wxControlPoint, wxRectangleShape)
1234
1235 wxControlPoint::wxControlPoint(wxShapeCanvas *theCanvas, wxShape *object, double size, double the_xoffset, double the_yoffset, int the_type):wxRectangleShape(size, size)
1236 {
1237 m_canvas = theCanvas;
1238 m_shape = object;
1239 m_xoffset = the_xoffset;
1240 m_yoffset = the_yoffset;
1241 m_type = the_type;
1242 SetPen(g_oglBlackForegroundPen);
1243 SetBrush(wxBLACK_BRUSH);
1244 m_oldCursor = NULL;
1245 m_visible = TRUE;
1246 m_eraseObject = TRUE;
1247 }
1248
1249 wxControlPoint::~wxControlPoint()
1250 {
1251 }
1252
1253 // Don't even attempt to draw any text - waste of time!
1254 void wxControlPoint::OnDrawContents(wxDC& dc)
1255 {
1256 }
1257
1258 void wxControlPoint::OnDraw(wxDC& dc)
1259 {
1260 m_xpos = m_shape->GetX() + m_xoffset;
1261 m_ypos = m_shape->GetY() + m_yoffset;
1262 wxRectangleShape::OnDraw(dc);
1263 }
1264
1265 void wxControlPoint::OnErase(wxDC& dc)
1266 {
1267 wxRectangleShape::OnErase(dc);
1268 }
1269
1270 // Implement resizing of canvas object
1271 void wxControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
1272 {
1273 m_shape->GetEventHandler()->OnSizingDragLeft(this, draw, x, y, keys, attachment);
1274 }
1275
1276 void wxControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
1277 {
1278 m_shape->GetEventHandler()->OnSizingBeginDragLeft(this, x, y, keys, attachment);
1279 }
1280
1281 void wxControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
1282 {
1283 m_shape->GetEventHandler()->OnSizingEndDragLeft(this, x, y, keys, attachment);
1284 }
1285
1286 int wxControlPoint::GetNumberOfAttachments() const
1287 {
1288 return 1;
1289 }
1290
1291 bool wxControlPoint::GetAttachmentPosition(int attachment, double *x, double *y,
1292 int nth, int no_arcs, wxLineShape *line)
1293 {
1294 *x = m_xpos; *y = m_ypos;
1295 return TRUE;
1296 }
1297
1298 // Control points ('handles') redirect control to the actual shape, to make it easier
1299 // to override sizing behaviour.
1300 void wxShape::OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys, int attachment)
1301 {
1302 double bound_x;
1303 double bound_y;
1304 this->GetBoundingBoxMin(&bound_x, &bound_y);
1305
1306 wxClientDC dc(GetCanvas());
1307 GetCanvas()->PrepareDC(dc);
1308
1309 dc.SetLogicalFunction(OGLRBLF);
1310
1311 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
1312 dc.SetPen(dottedPen);
1313 dc.SetBrush((* wxTRANSPARENT_BRUSH));
1314
1315 if (this->GetCentreResize())
1316 {
1317 // Maintain the same centre point.
1318 double new_width = (double)(2.0*fabs(x - this->GetX()));
1319 double new_height = (double)(2.0*fabs(y - this->GetY()));
1320
1321 // Constrain sizing according to what control point you're dragging
1322 if (pt->m_type == CONTROL_POINT_HORIZONTAL)
1323 {
1324 if (GetMaintainAspectRatio())
1325 {
1326 new_height = bound_y*(new_width/bound_x);
1327 }
1328 else
1329 new_height = bound_y;
1330 }
1331 else if (pt->m_type == CONTROL_POINT_VERTICAL)
1332 {
1333 if (GetMaintainAspectRatio())
1334 {
1335 new_width = bound_x*(new_height/bound_y);
1336 }
1337 else
1338 new_width = bound_x;
1339 }
1340 else if (pt->m_type == CONTROL_POINT_DIAGONAL && (keys & KEY_SHIFT))
1341 new_height = bound_y*(new_width/bound_x);
1342
1343 if (this->GetFixedWidth())
1344 new_width = bound_x;
1345
1346 if (this->GetFixedHeight())
1347 new_height = bound_y;
1348
1349 pt->sm_controlPointDragEndWidth = new_width;
1350 pt->sm_controlPointDragEndHeight = new_height;
1351
1352 this->GetEventHandler()->OnDrawOutline(dc, this->GetX(), this->GetY(),
1353 new_width, new_height);
1354 }
1355 else
1356 {
1357 // Don't maintain the same centre point!
1358 double newX1 = wxMin(pt->sm_controlPointDragStartX, x);
1359 double newY1 = wxMin(pt->sm_controlPointDragStartY, y);
1360 double newX2 = wxMax(pt->sm_controlPointDragStartX, x);
1361 double newY2 = wxMax(pt->sm_controlPointDragStartY, y);
1362 if (pt->m_type == CONTROL_POINT_HORIZONTAL)
1363 {
1364 newY1 = pt->sm_controlPointDragStartY;
1365 newY2 = newY1 + pt->sm_controlPointDragStartHeight;
1366 }
1367 else if (pt->m_type == CONTROL_POINT_VERTICAL)
1368 {
1369 newX1 = pt->sm_controlPointDragStartX;
1370 newX2 = newX1 + pt->sm_controlPointDragStartWidth;
1371 }
1372 else if (pt->m_type == CONTROL_POINT_DIAGONAL && ((keys & KEY_SHIFT) || GetMaintainAspectRatio()))
1373 {
1374 double newH = (double)((newX2 - newX1)*(pt->sm_controlPointDragStartHeight/pt->sm_controlPointDragStartWidth));
1375 if (GetY() > pt->sm_controlPointDragStartY)
1376 newY2 = (double)(newY1 + newH);
1377 else
1378 newY1 = (double)(newY2 - newH);
1379 }
1380 double newWidth = (double)(newX2 - newX1);
1381 double newHeight = (double)(newY2 - newY1);
1382
1383 if (pt->m_type == CONTROL_POINT_VERTICAL && GetMaintainAspectRatio())
1384 {
1385 newWidth = bound_x * (newHeight/bound_y) ;
1386 }
1387
1388 if (pt->m_type == CONTROL_POINT_HORIZONTAL && GetMaintainAspectRatio())
1389 {
1390 newHeight = bound_y * (newWidth/bound_x) ;
1391 }
1392
1393 pt->sm_controlPointDragPosX = (double)(newX1 + (newWidth/2.0));
1394 pt->sm_controlPointDragPosY = (double)(newY1 + (newHeight/2.0));
1395 if (this->GetFixedWidth())
1396 newWidth = bound_x;
1397
1398 if (this->GetFixedHeight())
1399 newHeight = bound_y;
1400
1401 pt->sm_controlPointDragEndWidth = newWidth;
1402 pt->sm_controlPointDragEndHeight = newHeight;
1403 this->GetEventHandler()->OnDrawOutline(dc, pt->sm_controlPointDragPosX, pt->sm_controlPointDragPosY, newWidth, newHeight);
1404 }
1405 }
1406
1407 void wxShape::OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
1408 {
1409 m_canvas->CaptureMouse();
1410
1411 wxClientDC dc(GetCanvas());
1412 GetCanvas()->PrepareDC(dc);
1413 /*
1414 if (pt->m_eraseObject)
1415 this->Erase(dc);
1416 */
1417
1418 dc.SetLogicalFunction(OGLRBLF);
1419
1420 double bound_x;
1421 double bound_y;
1422 this->GetBoundingBoxMin(&bound_x, &bound_y);
1423
1424 // Choose the 'opposite corner' of the object as the stationary
1425 // point in case this is non-centring resizing.
1426 if (pt->GetX() < this->GetX())
1427 pt->sm_controlPointDragStartX = (double)(this->GetX() + (bound_x/2.0));
1428 else
1429 pt->sm_controlPointDragStartX = (double)(this->GetX() - (bound_x/2.0));
1430
1431 if (pt->GetY() < this->GetY())
1432 pt->sm_controlPointDragStartY = (double)(this->GetY() + (bound_y/2.0));
1433 else
1434 pt->sm_controlPointDragStartY = (double)(this->GetY() - (bound_y/2.0));
1435
1436 if (pt->m_type == CONTROL_POINT_HORIZONTAL)
1437 pt->sm_controlPointDragStartY = (double)(this->GetY() - (bound_y/2.0));
1438 else if (pt->m_type == CONTROL_POINT_VERTICAL)
1439 pt->sm_controlPointDragStartX = (double)(this->GetX() - (bound_x/2.0));
1440
1441 // We may require the old width and height.
1442 pt->sm_controlPointDragStartWidth = bound_x;
1443 pt->sm_controlPointDragStartHeight = bound_y;
1444
1445 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
1446 dc.SetPen(dottedPen);
1447 dc.SetBrush((* wxTRANSPARENT_BRUSH));
1448
1449 if (this->GetCentreResize())
1450 {
1451 double new_width = (double)(2.0*fabs(x - this->GetX()));
1452 double new_height = (double)(2.0*fabs(y - this->GetY()));
1453
1454 // Constrain sizing according to what control point you're dragging
1455 if (pt->m_type == CONTROL_POINT_HORIZONTAL)
1456 {
1457 if (GetMaintainAspectRatio())
1458 {
1459 new_height = bound_y*(new_width/bound_x);
1460 }
1461 else
1462 new_height = bound_y;
1463 }
1464 else if (pt->m_type == CONTROL_POINT_VERTICAL)
1465 {
1466 if (GetMaintainAspectRatio())
1467 {
1468 new_width = bound_x*(new_height/bound_y);
1469 }
1470 else
1471 new_width = bound_x;
1472 }
1473 else if (pt->m_type == CONTROL_POINT_DIAGONAL && (keys & KEY_SHIFT))
1474 new_height = bound_y*(new_width/bound_x);
1475
1476 if (this->GetFixedWidth())
1477 new_width = bound_x;
1478
1479 if (this->GetFixedHeight())
1480 new_height = bound_y;
1481
1482 pt->sm_controlPointDragEndWidth = new_width;
1483 pt->sm_controlPointDragEndHeight = new_height;
1484 this->GetEventHandler()->OnDrawOutline(dc, this->GetX(), this->GetY(),
1485 new_width, new_height);
1486 }
1487 else
1488 {
1489 // Don't maintain the same centre point!
1490 double newX1 = wxMin(pt->sm_controlPointDragStartX, x);
1491 double newY1 = wxMin(pt->sm_controlPointDragStartY, y);
1492 double newX2 = wxMax(pt->sm_controlPointDragStartX, x);
1493 double newY2 = wxMax(pt->sm_controlPointDragStartY, y);
1494 if (pt->m_type == CONTROL_POINT_HORIZONTAL)
1495 {
1496 newY1 = pt->sm_controlPointDragStartY;
1497 newY2 = newY1 + pt->sm_controlPointDragStartHeight;
1498 }
1499 else if (pt->m_type == CONTROL_POINT_VERTICAL)
1500 {
1501 newX1 = pt->sm_controlPointDragStartX;
1502 newX2 = newX1 + pt->sm_controlPointDragStartWidth;
1503 }
1504 else if (pt->m_type == CONTROL_POINT_DIAGONAL && ((keys & KEY_SHIFT) || GetMaintainAspectRatio()))
1505 {
1506 double newH = (double)((newX2 - newX1)*(pt->sm_controlPointDragStartHeight/pt->sm_controlPointDragStartWidth));
1507 if (pt->GetY() > pt->sm_controlPointDragStartY)
1508 newY2 = (double)(newY1 + newH);
1509 else
1510 newY1 = (double)(newY2 - newH);
1511 }
1512 double newWidth = (double)(newX2 - newX1);
1513 double newHeight = (double)(newY2 - newY1);
1514
1515 if (pt->m_type == CONTROL_POINT_VERTICAL && GetMaintainAspectRatio())
1516 {
1517 newWidth = bound_x * (newHeight/bound_y) ;
1518 }
1519
1520 if (pt->m_type == CONTROL_POINT_HORIZONTAL && GetMaintainAspectRatio())
1521 {
1522 newHeight = bound_y * (newWidth/bound_x) ;
1523 }
1524
1525 pt->sm_controlPointDragPosX = (double)(newX1 + (newWidth/2.0));
1526 pt->sm_controlPointDragPosY = (double)(newY1 + (newHeight/2.0));
1527 if (this->GetFixedWidth())
1528 newWidth = bound_x;
1529
1530 if (this->GetFixedHeight())
1531 newHeight = bound_y;
1532
1533 pt->sm_controlPointDragEndWidth = newWidth;
1534 pt->sm_controlPointDragEndHeight = newHeight;
1535 this->GetEventHandler()->OnDrawOutline(dc, pt->sm_controlPointDragPosX, pt->sm_controlPointDragPosY, newWidth, newHeight);
1536 }
1537 }
1538
1539 void wxShape::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
1540 {
1541 wxClientDC dc(GetCanvas());
1542 GetCanvas()->PrepareDC(dc);
1543
1544 m_canvas->ReleaseMouse();
1545 dc.SetLogicalFunction(wxCOPY);
1546 this->Recompute();
1547 this->ResetControlPoints();
1548
1549 this->Erase(dc);
1550 /*
1551 if (!pt->m_eraseObject)
1552 this->Show(FALSE);
1553 */
1554
1555 this->SetSize(pt->sm_controlPointDragEndWidth, pt->sm_controlPointDragEndHeight);
1556
1557 // The next operation could destroy this control point (it does for label objects,
1558 // via formatting the text), so save all values we're going to use, or
1559 // we'll be accessing garbage.
1560 wxShape *theObject = this;
1561 wxShapeCanvas *theCanvas = m_canvas;
1562 bool eraseIt = pt->m_eraseObject;
1563
1564 if (theObject->GetCentreResize())
1565 theObject->Move(dc, theObject->GetX(), theObject->GetY());
1566 else
1567 theObject->Move(dc, pt->sm_controlPointDragPosX, pt->sm_controlPointDragPosY);
1568
1569 /*
1570 if (!eraseIt)
1571 theObject->Show(TRUE);
1572 */
1573
1574 // Recursively redraw links if we have a composite.
1575 if (theObject->GetChildren().Number() > 0)
1576 theObject->DrawLinks(dc, -1, TRUE);
1577
1578 double width, height;
1579 theObject->GetBoundingBoxMax(&width, &height);
1580 theObject->GetEventHandler()->OnEndSize(width, height);
1581
1582 if (!theCanvas->GetQuickEditMode() && eraseIt) theCanvas->Redraw(dc);
1583 }
1584
1585
1586
1587 // Polygon control points
1588
1589 IMPLEMENT_DYNAMIC_CLASS(wxPolygonControlPoint, wxControlPoint)
1590
1591 wxPolygonControlPoint::wxPolygonControlPoint(wxShapeCanvas *theCanvas, wxShape *object, double size,
1592 wxRealPoint *vertex, double the_xoffset, double the_yoffset):
1593 wxControlPoint(theCanvas, object, size, the_xoffset, the_yoffset, 0)
1594 {
1595 m_polygonVertex = vertex;
1596 m_originalDistance = 0.0;
1597 }
1598
1599 wxPolygonControlPoint::~wxPolygonControlPoint()
1600 {
1601 }
1602
1603 // Calculate what new size would be, at end of resize
1604 void wxPolygonControlPoint::CalculateNewSize(double x, double y)
1605 {
1606 double bound_x;
1607 double bound_y;
1608 GetShape()->GetBoundingBoxMin(&bound_x, &bound_y);
1609
1610 double dist = (double)sqrt((x - m_shape->GetX())*(x - m_shape->GetX()) +
1611 (y - m_shape->GetY())*(y - m_shape->GetY()));
1612
1613 m_newSize.x = (double)(dist/this->m_originalDistance)*this->m_originalSize.x;
1614 m_newSize.y = (double)(dist/this->m_originalDistance)*this->m_originalSize.y;
1615 }
1616
1617
1618 // Implement resizing polygon or moving the vertex.
1619 void wxPolygonControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
1620 {
1621 m_shape->GetEventHandler()->OnSizingDragLeft(this, draw, x, y, keys, attachment);
1622 }
1623
1624 void wxPolygonControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
1625 {
1626 m_shape->GetEventHandler()->OnSizingBeginDragLeft(this, x, y, keys, attachment);
1627 }
1628
1629 void wxPolygonControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
1630 {
1631 m_shape->GetEventHandler()->OnSizingEndDragLeft(this, x, y, keys, attachment);
1632 }
1633
1634 // Control points ('handles') redirect control to the actual shape, to make it easier
1635 // to override sizing behaviour.
1636 void wxPolygonShape::OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys, int attachment)
1637 {
1638 wxPolygonControlPoint* ppt = (wxPolygonControlPoint*) pt;
1639
1640 wxClientDC dc(GetCanvas());
1641 GetCanvas()->PrepareDC(dc);
1642
1643 dc.SetLogicalFunction(OGLRBLF);
1644
1645 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
1646 dc.SetPen(dottedPen);
1647 dc.SetBrush((* wxTRANSPARENT_BRUSH));
1648
1649 if (0) // keys & KEY_CTRL)
1650 {
1651 // TODO: mend this code. Currently we rely on altering the
1652 // actual points, but we should assume we're not, as per
1653 // the normal sizing case.
1654 m_canvas->Snap(&x, &y);
1655
1656 // Move point
1657 ppt->m_polygonVertex->x = x - this->GetX();
1658 ppt->m_polygonVertex->y = y - this->GetY();
1659 ppt->SetX(x);
1660 ppt->SetY(y);
1661 ((wxPolygonShape *)this)->CalculateBoundingBox();
1662 ((wxPolygonShape *)this)->CalculatePolygonCentre();
1663 }
1664 else
1665 {
1666 ppt->CalculateNewSize(x, y);
1667 }
1668
1669 this->GetEventHandler()->OnDrawOutline(dc, this->GetX(), this->GetY(),
1670 ppt->GetNewSize().x, ppt->GetNewSize().y);
1671 }
1672
1673 void wxPolygonShape::OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
1674 {
1675 wxPolygonControlPoint* ppt = (wxPolygonControlPoint*) pt;
1676
1677 wxClientDC dc(GetCanvas());
1678 GetCanvas()->PrepareDC(dc);
1679
1680 this->Erase(dc);
1681
1682 dc.SetLogicalFunction(OGLRBLF);
1683
1684 double bound_x;
1685 double bound_y;
1686 this->GetBoundingBoxMin(&bound_x, &bound_y);
1687
1688 double dist = (double)sqrt((x - this->GetX())*(x - this->GetX()) +
1689 (y - this->GetY())*(y - this->GetY()));
1690 ppt->m_originalDistance = dist;
1691 ppt->m_originalSize.x = bound_x;
1692 ppt->m_originalSize.y = bound_y;
1693
1694 if (ppt->m_originalDistance == 0.0) ppt->m_originalDistance = (double) 0.0001;
1695
1696 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
1697 dc.SetPen(dottedPen);
1698 dc.SetBrush((* wxTRANSPARENT_BRUSH));
1699
1700 if (0) // keys & KEY_CTRL)
1701 {
1702 // TODO: mend this code. Currently we rely on altering the
1703 // actual points, but we should assume we're not, as per
1704 // the normal sizing case.
1705 m_canvas->Snap(&x, &y);
1706
1707 // Move point
1708 ppt->m_polygonVertex->x = x - this->GetX();
1709 ppt->m_polygonVertex->y = y - this->GetY();
1710 ppt->SetX(x);
1711 ppt->SetY(y);
1712 ((wxPolygonShape *)this)->CalculateBoundingBox();
1713 ((wxPolygonShape *)this)->CalculatePolygonCentre();
1714 }
1715 else
1716 {
1717 ppt->CalculateNewSize(x, y);
1718 }
1719
1720 this->GetEventHandler()->OnDrawOutline(dc, this->GetX(), this->GetY(),
1721 ppt->GetNewSize().x, ppt->GetNewSize().y);
1722
1723 m_canvas->CaptureMouse();
1724 }
1725
1726 void wxPolygonShape::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
1727 {
1728 wxPolygonControlPoint* ppt = (wxPolygonControlPoint*) pt;
1729
1730 wxClientDC dc(GetCanvas());
1731 GetCanvas()->PrepareDC(dc);
1732
1733 m_canvas->ReleaseMouse();
1734 dc.SetLogicalFunction(wxCOPY);
1735
1736 // If we're changing shape, must reset the original points
1737 if (keys & KEY_CTRL)
1738 {
1739 ((wxPolygonShape *)this)->CalculateBoundingBox();
1740 ((wxPolygonShape *)this)->UpdateOriginalPoints();
1741 }
1742 else
1743 {
1744 SetSize(ppt->GetNewSize().x, ppt->GetNewSize().y);
1745 }
1746
1747 ((wxPolygonShape *)this)->CalculateBoundingBox();
1748 ((wxPolygonShape *)this)->CalculatePolygonCentre();
1749
1750 this->Recompute();
1751 this->ResetControlPoints();
1752 this->Move(dc, this->GetX(), this->GetY());
1753 if (!m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
1754 }
1755
1756 /*
1757 * Object region
1758 *
1759 */
1760 IMPLEMENT_DYNAMIC_CLASS(wxShapeRegion, wxObject)
1761
1762 wxShapeRegion::wxShapeRegion()
1763 {
1764 m_regionText = wxEmptyString;
1765 m_font = g_oglNormalFont;
1766 m_minHeight = 5.0;
1767 m_minWidth = 5.0;
1768 m_width = 0.0;
1769 m_height = 0.0;
1770 m_x = 0.0;
1771 m_y = 0.0;
1772
1773 m_regionProportionX = -1.0;
1774 m_regionProportionY = -1.0;
1775 m_formatMode = FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT;
1776 m_regionName = wxEmptyString;
1777 m_textColour = wxT("BLACK");
1778 m_penColour = wxT("BLACK");
1779 m_penStyle = wxSOLID;
1780 m_actualColourObject = NULL;
1781 m_actualPenObject = NULL;
1782 }
1783
1784 wxShapeRegion::wxShapeRegion(wxShapeRegion& region)
1785 {
1786 m_regionText = region.m_regionText;
1787 m_regionName = region.m_regionName;
1788 m_textColour = region.m_textColour;
1789
1790 m_font = region.m_font;
1791 m_minHeight = region.m_minHeight;
1792 m_minWidth = region.m_minWidth;
1793 m_width = region.m_width;
1794 m_height = region.m_height;
1795 m_x = region.m_x;
1796 m_y = region.m_y;
1797
1798 m_regionProportionX = region.m_regionProportionX;
1799 m_regionProportionY = region.m_regionProportionY;
1800 m_formatMode = region.m_formatMode;
1801 m_actualColourObject = NULL;
1802 m_actualPenObject = NULL;
1803 m_penStyle = region.m_penStyle;
1804 m_penColour = region.m_penColour;
1805
1806 ClearText();
1807 wxNode *node = region.m_formattedText.First();
1808 while (node)
1809 {
1810 wxShapeTextLine *line = (wxShapeTextLine *)node->Data();
1811 wxShapeTextLine *new_line =
1812 new wxShapeTextLine(line->GetX(), line->GetY(), line->GetText());
1813 m_formattedText.Append(new_line);
1814 node = node->Next();
1815 }
1816 }
1817
1818 wxShapeRegion::~wxShapeRegion()
1819 {
1820 ClearText();
1821 }
1822
1823 void wxShapeRegion::ClearText()
1824 {
1825 wxNode *node = m_formattedText.First();
1826 while (node)
1827 {
1828 wxShapeTextLine *line = (wxShapeTextLine *)node->Data();
1829 wxNode *next = node->Next();
1830 delete line;
1831 delete node;
1832 node = next;
1833 }
1834 }
1835
1836 void wxShapeRegion::SetFont(wxFont *f)
1837 {
1838 m_font = f;
1839 }
1840
1841 void wxShapeRegion::SetMinSize(double w, double h)
1842 {
1843 m_minWidth = w;
1844 m_minHeight = h;
1845 }
1846
1847 void wxShapeRegion::SetSize(double w, double h)
1848 {
1849 m_width = w;
1850 m_height = h;
1851 }
1852
1853 void wxShapeRegion::SetPosition(double xp, double yp)
1854 {
1855 m_x = xp;
1856 m_y = yp;
1857 }
1858
1859 void wxShapeRegion::SetProportions(double xp, double yp)
1860 {
1861 m_regionProportionX = xp;
1862 m_regionProportionY = yp;
1863 }
1864
1865 void wxShapeRegion::SetFormatMode(int mode)
1866 {
1867 m_formatMode = mode;
1868 }
1869
1870 void wxShapeRegion::SetColour(const wxString& col)
1871 {
1872 m_textColour = col;
1873 m_actualColourObject = NULL;
1874 }
1875
1876 wxColour *wxShapeRegion::GetActualColourObject()
1877 {
1878 if (!m_actualColourObject)
1879 m_actualColourObject = wxTheColourDatabase->FindColour(GetColour());
1880 if (!m_actualColourObject)
1881 m_actualColourObject = wxBLACK;
1882 return m_actualColourObject;
1883 }
1884
1885 void wxShapeRegion::SetPenColour(const wxString& col)
1886 {
1887 m_penColour = col;
1888 m_actualPenObject = NULL;
1889 }
1890
1891 // Returns NULL if the pen is invisible
1892 // (different to pen being transparent; indicates that
1893 // region boundary should not be drawn.)
1894 wxPen *wxShapeRegion::GetActualPen()
1895 {
1896 if (m_actualPenObject)
1897 return m_actualPenObject;
1898
1899 if (!m_penColour) return NULL;
1900 if (m_penColour == wxT("Invisible"))
1901 return NULL;
1902 m_actualPenObject = wxThePenList->FindOrCreatePen(m_penColour, 1, m_penStyle);
1903 return m_actualPenObject;
1904 }
1905
1906