1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Miscellaneous OGL support functions
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "misc.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include <wx/wxprec.h>
28 #include <wx/wxexpr.h>
48 wxFont
* g_oglNormalFont
;
50 wxPen
* g_oglWhiteBackgroundPen
;
51 wxPen
* g_oglTransparentPen
;
52 wxBrush
* g_oglWhiteBackgroundBrush
;
53 wxPen
* g_oglBlackForegroundPen
;
54 wxCursor
* g_oglBullseyeCursor
= NULL
;
56 char* oglBuffer
= NULL
;
58 wxList
oglObjectCopyMapping(wxKEY_INTEGER
);
60 void wxOGLInitialize()
62 g_oglBullseyeCursor
= new wxCursor(wxCURSOR_BULLSEYE
);
64 g_oglNormalFont
= new wxFont(10, wxSWISS
, wxNORMAL
, wxNORMAL
);
66 g_oglBlackPen
= new wxPen("BLACK", 1, wxSOLID
);
68 g_oglWhiteBackgroundPen
= new wxPen("WHITE", 1, wxSOLID
);
69 g_oglTransparentPen
= new wxPen("WHITE", 1, wxTRANSPARENT
);
70 g_oglWhiteBackgroundBrush
= new wxBrush("WHITE", wxSOLID
);
71 g_oglBlackForegroundPen
= new wxPen("BLACK", 1, wxSOLID
);
73 OGLInitializeConstraintTypes();
75 // Initialize big buffer used when writing images
76 oglBuffer
= new char[3000];
78 if (!oglPopupDivisionMenu
)
80 oglPopupDivisionMenu
= new wxMenu("", (wxFunction
)oglGraphicsDivisionMenuProc
);
81 oglPopupDivisionMenu
->Append(DIVISION_MENU_SPLIT_HORIZONTALLY
, "Split horizontally");
82 oglPopupDivisionMenu
->Append(DIVISION_MENU_SPLIT_VERTICALLY
, "Split vertically");
83 oglPopupDivisionMenu
->AppendSeparator();
84 oglPopupDivisionMenu
->Append(DIVISION_MENU_EDIT_LEFT_EDGE
, "Edit left edge");
85 oglPopupDivisionMenu
->Append(DIVISION_MENU_EDIT_TOP_EDGE
, "Edit top edge");
97 if (oglPopupDivisionMenu
)
99 delete oglPopupDivisionMenu
;
100 oglPopupDivisionMenu
= NULL
;
102 if (g_oglBullseyeCursor
)
104 delete g_oglBullseyeCursor
;
105 g_oglBullseyeCursor
= NULL
;
110 delete g_oglNormalFont
;
111 g_oglNormalFont
= NULL
;
115 delete g_oglBlackPen
;
116 g_oglBlackPen
= NULL
;
118 if (g_oglWhiteBackgroundPen
)
120 delete g_oglWhiteBackgroundPen
;
121 g_oglWhiteBackgroundPen
= NULL
;
123 if (g_oglTransparentPen
)
125 delete g_oglTransparentPen
;
126 g_oglTransparentPen
= NULL
;
128 if (g_oglWhiteBackgroundBrush
)
130 delete g_oglWhiteBackgroundBrush
;
131 g_oglWhiteBackgroundBrush
= NULL
;
133 if (g_oglBlackForegroundPen
)
135 delete g_oglBlackForegroundPen
;
136 g_oglBlackForegroundPen
= NULL
;
139 OGLCleanUpConstraintTypes();
142 wxFont
*oglMatchFont(int point_size
)
144 wxFont
*font
= wxTheFontList
->FindOrCreateFont(point_size
, wxSWISS
, wxNORMAL
, wxNORMAL
);
158 font
= swiss_font_12
;
161 font
= swiss_font_14
;
164 font
= swiss_font_18
;
167 font
= swiss_font_24
;
171 font
= swiss_font_10
;
178 int FontSizeDialog(wxFrame
*parent
, int old_size
)
183 sprintf(buf
, "%d", old_size
);
184 wxString ans
= wxGetTextFromUser("Enter point size", "Font size", buf
, parent
);
188 int new_size
= atoi(ans
);
189 if ((new_size
<= 0) || (new_size
> 40))
191 wxMessageBox("Invalid point size!", "Error", wxOK
);
205 char *ans = wxGetSingleChoice("Choose", "Choose a font size", 8, strings, parent);
209 sscanf(ans, "%d", &size);
210 return oglMatchFont(size);
216 // Centre a list of strings in the given box. xOffset and yOffset are the
217 // the positions that these lines should be relative to, and this might be
218 // the same as m_xpos, m_ypos, but might be zero if formatting from left-justifying.
219 void oglCentreText(wxDC
& dc
, wxList
*text_list
,
220 double m_xpos
, double m_ypos
, double width
, double height
,
223 int n
= text_list
->Number();
225 if (!text_list
|| (n
== 0))
228 // First, get maximum dimensions of box enclosing text
230 long char_height
= 0;
232 long current_width
= 0;
234 // Store text extents for speed
235 double *widths
= new double[n
];
237 wxNode
*current
= text_list
->First();
241 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
242 dc
.GetTextExtent(line
->GetText(), ¤t_width
, &char_height
);
243 widths
[i
] = current_width
;
245 if (current_width
> max_width
)
246 max_width
= current_width
;
247 current
= current
->Next();
251 double max_height
= n
*char_height
;
253 double xoffset
, yoffset
, xOffset
, yOffset
;
255 if (formatMode
& FORMAT_CENTRE_VERT
)
257 if (max_height
< height
)
258 yoffset
= (double)(m_ypos
- (height
/2.0) + (height
- max_height
)/2.0);
260 yoffset
= (double)(m_ypos
- (height
/2.0));
269 if (formatMode
& FORMAT_CENTRE_HORIZ
)
271 xoffset
= (double)(m_xpos
- width
/2.0);
280 current
= text_list
->First();
285 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
288 if ((formatMode
& FORMAT_CENTRE_HORIZ
) && (widths
[i
] < width
))
289 x
= (double)((width
- widths
[i
])/2.0 + xoffset
);
292 double y
= (double)(i
*char_height
+ yoffset
);
294 line
->SetX( x
- xOffset
); line
->SetY( y
- yOffset
);
295 current
= current
->Next();
302 // Centre a list of strings in the given box
303 void oglCentreTextNoClipping(wxDC
& dc
, wxList
*text_list
,
304 double m_xpos
, double m_ypos
, double width
, double height
)
306 int n
= text_list
->Number();
308 if (!text_list
|| (n
== 0))
311 // First, get maximum dimensions of box enclosing text
313 long char_height
= 0;
315 long current_width
= 0;
317 // Store text extents for speed
318 double *widths
= new double[n
];
320 wxNode
*current
= text_list
->First();
324 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
325 dc
.GetTextExtent(line
->GetText(), ¤t_width
, &char_height
);
326 widths
[i
] = current_width
;
328 if (current_width
> max_width
)
329 max_width
= current_width
;
330 current
= current
->Next();
334 double max_height
= n
*char_height
;
336 double yoffset
= (double)(m_ypos
- (height
/2.0) + (height
- max_height
)/2.0);
338 double xoffset
= (double)(m_xpos
- width
/2.0);
340 current
= text_list
->First();
345 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
347 double x
= (double)((width
- widths
[i
])/2.0 + xoffset
);
348 double y
= (double)(i
*char_height
+ yoffset
);
350 line
->SetX( x
- m_xpos
); line
->SetY( y
- m_ypos
);
351 current
= current
->Next();
357 void oglGetCentredTextExtent(wxDC
& dc
, wxList
*text_list
,
358 double m_xpos
, double m_ypos
, double width
, double height
,
359 double *actual_width
, double *actual_height
)
361 int n
= text_list
->Number();
363 if (!text_list
|| (n
== 0))
370 // First, get maximum dimensions of box enclosing text
372 long char_height
= 0;
374 long current_width
= 0;
376 wxNode
*current
= text_list
->First();
380 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
381 dc
.GetTextExtent(line
->GetText(), ¤t_width
, &char_height
);
383 if (current_width
> max_width
)
384 max_width
= current_width
;
385 current
= current
->Next();
389 *actual_height
= n
*char_height
;
390 *actual_width
= max_width
;
393 // Format a string to a list of strings that fit in the given box.
394 // Interpret %n and 10 or 13 as a new line.
395 wxStringList
*oglFormatText(wxDC
& dc
, const wxString
& text
, double width
, double height
, int formatMode
)
397 // First, parse the string into a list of words
398 wxStringList word_list
;
400 // Make new lines into NULL strings at this point
401 int i
= 0; int j
= 0; int len
= strlen(text
);
402 char word
[200]; word
[0] = 0;
403 bool end_word
= FALSE
; bool new_line
= FALSE
;
412 { word
[j
] = '%'; j
++; }
416 { new_line
= TRUE
; end_word
= TRUE
; i
++; }
418 { word
[j
] = '%'; j
++; word
[j
] = text
[i
]; j
++; i
++; }
424 new_line
= TRUE
; end_word
= TRUE
; i
++;
429 new_line
= TRUE
; end_word
= TRUE
; i
++;
444 if (i
== len
) end_word
= TRUE
;
449 word_list
.Append((wxObject
*)copystring(word
));
454 word_list
.Append((wxObject
*)NULL
);
458 // Now, make a list of strings which can fit in the box
459 wxStringList
*string_list
= new wxStringList
;
463 wxNode
*node
= word_list
.First();
468 wxString
oldBuffer(buffer
);
470 char *s
= (char *)node
->Data();
474 if (strlen(buffer
) > 0)
475 string_list
->Add(buffer
);
485 dc
.GetTextExtent(buffer
, &x
, &y
);
487 // Don't fit within the bounding box if we're fitting shape to contents
488 if ((x
> width
) && !(formatMode
& FORMAT_SIZE_TO_CONTENTS
))
490 // Deal with first word being wider than box
491 if (oldBuffer
.Length() > 0)
492 string_list
->Add(oldBuffer
);
502 string_list
->Add(buffer
);
507 void oglDrawFormattedText(wxDC
& dc
, wxList
*text_list
,
508 double m_xpos
, double m_ypos
, double width
, double height
,
511 double xoffset
, yoffset
;
512 if (formatMode
& FORMAT_CENTRE_HORIZ
)
515 xoffset
= (double)(m_xpos
- (width
/ 2.0));
517 if (formatMode
& FORMAT_CENTRE_VERT
)
520 yoffset
= (double)(m_ypos
- (height
/ 2.0));
522 dc
.SetClippingRegion(
523 (double)(m_xpos
- width
/2.0), (double)(m_ypos
- height
/2.0),
524 (double)width
, (double)height
);
526 wxNode
*current
= text_list
->First();
529 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
531 dc
.DrawText(line
->GetText(), WXROUND(xoffset
+ line
->GetX()), WXROUND(yoffset
+ line
->GetY()));
532 current
= current
->Next();
535 dc
.DestroyClippingRegion();
539 * Find centroid given list of points comprising polyline
543 void oglFindPolylineCentroid(wxList
*points
, double *x
, double *y
)
548 wxNode
*node
= points
->First();
551 wxRealPoint
*point
= (wxRealPoint
*)node
->Data();
557 *x
= (xcount
/points
->Number());
558 *y
= (ycount
/points
->Number());
562 * Check that (x1, y1) -> (x2, y2) hits (x3, y3) -> (x4, y4).
563 * If so, ratio1 gives the proportion along the first line
564 * that the intersection occurs (or something like that).
565 * Used by functions below.
568 void oglCheckLineIntersection(double x1
, double y1
, double x2
, double y2
,
569 double x3
, double y3
, double x4
, double y4
,
570 double *ratio1
, double *ratio2
)
572 double denominator_term
= (y4
- y3
)*(x2
- x1
) - (y2
- y1
)*(x4
- x3
);
573 double numerator_term
= (x3
- x1
)*(y4
- y3
) + (x4
- x3
)*(y1
- y3
);
575 double line_constant
;
576 double length_ratio
= 1.0;
579 // Check for parallel lines
580 if ((denominator_term
< 0.005) && (denominator_term
> -0.005))
581 line_constant
= -1.0;
583 line_constant
= numerator_term
/denominator_term
;
585 // Check for intersection
586 if ((line_constant
< 1.0) && (line_constant
> 0.0))
588 // Now must check that other line hits
589 if (((y4
- y3
) < 0.005) && ((y4
- y3
) > -0.005))
590 k_line
= ((x1
- x3
) + line_constant
*(x2
- x1
))/(x4
- x3
);
592 k_line
= ((y1
- y3
) + line_constant
*(y2
- y1
))/(y4
- y3
);
594 if ((k_line
>= 0.0) && (k_line
< 1.0))
595 length_ratio
= line_constant
;
599 *ratio1
= length_ratio
;
604 * Find where (x1, y1) -> (x2, y2) hits one of the lines in xvec, yvec.
605 * (*x3, *y3) is the point where it hits.
608 void oglFindEndForPolyline(double n
, double xvec
[], double yvec
[],
609 double x1
, double y1
, double x2
, double y2
, double *x3
, double *y3
)
612 double lastx
= xvec
[0];
613 double lasty
= yvec
[0];
615 double min_ratio
= 1.0;
619 for (i
= 1; i
< n
; i
++)
621 oglCheckLineIntersection(x1
, y1
, x2
, y2
, lastx
, lasty
, xvec
[i
], yvec
[i
],
622 &line_ratio
, &other_ratio
);
626 if (line_ratio
< min_ratio
)
627 min_ratio
= line_ratio
;
630 // Do last (implicit) line if last and first doubles are not identical
631 if (!(xvec
[0] == lastx
&& yvec
[0] == lasty
))
633 oglCheckLineIntersection(x1
, y1
, x2
, y2
, lastx
, lasty
, xvec
[0], yvec
[0],
634 &line_ratio
, &other_ratio
);
636 if (line_ratio
< min_ratio
)
637 min_ratio
= line_ratio
;
640 *x3
= (x1
+ (x2
- x1
)*min_ratio
);
641 *y3
= (y1
+ (y2
- y1
)*min_ratio
);
646 * Find where the line hits the box.
650 void oglFindEndForBox(double width
, double height
,
651 double x1
, double y1
, // Centre of box (possibly)
652 double x2
, double y2
, // other end of line
653 double *x3
, double *y3
) // End on box edge
658 xvec
[0] = (double)(x1
- width
/2.0);
659 yvec
[0] = (double)(y1
- height
/2.0);
660 xvec
[1] = (double)(x1
- width
/2.0);
661 yvec
[1] = (double)(y1
+ height
/2.0);
662 xvec
[2] = (double)(x1
+ width
/2.0);
663 yvec
[2] = (double)(y1
+ height
/2.0);
664 xvec
[3] = (double)(x1
+ width
/2.0);
665 yvec
[3] = (double)(y1
- height
/2.0);
666 xvec
[4] = (double)(x1
- width
/2.0);
667 yvec
[4] = (double)(y1
- height
/2.0);
669 oglFindEndForPolyline(5, xvec
, yvec
, x2
, y2
, x1
, y1
, x3
, y3
);
673 * Find where the line hits the circle.
677 void oglFindEndForCircle(double radius
,
678 double x1
, double y1
, // Centre of circle
679 double x2
, double y2
, // Other end of line
680 double *x3
, double *y3
)
682 double H
= (double)sqrt((x2
- x1
)*(x2
- x1
) + (y2
- y1
)*(y2
- y1
));
691 *y3
= radius
* (y2
- y1
)/H
+ y1
;
692 *x3
= radius
* (x2
- x1
)/H
+ x1
;
697 * Given the line (x1, y1) -> (x2, y2), and an arrow size of given length and width,
698 * return the position of the tip of the arrow and the left and right vertices of the arrow.
702 void oglGetArrowPoints(double x1
, double y1
, double x2
, double y2
,
703 double length
, double width
,
704 double *tip_x
, double *tip_y
,
705 double *side1_x
, double *side1_y
,
706 double *side2_x
, double *side2_y
)
708 double l
= (double)sqrt((x2
- x1
)*(x2
- x1
) + (y2
- y1
)*(y2
- y1
));
713 double i_bar
= (x2
- x1
)/l
;
714 double j_bar
= (y2
- y1
)/l
;
716 double x3
= (- length
*i_bar
) + x2
;
717 double y3
= (- length
*j_bar
) + y2
;
719 *side1_x
= width
*(-j_bar
) + x3
;
720 *side1_y
= width
*i_bar
+ y3
;
722 *side2_x
= -width
*(-j_bar
) + x3
;
723 *side2_y
= -width
*i_bar
+ y3
;
725 *tip_x
= x2
; *tip_y
= y2
;
729 * Given an ellipse and endpoints of a line, returns the point at which
730 * the line touches the ellipse in values x4, y4.
731 * This function assumes that the centre of the ellipse is at x1, y1, and the
732 * ellipse has a width of width1 and a height of height1. It also assumes you are
733 * wanting to draw an arc FROM point x2, y2 TOWARDS point x3, y3.
734 * This function calculates the x,y coordinates of the intersection point of
735 * the arc with the ellipse.
736 * Author: Ian Harrison
739 void oglDrawArcToEllipse(double x1
, double y1
, double width1
, double height1
, double x2
, double y2
, double x3
, double y3
,
740 double *x4
, double *y4
)
742 double a1
= (double)(width1
/2.0);
743 double b1
= (double)(height1
/2.0);
745 // These are required to give top left x and y coordinates for DrawEllipse
746 // double top_left_x1 = (double)(x1 - a1);
747 // double top_left_y1 = (double)(y1 - b1);
749 // Check for vertical line
750 if (fabs(x2 - x3) < 0.05)
754 *y4 = (double)(y1 - b1);
756 *y4 = (double)(y1 + b1);
760 // Check that x2 != x3
761 if (fabs(x2
- x3
) < 0.05)
765 *y4
= (double)(y1
- sqrt((b1
*b1
- (((x2
-x1
)*(x2
-x1
))*(b1
*b1
)/(a1
*a1
)))));
767 *y4
= (double)(y1
+ sqrt((b1
*b1
- (((x2
-x1
)*(x2
-x1
))*(b1
*b1
)/(a1
*a1
)))));
771 // Calculate the x and y coordinates of the point where arc intersects ellipse
773 double A
, B
, C
, D
, E
, F
, G
, H
, K
;
774 double ellipse1_x
, ellipse1_y
;
776 A
= (double)(1/(a1
* a1
));
777 B
= (double)((y3
- y2
) * (y3
- y2
)) / ((x3
- x2
) * (x3
- x2
) * b1
* b1
);
778 C
= (double)(2 * (y3
- y2
) * (y2
- y1
)) / ((x3
- x2
) * b1
* b1
);
779 D
= (double)((y2
- y1
) * (y2
- y1
)) / (b1
* b1
);
781 F
= (double)(C
- (2 * A
* x1
) - (2 * B
* x2
));
782 G
= (double)((A
* x1
* x1
) + (B
* x2
* x2
) - (C
* x2
) + D
- 1);
783 H
= (double)((y3
- y2
) / (x3
- x2
));
784 K
= (double)((F
* F
) - (4 * E
* G
));
787 // In this case the line intersects the ellipse, so calculate intersection
791 ellipse1_x
= (double)(((F
* -1) + sqrt(K
)) / (2 * E
));
792 ellipse1_y
= (double)((H
* (ellipse1_x
- x2
)) + y2
);
796 ellipse1_x
= (double)(((F
* -1) - sqrt(K
)) / (2 * E
));
797 ellipse1_y
= (double)((H
* (ellipse1_x
- x2
)) + y2
);
801 // in this case, arc does not intersect ellipse, so just draw arc
810 // Draw a little circle (radius = 2) at the end of the arc where it hits
813 double circle_x = ellipse1_x - 2.0;
814 double circle_y = ellipse1_y - 2.0;
815 m_canvas->DrawEllipse(circle_x, circle_y, 4.0, 4.0);
819 // Update a list item from a list of strings
820 void UpdateListBox(wxListBox
*item
, wxList
*list
)
826 wxNode
*node
= list
->First();
829 char *s
= (char *)node
->Data();
835 bool oglRoughlyEqual(double val1
, double val2
, double tol
)
837 return ( (val1
< (val2
+ tol
)) && (val1
> (val2
- tol
)) &&
838 (val2
< (val1
+ tol
)) && (val2
> (val1
- tol
)));
842 * Hex<->Dec conversion
845 // Array used in DecToHex conversion routine.
846 static char sg_HexArray
[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
847 'C', 'D', 'E', 'F' };
849 // Convert 2-digit hex number to decimal
850 unsigned int oglHexToDec(char* buf
)
852 int firstDigit
, secondDigit
;
855 firstDigit
= buf
[0] - 'A' + 10;
857 firstDigit
= buf
[0] - '0';
860 secondDigit
= buf
[1] - 'A' + 10;
862 secondDigit
= buf
[1] - '0';
864 return firstDigit
* 16 + secondDigit
;
867 // Convert decimal integer to 2-character hex string
868 void oglDecToHex(unsigned int dec
, char *buf
)
870 int firstDigit
= (int)(dec
/16.0);
871 int secondDigit
= (int)(dec
- (firstDigit
*16.0));
872 buf
[0] = sg_HexArray
[firstDigit
];
873 buf
[1] = sg_HexArray
[secondDigit
];
877 // 3-digit hex to wxColour
878 wxColour
oglHexToColour(const wxString
& hex
)
880 if (hex
.Length() == 6)
883 strncpy(buf
, hex
, 7);
884 unsigned int r
= oglHexToDec((char *)buf
);
885 unsigned int g
= oglHexToDec((char *)(buf
+2));
886 unsigned int b
= oglHexToDec((char *)(buf
+4));
887 return wxColour(r
, g
, b
);
890 return wxColour(0,0,0);
893 // RGB to 3-digit hex
894 wxString
oglColourToHex(const wxColour
& colour
)
897 unsigned int red
= colour
.Red();
898 unsigned int green
= colour
.Green();
899 unsigned int blue
= colour
.Blue();
901 oglDecToHex(red
, buf
);
902 oglDecToHex(green
, buf
+2);
903 oglDecToHex(blue
, buf
+4);
905 return wxString(buf
);